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
42 #include <vlc_common.h>
44 #include "configuration.h"
47 typedef struct key_descriptor_s
49 const char psz_key_string
[20];
53 static const struct key_descriptor_s vlc_keys
[] =
54 { /* Alphabetical order */
55 { "Backspace", KEY_BACKSPACE
},
56 { "Browser Back", KEY_BROWSER_BACK
},
57 { "Browser Favorites", KEY_BROWSER_FAVORITES
},
58 { "Browser Forward", KEY_BROWSER_FORWARD
},
59 { "Browser Home", KEY_BROWSER_HOME
},
60 { "Browser Refresh", KEY_BROWSER_REFRESH
},
61 { "Browser Search", KEY_BROWSER_SEARCH
},
62 { "Browser Stop", KEY_BROWSER_STOP
},
63 { "Delete", KEY_DELETE
},
66 { "Enter", KEY_ENTER
},
81 { "Insert", KEY_INSERT
},
83 { "Media Next Track", KEY_MEDIA_NEXT_TRACK
},
84 { "Media Play Pause", KEY_MEDIA_PLAY_PAUSE
},
85 { "Media Prev Track", KEY_MEDIA_PREV_TRACK
},
86 { "Media Stop", KEY_MEDIA_STOP
},
88 { "Mouse Wheel Down", KEY_MOUSEWHEELDOWN
},
89 { "Mouse Wheel Left", KEY_MOUSEWHEELLEFT
},
90 { "Mouse Wheel Right", KEY_MOUSEWHEELRIGHT
},
91 { "Mouse Wheel Up", KEY_MOUSEWHEELUP
},
92 { "Page Down", KEY_PAGEDOWN
},
93 { "Page Up", KEY_PAGEUP
},
94 { "Right", KEY_RIGHT
},
97 { "Unset", KEY_UNSET
},
99 { "Volume Mute", KEY_VOLUME_MUTE
},
100 { "Volume Down", KEY_VOLUME_DOWN
},
101 { "Volume Up", KEY_VOLUME_UP
},
103 #define KEYS_COUNT (sizeof(vlc_keys)/sizeof(vlc_keys[0]))
105 static int keystrcmp (const void *key
, const void *elem
)
107 const char *sa
= key
, *sb
= elem
;
108 return strcmp (sa
, sb
);
111 /* Convert Unicode code point to UTF-8 */
112 static char *utf8_cp (uint_fast32_t cp
, char *buf
)
119 else if (cp
< (1 << 11))
122 buf
[1] = 0x80 | (cp
& 0x3F);
126 else if (cp
< (1 << 16))
129 buf
[2] = 0x80 | (cp
& 0x3F);
131 buf
[1] = 0x80 | (cp
& 0x3F);
135 else if (cp
< (1 << 21))
138 buf
[3] = 0x80 | (cp
& 0x3F);
140 buf
[2] = 0x80 | (cp
& 0x3F);
142 buf
[1] = 0x80 | (cp
& 0x3F);
152 * Parse a human-readable string representation of a VLC key code.
153 * @return a VLC key code, or KEY_UNSET on failure.
155 uint_fast32_t vlc_str2keycode (const char *name
)
157 uint_fast32_t mods
= 0;
162 size_t len
= strcspn (name
, "-+");
163 if (len
== 0 || name
[len
] == '\0')
166 if (len
== 4 && !strncasecmp (name
, "Ctrl", 4))
167 mods
|= KEY_MODIFIER_CTRL
;
168 if (len
== 3 && !strncasecmp (name
, "Alt", 3))
169 mods
|= KEY_MODIFIER_ALT
;
170 if (len
== 5 && !strncasecmp (name
, "Shift", 5))
171 mods
|= KEY_MODIFIER_SHIFT
;
172 if (len
== 4 && !strncasecmp (name
, "Meta", 4))
173 mods
|= KEY_MODIFIER_META
;
174 if (len
== 7 && !strncasecmp (name
, "Command", 7))
175 mods
|= KEY_MODIFIER_COMMAND
;
180 key_descriptor_t
*d
= bsearch (name
, vlc_keys
, KEYS_COUNT
,
181 sizeof (vlc_keys
[0]), keystrcmp
);
183 code
= d
->i_key_code
;
185 if (vlc_towc (name
, &code
) <= 0)
188 if (code
!= KEY_UNSET
)
194 * Format a human-readable and unique representation of a VLC key code
195 * (including modifiers).
196 * @return a heap-allocated string, or NULL on error.
198 char *vlc_keycode2str (uint_fast32_t code
)
202 uintptr_t key
= code
& ~KEY_MODIFIER
;
204 for (size_t i
= 0; i
< KEYS_COUNT
; i
++)
205 if (vlc_keys
[i
].i_key_code
== key
)
207 name
= vlc_keys
[i
].psz_key_string
;
211 if (utf8_cp (key
, buf
) == NULL
)
216 if (asprintf (&str
, "%s%s%s%s%s%s",
217 (code
& KEY_MODIFIER_CTRL
) ? "Ctrl+" : "",
218 (code
& KEY_MODIFIER_ALT
) ? "Alt+" : "",
219 (code
& KEY_MODIFIER_SHIFT
) ? "Shift+" : "",
220 (code
& KEY_MODIFIER_META
) ? "Meta+" : "",
221 (code
& KEY_MODIFIER_COMMAND
) ? "Command+" : "", name
) == -1)
227 /*** VLC key map ***/
232 char name
[MAXACTION
];
236 static const struct action actions
[] =
238 /* *MUST* be sorted (ASCII order) */
239 { "aspect-ratio", ACTIONID_ASPECT_RATIO
, },
240 { "audio-track", ACTIONID_AUDIO_TRACK
, },
241 { "audiodelay-down", ACTIONID_AUDIODELAY_DOWN
, },
242 { "audiodelay-up", ACTIONID_AUDIODELAY_UP
, },
243 { "audiodevice-cycle", ACTIONID_AUDIODEVICE_CYCLE
, },
244 { "chapter-next", ACTIONID_CHAPTER_NEXT
, },
245 { "chapter-prev", ACTIONID_CHAPTER_PREV
, },
246 { "crop", ACTIONID_CROP
, },
247 { "crop-bottom", ACTIONID_CROP_BOTTOM
, },
248 { "crop-left", ACTIONID_CROP_LEFT
, },
249 { "crop-right", ACTIONID_CROP_RIGHT
, },
250 { "crop-top", ACTIONID_CROP_TOP
, },
251 { "decr-scalefactor", ACTIONID_SCALE_DOWN
, },
252 { "deinterlace", ACTIONID_DEINTERLACE
, },
253 { "disc-menu", ACTIONID_DISC_MENU
, },
254 { "faster", ACTIONID_FASTER
, },
255 { "frame-next", ACTIONID_FRAME_NEXT
, },
256 { "incr-scalefactor", ACTIONID_SCALE_UP
, },
257 { "intf-boss", ACTIONID_INTF_BOSS
, },
258 { "intf-show", ACTIONID_INTF_TOGGLE_FSC
, },
259 { "jump+extrashort", ACTIONID_JUMP_FORWARD_EXTRASHORT
, },
260 { "jump+long", ACTIONID_JUMP_FORWARD_LONG
, },
261 { "jump+medium", ACTIONID_JUMP_FORWARD_MEDIUM
, },
262 { "jump+short", ACTIONID_JUMP_FORWARD_SHORT
, },
263 { "jump-extrashort", ACTIONID_JUMP_BACKWARD_EXTRASHORT
, },
264 { "jump-long", ACTIONID_JUMP_BACKWARD_LONG
, },
265 { "jump-medium", ACTIONID_JUMP_BACKWARD_MEDIUM
, },
266 { "jump-short", ACTIONID_JUMP_BACKWARD_SHORT
, },
267 { "leave-fullscreen", ACTIONID_LEAVE_FULLSCREEN
, },
268 { "loop", ACTIONID_LOOP
, },
269 { "menu-down", ACTIONID_MENU_DOWN
, },
270 { "menu-left", ACTIONID_MENU_LEFT
, },
271 { "menu-off", ACTIONID_MENU_OFF
, },
272 { "menu-on", ACTIONID_MENU_ON
, },
273 { "menu-right", ACTIONID_MENU_RIGHT
, },
274 { "menu-select", ACTIONID_MENU_SELECT
, },
275 { "menu-up", ACTIONID_MENU_UP
, },
276 { "nav-activate", ACTIONID_NAV_ACTIVATE
, },
277 { "nav-down", ACTIONID_NAV_DOWN
, },
278 { "nav-left", ACTIONID_NAV_LEFT
, },
279 { "nav-right", ACTIONID_NAV_RIGHT
, },
280 { "nav-up", ACTIONID_NAV_UP
, },
281 { "next", ACTIONID_NEXT
, },
282 { "pause", ACTIONID_PAUSE
, },
283 { "play", ACTIONID_PLAY
, },
284 { "play-bookmark1", ACTIONID_PLAY_BOOKMARK1
, },
285 { "play-bookmark10", ACTIONID_PLAY_BOOKMARK10
, },
286 { "play-bookmark2", ACTIONID_PLAY_BOOKMARK2
, },
287 { "play-bookmark3", ACTIONID_PLAY_BOOKMARK3
, },
288 { "play-bookmark4", ACTIONID_PLAY_BOOKMARK4
, },
289 { "play-bookmark5", ACTIONID_PLAY_BOOKMARK5
, },
290 { "play-bookmark6", ACTIONID_PLAY_BOOKMARK6
, },
291 { "play-bookmark7", ACTIONID_PLAY_BOOKMARK7
, },
292 { "play-bookmark8", ACTIONID_PLAY_BOOKMARK8
, },
293 { "play-bookmark9", ACTIONID_PLAY_BOOKMARK9
, },
294 { "play-pause", ACTIONID_PLAY_PAUSE
, },
295 { "position", ACTIONID_POSITION
, },
296 { "prev", ACTIONID_PREV
, },
297 { "quit", ACTIONID_QUIT
, },
298 { "random", ACTIONID_RANDOM
, },
299 { "rate-faster-fine", ACTIONID_RATE_FASTER_FINE
, },
300 { "rate-normal", ACTIONID_RATE_NORMAL
, },
301 { "rate-slower-fine", ACTIONID_RATE_SLOWER_FINE
, },
302 { "record", ACTIONID_RECORD
, },
303 { "set-bookmark1", ACTIONID_SET_BOOKMARK1
, },
304 { "set-bookmark10", ACTIONID_SET_BOOKMARK10
, },
305 { "set-bookmark2", ACTIONID_SET_BOOKMARK2
, },
306 { "set-bookmark3", ACTIONID_SET_BOOKMARK3
, },
307 { "set-bookmark4", ACTIONID_SET_BOOKMARK4
, },
308 { "set-bookmark5", ACTIONID_SET_BOOKMARK5
, },
309 { "set-bookmark6", ACTIONID_SET_BOOKMARK6
, },
310 { "set-bookmark7", ACTIONID_SET_BOOKMARK7
, },
311 { "set-bookmark8", ACTIONID_SET_BOOKMARK8
, },
312 { "set-bookmark9", ACTIONID_SET_BOOKMARK9
, },
313 { "slower", ACTIONID_SLOWER
, },
314 { "snapshot", ACTIONID_SNAPSHOT
, },
315 { "stop", ACTIONID_STOP
, },
316 { "subdelay-down", ACTIONID_SUBDELAY_DOWN
, },
317 { "subdelay-up", ACTIONID_SUBDELAY_UP
, },
318 { "subpos-down", ACTIONID_SUBPOS_DOWN
, },
319 { "subpos-up", ACTIONID_SUBPOS_UP
, },
320 { "subtitle-track", ACTIONID_SUBTITLE_TRACK
, },
321 { "title-next", ACTIONID_TITLE_NEXT
, },
322 { "title-prev", ACTIONID_TITLE_PREV
, },
323 { "toggle-autoscale", ACTIONID_TOGGLE_AUTOSCALE
, },
324 { "toggle-fullscreen", ACTIONID_TOGGLE_FULLSCREEN
, },
325 { "uncrop-bottom", ACTIONID_UNCROP_BOTTOM
, },
326 { "uncrop-left", ACTIONID_UNCROP_LEFT
, },
327 { "uncrop-right", ACTIONID_UNCROP_RIGHT
, },
328 { "uncrop-top", ACTIONID_UNCROP_TOP
, },
329 { "unzoom", ACTIONID_UNZOOM
, },
330 { "vol-down", ACTIONID_VOL_DOWN
, },
331 { "vol-mute", ACTIONID_VOL_MUTE
, },
332 { "vol-up", ACTIONID_VOL_UP
, },
333 { "wallpaper", ACTIONID_WALLPAPER
, },
334 { "zoom", ACTIONID_ZOOM
, },
335 { "zoom-double", ACTIONID_ZOOM_DOUBLE
, },
336 { "zoom-half", ACTIONID_ZOOM_HALF
, },
337 { "zoom-original", ACTIONID_ZOOM_ORIGINAL
, },
338 { "zoom-quarter", ACTIONID_ZOOM_QUARTER
, },
340 #define ACTIONS_COUNT (sizeof (actions) / sizeof (actions[0]))
344 uint32_t key
; ///< Key code
345 vlc_action_t action
; ///< Action ID
348 static int keycmp (const void *a
, const void *b
)
350 const struct mapping
*ka
= a
, *kb
= b
;
352 #if (INT_MAX >= 0x7fffffff)
353 return ka
->key
- kb
->key
;
355 return (ka
->key
< kb
->key
) ? -1 : (ka
->key
> kb
->key
) ? +1 : 0;
361 void *map
; /* Key map */
362 void *global_map
; /* Grabbed/global key map */
363 struct hotkey keys
[0];
366 static int vlc_key_to_action (vlc_object_t
*obj
, const char *varname
,
367 vlc_value_t prevkey
, vlc_value_t curkey
, void *d
)
369 void *const *map
= d
;
370 const struct mapping
**pent
;
371 uint32_t keycode
= curkey
.i_int
;
373 pent
= tfind (&keycode
, map
, keycmp
);
379 return var_SetInteger (obj
, "key-action", (*pent
)->action
);
383 * Sets up all key mappings for a given action.
384 * \param map tree (of struct mapping entries) to write mappings to
385 * \param confname VLC configuration item to read mappings from
386 * \param action action ID
388 static void vlc_MapAction (vlc_object_t
*obj
, void **map
,
389 const char *confname
, vlc_action_t action
)
391 char *keys
= var_InheritString (obj
, confname
);
395 for (char *buf
, *key
= strtok_r (keys
, "\t", &buf
);
397 key
= strtok_r (NULL
, "\t", &buf
))
399 uint32_t code
= vlc_str2keycode (key
);
400 if (code
== KEY_UNSET
)
402 msg_Warn (obj
, "Key \"%s\" unrecognized", key
);
406 struct mapping
*entry
= malloc (sizeof (*entry
));
410 entry
->action
= action
;
412 struct mapping
**pent
= tsearch (entry
, map
, keycmp
);
413 if (unlikely(pent
== NULL
))
418 msg_Warn (obj
, "Key \"%s\" bound to multiple actions", key
);
426 * Initializes the key map from configuration.
428 struct vlc_actions
*vlc_InitActions (libvlc_int_t
*libvlc
)
430 vlc_object_t
*obj
= VLC_OBJECT(libvlc
);
432 struct vlc_actions
*as
= malloc (sizeof (*as
) + (ACTIONS_COUNT
+ 1) * sizeof (*keys
));
434 if (unlikely(as
== NULL
))
437 as
->global_map
= NULL
;
440 var_Create (obj
, "key-pressed", VLC_VAR_INTEGER
);
441 var_Create (obj
, "global-key-pressed", VLC_VAR_INTEGER
);
442 var_Create (obj
, "key-action", VLC_VAR_INTEGER
);
444 /* Initialize from configuration */
445 for (size_t i
= 0; i
< ACTIONS_COUNT
; i
++)
449 && strcmp (actions
[i
-1].name
, actions
[i
].name
) >= 0)
451 msg_Err (libvlc
, "key-%s and key-%s are not ordered properly",
452 actions
[i
-1].name
, actions
[i
].name
);
456 keys
->psz_action
= actions
[i
].name
;
459 char name
[12 + MAXACTION
];
461 snprintf (name
, sizeof (name
), "global-key-%s", actions
[i
].name
);
462 vlc_MapAction (obj
, &as
->map
, name
+ 7, actions
[i
].value
);
463 vlc_MapAction (obj
, &as
->global_map
, name
, actions
[i
].value
);
466 keys
->psz_action
= NULL
;
468 libvlc
->p_hotkeys
= as
->keys
;
469 var_AddCallback (obj
, "key-pressed", vlc_key_to_action
, &as
->map
);
470 var_AddCallback (obj
, "global-key-pressed", vlc_key_to_action
,
476 * Destroys the key map.
478 void vlc_DeinitActions (libvlc_int_t
*libvlc
, struct vlc_actions
*as
)
480 if (unlikely(as
== NULL
))
483 var_DelCallback (libvlc
, "global-key-pressed", vlc_key_to_action
,
485 var_DelCallback (libvlc
, "key-pressed", vlc_key_to_action
, &as
->map
);
487 tdestroy (as
->global_map
, free
);
488 tdestroy (as
->map
, free
);
490 libvlc
->p_hotkeys
= NULL
;
494 static int actcmp(const void *key
, const void *ent
)
496 const struct action
*act
= ent
;
497 return strcmp(key
, act
->name
);
501 * Get the action ID from the action name in the configuration subsystem.
502 * @return the action ID or ACTIONID_NONE on error.
504 vlc_action_t
vlc_GetActionId (const char *name
)
506 const struct action
*act
;
508 if (strncmp (name
, "key-", 4))
509 return ACTIONID_NONE
;
512 act
= bsearch(name
, actions
, ACTIONS_COUNT
, sizeof(*act
), actcmp
);
513 return (act
!= NULL
) ? act
->value
: ACTIONID_NONE
;