dbus.c 25.5 KB
Newer Older
1 2 3 4 5 6
/*****************************************************************************
 * dbus.c : D-Bus control interface
 *****************************************************************************
 * Copyright (C) 2006 Rafaël Carré
 * $Id$
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
7 8
 * Authors:    Rafaël Carré <funman at videolanorg>
 *             Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * 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.
 *****************************************************************************/

/*
Antoine Cellerier's avatar
Antoine Cellerier committed
26
 * D-Bus Specification:
27 28 29
 *      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
30 31 32 33
 *  extract:
    "If you use this low-level API directly, you're signing up for some pain."
 * MPRIS Specification (still drafting on June, 25 of 2007):
 *      http://wiki.xmms2.xmms.se/index.php/Media_Player_Interfaces
34 35 36 37 38 39 40 41 42 43 44
 */

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#include <dbus/dbus.h>

#include "dbus.h"

#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
45 46
#include <vlc_aout.h>
#include <vlc_interface.h>
47 48
#include <vlc_meta.h>
#include <vlc_input.h>
Antoine Cellerier's avatar
Antoine Cellerier committed
49
#include <vlc_playlist.h>
50 51 52 53 54 55 56

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/

static int  Open    ( vlc_object_t * );
static void Close   ( vlc_object_t * );
57
static void Run     ( intf_thread_t * );
58

59
static int TrackChange( vlc_object_t *p_this, const char *psz_var,
60 61
                    vlc_value_t oldval, vlc_value_t newval, void *p_data );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
62 63 64
static int GetInputMeta ( input_item_t *p_input, 
                    DBusMessageIter *args);

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
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
 *****************************************************************************/
86
#if 0
87
DBUS_METHOD( PlaylistExport_XSPF )
88
{ /*export playlist to an xspf file */
89 90

  /* reads the filename to export to */
91
  /* returns the status as int32:
92 93 94 95 96 97 98 99 100 101 102
   *    0 : success
   *    1 : error
   *    2 : playlist empty
   */
    REPLY_INIT;
    OUT_ARGUMENTS;

    DBusError error; 
    dbus_error_init( &error );

    char *psz_file;
103
    dbus_int32_t i_ret;
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_STRING, &psz_file,
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %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 );

    if( ( !playlist_IsEmpty( p_playlist ) ) &&
            ( p_playlist->p_root_category->i_children > 0 ) )
    {
        if( playlist_Export( p_playlist, psz_file,
                         p_playlist->p_root_category->pp_children[0],
                         "export-xspf" ) == VLC_SUCCESS )
            i_ret = 0;
        else
            i_ret = 1;
    }
    else
        i_ret = 2;

    pl_Release( ((vlc_object_t*) p_this ) );

134
    ADD_INT32( &i_ret );
135 136
    REPLY_SEND;
}
137 138 139
#endif

/* Player */
140

Rafaël Carré's avatar
Rafaël Carré committed
141 142 143 144 145 146
DBUS_METHOD( Quit )
{ /* exits vlc */
    REPLY_INIT;
    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
    playlist_Stop( p_playlist );
    pl_Release( ((vlc_object_t*) p_this) );
147
    vlc_object_kill(((vlc_object_t*)p_this)->p_libvlc);
Rafaël Carré's avatar
Rafaël Carré committed
148 149 150
    REPLY_SEND;
}

151 152 153 154 155
DBUS_METHOD( PositionGet )
{ /* returns position as an int in the range [0;1000] */
    REPLY_INIT;
    OUT_ARGUMENTS;
    vlc_value_t position;
156
    dbus_int32_t i_pos;
157 158 159 160 161 162 163 164 165 166 167 168

    playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
    input_thread_t *p_input = p_playlist->p_input;

    if( !p_input )
        i_pos = 0;
    else
    {
        var_Get( p_input, "position", &position );
        i_pos = position.f_float * 1000 ;
    }
    pl_Release( ((vlc_object_t*) p_this) );
169
    ADD_INT32( &i_pos );
170 171 172 173 174 175 176 177
    REPLY_SEND;
}

