azalea_shell/factory/network/
device.rs1use azalea_service::StaticServiceManager;
2use gtk::prelude::*;
3use relm4::{
4 FactorySender,
5 prelude::{DynamicIndex, FactoryComponent},
6};
7use zbus::zvariant::OwnedObjectPath;
8
9use crate::{
10 icon,
11 service::{
12 self,
13 dbus::network_manager::proxy::{NMDeviceState, NetworkManagerDeviceProxyBlocking},
14 },
15};
16
17#[derive(Debug)]
18pub struct Model {
19 device: OwnedObjectPath,
20 name: String,
21 proxy: Option<NetworkManagerDeviceProxyBlocking<'static>>,
22 is_activated: bool,
23 visible: bool,
24 #[allow(unused)]
25 connection: Option<zbus::blocking::Connection>,
26}
27
28#[derive(Debug)]
29pub enum Input {
30 Toggle,
31 Connect,
32 Disconnect,
33}
34
35#[derive(Debug)]
36pub enum CommandOutput {
37 State(service::dbus::network_manager::proxy::NMDeviceState),
38}
39
40#[derive(Debug)]
41pub enum Output {}
42
43#[relm4::factory(pub)]
44impl FactoryComponent for Model {
45 type Init = OwnedObjectPath;
46 type Input = Input;
47 type Output = Output;
48 type CommandOutput = CommandOutput;
49 type ParentWidget = gtk::Box;
50
51 view! {
52 #[root]
53 gtk::Box {
54 set_visible: self.visible,
55 set_spacing: 12,
56
57 gtk::Label {
58 set_halign: gtk::Align::Start,
59 set_hexpand: true,
60 set_label: &self.name,
61 },
62
63 gtk::Button {
64 set_halign: gtk::Align::End,
65
66 #[watch]
67 set_icon_name: if self.is_activated {
68 icon::PLUG_CONNECTED
69 } else {
70 icon::PLUG_DISCONNECTED
71 },
72
73 #[watch]
74 set_css_classes: if self.is_activated {
75 &[ "azalea-primary-container" ]
76 } else {
77 &[]
78 },
79
80 connect_clicked => Input::Toggle
81 }
82 }
83 }
84
85 fn init_model(device: Self::Init, _index: &DynamicIndex, sender: FactorySender<Self>) -> Self {
86 let connection = zbus::blocking::Connection::system().ok();
87
88 let proxy = connection
89 .as_ref()
90 .and_then(|conn| NetworkManagerDeviceProxyBlocking::new(&conn, device.clone()).ok());
91
92 let state_stream = proxy.as_ref().map(|p| p.receive_state_changed());
93 if let Some(mut state_stream) = state_stream {
94 let cmd_sender = sender.command_sender().clone();
95 relm4::spawn_blocking(move || {
96 while let Some(prop) = state_stream.next() {
97 if let Ok(state) = prop.get() {
98 if let Err(_) = cmd_sender.send(CommandOutput::State(state)) {
99 break;
100 }
101 }
102 }
103 });
104 }
105
106 Self {
107 connection,
108 device,
109 name: proxy
110 .as_ref()
111 .and_then(|p| p.interface().ok())
112 .unwrap_or(format!("unknown")),
113 is_activated: proxy
114 .as_ref()
115 .and_then(|p| p.state().ok())
116 .map(|s| s == NMDeviceState::NMDeviceStateActivated)
117 .unwrap_or(false),
118 visible: proxy
119 .as_ref()
120 .and_then(|p| p.device_type().ok())
121 .map(|t| match t {
122 _ => true,
123 })
124 .unwrap_or(false),
125 proxy,
126 }
127 }
128
129 fn update(&mut self, message: Self::Input, sender: FactorySender<Self>) {
130 match message {
131 Input::Toggle => {
132 if self.is_activated {
133 self.update(Input::Disconnect, sender);
134 } else {
135 self.update(Input::Connect, sender);
136 }
137 }
138 Input::Connect => {
139 service::dbus::network_manager::Service::send(
140 service::dbus::network_manager::Input::ActivateConnection {
141 connection: None,
142 device: Some(self.device.clone()),
143 specific_object: None,
144 },
145 );
146 }
147 Input::Disconnect => {
148 if let Some(proxy) = &self.proxy {
149 drop(proxy.disconnect());
150 };
151 }
152 }
153 }
154
155 fn update_cmd(&mut self, message: Self::CommandOutput, _sender: FactorySender<Self>) {
156 match message {
157 CommandOutput::State(nmdevice_state) => match nmdevice_state {
158 NMDeviceState::NMDeviceStateActivated => self.is_activated = true,
159 _ => self.is_activated = false,
160 },
161 }
162 }
163}