[gtk+/wip/quartzwork: 2/11] Refactor GtkApplication



commit 17af5b72dfc2ffc3f60f15497e25da739833e58d
Author: Ryan Lortie <desrt desrt ca>
Date:   Mon Dec 16 09:32:13 2013 -0500

    Refactor GtkApplication
    
    gtkapplication.c has turned into a bit of an #ifdef mess over time, and
    many of the current checks are incorrect.  As an example, if you build
    Gtk for wayland, and exclude the X11 backend, much of the functionality
    required by wayland (such as exporting menu models) will be disabled.
    
    Solve that by introducing a backend mechanism to GtkApplication (named
    GtkApplicationImpl) similar to the one in GApplication.  Add backends
    for Wayland, X11 and Quartz, with X11 and Wayland sharing a common
    'DBus' superclass.
    
                                 GtkApplicationImpl
                                          |
                           /--------------+-------------------\
                           |                                  |
                GtkApplicationImplDBus              GtkApplicationImplQuartz
                           |
               /-----------+-----------------\
               |                             |
      GtkApplicationImplX11      GtkApplicationImplWayland
    
    GtkApplicationImpl itself is essentially a bunch of vfuncs that serve as
    hooks for various things that the platform-specific backends may be
    interested in doing (startup, shutdown, managing windows, inhibit, etc.)
    
    With this change, all platform specific code has been removed from
    gtkapplication.c and gtkapplicationwindow.c (both of which are now free
    of #ifdefs, except for a UNIX-specific use of GDesktopAppInfo in
    gtkapplicationwindow.c).
    
    Additionally, because of the movement of the property-setting code out
    of GtkApplicationWindow, the _GTK_APPLICATION_ID properties (and
    friends) will be set on non-GtkApplicationWindows, such as dialogs.

 gtk/Makefile.am              |   24 ++
 gtk/gtkapplication-dbus.c    |  459 +++++++++++++++++++++++++
 gtk/gtkapplication-quartz.c  |  262 +++++++++++++++
 gtk/gtkapplication-wayland.c |   70 ++++
 gtk/gtkapplication-x11.c     |   89 +++++
 gtk/gtkapplication.c         |  757 ++++--------------------------------------
 gtk/gtkapplicationimpl.c     |  185 ++++++++++
 gtk/gtkapplicationprivate.h  |  178 +++++++++-
 gtk/gtkapplicationwindow.c   |  114 +------
 gtk/gtkwindow.c              |    6 +
 10 files changed, 1336 insertions(+), 808 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index fdbfac5..31c700e 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -628,6 +628,7 @@ gtk_base_c_sources =                \
        gtkappchoosermodule.c   \
        gtkappchooseronline.c   \
        gtkapplication.c        \
+       gtkapplicationimpl.c    \
        gtkapplicationwindow.c  \
        gtkarrow.c              \
        gtkaspectframe.c        \
@@ -962,7 +963,15 @@ gtk_use_x11_c_sources =    \
        gtksocket.c             \
        gtkxembed.c             \
        gtktrayicon-x11.c       \
+       gtkapplication-x11.c    \
        gtkmountoperation-x11.c
+
+gtk_use_wayland_c_sources = \
+       gtkapplication-wayland.c
+
+gtk_use_wayland_or_x11_c_sources = \
+       gtkapplication-dbus.c
+
 gtk_use_win32_c_sources =      \
        gtkwin32embed.c         \
        gtkwin32embedwidget.c   \
@@ -971,6 +980,7 @@ gtk_use_quartz_c_sources =  \
        gtksearchenginequartz.c \
        gtkmountoperation-stub.c \
        gtkmodelmenu-quartz.c \
+       gtkapplication-quartz.c \
        gtkquartz.c
 gtk_use_stub_c_sources =       \
        gtkmountoperation-stub.c
@@ -984,6 +994,20 @@ gtk_c_sources += $(gtk_use_x11_c_sources)
 gtk_private_h_sources += $(gtk_use_x11_private_h_sources)
 endif
 
+if USE_WAYLAND
+gtk_c_sources += $(gtk_use_wayland_c_sources)
+endif
+
+# pretty tricky way to write USE_WAYLAND || USE_X11...
+if USE_WAYLAND
+gtk_c_sources += $(gtk_use_wayland_or_x11_c_sources)
+else
+if USE_X11
+gtk_c_sources += $(gtk_use_wayland_or_x11_c_sources)
+else
+endif
+endif
+
 gtk_use_win32_private_h_sources =      \
        gtkwin32embed.h         \
        gtkwin32embedwidget.h
