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