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