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 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_interface.h>
36 #include <vlc_input.h>
39 #include <vlc_vout_osd.h>
40 #include <vlc_playlist.h>
44 #define CHANNELS_NUMBER 4
45 #define VOLUME_TEXT_CHAN p_intf->p_sys->p_channels[ 0 ]
46 #define VOLUME_WIDGET_CHAN p_intf->p_sys->p_channels[ 1 ]
47 #define POSITION_TEXT_CHAN p_intf->p_sys->p_channels[ 2 ]
48 #define POSITION_WIDGET_CHAN p_intf->p_sys->p_channels[ 3 ]
50 /*****************************************************************************
51 * intf_sys_t: description and status of FB interface
52 *****************************************************************************/
55 vout_thread_t *p_last_vout;
56 int p_channels[ CHANNELS_NUMBER ]; /* contains registered
60 /*****************************************************************************
62 *****************************************************************************/
63 static int Open ( vlc_object_t * );
64 static void Close ( vlc_object_t * );
65 static int ActionEvent( vlc_object_t *, char const *,
66 vlc_value_t, vlc_value_t, void * );
67 static void PlayBookmark( intf_thread_t *, int );
68 static void SetBookmark ( intf_thread_t *, int );
69 static void DisplayPosition( intf_thread_t *, vout_thread_t *, input_thread_t * );
70 static void DisplayVolume( intf_thread_t *, vout_thread_t *, float );
71 static void DisplayRate ( vout_thread_t *, float );
72 static float AdjustRateFine( vlc_object_t *, const int );
73 static void ClearChannels ( intf_thread_t *, vout_thread_t * );
75 #define DisplayMessage(vout, ch, fmt, ...) \
76 do { if(vout) vout_OSDMessage(vout, ch, fmt, __VA_ARGS__); } while(0)
77 #define DisplayIcon(vout, icon) \
78 do { if(vout) vout_OSDIcon(vout, SPU_DEFAULT_CHANNEL, icon); } while(0)
80 /*****************************************************************************
82 *****************************************************************************/
85 set_shortname( N_("Hotkeys") )
86 set_description( N_("Hotkeys management interface") )
87 set_capability( "interface", 0 )
88 set_callbacks( Open, Close )
89 set_category( CAT_INTERFACE )
90 set_subcategory( SUBCAT_INTERFACE_HOTKEYS )
94 /*****************************************************************************
95 * Open: initialize interface
96 *****************************************************************************/
97 static int Open( vlc_object_t *p_this )
99 intf_thread_t *p_intf = (intf_thread_t *)p_this;
101 p_sys = malloc( sizeof( intf_sys_t ) );
105 p_intf->p_sys = p_sys;
107 p_sys->p_last_vout = NULL;
109 var_AddCallback( p_intf->p_libvlc, "key-action", ActionEvent, p_intf );
113 /*****************************************************************************
114 * Close: destroy interface
115 *****************************************************************************/
116 static void Close( vlc_object_t *p_this )
118 intf_thread_t *p_intf = (intf_thread_t *)p_this;
119 intf_sys_t *p_sys = p_intf->p_sys;
121 var_DelCallback( p_intf->p_libvlc, "key-action", ActionEvent, p_intf );
123 /* Destroy structure */
127 static int PutAction( intf_thread_t *p_intf, int i_action )
129 intf_sys_t *p_sys = p_intf->p_sys;
130 playlist_t *p_playlist = pl_Get( p_intf );
132 /* Update the input */
133 input_thread_t *p_input = playlist_CurrentInput( p_playlist );
135 /* Update the vout */
136 vout_thread_t *p_vout = p_input ? input_GetVout( p_input ) : NULL;
138 /* Register OSD channels */
139 /* FIXME: this check can fail if the new vout is reallocated at the same
140 * address as the old one... We should rather listen to vout events.
141 * Alternatively, we should keep a reference to the vout thread. */
142 if( p_vout && p_vout != p_sys->p_last_vout )
143 for( unsigned i = 0; i < CHANNELS_NUMBER; i++ )
144 p_intf->p_sys->p_channels[i] = vout_RegisterSubpictureChannel( p_vout );
145 p_sys->p_last_vout = p_vout;
150 /* Libvlc / interface actions */
152 libvlc_Quit( p_intf->p_libvlc );
154 ClearChannels( p_intf, p_vout );
155 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL, "%s", _( "Quit" ) );
158 case ACTIONID_INTF_TOGGLE_FSC:
159 case ACTIONID_INTF_HIDE:
160 var_TriggerCallback( p_intf->p_libvlc, "intf-toggle-fscontrol" );
162 case ACTIONID_INTF_BOSS:
163 var_TriggerCallback( p_intf->p_libvlc, "intf-boss" );
165 case ACTIONID_INTF_POPUP_MENU:
166 var_TriggerCallback( p_intf->p_libvlc, "intf-popupmenu" );
169 /* Playlist actions (including audio) */
171 /* Toggle Normal -> Loop -> Repeat -> Normal ... */
172 if( var_GetBool( p_playlist, "repeat" ) )
173 var_SetBool( p_playlist, "repeat", false );
175 if( var_GetBool( p_playlist, "loop" ) )
176 { /* FIXME: this is not atomic, we should use a real tristate */
177 var_SetBool( p_playlist, "loop", false );
178 var_SetBool( p_playlist, "repeat", true );
181 var_SetBool( p_playlist, "loop", true );
184 case ACTIONID_RANDOM:
185 var_ToggleBool( p_playlist, "random" );
189 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL, "%s", _("Next") );
190 playlist_Next( p_playlist );
193 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL, "%s", _("Previous") );
194 playlist_Prev( p_playlist );
198 playlist_Stop( p_playlist );
201 case ACTIONID_RATE_NORMAL:
202 var_SetFloat( p_playlist, "rate", 1.f );
203 DisplayRate( p_vout, 1.f );
205 case ACTIONID_FASTER:
206 var_TriggerCallback( p_playlist, "rate-faster" );
207 DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
209 case ACTIONID_SLOWER:
210 var_TriggerCallback( p_playlist, "rate-slower" );
211 DisplayRate( p_vout, var_GetFloat( p_playlist, "rate" ) );
213 case ACTIONID_RATE_FASTER_FINE:
214 case ACTIONID_RATE_SLOWER_FINE:
216 const int i_dir = i_action == ACTIONID_RATE_FASTER_FINE ? 1 : -1;
217 float rate = AdjustRateFine( VLC_OBJECT(p_playlist), i_dir );
219 var_SetFloat( p_playlist, "rate", rate );
220 DisplayRate( p_vout, rate );
224 case ACTIONID_PLAY_BOOKMARK1:
225 case ACTIONID_PLAY_BOOKMARK2:
226 case ACTIONID_PLAY_BOOKMARK3:
227 case ACTIONID_PLAY_BOOKMARK4:
228 case ACTIONID_PLAY_BOOKMARK5:
229 case ACTIONID_PLAY_BOOKMARK6:
230 case ACTIONID_PLAY_BOOKMARK7:
231 case ACTIONID_PLAY_BOOKMARK8:
232 case ACTIONID_PLAY_BOOKMARK9:
233 case ACTIONID_PLAY_BOOKMARK10:
234 PlayBookmark( p_intf, i_action - ACTIONID_PLAY_BOOKMARK1 + 1 );
237 case ACTIONID_SET_BOOKMARK1:
238 case ACTIONID_SET_BOOKMARK2:
239 case ACTIONID_SET_BOOKMARK3:
240 case ACTIONID_SET_BOOKMARK4:
241 case ACTIONID_SET_BOOKMARK5:
242 case ACTIONID_SET_BOOKMARK6:
243 case ACTIONID_SET_BOOKMARK7:
244 case ACTIONID_SET_BOOKMARK8:
245 case ACTIONID_SET_BOOKMARK9:
246 case ACTIONID_SET_BOOKMARK10:
247 SetBookmark( p_intf, i_action - ACTIONID_SET_BOOKMARK1 + 1 );
250 case ACTIONID_VOL_UP:
253 if( playlist_VolumeUp( p_playlist, 1, &vol ) == 0 )
254 DisplayVolume( p_intf, p_vout, vol );
257 case ACTIONID_VOL_DOWN:
260 if( playlist_VolumeDown( p_playlist, 1, &vol ) == 0 )
261 DisplayVolume( p_intf, p_vout, vol );
264 case ACTIONID_VOL_MUTE:
265 if( playlist_MuteToggle( p_playlist ) == 0 )
267 float vol = playlist_VolumeGet( p_playlist );
268 if( playlist_MuteGet( p_playlist ) > 0 || vol == 0.f )
270 ClearChannels( p_intf, p_vout );
271 DisplayIcon( p_vout, OSD_MUTE_ICON );
274 DisplayVolume( p_intf, p_vout, vol );
278 case ACTIONID_AUDIODEVICE_CYCLE:
280 audio_output_t *p_aout = playlist_GetAout( p_playlist );
285 int n = aout_DevicesList( p_aout, &ids, &names );
289 char *dev = aout_DeviceGet( p_aout );
290 const char *devstr = (dev != NULL) ? dev : "";
293 for( int i = 0; i < n; i++ )
295 if( !strcmp(devstr, ids[i]) )
300 if( !aout_DeviceSet( p_aout, ids[idx] ) )
301 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
302 _("Audio Device: %s"), names[idx] );
303 vlc_object_release( p_aout );
305 for( int i = 0; i < n; i++ )
315 /* Playlist + input actions */
316 case ACTIONID_PLAY_PAUSE:
319 ClearChannels( p_intf, p_vout );
321 int state = var_GetInteger( p_input, "state" );
322 DisplayIcon( p_vout, state != PAUSE_S ? OSD_PAUSE_ICON : OSD_PLAY_ICON );
323 playlist_Pause( p_playlist );
326 playlist_Play( p_playlist );
330 if( p_input && var_GetFloat( p_input, "rate" ) != 1. )
331 /* Return to normal speed */
332 var_SetFloat( p_input, "rate", 1. );
335 ClearChannels( p_intf, p_vout );
336 DisplayIcon( p_vout, OSD_PLAY_ICON );
337 playlist_Play( p_playlist );
341 /* Playlist + video output actions */
342 case ACTIONID_WALLPAPER:
343 { /* FIXME: this is invalid if not using DirectX output!!! */
344 vlc_object_t *obj = p_vout ? VLC_OBJECT(p_vout)
345 : VLC_OBJECT(p_playlist);
346 var_ToggleBool( obj, "video-wallpaper" );
352 if( p_input && var_GetInteger( p_input, "state" ) != PAUSE_S )
354 ClearChannels( p_intf, p_vout );
355 DisplayIcon( p_vout, OSD_PAUSE_ICON );
356 var_SetInteger( p_input, "state", PAUSE_S );
360 case ACTIONID_RECORD:
361 if( p_input && var_GetBool( p_input, "can-record" ) )
363 const bool on = var_ToggleBool( p_input, "record" );
364 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL, "%s",
365 vlc_gettext(on ? N_("Recording") : N_("Recording done")) );
369 case ACTIONID_FRAME_NEXT:
372 var_TriggerCallback( p_input, "frame-next" );
373 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
374 "%s", _("Next frame") );
378 case ACTIONID_SUBDELAY_DOWN:
379 case ACTIONID_SUBDELAY_UP:
381 int diff = (i_action == ACTIONID_SUBDELAY_UP) ? 50000 : -50000;
384 int64_t i_delay = var_GetTime( p_input, "spu-delay" ) + diff;
386 var_SetTime( p_input, "spu-delay", i_delay );
387 ClearChannels( p_intf, p_vout );
388 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
389 _( "Subtitle delay %i ms" ),
390 (int)(i_delay/1000) );
394 case ACTIONID_AUDIODELAY_DOWN:
395 case ACTIONID_AUDIODELAY_UP:
397 int diff = (i_action == ACTIONID_AUDIODELAY_UP) ? 50000 : -50000;
400 int64_t i_delay = var_GetTime( p_input, "audio-delay" ) + diff;
402 var_SetTime( p_input, "audio-delay", i_delay );
403 ClearChannels( p_intf, p_vout );
404 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
405 _( "Audio delay %i ms" ),
406 (int)(i_delay/1000) );
411 case ACTIONID_AUDIO_TRACK:
414 vlc_value_t val, list, list2;
416 var_Get( p_input, "audio-es", &val );
417 var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
419 i_count = list.p_list->i_count;
422 for( i = 0; i < i_count; i++ )
424 if( val.i_int == list.p_list->p_values[i].i_int )
429 /* value of audio-es was not in choices list */
433 "invalid current audio track, selecting 0" );
436 else if( i == i_count - 1 )
440 var_Set( p_input, "audio-es", list.p_list->p_values[i] );
441 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
442 _("Audio track: %s"),
443 list2.p_list->p_values[i].psz_string );
445 var_FreeList( &list, &list2 );
448 case ACTIONID_SUBTITLE_TRACK:
451 vlc_value_t val, list, list2;
453 var_Get( p_input, "spu-es", &val );
455 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
457 i_count = list.p_list->i_count;
460 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
461 _("Subtitle track: %s"), _("N/A") );
462 var_FreeList( &list, &list2 );
465 for( i = 0; i < i_count; i++ )
467 if( val.i_int == list.p_list->p_values[i].i_int )
472 /* value of spu-es was not in choices list */
476 "invalid current subtitle track, selecting 0" );
479 else if( i == i_count - 1 )
483 var_Set( p_input, "spu-es", list.p_list->p_values[i] );
484 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
485 _("Subtitle track: %s"),
486 list2.p_list->p_values[i].psz_string );
487 var_FreeList( &list, &list2 );
490 case ACTIONID_PROGRAM_SID:
493 vlc_value_t val, list, list2;
495 var_Get( p_input, "program", &val );
497 var_Change( p_input, "program", VLC_VAR_GETCHOICES,
499 i_count = list.p_list->i_count;
502 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
503 _("Program Service ID: %s"), _("N/A") );
504 var_FreeList( &list, &list2 );
507 for( i = 0; i < i_count; i++ )
509 if( val.i_int == list.p_list->p_values[i].i_int )
514 /* value of program was not in choices list */
518 "invalid current program SID, selecting 0" );
521 else if( i == i_count - 1 )
525 var_Set( p_input, "program", list.p_list->p_values[i] );
526 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
527 _("Program Service ID: %s"),
528 list2.p_list->p_values[i].psz_string );
529 var_FreeList( &list, &list2 );
533 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
534 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
535 case ACTIONID_JUMP_BACKWARD_SHORT:
536 case ACTIONID_JUMP_FORWARD_SHORT:
537 case ACTIONID_JUMP_BACKWARD_MEDIUM:
538 case ACTIONID_JUMP_FORWARD_MEDIUM:
539 case ACTIONID_JUMP_BACKWARD_LONG:
540 case ACTIONID_JUMP_FORWARD_LONG:
542 if( p_input == NULL || !var_GetBool( p_input, "can-seek" ) )
549 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
551 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
552 varname = "extrashort-jump-size";
554 case ACTIONID_JUMP_BACKWARD_SHORT:
556 case ACTIONID_JUMP_FORWARD_SHORT:
557 varname = "short-jump-size";
559 case ACTIONID_JUMP_BACKWARD_MEDIUM:
561 case ACTIONID_JUMP_FORWARD_MEDIUM:
562 varname = "medium-jump-size";
564 case ACTIONID_JUMP_BACKWARD_LONG:
566 case ACTIONID_JUMP_FORWARD_LONG:
567 varname = "long-jump-size";
571 mtime_t it = var_InheritInteger( p_input, varname );
574 var_SetTime( p_input, "time-offset", it * sign * CLOCK_FREQ );
575 DisplayPosition( p_intf, p_vout, p_input );
579 /* Input navigation (DVD & MKV) */
580 case ACTIONID_TITLE_PREV:
582 var_TriggerCallback( p_input, "prev-title" );
584 case ACTIONID_TITLE_NEXT:
586 var_TriggerCallback( p_input, "next-title" );
588 case ACTIONID_CHAPTER_PREV:
590 var_TriggerCallback( p_input, "prev-chapter" );
592 case ACTIONID_CHAPTER_NEXT:
594 var_TriggerCallback( p_input, "next-chapter" );
596 case ACTIONID_DISC_MENU:
598 var_SetInteger( p_input, "title 0", 2 );
601 /* Video Output actions */
602 case ACTIONID_SNAPSHOT:
604 var_TriggerCallback( p_vout, "video-snapshot" );
607 case ACTIONID_TOGGLE_FULLSCREEN:
609 bool fs = var_ToggleBool( p_playlist, "fullscreen" );
611 var_SetBool( p_vout, "fullscreen", fs );
615 case ACTIONID_LEAVE_FULLSCREEN:
617 var_SetBool( p_vout, "fullscreen", false );
618 var_SetBool( p_playlist, "fullscreen", false );
621 case ACTIONID_ASPECT_RATIO:
624 vlc_value_t val={0}, val_list, text_list;
625 var_Get( p_vout, "aspect-ratio", &val );
626 if( var_Change( p_vout, "aspect-ratio", VLC_VAR_GETLIST,
627 &val_list, &text_list ) >= 0 )
630 for( i = 0; i < val_list.p_list->i_count; i++ )
632 if( !strcmp( val_list.p_list->p_values[i].psz_string,
639 if( i == val_list.p_list->i_count ) i = 0;
640 var_SetString( p_vout, "aspect-ratio",
641 val_list.p_list->p_values[i].psz_string );
642 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
643 _("Aspect ratio: %s"),
644 text_list.p_list->p_values[i].psz_string );
646 var_FreeList( &val_list, &text_list );
648 free( val.psz_string );
655 vlc_value_t val={0}, val_list, text_list;
656 var_Get( p_vout, "crop", &val );
657 if( var_Change( p_vout, "crop", VLC_VAR_GETLIST,
658 &val_list, &text_list ) >= 0 )
661 for( i = 0; i < val_list.p_list->i_count; i++ )
663 if( !strcmp( val_list.p_list->p_values[i].psz_string,
670 if( i == val_list.p_list->i_count ) i = 0;
671 var_SetString( p_vout, "crop",
672 val_list.p_list->p_values[i].psz_string );
673 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
675 text_list.p_list->p_values[i].psz_string );
677 var_FreeList( &val_list, &text_list );
679 free( val.psz_string );
682 case ACTIONID_CROP_TOP:
684 var_IncInteger( p_vout, "crop-top" );
686 case ACTIONID_UNCROP_TOP:
688 var_DecInteger( p_vout, "crop-top" );
690 case ACTIONID_CROP_BOTTOM:
692 var_IncInteger( p_vout, "crop-bottom" );
694 case ACTIONID_UNCROP_BOTTOM:
696 var_DecInteger( p_vout, "crop-bottom" );
698 case ACTIONID_CROP_LEFT:
700 var_IncInteger( p_vout, "crop-left" );
702 case ACTIONID_UNCROP_LEFT:
704 var_DecInteger( p_vout, "crop-left" );
706 case ACTIONID_CROP_RIGHT:
708 var_IncInteger( p_vout, "crop-right" );
710 case ACTIONID_UNCROP_RIGHT:
712 var_DecInteger( p_vout, "crop-right" );
715 case ACTIONID_TOGGLE_AUTOSCALE:
718 float f_scalefactor = var_GetFloat( p_vout, "scale" );
719 if ( f_scalefactor != 1.f )
721 var_SetFloat( p_vout, "scale", 1.f );
722 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
723 "%s", _("Zooming reset") );
727 bool b_autoscale = !var_GetBool( p_vout, "autoscale" );
728 var_SetBool( p_vout, "autoscale", b_autoscale );
730 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
731 "%s", _("Scaled to screen") );
733 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
734 "%s", _("Original Size") );
738 case ACTIONID_SCALE_UP:
741 float f_scalefactor = var_GetFloat( p_vout, "scale" );
743 if( f_scalefactor < 10.f )
744 f_scalefactor += .1f;
745 var_SetFloat( p_vout, "scale", f_scalefactor );
748 case ACTIONID_SCALE_DOWN:
751 float f_scalefactor = var_GetFloat( p_vout, "scale" );
753 if( f_scalefactor > .3f )
754 f_scalefactor -= .1f;
755 var_SetFloat( p_vout, "scale", f_scalefactor );
759 case ACTIONID_ZOOM_QUARTER:
760 case ACTIONID_ZOOM_HALF:
761 case ACTIONID_ZOOM_ORIGINAL:
762 case ACTIONID_ZOOM_DOUBLE:
768 case ACTIONID_ZOOM_QUARTER: f = 0.25; break;
769 case ACTIONID_ZOOM_HALF: f = 0.5; break;
770 case ACTIONID_ZOOM_ORIGINAL: f = 1.; break;
771 /*case ACTIONID_ZOOM_DOUBLE:*/
772 default: f = 2.; break;
774 var_SetFloat( p_vout, "zoom", f );
778 case ACTIONID_UNZOOM:
781 vlc_value_t val={0}, val_list, text_list;
782 var_Get( p_vout, "zoom", &val );
783 if( var_Change( p_vout, "zoom", VLC_VAR_GETLIST,
784 &val_list, &text_list ) >= 0 )
787 for( i = 0; i < val_list.p_list->i_count; i++ )
789 if( val_list.p_list->p_values[i].f_float
792 if( i_action == ACTIONID_ZOOM )
794 else /* ACTIONID_UNZOOM */
799 if( i == val_list.p_list->i_count ) i = 0;
800 if( i == -1 ) i = val_list.p_list->i_count-1;
801 var_SetFloat( p_vout, "zoom",
802 val_list.p_list->p_values[i].f_float );
803 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
805 text_list.p_list->p_values[i].psz_string );
807 var_FreeList( &val_list, &text_list );
812 case ACTIONID_DEINTERLACE:
815 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
816 if( i_deinterlace != 0 )
818 var_SetInteger( p_vout, "deinterlace", 0 );
819 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
820 "%s", _("Deinterlace off") );
824 var_SetInteger( p_vout, "deinterlace", 1 );
826 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
827 vlc_value_t vlist, tlist;
828 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ) >= 0 )
830 const char *psz_text = NULL;
831 for( int i = 0; i < vlist.p_list->i_count; i++ )
833 if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
835 psz_text = tlist.p_list->p_values[i].psz_string;
839 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
840 "%s (%s)", _("Deinterlace on"), psz_text ? psz_text : psz_mode );
842 var_FreeList( &vlist, &tlist );
848 case ACTIONID_DEINTERLACE_MODE:
851 char *psz_mode = var_GetString( p_vout, "deinterlace-mode" );
852 vlc_value_t vlist, tlist;
853 if( psz_mode && !var_Change( p_vout, "deinterlace-mode", VLC_VAR_GETCHOICES, &vlist, &tlist ) >= 0 )
855 const char *psz_text = NULL;
857 for( i = 0; i < vlist.p_list->i_count; i++ )
859 if( !strcmp( vlist.p_list->p_values[i].psz_string, psz_mode ) )
865 if( i == vlist.p_list->i_count ) i = 0;
866 psz_text = tlist.p_list->p_values[i].psz_string;
867 var_SetString( p_vout, "deinterlace-mode", vlist.p_list->p_values[i].psz_string );
869 int i_deinterlace = var_GetInteger( p_vout, "deinterlace" );
870 if( i_deinterlace != 0 )
872 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
873 "%s (%s)", _("Deinterlace on"), psz_text ? psz_text : psz_mode );
877 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
878 "%s (%s)", _("Deinterlace off"), psz_text ? psz_text : psz_mode );
881 var_FreeList( &vlist, &tlist );
887 case ACTIONID_SUBPOS_DOWN:
888 case ACTIONID_SUBPOS_UP:
891 if( i_action == ACTIONID_SUBPOS_DOWN )
892 i_pos = var_DecInteger( p_vout, "sub-margin" );
894 i_pos = var_IncInteger( p_vout, "sub-margin" );
896 ClearChannels( p_intf, p_vout );
897 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL,
898 _( "Subtitle position %d px" ), i_pos );
902 /* Input + video output */
903 case ACTIONID_POSITION:
904 if( p_vout && vout_OSDEpg( p_vout, input_GetItem( p_input ) ) )
905 DisplayPosition( p_intf, p_vout, p_input );
910 vlc_object_release( p_vout );
912 vlc_object_release( p_input );
916 /*****************************************************************************
917 * ActionEvent: callback for hotkey actions
918 *****************************************************************************/
919 static int ActionEvent( vlc_object_t *libvlc, char const *psz_var,
920 vlc_value_t oldval, vlc_value_t newval, void *p_data )
922 intf_thread_t *p_intf = (intf_thread_t *)p_data;
928 return PutAction( p_intf, newval.i_int );
931 static void PlayBookmark( intf_thread_t *p_intf, int i_num )
933 char *psz_bookmark_name;
934 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
937 playlist_t *p_playlist = pl_Get( p_intf );
938 char *psz_bookmark = var_CreateGetString( p_intf, psz_bookmark_name );
941 FOREACH_ARRAY( playlist_item_t *p_item, p_playlist->items )
942 char *psz_uri = input_item_GetURI( p_item->p_input );
943 if( !strcmp( psz_bookmark, psz_uri ) )
946 playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked,
955 free( psz_bookmark );
956 free( psz_bookmark_name );
959 static void SetBookmark( intf_thread_t *p_intf, int i_num )
961 char *psz_bookmark_name;
962 char *psz_uri = NULL;
963 if( asprintf( &psz_bookmark_name, "bookmark%i", i_num ) == -1 )
966 playlist_t *p_playlist = pl_Get( p_intf );
967 var_Create( p_intf, psz_bookmark_name,
968 VLC_VAR_STRING|VLC_VAR_DOINHERIT );
971 playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
972 if( p_item ) psz_uri = input_item_GetURI( p_item->p_input );
977 config_PutPsz( p_intf, psz_bookmark_name, psz_uri);
978 msg_Info( p_intf, "setting playlist bookmark %i to %s", i_num, psz_uri);
982 free( psz_bookmark_name );
985 static void DisplayPosition( intf_thread_t *p_intf, vout_thread_t *p_vout,
986 input_thread_t *p_input )
988 char psz_duration[MSTRTIME_MAX_SIZE];
989 char psz_time[MSTRTIME_MAX_SIZE];
990 vlc_value_t time, pos;
993 if( p_vout == NULL ) return;
995 ClearChannels( p_intf, p_vout );
997 var_Get( p_input, "time", &time );
998 i_seconds = time.i_time / 1000000;
999 secstotimestr ( psz_time, i_seconds );
1001 var_Get( p_input, "length", &time );
1002 if( time.i_time > 0 )
1004 secstotimestr( psz_duration, time.i_time / 1000000 );
1005 DisplayMessage( p_vout, POSITION_TEXT_CHAN, "%s / %s",
1006 psz_time, psz_duration );
1008 else if( i_seconds > 0 )
1010 DisplayMessage( p_vout, POSITION_TEXT_CHAN, "%s", psz_time );
1013 if( var_GetBool( p_vout, "fullscreen" ) )
1015 var_Get( p_input, "position", &pos );
1016 vout_OSDSlider( p_vout, POSITION_WIDGET_CHAN,
1017 pos.f_float * 100, OSD_HOR_SLIDER );
1021 static void DisplayVolume( intf_thread_t *p_intf, vout_thread_t *p_vout,
1024 if( p_vout == NULL )
1026 ClearChannels( p_intf, p_vout );
1028 if( var_GetBool( p_vout, "fullscreen" ) )
1029 vout_OSDSlider( p_vout, VOLUME_WIDGET_CHAN, lround(vol * 100.),
1031 DisplayMessage( p_vout, VOLUME_TEXT_CHAN, _( "Volume %ld%%" ),
1032 lround(vol * 100.) );
1035 static void DisplayRate( vout_thread_t *p_vout, float f_rate )
1037 DisplayMessage( p_vout, SPU_DEFAULT_CHANNEL, _("Speed: %.2fx"), f_rate );
1040 static float AdjustRateFine( vlc_object_t *p_obj, const int i_dir )
1042 const float f_rate_min = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MAX;
1043 const float f_rate_max = (float)INPUT_RATE_DEFAULT / INPUT_RATE_MIN;
1044 float f_rate = var_GetFloat( p_obj, "rate" );
1046 int i_sign = f_rate < 0 ? -1 : 1;
1048 f_rate = floor( fabs(f_rate) / 0.1 + i_dir + 0.05 ) * 0.1;
1050 if( f_rate < f_rate_min )
1051 f_rate = f_rate_min;
1052 else if( f_rate > f_rate_max )
1053 f_rate = f_rate_max;
1059 static void ClearChannels( intf_thread_t *p_intf, vout_thread_t *p_vout )
1063 vout_FlushSubpictureChannel( p_vout, SPU_DEFAULT_CHANNEL );
1064 for( int i = 0; i < CHANNELS_NUMBER; i++ )
1065 vout_FlushSubpictureChannel( p_vout, p_intf->p_sys->p_channels[i] );