Commit 147753f6 authored by Rafaël Carré's avatar Rafaël Carré

Initial support of D-Bus based control interface

parent a9658993
......@@ -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
......
......@@ -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
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment