1 /*****************************************************************************
2 * hotkeys.c: Hotkey handling for vlc
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
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 /*****************************************************************************
27 *****************************************************************************/
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>
40 #include <vlc_vout_osd.h>
41 #include <vlc_playlist.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 int64_t i_time_subtitle
;
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, SPU_DEFAULT_CHANNEL, __VA_ARGS__); \
92 #define DisplayIcon(vout, icon) \
93 do { if(vout) vout_OSDIcon(vout, SPU_DEFAULT_CHANNEL, 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 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
;
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
);
156 p_sys
->vrnav
.b_button_pressed
= false;
161 static void ChangeVout( intf_thread_t
*p_intf
, vout_thread_t
*p_vout
)
163 intf_sys_t
*p_sys
= p_intf
->p_sys
;
166 bool b_vrnav_can_change
;
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
;
179 p_sys
->slider_chan
= slider_chan
;
180 p_sys
->vrnav
.b_can_change
= b_vrnav_can_change
;
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
,
192 var_DelCallback( p_old_vout
, "mouse-button-down", ButtonEvent
,
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
,
203 var_AddCallback( p_sys
->p_vout
, "mouse-button-down", ButtonEvent
,
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
) );
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
237 if( p_old_vout
!= NULL
&& p_sys
->vrnav
.b_can_change
)
239 var_DelCallback( p_old_vout
, "mouse-moved", MovedEvent
,
241 var_DelCallback( p_old_vout
, "mouse-button-down", ButtonEvent
,
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
);
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
;
285 p_sys
= malloc( sizeof( intf_sys_t
) );
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
);
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 */
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
,
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
);
338 /* Libvlc / interface actions */
340 libvlc_Quit( p_intf
->obj
.libvlc
);
342 ClearChannels( p_vout
, slider_chan
);
343 DisplayMessage( p_vout
, _( "Quit" ) );
346 case ACTIONID_INTF_TOGGLE_FSC
:
347 case ACTIONID_INTF_HIDE
:
348 var_TriggerCallback( p_intf
->obj
.libvlc
, "intf-toggle-fscontrol" );
350 case ACTIONID_INTF_BOSS
:
351 var_TriggerCallback( p_intf
->obj
.libvlc
, "intf-boss" );
353 case ACTIONID_INTF_POPUP_MENU
:
354 var_TriggerCallback( p_intf
->obj
.libvlc
, "intf-popupmenu" );
357 /* Playlist actions (including audio) */
360 /* Toggle Normal -> Loop -> Repeat -> Normal ... */
362 if( var_GetBool( p_playlist
, "repeat" ) )
364 var_SetBool( p_playlist
, "repeat", false );
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 );
376 var_SetBool( p_playlist
, "loop", true );
379 DisplayMessage( p_vout
, _("Loop: %s"), vlc_gettext(mode
) );
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") ) );
392 DisplayMessage( p_vout
, _("Next") );
393 playlist_Next( p_playlist
);
396 DisplayMessage( p_vout
, _("Previous") );
397 playlist_Prev( p_playlist
);
401 playlist_Stop( p_playlist
);
404 case ACTIONID_RATE_NORMAL
:
405 var_SetFloat( p_playlist
, "rate", 1.f
);
406 DisplayRate( p_vout
, 1.f
);
408 case ACTIONID_FASTER
:
409 var_TriggerCallback( p_playlist
, "rate-faster" );
410 DisplayRate( p_vout
, var_GetFloat( p_playlist
, "rate" ) );
412 case ACTIONID_SLOWER
:
413 var_TriggerCallback( p_playlist
, "rate-slower" );
414 DisplayRate( p_vout
, var_GetFloat( p_playlist
, "rate" ) );
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
);
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 );
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 );
452 case ACTIONID_PLAY_CLEAR
:
454 playlist_t
*p_playlist
= pl_Get( p_intf
);
455 playlist_Clear( p_playlist
, pl_Unlocked
);
458 case ACTIONID_VOL_UP
:
461 if( playlist_VolumeUp( p_playlist
, 1, &vol
) == 0 )
462 DisplayVolume( p_vout
, slider_chan
, vol
);
465 case ACTIONID_VOL_DOWN
:
468 if( playlist_VolumeDown( p_playlist
, 1, &vol
) == 0 )
469 DisplayVolume( p_vout
, slider_chan
, vol
);
472 case ACTIONID_VOL_MUTE
:
474 int mute
= playlist_MuteGet( p_playlist
);
478 if( playlist_MuteSet( p_playlist
, mute
) )
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
);
488 DisplayVolume( p_vout
, slider_chan
, vol
);
492 case ACTIONID_AUDIODEVICE_CYCLE
:
494 audio_output_t
*p_aout
= playlist_GetAout( p_playlist
);
499 int n
= aout_DevicesList( p_aout
, &ids
, &names
);
503 char *dev
= aout_DeviceGet( p_aout
);
504 const char *devstr
= (dev
!= NULL
) ? dev
: "";
507 for( int i
= 0; i
< n
; i
++ )
509 if( !strcmp(devstr
, ids
[i
]) )
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
++ )
528 /* Playlist + input actions */
529 case ACTIONID_PLAY_PAUSE
:
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
);
541 if( p_input
&& var_GetFloat( p_input
, "rate" ) != 1.f
)
542 /* Return to normal speed */
543 var_SetFloat( p_input
, "rate", 1.f
);
546 ClearChannels( p_vout
, slider_chan
);
547 DisplayIcon( p_vout
, OSD_PLAY_ICON
);
548 playlist_Play( p_playlist
);
552 /* Playlist + video output actions */
553 case ACTIONID_WALLPAPER
:
555 bool wp
= var_ToggleBool( p_playlist
, "video-wallpaper" );
557 var_SetBool( p_vout
, "video-wallpaper", wp
);
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
);
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")) );
580 case ACTIONID_FRAME_NEXT
:
583 var_TriggerCallback( p_input
, "frame-next" );
584 DisplayMessage( p_vout
, _("Next frame") );
588 case ACTIONID_SUBSYNC_MARKAUDIO
:
590 p_sys
->subtitle_delaybookmarks
.i_time_audio
= mdate();
591 DisplayMessage( p_vout
, _("Sub sync: bookmarked audio time"));
594 case ACTIONID_SUBSYNC_MARKSUB
:
597 vlc_value_t val
, list
, list2
;
599 var_Get( p_input
, "spu-es", &val
);
601 var_Change( p_input
, "spu-es", VLC_VAR_GETCHOICES
,
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
);
610 p_sys
->subtitle_delaybookmarks
.i_time_subtitle
= mdate();
611 DisplayMessage( p_vout
,
612 _("Sub sync: bookmarked subtitle time"));
613 var_FreeList( &list
, &list2
);
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)
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) */
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!" ) );
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;
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;
660 case ACTIONID_SUBDELAY_DOWN
:
661 case ACTIONID_SUBDELAY_UP
:
663 int diff
= (i_action
== ACTIONID_SUBDELAY_UP
) ? 50000 : -50000;
666 vlc_value_t val
, list
, list2
;
668 var_Get( p_input
, "spu-es", &val
);
670 var_Change( p_input
, "spu-es", VLC_VAR_GETCHOICES
,
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
);
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
);
689 case ACTIONID_AUDIODELAY_DOWN
:
690 case ACTIONID_AUDIODELAY_UP
:
692 int diff
= (i_action
== ACTIONID_AUDIODELAY_UP
) ? 50000 : -50000;
695 int64_t i_delay
= var_GetInteger( p_input
, "audio-delay" )
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) );
706 case ACTIONID_AUDIO_TRACK
:
709 vlc_value_t val
, list
, list2
;
711 var_Get( p_input
, "audio-es", &val
);
712 var_Change( p_input
, "audio-es", VLC_VAR_GETCHOICES
,
714 i_count
= list
.p_list
->i_count
;
717 for( i
= 0; i
< i_count
; i
++ )
719 if( val
.i_int
== list
.p_list
->p_values
[i
].i_int
)
724 /* value of audio-es was not in choices list */
728 "invalid current audio track, selecting 0" );
731 else if( i
== i_count
- 1 )
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
);
742 case ACTIONID_SUBTITLE_TRACK
:
745 vlc_value_t val
, list
, list2
;
747 var_Get( p_input
, "spu-es", &val
);
749 var_Change( p_input
, "spu-es", VLC_VAR_GETCHOICES
,
751 i_count
= list
.p_list
->i_count
;
754 DisplayMessage( p_vout
, _("Subtitle track: %s"),
756 var_FreeList( &list
, &list2
);
759 for( i
= 0; i
< i_count
; i
++ )
761 if( val
.i_int
== list
.p_list
->p_values
[i
].i_int
)
766 /* value of spu-es was not in choices list */
770 "invalid current subtitle track, selecting 0" );
773 else if( i
== i_count
- 1 )
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
);
784 case ACTIONID_SUBTITLE_TOGGLE
:
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
,
794 i_count
= list
.p_list
->i_count
;
797 DisplayMessage( p_vout
, _("Subtitle track: %s"),
799 var_FreeList( &list
, &list2
);
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
)
809 /* if there is nothing to toggle choose the first track */
812 i_sel_id
= list
.p_list
->p_values
[1].i_int
;
813 var_SetInteger( p_input
, "spu-choice", i_sel_id
);
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
);
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
);
834 case ACTIONID_PROGRAM_SID_NEXT
:
835 case ACTIONID_PROGRAM_SID_PREV
:
838 vlc_value_t val
, list
, list2
;
840 var_Get( p_input
, "program", &val
);
842 var_Change( p_input
, "program", VLC_VAR_GETCHOICES
,
844 i_count
= list
.p_list
->i_count
;
847 DisplayMessage( p_vout
, _("Program Service ID: %s"),
849 var_FreeList( &list
, &list2
);
852 for( i
= 0; i
< i_count
; i
++ )
854 if( val
.i_int
== list
.p_list
->p_values
[i
].i_int
)
859 /* value of program sid was not in choices list */
863 "invalid current program SID, selecting 0" );
866 else if( i_action
== ACTIONID_PROGRAM_SID_NEXT
) {
867 if( i
== i_count
- 1 )
872 else { /* ACTIONID_PROGRAM_SID_PREV */
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
);
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" ) )
901 case ACTIONID_JUMP_BACKWARD_EXTRASHORT
:
903 case ACTIONID_JUMP_FORWARD_EXTRASHORT
:
904 varname
= "extrashort-jump-size";
906 case ACTIONID_JUMP_BACKWARD_SHORT
:
908 case ACTIONID_JUMP_FORWARD_SHORT
:
909 varname
= "short-jump-size";
911 case ACTIONID_JUMP_BACKWARD_MEDIUM
:
913 case ACTIONID_JUMP_FORWARD_MEDIUM
:
914 varname
= "medium-jump-size";
916 case ACTIONID_JUMP_BACKWARD_LONG
:
918 case ACTIONID_JUMP_FORWARD_LONG
:
919 varname
= "long-jump-size";
923 mtime_t it
= var_InheritInteger( p_input
, varname
);
926 var_SetInteger( p_input
, "time-offset", it
* sign
* CLOCK_FREQ
);
927 DisplayPosition( p_vout
, slider_chan
, p_input
);
931 /* Input navigation */
932 case ACTIONID_TITLE_PREV
:
934 var_TriggerCallback( p_input
, "prev-title" );
936 case ACTIONID_TITLE_NEXT
:
938 var_TriggerCallback( p_input
, "next-title" );
940 case ACTIONID_CHAPTER_PREV
:
942 var_TriggerCallback( p_input
, "prev-chapter" );
944 case ACTIONID_CHAPTER_NEXT
:
946 var_TriggerCallback( p_input
, "next-chapter" );
948 case ACTIONID_DISC_MENU
:
950 var_SetInteger( p_input
, "title 0", 2 );
952 case ACTIONID_NAV_ACTIVATE
:
954 input_Control( p_input
, INPUT_NAV_ACTIVATE
, NULL
);
956 case ACTIONID_NAV_UP
:
958 input_UpdateViewpoint( p_input
,
959 &(vlc_viewpoint_t
) { .pitch
= -1.f
},
962 input_Control( p_input
, INPUT_NAV_UP
, NULL
);
964 case ACTIONID_NAV_DOWN
:
966 input_UpdateViewpoint( p_input
,
967 &(vlc_viewpoint_t
) { .pitch
= 1.f
},
970 input_Control( p_input
, INPUT_NAV_DOWN
, NULL
);
972 case ACTIONID_NAV_LEFT
:
974 input_UpdateViewpoint( p_input
,
975 &(vlc_viewpoint_t
) { .yaw
= -1.f
},
978 input_Control( p_input
, INPUT_NAV_LEFT
, NULL
);
980 case ACTIONID_NAV_RIGHT
:
982 input_UpdateViewpoint( p_input
,
983 &(vlc_viewpoint_t
) { .yaw
= 1.f
},
986 input_Control( p_input
, INPUT_NAV_RIGHT
, NULL
);
989 /* Video Output actions */
990 case ACTIONID_SNAPSHOT
:
992 var_TriggerCallback( p_vout
, "video-snapshot" );
995 case ACTIONID_TOGGLE_FULLSCREEN
:
999 bool fs
= var_ToggleBool( p_vout
, "fullscreen" );
1000 var_SetBool( p_playlist
, "fullscreen", fs
);
1003 var_ToggleBool( p_playlist
, "fullscreen" );
1007 case ACTIONID_LEAVE_FULLSCREEN
:
1009 var_SetBool( p_vout
, "fullscreen", false );
1010 var_SetBool( p_playlist
, "fullscreen", false );
1013 case ACTIONID_ASPECT_RATIO
:
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 )
1022 for( i
= 0; i
< val_list
.p_list
->i_count
; i
++ )
1024 if( !strcmp( val_list
.p_list
->p_values
[i
].psz_string
,
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
);
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 )
1052 for( i
= 0; i
< val_list
.p_list
->i_count
; i
++ )
1054 if( !strcmp( val_list
.p_list
->p_values
[i
].psz_string
,
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
);
1072 case ACTIONID_CROP_TOP
:
1074 var_IncInteger( p_vout
, "crop-top" );
1076 case ACTIONID_UNCROP_TOP
:
1078 var_DecInteger( p_vout
, "crop-top" );
1080 case ACTIONID_CROP_BOTTOM
:
1082 var_IncInteger( p_vout
, "crop-bottom" );
1084 case ACTIONID_UNCROP_BOTTOM
:
1086 var_DecInteger( p_vout
, "crop-bottom" );
1088 case ACTIONID_CROP_LEFT
:
1090 var_IncInteger( p_vout
, "crop-left" );
1092 case ACTIONID_UNCROP_LEFT
:
1094 var_DecInteger( p_vout
, "crop-left" );
1096 case ACTIONID_CROP_RIGHT
:
1098 var_IncInteger( p_vout
, "crop-right" );
1100 case ACTIONID_UNCROP_RIGHT
:
1102 var_DecInteger( p_vout
, "crop-right" );
1105 case ACTIONID_VIEWPOINT_FOV_IN
:
1107 input_UpdateViewpoint( p_input
,
1108 &(vlc_viewpoint_t
) { .fov
= -1.f
},
1111 case ACTIONID_VIEWPOINT_FOV_OUT
:
1113 input_UpdateViewpoint( p_input
,
1114 &(vlc_viewpoint_t
) { .fov
= 1.f
},
1118 case ACTIONID_VIEWPOINT_ZOOM_IN
:
1120 input_UpdateViewpoint( p_input
,
1121 &(vlc_viewpoint_t
) { .zoom
= 0.01f
},
1124 case ACTIONID_VIEWPOINT_ZOOM_OUT
:
1126 input_UpdateViewpoint( p_input
,
1127 &(vlc_viewpoint_t
) { .zoom
= -0.01f
},
1131 case ACTIONID_VIEWPOINT_ROLL_CLOCK
:
1133 input_UpdateViewpoint( p_input
,
1134 &(vlc_viewpoint_t
) { .roll
= -1.f
},
1137 case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK
:
1139 input_UpdateViewpoint( p_input
,
1140 &(vlc_viewpoint_t
) { .roll
= 1.f
},
1144 case ACTIONID_TOGGLE_AUTOSCALE
:
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") );
1155 bool b_autoscale
= !var_GetBool( p_vout
, "autoscale" );
1156 var_SetBool( p_vout
, "autoscale", b_autoscale
);
1158 DisplayMessage( p_vout
, _("Scaled to screen") );
1160 DisplayMessage( p_vout
, _("Original Size") );
1164 case ACTIONID_SCALE_UP
:
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
);
1174 case ACTIONID_SCALE_DOWN
:
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
);
1185 case ACTIONID_ZOOM_QUARTER
:
1186 case ACTIONID_ZOOM_HALF
:
1187 case ACTIONID_ZOOM_ORIGINAL
:
1188 case ACTIONID_ZOOM_DOUBLE
:
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
);
1204 case ACTIONID_UNZOOM
:
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 )
1213 for( i
= 0; i
< val_list
.p_list
->i_count
; i
++ )
1215 if( val_list
.p_list
->p_values
[i
].f_float
1218 if( i_action
== ACTIONID_ZOOM
)
1220 else /* ACTIONID_UNZOOM */
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
);
1237 case ACTIONID_DEINTERLACE
:
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") );
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
;
1263 DisplayMessage( p_vout
, "%s (%s)", _("Deinterlace on"),
1264 psz_text
? psz_text
: psz_mode
);
1266 var_FreeList( &vlist
, &tlist
);
1272 case ACTIONID_DEINTERLACE_MODE
:
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
;
1281 for( i
= 0; i
< vlist
.p_list
->i_count
; i
++ )
1283 if( !strcmp( vlist
.p_list
->p_values
[i
].psz_string
, psz_mode
) )
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
);
1301 DisplayMessage( p_vout
, "%s (%s)", _("Deinterlace off"),
1302 psz_text
? psz_text
: psz_mode
);
1305 var_FreeList( &vlist
, &tlist
);
1311 case ACTIONID_SUBPOS_DOWN
:
1312 case ACTIONID_SUBPOS_UP
:
1316 vlc_value_t val
, list
, list2
;
1318 var_Get( p_input
, "spu-es", &val
);
1320 var_Change( p_input
, "spu-es", VLC_VAR_GETCHOICES
,
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
);
1332 if( i_action
== ACTIONID_SUBPOS_DOWN
)
1333 i_pos
= var_DecInteger( p_vout
, "sub-margin" );
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
);
1344 case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN
:
1345 case ACTIONID_SUBTITLE_TEXT_SCALE_UP
:
1346 case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL
:
1351 if( i_action
== ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL
)
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
);
1372 case ACTIONID_COMBO_VOL_ZOOM_UP
:
1374 DO_ACTION( ACTIONID_VIEWPOINT_ZOOM_IN
);
1376 DO_ACTION( ACTIONID_VOL_UP
);
1378 case ACTIONID_COMBO_VOL_ZOOM_DOWN
:
1380 DO_ACTION( ACTIONID_VIEWPOINT_ZOOM_OUT
);
1382 DO_ACTION( ACTIONID_VOL_DOWN
);
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
;
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
)
1405 vout_thread_t
*p_vout
= p_sys
->p_vout
? vlc_object_hold( p_sys
->p_vout
)
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
,
1414 if( p_input
!= NULL
)
1415 vlc_object_release( p_input
);
1416 if( p_vout
!= NULL
)
1417 vlc_object_release( p_vout
);
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 )
1428 playlist_t
*p_playlist
= pl_Get( p_intf
);
1429 char *psz_bookmark
= var_CreateGetString( p_intf
, psz_bookmark_name
);
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
) )
1437 playlist_Control( p_playlist
, PLAYLIST_VIEWPLAY
, pl_Locked
,
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 )
1457 playlist_t
*p_playlist
= pl_Get( p_intf
);
1458 var_Create( p_intf
, psz_bookmark_name
,
1459 VLC_VAR_STRING
|VLC_VAR_DOINHERIT
);
1462 playlist_item_t
* p_item
= playlist_CurrentPlayingItem( p_playlist
);
1463 if( p_item
) psz_uri
= input_item_GetURI( p_item
->p_input
);
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
);
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
);
1493 secstotimestr( psz_duration
, l
);
1494 DisplayMessage( p_vout
, "%s / %s", psz_time
, psz_duration
);
1498 DisplayMessage( p_vout
, "%s", psz_time
);
1501 if( var_GetBool( p_vout
, "fullscreen" ) )
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
)
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
;
1546 static void ClearChannels( vout_thread_t
*p_vout
, int slider_chan
)
1550 vout_FlushSubpictureChannel( p_vout
, SPU_DEFAULT_CHANNEL
);
1551 vout_FlushSubpictureChannel( p_vout
, slider_chan
);