hotkeys.c 34.2 KB
Newer Older
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
1 2 3
/*****************************************************************************
 * hotkeys.c: Hotkey handling for vlc
 *****************************************************************************
4
 * Copyright (C) 2005 the VideoLAN team
5
 * $Id$
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
6
 *
7
 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8
 *          Jean-Paul Saman <jpsaman #_at_# m2x.nl>
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
14
 *
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
15 16 17 18 19 20 21
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
23 24 25 26 27 28 29 30
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */

#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
31 32 33 34
#include <vlc_interface.h>
#include <vlc_input.h>
#include <vlc_vout.h>
#include <vlc_aout.h>
35
#include <vlc_osd.h>
Clément Stenac's avatar
Clément Stenac committed
36
#include <vlc_playlist.h>
Gildas Bazin's avatar
 
Gildas Bazin committed
37 38
#include "vlc_keys.h"

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
39
#define BUFFER_SIZE 10
40 41 42 43 44 45

#define CHANNELS_NUMBER 4
#define VOLUME_TEXT_CHAN     p_intf->p_sys->p_channels[ 0 ]
#define VOLUME_WIDGET_CHAN   p_intf->p_sys->p_channels[ 1 ]
#define POSITION_TEXT_CHAN   p_intf->p_sys->p_channels[ 2 ]
#define POSITION_WIDGET_CHAN p_intf->p_sys->p_channels[ 3 ]
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
46 47 48 49 50 51 52 53 54 55 56 57
/*****************************************************************************
 * intf_sys_t: description and status of FB interface
 *****************************************************************************/
struct intf_sys_t
{
    vlc_mutex_t         change_lock;  /* mutex to keep the callback
                                       * and the main loop from
                                       * stepping on each others
                                       * toes */
    int                 p_keys[ BUFFER_SIZE ]; /* buffer that contains
                                                * keyevents */
    int                 i_size;        /* number of events in buffer */
58 59
    int                 p_channels[ CHANNELS_NUMBER ]; /* contains registered
                                                        * channel IDs */
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
60 61 62 63 64 65 66 67 68 69 70 71 72
    input_thread_t *    p_input;       /* pointer to input */
    vout_thread_t *     p_vout;        /* pointer to vout object */
};

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  Open    ( vlc_object_t * );
static void Close   ( vlc_object_t * );
static void Run     ( intf_thread_t * );
static int  GetKey  ( intf_thread_t *);
static int  KeyEvent( vlc_object_t *, char const *,
                      vlc_value_t, vlc_value_t, void * );
Gildas Bazin's avatar
 
Gildas Bazin committed
73 74
static int  ActionKeyCB( vlc_object_t *, char const *,
                         vlc_value_t, vlc_value_t, void * );
75 76
static void PlayBookmark( intf_thread_t *, int );
static void SetBookmark ( intf_thread_t *, int );
77 78 79
static void DisplayPosition( intf_thread_t *, vout_thread_t *, input_thread_t * );
static void DisplayVolume  ( intf_thread_t *, vout_thread_t *, audio_volume_t );
static void ClearChannels  ( intf_thread_t *, vout_thread_t * );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
80 81 82 83

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
84 85 86 87 88 89 90 91 92 93 94
#define BOOKMARK1_TEXT    N_("Playlist bookmark 1")
#define BOOKMARK2_TEXT    N_("Playlist bookmark 2")
#define BOOKMARK3_TEXT    N_("Playlist bookmark 3")
#define BOOKMARK4_TEXT    N_("Playlist bookmark 4")
#define BOOKMARK5_TEXT    N_("Playlist bookmark 5")
#define BOOKMARK6_TEXT    N_("Playlist bookmark 6")
#define BOOKMARK7_TEXT    N_("Playlist bookmark 7")
#define BOOKMARK8_TEXT    N_("Playlist bookmark 8")
#define BOOKMARK9_TEXT    N_("Playlist bookmark 9")
#define BOOKMARK10_TEXT   N_("Playlist bookmark 10")
#define BOOKMARK_LONGTEXT N_("Define playlist bookmarks.")
95

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
96
vlc_module_begin();
97
    set_shortname( _("Hotkeys") );
Anil Daoud's avatar
Anil Daoud committed
98
    set_description( _("Hotkeys management interface") );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
99 100 101 102 103 104 105 106 107 108
    set_capability( "interface", 0 );
    set_callbacks( Open, Close );
vlc_module_end();

/*****************************************************************************
 * Open: initialize interface
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;
Clément Stenac's avatar
Woops  
Clément Stenac committed
109
    MALLOC_ERR( p_intf->p_sys, intf_sys_t );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
110 111 112 113 114

    vlc_mutex_init( p_intf, &p_intf->p_sys->change_lock );
    p_intf->p_sys->i_size = 0;
    p_intf->pf_run = Run;

115
    var_AddCallback( p_intf->p_libvlc, "key-pressed", KeyEvent, p_intf );
116
    return VLC_SUCCESS;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
117 118 119 120 121 122 123 124 125
}

/*****************************************************************************
 * Close: destroy interface
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;

126
    var_DelCallback( p_intf->p_libvlc, "key-pressed", KeyEvent, p_intf );
127

128
    vlc_mutex_destroy( &p_intf->p_sys->change_lock );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
129 130 131 132 133 134 135 136 137
    /* Destroy structure */
    free( p_intf->p_sys );
}

/*****************************************************************************
 * Run: main loop
 *****************************************************************************/
static void Run( intf_thread_t *p_intf )
{
138
    input_thread_t *p_input = NULL;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
139
    vout_thread_t *p_vout = NULL;
140
    vout_thread_t *p_last_vout = NULL;
141
    struct hotkey *p_hotkeys = p_intf->p_libvlc->p_hotkeys;
Gildas Bazin's avatar
 
Gildas Bazin committed
142 143
    vlc_value_t val;
    int i;
144
    playlist_t *p_playlist = pl_Yield( p_intf );
Gildas Bazin's avatar
 
Gildas Bazin committed
145 146 147 148

    /* Initialize hotkey structure */
    for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
    {
149
        var_Create( p_intf->p_libvlc, p_hotkeys[i].psz_action,
Gildas Bazin's avatar
 
Gildas Bazin committed
150 151
                    VLC_VAR_HOTKEY | VLC_VAR_DOINHERIT );

152
        var_AddCallback( p_intf->p_libvlc, p_hotkeys[i].psz_action,
Gildas Bazin's avatar
 
Gildas Bazin committed
153
                         ActionKeyCB, NULL );
154 155
        var_Get( p_intf->p_libvlc, p_hotkeys[i].psz_action, &val );
        var_Set( p_intf->p_libvlc, p_hotkeys[i].psz_action, val );
Gildas Bazin's avatar
 
Gildas Bazin committed
156 157
    }

158
    while( !intf_ShouldDie( p_intf ) )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
159
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
160
        int i_key, i_action;
161
        int i_times = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
162

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
163
        /* Sleep a bit */
164
//        msleep( INTF_IDLE_SLEEP );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
165

Gildas Bazin's avatar
 
Gildas Bazin committed
166 167
        /* Find action triggered by hotkey */
        i_action = 0;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
168
        i_key = GetKey( p_intf );
169
        for( i = 0; i_key != -1 && p_hotkeys[i].psz_action != NULL; i++ )
Gildas Bazin's avatar
 
Gildas Bazin committed
170 171 172
        {
            if( p_hotkeys[i].i_key == i_key )
            {
173
                i_action = p_hotkeys[i].i_action;
174 175
                i_times  = p_hotkeys[i].i_times;
                /* times key pressed within max. delta time */
176
                p_hotkeys[i].i_times = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
177 178 179 180
            }
        }

        if( !i_action )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
181
        {
182 183 184
            vlc_mutex_lock( &p_intf->object_lock );
            vlc_cond_wait( &p_intf->object_wait, &p_intf->object_lock );
            vlc_mutex_unlock( &p_intf->object_lock );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
185
            /* No key pressed, sleep a bit more */
186
//            msleep( INTF_IDLE_SLEEP );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
187 188
            continue;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
189

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
        /* Update the input */
        PL_LOCK;
        p_input = p_playlist->p_input;
        if( p_input )
            vlc_object_yield( p_input );
        PL_UNLOCK;
        
        /* Update the vout */
        p_last_vout = p_vout;
        p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );

        /* Register OSD channels */
        if( p_vout && p_vout != p_last_vout )
        {
            for( i = 0; i < CHANNELS_NUMBER; i++ )
            {
                spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER,
                             &p_intf->p_sys->p_channels[ i ] );
            }
        }

Gildas Bazin's avatar
 
Gildas Bazin committed
211
        if( i_action == ACTIONID_QUIT )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
