From ba80753bb0a79f052f007c6e614732afac88e3ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= 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. --- 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 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 191 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/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: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= 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: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= 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: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= 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