DBUS_METHOD( PositionSet )
{ /* set position from an int in the range [0;1000] */

    REPLY_INIT;
    vlc_value_t position;
178
    playlist_t* p_playlist = NULL;
179
    dbus_int32_t i_pos;
180 181 182 183 184

    DBusError error;
    dbus_error_init( &error );

    dbus_message_get_args( p_from, &error,
185
            DBUS_TYPE_INT32, &i_pos,
186 187 188 189 190 191 192 193 194
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }
195
    p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
196 197 198 199 200 201 202 203 204 205 206
    input_thread_t *p_input = p_playlist->p_input;

    if( p_input )
    {
        position.f_float = ((float)i_pos) / 1000;
        var_Set( p_input, "position", position );
    }
    pl_Release( ((vlc_object_t*) p_this) );
    REPLY_SEND;
}

Rafaël Carré's avatar
Rafaël Carré committed
207
DBUS_METHOD( VolumeGet )
208
{ /* returns volume in percentage */
Rafaël Carré's avatar
Rafaël Carré committed
209 210
    REPLY_INIT;
    OUT_ARGUMENTS;
211 212 213
    dbus_int32_t i_dbus_vol;
    audio_volume_t i_vol;
    /* 2nd argument of aout_VolumeGet is int32 */
Rafaël Carré's avatar
Rafaël Carré committed
214
    aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
215 216
    i_dbus_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
    ADD_INT32( &i_dbus_vol );
Rafaël Carré's avatar
Rafaël Carré committed
217 218 219 220 221 222 223 224 225 226
    REPLY_SEND;
}

DBUS_METHOD( VolumeSet )
{ /* set volume in percentage */
    REPLY_INIT;

    DBusError error;
    dbus_error_init( &error );

227 228
    dbus_int32_t i_dbus_vol;
    audio_volume_t i_vol;
Rafaël Carré's avatar
Rafaël Carré committed
229 230

    dbus_message_get_args( p_from, &error,
231
            DBUS_TYPE_INT32, &i_dbus_vol,
Rafaël Carré's avatar
Rafaël Carré committed
232 233 234 235 236 237 238 239 240 241
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

242 243
    i_vol = ( AOUT_VOLUME_MAX / 100 ) *i_dbus_vol;
    aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
Rafaël Carré's avatar
Rafaël Carré committed
244 245 246 247

    REPLY_SEND;
}

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
DBUS_METHOD( Next )
{ /* next playlist item */
    REPLY_INIT;
    playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
    playlist_Next( p_playlist );
    pl_Release( ((vlc_object_t*) p_this) );
    REPLY_SEND;
}

DBUS_METHOD( Prev )
{ /* previous playlist item */
    REPLY_INIT;
    playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
    playlist_Prev( p_playlist );
    pl_Release( ((vlc_object_t*) p_this) );
    REPLY_SEND;
}

DBUS_METHOD( Stop )
{ /* stop playing */
    REPLY_INIT;
    playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
    playlist_Stop( p_playlist );
    pl_Release( ((vlc_object_t*) p_this) );
    REPLY_SEND;
}
Rafaël Carré's avatar
Rafaël Carré committed
274

275 276
DBUS_METHOD( GetStatus )
{ /* returns an int: 0=playing 1=paused 2=stopped */
277 278 279
    REPLY_INIT;
    OUT_ARGUMENTS;

280
    dbus_int32_t i_status;
281
    vlc_value_t val;
282

283 284 285
    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
    input_thread_t *p_input = p_playlist->p_input;

286 287
    i_status = 2;
    if( p_input )
288 289 290
    {
        var_Get( p_input, "state", &val );
        if( val.i_int == PAUSE_S )
291
            i_status = 1;
292
        else if( val.i_int == PLAYING_S )
293
            i_status = 0;
294
    }
Antoine Cellerier's avatar
Antoine Cellerier committed
295

296 297
    pl_Release( p_playlist );

298
    ADD_INT32( &i_status );
299 300 301
    REPLY_SEND;
}

302 303
DBUS_METHOD( Pause )
{
304
    REPLY_INIT;
305 306 307 308 309
    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
    playlist_Pause( p_playlist );
    pl_Release( p_playlist );
    REPLY_SEND;
}
310

311 312 313
DBUS_METHOD( Play )
{
    REPLY_INIT;
314
    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
315
    playlist_Play( p_playlist );
316
    pl_Release( p_playlist );
317 318
    REPLY_SEND;
}
319

320 321 322 323 324 325
DBUS_METHOD( Disconnect )
{
    REPLY_INIT;
    DBusError error;
    int i;
    dbus_error_init( &error );
326
    i = dbus_bus_release_name( p_conn, "org.mpris.vlc", &error );
327 328 329 330 331 332 333 334 335
    if( ( i == -1 ) && ( dbus_error_is_set( &error ) ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus disconnection failed : %s\n",
            error.message );
        dbus_error_free( &error );
    }
    REPLY_SEND;
}

336 337 338 339 340 341 342 343 344 345 346 347
DBUS_METHOD( GetCurrentMetadata )
{
    REPLY_INIT;
    OUT_ARGUMENTS;
    playlist_t* p_playlist = pl_Yield( (vlc_object_t*) p_this );

    GetInputMeta( p_playlist->status.p_item->p_input, &args );

    pl_Release( p_playlist );
    REPLY_SEND;
}

348 349 350 351 352 353 354 355 356 357
/* Media Player information */

DBUS_METHOD( Identity )
{
    REPLY_INIT;
    OUT_ARGUMENTS;
    char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 1 );
    sprintf( psz_identity, "%s %s", PACKAGE, VERSION );
    ADD_STRING( &psz_identity );
    free( psz_identity );
358 359 360
    REPLY_SEND;
}