diff --git a/gtk/gtkapplication-dbus.c b/gtk/gtkapplication-dbus.c
new file mode 100644
index 0000000..667d2ca
--- /dev/null
+++ b/gtk/gtkapplication-dbus.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ * Copyright © 2012 Red Hat, Inc.
+ * Copyright © 2013 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ *         Matthias Clasen <mclasen redhat com>
+ */
+
+#include "config.h"
+
+#include "gtkapplicationprivate.h"
+
+G_DEFINE_TYPE (GtkApplicationImplDBus, gtk_application_impl_dbus, GTK_TYPE_APPLICATION_IMPL)
+
+
+static void
+unregister_client (GtkApplicationImplDBus *dbus)
+{
+  GError *error = NULL;
+
+  g_debug ("Unregistering client");
+
+  g_dbus_proxy_call_sync (dbus->sm_proxy,
+                          "UnregisterClient",
+                          g_variant_new ("(o)", dbus->client_path),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          G_MAXINT,
+                          NULL,
+                          &error);
+
+  if (error)
+    {
+      g_warning ("Failed to unregister client: %s", error->message);
+      g_error_free (error);
+    }
+
+  g_clear_object (&dbus->client_proxy);
+
+  g_free (dbus->client_path);
+  dbus->client_path = NULL;
+}
+
+static void
+send_quit_response (GtkApplicationImplDBus *dbus,
+                    gboolean                will_quit,
+                    const gchar            *reason)
+{
+  g_debug ("Calling EndSessionResponse %d '%s'", will_quit, reason);
+
+  g_dbus_proxy_call (dbus->client_proxy,
+                     "EndSessionResponse",
+                     g_variant_new ("(bs)", will_quit, reason ? reason : ""),
+                     G_DBUS_CALL_FLAGS_NONE,
+                     G_MAXINT,
+                     NULL, NULL, NULL);
+}
+
+static void
+client_proxy_signal (GDBusProxy  *proxy,
+                     const gchar *sender_name,
+                     const gchar *signal_name,
+                     GVariant    *parameters,
+                     gpointer     user_data)
+{
+  GtkApplicationImplDBus *dbus = user_data;
+
+  if (g_str_equal (signal_name, "QueryEndSession"))
+    {
+      g_debug ("Received QueryEndSession");
+      send_quit_response (dbus, TRUE, NULL);
+    }
+  else if (g_str_equal (signal_name, "CancelEndSession"))
+    {
+      g_debug ("Received CancelEndSession");
+    }
+  else if (g_str_equal (signal_name, "EndSession"))
+    {
+      g_debug ("Received EndSession");
+      send_quit_response (dbus, TRUE, NULL);
+      unregister_client (dbus);
+      g_application_quit (G_APPLICATION (dbus->impl.application));
+    }
+  else if (g_str_equal (signal_name, "Stop"))
+    {
+      g_debug ("Received Stop");
+      unregister_client (dbus);
+      g_application_quit (G_APPLICATION (dbus->impl.application));
+    }
+}
+
+static void
+gtk_application_impl_dbus_startup (GtkApplicationImpl *impl,
+                                   gboolean            register_session)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+  static gchar *client_id;
+  GError *error = NULL;
+  GVariant *res;
+
+  dbus->session = g_application_get_dbus_connection (G_APPLICATION (impl->application));
+
+  if (!dbus->session)
+    return;
+
+  dbus->application_id = g_application_get_application_id (G_APPLICATION (impl->application));
+  dbus->object_path = g_application_get_dbus_object_path (G_APPLICATION (impl->application));
+  dbus->unique_name = g_dbus_connection_get_unique_name (dbus->session);
+
+  if (client_id == NULL)
+    {
+      const gchar *desktop_autostart_id;
+
+      desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
+      /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
+       * use the same client id.
+       */
+      g_unsetenv ("DESKTOP_AUTOSTART_ID");
+      client_id = g_strdup (desktop_autostart_id ? desktop_autostart_id : "");
+    }
+
+  g_debug ("Connecting to session manager");
+
+  dbus->sm_proxy = g_dbus_proxy_new_sync (dbus->session,
+                                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                          G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+                                          NULL,
+                                          "org.gnome.SessionManager",
+                                          "/org/gnome/SessionManager",
+                                          "org.gnome.SessionManager",
+                                          NULL,
+                                          &error);
+
+  if (error)
+    {
+      g_warning ("Failed to get a session proxy: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  /* FIXME: should we reuse the D-Bus application id here ? */
+  dbus->app_id = g_strdup (g_get_prgname ());
+
+  if (!register_session)
+    return;
+
+  g_debug ("Registering client '%s' '%s'", dbus->app_id, client_id);
+
+  res = g_dbus_proxy_call_sync (dbus->sm_proxy,
+                                "RegisterClient",
+                                g_variant_new ("(ss)", dbus->app_id, client_id),
+                                G_DBUS_CALL_FLAGS_NONE,
+                                G_MAXINT,
+                                NULL,
+                                &error);
+
+  if (error)
+    {
+      g_warning ("Failed to register client: %s", error->message);
+      g_error_free (error);
+      g_clear_object (&dbus->sm_proxy);
+      return;
+    }
+
+  g_variant_get (res, "(o)", &dbus->client_path);
+  g_variant_unref (res);
+
+  g_debug ("Registered client at '%s'", dbus->client_path);
+
+  dbus->client_proxy = g_dbus_proxy_new_sync (dbus->session, 0,
+                                              NULL,
+                                              "org.gnome.SessionManager",
+                                              dbus->client_path,
+                                              "org.gnome.SessionManager.ClientPrivate",
+                                              NULL,
+                                              &error);
+  if (error)
+    {
+      g_warning ("Failed to get client proxy: %s", error->message);
+      g_error_free (error);
+      g_clear_object (&dbus->sm_proxy);
+      g_free (dbus->client_path);
+      dbus->client_path = NULL;
+      return;
+    }
+
+  g_signal_connect (dbus->client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), dbus);
+}
+
+static void
+gtk_application_impl_dbus_shutdown (GtkApplicationImpl *impl)
+{
+}
+
+G_DEFINE_QUARK (GtkApplicationImplDBus export id, gtk_application_impl_dbus_export_id)
+
+static void
+gtk_application_impl_dbus_window_added (GtkApplicationImpl *impl,
+                                        GtkWindow          *window)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+  GActionGroup *actions;
+  gchar *path;
+  guint id;
+
+  if (!dbus->session || !GTK_IS_APPLICATION_WINDOW (window))
+    return;
+
+  /* Export the action group of this window, based on its id */
+  actions = gtk_application_window_get_action_group (GTK_APPLICATION_WINDOW (window));
+
+  path = gtk_application_impl_dbus_get_window_path (dbus, window);
+  id = g_dbus_connection_export_action_group (dbus->session, path, actions, NULL);
+  g_free (path);
+
+  g_object_set_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark (), GUINT_TO_POINTER 
(id));
+}
+
+static void
+gtk_application_impl_dbus_window_removed (GtkApplicationImpl *impl,
+                                          GtkWindow          *window)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+  guint id;
+
+  id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark 
()));
+  if (id)
+    {
+      g_dbus_connection_unexport_action_group (dbus->session, id);
+      g_object_set_qdata (G_OBJECT (window), gtk_application_impl_dbus_export_id_quark (), NULL);
+    }
+}
+
+static void
+gtk_application_impl_dbus_active_window_changed (GtkApplicationImpl *impl,
+                                                 GtkWindow          *window)
+{
+}
+
+static void
+gtk_application_impl_dbus_publish_menu (GtkApplicationImplDBus  *dbus,
+                                        const gchar             *type,
+                                        GMenuModel              *model,
+                                        guint                   *id,
+                                        gchar                  **path)
+{
+  gint i;
+
+  if (dbus->session == NULL)
+    return;
+
+  /* unexport any existing menu */
+  if (*id)
+    {
+      g_dbus_connection_unexport_menu_model (dbus->session, *id);
+      g_free (*path);
+      *path = NULL;
+      *id = 0;
+    }
+
+  /* export the new menu, if there is one */
+  if (model != NULL)
+    {
+      /* try getting the preferred name */
+      *path = g_strconcat (dbus->object_path, "/menus/", type, NULL);
+      *id = g_dbus_connection_export_menu_model (dbus->session, *path, model, NULL);
+
+      /* keep trying until we get a working name... */
+      for (i = 0; *id == 0; i++)
+        {
+          g_free (*path);
+          *path = g_strdup_printf ("%s/menus/%s%d", dbus->object_path, type, i);
+          *id = g_dbus_connection_export_menu_model (dbus->session, *path, model, NULL);
+        }
+    }
+}
+
+static void
+gtk_application_impl_dbus_set_app_menu (GtkApplicationImpl *impl,
+                                        GMenuModel         *app_menu)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+
+  gtk_application_impl_dbus_publish_menu (dbus, "appmenu", app_menu, &dbus->app_menu_id, 
&dbus->app_menu_path);
+}
+
+static void
+gtk_application_impl_dbus_set_menubar (GtkApplicationImpl *impl,
+                                       GMenuModel         *menubar)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+
+  gtk_application_impl_dbus_publish_menu (dbus, "menubar", menubar, &dbus->menubar_id, &dbus->menubar_path);
+}
+
+static GVariant *
+gtk_application_impl_dbus_real_get_window_system_id (GtkApplicationImplDBus *dbus,
+                                                     GtkWindow              *window)
+{
+  return g_variant_new_uint32 (0);
+}
+
+/* returns floating */
+static GVariant *
+gtk_application_impl_dbus_get_window_system_id (GtkApplicationImplDBus *dbus,
+                                                GtkWindow              *window)
+{
+  return GTK_APPLICATION_IMPL_DBUS_GET_CLASS (dbus)->get_window_system_id (dbus, window);
+}
+
+static guint
+gtk_application_impl_dbus_inhibit (GtkApplicationImpl         *impl,
+                                   GtkWindow                  *window,
+                                   GtkApplicationInhibitFlags  flags,
+                                   const gchar                *reason)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+  GVariant *res;
+  GError *error = NULL;
+  guint cookie;
+
+  if (dbus->sm_proxy == NULL)
+    return 0;
+
+  res = g_dbus_proxy_call_sync (dbus->sm_proxy,
+                                "Inhibit",
+                                g_variant_new ("(s usu)",
+                                               g_application_get_application_id (G_APPLICATION 
(impl->application)),
+                                               gtk_application_impl_dbus_get_window_system_id (dbus, window),
+                                               reason,
+                                               flags),
+                                G_DBUS_CALL_FLAGS_NONE,
+                                G_MAXINT,
+                                NULL,
+                                &error);
+
+ if (error)
+    {
+      g_warning ("Calling Inhibit failed: %s", error->message);
+      g_error_free (error);
+      return 0;
+    }
+
+  g_variant_get (res, "(u)", &cookie);
+  g_variant_unref (res);
+
+  return cookie;
+
+}
+
+static void
+gtk_application_impl_dbus_uninhibit (GtkApplicationImpl *impl,
+                                     guint               cookie)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+
+  /* Application could only obtain a cookie through a session
+   * manager proxy, so it's valid to assert its presence here. */
+  g_return_if_fail (dbus->sm_proxy != NULL);
+
+  g_dbus_proxy_call (dbus->sm_proxy,
+                     "Uninhibit",
+                     g_variant_new ("(u)", cookie),
+                     G_DBUS_CALL_FLAGS_NONE,
+                     G_MAXINT,
+                     NULL, NULL, NULL);
+}
+
+static gboolean
+gtk_application_impl_dbus_is_inhibited (GtkApplicationImpl         *impl,
+                                        GtkApplicationInhibitFlags  flags)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+  GVariant *res;
+  GError *error = NULL;
+  gboolean inhibited;
+
+  if (dbus->sm_proxy == NULL)
+    return FALSE;
+
+  res = g_dbus_proxy_call_sync (dbus->sm_proxy,
+                                "IsInhibited",
+                                g_variant_new ("(u)", flags),
+                                G_DBUS_CALL_FLAGS_NONE,
+                                G_MAXINT,
+                                NULL,
+                                &error);
+  if (error)
+    {
+      g_warning ("Calling IsInhibited failed: %s", error->message);
+      g_error_free (error);
+      return FALSE;
+    }
+
+  g_variant_get (res, "(b)", &inhibited);
+  g_variant_unref (res);
+
+  return inhibited;
+}
+
+static void
+gtk_application_impl_dbus_init (GtkApplicationImplDBus *dbus)
+{
+}
+
+static void
+gtk_application_impl_dbus_finalize (GObject *object)
+{
+  //GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) object;
+
+  G_OBJECT_CLASS (gtk_application_impl_dbus_parent_class)->finalize (object);
+}
+
+static void
+gtk_application_impl_dbus_class_init (GtkApplicationImplDBusClass *class)
+{
+  GtkApplicationImplClass *impl_class = GTK_APPLICATION_IMPL_CLASS (class);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  class->get_window_system_id = gtk_application_impl_dbus_real_get_window_system_id;
+
+  impl_class->startup = gtk_application_impl_dbus_startup;
+  impl_class->shutdown = gtk_application_impl_dbus_shutdown;
+  impl_class->window_added = gtk_application_impl_dbus_window_added;
+  impl_class->window_removed = gtk_application_impl_dbus_window_removed;
+  impl_class->active_window_changed = gtk_application_impl_dbus_active_window_changed;
+  impl_class->set_app_menu = gtk_application_impl_dbus_set_app_menu;
+  impl_class->set_menubar = gtk_application_impl_dbus_set_menubar;
+  impl_class->inhibit = gtk_application_impl_dbus_inhibit;
+  impl_class->uninhibit = gtk_application_impl_dbus_uninhibit;
+  impl_class->is_inhibited = gtk_application_impl_dbus_is_inhibited;
+
+  gobject_class->finalize = gtk_application_impl_dbus_finalize;
+}
+
+gchar *
+gtk_application_impl_dbus_get_window_path (GtkApplicationImplDBus *dbus,
+                                           GtkWindow *window)
+{
+  if (dbus->session && GTK_IS_APPLICATION_WINDOW (window))
+    return g_strdup_printf ("%s/window/%d",
+                            dbus->object_path,
+                            gtk_application_window_get_id (GTK_APPLICATION_WINDOW (window)));
+  else
+    return NULL;
+}
diff --git a/gtk/gtkapplication-quartz.c b/gtk/gtkapplication-quartz.c
new file mode 100644
index 0000000..c141209
--- /dev/null
+++ b/gtk/gtkapplication-quartz.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ * Copyright © 2013 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gtkapplicationprivate.h"
+#include "gtkmodelmenu-quartz.h"
+#include "gtkmessagedialog.h"
+#include <glib/gi18n-lib.h>
+#import <Cocoa/Cocoa.h>
+
+typedef struct
+{
+  guint cookie;
+  GtkApplicationInhibitFlags flags;
+  char *reason;
+  GtkWindow *window;
+} GtkApplicationQuartzInhibitor;
+
+static void
+gtk_application_quartz_inhibitor_free (GtkApplicationQuartzInhibitor *inhibitor)
+{
+  g_free (inhibitor->reason);
+  g_clear_object (&inhibitor->window);
+  g_slice_free (GtkApplicationQuartzInhibitor, inhibitor);
+}
+
+typedef GtkApplicationImplClass GtkApplicationImplQuartzClass;
+
+typedef struct
+{
+  GtkApplicationImpl impl;
+
+  GSList *inhibitors;
+  gint quit_inhibit;
+  guint next_cookie;
+} GtkApplicationImplQuartz;
+
+G_DEFINE_TYPE (GtkApplicationImplQuartz, gtk_application_impl_quartz, GTK_TYPE_APPLICATION_IMPL)
+
+/* OS X implementation copied from EggSMClient, but simplified since
+ * it doesn't need to interact with the user.
+ */
+
+static gboolean
+idle_will_quit (gpointer user_data)
+{
+  GtkApplicationImplQuartz *quartz = user_data;
+
+  if (quartz->quit_inhibit == 0)
+    g_application_quit (G_APPLICATION (quartz->impl.application));
+  else
+    {
+      GtkApplicationQuartzInhibitor *inhibitor;
+      GSList *iter;
+      GtkWidget *dialog;
+
+      for (iter = quartz->inhibitors; iter; iter = iter->next)
+       {
+         inhibitor = iter->data;
+         if (inhibitor->flags & GTK_APPLICATION_INHIBIT_LOGOUT)
+           break;
+        }
+      g_assert (inhibitor != NULL);
+
+      dialog = gtk_message_dialog_new (inhibitor->window,
+                                      GTK_DIALOG_MODAL,
+                                      GTK_MESSAGE_ERROR,
+                                      GTK_BUTTONS_OK,
+                                      _("%s cannot quit at this time:\n\n%s"),
+                                      g_get_application_name (),
+                                      inhibitor->reason);
+      g_signal_connect_swapped (dialog,
+                                "response",
+                                G_CALLBACK (gtk_widget_destroy),
+                                dialog);
+      gtk_widget_show_all (dialog);
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
+static pascal OSErr
+quit_requested (const AppleEvent *aevt,
+                AppleEvent       *reply,
+                long              refcon)
+{
+  GtkApplicationImplQuartz *quartz = GSIZE_TO_POINTER ((gsize)refcon);
+
+  /* Don't emit the "quit" signal immediately, since we're
+   * called from a weird point in the guts of gdkeventloop-quartz.c
+   */
+  g_idle_add_full (G_PRIORITY_DEFAULT, idle_will_quit, quartz, NULL);
+
+  return quartz->quit_inhibit == 0 ? noErr : userCanceledErr;
+}
+
+static void
+gtk_application_impl_quartz_menu_changed (GtkApplicationImplQuartz *quartz)
+{
+  GMenu *combined;
+
+  combined = g_menu_new ();
+  g_menu_append_submenu (combined, "Application", gtk_application_get_app_menu (quartz->impl.application));
+  g_menu_append_section (combined, NULL, gtk_application_get_menubar (quartz->impl.application));
+
+  gtk_quartz_set_main_menu (G_MENU_MODEL (combined), quartz->impl.application);
+
+  g_object_unref (combined);
+}
+
+static void
+gtk_application_impl_quartz_startup (GtkApplicationImpl *impl,
+                                     gboolean            register_session)
+{
+  GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
+
+  if (register_session)
+    AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
+                           NewAEEventHandlerUPP (quit_requested),
+                           (long)GPOINTER_TO_SIZE (quartz), false);
+
+  gtk_application_impl_quartz_menu_changed (quartz);
+
+  [NSApp finishLaunching];
+}
+
+static void
+gtk_application_impl_quartz_shutdown (GtkApplicationImpl *impl)
+{
+  GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
+
+  gtk_quartz_clear_main_menu ();
+
+  g_slist_free_full (quartz->inhibitors, (GDestroyNotify) gtk_application_quartz_inhibitor_free);
+  quartz->inhibitors = NULL;
+}
+
+static void
+gtk_application_impl_quartz_set_app_menu (GtkApplicationImpl *impl,
+                                          GMenuModel         *app_menu)
+{
+  GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
+
+  gtk_application_impl_quartz_menu_changed (quartz);
+}
+
+static void
+gtk_application_impl_quartz_set_menubar (GtkApplicationImpl *impl,
+                                         GMenuModel         *menubar)
+{
+  GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
+
+  gtk_application_impl_quartz_menu_changed (quartz);
+}
+
+static guint
+gtk_application_impl_quartz_inhibit (GtkApplicationImpl         *impl,
+                                     GtkWindow                  *window,
+                                     GtkApplicationInhibitFlags  flags,
+                                     const gchar                *reason)
+{
+  GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
+  GtkApplicationQuartzInhibitor *inhibitor;
+
+  inhibitor = g_slice_new (GtkApplicationQuartzInhibitor);
+  inhibitor->cookie = ++quartz->next_cookie;
+  inhibitor->flags = flags;
+  inhibitor->reason = g_strdup (reason);
+  inhibitor->window = window ? g_object_ref (window) : NULL;
+
+  quartz->inhibitors = g_slist_prepend (quartz->inhibitors, inhibitor);
+
+  if (flags & GTK_APPLICATION_INHIBIT_LOGOUT)
+    quartz->quit_inhibit++;
+
+  return inhibitor->cookie;
+}
+
+static void
+gtk_application_impl_quartz_uninhibit (GtkApplicationImpl *impl,
+                                       guint               cookie)
+{
+  GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
+  GSList *iter;
+
+  for (iter = quartz->inhibitors; iter; iter = iter->next)
+    {
+      GtkApplicationQuartzInhibitor *inhibitor = iter->data;
+
+      if (inhibitor->cookie == cookie)
+        {
+          if (inhibitor->flags & GTK_APPLICATION_INHIBIT_LOGOUT)
+            quartz->quit_inhibit--;
+          gtk_application_quartz_inhibitor_free (inhibitor);
+          quartz->inhibitors = g_slist_delete_link (quartz->inhibitors, iter);
+          return;
+        }
+    }
+
+  g_warning ("Invalid inhibitor cookie");
+}
+
+static gboolean
+gtk_application_impl_quartz_is_inhibited (GtkApplicationImpl         *impl,
+                                          GtkApplicationInhibitFlags  flags)
+{
+  GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) impl;
+
+  if (flags & GTK_APPLICATION_INHIBIT_LOGOUT)
+    return quartz->quit_inhibit > 0;
+
+  return FALSE;
+}
+
+static void
+gtk_application_impl_quartz_init (GtkApplicationImplQuartz *quartz)
+{
+}
+
+static void
+gtk_application_impl_quartz_finalize (GObject *object)
+{
+  GtkApplicationImplQuartz *quartz = (GtkApplicationImplQuartz *) object;
+
+  g_slist_free_full (quartz->inhibitors, (GDestroyNotify) gtk_application_quartz_inhibitor_free);
+
+  G_OBJECT_CLASS (gtk_application_impl_quartz_parent_class)->finalize (object);
+}
+
+static void
+gtk_application_impl_quartz_class_init (GtkApplicationImplClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  class->startup = gtk_application_impl_quartz_startup;
+  class->shutdown = gtk_application_impl_quartz_shutdown;
+  class->set_app_menu = gtk_application_impl_quartz_set_app_menu;
+  class->set_menubar = gtk_application_impl_quartz_set_menubar;
+  class->inhibit = gtk_application_impl_quartz_inhibit;
+  class->uninhibit = gtk_application_impl_quartz_uninhibit;
+  class->is_inhibited = gtk_application_impl_quartz_is_inhibited;
+
+  gobject_class->finalize = gtk_application_impl_quartz_finalize;
+}
diff --git a/gtk/gtkapplication-wayland.c b/gtk/gtkapplication-wayland.c
new file mode 100644
index 0000000..c0b18bd
--- /dev/null
+++ b/gtk/gtkapplication-wayland.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ * Copyright © 2013 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gtkapplicationprivate.h"
+
+#include <gdk/wayland/gdkwayland.h>
+
+typedef GtkApplicationImplDBusClass GtkApplicationImplWaylandClass;
+
+typedef struct
+{
+  GtkApplicationImplDBus dbus;
+
+} GtkApplicationImplWayland;
+
+G_DEFINE_TYPE (GtkApplicationImplWayland, gtk_application_impl_wayland, GTK_TYPE_APPLICATION_IMPL_DBUS)
+
+static void
+gtk_application_impl_wayland_handle_window_map (GtkApplicationImpl *impl,
+                                                GtkWindow          *window)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+  GdkWindow *gdk_window;
+  gchar *window_path;
+
+  gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+
+  if (!GDK_IS_WAYLAND_WINDOW (gdk_window))
+    return;
+
+  window_path = gtk_application_impl_dbus_get_window_path (dbus, window);
+
+  gdk_wayland_window_set_dbus_properties_libgtk_only (gdk_window,
+                                                      dbus->application_id, dbus->app_menu_path, 
dbus->menubar_path,
+                                                      window_path, dbus->object_path, dbus->unique_name);
+
+  g_free (window_path);
+}
+
+static void
+gtk_application_impl_wayland_init (GtkApplicationImplWayland *wayland)
+{
+}
+
+static void
+gtk_application_impl_wayland_class_init (GtkApplicationImplWaylandClass *class)
+{
+  GtkApplicationImplClass *impl_class = GTK_APPLICATION_IMPL_CLASS (class);
+
+  impl_class->handle_window_map = gtk_application_impl_wayland_handle_window_map;
+}
diff --git a/gtk/gtkapplication-x11.c b/gtk/gtkapplication-x11.c
new file mode 100644
index 0000000..1bd3f29
--- /dev/null
+++ b/gtk/gtkapplication-x11.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ * Copyright © 2013 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gtkapplicationprivate.h"
+
+#include <gdk/gdkx.h>
+
+typedef GtkApplicationImplDBusClass GtkApplicationImplX11Class;
+
+typedef struct
+{
+  GtkApplicationImplDBus dbus;
+
+} GtkApplicationImplX11;
+
+G_DEFINE_TYPE (GtkApplicationImplX11, gtk_application_impl_x11, GTK_TYPE_APPLICATION_IMPL_DBUS)
+
+static void
+gtk_application_impl_x11_handle_window_realize (GtkApplicationImpl *impl,
+                                                GtkWindow          *window)
+{
+  GtkApplicationImplDBus *dbus = (GtkApplicationImplDBus *) impl;
+  GdkWindow *gdk_window;
+  gchar *window_path;
+
+  gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+
+  if (!GDK_IS_X11_WINDOW (gdk_window))
+    return;
+
+  window_path = gtk_application_impl_dbus_get_window_path (dbus, window);
+
+  gdk_x11_window_set_utf8_property (gdk_window, "_GTK_APPLICATION_ID", dbus->application_id);
+  gdk_x11_window_set_utf8_property (gdk_window, "_GTK_UNIQUE_BUS_NAME", dbus->unique_name);
+  gdk_x11_window_set_utf8_property (gdk_window, "_GTK_APPLICATION_OBJECT_PATH", dbus->object_path);
+  gdk_x11_window_set_utf8_property (gdk_window, "_GTK_WINDOW_OBJECT_PATH", window_path);
+  gdk_x11_window_set_utf8_property (gdk_window, "_GTK_APP_MENU_OBJECT_PATH", dbus->app_menu_path);
+  gdk_x11_window_set_utf8_property (gdk_window, "_GTK_MENUBAR_OBJECT_PATH", dbus->menubar_path);
+
+  g_free (window_path);
+}
+
+static GVariant *
+gtk_application_impl_x11_get_window_system_id (GtkApplicationImplDBus *dbus,
+                                               GtkWindow              *window)
+{
+  GdkWindow *gdk_window;
+
+  gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
+
+  if (GDK_IS_X11_WINDOW (gdk_window))
+    return g_variant_new_uint32 (GDK_WINDOW_XID (gdk_window));
+
+  return GTK_APPLICATION_IMPL_DBUS_CLASS (gtk_application_impl_x11_parent_class)->get_window_system_id 
(dbus, window);
+}
+
+static void
+gtk_application_impl_x11_init (GtkApplicationImplX11 *x11)
+{
+}
+
+static void
+gtk_application_impl_x11_class_init (GtkApplicationImplX11Class *class)
+{
+  GtkApplicationImplDBusClass *dbus_class = GTK_APPLICATION_IMPL_DBUS_CLASS (class);
+  GtkApplicationImplClass *impl_class = GTK_APPLICATION_IMPL_CLASS (class);
+
+  impl_class->handle_window_realize = gtk_application_impl_x11_handle_window_realize;
+  dbus_class->get_window_system_id = gtk_application_impl_x11_get_window_system_id;
+}
diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c
index d8af4a0..46540ab 100644
--- a/gtk/gtkapplication.c
+++ b/gtk/gtkapplication.c
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2010 Codethink Limited
+ * Copyright © 2013 Canonical Limited
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,9 +23,7 @@
 #include "gtkapplication.h"
 
 #include <stdlib.h>
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif
 #include <string.h>
 
 #include "gtkapplicationprivate.h"
