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