[gnome-disk-utility] Add gnome-settings-daemon plug-in to warn user on SMART failures



commit 314b9d0f29cdf8a8f88dc22b6ab8c520f2a83eb0
Author: David Zeuthen <zeuthen gmail com>
Date:   Mon Nov 12 12:48:44 2012 -0500

    Add gnome-settings-daemon plug-in to warn user on SMART failures
    
    This feature was cut in the rewrite that happened for 3.4 - just
    adding it back.
    
    Signed-off-by: David Zeuthen <zeuthen gmail com>

 configure.ac                                       |    9 +
 data/Makefile.am                                   |    5 +-
 ...ettings-daemon.plugins.gdu-sd.gschema.xml.in.in |   15 +
 src/Makefile.am                                    |    2 +-
 src/notify/Makefile.am                             |   95 +++++
 src/notify/gdu-sd-plugin.gnome-settings-plugin.in  |    8 +
 src/notify/gdusdmonitor.c                          |  368 ++++++++++++++++++++
 src/notify/gdusdmonitor.h                          |   31 ++
 src/notify/gdusdplugin.c                           |  124 +++++++
 src/notify/gdusdplugin.h                           |   33 ++
 src/notify/testplugin.c                            |   74 ++++
 11 files changed, 762 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a2ccf68..4deac78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -88,6 +88,8 @@ LIBSECRET1_REQUIRED=0.7
 PWQUALITY_REQUIRED=1.0.0
 CANBERRA_REQUIRED=0.1
 LIBDVDREAD_REQUIRED=4.2.0
+GSD_PLUGIN_REQUIRED=3.6
+LIBNOTIFY_REQUIRED=0.7
 
 PKG_CHECK_MODULES(GLIB2, [gmodule-2.0 gio-unix-2.0 >= $GLIB2_REQUIRED])
 PKG_CHECK_MODULES(UDISKS2, [udisks2 >= $UDISKS2_REQUIRED])
@@ -96,6 +98,11 @@ PKG_CHECK_MODULES(LIBSECRET1, [libsecret-1 >= $LIBSECRET1_REQUIRED])
 PKG_CHECK_MODULES(PWQUALITY, [pwquality >= $PWQUALITY_REQUIRED])
 PKG_CHECK_MODULES(CANBERRA, [libcanberra-gtk3 >= $CANBERRA_REQUIRED])
 PKG_CHECK_MODULES(LIBDVDREAD, [dvdread >= $LIBDVDREAD_REQUIRED])
+PKG_CHECK_MODULES(GSD_PLUGIN, [gnome-settings-daemon >= GSD_PLUGIN_REQUIRED])
+PKG_CHECK_MODULES(LIBNOTIFY, [libnotify >= LIBNOTIFY_REQUIRED])
+
+gsd_plugindir='${libdir}/gnome-settings-daemon-3.0'
+AC_SUBST([gsd_plugindir])
 
 GLIB_GSETTINGS
 
@@ -147,6 +154,7 @@ src/Makefile
 src/libgdu/Makefile
 src/disks/Makefile
 src/disk-image-mounter/Makefile
+src/notify/Makefile
 po/Makefile.in
 data/Makefile
 data/icons/Makefile
@@ -158,6 +166,7 @@ data/icons/48x48/Makefile
 data/icons/256x256/Makefile
 data/icons/scalable/Makefile
 data/org.gnome.Disks.gschema.xml.in
+data/org.gnome.settings-daemon.plugins.gdu-sd.gschema.xml.in
 data/ui/Makefile
 doc/Makefile
 doc/man/Makefile
diff --git a/data/Makefile.am b/data/Makefile.am
index 5924742..33ac654 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -7,7 +7,10 @@ desktop_in_files = gnome-disks.desktop.in gnome-disk-image-mounter.desktop.in
 desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
 
 # GSettings schemas
-gsettings_SCHEMAS = org.gnome.Disks.gschema.xml
+gsettings_SCHEMAS = 						\
+	org.gnome.Disks.gschema.xml 				\
+	org.gnome.settings-daemon.plugins.gdu-sd.gschema.xml	\
+	$(NULL)
 
 @INTLTOOL_DESKTOP_RULE@
 @INTLTOOL_XML_NOMERGE_RULE@