@@ -33,19 +32,12 @@
 #include "gtkmain.h"
 #include "gtkrecentmanager.h"
 #include "gtkaccelmapprivate.h"
+#include "gtkbuilder.h"
 #include "gtkintl.h"
 
-#ifdef GDK_WINDOWING_QUARTZ
-#include "gtkmodelmenu-quartz.h"
-#import <Cocoa/Cocoa.h>
-#include <Carbon/Carbon.h>
-#include "gtkmessagedialog.h"
-#endif
-
-#include <gdk/gdk.h>
-#ifdef GDK_WINDOWING_X11
-#include <gdk/x11/gdkx.h>
-#endif
+/* NB: please do not add backend-specific GDK headers here.  This should
+ * be abstracted via GtkApplicationImpl.
+ */
 
 /**
  * SECTION:gtkapplication
@@ -469,245 +461,33 @@ accels_finalize (Accels *accels)
 
 struct _GtkApplicationPrivate
 {
+  GtkApplicationImpl *impl;
+
   GList *windows;
 
   GMenuModel      *app_menu;
   GMenuModel      *menubar;
   Accels           accels;
+  guint            last_window_id;
 
   gboolean register_session;
   GtkActionMuxer  *muxer;
-
-#ifdef GDK_WINDOWING_X11
-  guint next_id;
-
-  GDBusConnection *session_bus;
-  const gchar     *application_id;
-  const gchar     *object_path;
-
-  gchar           *app_menu_path;
-  guint            app_menu_id;
-
-  guint            menubar_id;
-  gchar           *menubar_path;
-
-  GDBusProxy *sm_proxy;
-  GDBusProxy *client_proxy;
-  gchar *app_id;
-  gchar *client_path;
-#endif
-
-#ifdef GDK_WINDOWING_QUARTZ
-  GMenu *combined;
-
-  GSList *inhibitors;
-  gint quit_inhibit;
-  guint next_cookie;
-#endif
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkApplication, gtk_application, G_TYPE_APPLICATION)
 
-#ifdef GDK_WINDOWING_X11
-static void
-gtk_application_x11_publish_menu (GtkApplication  *application,
-                                  const gchar     *type,
-                                  GMenuModel      *model,
-                                  guint           *id,
-                                  gchar          **path)
-{
-  gint i;
-
-  if (application->priv->session_bus == NULL)
-    return;
-
-  /* unexport any existing menu */
-  if (*id)
-    {
-      g_dbus_connection_unexport_menu_model (application->priv->session_bus, *id);
-      g_free (*path);
-      *path = NULL;
-      *id = 0;
-    }
-
-  /* export the new menu, if there is one */
-  if (model != NULL)
-    {
-      /* try getting the preferred name */
-      *path = g_strconcat (application->priv->object_path, "/menus/", type, NULL);
-      *id = g_dbus_connection_export_menu_model (application->priv->session_bus, *path, model, NULL);
-
-      /* keep trying until we get a working name... */
-      for (i = 0; *id == 0; i++)
-        {
-          g_free (*path);
-          *path = g_strdup_printf ("%s/menus/%s%d", application->priv->object_path, type, i);
-          *id = g_dbus_connection_export_menu_model (application->priv->session_bus, *path, model, NULL);
-        }
-    }
-}
-
-static void
-gtk_application_set_app_menu_x11 (GtkApplication *application,
-                                  GMenuModel     *app_menu)
-{
-  gtk_application_x11_publish_menu (application, "appmenu", app_menu,
-                                    &application->priv->app_menu_id,
-                                    &application->priv->app_menu_path);
-}
-
-static void
-gtk_application_set_menubar_x11 (GtkApplication *application,
-                                 GMenuModel     *menubar)
-{
-  gtk_application_x11_publish_menu (application, "menubar", menubar,
-                                    &application->priv->menubar_id,
-                                    &application->priv->menubar_path);
-}
-
-static void
-gtk_application_window_added_x11 (GtkApplication *application,
-                                  GtkWindow      *window)
-{
-  if (application->priv->session_bus == NULL)
-    return;
-
-  if (GTK_IS_APPLICATION_WINDOW (window))
-    {
-      GtkApplicationWindow *app_window = GTK_APPLICATION_WINDOW (window);
-      gboolean success;
-
-      /* GtkApplicationWindow associates with us when it is first created,
-       * so surely it's not realized yet...
-       */
-      g_assert (!gtk_widget_get_realized (GTK_WIDGET (window)));
-
-      do
-        {
-          gchar *window_path;
-          guint window_id;
-
-          window_id = application->priv->next_id++;
-          window_path = g_strdup_printf ("%s/window/%u", application->priv->object_path, window_id);
-          success = gtk_application_window_publish (app_window, application->priv->session_bus, window_path, 
window_id);
-          g_free (window_path);
-        }
-      while (!success);
-    }
-}
-
-static void
-gtk_application_window_removed_x11 (GtkApplication *application,
-                                    GtkWindow      *window)
-{
-  if (application->priv->session_bus == NULL)
-    return;
-
-  if (GTK_IS_APPLICATION_WINDOW (window))
-    gtk_application_window_unpublish (GTK_APPLICATION_WINDOW (window));
-}
-
-static void gtk_application_startup_session_dbus (GtkApplication *app);
-
-static void
-gtk_application_startup_x11 (GtkApplication *application)
-{
-  application->priv->session_bus = g_application_get_dbus_connection (G_APPLICATION (application));
-  application->priv->object_path = g_application_get_dbus_object_path (G_APPLICATION (application));
-
-  gtk_application_startup_session_dbus (GTK_APPLICATION (application));
-}
-
-static void
-gtk_application_shutdown_x11 (GtkApplication *application)
-{
-  gtk_application_set_app_menu_x11 (application, NULL);
-  gtk_application_set_menubar_x11 (application, NULL);
-
-  application->priv->session_bus = NULL;
-  application->priv->object_path = NULL;
-
-  g_clear_object (&application->priv->sm_proxy);
-  g_clear_object (&application->priv->client_proxy);
-  g_free (application->priv->app_id);
-  g_free (application->priv->client_path);
-}
-
 const gchar *
 gtk_application_get_app_menu_object_path (GtkApplication *application)
 {
-  return application->priv->app_menu_path;
+  g_assert_not_reached (); /* XXX */
 }
 
 const gchar *
 gtk_application_get_menubar_object_path (GtkApplication *application)
 {
-  return application->priv->menubar_path;
-}
-
-#endif
-
-#ifdef GDK_WINDOWING_QUARTZ
-
-typedef struct {
-  guint cookie;
-  GtkApplicationInhibitFlags flags;
-  char *reason;
-  GtkWindow *window;
-} GtkApplicationQuartzInhibitor;
-
-static void
-gtk_application_quartz_inhibitor_free (GtkApplicationQuartzInhibitor *inhibitor)
-{
-  g_free (inhibitor->reason);
-  g_clear_object (&inhibitor->window);
-  g_slice_free (GtkApplicationQuartzInhibitor, inhibitor);
+  g_assert_not_reached (); /* XXX */
 }
 
