1 #include "network.hpp"
2 #include <glibmm/spawn.h>
3 #include <cassert>
4 #include <iostream>
5 #include <gtk-utils.hpp>
6
7 #define NM_DBUS_NAME "org.freedesktop.NetworkManager"
8 #define ACTIVE_CONNECTION "PrimaryConnection"
9 #define STRENGTH "Strength"
10
11 // status options
12 #define STATUS_CONN_ICON "icon_only"
13 #define STATUS_CONN_NAME "connection_name"
14
get_control_center_section(DBusProxy & nm)15 std::string WfNetworkConnectionInfo::get_control_center_section(DBusProxy& nm)
16 {
17 Glib::Variant<bool> wifi;
18 nm->get_cached_property(wifi, "WirelessEnabled");
19
20 return wifi.get() ? "wifi" : "network";
21 }
22
spawn_control_center(DBusProxy & nm)23 void WfNetworkConnectionInfo::spawn_control_center(DBusProxy& nm)
24 {
25 std::string command = "env XDG_CURRENT_DESKTOP=GNOME gnome-control-center ";
26 command += get_control_center_section(nm);
27
28 Glib::spawn_command_line_async(command);
29 }
30
31 struct NoConnectionInfo : public WfNetworkConnectionInfo
32 {
get_icon_nameNoConnectionInfo33 std::string get_icon_name(WfConnectionState state)
34 {
35 return "network-offline-symbolic";
36 }
37
get_connection_strengthNoConnectionInfo38 int get_connection_strength()
39 {
40 return 0;
41 }
42
get_ipNoConnectionInfo43 std::string get_ip()
44 {
45 return "127.0.0.1";
46 }
47
~NoConnectionInfoNoConnectionInfo48 virtual ~NoConnectionInfo() {}
49 };
50
51 struct WifiConnectionInfo : public WfNetworkConnectionInfo
52 {
53 WayfireNetworkInfo *widget;
54 DBusProxy ap;
55
WifiConnectionInfoWifiConnectionInfo56 WifiConnectionInfo(const DBusConnection& connection, std::string path,
57 WayfireNetworkInfo *widget)
58 {
59 this->widget = widget;
60
61 ap = Gio::DBus::Proxy::create_sync(connection, NM_DBUS_NAME, path,
62 "org.freedesktop.NetworkManager.AccessPoint");
63
64 if (ap) {
65 ap->signal_properties_changed().connect_notify(
66 sigc::mem_fun(this, &WifiConnectionInfo::on_properties_changed));
67 }
68 }
69
on_properties_changedWifiConnectionInfo70 void on_properties_changed(DBusPropMap changed, DBusPropList invalid)
71 {
72 bool needs_refresh = false;
73 for (auto& prop : changed)
74 {
75 if (prop.first == STRENGTH)
76 needs_refresh = true;
77 }
78
79 if (needs_refresh)
80 {
81 widget->update_icon();
82 widget->update_status();
83 }
84 }
85
get_strengthWifiConnectionInfo86 int get_strength()
87 {
88 assert(ap);
89
90 Glib::Variant<guchar> vstr;
91 ap->get_cached_property(vstr, STRENGTH);
92
93 return vstr.get();
94 }
95
get_strength_strWifiConnectionInfo96 std::string get_strength_str()
97 {
98 int value = get_strength();
99
100 if (value > 80)
101 return "excellent";
102 if (value > 55)
103 return "good";
104 if (value > 30)
105 return "ok";
106 if (value > 5)
107 return "weak";
108 return "none";
109 }
110
get_icon_nameWifiConnectionInfo111 virtual std::string get_icon_name(WfConnectionState state)
112 {
113 if (state <= CSTATE_ACTIVATING || state == CSTATE_DEACTIVATING)
114 return "network-wireless-acquiring-symbolic";
115
116 if (state == CSTATE_DEACTIVATED)
117 return "network-wireless-disconnected-symbolic";
118
119 if (ap) {
120 return "network-wireless-signal-" + get_strength_str() + "-symbolic";
121 } else {
122 return "network-wireless-no-route-symbolic";
123 }
124 }
125
get_connection_strengthWifiConnectionInfo126 virtual int get_connection_strength()
127 {
128 if (ap) {
129 return get_strength();
130 } else {
131 return 100;
132 }
133 }
134
get_ipWifiConnectionInfo135 virtual std::string get_ip()
136 {
137 return "0.0.0.0";
138 }
139
~WifiConnectionInfoWifiConnectionInfo140 virtual ~WifiConnectionInfo() {}
141 };
142
143 struct EthernetConnectionInfo : public WfNetworkConnectionInfo
144 {
145 DBusProxy ap;
EthernetConnectionInfoEthernetConnectionInfo146 EthernetConnectionInfo(const DBusConnection& connection, std::string path)
147 { }
148
get_icon_nameEthernetConnectionInfo149 virtual std::string get_icon_name(WfConnectionState state)
150 {
151 if (state <= CSTATE_ACTIVATING || state == CSTATE_DEACTIVATING)
152 return "network-wired-acquiring-symbolic";
153
154 if (state == CSTATE_DEACTIVATED)
155 return "network-wired-disconnected-symbolic";
156
157 return "network-wired-symbolic";
158 }
159
get_connection_nameEthernetConnectionInfo160 std::string get_connection_name()
161 {
162 return "Ethernet - " + connection_name;
163 }
164
get_connection_strengthEthernetConnectionInfo165 virtual int get_connection_strength()
166 {
167 return 100;
168 }
169
get_ipEthernetConnectionInfo170 virtual std::string get_ip()
171 {
172 return "0.0.0.0";
173 }
174
~EthernetConnectionInfoEthernetConnectionInfo175 virtual ~EthernetConnectionInfo() {}
176 };
177
178
179 /* TODO: handle Connectivity */
180
get_connection_state(DBusProxy connection)181 static WfConnectionState get_connection_state(DBusProxy connection)
182 {
183 if (!connection)
184 return CSTATE_DEACTIVATED;
185
186 Glib::Variant<guint32> state;
187 connection->get_cached_property(state, "State");
188 return (WfConnectionState)state.get();
189 }
190
update_icon()191 void WayfireNetworkInfo::update_icon()
192 {
193 auto icon_name = info->get_icon_name(
194 get_connection_state(active_connection_proxy));
195 WfIconLoadOptions options;
196 options.invert = icon_invert_opt;
197 options.user_scale = icon.get_scale_factor();
198 set_image_icon(icon, icon_name, icon_size_opt, options);
199 }
200
201 struct status_color
202 {
203 int point;
204 Gdk::RGBA rgba;
205 } status_colors[] = {
206 {0, Gdk::RGBA{"#ff0000"}},
207 {25, Gdk::RGBA{"#ff0000"}},
208 {40, Gdk::RGBA{"#ffff55"}},
209 {100, Gdk::RGBA{"#00ff00"}},
210 };
211
212 #define MAX_COLORS (sizeof(status_colors) / sizeof(status_color))
213
get_color_for_pc(int pc)214 static Gdk::RGBA get_color_for_pc(int pc)
215 {
216 for (int i = MAX_COLORS - 2; i >= 0; i--)
217 {
218 if (status_colors[i].point <= pc)
219 {
220 auto& r1 = status_colors[i].rgba;
221 auto& r2 = status_colors[i + 1].rgba;
222
223 double a = 1.0 * (pc - status_colors[i].point) / (status_colors[i + 1].point - status_colors[i].point);
224 Gdk::RGBA result;
225 result.set_rgba(
226 r1.get_red () * (1 - a) + r2.get_red () * a,
227 r1.get_green() * (1 - a) + r2.get_green() * a,
228 r1.get_blue () * (1 - a) + r2.get_blue () * a,
229 r1.get_alpha() * (1 - a) + r2.get_alpha() * a);
230
231 return result;
232 }
233 }
234
235 return Gdk::RGBA{"#ffffff"};
236 }
237
update_status()238 void WayfireNetworkInfo::update_status()
239 {
240 std::string description = info->get_connection_name();
241
242 status.set_text(description);
243 button.set_tooltip_text(description);
244
245 if (status_color_opt) {
246 status.override_color(get_color_for_pc(info->get_connection_strength()));
247 } else {
248 status.unset_color();
249 }
250 }
251
update_active_connection()252 void WayfireNetworkInfo::update_active_connection()
253 {
254 Glib::Variant<Glib::ustring> active_conn_path;
255 nm_proxy->get_cached_property(active_conn_path, ACTIVE_CONNECTION);
256
257 if (active_conn_path && active_conn_path.get() != "/")
258 {
259 active_connection_proxy = Gio::DBus::Proxy::create_sync(
260 connection, NM_DBUS_NAME, active_conn_path.get(),
261 "org.freedesktop.NetworkManager.Connection.Active");
262 } else {
263 active_connection_proxy = DBusProxy();
264 }
265
266 auto set_no_connection = [=] ()
267 {
268 info = std::unique_ptr<WfNetworkConnectionInfo> (new NoConnectionInfo());
269 info->connection_name = "No connection";
270 };
271
272 if (!active_connection_proxy)
273 {
274 set_no_connection();
275 } else {
276 Glib::Variant<Glib::ustring> vtype, vobject;
277 active_connection_proxy->get_cached_property(vtype, "Type");
278 active_connection_proxy->get_cached_property(vobject, "SpecificObject");
279 auto type = vtype.get();
280 auto object = vobject.get();
281
282 if (type.find("wireless") != type.npos)
283 {
284 info = std::unique_ptr<WfNetworkConnectionInfo> (
285 new WifiConnectionInfo(connection, object, this));
286 }
287 else if (type.find("ethernet") != type.npos)
288 {
289 info = std::unique_ptr<WfNetworkConnectionInfo> (
290 new EthernetConnectionInfo(connection, object));
291 } else if (type.find("bluetooth"))
292 {
293 std::cout << "Unimplemented: bluetooth connection" << std::endl;
294 set_no_connection();
295 // TODO
296 } else
297 {
298 std::cout << "Unimplemented: unknown connection type" << std::endl;
299 set_no_connection();
300 // TODO: implement Unknown connection
301 }
302
303 Glib::Variant<Glib::ustring> vname;
304 active_connection_proxy->get_cached_property(vname, "Id");
305 info->connection_name = vname.get();
306 }
307
308 update_icon();
309 update_status();
310 }
311
on_nm_properties_changed(const Gio::DBus::Proxy::MapChangedProperties & properties,const std::vector<Glib::ustring> & invalidated)312 void WayfireNetworkInfo::on_nm_properties_changed(
313 const Gio::DBus::Proxy::MapChangedProperties& properties,
314 const std::vector<Glib::ustring>& invalidated)
315 {
316 for (auto &prop : properties)
317 {
318 if (prop.first == ACTIVE_CONNECTION)
319 update_active_connection();
320 }
321 }
322
setup_dbus()323 bool WayfireNetworkInfo::setup_dbus()
324 {
325 auto cancellable = Gio::Cancellable::create();
326 connection = Gio::DBus::Connection::get_sync(Gio::DBus::BUS_TYPE_SYSTEM, cancellable);
327 if (!connection)
328 {
329 std::cerr << "Failed to connect to dbus" << std::endl;
330 return false;
331 }
332
333 nm_proxy = Gio::DBus::Proxy::create_sync(connection, NM_DBUS_NAME,
334 "/org/freedesktop/NetworkManager",
335 "org.freedesktop.NetworkManager");
336 if (!nm_proxy)
337 {
338 std::cerr << "Failed to connect to network manager, "
339 << "are you sure it is running?" << std::endl;
340 return false;
341 }
342
343 nm_proxy->signal_properties_changed().connect_notify(
344 sigc::mem_fun(this, &WayfireNetworkInfo::on_nm_properties_changed));
345
346 return true;
347 }
348
on_click()349 void WayfireNetworkInfo::on_click()
350 {
351 info->spawn_control_center(nm_proxy);
352 }
353
init(Gtk::HBox * container)354 void WayfireNetworkInfo::init(Gtk::HBox *container)
355 {
356 if (!setup_dbus())
357 {
358 enabled = false;
359 return;
360 }
361
362 container->add(button);
363 button.add(button_content);
364 button.get_style_context()->add_class("flat");
365
366 button.signal_clicked().connect_notify(
367 sigc::mem_fun(this, &WayfireNetworkInfo::on_click));
368
369 button_content.set_valign(Gtk::ALIGN_CENTER);
370 button_content.pack_start(icon, Gtk::PACK_SHRINK);
371 button_content.pack_start(status, Gtk::PACK_SHRINK);
372 button_content.set_spacing(6);
373
374 icon.set_valign(Gtk::ALIGN_CENTER);
375 icon.property_scale_factor().signal_changed().connect(
376 sigc::mem_fun(this, &WayfireNetworkInfo::update_icon));
377
378 update_active_connection();
379 handle_config_reload();
380 }
381
handle_config_reload()382 void WayfireNetworkInfo::handle_config_reload()
383 {
384 if ((std::string)status_font_opt == "default") {
385 status.unset_font();
386 } else {
387 status.override_font(
388 Pango::FontDescription((std::string)status_font_opt));
389 }
390
391 if (status_opt < NETWORK_STATUS_CONN_NAME)
392 {
393 if (status.get_parent())
394 button_content.remove(status);
395 } else
396 {
397 if (!status.get_parent())
398 {
399 button_content.pack_start(status);
400 button_content.show_all();
401 }
402 }
403
404 update_icon();
405 update_status();
406 }
407
~WayfireNetworkInfo()408 WayfireNetworkInfo::~WayfireNetworkInfo()
409 {
410 }
411