361 362 363
/* TrackList */

DBUS_METHOD( AddTrack )
364 365 366 367 368
{ /* add the string to the playlist, and play it if the boolean is true */
    REPLY_INIT;

    DBusError error;
    dbus_error_init( &error );
369
    playlist_t* p_playlist = NULL;
370 371 372 373 374 375 376 377 378 379 380

    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 ) )
    {
Rafaël Carré's avatar
Rafaël Carré committed
381 382
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
                error.message );
383 384 385 386
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

387
    p_playlist = pl_Yield( (vlc_object_t*) p_this );
Rafaël Carré's avatar
Rafaël Carré committed
388
    playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
389 390
            ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
            PLAYLIST_END, VLC_TRUE, VLC_FALSE );
391 392 393 394 395
    pl_Release( p_playlist );

    REPLY_SEND;
}

396
DBUS_METHOD( GetCurrentTrack )
397
{
398 399 400
    REPLY_INIT;
    OUT_ARGUMENTS;
    dbus_int32_t i_position = 0;
401 402
    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
    playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
403 404

    while ( p_tested_item->p_input->i_id !=
405
                    p_playlist->status.p_item->p_input->i_id )
406 407
    {
        i_position++;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
408
        TEST_NEXT;
409 410 411 412
    }

    pl_Release( p_playlist );

413 414 415 416 417
    ADD_INT32( &i_position );
    REPLY_SEND;
}

DBUS_METHOD( GetMetadata )
418
{
419 420 421 422 423
    REPLY_INIT;
    OUT_ARGUMENTS;
    DBusError error;
    dbus_error_init( &error );

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
424
    dbus_int32_t i_position, i_count = 0;
425

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
426 427
    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
    playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
428 429 430 431 432 433 434 435 436 437 438 439

    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_INT32, &i_position,
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }
440

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
441 442 443
    while ( i_count < i_position ) 
    {
        i_count++;
444
        TEST_NEXT;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
445
    }
446

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
447
    GetInputMeta ( p_tested_item->p_input, &args );
448

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
449
    pl_Release( p_playlist );
450 451 452 453
    REPLY_SEND;
}

DBUS_METHOD( GetLength )
454
{
455 456
    REPLY_INIT;
    OUT_ARGUMENTS;
457

458
    dbus_int32_t i_elements = 0;
459 460
    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
    playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
461
    playlist_item_t* p_last_item = playlist_GetLastLeaf( p_playlist,
462
                    p_playlist->p_root_onelevel ); 
463

464
    while ( p_tested_item->p_input->i_id != p_last_item->p_input->i_id )
465 466
    {
        i_elements++;
467
        TEST_NEXT;
468 469 470
    }

    pl_Release( p_playlist );
471

472 473 474 475 476
    ADD_INT32( &i_elements );
    REPLY_SEND;
}

