input: change INPUT_RATE_MAX/INPUT_RATE_MIN
[vlc.git] / modules / control / hotkeys.c
blob2b7bbfcd61fec35add93d58da57d32a3fe38aa8a
1 /*****************************************************************************
2 * hotkeys.c: Hotkey handling for vlc
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
6 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
7 * Jean-Paul Saman <jpsaman #_at_# m2x.nl>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_interface.h>
36 #include <vlc_input.h>
37 #include <vlc_aout.h>
38 #include <vlc_mouse.h>
39 #include <vlc_viewpoint.h>
40 #include <vlc_vout_osd.h>
41 #include <vlc_playlist_legacy.h>
42 #include <vlc_actions.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 vlc_tick_t i_time_subtitle;
61 vlc_tick_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, VOUT_SPU_CHANNEL_OSD, __VA_ARGS__); \
91 } while(0)
92 #define DisplayIcon(vout, icon) \
93 do { if(vout) vout_OSDIcon(vout, VOUT_SPU_CHANNEL_OSD, 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 void var_FreeList( size_t n, vlc_value_t *values, char **texts )
111 free( values );
113 for( size_t i = 0; i < n; i++ )
114 free( texts[i] );
115 free( texts );
118 static void var_FreeStringList( size_t n, vlc_value_t *values, char **texts )
120 for( size_t i = 0; i < n; i++ )
121 free( values[i].psz_string );
123 var_FreeList( n, values, texts );
126 static int MovedEvent( vlc_object_t *p_this, char const *psz_var,
127 vlc_value_t oldval, vlc_value_t newval, void *p_data )
129 intf_thread_t *p_intf = (intf_thread_t *)p_data;
130 intf_sys_t *p_sys = p_intf->p_sys;
132 (void) p_this; (void) psz_var; (void) oldval;
134 if( p_sys->vrnav.b_button_pressed )
136 int i_horizontal = newval.coords.x - p_sys->vrnav.x;
137 int i_vertical = newval.coords.y - p_sys->vrnav.y;
139 vlc_viewpoint_t viewpoint = {
140 .yaw = -i_horizontal * 0.05f,
141 .pitch = -i_vertical * 0.05f,
144 input_UpdateViewpoint( p_sys->p_input, &viewpoint, false );
146 p_sys->vrnav.x = newval.coords.x;
147 p_sys->vrnav.y = newval.coords.y;
150 return VLC_SUCCESS;
153 static int ViewpointMovedEvent( vlc_object_t *p_this, char const *psz_var,
154 vlc_value_t oldval, vlc_value_t newval,
155 void *p_data )
157 intf_thread_t *p_intf = (intf_thread_t *)p_data;
158 intf_sys_t *p_sys = p_intf->p_sys;
160 (void) p_this; (void) psz_var; (void) oldval;
162 input_UpdateViewpoint( p_sys->p_input, newval.p_address, false );
164 return VLC_SUCCESS;
167 static int ButtonEvent( vlc_object_t *p_this, char const *psz_var,
168 vlc_value_t oldval, vlc_value_t newval, void *p_data )
170 intf_thread_t *p_intf = p_data;
171 intf_sys_t *p_sys = p_intf->p_sys;
173 (void) psz_var;
175 if ((newval.i_int & (1 << MOUSE_BUTTON_LEFT)) && p_sys->vrnav.b_can_change)
177 if( !p_sys->vrnav.b_button_pressed )
179 p_sys->vrnav.b_button_pressed = true;
180 var_GetCoords( p_this, "mouse-moved",
181 &p_sys->vrnav.x, &p_sys->vrnav.y );
184 else
185 p_sys->vrnav.b_button_pressed = false;
187 unsigned pressed = newval.i_int & ~oldval.i_int;
189 if (pressed & (1 << MOUSE_BUTTON_LEFT))
190 var_SetBool(pl_Get(p_intf), "intf-popupmenu", false);
191 if (pressed & (1 << MOUSE_BUTTON_CENTER))
192 var_TriggerCallback(pl_Get(p_intf), "intf-toggle-fscontrol");
193 #ifndef _WIN32
194 if (pressed & (1 << MOUSE_BUTTON_RIGHT))
195 #else
196 if ((oldval.i_int & (1 << MOUSE_BUTTON_RIGHT))
197 && !(newval.i_int & (1 << MOUSE_BUTTON_RIGHT)))
198 #endif
199 var_SetBool(pl_Get(p_intf), "intf-popupmenu", true);
201 for (int i = MOUSE_BUTTON_WHEEL_UP; i <= MOUSE_BUTTON_WHEEL_RIGHT; i++)
202 if (pressed & (1 << i))
203 var_SetInteger(p_intf->obj.libvlc, "key-pressed",
204 i - MOUSE_BUTTON_WHEEL_UP + KEY_MOUSEWHEELUP);
206 return VLC_SUCCESS;
209 static void ChangeVout( intf_thread_t *p_intf, vout_thread_t *p_vout )
211 intf_sys_t *p_sys = p_intf->p_sys;
213 int slider_chan;
214 bool b_vrnav_can_change;
215 if( p_vout != NULL )
217 slider_chan = vout_RegisterSubpictureChannel( p_vout );
218 b_vrnav_can_change = var_GetBool( p_vout, "viewpoint-changeable" );
221 vout_thread_t *p_old_vout = p_sys->p_vout;
222 if( p_old_vout != NULL && p_sys->vrnav.b_can_change )
223 var_DelCallback( p_old_vout, "viewpoint-moved", ViewpointMovedEvent,
224 p_intf );
226 vlc_mutex_lock( &p_sys->lock );
227 p_sys->p_vout = p_vout;
228 if( p_vout != NULL )
230 p_sys->slider_chan = slider_chan;
231 p_sys->vrnav.b_can_change = b_vrnav_can_change;
233 else
234 p_sys->vrnav.b_can_change = false;
235 vlc_mutex_unlock( &p_sys->lock );
237 if( p_old_vout != NULL )
239 var_DelCallback( p_old_vout, "mouse-button-down", ButtonEvent,
240 p_intf );
241 var_DelCallback( p_old_vout, "mouse-moved", MovedEvent, p_intf );
242 vlc_object_release( p_old_vout );
245 if( p_vout != NULL )
247 var_AddCallback( p_vout, "mouse-moved", MovedEvent, p_intf );
248 var_AddCallback( p_vout, "mouse-button-down", ButtonEvent, p_intf );
250 if( p_sys->vrnav.b_can_change )
251 var_AddCallback( p_vout, "viewpoint-moved",
252 ViewpointMovedEvent, p_intf );
256 static int InputEvent( vlc_object_t *p_this, char const *psz_var,
257 vlc_value_t oldval, vlc_value_t val, void *p_data )
259 input_thread_t *p_input = (input_thread_t *)p_this;
260 intf_thread_t *p_intf = p_data;
262 (void) psz_var; (void) oldval;
264 if( val.i_int == INPUT_EVENT_VOUT )
265 ChangeVout( p_intf, input_GetVout( p_input ) );
267 return VLC_SUCCESS;
270 static void ChangeInput( intf_thread_t *p_intf, input_thread_t *p_input )
272 intf_sys_t *p_sys = p_intf->p_sys;
274 input_thread_t *p_old_input = p_sys->p_input;
275 vout_thread_t *p_old_vout = NULL;
276 if( p_old_input != NULL )
278 /* First, remove callbacks from previous input. It's safe to access it
279 * unlocked, since it's written from this thread */
280 var_DelCallback( p_old_input, "intf-event", InputEvent, p_intf );
282 p_old_vout = p_sys->p_vout;
283 /* Remove mouse events before setting new input, since callbacks may
284 * access it */
285 if( p_old_vout != NULL )
287 if( p_sys->vrnav.b_can_change )
288 var_DelCallback( p_old_vout, "viewpoint-moved",
289 ViewpointMovedEvent, p_intf );
291 var_DelCallback( p_old_vout, "mouse-button-down", ButtonEvent,
292 p_intf );
293 var_DelCallback( p_old_vout, "mouse-moved", MovedEvent,
294 p_intf );
298 /* Replace input and vout locked */
299 vlc_mutex_lock( &p_sys->lock );
300 p_sys->p_input = p_input ? vlc_object_hold( p_input ) : NULL;
301 p_sys->p_vout = NULL;
302 p_sys->vrnav.b_can_change = false;
303 vlc_mutex_unlock( &p_sys->lock );
305 /* Release old input and vout objects unlocked */
306 if( p_old_input != NULL )
308 if( p_old_vout != NULL )
309 vlc_object_release( p_old_vout );
310 vlc_object_release( p_old_input );
313 /* Register input events */
314 if( p_input != NULL )
315 var_AddCallback( p_input, "intf-event", InputEvent, p_intf );
318 static int PlaylistEvent( vlc_object_t *p_this, char const *psz_var,
319 vlc_value_t oldval, vlc_value_t val, void *p_data )
321 intf_thread_t *p_intf = p_data;
323 (void) p_this; (void) psz_var; (void) oldval;
325 ChangeInput( p_intf, val.p_address );
327 return VLC_SUCCESS;
330 /*****************************************************************************
331 * Open: initialize interface
332 *****************************************************************************/
333 static int Open( vlc_object_t *p_this )
335 intf_thread_t *p_intf = (intf_thread_t *)p_this;
336 intf_sys_t *p_sys;
337 p_sys = malloc( sizeof( intf_sys_t ) );
338 if( !p_sys )
339 return VLC_ENOMEM;
341 p_intf->p_sys = p_sys;
343 p_sys->p_vout = NULL;
344 p_sys->p_input = NULL;
345 p_sys->vrnav.b_can_change = false;
346 p_sys->vrnav.b_button_pressed = false;
347 p_sys->subtitle_delaybookmarks.i_time_audio = VLC_TICK_INVALID;
348 p_sys->subtitle_delaybookmarks.i_time_subtitle = VLC_TICK_INVALID;
350 vlc_mutex_init( &p_sys->lock );
352 var_AddCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
354 var_AddCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
356 return VLC_SUCCESS;
359 /*****************************************************************************
360 * Close: destroy interface
361 *****************************************************************************/
362 static void Close( vlc_object_t *p_this )
364 intf_thread_t *p_intf = (intf_thread_t *)p_this;
365 intf_sys_t *p_sys = p_intf->p_sys;
367 var_DelCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
369 var_DelCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
371 ChangeInput( p_intf, NULL );
373 vlc_mutex_destroy( &p_sys->lock );
375 /* Destroy structure */
376 free( p_sys );
379 static int PutAction( intf_thread_t *p_intf, input_thread_t *p_input,
380 vout_thread_t *p_vout, int slider_chan, bool b_vrnav,
381 int i_action )
383 #define DO_ACTION(x) PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav, x)
384 intf_sys_t *p_sys = p_intf->p_sys;
385 playlist_t *p_playlist = pl_Get( p_intf );
387 /* Quit */
388 switch( i_action )
390 /* Libvlc / interface actions */
391 case ACTIONID_QUIT:
392 libvlc_Quit( p_intf->obj.libvlc );
394 ClearChannels( p_vout, slider_chan );
395 DisplayMessage( p_vout, _( "Quit" ) );
396 break;
398 case ACTIONID_INTF_TOGGLE_FSC:
399 case ACTIONID_INTF_HIDE:
400 var_TriggerCallback( p_playlist, "intf-toggle-fscontrol" );
401 break;
402 case ACTIONID_INTF_BOSS:
403 var_TriggerCallback( p_playlist, "intf-boss" );
404 break;
405 case ACTIONID_INTF_POPUP_MENU:
406 var_TriggerCallback( p_playlist, "intf-popupmenu" );
407 break;
409 /* Playlist actions (including audio) */
410 case ACTIONID_LOOP:
412 /* Toggle Normal -> Loop -> Repeat -> Normal ... */
413 const char *mode;
414 if( var_GetBool( p_playlist, "repeat" ) )
416 var_SetBool( p_playlist, "repeat", false );
417 mode = N_("Off");
419 else
420 if( var_GetBool( p_playlist, "loop" ) )
421 { /* FIXME: this is not atomic, we should use a real tristate */
422 var_SetBool( p_playlist, "loop", false );
423 var_SetBool( p_playlist, "repeat", true );
424 mode = N_("One");
426 else
428 var_SetBool( p_playlist, "loop", true );
429 mode = N_("All");
431 DisplayMessage( p_vout, _("Loop: %s"), vlc_gettext(mode) );
432 break;
435 case ACTIONID_RANDOM:
437 const bool state = var_ToggleBool( p_playlist, "random" );
438 DisplayMessage( p_vout, _("Random: %s"),
439 vlc_gettext( state ? N_("On") : N_("Off") ) );
440 break;
443 case ACTIONID_NEXT:
444 DisplayMessage( p_vout, _("Next") );
445 playlist_Next( p_playlist );
446 break;
447 case ACTIONID_PREV:
448 DisplayMessage( p_vout, _("Previous") );
449 playlist_Prev( p_playlist );
450 break;
452 case ACTIONID_STOP:
453 playlist_Stop( p_playlist );
454 break;
456 case ACTIONID_RATE_NORMAL:
457 var_SetFloat( p_playlist, "rate", 1.f );
458 DisplayRate( p_vout, 1.f );
459 break;
460 case ACTIONID_FASTER:
461 var_TriggerCallback( p_playlist, "rate-faster" );
462 DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
463 break;
464 case ACTIONID_SLOWER:
465 var_TriggerCallback( p_playlist, "rate-slower" );
466 DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
467 break;
468 case ACTIONID_RATE_FASTER_FINE:
469 case ACTIONID_RATE_SLOWER_FINE:
471 const int i_dir = i_action == ACTIONID_RATE_FASTER_FINE ? 1 : -1;
472 float rate = AdjustRateFine( VLC_OBJECT(p_playlist), i_dir );
474 var_SetFloat( p_playlist, "rate", rate );
475 DisplayRate( p_vout, rate );
476 break;
479 case ACTIONID_PLAY_BOOKMARK1:
480 case ACTIONID_PLAY_BOOKMARK2:
481 case ACTIONID_PLAY_BOOKMARK3:
482 case ACTIONID_PLAY_BOOKMARK4:
483 case ACTIONID_PLAY_BOOKMARK5:
484 case ACTIONID_PLAY_BOOKMARK6:
485 case ACTIONID_PLAY_BOOKMARK7:
486 case ACTIONID_PLAY_BOOKMARK8:
487 case ACTIONID_PLAY_BOOKMARK9:
488 case ACTIONID_PLAY_BOOKMARK10:
489 PlayBookmark( p_intf, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
490 break;
492 case ACTIONID_SET_BOOKMARK1:
493 case ACTIONID_SET_BOOKMARK2:
494 case ACTIONID_SET_BOOKMARK3:
495 case ACTIONID_SET_BOOKMARK4:
496 case ACTIONID_SET_BOOKMARK5:
497 case ACTIONID_SET_BOOKMARK6:
498 case ACTIONID_SET_BOOKMARK7:
499 case ACTIONID_SET_BOOKMARK8:
500 case ACTIONID_SET_BOOKMARK9:
501 case ACTIONID_SET_BOOKMARK10:
502 SetBookmark( p_intf, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
503 break;
504 case ACTIONID_PLAY_CLEAR:
505 playlist_Clear( p_playlist, pl_Unlocked );
506 break;
507 case ACTIONID_VOL_UP:
509 float vol;
510 if( playlist_VolumeUp( p_playlist, 1, &vol ) == 0 )
511 DisplayVolume( p_vout, slider_chan, vol );
512 break;
514 case ACTIONID_VOL_DOWN:
516 float vol;
517 if( playlist_VolumeDown( p_playlist, 1, &vol ) == 0 )
518 DisplayVolume( p_vout, slider_chan, vol );
519 break;
521 case ACTIONID_VOL_MUTE:
523 int mute = playlist_MuteGet( p_playlist );
524 if( mute < 0 )
525 break;
526 mute = !mute;
527 if( playlist_MuteSet( p_playlist, mute ) )
528 break;
530 float vol = playlist_VolumeGet( p_playlist );
531 if( mute || vol == 0.f )
533 ClearChannels( p_vout, slider_chan );
534 DisplayIcon( p_vout, OSD_MUTE_ICON );
536 else
537 DisplayVolume( p_vout, slider_chan, vol );
538 break;
541 case ACTIONID_AUDIODEVICE_CYCLE:
543 audio_output_t *p_aout = playlist_GetAout( p_playlist );
544 if( p_aout == NULL )
545 break;
547 char **ids, **names;
548 int n = aout_DevicesList( p_aout, &ids, &names );
549 if( n == -1 )
550 break;
552 char *dev = aout_DeviceGet( p_aout );
553 const char *devstr = (dev != NULL) ? dev : "";
555 int idx = 0;
556 for( int i = 0; i < n; i++ )
558 if( !strcmp(devstr, ids[i]) )
559 idx = (i + 1) % n;
561 free( dev );
563 if( !aout_DeviceSet( p_aout, ids[idx] ) )
564 DisplayMessage( p_vout, _("Audio Device: %s"), names[idx] );
565 vlc_object_release( p_aout );
567 for( int i = 0; i < n; i++ )
569 free( ids[i] );
570 free( names[i] );
572 free( ids );
573 free( names );
574 break;
577 /* Playlist + input actions */
578 case ACTIONID_PLAY_PAUSE:
579 if( p_input )
581 ClearChannels( p_vout, slider_chan );
583 int state = var_GetInteger( p_input, "state" );
584 DisplayIcon( p_vout, state != PAUSE_S ? OSD_PAUSE_ICON : OSD_PLAY_ICON );
586 playlist_TogglePause( p_playlist );
587 break;
589 case ACTIONID_PLAY:
590 if( p_input && var_GetFloat( p_input, "rate" ) != 1.f )
591 /* Return to normal speed */
592 var_SetFloat( p_input, "rate", 1.f );
593 else
595 ClearChannels( p_vout, slider_chan );
596 DisplayIcon( p_vout, OSD_PLAY_ICON );
597 playlist_Play( p_playlist );
599 break;
601 /* Playlist + video output actions */
602 case ACTIONID_WALLPAPER:
604 bool wp = var_ToggleBool( p_playlist, "video-wallpaper" );
605 if( p_vout )
606 var_SetBool( p_vout, "video-wallpaper", wp );
607 break;
610 /* Input actions */
611 case ACTIONID_PAUSE:
612 if( p_input && var_GetInteger( p_input, "state" ) != PAUSE_S )
614 ClearChannels( p_vout, slider_chan );
615 DisplayIcon( p_vout, OSD_PAUSE_ICON );
616 var_SetInteger( p_input, "state", PAUSE_S );
618 break;
620 case ACTIONID_RECORD:
621 if( p_input && var_GetBool( p_input, "can-record" ) )
623 const bool on = var_ToggleBool( p_input, "record" );
624 DisplayMessage( p_vout, vlc_gettext(on
625 ? N_("Recording") : N_("Recording done")) );
627 break;
629 case ACTIONID_FRAME_NEXT:
630 if( p_input )
632 var_TriggerCallback( p_input, "frame-next" );
633 DisplayMessage( p_vout, _("Next frame") );
635 break;
637 case ACTIONID_SUBSYNC_MARKAUDIO:
639 p_sys->subtitle_delaybookmarks.i_time_audio = vlc_tick_now();
640 DisplayMessage( p_vout, _("Sub sync: bookmarked audio time"));
641 break;
643 case ACTIONID_SUBSYNC_MARKSUB:
644 if( p_input )
646 vlc_value_t val;
647 vlc_value_t *list;
648 size_t count;
650 var_Get( p_input, "spu-es", &val );
651 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
652 &count, &list, (char ***)NULL );
654 if( count < 1 || val.i_int < 0 )
656 DisplayMessage( p_vout, _("No active subtitle") );
658 else
660 p_sys->subtitle_delaybookmarks.i_time_subtitle = vlc_tick_now();
661 DisplayMessage(p_vout,
662 _("Sub sync: bookmarked subtitle time"));
664 free(list);
666 break;
667 case ACTIONID_SUBSYNC_APPLY:
669 /* Warning! Can yield a pause in the playback.
670 * For example, the following succession of actions will yield a 5 second delay :
671 * - Pressing Shift-H (ACTIONID_SUBSYNC_MARKAUDIO)
672 * - wait 5 second
673 * - Press Shift-J (ACTIONID_SUBSYNC_MARKSUB)
674 * - Press Shift-K (ACTIONID_SUBSYNC_APPLY)
675 * --> 5 seconds pause
676 * This is due to var_SetTime() (and ultimately UpdatePtsDelay())
677 * which causes the video to pause for an equivalent duration
678 * (This problem is also present in the "Track synchronization" window) */
679 if ( p_input )
681 if ( (p_sys->subtitle_delaybookmarks.i_time_audio == VLC_TICK_INVALID) || (p_sys->subtitle_delaybookmarks.i_time_subtitle == VLC_TICK_INVALID) )
683 DisplayMessage( p_vout, _( "Sub sync: set bookmarks first!" ) );
685 else
687 vlc_tick_t i_current_subdelay = var_GetInteger( p_input, "spu-delay" );
688 vlc_tick_t i_additional_subdelay = p_sys->subtitle_delaybookmarks.i_time_audio - p_sys->subtitle_delaybookmarks.i_time_subtitle;
689 vlc_tick_t i_total_subdelay = i_current_subdelay + i_additional_subdelay;
690 var_SetInteger( p_input, "spu-delay", i_total_subdelay);
691 ClearChannels( p_vout, slider_chan );
692 DisplayMessage( p_vout, _( "Sub sync: corrected %"PRId64" ms (total delay = %"PRId64" ms)" ),
693 MS_FROM_VLC_TICK( i_additional_subdelay ),
694 MS_FROM_VLC_TICK( i_total_subdelay ) );
695 p_sys->subtitle_delaybookmarks.i_time_audio = VLC_TICK_INVALID;
696 p_sys->subtitle_delaybookmarks.i_time_subtitle = VLC_TICK_INVALID;
699 break;
701 case ACTIONID_SUBSYNC_RESET:
703 var_SetInteger( p_input, "spu-delay", 0);
704 ClearChannels( p_vout, slider_chan );
705 DisplayMessage( p_vout, _( "Sub sync: delay reset" ) );
706 p_sys->subtitle_delaybookmarks.i_time_audio = VLC_TICK_INVALID;
707 p_sys->subtitle_delaybookmarks.i_time_subtitle = VLC_TICK_INVALID;
708 break;
711 case ACTIONID_SUBDELAY_DOWN:
712 case ACTIONID_SUBDELAY_UP:
714 vlc_tick_t diff = (i_action == ACTIONID_SUBDELAY_UP) ? VLC_TICK_FROM_MS(50) : VLC_TICK_FROM_MS(-50);
715 if( p_input )
717 vlc_value_t val;
718 vlc_value_t *list;
719 size_t count;
721 var_Get( p_input, "spu-es", &val );
722 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
723 &count, &list, (char ***)NULL );
725 if( count < 1 || val.i_int < 0 )
727 DisplayMessage( p_vout, _("No active subtitle") );
728 free(list);
729 break;
731 vlc_tick_t i_delay = var_GetInteger( p_input, "spu-delay" ) + diff;
733 var_SetInteger( p_input, "spu-delay", i_delay );
734 ClearChannels( p_vout, slider_chan );
735 DisplayMessage( p_vout, _( "Subtitle delay %i ms" ),
736 (int)MS_FROM_VLC_TICK(i_delay) );
737 free(list);
739 break;
741 case ACTIONID_AUDIODELAY_DOWN:
742 case ACTIONID_AUDIODELAY_UP:
744 vlc_tick_t diff = (i_action == ACTIONID_AUDIODELAY_UP) ? VLC_TICK_FROM_MS(50) : VLC_TICK_FROM_MS(-50);
745 if( p_input )
747 vlc_tick_t i_delay = var_GetInteger( p_input, "audio-delay" )
748 + diff;
750 var_SetInteger( p_input, "audio-delay", i_delay );
751 ClearChannels( p_vout, slider_chan );
752 DisplayMessage( p_vout, _( "Audio delay %i ms" ),
753 (int)MS_FROM_VLC_TICK(i_delay) );
755 break;
758 case ACTIONID_AUDIO_TRACK:
759 if( p_input )
761 vlc_value_t val;
762 vlc_value_t *list;
763 char **list2;
764 size_t count;
766 var_Get( p_input, "audio-es", &val );
767 var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
768 &count, &list, &list2 );
770 if( count > 1 )
772 size_t i;
774 for( i = 0; i < count; i++ )
775 if( val.i_int == list[i].i_int )
776 break;
777 /* value of audio-es was not in choices list */
778 if( i == count )
780 msg_Warn( p_input,
781 "invalid current audio track, selecting 0" );
782 i = 0;
784 else if( i == count - 1 )
785 i = 1;
786 else
787 i++;
788 var_Set( p_input, "audio-es", list[i] );
789 DisplayMessage( p_vout, _("Audio track: %s"), list2[i] );
791 var_FreeList( count, list, list2 );
793 break;
795 case ACTIONID_SUBTITLE_TRACK:
796 case ACTIONID_SUBTITLE_REVERSE_TRACK:
797 if( p_input )
799 vlc_value_t val;
800 vlc_value_t *list;
801 char **list2;
802 size_t count, i;
803 var_Get( p_input, "spu-es", &val );
805 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
806 &count, &list, &list2 );
808 if( count <= 1 )
810 DisplayMessage( p_vout, _("Subtitle track: %s"),
811 _("N/A") );
812 var_FreeList( count, list, list2 );
813 break;
815 for( i = 0; i < count; i++ )
816 if( val.i_int == list[i].i_int )
817 break;
818 /* value of spu-es was not in choices list */
819 if( i == count )
821 msg_Warn( p_input,
822 "invalid current subtitle track, selecting 0" );
823 i = 0;
825 else if ((i == count - 1) && (i_action == ACTIONID_SUBTITLE_TRACK))
826 i = 0;
827 else if ((i == 0) && (i_action == ACTIONID_SUBTITLE_REVERSE_TRACK))
828 i = count - 1;
829 else
830 i = (i_action == ACTIONID_SUBTITLE_TRACK) ? i+1 : i-1;
831 var_SetInteger( p_input, "spu-es", list[i].i_int );
832 DisplayMessage( p_vout, _("Subtitle track: %s"), list2[i] );
833 var_FreeList( count, list, list2 );
835 break;
836 case ACTIONID_SUBTITLE_TOGGLE:
837 if( p_input )
839 vlc_value_t *list;
840 char **list2;
841 size_t count;
843 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
844 &count, &list, &list2 );
846 if( count <= 1 )
848 DisplayMessage( p_vout, _("Subtitle track: %s"),
849 _("N/A") );
850 var_FreeList( count, list, list2 );
851 break;
854 int i_cur_id = var_GetInteger( p_input, "spu-es" );
855 int i_new_id;
856 if( i_cur_id == -1 )
858 /* subtitles were disabled: restore the saved track id */
859 i_new_id = var_GetInteger( p_input, "spu-choice" );
860 if( i_new_id != -1 )
861 var_SetInteger( p_input, "spu-choice", -1 );
863 else
865 /* subtitles were enabled: save the track id and disable */
866 i_new_id = -1;
867 var_SetInteger( p_input, "spu-choice", i_cur_id );
870 int i_new_index = 1; /* select first track by default */
871 /* if subtitles were disabled with no saved id, use the first track */
872 if( i_cur_id != -1 || i_new_id != -1 )
874 for( size_t i = 0; i < count; ++i )
876 if( i_new_id == list[i].i_int )
878 i_new_index = i;
879 break;
883 var_SetInteger( p_input, "spu-es", list[i_new_index].i_int );
884 DisplayMessage( p_vout, _("Subtitle track: %s"),
885 list2[i_new_index] );
886 var_FreeList( count, list, list2 );
888 break;
889 case ACTIONID_PROGRAM_SID_NEXT:
890 case ACTIONID_PROGRAM_SID_PREV:
891 if( p_input )
893 vlc_value_t val;
894 vlc_value_t *list;
895 char **list2;
896 size_t count, i;
897 var_Get( p_input, "program", &val );
899 var_Change( p_input, "program", VLC_VAR_GETCHOICES,
900 &count, &list, &list2 );
902 if( count <= 1 )
904 DisplayMessage( p_vout, _("Program Service ID: %s"),
905 _("N/A") );
906 var_FreeList( count, list, list2 );
907 break;
909 for( i = 0; i < count; i++ )
910 if( val.i_int == list[i].i_int )
911 break;
912 /* value of program sid was not in choices list */
913 if( i == count )
915 msg_Warn( p_input,
916 "invalid current program SID, selecting 0" );
917 i = 0;
919 else if( i_action == ACTIONID_PROGRAM_SID_NEXT ) {
920 if( i == count - 1 )
921 i = 0;
922 else
923 i++;
925 else { /* ACTIONID_PROGRAM_SID_PREV */
926 if( i == 0 )
927 i = count - 1;
928 else
929 i--;
931 var_Set( p_input, "program", list[i] );
932 DisplayMessage( p_vout, _("Program Service ID: %s"),
933 list2[i] );
934 var_FreeList( count, list, list2 );
936 break;
938 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
939 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
940 case ACTIONID_JUMP_BACKWARD_SHORT:
941 case ACTIONID_JUMP_FORWARD_SHORT:
942 case ACTIONID_JUMP_BACKWARD_MEDIUM:
943 case ACTIONID_JUMP_FORWARD_MEDIUM:
944 case ACTIONID_JUMP_BACKWARD_LONG:
945 case ACTIONID_JUMP_FORWARD_LONG:
947 if( p_input == NULL || !var_GetBool( p_input, "can-seek" ) )
948 break;
950 const char *varname;
951 int sign = +1;
952 switch( i_action )
954 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
955 sign = -1;
956 /* fall through */
957 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
958 varname = "extrashort-jump-size";
959 break;
960 case ACTIONID_JUMP_BACKWARD_SHORT:
961 sign = -1;
962 /* fall through */
963 case ACTIONID_JUMP_FORWARD_SHORT:
964 varname = "short-jump-size";
965 break;
966 case ACTIONID_JUMP_BACKWARD_MEDIUM:
967 sign = -1;
968 /* fall through */
969 case ACTIONID_JUMP_FORWARD_MEDIUM:
970 varname = "medium-jump-size";
971 break;
972 case ACTIONID_JUMP_BACKWARD_LONG:
973 sign = -1;
974 /* fall through */
975 case ACTIONID_JUMP_FORWARD_LONG:
976 varname = "long-jump-size";
977 break;
980 int it = var_InheritInteger( p_input, varname );
981 if( it < 0 )
982 break;
983 var_SetInteger( p_input, "time-offset", vlc_tick_from_sec( it * sign ) );
984 DisplayPosition( p_vout, slider_chan, p_input );
985 break;
988 /* Input navigation */
989 case ACTIONID_TITLE_PREV:
990 if( p_input )
991 var_TriggerCallback( p_input, "prev-title" );
992 break;
993 case ACTIONID_TITLE_NEXT:
994 if( p_input )
995 var_TriggerCallback( p_input, "next-title" );
996 break;
997 case ACTIONID_CHAPTER_PREV:
998 if( p_input )
999 var_TriggerCallback( p_input, "prev-chapter" );
1000 break;
1001 case ACTIONID_CHAPTER_NEXT:
1002 if( p_input )
1003 var_TriggerCallback( p_input, "next-chapter" );
1004 break;
1005 case ACTIONID_DISC_MENU:
1006 if( p_input )
1007 var_SetInteger( p_input, "title 0", 2 );
1008 break;
1009 case ACTIONID_NAV_ACTIVATE:
1010 if( p_input )
1011 input_Control( p_input, INPUT_NAV_ACTIVATE, NULL );
1012 break;
1013 case ACTIONID_NAV_UP:
1014 if( p_input )
1015 input_Control( p_input, INPUT_NAV_UP, NULL );
1016 break;
1017 case ACTIONID_NAV_DOWN:
1018 if( p_input )
1019 input_Control( p_input, INPUT_NAV_DOWN, NULL );
1020 break;
1021 case ACTIONID_NAV_LEFT:
1022 if( p_input )
1023 input_Control( p_input, INPUT_NAV_LEFT, NULL );
1024 break;
1025 case ACTIONID_NAV_RIGHT:
1026 if( p_input )
1027 input_Control( p_input, INPUT_NAV_RIGHT, NULL );
1028 break;
1030 /* Video Output actions */
1031 case ACTIONID_SNAPSHOT:
1032 if( p_vout )
1033 var_TriggerCallback( p_vout, "video-snapshot" );
1034 break;
1036 case ACTIONID_TOGGLE_FULLSCREEN:
1038 if( p_vout )
1040 bool fs = var_ToggleBool( p_vout, "fullscreen" );
1041 var_SetBool( p_playlist, "fullscreen", fs );
1043 else
1044 var_ToggleBool( p_playlist, "fullscreen" );
1045 break;
1048 case ACTIONID_LEAVE_FULLSCREEN:
1049 if( p_vout )
1050 var_SetBool( p_vout, "fullscreen", false );
1051 var_SetBool( p_playlist, "fullscreen", false );
1052 break;
1054 case ACTIONID_ASPECT_RATIO:
1055 if( p_vout )
1057 vlc_value_t val;
1058 vlc_value_t *val_list;
1059 char **text_list;
1060 size_t count;
1062 var_Get( p_vout, "aspect-ratio", &val );
1063 if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETCHOICES,
1064 &count, &val_list, &text_list ) >= 0 )
1066 size_t i;
1067 for( i = 0; i < count; i++ )
1069 if( !strcmp( val_list[i].psz_string, val.psz_string ) )
1071 i++;
1072 break;
1075 if( i == count ) i = 0;
1076 var_SetString( p_vout, "aspect-ratio",
1077 val_list[i].psz_string );
1078 DisplayMessage( p_vout, _("Aspect ratio: %s"),
1079 text_list[i] );
1081 var_FreeStringList( count, val_list, text_list );
1083 free( val.psz_string );
1085 break;
1087 case ACTIONID_CROP:
1088 if( p_vout )
1090 vlc_value_t val;
1091 vlc_value_t *val_list;
1092 char **text_list;
1093 size_t count;
1095 var_Get( p_vout, "crop", &val );
1096 if( var_Change( p_vout, "crop", VLC_VAR_GETCHOICES,
1097 &count, &val_list, &text_list ) >= 0 )
1099 size_t i;
1100 for( i = 0; i < count; i++ )
1102 if( !strcmp( val_list[i].psz_string, val.psz_string ) )
1104 i++;
1105 break;
1108 if( i == count ) i = 0;
1109 var_SetString( p_vout, "crop", val_list[i].psz_string );
1110 DisplayMessage( p_vout, _("Crop: %s"), text_list[i] );
1112 var_FreeStringList( count, val_list, text_list );
1114 free( val.psz_string );
1116 break;
1117 case ACTIONID_CROP_TOP:
1118 if( p_vout )
1119 var_IncInteger( p_vout, "crop-top" );
1120 break;
1121 case ACTIONID_UNCROP_TOP:
1122 if( p_vout )
1123 var_DecInteger( p_vout, "crop-top" );
1124 break;
1125 case ACTIONID_CROP_BOTTOM:
1126 if( p_vout )
1127 var_IncInteger( p_vout, "crop-bottom" );
1128 break;
1129 case ACTIONID_UNCROP_BOTTOM:
1130 if( p_vout )
1131 var_DecInteger( p_vout, "crop-bottom" );
1132 break;
1133 case ACTIONID_CROP_LEFT:
1134 if( p_vout )
1135 var_IncInteger( p_vout, "crop-left" );
1136 break;
1137 case ACTIONID_UNCROP_LEFT:
1138 if( p_vout )
1139 var_DecInteger( p_vout, "crop-left" );
1140 break;
1141 case ACTIONID_CROP_RIGHT:
1142 if( p_vout )
1143 var_IncInteger( p_vout, "crop-right" );
1144 break;
1145 case ACTIONID_UNCROP_RIGHT:
1146 if( p_vout )
1147 var_DecInteger( p_vout, "crop-right" );
1148 break;
1150 case ACTIONID_VIEWPOINT_FOV_IN:
1151 if( p_vout )
1152 input_UpdateViewpoint( p_input,
1153 &(vlc_viewpoint_t) { .fov = -1.f },
1154 false );
1155 break;
1156 case ACTIONID_VIEWPOINT_FOV_OUT:
1157 if( p_vout )
1158 input_UpdateViewpoint( p_input,
1159 &(vlc_viewpoint_t) { .fov = 1.f },
1160 false );
1161 break;
1163 case ACTIONID_VIEWPOINT_ROLL_CLOCK:
1164 if( p_vout )
1165 input_UpdateViewpoint( p_input,
1166 &(vlc_viewpoint_t) { .roll = -1.f },
1167 false );
1168 break;
1169 case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
1170 if( p_vout )
1171 input_UpdateViewpoint( p_input,
1172 &(vlc_viewpoint_t) { .roll = 1.f },
1173 false );
1174 break;
1176 case ACTIONID_TOGGLE_AUTOSCALE:
1177 if( p_vout )
1179 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1180 if ( f_scalefactor != 1.f )
1182 var_SetFloat( p_vout, "zoom", 1.f );
1183 DisplayMessage( p_vout, _("Zooming reset") );
1185 else
1187 bool b_autoscale = !var_GetBool( p_vout, "autoscale" );
1188 var_SetBool( p_vout, "autoscale", b_autoscale );
1189 if( b_autoscale )
1190 DisplayMessage( p_vout, _("Scaled to screen") );
1191 else
1192 DisplayMessage( p_vout, _("Original Size") );
1195 break;
1196 case ACTIONID_SCALE_UP:
1197 if( p_vout )
1199 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1201 if( f_scalefactor < 10.f )
1202 f_scalefactor += .1f;
1203 var_SetFloat( p_vout, "zoom", f_scalefactor );
1205 break;
1206 case ACTIONID_SCALE_DOWN:
1207 if( p_vout )
1209 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1211 if( f_scalefactor > .3f )
1212 f_scalefactor -= .1f;
1213 var_SetFloat( p_vout, "zoom", f_scalefactor );
1215 break;
1217 case ACTIONID_ZOOM_QUARTER:
1218 case ACTIONID_ZOOM_HALF:
1219 case ACTIONID_ZOOM_ORIGINAL:
1220 case ACTIONID_ZOOM_DOUBLE:
1221 if( p_vout )
1223 float f;
1224 switch( i_action )
1226 case ACTIONID_ZOOM_QUARTER: f = 0.25; break;
1227 case ACTIONID_ZOOM_HALF: f = 0.5; break;
1228 case ACTIONID_ZOOM_ORIGINAL: f = 1.; break;
1229 /*case ACTIONID_ZOOM_DOUBLE:*/
1230 default: f = 2.; break;
1232 var_SetFloat( p_vout, "zoom", f );
1234 break;
1235 case ACTIONID_ZOOM:
1236 case ACTIONID_UNZOOM:
1237 if( p_vout )
1239 vlc_value_t val;
1240 vlc_value_t *val_list;
1241 char **text_list;
1242 size_t count;
1244 var_Get( p_vout, "zoom", &val );
1245 if( var_Change( p_vout, "zoom", VLC_VAR_GETCHOICES,
1246 &count, &val_list, &text_list ) >= 0 )
1248 size_t i;
1249 for( i = 0; i < count; i++ )
1251 if( val_list[i].f_float == val.f_float )
1253 if( i_action == ACTIONID_ZOOM )
1254 i++;
1255 else /* ACTIONID_UNZOOM */
1256 i--;
1257 break;
1260 if( i == count ) i = 0;
1261 if( i == (size_t)-1 ) i = count-1;
1262 var_SetFloat( p_vout, "zoom", val_list[i].f_float );
1263 DisplayMessage( p_vout, _("Zoom mode: %s"), text_list[i] );
1265 var_FreeList( count, val_list, text_list );
1268 break;
1270 case ACTIONID_DEINTERLACE:
1271 if( p_vout )
1273 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1274 if( i_deinterlace != 0 )
1276 var_SetInteger( p_vout, "deinterlace", 0 );
1277 DisplayMessage( p_vout, _("Deinterlace off") );
1279 else
1281 var_SetInteger( p_vout, "deinterlace", 1 );
1283 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
1284 vlc_value_t *vlist;
1285 char **tlist;
1286 size_t count;
1288 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &count, &vlist, &tlist ) )
1290 const char *psz_text = NULL;
1291 for( size_t i = 0; i < count; i++ )
1293 if( !strcmp( vlist[i].psz_string, psz_mode ) )
1295 psz_text = tlist[i];
1296 break;
1299 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
1300 psz_text ? psz_text : psz_mode );
1302 var_FreeStringList( count, vlist, tlist );
1304 free( psz_mode );
1307 break;
1308 case ACTIONID_DEINTERLACE_MODE:
1309 if( p_vout )
1311 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
1312 vlc_value_t *vlist;
1313 char **tlist;
1314 size_t count;
1316 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &count, &vlist, &tlist ))
1318 const char *psz_text = NULL;
1319 size_t i;
1321 for( i = 0; i < count; i++ )
1323 if( !strcmp( vlist[i].psz_string, psz_mode ) )
1325 i++;
1326 break;
1329 if( i == count ) i = 0;
1330 psz_text = tlist[i];
1331 var_SetString( p_vout, "deinterlace-mode", vlist[i].psz_string );
1333 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1334 if( i_deinterlace != 0 )
1336 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
1337 psz_text ? psz_text : psz_mode );
1339 else
1341 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace off"),
1342 psz_text ? psz_text : psz_mode );
1345 var_FreeStringList( count, vlist, tlist );
1347 free( psz_mode );
1349 break;
1351 case ACTIONID_SUBPOS_DOWN:
1352 case ACTIONID_SUBPOS_UP:
1354 if( p_input )
1356 vlc_value_t val;
1357 vlc_value_t *list;
1358 size_t count;
1360 var_Get( p_input, "spu-es", &val );
1362 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
1363 &count, &list, (char ***)NULL );
1364 if( count < 1 || val.i_int < 0 )
1366 DisplayMessage( p_vout,
1367 _("Subtitle position: no active subtitle") );
1368 free(list);
1369 break;
1372 int i_pos;
1373 if( i_action == ACTIONID_SUBPOS_DOWN )
1374 i_pos = var_DecInteger( p_vout, "sub-margin" );
1375 else
1376 i_pos = var_IncInteger( p_vout, "sub-margin" );
1378 ClearChannels( p_vout, slider_chan );
1379 DisplayMessage( p_vout, _( "Subtitle position %d px" ), i_pos );
1380 free(list);
1382 break;
1385 case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN:
1386 case ACTIONID_SUBTITLE_TEXT_SCALE_UP:
1387 case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL:
1388 if( p_vout )
1390 int i_scale;
1391 if( i_action == ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL )
1393 i_scale = 100;
1395 else
1397 i_scale = var_GetInteger( p_playlist, "sub-text-scale" );
1398 unsigned increment = ((i_scale > 100 ? i_scale - 100 : 100 - i_scale) / 25) <= 1 ? 10 : 25;
1399 i_scale += ((i_action == ACTIONID_SUBTITLE_TEXT_SCALE_UP) ? 1 : -1) * increment;
1400 i_scale -= i_scale % increment;
1401 i_scale = VLC_CLIP( i_scale, 25, 500 );
1403 var_SetInteger( p_playlist, "sub-text-scale", i_scale );
1404 DisplayMessage( p_vout, _( "Subtitle text scale %d%%" ), i_scale );
1406 break;
1408 /* Input + video output */
1409 case ACTIONID_POSITION:
1410 if( p_vout && vout_OSDEpg( p_vout, input_GetItem( p_input ) ) )
1411 DisplayPosition( p_vout, slider_chan, p_input );
1412 break;
1414 case ACTIONID_COMBO_VOL_FOV_UP:
1415 if( b_vrnav )
1416 DO_ACTION( ACTIONID_VIEWPOINT_FOV_IN );
1417 else
1418 DO_ACTION( ACTIONID_VOL_UP );
1419 break;
1420 case ACTIONID_COMBO_VOL_FOV_DOWN:
1421 if( b_vrnav )
1422 DO_ACTION( ACTIONID_VIEWPOINT_FOV_OUT );
1423 else
1424 DO_ACTION( ACTIONID_VOL_DOWN );
1425 break;
1428 return VLC_SUCCESS;
1431 /*****************************************************************************
1432 * ActionEvent: callback for hotkey actions
1433 *****************************************************************************/
1434 static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
1435 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1437 intf_thread_t *p_intf = (intf_thread_t *)p_data;
1438 intf_sys_t *p_sys = p_intf->p_sys;
1440 (void)libvlc;
1441 (void)psz_var;
1442 (void)oldval;
1444 vlc_mutex_lock( &p_intf->p_sys->lock );
1445 input_thread_t *p_input = p_sys->p_input ? vlc_object_hold( p_sys->p_input )
1446 : NULL;
1447 vout_thread_t *p_vout = p_sys->p_vout ? vlc_object_hold( p_sys->p_vout )
1448 : NULL;
1449 int slider_chan = p_sys->slider_chan;
1450 bool b_vrnav = p_sys->vrnav.b_can_change;
1451 vlc_mutex_unlock( &p_intf->p_sys->lock );
1453 int i_ret = PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav,
1454 newval.i_int );
1456 if( p_input != NULL )
1457 vlc_object_release( p_input );
1458 if( p_vout != NULL )
1459 vlc_object_release( p_vout );
1461 return i_ret;
1464 static void PlayBookmark( intf_thread_t *p_intf, int i_num )
1466 char *psz_bookmark_name;
1467 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
1468 return;
1470 playlist_t *p_playlist = pl_Get( p_intf );
1471 char *psz_bookmark = var_CreateGetString( p_intf, psz_bookmark_name );
1473 PL_LOCK;
1474 playlist_item_t *p_item;
1475 ARRAY_FOREACH( p_item, p_playlist->items )
1477 char *psz_uri = input_item_GetURI( p_item->p_input );
1478 if( !strcmp( psz_bookmark, psz_uri ) )
1480 free( psz_uri );
1481 playlist_ViewPlay( p_playlist, NULL, p_item );
1482 break;
1484 else
1485 free( psz_uri );
1487 PL_UNLOCK;
1489 free( psz_bookmark );
1490 free( psz_bookmark_name );
1493 static void SetBookmark( intf_thread_t *p_intf, int i_num )
1495 char *psz_bookmark_name;
1496 char *psz_uri = NULL;
1497 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
1498 return;
1500 playlist_t *p_playlist = pl_Get( p_intf );
1501 var_Create( p_intf, psz_bookmark_name,
1502 VLC_VAR_STRING|VLC_VAR_DOINHERIT );
1504 PL_LOCK;
1505 playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
1506 if( p_item ) psz_uri = input_item_GetURI( p_item->p_input );
1507 PL_UNLOCK;
1509 if( p_item )
1511 config_PutPsz( psz_bookmark_name, psz_uri);
1512 msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num, psz_uri);
1515 free( psz_uri );
1516 free( psz_bookmark_name );
1519 static void DisplayPosition( vout_thread_t *p_vout, int slider_chan,
1520 input_thread_t *p_input )
1522 char psz_duration[MSTRTIME_MAX_SIZE];
1523 char psz_time[MSTRTIME_MAX_SIZE];
1525 if( p_vout == NULL ) return;
1527 ClearChannels( p_vout, slider_chan );
1529 int64_t t = SEC_FROM_VLC_TICK(var_GetInteger( p_input, "time" ));
1530 int64_t l = SEC_FROM_VLC_TICK(var_GetInteger( p_input, "length" ));
1532 secstotimestr( psz_time, t );
1534 if( l > 0 )
1536 secstotimestr( psz_duration, l );
1537 DisplayMessage( p_vout, "%s / %s", psz_time, psz_duration );
1539 else if( t > 0 )
1541 DisplayMessage( p_vout, "%s", psz_time );
1544 if( var_GetBool( p_vout, "fullscreen" ) )
1546 vlc_value_t pos;
1547 var_Get( p_input, "position", &pos );
1548 vout_OSDSlider( p_vout, slider_chan,
1549 pos.f_float * 100, OSD_HOR_SLIDER );
1553 static void DisplayVolume( vout_thread_t *p_vout, int slider_chan, float vol )
1555 if( p_vout == NULL )
1556 return;
1557 ClearChannels( p_vout, slider_chan );
1559 if( var_GetBool( p_vout, "fullscreen" ) )
1560 vout_OSDSlider( p_vout, slider_chan,
1561 lroundf(vol * 100.f), OSD_VERT_SLIDER );
1562 DisplayMessage( p_vout, _( "Volume %ld%%" ), lroundf(vol * 100.f) );
1565 static void DisplayRate( vout_thread_t *p_vout, float f_rate )
1567 DisplayMessage( p_vout, _("Speed: %.2fx"), (double) f_rate );
1570 static float AdjustRateFine( vlc_object_t *p_obj, const int i_dir )
1572 const float f_rate_min = INPUT_RATE_MIN;
1573 const float f_rate_max = INPUT_RATE_MAX;
1574 float f_rate = var_GetFloat( p_obj, "rate" );
1576 int i_sign = f_rate < 0 ? -1 : 1;
1578 f_rate = floor( fabs(f_rate) / 0.1 + i_dir + 0.05 ) * 0.1;
1580 if( f_rate < f_rate_min )
1581 f_rate = f_rate_min;
1582 else if( f_rate > f_rate_max )
1583 f_rate = f_rate_max;
1584 f_rate *= i_sign;
1586 return f_rate;
1589 static void ClearChannels( vout_thread_t *p_vout, int slider_chan )
1591 if( p_vout )
1593 vout_FlushSubpictureChannel( p_vout, VOUT_SPU_CHANNEL_OSD );
1594 vout_FlushSubpictureChannel( p_vout, slider_chan );