perf(bluetooth): reduce CPU usage and improve async performance
This commit is contained in:
parent
b3515bb9ba
commit
b19aea769a
4 changed files with 113 additions and 101 deletions
|
|
@ -34,6 +34,7 @@ use crate::{
|
||||||
|
|
||||||
static BLUETOOTH_ENABLED: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
static BLUETOOTH_ENABLED: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn run() -> cosmic::iced::Result {
|
pub fn run() -> cosmic::iced::Result {
|
||||||
cosmic::applet::run::<CosmicBluetoothApplet>(())
|
cosmic::applet::run::<CosmicBluetoothApplet>(())
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +54,7 @@ struct CosmicBluetoothApplet {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CosmicBluetoothApplet {
|
impl CosmicBluetoothApplet {
|
||||||
|
#[inline]
|
||||||
fn update_icon(&mut self) {
|
fn update_icon(&mut self) {
|
||||||
self.icon_name = if self.bluer_state.bluetooth_enabled {
|
self.icon_name = if self.bluer_state.bluetooth_enabled {
|
||||||
"cosmic-applet-bluetooth-active-symbolic"
|
"cosmic-applet-bluetooth-active-symbolic"
|
||||||
|
|
@ -117,7 +119,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
||||||
self.popup.replace(new_id);
|
self.popup.replace(new_id);
|
||||||
self.timeline = Timeline::new();
|
self.timeline = Timeline::new();
|
||||||
|
|
||||||
let mut popup_settings = self.core.applet.get_popup_settings(
|
let popup_settings = self.core.applet.get_popup_settings(
|
||||||
self.core.main_window_id().unwrap(),
|
self.core.main_window_id().unwrap(),
|
||||||
new_id,
|
new_id,
|
||||||
None,
|
None,
|
||||||
|
|
@ -348,6 +350,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
||||||
} = theme::active().cosmic().spacing;
|
} = theme::active().cosmic().spacing;
|
||||||
|
|
||||||
let mut known_bluetooth = vec![];
|
let mut known_bluetooth = vec![];
|
||||||
|
// PERF: This should be pre-filtered in an update.
|
||||||
for dev in self.bluer_state.devices.iter().filter(|d| {
|
for dev in self.bluer_state.devices.iter().filter(|d| {
|
||||||
!self
|
!self
|
||||||
.request_confirmation
|
.request_confirmation
|
||||||
|
|
@ -355,7 +358,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
||||||
.map_or(false, |(dev, _, _)| d.address == dev.address)
|
.map_or(false, |(dev, _, _)| d.address == dev.address)
|
||||||
}) {
|
}) {
|
||||||
let mut row = row![
|
let mut row = row![
|
||||||
icon::from_name(dev.icon.as_str()).size(16).symbolic(true),
|
icon::from_name(dev.icon).size(16).symbolic(true),
|
||||||
text::body(dev.name.clone())
|
text::body(dev.name.clone())
|
||||||
.align_x(Alignment::Start)
|
.align_x(Alignment::Start)
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
|
|
@ -364,12 +367,8 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
.spacing(12);
|
.spacing(12);
|
||||||
|
|
||||||
if let Some(DeviceProperty::BatteryPercentage(battery)) = dev
|
if let Some(battery) = dev.battery_percent {
|
||||||
.properties
|
let icon = match battery {
|
||||||
.iter()
|
|
||||||
.find(|p| matches!(p, DeviceProperty::BatteryPercentage(_)))
|
|
||||||
{
|
|
||||||
let icon = match *battery {
|
|
||||||
b if b >= 20 && b < 40 => "battery-low",
|
b if b >= 20 && b < 40 => "battery-low",
|
||||||
b if b < 20 => "battery-caution",
|
b if b < 20 => "battery-caution",
|
||||||
_ => "battery",
|
_ => "battery",
|
||||||
|
|
@ -474,9 +473,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
||||||
if let Some((device, pin, _)) = self.request_confirmation.as_ref() {
|
if let Some((device, pin, _)) = self.request_confirmation.as_ref() {
|
||||||
let row = column![
|
let row = column![
|
||||||
padded_control(row![
|
padded_control(row![
|
||||||
icon::from_name(device.icon.as_str())
|
icon::from_name(device.icon).size(16).symbolic(true),
|
||||||
.size(16)
|
|
||||||
.symbolic(true),
|
|
||||||
text::body(&device.name)
|
text::body(&device.name)
|
||||||
.align_x(Alignment::Start)
|
.align_x(Alignment::Start)
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
|
|
@ -528,7 +525,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
||||||
&& (d.has_name() || d.is_known_device_type())
|
&& (d.has_name() || d.is_known_device_type())
|
||||||
}) {
|
}) {
|
||||||
let row = row![
|
let row = row![
|
||||||
icon::from_name(dev.icon.as_str()).size(16).symbolic(true),
|
icon::from_name(dev.icon).size(16).symbolic(true),
|
||||||
text::body(dev.name.clone()).align_x(Alignment::Start),
|
text::body(dev.name.clone()).align_x(Alignment::Start),
|
||||||
]
|
]
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use cosmic::{
|
||||||
iced_futures::stream,
|
iced_futures::stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use futures::{stream::FuturesUnordered, FutureExt};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
spawn,
|
spawn,
|
||||||
|
|
@ -28,8 +29,8 @@ use tokio::{
|
||||||
task::JoinHandle,
|
task::JoinHandle,
|
||||||
time::timeout,
|
time::timeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Copied from https://github.com/bluez/bluez/blob/39467578207889fd015775cbe81a3db9dd26abea/src/dbus-common.c#L53
|
// Copied from https://github.com/bluez/bluez/blob/39467578207889fd015775cbe81a3db9dd26abea/src/dbus-common.c#L53
|
||||||
|
#[inline]
|
||||||
fn device_type_to_icon(device_type: &str) -> &'static str {
|
fn device_type_to_icon(device_type: &str) -> &'static str {
|
||||||
match device_type {
|
match device_type {
|
||||||
"computer" => "laptop-symbolic",
|
"computer" => "laptop-symbolic",
|
||||||
|
|
@ -49,6 +50,7 @@ fn device_type_to_icon(device_type: &str) -> &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||||
id: I,
|
id: I,
|
||||||
) -> iced::Subscription<BluerEvent> {
|
) -> iced::Subscription<BluerEvent> {
|
||||||
|
|
@ -70,7 +72,7 @@ pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||||
.await;
|
.await;
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = session_state.bluer_state().await;
|
let state = bluer_state(&session_state.adapter).await;
|
||||||
|
|
||||||
// reconnect to paired and trusted devices
|
// reconnect to paired and trusted devices
|
||||||
if state.bluetooth_enabled {
|
if state.bluetooth_enabled {
|
||||||
|
|
@ -90,29 +92,28 @@ pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut event_handler = async |event| match event {
|
let mut event_handler = async |event| {
|
||||||
BluerSessionEvent::ChangesProcessed(state) => {
|
let message = match event {
|
||||||
_ = output.send(BluerEvent::DevicesChanged { state }).await;
|
BluerSessionEvent::ChangesProcessed(state) => {
|
||||||
}
|
BluerEvent::DevicesChanged { state }
|
||||||
|
}
|
||||||
|
|
||||||
BluerSessionEvent::RequestResponse {
|
BluerSessionEvent::RequestResponse {
|
||||||
req,
|
req,
|
||||||
state,
|
state,
|
||||||
err_msg,
|
err_msg,
|
||||||
} => {
|
} => BluerEvent::RequestResponse {
|
||||||
_ = output
|
req,
|
||||||
.send(BluerEvent::RequestResponse {
|
state,
|
||||||
req,
|
err_msg,
|
||||||
state,
|
},
|
||||||
err_msg,
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
BluerSessionEvent::AgentEvent(e) => {
|
|
||||||
_ = output.send(BluerEvent::AgentEvent(e)).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {}
|
BluerSessionEvent::AgentEvent(e) => BluerEvent::AgentEvent(e),
|
||||||
|
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = output.send(message).await;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
||||||
|
|
@ -124,6 +125,7 @@ pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||||
|
|
||||||
if let Some(event) = session_rx.recv().await {
|
if let Some(event) = session_rx.recv().await {
|
||||||
event_handler(event).await;
|
event_handler(event).await;
|
||||||
|
// Consume any additional available events.
|
||||||
while let Some(event) = session_rx.try_recv().ok() {
|
while let Some(event) = session_rx.try_recv().ok() {
|
||||||
event_handler(event).await;
|
event_handler(event).await;
|
||||||
}
|
}
|
||||||
|
|
@ -190,15 +192,18 @@ pub enum BluerDeviceStatus {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BluerDevice {
|
pub struct BluerDevice {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub icon: &'static str,
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
pub status: BluerDeviceStatus,
|
pub status: BluerDeviceStatus,
|
||||||
pub properties: Vec<DeviceProperty>,
|
pub battery_percent: Option<u8>,
|
||||||
pub icon: String,
|
pub is_paired: bool,
|
||||||
|
pub is_trusted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for BluerDevice {}
|
impl Eq for BluerDevice {}
|
||||||
|
|
||||||
impl Ord for BluerDevice {
|
impl Ord for BluerDevice {
|
||||||
|
#[inline]
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
match self.status.cmp(&other.status) {
|
match self.status.cmp(&other.status) {
|
||||||
std::cmp::Ordering::Equal => self.name.to_lowercase().cmp(&other.name.to_lowercase()),
|
std::cmp::Ordering::Equal => self.name.to_lowercase().cmp(&other.name.to_lowercase()),
|
||||||
|
|
@ -208,6 +213,7 @@ impl Ord for BluerDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for BluerDevice {
|
impl PartialOrd for BluerDevice {
|
||||||
|
#[inline]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
match self.status.cmp(&other.status) {
|
match self.status.cmp(&other.status) {
|
||||||
std::cmp::Ordering::Equal => {
|
std::cmp::Ordering::Equal => {
|
||||||
|
|
@ -219,6 +225,7 @@ impl PartialOrd for BluerDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for BluerDevice {
|
impl PartialEq for BluerDevice {
|
||||||
|
#[inline]
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.name == other.name && self.address == other.address
|
self.name == other.name && self.address == other.address
|
||||||
}
|
}
|
||||||
|
|
@ -227,18 +234,26 @@ impl PartialEq for BluerDevice {
|
||||||
const DEFAULT_DEVICE_ICON: &str = "bluetooth-symbolic";
|
const DEFAULT_DEVICE_ICON: &str = "bluetooth-symbolic";
|
||||||
|
|
||||||
impl BluerDevice {
|
impl BluerDevice {
|
||||||
|
#[inline(never)]
|
||||||
pub async fn from_device(device: &bluer::Device) -> Self {
|
pub async fn from_device(device: &bluer::Device) -> Self {
|
||||||
let mut name = device
|
let (mut name, is_paired, is_trusted, is_connected, battery_percent, icon) = futures::join!(
|
||||||
.name()
|
device.name().map(|res| res
|
||||||
.await
|
.ok()
|
||||||
.unwrap_or_default()
|
.flatten()
|
||||||
.unwrap_or_else(|| device.address().to_string());
|
.unwrap_or_else(|| device.address().to_string())),
|
||||||
|
device.is_paired().map(Result::unwrap_or_default),
|
||||||
|
device.is_trusted().map(Result::unwrap_or_default),
|
||||||
|
device.is_connected().map(Result::unwrap_or_default),
|
||||||
|
device.battery_percentage().map(|res| res.ok().flatten()),
|
||||||
|
device
|
||||||
|
.icon()
|
||||||
|
.map(|res| device_type_to_icon(&res.ok().flatten().unwrap_or_default()))
|
||||||
|
);
|
||||||
|
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
name = device.address().to_string();
|
name = device.address().to_string();
|
||||||
};
|
};
|
||||||
let is_paired = device.is_paired().await.unwrap_or_default();
|
|
||||||
let is_connected = device.is_connected().await.unwrap_or_default();
|
|
||||||
let properties = device.all_properties().await.unwrap_or_default();
|
|
||||||
let status = if is_connected {
|
let status = if is_connected {
|
||||||
BluerDeviceStatus::Connected
|
BluerDeviceStatus::Connected
|
||||||
} else if is_paired {
|
} else if is_paired {
|
||||||
|
|
@ -246,44 +261,30 @@ impl BluerDevice {
|
||||||
} else {
|
} else {
|
||||||
BluerDeviceStatus::Disconnected
|
BluerDeviceStatus::Disconnected
|
||||||
};
|
};
|
||||||
let icon = properties
|
|
||||||
.iter()
|
|
||||||
.find_map(|p| {
|
|
||||||
if let DeviceProperty::Icon(icon) = p {
|
|
||||||
Some(device_type_to_icon(icon.clone().as_str()).to_string())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| device_type_to_icon(DEFAULT_DEVICE_ICON).to_string());
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
|
icon,
|
||||||
address: device.address(),
|
address: device.address(),
|
||||||
status,
|
status,
|
||||||
properties,
|
battery_percent,
|
||||||
icon,
|
is_paired,
|
||||||
|
is_trusted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn paired_and_trusted(&self) -> bool {
|
fn paired_and_trusted(&self) -> bool {
|
||||||
self.properties
|
self.is_paired && self.is_trusted
|
||||||
.iter()
|
|
||||||
.filter(|p| {
|
|
||||||
matches!(
|
|
||||||
p,
|
|
||||||
DeviceProperty::Trusted(true) | DeviceProperty::Paired(true)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.count()
|
|
||||||
== 2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_known_device_type(&self) -> bool {
|
pub fn is_known_device_type(&self) -> bool {
|
||||||
self.icon != DEFAULT_DEVICE_ICON
|
self.icon != DEFAULT_DEVICE_ICON
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn has_name(&self) -> bool {
|
pub fn has_name(&self) -> bool {
|
||||||
self.name != self.address.to_string()
|
self.name != self.address.to_string()
|
||||||
|
|
@ -326,9 +327,8 @@ pub struct BluerSessionState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BluerSessionState {
|
impl BluerSessionState {
|
||||||
pub(crate) async fn new(session: Session) -> anyhow::Result<Self> {
|
async fn new(session: Session) -> anyhow::Result<Self> {
|
||||||
let adapter = session.default_adapter().await?;
|
let adapter = session.default_adapter().await?;
|
||||||
let devices = build_device_list(&adapter).await;
|
|
||||||
let (tx, rx) = tokio::sync::mpsc::channel(100);
|
let (tx, rx) = tokio::sync::mpsc::channel(100);
|
||||||
let (req_tx, req_rx) = channel(100);
|
let (req_tx, req_rx) = channel(100);
|
||||||
let tx_clone_1 = tx.clone();
|
let tx_clone_1 = tx.clone();
|
||||||
|
|
@ -524,6 +524,7 @@ impl BluerSessionState {
|
||||||
Ok(self_)
|
Ok(self_)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn listen_bluetooth_power_changes(&self) {
|
fn listen_bluetooth_power_changes(&self) {
|
||||||
let tx = self.tx.clone();
|
let tx = self.tx.clone();
|
||||||
let req_tx = self.req_tx.clone();
|
let req_tx = self.req_tx.clone();
|
||||||
|
|
@ -532,13 +533,15 @@ impl BluerSessionState {
|
||||||
let _handle: JoinHandle<anyhow::Result<()>> = spawn(async move {
|
let _handle: JoinHandle<anyhow::Result<()>> = spawn(async move {
|
||||||
let mut status = adapter_clone.is_powered().await.unwrap_or_default();
|
let mut status = adapter_clone.is_powered().await.unwrap_or_default();
|
||||||
let mut interval = tokio::time::interval(Duration::from_secs(3));
|
let mut interval = tokio::time::interval(Duration::from_secs(3));
|
||||||
|
let mut devices = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
let new_status = adapter_clone.is_powered().await.unwrap_or_default();
|
let new_status = adapter_clone.is_powered().await.unwrap_or_default();
|
||||||
|
devices = build_device_list(devices, &adapter_clone).await;
|
||||||
if new_status != status {
|
if new_status != status {
|
||||||
status = new_status;
|
status = new_status;
|
||||||
let state = BluerState {
|
let state = BluerState {
|
||||||
devices: build_device_list(&adapter_clone).await,
|
devices: devices.clone(),
|
||||||
bluetooth_enabled: status,
|
bluetooth_enabled: status,
|
||||||
};
|
};
|
||||||
if state.bluetooth_enabled {
|
if state.bluetooth_enabled {
|
||||||
|
|
@ -555,7 +558,8 @@ impl BluerSessionState {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn process_changes(&mut self) {
|
#[inline]
|
||||||
|
fn process_changes(&mut self) {
|
||||||
let tx = self.tx.clone();
|
let tx = self.tx.clone();
|
||||||
let req_tx = self.req_tx.clone();
|
let req_tx = self.req_tx.clone();
|
||||||
let Some(mut wake_up) = self.wake_up_discover_rx.take() else {
|
let Some(mut wake_up) = self.wake_up_discover_rx.take() else {
|
||||||
|
|
@ -565,8 +569,8 @@ impl BluerSessionState {
|
||||||
let adapter_clone = self.adapter.clone();
|
let adapter_clone = self.adapter.clone();
|
||||||
let _monitor_devices: tokio::task::JoinHandle<Result<(), anyhow::Error>> = spawn(
|
let _monitor_devices: tokio::task::JoinHandle<Result<(), anyhow::Error>> = spawn(
|
||||||
async move {
|
async move {
|
||||||
let mut milli_timeout = 10;
|
|
||||||
let mut change_stream = {
|
let mut change_stream = {
|
||||||
|
let mut milli_timeout = 10;
|
||||||
let mut res = adapter_clone.discover_devices_with_changes().await;
|
let mut res = adapter_clone.discover_devices_with_changes().await;
|
||||||
while res.is_err() {
|
while res.is_err() {
|
||||||
_ = tokio::time::timeout(
|
_ = tokio::time::timeout(
|
||||||
|
|
@ -577,14 +581,15 @@ impl BluerSessionState {
|
||||||
res = adapter_clone.discover_devices_with_changes().await;
|
res = adapter_clone.discover_devices_with_changes().await;
|
||||||
milli_timeout = milli_timeout.saturating_mul(5);
|
milli_timeout = milli_timeout.saturating_mul(5);
|
||||||
}
|
}
|
||||||
milli_timeout = 10;
|
|
||||||
res.unwrap()
|
res.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
let mut interval = tokio::time::interval(Duration::from_secs(1));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
let mut milli_timeout = 10;
|
||||||
let mut devices: Vec<BluerDevice> = Vec::new();
|
let mut devices: Vec<BluerDevice> = Vec::new();
|
||||||
|
let mut new_devices = Vec::new();
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
change = timeout(Duration::from_millis(milli_timeout), change_stream.next()) => {
|
change = timeout(Duration::from_millis(milli_timeout), change_stream.next()) => {
|
||||||
|
|
@ -601,35 +606,34 @@ impl BluerSessionState {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut new_devices = build_device_list(&adapter_clone).await;
|
new_devices = build_device_list(new_devices, &adapter_clone).await;
|
||||||
for d in new_devices
|
for d in new_devices
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|d| !devices.contains(d) && d.paired_and_trusted())
|
.filter(|d| !devices.contains(d) && d.paired_and_trusted())
|
||||||
{
|
{
|
||||||
_ = req_tx.send(BluerRequest::ConnectDevice(d.address)).await;
|
_ = req_tx.send(BluerRequest::ConnectDevice(d.address)).await;
|
||||||
}
|
}
|
||||||
devices = mem::take(&mut new_devices);
|
|
||||||
|
|
||||||
|
mem::swap(&mut new_devices, &mut devices);
|
||||||
let _ = tx
|
let _ = tx
|
||||||
.send(BluerSessionEvent::ChangesProcessed(BluerState {
|
.send(BluerSessionEvent::ChangesProcessed(BluerState {
|
||||||
devices: build_device_list(&adapter_clone).await,
|
devices: devices.clone(),
|
||||||
bluetooth_enabled: adapter_clone
|
bluetooth_enabled: adapter_clone
|
||||||
.is_powered()
|
.is_powered()
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
}))
|
}))
|
||||||
.await;
|
.await;
|
||||||
// reset timeout
|
interval.tick().await;
|
||||||
milli_timeout = 10;
|
|
||||||
}
|
}
|
||||||
let _ = tx.send(BluerSessionEvent::ChangeStreamEnded).await;
|
let _ = tx.send(BluerSessionEvent::ChangeStreamEnded).await;
|
||||||
interval.tick().await;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn process_requests(&self, request_rx: Receiver<BluerRequest>) {
|
#[inline]
|
||||||
|
fn process_requests(&self, request_rx: Receiver<BluerRequest>) {
|
||||||
let active_requests = self.active_requests.clone();
|
let active_requests = self.active_requests.clone();
|
||||||
let adapter = self.adapter.clone();
|
let adapter = self.adapter.clone();
|
||||||
let tx = self.tx.clone();
|
let tx = self.tx.clone();
|
||||||
|
|
@ -740,15 +744,10 @@ impl BluerSessionState {
|
||||||
BluerRequest::StateUpdate => {}
|
BluerRequest::StateUpdate => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let state = BluerState {
|
|
||||||
devices: build_device_list(&adapter_clone).await,
|
|
||||||
bluetooth_enabled: adapter_clone.is_powered().await.unwrap_or_default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = tx_clone
|
let _ = tx_clone
|
||||||
.send(BluerSessionEvent::RequestResponse {
|
.send(BluerSessionEvent::RequestResponse {
|
||||||
req: req_clone,
|
req: req_clone,
|
||||||
state,
|
state: bluer_state(&adapter_clone).await,
|
||||||
err_msg,
|
err_msg,
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -763,28 +762,42 @@ impl BluerSessionState {
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn bluer_state(&self) -> BluerState {
|
#[inline]
|
||||||
BluerState {
|
async fn bluer_state(adapter: &Adapter) -> BluerState {
|
||||||
devices: build_device_list(&self.adapter).await,
|
let (devices, bluetooth_enabled) = futures::join!(
|
||||||
// TODO is this a proper way of checking if bluetooth is enabled?
|
build_device_list(Vec::new(), adapter),
|
||||||
bluetooth_enabled: self.adapter.is_powered().await.unwrap_or_default(),
|
// TODO is this a proper way of checking if bluetooth is enabled?
|
||||||
}
|
adapter.is_powered().map(Result::unwrap_or_default),
|
||||||
|
);
|
||||||
|
|
||||||
|
BluerState {
|
||||||
|
devices,
|
||||||
|
bluetooth_enabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn build_device_list(adapter: &Adapter) -> Vec<BluerDevice> {
|
#[inline(never)]
|
||||||
|
async fn build_device_list(mut devices: Vec<BluerDevice>, adapter: &Adapter) -> Vec<BluerDevice> {
|
||||||
let addrs = adapter.device_addresses().await.unwrap_or_default();
|
let addrs = adapter.device_addresses().await.unwrap_or_default();
|
||||||
let mut devices = Vec::with_capacity(addrs.len());
|
|
||||||
|
|
||||||
for address in addrs {
|
devices.clear();
|
||||||
let device = match adapter.device(address) {
|
if addrs.len() > devices.capacity() {
|
||||||
Ok(device) => device,
|
devices.reserve(addrs.len() - devices.capacity());
|
||||||
Err(_) => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
devices.push(BluerDevice::from_device(&device).await);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Concurrently collect bluer devices from each address.
|
||||||
|
let mut device_stream = addrs
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|address| adapter.device(address).ok())
|
||||||
|
.map(async move |device| BluerDevice::from_device(&device).await)
|
||||||
|
.collect::<FuturesUnordered<_>>();
|
||||||
|
|
||||||
|
while let Some(device) = device_stream.next().await {
|
||||||
|
devices.push(device)
|
||||||
|
}
|
||||||
|
|
||||||
devices.sort();
|
devices.sort();
|
||||||
devices
|
devices
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ mod localize;
|
||||||
|
|
||||||
use crate::localize::localize;
|
use crate::localize::localize;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn run() -> cosmic::iced::Result {
|
pub fn run() -> cosmic::iced::Result {
|
||||||
localize();
|
localize();
|
||||||
app::run()
|
app::run()
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ macro_rules! fl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the `Localizer` to be used for localizing this library.
|
// Get the `Localizer` to be used for localizing this library.
|
||||||
|
#[inline]
|
||||||
pub fn localizer() -> Box<dyn Localizer> {
|
pub fn localizer() -> Box<dyn Localizer> {
|
||||||
Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations))
|
Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue