hotkeys.c 35.7 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
        i_action = 0;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
167
        i_key = GetKey( p_intf );
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        /* Special action for mouse event */
        /* FIXME: This should probably be configurable */
        /* FIXME: rework hotkeys handling to allow more than 1 event
         * to trigger one same action */
        switch (i_key & KEY_SPECIAL)
        {
            case KEY_MOUSEWHEELUP:
                i_action = ACTIONID_VOL_UP;
                break;
            case KEY_MOUSEWHEELDOWN:
                i_action = ACTIONID_VOL_DOWN;
                break;
            case KEY_MOUSEWHEELLEFT:
                i_action = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
                break;
            case KEY_MOUSEWHEELRIGHT:
                i_action = ACTIONID_JUMP_FORWARD_EXTRASHORT;
                break;
            default: break;
        }
189 190 191

        /* No mouse action, find action triggered by hotkey */
        if(!i_action)
192 193 194 195 196 197 198 199 200 201 202 203 204
        {
            for( i = 0; i_key != -1 && p_hotkeys[i].psz_action != NULL; i++ )
            {
                if( p_hotkeys[i].i_key == i_key )
                {
                    i_action = p_hotkeys[i].i_action;
                    i_times  = p_hotkeys[i].i_times;
                    /* times key pressed within max. delta time */
                    p_hotkeys[i].i_times = 0;
                    break;
                }
            }
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
205 206

        if( !i_action )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
207
        {
208 209 210
            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
211
            /* No key pressed, sleep a bit more */
212
//            msleep( INTF_IDLE_SLEEP );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
213 214
            continue;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
215

216 217 218 219 220 221
        /* Update the input */
        PL_LOCK;
        p_input = p_playlist->p_input;
        if( p_input )
            vlc_object_yield( p_input );
        PL_UNLOCK;
222

223 224 225 226 227 228 229 230 231 232 233 234 235 236
        /* 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
237
        if( i_action == ACTIONID_QUIT )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
238
        {
239
            p_intf->p_libvlc->b_die = VLC_TRUE;
240 241
            ClearChannels( p_intf, p_vout );
            vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Quit" ) );
242 243 244 245
            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
246 247
            continue;
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
248
        else if( i_action == ACTIONID_VOL_UP )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
249 250 251
        {
            audio_volume_t i_newvol;
            aout_VolumeUp( p_intf, 1, &i_newvol );
252
            DisplayVolume( p_intf, p_vout, i_newvol );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
253
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
254
        else if( i_action == ACTIONID_VOL_DOWN )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
255 256 257
        {
            audio_volume_t i_newvol;
            aout_VolumeDown( p_intf, 1, &i_newvol );
258
            DisplayVolume( p_intf, p_vout, i_newvol );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
259
        }
Yoann Peronneau's avatar
Yoann Peronneau committed
260 261 262 263 264 265 266 267
        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 )
                {
268 269 270
                    ClearChannels( p_intf, p_vout );
                    vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
                                  OSD_MUTE_ICON );
Yoann Peronneau's avatar
Yoann Peronneau committed
271 272 273
                }
                else
                {
274
                    DisplayVolume( p_intf, p_vout, i_newvol );
Yoann Peronneau's avatar
Yoann Peronneau committed
275 276 277
                }
            }
        }
278
        else if( i_action == ACTIONID_INTF_SHOW )
279
            var_SetBool( p_playlist, "intf-show", VLC_TRUE );
280
        else if( i_action == ACTIONID_INTF_HIDE )
281
            var_SetBool( p_playlist, "intf-show", VLC_FALSE );
282 283 284 285
        else if( i_action == ACTIONID_SNAPSHOT )
        {
            if( p_vout ) vout_Control( p_vout, VOUT_SNAPSHOT );
        }
286
        else if( i_action == ACTIONID_FULLSCREEN )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
287
        {
288 289
            if( p_vout )
            {
290 291
                var_Get( p_vout, "fullscreen", &val );
                val.b_bool = !val.b_bool;
292 293 294 295
                var_Set( p_vout, "fullscreen", val );
            }
            else
            {
296 297 298
                var_Get( p_playlist, "fullscreen", &val );
                val.b_bool = !val.b_bool;
                var_Set( p_playlist, "fullscreen", val );
299
            }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
300
        }
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
        else if( i_action == ACTIONID_WALLPAPER )
        {
            if( p_vout )
            {
                var_Get( p_vout, "directx-wallpaper", &val );
                val.b_bool = !val.b_bool;
                var_Set( p_vout, "directx-wallpaper", val );
            }
            else
            {
                var_Get( p_playlist, "directx-wallpaper", &val );
                val.b_bool = !val.b_bool;
                var_Set( p_playlist, "directx-wallpaper", val );
            }
        }
Antoine Cellerier's avatar
Antoine Cellerier committed
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
        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
345
        else if( i_action == ACTIONID_PLAY_PAUSE )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
346
        {
347 348
            val.i_int = PLAYING_S;
            if( p_input )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
349
            {
350
                ClearChannels( p_intf, p_vout );
351 352 353 354 355 356 357 358 359 360 361 362 363 364

                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;
                }
365
                var_Set( p_input, "state", val );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
366 367 368
            }
            else
            {
369
                playlist_Play( p_playlist );
Gildas Bazin's avatar
 
Gildas Bazin committed
370
            }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
371 372 373
        }
        else if( p_input )
        {
Laurent Aimar's avatar
Laurent Aimar committed
374 375 376 377 378
            /* FIXME --fenrir
             * How to get a valid value ?
             * That's not that easy with some special stream
             */
            vlc_bool_t b_seekable = VLC_TRUE;
379
            int i_interval =0;
380

Gildas Bazin's avatar
 
Gildas Bazin committed
381
            if( i_action == ACTIONID_PAUSE )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
382
            {
383 384 385 386 387 388 389 390 391
                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
392
            }
393 394
            else if( i_action == ACTIONID_JUMP_BACKWARD_EXTRASHORT
                     && b_seekable )
395
            {
396
#define SET_TIME( a, b ) \
Clément Stenac's avatar
Clément Stenac committed
397
    i_interval = config_GetInt( p_input, a "-jump-size" ); \
398 399 400 401 402 403 404
    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 );
405
            }
406
            else if( i_action == ACTIONID_JUMP_FORWARD_EXTRASHORT && b_seekable )
407
            {
408
                SET_TIME( "extrashort", 1 );
409
            }
410
            else if( i_action == ACTIONID_JUMP_BACKWARD_SHORT && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
411
            {
412
                SET_TIME( "short", -1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
413
            }
414
            else if( i_action == ACTIONID_JUMP_FORWARD_SHORT && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
415
            {
416
                SET_TIME( "short", 1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
417
            }
418
            else if( i_action == ACTIONID_JUMP_BACKWARD_MEDIUM && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
419
            {
420
                SET_TIME( "medium", -1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
421
            }
422
            else if( i_action == ACTIONID_JUMP_FORWARD_MEDIUM && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
423
            {
424
                SET_TIME( "medium", 1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
425
            }
426
            else if( i_action == ACTIONID_JUMP_BACKWARD_LONG && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
427
            {
428
                SET_TIME( "long", -1 );
Gildas Bazin's avatar
 
Gildas Bazin committed
429
            }
430
            else if( i_action == ACTIONID_JUMP_FORWARD_LONG && b_seekable )
Gildas Bazin's avatar
 
Gildas Bazin committed
431
            {
432 433
                SET_TIME( "long", 1 );
#undef SET_TIME
Gildas Bazin's avatar
 
Gildas Bazin committed
434
            }
435 436 437 438 439 440 441 442
            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;
443 444 445 446
                if( i_count <= 1 )
                {
                    continue;
                }
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
                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",
466 467
                             list.p_list->p_values[1] );
                    i = 1;
468 469 470 471 472 473 474 475 476 477 478
                }
                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 );
            }
479
            else if( i_action == ACTIONID_SUBTITLE_TRACK )
480 481 482 483
            {
                vlc_value_t val, list, list2;
                int i_count, i;
                var_Get( p_input, "spu-es", &val );
484

485 486 487
                var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
                            &list, &list2 );
                i_count = list.p_list->i_count;
488 489
                if( i_count <= 1 )
                {
490 491
                    vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                     _("Subtitle track: %s"), _("N/A") );
492 493
                    continue;
                }
494 495 496 497 498 499 500
                for( i = 0; i < i_count; i++ )
                {
                    if( val.i_int == list.p_list->p_values[i].i_int )
                    {
                        break;
                    }
                }
501
                /* value of spu-es was not in choices list */
502 503
                if( i == i_count )
                {
504 505
                    msg_Warn( p_input,
                              "invalid current subtitle track, selecting 0" );
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
                    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 );
            }
523
            else if( i_action == ACTIONID_ASPECT_RATIO && p_vout )
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
            {
                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 );
            }
549
            else if( i_action == ACTIONID_CROP && p_vout )
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
            {
                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 );
            }
575
            else if( i_action == ACTIONID_DEINTERLACE && p_vout )
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
            {
                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 );
            }
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
            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 );
                }
            }
630
            else if( i_action == ACTIONID_CROP_TOP && p_vout )
631
                var_IncInteger( p_vout, "crop-top" );
632
            else if( i_action == ACTIONID_UNCROP_TOP && p_vout )
633
                var_DecInteger( p_vout, "crop-top" );
634
            else if( i_action == ACTIONID_CROP_BOTTOM && p_vout )
635
                var_IncInteger( p_vout, "crop-bottom" );
636
            else if( i_action == ACTIONID_UNCROP_BOTTOM && p_vout )
637
                 var_DecInteger( p_vout, "crop-bottom" );
638
            else if( i_action == ACTIONID_CROP_LEFT && p_vout )
639
                 var_IncInteger( p_vout, "crop-left" );
640
            else if( i_action == ACTIONID_UNCROP_LEFT && p_vout )
641
                 var_DecInteger( p_vout, "crop-left" );
642
            else if( i_action == ACTIONID_CROP_RIGHT && p_vout )
643
                 var_IncInteger( p_vout, "crop-right" );
644
            else if( i_action == ACTIONID_UNCROP_RIGHT && p_vout )
645 646
                 var_DecInteger( p_vout, "crop-right" );

Gildas Bazin's avatar
 
Gildas Bazin committed
647
            else if( i_action == ACTIONID_NEXT )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
