d3d11: handle VLC_CODEC_D3D11_OPAQUE_10B upload/download
[vlc.git] / modules / control / hotkeys.c
blobad23bf9e26f5910a3f0ed7ec92e31d4df7bcec72
1 /*****************************************************************************
2 * hotkeys.c: Hotkey handling for vlc
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
5 * $Id$
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Jean-Paul Saman <jpsaman #_at_# m2x.nl>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_interface.h>
37 #include <vlc_input.h>
38 #include <vlc_aout.h>
39 #include <vlc_viewpoint.h>
40 #include <vlc_vout_osd.h>
41 #include <vlc_playlist.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 int64_t i_time_subtitle;
61 int64_t i_time_audio;
62 } subtitle_delaybookmarks;
64 struct
66 bool b_can_change;
67 bool b_button_pressed;
68 int x, y;
69 } vrnav;
72 /*****************************************************************************
73 * Local prototypes
74 *****************************************************************************/
75 static int Open ( vlc_object_t * );
76 static void Close ( vlc_object_t * );
77 static int ActionEvent( vlc_object_t *, char const *,
78 vlc_value_t, vlc_value_t, void * );
79 static void PlayBookmark( intf_thread_t *, int );
80 static void SetBookmark ( intf_thread_t *, int );
81 static void DisplayPosition( vout_thread_t *, int, input_thread_t * );
82 static void DisplayVolume( vout_thread_t *, int, float );
83 static void DisplayRate ( vout_thread_t *, float );
84 static float AdjustRateFine( vlc_object_t *, const int );
85 static void ClearChannels ( vout_thread_t *, int );
87 #define DisplayMessage(vout, ...) \
88 do { \
89 if (vout) \
90 vout_OSDMessage(vout, 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 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 int i_horizontal = newval.coords.x - p_sys->vrnav.x;
120 int i_vertical = newval.coords.y - p_sys->vrnav.y;
122 vlc_viewpoint_t viewpoint = {
123 .yaw = -i_horizontal * 0.05f,
124 .pitch = -i_vertical * 0.05f,
127 input_UpdateViewpoint( p_sys->p_input, &viewpoint, false );
129 p_sys->vrnav.x = newval.coords.x;
130 p_sys->vrnav.y = newval.coords.y;
133 return VLC_SUCCESS;
136 static int ViewpointMovedEvent( vlc_object_t *p_this, char const *psz_var,
137 vlc_value_t oldval, vlc_value_t newval,
138 void *p_data )
140 intf_thread_t *p_intf = (intf_thread_t *)p_data;
141 intf_sys_t *p_sys = p_intf->p_sys;
143 (void) p_this; (void) psz_var; (void) oldval;
145 input_UpdateViewpoint( p_sys->p_input, newval.p_address, false );
147 return VLC_SUCCESS;
150 static int ButtonEvent( vlc_object_t *p_this, char const *psz_var,
151 vlc_value_t oldval, vlc_value_t newval, void *p_data )
153 intf_thread_t *p_intf = p_data;
154 intf_sys_t *p_sys = p_intf->p_sys;
156 (void) psz_var; (void) oldval;
158 if( newval.i_int & 0x01 )
160 if( !p_sys->vrnav.b_button_pressed )
162 p_sys->vrnav.b_button_pressed = true;
163 var_GetCoords( p_this, "mouse-moved",
164 &p_sys->vrnav.x, &p_sys->vrnav.y );
167 else
168 p_sys->vrnav.b_button_pressed = false;
170 return VLC_SUCCESS;
173 static void ChangeVout( intf_thread_t *p_intf, vout_thread_t *p_vout )
175 intf_sys_t *p_sys = p_intf->p_sys;
177 int slider_chan;
178 bool b_vrnav_can_change;
179 if( p_vout != NULL )
181 slider_chan = vout_RegisterSubpictureChannel( p_vout );
182 b_vrnav_can_change = var_GetBool( p_vout, "viewpoint-changeable" );
185 vlc_mutex_lock( &p_sys->lock );
186 vout_thread_t *p_old_vout = p_sys->p_vout;
187 bool b_vrnav_could_change = p_sys->vrnav.b_can_change;
188 p_sys->p_vout = p_vout;
189 if( p_vout != NULL )
191 p_sys->slider_chan = slider_chan;
192 p_sys->vrnav.b_can_change = b_vrnav_can_change;
194 else
195 p_sys->vrnav.b_can_change = false;
196 vlc_mutex_unlock( &p_sys->lock );
198 if( p_old_vout != NULL )
200 if( b_vrnav_could_change )
202 var_DelCallback( p_old_vout, "mouse-moved", MovedEvent,
203 p_intf );
204 var_DelCallback( p_old_vout, "mouse-button-down", ButtonEvent,
205 p_intf );
206 var_DelCallback( p_old_vout, "viewpoint-moved", ViewpointMovedEvent,
207 p_intf );
209 vlc_object_release( p_old_vout );
212 if( p_sys->vrnav.b_can_change )
214 assert( p_sys->p_vout != NULL );
215 var_AddCallback( p_sys->p_vout, "mouse-moved", MovedEvent,
216 p_intf );
217 var_AddCallback( p_sys->p_vout, "mouse-button-down", ButtonEvent,
218 p_intf );
219 var_AddCallback( p_sys->p_vout, "viewpoint-moved", ViewpointMovedEvent,
220 p_intf );
224 static int InputEvent( vlc_object_t *p_this, char const *psz_var,
225 vlc_value_t oldval, vlc_value_t val, void *p_data )
227 input_thread_t *p_input = (input_thread_t *)p_this;
228 intf_thread_t *p_intf = p_data;
230 (void) psz_var; (void) oldval;
232 if( val.i_int == INPUT_EVENT_VOUT )
233 ChangeVout( p_intf, input_GetVout( p_input ) );
235 return VLC_SUCCESS;
238 static void ChangeInput( intf_thread_t *p_intf, input_thread_t *p_input )
240 intf_sys_t *p_sys = p_intf->p_sys;
242 input_thread_t *p_old_input = p_sys->p_input;
243 vout_thread_t *p_old_vout = NULL;
244 if( p_old_input != NULL )
246 /* First, remove callbacks from previous input. It's safe to access it
247 * unlocked, since it's written from this thread */
248 var_DelCallback( p_old_input, "intf-event", InputEvent, p_intf );
250 p_old_vout = p_sys->p_vout;
251 /* Remove mouse events before setting new input, since callbacks may
252 * access it */
253 if( p_old_vout != NULL && p_sys->vrnav.b_can_change )
255 var_DelCallback( p_old_vout, "mouse-moved", MovedEvent,
256 p_intf );
257 var_DelCallback( p_old_vout, "mouse-button-down", ButtonEvent,
258 p_intf );
259 var_DelCallback( p_old_vout, "viewpoint-moved", ViewpointMovedEvent,
260 p_intf );
264 /* Replace input and vout locked */
265 vlc_mutex_lock( &p_sys->lock );
266 p_sys->p_input = p_input ? vlc_object_hold( p_input ) : NULL;
267 p_sys->p_vout = NULL;
268 p_sys->vrnav.b_can_change = false;
269 vlc_mutex_unlock( &p_sys->lock );
271 /* Release old input and vout objects unlocked */
272 if( p_old_input != NULL )
274 if( p_old_vout != NULL )
275 vlc_object_release( p_old_vout );
276 vlc_object_release( p_old_input );
279 /* Register input events */
280 if( p_input != NULL )
281 var_AddCallback( p_input, "intf-event", InputEvent, p_intf );
284 static int PlaylistEvent( vlc_object_t *p_this, char const *psz_var,
285 vlc_value_t oldval, vlc_value_t val, void *p_data )
287 intf_thread_t *p_intf = p_data;
289 (void) p_this; (void) psz_var; (void) oldval;
291 ChangeInput( p_intf, val.p_address );
293 return VLC_SUCCESS;
296 /*****************************************************************************
297 * Open: initialize interface
298 *****************************************************************************/
299 static int Open( vlc_object_t *p_this )
301 intf_thread_t *p_intf = (intf_thread_t *)p_this;
302 intf_sys_t *p_sys;
303 p_sys = malloc( sizeof( intf_sys_t ) );
304 if( !p_sys )
305 return VLC_ENOMEM;
307 p_intf->p_sys = p_sys;
309 p_sys->p_vout = NULL;
310 p_sys->p_input = NULL;
311 p_sys->vrnav.b_can_change = false;
312 p_sys->vrnav.b_button_pressed = false;
313 p_sys->subtitle_delaybookmarks.i_time_audio = 0;
314 p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
316 vlc_mutex_init( &p_sys->lock );
318 var_AddCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
320 var_AddCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
322 return VLC_SUCCESS;
325 /*****************************************************************************
326 * Close: destroy interface
327 *****************************************************************************/
328 static void Close( vlc_object_t *p_this )
330 intf_thread_t *p_intf = (intf_thread_t *)p_this;
331 intf_sys_t *p_sys = p_intf->p_sys;
333 var_DelCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
335 var_DelCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
337 ChangeInput( p_intf, NULL );
339 vlc_mutex_destroy( &p_sys->lock );
341 /* Destroy structure */
342 free( p_sys );
345 static int PutAction( intf_thread_t *p_intf, input_thread_t *p_input,
346 vout_thread_t *p_vout, int slider_chan, bool b_vrnav,
347 int i_action )
349 #define DO_ACTION(x) PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav, x)
350 intf_sys_t *p_sys = p_intf->p_sys;
351 playlist_t *p_playlist = pl_Get( p_intf );
353 /* Quit */
354 switch( i_action )
356 /* Libvlc / interface actions */
357 case ACTIONID_QUIT:
358 libvlc_Quit( p_intf->obj.libvlc );
360 ClearChannels( p_vout, slider_chan );
361 DisplayMessage( p_vout, _( "Quit" ) );
362 break;
364 case ACTIONID_INTF_TOGGLE_FSC:
365 case ACTIONID_INTF_HIDE:
366 var_TriggerCallback( p_intf->obj.libvlc, "intf-toggle-fscontrol" );
367 break;
368 case ACTIONID_INTF_BOSS:
369 var_TriggerCallback( p_intf->obj.libvlc, "intf-boss" );
370 break;
371 case ACTIONID_INTF_POPUP_MENU:
372 var_TriggerCallback( p_intf->obj.libvlc, "intf-popupmenu" );
373 break;
375 /* Playlist actions (including audio) */
376 case ACTIONID_LOOP:
378 /* Toggle Normal -> Loop -> Repeat -> Normal ... */
379 const char *mode;
380 if( var_GetBool( p_playlist, "repeat" ) )
382 var_SetBool( p_playlist, "repeat", false );
383 mode = N_("Off");
385 else
386 if( var_GetBool( p_playlist, "loop" ) )
387 { /* FIXME: this is not atomic, we should use a real tristate */
388 var_SetBool( p_playlist, "loop", false );
389 var_SetBool( p_playlist, "repeat", true );
390 mode = N_("One");
392 else
394 var_SetBool( p_playlist, "loop", true );
395 mode = N_("All");
397 DisplayMessage( p_vout, _("Loop: %s"), vlc_gettext(mode) );
398 break;
401 case ACTIONID_RANDOM:
403 const bool state = var_ToggleBool( p_playlist, "random" );
404 DisplayMessage( p_vout, _("Random: %s"),
405 vlc_gettext( state ? N_("On") : N_("Off") ) );
406 break;
409 case ACTIONID_NEXT:
410 DisplayMessage( p_vout, _("Next") );
411 playlist_Next( p_playlist );
412 break;
413 case ACTIONID_PREV:
414 DisplayMessage( p_vout, _("Previous") );
415 playlist_Prev( p_playlist );
416 break;
418 case ACTIONID_STOP:
419 playlist_Stop( p_playlist );
420 break;
422 case ACTIONID_RATE_NORMAL:
423 var_SetFloat( p_playlist, "rate", 1.f );
424 DisplayRate( p_vout, 1.f );
425 break;
426 case ACTIONID_FASTER:
427 var_TriggerCallback( p_playlist, "rate-faster" );
428 DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
429 break;
430 case ACTIONID_SLOWER:
431 var_TriggerCallback( p_playlist, "rate-slower" );
432 DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
433 break;
434 case ACTIONID_RATE_FASTER_FINE:
435 case ACTIONID_RATE_SLOWER_FINE:
437 const int i_dir = i_action == ACTIONID_RATE_FASTER_FINE ? 1 : -1;
438 float rate = AdjustRateFine( VLC_OBJECT(p_playlist), i_dir );
440 var_SetFloat( p_playlist, "rate", rate );
441 DisplayRate( p_vout, rate );
442 break;
445 case ACTIONID_PLAY_BOOKMARK1:
446 case ACTIONID_PLAY_BOOKMARK2:
447 case ACTIONID_PLAY_BOOKMARK3:
448 case ACTIONID_PLAY_BOOKMARK4:
449 case ACTIONID_PLAY_BOOKMARK5:
450 case ACTIONID_PLAY_BOOKMARK6:
451 case ACTIONID_PLAY_BOOKMARK7:
452 case ACTIONID_PLAY_BOOKMARK8:
453 case ACTIONID_PLAY_BOOKMARK9:
454 case ACTIONID_PLAY_BOOKMARK10:
455 PlayBookmark( p_intf, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
456 break;
458 case ACTIONID_SET_BOOKMARK1:
459 case ACTIONID_SET_BOOKMARK2:
460 case ACTIONID_SET_BOOKMARK3:
461 case ACTIONID_SET_BOOKMARK4:
462 case ACTIONID_SET_BOOKMARK5:
463 case ACTIONID_SET_BOOKMARK6:
464 case ACTIONID_SET_BOOKMARK7:
465 case ACTIONID_SET_BOOKMARK8:
466 case ACTIONID_SET_BOOKMARK9:
467 case ACTIONID_SET_BOOKMARK10:
468 SetBookmark( p_intf, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
469 break;
470 case ACTIONID_PLAY_CLEAR:
471 playlist_Clear( p_playlist, pl_Unlocked );
472 break;
473 case ACTIONID_VOL_UP:
475 float vol;
476 if( playlist_VolumeUp( p_playlist, 1, &vol ) == 0 )
477 DisplayVolume( p_vout, slider_chan, vol );
478 break;
480 case ACTIONID_VOL_DOWN:
482 float vol;
483 if( playlist_VolumeDown( p_playlist, 1, &vol ) == 0 )
484 DisplayVolume( p_vout, slider_chan, vol );
485 break;
487 case ACTIONID_VOL_MUTE:
489 int mute = playlist_MuteGet( p_playlist );
490 if( mute < 0 )
491 break;
492 mute = !mute;
493 if( playlist_MuteSet( p_playlist, mute ) )
494 break;
496 float vol = playlist_VolumeGet( p_playlist );
497 if( mute || vol == 0.f )
499 ClearChannels( p_vout, slider_chan );
500 DisplayIcon( p_vout, OSD_MUTE_ICON );
502 else
503 DisplayVolume( p_vout, slider_chan, vol );
504 break;
507 case ACTIONID_AUDIODEVICE_CYCLE:
509 audio_output_t *p_aout = playlist_GetAout( p_playlist );
510 if( p_aout == NULL )
511 break;
513 char **ids, **names;
514 int n = aout_DevicesList( p_aout, &ids, &names );
515 if( n == -1 )
516 break;
518 char *dev = aout_DeviceGet( p_aout );
519 const char *devstr = (dev != NULL) ? dev : "";
521 int idx = 0;
522 for( int i = 0; i < n; i++ )
524 if( !strcmp(devstr, ids[i]) )
525 idx = (i + 1) % n;
527 free( dev );
529 if( !aout_DeviceSet( p_aout, ids[idx] ) )
530 DisplayMessage( p_vout, _("Audio Device: %s"), names[idx] );
531 vlc_object_release( p_aout );
533 for( int i = 0; i < n; i++ )
535 free( ids[i] );
536 free( names[i] );
538 free( ids );
539 free( names );
540 break;
543 /* Playlist + input actions */
544 case ACTIONID_PLAY_PAUSE:
545 if( p_input )
547 ClearChannels( p_vout, slider_chan );
549 int state = var_GetInteger( p_input, "state" );
550 DisplayIcon( p_vout, state != PAUSE_S ? OSD_PAUSE_ICON : OSD_PLAY_ICON );
552 playlist_TogglePause( p_playlist );
553 break;
555 case ACTIONID_PLAY:
556 if( p_input && var_GetFloat( p_input, "rate" ) != 1.f )
557 /* Return to normal speed */
558 var_SetFloat( p_input, "rate", 1.f );
559 else
561 ClearChannels( p_vout, slider_chan );
562 DisplayIcon( p_vout, OSD_PLAY_ICON );
563 playlist_Play( p_playlist );
565 break;
567 /* Playlist + video output actions */
568 case ACTIONID_WALLPAPER:
570 bool wp = var_ToggleBool( p_playlist, "video-wallpaper" );
571 if( p_vout )
572 var_SetBool( p_vout, "video-wallpaper", wp );
573 break;
576 /* Input actions */
577 case ACTIONID_PAUSE:
578 if( p_input && var_GetInteger( p_input, "state" ) != PAUSE_S )
580 ClearChannels( p_vout, slider_chan );
581 DisplayIcon( p_vout, OSD_PAUSE_ICON );
582 var_SetInteger( p_input, "state", PAUSE_S );
584 break;
586 case ACTIONID_RECORD:
587 if( p_input && var_GetBool( p_input, "can-record" ) )
589 const bool on = var_ToggleBool( p_input, "record" );
590 DisplayMessage( p_vout, vlc_gettext(on
591 ? N_("Recording") : N_("Recording done")) );
593 break;
595 case ACTIONID_FRAME_NEXT:
596 if( p_input )
598 var_TriggerCallback( p_input, "frame-next" );
599 DisplayMessage( p_vout, _("Next frame") );
601 break;
603 case ACTIONID_SUBSYNC_MARKAUDIO:
605 p_sys->subtitle_delaybookmarks.i_time_audio = mdate();
606 DisplayMessage( p_vout, _("Sub sync: bookmarked audio time"));
607 break;
609 case ACTIONID_SUBSYNC_MARKSUB:
610 if( p_input )
612 vlc_value_t val, list, list2;
613 int i_count;
614 var_Get( p_input, "spu-es", &val );
616 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
617 &list, &list2 );
618 i_count = list.p_list->i_count;
619 if( i_count < 1 || val.i_int < 0 )
621 DisplayMessage( p_vout, _("No active subtitle") );
622 var_FreeList( &list, &list2 );
623 break;
625 p_sys->subtitle_delaybookmarks.i_time_subtitle = mdate();
626 DisplayMessage( p_vout,
627 _("Sub sync: bookmarked subtitle time"));
628 var_FreeList( &list, &list2 );
630 break;
631 case ACTIONID_SUBSYNC_APPLY:
633 /* Warning! Can yield a pause in the playback.
634 * For example, the following succession of actions will yield a 5 second delay :
635 * - Pressing Shift-H (ACTIONID_SUBSYNC_MARKAUDIO)
636 * - wait 5 second
637 * - Press Shift-J (ACTIONID_SUBSYNC_MARKSUB)
638 * - Press Shift-K (ACTIONID_SUBSYNC_APPLY)
639 * --> 5 seconds pause
640 * This is due to var_SetTime() (and ultimately UpdatePtsDelay())
641 * which causes the video to pause for an equivalent duration
642 * (This problem is also present in the "Track synchronization" window) */
643 if ( p_input )
645 if ( (p_sys->subtitle_delaybookmarks.i_time_audio == 0) || (p_sys->subtitle_delaybookmarks.i_time_subtitle == 0) )
647 DisplayMessage( p_vout, _( "Sub sync: set bookmarks first!" ) );
649 else
651 int64_t i_current_subdelay = var_GetInteger( p_input, "spu-delay" );
652 int64_t i_additional_subdelay = p_sys->subtitle_delaybookmarks.i_time_audio - p_sys->subtitle_delaybookmarks.i_time_subtitle;
653 int64_t i_total_subdelay = i_current_subdelay + i_additional_subdelay;
654 var_SetInteger( p_input, "spu-delay", i_total_subdelay);
655 ClearChannels( p_vout, slider_chan );
656 DisplayMessage( p_vout, _( "Sub sync: corrected %i ms (total delay = %i ms)" ),
657 (int)(i_additional_subdelay / 1000),
658 (int)(i_total_subdelay / 1000) );
659 p_sys->subtitle_delaybookmarks.i_time_audio = 0;
660 p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
663 break;
665 case ACTIONID_SUBSYNC_RESET:
667 var_SetInteger( p_input, "spu-delay", 0);
668 ClearChannels( p_vout, slider_chan );
669 DisplayMessage( p_vout, _( "Sub sync: delay reset" ) );
670 p_sys->subtitle_delaybookmarks.i_time_audio = 0;
671 p_sys->subtitle_delaybookmarks.i_time_subtitle = 0;
672 break;
675 case ACTIONID_SUBDELAY_DOWN:
676 case ACTIONID_SUBDELAY_UP:
678 int diff = (i_action == ACTIONID_SUBDELAY_UP) ? 50000 : -50000;
679 if( p_input )
681 vlc_value_t val, list, list2;
682 int i_count;
683 var_Get( p_input, "spu-es", &val );
685 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
686 &list, &list2 );
687 i_count = list.p_list->i_count;
688 if( i_count < 1 || val.i_int < 0 )
690 DisplayMessage( p_vout, _("No active subtitle") );
691 var_FreeList( &list, &list2 );
692 break;
694 int64_t i_delay = var_GetInteger( p_input, "spu-delay" ) + diff;
696 var_SetInteger( p_input, "spu-delay", i_delay );
697 ClearChannels( p_vout, slider_chan );
698 DisplayMessage( p_vout, _( "Subtitle delay %i ms" ),
699 (int)(i_delay/1000) );
700 var_FreeList( &list, &list2 );
702 break;
704 case ACTIONID_AUDIODELAY_DOWN:
705 case ACTIONID_AUDIODELAY_UP:
707 int diff = (i_action == ACTIONID_AUDIODELAY_UP) ? 50000 : -50000;
708 if( p_input )
710 int64_t i_delay = var_GetInteger( p_input, "audio-delay" )
711 + diff;
713 var_SetInteger( p_input, "audio-delay", i_delay );
714 ClearChannels( p_vout, slider_chan );
715 DisplayMessage( p_vout, _( "Audio delay %i ms" ),
716 (int)(i_delay/1000) );
718 break;
721 case ACTIONID_AUDIO_TRACK:
722 if( p_input )
724 vlc_value_t val, list, list2;
725 int i_count, i;
726 var_Get( p_input, "audio-es", &val );
727 var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
728 &list, &list2 );
729 i_count = list.p_list->i_count;
730 if( i_count > 1 )
732 for( i = 0; i < i_count; i++ )
734 if( val.i_int == list.p_list->p_values[i].i_int )
736 break;
739 /* value of audio-es was not in choices list */
740 if( i == i_count )
742 msg_Warn( p_input,
743 "invalid current audio track, selecting 0" );
744 i = 0;
746 else if( i == i_count - 1 )
747 i = 1;
748 else
749 i++;
750 var_Set( p_input, "audio-es", list.p_list->p_values[i] );
751 DisplayMessage( p_vout, _("Audio track: %s"),
752 list2.p_list->p_values[i].psz_string );
754 var_FreeList( &list, &list2 );
756 break;
758 case ACTIONID_SUBTITLE_TRACK:
759 case ACTIONID_SUBTITLE_REVERSE_TRACK:
760 if( p_input )
762 vlc_value_t val, list, list2;
763 int i_count, i;
764 var_Get( p_input, "spu-es", &val );
766 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
767 &list, &list2 );
768 i_count = list.p_list->i_count;
769 if( i_count <= 1 )
771 DisplayMessage( p_vout, _("Subtitle track: %s"),
772 _("N/A") );
773 var_FreeList( &list, &list2 );
774 break;
776 for( i = 0; i < i_count; i++ )
778 if( val.i_int == list.p_list->p_values[i].i_int )
780 break;
783 /* value of spu-es was not in choices list */
784 if( i == i_count )
786 msg_Warn( p_input,
787 "invalid current subtitle track, selecting 0" );
788 i = 0;
790 else if ((i == i_count - 1) && (i_action == ACTIONID_SUBTITLE_TRACK))
791 i = 0;
792 else if ((i == 0) && (i_action == ACTIONID_SUBTITLE_REVERSE_TRACK))
793 i = i_count - 1;
794 else
795 i = (i_action == ACTIONID_SUBTITLE_TRACK) ? i+1 : i-1;
796 var_SetInteger( p_input, "spu-es", list.p_list->p_values[i].i_int );
797 var_SetInteger( p_input, "spu-choice", list.p_list->p_values[i].i_int );
798 DisplayMessage( p_vout, _("Subtitle track: %s"),
799 list2.p_list->p_values[i].psz_string );
800 var_FreeList( &list, &list2 );
802 break;
803 case ACTIONID_SUBTITLE_TOGGLE:
804 if( p_input )
806 vlc_value_t list, list2;
807 int i_count, i_sel_index, i_sel_id, i_old_id, i_new_index;
808 i_old_id = var_GetInteger( p_input, "spu-es" );
809 i_sel_id = var_GetInteger( p_input, "spu-choice" );
811 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
812 &list, &list2 );
813 i_count = list.p_list->i_count;
814 if( i_count <= 1 )
816 DisplayMessage( p_vout, _("Subtitle track: %s"),
817 _("N/A") );
818 var_FreeList( &list, &list2 );
819 break;
821 for( i_sel_index = 0; i_sel_index < i_count; i_sel_index++ )
823 if( i_sel_id == list.p_list->p_values[i_sel_index].i_int )
825 break;
828 /* if there is nothing to toggle choose the first track */
829 if( !i_sel_index ) {
830 i_sel_index = 1;
831 i_sel_id = list.p_list->p_values[1].i_int;
832 var_SetInteger( p_input, "spu-choice", i_sel_id );
835 i_new_index = 0;
836 if( i_old_id != i_sel_id )
838 if( i_sel_index >= i_count )
840 var_SetInteger( p_input, "spu-choice", list.p_list->p_values[0].i_int );
842 else
844 i_new_index = i_sel_index;
847 var_SetInteger( p_input, "spu-es", list.p_list->p_values[i_new_index].i_int );
848 DisplayMessage( p_vout, _("Subtitle track: %s"),
849 list2.p_list->p_values[i_new_index].psz_string );
850 var_FreeList( &list, &list2 );
852 break;
853 case ACTIONID_PROGRAM_SID_NEXT:
854 case ACTIONID_PROGRAM_SID_PREV:
855 if( p_input )
857 vlc_value_t val, list, list2;
858 int i_count, i;
859 var_Get( p_input, "program", &val );
861 var_Change( p_input, "program", VLC_VAR_GETCHOICES,
862 &list, &list2 );
863 i_count = list.p_list->i_count;
864 if( i_count <= 1 )
866 DisplayMessage( p_vout, _("Program Service ID: %s"),
867 _("N/A") );
868 var_FreeList( &list, &list2 );
869 break;
871 for( i = 0; i < i_count; i++ )
873 if( val.i_int == list.p_list->p_values[i].i_int )
875 break;
878 /* value of program sid was not in choices list */
879 if( i == i_count )
881 msg_Warn( p_input,
882 "invalid current program SID, selecting 0" );
883 i = 0;
885 else if( i_action == ACTIONID_PROGRAM_SID_NEXT ) {
886 if( i == i_count - 1 )
887 i = 0;
888 else
889 i++;
891 else { /* ACTIONID_PROGRAM_SID_PREV */
892 if( i == 0 )
893 i = i_count - 1;
894 else
895 i--;
897 var_Set( p_input, "program", list.p_list->p_values[i] );
898 DisplayMessage( p_vout, _("Program Service ID: %s"),
899 list2.p_list->p_values[i].psz_string );
900 var_FreeList( &list, &list2 );
902 break;
904 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
905 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
906 case ACTIONID_JUMP_BACKWARD_SHORT:
907 case ACTIONID_JUMP_FORWARD_SHORT:
908 case ACTIONID_JUMP_BACKWARD_MEDIUM:
909 case ACTIONID_JUMP_FORWARD_MEDIUM:
910 case ACTIONID_JUMP_BACKWARD_LONG:
911 case ACTIONID_JUMP_FORWARD_LONG:
913 if( p_input == NULL || !var_GetBool( p_input, "can-seek" ) )
914 break;
916 const char *varname;
917 int sign = +1;
918 switch( i_action )
920 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
921 sign = -1;
922 /* fall through */
923 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
924 varname = "extrashort-jump-size";
925 break;
926 case ACTIONID_JUMP_BACKWARD_SHORT:
927 sign = -1;
928 /* fall through */
929 case ACTIONID_JUMP_FORWARD_SHORT:
930 varname = "short-jump-size";
931 break;
932 case ACTIONID_JUMP_BACKWARD_MEDIUM:
933 sign = -1;
934 /* fall through */
935 case ACTIONID_JUMP_FORWARD_MEDIUM:
936 varname = "medium-jump-size";
937 break;
938 case ACTIONID_JUMP_BACKWARD_LONG:
939 sign = -1;
940 /* fall through */
941 case ACTIONID_JUMP_FORWARD_LONG:
942 varname = "long-jump-size";
943 break;
946 mtime_t it = var_InheritInteger( p_input, varname );
947 if( it < 0 )
948 break;
949 var_SetInteger( p_input, "time-offset", it * sign * CLOCK_FREQ );
950 DisplayPosition( p_vout, slider_chan, p_input );
951 break;
954 /* Input navigation */
955 case ACTIONID_TITLE_PREV:
956 if( p_input )
957 var_TriggerCallback( p_input, "prev-title" );
958 break;
959 case ACTIONID_TITLE_NEXT:
960 if( p_input )
961 var_TriggerCallback( p_input, "next-title" );
962 break;
963 case ACTIONID_CHAPTER_PREV:
964 if( p_input )
965 var_TriggerCallback( p_input, "prev-chapter" );
966 break;
967 case ACTIONID_CHAPTER_NEXT:
968 if( p_input )
969 var_TriggerCallback( p_input, "next-chapter" );
970 break;
971 case ACTIONID_DISC_MENU:
972 if( p_input )
973 var_SetInteger( p_input, "title 0", 2 );
974 break;
975 case ACTIONID_NAV_ACTIVATE:
976 if( p_input )
977 input_Control( p_input, INPUT_NAV_ACTIVATE, NULL );
978 break;
979 case ACTIONID_NAV_UP:
980 if( p_input )
981 input_Control( p_input, INPUT_NAV_UP, NULL );
982 break;
983 case ACTIONID_NAV_DOWN:
984 if( p_input )
985 input_Control( p_input, INPUT_NAV_DOWN, NULL );
986 break;
987 case ACTIONID_NAV_LEFT:
988 if( p_input )
989 input_Control( p_input, INPUT_NAV_LEFT, NULL );
990 break;
991 case ACTIONID_NAV_RIGHT:
992 if( p_input )
993 input_Control( p_input, INPUT_NAV_RIGHT, NULL );
994 break;
996 /* Video Output actions */
997 case ACTIONID_SNAPSHOT:
998 if( p_vout )
999 var_TriggerCallback( p_vout, "video-snapshot" );
1000 break;
1002 case ACTIONID_TOGGLE_FULLSCREEN:
1004 if( p_vout )
1006 bool fs = var_ToggleBool( p_vout, "fullscreen" );
1007 var_SetBool( p_playlist, "fullscreen", fs );
1009 else
1010 var_ToggleBool( p_playlist, "fullscreen" );
1011 break;
1014 case ACTIONID_LEAVE_FULLSCREEN:
1015 if( p_vout )
1016 var_SetBool( p_vout, "fullscreen", false );
1017 var_SetBool( p_playlist, "fullscreen", false );
1018 break;
1020 case ACTIONID_ASPECT_RATIO:
1021 if( p_vout )
1023 vlc_value_t val={0}, val_list, text_list;
1024 var_Get( p_vout, "aspect-ratio", &val );
1025 if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETCHOICES,
1026 &val_list, &text_list ) >= 0 )
1028 int i;
1029 for( i = 0; i < val_list.p_list->i_count; i++ )
1031 if( !strcmp( val_list.p_list->p_values[i].psz_string,
1032 val.psz_string ) )
1034 i++;
1035 break;
1038 if( i == val_list.p_list->i_count ) i = 0;
1039 var_SetString( p_vout, "aspect-ratio",
1040 val_list.p_list->p_values[i].psz_string );
1041 DisplayMessage( p_vout, _("Aspect ratio: %s"),
1042 text_list.p_list->p_values[i].psz_string );
1044 var_FreeList( &val_list, &text_list );
1046 free( val.psz_string );
1048 break;
1050 case ACTIONID_CROP:
1051 if( p_vout )
1053 vlc_value_t val={0}, val_list, text_list;
1054 var_Get( p_vout, "crop", &val );
1055 if( var_Change( p_vout, "crop", VLC_VAR_GETCHOICES,
1056 &val_list, &text_list ) >= 0 )
1058 int i;
1059 for( i = 0; i < val_list.p_list->i_count; i++ )
1061 if( !strcmp( val_list.p_list->p_values[i].psz_string,
1062 val.psz_string ) )
1064 i++;
1065 break;
1068 if( i == val_list.p_list->i_count ) i = 0;
1069 var_SetString( p_vout, "crop",
1070 val_list.p_list->p_values[i].psz_string );
1071 DisplayMessage( p_vout, _("Crop: %s"),
1072 text_list.p_list->p_values[i].psz_string );
1074 var_FreeList( &val_list, &text_list );
1076 free( val.psz_string );
1078 break;
1079 case ACTIONID_CROP_TOP:
1080 if( p_vout )
1081 var_IncInteger( p_vout, "crop-top" );
1082 break;
1083 case ACTIONID_UNCROP_TOP:
1084 if( p_vout )
1085 var_DecInteger( p_vout, "crop-top" );
1086 break;
1087 case ACTIONID_CROP_BOTTOM:
1088 if( p_vout )
1089 var_IncInteger( p_vout, "crop-bottom" );
1090 break;
1091 case ACTIONID_UNCROP_BOTTOM:
1092 if( p_vout )
1093 var_DecInteger( p_vout, "crop-bottom" );
1094 break;
1095 case ACTIONID_CROP_LEFT:
1096 if( p_vout )
1097 var_IncInteger( p_vout, "crop-left" );
1098 break;
1099 case ACTIONID_UNCROP_LEFT:
1100 if( p_vout )
1101 var_DecInteger( p_vout, "crop-left" );
1102 break;
1103 case ACTIONID_CROP_RIGHT:
1104 if( p_vout )
1105 var_IncInteger( p_vout, "crop-right" );
1106 break;
1107 case ACTIONID_UNCROP_RIGHT:
1108 if( p_vout )
1109 var_DecInteger( p_vout, "crop-right" );
1110 break;
1112 case ACTIONID_VIEWPOINT_FOV_IN:
1113 if( p_vout )
1114 input_UpdateViewpoint( p_input,
1115 &(vlc_viewpoint_t) { .fov = -1.f },
1116 false );
1117 break;
1118 case ACTIONID_VIEWPOINT_FOV_OUT:
1119 if( p_vout )
1120 input_UpdateViewpoint( p_input,
1121 &(vlc_viewpoint_t) { .fov = 1.f },
1122 false );
1123 break;
1125 case ACTIONID_VIEWPOINT_ROLL_CLOCK:
1126 if( p_vout )
1127 input_UpdateViewpoint( p_input,
1128 &(vlc_viewpoint_t) { .roll = -1.f },
1129 false );
1130 break;
1131 case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
1132 if( p_vout )
1133 input_UpdateViewpoint( p_input,
1134 &(vlc_viewpoint_t) { .roll = 1.f },
1135 false );
1136 break;
1138 case ACTIONID_TOGGLE_AUTOSCALE:
1139 if( p_vout )
1141 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1142 if ( f_scalefactor != 1.f )
1144 var_SetFloat( p_vout, "zoom", 1.f );
1145 DisplayMessage( p_vout, _("Zooming reset") );
1147 else
1149 bool b_autoscale = !var_GetBool( p_vout, "autoscale" );
1150 var_SetBool( p_vout, "autoscale", b_autoscale );
1151 if( b_autoscale )
1152 DisplayMessage( p_vout, _("Scaled to screen") );
1153 else
1154 DisplayMessage( p_vout, _("Original Size") );
1157 break;
1158 case ACTIONID_SCALE_UP:
1159 if( p_vout )
1161 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1163 if( f_scalefactor < 10.f )
1164 f_scalefactor += .1f;
1165 var_SetFloat( p_vout, "zoom", f_scalefactor );
1167 break;
1168 case ACTIONID_SCALE_DOWN:
1169 if( p_vout )
1171 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1173 if( f_scalefactor > .3f )
1174 f_scalefactor -= .1f;
1175 var_SetFloat( p_vout, "zoom", f_scalefactor );
1177 break;
1179 case ACTIONID_ZOOM_QUARTER:
1180 case ACTIONID_ZOOM_HALF:
1181 case ACTIONID_ZOOM_ORIGINAL:
1182 case ACTIONID_ZOOM_DOUBLE:
1183 if( p_vout )
1185 float f;
1186 switch( i_action )
1188 case ACTIONID_ZOOM_QUARTER: f = 0.25; break;
1189 case ACTIONID_ZOOM_HALF: f = 0.5; break;
1190 case ACTIONID_ZOOM_ORIGINAL: f = 1.; break;
1191 /*case ACTIONID_ZOOM_DOUBLE:*/
1192 default: f = 2.; break;
1194 var_SetFloat( p_vout, "zoom", f );
1196 break;
1197 case ACTIONID_ZOOM:
1198 case ACTIONID_UNZOOM:
1199 if( p_vout )
1201 vlc_value_t val={0}, val_list, text_list;
1202 var_Get( p_vout, "zoom", &val );
1203 if( var_Change( p_vout, "zoom", VLC_VAR_GETCHOICES,
1204 &val_list, &text_list ) >= 0 )
1206 int i;
1207 for( i = 0; i < val_list.p_list->i_count; i++ )
1209 if( val_list.p_list->p_values[i].f_float
1210 == val.f_float )
1212 if( i_action == ACTIONID_ZOOM )
1213 i++;
1214 else /* ACTIONID_UNZOOM */
1215 i--;
1216 break;
1219 if( i == val_list.p_list->i_count ) i = 0;
1220 if( i == -1 ) i = val_list.p_list->i_count-1;
1221 var_SetFloat( p_vout, "zoom",
1222 val_list.p_list->p_values[i].f_float );
1223 DisplayMessage( p_vout, _("Zoom mode: %s"),
1224 text_list.p_list->p_values[i].psz_string );
1226 var_FreeList( &val_list, &text_list );
1229 break;
1231 case ACTIONID_DEINTERLACE:
1232 if( p_vout )
1234 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1235 if( i_deinterlace != 0 )
1237 var_SetInteger( p_vout, "deinterlace", 0 );
1238 DisplayMessage( p_vout, _("Deinterlace off") );
1240 else
1242 var_SetInteger( p_vout, "deinterlace", 1 );
1244 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
1245 vlc_value_t vlist, tlist;
1246 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ) )
1248 const char *psz_text = NULL;
1249 for( int i = 0; i < vlist.p_list->i_count; i++ )
1251 if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
1253 psz_text = tlist.p_list->p_values[i].psz_string;
1254 break;
1257 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
1258 psz_text ? psz_text : psz_mode );
1260 var_FreeList( &vlist, &tlist );
1262 free( psz_mode );
1265 break;
1266 case ACTIONID_DEINTERLACE_MODE:
1267 if( p_vout )
1269 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
1270 vlc_value_t vlist, tlist;
1271 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ))
1273 const char *psz_text = NULL;
1274 int i;
1275 for( i = 0; i < vlist.p_list->i_count; i++ )
1277 if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
1279 i++;
1280 break;
1283 if( i == vlist.p_list->i_count ) i = 0;
1284 psz_text = tlist.p_list->p_values[i].psz_string;
1285 var_SetString( p_vout, "deinterlace-mode", vlist.p_list->p_values[i].psz_string );
1287 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1288 if( i_deinterlace != 0 )
1290 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
1291 psz_text ? psz_text : psz_mode );
1293 else
1295 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace off"),
1296 psz_text ? psz_text : psz_mode );
1299 var_FreeList( &vlist, &tlist );
1301 free( psz_mode );
1303 break;
1305 case ACTIONID_SUBPOS_DOWN:
1306 case ACTIONID_SUBPOS_UP:
1308 if( p_input )
1310 vlc_value_t val, list, list2;
1311 int i_count;
1312 var_Get( p_input, "spu-es", &val );
1314 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
1315 &list, &list2 );
1316 i_count = list.p_list->i_count;
1317 if( i_count < 1 || val.i_int < 0 )
1319 DisplayMessage( p_vout,
1320 _("Subtitle position: no active subtitle") );
1321 var_FreeList( &list, &list2 );
1322 break;
1325 int i_pos;
1326 if( i_action == ACTIONID_SUBPOS_DOWN )
1327 i_pos = var_DecInteger( p_vout, "sub-margin" );
1328 else
1329 i_pos = var_IncInteger( p_vout, "sub-margin" );
1331 ClearChannels( p_vout, slider_chan );
1332 DisplayMessage( p_vout, _( "Subtitle position %d px" ), i_pos );
1333 var_FreeList( &list, &list2 );
1335 break;
1338 case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN:
1339 case ACTIONID_SUBTITLE_TEXT_SCALE_UP:
1340 case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL:
1341 if( p_vout )
1343 int i_scale;
1344 if( i_action == ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL )
1346 i_scale = 100;
1348 else
1350 i_scale = var_GetInteger( p_playlist, "sub-text-scale" );
1351 i_scale += ((i_action == ACTIONID_SUBTITLE_TEXT_SCALE_UP) ? 1 : -1) * 25;
1352 i_scale = VLC_CLIP( i_scale, 10, 500 );
1354 var_SetInteger( p_playlist, "sub-text-scale", i_scale );
1355 DisplayMessage( p_vout, _( "Subtitle text scale %d%%" ), i_scale );
1357 break;
1359 /* Input + video output */
1360 case ACTIONID_POSITION:
1361 if( p_vout && vout_OSDEpg( p_vout, input_GetItem( p_input ) ) )
1362 DisplayPosition( p_vout, slider_chan, p_input );
1363 break;
1365 case ACTIONID_COMBO_VOL_FOV_UP:
1366 if( b_vrnav )
1367 DO_ACTION( ACTIONID_VIEWPOINT_FOV_IN );
1368 else
1369 DO_ACTION( ACTIONID_VOL_UP );
1370 break;
1371 case ACTIONID_COMBO_VOL_FOV_DOWN:
1372 if( b_vrnav )
1373 DO_ACTION( ACTIONID_VIEWPOINT_FOV_OUT );
1374 else
1375 DO_ACTION( ACTIONID_VOL_DOWN );
1376 break;
1379 return VLC_SUCCESS;
1382 /*****************************************************************************
1383 * ActionEvent: callback for hotkey actions
1384 *****************************************************************************/
1385 static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
1386 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1388 intf_thread_t *p_intf = (intf_thread_t *)p_data;
1389 intf_sys_t *p_sys = p_intf->p_sys;
1391 (void)libvlc;
1392 (void)psz_var;
1393 (void)oldval;
1395 vlc_mutex_lock( &p_intf->p_sys->lock );
1396 input_thread_t *p_input = p_sys->p_input ? vlc_object_hold( p_sys->p_input )
1397 : NULL;
1398 vout_thread_t *p_vout = p_sys->p_vout ? vlc_object_hold( p_sys->p_vout )
1399 : NULL;
1400 int slider_chan = p_sys->slider_chan;
1401 bool b_vrnav = p_sys->vrnav.b_can_change;
1402 vlc_mutex_unlock( &p_intf->p_sys->lock );
1404 int i_ret = PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav,
1405 newval.i_int );
1407 if( p_input != NULL )
1408 vlc_object_release( p_input );
1409 if( p_vout != NULL )
1410 vlc_object_release( p_vout );
1412 return i_ret;
1415 static void PlayBookmark( intf_thread_t *p_intf, int i_num )
1417 char *psz_bookmark_name;
1418 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
1419 return;
1421 playlist_t *p_playlist = pl_Get( p_intf );
1422 char *psz_bookmark = var_CreateGetString( p_intf, psz_bookmark_name );
1424 PL_LOCK;
1425 FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
1426 char *psz_uri = input_item_GetURI( p_item->p_input );
1427 if( !strcmp( psz_bookmark, psz_uri ) )
1429 free( psz_uri );
1430 playlist_ViewPlay( p_playlist, NULL, p_item );
1431 break;
1433 else
1434 free( psz_uri );
1435 FOREACH_END();
1436 PL_UNLOCK;
1438 free( psz_bookmark );
1439 free( psz_bookmark_name );
1442 static void SetBookmark( intf_thread_t *p_intf, int i_num )
1444 char *psz_bookmark_name;
1445 char *psz_uri = NULL;
1446 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
1447 return;
1449 playlist_t *p_playlist = pl_Get( p_intf );
1450 var_Create( p_intf, psz_bookmark_name,
1451 VLC_VAR_STRING|VLC_VAR_DOINHERIT );
1453 PL_LOCK;
1454 playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
1455 if( p_item ) psz_uri = input_item_GetURI( p_item->p_input );
1456 PL_UNLOCK;
1458 if( p_item )
1460 config_PutPsz( psz_bookmark_name, psz_uri);
1461 msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num, psz_uri);
1464 free( psz_uri );
1465 free( psz_bookmark_name );
1468 static void DisplayPosition( vout_thread_t *p_vout, int slider_chan,
1469 input_thread_t *p_input )
1471 char psz_duration[MSTRTIME_MAX_SIZE];
1472 char psz_time[MSTRTIME_MAX_SIZE];
1474 if( p_vout == NULL ) return;
1476 ClearChannels( p_vout, slider_chan );
1478 int64_t t = var_GetInteger( p_input, "time" ) / CLOCK_FREQ;
1479 int64_t l = var_GetInteger( p_input, "length" ) / CLOCK_FREQ;
1481 secstotimestr( psz_time, t );
1483 if( l > 0 )
1485 secstotimestr( psz_duration, l );
1486 DisplayMessage( p_vout, "%s / %s", psz_time, psz_duration );
1488 else if( t > 0 )
1490 DisplayMessage( p_vout, "%s", psz_time );
1493 if( var_GetBool( p_vout, "fullscreen" ) )
1495 vlc_value_t pos;
1496 var_Get( p_input, "position", &pos );
1497 vout_OSDSlider( p_vout, slider_chan,
1498 pos.f_float * 100, OSD_HOR_SLIDER );
1502 static void DisplayVolume( vout_thread_t *p_vout, int slider_chan, float vol )
1504 if( p_vout == NULL )
1505 return;
1506 ClearChannels( p_vout, slider_chan );
1508 if( var_GetBool( p_vout, "fullscreen" ) )
1509 vout_OSDSlider( p_vout, slider_chan,
1510 lroundf(vol * 100.f), OSD_VERT_SLIDER );
1511 DisplayMessage( p_vout, _( "Volume %ld%%" ), lroundf(vol * 100.f) );
1514 static void DisplayRate( vout_thread_t *p_vout, float f_rate )
1516 DisplayMessage( p_vout, _("Speed: %.2fx"), (double) f_rate );
1519 static float AdjustRateFine( vlc_object_t *p_obj, const int i_dir )
1521 const float f_rate_min = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MAX;
1522 const float f_rate_max = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MIN;
1523 float f_rate = var_GetFloat( p_obj, "rate" );
1525 int i_sign = f_rate < 0 ? -1 : 1;
1527 f_rate = floor( fabs(f_rate) / 0.1 + i_dir + 0.05 ) * 0.1;
1529 if( f_rate < f_rate_min )
1530 f_rate = f_rate_min;
1531 else if( f_rate > f_rate_max )
1532 f_rate = f_rate_max;
1533 f_rate *= i_sign;
1535 return f_rate;
1538 static void ClearChannels( vout_thread_t *p_vout, int slider_chan )
1540 if( p_vout )
1542 vout_FlushSubpictureChannel( p_vout, VOUT_SPU_CHANNEL_OSD );
1543 vout_FlushSubpictureChannel( p_vout, slider_chan );