qt: playlist: use item title if available
[vlc.git] / modules / control / hotkeys.c
blobad8ba0e6e7e88f4e863125618ba905e302d39ff7
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 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
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>
39 #include <vlc_spu.h>
40 #include "math.h"
42 struct intf_sys_t
44 vlc_playlist_t *playlist;
45 vlc_player_listener_id *player_listener;
46 struct
48 bool btn_pressed;
49 int x, y;
50 } vrnav;
52 struct
54 vlc_tick_t audio_time;
55 vlc_tick_t subtitle_time;
56 } subsync;
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) \
67 static inline void \
68 action_handler_Intf##name(intf_thread_t *intf, vlc_action_id_t action_id)
70 INTF_ACTION_HANDLER()
72 char const *varname;
73 switch (action_id)
75 case ACTIONID_QUIT:
76 return libvlc_Quit(vlc_object_instance(intf));
77 case ACTIONID_INTF_TOGGLE_FSC:
78 case ACTIONID_INTF_HIDE:
79 varname = "intf-toggle-fscontrol";
80 break;
81 case ACTIONID_INTF_BOSS:
82 varname = "intf-boss";
83 break;
84 case ACTIONID_INTF_POPUP_MENU:
85 varname = "intf-popupmenu";
86 break;
87 default:
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");
98 vout_Release(vout);
99 switch (action_id)
101 case ACTIONID_COMBO_VOL_FOV_DOWN:
102 action_id = !vrnav
103 ? ACTIONID_VOL_DOWN
104 : ACTIONID_VIEWPOINT_FOV_OUT;
105 break;
106 case ACTIONID_COMBO_VOL_FOV_UP:
107 action_id = !vrnav
108 ? ACTIONID_VOL_UP
109 : ACTIONID_VIEWPOINT_FOV_IN;
110 break;
111 default:
112 vlc_assert_unreachable();
114 handle_action(intf, action_id);
117 /****************************
118 * playlist action handling *
119 ****************************/
121 #define PLAYLIST_ACTION_HANDLER(name) \
122 static inline void \
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)
128 VLC_UNUSED(intf);
129 switch (action_id)
131 case ACTIONID_PLAY_CLEAR:
132 vlc_playlist_Clear(playlist);
133 break;
134 case ACTIONID_PREV:
135 vlc_playlist_Prev(playlist);
136 break;
137 case ACTIONID_NEXT:
138 vlc_playlist_Next(playlist);
139 break;
140 default:
141 vlc_assert_unreachable();
145 PLAYLIST_ACTION_HANDLER(Playback)
147 VLC_UNUSED(intf);
148 switch (action_id)
150 case ACTIONID_LOOP:
152 enum vlc_playlist_playback_repeat repeat_mode =
153 vlc_playlist_GetPlaybackRepeat(playlist);
154 switch (repeat_mode)
156 case VLC_PLAYLIST_PLAYBACK_REPEAT_NONE:
157 repeat_mode = VLC_PLAYLIST_PLAYBACK_REPEAT_ALL;
158 break;
159 case VLC_PLAYLIST_PLAYBACK_REPEAT_ALL:
160 repeat_mode = VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT;
161 break;
162 case VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT:
163 repeat_mode = VLC_PLAYLIST_PLAYBACK_REPEAT_NONE;
164 break;
166 vlc_playlist_SetPlaybackRepeat(playlist, repeat_mode);
167 break;
169 case ACTIONID_RANDOM:
171 enum vlc_playlist_playback_order order_mode =
172 vlc_playlist_GetPlaybackOrder(playlist);
173 order_mode =
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);
178 break;
180 default:
181 vlc_assert_unreachable();
185 static inline void
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);
192 if (item)
194 char *uri = input_item_GetURI(item);
195 config_PutPsz(name, uri);
196 msg_Info(intf, "setting playlist bookmark %i to %s", id, uri);
197 free(uri);
201 static inline void
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);
207 size_t i;
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))
214 break;
215 free(item_uri);
217 if (i != count)
218 vlc_playlist_PlayAt(playlist, i);
219 free(bookmark_uri);
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);
227 id += action_id;
228 char *bookmark_name;
229 if (asprintf(&bookmark_name, "bookmark%i", id) == -1)
230 return;
231 vlc_playlist_t *playlist = intf->p_sys->playlist;
232 if (set)
233 playlist_bookmark_Set(intf, playlist, bookmark_name, id);
234 else
235 playlist_bookmark_Play(intf, playlist, bookmark_name);
236 free(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)
250 VLC_UNUSED(intf);
251 switch (action_id)
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);
258 else
259 vlc_player_Pause(player);
260 break;
262 case ACTIONID_PLAY:
263 vlc_player_Start(player);
264 break;
265 case ACTIONID_PAUSE:
266 vlc_player_Pause(player);
267 break;
268 case ACTIONID_STOP:
269 vlc_player_Stop(player);
270 break;
271 case ACTIONID_FRAME_NEXT:
272 vlc_player_NextVideoFrame(player);
273 break;
274 default:
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))
283 return;
284 char const *varname;
285 int sign = +1;
286 switch (action_id)
288 case ACTIONID_JUMP_BACKWARD_EXTRASHORT:
289 sign = -1;
290 /* fall through */
291 case ACTIONID_JUMP_FORWARD_EXTRASHORT:
292 varname = "extrashort-jump-size";
293 break;
294 case ACTIONID_JUMP_BACKWARD_SHORT:
295 sign = -1;
296 /* fall through */
297 case ACTIONID_JUMP_FORWARD_SHORT:
298 varname = "short-jump-size";
299 break;
300 case ACTIONID_JUMP_BACKWARD_MEDIUM:
301 sign = -1;
302 /* fall through */
303 case ACTIONID_JUMP_FORWARD_MEDIUM:
304 varname = "medium-jump-size";
305 break;
306 case ACTIONID_JUMP_BACKWARD_LONG:
307 sign = -1;
308 /* fall through */
309 case ACTIONID_JUMP_FORWARD_LONG:
310 varname = "long-jump-size";
311 break;
312 default:
313 vlc_assert_unreachable();
315 int jmpsz = var_InheritInteger(vlc_object_instance(intf), varname);
316 if (jmpsz >= 0)
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);
326 vout_Release(vout);
329 PLAYER_ACTION_HANDLER(NavigateMedia)
331 VLC_UNUSED(intf);
332 switch (action_id)
334 case ACTIONID_PROGRAM_SID_PREV:
335 vlc_player_SelectPrevProgram(player);
336 break;
337 case ACTIONID_PROGRAM_SID_NEXT:
338 vlc_player_SelectNextProgram(player);
339 break;
340 case ACTIONID_TITLE_PREV:
341 vlc_player_SelectPrevTitle(player);
342 break;
343 case ACTIONID_TITLE_NEXT:
344 vlc_player_SelectNextTitle(player);
345 break;
346 case ACTIONID_CHAPTER_PREV:
347 vlc_player_SelectPrevChapter(player);
348 break;
349 case ACTIONID_CHAPTER_NEXT:
350 vlc_player_SelectNextChapter(player);
351 break;
352 case ACTIONID_DISC_MENU:
353 vlc_player_Navigate(player, VLC_PLAYER_NAV_MENU);
354 break;
355 default:
356 vlc_assert_unreachable();
360 static void CycleSecondarySubtitles(intf_thread_t *intf, vlc_player_t *player,
361 bool next)
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);
366 if (!count)
367 return;
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);
378 assert(track);
380 if (track->selected)
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;
386 else
387 keep_id = track->es_id;
388 ++selected_count;
392 if ((sys->spu_channel_order == VLC_VOUT_ORDER_PRIMARY
393 && selected_count == 1) || selected_count == 0)
395 /* Only cycle the primary subtitle track */
396 if (next)
397 vlc_player_SelectNextTrack(player, cat);
398 else
399 vlc_player_SelectPrevTrack(player, cat);
401 else
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);
410 assert(track);
412 if (track->es_id == cycle_id)
414 index = i;
415 break;
419 /* Look for the next free (unselected) track */
420 while (true)
422 const struct vlc_player_track *track =
423 vlc_player_GetTrackAt(player, cat, index);
425 if (!track->selected)
427 cycle_id = track->es_id;
428 break;
430 /* Unselect if we reach the end of the cycle */
431 else if ((next && index + 1 == count) || (!next && index == 0))
433 cycle_id = NULL;
434 break;
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 */
441 if ( !keep_id )
443 keep_id = cycle_id;
444 cycle_id = NULL;
447 vlc_es_id_t *esIds[] = { keep_id, cycle_id, NULL };
448 vlc_player_SelectEsIdList(player, cat, esIds);
452 PLAYER_ACTION_HANDLER(Track)
454 switch (action_id)
456 case ACTIONID_AUDIO_TRACK:
457 vlc_player_SelectNextTrack(player, AUDIO_ES);
458 break;
459 case ACTIONID_SUBTITLE_REVERSE_TRACK:
460 case ACTIONID_SUBTITLE_TRACK:
461 CycleSecondarySubtitles(intf, player,
462 action_id == ACTIONID_SUBTITLE_TRACK);
463 break;
464 default:
465 vlc_assert_unreachable();
469 PLAYER_ACTION_HANDLER(Delay)
471 intf_sys_t *sys = intf->p_sys;
472 enum { AUDIODELAY, SUBDELAY } type;
473 int delta = 50;
474 switch (action_id)
476 case ACTIONID_AUDIODELAY_DOWN:
477 delta = -50;
478 /* fall-through */
479 case ACTIONID_AUDIODELAY_UP:
480 type = AUDIODELAY;
481 break;
482 case ACTIONID_SUBDELAY_DOWN:
483 delta = -50;
484 /* fall-through */
485 case ACTIONID_SUBDELAY_UP:
486 type = SUBDELAY;
487 break;
488 default:
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);
495 else
497 if (sys->spu_channel_order == VLC_VOUT_ORDER_PRIMARY)
498 vlc_player_SetSubtitleDelay(player, delta, whence);
499 else
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);
512 break;
519 static inline float
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;
526 if (rate < rate_min)
527 rate = rate_min;
528 else if (rate > rate_max)
529 rate = rate_max;
530 return rate * sign;
533 PLAYER_ACTION_HANDLER(Rate)
535 VLC_UNUSED(intf);
536 switch (action_id)
538 case ACTIONID_RATE_SLOWER:
539 vlc_player_DecrementRate(player);
540 break;
541 case ACTIONID_RATE_FASTER:
542 vlc_player_IncrementRate(player);
543 break;
544 default:
546 float rate;
547 switch (action_id)
549 case ACTIONID_RATE_NORMAL:
550 rate = 1.f;
551 break;
552 case ACTIONID_RATE_SLOWER_FINE:
553 case ACTIONID_RATE_FASTER_FINE:
555 int const dir = action_id == ACTIONID_RATE_SLOWER_FINE ?
556 -1 : +1;
557 rate = vlc_player_GetRate(player);
558 rate = AdjustRateFine(rate, dir);
559 break;
561 default:
562 vlc_assert_unreachable();
564 vlc_player_ChangeRate(player, rate);
565 break;
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;
593 switch (action_id)
595 case ACTIONID_SUBSYNC_MARKAUDIO:
596 sys->subsync.audio_time = vlc_tick_now();
597 vlc_player_osd_Message(player, _("Sub sync: bookmarked audio time"));
598 break;
599 case ACTIONID_SUBSYNC_MARKSUB:
600 sys->subsync.subtitle_time = vlc_tick_now();
601 vlc_player_osd_Message(player, _("Sub sync: bookmarked subtitle time"));
602 break;
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!"));
609 break;
611 vlc_tick_t delay =
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);
624 break;
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"));
631 break;
632 default:
633 vlc_assert_unreachable();
637 PLAYER_ACTION_HANDLER(Navigate)
639 VLC_UNUSED(intf);
640 enum vlc_player_nav nav;
641 switch (action_id)
643 #define PLAYER_NAV_FROM_ACTION(navval) \
644 case ACTIONID_NAV_##navval: \
645 nav = VLC_PLAYER_NAV_##navval; \
646 break;
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
653 default:
654 vlc_assert_unreachable();
656 vlc_player_Navigate(player, nav);
659 PLAYER_ACTION_HANDLER(Viewpoint)
661 VLC_UNUSED(intf);
662 vlc_viewpoint_t viewpoint;
663 switch (action_id)
665 case ACTIONID_VIEWPOINT_FOV_IN:
666 viewpoint.fov = -1.f;
667 break;
668 case ACTIONID_VIEWPOINT_FOV_OUT:
669 viewpoint.fov = +1.f;
670 break;
671 case ACTIONID_VIEWPOINT_ROLL_CLOCK:
672 viewpoint.roll = -1.f;
673 break;
674 case ACTIONID_VIEWPOINT_ROLL_ANTICLOCK:
675 viewpoint.roll = +1.f;
676 break;
677 default:
678 vlc_assert_unreachable();
680 vlc_player_UpdateViewpoint(player, &viewpoint,
681 VLC_PLAYER_WHENCE_RELATIVE);
684 PLAYER_ACTION_HANDLER(Record)
686 VLC_UNUSED(intf);
687 VLC_UNUSED(action_id);
688 vlc_player_ToggleRecording(player);
691 static int
692 AudioDeviceCycle(audio_output_t *aout, char **p_name)
694 int ret = -1;
696 char *device = aout_DeviceGet(aout);
697 if (!device)
698 goto end;
699 char **ids, **names;
700 int n = aout_DevicesList(aout, &ids, &names);
701 if (n == -1)
702 goto end;
704 int index;
705 for (index = 0; index < n; ++index)
706 if (!strcmp(ids[index], device))
708 index = (index + 1) % n;
709 break;
711 ret = aout_DeviceSet(aout, ids[index]);
712 if (p_name != NULL)
713 *p_name = strdup(names[index]);
715 free(device);
716 for (int i = 0; i < n; ++i)
718 free(ids[i]);
719 free(names[i]);
721 free(ids);
722 free(names);
723 end:
724 return ret;
727 PLAYER_ACTION_HANDLER(Aout)
729 VLC_UNUSED(intf);
730 switch (action_id)
732 case ACTIONID_VOL_DOWN:
733 vlc_player_aout_DecrementVolume(player, 1, NULL);
734 break;
735 case ACTIONID_VOL_UP:
736 vlc_player_aout_IncrementVolume(player, 1, NULL);
737 break;
738 case ACTIONID_VOL_MUTE:
739 vlc_player_aout_ToggleMute(player);
740 break;
741 case ACTIONID_AUDIODEVICE_CYCLE:
743 audio_output_t *aout = vlc_player_aout_Hold(player);
744 if (!aout)
745 break;
746 char *devname;
747 if (!AudioDeviceCycle(aout, &devname))
749 vlc_player_osd_Message(player, _("Audio device: %s"), devname);
750 free(devname);
752 aout_Release(aout);
753 break;
755 default:
756 vlc_assert_unreachable();
760 PLAYER_ACTION_HANDLER(Vouts)
762 VLC_UNUSED(intf);
763 switch (action_id)
765 case ACTIONID_TOGGLE_FULLSCREEN:
766 vlc_player_vout_ToggleFullscreen(player);
767 break;
768 case ACTIONID_LEAVE_FULLSCREEN:
769 vlc_player_vout_SetFullscreen(player, false);
770 break;
771 case ACTIONID_SNAPSHOT:
772 vlc_player_vout_Snapshot(player);
773 break;
774 case ACTIONID_WALLPAPER:
775 vlc_player_vout_ToggleWallpaperMode(player);
776 break;
777 default:
778 vlc_assert_unreachable();
782 /************************
783 * vout action handling *
784 ************************/
786 #define VOUT_ACTION_HANDLER(name) \
787 static inline void \
788 action_handler_Vout##name(intf_thread_t *intf, vout_thread_t *vout, vlc_action_id_t action_id)
790 static inline void
791 vout_CycleVariable(vout_thread_t *vout,
792 char const *varname, int vartype, bool next)
794 vlc_value_t val;
795 var_Get(vout, varname, &val);
796 size_t num_choices;
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)
810 curidx = 0;
811 else if (!next && curidx == (size_t)-1)
812 curidx = num_choices - 1;
813 choice = choices + curidx;
814 break;
816 if (choice == choices + num_choices)
817 choice = 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);
829 free(choices);
832 #define vout_CycleVariable(vout, varname, vartype, next) \
833 do \
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); \
839 } while (0)
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)
849 VLC_UNUSED(intf);
850 if (action_id == ACTIONID_CROP)
852 vout_CycleVariable(vout, "crop", VLC_VAR_STRING, true);
853 return;
855 char const *varname;
856 int delta;
857 switch (action_id)
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; \
864 break;
865 CASE_CROP(TOP, top)
866 CASE_CROP(BOTTOM, bottom)
867 CASE_CROP(LEFT, left)
868 CASE_CROP(RIGHT, right)
869 #undef CASE_CROP
870 default:
871 vlc_assert_unreachable();
873 int crop = var_GetInteger(vout, varname);
874 var_SetInteger(vout, varname, crop + delta);
877 VOUT_ACTION_HANDLER(Zoom)
879 VLC_UNUSED(intf);
880 char const *varname = "zoom";
881 switch (action_id)
883 case ACTIONID_TOGGLE_AUTOSCALE:
884 if (var_GetFloat(vout, varname) != 1.f)
885 var_SetFloat(vout, varname, 1.f);
886 else
887 var_ToggleBool(vout, "autoscale");
888 break;
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 ?
894 -.1f : +.1f;
895 if ((zoom >= .3f || delta > 0) && (zoom <= 10.f || delta < 0))
896 var_SetFloat(vout, varname, zoom + delta);
897 break;
899 case ACTIONID_ZOOM:
900 case ACTIONID_UNZOOM:
901 vout_CycleVariable(vout, varname, VLC_VAR_FLOAT,
902 action_id == ACTIONID_ZOOM);
903 break;
904 default:
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]);
909 break;
914 VOUT_ACTION_HANDLER(Deinterlace)
916 VLC_UNUSED(intf);
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";
929 switch (action_id)
931 case ACTIONID_SUBPOS_DOWN:
932 var_DecInteger(vout, psz_sub_margin);
933 break;
934 case ACTIONID_SUBPOS_UP:
935 var_IncInteger(vout, psz_sub_margin);
936 break;
937 default:
939 // FIXME all vouts
940 char const *varname = "sub-text-scale";
941 int scale;
942 if (action_id == ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL)
943 scale = 100;
944 else
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)
950 delta = -delta;
951 scale += delta;
952 scale -= scale % delta;
953 scale = VLC_CLIP(scale, 25, 500);
955 var_SetInteger(vout, varname, scale);
956 break;
961 /****************
962 * action table *
963 ****************/
965 struct vlc_action
967 enum
969 NULL_ACTION = -1,
970 INTF_ACTION,
971 PLAYLIST_ACTION,
972 PLAYER_ACTION,
973 VOUT_ACTION,
974 } type;
975 struct
977 vlc_action_id_t first;
978 vlc_action_id_t last;
979 } range;
980 union
982 void *fptr;
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);
987 } handler;
988 bool pl_need_lock;
991 static struct vlc_action const actions[] =
993 #define VLC_ACTION(typeval, first, last, name, lock) \
995 .type = typeval, \
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)
1033 /* vout actions */
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)
1039 /* null action */
1040 { .type = NULL_ACTION }
1042 #undef VLC_ACTION_VOUT
1043 #undef VLC_ACTION_PLAYER
1044 #undef VLC_ACTION_PLAYLIST
1045 #undef VLC_ACTION_INTF
1046 #undef VLC_ACTION
1049 static void
1050 handle_action(intf_thread_t *intf, vlc_action_id_t action_id)
1052 size_t action_idx;
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)
1056 break;
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)
1067 case INTF_ACTION:
1068 action->handler.pf_intf(intf, action_id);
1069 break;
1070 case PLAYLIST_ACTION:
1071 action->handler.pf_playlist(intf, playlist, action_id);
1072 break;
1073 case PLAYER_ACTION:
1074 case VOUT_ACTION:
1076 vlc_player_t *player = vlc_playlist_GetPlayer(playlist);
1077 if (action->type == PLAYER_ACTION)
1078 action->handler.pf_player(intf, player, action_id);
1079 else
1081 vout_thread_t *vout = vlc_player_vout_Hold(player);
1082 action->handler.pf_vout(intf, vout, action_id);
1083 vout_Release(vout);
1085 break;
1087 default:
1088 vlc_assert_unreachable();
1091 if (action->pl_need_lock)
1092 vlc_playlist_Unlock(playlist);
1095 /******************
1096 * vout callbacks *
1097 ******************/
1099 static int
1100 MouseButtonCallback(vlc_object_t *obj, char const *var,
1101 vlc_value_t oldval, vlc_value_t newval, void *data)
1103 VLC_UNUSED(var);
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);
1118 else
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");
1126 #ifndef _WIN32
1127 if (pressed & (1 << MOUSE_BUTTON_RIGHT))
1128 #else
1129 if ((oldval.i_int & (1 << MOUSE_BUTTON_RIGHT))
1130 && !(newval.i_int & (1 << MOUSE_BUTTON_RIGHT)))
1131 #endif
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);
1140 return VLC_SUCCESS;
1143 static int
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;
1166 return VLC_SUCCESS;
1169 static int
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);
1179 return VLC_SUCCESS;
1182 static void
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,
1186 void *data)
1188 VLC_UNUSED(order);
1189 intf_thread_t *intf = data;
1191 if (vlc_es_id_GetCat(es_id) != VIDEO_ES)
1192 return;
1194 bool vrnav = var_GetBool(vout, "viewpoint-changeable");
1195 switch (action)
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);
1200 if (vrnav)
1201 var_AddCallback(vout, "viewpoint-moved",
1202 ViewpointMovedCallback, player);
1203 break;
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);
1207 if (vrnav)
1208 var_DelCallback(vout, "viewpoint-moved",
1209 ViewpointMovedCallback, player);
1210 break;
1211 default:
1212 vlc_assert_unreachable();
1216 static int
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);
1222 return VLC_SUCCESS;
1225 /****************************
1226 * module opening / closing *
1227 ****************************/
1229 static int
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));
1234 if (!sys)
1235 return VLC_ENOMEM;
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)
1250 free(sys);
1251 return VLC_EGENERIC;
1253 var_AddCallback(vlc_object_instance(intf), "key-action", ActionCallback, intf);
1254 intf->p_sys = sys;
1255 return VLC_SUCCESS;
1258 static void
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);
1268 free(sys);
1271 static void AutoRun(libvlc_int_t *libvlc)
1273 intf_Create(libvlc, MODULE_STRING);
1276 vlc_module_begin ()
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)
1284 add_submodule()
1285 set_capability("autorun", 20)
1286 set_callback(AutoRun)
1287 vlc_module_end ()