212
        {
213
            p_intf->p_libvlc->b_die = VLC_TRUE;
214 215
            ClearChannels( p_intf, p_vout );
            vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Quit" ) );
216 217 218 219
            if( p_vout )
                vlc_object_release( p_vout );
            if( p_input )
                vlc_object_release( p_input );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
220 221
            continue;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
222
        else if( i_action == ACTIONID_VOL_UP )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
223 224 225
        {
            audio_volume_t i_newvol;
            aout_VolumeUp( p_intf, 1, &i_newvol );
226
            DisplayVolume( p_intf, p_vout, i_newvol );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
227
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
228
        else if( i_action == ACTIONID_VOL_DOWN )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
229 230 231
        {
            audio_volume_t i_newvol;
            aout_VolumeDown( p_intf, 1, &i_newvol );
232
            DisplayVolume( p_intf, p_vout, i_newvol );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
233
        }
Yoann Peronneau's avatar
Yoann Peronneau committed
234 235 236 237 238 239 240 241
        else if( i_action == ACTIONID_VOL_MUTE )
        {
            audio_volume_t i_newvol = -1;
            aout_VolumeMute( p_intf, &i_newvol );
            if( p_vout )
            {
                if( i_newvol == 0 )
                {
242 243 244
                    ClearChannels( p_intf, p_vout );
                    vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
                                  OSD_MUTE_ICON );
Yoann Peronneau's avatar
Yoann Peronneau committed
245 246 247
                }
                else
                {
248
                    DisplayVolume( p_intf, p_vout, i_newvol );
Yoann Peronneau's avatar
Yoann Peronneau committed
249 250 251
                }
            }
        }
252
        else if( i_action == ACTIONID_INTF_SHOW )
253
            var_SetBool( p_playlist, "intf-show", VLC_TRUE );
254
        else if( i_action == ACTIONID_INTF_HIDE )
255
            var_SetBool( p_playlist, "intf-show", VLC_FALSE );
256 257 258 259
        else if( i_action == ACTIONID_SNAPSHOT )
        {
            if( p_vout ) vout_Control( p_vout, VOUT_SNAPSHOT );
        }
260
        else if( i_action == ACTIONID_FULLSCREEN )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
261
        {
262 263
            if( p_vout )
            {
264 265
                var_Get( p_vout, "fullscreen", &val );
                val.b_bool = !val.b_bool;
266 267 268 269
                var_Set( p_vout, "fullscreen", val );
            }
            else
            {
270 271 272
                var_Get( p_playlist, "fullscreen", &val );
                val.b_bool = !val.b_bool;
                var_Set( p_playlist, "fullscreen", val );
273
            }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
274
        }
Antoine Cellerier's avatar
Antoine Cellerier committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
        else if( i_action == ACTIONID_LOOP )
        {
            /* Toggle Normal -> Loop -> Repeat -> Normal ... */
            vlc_value_t val2;
            var_Get( p_playlist, "loop", &val );
            var_Get( p_playlist, "repeat", &val2 );
            if( val2.b_bool == VLC_TRUE )
            {
                val.b_bool = VLC_FALSE;
                val2.b_bool = VLC_FALSE;
            }
            else if( val.b_bool == VLC_TRUE )
            {
                val.b_bool = VLC_FALSE;
                val2.b_bool = VLC_TRUE;
            }
            else
            {
                val.b_bool = VLC_TRUE;
            }
            var_Set( p_playlist, "loop", val );
            var_Set( p_playlist, "repeat", val2 );
        }
        else if( i_action == ACTIONID_RANDOM )
        {
            var_Get( p_playlist, "random", &val );
            val.b_bool = !val.b_bool;
            var_Set( p_playlist, "random", val );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
304
        else if( i_action == ACTIONID_PLAY_PAUSE )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
305
        {
306 307
            val.i_int = PLAYING_S;
            if( p_input )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
308
            {
309
                ClearChannels( p_intf, p_vout );
310 311 312 313 314 315 316 317 318 319 320 321 322 323

                var_Get( p_input, "state", &val );
                if( val.i_int != PAUSE_S )
                {
                    vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
                                  OSD_PAUSE_ICON );
                    val.i_int = PAUSE_S;
                }
                else
                {
                    vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
                                  OSD_PLAY_ICON );
                    val.i_int = PLAYING_S;
                }
324
                var_Set( p_input, "state", val );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
325 326 327
            }
            else
            {
328
                playlist_Play( p_playlist );
Gildas Bazin's avatar
 
Gildas Bazin committed
329
            }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
330 331 332
        }
        else if( p_input )
        {
Laurent Aimar's avatar
Laurent Aimar committed
333 334 335 336 337
            /* FIXME --fenrir
             * How to get a valid value ?
             * That's not that easy with some special stream
             */
            vlc_bool_t b_seekable = VLC_TRUE;
338
            int i_interval =0;
339

Gildas Bazin's avatar
 
Gildas Bazin committed
340
            if( i_action == ACTIONID_PAUSE )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
341
            {
342 343 344 345 346 347 348 349 350
                var_Get( p_input, "state", &val );
                if( val.i_int != PAUSE_S )
                {
                    ClearChannels( p_intf, p_vout );
                    vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
                                  OSD_PAUSE_ICON );
                    val.i_int = PAUSE_S;
                    var_Set( p_input, "state", val );
                }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
351
            }