-static void
-gtk_application_menu_changed_quartz (GObject    *object,
-                                     GParamSpec *pspec,
-                                     gpointer    user_data)
-{
-  GtkApplication *application = GTK_APPLICATION (object);
-  GMenu *combined;
-
-  combined = g_menu_new ();
-  g_menu_append_submenu (combined, "Application", gtk_application_get_app_menu (application));
-  g_menu_append_section (combined, NULL, gtk_application_get_menubar (application));
-
-  gtk_quartz_set_main_menu (G_MENU_MODEL (combined), application);
-
-  g_object_unref (combined);
-}
-
-static void gtk_application_startup_session_quartz (GtkApplication *app);
-
-static void
-gtk_application_startup_quartz (GtkApplication *application)
-{
-  [NSApp finishLaunching];
-
-  g_signal_connect (application, "notify::app-menu", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
-  g_signal_connect (application, "notify::menubar", G_CALLBACK (gtk_application_menu_changed_quartz), NULL);
-  gtk_application_menu_changed_quartz (G_OBJECT (application), NULL, NULL);
-
-  gtk_application_startup_session_quartz (application);
-}
-
-static void
-gtk_application_shutdown_quartz (GtkApplication *application)
-{
-  gtk_quartz_clear_main_menu ();
-
-  g_signal_handlers_disconnect_by_func (application, gtk_application_menu_changed_quartz, NULL);
-
-  g_slist_free_full (application->priv->inhibitors,
-                    (GDestroyNotify) gtk_application_quartz_inhibitor_free);
-  application->priv->inhibitors = NULL;
-}
-#endif
-
 static gboolean
 gtk_application_focus_in_event_cb (GtkWindow      *window,
                                    GdkEventFocus  *event,
@@ -724,6 +504,7 @@ gtk_application_focus_in_event_cb (GtkWindow      *window,
       priv->windows = g_list_concat (link, priv->windows);
     }
 
+  gtk_application_impl_active_window_changed (application->priv->impl, window);
   g_object_notify (G_OBJECT (application), "active-window");
 
   return FALSE;
@@ -741,25 +522,17 @@ gtk_application_startup (GApplication *g_application)
 
   gtk_init (0, 0);
 
-#ifdef GDK_WINDOWING_X11
-  gtk_application_startup_x11 (application);
-#endif
-
-#ifdef GDK_WINDOWING_QUARTZ
-  gtk_application_startup_quartz (application);
-#endif
+  application->priv->impl = gtk_application_impl_new (application, gdk_display_get_default ());
+  gtk_application_impl_startup (application->priv->impl, application->priv->register_session);
 }
 
 static void
-gtk_application_shutdown (GApplication *application)
+gtk_application_shutdown (GApplication *g_application)
 {
-#ifdef GDK_WINDOWING_X11
-  gtk_application_shutdown_x11 (GTK_APPLICATION (application));
-#endif
+  GtkApplication *application = GTK_APPLICATION (g_application);
 
-#ifdef GDK_WINDOWING_QUARTZ
-  gtk_application_shutdown_quartz (GTK_APPLICATION (application));
-#endif
+  gtk_application_impl_shutdown (application->priv->impl);
+  g_clear_object (&application->priv->impl);
 
   /* Keep this section in sync with gtk_main() */
 
@@ -770,7 +543,7 @@ gtk_application_shutdown (GApplication *application)
   _gtk_recent_manager_sync ();
 
   G_APPLICATION_CLASS (gtk_application_parent_class)
-    ->shutdown (application);
+    ->shutdown (g_application);
 }
 
 static void
@@ -779,39 +552,29 @@ gtk_application_add_platform_data (GApplication    *application,
 {
   const gchar *startup_id;
 
+  /* This is slightly evil.
+   *
+   * We don't have an impl here because we're remote so we can't figure
+   * out what to do on a per-display-server basis.
+   *
+   * So we do all the things... which currently is just one thing.
+   */
   startup_id = getenv ("DESKTOP_STARTUP_ID");
-  
+
   if (startup_id && g_utf8_validate (startup_id, -1, NULL))
     g_variant_builder_add (builder, "{sv}", "desktop-startup-id",
                            g_variant_new_string (startup_id));
 }
 
 static void
-gtk_application_before_emit (GApplication *application,
+gtk_application_before_emit (GApplication *g_application,
                              GVariant     *platform_data)
 {
-  GVariantIter iter;
-  const gchar *key;
-  GVariant *value;
+  GtkApplication *application = GTK_APPLICATION (g_application);
 
   gdk_threads_enter ();
 
-  g_variant_iter_init (&iter, platform_data);
-  while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
-    {
-#ifdef GDK_WINDOWING_X11
-      if (strcmp (key, "desktop-startup-id") == 0)
-        {
-          GdkDisplay *display;
-          const gchar *id;
-
-          display = gdk_display_get_default ();
-          id = g_variant_get_string (value, NULL);
-          if (GDK_IS_X11_DISPLAY (display))
-            gdk_x11_display_set_startup_notification_id (display, id);
-       }
-#endif
-    }
+  gtk_application_impl_before_emit (application->priv->impl, platform_data);
 }
 
 static void
@@ -831,10 +594,6 @@ gtk_application_init (GtkApplication *application)
   application->priv->muxer = gtk_action_muxer_new ();
 
   accels_init (&application->priv->accels);
-
-#ifdef GDK_WINDOWING_X11
-  application->priv->next_id = 1;
-#endif
 }
 
 static void
@@ -843,6 +602,9 @@ gtk_application_window_added (GtkApplication *application,
 {
   GtkApplicationPrivate *priv = application->priv;
 
+  if (GTK_IS_APPLICATION_WINDOW (window))
+    gtk_application_window_set_id (GTK_APPLICATION_WINDOW (window), ++application->priv->last_window_id);
+
   priv->windows = g_list_prepend (priv->windows, window);
   gtk_window_set_application (window, application);
   g_application_hold (G_APPLICATION (application));
@@ -851,10 +613,9 @@ gtk_application_window_added (GtkApplication *application,
                     G_CALLBACK (gtk_application_focus_in_event_cb),
                     application);
 
-#ifdef GDK_WINDOWING_X11
-  gtk_application_window_added_x11 (application, window);
-#endif
+  gtk_application_impl_window_added (application->priv->impl, window);
 
+  gtk_application_impl_active_window_changed (application->priv->impl, window);
   g_object_notify (G_OBJECT (application), "active-window");
 }
 
@@ -867,9 +628,7 @@ gtk_application_window_removed (GtkApplication *application,
 
   old_active = priv->windows;
 
-#ifdef GDK_WINDOWING_X11
-  gtk_application_window_removed_x11 (application, window);
-#endif
+  gtk_application_impl_window_removed (application->priv->impl, window);
 
   g_signal_handlers_disconnect_by_func (window,
                                         gtk_application_focus_in_event_cb,
@@ -880,7 +639,10 @@ gtk_application_window_removed (GtkApplication *application,
   gtk_window_set_application (window, NULL);
 
   if (priv->windows != old_active)
-    g_object_notify (G_OBJECT (application), "active-window");
+    {
+      gtk_application_impl_active_window_changed (application->priv->impl, priv->windows ? 
priv->windows->data : NULL);
+      g_object_notify (G_OBJECT (application), "active-window");
+    }
 }
 
 static void
@@ -1411,9 +1173,7 @@ gtk_application_set_app_menu (GtkApplication *application,
       if (app_menu)
         extract_accels_from_menu (app_menu, application);
 
-#ifdef GDK_WINDOWING_X11
-      gtk_application_set_app_menu_x11 (application, app_menu);
-#endif
+      gtk_application_impl_set_app_menu (application->priv->impl, app_menu);
 
       g_object_notify (G_OBJECT (application), "app-menu");
     }
@@ -1485,9 +1245,7 @@ gtk_application_set_menubar (GtkApplication *application,
       if (menubar)
         extract_accels_from_menu (menubar, application);
 
-#ifdef GDK_WINDOWING_X11
-      gtk_application_set_menubar_x11 (application, menubar);
-#endif
+      gtk_application_impl_set_menubar (application->priv->impl, menubar);
 
       g_object_notify (G_OBJECT (application), "menubar");
     }
@@ -1512,178 +1270,6 @@ gtk_application_get_menubar (GtkApplication *application)
   return application->priv->menubar;
 }
 
-#if defined(GDK_WINDOWING_X11)
-
-/* D-Bus Session Management
- *
- * The protocol and the D-Bus API are described here:
- * http://live.gnome.org/SessionManagement/GnomeSession
- * http://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html
- */
-
-static void
-unregister_client (GtkApplication *app)
-{
-  GError *error = NULL;
-
-  g_debug ("Unregistering client");
-
-  g_dbus_proxy_call_sync (app->priv->sm_proxy,
-                          "UnregisterClient",
-                          g_variant_new ("(o)", app->priv->client_path),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          G_MAXINT,
-                          NULL,
-                          &error);
-
-  if (error)
-    {
-      g_warning ("Failed to unregister client: %s", error->message);
-      g_error_free (error);
-    }
-
-  g_clear_object (&app->priv->client_proxy);
-
-  g_free (app->priv->client_path);
-  app->priv->client_path = NULL;
-}
-
-static void
-gtk_application_quit_response (GtkApplication *application,
-                               gboolean        will_quit,
-                               const gchar    *reason)
-{
-  g_debug ("Calling EndSessionResponse %d '%s'", will_quit, reason);
-
-  g_dbus_proxy_call (application->priv->client_proxy,
-                     "EndSessionResponse",
-                     g_variant_new ("(bs)", will_quit, reason ? reason : ""),
-                     G_DBUS_CALL_FLAGS_NONE,
-                     G_MAXINT,
-                     NULL, NULL, NULL);
-}
-static void
-client_proxy_signal (GDBusProxy     *proxy,
-                     const gchar    *sender_name,
-                     const gchar    *signal_name,
-                     GVariant       *parameters,
-                     GtkApplication *app)
-{
-  if (strcmp (signal_name, "QueryEndSession") == 0)
-    {
-      g_debug ("Received QueryEndSession");
-      gtk_application_quit_response (app, TRUE, NULL);
-    }
-  else if (strcmp (signal_name, "CancelEndSession") == 0)
-    {
-      g_debug ("Received CancelEndSession");
-    }
-  else if (strcmp (signal_name, "EndSession") == 0)
-    {
-      g_debug ("Received EndSession");
-      gtk_application_quit_response (app, TRUE, NULL);
-      unregister_client (app);
-      g_application_quit (G_APPLICATION (app));
-    }
-  else if (strcmp (signal_name, "Stop") == 0)
-    {
-      g_debug ("Received Stop");
-      unregister_client (app);
-      g_application_quit (G_APPLICATION (app));
-    }
-}
-
-static void
-gtk_application_startup_session_dbus (GtkApplication *app)
-{
-  static gchar *client_id;
-  GError *error = NULL;
-  GVariant *res;
-
-  if (app->priv->session_bus == NULL)
-    return;
-
-  if (client_id == NULL)
-    {
-      const gchar *desktop_autostart_id;
-
-      desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
-      /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to
-       * use the same client id.
-       */
-      g_unsetenv ("DESKTOP_AUTOSTART_ID");
-      client_id = g_strdup (desktop_autostart_id ? desktop_autostart_id : "");
-    }
-
-  g_debug ("Connecting to session manager");
-
-  app->priv->sm_proxy = g_dbus_proxy_new_sync (app->priv->session_bus,
-                                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-                                               G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-                                               NULL,
-                                               "org.gnome.SessionManager",
-                                               "/org/gnome/SessionManager",
-                                               "org.gnome.SessionManager",
-                                               NULL,
-                                               &error);
-  if (error)
-    {
-      g_warning ("Failed to get a session proxy: %s", error->message);
-      g_error_free (error);
-      return;
-    }
-
-  /* FIXME: should we reuse the D-Bus application id here ? */
-  app->priv->app_id = g_strdup (g_get_prgname ());
-
-  if (!app->priv->register_session)
-    return;
-
-  g_debug ("Registering client '%s' '%s'", app->priv->app_id, client_id);
-
-  res = g_dbus_proxy_call_sync (app->priv->sm_proxy,
-                                "RegisterClient",
-                                g_variant_new ("(ss)", app->priv->app_id, client_id),
-                                G_DBUS_CALL_FLAGS_NONE,
-                                G_MAXINT,
-                                NULL,
-                                &error);
-
-  if (error)
-    {
-      g_warning ("Failed to register client: %s", error->message);
-      g_error_free (error);
-      g_clear_object (&app->priv->sm_proxy);
-      return;
-    }
-
-  g_variant_get (res, "(o)", &app->priv->client_path);
-  g_variant_unref (res);
-
-  g_debug ("Registered client at '%s'", app->priv->client_path);
-
-  app->priv->client_proxy = g_dbus_proxy_new_sync (app->priv->session_bus, 0,
-                                                   NULL,
-                                                   "org.gnome.SessionManager",
-                                                   app->priv->client_path,
-                                                   "org.gnome.SessionManager.ClientPrivate",
-                                                   NULL,
-                                                   &error);
-  if (error)
-    {
-      g_warning ("Failed to get client proxy: %s", error->message);
-      g_error_free (error);
-      g_clear_object (&app->priv->sm_proxy);
-      g_free (app->priv->client_path);
-      app->priv->client_path = NULL;
-      return;
-    }
-
-  g_signal_connect (app->priv->client_proxy, "g-signal", G_CALLBACK (client_proxy_signal), app);
-}
-
-
-
 /**
  * GtkApplicationInhibitFlags:
  * @GTK_APPLICATION_INHIBIT_LOGOUT: Inhibit ending the user session
@@ -1742,52 +1328,10 @@ gtk_application_inhibit (GtkApplication             *application,
                          GtkApplicationInhibitFlags  flags,
                          const gchar                *reason)
 {
-  GVariant *res;
-  GError *error = NULL;
-  guint cookie;
-  guint xid = 0;
-
   g_return_val_if_fail (GTK_IS_APPLICATION (application), 0);
   g_return_val_if_fail (!g_application_get_is_remote (G_APPLICATION (application)), 0);
 
-  if (application->priv->sm_proxy == NULL)
-    return 0;
-
-  if (window != NULL)
-    {
-      GdkWindow *gdkwindow;
-
-      gdkwindow = gtk_widget_get_window (GTK_WIDGET (window));
-      if (gdkwindow == NULL)
-        g_warning ("Inhibit called with an unrealized window");
-#ifdef GDK_WINDOWING_X11
-      else if (GDK_IS_X11_WINDOW (gdkwindow))
-        xid = GDK_WINDOW_XID (gdkwindow);
-#endif
-    }
-
-  res = g_dbus_proxy_call_sync (application->priv->sm_proxy,
-                                "Inhibit",
-                                g_variant_new ("(susu)",
-                                               application->priv->app_id,
-                                               xid,
-                                               reason,
-                                               flags),
-                                G_DBUS_CALL_FLAGS_NONE,
-                                G_MAXINT,
-                                NULL,
-                                &error);
- if (error)
-    {
-      g_warning ("Calling Inhibit failed: %s", error->message);
-      g_error_free (error);
-      return 0;
-    }
-
-  g_variant_get (res, "(u)", &cookie);
-  g_variant_unref (res);
-
-  return cookie;
+  return gtk_application_impl_inhibit (application->priv->impl, window, flags, reason);
 }
 
 /**
@@ -1808,16 +1352,7 @@ gtk_application_uninhibit (GtkApplication *application,
   g_return_if_fail (!g_application_get_is_remote (G_APPLICATION (application)));
   g_return_if_fail (cookie > 0);
 
-  /* Application could only obtain a cookie through a session
-   * manager proxy, so it's valid to assert its presence here. */
-  g_return_if_fail (application->priv->sm_proxy != NULL);
-
-  g_dbus_proxy_call (application->priv->sm_proxy,
-                     "Uninhibit",
-                     g_variant_new ("(u)", cookie),
-                     G_DBUS_CALL_FLAGS_NONE,
-                     G_MAXINT,
-                     NULL, NULL, NULL);
+  gtk_application_impl_uninhibit (application->priv->impl, cookie);
 }
 
 /**
@@ -1836,194 +1371,12 @@ gboolean
 gtk_application_is_inhibited (GtkApplication             *application,
                               GtkApplicationInhibitFlags  flags)
 {
-  GVariant *res;
-  GError *error = NULL;
-  gboolean inhibited;
-
   g_return_val_if_fail (GTK_IS_APPLICATION (application), FALSE);
   g_return_val_if_fail (!g_application_get_is_remote (G_APPLICATION (application)), FALSE);
 
-  if (application->priv->sm_proxy == NULL)
-    return FALSE;
-
-  res = g_dbus_proxy_call_sync (application->priv->sm_proxy,
-                                "IsInhibited",
-                                g_variant_new ("(u)", flags),
-                                G_DBUS_CALL_FLAGS_NONE,
-                                G_MAXINT,
-                                NULL,
-                                &error);
-  if (error)
-    {
-      g_warning ("Calling IsInhibited failed: %s", error->message);
-      g_error_free (error);
-      return FALSE;
-    }
-
-  g_variant_get (res, "(b)", &inhibited);
-  g_variant_unref (res);
-
-  return inhibited;
-}
-
-#elif defined(GDK_WINDOWING_QUARTZ)
-
-/* OS X implementation copied from EggSMClient, but simplified since
- * it doesn't need to interact with the user.
- */
-
-static gboolean
-idle_will_quit (gpointer data)
-{
-  GtkApplication *app = data;
-
-  if (app->priv->quit_inhibit == 0)
-    g_application_quit (G_APPLICATION (app));
-  else
-    {
-      GtkApplicationQuartzInhibitor *inhibitor;
-      GSList *iter;
-      GtkWidget *dialog;
-
-      for (iter = app->priv->inhibitors; iter; iter = iter->next)
-       {
-         inhibitor = iter->data;
-         if (inhibitor->flags & GTK_APPLICATION_INHIBIT_LOGOUT)
-           break;
-        }
-      g_assert (inhibitor != NULL);
-
-      dialog = gtk_message_dialog_new (inhibitor->window,
-                                      GTK_DIALOG_MODAL,
-                                      GTK_MESSAGE_ERROR,
-                                      GTK_BUTTONS_OK,
-                                      _("%s cannot quit at this time:\n\n%s"),
-                                      g_get_application_name (),
-                                      inhibitor->reason);
-      g_signal_connect_swapped (dialog,
-                                "response",
-                                G_CALLBACK (gtk_widget_destroy),
-                                dialog);
-      gtk_widget_show_all (dialog);
-    }
-
-  return G_SOURCE_REMOVE;
-}
-
-static pascal OSErr
-quit_requested (const AppleEvent *aevt,
-                AppleEvent       *reply,
-                long              refcon)
-{
-  GtkApplication *app = GSIZE_TO_POINTER ((gsize)refcon);
-
-  /* Don't emit the "quit" signal immediately, since we're
-   * called from a weird point in the guts of gdkeventloop-quartz.c
-   */
-  g_idle_add_full (G_PRIORITY_DEFAULT, idle_will_quit, app, NULL);
-
-  return app->priv->quit_inhibit == 0 ? noErr : userCanceledErr;
+  return gtk_application_impl_is_inhibited (application->priv->impl, flags);
 }
 
-static void
-gtk_application_startup_session_quartz (GtkApplication *app)
-{
-  if (app->priv->register_session)
-    AEInstallEventHandler (kCoreEventClass, kAEQuitApplication,
-                           NewAEEventHandlerUPP (quit_requested),
-                           (long)GPOINTER_TO_SIZE (app), false);
-}
-
-guint
-gtk_application_inhibit (GtkApplication             *application,
-                         GtkWindow                  *window,
-                         GtkApplicationInhibitFlags  flags,
-                         const gchar                *reason)
-{
-  GtkApplicationQuartzInhibitor *inhibitor;
-
-  g_return_val_if_fail (GTK_IS_APPLICATION (application), 0);
-  g_return_val_if_fail (flags != 0, 0);
-
-  inhibitor = g_slice_new (GtkApplicationQuartzInhibitor);
-  inhibitor->cookie = ++application->priv->next_cookie;
-  inhibitor->flags = flags;
-  inhibitor->reason = g_strdup (reason);
-  inhibitor->window = window ? g_object_ref (window) : NULL;
-
-  application->priv->inhibitors = g_slist_prepend (application->priv->inhibitors, inhibitor);
-
-  if (flags & GTK_APPLICATION_INHIBIT_LOGOUT)
-    application->priv->quit_inhibit++;
-
-  return inhibitor->cookie;
-}
-
-void
-gtk_application_uninhibit (GtkApplication *application,
-                           guint           cookie)
-{
-  GSList *iter;
-
-  for (iter = application->priv->inhibitors; iter; iter = iter->next)
-    {
-      GtkApplicationQuartzInhibitor *inhibitor = iter->data;
-
-      if (inhibitor->cookie == cookie)
-       {
-         if (inhibitor->flags & GTK_APPLICATION_INHIBIT_LOGOUT)
-           application->priv->quit_inhibit--;
-         gtk_application_quartz_inhibitor_free (inhibitor);
-         application->priv->inhibitors = g_slist_delete_link (application->priv->inhibitors, iter);
-         return;
-       }
-    }
-
-  g_warning ("Invalid inhibitor cookie");
-}
-
-gboolean
-gtk_application_is_inhibited (GtkApplication             *application,
-                              GtkApplicationInhibitFlags  flags)
-{
-  if (flags & GTK_APPLICATION_INHIBIT_LOGOUT)
-    return application->priv->quit_inhibit > 0;
-
-  return FALSE;
-}
-
-#else
-
-/* Trivial implementation.
- *
- * For the inhibit API on Windows, see
- * http://msdn.microsoft.com/en-us/library/ms700677%28VS.85%29.aspx
- */
-
-guint
-gtk_application_inhibit (GtkApplication             *application,
-                         GtkWindow                  *window,
-                         GtkApplicationInhibitFlags  flags,
-                         const gchar                *reason)
-{
-  return 0;
-}
-
-void
-gtk_application_uninhibit (GtkApplication *application,
-                           guint           cookie)
-{
-}
-
-gboolean
-gtk_application_is_inhibited (GtkApplication             *application,
-                              GtkApplicationInhibitFlags  flags)
-{
-  return FALSE;
-}
-
-#endif
-
 GtkActionMuxer *
 gtk_application_get_parent_muxer_for_window (GtkWindow *window)
 {
@@ -2179,3 +1532,25 @@ gtk_application_get_accels_for_action (GtkApplication *application,
 
   return accels;
 }
+
+GtkActionMuxer *
+gtk_application_get_action_muxer (GtkApplication *application)
+{
+  g_assert (application->priv->muxer);
+
+  return application->priv->muxer;
+}
+
+void
+gtk_application_handle_window_realize (GtkApplication *application,
+                                       GtkWindow      *window)
+{
+  gtk_application_impl_handle_window_realize (application->priv->impl, window);
+}
+
+void
+gtk_application_handle_window_map (GtkApplication *application,
+                                   GtkWindow      *window)
+{
+  gtk_application_impl_handle_window_map (application->priv->impl, window);
+}
diff --git a/gtk/gtkapplicationimpl.c b/gtk/gtkapplicationimpl.c
new file mode 100644
index 0000000..6cb899a
--- /dev/null
+++ b/gtk/gtkapplicationimpl.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright © 2013 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gtkapplicationprivate.h"
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/x11/gdkx.h>
+#endif
+
+#ifdef GDK_WINDOWING_WAYLAND
+#include <gdk/wayland/gdkwayland.h>
+#endif
+
+#ifdef GDK_WINDOWING_QUARTZ
+#include <gdk/quartz/gdkquartz.h>
+#endif
+
+G_DEFINE_TYPE (GtkApplicationImpl, gtk_application_impl, G_TYPE_OBJECT)
+
+static void
+gtk_application_impl_init (GtkApplicationImpl *impl)
+{
+}
+
+static guint do_nothing (void) { return 0; }
+
+static void
+gtk_application_impl_class_init (GtkApplicationImplClass *class)
+{
+  /* NB: can only 'do_nothing' for functions with integer or void return */
+  class->startup = (gpointer) do_nothing;
+  class->shutdown = (gpointer) do_nothing;
+  class->before_emit = (gpointer) do_nothing;
+  class->window_added = (gpointer) do_nothing;
+  class->window_removed = (gpointer) do_nothing;
+  class->active_window_changed = (gpointer) do_nothing;
+  class->handle_window_realize = (gpointer) do_nothing;
+  class->handle_window_map = (gpointer) do_nothing;
+  class->set_app_menu = (gpointer) do_nothing;
+  class->set_menubar = (gpointer) do_nothing;
+  class->inhibit = (gpointer) do_nothing;
+  class->uninhibit = (gpointer) do_nothing;
+  class->is_inhibited = (gpointer) do_nothing;
+}
+
+void
+gtk_application_impl_startup (GtkApplicationImpl *impl,
+                              gboolean            register_session)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->startup (impl, register_session);
+}
+
+void
+gtk_application_impl_shutdown (GtkApplicationImpl *impl)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->shutdown (impl);
+}
+
+void
+gtk_application_impl_before_emit (GtkApplicationImpl *impl,
+                                  GVariant           *platform_data)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->before_emit (impl, platform_data);
+}
+
+void
+gtk_application_impl_window_added (GtkApplicationImpl *impl,
+                                   GtkWindow          *window)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->window_added (impl, window);
+}
+
+void
+gtk_application_impl_window_removed (GtkApplicationImpl *impl,
+                                     GtkWindow          *window)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->window_removed (impl, window);
+}
+
+void
+gtk_application_impl_active_window_changed (GtkApplicationImpl *impl,
+                                            GtkWindow          *window)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->active_window_changed (impl, window);
+}
+
+void
+gtk_application_impl_handle_window_realize (GtkApplicationImpl *impl,
+                                            GtkWindow          *window)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->handle_window_realize (impl, window);
+}
+
+void
+gtk_application_impl_handle_window_map (GtkApplicationImpl *impl,
+                                        GtkWindow          *window)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->handle_window_map (impl, window);
+}
+
+void
+gtk_application_impl_set_app_menu (GtkApplicationImpl *impl,
+                                   GMenuModel         *app_menu)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->set_app_menu (impl, app_menu);
+}
+
+void
+gtk_application_impl_set_menubar (GtkApplicationImpl *impl,
+                                  GMenuModel         *menubar)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->set_menubar (impl, menubar);
+}
+
+guint
+gtk_application_impl_inhibit (GtkApplicationImpl         *impl,
+                              GtkWindow                  *window,
+                              GtkApplicationInhibitFlags  flags,
+                              const gchar                *reason)
+{
+  return GTK_APPLICATION_IMPL_GET_CLASS (impl)->inhibit (impl, window, flags, reason);
+}
+
+void
+gtk_application_impl_uninhibit (GtkApplicationImpl *impl,
+                                guint               cookie)
+{
+  GTK_APPLICATION_IMPL_GET_CLASS (impl)->uninhibit (impl, cookie);
+}
+
+gboolean
+gtk_application_impl_is_inhibited (GtkApplicationImpl         *impl,
+                                   GtkApplicationInhibitFlags  flags)
+{
+  return GTK_APPLICATION_IMPL_GET_CLASS (impl)->is_inhibited (impl, flags);
+}
+
+GtkApplicationImpl *
+gtk_application_impl_new (GtkApplication *application,
+                          GdkDisplay     *display)
+{
+  GtkApplicationImpl *impl;
+  GType impl_type;
+
+  impl_type = gtk_application_impl_get_type ();
+
+#ifdef GDK_WINDOWING_X11
+  if (GDK_IS_X11_DISPLAY (display))
+    impl_type = gtk_application_impl_x11_get_type ();
+#endif
+
+#ifdef GDK_WINDOWING_WAYLAND
+  if (GDK_IS_WAYLAND_DISPLAY (display))
+    impl_type = gtk_application_impl_wayland_get_type ();
+#endif
+
+#ifdef GDK_WINDOWING_QUARTZ
+  if (GDK_IS_QUARTZ_DISPLAY (display))
+    impl_type = gtk_application_impl_quartz_get_type ();
+#endif
+
+  impl = g_object_new (impl_type, NULL);
+  impl->application = application;
+  impl->display = display;
+
+  return impl;
+}
diff --git a/gtk/gtkapplicationprivate.h b/gtk/gtkapplicationprivate.h
index 597b5a6..db2fa00 100644
--- a/gtk/gtkapplicationprivate.h
+++ b/gtk/gtkapplicationprivate.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011 Canonical Limited
+ * Copyright © 2011, 2013 Canonical Limited
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -27,22 +27,16 @@
 #include "gtkactionmuxer.h"
 
 G_GNUC_INTERNAL