648
            {
649 650
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN, _("Next") );
                playlist_Next( p_playlist );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
651
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
652
            else if( i_action == ACTIONID_PREV )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
653
            {
654 655 656
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                 _("Previous") );
                playlist_Prev( p_playlist );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
657
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
658
            else if( i_action == ACTIONID_STOP )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
659
            {
660
                playlist_Stop( p_playlist );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
661
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
662
            else if( i_action == ACTIONID_FASTER )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
663
            {
664
                var_SetVoid( p_input, "rate-faster" );
665 666
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                 _("Faster") );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
667
            }
668
            else if( i_action == ACTIONID_SLOWER )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
669
            {
670
                var_SetVoid( p_input, "rate-slower" );
671 672
                vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
                                 _("Slower") );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
673
            }
674
            else if( i_action == ACTIONID_POSITION && b_seekable )
675
            {
676
                DisplayPosition( p_intf, p_vout, p_input );
677 678 679 680 681 682 683 684 685 686
            }
            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 );
687
            }
688 689
            /* Only makes sense with DVD */
            else if( i_action == ACTIONID_TITLE_PREV )
690
                var_SetVoid( p_input, "prev-title" );
691
            else if( i_action == ACTIONID_TITLE_NEXT )
692
                var_SetVoid( p_input, "next-title" );
693
            else if( i_action == ACTIONID_CHAPTER_PREV )
694
                var_SetVoid( p_input, "prev-chapter" );
695
            else if( i_action == ACTIONID_CHAPTER_NEXT )
696
                var_SetVoid( p_input, "next-chapter" );
697
            else if( i_action == ACTIONID_DISC_MENU )
698 699
                var_SetInteger( p_input, "title  0", 2 );

700 701 702 703 704 705
            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
