]> git.sesse.net Git - vlc/blob - modules/control/hotkeys.c
ActionKeyCB: remove completely.
[vlc] / modules / control / hotkeys.c
1 /*****************************************************************************
2  * hotkeys.c: Hotkey handling for vlc
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Jean-Paul Saman <jpsaman #_at_# m2x.nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc/vlc.h>
34 #include <vlc_interface.h>
35 #include <vlc_input.h>
36 #include <vlc_vout.h>
37 #include <vlc_aout.h>
38 #include <vlc_osd.h>
39 #include <vlc_playlist.h>
40 #include "vlc_keys.h"
41
42 #define BUFFER_SIZE 10
43
44 #define CHANNELS_NUMBER 4
45 #define VOLUME_TEXT_CHAN     p_intf->p_sys->p_channels[ 0 ]
46 #define VOLUME_WIDGET_CHAN   p_intf->p_sys->p_channels[ 1 ]
47 #define POSITION_TEXT_CHAN   p_intf->p_sys->p_channels[ 2 ]
48 #define POSITION_WIDGET_CHAN p_intf->p_sys->p_channels[ 3 ]
49 /*****************************************************************************
50  * intf_sys_t: description and status of FB interface
51  *****************************************************************************/
52 struct intf_sys_t
53 {
54     vlc_mutex_t         change_lock;  /* mutex to keep the callback
55                                        * and the main loop from
56                                        * stepping on each others
57                                        * toes */
58     int                 p_keys[ BUFFER_SIZE ]; /* buffer that contains
59                                                 * keyevents */
60     int                 i_size;        /* number of events in buffer */
61     int                 p_channels[ CHANNELS_NUMBER ]; /* contains registered
62                                                         * channel IDs */
63     input_thread_t *    p_input;       /* pointer to input */
64     vout_thread_t *     p_vout;        /* pointer to vout object */
65 };
66
67 /*****************************************************************************
68  * Local prototypes
69  *****************************************************************************/
70 static int  Open    ( vlc_object_t * );
71 static void Close   ( vlc_object_t * );
72 static void Run     ( intf_thread_t * );
73 static int  GetKey  ( intf_thread_t *);
74 static int  KeyEvent( vlc_object_t *, char const *,
75                       vlc_value_t, vlc_value_t, void * );
76 static void PlayBookmark( intf_thread_t *, int );
77 static void SetBookmark ( intf_thread_t *, int );
78 static void DisplayPosition( intf_thread_t *, vout_thread_t *, input_thread_t * );
79 static void DisplayVolume  ( intf_thread_t *, vout_thread_t *, audio_volume_t );
80 static void ClearChannels  ( intf_thread_t *, vout_thread_t * );
81
82 /*****************************************************************************
83  * Module descriptor
84  *****************************************************************************/
85 #define BOOKMARK1_TEXT    N_("Playlist bookmark 1")
86 #define BOOKMARK2_TEXT    N_("Playlist bookmark 2")
87 #define BOOKMARK3_TEXT    N_("Playlist bookmark 3")
88 #define BOOKMARK4_TEXT    N_("Playlist bookmark 4")
89 #define BOOKMARK5_TEXT    N_("Playlist bookmark 5")
90 #define BOOKMARK6_TEXT    N_("Playlist bookmark 6")
91 #define BOOKMARK7_TEXT    N_("Playlist bookmark 7")
92 #define BOOKMARK8_TEXT    N_("Playlist bookmark 8")
93 #define BOOKMARK9_TEXT    N_("Playlist bookmark 9")
94 #define BOOKMARK10_TEXT   N_("Playlist bookmark 10")
95 #define BOOKMARK_LONGTEXT N_("Define playlist bookmarks.")
96
97 vlc_module_begin();
98     set_shortname( _("Hotkeys") );
99     set_description( _("Hotkeys management interface") );
100     set_capability( "interface", 0 );
101     set_callbacks( Open, Close );
102 vlc_module_end();
103
104 /*****************************************************************************
105  * Open: initialize interface
106  *****************************************************************************/
107 static int Open( vlc_object_t *p_this )
108 {
109     intf_thread_t *p_intf = (intf_thread_t *)p_this;
110     MALLOC_ERR( p_intf->p_sys, intf_sys_t );
111
112     vlc_mutex_init( p_intf, &p_intf->p_sys->change_lock );
113     p_intf->p_sys->i_size = 0;
114     p_intf->pf_run = Run;
115
116     var_AddCallback( p_intf->p_libvlc, "key-pressed", KeyEvent, p_intf );
117     return VLC_SUCCESS;
118 }
119
120 /*****************************************************************************
121  * Close: destroy interface
122  *****************************************************************************/
123 static void Close( vlc_object_t *p_this )
124 {
125     intf_thread_t *p_intf = (intf_thread_t *)p_this;
126
127     var_DelCallback( p_intf->p_libvlc, "key-pressed", KeyEvent, p_intf );
128
129     vlc_mutex_destroy( &p_intf->p_sys->change_lock );
130     /* Destroy structure */
131     free( p_intf->p_sys );
132 }
133
134 /*****************************************************************************
135  * Run: main loop
136  *****************************************************************************/
137 static void Run( intf_thread_t *p_intf )
138 {
139     input_thread_t *p_input = NULL;
140     vout_thread_t *p_vout = NULL;
141     vout_thread_t *p_last_vout = NULL;
142     struct hotkey *p_hotkeys = p_intf->p_libvlc->p_hotkeys;
143     vlc_value_t val;
144     int i;
145     playlist_t *p_playlist = pl_Yield( p_intf );
146
147     /* Initialize hotkey structure */
148     for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
149         p_hotkeys[i].i_key = config_GetInt( p_intf, p_hotkeys[i].psz_action );
150
151     for( vlc_bool_t b_quit = VLC_FALSE ; !b_quit; )
152     {
153         int i_key, i_action;
154         int i_times = 0;
155
156         /* Sleep a bit */
157         /* msleep( INTF_IDLE_SLEEP ); */
158
159         i_action = 0;
160         i_key = GetKey( p_intf );
161
162         /* Special action for mouse event */
163         /* FIXME: This should probably be configurable */
164         /* FIXME: rework hotkeys handling to allow more than 1 event
165          * to trigger one same action */
166         switch (i_key & KEY_SPECIAL)
167         {
168             case KEY_MOUSEWHEELUP:
169                 i_action = ACTIONID_VOL_UP;
170                 break;
171             case KEY_MOUSEWHEELDOWN:
172                 i_action = ACTIONID_VOL_DOWN;
173                 break;
174             case KEY_MOUSEWHEELLEFT:
175                 i_action = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
176                 break;
177             case KEY_MOUSEWHEELRIGHT:
178                 i_action = ACTIONID_JUMP_FORWARD_EXTRASHORT;
179                 break;
180             default: break;
181         }
182
183         /* No mouse action, find action triggered by hotkey */
184         if(!i_action)
185         {
186             for( i = 0; i_key != -1 && p_hotkeys[i].psz_action != NULL; i++ )
187             {
188                 if( p_hotkeys[i].i_key == i_key )
189                 {
190                     i_action = p_hotkeys[i].i_action;
191                     i_times  = p_hotkeys[i].i_times;
192                     /* times key pressed within max. delta time */
193                     p_hotkeys[i].i_times = 0;
194                     break;
195                 }
196             }
197         }
198
199         if( !i_action )
200         {
201             b_quit = vlc_object_lock_and_wait( p_intf );
202             /* No key pressed, sleep a bit more */
203             continue;
204         }
205
206         /* Update the input */
207         PL_LOCK;
208         p_input = p_playlist->p_input;
209         if( p_input )
210             vlc_object_yield( p_input );
211         PL_UNLOCK;
212
213         /* Update the vout */
214         p_last_vout = p_vout;
215         p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
216
217         /* Register OSD channels */
218         if( p_vout && p_vout != p_last_vout )
219         {
220             for( i = 0; i < CHANNELS_NUMBER; i++ )
221             {
222                 spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER,
223                              &p_intf->p_sys->p_channels[ i ] );
224             }
225         }
226
227         /* Quit */
228         if( i_action == ACTIONID_QUIT )
229         {
230             if( p_playlist )
231                 playlist_Stop( p_playlist );
232             vlc_object_kill( p_intf->p_libvlc );
233             vlc_object_kill( p_intf );
234             ClearChannels( p_intf, p_vout );
235             vout_OSDMessage( p_intf, DEFAULT_CHAN, _( "Quit" ) );
236             if( p_vout )
237                 vlc_object_release( p_vout );
238             if( p_input )
239                 vlc_object_release( p_input );
240             continue;
241         }
242         /* Volume and audio actions */
243         else if( i_action == ACTIONID_VOL_UP )
244         {
245             audio_volume_t i_newvol;
246             aout_VolumeUp( p_intf, 1, &i_newvol );
247             DisplayVolume( p_intf, p_vout, i_newvol );
248         }
249         else if( i_action == ACTIONID_VOL_DOWN )
250         {
251             audio_volume_t i_newvol;
252             aout_VolumeDown( p_intf, 1, &i_newvol );
253             DisplayVolume( p_intf, p_vout, i_newvol );
254         }
255         else if( i_action == ACTIONID_VOL_MUTE )
256         {
257             audio_volume_t i_newvol = -1;
258             aout_VolumeMute( p_intf, &i_newvol );
259             if( p_vout )
260             {
261                 if( i_newvol == 0 )
262                 {
263                     ClearChannels( p_intf, p_vout );
264                     vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
265                                   OSD_MUTE_ICON );
266                 }
267                 else
268                 {
269                     DisplayVolume( p_intf, p_vout, i_newvol );
270                 }
271             }
272         }
273         /* Interface showing */
274         else if( i_action == ACTIONID_INTF_SHOW )
275             var_SetBool( p_playlist, "intf-show", VLC_TRUE );
276         else if( i_action == ACTIONID_INTF_HIDE )
277             var_SetBool( p_playlist, "intf-show", VLC_FALSE );
278         /* Video Output actions */
279         else if( i_action == ACTIONID_SNAPSHOT )
280         {
281             if( p_vout ) vout_Control( p_vout, VOUT_SNAPSHOT );
282         }
283         else if( i_action == ACTIONID_TOGGLE_FULLSCREEN )
284         {
285             if( p_vout )
286             {
287                 var_Get( p_vout, "fullscreen", &val );
288                 val.b_bool = !val.b_bool;
289                 var_Set( p_vout, "fullscreen", val );
290             }
291             else
292             {
293                 var_Get( p_playlist, "fullscreen", &val );
294                 val.b_bool = !val.b_bool;
295                 var_Set( p_playlist, "fullscreen", val );
296             }
297         }
298         else if( i_action == ACTIONID_LEAVE_FULLSCREEN )
299         {
300             if( p_vout && var_GetBool( p_vout, "fullscreen" ) )
301             {
302                 var_SetBool( p_vout, "fullscreen", VLC_FALSE );
303             }
304         }
305         else if( i_action == ACTIONID_ZOOM_QUARTER ||
306                  i_action == ACTIONID_ZOOM_HALF ||
307                  i_action == ACTIONID_ZOOM_ORIGINAL ||
308                  i_action == ACTIONID_ZOOM_DOUBLE )
309         {
310             if( p_vout )
311             {
312                 if( i_action == ACTIONID_ZOOM_QUARTER )
313                     val.f_float = 0.25;
314                 if( i_action == ACTIONID_ZOOM_HALF )
315                     val.f_float = 0.5;
316                 if( i_action == ACTIONID_ZOOM_ORIGINAL )
317                     val.f_float = 1;
318                 if( i_action == ACTIONID_ZOOM_DOUBLE )
319                     val.f_float = 2;
320                 var_Set( p_vout, "zoom", val );
321             }
322         }
323         else if( i_action == ACTIONID_WALLPAPER )
324         {
325             if( p_vout )
326             {
327                 var_Get( p_vout, "directx-wallpaper", &val );
328                 val.b_bool = !val.b_bool;
329                 var_Set( p_vout, "directx-wallpaper", val );
330             }
331             else
332             {
333                 var_Get( p_playlist, "directx-wallpaper", &val );
334                 val.b_bool = !val.b_bool;
335                 var_Set( p_playlist, "directx-wallpaper", val );
336             }
337         }
338         /* Playlist actions */
339         else if( i_action == ACTIONID_LOOP )
340         {
341             /* Toggle Normal -> Loop -> Repeat -> Normal ... */
342             vlc_value_t val2;
343             var_Get( p_playlist, "loop", &val );
344             var_Get( p_playlist, "repeat", &val2 );
345             if( val2.b_bool == VLC_TRUE )
346             {
347                 val.b_bool = VLC_FALSE;
348                 val2.b_bool = VLC_FALSE;
349             }
350             else if( val.b_bool == VLC_TRUE )
351             {
352                 val.b_bool = VLC_FALSE;
353                 val2.b_bool = VLC_TRUE;
354             }
355             else
356             {
357                 val.b_bool = VLC_TRUE;
358             }
359             var_Set( p_playlist, "loop", val );
360             var_Set( p_playlist, "repeat", val2 );
361         }
362         else if( i_action == ACTIONID_RANDOM )
363         {
364             var_Get( p_playlist, "random", &val );
365             val.b_bool = !val.b_bool;
366             var_Set( p_playlist, "random", val );
367         }
368         else if( i_action == ACTIONID_PLAY_PAUSE )
369         {
370             val.i_int = PLAYING_S;
371             if( p_input )
372             {
373                 ClearChannels( p_intf, p_vout );
374
375                 var_Get( p_input, "state", &val );
376                 if( val.i_int != PAUSE_S )
377                 {
378                     vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
379                                   OSD_PAUSE_ICON );
380                     val.i_int = PAUSE_S;
381                 }
382                 else
383                 {
384                     vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
385                                   OSD_PLAY_ICON );
386                     val.i_int = PLAYING_S;
387                 }
388                 var_Set( p_input, "state", val );
389             }
390             else
391             {
392                 playlist_Play( p_playlist );
393             }
394         }
395         else if( i_action == ACTIONID_AUDIODEVICE_CYCLE )
396         {
397             vlc_value_t val, list, list2;
398             int i_count, i;
399
400             aout_instance_t *p_aout =
401                 vlc_object_find( p_intf, VLC_OBJECT_AOUT, FIND_ANYWHERE );
402             var_Get( p_aout, "audio-device", &val );
403             var_Change( p_aout, "audio-device", VLC_VAR_GETCHOICES,
404                     &list, &list2 );
405             i_count = list.p_list->i_count;
406
407             /* Not enough device to switch between */
408             if( i_count <= 1 )
409                 continue;
410
411             for( i = 0; i < i_count; i++ )
412             {
413                 if( val.i_int == list.p_list->p_values[i].i_int )
414                 {
415                     break;
416                 }
417             }
418             if( i == i_count )
419             {
420                 msg_Warn( p_aout,
421                         "invalid current audio device, selecting 0" );
422                 var_Set( p_aout, "audio-device",
423                         list.p_list->p_values[0] );
424                 i = 0;
425             }
426             else if( i == i_count -1 )
427             {
428                 var_Set( p_aout, "audio-device",
429                         list.p_list->p_values[0] );
430                 i = 0;
431             }
432             else
433             {
434                 var_Set( p_aout, "audio-device",
435                         list.p_list->p_values[i+1] );
436                 i++;
437             }
438             vout_OSDMessage( p_intf, DEFAULT_CHAN,
439                     _("Audio Device: %s"),
440                     list2.p_list->p_values[i].psz_string);
441             vlc_object_release( p_aout );
442         }
443         /* Input options */
444         else if( p_input )
445         {
446             /* FIXME --fenrir
447              * How to get a valid value ?
448              * That's not that easy with some special stream
449              */
450             vlc_bool_t b_seekable = VLC_TRUE;
451             int i_interval =0;
452
453             if( i_action == ACTIONID_PAUSE )
454             {
455                 var_Get( p_input, "state", &val );
456                 if( val.i_int != PAUSE_S )
457                 {
458                     ClearChannels( p_intf, p_vout );
459                     vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
460                                   OSD_PAUSE_ICON );
461                     val.i_int = PAUSE_S;
462                     var_Set( p_input, "state", val );
463                 }
464             }
465             else if( i_action == ACTIONID_JUMP_BACKWARD_EXTRASHORT
466                      && b_seekable )
467             {
468 #define SET_TIME( a, b ) \
469     i_interval = config_GetInt( p_input, a "-jump-size" ); \
470     if( i_interval > 0 ) { \
471         val.i_time = ( (mtime_t)(i_interval * b) * 1000000L \
472                        * ((mtime_t)(1 << i_times))); \
473         var_Set( p_input, "time-offset", val ); \
474         DisplayPosition( p_intf, p_vout, p_input ); \
475     }
476                 SET_TIME( "extrashort", -1 );
477             }
478             else if( i_action == ACTIONID_JUMP_FORWARD_EXTRASHORT && b_seekable )
479             {
480                 SET_TIME( "extrashort", 1 );
481             }
482             else if( i_action == ACTIONID_JUMP_BACKWARD_SHORT && b_seekable )
483             {
484                 SET_TIME( "short", -1 );
485             }
486             else if( i_action == ACTIONID_JUMP_FORWARD_SHORT && b_seekable )
487             {
488                 SET_TIME( "short", 1 );
489             }
490             else if( i_action == ACTIONID_JUMP_BACKWARD_MEDIUM && b_seekable )
491             {
492                 SET_TIME( "medium", -1 );
493             }
494             else if( i_action == ACTIONID_JUMP_FORWARD_MEDIUM && b_seekable )
495             {
496                 SET_TIME( "medium", 1 );
497             }
498             else if( i_action == ACTIONID_JUMP_BACKWARD_LONG && b_seekable )
499             {
500                 SET_TIME( "long", -1 );
501             }
502             else if( i_action == ACTIONID_JUMP_FORWARD_LONG && b_seekable )
503             {
504                 SET_TIME( "long", 1 );
505 #undef SET_TIME
506             }
507             else if( i_action == ACTIONID_AUDIO_TRACK )
508             {
509                 vlc_value_t val, list, list2;
510                 int i_count, i;
511                 var_Get( p_input, "audio-es", &val );
512                 var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
513                             &list, &list2 );
514                 i_count = list.p_list->i_count;
515                 if( i_count <= 1 )
516                 {
517                     continue;
518                 }
519                 for( i = 0; i < i_count; i++ )
520                 {
521                     if( val.i_int == list.p_list->p_values[i].i_int )
522                     {
523                         break;
524                     }
525                 }
526                 /* value of audio-es was not in choices list */
527                 if( i == i_count )
528                 {
529                     msg_Warn( p_input,
530                               "invalid current audio track, selecting 0" );
531                     var_Set( p_input, "audio-es",
532                              list.p_list->p_values[0] );
533                     i = 0;
534                 }
535                 else if( i == i_count - 1 )
536                 {
537                     var_Set( p_input, "audio-es",
538                              list.p_list->p_values[1] );
539                     i = 1;
540                 }
541                 else
542                 {
543                     var_Set( p_input, "audio-es",
544                              list.p_list->p_values[i+1] );
545                     i++;
546                 }
547                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
548                                  _("Audio track: %s"),
549                                  list2.p_list->p_values[i].psz_string );
550             }
551             else if( i_action == ACTIONID_SUBTITLE_TRACK )
552             {
553                 vlc_value_t val, list, list2;
554                 int i_count, i;
555                 var_Get( p_input, "spu-es", &val );
556
557                 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
558                             &list, &list2 );
559                 i_count = list.p_list->i_count;
560                 if( i_count <= 1 )
561                 {
562                     vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
563                                      _("Subtitle track: %s"), _("N/A") );
564                     continue;
565                 }
566                 for( i = 0; i < i_count; i++ )
567                 {
568                     if( val.i_int == list.p_list->p_values[i].i_int )
569                     {
570                         break;
571                     }
572                 }
573                 /* value of spu-es was not in choices list */
574                 if( i == i_count )
575                 {
576                     msg_Warn( p_input,
577                               "invalid current subtitle track, selecting 0" );
578                     var_Set( p_input, "spu-es", list.p_list->p_values[0] );
579                     i = 0;
580                 }
581                 else if( i == i_count - 1 )
582                 {
583                     var_Set( p_input, "spu-es", list.p_list->p_values[0] );
584                     i = 0;
585                 }
586                 else
587                 {
588                     var_Set( p_input, "spu-es", list.p_list->p_values[i+1] );
589                     i = i + 1;
590                 }
591                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
592                                  _("Subtitle track: %s"),
593                                  list2.p_list->p_values[i].psz_string );
594             }
595             else if( i_action == ACTIONID_ASPECT_RATIO && p_vout )
596             {
597                 vlc_value_t val={0}, val_list, text_list;
598                 var_Get( p_vout, "aspect-ratio", &val );
599                 if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETLIST,
600                                 &val_list, &text_list ) >= 0 )
601                 {
602                     int i;
603                     for( i = 0; i < val_list.p_list->i_count; i++ )
604                     {
605                         if( !strcmp( val_list.p_list->p_values[i].psz_string,
606                                      val.psz_string ) )
607                         {
608                             i++;
609                             break;
610                         }
611                     }
612                     if( i == val_list.p_list->i_count ) i = 0;
613                     var_SetString( p_vout, "aspect-ratio",
614                                    val_list.p_list->p_values[i].psz_string );
615                     vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
616                                      _("Aspect ratio: %s"),
617                                      text_list.p_list->p_values[i].psz_string );
618                 }
619                 free( val.psz_string );
620             }
621             else if( i_action == ACTIONID_CROP && p_vout )
622             {
623                 vlc_value_t val={0}, val_list, text_list;
624                 var_Get( p_vout, "crop", &val );
625                 if( var_Change( p_vout, "crop", VLC_VAR_GETLIST,
626                                 &val_list, &text_list ) >= 0 )
627                 {
628                     int i;
629                     for( i = 0; i < val_list.p_list->i_count; i++ )
630                     {
631                         if( !strcmp( val_list.p_list->p_values[i].psz_string,
632                                      val.psz_string ) )
633                         {
634                             i++;
635                             break;
636                         }
637                     }
638                     if( i == val_list.p_list->i_count ) i = 0;
639                     var_SetString( p_vout, "crop",
640                                    val_list.p_list->p_values[i].psz_string );
641                     vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
642                                      _("Crop: %s"),
643                                      text_list.p_list->p_values[i].psz_string );
644                 }
645                 free( val.psz_string );
646             }
647             else if( i_action == ACTIONID_DEINTERLACE && p_vout )
648             {
649                 vlc_value_t val={0}, val_list, text_list;
650                 var_Get( p_vout, "deinterlace", &val );
651                 if( var_Change( p_vout, "deinterlace", VLC_VAR_GETLIST,
652                                 &val_list, &text_list ) >= 0 )
653                 {
654                     int i;
655                     for( i = 0; i < val_list.p_list->i_count; i++ )
656                     {
657                         if( !strcmp( val_list.p_list->p_values[i].psz_string,
658                                      val.psz_string ) )
659                         {
660                             i++;
661                             break;
662                         }
663                     }
664                     if( i == val_list.p_list->i_count ) i = 0;
665                     var_SetString( p_vout, "deinterlace",
666                                    val_list.p_list->p_values[i].psz_string );
667                     vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
668                                      _("Deinterlace mode: %s"),
669                                      text_list.p_list->p_values[i].psz_string );
670                 }
671                 free( val.psz_string );
672             }
673             else if( ( i_action == ACTIONID_ZOOM || i_action == ACTIONID_UNZOOM ) && p_vout )
674             {
675                 vlc_value_t val={0}, val_list, text_list;
676                 var_Get( p_vout, "zoom", &val );
677                 if( var_Change( p_vout, "zoom", VLC_VAR_GETLIST,
678                                 &val_list, &text_list ) >= 0 )
679                 {
680                     int i;
681                     for( i = 0; i < val_list.p_list->i_count; i++ )
682                     {
683                         if( val_list.p_list->p_values[i].f_float
684                            == val.f_float )
685                         {
686                             if( i_action == ACTIONID_ZOOM )
687                                 i++;
688                             else /* ACTIONID_UNZOOM */
689                                 i--;
690                             break;
691                         }
692                     }
693                     if( i == val_list.p_list->i_count ) i = 0;
694                     if( i == -1 ) i = val_list.p_list->i_count-1;
695                     var_SetFloat( p_vout, "zoom",
696                                   val_list.p_list->p_values[i].f_float );
697                     vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
698                                      _("Zoom mode: %s"),
699                                 text_list.p_list->p_values[i].var.psz_name );
700                 }
701             }
702             else if( i_action == ACTIONID_CROP_TOP && p_vout )
703                 var_IncInteger( p_vout, "crop-top" );
704             else if( i_action == ACTIONID_UNCROP_TOP && p_vout )
705                 var_DecInteger( p_vout, "crop-top" );
706             else if( i_action == ACTIONID_CROP_BOTTOM && p_vout )
707                 var_IncInteger( p_vout, "crop-bottom" );
708             else if( i_action == ACTIONID_UNCROP_BOTTOM && p_vout )
709                  var_DecInteger( p_vout, "crop-bottom" );
710             else if( i_action == ACTIONID_CROP_LEFT && p_vout )
711                  var_IncInteger( p_vout, "crop-left" );
712             else if( i_action == ACTIONID_UNCROP_LEFT && p_vout )
713                  var_DecInteger( p_vout, "crop-left" );
714             else if( i_action == ACTIONID_CROP_RIGHT && p_vout )
715                  var_IncInteger( p_vout, "crop-right" );
716             else if( i_action == ACTIONID_UNCROP_RIGHT && p_vout )
717                  var_DecInteger( p_vout, "crop-right" );
718
719             else if( i_action == ACTIONID_NEXT )
720             {
721                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN, _("Next") );
722                 playlist_Next( p_playlist );
723             }
724             else if( i_action == ACTIONID_PREV )
725             {
726                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
727                                  _("Previous") );
728                 playlist_Prev( p_playlist );
729             }
730             else if( i_action == ACTIONID_STOP )
731             {
732                 playlist_Stop( p_playlist );
733             }
734             else if( i_action == ACTIONID_FASTER )
735             {
736                 var_SetVoid( p_input, "rate-faster" );
737                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
738                                  _("Faster") );
739             }
740             else if( i_action == ACTIONID_SLOWER )
741             {
742                 var_SetVoid( p_input, "rate-slower" );
743                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
744                                  _("Slower") );
745             }
746             else if( i_action == ACTIONID_POSITION && b_seekable )
747             {
748                 DisplayPosition( p_intf, p_vout, p_input );
749             }
750             else if( i_action >= ACTIONID_PLAY_BOOKMARK1 &&
751                      i_action <= ACTIONID_PLAY_BOOKMARK10 )
752             {
753                 PlayBookmark( p_intf, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
754             }
755             else if( i_action >= ACTIONID_SET_BOOKMARK1 &&
756                      i_action <= ACTIONID_SET_BOOKMARK10 )
757             {
758                 SetBookmark( p_intf, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
759             }
760             /* Only makes sense with DVD */
761             else if( i_action == ACTIONID_TITLE_PREV )
762                 var_SetVoid( p_input, "prev-title" );
763             else if( i_action == ACTIONID_TITLE_NEXT )
764                 var_SetVoid( p_input, "next-title" );
765             else if( i_action == ACTIONID_CHAPTER_PREV )
766                 var_SetVoid( p_input, "prev-chapter" );
767             else if( i_action == ACTIONID_CHAPTER_NEXT )
768                 var_SetVoid( p_input, "next-chapter" );
769             else if( i_action == ACTIONID_DISC_MENU )
770                 var_SetInteger( p_input, "title  0", 2 );
771
772             else if( i_action == ACTIONID_SUBDELAY_DOWN )
773             {
774                 int64_t i_delay = var_GetTime( p_input, "spu-delay" );
775                 i_delay -= 50000;    /* 50 ms */
776                 var_SetTime( p_input, "spu-delay", i_delay );
777                 ClearChannels( p_intf, p_vout );
778                 vout_OSDMessage( p_intf, DEFAULT_CHAN,
779                                  _( "Subtitle delay %i ms" ),
780                                  (int)(i_delay/1000) );
781             }
782             else if( i_action == ACTIONID_SUBDELAY_UP )
783             {
784                 int64_t i_delay = var_GetTime( p_input, "spu-delay" );
785                 i_delay += 50000;    /* 50 ms */
786                 var_SetTime( p_input, "spu-delay", i_delay );
787                 ClearChannels( p_intf, p_vout );
788                 vout_OSDMessage( p_intf, DEFAULT_CHAN,
789                                 _( "Subtitle delay %i ms" ),
790                                  (int)(i_delay/1000) );
791             }
792             else if( i_action == ACTIONID_AUDIODELAY_DOWN )
793             {
794                 int64_t i_delay = var_GetTime( p_input, "audio-delay" );
795                 i_delay -= 50000;    /* 50 ms */
796                 var_SetTime( p_input, "audio-delay", i_delay );
797                 ClearChannels( p_intf, p_vout );
798                 vout_OSDMessage( p_intf, DEFAULT_CHAN,
799                                 _( "Audio delay %i ms" ),
800                                  (int)(i_delay/1000) );
801             }
802             else if( i_action == ACTIONID_AUDIODELAY_UP )
803             {
804                 int64_t i_delay = var_GetTime( p_input, "audio-delay" );
805                 i_delay += 50000;    /* 50 ms */
806                 var_SetTime( p_input, "audio-delay", i_delay );
807                 ClearChannels( p_intf, p_vout );
808                 vout_OSDMessage( p_intf, DEFAULT_CHAN,
809                                 _( "Audio delay %i ms" ),
810                                  (int)(i_delay/1000) );
811             }
812             else if( i_action == ACTIONID_PLAY )
813             {
814                 var_Get( p_input, "rate", &val );
815                 if( val.i_int != INPUT_RATE_DEFAULT )
816                 {
817                     /* Return to normal speed */
818                     var_SetInteger( p_input, "rate", INPUT_RATE_DEFAULT );
819                 }
820                 else
821                 {
822                     ClearChannels( p_intf, p_vout );
823                     vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
824                                   OSD_PLAY_ICON );
825                     playlist_Play( p_playlist );
826                 }
827             }
828             else if( i_action == ACTIONID_MENU_ON )
829             {
830                 osd_MenuShow( VLC_OBJECT(p_intf) );
831             }
832             else if( i_action == ACTIONID_MENU_OFF )
833             {
834                 osd_MenuHide( VLC_OBJECT(p_intf) );
835             }
836             else if( i_action == ACTIONID_MENU_LEFT )
837             {
838                 osd_MenuPrev( VLC_OBJECT(p_intf) );
839             }
840             else if( i_action == ACTIONID_MENU_RIGHT )
841             {
842                 osd_MenuNext( VLC_OBJECT(p_intf) );
843             }
844             else if( i_action == ACTIONID_MENU_UP )
845             {
846                 osd_MenuUp( VLC_OBJECT(p_intf) );
847             }
848             else if( i_action == ACTIONID_MENU_DOWN )
849             {
850                 osd_MenuDown( VLC_OBJECT(p_intf) );
851             }
852             else if( i_action == ACTIONID_MENU_SELECT )
853             {
854                 osd_MenuActivate( VLC_OBJECT(p_intf) );
855             }
856         }
857         if( p_vout )
858             vlc_object_release( p_vout );
859         if( p_input )
860             vlc_object_release( p_input );
861     }
862     pl_Release( p_intf );
863 }
864
865 static int GetKey( intf_thread_t *p_intf)
866 {
867     vlc_mutex_lock( &p_intf->p_sys->change_lock );
868     if ( p_intf->p_sys->i_size == 0 )
869     {
870         vlc_mutex_unlock( &p_intf->p_sys->change_lock );
871         return -1;
872     }
873     else
874     {
875         int i_return = p_intf->p_sys->p_keys[ 0 ];
876         int i;
877         p_intf->p_sys->i_size--;
878         for ( i = 0; i < BUFFER_SIZE - 1; i++)
879         {
880             p_intf->p_sys->p_keys[ i ] = p_intf->p_sys->p_keys[ i + 1 ];
881         }
882         vlc_mutex_unlock( &p_intf->p_sys->change_lock );
883         return i_return;
884     }
885 }
886
887 /*****************************************************************************
888  * KeyEvent: callback for keyboard events
889  *****************************************************************************/
890 static int KeyEvent( vlc_object_t *p_this, char const *psz_var,
891                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
892 {
893     VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
894     intf_thread_t *p_intf = (intf_thread_t *)p_data;
895     if ( !newval.i_int )
896     {
897         msg_Warn( p_this, "Received invalid key event %d", newval.i_int );
898         return VLC_EGENERIC;
899     }
900     vlc_mutex_lock( &p_intf->p_sys->change_lock );
901     if ( p_intf->p_sys->i_size == BUFFER_SIZE )
902     {
903         msg_Warn( p_intf, "event buffer full, dropping keypress" );
904         vlc_mutex_unlock( &p_intf->p_sys->change_lock );
905         return VLC_EGENERIC;
906     }
907     else
908     {
909         p_intf->p_sys->p_keys[ p_intf->p_sys->i_size ] = newval.i_int;
910         p_intf->p_sys->i_size++;
911     }
912     vlc_mutex_lock( &p_intf->object_lock );
913     vlc_cond_signal( &p_intf->object_wait );
914     vlc_mutex_unlock( &p_intf->object_lock );
915     vlc_mutex_unlock( &p_intf->p_sys->change_lock );
916
917     return VLC_SUCCESS;
918 }
919
920 static void PlayBookmark( intf_thread_t *p_intf, int i_num )
921 {
922     vlc_value_t val;
923     char psz_bookmark_name[11];
924     playlist_t *p_playlist = pl_Yield( p_intf );
925
926     sprintf( psz_bookmark_name, "bookmark%i", i_num );
927     var_Create( p_intf, psz_bookmark_name, VLC_VAR_STRING|VLC_VAR_DOINHERIT );
928     var_Get( p_intf, psz_bookmark_name, &val );
929
930     char *psz_bookmark = strdup( val.psz_string );
931     PL_LOCK;
932     FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
933         char *psz_uri = input_item_GetURI( p_item->p_input );
934         if( !strcmp( psz_bookmark, psz_uri ) )
935         {
936             free( psz_uri );
937             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, VLC_TRUE,
938                               NULL, p_item );
939             break;
940         }
941         else
942             free( psz_uri );
943     FOREACH_END();
944     PL_UNLOCK;
945     vlc_object_release( p_playlist );
946 }
947
948 static void SetBookmark( intf_thread_t *p_intf, int i_num )
949 {
950     playlist_t *p_playlist = pl_Yield( p_intf );
951     char psz_bookmark_name[11];
952     sprintf( psz_bookmark_name, "bookmark%i", i_num );
953     var_Create( p_intf, psz_bookmark_name,
954                 VLC_VAR_STRING|VLC_VAR_DOINHERIT );
955     if( p_playlist->status.p_item )
956     {
957         char *psz_uri = input_item_GetURI( p_playlist->status.p_item->p_input );
958         config_PutPsz( p_intf, psz_bookmark_name, psz_uri);
959         msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num, psz_uri);
960         free( psz_uri );
961         config_SaveConfigFile( p_intf, "hotkeys" );
962     }
963     pl_Release( p_intf );
964 }
965
966 static void DisplayPosition( intf_thread_t *p_intf, vout_thread_t *p_vout,
967                              input_thread_t *p_input )
968 {
969     char psz_duration[MSTRTIME_MAX_SIZE];
970     char psz_time[MSTRTIME_MAX_SIZE];
971     vlc_value_t time, pos;
972     mtime_t i_seconds;
973
974     if( p_vout == NULL ) return;
975
976     ClearChannels( p_intf, p_vout );
977
978     var_Get( p_input, "time", &time );
979     i_seconds = time.i_time / 1000000;
980     secstotimestr ( psz_time, i_seconds );
981
982     var_Get( p_input, "length", &time );
983     if( time.i_time > 0 )
984     {
985         secstotimestr( psz_duration, time.i_time / 1000000 );
986         vout_OSDMessage( p_input, POSITION_TEXT_CHAN, (char *) "%s / %s",
987                          psz_time, psz_duration );
988     }
989     else if( i_seconds > 0 )
990     {
991         vout_OSDMessage( p_input, POSITION_TEXT_CHAN, psz_time );
992     }
993
994     if( !p_vout->p_parent_intf || p_vout->b_fullscreen )
995     {
996         var_Get( p_input, "position", &pos );
997         vout_OSDSlider( VLC_OBJECT( p_input ), POSITION_WIDGET_CHAN,
998                         pos.f_float * 100, OSD_HOR_SLIDER );
999     }
1000 }
1001
1002 static void DisplayVolume( intf_thread_t *p_intf, vout_thread_t *p_vout,
1003                            audio_volume_t i_vol )
1004 {
1005     if( p_vout == NULL )
1006     {
1007         return;
1008     }
1009     ClearChannels( p_intf, p_vout );
1010
1011     if( !p_vout->p_parent_intf || p_vout->b_fullscreen )
1012     {
1013         vout_OSDSlider( VLC_OBJECT( p_vout ), VOLUME_WIDGET_CHAN,
1014             i_vol*100/AOUT_VOLUME_MAX, OSD_VERT_SLIDER );
1015     }
1016     else
1017     {
1018         vout_OSDMessage( p_vout, VOLUME_TEXT_CHAN, _( "Volume %d%%" ),
1019                          i_vol*400/AOUT_VOLUME_MAX );
1020     }
1021 }
1022
1023 static void ClearChannels( intf_thread_t *p_intf, vout_thread_t *p_vout )
1024 {
1025     int i;
1026
1027     if( p_vout )
1028     {
1029         spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
1030         for( i = 0; i < CHANNELS_NUMBER; i++ )
1031         {
1032             spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR,
1033                          p_intf->p_sys->p_channels[ i ] );
1034         }
1035     }
1036 }