-gboolean                gtk_application_window_publish                  (GtkApplicationWindow *window,
-                                                                         GDBusConnection      *session,
-                                                                         const gchar          *object_path,
-                                                                         guint                 object_id);
-
+void                    gtk_application_window_set_id                   (GtkApplicationWindow     *window,
+                                                                         guint                     id);
 G_GNUC_INTERNAL
-void                    gtk_application_window_unpublish                (GtkApplicationWindow *window);
-
+GActionGroup *          gtk_application_window_get_action_group         (GtkApplicationWindow     *window);
 G_GNUC_INTERNAL
-GtkAccelGroup         * gtk_application_window_get_accel_group          (GtkApplicationWindow *window);
-
+void                    gtk_application_handle_window_realize           (GtkApplication           
*application,
+                                                                         GtkWindow                *window);
 G_GNUC_INTERNAL
-const gchar *           gtk_application_get_app_menu_object_path        (GtkApplication       *application);
-G_GNUC_INTERNAL
-const gchar *           gtk_application_get_menubar_object_path         (GtkApplication       *application);
-
+void                    gtk_application_handle_window_map               (GtkApplication           
*application,
+                                                                         GtkWindow                *window);
 G_GNUC_INTERNAL
 GtkActionMuxer *        gtk_application_get_parent_muxer_for_window     (GtkWindow                *window);
 
