[Xfce4-commits] <parole:master> Merge parole-mpris2 plugin, Thanks Hakan and Matias
Sean Davis
noreply at xfce.org
Thu Dec 12 00:18:42 CET 2013
Updating branch refs/heads/master
to a2eed21e9c2c6404f7ee17d8bd9a722109220c92 (commit)
from bb3adcf5d17aadd31663ea0c305b760b4501e3a8 (commit)
commit a2eed21e9c2c6404f7ee17d8bd9a722109220c92
Merge: bb3adcf 15abc9c
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Wed Dec 11 18:16:04 2013 -0500
Merge parole-mpris2 plugin, Thanks Hakan and Matias
commit 15abc9ceaeba65b552452a4f84a4f530a6ee13f7
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Mon Dec 9 11:27:04 2013 -0300
Remove jumped time as argument of MEDIA_SEEKED signal.
The information is lost by the previous commit.
commit 5ec88d21309db2a3fcab3d0e5f2178529a353025
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Mon Dec 9 11:12:47 2013 -0300
D'Oh!.
commit 4c9ae196e51553fcbe835f2c7c953548f0ba474e
Merge: c0567fc 5bbb38b
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Mon Dec 9 11:07:44 2013 -0300
Merge branch 'master' of https://github.com/aquaherd/parole-mpris2
commit c0567fce118afb41f37526d6cf32a92d7ade8a00
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Mon Dec 9 11:06:18 2013 -0300
Emit MEDIA_SEEKED signal when finalize the async thread.
Use gint64 vars where appropriate.. GVariant is sensitive to the type of data.
commit 5bbb38b777f6ab613880e8ad786c8535912a8e1a
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Dec 8 17:40:12 2013 -0500
Ignore artwork if video
commit 20f5473559b4c560985a211c1741ec9bc4e97fdf
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Dec 8 17:21:33 2013 -0500
Fix OpenUri to append the track to the playlist and play
commit d42144f794328c30c1662a40f950a10bd5e9446e
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Dec 8 16:55:46 2013 -0500
Add seeked signal
commit 3d390a079a60891db42c3c9c154a69c448430326
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Dec 8 07:24:43 2013 -0500
Tweak spacing and tab sizes
commit 5bddac966a1fcd33dc3e4e26465e921142096a02
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Dec 8 07:02:37 2013 -0500
Fix debug builds, enable no-cover albumart
commit ed2adfb6e94944cb2a19bf8f9e3dd0e92afcb686
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sat Dec 7 16:27:07 2013 -0500
Implement SeekBy and SetPosition
commit ff441e3e265ef10dc0dca7f17e8666f3571a77d6
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sat Dec 7 10:40:07 2013 -0500
Fix setting volume with mpris2
commit c96da1b170d495e9313206cad4821ee22b213d70
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sat Dec 7 08:49:12 2013 -0500
Handle volume updates from conf
commit 0b59cbffe41e539f763fd05978c54697e875676a
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sat Dec 7 08:30:15 2013 -0500
Fix GetCanSeek check and GetPosition
commit 8aa1d5eec7b7e089de47c0592e7d638a7e0e83c4
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Wed Nov 20 01:04:25 2013 -0300
D'Oh!. Fix array of SupportedUriSchemes variant.
commit 54233da8f19f7e7db8e82e3d2a93d8188deb8acc
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Mon Nov 18 20:27:47 2013 -0300
Fix warnings due NULL tags.
commit 91e44144a932fbe91966d630f37a42348e609a96
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 22:42:48 2013 -0500
Use G_BINDING_SYNC_CREATE for binding settings and initial values
commit 60f9808d30af676995c04c8553b1ac5c874ee301
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 16:45:12 2013 -0500
Add parole_provider_player_get_stream_position
commit 5a131b68acad5f22e0e56f1c8814a8747c6d54ac
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 15:19:44 2013 -0500
Add bitrate
commit 01c98b546a494d63225b8101ff17b3f01df62277
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 14:52:24 2013 -0500
Fix genre for real
commit 026ed6b8ad4fd15293b00c2c47e83bfe4165b5e2
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 14:50:10 2013 -0500
Fix Genre
commit 28472fbc56401e120bdd9747c2cb1e703920b872
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 14:31:28 2013 -0500
Fix Track ID
commit d132bc7880c30ceedcb1aaf5ed1752929680d1ee
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 11:08:56 2013 -0500
Add album art functionality
commit 9276bc68d7dca3d68590bad6ae217346b59776ae
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 07:03:59 2013 -0500
Fix warning for CanPause being False when the player is currently paused
commit e48f090c7c9c047e00a6b95dff84713599658adc
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sun Nov 17 06:55:20 2013 -0500
Add fullscreen mpris2 functionality
commit 0e5fd5f87afcd5622ad8af083708a543fbfe7a45
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sat Nov 16 21:55:58 2013 -0500
Fix Identifier, mimetypes, and CanPlay/CanPause status
commit 6d9c6fa9b028e6292650eba664426baebbb81666
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Sat Nov 16 20:56:12 2013 -0500
Populate uri schemes and uri schemes
commit f28e424562abfd1afdbb32e0023c9879b95e35ba
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Mon Nov 11 22:00:00 2013 -0500
Patch to parole-master
commit 4dcd29ac684c91d9dfdee42a74c992e6fc5bafd7
Author: Sean Davis <smd.seandavis at gmail.com>
Date: Mon Nov 11 21:52:20 2013 -0500
Fix xfconf notify when property changes
commit 38f53010485fc03c737ffd0b0144a2538bd5a7c8
Author: Hakan Erduman <smultimeter at gmail.com>
Date: Sun Nov 10 22:14:05 2013 +0100
Fixed Volume scaling
commit b473e35870a2ada2c3c1baa9d3aa496eea7e3e81
Author: Hakan Erduman <smultimeter at gmail.com>
Date: Sun Nov 10 20:38:45 2013 +0100
- Fixed Quit()
- Fixed property-change notifications from UI to mpris player excepting
volume
(Note this fix requires change to Parole codebase)
- Debug with G_MESSAGES_DEBUG=all to see effect
commit 8d012aa3f1949558425947103be267b06829368c
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Thu Nov 7 22:25:55 2013 -0300
Check shuffle and repeat changes on parole_mpris_update_any()
commit 2b35cf50f0b0c578015fc1e2b65ad1f59a99226e
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Thu Nov 7 21:46:08 2013 -0300
D'Oh!.
commit 5197e0836ee1432b20b10e8ae38e798680aa4b53
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Thu Nov 7 21:31:35 2013 -0300
Complete finalize method.
commit b8273a4cd22d36d972800a3fe2dc09b6bc83e7bf
Merge: a28bdac 232b540
Author: Hakan Erduman <smultimeter at gmail.com>
Date: Tue Nov 5 23:47:55 2013 +0100
Merge branch 'master' of github.com:aquaherd/parole-mpris2
commit a28bdac35e71fb6dd0c776a3bc455a884cfd2cfc
Author: Hakan Erduman <smultimeter at gmail.com>
Date: Tue Nov 5 23:46:31 2013 +0100
Some TODOs fixed, some added
commit 232b540632a960fc753575a603604f3ef0bfbf3d
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Tue Nov 5 14:58:25 2013 -0300
Emit metadata only when chage playback state to playing.
commit 3d28ecece50ca5127918c5d55d8138a0517a5fa1
Author: matiasdelellis <mati86dl at hotmail.com>
Date: Tue Nov 5 14:31:48 2013 -0300
Add basic org.mpris.MediaPlayer2 and org.mpris.MediaPlayer2.Player.
Tested with xfce4-soundmenu-plugin. Please, see TODOS in file.
commit 303674839f58a610ed1dfc99b036704faf240568
Author: Hakan Erduman <smultimeter at gmail.com>
Date: Tue Nov 5 12:56:56 2013 +0100
Omissions from first try added
commit d8ce4cfa839f47181c9f8905756b081504b9784b
Author: Hakan Erduman <smultimeter at gmail.com>
Date: Tue Nov 5 10:32:45 2013 +0100
Skeleton MPRIS2 plugin added
.gitignore | 3 +
configure.ac.in | 10 +
data/interfaces/parole.ui | 5 +-
data/pixmaps/Makefile.am | 3 +-
data/pixmaps/no-cover.png | Bin 0 -> 11327 bytes
src/gst/gstmarshal.list | 2 +-
src/gst/parole-gst.c | 83 +-
src/gst/parole-gst.h | 3 +
src/misc/parole-provider-player.c | 80 ++
src/misc/parole-provider-player.h | 17 +
src/misc/parole-stream.c | 102 +-
src/parole-conf-dialog.c | 97 +-
src/parole-conf.c | 18 +
src/parole-medialist.c | 16 +-
src/parole-medialist.h | 3 +
src/parole-player.c | 33 +-
src/parole-plugin-player.c | 84 +-
src/plugins/Makefile.am | 6 +-
src/plugins/{tray => mpris2}/Makefile.am | 25 +-
.../sample-plugin.c => mpris2/mpris2-plugin.c} | 6 +-
src/plugins/mpris2/mpris2-provider.c | 1096 ++++++++++++++++++++
src/plugins/mpris2/mpris2-provider.h | 45 +
src/plugins/mpris2/mpris2.desktop.in | 6 +
23 files changed, 1601 insertions(+), 142 deletions(-)
diff --git a/.gitignore b/.gitignore
index 9abea57..90e04f9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,3 +79,6 @@ src/parole
src/stamp-enum-gtypes.h
stamp-enum-types.h
stamp-h1
+.cproject
+.project
+.settings
diff --git a/configure.ac.in b/configure.ac.in
index 6edce23..8a27c7f 100644
--- a/configure.ac.in
+++ b/configure.ac.in
@@ -223,6 +223,14 @@ AC_MSG_CHECKING([whether to build the system tray plugin])
AM_CONDITIONAL([PAROLE_TRAY_PLUGIN], [test x"$ac_tray_plugin" = x"yes"])
AC_MSG_RESULT([$ac_tray_plugin])
+# MPRIS2 plugin.
+#--------------------------
+AC_ARG_ENABLE([mpris2-plugin], AC_HELP_STRING([--disable-mpris2-plugin], [Don't build parole mpris2 plugin]),
+ [ac_mpris2_plugin=$enableval], [ac_mpris2_plugin=yes])
+AC_MSG_CHECKING([whether to build the mpris2 plugin])
+AM_CONDITIONAL([PAROLE_MPRIS2_PLUGIN], [test x"$ac_mpris2_plugin" = x"yes"])
+AC_MSG_RESULT([$ac_mpris2_plugin])
+
# ===================================================== #
# Check for debugging support #
# ===================================================== #
@@ -256,6 +264,7 @@ src/plugins/Makefile
src/plugins/sample/Makefile
src/plugins/notify/Makefile
src/plugins/tray/Makefile
+src/plugins/mpris2/Makefile
docs/Makefile
docs/plugin-api/version.xml
docs/plugin-api/Makefile
@@ -284,6 +293,7 @@ echo "
=================
Notify: ${ac_notify_plugin}
System Tray icon: ${ac_tray_plugin}
+ MPRIS2: ${ac_mpris2_plugin}
------------------------------------------------------
diff --git a/data/interfaces/parole.ui b/data/interfaces/parole.ui
index ffb9781..e2fd011 100644
--- a/data/interfaces/parole.ui
+++ b/data/interfaces/parole.ui
@@ -422,8 +422,8 @@
<property name="use_underline">True</property>
<property name="image">image_volume_up</property>
<property name="use_stock">False</property>
- <accelerator key="plus" signal="activate"/>
<accelerator key="equal" signal="activate"/>
+ <accelerator key="plus" signal="activate"/>
<signal name="activate" handler="parole_player_volume_up" swapped="no"/>
</object>
</child>
@@ -932,7 +932,7 @@
<property name="show_text">True</property>
</object>
<packing>
- <property name="expand">False</property>
+ <property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
@@ -990,6 +990,7 @@ audio-volume-low-symbolic
audio-volume-medium-symbolic</property>
<property name="use_symbolic">True</property>
<signal name="value-changed" handler="parole_player_volume_value_changed_cb" swapped="no"/>
+ <signal name="scroll-event" handler="parole_player_volume_scroll_event_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
diff --git a/data/pixmaps/Makefile.am b/data/pixmaps/Makefile.am
index 44c3121..ec31ae3 100644
--- a/data/pixmaps/Makefile.am
+++ b/data/pixmaps/Makefile.am
@@ -1,5 +1,6 @@
pixmaps = \
- parole.png
+ parole.png \
+ no-cover.png
pixmaps_DATA = \
$(pixmaps)
diff --git a/data/pixmaps/no-cover.png b/data/pixmaps/no-cover.png
new file mode 100644
index 0000000..2ff85ba
Binary files /dev/null and b/data/pixmaps/no-cover.png differ
diff --git a/src/gst/gstmarshal.list b/src/gst/gstmarshal.list
index 4447534..8c09ebc 100644
--- a/src/gst/gstmarshal.list
+++ b/src/gst/gstmarshal.list
@@ -1,4 +1,4 @@
VOID:OBJECT,ENUM
VOID:OBJECT,INT64
VOID:OBJECT,INT
-
+VOID:OBJECT,DOUBLE
diff --git a/src/gst/parole-gst.c b/src/gst/parole-gst.c
index 7fc18cd..11e2334 100644
--- a/src/gst/parole-gst.c
+++ b/src/gst/parole-gst.c
@@ -123,6 +123,7 @@ struct ParoleGstPrivate
gboolean with_vis;
gboolean vis_loaded;
gboolean buffering;
+ gboolean seeking;
gboolean update_color_balance;
gdouble volume;
@@ -147,6 +148,7 @@ enum
MEDIA_STATE,
MEDIA_PROGRESSED,
MEDIA_TAG,
+ MEDIA_SEEKED,
BUFFERING,
ERROR,
DVD_CHAPTER_CHANGE,
@@ -922,6 +924,7 @@ parole_gst_evaluate_state (ParoleGst *gst, GstState old, GstState new, GstState
case GST_STATE_READY:
{
gst->priv->buffering = FALSE;
+ gst->priv->seeking = FALSE;
gst->priv->media_state = PAROLE_STATE_STOPPED;
g_signal_emit (G_OBJECT (gst), signals [MEDIA_STATE], 0,
gst->priv->stream, PAROLE_STATE_STOPPED);
@@ -945,6 +948,7 @@ parole_gst_evaluate_state (ParoleGst *gst, GstState old, GstState new, GstState
case GST_STATE_NULL:
{
gst->priv->buffering = FALSE;
+ gst->priv->seeking = FALSE;
gst->priv->media_state = PAROLE_STATE_STOPPED;
g_signal_emit (G_OBJECT (gst), signals [MEDIA_STATE], 0,
gst->priv->stream, PAROLE_STATE_STOPPED);
@@ -1280,6 +1284,7 @@ parole_gst_get_meta_data_cdda (ParoleGst *gst, GstTagList *tag)
"year", NULL,
"album", _("Audio CD"),
"comment", NULL,
+ "genre", NULL,
NULL);
parole_stream_set_image (G_OBJECT (gst->priv->stream), NULL);
@@ -1297,6 +1302,8 @@ parole_gst_get_meta_data_local_file (ParoleGst *gst, GstTagList *tag)
{
gchar *str;
GDate *date;
+ guint integer;
+
GdkPixbuf *pixbuf;
if ( gst_tag_list_get_string_index (tag, GST_TAG_TITLE, 0, &str) )
@@ -1347,6 +1354,31 @@ parole_gst_get_meta_data_local_file (ParoleGst *gst, GstTagList *tag)
g_free (str);
}
+ if ( gst_tag_list_get_string_index (tag, GST_TAG_GENRE, 0, &str) )
+ {
+ TRACE ("genre:%s", str);
+ g_object_set (G_OBJECT (gst->priv->stream),
+ "genre", str,
+ NULL);
+ g_free (str);
+ }
+
+ if ( gst_tag_list_get_uint (tag, GST_TAG_TRACK_NUMBER, &integer) )
+ {
+ TRACE ("track:%i", integer);
+ g_object_set (G_OBJECT (gst->priv->stream),
+ "track", integer,
+ NULL);
+ }
+
+ if ( gst_tag_list_get_uint (tag, GST_TAG_BITRATE, &integer) )
+ {
+ TRACE ("bitrate:%i", integer);
+ g_object_set (G_OBJECT (gst->priv->stream),
+ "bitrate", integer,
+ NULL);
+ }
+
pixbuf = parole_gst_tag_list_get_cover (gst, tag);
if (pixbuf)
{
@@ -1370,6 +1402,7 @@ parole_gst_get_meta_data_unknown (ParoleGst *gst)
"year", NULL,
"album", NULL,
"comment", NULL,
+ "genre", NULL,
NULL);
parole_stream_set_image (G_OBJECT (gst->priv->stream), NULL);
@@ -1647,6 +1680,13 @@ parole_gst_bus_event (GstBus *bus, GstMessage *msg, gpointer data)
case GST_MESSAGE_STREAM_STATUS:
TRACE ("Stream status");
break;
+ case GST_MESSAGE_ASYNC_DONE:
+ if (gst->priv->seeking)
+ {
+ gst->priv->seeking = FALSE;
+ g_signal_emit (G_OBJECT (gst), signals [MEDIA_SEEKED], 0);
+ }
+ break;
case GST_MESSAGE_WARNING:
case GST_MESSAGE_STEP_DONE:
case GST_MESSAGE_CLOCK_PROVIDE:
@@ -1656,7 +1696,6 @@ parole_gst_bus_event (GstBus *bus, GstMessage *msg, gpointer data)
case GST_MESSAGE_SEGMENT_START:
case GST_MESSAGE_LATENCY:
case GST_MESSAGE_ASYNC_START:
- case GST_MESSAGE_ASYNC_DONE:
default:
break;
}
@@ -1999,6 +2038,18 @@ parole_gst_conf_notify_cb (GObject *object, GParamSpec *spec, ParoleGst *gst)
}
}
+static void
+parole_gst_conf_notify_volume_cb (GObject *conf, GParamSpec *pspec, ParoleGst *gst)
+{
+ gint volume;
+
+ g_object_get (G_OBJECT (gst->priv->conf),
+ "volume", &volume,
+ NULL);
+
+ parole_gst_set_volume (gst, (double)(volume / 100.0));
+}
+
static void parole_gst_get_property (GObject *object,
guint prop_id,
GValue *value,
@@ -2052,6 +2103,8 @@ static void parole_gst_set_property (GObject *object,
g_signal_connect (G_OBJECT (gst->priv->conf), "notify",
G_CALLBACK (parole_gst_conf_notify_cb), gst);
+ g_signal_connect (G_OBJECT (gst->priv->conf), "notify::volume",
+ G_CALLBACK (parole_gst_conf_notify_volume_cb), gst);
}
break;
default:
@@ -2269,6 +2322,15 @@ parole_gst_class_init (ParoleGstClass *klass)
_gmarshal_VOID__OBJECT_INT64,
G_TYPE_NONE, 2,
G_TYPE_OBJECT, G_TYPE_INT64);
+
+ signals[MEDIA_SEEKED] =
+ g_signal_new ("media-seeked",
+ PAROLE_TYPE_GST,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ParoleGstClass, media_seeked),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
signals [MEDIA_TAG] =
g_signal_new ("media-tag",
@@ -2357,6 +2419,7 @@ parole_gst_init (ParoleGst *gst)
gst->priv->hidecursor_timer = g_timer_new ();
gst->priv->update_vis = FALSE;
gst->priv->buffering = FALSE;
+ gst->priv->seeking = FALSE;
gst->priv->update_color_balance = TRUE;
gst->priv->state_change_id = 0;
gst->priv->device = NULL;
@@ -2585,17 +2648,23 @@ void parole_gst_seek (ParoleGst *gst, gdouble seek)
GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH,
GST_SEEK_TYPE_SET, (int) seek * GST_SECOND,
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE));
+
+ gst->priv->seeking = TRUE;
}
void parole_gst_set_volume (ParoleGst *gst, gdouble volume)
{
- gst_stream_volume_set_volume (GST_STREAM_VOLUME (gst->priv->playbin),
- GST_STREAM_VOLUME_FORMAT_CUBIC,
- volume);
volume = CLAMP (volume, 0.0, 1.0);
- gst->priv->volume = volume;
-
- g_object_notify (G_OBJECT (gst), "volume");
+ if (gst->priv->volume != volume)
+ {
+ gst_stream_volume_set_volume (GST_STREAM_VOLUME (gst->priv->playbin),
+ GST_STREAM_VOLUME_FORMAT_CUBIC,
+ volume);
+
+ gst->priv->volume = volume;
+
+ g_object_notify (G_OBJECT (gst), "volume");
+ }
}
gdouble parole_gst_get_volume (ParoleGst *gst)
diff --git a/src/gst/parole-gst.h b/src/gst/parole-gst.h
index 1584339..6793675 100644
--- a/src/gst/parole-gst.h
+++ b/src/gst/parole-gst.h
@@ -76,6 +76,9 @@ typedef struct
void (*media_progressed) (ParoleGst *gst,
const ParoleStream *stream,
gint64 value);
+
+ void (*media_seeked) (ParoleGst *gst,
+ const ParoleStream *stream);
void (*buffering) (ParoleGst *gst,
const ParoleStream *stream,
diff --git a/src/misc/parole-provider-player.c b/src/misc/parole-provider-player.c
index 0dff22d..4496170 100644
--- a/src/misc/parole-provider-player.c
+++ b/src/misc/parole-provider-player.c
@@ -101,6 +101,23 @@ static void parole_provider_player_base_init (gpointer klass)
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, PAROLE_TYPE_STREAM);
+
+ /**
+ * ParoleProviderPlayerIface::seeked:
+ * @player: the object which received the signal.
+ * @value: the seeked position.
+ *
+ * Notifies when the stream has been manually advanced.
+ *
+ * Since: 0.6
+ **/
+ g_signal_new ("seeked",
+ G_TYPE_FROM_INTERFACE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ParoleProviderPlayerIface, seeked),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1, G_TYPE_DOUBLE);
initialized = TRUE;
}
@@ -402,6 +419,26 @@ gboolean parole_provider_player_seek (ParoleProviderPlayer *player, gdouble pos)
return ret;
}
+/**
+ * parole_provider_player_get_stream_position:
+ * @player: a #ParoleProviderPlayer
+ *
+ * Get stream position (microseconds) for Parole.
+ *
+ * Since: 0.6
+ **/
+gdouble parole_provider_player_get_stream_position(ParoleProviderPlayer *player)
+{
+ g_return_val_if_fail (PAROLE_IS_PROVIDER_PLAYER (player), 0);
+
+ if ( PAROLE_PROVIDER_PLAYER_GET_INTERFACE (player)->get_stream_position )
+ {
+ return (*PAROLE_PROVIDER_PLAYER_GET_INTERFACE (player)->get_stream_position) (player);
+ }
+
+ return FALSE;
+}
+
/**
* parole_provider_player_open_media_chooser:
@@ -434,3 +471,46 @@ GtkAction *parole_provider_player_get_action(ParoleProviderPlayer *player, Parol
{
return parole_player_get_action(action);
}
+
+/**
+ * parole_provider_player_get_fullscreen:
+ * @player: a #ParoleProviderPlayer
+ *
+ * Get fullscreen status for Parole.
+ *
+ * Since: 0.6
+ **/
+gboolean parole_provider_player_get_fullscreen(ParoleProviderPlayer *player)
+{
+ g_return_val_if_fail (PAROLE_IS_PROVIDER_PLAYER (player), NULL);
+
+ if ( PAROLE_PROVIDER_PLAYER_GET_INTERFACE (player)->get_stream )
+ {
+ return (*PAROLE_PROVIDER_PLAYER_GET_INTERFACE (player)->get_fullscreen) (player);
+ }
+
+ return FALSE;
+}
+
+/**
+ * parole_provider_player_set_fullscreen:
+ * @player: a #ParoleProviderPlayer
+ * @fullscreen: TRUE for fullscreen, FALSE for unfullscreen
+ *
+ * Set fullscreen status for Parole.
+ *
+ * Returns: TRUE if the fullscreen command succeeded, FALSE otherwise.
+ *
+ * Since: 0.6
+ **/
+gboolean parole_provider_player_set_fullscreen(ParoleProviderPlayer *player, gboolean fullscreen)
+{
+ g_return_val_if_fail (PAROLE_IS_PROVIDER_PLAYER (player), NULL);
+
+ if ( PAROLE_PROVIDER_PLAYER_GET_INTERFACE (player)->get_stream )
+ {
+ return (*PAROLE_PROVIDER_PLAYER_GET_INTERFACE (player)->set_fullscreen) (player, fullscreen);
+ }
+
+ return FALSE;
+}
diff --git a/src/misc/parole-provider-player.h b/src/misc/parole-provider-player.h
index 98bee2d..0c93d8e 100644
--- a/src/misc/parole-provider-player.h
+++ b/src/misc/parole-provider-player.h
@@ -80,12 +80,22 @@ struct _ParoleProviderPlayerIface
gboolean (*seek) (ParoleProviderPlayer *player,
gdouble pos);
+
+ gdouble (*get_stream_position) (ParoleProviderPlayer *player);
+
+ gboolean (*get_fullscreen) (ParoleProviderPlayer *player);
+
+ gboolean (*set_fullscreen) (ParoleProviderPlayer *player,
+ gboolean fullscreen);
void (*open_media_chooser) (ParoleProviderPlayer *player);
/*< signals >*/
void (*tag_message) (ParoleProviderPlayer *player,
const ParoleStream *stream);
+
+ void (*seeked) (ParoleProviderPlayer *player,
+ gdouble value);
void (*state_changed) (ParoleProviderPlayer *player,
const ParoleStream *stream,
@@ -123,12 +133,19 @@ gboolean parole_provider_player_play_next (ParoleProviderPlayer *player);
gboolean parole_provider_player_seek (ParoleProviderPlayer *player,
gdouble pos);
+
+gdouble parole_provider_player_get_stream_position(ParoleProviderPlayer *player);
void
parole_provider_player_open_media_chooser (ParoleProviderPlayer *player);
GtkAction *parole_provider_player_get_action(ParoleProviderPlayer *player, ParolePlayerAction action);
+gboolean parole_provider_player_get_fullscreen(ParoleProviderPlayer *player);
+
+gboolean parole_provider_player_set_fullscreen(ParoleProviderPlayer *player,
+ gboolean fullscreen);
+
G_END_DECLS
#endif /* __PAROLE_PLUGIN_IFACE_H__ */
diff --git a/src/misc/parole-stream.c b/src/misc/parole-stream.c
index dc403f9..3db0b61 100644
--- a/src/misc/parole-stream.c
+++ b/src/misc/parole-stream.c
@@ -29,6 +29,7 @@
#include <string.h>
#include <glib.h>
+#include <glib/gstdio.h>
#include "parole-stream.h"
#include "parole-enum-types.h"
@@ -70,7 +71,10 @@ struct _ParoleStreamPrivate
gchar *year;
gchar *album;
gchar *comment;
+ gchar *genre;
+ guint bitrate;
GdkPixbuf *image;
+ gchar *image_uri, *previous_image;
ParoleMediaType media_type;
};
@@ -98,7 +102,10 @@ enum
PROP_ARTIST,
PROP_YEAR,
PROP_ALBUM,
- PROP_COMMENT
+ PROP_COMMENT,
+ PROP_GENRE,
+ PROP_BITRATE,
+ PROP_IMAGE_URI
};
G_DEFINE_TYPE (ParoleStream, parole_stream, G_TYPE_OBJECT)
@@ -149,6 +156,11 @@ static void parole_stream_set_property (GObject *object,
parole_stream_get_media_type_from_uri (stream, priv->uri);
break;
}
+ case PROP_IMAGE_URI:
+ {
+ PAROLE_STREAM_GET_PRIVATE (stream)->image_uri = g_value_dup_string (value);
+ break;
+ }
case PROP_SUBTITLES:
PAROLE_STREAM_DUP_GVALUE_STRING (PAROLE_STREAM_GET_PRIVATE (stream)->subtitles, value);
break;
@@ -217,6 +229,12 @@ static void parole_stream_set_property (GObject *object,
case PROP_COMMENT:
PAROLE_STREAM_DUP_GVALUE_STRING (PAROLE_STREAM_GET_PRIVATE (stream)->comment, value);
break;
+ case PROP_GENRE:
+ PAROLE_STREAM_DUP_GVALUE_STRING (PAROLE_STREAM_GET_PRIVATE (stream)->genre, value);
+ break;
+ case PROP_BITRATE:
+ PAROLE_STREAM_GET_PRIVATE (stream)->bitrate = g_value_get_uint (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -236,6 +254,9 @@ static void parole_stream_get_property (GObject *object,
case PROP_URI:
g_value_set_string (value, PAROLE_STREAM_GET_PRIVATE (stream)->uri);
break;
+ case PROP_IMAGE_URI:
+ g_value_set_string (value, PAROLE_STREAM_GET_PRIVATE (stream)->image_uri);
+ break;
case PROP_SUBTITLES:
g_value_set_string (value, PAROLE_STREAM_GET_PRIVATE (stream)->subtitles);
break;
@@ -296,6 +317,12 @@ static void parole_stream_get_property (GObject *object,
case PROP_COMMENT:
g_value_set_string (value, PAROLE_STREAM_GET_PRIVATE (stream)->comment);
break;
+ case PROP_GENRE:
+ g_value_set_string (value, PAROLE_STREAM_GET_PRIVATE (stream)->genre);
+ break;
+ case PROP_BITRATE:
+ g_value_set_uint (value, PAROLE_STREAM_GET_PRIVATE (stream)->bitrate);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -318,6 +345,8 @@ void
parole_stream_set_image (GObject *object, GdkPixbuf *pixbuf)
{
ParoleStream *stream;
+ gchar *filename = NULL;
+ gint fid;
stream = PAROLE_STREAM (object);
@@ -325,9 +354,24 @@ parole_stream_set_image (GObject *object, GdkPixbuf *pixbuf)
g_object_unref(G_OBJECT(PAROLE_STREAM_GET_PRIVATE (stream)->image));
if (pixbuf)
+ {
PAROLE_STREAM_GET_PRIVATE (stream)->image = gdk_pixbuf_copy(pixbuf);
+
+ /* Create a jpeg of the artwork for other components to easily access */
+ fid = g_file_open_tmp ("parole-art-XXXXXX.jpg", &filename, NULL);
+ close(fid);
+ gdk_pixbuf_save (pixbuf, filename, "jpeg", NULL, "quality", "100", NULL);
+
+ PAROLE_STREAM_GET_PRIVATE (stream)->previous_image = g_strdup(filename);
+ PAROLE_STREAM_GET_PRIVATE (stream)->image_uri = g_strdup_printf("file://%s", filename);
+ g_free(filename);
+ }
else
+ {
PAROLE_STREAM_GET_PRIVATE (stream)->image = NULL;
+ PAROLE_STREAM_GET_PRIVATE (stream)->previous_image = NULL;
+ PAROLE_STREAM_GET_PRIVATE (stream)->image_uri = g_strdup_printf ("file://%s/no-cover.png", PIXMAPS_DIR);
+ }
}
GdkPixbuf *
@@ -683,6 +727,52 @@ parole_stream_class_init (ParoleStreamClass *klass)
"Comment",
NULL,
G_PARAM_READWRITE));
+
+ /**
+ * ParoleStream:genre:
+ *
+ * Genre.
+ *
+ * Since: 0.6
+ **/
+ g_object_class_install_property (object_class,
+ PROP_GENRE,
+ g_param_spec_string ("genre",
+ "Genre",
+ "Genre",
+ NULL,
+ G_PARAM_READWRITE));
+
+ /**
+ * ParoleStream:image_uri:
+ *
+ * URI for the currently playing album's artwork.
+ *
+ * Since: 0.6
+ **/
+ g_object_class_install_property (object_class,
+ PROP_IMAGE_URI,
+ g_param_spec_string ("image_uri",
+ "Image URI",
+ "URI for the album artwork",
+ NULL,
+ G_PARAM_READWRITE));
+
+ /**
+ * ParoleStream:bitrate:
+ *
+ * Current bitrate in bits/s.
+ *
+ * Since: 0.6
+ **/
+ g_object_class_install_property (object_class,
+ PROP_BITRATE,
+ g_param_spec_uint ("bitrate",
+ "Bitrate",
+ "Bitrate",
+ 0, 2147483647,
+ 0,
+ G_PARAM_READWRITE));
g_type_class_add_private (klass, sizeof (ParoleStreamPrivate));
}
@@ -721,6 +811,7 @@ void parole_stream_init_properties (ParoleStream *stream)
priv->track = 1;
priv->disp_par_n = 1;
priv->disp_par_d = 1;
+ priv->bitrate = 0;
PAROLE_STREAM_FREE_STR_PROP (priv->title);
PAROLE_STREAM_FREE_STR_PROP (priv->uri);
@@ -729,4 +820,13 @@ void parole_stream_init_properties (ParoleStream *stream)
PAROLE_STREAM_FREE_STR_PROP (priv->year);
PAROLE_STREAM_FREE_STR_PROP (priv->album);
PAROLE_STREAM_FREE_STR_PROP (priv->comment);
+ PAROLE_STREAM_FREE_STR_PROP (priv->genre);
+ PAROLE_STREAM_FREE_STR_PROP (priv->image_uri);
+
+ /* Remove the previous image if it exists */
+ if ( PAROLE_STREAM_GET_PRIVATE (stream)->previous_image )
+ {
+ g_remove (PAROLE_STREAM_GET_PRIVATE (stream)->previous_image);
+ }
+ PAROLE_STREAM_GET_PRIVATE (stream)->previous_image = NULL;
}
diff --git a/src/parole-conf-dialog.c b/src/parole-conf-dialog.c
index bdc1fa5..cb76a5b 100644
--- a/src/parole-conf-dialog.c
+++ b/src/parole-conf-dialog.c
@@ -52,9 +52,9 @@ parole_conf_dialog_vis_plugin_changed_cb (GtkComboBox *widget,
void parole_conf_dialog_font_set_cb (GtkFontButton *button,
ParoleConfDialog *self);
-void parole_conf_dialog_enable_vis_changed_cb (GObject *object,
- GParamSpec *pspec,
- gpointer *data);
+void parole_conf_dialog_enable_vis_changed_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer *data);
void
parole_conf_dialog_subtitle_encoding_changed_cb (GtkComboBox *widget,
@@ -305,87 +305,17 @@ parole_conf_dialog_set_default_vis_plugin (GtkTreeModel *model, GtkTreePath *pat
return ret;
}
-/* Load the default playlist settings */
-static void
-parole_conf_dialog_set_defaults_playlist (ParoleConfDialog *self, GtkBuilder *builder)
-{
- GtkWidget *widget;
- gboolean option;
-
- /**
- * Replace playlist with opened files.
- **/
- widget = GTK_WIDGET (gtk_builder_get_object (builder, "replace-playlist"));
-
- g_object_get (G_OBJECT (self->priv->conf),
- "replace-playlist", &option,
- NULL);
-
- gtk_switch_set_active (GTK_SWITCH (widget), option);
-
- /**
- * Start playing opened files
- **/
- widget = GTK_WIDGET (gtk_builder_get_object (builder, "start-playing-opened"));
-
- g_object_get (G_OBJECT (self->priv->conf),
- "play-opened-files", &option,
- NULL);
-
- gtk_switch_set_active (GTK_SWITCH (widget), option);
-
- /**
- * Remove duplicated playlist entries
- **/
- widget = GTK_WIDGET (gtk_builder_get_object (builder, "remove-duplicated"));
-
- g_object_get (G_OBJECT (self->priv->conf),
- "remove-duplicated", &option,
- NULL);
-
- gtk_switch_set_active (GTK_SWITCH (widget), option);
-
- /**
- * Remember playlist
- **/
- widget = GTK_WIDGET (gtk_builder_get_object (builder, "remember-playlist"));
-
- g_object_get (G_OBJECT (self->priv->conf),
- "remember-playlist", &option,
- NULL);
-
- gtk_switch_set_active (GTK_SWITCH (widget), option);
-}
-
-/* Load the multimedia-button default settings */
-static void
-parole_conf_dialog_set_defaults_general (ParoleConfDialog *self, GtkBuilder *builder)
-{
- GtkWidget *widget;
- gboolean option;
-
- widget = GTK_WIDGET (gtk_builder_get_object (builder, "multimedia-keys"));
-
- g_object_get (G_OBJECT (self->priv->conf),
- "multimedia-keys", &option,
- NULL);
-
- gtk_switch_set_active (GTK_SWITCH (widget), option);
-}
-
/* Load the rest of the settings stored in the rc file */
static void
parole_conf_dialog_set_defaults (ParoleConfDialog *self)
{
GtkTreeModel *model;
gboolean vis_enabled;
- gboolean subtitle;
gchar *subtitle_font;
gchar *subtitle_encoding;
g_object_get (G_OBJECT (self->priv->conf),
"vis-enabled", &vis_enabled,
- "enable-subtitle", &subtitle,
"subtitle-font", &subtitle_font,
"subtitle-encoding", &subtitle_encoding,
NULL);
@@ -393,9 +323,6 @@ parole_conf_dialog_set_defaults (ParoleConfDialog *self)
/* Update widget-states according to settings */
gtk_widget_set_sensitive (self->priv->vis_combox, vis_enabled);
- gtk_switch_set_active (GTK_SWITCH (self->priv->toggle_vis), vis_enabled);
- gtk_switch_set_active (GTK_SWITCH (self->priv->toggle_subtitle), subtitle);
-
model = gtk_combo_box_get_model (GTK_COMBO_BOX (self->priv->vis_combox));
gtk_tree_model_foreach (model,
@@ -448,8 +375,6 @@ void parole_conf_dialog_open (ParoleConfDialog *self, GtkWidget *parent)
self->priv->vis_combox = combox;
parole_conf_dialog_set_defaults (self);
- parole_conf_dialog_set_defaults_general (self, builder);
- parole_conf_dialog_set_defaults_playlist (self, builder);
with_display = parole_gst_get_is_xvimage_sink (PAROLE_GST (parole_gst_get ()));
@@ -508,50 +433,50 @@ void parole_conf_dialog_open (ParoleConfDialog *self, GtkWidget *parent)
switch_widget = GTK_WIDGET (gtk_builder_get_object (builder, "reset-saver"));
g_object_bind_property(G_OBJECT (self->priv->conf), "reset-saver",
switch_widget, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
/* General/Audio/Enable visualization while playing audio file */
switch_widget = GTK_WIDGET (gtk_builder_get_object (builder, "enable-vis"));
g_object_bind_property(G_OBJECT (self->priv->conf), "vis-enabled",
switch_widget, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
g_signal_connect(G_OBJECT(switch_widget), "notify::active", G_CALLBACK(parole_conf_dialog_enable_vis_changed_cb), self);
/* General/Keyboard/Enable keyboard multimedia keys */
switch_widget = GTK_WIDGET (gtk_builder_get_object (builder, "multimedia-keys"));
g_object_bind_property(G_OBJECT (self->priv->conf), "multimedia-keys",
switch_widget, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
/* Playlist/Always replace playlist with opened files */
switch_widget = GTK_WIDGET (gtk_builder_get_object (builder, "replace-playlist"));
g_object_bind_property(G_OBJECT (self->priv->conf), "replace-playlist",
switch_widget, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
/* Playlist/Check and remove duplicate media entries */
switch_widget = GTK_WIDGET (gtk_builder_get_object (builder, "remove-duplicated"));
g_object_bind_property(G_OBJECT (self->priv->conf), "remove-duplicated",
switch_widget, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
/* Playlist/Start playing opened files */
switch_widget = GTK_WIDGET (gtk_builder_get_object (builder, "start-playing-opened"));
g_object_bind_property(G_OBJECT (self->priv->conf), "play-opened-files",
switch_widget, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
/* Playlist/Remember playlist */
switch_widget = GTK_WIDGET (gtk_builder_get_object (builder, "remember-playlist"));
g_object_bind_property(G_OBJECT (self->priv->conf), "remember-playlist",
switch_widget, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
/* Subtitles/Automatically show subtitles when playing movie file */
switch_widget = GTK_WIDGET (gtk_builder_get_object (builder, "enable-subtitle"));
g_object_bind_property(G_OBJECT (self->priv->conf), "enable-subtitle",
switch_widget, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
g_signal_connect(G_OBJECT(dialog), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
diff --git a/src/parole-conf.c b/src/parole-conf.c
index a18e001..ccee601 100644
--- a/src/parole-conf.c
+++ b/src/parole-conf.c
@@ -188,6 +188,21 @@ static void parole_conf_set_property (GObject *object,
/* thaw */
g_signal_handler_unblock (conf->channel, conf->property_changed_id);
+
+ /* now we can notify the plugins */
+ switch(prop_id)
+ {
+ /* sadly this one recurses */
+ case PROP_VOLUME:
+ break;
+
+ /* these can be consumed by plugins */
+ /* case PROP_SHUFFLE: */
+ /* case PROP_REPEAT: */
+ default:
+ parole_conf_prop_changed(conf->channel, xfconf_nick, value, conf);
+ break;
+ }
}
/**
@@ -244,6 +259,7 @@ static void parole_conf_get_property (GObject *object,
}
}
+/* Facilitate the conversion from xfconf property name to parole property name */
gchar *parole_conf_map_xfconf_property_name (const gchar *prop_name)
{
gchar *value = NULL;
@@ -326,6 +342,8 @@ static void parole_conf_prop_changed (XfconfChannel *channel,
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (conf), parole_conf_map_xfconf_property_name(prop_name));
if (G_LIKELY (pspec != NULL))
g_object_notify_by_pspec (G_OBJECT (conf), pspec);
+
+ g_debug("Propchange:%s,%p", prop_name, pspec);
}
/**
diff --git a/src/parole-medialist.c b/src/parole-medialist.c
index c07671b..00f46f5 100644
--- a/src/parole-medialist.c
+++ b/src/parole-medialist.c
@@ -437,23 +437,29 @@ parole_media_list_files_opened_cb (ParoleMediaChooser *chooser,
parole_media_list_files_open (list, files, FALSE, play);
}
-static void
-parole_media_list_location_opened_cb (ParoleOpenLocation *obj, const gchar *location, ParoleMediaList *list)
+void
+parole_media_list_open_uri (ParoleMediaList *list, const gchar *uri)
{
ParoleFile *file;
- if ( parole_is_uri_disc (location) )
+ if ( parole_is_uri_disc (uri) )
{
- g_signal_emit (G_OBJECT (list), signals [URI_OPENED], 0, location);
+ g_signal_emit (G_OBJECT (list), signals [URI_OPENED], 0, uri);
}
else
{
- file = parole_file_new (location);
+ file = parole_file_new (uri);
parole_media_list_add (list, file, FALSE, TRUE, TRUE);
}
}
static void
+parole_media_list_location_opened_cb (ParoleOpenLocation *obj, const gchar *location, ParoleMediaList *list)
+{
+ parole_media_list_open_uri(list, location);
+}
+
+static void
parole_media_list_iso_opened_cb (ParoleMediaChooser *chooser,
gchar *filename,
ParoleMediaList *list)
diff --git a/src/parole-medialist.h b/src/parole-medialist.h
index 6e3fca2..b441e5f 100644
--- a/src/parole-medialist.h
+++ b/src/parole-medialist.h
@@ -163,6 +163,9 @@ void parole_media_list_open (ParoleMediaList *list);
void parole_media_list_open_location (ParoleMediaList *list);
+void parole_media_list_open_uri (ParoleMediaList *list,
+ const gchar *uri);
+
gboolean parole_media_list_add_files (ParoleMediaList *list,
gchar **filenames,
gboolean enqueue);
diff --git a/src/parole-player.c b/src/parole-player.c
index 9cb0f90..4a3954d 100644
--- a/src/parole-player.c
+++ b/src/parole-player.c
@@ -345,6 +345,8 @@ struct ParolePlayerPrivate
GtkFileFilter *video_filter;
GtkRecentManager *recent;
+
+ gdouble last_volume;
GtkWidget *window;
GtkWidget *playlist_nt;
@@ -2218,10 +2220,16 @@ void
parole_player_volume_value_changed_cb (GtkScaleButton *widget, gdouble value, ParolePlayer *player)
{
parole_player_change_volume (player, value);
- if ( value > 0.0 )
- g_object_set (G_OBJECT (player->priv->conf),
- "volume", (gint)(value * 100),
- NULL);
+
+ /* Do not update the value unless it has changed! */
+ if ((int)(value*100) != (int)(player->priv->last_volume*100))
+ {
+ player->priv->last_volume = value;
+ if ( value > 0.0 )
+ g_object_set (G_OBJECT (player->priv->conf),
+ "volume", (gint)(value * 100),
+ NULL);
+ }
}
void
@@ -2860,8 +2868,6 @@ parole_player_init (ParolePlayer *player)
GtkWidget *bug_report;
- gboolean repeat, shuffle;
-
GtkCellRenderer *cell, *sub_cell;
GtkWidget *hbox_infobar;
@@ -2989,7 +2995,7 @@ parole_player_init (ParolePlayer *player)
gtk_action_set_icon_name(GTK_ACTION(player->priv->toggle_repeat_action), "media-playlist-repeat-symbolic");
g_object_bind_property(G_OBJECT (player->priv->conf), "repeat",
player->priv->toggle_repeat_action, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
gtk_action_set_sensitive(GTK_ACTION(player->priv->toggle_repeat_action), TRUE);
/* Toggle Shuffle */
@@ -2997,7 +3003,7 @@ parole_player_init (ParolePlayer *player)
gtk_action_set_icon_name(GTK_ACTION(player->priv->toggle_shuffle_action), "media-playlist-shuffle-symbolic");
g_object_bind_property(G_OBJECT (player->priv->conf), "shuffle",
player->priv->toggle_shuffle_action, "active",
- G_BINDING_BIDIRECTIONAL);
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
gtk_action_set_sensitive(GTK_ACTION(player->priv->toggle_shuffle_action), TRUE);
@@ -3006,7 +3012,7 @@ parole_player_init (ParolePlayer *player)
*/
/* ParolePlayer Window */
player->priv->window = GTK_WIDGET (gtk_builder_get_object (builder, "main-window"));
- g_signal_connect( G_OBJECT(player->priv->window),
+ g_signal_connect_after( G_OBJECT(player->priv->window),
"window-state-event",
G_CALLBACK(parole_player_window_state_event),
PAROLE_PLAYER(player) );
@@ -3360,15 +3366,6 @@ parole_player_init (ParolePlayer *player)
* Load auto saved media list.
*/
parole_media_list_load (player->priv->list);
-
- g_object_get (G_OBJECT (player->priv->conf),
- "repeat", &repeat,
- "shuffle", &shuffle,
- NULL);
-
- gtk_toggle_action_set_active (player->priv->toggle_repeat_action, repeat);
-
- gtk_toggle_action_set_active (player->priv->toggle_shuffle_action, shuffle);
parole_gst_set_default_aspect_ratio (player, builder);
diff --git a/src/parole-plugin-player.c b/src/parole-plugin-player.c
index 2700cd8..191f190 100644
--- a/src/parole-plugin-player.c
+++ b/src/parole-plugin-player.c
@@ -51,7 +51,10 @@ struct ParolePluginPlayerPrivate
gulong state_changed;
gulong tag_message;
+ gulong seeked;
gboolean packed;
+
+ gboolean fullscreen;
};
@@ -122,11 +125,12 @@ parole_plugin_player_get_stream (ParoleProviderPlayer *provider)
static gboolean
parole_plugin_player_play_uri (ParoleProviderPlayer *provider, const gchar *uri)
{
- ParolePluginPlayer *player;
-
- player = PAROLE_PLUGIN_PLAYER (provider);
+ ParoleMediaList *list;
+
+ list = PAROLE_MEDIA_LIST (parole_media_list_get ());
+ parole_media_list_open_uri (list, uri);
- parole_gst_play_uri (PAROLE_GST (player->priv->gst), uri, NULL);
+ g_object_unref (list);
return TRUE;
}
@@ -195,6 +199,20 @@ parole_plugin_player_seek (ParoleProviderPlayer *provider, gdouble pos)
return TRUE;
}
+static gdouble
+parole_plugin_player_get_stream_position (ParoleProviderPlayer *provider)
+{
+ ParolePluginPlayer *player;
+ gdouble position = 0;
+
+ player = PAROLE_PLUGIN_PLAYER (provider);
+
+ position = parole_gst_get_stream_position (PAROLE_GST (player->priv->gst));
+
+ /* Return as microseconds */
+ return position*1000000.0;
+}
+
static void parole_plugin_player_open_media_chooser (ParoleProviderPlayer *provider)
{
ParoleMediaList *list;
@@ -205,6 +223,29 @@ static void parole_plugin_player_open_media_chooser (ParoleProviderPlayer *provi
g_object_unref (list);
}
+static gboolean parole_plugin_player_get_fullscreen (ParoleProviderPlayer *provider)
+{
+ ParolePluginPlayer *player;
+
+ player = PAROLE_PLUGIN_PLAYER (provider);
+
+ return player->priv->fullscreen;
+}
+
+static gboolean parole_plugin_player_set_fullscreen (ParoleProviderPlayer *provider, gboolean fullscreen)
+{
+ GtkWidget *window;
+
+ window = parole_plugin_player_get_main_window (provider);
+
+ if (fullscreen)
+ gtk_window_fullscreen(GTK_WINDOW(window));
+ else
+ gtk_window_unfullscreen(GTK_WINDOW(window));
+
+ return TRUE;
+}
+
static void parole_plugin_player_iface_init (ParoleProviderPlayerIface *iface)
{
iface->get_main_window = parole_plugin_player_get_main_window;
@@ -218,7 +259,10 @@ static void parole_plugin_player_iface_init (ParoleProviderPlayerIface *iface)
iface->play_previous = parole_plugin_player_play_previous;
iface->play_next = parole_plugin_player_play_next;
iface->seek = parole_plugin_player_seek;
+ iface->get_stream_position = parole_plugin_player_get_stream_position;
iface->open_media_chooser = parole_plugin_player_open_media_chooser;
+ iface->get_fullscreen = parole_plugin_player_get_fullscreen;
+ iface->set_fullscreen = parole_plugin_player_set_fullscreen;
}
static void
@@ -235,6 +279,24 @@ parole_plugin_player_media_tag_cb (ParoleGst *gst, const ParoleStream *stream, P
}
static void
+parole_plugin_player_media_seeked_cb (ParoleGst *gst, ParolePluginPlayer *player)
+{
+ g_signal_emit_by_name (G_OBJECT (player), "seeked");
+}
+
+static gboolean
+parole_plugin_player_window_state_event (GtkWidget *widget,
+ GdkEventWindowState *event,
+ ParolePluginPlayer *player)
+{
+ if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
+ player->priv->fullscreen = TRUE;
+ else
+ player->priv->fullscreen = FALSE;
+ return FALSE;
+}
+
+static void
parole_plugin_player_class_init (ParolePluginPlayerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -247,6 +309,7 @@ parole_plugin_player_class_init (ParolePluginPlayerClass *klass)
static void
parole_plugin_player_init (ParolePluginPlayer *player)
{
+ GtkWidget *window;
player->priv = PAROLE_PLUGIN_PLAYER_GET_PRIVATE (player);
player->priv->gst = parole_gst_get ();
@@ -256,6 +319,16 @@ parole_plugin_player_init (ParolePluginPlayer *player)
player->priv->tag_message = g_signal_connect (G_OBJECT (player->priv->gst), "media-tag",
G_CALLBACK (parole_plugin_player_media_tag_cb), player);
+
+ player->priv->seeked = g_signal_connect (G_OBJECT (player->priv->gst), "media-seeked",
+ G_CALLBACK (parole_plugin_player_media_seeked_cb), player);
+
+ player->priv->fullscreen = FALSE;
+ window = GTK_WIDGET(gtk_widget_get_toplevel (player->priv->gst));
+ g_signal_connect( G_OBJECT(window),
+ "window-state-event",
+ G_CALLBACK(parole_plugin_player_window_state_event),
+ player );
player->priv->packed = FALSE;
player->priv->box = NULL;
@@ -275,6 +348,9 @@ parole_plugin_player_finalize (GObject *object)
if (g_signal_handler_is_connected (player->priv->gst, player->priv->tag_message))
g_signal_handler_disconnect (player->priv->gst, player->priv->tag_message);
+
+ if (g_signal_handler_is_connected (player->priv->gst, player->priv->seeked))
+ g_signal_handler_disconnect (player->priv->gst, player->priv->seeked);
}
if ( player->priv->packed && GTK_IS_WIDGET (player->priv->box))
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index 7b2439b..9089e4a 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -6,4 +6,8 @@ endif
if PAROLE_NOTIFY_PLUGIN
SUBDIRS+=notify
-endif
\ No newline at end of file
+endif
+
+if PAROLE_MPRIS2_PLUGIN
+SUBDIRS+=mpris2
+endif
diff --git a/src/plugins/tray/Makefile.am b/src/plugins/mpris2/Makefile.am
similarity index 68%
copy from src/plugins/tray/Makefile.am
copy to src/plugins/mpris2/Makefile.am
index fd076c4..ba9f37d 100644
--- a/src/plugins/tray/Makefile.am
+++ b/src/plugins/mpris2/Makefile.am
@@ -2,7 +2,7 @@ INCLUDES = \
-I$(top_builddir) \
-I$(top_srcdir) \
-I$(top_srcdir)/src \
- -DG_LOG_DOMAIN=\"parole_tray\" \
+ -DG_LOG_DOMAIN=\"mpris2_plugin\" \
-DLIBEXECDIR=\"$(libexecdir)\" \
-DPACKAGE_LOCALE_DIR=\"$(localedir)\"
@@ -10,36 +10,35 @@ pluginsdir = \
$(libdir)/parole-$(PAROLE_VERSION_API)
plugins_LTLIBRARIES = \
- tray-icon.la
+ parole-mpris2.la
-tray_icon_la_SOURCES = \
- tray-plugin.c \
- tray-provider.c \
- tray-provider.h
+parole_mpris2_la_SOURCES = \
+ mpris2-plugin.c \
+ mpris2-provider.c \
+ mpris2-provider.h
-tray_icon_la_CFLAGS = \
+parole_mpris2_la_CFLAGS = \
$(PLATFORM_CFLAGS) \
$(GTK_CFLAGS) \
- $(XFCONF_CFLAGS) \
- $(LIBXFCE4UI_CFLAGS) \
+ $(GST_INTERFACES_CFLAGS) \
$(LIBXFCE4UTIL_CFLAGS)
-tray_icon_la_LDFLAGS = \
+parole_mpris2_la_LDFLAGS = \
-avoid-version \
-export-dynamic \
-module \
$(PLATFORM_LDFLAGS)
-tray_icon_la_LIBADD = \
+parole_mpris2_la_LIBADD = \
$(top_builddir)/src/misc/libparole.la \
$(GTK_LIBS) \
- $(XFCONF_LIBS) \
+ $(GST_LIBS) \
$(LIBXFCE4UTIL_LIBS)
#
# .desktop file
#
-desktop_in_files = system-tray.desktop.in
+desktop_in_files = mpris2.desktop.in
desktopdir = $(datadir)/parole/parole-plugins-$(PAROLE_VERSION_API)
desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
@INTLTOOL_DESKTOP_RULE@
diff --git a/src/plugins/sample/sample-plugin.c b/src/plugins/mpris2/mpris2-plugin.c
similarity index 92%
copy from src/plugins/sample/sample-plugin.c
copy to src/plugins/mpris2/mpris2-plugin.c
index f842459..80d70d1 100644
--- a/src/plugins/sample/sample-plugin.c
+++ b/src/plugins/mpris2/mpris2-plugin.c
@@ -24,7 +24,7 @@
#include <src/misc/parole.h>
-#include "sample-provider.h"
+#include "mpris2-provider.h"
G_MODULE_EXPORT GType parole_plugin_initialize (ParoleProviderPlugin *plugin);
@@ -34,8 +34,8 @@ G_MODULE_EXPORT GType
parole_plugin_initialize (ParoleProviderPlugin *plugin)
{
xfce_textdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
- sample_provider_register_type (plugin);
- return SAMPLE_TYPE_PROVIDER;
+ mpris2_provider_register_type (plugin);
+ return MPRIS2_TYPE_PROVIDER;
}
G_MODULE_EXPORT void
diff --git a/src/plugins/mpris2/mpris2-provider.c b/src/plugins/mpris2/mpris2-provider.c
new file mode 100644
index 0000000..ef9d979
--- /dev/null
+++ b/src/plugins/mpris2/mpris2-provider.c
@@ -0,0 +1,1096 @@
+/*
+ * * Copyright (C) 2009-2011 Ali <aliov at xfce.org>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "mpris2-provider.h"
+
+#include <gst/tag/tag.h>
+
+static void mpris2_provider_iface_init (ParoleProviderPluginIface *iface);
+static void mpris2_provider_finalize (GObject *object);
+
+#define MPRIS_NAME "org.mpris.MediaPlayer2.parole"
+#define MPRIS_PATH "/org/mpris/MediaPlayer2"
+
+struct _Mpris2ProviderClass
+{
+ GObjectClass parent_class;
+};
+
+struct _Mpris2Provider
+{
+ GObject parent;
+ ParoleProviderPlayer *player;
+ ParoleConf *conf;
+
+ guint owner_id;
+ guint registration_id0;
+ guint registration_id1;
+ GDBusNodeInfo *introspection_data;
+ GDBusConnection *dbus_connection;
+ GQuark interface_quarks[2];
+
+ gboolean saved_playbackstatus;
+ gboolean saved_shuffle;
+ gboolean saved_fullscreen;
+ gchar *saved_title;
+ gdouble volume;
+ ParoleState state;
+};
+
+PAROLE_DEFINE_TYPE_WITH_CODE (Mpris2Provider,
+ mpris2_provider,
+ G_TYPE_OBJECT,
+ PAROLE_IMPLEMENT_INTERFACE (PAROLE_TYPE_PROVIDER_PLUGIN,
+ mpris2_provider_iface_init));
+
+static const gchar mpris2xml[] =
+"<node>"
+" <interface name='org.mpris.MediaPlayer2'>"
+" <method name='Raise'/>"
+" <method name='Quit'/>"
+" <property name='CanQuit' type='b' access='read'/>"
+" <property name='CanRaise' type='b' access='read'/>"
+" <property name='HasTrackList' type='b' access='read'/>"
+" <property name='Identity' type='s' access='read'/>"
+" <property name='DesktopEntry' type='s' access='read'/>"
+" <property name='SupportedUriSchemes' type='as' access='read'/>"
+" <property name='SupportedMimeTypes' type='as' access='read'/>"
+" <property name='Fullscreen' type='b' access='readwrite'/>"
+" <property name='CanSetFullscreen' type='b' access='read'/>"
+" </interface>"
+" <interface name='org.mpris.MediaPlayer2.Player'>"
+" <method name='Next'/>"
+" <method name='Previous'/>"
+" <method name='Pause'/>"
+" <method name='PlayPause'/>"
+" <method name='Stop'/>"
+" <method name='Play'/>"
+" <method name='Seek'>"
+" <arg direction='in' name='Offset' type='x'/>"
+" </method>"
+" <method name='SetPosition'>"
+" <arg direction='in' name='TrackId' type='o'/>"
+" <arg direction='in' name='Position' type='x'/>"
+" </method>"
+" <method name='OpenUri'>"
+" <arg direction='in' name='Uri' type='s'/>"
+" </method>"
+" <signal name='Seeked'><arg name='Position' type='x'/></signal>"
+" <property name='PlaybackStatus' type='s' access='read'/>"
+" <property name='LoopStatus' type='s' access='readwrite'/>"
+" <property name='Rate' type='d' access='readwrite'/>"
+" <property name='Shuffle' type='b' access='readwrite'/>"
+" <property name='Metadata' type='a{sv}' access='read'/>"
+" <property name='Volume' type='d' access='readwrite'/>"
+" <property name='Position' type='x' access='read'/>"
+" <property name='MinimumRate' type='d' access='read'/>"
+" <property name='MaximumRate' type='d' access='read'/>"
+" <property name='CanGoNext' type='b' access='read'/>"
+" <property name='CanGoPrevious' type='b' access='read'/>"
+" <property name='CanPlay' type='b' access='read'/>"
+" <property name='CanPause' type='b' access='read'/>"
+" <property name='CanSeek' type='b' access='read'/>"
+" <property name='CanControl' type='b' access='read'/>"
+" </interface>"
+"</node>";
+
+/* some MFCisms */
+#define BEGIN_INTERFACE(x) \
+ if(g_quark_try_string(interface_name)==provider->interface_quarks[x]) {
+#define MAP_METHOD(x,y) \
+ if(!g_strcmp0(#y, method_name)) { \
+ mpris_##x##_##y(invocation, parameters, provider); return; }
+#define PROPGET(x,y) \
+ if(!g_strcmp0(#y, property_name)) \
+ return mpris_##x##_get_##y(error, provider);
+#define PROPPUT(x,y) \
+ if(g_quark_try_string(property_name)==g_quark_from_static_string(#y)) \
+ mpris_##x##_put_##y(value, error, provider);
+#define END_INTERFACE }
+
+/*
+ * org.mpris.MediaPlayer2
+ */
+static void mpris_Root_Raise (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ GtkWidget *widget = parole_provider_player_get_main_window(provider->player);
+ if(widget)
+ {
+ GdkWindow *window = gtk_widget_get_window(widget);
+ if(window)
+ {
+ gdk_window_raise(window);
+ }
+ }
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Root_Quit (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ /* TODO: optionally get a real close API since this won't work always */
+ gtk_main_quit();
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static GVariant* mpris_Root_get_CanQuit (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_boolean(TRUE);
+}
+
+static GVariant* mpris_Root_get_CanRaise (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_boolean(TRUE);
+}
+
+static GVariant* mpris_Root_get_Fullscreen (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_boolean(provider->saved_fullscreen);
+}
+
+static void mpris_Root_put_Fullscreen (GVariant *value, GError **error, Mpris2Provider *provider)
+{
+ gboolean fullscreen = g_variant_get_boolean(value);
+
+ GtkWidget *window = parole_provider_player_get_main_window(provider->player);
+ if (window)
+ {
+ if (fullscreen)
+ gtk_window_fullscreen(GTK_WINDOW(window));
+ else
+ gtk_window_unfullscreen(GTK_WINDOW(window));
+ }
+}
+
+static GVariant* mpris_Root_get_CanSetFullscreen (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_boolean(TRUE);
+}
+
+static GVariant* mpris_Root_get_HasTrackList (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_boolean(TRUE);
+}
+
+static GVariant* mpris_Root_get_Identity (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_string(_("Parole Media Player"));
+}
+
+static GVariant* mpris_Root_get_DesktopEntry (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_string("parole");
+}
+
+static GVariant* mpris_Root_get_SupportedUriSchemes (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_parse(G_VARIANT_TYPE("as"),
+ "['cdda', 'dvd', 'file', 'icy', 'icyx', 'mms', 'mmsh', 'net', "
+ "'pnm', 'rtmp', 'rtp', 'rtsp', 'uvox']", NULL, NULL, NULL);
+}
+
+static GVariant* mpris_Root_get_SupportedMimeTypes (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_parse(G_VARIANT_TYPE("as"),
+ "['application/mxf', 'application/ogg', 'application/ram', "
+ "'application/sdp', 'application/vnd.apple.mpegurl', "
+ "'application/vnd.ms-wpl', 'application/vnd.rn-realmedia', "
+ "'application/vnd.rn-realmedia', 'application/x-extension-m4a', "
+ "'application/x-extension-mp4', 'application/x-flac', "
+ "'application/x-flash-video', 'application/x-matroska', "
+ "'application/x-netshow-channel', 'application/x-ogg', "
+ "'application/x-quicktimeplayer', 'application/x-shorten', "
+ "'audio/3gpp', 'audio/ac3', 'audio/AMR', 'audio/AMR-WB', "
+ "'audio/basic', 'audio/flac', 'audio/midi', 'audio/mp2', 'audio/mp4', "
+ "'audio/mpeg', 'audio/ogg', 'audio/prs.sid', 'audio/vnd.rn-realaudio', "
+ "'audio/x-aiff', 'audio/x-ape', 'audio/x-flac', 'audio/x-gsm', "
+ "'audio/x-it', 'audio/x-m4a', 'audio/x-matroska', 'audio/x-mod', "
+ "'audio/x-mp3', 'audio/x-mpeg', 'audio/x-ms-asf', 'audio/x-ms-asx', "
+ "'audio/x-ms-wax', 'audio/x-ms-wma', 'audio/x-musepack', "
+ "'audio/x-pn-aiff', 'audio/x-pn-au', 'audio/x-pn-realaudio', "
+ "'audio/x-pn-wav', 'audio/x-pn-windows-acm', 'audio/x-real-audio', "
+ "'audio/x-realaudio', 'audio/x-s3m', 'audio/x-sbc', 'audio/x-speex', "
+ "'audio/x-stm', 'audio/x-tta', 'audio/x-vorbis', 'audio/x-vorbis+ogg', "
+ "'audio/x-wav', 'audio/x-wavpack', 'audio/x-xm', "
+ "'image/vnd.rn-realpix', 'image/x-pict', "
+ "'text/x-google-video-pointer', 'video/3gp', 'video/3gpp', "
+ "'video/divx', 'video/dv', 'video/fli', 'video/flv', 'video/mp2t', "
+ "'video/mp4', 'video/mp4v-es', 'video/mpeg', 'video/msvideo', "
+ "'video/ogg', 'video/quicktime', 'video/vivo', 'video/vnd.divx', "
+ "'video/vnd.mpegurl', 'video/vnd.rn-realvideo', 'video/vnd.vivo', "
+ "'video/webm', 'video/x-anim', 'video/x-avi', 'video/x-flc', "
+ "'video/x-fli', 'video/x-flic', 'video/x-flv', 'video/x-m4v', "
+ "'video/x-matroska', 'video/x-mpeg', 'video/x-mpeg2', "
+ "'video/x-ms-asf', 'video/x-ms-asx', 'video/x-msvideo', "
+ "'video/x-ms-wm', 'video/x-ms-wmv', 'video/x-ms-wmx', "
+ "'video/x-ms-wvx', 'video/x-nsv', 'video/x-ogm+ogg', "
+ "'video/x-theora+ogg', 'video/x-totem-stream']", NULL, NULL, NULL);
+}
+
+/*
+ * org.mpris.MediaPlayer2.Player
+ */
+static void mpris_Player_Play (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ ParoleProviderPlayer *player = provider->player;
+ ParoleState state = parole_provider_player_get_state (player);
+
+ switch(state)
+ {
+ case PAROLE_STATE_PAUSED:
+ parole_provider_player_resume (provider->player);
+ break;
+
+ case PAROLE_STATE_STOPPED:
+ case PAROLE_STATE_PLAYBACK_FINISHED:
+ parole_provider_player_play_next (provider->player);
+ break;
+
+ case PAROLE_STATE_ABOUT_TO_FINISH:
+ case PAROLE_STATE_PLAYING:
+ g_debug("MPRIS: Unexpected: play command while playing");
+ break;
+ }
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Player_Next (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ parole_provider_player_play_next (provider->player);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Player_Previous (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ parole_provider_player_play_previous (provider->player);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Player_Pause (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ parole_provider_player_pause (provider->player);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Player_PlayPause (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ ParoleProviderPlayer *player = provider->player;
+ ParoleState state = parole_provider_player_get_state (player);
+
+ switch(state)
+ {
+ case PAROLE_STATE_PAUSED:
+ parole_provider_player_resume (player);
+ break;
+
+ case PAROLE_STATE_STOPPED:
+ case PAROLE_STATE_PLAYBACK_FINISHED:
+ parole_provider_player_play_next (player);
+ break;
+
+ case PAROLE_STATE_ABOUT_TO_FINISH:
+ case PAROLE_STATE_PLAYING:
+ parole_provider_player_pause(player);
+ break;
+ }
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Player_Stop (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ parole_provider_player_stop (provider->player);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Player_Seek (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ ParoleProviderPlayer *player = provider->player;
+ const ParoleStream *stream = parole_provider_player_get_stream(player);
+ gint64 param;
+ gint64 curr_pos;
+ gint64 seek;
+ gint64 duration;
+
+ if(parole_provider_player_get_state (player) == PAROLE_STATE_STOPPED) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Nothing to seek");
+ return;
+ }
+
+ g_variant_get(parameters, "(x)", ¶m);
+
+ curr_pos = parole_provider_player_get_stream_position (player);
+ seek = (curr_pos + param) / GST_MSECOND;
+
+ g_object_get (G_OBJECT (stream), "duration", &duration, NULL);
+
+ seek = CLAMP (seek, 0, duration);
+
+ parole_provider_player_seek (player, seek);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Player_SetPosition (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ ParoleProviderPlayer *player = provider->player;
+ const ParoleStream *stream = parole_provider_player_get_stream(player);
+ gchar *track_id = NULL;
+ gint64 param;
+ gint64 seek;
+ gint64 duration;
+
+ if(parole_provider_player_get_state (player) == PAROLE_STATE_STOPPED) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Nothing to seek");
+ return;
+ }
+
+ /* Do we need to do anything with track_id? */
+ g_variant_get(parameters, "(ox)", &track_id, ¶m);
+ g_free(track_id);
+
+ seek = param / GST_MSECOND;
+
+ g_object_get (G_OBJECT (stream), "duration", &duration, NULL);
+
+ seek = CLAMP (seek, 0, duration);
+
+ parole_provider_player_seek (player, seek);
+
+ g_dbus_method_invocation_return_value (invocation, NULL);
+}
+
+static void mpris_Player_OpenUri (GDBusMethodInvocation *invocation, GVariant* parameters, Mpris2Provider *provider)
+{
+ gchar *uri = NULL;
+ gboolean happened = FALSE;
+ ParoleProviderPlayer *player = provider->player;
+
+ g_variant_get(parameters, "(s)", &uri);
+ if (uri) {
+ happened = parole_provider_player_play_uri (player, uri);
+ g_free(uri);
+ }
+
+ if(happened)
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ else
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_FILE_CONTENT,
+ "This file does not play here.");
+}
+
+static GVariant* mpris_Player_get_PlaybackStatus (GError **error, Mpris2Provider *provider)
+{
+ ParoleProviderPlayer *player = provider->player;
+
+ switch (parole_provider_player_get_state(player))
+ {
+ case PAROLE_STATE_ABOUT_TO_FINISH:
+ case PAROLE_STATE_PLAYING:
+ return g_variant_new_string("Playing");
+ case PAROLE_STATE_PAUSED:
+ return g_variant_new_string("Paused");
+ default:
+ return g_variant_new_string("Stopped");
+ }
+}
+
+static GVariant* mpris_Player_get_LoopStatus (GError **error, Mpris2Provider *provider)
+{
+ gboolean repeat = FALSE;
+ g_object_get (G_OBJECT (provider->conf), "repeat", &repeat, NULL);
+
+ return g_variant_new_string(repeat ? "Playlist" : "None");
+}
+
+static void mpris_Player_put_LoopStatus (GVariant *value, GError **error, Mpris2Provider *provider)
+{
+ const gchar *new_loop = g_variant_get_string(value, NULL);
+
+ gboolean repeat = g_strcmp0("Playlist", new_loop) ? FALSE : TRUE;
+
+ g_object_set (G_OBJECT (provider->conf), "repeat", repeat, NULL);
+}
+
+static GVariant* mpris_Player_get_Rate (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_double(1.0);
+}
+
+static void mpris_Player_put_Rate (GVariant *value, GError **error, Mpris2Provider *provider)
+{
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "This is not alsaplayer.");
+}
+
+static GVariant* mpris_Player_get_Shuffle (GError **error, Mpris2Provider *provider)
+{
+ gboolean shuffle = FALSE;
+
+ g_object_get (G_OBJECT (provider->conf), "shuffle", &shuffle, NULL);
+
+ return g_variant_new_boolean(shuffle);
+}
+
+static void mpris_Player_put_Shuffle (GVariant *value, GError **error, Mpris2Provider *provider)
+{
+ gboolean shuffle = g_variant_get_boolean(value);
+
+ g_object_set (G_OBJECT (provider->conf), "shuffle", shuffle, NULL);
+}
+
+static GVariant * handle_get_trackid(const ParoleStream *stream)
+{
+ // TODO: Returning a path requires TrackList interface implementation
+ gchar *o = alloca(260);
+ if(NULL == stream)
+ return g_variant_new_object_path("/");
+
+ g_snprintf(o, 260, "%s/TrackList/%p", MPRIS_PATH, stream);
+
+ return g_variant_new_object_path(o);
+}
+
+static void g_variant_builder_add_array (GVariantBuilder *b, const gchar *tag, const gchar *val)
+{
+ GVariant *vval = NULL, *vvals = NULL;
+
+ if (!val)
+ return;
+
+ vval = g_variant_new_string(val);
+ vvals = g_variant_new_array(G_VARIANT_TYPE_STRING, &vval, 1);
+
+ g_variant_builder_add (b, "{sv}", tag, vvals);
+}
+
+static void g_variant_builder_add_string (GVariantBuilder *b, const gchar *tag, const gchar *val)
+{
+ if (!val)
+ return;
+
+ g_variant_builder_add (b, "{sv}", tag,
+ g_variant_new_string(val));
+}
+
+static void handle_get_metadata (const ParoleStream *stream, GVariantBuilder *b)
+{
+ gchar *title, *album, *artist, *year, *comment, *stream_uri, *genre, *image_uri;
+ gint track_id, bitrate;
+ gint64 duration;
+ gboolean has_video;
+
+ g_object_get (G_OBJECT (stream),
+ "title", &title,
+ "album", &album,
+ "artist", &artist,
+ "year", &year,
+ "comment", &comment,
+ "duration", &duration,
+ "uri", &stream_uri,
+ "genre", &genre,
+ "image_uri", &image_uri,
+ "track", &track_id,
+ "bitrate", &bitrate,
+ "has-video", &has_video,
+ NULL);
+
+ if (has_video)
+ {
+ g_free(image_uri);
+ image_uri = NULL;
+ }
+
+ g_variant_builder_add (b, "{sv}", "mpris:trackid",
+ handle_get_trackid(stream));
+ g_variant_builder_add_string (b, "mpris:artUrl", image_uri);
+ g_variant_builder_add_string (b, "xesam:url", stream_uri);
+ g_variant_builder_add_string (b, "xesam:title", title);
+ g_variant_builder_add_array (b, "xesam:artist", artist);
+ g_variant_builder_add_string (b, "xesam:album", album);
+ g_variant_builder_add_array (b, "xesam:genre", genre);
+ g_variant_builder_add_string (b, "xesam:contentCreated", year);
+ g_variant_builder_add (b, "{sv}", "xesam:trackNumber",
+ g_variant_new_int32(track_id));
+ g_variant_builder_add_array (b, "xesam:comment", comment);
+ g_variant_builder_add (b, "{sv}", "mpris:length",
+ g_variant_new_int64((gint64)duration * 1000000));
+ g_variant_builder_add (b, "{sv}", "audio-bitrate",
+ g_variant_new_int32(bitrate));
+ g_variant_builder_add (b, "{sv}", "audio-channels", // No GST_TAG
+ g_variant_new_int32(0));
+ g_variant_builder_add (b, "{sv}", "audio-samplerate", // No GST_TAG
+ g_variant_new_int32(0));
+
+ g_free(title);
+ g_free(album);
+ g_free(artist);
+ g_free(year);
+ g_free(comment);
+ g_free(stream_uri);
+ g_free(image_uri);
+}
+
+static GVariant* mpris_Player_get_Metadata (GError **error, Mpris2Provider *provider)
+{
+ GVariantBuilder b;
+ const ParoleStream *stream;
+ ParoleProviderPlayer *player = provider->player;
+
+ g_variant_builder_init(&b, G_VARIANT_TYPE ("a{sv}"));
+
+ if (parole_provider_player_get_state(player) != PAROLE_STATE_STOPPED) {
+ stream = parole_provider_player_get_stream(player);
+
+ handle_get_metadata (stream, &b);
+ }
+ else {
+ g_variant_builder_add (&b, "{sv}", "mpris:trackid",
+ handle_get_trackid(NULL));
+ }
+ return g_variant_builder_end(&b);
+}
+
+static gdouble handle_get_volume (Mpris2Provider *provider)
+{
+ gint volume_int = 0;
+ gdouble volume_double;
+
+ g_object_get (G_OBJECT (provider->conf), "volume", &volume_int, NULL);
+ volume_double = (double)volume_int / 100.0;
+
+ return volume_double;
+}
+
+static GVariant* mpris_Player_get_Volume (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_double(handle_get_volume(provider));
+}
+
+static void mpris_Player_put_Volume (GVariant *value, GError **error, Mpris2Provider *provider)
+{
+ gdouble volume = g_variant_get_double(value);
+
+ if(volume < 0.0)
+ volume = 0.0;
+ if(volume > 1.0)
+ volume = 1.0;
+
+ g_object_set(G_OBJECT(provider->conf), "volume", (gint) (volume * 100.0), NULL);
+}
+
+static GVariant* mpris_Player_get_Position (GError **error, Mpris2Provider *provider)
+{
+ ParoleProviderPlayer *player = provider->player;
+ gint64 position = 0;
+
+ position = (gint64) parole_provider_player_get_stream_position (player);
+
+ return g_variant_new_int64(position);
+}
+
+static GVariant* mpris_Player_get_MinimumRate (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_double(1.0);
+}
+
+static GVariant* mpris_Player_get_MaximumRate (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_double(1.0);
+}
+
+static GVariant* mpris_Player_get_CanGoNext (GError **error, Mpris2Provider *provider)
+{
+ // do we need to go into such detail?
+ return g_variant_new_boolean(TRUE);
+}
+
+static GVariant* mpris_Player_get_CanGoPrevious (GError **error, Mpris2Provider *provider)
+{
+ // do we need to go into such detail?
+ return g_variant_new_boolean(TRUE);
+}
+
+static GVariant* mpris_Player_get_CanPlay (GError **error, Mpris2Provider *provider)
+{
+ /* The CanPlay property should be true when the player is playing or paused. */
+ ParoleProviderPlayer *player = provider->player;
+ gint state = parole_provider_player_get_state (player);
+ return g_variant_new_boolean (state == PAROLE_STATE_PAUSED || state == PAROLE_STATE_PLAYING);
+}
+
+static GVariant* mpris_Player_get_CanPause (GError **error, Mpris2Provider *provider)
+{
+ /* The CanPause property should be true when the player is playing or paused. */
+ ParoleProviderPlayer *player = provider->player;
+ gint state = parole_provider_player_get_state (player);
+ return g_variant_new_boolean (state == PAROLE_STATE_PAUSED || state == PAROLE_STATE_PLAYING);
+}
+
+static GVariant* mpris_Player_get_CanSeek (GError **error, Mpris2Provider *provider)
+{
+ gboolean seekable = FALSE;
+ ParoleProviderPlayer *player = provider->player;
+
+ const ParoleStream *stream;
+ stream = parole_provider_player_get_stream(player);
+
+ g_object_get (G_OBJECT (stream),
+ "seekable", &seekable,
+ NULL);
+
+ return g_variant_new_boolean (seekable);
+}
+
+static GVariant* mpris_Player_get_CanControl (GError **error, Mpris2Provider *provider)
+{
+ return g_variant_new_boolean(TRUE);
+}
+
+/*
+ * Update state.
+ */
+
+static void parole_mpris_update_any (Mpris2Provider *provider)
+{
+ const ParoleStream *stream;
+ gboolean change_detected = FALSE, shuffle = FALSE, repeat = FALSE;
+ gchar *stream_uri = NULL;
+ GVariantBuilder b;
+ gdouble curr_vol = 0;
+
+ ParoleProviderPlayer *player = provider->player;
+
+ if(NULL == provider->dbus_connection)
+ return; /* better safe than sorry */
+
+ g_debug ("MPRIS: update any");
+
+ stream = parole_provider_player_get_stream(player);
+ g_object_get (G_OBJECT (stream),
+ "uri", &stream_uri,
+ NULL);
+
+ g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
+
+ g_object_get (G_OBJECT (provider->conf), "shuffle", &shuffle, NULL);
+ if(provider->saved_shuffle != shuffle)
+ {
+ change_detected = TRUE;
+ provider->saved_shuffle = shuffle;
+ g_variant_builder_add (&b, "{sv}", "Shuffle", mpris_Player_get_Shuffle (NULL, provider));
+ }
+ if(provider->state != parole_provider_player_get_state (player))
+ {
+ change_detected = TRUE;
+ provider->state = parole_provider_player_get_state (player);
+ g_variant_builder_add (&b, "{sv}", "PlaybackStatus", mpris_Player_get_PlaybackStatus (NULL, provider));
+ g_variant_builder_add (&b, "{sv}", "CanPlay", mpris_Player_get_CanPlay(NULL, provider));
+ g_variant_builder_add (&b, "{sv}", "CanPause", mpris_Player_get_CanPause(NULL, provider));
+ g_variant_builder_add (&b, "{sv}", "CanSeek", mpris_Player_get_CanSeek(NULL, provider));
+ }
+ g_object_get (G_OBJECT (provider->conf), "repeat", &repeat, NULL);
+ if(provider->saved_playbackstatus != repeat)
+ {
+ change_detected = TRUE;
+ provider->saved_playbackstatus = repeat;
+ g_variant_builder_add (&b, "{sv}", "LoopStatus", mpris_Player_get_LoopStatus (NULL, provider));
+ }
+ curr_vol = handle_get_volume(provider);
+ if(provider->volume != curr_vol)
+ {
+ change_detected = TRUE;
+ provider->volume = curr_vol;
+ g_variant_builder_add (&b, "{sv}", "Volume", mpris_Player_get_Volume (NULL, provider));
+ }
+ if (parole_provider_player_get_state (player) == PAROLE_STATE_PLAYING)
+ {
+ if(g_strcmp0(provider->saved_title, stream_uri))
+ {
+ change_detected = TRUE;
+ if(provider->saved_title)
+ g_free(provider->saved_title);
+ if (stream_uri && (stream_uri)[0])
+ provider->saved_title = stream_uri;
+ else
+ provider->saved_title = NULL;
+
+ g_variant_builder_add (&b, "{sv}", "Metadata", mpris_Player_get_Metadata (NULL, provider));
+ }
+ }
+ if (provider->saved_fullscreen != parole_provider_player_get_fullscreen(player))
+ {
+ change_detected = TRUE;
+ provider->saved_fullscreen = !provider->saved_fullscreen;
+ g_variant_builder_add (&b, "{sv}", "Fullscreen", mpris_Root_get_Fullscreen (NULL, provider));
+ }
+ if(change_detected)
+ {
+ GVariant * tuples[] = {
+ g_variant_new_string("org.mpris.MediaPlayer2.Player"),
+ g_variant_builder_end(&b),
+ g_variant_new_strv(NULL, 0)
+ };
+
+ g_dbus_connection_emit_signal(provider->dbus_connection, NULL, MPRIS_PATH,
+ "org.freedesktop.DBus.Properties", "PropertiesChanged",
+ g_variant_new_tuple(tuples, 3) , NULL);
+ }
+ else
+ {
+ g_variant_builder_clear(&b);
+ }
+}
+
+
+static void
+state_changed_cb (ParoleProviderPlayer *player, const ParoleStream *stream, ParoleState state, Mpris2Provider *provider)
+{
+ parole_mpris_update_any (provider);
+}
+
+static void
+seeked_cb (ParoleProviderPlayer *player, Mpris2Provider *provider)
+{
+ gint64 position = 0;
+
+ if(NULL == provider->dbus_connection)
+ return; /* better safe than sorry */
+
+ position = (gint64) parole_provider_player_get_stream_position (provider->player);
+
+ g_dbus_connection_emit_signal(provider->dbus_connection, NULL, MPRIS_PATH,
+ "org.mpris.MediaPlayer2.Player", "Seeked",
+ g_variant_new ("(x)", position), NULL);
+}
+
+static void
+conf_changed_cb (ParoleConf *conf, GParamSpec *pspec, Mpris2Provider *provider)
+{
+ parole_mpris_update_any (provider);
+}
+
+
+/*
+ * Dbus callbacks
+ */
+static void
+handle_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ Mpris2Provider *provider;
+ ParoleProviderPlugin *plugin = user_data;
+ provider = MPRIS2_PROVIDER (plugin);
+
+ /* org.mpris.MediaPlayer2 */
+ BEGIN_INTERFACE(0)
+ MAP_METHOD(Root, Raise)
+ MAP_METHOD(Root, Quit)
+ END_INTERFACE
+ /* org.mpris.MediaPlayer2.Player */
+ BEGIN_INTERFACE(1)
+ MAP_METHOD(Player, Next)
+ MAP_METHOD(Player, Previous)
+ MAP_METHOD(Player, Pause)
+ MAP_METHOD(Player, PlayPause)
+ MAP_METHOD(Player, Stop)
+ MAP_METHOD(Player, Play)
+ MAP_METHOD(Player, Seek)
+ MAP_METHOD(Player, SetPosition)
+ MAP_METHOD(Player, OpenUri)
+ END_INTERFACE
+}
+
+static GVariant *
+handle_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ Mpris2Provider *provider;
+ ParoleProviderPlugin *plugin = user_data;
+ provider = MPRIS2_PROVIDER (plugin);
+
+ /* org.mpris.MediaPlayer2 */
+ BEGIN_INTERFACE(0)
+ PROPGET(Root, CanQuit)
+ PROPGET(Root, CanRaise)
+ PROPGET(Root, HasTrackList)
+ PROPGET(Root, Identity)
+ PROPGET(Root, DesktopEntry)
+ PROPGET(Root, SupportedUriSchemes)
+ PROPGET(Root, SupportedMimeTypes)
+ PROPGET(Root, Fullscreen)
+ PROPGET(Root, CanSetFullscreen)
+ END_INTERFACE
+ /* org.mpris.MediaPlayer2.Player */
+ BEGIN_INTERFACE(1)
+ PROPGET(Player, PlaybackStatus)
+ PROPGET(Player, LoopStatus)
+ PROPGET(Player, Rate)
+ PROPGET(Player, Shuffle)
+ PROPGET(Player, Metadata)
+ PROPGET(Player, Volume)
+ PROPGET(Player, Position)
+ PROPGET(Player, MinimumRate)
+ PROPGET(Player, MaximumRate)
+ PROPGET(Player, CanGoNext)
+ PROPGET(Player, CanGoPrevious)
+ PROPGET(Player, CanPlay)
+ PROPGET(Player, CanPause)
+ PROPGET(Player, CanSeek)
+ PROPGET(Player, CanControl)
+ END_INTERFACE
+
+ return NULL;
+}
+
+static gboolean
+handle_set_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GVariant *value,
+ GError **error,
+ void *user_data)
+{
+ ParoleProviderPlugin *plugin = user_data;
+ Mpris2Provider *provider = MPRIS2_PROVIDER (plugin);
+
+ /* org.mpris.MediaPlayer2 */
+ BEGIN_INTERFACE(0)
+ PROPPUT(Root, Fullscreen)
+ END_INTERFACE
+ /* org.mpris.MediaPlayer2.Player */
+ BEGIN_INTERFACE(1)
+ PROPPUT(Player, LoopStatus)
+ PROPPUT(Player, Rate)
+ PROPPUT(Player, Shuffle)
+ PROPPUT(Player, Volume)
+ END_INTERFACE
+
+ return (NULL == *error);
+}
+
+static const
+GDBusInterfaceVTable interface_vtable =
+{
+ handle_method_call,
+ handle_get_property,
+ handle_set_property
+};
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ Mpris2Provider *provider;
+ guint registration_id;
+
+ ParoleProviderPlugin *plugin = user_data;
+
+ provider = MPRIS2_PROVIDER (plugin);
+
+ provider->interface_quarks[0] = g_quark_from_string(provider->introspection_data->interfaces[0]->name);
+ registration_id = g_dbus_connection_register_object (connection,
+ MPRIS_PATH,
+ provider->introspection_data->interfaces[0],
+ &interface_vtable,
+ plugin, /* user_data */
+ NULL, /* user_data_free_func */
+ NULL); /* GError** */
+ g_assert (registration_id > 0);
+ provider->registration_id0 = registration_id;
+
+ provider->interface_quarks[1] = g_quark_from_string(provider->introspection_data->interfaces[1]->name);
+ registration_id = g_dbus_connection_register_object (connection,
+ MPRIS_PATH,
+ provider->introspection_data->interfaces[1],
+ &interface_vtable,
+ plugin, /* user_data */
+ NULL, /* user_data_free_func */
+ NULL); /* GError** */
+ g_assert (registration_id > 0);
+ provider->registration_id1 = registration_id;
+
+ provider->dbus_connection = connection;
+ g_object_ref(G_OBJECT(provider->dbus_connection));
+}
+
+static void
+on_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ g_debug("MPRIS: Acquired DBus name %s", name);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+
+{
+ Mpris2Provider *provider;
+ ParoleProviderPlugin *plugin = user_data;
+ provider = MPRIS2_PROVIDER (plugin);
+
+ if (NULL != provider->dbus_connection) {
+ g_object_unref(G_OBJECT(provider->dbus_connection));
+ provider->dbus_connection = NULL;
+ }
+
+ g_warning ("Lost DBus name %s", name);
+}
+
+static gboolean
+on_window_state_event (GtkWidget *widget,
+ GdkEventWindowState *event,
+ Mpris2Provider *provider)
+{
+ parole_mpris_update_any (provider);
+ return FALSE;
+}
+
+/*
+ * Plugin interface.
+ */
+
+static gboolean mpris2_provider_is_configurable (ParoleProviderPlugin *plugin)
+{
+ return FALSE;
+}
+
+static void
+mpris2_provider_set_player (ParoleProviderPlugin *plugin, ParoleProviderPlayer *player)
+{
+ Mpris2Provider *provider;
+ GtkWidget *window;
+ provider = MPRIS2_PROVIDER (plugin);
+
+ provider->player = player;
+ provider->saved_fullscreen = FALSE;
+
+ provider->introspection_data = g_dbus_node_info_new_for_xml (mpris2xml, NULL);
+ g_assert (provider->introspection_data != NULL);
+
+ provider->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ MPRIS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_bus_acquired,
+ on_name_acquired,
+ on_name_lost,
+ plugin,
+ NULL);
+
+ g_signal_connect (player, "state_changed",
+ G_CALLBACK (state_changed_cb), plugin);
+
+ g_signal_connect (player, "seeked",
+ G_CALLBACK (seeked_cb), plugin);
+
+ provider->conf = parole_conf_new();
+
+ g_signal_connect (provider->conf, "notify::repeat",
+ G_CALLBACK (conf_changed_cb), plugin);
+
+ g_signal_connect (provider->conf, "notify::volume",
+ G_CALLBACK (conf_changed_cb), plugin);
+
+ window = parole_provider_player_get_main_window(provider->player);
+ g_signal_connect( G_OBJECT(window),
+ "window-state-event",
+ G_CALLBACK(on_window_state_event),
+ provider );
+}
+
+static void
+mpris2_provider_iface_init (ParoleProviderPluginIface *iface)
+{
+ iface->get_is_configurable = mpris2_provider_is_configurable;
+ iface->set_player = mpris2_provider_set_player;
+}
+
+static void mpris2_provider_class_init (Mpris2ProviderClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = mpris2_provider_finalize;
+}
+
+static void mpris2_provider_init (Mpris2Provider *provider)
+{
+ provider->player = NULL;
+ provider->conf = NULL;
+}
+
+static void mpris2_provider_finalize (GObject *object)
+{
+ Mpris2Provider *provider;
+ provider = MPRIS2_PROVIDER (object);
+
+ if (NULL != provider->dbus_connection) {
+ g_dbus_connection_unregister_object (provider->dbus_connection,
+ provider->registration_id0);
+ g_dbus_connection_unregister_object (provider->dbus_connection,
+ provider->registration_id1);
+ }
+
+ if (NULL != provider->dbus_connection)
+ g_bus_unown_name (provider->owner_id);
+
+ if (NULL != provider->introspection_data) {
+ g_dbus_node_info_unref (provider->introspection_data);
+ provider->introspection_data = NULL;
+ }
+
+ if (NULL != provider->dbus_connection) {
+ g_object_unref (G_OBJECT (provider->dbus_connection));
+ provider->dbus_connection = NULL;
+ }
+
+ g_object_unref (provider->conf);
+
+ g_free (provider->saved_title);
+
+ G_OBJECT_CLASS (mpris2_provider_parent_class)->finalize (object);
+}
diff --git a/src/plugins/mpris2/mpris2-provider.h b/src/plugins/mpris2/mpris2-provider.h
new file mode 100644
index 0000000..ae64a97
--- /dev/null
+++ b/src/plugins/mpris2/mpris2-provider.h
@@ -0,0 +1,45 @@
+/*
+ * * Copyright (C) 2009-2011 Ali <aliov at xfce.org>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef MPRIS2_PROVIDER_H_
+#define MPRIS2_PROVIDER_H_
+
+#include <src/misc/parole.h>
+#include <src/parole-conf.h>
+
+G_BEGIN_DECLS
+
+typedef struct _Mpris2ProviderClass Mpris2ProviderClass;
+typedef struct _Mpris2Provider Mpris2Provider;
+
+#define MPRIS2_TYPE_PROVIDER (mpris2_provider_get_type ())
+#define MPRIS2_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MPRIS2_TYPE_PROVIDER, Mpris2Provider))
+#define MPRIS2_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MPRIS2_TYPE_PROVIDER, Mpris2ProviderClass))
+#define MPRIS2_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MPRIS2_TYPE_PROVIDER))
+#define MPRIS2_IS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MPRIS2_TYPE_PROVIDER))
+#define MPRIS2_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MPRIS2_TYPE_PROVIDER, Mpris2ProviderClass))
+
+GType mpris2_provider_get_type (void) G_GNUC_CONST G_GNUC_INTERNAL;
+
+void mpris2_provider_register_type (ParoleProviderPlugin *plugin);
+
+G_END_DECLS
+
+#endif /*MPRIS2_PROVIDER_H_*/
diff --git a/src/plugins/mpris2/mpris2.desktop.in b/src/plugins/mpris2/mpris2.desktop.in
new file mode 100644
index 0000000..37cb641
--- /dev/null
+++ b/src/plugins/mpris2/mpris2.desktop.in
@@ -0,0 +1,6 @@
+[Parole Plugin]
+Module=parole-mpris2
+_Name=MPRIS2
+_Description=MPRIS2 remote control
+Authors=Matias De Lellis mati86dl at gmail.com Hakan Erduman smultimeter at gmail.com
+Website=http://goodies.xfce.org/projects/applications/parole
More information about the Xfce4-commits
mailing list