ncurses.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:85k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * ncurses.c : NCurses interface for vlc
  3.  *****************************************************************************
  4.  * Copyright © 2001-2007 the VideoLAN team
  5.  * $Id: 2e815993932f817dc805a0316d87c210b7ba66ea $
  6.  *
  7.  * Authors: Sam Hocevar <sam@zoy.org>
  8.  *          Laurent Aimar <fenrir@via.ecp.fr>
  9.  *          Yoann Peronneau <yoann@videolan.org>
  10.  *          Derk-Jan Hartman <hartman at videolan dot org>
  11.  *          Rafaël Carré <funman@videolanorg>
  12.  *
  13.  * This program is free software; you can redistribute it and/or modify
  14.  * it under the terms of the GNU General Public License as published by
  15.  * the Free Software Foundation; either version 2 of the License, or
  16.  * (at your option) any later version.
  17.  *
  18.  * This program is distributed in the hope that it will be useful,
  19.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.  * GNU General Public License for more details.
  22.  *
  23.  * You should have received a copy of the GNU General Public License
  24.  * along with this program; if not, write to the Free Software
  25.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  26.  *****************************************************************************/
  27. /*
  28.  * Note that when we use wide characters (and link with libncursesw),
  29.  * we assume that an UTF8 locale is used (or compatible, such as ASCII).
  30.  * Other characters encodings are not supported.
  31.  */
  32. /*****************************************************************************
  33.  * Preamble
  34.  *****************************************************************************/
  35. #ifdef HAVE_CONFIG_H
  36. # include "config.h"
  37. #endif
  38. #include <vlc_common.h>
  39. #include <vlc_plugin.h>
  40. #ifdef HAVE_NCURSESW
  41. #   define _XOPEN_SOURCE_EXTENDED 1
  42. #   include <wchar.h>
  43. #endif
  44. #include <ncurses.h>
  45. #include <vlc_interface.h>
  46. #include <vlc_vout.h>
  47. #include <vlc_aout.h>
  48. #include <vlc_charset.h>
  49. #include <vlc_input.h>
  50. #include <vlc_es.h>
  51. #include <vlc_playlist.h>
  52. #include <vlc_meta.h>
  53. #include <assert.h>
  54. #ifdef HAVE_SYS_STAT_H
  55. #   include <sys/stat.h>
  56. #endif
  57. #if (!defined( WIN32 ) || defined(__MINGW32__))
  58. /* Mingw has its own version of dirent */
  59. #   include <dirent.h>
  60. #endif
  61. #ifdef HAVE_CDDAX
  62. #define CDDA_MRL "cddax://"
  63. #else
  64. #define CDDA_MRL "cdda://"
  65. #endif
  66. #ifdef HAVE_VCDX
  67. #define VCD_MRL "vcdx://"
  68. #else
  69. #define VCD_MRL "vcd://"
  70. #endif
  71. #define SEARCH_CHAIN_SIZE 20
  72. #define OPEN_CHAIN_SIZE 50
  73. /*****************************************************************************
  74.  * Local prototypes.
  75.  *****************************************************************************/
  76. static int  Open           ( vlc_object_t * );
  77. static void Close          ( vlc_object_t * );
  78. static void Run            ( intf_thread_t * );
  79. static void PlayPause      ( intf_thread_t * );
  80. static void Eject          ( intf_thread_t * );
  81. static int  HandleKey      ( intf_thread_t *, int );
  82. static void Redraw         ( intf_thread_t *, time_t * );
  83. static playlist_item_t *PlaylistGetRoot( intf_thread_t * );
  84. static void PlaylistRebuild( intf_thread_t * );
  85. static void PlaylistAddNode( intf_thread_t *, playlist_item_t *, int, const char *);
  86. static void PlaylistDestroy( intf_thread_t * );
  87. static int  PlaylistChanged( vlc_object_t *, const char *, vlc_value_t,
  88.                              vlc_value_t, void * );
  89. static inline bool PlaylistIsPlaying( playlist_t *, playlist_item_t * );
  90. static void FindIndex      ( intf_thread_t *, playlist_t * );
  91. static void SearchPlaylist ( intf_thread_t *, char * );
  92. static int  SubSearchPlaylist( intf_thread_t *, char *, int, int );
  93. static void ManageSlider   ( intf_thread_t * );
  94. static void ReadDir        ( intf_thread_t * );
  95. static void start_color_and_pairs ( intf_thread_t * );
  96. /*****************************************************************************
  97.  * Module descriptor
  98.  *****************************************************************************/
  99. #define BROWSE_TEXT N_("Filebrowser starting point")
  100. #define BROWSE_LONGTEXT N_( 
  101.     "This option allows you to specify the directory the ncurses filebrowser " 
  102.     "will show you initially.")
  103. vlc_module_begin ()
  104.     set_shortname( "Ncurses" )
  105.     set_description( N_("Ncurses interface") )
  106.     set_capability( "interface", 10 )
  107.     set_category( CAT_INTERFACE )
  108.     set_subcategory( SUBCAT_INTERFACE_MAIN )
  109.     set_callbacks( Open, Close )
  110.     add_shortcut( "curses" )
  111.     add_directory( "browse-dir", NULL, NULL, BROWSE_TEXT, BROWSE_LONGTEXT, false )
  112. vlc_module_end ()
  113. /*****************************************************************************
  114.  * intf_sys_t: description and status of ncurses interface
  115.  *****************************************************************************/
  116. enum
  117. {
  118.     BOX_NONE,
  119.     BOX_HELP,
  120.     BOX_INFO,
  121.     BOX_LOG,
  122.     BOX_PLAYLIST,
  123.     BOX_SEARCH,
  124.     BOX_OPEN,
  125.     BOX_BROWSE,
  126.     BOX_META,
  127.     BOX_OBJECTS,
  128.     BOX_STATS
  129. };
  130. enum
  131. {
  132.     C_DEFAULT = 0,
  133.     C_TITLE,
  134.     C_PLAYLIST_1,
  135.     C_PLAYLIST_2,
  136.     C_PLAYLIST_3,
  137.     C_BOX,
  138.     C_STATUS,
  139.     C_INFO,
  140.     C_ERROR,
  141.     C_WARNING,
  142.     C_DEBUG,
  143.     C_CATEGORY,
  144.     C_FOLDER
  145. };
  146. enum
  147. {
  148.     VIEW_CATEGORY,
  149.     VIEW_ONELEVEL
  150. };
  151. struct dir_entry_t
  152. {
  153.     bool  b_file;
  154.     char        *psz_path;
  155. };
  156. struct pl_item_t
  157. {
  158.     playlist_item_t *p_item;
  159.     char            *psz_display;
  160. };
  161. struct intf_sys_t
  162. {
  163.     input_thread_t *p_input;
  164.     playlist_t     *p_playlist;
  165.     bool      b_color;
  166.     bool      b_color_started;
  167.     float           f_slider;
  168.     float           f_slider_old;
  169.     WINDOW          *w;
  170.     int             i_box_type;
  171.     int             i_box_y;
  172.     int             i_box_lines;
  173.     int             i_box_lines_total;
  174.     int             i_box_start;
  175.     int             i_box_plidx;    /* Playlist index */
  176.     int             b_box_plidx_follow;
  177.     int             i_box_bidx;     /* browser index */
  178.     playlist_item_t *p_node;        /* current node */
  179.     int             b_box_cleared;
  180.     msg_subscription_t* p_sub;                  /* message bank subscription */
  181.     char            *psz_search_chain;          /* for playlist searching    */
  182.     char            *psz_old_search;            /* for searching next        */
  183.     int             i_before_search;
  184.     char            *psz_open_chain;
  185. #ifndef HAVE_NCURSESW
  186.     char             psz_partial_keys[7];
  187. #endif
  188.     char            *psz_current_dir;
  189.     int             i_dir_entries;
  190.     struct dir_entry_t  **pp_dir_entries;
  191.     bool      b_show_hidden_files;
  192.     int             i_current_view;             /* playlist view             */
  193.     struct pl_item_t    **pp_plist;
  194.     int             i_plist_entries;
  195.     bool      b_need_update;              /* for playlist view         */
  196. };
  197. static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title, bool b_color );
  198. static void DrawLine( WINDOW *win, int y, int x, int w );
  199. static void DrawEmptyLine( WINDOW *win, int y, int x, int w );
  200. /*****************************************************************************
  201.  * Open: initialize and create window
  202.  *****************************************************************************/
  203. static int Open( vlc_object_t *p_this )
  204. {
  205.     intf_thread_t *p_intf = (intf_thread_t *)p_this;
  206.     intf_sys_t    *p_sys;
  207.     /* Allocate instance and initialize some members */
  208.     p_sys = p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
  209.     if( !p_sys )
  210.         return VLC_ENOMEM;
  211.     p_sys->p_node = NULL;
  212.     p_sys->p_input = NULL;
  213.     p_sys->f_slider = 0.0;
  214.     p_sys->f_slider_old = 0.0;
  215.     p_sys->i_box_type = BOX_PLAYLIST;
  216.     p_sys->i_box_lines = 0;
  217.     p_sys->i_box_start= 0;
  218.     p_sys->i_box_lines_total = 0;
  219.     p_sys->b_box_plidx_follow = true;
  220.     p_sys->b_box_cleared = false;
  221.     p_sys->i_box_plidx = 0;
  222.     p_sys->i_box_bidx = 0;
  223. // FIXME    p_sys->p_sub = msg_Subscribe( p_intf );
  224.     p_sys->b_color = var_CreateGetBool( p_intf, "color" );
  225.     p_sys->b_color_started = false;
  226. #ifndef HAVE_NCURSESW
  227.     memset( p_sys->psz_partial_keys, 0, sizeof( p_sys->psz_partial_keys ) );
  228. #endif
  229.     /* Initialize the curses library */
  230.     p_sys->w = initscr();
  231.     if( p_sys->b_color )
  232.         start_color_and_pairs( p_intf );
  233.     keypad( p_sys->w, TRUE );
  234.     /* Don't do NL -> CR/NL */
  235.     nonl();
  236.     /* Take input chars one at a time */
  237.     cbreak();
  238.     /* Don't echo */
  239.     noecho();
  240.     /* Invisible cursor */
  241.     curs_set( 0 );
  242.     /* Non blocking wgetch() */
  243.     wtimeout( p_sys->w, 0 );
  244.     clear();
  245.     /* exported function */
  246.     p_intf->pf_run = Run;
  247.     /* Stop printing errors to the console */
  248.     freopen( "/dev/null", "wb", stderr );
  249.     /* Set defaul playlist view */
  250.     p_sys->i_current_view = VIEW_CATEGORY;
  251.     p_sys->pp_plist = NULL;
  252.     p_sys->i_plist_entries = 0;
  253.     p_sys->b_need_update = false;
  254.     /* Initialize search chain */
  255.     p_sys->psz_search_chain = malloc( SEARCH_CHAIN_SIZE + 1 );
  256.     p_sys->psz_old_search = NULL;
  257.     p_sys->i_before_search = 0;
  258.     /* Initialize open chain */
  259.     p_sys->psz_open_chain = malloc( OPEN_CHAIN_SIZE + 1 );
  260.     /* Initialize browser options */
  261.     char* psz_tmp = var_CreateGetString( p_intf, "browse-dir" );
  262.     if( psz_tmp && *psz_tmp )
  263.         p_sys->psz_current_dir = psz_tmp;
  264.     else
  265.     {
  266.         p_sys->psz_current_dir = strdup( config_GetHomeDir() );
  267.         free( psz_tmp );
  268.     }
  269.     p_sys->i_dir_entries = 0;
  270.     p_sys->pp_dir_entries = NULL;
  271.     p_sys->b_show_hidden_files = false;
  272.     ReadDir( p_intf );
  273.     return VLC_SUCCESS;
  274. }
  275. /*****************************************************************************
  276.  * Close: destroy interface window
  277.  *****************************************************************************/
  278. static void Close( vlc_object_t *p_this )
  279. {
  280.     intf_thread_t *p_intf = (intf_thread_t *)p_this;
  281.     intf_sys_t    *p_sys = p_intf->p_sys;
  282.     PlaylistDestroy( p_intf );
  283.     while( p_sys->i_dir_entries )
  284.     {
  285.         struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[0];
  286.         free( p_dir_entry->psz_path );
  287.         REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, 0 );
  288.         free( p_dir_entry );
  289.     }
  290.     p_sys->pp_dir_entries = NULL;
  291.     free( p_sys->psz_current_dir );
  292.     free( p_sys->psz_search_chain );
  293.     free( p_sys->psz_old_search );
  294.     free( p_sys->psz_open_chain );
  295.     if( p_sys->p_input )
  296.     {
  297.         vlc_object_release( p_sys->p_input );
  298.     }
  299.     pl_Release( p_intf );
  300.     /* Close the ncurses interface */
  301.     endwin();
  302. // FIXME    msg_Unsubscribe( p_intf, p_sys->p_sub );
  303.     /* Destroy structure */
  304.     free( p_sys );
  305. }
  306. /*****************************************************************************
  307.  * Run: ncurses thread
  308.  *****************************************************************************/
  309. static void Run( intf_thread_t *p_intf )
  310. {
  311.     intf_sys_t    *p_sys = p_intf->p_sys;
  312.     playlist_t    *p_playlist = pl_Hold( p_intf );
  313.     p_sys->p_playlist = p_playlist;
  314.     int i_key;
  315.     time_t t_last_refresh;
  316.     int canc = vlc_savecancel();
  317.     /*
  318.      * force drawing the interface for the first time
  319.      */
  320.     t_last_refresh = ( time( 0 ) - 1);
  321.     /*
  322.      * force building of the playlist array
  323.      */
  324.     PlaylistRebuild( p_intf );
  325.     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, p_intf );
  326.     var_AddCallback( p_playlist, "playlist-item-append", PlaylistChanged, p_intf );
  327.     while( vlc_object_alive( p_intf ) )
  328.     {
  329.         msleep( INTF_IDLE_SLEEP );
  330.         /* Update the input */
  331.         if( p_sys->p_input == NULL )
  332.         {
  333.             p_sys->p_input = playlist_CurrentInput( p_playlist );
  334.         }
  335.         else if( p_sys->p_input->b_dead )
  336.         {
  337.             vlc_object_release( p_sys->p_input );
  338.             p_sys->p_input = NULL;
  339.             p_sys->f_slider = p_sys->f_slider_old = 0.0;
  340.             p_sys->b_box_cleared = false;
  341.         }
  342.         PL_LOCK;
  343.         if( p_sys->b_box_plidx_follow && playlist_CurrentPlayingItem(p_playlist) )
  344.         {
  345.             PL_UNLOCK;
  346.             FindIndex( p_intf, p_playlist );
  347.         }
  348.         else
  349.             PL_UNLOCK;
  350.         while( ( i_key = wgetch( p_sys->w ) ) != -1 )
  351.         {
  352.             /*
  353.              * HandleKey returns 1 if the screen needs to be redrawn
  354.              */
  355.             if( HandleKey( p_intf, i_key ) )
  356.             {
  357.                 Redraw( p_intf, &t_last_refresh );
  358.             }
  359.         }
  360.         /* Hack */
  361.         if( p_sys->f_slider > 0.0001 && !p_sys->b_box_cleared )
  362.         {
  363.             clear();
  364.             Redraw( p_intf, &t_last_refresh );
  365.             p_sys->b_box_cleared = true;
  366.         }
  367.         /*
  368.          * redraw the screen every second
  369.          */
  370.         if( (time(0) - t_last_refresh) >= 1 )
  371.         {
  372.             ManageSlider( p_intf );
  373.             Redraw( p_intf, &t_last_refresh );
  374.         }
  375.     }
  376.     var_DelCallback( p_playlist, "intf-change", PlaylistChanged, p_intf );
  377.     var_DelCallback( p_playlist, "playlist-item-append", PlaylistChanged, p_intf );
  378.     vlc_restorecancel( canc );
  379. }
  380. /* following functions are local */
  381. static void start_color_and_pairs( intf_thread_t *p_intf )
  382. {
  383.     assert( p_intf->p_sys->b_color && !p_intf->p_sys->b_color_started );
  384.     if( !has_colors() )
  385.     {
  386.         p_intf->p_sys->b_color = false;
  387.         msg_Warn( p_intf, "Terminal doesn't support colors" );
  388.         return;
  389.     }
  390.     start_color();
  391.     /* Available colors: BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE */
  392.     /* untested, in all my terminals, !can_change_color() --funman */
  393.     if( can_change_color() )
  394.         init_color( COLOR_YELLOW, 960, 500, 0 ); /* YELLOW -> ORANGE */
  395.     /* title */
  396.     init_pair( C_TITLE, COLOR_YELLOW, COLOR_BLACK );
  397.     /* jamaican playlist */
  398.     init_pair( C_PLAYLIST_1, COLOR_GREEN, COLOR_BLACK );
  399.     init_pair( C_PLAYLIST_2, COLOR_YELLOW, COLOR_BLACK );
  400.     init_pair( C_PLAYLIST_3, COLOR_RED, COLOR_BLACK );
  401.     /* used in DrawBox() */
  402.     init_pair( C_BOX, COLOR_CYAN, COLOR_BLACK );
  403.     /* Source, State, Position, Volume, Chapters, etc...*/
  404.     init_pair( C_STATUS, COLOR_BLUE, COLOR_BLACK );
  405.     /* VLC messages, keep the order from highest priority to lowest */
  406.     /* infos */
  407.     init_pair( C_INFO, COLOR_BLACK, COLOR_WHITE );
  408.     /* errors */
  409.     init_pair( C_ERROR, COLOR_RED, COLOR_BLACK );
  410.     /* warnings */
  411.     init_pair( C_WARNING, COLOR_YELLOW, COLOR_BLACK );
  412. /* debug */
  413.     init_pair( C_DEBUG, COLOR_WHITE, COLOR_BLACK );
  414.     /* Category title (help, info, metadata) */
  415.     init_pair( C_CATEGORY, COLOR_MAGENTA, COLOR_BLACK );
  416.     /* Folder (BOX_BROWSE) */
  417.     init_pair( C_FOLDER, COLOR_RED, COLOR_BLACK );
  418.     p_intf->p_sys->b_color_started = true;
  419. }
  420. #ifndef HAVE_NCURSESW
  421. static char *KeyToUTF8( int i_key, char *psz_part )
  422. {
  423.     char *psz_utf8;
  424.     int len = strlen( psz_part );
  425.     if( len == 6 )
  426.     {
  427.         /* overflow error - should not happen */
  428.         memset( psz_part, 0, 6 );
  429.         return NULL;
  430.     }
  431.     psz_part[len] = (char)i_key;
  432.     psz_utf8 = FromLocaleDup( psz_part );
  433.     /* Ugly check for incomplete bytes sequences
  434.      * (in case of non-UTF8 multibyte local encoding) */
  435.     char *psz;
  436.     for( psz = psz_utf8; *psz; psz++ )
  437.         if( ( *psz == '?' ) && ( *psz_utf8 != '?' ) )
  438.         {
  439.             /* incomplete bytes sequence detected
  440.              * (VLC core inserted dummy question marks) */
  441.             free( psz_utf8 );
  442.             return NULL;
  443.         }
  444.     /* Check for incomplete UTF8 bytes sequence */
  445.     if( EnsureUTF8( psz_utf8 ) == NULL )
  446.     {
  447.         free( psz_utf8 );
  448.         return NULL;
  449.     }
  450.     memset( psz_part, 0, 6 );
  451.     return psz_utf8;
  452. }
  453. #endif
  454. static inline int RemoveLastUTF8Entity( char *psz, int len )
  455. {
  456.     while( len && ( (psz[--len] & 0xc0) == 0x80 ) );
  457.                        /* UTF8 continuation byte */
  458.     psz[len] = '';
  459.     return len;
  460. }
  461. static int HandleKey( intf_thread_t *p_intf, int i_key )
  462. {
  463.     intf_sys_t *p_sys = p_intf->p_sys;
  464.     vlc_value_t val;
  465.     #define ReturnTrue 
  466.     do { 
  467.     pl_Release( p_intf ); 
  468.     return 1; 
  469.     } while(0)
  470.     #define ReturnFalse 
  471.     do { 
  472.     pl_Release( p_intf ); 
  473.     return 0; 
  474.     } while(0)
  475.     playlist_t *p_playlist = pl_Hold( p_intf );
  476.     if( p_sys->i_box_type == BOX_PLAYLIST )
  477.     {
  478.         int b_ret = true;
  479.         bool b_box_plidx_follow = false;
  480.         switch( i_key )
  481.         {
  482.             vlc_value_t val;
  483.             /* Playlist Settings */
  484.             case 'r':
  485.                 var_Get( p_playlist, "random", &val );
  486.                 val.b_bool = !val.b_bool;
  487.                 var_Set( p_playlist, "random", val );
  488.                 ReturnTrue;
  489.             case 'l':
  490.                 var_Get( p_playlist, "loop", &val );
  491.                 val.b_bool = !val.b_bool;
  492.                 var_Set( p_playlist, "loop", val );
  493.                 ReturnTrue;
  494.             case 'R':
  495.                 var_Get( p_playlist, "repeat", &val );
  496.                 val.b_bool = !val.b_bool;
  497.                 var_Set( p_playlist, "repeat", val );
  498.                 ReturnTrue;
  499.             /* Playlist sort */
  500.             case 'o':
  501.                 playlist_RecursiveNodeSort( p_playlist,
  502.                                             PlaylistGetRoot( p_intf ),
  503.                                             SORT_TITLE_NODES_FIRST, ORDER_NORMAL );
  504.                 p_sys->b_need_update = true;
  505.                 ReturnTrue;
  506.             case 'O':
  507.                 playlist_RecursiveNodeSort( p_playlist,
  508.                                             PlaylistGetRoot( p_intf ),
  509.                                             SORT_TITLE_NODES_FIRST, ORDER_REVERSE );
  510.                 p_sys->b_need_update = true;
  511.                 ReturnTrue;
  512.             /* Playlist view */
  513.             case 'v':
  514.                 switch( p_sys->i_current_view )
  515.                 {
  516.                     case VIEW_CATEGORY:
  517.                         p_sys->i_current_view = VIEW_ONELEVEL;
  518.                         break;
  519.                     default:
  520.                         p_sys->i_current_view = VIEW_CATEGORY;
  521.                 }
  522.                 //p_sys->b_need_update = true;
  523.                 PlaylistRebuild( p_intf );
  524.                 ReturnTrue;
  525.             /* Playlist navigation */
  526.             case 'g':
  527.                 FindIndex( p_intf, p_playlist );
  528.                 break;
  529.             case KEY_HOME:
  530.                 p_sys->i_box_plidx = 0;
  531.                 break;
  532. #ifdef __FreeBSD__
  533. /* workaround for FreeBSD + xterm:
  534.  * see http://www.nabble.com/curses-vs.-xterm-key-mismatch-t3574377.html */
  535.             case KEY_SELECT:
  536. #endif
  537.             case KEY_END:
  538.                 p_sys->i_box_plidx = p_playlist->items.i_size - 1;
  539.                 break;
  540.             case KEY_UP:
  541.                 p_sys->i_box_plidx--;
  542.                 break;
  543.             case KEY_DOWN:
  544.                 p_sys->i_box_plidx++;
  545.                 break;
  546.             case KEY_PPAGE:
  547.                 p_sys->i_box_plidx -= p_sys->i_box_lines;
  548.                 break;
  549.             case KEY_NPAGE:
  550.                 p_sys->i_box_plidx += p_sys->i_box_lines;
  551.                 break;
  552.             case 'D':
  553.             case KEY_BACKSPACE:
  554.             case 0x7f:
  555.             case KEY_DC:
  556.             {
  557.                 playlist_item_t *p_item;
  558.                 PL_LOCK;
  559.                 p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
  560.                 if( p_item->i_children == -1 )
  561.                 {
  562.                     playlist_DeleteFromInput( p_playlist,
  563.                                               p_item->p_input->i_id, pl_Locked );
  564.                 }
  565.                 else
  566.                 {
  567.                     playlist_NodeDelete( p_playlist, p_item,
  568.                                          true , false );
  569.                 }
  570.                 PL_UNLOCK;
  571.                 PlaylistRebuild( p_intf );
  572.                 break;
  573.             }
  574.             case KEY_ENTER:
  575.             case 'r':
  576.             case 'n':
  577.                 if( !p_sys->pp_plist[p_sys->i_box_plidx] )
  578.                 {
  579.                     b_ret = false;
  580.                     break;
  581.                 }
  582.                 if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
  583.                         == -1 )
  584.                 {
  585.                     playlist_item_t *p_item, *p_parent;
  586.                     p_item = p_parent =
  587.                             p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
  588.                     if( !p_parent )
  589.                         p_parent = p_playlist->p_root_onelevel;
  590.                     while( p_parent->p_parent )
  591.                         p_parent = p_parent->p_parent;
  592.                     playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
  593.                                       pl_Unlocked, p_parent, p_item );
  594.                 }
  595.                 else if( p_sys->pp_plist[p_sys->i_box_plidx]->p_item->i_children
  596.                         == 0 )
  597.                 {   /* We only want to set the current node */
  598.                     playlist_Stop( p_playlist );
  599.                     p_sys->p_node = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
  600.                 }
  601.                 else
  602.                 {
  603.                     p_sys->p_node = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
  604.                     playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Unlocked,
  605.                         p_sys->pp_plist[p_sys->i_box_plidx]->p_item, NULL );
  606.                 }
  607.                 b_box_plidx_follow = true;
  608.                 break;
  609.             default:
  610.                 b_ret = false;
  611.                 break;
  612.         }
  613.         if( b_ret )
  614.         {
  615.             int i_max = p_sys->i_plist_entries;
  616.             if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
  617.             if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
  618.             PL_LOCK;
  619.             if( PlaylistIsPlaying( p_playlist,
  620.                                    p_sys->pp_plist[p_sys->i_box_plidx]->p_item ) )
  621.                 b_box_plidx_follow = true;
  622.             PL_UNLOCK;
  623.             p_sys->b_box_plidx_follow = b_box_plidx_follow;
  624.             ReturnTrue;
  625.         }
  626.     }
  627.     if( p_sys->i_box_type == BOX_BROWSE )
  628.     {
  629.         bool b_ret = true;
  630.         /* Browser navigation */
  631.         switch( i_key )
  632.         {
  633.             case KEY_HOME:
  634.                 p_sys->i_box_bidx = 0;
  635.                 break;
  636. #ifdef __FreeBSD__
  637.             case KEY_SELECT:
  638. #endif
  639.             case KEY_END:
  640.                 p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
  641.                 break;
  642.             case KEY_UP:
  643.                 p_sys->i_box_bidx--;
  644.                 break;
  645.             case KEY_DOWN:
  646.                 p_sys->i_box_bidx++;
  647.                 break;
  648.             case KEY_PPAGE:
  649.                 p_sys->i_box_bidx -= p_sys->i_box_lines;
  650.                 break;
  651.             case KEY_NPAGE:
  652.                 p_sys->i_box_bidx += p_sys->i_box_lines;
  653.                 break;
  654.             case '.': /* Toggle show hidden files */
  655.                 p_sys->b_show_hidden_files = ( p_sys->b_show_hidden_files ==
  656.                     true ? false : true );
  657.                 ReadDir( p_intf );
  658.                 break;
  659.             case KEY_ENTER:
  660.             case 'r':
  661.             case 'n':
  662.             case ' ':
  663.                 if( p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file || i_key == ' ' )
  664.                 {
  665.                     char* psz_uri;
  666.                     if( asprintf( &psz_uri, "%s://%s/%s",
  667.                         p_sys->pp_dir_entries[p_sys->i_box_bidx]->b_file ?
  668.                             "file" : "directory",
  669.                         p_sys->psz_current_dir,
  670.                         p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path
  671.                         ) == -1 )
  672.                     {
  673.                         psz_uri = NULL;
  674.                     }
  675.                     playlist_item_t *p_parent = p_sys->p_node;
  676.                     if( !p_parent )
  677.                     {
  678.                         PL_LOCK;
  679.                         p_parent = playlist_CurrentPlayingItem(p_playlist) ? playlist_CurrentPlayingItem(p_playlist)->p_parent : NULL;
  680.                         PL_UNLOCK;
  681.                         if( !p_parent )
  682.                             p_parent = p_playlist->p_local_onelevel;
  683.                     }
  684.                     while( p_parent->p_parent && p_parent->p_parent->p_parent )
  685.                         p_parent = p_parent->p_parent;
  686.                     playlist_Add( p_playlist, psz_uri, NULL, PLAYLIST_APPEND,
  687.                                   PLAYLIST_END,
  688.                                   p_parent->p_input ==
  689.                                     p_playlist->p_local_onelevel->p_input
  690.                                   , false );
  691.                     p_sys->i_box_type = BOX_PLAYLIST;
  692.                     free( psz_uri );
  693.                 }
  694.                 else
  695.                 {
  696.                     if( asprintf( &(p_sys->psz_current_dir), "%s/%s", p_sys->psz_current_dir,
  697.                                   p_sys->pp_dir_entries[p_sys->i_box_bidx]->psz_path ) != -1 )
  698.                         ReadDir( p_intf );
  699.                 }
  700.                 break;
  701.             default:
  702.                 b_ret = false;
  703.                 break;
  704.         }
  705.         if( b_ret )
  706.         {
  707.             if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_bidx = p_sys->i_dir_entries - 1;
  708.             if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
  709.             ReturnTrue;
  710.         }
  711.     }
  712.     else if( p_sys->i_box_type == BOX_HELP || p_sys->i_box_type == BOX_INFO ||
  713.              p_sys->i_box_type == BOX_META || p_sys->i_box_type == BOX_STATS ||
  714.              p_sys->i_box_type == BOX_OBJECTS )
  715.     {
  716.         switch( i_key )
  717.         {
  718.             case KEY_HOME:
  719.                 p_sys->i_box_start = 0;
  720.                 ReturnTrue;
  721. #ifdef __FreeBSD__
  722.             case KEY_SELECT:
  723. #endif
  724.             case KEY_END:
  725.                 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
  726.                 ReturnTrue;
  727.             case KEY_UP:
  728.                 if( p_sys->i_box_start > 0 ) p_sys->i_box_start--;
  729.                 ReturnTrue;
  730.             case KEY_DOWN:
  731.                 if( p_sys->i_box_start < p_sys->i_box_lines_total - 1 )
  732.                 {
  733.                     p_sys->i_box_start++;
  734.                 }
  735.                 ReturnTrue;
  736.             case KEY_PPAGE:
  737.                 p_sys->i_box_start -= p_sys->i_box_lines;
  738.                 if( p_sys->i_box_start < 0 ) p_sys->i_box_start = 0;
  739.                 ReturnTrue;
  740.             case KEY_NPAGE:
  741.                 p_sys->i_box_start += p_sys->i_box_lines;
  742.                 if( p_sys->i_box_start >= p_sys->i_box_lines_total )
  743.                 {
  744.                     p_sys->i_box_start = p_sys->i_box_lines_total - 1;
  745.                 }
  746.                 ReturnTrue;
  747.             default:
  748.                 break;
  749.         }
  750.     }
  751.     else if( p_sys->i_box_type == BOX_NONE )
  752.     {
  753.         switch( i_key )
  754.         {
  755.             case KEY_HOME:
  756.                 p_sys->f_slider = 0;
  757.                 ManageSlider( p_intf );
  758.                 ReturnTrue;
  759. #ifdef __FreeBSD__
  760.             case KEY_SELECT:
  761. #endif
  762.             case KEY_END:
  763.                 p_sys->f_slider = 99.9;
  764.                 ManageSlider( p_intf );
  765.                 ReturnTrue;
  766.             case KEY_UP:
  767.                 p_sys->f_slider += 5.0;
  768.                 if( p_sys->f_slider >= 99.0 ) p_sys->f_slider = 99.0;
  769.                 ManageSlider( p_intf );
  770.                 ReturnTrue;
  771.             case KEY_DOWN:
  772.                 p_sys->f_slider -= 5.0;
  773.                 if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
  774.                 ManageSlider( p_intf );
  775.                 ReturnTrue;
  776.             default:
  777.                 break;
  778.         }
  779.     }
  780.     else if( p_sys->i_box_type == BOX_SEARCH && p_sys->psz_search_chain )
  781.     {
  782.         int i_chain_len = strlen( p_sys->psz_search_chain );
  783.         switch( i_key )
  784.         {
  785.             case KEY_CLEAR:
  786.             case 0x0c:      /* ^l */
  787.                 clear();
  788.                 ReturnTrue;
  789.             case KEY_ENTER:
  790.             case 'r':
  791.             case 'n':
  792.                 if( i_chain_len > 0 )
  793.                 {
  794.                     p_sys->psz_old_search = strdup( p_sys->psz_search_chain );
  795.                 }
  796.                 else if( p_sys->psz_old_search )
  797.                 {
  798.                     SearchPlaylist( p_intf, p_sys->psz_old_search );
  799.                 }
  800.                 p_sys->i_box_type = BOX_PLAYLIST;
  801.                 ReturnTrue;
  802.             case 0x1b: /* ESC */
  803.                 /* Alt+key combinations return 2 keys in the terminal keyboard:
  804.                  * ESC, and the 2nd key.
  805.                  * If some other key is available immediately (where immediately
  806.                  * means after wgetch() 1 second delay ), that means that the
  807.                  * ESC key was not pressed.
  808.                  *
  809.                  * man 3X curs_getch says:
  810.                  *
  811.                  * Use of the escape key by a programmer for a single
  812.                  * character function is discouraged, as it will cause a delay
  813.                  * of up to one second while the keypad code looks for a
  814.                  * following function-key sequence.
  815.                  *
  816.                  */
  817.                 if( wgetch( p_sys->w ) != ERR )
  818.                     ReturnFalse;
  819.                 p_sys->i_box_plidx = p_sys->i_before_search;
  820.                 p_sys->i_box_type = BOX_PLAYLIST;
  821.                 ReturnTrue;
  822.             case KEY_BACKSPACE:
  823.             case 0x7f:
  824.                 RemoveLastUTF8Entity( p_sys->psz_search_chain, i_chain_len );
  825.                 break;
  826.             default:
  827.             {
  828. #ifdef HAVE_NCURSESW
  829.                 if( i_chain_len + 1 < SEARCH_CHAIN_SIZE )
  830.                 {
  831.                     p_sys->psz_search_chain[i_chain_len] = (char) i_key;
  832.                     p_sys->psz_search_chain[i_chain_len + 1] = '';
  833.                 }
  834. #else
  835.                 char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
  836.                 if( psz_utf8 != NULL )
  837.                 {
  838.                     if( i_chain_len + strlen( psz_utf8 ) < SEARCH_CHAIN_SIZE )
  839.                     {
  840.                         strcpy( p_sys->psz_search_chain + i_chain_len,
  841.                                 psz_utf8 );
  842.                     }
  843.                     free( psz_utf8 );
  844.                 }
  845. #endif
  846.                 break;
  847.             }
  848.         }
  849.         free( p_sys->psz_old_search );
  850.         p_sys->psz_old_search = NULL;
  851.         SearchPlaylist( p_intf, p_sys->psz_search_chain );
  852.         ReturnTrue;
  853.     }
  854.     else if( p_sys->i_box_type == BOX_OPEN && p_sys->psz_open_chain )
  855.     {
  856.         int i_chain_len = strlen( p_sys->psz_open_chain );
  857.         switch( i_key )
  858.         {
  859.             case KEY_CLEAR:
  860.             case 0x0c:          /* ^l */
  861.                 clear();
  862.                 ReturnTrue;
  863.             case KEY_ENTER:
  864.             case 'r':
  865.             case 'n':
  866.                 if( i_chain_len > 0 )
  867.                 {
  868.                     playlist_item_t *p_parent = p_sys->p_node;
  869.                     if( !p_parent )
  870.                     p_parent = playlist_CurrentPlayingItem(p_playlist) ? playlist_CurrentPlayingItem(p_playlist)->p_parent : NULL;
  871.                     if( !p_parent )
  872.                         p_parent = p_playlist->p_local_onelevel;
  873.                     while( p_parent->p_parent && p_parent->p_parent->p_parent )
  874.                         p_parent = p_parent->p_parent;
  875.                     playlist_Add( p_playlist, p_sys->psz_open_chain, NULL,
  876.                                   PLAYLIST_APPEND|PLAYLIST_GO, PLAYLIST_END,
  877.                                   p_parent->p_input ==
  878.                                     p_playlist->p_local_onelevel->p_input
  879.                                   , false );
  880.                     p_sys->b_box_plidx_follow = true;
  881.                 }
  882.                 p_sys->i_box_type = BOX_PLAYLIST;
  883.                 ReturnTrue;
  884.             case 0x1b:  /* ESC */
  885.                 if( wgetch( p_sys->w ) != ERR )
  886.                     ReturnFalse;
  887.                 p_sys->i_box_type = BOX_PLAYLIST;
  888.                 ReturnTrue;
  889.             case KEY_BACKSPACE:
  890.             case 0x7f:
  891.                 RemoveLastUTF8Entity( p_sys->psz_open_chain, i_chain_len );
  892.                 break;
  893.             default:
  894.             {
  895. #ifdef HAVE_NCURSESW
  896.                 if( i_chain_len + 1 < OPEN_CHAIN_SIZE )
  897.                 {
  898.                     p_sys->psz_open_chain[i_chain_len] = (char) i_key;
  899.                     p_sys->psz_open_chain[i_chain_len + 1] = '';
  900.                 }
  901. #else
  902.                 char *psz_utf8 = KeyToUTF8( i_key, p_sys->psz_partial_keys );
  903.                 if( psz_utf8 != NULL )
  904.                 {
  905.                     if( i_chain_len + strlen( psz_utf8 ) < OPEN_CHAIN_SIZE )
  906.                     {
  907.                         strcpy( p_sys->psz_open_chain + i_chain_len,
  908.                                 psz_utf8 );
  909.                     }
  910.                     free( psz_utf8 );
  911.                 }
  912. #endif
  913.                 break;
  914.             }
  915.         }
  916.         ReturnTrue;
  917.     }
  918.     /* Common keys */
  919.     switch( i_key )
  920.     {
  921.         case 0x1b:  /* ESC */
  922.             if( wgetch( p_sys->w ) != ERR )
  923.                 ReturnFalse;
  924.         case 'q':
  925.         case 'Q':
  926.         case KEY_EXIT:
  927.             libvlc_Quit( p_intf->p_libvlc );
  928.             ReturnFalse;
  929.         /* Box switching */
  930.         case 'i':
  931.             if( p_sys->i_box_type == BOX_INFO )
  932.                 p_sys->i_box_type = BOX_NONE;
  933.             else
  934.                 p_sys->i_box_type = BOX_INFO;
  935.             p_sys->i_box_lines_total = 0;
  936.             ReturnTrue;
  937.         case 'm':
  938.             if( p_sys->i_box_type == BOX_META )
  939.                 p_sys->i_box_type = BOX_NONE;
  940.             else
  941.                 p_sys->i_box_type = BOX_META;
  942.             p_sys->i_box_lines_total = 0;
  943.             ReturnTrue;
  944.         case 'L':
  945.             if( p_sys->i_box_type == BOX_LOG )
  946.                 p_sys->i_box_type = BOX_NONE;
  947.             else
  948.                 p_sys->i_box_type = BOX_LOG;
  949.             ReturnTrue;
  950.         case 'P':
  951.             if( p_sys->i_box_type == BOX_PLAYLIST )
  952.                 p_sys->i_box_type = BOX_NONE;
  953.             else
  954.                 p_sys->i_box_type = BOX_PLAYLIST;
  955.             ReturnTrue;
  956.         case 'B':
  957.             if( p_sys->i_box_type == BOX_BROWSE )
  958.                 p_sys->i_box_type = BOX_NONE;
  959.             else
  960.                 p_sys->i_box_type = BOX_BROWSE;
  961.             ReturnTrue;
  962.         case 'x':
  963.             if( p_sys->i_box_type == BOX_OBJECTS )
  964.                 p_sys->i_box_type = BOX_NONE;
  965.             else
  966.                 p_sys->i_box_type = BOX_OBJECTS;
  967.             ReturnTrue;
  968.         case 'S':
  969.             if( p_sys->i_box_type == BOX_STATS )
  970.                 p_sys->i_box_type = BOX_NONE;
  971.             else
  972.                 p_sys->i_box_type = BOX_STATS;
  973.             ReturnTrue;
  974.         case 'c':
  975.             p_sys->b_color = !p_sys->b_color;
  976.             if( p_sys->b_color && !p_sys->b_color_started )
  977.                 start_color_and_pairs( p_intf );
  978.             ReturnTrue;
  979.         case 'h':
  980.         case 'H':
  981.             if( p_sys->i_box_type == BOX_HELP )
  982.                 p_sys->i_box_type = BOX_NONE;
  983.             else
  984.                 p_sys->i_box_type = BOX_HELP;
  985.             p_sys->i_box_lines_total = 0;
  986.             ReturnTrue;
  987.         case '/':
  988.             if( p_sys->i_box_type != BOX_SEARCH )
  989.             {
  990.                 if( p_sys->psz_search_chain == NULL )
  991.                     ReturnTrue;
  992.                 p_sys->psz_search_chain[0] = '';
  993.                 p_sys->b_box_plidx_follow = false;
  994.                 p_sys->i_before_search = p_sys->i_box_plidx;
  995.                 p_sys->i_box_type = BOX_SEARCH;
  996.             }
  997.             ReturnTrue;
  998.         case 'A': /* Open */
  999.             if( p_sys->i_box_type != BOX_OPEN )
  1000.             {
  1001.                 if( p_sys->psz_open_chain == NULL )
  1002.                     ReturnTrue;
  1003.                 p_sys->psz_open_chain[0] = '';
  1004.                 p_sys->i_box_type = BOX_OPEN;
  1005.             }
  1006.             ReturnTrue;
  1007.         /* Navigation */
  1008.         case KEY_RIGHT:
  1009.             p_sys->f_slider += 1.0;
  1010.             if( p_sys->f_slider > 99.9 ) p_sys->f_slider = 99.9;
  1011.             ManageSlider( p_intf );
  1012.             ReturnTrue;
  1013.         case KEY_LEFT:
  1014.             p_sys->f_slider -= 1.0;
  1015.             if( p_sys->f_slider < 0.0 ) p_sys->f_slider = 0.0;
  1016.             ManageSlider( p_intf );
  1017.             ReturnTrue;
  1018.         /* Common control */
  1019.         case 'f':
  1020.         {
  1021.             if( p_intf->p_sys->p_input )
  1022.             {
  1023.                 vout_thread_t *p_vout;
  1024.                 p_vout = vlc_object_find( p_intf->p_sys->p_input,
  1025.                                           VLC_OBJECT_VOUT, FIND_CHILD );
  1026.                 if( p_vout )
  1027.                 {
  1028.                     var_Get( p_vout, "fullscreen", &val );
  1029.                     val.b_bool = !val.b_bool;
  1030.                     var_Set( p_vout, "fullscreen", val );
  1031.                     vlc_object_release( p_vout );
  1032.                 }
  1033.                 else
  1034.                 {
  1035.                     var_Get( p_playlist, "fullscreen", &val );
  1036.                     val.b_bool = !val.b_bool;
  1037.                     var_Set( p_playlist, "fullscreen", val );
  1038.                 }
  1039.             }
  1040.             ReturnFalse;
  1041.         }
  1042.         case ' ':
  1043.             PlayPause( p_intf );
  1044.             ReturnTrue;
  1045.         case 's':
  1046.             playlist_Stop( p_playlist );
  1047.             ReturnTrue;
  1048.         case 'e':
  1049.             Eject( p_intf );
  1050.             ReturnTrue;
  1051.         case '[':
  1052.             if( p_sys->p_input )
  1053.                 var_SetVoid( p_sys->p_input, "prev-title" );
  1054.             ReturnTrue;
  1055.         case ']':
  1056.             if( p_sys->p_input )
  1057.                 var_SetVoid( p_sys->p_input, "next-title" );
  1058.             ReturnTrue;
  1059.         case '<':
  1060.             if( p_sys->p_input )
  1061.                 var_SetVoid( p_sys->p_input, "prev-chapter" );
  1062.             ReturnTrue;
  1063.         case '>':
  1064.             if( p_sys->p_input )
  1065.                 var_SetVoid( p_sys->p_input, "next-chapter" );
  1066.             ReturnTrue;
  1067.         case 'p':
  1068.             playlist_Prev( p_playlist );
  1069.             clear();
  1070.             ReturnTrue;
  1071.         case 'n':
  1072.             playlist_Next( p_playlist );
  1073.             clear();
  1074.             ReturnTrue;
  1075.         case 'a':
  1076.             aout_VolumeUp( p_intf, 1, NULL );
  1077.             clear();
  1078.             ReturnTrue;
  1079.         case 'z':
  1080.             aout_VolumeDown( p_intf, 1, NULL );
  1081.             clear();
  1082.             ReturnTrue;
  1083.         /*
  1084.          * ^l should clear and redraw the screen
  1085.          */
  1086.         case KEY_CLEAR:
  1087.         case 0x0c:          /* ^l */
  1088.             clear();
  1089.             ReturnTrue;
  1090.         default:
  1091.             ReturnFalse;
  1092.     }
  1093. #undef ReturnFalse
  1094. #undef ReturnTrue
  1095. }
  1096. static void ManageSlider( intf_thread_t *p_intf )
  1097. {
  1098.     intf_sys_t     *p_sys = p_intf->p_sys;
  1099.     input_thread_t *p_input = p_sys->p_input;
  1100.     vlc_value_t     val;
  1101.     if( p_input == NULL )
  1102.     {
  1103.         return;
  1104.     }
  1105.     var_Get( p_input, "state", &val );
  1106.     if( val.i_int != PLAYING_S )
  1107.     {
  1108.         return;
  1109.     }
  1110.     var_Get( p_input, "position", &val );
  1111.     if( p_sys->f_slider == p_sys->f_slider_old )
  1112.     {
  1113.         p_sys->f_slider =
  1114.         p_sys->f_slider_old = 100 * val.f_float;
  1115.     }
  1116.     else
  1117.     {
  1118.         p_sys->f_slider_old = p_sys->f_slider;
  1119.         val.f_float = p_sys->f_slider / 100.0;
  1120.         var_Set( p_input, "position", val );
  1121.     }
  1122. }
  1123. static void SearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring )
  1124. {
  1125.     int i_max;
  1126.     int i_first = 0 ;
  1127.     int i_item = -1;
  1128.     intf_sys_t *p_sys = p_intf->p_sys;
  1129.     if( p_sys->i_before_search >= 0 )
  1130.     {
  1131.         i_first = p_sys->i_before_search;
  1132.     }
  1133.     if( ( ! psz_searchstring ) ||  strlen( psz_searchstring ) <= 0 )
  1134.     {
  1135.         p_sys->i_box_plidx = p_sys->i_before_search;
  1136.         return;
  1137.     }
  1138.     i_max = p_sys->i_plist_entries;
  1139.     i_item = SubSearchPlaylist( p_intf, psz_searchstring, i_first + 1, i_max );
  1140.     if( i_item < 0 )
  1141.     {
  1142.         i_item = SubSearchPlaylist( p_intf, psz_searchstring, 0, i_first );
  1143.     }
  1144.     if( i_item < 0 || i_item >= i_max ) return;
  1145.     p_sys->i_box_plidx = i_item;
  1146. }
  1147. static int SubSearchPlaylist( intf_thread_t *p_intf, char *psz_searchstring,
  1148.                               int i_start, int i_stop )
  1149. {
  1150.     intf_sys_t *p_sys = p_intf->p_sys;
  1151.     int i, i_item = -1;
  1152.     for( i = i_start + 1; i < i_stop; i++ )
  1153.     {
  1154.         if( strcasestr( p_sys->pp_plist[i]->psz_display,
  1155.                         psz_searchstring ) != NULL )
  1156.         {
  1157.             i_item = i;
  1158.             break;
  1159.         }
  1160.     }
  1161.     return i_item;
  1162. }
  1163. static void mvnprintw( int y, int x, int w, const char *p_fmt, ... )
  1164. {
  1165.     va_list  vl_args;
  1166.     char    *p_buf = NULL;
  1167.     int      i_len;
  1168.     if( w <= 0 )
  1169.         return;
  1170.     va_start( vl_args, p_fmt );
  1171.     if( vasprintf( &p_buf, p_fmt, vl_args ) == -1 )
  1172.         return;
  1173.     va_end( vl_args );
  1174.     i_len = strlen( p_buf );
  1175. #ifdef HAVE_NCURSESW
  1176.     wchar_t psz_wide[i_len + 1];
  1177.     EnsureUTF8( p_buf );
  1178.     size_t i_char_len = mbstowcs( psz_wide, p_buf, i_len );
  1179.     size_t i_width; /* number of columns */
  1180.     if( i_char_len == (size_t)-1 )
  1181.     /* an invalid character was encountered */
  1182.     {
  1183.         free( p_buf );
  1184.         return;
  1185.     }
  1186.     else
  1187.     {
  1188.         i_width = wcswidth( psz_wide, i_char_len );
  1189.         if( i_width == (size_t)-1 )
  1190.         {
  1191.             /* a non printable character was encountered */
  1192.             unsigned int i;
  1193.             int i_cwidth;
  1194.             i_width = 0;
  1195.             for( i = 0 ; i < i_char_len ; i++ )
  1196.             {
  1197.                 i_cwidth = wcwidth( psz_wide[i] );
  1198.                 if( i_cwidth != -1 )
  1199.                     i_width += i_cwidth;
  1200.             }
  1201.         }
  1202.     }
  1203.     if( i_width > (size_t)w )
  1204.     {
  1205.         int i_total_width = 0;
  1206.         int i = 0;
  1207.         while( i_total_width < w )
  1208.         {
  1209.             i_total_width += wcwidth( psz_wide[i] );
  1210.             if( w > 7 && i_total_width >= w/2 )
  1211.             {
  1212.                 psz_wide[i  ] = '.';
  1213.                 psz_wide[i+1] = '.';
  1214.                 i_total_width -= wcwidth( psz_wide[i] ) - 2;
  1215.                 if( i > 0 )
  1216.                 {
  1217.                     /* we require this check only if at least one character
  1218.                      * 4 or more columns wide exists (which i doubt) */
  1219.                     psz_wide[i-1] = '.';
  1220.                     i_total_width -= wcwidth( psz_wide[i-1] ) - 1;
  1221.                 }
  1222.                 /* find the widest string */
  1223.                 int j, i_2nd_width = 0;
  1224.                 for( j = i_char_len - 1; i_2nd_width < w - i_total_width; j-- )
  1225.                     i_2nd_width += wcwidth( psz_wide[j] );
  1226.                 /* we already have i_total_width columns filled, and we can't
  1227.                  * have more than w columns */
  1228.                 if( i_2nd_width > w - i_total_width )
  1229.                     j++;
  1230.                 wmemmove( &psz_wide[i+2], &psz_wide[j+1], i_char_len - j - 1 );
  1231.                 psz_wide[i + 2 + i_char_len - j - 1] = '';
  1232.                 break;
  1233.             }
  1234.             i++;
  1235.         }
  1236.         if( w <= 7 ) /* we don't add the '...' else we lose too much chars */
  1237.             psz_wide[i] = '';
  1238.         size_t i_wlen = wcslen( psz_wide ) * 6 + 1; /* worst case */
  1239.         char psz_ellipsized[i_wlen];
  1240.         wcstombs( psz_ellipsized, psz_wide, i_wlen );
  1241.         mvprintw( y, x, "%s", psz_ellipsized );
  1242.     }
  1243.     else
  1244.     {
  1245.         mvprintw( y, x, "%s", p_buf );
  1246.         mvhline( y, x + i_width, ' ', w - i_width );
  1247.     }
  1248. #else
  1249.     if( i_len > w )
  1250.     {
  1251.         int i_cut = i_len - w;
  1252.         int x1 = i_len/2 - i_cut/2;
  1253.         int x2 = x1 + i_cut;
  1254.         if( i_len > x2 )
  1255.         {
  1256.             memmove( &p_buf[x1], &p_buf[x2], i_len - x2 );
  1257.         }
  1258.         p_buf[w] = '';
  1259.         if( w > 7 )
  1260.         {
  1261.             p_buf[w/2-1] = '.';
  1262.             p_buf[w/2  ] = '.';
  1263.             p_buf[w/2+1] = '.';
  1264.         }
  1265.         char *psz_local = ToLocale( p_buf );
  1266.         mvprintw( y, x, "%s", psz_local );
  1267.         LocaleFree( p_buf );
  1268.     }
  1269.     else
  1270.     {
  1271.         char *psz_local = ToLocale( p_buf );
  1272.         mvprintw( y, x, "%s", psz_local );
  1273.         LocaleFree( p_buf );
  1274.         mvhline( y, x + i_len, ' ', w - i_len );
  1275.     }
  1276. #endif
  1277.     free( p_buf );
  1278. }
  1279. static void MainBoxWrite( intf_thread_t *p_intf, int l, int x, const char *p_fmt, ... )
  1280. {
  1281.     intf_sys_t     *p_sys = p_intf->p_sys;
  1282.     va_list  vl_args;
  1283.     char    *p_buf = NULL;
  1284.     if( l < p_sys->i_box_start || l - p_sys->i_box_start >= p_sys->i_box_lines )
  1285.     {
  1286.         return;
  1287.     }
  1288.     va_start( vl_args, p_fmt );
  1289.     if( vasprintf( &p_buf, p_fmt, vl_args ) == -1 )
  1290.         return;
  1291.     va_end( vl_args );
  1292.     mvnprintw( p_sys->i_box_y + l - p_sys->i_box_start, x, COLS - x - 1, "%s", p_buf );
  1293.     free( p_buf );
  1294. }
  1295. static void DumpObject( intf_thread_t *p_intf, int *l, vlc_object_t *p_obj, int i_level )
  1296. {
  1297.     if( p_obj->psz_object_name )
  1298.         MainBoxWrite( p_intf, (*l)++, 1 + 2 * i_level, "%s "%s" (%p)",
  1299.                 p_obj->psz_object_type, p_obj->psz_object_name,
  1300.                 p_obj );
  1301.     else
  1302.         MainBoxWrite( p_intf, (*l)++, 1 + 2 * i_level, "%s (%o)",
  1303.                 p_obj->psz_object_type, p_obj );
  1304.     vlc_list_t *list = vlc_list_children( p_obj );
  1305.     for( int i = 0; i < list->i_count ; i++ )
  1306.     {
  1307.         MainBoxWrite( p_intf, *l, 1 + 2 * i_level,
  1308.             i == list->i_count - 1 ? "`-" : "|-" );
  1309.         DumpObject( p_intf, l, list->p_values[i].p_object, i_level + 1 );
  1310.     }
  1311.     vlc_list_release( list );
  1312. }
  1313. static void Redraw( intf_thread_t *p_intf, time_t *t_last_refresh )
  1314. {
  1315.     intf_sys_t     *p_sys = p_intf->p_sys;
  1316.     input_thread_t *p_input = p_sys->p_input;
  1317.     playlist_t     *p_playlist = pl_Hold( p_intf );
  1318.     int y = 0;
  1319.     int h;
  1320.     int y_end;
  1321.     /* Title */
  1322.     attrset( A_REVERSE );
  1323.     int i_len = strlen( "VLC media player "PACKAGE_VERSION );
  1324.     int mid = ( COLS - i_len ) / 2;
  1325.     if( mid < 0 )
  1326.         mid = 0;
  1327.     int i_size = ( COLS > i_len + 1 ) ? COLS : i_len + 1;
  1328.     char psz_title[i_size];
  1329.     memset( psz_title, ' ', mid );
  1330.     if( p_sys->b_color )
  1331.         wcolor_set( p_sys->w, C_TITLE, NULL );
  1332.     snprintf( &psz_title[mid], i_size, "VLC media player "PACKAGE_VERSION );
  1333.     mvnprintw( y, 0, COLS, "%s", psz_title );
  1334.     attroff( A_REVERSE );
  1335.     y += 2;
  1336.     if( p_sys->b_color )
  1337.         wcolor_set( p_sys->w, C_STATUS, NULL );
  1338.     /* Infos */
  1339.     char *psz_state;
  1340.     if( asprintf( &psz_state, "%s%s%s",
  1341.             var_GetBool( p_playlist, "repeat" ) ? _( "[Repeat] " ) : "",
  1342.             var_GetBool( p_playlist, "random" ) ? _( "[Random] " ) : "",
  1343.             var_GetBool( p_playlist, "loop" ) ? _( "[Loop]" ) : "" ) == -1 )
  1344.         psz_state = NULL;
  1345.     if( p_input && !p_input->b_dead )
  1346.     {
  1347.         char buf1[MSTRTIME_MAX_SIZE];
  1348.         char buf2[MSTRTIME_MAX_SIZE];
  1349.         vlc_value_t val;
  1350.         vlc_value_t val_list;
  1351.         /* Source */
  1352.         char *psz_uri = input_item_GetURI( input_GetItem( p_input ) );
  1353.         mvnprintw( y++, 0, COLS, _(" Source   : %s"), psz_uri );
  1354.         free( psz_uri );
  1355.         /* State */
  1356.         var_Get( p_input, "state", &val );
  1357.         if( val.i_int == PLAYING_S )
  1358.         {
  1359.             mvnprintw( y++, 0, COLS, _(" State    : Playing %s"), psz_state );
  1360.         }
  1361.         else if( val.i_int == OPENING_S )
  1362.         {
  1363.             mvnprintw( y++, 0, COLS, _(" State    : Opening/Connecting %s"), psz_state );
  1364.         }
  1365.         else if( val.i_int == PAUSE_S )
  1366.         {
  1367.             mvnprintw( y++, 0, COLS, _(" State    : Paused %s"), psz_state );
  1368.         }
  1369.         if( val.i_int != INIT_S && val.i_int != END_S )
  1370.         {
  1371.             audio_volume_t i_volume;
  1372.             /* Position */
  1373.             var_Get( p_input, "time", &val );
  1374.             msecstotimestr( buf1, val.i_time / 1000 );
  1375.             var_Get( p_input, "length", &val );
  1376.             msecstotimestr( buf2, val.i_time / 1000 );
  1377.             mvnprintw( y++, 0, COLS, _(" Position : %s/%s (%.2f%%)"), buf1, buf2, p_sys->f_slider );
  1378.             /* Volume */
  1379.             aout_VolumeGet( p_intf, &i_volume );
  1380.             mvnprintw( y++, 0, COLS, _(" Volume   : %i%%"), i_volume*200/AOUT_VOLUME_MAX );
  1381.             /* Title */
  1382.             if( !var_Get( p_input, "title", &val ) )
  1383.             {
  1384.                 var_Change( p_input, "title", VLC_VAR_GETCHOICES, &val_list, NULL );
  1385.                 if( val_list.p_list->i_count > 0 )
  1386.                 {
  1387.                     mvnprintw( y++, 0, COLS, _(" Title    : %d/%d"), val.i_int, val_list.p_list->i_count );
  1388.                 }
  1389.                 var_Change( p_input, "title", VLC_VAR_FREELIST, &val_list, NULL );
  1390.             }
  1391.             /* Chapter */
  1392.             if( !var_Get( p_input, "chapter", &val ) )
  1393.             {
  1394.                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES, &val_list, NULL );
  1395.                 if( val_list.p_list->i_count > 0 )
  1396.                 {
  1397.                     mvnprintw( y++, 0, COLS, _(" Chapter  : %d/%d"), val.i_int, val_list.p_list->i_count );
  1398.                 }
  1399.                 var_Change( p_input, "chapter", VLC_VAR_FREELIST, &val_list, NULL );
  1400.             }
  1401.         }
  1402.         else
  1403.         {
  1404.             y += 2;
  1405.         }
  1406.     }
  1407.     else
  1408.     {
  1409.         mvnprintw( y++, 0, COLS, _(" Source: <no current item> %s"), psz_state );
  1410.         DrawEmptyLine( p_sys->w, y++, 0, COLS );
  1411.         mvnprintw( y++, 0, COLS, _(" [ h for help ]") );
  1412.         DrawEmptyLine( p_sys->w, y++, 0, COLS );
  1413.     }
  1414.     free( psz_state );
  1415.     if( p_sys->b_color )
  1416.         wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1417.     DrawBox( p_sys->w, y, 0, 3, COLS, "", p_sys->b_color );
  1418.     DrawEmptyLine( p_sys->w, y+1, 1, COLS-2);
  1419.     DrawLine( p_sys->w, y+1, 1, (int)(p_intf->p_sys->f_slider/100.0 * (COLS -2)) );
  1420.     y += 3;
  1421.     p_sys->i_box_y = y + 1;
  1422.     p_sys->i_box_lines = LINES - y - 2;
  1423.     h = LINES - y;
  1424.     y_end = y + h - 1;
  1425.     if( p_sys->i_box_type == BOX_HELP )
  1426.     {
  1427.         /* Help box */
  1428.         int l = 0;
  1429.         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Help "), p_sys->b_color );
  1430.         if( p_sys->b_color )
  1431.             wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1432.         MainBoxWrite( p_intf, l++, 1, _("[Display]") );
  1433.         if( p_sys->b_color )
  1434.             wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1435.         MainBoxWrite( p_intf, l++, 1, _("     h,H         Show/Hide help box") );
  1436.         MainBoxWrite( p_intf, l++, 1, _("     i           Show/Hide info box") );
  1437.         MainBoxWrite( p_intf, l++, 1, _("     m           Show/Hide metadata box") );
  1438.         MainBoxWrite( p_intf, l++, 1, _("     L           Show/Hide messages box") );
  1439.         MainBoxWrite( p_intf, l++, 1, _("     P           Show/Hide playlist box") );
  1440.         MainBoxWrite( p_intf, l++, 1, _("     B           Show/Hide filebrowser") );
  1441.         MainBoxWrite( p_intf, l++, 1, _("     x           Show/Hide objects box") );
  1442.         MainBoxWrite( p_intf, l++, 1, _("     S           Show/Hide statistics box" ) );
  1443.         MainBoxWrite( p_intf, l++, 1, _("     c           Switch color on/off") );
  1444.         MainBoxWrite( p_intf, l++, 1, _("     Esc         Close Add/Search entry") );
  1445.         MainBoxWrite( p_intf, l++, 1, "" );
  1446.         if( p_sys->b_color )
  1447.             wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1448.         MainBoxWrite( p_intf, l++, 1, _("[Global]") );
  1449.         if( p_sys->b_color )
  1450.             wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1451.         MainBoxWrite( p_intf, l++, 1, _("     q, Q, Esc   Quit") );
  1452.         MainBoxWrite( p_intf, l++, 1, _("     s           Stop") );
  1453.         MainBoxWrite( p_intf, l++, 1, _("     <space>     Pause/Play") );
  1454.         MainBoxWrite( p_intf, l++, 1, _("     f           Toggle Fullscreen") );
  1455.         MainBoxWrite( p_intf, l++, 1, _("     n, p        Next/Previous playlist item") );
  1456.         MainBoxWrite( p_intf, l++, 1, _("     [, ]        Next/Previous title") );
  1457.         MainBoxWrite( p_intf, l++, 1, _("     <, >        Next/Previous chapter") );
  1458.         MainBoxWrite( p_intf, l++, 1, _("     <right>     Seek +1%%") );
  1459.         MainBoxWrite( p_intf, l++, 1, _("     <left>      Seek -1%%") );
  1460.         MainBoxWrite( p_intf, l++, 1, _("     a           Volume Up") );
  1461.         MainBoxWrite( p_intf, l++, 1, _("     z           Volume Down") );
  1462.         MainBoxWrite( p_intf, l++, 1, "" );
  1463.         if( p_sys->b_color )
  1464.             wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1465.         MainBoxWrite( p_intf, l++, 1, _("[Playlist]") );
  1466.         if( p_sys->b_color )
  1467.             wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1468.         MainBoxWrite( p_intf, l++, 1, _("     r           Toggle Random playing") );
  1469.         MainBoxWrite( p_intf, l++, 1, _("     l           Toggle Loop Playlist") );
  1470.         MainBoxWrite( p_intf, l++, 1, _("     R           Toggle Repeat item") );
  1471.         MainBoxWrite( p_intf, l++, 1, _("     o           Order Playlist by title") );
  1472.         MainBoxWrite( p_intf, l++, 1, _("     O           Reverse order Playlist by title") );
  1473.         MainBoxWrite( p_intf, l++, 1, _("     g           Go to the current playing item") );
  1474.         MainBoxWrite( p_intf, l++, 1, _("     /           Look for an item") );
  1475.         MainBoxWrite( p_intf, l++, 1, _("     A           Add an entry") );
  1476.         MainBoxWrite( p_intf, l++, 1, _("     D, <del>    Delete an entry") );
  1477.         MainBoxWrite( p_intf, l++, 1, _("     <backspace> Delete an entry") );
  1478.         MainBoxWrite( p_intf, l++, 1, _("     e           Eject (if stopped)") );
  1479.         MainBoxWrite( p_intf, l++, 1, "" );
  1480.         if( p_sys->b_color )
  1481.             wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1482.         MainBoxWrite( p_intf, l++, 1, _("[Filebrowser]") );
  1483.         if( p_sys->b_color )
  1484.             wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1485.         MainBoxWrite( p_intf, l++, 1, _("     <enter>     Add the selected file to the playlist") );
  1486.         MainBoxWrite( p_intf, l++, 1, _("     <space>     Add the selected directory to the playlist") );
  1487.         MainBoxWrite( p_intf, l++, 1, _("     .           Show/Hide hidden files") );
  1488.         MainBoxWrite( p_intf, l++, 1, "" );
  1489.         if( p_sys->b_color )
  1490.             wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1491.         MainBoxWrite( p_intf, l++, 1, _("[Boxes]") );
  1492.         if( p_sys->b_color )
  1493.             wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1494.         MainBoxWrite( p_intf, l++, 1, _("     <up>,<down>     Navigate through the box line by line") );
  1495.         MainBoxWrite( p_intf, l++, 1, _("     <pgup>,<pgdown> Navigate through the box page by page") );
  1496.         MainBoxWrite( p_intf, l++, 1, "" );
  1497.         if( p_sys->b_color )
  1498.             wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1499.         MainBoxWrite( p_intf, l++, 1, _("[Player]") );
  1500.         if( p_sys->b_color )
  1501.             wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1502.         MainBoxWrite( p_intf, l++, 1, _("     <up>,<down>     Seek +/-5%%") );
  1503.         MainBoxWrite( p_intf, l++, 1, "" );
  1504.         if( p_sys->b_color )
  1505.             wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1506.         MainBoxWrite( p_intf, l++, 1, _("[Miscellaneous]") );
  1507.         if( p_sys->b_color )
  1508.             wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1509.         MainBoxWrite( p_intf, l++, 1, _("     Ctrl-l          Refresh the screen") );
  1510.         p_sys->i_box_lines_total = l;
  1511.         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
  1512.         {
  1513.             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
  1514.         }
  1515.         if( l - p_sys->i_box_start < p_sys->i_box_lines )
  1516.         {
  1517.             y += l - p_sys->i_box_start;
  1518.         }
  1519.         else
  1520.         {
  1521.             y += p_sys->i_box_lines;
  1522.         }
  1523.     }
  1524.     else if( p_sys->i_box_type == BOX_INFO )
  1525.     {
  1526.         /* Info box */
  1527.         int l = 0;
  1528.         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Information "), p_sys->b_color );
  1529.         if( p_input )
  1530.         {
  1531.             int i,j;
  1532.             vlc_mutex_lock( &input_GetItem(p_input)->lock );
  1533.             for( i = 0; i < input_GetItem(p_input)->i_categories; i++ )
  1534.             {
  1535.                 info_category_t *p_category = input_GetItem(p_input)->pp_categories[i];
  1536.                 if( y >= y_end ) break;
  1537.                 if( p_sys->b_color )
  1538.                     wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1539.                 MainBoxWrite( p_intf, l++, 1, _("  [%s]"), p_category->psz_name );
  1540.                 if( p_sys->b_color )
  1541.                     wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1542.                 for( j = 0; j < p_category->i_infos; j++ )
  1543.                 {
  1544.                     info_t *p_info = p_category->pp_infos[j];
  1545.                     if( y >= y_end ) break;
  1546.                     MainBoxWrite( p_intf, l++, 1, _("      %s: %s"), p_info->psz_name, p_info->psz_value );
  1547.                 }
  1548.             }
  1549.             vlc_mutex_unlock( &input_GetItem(p_input)->lock );
  1550.         }
  1551.         else
  1552.         {
  1553.             MainBoxWrite( p_intf, l++, 1, _("No item currently playing") );
  1554.         }
  1555.         p_sys->i_box_lines_total = l;
  1556.         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
  1557.         {
  1558.             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
  1559.         }
  1560.         if( l - p_sys->i_box_start < p_sys->i_box_lines )
  1561.         {
  1562.             y += l - p_sys->i_box_start;
  1563.         }
  1564.         else
  1565.         {
  1566.             y += p_sys->i_box_lines;
  1567.         }
  1568.     }
  1569.     else if( p_sys->i_box_type == BOX_META )
  1570.     {
  1571.         /* Meta data box */
  1572.         int l = 0;
  1573.         DrawBox( p_sys->w, y++, 0, h, COLS, _("Meta-information"),
  1574.                  p_sys->b_color );
  1575.         if( p_input )
  1576.         {
  1577.             int i;
  1578.             input_item_t *p_item = input_GetItem( p_input );
  1579.             vlc_mutex_lock( &p_item->lock );
  1580.             for( i=0; i<VLC_META_TYPE_COUNT; i++ )
  1581.             {
  1582.                 if( y >= y_end ) break;
  1583.                 char *psz_meta = p_item->p_meta->ppsz_meta[i];
  1584.                 if( psz_meta && *psz_meta )
  1585.                 {
  1586.                     const char *psz_meta_title;
  1587.                     switch( i )
  1588.                     {
  1589.                         case 0:
  1590.                             psz_meta_title = VLC_META_TITLE; break;
  1591.                         case 1:
  1592.                             psz_meta_title = VLC_META_ARTIST; break;
  1593.                         case 2:
  1594.                             psz_meta_title = VLC_META_GENRE ; break;
  1595.                         case 3:
  1596.                             psz_meta_title = VLC_META_COPYRIGHT; break;
  1597.                         case 4:
  1598.                             psz_meta_title = VLC_META_ALBUM; break;
  1599.                         case 5:
  1600.                             psz_meta_title = VLC_META_TRACK_NUMBER; break;
  1601.                         case 6:
  1602.                             psz_meta_title = VLC_META_DESCRIPTION; break;
  1603.                         case 7:
  1604.                             psz_meta_title = VLC_META_RATING; break;
  1605.                         case 8:
  1606.                             psz_meta_title = VLC_META_DATE; break;
  1607.                         case 9:
  1608.                             psz_meta_title = VLC_META_SETTING; break;
  1609.                         case 10:
  1610.                             psz_meta_title = VLC_META_URL; break;
  1611.                         case 11:
  1612.                             psz_meta_title = VLC_META_LANGUAGE; break;
  1613.                         case 12:
  1614.                             psz_meta_title = VLC_META_NOW_PLAYING; break;
  1615.                         case 13:
  1616.                             psz_meta_title = VLC_META_PUBLISHER; break;
  1617.                         case 14:
  1618.                             psz_meta_title = VLC_META_ENCODED_BY; break;
  1619.                         case 15:
  1620.                             psz_meta_title = VLC_META_ART_URL; break;
  1621.                         case 16:
  1622.                             psz_meta_title = VLC_META_TRACKID; break;
  1623.                         default:
  1624.                             psz_meta_title = ""; break;
  1625.                     }
  1626.                     if( p_sys->b_color )
  1627.                         wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1628.                     MainBoxWrite( p_intf, l++, 1, "  [%s]", psz_meta_title );
  1629.                     if( p_sys->b_color )
  1630.                         wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1631.                     MainBoxWrite( p_intf, l++, 1, "      %s", psz_meta );
  1632.                 }
  1633.             }
  1634.             vlc_mutex_unlock( &p_item->lock );
  1635.         }
  1636.         else
  1637.         {
  1638.             MainBoxWrite( p_intf, l++, 1, _("No item currently playing") );
  1639.         }
  1640.         p_sys->i_box_lines_total = l;
  1641.         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
  1642.         {
  1643.             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
  1644.         }
  1645.         if( l - p_sys->i_box_start < p_sys->i_box_lines )
  1646.         {
  1647.             y += l - p_sys->i_box_start;
  1648.         }
  1649.         else
  1650.         {
  1651.             y += p_sys->i_box_lines;
  1652.         }
  1653.     }
  1654.     else if( p_sys->i_box_type == BOX_LOG )
  1655.     {
  1656. #warning Deprecated API
  1657. #if 0
  1658.         int i_line = 0;
  1659.         int i_stop;
  1660.         int i_start;
  1661.         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Logs "), p_sys->b_color );
  1662.         i_start = p_intf->p_sys->p_sub->i_start;
  1663.         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
  1664.         i_stop = *p_intf->p_sys->p_sub->pi_stop;
  1665.         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
  1666.         for( ;; )
  1667.         {
  1668.             static const char *ppsz_type[4] = { "", "error", "warning", "debug" };
  1669.             if( i_line >= h - 2 )
  1670.             {
  1671.                 break;
  1672.             }
  1673.             i_stop--;
  1674.             i_line++;
  1675.             if( i_stop < 0 ) i_stop += VLC_MSG_QSIZE;
  1676.             if( i_stop == i_start )
  1677.             {
  1678.                 break;
  1679.             }
  1680.             if( p_sys->b_color )
  1681.                 wcolor_set( p_sys->w,
  1682.                     p_sys->p_sub->p_msg[i_stop].i_type + C_INFO,
  1683.                     NULL );
  1684.             mvnprintw( y + h-2-i_line, 1, COLS - 2, "   [%s] %s",
  1685.                       ppsz_type[p_sys->p_sub->p_msg[i_stop].i_type],
  1686.                       p_sys->p_sub->p_msg[i_stop].psz_msg );
  1687.             if( p_sys->b_color )
  1688.                 wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1689.         }
  1690.         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
  1691.         p_intf->p_sys->p_sub->i_start = i_stop;
  1692.         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
  1693.         y = y_end;
  1694. #endif
  1695.     }
  1696.     else if( p_sys->i_box_type == BOX_BROWSE )
  1697.     {
  1698.         /* Filebrowser box */
  1699.         int        i_start, i_stop;
  1700.         int        i_item;
  1701.         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Browse "), p_sys->b_color );
  1702.         if( p_sys->i_box_bidx >= p_sys->i_dir_entries ) p_sys->i_box_plidx = p_sys->i_dir_entries - 1;
  1703.         if( p_sys->i_box_bidx < 0 ) p_sys->i_box_bidx = 0;
  1704.         if( p_sys->i_box_bidx < (h - 2)/2 )
  1705.         {
  1706.             i_start = 0;
  1707.             i_stop = h - 2;
  1708.         }
  1709.         else if( p_sys->i_dir_entries - p_sys->i_box_bidx > (h - 2)/2 )
  1710.         {
  1711.             i_start = p_sys->i_box_bidx - (h - 2)/2;
  1712.             i_stop = i_start + h - 2;
  1713.         }
  1714.         else
  1715.         {
  1716.             i_stop = p_sys->i_dir_entries;
  1717.             i_start = p_sys->i_dir_entries - (h - 2);
  1718.         }
  1719.         if( i_start < 0 )
  1720.         {
  1721.             i_start = 0;
  1722.         }
  1723.         if( i_stop > p_sys->i_dir_entries )
  1724.         {
  1725.             i_stop = p_sys->i_dir_entries;
  1726.         }
  1727.         for( i_item = i_start; i_item < i_stop; i_item++ )
  1728.         {
  1729.             bool b_selected = ( p_sys->i_box_bidx == i_item );
  1730.             if( y >= y_end ) break;
  1731.             if( b_selected )
  1732.             {
  1733.                 attrset( A_REVERSE );
  1734.             }
  1735.             if( p_sys->b_color && !p_sys->pp_dir_entries[i_item]->b_file )
  1736.                 wcolor_set( p_sys->w, C_FOLDER, NULL );
  1737.             mvnprintw( y++, 1, COLS - 2, " %c %s", p_sys->pp_dir_entries[i_item]->b_file == true ? ' ' : '+',
  1738.                             p_sys->pp_dir_entries[i_item]->psz_path );
  1739.             if( p_sys->b_color && !p_sys->pp_dir_entries[i_item]->b_file )
  1740.                 wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1741.             if( b_selected )
  1742.             {
  1743.                 attroff( A_REVERSE );
  1744.             }
  1745.         }
  1746.     }
  1747.     else if( p_sys->i_box_type == BOX_OBJECTS )
  1748.     {
  1749.         int l = 0;
  1750.         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Objects "), p_sys->b_color );
  1751.         DumpObject( p_intf, &l, VLC_OBJECT( p_intf->p_libvlc ), 0 );
  1752.         p_sys->i_box_lines_total = l;
  1753.         if( p_sys->i_box_start >= p_sys->i_box_lines_total )
  1754.             p_sys->i_box_start = p_sys->i_box_lines_total - 1;
  1755.         if( l - p_sys->i_box_start < p_sys->i_box_lines )
  1756.             y += l - p_sys->i_box_start;
  1757.         else
  1758.             y += p_sys->i_box_lines;
  1759.     }
  1760.     else if( p_sys->i_box_type == BOX_STATS )
  1761.     {
  1762.         DrawBox( p_sys->w, y++, 0, h, COLS, _(" Stats "), p_sys->b_color );
  1763.         if( p_input )
  1764.         {
  1765.             input_item_t *p_item = input_GetItem( p_input );
  1766.             assert( p_item );
  1767.             vlc_mutex_lock( &p_item->lock );
  1768.             vlc_mutex_lock( &p_item->p_stats->lock );
  1769.             int i_audio = 0;
  1770.             int i_video = 0;
  1771.             int i;
  1772.             if( !p_item->i_es )
  1773.                 i_video = i_audio = 1;
  1774.             else
  1775.                 for( i = 0; i < p_item->i_es ; i++ )
  1776.                 {
  1777.                     i_audio += ( p_item->es[i]->i_cat == AUDIO_ES );
  1778.                     i_video += ( p_item->es[i]->i_cat == VIDEO_ES );
  1779.                 }
  1780.             int l = 0;
  1781. #define SHOW_ACS(x,c) 
  1782.     if(l >= p_sys->i_box_start && l - p_sys->i_box_start < p_sys->i_box_lines) 
  1783.         mvaddch( p_sys->i_box_y - p_sys->i_box_start + l, x, c )
  1784.             /* Input */
  1785.             if( p_sys->b_color ) wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1786.             MainBoxWrite( p_intf, l, 1, _("+-[Incoming]"));
  1787.             SHOW_ACS( 1, ACS_ULCORNER );  SHOW_ACS( 2, ACS_HLINE ); l++;
  1788.             if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1789.             MainBoxWrite( p_intf, l, 1, _("| input bytes read : %8.0f kB"),
  1790.                     (float)(p_item->p_stats->i_read_bytes)/1000 );
  1791.             SHOW_ACS( 1, ACS_VLINE ); l++;
  1792.             MainBoxWrite( p_intf, l, 1, _("| input bitrate    :   %6.0f kb/s"),
  1793.                     (float)(p_item->p_stats->f_input_bitrate)*8000 );
  1794.             MainBoxWrite( p_intf, l, 1, _("| demux bytes read : %8.0f kB"),
  1795.                     (float)(p_item->p_stats->i_demux_read_bytes)/1000 );
  1796.             SHOW_ACS( 1, ACS_VLINE ); l++;
  1797.             MainBoxWrite( p_intf, l, 1, _("| demux bitrate    :   %6.0f kb/s"),
  1798.                     (float)(p_item->p_stats->f_demux_bitrate)*8000 );
  1799.             SHOW_ACS( 1, ACS_VLINE ); l++;
  1800.             DrawEmptyLine( p_sys->w, p_sys->i_box_y + l - p_sys->i_box_start, 1, COLS - 2 );
  1801.             SHOW_ACS( 1, ACS_VLINE ); l++;
  1802.             /* Video */
  1803.             if( i_video )
  1804.             {
  1805.                 if( p_sys->b_color ) wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1806.                 MainBoxWrite( p_intf, l, 1, _("+-[Video Decoding]"));
  1807.                 SHOW_ACS( 1, ACS_LTEE );  SHOW_ACS( 2, ACS_HLINE ); l++;
  1808.                 if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1809.                 MainBoxWrite( p_intf, l, 1, _("| video decoded    :    %5i"),
  1810.                         p_item->p_stats->i_decoded_video );
  1811.                 SHOW_ACS( 1, ACS_VLINE ); l++;
  1812.                 MainBoxWrite( p_intf, l, 1, _("| frames displayed :    %5i"),
  1813.                         p_item->p_stats->i_displayed_pictures );
  1814.                 SHOW_ACS( 1, ACS_VLINE ); l++;
  1815.                 MainBoxWrite( p_intf, l, 1, _("| frames lost      :    %5i"),
  1816.                         p_item->p_stats->i_lost_pictures );
  1817.                 SHOW_ACS( 1, ACS_VLINE ); l++;
  1818.                 DrawEmptyLine( p_sys->w, p_sys->i_box_y + l - p_sys->i_box_start, 1, COLS - 2 );
  1819.                 SHOW_ACS( 1, ACS_VLINE ); l++;
  1820.             }
  1821.             /* Audio*/
  1822.             if( i_audio )
  1823.             {
  1824.                 if( p_sys->b_color ) wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1825.                 MainBoxWrite( p_intf, l, 1, _("+-[Audio Decoding]"));
  1826.                 SHOW_ACS( 1, ACS_LTEE );  SHOW_ACS( 2, ACS_HLINE ); l++;
  1827.                 if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1828.                 MainBoxWrite( p_intf, l, 1, _("| audio decoded    :    %5i"),
  1829.                         p_item->p_stats->i_decoded_audio );
  1830.                 SHOW_ACS( 1, ACS_VLINE ); l++;
  1831.                 MainBoxWrite( p_intf, l, 1, _("| buffers played   :    %5i"),
  1832.                         p_item->p_stats->i_played_abuffers );
  1833.                 SHOW_ACS( 1, ACS_VLINE ); l++;
  1834.                 MainBoxWrite( p_intf, l, 1, _("| buffers lost     :    %5i"),
  1835.                         p_item->p_stats->i_lost_abuffers );
  1836.                 SHOW_ACS( 1, ACS_VLINE ); l++;
  1837.                 DrawEmptyLine( p_sys->w, p_sys->i_box_y + l - p_sys->i_box_start, 1, COLS - 2 );
  1838.                 SHOW_ACS( 1, ACS_VLINE ); l++;
  1839.             }
  1840.             /* Sout */
  1841.             if( p_sys->b_color ) wcolor_set( p_sys->w, C_CATEGORY, NULL );
  1842.             MainBoxWrite( p_intf, l, 1, _("+-[Streaming]"));
  1843.             SHOW_ACS( 1, ACS_LTEE );  SHOW_ACS( 2, ACS_HLINE ); l++;
  1844.             if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1845.             MainBoxWrite( p_intf, l, 1, _("| packets sent     :    %5i"), p_item->p_stats->i_sent_packets );
  1846.             SHOW_ACS( 1, ACS_VLINE ); l++;
  1847.             MainBoxWrite( p_intf, l, 1, _("| bytes sent       : %8.0f kB"),
  1848.                     (float)(p_item->p_stats->i_sent_bytes)/1000 );
  1849.             SHOW_ACS( 1, ACS_VLINE ); l++;
  1850.             MainBoxWrite( p_intf, l, 1, _("\ sending bitrate  :   %6.0f kb/s"),
  1851.                     (float)(p_item->p_stats->f_send_bitrate*8)*1000 );
  1852.             SHOW_ACS( 1, ACS_LLCORNER ); l++;
  1853.             if( p_sys->b_color ) wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1854. #undef SHOW_ACS
  1855.             p_sys->i_box_lines_total = l;
  1856.             if( p_sys->i_box_start >= p_sys->i_box_lines_total )
  1857.                 p_sys->i_box_start = p_sys->i_box_lines_total - 1;
  1858.             if( l - p_sys->i_box_start < p_sys->i_box_lines )
  1859.                 y += l - p_sys->i_box_start;
  1860.             else
  1861.                 y += p_sys->i_box_lines;
  1862.             vlc_mutex_unlock( &p_item->p_stats->lock );
  1863.             vlc_mutex_unlock( &p_item->lock );
  1864.         }
  1865.     }
  1866.     else if( p_sys->i_box_type == BOX_PLAYLIST ||
  1867.                p_sys->i_box_type == BOX_SEARCH ||
  1868.                p_sys->i_box_type == BOX_OPEN   )
  1869.     {
  1870.         /* Playlist box */
  1871.         int        i_start, i_stop, i_max = p_sys->i_plist_entries;
  1872.         int        i_item;
  1873.         char       *psz_title;
  1874.         switch( p_sys->i_current_view )
  1875.         {
  1876.             case VIEW_ONELEVEL:
  1877.                 psz_title = strdup( _(" Playlist (All, one level) ") );
  1878.                 break;
  1879.             case VIEW_CATEGORY:
  1880.                 psz_title = strdup( _(" Playlist (By category) ") );
  1881.                 break;
  1882.             default:
  1883.                 psz_title = strdup( _(" Playlist (Manually added) ") );
  1884.         }
  1885.         DrawBox( p_sys->w, y++, 0, h, COLS, psz_title, p_sys->b_color );
  1886.         free( psz_title );
  1887.         if( p_sys->b_need_update || p_sys->pp_plist == NULL )
  1888.         {
  1889.             PlaylistRebuild( p_intf );
  1890.         }
  1891.         if( p_sys->b_box_plidx_follow )
  1892.         {
  1893.             FindIndex( p_intf, p_playlist );
  1894.         }
  1895.         if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
  1896.         if( p_sys->i_box_plidx < 0 ) p_sys->i_box_plidx = 0;
  1897.         if( p_sys->i_box_plidx >= i_max ) p_sys->i_box_plidx = i_max - 1;
  1898.         if( p_sys->i_box_plidx < (h - 2)/2 )
  1899.         {
  1900.             i_start = 0;
  1901.             i_stop = h - 2;
  1902.         }
  1903.         else if( i_max - p_sys->i_box_plidx > (h - 2)/2 )
  1904.         {
  1905.             i_start = p_sys->i_box_plidx - (h - 2)/2;
  1906.             i_stop = i_start + h - 2;
  1907.         }
  1908.         else
  1909.         {
  1910.             i_stop = i_max;
  1911.             i_start = i_max - (h - 2);
  1912.         }
  1913.         if( i_start < 0 )
  1914.         {
  1915.             i_start = 0;
  1916.         }
  1917.         if( i_stop > i_max )
  1918.         {
  1919.             i_stop = i_max;
  1920.         }
  1921.         for( i_item = i_start; i_item < i_stop; i_item++ )
  1922.         {
  1923.             bool b_selected = ( p_sys->i_box_plidx == i_item );
  1924.             playlist_item_t *p_item = p_sys->pp_plist[i_item]->p_item;
  1925.             playlist_item_t *p_node = p_sys->p_node;
  1926.             int c = ' ';
  1927.             input_thread_t *p_input2 = playlist_CurrentInput( p_playlist );
  1928.             PL_LOCK;
  1929.             if( ( p_node && p_item->p_input == p_node->p_input ) ||
  1930.                         ( !p_node && p_input2 &&
  1931.                           p_item->p_input == playlist_CurrentPlayingItem(p_playlist)->p_input ) )
  1932.                 c = '*';
  1933.             else if( p_item == p_node || ( p_item != p_node &&
  1934.                         PlaylistIsPlaying( p_playlist, p_item ) ) )
  1935.                 c = '>';
  1936.             PL_UNLOCK;
  1937.             if( p_input2 )
  1938.                 vlc_object_release( p_input2 );
  1939.             if( y >= y_end ) break;
  1940.             if( b_selected )
  1941.             {
  1942.                 attrset( A_REVERSE );
  1943.             }
  1944.             if( p_sys->b_color )
  1945.                 wcolor_set( p_sys->w, i_item % 3 + C_PLAYLIST_1, NULL );
  1946.             mvnprintw( y++, 1, COLS - 2, "%c%s", c,
  1947.                        p_sys->pp_plist[i_item]->psz_display );
  1948.             if( p_sys->b_color )
  1949.                 wcolor_set( p_sys->w, C_DEFAULT, NULL );
  1950.             if( b_selected )
  1951.             {
  1952.                 attroff( A_REVERSE );
  1953.             }
  1954.         }
  1955.     }
  1956.     else
  1957.     {
  1958.         y++;
  1959.     }
  1960.     if( p_sys->i_box_type == BOX_SEARCH )
  1961.     {
  1962.         DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
  1963.         if( p_sys->psz_search_chain )
  1964.         {
  1965.             if( strlen( p_sys->psz_search_chain ) == 0 &&
  1966.                 p_sys->psz_old_search != NULL )
  1967.             {
  1968.                 /* Searching next entry */
  1969.                 mvnprintw( 7, 1, COLS-2, _("Find: %s"), p_sys->psz_old_search );
  1970.             }
  1971.             else
  1972.             {
  1973.                 mvnprintw( 7, 1, COLS-2, _("Find: %s"), p_sys->psz_search_chain );
  1974.             }
  1975.         }
  1976.     }
  1977.     if( p_sys->i_box_type == BOX_OPEN )
  1978.     {
  1979.         if( p_sys->psz_open_chain )
  1980.         {
  1981.             DrawEmptyLine( p_sys->w, 7, 1, COLS-2 );
  1982.             mvnprintw( 7, 1, COLS-2, _("Open: %s"), p_sys->psz_open_chain );
  1983.         }
  1984.     }
  1985.     while( y < y_end )
  1986.     {
  1987.         DrawEmptyLine( p_sys->w, y++, 1, COLS - 2 );
  1988.     }
  1989.     refresh();
  1990.     *t_last_refresh = time( 0 );
  1991.     pl_Release( p_intf );
  1992. }
  1993. static playlist_item_t *PlaylistGetRoot( intf_thread_t *p_intf )
  1994. {
  1995.     intf_sys_t *p_sys = p_intf->p_sys;
  1996.     playlist_t *p_playlist = pl_Hold( p_intf );
  1997.     playlist_item_t *p_item;
  1998.     switch( p_sys->i_current_view )
  1999.     {
  2000.         case VIEW_CATEGORY:
  2001.             p_item = p_playlist->p_root_category;
  2002.             break;
  2003.         default:
  2004.             p_item = p_playlist->p_root_onelevel;
  2005.     }
  2006.     pl_Release( p_intf );
  2007.     return p_item;
  2008. }
  2009. static void PlaylistRebuild( intf_thread_t *p_intf )
  2010. {
  2011.     intf_sys_t *p_sys = p_intf->p_sys;
  2012.     playlist_t *p_playlist = pl_Hold( p_intf );
  2013.     PL_LOCK;
  2014.     /* First clear the old one */
  2015.     PlaylistDestroy( p_intf );
  2016.     /* Build the new one */
  2017.     PlaylistAddNode( p_intf, PlaylistGetRoot( p_intf ), 0, "" );
  2018.     p_sys->b_need_update = false;
  2019.     PL_UNLOCK;
  2020.     pl_Release( p_intf );
  2021. }
  2022. static void PlaylistAddNode( intf_thread_t *p_intf, playlist_item_t *p_node,
  2023.                              int i, const char *c )
  2024. {
  2025.     intf_sys_t *p_sys = p_intf->p_sys;
  2026.     playlist_item_t *p_child;
  2027.     int k;
  2028.     for( k = 0; k < p_node->i_children; k++ )
  2029.     {
  2030.         char *psz_display;
  2031.         p_child = p_node->pp_children[k];
  2032.         char *psz_name = input_item_GetTitleFbName( p_child->p_input );
  2033.         if( c && *c )
  2034.         {
  2035.             if( asprintf( &psz_display, "%s%c-%s", c,
  2036.                     k == p_node->i_children - 1 ?  '`' : '|', psz_name ) == -1 )
  2037.                 return;
  2038.         }
  2039.         else
  2040.         {
  2041.             if( asprintf( &psz_display, " %s", psz_name ) == -1 )
  2042.                 return;
  2043.         }
  2044.         free( psz_name );
  2045.         struct pl_item_t *p_pl_item = malloc( sizeof( struct pl_item_t ) );
  2046.         if( !p_pl_item )
  2047.             return;
  2048.         p_pl_item->psz_display = psz_display;
  2049.         p_pl_item->p_item = p_child;
  2050.         INSERT_ELEM( p_sys->pp_plist, p_sys->i_plist_entries,
  2051.                      p_sys->i_plist_entries, p_pl_item );
  2052.         i++;
  2053.         if( p_child->i_children > 0 )
  2054.         {
  2055.             char *psz_tmp;
  2056.             if( asprintf( &psz_tmp, "%s%c ", c,
  2057.                      k == p_node->i_children - 1 ? ' ' : '|' ) == -1 )
  2058.                 return;
  2059.             PlaylistAddNode( p_intf, p_child, i,
  2060.                              strlen( c ) ? psz_tmp : " " );
  2061.             free( psz_tmp );
  2062.         }
  2063.     }
  2064. }
  2065. static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
  2066.                             vlc_value_t oval, vlc_value_t nval, void *param )
  2067. {
  2068.     VLC_UNUSED(p_this); VLC_UNUSED(psz_variable);
  2069.     VLC_UNUSED(oval); VLC_UNUSED(nval);
  2070.     intf_thread_t *p_intf = (intf_thread_t *)param;
  2071.     playlist_t *p_playlist = pl_Hold( p_intf );
  2072.     p_intf->p_sys->b_need_update = true;
  2073.     p_intf->p_sys->p_node = playlist_CurrentPlayingItem(p_playlist) ? playlist_CurrentPlayingItem(p_playlist)->p_parent : NULL;
  2074.     pl_Release( p_intf );
  2075.     return VLC_SUCCESS;
  2076. }
  2077. /* Playlist suxx */
  2078. /* This function have to be called with the playlist locked */
  2079. static inline bool PlaylistIsPlaying( playlist_t *p_playlist,
  2080.                                       playlist_item_t *p_item )
  2081. {
  2082.     playlist_item_t *p_played_item = playlist_CurrentPlayingItem( p_playlist );
  2083.     return( p_item != NULL && p_played_item != NULL &&
  2084.             p_item->p_input != NULL && p_played_item->p_input != NULL &&
  2085.             p_item->p_input->i_id == p_played_item->p_input->i_id );
  2086. }
  2087. static void FindIndex( intf_thread_t *p_intf, playlist_t *p_playlist )
  2088. {
  2089.     intf_sys_t *p_sys = p_intf->p_sys;
  2090.     int i;
  2091.     if( p_sys->i_box_plidx < p_sys->i_plist_entries && p_sys->i_box_plidx >= 0 )
  2092.     {
  2093.         playlist_item_t *p_item = p_sys->pp_plist[p_sys->i_box_plidx]->p_item;
  2094.         PL_LOCK;
  2095.         if( ( p_item->i_children == 0 && p_item == p_sys->p_node ) ||
  2096.                 PlaylistIsPlaying( p_playlist, p_item ) )
  2097.         {
  2098.             PL_UNLOCK;
  2099.             return;
  2100.         }
  2101.     }
  2102.     for( i = 0; i < p_sys->i_plist_entries; i++ )
  2103.     {
  2104.         playlist_item_t *p_item = p_sys->pp_plist[i]->p_item;
  2105.         if( ( p_item->i_children == 0 && p_item == p_sys->p_node ) ||
  2106.                 PlaylistIsPlaying( p_playlist, p_sys->pp_plist[i]->p_item ) )
  2107.         {
  2108.             p_sys->i_box_plidx = i;
  2109.             break;
  2110.         }
  2111.     }
  2112.     PL_UNLOCK;
  2113. }
  2114. static void PlaylistDestroy( intf_thread_t *p_intf )
  2115. {
  2116.     intf_sys_t *p_sys = p_intf->p_sys;
  2117.     while( p_sys->i_plist_entries )
  2118.     {
  2119.         struct pl_item_t *p_pl_item = p_sys->pp_plist[0];
  2120.         free( p_pl_item->psz_display );
  2121.         REMOVE_ELEM( p_sys->pp_plist, p_sys->i_plist_entries, 0 );
  2122.         free( p_pl_item );
  2123.     }
  2124.     p_sys->pp_plist = NULL;
  2125. }
  2126. static void Eject( intf_thread_t *p_intf )
  2127. {
  2128.     char *psz_device = NULL, *psz_parser, *psz_name;
  2129.     /*
  2130.      * Get the active input
  2131.      * Determine whether we can eject a media, ie it's a DVD, VCD or CD-DA
  2132.      * If it's neither of these, then return
  2133.      */
  2134.     playlist_t * p_playlist = pl_Hold( p_intf );
  2135.     PL_LOCK;
  2136.     if( playlist_CurrentPlayingItem(p_playlist) == NULL )
  2137.     {
  2138.         PL_UNLOCK;
  2139.         pl_Release( p_intf );
  2140.         return;
  2141.     }
  2142.     psz_name = playlist_CurrentPlayingItem(p_playlist)->p_input->psz_name;
  2143.     if( psz_name )
  2144.     {
  2145.         if( !strncmp(psz_name, "dvd://", 4) )
  2146.         {
  2147.             switch( psz_name[strlen("dvd://")] )
  2148.             {
  2149.             case '':
  2150.             case '@':
  2151.                 psz_device = config_GetPsz( p_intf, "dvd" );
  2152.                 break;
  2153.             default:
  2154.                 /* Omit the first MRL-selector characters */
  2155.                 psz_device = strdup( psz_name + strlen("dvd://" ) );
  2156.                 break;
  2157.             }
  2158.         }
  2159.         else if( !strncmp(psz_name, VCD_MRL, strlen(VCD_MRL)) )
  2160.         {
  2161.             switch( psz_name[strlen(VCD_MRL)] )
  2162.             {
  2163.             case '':
  2164.             case '@':
  2165.                 psz_device = config_GetPsz( p_intf, VCD_MRL );
  2166.                 break;
  2167.             default:
  2168.                 /* Omit the beginning MRL-selector characters */
  2169.                 psz_device = strdup( psz_name + strlen(VCD_MRL) );
  2170.                 break;
  2171.             }
  2172.         }
  2173.         else if( !strncmp(psz_name, CDDA_MRL, strlen(CDDA_MRL) ) )
  2174.         {
  2175.             switch( psz_name[strlen(CDDA_MRL)] )
  2176.             {
  2177.             case '':
  2178.             case '@':
  2179.                 psz_device = config_GetPsz( p_intf, "cd-audio" );
  2180.                 break;
  2181.             default:
  2182.                 /* Omit the beginning MRL-selector characters */
  2183.                 psz_device = strdup( psz_name + strlen(CDDA_MRL) );
  2184.                 break;
  2185.             }
  2186.         }
  2187.         else
  2188.         {
  2189.             psz_device = strdup( psz_name );
  2190.         }
  2191.     }
  2192.     PL_UNLOCK;
  2193.     if( psz_device == NULL )
  2194.     {
  2195.         pl_Release( p_intf );
  2196.         return;
  2197.     }
  2198.     /* Remove what we have after @ */
  2199.     psz_parser = psz_device;
  2200.     for( psz_parser = psz_device ; *psz_parser ; psz_parser++ )
  2201.     {
  2202.         if( *psz_parser == '@' )
  2203.         {
  2204.             *psz_parser = '';
  2205.             break;
  2206.         }
  2207.     }
  2208.     /* If there's a stream playing, we aren't allowed to eject ! */
  2209.     if( p_intf->p_sys->p_input == NULL )
  2210.     {
  2211.         msg_Dbg( p_intf, "ejecting %s", psz_device );
  2212.         intf_Eject( p_intf, psz_device );
  2213.     }
  2214.     free( psz_device );
  2215.     pl_Release( p_intf );
  2216.     return;
  2217. }
  2218. static int comp_dir_entries( const void *pp_dir_entry1,
  2219.                              const void *pp_dir_entry2 )
  2220. {
  2221.     struct dir_entry_t *p_dir_entry1 = *(struct dir_entry_t**)pp_dir_entry1;
  2222.     struct dir_entry_t *p_dir_entry2 = *(struct dir_entry_t**)pp_dir_entry2;
  2223.     if ( p_dir_entry1->b_file == p_dir_entry2->b_file ) {
  2224.         return strcasecmp( p_dir_entry1->psz_path, p_dir_entry2->psz_path );
  2225.     }
  2226.     else
  2227.     {
  2228.         return ( p_dir_entry1->b_file ? 1 : -1 );
  2229.     }
  2230. }
  2231. static void ReadDir( intf_thread_t *p_intf )
  2232. {
  2233.     intf_sys_t *p_sys = p_intf->p_sys;
  2234.     DIR *p_current_dir;
  2235.     int i;
  2236.     if( p_sys->psz_current_dir && *p_sys->psz_current_dir )
  2237.     {
  2238.         char *psz_entry;
  2239.         /* Open the dir */
  2240.         p_current_dir = utf8_opendir( p_sys->psz_current_dir );
  2241.         if( p_current_dir == NULL )
  2242.         {
  2243.             /* something went bad, get out of here ! */
  2244.             msg_Warn( p_intf, "cannot open directory `%s' (%m)",
  2245.                       p_sys->psz_current_dir );
  2246.             return;
  2247.         }
  2248.         /* Clean the old shit */
  2249.         for( i = 0; i < p_sys->i_dir_entries; i++ )
  2250.         {
  2251.             struct dir_entry_t *p_dir_entry = p_sys->pp_dir_entries[i];
  2252.             free( p_dir_entry->psz_path );
  2253.             REMOVE_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries, i );
  2254.             free( p_dir_entry );
  2255.         }
  2256.         p_sys->pp_dir_entries = NULL;
  2257.         p_sys->i_dir_entries = 0;
  2258.         /* while we still have entries in the directory */
  2259.         while( ( psz_entry = utf8_readdir( p_current_dir ) ) != NULL )
  2260.         {
  2261. #if defined( S_ISDIR )
  2262.             struct stat stat_data;
  2263. #endif
  2264.             struct dir_entry_t *p_dir_entry;
  2265.             char *psz_uri;
  2266.             if( p_sys->b_show_hidden_files == false &&
  2267.                 ( strlen( psz_entry ) && psz_entry[0] == '.' ) &&
  2268.                 strcmp( psz_entry, ".." ) )
  2269.             {
  2270.                 free( psz_entry );
  2271.                 continue;
  2272.             }
  2273.             if( asprintf( &psz_uri, "%s/%s", p_sys->psz_current_dir,
  2274.                         psz_entry ) == -1)
  2275.             {
  2276.                 free( psz_entry );
  2277.                 continue;
  2278.             }
  2279.             if( !( p_dir_entry = malloc( sizeof( struct dir_entry_t) ) ) )
  2280.             {
  2281.                 free( psz_uri );
  2282.                 free( psz_entry );
  2283.                 continue;
  2284.             }
  2285. #if defined( S_ISDIR )
  2286.             if( !utf8_stat( psz_uri, &stat_data )
  2287.              && S_ISDIR(stat_data.st_mode) )
  2288. /*#elif defined( DT_DIR )
  2289.             if( p_dir_content->d_type & DT_DIR )*/
  2290. #else
  2291.             if( 0 )
  2292. #endif
  2293.             {
  2294.                 p_dir_entry->psz_path = strdup( psz_entry );
  2295.                 p_dir_entry->b_file = false;
  2296.                 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
  2297.                      p_sys->i_dir_entries, p_dir_entry );
  2298.             }
  2299.             else
  2300.             {
  2301.                 p_dir_entry->psz_path = strdup( psz_entry );
  2302.                 p_dir_entry->b_file = true;
  2303.                 INSERT_ELEM( p_sys->pp_dir_entries, p_sys->i_dir_entries,
  2304.                      p_sys->i_dir_entries, p_dir_entry );
  2305.             }
  2306.             free( psz_uri );
  2307.             free( psz_entry );
  2308.         }
  2309.         /* Sort */
  2310.         qsort( p_sys->pp_dir_entries, p_sys->i_dir_entries,
  2311.                sizeof(struct dir_entry_t*), &comp_dir_entries );
  2312.         closedir( p_current_dir );
  2313.         return;
  2314.     }
  2315.     else
  2316.     {
  2317.         msg_Dbg( p_intf, "no current dir set" );
  2318.         return;
  2319.     }
  2320. }
  2321. static void PlayPause( intf_thread_t *p_intf )
  2322. {
  2323.     input_thread_t *p_input = p_intf->p_sys->p_input;
  2324.     playlist_t *p_playlist = pl_Hold( p_intf );
  2325.     vlc_value_t val;
  2326.     if( p_input )
  2327.     {
  2328.         var_Get( p_input, "state", &val );
  2329.         if( val.i_int != PAUSE_S )
  2330.         {
  2331.             val.i_int = PAUSE_S;
  2332.         }
  2333.         else
  2334.         {
  2335.             val.i_int = PLAYING_S;
  2336.         }
  2337.         var_Set( p_input, "state", val );
  2338.     }
  2339.     else
  2340.         playlist_Play( p_playlist );
  2341.     pl_Release( p_intf );
  2342. }
  2343. /****************************************************************************
  2344.  *
  2345.  ****************************************************************************/
  2346. static void DrawBox( WINDOW *win, int y, int x, int h, int w, const char *title, bool b_color )
  2347. {
  2348.     int i;
  2349.     int i_len;
  2350.     if( w > 3 && h > 2 )
  2351.     {
  2352.         if( b_color )
  2353.             wcolor_set( win, C_BOX, NULL );
  2354.         if( title == NULL ) title = "";
  2355.         i_len = strlen( title );
  2356.         if( i_len > w - 2 ) i_len = w - 2;
  2357.         mvwaddch( win, y, x,    ACS_ULCORNER );
  2358.         mvwhline( win, y, x+1,  ACS_HLINE, ( w-i_len-2)/2 );
  2359.         mvwprintw( win,y, x+1+(w-i_len-2)/2, "%s", title );
  2360.         mvwhline( win, y, x+(w-i_len)/2+i_len,  ACS_HLINE, w - 1 - ((w-i_len)/2+i_len) );
  2361.         mvwaddch( win, y, x+w-1,ACS_URCORNER );
  2362.         for( i = 0; i < h-2; i++ )
  2363.         {
  2364.             mvwaddch( win, y+i+1, x,     ACS_VLINE );
  2365.             mvwaddch( win, y+i+1, x+w-1, ACS_VLINE );
  2366.         }
  2367.         mvwaddch( win, y+h-1, x,     ACS_LLCORNER );
  2368.         mvwhline( win, y+h-1, x+1,   ACS_HLINE, w - 2 );
  2369.         mvwaddch( win, y+h-1, x+w-1, ACS_LRCORNER );
  2370.         if( b_color )
  2371.             wcolor_set( win, C_DEFAULT, NULL );
  2372.     }
  2373. }
  2374. static void DrawEmptyLine( WINDOW *win, int y, int x, int w )
  2375. {
  2376.     if( w > 0 )
  2377.     {
  2378.         mvwhline( win, y, x, ' ', w );
  2379.     }
  2380. }
  2381. static void DrawLine( WINDOW *win, int y, int x, int w )
  2382. {
  2383.     if( w > 0 )
  2384.     {
  2385.         attrset( A_REVERSE );
  2386.         mvwhline( win, y, x, ' ', w );
  2387.         attroff( A_REVERSE );
  2388.     }
  2389. }