]> git.sesse.net Git - vlc/blob - modules/gui/ncurses.c
ncurses: workaround a race condition spotted by akem. darkwired pwnage
[vlc] / modules / gui / ncurses.c
1 /*****************************************************************************
2  * ncurses.c : NCurses plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001-2006 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <vlc/vlc.h>
31
32 #include <errno.h>                                                 /* ENOMEM */
33 #include <time.h>
34
35 #ifdef HAVE_NCURSESW
36 #   define _XOPEN_SOURCE_EXTENDED 1
37 #endif
38
39 #include <ncurses.h>
40
41 #include <vlc_interface.h>
42 #include <vlc_vout.h>
43 #include <vlc_aout.h>
44 #include <vlc_charset.h>
45 #include <vlc_input.h>
46 #include <vlc_playlist.h>
47
48 #ifdef HAVE_SYS_STAT_H
49 #   include <sys/stat.h>
50 #endif
51 #if (!defined( WIN32 ) || defined(__MINGW32__))
52 /* Mingw has its own version of dirent */
53 #   include <dirent.h>
54 #endif
55
56 #ifdef HAVE_CDDAX
57 #define CDDA_MRL "cddax://"
58 #else
59 #define CDDA_MRL "cdda://"
60 #endif
61
62 #ifdef HAVE_VCDX
63 #define VCD_MRL "vcdx://"
64 #else
65 #define VCD_MRL "vcd://"
66 #endif
67
68 #define SEARCH_CHAIN_SIZE 20
69 #define OPEN_CHAIN_SIZE 50
70
71 /*****************************************************************************
72  * Local prototypes.
73  *****************************************************************************/
74 static int  Open           ( vlc_object_t * );
75 static void Close          ( vlc_object_t * );
76
77 static void Run            ( intf_thread_t * );
78 static void PlayPause      ( intf_thread_t * );
79 static void Eject          ( intf_thread_t * );
80
81 static int  HandleKey      ( intf_thread_t *, int );
82 static void Redraw         ( intf_thread_t *, time_t * );
83
84 static playlist_item_t *PlaylistGetRoot( intf_thread_t * );
85 static void PlaylistRebuild( intf_thread_t * );
86 static void PlaylistAddNode( intf_thread_t *, playlist_item_t *, int, const char *);
87 static void PlaylistDestroy( intf_thread_t * );
88 static int  PlaylistChanged( vlc_object_t *, const char *, vlc_value_t,
89                              vlc_value_t, void * );
90 static inline vlc_bool_t PlaylistIsPlaying( intf_thread_t *,
91                                             playlist_item_t * );
92 static void FindIndex      ( intf_thread_t * );
93 static void SearchPlaylist ( intf_thread_t *, char * );
94 static int  SubSearchPlaylist( intf_thread_t *, char *, int, int );
95
96 static void ManageSlider   ( intf_thread_t * );
97 static void ReadDir        ( intf_thread_t * );
98
99 /*****************************************************************************
100  * Module descriptor
101  *****************************************************************************/
102
103 #define BROWSE_TEXT N_("Filebrowser starting point")
104 #define BROWSE_LONGTEXT N_( \
105     "This option allows you to specify the directory the ncurses filebrowser " \
106     "will show you initially.")
107
108 vlc_module_begin();
109     set_shortname( "Ncurses" );
110     set_description( _("Ncurses interface") );
111     set_capability( "interface", 10 );
112     set_category( CAT_INTERFACE );
113     set_subcategory( SUBCAT_INTERFACE_MAIN );
114     set_callbacks( Open, Close );
115     add_shortcut( "curses" );
116     add_directory( "browse-dir", NULL, NULL, BROWSE_TEXT, BROWSE_LONGTEXT, VLC_FALSE );
117 vlc_module_end();
118
119 /*****************************************************************************
120  * intf_sys_t: description and status of ncurses interface
121  *****************************************************************************/
122 enum
123 {
124     BOX_NONE,
125     BOX_HELP,
126     BOX_INFO,
127     BOX_LOG,
128     BOX_PLAYLIST,
129     BOX_SEARCH,
130     BOX_OPEN,
131     BOX_BROWSE
132 };
133 enum
134 {
135     VIEW_CATEGORY,
136     VIEW_ONELEVEL
137 };
138 struct dir_entry_t
139 {
140     vlc_bool_t  b_file;
141     char        *psz_path;
142 };
143 struct pl_item_t
144 {
145     playlist_item_t *p_item;
146     char            *psz_display;
147 };
148 struct intf_sys_t
149 {
150     playlist_t     *p_playlist;
151     input_thread_t *p_input;
152
153     float           f_slider;
154     float           f_slider_old;
155
156     WINDOW          *w;
157
158     int             i_box_type;
159     int             i_box_y;
160     int             i_box_lines;
161     int             i_box_lines_total;
162     int             i_box_start;
163
164     int             i_box_plidx;    /* Playlist index */
165     int             b_box_plidx_follow;
166     int             i_box_bidx;     /* browser index */
167
168     int             b_box_cleared;
169
170     msg_subscription_t* p_sub;                  /* message bank subscription */
171
172     char            *psz_search_chain;          /* for playlist searching    */
173     char            *psz_old_search;            /* for searching next        */
174     int             i_before_search;
175
176     char            *psz_open_chain;
177     char             psz_partial_keys[7];
178
179     char            *psz_current_dir;
180     int             i_dir_entries;
181     struct dir_entry_t  **pp_dir_entries;
182     vlc_bool_t      b_show_hidden_files;
183
184     int             i_current_view;             /* playlist view             */
185     struct pl_item_t    **pp_plist;
186     int             i_plist_entries;
187     vlc_bool_t      b_need_update;              /* for playlist view         */
188
189     int             i_verbose;                  /* stores verbosity level    */
190 };
191
192 static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title );
193 static void DrawLine( WINDOW *win, int y, int x, int w );
194 static void DrawEmptyLine( WINDOW *win, int y, int x, int w );
195
196 /*****************************************************************************
197  * Open: initialize and create window
198  *****************************************************************************/
199 static int Open( vlc_object_t *p_this )
200 {
201     intf_thread_t *p_intf = (intf_thread_t *)p_this;
202     intf_sys_t    *p_sys;
203     vlc_value_t    val;
204
205     /* Allocate instance and initialize some members */
206     p_sys = p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
207     p_sys->p_playlist = NULL;
208     p_sys->p_input = NULL;
209     p_sys->f_slider = 0.0;
210     p_sys->f_slider_old = 0.0;
211     p_sys->i_box_type = BOX_PLAYLIST;
212     p_sys->i_box_lines = 0;
213     p_sys->i_box_start= 0;
214     p_sys->i_box_lines_total = 0;
215     p_sys->b_box_plidx_follow = VLC_TRUE;
216     p_sys->b_box_cleared = VLC_FALSE;
217     p_sys->i_box_plidx = 0;
218     p_sys->i_box_bidx = 0;
219     p_sys->p_sub = msg_Subscribe( p_intf, MSG_QUEUE_NORMAL );
220     memset( p_sys->psz_partial_keys, 0, sizeof( p_sys->psz_partial_keys ) );
221
222     /* Initialize the curses library */
223     p_sys->w = initscr();
224     keypad( p_sys->w, TRUE );
225     /* Don't do NL -> CR/NL */
226     nonl();
227     /* Take input chars one at a time */
228     cbreak();
229     /* Don't echo */
230     noecho();
231
232     curs_set(0);
233     timeout(0);
234
235     clear();
236
237     /* exported function */
238     p_intf->pf_run = Run;
239
240     /* Remember verbosity level */
241     var_Get( p_intf->p_libvlc, "verbose", &val );
242     p_sys->i_verbose = val.i_int;
243     /* Set quiet mode */
244     val.i_int = -1;
245     var_Set( p_intf->p_libvlc, "verbose", val );
246
247     /* Set defaul playlist view */
248     p_sys->i_current_view = VIEW_CATEGORY;
249     p_sys->pp_plist = NULL;
250     p_sys->i_plist_entries = 0;
251     p_sys->b_need_update = VLC_FALSE;
252
253     /* Initialize search chain */
254     p_sys->psz_search_chain = (char *)malloc( SEARCH_CHAIN_SIZE + 1 );
255     p_sys->psz_old_search = NULL;
256     p_sys->i_before_search = 0;
257
258     /* Initialize open chain */
259     p_sys->psz_open_chain = (char *)malloc( OPEN_CHAIN_SIZE + 1 );
260
261     /* Initialize browser options */
262     var_Create( p_intf, "browse-dir", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
263     var_Get( p_intf, "browse-dir", &val);
264
265     if( val.psz_string && *val.psz_string )
266     {
267         p_sys->psz_current_dir = strdup( val.psz_string );
268         free( val.psz_string );
269     }
270     else
271     {
272         p_sys->psz_current_dir = strdup( p_intf->p_libvlc->psz_homedir );
273     }
274
275     p_sys->i_dir_entries = 0;
276     p_sys->pp_dir_entries = NULL;
277     p_sys->b_show_hidden_files = VLC_FALSE;
278     ReadDir( p_intf );
279
280     return VLC_SUCCESS;
281 }
282
283 /*****************************************************************************
284  * Close: destroy interface window
285  *****************************************************************************/
286 static void Close( vlc_object_t *p_this )
287 {
288     intf_thread_t *p_intf = (intf_thread_t *)p_this;
289     intf_sys_t    *p_sys = p_intf->p_sys;
290     int i;
291
292     var_DelCallback( p_sys->p_playlist, "intf-change", PlaylistChanged,
293                      p_intf );
294     var_DelCallback( p_sys->p_playlist, "item-append", PlaylistChanged,
295                      p_intf );
296
297     PlaylistDestroy( p_intf );
298
299     for( i = 0; i < p_sys->i_dir_entries; i++ )
300     {
301         struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
302         if( p_dir_entry->psz_path ) free( p_dir_entry->psz_path );
303         REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
304         if( p_dir_entry ) free( p_dir_entry );
305     }
306     p_sys->pp_dir_entries = NULL;
307
308     if( p_sys->psz_current_dir ) free( p_sys->psz_current_dir );
309     if( p_sys->psz_search_chain ) free( p_sys->psz_search_chain );
310     if( p_sys->psz_old_search ) free( p_sys->psz_old_search );
311     if( p_sys->psz_open_chain ) free( p_sys->psz_open_chain );
312
313     if( p_sys->p_input )
314     {
315         vlc_object_release( p_sys->p_input );
316     }
317     if( p_sys->p_playlist )
318     {
319         vlc_object_release( p_sys->p_playlist );
320     }
321
322     /* Close the ncurses interface */
323     endwin();
324
325     msg_Unsubscribe( p_intf, p_sys->p_sub );
326
327     /* Restores initial verbose setting */
328     vlc_value_t val;
329     val.i_int = p_sys->i_verbose;
330     var_Set( p_intf->p_libvlc, "verbose", val );
331
332     /* Destroy structure */
333     free( p_sys );
334 }
335
336 /*****************************************************************************
337  * Run: ncurses thread
338  *****************************************************************************/
339 static void Run( intf_thread_t *p_intf )
340 {
341     intf_sys_t    *p_sys = p_intf->p_sys;
342
343     int i_key;
344     time_t t_last_refresh;
345
346     /*
347      * force drawing the interface for the first time
348      */
349     t_last_refresh = ( time( 0 ) - 1);
350
351     while( !intf_ShouldDie( p_intf ) )
352     {
353         msleep( INTF_IDLE_SLEEP );
354
355         /* Update the input */
356         if( p_sys->p_playlist == NULL )
357         {
358             p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
359                                                  FIND_ANYWHERE );
360             if( p_sys->p_playlist )
361             {
362                 var_AddCallback( p_sys->p_playlist, "intf-change",
363                                  PlaylistChanged, p_intf );
364                 var_AddCallback( p_sys->p_playlist, "item-append",
365                                  PlaylistChanged, p_intf );
366             }
367         }
368         if( p_sys->p_playlist )
369         {
370             vlc_mutex_lock( &p_sys->p_playlist->object_lock );
371             if( p_sys->p_input == NULL )
372             {
373                 p_sys->p_input = p_sys->p_playlist->p_input;
374                 if( p_sys->p_input )
375                 {
376                     if( !p_sys->p_input->b_dead )
377                     {
378                         vlc_object_yield( p_sys->p_input );
379                     }
380                 }
381             }
382             else if( p_sys->p_input->b_dead )
383             {
384                 vlc_object_release( p_sys->p_input );
385                 p_sys->p_input = NULL;
386                 p_sys->f_slider = p_sys->f_slider_old = 0.0;
387                 p_sys->b_box_cleared = VLC_FALSE;
388             }
389             vlc_mutex_unlock( &p_sys->p_playlist->object_lock );
390         }
391
392         if( p_sys->b_box_plidx_follow && p_sys->p_playlist->status.p_item )
393         {
394             FindIndex( p_intf );
395         }
396
397         while( ( i_key = getch() ) != -1 )
398         {
399             /*
400              * HandleKey returns 1 if the screen needs to be redrawn
401              */
402             if( HandleKey( p_intf, i_key ) )
403             {
404                 Redraw( p_intf, &t_last_refresh );
405             }
406         }
407         /* Hack */
408         if( p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared )
409         {
410             clear();
411             Redraw( p_intf, &t_last_refresh );
412             p_sys->b_box_cleared = VLC_TRUE;
413         }
414
415         /*
416          * redraw the screen every second
417          */
418         if( (time(0) - t_last_refresh) >= 1 )
419         {
420             ManageSlider( p_intf );
421             Redraw( p_intf, &t_last_refresh );
422         }
423     }
424 }
425
426 /* following functions are local */
427 static char *KeyToUTF8( int i_key, char *psz_part )
428 {
429     char *psz_utf8;
430     int len = strlen( psz_part );
431     if( len == 6 )
432     {
433         /* overflow error - should not happen */
434         memset( psz_part, 0, 6 );
435         return NULL;
436     }
437
438     psz_part[len] = (char)i_key;
439
440 #ifdef HAVE_NCURSESW
441     psz_utf8 = strdup( psz_part );
442 #else
443     psz_utf8 = FromLocaleDup( psz_part );
444
445     /* Ugly check for incomplete bytes sequences
446      * (in case of non-UTF8 multibyte local encoding) */
447     char *psz;
448     for( psz = psz_utf8; *psz; psz++ )
449         if( ( *psz == '?' ) && ( *psz_utf8 != '?' ) )
450         {
451             /* incomplete bytes sequence detected
452              * (VLC core inserted dummy question marks) */
453             free( psz_utf8 );
454             return NULL;
455         }
456
457     /* Check for incomplete UTF8 bytes sequence */
458     if( EnsureUTF8( psz_utf8 ) == NULL )
459     {
460         free( psz_utf8 );
461         return NULL;
462     }
463 #endif
464
465     memset( psz_part, 0, 6 );
466     return psz_utf8;
467 }
468
469 static inline int RemoveLastUTF8Entity( char *psz, int len )
470 {
471     while( len && ( (psz[--len] & 0xc0) == 0x80 ) );
472                        /* UTF8 continuation byte */
473
474     psz[len] = '\0';
475     return len;
476 }
477
478 static int HandleKey( intf_thread_t *p_intf, int i_key )
479 {
480     intf_sys_t *p_sys = p_intf->p_sys;
481     vlc_value_t val;
482
483     if( p_sys->i_box_type == BOX_PLAYLIST && p_sys->p_playlist )
484     {
485         int b_ret = VLC_TRUE;
486         vlc_bool_t b_box_plidx_follow = VLC_FALSE;
487
488         switch( i_key )
489         {
490             vlc_value_t val;
491             /* Playlist Settings */
492             case 'r':
493                 var_Get( p_sys->p_playlist, "random", &val );
494                 val.b_bool = !val.b_bool;
495                 var_Set( p_sys->p_playlist, "random", val );
496                 return 1;
497             case 'l':
498                 var_Get( p_sys->p_playlist, "loop", &val );
499                 val.b_bool = !val.b_bool;
500                 var_Set( p_sys->p_playlist, "loop", val );
501                 return 1;
502             case 'R':
503                 var_Get( p_sys->p_playlist, "repeat", &val );
504                 val.b_bool = !val.b_bool;
505                 var_Set( p_sys->p_playlist, "repeat", val );
506                 return 1;
507
508             /* Playlist sort */
509             case 'o':
510                 playlist_RecursiveNodeSort( p_sys->p_playlist,
511                                             PlaylistGetRoot( p_intf ),
512                                             SORT_TITLE_NODES_FIRST, ORDER_NORMAL );
513                 p_sys->b_need_update = VLC_TRUE;
514                 return 1;
515             case 'O':
516                 playlist_RecursiveNodeSort( p_sys->p_playlist,
517                                             PlaylistGetRoot( p_intf ),
518                                             SORT_TITLE_NODES_FIRST, ORDER_REVERSE );
519                 p_sys->b_need_update = VLC_TRUE;
520                 return 1;
521
522             /* Playlist view */
523             case 'v':
524                 switch( p_sys->i_current_view )
525                 {
526                     case VIEW_CATEGORY:
527                         p_sys->i_current_view = VIEW_ONELEVEL;
528                         break;
529                     default:
530                         p_sys->i_current_view = VIEW_CATEGORY;
531                 }
532                 //p_sys->b_need_update = VLC_TRUE;
533                 PlaylistRebuild( p_intf );
534                 return 1;
535
536             /* Playlist navigation */
537             case KEY_HOME:
538                 p_sys->i_box_plidx = 0;
539                 break;
540             case KEY_END:
541                 p_sys->i_box_plidx = p_sys->p_playlist->items.i_size - 1;
542                 break;
543             case KEY_UP:
544                 p_sys->i_box_plidx--;
545                 break;
546             case KEY_DOWN:
547                 p_sys->i_box_plidx++;
548                 break;
549             case KEY_PPAGE:
550                 p_sys->i_box_plidx -= p_sys->i_box_lines;
551                 break;
552             case KEY_NPAGE:
553                 p_sys->i_box_plidx += p_sys->i_box_lines;
554                 break;
555             case 'D':
556             case KEY_BACKSPACE:
557             case KEY_DC:
558             {
559                 playlist_t *p_playlist = p_sys->p_playlist;
560                 playlist_item_t *p_item;
561
562                 vlc_mutex_lock( &p_playlist->object_lock );
563                 p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
564                 if( p_item->i_children == -1 )
565                 {
566                     playlist_DeleteFromInput( p_playlist,
567                                               p_item->p_input->i_id, VLC_TRUE );
568                 }
569                 else
570                 {
571                     playlist_NodeDelete( p_playlist, p_item,
572                                          VLC_TRUE , VLC_FALSE );
573                 }
574                 vlc_mutex_unlock( &p_playlist->object_lock );
575                 p_sys->b_need_update = VLC_TRUE;
576
577                 break;
578             }
579
580             case KEY_ENTER:
581             case 0x0d:
582                 if( !p_sys->pp_plist || !p_sys->pp_plist[p_sys->i_box_plidx] )
583                 {
584                     b_ret = VLC_FALSE;
585                     break;
586                 }
587                 if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
588                         == -1 )
589                 {
590                     playlist_item_t *p_item, *p_parent;
591                     p_item = p_parent =
592                             p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
593
594                     if( !p_parent )
595                         p_parent = p_sys->p_playlist->p_root_onelevel;
596                     while( p_parent->p_parent )
597                         p_parent = p_parent->p_parent;
598                     playlist_Control( p_sys->p_playlist, PLAYLIST_VIEWPLAY,
599                                       VLC_TRUE, p_parent, p_item );
600                 }
601                 else
602                 {
603                     playlist_Control( p_sys->p_playlist, PLAYLIST_VIEWPLAY,
604                         VLC_TRUE,
605                         p_sys->pp_plist[p_sys->i_box_plidx]->p_item,
606                         NULL );
607                 }
608                 b_box_plidx_follow = VLC_TRUE;
609                 break;
610             default:
611                 b_ret = VLC_FALSE;
612                 break;
613         }
614
615         if( b_ret )
616         {
617             int i_max = p_sys->i_plist_entries;
618             if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
619             if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
620             if( PlaylistIsPlaying( p_intf,
621                     p_sys->pp_plist[p_sys->i_box_plidx]->p_item ) )
622                 b_box_plidx_follow = VLC_TRUE;
623             p_sys->b_box_plidx_follow = b_box_plidx_follow;
624             return 1;
625         }
626     }
627     if( p_sys->i_box_type == BOX_BROWSE )
628     {
629         vlc_bool_t b_ret = VLC_TRUE;
630         /* Browser navigation */
631         switch( i_key )
632         {
633             case KEY_HOME:
634                 p_sys->i_box_bidx = 0;
635                 break;
636             case KEY_END:
637                 p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
638                 break;
639             case KEY_UP:
640                 p_sys->i_box_bidx--;
641                 break;
642             case KEY_DOWN:
643                 p_sys->i_box_bidx++;
644                 break;
645             case KEY_PPAGE:
646                 p_sys->i_box_bidx -= p_sys->i_box_lines;
647                 break;
648             case KEY_NPAGE:
649                 p_sys->i_box_bidx += p_sys->i_box_lines;
650                 break;
651             case '.': /* Toggle show hidden files */
652                 p_sys->b_show_hidden_files = ( p_sys->b_show_hidden_files ==
653                     VLC_TRUE ? VLC_FALSE : VLC_TRUE );
654                 ReadDir( p_intf );
655                 break;
656
657             case KEY_ENTER:
658             case 0x0d:
659             case ' ':
660                 if( p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file || i_key == ' ' )
661                 {
662                     int i_size_entry = strlen( p_sys->psz_current_dir ) +
663                                        strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
664                     char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
665
666                     sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
667
668                     playlist_item_t *p_parent = p_sys->p_playlist->status.p_node;
669                     if( !p_parent )
670                         p_parent = p_sys->p_playlist->p_root_onelevel;
671
672                     while( p_parent->p_parent )
673                         p_parent = p_parent->p_parent;
674
675                     playlist_Add( p_sys->p_playlist, psz_uri, NULL,
676                                   PLAYLIST_APPEND, PLAYLIST_END,
677                                   p_parent == p_sys->p_playlist->p_root_onelevel
678                                   , VLC_FALSE );
679                     p_sys->i_box_type = BOX_PLAYLIST;
680                     free( psz_uri );
681                 }
682                 else
683                 {
684                     int i_size_entry = strlen( p_sys->psz_current_dir ) +
685                                        strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
686                     char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
687
688                     sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
689
690                     p_sys->psz_current_dir = strdup( psz_uri );
691                     ReadDir( p_intf );
692                     free( psz_uri );
693                 }
694                 break;
695             default:
696                 b_ret = VLC_FALSE;
697                 break;
698         }
699         if( b_ret )
700         {
701             if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
702             if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
703             return 1;
704         }
705     }
706     else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO )
707     {
708         switch( i_key )
709         {
710             case KEY_HOME:
711                 p_sys->i_box_start = 0;
712                 return 1;
713             case KEY_END:
714                 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
715                 return 1;
716             case KEY_UP:
717                 if( p_sys->i_box_start > 0 ) p_sys->i_box_start--;
718                 return 1;
719             case KEY_DOWN:
720                 if( p_sys->i_box_start < p_sys->i_box_lines_total - 1 )
721                 {
722                     p_sys->i_box_start++;
723                 }
724                 return 1;
725             case KEY_PPAGE:
726                 p_sys->i_box_start -= p_sys->i_box_lines;
727                 if( p_sys->i_box_start < 0 ) p_sys->i_box_start = 0;
728                 return 1;
729             case KEY_NPAGE:
730                 p_sys->i_box_start += p_sys->i_box_lines;
731                 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
732                 {
733                     p_sys->i_box_start = p_sys->i_box_lines_total - 1;
734                 }
735                 return 1;
736             default:
737                 break;
738         }
739     }
740     else if( p_sys->i_box_type == BOX_NONE )
741     {
742         switch( i_key )
743         {
744             case KEY_HOME:
745                 p_sys->f_slider = 0;
746                 ManageSlider( p_intf );
747                 return 1;
748             case KEY_END:
749                 p_sys->f_slider = 99.9;
750                 ManageSlider( p_intf );
751                 return 1;
752             case KEY_UP:
753                 p_sys->f_slider += 5.0;
754                 if( p_sys->f_slider >= 99.0 ) p_sys->f_slider = 99.0;
755                 ManageSlider( p_intf );
756                 return 1;
757             case KEY_DOWN:
758                 p_sys->f_slider -= 5.0;
759                 if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
760                 ManageSlider( p_intf );
761                 return 1;
762
763             default:
764                 break;
765         }
766     }
767     else if( p_sys->i_box_type == BOX_SEARCH && p_sys->psz_search_chain )
768     {
769         int i_chain_len;
770         i_chain_len = strlen( p_sys->psz_search_chain );
771         switch( i_key )
772         {
773             case 0x0c:      /* ^l */
774                 clear();
775                 return 1;
776             case KEY_ENTER:
777             case 0x0d:
778                 if( i_chain_len > 0 )
779                 {
780                     p_sys->psz_old_search = strdup( p_sys->psz_search_chain );
781                 }
782                 else if( p_sys->psz_old_search )
783                 {
784                     SearchPlaylist( p_intf, p_sys->psz_old_search );
785                 }
786                 p_sys->i_box_type = BOX_PLAYLIST;
787                 return 1;
788             case 0x1b:      /* Esc. */
789                 p_sys->i_box_plidx = p_sys->i_before_search;
790                 p_sys->i_box_type = BOX_PLAYLIST;
791                 return 1;
792             case KEY_BACKSPACE:
793                 RemoveLastUTF8Entity( p_sys->psz_search_chain, i_chain_len );
794                 break;
795             default:
796             {
797                 char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
798
799                 if( psz_utf8 != NULL )
800                 {
801                     if( i_chain_len + strlen( psz_utf8 ) < SEARCH_CHAIN_SIZE )
802                     {
803                         strcpy( p_sys->psz_search_chain + i_chain_len,
804                                 psz_utf8 );
805                     }
806                     free( psz_utf8 );
807                 }
808                 break;
809             }
810         }
811         if( p_sys->psz_old_search )
812         {
813             free( p_sys->psz_old_search );
814             p_sys->psz_old_search = NULL;
815         }
816         SearchPlaylist( p_intf, p_sys->psz_search_chain );
817         return 1;
818     }
819     else if( p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain )
820     {
821         int i_chain_len = strlen( p_sys->psz_open_chain );
822         playlist_t *p_playlist = p_sys->p_playlist;
823
824         switch( i_key )
825         {
826             case 0x0c:      /* ^l */
827                 clear();
828                 return 1;
829             case KEY_ENTER:
830             case 0x0d:
831                 if( p_playlist && i_chain_len > 0 )
832                 {
833                     playlist_item_t *p_parent = p_sys->p_playlist->status.p_node;
834                     if( !p_parent )
835                         p_parent = p_sys->p_playlist->p_root_onelevel;
836
837                     while( p_parent->p_parent )
838                         p_parent = p_parent->p_parent;
839
840                     playlist_Add( p_playlist, p_sys->psz_open_chain, NULL,
841                                   PLAYLIST_APPEND|PLAYLIST_GO, PLAYLIST_END,
842                                   p_parent == p_sys->p_playlist->p_root_onelevel
843                                   , VLC_FALSE );
844                     p_sys->b_box_plidx_follow = VLC_TRUE;
845                 }
846                 p_sys->i_box_type = BOX_PLAYLIST;
847                 return 1;
848             case 0x1b:      /* Esc. */
849                 p_sys->i_box_type = BOX_PLAYLIST;
850                 return 1;
851             case KEY_BACKSPACE:
852                 RemoveLastUTF8Entity( p_sys->psz_open_chain, i_chain_len );
853                 break;
854             default:
855             {
856                 char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
857
858                 if( psz_utf8 != NULL )
859                 {
860                     if( i_chain_len + strlen( psz_utf8 ) < OPEN_CHAIN_SIZE )
861                     {
862                         strcpy( p_sys->psz_open_chain + i_chain_len,
863                                 psz_utf8 );
864                     }
865                     free( psz_utf8 );
866                 }
867                 break;
868             }
869         }
870         return 1;
871     }
872
873
874     /* Common keys */
875     switch( i_key )
876     {
877         case 'q':
878         case 'Q':
879         case 0x1b:  /* Esc */
880             vlc_object_kill( p_intf->p_libvlc );
881             return 0;
882
883         /* Box switching */
884         case 'i':
885             if( p_sys->i_box_type == BOX_INFO )
886                 p_sys->i_box_type = BOX_NONE;
887             else
888                 p_sys->i_box_type = BOX_INFO;
889             p_sys->i_box_lines_total = 0;
890             return 1;
891         case 'L':
892             if( p_sys->i_box_type == BOX_LOG )
893                 p_sys->i_box_type = BOX_NONE;
894             else
895                 p_sys->i_box_type = BOX_LOG;
896             return 1;
897         case 'P':
898             if( p_sys->i_box_type == BOX_PLAYLIST )
899                 p_sys->i_box_type = BOX_NONE;
900             else
901                 p_sys->i_box_type = BOX_PLAYLIST;
902             return 1;
903         case 'B':
904             if( p_sys->i_box_type == BOX_BROWSE )
905                 p_sys->i_box_type = BOX_NONE;
906             else
907                 p_sys->i_box_type = BOX_BROWSE;
908             return 1;
909         case 'h':
910         case 'H':
911             if( p_sys->i_box_type == BOX_HELP )
912                 p_sys->i_box_type = BOX_NONE;
913             else
914                 p_sys->i_box_type = BOX_HELP;
915             p_sys->i_box_lines_total = 0;
916             return 1;
917         case '/':
918             if( p_sys->i_box_type != BOX_SEARCH )
919             {
920                 if( p_sys->psz_search_chain == NULL )
921                 {
922                     return 1;
923                 }
924                 p_sys->psz_search_chain[0] = '\0';
925                 p_sys->b_box_plidx_follow = VLC_FALSE;
926                 p_sys->i_before_search = p_sys->i_box_plidx;
927                 p_sys->i_box_type = BOX_SEARCH;
928             }
929             return 1;
930         case 'A': /* Open */
931             if( p_sys->i_box_type != BOX_OPEN )
932             {
933                 if( p_sys->psz_open_chain == NULL )
934                 {
935                     return 1;
936                 }
937                 p_sys->psz_open_chain[0] = '\0';
938                 p_sys->i_box_type = BOX_OPEN;
939             }
940             return 1;
941
942         /* Navigation */
943         case KEY_RIGHT:
944             p_sys->f_slider += 1.0;
945             if( p_sys->f_slider > 99.9 ) p_sys->f_slider = 99.9;
946             ManageSlider( p_intf );
947             return 1;
948
949         case KEY_LEFT:
950             p_sys->f_slider -= 1.0;
951             if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
952             ManageSlider( p_intf );
953             return 1;
954
955         /* Common control */
956         case 'f':
957         {
958             if( p_intf->p_sys->p_input )
959             {
960                 vout_thread_t *p_vout;
961                 p_vout = vlc_object_find( p_intf->p_sys->p_input,
962                                           VLC_OBJECT_VOUT, FIND_CHILD );
963                 if( p_vout )
964                 {
965                     var_Get( p_vout, "fullscreen", &val );
966                     val.b_bool = !val.b_bool;
967                     var_Set( p_vout, "fullscreen", val );
968                     vlc_object_release( p_vout );
969                 }
970                 else
971                 {
972                     playlist_t *p_playlist;
973                     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
974                                                   FIND_ANYWHERE );
975                     if( p_playlist )
976                     {
977                         var_Get( p_playlist, "fullscreen", &val );
978                         val.b_bool = !val.b_bool;
979                         var_Set( p_playlist, "fullscreen", val );
980                         vlc_object_release( p_playlist );
981                     }
982                 }
983             }
984             return 0;
985         }
986
987         case ' ':
988             PlayPause( p_intf );
989             return 1;
990
991         case 's':
992             if( p_intf->p_sys->p_playlist )
993             {
994                 playlist_Stop( p_intf->p_sys->p_playlist );
995             }
996             return 1;
997
998         case 'e':
999             Eject( p_intf );
1000             return 1;
1001
1002         case '[':
1003             if( p_sys->p_input )
1004             {
1005                 val.b_bool = VLC_TRUE;
1006                 var_Set( p_sys->p_input, "prev-title", val );
1007             }
1008             return 1;
1009
1010         case ']':
1011             if( p_sys->p_input )
1012             {
1013                 val.b_bool = VLC_TRUE;
1014                 var_Set( p_sys->p_input, "next-title", val );
1015             }
1016             return 1;
1017
1018         case '<':
1019             if( p_sys->p_input )
1020             {
1021                 val.b_bool = VLC_TRUE;
1022                 var_Set( p_sys->p_input, "prev-chapter", val );
1023             }
1024             return 1;
1025
1026         case '>':
1027             if( p_sys->p_input )
1028             {
1029                 val.b_bool = VLC_TRUE;
1030                 var_Set( p_sys->p_input, "next-chapter", val );
1031             }
1032             return 1;
1033
1034         case 'p':
1035             if( p_intf->p_sys->p_playlist )
1036             {
1037                 playlist_Prev( p_intf->p_sys->p_playlist );
1038             }
1039             clear();
1040             return 1;
1041
1042         case 'n':
1043             if( p_intf->p_sys->p_playlist )
1044             {
1045                 playlist_Next( p_intf->p_sys->p_playlist );
1046             }
1047             clear();
1048             return 1;
1049
1050         case 'a':
1051             aout_VolumeUp( p_intf, 1, NULL );
1052             clear();
1053             return 1;
1054
1055         case 'z':
1056             aout_VolumeDown( p_intf, 1, NULL );
1057             clear();
1058             return 1;
1059
1060         /*
1061          * ^l should clear and redraw the screen
1062          */
1063         case 0x0c:
1064             clear();
1065             return 1;
1066
1067         default:
1068             return 0;
1069     }
1070 }
1071
1072 static void ManageSlider( intf_thread_t *p_intf )
1073 {
1074     intf_sys_t     *p_sys = p_intf->p_sys;
1075     input_thread_t *p_input = p_sys->p_input;
1076     vlc_value_t     val;
1077
1078     if( p_input == NULL )
1079     {
1080         return;
1081     }
1082     var_Get( p_input, "state", &val );
1083     if( val.i_int != PLAYING_S )
1084     {
1085         return;
1086     }
1087
1088     var_Get( p_input, "position", &val );
1089     if( p_sys->f_slider == p_sys->f_slider_old )
1090     {
1091         p_sys->f_slider =
1092         p_sys->f_slider_old = 100 * val.f_float;
1093     }
1094     else
1095     {
1096         p_sys->f_slider_old = p_sys->f_slider;
1097
1098         val.f_float = p_sys->f_slider / 100.0;
1099         var_Set( p_input, "position", val );
1100     }
1101 }
1102
1103 static void SearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring )
1104 {
1105     int i_max;
1106     int i_first = 0 ;
1107     int i_item = -1;
1108     intf_sys_t *p_sys = p_intf->p_sys;
1109
1110     if( p_sys->i_before_search >= 0 )
1111     {
1112         i_first = p_sys->i_before_search;
1113     }
1114
1115     if( ( ! psz_searchstring ) ||  strlen( psz_searchstring ) <= 0 )
1116     {
1117         p_sys->i_box_plidx = p_sys->i_before_search;
1118         return;
1119     }
1120
1121     i_max = p_sys->i_plist_entries;
1122
1123     i_item = SubSearchPlaylist( p_intf, psz_searchstring, i_first + 1, i_max );
1124     if( i_item < 0 )
1125     {
1126         i_item = SubSearchPlaylist( p_intf, psz_searchstring, 0, i_first );
1127     }
1128
1129     if( i_item < 0 || i_item >= i_max ) return;
1130
1131     p_sys->i_box_plidx = i_item;
1132 }
1133
1134 static int SubSearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring,
1135                               int i_start, int i_stop )
1136 {
1137     intf_sys_t *p_sys = p_intf->p_sys;
1138     int i, i_item = -1;
1139
1140     for( i = i_start + 1; i < i_stop; i++ )
1141     {
1142         if( strcasestr( p_sys->pp_plist[i]->psz_display,
1143                         psz_searchstring ) != NULL )
1144         {
1145             i_item = i;
1146             break;
1147         }
1148     }
1149
1150     return i_item;
1151 }
1152
1153
1154 static void mvnprintw( int y, int x, int w, const char *p_fmt, ... )
1155 {
1156     va_list  vl_args;
1157     char    *p_buf = NULL;
1158     int      i_len;
1159 #ifdef HAVE_NCURSESW
1160     size_t   i_char_len;    /* UCS character length */
1161     size_t   i_width;       /* Display width */
1162     wchar_t *psz_wide;      /* wchar_t representation of p_buf */
1163 #endif
1164
1165     va_start( vl_args, p_fmt );
1166     if( vasprintf( &p_buf, p_fmt, vl_args ) == -1 )
1167         return;
1168     va_end( vl_args );
1169
1170     if( ( p_buf == NULL ) || ( w <= 0 ) )
1171         return;
1172
1173     i_len = strlen( p_buf );
1174 #ifdef HAVE_NCURSESW
1175     psz_wide = (wchar_t *) malloc( sizeof( wchar_t ) * ( i_len + 1 ) );
1176
1177     i_char_len = mbstowcs( psz_wide, p_buf, i_len );
1178
1179     if( i_char_len == (size_t)-1 ) /* an invalid character was encountered */
1180         i_width = i_len;
1181     else
1182     {
1183         i_width = wcswidth( psz_wide, i_char_len );
1184         if( i_width == (size_t)-1 ) /* a non printable character was encountered */
1185             i_width = i_len;
1186     }
1187     if( i_width > (size_t)w )
1188 #else
1189     if( i_len > w )
1190 #endif
1191     { /* FIXME: ncursesw: ellipsize psz_wide while keeping the width in mind */
1192         int i_cut = i_len - w;
1193         int x1 = i_len/2 - i_cut/2;
1194         int x2 = x1 + i_cut;
1195
1196         if( i_len > x2 )
1197         {
1198             memmove( &p_buf[x1], &p_buf[x2], i_len - x2 );
1199         }
1200         p_buf[w] = '\0';
1201         if( w > 7 )
1202         {
1203             p_buf[w/2-1] = '.';
1204             p_buf[w/2  ] = '.';
1205             p_buf[w/2+1] = '.';
1206         }
1207 #ifdef HAVE_NCURSESW
1208         mvprintw( y, x, "%s", p_buf );
1209 #else
1210         char *psz_local = ToLocale( p_buf );
1211         mvprintw( y, x, "%s", psz_local );
1212         LocaleFree( p_buf );
1213 #endif
1214     }
1215     else
1216     {
1217 #ifdef HAVE_NCURSESW
1218         mvprintw( y, x, "%s", p_buf );
1219         mvhline( y, x + i_width, ' ', w - i_width );
1220 #else
1221         char *psz_local = ToLocale( p_buf );
1222         mvprintw( y, x, "%s", psz_local );
1223         LocaleFree( p_buf );
1224         mvhline( y, x + i_len, ' ', w - i_len );
1225 #endif
1226     }
1227 }
1228 static void MainBoxWrite( intf_thread_t *p_intf, int l, int x, const char *p_fmt, ... )
1229 {
1230     intf_sys_t     *p_sys = p_intf->p_sys;
1231
1232     va_list  vl_args;
1233     char    *p_buf = NULL;
1234
1235     if( l < p_sys->i_box_start || l - p_sys->i_box_start >= p_sys->i_box_lines )
1236     {
1237         return;
1238     }
1239
1240     va_start( vl_args, p_fmt );
1241     if( vasprintf( &p_buf, p_fmt, vl_args ) == -1 )
1242         return;
1243     va_end( vl_args );
1244
1245     if( p_buf == NULL )
1246     {
1247         return;
1248     }
1249
1250     mvnprintw( p_sys->i_box_y + l - p_sys->i_box_start, x, COLS - x - 1, "%s", p_buf );
1251 }
1252
1253 static void Redraw( intf_thread_t *p_intf, time_t *t_last_refresh )
1254 {
1255     intf_sys_t     *p_sys = p_intf->p_sys;
1256     input_thread_t *p_input = p_sys->p_input;
1257     int y = 0;
1258     int h;
1259     int y_end;
1260
1261     clear();
1262
1263     /* Title */
1264     attrset( A_REVERSE );
1265     mvnprintw( y, 0, COLS, "VLC media player" " (ncurses interface) [ h for help ]" );
1266     attroff( A_REVERSE );
1267     y += 2;
1268
1269     /* Infos */
1270     if( p_input && !p_input->b_dead )
1271     {
1272         char buf1[MSTRTIME_MAX_SIZE];
1273         char buf2[MSTRTIME_MAX_SIZE];
1274         vlc_value_t val;
1275         vlc_value_t val_list;
1276
1277         /* Source */
1278         char *psz_uri = input_item_GetURI( input_GetItem( p_input ) );
1279         mvnprintw( y++, 0, COLS, " Source   : %s", psz_uri );
1280         free( psz_uri );
1281
1282         /* State */
1283         var_Get( p_input, "state", &val );
1284         if( val.i_int == PLAYING_S )
1285         {
1286             mvnprintw( y, 0, COLS, " State    : Playing" );
1287         }
1288         else if( val.i_int == OPENING_S )
1289         {
1290             mvnprintw( y, 0, COLS, " State    : Opening/Connecting" );
1291         }
1292         else if( val.i_int == BUFFERING_S )
1293         {
1294             mvnprintw( y, 0, COLS, " State    : Buffering" );
1295         }
1296         else if( val.i_int == PAUSE_S )
1297         {
1298             mvnprintw( y, 0, COLS, " State    : Paused" );
1299         }
1300         char *psz_playlist_state = malloc( 25 );
1301         /* strlen( "[Repeat] [Random] [Loop] ) == 24, + '\0' */
1302         psz_playlist_state[0] = '\0';
1303         if( var_GetBool( p_sys->p_playlist, "repeat" ) )
1304             strcat( psz_playlist_state, "[Repeat] " );
1305         if( var_GetBool( p_sys->p_playlist, "random" ) )
1306             strcat( psz_playlist_state, "[Random] " );
1307         if( var_GetBool( p_sys->p_playlist, "loop" ) )
1308             strcat( psz_playlist_state, "[Loop]" );
1309         mvnprintw( y++, 32, COLS, psz_playlist_state );
1310         free( psz_playlist_state );
1311
1312         if( val.i_int != INIT_S && val.i_int != END_S )
1313         {
1314             audio_volume_t i_volume;
1315
1316             /* Position */
1317             var_Get( p_input, "time", &val );
1318             msecstotimestr( buf1, val.i_time / 1000 );
1319
1320             var_Get( p_input, "length", &val );
1321             msecstotimestr( buf2, val.i_time / 1000 );
1322
1323             mvnprintw( y++, 0, COLS, " Position : %s/%s (%.2f%%)", buf1, buf2, p_sys->f_slider );
1324
1325             /* Volume */
1326             aout_VolumeGet( p_intf, &i_volume );
1327             mvnprintw( y++, 0, COLS, " Volume   : %i%%", i_volume*200/AOUT_VOLUME_MAX );
1328
1329             /* Title */
1330             if( !var_Get( p_input, "title", &val ) )
1331             {
1332                 var_Change( p_input, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
1333                 if( val_list.p_list->i_count > 0 )
1334                 {
1335                     mvnprintw( y++, 0, COLS, " Title    : %d/%d", val.i_int, val_list.p_list->i_count );
1336                 }
1337                 var_Change( p_input, "title", VLC_VAR_FREELIST, &val_list, NULL );
1338             }
1339
1340             /* Chapter */
1341             if( !var_Get( p_input, "chapter", &val ) )
1342             {
1343                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
1344                 if( val_list.p_list->i_count > 0 )
1345                 {
1346                     mvnprintw( y++, 0, COLS, " Chapter  : %d/%d", val.i_int, val_list.p_list->i_count );
1347                 }
1348                 var_Change( p_input, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
1349             }
1350         }
1351         else
1352         {
1353             y += 2;
1354         }
1355     }
1356     else
1357     {
1358         mvnprintw( y++, 0, COLS, "Source: <no current item>" );
1359         DrawEmptyLine( p_sys->w, y++, 0, COLS );
1360         DrawEmptyLine( p_sys->w, y++, 0, COLS );
1361         DrawEmptyLine( p_sys->w, y++, 0, COLS );
1362     }
1363
1364     DrawBox( p_sys->w, y, 0, 3, COLS, "" );
1365     DrawEmptyLine( p_sys->w, y+1, 1, COLS-2);
1366     DrawLine( p_sys->w, y+1, 1, (int)(p_intf->p_sys->f_slider/100.0 * (COLS -2)) );
1367     y += 3;
1368
1369     p_sys->i_box_y = y + 1;
1370     p_sys->i_box_lines = LINES - y - 2;
1371
1372     h = LINES - y;
1373     y_end = y + h - 1;
1374
1375     if( p_sys->i_box_type == BOX_HELP )
1376     {
1377         /* Help box */
1378         int l = 0;
1379         DrawBox( p_sys->w, y++, 0, h, COLS, " Help " );
1380
1381         MainBoxWrite( p_intf, l++, 1, "[Display]" );
1382         MainBoxWrite( p_intf, l++, 1, "     h,H         Show/Hide help box" );
1383         MainBoxWrite( p_intf, l++, 1, "     i           Show/Hide info box" );
1384         MainBoxWrite( p_intf, l++, 1, "     L           Show/Hide messages box" );
1385         MainBoxWrite( p_intf, l++, 1, "     P           Show/Hide playlist box" );
1386         MainBoxWrite( p_intf, l++, 1, "     B           Show/Hide filebrowser" );
1387         MainBoxWrite( p_intf, l++, 1, "" );
1388
1389         MainBoxWrite( p_intf, l++, 1, "[Global]" );
1390         MainBoxWrite( p_intf, l++, 1, "     q, Q        Quit" );
1391         MainBoxWrite( p_intf, l++, 1, "     s           Stop" );
1392         MainBoxWrite( p_intf, l++, 1, "     <space>     Pause/Play" );
1393         MainBoxWrite( p_intf, l++, 1, "     f           Toggle Fullscreen" );
1394         MainBoxWrite( p_intf, l++, 1, "     n, p        Next/Previous playlist item" );
1395         MainBoxWrite( p_intf, l++, 1, "     [, ]        Next/Previous title" );
1396         MainBoxWrite( p_intf, l++, 1, "     <, >        Next/Previous chapter" );
1397         MainBoxWrite( p_intf, l++, 1, "     <right>     Seek +1%%" );
1398         MainBoxWrite( p_intf, l++, 1, "     <left>      Seek -1%%" );
1399         MainBoxWrite( p_intf, l++, 1, "     a           Volume Up" );
1400         MainBoxWrite( p_intf, l++, 1, "     z           Volume Down" );
1401         MainBoxWrite( p_intf, l++, 1, "" );
1402
1403         MainBoxWrite( p_intf, l++, 1, "[Playlist]" );
1404         MainBoxWrite( p_intf, l++, 1, "     r           Random" );
1405         MainBoxWrite( p_intf, l++, 1, "     l           Loop Playlist" );
1406         MainBoxWrite( p_intf, l++, 1, "     R           Repeat item" );
1407         MainBoxWrite( p_intf, l++, 1, "     o           Order Playlist by title" );
1408         MainBoxWrite( p_intf, l++, 1, "     O           Reverse order Playlist by title" );
1409         MainBoxWrite( p_intf, l++, 1, "     /           Look for an item" );
1410         MainBoxWrite( p_intf, l++, 1, "     A           Add an entry" );
1411         MainBoxWrite( p_intf, l++, 1, "     D, <del>    Delete an entry" );
1412         MainBoxWrite( p_intf, l++, 1, "     <backspace> Delete an entry" );
1413         MainBoxWrite( p_intf, l++, 1, "" );
1414
1415         MainBoxWrite( p_intf, l++, 1, "[Filebrowser]" );
1416         MainBoxWrite( p_intf, l++, 1, "     <enter>     Add the selected file to the playlist" );
1417         MainBoxWrite( p_intf, l++, 1, "     <space>     Add the selected directory to the playlist" );
1418         MainBoxWrite( p_intf, l++, 1, "     .           Show/Hide hidden files" );
1419         MainBoxWrite( p_intf, l++, 1, "" );
1420
1421         MainBoxWrite( p_intf, l++, 1, "[Boxes]" );
1422         MainBoxWrite( p_intf, l++, 1, "     <up>,<down>     Navigate through the box line by line" );
1423         MainBoxWrite( p_intf, l++, 1, "     <pgup>,<pgdown> Navigate through the box page by page" );
1424         MainBoxWrite( p_intf, l++, 1, "" );
1425
1426         MainBoxWrite( p_intf, l++, 1, "[Player]" );
1427         MainBoxWrite( p_intf, l++, 1, "     <up>,<down>     Seek +/-5%%" );
1428         MainBoxWrite( p_intf, l++, 1, "" );
1429
1430         MainBoxWrite( p_intf, l++, 1, "[Miscellaneous]" );
1431         MainBoxWrite( p_intf, l++, 1, "     Ctrl-l          Refresh the screen" );
1432
1433         p_sys->i_box_lines_total = l;
1434         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1435         {
1436             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1437         }
1438
1439         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1440         {
1441             y += l - p_sys->i_box_start;
1442         }
1443         else
1444         {
1445             y += p_sys->i_box_lines;
1446         }
1447     }
1448     else if( p_sys->i_box_type == BOX_INFO )
1449     {
1450         /* Info box */
1451         int l = 0;
1452         DrawBox( p_sys->w, y++, 0, h, COLS, " Information " );
1453
1454         if( p_input )
1455         {
1456             int i,j;
1457             vlc_mutex_lock( &input_GetItem(p_input)->lock );
1458             for( i = 0; i < input_GetItem(p_input)->i_categories; i++ )
1459             {
1460                 info_category_t *p_category = input_GetItem(p_input)->pp_categories[i];
1461                 if( y >= y_end ) break;
1462                 MainBoxWrite( p_intf, l++, 1, "  [%s]", p_category->psz_name );
1463                 for( j = 0; j < p_category->i_infos; j++ )
1464                 {
1465                     info_t *p_info = p_category->pp_infos[j];
1466                     if( y >= y_end ) break;
1467                     MainBoxWrite( p_intf, l++, 1, "      %s: %s", p_info->psz_name, p_info->psz_value );
1468                 }
1469             }
1470             vlc_mutex_unlock( &input_GetItem(p_input)->lock );
1471         }
1472         else
1473         {
1474             MainBoxWrite( p_intf, l++, 1, "No item currently playing" );
1475         }
1476         p_sys->i_box_lines_total = l;
1477         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1478         {
1479             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1480         }
1481
1482         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1483         {
1484             y += l - p_sys->i_box_start;
1485         }
1486         else
1487         {
1488             y += p_sys->i_box_lines;
1489         }
1490     }
1491     else if( p_sys->i_box_type == BOX_LOG )
1492     {
1493         int i_line = 0;
1494         int i_stop;
1495         int i_start;
1496
1497         DrawBox( p_sys->w, y++, 0, h, COLS, " Logs " );
1498
1499         i_start = p_intf->p_sys->p_sub->i_start;
1500
1501         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1502         i_stop = *p_intf->p_sys->p_sub->pi_stop;
1503         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1504
1505         for( ;; )
1506         {
1507             static const char *ppsz_type[4] = { "", "error", "warning", "debug" };
1508             if( i_line >= h - 2 )
1509             {
1510                 break;
1511             }
1512             i_stop--;
1513             i_line++;
1514             if( i_stop < 0 ) i_stop += VLC_MSG_QSIZE;
1515             if( i_stop == i_start )
1516             {
1517                 break;
1518             }
1519             mvnprintw( y + h-2-i_line, 1, COLS - 2, "   [%s] %s",
1520                       ppsz_type[p_sys->p_sub->p_msg[i_stop].i_type],
1521                       p_sys->p_sub->p_msg[i_stop].psz_msg );
1522         }
1523
1524         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1525         p_intf->p_sys->p_sub->i_start = i_stop;
1526         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1527         y = y_end;
1528     }
1529     else if( p_sys->i_box_type == BOX_BROWSE )
1530     {
1531         /* Filebrowser box */
1532         int        i_start, i_stop;
1533         int        i_item;
1534         DrawBox( p_sys->w, y++, 0, h, COLS, " Browse " );
1535
1536         if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_plidx = p_sys->i_dir_entries - 1;
1537         if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
1538
1539         if( p_sys->i_box_bidx < (h - 2)/2 )
1540         {
1541             i_start = 0;
1542             i_stop = h - 2;
1543         }
1544         else if( p_sys->i_dir_entries - p_sys->i_box_bidx > (h - 2)/2 )
1545         {
1546             i_start = p_sys->i_box_bidx - (h - 2)/2;
1547             i_stop = i_start + h - 2;
1548         }
1549         else
1550         {
1551             i_stop = p_sys->i_dir_entries;
1552             i_start = p_sys->i_dir_entries - (h - 2);
1553         }
1554         if( i_start < 0 )
1555         {
1556             i_start = 0;
1557         }
1558         if( i_stop > p_sys->i_dir_entries )
1559         {
1560             i_stop = p_sys->i_dir_entries;
1561         }
1562
1563         for( i_item = i_start; i_item < i_stop; i_item++ )
1564         {
1565             vlc_bool_t b_selected = ( p_sys->i_box_bidx == i_item );
1566
1567             if( y >= y_end ) break;
1568             if( b_selected )
1569             {
1570                 attrset( A_REVERSE );
1571             }
1572             mvnprintw( y++, 1, COLS - 2, " %c %s", p_sys->pp_dir_entries[i_item]->b_file == VLC_TRUE ? ' ' : '+',
1573                             p_sys->pp_dir_entries[i_item]->psz_path );
1574             if( b_selected )
1575             {
1576                 attroff( A_REVERSE );
1577             }
1578         }
1579
1580     }
1581     else if( ( p_sys->i_box_type == BOX_PLAYLIST ||
1582                p_sys->i_box_type == BOX_SEARCH ||
1583                p_sys->i_box_type == BOX_OPEN  ) && p_sys->p_playlist )
1584     {
1585         /* Playlist box */
1586         int        i_start, i_stop, i_max = p_sys->i_plist_entries;
1587         int        i_item;
1588         char       *psz_title;
1589
1590         switch( p_sys->i_current_view )
1591         {
1592             case VIEW_ONELEVEL:
1593                 psz_title = strdup( " Playlist (All, one level) " );
1594                 break;
1595             case VIEW_CATEGORY:
1596                 psz_title = strdup( " Playlist (By category) " );
1597                 break;
1598             default:
1599                 psz_title = strdup( " Playlist (Manually added) " );
1600         }
1601
1602         DrawBox( p_sys->w, y++, 0, h, COLS, psz_title );
1603
1604         if( p_sys->b_need_update || p_sys->pp_plist == NULL )
1605         {
1606             PlaylistRebuild( p_intf );
1607         }
1608         if( p_sys->b_box_plidx_follow )
1609         {
1610             FindIndex( p_intf );
1611         }
1612
1613         if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
1614         if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
1615         if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
1616
1617         if( p_sys->i_box_plidx < (h - 2)/2 )
1618         {
1619             i_start = 0;
1620             i_stop = h - 2;
1621         }
1622         else if( i_max - p_sys->i_box_plidx > (h - 2)/2 )
1623         {
1624             i_start = p_sys->i_box_plidx - (h - 2)/2;
1625             i_stop = i_start + h - 2;
1626         }
1627         else
1628         {
1629             i_stop = i_max;
1630             i_start = i_max - (h - 2);
1631         }
1632         if( i_start < 0 )
1633         {
1634             i_start = 0;
1635         }
1636         if( i_stop > i_max )
1637         {
1638             i_stop = i_max;
1639         }
1640
1641         for( i_item = i_start; i_item < i_stop; i_item++ )
1642         {
1643             vlc_bool_t b_selected = ( p_sys->i_box_plidx == i_item );
1644             int c = ( PlaylistIsPlaying( p_intf,
1645                           p_sys->pp_plist[i_item]->p_item ) ) ? '>' : ' ';
1646
1647             if( y >= y_end ) break;
1648             if( b_selected )
1649             {
1650                 attrset( A_REVERSE );
1651             }
1652             mvnprintw( y++, 1, COLS - 2, "%c%s", c,
1653                        p_sys->pp_plist[i_item]->psz_display );
1654             if( b_selected )
1655             {
1656                 attroff( A_REVERSE );
1657             }
1658         }
1659
1660     }
1661     else
1662     {
1663         y++;
1664     }
1665     if( p_sys->i_box_type == BOX_SEARCH )
1666     {
1667         DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
1668         if( p_sys->psz_search_chain )
1669         {
1670             if( strlen( p_sys->psz_search_chain ) == 0 &&
1671                 p_sys->psz_old_search != NULL )
1672             {
1673                 /* Searching next entry */
1674                 mvnprintw( 7, 1, COLS-2, "Find: %s", p_sys->psz_old_search );
1675             }
1676             else
1677             {
1678                 mvnprintw( 7, 1, COLS-2, "Find: %s", p_sys->psz_search_chain );
1679             }
1680         }
1681     }
1682     if( p_sys->i_box_type == BOX_OPEN )
1683     {
1684         if( p_sys->psz_open_chain )
1685         {
1686             DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
1687             mvnprintw( 7, 1, COLS-2, "Open: %s", p_sys->psz_open_chain );
1688         }
1689     }
1690
1691     while( y < y_end )
1692     {
1693         DrawEmptyLine( p_sys->w, y++, 1, COLS - 2 );
1694     }
1695
1696     refresh();
1697
1698     *t_last_refresh = time( 0 );
1699 }
1700
1701 static playlist_item_t *PlaylistGetRoot( intf_thread_t *p_intf )
1702 {
1703     intf_sys_t *p_sys = p_intf->p_sys;
1704     playlist_t *p_playlist = p_sys->p_playlist;
1705
1706     if( p_playlist == NULL )
1707     {
1708         return NULL;
1709     }
1710
1711     switch( p_sys->i_current_view )
1712     {
1713         case VIEW_CATEGORY:
1714             return p_playlist->p_root_category;
1715         default:
1716             return p_playlist->p_root_onelevel;
1717     }
1718 }
1719
1720 static void PlaylistRebuild( intf_thread_t *p_intf )
1721 {
1722     intf_sys_t *p_sys = p_intf->p_sys;
1723     playlist_t *p_playlist = p_sys->p_playlist;
1724
1725     if( p_playlist == NULL )
1726     {
1727         return;
1728     }
1729
1730     vlc_mutex_lock( &p_playlist->object_lock );
1731
1732     /* First clear the old one */
1733     PlaylistDestroy( p_intf );
1734
1735     /* Build the new one */
1736     PlaylistAddNode( p_intf, PlaylistGetRoot( p_intf ), 0, "" );
1737
1738     p_sys->b_need_update = VLC_FALSE;
1739
1740     vlc_mutex_unlock( &p_playlist->object_lock );
1741 }
1742
1743 static void PlaylistAddNode( intf_thread_t *p_intf, playlist_item_t *p_node,
1744                              int i, const char *c )
1745 {
1746     intf_sys_t *p_sys = p_intf->p_sys;
1747     playlist_item_t *p_child;
1748     char *psz_tmp;
1749     int k;
1750
1751     psz_tmp = (char *)malloc( strlen( c ) + 4 );
1752     if( psz_tmp == NULL ) return;
1753     for( k = 0; k < p_node->i_children; k++ )
1754     {
1755         struct pl_item_t *p_pl_item;
1756         char *buff;
1757         int i_size;
1758
1759         p_child = p_node->pp_children[k];
1760         i_size = strlen( c ) + strlen( p_child->p_input->psz_name ) + 4;
1761         buff = (char *)malloc( sizeof( char ) * i_size );
1762         p_pl_item = (struct pl_item_t *)malloc( sizeof( struct pl_item_t ) );
1763         if( p_pl_item == NULL || buff == NULL ) return;
1764
1765         if( strlen( c ) )
1766         {
1767             sprintf( buff, "%s%c-%s", c, k == p_node->i_children - 1 ?
1768                      '`' : '|', p_child->p_input->psz_name );
1769         }
1770         else
1771         {
1772             sprintf( buff, " %s", p_child->p_input->psz_name );
1773         }
1774         p_pl_item->psz_display = strdup( buff );
1775         p_pl_item->p_item = p_child;
1776         INSERT_ELEM( p_sys->pp_plist, p_sys->i_plist_entries,
1777                      p_sys->i_plist_entries, p_pl_item );
1778         free( buff );
1779         i++;
1780
1781         if( p_child->i_children > 0 )
1782         {
1783             sprintf( psz_tmp, "%s%c ", c,
1784                      k == p_node->i_children - 1 ? ' ' : '|' );
1785             PlaylistAddNode( p_intf, p_child, i,
1786                              strlen( c ) ? psz_tmp : " " );
1787         }
1788     }
1789     free( psz_tmp );
1790 }
1791
1792 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
1793                             vlc_value_t oval, vlc_value_t nval, void *param )
1794 {
1795     intf_thread_t *p_intf = (intf_thread_t *)param;
1796     p_intf->p_sys->b_need_update = VLC_TRUE;
1797     return VLC_SUCCESS;
1798 }
1799
1800 /* Playlist suxx */
1801 static inline vlc_bool_t PlaylistIsPlaying( intf_thread_t *p_intf,
1802                                             playlist_item_t *p_item )
1803 {
1804     playlist_item_t *p_played_item = p_intf->p_sys->p_playlist->status.p_item;
1805     return( p_item != NULL && p_played_item != NULL &&
1806             p_item->p_input != NULL && p_played_item->p_input != NULL &&
1807             p_item->p_input->i_id == p_played_item->p_input->i_id );
1808 }
1809
1810 static void FindIndex( intf_thread_t *p_intf )
1811 {
1812     intf_sys_t *p_sys = p_intf->p_sys;
1813     int i;
1814
1815     if( p_sys->i_box_plidx < p_sys->i_plist_entries &&
1816         p_sys->i_box_plidx >= 0 &&
1817         PlaylistIsPlaying( p_intf,
1818                            p_sys->pp_plist[p_sys->i_box_plidx]->p_item ) )
1819     {
1820         return;
1821     }
1822
1823     for( i = 0; i < p_sys->i_plist_entries; i++ )
1824     {
1825         if( PlaylistIsPlaying( p_intf, p_sys->pp_plist[i]->p_item ) )
1826         {
1827             p_sys->i_box_plidx = i;
1828             break;
1829         }
1830     }
1831 }
1832
1833 static void PlaylistDestroy( intf_thread_t *p_intf )
1834 {
1835     intf_sys_t *p_sys = p_intf->p_sys;
1836     int i;
1837
1838     for( i = 0; i < p_sys->i_plist_entries; i++ )
1839     {
1840         struct pl_item_t *p_pl_item = p_sys->pp_plist[i];
1841         free( p_pl_item->psz_display );
1842         REMOVE_ELEM( p_sys->pp_plist, p_sys->i_plist_entries, i );
1843         free( p_pl_item );
1844     }
1845     p_sys->pp_plist = NULL;
1846     p_sys->i_plist_entries = 0;
1847 }
1848
1849 static void Eject( intf_thread_t *p_intf )
1850 {
1851     char *psz_device = NULL, *psz_parser, *psz_name;
1852
1853     /*
1854      * Get the active input
1855      * Determine whether we can eject a media, ie it's a DVD, VCD or CD-DA
1856      * If it's neither of these, then return
1857      */
1858
1859     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1860                                                        FIND_ANYWHERE );
1861
1862     if( p_playlist == NULL )
1863     {
1864         return;
1865     }
1866
1867     vlc_mutex_lock( &p_playlist->object_lock );
1868
1869     if( p_playlist->status.p_item == NULL )
1870     {
1871         vlc_mutex_unlock( &p_playlist->object_lock );
1872         vlc_object_release( p_playlist );
1873         return;
1874     }
1875
1876     psz_name = p_playlist->status.p_item->p_input->psz_name;
1877
1878     if( psz_name )
1879     {
1880         if( !strncmp(psz_name, "dvd://", 4) )
1881         {
1882             switch( psz_name[strlen("dvd://")] )
1883             {
1884             case '\0':
1885             case '@':
1886                 psz_device = config_GetPsz( p_intf, "dvd" );
1887                 break;
1888             default:
1889                 /* Omit the first MRL-selector characters */
1890                 psz_device = strdup( psz_name + strlen("dvd://" ) );
1891                 break;
1892             }
1893         }
1894         else if( !strncmp(psz_name, VCD_MRL, strlen(VCD_MRL)) )
1895         {
1896             switch( psz_name[strlen(VCD_MRL)] )
1897             {
1898             case '\0':
1899             case '@':
1900                 psz_device = config_GetPsz( p_intf, VCD_MRL );
1901                 break;
1902             default:
1903                 /* Omit the beginning MRL-selector characters */
1904                 psz_device = strdup( psz_name + strlen(VCD_MRL) );
1905                 break;
1906             }
1907         }
1908         else if( !strncmp(psz_name, CDDA_MRL, strlen(CDDA_MRL) ) )
1909         {
1910             switch( psz_name[strlen(CDDA_MRL)] )
1911             {
1912             case '\0':
1913             case '@':
1914                 psz_device = config_GetPsz( p_intf, "cd-audio" );
1915                 break;
1916             default:
1917                 /* Omit the beginning MRL-selector characters */
1918                 psz_device = strdup( psz_name + strlen(CDDA_MRL) );
1919                 break;
1920             }
1921         }
1922         else
1923         {
1924             psz_device = strdup( psz_name );
1925         }
1926     }
1927
1928     vlc_mutex_unlock( &p_playlist->object_lock );
1929     vlc_object_release( p_playlist );
1930
1931     if( psz_device == NULL )
1932     {
1933         return;
1934     }
1935
1936     /* Remove what we have after @ */
1937     psz_parser = psz_device;
1938     for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
1939     {
1940         if( *psz_parser == '@' )
1941         {
1942             *psz_parser = '\0';
1943             break;
1944         }
1945     }
1946
1947     /* If there's a stream playing, we aren't allowed to eject ! */
1948     if( p_intf->p_sys->p_input == NULL )
1949     {
1950         msg_Dbg( p_intf, "ejecting %s", psz_device );
1951
1952         intf_Eject( p_intf, psz_device );
1953     }
1954
1955     free(psz_device);
1956     return;
1957 }
1958
1959 static int comp_dir_entries( const void *pp_dir_entry1,
1960                              const void *pp_dir_entry2 )
1961 {
1962     struct dir_entry_t *p_dir_entry1 = *(struct dir_entry_t**)pp_dir_entry1;
1963     struct dir_entry_t *p_dir_entry2 = *(struct dir_entry_t**)pp_dir_entry2;
1964     if ( p_dir_entry1->b_file == p_dir_entry2->b_file ) {
1965         return strcasecmp( p_dir_entry1->psz_path, p_dir_entry2->psz_path );
1966     }
1967     else
1968     {
1969         return ( p_dir_entry1->b_file ? 1 : -1 );
1970     }
1971 }
1972
1973 static void ReadDir( intf_thread_t *p_intf )
1974 {
1975     intf_sys_t *p_sys = p_intf->p_sys;
1976     DIR *p_current_dir;
1977     int i;
1978
1979     if( p_sys->psz_current_dir && *p_sys->psz_current_dir )
1980     {
1981         char *psz_entry;
1982
1983         /* Open the dir */
1984         p_current_dir = utf8_opendir( p_sys->psz_current_dir );
1985
1986         if( p_current_dir == NULL )
1987         {
1988             /* something went bad, get out of here ! */
1989             msg_Warn( p_intf, "cannot open directory `%s' (%m)",
1990                       p_sys->psz_current_dir );
1991             return;
1992         }
1993
1994         /* Clean the old shit */
1995         for( i = 0; i < p_sys->i_dir_entries; i++ )
1996         {
1997             struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
1998             free( p_dir_entry->psz_path );
1999             REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
2000             free( p_dir_entry );
2001         }
2002         p_sys->pp_dir_entries = NULL;
2003         p_sys->i_dir_entries = 0;
2004
2005         /* while we still have entries in the directory */
2006         while( ( psz_entry = utf8_readdir( p_current_dir ) ) != NULL )
2007         {
2008 #if defined( S_ISDIR )
2009             struct stat stat_data;
2010 #endif
2011             struct dir_entry_t *p_dir_entry;
2012             int i_size_entry = strlen( p_sys->psz_current_dir ) +
2013                                strlen( psz_entry ) + 2;
2014             char *psz_uri;
2015
2016             if( p_sys->b_show_hidden_files == VLC_FALSE &&
2017                 ( strlen( psz_entry ) && psz_entry[0] == '.' ) &&
2018                 strcmp( psz_entry, ".." ) )
2019             {
2020                 free( psz_entry );
2021                 continue;
2022             }
2023
2024             psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
2025             sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, psz_entry );
2026
2027             if( !( p_dir_entry = malloc( sizeof( struct dir_entry_t) ) ) )
2028             {
2029                 free( psz_uri );
2030                 free( psz_entry );
2031                 continue;
2032             }
2033
2034 #if defined( S_ISDIR )
2035             if( !utf8_stat( psz_uri, &stat_data )
2036              && S_ISDIR(stat_data.st_mode) )
2037 /*#elif defined( DT_DIR )
2038             if( p_dir_content->d_type & DT_DIR )*/
2039 #else
2040             if( 0 )
2041 #endif
2042             {
2043                 p_dir_entry->psz_path = strdup( psz_entry );
2044                 p_dir_entry->b_file = VLC_FALSE;
2045                 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2046                      p_sys->i_dir_entries, p_dir_entry );
2047             }
2048             else
2049             {
2050                 p_dir_entry->psz_path = strdup( psz_entry );
2051                 p_dir_entry->b_file = VLC_TRUE;
2052                 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2053                      p_sys->i_dir_entries, p_dir_entry );
2054             }
2055
2056             free( psz_uri );
2057             free( psz_entry );
2058         }
2059
2060         /* Sort */
2061         qsort( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2062                sizeof(struct dir_entry_t*), &comp_dir_entries );
2063
2064         closedir( p_current_dir );
2065         return;
2066     }
2067     else
2068     {
2069         msg_Dbg( p_intf, "no current dir set" );
2070         return;
2071     }
2072 }
2073
2074 static void PlayPause( intf_thread_t *p_intf )
2075 {
2076     input_thread_t *p_input = p_intf->p_sys->p_input;
2077     vlc_value_t val;
2078
2079     if( p_input )
2080     {
2081         var_Get( p_input, "state", &val );
2082         if( val.i_int != PAUSE_S )
2083         {
2084             val.i_int = PAUSE_S;
2085         }
2086         else
2087         {
2088             val.i_int = PLAYING_S;
2089         }
2090         var_Set( p_input, "state", val );
2091     }
2092     else if( p_intf->p_sys->p_playlist )
2093     {
2094         playlist_Play( p_intf->p_sys->p_playlist );
2095     }
2096 }
2097
2098 /****************************************************************************
2099  *
2100  ****************************************************************************/
2101 static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title )
2102 {
2103     int i;
2104     int i_len;
2105
2106     if( w > 3 && h > 2 )
2107     {
2108         if( title == NULL ) title = "";
2109         i_len = strlen( title );
2110
2111         if( i_len > w - 2 ) i_len = w - 2;
2112
2113         mvwaddch( win, y, x,    ACS_ULCORNER );
2114         mvwhline( win, y, x+1,  ACS_HLINE, ( w-i_len-2)/2 );
2115         mvwprintw( win,y, x+1+(w-i_len-2)/2, "%s", title );
2116         mvwhline( win, y, x+(w-i_len)/2+i_len,  ACS_HLINE, w - 1 - ((w-i_len)/2+i_len) );
2117         mvwaddch( win, y, x+w-1,ACS_URCORNER );
2118
2119         for( i = 0; i < h-2; i++ )
2120         {
2121             mvwaddch( win, y+i+1, x,     ACS_VLINE );
2122             mvwaddch( win, y+i+1, x+w-1, ACS_VLINE );
2123         }
2124
2125         mvwaddch( win, y+h-1, x,     ACS_LLCORNER );
2126         mvwhline( win, y+h-1, x+1,   ACS_HLINE, w - 2 );
2127         mvwaddch( win, y+h-1, x+w-1, ACS_LRCORNER );
2128     }
2129 }
2130
2131 static void DrawEmptyLine( WINDOW *win, int y, int x, int w )
2132 {
2133     if( w > 0 )
2134     {
2135         mvhline( y, x, ' ', w );
2136     }
2137 }
2138
2139 static void DrawLine( WINDOW *win, int y, int x, int w )
2140 {
2141     if( w > 0 )
2142     {
2143         attrset( A_REVERSE );
2144         mvhline( y, x, ' ', w );
2145         attroff( A_REVERSE );
2146     }
2147 }