]> git.sesse.net Git - vlc/blob - modules/control/hotkeys.c
playlist: Move more members as private.
[vlc] / modules / control / hotkeys.c
1 /*****************************************************************************
2  * hotkeys.c: Hotkey handling for vlc
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8  *          Jean-Paul Saman <jpsaman #_at_# m2x.nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_interface.h>
36 #include <vlc_input.h>
37 #include <vlc_vout.h>
38 #include <vlc_aout.h>
39 #include <vlc_osd.h>
40 #include <vlc_playlist.h>
41 #include "vlc_keys.h"
42
43 #define BUFFER_SIZE 10
44
45 #define CHANNELS_NUMBER 4
46 #define VOLUME_TEXT_CHAN     p_intf->p_sys->p_channels[ 0 ]
47 #define VOLUME_WIDGET_CHAN   p_intf->p_sys->p_channels[ 1 ]
48 #define POSITION_TEXT_CHAN   p_intf->p_sys->p_channels[ 2 ]
49 #define POSITION_WIDGET_CHAN p_intf->p_sys->p_channels[ 3 ]
50 /*****************************************************************************
51  * intf_sys_t: description and status of FB interface
52  *****************************************************************************/
53 struct intf_sys_t
54 {
55     int                 p_actions[ BUFFER_SIZE ]; /* buffer that contains
56                                                    * action events */
57     int                 i_size;        /* number of events in buffer */
58     int                 p_channels[ CHANNELS_NUMBER ]; /* contains registered
59                                                         * channel IDs */
60     input_thread_t *    p_input;       /* pointer to input */
61     vout_thread_t *     p_vout;        /* pointer to vout object */
62     vlc_mutex_t         lock; /* callback lock */
63     vlc_cond_t          wait; /* callback event */
64 };
65
66 /*****************************************************************************
67  * Local prototypes
68  *****************************************************************************/
69 static int  Open    ( vlc_object_t * );
70 static void Close   ( vlc_object_t * );
71 static void Run     ( intf_thread_t * );
72 static int  GetAction( intf_thread_t *);
73 static int  ActionEvent( vlc_object_t *, char const *,
74                          vlc_value_t, vlc_value_t, void * );
75 static int  SpecialKeyEvent( vlc_object_t *, char const *,
76                              vlc_value_t, vlc_value_t, void * );
77 static void PlayBookmark( intf_thread_t *, int );
78 static void SetBookmark ( intf_thread_t *, int );
79 static void DisplayPosition( intf_thread_t *, vout_thread_t *, input_thread_t * );
80 static void DisplayVolume  ( intf_thread_t *, vout_thread_t *, audio_volume_t );
81 static void ClearChannels  ( intf_thread_t *, vout_thread_t * );
82
83 /*****************************************************************************
84  * Module descriptor
85  *****************************************************************************/
86 #define BOOKMARK1_TEXT    N_("Playlist bookmark 1")
87 #define BOOKMARK2_TEXT    N_("Playlist bookmark 2")
88 #define BOOKMARK3_TEXT    N_("Playlist bookmark 3")
89 #define BOOKMARK4_TEXT    N_("Playlist bookmark 4")
90 #define BOOKMARK5_TEXT    N_("Playlist bookmark 5")
91 #define BOOKMARK6_TEXT    N_("Playlist bookmark 6")
92 #define BOOKMARK7_TEXT    N_("Playlist bookmark 7")
93 #define BOOKMARK8_TEXT    N_("Playlist bookmark 8")
94 #define BOOKMARK9_TEXT    N_("Playlist bookmark 9")
95 #define BOOKMARK10_TEXT   N_("Playlist bookmark 10")
96 #define BOOKMARK_LONGTEXT N_("Define playlist bookmarks.")
97
98 vlc_module_begin();
99     set_shortname( N_("Hotkeys") );
100     set_description( N_("Hotkeys management interface") );
101     set_capability( "interface", 0 );
102     set_callbacks( Open, Close );
103 vlc_module_end();
104
105 /*****************************************************************************
106  * Open: initialize interface
107  *****************************************************************************/
108 static int Open( vlc_object_t *p_this )
109 {
110     intf_thread_t *p_intf = (intf_thread_t *)p_this;
111     intf_sys_t *p_sys;
112     MALLOC_ERR( p_sys, intf_sys_t );
113
114     p_intf->p_sys = p_sys;
115     p_intf->pf_run = Run;
116
117     p_sys->i_size = 0;
118     vlc_mutex_init( &p_sys->lock );
119     vlc_cond_init( &p_sys->wait );
120
121     var_AddCallback( p_intf->p_libvlc, "key-pressed", SpecialKeyEvent, p_intf );
122     var_AddCallback( p_intf->p_libvlc, "key-action", ActionEvent, p_intf );
123     return VLC_SUCCESS;
124 }
125
126 /*****************************************************************************
127  * Close: destroy interface
128  *****************************************************************************/
129 static void Close( vlc_object_t *p_this )
130 {
131     intf_thread_t *p_intf = (intf_thread_t *)p_this;
132     intf_sys_t *p_sys = p_intf->p_sys;
133
134     var_DelCallback( p_intf->p_libvlc, "key-action", ActionEvent, p_intf );
135     var_DelCallback( p_intf->p_libvlc, "key-pressed", SpecialKeyEvent, p_intf );
136
137     vlc_cond_destroy( &p_sys->wait );
138     vlc_mutex_destroy( &p_sys->lock );
139
140     /* Destroy structure */
141     free( p_intf->p_sys );
142 }
143
144 /*****************************************************************************
145  * Run: main loop
146  *****************************************************************************/
147 static void Run( intf_thread_t *p_intf )
148 {
149     vout_thread_t *p_vout = NULL;
150     vlc_value_t val;
151     int i;
152     playlist_t *p_playlist = pl_Hold( p_intf );
153     int canc = vlc_savecancel();
154
155     vlc_cleanup_push( __pl_Release, p_intf );
156
157     /* Initialize hotkey structure */
158     for( struct hotkey *p_hotkey = p_intf->p_libvlc->p_hotkeys;
159          p_hotkey->psz_action != NULL;
160          p_hotkey++ )
161     {
162         p_hotkey->i_key = config_GetInt( p_intf, p_hotkey->psz_action );
163     }
164
165     for( ;; )
166     {
167         input_thread_t *p_input;
168         vout_thread_t *p_last_vout;
169         int i_action;
170
171         vlc_restorecancel( canc );
172         i_action = GetAction( p_intf );
173
174         canc = vlc_savecancel();
175
176         /* Update the input */
177         p_input = playlist_CurrentInput( p_playlist );
178
179         /* Update the vout */
180         p_last_vout = p_vout;
181         p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
182
183         /* Register OSD channels */
184         if( p_vout && p_vout != p_last_vout )
185         {
186             for( i = 0; i < CHANNELS_NUMBER; i++ )
187             {
188                 spu_Control( p_vout->p_spu, SPU_CHANNEL_REGISTER,
189                              &p_intf->p_sys->p_channels[ i ] );
190             }
191         }
192
193         /* Quit */
194         if( i_action == ACTIONID_QUIT )
195         {
196             if( p_playlist )
197                 playlist_Stop( p_playlist );
198             vlc_object_kill( p_intf->p_libvlc );
199             vlc_object_kill( p_intf );
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, "seekable" );
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_DEINTERLACE && p_vout )
613             {
614                 vlc_value_t val={0}, val_list, text_list;
615                 var_Get( p_vout, "deinterlace", &val );
616                 if( var_Change( p_vout, "deinterlace", VLC_VAR_GETLIST,
617                                 &val_list, &text_list ) >= 0 )
618                 {
619                     int i;
620                     for( i = 0; i < val_list.p_list->i_count; i++ )
621                     {
622                         if( !strcmp( val_list.p_list->p_values[i].psz_string,
623                                      val.psz_string ) )
624                         {
625                             i++;
626                             break;
627                         }
628                     }
629                     if( i == val_list.p_list->i_count ) i = 0;
630                     var_SetString( p_vout, "deinterlace",
631                                    val_list.p_list->p_values[i].psz_string );
632                     vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
633                                      _("Deinterlace mode: %s"),
634                                      text_list.p_list->p_values[i].psz_string );
635
636                     var_Change( p_vout, "deinterlace", VLC_VAR_FREELIST, &val_list, &text_list );
637                 }
638                 free( val.psz_string );
639             }
640             else if( ( i_action == ACTIONID_ZOOM || i_action == ACTIONID_UNZOOM ) && p_vout )
641             {
642                 vlc_value_t val={0}, val_list, text_list;
643                 var_Get( p_vout, "zoom", &val );
644                 if( var_Change( p_vout, "zoom", VLC_VAR_GETLIST,
645                                 &val_list, &text_list ) >= 0 )
646                 {
647                     int i;
648                     for( i = 0; i < val_list.p_list->i_count; i++ )
649                     {
650                         if( val_list.p_list->p_values[i].f_float
651                            == val.f_float )
652                         {
653                             if( i_action == ACTIONID_ZOOM )
654                                 i++;
655                             else /* ACTIONID_UNZOOM */
656                                 i--;
657                             break;
658                         }
659                     }
660                     if( i == val_list.p_list->i_count ) i = 0;
661                     if( i == -1 ) i = val_list.p_list->i_count-1;
662                     var_SetFloat( p_vout, "zoom",
663                                   val_list.p_list->p_values[i].f_float );
664                     vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
665                                      _("Zoom mode: %s"),
666                                 text_list.p_list->p_values[i].var.psz_name );
667
668                     var_Change( p_vout, "zoom", VLC_VAR_FREELIST, &val_list, &text_list );
669                 }
670             }
671             else if( i_action == ACTIONID_CROP_TOP && p_vout )
672                 var_IncInteger( p_vout, "crop-top" );
673             else if( i_action == ACTIONID_UNCROP_TOP && p_vout )
674                 var_DecInteger( p_vout, "crop-top" );
675             else if( i_action == ACTIONID_CROP_BOTTOM && p_vout )
676                 var_IncInteger( p_vout, "crop-bottom" );
677             else if( i_action == ACTIONID_UNCROP_BOTTOM && p_vout )
678                  var_DecInteger( p_vout, "crop-bottom" );
679             else if( i_action == ACTIONID_CROP_LEFT && p_vout )
680                  var_IncInteger( p_vout, "crop-left" );
681             else if( i_action == ACTIONID_UNCROP_LEFT && p_vout )
682                  var_DecInteger( p_vout, "crop-left" );
683             else if( i_action == ACTIONID_CROP_RIGHT && p_vout )
684                  var_IncInteger( p_vout, "crop-right" );
685             else if( i_action == ACTIONID_UNCROP_RIGHT && p_vout )
686                  var_DecInteger( p_vout, "crop-right" );
687
688             else if( i_action == ACTIONID_NEXT )
689             {
690                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN, _("Next") );
691                 playlist_Next( p_playlist );
692             }
693             else if( i_action == ACTIONID_PREV )
694             {
695                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
696                                  _("Previous") );
697                 playlist_Prev( p_playlist );
698             }
699             else if( i_action == ACTIONID_STOP )
700             {
701                 playlist_Stop( p_playlist );
702             }
703             else if( i_action == ACTIONID_FASTER )
704             {
705                 var_SetVoid( p_input, "rate-faster" );
706                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
707                                  _("Faster") );
708             }
709             else if( i_action == ACTIONID_SLOWER )
710             {
711                 var_SetVoid( p_input, "rate-slower" );
712                 vout_OSDMessage( VLC_OBJECT(p_input), DEFAULT_CHAN,
713                                  _("Slower") );
714             }
715             else if( i_action == ACTIONID_POSITION && b_seekable )
716             {
717                 DisplayPosition( p_intf, p_vout, p_input );
718             }
719             else if( i_action >= ACTIONID_PLAY_BOOKMARK1 &&
720                      i_action <= ACTIONID_PLAY_BOOKMARK10 )
721             {
722                 PlayBookmark( p_intf, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
723             }
724             else if( i_action >= ACTIONID_SET_BOOKMARK1 &&
725                      i_action <= ACTIONID_SET_BOOKMARK10 )
726             {
727                 SetBookmark( p_intf, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
728             }
729             /* Only makes sense with DVD */
730             else if( i_action == ACTIONID_TITLE_PREV )
731                 var_SetVoid( p_input, "prev-title" );
732             else if( i_action == ACTIONID_TITLE_NEXT )
733                 var_SetVoid( p_input, "next-title" );
734             else if( i_action == ACTIONID_CHAPTER_PREV )
735                 var_SetVoid( p_input, "prev-chapter" );
736             else if( i_action == ACTIONID_CHAPTER_NEXT )
737                 var_SetVoid( p_input, "next-chapter" );
738             else if( i_action == ACTIONID_DISC_MENU )
739                 var_SetInteger( p_input, "title  0", 2 );
740
741             else if( i_action == ACTIONID_SUBDELAY_DOWN )
742             {
743                 int64_t i_delay = var_GetTime( p_input, "spu-delay" );
744                 i_delay -= 50000;    /* 50 ms */
745                 var_SetTime( p_input, "spu-delay", i_delay );
746                 ClearChannels( p_intf, p_vout );
747                 vout_OSDMessage( p_intf, DEFAULT_CHAN,
748                                  _( "Subtitle delay %i ms" ),
749                                  (int)(i_delay/1000) );
750             }
751             else if( i_action == ACTIONID_SUBDELAY_UP )
752             {
753                 int64_t i_delay = var_GetTime( p_input, "spu-delay" );
754                 i_delay += 50000;    /* 50 ms */
755                 var_SetTime( p_input, "spu-delay", i_delay );
756                 ClearChannels( p_intf, p_vout );
757                 vout_OSDMessage( p_intf, DEFAULT_CHAN,
758                                 _( "Subtitle delay %i ms" ),
759                                  (int)(i_delay/1000) );
760             }
761             else if( i_action == ACTIONID_AUDIODELAY_DOWN )
762             {
763                 int64_t i_delay = var_GetTime( p_input, "audio-delay" );
764                 i_delay -= 50000;    /* 50 ms */
765                 var_SetTime( p_input, "audio-delay", i_delay );
766                 ClearChannels( p_intf, p_vout );
767                 vout_OSDMessage( p_intf, DEFAULT_CHAN,
768                                 _( "Audio delay %i ms" ),
769                                  (int)(i_delay/1000) );
770             }
771             else if( i_action == ACTIONID_AUDIODELAY_UP )
772             {
773                 int64_t i_delay = var_GetTime( p_input, "audio-delay" );
774                 i_delay += 50000;    /* 50 ms */
775                 var_SetTime( p_input, "audio-delay", i_delay );
776                 ClearChannels( p_intf, p_vout );
777                 vout_OSDMessage( p_intf, DEFAULT_CHAN,
778                                 _( "Audio delay %i ms" ),
779                                  (int)(i_delay/1000) );
780             }
781             else if( i_action == ACTIONID_PLAY )
782             {
783                 var_Get( p_input, "rate", &val );
784                 if( val.i_int != INPUT_RATE_DEFAULT )
785                 {
786                     /* Return to normal speed */
787                     var_SetInteger( p_input, "rate", INPUT_RATE_DEFAULT );
788                 }
789                 else
790                 {
791                     ClearChannels( p_intf, p_vout );
792                     vout_OSDIcon( VLC_OBJECT( p_intf ), DEFAULT_CHAN,
793                                   OSD_PLAY_ICON );
794                     playlist_Play( p_playlist );
795                 }
796             }
797             else if( i_action == ACTIONID_MENU_ON )
798             {
799                 osd_MenuShow( VLC_OBJECT(p_intf) );
800             }
801             else if( i_action == ACTIONID_MENU_OFF )
802             {
803                 osd_MenuHide( VLC_OBJECT(p_intf) );
804             }
805             else if( i_action == ACTIONID_MENU_LEFT )
806             {
807                 osd_MenuPrev( VLC_OBJECT(p_intf) );
808             }
809             else if( i_action == ACTIONID_MENU_RIGHT )
810             {
811                 osd_MenuNext( VLC_OBJECT(p_intf) );
812             }
813             else if( i_action == ACTIONID_MENU_UP )
814             {
815                 osd_MenuUp( VLC_OBJECT(p_intf) );
816             }
817             else if( i_action == ACTIONID_MENU_DOWN )
818             {
819                 osd_MenuDown( VLC_OBJECT(p_intf) );
820             }
821             else if( i_action == ACTIONID_MENU_SELECT )
822             {
823                 osd_MenuActivate( VLC_OBJECT(p_intf) );
824             }
825             else if( i_action == ACTIONID_RECORD )
826             {
827                 if( var_GetBool( p_input, "can-record" ) )
828                 {
829                     const bool b_record = !var_GetBool( p_input, "record" );
830
831                     if( b_record )
832                         vout_OSDMessage( p_intf, DEFAULT_CHAN, _("Recording") );
833                     else
834                         vout_OSDMessage( p_intf, DEFAULT_CHAN, _("Recording done") );
835                     var_SetBool( p_input, "record", b_record );
836                 }
837             }
838         }
839         if( p_vout )
840             vlc_object_release( p_vout );
841         if( p_input )
842             vlc_object_release( p_input );
843     }
844
845     /* dead code */
846     abort();
847     vlc_cleanup_pop();
848 }
849
850 static int GetAction( intf_thread_t *p_intf )
851 {
852     intf_sys_t *p_sys = p_intf->p_sys;
853     int i_ret;
854
855     vlc_mutex_lock( &p_sys->lock );
856     mutex_cleanup_push( &p_sys->lock );
857
858     while( p_sys->i_size == 0 )
859         vlc_cond_wait( &p_sys->wait, &p_sys->lock );
860
861     i_ret = p_sys->p_actions[ 0 ];
862     p_sys->i_size--;
863     for( int i = 0; i < p_sys->i_size; i++ )
864         p_sys->p_actions[i] = p_sys->p_actions[i + 1];
865
866     vlc_cleanup_run();
867     return i_ret;
868 }
869
870 static int PutAction( intf_thread_t *p_intf, int i_action )
871 {
872     intf_sys_t *p_sys = p_intf->p_sys;
873     int i_ret = VLC_EGENERIC;
874
875     vlc_mutex_lock( &p_sys->lock );
876     if ( p_sys->i_size >= BUFFER_SIZE )
877         msg_Warn( p_intf, "event buffer full, dropping key actions" );
878     else
879         p_sys->p_actions[p_sys->i_size++] = i_action;
880
881     vlc_cond_signal( &p_sys->wait );
882     vlc_mutex_unlock( &p_sys->lock );
883     return i_ret;
884 }
885
886 /*****************************************************************************
887  * SpecialKeyEvent: callback for mouse events
888  *****************************************************************************/
889 static int SpecialKeyEvent( vlc_object_t *libvlc, char const *psz_var,
890                             vlc_value_t oldval, vlc_value_t newval,
891                             void *p_data )
892 {
893     intf_thread_t *p_intf = (intf_thread_t *)p_data;
894     int i_action;
895
896     (void)libvlc;
897     (void)psz_var;
898     (void)oldval;
899
900     /* Special action for mouse event */
901     /* FIXME: This should probably be configurable */
902     /* FIXME: rework hotkeys handling to allow more than 1 event
903      * to trigger one same action */
904     switch (newval.i_int & KEY_SPECIAL)
905     {
906         case KEY_MOUSEWHEELUP:
907             i_action = ACTIONID_VOL_UP;
908             break;
909         case KEY_MOUSEWHEELDOWN:
910             i_action = ACTIONID_VOL_DOWN;
911             break;
912         case KEY_MOUSEWHEELLEFT:
913             i_action = ACTIONID_JUMP_BACKWARD_EXTRASHORT;
914             break;
915         case KEY_MOUSEWHEELRIGHT:
916             i_action = ACTIONID_JUMP_FORWARD_EXTRASHORT;
917             break;
918         default:
919           return VLC_SUCCESS;
920     }
921
922     if( i_action )
923         return PutAction( p_intf, i_action );
924     return VLC_SUCCESS;
925 }
926
927 /*****************************************************************************
928  * ActionEvent: callback for hotkey actions
929  *****************************************************************************/
930 static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
931                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
932 {
933     intf_thread_t *p_intf = (intf_thread_t *)p_data;
934
935     (void)libvlc;
936     (void)psz_var;
937     (void)oldval;
938
939     return PutAction( p_intf, newval.i_int );
940 }
941
942 static void PlayBookmark( intf_thread_t *p_intf, int i_num )
943 {
944     vlc_value_t val;
945     char psz_bookmark_name[11];
946     playlist_t *p_playlist = pl_Hold( p_intf );
947
948     sprintf( psz_bookmark_name, "bookmark%i", i_num );
949     var_Create( p_intf, psz_bookmark_name, VLC_VAR_STRING|VLC_VAR_DOINHERIT );
950     var_Get( p_intf, psz_bookmark_name, &val );
951
952     char *psz_bookmark = strdup( val.psz_string );
953     PL_LOCK;
954     FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
955         char *psz_uri = input_item_GetURI( p_item->p_input );
956         if( !strcmp( psz_bookmark, psz_uri ) )
957         {
958             free( psz_uri );
959             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked,
960                               NULL, p_item );
961             break;
962         }
963         else
964             free( psz_uri );
965     FOREACH_END();
966     PL_UNLOCK;
967     vlc_object_release( p_playlist );
968 }
969
970 static void SetBookmark( intf_thread_t *p_intf, int i_num )
971 {
972     playlist_t *p_playlist = pl_Hold( p_intf );
973     char psz_bookmark_name[11];
974     sprintf( psz_bookmark_name, "bookmark%i", i_num );
975     var_Create( p_intf, psz_bookmark_name,
976                 VLC_VAR_STRING|VLC_VAR_DOINHERIT );
977     playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
978     if( p_item )
979     {
980         char *psz_uri = input_item_GetURI( p_item->p_input );
981         config_PutPsz( p_intf, psz_bookmark_name, psz_uri);
982         msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num, psz_uri);
983         free( psz_uri );
984         config_SaveConfigFile( p_intf, "hotkeys" );
985     }
986     pl_Release( p_intf );
987 }
988
989 static void DisplayPosition( intf_thread_t *p_intf, vout_thread_t *p_vout,
990                              input_thread_t *p_input )
991 {
992     char psz_duration[MSTRTIME_MAX_SIZE];
993     char psz_time[MSTRTIME_MAX_SIZE];
994     vlc_value_t time, pos;
995     mtime_t i_seconds;
996
997     if( p_vout == NULL ) return;
998
999     ClearChannels( p_intf, p_vout );
1000
1001     var_Get( p_input, "time", &time );
1002     i_seconds = time.i_time / 1000000;
1003     secstotimestr ( psz_time, i_seconds );
1004
1005     var_Get( p_input, "length", &time );
1006     if( time.i_time > 0 )
1007     {
1008         secstotimestr( psz_duration, time.i_time / 1000000 );
1009         vout_OSDMessage( p_input, POSITION_TEXT_CHAN, (char *) "%s / %s",
1010                          psz_time, psz_duration );
1011     }
1012     else if( i_seconds > 0 )
1013     {
1014         vout_OSDMessage( p_input, POSITION_TEXT_CHAN, psz_time );
1015     }
1016
1017     if( !p_vout->p_window || p_vout->b_fullscreen )
1018     {
1019         var_Get( p_input, "position", &pos );
1020         vout_OSDSlider( VLC_OBJECT( p_input ), POSITION_WIDGET_CHAN,
1021                         pos.f_float * 100, OSD_HOR_SLIDER );
1022     }
1023 }
1024
1025 static void DisplayVolume( intf_thread_t *p_intf, vout_thread_t *p_vout,
1026                            audio_volume_t i_vol )
1027 {
1028     if( p_vout == NULL )
1029     {
1030         return;
1031     }
1032     ClearChannels( p_intf, p_vout );
1033
1034     if( !p_vout->p_window || p_vout->b_fullscreen )
1035     {
1036         vout_OSDSlider( VLC_OBJECT( p_vout ), VOLUME_WIDGET_CHAN,
1037             i_vol*100/AOUT_VOLUME_MAX, OSD_VERT_SLIDER );
1038     }
1039     else
1040     {
1041         vout_OSDMessage( p_vout, VOLUME_TEXT_CHAN, _( "Volume %d%%" ),
1042                          i_vol*400/AOUT_VOLUME_MAX );
1043     }
1044 }
1045
1046 static void ClearChannels( intf_thread_t *p_intf, vout_thread_t *p_vout )
1047 {
1048     int i;
1049
1050     if( p_vout )
1051     {
1052         spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
1053         for( i = 0; i < CHANNELS_NUMBER; i++ )
1054         {
1055             spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR,
1056                          p_intf->p_sys->p_channels[ i ] );
1057         }
1058     }
1059 }