]> git.sesse.net Git - vlc/blob - modules/gui/ncurses/ncurses.c
* mtime_t != int This was causing incorrect results on big endian.
[vlc] / modules / gui / ncurses / ncurses.c
1 /*****************************************************************************
2  * ncurses.c : NCurses plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN
5  * $Id: ncurses.c,v 1.8 2004/01/10 03:36:03 hartman Exp $
6  *
7  * Authors: Sam Hocevar <sam@zoy.org>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdio.h>
32 #include <time.h>
33
34 #include <curses.h>
35
36 #include <vlc/vlc.h>
37 #include <vlc/intf.h>
38 #include <vlc/vout.h>
39
40 #ifdef HAVE_CDDAX
41 #define CDDA_MRL "cddax://"
42 #else
43 #define CDDA_MRL "cdda://"
44 #endif
45
46 #ifdef HAVE_VCDX
47 #define VCD_MRL "vcdx://"
48 #else
49 #define VCD_MRL "vcdx://"
50 #endif
51
52 /*****************************************************************************
53  * Local prototypes.
54  *****************************************************************************/
55 static int  Open           ( vlc_object_t * );
56 static void Close          ( vlc_object_t * );
57
58 static void Run            ( intf_thread_t * );
59 static void PlayPause      ( intf_thread_t * );
60 static void Eject          ( intf_thread_t * );
61
62 static int  HandleKey      ( intf_thread_t *, int );
63 static void Redraw           ( intf_thread_t *, time_t * );
64 static void ManageSlider   ( intf_thread_t * );
65
66 /*****************************************************************************
67  * Module descriptor
68  *****************************************************************************/
69 vlc_module_begin();
70     set_description( _("ncurses interface") );
71     set_capability( "interface", 10 );
72     set_callbacks( Open, Close );
73     add_shortcut( "curses" );
74 vlc_module_end();
75
76 /*****************************************************************************
77  * intf_sys_t: description and status of ncurses interface
78  *****************************************************************************/
79 enum
80 {
81     BOX_NONE,
82     BOX_HELP,
83     BOX_INFO,
84     BOX_LOG,
85     BOX_PLAYLIST
86 };
87 struct intf_sys_t
88 {
89     playlist_t     *p_playlist;
90     input_thread_t *p_input;
91
92     float           f_slider;
93     float           f_slider_old;
94
95     WINDOW          *w;
96
97     int             i_box_type;
98     int             i_box_y;
99     int             i_box_lines;
100     int             i_box_lines_total;
101     int             i_box_start;
102
103     int             b_box_cleared;
104
105     msg_subscription_t* p_sub;                  /* message bank subscription */
106 };
107
108 static void DrawBox( WINDOW *win, int y, int x, int h, int w, char *title );
109 static void DrawLine( WINDOW *win, int y, int x, int w );
110 static void DrawEmptyLine( WINDOW *win, int y, int x, int w );
111
112 /*****************************************************************************
113  * Open: initialize and create window
114  *****************************************************************************/
115 static int Open( vlc_object_t *p_this )
116 {
117     intf_thread_t *p_intf = (intf_thread_t *)p_this;
118     intf_sys_t    *p_sys;
119
120     /* Allocate instance and initialize some members */
121     p_sys = p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
122     p_sys->p_playlist = NULL;
123     p_sys->p_input = NULL;
124     p_sys->f_slider = 0.0;
125     p_sys->f_slider_old = 0.0;
126     p_sys->i_box_type = BOX_PLAYLIST;
127     p_sys->i_box_lines = 0;
128     p_sys->i_box_start= 0;
129     p_sys->i_box_lines_total = 0;
130     p_sys->b_box_cleared = VLC_FALSE;
131     p_sys->p_sub = msg_Subscribe( p_intf );
132
133     /* Initialize the curses library */
134     p_sys->w = initscr();
135     keypad( p_sys->w, TRUE );
136     /* Don't do NL -> CR/NL */
137     nonl();
138     /* Take input chars one at a time */
139     cbreak();
140     /* Don't echo */
141     noecho();
142
143     curs_set(0);
144     timeout(0);
145
146     clear();
147
148     /* exported function */
149     p_intf->pf_run = Run;
150
151     return VLC_SUCCESS;
152 }
153
154 /*****************************************************************************
155  * Close: destroy interface window
156  *****************************************************************************/
157 static void Close( vlc_object_t *p_this )
158 {
159     intf_thread_t *p_intf = (intf_thread_t *)p_this;
160     intf_sys_t    *p_sys = p_intf->p_sys;
161
162     if( p_sys->p_input )
163     {
164         vlc_object_release( p_sys->p_input );
165     }
166     if( p_sys->p_playlist )
167     {
168         vlc_object_release( p_sys->p_playlist );
169     }
170
171     /* Close the ncurses interface */
172     endwin();
173
174     msg_Unsubscribe( p_intf, p_sys->p_sub );
175
176     /* Destroy structure */
177     free( p_sys );
178 }
179
180 /*****************************************************************************
181  * Run: ncurses thread
182  *****************************************************************************/
183 static void Run( intf_thread_t *p_intf )
184 {
185     intf_sys_t    *p_sys = p_intf->p_sys;
186
187     int i_key;
188     time_t t_last_refresh;
189
190     /*
191      * force drawing the interface for the first time
192      */
193     t_last_refresh = ( time( 0 ) - 1);
194
195     while( !p_intf->b_die )
196     {
197         msleep( INTF_IDLE_SLEEP );
198
199         /* Update the input */
200         if( p_sys->p_playlist == NULL )
201         {
202             p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
203         }
204         if( p_sys->p_input == NULL )
205         {
206             if( p_sys->p_playlist )
207             {
208                 p_sys->p_input = vlc_object_find( p_sys->p_playlist,
209                                                   VLC_OBJECT_INPUT,FIND_CHILD );
210             }
211             else
212             {
213                 p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
214                                                    FIND_ANYWHERE );
215             }
216             p_sys->b_box_cleared = VLC_FALSE;
217         }
218         else if( p_sys->p_input->b_dead )
219         {
220             vlc_object_release( p_sys->p_input );
221             p_sys->p_input = NULL;
222             p_sys->f_slider = p_sys->f_slider_old = 0.0;
223             p_sys->b_box_cleared = VLC_FALSE;
224         }
225
226         while( ( i_key = getch()) != -1 )
227         {
228             /*
229              * HandleKey returns 1 if the screen needs to be redrawn
230              */
231             if ( HandleKey( p_intf, i_key ) )
232             {
233                 Redraw( p_intf, &t_last_refresh );
234             }
235         }
236         /* Hack */
237         if( p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared )
238         {
239             clear();
240             Redraw( p_intf, &t_last_refresh );
241             p_sys->b_box_cleared = VLC_TRUE;
242         }
243
244         /*
245          * redraw the screen every second
246          */
247         if ( (time(0) - t_last_refresh) >= 1 )
248         {
249             ManageSlider ( p_intf );
250             Redraw( p_intf, &t_last_refresh );
251         }
252     }
253 }
254
255 /* following functions are local */
256
257 static int HandleKey( intf_thread_t *p_intf, int i_key )
258 {
259     intf_sys_t *p_sys = p_intf->p_sys;
260     vlc_value_t val;
261
262     if( p_sys->i_box_type == BOX_PLAYLIST && p_sys->p_playlist )
263     {
264         switch( i_key )
265         {
266             /* Playlist sort */
267             case 'r':
268                 playlist_Sort( p_sys->p_playlist, SORT_RANDOM, ORDER_NORMAL );
269                 return 1;
270             case 'o':
271                 playlist_Sort( p_sys->p_playlist, SORT_TITLE, ORDER_NORMAL );
272                 return 1;
273             case 'O':
274                 playlist_Sort( p_sys->p_playlist, SORT_TITLE, ORDER_REVERSE );
275                 return 1;
276
277             /* Playlist navigation */
278             case KEY_HOME:
279                 playlist_Goto( p_sys->p_playlist, 0 );
280                 return 1;
281             case KEY_END:
282                 playlist_Goto( p_sys->p_playlist, p_sys->p_playlist->i_size - 1 );
283                 return 1;
284             case KEY_UP:
285                 playlist_Prev( p_sys->p_playlist );
286                 return 1;
287             case KEY_DOWN:
288                 playlist_Next( p_sys->p_playlist );
289                 return 1;
290             case KEY_PPAGE:
291             {
292                 int i_item = p_sys->p_playlist->i_index - p_sys->i_box_lines;
293                 if( i_item < 0 ) i_item = 0;
294                 playlist_Goto( p_sys->p_playlist, i_item );
295                 return 1;
296             }
297             case KEY_NPAGE:
298             {
299                 int i_item = p_sys->p_playlist->i_index + p_sys->i_box_lines;
300                 if( i_item >= p_sys->p_playlist->i_size ) i_item = p_sys->p_playlist->i_size - 1;
301                 if( i_item > 0 )
302                 {
303                     playlist_Goto( p_sys->p_playlist, i_item );
304                 }
305                 return 1;
306             }
307             case KEY_BACKSPACE:
308                 playlist_Delete( p_sys->p_playlist, p_sys->p_playlist->i_index );
309                 return 1;
310
311             case KEY_DC:
312             {
313                 int i_item = p_sys->p_playlist->i_index;
314                 playlist_Delete( p_sys->p_playlist, p_sys->p_playlist->i_index );
315                 if( i_item < p_sys->p_playlist->i_size )
316                 {
317                     playlist_Goto( p_sys->p_playlist, i_item );
318                 }
319                 return 1;
320             }
321
322             default:
323                 break;
324         }
325     }
326     else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO )
327     {
328         switch( i_key )
329         {
330             case KEY_HOME:
331                 p_sys->i_box_start = 0;
332                 return 1;
333             case KEY_END:
334                 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
335                 return 1;
336             case KEY_UP:
337                 if( p_sys->i_box_start > 0 ) p_sys->i_box_start--;
338                 return 1;
339             case KEY_DOWN:
340                 if( p_sys->i_box_start < p_sys->i_box_lines_total - 1 )
341                 {
342                     p_sys->i_box_start++;
343                 }
344                 return 1;
345             case KEY_PPAGE:
346                 p_sys->i_box_start -= p_sys->i_box_lines;
347                 if( p_sys->i_box_start < 0 ) p_sys->i_box_start = 0;
348                 return 1;
349             case KEY_NPAGE:
350                 p_sys->i_box_start += p_sys->i_box_lines;
351                 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
352                 {
353                     p_sys->i_box_start = p_sys->i_box_lines_total - 1;
354                 }
355                 return 1;
356             default:
357                 break;
358         }
359     }
360     else if( p_sys->i_box_type == BOX_NONE )
361     {
362         switch( i_key )
363         {
364             case KEY_HOME:
365                 p_sys->f_slider = 0;
366                 ManageSlider ( p_intf );
367                 return 1;
368             case KEY_END:
369                 p_sys->f_slider = 99.9;
370                 ManageSlider ( p_intf );
371                 return 1;
372             case KEY_UP:
373                 p_sys->f_slider += 20.0;
374                 if( p_sys->f_slider >= 99.0 ) p_sys->f_slider = 99.0;
375                 ManageSlider ( p_intf );
376                 return 1;
377             case KEY_DOWN:
378                 p_sys->f_slider -= 20.0;
379                 if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
380                 ManageSlider ( p_intf );
381                 return 1;
382
383             default:
384                 break;
385         }
386     }
387
388     /* Common keys */
389     switch( i_key )
390     {
391         case 'q':
392         case 'Q':
393         case 0x1b:  /* Esc */
394             p_intf->b_die = 1;
395             return 0;
396
397         /* Box switching */
398         case 'i':
399             if( p_sys->i_box_type == BOX_INFO )
400                 p_sys->i_box_type = BOX_NONE;
401             else
402                 p_sys->i_box_type = BOX_INFO;
403             p_sys->i_box_lines_total = 0;
404             return 1;
405         case 'l':
406             if( p_sys->i_box_type == BOX_LOG )
407                 p_sys->i_box_type = BOX_NONE;
408             else
409                 p_sys->i_box_type = BOX_LOG;
410             return 1;
411         case 'P':
412             if( p_sys->i_box_type == BOX_PLAYLIST )
413                 p_sys->i_box_type = BOX_NONE;
414             else
415                 p_sys->i_box_type = BOX_PLAYLIST;
416             return 1;
417         case 'h':
418         case 'H':
419             if( p_sys->i_box_type == BOX_HELP )
420                 p_sys->i_box_type = BOX_NONE;
421             else
422                 p_sys->i_box_type = BOX_HELP;
423             p_sys->i_box_lines_total = 0;
424             return 1;
425
426         /* Navigation */
427         case KEY_RIGHT:
428             p_sys->f_slider += 5.0;
429             if( p_sys->f_slider > 99.9 ) p_sys->f_slider = 99.9;
430             ManageSlider ( p_intf );
431             return 1;
432
433         case KEY_LEFT:
434             p_sys->f_slider -= 5.0;
435             if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
436             ManageSlider ( p_intf );
437             return 1;
438
439         /* Common control */
440         case 'f':
441         {
442             vout_thread_t *p_vout;
443             p_vout = vlc_object_find( p_intf->p_sys->p_input,
444                                       VLC_OBJECT_VOUT, FIND_CHILD );
445             if( p_vout )
446             {
447                 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
448                 vlc_object_release( p_vout );
449             }
450             return 0;
451         }
452
453         case ' ':
454             PlayPause( p_intf );
455             return 1;
456
457         case 's':
458             if( p_intf->p_sys->p_playlist )
459             {
460                 playlist_Stop( p_intf->p_sys->p_playlist );
461             }
462             return 1;
463
464         case 'e':
465             Eject( p_intf );
466             return 1;
467
468         case '[':
469             if( p_sys->p_input )
470             {
471                 val.b_bool = VLC_TRUE;
472                 var_Set( p_sys->p_input, "prev-title", val );
473             }
474             break;
475
476         case ']':
477             if( p_sys->p_input )
478             {
479                 val.b_bool = VLC_TRUE;
480                 var_Set( p_sys->p_input, "next-title", val );
481             }
482             break;
483
484         case '<':
485             if( p_sys->p_input )
486             {
487                 val.b_bool = VLC_TRUE;
488                 var_Set( p_sys->p_input, "prev-chapter", val );
489             }
490             break;
491
492         case '>':
493             if( p_sys->p_input )
494             {
495                 val.b_bool = VLC_TRUE;
496                 var_Set( p_sys->p_input, "next-chapter", val );
497             }
498             break;
499
500         case 'p':
501             if( p_intf->p_sys->p_playlist )
502             {
503                 playlist_Prev( p_intf->p_sys->p_playlist );
504             }
505             clear();
506             return 1;
507         case 'n':
508             if( p_intf->p_sys->p_playlist )
509             {
510                 playlist_Next( p_intf->p_sys->p_playlist );
511             }
512             clear();
513             return 1;
514
515         /*
516          * ^l should clear and redraw the screen
517          */
518         case 0x0c:
519             clear();
520             return 1;
521
522         default:
523             return 0;
524     }
525 }
526
527 static void ManageSlider ( intf_thread_t *p_intf )
528 {
529     intf_sys_t     *p_sys = p_intf->p_sys;
530     input_thread_t *p_input = p_sys->p_input;
531     vlc_value_t     val;
532
533     if( p_input == NULL )
534     {
535         return;
536     }
537     var_Get( p_input, "state", &val );
538     if( val.i_int != PLAYING_S )
539     {
540         return;
541     }
542
543     var_Get( p_input, "position", &val );
544     if( p_sys->f_slider == p_sys->f_slider_old )
545     {
546         p_sys->f_slider =
547         p_sys->f_slider_old = 100 * val.f_float;
548     }
549     else
550     {
551         p_sys->f_slider_old = p_sys->f_slider;
552
553         val.f_float = p_sys->f_slider / 100.0;
554         var_Set( p_input, "position", val );
555     }
556 }
557
558 static void mvnprintw( int y, int x, int w, const char *p_fmt, ... )
559 {
560     va_list  vl_args;
561     char    *p_buf = NULL;
562     int      i_len;
563
564     va_start ( vl_args, p_fmt );
565     vasprintf ( &p_buf, p_fmt, vl_args );
566     va_end ( vl_args );
567
568     if ( p_buf == NULL )
569     {
570         return;
571     }
572     if(  w > 0 )
573     {
574         if( ( i_len = strlen( p_buf ) ) > w )
575         {
576             int i_cut = i_len - w;
577             int x1 = i_len/2 - i_cut/2;
578             int x2 = x1 + i_cut;
579
580             if( i_len > x2 )
581             {
582                 memmove( &p_buf[x1], &p_buf[x2], i_len - x2 );
583             }
584             p_buf[w] = '\0';
585             if( w > 7 )
586             {
587                 p_buf[w/2-1] = '.';
588                 p_buf[w/2  ] = '.';
589                 p_buf[w/2+1] = '.';
590             }
591             mvprintw( y, x, "%s", p_buf );
592         }
593         else
594         {
595             mvprintw( y, x, "%s", p_buf );
596             mvhline( y, x + i_len, ' ', w - i_len );
597         }
598     }
599 }
600 static void MainBoxWrite( intf_thread_t *p_intf, int l, int x, const char *p_fmt, ... )
601 {
602     intf_sys_t     *p_sys = p_intf->p_sys;
603
604     va_list  vl_args;
605     char    *p_buf = NULL;
606
607     if( l < p_sys->i_box_start || l - p_sys->i_box_start >= p_sys->i_box_lines )
608     {
609         return;
610     }
611
612     va_start ( vl_args, p_fmt );
613     vasprintf ( &p_buf, p_fmt, vl_args );
614     va_end ( vl_args );
615
616     if( p_buf == NULL )
617     {
618         return;
619     }
620
621     mvnprintw( p_sys->i_box_y + l - p_sys->i_box_start, x, COLS - x - 1, "%s", p_buf );
622 }
623
624 static void Redraw ( intf_thread_t *p_intf, time_t *t_last_refresh )
625 {
626     intf_sys_t     *p_sys = p_intf->p_sys;
627     input_thread_t *p_input = p_sys->p_input;
628     int y = 0;
629     int h;
630     int y_end;
631
632     //clear();
633
634     /* Title */
635     attrset ( A_REVERSE );
636     mvnprintw( y, 0, COLS, VOUT_TITLE " (ncurses interface) [ h for help ]" );
637     attroff ( A_REVERSE );
638     y += 2;
639
640     /* Infos */
641     if( p_input && !p_input->b_dead )
642     {
643         char buf1[MSTRTIME_MAX_SIZE];
644         char buf2[MSTRTIME_MAX_SIZE];
645         vlc_value_t val;
646         vlc_value_t val_list;
647
648         /* Source */
649         mvnprintw( y++, 0, COLS, " Source   : %s", p_input->psz_source );
650
651         /* State */
652         var_Get( p_input, "state", &val );
653         if( val.i_int == PLAYING_S )
654         {
655             mvnprintw( y++, 0, COLS, " State    : Playing" );
656         }
657         else if( val.i_int == PAUSE_S )
658         {
659             mvnprintw( y++, 0, COLS, " State    : Paused" );
660         }
661         else
662         {
663             y++;
664         }
665         if( val.i_int != INIT_S && val.i_int != END_S )
666         {
667             /* Position */
668             var_Get( p_input, "time", &val );
669             msecstotimestr( buf1, val.i_time / 1000 );
670
671             var_Get( p_input, "length", &val );
672             msecstotimestr( buf2, val.i_time / 1000 );
673
674             mvnprintw( y++, 0, COLS, " Position : %s/%s (%.2f%%)", buf1, buf2, p_sys->f_slider );
675
676             /* Title */
677             if( !var_Get( p_input, "title", &val ) )
678             {
679                 var_Change( p_input, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
680                 if( val_list.p_list->i_count > 0 )
681                 {
682                     mvnprintw( y++, 0, COLS, " Title    : %d/%d", val.i_int, val_list.p_list->i_count );
683                 }
684                 var_Change( p_input, "title", VLC_VAR_FREELIST, &val_list, NULL );
685             }
686
687             /* Chapter */
688             if( !var_Get( p_input, "chapter", &val ) )
689             {
690                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
691                 if( val_list.p_list->i_count > 0 )
692                 {
693                     mvnprintw( y++, 0, COLS, " Chapter  : %d/%d", val.i_int, val_list.p_list->i_count );
694                 }
695                 var_Change( p_input, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
696             }
697         }
698         else
699         {
700             y++;
701         }
702     }
703     else
704     {
705         mvnprintw( y++, 0, COLS, "Source: <no current item>" );
706         DrawEmptyLine( p_sys->w, y++, 0, COLS );
707         DrawEmptyLine( p_sys->w, y++, 0, COLS );
708     }
709
710     DrawBox( p_sys->w, y, 0, 3, COLS, "" );
711     DrawEmptyLine( p_sys->w, y+1, 1, COLS-2);
712     DrawLine( p_sys->w, y+1, 1, (int)(p_intf->p_sys->f_slider/100.0 * (COLS -2)) );
713     y += 3;
714
715     p_sys->i_box_y = y + 1;
716     p_sys->i_box_lines = LINES - y - 2;
717
718     h = LINES - y;
719     y_end = y + h - 1;
720
721     if( p_sys->i_box_type == BOX_HELP )
722     {
723         /* Help box */
724         int l = 0;
725         DrawBox( p_sys->w, y++, 0, h, COLS, " Help " );
726
727         MainBoxWrite( p_intf, l++, 1, "[Display]" );
728         MainBoxWrite( p_intf, l++, 1, "     h,H         Show/Hide help box" );
729         MainBoxWrite( p_intf, l++, 1, "     i           Show/Hide informations box" );
730         MainBoxWrite( p_intf, l++, 1, "     l           Show/Hide logs box" );
731         MainBoxWrite( p_intf, l++, 1, "     P           Show/Hide playlist box" );
732         MainBoxWrite( p_intf, l++, 1, "" );
733
734         MainBoxWrite( p_intf, l++, 1, "[Global]" );
735         MainBoxWrite( p_intf, l++, 1, "     q, Q        Quit" );
736         MainBoxWrite( p_intf, l++, 1, "     s           Stop" );
737         MainBoxWrite( p_intf, l++, 1, "   <space>       Pause/Play" );
738         MainBoxWrite( p_intf, l++, 1, "     n, p        Next/Previous item" );
739         MainBoxWrite( p_intf, l++, 1, "     [, ]        Next/Previous title" );
740         MainBoxWrite( p_intf, l++, 1, "     <, >        Next/Previous title" );
741         MainBoxWrite( p_intf, l++, 1, "     <right>     Seek +5%%" );
742         MainBoxWrite( p_intf, l++, 1, "     <left>      Seek -5%%" );
743         MainBoxWrite( p_intf, l++, 1, "" );
744
745         MainBoxWrite( p_intf, l++, 1, "[Playlist]" );
746         MainBoxWrite( p_intf, l++, 1, "     r           Randomize playlist" );
747         MainBoxWrite( p_intf, l++, 1, "     o           Order Playlist" );
748         MainBoxWrite( p_intf, l++, 1, "     O           Reverse order Playlist" );
749         MainBoxWrite( p_intf, l++, 1, "   <del>         Delete an entry" );
750         MainBoxWrite( p_intf, l++, 1, "  <backspace>    Delete an entry" );
751         MainBoxWrite( p_intf, l++, 1, "" );
752
753         MainBoxWrite( p_intf, l++, 1, "[Boxes]" );
754         MainBoxWrite( p_intf, l++, 1, "  <up>,<down>    Navigate through the box line by line" );
755         MainBoxWrite( p_intf, l++, 1, " <pgup>,<pgdown> Navigate through the box page by page" );
756         MainBoxWrite( p_intf, l++, 1, "" );
757
758         MainBoxWrite( p_intf, l++, 1, "[Player]" );
759         MainBoxWrite( p_intf, l++, 1, "  <up>,<down>    Seek +/-20%%" );
760         MainBoxWrite( p_intf, l++, 1, "" );
761
762         MainBoxWrite( p_intf, l++, 1, "[Miscellaneous]" );
763         MainBoxWrite( p_intf, l++, 1, "   Ctrl-L        Refresh the screen" );
764
765         p_sys->i_box_lines_total = l;
766         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
767         {
768             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
769         }
770
771         if( l - p_sys->i_box_start < p_sys->i_box_lines )
772         {
773             y += l - p_sys->i_box_start;
774         }
775         else
776         {
777             y += p_sys->i_box_lines;
778         }
779     }
780     else if( p_sys->i_box_type == BOX_INFO )
781     {
782         /* Info box */
783         int l = 0;
784         DrawBox( p_sys->w, y++, 0, h, COLS, " Informations " );
785
786         if( p_input )
787         {
788             input_info_category_t * p_category;
789             input_info_t * p_info;
790
791             vlc_mutex_lock( &p_input->stream.stream_lock );
792             p_category = p_input->stream.p_info;
793             while ( p_category )
794             {
795                 if( y >= y_end ) break;
796                 MainBoxWrite( p_intf, l++, 1, "  [%s]", p_category->psz_name );
797                 p_info = p_category->p_info;
798                 while ( p_info )
799                 {
800                     if( y >= y_end ) break;
801                     MainBoxWrite( p_intf, l++, 1, "      %s: %s", p_info->psz_name, p_info->psz_value );
802                     p_info = p_info->p_next;
803                 }
804                 p_category = p_category->p_next;
805             }
806             vlc_mutex_unlock( &p_input->stream.stream_lock );
807         }
808         else
809         {
810             MainBoxWrite( p_intf, l++, 1, "No item currently playing" );
811         }
812         p_sys->i_box_lines_total = l;
813         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
814         {
815             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
816         }
817
818         if( l - p_sys->i_box_start < p_sys->i_box_lines )
819         {
820             y += l - p_sys->i_box_start;
821         }
822         else
823         {
824             y += p_sys->i_box_lines;
825         }
826     }
827     else if( p_sys->i_box_type == BOX_LOG )
828     {
829         int i_line = 0;
830         int i_stop;
831         int i_start;
832
833         DrawBox( p_sys->w, y++, 0, h, COLS, " Logs " );
834
835         i_start = p_intf->p_sys->p_sub->i_start;
836
837         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
838         i_stop = *p_intf->p_sys->p_sub->pi_stop;
839         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
840
841         for( ;; )
842         {
843             static const char *ppsz_type[4] = { "", "error", "warning", "debug" };
844             if( i_line >= h - 2 )
845             {
846                 break;
847             }
848             i_stop--;
849             i_line++;
850             if( i_stop < 0 ) i_stop += VLC_MSG_QSIZE;
851             if( i_stop == i_start )
852             {
853                 break;
854             }
855             mvnprintw( y + h-2-i_line, 1, COLS - 2, "   [%s] %s",
856                       ppsz_type[p_sys->p_sub->p_msg[i_stop].i_type],
857                       p_sys->p_sub->p_msg[i_stop].psz_msg );
858         }
859
860         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
861         p_intf->p_sys->p_sub->i_start = i_stop;
862         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
863         y = y_end;
864     }
865     else if( p_sys->i_box_type == BOX_PLAYLIST && p_sys->p_playlist )
866     {
867         /* Playlist box */
868         playlist_t *p_playlist = p_sys->p_playlist;
869         int        i_start, i_stop;
870         int        i_item;
871         DrawBox( p_sys->w, y++, 0, h, COLS, " Playlist " );
872
873         if( p_playlist->i_index < (h - 2)/2 )
874         {
875             i_start = 0;
876             i_stop = h - 2;
877         }
878         else if( p_playlist->i_size - p_playlist->i_index > (h - 2)/2 )
879         {
880             i_start = p_playlist->i_index - (h - 2)/2;
881             i_stop = i_start + h - 2;
882         }
883         else
884         {
885             i_stop = p_playlist->i_size;
886             i_start = p_playlist->i_size - (h - 2);
887         }
888         if( i_start < 0 )
889         {
890             i_start = 0;
891         }
892         if( i_stop > p_playlist->i_size )
893         {
894             i_stop = p_playlist->i_size;
895         }
896
897         for( i_item = i_start; i_item < i_stop; i_item++ )
898         {
899             vlc_bool_t b_selected = ( p_playlist->i_index == i_item );
900             if( y >= y_end ) break;
901             if( b_selected )
902             {
903                 attrset( A_REVERSE );
904             }
905             if( !strcmp( p_playlist->pp_items[i_item]->psz_name, p_playlist->pp_items[i_item]->psz_uri ) )
906             {
907                 mvnprintw( y++, 1, COLS - 2, "  %d - '%s'",
908                            i_item,
909                            p_playlist->pp_items[i_item]->psz_uri );
910             }
911             else
912             {
913                 mvnprintw( y++, 1, COLS - 2, "  %d - '%s' (%s)",
914                           i_item,
915                           p_playlist->pp_items[i_item]->psz_uri,
916                           p_playlist->pp_items[i_item]->psz_name );
917             }
918             if( b_selected )
919             {
920                 attroff ( A_REVERSE );
921             }
922         }
923     }
924     else
925     {
926         y++;
927     }
928
929     while( y < y_end )
930     {
931         DrawEmptyLine( p_sys->w, y++, 1, COLS - 2 );
932     }
933
934     refresh();
935
936     *t_last_refresh = time( 0 );
937 }
938
939 static void Eject ( intf_thread_t *p_intf )
940 {
941     char *psz_device = NULL, *psz_parser, *psz_name;
942
943     /*
944      * Get the active input
945      * Determine whether we can eject a media, ie it's a DVD, VCD or CD-DA
946      * If it's neither of these, then return
947      */
948
949     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
950                                                        FIND_ANYWHERE );
951
952     if( p_playlist == NULL )
953     {
954         return;
955     }
956
957     vlc_mutex_lock( &p_playlist->object_lock );
958
959     if( p_playlist->i_index < 0 )
960     {
961         vlc_mutex_unlock( &p_playlist->object_lock );
962         vlc_object_release( p_playlist );
963         return;
964     }
965
966     psz_name = p_playlist->pp_items[ p_playlist->i_index ]->psz_name;
967
968     if( psz_name )
969     {
970         if( !strncmp(psz_name, "dvd://", 4) )
971         {
972             switch( psz_name[strlen("dvd://")] )
973             {
974             case '\0':
975             case '@':
976                 psz_device = config_GetPsz( p_intf, "dvd" );
977                 break;
978             default:
979                 /* Omit the first MRL-selector characters */
980                 psz_device = strdup( psz_name + strlen("dvd://" ) );
981                 break;
982             }
983         }
984         else if( !strncmp(psz_name, VCD_MRL, strlen(VCD_MRL)) )
985         {
986             switch( psz_name[strlen(VCD_MRL)] )
987             {
988             case '\0':
989             case '@':
990                 psz_device = config_GetPsz( p_intf, VCD_MRL );
991                 break;
992             default:
993                 /* Omit the beginning MRL-selector characters */
994                 psz_device = strdup( psz_name + strlen(VCD_MRL) );
995                 break;
996             }
997         }
998         else if( !strncmp(psz_name, CDDA_MRL, strlen(CDDA_MRL) ) )
999         {
1000             switch( psz_name[strlen(CDDA_MRL)] )
1001             {
1002             case '\0':
1003             case '@':
1004                 psz_device = config_GetPsz( p_intf, "cd-audio" );
1005                 break;
1006             default:
1007                 /* Omit the beginning MRL-selector characters */
1008                 psz_device = strdup( psz_name + strlen(CDDA_MRL) );
1009                 break;
1010             }
1011         }
1012         else
1013         {
1014             psz_device = strdup( psz_name );
1015         }
1016     }
1017
1018     vlc_mutex_unlock( &p_playlist->object_lock );
1019     vlc_object_release( p_playlist );
1020
1021     if( psz_device == NULL )
1022     {
1023         return;
1024     }
1025
1026     /* Remove what we have after @ */
1027     psz_parser = psz_device;
1028     for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
1029     {
1030         if( *psz_parser == '@' )
1031         {
1032             *psz_parser = '\0';
1033             break;
1034         }
1035     }
1036
1037     /* If there's a stream playing, we aren't allowed to eject ! */
1038     if( p_intf->p_sys->p_input == NULL )
1039     {
1040         msg_Dbg( p_intf, "ejecting %s", psz_device );
1041
1042         intf_Eject( p_intf, psz_device );
1043     }
1044
1045     free(psz_device);
1046     return;
1047 }
1048
1049 static void PlayPause ( intf_thread_t *p_intf )
1050 {
1051     input_thread_t *p_input = p_intf->p_sys->p_input;
1052     vlc_value_t val;
1053
1054     if( p_input )
1055     {
1056         var_Get( p_input, "state", &val );
1057         if( val.i_int != PAUSE_S )
1058         {
1059             val.i_int = PAUSE_S;
1060         }
1061         else
1062         {
1063             val.i_int = PLAYING_S;
1064         }
1065         var_Set( p_input, "state", val );
1066     }
1067     else if( p_intf->p_sys->p_playlist )
1068     {
1069         playlist_Play( p_intf->p_sys->p_playlist );
1070     }
1071 }
1072
1073 /****************************************************************************
1074  *
1075  ****************************************************************************/
1076 static void DrawBox( WINDOW *win, int y, int x, int h, int w, char *title )
1077 {
1078     int i;
1079     int i_len;
1080
1081     if(  w > 3 && h > 2 )
1082     {
1083         if( title == NULL ) title = "";
1084         i_len = strlen( title );
1085
1086         if( i_len > w - 2 ) i_len = w - 2;
1087
1088         mvwaddch( win, y, x,    ACS_ULCORNER );
1089         mvwhline( win, y, x+1,  ACS_HLINE, ( w-i_len-2)/2 );
1090         mvwprintw( win,y, x+1+(w-i_len-2)/2, "%s", title );
1091         mvwhline( win, y, x+(w-i_len)/2+i_len,  ACS_HLINE, w - 1 - ((w-i_len)/2+i_len) );
1092         mvwaddch( win, y, x+w-1,ACS_URCORNER );
1093
1094         for( i = 0; i < h-2; i++ )
1095         {
1096             mvwaddch( win, y+i+1, x,     ACS_VLINE );
1097             mvwaddch( win, y+i+1, x+w-1, ACS_VLINE );
1098         }
1099
1100         mvwaddch( win, y+h-1, x,     ACS_LLCORNER );
1101         mvwhline( win, y+h-1, x+1,   ACS_HLINE, w - 2 );
1102         mvwaddch( win, y+h-1, x+w-1, ACS_LRCORNER );
1103     }
1104 }
1105
1106 static void DrawEmptyLine( WINDOW *win, int y, int x, int w )
1107 {
1108     if( w > 0 )
1109     {
1110         mvhline( y, x, ' ', w );
1111     }
1112 }
1113
1114 static void DrawLine( WINDOW *win, int y, int x, int w )
1115 {
1116     if( w > 0 )
1117     {
1118         attrset( A_REVERSE );
1119         mvhline( y, x, ' ', w );
1120         attroff ( A_REVERSE );
1121     }
1122 }