Use org.gtk.Application proxy to monitor app's busy state

This simplifies the code and fixes a race where an application could
call g_application_mark_busy() before the shell subscribed to change
notifications on the application's busy state.

https://bugzilla.gnome.org/show_bug.cgi?id=736492
This commit is contained in:
Phillip Wood 2014-09-16 11:05:52 +01:00
parent 943f6c9d28
commit e00bfcc2cf

View File

@ -16,6 +16,7 @@
#include "shell-window-tracker-private.h" #include "shell-window-tracker-private.h"
#include "st.h" #include "st.h"
#include "gtkactionmuxer.h" #include "gtkactionmuxer.h"
#include "org-gtk-application.h"
#ifdef HAVE_SYSTEMD #ifdef HAVE_SYSTEMD
#include <systemd/sd-journal.h> #include <systemd/sd-journal.h>
@ -47,14 +48,16 @@ typedef struct {
/* Whether or not we need to resort the windows; this is done on demand */ /* Whether or not we need to resort the windows; this is done on demand */
guint window_sort_stale : 1; guint window_sort_stale : 1;
/* DBus property notification subscription */
guint properties_changed_id : 1;
/* See GApplication documentation */ /* See GApplication documentation */
GDBusMenuModel *remote_menu; GDBusMenuModel *remote_menu;
GtkActionMuxer *muxer; GtkActionMuxer *muxer;
char *unique_bus_name; char *unique_bus_name;
GDBusConnection *session; GDBusConnection *session;
/* GDBus Proxy for getting application busy state */
ShellOrgGtkApplication *application_proxy;
GCancellable *cancellable;
} ShellAppRunningState; } ShellAppRunningState;
/** /**
@ -1058,37 +1061,49 @@ shell_app_on_ws_switch (MetaScreen *screen,
} }
static void static void
application_properties_changed (GDBusConnection *connection, busy_changed_cb (GObject *object,
const gchar *sender_name, GParamSpec *pspec,
const gchar *object_path, gpointer user_data)
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{ {
ShellOrgGtkApplication *proxy;
ShellApp *app = user_data; ShellApp *app = user_data;
GVariant *changed_properties;
gboolean busy = FALSE;
const gchar *interface_name_for_signal;
g_variant_get (parameters, g_assert (SHELL_IS_ORG_GTK_APPLICATION (object));
"(&s@a{sv}as)", g_assert (SHELL_IS_APP (app));
&interface_name_for_signal,
&changed_properties,
NULL);
if (g_strcmp0 (interface_name_for_signal, "org.gtk.Application") != 0) proxy = SHELL_ORG_GTK_APPLICATION (object);
return; if (shell_org_gtk_application_get_busy (proxy))
g_variant_lookup (changed_properties, "Busy", "b", &busy);
if (busy)
shell_app_state_transition (app, SHELL_APP_STATE_BUSY); shell_app_state_transition (app, SHELL_APP_STATE_BUSY);
else else
shell_app_state_transition (app, SHELL_APP_STATE_RUNNING); shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
}
if (changed_properties != NULL) static void
g_variant_unref (changed_properties); get_application_proxy (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
ShellApp *app = user_data;
ShellOrgGtkApplication *proxy;
g_assert (SHELL_IS_APP (app));
proxy = shell_org_gtk_application_proxy_new_finish (result, NULL);
if (proxy != NULL)
{
app->running_state->application_proxy = proxy;
g_signal_connect (proxy,
"notify::busy",
G_CALLBACK (busy_changed_cb),
app);
if (shell_org_gtk_application_get_busy (proxy))
shell_app_state_transition (app, SHELL_APP_STATE_BUSY);
}
if (app->running_state != NULL)
g_clear_object (&app->running_state->cancellable);
g_object_unref (app);
} }
static void static void
@ -1098,7 +1113,8 @@ shell_app_ensure_busy_watch (ShellApp *app)
MetaWindow *window; MetaWindow *window;
const gchar *object_path; const gchar *object_path;
if (running_state->properties_changed_id != 0) if (running_state->application_proxy != NULL ||
running_state->cancellable != NULL)
return; return;
if (running_state->unique_bus_name == NULL) if (running_state->unique_bus_name == NULL)
@ -1110,15 +1126,16 @@ shell_app_ensure_busy_watch (ShellApp *app)
if (object_path == NULL) if (object_path == NULL)
return; return;
running_state->properties_changed_id = running_state->cancellable = g_cancellable_new();
g_dbus_connection_signal_subscribe (running_state->session, /* Take a reference to app to make sure it isn't finalized before
running_state->unique_bus_name, get_application_proxy runs */
"org.freedesktop.DBus.Properties", shell_org_gtk_application_proxy_new (running_state->session,
"PropertiesChanged", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
object_path, running_state->unique_bus_name,
"org.gtk.Application", object_path,
G_DBUS_SIGNAL_FLAGS_NONE, running_state->cancellable,
application_properties_changed, app, NULL); get_application_proxy,
g_object_ref (app));
} }
void void
@ -1461,8 +1478,13 @@ unref_running_state (ShellAppRunningState *state)
screen = shell_global_get_screen (shell_global_get ()); screen = shell_global_get_screen (shell_global_get ());
g_signal_handler_disconnect (screen, state->workspace_switch_id); g_signal_handler_disconnect (screen, state->workspace_switch_id);
if (state->properties_changed_id != 0) g_clear_object (&state->application_proxy);
g_dbus_connection_signal_unsubscribe (state->session, state->properties_changed_id);
if (state->cancellable != NULL)
{
g_cancellable_cancel (state->cancellable);
g_clear_object (&state->cancellable);
}
g_clear_object (&state->remote_menu); g_clear_object (&state->remote_menu);
g_clear_object (&state->muxer); g_clear_object (&state->muxer);