(root)/
glib-2.79.0/
gio/
tests/
gnotification-server.c
       1  /*
       2   * Copyright © 2013 Lars Uebernickel
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General
      17   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      18   *
      19   * Authors: Lars Uebernickel <lars@uebernic.de>
      20   */
      21  
      22  #include "gnotification-server.h"
      23  
      24  #include <gio/gio.h>
      25  
      26  typedef GObjectClass GNotificationServerClass;
      27  
      28  struct _GNotificationServer
      29  {
      30    GObject parent;
      31  
      32    GDBusConnection *connection;
      33    guint name_owner_id;
      34    guint object_id;
      35  
      36    guint is_running;
      37  
      38    /* app_ids -> hashtables of notification ids -> a{sv} */
      39    GHashTable *applications;
      40  };
      41  
      42  G_DEFINE_TYPE (GNotificationServer, g_notification_server, G_TYPE_OBJECT)
      43  
      44  enum
      45  {
      46    PROP_0,
      47    PROP_IS_RUNNING
      48  };
      49  
      50  static GDBusInterfaceInfo *
      51  org_gtk_Notifications_get_interface (void)
      52  {
      53    static GDBusInterfaceInfo *iface_info;
      54  
      55    if (iface_info == NULL)
      56      {
      57        GDBusNodeInfo *info;
      58        GError *error = NULL;
      59  
      60        info = g_dbus_node_info_new_for_xml (
      61          "<node>"
      62          "  <interface name='org.gtk.Notifications'>"
      63          "    <method name='AddNotification'>"
      64          "      <arg type='s' direction='in' />"
      65          "      <arg type='s' direction='in' />"
      66          "      <arg type='a{sv}' direction='in' />"
      67          "    </method>"
      68          "    <method name='RemoveNotification'>"
      69          "      <arg type='s' direction='in' />"
      70          "      <arg type='s' direction='in' />"
      71          "    </method>"
      72          "  </interface>"
      73          "</node>", &error);
      74  
      75        if (info == NULL)
      76          g_error ("%s", error->message);
      77  
      78        iface_info = g_dbus_node_info_lookup_interface (info, "org.gtk.Notifications");
      79        g_assert (iface_info);
      80  
      81        g_dbus_interface_info_ref (iface_info);
      82        g_dbus_node_info_unref (info);
      83      }
      84  
      85    return iface_info;
      86  }
      87  
      88  static void
      89  g_notification_server_notification_added (GNotificationServer *server,
      90                                            const gchar         *app_id,
      91                                            const gchar         *notification_id,
      92                                            GVariant            *notification)
      93  {
      94    GHashTable *notifications;
      95  
      96    notifications = g_hash_table_lookup (server->applications, app_id);
      97    if (notifications == NULL)
      98      {
      99        notifications = g_hash_table_new_full (g_str_hash, g_str_equal,
     100                                               g_free, (GDestroyNotify) g_variant_unref);
     101        g_hash_table_insert (server->applications, g_strdup (app_id), notifications);
     102      }
     103  
     104    g_hash_table_replace (notifications, g_strdup (notification_id), g_variant_ref (notification));
     105  
     106    g_signal_emit_by_name (server, "notification-received", app_id, notification_id, notification);
     107  }
     108  
     109  static void
     110  g_notification_server_notification_removed (GNotificationServer *server,
     111                                              const gchar         *app_id,
     112                                              const gchar         *notification_id)
     113  {
     114    GHashTable *notifications;
     115  
     116    notifications = g_hash_table_lookup (server->applications, app_id);
     117    if (notifications)
     118      {
     119        g_hash_table_remove (notifications, notification_id);
     120        if (g_hash_table_size (notifications) == 0)
     121          g_hash_table_remove (server->applications, app_id);
     122      }
     123  
     124    g_signal_emit_by_name (server, "notification-removed", app_id, notification_id);
     125  }
     126  
     127  static void
     128  org_gtk_Notifications_method_call (GDBusConnection       *connection,
     129                                     const gchar           *sender,
     130                                     const gchar           *object_path,
     131                                     const gchar           *interface_name,
     132                                     const gchar           *method_name,
     133                                     GVariant              *parameters,
     134                                     GDBusMethodInvocation *invocation,
     135                                     gpointer               user_data)
     136  {
     137    GNotificationServer *server = user_data;
     138  
     139    if (g_str_equal (method_name, "AddNotification"))
     140      {
     141        const gchar *app_id;
     142        const gchar *notification_id;
     143        GVariant *notification;
     144  
     145        g_variant_get (parameters, "(&s&s@a{sv})", &app_id, &notification_id, &notification);
     146        g_notification_server_notification_added (server, app_id, notification_id, notification);
     147        g_dbus_method_invocation_return_value (invocation, NULL);
     148  
     149        g_variant_unref (notification);
     150      }
     151    else if (g_str_equal (method_name, "RemoveNotification"))
     152      {
     153        const gchar *app_id;
     154        const gchar *notification_id;
     155  
     156        g_variant_get (parameters, "(&s&s)", &app_id, &notification_id);
     157        g_notification_server_notification_removed (server, app_id, notification_id);
     158        g_dbus_method_invocation_return_value (invocation, NULL);
     159      }
     160    else
     161      {
     162        g_dbus_method_invocation_return_dbus_error (invocation, "UnknownMethod", "No such method");
     163      }
     164  }
     165  
     166  static void
     167  g_notification_server_dispose (GObject *object)
     168  {
     169    GNotificationServer *server = G_NOTIFICATION_SERVER (object);
     170  
     171    g_notification_server_stop (server);
     172  
     173    g_clear_pointer (&server->applications, g_hash_table_unref);
     174    g_clear_object (&server->connection);
     175  
     176    G_OBJECT_CLASS (g_notification_server_parent_class)->dispose (object);
     177  }
     178  
     179  static void
     180  g_notification_server_get_property (GObject    *object,
     181                                      guint       property_id,
     182                                      GValue     *value,
     183                                      GParamSpec *pspec)
     184  {
     185    GNotificationServer *server = G_NOTIFICATION_SERVER (object);
     186  
     187    switch (property_id)
     188      {
     189      case PROP_IS_RUNNING:
     190        g_value_set_boolean (value, server->is_running);
     191        break;
     192  
     193      default:
     194        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     195      }
     196  }
     197  
     198  static void
     199  g_notification_server_class_init (GNotificationServerClass *class)
     200  {
     201    GObjectClass *object_class = G_OBJECT_CLASS (class);
     202  
     203    object_class->get_property = g_notification_server_get_property;
     204    object_class->dispose = g_notification_server_dispose;
     205  
     206    g_object_class_install_property (object_class, PROP_IS_RUNNING,
     207                                     g_param_spec_boolean ("is-running", "", "", FALSE,
     208                                                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
     209  
     210    g_signal_new ("notification-received", G_TYPE_NOTIFICATION_SERVER, G_SIGNAL_RUN_FIRST,
     211                  0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 3,
     212                  G_TYPE_STRING, G_TYPE_STRING, G_TYPE_VARIANT);
     213  
     214    g_signal_new ("notification-removed", G_TYPE_NOTIFICATION_SERVER, G_SIGNAL_RUN_FIRST,
     215                  0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2,
     216                  G_TYPE_STRING, G_TYPE_STRING);
     217  }
     218  
     219  static void
     220  g_notification_server_bus_acquired (GDBusConnection *connection,
     221                                      const gchar     *name,
     222                                      gpointer         user_data)
     223  {
     224    const GDBusInterfaceVTable vtable = {
     225      org_gtk_Notifications_method_call, NULL, NULL, { 0 }
     226    };
     227    GNotificationServer *server = user_data;
     228  
     229    server->object_id = g_dbus_connection_register_object (connection, "/org/gtk/Notifications",
     230                                                           org_gtk_Notifications_get_interface (),
     231                                                           &vtable, server, NULL, NULL);
     232  
     233    /* register_object only fails if the same object is exported more than once */
     234    g_assert (server->object_id > 0);
     235  
     236    server->connection = g_object_ref (connection);
     237  }
     238  
     239  static void
     240  g_notification_server_name_acquired (GDBusConnection *connection,
     241                                       const gchar     *name,
     242                                       gpointer         user_data)
     243  {
     244    GNotificationServer *server = user_data;
     245  
     246    server->is_running = TRUE;
     247    g_object_notify (G_OBJECT (server), "is-running");
     248  }
     249  
     250  static void
     251  g_notification_server_name_lost (GDBusConnection *connection,
     252                                   const gchar     *name,
     253                                   gpointer         user_data)
     254  {
     255    GNotificationServer *server = user_data;
     256  
     257    g_notification_server_stop (server);
     258  
     259    if (connection == NULL && server->connection)
     260      g_clear_object (&server->connection);
     261  }
     262  
     263  static void
     264  g_notification_server_init (GNotificationServer *server)
     265  {
     266    server->applications = g_hash_table_new_full (g_str_hash, g_str_equal,
     267                                                  g_free, (GDestroyNotify) g_hash_table_unref);
     268  
     269    server->name_owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
     270                                            "org.gtk.Notifications",
     271                                            G_BUS_NAME_OWNER_FLAGS_NONE,
     272                                            g_notification_server_bus_acquired,
     273                                            g_notification_server_name_acquired,
     274                                            g_notification_server_name_lost,
     275                                            server, NULL);
     276  }
     277  
     278  GNotificationServer *
     279  g_notification_server_new (void)
     280  {
     281    return g_object_new (G_TYPE_NOTIFICATION_SERVER, NULL);
     282  }
     283  
     284  void
     285  g_notification_server_stop (GNotificationServer *server)
     286  {
     287    g_return_if_fail (G_IS_NOTIFICATION_SERVER (server));
     288  
     289    if (server->name_owner_id)
     290      {
     291        g_bus_unown_name (server->name_owner_id);
     292        server->name_owner_id = 0;
     293      }
     294  
     295    if (server->object_id && server->connection)
     296      {
     297        g_dbus_connection_unregister_object (server->connection, server->object_id);
     298        server->object_id = 0;
     299      }
     300  
     301    if (server->is_running)
     302      {
     303        server->is_running = FALSE;
     304        g_object_notify (G_OBJECT (server), "is-running");
     305      }
     306  }
     307  
     308  gboolean
     309  g_notification_server_get_is_running (GNotificationServer *server)
     310  {
     311    g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), FALSE);
     312  
     313    return server->is_running;
     314  }
     315  
     316  gchar **
     317  g_notification_server_list_applications (GNotificationServer *server)
     318  {
     319    g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), NULL);
     320  
     321    return (gchar **) g_hash_table_get_keys_as_array (server->applications, NULL);
     322  }
     323  
     324  gchar **
     325  g_notification_server_list_notifications (GNotificationServer *server,
     326                                            const gchar         *app_id)
     327  {
     328    GHashTable *notifications;
     329  
     330    g_return_val_if_fail (G_IS_NOTIFICATION_SERVER (server), NULL);
     331    g_return_val_if_fail (app_id != NULL, NULL);
     332  
     333    notifications = g_hash_table_lookup (server->applications, app_id);
     334  
     335    if (notifications == NULL)
     336      return NULL;
     337  
     338    return (gchar **) g_hash_table_get_keys_as_array (notifications, NULL);
     339  }