352 353
            else if( i_action == ACTIONID_JUMP_BACKWARD_EXTRASHORT
                     && b_seekable )
354
            {
355
#define SET_TIME( a, b ) \
Clément Stenac's avatar
Clément Stenac committed
356
    i_interval = config_GetInt( p_input, a "-jump-size" ); \
357 358 359 360 361 362 363
    if( i_interval > 0 ) { \
        val.i_time = ( (mtime_t)(i_interval * b) * 1000000L \
                       * ((mtime_t)(1 << i_times))); \
        var_Set( p_input, "time-offset", val ); \
        DisplayPosition( p_intf, p_vout, p_input ); \
    }
                SET_TIME( "extrashort", -1 );
364
            }
365
            else if( i_action == ACTIONID_JUMP_FORWARD_EXTRASHORT && b_seekable )
366
            {
367
                SET_TIME( "extrashort", 1 );
368
            }
369
            else if( i_action == ACTIONID_JUMP_BACKWARD_SHORT && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
370
            {
371
                SET_TIME( "short", -1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
372
            }
373
            else if( i_action == ACTIONID_JUMP_FORWARD_SHORT && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
374
            {
375
                SET_TIME( "short", 1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
376
            }
377
            else if( i_action == ACTIONID_JUMP_BACKWARD_MEDIUM && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
378
            {
379
                SET_TIME( "medium", -1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
380
            }
381
            else if( i_action == ACTIONID_JUMP_FORWARD_MEDIUM && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
382
            {
383
                SET_TIME( "medium", 1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
384
            }
385
            else if( i_action == ACTIONID_JUMP_BACKWARD_LONG && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
386
            {
387
                SET_TIME( "long", -1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
388
            }
389
            else if( i_action == ACTIONID_JUMP_FORWARD_LONG && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
390
            {
391 392
                SET_TIME( "long", 1 );
#undef SET_TIME
Gildas Bazin's avatar
 
Gildas Bazin committed
393
            }
394 395 396 397 398 399 400 401
            else if( i_action == ACTIONID_AUDIO_TRACK )
            {
                vlc_value_t val, list, list2;
                int i_count, i;
                var_Get( p_input, "audio-es", &val );
                var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
                            &list, &list2 );
                i_count = list.p_list->i_count;
402 403 404 405
                if( i_count <= 1 )
                {
                    continue;
                }
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
                for( i = 0; i < i_count; i++ )
                {
                    if( val.i_int == list.p_list->p_values[i].i_int )
                    {
                        break;
                    }
                }
                /* value of audio-es was not in choices list */
                if( i == i_count )
                {
                    msg_Warn( p_input,
                              "invalid current audio track, selecting 0" );
                    var_Set( p_input, "audio-es",
                             list.p_list->p_values[0] );
                    i = 0;
                }
                else if( i == i_count - 1 )
                {
                    var_Set( p_input, "audio-es",
425 426
                             list.p_list->p_values[1] );
                    i = 1;
427 428 429 430 431 432 433 434 435 436 437
                }
                else
                {
                    var_Set( p_input, "audio-es",
                             list.p_list->p_values[i+1] );
                    i++;
                }
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                 _("Audio track: %s"),
                                 list2.p_list->p_values[i].psz_string );
            }
438
            else if( i_action == ACTIONID_SUBTITLE_TRACK )
439 440 441 442
            {
                vlc_value_t val, list, list2;
                int i_count, i;
                var_Get( p_input, "spu-es", &val );
443

444 445 446
                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
                            &list, &list2 );
                i_count = list.p_list->i_count;
447 448
                if( i_count <= 1 )
                {
449 450
                    vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                     _("Subtitle track: %s"), _("N/A") );
451 452
                    continue;
                }
453 454 455 456 457 458 459
                for( i = 0; i < i_count; i++ )
                {
                    if( val.i_int == list.p_list->p_values[i].i_int )
                    {
                        break;
                    }
                }
460
                /* value of spu-es was not in choices list */
461 462
                if( i == i_count )
                {
463 464
                    msg_Warn( p_input,
                              "invalid current subtitle track, selecting 0" );
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
                    var_Set( p_input, "spu-es", list.p_list->p_values[0] );
                    i = 0;
                }
                else if( i == i_count - 1 )
                {
                    var_Set( p_input, "spu-es", list.p_list->p_values[0] );
                    i = 0;
                }
                else
                {
                    var_Set( p_input, "spu-es", list.p_list->p_values[i+1] );
                    i = i + 1;
                }
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                 _("Subtitle track: %s"),
                                 list2.p_list->p_values[i].psz_string );
            }
482
            else if( i_action == ACTIONID_ASPECT_RATIO && p_vout )
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
            {
                vlc_value_t val={0}, val_list, text_list;
                var_Get( p_vout, "aspect-ratio", &val );
                if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETLIST,
                                &val_list, &text_list ) >= 0 )
                {
                    int i;
                    for( i = 0; i < val_list.p_list->i_count; i++ )
                    {
                        if( !strcmp( val_list.p_list->p_values[i].psz_string,
                                     val.psz_string ) )
                        {
                            i++;
                            break;
                        }
                    }
                    if( i == val_list.p_list->i_count ) i = 0;
                    var_SetString( p_vout, "aspect-ratio",
                                   val_list.p_list->p_values[i].psz_string );
                    vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                     _("Aspect ratio: %s"),
                                     text_list.p_list->p_values[i].psz_string );
                }
                free( val.psz_string );
            }
508
            else if( i_action == ACTIONID_CROP && p_vout )
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
            {
                vlc_value_t val={0}, val_list, text_list;
                var_Get( p_vout, "crop", &val );
                if( var_Change( p_vout, "crop", VLC_VAR_GETLIST,
                                &val_list, &text_list ) >= 0 )
                {
                    int i;
                    for( i = 0; i < val_list.p_list->i_count; i++ )
                    {
                        if( !strcmp( val_list.p_list->p_values[i].psz_string,
                                     val.psz_string ) )
                        {
                            i++;
                            break;
                        }
                    }
                    if( i == val_list.p_list->i_count ) i = 0;
                    var_SetString( p_vout, "crop",
                                   val_list.p_list->p_values[i].psz_string );
                    vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                     _("Crop: %s"),
                                     text_list.p_list->p_values[i].psz_string );
                }
                free( val.psz_string );
            }
534
            else if( i_action == ACTIONID_DEINTERLACE && p_vout )
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
            {
                vlc_value_t val={0}, val_list, text_list;
                var_Get( p_vout, "deinterlace", &val );
                if( var_Change( p_vout, "deinterlace", VLC_VAR_GETLIST,
                                &val_list, &text_list ) >= 0 )
                {
                    int i;
                    for( i = 0; i < val_list.p_list->i_count; i++ )
                    {
                        if( !strcmp( val_list.p_list->p_values[i].psz_string,
                                     val.psz_string ) )
                        {
                            i++;
                            break;
                        }
                    }
                    if( i == val_list.p_list->i_count ) i = 0;
                    var_SetString( p_vout, "deinterlace",
                                   val_list.p_list->p_values[i].psz_string );
                    vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                     _("Deinterlace mode: %s"),
                                     text_list.p_list->p_values[i].psz_string );
                }
                free( val.psz_string );
            }
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
            else if( ( i_action == ACTIONID_ZOOM || i_action == ACTIONID_UNZOOM ) && p_vout )
            {
                vlc_value_t val={0}, val_list, text_list;
                var_Get( p_vout, "zoom", &val );
                if( var_Change( p_vout, "zoom", VLC_VAR_GETLIST,
                                &val_list, &text_list ) >= 0 )
                {
                    int i;
                    for( i = 0; i < val_list.p_list->i_count; i++ )
                    {
                        if( val_list.p_list->p_values[i].f_float
                           == val.f_float )
                        {
                            if( i_action == ACTIONID_ZOOM )
                                i++;
                            else /* ACTIONID_UNZOOM */
                                i--;
                            break;
                        }
                    }
                    if( i == val_list.p_list->i_count ) i = 0;
                    if( i == -1 ) i = val_list.p_list->i_count-1;
                    var_SetFloat( p_vout, "zoom",
                                  val_list.p_list->p_values[i].f_float );
                    vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                     _("Zoom mode: %s"),
                                text_list.p_list->p_values[i].var.psz_name );
                }
            }