@@ -57,5 +51,161 @@ void                    gtk_application_foreach_accel_keys              (GtkAppl
                                                                          GtkWindow                *window,
                                                                          GtkWindowKeysForeachFunc  callback,
                                                                          gpointer                  
user_data);
+G_GNUC_INTERNAL
+GtkActionMuxer *        gtk_application_get_action_muxer                (GtkApplication           
*application);
+
+
+#define GTK_TYPE_APPLICATION_IMPL                           (gtk_application_impl_get_type ())
+#define GTK_APPLICATION_IMPL_CLASS(class)                   (G_TYPE_CHECK_CLASS_CAST ((class),               
      \
+                                                             GTK_TYPE_APPLICATION_IMPL,                      
      \
+                                                             GtkApplicationImplClass))
+#define GTK_APPLICATION_IMPL_GET_CLASS(obj)                 (G_TYPE_INSTANCE_GET_CLASS ((obj),               
      \
+                                                             GTK_TYPE_APPLICATION_IMPL,                      
      \
+                                                             GtkApplicationImplClass))
+
+typedef struct
+{
+  GObject parent_instance;
+  GtkApplication *application;
+  GdkDisplay *display;
+} GtkApplicationImpl;
+
+typedef struct
+{
+  GObjectClass parent_class;
+
+  void        (* startup)                   (GtkApplicationImpl          *impl,
+                                             gboolean                     register_session);
+  void        (* shutdown)                  (GtkApplicationImpl          *impl);
+
+  void        (* before_emit)               (GtkApplicationImpl          *impl,
+                                             GVariant                    *platform_data);
+
+  void        (* window_added)              (GtkApplicationImpl          *impl,
+                                             GtkWindow                   *window);
+  void        (* window_removed)            (GtkApplicationImpl          *impl,
+                                             GtkWindow                   *window);
+  void        (* active_window_changed)     (GtkApplicationImpl          *impl,
+                                             GtkWindow                   *window);
+  void        (* handle_window_realize)     (GtkApplicationImpl          *impl,
+                                             GtkWindow                   *window);
+  void        (* handle_window_map)         (GtkApplicationImpl          *impl,
+                                             GtkWindow                   *window);
+
+  void        (* set_app_menu)              (GtkApplicationImpl          *impl,
+                                             GMenuModel                  *app_menu);
+  void        (* set_menubar)               (GtkApplicationImpl          *impl,
+                                             GMenuModel                  *menubar);
+
+  guint       (* inhibit)                   (GtkApplicationImpl          *impl,
+                                             GtkWindow                   *window,
+                                             GtkApplicationInhibitFlags   flags,
+                                             const gchar                 *reason);
+  void        (* uninhibit)                 (GtkApplicationImpl          *impl,
+                                             guint                        cookie);
+  gboolean    (* is_inhibited)              (GtkApplicationImpl          *impl,
+                                             GtkApplicationInhibitFlags   flags);
+
+
+} GtkApplicationImplClass;
+
+#define GTK_TYPE_APPLICATION_IMPL_DBUS                      (gtk_application_impl_dbus_get_type ())
+#define GTK_APPLICATION_IMPL_DBUS_CLASS(class)              (G_TYPE_CHECK_CLASS_CAST ((class),               
      \