DBUS_METHOD( DelTrack )
477
{
478 479 480 481 482
    REPLY_INIT;

    DBusError error;
    dbus_error_init( &error );

483 484 485
    dbus_int32_t i_position, i_count = 0;
    playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
    playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
486 487 488 489 490 491 492 493 494 495 496 497

    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_INT32, &i_position,
            DBUS_TYPE_INVALID );

    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }
498

499 500 501
    while ( i_count < i_position ) 
    {
        i_count++;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
502
        TEST_NEXT;
503
    }
504

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
505
    PL_LOCK; 
506 507 508
    playlist_DeleteFromInput( p_playlist,
        p_tested_item->p_input->i_id,
        VLC_TRUE );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
509
    PL_UNLOCK;
510

511
    pl_Release( p_playlist );
512

513 514 515
    REPLY_SEND;
}

516 517 518 519 520 521 522 523 524
DBUS_METHOD( Loop )
{
    REPLY_INIT;
    OUT_ARGUMENTS;

    DBusError error;
    dbus_bool_t b_loop;
    vlc_value_t val;
    playlist_t* p_playlist = NULL;
525

526 527 528 529
    dbus_error_init( &error );
    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_BOOLEAN, &b_loop,
            DBUS_TYPE_INVALID );
530

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    val.b_bool = ( b_loop == TRUE ) ? VLC_TRUE : VLC_FALSE ;
    p_playlist = pl_Yield( (vlc_object_t*) p_this );
    var_Set ( p_playlist, "loop", val );
    pl_Release( ((vlc_object_t*) p_this) );

    REPLY_SEND;
}

DBUS_METHOD( Repeat )
{
    REPLY_INIT;
    OUT_ARGUMENTS;

    DBusError error;
    dbus_bool_t b_repeat;
    vlc_value_t val;
    playlist_t* p_playlist = NULL;
556

557 558 559 560
    dbus_error_init( &error );
    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_BOOLEAN, &b_repeat,
            DBUS_TYPE_INVALID );
561

562 563 564 565 566 567 568 569 570
    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    val.b_bool = ( b_repeat == TRUE ) ? VLC_TRUE : VLC_FALSE ;
571

572 573 574 575 576 577 578
    p_playlist = pl_Yield( (vlc_object_t*) p_this );
    var_Set ( p_playlist, "repeat", val );
    pl_Release( ((vlc_object_t*) p_this) );

    REPLY_SEND;
}

579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
DBUS_METHOD( Random )
{
    REPLY_INIT;
    OUT_ARGUMENTS;

    DBusError error;
    dbus_bool_t b_random;
    vlc_value_t val;
    playlist_t* p_playlist = NULL;
    
    dbus_error_init( &error );
    dbus_message_get_args( p_from, &error,
            DBUS_TYPE_BOOLEAN, &b_random,
            DBUS_TYPE_INVALID );
    
    if( dbus_error_is_set( &error ) )
    {
        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
                error.message );
        dbus_error_free( &error );
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    }

    val.b_bool = ( b_random == TRUE ) ? VLC_TRUE : VLC_FALSE ;
    
    p_playlist = pl_Yield( (vlc_object_t*) p_this );
    var_Set ( p_playlist, "random", val );
    pl_Release( ((vlc_object_t*) p_this) );

    REPLY_SEND;
}
610 611 612 613
/*****************************************************************************
 * Introspection method
 *****************************************************************************/

614
DBUS_METHOD( handle_introspect_root )
615
{ /* handles introspection of root object */
616 617
    REPLY_INIT;
    OUT_ARGUMENTS;
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
    ADD_STRING( &psz_introspection_xml_data_root );
    REPLY_SEND;
}

DBUS_METHOD( handle_introspect_player )
{
    REPLY_INIT;
    OUT_ARGUMENTS;
    ADD_STRING( &psz_introspection_xml_data_player );
    REPLY_SEND;
}

DBUS_METHOD( handle_introspect_tracklist )
{
    REPLY_INIT;
    OUT_ARGUMENTS;
    ADD_STRING( &psz_introspection_xml_data_tracklist );
635 636 637 638
    REPLY_SEND;
}

