intf.c
上传用户:riyaled888
上传日期:2009-03-27
资源大小:7338k
文件大小:28k
源码类别:

多媒体

开发平台:

MultiPlatform

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