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