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