1 /*****************************************************************************
2 * ncurses.c : NCurses plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: ncurses.c,v 1.20 2002/07/31 20:56:52 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 int Open ( vlc_object_t * );
43 static void Close ( vlc_object_t * );
45 static void Run ( intf_thread_t * );
46 static void FullScreen ( intf_thread_t * );
47 static void Play ( intf_thread_t * );
48 static void Stop ( intf_thread_t * );
49 static void Next ( intf_thread_t * );
50 static void Eject ( intf_thread_t * );
51 static void Pause ( intf_thread_t * );
52 static void PrevTitle ( intf_thread_t * );
53 static void NextTitle ( intf_thread_t * );
54 static void PrevChapter ( intf_thread_t * );
55 static void NextChapter ( intf_thread_t * );
57 static int HandleKey ( intf_thread_t *, int );
58 static void Redraw ( intf_thread_t *, time_t * );
59 static int PrintFullLine ( const char *p_fmt, ... );
60 static void ManageSlider ( intf_thread_t * );
62 /*****************************************************************************
64 *****************************************************************************/
66 set_description( _("ncurses interface module") );
67 set_capability( "interface", 10 );
68 set_callbacks( Open, Close );
69 add_shortcut( "curses" );
72 /*****************************************************************************
73 * intf_sys_t: description and status of ncurses interface
74 *****************************************************************************/
77 input_thread_t * p_input;
80 float f_slider_state_old;
83 /*****************************************************************************
84 * Open: initialize and create window
85 *****************************************************************************/
86 static int Open( vlc_object_t *p_this )
88 intf_thread_t *p_intf = (intf_thread_t *)p_this;
90 /* Allocate instance and initialize some members */
91 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
92 if( p_intf->p_sys == NULL )
94 msg_Err( p_intf, "out of memory" );
98 p_intf->p_sys->p_input = NULL;
100 p_intf->pf_run = Run;
102 /* Initialize the curses library */
104 /* Don't do NL -> CR/NL */
106 /* Take input chars one at a time */
119 /*****************************************************************************
120 * Close: destroy interface window
121 *****************************************************************************/
122 static void Close( vlc_object_t *p_this )
124 intf_thread_t *p_intf = (intf_thread_t *)p_this;
126 if( p_intf->p_sys->p_input )
128 vlc_object_release( p_intf->p_sys->p_input );
131 /* Close the ncurses interface */
134 /* Destroy structure */
135 free( p_intf->p_sys );
138 /*****************************************************************************
139 * Run: ncurses thread
140 *****************************************************************************/
141 static void Run( intf_thread_t *p_intf )
144 time_t t_last_refresh;
147 * force drawing the interface for the first time
149 t_last_refresh = ( time( 0 ) - 1);
151 while( !p_intf->b_die )
153 msleep( INTF_IDLE_SLEEP );
155 /* Update the input */
156 if( p_intf->p_sys->p_input == NULL )
158 p_intf->p_sys->p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
161 else if( p_intf->p_sys->p_input->b_dead )
163 vlc_object_release( p_intf->p_sys->p_input );
164 p_intf->p_sys->p_input = NULL;
167 while( (i_key = getch()) != -1 )
170 * HandleKey returns 1 if the screen needs to be redrawn
172 if ( HandleKey( p_intf, i_key ) )
174 Redraw( p_intf, &t_last_refresh );
179 * redraw the screen every second
181 if ( (time(0) - t_last_refresh) >= 1 )
183 ManageSlider ( p_intf );
184 Redraw( p_intf, &t_last_refresh );
189 /* following functions are local */
191 static int HandleKey( intf_thread_t *p_intf, int i_key )
201 FullScreen( p_intf );
233 PrevChapter( p_intf );
237 NextChapter( p_intf );
241 p_intf->p_sys->f_slider_state += 100;
242 ManageSlider ( p_intf );
246 p_intf->p_sys->f_slider_state--;
247 ManageSlider ( p_intf );
251 * ^l should clear and redraw the screen
264 static int PrintFullLine ( const char *p_fmt, ... )
270 va_start ( vl_args, p_fmt );
271 vasprintf ( &p_buf, p_fmt, vl_args );
276 //X msg_Err( p_input, "intf error: %s", strerror ( ENOMEM ) );
280 i_len = strlen( p_buf );
283 * make sure we don't exceed the border on the right side
289 printw( "%s", p_buf );
293 printw( "%s", p_buf );
294 hline( ' ', COLS - i_len );
303 Redraw ( intf_thread_t *p_intf, time_t *t_last_refresh )
309 attrset ( A_REVERSE );
310 PrintFullLine( VOUT_TITLE " (ncurses interface)" );
311 attroff ( A_REVERSE );
318 if ( p_intf->p_sys->p_input != NULL )
320 PrintFullLine ( " DVD Chapter:%3d DVD Title:%3d",
321 p_intf->p_sys->p_input->stream.p_selected_area->i_part,
322 p_intf->p_sys->p_input->stream.p_selected_area->i_id );
326 mvaddch ( row, 0, ACS_ULCORNER );
327 mvhline ( row, 1, ACS_HLINE, COLS-2 );
328 mvaddch ( row, COLS-1, ACS_URCORNER );
331 mvaddch ( row, 0, ACS_VLINE );
332 attrset ( A_REVERSE );
333 mvhline ( row, 1, ' ', ( (int) p_intf->p_sys->f_slider_state % COLS-2) );
334 attroff ( A_REVERSE );
335 mvaddch ( row, COLS-1, ACS_VLINE );
338 mvaddch ( row, 0, ACS_LLCORNER );
339 mvhline ( row, 1, ACS_HLINE, COLS-2 );
340 mvaddch ( row, COLS-1, ACS_LRCORNER );
344 *t_last_refresh = time( 0 );
347 static void FullScreen( intf_thread_t *p_intf )
349 vout_thread_t *p_vout;
351 p_vout = vlc_object_find( p_intf->p_sys->p_input,
352 VLC_OBJECT_VOUT, FIND_CHILD );
358 p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
359 vlc_object_release( p_vout );
362 static void Eject ( intf_thread_t *p_intf )
364 char *psz_device = NULL, *psz_parser, *psz_name;
367 * Get the active input
368 * Determine whether we can eject a media, ie it's a VCD or DVD
369 * If it's neither a VCD nor a DVD, then return
372 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
375 if( p_playlist == NULL )
380 vlc_mutex_lock( &p_playlist->object_lock );
382 if( p_playlist->i_index < 0 )
384 vlc_mutex_unlock( &p_playlist->object_lock );
385 vlc_object_release( p_playlist );
389 psz_name = p_playlist->pp_items[ p_playlist->i_index ]->psz_name;
393 if( !strncmp(psz_name, "dvd:", 4) )
395 switch( psz_name[4] )
399 psz_device = config_GetPsz( p_intf, "dvd_device" );
402 /* Omit the first 4 characters */
403 psz_device = strdup( psz_name + 4 );
407 else if( !strncmp(psz_name, "vcd:", 4) )
409 switch( psz_name[4] )
413 psz_device = config_GetPsz( p_intf, "vcd_device" );
416 /* Omit the first 4 characters */
417 psz_device = strdup( psz_name + 4 );
423 psz_device = strdup( psz_name );
427 vlc_mutex_unlock( &p_playlist->object_lock );
428 vlc_object_release( p_playlist );
430 if( psz_device == NULL )
435 /* Remove what we have after @ */
436 psz_parser = psz_device;
437 for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
439 if( *psz_parser == '@' )
446 /* If there's a stream playing, we aren't allowed to eject ! */
447 if( p_intf->p_sys->p_input == NULL )
449 //X msg_Dbg( p_input, "ejecting %s", psz_device );
451 intf_Eject( p_intf, psz_device );
458 static void Play ( intf_thread_t *p_intf )
460 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
464 vlc_mutex_lock( &p_playlist->object_lock );
466 if( p_playlist->i_size )
468 vlc_mutex_unlock( &p_playlist->object_lock );
469 playlist_Play( p_playlist );
470 vlc_object_release( p_playlist );
474 vlc_mutex_unlock( &p_playlist->object_lock );
475 vlc_object_release( p_playlist );
480 static void Pause ( intf_thread_t *p_intf )
482 if( p_intf->p_sys->p_input == NULL )
487 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PAUSE );
492 static void Stop ( intf_thread_t *p_intf )
494 playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
496 if( p_playlist == NULL )
501 playlist_Stop( p_playlist );
502 vlc_object_release( p_playlist );
507 static void Next ( intf_thread_t *p_intf )
510 input_area_t * p_area;
512 i_id = p_intf->p_sys->p_input->stream.p_selected_area->i_id+1;
514 if ( i_id < p_intf->p_sys->p_input->stream.i_area_nb )
516 p_area = p_intf->p_sys->p_input->stream.pp_areas[i_id];
518 input_ChangeArea( p_intf->p_sys->p_input,
519 (input_area_t *) p_area );
521 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
525 static void ManageSlider ( intf_thread_t *p_intf )
527 if( p_intf->p_sys->p_input != NULL )
529 vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
531 if( p_intf->p_sys->p_input->stream.b_seekable &&
532 p_intf->p_sys->p_input->stream.control.i_status == PLAYING_S )
534 float newvalue = p_intf->p_sys->f_slider_state;
536 #define p_area p_intf->p_sys->p_input->stream.p_selected_area
538 /* If the user hasn't touched the slider since the last time,
539 * then the input can safely change it */
540 if( newvalue == p_intf->p_sys->f_slider_state_old )
542 /* Update the value */
543 p_intf->p_sys->f_slider_state =
544 p_intf->p_sys->f_slider_state_old =
545 ( 100 * p_area->i_tell ) / p_area->i_size;
547 /* Otherwise, send message to the input if the user has
548 * finished dragging the slider */
551 off_t i_seek = ( newvalue * p_area->i_size ) / 100;
553 /* release the lock to be able to seek */
554 vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
555 input_Seek( p_intf, i_seek, INPUT_SEEK_SET );
556 vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
558 /* Update the old value */
559 p_intf->p_sys->f_slider_state_old = newvalue;
564 vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
568 static void PrevTitle ( intf_thread_t *p_intf )
570 input_area_t * p_area;
573 i_id = p_intf->p_sys->p_input->stream.p_selected_area->i_id - 1;
575 /* Disallow area 0 since it is used for video_ts.vob */
578 p_area = p_intf->p_sys->p_input->stream.pp_areas[i_id];
579 input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
581 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
585 static void NextTitle ( intf_thread_t *p_intf )
587 input_area_t * p_area;
590 i_id = p_intf->p_sys->p_input->stream.p_selected_area->i_id + 1;
592 if ( i_id < p_intf->p_sys->p_input->stream.i_area_nb )
594 p_area = p_intf->p_sys->p_input->stream.pp_areas[i_id];
595 input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
597 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
601 static void PrevChapter ( intf_thread_t *p_intf )
603 input_area_t * p_area;
605 p_area = p_intf->p_sys->p_input->stream.p_selected_area;
607 if ( p_area->i_part > 0 )
610 input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
612 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
616 static void NextChapter( intf_thread_t *p_intf )
618 input_area_t * p_area;
620 p_area = p_intf->p_sys->p_input->stream.p_selected_area;
622 if ( p_area->i_part < p_area->i_part_nb )
625 input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
627 input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );