diff --git a/src/shell-app.c b/src/shell-app.c index f4abc5f18..42d11654e 100644 --- a/src/shell-app.c +++ b/src/shell-app.c @@ -39,10 +39,11 @@ typedef struct { gboolean window_sort_stale : 1; /* See GApplication documentation */ - guint name_watcher_id; + gint name_watcher_id; gchar *dbus_name; + GDBusProxy *app_proxy; GDBusActionGroup *remote_actions; - GMenuProxy *remote_menu; + GMenuModel *remote_menu; GCancellable *dbus_cancellable; } ShellAppRunningState; @@ -1029,7 +1030,7 @@ on_action_group_acquired (GObject *object, ShellApp *self = SHELL_APP (user_data); ShellAppRunningState *state = self->running_state; GError *error = NULL; - char *object_path; + GVariant *menu_property; state->remote_actions = g_dbus_action_group_new_finish (result, &error); @@ -1042,6 +1043,65 @@ on_action_group_acquired (GObject *object, g_warning ("Unexpected error while reading application actions: %s", error->message); } + g_clear_error (&error); + g_clear_object (&state->dbus_cancellable); + g_clear_object (&state->app_proxy); + + if (state->name_watcher_id) + { + g_bus_unwatch_name (state->name_watcher_id); + state->name_watcher_id = 0; + } + + g_free (state->dbus_name); + state->dbus_name = NULL; + + g_object_unref (self); + return; + } + + g_object_notify (G_OBJECT (self), "action-group"); + + /* third step: the application menu */ + + menu_property = g_dbus_proxy_get_cached_property (state->app_proxy, "AppMenu"); + + if (menu_property && g_variant_n_children (menu_property) > 0) + { + const gchar *object_path; + + g_variant_get_child (menu_property, 0, "&o", &object_path); + + state->remote_menu = G_MENU_MODEL (g_dbus_menu_model_get (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL), + state->dbus_name, + object_path)); + + g_object_notify (G_OBJECT (self), "menu"); + } + + g_object_unref (self); +} + +static void +on_dbus_proxy_gotten (GObject *initable, + GAsyncResult *result, + gpointer user_data) +{ + ShellApp *self = SHELL_APP (user_data); + ShellAppRunningState *state = self->running_state; + GError *error = NULL; + + state->app_proxy = g_dbus_proxy_new_finish (result, + &error); + + if (error) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) + { + g_warning ("Unexpected error while creating application proxy: %s", error->message); + } + g_clear_error (&error); g_clear_object (&state->dbus_cancellable); @@ -1058,19 +1118,15 @@ on_action_group_acquired (GObject *object, return; } - object_path = g_strconcat ("/", state->dbus_name, NULL); - g_strdelimit (object_path, ".", '/'); + /* on to the second step, the primary action group */ - state->remote_menu = g_menu_proxy_get (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL), - state->dbus_name, - object_path); - - g_object_notify (G_OBJECT (self), "dbus-id"); - g_object_notify (G_OBJECT (self), "action-group"); - g_object_notify (G_OBJECT (self), "menu"); - - g_object_unref (self); - g_free (object_path); + g_dbus_action_group_new (g_dbus_proxy_get_connection (state->app_proxy), + g_dbus_proxy_get_name (state->app_proxy), + g_dbus_proxy_get_object_path (state->app_proxy), + G_DBUS_ACTION_GROUP_FLAGS_NONE, + state->dbus_cancellable, + on_action_group_acquired, + self); } static void @@ -1091,13 +1147,19 @@ on_dbus_name_appeared (GDBusConnection *bus, if (!state->dbus_cancellable) state->dbus_cancellable = g_cancellable_new (); - g_dbus_action_group_new (bus, - name, - object_path, - G_DBUS_ACTION_GROUP_FLAGS_NONE, - state->dbus_cancellable, - on_action_group_acquired, - g_object_ref (self)); + /* first step: the application proxy */ + + g_dbus_proxy_new (bus, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* interface info */ + name_owner, + object_path, + "org.gtk.Application", + state->dbus_cancellable, + on_dbus_proxy_gotten, + g_object_ref (self)); + + g_object_notify (G_OBJECT (self), "dbus-id"); g_free (object_path); } @@ -1118,6 +1180,7 @@ on_dbus_name_disappeared (GDBusConnection *bus, g_clear_object (&state->dbus_cancellable); } + g_clear_object (&state->app_proxy); g_clear_object (&state->remote_actions); g_clear_object (&state->remote_menu); @@ -1362,6 +1425,7 @@ unref_running_state (ShellAppRunningState *state) g_object_unref (state->dbus_cancellable); } + g_clear_object (&state->app_proxy); g_clear_object (&state->remote_actions); g_clear_object (&state->remote_menu); g_free (state->dbus_name); @@ -1650,7 +1714,7 @@ shell_app_class_init(ShellAppClass *klass) g_param_spec_object ("menu", "Application Menu", "The primary menu exported by the remote application", - G_TYPE_MENU_PROXY, + G_TYPE_MENU_MODEL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } diff --git a/src/test-gapplication.js b/src/test-gapplication.js index 7d101032d..dfae2abe9 100755 --- a/src/test-gapplication.js +++ b/src/test-gapplication.js @@ -19,7 +19,7 @@ function do_action_toggle(action) { } function do_action_state_change(action) { - print ("Action '" + action.name + "' has now state '" + action.state.deep_unpack() + "'"); + print ("Action '" + action.name + "' has now state " + action.state.print(true)); } function main() { @@ -57,17 +57,17 @@ function main() { app.add_action(action); let menu = new Gio.Menu(); - menu.append('An action', 'one'); + menu.append('An action', 'app.one'); let section = new Gio.Menu(); - section.append('Another action', 'two'); - section.append('Same as above', 'two'); + section.append('Another action', 'app.two'); + section.append('Same as above', 'app.two'); menu.append_section(null, section); // another section, to check separators section = new Gio.Menu(); - section.append('Checkbox', 'toggle'); - section.append('Disabled', 'disable'); + section.append('Checkbox', 'app.toggle'); + section.append('Disabled', 'app.disable'); menu.append_section(null, section); // empty sections or submenus should be invisible @@ -75,25 +75,26 @@ function main() { menu.append_submenu('Empty submenu', new Gio.Menu()); let submenu = new Gio.Menu(); - submenu.append('Open c:\\', 'parameter-string::c:\\'); - submenu.append('Open /home', 'parameter-string::/home'); + submenu.append('Open c:\\', 'app.parameter-string::c:\\'); + submenu.append('Open /home', 'app.parameter-string::/home'); menu.append_submenu('Recent files', submenu); let item = Gio.MenuItem.new('Say 42', null); - item.set_action_and_target_value('parameter-int', GLib.Variant.new('u', 42)); + item.set_action_and_target_value('app.parameter-int', GLib.Variant.new('u', 42)); menu.append_item(item); let item = Gio.MenuItem.new('Say 43', null); - item.set_action_and_target_value('parameter-int', GLib.Variant.new('u', 43)); + item.set_action_and_target_value('app.parameter-int', GLib.Variant.new('u', 43)); menu.append_item(item); app.set_app_menu(menu); let window = null; + app.connect_after('startup', function(app) { + window = new Gtk.ApplicationWindow({ title: "Test Application", application: app }); + }); app.connect('activate', function(app) { - if (!window) - window = new Gtk.Window({ title: "Test Application", application: app }); window.present(); });