1/*
2 * Copyright © 2010 Codethink Limited
3 * Copyright © 2012 Red Hat, Inc.
4 * Copyright © 2013 Canonical Limited
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 of the licence, 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 Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Ryan Lortie <desrt@desrt.ca>
20 * Matthias Clasen <mclasen@redhat.com>
21 */
22
23#include "config.h"
24
25#include "gtkapplicationprivate.h"
26#include "gtksettings.h"
27#include "gtkprivate.h"
28#include "gtkintl.h"
29
30#include "gdk/gdkconstructor.h"
31
32G_DEFINE_TYPE (GtkApplicationImplDBus, gtk_application_impl_dbus, GTK_TYPE_APPLICATION_IMPL)
33
34#define DBUS_BUS_NAME "org.freedesktop.DBus"
35#define DBUS_OBJECT_PATH "/org/freedesktop/DBus"
36#define DBUS_BUS_INTERFACE "org.freedesktop.DBus"
37#define GNOME_DBUS_NAME "org.gnome.SessionManager"
38#define GNOME_DBUS_OBJECT_PATH "/org/gnome/SessionManager"
39#define GNOME_DBUS_INTERFACE "org.gnome.SessionManager"
40#define GNOME_DBUS_CLIENT_INTERFACE "org.gnome.SessionManager.ClientPrivate"
41#define XFCE_DBUS_NAME "org.xfce.SessionManager"
42#define XFCE_DBUS_OBJECT_PATH "/org/xfce/SessionManager"
43#define XFCE_DBUS_INTERFACE "org.xfce.Session.Manager"
44#define XFCE_DBUS_CLIENT_INTERFACE "org.xfce.Session.Client"
45#define GNOME_SCREENSAVER_DBUS_NAME "org.gnome.ScreenSaver"
46#define GNOME_SCREENSAVER_DBUS_OBJECT_PATH "/org/gnome/ScreenSaver"
47#define GNOME_SCREENSAVER_DBUS_INTERFACE "org.gnome.ScreenSaver"
48
49static void
50unregister_client (GtkApplicationImplDBus *dbus)
51{
52 GError *error = NULL;
53
54 g_debug ("Unregistering client");
55
56 g_dbus_proxy_call_sync (proxy: dbus->sm_proxy,
57 method_name: "UnregisterClient",
58 parameters: g_variant_new (format_string: "(o)", dbus->client_path),
59 flags: G_DBUS_CALL_FLAGS_NONE,
60 G_MAXINT,
61 NULL,
62 error: &error);
63
64 if (error)
65 {
66 g_warning ("Failed to unregister client: %s", error->message);
67 g_error_free (error);
68 }
69
70 g_clear_object (&dbus->client_proxy);
71
72 g_free (mem: dbus->client_path);
73 dbus->client_path = NULL;
74}
75
76static void
77send_quit_response (GtkApplicationImplDBus *dbus,
78 gboolean will_quit,
79 const char *reason)
80{
81 g_debug ("Calling EndSessionResponse %d '%s'", will_quit, reason);
82
83 g_dbus_proxy_call (proxy: dbus->client_proxy,
84 method_name: "EndSessionResponse",
85 parameters: g_variant_new (format_string: "(bs)", will_quit, reason ? reason : ""),
86 flags: G_DBUS_CALL_FLAGS_NONE,
87 G_MAXINT,
88 NULL, NULL, NULL);
89}
90
91static void
92client_proxy_signal (GDBusProxy *proxy,
93 const char *sender_name,
94 const char *signal_name,
95 GVariant *parameters,
96 gpointer user_data)
97{
98 GtkApplicationImplDBus *dbus = user_data;
99
100 if (g_str_equal (v1: signal_name, v2: "QueryEndSession"))
101 {
102 g_debug ("Received QueryEndSession");
103 g_signal_emit_by_name (instance: dbus->impl.application, detailed_signal: "query-end");
104 send_quit_response (dbus, TRUE, NULL);
105 }
106 else if (g_str_equal (v1: signal_name, v2: "CancelEndSession"))
107 {
108 g_debug ("Received CancelEndSession");
109 }
110 else if (g_str_equal (v1: signal_name, v2: "EndSession"))
111 {
112 g_debug ("Received EndSession");
113 send_quit_response (dbus, TRUE, NULL);
114 unregister_client (dbus);
115 g_application_quit (G_APPLICATION (dbus->impl.application));
116 }
117 else if (g_str_equal (v1: signal_name, v2: "Stop"))
118 {
119 g_debug ("Received Stop");
120 unregister_client (dbus);
121 g_application_quit (G_APPLICATION (dbus->impl.application));
122 }
123}
124
125static GDBusProxy*
126gtk_application_get_proxy_if_service_present (GDBusConnection *connection,
127 GDBusProxyFlags flags,
128 const char *bus_name,
129 const char *object_path,
130 const char *interface,
131 GError **error)
132{
133 GDBusProxy *proxy;
134 char *owner;
135
136 proxy = g_dbus_proxy_new_sync (connection,
137 flags,
138 NULL,
139 name: bus_name,
140 object_path,
141 interface_name: interface,
142 NULL,
143 error);
144
145 if (!proxy)
146 return NULL;
147
148 /* is there anyone actually providing the service? */
149 owner = g_dbus_proxy_get_name_owner (proxy);
150 if (owner == NULL)
151 {
152 g_clear_object (&proxy);
153 g_set_error (err: error, G_DBUS_ERROR, code: G_DBUS_ERROR_NAME_HAS_NO_OWNER,
154 format: "The name %s is not owned", bus_name);
155 }
156 else
157 g_free (mem: owner);
158
159 return proxy;
160}
161
162#ifdef G_HAS_CONSTRUCTORS
163#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
164#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(stash_desktop_autostart_id)
165#endif
166G_DEFINE_CONSTRUCTOR(stash_desktop_autostart_id)
167#endif
168
169static char *client_id = NULL;
170
171static void
172stash_desktop_autostart_id (void)
173{
174 const char *desktop_autostart_id;
175
176 desktop_autostart_id = g_getenv (variable: "DESKTOP_AUTOSTART_ID");
177 client_id = g_strdup (str: desktop_autostart_id ? desktop_autostart_id : "");
178
179 /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
180 * use the same client id.
181 */
182 g_unsetenv (variable: "DESKTOP_AUTOSTART_ID");
183}
184
185static void
186screensaver_signal_session (GDBusProxy *proxy,
187 const char *sender_name,
188 const char *signal_name,
189 GVariant *parameters,
190 GtkApplication *application)
191{
192 gboolean active;
193
194 if (!g_str_equal (v1: signal_name, v2: "ActiveChanged"))
195 return;
196
197 g_variant_get (value: parameters, format_string: "(b)", &active);
198 gtk_application_set_screensaver_active (application, active);
199}
200
201enum {
202 UNKNOWN = 0,
203 RUNNING = 1,
204 QUERY_END = 2,
205 ENDING = 3
206};
207
208static void
209screensaver_signal_portal (GDBusConnection *connection,
210 const char *sender_name,
211 const char *object_path,
212 const char *interface_name,
213 const char *signal_name,
214 GVariant *parameters,
215 gpointer data)
216{
217 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *)data;
218 GtkApplication *application = data;
219 gboolean active;
220 GVariant *state;
221 guint32 session_state = UNKNOWN;
222
223 if (!g_str_equal (v1: signal_name, v2: "StateChanged"))
224 return;
225
226 g_variant_get (value: parameters, format_string: "(o@a{sv})", NULL, &state);
227 g_variant_lookup (dictionary: state, key: "screensaver-active", format_string: "b", &active);
228 gtk_application_set_screensaver_active (application: dbus->impl.application, active);
229
230 g_variant_lookup (dictionary: state, key: "session-state", format_string: "u", &session_state);
231 if (session_state != dbus->session_state)
232 {
233 dbus->session_state = session_state;
234
235 /* Note that we'll only ever get here if we get a session-state,
236 * in which case, the interface is new enough to have QueryEndResponse.
237 */
238 if (session_state == ENDING)
239 {
240 g_application_quit (G_APPLICATION (application));
241 }
242 else if (session_state == QUERY_END)
243 {
244 g_signal_emit_by_name (instance: dbus->impl.application, detailed_signal: "query-end");
245
246 g_dbus_proxy_call (proxy: dbus->inhibit_proxy,
247 method_name: "QueryEndResponse",
248 parameters: g_variant_new (format_string: "(o)", dbus->session_id),
249 flags: G_DBUS_CALL_FLAGS_NONE,
250 G_MAXINT,
251 NULL,
252 NULL, NULL);
253 }
254 }
255}
256
257static void
258ss_get_active_cb (GObject *source,
259 GAsyncResult *result,
260 gpointer data)
261{
262 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) data;
263 GDBusProxy *proxy = G_DBUS_PROXY (source);
264 GError *error = NULL;
265 GVariant *ret;
266 gboolean active;
267
268 ret = g_dbus_proxy_call_finish (proxy, res: result, error: &error);
269 if (ret == NULL)
270 {
271 g_warning ("Getting screensaver status failed: %s", error->message);
272 g_error_free (error);
273 return;
274 }
275
276 g_variant_get (value: ret, format_string: "(b)", &active);
277 g_variant_unref (value: ret);
278 gtk_application_set_screensaver_active (application: dbus->impl.application, active);
279}
280
281static void
282create_monitor_cb (GObject *source,
283 GAsyncResult *result,
284 gpointer data)
285{
286 GDBusProxy *proxy = G_DBUS_PROXY (source);
287 GError *error = NULL;
288 GVariant *ret = NULL;
289
290 ret = g_dbus_proxy_call_finish (proxy, res: result, error: &error);
291 if (ret == NULL)
292 {
293 g_warning ("Creating a portal monitor failed: %s", error->message);
294 g_error_free (error);
295 return;
296 }
297
298 g_variant_unref (value: ret);
299}
300
301static void
302gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
303 gboolean register_session)
304{
305 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
306 GError *error = NULL;
307 GVariant *res;
308 gboolean same_bus;
309 const char *bus_name;
310 const char *client_interface;
311
312#ifndef G_HAS_CONSTRUCTORS
313 stash_desktop_autostart_id ();
314#endif
315
316 dbus->session = g_application_get_dbus_connection (G_APPLICATION (impl->application));
317
318 if (!dbus->session)
319 goto out;
320
321 dbus->application_id = g_application_get_application_id (G_APPLICATION (impl->application));
322 dbus->object_path = g_application_get_dbus_object_path (G_APPLICATION (impl->application));
323 dbus->unique_name = g_dbus_connection_get_unique_name (connection: dbus->session);
324
325 if (gdk_should_use_portal ())
326 goto out;
327
328 g_debug ("Connecting to session manager");
329
330 /* Try the GNOME session manager first */
331 dbus->sm_proxy = gtk_application_get_proxy_if_service_present (connection: dbus->session,
332 flags: G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
333 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
334 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
335 GNOME_DBUS_NAME,
336 GNOME_DBUS_OBJECT_PATH,
337 GNOME_DBUS_INTERFACE,
338 error: &error);
339
340 if (error)
341 {
342 g_debug ("Failed to get the GNOME session proxy: %s", error->message);
343 g_clear_error (err: &error);
344 }
345
346 if (!dbus->sm_proxy)
347 {
348 /* Fallback to trying the Xfce session manager */
349 dbus->sm_proxy = gtk_application_get_proxy_if_service_present (connection: dbus->session,
350 flags: G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
351 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
352 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
353 XFCE_DBUS_NAME,
354 XFCE_DBUS_OBJECT_PATH,
355 XFCE_DBUS_INTERFACE,
356 error: &error);
357
358 if (error)
359 {
360 g_debug ("Failed to get the Xfce session proxy: %s", error->message);
361 g_clear_error (err: &error);
362 goto out;
363 }
364 }
365
366 if (!register_session)
367 goto out;
368
369 dbus->ss_proxy = gtk_application_get_proxy_if_service_present (connection: dbus->session,
370 flags: G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
371 G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
372 G_DBUS_PROXY_FLAGS_NONE,
373 GNOME_SCREENSAVER_DBUS_NAME,
374 GNOME_SCREENSAVER_DBUS_OBJECT_PATH,
375 GNOME_SCREENSAVER_DBUS_INTERFACE,
376 error: &error);
377 if (error)
378 {
379 g_debug ("Failed to get the GNOME screensaver proxy: %s", error->message);
380 g_clear_error (err: &error);
381 g_clear_object (&dbus->ss_proxy);
382 }
383
384 if (dbus->ss_proxy)
385 {
386 g_signal_connect (dbus->ss_proxy, "g-signal",
387 G_CALLBACK (screensaver_signal_session), impl->application);
388
389 g_dbus_proxy_call (proxy: dbus->ss_proxy,
390 method_name: "GetActive",
391 NULL,
392 flags: G_DBUS_CALL_FLAGS_NONE,
393 G_MAXINT,
394 NULL,
395 callback: ss_get_active_cb,
396 user_data: dbus);
397 }
398
399 g_debug ("Registering client '%s' '%s'", dbus->application_id, client_id);
400
401 res = g_dbus_proxy_call_sync (proxy: dbus->sm_proxy,
402 method_name: "RegisterClient",
403 parameters: g_variant_new (format_string: "(ss)", dbus->application_id, client_id),
404 flags: G_DBUS_CALL_FLAGS_NONE,
405 G_MAXINT,
406 NULL,
407 error: &error);
408
409 if (error)
410 {
411 g_warning ("Failed to register client: %s", error->message);
412 g_clear_error (err: &error);
413 g_clear_object (&dbus->sm_proxy);
414 goto out;
415 }
416
417 g_variant_get (value: res, format_string: "(o)", &dbus->client_path);
418 g_variant_unref (value: res);
419
420 g_debug ("Registered client at '%s'", dbus->client_path);
421
422 if (g_str_equal (v1: g_dbus_proxy_get_name (proxy: dbus->sm_proxy), GNOME_DBUS_NAME))
423 {
424 bus_name = GNOME_DBUS_NAME;
425 client_interface = GNOME_DBUS_CLIENT_INTERFACE;
426 }
427 else
428 {
429 bus_name = XFCE_DBUS_NAME;
430 client_interface = XFCE_DBUS_CLIENT_INTERFACE;
431 }
432
433 dbus->client_proxy = g_dbus_proxy_new_sync (connection: dbus->session, flags: 0,
434 NULL,
435 name: bus_name,
436 object_path: dbus->client_path,
437 interface_name: client_interface,
438 NULL,
439 error: &error);
440 if (error)
441 {
442 g_warning ("Failed to get client proxy: %s", error->message);
443 g_clear_error (err: &error);
444 g_free (mem: dbus->client_path);
445 dbus->client_path = NULL;
446 goto out;
447 }
448
449 g_signal_connect (dbus->client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), dbus);
450
451 out:
452 same_bus = FALSE;
453
454 if (dbus->session)
455 {
456 const char *id;
457 const char *id2;
458 GValue value = G_VALUE_INIT;
459
460 g_value_init (value: &value, G_TYPE_STRING);
461 gdk_display_get_setting (display: gdk_display_get_default (), name: "gtk-session-bus-id", value: &value);
462 id = g_value_get_string (value: &value);
463
464 if (id && id[0])
465 {
466 res = g_dbus_connection_call_sync (connection: dbus->session,
467 DBUS_BUS_NAME,
468 DBUS_OBJECT_PATH,
469 DBUS_BUS_INTERFACE,
470 method_name: "GetId",
471 NULL,
472 NULL,
473 flags: G_DBUS_CALL_FLAGS_NONE,
474 timeout_msec: -1,
475 NULL,
476 NULL);
477 if (res)
478 {
479 g_variant_get (value: res, format_string: "(&s)", &id2);
480
481 if (g_strcmp0 (str1: id, str2: id2) == 0)
482 same_bus = TRUE;
483
484 g_variant_unref (value: res);
485 }
486 }
487 else
488 same_bus = TRUE;
489
490 g_value_unset (value: &value);
491 }
492
493 if (!same_bus)
494 g_object_set (object: gtk_settings_get_default (),
495 first_property_name: "gtk-shell-shows-app-menu", FALSE,
496 "gtk-shell-shows-menubar", FALSE,
497 NULL);
498
499 if (dbus->sm_proxy == NULL && dbus->session)
500 {
501 dbus->inhibit_proxy = gtk_application_get_proxy_if_service_present (connection: dbus->session,
502 flags: G_DBUS_PROXY_FLAGS_NONE,
503 PORTAL_BUS_NAME,
504 PORTAL_OBJECT_PATH,
505 PORTAL_INHIBIT_INTERFACE,
506 error: &error);
507 if (error)
508 {
509 g_debug ("Failed to get an inhibit portal proxy: %s", error->message);
510 g_clear_error (err: &error);
511 return;
512 }
513
514 if (register_session)
515 {
516 char *token;
517 GVariantBuilder opt_builder;
518
519 /* Monitor screensaver state */
520
521 dbus->session_id = gtk_get_portal_session_path (connection: dbus->session, token: &token);
522 dbus->state_changed_handler =
523 g_dbus_connection_signal_subscribe (connection: dbus->session,
524 PORTAL_BUS_NAME,
525 PORTAL_INHIBIT_INTERFACE,
526 member: "StateChanged",
527 PORTAL_OBJECT_PATH,
528 NULL,
529 flags: G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
530 callback: screensaver_signal_portal,
531 user_data: dbus,
532 NULL);
533 g_variant_builder_init (builder: &opt_builder, G_VARIANT_TYPE_VARDICT);
534 g_variant_builder_add (builder: &opt_builder, format_string: "{sv}",
535 "session_handle_token", g_variant_new_string (string: token));
536 g_dbus_proxy_call (proxy: dbus->inhibit_proxy,
537 method_name: "CreateMonitor",
538 parameters: g_variant_new (format_string: "(sa{sv})", "", &opt_builder),
539 flags: G_DBUS_CALL_FLAGS_NONE,
540 G_MAXINT,
541 NULL,
542 callback: create_monitor_cb, user_data: dbus);
543 g_free (mem: token);
544 }
545 }
546}
547
548static void
549gtk_application_impl_dbus_shutdown (GtkApplicationImpl *impl)
550{
551}
552
553GQuark gtk_application_impl_dbus_export_id_quark (void);
554
555G_DEFINE_QUARK (GtkApplicationImplDBus export id, gtk_application_impl_dbus_export_id)
556
557static void
558gtk_application_impl_dbus_window_added (GtkApplicationImpl *impl,
559 GtkWindow *window)
560{
561 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
562 GActionGroup *actions;
563 char *path;
564 guint id;
565
566 if (!dbus->session || !GTK_IS_APPLICATION_WINDOW (window))
567 return;
568
569 /* Export the action group of this window, based on its id */
570 actions = gtk_application_window_get_action_group (GTK_APPLICATION_WINDOW (window));
571
572 path = gtk_application_impl_dbus_get_window_path (dbus, window);
573 id = g_dbus_connection_export_action_group (connection: dbus->session, object_path: path, action_group: actions, NULL);
574 g_free (mem: path);
575
576 g_object_set_qdata (G_OBJECT (window), quark: gtk_application_impl_dbus_export_id_quark (), GUINT_TO_POINTER (id));
577}
578
579static void
580gtk_application_impl_dbus_window_removed (GtkApplicationImpl *impl,
581 GtkWindow *window)
582{
583 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
584 guint id;
585
586 id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark ()));
587 if (id)
588 {
589 g_dbus_connection_unexport_action_group (connection: dbus->session, export_id: id);
590 g_object_set_qdata (G_OBJECT (window), quark: gtk_application_impl_dbus_export_id_quark (), NULL);
591 }
592}
593
594static void
595gtk_application_impl_dbus_active_window_changed (GtkApplicationImpl *impl,
596 GtkWindow *window)
597{
598}
599
600static void
601gtk_application_impl_dbus_publish_menu (GtkApplicationImplDBus *dbus,
602 const char *type,
603 GMenuModel *model,
604 guint *id,
605 char **path)
606{
607 int i;
608
609 if (dbus->session == NULL)
610 return;
611
612 /* unexport any existing menu */
613 if (*id)
614 {
615 g_dbus_connection_unexport_menu_model (connection: dbus->session, export_id: *id);
616 g_free (mem: *path);
617 *path = NULL;
618 *id = 0;
619 }
620
621 /* export the new menu, if there is one */
622 if (model != NULL)
623 {
624 /* try getting the preferred name */
625 *path = g_strconcat (string1: dbus->object_path, "/menus/", type, NULL);
626 *id = g_dbus_connection_export_menu_model (connection: dbus->session, object_path: *path, menu: model, NULL);
627
628 /* keep trying until we get a working name... */
629 for (i = 0; *id == 0; i++)
630 {
631 g_free (mem: *path);
632 *path = g_strdup_printf (format: "%s/menus/%s%d", dbus->object_path, type, i);
633 *id = g_dbus_connection_export_menu_model (connection: dbus->session, object_path: *path, menu: model, NULL);
634 }
635 }
636}
637
638static void
639gtk_application_impl_dbus_set_app_menu (GtkApplicationImpl *impl,
640 GMenuModel *app_menu)
641{
642 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
643
644 gtk_application_impl_dbus_publish_menu (dbus, type: "appmenu", model: app_menu, id: &dbus->app_menu_id, path: &dbus->app_menu_path);
645}
646
647static void
648gtk_application_impl_dbus_set_menubar (GtkApplicationImpl *impl,
649 GMenuModel *menubar)
650{
651 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
652
653 gtk_application_impl_dbus_publish_menu (dbus, type: "menubar", model: menubar, id: &dbus->menubar_id, path: &dbus->menubar_path);
654}
655
656static GVariant *
657gtk_application_impl_dbus_real_get_window_system_id (GtkApplicationImplDBus *dbus,
658 GtkWindow *window)
659{
660 return g_variant_new_uint32 (value: 0);
661}
662
663/* returns floating */
664static GVariant *
665gtk_application_impl_dbus_get_window_system_id (GtkApplicationImplDBus *dbus,
666 GtkWindow *window)
667{
668 return GTK_APPLICATION_IMPL_DBUS_GET_CLASS (dbus)->get_window_system_id (dbus, window);
669}
670
671static int next_cookie;
672
673typedef struct {
674 char *handle;
675 int cookie;
676} InhibitHandle;
677
678static void
679inhibit_handle_free (gpointer data)
680{
681 InhibitHandle *handle = data;
682
683 g_free (mem: handle->handle);
684 g_free (mem: handle);
685}
686
687static guint
688gtk_application_impl_dbus_inhibit (GtkApplicationImpl *impl,
689 GtkWindow *window,
690 GtkApplicationInhibitFlags flags,
691 const char *reason)
692{
693 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
694 GVariant *res;
695 GError *error = NULL;
696 guint cookie;
697 static gboolean warned = FALSE;
698
699 if (dbus->sm_proxy)
700 {
701 if (reason == NULL)
702 /* Translators: This is the 'reason' given when inhibiting
703 * suspend or screen locking, and the caller hasn't specified
704 * a reason.
705 */
706 reason = _("Reason not specified");
707
708 res = g_dbus_proxy_call_sync (proxy: dbus->sm_proxy,
709 method_name: "Inhibit",
710 parameters: g_variant_new (format_string: "(s@usu)",
711 dbus->application_id,
712 window ? gtk_application_impl_dbus_get_window_system_id (dbus, window) : g_variant_new_uint32 (value: 0),
713 reason,
714 flags),
715 flags: G_DBUS_CALL_FLAGS_NONE,
716 G_MAXINT,
717 NULL,
718 error: &error);
719
720 if (res)
721 {
722 g_variant_get (value: res, format_string: "(u)", &cookie);
723 g_variant_unref (value: res);
724 return cookie;
725 }
726
727 if (error)
728 {
729 if (!warned)
730 {
731 g_warning ("Calling %s.Inhibit failed: %s",
732 g_dbus_proxy_get_interface_name (dbus->sm_proxy),
733 error->message);
734 warned = TRUE;
735 }
736 g_clear_error (err: &error);
737 }
738 }
739 else if (dbus->inhibit_proxy)
740 {
741 GVariantBuilder options;
742
743 g_variant_builder_init (builder: &options, G_VARIANT_TYPE_VARDICT);
744 g_variant_builder_add (builder: &options, format_string: "{sv}", "reason", g_variant_new_string (string: reason));
745 res = g_dbus_proxy_call_sync (proxy: dbus->inhibit_proxy,
746 method_name: "Inhibit",
747 parameters: g_variant_new (format_string: "(su@a{sv})",
748 "", /* window */
749 flags,
750 g_variant_builder_end (builder: &options)),
751 flags: G_DBUS_CALL_FLAGS_NONE,
752 G_MAXINT,
753 NULL,
754 error: &error);
755 if (res)
756 {
757 InhibitHandle *handle;
758
759 handle = g_new (InhibitHandle, 1);
760 handle->cookie = ++next_cookie;
761
762 g_variant_get (value: res, format_string: "(o)", &handle->handle);
763 g_variant_unref (value: res);
764
765 dbus->inhibit_handles = g_slist_prepend (list: dbus->inhibit_handles, data: handle);
766
767 return handle->cookie;
768 }
769
770 if (error)
771 {
772 if (!warned)
773 {
774 g_warning ("Calling %s.Inhibit failed: %s",
775 g_dbus_proxy_get_interface_name (dbus->inhibit_proxy),
776 error->message);
777 warned = TRUE;
778 }
779 g_clear_error (err: &error);
780 }
781 }
782
783 return 0;
784}
785
786static void
787gtk_application_impl_dbus_uninhibit (GtkApplicationImpl *impl,
788 guint cookie)
789{
790 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
791
792 if (dbus->sm_proxy)
793 {
794 g_dbus_proxy_call (proxy: dbus->sm_proxy,
795 method_name: "Uninhibit",
796 parameters: g_variant_new (format_string: "(u)", cookie),
797 flags: G_DBUS_CALL_FLAGS_NONE,
798 G_MAXINT,
799 NULL, NULL, NULL);
800 }
801 else if (dbus->inhibit_proxy)
802 {
803 GSList *l;
804
805 for (l = dbus->inhibit_handles; l; l = l->next)
806 {
807 InhibitHandle *handle = l->data;
808 if (handle->cookie == cookie)
809 {
810 g_dbus_connection_call (connection: dbus->session,
811 PORTAL_BUS_NAME,
812 object_path: handle->handle,
813 PORTAL_REQUEST_INTERFACE,
814 method_name: "Close",
815 parameters: g_variant_new (format_string: "()"),
816 G_VARIANT_TYPE_UNIT,
817 flags: G_DBUS_CALL_FLAGS_NONE,
818 G_MAXINT,
819 NULL, NULL, NULL);
820 dbus->inhibit_handles = g_slist_remove (list: dbus->inhibit_handles, data: handle);
821 inhibit_handle_free (data: handle);
822 break;
823 }
824 }
825 }
826}
827
828static gboolean
829gtk_application_impl_dbus_prefers_app_menu (GtkApplicationImpl *impl)
830{
831 static gboolean decided;
832 static gboolean result;
833
834 /* We do not support notifying if/when the result changes, so make
835 * sure that once we give an answer, we will always give the same one.
836 */
837 if (!decided)
838 {
839 GtkSettings *gtk_settings;
840 gboolean show_app_menu;
841 gboolean show_menubar;
842
843 gtk_settings = gtk_settings_get_default ();
844 g_object_get (G_OBJECT (gtk_settings),
845 first_property_name: "gtk-shell-shows-app-menu", &show_app_menu,
846 "gtk-shell-shows-menubar", &show_menubar,
847 NULL);
848
849 /* We prefer traditional menus when we have a shell that doesn't
850 * show the appmenu or we have a shell that shows menubars
851 * (ie: Unity)
852 */
853 result = show_app_menu && !show_menubar;
854 decided = TRUE;
855 }
856
857 return result;
858}
859
860static void
861gtk_application_impl_dbus_init (GtkApplicationImplDBus *dbus)
862{
863}
864
865static void
866gtk_application_impl_dbus_finalize (GObject *object)
867{
868 GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) object;
869
870 if (dbus->session_id)
871 {
872 g_dbus_connection_call (connection: dbus->session,
873 PORTAL_BUS_NAME,
874 object_path: dbus->session_id,
875 PORTAL_SESSION_INTERFACE,
876 method_name: "Close",
877 NULL, NULL, flags: 0, timeout_msec: -1, NULL, NULL, NULL);
878
879 g_free (mem: dbus->session_id);
880 }
881
882 if (dbus->state_changed_handler)
883 g_dbus_connection_signal_unsubscribe (connection: dbus->session,
884 subscription_id: dbus->state_changed_handler);
885
886 g_clear_object (&dbus->inhibit_proxy);
887 g_slist_free_full (list: dbus->inhibit_handles, free_func: inhibit_handle_free);
888 g_free (mem: dbus->app_menu_path);
889 g_free (mem: dbus->menubar_path);
890 g_clear_object (&dbus->sm_proxy);
891 g_clear_object (&dbus->ss_proxy);
892
893 G_OBJECT_CLASS (gtk_application_impl_dbus_parent_class)->finalize (object);
894}
895
896static void
897gtk_application_impl_dbus_class_init (GtkApplicationImplDBusClass *class)
898{
899 GtkApplicationImplClass *impl_class = GTK_APPLICATION_IMPL_CLASS (class);
900 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
901
902 class->get_window_system_id = gtk_application_impl_dbus_real_get_window_system_id;
903
904 impl_class->startup = gtk_application_impl_dbus_startup;
905 impl_class->shutdown = gtk_application_impl_dbus_shutdown;
906 impl_class->window_added = gtk_application_impl_dbus_window_added;
907 impl_class->window_removed = gtk_application_impl_dbus_window_removed;
908 impl_class->active_window_changed = gtk_application_impl_dbus_active_window_changed;
909 impl_class->set_app_menu = gtk_application_impl_dbus_set_app_menu;
910 impl_class->set_menubar = gtk_application_impl_dbus_set_menubar;
911 impl_class->inhibit = gtk_application_impl_dbus_inhibit;
912 impl_class->uninhibit = gtk_application_impl_dbus_uninhibit;
913 impl_class->prefers_app_menu = gtk_application_impl_dbus_prefers_app_menu;
914
915 gobject_class->finalize = gtk_application_impl_dbus_finalize;
916}
917
918char *
919gtk_application_impl_dbus_get_window_path (GtkApplicationImplDBus *dbus,
920 GtkWindow *window)
921{
922 if (dbus->session && GTK_IS_APPLICATION_WINDOW (window))
923 return g_strdup_printf (format: "%s/window/%d",
924 dbus->object_path,
925 gtk_application_window_get_id (GTK_APPLICATION_WINDOW (window)));
926 else
927 return NULL;
928}
929

source code of gtk/gtk/gtkapplication-dbus.c