diff options
author | David Zeuthen <davidz@redhat.com> | 2009-04-11 21:18:06 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2009-04-11 21:18:06 -0400 |
commit | f6c622352767cdd7ffe94b78f58749b9e76ba71a (patch) | |
tree | fae12d4bae050b3d4afb942fe24a6c4b9257b212 | |
parent | c8bc2e5d25ec5a1a7fb882567e6c6b6dbc343798 (diff) | |
download | gnome-disk-utility-f6c622352767cdd7ffe94b78f58749b9e76ba71a.tar.gz gnome-disk-utility-f6c622352767cdd7ffe94b78f58749b9e76ba71a.tar.xz gnome-disk-utility-f6c622352767cdd7ffe94b78f58749b9e76ba71a.zip |
show an icon in the notification area if one or more disks are failing
Screenshot: http://people.freedesktop.org/~david/gdu-ata-smart-warning.png
Bug: http://bugzilla.gnome.org/show_bug.cgi?id=575572
-rw-r--r-- | src/notification/Makefile.am | 2 | ||||
-rw-r--r-- | src/notification/notification-main.c | 351 |
2 files changed, 292 insertions, 61 deletions
diff --git a/src/notification/Makefile.am b/src/notification/Makefile.am index 4a30c21..0a978a3 100644 --- a/src/notification/Makefile.am +++ b/src/notification/Makefile.am @@ -11,7 +11,7 @@ gdu_notification_daemon_SOURCES = \ gdu_notification_daemon_CPPFLAGS = \ -I$(top_srcdir)/src/ \ -I$(top_builddir)/src/ \ - -DG_LOG_DOMAIN=\"Palimpsest\" \ + -DG_LOG_DOMAIN=\"GduNotification\" \ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ $(DISABLE_DEPRECATED) \ -DGDU_API_IS_SUBJECT_TO_CHANGE \ diff --git a/src/notification/notification-main.c b/src/notification/notification-main.c index a5cb827..e4f19b2 100644 --- a/src/notification/notification-main.c +++ b/src/notification/notification-main.c @@ -31,6 +31,145 @@ /* ---------------------------------------------------------------------------------------------------- */ +typedef struct +{ + GduPool *pool; + + GtkStatusIcon *status_icon; + + /* List of GduDevice objects currently being unmounted */ + GList *devices_being_unmounted; + + /* List of GduDevice objects with ATA SMART failures */ + GList *ata_smart_failures; +} NotificationData; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void diff_sorted_lists (GList *list1, + GList *list2, + GCompareFunc compare, + GList **added, + GList **removed); + +static gint ptr_compare (gconstpointer a, gconstpointer b); + +static void update_unmount_dialogs (NotificationData *data); + +static void update_ata_smart_failures (NotificationData *data); + +static void update_status_icon (NotificationData *data); + +static void show_menu_for_status_icon (NotificationData *data); + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +update_all (NotificationData *data) +{ + update_unmount_dialogs (data); + update_ata_smart_failures (data); +} + +static void +on_device_added (GduPool *pool, + GduDevice *device, + gpointer user_data) +{ + NotificationData *data = user_data; + update_all (data); +} + +static void +on_device_removed (GduPool *pool, + GduDevice *device, + gpointer user_data) +{ + NotificationData *data = user_data; + update_all (data); +} + +static void +on_device_changed (GduPool *pool, + GduDevice *device, + gpointer user_data) +{ + NotificationData *data = user_data; + update_all (data); +} + +static void +on_device_job_changed (GduPool *pool, + GduDevice *device, + gpointer user_data) +{ + NotificationData *data = user_data; + update_all (data); +} + +static void +on_status_icon_activate (GtkStatusIcon *status_icon, + gpointer user_data) +{ + NotificationData *data = user_data; + show_menu_for_status_icon (data); +} + +static void +on_status_icon_popup_menu (GtkStatusIcon *status_icon, + guint button, + guint activate_time, + gpointer user_data) +{ + NotificationData *data = user_data; + show_menu_for_status_icon (data); +} + +static NotificationData * +notification_data_new (void) +{ + NotificationData *data; + + data = g_new0 (NotificationData, 1); + + data->pool = gdu_pool_new (); + g_signal_connect (data->pool, "device-added", G_CALLBACK (on_device_added), data); + g_signal_connect (data->pool, "device-removed", G_CALLBACK (on_device_removed), data); + g_signal_connect (data->pool, "device-changed", G_CALLBACK (on_device_changed), data); + g_signal_connect (data->pool, "device-job-changed", G_CALLBACK (on_device_job_changed), data); + + data->status_icon = gtk_status_icon_new (); + gtk_status_icon_set_visible (data->status_icon, FALSE); + gtk_status_icon_set_from_icon_name (data->status_icon, "gdu-warning"); + gtk_status_icon_set_tooltip_markup (data->status_icon, _("One or more disks are failing")); + g_signal_connect (data->status_icon, "activate", G_CALLBACK (on_status_icon_activate), data); + g_signal_connect (data->status_icon, "popup-menu", G_CALLBACK (on_status_icon_popup_menu), data); + + return data; +} + +static void +notification_data_free (NotificationData *data) +{ + g_signal_handlers_disconnect_by_func (data->status_icon, on_status_icon_activate, data); + g_signal_handlers_disconnect_by_func (data->status_icon, on_status_icon_popup_menu, data); + g_object_unref (data->status_icon); + + g_signal_handlers_disconnect_by_func (data->pool, on_device_added, data); + g_signal_handlers_disconnect_by_func (data->pool, on_device_removed, data); + g_signal_handlers_disconnect_by_func (data->pool, on_device_changed, data); + g_signal_handlers_disconnect_by_func (data->pool, on_device_job_changed, data); + g_object_unref (data->pool); + + g_list_foreach (data->devices_being_unmounted, (GFunc) g_object_unref, NULL); + g_list_free (data->devices_being_unmounted); + g_list_foreach (data->ata_smart_failures, (GFunc) g_object_unref, NULL); + g_list_free (data->ata_smart_failures); + g_free (data); +} + +/* ---------------------------------------------------------------------------------------------------- */ + static void diff_sorted_lists (GList *list1, GList *list2, @@ -75,17 +214,6 @@ diff_sorted_lists (GList *list1, } } -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - GduPool *pool; - - GList *devices_being_unmounted; -} NotificationData; - -/* ---------------------------------------------------------------------------------------------------- */ - static gint ptr_compare (gconstpointer a, gconstpointer b) { @@ -97,13 +225,15 @@ ptr_compare (gconstpointer a, gconstpointer b) return 0; } +/* ---------------------------------------------------------------------------------------------------- */ + static gboolean show_unmount_dialog (gpointer user_data) { GduDevice *device = GDU_DEVICE (user_data); GtkWidget *dialog; - g_debug ("show unmount dialog for %s", gdu_device_get_device_file (device)); + //g_debug ("show unmount dialog for %s", gdu_device_get_device_file (device)); dialog = gdu_slow_unmount_dialog_new (NULL, device); gtk_widget_show_all (GTK_WIDGET (dialog)); @@ -117,14 +247,14 @@ static void update_unmount_dialogs (NotificationData *data) { GList *devices; - GList *currently_being_unmounted; + GList *current; GList *l; GList *added; GList *removed; devices = gdu_pool_get_devices (data->pool); - currently_being_unmounted = NULL; + current = NULL; for (l = devices; l != NULL; l = l->next) { GduDevice *device = GDU_DEVICE (l->data); @@ -138,15 +268,15 @@ update_unmount_dialogs (NotificationData *data) gdu_device_job_get_initiated_by_uid (device) == getuid ())) continue; - currently_being_unmounted = g_list_prepend (currently_being_unmounted, device); + current = g_list_prepend (current, device); } - currently_being_unmounted = g_list_sort (currently_being_unmounted, ptr_compare); + current = g_list_sort (current, ptr_compare); data->devices_being_unmounted = g_list_sort (data->devices_being_unmounted, ptr_compare); added = removed = NULL; diff_sorted_lists (data->devices_being_unmounted, - currently_being_unmounted, + current, ptr_compare, &added, &removed); @@ -187,65 +317,165 @@ update_unmount_dialogs (NotificationData *data) /* ---------------------------------------------------------------------------------------------------- */ static void -on_device_added (GduPool *pool, - GduDevice *device, - gpointer user_data) +update_ata_smart_failures (NotificationData *data) { - NotificationData *data = user_data; - update_unmount_dialogs (data); -} + GList *devices; + GList *current; + GList *l; + GList *added; + GList *removed; -static void -on_device_removed (GduPool *pool, - GduDevice *device, - gpointer user_data) -{ - NotificationData *data = user_data; - update_unmount_dialogs (data); + devices = gdu_pool_get_devices (data->pool); + + current = NULL; + + for (l = devices; l != NULL; l = l->next) { + GduDevice *device = GDU_DEVICE (l->data); + + if (!gdu_device_drive_ata_smart_get_is_available (device)) + continue; + + if (!((gdu_device_drive_ata_smart_get_is_failing (device) && + gdu_device_drive_ata_smart_get_is_failing_valid (device)) || + gdu_device_drive_ata_smart_get_has_bad_sectors (device) || + gdu_device_drive_ata_smart_get_has_bad_attributes (device))) + continue; + + current = g_list_prepend (current, device); + } + + current = g_list_sort (current, ptr_compare); + data->ata_smart_failures = g_list_sort (data->ata_smart_failures, ptr_compare); + + added = removed = NULL; + diff_sorted_lists (data->ata_smart_failures, + current, + ptr_compare, + &added, + &removed); + + for (l = removed; l != NULL; l = l->next) { + GduDevice *device = GDU_DEVICE (l->data); + + //g_debug ("%s is no longer failing", gdu_device_get_device_file (device)); + + data->ata_smart_failures = g_list_remove (data->ata_smart_failures, device); + g_object_unref (device); + } + + for (l = added; l != NULL; l = l->next) { + GduDevice *device = GDU_DEVICE (l->data); + data->ata_smart_failures = g_list_prepend (data->ata_smart_failures, g_object_ref (device)); + + //g_debug ("%s is now failing", gdu_device_get_device_file (device)); + } + + g_list_foreach (devices, (GFunc) g_object_unref, NULL); + g_list_free (devices); + + update_status_icon (data); } +/* ---------------------------------------------------------------------------------------------------- */ + static void -on_device_changed (GduPool *pool, - GduDevice *device, - gpointer user_data) +update_status_icon (NotificationData *data) { - NotificationData *data = user_data; - update_unmount_dialogs (data); + gboolean show_icon; + + show_icon = FALSE; + if (g_list_length (data->ata_smart_failures) > 0) + show_icon = TRUE; + + if (!show_icon) { + gtk_status_icon_set_visible (data->status_icon, FALSE); + goto out; + } + + gtk_status_icon_set_visible (data->status_icon, TRUE); + out: + ; } +/* ---------------------------------------------------------------------------------------------------- */ + static void -on_device_job_changed (GduPool *pool, - GduDevice *device, - gpointer user_data) +on_menu_item_activated (GtkMenuItem *menu_item, + gpointer user_data) { NotificationData *data = user_data; - update_unmount_dialogs (data); -} + GduDevice *device; + GdkScreen *screen; + gchar *command_line; -static NotificationData * -notification_data_new (void) -{ - NotificationData *data; - data = g_new0 (NotificationData, 1); - data->pool = gdu_pool_new (); - g_signal_connect (data->pool, "device-added", G_CALLBACK (on_device_added), data); - g_signal_connect (data->pool, "device-removed", G_CALLBACK (on_device_removed), data); - g_signal_connect (data->pool, "device-changed", G_CALLBACK (on_device_changed), data); - g_signal_connect (data->pool, "device-job-changed", G_CALLBACK (on_device_job_changed), data); - return data; + device = GDU_DEVICE (g_object_get_data (G_OBJECT (menu_item), "gdu-device")); + + screen = gtk_status_icon_get_screen (data->status_icon); + command_line = g_strdup_printf ("palimpsest --show-drive=%s", gdu_device_get_device_file (device)); + gdk_spawn_command_line_on_screen (screen, command_line, NULL); + g_free (command_line); } static void -notification_data_free (NotificationData *data) +show_menu_for_status_icon (NotificationData *data) { - g_signal_handlers_disconnect_by_func (data->pool, on_device_added, data); - g_signal_handlers_disconnect_by_func (data->pool, on_device_removed, data); - g_signal_handlers_disconnect_by_func (data->pool, on_device_changed, data); - g_signal_handlers_disconnect_by_func (data->pool, on_device_job_changed, data); - g_object_unref (data->pool); - g_list_foreach (data->devices_being_unmounted, (GFunc) g_object_unref, NULL); - g_list_free (data->devices_being_unmounted); - g_free (data); + GtkWidget *menu; + GList *l; + + /* TODO: it would be nice to display something like + * + * Select a disk to get more information... + * ----------------------------------------------- + * [Icon] 80 GB ATA INTEL SSDSA2MH08 + * [Icon] 250GB WD 2500JB External + * + * but unfortunately that would require fucking with gtk+'s + * internals the same way the display-settings applet does + * it; see e.g. line 951 of + * + * http://svn.gnome.org/viewvc/gnome-settings-daemon/trunk/plugins/xrandr/gsd-xrandr-manager.c?revision=810&view=markup + */ + + menu = gtk_menu_new (); + for (l = data->ata_smart_failures; l != NULL; l = l->next) { + GduDevice *device = GDU_DEVICE (l->data); + GduPresentable *presentable; + gchar *device_name; + GdkPixbuf *pixbuf; + GtkWidget *image; + GtkWidget *menu_item; + + presentable = gdu_pool_get_drive_by_device (data->pool, device); + device_name = gdu_presentable_get_name (presentable); + + menu_item = gtk_image_menu_item_new_with_label (device_name); + g_object_set_data_full (G_OBJECT (menu_item), "gdu-device", g_object_ref (device), g_object_unref); + + pixbuf = gdu_util_get_pixbuf_for_presentable (presentable, GTK_ICON_SIZE_MENU); + image = gtk_image_new_from_pixbuf (pixbuf); + g_object_unref (pixbuf); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), image); + + g_signal_connect (menu_item, + "activate", + G_CALLBACK (on_menu_item_activated), + data); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); + + g_free (device_name); + g_object_unref (presentable); + } + gtk_widget_show_all (menu); + + gtk_menu_popup (GTK_MENU (menu), + NULL, + NULL, + gtk_status_icon_position_menu, + data->status_icon, + 0, + gtk_get_current_event_time ()); + } /* ---------------------------------------------------------------------------------------------------- */ @@ -264,6 +494,7 @@ main (int argc, char **argv) gtk_window_set_default_icon_name ("palimpsest"); data = notification_data_new (); + update_all (data); gtk_main (); |