589
            else if( i_action == ACTIONID_CROP_TOP && p_vout )
590
                var_IncInteger( p_vout, "crop-top" );
591
            else if( i_action == ACTIONID_UNCROP_TOP && p_vout )
592
                var_DecInteger( p_vout, "crop-top" );
593
            else if( i_action == ACTIONID_CROP_BOTTOM && p_vout )
594
                var_IncInteger( p_vout, "crop-bottom" );
595
            else if( i_action == ACTIONID_UNCROP_BOTTOM && p_vout )
596
                 var_DecInteger( p_vout, "crop-bottom" );
597
            else if( i_action == ACTIONID_CROP_LEFT && p_vout )
598
                 var_IncInteger( p_vout, "crop-left" );
599
            else if( i_action == ACTIONID_UNCROP_LEFT && p_vout )
600
                 var_DecInteger( p_vout, "crop-left" );
601
            else if( i_action == ACTIONID_CROP_RIGHT && p_vout )
602
                 var_IncInteger( p_vout, "crop-right" );
603
            else if( i_action == ACTIONID_UNCROP_RIGHT && p_vout )
604 605
                 var_DecInteger( p_vout, "crop-right" );

Gildas Bazin's avatar
 
Gildas Bazin committed
606
            else if( i_action == ACTIONID_NEXT )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
