Initial support of D-Bus based control interface
authorRafaël Carré <funman@videolan.org>
Sat, 18 Nov 2006 18:37:45 +0000 (18:37 +0000)
committerRafaël Carré <funman@videolan.org>
Sat, 18 Nov 2006 18:37:45 +0000 (18:37 +0000)
configure.ac
modules/control/Modules.am
modules/control/dbus.c [new file with mode: 0644]
modules/control/dbus.h [new file with mode: 0644]

index 6186e9e..78fcd49 100644 (file)
@@ -848,26 +848,44 @@ AC_ARG_ENABLE(dbus,
  
 if test "${enable_dbus}" != "no"
 then
-  PKG_CHECK_MODULES(DBUS, dbus-1 >= 0.92,
-    [ AC_DEFINE( HAVE_DBUS_2, 1, [Define if you have the D-BUS library API >= 0.92] )
+  dnl api stable dbus
+  PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0.0,
+    [ AC_DEFINE( HAVE_DBUS_3, 1, [Define if you have the D-BUS library API >= 1.0.0] )
+      AC_DEFINE( HAVE_DBUS_2, 1, [Define if you have the D-BUS library API >= 0.92] )
       AC_DEFINE( HAVE_DBUS_1, 1, [Define if you have the D-BUS library API >= 0.30] )
       AC_DEFINE( HAVE_DBUS, 1, [Define if you have the D-BUS library] )
       VLC_ADD_LDFLAGS([screensaver],[$DBUS_LIBS])
-      VLC_ADD_CFLAGS([screensaver],[$DBUS_CFLAGS])],
-    dnl older dbus
-    [ PKG_CHECK_MODULES(DBUS, dbus-1 >= 0.30,
-      [ AC_DEFINE( HAVE_DBUS_1, 1, [Define if you have the D-BUS library API >= 0.30 ] )
+      VLC_ADD_CFLAGS([screensaver],[$DBUS_CFLAGS])
+      dnl Check for dbus control interface
+        AC_ARG_ENABLE(dbus-control, [  --enable-dbus-control   D-BUS control interface (default disabled)])
+        if test "${enable_dbus_control}" = "yes"
+        then
+          VLC_ADD_PLUGINS([dbus])
+          VLC_ADD_LDFLAGS([dbus],[$DBUS_LIBS])
+          VLC_ADD_CFLAGS([dbus],[$DBUS_CFLAGS])
+        fi],
+    dnl not too old dbus
+    PKG_CHECK_MODULES(DBUS, dbus-1 >= 0.92,
+      [ AC_DEFINE( HAVE_DBUS_2, 1, [Define if you have the D-BUS library API >= 0.92] )
+        AC_DEFINE( HAVE_DBUS_1, 1, [Define if you have the D-BUS library API >= 0.30] )
         AC_DEFINE( HAVE_DBUS, 1, [Define if you have the D-BUS library] )
         VLC_ADD_LDFLAGS([screensaver],[$DBUS_LIBS])
         VLC_ADD_CFLAGS([screensaver],[$DBUS_CFLAGS])],
-      dnl much older dbus
-      [ PKG_CHECK_MODULES( DBUS, dbus-1, 
-         [AC_DEFINE(HAVE_DBUS, 1, [Define if you have the D-BUS library])
+      dnl older dbus
+      [ PKG_CHECK_MODULES(DBUS, dbus-1 >= 0.30,
+        [ AC_DEFINE( HAVE_DBUS_1, 1, [Define if you have the D-BUS library API >= 0.30 ] )
+          AC_DEFINE( HAVE_DBUS, 1, [Define if you have the D-BUS library] )
           VLC_ADD_LDFLAGS([screensaver],[$DBUS_LIBS])
           VLC_ADD_CFLAGS([screensaver],[$DBUS_CFLAGS])],
-         [AC_MSG_WARN(DBUS library not found)])
-      ]
-   )]
+        dnl much older dbus
+        [ PKG_CHECK_MODULES( DBUS, dbus-1, 
+           [AC_DEFINE(HAVE_DBUS, 1, [Define if you have the D-BUS library])
+            VLC_ADD_LDFLAGS([screensaver],[$DBUS_LIBS])
+            VLC_ADD_CFLAGS([screensaver],[$DBUS_CFLAGS])],
+           [AC_MSG_WARN(DBUS library not found)])
+        ]
+     )]
+    )
   )
 fi
 
index 34e8b5e..bc50699 100644 (file)
@@ -7,3 +7,4 @@ SOURCES_hotkeys = hotkeys.c
 SOURCES_lirc = lirc.c
 SOURCES_rc = rc.c
 SOURCES_motion = motion.c
+SOURCES_dbus = dbus.c dbus.h
diff --git a/modules/control/dbus.c b/modules/control/dbus.c
new file mode 100644 (file)
index 0000000..0b8f392
--- /dev/null
@@ -0,0 +1,383 @@
+/*****************************************************************************
+ * dbus.c : D-Bus control interface
+ *****************************************************************************
+ * Copyright (C) 2006 Rafaël Carré
+ * $Id$
+ *
+ * Author:    Rafaël Carré <funman at videolanorg>
+ *
+ * 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.
+ *****************************************************************************/
+
+/*
+ * D-Bus Specification: 
+ *      http://dbus.freedesktop.org/doc/dbus-specification.html
+ * D-Bus low-level C API (libdbus)
+ *      http://dbus.freedesktop.org/doc/dbus/api/html/index.html
+ */
+
+/*
+ * TODO:
+ *  properties ?
+ *
+ *  macros to read incoming arguments
+ *
+ *  explore different possible types (arrays..)
+ *
+ *  what must we do if org.videolan.vlc already exist on the bus ?
+ *  ( there is more than one vlc instance )
+ */
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#include <dbus/dbus.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h> /* getpid() */
+#include <unistd.h> /* getpid() */
+
+#include "dbus.h"
+
+#include <vlc/vlc.h>
+#include <vlc/intf.h>
+#include <vlc_meta.h>
+#include <vlc_input.h>
+
+/*****************************************************************************
+ * Local prototypes.
+ *****************************************************************************/
+
+static int  Open    ( vlc_object_t * );
+static void Close   ( vlc_object_t * );
+static void Run        ( intf_thread_t * );
+
+
+static int ItemChange( vlc_object_t *p_this, const char *psz_var,
+                    vlc_value_t oldval, vlc_value_t newval, void *p_data );
+
+struct intf_sys_t
+{
+    DBusConnection *p_conn;
+};
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+vlc_module_begin();
+    set_shortname( _("dbus"));
+    set_category( CAT_INTERFACE );
+    set_subcategory( SUBCAT_INTERFACE_CONTROL );
+    set_description( _("D-Bus control interface") );
+    set_capability( "interface", 0 );
+    set_callbacks( Open, Close );
+vlc_module_end();
+
+/*****************************************************************************
+ * Methods
+ *****************************************************************************/
+
+DBUS_METHOD( Nothing )
+{ /* do nothing */
+    REPLY_INIT;
+    REPLY_SEND;
+}
+
+DBUS_METHOD( GetPlayStatus )
+{ /* return a string */
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    char *psz_play;
+    vlc_value_t val;
+    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
+    PL_LOCK;
+    input_thread_t *p_input = p_playlist->p_input;
+
+    if( !p_input )
+        psz_play = strdup( "stopped" );
+    else
+    {
+        var_Get( p_input, "state", &val );
+        if( val.i_int == PAUSE_S )
+            psz_play = strdup( "pause" );
+        else if( val.i_int == PLAYING_S )
+            psz_play = strdup( "playing" );
+        else psz_play = strdup( "unknown" );
+    }
+    
+    PL_UNLOCK;
+    pl_Release( p_playlist );
+
+    ADD_STRING( &psz_play );
+    free( psz_play );
+    REPLY_SEND;
+}
+
+DBUS_METHOD( TogglePause )
+{ /* return a bool: true if playing */
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    vlc_value_t val;
+    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
+    PL_LOCK;
+    input_thread_t *p_input = p_playlist->p_input;
+
+    if( p_input )
+    {
+        var_Get( p_input, "state", &val );
+        if( val.i_int != PAUSE_S )
+        {
+            val.i_int = PAUSE_S;
+        }
+        else
+        {
+            val.i_int = PLAYING_S;
+        }
+        var_Set( p_input, "state", val );
+    }
+    PL_UNLOCK;
+    pl_Release( p_playlist );
+
+    dbus_bool_t pause = ( val.i_int == PLAYING_S ) ? TRUE : FALSE;
+    ADD_BOOL( &pause );
+    REPLY_SEND;
+}
+
+DBUS_SIGNAL( NewInstance )
+{ /* emits a signal with vlc pid */
+    SIGNAL_INIT( "NewInstance" );
+    OUT_ARGUMENTS;
+    dbus_uint32_t i_pid = (dbus_uint32_t) getpid();
+    ADD_UINT32( &i_pid );
+    SIGNAL_SEND;
+}
+
+DBUS_METHOD( AddMRL )
+{ /* add the string to the playlist, and play it if the boolean is true */
+    REPLY_INIT;
+
+    DBusError error;
+    dbus_error_init( &error );
+
+    intf_thread_t *p_intf = (intf_thread_t*) p_this;
+    char *psz_mrl;
+    dbus_bool_t b_play;
+
+    dbus_message_get_args( p_from, &error,
+            DBUS_TYPE_STRING, &psz_mrl,
+            DBUS_TYPE_BOOLEAN, &b_play,
+            DBUS_TYPE_INVALID );
+
+    if( dbus_error_is_set( &error ) )
+    {
+        printf("error: %s\n", error.message );
+        dbus_error_free( &error );
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
+    input_item_t *p_item = input_ItemNew( p_intf, psz_mrl, NULL );
+    if( p_item )
+    {
+        playlist_AddInput( p_playlist, p_item,
+            ( b_play == TRUE ) ? PLAYLIST_GO|PLAYLIST_APPEND : PLAYLIST_APPEND,
+            PLAYLIST_END, VLC_TRUE );
+    }
+    pl_Release( p_playlist );
+
+    REPLY_SEND;
+}
+
+/*****************************************************************************
+ * Introspection method
+ *****************************************************************************/
+
+DBUS_METHOD( handle_introspect )
+{ /* handles introspection of /org/videolan/vlc */
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+    ADD_STRING( &psz_introspection_xml_data );
+    REPLY_SEND;
+}
+
+/*****************************************************************************
+ * handle_messages: answer to incoming messages
+ *****************************************************************************/
+
+#define METHOD_FUNC( method, function ) \
+    else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\
+        return function( p_conn, p_from, p_this )
+
+DBUS_METHOD( handle_messages )
+{ /* the main handler, that call methods */
+
+    if( dbus_message_is_method_call( p_from,
+                DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
+        return handle_introspect( p_conn, p_from, p_this );
+
+    /* here D-Bus method's names are associated to an handler */
+
+    METHOD_FUNC( "GetPlayStatus",   GetPlayStatus );
+    METHOD_FUNC( "AddMRL",          AddMRL );
+    METHOD_FUNC( "TogglePause",     TogglePause );
+    METHOD_FUNC( "Nothing",         Nothing );
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+/*****************************************************************************
+ * Open: initialize interface
+ *****************************************************************************/
+
+static int Open( vlc_object_t *p_this )
+{ /* initialisation of the connection */
+    intf_thread_t   *p_intf = (intf_thread_t*)p_this;
+    intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
+    playlist_t      *p_playlist;
+    DBusConnection  *p_conn;
+    DBusError       error;
+
+    if( !p_sys )
+        return VLC_ENOMEM;
+
+    dbus_threads_init_default();
+    
+    dbus_error_init( &error );
+    
+    /* connect to the session bus */
+    p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
+    if( !p_conn )
+    {
+        msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
+                error.message );
+        dbus_error_free( &error );
+        free( p_sys );
+        return VLC_EGENERIC;
+    }
+
+    /* we request the service org.videolan.vlc */    
+    dbus_bus_request_name( p_conn, VLC_DBUS_SERVICE,
+            DBUS_NAME_FLAG_REPLACE_EXISTING , &error );
+    if (dbus_error_is_set( &error ) )
+    { 
+        msg_Err( p_this, "Error requesting %s service: %s\n", VLC_DBUS_SERVICE,
+                error.message );
+        dbus_error_free( &error );
+        free( p_sys );
+        return VLC_EGENERIC;
+    }
+
+    /* we register the object /org/videolan/vlc */
+    dbus_connection_register_object_path( p_conn, VLC_DBUS_OBJECT_PATH,
+            &vlc_dbus_vtable, p_this );
+
+    dbus_connection_flush( p_conn );
+
+    p_playlist = pl_Yield( p_intf );
+    PL_LOCK;
+    var_AddCallback( p_playlist, "playlist-current", ItemChange, p_intf );
+    PL_UNLOCK;
+    pl_Release( p_playlist );
+
+    p_intf->pf_run = Run;
+    p_intf->p_sys = p_sys;
+    p_sys->p_conn = p_conn;
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close: destroy interface
+ *****************************************************************************/
+
+static void Close   ( vlc_object_t *p_this )
+{
+    intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
+    playlist_t      *p_playlist = pl_Yield( p_intf );;
+
+    PL_LOCK;
+    var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf );
+    PL_UNLOCK;
+    pl_Release( p_playlist );
+
+    free( p_intf->p_sys );
+}
+
+/*****************************************************************************
+ * Run: main loop    
+ *****************************************************************************/
+
+static void Run          ( intf_thread_t *p_intf )
+{
+    NewInstance( p_intf->p_sys->p_conn, NULL );
+
+    while( !p_intf->b_die )
+    {
+        msleep( INTF_IDLE_SLEEP );
+        dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
+    }
+}
+
+/*****************************************************************************
+ * ItemChange: Playlist item change callback
+ *****************************************************************************/
+
+DBUS_SIGNAL( ItemChangeSignal )
+{ /* emit the name of the new item */
+    SIGNAL_INIT( "ItemChange" );
+    OUT_ARGUMENTS;
+
+    input_thread_t *p_input = (input_thread_t*) p_data;
+    ADD_STRING( &p_input->input.p_item->psz_name );
+
+    SIGNAL_SEND;
+}
+
+static int ItemChange( vlc_object_t *p_this, const char *psz_var,
+            vlc_value_t oldval, vlc_value_t newval, void *p_data )
+{
+    intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
+    intf_sys_t          *p_sys      = p_intf->p_sys;
+    playlist_t          *p_playlist;
+    input_thread_t      *p_input    = NULL;
+    (void)p_this; (void)psz_var; (void)oldval; (void)newval;
+
+    p_playlist = pl_Yield( p_intf );
+    PL_LOCK;
+    p_input = p_playlist->p_input;
+
+    if( !p_input )
+    {
+        PL_UNLOCK;
+        pl_Release( p_playlist );
+        return VLC_SUCCESS;
+    }
+
+    vlc_object_yield( p_input );
+    PL_UNLOCK;
+    pl_Release( p_playlist );
+
+    ItemChangeSignal( p_sys->p_conn, p_input );
+
+    vlc_object_release( p_input );
+    return VLC_SUCCESS;
+}
+
diff --git a/modules/control/dbus.h b/modules/control/dbus.h
new file mode 100644 (file)
index 0000000..533911a
--- /dev/null
@@ -0,0 +1,117 @@
+/*****************************************************************************
+ * dbus.h : D-Bus control interface
+ *****************************************************************************
+ * Copyright (C) 2006 Rafaël Carré
+ * $Id$
+ *
+ * Author:    Rafaël Carré <funman at videolanorg>
+ *
+ * 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.
+ *****************************************************************************/
+
+/* DBUS IDENTIFIERS */
+
+#define VLC_DBUS_SERVICE        "org.videolan.vlc"
+#define VLC_DBUS_INTERFACE      "org.videolan.vlc"
+#define VLC_DBUS_OBJECT_PATH    "/org/videolan/vlc"
+
+/* MACROS */
+
+#define DBUS_METHOD( method_function ) \
+    static DBusHandlerResult method_function \
+            ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this ) 
+
+#define DBUS_SIGNAL( signal_function ) \
+    static DBusHandlerResult signal_function \
+            ( DBusConnection *p_conn, void *p_data )
+
+#define REPLY_INIT \
+    DBusMessage* p_msg = dbus_message_new_method_return( p_from ); \
+    if( !p_msg ) return DBUS_HANDLER_RESULT_NEED_MEMORY; \
+    dbus_uint32_t i_serial = 0
+
+#define REPLY_SEND \
+    if( !dbus_connection_send( p_conn, p_msg, &i_serial ) ) \
+        return DBUS_HANDLER_RESULT_NEED_MEMORY; \
+    dbus_connection_flush( p_conn ); \
+    dbus_message_unref( p_msg ); \
+    return DBUS_HANDLER_RESULT_HANDLED
+
+#define SIGNAL_INIT( signal ) \
+    DBusMessage *p_msg = dbus_message_new_signal( VLC_DBUS_OBJECT_PATH, \
+        VLC_DBUS_INTERFACE, signal ); \
+    if( !p_msg ) return DBUS_HANDLER_RESULT_NEED_MEMORY; \
+    dbus_uint32_t i_serial = 0
+
+#define SIGNAL_SEND \
+    if( !dbus_connection_send( p_conn, p_msg, &i_serial ) ) \
+    return DBUS_HANDLER_RESULT_NEED_MEMORY; \
+    dbus_message_unref( p_msg ); \
+    dbus_connection_flush( p_conn ); \
+    return DBUS_HANDLER_RESULT_HANDLED
+
+#define OUT_ARGUMENTS \
+    DBusMessageIter args; \
+    dbus_message_iter_init_append( p_msg, &args )
+
+#define DBUS_ADD( dbus_type, value ) \
+    if( !dbus_message_iter_append_basic( &args, dbus_type, value ) ) \
+        return DBUS_HANDLER_RESULT_NEED_MEMORY
+
+#define ADD_STRING( s ) DBUS_ADD( DBUS_TYPE_STRING, s )
+#define ADD_BOOL( b ) DBUS_ADD( DBUS_TYPE_BOOLEAN, b )
+#define ADD_UINT32( i ) DBUS_ADD( DBUS_TYPE_UINT32, i )
+
+/* XML data to answer org.freedesktop.DBus.Introspectable.Introspect requests */
+
+const char* psz_introspection_xml_data =
+"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+"<node>"
+"  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+"    <method name=\"Introspect\">\n"
+"      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
+"    </method>\n"
+"  </interface>\n"
+"  <interface name=\"org.videolan.vlc\">\n"
+"    <method name=\"GetPlayStatus\">\n"
+"      <arg type=\"s\" direction=\"out\" />\n"
+"    </method>\n"
+"    <method name=\"TogglePause\">\n"
+"      <arg type=\"s\" direction=\"out\" />\n"
+"    </method>\n"
+"    <method name=\"AddMRL\">\n"
+"      <arg type=\"s\" direction=\"in\" />\n"
+"      <arg type=\"b\" direction=\"in\" />\n"
+"    </method>\n"
+"    <method name=\"Nothing\">\n"
+"    </method>\n"
+"  </interface>\n"
+"</node>\n"
+;
+
+/* Handling of messages received onn VLC_DBUS_OBJECT_PATH */
+DBUS_METHOD( handle_messages ); /* handler function */
+
+/* vtable passed to dbus_connection_register_object_path() */
+static DBusObjectPathVTable vlc_dbus_vtable = {
+        NULL, /* Called when vtable is unregistered or its connection is freed*/
+        handle_messages, /* handler function */
+        NULL,
+        NULL,
+        NULL,
+        NULL
+};
+