/* * mate-panel-applet-factory.c: panel applet writing API. * * Copyright (C) 2010 Carlos Garcia Campos * Copyright (C) 2012-2021 MATE Developers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include "mate-panel-applet.h" #include "panel-applet-private.h" #include "mate-panel-applet-factory.h" #ifdef HAVE_X11 #include #endif struct _MatePanelAppletFactory { GObject base; gchar *factory_id; guint n_applets; gboolean out_of_process; GType applet_type; GClosure *closure; GDBusConnection *connection; gint owner_id; gint registration_id; GHashTable *applets; guint next_uid; }; #define MATE_PANEL_APPLET_FACTORY_OBJECT_PATH "/org/mate/panel/applet/%s" #define MATE_PANEL_APPLET_FACTORY_SERVICE_NAME "org.mate.panel.applet.%s" G_DEFINE_TYPE (MatePanelAppletFactory, mate_panel_applet_factory, G_TYPE_OBJECT) static GHashTable *factories = NULL; static void mate_panel_applet_factory_finalize (GObject *object) { MatePanelAppletFactory *factory = MATE_PANEL_APPLET_FACTORY (object); if (factory->registration_id) { g_dbus_connection_unregister_object (factory->connection, factory->registration_id); factory->registration_id = 0; } if (factory->owner_id) { g_bus_unown_name (factory->owner_id); factory->owner_id = 0; } g_hash_table_remove (factories, factory->factory_id); if (g_hash_table_size (factories) == 0) { g_hash_table_unref (factories); factories = NULL; } if (factory->factory_id) { g_free (factory->factory_id); factory->factory_id = NULL; } if (factory->applets) { g_hash_table_unref (factory->applets); factory->applets = NULL; } if (factory->closure) { g_closure_unref (factory->closure); factory->closure = NULL; } G_OBJECT_CLASS (mate_panel_applet_factory_parent_class) ->finalize (object); } static void mate_panel_applet_factory_init (MatePanelAppletFactory *factory) { factory->applets = g_hash_table_new (NULL, NULL); factory->next_uid = 1; } static void mate_panel_applet_factory_class_init (MatePanelAppletFactoryClass *klass) { GObjectClass *g_object_class; g_object_class = G_OBJECT_CLASS (klass); g_object_class->finalize = mate_panel_applet_factory_finalize; } static void mate_panel_applet_factory_applet_removed (MatePanelAppletFactory *factory, GObject *applet) { guint uid; uid = GPOINTER_TO_UINT (g_object_get_data (applet, "uid")); g_hash_table_remove (factory->applets, GUINT_TO_POINTER (uid)); factory->n_applets--; if (factory->n_applets == 0) g_object_unref (factory); } MatePanelAppletFactory * mate_panel_applet_factory_new (const gchar *factory_id, gboolean out_of_process, GType applet_type, GClosure *closure) { MatePanelAppletFactory *factory; factory = MATE_PANEL_APPLET_FACTORY (g_object_new (PANEL_TYPE_APPLET_FACTORY, NULL)); factory->factory_id = g_strdup (factory_id); factory->out_of_process = out_of_process; factory->applet_type = applet_type; factory->closure = g_closure_ref (closure); if (factories == NULL) factories = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (factories, factory->factory_id, factory); return factory; } static void set_applet_constructor_properties (GObject *applet, GVariant *props) { GVariantIter iter; GVariant *value; gchar *key; g_variant_iter_init (&iter, props); while (g_variant_iter_loop (&iter, "{sv}", &key, &value)) { switch (g_variant_classify (value)) { case G_VARIANT_CLASS_UINT32: { guint32 v = g_variant_get_uint32 (value); g_object_set (applet, key, v, NULL); break; } case G_VARIANT_CLASS_STRING: { const gchar *v = g_variant_get_string (value, NULL); g_object_set (applet, key, v, NULL); break; } case G_VARIANT_CLASS_BOOLEAN: { gboolean v = g_variant_get_boolean (value); g_object_set (applet, key, v, NULL); break; } default: g_assert_not_reached (); } } } static void mate_panel_applet_factory_get_applet (MatePanelAppletFactory *factory, GDBusConnection *connection, GVariant *parameters, GDBusMethodInvocation *invocation) { GObject *applet; const gchar *applet_id; gint screen_num; GVariant *props; GVariant *return_value; guint32 xid; guint32 uid; const gchar *object_path; g_variant_get (parameters, "(&si@a{sv})", &applet_id, &screen_num, &props); applet = g_object_new (factory->applet_type, "out-of-process", factory->out_of_process, "id", applet_id, "connection", connection, "closure", factory->closure, NULL); factory->n_applets++; g_object_weak_ref (applet, (GWeakNotify) mate_panel_applet_factory_applet_removed, factory); set_applet_constructor_properties (applet, props); g_variant_unref (props); #ifdef HAVE_X11 if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) { GdkScreen *screen; screen = screen_num != -1 ? gdk_display_get_default_screen (gdk_display_get_default ()) : gdk_screen_get_default (); xid = mate_panel_applet_get_xid (MATE_PANEL_APPLET (applet), screen); } else #endif /* HAVE_X11 */ { /* Not using X11 */ xid = 0; } uid = factory->next_uid++; object_path = mate_panel_applet_get_object_path (MATE_PANEL_APPLET (applet)); g_hash_table_insert (factory->applets, GUINT_TO_POINTER (uid), applet); g_object_set_data (applet, "uid", GUINT_TO_POINTER (uid)); return_value = g_variant_new ("(obuu)", object_path, factory->out_of_process, xid, uid); g_dbus_method_invocation_return_value (invocation, return_value); } static void method_call_cb (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { MatePanelAppletFactory *factory = MATE_PANEL_APPLET_FACTORY (user_data); if (g_strcmp0 (method_name, "GetApplet") == 0) mate_panel_applet_factory_get_applet (factory, connection, parameters, invocation); } static const gchar introspection_xml[] = "" "" "" "" "" "" "" "" "" "" "" "" ""; static const GDBusInterfaceVTable interface_vtable = { method_call_cb, NULL, NULL, { 0 } }; static GDBusNodeInfo *introspection_data = NULL; static void on_bus_acquired (GDBusConnection *connection, const gchar *name, MatePanelAppletFactory *factory) { gchar *object_path; GError *error = NULL; if (!introspection_data) introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); object_path = g_strdup_printf (MATE_PANEL_APPLET_FACTORY_OBJECT_PATH, factory->factory_id); factory->connection = connection; factory->registration_id = g_dbus_connection_register_object (connection, object_path, introspection_data->interfaces[0], &interface_vtable, factory, NULL, &error); if (error) { g_printerr ("Failed to register object %s: %s\n", object_path, error->message); g_error_free (error); } g_free (object_path); } static void on_name_lost (GDBusConnection *connection, const gchar *name, MatePanelAppletFactory *factory) { g_object_unref (factory); } gboolean mate_panel_applet_factory_register_service (MatePanelAppletFactory *factory) { gchar *service_name; if (!factory) return FALSE; service_name = g_strdup_printf (MATE_PANEL_APPLET_FACTORY_SERVICE_NAME, factory->factory_id); factory->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, service_name, G_BUS_NAME_OWNER_FLAGS_NONE, (GBusAcquiredCallback) on_bus_acquired, NULL, (GBusNameLostCallback) on_name_lost, factory, NULL); g_free (service_name); return TRUE; } GtkWidget * mate_panel_applet_factory_get_applet_widget (const gchar *id, guint uid) { MatePanelAppletFactory *factory; GObject *object; if (!factories) return NULL; factory = g_hash_table_lookup (factories, id); if (!factory) return NULL; object = g_hash_table_lookup (factory->applets, GUINT_TO_POINTER (uid)); if (!object || !GTK_IS_WIDGET (object)) return NULL; return GTK_WIDGET (object); }