607
            {
608 609
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN, _("Next") );
                playlist_Next( p_playlist );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
610
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
611
            else if( i_action == ACTIONID_PREV )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
612
            {
613 614 615
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                 _("Previous") );
                playlist_Prev( p_playlist );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
616
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
617
            else if( i_action == ACTIONID_STOP )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
618
            {
619
                playlist_Stop( p_playlist );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
620
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
621
            else if( i_action == ACTIONID_FASTER )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
622
            {
623
                var_SetVoid( p_input, "rate-faster" );
624 625
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                 _("Faster") );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
626
            }
627
            else if( i_action == ACTIONID_SLOWER )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
628
            {
629
                var_SetVoid( p_input, "rate-slower" );
630 631
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                 _("Slower") );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
632
            }
633
            else if( i_action == ACTIONID_POSITION && b_seekable )
634
            {
635
                DisplayPosition( p_intf, p_vout, p_input );
636 637 638 639 640 641 642 643 644 645
            }
            else if( i_action >= ACTIONID_PLAY_BOOKMARK1 &&
                     i_action <= ACTIONID_PLAY_BOOKMARK10 )
            {
                PlayBookmark( p_intf, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
            }
            else if( i_action >= ACTIONID_SET_BOOKMARK1 &&
                     i_action <= ACTIONID_SET_BOOKMARK10 )
            {
                SetBookmark( p_intf, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
646
            }
647 648
            /* Only makes sense with DVD */
            else if( i_action == ACTIONID_TITLE_PREV )
649
                var_SetVoid( p_input, "prev-title" );
650
            else if( i_action == ACTIONID_TITLE_NEXT )
651
                var_SetVoid( p_input, "next-title" );
652
            else if( i_action == ACTIONID_CHAPTER_PREV )
653
                var_SetVoid( p_input, "prev-chapter" );
654
            else if( i_action == ACTIONID_CHAPTER_NEXT )
655
                var_SetVoid( p_input, "next-chapter" );
656
            else if( i_action == ACTIONID_DISC_MENU )
657 658
                var_SetInteger( p_input, "title  0", 2 );

659 660 661 662 663 664
            else if( i_action == ACTIONID_SUBDELAY_DOWN )
            {
                int64_t i_delay = var_GetTime( p_input, "spu-delay" );
                i_delay -= 50000;    /* 50 ms */
                var_SetTime( p_input, "spu-delay", i_delay );
                ClearChannels( p_intf, p_vout );
Clément Stenac's avatar
Clément Stenac committed
665 666
                vout_OSDMessage( p_intf, DEFAULT_CHAN,
                                 _( "Subtitle delay %i ms" ),
667 668 669 670 671 672 673 674
                                 (int)(i_delay/1000) );
            }
            else if( i_action == ACTIONID_SUBDELAY_UP )
            {
                int64_t i_delay = var_GetTime( p_input, "spu-delay" );
                i_delay += 50000;    /* 50 ms */
                var_SetTime( p_input, "spu-delay", i_delay );
                ClearChannels( p_intf, p_vout );
Clément Stenac's avatar
Clément Stenac committed
675 676
                vout_OSDMessage( p_intf, DEFAULT_CHAN,
                                _( "Subtitle delay %i ms" ),
677 678 679 680 681 682 683 684
                                 (int)(i_delay/1000) );
            }
            else if( i_action == ACTIONID_AUDIODELAY_DOWN )
            {
                int64_t i_delay = var_GetTime( p_input, "audio-delay" );
                i_delay -= 50000;    /* 50 ms */
                var_SetTime( p_input, "audio-delay", i_delay );
                ClearChannels( p_intf, p_vout );
Clément Stenac's avatar
Clément Stenac committed
685 686
                vout_OSDMessage( p_intf, DEFAULT_CHAN,
                                _( "Audio delay %i ms" ),
687 688 689 690 691 692 693 694
                                 (int)(i_delay/1000) );
            }
            else if( i_action == ACTIONID_AUDIODELAY_UP )
            {
                int64_t i_delay = var_GetTime( p_input, "audio-delay" );
                i_delay += 50000;    /* 50 ms */
                var_SetTime( p_input, "audio-delay", i_delay );
                ClearChannels( p_intf, p_vout );
Clément Stenac's avatar
Clément Stenac committed
695 696
                vout_OSDMessage( p_intf, DEFAULT_CHAN,
                                _( "Audio delay %i ms" ),
697 698 699 700
                                 (int)(i_delay/1000) );
            }
            else if( i_action == ACTIONID_PLAY )
            {
701 702
                var_Get( p_input, "rate", &val );
                if( val.i_int != INPUT_RATE_DEFAULT )
703
                {
704 705 706 707 708 709 710 711 712
                    /* Return to normal speed */
                    var_SetInteger( p_input, "rate", INPUT_RATE_DEFAULT );
                }
                else
                {
                    ClearChannels( p_intf, p_vout );
                    vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
                                  OSD_PLAY_ICON );
                    playlist_Play( p_playlist );
713 714
                }
            }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
715
        }
