Skip to content

Commit 6080318

Browse files
Merge pull request #375 from swetar-mecha/pre/status-bar
feat(status bar): service integration in UI
2 parents 8296941 + da7f596 commit 6080318

File tree

11 files changed

+323
-28
lines changed

11 files changed

+323
-28
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,6 @@ gpui = { git = "https://github.com/namana-mecha/zed.git", rev = "7a0d37756b35e60
4343
"linux-impeller",
4444
] }
4545
rust-embed = "8.8.0"
46+
networkmanager = { path = "dbus/freedesktop/networkmanager" }
47+
bluez = { path = "dbus/freedesktop/bluez" }
48+
upower = { path = "dbus/freedesktop/upower" }

shell/crates/status_bar/Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ categories.workspace = true
1111
keywords.workspace = true
1212

1313
[dependencies]
14+
networkmanager = { workspace = true }
15+
bluez = { workspace = true }
16+
upower = { workspace = true }
17+
commons = { path = "../commons" }
1418
gpui = { workspace = true }
1519
rust-embed = { workspace = true }
1620
anyhow = { workspace = true }
17-
commons = { path = "../commons" }
18-
chrono = { version = "0.4", features = ["clock"] }
21+
chrono = { version = "0.4", features = ["clock"] }
22+
futures = "0.3.31"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use upower::interfaces::device::BatteryState;
2+
3+
#[derive(Debug)]
4+
pub enum AppEvents {
5+
WirelessStatusChanged { enabled: bool },
6+
WirelessStrength { strength: u8 },
7+
BluetoothEnabled { enabled: bool },
8+
BluetoothConnectionStatus { connected: bool },
9+
BatteryStateChanged { state: BatteryState },
10+
BatteryLevelChanged { level: u8 },
11+
TimeUpdated
12+
}

shell/crates/status_bar/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
mod ui;
2+
mod events;
3+
pub mod services;
24

35
pub mod prelude {
4-
pub use crate::ui::StatusBar;
5-
}
6+
pub use crate::ui::{StatusBar, Icon, IconName};
7+
pub use crate::events::AppEvents;
8+
}

shell/crates/status_bar/src/main.rs

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use commons::prelude::*;
2+
use futures::{StreamExt, channel::mpsc};
23
use gpui::*;
34
use status_bar::prelude::*;
5+
use status_bar::services::*;
6+
use std::time::Duration;
7+
use upower::interfaces::device::BatteryState;
48

