]> git.sesse.net Git - vlc/blob - modules/gui/ncurses.c
Display live stats in ncurses interface.
[vlc] / modules / gui / ncurses.c
1 /*****************************************************************************
2  * ncurses.c : NCurses interface for vlc
3  *****************************************************************************
4  * Copyright © 2001-2007 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  *          Rafaël Carré <funman@videolanorg>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*
29  * Note that when we use wide characters (and link with libncursesw),
30  * we assume that an UTF8 locale is used (or compatible, such as ASCII).
31  * Other characters encodings are not supported.
32  */
33
34 /*****************************************************************************
35  * Preamble
36  *****************************************************************************/
37 #ifdef HAVE_CONFIG_H
38 # include "config.h"
39 #endif
40
41 #include <vlc/vlc.h>
42
43 #ifdef HAVE_NCURSESW
44 #   define _XOPEN_SOURCE_EXTENDED 1
45 #endif
46
47 #include <ncurses.h>
48
49 #include <vlc_interface.h>
50 #include <vlc_vout.h>
51 #include <vlc_aout.h>
52 #include <vlc_charset.h>
53 #include <vlc_input.h>
54 #include <vlc_playlist.h>
55 #include <vlc_meta.h>
56
57 #ifdef HAVE_SYS_STAT_H
58 #   include <sys/stat.h>
59 #endif
60 #if (!defined( WIN32 ) || defined(__MINGW32__))
61 /* Mingw has its own version of dirent */
62 #   include <dirent.h>
63 #endif
64
65 #ifdef HAVE_CDDAX
66 #define CDDA_MRL "cddax://"
67 #else
68 #define CDDA_MRL "cdda://"
69 #endif
70
71 #ifdef HAVE_VCDX
72 #define VCD_MRL "vcdx://"
73 #else
74 #define VCD_MRL "vcd://"
75 #endif
76
77 #define SEARCH_CHAIN_SIZE 20
78 #define OPEN_CHAIN_SIZE 50
79
80 /*****************************************************************************
81  * Local prototypes.
82  *****************************************************************************/
83 static int  Open           ( vlc_object_t * );
84 static void Close          ( vlc_object_t * );
85
86 static void Run            ( intf_thread_t * );
87 static void PlayPause      ( intf_thread_t * );
88 static void Eject          ( intf_thread_t * );
89
90 static int  HandleKey      ( intf_thread_t *, int );
91 static void Redraw         ( intf_thread_t *, time_t * );
92
93 static playlist_item_t *PlaylistGetRoot( intf_thread_t * );
94 static void PlaylistRebuild( intf_thread_t * );
95 static void PlaylistAddNode( intf_thread_t *, playlist_item_t *, int, const char *);
96 static void PlaylistDestroy( intf_thread_t * );
97 static int  PlaylistChanged( vlc_object_t *, const char *, vlc_value_t,
98                              vlc_value_t, void * );
99 static inline vlc_bool_t PlaylistIsPlaying( intf_thread_t *,
100                                             playlist_item_t * );
101 static void FindIndex      ( intf_thread_t * );
102 static void SearchPlaylist ( intf_thread_t *, char * );
103 static int  SubSearchPlaylist( intf_thread_t *, char *, int, int );
104
105 static void ManageSlider   ( intf_thread_t * );
106 static void ReadDir        ( intf_thread_t * );
107
108 static void start_color_and_pairs ( intf_thread_t * );
109
110 /*****************************************************************************
111  * Module descriptor
112  *****************************************************************************/
113
114 #define BROWSE_TEXT N_("Filebrowser starting point")
115 #define BROWSE_LONGTEXT N_( \
116     "This option allows you to specify the directory the ncurses filebrowser " \
117     "will show you initially.")
118
119 vlc_module_begin();
120     set_shortname( "Ncurses" );
121     set_description( _("Ncurses interface") );
122     set_capability( "interface", 10 );
123     set_category( CAT_INTERFACE );
124     set_subcategory( SUBCAT_INTERFACE_MAIN );
125     set_callbacks( Open, Close );
126     add_shortcut( "curses" );
127     add_directory( "browse-dir", NULL, NULL, BROWSE_TEXT, BROWSE_LONGTEXT, VLC_FALSE );
128 vlc_module_end();
129
130 /*****************************************************************************
131  * intf_sys_t: description and status of ncurses interface
132  *****************************************************************************/
133 enum
134 {
135     BOX_NONE,
136     BOX_HELP,
137     BOX_INFO,
138     BOX_LOG,
139     BOX_PLAYLIST,
140     BOX_SEARCH,
141     BOX_OPEN,
142     BOX_BROWSE,
143     BOX_META,
144     BOX_OBJECTS,
145     BOX_STATS
146 };
147 enum
148 {
149     C_DEFAULT = 0,
150     C_TITLE,
151     C_PLAYLIST_1,
152     C_PLAYLIST_2,
153     C_PLAYLIST_3,
154     C_BOX,
155     C_STATUS,
156     C_INFO,
157     C_ERROR,
158     C_WARNING,
159     C_DEBUG,
160     C_CATEGORY,
161     C_FOLDER
162 };
163 enum
164 {
165     VIEW_CATEGORY,
166     VIEW_ONELEVEL
167 };
168 struct dir_entry_t
169 {
170     vlc_bool_t  b_file;
171     char        *psz_path;
172 };
173 struct pl_item_t
174 {
175     playlist_item_t *p_item;
176     char            *psz_display;
177 };
178 struct intf_sys_t
179 {
180     input_thread_t *p_input;
181
182     vlc_bool_t      b_color;
183     vlc_bool_t      b_color_started;
184
185     float           f_slider;
186     float           f_slider_old;
187
188     WINDOW          *w;
189
190     int             i_box_type;
191     int             i_box_y;
192     int             i_box_lines;
193     int             i_box_lines_total;
194     int             i_box_start;
195
196     int             i_box_plidx;    /* Playlist index */
197     int             b_box_plidx_follow;
198     int             i_box_bidx;     /* browser index */
199
200     playlist_item_t *p_node;        /* current node */
201
202     int             b_box_cleared;
203
204     msg_subscription_t* p_sub;                  /* message bank subscription */
205
206     char            *psz_search_chain;          /* for playlist searching    */
207     char            *psz_old_search;            /* for searching next        */
208     int             i_before_search;
209
210     char            *psz_open_chain;
211 #ifndef HAVE_NCURSESW
212     char             psz_partial_keys[7];
213 #endif
214
215     char            *psz_current_dir;
216     int             i_dir_entries;
217     struct dir_entry_t  **pp_dir_entries;
218     vlc_bool_t      b_show_hidden_files;
219
220     int             i_current_view;             /* playlist view             */
221     struct pl_item_t    **pp_plist;
222     int             i_plist_entries;
223     vlc_bool_t      b_need_update;              /* for playlist view         */
224
225     int             i_verbose;                  /* stores verbosity level    */
226 };
227
228 static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title, vlc_bool_t b_color );
229 static void DrawLine( WINDOW *win, int y, int x, int w );
230 static void DrawEmptyLine( WINDOW *win, int y, int x, int w );
231
232 /*****************************************************************************
233  * Open: initialize and create window
234  *****************************************************************************/
235 static int Open( vlc_object_t *p_this )
236 {
237     intf_thread_t *p_intf = (intf_thread_t *)p_this;
238     intf_sys_t    *p_sys;
239     vlc_value_t    val;
240
241     /* Allocate instance and initialize some members */
242     p_sys = p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
243     p_sys->p_node = NULL;
244     p_sys->p_input = NULL;
245     p_sys->f_slider = 0.0;
246     p_sys->f_slider_old = 0.0;
247     p_sys->i_box_type = BOX_PLAYLIST;
248     p_sys->i_box_lines = 0;
249     p_sys->i_box_start= 0;
250     p_sys->i_box_lines_total = 0;
251     p_sys->b_box_plidx_follow = VLC_TRUE;
252     p_sys->b_box_cleared = VLC_FALSE;
253     p_sys->i_box_plidx = 0;
254     p_sys->i_box_bidx = 0;
255     p_sys->p_sub = msg_Subscribe( p_intf, MSG_QUEUE_NORMAL );
256     p_sys->b_color = p_this->p_libvlc->b_color;
257     p_sys->b_color_started = VLC_FALSE;
258
259 #ifndef HAVE_NCURSESW
260     memset( p_sys->psz_partial_keys, 0, sizeof( p_sys->psz_partial_keys ) );
261 #endif
262
263     /* Initialize the curses library */
264     p_sys->w = initscr();
265
266     if( p_sys->b_color )
267         start_color_and_pairs( p_intf );
268
269     keypad( p_sys->w, TRUE );
270     /* Don't do NL -> CR/NL */
271     nonl();
272     /* Take input chars one at a time */
273     cbreak();
274     /* Don't echo */
275     noecho();
276     /* Invisible cursor */
277     curs_set( 0 );
278     /* Non blocking wgetch() */
279     wtimeout( p_sys->w, 0 );
280
281     clear();
282
283     /* exported function */
284     p_intf->pf_run = Run;
285
286     /* Remember verbosity level */
287     var_Get( p_intf->p_libvlc, "verbose", &val );
288     p_sys->i_verbose = val.i_int;
289     /* Set quiet mode */
290     val.i_int = -1;
291     var_Set( p_intf->p_libvlc, "verbose", val );
292
293     /* Set defaul playlist view */
294     p_sys->i_current_view = VIEW_CATEGORY;
295     p_sys->pp_plist = NULL;
296     p_sys->i_plist_entries = 0;
297     p_sys->b_need_update = VLC_FALSE;
298
299     /* Initialize search chain */
300     p_sys->psz_search_chain = (char *)malloc( SEARCH_CHAIN_SIZE + 1 );
301     p_sys->psz_old_search = NULL;
302     p_sys->i_before_search = 0;
303
304     /* Initialize open chain */
305     p_sys->psz_open_chain = (char *)malloc( OPEN_CHAIN_SIZE + 1 );
306
307     /* Initialize browser options */
308     var_Create( p_intf, "browse-dir", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
309     var_Get( p_intf, "browse-dir", &val);
310
311     if( val.psz_string && *val.psz_string )
312     {
313         p_sys->psz_current_dir = strdup( val.psz_string );
314         free( val.psz_string );
315     }
316     else
317     {
318         p_sys->psz_current_dir = strdup( p_intf->p_libvlc->psz_homedir );
319     }
320
321     p_sys->i_dir_entries = 0;
322     p_sys->pp_dir_entries = NULL;
323     p_sys->b_show_hidden_files = VLC_FALSE;
324     ReadDir( p_intf );
325
326     return VLC_SUCCESS;
327 }
328
329 /*****************************************************************************
330  * Close: destroy interface window
331  *****************************************************************************/
332 static void Close( vlc_object_t *p_this )
333 {
334     intf_thread_t *p_intf = (intf_thread_t *)p_this;
335     intf_sys_t    *p_sys = p_intf->p_sys;
336     playlist_t    *p_playlist = pl_Get( p_intf );
337     int i;
338
339     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, p_intf );
340     var_DelCallback( p_playlist, "item-append", PlaylistChanged, p_intf );
341
342     PlaylistDestroy( p_intf );
343
344     for( i = 0; i < p_sys->i_dir_entries; i++ )
345     {
346         struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
347         free( p_dir_entry->psz_path );
348         REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
349         free( p_dir_entry );
350     }
351     p_sys->pp_dir_entries = NULL;
352
353     free( p_sys->psz_current_dir );
354     free( p_sys->psz_search_chain );
355     free( p_sys->psz_old_search );
356     free( p_sys->psz_open_chain );
357
358     if( p_sys->p_input )
359     {
360         vlc_object_release( p_sys->p_input );
361     }
362     pl_Release( p_intf );
363
364     /* Close the ncurses interface */
365     endwin();
366
367     msg_Unsubscribe( p_intf, p_sys->p_sub );
368
369     /* Restores initial verbose setting */
370     vlc_value_t val;
371     val.i_int = p_sys->i_verbose;
372     var_Set( p_intf->p_libvlc, "verbose", val );
373
374     /* Destroy structure */
375     free( p_sys );
376 }
377
378 /*****************************************************************************
379  * Run: ncurses thread
380  *****************************************************************************/
381 static void Run( intf_thread_t *p_intf )
382 {
383     intf_sys_t    *p_sys = p_intf->p_sys;
384     playlist_t    *p_playlist = pl_Yield( p_intf );
385
386     int i_key;
387     time_t t_last_refresh;
388
389     /*
390      * force drawing the interface for the first time
391      */
392     t_last_refresh = ( time( 0 ) - 1);
393     /*
394      * force building of the playlist array
395      */
396     PlaylistRebuild( p_intf );
397
398     while( !intf_ShouldDie( p_intf ) )
399     {
400         msleep( INTF_IDLE_SLEEP );
401
402         /* Update the input */
403         var_AddCallback( p_playlist, "intf-change", PlaylistChanged, p_intf );
404         var_AddCallback( p_playlist, "item-append", PlaylistChanged, p_intf );
405
406         PL_LOCK;
407         if( p_sys->p_input == NULL )
408         {
409             p_sys->p_input = p_playlist->p_input;
410             if( p_sys->p_input )
411             {
412                 if( !p_sys->p_input->b_dead )
413                 {
414                     vlc_object_yield( p_sys->p_input );
415                 }
416             }
417         }
418         else if( p_sys->p_input->b_dead )
419         {
420             vlc_object_release( p_sys->p_input );
421             p_sys->p_input = NULL;
422             p_sys->f_slider = p_sys->f_slider_old = 0.0;
423             p_sys->b_box_cleared = VLC_FALSE;
424         }
425         PL_UNLOCK;
426
427         if( p_sys->b_box_plidx_follow && p_playlist->status.p_item )
428         {
429             FindIndex( p_intf );
430         }
431     
432         while( ( i_key = wgetch( p_sys->w ) ) != -1 )
433         {
434             /*
435              * HandleKey returns 1 if the screen needs to be redrawn
436              */
437             if( HandleKey( p_intf, i_key ) )
438             {
439                 Redraw( p_intf, &t_last_refresh );
440             }
441         }
442         /* Hack */
443         if( p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared )
444         {
445             clear();
446             Redraw( p_intf, &t_last_refresh );
447             p_sys->b_box_cleared = VLC_TRUE;
448         }
449
450         /*
451          * redraw the screen every second
452          */
453         if( (time(0) - t_last_refresh) >= 1 )
454         {
455             ManageSlider( p_intf );
456             Redraw( p_intf, &t_last_refresh );
457         }
458     }
459 }
460
461 /* following functions are local */
462 static void start_color_and_pairs( intf_thread_t *p_intf )
463 {
464     assert( p_intf->p_sys->b_color && !p_intf->p_sys->b_color_started );
465
466     if( !has_colors() )
467     {
468         p_intf->p_sys->b_color = VLC_FALSE;
469         msg_Warn( p_intf, "Terminal doesn't support colors" );
470         return;
471     }
472
473     start_color();
474
475     /* Available colors: BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE */
476
477     /* untested, in all my terminals, !can_change_color() --funman */
478     if( can_change_color() )
479         init_color( COLOR_YELLOW, 960, 500, 0 ); /* YELLOW -> ORANGE */
480
481     /* title */
482     init_pair( C_TITLE, COLOR_YELLOW, COLOR_BLACK );
483
484     /* jamaican playlist */
485     init_pair( C_PLAYLIST_1, COLOR_GREEN, COLOR_BLACK );
486     init_pair( C_PLAYLIST_2, COLOR_YELLOW, COLOR_BLACK );
487     init_pair( C_PLAYLIST_3, COLOR_RED, COLOR_BLACK );
488
489     /* used in DrawBox() */
490     init_pair( C_BOX, COLOR_CYAN, COLOR_BLACK );
491     /* Source, State, Position, Volume, Chapters, etc...*/
492     init_pair( C_STATUS, COLOR_BLUE, COLOR_BLACK );
493
494     /* VLC messages, keep the order from highest priority to lowest */
495
496     /* infos */
497     init_pair( C_INFO, COLOR_BLACK, COLOR_WHITE );
498     /* errors */
499     init_pair( C_ERROR, COLOR_RED, COLOR_BLACK );
500     /* warnings */
501     init_pair( C_WARNING, COLOR_YELLOW, COLOR_BLACK );
502 /* debug */
503     init_pair( C_DEBUG, COLOR_WHITE, COLOR_BLACK );
504
505     /* Category title (help, info, metadata) */
506     init_pair( C_CATEGORY, COLOR_MAGENTA, COLOR_BLACK );
507
508     /* Folder (BOX_BROWSE) */
509     init_pair( C_FOLDER, COLOR_RED, COLOR_BLACK );
510
511     p_intf->p_sys->b_color_started = VLC_TRUE;
512 }
513
514 #ifndef HAVE_NCURSESW
515 static char *KeyToUTF8( int i_key, char *psz_part )
516 {
517     char *psz_utf8;
518     int len = strlen( psz_part );
519     if( len == 6 )
520     {
521         /* overflow error - should not happen */
522         memset( psz_part, 0, 6 );
523         return NULL;
524     }
525
526     psz_part[len] = (char)i_key;
527
528     psz_utf8 = FromLocaleDup( psz_part );
529
530     /* Ugly check for incomplete bytes sequences
531      * (in case of non-UTF8 multibyte local encoding) */
532     char *psz;
533     for( psz = psz_utf8; *psz; psz++ )
534         if( ( *psz == '?' ) && ( *psz_utf8 != '?' ) )
535         {
536             /* incomplete bytes sequence detected
537              * (VLC core inserted dummy question marks) */
538             free( psz_utf8 );
539             return NULL;
540         }
541
542     /* Check for incomplete UTF8 bytes sequence */
543     if( EnsureUTF8( psz_utf8 ) == NULL )
544     {
545         free( psz_utf8 );
546         return NULL;
547     }
548
549     memset( psz_part, 0, 6 );
550     return psz_utf8;
551 }
552 #endif
553
554 static inline int RemoveLastUTF8Entity( char *psz, int len )
555 {
556     while( len && ( (psz[--len] & 0xc0) == 0x80 ) );
557                        /* UTF8 continuation byte */
558
559     psz[len] = '\0';
560     return len;
561 }
562
563 static int HandleKey( intf_thread_t *p_intf, int i_key )
564 {
565     intf_sys_t *p_sys = p_intf->p_sys;
566     vlc_value_t val;
567     playlist_t *p_playlist = pl_Get( p_intf );
568
569     if( p_sys->i_box_type == BOX_PLAYLIST )
570     {
571         int b_ret = VLC_TRUE;
572         vlc_bool_t b_box_plidx_follow = VLC_FALSE;
573
574         switch( i_key )
575         {
576             vlc_value_t val;
577             /* Playlist Settings */
578             case 'r':
579                 var_Get( p_playlist, "random", &val );
580                 val.b_bool = !val.b_bool;
581                 var_Set( p_playlist, "random", val );
582                 return 1;
583             case 'l':
584                 var_Get( p_playlist, "loop", &val );
585                 val.b_bool = !val.b_bool;
586                 var_Set( p_playlist, "loop", val );
587                 return 1;
588             case 'R':
589                 var_Get( p_playlist, "repeat", &val );
590                 val.b_bool = !val.b_bool;
591                 var_Set( p_playlist, "repeat", val );
592                 return 1;
593
594             /* Playlist sort */
595             case 'o':
596                 playlist_RecursiveNodeSort( p_playlist,
597                                             PlaylistGetRoot( p_intf ),
598                                             SORT_TITLE_NODES_FIRST, ORDER_NORMAL );
599                 p_sys->b_need_update = VLC_TRUE;
600                 return 1;
601             case 'O':
602                 playlist_RecursiveNodeSort( p_playlist,
603                                             PlaylistGetRoot( p_intf ),
604                                             SORT_TITLE_NODES_FIRST, ORDER_REVERSE );
605                 p_sys->b_need_update = VLC_TRUE;
606                 return 1;
607
608             /* Playlist view */
609             case 'v':
610                 switch( p_sys->i_current_view )
611                 {
612                     case VIEW_CATEGORY:
613                         p_sys->i_current_view = VIEW_ONELEVEL;
614                         break;
615                     default:
616                         p_sys->i_current_view = VIEW_CATEGORY;
617                 }
618                 //p_sys->b_need_update = VLC_TRUE;
619                 PlaylistRebuild( p_intf );
620                 return 1;
621
622             /* Playlist navigation */
623             case 'g':
624                 FindIndex( p_intf );
625                 break;
626             case KEY_HOME:
627                 p_sys->i_box_plidx = 0;
628                 break;
629 #ifdef __FreeBSD__
630 /* workaround for FreeBSD + xterm:
631  * see http://www.nabble.com/curses-vs.-xterm-key-mismatch-t3574377.html */
632             case KEY_SELECT:
633 #endif
634             case KEY_END:
635                 p_sys->i_box_plidx = p_playlist->items.i_size - 1;
636                 break;
637             case KEY_UP:
638                 p_sys->i_box_plidx--;
639                 break;
640             case KEY_DOWN:
641                 p_sys->i_box_plidx++;
642                 break;
643             case KEY_PPAGE:
644                 p_sys->i_box_plidx -= p_sys->i_box_lines;
645                 break;
646             case KEY_NPAGE:
647                 p_sys->i_box_plidx += p_sys->i_box_lines;
648                 break;
649             case 'D':
650             case KEY_BACKSPACE:
651             case 0x7f:
652             case KEY_DC:
653             {
654                 playlist_item_t *p_item;
655
656                 PL_LOCK;
657                 p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
658                 if( p_item->i_children == -1 )
659                 {
660                     playlist_DeleteFromInput( p_playlist,
661                                               p_item->p_input->i_id, VLC_TRUE );
662                 }
663                 else
664                 {
665                     playlist_NodeDelete( p_playlist, p_item,
666                                          VLC_TRUE , VLC_FALSE );
667                 }
668                 PL_UNLOCK;
669                 PlaylistRebuild( p_intf );
670                 break;
671             }
672
673             case KEY_ENTER:
674             case '\r':
675             case '\n':
676                 if( !p_sys->pp_plist[p_sys->i_box_plidx] )
677                 {
678                     b_ret = VLC_FALSE;
679                     break;
680                 }
681                 if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
682                         == -1 )
683                 {
684                     playlist_item_t *p_item, *p_parent;
685                     p_item = p_parent =
686                             p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
687
688                     if( !p_parent )
689                         p_parent = p_playlist->p_root_onelevel;
690                     while( p_parent->p_parent )
691                         p_parent = p_parent->p_parent;
692                     playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
693                                       VLC_TRUE, p_parent, p_item );
694                 }
695                 else if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
696                         == 0 )
697                 {   /* We only want to set the current node */
698                     playlist_Stop( p_playlist );
699                     p_sys->p_node = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
700                 }
701                 else
702                 {
703                     p_sys->p_node = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
704                     playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE,
705                         p_sys->pp_plist[p_sys->i_box_plidx]->p_item, NULL );
706                 }
707                 b_box_plidx_follow = VLC_TRUE;
708                 break;
709             default:
710                 b_ret = VLC_FALSE;
711                 break;
712         }
713
714         if( b_ret )
715         {
716             int i_max = p_sys->i_plist_entries;
717             if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
718             if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
719             if( PlaylistIsPlaying( p_intf,
720                     p_sys->pp_plist[p_sys->i_box_plidx]->p_item ) )
721                 b_box_plidx_follow = VLC_TRUE;
722             p_sys->b_box_plidx_follow = b_box_plidx_follow;
723             return 1;
724         }
725     }
726     if( p_sys->i_box_type == BOX_BROWSE )
727     {
728         vlc_bool_t b_ret = VLC_TRUE;
729         /* Browser navigation */
730         switch( i_key )
731         {
732             case KEY_HOME:
733                 p_sys->i_box_bidx = 0;
734                 break;
735 #ifdef __FreeBSD__
736             case KEY_SELECT:
737 #endif
738             case KEY_END:
739                 p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
740                 break;
741             case KEY_UP:
742                 p_sys->i_box_bidx--;
743                 break;
744             case KEY_DOWN:
745                 p_sys->i_box_bidx++;
746                 break;
747             case KEY_PPAGE:
748                 p_sys->i_box_bidx -= p_sys->i_box_lines;
749                 break;
750             case KEY_NPAGE:
751                 p_sys->i_box_bidx += p_sys->i_box_lines;
752                 break;
753             case '.': /* Toggle show hidden files */
754                 p_sys->b_show_hidden_files = ( p_sys->b_show_hidden_files ==
755                     VLC_TRUE ? VLC_FALSE : VLC_TRUE );
756                 ReadDir( p_intf );
757                 break;
758
759             case KEY_ENTER:
760             case '\r':
761             case '\n':
762             case ' ':
763                 if( p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file || i_key == ' ' )
764                 {
765                     int i_size_entry = strlen( "directory://" ) +
766                                        strlen( p_sys->psz_current_dir ) +
767                                        strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
768                     char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
769
770                     sprintf( psz_uri, "directory://%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
771
772                     playlist_item_t *p_parent = p_sys->p_node;
773                     if( !p_parent )
774                     p_parent = p_playlist->status.p_node;
775                     if( !p_parent )
776                         p_parent = p_playlist->p_local_onelevel;
777
778                     while( p_parent->p_parent && p_parent->p_parent->p_parent )
779                         p_parent = p_parent->p_parent;
780
781                     playlist_Add( p_playlist, psz_uri, NULL, PLAYLIST_APPEND,
782                                   PLAYLIST_END,
783                                   p_parent->p_input == 
784                                     p_playlist->p_local_onelevel->p_input
785                                   , VLC_FALSE );
786
787                     p_sys->i_box_type = BOX_PLAYLIST;
788                     free( psz_uri );
789                 }
790                 else
791                 {
792                     int i_size_entry = strlen( p_sys->psz_current_dir ) +
793                                        strlen( p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) + 2;
794                     char *psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
795
796                     sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path );
797
798                     p_sys->psz_current_dir = strdup( psz_uri );
799                     ReadDir( p_intf );
800                     free( psz_uri );
801                 }
802                 break;
803             default:
804                 b_ret = VLC_FALSE;
805                 break;
806         }
807         if( b_ret )
808         {
809             if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
810             if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
811             return 1;
812         }
813     }
814     else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO ||
815              p_sys->i_box_type == BOX_META || p_sys->i_box_type == BOX_STATS ||
816              p_sys->i_box_type == BOX_OBJECTS )
817     {
818         switch( i_key )
819         {
820             case KEY_HOME:
821                 p_sys->i_box_start = 0;
822                 return 1;
823 #ifdef __FreeBSD__
824             case KEY_SELECT:
825 #endif
826             case KEY_END:
827                 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
828                 return 1;
829             case KEY_UP:
830                 if( p_sys->i_box_start > 0 ) p_sys->i_box_start--;
831                 return 1;
832             case KEY_DOWN:
833                 if( p_sys->i_box_start < p_sys->i_box_lines_total - 1 )
834                 {
835                     p_sys->i_box_start++;
836                 }
837                 return 1;
838             case KEY_PPAGE:
839                 p_sys->i_box_start -= p_sys->i_box_lines;
840                 if( p_sys->i_box_start < 0 ) p_sys->i_box_start = 0;
841                 return 1;
842             case KEY_NPAGE:
843                 p_sys->i_box_start += p_sys->i_box_lines;
844                 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
845                 {
846                     p_sys->i_box_start = p_sys->i_box_lines_total - 1;
847                 }
848                 return 1;
849             default:
850                 break;
851         }
852     }
853     else if( p_sys->i_box_type == BOX_NONE )
854     {
855         switch( i_key )
856         {
857             case KEY_HOME:
858                 p_sys->f_slider = 0;
859                 ManageSlider( p_intf );
860                 return 1;
861 #ifdef __FreeBSD__
862             case KEY_SELECT:
863 #endif
864             case KEY_END:
865                 p_sys->f_slider = 99.9;
866                 ManageSlider( p_intf );
867                 return 1;
868             case KEY_UP:
869                 p_sys->f_slider += 5.0;
870                 if( p_sys->f_slider >= 99.0 ) p_sys->f_slider = 99.0;
871                 ManageSlider( p_intf );
872                 return 1;
873             case KEY_DOWN:
874                 p_sys->f_slider -= 5.0;
875                 if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
876                 ManageSlider( p_intf );
877                 return 1;
878
879             default:
880                 break;
881         }
882     }
883     else if( p_sys->i_box_type == BOX_SEARCH && p_sys->psz_search_chain )
884     {
885         int i_chain_len = strlen( p_sys->psz_search_chain );
886         switch( i_key )
887         {
888             case KEY_CLEAR:
889             case 0x0c:      /* ^l */
890                 clear();
891                 return 1;
892             case KEY_ENTER:
893             case '\r':
894             case '\n':
895                 if( i_chain_len > 0 )
896                 {
897                     p_sys->psz_old_search = strdup( p_sys->psz_search_chain );
898                 }
899                 else if( p_sys->psz_old_search )
900                 {
901                     SearchPlaylist( p_intf, p_sys->psz_old_search );
902                 }
903                 p_sys->i_box_type = BOX_PLAYLIST;
904                 return 1;
905             case 0x1b: /* ESC */
906                 /* Alt+key combinations return 2 keys in the terminal keyboard:
907                  * ESC, and the 2nd key.
908                  * If some other key is available immediately (where immediately
909                  * means after wgetch() 1 second delay ), that means that the
910                  * ESC key was not pressed.
911                  *
912                  * man 3X curs_getch says:
913                  *
914                  * Use of the escape key by a programmer for a single
915                  * character function is discouraged, as it will cause a delay 
916                  * of up to one second while the keypad code looks for a
917                  * following function-key sequence.
918                  *
919                  */
920                 if( wgetch( p_sys->w ) != ERR )
921                     return 0;
922                 p_sys->i_box_plidx = p_sys->i_before_search;
923                 p_sys->i_box_type = BOX_PLAYLIST;
924                 return 1;
925             case KEY_BACKSPACE:
926             case 0x7f:
927                 RemoveLastUTF8Entity( p_sys->psz_search_chain, i_chain_len );
928                 break;
929             default:
930             {
931 #ifdef HAVE_NCURSESW
932                 if( i_chain_len + 1 < SEARCH_CHAIN_SIZE )
933                 {
934                     p_sys->psz_search_chain[i_chain_len] = (char) i_key;
935                     p_sys->psz_search_chain[i_chain_len + 1] = '\0';
936                 }
937 #else
938                 char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
939
940                 if( psz_utf8 != NULL )
941                 {
942                     if( i_chain_len + strlen( psz_utf8 ) < SEARCH_CHAIN_SIZE )
943                     {
944                         strcpy( p_sys->psz_search_chain + i_chain_len,
945                                 psz_utf8 );
946                     }
947                     free( psz_utf8 );
948                 }
949 #endif
950                 break;
951             }
952         }
953         free( p_sys->psz_old_search );
954         p_sys->psz_old_search = NULL;
955         SearchPlaylist( p_intf, p_sys->psz_search_chain );
956         return 1;
957     }
958     else if( p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain )
959     {
960         int i_chain_len = strlen( p_sys->psz_open_chain );
961
962         switch( i_key )
963         {
964             case KEY_CLEAR:
965             case 0x0c:          /* ^l */
966                 clear();
967                 return 1;
968             case KEY_ENTER:
969             case '\r': 
970             case '\n':
971                 if( i_chain_len > 0 )
972                 {
973                     playlist_item_t *p_parent = p_sys->p_node;
974                    
975                     if( !p_parent )
976                     p_parent = p_playlist->status.p_node;
977                     if( !p_parent )
978                         p_parent = p_playlist->p_local_onelevel;
979
980                     while( p_parent->p_parent && p_parent->p_parent->p_parent )
981                         p_parent = p_parent->p_parent;
982
983                     playlist_Add( p_playlist, p_sys->psz_open_chain, NULL,
984                                   PLAYLIST_APPEND|PLAYLIST_GO, PLAYLIST_END,
985                                   p_parent->p_input == 
986                                     p_playlist->p_local_onelevel->p_input
987                                   , VLC_FALSE );
988
989                     p_sys->b_box_plidx_follow = VLC_TRUE;
990                 }
991                 p_sys->i_box_type = BOX_PLAYLIST;
992                 return 1;
993             case 0x1b:  /* ESC */
994                 if( wgetch( p_sys->w ) != ERR )
995                     return 0;
996                 p_sys->i_box_type = BOX_PLAYLIST;
997                 return 1;
998             case KEY_BACKSPACE:
999             case 0x7f:
1000                 RemoveLastUTF8Entity( p_sys->psz_open_chain, i_chain_len );
1001                 break;
1002             default:
1003             {
1004 #ifdef HAVE_NCURSESW
1005                 if( i_chain_len + 1 < OPEN_CHAIN_SIZE )
1006                 {
1007                     p_sys->psz_open_chain[i_chain_len] = (char) i_key;
1008                     p_sys->psz_open_chain[i_chain_len + 1] = '\0';
1009                 }
1010 #else
1011                 char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
1012
1013                 if( psz_utf8 != NULL )
1014                 {
1015                     if( i_chain_len + strlen( psz_utf8 ) < OPEN_CHAIN_SIZE )
1016                     {
1017                         strcpy( p_sys->psz_open_chain + i_chain_len,
1018                                 psz_utf8 );
1019                     }
1020                     free( psz_utf8 );
1021                 }
1022 #endif
1023                 break;
1024             }
1025         }
1026         return 1;
1027     }
1028
1029
1030     /* Common keys */
1031     switch( i_key )
1032     {
1033         case 0x1b:  /* ESC */
1034             if( wgetch( p_sys->w ) != ERR )
1035                 return 0;
1036         case 'q':
1037         case 'Q':
1038         case KEY_EXIT:
1039             vlc_object_kill( p_intf->p_libvlc );
1040             return 0;
1041
1042         /* Box switching */
1043         case 'i':
1044             if( p_sys->i_box_type == BOX_INFO )
1045                 p_sys->i_box_type = BOX_NONE;
1046             else
1047                 p_sys->i_box_type = BOX_INFO;
1048             p_sys->i_box_lines_total = 0;
1049             return 1;
1050         case 'm':
1051             if( p_sys->i_box_type == BOX_META )
1052                 p_sys->i_box_type = BOX_NONE;
1053             else
1054                 p_sys->i_box_type = BOX_META;
1055             p_sys->i_box_lines_total = 0;
1056             return 1;
1057         case 'L':
1058             if( p_sys->i_box_type == BOX_LOG )
1059                 p_sys->i_box_type = BOX_NONE;
1060             else
1061                 p_sys->i_box_type = BOX_LOG;
1062             return 1;
1063         case 'P':
1064             if( p_sys->i_box_type == BOX_PLAYLIST )
1065                 p_sys->i_box_type = BOX_NONE;
1066             else
1067                 p_sys->i_box_type = BOX_PLAYLIST;
1068             return 1;
1069         case 'B':
1070             if( p_sys->i_box_type == BOX_BROWSE )
1071                 p_sys->i_box_type = BOX_NONE;
1072             else
1073                 p_sys->i_box_type = BOX_BROWSE;
1074             return 1;
1075         case 'x':
1076             if( p_sys->i_box_type == BOX_OBJECTS )
1077                 p_sys->i_box_type = BOX_NONE;
1078             else
1079                 p_sys->i_box_type = BOX_OBJECTS;
1080             return 1;
1081         case 'S':
1082             if( p_sys->i_box_type == BOX_STATS )
1083                 p_sys->i_box_type = BOX_NONE;
1084             else
1085                 p_sys->i_box_type = BOX_STATS;
1086             return 1;
1087         case 'c':
1088             p_sys->b_color = !p_sys->b_color;
1089             if( p_sys->b_color && !p_sys->b_color_started )
1090                 start_color_and_pairs( p_intf );
1091             return 1;
1092         case 'h':
1093         case 'H':
1094             if( p_sys->i_box_type == BOX_HELP )
1095                 p_sys->i_box_type = BOX_NONE;
1096             else
1097                 p_sys->i_box_type = BOX_HELP;
1098             p_sys->i_box_lines_total = 0;
1099             return 1;
1100         case '/':
1101             if( p_sys->i_box_type != BOX_SEARCH )
1102             {
1103                 if( p_sys->psz_search_chain == NULL )
1104                 {
1105                     return 1;
1106                 }
1107                 p_sys->psz_search_chain[0] = '\0';
1108                 p_sys->b_box_plidx_follow = VLC_FALSE;
1109                 p_sys->i_before_search = p_sys->i_box_plidx;
1110                 p_sys->i_box_type = BOX_SEARCH;
1111             }
1112             return 1;
1113         case 'A': /* Open */
1114             if( p_sys->i_box_type != BOX_OPEN )
1115             {
1116                 if( p_sys->psz_open_chain == NULL )
1117                 {
1118                     return 1;
1119                 }
1120                 p_sys->psz_open_chain[0] = '\0';
1121                 p_sys->i_box_type = BOX_OPEN;
1122             }
1123             return 1;
1124
1125         /* Navigation */
1126         case KEY_RIGHT:
1127             p_sys->f_slider += 1.0;
1128             if( p_sys->f_slider > 99.9 ) p_sys->f_slider = 99.9;
1129             ManageSlider( p_intf );
1130             return 1;
1131
1132         case KEY_LEFT:
1133             p_sys->f_slider -= 1.0;
1134             if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
1135             ManageSlider( p_intf );
1136             return 1;
1137
1138         /* Common control */
1139         case 'f':
1140         {
1141             if( p_intf->p_sys->p_input )
1142             {
1143                 vout_thread_t *p_vout;
1144                 p_vout = vlc_object_find( p_intf->p_sys->p_input,
1145                                           VLC_OBJECT_VOUT, FIND_CHILD );
1146                 if( p_vout )
1147                 {
1148                     var_Get( p_vout, "fullscreen", &val );
1149                     val.b_bool = !val.b_bool;
1150                     var_Set( p_vout, "fullscreen", val );
1151                     vlc_object_release( p_vout );
1152                 }
1153                 else
1154                 {
1155                     var_Get( p_playlist, "fullscreen", &val );
1156                     val.b_bool = !val.b_bool;
1157                     var_Set( p_playlist, "fullscreen", val );
1158                 }
1159             }
1160             return 0;
1161         }
1162
1163         case ' ':
1164             PlayPause( p_intf );
1165             return 1;
1166
1167         case 's':
1168             playlist_Stop( p_playlist );
1169             return 1;
1170
1171         case 'e':
1172             Eject( p_intf );
1173             return 1;
1174
1175         case '[':
1176             if( p_sys->p_input )
1177             {
1178                 val.b_bool = VLC_TRUE;
1179                 var_Set( p_sys->p_input, "prev-title", val );
1180             }
1181             return 1;
1182
1183         case ']':
1184             if( p_sys->p_input )
1185             {
1186                 val.b_bool = VLC_TRUE;
1187                 var_Set( p_sys->p_input, "next-title", val );
1188             }
1189             return 1;
1190
1191         case '<':
1192             if( p_sys->p_input )
1193             {
1194                 val.b_bool = VLC_TRUE;
1195                 var_Set( p_sys->p_input, "prev-chapter", val );
1196             }
1197             return 1;
1198
1199         case '>':
1200             if( p_sys->p_input )
1201             {
1202                 val.b_bool = VLC_TRUE;
1203                 var_Set( p_sys->p_input, "next-chapter", val );
1204             }
1205             return 1;
1206
1207         case 'p':
1208             playlist_Prev( p_playlist );
1209             clear();
1210             return 1;
1211
1212         case 'n':
1213             playlist_Next( p_playlist );
1214             clear();
1215             return 1;
1216
1217         case 'a':
1218             aout_VolumeUp( p_intf, 1, NULL );
1219             clear();
1220             return 1;
1221
1222         case 'z':
1223             aout_VolumeDown( p_intf, 1, NULL );
1224             clear();
1225             return 1;
1226
1227         /*
1228          * ^l should clear and redraw the screen
1229          */
1230         case KEY_CLEAR:
1231         case 0x0c:          /* ^l */
1232             clear();
1233             return 1;
1234
1235         default:
1236             return 0;
1237     }
1238 }
1239
1240 static void ManageSlider( intf_thread_t *p_intf )
1241 {
1242     intf_sys_t     *p_sys = p_intf->p_sys;
1243     input_thread_t *p_input = p_sys->p_input;
1244     vlc_value_t     val;
1245
1246     if( p_input == NULL )
1247     {
1248         return;
1249     }
1250     var_Get( p_input, "state", &val );
1251     if( val.i_int != PLAYING_S )
1252     {
1253         return;
1254     }
1255
1256     var_Get( p_input, "position", &val );
1257     if( p_sys->f_slider == p_sys->f_slider_old )
1258     {
1259         p_sys->f_slider =
1260         p_sys->f_slider_old = 100 * val.f_float;
1261     }
1262     else
1263     {
1264         p_sys->f_slider_old = p_sys->f_slider;
1265
1266         val.f_float = p_sys->f_slider / 100.0;
1267         var_Set( p_input, "position", val );
1268     }
1269 }
1270
1271 static void SearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring )
1272 {
1273     int i_max;
1274     int i_first = 0 ;
1275     int i_item = -1;
1276     intf_sys_t *p_sys = p_intf->p_sys;
1277
1278     if( p_sys->i_before_search >= 0 )
1279     {
1280         i_first = p_sys->i_before_search;
1281     }
1282
1283     if( ( ! psz_searchstring ) ||  strlen( psz_searchstring ) <= 0 )
1284     {
1285         p_sys->i_box_plidx = p_sys->i_before_search;
1286         return;
1287     }
1288
1289     i_max = p_sys->i_plist_entries;
1290
1291     i_item = SubSearchPlaylist( p_intf, psz_searchstring, i_first + 1, i_max );
1292     if( i_item < 0 )
1293     {
1294         i_item = SubSearchPlaylist( p_intf, psz_searchstring, 0, i_first );
1295     }
1296
1297     if( i_item < 0 || i_item >= i_max ) return;
1298
1299     p_sys->i_box_plidx = i_item;
1300 }
1301
1302 static int SubSearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring,
1303                               int i_start, int i_stop )
1304 {
1305     intf_sys_t *p_sys = p_intf->p_sys;
1306     int i, i_item = -1;
1307
1308     for( i = i_start + 1; i < i_stop; i++ )
1309     {
1310         if( strcasestr( p_sys->pp_plist[i]->psz_display,
1311                         psz_searchstring ) != NULL )
1312         {
1313             i_item = i;
1314             break;
1315         }
1316     }
1317
1318     return i_item;
1319 }
1320
1321
1322 static void mvnprintw( int y, int x, int w, const char *p_fmt, ... )
1323 {
1324     va_list  vl_args;
1325     char    *p_buf = NULL;
1326     int      i_len;
1327
1328     va_start( vl_args, p_fmt );
1329     if( vasprintf( &p_buf, p_fmt, vl_args ) == -1 )
1330         return;
1331     va_end( vl_args );
1332
1333     if( ( p_buf == NULL ) || ( w <= 0 ) )
1334         return;
1335
1336     i_len = strlen( p_buf );
1337
1338 #ifdef HAVE_NCURSESW
1339     wchar_t psz_wide[i_len + 1];
1340     
1341     EnsureUTF8( p_buf );
1342     size_t i_char_len = mbstowcs( psz_wide, p_buf, i_len );
1343
1344     size_t i_width; /* number of columns */
1345
1346     if( i_char_len == (size_t)-1 )
1347         /* an invalid character was encountered */
1348         return;
1349     else
1350     {
1351         i_width = wcswidth( psz_wide, i_char_len );
1352         if( i_width == (size_t)-1 )
1353         {
1354             /* a non printable character was encountered */
1355             unsigned int i;
1356             int i_cwidth;
1357             i_width = 0;
1358             for( i = 0 ; i < i_char_len ; i++ )
1359             {
1360                 i_cwidth = wcwidth( psz_wide[i] );
1361                 if( i_cwidth != -1 )
1362                     i_width += i_cwidth;
1363             }
1364         }
1365     }
1366     if( i_width > (size_t)w )
1367     {
1368         int i_total_width = 0;
1369         int i = 0;
1370         while( i_total_width < w )
1371         {
1372             i_total_width += wcwidth( psz_wide[i] );
1373             if( w > 7 && i_total_width >= w/2 )
1374             {
1375                 psz_wide[i  ] = '.';
1376                 psz_wide[i+1] = '.';
1377                 i_total_width -= wcwidth( psz_wide[i] ) - 2;
1378                 if( i > 0 )
1379                 {
1380                     /* we require this check only if at least one character
1381                      * 4 or more columns wide exists (which i doubt) */
1382                     psz_wide[i-1] = '.';
1383                     i_total_width -= wcwidth( psz_wide[i-1] ) - 1;
1384                 }
1385
1386                 /* find the widest string */
1387                 int j, i_2nd_width = 0;
1388                 for( j = i_char_len - 1; i_2nd_width < w - i_total_width; j-- )
1389                     i_2nd_width += wcwidth( psz_wide[j] );
1390
1391                 /* we already have i_total_width columns filled, and we can't
1392                  * have more than w columns */
1393                 if( i_2nd_width > w - i_total_width )
1394                     j++;
1395
1396                 wmemmove( &psz_wide[i+2], &psz_wide[j+1], i_char_len - j - 1 );
1397                 psz_wide[i + 2 + i_char_len - j - 1] = '\0';
1398                 break;
1399             }
1400             i++;
1401         }
1402         if( w <= 7 ) /* we don't add the '...' else we lose too much chars */
1403             psz_wide[i] = '\0';
1404
1405         size_t i_wlen = wcslen( psz_wide ) * 6 + 1; /* worst case */
1406         char psz_ellipsized[i_wlen];
1407         wcstombs( psz_ellipsized, psz_wide, i_wlen );
1408         mvprintw( y, x, "%s", psz_ellipsized );
1409     }
1410     else
1411     {
1412         mvprintw( y, x, "%s", p_buf );
1413         mvhline( y, x + i_width, ' ', w - i_width );
1414     }
1415 #else
1416     if( i_len > w )
1417     {
1418         int i_cut = i_len - w;
1419         int x1 = i_len/2 - i_cut/2;
1420         int x2 = x1 + i_cut;
1421
1422         if( i_len > x2 )
1423         {
1424             memmove( &p_buf[x1], &p_buf[x2], i_len - x2 );
1425         }
1426         p_buf[w] = '\0';
1427         if( w > 7 )
1428         {
1429             p_buf[w/2-1] = '.';
1430             p_buf[w/2  ] = '.';
1431             p_buf[w/2+1] = '.';
1432         }
1433         char *psz_local = ToLocale( p_buf );
1434         mvprintw( y, x, "%s", psz_local );
1435         LocaleFree( p_buf );
1436     }
1437     else
1438     {
1439         char *psz_local = ToLocale( p_buf );
1440         mvprintw( y, x, "%s", psz_local );
1441         LocaleFree( p_buf );
1442         mvhline( y, x + i_len, ' ', w - i_len );
1443     }
1444 #endif
1445 }
1446 static void MainBoxWrite( intf_thread_t *p_intf, int l, int x, const char *p_fmt, ... )
1447 {
1448     intf_sys_t     *p_sys = p_intf->p_sys;
1449
1450     va_list  vl_args;
1451     char    *p_buf = NULL;
1452
1453     if( l < p_sys->i_box_start || l - p_sys->i_box_start >= p_sys->i_box_lines )
1454     {
1455         return;
1456     }
1457
1458     va_start( vl_args, p_fmt );
1459     if( vasprintf( &p_buf, p_fmt, vl_args ) == -1 )
1460         return;
1461     va_end( vl_args );
1462
1463     if( p_buf == NULL )
1464     {
1465         return;
1466     }
1467
1468     mvnprintw( p_sys->i_box_y + l - p_sys->i_box_start, x, COLS - x - 1, "%s", p_buf );
1469 }
1470
1471 static void DumpObject( intf_thread_t *p_intf, int *l, vlc_object_t *p_obj, int i_level )
1472 {
1473     vlc_object_yield( p_obj );
1474
1475     if( p_obj->psz_object_name )
1476         MainBoxWrite( p_intf, (*l)++, 1 + 2 * i_level, "%s \"%s\" (%d)",
1477                 p_obj->psz_object_type, p_obj->psz_object_name,
1478                 p_obj->i_object_id );
1479     else
1480         MainBoxWrite( p_intf, (*l)++, 1 + 2 * i_level, "%s (%d)",
1481                 p_obj->psz_object_type, p_obj->i_object_id );
1482     int i;
1483     for( i = 0; i < p_obj->i_children ; i++ )
1484     {
1485         MainBoxWrite( p_intf, *l, 1 + 2 * i_level, 
1486             i == p_obj->i_children - 1 ? "`-" : "|-" );
1487         DumpObject( p_intf, l, p_obj->pp_children[i], i_level + 1 );
1488     }
1489
1490     vlc_object_release( p_obj );
1491 }
1492
1493 static void Redraw( intf_thread_t *p_intf, time_t *t_last_refresh )
1494 {
1495     intf_sys_t     *p_sys = p_intf->p_sys;
1496     input_thread_t *p_input = p_sys->p_input;
1497     playlist_t     *p_playlist = pl_Get( p_intf );
1498     int y = 0;
1499     int h;
1500     int y_end;
1501
1502     clear();
1503
1504     /* Title */
1505     attrset( A_REVERSE );
1506     int i_len = strlen( "VLC media player "PACKAGE_VERSION );
1507     int mid = ( COLS - i_len ) / 2;
1508     if( mid < 0 )
1509         mid = 0;
1510     int i_size = ( COLS > i_len + 1 ) ? COLS : i_len + 1;
1511     char psz_title[i_size];
1512     memset( psz_title, ' ', mid );
1513     if( p_sys->b_color )
1514         wcolor_set( p_sys->w, C_TITLE, NULL );
1515     snprintf( &psz_title[mid], i_size, "VLC media player "PACKAGE_VERSION );
1516     mvnprintw( y, 0, COLS, "%s", psz_title );
1517     attroff( A_REVERSE );
1518     y += 2;
1519
1520     if( p_sys->b_color )
1521         wcolor_set( p_sys->w, C_STATUS, NULL );
1522
1523     /* Infos */
1524     char *psz_state;
1525     if( asprintf( &psz_state, "%s%s%s",
1526             var_GetBool( p_playlist, "repeat" ) ? _( "[Repeat] " ) : "",
1527             var_GetBool( p_playlist, "random" ) ? _( "[Random] " ) : "",
1528             var_GetBool( p_playlist, "loop" ) ? _( "[Loop]" ) : "" ) == -1 )
1529         psz_state = NULL;
1530
1531     if( p_input && !p_input->b_dead )
1532     {
1533         char buf1[MSTRTIME_MAX_SIZE];
1534         char buf2[MSTRTIME_MAX_SIZE];
1535         vlc_value_t val;
1536         vlc_value_t val_list;
1537
1538         /* Source */
1539         char *psz_uri = input_item_GetURI( input_GetItem( p_input ) );
1540         mvnprintw( y++, 0, COLS, _(" Source   : %s"), psz_uri );
1541         free( psz_uri );
1542
1543         /* State */
1544         var_Get( p_input, "state", &val );
1545         if( val.i_int == PLAYING_S )
1546         {
1547             mvnprintw( y++, 0, COLS, _(" State    : Playing %s"), psz_state );
1548         }
1549         else if( val.i_int == OPENING_S )
1550         {
1551             mvnprintw( y++, 0, COLS, _(" State    : Opening/Connecting %s"), psz_state );
1552         }
1553         else if( val.i_int == BUFFERING_S )
1554         {
1555             mvnprintw( y++, 0, COLS, _(" State    : Buffering %s"), psz_state );
1556         }
1557         else if( val.i_int == PAUSE_S )
1558         {
1559             mvnprintw( y++, 0, COLS, _(" State    : Paused %s"), psz_state );
1560         }
1561
1562         if( val.i_int != INIT_S && val.i_int != END_S )
1563         {
1564             audio_volume_t i_volume;
1565
1566             /* Position */
1567             var_Get( p_input, "time", &val );
1568             msecstotimestr( buf1, val.i_time / 1000 );
1569
1570             var_Get( p_input, "length", &val );
1571             msecstotimestr( buf2, val.i_time / 1000 );
1572
1573             mvnprintw( y++, 0, COLS, _(" Position : %s/%s (%.2f%%)"), buf1, buf2, p_sys->f_slider );
1574
1575             /* Volume */
1576             aout_VolumeGet( p_intf, &i_volume );
1577             mvnprintw( y++, 0, COLS, _(" Volume   : %i%%"), i_volume*200/AOUT_VOLUME_MAX );
1578
1579             /* Title */
1580             if( !var_Get( p_input, "title", &val ) )
1581             {
1582                 var_Change( p_input, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
1583                 if( val_list.p_list->i_count > 0 )
1584                 {
1585                     mvnprintw( y++, 0, COLS, _(" Title    : %d/%d"), val.i_int, val_list.p_list->i_count );
1586                 }
1587                 var_Change( p_input, "title", VLC_VAR_FREELIST, &val_list, NULL );
1588             }
1589
1590             /* Chapter */
1591             if( !var_Get( p_input, "chapter", &val ) )
1592             {
1593                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
1594                 if( val_list.p_list->i_count > 0 )
1595                 {
1596                     mvnprintw( y++, 0, COLS, _(" Chapter  : %d/%d"), val.i_int, val_list.p_list->i_count );
1597                 }
1598                 var_Change( p_input, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
1599             }
1600         }
1601         else
1602         {
1603             y += 2;
1604         }
1605     }
1606     else
1607     {
1608         mvnprintw( y++, 0, COLS, _(" Source: <no current item> %s"), psz_state );
1609         DrawEmptyLine( p_sys->w, y++, 0, COLS );
1610         mvnprintw( y++, 0, COLS, _(" [ h for help ]") );
1611         DrawEmptyLine( p_sys->w, y++, 0, COLS );
1612     }
1613     free( psz_state );
1614     if( p_sys->b_color )
1615         wcolor_set( p_sys->w, C_DEFAULT, NULL );
1616
1617     DrawBox( p_sys->w, y, 0, 3, COLS, "", p_sys->b_color );
1618     DrawEmptyLine( p_sys->w, y+1, 1, COLS-2);
1619     DrawLine( p_sys->w, y+1, 1, (int)(p_intf->p_sys->f_slider/100.0 * (COLS -2)) );
1620     y += 3;
1621
1622     p_sys->i_box_y = y + 1;
1623     p_sys->i_box_lines = LINES - y - 2;
1624
1625     h = LINES - y;
1626     y_end = y + h - 1;
1627
1628     if( p_sys->i_box_type == BOX_HELP )
1629     {
1630         /* Help box */
1631         int l = 0;
1632         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Help "), p_sys->b_color );
1633
1634         if( p_sys->b_color )
1635             wcolor_set( p_sys->w, C_CATEGORY, NULL );
1636         MainBoxWrite( p_intf, l++, 1, _("[Display]") );
1637         if( p_sys->b_color )
1638             wcolor_set( p_sys->w, C_DEFAULT, NULL );
1639         MainBoxWrite( p_intf, l++, 1, _("     h,H         Show/Hide help box") );
1640         MainBoxWrite( p_intf, l++, 1, _("     i           Show/Hide info box") );
1641         MainBoxWrite( p_intf, l++, 1, _("     m           Show/Hide metadata box") );
1642         MainBoxWrite( p_intf, l++, 1, _("     L           Show/Hide messages box") );
1643         MainBoxWrite( p_intf, l++, 1, _("     P           Show/Hide playlist box") );
1644         MainBoxWrite( p_intf, l++, 1, _("     B           Show/Hide filebrowser") );
1645         MainBoxWrite( p_intf, l++, 1, _("     x           Show/Hide objects box") );
1646         MainBoxWrite( p_intf, l++, 1, _("     S           Show/Hide statistics box" ) );
1647         MainBoxWrite( p_intf, l++, 1, _("     c           Switch color on/off") );
1648         MainBoxWrite( p_intf, l++, 1, _("     Esc         Close Add/Search entry") );
1649         MainBoxWrite( p_intf, l++, 1, "" );
1650
1651         if( p_sys->b_color )
1652             wcolor_set( p_sys->w, C_CATEGORY, NULL );
1653         MainBoxWrite( p_intf, l++, 1, _("[Global]") );
1654         if( p_sys->b_color )
1655             wcolor_set( p_sys->w, C_DEFAULT, NULL );
1656         MainBoxWrite( p_intf, l++, 1, _("     q, Q, Esc   Quit") );
1657         MainBoxWrite( p_intf, l++, 1, _("     s           Stop") );
1658         MainBoxWrite( p_intf, l++, 1, _("     <space>     Pause/Play") );
1659         MainBoxWrite( p_intf, l++, 1, _("     f           Toggle Fullscreen") );
1660         MainBoxWrite( p_intf, l++, 1, _("     n, p        Next/Previous playlist item") );
1661         MainBoxWrite( p_intf, l++, 1, _("     [, ]        Next/Previous title") );
1662         MainBoxWrite( p_intf, l++, 1, _("     <, >        Next/Previous chapter") );
1663         MainBoxWrite( p_intf, l++, 1, _("     <right>     Seek +1%%") );
1664         MainBoxWrite( p_intf, l++, 1, _("     <left>      Seek -1%%") );
1665         MainBoxWrite( p_intf, l++, 1, _("     a           Volume Up") );
1666         MainBoxWrite( p_intf, l++, 1, _("     z           Volume Down") );
1667         MainBoxWrite( p_intf, l++, 1, "" );
1668
1669         if( p_sys->b_color )
1670             wcolor_set( p_sys->w, C_CATEGORY, NULL );
1671         MainBoxWrite( p_intf, l++, 1, _("[Playlist]") );
1672         if( p_sys->b_color )
1673             wcolor_set( p_sys->w, C_DEFAULT, NULL );
1674         MainBoxWrite( p_intf, l++, 1, _("     r           Toggle Random playing") );
1675         MainBoxWrite( p_intf, l++, 1, _("     l           Toggle Loop Playlist") );
1676         MainBoxWrite( p_intf, l++, 1, _("     R           Toggle Repeat item") );
1677         MainBoxWrite( p_intf, l++, 1, _("     o           Order Playlist by title") );
1678         MainBoxWrite( p_intf, l++, 1, _("     O           Reverse order Playlist by title") );
1679         MainBoxWrite( p_intf, l++, 1, _("     g           Go to the current playing item") );
1680         MainBoxWrite( p_intf, l++, 1, _("     /           Look for an item") );
1681         MainBoxWrite( p_intf, l++, 1, _("     A           Add an entry") );
1682         MainBoxWrite( p_intf, l++, 1, _("     D, <del>    Delete an entry") );
1683         MainBoxWrite( p_intf, l++, 1, _("     <backspace> Delete an entry") );
1684         MainBoxWrite( p_intf, l++, 1, _("     e           Eject (if stopped)") );
1685         MainBoxWrite( p_intf, l++, 1, "" );
1686
1687         if( p_sys->b_color )
1688             wcolor_set( p_sys->w, C_CATEGORY, NULL );
1689         MainBoxWrite( p_intf, l++, 1, _("[Filebrowser]") );
1690         if( p_sys->b_color )
1691             wcolor_set( p_sys->w, C_DEFAULT, NULL );
1692         MainBoxWrite( p_intf, l++, 1, _("     <enter>     Add the selected file to the playlist") );
1693         MainBoxWrite( p_intf, l++, 1, _("     <space>     Add the selected directory to the playlist") );
1694         MainBoxWrite( p_intf, l++, 1, _("     .           Show/Hide hidden files") );
1695         MainBoxWrite( p_intf, l++, 1, "" );
1696
1697         if( p_sys->b_color )
1698             wcolor_set( p_sys->w, C_CATEGORY, NULL );
1699         MainBoxWrite( p_intf, l++, 1, _("[Boxes]") );
1700         if( p_sys->b_color )
1701             wcolor_set( p_sys->w, C_DEFAULT, NULL );
1702         MainBoxWrite( p_intf, l++, 1, _("     <up>,<down>     Navigate through the box line by line") );
1703         MainBoxWrite( p_intf, l++, 1, _("     <pgup>,<pgdown> Navigate through the box page by page") );
1704         MainBoxWrite( p_intf, l++, 1, "" );
1705
1706         if( p_sys->b_color )
1707             wcolor_set( p_sys->w, C_CATEGORY, NULL );
1708         MainBoxWrite( p_intf, l++, 1, _("[Player]") );
1709         if( p_sys->b_color )
1710             wcolor_set( p_sys->w, C_DEFAULT, NULL );
1711         MainBoxWrite( p_intf, l++, 1, _("     <up>,<down>     Seek +/-5%%") );
1712         MainBoxWrite( p_intf, l++, 1, "" );
1713
1714         if( p_sys->b_color )
1715             wcolor_set( p_sys->w, C_CATEGORY, NULL );
1716         MainBoxWrite( p_intf, l++, 1, _("[Miscellaneous]") );
1717         if( p_sys->b_color )
1718             wcolor_set( p_sys->w, C_DEFAULT, NULL );
1719         MainBoxWrite( p_intf, l++, 1, _("     Ctrl-l          Refresh the screen") );
1720
1721         p_sys->i_box_lines_total = l;
1722         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1723         {
1724             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1725         }
1726
1727         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1728         {
1729             y += l - p_sys->i_box_start;
1730         }
1731         else
1732         {
1733             y += p_sys->i_box_lines;
1734         }
1735     }
1736     else if( p_sys->i_box_type == BOX_INFO )
1737     {
1738         /* Info box */
1739         int l = 0;
1740         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Information "), p_sys->b_color );
1741
1742         if( p_input )
1743         {
1744             int i,j;
1745             vlc_mutex_lock( &input_GetItem(p_input)->lock );
1746             for( i = 0; i < input_GetItem(p_input)->i_categories; i++ )
1747             {
1748                 info_category_t *p_category = input_GetItem(p_input)->pp_categories[i];
1749                 if( y >= y_end ) break;
1750                 if( p_sys->b_color )
1751                     wcolor_set( p_sys->w, C_CATEGORY, NULL );
1752                 MainBoxWrite( p_intf, l++, 1, _("  [%s]"), p_category->psz_name );
1753                 if( p_sys->b_color )
1754                     wcolor_set( p_sys->w, C_DEFAULT, NULL );
1755                 for( j = 0; j < p_category->i_infos; j++ )
1756                 {
1757                     info_t *p_info = p_category->pp_infos[j];
1758                     if( y >= y_end ) break;
1759                     MainBoxWrite( p_intf, l++, 1, _("      %s: %s"), p_info->psz_name, p_info->psz_value );
1760                 }
1761             }
1762             vlc_mutex_unlock( &input_GetItem(p_input)->lock );
1763         }
1764         else
1765         {
1766             MainBoxWrite( p_intf, l++, 1, _("No item currently playing") );
1767         }
1768         p_sys->i_box_lines_total = l;
1769         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1770         {
1771             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1772         }
1773
1774         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1775         {
1776             y += l - p_sys->i_box_start;
1777         }
1778         else
1779         {
1780             y += p_sys->i_box_lines;
1781         }
1782     }
1783     else if( p_sys->i_box_type == BOX_META )
1784     {
1785         /* Meta data box */
1786         int l = 0;
1787
1788         int i_len = strlen( VLC_META_INFO_CAT );
1789         char psz_title[i_len + 3];
1790         psz_title[0] = ' ';
1791         psz_title[1] = '\0';
1792         strcat( &psz_title[1], VLC_META_INFO_CAT );
1793         psz_title[i_len + 1] = ' ';
1794         psz_title[i_len + 2] = '\0';
1795         DrawBox( p_sys->w, y++, 0, h, COLS, psz_title, p_sys->b_color );
1796
1797         if( p_input )
1798         {
1799             int i;
1800             input_item_t *p_item = input_GetItem( p_input );
1801             vlc_mutex_lock( &p_item->lock );
1802             for( i=0; i<VLC_META_TYPE_COUNT; i++ )
1803             {
1804                 if( y >= y_end ) break;
1805                 char *psz_meta = p_item->p_meta->ppsz_meta[i];
1806                 if( psz_meta && *psz_meta )
1807                 {
1808                     const char *psz_meta_title;
1809                     switch( i )
1810                     {
1811                         case 0:
1812                             psz_meta_title = VLC_META_TITLE; break;
1813                         case 1:
1814                             psz_meta_title = VLC_META_ARTIST; break;
1815                         case 2:
1816                             psz_meta_title = VLC_META_GENRE ; break;
1817                         case 3:
1818                             psz_meta_title = VLC_META_COPYRIGHT; break;
1819                         case 4:
1820                             psz_meta_title = VLC_META_COLLECTION; break;
1821                         case 5:
1822                             psz_meta_title = VLC_META_SEQ_NUM; break;
1823                         case 6:
1824                             psz_meta_title = VLC_META_DESCRIPTION; break;
1825                         case 7:
1826                             psz_meta_title = VLC_META_RATING; break;
1827                         case 8:
1828                             psz_meta_title = VLC_META_DATE; break;
1829                         case 9:
1830                             psz_meta_title = VLC_META_SETTING; break;
1831                         case 10:
1832                             psz_meta_title = VLC_META_URL; break;
1833                         case 11:
1834                             psz_meta_title = VLC_META_LANGUAGE; break;
1835                         case 12:
1836                             psz_meta_title = VLC_META_NOW_PLAYING; break;
1837                         case 13:
1838                             psz_meta_title = VLC_META_PUBLISHER; break;
1839                         case 14:
1840                             psz_meta_title = VLC_META_ENCODED_BY; break;
1841                         case 15:
1842                             psz_meta_title = VLC_META_ART_URL; break;
1843                         case 16:
1844                             psz_meta_title = VLC_META_TRACKID; break;
1845                         default:
1846                             psz_meta_title = ""; break;
1847                     }
1848                     if( p_sys->b_color )
1849                         wcolor_set( p_sys->w, C_CATEGORY, NULL );
1850                     MainBoxWrite( p_intf, l++, 1, "  [%s]", psz_meta_title );
1851                     if( p_sys->b_color )
1852                         wcolor_set( p_sys->w, C_DEFAULT, NULL );
1853                     MainBoxWrite( p_intf, l++, 1, "      %s", psz_meta );
1854                 }
1855             }
1856             vlc_mutex_unlock( &p_item->lock );
1857         }
1858         else
1859         {
1860             MainBoxWrite( p_intf, l++, 1, _("No item currently playing") );
1861         }
1862         p_sys->i_box_lines_total = l;
1863         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1864         {
1865             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1866         }
1867
1868         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1869         {
1870             y += l - p_sys->i_box_start;
1871         }
1872         else
1873         {
1874             y += p_sys->i_box_lines;
1875         }
1876     }
1877     else if( p_sys->i_box_type == BOX_LOG )
1878     {
1879         int i_line = 0;
1880         int i_stop;
1881         int i_start;
1882
1883         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Logs "), p_sys->b_color );
1884
1885         i_start = p_intf->p_sys->p_sub->i_start;
1886
1887         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1888         i_stop = *p_intf->p_sys->p_sub->pi_stop;
1889         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1890
1891         for( ;; )
1892         {
1893             static const char *ppsz_type[4] = { "", "error", "warning", "debug" };
1894             if( i_line >= h - 2 )
1895             {
1896                 break;
1897             }
1898             i_stop--;
1899             i_line++;
1900             if( i_stop < 0 ) i_stop += VLC_MSG_QSIZE;
1901             if( i_stop == i_start )
1902             {
1903                 break;
1904             }
1905             if( p_sys->b_color )
1906                 wcolor_set( p_sys->w, 
1907                     p_sys->p_sub->p_msg[i_stop].i_type + C_INFO,
1908                     NULL );
1909             mvnprintw( y + h-2-i_line, 1, COLS - 2, "   [%s] %s",
1910                       ppsz_type[p_sys->p_sub->p_msg[i_stop].i_type],
1911                       p_sys->p_sub->p_msg[i_stop].psz_msg );
1912             if( p_sys->b_color )
1913                 wcolor_set( p_sys->w, C_DEFAULT, NULL );
1914         }
1915
1916         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1917         p_intf->p_sys->p_sub->i_start = i_stop;
1918         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1919         y = y_end;
1920     }
1921     else if( p_sys->i_box_type == BOX_BROWSE )
1922     {
1923         /* Filebrowser box */
1924         int        i_start, i_stop;
1925         int        i_item;
1926         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Browse "), p_sys->b_color );
1927
1928         if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_plidx = p_sys->i_dir_entries - 1;
1929         if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
1930
1931         if( p_sys->i_box_bidx < (h - 2)/2 )
1932         {
1933             i_start = 0;
1934             i_stop = h - 2;
1935         }
1936         else if( p_sys->i_dir_entries - p_sys->i_box_bidx > (h - 2)/2 )
1937         {
1938             i_start = p_sys->i_box_bidx - (h - 2)/2;
1939             i_stop = i_start + h - 2;
1940         }
1941         else
1942         {
1943             i_stop = p_sys->i_dir_entries;
1944             i_start = p_sys->i_dir_entries - (h - 2);
1945         }
1946         if( i_start < 0 )
1947         {
1948             i_start = 0;
1949         }
1950         if( i_stop > p_sys->i_dir_entries )
1951         {
1952             i_stop = p_sys->i_dir_entries;
1953         }
1954
1955         for( i_item = i_start; i_item < i_stop; i_item++ )
1956         {
1957             vlc_bool_t b_selected = ( p_sys->i_box_bidx == i_item );
1958
1959             if( y >= y_end ) break;
1960             if( b_selected )
1961             {
1962                 attrset( A_REVERSE );
1963             }
1964             if( p_sys->b_color && !p_sys->pp_dir_entries[i_item]->b_file )
1965                 wcolor_set( p_sys->w, C_FOLDER, NULL );
1966             mvnprintw( y++, 1, COLS - 2, " %c %s", p_sys->pp_dir_entries[i_item]->b_file == VLC_TRUE ? ' ' : '+',
1967                             p_sys->pp_dir_entries[i_item]->psz_path );
1968             if( p_sys->b_color && !p_sys->pp_dir_entries[i_item]->b_file )
1969                 wcolor_set( p_sys->w, C_DEFAULT, NULL );
1970
1971             if( b_selected )
1972             {
1973                 attroff( A_REVERSE );
1974             }
1975         }
1976
1977     }
1978     else if( p_sys->i_box_type == BOX_OBJECTS )
1979     {
1980         int l = 0;
1981         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Objects "), p_sys->b_color );
1982         DumpObject( p_intf, &l, VLC_OBJECT( p_intf->p_libvlc ), 0 );
1983
1984         p_sys->i_box_lines_total = l;
1985         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
1986             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
1987
1988         if( l - p_sys->i_box_start < p_sys->i_box_lines )
1989             y += l - p_sys->i_box_start;
1990         else
1991             y += p_sys->i_box_lines;
1992     }
1993     else if( p_sys->i_box_type == BOX_STATS )
1994     {
1995         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Stats "), p_sys->b_color );
1996
1997         if( p_input )
1998         {
1999             input_item_t *p_item = input_GetItem( p_input );
2000             assert( p_item );
2001             vlc_mutex_lock( &p_item->lock );
2002             vlc_mutex_lock( &p_item->p_stats->lock );
2003
2004             /* Input */
2005             if( p_sys->b_color ) wcolor_set( p_sys->w, C_CATEGORY, NULL );
2006             mvnprintw( y++, 1, COLS-2, _("+-[Incoming]"));
2007             if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
2008             mvnprintw( y++, 1, COLS-2, _("| input bytes read : %8.0f kB"),
2009                     (float)(p_item->p_stats->i_read_bytes)/1000 );
2010             mvnprintw( y++, 1, COLS-2, _("| input bitrate    :   %6.0f kb/s"),
2011                     (float)(p_item->p_stats->f_input_bitrate)*8000 );
2012             mvnprintw( y++, 1, COLS-2,_("| demux bytes read : %8.0f kB"),
2013                     (float)(p_item->p_stats->i_demux_read_bytes)/1000 );
2014             mvnprintw( y++, 1, COLS-2,_("| demux bitrate    :   %6.0f kb/s"),
2015                     (float)(p_item->p_stats->f_demux_bitrate)*8000 );
2016             mvnprintw( y++, 1, COLS-2,"|");
2017             /* Video */
2018             if( p_sys->b_color ) wcolor_set( p_sys->w, C_CATEGORY, NULL );
2019             mvnprintw( y++, 1, COLS-2,_("+-[Video Decoding]"));
2020             if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
2021             mvnprintw( y++, 1, COLS-2,_("| video decoded    :    %5i"),
2022                     p_item->p_stats->i_decoded_video );
2023             mvnprintw( y++, 1, COLS-2,_("| frames displayed :    %5i"),
2024                     p_item->p_stats->i_displayed_pictures );
2025             mvnprintw( y++, 1, COLS-2,_("| frames lost      :    %5i"),
2026                     p_item->p_stats->i_lost_pictures );
2027             mvnprintw( y++, 1, COLS-2,"|");
2028             /* Audio*/
2029             if( p_sys->b_color ) wcolor_set( p_sys->w, C_CATEGORY, NULL );
2030             mvnprintw( y++, 1, COLS-2,_("+-[Audio Decoding]"));
2031             if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
2032             mvnprintw( y++, 1, COLS-2,_("| audio decoded    :    %5i"),
2033                     p_item->p_stats->i_decoded_audio );
2034             mvnprintw( y++, 1, COLS-2,_("| buffers played   :    %5i"),
2035                     p_item->p_stats->i_played_abuffers );
2036             mvnprintw( y++, 1, COLS-2,_("| buffers lost     :    %5i"),
2037                     p_item->p_stats->i_lost_abuffers );
2038             mvnprintw( y++, 1, COLS-2,"|");
2039             /* Sout */
2040             if( p_sys->b_color ) wcolor_set( p_sys->w, C_CATEGORY, NULL );
2041             mvnprintw( y++, 1, COLS-2,_("+-[Streaming]"));
2042             if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
2043             mvnprintw( y++, 1, COLS-2,_("| packets sent     :    %5i"), p_item->p_stats->i_sent_packets );
2044             mvnprintw( y++, 1, COLS-2,_("| bytes sent       : %8.0f kB"),
2045                     (float)(p_item->p_stats->i_sent_bytes)/1000 );
2046             mvnprintw( y++, 1, COLS-2,_("| sending bitrate  :   %6.0f kb/s"),
2047                     (float)(p_item->p_stats->f_send_bitrate*8)*1000 );
2048             if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
2049
2050             vlc_mutex_unlock( &p_item->p_stats->lock );
2051             vlc_mutex_unlock( &p_item->lock );
2052         }
2053     }
2054     else if( p_sys->i_box_type == BOX_PLAYLIST ||
2055                p_sys->i_box_type == BOX_SEARCH ||
2056                p_sys->i_box_type == BOX_OPEN   )
2057     {
2058         /* Playlist box */
2059         int        i_start, i_stop, i_max = p_sys->i_plist_entries;
2060         int        i_item;
2061         char       *psz_title;
2062
2063         switch( p_sys->i_current_view )
2064         {
2065             case VIEW_ONELEVEL:
2066                 psz_title = strdup( _(" Playlist (All, one level) ") );
2067                 break;
2068             case VIEW_CATEGORY:
2069                 psz_title = strdup( _(" Playlist (By category) ") );
2070                 break;
2071             default:
2072                 psz_title = strdup( _(" Playlist (Manually added) ") );
2073         }
2074
2075         DrawBox( p_sys->w, y++, 0, h, COLS, psz_title, p_sys->b_color );
2076
2077         if( p_sys->b_need_update || p_sys->pp_plist == NULL )
2078         {
2079             PlaylistRebuild( p_intf );
2080         }
2081         if( p_sys->b_box_plidx_follow )
2082         {
2083             FindIndex( p_intf );
2084         }
2085
2086         if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
2087         if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
2088         if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
2089
2090         if( p_sys->i_box_plidx < (h - 2)/2 )
2091         {
2092             i_start = 0;
2093             i_stop = h - 2;
2094         }
2095         else if( i_max - p_sys->i_box_plidx > (h - 2)/2 )
2096         {
2097             i_start = p_sys->i_box_plidx - (h - 2)/2;
2098             i_stop = i_start + h - 2;
2099         }
2100         else
2101         {
2102             i_stop = i_max;
2103             i_start = i_max - (h - 2);
2104         }
2105         if( i_start < 0 )
2106         {
2107             i_start = 0;
2108         }
2109         if( i_stop > i_max )
2110         {
2111             i_stop = i_max;
2112         }
2113
2114         for( i_item = i_start; i_item < i_stop; i_item++ )
2115         {
2116             vlc_bool_t b_selected = ( p_sys->i_box_plidx == i_item );
2117             playlist_item_t *p_item = p_sys->pp_plist[i_item]->p_item;
2118             playlist_item_t *p_node = p_sys->p_node;
2119             int c = ' ';
2120             if( ( p_node && p_item->p_input == p_node->p_input ) ||
2121                         ( !p_node && p_item->p_input ==
2122                         p_playlist->status.p_node->p_input ) )
2123                 c = '*';
2124             else if( p_item == p_node || ( p_item != p_node &&
2125                         PlaylistIsPlaying( p_intf, p_item ) ) )
2126                 c = '>';
2127
2128             if( y >= y_end ) break;
2129             if( b_selected )
2130             {
2131                 attrset( A_REVERSE );
2132             }
2133             if( p_sys->b_color )
2134                 wcolor_set( p_sys->w, i_item % 3 + C_PLAYLIST_1, NULL );
2135             mvnprintw( y++, 1, COLS - 2, "%c%s", c,
2136                        p_sys->pp_plist[i_item]->psz_display );
2137             if( p_sys->b_color )
2138                 wcolor_set( p_sys->w, C_DEFAULT, NULL );
2139             if( b_selected )
2140             {
2141                 attroff( A_REVERSE );
2142             }
2143         }
2144
2145     }
2146     else
2147     {
2148         y++;
2149     }
2150     if( p_sys->i_box_type == BOX_SEARCH )
2151     {
2152         DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
2153         if( p_sys->psz_search_chain )
2154         {
2155             if( strlen( p_sys->psz_search_chain ) == 0 &&
2156                 p_sys->psz_old_search != NULL )
2157             {
2158                 /* Searching next entry */
2159                 mvnprintw( 7, 1, COLS-2, _("Find: %s"), p_sys->psz_old_search );
2160             }
2161             else
2162             {
2163                 mvnprintw( 7, 1, COLS-2, _("Find: %s"), p_sys->psz_search_chain );
2164             }
2165         }
2166     }
2167     if( p_sys->i_box_type == BOX_OPEN )
2168     {
2169         if( p_sys->psz_open_chain )
2170         {
2171             DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
2172             mvnprintw( 7, 1, COLS-2, _("Open: %s"), p_sys->psz_open_chain );
2173         }
2174     }
2175
2176     while( y < y_end )
2177     {
2178         DrawEmptyLine( p_sys->w, y++, 1, COLS - 2 );
2179     }
2180
2181     refresh();
2182
2183     *t_last_refresh = time( 0 );
2184 }
2185
2186 static playlist_item_t *PlaylistGetRoot( intf_thread_t *p_intf )
2187 {
2188     intf_sys_t *p_sys = p_intf->p_sys;
2189     playlist_t *p_playlist = pl_Get( p_intf );
2190
2191     switch( p_sys->i_current_view )
2192     {
2193         case VIEW_CATEGORY:
2194             return p_playlist->p_root_category;
2195         default:
2196             return p_playlist->p_root_onelevel;
2197     }
2198 }
2199
2200 static void PlaylistRebuild( intf_thread_t *p_intf )
2201 {
2202     intf_sys_t *p_sys = p_intf->p_sys;
2203     playlist_t *p_playlist = pl_Get( p_intf );
2204
2205     PL_LOCK;
2206
2207     /* First clear the old one */
2208     PlaylistDestroy( p_intf );
2209
2210     /* Build the new one */
2211     PlaylistAddNode( p_intf, PlaylistGetRoot( p_intf ), 0, "" );
2212
2213     p_sys->b_need_update = VLC_FALSE;
2214
2215     PL_UNLOCK;
2216 }
2217
2218 static void PlaylistAddNode( intf_thread_t *p_intf, playlist_item_t *p_node,
2219                              int i, const char *c )
2220 {
2221     intf_sys_t *p_sys = p_intf->p_sys;
2222     playlist_item_t *p_child;
2223     int k;
2224
2225     for( k = 0; k < p_node->i_children; k++ )
2226     {
2227         char *psz_display;
2228         p_child = p_node->pp_children[k];
2229         char *psz_name = input_item_GetTitle( p_child->p_input );
2230         if( !psz_name || !*psz_name )
2231         {
2232             free( psz_name );
2233             psz_name = input_item_GetName( p_child->p_input );
2234         }
2235
2236         if( c && *c )
2237         {
2238             if( asprintf( &psz_display, "%s%c-%s", c,
2239                     k == p_node->i_children - 1 ?  '`' : '|', psz_name ) == -1 )
2240                 return;
2241         }
2242         else
2243         {
2244             if( asprintf( &psz_display, " %s", psz_name ) == -1 )
2245                 return;
2246         }
2247         free( psz_name );
2248         struct pl_item_t *p_pl_item = malloc( sizeof( struct pl_item_t ) );
2249         if( !p_pl_item )
2250             return;
2251         p_pl_item->psz_display = psz_display;
2252         p_pl_item->p_item = p_child;
2253         INSERT_ELEM( p_sys->pp_plist, p_sys->i_plist_entries,
2254                      p_sys->i_plist_entries, p_pl_item );
2255         i++;
2256
2257         if( p_child->i_children > 0 )
2258         {
2259             char *psz_tmp;
2260             if( asprintf( &psz_tmp, "%s%c ", c,
2261                      k == p_node->i_children - 1 ? ' ' : '|' ) == -1 )
2262                 return; 
2263             PlaylistAddNode( p_intf, p_child, i,
2264                              strlen( c ) ? psz_tmp : " " );
2265             free( psz_tmp );
2266         }
2267     }
2268 }
2269
2270 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
2271                             vlc_value_t oval, vlc_value_t nval, void *param )
2272 {
2273     VLC_UNUSED(p_this); VLC_UNUSED(psz_variable);
2274     VLC_UNUSED(oval); VLC_UNUSED(nval);
2275     intf_thread_t *p_intf = (intf_thread_t *)param;
2276     playlist_t *p_playlist = pl_Get( p_intf );
2277     p_intf->p_sys->b_need_update = VLC_TRUE;
2278     p_intf->p_sys->p_node = p_playlist->status.p_node;
2279     return VLC_SUCCESS;
2280 }
2281
2282 /* Playlist suxx */
2283 static inline vlc_bool_t PlaylistIsPlaying( intf_thread_t *p_intf,
2284                                             playlist_item_t *p_item )
2285 {
2286     playlist_t *p_playlist = pl_Get( p_intf );
2287     playlist_item_t *p_played_item = p_playlist->status.p_item;
2288     return( p_item != NULL && p_played_item != NULL &&
2289             p_item->p_input != NULL && p_played_item->p_input != NULL &&
2290             p_item->p_input->i_id == p_played_item->p_input->i_id );
2291 }
2292
2293 static void FindIndex( intf_thread_t *p_intf )
2294 {
2295     intf_sys_t *p_sys = p_intf->p_sys;
2296     int i;
2297
2298     if( p_sys->i_box_plidx < p_sys->i_plist_entries && p_sys->i_box_plidx >= 0 )
2299     {
2300         playlist_item_t *p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
2301         if( ( p_item->i_children == 0 && p_item == p_sys->p_node ) ||
2302                 PlaylistIsPlaying( p_intf, p_item ) )
2303             return;
2304     }
2305
2306     for( i = 0; i < p_sys->i_plist_entries; i++ )
2307     {
2308         playlist_item_t *p_item = p_sys->pp_plist[i]->p_item;
2309         if( ( p_item->i_children == 0 && p_item == p_sys->p_node ) ||
2310                 PlaylistIsPlaying( p_intf, p_sys->pp_plist[i]->p_item ) )
2311         {
2312             p_sys->i_box_plidx = i;
2313             break;
2314         }
2315     }
2316 }
2317
2318 static void PlaylistDestroy( intf_thread_t *p_intf )
2319 {
2320     intf_sys_t *p_sys = p_intf->p_sys;
2321     int i;
2322
2323     for( i = 0; i < p_sys->i_plist_entries; i++ )
2324     {
2325         struct pl_item_t *p_pl_item = p_sys->pp_plist[i];
2326         free( p_pl_item->psz_display );
2327         REMOVE_ELEM( p_sys->pp_plist, p_sys->i_plist_entries, i );
2328         free( p_pl_item );
2329     }
2330     p_sys->pp_plist = NULL;
2331     p_sys->i_plist_entries = 0;
2332 }
2333
2334 static void Eject( intf_thread_t *p_intf )
2335 {
2336     char *psz_device = NULL, *psz_parser, *psz_name;
2337
2338     /*
2339      * Get the active input
2340      * Determine whether we can eject a media, ie it's a DVD, VCD or CD-DA
2341      * If it's neither of these, then return
2342      */
2343
2344     playlist_t * p_playlist = pl_Get( p_intf );
2345     PL_LOCK;
2346
2347     if( p_playlist->status.p_item == NULL )
2348     {
2349         PL_UNLOCK;
2350         return;
2351     }
2352
2353     psz_name = p_playlist->status.p_item->p_input->psz_name;
2354
2355     if( psz_name )
2356     {
2357         if( !strncmp(psz_name, "dvd://", 4) )
2358         {
2359             switch( psz_name[strlen("dvd://")] )
2360             {
2361             case '\0':
2362             case '@':
2363                 psz_device = config_GetPsz( p_intf, "dvd" );
2364                 break;
2365             default:
2366                 /* Omit the first MRL-selector characters */
2367                 psz_device = strdup( psz_name + strlen("dvd://" ) );
2368                 break;
2369             }
2370         }
2371         else if( !strncmp(psz_name, VCD_MRL, strlen(VCD_MRL)) )
2372         {
2373             switch( psz_name[strlen(VCD_MRL)] )
2374             {
2375             case '\0':
2376             case '@':
2377                 psz_device = config_GetPsz( p_intf, VCD_MRL );
2378                 break;
2379             default:
2380                 /* Omit the beginning MRL-selector characters */
2381                 psz_device = strdup( psz_name + strlen(VCD_MRL) );
2382                 break;
2383             }
2384         }
2385         else if( !strncmp(psz_name, CDDA_MRL, strlen(CDDA_MRL) ) )
2386         {
2387             switch( psz_name[strlen(CDDA_MRL)] )
2388             {
2389             case '\0':
2390             case '@':
2391                 psz_device = config_GetPsz( p_intf, "cd-audio" );
2392                 break;
2393             default:
2394                 /* Omit the beginning MRL-selector characters */
2395                 psz_device = strdup( psz_name + strlen(CDDA_MRL) );
2396                 break;
2397             }
2398         }
2399         else
2400         {
2401             psz_device = strdup( psz_name );
2402         }
2403     }
2404
2405     PL_UNLOCK;
2406
2407     if( psz_device == NULL )
2408     {
2409         return;
2410     }
2411
2412     /* Remove what we have after @ */
2413     psz_parser = psz_device;
2414     for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
2415     {
2416         if( *psz_parser == '@' )
2417         {
2418             *psz_parser = '\0';
2419             break;
2420         }
2421     }
2422
2423     /* If there's a stream playing, we aren't allowed to eject ! */
2424     if( p_intf->p_sys->p_input == NULL )
2425     {
2426         msg_Dbg( p_intf, "ejecting %s", psz_device );
2427
2428         intf_Eject( p_intf, psz_device );
2429     }
2430
2431     free( psz_device );
2432     return;
2433 }
2434
2435 static int comp_dir_entries( const void *pp_dir_entry1,
2436                              const void *pp_dir_entry2 )
2437 {
2438     struct dir_entry_t *p_dir_entry1 = *(struct dir_entry_t**)pp_dir_entry1;
2439     struct dir_entry_t *p_dir_entry2 = *(struct dir_entry_t**)pp_dir_entry2;
2440     if ( p_dir_entry1->b_file == p_dir_entry2->b_file ) {
2441         return strcasecmp( p_dir_entry1->psz_path, p_dir_entry2->psz_path );
2442     }
2443     else
2444     {
2445         return ( p_dir_entry1->b_file ? 1 : -1 );
2446     }
2447 }
2448
2449 static void ReadDir( intf_thread_t *p_intf )
2450 {
2451     intf_sys_t *p_sys = p_intf->p_sys;
2452     DIR *p_current_dir;
2453     int i;
2454
2455     if( p_sys->psz_current_dir && *p_sys->psz_current_dir )
2456     {
2457         char *psz_entry;
2458
2459         /* Open the dir */
2460         p_current_dir = utf8_opendir( p_sys->psz_current_dir );
2461
2462         if( p_current_dir == NULL )
2463         {
2464             /* something went bad, get out of here ! */
2465             msg_Warn( p_intf, "cannot open directory `%s' (%m)",
2466                       p_sys->psz_current_dir );
2467             return;
2468         }
2469
2470         /* Clean the old shit */
2471         for( i = 0; i < p_sys->i_dir_entries; i++ )
2472         {
2473             struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
2474             free( p_dir_entry->psz_path );
2475             REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
2476             free( p_dir_entry );
2477         }
2478         p_sys->pp_dir_entries = NULL;
2479         p_sys->i_dir_entries = 0;
2480
2481         /* while we still have entries in the directory */
2482         while( ( psz_entry = utf8_readdir( p_current_dir ) ) != NULL )
2483         {
2484 #if defined( S_ISDIR )
2485             struct stat stat_data;
2486 #endif
2487             struct dir_entry_t *p_dir_entry;
2488             int i_size_entry = strlen( p_sys->psz_current_dir ) +
2489                                strlen( psz_entry ) + 2;
2490             char *psz_uri;
2491
2492             if( p_sys->b_show_hidden_files == VLC_FALSE &&
2493                 ( strlen( psz_entry ) && psz_entry[0] == '.' ) &&
2494                 strcmp( psz_entry, ".." ) )
2495             {
2496                 free( psz_entry );
2497                 continue;
2498             }
2499
2500             psz_uri = (char *)malloc( sizeof(char)*i_size_entry);
2501             sprintf( psz_uri, "%s/%s", p_sys->psz_current_dir, psz_entry );
2502
2503             if( !( p_dir_entry = malloc( sizeof( struct dir_entry_t) ) ) )
2504             {
2505                 free( psz_uri );
2506                 free( psz_entry );
2507                 continue;
2508             }
2509
2510 #if defined( S_ISDIR )
2511             if( !utf8_stat( psz_uri, &stat_data )
2512              && S_ISDIR(stat_data.st_mode) )
2513 /*#elif defined( DT_DIR )
2514             if( p_dir_content->d_type & DT_DIR )*/
2515 #else
2516             if( 0 )
2517 #endif
2518             {
2519                 p_dir_entry->psz_path = strdup( psz_entry );
2520                 p_dir_entry->b_file = VLC_FALSE;
2521                 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2522                      p_sys->i_dir_entries, p_dir_entry );
2523             }
2524             else
2525             {
2526                 p_dir_entry->psz_path = strdup( psz_entry );
2527                 p_dir_entry->b_file = VLC_TRUE;
2528                 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2529                      p_sys->i_dir_entries, p_dir_entry );
2530             }
2531
2532             free( psz_uri );
2533             free( psz_entry );
2534         }
2535
2536         /* Sort */
2537         qsort( p_sys->pp_dir_entries, p_sys->i_dir_entries,
2538                sizeof(struct dir_entry_t*), &comp_dir_entries );
2539
2540         closedir( p_current_dir );
2541         return;
2542     }
2543     else
2544     {
2545         msg_Dbg( p_intf, "no current dir set" );
2546         return;
2547     }
2548 }
2549
2550 static void PlayPause( intf_thread_t *p_intf )
2551 {
2552     input_thread_t *p_input = p_intf->p_sys->p_input;
2553     playlist_t *p_playlist = pl_Get( p_intf );
2554     vlc_value_t val;
2555
2556     if( p_input )
2557     {
2558         var_Get( p_input, "state", &val );
2559         if( val.i_int != PAUSE_S )
2560         {
2561             val.i_int = PAUSE_S;
2562         }
2563         else
2564         {
2565             val.i_int = PLAYING_S;
2566         }
2567         var_Set( p_input, "state", val );
2568     }
2569     else
2570         playlist_Play( p_playlist );
2571 }
2572
2573 /****************************************************************************
2574  *
2575  ****************************************************************************/
2576 static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title, vlc_bool_t b_color )
2577 {
2578     int i;
2579     int i_len;
2580
2581     if( w > 3 && h > 2 )
2582     {
2583         if( b_color )
2584             wcolor_set( win, C_BOX, NULL );
2585         if( title == NULL ) title = "";
2586         i_len = strlen( title );
2587
2588         if( i_len > w - 2 ) i_len = w - 2;
2589
2590         mvwaddch( win, y, x,    ACS_ULCORNER );
2591         mvwhline( win, y, x+1,  ACS_HLINE, ( w-i_len-2)/2 );
2592         mvwprintw( win,y, x+1+(w-i_len-2)/2, "%s", title );
2593         mvwhline( win, y, x+(w-i_len)/2+i_len,  ACS_HLINE, w - 1 - ((w-i_len)/2+i_len) );
2594         mvwaddch( win, y, x+w-1,ACS_URCORNER );
2595
2596         for( i = 0; i < h-2; i++ )
2597         {
2598             mvwaddch( win, y+i+1, x,     ACS_VLINE );
2599             mvwaddch( win, y+i+1, x+w-1, ACS_VLINE );
2600         }
2601
2602         mvwaddch( win, y+h-1, x,     ACS_LLCORNER );
2603         mvwhline( win, y+h-1, x+1,   ACS_HLINE, w - 2 );
2604         mvwaddch( win, y+h-1, x+w-1, ACS_LRCORNER );
2605         if( b_color )
2606             wcolor_set( win, C_DEFAULT, NULL );
2607     }
2608 }
2609
2610 static void DrawEmptyLine( WINDOW *win, int y, int x, int w )
2611 {
2612     if( w > 0 )
2613     {
2614         mvwhline( win, y, x, ' ', w );
2615     }
2616 }
2617
2618 static void DrawLine( WINDOW *win, int y, int x, int w )
2619 {
2620     if( w > 0 )
2621     {
2622         attrset( A_REVERSE );
2623         mvwhline( win, y, x, ' ', w );
2624         attroff( A_REVERSE );
2625     }
2626 }