716 717 718 719
        if( p_vout )
            vlc_object_release( p_vout );
        if( p_input )
            vlc_object_release( p_input );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
720
    }
721
    pl_Release( p_intf );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
722 723
}

Gildas Bazin's avatar
 
Gildas Bazin committed
724
static int GetKey( intf_thread_t *p_intf)
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
725 726 727 728 729
{
    vlc_mutex_lock( &p_intf->p_sys->change_lock );
    if ( p_intf->p_sys->i_size == 0 )
    {
        vlc_mutex_unlock( &p_intf->p_sys->change_lock );
Gildas Bazin's avatar
 
Gildas Bazin committed
730
        return -1;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
    }
    else
    {
        int i_return = p_intf->p_sys->p_keys[ 0 ];
        int i;
        p_intf->p_sys->i_size--;
        for ( i = 0; i < BUFFER_SIZE - 1; i++)
        {
            p_intf->p_sys->p_keys[ i ] = p_intf->p_sys->p_keys[ i + 1 ];
        }
        vlc_mutex_unlock( &p_intf->p_sys->change_lock );
        return i_return;
    }
}

/*****************************************************************************
 * KeyEvent: callback for keyboard events
 *****************************************************************************/
static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
Gildas Bazin's avatar
 
Gildas Bazin committed
750
                     vlc_value_t oldval, vlc_value_t newval, void *p_data )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
751 752 753 754 755
{
    intf_thread_t *p_intf = (intf_thread_t *)p_data;
    vlc_mutex_lock( &p_intf->p_sys->change_lock );
    if ( p_intf->p_sys->i_size == BUFFER_SIZE )
    {
Anil Daoud's avatar
Anil Daoud committed
756
        msg_Warn( p_intf, "event buffer full, dropping keypress" );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
757 758 759 760 761 762 763
        vlc_mutex_unlock( &p_intf->p_sys->change_lock );
        return VLC_EGENERIC;
    }
    else
    {
        p_intf->p_sys->p_keys[ p_intf->p_sys->i_size ] = newval.i_int;
        p_intf->p_sys->i_size++;
Gildas Bazin's avatar
 
Gildas Bazin committed
764
    }
765 766 767
    vlc_mutex_lock( &p_intf->object_lock );
    vlc_cond_signal( &p_intf->object_wait );
    vlc_mutex_unlock( &p_intf->object_lock );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
768 769 770 771 772
    vlc_mutex_unlock( &p_intf->p_sys->change_lock );

    return VLC_SUCCESS;
}

Gildas Bazin's avatar
 
Gildas Bazin committed
773 774 775
static int ActionKeyCB( vlc_object_t *p_this, char const *psz_var,
                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
776 777
    libvlc_int_t *p_libvlc = (libvlc_int_t *)p_this;
    struct hotkey *p_hotkeys = p_libvlc->p_hotkeys;
778
    mtime_t i_date;
Gildas Bazin's avatar
 
Gildas Bazin committed
779 780 781 782 783 784 785
    int i;

    for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
    {
        if( !strcmp( p_hotkeys[i].psz_action, psz_var ) )
        {
            p_hotkeys[i].i_key = newval.i_int;
786
            /* do hotkey accounting */
787 788 789 790 791 792 793
            i_date = mdate();
            if( (p_hotkeys[i].i_delta_date > 0) &&
                (p_hotkeys[i].i_delta_date <= (i_date - p_hotkeys[i].i_last_date) ) )
                p_hotkeys[i].i_times = 0;
            else
                p_hotkeys[i].i_times++;
            p_hotkeys[i].i_last_date = i_date;
Gildas Bazin's avatar
 
Gildas Bazin committed
794 795 796 797 798
        }
    }

    return VLC_SUCCESS;
}
799 800

