/* * dhcpcd-gtk * Copyright 2009-2015 Roy Marples * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include "dhcpcd-gtk.h" static const char *copyright = "Copyright (c) 2009-2015 Roy Marples"; static GtkStatusIcon *sicon; static GtkWidget *menu; static GtkAboutDialog *about; static guint bgscan_timer; static void on_pref(_unused GObject *o, gpointer data) { prefs_show((DHCPCD_CONNECTION *)data); } static void on_quit(void) { wpa_abort(); gtk_main_quit(); } static void ssid_hook(GtkMenuItem *item, _unused gpointer data) { DHCPCD_WI_SCAN *scan; WI_SCAN *wi; scan = g_object_get_data(G_OBJECT(item), "dhcpcd_wi_scan"); wi = wi_scan_find(scan); if (wi) { DHCPCD_CONNECTION *con; con = dhcpcd_if_connection(wi->interface); if (con) { DHCPCD_WPA *wpa; wpa = dhcpcd_wpa_find(con, wi->interface->ifname); if (wpa) wpa_configure(wpa, scan); } } } static void on_about(_unused GtkMenuItem *item) { if (about == NULL) { about = GTK_ABOUT_DIALOG(gtk_about_dialog_new()); gtk_about_dialog_set_version(about, VERSION); gtk_about_dialog_set_copyright(about, copyright); gtk_about_dialog_set_website_label(about, "dhcpcd Website"); gtk_about_dialog_set_website(about, "http://roy.marples.name/projects/dhcpcd"); gtk_about_dialog_set_logo_icon_name(about, "network-transmit-receive"); gtk_about_dialog_set_comments(about, "Part of the dhcpcd project"); } gtk_window_set_position(GTK_WINDOW(about), GTK_WIN_POS_MOUSE); gtk_window_present(GTK_WINDOW(about)); gtk_dialog_run(GTK_DIALOG(about)); gtk_widget_hide(GTK_WIDGET(about)); } static bool is_associated(WI_SCAN *wi, DHCPCD_WI_SCAN *scan) { return dhcpcd_wi_associated(wi->interface, scan); } static bool get_security_icon(unsigned int flags, const char **icon) { bool active; active = true; if (flags & WSF_SECURE) { if (flags & WSF_PSK) *icon = "network-wireless-encrypted"; else { *icon = "network-error"; active = false; } } else *icon = "dialog-warning"; return active; } static void update_item(WI_SCAN *wi, WI_MENU *m, DHCPCD_WI_SCAN *scan) { const char *icon; GtkWidget *sel; bool active; #if 0 char tip[256]; #endif m->scan = scan; g_object_set_data(G_OBJECT(m->menu), "dhcpcd_wi_scan", scan); m->associated = is_associated(wi, scan); if (m->associated) sel = gtk_image_new_from_icon_name("dialog-ok-apply", GTK_ICON_SIZE_MENU); else sel = NULL; gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(m->menu), sel); if (m->associated) { gchar *lbl; lbl = g_markup_printf_escaped("%s", scan->ssid); gtk_label_set_markup(GTK_LABEL(m->ssid), lbl); g_free(lbl); } else gtk_label_set_text(GTK_LABEL(m->ssid), scan->ssid); active = get_security_icon(scan->flags, &icon); m->icon = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_MENU); if (gtk_widget_get_sensitive(GTK_WIDGET(m->menu)) != active) gtk_widget_set_sensitive(GTK_WIDGET(m->menu), active); icon = get_strength_icon_name(scan->strength.value); m->strength = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_MENU); #if 0 dhcpcd_wi_print_tooltip(tip, sizeof(tip), scan, 0); gtk_widget_set_tooltip_text(m->menu, tip); #endif g_object_set_data(G_OBJECT(m->menu), "dhcpcd_wi_scan", scan); } static WI_MENU * create_menu(WI_SCAN *wis, DHCPCD_WI_SCAN *scan) { WI_MENU *wim; GtkWidget *box; const char *icon; wim = g_malloc(sizeof(*wim)); wim->scan = scan; wim->menu = gtk_image_menu_item_new(); box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); gtk_container_add(GTK_CONTAINER(wim->menu), box); wim->ssid = gtk_label_new(NULL); gtk_misc_set_alignment(GTK_MISC(wim->ssid), 0.0, 0.5); gtk_box_pack_start(GTK_BOX(box), wim->ssid, TRUE, TRUE, 0); get_security_icon(scan->flags, &icon); wim->icon = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_MENU); gtk_box_pack_start(GTK_BOX(box), wim->icon, FALSE, FALSE, 0); icon = get_strength_icon_name(scan->strength.value); wim->strength = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_MENU); gtk_box_pack_start(GTK_BOX(box), wim->strength, FALSE, FALSE, 0); #if 0 if (scan->wpa_flags[0] == '\0') gtk_widget_set_tooltip_text(wim->menu, scan->bssid); else { char *tip; tip = g_strconcat(scan->bssid, " ", scan->wpa_flags, NULL); gtk_widget_set_tooltip_text(wim->menu, tip); g_free(tip); } #endif update_item(wis, wim, scan); g_signal_connect(G_OBJECT(wim->menu), "activate", G_CALLBACK(ssid_hook), NULL); g_object_set_data(G_OBJECT(wim->menu), "dhcpcd_wi_scan", scan); return wim; } void menu_update_scans(WI_SCAN *wi, DHCPCD_WI_SCAN *scans) { WI_MENU *wim, *win; DHCPCD_WI_SCAN *s; bool found; int position; if (wi->ifmenu == NULL) { dhcpcd_wi_scans_free(wi->scans); wi->scans = scans; return; } TAILQ_FOREACH_SAFE(wim, &wi->menus, next, win) { found = false; for (s = scans; s; s = s->next) { if (strcmp(wim->scan->ssid, s->ssid) == 0) { /* If assoication changes, we * need to remove the item to replace it */ if (wim->associated == is_associated(wi, s)) { found = true; update_item(wi, wim, s); } break; } } if (!found) { TAILQ_REMOVE(&wi->menus, wim, next); gtk_widget_destroy(wim->menu); g_free(wim); } } for (s = scans; s; s = s->next) { found = false; position = 0; TAILQ_FOREACH(wim, &wi->menus, next) { if (strcmp(wim->scan->ssid, s->ssid) == 0) { found = true; break; } /* Assoicated scans are always first */ if (!is_associated(wi, s) && dhcpcd_wi_scan_compare(wim->scan, s) < 0) position++; } if (!found) { wim = create_menu(wi, s); TAILQ_INSERT_TAIL(&wi->menus, wim, next); gtk_menu_shell_insert(GTK_MENU_SHELL(wi->ifmenu), wim->menu, position); gtk_widget_show_all(wim->menu); } } dhcpcd_wi_scans_free(wi->scans); wi->scans = scans; if (gtk_widget_get_visible(wi->ifmenu)) gtk_menu_reposition(GTK_MENU(wi->ifmenu)); } void menu_remove_if(WI_SCAN *wi) { WI_MENU *wim; if (wi->ifmenu == NULL) return; if (wi->ifmenu == menu) menu = NULL; gtk_widget_destroy(wi->ifmenu); wi->ifmenu = NULL; while ((wim = TAILQ_FIRST(&wi->menus))) { TAILQ_REMOVE(&wi->menus, wim, next); g_free(wim); } if (menu && gtk_widget_get_visible(menu)) gtk_menu_reposition(GTK_MENU(menu)); } static GtkWidget * add_scans(WI_SCAN *wi) { GtkWidget *m; DHCPCD_WI_SCAN *wis; WI_MENU *wim; int position; if (wi->scans == NULL) return NULL; m = gtk_menu_new(); position = 0; for (wis = wi->scans; wis; wis = wis->next) { wim = create_menu(wi, wis); TAILQ_INSERT_TAIL(&wi->menus, wim, next); gtk_menu_shell_insert(GTK_MENU_SHELL(m), wim->menu, is_associated(wi, wis) ? 0 : position); position++; } return m; } void menu_abort(void) { WI_SCAN *wis; WI_MENU *wim; if (bgscan_timer) { g_source_remove(bgscan_timer); bgscan_timer = 0; } TAILQ_FOREACH(wis, &wi_scans, next) { wis->ifmenu = NULL; while ((wim = TAILQ_FIRST(&wis->menus))) { TAILQ_REMOVE(&wis->menus, wim, next); g_free(wim); } } if (menu != NULL) { gtk_widget_destroy(menu); g_object_ref_sink(menu); g_object_unref(menu); menu = NULL; } } static gboolean menu_bgscan(gpointer data) { WI_SCAN *w; DHCPCD_CONNECTION *con; DHCPCD_WPA *wpa; if (menu == NULL || !gtk_widget_get_visible(menu)) { bgscan_timer = 0; return FALSE; } con = (DHCPCD_CONNECTION *)data; TAILQ_FOREACH(w, &wi_scans, next) { if (w->interface->wireless && w->interface->up) { wpa = dhcpcd_wpa_find(con, w->interface->ifname); if (wpa && (!w->interface->up || dhcpcd_wpa_can_background_scan(wpa))) dhcpcd_wpa_scan(wpa); } } return TRUE; } static void on_activate(GtkStatusIcon *icon) { WI_SCAN *w, *l; GtkWidget *item, *image; sicon = icon; notify_close(); prefs_abort(); menu_abort(); if ((w = TAILQ_FIRST(&wi_scans)) == NULL) return; if ((l = TAILQ_LAST(&wi_scans, wi_scan_head)) && l != w) { menu = gtk_menu_new(); TAILQ_FOREACH(l, &wi_scans, next) { item = gtk_image_menu_item_new_with_label( l->interface->ifname); image = gtk_image_new_from_icon_name( "network-wireless", GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image( GTK_IMAGE_MENU_ITEM(item), image); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); l->ifmenu = add_scans(l); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), l->ifmenu); } } else { w->ifmenu = menu = add_scans(w); } if (menu) { gtk_widget_show_all(GTK_WIDGET(menu)); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, gtk_status_icon_position_menu, icon, 1, gtk_get_current_event_time()); bgscan_timer = g_timeout_add(DHCPCD_WPA_SCAN_SHORT, menu_bgscan, dhcpcd_if_connection(w->interface)); } } static void on_popup(GtkStatusIcon *icon, guint button, guint32 atime, gpointer data) { DHCPCD_CONNECTION *con; GtkMenu *mnu; GtkWidget *item, *image; notify_close(); con = (DHCPCD_CONNECTION *)data; mnu = (GtkMenu *)gtk_menu_new(); item = gtk_image_menu_item_new_with_mnemonic(_("_Preferences")); image = gtk_image_new_from_icon_name("preferences-system-network", GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); if (dhcpcd_status(con, NULL) == DHC_DOWN) gtk_widget_set_sensitive(item, false); else g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(on_pref), data); gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item); item = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item); item = gtk_image_menu_item_new_with_mnemonic(_("_About")); image = gtk_image_new_from_icon_name("help-about", GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(on_about), icon); gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item); item = gtk_image_menu_item_new_with_mnemonic(_("_Quit")); image = gtk_image_new_from_icon_name("application-exit", GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(on_quit), icon); gtk_menu_shell_append(GTK_MENU_SHELL(mnu), item); gtk_widget_show_all(GTK_WIDGET(mnu)); gtk_menu_popup(GTK_MENU(mnu), NULL, NULL, gtk_status_icon_position_menu, icon, button, atime); if (button == 0) gtk_menu_shell_select_first(GTK_MENU_SHELL(mnu), FALSE); } void menu_init(GtkStatusIcon *icon, DHCPCD_CONNECTION *con) { g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(on_activate), con); g_signal_connect(G_OBJECT(icon), "popup_menu", G_CALLBACK(on_popup), con); } #if GTK_MAJOR_VERSION == 2 GtkWidget * gtk_box_new(GtkOrientation o, gint s) { if (o == GTK_ORIENTATION_HORIZONTAL) return gtk_hbox_new(false, s); else return gtk_vbox_new(false, s); } #endif