/src/vlc/src/misc/actions.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * actions.c: handle vlc actions |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2003-2016 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org> |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program; if not, write to the Free Software Foundation, |
20 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
21 | | *****************************************************************************/ |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | # include <config.h> |
25 | | #endif |
26 | | |
27 | | /** |
28 | | * \file |
29 | | * This file defines functions and structures for hotkey handling in vlc |
30 | | */ |
31 | | |
32 | | #include <assert.h> |
33 | | #include <stdlib.h> |
34 | | #include <limits.h> |
35 | | #ifdef HAVE_SEARCH_H |
36 | | # include <search.h> |
37 | | #endif |
38 | | #include <errno.h> |
39 | | |
40 | | #include <vlc_common.h> |
41 | | #include <vlc_actions.h> |
42 | | #include <vlc_charset.h> |
43 | | #include "../libvlc.h" |
44 | | |
45 | | static const struct key_descriptor |
46 | | { |
47 | | const char psz[20]; |
48 | | uint32_t i_code; |
49 | | } s_keys[] = |
50 | | { /* Alphabetical order */ |
51 | | { N_("Backspace"), KEY_BACKSPACE }, |
52 | | { N_("Brightness Down"), KEY_BRIGHTNESS_DOWN }, |
53 | | { N_("Brightness Up"), KEY_BRIGHTNESS_UP }, |
54 | | { N_("Browser Back"), KEY_BROWSER_BACK }, |
55 | | { N_("Browser Favorites"), KEY_BROWSER_FAVORITES }, |
56 | | { N_("Browser Forward"), KEY_BROWSER_FORWARD }, |
57 | | { N_("Browser Home"), KEY_BROWSER_HOME }, |
58 | | { N_("Browser Refresh"), KEY_BROWSER_REFRESH }, |
59 | | { N_("Browser Search"), KEY_BROWSER_SEARCH }, |
60 | | { N_("Browser Stop"), KEY_BROWSER_STOP }, |
61 | | { N_("Delete"), KEY_DELETE }, |
62 | | { N_("Down"), KEY_DOWN }, |
63 | | { N_("End"), KEY_END }, |
64 | | { N_("Enter"), KEY_ENTER }, |
65 | | { N_("Esc"), KEY_ESC }, |
66 | | { N_("F1"), KEY_F1 }, |
67 | | { N_("F10"), KEY_F10 }, |
68 | | { N_("F11"), KEY_F11 }, |
69 | | { N_("F12"), KEY_F12 }, |
70 | | { N_("F13"), KEY_F(13) }, |
71 | | { N_("F14"), KEY_F(14) }, |
72 | | { N_("F15"), KEY_F(15) }, |
73 | | { N_("F16"), KEY_F(16) }, |
74 | | { N_("F17"), KEY_F(17) }, |
75 | | { N_("F18"), KEY_F(18) }, |
76 | | { N_("F19"), KEY_F(19) }, |
77 | | { N_("F2"), KEY_F2 }, |
78 | | { N_("F20"), KEY_F(20) }, |
79 | | { N_("F21"), KEY_F(21) }, |
80 | | { N_("F22"), KEY_F(22) }, |
81 | | { N_("F23"), KEY_F(23) }, |
82 | | { N_("F24"), KEY_F(24) }, |
83 | | { N_("F25"), KEY_F(25) }, |
84 | | { N_("F26"), KEY_F(26) }, |
85 | | { N_("F27"), KEY_F(27) }, |
86 | | { N_("F28"), KEY_F(28) }, |
87 | | { N_("F29"), KEY_F(29) }, |
88 | | { N_("F3"), KEY_F3 }, |
89 | | { N_("F30"), KEY_F(30) }, |
90 | | { N_("F31"), KEY_F(31) }, |
91 | | { N_("F32"), KEY_F(32) }, |
92 | | { N_("F33"), KEY_F(33) }, |
93 | | { N_("F34"), KEY_F(34) }, |
94 | | { N_("F35"), KEY_F(35) }, |
95 | | { N_("F4"), KEY_F4 }, |
96 | | { N_("F5"), KEY_F5 }, |
97 | | { N_("F6"), KEY_F6 }, |
98 | | { N_("F7"), KEY_F7 }, |
99 | | { N_("F8"), KEY_F8 }, |
100 | | { N_("F9"), KEY_F9 }, |
101 | | { N_("Home"), KEY_HOME }, |
102 | | { N_("Insert"), KEY_INSERT }, |
103 | | { N_("Left"), KEY_LEFT }, |
104 | | { N_("Media Angle"), KEY_MEDIA_ANGLE }, |
105 | | { N_("Media Audio Track"), KEY_MEDIA_AUDIO }, |
106 | | { N_("Media Forward"), KEY_MEDIA_FORWARD }, |
107 | | { N_("Media Menu"), KEY_MEDIA_MENU }, |
108 | | { N_("Media Next Frame"), KEY_MEDIA_FRAME_NEXT }, |
109 | | { N_("Media Next Track"), KEY_MEDIA_NEXT_TRACK }, |
110 | | { N_("Media Play Pause"), KEY_MEDIA_PLAY_PAUSE }, |
111 | | { N_("Media Prev Frame"), KEY_MEDIA_FRAME_PREV }, |
112 | | { N_("Media Prev Track"), KEY_MEDIA_PREV_TRACK }, |
113 | | { N_("Media Record"), KEY_MEDIA_RECORD }, |
114 | | { N_("Media Repeat"), KEY_MEDIA_REPEAT }, |
115 | | { N_("Media Rewind"), KEY_MEDIA_REWIND }, |
116 | | { N_("Media Select"), KEY_MEDIA_SELECT }, |
117 | | { N_("Media Shuffle"), KEY_MEDIA_SHUFFLE }, |
118 | | { N_("Media Stop"), KEY_MEDIA_STOP }, |
119 | | { N_("Media Subtitle"), KEY_MEDIA_SUBTITLE }, |
120 | | { N_("Media Time"), KEY_MEDIA_TIME }, |
121 | | { N_("Media View"), KEY_MEDIA_VIEW }, |
122 | | { N_("Menu"), KEY_MENU }, |
123 | | { N_("Mouse Wheel Down"), KEY_MOUSEWHEELDOWN }, |
124 | | { N_("Mouse Wheel Left"), KEY_MOUSEWHEELLEFT }, |
125 | | { N_("Mouse Wheel Right"), KEY_MOUSEWHEELRIGHT }, |
126 | | { N_("Mouse Wheel Up"), KEY_MOUSEWHEELUP }, |
127 | | { N_("Page Down"), KEY_PAGEDOWN }, |
128 | | { N_("Page Up"), KEY_PAGEUP }, |
129 | | { N_("Pause"), KEY_PAUSE }, |
130 | | { N_("Print"), KEY_PRINT }, |
131 | | { N_("Right"), KEY_RIGHT }, |
132 | | { N_("Space"), ' ' }, |
133 | | { N_("Tab"), KEY_TAB }, |
134 | | { N_("Unset"), KEY_UNSET }, |
135 | | { N_("Up"), KEY_UP }, |
136 | | { N_("Volume Down"), KEY_VOLUME_DOWN }, |
137 | | { N_("Volume Mute"), KEY_VOLUME_MUTE }, |
138 | | { N_("Volume Up"), KEY_VOLUME_UP }, |
139 | | { N_("Zoom In"), KEY_ZOOM_IN }, |
140 | | { N_("Zoom Out"), KEY_ZOOM_OUT }, |
141 | | }; |
142 | | |
143 | | static int keystrcmp (const void *key, const void *elem) |
144 | 37.6k | { |
145 | 37.6k | const char *sa = key, *sb = elem; |
146 | 37.6k | return strcmp (sa, sb); |
147 | 37.6k | } |
148 | | |
149 | | /* Convert Unicode code point to UTF-8 */ |
150 | | static char *utf8_cp (uint_fast32_t cp, char *buf) |
151 | 0 | { |
152 | 0 | if (cp < (1 << 7)) |
153 | 0 | { |
154 | 0 | buf[1] = 0; |
155 | 0 | buf[0] = cp; |
156 | 0 | } |
157 | 0 | else if (cp < (1 << 11)) |
158 | 0 | { |
159 | 0 | buf[2] = 0; |
160 | 0 | buf[1] = 0x80 | (cp & 0x3F); |
161 | 0 | cp >>= 6; |
162 | 0 | buf[0] = 0xC0 | cp; |
163 | 0 | } |
164 | 0 | else if (cp < (1 << 16)) |
165 | 0 | { |
166 | 0 | buf[3] = 0; |
167 | 0 | buf[2] = 0x80 | (cp & 0x3F); |
168 | 0 | cp >>= 6; |
169 | 0 | buf[1] = 0x80 | (cp & 0x3F); |
170 | 0 | cp >>= 6; |
171 | 0 | buf[0] = 0xE0 | cp; |
172 | 0 | } |
173 | 0 | else if (cp < (1 << 21)) |
174 | 0 | { |
175 | 0 | buf[4] = 0; |
176 | 0 | buf[3] = 0x80 | (cp & 0x3F); |
177 | 0 | cp >>= 6; |
178 | 0 | buf[2] = 0x80 | (cp & 0x3F); |
179 | 0 | cp >>= 6; |
180 | 0 | buf[1] = 0x80 | (cp & 0x3F); |
181 | 0 | cp >>= 6; |
182 | 0 | buf[0] = 0xE0 | cp; |
183 | 0 | } |
184 | 0 | else |
185 | 0 | return NULL; |
186 | 0 | return buf; |
187 | 0 | } |
188 | | |
189 | | /** |
190 | | * Parse a human-readable string representation of a VLC key code. |
191 | | * @note This only works with the American English representation |
192 | | * (a.k.a. C or POSIX), not with the local representation returned from |
193 | | * vlc_keycode2str(). |
194 | | * @return a VLC key code, or KEY_UNSET on failure. |
195 | | */ |
196 | | uint_fast32_t vlc_str2keycode (const char *name) |
197 | 6.21k | { |
198 | 6.21k | uint_fast32_t mods = 0; |
199 | 6.21k | uint32_t code; |
200 | | |
201 | 6.21k | for (;;) |
202 | 9.77k | { |
203 | 9.77k | size_t len = strcspn (name, "-+"); |
204 | 9.77k | if (len == 0 || name[len] == '\0') |
205 | 6.21k | break; |
206 | | |
207 | 3.56k | if (len == 4 && !strncasecmp (name, "Ctrl", 4)) |
208 | 1.24k | mods |= KEY_MODIFIER_CTRL; |
209 | 3.56k | if (len == 3 && !strncasecmp (name, "Alt", 3)) |
210 | 1.02k | mods |= KEY_MODIFIER_ALT; |
211 | 3.56k | if (len == 5 && !strncasecmp (name, "Shift", 5)) |
212 | 1.29k | mods |= KEY_MODIFIER_SHIFT; |
213 | 3.56k | if (len == 4 && !strncasecmp (name, "Meta", 4)) |
214 | 0 | mods |= KEY_MODIFIER_META; |
215 | 3.56k | if (len == 7 && !strncasecmp (name, "Command", 7)) |
216 | 0 | mods |= KEY_MODIFIER_COMMAND; |
217 | | |
218 | 3.56k | name += len + 1; |
219 | 3.56k | } |
220 | | |
221 | 6.21k | struct key_descriptor *d = bsearch (name, s_keys, ARRAY_SIZE(s_keys), |
222 | 6.21k | sizeof (s_keys[0]), keystrcmp); |
223 | 6.21k | if (d != NULL) |
224 | 2.75k | code = d->i_code; |
225 | 3.45k | else if (vlc_towc (name, &code) <= 0) |
226 | 0 | return KEY_UNSET; |
227 | | |
228 | 6.21k | if (code != KEY_UNSET) |
229 | 6.21k | code |= mods; |
230 | 6.21k | return code; |
231 | 6.21k | } |
232 | | |
233 | | static const char *nooptext (const char *txt) |
234 | 0 | { |
235 | 0 | return txt; |
236 | 0 | } |
237 | | |
238 | | /** |
239 | | * Format a human-readable and unique representation of a VLC key code |
240 | | * (including modifiers). |
241 | | * @param code key code to translate to a string |
242 | | * @param locale true to get a localized string, |
243 | | * false to get a C string suitable for 'vlcrc' |
244 | | * @return a heap-allocated string, or NULL on error. |
245 | | */ |
246 | | char *vlc_keycode2str (uint_fast32_t code, bool locale) |
247 | 0 | { |
248 | 0 | const char *(*tr)(const char *) = locale ? vlc_gettext : nooptext; |
249 | 0 | const char *name; |
250 | 0 | char *str, buf[5]; |
251 | 0 | uintptr_t key = code & ~KEY_MODIFIER; |
252 | |
|
253 | 0 | for (size_t i = 0; i < ARRAY_SIZE(s_keys); i++) |
254 | 0 | if (s_keys[i].i_code == key) |
255 | 0 | { |
256 | 0 | name = s_keys[i].psz; |
257 | 0 | goto found; |
258 | 0 | } |
259 | | |
260 | 0 | if (utf8_cp (key, buf) == NULL) |
261 | 0 | return NULL; |
262 | 0 | name = buf; |
263 | |
|
264 | 0 | found: |
265 | 0 | if (asprintf (&str, "%s%s%s%s%s%s", |
266 | 0 | (code & KEY_MODIFIER_CTRL) ? tr(N_("Ctrl+")) : "", |
267 | 0 | (code & KEY_MODIFIER_ALT) ? tr(N_("Alt+")) : "", |
268 | 0 | (code & KEY_MODIFIER_SHIFT) ? tr(N_("Shift+")) : "", |
269 | 0 | (code & KEY_MODIFIER_META) ? tr(N_("Meta+")) : "", |
270 | 0 | (code & KEY_MODIFIER_COMMAND) ? tr(N_("Command+")) : "", |
271 | 0 | tr(name)) == -1) |
272 | 0 | return NULL; |
273 | 0 | return str; |
274 | 0 | } |
275 | | |
276 | | |
277 | | /*** VLC key map ***/ |
278 | | |
279 | | #define MAXACTION 27 |
280 | | static const struct name2action |
281 | | { |
282 | | char psz[MAXACTION]; |
283 | | vlc_action_id_t id; |
284 | | } s_names2actions[] = |
285 | | { |
286 | | /* *MUST* be sorted (ASCII order) */ |
287 | | { "aspect-ratio", ACTIONID_ASPECT_RATIO, }, |
288 | | { "audio-track", ACTIONID_AUDIO_TRACK, }, |
289 | | { "audiodelay-down", ACTIONID_AUDIODELAY_DOWN, }, |
290 | | { "audiodelay-up", ACTIONID_AUDIODELAY_UP, }, |
291 | | { "audiodevice-cycle", ACTIONID_AUDIODEVICE_CYCLE, }, |
292 | | { "chapter-next", ACTIONID_CHAPTER_NEXT, }, |
293 | | { "chapter-prev", ACTIONID_CHAPTER_PREV, }, |
294 | | { "clear-playlist", ACTIONID_PLAY_CLEAR, }, |
295 | | { "crop", ACTIONID_CROP, }, |
296 | | { "crop-bottom", ACTIONID_CROP_BOTTOM, }, |
297 | | { "crop-left", ACTIONID_CROP_LEFT, }, |
298 | | { "crop-right", ACTIONID_CROP_RIGHT, }, |
299 | | { "crop-top", ACTIONID_CROP_TOP, }, |
300 | | { "decr-scalefactor", ACTIONID_SCALE_DOWN, }, |
301 | | { "deinterlace", ACTIONID_DEINTERLACE, }, |
302 | | { "deinterlace-mode", ACTIONID_DEINTERLACE_MODE, }, |
303 | | { "disc-menu", ACTIONID_DISC_MENU, }, |
304 | | { "faster", ACTIONID_RATE_FASTER, }, |
305 | | { "frame-next", ACTIONID_FRAME_NEXT, }, |
306 | | { "incr-scalefactor", ACTIONID_SCALE_UP, }, |
307 | | { "intf-boss", ACTIONID_INTF_BOSS, }, |
308 | | { "intf-popup-menu", ACTIONID_INTF_POPUP_MENU, }, |
309 | | { "intf-show", ACTIONID_INTF_TOGGLE_FSC, }, |
310 | | { "jump+extrashort", ACTIONID_JUMP_FORWARD_EXTRASHORT, }, |
311 | | { "jump+long", ACTIONID_JUMP_FORWARD_LONG, }, |
312 | | { "jump+medium", ACTIONID_JUMP_FORWARD_MEDIUM, }, |
313 | | { "jump+short", ACTIONID_JUMP_FORWARD_SHORT, }, |
314 | | { "jump-extrashort", ACTIONID_JUMP_BACKWARD_EXTRASHORT, }, |
315 | | { "jump-long", ACTIONID_JUMP_BACKWARD_LONG, }, |
316 | | { "jump-medium", ACTIONID_JUMP_BACKWARD_MEDIUM, }, |
317 | | { "jump-short", ACTIONID_JUMP_BACKWARD_SHORT, }, |
318 | | { "leave-fullscreen", ACTIONID_LEAVE_FULLSCREEN, }, |
319 | | { "loop", ACTIONID_LOOP, }, |
320 | | { "nav-activate", ACTIONID_NAV_ACTIVATE, }, |
321 | | { "nav-down", ACTIONID_NAV_DOWN, }, |
322 | | { "nav-left", ACTIONID_NAV_LEFT, }, |
323 | | { "nav-right", ACTIONID_NAV_RIGHT, }, |
324 | | { "nav-up", ACTIONID_NAV_UP, }, |
325 | | { "next", ACTIONID_NEXT, }, |
326 | | { "pause", ACTIONID_PAUSE, }, |
327 | | { "play", ACTIONID_PLAY, }, |
328 | | { "play-bookmark1", ACTIONID_PLAY_BOOKMARK1, }, |
329 | | { "play-bookmark10", ACTIONID_PLAY_BOOKMARK10, }, |
330 | | { "play-bookmark2", ACTIONID_PLAY_BOOKMARK2, }, |
331 | | { "play-bookmark3", ACTIONID_PLAY_BOOKMARK3, }, |
332 | | { "play-bookmark4", ACTIONID_PLAY_BOOKMARK4, }, |
333 | | { "play-bookmark5", ACTIONID_PLAY_BOOKMARK5, }, |
334 | | { "play-bookmark6", ACTIONID_PLAY_BOOKMARK6, }, |
335 | | { "play-bookmark7", ACTIONID_PLAY_BOOKMARK7, }, |
336 | | { "play-bookmark8", ACTIONID_PLAY_BOOKMARK8, }, |
337 | | { "play-bookmark9", ACTIONID_PLAY_BOOKMARK9, }, |
338 | | { "play-pause", ACTIONID_PLAY_PAUSE, }, |
339 | | { "position", ACTIONID_POSITION, }, |
340 | | { "prev", ACTIONID_PREV, }, |
341 | | { "program-sid-next", ACTIONID_PROGRAM_SID_NEXT, }, |
342 | | { "program-sid-prev", ACTIONID_PROGRAM_SID_PREV, }, |
343 | | { "projection-toggle", ACTIONID_PROJECTION_TOGGLE, }, |
344 | | { "quit", ACTIONID_QUIT, }, |
345 | | { "random", ACTIONID_RANDOM, }, |
346 | | { "rate-faster-fine", ACTIONID_RATE_FASTER_FINE, }, |
347 | | { "rate-normal", ACTIONID_RATE_NORMAL, }, |
348 | | { "rate-slower-fine", ACTIONID_RATE_SLOWER_FINE, }, |
349 | | { "record", ACTIONID_RECORD, }, |
350 | | { "set-bookmark1", ACTIONID_SET_BOOKMARK1, }, |
351 | | { "set-bookmark10", ACTIONID_SET_BOOKMARK10, }, |
352 | | { "set-bookmark2", ACTIONID_SET_BOOKMARK2, }, |
353 | | { "set-bookmark3", ACTIONID_SET_BOOKMARK3, }, |
354 | | { "set-bookmark4", ACTIONID_SET_BOOKMARK4, }, |
355 | | { "set-bookmark5", ACTIONID_SET_BOOKMARK5, }, |
356 | | { "set-bookmark6", ACTIONID_SET_BOOKMARK6, }, |
357 | | { "set-bookmark7", ACTIONID_SET_BOOKMARK7, }, |
358 | | { "set-bookmark8", ACTIONID_SET_BOOKMARK8, }, |
359 | | { "set-bookmark9", ACTIONID_SET_BOOKMARK9, }, |
360 | | { "slower", ACTIONID_RATE_SLOWER, }, |
361 | | { "snapshot", ACTIONID_SNAPSHOT, }, |
362 | | { "stop", ACTIONID_STOP, }, |
363 | | { "subdelay-down", ACTIONID_SUBDELAY_DOWN, }, |
364 | | { "subdelay-up", ACTIONID_SUBDELAY_UP, }, |
365 | | { "subpos-down", ACTIONID_SUBPOS_DOWN, }, |
366 | | { "subpos-up", ACTIONID_SUBPOS_UP, }, |
367 | | { "subsync-apply", ACTIONID_SUBSYNC_APPLY, }, |
368 | | { "subsync-markaudio", ACTIONID_SUBSYNC_MARKAUDIO, }, |
369 | | { "subsync-marksub", ACTIONID_SUBSYNC_MARKSUB, }, |
370 | | { "subsync-reset", ACTIONID_SUBSYNC_RESET, }, |
371 | | { "subtitle-control-secondary", ACTIONID_SUBTITLE_CONTROL_SECONDARY, }, |
372 | | { "subtitle-revtrack", ACTIONID_SUBTITLE_REVERSE_TRACK, }, |
373 | | { "subtitle-text-scale-down", ACTIONID_SUBTITLE_TEXT_SCALE_DOWN, }, |
374 | | { "subtitle-text-scale-normal", ACTIONID_SUBTITLE_TEXT_SCALE_NORMAL, }, |
375 | | { "subtitle-text-scale-up", ACTIONID_SUBTITLE_TEXT_SCALE_UP, }, |
376 | | { "subtitle-toggle", ACTIONID_SUBTITLE_TOGGLE, }, |
377 | | { "subtitle-track", ACTIONID_SUBTITLE_TRACK, }, |
378 | | { "title-next", ACTIONID_TITLE_NEXT, }, |
379 | | { "title-prev", ACTIONID_TITLE_PREV, }, |
380 | | { "toggle-autoscale", ACTIONID_TOGGLE_AUTOSCALE, }, |
381 | | { "toggle-fullscreen", ACTIONID_TOGGLE_FULLSCREEN, }, |
382 | | { "uncrop-bottom", ACTIONID_UNCROP_BOTTOM, }, |
383 | | { "uncrop-left", ACTIONID_UNCROP_LEFT, }, |
384 | | { "uncrop-right", ACTIONID_UNCROP_RIGHT, }, |
385 | | { "uncrop-top", ACTIONID_UNCROP_TOP, }, |
386 | | { "unzoom", ACTIONID_UNZOOM, }, |
387 | | { "viewpoint-fov-in", ACTIONID_VIEWPOINT_FOV_IN, }, |
388 | | { "viewpoint-fov-out", ACTIONID_VIEWPOINT_FOV_OUT, }, |
389 | | { "viewpoint-roll-anticlock", ACTIONID_VIEWPOINT_ROLL_ANTICLOCK, }, |
390 | | { "viewpoint-roll-clock", ACTIONID_VIEWPOINT_ROLL_CLOCK, }, |
391 | | { "vol-down", ACTIONID_VOL_DOWN, }, |
392 | | { "vol-mute", ACTIONID_VOL_MUTE, }, |
393 | | { "vol-up", ACTIONID_VOL_UP, }, |
394 | | { "wallpaper", ACTIONID_WALLPAPER, }, |
395 | | { "zoom", ACTIONID_ZOOM, }, |
396 | | { "zoom-double", ACTIONID_ZOOM_DOUBLE, }, |
397 | | { "zoom-half", ACTIONID_ZOOM_HALF, }, |
398 | | { "zoom-original", ACTIONID_ZOOM_ORIGINAL, }, |
399 | | { "zoom-quarter", ACTIONID_ZOOM_QUARTER, }, |
400 | | }; |
401 | | |
402 | | struct mapping |
403 | | { |
404 | | uint32_t key; ///< Key code |
405 | | vlc_action_id_t action; ///< Action ID |
406 | | }; |
407 | | |
408 | | static int keycmp (const void *a, const void *b) |
409 | 39.7k | { |
410 | 39.7k | const struct mapping *ka = a, *kb = b; |
411 | | |
412 | 39.7k | return (ka->key < kb->key) ? -1 : (ka->key > kb->key) ? +1 : 0; |
413 | 39.7k | } |
414 | | |
415 | | struct vlc_actions_t |
416 | | { |
417 | | void *map; /* Key map */ |
418 | | void *global_map; /* Grabbed/global key map */ |
419 | | const char *ppsz_keys[]; |
420 | | }; |
421 | | |
422 | | static int vlc_key_to_action (vlc_object_t *obj, const char *varname, |
423 | | vlc_value_t prevkey, vlc_value_t curkey, void *d) |
424 | 0 | { |
425 | 0 | void *const *map = d; |
426 | 0 | const void **pent; |
427 | 0 | uint32_t keycode = curkey.i_int; |
428 | |
|
429 | 0 | pent = tfind (&keycode, map, keycmp); |
430 | 0 | if (pent == NULL) |
431 | 0 | return VLC_SUCCESS; |
432 | | |
433 | 0 | const struct mapping *ent = *pent; |
434 | |
|
435 | 0 | (void) varname; |
436 | 0 | (void) prevkey; |
437 | 0 | return var_SetInteger (obj, "key-action", ent->action); |
438 | 0 | } |
439 | | |
440 | | /** |
441 | | * Adds a mapping from a certain key code to a certain action. |
442 | | */ |
443 | | static int add_mapping (void **map, uint32_t keycode, vlc_action_id_t action) |
444 | 6.42k | { |
445 | 6.42k | struct mapping *entry = malloc (sizeof (*entry)); |
446 | 6.42k | if (entry == NULL) |
447 | 0 | return ENOMEM; |
448 | 6.42k | entry->key = keycode; |
449 | 6.42k | entry->action = action; |
450 | | |
451 | 6.42k | void **pent = tsearch (entry, map, keycmp); |
452 | 6.42k | if (unlikely(pent == NULL)) |
453 | 0 | return ENOMEM; |
454 | 6.42k | if (*pent != entry) |
455 | 0 | { |
456 | 0 | free (entry); |
457 | 0 | return EEXIST; |
458 | 0 | } |
459 | 6.42k | return 0; |
460 | 6.42k | } |
461 | | |
462 | | static void add_wheel_mapping (void **map, uint32_t kmore, uint32_t kless, |
463 | | int mode) |
464 | 108 | { |
465 | 108 | vlc_action_id_t amore = ACTIONID_NONE, aless = ACTIONID_NONE; |
466 | | |
467 | 108 | switch (mode) |
468 | 108 | { |
469 | 54 | case 0: /* volume up/down */ |
470 | 54 | amore = ACTIONID_COMBO_VOL_FOV_UP; |
471 | 54 | aless = ACTIONID_COMBO_VOL_FOV_DOWN; |
472 | 54 | break; |
473 | 54 | case 2: /* position latter/earlier */ |
474 | 54 | amore = ACTIONID_JUMP_FORWARD_EXTRASHORT; |
475 | 54 | aless = ACTIONID_JUMP_BACKWARD_EXTRASHORT; |
476 | 54 | break; |
477 | 0 | case 3: /* position earlier/latter */ |
478 | 0 | amore = ACTIONID_JUMP_BACKWARD_EXTRASHORT; |
479 | 0 | aless = ACTIONID_JUMP_FORWARD_EXTRASHORT; |
480 | 0 | break; |
481 | 108 | } |
482 | | |
483 | 108 | if (amore != ACTIONID_NONE) |
484 | 108 | add_mapping (map, kmore, amore); |
485 | 108 | if (aless != ACTIONID_NONE) |
486 | 108 | add_mapping (map, kless, aless); |
487 | 108 | } |
488 | | |
489 | | |
490 | | /** |
491 | | * Sets up all key mappings for a given action. |
492 | | * |
493 | | * \param obj the VLC object to inherit keys from and log to |
494 | | * \param map tree (of struct mapping entries) to write mappings to |
495 | | * \param confname VLC configuration item to read mappings from |
496 | | * \param action action ID |
497 | | */ |
498 | | static void init_action (vlc_object_t *obj, void **map, |
499 | | const char *confname, vlc_action_id_t action) |
500 | 12.2k | { |
501 | 12.2k | char *keys = var_InheritString (obj, confname); |
502 | 12.2k | if (keys == NULL) |
503 | 6.42k | return; |
504 | | |
505 | 5.77k | for (char *buf, *key = strtok_r (keys, "\t", &buf); |
506 | 11.9k | key != NULL; |
507 | 6.21k | key = strtok_r (NULL, "\t", &buf)) |
508 | 6.21k | { |
509 | 6.21k | uint32_t code = vlc_str2keycode (key); |
510 | 6.21k | if (code == KEY_UNSET) |
511 | 0 | { |
512 | 0 | msg_Warn (obj, "Key \"%s\" unrecognized", key); |
513 | 0 | continue; |
514 | 0 | } |
515 | | |
516 | 6.21k | if (add_mapping (map, code, action) == EEXIST) |
517 | 6.21k | msg_Warn (obj, "Key \"%s\" bound to multiple actions", key); |
518 | 6.21k | } |
519 | 5.77k | free (keys); |
520 | 5.77k | } |
521 | | |
522 | | /** |
523 | | * Initializes the key map from configuration. |
524 | | */ |
525 | | int libvlc_InternalActionsInit (libvlc_int_t *libvlc) |
526 | 54 | { |
527 | 54 | assert(libvlc != NULL); |
528 | | |
529 | 54 | vlc_object_t *obj = VLC_OBJECT(libvlc); |
530 | 54 | vlc_actions_t *as = malloc (sizeof (*as) + (1 + ARRAY_SIZE(s_names2actions)) |
531 | 54 | * sizeof (*as->ppsz_keys)); |
532 | | |
533 | 54 | if (unlikely(as == NULL)) |
534 | 0 | return VLC_ENOMEM; |
535 | 54 | as->map = NULL; |
536 | 54 | as->global_map = NULL; |
537 | | |
538 | 54 | var_Create (obj, "key-pressed", VLC_VAR_INTEGER); |
539 | 54 | var_Create (obj, "global-key-pressed", VLC_VAR_INTEGER); |
540 | 54 | var_Create (obj, "key-action", VLC_VAR_INTEGER); |
541 | | |
542 | | /* Initialize from configuration */ |
543 | 6.15k | for (size_t i = 0; i < ARRAY_SIZE(s_names2actions); i++) |
544 | 6.10k | { |
545 | 6.10k | #ifndef NDEBUG |
546 | 6.10k | if (i > 0 |
547 | 6.04k | && strcmp (s_names2actions[i-1].psz, s_names2actions[i].psz) >= 0) |
548 | 0 | { |
549 | 0 | msg_Err (libvlc, "key-%s and key-%s are not ordered properly", |
550 | 0 | s_names2actions[i-1].psz, s_names2actions[i].psz); |
551 | 0 | abort (); |
552 | 0 | } |
553 | 6.10k | #endif |
554 | 6.10k | as->ppsz_keys[i] = s_names2actions[i].psz; |
555 | | |
556 | 6.10k | #define STRINGIFY_(x) #x |
557 | 6.10k | #define STRINGIFY(x) STRINGIFY_(x) |
558 | 6.10k | char name[12 + MAXACTION]; |
559 | | |
560 | 6.10k | snprintf (name, sizeof (name), "global-key-%." STRINGIFY(MAXACTION) "s", s_names2actions[i].psz); |
561 | 6.10k | init_action (obj, &as->map, name + 7, s_names2actions[i].id); |
562 | 6.10k | init_action (obj, &as->global_map, name, s_names2actions[i].id); |
563 | 6.10k | } |
564 | 54 | as->ppsz_keys[ARRAY_SIZE(s_names2actions)] = NULL; |
565 | | |
566 | | /* Initialize mouse wheel events */ |
567 | 54 | add_wheel_mapping (&as->map, KEY_MOUSEWHEELRIGHT, KEY_MOUSEWHEELLEFT, |
568 | 54 | var_InheritInteger (obj, "hotkeys-x-wheel-mode")); |
569 | 54 | add_wheel_mapping (&as->map, KEY_MOUSEWHEELUP, KEY_MOUSEWHEELDOWN, |
570 | 54 | var_InheritInteger (obj, "hotkeys-y-wheel-mode")); |
571 | | |
572 | 54 | libvlc_priv(libvlc)->actions = as; |
573 | 54 | var_AddCallback (obj, "key-pressed", vlc_key_to_action, &as->map); |
574 | 54 | var_AddCallback (obj, "global-key-pressed", vlc_key_to_action, |
575 | 54 | &as->global_map); |
576 | 54 | return VLC_SUCCESS; |
577 | 54 | } |
578 | | |
579 | | /** |
580 | | * Destroys the key map. |
581 | | */ |
582 | | void libvlc_InternalActionsClean (libvlc_int_t *libvlc) |
583 | 0 | { |
584 | 0 | assert(libvlc != NULL); |
585 | |
|
586 | 0 | vlc_actions_t *as = libvlc_priv(libvlc)->actions; |
587 | 0 | if (unlikely(as == NULL)) |
588 | 0 | return; |
589 | | |
590 | 0 | var_DelCallback (libvlc, "global-key-pressed", vlc_key_to_action, |
591 | 0 | &as->global_map); |
592 | 0 | var_DelCallback (libvlc, "key-pressed", vlc_key_to_action, &as->map); |
593 | |
|
594 | 0 | tdestroy (as->global_map, free); |
595 | 0 | tdestroy (as->map, free); |
596 | 0 | free (as); |
597 | 0 | libvlc_priv(libvlc)->actions = NULL; |
598 | 0 | } |
599 | | |
600 | | |
601 | | static int actcmp(const void *key, const void *ent) |
602 | 0 | { |
603 | 0 | const struct name2action *act = ent; |
604 | 0 | return strcmp(key, act->psz); |
605 | 0 | } |
606 | | |
607 | | /** |
608 | | * Get the action ID from the action name in the configuration subsystem. |
609 | | * @return the action ID or ACTIONID_NONE on error. |
610 | | */ |
611 | | vlc_action_id_t |
612 | | vlc_actions_get_id (const char *name) |
613 | 0 | { |
614 | 0 | const struct name2action *act; |
615 | |
|
616 | 0 | if (strncmp (name, "key-", 4)) |
617 | 0 | return ACTIONID_NONE; |
618 | 0 | name += 4; |
619 | |
|
620 | 0 | act = bsearch(name, s_names2actions, ARRAY_SIZE(s_names2actions), |
621 | 0 | sizeof(*act), actcmp); |
622 | 0 | return (act != NULL) ? act->id : ACTIONID_NONE; |
623 | 0 | } |
624 | | |
625 | | #undef vlc_actions_get_keycodes |
626 | | size_t |
627 | | vlc_actions_get_keycodes(vlc_object_t *p_obj, const char *psz_key_name, |
628 | | bool b_global, uint_fast32_t **pp_keycodes) |
629 | 0 | { |
630 | 0 | assert(strlen( psz_key_name ) <= MAXACTION); |
631 | 0 | char varname[12 /* "global-key-" */ + MAXACTION]; |
632 | 0 | snprintf( varname, ARRAY_SIZE(varname), "%skey-%s", b_global ? "global-" : "", psz_key_name ); |
633 | |
|
634 | 0 | *pp_keycodes = NULL; |
635 | |
|
636 | 0 | char *psz_keys = var_InheritString( p_obj, varname ); |
637 | 0 | if( psz_keys == NULL ) |
638 | 0 | return 0; |
639 | | |
640 | 0 | size_t i_nb_keycodes = 0; |
641 | 0 | for( const char* psz_it = psz_keys; *psz_it; ++psz_it ) |
642 | 0 | { |
643 | 0 | if( *psz_it == '\t' ) |
644 | 0 | ++i_nb_keycodes; |
645 | 0 | } |
646 | 0 | ++i_nb_keycodes; |
647 | 0 | *pp_keycodes = vlc_alloc( i_nb_keycodes, sizeof( **pp_keycodes ) ); |
648 | 0 | if( unlikely( !*pp_keycodes ) ) |
649 | 0 | { |
650 | 0 | free( psz_keys ); |
651 | 0 | return 0; |
652 | 0 | } |
653 | 0 | size_t i = 0; |
654 | 0 | for( char *buf, *key = strtok_r( psz_keys, "\t", &buf ); |
655 | 0 | key != NULL; |
656 | 0 | key = strtok_r( NULL, "\t", &buf ), ++i ) |
657 | 0 | { |
658 | 0 | (*pp_keycodes)[i] = vlc_str2keycode( key ); |
659 | 0 | } |
660 | 0 | assert( i == i_nb_keycodes ); |
661 | 0 | free( psz_keys ); |
662 | 0 | return i_nb_keycodes; |
663 | 0 | } |
664 | | |
665 | | #undef vlc_actions_get_key_names |
666 | | const char* const* |
667 | | vlc_actions_get_key_names(vlc_object_t *p_obj) |
668 | 0 | { |
669 | 0 | vlc_actions_t *as = libvlc_priv(vlc_object_instance(p_obj))->actions; |
670 | 0 | return as->ppsz_keys; |
671 | 0 | } |