952 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			952 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright © 2010 Codethink Limited
 | |
|  * Copyright © 2012 Red Hat, Inc.
 | |
|  * Copyright © 2013 Canonical Limited
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2 of the licence, 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
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * Author: Ryan Lortie <desrt@desrt.ca>
 | |
|  *         Matthias Clasen <mclasen@redhat.com>
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include "gtkapplicationprivate.h"
 | |
| #include "gtksettings.h"
 | |
| #include "gtkprivate.h"
 | |
| 
 | |
| #include "gdk/gdk-private.h"
 | |
| 
 | |
| G_DEFINE_TYPE (GtkApplicationImplDBus, gtk_application_impl_dbus, GTK_TYPE_APPLICATION_IMPL)
 | |
| 
 | |
| #define GNOME_DBUS_NAME             "org.gnome.SessionManager"
 | |
| #define GNOME_DBUS_OBJECT_PATH      "/org/gnome/SessionManager"
 | |
| #define GNOME_DBUS_INTERFACE        "org.gnome.SessionManager"
 | |
| #define GNOME_DBUS_CLIENT_INTERFACE "org.gnome.SessionManager.ClientPrivate"
 | |
| #define XFCE_DBUS_NAME              "org.xfce.SessionManager"
 | |
| #define XFCE_DBUS_OBJECT_PATH       "/org/xfce/SessionManager"
 | |
| #define XFCE_DBUS_INTERFACE         "org.xfce.Session.Manager"
 | |
| #define XFCE_DBUS_CLIENT_INTERFACE  "org.xfce.Session.Client"
 | |
| #define GNOME_SCREENSAVER_DBUS_NAME             "org.gnome.ScreenSaver"
 | |
| #define GNOME_SCREENSAVER_DBUS_OBJECT_PATH      "/org/gnome/ScreenSaver"
 | |
| #define GNOME_SCREENSAVER_DBUS_INTERFACE        "org.gnome.ScreenSaver"
 | |
| 
 | |
| static void client_proxy_signal (GDBusProxy  *proxy,
 | |
|                                  const gchar *sender_name,
 | |
|                                  const gchar *signal_name,
 | |
|                                  GVariant    *parameters,
 | |
|                                  gpointer     user_data);
 | |
| 
 | |
| static void
 | |
| unregister_client (GtkApplicationImplDBus *dbus)
 | |
| {
 | |
|   GError *error = NULL;
 | |
| 
 | |
|   g_debug ("Unregistering client");
 | |
| 
 | |
|   g_dbus_proxy_call_sync (dbus->sm_proxy,
 | |
|                           "UnregisterClient",
 | |
|                           g_variant_new ("(o)", dbus->client_path),
 | |
|                           G_DBUS_CALL_FLAGS_NONE,
 | |
|                           G_MAXINT,
 | |
|                           NULL,
 | |
|                           &error);
 | |
| 
 | |
|   if (error)
 | |
|     {
 | |
|       g_warning ("Failed to unregister client: %s", error->message);
 | |
|       g_error_free (error);
 | |
|     }
 | |
| 
 | |
|   g_signal_handlers_disconnect_by_func (dbus->client_proxy, client_proxy_signal, dbus);
 | |
|   g_clear_object (&dbus->client_proxy);
 | |
| 
 | |
|   g_free (dbus->client_path);
 | |
|   dbus->client_path = NULL;
 | |
| }
 | |
| 
 | |
| static void
 | |
| send_quit_response (GtkApplicationImplDBus *dbus,
 | |
|                     gboolean                will_quit,
 | |
|                     const gchar            *reason)
 | |
| {
 | |
|   g_debug ("Calling EndSessionResponse %d '%s'", will_quit, reason);
 | |
| 
 | |
|   g_dbus_proxy_call (dbus->client_proxy,
 | |
|                      "EndSessionResponse",
 | |
|                      g_variant_new ("(bs)", will_quit, reason ? reason : ""),
 | |
|                      G_DBUS_CALL_FLAGS_NONE,
 | |
|                      G_MAXINT,
 | |
|                      NULL, NULL, NULL);
 | |
| }
 | |
| 
 | |
| static void
 | |
| client_proxy_signal (GDBusProxy  *proxy,
 | |
|                      const gchar *sender_name,
 | |
|                      const gchar *signal_name,
 | |
|                      GVariant    *parameters,
 | |
|                      gpointer     user_data)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = user_data;
 | |
| 
 | |
|   if (g_str_equal (signal_name, "QueryEndSession"))
 | |
|     {
 | |
|       g_debug ("Received QueryEndSession");
 | |
|       g_signal_emit_by_name (dbus->impl.application, "query-end");
 | |
|       send_quit_response (dbus, TRUE, NULL);
 | |
|     }
 | |
|   else if (g_str_equal (signal_name, "CancelEndSession"))
 | |
