aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLars Uebernickel <lars.uebernickel@canonical.com>2013-03-28 12:34:35 -0400
committerLars Uebernickel <lars.uebernickel@canonical.com>2013-03-28 12:34:35 -0400
commit1419ef39b81be9d8a42b915ad9f56e81ab4eb03c (patch)
treec2265dd76e20dfcdf6bb42fd17915fa63c70dfbb /src
parent0386c87d0176c3d539f6cac6f5d52ba1adf9d761 (diff)
downloadayatana-indicator-sound-1419ef39b81be9d8a42b915ad9f56e81ab4eb03c.tar.gz
ayatana-indicator-sound-1419ef39b81be9d8a42b915ad9f56e81ab4eb03c.tar.bz2
ayatana-indicator-sound-1419ef39b81be9d8a42b915ad9f56e81ab4eb03c.zip
Show running media players in the menu
Each player has its own action with a dictionary state. Right now, this state only contains one key "running", which signifies whether an instance of the player is currently running. It does not yet show non-running players on startup, and ignores the blacklist.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/media-player-list.vala70
-rw-r--r--src/media-player.vala124
-rw-r--r--src/service.vala40
4 files changed, 235 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 2421683..1de360c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,6 +20,8 @@ music_bridge_VALASOURCES = \
service.vala \
main.vala \
volume-control.vala \
+ media-player.vala \
+ media-player-list.vala \
music-player-bridge.vala \
transport-menu-item.vala \
specific-items-manager.vala \
diff --git a/src/media-player-list.vala b/src/media-player-list.vala
new file mode 100644
index 0000000..0c0a212
--- /dev/null
+++ b/src/media-player-list.vala
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+/**
+ * MediaPlayerList is a list of media players that should appear in the sound menu. Its main responsibility is
+ * to listen for MPRIS players on the bus and attach them to the corresponding %Player objects.
+ */
+public class MediaPlayerList {
+
+ public MediaPlayerList () {
+ this._players = new HashTable<string, MediaPlayer> (str_hash, str_equal);
+
+ this.mpris_watcher = new Mpris2Watcher ();
+ this.mpris_watcher.client_appeared.connect (this.player_appeared);
+ this.mpris_watcher.client_disappeared.connect (this.player_disappeared);
+ }
+
+ public List<MediaPlayer> players {
+ owned get {
+ return this._players.get_values ();
+ }
+ }
+
+ public signal void player_added (MediaPlayer player);
+
+ HashTable<string, MediaPlayer> _players;
+ Mpris2Watcher mpris_watcher;
+
+ void player_appeared (string desktop_id, string dbus_name, bool use_playlists) {
+ var appinfo = new DesktopAppInfo (desktop_id + ".desktop");
+ if (appinfo == null) {
+ warning ("unable to find application '%s'", desktop_id);
+ return;
+ }
+
+ MediaPlayer? player = this._players.lookup (desktop_id);
+ if (player == null) {
+ player = new MediaPlayer (appinfo);
+ this._players.insert (player.id, player);
+ this.player_added (player);
+ }
+
+ player.attach (dbus_name);
+ }
+
+ void player_disappeared (string dbus_name) {
+ MediaPlayer? player = this._players.find ( (name, player) => {
+ return player.dbus_name == dbus_name;
+ });
+
+ if (player != null)
+ player.detach ();
+ }
+}
diff --git a/src/media-player.vala b/src/media-player.vala
new file mode 100644
index 0000000..8037ddb
--- /dev/null
+++ b/src/media-player.vala
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program 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 program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+/**
+ * MediaPlayer represents an MRPIS-capable media player.
+ */
+public class MediaPlayer: Object {
+
+ public MediaPlayer (DesktopAppInfo appinfo) {
+ this.appinfo = appinfo;
+ }
+
+ /** Desktop id of the player */
+ public string id {
+ get {
+ return this.appinfo.get_id ();
+ }
+ }
+
+ /** Display name of the player */
+ public string name {
+ get {
+ return this.appinfo.get_name ();
+ }
+ }
+
+ /** Application icon of the player */
+ public Icon icon {
+ get {
+ return this.appinfo.get_icon ();
+ }
+ }
+
+ /**
+ * True if an instance of the player is currently running.
+ *
+ * See also: attach(), detach()
+ */
+ public bool is_running {
+ get {
+ return this.proxy != null;
+ }
+ }
+
+ /** Name of the player on the bus, if an instance is currently running */
+ public string dbus_name {
+ get {
+ return this._dbus_name;
+ }
+ }
+
+ /**
+ * Attach this object to a process of the associated media player. The player must own @dbus_name and
+ * implement the org.mpris.MediaPlayer2.Player interface.
+ *
+ * Only one player can be attached at any given time. Use detach() to detach a player.
+ *
+ * This method does not block. If it is successful, "is-running" will be set to %TRUE.
+ */
+ public void attach (string dbus_name) {
+ return_if_fail (this._dbus_name == null && this.proxy == null);
+
+ this._dbus_name = dbus_name;
+ Bus.get_proxy.begin<MprisPlayer> (BusType.SESSION, dbus_name, "/org/mpris/MediaPlayer2",
+ DBusProxyFlags.NONE, null, got_proxy);
+ }
+
+ /**
+ * Detach this object from a process running the associated media player.
+ *
+ * See also: attach()
+ */
+ public void detach () {
+ this.proxy = null;
+ this._dbus_name = null;
+ this.notify_property ("is-running");
+ }
+
+ /**
+ * Launch the associated media player.
+ *
+ * Note: this will _not_ call attach(), because it doesn't know on which dbus-name the player will appear.
+ * Use attach() to attach this object to a running instance of the player.
+ */
+ public void launch () {
+ try {
+ this.appinfo.launch (null, null);
+ }
+ catch (Error e) {
+ warning ("unable to launch %s: %s", appinfo.get_name (), e.message);
+ }
+ }
+
+ DesktopAppInfo appinfo;
+ MprisPlayer? proxy;
+ string _dbus_name;
+
+ void got_proxy (Object? obj, AsyncResult res) {
+ try {
+ this.proxy = Bus.get_proxy.end (res);
+ this.notify_property ("is-running");
+ }
+ catch (Error e) {
+ this._dbus_name = null;
+ warning ("unable to attach to media player: %s", e.message);
+ }
+ }
+}
diff --git a/src/service.vala b/src/service.vala
index aa6664e..4ea4a95 100644
--- a/src/service.vala
+++ b/src/service.vala
@@ -2,6 +2,9 @@
public class IndicatorSound.Service {
public Service () {
this.volume_control = new VolumeControl ();
+
+ this.players = new MediaPlayerList ();
+ this.players.player_added.connect (player_added);
}
public int run () {
@@ -28,6 +31,8 @@ public class IndicatorSound.Service {
SimpleActionGroup actions;
Menu menu;
VolumeControl volume_control;
+ MediaPlayerList players;
+ uint player_action_update_id;
void activate_settings (SimpleAction action, Variant? param) {
try {
@@ -41,7 +46,7 @@ public class IndicatorSound.Service {
var submenu = new Menu ();
submenu.append ("Mute", "indicator.mute");
- var slider = new MenuItem ("null", "indicator.volume");
+ var slider = new MenuItem (null, "indicator.volume");
slider.set_attribute ("x-canonical-type", "s", "com.canonical.unity.slider");
submenu.append_item (slider);
@@ -111,4 +116,37 @@ public class IndicatorSound.Service {
void name_lost (DBusConnection connection, string name) {
this.loop.quit ();
}
+
+ bool update_player_action (MediaPlayer player) {
+ var builder = new VariantBuilder (new VariantType ("a{sv}"));
+ builder.add ("{sv}", "running", new Variant ("b", player.is_running));
+ var state = builder.end ();
+
+ SimpleAction? action = this.actions.lookup (player.id) as SimpleAction;
+ if (action == null) {
+ action = new SimpleAction.stateful (player.id, null, state);
+ action.activate.connect ( () => { player.launch (); });
+ this.actions.insert (action);
+ }
+ else {
+ action.set_state (state);
+ }
+
+ this.player_action_update_id = 0;
+ return false;
+ }
+
+ void eventually_update_player_action (MediaPlayer player) {
+ if (player_action_update_id == 0)
+ this.player_action_update_id = Idle.add ( () => this.update_player_action (player) );
+ }
+
+ void player_added (MediaPlayer player) {
+ var item = new MenuItem (player.name, player.id);
+ item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.media-player");
+ this.menu.insert_item (this.menu.get_n_items () -1, item);
+
+ eventually_update_player_action (player);
+ player.notify.connect ( () => eventually_update_player_action (player) );
+ }
}