static void PlayBookmark( intf_thread_t *p_intf, int i_num )
801
{
802 803
    vlc_value_t val;
    char psz_bookmark_name[11];
804
    playlist_t *p_playlist = pl_Yield( p_intf );
805

806 807 808
    sprintf( psz_bookmark_name, "bookmark%i", i_num );
    var_Create( p_intf, psz_bookmark_name, VLC_VAR_STRING|VLC_VAR_DOINHERIT );
    var_Get( p_intf, psz_bookmark_name, &val );
809

810
    char *psz_bookmark = strdup( val.psz_string );
811 812 813
    PL_LOCK;
    FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
        if( !strcmp( psz_bookmark, p_item->p_input->psz_uri ) )
814
        {
815 816
            playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE,
                              NULL, p_item );
817
            break;
818
        }
819
    FOREACH_END();
820
    PL_UNLOCK;
821
    vlc_object_release( p_playlist );
822 823 824 825
}

static void SetBookmark( intf_thread_t *p_intf, int i_num )
{
826 827 828 829 830 831
    playlist_t *p_playlist = pl_Yield( p_intf );
    char psz_bookmark_name[11];
    sprintf( psz_bookmark_name, "bookmark%i", i_num );
    var_Create( p_intf, psz_bookmark_name,
                VLC_VAR_STRING|VLC_VAR_DOINHERIT );
    if( p_playlist->status.p_item )
832
    {
833 834 835 836 837
        config_PutPsz( p_intf, psz_bookmark_name,
                       p_playlist->status.p_item->p_input->psz_uri);
        msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num,
                  p_playlist->status.p_item->p_input->psz_uri);
        config_SaveConfigFile( p_intf, "hotkeys" );
838
    }
839
    pl_Release( p_intf );
840
}
841

842 843
static void DisplayPosition( intf_thread_t *p_intf, vout_thread_t *p_vout,
                             input_thread_t *p_input )
844 845 846
{
    char psz_duration[MSTRTIME_MAX_SIZE];
    char psz_time[MSTRTIME_MAX_SIZE];
847
    vlc_value_t time, pos;
848 849
    mtime_t i_seconds;

850 851
    if( p_vout == NULL ) return;

852
    ClearChannels( p_intf, p_vout );
853

854 855 856 857 858 859 860 861
    var_Get( p_input, "time", &time );
    i_seconds = time.i_time / 1000000;
    secstotimestr ( psz_time, i_seconds );

    var_Get( p_input, "length", &time );
    if( time.i_time > 0 )
    {
        secstotimestr( psz_duration, time.i_time / 1000000 );
Clément Stenac's avatar
Clément Stenac committed
862
        vout_OSDMessage( p_input, POSITION_TEXT_CHAN, (char *) "%s / %s",
863 864 865 866
                         psz_time, psz_duration );
    }
    else if( i_seconds > 0 )
    {
867
        vout_OSDMessage( p_input, POSITION_TEXT_CHAN, psz_time );
868
    }
869 870 871 872

    if( !p_vout->p_parent_intf || p_vout->b_fullscreen )
    {
        var_Get( p_input, "position", &pos );
873
        vout_OSDSlider( VLC_OBJECT( p_input ), POSITION_WIDGET_CHAN,
874 875 876 877
                        pos.f_float * 100, OSD_HOR_SLIDER );
    }
}

878 879
static void DisplayVolume( intf_thread_t *p_intf, vout_thread_t *p_vout,
                           audio_volume_t i_vol )
880 881 882 883 884
{
    if( p_vout == NULL )
    {
        return;
    }
885
    ClearChannels( p_intf, p_vout );
886 887 888

    if( !p_vout->p_parent_intf || p_vout->b_fullscreen )
    {
889
        vout_OSDSlider( VLC_OBJECT( p_vout ), VOLUME_WIDGET_CHAN,
890 891 892 893
            i_vol*100/AOUT_VOLUME_MAX, OSD_VERT_SLIDER );
    }
    else
    {
Clément Stenac's avatar
Clément Stenac committed
894
        vout_OSDMessage( p_vout, VOLUME_TEXT_CHAN, _( "Volume %d%%" ),
895
                         i_vol*400/AOUT_VOLUME_MAX );
896
    }
897
}
898 899 900 901 902

static void ClearChannels( intf_thread_t *p_intf, vout_thread_t *p_vout )
{
    int i;

903
    if( p_vout )
904
    {
905
        spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
906 907
        for( i = 0; i < CHANNELS_NUMBER; i++ )
        {
908 909
            spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR,
                         p_intf->p_sys->p_channels[ i ] );
910
        }
911 912
    }
}