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