[gnome-panel] libpanel-applet-private: add GpModule



commit 187d919b9cb514d87cd8247c185a8320adb798ea
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Thu Oct 27 23:19:56 2016 +0300

    libpanel-applet-private: add GpModule

 gnome-panel/libpanel-applet-private/Makefile.am    |    2 +
 .../libpanel-applet-private/gp-module-private.h    |   61 +++
 gnome-panel/libpanel-applet-private/gp-module.c    |  445 ++++++++++++++++++++
 3 files changed, 508 insertions(+), 0 deletions(-)
---
diff --git a/gnome-panel/libpanel-applet-private/Makefile.am b/gnome-panel/libpanel-applet-private/Makefile.am
index 198d0cf..0fdca48 100644
--- a/gnome-panel/libpanel-applet-private/Makefile.am
+++ b/gnome-panel/libpanel-applet-private/Makefile.am
@@ -23,6 +23,8 @@ libpanel_applet_private_la_SOURCES =  \
        gp-applet-frame.h \
        gp-applet-manager.c \
        gp-applet-manager.h \
+       gp-module.c \
+       gp-module-private.h \
        panel-applets-manager-dbus.c    \
        panel-applets-manager-dbus.h    \
        panel-applet-container.c        \
diff --git a/gnome-panel/libpanel-applet-private/gp-module-private.h 
b/gnome-panel/libpanel-applet-private/gp-module-private.h
new file mode 100644
index 0000000..c6ede2c
--- /dev/null
+++ b/gnome-panel/libpanel-applet-private/gp-module-private.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GP_MODULE_PRIVATE_H
+#define GP_MODULE_PRIVATE_H
+
+#include "libgnome-panel/gp-applet.h"
+#include "libgnome-panel/gp-module.h"
+
+G_BEGIN_DECLS
+
+#define GP_TYPE_MODULE gp_module_get_type ()
+G_DECLARE_FINAL_TYPE (GpModule, gp_module, GP, MODULE, GObject)
+
+typedef enum
+{
+  GP_MODULE_ERROR_APPLET_DOES_NOT_EXIST,
+  GP_MODULE_ERROR_MISSING_APPLET_INFO,
+  GP_MODULE_ERROR_MISSING_APPLET_TYPE,
+} GpModuleError;
+
+#define GP_MODULE_ERROR gp_module_error_quark ()
+GQuark gp_module_error_quark (void);
+
+GpModule            *gp_module_new_from_path   (const gchar           *path);
+
+GpModule            *gp_module_new_from_vtable (const GpModuleVTable  *vtable);
+
+const gchar         *gp_module_get_id          (GpModule              *module);
+
+const gchar * const *gp_module_get_applets     (GpModule              *module);
+
+GpAppletInfo        *gp_module_get_applet_info (GpModule              *module,
+                                                const gchar           *applet,
+                                                GError               **error);
+
+GpApplet            *gp_module_applet_new      (GpModule              *module,
+                                                const gchar           *applet,
+                                                const gchar           *settings_path,
+                                                gboolean               locked_down,
+                                                GtkOrientation         orientation,
+                                                GtkPositionType        position,
+                                                GError               **error);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-panel/libpanel-applet-private/gp-module.c b/gnome-panel/libpanel-applet-private/gp-module.c
new file mode 100644
index 0000000..619fea5
--- /dev/null
+++ b/gnome-panel/libpanel-applet-private/gp-module.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2016 Alberts Muktupāvels
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/gdkwayland.h>
+#endif
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#include "gp-module-private.h"
+#include "libgnome-panel/gp-applet-info-private.h"
+#include "libgnome-panel/gp-module-info-private.h"
+
+typedef guint32 (* GetAbiVersionFunc) (void);
+typedef void    (* GetVTableFunc)     (GpModuleVTable *vtable);
+
+struct _GpModule
+{
+  GObject         parent;
+
+  gboolean        builtin;
+
+  gchar          *path;
+  GModule        *library;
+
+  GpModuleVTable  vtable;
+
+  GpModuleInfo   *info;
+  GHashTable     *applets;
+};
+
+G_DEFINE_TYPE (GpModule, gp_module, G_TYPE_OBJECT)
+
+static gboolean
+match_backend (GpAppletInfo *info)
+{
+  GdkDisplay *display;
+  gchar **backends;
+  gboolean match;
+  guint i;
+
+  if (info->backends == NULL)
+    return TRUE;
+
+  display = gdk_display_get_default ();
+  backends = g_strsplit (info->backends, ",", -1);
+  match = FALSE;
+
+  for (i = 0; backends[i] != NULL; i++)
+    {
+      if (g_strcmp0 (backends[i], "*") == 0)
+        {
+          match = TRUE;
+          break;
+        }
+
+#ifdef GDK_WINDOWING_WAYLAND
+      if (g_strcmp0 (backends[i], "wayland") == 0 &&
+          GDK_IS_WAYLAND_DISPLAY (display))
+        {
+          match = TRUE;
+          break;
+        }
+#endif
+
+#ifdef GDK_WINDOWING_X11
+      if (g_strcmp0 (backends[i], "x11") == 0 && GDK_IS_X11_DISPLAY (display))
+        {
+          match = TRUE;
+          break;
+        }
+#endif
+    }
+
+  g_strfreev (backends);
+
+  return match;
+}
+
+static const gchar *
+get_current_backend (void)
+{
+#ifdef GDK_WINDOWING_WAYLAND
+  if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()))
+    return "wayland";
+#endif
+
+#ifdef GDK_WINDOWING_X11
+  if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
+    return "x11";
+#endif
+
+  return "unknown";
+}
+
+static gboolean
+is_valid_applet (GpModule     *module,
+                 const gchar  *applet,
+                 GError      **error)
+{
+  guint i;
+
+  for (i = 0; module->info->applets[i] != NULL; i++)
+    {
+      if (g_strcmp0 (module->info->applets[i], applet) == 0)
+        return TRUE;
+    }
+
+  g_set_error (error, GP_MODULE_ERROR, GP_MODULE_ERROR_APPLET_DOES_NOT_EXIST,
+               "Module '%s' does not have applet '%s'",
+               module->info->id, applet);
+
+  return FALSE;
+}
+
+static GpAppletInfo *
+get_applet_info (GpModule     *module,
+                 const gchar  *applet,
+                 GError      **error)
+{
+  GpAppletInfo *info;
+
+  info = g_hash_table_lookup (module->applets, applet);
+
+  if (info != NULL)
+    return info;
+
+  info = module->vtable.get_applet_info (applet);
+
+  if (info == NULL)
+    {
+      g_set_error (error, GP_MODULE_ERROR, GP_MODULE_ERROR_MISSING_APPLET_INFO,
+                   "Module '%s' did not return required info about applet '%s'",
+                   module->info->id, applet);
+
+      return NULL;
+    }
+
+  g_hash_table_insert (module->applets, g_strdup (applet), info);
+
+  return info;
+}
+
+static void
+applet_info_free (gpointer data)
+{
+  GpAppletInfo *info;
+
+  info = (GpAppletInfo *) data;
+
+  gp_applet_info_free (info);
+}
+
+static gboolean
+load_module_info (GpModule *module)
+{
+  GpModuleInfo *info;
+
+  module->info = info = module->vtable.get_module_info ();
+
+  if (info == NULL)
+    {
+      g_warning ("Failed to get 'GpModuleInfo' from module '%s'", module->path);
+      return FALSE;
+    }
+
+  if (info->id == NULL || *info->id == '\0')
+    {
+      g_warning ("Module '%s' does not have valid id", module->path);
+      return FALSE;
+    }
+
+  if (info->applets == NULL || info->applets[0] == NULL)
+    {
+      g_warning ("Module '%s' does not have valid applets", module->path);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+gp_module_finalize (GObject *object)
+{
+  GpModule *module;
+
+  module = GP_MODULE (object);
+
+  g_clear_pointer (&module->path, g_free);
+
+  if (module->library != NULL)
+    {
+      g_module_close (module->library);
+      module->library = NULL;
+    }
+
+  g_clear_pointer (&module->info, gp_module_info_free);
+  g_clear_pointer (&module->info, g_hash_table_destroy);
+
+  G_OBJECT_CLASS (gp_module_parent_class)->finalize (object);
+}
+
+static void
+gp_module_class_init (GpModuleClass *module_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (module_class);
+
+  object_class->finalize = gp_module_finalize;
+}
+
+static void
+gp_module_init (GpModule *module)
+{
+  module->applets = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                           g_free, applet_info_free);
+}
+
+GQuark
+gp_module_error_quark (void)
+{
+  return g_quark_from_static_string ("gp-module-error-quark");
+}
+
+GpModule *
+gp_module_new_from_path (const gchar *path)
+{
+  GpModule *module;
+  GModuleFlags flags;
+  const gchar *symbol;
+  GetAbiVersionFunc abi_version_func;
+  GetVTableFunc vtable_func;
+
+  g_return_val_if_fail (path != NULL && *path != '\0', NULL);
+
+  module = g_object_new (GP_TYPE_MODULE, NULL);
+  module->builtin = FALSE;
+
+  flags = G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL;
+
+  module->path = g_strdup (path);
+  module->library = g_module_open (path, flags);
+
+  if (module->library == NULL)
+    {
+      g_warning ("Failed to load module '%s': %s", path, g_module_error ());
+
+      g_object_unref (module);
+      return NULL;
+    }
+
+  symbol = "gp_module_get_abi_version";
+  if (!g_module_symbol (module->library, symbol, (gpointer) &abi_version_func))
+    {
+      g_warning ("Failed to get '%s' for module '%s': %s",
+                 symbol, path, g_module_error ());
+
+      g_object_unref (module);
+      return NULL;
+    }
+
+  if (abi_version_func == NULL)
+    {
+      g_warning ("Invalid '%s' in module '%s'", symbol, path);
+
+      g_object_unref (module);
+      return NULL;
+    }
+
+  if (abi_version_func () != GP_MODULE_ABI_VERSION)
+    {
+      g_warning ("Module '%s' ABI version does not match", path);
+
+      g_object_unref (module);
+      return NULL;
+    }
+
+  symbol = "gp_module_get_vtable";
+  if (!g_module_symbol (module->library, symbol, (gpointer) &vtable_func))
+    {
+      g_warning ("Failed to get '%s' for module '%s': %s",
+                 symbol, path, g_module_error ());
+
+      g_object_unref (module);
+      return NULL;
+    }
+
+  if (vtable_func == NULL)
+    {
+      g_warning ("Invalid '%s' in module '%s'", symbol, path);
+
+      g_object_unref (module);
+      return NULL;
+    }
+
+  vtable_func (&module->vtable);
+
+  if (!load_module_info (module))
+    {
+      g_object_unref (module);
+      return NULL;
+    }
+
+  return module;
+}
+
+GpModule *
+gp_module_new_from_vtable (const GpModuleVTable *vtable)
+{
+  GpModule *module;
+
+  module = g_object_new (GP_TYPE_MODULE, NULL);
+  module->builtin = TRUE;
+
+  module->vtable = *vtable;
+
+  if (!load_module_info (module))
+    {
+      g_object_unref (module);
+      return NULL;
+    }
+
+  return module;
+}
+
+const gchar *
+gp_module_get_id (GpModule *module)
+{
+  return module->info->id;
+}
+
+const gchar * const *
+gp_module_get_applets (GpModule *module)
+{
+  return (const gchar * const *) module->info->applets;
+}
+
+/**
+ * gp_module_applet_new:
+ * @module: a #GpModule
+ * @applet: the applet id
+ * @error: return location for a #GError, or %NULL
+ *
+ * Returns the #GpAppletInfo for @applet in @module if exists, %NULL
+ * otherwise.
+ *
+ * Returns: (transfer none): the #GpAppletInfo, or %NULL.
+ */
+GpAppletInfo *
+gp_module_get_applet_info (GpModule     *module,
+                           const gchar  *applet,
+                           GError      **error)
+{
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  if (!is_valid_applet (module, applet, error))
+    return NULL;
+
+  return get_applet_info (module, applet, error);
+}
+
+/**
+ * gp_module_applet_new:
+ * @module: a #GpModule
+ * @applet: the applet id
+ * @settings_path: the #GSettings path to the per-instance settings
+ * @locked_down: whether applet is on locked down panel
+ * @orientation: the orientation of the panel
+ * @position: the position of the panel
+ * @error: return location for a #GError, or %NULL
+ *
+ * Returns a newly allocated applet.
+ *
+ * Returns: (transfer full): a newly created #GpApplet, or %NULL.
+ */
+GpApplet *
+gp_module_applet_new (GpModule         *module,
+                      const gchar      *applet,
+                      const gchar      *settings_path,
+                      gboolean          locked_down,
+                      GtkOrientation    orientation,
+                      GtkPositionType   position,
+                      GError          **error)
+{
+  GpAppletInfo *info;
+  GType type;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  if (!is_valid_applet (module, applet, error))
+    return NULL;
+
+  info = get_applet_info (module, applet, error);
+  if (info == NULL)
+    return NULL;
+
+  if (!match_backend (info))
+    {
+      g_set_error (error, GP_MODULE_ERROR, GP_MODULE_ERROR_MISSING_APPLET_INFO,
+                   "Module '%s' did not return required info about applet '%s'",
+                   module->info->id, applet);
+
+      return NULL;
+    }
+
+  type = module->vtable.get_applet_type (applet);
+  if (type == G_TYPE_NONE)
+    {
+      g_set_error (error, GP_MODULE_ERROR, GP_MODULE_ERROR_MISSING_APPLET_TYPE,
+                   "Applet '%s' from module '%s' does not work with current backend '%s'",
+                   applet, module->info->id, get_current_backend ());
+
+      return NULL;
+    }
+
+  return g_object_new (type,
+                       "id", applet,
+                       "settings-path", settings_path,
+                       "translation-domain", module->info->domain,
+                       "locked-down", locked_down,
+                       "orientation", orientation,
+                       "position", position,
+                       NULL);
+}


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