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 | |
32 | G_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 | |
49 | static void |
50 | unregister_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 | |
76 | static void |
77 | send_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 | |
91 | static void |
92 | client_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 | |
125 | static GDBusProxy* |
126 | gtk_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 |
166 | G_DEFINE_CONSTRUCTOR(stash_desktop_autostart_id) |
167 | #endif |
168 | |
169 | static char *client_id = NULL; |
170 | |
171 | static void |
172 | stash_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 | |
185 | static void |
186 | screensaver_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 | |
201 | enum { |
202 | UNKNOWN = 0, |
203 | RUNNING = 1, |
204 | QUERY_END = 2, |
205 | ENDING = 3 |
206 | }; |
207 | |
208 | static void |
209 | screensaver_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 | |
257 | static void |
258 | ss_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 | |
281 | static void |
282 | create_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 | |
301 | static void |
302 | gtk_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 | |
548 | static void |
549 | gtk_application_impl_dbus_shutdown (GtkApplicationImpl *impl) |
550 | { |
551 | } |
552 | |
553 | GQuark gtk_application_impl_dbus_export_id_quark (void); |
554 | |
555 | G_DEFINE_QUARK (GtkApplicationImplDBus export id, gtk_application_impl_dbus_export_id) |
556 | |
557 | static void |
558 | gtk_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 | |
579 | static void |
580 | gtk_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 | |
594 | static void |
595 | gtk_application_impl_dbus_active_window_changed (GtkApplicationImpl *impl, |
596 | GtkWindow *window) |
597 | { |
598 | } |
599 | |
600 | static void |
601 | (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 | |
638 | static void |
639 | (GtkApplicationImpl *impl, |
640 | GMenuModel *) |
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 | |
647 | static void |
648 | (GtkApplicationImpl *impl, |
649 | GMenuModel *) |
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 | |
656 | static GVariant * |
657 | gtk_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 */ |
664 | static GVariant * |
665 | gtk_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 | |
671 | static int next_cookie; |
672 | |
673 | typedef struct { |
674 | char *handle; |
675 | int cookie; |
676 | } InhibitHandle; |
677 | |
678 | static void |
679 | inhibit_handle_free (gpointer data) |
680 | { |
681 | InhibitHandle *handle = data; |
682 | |
683 | g_free (mem: handle->handle); |
684 | g_free (mem: handle); |
685 | } |
686 | |
687 | static guint |
688 | gtk_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 | |
786 | static void |
787 | gtk_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 | |
828 | static gboolean |
829 | (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 ; |
841 | gboolean ; |
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 | |
860 | static void |
861 | gtk_application_impl_dbus_init (GtkApplicationImplDBus *dbus) |
862 | { |
863 | } |
864 | |
865 | static void |
866 | gtk_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 | |
896 | static void |
897 | gtk_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 | |
918 | char * |
919 | gtk_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 | |