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