[gnome-disk-utility] show an icon in the notification area if one or more disks are failing



commit f6c622352767cdd7ffe94b78f58749b9e76ba71a
Author: David Zeuthen <davidz redhat com>
Date:   Sat Apr 11 21:18:06 2009 -0400

    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
---
 src/notification/Makefile.am         |    2 +-
 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 ();
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]