hotkeys: handle 360° zoom with mouse wheel
[vlc.git] / modules / control / hotkeys.c
blob59bfa82d32bc27ee91c8b85308c9bfbec10b6682
1 /*****************************************************************************
2 * hotkeys.c: Hotkey handling for vlc
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
5 * $Id$
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Jean-Paul Saman <jpsaman #_at_# m2x.nl>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_interface.h>
37 #include <vlc_input.h>
38 #include <vlc_aout.h>
39 #include <vlc_vout.h>
40 #include <vlc_vout_osd.h>
41 #include <vlc_playlist.h>
42 #include <vlc_keys.h>
43 #include "math.h"
45 #include <assert.h>
47 /*****************************************************************************
48 * intf_sys_t: description and status of FB interface
49 *****************************************************************************/
50 struct intf_sys_t
52 vlc_mutex_t lock;
53 vout_thread_t *p_vout;
54 input_thread_t *p_input;
55 int slider_chan;
57 /*subtitle_delaybookmarks: placeholder for storing subtitle sync timestamps*/
58 struct
60 int64_t i_time_subtitle;
61 int64_t i_time_audio;
62 } subtitle_delaybookmarks;
64 struct
66 bool b_can_change;
67 bool b_button_pressed;
68 int x, y;
69 } vrnav;
72 /*****************************************************************************
73 * Local prototypes
74 *****************************************************************************/
75 static int Open ( vlc_object_t * );
76 static void Close ( vlc_object_t * );
77 static int ActionEvent( vlc_object_t *, char const *,
78 vlc_value_t, vlc_value_t, void * );
79 static void PlayBookmark( intf_thread_t *, int );
80 static void SetBookmark ( intf_thread_t *, int );
81 static void DisplayPosition( vout_thread_t *, int, input_thread_t * );
82 static void DisplayVolume( vout_thread_t *, int, float );
83 static void DisplayRate ( vout_thread_t *, float );
84 static float AdjustRateFine( vlc_object_t *, const int );
85 static void ClearChannels ( vout_thread_t *, int );
87 #define DisplayMessage(vout, ...) \
88 do { \
89 if (vout) \
90 vout_OSDMessage(vout, SPU_DEFAULT_CHANNEL, __VA_ARGS__); \
91 } while(0)
92 #define DisplayIcon(vout, icon) \
93 do { if(vout) vout_OSDIcon(vout, SPU_DEFAULT_CHANNEL, icon); } while(0)
95 /*****************************************************************************
96 * Module descriptor
97 *****************************************************************************/
99 vlc_module_begin ()
100 set_shortname( N_("Hotkeys") )
101 set_description( N_("Hotkeys management interface") )
102 set_capability( "interface", 0 )
103 set_callbacks( Open, Close )
104 set_category( CAT_INTERFACE )
105 set_subcategory( SUBCAT_INTERFACE_HOTKEYS )
107 vlc_module_end ()
109 static int MovedEvent( vlc_object_t *p_this, char const *psz_var,
110 vlc_value_t oldval, vlc_value_t newval, void *p_data )
112 intf_thread_t *p_intf = (intf_thread_t *)p_data;
113 intf_sys_t *p_sys = p_intf->p_sys;
115 (void) p_this; (void) psz_var; (void) oldval;
117 if( p_sys->vrnav.b_button_pressed )
119 #define RAD(d) ((float) ((d) * M_PI / 180.f))
120 int i_horizontal = newval.coords.x - p_sys->vrnav.x;
121 int i_vertical = newval.coords.y - p_sys->vrnav.y;
123 vlc_viewpoint_t viewpoint = {
124 .yaw = RAD(-i_horizontal * 2.5f),
125 .pitch = RAD(-i_vertical * 2.5f),
128 input_UpdateViewpoint( p_sys->p_input, &viewpoint, false );
130 p_sys->vrnav.x = newval.coords.x;
131 p_sys->vrnav.y = newval.coords.y;
132 #undef RAD
135 return VLC_SUCCESS;
138 static int ButtonEvent( vlc_object_t *p_this, char const *psz_var,
139 vlc_value_t oldval, vlc_value_t newval, void *p_data )
141 intf_thread_t *p_intf = p_data;
142 intf_sys_t *p_sys = p_intf->p_sys;
144 (void) psz_var; (void) oldval;
146 if( newval.i_int & 0x01 )
148 if( !p_sys->vrnav.b_button_pressed )
150 p_sys->vrnav.b_button_pressed = true;
151 var_GetCoords( p_this, "mouse-moved",
152 &p_sys->vrnav.x, &p_sys->vrnav.y );
155 else
156 p_sys->vrnav.b_button_pressed = false;
158 return VLC_SUCCESS;
161 static void ChangeVout( intf_thread_t *p_intf, vout_thread_t *p_vout )
163 intf_sys_t *p_sys = p_intf->p_sys;
165 int slider_chan;
166 bool b_vrnav_can_change;
167 if( p_vout != NULL )
169 slider_chan = vout_RegisterSubpictureChannel( p_vout );
170 b_vrnav_can_change = var_GetBool( p_vout, "viewpoint-changeable" );
173 vlc_mutex_lock( &p_sys->lock );
174 vout_thread_t *p_old_vout = p_sys->p_vout;
175 bool b_vrnav_could_change = p_sys->vrnav.b_can_change;
176 p_sys->p_vout = p_vout;
177 if( p_vout != NULL )
179 p_sys->slider_chan = slider_chan;
180 p_sys->vrnav.b_can_change = b_vrnav_can_change;
182 else
183 p_sys->vrnav.b_can_change = false;
184 vlc_mutex_unlock( &p_sys->lock );
186 if( p_old_vout != NULL )
188 if( b_vrnav_could_change )
190 var_DelCallback( p_old_vout, "mouse-moved", MovedEvent,
191 p_intf );
192 var_DelCallback( p_old_vout, "mouse-button-down", ButtonEvent,
193 p_intf );
195 vlc_object_release( p_old_vout );
198 if( p_sys->vrnav.b_can_change )
200 assert( p_sys->p_vout != NULL );
201 var_AddCallback( p_sys->p_vout, "mouse-moved", MovedEvent,
202 p_intf );
203 var_AddCallback( p_sys->p_vout, "mouse-button-down", ButtonEvent,
204 p_intf );
208 static int InputEvent( vlc_object_t *p_this, char const *psz_var,
209 vlc_value_t oldval, vlc_value_t val, void *p_data )
211 input_thread_t *p_input = (input_thread_t *)p_this;
212 intf_thread_t *p_intf = p_data;
214 (void) psz_var; (void) oldval;
216 if( val.i_int == INPUT_EVENT_VOUT )
217 ChangeVout( p_intf, input_GetVout( p_input ) );
219 return VLC_SUCCESS;
222 static void ChangeInput( intf_thread_t *p_intf, input_thread_t *p_input )
224 intf_sys_t *p_sys = p_intf->p_sys;
226 input_thread_t *p_old_input = p_sys->p_input;
227 vout_thread_t *p_old_vout = NULL;
228 if( p_old_input != NULL )
230 /* First, remove callbacks from previous input. It's safe to access it
231 * unlocked, since it's written from this thread */
232 var_DelCallback( p_old_input, "intf-event", InputEvent, p_intf );
234 p_old_vout = p_sys->p_vout;
235 /* Remove mouse events before setting new input, since callbacks may
236 * access it */
237 if( p_old_vout != NULL && p_sys->vrnav.b_can_change )
239 var_DelCallback( p_old_vout, "mouse-moved", MovedEvent,
240 p_intf );
241 var_DelCallback( p_old_vout, "mouse-button-down", ButtonEvent,
242 p_intf );
246 /* Replace input and vout locked */
247 vlc_mutex_lock( &p_sys->lock );
248 p_sys->p_input = p_input ? vlc_object_hold( p_input ) : NULL;
249 p_sys->p_vout = NULL;
250 p_sys->vrnav.b_can_change = false;
251 vlc_mutex_unlock( &p_sys->lock );
253 /* Release old input and vout objects unlocked */
254 if( p_old_input != NULL )
256 if( p_old_vout != NULL )
257 vlc_object_release( p_old_vout );
258 vlc_object_release( p_old_input );
261 /* Register input events */
262 if( p_input != NULL )
263 var_AddCallback( p_input, "intf-event", InputEvent, p_intf );
266 static int PlaylistEvent( vlc_object_t *p_this, char const *psz_var,
267 vlc_value_t oldval, vlc_value_t val, void *p_data )
269 intf_thread_t *p_intf = p_data;
271 (void) p_this; (void) psz_var; (void) oldval;
273 ChangeInput( p_intf, val.p_address );
275 return VLC_SUCCESS;
278 /*****************************************************************************
279 * Open: initialize interface
280 *****************************************************************************/
281 static int Open( vlc_object_t *p_this )
283 intf_thread_t *p_intf = (intf_thread_t *)p_this;
284 intf_sys_t *p_sys;
285 p_sys = malloc( sizeof( intf_sys_t ) );
286 if( !p_sys )
287 return VLC_ENOMEM;
289 p_intf->p_sys = p_sys;
291 p_sys->p_vout = NULL;
292 p_sys->p_input = NULL;
293 p_sys->vrnav.b_can_change = false;
294 p_sys->vrnav.b_button_pressed = false;
295 p_sys->subtitle_delaybookmarks.i_time_audio = 0;
296 p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
298 vlc_mutex_init( &p_sys->lock );
300 var_AddCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
302 var_AddCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
304 return VLC_SUCCESS;
307 /*****************************************************************************
308 * Close: destroy interface
309 *****************************************************************************/
310 static void Close( vlc_object_t *p_this )
312 intf_thread_t *p_intf = (intf_thread_t *)p_this;
313 intf_sys_t *p_sys = p_intf->p_sys;
315 var_DelCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
317 var_DelCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
319 ChangeInput( p_intf, NULL );
321 vlc_mutex_destroy( &p_sys->lock );
323 /* Destroy structure */
324 free( p_sys );
327 static int PutAction( intf_thread_t *p_intf, input_thread_t *p_input,
328 vout_thread_t *p_vout, int slider_chan, bool b_vrnav,
329 int i_action )
331 #define DO_ACTION(x) PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav, x)
332 intf_sys_t *p_sys = p_intf->p_sys;
333 playlist_t *p_playlist = pl_Get( p_intf );
335 /* Quit */
336 switch( i_action )
338 /* Libvlc / interface actions */
339 case ACTIONID_QUIT:
340 libvlc_Quit( p_intf->obj.libvlc );
342 ClearChannels( p_vout, slider_chan );
343 DisplayMessage( p_vout, _( "Quit" ) );
344 break;
346 case ACTIONID_INTF_TOGGLE_FSC:
347 case ACTIONID_INTF_HIDE:
348 var_TriggerCallback( p_intf->obj.libvlc, "intf-toggle-fscontrol" );
349 break;
350 case ACTIONID_INTF_BOSS:
351 var_TriggerCallback( p_intf->obj.libvlc, "intf-boss" );
352 break;
353 case ACTIONID_INTF_POPUP_MENU:
354 var_TriggerCallback( p_intf->obj.libvlc, "intf-popupmenu" );
355 break;
357 /* Playlist actions (including audio) */
358 case ACTIONID_LOOP:
360 /* Toggle Normal -> Loop -> Repeat -> Normal ... */
361 const char *mode;
362 if( var_GetBool( p_playlist, "repeat" ) )
364 var_SetBool( p_playlist, "repeat", false );
365 mode = N_("Off");
367 else
368 if( var_GetBool( p_playlist, "loop" ) )
369 { /* FIXME: this is not atomic, we should use a real tristate */
370 var_SetBool( p_playlist, "loop", false );
371 var_SetBool( p_playlist, "repeat", true );
372 mode = N_("One");
374 else
376 var_SetBool( p_playlist, "loop", true );
377 mode = N_("All");
379 DisplayMessage( p_vout, _("Loop: %s"), vlc_gettext(mode) );
380 break;
383 case ACTIONID_RANDOM:
385 const bool state = var_ToggleBool( p_playlist, "random" );
386 DisplayMessage( p_vout, _("Random: %s"),
387 vlc_gettext( state ? N_("On") : N_("Off") ) );
388 break;
391 case ACTIONID_NEXT:
392 DisplayMessage( p_vout, _("Next") );
393 playlist_Next( p_playlist );
394 break;
395 case ACTIONID_PREV:
396 DisplayMessage( p_vout, _("Previous") );
397 playlist_Prev( p_playlist );
398 break;
400 case ACTIONID_STOP:
401 playlist_Stop( p_playlist );
402 break;
404 case ACTIONID_RATE_NORMAL:
405 var_SetFloat( p_playlist, "rate", 1.f );
406 DisplayRate( p_vout, 1.f );
407 break;
408 case ACTIONID_FASTER:
409 var_TriggerCallback( p_playlist, "rate-faster" );
410 DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
411 break;
412 case ACTIONID_SLOWER:
413 var_TriggerCallback( p_playlist, "rate-slower" );
414 DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
415 break;
416 case ACTIONID_RATE_FASTER_FINE:
417 case ACTIONID_RATE_SLOWER_FINE:
419 const int i_dir = i_action == ACTIONID_RATE_FASTER_FINE ? 1 : -1;
420 float rate = AdjustRateFine( VLC_OBJECT(p_playlist), i_dir );
422 var_SetFloat( p_playlist, "rate", rate );
423 DisplayRate( p_vout, rate );
424 break;
427 case ACTIONID_PLAY_BOOKMARK1:
428 case ACTIONID_PLAY_BOOKMARK2:
429 case ACTIONID_PLAY_BOOKMARK3:
430 case ACTIONID_PLAY_BOOKMARK4:
431 case ACTIONID_PLAY_BOOKMARK5:
432 case ACTIONID_PLAY_BOOKMARK6:
433 case ACTIONID_PLAY_BOOKMARK7:
434 case ACTIONID_PLAY_BOOKMARK8:
435 case ACTIONID_PLAY_BOOKMARK9:
436 case ACTIONID_PLAY_BOOKMARK10:
437 PlayBookmark( p_intf, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
438 break;
440 case ACTIONID_SET_BOOKMARK1:
441 case ACTIONID_SET_BOOKMARK2:
442 case ACTIONID_SET_BOOKMARK3:
443 case ACTIONID_SET_BOOKMARK4:
444 case ACTIONID_SET_BOOKMARK5:
445 case ACTIONID_SET_BOOKMARK6:
446 case ACTIONID_SET_BOOKMARK7:
447 case ACTIONID_SET_BOOKMARK8:
448 case ACTIONID_SET_BOOKMARK9:
449 case ACTIONID_SET_BOOKMARK10:
450 SetBookmark( p_intf, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
451 break;
452 case ACTIONID_PLAY_CLEAR:
454 playlist_t *p_playlist = pl_Get( p_intf );
455 playlist_Clear( p_playlist, pl_Unlocked );
456 break;
458 case ACTIONID_VOL_UP:
460 float vol;
461 if( playlist_VolumeUp( p_playlist, 1, &vol ) == 0 )
462 DisplayVolume( p_vout, slider_chan, vol );
463 break;
465 case ACTIONID_VOL_DOWN:
467 float vol;
468 if( playlist_VolumeDown( p_playlist, 1, &vol ) == 0 )
469 DisplayVolume( p_vout, slider_chan, vol );
470 break;
472 case ACTIONID_VOL_MUTE:
474 int mute = playlist_MuteGet( p_playlist );
475 if( mute < 0 )
476 break;
477 mute = !mute;
478 if( playlist_MuteSet( p_playlist, mute ) )
479 break;
481 float vol = playlist_VolumeGet( p_playlist );
482 if( mute || vol == 0.f )
484 ClearChannels( p_vout, slider_chan );
485 DisplayIcon( p_vout, OSD_MUTE_ICON );
487 else
488 DisplayVolume( p_vout, slider_chan, vol );
489 break;
492 case ACTIONID_AUDIODEVICE_CYCLE:
494 audio_output_t *p_aout = playlist_GetAout( p_playlist );
495 if( p_aout == NULL )
496 break;
498 char **ids, **names;
499 int n = aout_DevicesList( p_aout, &ids, &names );
500 if( n == -1 )
501 break;
503 char *dev = aout_DeviceGet( p_aout );
504 const char *devstr = (dev != NULL) ? dev : "";
506 int idx = 0;
507 for( int i = 0; i < n; i++ )
509 if( !strcmp(devstr, ids[i]) )
510 idx = (i + 1) % n;
512 free( dev );
514 if( !aout_DeviceSet( p_aout, ids[idx] ) )
515 DisplayMessage( p_vout, _("Audio Device: %s"), names[idx] );
516 vlc_object_release( p_aout );
518 for( int i = 0; i < n; i++ )
520 free( ids[i] );
521 free( names[i] );
523 free( ids );
524 free( names );
525 break;
528 /* Playlist + input actions */
529 case ACTIONID_PLAY_PAUSE:
530 if( p_input )
532 ClearChannels( p_vout, slider_chan );
534 int state = var_GetInteger( p_input, "state" );
535 DisplayIcon( p_vout, state != PAUSE_S ? OSD_PAUSE_ICON : OSD_PLAY_ICON );
537 playlist_TogglePause( p_playlist );
538 break;
540 case ACTIONID_PLAY:
541 if( p_input && var_GetFloat( p_input, "rate" ) != 1.f )
542 /* Return to normal speed */
543 var_SetFloat( p_input, "rate", 1.f );
544 else
546 ClearChannels( p_vout, slider_chan );
547 DisplayIcon( p_vout, OSD_PLAY_ICON );
548 playlist_Play( p_playlist );
550 break;
552 /* Playlist + video output actions */
553 case ACTIONID_WALLPAPER:
555 bool wp = var_ToggleBool( p_playlist, "video-wallpaper" );
556 if( p_vout )
557 var_SetBool( p_vout, "video-wallpaper", wp );
558 break;
561 /* Input actions */
562 case ACTIONID_PAUSE:
563 if( p_input && var_GetInteger( p_input, "state" ) != PAUSE_S )
565 ClearChannels( p_vout, slider_chan );
566 DisplayIcon( p_vout, OSD_PAUSE_ICON );
567 var_SetInteger( p_input, "state", PAUSE_S );
569 break;
571 case ACTIONID_RECORD:
572 if( p_input && var_GetBool( p_input, "can-record" ) )
574 const bool on = var_ToggleBool( p_input, "record" );
575 DisplayMessage( p_vout, vlc_gettext(on
576 ? N_("Recording") : N_("Recording done")) );
578 break;
580 case ACTIONID_FRAME_NEXT:
581 if( p_input )
583 var_TriggerCallback( p_input, "frame-next" );
584 DisplayMessage( p_vout, _("Next frame") );
586 break;
588 case ACTIONID_SUBSYNC_MARKAUDIO:
590 p_sys->subtitle_delaybookmarks.i_time_audio = mdate();
591 DisplayMessage( p_vout, _("Sub sync: bookmarked audio time"));
592 break;
594 case ACTIONID_SUBSYNC_MARKSUB:
595 if( p_input )
597 vlc_value_t val, list, list2;
598 int i_count;
599 var_Get( p_input, "spu-es", &val );
601 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
602 &list, &list2 );
603 i_count = list.p_list->i_count;
604 if( i_count < 1 || val.i_int < 0 )
606 DisplayMessage( p_vout, _("No active subtitle") );
607 var_FreeList( &list, &list2 );
608 break;
610 p_sys->subtitle_delaybookmarks.i_time_subtitle = mdate();
611 DisplayMessage( p_vout,
612 _("Sub sync: bookmarked subtitle time"));
613 var_FreeList( &list, &list2 );
615 break;
616 case ACTIONID_SUBSYNC_APPLY:
618 /* Warning! Can yield a pause in the playback.
619 * For example, the following succession of actions will yield a 5 second delay :
620 * - Pressing Shift-H (ACTIONID_SUBSYNC_MARKAUDIO)
621 * - wait 5 second
622 * - Press Shift-J (ACTIONID_SUBSYNC_MARKSUB)
623 * - Press Shift-K (ACTIONID_SUBSYNC_APPLY)
624 * --> 5 seconds pause
625 * This is due to var_SetTime() (and ultimately UpdatePtsDelay())
626 * which causes the video to pause for an equivalent duration
627 * (This problem is also present in the "Track synchronization" window) */
628 if ( p_input )
630 if ( (p_sys->subtitle_delaybookmarks.i_time_audio == 0) || (p_sys->subtitle_delaybookmarks.i_time_subtitle == 0) )
632 DisplayMessage( p_vout, _( "Sub sync: set bookmarks first!" ) );
634 else
636 int64_t i_current_subdelay = var_GetInteger( p_input, "spu-delay" );
637 int64_t i_additional_subdelay = p_sys->subtitle_delaybookmarks.i_time_audio - p_sys->subtitle_delaybookmarks.i_time_subtitle;
638 int64_t i_total_subdelay = i_current_subdelay + i_additional_subdelay;
639 var_SetInteger( p_input, "spu-delay", i_total_subdelay);
640 ClearChannels( p_vout, slider_chan );
641 DisplayMessage( p_vout, _( "Sub sync: corrected %i ms (total delay = %i ms)" ),
642 (int)(i_additional_subdelay / 1000),
643 (int)(i_total_subdelay / 1000) );
644 p_sys->subtitle_delaybookmarks.i_time_audio = 0;
645 p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
648 break;
650 case ACTIONID_SUBSYNC_RESET:
652 var_SetInteger( p_input, "spu-delay", 0);
653 ClearChannels( p_vout, slider_chan );
654 DisplayMessage( p_vout, _( "Sub sync: delay reset" ) );
655 p_sys->subtitle_delaybookmarks.i_time_audio = 0;
656 p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
657 break;
660 case ACTIONID_SUBDELAY_DOWN:
661 case ACTIONID_SUBDELAY_UP:
663 int diff = (i_action == ACTIONID_SUBDELAY_UP) ? 50000 : -50000;
664 if( p_input )
666 vlc_value_t val, list, list2;
667 int i_count;
668 var_Get( p_input, "spu-es", &val );
670 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
671 &list, &list2 );
672 i_count = list.p_list->i_count;
673 if( i_count < 1 || val.i_int < 0 )
675 DisplayMessage( p_vout, _("No active subtitle") );
676 var_FreeList( &list, &list2 );
677 break;
679 int64_t i_delay = var_GetInteger( p_input, "spu-delay" ) + diff;
681 var_SetInteger( p_input, "spu-delay", i_delay );
682 ClearChannels( p_vout, slider_chan );
683 DisplayMessage( p_vout, _( "Subtitle delay %i ms" ),
684 (int)(i_delay/1000) );
685 var_FreeList( &list, &list2 );
687 break;
689 case ACTIONID_AUDIODELAY_DOWN:
690 case ACTIONID_AUDIODELAY_UP:
692 int diff = (i_action == ACTIONID_AUDIODELAY_UP) ? 50000 : -50000;
693 if( p_input )
695 int64_t i_delay = var_GetInteger( p_input, "audio-delay" )
696 + diff;
698 var_SetInteger( p_input, "audio-delay", i_delay );
699 ClearChannels( p_vout, slider_chan );
700 DisplayMessage( p_vout, _( "Audio delay %i ms" ),
701 (int)(i_delay/1000) );
703 break;
706 case ACTIONID_AUDIO_TRACK:
707 if( p_input )
709 vlc_value_t val, list, list2;
710 int i_count, i;
711 var_Get( p_input, "audio-es", &val );
712 var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
713 &list, &list2 );
714 i_count = list.p_list->i_count;
715 if( i_count > 1 )
717 for( i = 0; i < i_count; i++ )
719 if( val.i_int == list.p_list->p_values[i].i_int )
721 break;
724 /* value of audio-es was not in choices list */
725 if( i == i_count )
727 msg_Warn( p_input,
728 "invalid current audio track, selecting 0" );
729 i = 0;
731 else if( i == i_count - 1 )
732 i = 1;
733 else
734 i++;
735 var_Set( p_input, "audio-es", list.p_list->p_values[i] );
736 DisplayMessage( p_vout, _("Audio track: %s"),
737 list2.p_list->p_values[i].psz_string );
739 var_FreeList( &list, &list2 );
741 break;
742 case ACTIONID_SUBTITLE_TRACK:
743 if( p_input )
745 vlc_value_t val, list, list2;
746 int i_count, i;
747 var_Get( p_input, "spu-es", &val );
749 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
750 &list, &list2 );
751 i_count = list.p_list->i_count;
752 if( i_count <= 1 )
754 DisplayMessage( p_vout, _("Subtitle track: %s"),
755 _("N/A") );
756 var_FreeList( &list, &list2 );
757 break;
759 for( i = 0; i < i_count; i++ )
761 if( val.i_int == list.p_list->p_values[i].i_int )
763 break;
766 /* value of spu-es was not in choices list */
767 if( i == i_count )
769 msg_Warn( p_input,
770 "invalid current subtitle track, selecting 0" );
771 i = 0;
773 else if( i == i_count - 1 )
774 i = 0;
775 else
776 i++;
777 var_SetInteger( p_input, "spu-es", list.p_list->p_values[i].i_int );
778 var_SetInteger( p_input, "spu-choice", list.p_list->p_values[i].i_int );
779 DisplayMessage( p_vout, _("Subtitle track: %s"),
780 list2.p_list->p_values[i].psz_string );
781 var_FreeList( &list, &list2 );
783 break;
784 case ACTIONID_SUBTITLE_TOGGLE:
785 if( p_input )
787 vlc_value_t list, list2;
788 int i_count, i_sel_index, i_sel_id, i_old_id, i_new_index;
789 i_old_id = var_GetInteger( p_input, "spu-es" );
790 i_sel_id = var_GetInteger( p_input, "spu-choice" );
792 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
793 &list, &list2 );
794 i_count = list.p_list->i_count;
795 if( i_count <= 1 )
797 DisplayMessage( p_vout, _("Subtitle track: %s"),
798 _("N/A") );
799 var_FreeList( &list, &list2 );
800 break;
802 for( i_sel_index = 0; i_sel_index < i_count; i_sel_index++ )
804 if( i_sel_id == list.p_list->p_values[i_sel_index].i_int )
806 break;
809 /* if there is nothing to toggle choose the first track */
810 if( !i_sel_index ) {
811 i_sel_index = 1;
812 i_sel_id = list.p_list->p_values[1].i_int;
813 var_SetInteger( p_input, "spu-choice", i_sel_id );
816 i_new_index = 0;
817 if( i_old_id != i_sel_id )
819 if( i_sel_index >= i_count )
821 var_SetInteger( p_input, "spu-choice", list.p_list->p_values[0].i_int );
823 else
825 i_new_index = i_sel_index;
828 var_SetInteger( p_input, "spu-es", list.p_list->p_values[i_new_index].i_int );
829 DisplayMessage( p_vout, _("Subtitle track: %s"),
830 list2.p_list->p_values[i_new_index].psz_string );
831 var_FreeList( &list, &list2 );
833 break;
834 case ACTIONID_PROGRAM_SID_NEXT:
835 case ACTIONID_PROGRAM_SID_PREV:
836 if( p_input )
838 vlc_value_t val, list, list2;
839 int i_count, i;
840 var_Get( p_input, "program", &val );
842 var_Change( p_input, "program", VLC_VAR_GETCHOICES,
843 &list, &list2 );
844 i_count = list.p_list->i_count;
845 if( i_count <= 1 )
847 DisplayMessage( p_vout, _("Program Service ID: %s"),
848 _("N/A") );
849 var_FreeList( &list, &list2 );
850 break;
852 for( i = 0; i < i_count; i++ )
854 if( val.i_int == list.p_list->p_values[i].i_int )
856 break;
859 /* value of program sid was not in choices list */
860 if( i == i_count )
862 msg_Warn( p_input,
863 "invalid current program SID, selecting 0" );
864 i = 0;
866 else if( i_action == ACTIONID_PROGRAM_SID_NEXT ) {
867 if( i == i_count - 1 )
868 i = 0;
869 else
870 i++;
872 else { /* ACTIONID_PROGRAM_SID_PREV */
873 if( i == 0 )
874 i = i_count - 1;
875 else
876 i--;
878 var_Set( p_input, "program", list.p_list->p_values[i] );
879 DisplayMessage( p_vout, _("Program Service ID: %s"),
880 list2.p_list->p_values[i].psz_string );
881 var_FreeList( &list, &list2 );
883 break;
885 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
886 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
887 case ACTIONID_JUMP_BACKWARD_SHORT:
888 case ACTIONID_JUMP_FORWARD_SHORT:
889 case ACTIONID_JUMP_BACKWARD_MEDIUM:
890 case ACTIONID_JUMP_FORWARD_MEDIUM:
891 case ACTIONID_JUMP_BACKWARD_LONG:
892 case ACTIONID_JUMP_FORWARD_LONG:
894 if( p_input == NULL || !var_GetBool( p_input, "can-seek" ) )
895 break;
897 const char *varname;
898 int sign = +1;
899 switch( i_action )
901 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
902 sign = -1;
903 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
904 varname = "extrashort-jump-size";
905 break;
906 case ACTIONID_JUMP_BACKWARD_SHORT:
907 sign = -1;
908 case ACTIONID_JUMP_FORWARD_SHORT:
909 varname = "short-jump-size";
910 break;
911 case ACTIONID_JUMP_BACKWARD_MEDIUM:
912 sign = -1;
913 case ACTIONID_JUMP_FORWARD_MEDIUM:
914 varname = "medium-jump-size";
915 break;
916 case ACTIONID_JUMP_BACKWARD_LONG:
917 sign = -1;
918 case ACTIONID_JUMP_FORWARD_LONG:
919 varname = "long-jump-size";
920 break;
923 mtime_t it = var_InheritInteger( p_input, varname );
924 if( it < 0 )
925 break;
926 var_SetInteger( p_input, "time-offset", it * sign * CLOCK_FREQ );
927 DisplayPosition( p_vout, slider_chan, p_input );
928 break;
931 /* Input navigation */
932 case ACTIONID_TITLE_PREV:
933 if( p_input )
934 var_TriggerCallback( p_input, "prev-title" );
935 break;
936 case ACTIONID_TITLE_NEXT:
937 if( p_input )
938 var_TriggerCallback( p_input, "next-title" );
939 break;
940 case ACTIONID_CHAPTER_PREV:
941 if( p_input )
942 var_TriggerCallback( p_input, "prev-chapter" );
943 break;
944 case ACTIONID_CHAPTER_NEXT:
945 if( p_input )
946 var_TriggerCallback( p_input, "next-chapter" );
947 break;
948 case ACTIONID_DISC_MENU:
949 if( p_input )
950 var_SetInteger( p_input, "title 0", 2 );
951 break;
952 case ACTIONID_NAV_ACTIVATE:
953 if( p_input )
954 input_Control( p_input, INPUT_NAV_ACTIVATE, NULL );
955 break;
956 case ACTIONID_NAV_UP:
957 if( p_vout )
958 input_UpdateViewpoint( p_input,
959 &(vlc_viewpoint_t) { .pitch = -1.f },
960 false );
961 if( p_input )
962 input_Control( p_input, INPUT_NAV_UP, NULL );
963 break;
964 case ACTIONID_NAV_DOWN:
965 if( p_vout )
966 input_UpdateViewpoint( p_input,
967 &(vlc_viewpoint_t) { .pitch = 1.f },
968 false );
969 if( p_input )
970 input_Control( p_input, INPUT_NAV_DOWN, NULL );
971 break;
972 case ACTIONID_NAV_LEFT:
973 if( p_vout )
974 input_UpdateViewpoint( p_input,
975 &(vlc_viewpoint_t) { .yaw = -1.f },
976 false );
977 if( p_input )
978 input_Control( p_input, INPUT_NAV_LEFT, NULL );
979 break;
980 case ACTIONID_NAV_RIGHT:
981 if( p_vout )
982 input_UpdateViewpoint( p_input,
983 &(vlc_viewpoint_t) { .yaw = 1.f },
984 false );
985 if( p_input )
986 input_Control( p_input, INPUT_NAV_RIGHT, NULL );
987 break;
989 /* Video Output actions */
990 case ACTIONID_SNAPSHOT:
991 if( p_vout )
992 var_TriggerCallback( p_vout, "video-snapshot" );
993 break;
995 case ACTIONID_TOGGLE_FULLSCREEN:
997 if( p_vout )
999 bool fs = var_ToggleBool( p_vout, "fullscreen" );
1000 var_SetBool( p_playlist, "fullscreen", fs );
1002 else
1003 var_ToggleBool( p_playlist, "fullscreen" );
1004 break;
1007 case ACTIONID_LEAVE_FULLSCREEN:
1008 if( p_vout )
1009 var_SetBool( p_vout, "fullscreen", false );
1010 var_SetBool( p_playlist, "fullscreen", false );
1011 break;
1013 case ACTIONID_ASPECT_RATIO:
1014 if( p_vout )
1016 vlc_value_t val={0}, val_list, text_list;
1017 var_Get( p_vout, "aspect-ratio", &val );
1018 if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETCHOICES,
1019 &val_list, &text_list ) >= 0 )
1021 int i;
1022 for( i = 0; i < val_list.p_list->i_count; i++ )
1024 if( !strcmp( val_list.p_list->p_values[i].psz_string,
1025 val.psz_string ) )
1027 i++;
1028 break;
1031 if( i == val_list.p_list->i_count ) i = 0;
1032 var_SetString( p_vout, "aspect-ratio",
1033 val_list.p_list->p_values[i].psz_string );
1034 DisplayMessage( p_vout, _("Aspect ratio: %s"),
1035 text_list.p_list->p_values[i].psz_string );
1037 var_FreeList( &val_list, &text_list );
1039 free( val.psz_string );
1041 break;
1043 case ACTIONID_CROP:
1044 if( p_vout )
1046 vlc_value_t val={0}, val_list, text_list;
1047 var_Get( p_vout, "crop", &val );
1048 if( var_Change( p_vout, "crop", VLC_VAR_GETCHOICES,
1049 &val_list, &text_list ) >= 0 )
1051 int i;
1052 for( i = 0; i < val_list.p_list->i_count; i++ )
1054 if( !strcmp( val_list.p_list->p_values[i].psz_string,
1055 val.psz_string ) )
1057 i++;
1058 break;
1061 if( i == val_list.p_list->i_count ) i = 0;
1062 var_SetString( p_vout, "crop",
1063 val_list.p_list->p_values[i].psz_string );
1064 DisplayMessage( p_vout, _("Crop: %s"),
1065 text_list.p_list->p_values[i].psz_string );
1067 var_FreeList( &val_list, &text_list );
1069 free( val.psz_string );
1071 break;
1072 case ACTIONID_CROP_TOP:
1073 if( p_vout )
1074 var_IncInteger( p_vout, "crop-top" );
1075 break;
1076 case ACTIONID_UNCROP_TOP:
1077 if( p_vout )
1078 var_DecInteger( p_vout, "crop-top" );
1079 break;
1080 case ACTIONID_CROP_BOTTOM:
1081 if( p_vout )
1082 var_IncInteger( p_vout, "crop-bottom" );
1083 break;
1084 case ACTIONID_UNCROP_BOTTOM:
1085 if( p_vout )
1086 var_DecInteger( p_vout, "crop-bottom" );
1087 break;
1088 case ACTIONID_CROP_LEFT:
1089 if( p_vout )
1090 var_IncInteger( p_vout, "crop-left" );
1091 break;
1092 case ACTIONID_UNCROP_LEFT:
1093 if( p_vout )
1094 var_DecInteger( p_vout, "crop-left" );
1095 break;
1096 case ACTIONID_CROP_RIGHT:
1097 if( p_vout )
1098 var_IncInteger( p_vout, "crop-right" );
1099 break;
1100 case ACTIONID_UNCROP_RIGHT:
1101 if( p_vout )
1102 var_DecInteger( p_vout, "crop-right" );
1103 break;
1105 case ACTIONID_VIEWPOINT_FOV_IN:
1106 if( p_vout )
1107 input_UpdateViewpoint( p_input,
1108 &(vlc_viewpoint_t) { .fov = -1.f },
1109 false );
1110 break;
1111 case ACTIONID_VIEWPOINT_FOV_OUT:
1112 if( p_vout )
1113 input_UpdateViewpoint( p_input,
1114 &(vlc_viewpoint_t) { .fov = 1.f },
1115 false );
1116 break;
1118 case ACTIONID_VIEWPOINT_ZOOM_IN:
1119 if( p_vout )
1120 input_UpdateViewpoint( p_input,
1121 &(vlc_viewpoint_t) { .zoom = 0.01f },
1122 false );
1123 break;
1124 case ACTIONID_VIEWPOINT_ZOOM_OUT:
1125 if( p_vout )
1126 input_UpdateViewpoint( p_input,
1127 &(vlc_viewpoint_t) { .zoom = -0.01f },
1128 false );
1129 break;
1131 case ACTIONID_VIEWPOINT_ROLL_CLOCK:
1132 if( p_vout )
1133 input_UpdateViewpoint( p_input,
1134 &(vlc_viewpoint_t) { .roll = -1.f },
1135 false );
1136 break;
1137 case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
1138 if( p_vout )
1139 input_UpdateViewpoint( p_input,
1140 &(vlc_viewpoint_t) { .roll = 1.f },
1141 false );
1142 break;
1144 case ACTIONID_TOGGLE_AUTOSCALE:
1145 if( p_vout )
1147 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1148 if ( f_scalefactor != 1.f )
1150 var_SetFloat( p_vout, "zoom", 1.f );
1151 DisplayMessage( p_vout, _("Zooming reset") );
1153 else
1155 bool b_autoscale = !var_GetBool( p_vout, "autoscale" );
1156 var_SetBool( p_vout, "autoscale", b_autoscale );
1157 if( b_autoscale )
1158 DisplayMessage( p_vout, _("Scaled to screen") );
1159 else
1160 DisplayMessage( p_vout, _("Original Size") );
1163 break;
1164 case ACTIONID_SCALE_UP:
1165 if( p_vout )
1167 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1169 if( f_scalefactor < 10.f )
1170 f_scalefactor += .1f;
1171 var_SetFloat( p_vout, "zoom", f_scalefactor );
1173 break;
1174 case ACTIONID_SCALE_DOWN:
1175 if( p_vout )
1177 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1179 if( f_scalefactor > .3f )
1180 f_scalefactor -= .1f;
1181 var_SetFloat( p_vout, "zoom", f_scalefactor );
1183 break;
1185 case ACTIONID_ZOOM_QUARTER:
1186 case ACTIONID_ZOOM_HALF:
1187 case ACTIONID_ZOOM_ORIGINAL:
1188 case ACTIONID_ZOOM_DOUBLE:
1189 if( p_vout )
1191 float f;
1192 switch( i_action )
1194 case ACTIONID_ZOOM_QUARTER: f = 0.25; break;
1195 case ACTIONID_ZOOM_HALF: f = 0.5; break;
1196 case ACTIONID_ZOOM_ORIGINAL: f = 1.; break;
1197 /*case ACTIONID_ZOOM_DOUBLE:*/
1198 default: f = 2.; break;
1200 var_SetFloat( p_vout, "zoom", f );
1202 break;
1203 case ACTIONID_ZOOM:
1204 case ACTIONID_UNZOOM:
1205 if( p_vout )
1207 vlc_value_t val={0}, val_list, text_list;
1208 var_Get( p_vout, "zoom", &val );
1209 if( var_Change( p_vout, "zoom", VLC_VAR_GETCHOICES,
1210 &val_list, &text_list ) >= 0 )
1212 int i;
1213 for( i = 0; i < val_list.p_list->i_count; i++ )
1215 if( val_list.p_list->p_values[i].f_float
1216 == val.f_float )
1218 if( i_action == ACTIONID_ZOOM )
1219 i++;
1220 else /* ACTIONID_UNZOOM */
1221 i--;
1222 break;
1225 if( i == val_list.p_list->i_count ) i = 0;
1226 if( i == -1 ) i = val_list.p_list->i_count-1;
1227 var_SetFloat( p_vout, "zoom",
1228 val_list.p_list->p_values[i].f_float );
1229 DisplayMessage( p_vout, _("Zoom mode: %s"),
1230 text_list.p_list->p_values[i].psz_string );
1232 var_FreeList( &val_list, &text_list );
1235 break;
1237 case ACTIONID_DEINTERLACE:
1238 if( p_vout )
1240 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1241 if( i_deinterlace != 0 )
1243 var_SetInteger( p_vout, "deinterlace", 0 );
1244 DisplayMessage( p_vout, _("Deinterlace off") );
1246 else
1248 var_SetInteger( p_vout, "deinterlace", 1 );
1250 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
1251 vlc_value_t vlist, tlist;
1252 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ) )
1254 const char *psz_text = NULL;
1255 for( int i = 0; i < vlist.p_list->i_count; i++ )
1257 if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
1259 psz_text = tlist.p_list->p_values[i].psz_string;
1260 break;
1263 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
1264 psz_text ? psz_text : psz_mode );
1266 var_FreeList( &vlist, &tlist );
1268 free( psz_mode );
1271 break;
1272 case ACTIONID_DEINTERLACE_MODE:
1273 if( p_vout )
1275 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
1276 vlc_value_t vlist, tlist;
1277 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ))
1279 const char *psz_text = NULL;
1280 int i;
1281 for( i = 0; i < vlist.p_list->i_count; i++ )
1283 if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
1285 i++;
1286 break;
1289 if( i == vlist.p_list->i_count ) i = 0;
1290 psz_text = tlist.p_list->p_values[i].psz_string;
1291 var_SetString( p_vout, "deinterlace-mode", vlist.p_list->p_values[i].psz_string );
1293 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1294 if( i_deinterlace != 0 )
1296 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
1297 psz_text ? psz_text : psz_mode );
1299 else
1301 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace off"),
1302 psz_text ? psz_text : psz_mode );
1305 var_FreeList( &vlist, &tlist );
1307 free( psz_mode );
1309 break;
1311 case ACTIONID_SUBPOS_DOWN:
1312 case ACTIONID_SUBPOS_UP:
1314 if( p_input )
1316 vlc_value_t val, list, list2;
1317 int i_count;
1318 var_Get( p_input, "spu-es", &val );
1320 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
1321 &list, &list2 );
1322 i_count = list.p_list->i_count;
1323 if( i_count < 1 || val.i_int < 0 )
1325 DisplayMessage( p_vout,
1326 _("Subtitle position: no active subtitle") );
1327 var_FreeList( &list, &list2 );
1328 break;
1331 int i_pos;
1332 if( i_action == ACTIONID_SUBPOS_DOWN )
1333 i_pos = var_DecInteger( p_vout, "sub-margin" );
1334 else
1335 i_pos = var_IncInteger( p_vout, "sub-margin" );
1337 ClearChannels( p_vout, slider_chan );
1338 DisplayMessage( p_vout, _( "Subtitle position %d px" ), i_pos );
1339 var_FreeList( &list, &list2 );
1341 break;
1344 case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN:
1345 case ACTIONID_SUBTITLE_TEXT_SCALE_UP:
1346 case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL:
1348 if( p_vout )
1350 int i_scale;
1351 if( i_action == ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL )
1353 i_scale = 100;
1355 else
1357 i_scale = var_GetInteger( p_vout, "sub-text-scale" );
1358 i_scale += ((i_action == ACTIONID_SUBTITLE_TEXT_SCALE_UP) ? 1 : -1) * 25;
1359 i_scale = VLC_CLIP( i_scale, 10, 500 );
1361 var_SetInteger( p_vout, "sub-text-scale", i_scale );
1362 DisplayMessage( p_vout, _( "Subtitle text scale %d%%" ), i_scale );
1366 /* Input + video output */
1367 case ACTIONID_POSITION:
1368 if( p_vout && vout_OSDEpg( p_vout, input_GetItem( p_input ) ) )
1369 DisplayPosition( p_vout, slider_chan, p_input );
1370 break;
1372 case ACTIONID_COMBO_VOL_ZOOM_UP:
1373 if( b_vrnav )
1374 DO_ACTION( ACTIONID_VIEWPOINT_ZOOM_IN );
1375 else
1376 DO_ACTION( ACTIONID_VOL_UP );
1377 break;
1378 case ACTIONID_COMBO_VOL_ZOOM_DOWN:
1379 if( b_vrnav )
1380 DO_ACTION( ACTIONID_VIEWPOINT_ZOOM_OUT );
1381 else
1382 DO_ACTION( ACTIONID_VOL_DOWN );
1383 break;
1386 return VLC_SUCCESS;
1389 /*****************************************************************************
1390 * ActionEvent: callback for hotkey actions
1391 *****************************************************************************/
1392 static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
1393 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1395 intf_thread_t *p_intf = (intf_thread_t *)p_data;
1396 intf_sys_t *p_sys = p_intf->p_sys;
1398 (void)libvlc;
1399 (void)psz_var;
1400 (void)oldval;
1402 vlc_mutex_lock( &p_intf->p_sys->lock );
1403 input_thread_t *p_input = p_sys->p_input ? vlc_object_hold( p_sys->p_input )
1404 : NULL;
1405 vout_thread_t *p_vout = p_sys->p_vout ? vlc_object_hold( p_sys->p_vout )
1406 : NULL;
1407 int slider_chan = p_sys->slider_chan;
1408 bool b_vrnav = p_sys->vrnav.b_can_change;
1409 vlc_mutex_unlock( &p_intf->p_sys->lock );
1411 int i_ret = PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav,
1412 newval.i_int );
1414 if( p_input != NULL )
1415 vlc_object_release( p_input );
1416 if( p_vout != NULL )
1417 vlc_object_release( p_vout );
1419 return i_ret;
1422 static void PlayBookmark( intf_thread_t *p_intf, int i_num )
1424 char *psz_bookmark_name;
1425 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
1426 return;
1428 playlist_t *p_playlist = pl_Get( p_intf );
1429 char *psz_bookmark = var_CreateGetString( p_intf, psz_bookmark_name );
1431 PL_LOCK;
1432 FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
1433 char *psz_uri = input_item_GetURI( p_item->p_input );
1434 if( !strcmp( psz_bookmark, psz_uri ) )
1436 free( psz_uri );
1437 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked,
1438 NULL, p_item );
1439 break;
1441 else
1442 free( psz_uri );
1443 FOREACH_END();
1444 PL_UNLOCK;
1446 free( psz_bookmark );
1447 free( psz_bookmark_name );
1450 static void SetBookmark( intf_thread_t *p_intf, int i_num )
1452 char *psz_bookmark_name;
1453 char *psz_uri = NULL;
1454 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
1455 return;
1457 playlist_t *p_playlist = pl_Get( p_intf );
1458 var_Create( p_intf, psz_bookmark_name,
1459 VLC_VAR_STRING|VLC_VAR_DOINHERIT );
1461 PL_LOCK;
1462 playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
1463 if( p_item ) psz_uri = input_item_GetURI( p_item->p_input );
1464 PL_UNLOCK;
1466 if( p_item )
1468 config_PutPsz( p_intf, psz_bookmark_name, psz_uri);
1469 msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num, psz_uri);
1472 free( psz_uri );
1473 free( psz_bookmark_name );
1476 static void DisplayPosition( vout_thread_t *p_vout, int slider_chan,
1477 input_thread_t *p_input )
1479 char psz_duration[MSTRTIME_MAX_SIZE];
1480 char psz_time[MSTRTIME_MAX_SIZE];
1482 if( p_vout == NULL ) return;
1484 ClearChannels( p_vout, slider_chan );
1486 int64_t t = var_GetInteger( p_input, "time" ) / CLOCK_FREQ;
1487 int64_t l = var_GetInteger( p_input, "length" ) / CLOCK_FREQ;
1489 secstotimestr( psz_time, t );
1491 if( l > 0 )
1493 secstotimestr( psz_duration, l );
1494 DisplayMessage( p_vout, "%s / %s", psz_time, psz_duration );
1496 else if( t > 0 )
1498 DisplayMessage( p_vout, "%s", psz_time );
1501 if( var_GetBool( p_vout, "fullscreen" ) )
1503 vlc_value_t pos;
1504 var_Get( p_input, "position", &pos );
1505 vout_OSDSlider( p_vout, slider_chan,
1506 pos.f_float * 100, OSD_HOR_SLIDER );
1510 static void DisplayVolume( vout_thread_t *p_vout, int slider_chan, float vol )
1512 if( p_vout == NULL )
1513 return;
1514 ClearChannels( p_vout, slider_chan );
1516 if( var_GetBool( p_vout, "fullscreen" ) )
1517 vout_OSDSlider( p_vout, slider_chan,
1518 lroundf(vol * 100.f), OSD_VERT_SLIDER );
1519 DisplayMessage( p_vout, _( "Volume %ld%%" ), lroundf(vol * 100.f) );
1522 static void DisplayRate( vout_thread_t *p_vout, float f_rate )
1524 DisplayMessage( p_vout, _("Speed: %.2fx"), (double) f_rate );
1527 static float AdjustRateFine( vlc_object_t *p_obj, const int i_dir )
1529 const float f_rate_min = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MAX;
1530 const float f_rate_max = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MIN;
1531 float f_rate = var_GetFloat( p_obj, "rate" );
1533 int i_sign = f_rate < 0 ? -1 : 1;
1535 f_rate = floor( fabs(f_rate) / 0.1 + i_dir + 0.05 ) * 0.1;
1537 if( f_rate < f_rate_min )
1538 f_rate = f_rate_min;
1539 else if( f_rate > f_rate_max )
1540 f_rate = f_rate_max;
1541 f_rate *= i_sign;
1543 return f_rate;
1546 static void ClearChannels( vout_thread_t *p_vout, int slider_chan )
1548 if( p_vout )
1550 vout_FlushSubpictureChannel( p_vout, SPU_DEFAULT_CHANNEL );
1551 vout_FlushSubpictureChannel( p_vout, slider_chan );