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