+                                                             GTK_TYPE_APPLICATION_IMPL_DBUS,                 
      \
+                                                             GtkApplicationImplDBusClass))
+#define GTK_APPLICATION_IMPL_DBUS_GET_CLASS(obj)            (G_TYPE_INSTANCE_GET_CLASS ((obj),               
      \
+                                                             GTK_TYPE_APPLICATION_IMPL_DBUS,                 
      \
+                                                             GtkApplicationImplDBusClass))
+
+typedef struct
+{
+  GtkApplicationImpl impl;
+
+  GDBusConnection *session;
+
+  const gchar     *application_id;
+  const gchar     *unique_name;
+  const gchar     *object_path;
+
+  gchar           *app_menu_path;
+  guint            app_menu_id;
+
+  gchar           *menubar_path;
+  guint            menubar_id;
+
+  /* Session management... */
+  gchar           *app_id; /* actually prgname... */
+  GDBusProxy      *sm_proxy;
+  GDBusProxy      *client_proxy;
+  gchar           *client_path;
+} GtkApplicationImplDBus;
+
+typedef struct
+{
+  GtkApplicationImplClass parent_class;
+
+  /* returns floating */
+  GVariant *  (* get_window_system_id)      (GtkApplicationImplDBus      *dbus,
+                                             GtkWindow                   *window);
+} GtkApplicationImplDBusClass;
+
+G_GNUC_INTERNAL
+GType                   gtk_application_impl_get_type                   (void);
+G_GNUC_INTERNAL
+GType                   gtk_application_impl_dbus_get_type              (void);
+G_GNUC_INTERNAL
+GType                   gtk_application_impl_x11_get_type               (void);
+G_GNUC_INTERNAL
+GType                   gtk_application_impl_wayland_get_type           (void);
+G_GNUC_INTERNAL
+GType                   gtk_application_impl_quartz_get_type            (void);
+
+G_GNUC_INTERNAL
+GtkApplicationImpl *    gtk_application_impl_new                        (GtkApplication              
*application,
+                                                                         GdkDisplay                  
*display);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_startup                    (GtkApplicationImpl          *impl,
+                                                                         gboolean                     
register_sesion);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_shutdown                   (GtkApplicationImpl          *impl);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_before_emit                (GtkApplicationImpl          *impl,
+                                                                         GVariant                    
*platform_data);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_window_added               (GtkApplicationImpl          *impl,
+                                                                         GtkWindow                   
*window);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_window_removed             (GtkApplicationImpl          *impl,
+                                                                         GtkWindow                   
*window);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_active_window_changed      (GtkApplicationImpl          *impl,
+                                                                         GtkWindow                   
*window);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_handle_window_realize      (GtkApplicationImpl          *impl,
+                                                                         GtkWindow                   
*window);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_handle_window_map          (GtkApplicationImpl          *impl,
+                                                                         GtkWindow                   
*window);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_set_app_menu               (GtkApplicationImpl          *impl,
+                                                                         GMenuModel                  
*app_menu);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_set_menubar                (GtkApplicationImpl          *impl,
+                                                                         GMenuModel                  
*menubar);
+G_GNUC_INTERNAL
+guint                   gtk_application_impl_inhibit                    (GtkApplicationImpl          *impl,
+                                                                         GtkWindow                   *window,
+                                                                         GtkApplicationInhibitFlags   flags,
+                                                                         const gchar                 
*reason);
+G_GNUC_INTERNAL
+void                    gtk_application_impl_uninhibit                  (GtkApplicationImpl          *impl,
+                                                                         guint                        
cookie);
+G_GNUC_INTERNAL
+gboolean                gtk_application_impl_is_inhibited               (GtkApplicationImpl          *impl,
+                                                                         GtkApplicationInhibitFlags   flags);
+
+G_GNUC_INTERNAL
+gchar *                 gtk_application_impl_dbus_get_window_path       (GtkApplicationImplDBus      *dbus,
+                                                                         GtkWindow                   
*window);
 
 #endif /* __GTK_APPLICATION_PRIVATE_H__ */
