]> Pileus Git - ~andy/gtk/blob - gtk/gtkapplication.c
Docs: GtkApplication: Hint about GtkActionMap
[~andy/gtk] / gtk / gtkapplication.c
1 /*
2  * Copyright © 2010 Codethink Limited
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the licence, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Author: Ryan Lortie <desrt@desrt.ca>
18  */
19
20 #include "config.h"
21
22 #include "gtkapplication.h"
23
24 #include <stdlib.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include <string.h>
29
30 #include "gtkapplicationprivate.h"
31 #include "gtkclipboard.h"
32 #include "gtkmarshalers.h"
33 #include "gtkmain.h"
34 #include "gtkrecentmanager.h"
35 #include "gtkaccelmapprivate.h"
36 #include "gactionmuxer.h"
37 #include "gtkintl.h"
38
39 #ifdef GDK_WINDOWING_QUARTZ
40 #include "gtkquartz-menu.h"
41 #import <Cocoa/Cocoa.h>
42 #include <Carbon/Carbon.h>
43 #include "gtkmessagedialog.h"
44 #endif
45
46 #include <gdk/gdk.h>
47 #ifdef GDK_WINDOWING_X11
48 #include <gdk/x11/gdkx.h>
49 #endif
50
51 /**
52  * SECTION:gtkapplication
53  * @title: GtkApplication
54  * @short_description: Application class
55  *
56  * #GtkApplication is a class that handles many important aspects
57  * of a GTK+ application in a convenient fashion, without enforcing
58  * a one-size-fits-all application model.
59  *
60  * Currently, GtkApplication handles GTK+ initialization, application
61  * uniqueness, session management, provides some basic scriptability and
62  * desktop shell integration by exporting actions and menus and manages a
63  * list of toplevel windows whose life-cycle is automatically tied to the
64  * life-cycle of your application.
65  *
66  * While GtkApplication works fine with plain #GtkWindows, it is recommended
67  * to use it together with #GtkApplicationWindow.
68  *
69  * When GDK threads are enabled, GtkApplication will acquire the GDK
70  * lock when invoking actions that arrive from other processes.  The GDK
71  * lock is not touched for local action invocations.  In order to have
72  * actions invoked in a predictable context it is therefore recommended
73  * that the GDK lock be held while invoking actions locally with
74  * g_action_group_activate_action().  The same applies to actions
75  * associated with #GtkApplicationWindow and to the 'activate' and
76  * 'open' #GApplication methods.
77  *
78  * To set an application menu for a GtkApplication, use
79  * gtk_application_set_app_menu(). The #GMenuModel that this function
80  * expects is usually constructed using #GtkBuilder, as seen in the
81  * following example. To specify a menubar that will be shown by
82  * #GApplicationWindows, use gtk_application_set_menubar(). Use the base
83  * #GActionMap interface to add actions, to respond to the user
84  * selecting these menu items.
85  *
86  * GTK+ displays these menus as expected, depending on the platform
87  * the application is running on.
88  *
89  * <figure label="Menu integration in OS X">
90  * <graphic fileref="bloatpad-osx.png" format="PNG"/>
91  * </figure>
92  *
93  * <figure label="Menu integration in GNOME">
94  * <graphic fileref="bloatpad-gnome.png" format="PNG"/>
95  * </figure>
96  *
97  * <figure label="Menu integration in Xfce">
98  * <graphic fileref="bloatpad-xfce.png" format="PNG"/>
99  * </figure>
100  *
101  * <example id="gtkapplication"><title>A simple application</title>
102  * <programlisting>
103  * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../examples/bloatpad.c">
104  *  <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
105  * </xi:include>
106  * </programlisting>
107  * </example>
108  *
109  * GtkApplication optionally registers with a session manager
110  * of the users session (if you set the #GtkApplication:register-session
111  * property) and offers various functionality related to the session
112  * life-cycle.
113  *
114  * An application can block various ways to end the session with
115  * the gtk_application_inhibit() function. Typical use cases for
116  * this kind of inhibiting are long-running, uninterruptible operations,
117  * such as burning a CD or performing a disk backup. The session
118  * manager may not honor the inhibitor, but it can be expected to
119  * inform the user about the negative consequences of ending the
120  * session while inhibitors are present.
121  */
122
123 enum {
124   WINDOW_ADDED,
125   WINDOW_REMOVED,
126   LAST_SIGNAL
127 };
128
129 static guint gtk_application_signals[LAST_SIGNAL];
130
131 enum {
132   PROP_ZERO,
133   PROP_REGISTER_SESSION,
134   PROP_APP_MENU,
135   PROP_MENUBAR
136 };
137
138 G_DEFINE_TYPE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
139
140 struct _GtkApplicationPrivate
141 {
142   GList *windows;
143
144   gboolean register_session;
145
146   GMenuModel      *app_menu;
147   GMenuModel      *menubar;
148
149 #ifdef GDK_WINDOWING_X11
150   GDBusConnection *session_bus;
151   const gchar     *application_id;
152   gchar           *object_path;
153
154   gchar           *app_menu_path;
155   guint            app_menu_id;
156
157   gchar           *menubar_path;
158   guint            menubar_id;
159
160   guint next_id;
161
162   GDBusProxy *sm_proxy;
163   GDBusProxy *client_proxy;
164   gchar *app_id;
165   gchar *client_path;
166 #endif
167
168 #ifdef GDK_WINDOWING_QUARTZ
169   GActionMuxer *muxer;
170   GMenu *combined;
171
172   GSList *inhibitors;
173   gint quit_inhibit;
174   guint next_cookie;
175 #endif
176 };
177
178 #ifdef GDK_WINDOWING_X11
179 static void
180 gtk_application_x11_publish_menu (GtkApplication  *application,
181                                   const gchar     *type,
182                                   GMenuModel      *model,
183                                   guint           *id,
184                                   gchar          **path)
185 {
186   gint i;
187
188   /* unexport any existing menu */
189   if (*id)
190     {
191       g_dbus_connection_unexport_menu_model (application->priv->session_bus, *id);
192       g_free (*path);
193       *path = NULL;
194       *id = 0;
195     }
196
197   /* export the new menu, if there is one */
198   if (model != NULL)
199     {
200       /* try getting the preferred name */
201       *path = g_strconcat (application->priv->object_path, "/menus/", type, NULL);
202       *id = g_dbus_connection_export_menu_model (application->priv->session_bus, *path, model, NULL);
203
204       /* keep trying until we get a working name... */
205       for (i = 0; *id == 0; i++)
206         {
207           g_free (*path);
208           *path = g_strdup_printf ("%s/menus/%s%d", application->priv->object_path, type, i);
209           *id = g_dbus_connection_export_menu_model (application->priv->session_bus, *path, model, NULL);
210         }
211     }
212 }
213
214 static void
215 gtk_application_set_app_menu_x11 (GtkApplication *application,
216                                   GMenuModel     *app_menu)
217 {
218   gtk_application_x11_publish_menu (application, "appmenu", app_menu,
219                                     &application->priv->app_menu_id,
220                                     &application->priv->app_menu_path);
221 }
222
223 static void
224 gtk_application_set_menubar_x11 (GtkApplication *application,
225                                  GMenuModel     *menubar)
226 {
227   gtk_application_x11_publish_menu (application, "menubar", menubar,
228                                     &application->priv->menubar_id,
229                                     &application->priv->menubar_path);
230 }
231
232 static void
233 gtk_application_window_added_x11 (GtkApplication *application,
234                                   GtkWindow      *window)
235 {
236   if (application->priv->session_bus == NULL)
237     return;
238
239   if (GTK_IS_APPLICATION_WINDOW (window))
240     {
241       GtkApplicationWindow *app_window = GTK_APPLICATION_WINDOW (window);
242       gboolean success;
243
244       /* GtkApplicationWindow associates with us when it is first created,
245        * so surely it's not realized yet...
246        */
247       g_assert (!gtk_widget_get_realized (GTK_WIDGET (window)));
248
249       do
250         {
251           gchar *window_path;
252           guint window_id;
253
254           window_id = application->priv->next_id++;
255           window_path = g_strdup_printf ("%s/window/%d", application->priv->object_path, window_id);
256           success = gtk_application_window_publish (app_window, application->priv->session_bus, window_path);
257           g_free (window_path);
258         }
259       while (!success);
260     }
261 }
262
263 static void
264 gtk_application_window_removed_x11 (GtkApplication *application,
265                                     GtkWindow      *window)
266 {
267   if (application->priv->session_bus == NULL)
268     return;
269
270   if (GTK_IS_APPLICATION_WINDOW (window))
271     gtk_application_window_unpublish (GTK_APPLICATION_WINDOW (window));
272 }
273
274 static gchar *
275 object_path_from_appid (const gchar *appid)
276 {
277   gchar *appid_path, *iter;
278
279   appid_path = g_strconcat ("/", appid, NULL);
280   for (iter = appid_path; *iter; iter++)
281     {
282       if (*iter == '.')
283         *iter = '/';
284
285       if (*iter == '-')
286         *iter = '_';
287     }
288
289   return appid_path;
290 }
291
292 static void gtk_application_startup_session_dbus (GtkApplication *app);
293
294 static void
295 gtk_application_startup_x11 (GtkApplication *application)
296 {
297   const gchar *application_id;
298
299   application_id = g_application_get_application_id (G_APPLICATION (application));
300   application->priv->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
301   application->priv->object_path = object_path_from_appid (application_id);
302
303   gtk_application_startup_session_dbus (GTK_APPLICATION (application));
304 }
305
306 static void
307 gtk_application_shutdown_x11 (GtkApplication *application)
308 {
309   g_free (application->priv->object_path);
310   application->priv->object_path = NULL;
311   g_clear_object (&application->priv->session_bus);
312
313   g_clear_object (&application->priv->sm_proxy);
314   g_clear_object (&application->priv->client_proxy);
315   g_free (application->priv->app_id);
316   g_free (application->priv->client_path);
317 }
318
319 const gchar *
320 gtk_application_get_dbus_object_path (GtkApplication *application)
321 {
322   return application->priv->object_path;
323 }
324
325 const gchar *
326 gtk_application_get_app_menu_object_path (GtkApplication *application)
327 {
328   return application->priv->app_menu_path;
329 }
330
331 const gchar *
332 gtk_application_get_menubar_object_path (GtkApplication *application)
333 {
334   return application->priv->menubar_path;
335 }
336
337 #endif
338
339 #ifdef GDK_WINDOWING_QUARTZ
340
341 typedef struct {
342   guint cookie;
343   GtkApplicationInhibitFlags flags;
344   char *reason;
345   GtkWindow *window;
346 } GtkApplicationQuartzInhibitor;
347
348 static void
349 gtk_application_quartz_inhibitor_free (GtkApplicationQuartzInhibitor *inhibitor)
350 {
351   g_free (inhibitor->reason);
352   g_clear_object (&inhibitor->window);
353   g_slice_free (GtkApplicationQuartzInhibitor, inhibitor);
354 }
355
356 static void
357 gtk_application_menu_changed_quartz (GObject    *object,
358                                      GParamSpec *pspec,
359                                      gpointer    user_data)
360 {
361   GtkApplication *application = GTK_APPLICATION (object);
362   GMenu *combined;
363
364   combined = g_menu_new ();
365   g_menu_append_submenu (combined, "Application", gtk_application_get_app_menu (application));
366   g_menu_append_section (combined, NULL, gtk_application_get_menubar (application));
367
368   gtk_quartz_set_main_menu (G_MENU_MODEL (combined), G_ACTION_OBSERVABLE (application->priv->muxer));
369 }
370
371 static void gtk_application_startup_session_quartz (GtkApplication *app);
372
373 static void
374 gtk_application_startup_quartz (GtkApplication *application)
375 {
376   [NSApp finishLaunching];
377
378   application->priv->muxer = g_action_muxer_new ();
379   g_action_muxer_insert (application->priv->muxer, "app", G_ACTION_GROUP (application));
380
381   g_signal_connect (application, "notify::app-menu", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
382   g_signal_connect (application, "notify::menubar", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
383   gtk_application_menu_changed_quartz (G_OBJECT (application), NULL, NULL);
384
385   gtk_application_startup_session_quartz (application);
386 }
387
388 static void
389 gtk_application_shutdown_quartz (GtkApplication *application)
390 {
391   g_signal_handlers_disconnect_by_func (application, gtk_application_menu_changed_quartz, NULL);
392
393   g_object_unref (application->priv->muxer);
394   application->priv->muxer = NULL;
395
396   g_slist_free_full (application->priv->inhibitors,
397                      (GDestroyNotify) gtk_application_quartz_inhibitor_free);
398   application->priv->inhibitors = NULL;
399 }
400
401 static void
402 gtk_application_focus_changed (GtkApplication *application,
403                                GtkWindow      *window)
404 {
405   if (G_IS_ACTION_GROUP (window))
406     g_action_muxer_insert (application->priv->muxer, "win", G_ACTION_GROUP (window));
407   else
408     g_action_muxer_remove (application->priv->muxer, "win");
409 }
410 #endif
411
412 static gboolean
413 gtk_application_focus_in_event_cb (GtkWindow      *window,
414                                    GdkEventFocus  *event,
415                                    GtkApplication *application)
416 {
417   GtkApplicationPrivate *priv = application->priv;
418   GList *link;
419
420   /* Keep the window list sorted by most-recently-focused. */
421   link = g_list_find (priv->windows, window);
422   if (link != NULL && link != priv->windows)
423     {
424       priv->windows = g_list_remove_link (priv->windows, link);
425       priv->windows = g_list_concat (link, priv->windows);
426     }
427
428 #ifdef GDK_WINDOWING_QUARTZ
429   gtk_application_focus_changed (application, window);
430 #endif
431
432   return FALSE;
433 }
434
435 static void
436 gtk_application_startup (GApplication *application)
437 {
438   G_APPLICATION_CLASS (gtk_application_parent_class)
439     ->startup (application);
440
441   gtk_init (0, 0);
442
443 #ifdef GDK_WINDOWING_X11
444   gtk_application_startup_x11 (GTK_APPLICATION (application));
445 #endif
446
447 #ifdef GDK_WINDOWING_QUARTZ
448   gtk_application_startup_quartz (GTK_APPLICATION (application));
449 #endif
450 }
451
452 static void
453 gtk_application_shutdown (GApplication *application)
454 {
455 #ifdef GDK_WINDOWING_X11
456   gtk_application_shutdown_x11 (GTK_APPLICATION (application));
457 #endif
458
459 #ifdef GDK_WINDOWING_QUARTZ
460   gtk_application_shutdown_quartz (GTK_APPLICATION (application));
461 #endif
462
463   /* Try storing all clipboard data we have */
464   _gtk_clipboard_store_all ();
465
466   /* Synchronize the recent manager singleton */
467   _gtk_recent_manager_sync ();
468
469   G_APPLICATION_CLASS (gtk_application_parent_class)
470     ->shutdown (application);
471 }
472
473 static void
474 gtk_application_add_platform_data (GApplication    *application,
475                                    GVariantBuilder *builder)
476 {
477   const gchar *startup_id;
478
479   startup_id = getenv ("DESKTOP_STARTUP_ID");
480   
481   if (startup_id && g_utf8_validate (startup_id, -1, NULL))
482     g_variant_builder_add (builder, "{sv}", "desktop-startup-id",
483                            g_variant_new_string (startup_id));
484 }
485
486 static void
487 gtk_application_before_emit (GApplication *application,
488                              GVariant     *platform_data)
489 {
490   GVariantIter iter;
491   const gchar *key;
492   GVariant *value;
493
494   gdk_threads_enter ();
495
496   g_variant_iter_init (&iter, platform_data);
497   while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
498     {
499 #ifdef GDK_WINDOWING_X11
500       if (strcmp (key, "desktop-startup-id") == 0)
501         {
502           GdkDisplay *display;
503           const gchar *id;
504
505           display = gdk_display_get_default ();
506           id = g_variant_get_string (value, NULL);
507           if (GDK_IS_X11_DISPLAY (display))
508             gdk_x11_display_set_startup_notification_id (display, id);
509        }
510 #endif
511     }
512 }
513
514 static void
515 gtk_application_after_emit (GApplication *application,
516                             GVariant     *platform_data)
517 {
518   gdk_notify_startup_complete ();
519
520   gdk_threads_leave ();
521 }
522
523 static void
524 gtk_application_init (GtkApplication *application)
525 {
526   application->priv = G_TYPE_INSTANCE_GET_PRIVATE (application,
527                                                    GTK_TYPE_APPLICATION,
528                                                    GtkApplicationPrivate);
529 }
530
531 static void
532 gtk_application_window_added (GtkApplication *application,
533                               GtkWindow      *window)
534 {
535   GtkApplicationPrivate *priv = application->priv;
536
537   priv->windows = g_list_prepend (priv->windows, window);
538   gtk_window_set_application (window, application);
539   g_application_hold (G_APPLICATION (application));
540
541   g_signal_connect (window, "focus-in-event",
542                     G_CALLBACK (gtk_application_focus_in_event_cb),
543                     application);
544
545 #ifdef GDK_WINDOWING_X11
546   gtk_application_window_added_x11 (application, window);
547 #endif
548 }
549
550 static void
551 gtk_application_window_removed (GtkApplication *application,
552                                 GtkWindow      *window)
553 {
554   GtkApplicationPrivate *priv = application->priv;
555
556 #ifdef GDK_WINDOWING_X11
557   gtk_application_window_removed_x11 (application, window);
558 #endif
559
560   g_signal_handlers_disconnect_by_func (window,
561                                         gtk_application_focus_in_event_cb,
562                                         application);
563
564   g_application_release (G_APPLICATION (application));
565   priv->windows = g_list_remove (priv->windows, window);
566   gtk_window_set_application (window, NULL);
567 }
568
569 static void
570 extract_accel_from_menu_item (GMenuModel     *model,
571                               gint            item,
572                               GtkApplication *app)
573 {
574   GMenuAttributeIter *iter;
575   const gchar *key;
576   GVariant *value;
577   const gchar *accel = NULL;
578   const gchar *action = NULL;
579   GVariant *target = NULL;
580
581   iter = g_menu_model_iterate_item_attributes (model, item);
582   while (g_menu_attribute_iter_get_next (iter, &key, &value))
583     {
584       if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
585         action = g_variant_get_string (value, NULL);
586       else if (g_str_equal (key, "accel") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
587         accel = g_variant_get_string (value, NULL);
588       else if (g_str_equal (key, "target"))
589         target = g_variant_ref (value);
590       g_variant_unref (value);
591     }
592   g_object_unref (iter);
593
594   if (accel && action)
595     gtk_application_add_accelerator (app, accel, action, target);
596
597   if (target)
598     g_variant_unref (target);
599 }
600
601 static void
602 extract_accels_from_menu (GMenuModel     *model,
603                           GtkApplication *app)
604 {
605   gint i;
606   GMenuLinkIter *iter;
607   const gchar *key;
608   GMenuModel *m;
609
610   for (i = 0; i < g_menu_model_get_n_items (model); i++)
611     {
612       extract_accel_from_menu_item (model, i, app);
613
614       iter = g_menu_model_iterate_item_links (model, i);
615       while (g_menu_link_iter_get_next (iter, &key, &m))
616         {
617           extract_accels_from_menu (m, app);
618           g_object_unref (m);
619         }
620       g_object_unref (iter);
621     }
622 }
623
624 static void
625 gtk_application_get_property (GObject    *object,
626                               guint       prop_id,
627                               GValue     *value,
628                               GParamSpec *pspec)
629 {
630   GtkApplication *application = GTK_APPLICATION (object);
631
632   switch (prop_id)
633     {
634     case PROP_REGISTER_SESSION:
635       g_value_set_boolean (value, application->priv->register_session);
636       break;
637
638     case PROP_APP_MENU:
639       g_value_set_object (value, gtk_application_get_app_menu (application));
640       break;
641
642     case PROP_MENUBAR:
643       g_value_set_object (value, gtk_application_get_menubar (application));
644       break;
645
646     default:
647       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
648       break;
649     }
650 }
651
652 static void
653 gtk_application_set_property (GObject      *object,
654                               guint         prop_id,
655                               const GValue *value,
656                               GParamSpec   *pspec)
657 {
658   GtkApplication *application = GTK_APPLICATION (object);
659
660   switch (prop_id)
661     {
662     case PROP_REGISTER_SESSION:
663       application->priv->register_session = g_value_get_boolean (value);
664       break;
665
666     case PROP_APP_MENU:
667       gtk_application_set_app_menu (application, g_value_get_object (value));
668       break;
669
670     case PROP_MENUBAR:
671       gtk_application_set_menubar (application, g_value_get_object (value));
672       break;
673
674     default:
675       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
676       break;
677     }
678 }
679
680 static void
681 gtk_application_finalize (GObject *object)
682 {
683   GtkApplication *application = GTK_APPLICATION (object);
684
685   g_clear_object (&application->priv->app_menu);
686   g_clear_object (&application->priv->menubar);
687
688   G_OBJECT_CLASS (gtk_application_parent_class)
689     ->finalize (object);
690 }
691
692 static void
693 gtk_application_class_init (GtkApplicationClass *class)
694 {
695   GObjectClass *object_class = G_OBJECT_CLASS (class);
696   GApplicationClass *application_class = G_APPLICATION_CLASS (class);
697
698   object_class->get_property = gtk_application_get_property;
699   object_class->set_property = gtk_application_set_property;
700   object_class->finalize = gtk_application_finalize;
701
702   application_class->add_platform_data = gtk_application_add_platform_data;
703   application_class->before_emit = gtk_application_before_emit;
704   application_class->after_emit = gtk_application_after_emit;
705   application_class->startup = gtk_application_startup;
706   application_class->shutdown = gtk_application_shutdown;
707
708   class->window_added = gtk_application_window_added;
709   class->window_removed = gtk_application_window_removed;
710
711   g_type_class_add_private (class, sizeof (GtkApplicationPrivate));
712
713   /**
714    * GtkApplication::window-added:
715    * @application: the #GtkApplication which emitted the signal
716    * @window: the newly-added #GtkWindow
717    *
718    * Emitted when a #GtkWindow is added to @application through
719    * gtk_application_add_window().
720    *
721    * Since: 3.2
722    */
723   gtk_application_signals[WINDOW_ADDED] =
724     g_signal_new ("window-added", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_FIRST,
725                   G_STRUCT_OFFSET (GtkApplicationClass, window_added),
726                   NULL, NULL,
727                   g_cclosure_marshal_VOID__OBJECT,
728                   G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
729
730   /**
731    * GtkApplication::window-removed:
732    * @application: the #GtkApplication which emitted the signal
733    * @window: the #GtkWindow that is being removed
734    *
735    * Emitted when a #GtkWindow is removed from @application,
736    * either as a side-effect of being destroyed or explicitly
737    * through gtk_application_remove_window().
738    *
739    * Since: 3.2
740    */
741   gtk_application_signals[WINDOW_REMOVED] =
742     g_signal_new ("window-removed", GTK_TYPE_APPLICATION, G_SIGNAL_RUN_FIRST,
743                   G_STRUCT_OFFSET (GtkApplicationClass, window_removed),
744                   NULL, NULL,
745                   g_cclosure_marshal_VOID__OBJECT,
746                   G_TYPE_NONE, 1, GTK_TYPE_WINDOW);
747
748   /**
749    * GtkApplication:register-session:
750    *
751    * Set this property to %TRUE to register with the session manager.
752    *
753    * Since: 3.4
754    */
755   g_object_class_install_property (object_class, PROP_REGISTER_SESSION,
756     g_param_spec_boolean ("register-session",
757                           P_("Register session"),
758                           P_("Register with the session manager"),
759                           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
760
761   g_object_class_install_property (object_class, PROP_APP_MENU,
762     g_param_spec_object ("app-menu",
763                          P_("Application menu"),
764                          P_("The GMenuModel for the application menu"),
765                          G_TYPE_MENU_MODEL,
766                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
767
768   g_object_class_install_property (object_class, PROP_MENUBAR,
769     g_param_spec_object ("menubar",
770                          P_("Menubar"),
771                          P_("The GMenuModel for the menubar"),
772                          G_TYPE_MENU_MODEL,
773                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
774 }
775
776 /**
777  * gtk_application_new:
778  * @application_id: the application id
779  * @flags: the application flags
780  *
781  * Creates a new #GtkApplication instance.
782  *
783  * This function calls g_type_init() for you. gtk_init() is called
784  * as soon as the application gets registered as the primary instance.
785  *
786  * Concretely, gtk_init() is called in the default handler for the
787  * startup() signal. Therefore, #GtkApplication subclasses should
788  * chain up in their startup() handler before using any GTK+ API.
789  *
790  * Note that commandline arguments are not passed to gtk_init().
791  * All GTK+ functionality that is available via commandline arguments
792  * can also be achieved by setting suitable environment variables
793  * such as <envar>G_DEBUG</envar>, so this should not be a big
794  * problem. If you absolutely must support GTK+ commandline arguments,
795  * you can explicitly call gtk_init() before creating the application
796  * instance.
797  *
798  * The application id must be valid. See g_application_id_is_valid().
799  *
800  * Returns: a new #GtkApplication instance
801  *
802  * Since: 3.0
803  */
804 GtkApplication *
805 gtk_application_new (const gchar       *application_id,
806                      GApplicationFlags  flags)
807 {
808   g_return_val_if_fail (g_application_id_is_valid (application_id), NULL);
809
810   g_type_init ();
811
812   return g_object_new (GTK_TYPE_APPLICATION,
813                        "application-id", application_id,
814                        "flags", flags,
815                        NULL);
816 }
817
818 /**
819  * gtk_application_add_window:
820  * @application: a #GtkApplication
821  * @window: a #GtkWindow
822  *
823  * Adds a window from @application.
824  *
825  * This call is equivalent to setting the #GtkWindow:application
826  * property of @window to @application.
827  *
828  * Normally, the connection between the application and the window
829  * will remain until the window is destroyed, but you can explicitly
830  * remove it with gtk_application_remove_window().
831  *
832  * GTK+ will keep the application running as long as it has
833  * any windows.
834  *
835  * Since: 3.0
836  **/
837 void
838 gtk_application_add_window (GtkApplication *application,
839                             GtkWindow      *window)
840 {
841   g_return_if_fail (GTK_IS_APPLICATION (application));
842
843   if (!g_list_find (application->priv->windows, window))
844     g_signal_emit (application,
845                    gtk_application_signals[WINDOW_ADDED], 0, window);
846 }
847
848 /**
849  * gtk_application_remove_window:
850  * @application: a #GtkApplication
851  * @window: a #GtkWindow
852  *
853  * Remove a window from @application.
854  *
855  * If @window belongs to @application then this call is equivalent to
856  * setting the #GtkWindow:application property of @window to
857  * %NULL.
858  *
859  * The application may stop running as a result of a call to this
860  * function.
861  *
862  * Since: 3.0
863  **/
864 void
865 gtk_application_remove_window (GtkApplication *application,
866                                GtkWindow      *window)
867 {
868   g_return_if_fail (GTK_IS_APPLICATION (application));
869
870   if (g_list_find (application->priv->windows, window))
871     g_signal_emit (application,
872                    gtk_application_signals[WINDOW_REMOVED], 0, window);
873 }
874
875 /**
876  * gtk_application_get_windows:
877  * @application: a #GtkApplication
878  *
879  * Gets a list of the #GtkWindows associated with @application.
880  *
881  * The list is sorted by most recently focused window, such that the first
882  * element is the currently focused window. (Useful for choosing a parent
883  * for a transient window.)
884  *
885  * The list that is returned should not be modified in any way. It will
886  * only remain valid until the next focus change or window creation or
887  * deletion.
888  *
889  * Returns: (element-type GtkWindow) (transfer none): a #GList of #GtkWindow
890  *
891  * Since: 3.0
892  **/
893 GList *
894 gtk_application_get_windows (GtkApplication *application)
895 {
896   g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
897
898   return application->priv->windows;
899 }
900
901 /**
902  * gtk_application_add_accelerator:
903  * @application: a #GtkApplication
904  * @accelerator: accelerator string
905  * @action_name: the name of the action to activate
906  * @parameter: (allow-none): parameter to pass when activating the action,
907  *   or %NULL if the action does not accept an activation parameter
908  *
909  * Installs an accelerator that will cause the named action
910  * to be activated when the key combination specificed by @accelerator
911  * is pressed.
912  *
913  * @accelerator must be a string that can be parsed by
914  * gtk_accelerator_parse(), e.g. "<Primary>q" or "<Control><Alt>p".
915  *
916  * @action_name must be the name of an action as it would be used
917  * in the app menu, i.e. actions that have been added to the application
918  * are referred to with an "app." prefix, and window-specific actions
919  * with a "win." prefix.
920  *
921  * GtkApplication also extracts accelerators out of 'accel' attributes
922  * in the #GMenuModels passed to gtk_application_set_app_menu() and
923  * gtk_application_set_menubar(), which is usually more convenient
924  * than calling this function for each accelerator.
925  *
926  * Since: 3.4
927  */
928 void
929 gtk_application_add_accelerator (GtkApplication *application,
930                                  const gchar    *accelerator,
931                                  const gchar    *action_name,
932                                  GVariant       *parameter)
933 {
934   gchar *accel_path;
935   guint accel_key;
936   GdkModifierType accel_mods;
937
938   g_return_if_fail (GTK_IS_APPLICATION (application));
939
940   /* Call this here, since gtk_init() is only getting called in startup() */
941   _gtk_accel_map_init ();
942
943   gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
944
945   if (accel_key == 0)
946     {
947       g_warning ("Failed to parse accelerator: '%s'\n", accelerator);
948       return;
949     }
950
951   accel_path = _gtk_accel_path_for_action (action_name, parameter);
952
953   if (gtk_accel_map_lookup_entry (accel_path, NULL))
954     gtk_accel_map_change_entry (accel_path, accel_key, accel_mods, TRUE);
955   else
956     gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
957
958   g_free (accel_path);
959 }
960
961 /**
962  * gtk_application_remove_accelerator:
963  * @application: a #GtkApplication
964  * @action_name: the name of the action to activate
965  * @parameter: (allow-none): parameter to pass when activating the action,
966  *   or %NULL if the action does not accept an activation parameter
967  *
968  * Removes an accelerator that has been previously added
969  * with gtk_application_add_accelerator().
970  *
971  * Since: 3.4
972  */
973 void
974 gtk_application_remove_accelerator (GtkApplication *application,
975                                     const gchar    *action_name,
976                                     GVariant       *parameter)
977 {
978   gchar *accel_path;
979
980   g_return_if_fail (GTK_IS_APPLICATION (application));
981
982   accel_path = _gtk_accel_path_for_action (action_name, parameter);
983
984   if (!gtk_accel_map_lookup_entry (accel_path, NULL))
985     {
986       g_warning ("No accelerator found for '%s'\n", accel_path);
987       g_free (accel_path);
988       return;
989     }
990
991   gtk_accel_map_change_entry (accel_path, 0, 0, FALSE);
992   g_free (accel_path);
993 }
994
995 /**
996  * gtk_application_set_app_menu:
997  * @application: a #GtkApplication
998  * @app_menu: (allow-none): a #GMenuModel, or %NULL
999  *
1000  * Sets or unsets the application menu for @application.
1001  *
1002  * The application menu is a single menu containing items that typically
1003  * impact the application as a whole, rather than acting on a specific
1004  * window or document.  For example, you would expect to see
1005  * "Preferences" or "Quit" in an application menu, but not "Save" or
1006  * "Print".
1007  *
1008  * If supported, the application menu will be rendered by the desktop
1009  * environment.
1010  *
1011  * Use the base #GActionMap interface to add actions, to respond to the user
1012  * selecting these menu items.
1013  *
1014  * Since: 3.4
1015  */
1016 void
1017 gtk_application_set_app_menu (GtkApplication *application,
1018                               GMenuModel     *app_menu)
1019 {
1020   g_return_if_fail (GTK_IS_APPLICATION (application));
1021
1022   if (app_menu != application->priv->app_menu)
1023     {
1024       if (application->priv->app_menu != NULL)
1025         g_object_unref (application->priv->app_menu);
1026
1027       application->priv->app_menu = app_menu;
1028
1029       if (application->priv->app_menu != NULL)
1030         g_object_ref (application->priv->app_menu);
1031
1032       extract_accels_from_menu (app_menu, application);
1033
1034 #ifdef GDK_WINDOWING_X11
1035       gtk_application_set_app_menu_x11 (application, app_menu);
1036 #endif
1037
1038       g_object_notify (G_OBJECT (application), "app-menu");
1039     }
1040 }
1041
1042 /**
1043  * gtk_application_get_app_menu:
1044  * @application: a #GtkApplication
1045  *
1046  * Returns the menu model that has been set with
1047  * gtk_application_set_app_menu().
1048  *
1049  * Returns: (transfer none): the application menu of @application
1050  *
1051  * Since: 3.4
1052  */
1053 GMenuModel *
1054 gtk_application_get_app_menu (GtkApplication *application)
1055 {
1056   g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
1057
1058   return application->priv->app_menu;
1059 }
1060
1061 /**
1062  * gtk_application_set_menubar:
1063  * @application: a #GtkApplication
1064  * @menubar: (allow-none): a #GMenuModel, or %NULL
1065  *
1066  * Sets or unsets the menubar for windows of @application.
1067  *
1068  * This is a menubar in the traditional sense.
1069  *
1070  * Depending on the desktop environment, this may appear at the top of
1071  * each window, or at the top of the screen.  In some environments, if
1072  * both the application menu and the menubar are set, the application
1073  * menu will be presented as if it were the first item of the menubar.
1074  * Other environments treat the two as completely separate -- for
1075  * example, the application menu may be rendered by the desktop shell
1076  * while the menubar (if set) remains in each individual window.
1077  *
1078  * Use the base #GActionMap interface to add actions, to respond to the user
1079  * selecting these menu items.
1080  *
1081  * Since: 3.4
1082  */
1083 void
1084 gtk_application_set_menubar (GtkApplication *application,
1085                              GMenuModel     *menubar)
1086 {
1087   g_return_if_fail (GTK_IS_APPLICATION (application));
1088
1089   if (menubar != application->priv->menubar)
1090     {
1091       if (application->priv->menubar != NULL)
1092         g_object_unref (application->priv->menubar);
1093
1094       application->priv->menubar = menubar;
1095
1096       if (application->priv->menubar != NULL)
1097         g_object_ref (application->priv->menubar);
1098
1099       extract_accels_from_menu (menubar, application);
1100
1101 #ifdef GDK_WINDOWING_X11
1102       gtk_application_set_menubar_x11 (application, menubar);
1103 #endif
1104
1105       g_object_notify (G_OBJECT (application), "menubar");
1106     }
1107 }
1108
1109 /**
1110  * gtk_application_get_menubar:
1111  * @application: a #GtkApplication
1112  *
1113  * Returns the menu model that has been set with
1114  * gtk_application_set_menubar().
1115  *
1116  * Returns: (transfer none): the menubar for windows of @application
1117  *
1118  * Since: 3.4
1119  */
1120 GMenuModel *
1121 gtk_application_get_menubar (GtkApplication *application)
1122 {
1123   g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL);
1124
1125   return application->priv->menubar;
1126 }
1127
1128 #if defined(GDK_WINDOWING_X11)
1129
1130 /* D-Bus Session Management
1131  *
1132  * The protocol and the D-Bus API are described here:
1133  * http://live.gnome.org/SessionManagement/GnomeSession
1134  * http://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html
1135  */
1136
1137 static void
1138 unregister_client (GtkApplication *app)
1139 {
1140   GError *error = NULL;
1141
1142   g_debug ("Unregistering client");
1143
1144   g_dbus_proxy_call_sync (app->priv->sm_proxy,
1145                           "UnregisterClient",
1146                           g_variant_new ("(o)", app->priv->client_path),
1147                           G_DBUS_CALL_FLAGS_NONE,
1148                           G_MAXINT,
1149                           NULL,
1150                           &error);
1151
1152   if (error)
1153     {
1154       g_warning ("Failed to unregister client: %s", error->message);
1155       g_error_free (error);
1156     }
1157
1158   g_clear_object (&app->priv->client_proxy);
1159
1160   g_free (app->priv->client_path);
1161   app->priv->client_path = NULL;
1162 }
1163
1164 static void
1165 gtk_application_quit_response (GtkApplication *application,
1166                                gboolean        will_quit,
1167                                const gchar    *reason)
1168 {
1169   g_debug ("Calling EndSessionResponse %d '%s'", will_quit, reason);
1170
1171   g_dbus_proxy_call (application->priv->client_proxy,
1172                      "EndSessionResponse",
1173                      g_variant_new ("(bs)", will_quit, reason ? reason : ""),
1174                      G_DBUS_CALL_FLAGS_NONE,
1175                      G_MAXINT,
1176                      NULL, NULL, NULL);
1177 }
1178 static void
1179 client_proxy_signal (GDBusProxy     *proxy,
1180                      const gchar    *sender_name,
1181                      const gchar    *signal_name,
1182                      GVariant       *parameters,
1183                      GtkApplication *app)
1184 {
1185   if (strcmp (signal_name, "QueryEndSession") == 0)
1186     {
1187       g_debug ("Received QueryEndSession");
1188       gtk_application_quit_response (app, TRUE, NULL);
1189     }
1190   else if (strcmp (signal_name, "CancelEndSession") == 0)
1191     {
1192       g_debug ("Received CancelEndSession");
1193     }
1194   else if (strcmp (signal_name, "EndSession") == 0)
1195     {
1196       g_debug ("Received EndSession");
1197       gtk_application_quit_response (app, TRUE, NULL);
1198       unregister_client (app);
1199       g_application_quit (G_APPLICATION (app));
1200     }
1201   else if (strcmp (signal_name, "Stop") == 0)
1202     {
1203       g_debug ("Received Stop");
1204       unregister_client (app);
1205       g_application_quit (G_APPLICATION (app));
1206     }
1207 }
1208
1209 static void
1210 gtk_application_startup_session_dbus (GtkApplication *app)
1211 {
1212   static gchar *client_id;
1213   GError *error = NULL;
1214   GVariant *res;
1215
1216   if (app->priv->session_bus == NULL)
1217     return;
1218
1219   if (client_id == NULL)
1220     {
1221       const gchar *desktop_autostart_id;
1222
1223       desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
1224       /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
1225        * use the same client id.
1226        */
1227       g_unsetenv ("DESKTOP_AUTOSTART_ID");
1228       client_id = g_strdup (desktop_autostart_id ? desktop_autostart_id : "");
1229     }
1230
1231   g_debug ("Connecting to session manager");
1232
1233   app->priv->sm_proxy = g_dbus_proxy_new_sync (app->priv->session_bus,
1234                                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
1235                                                G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
1236                                                NULL,
1237                                                "org.gnome.SessionManager",
1238                                                "/org/gnome/SessionManager",
1239                                                "org.gnome.SessionManager",
1240                                                NULL,
1241                                                &error);
1242   if (error)
1243     {
1244       g_warning ("Failed to get a session proxy: %s", error->message);
1245       g_error_free (error);
1246       return;
1247     }
1248
1249   /* FIXME: should we reuse the D-Bus application id here ? */
1250   app->priv->app_id = g_strdup (g_get_prgname ());
1251
1252   if (!app->priv->register_session)
1253     return;
1254
1255   g_debug ("Registering client '%s' '%s'", app->priv->app_id, client_id);
1256
1257   res = g_dbus_proxy_call_sync (app->priv->sm_proxy,
1258                                 "RegisterClient",
1259                                 g_variant_new ("(ss)", app->priv->app_id, client_id),
1260                                 G_DBUS_CALL_FLAGS_NONE,
1261                                 G_MAXINT,
1262                                 NULL,
1263                                 &error);
1264
1265   if (error)
1266     {
1267       g_warning ("Failed to register client: %s", error->message);
1268       g_error_free (error);
1269       g_clear_object (&app->priv->sm_proxy);
1270       return;
1271     }
1272
1273   g_variant_get (res, "(o)", &app->priv->client_path);
1274   g_variant_unref (res);
1275
1276   g_debug ("Registered client at '%s'", app->priv->client_path);
1277
1278   app->priv->client_proxy = g_dbus_proxy_new_sync (app->priv->session_bus, 0,
1279                                                    NULL,
1280                                                    "org.gnome.SessionManager",
1281                                                    app->priv->client_path,
1282                                                    "org.gnome.SessionManager.ClientPrivate",
1283                                                    NULL,
1284                                                    &error);
1285   if (error)
1286     {
1287       g_warning ("Failed to get client proxy: %s", error->message);
1288       g_error_free (error);
1289       g_clear_object (&app->priv->sm_proxy);
1290       g_free (app->priv->client_path);
1291       app->priv->client_path = NULL;
1292       return;
1293     }
1294
1295   g_signal_connect (app->priv->client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), app);
1296 }
1297
1298
1299
1300 /**
1301  * GtkApplicationInhibitFlags:
1302  * @GTK_APPLICATION_INHIBIT_LOGOUT: Inhibit ending the user session
1303  *     by logging out or by shutting down the computer
1304  * @GTK_APPLICATION_INHIBIT_SWITCH: Inhibit user switching
1305  * @GTK_APPLICATION_INHIBIT_SUSPEND: Inhibit suspending the
1306  *     session or computer
1307  * @GTK_APPLICATION_INHIBIT_IDLE: Inhibit the session being
1308  *     marked as idle (and possibly locked)
1309  *
1310  * Types of user actions that may be blocked by gtk_application_inhibit().
1311  *
1312  * Since: 3.4
1313  */
1314
1315 /**
1316  * gtk_application_inhibit:
1317  * @application: the #GApplication
1318  * @window: (allow-none): a #GtkWindow, or %NULL
1319  * @flags: what types of actions should be inhibited
1320  * @reason: (allow-none): a short, human-readable string that explains
1321  *     why these operations are inhibited
1322  *
1323  * Inform the session manager that certain types of actions should be
1324  * inhibited. This is not guaranteed to work on all platforms and for
1325  * all types of actions.
1326  *
1327  * Applications should invoke this method when they begin an operation
1328  * that should not be interrupted, such as creating a CD or DVD. The
1329  * types of actions that may be blocked are specified by the @flags
1330  * parameter. When the application completes the operation it should
1331  * call g_application_uninhibit() to remove the inhibitor. Note that
1332  * an application can have multiple inhibitors, and all of the must
1333  * be individually removed. Inhibitors are also cleared when the
1334  * application exits.
1335  *
1336  * Applications should not expect that they will always be able to block
1337  * the action. In most cases, users will be given the option to force
1338  * the action to take place.
1339  *
1340  * Reasons should be short and to the point.
1341  *
1342  * If @window is given, the session manager may point the user to
1343  * this window to find out more about why the action is inhibited.
1344  *
1345  * Returns: A non-zero cookie that is used to uniquely identify this
1346  *     request. It should be used as an argument to g_application_uninhibit()
1347  *     in order to remove the request. If the platform does not support
1348  *     inhibiting or the request failed for some reason, 0 is returned.
1349  *
1350  * Since: 3.4
1351  */
1352 guint
1353 gtk_application_inhibit (GtkApplication             *application,
1354                          GtkWindow                  *window,
1355                          GtkApplicationInhibitFlags  flags,
1356                          const gchar                *reason)
1357 {
1358   GVariant *res;
1359   GError *error = NULL;
1360   guint cookie;
1361   guint xid;
1362
1363   g_return_val_if_fail (GTK_IS_APPLICATION (application), 0);
1364   g_return_val_if_fail (!g_application_get_is_remote (G_APPLICATION (application)), 0);
1365   g_return_val_if_fail (application->priv->sm_proxy != NULL, 0);
1366
1367   if (window != NULL)
1368     xid = GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (window)));
1369   else
1370     xid = 0;
1371
1372   res = g_dbus_proxy_call_sync (application->priv->sm_proxy,
1373                                 "Inhibit",
1374                                 g_variant_new ("(susu)",
1375                                                application->priv->app_id,
1376                                                xid,
1377                                                reason,
1378                                                flags),
1379                                 G_DBUS_CALL_FLAGS_NONE,
1380                                 G_MAXINT,
1381                                 NULL,
1382                                 &error);
1383  if (error)
1384     {
1385       g_warning ("Calling Inhibit failed: %s", error->message);
1386       g_error_free (error);
1387       return 0;
1388     }
1389
1390   g_variant_get (res, "(u)", &cookie);
1391   g_variant_unref (res);
1392
1393   return cookie;
1394 }
1395
1396 /**
1397  * gtk_application_uninhibit:
1398  * @application: the #GApplication
1399  * @cookie: a cookie that was returned by g_application_inhibit()
1400  *
1401  * Removes an inhibitor that has been established with g_application_inhibit().
1402  * Inhibitors are also cleared when the application exits.
1403  *
1404  * Since: 3.4
1405  */
1406 void
1407 gtk_application_uninhibit (GtkApplication *application,
1408                            guint           cookie)
1409 {
1410   g_return_if_fail (GTK_IS_APPLICATION (application));
1411   g_return_if_fail (!g_application_get_is_remote (G_APPLICATION (application)));
1412   g_return_if_fail (application->priv->sm_proxy != NULL);
1413
1414   g_dbus_proxy_call (application->priv->sm_proxy,
1415                      "Uninhibit",
1416                      g_variant_new ("(u)", cookie),
1417                      G_DBUS_CALL_FLAGS_NONE,
1418                      G_MAXINT,
1419                      NULL, NULL, NULL);
1420 }
1421
1422 /**
1423  * gtk_application_is_inhibited:
1424  * @application: the #GApplication
1425  * @flags: what types of actions should be queried
1426  *
1427  * Determines if any of the actions specified in @flags are
1428  * currently inhibited (possibly by another application).
1429  *
1430  * Returns: %TRUE if any of the actions specified in @flags are inhibited
1431  *
1432  * Since: 3.4
1433  */
1434 gboolean
1435 gtk_application_is_inhibited (GtkApplication             *application,
1436                               GtkApplicationInhibitFlags  flags)
1437 {
1438   GVariant *res;
1439   GError *error = NULL;
1440   gboolean inhibited;
1441
1442   g_return_val_if_fail (GTK_IS_APPLICATION (application), FALSE);
1443   g_return_val_if_fail (!g_application_get_is_remote (G_APPLICATION (application)), FALSE);
1444   g_return_val_if_fail (application->priv->sm_proxy != NULL, FALSE);
1445
1446   res = g_dbus_proxy_call_sync (application->priv->sm_proxy,
1447                                 "IsInhibited",
1448                                 g_variant_new ("(u)", flags),
1449                                 G_DBUS_CALL_FLAGS_NONE,
1450                                 G_MAXINT,
1451                                 NULL,
1452                                 &error);
1453   if (error)
1454     {
1455       g_warning ("Calling IsInhibited failed: %s", error->message);
1456       g_error_free (error);
1457       return FALSE;
1458     }
1459
1460   g_variant_get (res, "(b)", &inhibited);
1461   g_variant_unref (res);
1462
1463   return inhibited;
1464 }
1465
1466 #elif defined(GDK_WINDOWING_QUARTZ)
1467
1468 /* OS X implementation copied from EggSMClient, but simplified since
1469  * it doesn't need to interact with the user.
1470  */
1471
1472 static gboolean
1473 idle_will_quit (gpointer data)
1474 {
1475   GtkApplication *app = data;
1476
1477   if (app->priv->quit_inhibit == 0)
1478     g_application_quit (G_APPLICATION (app));
1479   else
1480     {
1481       GtkApplicationQuartzInhibitor *inhibitor;
1482       GSList *iter;
1483       GtkWidget *dialog;
1484
1485       for (iter = app->priv->inhibitors; iter; iter = iter->next)
1486         {
1487           inhibitor = iter->data;
1488           if (inhibitor->flags & GTK_APPLICATION_INHIBIT_LOGOUT)
1489             break;
1490         }
1491       g_assert (inhibitor != NULL);
1492
1493       dialog = gtk_message_dialog_new (inhibitor->window,
1494                                        GTK_DIALOG_MODAL,
1495                                        GTK_MESSAGE_ERROR,
1496                                        GTK_BUTTONS_OK,
1497                                        _("%s cannot quit at this time:\n\n%s"),
1498                                        g_get_application_name (),
1499                                        inhibitor->reason);
1500       g_signal_connect_swapped (dialog,
1501                                 "response",
1502                                 G_CALLBACK (gtk_widget_destroy),
1503                                 dialog);
1504       gtk_widget_show_all (dialog);
1505     }
1506
1507   return G_SOURCE_REMOVE;
1508 }
1509
1510 static pascal OSErr
1511 quit_requested (const AppleEvent *aevt,
1512                 AppleEvent       *reply,
1513                 long              refcon)
1514 {
1515   GtkApplication *app = GSIZE_TO_POINTER ((gsize)refcon);
1516
1517   /* Don't emit the "quit" signal immediately, since we're
1518    * called from a weird point in the guts of gdkeventloop-quartz.c
1519    */
1520   g_idle_add_full (G_PRIORITY_DEFAULT, idle_will_quit, app, NULL);
1521
1522   return app->priv->quit_inhibit == 0 ? noErr : userCanceledErr;
1523 }
1524
1525 static void
1526 gtk_application_startup_session_quartz (GtkApplication *app)
1527 {
1528   if (app->priv->register_session)
1529     AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
1530                            NewAEEventHandlerUPP (quit_requested),
1531                            (long)GPOINTER_TO_SIZE (app), false);
1532 }
1533
1534 guint
1535 gtk_application_inhibit (GtkApplication             *application,
1536                          GtkWindow                  *window,
1537                          GtkApplicationInhibitFlags  flags,
1538                          const gchar                *reason)
1539 {
1540   GtkApplicationQuartzInhibitor *inhibitor;
1541
1542   g_return_val_if_fail (GTK_IS_APPLICATION (application), 0);
1543   g_return_val_if_fail (flags != 0, 0);
1544
1545   inhibitor = g_slice_new (GtkApplicationQuartzInhibitor);
1546   inhibitor->cookie = ++application->priv->next_cookie;
1547   inhibitor->flags = flags;
1548   inhibitor->reason = g_strdup (reason);
1549   inhibitor->window = window ? g_object_ref (window) : NULL;
1550
1551   application->priv->inhibitors = g_slist_prepend (application->priv->inhibitors, inhibitor);
1552
1553   if (flags & GTK_APPLICATION_INHIBIT_LOGOUT)
1554     application->priv->quit_inhibit++;
1555
1556   return inhibitor->cookie;
1557 }
1558
1559 void
1560 gtk_application_uninhibit (GtkApplication *application,
1561                            guint           cookie)
1562 {
1563   GSList *iter;
1564
1565   for (iter = application->priv->inhibitors; iter; iter = iter->next)
1566     {
1567       GtkApplicationQuartzInhibitor *inhibitor = iter->data;
1568
1569       if (inhibitor->cookie == cookie)
1570         {
1571           if (inhibitor->flags & GTK_APPLICATION_INHIBIT_LOGOUT)
1572             application->priv->quit_inhibit--;
1573           gtk_application_quartz_inhibitor_free (inhibitor);
1574           application->priv->inhibitors = g_slist_delete_link (application->priv->inhibitors, iter);
1575           return;
1576         }
1577     }
1578
1579   g_warning ("Invalid inhibitor cookie");
1580 }
1581
1582 gboolean
1583 gtk_application_is_inhibited (GtkApplication             *application,
1584                               GtkApplicationInhibitFlags  flags)
1585 {
1586   if (flags & GTK_APPLICATION_INHIBIT_LOGOUT)
1587     return application->priv->quit_inhibit > 0;
1588
1589   return FALSE;
1590 }
1591
1592 #else
1593
1594 /* Trivial implementation.
1595  *
1596  * For the inhibit API on Windows, see
1597  * http://msdn.microsoft.com/en-us/library/ms700677%28VS.85%29.aspx
1598  */
1599
1600 guint
1601 gtk_application_inhibit (GtkApplication             *application,
1602                          GtkWindow                  *window,
1603                          GtkApplicationInhibitFlags  flags,
1604                          const gchar                *reason)
1605 {
1606   return 0;
1607 }
1608
1609 void
1610 gtk_application_uninhibit (GtkApplication *application,
1611                            guint           cookie)
1612 {
1613 }
1614
1615 gboolean
1616 gtk_application_is_inhibited (GtkApplication             *application,
1617                               GtkApplicationInhibitFlags  flags)
1618 {
1619   return FALSE;
1620 }
1621
1622 #endif