]> git.sesse.net Git - vlc/blob - modules/gui/ncurses.c
* modules/gui/wxwindows/interface.cpp: fixed hotkeys with modifiers on win32.
[vlc] / modules / gui / ncurses.c
1 /*****************************************************************************
2  * ncurses.c : NCurses plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN
5  * $Id$
6  *
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>
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 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <stdlib.h>                                      /* malloc(), free() */
31 #include <string.h>
32 #include <errno.h>                                                 /* ENOMEM */
33 #include <stdio.h>
34 #include <time.h>
35
36 #include <curses.h>
37
38 #include <vlc/vlc.h>
39 #include <vlc/intf.h>
40 #include <vlc/vout.h>
41 #include <vlc/aout.h>
42
43 #ifdef HAVE_SYS_STAT_H
44 #   include <sys/stat.h>
45 #endif
46 #if (!defined( WIN32 ) || defined(__MINGW32__))
47 /* Mingw has its own version of dirent */
48 #   include <dirent.h>
49 #endif
50
51 #ifdef HAVE_CDDAX
52 #define CDDA_MRL "cddax://"
53 #else
54 #define CDDA_MRL "cdda://"
55 #endif
56
57 #ifdef HAVE_VCDX
58 #define VCD_MRL "vcdx://"
59 #else
60 #define VCD_MRL "vcdx://"
61 #endif
62
63 #define SEARCH_CHAIN_SIZE 20
64 #define OPEN_CHAIN_SIZE 50
65
66 /*****************************************************************************
67  * Local prototypes.
68  *****************************************************************************/
69 static int  Open           ( vlc_object_t * );
70 static void Close          ( vlc_object_t * );
71
72 static void Run            ( intf_thread_t * );
73 static void PlayPause      ( intf_thread_t * );
74 static void Eject          ( intf_thread_t * );
75
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 * );
81
82 /*****************************************************************************
83  * Module descriptor
84  *****************************************************************************/
85
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.")
90
91 vlc_module_begin();
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 );
99 vlc_module_end();
100
101 /*****************************************************************************
102  * intf_sys_t: description and status of ncurses interface
103  *****************************************************************************/
104 enum
105 {
106     BOX_NONE,
107     BOX_HELP,
108     BOX_INFO,
109     BOX_LOG,
110     BOX_PLAYLIST,
111     BOX_SEARCH,
112     BOX_OPEN,
113     BOX_BROWSE
114 };
115 struct dir_entry_t
116 {
117     vlc_bool_t  b_file;
118     char        *psz_path;
119 };
120 struct intf_sys_t
121 {
122     playlist_t     *p_playlist;
123     input_thread_t *p_input;
124
125     float           f_slider;
126     float           f_slider_old;
127
128     WINDOW          *w;
129
130     int             i_box_type;
131     int             i_box_y;
132     int             i_box_lines;
133     int             i_box_lines_total;
134     int             i_box_start;
135
136     int             i_box_plidx;    /* Playlist index */
137     int             b_box_plidx_follow;
138     int             i_box_bidx;    /* browser index */
139
140     int             b_box_cleared;
141
142     msg_subscription_t* p_sub;                  /* message bank subscription */
143
144     char            *psz_search_chain;          /* for playlist searching    */
145     char            *psz_old_search;            /* for searching next        */
146     int             i_before_search;
147
148     char            *psz_open_chain;
149     
150     char            *psz_current_dir;
151     int             i_dir_entries;
152     struct dir_entry_t  **pp_dir_entries;
153 };
154
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 );
158
159 /*****************************************************************************
160  * Open: initialize and create window
161  *****************************************************************************/
162 static int Open( vlc_object_t *p_this )
163 {
164     intf_thread_t *p_intf = (intf_thread_t *)p_this;
165     intf_sys_t    *p_sys;
166     vlc_value_t    val;
167
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 );
183
184     /* Initialize the curses library */
185     p_sys->w = initscr();
186     keypad( p_sys->w, TRUE );
187     /* Don't do NL -> CR/NL */
188     nonl();
189     /* Take input chars one at a time */
190     cbreak();
191     /* Don't echo */
192     noecho();
193
194     curs_set(0);
195     timeout(0);
196
197     clear();
198
199     /* exported function */
200     p_intf->pf_run = Run;
201
202     /* Set quiet mode */
203     val.i_int = -1;
204     var_Set( p_intf->p_vlc, "verbose", val );
205
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;
210
211     /* Initialize open chain */
212     p_sys->psz_open_chain = (char *)malloc( OPEN_CHAIN_SIZE + 1 );
213     
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);
217     
218     if( val.psz_string && *val.psz_string )
219     {
220         p_sys->psz_current_dir = strdup( val.psz_string);
221         free( val.psz_string );
222     }
223     else
224     {
225         p_sys->psz_current_dir = strdup( p_intf->p_vlc->psz_homedir );
226     }
227     
228     p_sys->i_dir_entries = 0;
229     p_sys->pp_dir_entries  = NULL;
230     ReadDir( p_intf );
231
232     return VLC_SUCCESS;
233 }
234
235 /*****************************************************************************
236  * Close: destroy interface window
237  *****************************************************************************/
238 static void Close( vlc_object_t *p_this )
239 {
240     intf_thread_t *p_intf = (intf_thread_t *)p_this;
241     intf_sys_t    *p_sys = p_intf->p_sys;
242     int i;
243
244     for( i = 0; i < p_sys->i_dir_entries; i++ )
245     {
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 );
250     }
251     p_sys->pp_dir_entries = NULL;
252     
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 );
257
258     if( p_sys->p_input )
259     {
260         vlc_object_release( p_sys->p_input );
261     }
262     if( p_sys->p_playlist )
263     {
264         vlc_object_release( p_sys->p_playlist );
265     }
266
267     /* Close the ncurses interface */
268     endwin();
269
270     msg_Unsubscribe( p_intf, p_sys->p_sub );
271
272     /* Destroy structure */
273     free( p_sys );
274 }
275
276 /*****************************************************************************
277  * Run: ncurses thread
278  *****************************************************************************/
279 static void Run( intf_thread_t *p_intf )
280 {
281     intf_sys_t    *p_sys = p_intf->p_sys;
282
283     int i_key;
284     time_t t_last_refresh;
285
286     /*
287      * force drawing the interface for the first time
288      */
289     t_last_refresh = ( time( 0 ) - 1);
290
291     while( !p_intf->b_die )
292     {
293         msleep( INTF_IDLE_SLEEP );
294
295         /* Update the input */
296         if( p_sys->p_playlist == NULL )
297         {
298             p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
299         }
300         if( p_sys->p_playlist )
301         {
302             vlc_mutex_lock( &p_sys->p_playlist->object_lock );
303             if( p_sys->p_input == NULL )
304             {
305                 p_sys->p_input = p_sys->p_playlist->p_input;
306                 if( p_sys->p_input )
307                 {
308                     if( !p_sys->p_input->b_dead )
309                     {
310                         vlc_object_yield( p_sys->p_input );
311                     }
312                 }
313             }
314             else if( p_sys->p_input->b_dead )
315             {
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;
320             }
321             vlc_mutex_unlock( &p_sys->p_playlist->object_lock );
322         }
323
324         if( p_sys->b_box_plidx_follow && p_sys->p_playlist->i_index >= 0 )
325         {
326             p_sys->i_box_plidx = p_sys->p_playlist->i_index;
327         }
328
329         while( ( i_key = getch()) != -1 )
330         {
331             /*
332              * HandleKey returns 1 if the screen needs to be redrawn
333              */
334             if ( HandleKey( p_intf, i_key ) )
335             {
336                 Redraw( p_intf, &t_last_refresh );
337             }
338         }
339         /* Hack */
340         if( p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared )
341         {
342             clear();
343             Redraw( p_intf, &t_last_refresh );
344             p_sys->b_box_cleared = VLC_TRUE;
345         }
346
347         /*
348          * redraw the screen every second
349          */
350         if ( (time(0) - t_last_refresh) >= 1 )
351         {
352             ManageSlider ( p_intf );
353             Redraw( p_intf, &t_last_refresh );
354         }
355     }
356 }
357
358 /* following functions are local */
359
360 static int HandleKey( intf_thread_t *p_intf, int i_key )
361 {
362     intf_sys_t *p_sys = p_intf->p_sys;
363     vlc_value_t val;
364
365     if( p_sys->i_box_type == BOX_PLAYLIST && p_sys->p_playlist )
366     {
367         int b_ret = VLC_TRUE;
368
369         switch( i_key )
370         {
371             vlc_value_t val;
372             /* Playlist Settings */
373             case 'r':
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 );
377                 return 1;
378             case 'l':
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 );
382                 return 1;
383             case 'R':
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 );
387                 return 1;
388
389             /* Playlist sort */
390             case 'o':
391                 playlist_Sort( p_sys->p_playlist, SORT_TITLE, ORDER_NORMAL );
392                 return 1;
393             case 'O':
394                 playlist_Sort( p_sys->p_playlist, SORT_TITLE, ORDER_REVERSE );
395                 return 1;
396
397             /* Playlist navigation */
398             case KEY_HOME:
399                 p_sys->i_box_plidx = 0;
400                 break;
401             case KEY_END:
402                 p_sys->i_box_plidx = p_sys->p_playlist->i_size - 1;
403                 break;
404             case KEY_UP:
405                 p_sys->i_box_plidx--;
406                 break;
407             case KEY_DOWN:
408                 p_sys->i_box_plidx++;
409                 break;
410             case KEY_PPAGE:
411                 p_sys->i_box_plidx -= p_sys->i_box_lines;
412                 break;
413             case KEY_NPAGE:
414                 p_sys->i_box_plidx += p_sys->i_box_lines;
415                 break;
416             case 'D':
417             case KEY_BACKSPACE:
418             case KEY_DC:
419             {
420                 int i_item = p_sys->p_playlist->i_index;
421
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 )
425                 {
426                     playlist_Goto( p_sys->p_playlist, i_item );
427                 }
428                 break;
429             }
430
431             case KEY_ENTER:
432             case 0x0d:
433                 playlist_Goto( p_sys->p_playlist, p_sys->i_box_plidx );
434                 break;
435             default:
436                 b_ret = VLC_FALSE;
437                 break;
438         }
439
440         if( b_ret )
441         {
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;
446             else
447                 p_sys->b_box_plidx_follow = VLC_FALSE;
448             return 1;
449         }
450     }
451     if( p_sys->i_box_type == BOX_BROWSE )
452     {
453         vlc_bool_t b_ret = VLC_TRUE;
454         /* Browser navigation */
455         switch( i_key )
456         {
457             case KEY_HOME:
458                 p_sys->i_box_bidx = 0;
459                 break;
460             case KEY_END:
461                 p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
462                 break;
463             case KEY_UP:
464                 p_sys->i_box_bidx--;
465                 break;
466             case KEY_DOWN:
467                 p_sys->i_box_bidx++;
468                 break;
469             case KEY_PPAGE:
470                 p_sys->i_box_bidx -= p_sys->i_box_lines;
471                 break;
472             case KEY_NPAGE:
473                 p_sys->i_box_bidx += p_sys->i_box_lines;
474                 break;
475
476             case KEY_ENTER:
477             case 0x0d:
478                 if( p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file )
479                 {
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);
483
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,
486                                   psz_uri,
487                                   PLAYLIST_APPEND, PLAYLIST_END );
488                     p_sys->i_box_type = BOX_PLAYLIST;
489                     free( psz_uri );
490                 }
491                 else
492                 {
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);
496
497                     sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
498                     
499                     p_sys->psz_current_dir = strdup( psz_uri );
500                     ReadDir( p_intf );
501                     free( psz_uri );
502                 }
503                 break;
504             default:
505                 b_ret = VLC_FALSE;
506                 break;
507         }
508         if( b_ret )
509         {
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;
512             return 1;
513         }
514     }
515     else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO )
516     {
517         switch( i_key )
518         {
519             case KEY_HOME:
520                 p_sys->i_box_start = 0;
521                 return 1;
522             case KEY_END:
523                 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
524                 return 1;
525             case KEY_UP:
526                 if( p_sys->i_box_start > 0 ) p_sys->i_box_start--;
527                 return 1;
528             case KEY_DOWN:
529                 if( p_sys->i_box_start < p_sys->i_box_lines_total - 1 )
530                 {
531                     p_sys->i_box_start++;
532                 }
533                 return 1;
534             case KEY_PPAGE:
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;
537                 return 1;
538             case KEY_NPAGE:
539                 p_sys->i_box_start += p_sys->i_box_lines;
540                 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
541                 {
542                     p_sys->i_box_start = p_sys->i_box_lines_total - 1;
543                 }
544                 return 1;
545             default:
546                 break;
547         }
548     }
549     else if( p_sys->i_box_type == BOX_NONE )
550     {
551         switch( i_key )
552         {
553             case KEY_HOME:
554                 p_sys->f_slider = 0;
555                 ManageSlider ( p_intf );
556                 return 1;
557             case KEY_END:
558                 p_sys->f_slider = 99.9;
559                 ManageSlider ( p_intf );
560                 return 1;
561             case KEY_UP:
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 );
565                 return 1;
566             case KEY_DOWN:
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 );
570                 return 1;
571
572             default:
573                 break;
574         }
575     }
576     else if( p_sys->i_box_type == BOX_SEARCH && p_sys->psz_search_chain )
577     {
578         int i_chain_len;
579         i_chain_len = strlen( p_sys->psz_search_chain );
580         switch( i_key )
581         {
582             case 0x0c:      /* ^l */
583                 clear();
584                 return 1;
585             case KEY_ENTER:
586             case 0x0d:
587                 if( i_chain_len > 0 )
588                 {
589                     p_sys->psz_old_search = strdup( p_sys->psz_search_chain );
590                 }
591                 else if( p_sys->psz_old_search )
592                 {
593                     SearchPlaylist( p_intf, p_sys->psz_old_search );
594                 }
595                 p_sys->i_box_type = BOX_PLAYLIST;
596                 return 1;
597             case 0x1b:      /* Esc. */
598                 p_sys->i_box_plidx = p_sys->i_before_search;
599                 p_sys->i_box_type = BOX_PLAYLIST;
600                 return 1;
601             case KEY_BACKSPACE:
602                 if( i_chain_len > 0 )
603                 {
604                     p_sys->psz_search_chain[ i_chain_len - 1 ] = '\0';
605                 }
606                 break;
607             default:
608                 if( i_chain_len < SEARCH_CHAIN_SIZE )
609                 {
610                     p_sys->psz_search_chain[ i_chain_len++ ] = i_key;
611                     p_sys->psz_search_chain[ i_chain_len ] = 0;
612                 }
613                 break;
614         }
615         if( p_sys->psz_old_search )
616         {
617             free( p_sys->psz_old_search );
618             p_sys->psz_old_search = NULL;
619         }
620         SearchPlaylist( p_intf, p_sys->psz_search_chain );
621         return 1;
622     }
623     else if( p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain )
624     {
625         int i_chain_len = strlen( p_sys->psz_open_chain );
626         playlist_t *p_playlist = p_sys->p_playlist;
627
628         switch( i_key )
629         {
630             case 0x0c:      /* ^l */
631                 clear();
632                 return 1;
633             case KEY_ENTER:
634             case 0x0d:
635                 if( p_playlist && i_chain_len > 0 )
636                 {
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;
641                 }
642                 p_sys->i_box_type = BOX_PLAYLIST;
643                 return 1;
644             case 0x1b:      /* Esc. */
645                 p_sys->i_box_type = BOX_PLAYLIST;
646                 return 1;
647             case KEY_BACKSPACE:
648                 if( i_chain_len > 0 )
649                 {
650                     p_sys->psz_open_chain[ i_chain_len - 1 ] = '\0';
651                 }
652                 break;
653             default:
654                 if( i_chain_len < OPEN_CHAIN_SIZE )
655                 {
656                     p_sys->psz_open_chain[ i_chain_len++ ] = i_key;
657                     p_sys->psz_open_chain[ i_chain_len ] = 0;
658                 }
659                 break;
660         }
661         return 1;
662     }
663
664
665     /* Common keys */
666     switch( i_key )
667     {
668         case 'q':
669         case 'Q':
670         case 0x1b:  /* Esc */
671             p_intf->p_vlc->b_die = VLC_TRUE;
672             return 0;
673
674         /* Box switching */
675         case 'i':
676             if( p_sys->i_box_type == BOX_INFO )
677                 p_sys->i_box_type = BOX_NONE;
678             else
679                 p_sys->i_box_type = BOX_INFO;
680             p_sys->i_box_lines_total = 0;
681             return 1;
682         case 'L':
683             if( p_sys->i_box_type == BOX_LOG )
684                 p_sys->i_box_type = BOX_NONE;
685             else
686                 p_sys->i_box_type = BOX_LOG;
687             return 1;
688         case 'P':
689             if( p_sys->i_box_type == BOX_PLAYLIST )
690                 p_sys->i_box_type = BOX_NONE;
691             else
692                 p_sys->i_box_type = BOX_PLAYLIST;
693             return 1;
694         case 'B':
695             if( p_sys->i_box_type == BOX_BROWSE )
696                 p_sys->i_box_type = BOX_NONE;
697             else
698                 p_sys->i_box_type = BOX_BROWSE;
699             return 1;
700         case 'h':
701         case 'H':
702             if( p_sys->i_box_type == BOX_HELP )
703                 p_sys->i_box_type = BOX_NONE;
704             else
705                 p_sys->i_box_type = BOX_HELP;
706             p_sys->i_box_lines_total = 0;
707             return 1;
708         case '/':
709             if( p_sys->i_box_type != BOX_SEARCH )
710             {
711                 if( p_sys->psz_search_chain == NULL )
712                 {
713                     return 1;
714                 }
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;
719             }
720             return 1;
721         case 'A': /* Open */
722             if( p_sys->i_box_type != BOX_OPEN )
723             {
724                 if( p_sys->psz_open_chain == NULL )
725                 {
726                     return 1;
727                 }
728                 p_sys->psz_open_chain[0] = '\0';
729                 p_sys->i_box_type = BOX_OPEN;
730             }
731             return 1;
732
733         /* Navigation */
734         case KEY_RIGHT:
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 );
738             return 1;
739
740         case KEY_LEFT:
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 );
744             return 1;
745
746         /* Common control */
747         case 'f':
748         {
749             vout_thread_t *p_vout;
750             if( p_intf->p_sys->p_input )
751             {
752                 p_vout = vlc_object_find( p_intf->p_sys->p_input,
753                                           VLC_OBJECT_VOUT, FIND_CHILD );
754                 if( p_vout )
755                 {
756                     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
757                     vlc_object_release( p_vout );
758                 }
759             }
760             return 0;
761         }
762
763         case ' ':
764             PlayPause( p_intf );
765             return 1;
766
767         case 's':
768             if( p_intf->p_sys->p_playlist )
769             {
770                 playlist_Stop( p_intf->p_sys->p_playlist );
771             }
772             return 1;
773
774         case 'e':
775             Eject( p_intf );
776             return 1;
777
778         case '[':
779             if( p_sys->p_input )
780             {
781                 val.b_bool = VLC_TRUE;
782                 var_Set( p_sys->p_input, "prev-title", val );
783             }
784             return 1;
785
786         case ']':
787             if( p_sys->p_input )
788             {
789                 val.b_bool = VLC_TRUE;
790                 var_Set( p_sys->p_input, "next-title", val );
791             }
792             return 1;
793
794         case '<':
795             if( p_sys->p_input )
796             {
797                 val.b_bool = VLC_TRUE;
798                 var_Set( p_sys->p_input, "prev-chapter", val );
799             }
800             return 1;
801
802         case '>':
803             if( p_sys->p_input )
804             {
805                 val.b_bool = VLC_TRUE;
806                 var_Set( p_sys->p_input, "next-chapter", val );
807             }
808             return 1;
809
810         case 'p':
811             if( p_intf->p_sys->p_playlist )
812             {
813                 playlist_Prev( p_intf->p_sys->p_playlist );
814             }
815             clear();
816             return 1;
817
818         case 'n':
819             if( p_intf->p_sys->p_playlist )
820             {
821                 playlist_Next( p_intf->p_sys->p_playlist );
822             }
823             clear();
824             return 1;
825
826         case 'a':
827             aout_VolumeUp( p_intf, 1, NULL );
828             clear();
829             return 1;
830
831         case 'z':
832             aout_VolumeDown( p_intf, 1, NULL );
833             clear();
834             return 1;
835
836         /*
837          * ^l should clear and redraw the screen
838          */
839         case 0x0c:
840             clear();
841             return 1;
842
843         default:
844             return 0;
845     }
846 }
847
848 static void ManageSlider ( intf_thread_t *p_intf )
849 {
850     intf_sys_t     *p_sys = p_intf->p_sys;
851     input_thread_t *p_input = p_sys->p_input;
852     vlc_value_t     val;
853
854     if( p_input == NULL )
855     {
856         return;
857     }
858     var_Get( p_input, "state", &val );
859     if( val.i_int != PLAYING_S )
860     {
861         return;
862     }
863
864     var_Get( p_input, "position", &val );
865     if( p_sys->f_slider == p_sys->f_slider_old )
866     {
867         p_sys->f_slider =
868         p_sys->f_slider_old = 100 * val.f_float;
869     }
870     else
871     {
872         p_sys->f_slider_old = p_sys->f_slider;
873
874         val.f_float = p_sys->f_slider / 100.0;
875         var_Set( p_input, "position", val );
876     }
877 }
878
879 static void SearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring )
880 {
881     bool b_ok = false;
882     int i_current;
883     int i_first = 0 ;
884     int i_item = -1;
885     intf_sys_t *p_sys = p_intf->p_sys;
886     playlist_t *p_playlist = p_sys->p_playlist;
887
888     if( p_sys->i_before_search >= 0 )
889         i_first = p_sys->i_before_search;
890
891     if( ( ! psz_searchstring ) ||  strlen( psz_searchstring ) <= 0 )
892     {
893         p_sys->i_box_plidx = p_sys->i_before_search;
894         return;
895     }
896
897     for( i_current = i_first + 1; i_current < p_playlist->i_size;
898          i_current++ )
899     {
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 )
904         {
905             i_item = i_current;
906             b_ok = true;
907             break;
908         }
909     }
910     if( !b_ok )
911     {
912         for( i_current = 0; i_current < i_first; i_current++ )
913         {
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 )
918             {
919                 i_item = i_current;
920                 b_ok = true;
921                 break;
922             }
923         }
924     }
925
926     if( i_item < 0 || i_item >= p_playlist->i_size ) return;
927
928     p_sys->i_box_plidx = i_item;
929 }
930
931
932 static void mvnprintw( int y, int x, int w, const char *p_fmt, ... )
933 {
934     va_list  vl_args;
935     char    *p_buf = NULL;
936     int      i_len;
937
938     va_start ( vl_args, p_fmt );
939     vasprintf ( &p_buf, p_fmt, vl_args );
940     va_end ( vl_args );
941
942     if ( p_buf == NULL )
943     {
944         return;
945     }
946     if(  w > 0 )
947     {
948         if( ( i_len = strlen( p_buf ) ) > w )
949         {
950             int i_cut = i_len - w;
951             int x1 = i_len/2 - i_cut/2;
952             int x2 = x1 + i_cut;
953
954             if( i_len > x2 )
955             {
956                 memmove( &p_buf[x1], &p_buf[x2], i_len - x2 );
957             }
958             p_buf[w] = '\0';
959             if( w > 7 )
960             {
961                 p_buf[w/2-1] = '.';
962                 p_buf[w/2  ] = '.';
963                 p_buf[w/2+1] = '.';
964             }
965             mvprintw( y, x, "%s", p_buf );
966         }
967         else
968         {
969             mvprintw( y, x, "%s", p_buf );
970             mvhline( y, x + i_len, ' ', w - i_len );
971         }
972     }
973 }
974 static void MainBoxWrite( intf_thread_t *p_intf, int l, int x, const char *p_fmt, ... )
975 {
976     intf_sys_t     *p_sys = p_intf->p_sys;
977
978     va_list  vl_args;
979     char    *p_buf = NULL;
980
981     if( l < p_sys->i_box_start || l - p_sys->i_box_start >= p_sys->i_box_lines )
982     {
983         return;
984     }
985
986     va_start ( vl_args, p_fmt );
987     vasprintf ( &p_buf, p_fmt, vl_args );
988     va_end ( vl_args );
989
990     if( p_buf == NULL )
991     {
992         return;
993     }
994
995     mvnprintw( p_sys->i_box_y + l - p_sys->i_box_start, x, COLS - x - 1, "%s", p_buf );
996 }
997
998 static void Redraw ( intf_thread_t *p_intf, time_t *t_last_refresh )
999 {
1000     intf_sys_t     *p_sys = p_intf->p_sys;
1001     input_thread_t *p_input = p_sys->p_input;
1002     int y = 0;
1003     int h;
1004     int y_end;
1005
1006     //clear();
1007
1008     /* Title */
1009     attrset ( A_REVERSE );
1010     mvnprintw( y, 0, COLS, "VLC media player" " (ncurses interface) [ h for help ]" );
1011     attroff ( A_REVERSE );
1012     y += 2;
1013
1014     /* Infos */
1015     if( p_input && !p_input->b_dead )
1016     {
1017         char buf1[MSTRTIME_MAX_SIZE];
1018         char buf2[MSTRTIME_MAX_SIZE];
1019         vlc_value_t val;
1020         vlc_value_t val_list;
1021
1022         /* Source */
1023         mvnprintw( y++, 0, COLS, " Source   : %s",
1024                    p_input->input.p_item->psz_uri );
1025
1026         /* State */
1027         var_Get( p_input, "state", &val );
1028         if( val.i_int == PLAYING_S )
1029         {
1030             mvnprintw( y++, 0, COLS, " State    : Playing" );
1031         }
1032         else if( val.i_int == PAUSE_S )
1033         {
1034             mvnprintw( y++, 0, COLS, " State    : Paused" );
1035         }
1036         else
1037         {
1038             y++;
1039         }
1040         if( val.i_int != INIT_S && val.i_int != END_S )
1041         {
1042             audio_volume_t i_volume;
1043
1044             /* Position */
1045             var_Get( p_input, "time", &val );
1046             msecstotimestr( buf1, val.i_time / 1000 );
1047
1048             var_Get( p_input, "length", &val );
1049             msecstotimestr( buf2, val.i_time / 1000 );
1050
1051             mvnprintw( y++, 0, COLS, " Position : %s/%s (%.2f%%)", buf1, buf2, p_sys->f_slider );
1052
1053             /* Volume */
1054             aout_VolumeGet( p_intf, &i_volume );
1055             mvnprintw( y++, 0, COLS, " Volume   : %i%%", i_volume*200/AOUT_VOLUME_MAX );
1056
1057             /* Title */
1058             if( !var_Get( p_input, "title", &val ) )
1059             {
1060                 var_Change( p_input, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
1061                 if( val_list.p_list->i_count > 0 )
1062                 {
1063                     mvnprintw( y++, 0, COLS, " Title    : %d/%d", val.i_int, val_list.p_list->i_count );
1064                 }
1065                 var_Change( p_input, "title", VLC_VAR_FREELIST, &val_list, NULL );
1066             }
1067
1068             /* Chapter */
1069             if( !var_Get( p_input, "chapter", &val ) )
1070             {
1071                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
1072                 if( val_list.p_list->i_count > 0 )
1073                 {
1074                     mvnprintw( y++, 0, COLS, " Chapter  : %d/%d", val.i_int, val_list.p_list->i_count );
1075                 }
1076                 var_Change( p_input, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
1077             }
1078         }
1079         else
1080         {
1081             y++;
1082         }
1083     }
1084     else
1085     {
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 );
1090     }
1091
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)) );
1095     y += 3;
1096
1097     p_sys->i_box_y = y + 1;
1098     p_sys->i_box_lines = LINES - y - 2;
1099
1100     h = LINES - y;
1101     y_end = y + h - 1;
1102
1103     if( p_sys->i_box_type == BOX_HELP )
1104     {
1105         /* Help box */
1106         int l = 0;
1107         DrawBox( p_sys->w, y++, 0, h, COLS, " Help " );
1108
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, "" );
1116
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, "" );
1130
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, "" );
1142
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, "" );
1147
1148         MainBoxWrite( p_intf, l++, 1, "[Player]" );
1149         MainBoxWrite( p_intf, l++, 1, "     <up>,<down>     Seek +/-5%%" );
1150         MainBoxWrite( p_intf, l++, 1, "" );
1151
1152         MainBoxWrite( p_intf, l++, 1, "[Miscellaneous]" );
1153         MainBoxWrite( p_intf, l++, 1, "     Ctrl-l          Refresh the screen" );
1154
1155         p_sys->i_box_lines_total = l;
1156         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1157         {
1158             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1159         }
1160
1161         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1162         {
1163             y += l - p_sys->i_box_start;
1164         }
1165         else
1166         {
1167             y += p_sys->i_box_lines;
1168         }
1169     }
1170     else if( p_sys->i_box_type == BOX_INFO )
1171     {
1172         /* Info box */
1173         int l = 0;
1174         DrawBox( p_sys->w, y++, 0, h, COLS, " Information " );
1175
1176         if( p_input )
1177         {
1178             int i,j;
1179             vlc_mutex_lock( &p_input->input.p_item->lock );
1180             for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
1181             {
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++ )
1186                 {
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 );
1190                 }
1191             }
1192             vlc_mutex_unlock( &p_input->input.p_item->lock );
1193         }
1194         else
1195         {
1196             MainBoxWrite( p_intf, l++, 1, "No item currently playing" );
1197         }
1198         p_sys->i_box_lines_total = l;
1199         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1200         {
1201             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1202         }
1203
1204         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1205         {
1206             y += l - p_sys->i_box_start;
1207         }
1208         else
1209         {
1210             y += p_sys->i_box_lines;
1211         }
1212     }
1213     else if( p_sys->i_box_type == BOX_LOG )
1214     {
1215         int i_line = 0;
1216         int i_stop;
1217         int i_start;
1218
1219         DrawBox( p_sys->w, y++, 0, h, COLS, " Logs " );
1220
1221         i_start = p_intf->p_sys->p_sub->i_start;
1222
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 );
1226
1227         for( ;; )
1228         {
1229             static const char *ppsz_type[4] = { "", "error", "warning", "debug" };
1230             if( i_line >= h - 2 )
1231             {
1232                 break;
1233             }
1234             i_stop--;
1235             i_line++;
1236             if( i_stop < 0 ) i_stop += VLC_MSG_QSIZE;
1237             if( i_stop == i_start )
1238             {
1239                 break;
1240             }
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 );
1244         }
1245
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 );
1249         y = y_end;
1250     }
1251     else if( p_sys->i_box_type == BOX_BROWSE )
1252     {
1253         /* Playlist box */
1254         int        i_start, i_stop;
1255         int        i_item;
1256         DrawBox( p_sys->w, y++, 0, h, COLS, " Browse " );
1257
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;
1260
1261         if( p_sys->i_box_bidx < (h - 2)/2 )
1262         {
1263             i_start = 0;
1264             i_stop = h - 2;
1265         }
1266         else if( p_sys->i_dir_entries - p_sys->i_box_bidx > (h - 2)/2 )
1267         {
1268             i_start = p_sys->i_box_bidx - (h - 2)/2;
1269             i_stop = i_start + h - 2;
1270         }
1271         else
1272         {
1273             i_stop = p_sys->i_dir_entries;
1274             i_start = p_sys->i_dir_entries - (h - 2);
1275         }
1276         if( i_start < 0 )
1277         {
1278             i_start = 0;
1279         }
1280         if( i_stop > p_sys->i_dir_entries )
1281         {
1282             i_stop = p_sys->i_dir_entries;
1283         }
1284
1285         for( i_item = i_start; i_item < i_stop; i_item++ )
1286         {
1287             vlc_bool_t b_selected = ( p_sys->i_box_bidx == i_item );
1288
1289             if( y >= y_end ) break;
1290             if( b_selected )
1291             {
1292                 attrset( A_REVERSE );
1293             }
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 );
1296             if( b_selected )
1297             {
1298                 attroff ( A_REVERSE );
1299             }
1300         }
1301
1302     }
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 )
1306     {
1307         /* Playlist box */
1308         playlist_t *p_playlist = p_sys->p_playlist;
1309         int        i_start, i_stop;
1310         int        i_item;
1311         DrawBox( p_sys->w, y++, 0, h, COLS, " Playlist " );
1312
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;
1315
1316         if( p_sys->i_box_plidx < (h - 2)/2 )
1317         {
1318             i_start = 0;
1319             i_stop = h - 2;
1320         }
1321         else if( p_playlist->i_size - p_sys->i_box_plidx > (h - 2)/2 )
1322         {
1323             i_start = p_sys->i_box_plidx - (h - 2)/2;
1324             i_stop = i_start + h - 2;
1325         }
1326         else
1327         {
1328             i_stop = p_playlist->i_size;
1329             i_start = p_playlist->i_size - (h - 2);
1330         }
1331         if( i_start < 0 )
1332         {
1333             i_start = 0;
1334         }
1335         if( i_stop > p_playlist->i_size )
1336         {
1337             i_stop = p_playlist->i_size;
1338         }
1339
1340         for( i_item = i_start; i_item < i_stop; i_item++ )
1341         {
1342             vlc_bool_t b_selected = ( p_sys->i_box_plidx == i_item );
1343             int c = p_playlist->i_index == i_item ? '>' : ' ';
1344
1345             if( y >= y_end ) break;
1346             if( b_selected )
1347             {
1348                 attrset( A_REVERSE );
1349             }
1350             if( !strcmp( p_playlist->pp_items[i_item]->input.psz_name,
1351                          p_playlist->pp_items[i_item]->input.psz_uri ) )
1352             {
1353                 mvnprintw( y++, 1, COLS - 2, "%c %d - '%s'",
1354                            c,
1355                            i_item,
1356                            p_playlist->pp_items[i_item]->input.psz_uri );
1357             }
1358             else
1359             {
1360                 mvnprintw( y++, 1, COLS - 2, "%c %d - '%s' (%s)",
1361                           c,
1362                           i_item,
1363                           p_playlist->pp_items[i_item]->input.psz_uri,
1364                           p_playlist->pp_items[i_item]->input.psz_name );
1365             }
1366             if( b_selected )
1367             {
1368                 attroff ( A_REVERSE );
1369             }
1370         }
1371     }
1372     else
1373     {
1374         y++;
1375     }
1376     if( p_sys->i_box_type == BOX_SEARCH )
1377     {
1378         DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
1379         if( p_sys->psz_search_chain )
1380         {
1381             if( strlen( p_sys->psz_search_chain ) == 0 &&
1382                 p_sys->psz_old_search != NULL )
1383             {
1384                 /* Searching next entry */
1385                 mvnprintw( 7, 1, COLS-2, "Find: %s", p_sys->psz_old_search );
1386             }
1387             else
1388             {
1389                 mvnprintw( 7, 1, COLS-2, "Find: %s", p_sys->psz_search_chain );
1390             }
1391         }
1392     }
1393     if( p_sys->i_box_type == BOX_OPEN )
1394     {
1395         if( p_sys->psz_open_chain )
1396         {
1397             DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
1398             mvnprintw( 7, 1, COLS-2, "Open: %s", p_sys->psz_open_chain );
1399         }
1400     }
1401
1402     while( y < y_end )
1403     {
1404         DrawEmptyLine( p_sys->w, y++, 1, COLS - 2 );
1405     }
1406
1407     refresh();
1408
1409     *t_last_refresh = time( 0 );
1410 }
1411
1412 static void Eject ( intf_thread_t *p_intf )
1413 {
1414     char *psz_device = NULL, *psz_parser, *psz_name;
1415
1416     /*
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
1420      */
1421
1422     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1423                                                        FIND_ANYWHERE );
1424
1425     if( p_playlist == NULL )
1426     {
1427         return;
1428     }
1429
1430     vlc_mutex_lock( &p_playlist->object_lock );
1431
1432     if( p_playlist->i_index < 0 )
1433     {
1434         vlc_mutex_unlock( &p_playlist->object_lock );
1435         vlc_object_release( p_playlist );
1436         return;
1437     }
1438
1439     psz_name = p_playlist->pp_items[ p_playlist->i_index ]->input.psz_name;
1440
1441     if( psz_name )
1442     {
1443         if( !strncmp(psz_name, "dvd://", 4) )
1444         {
1445             switch( psz_name[strlen("dvd://")] )
1446             {
1447             case '\0':
1448             case '@':
1449                 psz_device = config_GetPsz( p_intf, "dvd" );
1450                 break;
1451             default:
1452                 /* Omit the first MRL-selector characters */
1453                 psz_device = strdup( psz_name + strlen("dvd://" ) );
1454                 break;
1455             }
1456         }
1457         else if( !strncmp(psz_name, VCD_MRL, strlen(VCD_MRL)) )
1458         {
1459             switch( psz_name[strlen(VCD_MRL)] )
1460             {
1461             case '\0':
1462             case '@':
1463                 psz_device = config_GetPsz( p_intf, VCD_MRL );
1464                 break;
1465             default:
1466                 /* Omit the beginning MRL-selector characters */
1467                 psz_device = strdup( psz_name + strlen(VCD_MRL) );
1468                 break;
1469             }
1470         }
1471         else if( !strncmp(psz_name, CDDA_MRL, strlen(CDDA_MRL) ) )
1472         {
1473             switch( psz_name[strlen(CDDA_MRL)] )
1474             {
1475             case '\0':
1476             case '@':
1477                 psz_device = config_GetPsz( p_intf, "cd-audio" );
1478                 break;
1479             default:
1480                 /* Omit the beginning MRL-selector characters */
1481                 psz_device = strdup( psz_name + strlen(CDDA_MRL) );
1482                 break;
1483             }
1484         }
1485         else
1486         {
1487             psz_device = strdup( psz_name );
1488         }
1489     }
1490
1491     vlc_mutex_unlock( &p_playlist->object_lock );
1492     vlc_object_release( p_playlist );
1493
1494     if( psz_device == NULL )
1495     {
1496         return;
1497     }
1498
1499     /* Remove what we have after @ */
1500     psz_parser = psz_device;
1501     for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
1502     {
1503         if( *psz_parser == '@' )
1504         {
1505             *psz_parser = '\0';
1506             break;
1507         }
1508     }
1509
1510     /* If there's a stream playing, we aren't allowed to eject ! */
1511     if( p_intf->p_sys->p_input == NULL )
1512     {
1513         msg_Dbg( p_intf, "ejecting %s", psz_device );
1514
1515         intf_Eject( p_intf, psz_device );
1516     }
1517
1518     free(psz_device);
1519     return;
1520 }
1521
1522 static void ReadDir( intf_thread_t *p_intf )
1523 {
1524     intf_sys_t     *p_sys = p_intf->p_sys;
1525     DIR *                       p_current_dir;
1526     struct dirent *             p_dir_content;
1527     int i;
1528
1529     if( p_sys->psz_current_dir && *p_sys->psz_current_dir )
1530     {
1531         /* Open the dir */
1532         p_current_dir = opendir( p_sys->psz_current_dir );
1533
1534         if( p_current_dir == NULL )
1535         {
1536             /* something went bad, get out of here ! */
1537 #ifdef HAVE_ERRNO_H
1538             msg_Warn( p_intf, "cannot open directory `%s' (%s)",
1539                       p_sys->psz_current_dir, strerror(errno));
1540 #else
1541             msg_Warn( p_intf, "cannot open directory `%s'", p_sys->psz_current_dir );
1542 #endif
1543             return;
1544         }
1545         
1546         /* Clean the old shit */
1547         for( i = 0; i < p_sys->i_dir_entries; i++ )
1548         {
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 );
1553         }
1554         p_sys->pp_dir_entries = NULL;
1555         p_sys->i_dir_entries = 0;
1556
1557         /* get the first directory entry */
1558         p_dir_content = readdir( p_current_dir );
1559
1560         /* while we still have entries in the directory */
1561         while( p_dir_content != NULL )
1562         {
1563 #if defined( S_ISDIR )
1564             struct stat stat_data;
1565 #endif
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);
1570
1571             sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir,
1572                      p_dir_content->d_name );
1573
1574             if( !( p_dir_entry = malloc( sizeof( struct dir_entry_t) ) ) )
1575             {
1576                 free( psz_uri);
1577                 return;
1578             }
1579
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 )
1585 #else
1586             if( 0 )
1587 #endif
1588             {
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 );
1593             }
1594             else
1595             {
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 );
1600             }
1601
1602             free( psz_uri );
1603             /* Read next entry */
1604             p_dir_content = readdir( p_current_dir );
1605         }
1606         closedir( p_current_dir );
1607         return;
1608     }
1609     else
1610     {
1611         msg_Dbg( p_intf, "no current dir set" );
1612         return;
1613     }
1614 }
1615
1616 static void PlayPause( intf_thread_t *p_intf )
1617 {
1618     input_thread_t *p_input = p_intf->p_sys->p_input;
1619     vlc_value_t val;
1620
1621     if( p_input )
1622     {
1623         var_Get( p_input, "state", &val );
1624         if( val.i_int != PAUSE_S )
1625         {
1626             val.i_int = PAUSE_S;
1627         }
1628         else
1629         {
1630             val.i_int = PLAYING_S;
1631         }
1632         var_Set( p_input, "state", val );
1633     }
1634     else if( p_intf->p_sys->p_playlist )
1635     {
1636         playlist_Play( p_intf->p_sys->p_playlist );
1637     }
1638 }
1639
1640 /****************************************************************************
1641  *
1642  ****************************************************************************/
1643 static void DrawBox( WINDOW *win, int y, int x, int h, int w, char *title )
1644 {
1645     int i;
1646     int i_len;
1647
1648     if(  w > 3 && h > 2 )
1649     {
1650         if( title == NULL ) title = "";
1651         i_len = strlen( title );
1652
1653         if( i_len > w - 2 ) i_len = w - 2;
1654
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 );
1660
1661         for( i = 0; i < h-2; i++ )
1662         {
1663             mvwaddch( win, y+i+1, x,     ACS_VLINE );
1664             mvwaddch( win, y+i+1, x+w-1, ACS_VLINE );
1665         }
1666
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 );
1670     }
1671 }
1672
1673 static void DrawEmptyLine( WINDOW *win, int y, int x, int w )
1674 {
1675     if( w > 0 )
1676     {
1677         mvhline( y, x, ' ', w );
1678     }
1679 }
1680
1681 static void DrawLine( WINDOW *win, int y, int x, int w )
1682 {
1683     if( w > 0 )
1684     {
1685         attrset( A_REVERSE );
1686         mvhline( y, x, ' ', w );
1687         attroff ( A_REVERSE );
1688     }
1689 }