]> git.sesse.net Git - vlc/commitdiff
* ./plugins/text/ncurses.c: ncurses interface improvements by Thomas Graf.
authorSam Hocevar <sam@videolan.org>
Mon, 13 May 2002 17:57:46 +0000 (17:57 +0000)
committerSam Hocevar <sam@videolan.org>
Mon, 13 May 2002 17:57:46 +0000 (17:57 +0000)
plugins/text/ncurses.c

index 9701bcc80a264eb298495b45ac152c670b85345b..14265b209c39235bb2bea80555c371846af3fb1e 100644 (file)
@@ -2,7 +2,7 @@
  * ncurses.c : NCurses plugin for vlc
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: ncurses.c,v 1.14 2002/04/19 13:56:11 sam Exp $
+ * $Id: ncurses.c,v 1.15 2002/05/13 17:57:46 sam Exp $
  *
  * Authors: Samuel Hocevar <sam@zoy.org>
  *      
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <errno.h>                                                 /* ENOMEM */
 #include <stdio.h>
+#include <time.h>
 
 #include <curses.h>
 
 #include "input_ext-intf.h"
 
 #include "interface.h"
+#include "intf_playlist.h"
+#include "intf_eject.h"
+
+#include "video.h"
+#include "video_output.h"
 
 /*****************************************************************************
  * Local prototypes.
@@ -46,6 +52,23 @@ static int  intf_Open         ( intf_thread_t *p_intf );
 static void intf_Close        ( intf_thread_t *p_intf );
 static void intf_Run          ( intf_thread_t *p_intf );
 
+static void ncurses_Fullscreen   ( void );
+static void ncurses_Play         ( void );
+static void ncurses_Stop         ( void );
+static void ncurses_Next         ( void );
+static void ncurses_Eject        ( void );
+static void ncurses_Pause        ( void );
+static void ncurses_TitlePrev    ( void );
+static void ncurses_TitleNext    ( void );
+static void ncurses_ChapterPrev  ( void );
+static void ncurses_ChapterNext  ( void );
+
+static int  ncurses_handleKey      ( intf_thread_t *p_intf, int i_key );
+static void ncurses_draw           ( time_t *t_last_refresh,
+                                     intf_thread_t *p_intf );
+static int  ncurses_printFullLine  ( const char *p_fmt, ... );
+static void ncurses_manageSlider   ( intf_thread_t *p_intf );
+
 /*****************************************************************************
  * Building configuration tree
  *****************************************************************************/
@@ -74,6 +97,9 @@ typedef struct intf_sys_s
     /* special actions */
     vlc_mutex_t         change_lock;                      /* the change lock */
 
+    float             f_slider_state;
+    float             f_slider_state_old;
+
 } intf_sys_t;
 
 /*****************************************************************************
@@ -100,6 +126,8 @@ static int intf_Open( intf_thread_t *p_intf )
         return( 1 );
     }
 
+    memset ( p_intf->p_sys, 0, sizeof ( intf_sys_t ) );
+
     /* Initialize the curses library */
     initscr();
     /* Don't do NL -> CR/NL */
@@ -112,6 +140,8 @@ static int intf_Open( intf_thread_t *p_intf )
     curs_set(0);
     timeout(0);
 
+    clear();
+
     return( 0 );
 }
 
@@ -133,6 +163,12 @@ static void intf_Close( intf_thread_t *p_intf )
 static void intf_Run( intf_thread_t *p_intf )
 {
     signed char i_key;
+    time_t t_last_refresh;
+
+    /*
+     * force drawing the interface for the first time
+     */
+    t_last_refresh = ( time( 0 ) - 1);
 
     while( !p_intf->b_die )
     {
@@ -140,26 +176,477 @@ static void intf_Run( intf_thread_t *p_intf )
 
         msleep( INTF_IDLE_SLEEP );
 
-        mvaddstr( 1, 2, VOUT_TITLE " (ncurses interface)" );
-        mvaddstr( 3, 2, "keys:" );
-        mvaddstr( 4, 2, "Q,q.......quit" );
-        //mvaddstr( 5, 2, "No other keys are active yet." );
-
         while( (i_key = getch()) != -1 )
         {
-            switch( i_key )
+            /*
+             * ncurses_HandleKey returns 1 if the screen needs to be redrawn
+             */
+            if ( ncurses_handleKey( p_intf, i_key ) )
             {
-                case 'q':
-                case 'Q':
-                    p_intf->b_die = 1;
-                    break;
-
-                default:
-                    break;
+                ncurses_draw( &t_last_refresh, p_intf );
             }
         }
+
+        /*
+         * redraw the screen every second
+         */
+        if ( (time(0) - t_last_refresh) >= 1 )
+        {
+            ncurses_manageSlider ( p_intf );
+            ncurses_draw( &t_last_refresh, p_intf );
+        }
     }
 }
 
 /* following functions are local */
 
