1 /*****************************************************************************
2 * hotkeys.c: Hotkey handling for vlc
3 *****************************************************************************
4 * Copyright (C) 2005-2009 the VideoLAN team
6 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
7 * Jean-Paul Saman <jpsaman #_at_# m2x.nl>
8 * Victorien Le Couviour--Tuffet <victorien.lecouviour.tuffet@gmail.com>
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 *****************************************************************************/
29 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_interface.h>
33 #include <vlc_input_item.h>
34 #include <vlc_mouse.h>
35 #include <vlc_viewpoint.h>
36 #include <vlc_player.h>
37 #include <vlc_playlist.h>
38 #include <vlc_actions.h>
44 vlc_playlist_t
*playlist
;
45 vlc_player_listener_id
*player_listener
;
54 vlc_tick_t audio_time
;
55 vlc_tick_t subtitle_time
;
57 enum vlc_vout_order spu_channel_order
;
60 static void handle_action(intf_thread_t
*, vlc_action_id_t
);
62 /*****************************
63 * interface action handling *
64 *****************************/
66 #define INTF_ACTION_HANDLER(name) \
68 action_handler_Intf##name(intf_thread_t *intf, vlc_action_id_t action_id)
76 return libvlc_Quit(vlc_object_instance(intf
));
77 case ACTIONID_INTF_TOGGLE_FSC
:
78 case ACTIONID_INTF_HIDE
:
79 varname
= "intf-toggle-fscontrol";
81 case ACTIONID_INTF_BOSS
:
82 varname
= "intf-boss";
84 case ACTIONID_INTF_POPUP_MENU
:
85 varname
= "intf-popupmenu";
88 vlc_assert_unreachable();
90 var_TriggerCallback(vlc_object_instance(intf
), varname
);
93 INTF_ACTION_HANDLER(ActionCombo
)
95 vlc_player_t
*player
= vlc_playlist_GetPlayer(intf
->p_sys
->playlist
);
96 vout_thread_t
*vout
= vlc_player_vout_Hold(player
);
97 bool vrnav
= var_GetBool(vout
, "viewpoint-changeable");
101 case ACTIONID_COMBO_VOL_FOV_DOWN
:
104 : ACTIONID_VIEWPOINT_FOV_OUT
;
106 case ACTIONID_COMBO_VOL_FOV_UP
:
109 : ACTIONID_VIEWPOINT_FOV_IN
;
112 vlc_assert_unreachable();
114 handle_action(intf
, action_id
);
117 /****************************
118 * playlist action handling *
119 ****************************/
121 #define PLAYLIST_ACTION_HANDLER(name) \
123 action_handler_Playlist##name(intf_thread_t *intf, vlc_playlist_t *playlist, \
124 vlc_action_id_t action_id)
126 PLAYLIST_ACTION_HANDLER(Interact
)
131 case ACTIONID_PLAY_CLEAR
:
132 vlc_playlist_Clear(playlist
);
135 vlc_playlist_Prev(playlist
);
138 vlc_playlist_Next(playlist
);
141 vlc_assert_unreachable();
145 PLAYLIST_ACTION_HANDLER(Playback
)
152 enum vlc_playlist_playback_repeat repeat_mode
=
153 vlc_playlist_GetPlaybackRepeat(playlist
);
156 case VLC_PLAYLIST_PLAYBACK_REPEAT_NONE
:
157 repeat_mode
= VLC_PLAYLIST_PLAYBACK_REPEAT_ALL
;
159 case VLC_PLAYLIST_PLAYBACK_REPEAT_ALL
:
160 repeat_mode
= VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT
;
162 case VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT
:
163 repeat_mode
= VLC_PLAYLIST_PLAYBACK_REPEAT_NONE
;
166 vlc_playlist_SetPlaybackRepeat(playlist
, repeat_mode
);
169 case ACTIONID_RANDOM
:
171 enum vlc_playlist_playback_order order_mode
=
172 vlc_playlist_GetPlaybackOrder(playlist
);
174 order_mode
== VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL
175 ? VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM
176 : VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL
;
177 vlc_playlist_SetPlaybackOrder(playlist
, order_mode
);
181 vlc_assert_unreachable();
186 playlist_bookmark_Set(intf_thread_t
*intf
,
187 vlc_playlist_t
*playlist
, char *name
, int id
)
189 var_Create(intf
, name
, VLC_VAR_STRING
| VLC_VAR_DOINHERIT
);
190 vlc_player_t
*player
= vlc_playlist_GetPlayer(playlist
);
191 input_item_t
*item
= vlc_player_GetCurrentMedia(player
);
194 char *uri
= input_item_GetURI(item
);
195 config_PutPsz(name
, uri
);
196 msg_Info(intf
, "setting playlist bookmark %i to %s", id
, uri
);
202 playlist_bookmark_Play(intf_thread_t
*intf
,
203 vlc_playlist_t
*playlist
, char *name
)
205 char *bookmark_uri
= var_CreateGetString(intf
, name
);
206 size_t count
= vlc_playlist_Count(playlist
);
208 for (i
= 0; i
< count
; ++i
)
210 vlc_playlist_item_t
*plitem
= vlc_playlist_Get(playlist
, i
);
211 input_item_t
*item
= vlc_playlist_item_GetMedia(plitem
);
212 char *item_uri
= input_item_GetURI(item
);
213 if (!strcmp(bookmark_uri
, item_uri
))
218 vlc_playlist_PlayAt(playlist
, i
);
222 INTF_ACTION_HANDLER(PlaylistBookmark
)
224 bool set
= action_id
>= ACTIONID_SET_BOOKMARK1
&&
225 action_id
<= ACTIONID_SET_BOOKMARK10
;
226 int id
= (set
? 1 - ACTIONID_SET_BOOKMARK1
: 1 - ACTIONID_PLAY_BOOKMARK1
);
229 if (asprintf(&bookmark_name
, "bookmark%i", id
) == -1)
231 vlc_playlist_t
*playlist
= intf
->p_sys
->playlist
;
233 playlist_bookmark_Set(intf
, playlist
, bookmark_name
, id
);
235 playlist_bookmark_Play(intf
, playlist
, bookmark_name
);
239 /**************************
240 * player action handling *
241 **************************/
243 #define PLAYER_ACTION_HANDLER(name) \
244 static inline void action_handler_Player##name(intf_thread_t *intf, \
245 vlc_player_t *player, \
246 vlc_action_id_t action_id)
248 PLAYER_ACTION_HANDLER(State
)
253 case ACTIONID_PLAY_PAUSE
:
255 enum vlc_player_state state
= vlc_player_GetState(player
);
256 if (state
== VLC_PLAYER_STATE_PAUSED
)
257 vlc_player_Resume(player
);
259 vlc_player_Pause(player
);
263 vlc_player_Start(player
);
266 vlc_player_Pause(player
);
269 vlc_player_Stop(player
);
271 case ACTIONID_FRAME_NEXT
:
272 vlc_player_NextVideoFrame(player
);
275 vlc_assert_unreachable();
279 INTF_ACTION_HANDLER(PlayerSeek
)
281 vlc_player_t
*player
= vlc_playlist_GetPlayer(intf
->p_sys
->playlist
);
282 if (!vlc_player_CanSeek(player
))
288 case ACTIONID_JUMP_BACKWARD_EXTRASHORT
:
291 case ACTIONID_JUMP_FORWARD_EXTRASHORT
:
292 varname
= "extrashort-jump-size";
294 case ACTIONID_JUMP_BACKWARD_SHORT
:
297 case ACTIONID_JUMP_FORWARD_SHORT
:
298 varname
= "short-jump-size";
300 case ACTIONID_JUMP_BACKWARD_MEDIUM
:
303 case ACTIONID_JUMP_FORWARD_MEDIUM
:
304 varname
= "medium-jump-size";
306 case ACTIONID_JUMP_BACKWARD_LONG
:
309 case ACTIONID_JUMP_FORWARD_LONG
:
310 varname
= "long-jump-size";
313 vlc_assert_unreachable();
315 int jmpsz
= var_InheritInteger(vlc_object_instance(intf
), varname
);
317 vlc_player_JumpTime(player
, vlc_tick_from_sec(jmpsz
* sign
));
320 PLAYER_ACTION_HANDLER(Position
)
322 VLC_UNUSED(action_id
); VLC_UNUSED(intf
);
323 vout_thread_t
*vout
= vlc_player_vout_Hold(player
);
324 if (vout_OSDEpg(vout
, vlc_player_GetCurrentMedia(player
)))
325 vlc_player_DisplayPosition(player
);
329 PLAYER_ACTION_HANDLER(NavigateMedia
)
334 case ACTIONID_PROGRAM_SID_PREV
:
335 vlc_player_SelectPrevProgram(player
);
337 case ACTIONID_PROGRAM_SID_NEXT
:
338 vlc_player_SelectNextProgram(player
);
340 case ACTIONID_TITLE_PREV
:
341 vlc_player_SelectPrevTitle(player
);
343 case ACTIONID_TITLE_NEXT
:
344 vlc_player_SelectNextTitle(player
);
346 case ACTIONID_CHAPTER_PREV
:
347 vlc_player_SelectPrevChapter(player
);
349 case ACTIONID_CHAPTER_NEXT
:
350 vlc_player_SelectNextChapter(player
);
352 case ACTIONID_DISC_MENU
:
353 vlc_player_Navigate(player
, VLC_PLAYER_NAV_MENU
);
356 vlc_assert_unreachable();
360 static void CycleSecondarySubtitles(intf_thread_t
*intf
, vlc_player_t
*player
,
363 intf_sys_t
*sys
= intf
->p_sys
;
364 const enum es_format_category_e cat
= SPU_ES
;
365 size_t count
= vlc_player_GetTrackCount(player
, cat
);
369 vlc_es_id_t
*cycle_id
= NULL
;
370 vlc_es_id_t
*keep_id
= NULL
;
372 /* Check how many subtitle tracks are already selected */
373 size_t selected_count
= 0;
374 for (size_t i
= 0; i
< count
; ++i
)
376 const struct vlc_player_track
*track
=
377 vlc_player_GetTrackAt(player
, cat
, i
);
382 enum vlc_vout_order order
;
383 if (vlc_player_GetEsIdVout(player
, track
->es_id
, &order
)
384 && order
== sys
->spu_channel_order
)
385 cycle_id
= track
->es_id
;
387 keep_id
= track
->es_id
;
392 if ((sys
->spu_channel_order
== VLC_VOUT_ORDER_PRIMARY
393 && selected_count
== 1) || selected_count
== 0)
395 /* Only cycle the primary subtitle track */
397 vlc_player_SelectNextTrack(player
, cat
);
399 vlc_player_SelectPrevTrack(player
, cat
);
403 /* Find out the current selected index.
404 If no track selected, select the first or the last track */
405 size_t index
= next
? 0 : count
- 1;
406 for (size_t i
= 0; i
< count
; ++i
)
408 const struct vlc_player_track
*track
=
409 vlc_player_GetTrackAt(player
, cat
, i
);
412 if (track
->es_id
== cycle_id
)
419 /* Look for the next free (unselected) track */
422 const struct vlc_player_track
*track
=
423 vlc_player_GetTrackAt(player
, cat
, index
);
425 if (!track
->selected
)
427 cycle_id
= track
->es_id
;
430 /* Unselect if we reach the end of the cycle */
431 else if ((next
&& index
+ 1 == count
) || (!next
&& index
== 0))
436 else /* Switch to the next or previous track */
437 index
= index
+ (next
? 1 : -1);
440 /* Make sure the list never contains NULL before a valid id */
447 vlc_es_id_t
*esIds
[] = { keep_id
, cycle_id
, NULL
};
448 vlc_player_SelectEsIdList(player
, cat
, esIds
);
452 PLAYER_ACTION_HANDLER(Track
)
456 case ACTIONID_AUDIO_TRACK
:
457 vlc_player_SelectNextTrack(player
, AUDIO_ES
);
459 case ACTIONID_SUBTITLE_REVERSE_TRACK
:
460 case ACTIONID_SUBTITLE_TRACK
:
461 CycleSecondarySubtitles(intf
, player
,
462 action_id
== ACTIONID_SUBTITLE_TRACK
);
465 vlc_assert_unreachable();
469 PLAYER_ACTION_HANDLER(Delay
)
471 intf_sys_t
*sys
= intf
->p_sys
;
472 enum { AUDIODELAY
, SUBDELAY
} type
;
476 case ACTIONID_AUDIODELAY_DOWN
:
479 case ACTIONID_AUDIODELAY_UP
:
482 case ACTIONID_SUBDELAY_DOWN
:
485 case ACTIONID_SUBDELAY_UP
:
489 vlc_assert_unreachable();
491 enum vlc_player_whence whence
= VLC_PLAYER_WHENCE_RELATIVE
;
492 delta
= VLC_TICK_FROM_MS(delta
);
493 if (type
== AUDIODELAY
)
494 vlc_player_SetAudioDelay(player
, delta
, whence
);
497 if (sys
->spu_channel_order
== VLC_VOUT_ORDER_PRIMARY
)
498 vlc_player_SetSubtitleDelay(player
, delta
, whence
);
501 size_t count
= vlc_player_GetTrackCount(player
, SPU_ES
);
502 for (size_t i
= 0; i
< count
; ++i
)
504 const struct vlc_player_track
*track
=
505 vlc_player_GetTrackAt(player
, SPU_ES
, i
);
507 enum vlc_vout_order order
;
508 if (vlc_player_GetEsIdVout(player
, track
->es_id
, &order
)
509 && order
== VLC_VOUT_ORDER_SECONDARY
)
511 vlc_player_SetEsIdDelay(player
, track
->es_id
, delta
, whence
);
520 AdjustRateFine(float rate
, int const dir
)
522 float rate_min
= INPUT_RATE_MIN
;
523 float rate_max
= INPUT_RATE_MAX
;
524 int sign
= rate
< 0 ? -1 : 1;
525 rate
= floor(fabs(rate
) / 0.1 + dir
+ 0.05) * 0.1;
528 else if (rate
> rate_max
)
533 PLAYER_ACTION_HANDLER(Rate
)
538 case ACTIONID_RATE_SLOWER
:
539 vlc_player_DecrementRate(player
);
541 case ACTIONID_RATE_FASTER
:
542 vlc_player_IncrementRate(player
);
549 case ACTIONID_RATE_NORMAL
:
552 case ACTIONID_RATE_SLOWER_FINE
:
553 case ACTIONID_RATE_FASTER_FINE
:
555 int const dir
= action_id
== ACTIONID_RATE_SLOWER_FINE
?
557 rate
= vlc_player_GetRate(player
);
558 rate
= AdjustRateFine(rate
, dir
);
562 vlc_assert_unreachable();
564 vlc_player_ChangeRate(player
, rate
);
570 PLAYER_ACTION_HANDLER(ToggleSubtitle
)
572 VLC_UNUSED(action_id
); VLC_UNUSED(intf
);
573 vlc_player_ToggleSubtitle(player
);
576 PLAYER_ACTION_HANDLER(ControlSubtitleSecondary
)
578 VLC_UNUSED(action_id
);
579 intf_sys_t
*sys
= intf
->p_sys
;
581 sys
->spu_channel_order
=
582 sys
->spu_channel_order
== VLC_VOUT_ORDER_PRIMARY
?
583 VLC_VOUT_ORDER_SECONDARY
: VLC_VOUT_ORDER_PRIMARY
;
585 vlc_player_osd_Message(player
, _("%s subtitle control"),
586 sys
->spu_channel_order
== VLC_VOUT_ORDER_PRIMARY
? "Primary" : "Secondary");
589 PLAYER_ACTION_HANDLER(SyncSubtitle
)
591 intf_sys_t
*sys
= intf
->p_sys
;
595 case ACTIONID_SUBSYNC_MARKAUDIO
:
596 sys
->subsync
.audio_time
= vlc_tick_now();
597 vlc_player_osd_Message(player
, _("Sub sync: bookmarked audio time"));
599 case ACTIONID_SUBSYNC_MARKSUB
:
600 sys
->subsync
.subtitle_time
= vlc_tick_now();
601 vlc_player_osd_Message(player
, _("Sub sync: bookmarked subtitle time"));
603 case ACTIONID_SUBSYNC_APPLY
:
605 if (sys
->subsync
.audio_time
== VLC_TICK_INVALID
||
606 sys
->subsync
.subtitle_time
== VLC_TICK_INVALID
)
608 vlc_player_osd_Message(player
, _("Sub sync: set bookmarks first!"));
612 sys
->subsync
.audio_time
- sys
->subsync
.subtitle_time
;
613 sys
->subsync
.audio_time
= VLC_TICK_INVALID
;
614 sys
->subsync
.subtitle_time
= VLC_TICK_INVALID
;
616 vlc_tick_t previous_delay
= vlc_player_GetSubtitleDelay(player
);
617 vlc_player_SetSubtitleDelay(player
, delay
, VLC_PLAYER_WHENCE_RELATIVE
);
619 long long delay_ms
= MS_FROM_VLC_TICK(delay
);
620 long long totdelay_ms
= MS_FROM_VLC_TICK(previous_delay
+ delay
);
621 vlc_player_osd_Message(player
, _("Sub sync: corrected %"PRId64
622 " ms (total delay = %"PRId64
" ms)"),
623 delay_ms
, totdelay_ms
);
626 case ACTIONID_SUBSYNC_RESET
:
627 sys
->subsync
.audio_time
= VLC_TICK_INVALID
;
628 sys
->subsync
.subtitle_time
= VLC_TICK_INVALID
;
629 vlc_player_SetSubtitleDelay(player
, 0, VLC_PLAYER_WHENCE_ABSOLUTE
);
630 vlc_player_osd_Message(player
, _("Sub sync: delay reset"));
633 vlc_assert_unreachable();
637 PLAYER_ACTION_HANDLER(Navigate
)
640 enum vlc_player_nav nav
;
643 #define PLAYER_NAV_FROM_ACTION(navval) \
644 case ACTIONID_NAV_##navval: \
645 nav = VLC_PLAYER_NAV_##navval; \
647 PLAYER_NAV_FROM_ACTION(ACTIVATE
)
648 PLAYER_NAV_FROM_ACTION(UP
)
649 PLAYER_NAV_FROM_ACTION(DOWN
)
650 PLAYER_NAV_FROM_ACTION(LEFT
)
651 PLAYER_NAV_FROM_ACTION(RIGHT
)
652 #undef PLAYER_NAV_FROM_ACTION
654 vlc_assert_unreachable();
656 vlc_player_Navigate(player
, nav
);
659 PLAYER_ACTION_HANDLER(Viewpoint
)
662 vlc_viewpoint_t viewpoint
;
665 case ACTIONID_VIEWPOINT_FOV_IN
:
666 viewpoint
.fov
= -1.f
;
668 case ACTIONID_VIEWPOINT_FOV_OUT
:
669 viewpoint
.fov
= +1.f
;
671 case ACTIONID_VIEWPOINT_ROLL_CLOCK
:
672 viewpoint
.roll
= -1.f
;
674 case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK
:
675 viewpoint
.roll
= +1.f
;
678 vlc_assert_unreachable();
680 vlc_player_UpdateViewpoint(player
, &viewpoint
,
681 VLC_PLAYER_WHENCE_RELATIVE
);
684 PLAYER_ACTION_HANDLER(Record
)
687 VLC_UNUSED(action_id
);
688 vlc_player_ToggleRecording(player
);
692 AudioDeviceCycle(audio_output_t
*aout
, char **p_name
)
696 char *device
= aout_DeviceGet(aout
);
700 int n
= aout_DevicesList(aout
, &ids
, &names
);
705 for (index
= 0; index
< n
; ++index
)
706 if (!strcmp(ids
[index
], device
))
708 index
= (index
+ 1) % n
;
711 ret
= aout_DeviceSet(aout
, ids
[index
]);
713 *p_name
= strdup(names
[index
]);
716 for (int i
= 0; i
< n
; ++i
)
727 PLAYER_ACTION_HANDLER(Aout
)
732 case ACTIONID_VOL_DOWN
:
733 vlc_player_aout_DecrementVolume(player
, 1, NULL
);
735 case ACTIONID_VOL_UP
:
736 vlc_player_aout_IncrementVolume(player
, 1, NULL
);
738 case ACTIONID_VOL_MUTE
:
739 vlc_player_aout_ToggleMute(player
);
741 case ACTIONID_AUDIODEVICE_CYCLE
:
743 audio_output_t
*aout
= vlc_player_aout_Hold(player
);
747 if (!AudioDeviceCycle(aout
, &devname
))
749 vlc_player_osd_Message(player
, _("Audio device: %s"), devname
);
756 vlc_assert_unreachable();
760 PLAYER_ACTION_HANDLER(Vouts
)
765 case ACTIONID_TOGGLE_FULLSCREEN
:
766 vlc_player_vout_ToggleFullscreen(player
);
768 case ACTIONID_LEAVE_FULLSCREEN
:
769 vlc_player_vout_SetFullscreen(player
, false);
771 case ACTIONID_SNAPSHOT
:
772 vlc_player_vout_Snapshot(player
);
774 case ACTIONID_WALLPAPER
:
775 vlc_player_vout_ToggleWallpaperMode(player
);
778 vlc_assert_unreachable();
782 /************************
783 * vout action handling *
784 ************************/
786 #define VOUT_ACTION_HANDLER(name) \
788 action_handler_Vout##name(intf_thread_t *intf, vout_thread_t *vout, vlc_action_id_t action_id)
791 vout_CycleVariable(vout_thread_t
*vout
,
792 char const *varname
, int vartype
, bool next
)
795 var_Get(vout
, varname
, &val
);
797 vlc_value_t
*choices
;
798 var_Change(vout
, varname
, VLC_VAR_GETCHOICES
,
799 &num_choices
, &choices
, NULL
);
801 vlc_value_t
*choice
= choices
;
802 for (size_t curidx
= 0; curidx
< num_choices
; ++curidx
, ++choice
)
803 if ((vartype
== VLC_VAR_FLOAT
&&
804 choice
->f_float
== val
.f_float
) ||
805 (vartype
== VLC_VAR_STRING
&&
806 !strcmp(choice
->psz_string
, val
.psz_string
)))
808 curidx
+= next
? +1 : -1;
809 if (next
&& curidx
== num_choices
)
811 else if (!next
&& curidx
== (size_t)-1)
812 curidx
= num_choices
- 1;
813 choice
= choices
+ curidx
;
816 if (choice
== choices
+ num_choices
)
818 if (vartype
== VLC_VAR_FLOAT
)
819 var_SetFloat(vout
, varname
, choice
->f_float
);
820 else if (vartype
== VLC_VAR_STRING
)
821 var_SetString(vout
, varname
, choice
->psz_string
);
823 if (vartype
== VLC_VAR_STRING
)
825 free(val
.psz_string
);
826 for (size_t i
= 0; i
< num_choices
; ++i
)
827 free(choices
[i
].psz_string
);
832 #define vout_CycleVariable(vout, varname, vartype, next) \
835 static_assert(vartype == VLC_VAR_FLOAT || \
836 vartype == VLC_VAR_STRING, \
837 "vartype must be either VLC_VAR_FLOAT or VLC_VAR_STRING"); \
838 vout_CycleVariable(vout, varname, vartype, next); \
841 VOUT_ACTION_HANDLER(AspectRatio
)
843 VLC_UNUSED(action_id
); VLC_UNUSED(intf
);
844 vout_CycleVariable(vout
, "aspect-ratio", VLC_VAR_STRING
, true);
847 VOUT_ACTION_HANDLER(Crop
)
850 if (action_id
== ACTIONID_CROP
)
852 vout_CycleVariable(vout
, "crop", VLC_VAR_STRING
, true);
859 #define CASE_CROP(crop, var) \
860 case ACTIONID_CROP_##crop: \
861 case ACTIONID_UNCROP_##crop: \
862 varname = "crop-"#var; \
863 delta = action_id == ACTIONID_CROP_##crop? +1 : -1; \
866 CASE_CROP(BOTTOM
, bottom
)
867 CASE_CROP(LEFT
, left
)
868 CASE_CROP(RIGHT
, right
)
871 vlc_assert_unreachable();
873 int crop
= var_GetInteger(vout
, varname
);
874 var_SetInteger(vout
, varname
, crop
+ delta
);
877 VOUT_ACTION_HANDLER(Zoom
)
880 char const *varname
= "zoom";
883 case ACTIONID_TOGGLE_AUTOSCALE
:
884 if (var_GetFloat(vout
, varname
) != 1.f
)
885 var_SetFloat(vout
, varname
, 1.f
);
887 var_ToggleBool(vout
, "autoscale");
889 case ACTIONID_SCALE_DOWN
:
890 case ACTIONID_SCALE_UP
:
892 float zoom
= var_GetFloat(vout
, varname
);
893 float delta
= action_id
== ACTIONID_SCALE_DOWN
?
895 if ((zoom
>= .3f
|| delta
> 0) && (zoom
<= 10.f
|| delta
< 0))
896 var_SetFloat(vout
, varname
, zoom
+ delta
);
900 case ACTIONID_UNZOOM
:
901 vout_CycleVariable(vout
, varname
, VLC_VAR_FLOAT
,
902 action_id
== ACTIONID_ZOOM
);
906 static float const zoom_modes
[] = { .25f
, .5f
, 1.f
, 2.f
};
907 var_SetFloat(vout
, varname
,
908 zoom_modes
[action_id
- ACTIONID_ZOOM_QUARTER
]);
914 VOUT_ACTION_HANDLER(Deinterlace
)
917 if (action_id
== ACTIONID_DEINTERLACE
)
918 var_SetInteger(vout
, "deinterlace",
919 var_GetInteger(vout
, "deinterlace") ? 0 : 1);
920 else if (action_id
== ACTIONID_DEINTERLACE_MODE
)
921 vout_CycleVariable(vout
, "deinterlace-mode", VLC_VAR_STRING
, true);
924 VOUT_ACTION_HANDLER(SubtitleDisplay
)
926 intf_sys_t
*sys
= intf
->p_sys
;
927 const char* psz_sub_margin
= sys
->spu_channel_order
== VLC_VOUT_ORDER_PRIMARY
?
928 "sub-margin" : "secondary-sub-margin";
931 case ACTIONID_SUBPOS_DOWN
:
932 var_DecInteger(vout
, psz_sub_margin
);
934 case ACTIONID_SUBPOS_UP
:
935 var_IncInteger(vout
, psz_sub_margin
);
940 char const *varname
= "sub-text-scale";
942 if (action_id
== ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL
)
946 scale
= var_GetInteger(vout
, varname
);
947 unsigned delta
= (scale
> 100 ? scale
- 100 : 100 - scale
) / 25;
948 delta
= delta
<= 1 ? 10 : 25;
949 if (action_id
== ACTIONID_SUBTITLE_TEXT_SCALE_DOWN
)
952 scale
-= scale
% delta
;
953 scale
= VLC_CLIP(scale
, 25, 500);
955 var_SetInteger(vout
, varname
, scale
);
977 vlc_action_id_t first
;
978 vlc_action_id_t last
;
983 void (*pf_intf
)(intf_thread_t
*, vlc_action_id_t
);
984 void (*pf_playlist
)(intf_thread_t
*, vlc_playlist_t
*, vlc_action_id_t
);
985 void (*pf_player
)(intf_thread_t
*, vlc_player_t
*, vlc_action_id_t
);
986 void (*pf_vout
)(intf_thread_t
*, vout_thread_t
*vout
, vlc_action_id_t
);
991 static struct vlc_action
const actions
[] =
993 #define VLC_ACTION(typeval, first, last, name, lock) \
996 .range = { ACTIONID_##first, ACTIONID_##last }, \
997 .handler.fptr = action_handler_##name, \
998 .pl_need_lock = lock \
1000 #define VLC_ACTION_INTF(first, last, name, lock) \
1001 VLC_ACTION(INTF_ACTION, first, last, Intf ## name, lock)
1002 #define VLC_ACTION_PLAYLIST(first, last, name) \
1003 VLC_ACTION(PLAYLIST_ACTION, first, last, Playlist ## name, true)
1004 #define VLC_ACTION_PLAYER(first, last, name, lock) \
1005 VLC_ACTION(PLAYER_ACTION, first, last, Player ## name, lock)
1006 #define VLC_ACTION_VOUT(first, last, name) \
1007 VLC_ACTION(VOUT_ACTION, first, last, Vout ## name, false)
1009 /* interface actions */
1010 VLC_ACTION_INTF(QUIT
, INTF_POPUP_MENU
, , false)
1011 VLC_ACTION_INTF(COMBO_VOL_FOV_DOWN
, COMBO_VOL_FOV_UP
, ActionCombo
, false)
1012 /* playlist actions */
1013 VLC_ACTION_PLAYLIST(PLAY_CLEAR
, NEXT
, Interact
)
1014 VLC_ACTION_PLAYLIST(LOOP
, RANDOM
, Playback
)
1015 VLC_ACTION_INTF(SET_BOOKMARK1
, PLAY_BOOKMARK10
, PlaylistBookmark
, true)
1016 /* player actions */
1017 VLC_ACTION_PLAYER(PLAY_PAUSE
, FRAME_NEXT
, State
, true)
1018 VLC_ACTION_INTF(JUMP_BACKWARD_EXTRASHORT
, JUMP_FORWARD_LONG
, PlayerSeek
, true)
1019 VLC_ACTION_PLAYER(POSITION
, POSITION
, Position
, true)
1020 VLC_ACTION_PLAYER(PROGRAM_SID_PREV
, DISC_MENU
, NavigateMedia
, true)
1021 VLC_ACTION_PLAYER(AUDIO_TRACK
, SUBTITLE_TRACK
, Track
, true)
1022 VLC_ACTION_PLAYER(AUDIODELAY_DOWN
, SUBDELAY_UP
, Delay
, true)
1023 VLC_ACTION_PLAYER(RATE_NORMAL
, RATE_FASTER_FINE
, Rate
, true)
1024 VLC_ACTION_PLAYER(SUBTITLE_TOGGLE
, SUBTITLE_TOGGLE
, ToggleSubtitle
, true)
1025 VLC_ACTION_PLAYER(SUBTITLE_CONTROL_SECONDARY
, SUBTITLE_CONTROL_SECONDARY
,
1026 ControlSubtitleSecondary
, true)
1027 VLC_ACTION_PLAYER(SUBSYNC_MARKAUDIO
, SUBSYNC_RESET
, SyncSubtitle
, true)
1028 VLC_ACTION_PLAYER(NAV_ACTIVATE
, NAV_RIGHT
, Navigate
, true)
1029 VLC_ACTION_PLAYER(VIEWPOINT_FOV_IN
, VIEWPOINT_ROLL_ANTICLOCK
, Viewpoint
, true)
1030 VLC_ACTION_PLAYER(RECORD
, RECORD
, Record
, true)
1031 VLC_ACTION_PLAYER(VOL_DOWN
, AUDIODEVICE_CYCLE
, Aout
, false)
1032 VLC_ACTION_PLAYER(TOGGLE_FULLSCREEN
, WALLPAPER
, Vouts
, false)
1034 VLC_ACTION_VOUT(ASPECT_RATIO
, ASPECT_RATIO
, AspectRatio
)
1035 VLC_ACTION_VOUT(CROP
, UNCROP_RIGHT
, Crop
)
1036 VLC_ACTION_VOUT(TOGGLE_AUTOSCALE
, ZOOM_DOUBLE
, Zoom
)
1037 VLC_ACTION_VOUT(DEINTERLACE
, DEINTERLACE_MODE
, Deinterlace
)
1038 VLC_ACTION_VOUT(SUBPOS_DOWN
, SUBTITLE_TEXT_SCALE_UP
, SubtitleDisplay
)
1040 { .type
= NULL_ACTION
}
1042 #undef VLC_ACTION_VOUT
1043 #undef VLC_ACTION_PLAYER
1044 #undef VLC_ACTION_PLAYLIST
1045 #undef VLC_ACTION_INTF
1050 handle_action(intf_thread_t
*intf
, vlc_action_id_t action_id
)
1053 for (action_idx
= 0; actions
[action_idx
].type
!= NULL_ACTION
; ++action_idx
)
1054 if (actions
[action_idx
].range
.first
<= action_id
&&
1055 actions
[action_idx
].range
.last
>= action_id
)
1057 if (actions
[action_idx
].type
== NULL_ACTION
)
1058 return msg_Warn(intf
, "no handler for action %d", action_id
);
1059 struct vlc_action
const *action
= actions
+ action_idx
;
1061 vlc_playlist_t
*playlist
= intf
->p_sys
->playlist
;
1062 if (action
->pl_need_lock
)
1063 vlc_playlist_Lock(playlist
);
1065 switch (action
->type
)
1068 action
->handler
.pf_intf(intf
, action_id
);
1070 case PLAYLIST_ACTION
:
1071 action
->handler
.pf_playlist(intf
, playlist
, action_id
);
1076 vlc_player_t
*player
= vlc_playlist_GetPlayer(playlist
);
1077 if (action
->type
== PLAYER_ACTION
)
1078 action
->handler
.pf_player(intf
, player
, action_id
);
1081 vout_thread_t
*vout
= vlc_player_vout_Hold(player
);
1082 action
->handler
.pf_vout(intf
, vout
, action_id
);
1088 vlc_assert_unreachable();
1091 if (action
->pl_need_lock
)
1092 vlc_playlist_Unlock(playlist
);
1100 MouseButtonCallback(vlc_object_t
*obj
, char const *var
,
1101 vlc_value_t oldval
, vlc_value_t newval
, void *data
)
1105 intf_thread_t
*intf
= data
;
1106 intf_sys_t
*sys
= intf
->p_sys
;
1107 vout_thread_t
*vout
= (vout_thread_t
*)obj
;
1109 if ((newval
.i_int
& (1 << MOUSE_BUTTON_LEFT
)) &&
1110 var_GetBool(vout
, "viewpoint-changeable"))
1112 if (!sys
->vrnav
.btn_pressed
)
1114 sys
->vrnav
.btn_pressed
= true;
1115 var_GetCoords(vout
, "mouse-moved", &sys
->vrnav
.x
, &sys
->vrnav
.y
);
1119 sys
->vrnav
.btn_pressed
= false;
1121 unsigned pressed
= newval
.i_int
& ~oldval
.i_int
;
1122 if (pressed
& (1 << MOUSE_BUTTON_LEFT
))
1123 var_SetBool(vlc_object_instance(intf
), "intf-popupmenu", false);
1124 if (pressed
& (1 << MOUSE_BUTTON_CENTER
))
1125 var_TriggerCallback(vlc_object_instance(intf
), "intf-toggle-fscontrol");
1127 if (pressed
& (1 << MOUSE_BUTTON_RIGHT
))
1129 if ((oldval
.i_int
& (1 << MOUSE_BUTTON_RIGHT
))
1130 && !(newval
.i_int
& (1 << MOUSE_BUTTON_RIGHT
)))
1132 var_SetBool(vlc_object_instance(intf
), "intf-popupmenu", true);
1133 for (int i
= MOUSE_BUTTON_WHEEL_UP
; i
<= MOUSE_BUTTON_WHEEL_RIGHT
; i
++)
1134 if (pressed
& (1 << i
))
1136 int keycode
= KEY_MOUSEWHEEL_FROM_BUTTON(i
);
1137 var_SetInteger(vlc_object_instance(intf
), "key-pressed", keycode
);
1144 MouseMovedCallback(vlc_object_t
*obj
, char const *var
,
1145 vlc_value_t ov
, vlc_value_t newval
, void *data
)
1147 VLC_UNUSED(obj
); VLC_UNUSED(var
); VLC_UNUSED(ov
);
1148 intf_sys_t
*sys
= data
;
1149 vlc_player_t
*player
= vlc_playlist_GetPlayer(sys
->playlist
);
1150 if (sys
->vrnav
.btn_pressed
)
1152 int i_horizontal
= newval
.coords
.x
- sys
->vrnav
.x
;
1153 int i_vertical
= newval
.coords
.y
- sys
->vrnav
.y
;
1154 vlc_viewpoint_t viewpoint
=
1156 .yaw
= -i_horizontal
* 0.05f
,
1157 .pitch
= -i_vertical
* 0.05f
,
1159 vlc_player_Lock(player
);
1160 vlc_player_UpdateViewpoint(player
, &viewpoint
,
1161 VLC_PLAYER_WHENCE_RELATIVE
);
1162 vlc_player_Unlock(player
);
1163 sys
->vrnav
.x
= newval
.coords
.x
;
1164 sys
->vrnav
.y
= newval
.coords
.y
;
1170 ViewpointMovedCallback(vlc_object_t
*obj
, char const *var
,
1171 vlc_value_t ov
, vlc_value_t newval
, void *data
)
1173 VLC_UNUSED(obj
); VLC_UNUSED(var
); VLC_UNUSED(ov
);
1174 vlc_player_t
*player
= data
;
1175 vlc_player_Lock(player
);
1176 vlc_player_UpdateViewpoint(player
, (vlc_viewpoint_t
*)newval
.p_address
,
1177 VLC_PLAYER_WHENCE_RELATIVE
);
1178 vlc_player_Unlock(player
);
1183 player_on_vout_changed(vlc_player_t
*player
,
1184 enum vlc_player_vout_action action
, vout_thread_t
*vout
,
1185 enum vlc_vout_order order
, vlc_es_id_t
*es_id
,
1189 intf_thread_t
*intf
= data
;
1191 if (vlc_es_id_GetCat(es_id
) != VIDEO_ES
)
1194 bool vrnav
= var_GetBool(vout
, "viewpoint-changeable");
1197 case VLC_PLAYER_VOUT_STARTED
:
1198 var_AddCallback(vout
, "mouse-button-down", MouseButtonCallback
, intf
);
1199 var_AddCallback(vout
, "mouse-moved", MouseMovedCallback
, intf
->p_sys
);
1201 var_AddCallback(vout
, "viewpoint-moved",
1202 ViewpointMovedCallback
, player
);
1204 case VLC_PLAYER_VOUT_STOPPED
:
1205 var_DelCallback(vout
, "mouse-button-down", MouseButtonCallback
, intf
);
1206 var_DelCallback(vout
, "mouse-moved", MouseMovedCallback
, intf
->p_sys
);
1208 var_DelCallback(vout
, "viewpoint-moved",
1209 ViewpointMovedCallback
, player
);
1212 vlc_assert_unreachable();
1217 ActionCallback(vlc_object_t
*obj
, char const *var
,
1218 vlc_value_t ov
, vlc_value_t newval
, void *data
)
1220 VLC_UNUSED(obj
); VLC_UNUSED(var
); VLC_UNUSED(ov
);
1221 handle_action(data
, newval
.i_int
);
1225 /****************************
1226 * module opening / closing *
1227 ****************************/
1230 Open(vlc_object_t
*this)
1232 intf_thread_t
*intf
= (intf_thread_t
*)this;
1233 intf_sys_t
*sys
= malloc(sizeof(intf_sys_t
));
1236 sys
->vrnav
.btn_pressed
= false;
1237 sys
->playlist
= vlc_intf_GetMainPlaylist(intf
);
1238 sys
->subsync
.audio_time
= sys
->subsync
.subtitle_time
= VLC_TICK_INVALID
;
1239 sys
->spu_channel_order
= VLC_VOUT_ORDER_PRIMARY
;
1240 static struct vlc_player_cbs
const player_cbs
=
1242 .on_vout_changed
= player_on_vout_changed
,
1244 vlc_player_t
*player
= vlc_playlist_GetPlayer(sys
->playlist
);
1245 vlc_player_Lock(player
);
1246 sys
->player_listener
= vlc_player_AddListener(player
, &player_cbs
, intf
);
1247 vlc_player_Unlock(player
);
1248 if (!sys
->player_listener
)
1251 return VLC_EGENERIC
;
1253 var_AddCallback(vlc_object_instance(intf
), "key-action", ActionCallback
, intf
);
1259 Close(vlc_object_t
*this)
1261 intf_thread_t
*intf
= (intf_thread_t
*)this;
1262 intf_sys_t
*sys
= intf
->p_sys
;
1263 vlc_player_t
*player
= vlc_playlist_GetPlayer(sys
->playlist
);
1264 vlc_player_Lock(player
);
1265 vlc_player_RemoveListener(player
, sys
->player_listener
);
1266 vlc_player_Unlock(player
);
1267 var_DelCallback(vlc_object_instance(intf
), "key-action", ActionCallback
, intf
);
1271 static void AutoRun(libvlc_int_t
*libvlc
)
1273 intf_Create(libvlc
, MODULE_STRING
);
1277 set_shortname(N_("Hotkeys"))
1278 set_description(N_("Hotkeys management interface"))
1279 set_capability("interface", 0)
1280 set_callbacks(Open
, Close
)
1281 set_category(CAT_INTERFACE
)
1282 set_subcategory(SUBCAT_INTERFACE_HOTKEYS
)
1285 set_capability("autorun", 20)
1286 set_callback(AutoRun
)