1 /*****************************************************************************
2 * hotkeys.c: Hotkey handling for vlc
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
5 * $Id: 91ebde82c1a9018b5f73257024bada7a12328118 $
6 *
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * Jean-Paul Saman <jpsaman #_at_# m2x.nl>
9 *
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.
14 *
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.
19 *
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 *****************************************************************************/
24
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
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"
44
45 #include <assert.h>
46
47 /*****************************************************************************
48 * intf_sys_t: description and status of FB interface
49 *****************************************************************************/
50 struct intf_sys_t
51 {
52 vlc_mutex_t lock;
53 vout_thread_t *p_vout;
54 input_thread_t *p_input;
55 int slider_chan;
56
57 /*subtitle_delaybookmarks: placeholder for storing subtitle sync timestamps*/
58 struct
59 {
60 int64_t i_time_subtitle;
61 int64_t i_time_audio;
62 } subtitle_delaybookmarks;
63
64 struct
65 {
66 bool b_can_change;
67 bool b_button_pressed;
68 int x, y;
69 } vrnav;
70 };
71
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 );
86
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)
94
95 /*****************************************************************************
96 * Module descriptor
97 *****************************************************************************/
98
99 vlc_module_begin ()
100 set_shortname( N_("Hotkeys") )
101 set_description( N_("Hotkeys management interface") )
102 set_capability( "interface", 0 )
set_callbacks(Open,Close)103 set_callbacks( Open, Close )
104 set_category( CAT_INTERFACE )
105 set_subcategory( SUBCAT_INTERFACE_HOTKEYS )
106
107 vlc_module_end ()
108
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 )
111 {
112 intf_thread_t *p_intf = (intf_thread_t *)p_data;
113 intf_sys_t *p_sys = p_intf->p_sys;
114
115 (void) p_this; (void) psz_var; (void) oldval;
116
117 if( p_sys->vrnav.b_button_pressed )
118 {
119 int i_horizontal = newval.coords.x - p_sys->vrnav.x;
120 int i_vertical = newval.coords.y - p_sys->vrnav.y;
121
122 vlc_viewpoint_t viewpoint = {
123 .yaw = -i_horizontal * 0.05f,
124 .pitch = -i_vertical * 0.05f,
125 };
126
127 input_UpdateViewpoint( p_sys->p_input, &viewpoint, false );
128
129 p_sys->vrnav.x = newval.coords.x;
130 p_sys->vrnav.y = newval.coords.y;
131 }
132
133 return VLC_SUCCESS;
134 }
135
ViewpointMovedEvent(vlc_object_t * p_this,char const * psz_var,vlc_value_t oldval,vlc_value_t newval,void * p_data)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 )
139 {
140 intf_thread_t *p_intf = (intf_thread_t *)p_data;
141 intf_sys_t *p_sys = p_intf->p_sys;
142
143 (void) p_this; (void) psz_var; (void) oldval;
144
145 input_UpdateViewpoint( p_sys->p_input, newval.p_address, false );
146
147 return VLC_SUCCESS;
148 }
149
ButtonEvent(vlc_object_t * p_this,char const * psz_var,vlc_value_t oldval,vlc_value_t newval,void * p_data)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 )
152 {
153 intf_thread_t *p_intf = p_data;
154 intf_sys_t *p_sys = p_intf->p_sys;
155
156 (void) psz_var; (void) oldval;
157
158 if( newval.i_int & 0x01 )
159 {
160 if( !p_sys->vrnav.b_button_pressed )
161 {
162 p_sys->vrnav.b_button_pressed = true;
163 var_GetCoords( p_this, "mouse-moved",
164 &p_sys->vrnav.x, &p_sys->vrnav.y );
165 }
166 }
167 else
168 p_sys->vrnav.b_button_pressed = false;
169
170 return VLC_SUCCESS;
171 }
172
ChangeVout(intf_thread_t * p_intf,vout_thread_t * p_vout)173 static void ChangeVout( intf_thread_t *p_intf, vout_thread_t *p_vout )
174 {
175 intf_sys_t *p_sys = p_intf->p_sys;
176
177 int slider_chan;
178 bool b_vrnav_can_change;
179 if( p_vout != NULL )
180 {
181 slider_chan = vout_RegisterSubpictureChannel( p_vout );
182 b_vrnav_can_change = var_GetBool( p_vout, "viewpoint-changeable" );
183 }
184
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 )
190 {
191 p_sys->slider_chan = slider_chan;
192 p_sys->vrnav.b_can_change = b_vrnav_can_change;
193 }
194 else
195 p_sys->vrnav.b_can_change = false;
196 vlc_mutex_unlock( &p_sys->lock );
197
198 if( p_old_vout != NULL )
199 {
200 if( b_vrnav_could_change )
201 {
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 );
208 }
209 vlc_object_release( p_old_vout );
210 }
211
212 if( p_sys->vrnav.b_can_change )
213 {
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 );
221 }
222 }
223
InputEvent(vlc_object_t * p_this,char const * psz_var,vlc_value_t oldval,vlc_value_t val,void * p_data)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 )
226 {
227 input_thread_t *p_input = (input_thread_t *)p_this;
228 intf_thread_t *p_intf = p_data;
229
230 (void) psz_var; (void) oldval;
231
232 if( val.i_int == INPUT_EVENT_VOUT )
233 ChangeVout( p_intf, input_GetVout( p_input ) );
234
235 return VLC_SUCCESS;
236 }
237
ChangeInput(intf_thread_t * p_intf,input_thread_t * p_input)238 static void ChangeInput( intf_thread_t *p_intf, input_thread_t *p_input )
239 {
240 intf_sys_t *p_sys = p_intf->p_sys;
241
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 )
245 {
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 );
249
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 )
254 {
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 );
261 }
262 }
263
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 );
270
271 /* Release old input and vout objects unlocked */
272 if( p_old_input != NULL )
273 {
274 if( p_old_vout != NULL )
275 vlc_object_release( p_old_vout );
276 vlc_object_release( p_old_input );
277 }
278
279 /* Register input events */
280 if( p_input != NULL )
281 var_AddCallback( p_input, "intf-event", InputEvent, p_intf );
282 }
283
PlaylistEvent(vlc_object_t * p_this,char const * psz_var,vlc_value_t oldval,vlc_value_t val,void * p_data)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 )
286 {
287 intf_thread_t *p_intf = p_data;
288
289 (void) p_this; (void) psz_var; (void) oldval;
290
291 ChangeInput( p_intf, val.p_address );
292
293 return VLC_SUCCESS;
294 }
295
296 /*****************************************************************************
297 * Open: initialize interface
298 *****************************************************************************/
Open(vlc_object_t * p_this)299 static int Open( vlc_object_t *p_this )
300 {
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;
306
307 p_intf->p_sys = p_sys;
308
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;
315
316 vlc_mutex_init( &p_sys->lock );
317
318 var_AddCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
319
320 var_AddCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
321
322 return VLC_SUCCESS;
323 }
324
325 /*****************************************************************************
326 * Close: destroy interface
327 *****************************************************************************/
Close(vlc_object_t * p_this)328 static void Close( vlc_object_t *p_this )
329 {
330 intf_thread_t *p_intf = (intf_thread_t *)p_this;
331 intf_sys_t *p_sys = p_intf->p_sys;
332
333 var_DelCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
334
335 var_DelCallback( p_intf->obj.libvlc, "key-action", ActionEvent, p_intf );
336
337 ChangeInput( p_intf, NULL );
338
339 vlc_mutex_destroy( &p_sys->lock );
340
341 /* Destroy structure */
342 free( p_sys );
343 }
344
PutAction(intf_thread_t * p_intf,input_thread_t * p_input,vout_thread_t * p_vout,int slider_chan,bool b_vrnav,int i_action)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 )
348 {
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 );
352
353 /* Quit */
354 switch( i_action )
355 {
356 /* Libvlc / interface actions */
357 case ACTIONID_QUIT:
358 libvlc_Quit( p_intf->obj.libvlc );
359
360 ClearChannels( p_vout, slider_chan );
361 DisplayMessage( p_vout, _( "Quit" ) );
362 break;
363
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;
374
375 /* Playlist actions (including audio) */
376 case ACTIONID_LOOP:
377 {
378 /* Toggle Normal -> Loop -> Repeat -> Normal ... */
379 const char *mode;
380 if( var_GetBool( p_playlist, "repeat" ) )
381 {
382 var_SetBool( p_playlist, "repeat", false );
383 mode = N_("Off");
384 }
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");
391 }
392 else
393 {
394 var_SetBool( p_playlist, "loop", true );
395 mode = N_("All");
396 }
397 DisplayMessage( p_vout, _("Loop: %s"), vlc_gettext(mode) );
398 break;
399 }
400
401 case ACTIONID_RANDOM:
402 {
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;
407 }
408
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;
417
418 case ACTIONID_STOP:
419 playlist_Stop( p_playlist );
420 break;
421
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:
436 {
437 const int i_dir = i_action == ACTIONID_RATE_FASTER_FINE ? 1 : -1;
438 float rate = AdjustRateFine( VLC_OBJECT(p_playlist), i_dir );
439
440 var_SetFloat( p_playlist, "rate", rate );
441 DisplayRate( p_vout, rate );
442 break;
443 }
444
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;
457
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:
474 {
475 float vol;
476 if( playlist_VolumeUp( p_playlist, 1, &vol ) == 0 )
477 DisplayVolume( p_vout, slider_chan, vol );
478 break;
479 }
480 case ACTIONID_VOL_DOWN:
481 {
482 float vol;
483 if( playlist_VolumeDown( p_playlist, 1, &vol ) == 0 )
484 DisplayVolume( p_vout, slider_chan, vol );
485 break;
486 }
487 case ACTIONID_VOL_MUTE:
488 {
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;
495
496 float vol = playlist_VolumeGet( p_playlist );
497 if( mute || vol == 0.f )
498 {
499 ClearChannels( p_vout, slider_chan );
500 DisplayIcon( p_vout, OSD_MUTE_ICON );
501 }
502 else
503 DisplayVolume( p_vout, slider_chan, vol );
504 break;
505 }
506
507 case ACTIONID_AUDIODEVICE_CYCLE:
508 {
509 audio_output_t *p_aout = playlist_GetAout( p_playlist );
510 if( p_aout == NULL )
511 break;
512
513 char **ids, **names;
514 int n = aout_DevicesList( p_aout, &ids, &names );
515 if( n == -1 )
516 break;
517
518 char *dev = aout_DeviceGet( p_aout );
519 const char *devstr = (dev != NULL) ? dev : "";
520
521 int idx = 0;
522 for( int i = 0; i < n; i++ )
523 {
524 if( !strcmp(devstr, ids[i]) )
525 idx = (i + 1) % n;
526 }
527 free( dev );
528
529 if( !aout_DeviceSet( p_aout, ids[idx] ) )
530 DisplayMessage( p_vout, _("Audio Device: %s"), names[idx] );
531 vlc_object_release( p_aout );
532
533 for( int i = 0; i < n; i++ )
534 {
535 free( ids[i] );
536 free( names[i] );
537 }
538 free( ids );
539 free( names );
540 break;
541 }
542
543 /* Playlist + input actions */
544 case ACTIONID_PLAY_PAUSE:
545 if( p_input )
546 {
547 ClearChannels( p_vout, slider_chan );
548
549 int state = var_GetInteger( p_input, "state" );
550 DisplayIcon( p_vout, state != PAUSE_S ? OSD_PAUSE_ICON : OSD_PLAY_ICON );
551 }
552 playlist_TogglePause( p_playlist );
553 break;
554
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
560 {
561 ClearChannels( p_vout, slider_chan );
562 DisplayIcon( p_vout, OSD_PLAY_ICON );
563 playlist_Play( p_playlist );
564 }
565 break;
566
567 /* Playlist + video output actions */
568 case ACTIONID_WALLPAPER:
569 {
570 bool wp = var_ToggleBool( p_playlist, "video-wallpaper" );
571 if( p_vout )
572 var_SetBool( p_vout, "video-wallpaper", wp );
573 break;
574 }
575
576 /* Input actions */
577 case ACTIONID_PAUSE:
578 if( p_input && var_GetInteger( p_input, "state" ) != PAUSE_S )
579 {
580 ClearChannels( p_vout, slider_chan );
581 DisplayIcon( p_vout, OSD_PAUSE_ICON );
582 var_SetInteger( p_input, "state", PAUSE_S );
583 }
584 break;
585
586 case ACTIONID_RECORD:
587 if( p_input && var_GetBool( p_input, "can-record" ) )
588 {
589 const bool on = var_ToggleBool( p_input, "record" );
590 DisplayMessage( p_vout, vlc_gettext(on
591 ? N_("Recording") : N_("Recording done")) );
592 }
593 break;
594
595 case ACTIONID_FRAME_NEXT:
596 if( p_input )
597 {
598 var_TriggerCallback( p_input, "frame-next" );
599 DisplayMessage( p_vout, _("Next frame") );
600 }
601 break;
602
603 case ACTIONID_SUBSYNC_MARKAUDIO:
604 {
605 p_sys->subtitle_delaybookmarks.i_time_audio = mdate();
606 DisplayMessage( p_vout, _("Sub sync: bookmarked audio time"));
607 break;
608 }
609 case ACTIONID_SUBSYNC_MARKSUB:
610 if( p_input )
611 {
612 vlc_value_t val, list, list2;
613 int i_count;
614 var_Get( p_input, "spu-es", &val );
615
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 )
620 {
621 DisplayMessage( p_vout, _("No active subtitle") );
622 var_FreeList( &list, &list2 );
623 break;
624 }
625 p_sys->subtitle_delaybookmarks.i_time_subtitle = mdate();
626 DisplayMessage( p_vout,
627 _("Sub sync: bookmarked subtitle time"));
628 var_FreeList( &list, &list2 );
629 }
630 break;
631 case ACTIONID_SUBSYNC_APPLY:
632 {
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 )
644 {
645 if ( (p_sys->subtitle_delaybookmarks.i_time_audio == 0) || (p_sys->subtitle_delaybookmarks.i_time_subtitle == 0) )
646 {
647 DisplayMessage( p_vout, _( "Sub sync: set bookmarks first!" ) );
648 }
649 else
650 {
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;
661 }
662 }
663 break;
664 }
665 case ACTIONID_SUBSYNC_RESET:
666 {
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;
673 }
674
675 case ACTIONID_SUBDELAY_DOWN:
676 case ACTIONID_SUBDELAY_UP:
677 {
678 int diff = (i_action == ACTIONID_SUBDELAY_UP) ? 50000 : -50000;
679 if( p_input )
680 {
681 vlc_value_t val, list, list2;
682 int i_count;
683 var_Get( p_input, "spu-es", &val );
684
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 )
689 {
690 DisplayMessage( p_vout, _("No active subtitle") );
691 var_FreeList( &list, &list2 );
692 break;
693 }
694 int64_t i_delay = var_GetInteger( p_input, "spu-delay" ) + diff;
695
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 );
701 }
702 break;
703 }
704 case ACTIONID_AUDIODELAY_DOWN:
705 case ACTIONID_AUDIODELAY_UP:
706 {
707 int diff = (i_action == ACTIONID_AUDIODELAY_UP) ? 50000 : -50000;
708 if( p_input )
709 {
710 int64_t i_delay = var_GetInteger( p_input, "audio-delay" )
711 + diff;
712
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) );
717 }
718 break;
719 }
720
721 case ACTIONID_AUDIO_TRACK:
722 if( p_input )
723 {
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 )
731 {
732 for( i = 0; i < i_count; i++ )
733 {
734 if( val.i_int == list.p_list->p_values[i].i_int )
735 {
736 break;
737 }
738 }
739 /* value of audio-es was not in choices list */
740 if( i == i_count )
741 {
742 msg_Warn( p_input,
743 "invalid current audio track, selecting 0" );
744 i = 0;
745 }
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 );
753 }
754 var_FreeList( &list, &list2 );
755 }
756 break;
757
758 case ACTIONID_SUBTITLE_TRACK:
759 case ACTIONID_SUBTITLE_REVERSE_TRACK:
760 if( p_input )
761 {
762 vlc_value_t val, list, list2;
763 int i_count, i;
764 var_Get( p_input, "spu-es", &val );
765
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 )
770 {
771 DisplayMessage( p_vout, _("Subtitle track: %s"),
772 _("N/A") );
773 var_FreeList( &list, &list2 );
774 break;
775 }
776 for( i = 0; i < i_count; i++ )
777 {
778 if( val.i_int == list.p_list->p_values[i].i_int )
779 {
780 break;
781 }
782 }
783 /* value of spu-es was not in choices list */
784 if( i == i_count )
785 {
786 msg_Warn( p_input,
787 "invalid current subtitle track, selecting 0" );
788 i = 0;
789 }
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 DisplayMessage( p_vout, _("Subtitle track: %s"),
798 list2.p_list->p_values[i].psz_string );
799 var_FreeList( &list, &list2 );
800 }
801 break;
802 case ACTIONID_SUBTITLE_TOGGLE:
803 if( p_input )
804 {
805 vlc_value_t list, list2;
806 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
807 &list, &list2 );
808 int i_count = list.p_list->i_count;
809 if( i_count <= 1 )
810 {
811 DisplayMessage( p_vout, _("Subtitle track: %s"),
812 _("N/A") );
813 var_FreeList( &list, &list2 );
814 break;
815 }
816
817 int i_cur_id = var_GetInteger( p_input, "spu-es" );
818 int i_new_id;
819 if( i_cur_id == -1 )
820 {
821 /* subtitles were disabled: restore the saved track id */
822 i_new_id = var_GetInteger( p_input, "spu-choice" );
823 if( i_new_id != -1 )
824 var_SetInteger( p_input, "spu-choice", -1 );
825 }
826 else
827 {
828 /* subtitles were enabled: save the track id and disable */
829 i_new_id = -1;
830 var_SetInteger( p_input, "spu-choice", i_cur_id );
831 }
832
833 int i_new_index = 1; /* select first track by default */
834 /* if subtitles were disabled with no saved id, use the first track */
835 if( i_cur_id != -1 || i_new_id != -1 )
836 {
837 for( int i = 0; i < i_count; ++i )
838 {
839 if( i_new_id == list.p_list->p_values[i].i_int )
840 {
841 i_new_index = i;
842 break;
843 }
844 }
845 }
846 var_SetInteger( p_input, "spu-es", list.p_list->p_values[i_new_index].i_int );
847 DisplayMessage( p_vout, _("Subtitle track: %s"),
848 list2.p_list->p_values[i_new_index].psz_string );
849 var_FreeList( &list, &list2 );
850 }
851 break;
852 case ACTIONID_PROGRAM_SID_NEXT:
853 case ACTIONID_PROGRAM_SID_PREV:
854 if( p_input )
855 {
856 vlc_value_t val, list, list2;
857 int i_count, i;
858 var_Get( p_input, "program", &val );
859
860 var_Change( p_input, "program", VLC_VAR_GETCHOICES,
861 &list, &list2 );
862 i_count = list.p_list->i_count;
863 if( i_count <= 1 )
864 {
865 DisplayMessage( p_vout, _("Program Service ID: %s"),
866 _("N/A") );
867 var_FreeList( &list, &list2 );
868 break;
869 }
870 for( i = 0; i < i_count; i++ )
871 {
872 if( val.i_int == list.p_list->p_values[i].i_int )
873 {
874 break;
875 }
876 }
877 /* value of program sid was not in choices list */
878 if( i == i_count )
879 {
880 msg_Warn( p_input,
881 "invalid current program SID, selecting 0" );
882 i = 0;
883 }
884 else if( i_action == ACTIONID_PROGRAM_SID_NEXT ) {
885 if( i == i_count - 1 )
886 i = 0;
887 else
888 i++;
889 }
890 else { /* ACTIONID_PROGRAM_SID_PREV */
891 if( i == 0 )
892 i = i_count - 1;
893 else
894 i--;
895 }
896 var_Set( p_input, "program", list.p_list->p_values[i] );
897 DisplayMessage( p_vout, _("Program Service ID: %s"),
898 list2.p_list->p_values[i].psz_string );
899 var_FreeList( &list, &list2 );
900 }
901 break;
902
903 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
904 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
905 case ACTIONID_JUMP_BACKWARD_SHORT:
906 case ACTIONID_JUMP_FORWARD_SHORT:
907 case ACTIONID_JUMP_BACKWARD_MEDIUM:
908 case ACTIONID_JUMP_FORWARD_MEDIUM:
909 case ACTIONID_JUMP_BACKWARD_LONG:
910 case ACTIONID_JUMP_FORWARD_LONG:
911 {
912 if( p_input == NULL || !var_GetBool( p_input, "can-seek" ) )
913 break;
914
915 const char *varname;
916 int sign = +1;
917 switch( i_action )
918 {
919 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
920 sign = -1;
921 /* fall through */
922 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
923 varname = "extrashort-jump-size";
924 break;
925 case ACTIONID_JUMP_BACKWARD_SHORT:
926 sign = -1;
927 /* fall through */
928 case ACTIONID_JUMP_FORWARD_SHORT:
929 varname = "short-jump-size";
930 break;
931 case ACTIONID_JUMP_BACKWARD_MEDIUM:
932 sign = -1;
933 /* fall through */
934 case ACTIONID_JUMP_FORWARD_MEDIUM:
935 varname = "medium-jump-size";
936 break;
937 case ACTIONID_JUMP_BACKWARD_LONG:
938 sign = -1;
939 /* fall through */
940 case ACTIONID_JUMP_FORWARD_LONG:
941 varname = "long-jump-size";
942 break;
943 }
944
945 mtime_t it = var_InheritInteger( p_input, varname );
946 if( it < 0 )
947 break;
948 var_SetInteger( p_input, "time-offset", it * sign * CLOCK_FREQ );
949 DisplayPosition( p_vout, slider_chan, p_input );
950 break;
951 }
952
953 /* Input navigation */
954 case ACTIONID_TITLE_PREV:
955 if( p_input )
956 var_TriggerCallback( p_input, "prev-title" );
957 break;
958 case ACTIONID_TITLE_NEXT:
959 if( p_input )
960 var_TriggerCallback( p_input, "next-title" );
961 break;
962 case ACTIONID_CHAPTER_PREV:
963 if( p_input )
964 var_TriggerCallback( p_input, "prev-chapter" );
965 break;
966 case ACTIONID_CHAPTER_NEXT:
967 if( p_input )
968 var_TriggerCallback( p_input, "next-chapter" );
969 break;
970 case ACTIONID_DISC_MENU:
971 if( p_input )
972 var_SetInteger( p_input, "title 0", 2 );
973 break;
974 case ACTIONID_NAV_ACTIVATE:
975 if( p_input )
976 input_Control( p_input, INPUT_NAV_ACTIVATE, NULL );
977 break;
978 case ACTIONID_NAV_UP:
979 if( p_input )
980 input_Control( p_input, INPUT_NAV_UP, NULL );
981 break;
982 case ACTIONID_NAV_DOWN:
983 if( p_input )
984 input_Control( p_input, INPUT_NAV_DOWN, NULL );
985 break;
986 case ACTIONID_NAV_LEFT:
987 if( p_input )
988 input_Control( p_input, INPUT_NAV_LEFT, NULL );
989 break;
990 case ACTIONID_NAV_RIGHT:
991 if( p_input )
992 input_Control( p_input, INPUT_NAV_RIGHT, NULL );
993 break;
994
995 /* Video Output actions */
996 case ACTIONID_SNAPSHOT:
997 if( p_vout )
998 var_TriggerCallback( p_vout, "video-snapshot" );
999 break;
1000
1001 case ACTIONID_TOGGLE_FULLSCREEN:
1002 {
1003 if( p_vout )
1004 {
1005 bool fs = var_ToggleBool( p_vout, "fullscreen" );
1006 var_SetBool( p_playlist, "fullscreen", fs );
1007 }
1008 else
1009 var_ToggleBool( p_playlist, "fullscreen" );
1010 break;
1011 }
1012
1013 case ACTIONID_LEAVE_FULLSCREEN:
1014 if( p_vout )
1015 var_SetBool( p_vout, "fullscreen", false );
1016 var_SetBool( p_playlist, "fullscreen", false );
1017 break;
1018
1019 case ACTIONID_ASPECT_RATIO:
1020 if( p_vout )
1021 {
1022 vlc_value_t val={0}, val_list, text_list;
1023 var_Get( p_vout, "aspect-ratio", &val );
1024 if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETCHOICES,
1025 &val_list, &text_list ) >= 0 )
1026 {
1027 int i;
1028 for( i = 0; i < val_list.p_list->i_count; i++ )
1029 {
1030 if( !strcmp( val_list.p_list->p_values[i].psz_string,
1031 val.psz_string ) )
1032 {
1033 i++;
1034 break;
1035 }
1036 }
1037 if( i == val_list.p_list->i_count ) i = 0;
1038 var_SetString( p_vout, "aspect-ratio",
1039 val_list.p_list->p_values[i].psz_string );
1040 DisplayMessage( p_vout, _("Aspect ratio: %s"),
1041 text_list.p_list->p_values[i].psz_string );
1042
1043 var_FreeList( &val_list, &text_list );
1044 }
1045 free( val.psz_string );
1046 }
1047 break;
1048
1049 case ACTIONID_CROP:
1050 if( p_vout )
1051 {
1052 vlc_value_t val={0}, val_list, text_list;
1053 var_Get( p_vout, "crop", &val );
1054 if( var_Change( p_vout, "crop", VLC_VAR_GETCHOICES,
1055 &val_list, &text_list ) >= 0 )
1056 {
1057 int i;
1058 for( i = 0; i < val_list.p_list->i_count; i++ )
1059 {
1060 if( !strcmp( val_list.p_list->p_values[i].psz_string,
1061 val.psz_string ) )
1062 {
1063 i++;
1064 break;
1065 }
1066 }
1067 if( i == val_list.p_list->i_count ) i = 0;
1068 var_SetString( p_vout, "crop",
1069 val_list.p_list->p_values[i].psz_string );
1070 DisplayMessage( p_vout, _("Crop: %s"),
1071 text_list.p_list->p_values[i].psz_string );
1072
1073 var_FreeList( &val_list, &text_list );
1074 }
1075 free( val.psz_string );
1076 }
1077 break;
1078 case ACTIONID_CROP_TOP:
1079 if( p_vout )
1080 var_IncInteger( p_vout, "crop-top" );
1081 break;
1082 case ACTIONID_UNCROP_TOP:
1083 if( p_vout )
1084 var_DecInteger( p_vout, "crop-top" );
1085 break;
1086 case ACTIONID_CROP_BOTTOM:
1087 if( p_vout )
1088 var_IncInteger( p_vout, "crop-bottom" );
1089 break;
1090 case ACTIONID_UNCROP_BOTTOM:
1091 if( p_vout )
1092 var_DecInteger( p_vout, "crop-bottom" );
1093 break;
1094 case ACTIONID_CROP_LEFT:
1095 if( p_vout )
1096 var_IncInteger( p_vout, "crop-left" );
1097 break;
1098 case ACTIONID_UNCROP_LEFT:
1099 if( p_vout )
1100 var_DecInteger( p_vout, "crop-left" );
1101 break;
1102 case ACTIONID_CROP_RIGHT:
1103 if( p_vout )
1104 var_IncInteger( p_vout, "crop-right" );
1105 break;
1106 case ACTIONID_UNCROP_RIGHT:
1107 if( p_vout )
1108 var_DecInteger( p_vout, "crop-right" );
1109 break;
1110
1111 case ACTIONID_VIEWPOINT_FOV_IN:
1112 if( p_vout )
1113 input_UpdateViewpoint( p_input,
1114 &(vlc_viewpoint_t) { .fov = -1.f },
1115 false );
1116 break;
1117 case ACTIONID_VIEWPOINT_FOV_OUT:
1118 if( p_vout )
1119 input_UpdateViewpoint( p_input,
1120 &(vlc_viewpoint_t) { .fov = 1.f },
1121 false );
1122 break;
1123
1124 case ACTIONID_VIEWPOINT_ROLL_CLOCK:
1125 if( p_vout )
1126 input_UpdateViewpoint( p_input,
1127 &(vlc_viewpoint_t) { .roll = -1.f },
1128 false );
1129 break;
1130 case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
1131 if( p_vout )
1132 input_UpdateViewpoint( p_input,
1133 &(vlc_viewpoint_t) { .roll = 1.f },
1134 false );
1135 break;
1136
1137 case ACTIONID_TOGGLE_AUTOSCALE:
1138 if( p_vout )
1139 {
1140 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1141 if ( f_scalefactor != 1.f )
1142 {
1143 var_SetFloat( p_vout, "zoom", 1.f );
1144 DisplayMessage( p_vout, _("Zooming reset") );
1145 }
1146 else
1147 {
1148 bool b_autoscale = !var_GetBool( p_vout, "autoscale" );
1149 var_SetBool( p_vout, "autoscale", b_autoscale );
1150 if( b_autoscale )
1151 DisplayMessage( p_vout, _("Scaled to screen") );
1152 else
1153 DisplayMessage( p_vout, _("Original Size") );
1154 }
1155 }
1156 break;
1157 case ACTIONID_SCALE_UP:
1158 if( p_vout )
1159 {
1160 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1161
1162 if( f_scalefactor < 10.f )
1163 f_scalefactor += .1f;
1164 var_SetFloat( p_vout, "zoom", f_scalefactor );
1165 }
1166 break;
1167 case ACTIONID_SCALE_DOWN:
1168 if( p_vout )
1169 {
1170 float f_scalefactor = var_GetFloat( p_vout, "zoom" );
1171
1172 if( f_scalefactor > .3f )
1173 f_scalefactor -= .1f;
1174 var_SetFloat( p_vout, "zoom", f_scalefactor );
1175 }
1176 break;
1177
1178 case ACTIONID_ZOOM_QUARTER:
1179 case ACTIONID_ZOOM_HALF:
1180 case ACTIONID_ZOOM_ORIGINAL:
1181 case ACTIONID_ZOOM_DOUBLE:
1182 if( p_vout )
1183 {
1184 float f;
1185 switch( i_action )
1186 {
1187 case ACTIONID_ZOOM_QUARTER: f = 0.25; break;
1188 case ACTIONID_ZOOM_HALF: f = 0.5; break;
1189 case ACTIONID_ZOOM_ORIGINAL: f = 1.; break;
1190 /*case ACTIONID_ZOOM_DOUBLE:*/
1191 default: f = 2.; break;
1192 }
1193 var_SetFloat( p_vout, "zoom", f );
1194 }
1195 break;
1196 case ACTIONID_ZOOM:
1197 case ACTIONID_UNZOOM:
1198 if( p_vout )
1199 {
1200 vlc_value_t val={0}, val_list, text_list;
1201 var_Get( p_vout, "zoom", &val );
1202 if( var_Change( p_vout, "zoom", VLC_VAR_GETCHOICES,
1203 &val_list, &text_list ) >= 0 )
1204 {
1205 int i;
1206 for( i = 0; i < val_list.p_list->i_count; i++ )
1207 {
1208 if( val_list.p_list->p_values[i].f_float
1209 == val.f_float )
1210 {
1211 if( i_action == ACTIONID_ZOOM )
1212 i++;
1213 else /* ACTIONID_UNZOOM */
1214 i--;
1215 break;
1216 }
1217 }
1218 if( i == val_list.p_list->i_count ) i = 0;
1219 if( i == -1 ) i = val_list.p_list->i_count-1;
1220 var_SetFloat( p_vout, "zoom",
1221 val_list.p_list->p_values[i].f_float );
1222 DisplayMessage( p_vout, _("Zoom mode: %s"),
1223 text_list.p_list->p_values[i].psz_string );
1224
1225 var_FreeList( &val_list, &text_list );
1226 }
1227 }
1228 break;
1229
1230 case ACTIONID_DEINTERLACE:
1231 if( p_vout )
1232 {
1233 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1234 if( i_deinterlace != 0 )
1235 {
1236 var_SetInteger( p_vout, "deinterlace", 0 );
1237 DisplayMessage( p_vout, _("Deinterlace off") );
1238 }
1239 else
1240 {
1241 var_SetInteger( p_vout, "deinterlace", 1 );
1242
1243 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
1244 vlc_value_t vlist, tlist;
1245 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ) )
1246 {
1247 const char *psz_text = NULL;
1248 for( int i = 0; i < vlist.p_list->i_count; i++ )
1249 {
1250 if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
1251 {
1252 psz_text = tlist.p_list->p_values[i].psz_string;
1253 break;
1254 }
1255 }
1256 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
1257 psz_text ? psz_text : psz_mode );
1258
1259 var_FreeList( &vlist, &tlist );
1260 }
1261 free( psz_mode );
1262 }
1263 }
1264 break;
1265 case ACTIONID_DEINTERLACE_MODE:
1266 if( p_vout )
1267 {
1268 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
1269 vlc_value_t vlist, tlist;
1270 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ))
1271 {
1272 const char *psz_text = NULL;
1273 int i;
1274 for( i = 0; i < vlist.p_list->i_count; i++ )
1275 {
1276 if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
1277 {
1278 i++;
1279 break;
1280 }
1281 }
1282 if( i == vlist.p_list->i_count ) i = 0;
1283 psz_text = tlist.p_list->p_values[i].psz_string;
1284 var_SetString( p_vout, "deinterlace-mode", vlist.p_list->p_values[i].psz_string );
1285
1286 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
1287 if( i_deinterlace != 0 )
1288 {
1289 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace on"),
1290 psz_text ? psz_text : psz_mode );
1291 }
1292 else
1293 {
1294 DisplayMessage( p_vout, "%s (%s)", _("Deinterlace off"),
1295 psz_text ? psz_text : psz_mode );
1296 }
1297
1298 var_FreeList( &vlist, &tlist );
1299 }
1300 free( psz_mode );
1301 }
1302 break;
1303
1304 case ACTIONID_SUBPOS_DOWN:
1305 case ACTIONID_SUBPOS_UP:
1306 {
1307 if( p_input )
1308 {
1309 vlc_value_t val, list, list2;
1310 int i_count;
1311 var_Get( p_input, "spu-es", &val );
1312
1313 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
1314 &list, &list2 );
1315 i_count = list.p_list->i_count;
1316 if( i_count < 1 || val.i_int < 0 )
1317 {
1318 DisplayMessage( p_vout,
1319 _("Subtitle position: no active subtitle") );
1320 var_FreeList( &list, &list2 );
1321 break;
1322 }
1323
1324 int i_pos;
1325 if( i_action == ACTIONID_SUBPOS_DOWN )
1326 i_pos = var_DecInteger( p_vout, "sub-margin" );
1327 else
1328 i_pos = var_IncInteger( p_vout, "sub-margin" );
1329
1330 ClearChannels( p_vout, slider_chan );
1331 DisplayMessage( p_vout, _( "Subtitle position %d px" ), i_pos );
1332 var_FreeList( &list, &list2 );
1333 }
1334 break;
1335 }
1336
1337 case ACTIONID_SUBTITLE_TEXT_SCALE_DOWN:
1338 case ACTIONID_SUBTITLE_TEXT_SCALE_UP:
1339 case ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL:
1340 if( p_vout )
1341 {
1342 int i_scale;
1343 if( i_action == ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL )
1344 {
1345 i_scale = 100;
1346 }
1347 else
1348 {
1349 i_scale = var_GetInteger( p_playlist, "sub-text-scale" );
1350 unsigned increment = ((i_scale > 100 ? i_scale - 100 : 100 - i_scale) / 25) <= 1 ? 10 : 25;
1351 i_scale += ((i_action == ACTIONID_SUBTITLE_TEXT_SCALE_UP) ? 1 : -1) * increment;
1352 i_scale -= i_scale % increment;
1353 i_scale = VLC_CLIP( i_scale, 25, 500 );
1354 }
1355 var_SetInteger( p_playlist, "sub-text-scale", i_scale );
1356 DisplayMessage( p_vout, _( "Subtitle text scale %d%%" ), i_scale );
1357 }
1358 break;
1359
1360 /* Input + video output */
1361 case ACTIONID_POSITION:
1362 if( p_vout && vout_OSDEpg( p_vout, input_GetItem( p_input ) ) )
1363 DisplayPosition( p_vout, slider_chan, p_input );
1364 break;
1365
1366 case ACTIONID_COMBO_VOL_FOV_UP:
1367 if( b_vrnav )
1368 DO_ACTION( ACTIONID_VIEWPOINT_FOV_IN );
1369 else
1370 DO_ACTION( ACTIONID_VOL_UP );
1371 break;
1372 case ACTIONID_COMBO_VOL_FOV_DOWN:
1373 if( b_vrnav )
1374 DO_ACTION( ACTIONID_VIEWPOINT_FOV_OUT );
1375 else
1376 DO_ACTION( ACTIONID_VOL_DOWN );
1377 break;
1378 }
1379
1380 return VLC_SUCCESS;
1381 }
1382
1383 /*****************************************************************************
1384 * ActionEvent: callback for hotkey actions
1385 *****************************************************************************/
ActionEvent(vlc_object_t * libvlc,char const * psz_var,vlc_value_t oldval,vlc_value_t newval,void * p_data)1386 static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
1387 vlc_value_t oldval, vlc_value_t newval, void *p_data )
1388 {
1389 intf_thread_t *p_intf = (intf_thread_t *)p_data;
1390 intf_sys_t *p_sys = p_intf->p_sys;
1391
1392 (void)libvlc;
1393 (void)psz_var;
1394 (void)oldval;
1395
1396 vlc_mutex_lock( &p_intf->p_sys->lock );
1397 input_thread_t *p_input = p_sys->p_input ? vlc_object_hold( p_sys->p_input )
1398 : NULL;
1399 vout_thread_t *p_vout = p_sys->p_vout ? vlc_object_hold( p_sys->p_vout )
1400 : NULL;
1401 int slider_chan = p_sys->slider_chan;
1402 bool b_vrnav = p_sys->vrnav.b_can_change;
1403 vlc_mutex_unlock( &p_intf->p_sys->lock );
1404
1405 int i_ret = PutAction( p_intf, p_input, p_vout, slider_chan, b_vrnav,
1406 newval.i_int );
1407
1408 if( p_input != NULL )
1409 vlc_object_release( p_input );
1410 if( p_vout != NULL )
1411 vlc_object_release( p_vout );
1412
1413 return i_ret;
1414 }
1415
PlayBookmark(intf_thread_t * p_intf,int i_num)1416 static void PlayBookmark( intf_thread_t *p_intf, int i_num )
1417 {
1418 char *psz_bookmark_name;
1419 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
1420 return;
1421
1422 playlist_t *p_playlist = pl_Get( p_intf );
1423 char *psz_bookmark = var_CreateGetString( p_intf, psz_bookmark_name );
1424
1425 PL_LOCK;
1426 FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
1427 char *psz_uri = input_item_GetURI( p_item->p_input );
1428 if( !strcmp( psz_bookmark, psz_uri ) )
1429 {
1430 free( psz_uri );
1431 playlist_ViewPlay( p_playlist, NULL, p_item );
1432 break;
1433 }
1434 else
1435 free( psz_uri );
1436 FOREACH_END();
1437 PL_UNLOCK;
1438
1439 free( psz_bookmark );
1440 free( psz_bookmark_name );
1441 }
1442
SetBookmark(intf_thread_t * p_intf,int i_num)1443 static void SetBookmark( intf_thread_t *p_intf, int i_num )
1444 {
1445 char *psz_bookmark_name;
1446 char *psz_uri = NULL;
1447 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
1448 return;
1449
1450 playlist_t *p_playlist = pl_Get( p_intf );
1451 var_Create( p_intf, psz_bookmark_name,
1452 VLC_VAR_STRING|VLC_VAR_DOINHERIT );
1453
1454 PL_LOCK;
1455 playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
1456 if( p_item ) psz_uri = input_item_GetURI( p_item->p_input );
1457 PL_UNLOCK;
1458
1459 if( p_item )
1460 {
1461 config_PutPsz( p_intf, psz_bookmark_name, psz_uri);
1462 msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num, psz_uri);
1463 }
1464
1465 free( psz_uri );
1466 free( psz_bookmark_name );
1467 }
1468
DisplayPosition(vout_thread_t * p_vout,int slider_chan,input_thread_t * p_input)1469 static void DisplayPosition( vout_thread_t *p_vout, int slider_chan,
1470 input_thread_t *p_input )
1471 {
1472 char psz_duration[MSTRTIME_MAX_SIZE];
1473 char psz_time[MSTRTIME_MAX_SIZE];
1474
1475 if( p_vout == NULL ) return;
1476
1477 ClearChannels( p_vout, slider_chan );
1478
1479 int64_t t = var_GetInteger( p_input, "time" ) / CLOCK_FREQ;
1480 int64_t l = var_GetInteger( p_input, "length" ) / CLOCK_FREQ;
1481
1482 secstotimestr( psz_time, t );
1483
1484 if( l > 0 )
1485 {
1486 secstotimestr( psz_duration, l );
1487 DisplayMessage( p_vout, "%s / %s", psz_time, psz_duration );
1488 }
1489 else if( t > 0 )
1490 {
1491 DisplayMessage( p_vout, "%s", psz_time );
1492 }
1493
1494 if( var_GetBool( p_vout, "fullscreen" ) )
1495 {
1496 vlc_value_t pos;
1497 var_Get( p_input, "position", &pos );
1498 vout_OSDSlider( p_vout, slider_chan,
1499 pos.f_float * 100, OSD_HOR_SLIDER );
1500 }
1501 }
1502
DisplayVolume(vout_thread_t * p_vout,int slider_chan,float vol)1503 static void DisplayVolume( vout_thread_t *p_vout, int slider_chan, float vol )
1504 {
1505 if( p_vout == NULL )
1506 return;
1507 ClearChannels( p_vout, slider_chan );
1508
1509 if( var_GetBool( p_vout, "fullscreen" ) )
1510 vout_OSDSlider( p_vout, slider_chan,
1511 lroundf(vol * 100.f), OSD_VERT_SLIDER );
1512 DisplayMessage( p_vout, _( "Volume %ld%%" ), lroundf(vol * 100.f) );
1513 }
1514
DisplayRate(vout_thread_t * p_vout,float f_rate)1515 static void DisplayRate( vout_thread_t *p_vout, float f_rate )
1516 {
1517 DisplayMessage( p_vout, _("Speed: %.2fx"), (double) f_rate );
1518 }
1519
AdjustRateFine(vlc_object_t * p_obj,const int i_dir)1520 static float AdjustRateFine( vlc_object_t *p_obj, const int i_dir )
1521 {
1522 const float f_rate_min = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MAX;
1523 const float f_rate_max = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MIN;
1524 float f_rate = var_GetFloat( p_obj, "rate" );
1525
1526 int i_sign = f_rate < 0 ? -1 : 1;
1527
1528 f_rate = floor( fabs(f_rate) / 0.1 + i_dir + 0.05 ) * 0.1;
1529
1530 if( f_rate < f_rate_min )
1531 f_rate = f_rate_min;
1532 else if( f_rate > f_rate_max )
1533 f_rate = f_rate_max;
1534 f_rate *= i_sign;
1535
1536 return f_rate;
1537 }
1538
ClearChannels(vout_thread_t * p_vout,int slider_chan)1539 static void ClearChannels( vout_thread_t *p_vout, int slider_chan )
1540 {
1541 if( p_vout )
1542 {
1543 vout_FlushSubpictureChannel( p_vout, VOUT_SPU_CHANNEL_OSD );
1544 vout_FlushSubpictureChannel( p_vout, slider_chan );
1545 }
1546 }
1547