3 * Copyright 2009-2015 Roy Marples <roy@marples.name>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include "dhcpcd-gtk.h"
30 static const char *copyright = "Copyright (c) 2009-2015 Roy Marples";
32 static GtkStatusIcon *sicon;
33 static GtkWidget *menu;
34 static GtkAboutDialog *about;
35 static guint bgscan_timer;
38 on_pref(_unused GObject *o, gpointer data)
41 prefs_show((DHCPCD_CONNECTION *)data);
53 ssid_hook(GtkMenuItem *item, _unused gpointer data)
58 scan = g_object_get_data(G_OBJECT(item), "dhcpcd_wi_scan");
59 wi = wi_scan_find(scan);
61 DHCPCD_CONNECTION *con;
63 con = dhcpcd_if_connection(wi->interface);
67 wpa = dhcpcd_wpa_find(con, wi->interface->ifname);
69 wpa_configure(wpa, scan);
75 on_about(_unused GtkMenuItem *item)
79 about = GTK_ABOUT_DIALOG(gtk_about_dialog_new());
80 gtk_about_dialog_set_version(about, VERSION);
81 gtk_about_dialog_set_copyright(about, copyright);
82 gtk_about_dialog_set_website_label(about, "dhcpcd Website");
83 gtk_about_dialog_set_website(about,
84 "http://roy.marples.name/projects/dhcpcd");
85 gtk_about_dialog_set_logo_icon_name(about,
86 "network-transmit-receive");
87 gtk_about_dialog_set_comments(about,
88 "Part of the dhcpcd project");
90 gtk_window_set_position(GTK_WINDOW(about), GTK_WIN_POS_MOUSE);
91 gtk_window_present(GTK_WINDOW(about));
92 gtk_dialog_run(GTK_DIALOG(about));
93 gtk_widget_hide(GTK_WIDGET(about));
97 is_associated(WI_SCAN *wi, DHCPCD_WI_SCAN *scan)
100 return dhcpcd_wi_associated(wi->interface, scan);
104 get_security_icon(unsigned int flags, const char **icon)
109 if (flags & WSF_SECURE) {
111 *icon = "network-wireless-encrypted";
113 *icon = "network-error";
117 *icon = "dialog-warning";
122 update_item(WI_SCAN *wi, WI_MENU *m, DHCPCD_WI_SCAN *scan)
133 g_object_set_data(G_OBJECT(m->menu), "dhcpcd_wi_scan", scan);
135 m->associated = is_associated(wi, scan);
137 sel = gtk_image_new_from_icon_name("dialog-ok-apply",
141 gtk_image_menu_item_set_image(
142 GTK_IMAGE_MENU_ITEM(m->menu), sel);
147 lbl = g_markup_printf_escaped("<b>%s</b>",
149 gtk_label_set_markup(GTK_LABEL(m->ssid), lbl);
152 gtk_label_set_text(GTK_LABEL(m->ssid), scan->ssid);
154 active = get_security_icon(scan->flags, &icon);
155 m->icon = gtk_image_new_from_icon_name(icon,
157 if (gtk_widget_get_sensitive(GTK_WIDGET(m->menu)) != active)
158 gtk_widget_set_sensitive(GTK_WIDGET(m->menu), active);
160 icon = get_strength_icon_name(scan->strength.value);
161 m->strength = gtk_image_new_from_icon_name(icon,
165 dhcpcd_wi_print_tooltip(tip, sizeof(tip), scan, 0);
166 gtk_widget_set_tooltip_text(m->menu, tip);
169 g_object_set_data(G_OBJECT(m->menu), "dhcpcd_wi_scan", scan);
173 create_menu(WI_SCAN *wis, DHCPCD_WI_SCAN *scan)
179 wim = g_malloc(sizeof(*wim));
181 wim->menu = gtk_image_menu_item_new();
182 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
183 gtk_container_add(GTK_CONTAINER(wim->menu), box);
185 wim->ssid = gtk_label_new(NULL);
186 gtk_misc_set_alignment(GTK_MISC(wim->ssid), 0.0, 0.5);
187 gtk_box_pack_start(GTK_BOX(box), wim->ssid, TRUE, TRUE, 0);
189 get_security_icon(scan->flags, &icon);
190 wim->icon = gtk_image_new_from_icon_name(icon,
192 gtk_box_pack_start(GTK_BOX(box), wim->icon, FALSE, FALSE, 0);
194 icon = get_strength_icon_name(scan->strength.value);
195 wim->strength = gtk_image_new_from_icon_name(icon,
197 gtk_box_pack_start(GTK_BOX(box), wim->strength, FALSE, FALSE, 0);
200 if (scan->wpa_flags[0] == '\0')
201 gtk_widget_set_tooltip_text(wim->menu, scan->bssid);
205 tip = g_strconcat(scan->bssid, " ", scan->wpa_flags, NULL);
206 gtk_widget_set_tooltip_text(wim->menu, tip);
210 update_item(wis, wim, scan);
212 g_signal_connect(G_OBJECT(wim->menu), "activate",
213 G_CALLBACK(ssid_hook), NULL);
214 g_object_set_data(G_OBJECT(wim->menu), "dhcpcd_wi_scan", scan);
220 menu_update_scans(WI_SCAN *wi, DHCPCD_WI_SCAN *scans)
227 if (wi->ifmenu == NULL) {
228 dhcpcd_wi_scans_free(wi->scans);
233 TAILQ_FOREACH_SAFE(wim, &wi->menus, next, win) {
235 for (s = scans; s; s = s->next) {
236 if (strcmp(wim->scan->ssid, s->ssid) == 0) {
237 /* If assoication changes, we
238 * need to remove the item to replace it */
239 if (wim->associated ==
240 is_associated(wi, s))
243 update_item(wi, wim, s);
249 TAILQ_REMOVE(&wi->menus, wim, next);
250 gtk_widget_destroy(wim->menu);
255 for (s = scans; s; s = s->next) {
258 TAILQ_FOREACH(wim, &wi->menus, next) {
259 if (strcmp(wim->scan->ssid, s->ssid) == 0) {
263 /* Assoicated scans are always first */
264 if (!is_associated(wi, s) &&
265 dhcpcd_wi_scan_compare(wim->scan, s) < 0)
269 wim = create_menu(wi, s);
270 TAILQ_INSERT_TAIL(&wi->menus, wim, next);
271 gtk_menu_shell_insert(GTK_MENU_SHELL(wi->ifmenu),
272 wim->menu, position);
273 gtk_widget_show_all(wim->menu);
277 dhcpcd_wi_scans_free(wi->scans);
280 if (gtk_widget_get_visible(wi->ifmenu))
281 gtk_menu_reposition(GTK_MENU(wi->ifmenu));
285 menu_remove_if(WI_SCAN *wi)
289 if (wi->ifmenu == NULL)
292 if (wi->ifmenu == menu)
295 gtk_widget_destroy(wi->ifmenu);
297 while ((wim = TAILQ_FIRST(&wi->menus))) {
298 TAILQ_REMOVE(&wi->menus, wim, next);
302 if (menu && gtk_widget_get_visible(menu))
303 gtk_menu_reposition(GTK_MENU(menu));
307 add_scans(WI_SCAN *wi)
314 if (wi->scans == NULL)
319 for (wis = wi->scans; wis; wis = wis->next) {
320 wim = create_menu(wi, wis);
321 TAILQ_INSERT_TAIL(&wi->menus, wim, next);
322 gtk_menu_shell_insert(GTK_MENU_SHELL(m),
323 wim->menu, is_associated(wi, wis) ? 0 : position);
337 g_source_remove(bgscan_timer);
341 TAILQ_FOREACH(wis, &wi_scans, next) {
343 while ((wim = TAILQ_FIRST(&wis->menus))) {
344 TAILQ_REMOVE(&wis->menus, wim, next);
350 gtk_widget_destroy(menu);
351 g_object_ref_sink(menu);
352 g_object_unref(menu);
358 menu_bgscan(gpointer data)
361 DHCPCD_CONNECTION *con;
364 if (menu == NULL || !gtk_widget_get_visible(menu)) {
369 con = (DHCPCD_CONNECTION *)data;
370 TAILQ_FOREACH(w, &wi_scans, next) {
371 if (w->interface->wireless && w->interface->up) {
372 wpa = dhcpcd_wpa_find(con, w->interface->ifname);
374 (!w->interface->up ||
375 dhcpcd_wpa_can_background_scan(wpa)))
376 dhcpcd_wpa_scan(wpa);
384 on_activate(GtkStatusIcon *icon)
387 GtkWidget *item, *image;
394 if ((w = TAILQ_FIRST(&wi_scans)) == NULL)
397 if ((l = TAILQ_LAST(&wi_scans, wi_scan_head)) && l != w) {
398 menu = gtk_menu_new();
399 TAILQ_FOREACH(l, &wi_scans, next) {
400 item = gtk_image_menu_item_new_with_label(
401 l->interface->ifname);
402 image = gtk_image_new_from_icon_name(
403 "network-wireless", GTK_ICON_SIZE_MENU);
404 gtk_image_menu_item_set_image(
405 GTK_IMAGE_MENU_ITEM(item), image);
406 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
407 l->ifmenu = add_scans(l);
408 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item),
412 w->ifmenu = menu = add_scans(w);
416 gtk_widget_show_all(GTK_WIDGET(menu));
417 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
418 gtk_status_icon_position_menu, icon,
419 1, gtk_get_current_event_time());
421 bgscan_timer = g_timeout_add(DHCPCD_WPA_SCAN_SHORT,
422 menu_bgscan, dhcpcd_if_connection(w->interface));
427 on_popup(GtkStatusIcon *icon, guint button, guint32 atime, gpointer data)
429 DHCPCD_CONNECTION *con;
431 GtkWidget *item, *image;
435 con = (DHCPCD_CONNECTION *)data;
436 mnu = (GtkMenu *)gtk_menu_new();
438 item = gtk_image_menu_item_new_with_mnemonic(_("_Preferences"));
439 image = gtk_image_new_from_icon_name("preferences-system-network",
441 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
442 if (dhcpcd_status(con, NULL) == DHC_DOWN)
443 gtk_widget_set_sensitive(item, false);
445 g_signal_connect(G_OBJECT(item), "activate",
446 G_CALLBACK(on_pref), data);
447 gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
449 item = gtk_separator_menu_item_new();
450 gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
452 item = gtk_image_menu_item_new_with_mnemonic(_("_About"));
453 image = gtk_image_new_from_icon_name("help-about",
455 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
456 g_signal_connect(G_OBJECT(item), "activate",
457 G_CALLBACK(on_about), icon);
458 gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
460 item = gtk_image_menu_item_new_with_mnemonic(_("_Quit"));
461 image = gtk_image_new_from_icon_name("application-exit",
463 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
464 g_signal_connect(G_OBJECT(item), "activate",
465 G_CALLBACK(on_quit), icon);
466 gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item);
468 gtk_widget_show_all(GTK_WIDGET(mnu));
469 gtk_menu_popup(GTK_MENU(mnu), NULL, NULL,
470 gtk_status_icon_position_menu, icon, button, atime);
472 gtk_menu_shell_select_first(GTK_MENU_SHELL(mnu), FALSE);
476 menu_init(GtkStatusIcon *icon, DHCPCD_CONNECTION *con)
479 g_signal_connect(G_OBJECT(icon), "activate",
480 G_CALLBACK(on_activate), con);
481 g_signal_connect(G_OBJECT(icon), "popup_menu",
482 G_CALLBACK(on_popup), con);
486 #if GTK_MAJOR_VERSION == 2
488 gtk_box_new(GtkOrientation o, gint s)
491 if (o == GTK_ORIENTATION_HORIZONTAL)
492 return gtk_hbox_new(false, s);
494 return gtk_vbox_new(false, s);