1 /*****************************************************************************
2 * ncurses.c : NCurses plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: ncurses.c,v 1.17 2002/06/01 18:04:49 sam Exp $
7 * Authors: Samuel Hocevar <sam@zoy.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
29 #include <errno.h> /* ENOMEM */
39 /*****************************************************************************
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 * );
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 * );
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 * );
63 /*****************************************************************************
64 * Building configuration tree
65 *****************************************************************************/
70 SET_DESCRIPTION( _("ncurses interface module") )
71 ADD_CAPABILITY( INTF, 10 )
72 ADD_SHORTCUT( "curses" )
76 intf_getfunctions( &p_module->p_functions->intf );
79 MODULE_DEACTIVATE_START
80 MODULE_DEACTIVATE_STOP
82 /*****************************************************************************
83 * intf_sys_t: description and status of ncurses interface
84 *****************************************************************************/
88 vlc_mutex_t change_lock; /* the change lock */
91 float f_slider_state_old;
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 )
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;
105 /*****************************************************************************
106 * intf_Open: initialize and create window
107 *****************************************************************************/
108 static int intf_Open( intf_thread_t *p_intf )
110 /* Allocate instance and initialize some members */
111 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
112 if( p_intf->p_sys == NULL )
114 msg_Err( p_intf, "out of memory" );
118 memset ( p_intf->p_sys, 0, sizeof ( intf_sys_t ) );
120 /* Initialize the curses library */
122 /* Don't do NL -> CR/NL */
124 /* Take input chars one at a time */
137 /*****************************************************************************
138 * intf_Close: destroy interface window
139 *****************************************************************************/
140 static void intf_Close( intf_thread_t *p_intf )
142 /* Close the ncurses interface */
145 /* Destroy structure */
146 free( p_intf->p_sys );
149 /*****************************************************************************
150 * intf_Run: ncurses thread
151 *****************************************************************************/
152 static void intf_Run( intf_thread_t *p_intf )
155 time_t t_last_refresh;
158 * force drawing the interface for the first time
160 t_last_refresh = ( time( 0 ) - 1);
162 while( !p_intf->p_vlc->b_die )
164 p_intf->pf_manage( p_intf );
166 msleep( INTF_IDLE_SLEEP );
168 while( (i_key = getch()) != -1 )
171 * HandleKey returns 1 if the screen needs to be redrawn
173 if ( HandleKey( p_intf, i_key ) )
175 Redraw( p_intf, &t_last_refresh );
180 * redraw the screen every second
182 if ( (time(0) - t_last_refresh) >= 1 )
184 ManageSlider ( p_intf );
185 Redraw( p_intf, &t_last_refresh );
190 /* following functions are local */
192 static int HandleKey( intf_thread_t *p_intf, int i_key )
202 FullScreen( p_intf );
234 PrevChapter( p_intf );
238 NextChapter( p_intf );
242 p_intf->p_sys->f_slider_state += 100;
243 ManageSlider ( p_intf );
247 p_intf->p_sys->f_slider_state--;
248 ManageSlider ( p_intf );
252 * ^l should clear and redraw the screen
265 static int PrintFullLine ( const char *p_fmt, ... )
271 va_start ( vl_args, p_fmt );
272 vasprintf ( &p_buf, p_fmt, vl_args );
277 //X msg_Err( p_input, "intf error: %s", strerror ( ENOMEM ) );
281 i_len = strlen( p_buf );
284 * make sure we don't exceed the border on the right side
290 printw( "%s", p_buf );
294 printw( "%s", p_buf );
295 hline( ' ', COLS - i_len );
304 Redraw ( intf_thread_t *p_intf, time_t *t_last_refresh )
310 attrset ( A_REVERSE );
311 PrintFullLine( VOUT_TITLE " (ncurses interface)" );
312 attroff ( A_REVERSE );
319 if ( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
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 );
327 mvaddch ( row, 0, ACS_ULCORNER );
328 mvhline ( row, 1, ACS_HLINE, COLS-2 );
329 mvaddch ( row, COLS-1, ACS_URCORNER );
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 );
339 mvaddch ( row, 0, ACS_LLCORNER );
340 mvhline ( row, 1, ACS_HLINE, COLS-2 );
341 mvaddch ( row, COLS-1, ACS_LRCORNER );
345 *t_last_refresh = time( 0 );
348 static void FullScreen( intf_thread_t *p_intf )
350 vlc_mutex_lock( &p_intf->p_vlc->p_vout_bank->pp_vout[0]->change_lock );
352 p_intf->p_vlc->p_vout_bank->pp_vout[0]->i_changes |= VOUT_FULLSCREEN_CHANGE;
354 vlc_mutex_unlock( &p_intf->p_vlc->p_vout_bank->pp_vout[0]->change_lock );
357 static void Eject ( intf_thread_t *p_intf )
359 char *psz_device = NULL;
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
369 * Don't really know if I must lock the stuff here, we're using it read-only
372 if( p_intf->p_vlc->p_playlist->current.psz_name != NULL)
374 if( !strncmp(p_intf->p_vlc->p_playlist->current.psz_name, "dvd:", 4) )
376 switch( p_intf->p_vlc->p_playlist->current.psz_name[4] )
380 psz_device = config_GetPsz( p_intf, "dvd_device" );
383 /* Omit the first 4 characters */
384 psz_device = strdup( p_intf->p_vlc->p_playlist->current.psz_name + 4 );
388 else if( !strncmp(p_intf->p_vlc->p_playlist->current.psz_name, "vcd:", 4) )
390 switch( p_intf->p_vlc->p_playlist->current.psz_name[4] )
394 psz_device = config_GetPsz( p_intf, "vcd_device" );
397 /* Omit the first 4 characters */
398 psz_device = strdup( p_intf->p_vlc->p_playlist->current.psz_name + 4 );
404 psz_device = strdup( p_intf->p_vlc->p_playlist->current.psz_name );
408 if( psz_device == NULL )
413 /* Remove what we have after @ */
414 psz_parser = psz_device;
415 for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
417 if( *psz_parser == '@' )
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 )
427 //X msg_Dbg( p_input, "ejecting %s", psz_device );
429 intf_Eject( p_intf, psz_device );
436 static void Play ( intf_thread_t *p_intf )
438 if( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
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;
445 vlc_mutex_lock( &p_intf->p_vlc->p_playlist->change_lock );
447 if( p_intf->p_vlc->p_playlist->b_stopped )
449 if( p_intf->p_vlc->p_playlist->i_size )
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 );
457 vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
463 vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
469 static void Pause ( intf_thread_t *p_intf )
471 if( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
473 if ( p_intf->p_vlc->p_input_bank->pp_input[0]->i_status & INPUT_STATUS_PLAY )
475 input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PAUSE );
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 );
488 static void Stop ( intf_thread_t *p_intf )
490 if( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
492 /* end playing item */
493 p_intf->p_vlc->p_input_bank->pp_input[0]->b_eof = 1;
495 /* update playlist */
496 vlc_mutex_lock( &p_intf->p_vlc->p_playlist->change_lock );
498 p_intf->p_vlc->p_playlist->i_index--;
499 p_intf->p_vlc->p_playlist->b_stopped = 1;
501 vlc_mutex_unlock( &p_intf->p_vlc->p_playlist->change_lock );
506 static void Next ( intf_thread_t *p_intf )
509 input_area_t * p_area;
511 i_id = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area->i_id+1;
513 if ( i_id < p_intf->p_vlc->p_input_bank->pp_input[0]->stream.i_area_nb )
515 p_area = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.pp_areas[i_id];
517 input_ChangeArea( p_intf->p_vlc->p_input_bank->pp_input[0],
518 (input_area_t *) p_area );
520 input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
524 static void ManageSlider ( intf_thread_t *p_intf )
526 if( p_intf->p_vlc->p_input_bank->pp_input[0] != NULL )
528 vlc_mutex_lock( &p_intf->p_vlc->p_input_bank->pp_input[0]->stream.stream_lock );
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 )
533 float newvalue = p_intf->p_sys->f_slider_state;
535 #define p_area p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area
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 )
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;
546 /* Otherwise, send message to the input if the user has
547 * finished dragging the slider */
550 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
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 );
557 /* Update the old value */
558 p_intf->p_sys->f_slider_state_old = newvalue;
563 vlc_mutex_unlock( &p_intf->p_vlc->p_input_bank->pp_input[0]->stream.stream_lock );
567 static void PrevTitle ( intf_thread_t *p_intf )
569 input_area_t * p_area;
572 i_id = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area->i_id - 1;
574 /* Disallow area 0 since it is used for video_ts.vob */
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 );
580 input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
584 static void NextTitle ( intf_thread_t *p_intf )
586 input_area_t * p_area;
589 i_id = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area->i_id + 1;
591 if ( i_id < p_intf->p_vlc->p_input_bank->pp_input[0]->stream.i_area_nb )
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 );
596 input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
600 static void PrevChapter ( intf_thread_t *p_intf )
602 input_area_t * p_area;
604 p_area = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area;
606 if ( p_area->i_part > 0 )
609 input_ChangeArea( p_intf->p_vlc->p_input_bank->pp_input[0], (input_area_t*)p_area );
611 input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
615 static void NextChapter( intf_thread_t *p_intf )
617 input_area_t * p_area;
619 p_area = p_intf->p_vlc->p_input_bank->pp_input[0]->stream.p_selected_area;
621 if ( p_area->i_part < p_area->i_part_nb )
624 input_ChangeArea( p_intf->p_vlc->p_input_bank->pp_input[0], (input_area_t*)p_area );
626 input_SetStatus( p_intf->p_vlc->p_input_bank->pp_input[0], INPUT_STATUS_PLAY );