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