|     {
 | |
|       g_debug ("Received CancelEndSession");
 | |
|     }
 | |
|   else if (g_str_equal (signal_name, "EndSession"))
 | |
|     {
 | |
|       g_debug ("Received EndSession");
 | |
|       send_quit_response (dbus, TRUE, NULL);
 | |
|       unregister_client (dbus);
 | |
|       g_application_quit (G_APPLICATION (dbus->impl.application));
 | |
|     }
 | |
|   else if (g_str_equal (signal_name, "Stop"))
 | |
|     {
 | |
|       g_debug ("Received Stop");
 | |
|       unregister_client (dbus);
 | |
|       g_application_quit (G_APPLICATION (dbus->impl.application));
 | |
|     }
 | |
| }
 | |
| 
 | |
| static GDBusProxy*
 | |
| gtk_application_get_proxy_if_service_present (GDBusConnection *connection,
 | |
|                                               GDBusProxyFlags  flags,
 | |
|                                               const gchar     *bus_name,
 | |
|                                               const gchar     *object_path,
 | |
|                                               const gchar     *interface,
 | |
|                                               GError         **error)
 | |
| {
 | |
|   GDBusProxy *proxy;
 | |
|   gchar *owner;
 | |
| 
 | |
|   proxy = g_dbus_proxy_new_sync (connection,
 | |
|                                  flags,
 | |
|                                  NULL,
 | |
|                                  bus_name,
 | |
|                                  object_path,
 | |
|                                  interface,
 | |
|                                  NULL,
 | |
|                                  error);
 | |
| 
 | |
|   if (!proxy)
 | |
|     return NULL;
 | |
| 
 | |
|   /* is there anyone actually providing the service? */
 | |
|   owner = g_dbus_proxy_get_name_owner (proxy);
 | |
|   if (owner == NULL)
 | |
|     {
 | |
|       g_clear_object (&proxy);
 | |
|       g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
 | |
|                    "The name %s is not owned", bus_name);
 | |
|     }
 | |
|   else
 | |
|     g_free (owner);
 | |
| 
 | |
|   return proxy;
 | |
| }
 | |
| 
 | |
| static void
 | |
| screensaver_signal_session (GDBusProxy     *proxy,
 | |
|                             const char     *sender_name,
 | |
|                             const char     *signal_name,
 | |
|                             GVariant       *parameters,
 | |
|                             GtkApplication *application)
 | |
| {
 | |
|   gboolean active;
 | |
| 
 | |
|   if (!g_str_equal (signal_name, "ActiveChanged"))
 | |
|     return;
 | |
| 
 | |
|   g_variant_get (parameters, "(b)", &active);
 | |
|   gtk_application_set_screensaver_active (application, active);
 | |
| }
 | |
| 
 | |
| enum {
 | |
|   UNKNOWN   = 0,
 | |
|   RUNNING   = 1,
 | |
|   QUERY_END = 2,
 | |
|   ENDING    = 3
 | |
| };
 | |
| 
 | |
| static void
 | |
| screensaver_signal_portal (GDBusConnection *connection,
 | |
|                            const char       *sender_name,
 | |
|                            const char       *object_path,
 | |
|                            const char       *interface_name,
 | |
|                            const char       *signal_name,
 | |
|                            GVariant         *parameters,
 | |
|                            gpointer          data)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *)data;
 | |
|   GtkApplication *application = data;
 | |
|   gboolean active;
 | |
|   GVariant *state;
 | |
|   guint32 session_state = UNKNOWN;
 | |
| 
 | |
|   if (!g_str_equal (signal_name, "StateChanged"))
 | |
|     return;
 | |
| 
 | |
|   g_variant_get (parameters, "(o@a{sv})", NULL, &state);
 | |
|   g_variant_lookup (state, "screensaver-active", "b", &active);
 | |
|   gtk_application_set_screensaver_active (dbus->impl.application, active);
 | |
| 
 | |
|   g_variant_lookup (state, "session-state", "u", &session_state);
 | |
|   if (session_state != dbus->session_state)
 | |
