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 /*****************************************************************************
26 *****************************************************************************/
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>
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>
47 /*****************************************************************************
48 * intf_sys_t: description and status of FB interface
49 *****************************************************************************/
53 vout_thread_t
*p_vout
;
54 input_thread_t
*p_input
;
57 /*subtitle_delaybookmarks: placeholder for storing subtitle sync timestamps*/
60 vlc_tick_t i_time_subtitle
;
61 vlc_tick_t i_time_audio
;
62 } subtitle_delaybookmarks
;
67 bool b_button_pressed
;
72 /*****************************************************************************
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, ...) \
90 vout_OSDMessage(vout, VOUT_SPU_CHANNEL_OSD, __VA_ARGS__); \
92 #define DisplayIcon(vout, icon) \
93 do { if(vout) vout_OSDIcon(vout, VOUT_SPU_CHANNEL_OSD, icon); } while(0)
95 /*****************************************************************************
97 *****************************************************************************/
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
)
109 static void var_FreeList( size_t n
, vlc_value_t
*values
, char **texts
)
113 for( size_t i
= 0; i
< n
; i
++ )
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
;
153 static int ViewpointMovedEvent( vlc_object_t
*p_this
, char const *psz_var
,
154 vlc_value_t oldval
, vlc_value_t newval
,
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 );
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
;
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
);
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");
194 if (pressed
& (1 << MOUSE_BUTTON_RIGHT
))
196 if ((oldval
.i_int
& (1 << MOUSE_BUTTON_RIGHT
))
197 && !(newval
.i_int
& (1 << MOUSE_BUTTON_RIGHT
)))
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
);
209 static void ChangeVout( intf_thread_t
*p_intf
, vout_thread_t
*p_vout
)
211 intf_sys_t
*p_sys
= p_intf
->p_sys
;
214 bool b_vrnav_can_change
;
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
,
226 vlc_mutex_lock( &p_sys
->lock
);
227 p_sys
->p_vout
= p_vout
;
230 p_sys
->slider_chan
= slider_chan
;
231 p_sys
->vrnav
.b_can_change
= b_vrnav_can_change
;
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
,
241 var_DelCallback( p_old_vout
, "mouse-moved", MovedEvent
, p_intf
);
242 vlc_object_release( p_old_vout
);
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
) );
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
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
,
293 var_DelCallback( p_old_vout
, "mouse-moved", MovedEvent
,
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
);
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
;
337 p_sys
= malloc( sizeof( intf_sys_t
) );
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
);
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 */
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
,
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
);
390 /* Libvlc / interface actions */
392 libvlc_Quit( p_intf
->obj
.libvlc
);
394 ClearChannels( p_vout
, slider_chan
);
395 DisplayMessage( p_vout
, _( "Quit" ) );
398 case ACTIONID_INTF_TOGGLE_FSC
:
399 case ACTIONID_INTF_HIDE
:
400 var_TriggerCallback( p_playlist
, "intf-toggle-fscontrol" );
402 case ACTIONID_INTF_BOSS
:
403 var_TriggerCallback( p_playlist
, "intf-boss" );
405 case ACTIONID_INTF_POPUP_MENU
:
406 var_TriggerCallback( p_playlist
, "intf-popupmenu" );
409 /* Playlist actions (including audio) */
412 /* Toggle Normal -> Loop -> Repeat -> Normal ... */
414 if( var_GetBool( p_playlist
, "repeat" ) )
416 var_SetBool( p_playlist
, "repeat", false );
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 );
428 var_SetBool( p_playlist
, "loop", true );
431 DisplayMessage( p_vout
, _("Loop: %s"), vlc_gettext(mode
) );
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") ) );
444 DisplayMessage( p_vout
, _("Next") );
445 playlist_Next( p_playlist
);
448 DisplayMessage( p_vout
, _("Previous") );
449 playlist_Prev( p_playlist
);
453 playlist_Stop( p_playlist
);
456 case ACTIONID_RATE_NORMAL
:
457 var_SetFloat( p_playlist
, "rate", 1.f
);
458 DisplayRate( p_vout
, 1.f
);
460 case ACTIONID_FASTER
:
461 var_TriggerCallback( p_playlist
, "rate-faster" );
462 DisplayRate( p_vout
, var_GetFloat( p_playlist
, "rate" ) );
464 case ACTIONID_SLOWER
:
465 var_TriggerCallback( p_playlist
, "rate-slower" );
466 DisplayRate( p_vout
, var_GetFloat( p_playlist
, "rate" ) );
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
);
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 );
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 );
504 case ACTIONID_PLAY_CLEAR
:
505 playlist_Clear( p_playlist
, pl_Unlocked
);
507 case ACTIONID_VOL_UP
:
510 if( playlist_VolumeUp( p_playlist
, 1, &vol
) == 0 )
511 DisplayVolume( p_vout
, slider_chan
, vol
);
514 case ACTIONID_VOL_DOWN
:
517 if( playlist_VolumeDown( p_playlist
, 1, &vol
) == 0 )
518 DisplayVolume( p_vout
, slider_chan
, vol
);
521 case ACTIONID_VOL_MUTE
:
523 int mute
= playlist_MuteGet( p_playlist
);
527 if( playlist_MuteSet( p_playlist
, mute
) )
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
);
537 DisplayVolume( p_vout
, slider_chan
, vol
);
541 case ACTIONID_AUDIODEVICE_CYCLE
:
543 audio_output_t
*p_aout
= playlist_GetAout( p_playlist
);
548 int n
= aout_DevicesList( p_aout
, &ids
, &names
);
552 char *dev
= aout_DeviceGet( p_aout
);
553 const char *devstr
= (dev
!= NULL
) ? dev
: "";
556 for( int i
= 0; i
< n
; i
++ )
558 if( !strcmp(devstr
, ids
[i
]) )
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
++ )
577 /* Playlist + input actions */
578 case ACTIONID_PLAY_PAUSE
:
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
);
590 if( p_input
&& var_GetFloat( p_input
, "rate" ) != 1.f
)
591 /* Return to normal speed */
592 var_SetFloat( p_input
, "rate", 1.f
);
595 ClearChannels( p_vout
, slider_chan
);
596 DisplayIcon( p_vout
, OSD_PLAY_ICON
);
597 playlist_Play( p_playlist
);
601 /* Playlist + video output actions */
602 case ACTIONID_WALLPAPER
:
604 bool wp
= var_ToggleBool( p_playlist
, "video-wallpaper" );
606 var_SetBool( p_vout
, "video-wallpaper", wp
);
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
);
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")) );
629 case ACTIONID_FRAME_NEXT
:
632 var_TriggerCallback( p_input
, "frame-next" );
633 DisplayMessage( p_vout
, _("Next frame") );
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"));
643 case ACTIONID_SUBSYNC_MARKSUB
:
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") );
660 p_sys
->subtitle_delaybookmarks
.i_time_subtitle
= vlc_tick_now();
661 DisplayMessage(p_vout
,
662 _("Sub sync: bookmarked subtitle time"));
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)
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) */
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!" ) );
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
;
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
;
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);
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") );
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
) );
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);
747 vlc_tick_t i_delay
= var_GetInteger( p_input
, "audio-delay" )
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
) );
758 case ACTIONID_AUDIO_TRACK
:
766 var_Get( p_input
, "audio-es", &val
);
767 var_Change( p_input
, "audio-es", VLC_VAR_GETCHOICES
,
768 &count
, &list
, &list2
);
774 for( i
= 0; i
< count
; i
++ )
775 if( val
.i_int
== list
[i
].i_int
)
777 /* value of audio-es was not in choices list */
781 "invalid current audio track, selecting 0" );
784 else if( i
== count
- 1 )
788 var_Set( p_input
, "audio-es", list
[i
] );
789 DisplayMessage( p_vout
, _("Audio track: %s"), list2
[i
] );
791 var_FreeList( count
, list
, list2
);
795 case ACTIONID_SUBTITLE_TRACK
:
796 case ACTIONID_SUBTITLE_REVERSE_TRACK
:
803 var_Get( p_input
, "spu-es", &val
);
805 var_Change( p_input
, "spu-es", VLC_VAR_GETCHOICES
,
806 &count
, &list
, &list2
);
810 DisplayMessage( p_vout
, _("Subtitle track: %s"),
812 var_FreeList( count
, list
, list2
);
815 for( i
= 0; i
< count
; i
++ )
816 if( val
.i_int
== list
[i
].i_int
)
818 /* value of spu-es was not in choices list */
822 "invalid current subtitle track, selecting 0" );
825 else if ((i
== count
- 1) && (i_action
== ACTIONID_SUBTITLE_TRACK
))
827 else if ((i
== 0) && (i_action
== ACTIONID_SUBTITLE_REVERSE_TRACK
))
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
);
836 case ACTIONID_SUBTITLE_TOGGLE
:
843 var_Change( p_input
, "spu-es", VLC_VAR_GETCHOICES
,
844 &count
, &list
, &list2
);
848 DisplayMessage( p_vout
, _("Subtitle track: %s"),
850 var_FreeList( count
, list
, list2
);
854 int i_cur_id
= var_GetInteger( p_input
, "spu-es" );
858 /* subtitles were disabled: restore the saved track id */
859 i_new_id
= var_GetInteger( p_input
, "spu-choice" );
861 var_SetInteger( p_input
, "spu-choice", -1 );
865 /* subtitles were enabled: save the track id and disable */
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
)
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
);
889 case ACTIONID_PROGRAM_SID_NEXT
:
890 case ACTIONID_PROGRAM_SID_PREV
:
897 var_Get( p_input
, "program", &val
);
899 var_Change( p_input
, "program", VLC_VAR_GETCHOICES
,
900 &count
, &list
, &list2
);
904 DisplayMessage( p_vout
, _("Program Service ID: %s"),
906 var_FreeList( count
, list
, list2
);
909 for( i
= 0; i
< count
; i
++ )
910 if( val
.i_int
== list
[i
].i_int
)
912 /* value of program sid was not in choices list */
916 "invalid current program SID, selecting 0" );
919 else if( i_action
== ACTIONID_PROGRAM_SID_NEXT
) {
925 else { /* ACTIONID_PROGRAM_SID_PREV */
931 var_Set( p_input
, "program", list
[i
] );
932 DisplayMessage( p_vout
, _("Program Service ID: %s"),
934 var_FreeList( count
, list
, list2
);
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" ) )
954 case ACTIONID_JUMP_BACKWARD_EXTRASHORT
:
957 case ACTIONID_JUMP_FORWARD_EXTRASHORT
:
958 varname
= "extrashort-jump-size";
960 case ACTIONID_JUMP_BACKWARD_SHORT
:
963 case ACTIONID_JUMP_FORWARD_SHORT
:
964 varname
= "short-jump-size";
966 case ACTIONID_JUMP_BACKWARD_MEDIUM
:
969 case ACTIONID_JUMP_FORWARD_MEDIUM
:
970 varname
= "medium-jump-size";
972 case ACTIONID_JUMP_BACKWARD_LONG
:
975 case ACTIONID_JUMP_FORWARD_LONG
:
976 varname
= "long-jump-size";
980 int it
= var_InheritInteger( p_input
, varname
);
983 var_SetInteger( p_input
, "time-offset", vlc_tick_from_sec( it
* sign
) );
984 DisplayPosition( p_vout
, slider_chan
, p_input
);
988 /* Input navigation */
989 case ACTIONID_TITLE_PREV
:
991 var_TriggerCallback( p_input
, "prev-title" );
993 case ACTIONID_TITLE_NEXT
:
995 var_TriggerCallback( p_input
, "next-title" );
997 case ACTIONID_CHAPTER_PREV
:
999 var_TriggerCallback( p_input
, "prev-chapter" );
1001 case ACTIONID_CHAPTER_NEXT
:
1003 var_TriggerCallback( p_input
, "next-chapter" );
1005 case ACTIONID_DISC_MENU
:
1007 var_SetInteger( p_input
, "title 0", 2 );
1009 case ACTIONID_NAV_ACTIVATE
:
1011 input_Control( p_input
, INPUT_NAV_ACTIVATE
, NULL
);
1013 case ACTIONID_NAV_UP
:
1015 input_Control( p_input
, INPUT_NAV_UP
, NULL
);
1017 case ACTIONID_NAV_DOWN
:
1019 input_Control( p_input
, INPUT_NAV_DOWN
, NULL
);
1021 case ACTIONID_NAV_LEFT
:
1023 input_Control( p_input
, INPUT_NAV_LEFT
, NULL
);
1025 case ACTIONID_NAV_RIGHT
:
1027 input_Control( p_input
, INPUT_NAV_RIGHT
, NULL
);
1030 /* Video Output actions */
1031 case ACTIONID_SNAPSHOT
:
1033 var_TriggerCallback( p_vout
, "video-snapshot" );
1036 case ACTIONID_TOGGLE_FULLSCREEN
:
1040 bool fs
= var_ToggleBool( p_vout
, "fullscreen" );
1041 var_SetBool( p_playlist
, "fullscreen", fs
);
1044 var_ToggleBool( p_playlist
, "fullscreen" );
1048 case ACTIONID_LEAVE_FULLSCREEN
:
1050 var_SetBool( p_vout
, "fullscreen", false );
1051 var_SetBool( p_playlist
, "fullscreen", false );
1054 case ACTIONID_ASPECT_RATIO
:
1058 vlc_value_t
*val_list
;
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 )
1067 for( i
= 0; i
< count
; i
++ )
1069 if( !strcmp( val_list
[i
].psz_string
, val
.psz_string
) )
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"),
1081 var_FreeStringList( count
, val_list
, text_list
);
1083 free( val
.psz_string
);
1091 vlc_value_t
*val_list
;
1095 var_Get( p_vout
, "crop", &val
);
1096 if( var_Change( p_vout
, "crop", VLC_VAR_GETCHOICES
,
1097 &count
, &val_list
, &text_list
) >= 0 )
1100 for( i
= 0; i
< count
; i
++ )
1102 if( !strcmp( val_list
[i
].psz_string
, val
.psz_string
) )
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
);
1117 case ACTIONID_CROP_TOP
:
1119 var_IncInteger( p_vout
, "crop-top" );
1121 case ACTIONID_UNCROP_TOP
:
1123 var_DecInteger( p_vout
, "crop-top" );
1125 case ACTIONID_CROP_BOTTOM
:
1127 var_IncInteger( p_vout
, "crop-bottom" );
1129 case ACTIONID_UNCROP_BOTTOM
:
1131 var_DecInteger( p_vout
, "crop-bottom" );
1133 case ACTIONID_CROP_LEFT
:
1135 var_IncInteger( p_vout
, "crop-left" );
1137 case ACTIONID_UNCROP_LEFT
:
1139 var_DecInteger( p_vout
, "crop-left" );
1141 case ACTIONID_CROP_RIGHT
:
1143 var_IncInteger( p_vout
, "crop-right" );
1145 case ACTIONID_UNCROP_RIGHT
:
1147 var_DecInteger( p_vout
, "crop-right" );
1150 case ACTIONID_VIEWPOINT_FOV_IN
:
1152 input_UpdateViewpoint( p_input
,
1153 &(vlc_viewpoint_t
) { .fov
= -1.f
},
1156 case ACTIONID_VIEWPOINT_FOV_OUT
:
1158 input_UpdateViewpoint( p_input
,
1159 &(vlc_viewpoint_t
) { .fov
= 1.f
},
1163 case ACTIONID_VIEWPOINT_ROLL_CLOCK
:
1165 input_UpdateViewpoint( p_input
,
1166 &(vlc_viewpoint_t
) { .roll
= -1.f
},
1169 case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK
:
1171 input_UpdateViewpoint( p_input
,
1172 &(vlc_viewpoint_t
) { .roll
= 1.f
},
1176 case ACTIONID_TOGGLE_AUTOSCALE
:
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") );
1187 bool b_autoscale
= !var_GetBool( p_vout
, "autoscale" );
1188 var_SetBool( p_vout
, "autoscale", b_autoscale
);
1190 DisplayMessage( p_vout
, _("Scaled to screen") );
1192 DisplayMessage( p_vout
, _("Original Size") );
1196 case ACTIONID_SCALE_UP
:
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
);
1206 case ACTIONID_SCALE_DOWN
:
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
);
1217 case ACTIONID_ZOOM_QUARTER
:
1218 case ACTIONID_ZOOM_HALF
:
1219 case ACTIONID_ZOOM_ORIGINAL
:
1220 case ACTIONID_ZOOM_DOUBLE
:
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
);
1236 case ACTIONID_UNZOOM
:
1240 vlc_value_t
*val_list
;
1244 var_Get( p_vout
, "zoom", &val
);
1245 if( var_Change( p_vout
, "zoom", VLC_VAR_GETCHOICES
,
1246 &count
, &val_list
, &text_list
) >= 0 )
1249 for( i
= 0; i
< count
; i
++ )
1251 if( val_list
[i
].f_float
== val
.f_float
)
1253 if( i_action
== ACTIONID_ZOOM
)
1255 else /* ACTIONID_UNZOOM */
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
);
1270 case ACTIONID_DEINTERLACE
:
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") );
1281 var_SetInteger( p_vout
, "deinterlace", 1 );
1283 char *psz_mode
= var_GetString( p_vout
, "deinterlace-mode" );
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
];
1299 DisplayMessage( p_vout
, "%s (%s)", _("Deinterlace on"),
1300 psz_text
? psz_text
: psz_mode
);
1302 var_FreeStringList( count
, vlist
, tlist
);
1308 case ACTIONID_DEINTERLACE_MODE
:
1311 char *psz_mode
= var_GetString( p_vout
, "deinterlace-mode" );
1316 if( psz_mode
&& !var_Change( p_vout
, "deinterlace-mode", VLC_VAR_GETCHOICES
, &count
, &vlist
, &tlist
))
1318 const char *psz_text
= NULL
;
1321 for( i
= 0; i
< count
; i
++ )
1323 if( !strcmp( vlist
[i
].psz_string
, psz_mode
) )
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
);
1341 DisplayMessage( p_vout
, "%s (%s)", _("Deinterlace off"),
1342 psz_text
? psz_text
: psz_mode
);
1345 var_FreeStringList( count
, vlist
, tlist
);
1351 case ACTIONID_SUBPOS_DOWN
:
1352 case ACTIONID_SUBPOS_UP
:
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") );
1373 if( i_action
== ACTIONID_SUBPOS_DOWN
)
1374 i_pos
= var_DecInteger( p_vout
, "sub-margin" );
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
);
1385 case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN
:
1386 case ACTIONID_SUBTITLE_TEXT_SCALE_UP
:
1387 case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL
:
1391 if( i_action
== ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL
)
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
);
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
);
1414 case ACTIONID_COMBO_VOL_FOV_UP
:
1416 DO_ACTION( ACTIONID_VIEWPOINT_FOV_IN
);
1418 DO_ACTION( ACTIONID_VOL_UP
);
1420 case ACTIONID_COMBO_VOL_FOV_DOWN
:
1422 DO_ACTION( ACTIONID_VIEWPOINT_FOV_OUT
);
1424 DO_ACTION( ACTIONID_VOL_DOWN
);
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
;
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
)
1447 vout_thread_t
*p_vout
= p_sys
->p_vout
? vlc_object_hold( p_sys
->p_vout
)
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
,
1456 if( p_input
!= NULL
)
1457 vlc_object_release( p_input
);
1458 if( p_vout
!= NULL
)
1459 vlc_object_release( p_vout
);
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 )
1470 playlist_t
*p_playlist
= pl_Get( p_intf
);
1471 char *psz_bookmark
= var_CreateGetString( p_intf
, psz_bookmark_name
);
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
) )
1481 playlist_ViewPlay( p_playlist
, NULL
, p_item
);
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 )
1500 playlist_t
*p_playlist
= pl_Get( p_intf
);
1501 var_Create( p_intf
, psz_bookmark_name
,
1502 VLC_VAR_STRING
|VLC_VAR_DOINHERIT
);
1505 playlist_item_t
* p_item
= playlist_CurrentPlayingItem( p_playlist
);
1506 if( p_item
) psz_uri
= input_item_GetURI( p_item
->p_input
);
1511 config_PutPsz( psz_bookmark_name
, psz_uri
);
1512 msg_Info( p_intf
, "setting playlist bookmark %i to %s", i_num
, 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
);
1536 secstotimestr( psz_duration
, l
);
1537 DisplayMessage( p_vout
, "%s / %s", psz_time
, psz_duration
);
1541 DisplayMessage( p_vout
, "%s", psz_time
);
1544 if( var_GetBool( p_vout
, "fullscreen" ) )
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
)
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
;
1589 static void ClearChannels( vout_thread_t
*p_vout
, int slider_chan
)
1593 vout_FlushSubpictureChannel( p_vout
, VOUT_SPU_CHANNEL_OSD
);
1594 vout_FlushSubpictureChannel( p_vout
, slider_chan
);