/*****************************************************************************
639
 * handle_*: answer to incoming messages
640 641 642 643 644 645
 *****************************************************************************/

#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 )

646 647
DBUS_METHOD( handle_root )
{
648 649 650

    if( dbus_message_is_method_call( p_from,
                DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
        return handle_introspect_root( p_conn, p_from, p_this );

    /* here D-Bus method's names are associated to an handler */

    METHOD_FUNC( "Identity",                Identity );

    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}


DBUS_METHOD( handle_player )
{
    if( dbus_message_is_method_call( p_from,
                DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
    return handle_introspect_player( p_conn, p_from, p_this );
666 667 668

    /* here D-Bus method's names are associated to an handler */

669 670 671 672
    METHOD_FUNC( "Prev",                    Prev );
    METHOD_FUNC( "Next",                    Next );
    METHOD_FUNC( "Quit",                    Quit );
    METHOD_FUNC( "Stop",                    Stop );
673 674
    METHOD_FUNC( "Play",                    Play );
    METHOD_FUNC( "Pause",                   Pause );
675
    METHOD_FUNC( "Repeat",                  Repeat );
676
    METHOD_FUNC( "Disconnect",              Disconnect );
677 678 679 680
    METHOD_FUNC( "VolumeSet",               VolumeSet );
    METHOD_FUNC( "VolumeGet",               VolumeGet );
    METHOD_FUNC( "PositionSet",             PositionSet );
    METHOD_FUNC( "PositionGet",             PositionGet );
681
    METHOD_FUNC( "GetStatus",               GetStatus );
682
    METHOD_FUNC( "GetMetadata",             GetCurrentMetadata );
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699

    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

DBUS_METHOD( handle_tracklist )
{
    if( dbus_message_is_method_call( p_from,
                DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
    return handle_introspect_tracklist( p_conn, p_from, p_this );

    /* here D-Bus method's names are associated to an handler */

    METHOD_FUNC( "GetMetadata",             GetMetadata );
    METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
    METHOD_FUNC( "GetLength",               GetLength );
    METHOD_FUNC( "AddTrack",                AddTrack );
    METHOD_FUNC( "DelTrack",                DelTrack );
700
    METHOD_FUNC( "Loop",                    Loop );
701
    METHOD_FUNC( "Random",                  Random );
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721

    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();
Antoine Cellerier's avatar
Antoine Cellerier committed
722

723
    dbus_error_init( &error );
Antoine Cellerier's avatar
Antoine Cellerier committed
724

725 726 727 728 729 730 731 732 733 734 735
    /* 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;
    }

736
    /* register a well-known name on the bus */
737
    dbus_bus_request_name( p_conn, "org.mpris.vlc", 0, &error );
738 739
    if( dbus_error_is_set( &error ) )
    {
740
        msg_Err( p_this, "Error requesting org.mpris.vlc service:"                " %s\n", error.message );
741 742 743 744 745 746 747 748 749 750 751 752
        dbus_error_free( &error );
        free( p_sys );
        return VLC_EGENERIC;
    }

    /* we register the objects */
    dbus_connection_register_object_path( p_conn, VLC_DBUS_ROOT_PATH,
            &vlc_dbus_root_vtable, p_this );
    dbus_connection_register_object_path( p_conn, VLC_DBUS_PLAYER_PATH,
            &vlc_dbus_player_vtable, p_this );
    dbus_connection_register_object_path( p_conn, VLC_DBUS_TRACKLIST_PATH,
            &vlc_dbus_tracklist_vtable, p_this );
753 754 755 756 757

    dbus_connection_flush( p_conn );

    p_playlist = pl_Yield( p_intf );
    PL_LOCK;
758
    var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
    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;
779
    var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
780 781 782
    PL_UNLOCK;
    pl_Release( p_playlist );

783 784
    dbus_connection_unref( p_intf->p_sys->p_conn );

785 786 787 788
    free( p_intf->p_sys );
}

/*****************************************************************************
Antoine Cellerier's avatar
Antoine Cellerier committed
789
 * Run: main loop
790 791 792 793 794 795 796 797 798 799 800 801
 *****************************************************************************/

static void Run          ( intf_thread_t *p_intf )
{
    while( !p_intf->b_die )
    {
        msleep( INTF_IDLE_SLEEP );
        dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
    }
}

/*****************************************************************************
802
 * TrackChange: Playlist item change callback
803 804
 *****************************************************************************/

805
DBUS_SIGNAL( TrackChangeSignal )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
806
{ /* emit the metadata of the new item */
807
    SIGNAL_INIT( "TrackChange" );
808 809 810
    OUT_ARGUMENTS;

    input_thread_t *p_input = (input_thread_t*) p_data;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
811
    GetInputMeta ( input_GetItem(p_input), &args );
812 813 814 815

    SIGNAL_SEND;
}

816
static int TrackChange( vlc_object_t *p_this, const char *psz_var,
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
            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 );

840
    TrackChangeSignal( p_sys->p_conn, p_input );
841 842 843 844 845

    vlc_object_release( p_input );
    return VLC_SUCCESS;
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
846 847 848 849 850 851 852 853 854 855
/*****************************************************************************
 * GetInputMeta: Fill a DBusMessage with the given input item metadata
 *****************************************************************************/

#define ADD_META( entry, type, data ) \
    if( data ) { \
        dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
                NULL, &dict_entry ); \
        dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
                &ppsz_meta_items[entry] ); \
856
        dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
857 858 859 860 861
                type##_AS_STRING, &variant ); \
        dbus_message_iter_append_basic( &variant, \
                type, \
                & data ); \
        dbus_message_iter_close_container( &dict_entry, &variant ); \
862
        dbus_message_iter_close_container( &dict, &dict_entry ); }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
863 864

#define ADD_VLC_META_STRING( entry, item ) \
865
    { \
866
        char * psz = input_item_Get##item( p_input );\
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
867
        ADD_META( entry, DBUS_TYPE_STRING, \
868
                  psz ); \
869
        free( psz ); \
870
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
871

872
static int GetInputMeta( input_item_t* p_input,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
873
                        DBusMessageIter *args )
874 875
{ /*FIXME: Works only for already read metadata. */

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
876
    DBusMessageIter dict, dict_entry, variant;
877 878 879 880
    /* We need the track length to be expressed in seconds
     * instead of milliseconds */
    dbus_int64_t i_length = (p_input->i_duration / 1000);

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
881 882 883 884 885 886 887 888 889

    const char* ppsz_meta_items[] = 
    {
    "title", "artist", "genre", "copyright", "album", "tracknum",
    "description", "rating", "date", "setting", "url", "language",
    "nowplaying", "publisher", "encodedby", "arturl", "trackid",
    "status", "URI", "length", "video-codec", "audio-codec",
    "video-bitrate", "audio-bitrate", "audio-samplerate"
    };
890

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
891 892
    dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );

893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
    ADD_VLC_META_STRING( 0,  Title );
    ADD_VLC_META_STRING( 1,  Artist );
    ADD_VLC_META_STRING( 2,  Genre );
    ADD_VLC_META_STRING( 3,  Copyright );
    ADD_VLC_META_STRING( 4,  Album );
    ADD_VLC_META_STRING( 5,  TrackNum );
    ADD_VLC_META_STRING( 6,  Description );
    ADD_VLC_META_STRING( 7,  Rating );
    ADD_VLC_META_STRING( 8,  Date );
    ADD_VLC_META_STRING( 9,  Setting );
    ADD_VLC_META_STRING( 10, URL );
    ADD_VLC_META_STRING( 11, Language );
    ADD_VLC_META_STRING( 12, NowPlaying );
    ADD_VLC_META_STRING( 13, Publisher );
    ADD_VLC_META_STRING( 14, EncodedBy );
    ADD_VLC_META_STRING( 15, ArtURL );
    ADD_VLC_META_STRING( 16, TrackID ); 
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
910 911

    ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status );
912 913
    ADD_META( 18, DBUS_TYPE_STRING, p_input->psz_uri );
    ADD_META( 19, DBUS_TYPE_INT64, i_length );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
914 915 916 917 918 919 920

    dbus_message_iter_close_container( args, &dict );
    return VLC_SUCCESS;
}

#undef ADD_META
#undef ADD_VLC_META_STRING