diff --git a/data/org.gnome.settings-daemon.plugins.gdu-sd.gschema.xml.in.in b/data/org.gnome.settings-daemon.plugins.gdu-sd.gschema.xml.in.in
new file mode 100644
index 0000000..d16aa7f
--- /dev/null
+++ b/data/org.gnome.settings-daemon.plugins.gdu-sd.gschema.xml.in.in
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+  <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.settings-daemon.plugins.gdu-sd" path="/org/gnome/settings-daemon/plugins/gdu-sd/">
+    <key name="active" type="b">
+      <default>true</default>
+      <_summary>Activation of this plugin</_summary>
+      <_description>Whether this plugin would be activated by gnome-settings-daemon or not</_description>
+    </key>
+    <key name="priority" type="i">
+      <default>1</default>
+      <_summary>Priority to use for this plugin</_summary>
+      <_description>Priority to use for this plugin in gnome-settings-daemon startup queue</_description>
+    </key>
+  </schema>
+</schemalist>
diff --git a/src/Makefile.am b/src/Makefile.am
index 4aceda0..b1d8e95 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = libgdu disks disk-image-mounter
+SUBDIRS = libgdu disks disk-image-mounter notify
 
 clean-local :
 	rm -f *~
diff --git a/src/notify/Makefile.am b/src/notify/Makefile.am
new file mode 100644
index 0000000..e20323b
--- /dev/null
+++ b/src/notify/Makefile.am
@@ -0,0 +1,95 @@
+
+NULL =
+
+gdu_sd_plugin_name = gdu-sd
+
+gdu_sd_plugindir=$(gsd_plugindir)
+
+gdu_sd_plugin_LTLIBRARIES = 					\
+	libgdu-sd.la						\
+	$(NULL)
+
+libgdu_sd_la_SOURCES = 						\
+	gdusdplugin.h		gdusdplugin.c			\
+	gdusdmonitor.h		gdusdmonitor.c			\
+	$(NULL)
+
+libgdu_sd_la_CPPFLAGS = 					\
+	-DGNOME_SETTINGS_LOCALEDIR=\""$(datadir)/locale"\" 	\
+	$(AM_CPPFLAGS)
+
+libgdu_sd_la_CFLAGS = 						\
+	$(GSD_PLUGIN_CFLAGS)					\
+	$(GLIB2_CFLAGS)						\
+	$(GTK3_CFLAGS)						\
+	$(UDISKS2_CFLAGS)					\
+	$(LIBNOTIFY_CFLAGS)					\
+	$(NULL)
+
+libgdu_sd_la_LDFLAGS = 						\
+	-export-dynamic 					\
+	-export-symbols-regex '^register_gnome_settings_plugin' \
+	-module 						\
+	-avoid-version 						\
+	-no-undefined 						\
+	$(NULL)
+
+libgdu_sd_la_LIBADD  = 						\
+	$(GSD_PLUGIN_LIBS)					\
+	$(GLIB2_LIBS)						\
+	$(GTK3_LIBS)						\
+	$(UDISKS2_LIBS)						\
+	$(LIBNOTIFY_LIBS)					\
+	$(NULL)
+
+# ----------------------------------------------------------------------
+
+noinst_PROGRAMS = test-plugin
+
+test_plugin_SOURCES = 						\
+	testplugin.c						\
+	gdusdmonitor.h		gdusdmonitor.c			\
+	$(NULL)
+
+test_plugin_CPPFLAGS =						\
+	$(NULL)
+
+test_plugin_CFLAGS =						\
+	$(GLIB2_CFLAGS)						\
+	$(GTK3_CFLAGS)						\
+	$(UDISKS2_CFLAGS)					\
+	$(LIBNOTIFY_CFLAGS)					\
+	$(NULL)
+
+test_plugin_LDADD =						\
+	$(GLIB2_LIBS)						\
+	$(GTK3_LIBS)						\
+	$(UDISKS2_LIBS)						\
+	$(LIBNOTIFY_LIBS)					\
+	$(NULL)
+
+# ----------------------------------------------------------------------
+
+gdu_sd_plugin_in_files = 					\
+	gdu-sd-plugin.gnome-settings-plugin.in			\
+	$(NULL)
+
+gdu_sd_plugin_DATA = $(gdu_sd_plugin_in_files:.gnome-settings-plugin.in=.gnome-settings-plugin)
+
+%.gnome-settings-plugin: %.gnome-settings-plugin.in
+	LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@
+
+EXTRA_DIST = 				\
+	$(gdu_sd_plugin_in_files)	\
+	$(NULL)
+
+CLEANFILES = 				\
+	$(gdu_sd_plugin_DATA)		\
+	$(NULL)
+
+DISTCLEANFILES =			\
+	$(gdu_sd_plugin_DATA)		\
+	$(NULL)
+
+clean-local :
+	rm -f *~
diff --git a/src/notify/gdu-sd-plugin.gnome-settings-plugin.in b/src/notify/gdu-sd-plugin.gnome-settings-plugin.in
new file mode 100644
index 0000000..6c9aac6
--- /dev/null
+++ b/src/notify/gdu-sd-plugin.gnome-settings-plugin.in
@@ -0,0 +1,8 @@
+[GNOME Settings Plugin]
+Module=gdu-sd
+IAge=0
+_Name=Disks Problem Monitor
+_Description=Warns about problems with disks and storage devices
+Authors=David Zeuthen
+Copyright=Copyright © 2012 David Zeuthen
+Website=
diff --git a/src/notify/gdusdmonitor.c b/src/notify/gdusdmonitor.c
new file mode 100644
index 0000000..7267663
--- /dev/null
+++ b/src/notify/gdusdmonitor.c
@@ -0,0 +1,368 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: David Zeuthen <zeuthen gmail com>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gio/gio.h>
+#include <libnotify/notify.h>
+
+#include <udisks/udisks.h>
+
+#include "gdusdmonitor.h"
+
+struct GduSdMonitorClass;
+typedef struct GduSdMonitorClass GduSdMonitorClass;
+
+struct GduSdMonitorClass {
+  GObjectClass parent_class;
+};
+
+struct GduSdMonitor {
+  GObject parent_instance;
+
+  UDisksClient *client;
+
+  /* ATA SMART problems */
+  GList *ata_smart_problems;
+  NotifyNotification *ata_smart_notification;
+};
+
+G_DEFINE_TYPE (GduSdMonitor, gdu_sd_monitor, G_TYPE_OBJECT);
+
+static void on_client_changed (UDisksClient *client,
+                               gpointer      user_data);
+
+static void update (GduSdMonitor *monitor);
+
+static void
+udisks_client_cb (GObject      *source_object,
+                  GAsyncResult *res,
+                  gpointer      user_data)
+{
+  GduSdMonitor *monitor = GDU_SD_MONITOR (user_data);
+  GError *error = NULL;
+
+  monitor->client = udisks_client_new_finish (res, &error);
+  if (monitor->client == NULL)
+    {
+      g_warning ("Error initializing udisks client: %s (%s, %d)",
+                 error->message, g_quark_to_string (error->domain), error->code);
+      g_clear_error (&error);
+    }
+  else
+    {
+      g_signal_connect (monitor->client,
+                        "changed",
+                        G_CALLBACK (on_client_changed),
+                        monitor);
+      update (monitor);
+    }
+  g_object_unref (monitor);
+}
+
+static void
+gdu_sd_monitor_init (GduSdMonitor *monitor)
+{
+  udisks_client_new (NULL, /* GCancellable* */
+                     udisks_client_cb,
+                     g_object_ref (monitor));
+}
+
+static void
+gdu_sd_monitor_finalize (GObject *object)
+{
+  GduSdMonitor *monitor = GDU_SD_MONITOR (object);
+
+  if (monitor->client != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (monitor->client, on_client_changed, monitor);
+      g_clear_object (&monitor->client);
+    }
+
+  g_list_free_full (monitor->ata_smart_problems, g_object_unref);
+  g_clear_object (&monitor->ata_smart_notification);
+
+  G_OBJECT_CLASS (gdu_sd_monitor_parent_class)->finalize (object);
+}
+
+static void
+gdu_sd_monitor_class_init (GduSdMonitorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gdu_sd_monitor_finalize;
+}
+
+GduSdMonitor *
+gdu_sd_monitor_new (void)
+{
+  return GDU_SD_MONITOR (g_object_new (GDU_TYPE_SD_MONITOR, NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+diff_sorted_lists (GList         *list1,
+                   GList         *list2,
+                   GCompareFunc   compare,
+                   GList        **added,
+                   GList        **removed)
+{
+  int order;
+
+  *added = *removed = NULL;
+
+  while (list1 != NULL &&
+         list2 != NULL)
+    {
+      order = (*compare) (list1->data, list2->data);
+      if (order < 0)
+        {
+          *removed = g_list_prepend (*removed, list1->data);
+          list1 = list1->next;
+        }
+      else if (order > 0)
+        {
+          *added = g_list_prepend (*added, list2->data);
+          list2 = list2->next;
+        }
+      else
+        { /* same item */
+          list1 = list1->next;
+          list2 = list2->next;
+        }
+    }
+
+  while (list1 != NULL)
+    {
+      *removed = g_list_prepend (*removed, list1->data);
+      list1 = list1->next;
+    }
+  while (list2 != NULL)
+    {
+      *added = g_list_prepend (*added, list2->data);
+      list2 = list2->next;
+    }
+}
+
+static gint
+ptr_compare (gconstpointer a, gconstpointer b)
+{
+  if (a > b)
+    return 1;
+  else if (a < b)
+    return -1;
+  else
+    return 0;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef gboolean (*CheckProblemFunc) (GduSdMonitor   *monitor,
+                                      UDisksObject   *object);
+
+static void
+update_problems (GduSdMonitor      *monitor,
+                 GList            **problem_list,
+                 CheckProblemFunc   check_func)
+{
+  GList *want = NULL;
+  GList *added = NULL;
+  GList *removed = NULL;
+  GList *objects;
+  GList *l;
+
+  objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (monitor->client));
+  for (l = objects; l != NULL; l = l->next)
+    {
+      UDisksObject *object = UDISKS_OBJECT (l->data);
+      if (check_func (monitor, object))
+        want = g_list_prepend (want, object);
+    }
+
+  want = g_list_sort (want, ptr_compare);
+  *problem_list = g_list_sort (*problem_list, ptr_compare);
+  diff_sorted_lists (*problem_list,
+                     want,
+                     ptr_compare,
+                     &added,
+                     &removed);
+
+  for (l = removed; l != NULL; l = l->next)
+    {
+      UDisksObject *object = UDISKS_OBJECT (l->data);
+      *problem_list = g_list_remove (*problem_list, object);
+      g_object_unref (object);
+    }
+
+  for (l = added; l != NULL; l = l->next)
+    {
+      UDisksObject *object = UDISKS_OBJECT (l->data);
+      *problem_list = g_list_prepend (*problem_list, g_object_ref (object));
+    }
+
+  g_list_free (removed);
+  g_list_free (added);
+  g_list_free (want);
+  g_list_free_full (objects, g_object_unref);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_examine_action_clicked (NotifyNotification  *notification,
+                           const gchar         *action,
+                           gpointer             user_data)
+{
+  GduSdMonitor *monitor = GDU_SD_MONITOR (user_data);
+  const gchar *device_file = NULL;
+  gchar *command_line = NULL;
+  GAppInfo *app_info = NULL;
+  GError *error = NULL;
+
+  if (g_strcmp0 (action, "examine-smart") == 0)
+    {
+      if (monitor->ata_smart_problems != NULL)
+        {
+          UDisksObject *object = UDISKS_OBJECT (monitor->ata_smart_problems->data);
+          if (object != NULL)
+            {
+              UDisksDrive *drive = udisks_object_peek_drive (object);
+              if (drive != NULL)
+                {
+                  UDisksBlock *block = udisks_client_get_block_for_drive (monitor->client,
+                                                                          drive,
+                                                                          TRUE); /* get_physical */
+                  if (block != NULL)
+                    {
+                      device_file = udisks_block_get_device (block);
+                      g_object_ref (block);
+                    }
+                }
+            }
+        }
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
+
+  if (device_file != NULL)
+    command_line = g_strdup_printf ("gnome-disks --block-device %s", device_file);
+  else
+    command_line = g_strdup_printf ("gnome-disks");
+
+
+  app_info = g_app_info_create_from_commandline (command_line,
+                                                 NULL, /* application name */
+                                                 G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION,
+                                                 NULL);
+  if (!g_app_info_launch (app_info, NULL, NULL, &error))
+    {
+      g_warning ("Error launching gnome-disks: %s (%s, %d)",
+                 error->message, g_quark_to_string (error->domain), error->code);
+      g_clear_error (&error);
+    }
+  g_clear_object (&app_info);
+  g_free (command_line);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+update_notification (GduSdMonitor        *monitor,
+                     GList               *problems,
+                     NotifyNotification **notification,
+                     const gchar         *title,
+                     const gchar         *text,
+                     const gchar         *icon_name,
+                     const gchar         *action,
+                     const gchar         *action_label)
+{
+  if (g_list_length (problems) > 0)
+    {
+      g_warn_if_fail (*notification == NULL);
+      *notification = notify_notification_new (title, text, icon_name);
+      notify_notification_set_urgency (*notification, NOTIFY_URGENCY_CRITICAL);
+      notify_notification_set_timeout (*notification, NOTIFY_EXPIRES_NEVER);
+      notify_notification_set_hint_string (*notification, "desktop-entry", "gnome-disks");
+      notify_notification_add_action (*notification,
+                                      action,
+                                      action_label,
+                                      (NotifyActionCallback) on_examine_action_clicked,
+                                      monitor,
+                                      NULL);
+      notify_notification_show (*notification, NULL);
+    }
+  else
+    {
+      if (*notification != NULL)
+        {
+          notify_notification_close (*notification, NULL);
+          g_clear_object (notification);
+        }
+    }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+
+static gboolean
+check_for_ata_smart_problem (GduSdMonitor  *monitor,
+                             UDisksObject  *object)
+{
+  gboolean ret = FALSE;
+  UDisksDriveAta *ata = NULL;
+
+  ata = udisks_object_peek_drive_ata (object);
+  if (ata == NULL)
+    goto out;
+
+  /* For now we only check if the SMART status is set to FAIL
+   *
+   * - could add other heuristics (attributes failing, many bad sectors, temperature, etc.)
+   * - could check if user wants to ignore the failure
+   */
+  if (!udisks_drive_ata_get_smart_failing (ata))
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+static void
+update (GduSdMonitor *monitor)
+{
+  update_problems (monitor, &monitor->ata_smart_problems, check_for_ata_smart_problem);
+  update_notification (monitor,
+                       monitor->ata_smart_problems,
+                       &monitor->ata_smart_notification,
+                       /* Translators: This is used as the title of the SMART failure notification */
+                       C_("notify-smart", "Hard Disk Problems Detected"),
+                       /* Translators: This is used as the text of the SMART failure notification */
+                       C_("notify-smart", "One or more hard disks are likely to fail soon."),
+                       "gnome-disks",
+                       "examine-smart",
+                       /* Translators: Text for button in SMART failure notification */
+                       C_("notify-smart", "Examine"));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+on_client_changed (UDisksClient *client,
+                   gpointer      user_data)
+{
+  GduSdMonitor *monitor = GDU_SD_MONITOR (user_data);
+  update (monitor);
+}
diff --git a/src/notify/gdusdmonitor.h b/src/notify/gdusdmonitor.h
new file mode 100644
index 0000000..5f34974
--- /dev/null
+++ b/src/notify/gdusdmonitor.h
@@ -0,0 +1,31 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: David Zeuthen <zeuthen gmail com>
+ */
+
+#ifndef __GDU_SD_MONITOR_H__
+#define __GDU_SD_MONITOR_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gmodule.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_SD_MONITOR  (gdu_sd_monitor_get_type ())
+#define GDU_SD_MONITOR(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_SD_MONITOR, GduSdMonitor))
+#define GDU_IS_SD_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_SD_MONITOR))
+
+struct GduSdMonitor;
+typedef struct GduSdMonitor GduSdMonitor;
+
+GType         gdu_sd_monitor_get_type (void) G_GNUC_CONST;
+GduSdMonitor *gdu_sd_monitor_new (void);
+
+G_END_DECLS
+
+#endif /* __GDU_SD_MONITOR_H__ */
diff --git a/src/notify/gdusdplugin.c b/src/notify/gdusdplugin.c
new file mode 100644
index 0000000..19ef845
--- /dev/null
+++ b/src/notify/gdusdplugin.c
@@ -0,0 +1,124 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: David Zeuthen <zeuthen gmail com>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gio/gio.h>
+
+#include "gdusdplugin.h"
+#include "gdusdmonitor.h"
+
+#include <gnome-settings-daemon/gnome-settings-plugin.h>
+
+struct GduSdPluginClass;
+typedef struct GduSdPluginClass GduSdPluginClass;
+
+struct GduSdPlugin
+{
+  GnomeSettingsPlugin parent_instance;
+  GduSdMonitor *monitor;
+  guint name_owner_id;
+};
+
+struct GduSdPluginClass
+{
+  GnomeSettingsPluginClass parent_class;
+};
+
+GNOME_SETTINGS_PLUGIN_REGISTER (GduSdPlugin, gdu_sd_plugin)
+
+static void
+name_acquired_handler (GDBusConnection *connection,
+                       const gchar     *name,
+                       gpointer         user_data)
+{
+  GduSdPlugin *plugin = GDU_SD_PLUGIN (user_data);
+  g_warn_if_fail (plugin->monitor == NULL);
+  g_clear_object (&plugin->monitor);
+  plugin->monitor = gdu_sd_monitor_new ();
+}
+
+static void
+name_lost_handler (GDBusConnection *connection,
+                   const gchar     *name,
+                   gpointer         user_data)
+{
+  GduSdPlugin *plugin = GDU_SD_PLUGIN (user_data);
+  g_clear_object (&plugin->monitor);
+}
+
+static void
+gdu_sd_plugin_init (GduSdPlugin *plugin)
+{
+}
+
+static void
+gdu_sd_plugin_finalize (GObject *object)
+{
+  GduSdPlugin *plugin = GDU_SD_PLUGIN (object);
+
+  if (plugin->name_owner_id == 0)
+    {
+      g_bus_unown_name (plugin->name_owner_id);
+      plugin->name_owner_id = 0;
+      g_clear_object (&plugin->monitor);
+    }
+  g_clear_object (&plugin->monitor);
+
+  G_OBJECT_CLASS (gdu_sd_plugin_parent_class)->finalize (object);
+}
+
+static void
+impl_activate (GnomeSettingsPlugin *_plugin)
+{
+  GduSdPlugin *plugin = GDU_SD_PLUGIN (_plugin);
+  /* The reason for claiming a unique name is so it's easier to test
+   * code changes - it helps ensure that only one instance of
+   * GduSdMonitor is running at any one time. See also testplugin.c.
+   */
+  if (plugin->name_owner_id == 0)
+    {
+      plugin->name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                                              "org.gnome.Disks.NotificationMonitor",
+                                              G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+                                              G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                                              NULL, /* bus_acquired_handler */
+                                              name_acquired_handler,
+                                              name_lost_handler,
+                                              plugin,
+                                              NULL); /* GDestroyNotify */
+    }
+
+}
+
+static void
+impl_deactivate (GnomeSettingsPlugin *_plugin)
+{
+  GduSdPlugin *plugin = GDU_SD_PLUGIN (_plugin);
+  if (plugin->name_owner_id == 0)
+    {
+      g_bus_unown_name (plugin->name_owner_id);
+      plugin->name_owner_id = 0;
+      g_clear_object (&plugin->monitor);
+    }
+  g_clear_object (&plugin->monitor);
+}
+
+static void
+gdu_sd_plugin_class_init (GduSdPluginClass *klass)
+{
+  GObjectClass           *object_class = G_OBJECT_CLASS (klass);
+  GnomeSettingsPluginClass *plugin_class = GNOME_SETTINGS_PLUGIN_CLASS (klass);
+
+  object_class->finalize = gdu_sd_plugin_finalize;
+  plugin_class->activate = impl_activate;
+  plugin_class->deactivate = impl_deactivate;
+}
diff --git a/src/notify/gdusdplugin.h b/src/notify/gdusdplugin.h
new file mode 100644
index 0000000..5cadba1
--- /dev/null
+++ b/src/notify/gdusdplugin.h
@@ -0,0 +1,33 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: David Zeuthen <zeuthen gmail com>
+ */
+
+#ifndef __GDU_SD_PLUGIN_H__
+#define __GDU_SD_PLUGIN_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gmodule.h>
+
+G_BEGIN_DECLS
+
+#define GDU_TYPE_SD_PLUGIN_PLUGIN  (gdu_sd_plugin_get_type ())
+#define GDU_SD_PLUGIN(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_SD_PLUGIN_PLUGIN, GduSdPlugin))
+#define GDU_IS_SD_PLUGIN_PLUGIN(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_SD_PLUGIN_PLUGIN))
+
+struct GduSdPlugin;
+typedef struct GduSdPlugin GduSdPlugin;
+
+GType gdu_sd_plugin_get_type (void) G_GNUC_CONST;
+
+/* All the plugins must implement this function */
+G_MODULE_EXPORT GType register_gnome_settings_plugin (GTypeModule *module);
+
+G_END_DECLS
+
+#endif /* __GDU_SD_PLUGIN_H__ */
diff --git a/src/notify/testplugin.c b/src/notify/testplugin.c
new file mode 100644
index 0000000..756a337
--- /dev/null
+++ b/src/notify/testplugin.c
@@ -0,0 +1,74 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Licensed under GPL version 2 or later.
+ *
+ * Author: David Zeuthen <zeuthen gmail com>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+#include <libnotify/notify.h>
+
+#include "gdusdmonitor.h"
+
+static void
+name_acquired_handler (GDBusConnection *connection,
+                       const gchar     *name,
+                       gpointer         user_data)
+{
+  GduSdMonitor **monitor = user_data;
+
+  g_warn_if_fail (*monitor == NULL);
+  g_clear_object (monitor);
+  *monitor = gdu_sd_monitor_new ();
+}
+
+static void
+name_lost_handler (GDBusConnection *connection,
+                   const gchar     *name,
+                   gpointer         user_data)
+{
+  GduSdMonitor **monitor = user_data;
+
+  g_clear_object (monitor);
+}
+
+int
+main (int argc, char *argv[])
+{
+  GMainLoop *loop = NULL;
+  guint name_owner_id = 0;
+  GduSdMonitor *monitor = NULL;
+
+  g_type_init ();
+
+  notify_init ("test-gdusdplugin");
+
+  loop = g_main_loop_new (NULL, FALSE);
+
+  /* The reason for claiming a unique name is so it's easier to test
+   * code changes - it helps ensure that only one instance of
+   * GduSdMonitor is running at any one time. See also
+   * gdusdplugin.c:impl_activate().
+   */
+  name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                                  "org.gnome.Disks.NotificationMonitor",
+                                  G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
+                                  G_BUS_NAME_OWNER_FLAGS_REPLACE,
+                                  NULL, /* bus_acquired_handler */
+                                  name_acquired_handler,
+                                  name_lost_handler,
+                                  &monitor,
+                                  NULL); /* GDestroyNotify */
+
+  g_main_loop_run (loop);
+
+  g_bus_unown_name (name_owner_id);
+  g_main_loop_unref (loop);
+  g_object_unref (monitor);
+
+  return 0;
+}



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