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
10 * Authors: Andre Pang <Andre.Pang@csiro.au>
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.
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.
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 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
30 #include <stdlib.h> /* malloc(), free() */
39 #include <vlc/decoder.h>
40 #include <vlc/input.h>
48 #include "browser_open.h"
53 #undef CMML_INTF_USE_TIMED_URIS
55 #undef CMML_INTF_DEBUG
56 #undef CMML_INTF_SUBPICTURE_DEBUG
57 #undef CMML_INTF_HISTORY_DEBUG
59 /*****************************************************************************
60 * intf_sys_t: description and status of interface
61 *****************************************************************************/
64 decoder_t * p_cmml_decoder;
65 input_thread_t * p_input;
67 vlc_bool_t b_key_pressed;
70 struct navigation_history_t
76 /*****************************************************************************
78 *****************************************************************************/
79 static int InitThread ( intf_thread_t * );
80 static int MouseEvent ( vlc_object_t *, char const *,
81 vlc_value_t, vlc_value_t, void * );
82 static int KeyEvent ( vlc_object_t *, char const *,
83 vlc_value_t, vlc_value_t, void * );
85 static void FollowAnchor ( intf_thread_t * );
86 static void GoBack ( intf_thread_t * );
87 static void GoForward ( intf_thread_t * );
89 static int FollowAnchorCallback ( vlc_object_t *, char const *,
90 vlc_value_t, vlc_value_t, void * );
91 static int GoBackCallback ( vlc_object_t *, char const *,
92 vlc_value_t, vlc_value_t, void * );
93 static int GoForwardCallback ( vlc_object_t *, char const *,
94 vlc_value_t, vlc_value_t, void * );
96 static char *GetTimedURLFromPlaylistItem( intf_thread_t *, playlist_item_t * );
97 static char *GetTimedURIFragmentForTime ( int );
98 static int GetCurrentTimeInSeconds ( input_thread_t * );
99 static int DisplayAnchor ( intf_thread_t *, vout_thread_t *,
101 static int DisplayPendingAnchor ( intf_thread_t *, vout_thread_t * );
102 static history_t * GetHistory ( playlist_t * );
103 static void ReplacePlaylistItem ( playlist_t *, char * );
105 /* Exported functions */
106 static void RunIntf ( intf_thread_t *p_intf );
108 /*****************************************************************************
109 * OpenIntf: initialize CMML interface
110 *****************************************************************************/
111 int E_(OpenIntf) ( vlc_object_t *p_this )
113 intf_thread_t *p_intf = (intf_thread_t *)p_this;
115 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
116 if( p_intf->p_sys == NULL )
121 p_intf->pf_run = RunIntf;
123 var_AddCallback( p_intf->p_vlc, "key-pressed", KeyEvent, p_intf );
124 /* we also need to add the callback for "mouse-clicked", but do that later
125 * when we've found a p_vout */
127 var_Create( p_intf->p_vlc, "browse-go-back", VLC_VAR_VOID );
128 var_AddCallback( p_intf->p_vlc, "browse-go-back",
129 GoBackCallback, p_intf );
130 var_Create( p_intf->p_vlc, "browse-go-forward", VLC_VAR_VOID );
131 var_AddCallback( p_intf->p_vlc, "browse-go-forward",
132 GoForwardCallback, p_intf );
133 var_Create( p_intf->p_vlc, "browse-follow-anchor", VLC_VAR_VOID );
134 var_AddCallback( p_intf->p_vlc, "browse-follow-anchor",
135 FollowAnchorCallback, p_intf );
140 /*****************************************************************************
141 * CloseIntf: destroy dummy interface
142 *****************************************************************************/
143 void E_(CloseIntf) ( vlc_object_t *p_this )
145 intf_thread_t * p_intf = (intf_thread_t *)p_this;
146 vout_thread_t * p_vout;
148 #ifdef CMML_INTF_DEBUG
149 msg_Dbg( p_intf, "freeing CMML interface" );
152 /* Erase the anchor text description from the video output if it exists */
153 p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
156 spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
157 vlc_object_release( p_vout );
160 var_DelCallback( p_intf->p_vlc, "key-pressed", KeyEvent, p_intf );
162 vlc_object_release( p_intf->p_sys->p_cmml_decoder );
164 free( p_intf->p_sys );
168 /*****************************************************************************
170 *****************************************************************************/
171 static void RunIntf( intf_thread_t *p_intf )
173 vout_thread_t * p_vout = NULL;
175 if( InitThread( p_intf ) < 0 )
177 msg_Err( p_intf, "can't initialize CMML interface" );
180 #ifdef CMML_INTF_DEBUG
181 msg_Dbg( p_intf, "CMML intf initialized" );
184 /* if video output is dying, disassociate ourselves from it */
185 if( p_vout && p_vout->b_die )
187 var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
188 vlc_object_release( p_vout );
193 while( !p_intf->b_die )
196 /* find a video output if we currently don't have one */
199 p_vout = vlc_object_find( p_intf->p_sys->p_input,
200 VLC_OBJECT_VOUT, FIND_CHILD );
203 #ifdef CMML_INTF_DEBUG
204 msg_Dbg( p_intf, "found vout thread" );
206 var_AddCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
210 vlc_mutex_lock( &p_intf->change_lock );
215 if( p_intf->p_sys->b_key_pressed )
218 int i, i_action = -1;
219 struct hotkey *p_hotkeys = p_intf->p_vlc->p_hotkeys;
221 /* Find action triggered by hotkey (if any) */
222 var_Get( p_intf->p_vlc, "key-pressed", &val );
224 /* Acknowledge that we've handled the b_key_pressed event */
225 p_intf->p_sys->b_key_pressed = VLC_FALSE;
227 #ifdef CMML_INTF_DEBUG
228 msg_Dbg( p_intf, "Got a keypress: %d", val.i_int );
231 for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
233 if( p_hotkeys[i].i_key == val.i_int )
234 i_action = p_hotkeys[i].i_action;
237 /* What did the user do? */
242 case ACTIONID_NAV_ACTIVATE:
243 FollowAnchor( p_intf );
245 case ACTIONID_HISTORY_BACK:
248 case ACTIONID_HISTORY_FORWARD:
257 vlc_mutex_unlock( &p_intf->change_lock );
259 (void) DisplayPendingAnchor( p_intf, p_vout );
262 msleep( INTF_IDLE_SLEEP );
265 /* if we're here, the video output is dying: release the vout object */
269 var_DelCallback( p_vout, "mouse-clicked", MouseEvent, p_intf );
270 vlc_object_release( p_vout );
273 vlc_object_release( p_intf->p_sys->p_input );
276 /*****************************************************************************
277 * DisplayPendingAnchor: get a pending anchor description/URL from the CMML
278 * decoder and display it on screen
279 *****************************************************************************/
280 static int DisplayPendingAnchor( intf_thread_t *p_intf, vout_thread_t *p_vout )
282 decoder_t *p_cmml_decoder;
283 char *psz_description = NULL;
284 char *psz_url = NULL;
286 intf_thread_t *p_primary_intf;
289 p_cmml_decoder = p_intf->p_sys->p_cmml_decoder;
290 if( var_Get( p_cmml_decoder, "psz-current-anchor-description", &val )
299 psz_description = val.p_address;
301 if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val )
304 psz_url = val.p_address;
309 /* don't display anchor if main interface can display it */
310 p_primary_intf = vlc_object_find( p_intf->p_vlc, VLC_OBJECT_INTF,
315 if( var_Get( p_primary_intf, "intf-displays-cmml-description", &val )
318 if( val.b_bool == VLC_TRUE ) return VLC_TRUE;
322 /* display anchor as subtitle on-screen */
323 if( DisplayAnchor( p_intf, p_vout, psz_description, psz_url )
326 /* text render unsuccessful: do nothing */
330 /* text render successful: clear description */
331 val.p_address = NULL;
332 if( var_Set( p_cmml_decoder, "psz-current-anchor-description", val )
336 "reset of psz-current-anchor-description failed" );
338 free( psz_description );
346 /*****************************************************************************
348 *****************************************************************************/
349 static int InitThread( intf_thread_t * p_intf )
351 /* We might need some locking here */
354 input_thread_t * p_input;
355 decoder_t *p_cmml_decoder;
357 p_cmml_decoder = vlc_object_find( p_intf, VLC_OBJECT_DECODER, FIND_PARENT );
358 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_PARENT );
360 #ifdef CMML_INTF_DEBUG
361 msg_Dbg( p_intf, "cmml decoder at %p, input thread at %p",
362 p_cmml_decoder, p_input );
365 /* Maybe the input just died */
366 if( p_input == NULL )
371 vlc_mutex_lock( &p_intf->change_lock );
373 p_intf->p_sys->p_input = p_input;
374 p_intf->p_sys->p_cmml_decoder = p_cmml_decoder;
376 p_intf->p_sys->b_key_pressed = VLC_FALSE;
378 vlc_mutex_unlock( &p_intf->change_lock );
388 /*****************************************************************************
389 * MouseEvent: callback for mouse events
390 *****************************************************************************/
391 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
392 vlc_value_t oldval, vlc_value_t newval, void *p_data )
394 /* TODO: handle mouse clicks on the anchor text */
399 /*****************************************************************************
400 * KeyEvent: callback for keyboard events
401 *****************************************************************************/
402 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
403 vlc_value_t oldval, vlc_value_t newval, void *p_data )
405 intf_thread_t *p_intf = (intf_thread_t *)p_data;
406 vlc_mutex_lock( &p_intf->change_lock );
408 p_intf->p_sys->b_key_pressed = VLC_TRUE;
410 vlc_mutex_unlock( &p_intf->change_lock );
415 /*****************************************************************************
416 * FollowAnchor: follow the current anchor being displayed to the user
417 *****************************************************************************/
418 static void FollowAnchor ( intf_thread_t *p_intf )
421 decoder_t *p_cmml_decoder;
422 char *psz_url = NULL;
425 msg_Dbg( p_intf, "User followed anchor" );
427 p_sys = p_intf->p_sys;
428 p_cmml_decoder = p_sys->p_cmml_decoder;
430 if( var_Get( p_cmml_decoder, "psz-current-anchor-url", &val ) ==
433 if( val.p_address ) psz_url = val.p_address;
436 #ifdef CMML_INTF_DEBUG
437 msg_Dbg( p_intf, "Current URL is \"%s\"", psz_url );
442 playlist_t *p_playlist;
443 playlist_item_t *p_current_item;
444 char *psz_uri_to_load;
448 p_playlist = (playlist_t *) vlc_object_find( p_intf,
449 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
452 msg_Warn( p_intf, "can't find playlist" );
457 p_current_item = p_playlist->pp_items[p_playlist->i_index];
458 #ifdef CMML_INTF_DEBUG
459 msg_Dbg( p_intf, "Current playlist item URL is \"%s\"",
460 p_current_item->input.psz_uri );
463 psz_uri_to_load = XURL_Concat( p_current_item->input.psz_uri,
466 #ifdef CMML_INTF_DEBUG
467 msg_Dbg( p_intf, "URL to load is \"%s\"", psz_uri_to_load );
470 if( var_Get( p_intf->p_sys->p_input, "time", &time ) )
472 msg_Dbg( p_intf, "couldn't get time from current clip" );
475 i_seconds = time.i_time / 1000000;
476 #ifdef CMML_INTF_DEBUG
477 msg_Dbg( p_intf, "Current time is \"%lld\"", i_seconds );
480 /* TODO: we need a (much) more robust way of detecting whether
481 * the file's a media file ... */
482 if( strstr( psz_uri_to_load, ".anx" ) != NULL )
484 history_t *p_history = NULL;
485 history_item_t *p_history_item = NULL;
488 p_history = GetHistory( p_playlist );
490 /* create history item */
491 psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item );
492 p_history_item = historyItem_New( psz_timed_url, psz_timed_url );
493 free( psz_timed_url );
495 if( !p_history_item )
497 msg_Warn( p_intf, "could not initialise history item" );
501 #ifdef CMML_INTF_DEBUG
502 msg_Dbg( p_intf, "history pre-index %d", p_history->i_index );
504 history_PruneAndInsert( p_history, p_history_item );
505 #ifdef CMML_INTF_DEBUG
506 msg_Dbg( p_intf, "new history item at %p, uri is \"%s\"",
507 p_history_item, p_history_item->psz_uri );
508 msg_Dbg( p_intf, "history index now %d", p_history->i_index );
512 /* free current-anchor-url */
514 val.p_address = NULL;
515 if( var_Set( p_cmml_decoder, "psz-current-anchor-url", val ) !=
518 msg_Dbg( p_intf, "couldn't reset psz-current-anchor-url" );
521 ReplacePlaylistItem( p_playlist, psz_uri_to_load );
525 #ifdef CMML_INTF_DEBUG
526 msg_Dbg( p_intf, "calling browser_Open with \"%s\"", psz_url );
528 (void) browser_Open( psz_url );
529 playlist_Control( p_playlist, PLAYLIST_PAUSE, 0 );
532 free( psz_uri_to_load );
534 vlc_object_release( p_playlist );
539 char *GetTimedURLFromPlaylistItem( intf_thread_t *p_intf,
540 playlist_item_t *p_current_item )
542 #ifdef CMML_INTF_USE_TIMED_URIS
543 char *psz_url = NULL;
544 char *psz_return_value = NULL;
545 char *psz_seconds = NULL;
548 psz_url = XURL_GetWithoutFragment( p_current_item->input->psz_uri );
550 /* Get current time as a string */
551 if( XURL_IsFileURL( psz_url ) == VLC_TRUE )
552 psz_url = xstrcat( psz_url, "#" );
554 psz_url = xstrcat( psz_url, "?" );
556 /* jump back to 2 seconds before where we are now */
557 i_seconds = GetCurrentTimeInSeconds( p_intf->p_sys->p_input ) - 2;
558 psz_seconds = GetTimedURIFragmentForTime( i_seconds < 0 ? 0 : i_seconds );
561 psz_url = xstrcat( psz_url, psz_seconds );
563 psz_return_value = psz_url;
566 return psz_return_value;
570 /* Suppress warning messages about unused functions */
571 p = GetTimedURIFragmentForTime; /* unused */
572 p = GetCurrentTimeInSeconds; /* unused */
574 return strdup( p_current_item->input.psz_uri );
580 * Get the current time, rounded down to the nearest second
582 * http://www.ietf.org/internet-drafts/draft-pfeiffer-temporal-fragments-02.txt
585 int GetCurrentTimeInSeconds( input_thread_t *p_input )
590 var_Get( p_input, "time", &time );
591 i_seconds = time.i_time / 1000000;
597 char *GetTimedURIFragmentForTime( int seconds )
601 asprintf( &psz_time, "%d", seconds );
607 int GoBackCallback( vlc_object_t *p_this, char const *psz_var,
608 vlc_value_t oldval, vlc_value_t newval, void *p_data )
610 intf_thread_t *p_intf = (intf_thread_t *) p_data;
616 int GoForwardCallback( vlc_object_t *p_this, char const *psz_var,
617 vlc_value_t oldval, vlc_value_t newval, void *p_data )
619 intf_thread_t *p_intf = (intf_thread_t *) p_data;
625 int FollowAnchorCallback( vlc_object_t *p_this, char const *psz_var,
626 vlc_value_t oldval, vlc_value_t newval,
629 intf_thread_t *p_intf = (intf_thread_t *) p_data;
630 FollowAnchor( p_intf );
635 void GoBack( intf_thread_t *p_intf )
638 history_t *p_history = NULL;
639 history_item_t *p_history_item = NULL;
640 history_item_t *p_new_history_item = NULL;
641 playlist_t *p_playlist = NULL;
642 char *psz_timed_url = NULL;
643 playlist_item_t *p_current_item;
645 #ifdef CMML_INTF_DEBUG
646 msg_Dbg( p_intf, "Going back in navigation history" );
649 /* Find the playlist */
650 p_playlist = (playlist_t *) vlc_object_find( p_intf,
651 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
654 msg_Warn( p_intf, "can't find playlist" );
658 /* Retrieve navigation history from playlist */
659 if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS ||
662 /* History doesn't exist yet: ignore user's request */
663 msg_Warn( p_intf, "can't go back: no history exists yet" );
664 vlc_object_release( p_playlist );
668 p_history = history.p_address;
669 #ifdef CMML_INTF_DEBUG
670 msg_Dbg( p_intf, "back: nav history retrieved from %p", p_history );
671 msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index,
672 p_history->p_xarray );
675 /* Check whether we can go back in the history */
676 if( history_CanGoBack( p_history ) == VLC_FALSE )
678 msg_Warn( p_intf, "can't go back: already at beginning of history" );
679 vlc_object_release( p_playlist );
683 p_current_item = p_playlist->pp_items[p_playlist->i_index];
685 /* Save the currently-playing media in a new history item */
686 psz_timed_url = GetTimedURLFromPlaylistItem( p_intf, p_current_item );
687 p_new_history_item = historyItem_New( psz_timed_url, psz_timed_url );
688 free( psz_timed_url );
690 if( !p_new_history_item )
692 #ifdef CMML_INTF_DEBUG
693 msg_Dbg( p_intf, "back: could not initialise new history item" );
695 vlc_object_release( p_playlist );
699 /* Go back in the history, saving the currently-playing item */
700 (void) history_GoBackSavingCurrentItem( p_history, p_new_history_item );
701 p_history_item = history_Item( p_history );
703 #ifdef CMML_INTF_DEBUG
704 msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index );
705 msg_Dbg( p_intf, "got previous history item: %p", p_history_item );
706 msg_Dbg( p_intf, "prev history item URL: \"%s\"", p_history_item->psz_uri );
709 ReplacePlaylistItem( p_playlist, p_history_item->psz_uri );
710 vlc_object_release( p_playlist );
714 void GoForward( intf_thread_t *p_intf )
717 history_t *p_history = NULL;
718 history_item_t *p_history_item = NULL;
719 history_item_t *p_new_history_item = NULL;
720 playlist_t *p_playlist = NULL;
721 playlist_item_t *p_current_item;
723 #ifdef CMML_INTF_DEBUG
724 msg_Dbg( p_intf, "Going forward in navigation history" );
727 /* Find the playlist */
728 p_playlist = (playlist_t *) vlc_object_find( p_intf,
729 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
732 msg_Warn( p_intf, "can't find playlist" );
736 /* Retrieve navigation history from playlist */
737 if( var_Get( p_playlist, "navigation-history", &history ) != VLC_SUCCESS ||
740 /* History doesn't exist yet: ignore user's request */
741 msg_Warn( p_intf, "can't go back: no history exists yet" );
742 vlc_object_release( p_playlist );
746 p_history = history.p_address;
747 #ifdef CMML_INTF_DEBUG
748 msg_Dbg( p_intf, "forward: nav history retrieved from %p", p_history );
749 msg_Dbg( p_intf, "nav history index:%d, p_xarray:%p", p_history->i_index,
750 p_history->p_xarray );
753 /* Check whether we can go forward in the history */
754 if( history_CanGoForward( p_history ) == VLC_FALSE )
756 msg_Warn( p_intf, "can't go forward: already at end of history" );
757 vlc_object_release( p_playlist );
761 /* Save the currently-playing media in a new history item */
762 p_new_history_item = malloc( sizeof(history_item_t) );
763 if( !p_new_history_item )
765 #ifdef CMML_INTF_DEBUG
766 msg_Dbg( p_intf, "forward: could not initialise new history item" );
768 vlc_object_release( p_playlist );
771 p_current_item = p_playlist->pp_items[p_playlist->i_index];
772 p_new_history_item->psz_uri = GetTimedURLFromPlaylistItem( p_intf,
774 p_new_history_item->psz_name = p_new_history_item->psz_uri;
776 /* Go forward in the history, saving the currently-playing item */
777 (void) history_GoForwardSavingCurrentItem( p_history, p_new_history_item );
778 p_history_item = history_Item( p_history );
780 #ifdef CMML_INTF_DEBUG
781 msg_Dbg( p_intf, "retrieving item from h index %d", p_history->i_index );
782 msg_Dbg( p_intf, "got next history item: %p", p_history_item );
783 msg_Dbg( p_intf, "next history item URL: \"%s\"", p_history_item->psz_uri );
786 ReplacePlaylistItem( p_playlist, p_history_item->psz_uri );
787 vlc_object_release( p_playlist );
790 static void ReplacePlaylistItem( playlist_t *p_playlist, char *psz_uri )
792 playlist_Stop( p_playlist );
793 (void) playlist_Add( p_playlist, psz_uri, psz_uri,
794 PLAYLIST_REPLACE, p_playlist->i_index );
795 playlist_Goto( p_playlist, p_playlist->i_index );
798 /****************************************************************************
799 * DisplayAnchor: displays an anchor on the given video output
800 ****************************************************************************/
801 static int DisplayAnchor( intf_thread_t *p_intf,
802 vout_thread_t *p_vout,
803 char *psz_anchor_description,
804 char *psz_anchor_url )
806 int i_margin_h, i_margin_v;
816 text_style_t *p_style = NULL;
818 text_style_t blue_with_underline = default_text_style;
819 blue_with_underline.b_underline = VLC_TRUE;
820 blue_with_underline.i_color = 0x22ff22;
824 /* Should display subtitle underlined and in blue,
825 * but it looks like VLC doesn't implement any
826 * text styles yet. D'oh! */
827 p_style = &blue_with_underline;
831 /* TODO: p_subpicture doesn't have the proper i_x and i_y
832 * coordinates. Need to look at the subpicture display system to
834 if ( vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN,
835 psz_anchor_description, p_style, OSD_ALIGN_BOTTOM,
836 i_margin_h, i_margin_v, i_now, 0 ) == VLC_SUCCESS )
838 /* Displayed successfully */
839 #ifdef CMML_INTF_SUBPICTURE_DEBUG
840 msg_Dbg( p_intf, "subpicture created at (%d, %d) (%d, %d)",
841 p_subpicture->i_x, p_subpicture->i_y,
842 p_subpicture->i_width, p_subpicture->i_height );
853 msg_Dbg( p_intf, "DisplayAnchor couldn't find a video output" );
860 static history_t * GetHistory( playlist_t *p_playlist )
863 history_t *p_history = NULL;
865 if( var_Get( p_playlist, "navigation-history", &val ) != VLC_SUCCESS )
867 /* history doesn't exist yet: need to create it */
868 history_t *new_history = history_New();
869 val.p_address = new_history;
870 var_Create( p_playlist, "navigation-history",
871 VLC_VAR_ADDRESS|VLC_VAR_DOINHERIT );
872 if( var_Set( p_playlist, "navigation-history", val ) != VLC_SUCCESS )
874 msg_Warn( p_playlist, "could not initialise history" );
878 p_history = new_history;
879 #ifdef CMML_INTF_HISTORY_DEBUG
880 msg_Dbg( p_playlist, "nav history created at %p", new_history );
881 msg_Dbg( p_playlist, "nav history index:%d, p_xarray:%p",
882 p_history->i_index, p_history->p_xarray );
888 p_history = val.p_address;
889 #ifdef CMML_INTF_HISTORY_DEBUG
890 msg_Dbg( p_playlist, "nav history retrieved from %p", p_history );