+static int
+ncurses_handleKey( intf_thread_t *p_intf, int i_key )
+{
+    switch( i_key )
+    {
+        case 'q':
+        case 'Q':
+            p_intf->b_die = 1;
+            return 0;
+
+        case 'f':
+            ncurses_Fullscreen();
+            return 1;
+
+        case 'p':
+            ncurses_Play();
+            return 1;
+
+        case ' ':
+            ncurses_Pause();
+            return 1;
+
+        case 's':
+            ncurses_Stop();
+            return 1;
+
+        case 'n':
+            ncurses_Next();
+            return 1;
+
+        case 'e':
+            ncurses_Eject();
+            return 1;
+
+        case '[':
+            ncurses_TitlePrev ();
+            break;
+
+        case ']':
+            ncurses_TitleNext ();
+            break;
+
+        case '<':
+            ncurses_ChapterPrev ();
+            break;
+
+        case '>':
+            ncurses_ChapterNext ();
+            break;
+
+        case KEY_RIGHT:
+            p_intf->p_sys->f_slider_state += 100;
+            ncurses_manageSlider ( p_intf );
+            break;
+
+        case KEY_LEFT:
+            p_intf->p_sys->f_slider_state--;
+            ncurses_manageSlider ( p_intf );
+            break;
+
+        /*
+         * ^l should clear and redraw the screen
+         */
+        case 0x0c:
+            clear();
+            return 1;
+
+        default:
+            break;
+    }
+
+    return 0;
+}
+
+static int
+ncurses_printFullLine ( const char *p_fmt, ... )
+{
+    va_list  vl_args;
+    char *    p_buf        = NULL;
+    int       i_len;
+
+    va_start ( vl_args, p_fmt );
+    vasprintf ( &p_buf, p_fmt, vl_args );
+    va_end ( vl_args );
+
+    if ( p_buf == NULL )
+    {
+        intf_ErrMsg ( "intf error: %s", strerror ( ENOMEM ) );
+        return ( -1 );
+    }
+
+    i_len = strlen( p_buf );
+
+    /*
+     * make sure we don't exceed the border on the right side
+     */
+    if ( i_len > COLS )
+    {
+        p_buf[COLS] = '\0';
+        i_len = COLS;
+        printw( "%s", p_buf );
+    }
+    else
+    {
+        printw( "%s", p_buf );
+        hline( ' ', COLS - i_len );
+    }
+
+    free ( p_buf );
+
+    return i_len;
+}
+
+static void
+ncurses_draw ( time_t *t_last_refresh , intf_thread_t *p_intf )
+{
+    int row = 0;
+
+    move ( row, 0 );
+
+    attrset ( A_REVERSE );
+    ncurses_printFullLine( VOUT_TITLE " (ncurses interface)" );
+    attroff ( A_REVERSE );
+
+    row++;
+
+    row++;
+    move ( row, 0 );
+
+    if ( p_input_bank->pp_input[0] != NULL )
+    {
+        ncurses_printFullLine ( " DVD Chapter:%3d     DVD Title:%3d",
+            p_input_bank->pp_input[0]->stream.p_selected_area->i_part,
+            p_input_bank->pp_input[0]->stream.p_selected_area->i_id );
+    }
+
+    row++;
+    mvaddch ( row, 0, ACS_ULCORNER );
+    mvhline ( row, 1, ACS_HLINE, COLS-2 );
+    mvaddch ( row, COLS-1, ACS_URCORNER );
+
+    row++;
+    mvaddch ( row, 0, ACS_VLINE );
+    attrset ( A_REVERSE );
+    mvhline ( row, 1, ' ', ( (int) p_intf->p_sys->f_slider_state % COLS-2) );
+    attroff ( A_REVERSE );
+    mvaddch ( row, COLS-1, ACS_VLINE );
+
+    row++;
+    mvaddch ( row, 0, ACS_LLCORNER );
+    mvhline ( row, 1, ACS_HLINE, COLS-2 );
+    mvaddch ( row, COLS-1, ACS_LRCORNER );
+
+    refresh();
+
+    *t_last_refresh = time( 0 );
+}
+
+static void
+ncurses_Fullscreen ( void )
+{
+    vlc_mutex_lock( &p_vout_bank->pp_vout[0]->change_lock );
+
+    p_vout_bank->pp_vout[0]->i_changes |= VOUT_FULLSCREEN_CHANGE;
+
+    vlc_mutex_unlock( &p_vout_bank->pp_vout[0]->change_lock );
+}
+
+static void
+ncurses_Eject ( void )
+{
+    char *psz_device = NULL;
+    char *psz_parser;
+
+    /*
+     * Get the active input
+     * Determine whether we can eject a media, ie it's a VCD or DVD
+     * If it's neither a VCD nor a DVD, then return
+     */
+
+    /*
+     * Don't really know if I must lock the stuff here, we're using it read-only
+     */
+
+    if (p_main->p_playlist->current.psz_name != NULL)
+    {
+        if( !strncmp(p_main->p_playlist->current.psz_name, "dvd:", 4) )
+        {
+            switch( p_main->p_playlist->current.psz_name[4] )
+            {
+            case '\0':
+            case '@':
+                psz_device = config_GetPszVariable( "dvd_device" );
+                break;
+            default:
+                /* Omit the first 4 characters */
+                psz_device = strdup( p_main->p_playlist->current.psz_name + 4 );
+                break;
+            }
+        }
+        else if( !strncmp(p_main->p_playlist->current.psz_name, "vcd:", 4) )
+        {
+            switch( p_main->p_playlist->current.psz_name[4] )
+            {
+            case '\0':
+            case '@':
+                psz_device = config_GetPszVariable( "vcd_device" );
+                break;
+            default:
+                /* Omit the first 4 characters */
+                psz_device = strdup( p_main->p_playlist->current.psz_name + 4 );
+                break;
+            }
+        }
+        else
+        {
+            psz_device = strdup( p_main->p_playlist->current.psz_name );
+        }
+    }
+
+    if( psz_device == NULL )
+    {
+        return;
+    }
+
+    /* Remove what we have after @ */
+    psz_parser = psz_device;
+    for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
+    {
+        if( *psz_parser == '@' )
+        {
+            *psz_parser = '\0';
+            break;
+        }
+    }
+
+    /* If there's a stream playing, we aren't allowed to eject ! */
+    if( p_input_bank->pp_input[0] == NULL )
+    {
+        intf_WarnMsg( 4, "intf: ejecting %s", psz_device );
+
+        intf_Eject( psz_device );
+    }
+
+    free(psz_device);
+    return;
+}
+
+static void
+ncurses_Play ( void )
+{
+    if( p_input_bank->pp_input[0] != NULL )
+    {
+        input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
+        p_main->p_playlist->b_stopped = 0;
+    }
+    else
+    {
+        vlc_mutex_lock( &p_main->p_playlist->change_lock );
+
+        if( p_main->p_playlist->b_stopped )
+        {
+            if( p_main->p_playlist->i_size )
+            {
+                vlc_mutex_unlock( &p_main->p_playlist->change_lock );
+                intf_PlaylistJumpto( p_main->p_playlist,
+                                     p_main->p_playlist->i_index );
+            }
+            else
+            {
+                vlc_mutex_unlock( &p_main->p_playlist->change_lock );
+            }
+        }
+        else
+        {
+
+            vlc_mutex_unlock( &p_main->p_playlist->change_lock );
+        }
+
+    }
+}
+
+static void
+ncurses_Pause ( void )
+{
+    if( p_input_bank->pp_input[0] != NULL )
+    {
+        if ( p_input_bank->pp_input[0]->i_status & INPUT_STATUS_PLAY )
+        {
+            input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PAUSE );
+
+            vlc_mutex_lock( &p_main->p_playlist->change_lock );
+            p_main->p_playlist->b_stopped = 0;
+            vlc_mutex_unlock( &p_main->p_playlist->change_lock );
+        }
+        else
+        {
+            ncurses_Play ();
+        }
+    }
+}
+
+static void
+ncurses_Stop ( void )
+{
+    if( p_input_bank->pp_input[0] != NULL )
+    {
+        /* end playing item */
+        p_input_bank->pp_input[0]->b_eof = 1;
+
+        /* update playlist */
+        vlc_mutex_lock( &p_main->p_playlist->change_lock );
+
+        p_main->p_playlist->i_index--;
+        p_main->p_playlist->b_stopped = 1;
+
+        vlc_mutex_unlock( &p_main->p_playlist->change_lock );
+
+    }
+}
+
+static void
+ncurses_Next ( void )
+{
+    int i_id;
+    input_area_t * p_area;
+
+    i_id = p_input_bank->pp_input[0]->stream.p_selected_area->i_id+1;
+
+    if ( i_id < p_input_bank->pp_input[0]->stream.i_area_nb )
+    {
+        p_area = p_input_bank->pp_input[0]->stream.pp_areas[i_id];
+
+        input_ChangeArea( p_input_bank->pp_input[0],
+                (input_area_t *) p_area );
+
+        input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
+    }
+}
+
+static void
+ncurses_manageSlider ( intf_thread_t *p_intf )
+{
+    if( p_input_bank->pp_input[0] != NULL )
+    {
+        vlc_mutex_lock( &p_input_bank->pp_input[0]->stream.stream_lock );
+
+        if( p_input_bank->pp_input[0]->stream.b_seekable &&
+            p_input_bank->pp_input[0]->i_status & INPUT_STATUS_PLAY )
+        {
+            float newvalue = p_intf->p_sys->f_slider_state;
+
+#define p_area p_input_bank->pp_input[0]->stream.p_selected_area
+
+            /* If the user hasn't touched the slider since the last time,
+             * then the input can safely change it */
+            if( newvalue == p_intf->p_sys->f_slider_state_old )
+            {
+                /* Update the value */
+                p_intf->p_sys->f_slider_state =
+                    p_intf->p_sys->f_slider_state_old =
+                    ( 100 * p_area->i_tell ) / p_area->i_size;
+            }
+            /* Otherwise, send message to the input if the user has
+             * finished dragging the slider */
+            else
+            {
+                off_t i_seek = ( newvalue * p_area->i_size ) / 100;
+
+                /* release the lock to be able to seek */
+                vlc_mutex_unlock( &p_input_bank->pp_input[0]->stream.stream_lock );
+                input_Seek( p_input_bank->pp_input[0], i_seek );
+                vlc_mutex_lock( &p_input_bank->pp_input[0]->stream.stream_lock );
+
+                /* Update the old value */
+                p_intf->p_sys->f_slider_state_old = newvalue;
+            }
+#    undef p_area
+        }
+
+        vlc_mutex_unlock( &p_input_bank->pp_input[0]->stream.stream_lock );
+    }
+}
+
+static void
+ncurses_TitlePrev ( void )
+{
+    input_area_t *  p_area;
+    int             i_id;
+
+    i_id = p_input_bank->pp_input[0]->stream.p_selected_area->i_id - 1;
+
+    /* Disallow area 0 since it is used for video_ts.vob */
+    if ( i_id > 0 )
+    {
+        p_area = p_input_bank->pp_input[0]->stream.pp_areas[i_id];
+        input_ChangeArea( p_input_bank->pp_input[0], (input_area_t*)p_area );
+
+        input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
+    }
+}
+
+static void
+ncurses_TitleNext ( void )
+{
+    input_area_t *  p_area;
+    int             i_id;
+
+    i_id = p_input_bank->pp_input[0]->stream.p_selected_area->i_id + 1;
+
+    if ( i_id < p_input_bank->pp_input[0]->stream.i_area_nb )
+    {
+        p_area = p_input_bank->pp_input[0]->stream.pp_areas[i_id];
+        input_ChangeArea( p_input_bank->pp_input[0], (input_area_t*)p_area );
+
+        input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
+    }
+}
+
+static void
+ncurses_ChapterPrev ( void )
+{
+    input_area_t *  p_area;
+
+    p_area = p_input_bank->pp_input[0]->stream.p_selected_area;
+
+    if ( p_area->i_part > 0 )
+    {
+        p_area->i_part--;
+        input_ChangeArea( p_input_bank->pp_input[0], (input_area_t*)p_area );
+
+        input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
+    }
+}
+
+static void
+ncurses_ChapterNext ( void )
+{
+    input_area_t *  p_area;
+
+    p_area = p_input_bank->pp_input[0]->stream.p_selected_area;
+
+    if ( p_area->i_part < p_area->i_part_nb )
+    {
+        p_area->i_part++;
+        input_ChangeArea( p_input_bank->pp_input[0], (input_area_t*)p_area );
+
+        input_SetStatus( p_input_bank->pp_input[0], INPUT_STATUS_PLAY );
+    }
+}