59
fn main() {
610
let application = gpui::Application::new().with_assets(Assets {});
@@ -13,7 +17,98 @@ fn main() {
1317
window_bounds: Some(window_bounds),
1418
..Default::default()
1519
},
16-
|_window, cx| cx.new(|_cx| StatusBar::new()),
20+
|_window, cx| {
21+
let (app_channel_tx, mut app_channel_rx) = mpsc::channel::<AppEvents>(120);
22+
let executor = cx.background_executor();
23+
24+
executor
25+
.spawn(sync_network_status(app_channel_tx.clone()))
26+
.detach();
27+
executor
28+
.spawn(sync_network_strength(app_channel_tx.clone()))
29+
.detach();
30+
31+
executor
32+
.spawn(sync_bluetooth_status(app_channel_tx.clone()))
33+
.detach();
34+
executor
35+
.spawn(sync_bluetooth_connected_status(app_channel_tx.clone()))
36+
.detach();
37+
38+
executor
39+
.spawn(sync_battery_state(app_channel_tx.clone()))
40+
.detach();
41+
executor
42+
.spawn(sync_battery_level(app_channel_tx.clone()))
43+
.detach();
44+
45+
cx.new(|cx| {
46+
cx.spawn(async move |app, cx| {
47+
while let Some(event) = app_channel_rx.next().await {
48+
match event {
49+
AppEvents::WirelessStatusChanged { enabled } => {
50+
let _ = app.update(cx, |this: &mut StatusBar, cx| {
51+
this.wireless_enabled = enabled;
52+
cx.notify();
53+
});
54+
}
55+
AppEvents::WirelessStrength { strength } => {
56+
let _ = app.update(cx, |this: &mut StatusBar, cx| {
57+
this.wireless_strength = strength;
58+
cx.notify();
59+
});
60+
}
61+
AppEvents::BluetoothEnabled { enabled } => {
62+
let _ = app.update(cx, |this: &mut StatusBar, cx| {
63+
this.bluetooth_enabled = enabled;
64+
cx.notify();
65+
});
66+
}
67+
AppEvents::BluetoothConnectionStatus { connected } => {
68+
let _ = app.update(cx, |this: &mut StatusBar, cx| {
69+
this.bluetooth_connected = connected;
70+
cx.notify();
71+
});
72+
}
73+
AppEvents::BatteryStateChanged { state } => {
74+
let _ = app.update(cx, |this: &mut StatusBar, cx| {
75+
this.battery_state = match state {
76+
BatteryState::Charging => BatteryState::Charging,
77+
BatteryState::Discharging => BatteryState::Discharging,
78+
_ => BatteryState::Unknown,
79+
};
80+
cx.notify();
81+
});
82+
}
83+
AppEvents::BatteryLevelChanged { level } => {
84+
let _ = app.update(cx, |this: &mut StatusBar, cx| {
85+
this.battery_level = level;
86+
cx.notify();
87+
});
88+
}
89+
AppEvents::TimeUpdated => {
90+
let _ = app.update(cx, |this: &mut StatusBar, cx| {
91+
this.update_time(cx);
92+
});
93+
}
94+
}
95+
}
96+
})
97+
.detach();
98+
99+
cx.spawn(async move |app, cx| {
100+
loop {
101+
cx.background_executor().timer(Duration::from_secs(1)).await;
102+
let _ = app.update(cx, |this: &mut StatusBar, cx| {
103+
this.update_time(cx);
104+
});
105+
}
106+
})
107+
.detach();
108+
109+
StatusBar::new()
110+
})
111+
},
17112
)
18113
.unwrap();
19114
cx.activate(true);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use crate::events::AppEvents;
2+
use futures::{SinkExt, channel::mpsc};
3+
use upower::service::UPowerService;
4+
5+
pub async fn sync_battery_level(mut tx: mpsc::Sender<AppEvents>) {
6+
let upower_manager = match UPowerService::new().await {
7+
Ok(upower_manager) => upower_manager,
8+
Err(e) => {
9+
eprintln!("Failed to create UpowerService: {}", e);
10+
return;
11+
}
12+
};
13+
14+
let status_receiver = upower_manager.stream_device_percentage().await;
15+
16+
while let Ok(percentage) = status_receiver.recv() {
17+
let level = percentage as u8;
18+
let _ = tx.send(AppEvents::BatteryLevelChanged { level }).await;
19+
}
20+
}
21+
22+
pub async fn sync_battery_state(mut tx: mpsc::Sender<AppEvents>) {
23+
let upower_manager = match UPowerService::new().await {
24+
Ok(upower_manager) => upower_manager,
25+
Err(e) => {
26+
eprintln!("Failed to create UpowerService: {}", e);
27+
return;
28+
}
29+
};
30+
31+
let status_receiver = upower_manager.stream_device_state().await;
32+
33+
while let Ok(state) = status_receiver.recv() {
34+
let _ = tx.send(AppEvents::BatteryStateChanged { state }).await;
35+
}
36+
37+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use crate::events::AppEvents;
2+
use bluez::service::BluetoothService;
3+
use futures::{SinkExt, channel::mpsc};
4+
5+
pub async fn sync_bluetooth_status(mut tx: mpsc::Sender<AppEvents>) {
6+
let bluetooth_manager = match BluetoothService::new().await {
7+
Ok(bluetooth_manager) => bluetooth_manager,
8+
Err(e) => {
9+
eprintln!("Failed to create BluetoothService: {}", e);
10+
return;
11+
}
12+
};
13+
14+
let status_receiver = bluetooth_manager.stream_bluetooth_enabled_status().await;
15+
16+
while let Ok(enabled) = status_receiver.recv() {
17+
let _ = tx.send(AppEvents::BluetoothEnabled { enabled }).await;
18+
}
19+
}
20+
21+
pub async fn sync_bluetooth_connected_status(mut tx: mpsc::Sender<AppEvents>) {
22+
let bluetooth_manager = match BluetoothService::new().await {
23+
Ok(bluetooth_manager) => bluetooth_manager,
24+
Err(e) => {
25+
eprintln!("Failed to create BluetoothService: {}", e);
26+
return;
27+
}
28+
};
29+
30+
let connected_devices_count = match bluetooth_manager.get_connected_devices().await {
31+
Ok(r) => r.len(),
32+
Err(e) => {
33+
eprintln!("Failed to get connected devices: {}", e);
34+
return;
35+
}
36+
};
37+
let connected = connected_devices_count > 0;
38+
39+
let _ = tx
40+
.send(AppEvents::BluetoothConnectionStatus { connected })
41+
.await;
42+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pub mod network_sync;
2+
pub mod bluetooth_sync;
3+
pub mod battery_sync;
4+
5+
pub use network_sync::{sync_network_status, sync_network_strength};
6+
pub use bluetooth_sync::{sync_bluetooth_status, sync_bluetooth_connected_status};
7+
pub use battery_sync::{sync_battery_level, sync_battery_state};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use crate::events::AppEvents;
2+
use futures::{SinkExt, channel::mpsc};
3+
use networkmanager::service::NetworkManagerService;
4+
5+
pub async fn sync_network_status(mut tx: mpsc::Sender<AppEvents>) {
6+
let network_manager = match NetworkManagerService::new().await {
7+
Ok(nm) => nm,
8+
Err(e) => {
9+
eprintln!("Failed to create NetworkManagerService: {}", e);
10+
return;
11+
}
12+
};
13+
let wireless_status_receiver = network_manager.stream_wireless_enabled_status().await;
14+
while let Ok(is_wireless_enabled) = wireless_status_receiver.recv() {
15+
let _ = tx
16+
.send(AppEvents::WirelessStatusChanged {
17+
enabled: is_wireless_enabled,
18+
})
19+
.await;
20+
}
21+
}
22+
23+
pub async fn sync_network_strength(mut tx: mpsc::Sender<AppEvents>) {
24+
let network_manager = match NetworkManagerService::new().await {
25+
Ok(nm) => nm,
26+
Err(e) => {
27+
eprintln!("Failed to create NetworkManagerService: {}", e);
28+
return;
29+
}
30+
};
31+
32+
let strength_receiver = network_manager.stream_active_network_strength().await;
33+
34+
while let Ok(strength) = strength_receiver.recv() {
35+
let _ = tx.send(AppEvents::WirelessStrength { strength }).await;
36+
}
37+
}

shell/crates/status_bar/src/ui/icon.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use gpui::{prelude::FluentBuilder, *};
22
pub const STATUS_BAR_ICONS_DIR: &str = "icons/status-bar/";
33

4-
#[derive(IntoElement, Clone)]
4+
#[derive(IntoElement, Clone, Debug)]
55
pub enum IconName {
66
WirelessOn,
77
WirelessOff,

0 commit comments

Comments
 (0)