]> git.sesse.net Git - vlc/blob - plugins/text/ncurses.c
* ALL: the first libvlc commit.
[vlc] / plugins / text / ncurses.c
1 /*****************************************************************************
2  * ncurses.c : NCurses plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: ncurses.c,v 1.16 2002/06/01 12:32:00 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *      
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <stdio.h>
31 #include <time.h>
32
33 #include <curses.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/intf.h>
37 #include <vlc/vout.h>
38
39 /*****************************************************************************
40  * Local prototypes.
41  *****************************************************************************/
42 static void intf_getfunctions ( function_list_t * );
43 static int  intf_Open         ( intf_thread_t * );
44 static void intf_Close        ( intf_thread_t * );
45 static void intf_Run          ( intf_thread_t * );
46
47 static void FullScreen   ( intf_thread_t * );
48 static void Play         ( intf_thread_t * );
49 static void Stop         ( intf_thread_t * );
50 static void Next           ( intf_thread_t * );
51 static void Eject          ( intf_thread_t * );
52 static void Pause          ( intf_thread_t * );
53 static void PrevTitle      ( intf_thread_t * );
54 static void NextTitle      ( intf_thread_t * );
55 static void PrevChapter    ( intf_thread_t * );
56 static void NextChapter    ( intf_thread_t * );
57
58 static int  HandleKey      ( intf_thread_t *, int );
59 static void Redraw           ( intf_thread_t *, time_t * );
60 static int  PrintFullLine  ( const char *p_fmt, ... );
61 static void ManageSlider   ( intf_thread_t * );
62
63 /*****************************************************************************
64  * Building configuration tree
65  *****************************************************************************/
66 MODULE_CONFIG_START
67 MODULE_CONFIG_STOP
68
69 MODULE_INIT_START
70     SET_DESCRIPTION( _("ncurses interface module") )
71     ADD_CAPABILITY( INTF, 10 )
72     ADD_SHORTCUT( "curses" )
73 MODULE_INIT_STOP
74
75 MODULE_ACTIVATE_START
76     intf_getfunctions( &p_module->p_functions->intf );
77 MODULE_ACTIVATE_STOP
78
79 MODULE_DEACTIVATE_START
80 MODULE_DEACTIVATE_STOP
81
82 /*****************************************************************************
83  * intf_sys_t: description and status of ncurses interface
84  *****************************************************************************/
85 struct intf_sys_s
86 {
87     /* special actions */
88     vlc_mutex_t         change_lock;                      /* the change lock */
89
90     float               f_slider_state;
91     float               f_slider_state_old;
92 };
93
94 /*****************************************************************************
95  * Functions exported as capabilities. They are declared as static so that
96  * we don't pollute the namespace too much.
97  *****************************************************************************/
98 static void intf_getfunctions( function_list_t * p_function_list )
99 {
100     p_function_list->functions.intf.pf_open  = intf_Open;
101     p_function_list->functions.intf.pf_close = intf_Close;
102     p_function_list->functions.intf.pf_run   = intf_Run;
103 }
104
105 /*****************************************************************************
106  * intf_Open: initialize and create window
107  *****************************************************************************/
108 static int intf_Open( intf_thread_t *p_intf )
109 {
110     /* Allocate instance and initialize some members */
111     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
112     if( p_intf->p_sys == NULL )
113     {
114         msg_Err( p_intf, "out of memory" );
115         return( 1 );
116     }
117
118     memset ( p_intf->p_sys, 0, sizeof ( intf_sys_t ) );
119
120     /* Initialize the curses library */
121     initscr();
122     /* Don't do NL -> CR/NL */
123     nonl();
124     /* Take input chars one at a time */
125     cbreak();
126     /* Don't echo */
127     noecho();
128
129     curs_set(0);
130     timeout(0);
131
132     clear();
133
134     return( 0 );
135 }
136
137 /*****************************************************************************
138  * intf_Close: destroy interface window
139  *****************************************************************************/
140 static void intf_Close( intf_thread_t *p_intf )
141 {
142     /* Close the ncurses interface */
143     endwin();
144
145     /* Destroy structure */
146     free( p_intf->p_sys );
147 }
148
149 /*****************************************************************************
150  * intf_Run: ncurses thread
151  *****************************************************************************/
152 static void intf_Run( intf_thread_t *p_intf )
153 {
154     signed char i_key;
155     time_t t_last_refresh;
156
157     /*
158      * force drawing the interface for the first time
159      */
160     t_last_refresh = ( time( 0 ) - 1);
161
162     while( !p_intf->p_vlc->b_die )
163     {
164         p_intf->pf_manage( p_intf );
165
166         msleep( INTF_IDLE_SLEEP );
167
168         while( (i_key = getch()) != -1 )
169         {
170             /*
171              * HandleKey returns 1 if the screen needs to be redrawn
172              */
173             if ( HandleKey( p_intf, i_key ) )
174             {
175                 Redraw( p_intf, &t_last_refresh );
176             }
177         }
178
179         /*
180          * redraw the screen every second
181          */
182         if ( (time(0) - t_last_refresh) >= 1 )
183         {
184             ManageSlider ( p_intf );
185             Redraw( p_intf, &t_last_refresh );
186         }
187     }
188 }
189
190 /* following functions are local */
191
192 static int HandleKey( intf_thread_t *p_intf, int i_key )
193 {
194     switch( i_key )
195     {
196         case 'q':
197         case 'Q':
198             p_intf->b_die = 1;
199             return 0;
200
201         case 'f':
202             FullScreen( p_intf );
203             return 1;
204
205         case 'p':
206             Play( p_intf );
207             return 1;
208
209         case ' ':
210             Pause( p_intf );
211             return 1;
212
213         case 's':
214             Stop( p_intf );
215             return 1;
216
217         case 'n':
218             Next( p_intf );
219             return 1;
220
221         case 'e':
222             Eject( p_intf );
223             return 1;
224
225         case '[':
226             PrevTitle( p_intf );
227             break;
228
229         case ']':
230             NextTitle( p_intf );
231             break;
232
233         case '<':
234             PrevChapter( p_intf );
235             break;
236
237         case '>':
238             NextChapter( p_intf );
239             break;
240
241         case KEY_RIGHT:
242             p_intf->p_sys->f_slider_state += 100;
243             ManageSlider ( p_intf );
244             break;
245
246         case KEY_LEFT:
247             p_intf->p_sys->f_slider_state--;
248             ManageSlider ( p_intf );
249             break;
250
251         /*
252          * ^l should clear and redraw the screen
253          */
254         case 0x0c:
255             clear();
256             return 1;
257
258         default:
259             break;
260     }
261
262     return 0;
263 }
264
265 static int PrintFullLine ( const char *p_fmt, ... )
266 {
267     va_list  vl_args;
268     char *    p_buf        = NULL;
269     int       i_len;
270
271     va_start ( vl_args, p_fmt );
272     vasprintf ( &p_buf, p_fmt, vl_args );
273     va_end ( vl_args );
274
275     if ( p_buf == NULL )
276     {
277 //X        msg_Err( p_input, "intf error: %s", strerror ( ENOMEM ) );
278         return ( -1 );
279     }
280
281     i_len = strlen( p_buf );
282
283     /*
284      * make sure we don't exceed the border on the right side
285      */
286     if ( i_len > COLS )
287     {
288         p_buf[COLS] = '\0';
289         i_len = COLS;
290         printw( "%s", p_buf );
291     }
292     else
293     {
294         printw( "%s", p_buf );
295         hline( ' ', COLS - i_len );
296     }
297
298     free ( p_buf );
299
300     return i_len;
301 }
302
303 static void
304 Redraw ( intf_thread_t *p_intf, time_t *t_last_refresh )
305 {
306     int row = 0;
307
308     move ( row, 0 );
309
310     attrset ( A_REVERSE );
311     PrintFullLine( VOUT_TITLE " (ncurses interface)" );
312     attroff ( A_REVERSE );
313
314     row++;
315
316     row++;
317     move ( row, 0 );
318
319     if ( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
320     {
321         PrintFullLine ( " DVD Chapter:%3d     DVD Title:%3d",
322             p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area->i_part,
323             p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area->i_id );
324     }
325
326     row++;
327     mvaddch ( row, 0, ACS_ULCORNER );
328     mvhline ( row, 1, ACS_HLINE, COLS-2 );
329     mvaddch ( row, COLS-1, ACS_URCORNER );
330
331     row++;
332     mvaddch ( row, 0, ACS_VLINE );
333     attrset ( A_REVERSE );
334     mvhline ( row, 1, ' ', ( (int) p_intf->p_sys->f_slider_state % COLS-2) );
335     attroff ( A_REVERSE );
336     mvaddch ( row, COLS-1, ACS_VLINE );
337
338     row++;
339     mvaddch ( row, 0, ACS_LLCORNER );
340     mvhline ( row, 1, ACS_HLINE, COLS-2 );
341     mvaddch ( row, COLS-1, ACS_LRCORNER );
342
343     refresh();
344
345     *t_last_refresh = time( 0 );
346 }
347
348 static void FullScreen( intf_thread_t *p_intf )
349 {
350     vlc_mutex_lock( &p_intf->p_vlc->p_vout_bank->pp_vout[0]->change_lock );
351
352     p_intf->p_vlc->p_vout_bank->pp_vout[0]->i_changes |= VOUT_FULLSCREEN_CHANGE;
353
354     vlc_mutex_unlock( &p_intf->p_vlc->p_vout_bank->pp_vout[0]->change_lock );
355 }
356
357 static void Eject ( intf_thread_t *p_intf )
358 {
359     char *psz_device = NULL;
360     char *psz_parser;
361
362     /*
363      * Get the active input
364      * Determine whether we can eject a media, ie it's a VCD or DVD
365      * If it's neither a VCD nor a DVD, then return
366      */
367
368     /*
369      * Don't really know if I must lock the stuff here, we're using it read-only
370      */
371
372     if( p_intf->p_vlc->p_playlist->current.psz_name != NULL)
373     {
374         if( !strncmp(p_intf->p_vlc->p_playlist->current.psz_name, "dvd:", 4) )
375         {
376             switch( p_intf->p_vlc->p_playlist->current.psz_name[4] )
377             {
378             case '\0':
379             case '@':
380                 psz_device = config_GetPsz( p_intf, "dvd_device" );
381                 break;
382             default:
383                 /* Omit the first 4 characters */
384                 psz_device = strdup( p_intf->p_vlc->p_playlist->current.psz_name + 4 );
385                 break;
386             }
387         }
388         else if( !strncmp(p_intf->p_vlc->p_playlist->current.psz_name, "vcd:", 4) )
389         {
390             switch( p_intf->p_vlc->p_playlist->current.psz_name[4] )
391             {
392             case '\0':
393             case '@':
394                 psz_device = config_GetPsz( p_intf, "vcd_device" );
395                 break;
396             default:
397                 /* Omit the first 4 characters */
398                 psz_device = strdup( p_intf->p_vlc->p_playlist->current.psz_name + 4 );
399                 break;
400             }
401         }
402         else
403         {
404             psz_device = strdup( p_intf->p_vlc->p_playlist->current.psz_name );
405         }
406     }
407
408     if( psz_device == NULL )
409     {
410         return;
411     }
412
413     /* Remove what we have after @ */
414     psz_parser = psz_device;
415     for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
416     {
417         if( *psz_parser == '@' )
418         {
419             *psz_parser = '\0';
420             break;
421         }
422     }
423
424     /* If there's a stream playing, we aren't allowed to eject ! */
425     if( p_intf->p_vlc->p_input_bank->pp_input[0] == NULL )
426     {
427 //X        msg_Dbg( p_input, "ejecting %s", psz_device );
428
429         intf_Eject( p_intf->p_this, psz_device );
430     }
431
432     free(psz_device);
433     return;
434 }
435
436 static void Play ( intf_thread_t *p_intf )
437 {
438     if( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
439     {
440         input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
441         p_intf->p_vlc->p_playlist->b_stopped = 0;
442     }
443     else
444     {
445         vlc_mutex_lock( &p_intf->p_vlc->p_playlist->change_lock );
446
447         if( p_intf->p_vlc->p_playlist->b_stopped )
448         {
449             if( p_intf->p_vlc->p_playlist->i_size )
450             {
451                 vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
452                 intf_PlaylistJumpto( p_intf->p_vlc->p_playlist,
453                                      p_intf->p_vlc->p_playlist->i_index );
454             }
455             else
456             {
457                 vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
458             }
459         }
460         else
461         {
462
463             vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
464         }
465
466     }
467 }
468
469 static void Pause ( intf_thread_t *p_intf )
470 {
471     if( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
472     {
473         if ( p_intf->p_vlc->p_input_bank->pp_input[0]->i_status & INPUT_STATUS_PLAY )
474         {
475             input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PAUSE );
476
477             vlc_mutex_lock( &p_intf->p_vlc->p_playlist->change_lock );
478             p_intf->p_vlc->p_playlist->b_stopped = 0;
479             vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
480         }
481         else
482         {
483             Play ( p_intf );
484         }
485     }
486 }
487
488 static void Stop ( intf_thread_t *p_intf )
489 {
490     if( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
491     {
492         /* end playing item */
493         p_intf->p_vlc->p_input_bank->pp_input[0]->b_eof = 1;
494
495         /* update playlist */
496         vlc_mutex_lock( &p_intf->p_vlc->p_playlist->change_lock );
497
498         p_intf->p_vlc->p_playlist->i_index--;
499         p_intf->p_vlc->p_playlist->b_stopped = 1;
500
501         vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
502
503     }
504 }
505
506 static void Next ( intf_thread_t *p_intf )
507 {
508     int i_id;
509     input_area_t * p_area;
510
511     i_id = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area->i_id+1;
512
513     if ( i_id < p_intf->p_vlc->p_input_bank->pp_input[0]->stream.i_area_nb )
514     {
515         p_area = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.pp_areas[i_id];
516
517         input_ChangeArea( p_intf->p_vlc->p_input_bank->pp_input[0],
518                 (input_area_t *) p_area );
519
520         input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
521     }
522 }
523
524 static void ManageSlider ( intf_thread_t *p_intf )
525 {
526     if( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
527     {
528         vlc_mutex_lock( &p_intf->p_vlc->p_input_bank->pp_input[0]->stream.stream_lock );
529
530         if( p_intf->p_vlc->p_input_bank->pp_input[0]->stream.b_seekable &&
531             p_intf->p_vlc->p_input_bank->pp_input[0]->i_status & INPUT_STATUS_PLAY )
532         {
533             float newvalue = p_intf->p_sys->f_slider_state;
534
535 #define p_area p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area
536
537             /* If the user hasn't touched the slider since the last time,
538              * then the input can safely change it */
539             if( newvalue == p_intf->p_sys->f_slider_state_old )
540             {
541                 /* Update the value */
542                 p_intf->p_sys->f_slider_state =
543                     p_intf->p_sys->f_slider_state_old =
544                     ( 100 * p_area->i_tell ) / p_area->i_size;
545             }
546             /* Otherwise, send message to the input if the user has
547              * finished dragging the slider */
548             else
549             {
550                 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
551
552                 /* release the lock to be able to seek */
553                 vlc_mutex_unlock( &p_intf->p_vlc->p_input_bank->pp_input[0]->stream.stream_lock );
554                 input_Seek( p_intf->p_vlc->p_input_bank->pp_input[0]->p_this, i_seek, INPUT_SEEK_SET );
555                 vlc_mutex_lock( &p_intf->p_vlc->p_input_bank->pp_input[0]->stream.stream_lock );
556
557                 /* Update the old value */
558                 p_intf->p_sys->f_slider_state_old = newvalue;
559             }
560 #    undef p_area
561         }
562
563         vlc_mutex_unlock( &p_intf->p_vlc->p_input_bank->pp_input[0]->stream.stream_lock );
564     }
565 }
566
567 static void PrevTitle ( intf_thread_t *p_intf )
568 {
569     input_area_t *  p_area;
570     int             i_id;
571
572     i_id = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area->i_id - 1;
573
574     /* Disallow area 0 since it is used for video_ts.vob */
575     if ( i_id > 0 )
576     {
577         p_area = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.pp_areas[i_id];
578         input_ChangeArea( p_intf->p_vlc->p_input_bank->pp_input[0], (input_area_t*)p_area );
579
580         input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
581     }
582 }
583
584 static void NextTitle ( intf_thread_t *p_intf )
585 {
586     input_area_t *  p_area;
587     int             i_id;
588
589     i_id = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area->i_id + 1;
590
591     if ( i_id < p_intf->p_vlc->p_input_bank->pp_input[0]->stream.i_area_nb )
592     {
593         p_area = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.pp_areas[i_id];
594         input_ChangeArea( p_intf->p_vlc->p_input_bank->pp_input[0], (input_area_t*)p_area );
595
596         input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
597     }
598 }
599
600 static void PrevChapter ( intf_thread_t *p_intf )
601 {
602     input_area_t *  p_area;
603
604     p_area = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area;
605
606     if ( p_area->i_part > 0 )
607     {
608         p_area->i_part--;
609         input_ChangeArea( p_intf->p_vlc->p_input_bank->pp_input[0], (input_area_t*)p_area );
610
611         input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
612     }
613 }
614
615 static void NextChapter( intf_thread_t *p_intf )
616 {
617     input_area_t *  p_area;
618
619     p_area = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area;
620
621     if ( p_area->i_part < p_area->i_part_nb )
622     {
623         p_area->i_part++;
624         input_ChangeArea( p_intf->p_vlc->p_input_bank->pp_input[0], (input_area_t*)p_area );
625
626         input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
627     }
628 }
629