[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)", &param);
+
+    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, &param);
+    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