diff --git a/gtk/gtkapplicationwindow.c b/gtk/gtkapplicationwindow.c
index 1527251..5cbe71e 100644
--- a/gtk/gtkapplicationwindow.c
+++ b/gtk/gtkapplicationwindow.c
@@ -29,14 +29,6 @@
 #include "gtkintl.h"
 #include "gtksettings.h"
 
-#include <gdk/gdk.h>
-#ifdef GDK_WINDOWING_X11
-#include <gdk/x11/gdkx.h>
-#endif
-#ifdef GDK_WINDOWING_WAYLAND
-#include <gdk/wayland/gdkwayland.h>
-#endif
-
 #ifdef HAVE_GIO_UNIX
 #include <gio/gdesktopappinfo.h>
 #endif
@@ -223,10 +215,6 @@ struct _GtkApplicationWindowPrivate
   GMenu *app_menu_section;
   GMenu *menubar_section;
 
-  GDBusConnection *session;
-  gchar           *object_path;
-  guint            export_id;
-
   guint            id;
 };
 
@@ -639,10 +627,8 @@ static void
 gtk_application_window_real_realize (GtkWidget *widget)
 {
   GtkApplicationWindow *window = GTK_APPLICATION_WINDOW (widget);
-  GtkApplication *application;
   GtkSettings *settings;
 
-  application = gtk_window_get_application (GTK_WINDOW (window));
   settings = gtk_widget_get_settings (widget);
 
   g_signal_connect (settings, "notify::gtk-shell-shows-app-menu",
@@ -655,35 +641,6 @@ gtk_application_window_real_realize (GtkWidget *widget)
   gtk_application_window_update_shell_shows_app_menu (window, settings);
   gtk_application_window_update_shell_shows_menubar (window, settings);
   gtk_application_window_update_menubar (window);
-
-#ifdef GDK_WINDOWING_X11
-  {
-    GdkWindow *gdkwindow;
-
-    gdkwindow = gtk_widget_get_window (GTK_WIDGET (window));
-
-    if (GDK_IS_X11_WINDOW (gdkwindow) && window->priv->session)
-      {
-        gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_APPLICATION_ID",
-                                          g_application_get_application_id (G_APPLICATION (application)));
-
-        gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_UNIQUE_BUS_NAME",
-                                          g_dbus_connection_get_unique_name (window->priv->session));
-
-        gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_APPLICATION_OBJECT_PATH",
-                                          g_application_get_dbus_object_path (G_APPLICATION (application)));
-
-        gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_WINDOW_OBJECT_PATH",
-                                          window->priv->object_path);
-
-        gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_APP_MENU_OBJECT_PATH",
-                                          gtk_application_get_app_menu_object_path (application));
-
-        gdk_x11_window_set_utf8_property (gdkwindow, "_GTK_MENUBAR_OBJECT_PATH",
-                                          gtk_application_get_menubar_object_path (application));
-      }
-  }
-#endif
 }
 
 static void
@@ -699,46 +656,10 @@ gtk_application_window_real_unrealize (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_application_window_parent_class)->unrealize (widget);
 }
 
-gboolean
-gtk_application_window_publish (GtkApplicationWindow *window,
-                                GDBusConnection      *session,
-                                const gchar          *object_path,
-                                guint                 object_id)
+GActionGroup *
+gtk_application_window_get_action_group (GtkApplicationWindow *window)
 {
-  g_assert (window->priv->session == NULL);
-  g_assert (window->priv->export_id == 0);
-  g_assert (window->priv->object_path == NULL);
-  g_assert (window->priv->id == 0);
-
-  window->priv->export_id = g_dbus_connection_export_action_group (session, object_path,
-                                                                   G_ACTION_GROUP (window->priv->actions),
-                                                                   NULL);
-
-  if (window->priv->export_id == 0)
-    return FALSE;
-
-  window->priv->session = session;
-  window->priv->object_path = g_strdup (object_path);
-  window->priv->id = object_id;
-
-  return TRUE;
-}
-
-void
-gtk_application_window_unpublish (GtkApplicationWindow *window)
-{
-  g_assert (window->priv->session != NULL);
-  g_assert (window->priv->export_id != 0);
-  g_assert (window->priv->object_path != NULL);
-  g_assert (window->priv->id != 0);
-
-  g_dbus_connection_unexport_action_group (window->priv->session, window->priv->export_id);
-  window->priv->session = NULL;
-  window->priv->export_id = 0;
-  window->priv->id = 0;
-
-  g_free (window->priv->object_path);
-  window->priv->object_path = NULL;
+  return G_ACTION_GROUP (window->priv->actions);
 }
 
 static void
@@ -751,27 +672,6 @@ gtk_application_window_real_map (GtkWidget *widget)
     gtk_widget_map (window->priv->menubar);
 
   GTK_WIDGET_CLASS (gtk_application_window_parent_class)->map (widget);
-
-#ifdef GDK_WINDOWING_WAYLAND
-  {
-    GdkWindow *gdkwindow;
-    GtkApplication *application;
-
-    application = gtk_window_get_application (GTK_WINDOW (window));
-    gdkwindow = gtk_widget_get_window (widget);
-
-    if (GDK_IS_WAYLAND_WINDOW (gdkwindow) && window->priv->session)
-      {
-       gdk_wayland_window_set_dbus_properties_libgtk_only (gdkwindow,
-                                                           g_application_get_application_id (G_APPLICATION 
(application)),
-                                                           gtk_application_get_app_menu_object_path 
(application),
-                                                           gtk_application_get_menubar_object_path 
(application),
-                                                           window->priv->object_path,
-                                                           g_application_get_dbus_object_path (G_APPLICATION 
(application)),
-                                                           g_dbus_connection_get_unique_name 
(window->priv->session));
-      }
-  }
-#endif
 }
 
 static void
@@ -994,3 +894,11 @@ gtk_application_window_get_id (GtkApplicationWindow *window)
 
   return window->priv->id;
 }
+
+void
+gtk_application_window_set_id (GtkApplicationWindow *window,
+                               guint                 id)
+{
+  g_return_if_fail (GTK_IS_APPLICATION_WINDOW (window));
+  window->priv->id = id;
+}
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 5c6d018..c6e6649 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -5451,6 +5451,9 @@ gtk_window_map (GtkWidget *widget)
     gtk_window_set_focus_visible (window, gtk_window_get_focus_visible (priv->transient_parent));
   else
     gtk_window_set_focus_visible (window, FALSE);
+
+  if (priv->application)
+    gtk_application_handle_window_map (priv->application, window);
 }
 
 static gboolean
@@ -5855,6 +5858,9 @@ gtk_window_realize (GtkWidget *widget)
     }
 #endif
 
+  if (priv->application)
+    gtk_application_handle_window_realize (priv->application, window);
+
   /* Icons */
   gtk_window_realize_icon (window);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]