1 /*****************************************************************************
2 * keys.c: keys configuration
3 *****************************************************************************
4 * Copyright (C) 2003-2009 the VideoLAN team
6 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
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 General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
29 * This file defines functions and structures for hotkey handling in vlc
39 #include <vlc_common.h>
41 #include "configuration.h"
44 typedef struct key_descriptor_s
46 const char psz_key_string[20];
50 static const struct key_descriptor_s vlc_keys[] =
52 { "Unset", KEY_UNSET },
53 { "Backspace", KEY_BACKSPACE },
55 { "Enter", KEY_ENTER },
59 { "Right", KEY_RIGHT },
76 { "Insert", KEY_INSERT },
77 { "Delete", KEY_DELETE },
79 { "Page Up", KEY_PAGEUP },
80 { "Page Down", KEY_PAGEDOWN },
81 { "Browser Back", KEY_BROWSER_BACK },
82 { "Browser Forward", KEY_BROWSER_FORWARD },
83 { "Browser Refresh", KEY_BROWSER_REFRESH },
84 { "Browser Stop", KEY_BROWSER_STOP },
85 { "Browser Search", KEY_BROWSER_SEARCH },
86 { "Browser Favorites", KEY_BROWSER_FAVORITES },
87 { "Browser Home", KEY_BROWSER_HOME },
88 { "Volume Mute", KEY_VOLUME_MUTE },
89 { "Volume Down", KEY_VOLUME_DOWN },
90 { "Volume Up", KEY_VOLUME_UP },
91 { "Media Next Track", KEY_MEDIA_NEXT_TRACK },
92 { "Media Prev Track", KEY_MEDIA_PREV_TRACK },
93 { "Media Stop", KEY_MEDIA_STOP },
94 { "Media Play Pause", KEY_MEDIA_PLAY_PAUSE },
95 { "Mouse Wheel Up", KEY_MOUSEWHEELUP },
96 { "Mouse Wheel Down", KEY_MOUSEWHEELDOWN },
97 { "Mouse Wheel Left", KEY_MOUSEWHEELLEFT },
98 { "Mouse Wheel Right", KEY_MOUSEWHEELRIGHT },
100 enum { vlc_num_keys=sizeof(vlc_keys)/sizeof(struct key_descriptor_s) };
102 static int cmpkey (const void *key, const void *elem)
104 return ((uintptr_t)key) - ((key_descriptor_t *)elem)->i_key_code;
107 /* Convert Unicode code point to UTF-8 */
108 static char *utf8_cp (uint_fast32_t cp, char *buf)
115 else if (cp < (1 << 11))
118 buf[1] = 0x80 | (cp & 0x3F);
122 else if (cp < (1 << 16))
125 buf[2] = 0x80 | (cp & 0x3F);
127 buf[1] = 0x80 | (cp & 0x3F);
131 else if (cp < (1 << 21))
134 buf[3] = 0x80 | (cp & 0x3F);
136 buf[2] = 0x80 | (cp & 0x3F);
138 buf[1] = 0x80 | (cp & 0x3F);
148 * Parse a human-readable string representation of a VLC key code.
149 * @return a VLC key code, or KEY_UNSET on failure.
152 uint_fast32_t vlc_str2keycode (const char *name)
154 uint_fast32_t mods = 0;
159 size_t len = strcspn (name, "-+");
160 if (len == 0 || name[len] == '\0')
163 if (len == 4 && !strncasecmp (name, "Ctrl", 4))
164 mods |= KEY_MODIFIER_CTRL;
165 if (len == 3 && !strncasecmp (name, "Alt", 3))
166 mods |= KEY_MODIFIER_ALT;
167 if (len == 5 && !strncasecmp (name, "Shift", 5))
168 mods |= KEY_MODIFIER_SHIFT;
169 if (len == 4 && !strncasecmp (name, "Meta", 4))
170 mods |= KEY_MODIFIER_META;
171 if (len == 7 && !strncasecmp (name, "Command", 7))
172 mods |= KEY_MODIFIER_COMMAND;
177 for (size_t i = 0; i < vlc_num_keys; i++)
178 if (!strcasecmp( vlc_keys[i].psz_key_string, name))
179 return vlc_keys[i].i_key_code | mods;
181 return (vlc_towc (name, &cp) > 0) ? (mods | cp) : KEY_UNSET;
185 * Format a human-readable and unique representation of a VLC key code
186 * (including modifiers).
187 * @return a heap-allocated string, or NULL on error.
189 char *vlc_keycode2str (uint_fast32_t code)
192 uintptr_t key = code & ~KEY_MODIFIER;
194 key_descriptor_t *d = bsearch ((void *)key, vlc_keys, vlc_num_keys,
195 sizeof (vlc_keys[0]), cmpkey);
196 if (d == NULL && utf8_cp (key, buf) == NULL)
199 if (asprintf (&str, "%s%s%s%s%s%s",
200 (code & KEY_MODIFIER_CTRL) ? "Ctrl+" : "",
201 (code & KEY_MODIFIER_ALT) ? "Alt+" : "",
202 (code & KEY_MODIFIER_SHIFT) ? "Shift+" : "",
203 (code & KEY_MODIFIER_META) ? "Meta+" : "",
204 (code & KEY_MODIFIER_COMMAND) ? "Command+" : "",
205 (d != NULL) ? d->psz_key_string : buf) == -1)
214 typedef struct action
220 static const struct action actions[] =
222 /* *MUST* be sorted (ASCII order) */
223 { "key-aspect-ratio", ACTIONID_ASPECT_RATIO, },
224 { "key-audio-track", ACTIONID_AUDIO_TRACK, },
225 { "key-audiodelay-down", ACTIONID_AUDIODELAY_DOWN, },
226 { "key-audiodelay-up", ACTIONID_AUDIODELAY_UP, },
227 { "key-audiodevice-cycle", ACTIONID_AUDIODEVICE_CYCLE, },
228 { "key-chapter-next", ACTIONID_CHAPTER_NEXT, },
229 { "key-chapter-prev", ACTIONID_CHAPTER_PREV, },
230 { "key-crop", ACTIONID_CROP, },
231 { "key-crop-bottom", ACTIONID_CROP_BOTTOM, },
232 { "key-crop-left", ACTIONID_CROP_LEFT, },
233 { "key-crop-right", ACTIONID_CROP_RIGHT, },
234 { "key-crop-top", ACTIONID_CROP_TOP, },
235 { "key-decr-scalefactor", ACTIONID_SCALE_DOWN, },
236 { "key-deinterlace", ACTIONID_DEINTERLACE, },
237 { "key-disc-menu", ACTIONID_DISC_MENU, },
238 { "key-faster", ACTIONID_FASTER, },
239 { "key-frame-next", ACTIONID_FRAME_NEXT, },
240 { "key-incr-scalefactor", ACTIONID_SCALE_UP, },
241 { "key-intf-hide", ACTIONID_INTF_HIDE, },
242 { "key-intf-show", ACTIONID_INTF_SHOW, },
243 { "key-jump+extrashort", ACTIONID_JUMP_FORWARD_EXTRASHORT, },
244 { "key-jump+long", ACTIONID_JUMP_FORWARD_LONG, },
245 { "key-jump+medium", ACTIONID_JUMP_FORWARD_MEDIUM, },
246 { "key-jump+short", ACTIONID_JUMP_FORWARD_SHORT, },
247 { "key-jump-extrashort", ACTIONID_JUMP_BACKWARD_EXTRASHORT, },
248 { "key-jump-long", ACTIONID_JUMP_BACKWARD_LONG, },
249 { "key-jump-medium", ACTIONID_JUMP_BACKWARD_MEDIUM, },
250 { "key-jump-short", ACTIONID_JUMP_BACKWARD_SHORT, },
251 { "key-leave-fullscreen", ACTIONID_LEAVE_FULLSCREEN, },
252 { "key-loop", ACTIONID_LOOP, },
253 { "key-menu-down", ACTIONID_MENU_DOWN, },
254 { "key-menu-left", ACTIONID_MENU_LEFT, },
255 { "key-menu-off", ACTIONID_MENU_OFF, },
256 { "key-menu-on", ACTIONID_MENU_ON, },
257 { "key-menu-right", ACTIONID_MENU_RIGHT, },
258 { "key-menu-select", ACTIONID_MENU_SELECT, },
259 { "key-menu-up", ACTIONID_MENU_UP, },
260 { "key-nav-activate", ACTIONID_NAV_ACTIVATE, },
261 { "key-nav-down", ACTIONID_NAV_DOWN, },
262 { "key-nav-left", ACTIONID_NAV_LEFT, },
263 { "key-nav-right", ACTIONID_NAV_RIGHT, },
264 { "key-nav-up", ACTIONID_NAV_UP, },
265 { "key-next", ACTIONID_NEXT, },
266 { "key-pause", ACTIONID_PAUSE, },
267 { "key-play", ACTIONID_PLAY, },
268 { "key-play-bookmark1", ACTIONID_PLAY_BOOKMARK1, },
269 { "key-play-bookmark10", ACTIONID_PLAY_BOOKMARK10, },
270 { "key-play-bookmark2", ACTIONID_PLAY_BOOKMARK2, },
271 { "key-play-bookmark3", ACTIONID_PLAY_BOOKMARK3, },
272 { "key-play-bookmark4", ACTIONID_PLAY_BOOKMARK4, },
273 { "key-play-bookmark5", ACTIONID_PLAY_BOOKMARK5, },
274 { "key-play-bookmark6", ACTIONID_PLAY_BOOKMARK6, },
275 { "key-play-bookmark7", ACTIONID_PLAY_BOOKMARK7, },
276 { "key-play-bookmark8", ACTIONID_PLAY_BOOKMARK8, },
277 { "key-play-bookmark9", ACTIONID_PLAY_BOOKMARK9, },
278 { "key-play-pause", ACTIONID_PLAY_PAUSE, },
279 { "key-position", ACTIONID_POSITION, },
280 { "key-prev", ACTIONID_PREV, },
281 { "key-quit", ACTIONID_QUIT, },
282 { "key-random", ACTIONID_RANDOM, },
283 { "key-rate-faster-fine", ACTIONID_RATE_FASTER_FINE, },
284 { "key-rate-normal", ACTIONID_RATE_NORMAL, },
285 { "key-rate-slower-fine", ACTIONID_RATE_SLOWER_FINE, },
286 { "key-record", ACTIONID_RECORD, },
287 { "key-set-bookmark1", ACTIONID_SET_BOOKMARK1, },
288 { "key-set-bookmark10", ACTIONID_SET_BOOKMARK10, },
289 { "key-set-bookmark2", ACTIONID_SET_BOOKMARK2, },
290 { "key-set-bookmark3", ACTIONID_SET_BOOKMARK3, },
291 { "key-set-bookmark4", ACTIONID_SET_BOOKMARK4, },
292 { "key-set-bookmark5", ACTIONID_SET_BOOKMARK5, },
293 { "key-set-bookmark6", ACTIONID_SET_BOOKMARK6, },
294 { "key-set-bookmark7", ACTIONID_SET_BOOKMARK7, },
295 { "key-set-bookmark8", ACTIONID_SET_BOOKMARK8, },
296 { "key-set-bookmark9", ACTIONID_SET_BOOKMARK9, },
297 { "key-slower", ACTIONID_SLOWER, },
298 { "key-snapshot", ACTIONID_SNAPSHOT, },
299 { "key-stop", ACTIONID_STOP, },
300 { "key-subdelay-down", ACTIONID_SUBDELAY_DOWN, },
301 { "key-subdelay-up", ACTIONID_SUBDELAY_UP, },
302 { "key-subpos-down", ACTIONID_SUBPOS_DOWN, },
303 { "key-subpos-up", ACTIONID_SUBPOS_UP, },
304 { "key-subtitle-track", ACTIONID_SUBTITLE_TRACK, },
305 { "key-title-next", ACTIONID_TITLE_NEXT, },
306 { "key-title-prev", ACTIONID_TITLE_PREV, },
307 { "key-toggle-autoscale", ACTIONID_TOGGLE_AUTOSCALE, },
308 { "key-toggle-fullscreen", ACTIONID_TOGGLE_FULLSCREEN, },
309 { "key-uncrop-bottom", ACTIONID_UNCROP_BOTTOM, },
310 { "key-uncrop-left", ACTIONID_UNCROP_LEFT, },
311 { "key-uncrop-right", ACTIONID_UNCROP_RIGHT, },
312 { "key-uncrop-top", ACTIONID_UNCROP_TOP, },
313 { "key-unzoom", ACTIONID_UNZOOM, },
314 { "key-vol-down", ACTIONID_VOL_DOWN, },
315 { "key-vol-mute", ACTIONID_VOL_MUTE, },
316 { "key-vol-up", ACTIONID_VOL_UP, },
317 { "key-wallpaper", ACTIONID_WALLPAPER, },
318 { "key-zoom", ACTIONID_ZOOM, },
319 { "key-zoom-double", ACTIONID_ZOOM_DOUBLE, },
320 { "key-zoom-half", ACTIONID_ZOOM_HALF, },
321 { "key-zoom-original", ACTIONID_ZOOM_ORIGINAL, },
322 { "key-zoom-quarter", ACTIONID_ZOOM_QUARTER, },
325 #define ACTIONS_COUNT (sizeof (actions) / sizeof (actions[0]))
327 static int keycmp (const void *a, const void *b)
329 const struct hotkey *ka = a, *kb = b;
330 #if (INT_MAX >= 0x7fffffff)
331 return ka->i_key - kb->i_key;
333 return (ka->i_key < kb->i_key) ? -1 : (ka->i_key > kb->i_key) ? +1 : 0;
338 * Get the action ID associated with a VLC key code, if any.
341 vlc_key_t vlc_TranslateKey (const vlc_object_t *obj, uint_fast32_t keycode)
343 struct hotkey k = { .psz_action = NULL, .i_key = keycode, .i_action = 0 };
344 const struct hotkey *key;
346 key = bsearch (&k, obj->p_libvlc->p_hotkeys, ACTIONS_COUNT,
347 sizeof (*key), keycmp);
348 return (key != NULL) ? key->i_action : ACTIONID_NONE;
351 static int vlc_key_to_action (vlc_object_t *libvlc, const char *varname,
352 vlc_value_t prevkey, vlc_value_t curkey, void *d)
358 vlc_key_t action = vlc_TranslateKey (libvlc, curkey.i_int);
361 return var_SetInteger (libvlc, "key-action", action);
365 int vlc_InitActions (libvlc_int_t *libvlc)
369 var_Create (libvlc, "key-pressed", VLC_VAR_INTEGER);
370 var_Create (libvlc, "key-action", VLC_VAR_INTEGER);
372 keys = malloc ((ACTIONS_COUNT + 1) * sizeof (*keys));
375 libvlc->p_hotkeys = NULL;
379 /* Initialize from configuration */
380 for (size_t i = 0; i < ACTIONS_COUNT; i++)
382 char *str = var_InheritString (libvlc, actions[i].name);
383 uint32_t code = str ? vlc_str2keycode (str) : KEY_UNSET;
385 keys[i].psz_action = actions[i].name;
386 keys[i].i_key = code;
387 keys[i].i_action = actions[i].value;
390 && strcmp (actions[i-1].name, actions[i].name) >= 0)
392 msg_Err (libvlc, "%s and %s are not ordered properly",
393 actions[i-1].name, actions[i].name);
398 qsort (keys, ACTIONS_COUNT, sizeof (*keys), keycmp);
400 keys[ACTIONS_COUNT].psz_action = NULL;
401 keys[ACTIONS_COUNT].i_key = 0;
402 keys[ACTIONS_COUNT].i_action = 0;
404 libvlc->p_hotkeys = keys;
405 var_AddCallback (libvlc, "key-pressed", vlc_key_to_action, NULL);
409 void vlc_DeinitActions (libvlc_int_t *libvlc)
411 if (unlikely(libvlc->p_hotkeys == NULL))
413 var_DelCallback (libvlc, "key-pressed", vlc_key_to_action, NULL);
414 free ((void *)libvlc->p_hotkeys);
418 static int actcmp(const void *key, const void *ent)
420 const struct action *act = ent;
421 return strcmp(key, act->name);
425 * Get the action ID from the action name in the configuration subsystem.
426 * @return the action ID or ACTIONID_NONE on error.
428 vlc_key_t vlc_GetActionId(const char *name)
430 const struct action *act;
432 act = bsearch(name, actions, ACTIONS_COUNT, sizeof(*act), actcmp);
433 return (act != NULL) ? act->value : ACTIONID_NONE;