intf.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:26k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * intf.c: interface for CMML annotations/hyperlinks
  3.  *****************************************************************************
  4.  * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research
  5.  *                         Organisation (CSIRO) Australia
  6.  * Copyright (C) 2004 the VideoLAN team
  7.  *
  8.  * $Id: 2600286d8b3224155e7f476aaab72b291487d853 $
  9.  *
  10.  * Authors: Andre Pang <Andre.Pang@csiro.au>
  11.  *
  12.  * This program is free software; you can redistribute it and/or modify
  13.  * it under the terms of the GNU General Public License as published by
  14.  * the Free Software Foundation; either version 2 of the License, or
  15.  * (at your option) any later version.
  16.  *
  17.  * This program is distributed in the hope that it will be useful,
  18.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20.  * GNU General Public License for more details.
  21.  *
  22.  * You should have received a copy of the GNU General Public License
  23.  * along with this program; if not, write to the Free Software
  24.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  25.  *****************************************************************************/
  26. /*****************************************************************************
  27.  * Preamble
  28.  *****************************************************************************/
  29. #ifdef HAVE_CONFIG_H
  30. # include "config.h"
  31. #endif
  32. #include <vlc_common.h>
  33. #include <stdio.h>
  34. #ifdef HAVE_UNISTD_H
  35. #    include <unistd.h>
  36. #endif
  37. #include <vlc_codec.h>
  38. #include <vlc_interface.h>
  39. #include <vlc_playlist.h>
  40. #include <vlc_osd.h>
  41. #include <vlc_keys.h>
  42. #include "browser_open.h"
  43. #include "history.h"
  44. #include "xstrcat.h"
  45. #include "xurl.h"
  46. #undef CMML_INTF_USE_TIMED_URIS
  47. #undef  CMML_INTF_DEBUG
  48. #undef  CMML_INTF_HISTORY_DEBUG
  49. /*****************************************************************************
  50.  * intf_sys_t: description and status of interface
  51.  *****************************************************************************/
  52. struct intf_sys_t
  53. {
  54.     vlc_mutex_t         lock;
  55.     decoder_t *         p_cmml_decoder;
  56.     input_thread_t *    p_input;
  57.     int                 i_key_action;
  58. };
  59. struct navigation_history_t
  60. {
  61.     int i_history_size;
  62.     int i_last_item;
  63. };
  64. /*****************************************************************************
  65.  * Local prototypes.
  66.  *****************************************************************************/
  67. int          OpenIntf               ( vlc_object_t * );
  68. void         CloseIntf              ( vlc_object_t * );
  69. static int   InitThread                 ( intf_thread_t * );
  70. static int   MouseEvent                 ( vlc_object_t *, char const *,
  71.                                           vlc_value_t, vlc_value_t, void * );
  72. static int   KeyEvent                   ( vlc_object_t *, char const *,
  73.                                           vlc_value_t, vlc_value_t, void * );
  74. static void  FollowAnchor               ( intf_thread_t * );
  75. static void  GoBack                     ( intf_thread_t * );
  76. static void  GoForward                  ( intf_thread_t * );
  77. static int   FollowAnchorCallback       ( vlc_object_t *, char const *,
  78.                                           vlc_value_t, vlc_value_t, void * );
  79. static int   GoBackCallback             ( vlc_object_t *, char const *,
  80.                                           vlc_value_t, vlc_value_t, void * );
  81. static int   GoForwardCallback          ( vlc_object_t *, char const *,
  82.                                           vlc_value_t, vlc_value_t, void * );
  83. static char *GetTimedURLFromPlaylistItem( intf_thread_t *, playlist_item_t * );
  84. #ifdef CMML_INTF_USE_TIMED_URIS
  85. static int   GetCurrentTimeInSeconds    ( input_thread_t * );
  86. static char *GetTimedURIFragmentForTime ( int );
  87. #endif
  88. static int   DisplayAnchor              ( intf_thread_t *, vout_thread_t *,
  89.                                           char *, char * );
  90. static int   DisplayPendingAnchor       ( intf_thread_t *, vout_thread_t * );
  91. static history_t * GetHistory           ( playlist_t * );
  92. static void  ReplacePlaylistItem        ( playlist_t *, char * );
  93. /* Exported functions */
  94. static void RunIntf        ( intf_thread_t *p_intf );
  95. /*****************************************************************************
  96.  * OpenIntf: initialize CMML interface
  97.  *****************************************************************************/
  98. int OpenIntf ( vlc_object_t *p_this )
  99. {
  100.     intf_thread_t *p_intf = (intf_thread_t *)p_this;
  101.     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
  102.     if( p_intf->p_sys == NULL )
  103.         return VLC_ENOMEM;
  104.     p_intf->pf_run = RunIntf;
  105.     vlc_mutex_init( &p_intf->p_sys->lock );
  106.     var_AddCallback( p_intf->p_libvlc, "key-action", KeyEvent, p_intf );
  107.     /* we also need to add the callback for "mouse-clicked", but do that later
  108.      * when we've found a p_vout */
  109.     var_Create( p_intf->p_libvlc, "browse-go-back", VLC_VAR_VOID );
  110.     var_AddCallback( p_intf->p_libvlc, "browse-go-back",
  111.                      GoBackCallback, p_intf );
  112.     var_Create( p_intf->p_libvlc, "browse-go-forward", VLC_VAR_VOID );
  113.     var_AddCallback( p_intf->p_libvlc, "browse-go-forward",
  114.                      GoForwardCallback, p_intf );
  115.     var_Create( p_intf->p_libvlc, "browse-follow-anchor", VLC_VAR_VOID );
  116.     var_AddCallback( p_intf->p_libvlc, "browse-follow-anchor",
  117.                      FollowAnchorCallback, p_intf );
  118.     return VLC_SUCCESS;
  119. }
  120. /*****************************************************************************
  121.  * CloseIntf: destroy dummy interface
  122.  *****************************************************************************/
  123. void CloseIntf ( vlc_object_t *p_this )
  124. {
  125.     intf_thread_t * p_intf = (intf_thread_t *)p_this;
  126.     vout_thread_t * p_vout;
  127. #ifdef CMML_INTF_DEBUG
  128.     msg_Dbg( p_intf, "freeing CMML interface" );
  129. #endif
  130.     /* erase the anchor text description from the video output if it exists */
  131.     p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
  132.     if( p_vout )
  133.     {
  134.         /* enable CMML as a subtitle track */
  135.         spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
  136.         vlc_object_release( p_vout );
  137.     }
  138.     var_DelCallback( p_intf->p_libvlc, "key-action", KeyEvent, p_intf );
  139.     vlc_object_release( p_intf->p_sys->p_cmml_decoder );
  140.     vlc_mutex_destroy( &p_intf->p_sys->lock );
  141.     free( p_intf->p_sys );
  142. }
  143. /*****************************************************************************
  144.  * RunIntf: main loop
  145.  *****************************************************************************/
  146. static void RunIntf( intf_thread_t *p_intf )
  147. {
  148.     int canc = vlc_savecancel();
  149.     vout_thread_t * p_vout = NULL;
  150.     if( InitThread( p_intf ) < 0 )
  151.     {
  152.         msg_Err( p_intf, "can't initialize CMML interface" );
  153.         return;
  154.     }
  155. #ifdef CMML_INTF_DEBUG
  156.     msg_Dbg( p_intf, "CMML intf initialized" );
  157. #endif
  158.     /* Main loop */
  159.     while( vlc_object_alive (p_intf) )
  160.     {
  161.         /* if video output is dying, disassociate ourselves from it */
  162.         if( p_vout && !vlc_object_alive (p_vout) )
  163.         {
  164.             var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
  165.             vlc_object_release( p_vout );
  166.             p_vout = NULL;
  167.         }
  168.         /* find a video output if we currently don't have one */
  169.         if( p_vout == NULL )
  170.         {
  171.             p_vout = vlc_object_find( p_intf->p_sys->p_input,
  172.                                       VLC_OBJECT_VOUT, FIND_CHILD );
  173.             if( p_vout )
  174.             {
  175. #ifdef CMML_INTF_DEBUG
  176.                 msg_Dbg( p_intf, "found vout thread" );
  177. #endif
  178.                 var_AddCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
  179.             }
  180.         }
  181.         vlc_mutex_lock( &p_intf->p_sys->lock );
  182.         /*
  183.          * keyboard event
  184.          */
  185.         switch( p_intf->p_sys->i_key_action )
  186.         {
  187.             case ACTIONID_NAV_ACTIVATE:
  188.                 FollowAnchor( p_intf );
  189.                 break;
  190.             case ACTIONID_HISTORY_BACK:
  191.                 GoBack( p_intf );
  192.                 break;
  193.             case ACTIONID_HISTORY_FORWARD:
  194.                 GoForward( p_intf );
  195.                 break;
  196.             default:
  197.                 break;
  198.         }
  199.         p_intf->p_sys->i_key_action = 0;
  200.         vlc_mutex_unlock( &p_intf->p_sys->lock );
  201.         (void) DisplayPendingAnchor( p_intf, p_vout );
  202.         /* Wait a bit */
  203.         msleep( INTF_IDLE_SLEEP );
  204.     }
  205.     /* if we're here, the video output is dying: release the vout object */
  206.     if( p_vout )
  207.     {
  208.         var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
  209.         vlc_object_release( p_vout );
  210.     }
  211.     vlc_object_release( p_intf->p_sys->p_input );
  212.     vlc_restorecancel( canc );
  213. }
  214. /*****************************************************************************
  215.  * DisplayPendingAnchor: get a pending anchor description/URL from the CMML
  216.  * decoder and display it on screen
  217.  *****************************************************************************/
  218. static int DisplayPendingAnchor( intf_thread_t *p_intf, vout_thread_t *p_vout )
  219. {
  220.     decoder_t *p_cmml_decoder;
  221.     char *psz_description = NULL;
  222.     char *psz_url = NULL;
  223.     intf_thread_t *p_primary_intf;
  224.     vlc_value_t val;
  225.     p_cmml_decoder = p_intf->p_sys->p_cmml_decoder;
  226.     if( var_Get( p_cmml_decoder, "psz-current-anchor-description", &val )
  227.             != VLC_SUCCESS )
  228.     {
  229.         return true;
  230.     }
  231.     if( !val.p_address )
  232.         return true;
  233.     psz_description = val.p_address;
  234.     if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val )
  235.             == VLC_SUCCESS )
  236.     {
  237.         psz_url = val.p_address;
  238.     }
  239.     if( p_vout != NULL )
  240.     {
  241.         /* display anchor as subtitle on-screen */
  242.         if( DisplayAnchor( p_intf, p_vout, psz_description, psz_url )
  243.                 != VLC_SUCCESS )
  244.         {
  245.             /* text render unsuccessful: do nothing */
  246.             return false;
  247.         }
  248.         /* text render successful: clear description */
  249.         val.p_address = NULL;
  250.         if( var_Set( p_cmml_decoder, "psz-current-anchor-description", val )
  251.                 != VLC_SUCCESS )
  252.         {
  253.             msg_Dbg( p_intf,
  254.                      "reset of psz-current-anchor-description failed" );
  255.         }
  256.         free( psz_description );
  257.         psz_url = NULL;
  258.     }
  259.     return true;
  260. }
  261. /*****************************************************************************
  262.  * InitThread:
  263.  *****************************************************************************/
  264. static int InitThread( intf_thread_t * p_intf )
  265. {
  266.     /* We might need some locking here */
  267.     if( vlc_object_alive (p_intf) )
  268.     {
  269.         input_thread_t * p_input;
  270.         decoder_t *p_cmml_decoder;
  271.         p_cmml_decoder = vlc_object_find( p_intf, VLC_OBJECT_DECODER, FIND_PARENT );
  272.         p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_PARENT );
  273. #ifdef CMML_INTF_DEBUG
  274.         msg_Dbg( p_intf, "cmml decoder at %p, input thread at %p",
  275.                  p_cmml_decoder, p_input );
  276. #endif
  277.         /* Maybe the input just died */
  278.         if( p_input == NULL )
  279.         {
  280.             return VLC_EGENERIC;
  281.         }
  282.         vlc_mutex_lock( &p_intf->p_sys->lock );
  283.         p_intf->p_sys->p_input = p_input;
  284.         p_intf->p_sys->p_cmml_decoder = p_cmml_decoder;
  285.         p_intf->p_sys->i_key_action = 0;
  286.         vlc_mutex_unlock( &p_intf->p_sys->lock );
  287.         return VLC_SUCCESS;
  288.     }
  289.     else
  290.     {
  291.         return VLC_EGENERIC;
  292.     }
  293. }
  294. /*****************************************************************************
  295.  * MouseEvent: callback for mouse events
  296.  *****************************************************************************/
  297. static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
  298.                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
  299. {
  300.     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
  301.     VLC_UNUSED(oldval); VLC_UNUSED(newval);
  302.     VLC_UNUSED(p_data);
  303.     /* TODO: handle mouse clicks on the anchor text */
  304.     return VLC_SUCCESS;
  305. }
  306. /*****************************************************************************
  307.  * KeyEvent: callback for keyboard events
  308.  *****************************************************************************/
  309. static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
  310.                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
  311. {
  312.     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
  313.     VLC_UNUSED(oldval); VLC_UNUSED(newval);
  314.     intf_thread_t *p_intf = (intf_thread_t *)p_data;
  315.     vlc_mutex_lock( &p_intf->p_sys->lock );
  316.     /* FIXME: key presses might get lost here... */
  317.     p_intf->p_sys->i_key_action = newval.i_int;
  318.     vlc_mutex_unlock( &p_intf->p_sys->lock );
  319.     return VLC_SUCCESS;
  320. }
  321. /*****************************************************************************
  322.  * FollowAnchor: follow the current anchor being displayed to the user
  323.  *****************************************************************************/
  324. static void FollowAnchor ( intf_thread_t *p_intf )
  325. {
  326.     intf_sys_t *p_sys;
  327.     decoder_t *p_cmml_decoder;
  328.     char *psz_url = NULL;
  329.     vlc_value_t val;
  330.     msg_Dbg( p_intf, "User followed anchor" );
  331.     p_sys = p_intf->p_sys;
  332.     p_cmml_decoder = p_sys->p_cmml_decoder;
  333.     if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val ) ==
  334.             VLC_SUCCESS )
  335.     {
  336.         if( val.p_address ) psz_url = val.p_address;
  337.     }
  338. #ifdef CMML_INTF_DEBUG
  339.     msg_Dbg( p_intf, "Current URL is "%s"", psz_url );
  340. #endif
  341.     if( psz_url )
  342.     {
  343.         playlist_t *p_playlist;
  344.         playlist_item_t *p_current_item;
  345.         char *psz_uri_to_load;
  346.         mtime_t i_seconds;
  347.         vlc_value_t time;
  348.         p_playlist = pl_Hold( p_intf );
  349.         /* Get new URL */
  350.         p_current_item = playlist_CurrentPlayingItem( p_playlist );
  351.         char *psz_uri = input_item_GetURI( p_current_item->p_input );
  352. #ifdef CMML_INTF_DEBUG
  353.         msg_Dbg( p_intf, "Current playlist item URL is "%s"", psz_uri );
  354. #endif
  355.         psz_uri_to_load = XURL_Concat( psz_uri, psz_url );
  356.         free( psz_uri );
  357. #ifdef CMML_INTF_DEBUG
  358.         msg_Dbg( p_intf, "URL to load is "%s"", psz_uri_to_load );
  359. #endif
  360.         if( var_Get( p_intf->p_sys->p_input, "time", &time ) )
  361.         {
  362.             msg_Dbg( p_intf, "couldn't get time from current clip" );
  363.             time.i_time = 0;
  364.         }
  365.         i_seconds = time.i_time / 1000000;
  366. #ifdef CMML_INTF_DEBUG
  367.         msg_Dbg( p_intf, "Current time is "%lld"", i_seconds );
  368. #endif
  369.         /* TODO: we need a (much) more robust way of detecting whether
  370.          * the file's a media file ... */
  371.         if( strstr( psz_uri_to_load, ".anx" ) != NULL )
  372.         {
  373.             history_t *p_history = NULL;
  374.             history_item_t *p_history_item = NULL;
  375.             char *psz_timed_url;
  376.             p_history = GetHistory( p_playlist );
  377.             /* create history item */
  378.             psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item );
  379.             p_history_item = historyItem_New( psz_timed_url, psz_timed_url );
  380.             free( psz_timed_url );
  381.             if( !p_history_item )
  382.             {
  383.                 msg_Warn( p_intf, "could not initialise history item" );
  384.             }
  385.             else
  386.             {
  387. #ifdef CMML_INTF_DEBUG
  388.                 msg_Dbg( p_intf, "history pre-index %d", p_history->i_index );
  389. #endif
  390.                 history_PruneAndInsert( p_history, p_history_item );
  391. #ifdef CMML_INTF_DEBUG
  392.                 msg_Dbg( p_intf, "new history item at %p, uri is "%s"",
  393.                          p_history_item, p_history_item->psz_uri );
  394.                 msg_Dbg( p_intf, "history index now %d", p_history->i_index );
  395. #endif
  396.             }
  397.             /* free current-anchor-url */
  398.             free( psz_url );
  399.             val.p_address = NULL;
  400.             if( var_Set( p_cmml_decoder, "psz-current-anchor-url", val ) !=
  401.                     VLC_SUCCESS )
  402.             {
  403.                 msg_Dbg( p_intf, "couldn't reset psz-current-anchor-url" );
  404.             }
  405.             ReplacePlaylistItem( p_playlist, psz_uri_to_load );
  406.         }
  407.         else
  408.         {
  409. #ifdef CMML_INTF_DEBUG
  410.             msg_Dbg( p_intf, "calling browser_Open with "%s"", psz_url );
  411. #endif
  412.             (void) browser_Open( psz_url );
  413.             playlist_Control( p_playlist, PLAYLIST_PAUSE, pl_Unlocked, 0 );
  414.         }
  415.         free( psz_uri_to_load );
  416.         pl_Release( p_intf );
  417.     }
  418. }
  419. static
  420. char *GetTimedURLFromPlaylistItem( intf_thread_t *p_intf,
  421.         playlist_item_t *p_current_item )
  422. {
  423. #ifdef CMML_INTF_USE_TIMED_URIS
  424.     char *psz_url = NULL;
  425.     char *psz_return_value = NULL;
  426.     char *psz_seconds = NULL;
  427.     int i_seconds;
  428.     char *psz_uri = input_item_GetURI( p_current_item->p_input );
  429.     psz_url = XURL_GetWithoutFragment( psz_uri );
  430.     free( psz_uri );
  431.     /* Get current time as a string */
  432.     if( XURL_IsFileURL( psz_url ) == true )
  433.         psz_url = xstrcat( psz_url, "#" );
  434.     else
  435.         psz_url = xstrcat( psz_url, "?" );
  436.     /* jump back to 2 seconds before where we are now */
  437.     i_seconds = GetCurrentTimeInSeconds( p_intf->p_sys->p_input ) - 2;
  438.     psz_seconds = GetTimedURIFragmentForTime( i_seconds < 0 ? 0 : i_seconds );
  439.     if( psz_seconds )
  440.     {
  441.         psz_url = xstrcat( psz_url, psz_seconds );
  442.         free( psz_seconds );
  443.         psz_return_value = psz_url;
  444.     }
  445.     return psz_return_value;
  446. #else
  447.     VLC_UNUSED(p_intf);
  448.     return input_item_GetURI( p_current_item->p_input );
  449. #endif
  450. }
  451. #ifdef CMML_INTF_USE_TIMED_URIS
  452. /*
  453.  * Get the current time, rounded down to the nearest second
  454.  *
  455.  * http://www.ietf.org/internet-drafts/draft-pfeiffer-temporal-fragments-02.txt
  456.  */
  457. static
  458. int GetCurrentTimeInSeconds( input_thread_t *p_input )
  459. {
  460.     vlc_value_t time;
  461.     mtime_t i_seconds;
  462.     var_Get( p_input, "time", &time );
  463.     i_seconds = time.i_time / 1000000;
  464.     return i_seconds;
  465. }
  466. static
  467. char *GetTimedURIFragmentForTime( int seconds )
  468. {
  469.     char *psz_time;
  470.     if( asprintf( &psz_time, "%d", seconds ) == -1 )
  471.         return NULL;
  472.     return psz_time;
  473. }
  474. #endif
  475. static
  476. int GoBackCallback( vlc_object_t *p_this, char const *psz_var,
  477.                     vlc_value_t oldval, vlc_value_t newval, void *p_data )
  478. {
  479.     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
  480.     VLC_UNUSED(oldval); VLC_UNUSED(newval);
  481.     intf_thread_t *p_intf = (intf_thread_t *) p_data;
  482.     GoBack( p_intf );
  483.     return VLC_SUCCESS;
  484. }
  485. static
  486. int GoForwardCallback( vlc_object_t *p_this, char const *psz_var,
  487.                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
  488. {
  489.     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
  490.     VLC_UNUSED(oldval); VLC_UNUSED(newval);
  491.     intf_thread_t *p_intf = (intf_thread_t *) p_data;
  492.     GoForward( p_intf );
  493.     return VLC_SUCCESS;
  494. }
  495. static
  496. int FollowAnchorCallback( vlc_object_t *p_this, char const *psz_var,
  497.                           vlc_value_t oldval, vlc_value_t newval,
  498.                           void *p_data )
  499. {
  500.     VLC_UNUSED(p_this); VLC_UNUSED(psz_var);
  501.     VLC_UNUSED(oldval); VLC_UNUSED(newval);
  502.     intf_thread_t *p_intf = (intf_thread_t *) p_data;
  503.     FollowAnchor( p_intf );
  504.     return VLC_SUCCESS;
  505. }
  506. static
  507. void GoBack( intf_thread_t *p_intf )
  508. {
  509.     vlc_value_t history;
  510.     history_t *p_history = NULL;
  511.     history_item_t *p_history_item = NULL;
  512.     history_item_t *p_new_history_item = NULL;
  513.     playlist_t *p_playlist = NULL;
  514.     char *psz_timed_url = NULL;
  515.     playlist_item_t *p_current_item;
  516. #ifdef CMML_INTF_DEBUG
  517.     msg_Dbg( p_intf, "Going back in navigation history" );
  518. #endif
  519.     /* Find the playlist */
  520.     p_playlist = pl_Hold( p_intf );
  521.     /* Retrieve navigation history from playlist */
  522.     if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS ||
  523.         !history.p_address )
  524.     {
  525.         /* History doesn't exist yet: ignore user's request */
  526.         msg_Warn( p_intf, "can't go back: no history exists yet" );
  527.         pl_Release( p_intf );
  528.         return;
  529.     }
  530.     p_history = history.p_address;
  531. #ifdef CMML_INTF_DEBUG
  532.     msg_Dbg( p_intf, "back: nav history retrieved from %p", p_history );
  533.     msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index,
  534.              p_history->p_xarray );
  535. #endif
  536.     /* Check whether we can go back in the history */
  537.     if( history_CanGoBack( p_history ) == false )
  538.     {
  539.         msg_Warn( p_intf, "can't go back: already at beginning of history" );
  540.         pl_Release( p_intf );
  541.         return;
  542.     }
  543.     p_current_item = playlist_CurrentPlayingItem( p_playlist );
  544.     /* Save the currently-playing media in a new history item */
  545.     psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item );
  546.     p_new_history_item = historyItem_New( psz_timed_url, psz_timed_url );
  547.     free( psz_timed_url );
  548.     if( !p_new_history_item )
  549.     {
  550. #ifdef CMML_INTF_DEBUG
  551.         msg_Dbg( p_intf, "back: could not initialise new history item" );
  552. #endif
  553.         pl_Release( p_intf );
  554.         return;
  555.     }
  556.     /* Go back in the history, saving the currently-playing item */
  557.     (void) history_GoBackSavingCurrentItem( p_history, p_new_history_item );
  558.     p_history_item = history_Item( p_history );
  559. #ifdef CMML_INTF_DEBUG
  560.     msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index );
  561.     msg_Dbg( p_intf, "got previous history item: %p", p_history_item );
  562.     msg_Dbg( p_intf, "prev history item URL: "%s"", p_history_item->psz_uri );
  563. #endif
  564.     ReplacePlaylistItem( p_playlist, p_history_item->psz_uri );
  565.     pl_Release( p_intf );
  566. }
  567. static
  568. void GoForward( intf_thread_t *p_intf )
  569. {
  570.     vlc_value_t history;
  571.     history_t *p_history = NULL;
  572.     history_item_t *p_history_item = NULL;
  573.     history_item_t *p_new_history_item = NULL;
  574.     playlist_t *p_playlist = NULL;
  575.     playlist_item_t *p_current_item;
  576. #ifdef CMML_INTF_DEBUG
  577.     msg_Dbg( p_intf, "Going forward in navigation history" );
  578. #endif
  579.     /* Find the playlist */
  580.     p_playlist = pl_Hold( p_intf );
  581.     /* Retrieve navigation history from playlist */
  582.     if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS ||
  583.         !history.p_address )
  584.     {
  585.         /* History doesn't exist yet: ignore user's request */
  586.         msg_Warn( p_intf, "can't go back: no history exists yet" );
  587.         pl_Release( p_intf );
  588.         return;
  589.     }
  590.     p_history = history.p_address;
  591. #ifdef CMML_INTF_DEBUG
  592.     msg_Dbg( p_intf, "forward: nav history retrieved from %p", p_history );
  593.     msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index,
  594.              p_history->p_xarray );
  595. #endif
  596.     /* Check whether we can go forward in the history */
  597.     if( history_CanGoForward( p_history ) == false )
  598.     {
  599.         msg_Warn( p_intf, "can't go forward: already at end of history" );
  600.         pl_Release( p_intf );
  601.         return;
  602.     }
  603.     /* Save the currently-playing media in a new history item */
  604.     p_new_history_item = malloc( sizeof(history_item_t) );
  605.     if( !p_new_history_item )
  606.     {
  607. #ifdef CMML_INTF_DEBUG
  608.         msg_Dbg( p_intf, "forward: could not initialise new history item" );
  609. #endif
  610.         pl_Release( p_intf );
  611.         return;
  612.     }
  613.     p_current_item = playlist_CurrentPlayingItem( p_playlist );
  614.     p_new_history_item->psz_uri = GetTimedURLFromPlaylistItem( p_intf,
  615.             p_current_item );
  616.     p_new_history_item->psz_name = p_new_history_item->psz_uri;
  617.     /* Go forward in the history, saving the currently-playing item */
  618.     (void) history_GoForwardSavingCurrentItem( p_history, p_new_history_item );
  619.     p_history_item = history_Item( p_history );
  620. #ifdef CMML_INTF_DEBUG
  621.     msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index );
  622.     msg_Dbg( p_intf, "got next history item: %p", p_history_item );
  623.     msg_Dbg( p_intf, "next history item URL: "%s"", p_history_item->psz_uri );
  624. #endif
  625.     ReplacePlaylistItem( p_playlist, p_history_item->psz_uri );
  626.     pl_Release( p_intf );
  627. }
  628. static void ReplacePlaylistItem( playlist_t *p_playlist, char *psz_uri )
  629. {
  630.     playlist_Stop( p_playlist );
  631.     (void) playlist_Add( p_playlist, psz_uri, psz_uri,
  632.                          PLAYLIST_INSERT /* FIXME: used to be PLAYLIST_REPLACE */, PLAYLIST_END|PLAYLIST_GO, true /* FIXME: p_playlist->status.i_index */,
  633.                          false);
  634. }
  635. /****************************************************************************
  636.  * DisplayAnchor: displays an anchor on the given video output
  637.  ****************************************************************************/
  638. static int DisplayAnchor( intf_thread_t *p_intf,
  639.         vout_thread_t *p_vout,
  640.         char *psz_anchor_description,
  641.         char *psz_anchor_url )
  642. {
  643.     int i_margin_h, i_margin_v;
  644.     mtime_t i_now;
  645.     i_margin_h = 0;
  646.     i_margin_v = 10;
  647.     i_now = mdate();
  648.     if( p_vout )
  649.     {
  650.         if( psz_anchor_url )
  651.         {
  652.             /* Should display subtitle underlined and in blue, but it looks
  653.              * like VLC doesn't implement any text styles yet.  D'oh! */
  654.             // p_style = &blue_with_underline;
  655.         }
  656.         /* TODO: p_subpicture doesn't have the proper i_x and i_y
  657.          * coordinates.  Need to look at the subpicture display system to
  658.          * work out why. */
  659.         if ( vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
  660.                 psz_anchor_description, NULL, OSD_ALIGN_BOTTOM,
  661.                 i_margin_h, i_margin_v, i_now, 0 ) == VLC_SUCCESS )
  662.         {
  663.             /* Displayed successfully */
  664.         }
  665.         else
  666.         {
  667.             return VLC_EGENERIC;
  668.         }
  669.     }
  670.     else
  671.     {
  672.         msg_Dbg( p_intf, "DisplayAnchor couldn't find a video output" );
  673.         return VLC_EGENERIC;
  674.     }
  675.     return VLC_SUCCESS;
  676. }
  677. static history_t * GetHistory( playlist_t *p_playlist )
  678. {
  679.     vlc_value_t val;
  680.     history_t *p_history = NULL;
  681.     if( var_Get( p_playlist, "navigation-history", &val ) != VLC_SUCCESS )
  682.     {
  683.         /* history doesn't exist yet: need to create it */
  684.         history_t *new_history = history_New();
  685.         val.p_address = new_history;
  686.         var_Create( p_playlist, "navigation-history",
  687.                 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
  688.         if( var_Set( p_playlist, "navigation-history", val ) != VLC_SUCCESS )
  689.         {
  690.             msg_Warn( p_playlist, "could not initialise history" );
  691.         }
  692.         else
  693.         {
  694.             p_history = new_history;
  695. #ifdef CMML_INTF_HISTORY_DEBUG
  696.             msg_Dbg( p_playlist, "nav history created at %p", new_history );
  697.             msg_Dbg( p_playlist, "nav history index:%d, p_xarray:%p",
  698.                      p_history->i_index, p_history->p_xarray );
  699. #endif
  700.         }
  701.     }
  702.     else
  703.     {
  704.         p_history = val.p_address;
  705. #ifdef CMML_INTF_HISTORY_DEBUG
  706.         msg_Dbg( p_playlist, "nav history retrieved from %p", p_history );
  707. #endif
  708.     }
  709.     return p_history;
  710. }