|     {
 | |
|       dbus->session_state = session_state;
 | |
| 
 | |
|       /* Note that we'll only ever get here if we get a session-state,
 | |
|        * in which case, the interface is new enough to have QueryEndResponse.
 | |
|        */
 | |
|       if (session_state == ENDING)
 | |
|         {
 | |
|           g_application_quit (G_APPLICATION (application));
 | |
|         }
 | |
|       else if (session_state == QUERY_END)
 | |
|         {
 | |
|           g_signal_emit_by_name (dbus->impl.application, "query-end");
 | |
| 
 | |
|           g_dbus_proxy_call (dbus->inhibit_proxy,
 | |
|                              "QueryEndResponse",
 | |
|                              g_variant_new ("(o)", dbus->session_id),
 | |
|                              G_DBUS_CALL_FLAGS_NONE,
 | |
|                              G_MAXINT,
 | |
|                              NULL,
 | |
|                              NULL, NULL);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| ss_get_active_cb (GObject      *source,
 | |
|                   GAsyncResult *result,
 | |
|                   gpointer      data)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) data;
 | |
|   GDBusProxy *proxy = G_DBUS_PROXY (source);
 | |
|   GError *error = NULL;
 | |
|   GVariant *ret;
 | |
|   gboolean active;
 | |
| 
 | |
|   ret = g_dbus_proxy_call_finish (proxy, result, &error);
 | |
|   if (ret == NULL)
 | |
|     {
 | |
|       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 | |
|         g_warning ("Getting screensaver status failed: %s",
 | |
|                    error ? error->message : "");
 | |
|       g_clear_error (&error);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   g_variant_get (ret, "(b)", &active);
 | |
|   g_variant_unref (ret);
 | |
|   gtk_application_set_screensaver_active (dbus->impl.application, active);
 | |
| }
 | |
| 
 | |
| static void
 | |
| create_monitor_cb (GObject      *source,
 | |
|                    GAsyncResult *result,
 | |
|                    gpointer      data)
 | |
| {
 | |
|   GDBusProxy *proxy = G_DBUS_PROXY (source);
 | |
|   GError *error = NULL;
 | |
|   GVariant *ret = NULL;
 | |
| 
 | |
|   ret = g_dbus_proxy_call_finish (proxy, result, &error);
 | |
|   if (ret == NULL)
 | |
|     {
 | |
|       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 | |
|         g_warning ("Creating a portal monitor failed: %s",
 | |
|                    error ? error->message : "");
 | |
|       g_clear_error (&error);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|   g_variant_unref (ret);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
 | |
|                                    gboolean            register_session)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
|   GError *error = NULL;
 | |
|   GVariant *res;
 | |
|   gboolean same_bus;
 | |
|   const char *bus_name;
 | |
|   const char *client_interface;
 | |
|   const char *client_id = GDK_PRIVATE_CALL (gdk_get_desktop_autostart_id) ();
 | |
| 
 | |
|   dbus->session = g_application_get_dbus_connection (G_APPLICATION (impl->application));
 | |
| 
 | |
|   if (!dbus->session)
 | |
|     goto out;
 | |
| 
 | |
|   dbus->application_id = g_application_get_application_id (G_APPLICATION (impl->application));
 | |
|   dbus->object_path = g_application_get_dbus_object_path (G_APPLICATION (impl->application));
 | |
|   dbus->unique_name = g_dbus_connection_get_unique_name (dbus->session);
 | |
| 
 | |
|   if (gtk_should_use_portal ())
 | |
|     goto out;
 | |
| 
 | |
|   dbus->cancellable = g_cancellable_new ();
 | |
| 
 | |
|   g_debug ("Connecting to session manager");
 | |
| 
 | |
|   /* Try the GNOME session manager first */
 | |
|   dbus->sm_proxy = gtk_application_get_proxy_if_service_present (dbus->session,
 | |
|                                                                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
 | |
|                                                                  G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
 | |
|                                                                  G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
 | |
|                                                                  GNOME_DBUS_NAME,
 | |
|                                                                  GNOME_DBUS_OBJECT_PATH,
 | |
|                                                                  GNOME_DBUS_INTERFACE,
 | |
|                                                                  &error);
 | |
| 
 | |
|   if (error)
 | |
|     {
 | |
|       g_debug ("Failed to get the GNOME session proxy: %s", error->message);
 | |
|       g_clear_error (&error);
 | |
|     }
 | |
| 
 | |
|   if (!dbus->sm_proxy)
 | |
|     {
 | |
|       /* Fallback to trying the Xfce session manager */
 | |
|       dbus->sm_proxy = gtk_application_get_proxy_if_service_present (dbus->session,
 | |
|                                                                      G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
 | |
|                                                                      G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
 | |
|                                                                      G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
 | |
|                                                                      XFCE_DBUS_NAME,
 | |
|                                                                      XFCE_DBUS_OBJECT_PATH,
 | |
|                                                                      XFCE_DBUS_INTERFACE,
 | |
|                                                                      &error);
 | |
| 
 | |
|       if (error)
 | |
|         {
 | |
|           g_debug ("Failed to get the Xfce session proxy: %s", error->message);
 | |
|           g_clear_error (&error);
 | |
|           goto out;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   if (!register_session)
 | |
|     goto out;
 | |
| 
 | |
|   dbus->ss_proxy = gtk_application_get_proxy_if_service_present (dbus->session,
 | |
|                                                                  G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
 | |
|                                                                  G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
 | |
|                                                                  G_DBUS_PROXY_FLAGS_NONE,
 | |
|                                                                  GNOME_SCREENSAVER_DBUS_NAME,
 | |
|                                                                  GNOME_SCREENSAVER_DBUS_OBJECT_PATH,
 | |
|                                                                  GNOME_SCREENSAVER_DBUS_INTERFACE,
 | |
|                                                                  &error);
 | |
|   if (error)
 | |
|     {
 | |
|       g_debug ("Failed to get the GNOME screensaver proxy: %s", error->message);
 | |
|       g_clear_error (&error);
 | |
|       g_clear_object (&dbus->ss_proxy);
 | |
|     }
 | |
| 
 | |
|   if (dbus->ss_proxy)
 | |
|     {
 | |
|       g_signal_connect (dbus->ss_proxy, "g-signal",
 | |
|                         G_CALLBACK (screensaver_signal_session), impl->application);
 | |
| 
 | |
|       g_dbus_proxy_call (dbus->ss_proxy,
 | |
|                          "GetActive",
 | |
|                          NULL,
 | |
|                          G_DBUS_CALL_FLAGS_NONE,
 | |
|                          G_MAXINT,
 | |
|                          dbus->cancellable,
 | |
|                          ss_get_active_cb,
 | |
|                          dbus);
 | |
|     }
 | |
| 
 | |
|   g_debug ("Registering client '%s' '%s'", dbus->application_id, client_id);
 | |
| 
 | |
|   res = g_dbus_proxy_call_sync (dbus->sm_proxy,
 | |
|                                 "RegisterClient",
 | |
|                                 g_variant_new ("(ss)", dbus->application_id, client_id),
 | |
|                                 G_DBUS_CALL_FLAGS_NONE,
 | |
|                                 G_MAXINT,
 | |
|                                 NULL,
 | |
|                                 &error);
 | |
| 
 | |
|   if (error)
 | |
|     {
 | |
|       g_warning ("Failed to register client: %s", error->message);
 | |
|       g_clear_error (&error);
 | |
|       g_clear_object (&dbus->sm_proxy);
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   g_variant_get (res, "(o)", &dbus->client_path);
 | |
|   g_variant_unref (res);
 | |
| 
 | |
|   g_debug ("Registered client at '%s'", dbus->client_path);
 | |
| 
 | |
|   if (g_str_equal (g_dbus_proxy_get_name (dbus->sm_proxy), GNOME_DBUS_NAME))
 | |
|     {
 | |
|       bus_name = GNOME_DBUS_NAME;
 | |
|       client_interface = GNOME_DBUS_CLIENT_INTERFACE;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       bus_name = XFCE_DBUS_NAME;
 | |
|       client_interface = XFCE_DBUS_CLIENT_INTERFACE;
 | |
|     }
 | |
| 
 | |
|   dbus->client_proxy = g_dbus_proxy_new_sync (dbus->session, 0,
 | |
|                                               NULL,
 | |
|                                               bus_name,
 | |
|                                               dbus->client_path,
 | |
|                                               client_interface,
 | |
|                                               NULL,
 | |
|                                               &error);
 | |
|   if (error)
 | |
|     {
 | |
|       g_warning ("Failed to get client proxy: %s", error->message);
 | |
|       g_clear_error (&error);
 | |
|       g_free (dbus->client_path);
 | |
|       dbus->client_path = NULL;
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   g_signal_connect (dbus->client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), dbus);
 | |
| 
 | |
|  out:
 | |
|   same_bus = FALSE;
 | |
| 
 | |
|   if (dbus->session)
 | |
|     {
 | |
|       const gchar *id;
 | |
|       const gchar *id2;
 | |
|       GValue value = G_VALUE_INIT;
 | |
| 
 | |
|       g_value_init (&value, G_TYPE_STRING);
 | |
|       gdk_screen_get_setting (gdk_screen_get_default (), "gtk-session-bus-id", &value);
 | |
|       id = g_value_get_string (&value);
 | |
| 
 | |
|       if (id && id[0])
 | |
|         {
 | |
|           res = g_dbus_connection_call_sync (dbus->session,
 | |
|                                              "org.freedesktop.DBus",
 | |
|                                              "/org/freedesktop/DBus",
 | |
|                                              "org.freedesktop.DBus",
 | |
|                                              "GetId",
 | |
|                                              NULL,
 | |
|                                              NULL,
 | |
|                                              G_DBUS_CALL_FLAGS_NONE,
 | |
|                                              -1,
 | |
|                                              NULL,
 | |
|                                              NULL);
 | |
|           if (res)
 | |
|             {
 | |
|               g_variant_get (res, "(&s)", &id2);
 | |
| 
 | |
|               if (g_strcmp0 (id, id2) == 0)
 | |
|                 same_bus = TRUE;
 | |
| 
 | |
|               g_variant_unref (res);
 | |
|             }
 | |
|         }
 | |
|       else
 | |
|         same_bus = TRUE;
 | |
| 
 | |
|       g_value_unset (&value);
 | |
|     }
 | |
| 
 | |
|   if (!same_bus)
 | |
|     g_object_set (gtk_settings_get_default (),
 | |
|                   "gtk-shell-shows-app-menu", FALSE,
 | |
|                   "gtk-shell-shows-menubar", FALSE,
 | |
|                   NULL);
 | |
| 
 | |
|   if (dbus->sm_proxy == NULL && dbus->session)
 | |
|     {
 | |
|       dbus->inhibit_proxy = gtk_application_get_proxy_if_service_present (dbus->session,
 | |
|                                                                           G_DBUS_PROXY_FLAGS_NONE,
 | |
|                                                                           "org.freedesktop.portal.Desktop",
 | |
|                                                                           "/org/freedesktop/portal/desktop",
 | |
|                                                                           "org.freedesktop.portal.Inhibit",
 | |
|                                                                           &error);
 | |
|       if (error)
 | |
|         {
 | |
|           g_debug ("Failed to get an inhibit portal proxy: %s", error->message);
 | |
|           g_clear_error (&error);
 | |
|           goto end;
 | |
|         }
 | |
| 
 | |
|       if (register_session)
 | |
|         {
 | |
|           char *token;
 | |
|           GVariantBuilder opt_builder;
 | |
| 
 | |
|           /* Monitor screensaver state */
 | |
| 
 | |
|           dbus->session_id = gtk_get_portal_session_path (dbus->session, &token);
 | |
|           dbus->state_changed_handler =
 | |
|               g_dbus_connection_signal_subscribe (dbus->session,
 | |
|                                                   "org.freedesktop.portal.Desktop",
 | |
|                                                   "org.freedesktop.portal.Inhibit",
 | |
|                                                   "StateChanged",
 | |
|                                                   "/org/freedesktop/portal/desktop",
 | |
|                                                   NULL,
 | |
|                                                   G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
 | |
|                                                   screensaver_signal_portal,
 | |
|                                                   dbus,
 | |
|                                                   NULL);
 | |
|           g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
 | |
|           g_variant_builder_add (&opt_builder, "{sv}",
 | |
|                                  "session_handle_token", g_variant_new_string (token));
 | |
|           g_dbus_proxy_call (dbus->inhibit_proxy,
 | |
|                              "CreateMonitor",
 | |
|                              g_variant_new ("(sa{sv})", "", &opt_builder),
 | |
|                              G_DBUS_CALL_FLAGS_NONE,
 | |
|                              G_MAXINT,
 | |
|                              dbus->cancellable,
 | |
|                              create_monitor_cb, dbus);
 | |
|           g_free (token);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| end:;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_shutdown (GtkApplicationImpl *impl)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
|   g_cancellable_cancel (dbus->cancellable);
 | |
| }
 | |
| 
 | |
| GQuark gtk_application_impl_dbus_export_id_quark (void);
 | |
| 
 | |
| G_DEFINE_QUARK (GtkApplicationImplDBus export id, gtk_application_impl_dbus_export_id)
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_window_added (GtkApplicationImpl *impl,
 | |
|                                         GtkWindow          *window)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
|   GActionGroup *actions;
 | |
|   gchar *path;
 | |
|   guint id;
 | |
| 
 | |
|   if (!dbus->session || !GTK_IS_APPLICATION_WINDOW (window))
 | |
|     return;
 | |
| 
 | |
|   /* Export the action group of this window, based on its id */
 | |
|   actions = gtk_application_window_get_action_group (GTK_APPLICATION_WINDOW (window));
 | |
| 
 | |
|   path = gtk_application_impl_dbus_get_window_path (dbus, window);
 | |
|   id = g_dbus_connection_export_action_group (dbus->session, path, actions, NULL);
 | |
|   g_free (path);
 | |
| 
 | |
|   g_object_set_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark (), GUINT_TO_POINTER (id));
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_window_removed (GtkApplicationImpl *impl,
 | |
|                                           GtkWindow          *window)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
|   guint id;
 | |
| 
 | |
|   id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark ()));
 | |
|   if (id)
 | |
|     {
 | |
|       g_dbus_connection_unexport_action_group (dbus->session, id);
 | |
|       g_object_set_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark (), NULL);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_active_window_changed (GtkApplicationImpl *impl,
 | |
|                                                  GtkWindow          *window)
 | |
| {
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_publish_menu (GtkApplicationImplDBus  *dbus,
 | |
|                                         const gchar             *type,
 | |
|                                         GMenuModel              *model,
 | |
|                                         guint                   *id,
 | |
|                                         gchar                  **path)
 | |
| {
 | |
|   gint i;
 | |
| 
 | |
|   if (dbus->session == NULL)
 | |
|     return;
 | |
| 
 | |
|   /* unexport any existing menu */
 | |
|   if (*id)
 | |
|     {
 | |
|       g_dbus_connection_unexport_menu_model (dbus->session, *id);
 | |
|       g_free (*path);
 | |
|       *path = NULL;
 | |
|       *id = 0;
 | |
|     }
 | |
| 
 | |
|   /* export the new menu, if there is one */
 | |
|   if (model != NULL)
 | |
|     {
 | |
|       /* try getting the preferred name */
 | |
|       *path = g_strconcat (dbus->object_path, "/menus/", type, NULL);
 | |
|       *id = g_dbus_connection_export_menu_model (dbus->session, *path, model, NULL);
 | |
| 
 | |
|       /* keep trying until we get a working name... */
 | |
|       for (i = 0; *id == 0; i++)
 | |
|         {
 | |
|           g_free (*path);
 | |
|           *path = g_strdup_printf ("%s/menus/%s%d", dbus->object_path, type, i);
 | |
|           *id = g_dbus_connection_export_menu_model (dbus->session, *path, model, NULL);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_set_app_menu (GtkApplicationImpl *impl,
 | |
|                                         GMenuModel         *app_menu)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
| 
 | |
|   gtk_application_impl_dbus_publish_menu (dbus, "appmenu", app_menu, &dbus->app_menu_id, &dbus->app_menu_path);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_set_menubar (GtkApplicationImpl *impl,
 | |
|                                        GMenuModel         *menubar)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
| 
 | |
|   gtk_application_impl_dbus_publish_menu (dbus, "menubar", menubar, &dbus->menubar_id, &dbus->menubar_path);
 | |
| }
 | |
| 
 | |
| static GVariant *
 | |
| gtk_application_impl_dbus_real_get_window_system_id (GtkApplicationImplDBus *dbus,
 | |
|                                                      GtkWindow              *window)
 | |
| {
 | |
|   return g_variant_new_uint32 (0);
 | |
| }
 | |
| 
 | |
| /* returns floating */
 | |
| static GVariant *
 | |
| gtk_application_impl_dbus_get_window_system_id (GtkApplicationImplDBus *dbus,
 | |
|                                                 GtkWindow              *window)
 | |
| {
 | |
|   return GTK_APPLICATION_IMPL_DBUS_GET_CLASS (dbus)->get_window_system_id (dbus, window);
 | |
| }
 | |
| 
 | |
| static int next_cookie;
 | |
| 
 | |
| typedef struct {
 | |
|   char *handle;
 | |
|   int cookie;
 | |
| } InhibitHandle;
 | |
| 
 | |
| static void
 | |
| inhibit_handle_free (gpointer data)
 | |
| {
 | |
|   InhibitHandle *handle = data;
 | |
| 
 | |
|   g_free (handle->handle);
 | |
|   g_free (handle);
 | |
| }
 | |
| 
 | |
| static guint
 | |
| gtk_application_impl_dbus_inhibit (GtkApplicationImpl         *impl,
 | |
|                                    GtkWindow                  *window,
 | |
|                                    GtkApplicationInhibitFlags  flags,
 | |
|                                    const gchar                *reason)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
|   GVariant *res;
 | |
|   GError *error = NULL;
 | |
|   guint cookie;
 | |
|   static gboolean warned = FALSE;
 | |
| 
 | |
|   if (dbus->sm_proxy)
 | |
|     {
 | |
|       res = g_dbus_proxy_call_sync (dbus->sm_proxy,
 | |
|                                     "Inhibit",
 | |
|                                     g_variant_new ("(s@usu)",
 | |
|                                                    dbus->application_id,
 | |
|                                                    window ? gtk_application_impl_dbus_get_window_system_id (dbus, window) : g_variant_new_uint32 (0),
 | |
|                                                    reason ? reason : "",
 | |
|                                                    flags),
 | |
|                                     G_DBUS_CALL_FLAGS_NONE,
 | |
|                                     G_MAXINT,
 | |
|                                     NULL,
 | |
|                                     &error);
 | |
| 
 | |
|       if (res)
 | |
|         {
 | |
|           g_variant_get (res, "(u)", &cookie);
 | |
|           g_variant_unref (res);
 | |
|           return cookie;
 | |
|         }
 | |
| 
 | |
|       if (error)
 | |
|         {
 | |
|           if (!warned)
 | |
|             {
 | |
|               g_warning ("Calling %s.Inhibit failed: %s",
 | |
|                          g_dbus_proxy_get_interface_name (dbus->sm_proxy),
 | |
|                          error->message);
 | |
|               warned = TRUE;
 | |
|             }
 | |
|           g_clear_error (&error);
 | |
|         }
 | |
|     }
 | |
|   else if (dbus->inhibit_proxy)
 | |
|     {
 | |
|       GVariantBuilder options;
 | |
| 
 | |
|       g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
 | |
|       g_variant_builder_add (&options, "{sv}", "reason", g_variant_new_string (reason));
 | |
|       res = g_dbus_proxy_call_sync (dbus->inhibit_proxy,
 | |
|                                     "Inhibit",
 | |
|                                     g_variant_new ("(su@a{sv})",
 | |
|                                                    "", /* window */
 | |
|                                                    flags,
 | |
|                                                    g_variant_builder_end (&options)),
 | |
|                                     G_DBUS_CALL_FLAGS_NONE,
 | |
|                                     G_MAXINT,
 | |
|                                     NULL,
 | |
|                                     &error);
 | |
|       if (res)
 | |
|         {
 | |
|           InhibitHandle *handle;
 | |
| 
 | |
|           handle = g_new (InhibitHandle, 1);
 | |
|           handle->cookie = ++next_cookie;
 | |
| 
 | |
|           g_variant_get (res, "(o)", &handle->handle);
 | |
|           g_variant_unref (res);
 | |
| 
 | |
|           dbus->inhibit_handles = g_slist_prepend (dbus->inhibit_handles, handle);
 | |
| 
 | |
|           return handle->cookie;
 | |
|         }
 | |
| 
 | |
|       if (error)
 | |
|         {
 | |
|           if (!warned)
 | |
|             {
 | |
|               g_warning ("Calling %s.Inhibit failed: %s",
 | |
|                          g_dbus_proxy_get_interface_name (dbus->inhibit_proxy),
 | |
|                          error->message);
 | |
|               warned = TRUE;
 | |
|             }
 | |
|           g_clear_error (&error);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_uninhibit (GtkApplicationImpl *impl,
 | |
|                                      guint               cookie)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
| 
 | |
|   if (dbus->sm_proxy)
 | |
|     {
 | |
|       g_dbus_proxy_call (dbus->sm_proxy,
 | |
|                          "Uninhibit",
 | |
|                          g_variant_new ("(u)", cookie),
 | |
|                          G_DBUS_CALL_FLAGS_NONE,
 | |
|                          G_MAXINT,
 | |
|                          NULL, NULL, NULL);
 | |
|     }
 | |
|   else if (dbus->inhibit_proxy)
 | |
|     {
 | |
|       GSList *l;
 | |
| 
 | |
|       for (l = dbus->inhibit_handles; l; l = l->next)
 | |
|         {
 | |
|           InhibitHandle *handle = l->data;
 | |
|           if (handle->cookie == cookie)
 | |
|             {
 | |
|               g_dbus_connection_call (dbus->session,
 | |
|                                       "org.freedesktop.portal.Desktop",
 | |
|                                       handle->handle,
 | |
|                                       "org.freedesktop.portal.Request",
 | |
|                                       "Close",
 | |
|                                       g_variant_new ("()"),
 | |
|                                       G_VARIANT_TYPE_UNIT,
 | |
|                                       G_DBUS_CALL_FLAGS_NONE,
 | |
|                                       G_MAXINT,
 | |
|                                       NULL, NULL, NULL);
 | |
|               dbus->inhibit_handles = g_slist_remove (dbus->inhibit_handles, handle);
 | |
|               inhibit_handle_free (handle);
 | |
|               break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| gtk_application_impl_dbus_is_inhibited (GtkApplicationImpl         *impl,
 | |
|                                         GtkApplicationInhibitFlags  flags)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
 | |
|   GVariant *res;
 | |
|   GError *error = NULL;
 | |
|   gboolean inhibited;
 | |
|   static gboolean warned = FALSE;
 | |
| 
 | |
|   if (dbus->sm_proxy == NULL)
 | |
|     return FALSE;
 | |
| 
 | |
|   res = g_dbus_proxy_call_sync (dbus->sm_proxy,
 | |
|                                 "IsInhibited",
 | |
|                                 g_variant_new ("(u)", flags),
 | |
|                                 G_DBUS_CALL_FLAGS_NONE,
 | |
|                                 G_MAXINT,
 | |
|                                 NULL,
 | |
|                                 &error);
 | |
|   if (error)
 | |
|     {
 | |
|       if (!warned)
 | |
|         {
 | |
|           g_warning ("Calling %s.IsInhibited failed: %s",
 | |
|                      g_dbus_proxy_get_interface_name (dbus->sm_proxy),
 | |
|                      error->message);
 | |
|           warned = TRUE;
 | |
|         }
 | |
|       g_error_free (error);
 | |
|       return FALSE;
 | |
|     }
 | |
| 
 | |
|   g_variant_get (res, "(b)", &inhibited);
 | |
|   g_variant_unref (res);
 | |
| 
 | |
|   return inhibited;
 | |
| }
 | |
| 
 | |
| static gboolean
 | |
| gtk_application_impl_dbus_prefers_app_menu (GtkApplicationImpl *impl)
 | |
| {
 | |
|   static gboolean decided;
 | |
|   static gboolean result;
 | |
| 
 | |
|   /* We do not support notifying if/when the result changes, so make
 | |
|    * sure that once we give an answer, we will always give the same one.
 | |
|    */
 | |
|   if (!decided)
 | |
|     {
 | |
|       GtkSettings *gtk_settings;
 | |
|       gboolean show_app_menu;
 | |
|       gboolean show_menubar;
 | |
| 
 | |
|       gtk_settings = gtk_settings_get_default ();
 | |
|       g_object_get (G_OBJECT (gtk_settings),
 | |
|                     "gtk-shell-shows-app-menu", &show_app_menu,
 | |
|                     "gtk-shell-shows-menubar", &show_menubar,
 | |
|                     NULL);
 | |
| 
 | |
|       /* We prefer traditional menus when we have a shell that doesn't
 | |
|        * show the appmenu or we have a shell that shows menubars
 | |
|        * (ie: Unity)
 | |
|        */
 | |
|       result = show_app_menu && !show_menubar;
 | |
|       decided = TRUE;
 | |
|     }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_init (GtkApplicationImplDBus *dbus)
 | |
| {
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_finalize (GObject *object)
 | |
| {
 | |
|   GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) object;
 | |
| 
 | |
|   if (dbus->session_id)
 | |
|     {
 | |
|       g_dbus_connection_call (dbus->session,
 | |
|                               "org.freedesktop.portal.Desktop",
 | |
|                               dbus->session_id,
 | |
|                               "org.freedesktop.portal.Session",
 | |
|                               "Close",
 | |
|                               NULL, NULL, 0, -1, NULL, NULL, NULL);
 | |
| 
 | |
|       g_free (dbus->session_id);
 | |
|     }
 | |
| 
 | |
|   if (dbus->state_changed_handler)
 | |
|     g_dbus_connection_signal_unsubscribe (dbus->session,
 | |
|                                           dbus->state_changed_handler);
 | |
| 
 | |
|   g_clear_object (&dbus->inhibit_proxy);
 | |
|   g_slist_free_full (dbus->inhibit_handles, inhibit_handle_free);
 | |
|   g_free (dbus->app_menu_path);
 | |
|   g_free (dbus->menubar_path);
 | |
|   g_clear_object (&dbus->sm_proxy);
 | |
|   if (dbus->ss_proxy)
 | |
|     g_signal_handlers_disconnect_by_func (dbus->ss_proxy, screensaver_signal_session, dbus->impl.application);
 | |
|   g_clear_object (&dbus->ss_proxy);
 | |
|   g_clear_object (&dbus->cancellable);
 | |
| 
 | |
|   G_OBJECT_CLASS (gtk_application_impl_dbus_parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_application_impl_dbus_class_init (GtkApplicationImplDBusClass *class)
 | |
| {
 | |
|   GtkApplicationImplClass *impl_class = GTK_APPLICATION_IMPL_CLASS (class);
 | |
|   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
 | |
| 
 | |
|   class->get_window_system_id = gtk_application_impl_dbus_real_get_window_system_id;
 | |
| 
 | |
|   impl_class->startup = gtk_application_impl_dbus_startup;
 | |
|   impl_class->shutdown = gtk_application_impl_dbus_shutdown;
 | |
|   impl_class->window_added = gtk_application_impl_dbus_window_added;
 | |
|   impl_class->window_removed = gtk_application_impl_dbus_window_removed;
 | |
|   impl_class->active_window_changed = gtk_application_impl_dbus_active_window_changed;
 | |
|   impl_class->set_app_menu = gtk_application_impl_dbus_set_app_menu;
 | |
|   impl_class->set_menubar = gtk_application_impl_dbus_set_menubar;
 | |
|   impl_class->inhibit = gtk_application_impl_dbus_inhibit;
 | |
|   impl_class->uninhibit = gtk_application_impl_dbus_uninhibit;
 | |
|   impl_class->is_inhibited = gtk_application_impl_dbus_is_inhibited;
 | |
|   impl_class->prefers_app_menu = gtk_application_impl_dbus_prefers_app_menu;
 | |
| 
 | |
|   gobject_class->finalize = gtk_application_impl_dbus_finalize;
 | |
| }
 | |
| 
 | |
| gchar *
 | |
| gtk_application_impl_dbus_get_window_path (GtkApplicationImplDBus *dbus,
 | |
|                                            GtkWindow *window)
 | |
| {
 | |
|   if (dbus->session && GTK_IS_APPLICATION_WINDOW (window))
 | |
|     return g_strdup_printf ("%s/window/%d",
 | |
|                             dbus->object_path,
 | |
|                             gtk_application_window_get_id (GTK_APPLICATION_WINDOW (window)));
 | |
|   else
 | |
|     return NULL;
 | |
| }
 | 