706 707
                vout_OSDMessage( p_intf, DEFAULT_CHAN,
                                 _( "Subtitle delay %i ms" ),
708 709 710 711 712 713 714 715
                                 (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
716 717
                vout_OSDMessage( p_intf, DEFAULT_CHAN,
                                _( "Subtitle delay %i ms" ),
718 719 720 721 722 723 724 725
                                 (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
726 727
                vout_OSDMessage( p_intf, DEFAULT_CHAN,
                                _( "Audio delay %i ms" ),
728 729 730 731 732 733 734 735
                                 (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
736 737
                vout_OSDMessage( p_intf, DEFAULT_CHAN,
                                _( "Audio delay %i ms" ),
738 739 740 741
                                 (int)(i_delay/1000) );
            }
            else if( i_action == ACTIONID_PLAY )
            {
742 743
                var_Get( p_input, "rate", &val );
                if( val.i_int != INPUT_RATE_DEFAULT )
744
                {
745 746 747 748 749 750 751 752 753
                    /* 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 );
754 755
                }
            }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
756
        }
757 758 759 760
        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
761
    }
762
    pl_Release( p_intf );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
763 764
}

Gildas Bazin's avatar
 
Gildas Bazin committed
765
static int GetKey( intf_thread_t *p_intf)
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
766 767 768 769 770
{
    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
771
        return -1;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
    }
    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
791
                     vlc_value_t oldval, vlc_value_t newval, void *p_data )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
792 793
{
    intf_thread_t *p_intf = (intf_thread_t *)p_data;
794 795 796 797 798
    if ( !newval.i_int )
    {
        msg_Warn( p_this, "Received invalid key event %d", newval.i_int );
        return VLC_EGENERIC;
    }
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
799 800 801
    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
802
        msg_Warn( p_intf, "event buffer full, dropping keypress" );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
803 804 805 806 807 808 809
        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
810
    }
811 812 813
    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
814 815 816 817 818
    vlc_mutex_unlock( &p_intf->p_sys->change_lock );

    return VLC_SUCCESS;
}

Gildas Bazin's avatar
 
Gildas Bazin committed
819 820 821
static int ActionKeyCB( vlc_object_t *p_this, char const *psz_var,
                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
822 823
    libvlc_int_t *p_libvlc = (libvlc_int_t *)p_this;
    struct hotkey *p_hotkeys = p_libvlc->p_hotkeys;
824
    mtime_t i_date;
Gildas Bazin's avatar
 
Gildas Bazin committed
825 826 827 828 829 830 831
    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;
832
            /* do hotkey accounting */
833 834 835 836 837 838 839
            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
840 841 842 843 844
        }
    }

    return VLC_SUCCESS;
}
845 846

static void PlayBookmark( intf_thread_t *p_intf, int i_num )
847
{
848 849
    vlc_value_t val;
    char psz_bookmark_name[11];
850
    playlist_t *p_playlist = pl_Yield( p_intf );
851

852 853 854
    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 );
855

856
    char *psz_bookmark = strdup( val.psz_string );
857 858 859
    PL_LOCK;
    FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
        if( !strcmp( psz_bookmark, p_item->p_input->psz_uri ) )
860
        {
861 862
            playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE,
                              NULL, p_item );
863
            break;
864
        }
865
    FOREACH_END();
866
    PL_UNLOCK;
867
    vlc_object_release( p_playlist );
868 869 870 871
}

static void SetBookmark( intf_thread_t *p_intf, int i_num )
{
872 873 874 875 876 877
    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 )
878
    {
879 880 881 882 883
        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" );
884
    }
885
    pl_Release( p_intf );
886
}
887

888 889
static void DisplayPosition( intf_thread_t *p_intf, vout_thread_t *p_vout,
                             input_thread_t *p_input )
890 891 892
{
    char psz_duration[MSTRTIME_MAX_SIZE];
    char psz_time[MSTRTIME_MAX_SIZE];
893
    vlc_value_t time, pos;
894 895
    mtime_t i_seconds;

896 897
    if( p_vout == NULL ) return;

898
    ClearChannels( p_intf, p_vout );
899

900 901 902 903 904 905 906 907
    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
908
        vout_OSDMessage( p_input, POSITION_TEXT_CHAN, (char *) "%s / %s",
909 910 911 912
                         psz_time, psz_duration );
    }
    else if( i_seconds > 0 )
    {
913
        vout_OSDMessage( p_input, POSITION_TEXT_CHAN, psz_time );
914
    }
915 916 917 918

    if( !p_vout->p_parent_intf || p_vout->b_fullscreen )
    {
        var_Get( p_input, "position", &pos );
919
        vout_OSDSlider( VLC_OBJECT( p_input ), POSITION_WIDGET_CHAN,
920 921 922 923
                        pos.f_float * 100, OSD_HOR_SLIDER );
    }
}

924 925
static void DisplayVolume( intf_thread_t *p_intf, vout_thread_t *p_vout,
                           audio_volume_t i_vol )
926 927 928 929 930
{
    if( p_vout == NULL )
    {
        return;
    }
931
    ClearChannels( p_intf, p_vout );
932 933 934

    if( !p_vout->p_parent_intf || p_vout->b_fullscreen )
    {
935
        vout_OSDSlider( VLC_OBJECT( p_vout ), VOLUME_WIDGET_CHAN,
936 937 938 939
            i_vol*100/AOUT_VOLUME_MAX, OSD_VERT_SLIDER );
    }
    else
    {
Clément Stenac's avatar
Clément Stenac committed
940
        vout_OSDMessage( p_vout, VOLUME_TEXT_CHAN, _( "Volume %d%%" ),
941
                         i_vol*400/AOUT_VOLUME_MAX );
942
    }
943
}
944 945 946 947 948

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

949
    if( p_vout )
950
    {
951
        spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
952 953
        for( i = 0; i < CHANNELS_NUMBER; i++ )
        {
954 955
            spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR,
                         p_intf->p_sys->p_channels[ i ] );
956
        }
957 958
    }
}