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