From a197977094bed8a424b0e5573f492d0c5ba96fb8 Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Mon, 4 Mar 2013 18:05:09 -0500 Subject: Mpris2Watcher: use match rules to avoid unnecessary wakeups Previously, indicator-sound-service was waking up every time the owner of any name on the bus changed. --- src/mpris2-watcher.vala | 75 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/mpris2-watcher.vala b/src/mpris2-watcher.vala index 06ccb6e..cdfb9c6 100644 --- a/src/mpris2-watcher.vala +++ b/src/mpris2-watcher.vala @@ -21,8 +21,8 @@ using Xml; public class Mpris2Watcher : GLib.Object { - FreeDesktopObject fdesktop_obj; - + DBusConnection session_bus; + public signal void client_appeared ( string desktop_file_name, string dbus_name, bool use_playlists ); @@ -33,18 +33,37 @@ public class Mpris2Watcher : GLib.Object } construct - { + { + const string match_rule = "type='signal'," + + "sender='org.freedesktop.DBus'," + + "interface='org.freedesktop.DBus'," + + "member='NameOwnerChanged'," + + "path='/org/freedesktop/DBus'," + + "arg0namespace='org.mpris.MediaPlayer2'"; try { - this.fdesktop_obj = Bus.get_proxy_sync ( BusType.SESSION, - FREEDESKTOP_SERVICE, - FREEDESKTOP_OBJECT, - DBusProxyFlags.DO_NOT_LOAD_PROPERTIES ); - this.fdesktop_obj.name_owner_changed.connect (this.name_changes_detected); + this.session_bus = Bus.get_sync (BusType.SESSION); + + this.session_bus.call_sync ("org.freedesktop.DBus", + "/", + "org.freedesktop.DBus", + "AddMatch", + new Variant ("(s)", match_rule), + VariantType.TUPLE, + DBusCallFlags.NONE, + -1); + + this.session_bus.signal_subscribe ("org.freedesktop.DBus", + "org.freedesktop.DBus", + "NameOwnerChanged", + "/org/freedesktop/DBus", + null, + DBusSignalFlags.NO_MATCH_RULE, + this.name_owner_changed); + this.check_for_active_clients.begin(); } - catch ( IOError e ){ - warning( "Mpris2watcher could not set up a watch for mpris clients appearing on the bus: %s", - e.message ); + catch (GLib.Error e) { + warning ("unable to set up name watch for mrpis clients: %s", e.message); } } @@ -52,16 +71,25 @@ public class Mpris2Watcher : GLib.Object // More relevant for development and daemon's like mpd. public async void check_for_active_clients() { - string[] interfaces; - try{ - interfaces = yield this.fdesktop_obj.list_names(); + Variant interfaces; + + try { + interfaces = yield this.session_bus.call ("org.freedesktop.DBus", + "/", + "org.freedesktop.DBus", + "ListNames", + null, + new VariantType ("(as)"), + DBusCallFlags.NONE, + -1); } - catch ( IOError e) { - warning( "Mpris2watcher could fetch active interfaces at startup: %s", - e.message ); + catch (GLib.Error e) { + warning ("unable to search for existing mpris clients: %s ", e.message); return; } - foreach (var address in interfaces) { + + foreach (var val in interfaces.get_child_value (0)) { + var address = (string) val; if (address.has_prefix (MPRIS_PREFIX)){ MprisRoot? mpris2_root = this.create_mpris_root(address); if (mpris2_root == null) return; @@ -71,13 +99,14 @@ public class Mpris2Watcher : GLib.Object } } - private void name_changes_detected ( FreeDesktopObject dbus_obj, - string name, - string previous_owner, - string current_owner ) + public void name_owner_changed (DBusConnection con, string sender, string object_path, + string interface_name, string signal_name, Variant parameters) { - MprisRoot? mpris2_root = this.create_mpris_root(name); + string name, previous_owner, current_owner; + + parameters.get ("(sss)", out name, out previous_owner, out current_owner); + MprisRoot? mpris2_root = this.create_mpris_root (name); if (mpris2_root == null) return; if (previous_owner != "" && current_owner == "") { -- cgit v1.2.3 From eacbe640fa7ffb8a99a04ebf21f94982c3679a8c Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Sat, 30 Mar 2013 19:25:21 +0100 Subject: MenuItem: use GtkLaunchContext with proper event timeout to launch applications Also discards non-activation events --- src/Makefile.am | 1 + src/metadata-menu-item.vala | 9 ++++++--- src/mpris2-controller.vala | 2 +- src/player-controller.vala | 7 +++++-- src/sound-service.c | 2 +- src/transport-menu-item.vala | 5 ++++- 6 files changed, 18 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 5a36822..0a67a7c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -81,6 +81,7 @@ music_bridge_VALAFLAGS = \ --pkg config \ --pkg gio-2.0 \ --pkg gio-unix-2.0 \ + --pkg gdk-3.0 \ --pkg gdk-pixbuf-2.0 \ --pkg libxml-2.0 diff --git a/src/metadata-menu-item.vala b/src/metadata-menu-item.vala index b2dd752..bcd4378 100644 --- a/src/metadata-menu-item.vala +++ b/src/metadata-menu-item.vala @@ -168,13 +168,16 @@ public class MetadataMenuitem : PlayerItem public override void handle_event (string name, Variant input_value, uint timestamp) - { + { + if (name != Dbusmenu.MENUITEM_EVENT_ACTIVATED) + return; + if(this.owner.current_state == PlayerController.state.OFFLINE) { - this.owner.instantiate(); + this.owner.instantiate(timestamp); } else if(this.owner.current_state == PlayerController.state.CONNECTED){ - this.owner.mpris_bridge.expose(); + this.owner.mpris_bridge.expose(timestamp); } } diff --git a/src/mpris2-controller.vala b/src/mpris2-controller.vala index 2cc8d12..5015f80 100644 --- a/src/mpris2-controller.vala +++ b/src/mpris2-controller.vala @@ -208,7 +208,7 @@ public class Mpris2Controller : GLib.Object return (this.player != null && this.mpris2_root != null); } - public void expose() + public void expose(uint timestamp) { if(this.connected() == true){ this.mpris2_root.Raise.begin(); diff --git a/src/player-controller.vala b/src/player-controller.vala index fd66a8a..764efa5 100644 --- a/src/player-controller.vala +++ b/src/player-controller.vala @@ -98,11 +98,14 @@ public class PlayerController : GLib.Object There is a need to wait before the application is on DBus before attempting to access its mpris address Hence only when the it has registered with us via libindicate do we attempt to kick off mpris communication */ - public void instantiate() + public void instantiate(uint timestamp) { debug("instantiate in player controller for %s", this.app_info.get_name() ); + try{ - this.app_info.launch(null, null); + var context = Gdk.Display.get_default().get_app_launch_context(); + context.set_timestamp(timestamp); + this.app_info.launch(null, context); this.update_state(state.INSTANTIATING); } catch(GLib.Error error){ diff --git a/src/sound-service.c b/src/sound-service.c index 0170f81..66ef7b0 100644 --- a/src/sound-service.c +++ b/src/sound-service.c @@ -88,7 +88,7 @@ main (int argc, char ** argv) { gboolean greeter_mode; - g_type_init(); + gdk_init(&argc, &argv); textdomain (GETTEXT_PACKAGE); bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); setlocale (LC_ALL, ""); diff --git a/src/transport-menu-item.vala b/src/transport-menu-item.vala index 4ec7dad..0af3118 100644 --- a/src/transport-menu-item.vala +++ b/src/transport-menu-item.vala @@ -70,6 +70,9 @@ public class TransportMenuitem : PlayerItem Variant input_value, uint timestamp) { + if (name != Dbusmenu.MENUITEM_EVENT_ACTIVATED) + return; + Variant v = input_value; if ( input_value.is_of_type (VariantType.VARIANT)){ v = input_value.get_variant(); @@ -82,7 +85,7 @@ public class TransportMenuitem : PlayerItem } else{ this.cached_action = (Transport.Action)input; - this.owner.instantiate(); + this.owner.instantiate(timestamp); this.property_set_int (MENUITEM_PLAY_STATE, (int)Transport.State.LAUNCHING); } } -- cgit v1.2.3 From fab924562a7db79a96ec367d28601fd6433e9c51 Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Sat, 30 Mar 2013 20:13:53 +0100 Subject: GtkApplicationPlayer: add a class to handle the GtkApplication players It allows to check if the given player implements the "org.gtk.Application" interface and if it's the case, it Activate the application with the proper timestamp when requested. --- src/Makefile.am | 3 +- src/gtk-application-player.vala | 123 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/gtk-application-player.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 0a67a7c..381da56 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -67,7 +67,8 @@ music_bridge_VALASOURCES = \ settings-manager.vala \ playlists-menu-item.vala \ freedesktop-interfaces.vala \ - fetch-file.vala + fetch-file.vala \ + gtk-application-player.vala music_bridge_VALAFLAGS = \ --ccode \ diff --git a/src/gtk-application-player.vala b/src/gtk-application-player.vala new file mode 100644 index 0000000..8422ca5 --- /dev/null +++ b/src/gtk-application-player.vala @@ -0,0 +1,123 @@ +/* +Copyright 2013 Canonical Ltd. + +Authors: + Marco Trevisan + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License version 3, as published +by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see . +*/ + +[DBus (name = "org.gtk.Application")] +public interface DBusGtkApplication : Object { + public abstract void Activate(GLib.HashTable platform_data) throws IOError; +} + +public class GtkApplicationPlayer : GLib.Object +{ + public PlayerController owner {get; construct;} + + private bool gtk_application_searched = false; + private DBusGtkApplication gtk_application; + + public GtkApplicationPlayer(PlayerController ctrl) + { + GLib.Object(owner: ctrl); + } + + public void activate(uint timestamp) + { + this.setup_gtk_application(); + + if (this.gtk_application == null) { + return; + } + + var context = Gdk.Display.get_default().get_app_launch_context(); + context.set_timestamp(timestamp); + + var data = new GLib.HashTable(str_hash, str_equal); + data["desktop-startup-id"] = context.get_startup_notify_id(this.owner.app_info, new GLib.List()); + + try { + this.gtk_application.Activate(data); + } + catch (IOError e) {} + } + + private void setup_gtk_application() + { + if (owner.current_state != PlayerController.state.CONNECTED) + return; + + if (this.gtk_application != null || this.gtk_application_searched) + return; + + try { + var connection = Bus.get_sync(BusType.SESSION); + var name = this.owner.dbus_name; + string gtk_application_path; + this.find_iface_path(connection, name, "/", "org.gtk.Application", out gtk_application_path); + this.gtk_application_searched = true; + + if (gtk_application_path != null) { + this.gtk_application = Bus.get_proxy_sync(BusType.SESSION, this.owner.dbus_name, gtk_application_path); + } + } catch (Error e) { + return; + } + } + + private void find_iface_path(DBusConnection connection, string name, string path, string target_iface, out string found_path) + { + found_path = null; + DBusNodeInfo node = null; + + try { + unowned string xml_string; + var xml = connection.call_sync(name, path, "org.freedesktop.DBus.Introspectable", "Introspect", null, new VariantType("(s)"), DBusCallFlags.NONE, 1000); + xml.get("(&s)", out xml_string); + node = new DBusNodeInfo.for_xml(xml_string); + } catch (Error e) { + return; + } + + if (node == null) { + return; + } + + foreach (var iface in node.interfaces) { + if (iface.name == target_iface) { + found_path = path; + return; + } + } + + bool is_root = (path == "/"); + + foreach (var subnode in node.nodes) { + string new_path = path; + + if (!is_root) { + new_path += "/"; + } + + new_path += subnode.path; + + find_iface_path(connection, name, new_path, target_iface, out found_path); + + if (found_path != null) { + return; + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From e6779eca9277231ee59c6441bf6982599a0d611e Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Sat, 30 Mar 2013 20:15:23 +0100 Subject: PlayerController use GtkApplicationPlayer and activate it when we need to raise --- src/metadata-menu-item.vala | 3 ++- src/mpris2-controller.vala | 4 ++-- src/player-controller.vala | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/metadata-menu-item.vala b/src/metadata-menu-item.vala index bcd4378..a81c143 100644 --- a/src/metadata-menu-item.vala +++ b/src/metadata-menu-item.vala @@ -176,7 +176,8 @@ public class MetadataMenuitem : PlayerItem { this.owner.instantiate(timestamp); } - else if(this.owner.current_state == PlayerController.state.CONNECTED){ + else if (this.owner.current_state == PlayerController.state.CONNECTED) { + this.owner.gtk_app_player.activate(timestamp); this.owner.mpris_bridge.expose(timestamp); } } diff --git a/src/mpris2-controller.vala b/src/mpris2-controller.vala index 5015f80..9230a59 100644 --- a/src/mpris2-controller.vala +++ b/src/mpris2-controller.vala @@ -208,9 +208,9 @@ public class Mpris2Controller : GLib.Object return (this.player != null && this.mpris2_root != null); } - public void expose(uint timestamp) + public void expose(uint timestmap) { - if(this.connected() == true){ + if (this.connected() == true) { this.mpris2_root.Raise.begin(); } } diff --git a/src/player-controller.vala b/src/player-controller.vala index 764efa5..dbe7c3c 100644 --- a/src/player-controller.vala +++ b/src/player-controller.vala @@ -45,6 +45,7 @@ public class PlayerController : GLib.Object public string dbus_name { get; set;} public ArrayList custom_items; public Mpris2Controller mpris_bridge; + public GtkApplicationPlayer gtk_app_player; public AppInfo? app_info { get; set;} public int menu_offset { get; set;} public string icon_name { get; set; } @@ -149,6 +150,7 @@ public class PlayerController : GLib.Object debug ( " establish mpris connection - use playlists value = %s ", this.use_playlists.to_string() ); this.mpris_bridge = new Mpris2Controller (this); + this.gtk_app_player = new GtkApplicationPlayer (this); this.determine_state (); } -- cgit v1.2.3 From ba80753bb0a79f052f007c6e614732afac88e3ad Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Tue, 2 Apr 2013 17:54:12 +0200 Subject: PlayerActivator: Use BAMF to find the windows to activate with timestamp Improved the"old" GtkApplicationPlayer, using BAMF as a fallback method to activate an application's windows. Basically we try to get the windows of the selected application and when found we focus them using the activation timestamp. --- configure.ac | 2 + debian/control | 1 + src/Makefile.am | 4 +- src/gtk-application-player.vala | 123 ------------------------- src/metadata-menu-item.vala | 2 +- src/player-controller.vala | 4 +- src/player-windows-activator.vala | 185 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 194 insertions(+), 127 deletions(-) delete mode 100644 src/gtk-application-player.vala create mode 100644 src/player-windows-activator.vala (limited to 'src') diff --git a/configure.ac b/configure.ac index 7d8c008..3a72c5c 100644 --- a/configure.ac +++ b/configure.ac @@ -52,7 +52,9 @@ PKG_CHECK_MODULES(APPLET, gtk+-3.0 >= $GTK_REQUIRED_VERSION PKG_CHECK_MODULES(SOUNDSERVICE, dbusmenu-glib-0.4 >= $DBUSMENUGLIB_REQUIRED_VERSION indicator3-0.4 gee-1.0 + gdk-x11-3.0 gio-unix-2.0 + libbamf3 libxml-2.0) AC_SUBST(APPLET_CFLAGS) diff --git a/debian/control b/debian/control index 320f8b7..abd1f75 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,7 @@ Build-Depends: debhelper (>= 9.0), gnome-common, autotools-dev, valac-0.18, + libbamf3-dev, libglib2.0-dev (>= 2.22.3), libgtk-3-dev, libdbusmenu-glib-dev (>= 0.5.90), diff --git a/src/Makefile.am b/src/Makefile.am index 381da56..9807477 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,7 +68,7 @@ music_bridge_VALASOURCES = \ playlists-menu-item.vala \ freedesktop-interfaces.vala \ fetch-file.vala \ - gtk-application-player.vala + player-windows-activator.vala music_bridge_VALAFLAGS = \ --ccode \ @@ -83,7 +83,9 @@ music_bridge_VALAFLAGS = \ --pkg gio-2.0 \ --pkg gio-unix-2.0 \ --pkg gdk-3.0 \ + --pkg gdk-x11-3.0 \ --pkg gdk-pixbuf-2.0 \ + --pkg libbamf3 \ --pkg libxml-2.0 $(MAINTAINER_VALAFLAGS) diff --git a/src/gtk-application-player.vala b/src/gtk-application-player.vala deleted file mode 100644 index 8422ca5..0000000 --- a/src/gtk-application-player.vala +++ /dev/null @@ -1,123 +0,0 @@ -/* -Copyright 2013 Canonical Ltd. - -Authors: - Marco Trevisan - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ - -[DBus (name = "org.gtk.Application")] -public interface DBusGtkApplication : Object { - public abstract void Activate(GLib.HashTable platform_data) throws IOError; -} - -public class GtkApplicationPlayer : GLib.Object -{ - public PlayerController owner {get; construct;} - - private bool gtk_application_searched = false; - private DBusGtkApplication gtk_application; - - public GtkApplicationPlayer(PlayerController ctrl) - { - GLib.Object(owner: ctrl); - } - - public void activate(uint timestamp) - { - this.setup_gtk_application(); - - if (this.gtk_application == null) { - return; - } - - var context = Gdk.Display.get_default().get_app_launch_context(); - context.set_timestamp(timestamp); - - var data = new GLib.HashTable(str_hash, str_equal); - data["desktop-startup-id"] = context.get_startup_notify_id(this.owner.app_info, new GLib.List()); - - try { - this.gtk_application.Activate(data); - } - catch (IOError e) {} - } - - private void setup_gtk_application() - { - if (owner.current_state != PlayerController.state.CONNECTED) - return; - - if (this.gtk_application != null || this.gtk_application_searched) - return; - - try { - var connection = Bus.get_sync(BusType.SESSION); - var name = this.owner.dbus_name; - string gtk_application_path; - this.find_iface_path(connection, name, "/", "org.gtk.Application", out gtk_application_path); - this.gtk_application_searched = true; - - if (gtk_application_path != null) { - this.gtk_application = Bus.get_proxy_sync(BusType.SESSION, this.owner.dbus_name, gtk_application_path); - } - } catch (Error e) { - return; - } - } - - private void find_iface_path(DBusConnection connection, string name, string path, string target_iface, out string found_path) - { - found_path = null; - DBusNodeInfo node = null; - - try { - unowned string xml_string; - var xml = connection.call_sync(name, path, "org.freedesktop.DBus.Introspectable", "Introspect", null, new VariantType("(s)"), DBusCallFlags.NONE, 1000); - xml.get("(&s)", out xml_string); - node = new DBusNodeInfo.for_xml(xml_string); - } catch (Error e) { - return; - } - - if (node == null) { - return; - } - - foreach (var iface in node.interfaces) { - if (iface.name == target_iface) { - found_path = path; - return; - } - } - - bool is_root = (path == "/"); - - foreach (var subnode in node.nodes) { - string new_path = path; - - if (!is_root) { - new_path += "/"; - } - - new_path += subnode.path; - - find_iface_path(connection, name, new_path, target_iface, out found_path); - - if (found_path != null) { - return; - } - } - } -} \ No newline at end of file diff --git a/src/metadata-menu-item.vala b/src/metadata-menu-item.vala index a81c143..f4a7e68 100644 --- a/src/metadata-menu-item.vala +++ b/src/metadata-menu-item.vala @@ -177,7 +177,7 @@ public class MetadataMenuitem : PlayerItem this.owner.instantiate(timestamp); } else if (this.owner.current_state == PlayerController.state.CONNECTED) { - this.owner.gtk_app_player.activate(timestamp); + this.owner.player_activator.activate(timestamp); this.owner.mpris_bridge.expose(timestamp); } } diff --git a/src/player-controller.vala b/src/player-controller.vala index dbe7c3c..8c3339e 100644 --- a/src/player-controller.vala +++ b/src/player-controller.vala @@ -45,7 +45,7 @@ public class PlayerController : GLib.Object public string dbus_name { get; set;} public ArrayList custom_items; public Mpris2Controller mpris_bridge; - public GtkApplicationPlayer gtk_app_player; + public PlayerActivator player_activator; public AppInfo? app_info { get; set;} public int menu_offset { get; set;} public string icon_name { get; set; } @@ -150,7 +150,7 @@ public class PlayerController : GLib.Object debug ( " establish mpris connection - use playlists value = %s ", this.use_playlists.to_string() ); this.mpris_bridge = new Mpris2Controller (this); - this.gtk_app_player = new GtkApplicationPlayer (this); + this.player_activator = new PlayerActivator (this); this.determine_state (); } diff --git a/src/player-windows-activator.vala b/src/player-windows-activator.vala new file mode 100644 index 0000000..e8cd616 --- /dev/null +++ b/src/player-windows-activator.vala @@ -0,0 +1,185 @@ +/* +Copyright 2013 Canonical Ltd. + +Authors: + Marco Trevisan + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License version 3, as published +by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see . +*/ + +[DBus (name = "org.gtk.Application")] +public interface DBusGtkApplication : Object { + public abstract void Activate(GLib.HashTable platform_data) throws IOError; +} + +public class PlayerActivator : GLib.Object +{ + public PlayerController owner {get; construct;} + + private bool gtk_application_searched = false; + private DBusGtkApplication gtk_application; + private Bamf.Application bamf_application; + + private const uint MAX_BAMF_APPLICATION_WAIT_MS = 1000; + private int64 last_check_time; + + public PlayerActivator(PlayerController ctrl) + { + GLib.Object(owner: ctrl); + } + + public void activate(uint timestamp) + { + if (!activate_gtk_appplication(timestamp)) { + if (!activate_bamf_appplication(timestamp)) { + // Let's wait BAMF to update its windows list + this.last_check_time = get_monotonic_time(); + + Idle.add(() => { + bool activated = activate_bamf_appplication(timestamp); + int64 waited = (get_monotonic_time() - this.last_check_time) / 1000; + return !activated && waited < MAX_BAMF_APPLICATION_WAIT_MS; + }); + } + } + } + + private bool activate_gtk_appplication(uint timestamp) + { + this.setup_gtk_application(); + + if (this.gtk_application == null) { + return false; + } + + var context = Gdk.Display.get_default().get_app_launch_context(); + context.set_timestamp(timestamp); + + var data = new GLib.HashTable(str_hash, str_equal); + data["desktop-startup-id"] = context.get_startup_notify_id(this.owner.app_info, new GLib.List()); + + try { + this.gtk_application.Activate(data); + } + catch (IOError e) { + return false; + } + + return true; + } + + private void setup_gtk_application() + { + if (owner.current_state != PlayerController.state.CONNECTED) + return; + + if (this.gtk_application != null || this.gtk_application_searched) + return; + + try { + var connection = Bus.get_sync(BusType.SESSION); + var name = this.owner.dbus_name; + string gtk_application_path; + this.find_iface_path(connection, name, "/", "org.gtk.Application", out gtk_application_path); + this.gtk_application_searched = true; + + if (gtk_application_path != null) { + this.gtk_application = Bus.get_proxy_sync(BusType.SESSION, this.owner.dbus_name, gtk_application_path); + } + } catch (Error e) { + return; + } + } + + private void find_iface_path(DBusConnection connection, string name, string path, string target_iface, out string found_path) + { + found_path = null; + DBusNodeInfo node = null; + + try { + unowned string xml_string; + var xml = connection.call_sync(name, path, "org.freedesktop.DBus.Introspectable", "Introspect", null, new VariantType("(s)"), DBusCallFlags.NONE, 1000); + xml.get("(&s)", out xml_string); + node = new DBusNodeInfo.for_xml(xml_string); + } catch (Error e) { + return; + } + + if (node == null) { + return; + } + + foreach (var iface in node.interfaces) { + if (iface.name == target_iface) { + found_path = path; + return; + } + } + + bool is_root = (path == "/"); + + foreach (var subnode in node.nodes) { + string new_path = path; + + if (!is_root) { + new_path += "/"; + } + + new_path += subnode.path; + + find_iface_path(connection, name, new_path, target_iface, out found_path); + + if (found_path != null) { + return; + } + } + } + + private void setup_bamf_application() + { + this.bamf_application = null; + var desktop_app = this.owner.app_info as DesktopAppInfo; + + if (desktop_app == null) + return; + + foreach (var app in Bamf.Matcher.get_default().get_applications()) { + if (app.get_desktop_file() == desktop_app.get_filename()) { + this.bamf_application = app; + break; + } + } + } + + private bool activate_bamf_appplication(uint timestamp) + { + this.setup_bamf_application(); + + if (this.bamf_application == null) + return false; + + bool focused = false; + var dpy = Gdk.Display.get_default(); + + foreach (var win in this.bamf_application.get_windows()) { + if (win.get_window_type() != Bamf.WindowType.NORMAL) + continue; + + var xwin = Gdk.X11Window.foreign_new_for_display(dpy, win.get_xid()); + xwin.focus(timestamp); + focused = true; + } + + return focused; + } +} \ No newline at end of file -- cgit v1.2.3 From e90191e4416e2dc2d96da25f1b5dfe4a494570e7 Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Tue, 2 Apr 2013 18:16:41 +0200 Subject: PlayerActivator: fix crash and activation with WebApplications --- src/Makefile.am | 2 +- src/player-activator.vala | 200 ++++++++++++++++++++++++++++++++++++++ src/player-windows-activator.vala | 185 ----------------------------------- 3 files changed, 201 insertions(+), 186 deletions(-) create mode 100644 src/player-activator.vala delete mode 100644 src/player-windows-activator.vala (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 9807477..55df0f6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -68,7 +68,7 @@ music_bridge_VALASOURCES = \ playlists-menu-item.vala \ freedesktop-interfaces.vala \ fetch-file.vala \ - player-windows-activator.vala + player-activator.vala music_bridge_VALAFLAGS = \ --ccode \ diff --git a/src/player-activator.vala b/src/player-activator.vala new file mode 100644 index 0000000..abad81b --- /dev/null +++ b/src/player-activator.vala @@ -0,0 +1,200 @@ +/* +Copyright 2013 Canonical Ltd. + +Authors: + Marco Trevisan + +This program is free software: you can redistribute it and/or modify it +under the terms of the GNU General Public License version 3, as published +by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranties of +MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR +PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program. If not, see . +*/ + +[DBus (name = "org.gtk.Application")] +public interface DBusGtkApplication : Object { + public abstract void Activate(GLib.HashTable platform_data) throws IOError; +} + +public class PlayerActivator : GLib.Object +{ + public PlayerController owner {get; construct;} + + private bool gtk_application_searched = false; + private DBusGtkApplication gtk_application; + private Bamf.Application bamf_application; + + private const uint MAX_BAMF_APPLICATION_WAIT_MS = 1000; + private int64 last_check_time; + + public PlayerActivator(PlayerController ctrl) + { + GLib.Object(owner: ctrl); + } + + public void activate(uint timestamp) + { + if (!activate_gtk_appplication(timestamp)) { + if (!activate_bamf_appplication(timestamp)) { + // Let's wait BAMF to update its windows list + this.last_check_time = get_monotonic_time(); + + Idle.add(() => { + bool activated = activate_bamf_appplication(timestamp); + int64 waited = (get_monotonic_time() - this.last_check_time) / 1000; + return !activated && waited < MAX_BAMF_APPLICATION_WAIT_MS; + }); + } + } + } + + private bool activate_gtk_appplication(uint timestamp) + { + this.setup_gtk_application(); + + if (this.gtk_application == null) { + return false; + } + + var context = Gdk.Display.get_default().get_app_launch_context(); + context.set_timestamp(timestamp); + + var data = new GLib.HashTable(str_hash, str_equal); + data["desktop-startup-id"] = context.get_startup_notify_id(this.owner.app_info, new GLib.List()); + + try { + this.gtk_application.Activate(data); + } + catch (IOError e) { + return false; + } + + return true; + } + + private void setup_gtk_application() + { + if (owner.current_state != PlayerController.state.CONNECTED) + return; + + if (this.gtk_application != null || this.gtk_application_searched) + return; + + try { + var connection = Bus.get_sync(BusType.SESSION); + var name = this.owner.dbus_name; + string gtk_application_path; + this.find_iface_path(connection, name, "/", "org.gtk.Application", out gtk_application_path); + this.gtk_application_searched = true; + + if (gtk_application_path != null) { + this.gtk_application = Bus.get_proxy_sync(BusType.SESSION, this.owner.dbus_name, gtk_application_path); + } + } catch (Error e) { + return; + } + } + + private void find_iface_path(DBusConnection connection, string name, string path, string target_iface, out string found_path) + { + found_path = null; + DBusNodeInfo node = null; + + try { + unowned string xml_string; + var xml = connection.call_sync(name, path, "org.freedesktop.DBus.Introspectable", "Introspect", null, new VariantType("(s)"), DBusCallFlags.NONE, 1000); + xml.get("(&s)", out xml_string); + node = new DBusNodeInfo.for_xml(xml_string); + } catch (Error e) { + return; + } + + if (node == null) { + return; + } + + foreach (var iface in node.interfaces) { + if (iface.name == target_iface) { + found_path = path; + return; + } + } + + bool is_root = (path == "/"); + + foreach (var subnode in node.nodes) { + string new_path = path; + + if (!is_root) { + new_path += "/"; + } + + new_path += subnode.path; + + find_iface_path(connection, name, new_path, target_iface, out found_path); + + if (found_path != null) { + return; + } + } + } + + private void setup_bamf_application() + { + this.bamf_application = null; + var desktop_app = this.owner.app_info as DesktopAppInfo; + + if (desktop_app == null) + return; + + foreach (var app in Bamf.Matcher.get_default().get_applications()) { + if (app.get_desktop_file() == desktop_app.get_filename()) { + this.bamf_application = app; + break; + } + } + } + + private bool activate_bamf_appplication(uint timestamp) + { + this.setup_bamf_application(); + + if (this.bamf_application == null) + return false; + + bool focused = false; + var dpy = Gdk.Display.get_default(); + + foreach (var win in this.bamf_application.get_windows()) { + X.Window xid = 0; + + if (win is Bamf.Window) { + if (win.get_window_type() != Bamf.WindowType.NORMAL) + continue; + + xid = win.get_xid(); + } + else if (win is Bamf.Tab) + { + xid = (X.Window) (win as Bamf.Tab).get_xid(); + } + + if (xid > 0) { + var xwin = Gdk.X11Window.foreign_new_for_display(dpy, xid); + + if (xwin != null) { + xwin.focus(timestamp); + focused = true; + } + } + } + + return focused; + } +} \ No newline at end of file diff --git a/src/player-windows-activator.vala b/src/player-windows-activator.vala deleted file mode 100644 index e8cd616..0000000 --- a/src/player-windows-activator.vala +++ /dev/null @@ -1,185 +0,0 @@ -/* -Copyright 2013 Canonical Ltd. - -Authors: - Marco Trevisan - -This program is free software: you can redistribute it and/or modify it -under the terms of the GNU General Public License version 3, as published -by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranties of -MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program. If not, see . -*/ - -[DBus (name = "org.gtk.Application")] -public interface DBusGtkApplication : Object { - public abstract void Activate(GLib.HashTable platform_data) throws IOError; -} - -public class PlayerActivator : GLib.Object -{ - public PlayerController owner {get; construct;} - - private bool gtk_application_searched = false; - private DBusGtkApplication gtk_application; - private Bamf.Application bamf_application; - - private const uint MAX_BAMF_APPLICATION_WAIT_MS = 1000; - private int64 last_check_time; - - public PlayerActivator(PlayerController ctrl) - { - GLib.Object(owner: ctrl); - } - - public void activate(uint timestamp) - { - if (!activate_gtk_appplication(timestamp)) { - if (!activate_bamf_appplication(timestamp)) { - // Let's wait BAMF to update its windows list - this.last_check_time = get_monotonic_time(); - - Idle.add(() => { - bool activated = activate_bamf_appplication(timestamp); - int64 waited = (get_monotonic_time() - this.last_check_time) / 1000; - return !activated && waited < MAX_BAMF_APPLICATION_WAIT_MS; - }); - } - } - } - - private bool activate_gtk_appplication(uint timestamp) - { - this.setup_gtk_application(); - - if (this.gtk_application == null) { - return false; - } - - var context = Gdk.Display.get_default().get_app_launch_context(); - context.set_timestamp(timestamp); - - var data = new GLib.HashTable(str_hash, str_equal); - data["desktop-startup-id"] = context.get_startup_notify_id(this.owner.app_info, new GLib.List()); - - try { - this.gtk_application.Activate(data); - } - catch (IOError e) { - return false; - } - - return true; - } - - private void setup_gtk_application() - { - if (owner.current_state != PlayerController.state.CONNECTED) - return; - - if (this.gtk_application != null || this.gtk_application_searched) - return; - - try { - var connection = Bus.get_sync(BusType.SESSION); - var name = this.owner.dbus_name; - string gtk_application_path; - this.find_iface_path(connection, name, "/", "org.gtk.Application", out gtk_application_path); - this.gtk_application_searched = true; - - if (gtk_application_path != null) { - this.gtk_application = Bus.get_proxy_sync(BusType.SESSION, this.owner.dbus_name, gtk_application_path); - } - } catch (Error e) { - return; - } - } - - private void find_iface_path(DBusConnection connection, string name, string path, string target_iface, out string found_path) - { - found_path = null; - DBusNodeInfo node = null; - - try { - unowned string xml_string; - var xml = connection.call_sync(name, path, "org.freedesktop.DBus.Introspectable", "Introspect", null, new VariantType("(s)"), DBusCallFlags.NONE, 1000); - xml.get("(&s)", out xml_string); - node = new DBusNodeInfo.for_xml(xml_string); - } catch (Error e) { - return; - } - - if (node == null) { - return; - } - - foreach (var iface in node.interfaces) { - if (iface.name == target_iface) { - found_path = path; - return; - } - } - - bool is_root = (path == "/"); - - foreach (var subnode in node.nodes) { - string new_path = path; - - if (!is_root) { - new_path += "/"; - } - - new_path += subnode.path; - - find_iface_path(connection, name, new_path, target_iface, out found_path); - - if (found_path != null) { - return; - } - } - } - - private void setup_bamf_application() - { - this.bamf_application = null; - var desktop_app = this.owner.app_info as DesktopAppInfo; - - if (desktop_app == null) - return; - - foreach (var app in Bamf.Matcher.get_default().get_applications()) { - if (app.get_desktop_file() == desktop_app.get_filename()) { - this.bamf_application = app; - break; - } - } - } - - private bool activate_bamf_appplication(uint timestamp) - { - this.setup_bamf_application(); - - if (this.bamf_application == null) - return false; - - bool focused = false; - var dpy = Gdk.Display.get_default(); - - foreach (var win in this.bamf_application.get_windows()) { - if (win.get_window_type() != Bamf.WindowType.NORMAL) - continue; - - var xwin = Gdk.X11Window.foreign_new_for_display(dpy, win.get_xid()); - xwin.focus(timestamp); - focused = true; - } - - return focused; - } -} \ No newline at end of file -- cgit v1.2.3 From d858306353881b646481e2d9cf1a79692c2db66e Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Tue, 2 Apr 2013 18:22:43 +0200 Subject: PlayerActivator: style fix --- src/player-activator.vala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src') diff --git a/src/player-activator.vala b/src/player-activator.vala index abad81b..9508426 100644 --- a/src/player-activator.vala +++ b/src/player-activator.vala @@ -180,8 +180,7 @@ public class PlayerActivator : GLib.Object xid = win.get_xid(); } - else if (win is Bamf.Tab) - { + else if (win is Bamf.Tab) { xid = (X.Window) (win as Bamf.Tab).get_xid(); } -- cgit v1.2.3 From 0762482ba33dc83ce4675481895bf0cd6568e53d Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Tue, 2 Apr 2013 18:23:58 +0200 Subject: PlayerActivator: Adding end line to file. --- src/player-activator.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/player-activator.vala b/src/player-activator.vala index 9508426..7437a35 100644 --- a/src/player-activator.vala +++ b/src/player-activator.vala @@ -196,4 +196,4 @@ public class PlayerActivator : GLib.Object return focused; } -} \ No newline at end of file +} -- cgit v1.2.3 From 4d84133ab07c5ad5a5eee625f1385778e60273ae Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Thu, 11 Apr 2013 17:49:46 +0200 Subject: common-defs: add DbusmenuTransport.MENUITEM_STATE_CHANGE And use it in transport-widget.c --- src/common-defs.h | 5 +++-- src/transport-widget.c | 8 ++++---- vapi/common-defs.vapi | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/common-defs.h b/src/common-defs.h index a20fb03..efca5b2 100644 --- a/src/common-defs.h +++ b/src/common-defs.h @@ -64,8 +64,9 @@ typedef enum { #define DBUSMENU_MUTE_MENUITEM_TYPE "x-canonical-sound-menu-mute-type" #define DBUSMENU_MUTE_MENUITEM_VALUE "x-canonical-sound-menu-mute-value" -#define DBUSMENU_TRANSPORT_MENUITEM_TYPE "x-canonical-sound-menu-player-transport-type" -#define DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE "x-canonical-sound-menu-player-transport-state" +#define DBUSMENU_TRANSPORT_MENUITEM_TYPE "x-canonical-sound-menu-player-transport-type" +#define DBUSMENU_TRANSPORT_MENUITEM_PLAY_STATE "x-canonical-sound-menu-player-transport-state" +#define DBUSMENU_TRANSPORT_MENUITEM_STATE_CHANGE "Transport state change" #define DBUSMENU_TRACK_SPECIFIC_MENUITEM_TYPE "x-canonical-sound-menu-player-track-specific-type" diff --git a/src/transport-widget.c b/src/transport-widget.c index ebe8282..7df656e 100644 --- a/src/transport-widget.c +++ b/src/transport-widget.c @@ -450,7 +450,7 @@ transport_widget_seek (gpointer userdata) new_transport_state = g_variant_new_int32 ((int)TRANSPORT_ACTION_FORWIND); dbusmenu_menuitem_handle_event ( priv->twin_item, - "Transport state change", + DBUSMENU_TRANSPORT_MENUITEM_STATE_CHANGE, new_transport_state, 0 ); @@ -460,7 +460,7 @@ transport_widget_seek (gpointer userdata) new_transport_state = g_variant_new_int32 ((int)TRANSPORT_ACTION_REWIND); dbusmenu_menuitem_handle_event ( priv->twin_item, - "Transport state change", + DBUSMENU_TRANSPORT_MENUITEM_STATE_CHANGE, new_transport_state, 0 ); } @@ -482,7 +482,7 @@ transport_widget_button_release_event (GtkWidget *menuitem, priv->skip_frequency == 0){ GVariant* new_transport_state = g_variant_new_int32 ((int)result); dbusmenu_menuitem_handle_event ( priv->twin_item, - "Transport state change", + DBUSMENU_TRANSPORT_MENUITEM_STATE_CHANGE, new_transport_state, 0 ); } @@ -533,7 +533,7 @@ transport_widget_react_to_key_release_event ( TransportWidget* transport, GVariant* new_transport_event = g_variant_new_int32((int)transport_event); if (priv->skip_frequency == 0){ dbusmenu_menuitem_handle_event ( priv->twin_item, - "Transport state change", + DBUSMENU_TRANSPORT_MENUITEM_STATE_CHANGE, new_transport_event, 0 ); } diff --git a/vapi/common-defs.vapi b/vapi/common-defs.vapi index 33b4aad..a53d636 100644 --- a/vapi/common-defs.vapi +++ b/vapi/common-defs.vapi @@ -34,6 +34,7 @@ namespace DbusmenuMetadata{ namespace DbusmenuTransport{ public const string MENUITEM_TYPE; public const string MENUITEM_PLAY_STATE; + public const string MENUITEM_STATE_CHANGE; } [CCode (cheader_filename = "common-defs.h")] -- cgit v1.2.3 From 0c7736720fe80163f9a59f2c7c83ddd5be88e034 Mon Sep 17 00:00:00 2001 From: "Marco Trevisan (Treviño)" Date: Thu, 11 Apr 2013 17:54:50 +0200 Subject: TransportMenuItem: ignore events if they are not of the dbus-menu transport --- src/transport-menu-item.vala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/transport-menu-item.vala b/src/transport-menu-item.vala index 0af3118..2a77d5a 100644 --- a/src/transport-menu-item.vala +++ b/src/transport-menu-item.vala @@ -70,7 +70,7 @@ public class TransportMenuitem : PlayerItem Variant input_value, uint timestamp) { - if (name != Dbusmenu.MENUITEM_EVENT_ACTIVATED) + if (name != DbusmenuTransport.MENUITEM_STATE_CHANGE) return; Variant v = input_value; -- cgit v1.2.3