1 /*****************************************************************************
2 * ncurses.c : NCurses plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001-2004 VideoLAN
7 * Authors: Sam Hocevar <sam@zoy.org>
8 * Laurent Aimar <fenrir@via.ecp.fr>
9 * Yoann Peronneau <yoann@videolan.org>
10 * Derk-Jan Hartman <hartman at videolan dot org>
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() */
32 #include <errno.h> /* ENOMEM */
43 #ifdef HAVE_SYS_STAT_H
44 # include <sys/stat.h>
46 #if (!defined( WIN32 ) || defined(__MINGW32__))
47 /* Mingw has its own version of dirent */
52 #define CDDA_MRL "cddax://"
54 #define CDDA_MRL "cdda://"
58 #define VCD_MRL "vcdx://"
60 #define VCD_MRL "vcdx://"
63 #define SEARCH_CHAIN_SIZE 20
64 #define OPEN_CHAIN_SIZE 50
66 /*****************************************************************************
68 *****************************************************************************/
69 static int Open ( vlc_object_t * );
70 static void Close ( vlc_object_t * );
72 static void Run ( intf_thread_t * );
73 static void PlayPause ( intf_thread_t * );
74 static void Eject ( intf_thread_t * );
76 static int HandleKey ( intf_thread_t *, int );
77 static void Redraw ( intf_thread_t *, time_t * );
78 static void SearchPlaylist ( intf_thread_t *, char * );
79 static void ManageSlider ( intf_thread_t * );
80 static void ReadDir ( intf_thread_t * );
82 /*****************************************************************************
84 *****************************************************************************/
86 #define BROWSE_TEXT N_("Filebrowser starting point")
87 #define BROWSE_LONGTEXT N_( \
88 "This option allows you to specify the directory the ncurses filebrowser " \
89 "will show you initially.")
92 set_description( _("ncurses interface") );
93 set_capability( "interface", 10 );
94 set_category( CAT_INTERFACE );
95 set_subcategory( SUBCAT_INTERFACE_GENERAL );
96 set_callbacks( Open, Close );
97 add_shortcut( "curses" );
98 add_directory( "browse-dir", NULL, NULL, BROWSE_TEXT, BROWSE_LONGTEXT, VLC_FALSE );
101 /*****************************************************************************
102 * intf_sys_t: description and status of ncurses interface
103 *****************************************************************************/
122 playlist_t *p_playlist;
123 input_thread_t *p_input;
133 int i_box_lines_total;
136 int i_box_plidx; /* Playlist index */
137 int b_box_plidx_follow;
138 int i_box_bidx; /* browser index */
142 msg_subscription_t* p_sub; /* message bank subscription */
144 char *psz_search_chain; /* for playlist searching */
145 char *psz_old_search; /* for searching next */
148 char *psz_open_chain;
150 char *psz_current_dir;
152 struct dir_entry_t **pp_dir_entries;
155 static void DrawBox( WINDOW *win, int y, int x, int h, int w, char *title );
156 static void DrawLine( WINDOW *win, int y, int x, int w );
157 static void DrawEmptyLine( WINDOW *win, int y, int x, int w );
159 /*****************************************************************************
160 * Open: initialize and create window
161 *****************************************************************************/
162 static int Open( vlc_object_t *p_this )
164 intf_thread_t *p_intf = (intf_thread_t *)p_this;
168 /* Allocate instance and initialize some members */
169 p_sys = p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
170 p_sys->p_playlist = NULL;
171 p_sys->p_input = NULL;
172 p_sys->f_slider = 0.0;
173 p_sys->f_slider_old = 0.0;
174 p_sys->i_box_type = BOX_PLAYLIST;
175 p_sys->i_box_lines = 0;
176 p_sys->i_box_start= 0;
177 p_sys->i_box_lines_total = 0;
178 p_sys->b_box_plidx_follow = VLC_TRUE;
179 p_sys->b_box_cleared = VLC_FALSE;
180 p_sys->i_box_plidx = 0;
181 p_sys->i_box_bidx = 0;
182 p_sys->p_sub = msg_Subscribe( p_intf );
184 /* Initialize the curses library */
185 p_sys->w = initscr();
186 keypad( p_sys->w, TRUE );
187 /* Don't do NL -> CR/NL */
189 /* Take input chars one at a time */
199 /* exported function */
200 p_intf->pf_run = Run;
204 var_Set( p_intf->p_vlc, "verbose", val );
206 /* Initialize search chain */
207 p_sys->psz_search_chain = (char *)malloc( SEARCH_CHAIN_SIZE + 1 );
208 p_sys->psz_old_search = NULL;
209 p_sys->i_before_search = 0;
211 /* Initialize open chain */
212 p_sys->psz_open_chain = (char *)malloc( OPEN_CHAIN_SIZE + 1 );
214 /* Initialize browser options */
215 var_Create( p_intf, "browse-dir", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
216 var_Get( p_intf, "browse-dir", &val);
218 if( val.psz_string && *val.psz_string )
220 p_sys->psz_current_dir = strdup( val.psz_string);
221 free( val.psz_string );
225 p_sys->psz_current_dir = strdup( p_intf->p_vlc->psz_homedir );
228 p_sys->i_dir_entries = 0;
229 p_sys->pp_dir_entries = NULL;
235 /*****************************************************************************
236 * Close: destroy interface window
237 *****************************************************************************/
238 static void Close( vlc_object_t *p_this )
240 intf_thread_t *p_intf = (intf_thread_t *)p_this;
241 intf_sys_t *p_sys = p_intf->p_sys;
244 for( i = 0; i < p_sys->i_dir_entries; i++ )
246 struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
247 if( p_dir_entry->psz_path ) free( p_dir_entry->psz_path );
248 REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
249 if( p_dir_entry ) free( p_dir_entry );
251 p_sys->pp_dir_entries = NULL;
253 if( p_sys->psz_current_dir ) free( p_sys->psz_current_dir );
254 if( p_sys->psz_search_chain ) free( p_sys->psz_search_chain );
255 if( p_sys->psz_old_search ) free( p_sys->psz_old_search );
256 if( p_sys->psz_open_chain ) free( p_sys->psz_open_chain );
260 vlc_object_release( p_sys->p_input );
262 if( p_sys->p_playlist )
264 vlc_object_release( p_sys->p_playlist );
267 /* Close the ncurses interface */
270 msg_Unsubscribe( p_intf, p_sys->p_sub );
272 /* Destroy structure */
276 /*****************************************************************************
277 * Run: ncurses thread
278 *****************************************************************************/
279 static void Run( intf_thread_t *p_intf )
281 intf_sys_t *p_sys = p_intf->p_sys;
284 time_t t_last_refresh;
287 * force drawing the interface for the first time
289 t_last_refresh = ( time( 0 ) - 1);
291 while( !p_intf->b_die )
293 msleep( INTF_IDLE_SLEEP );
295 /* Update the input */
296 if( p_sys->p_playlist == NULL )
298 p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
300 if( p_sys->p_playlist )
302 vlc_mutex_lock( &p_sys->p_playlist->object_lock );
303 if( p_sys->p_input == NULL )
305 p_sys->p_input = p_sys->p_playlist->p_input;
308 if( !p_sys->p_input->b_dead )
310 vlc_object_yield( p_sys->p_input );
314 else if( p_sys->p_input->b_dead )
316 vlc_object_release( p_sys->p_input );
317 p_sys->p_input = NULL;
318 p_sys->f_slider = p_sys->f_slider_old = 0.0;
319 p_sys->b_box_cleared = VLC_FALSE;
321 vlc_mutex_unlock( &p_sys->p_playlist->object_lock );
324 if( p_sys->b_box_plidx_follow && p_sys->p_playlist->i_index >= 0 )
326 p_sys->i_box_plidx = p_sys->p_playlist->i_index;
329 while( ( i_key = getch()) != -1 )
332 * HandleKey returns 1 if the screen needs to be redrawn
334 if ( HandleKey( p_intf, i_key ) )
336 Redraw( p_intf, &t_last_refresh );
340 if( p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared )
343 Redraw( p_intf, &t_last_refresh );
344 p_sys->b_box_cleared = VLC_TRUE;
348 * redraw the screen every second
350 if ( (time(0) - t_last_refresh) >= 1 )
352 ManageSlider ( p_intf );
353 Redraw( p_intf, &t_last_refresh );
358 /* following functions are local */
360 static int HandleKey( intf_thread_t *p_intf, int i_key )
362 intf_sys_t *p_sys = p_intf->p_sys;
365 if( p_sys->i_box_type == BOX_PLAYLIST && p_sys->p_playlist )
367 int b_ret = VLC_TRUE;
372 /* Playlist Settings */
374 var_Get( p_sys->p_playlist, "random", &val );
375 val.b_bool = !val.b_bool;
376 var_Set( p_sys->p_playlist, "random", val );
379 var_Get( p_sys->p_playlist, "loop", &val );
380 val.b_bool = !val.b_bool;
381 var_Set( p_sys->p_playlist, "loop", val );
384 var_Get( p_sys->p_playlist, "repeat", &val );
385 val.b_bool = !val.b_bool;
386 var_Set( p_sys->p_playlist, "repeat", val );
391 playlist_Sort( p_sys->p_playlist, SORT_TITLE, ORDER_NORMAL );
394 playlist_Sort( p_sys->p_playlist, SORT_TITLE, ORDER_REVERSE );
397 /* Playlist navigation */
399 p_sys->i_box_plidx = 0;
402 p_sys->i_box_plidx = p_sys->p_playlist->i_size - 1;
405 p_sys->i_box_plidx--;
408 p_sys->i_box_plidx++;
411 p_sys->i_box_plidx -= p_sys->i_box_lines;
414 p_sys->i_box_plidx += p_sys->i_box_lines;
420 int i_item = p_sys->p_playlist->i_index;
422 playlist_LockDelete( p_sys->p_playlist, p_sys->i_box_plidx );
423 if( i_item < p_sys->p_playlist->i_size &&
424 i_item != p_sys->p_playlist->i_index )
426 playlist_Goto( p_sys->p_playlist, i_item );
433 playlist_Goto( p_sys->p_playlist, p_sys->i_box_plidx );
442 if( p_sys->i_box_plidx >= p_sys->p_playlist->i_size ) p_sys->i_box_plidx = p_sys->p_playlist->i_size - 1;
443 if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
444 if( p_sys->i_box_plidx == p_sys->p_playlist->i_index )
445 p_sys->b_box_plidx_follow = VLC_TRUE;
447 p_sys->b_box_plidx_follow = VLC_FALSE;
451 if( p_sys->i_box_type == BOX_BROWSE )
453 vlc_bool_t b_ret = VLC_TRUE;
454 /* Browser navigation */
458 p_sys->i_box_bidx = 0;
461 p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
470 p_sys->i_box_bidx -= p_sys->i_box_lines;
473 p_sys->i_box_bidx += p_sys->i_box_lines;
478 if( p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file )
480 int i_size_entry = strlen( p_sys->psz_current_dir ) +
481 strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
482 char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
484 sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
485 playlist_Add( p_sys->p_playlist, psz_uri,
487 PLAYLIST_APPEND, PLAYLIST_END );
488 p_sys->i_box_type = BOX_PLAYLIST;
493 int i_size_entry = strlen( p_sys->psz_current_dir ) +
494 strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
495 char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
497 sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
499 p_sys->psz_current_dir = strdup( psz_uri );
510 if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
511 if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
515 else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO )
520 p_sys->i_box_start = 0;
523 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
526 if( p_sys->i_box_start > 0 ) p_sys->i_box_start--;
529 if( p_sys->i_box_start < p_sys->i_box_lines_total - 1 )
531 p_sys->i_box_start++;
535 p_sys->i_box_start -= p_sys->i_box_lines;
536 if( p_sys->i_box_start < 0 ) p_sys->i_box_start = 0;
539 p_sys->i_box_start += p_sys->i_box_lines;
540 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
542 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
549 else if( p_sys->i_box_type == BOX_NONE )
555 ManageSlider ( p_intf );
558 p_sys->f_slider = 99.9;
559 ManageSlider ( p_intf );
562 p_sys->f_slider += 5.0;
563 if( p_sys->f_slider >= 99.0 ) p_sys->f_slider = 99.0;
564 ManageSlider ( p_intf );
567 p_sys->f_slider -= 5.0;
568 if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
569 ManageSlider ( p_intf );
576 else if( p_sys->i_box_type == BOX_SEARCH && p_sys->psz_search_chain )
579 i_chain_len = strlen( p_sys->psz_search_chain );
587 if( i_chain_len > 0 )
589 p_sys->psz_old_search = strdup( p_sys->psz_search_chain );
591 else if( p_sys->psz_old_search )
593 SearchPlaylist( p_intf, p_sys->psz_old_search );
595 p_sys->i_box_type = BOX_PLAYLIST;
597 case 0x1b: /* Esc. */
598 p_sys->i_box_plidx = p_sys->i_before_search;
599 p_sys->i_box_type = BOX_PLAYLIST;
602 if( i_chain_len > 0 )
604 p_sys->psz_search_chain[ i_chain_len - 1 ] = '\0';
608 if( i_chain_len < SEARCH_CHAIN_SIZE )
610 p_sys->psz_search_chain[ i_chain_len++ ] = i_key;
611 p_sys->psz_search_chain[ i_chain_len ] = 0;
615 if( p_sys->psz_old_search )
617 free( p_sys->psz_old_search );
618 p_sys->psz_old_search = NULL;
620 SearchPlaylist( p_intf, p_sys->psz_search_chain );
623 else if( p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain )
625 int i_chain_len = strlen( p_sys->psz_open_chain );
626 playlist_t *p_playlist = p_sys->p_playlist;
635 if( p_playlist && i_chain_len > 0 )
637 playlist_Add( p_playlist, p_sys->psz_open_chain,
638 p_sys->psz_open_chain,
639 PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END );
640 p_sys->b_box_plidx_follow = VLC_TRUE;
642 p_sys->i_box_type = BOX_PLAYLIST;
644 case 0x1b: /* Esc. */
645 p_sys->i_box_type = BOX_PLAYLIST;
648 if( i_chain_len > 0 )
650 p_sys->psz_open_chain[ i_chain_len - 1 ] = '\0';
654 if( i_chain_len < OPEN_CHAIN_SIZE )
656 p_sys->psz_open_chain[ i_chain_len++ ] = i_key;
657 p_sys->psz_open_chain[ i_chain_len ] = 0;
671 p_intf->p_vlc->b_die = VLC_TRUE;
676 if( p_sys->i_box_type == BOX_INFO )
677 p_sys->i_box_type = BOX_NONE;
679 p_sys->i_box_type = BOX_INFO;
680 p_sys->i_box_lines_total = 0;
683 if( p_sys->i_box_type == BOX_LOG )
684 p_sys->i_box_type = BOX_NONE;
686 p_sys->i_box_type = BOX_LOG;
689 if( p_sys->i_box_type == BOX_PLAYLIST )
690 p_sys->i_box_type = BOX_NONE;
692 p_sys->i_box_type = BOX_PLAYLIST;
695 if( p_sys->i_box_type == BOX_BROWSE )
696 p_sys->i_box_type = BOX_NONE;
698 p_sys->i_box_type = BOX_BROWSE;
702 if( p_sys->i_box_type == BOX_HELP )
703 p_sys->i_box_type = BOX_NONE;
705 p_sys->i_box_type = BOX_HELP;
706 p_sys->i_box_lines_total = 0;
709 if( p_sys->i_box_type != BOX_SEARCH )
711 if( p_sys->psz_search_chain == NULL )
715 p_sys->psz_search_chain[0] = '\0';
716 p_sys->b_box_plidx_follow = VLC_FALSE;
717 p_sys->i_before_search = p_sys->i_box_plidx;
718 p_sys->i_box_type = BOX_SEARCH;
722 if( p_sys->i_box_type != BOX_OPEN )
724 if( p_sys->psz_open_chain == NULL )
728 p_sys->psz_open_chain[0] = '\0';
729 p_sys->i_box_type = BOX_OPEN;
735 p_sys->f_slider += 1.0;
736 if( p_sys->f_slider > 99.9 ) p_sys->f_slider = 99.9;
737 ManageSlider ( p_intf );
741 p_sys->f_slider -= 1.0;
742 if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
743 ManageSlider ( p_intf );
749 vout_thread_t *p_vout;
750 if( p_intf->p_sys->p_input )
752 p_vout = vlc_object_find( p_intf->p_sys->p_input,
753 VLC_OBJECT_VOUT, FIND_CHILD );
756 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
757 vlc_object_release( p_vout );
768 if( p_intf->p_sys->p_playlist )
770 playlist_Stop( p_intf->p_sys->p_playlist );
781 val.b_bool = VLC_TRUE;
782 var_Set( p_sys->p_input, "prev-title", val );
789 val.b_bool = VLC_TRUE;
790 var_Set( p_sys->p_input, "next-title", val );
797 val.b_bool = VLC_TRUE;
798 var_Set( p_sys->p_input, "prev-chapter", val );
805 val.b_bool = VLC_TRUE;
806 var_Set( p_sys->p_input, "next-chapter", val );
811 if( p_intf->p_sys->p_playlist )
813 playlist_Prev( p_intf->p_sys->p_playlist );
819 if( p_intf->p_sys->p_playlist )
821 playlist_Next( p_intf->p_sys->p_playlist );
827 aout_VolumeUp( p_intf, 1, NULL );
832 aout_VolumeDown( p_intf, 1, NULL );
837 * ^l should clear and redraw the screen
848 static void ManageSlider ( intf_thread_t *p_intf )
850 intf_sys_t *p_sys = p_intf->p_sys;
851 input_thread_t *p_input = p_sys->p_input;
854 if( p_input == NULL )
858 var_Get( p_input, "state", &val );
859 if( val.i_int != PLAYING_S )
864 var_Get( p_input, "position", &val );
865 if( p_sys->f_slider == p_sys->f_slider_old )
868 p_sys->f_slider_old = 100 * val.f_float;
872 p_sys->f_slider_old = p_sys->f_slider;
874 val.f_float = p_sys->f_slider / 100.0;
875 var_Set( p_input, "position", val );
879 static void SearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring )
885 intf_sys_t *p_sys = p_intf->p_sys;
886 playlist_t *p_playlist = p_sys->p_playlist;
888 if( p_sys->i_before_search >= 0 )
889 i_first = p_sys->i_before_search;
891 if( ( ! psz_searchstring ) || strlen( psz_searchstring ) <= 0 )
893 p_sys->i_box_plidx = p_sys->i_before_search;
897 for( i_current = i_first + 1; i_current < p_playlist->i_size;
900 if( strcasestr( p_playlist->pp_items[i_current]->input.psz_name,
901 psz_searchstring ) != NULL
902 || strcasestr( p_playlist->pp_items[i_current]->input.psz_uri,
903 psz_searchstring ) != NULL )
912 for( i_current = 0; i_current < i_first; i_current++ )
914 if( strcasestr( p_playlist->pp_items[i_current]->input.psz_name,
915 psz_searchstring ) != NULL
916 || strcasestr( p_playlist->pp_items[i_current]->input.psz_uri,
917 psz_searchstring ) != NULL )
926 if( i_item < 0 || i_item >= p_playlist->i_size ) return;
928 p_sys->i_box_plidx = i_item;
932 static void mvnprintw( int y, int x, int w, const char *p_fmt, ... )
938 va_start ( vl_args, p_fmt );
939 vasprintf ( &p_buf, p_fmt, vl_args );
948 if( ( i_len = strlen( p_buf ) ) > w )
950 int i_cut = i_len - w;
951 int x1 = i_len/2 - i_cut/2;
956 memmove( &p_buf[x1], &p_buf[x2], i_len - x2 );
965 mvprintw( y, x, "%s", p_buf );
969 mvprintw( y, x, "%s", p_buf );
970 mvhline( y, x + i_len, ' ', w - i_len );
974 static void MainBoxWrite( intf_thread_t *p_intf, int l, int x, const char *p_fmt, ... )
976 intf_sys_t *p_sys = p_intf->p_sys;
981 if( l < p_sys->i_box_start || l - p_sys->i_box_start >= p_sys->i_box_lines )
986 va_start ( vl_args, p_fmt );
987 vasprintf ( &p_buf, p_fmt, vl_args );
995 mvnprintw( p_sys->i_box_y + l - p_sys->i_box_start, x, COLS - x - 1, "%s", p_buf );
998 static void Redraw ( intf_thread_t *p_intf, time_t *t_last_refresh )
1000 intf_sys_t *p_sys = p_intf->p_sys;
1001 input_thread_t *p_input = p_sys->p_input;
1009 attrset ( A_REVERSE );
1010 mvnprintw( y, 0, COLS, "VLC media player" " (ncurses interface) [ h for help ]" );
1011 attroff ( A_REVERSE );
1015 if( p_input && !p_input->b_dead )
1017 char buf1[MSTRTIME_MAX_SIZE];
1018 char buf2[MSTRTIME_MAX_SIZE];
1020 vlc_value_t val_list;
1023 mvnprintw( y++, 0, COLS, " Source : %s",
1024 p_input->input.p_item->psz_uri );
1027 var_Get( p_input, "state", &val );
1028 if( val.i_int == PLAYING_S )
1030 mvnprintw( y++, 0, COLS, " State : Playing" );
1032 else if( val.i_int == PAUSE_S )
1034 mvnprintw( y++, 0, COLS, " State : Paused" );
1040 if( val.i_int != INIT_S && val.i_int != END_S )
1042 audio_volume_t i_volume;
1045 var_Get( p_input, "time", &val );
1046 msecstotimestr( buf1, val.i_time / 1000 );
1048 var_Get( p_input, "length", &val );
1049 msecstotimestr( buf2, val.i_time / 1000 );
1051 mvnprintw( y++, 0, COLS, " Position : %s/%s (%.2f%%)", buf1, buf2, p_sys->f_slider );
1054 aout_VolumeGet( p_intf, &i_volume );
1055 mvnprintw( y++, 0, COLS, " Volume : %i%%", i_volume*200/AOUT_VOLUME_MAX );
1058 if( !var_Get( p_input, "title", &val ) )
1060 var_Change( p_input, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
1061 if( val_list.p_list->i_count > 0 )
1063 mvnprintw( y++, 0, COLS, " Title : %d/%d", val.i_int, val_list.p_list->i_count );
1065 var_Change( p_input, "title", VLC_VAR_FREELIST, &val_list, NULL );
1069 if( !var_Get( p_input, "chapter", &val ) )
1071 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
1072 if( val_list.p_list->i_count > 0 )
1074 mvnprintw( y++, 0, COLS, " Chapter : %d/%d", val.i_int, val_list.p_list->i_count );
1076 var_Change( p_input, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
1086 mvnprintw( y++, 0, COLS, "Source: <no current item>" );
1087 DrawEmptyLine( p_sys->w, y++, 0, COLS );
1088 DrawEmptyLine( p_sys->w, y++, 0, COLS );
1089 DrawEmptyLine( p_sys->w, y++, 0, COLS );
1092 DrawBox( p_sys->w, y, 0, 3, COLS, "" );
1093 DrawEmptyLine( p_sys->w, y+1, 1, COLS-2);
1094 DrawLine( p_sys->w, y+1, 1, (int)(p_intf->p_sys->f_slider/100.0 * (COLS -2)) );
1097 p_sys->i_box_y = y + 1;
1098 p_sys->i_box_lines = LINES - y - 2;
1103 if( p_sys->i_box_type == BOX_HELP )
1107 DrawBox( p_sys->w, y++, 0, h, COLS, " Help " );
1109 MainBoxWrite( p_intf, l++, 1, "[Display]" );
1110 MainBoxWrite( p_intf, l++, 1, " h,H Show/Hide help box" );
1111 MainBoxWrite( p_intf, l++, 1, " i Show/Hide info box" );
1112 MainBoxWrite( p_intf, l++, 1, " L Show/Hide messages box" );
1113 MainBoxWrite( p_intf, l++, 1, " P Show/Hide playlist box" );
1114 MainBoxWrite( p_intf, l++, 1, " B Show/Hide filebrowser" );
1115 MainBoxWrite( p_intf, l++, 1, "" );
1117 MainBoxWrite( p_intf, l++, 1, "[Global]" );
1118 MainBoxWrite( p_intf, l++, 1, " q, Q Quit" );
1119 MainBoxWrite( p_intf, l++, 1, " s Stop" );
1120 MainBoxWrite( p_intf, l++, 1, " <space> Pause/Play" );
1121 MainBoxWrite( p_intf, l++, 1, " f Toggle Fullscreen" );
1122 MainBoxWrite( p_intf, l++, 1, " n, p Next/Previous playlist item" );
1123 MainBoxWrite( p_intf, l++, 1, " [, ] Next/Previous title" );
1124 MainBoxWrite( p_intf, l++, 1, " <, > Next/Previous chapter" );
1125 MainBoxWrite( p_intf, l++, 1, " <right> Seek +1%%" );
1126 MainBoxWrite( p_intf, l++, 1, " <left> Seek -1%%" );
1127 MainBoxWrite( p_intf, l++, 1, " a Volume Up" );
1128 MainBoxWrite( p_intf, l++, 1, " z Volume Down" );
1129 MainBoxWrite( p_intf, l++, 1, "" );
1131 MainBoxWrite( p_intf, l++, 1, "[Playlist]" );
1132 MainBoxWrite( p_intf, l++, 1, " r Random" );
1133 MainBoxWrite( p_intf, l++, 1, " l Loop Playlist" );
1134 MainBoxWrite( p_intf, l++, 1, " R Repeat item" );
1135 MainBoxWrite( p_intf, l++, 1, " o Order Playlist by title" );
1136 MainBoxWrite( p_intf, l++, 1, " O Reverse order Playlist by title" );
1137 MainBoxWrite( p_intf, l++, 1, " / Look for an item" );
1138 MainBoxWrite( p_intf, l++, 1, " A Add an entry" );
1139 MainBoxWrite( p_intf, l++, 1, " D, <del> Delete an entry" );
1140 MainBoxWrite( p_intf, l++, 1, " <backspace> Delete an entry" );
1141 MainBoxWrite( p_intf, l++, 1, "" );
1143 MainBoxWrite( p_intf, l++, 1, "[Boxes]" );
1144 MainBoxWrite( p_intf, l++, 1, " <up>,<down> Navigate through the box line by line" );
1145 MainBoxWrite( p_intf, l++, 1, " <pgup>,<pgdown> Navigate through the box page by page" );
1146 MainBoxWrite( p_intf, l++, 1, "" );
1148 MainBoxWrite( p_intf, l++, 1, "[Player]" );
1149 MainBoxWrite( p_intf, l++, 1, " <up>,<down> Seek +/-5%%" );
1150 MainBoxWrite( p_intf, l++, 1, "" );
1152 MainBoxWrite( p_intf, l++, 1, "[Miscellaneous]" );
1153 MainBoxWrite( p_intf, l++, 1, " Ctrl-l Refresh the screen" );
1155 p_sys->i_box_lines_total = l;
1156 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1158 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1161 if( l - p_sys->i_box_start < p_sys->i_box_lines )
1163 y += l - p_sys->i_box_start;
1167 y += p_sys->i_box_lines;
1170 else if( p_sys->i_box_type == BOX_INFO )
1174 DrawBox( p_sys->w, y++, 0, h, COLS, " Information " );
1179 vlc_mutex_lock( &p_input->input.p_item->lock );
1180 for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
1182 info_category_t *p_category = p_input->input.p_item->pp_categories[i];
1183 if( y >= y_end ) break;
1184 MainBoxWrite( p_intf, l++, 1, " [%s]", p_category->psz_name );
1185 for ( j = 0; j < p_category->i_infos; j++ )
1187 info_t *p_info = p_category->pp_infos[j];
1188 if( y >= y_end ) break;
1189 MainBoxWrite( p_intf, l++, 1, " %s: %s", p_info->psz_name, p_info->psz_value );
1192 vlc_mutex_unlock( &p_input->input.p_item->lock );
1196 MainBoxWrite( p_intf, l++, 1, "No item currently playing" );
1198 p_sys->i_box_lines_total = l;
1199 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1201 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1204 if( l - p_sys->i_box_start < p_sys->i_box_lines )
1206 y += l - p_sys->i_box_start;
1210 y += p_sys->i_box_lines;
1213 else if( p_sys->i_box_type == BOX_LOG )
1219 DrawBox( p_sys->w, y++, 0, h, COLS, " Logs " );
1221 i_start = p_intf->p_sys->p_sub->i_start;
1223 vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1224 i_stop = *p_intf->p_sys->p_sub->pi_stop;
1225 vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1229 static const char *ppsz_type[4] = { "", "error", "warning", "debug" };
1230 if( i_line >= h - 2 )
1236 if( i_stop < 0 ) i_stop += VLC_MSG_QSIZE;
1237 if( i_stop == i_start )
1241 mvnprintw( y + h-2-i_line, 1, COLS - 2, " [%s] %s",
1242 ppsz_type[p_sys->p_sub->p_msg[i_stop].i_type],
1243 p_sys->p_sub->p_msg[i_stop].psz_msg );
1246 vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1247 p_intf->p_sys->p_sub->i_start = i_stop;
1248 vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1251 else if( p_sys->i_box_type == BOX_BROWSE )
1254 int i_start, i_stop;
1256 DrawBox( p_sys->w, y++, 0, h, COLS, " Browse " );
1258 if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_plidx = p_sys->i_dir_entries - 1;
1259 if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
1261 if( p_sys->i_box_bidx < (h - 2)/2 )
1266 else if( p_sys->i_dir_entries - p_sys->i_box_bidx > (h - 2)/2 )
1268 i_start = p_sys->i_box_bidx - (h - 2)/2;
1269 i_stop = i_start + h - 2;
1273 i_stop = p_sys->i_dir_entries;
1274 i_start = p_sys->i_dir_entries - (h - 2);
1280 if( i_stop > p_sys->i_dir_entries )
1282 i_stop = p_sys->i_dir_entries;
1285 for( i_item = i_start; i_item < i_stop; i_item++ )
1287 vlc_bool_t b_selected = ( p_sys->i_box_bidx == i_item );
1289 if( y >= y_end ) break;
1292 attrset( A_REVERSE );
1294 mvnprintw( y++, 1, COLS - 2, "%c %s", p_sys->pp_dir_entries[i_item]->b_file == VLC_TRUE ? '-' : '+',
1295 p_sys->pp_dir_entries[i_item]->psz_path );
1298 attroff ( A_REVERSE );
1303 else if( ( p_sys->i_box_type == BOX_PLAYLIST ||
1304 p_sys->i_box_type == BOX_SEARCH ||
1305 p_sys->i_box_type == BOX_OPEN ) && p_sys->p_playlist )
1308 playlist_t *p_playlist = p_sys->p_playlist;
1309 int i_start, i_stop;
1311 DrawBox( p_sys->w, y++, 0, h, COLS, " Playlist " );
1313 if( p_sys->i_box_plidx >= p_playlist->i_size ) p_sys->i_box_plidx = p_playlist->i_size - 1;
1314 if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
1316 if( p_sys->i_box_plidx < (h - 2)/2 )
1321 else if( p_playlist->i_size - p_sys->i_box_plidx > (h - 2)/2 )
1323 i_start = p_sys->i_box_plidx - (h - 2)/2;
1324 i_stop = i_start + h - 2;
1328 i_stop = p_playlist->i_size;
1329 i_start = p_playlist->i_size - (h - 2);
1335 if( i_stop > p_playlist->i_size )
1337 i_stop = p_playlist->i_size;
1340 for( i_item = i_start; i_item < i_stop; i_item++ )
1342 vlc_bool_t b_selected = ( p_sys->i_box_plidx == i_item );
1343 int c = p_playlist->i_index == i_item ? '>' : ' ';
1345 if( y >= y_end ) break;
1348 attrset( A_REVERSE );
1350 if( !strcmp( p_playlist->pp_items[i_item]->input.psz_name,
1351 p_playlist->pp_items[i_item]->input.psz_uri ) )
1353 mvnprintw( y++, 1, COLS - 2, "%c %d - '%s'",
1356 p_playlist->pp_items[i_item]->input.psz_uri );
1360 mvnprintw( y++, 1, COLS - 2, "%c %d - '%s' (%s)",
1363 p_playlist->pp_items[i_item]->input.psz_uri,
1364 p_playlist->pp_items[i_item]->input.psz_name );
1368 attroff ( A_REVERSE );
1376 if( p_sys->i_box_type == BOX_SEARCH )
1378 DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
1379 if( p_sys->psz_search_chain )
1381 if( strlen( p_sys->psz_search_chain ) == 0 &&
1382 p_sys->psz_old_search != NULL )
1384 /* Searching next entry */
1385 mvnprintw( 7, 1, COLS-2, "Find: %s", p_sys->psz_old_search );
1389 mvnprintw( 7, 1, COLS-2, "Find: %s", p_sys->psz_search_chain );
1393 if( p_sys->i_box_type == BOX_OPEN )
1395 if( p_sys->psz_open_chain )
1397 DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
1398 mvnprintw( 7, 1, COLS-2, "Open: %s", p_sys->psz_open_chain );
1404 DrawEmptyLine( p_sys->w, y++, 1, COLS - 2 );
1409 *t_last_refresh = time( 0 );
1412 static void Eject ( intf_thread_t *p_intf )
1414 char *psz_device = NULL, *psz_parser, *psz_name;
1417 * Get the active input
1418 * Determine whether we can eject a media, ie it's a DVD, VCD or CD-DA
1419 * If it's neither of these, then return
1422 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1425 if( p_playlist == NULL )
1430 vlc_mutex_lock( &p_playlist->object_lock );
1432 if( p_playlist->i_index < 0 )
1434 vlc_mutex_unlock( &p_playlist->object_lock );
1435 vlc_object_release( p_playlist );
1439 psz_name = p_playlist->pp_items[ p_playlist->i_index ]->input.psz_name;
1443 if( !strncmp(psz_name, "dvd://", 4) )
1445 switch( psz_name[strlen("dvd://")] )
1449 psz_device = config_GetPsz( p_intf, "dvd" );
1452 /* Omit the first MRL-selector characters */
1453 psz_device = strdup( psz_name + strlen("dvd://" ) );
1457 else if( !strncmp(psz_name, VCD_MRL, strlen(VCD_MRL)) )
1459 switch( psz_name[strlen(VCD_MRL)] )
1463 psz_device = config_GetPsz( p_intf, VCD_MRL );
1466 /* Omit the beginning MRL-selector characters */
1467 psz_device = strdup( psz_name + strlen(VCD_MRL) );
1471 else if( !strncmp(psz_name, CDDA_MRL, strlen(CDDA_MRL) ) )
1473 switch( psz_name[strlen(CDDA_MRL)] )
1477 psz_device = config_GetPsz( p_intf, "cd-audio" );
1480 /* Omit the beginning MRL-selector characters */
1481 psz_device = strdup( psz_name + strlen(CDDA_MRL) );
1487 psz_device = strdup( psz_name );
1491 vlc_mutex_unlock( &p_playlist->object_lock );
1492 vlc_object_release( p_playlist );
1494 if( psz_device == NULL )
1499 /* Remove what we have after @ */
1500 psz_parser = psz_device;
1501 for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
1503 if( *psz_parser == '@' )
1510 /* If there's a stream playing, we aren't allowed to eject ! */
1511 if( p_intf->p_sys->p_input == NULL )
1513 msg_Dbg( p_intf, "ejecting %s", psz_device );
1515 intf_Eject( p_intf, psz_device );
1522 static void ReadDir( intf_thread_t *p_intf )
1524 intf_sys_t *p_sys = p_intf->p_sys;
1525 DIR * p_current_dir;
1526 struct dirent * p_dir_content;
1529 if( p_sys->psz_current_dir && *p_sys->psz_current_dir )
1532 p_current_dir = opendir( p_sys->psz_current_dir );
1534 if( p_current_dir == NULL )
1536 /* something went bad, get out of here ! */
1538 msg_Warn( p_intf, "cannot open directory `%s' (%s)",
1539 p_sys->psz_current_dir, strerror(errno));
1541 msg_Warn( p_intf, "cannot open directory `%s'", p_sys->psz_current_dir );
1546 /* Clean the old shit */
1547 for( i = 0; i < p_sys->i_dir_entries; i++ )
1549 struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
1550 free( p_dir_entry->psz_path );
1551 REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
1552 free( p_dir_entry );
1554 p_sys->pp_dir_entries = NULL;
1555 p_sys->i_dir_entries = 0;
1557 /* get the first directory entry */
1558 p_dir_content = readdir( p_current_dir );
1560 /* while we still have entries in the directory */
1561 while( p_dir_content != NULL )
1563 #if defined( S_ISDIR )
1564 struct stat stat_data;
1566 struct dir_entry_t *p_dir_entry;
1567 int i_size_entry = strlen( p_sys->psz_current_dir ) +
1568 strlen( p_dir_content->d_name ) + 2;
1569 char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
1571 sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir,
1572 p_dir_content->d_name );
1574 if( !( p_dir_entry = malloc( sizeof( struct dir_entry_t) ) ) )
1580 #if defined( S_ISDIR )
1581 stat( psz_uri, &stat_data );
1582 if( S_ISDIR(stat_data.st_mode) )
1583 #elif defined( DT_DIR )
1584 if( p_dir_content->d_type & DT_DIR )
1589 p_dir_entry->psz_path = strdup( p_dir_content->d_name );
1590 p_dir_entry->b_file = VLC_FALSE;
1591 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
1592 p_sys->i_dir_entries, p_dir_entry );
1596 p_dir_entry->psz_path = strdup( p_dir_content->d_name );
1597 p_dir_entry->b_file = VLC_TRUE;
1598 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
1599 p_sys->i_dir_entries, p_dir_entry );
1603 /* Read next entry */
1604 p_dir_content = readdir( p_current_dir );
1606 closedir( p_current_dir );
1611 msg_Dbg( p_intf, "no current dir set" );
1616 static void PlayPause( intf_thread_t *p_intf )
1618 input_thread_t *p_input = p_intf->p_sys->p_input;
1623 var_Get( p_input, "state", &val );
1624 if( val.i_int != PAUSE_S )
1626 val.i_int = PAUSE_S;
1630 val.i_int = PLAYING_S;
1632 var_Set( p_input, "state", val );
1634 else if( p_intf->p_sys->p_playlist )
1636 playlist_Play( p_intf->p_sys->p_playlist );
1640 /****************************************************************************
1642 ****************************************************************************/
1643 static void DrawBox( WINDOW *win, int y, int x, int h, int w, char *title )
1648 if( w > 3 && h > 2 )
1650 if( title == NULL ) title = "";
1651 i_len = strlen( title );
1653 if( i_len > w - 2 ) i_len = w - 2;
1655 mvwaddch( win, y, x, ACS_ULCORNER );
1656 mvwhline( win, y, x+1, ACS_HLINE, ( w-i_len-2)/2 );
1657 mvwprintw( win,y, x+1+(w-i_len-2)/2, "%s", title );
1658 mvwhline( win, y, x+(w-i_len)/2+i_len, ACS_HLINE, w - 1 - ((w-i_len)/2+i_len) );
1659 mvwaddch( win, y, x+w-1,ACS_URCORNER );
1661 for( i = 0; i < h-2; i++ )
1663 mvwaddch( win, y+i+1, x, ACS_VLINE );
1664 mvwaddch( win, y+i+1, x+w-1, ACS_VLINE );
1667 mvwaddch( win, y+h-1, x, ACS_LLCORNER );
1668 mvwhline( win, y+h-1, x+1, ACS_HLINE, w - 2 );
1669 mvwaddch( win, y+h-1, x+w-1, ACS_LRCORNER );
1673 static void DrawEmptyLine( WINDOW *win, int y, int x, int w )
1677 mvhline( y, x, ' ', w );
1681 static void DrawLine( WINDOW *win, int y, int x, int w )
1685 attrset( A_REVERSE );
1686 mvhline( y, x, ' ', w );
1687 attroff ( A_REVERSE );