]> git.sesse.net Git - vlc/blob - src/config/keys.c
Some documentation
[vlc] / src / config / keys.c
1 /*****************************************************************************
2  * keys.c: keys configuration
3  *****************************************************************************
4  * Copyright (C) 2003-2009 the VideoLAN team
5  *
6  * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
7  *
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.
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 General Public License for more details.
17  *
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  *****************************************************************************/
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 #ifdef HAVE_CONFIG_H
33 # include <config.h>
34 #endif
35
36 #include <stdlib.h>
37 #include <limits.h>
38
39 #include <vlc_common.h>
40 #include <vlc_keys.h>
41 #include "configuration.h"
42 #include "libvlc.h"
43
44 typedef struct key_descriptor_s
45 {
46     const char psz_key_string[20];
47     uint32_t i_key_code;
48 } key_descriptor_t;
49
50 static const struct key_descriptor_s vlc_keys[] =
51 {
52     { "Unset", KEY_UNSET },
53     { "Backspace", KEY_BACKSPACE },
54     { "Tab", KEY_TAB },
55     { "Enter", KEY_ENTER },
56     { "Esc", KEY_ESC },
57     { "Space", ' ' },
58     { "Left", KEY_LEFT },
59     { "Right", KEY_RIGHT },
60     { "Up", KEY_UP },
61     { "Down", KEY_DOWN },
62     { "F1", KEY_F1 },
63     { "F2", KEY_F2 },
64     { "F3", KEY_F3 },
65     { "F4", KEY_F4 },
66     { "F5", KEY_F5 },
67     { "F6", KEY_F6 },
68     { "F7", KEY_F7 },
69     { "F8", KEY_F8 },
70     { "F9", KEY_F9 },
71     { "F10", KEY_F10 },
72     { "F11", KEY_F11 },
73     { "F12", KEY_F12 },
74     { "Home", KEY_HOME },
75     { "End", KEY_END },
76     { "Insert", KEY_INSERT },
77     { "Delete", KEY_DELETE },
78     { "Menu", KEY_MENU },
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 },
99 };
100 enum { vlc_num_keys=sizeof(vlc_keys)/sizeof(struct key_descriptor_s) };
101
102 static int cmpkey (const void *key, const void *elem)
103 {
104     return ((uintptr_t)key) - ((key_descriptor_t *)elem)->i_key_code;
105 }
106
107 /* Convert Unicode code point to UTF-8 */
108 static char *utf8_cp (uint_fast32_t cp, char *buf)
109 {
110     if (cp < (1 << 7))
111     {
112         buf[1] = 0;
113         buf[0] = cp;
114     }
115     else if (cp < (1 << 11))
116     {
117         buf[2] = 0;
118         buf[1] = 0x80 | (cp & 0x3F);
119         cp >>= 6;
120         buf[0] = 0xC0 | cp;
121     }
122     else if (cp < (1 << 16))
123     {
124         buf[3] = 0;
125         buf[2] = 0x80 | (cp & 0x3F);
126         cp >>= 6;
127         buf[1] = 0x80 | (cp & 0x3F);
128         cp >>= 6;
129         buf[0] = 0xE0 | cp;
130     }
131     else if (cp < (1 << 21))
132     {
133         buf[4] = 0;
134         buf[3] = 0x80 | (cp & 0x3F);
135         cp >>= 6;
136         buf[2] = 0x80 | (cp & 0x3F);
137         cp >>= 6;
138         buf[1] = 0x80 | (cp & 0x3F);
139         cp >>= 6;
140         buf[0] = 0xE0 | cp;
141     }
142     else
143         return NULL;
144     return buf;
145 }
146
147 uint_fast32_t ConfigStringToKey (const char *name)
148 {
149     uint_fast32_t mods = 0;
150     uint32_t cp;
151
152     for (;;)
153     {
154         size_t len = strcspn (name, "-+");
155         if (len == 0 || name[len] == '\0')
156             break;
157
158         if (len == 4 && !strncasecmp (name, "Ctrl", 4))
159             mods |= KEY_MODIFIER_CTRL;
160         if (len == 3 && !strncasecmp (name, "Alt", 3))
161             mods |= KEY_MODIFIER_ALT;
162         if (len == 5 && !strncasecmp (name, "Shift", 5))
163             mods |= KEY_MODIFIER_SHIFT;
164         if (len == 4 && !strncasecmp (name, "Meta", 4))
165             mods |= KEY_MODIFIER_META;
166         if (len == 7 && !strncasecmp (name, "Command", 7))
167             mods |= KEY_MODIFIER_COMMAND;
168
169         name += len + 1;
170     }
171
172     for (size_t i = 0; i < vlc_num_keys; i++)
173         if (!strcasecmp( vlc_keys[i].psz_key_string, name))
174             return vlc_keys[i].i_key_code | mods;
175
176     return (vlc_towc (name, &cp) > 0) ? (mods | cp) : KEY_UNSET;
177 }
178
179 /**
180  * Format a human-readable and unique representation of a VLC key code
181  * (including modifiers).
182  * @return a heap-allocated string, or NULL on error.
183  */
184 char *vlc_keycode2str (uint_fast32_t code)
185 {
186     char *str, buf[5];
187     uintptr_t key = code & ~KEY_MODIFIER;
188
189     key_descriptor_t *d = bsearch ((void *)key, vlc_keys, vlc_num_keys,
190                                    sizeof (vlc_keys[0]), cmpkey);
191     if (d == NULL && utf8_cp (key, buf) == NULL)
192         return NULL;
193
194     if (asprintf (&str, "%s%s%s%s%s%s",
195                   (code & KEY_MODIFIER_CTRL) ? "Ctrl+" : "",
196                   (code & KEY_MODIFIER_ALT) ? "Alt+" : "",
197                   (code & KEY_MODIFIER_SHIFT) ? "Shift+" : "",
198                   (code & KEY_MODIFIER_META) ? "Meta+" : "",
199                   (code & KEY_MODIFIER_COMMAND) ? "Command+" : "",
200                   (d != NULL) ? d->psz_key_string : buf) == -1)
201         return NULL;
202
203     return str;
204 }
205
206
207 static int keycmp (const void *a, const void *b)
208 {
209     const struct hotkey *ka = a, *kb = b;
210 #if (INT_MAX >= 0x7fffffff)
211     return ka->i_key - kb->i_key;
212 #else
213     return (ka->i_key < kb->i_key) ? -1 : (ka->i_key > kb->i_key) ? +1 : 0;
214 #endif
215 }
216
217 /**
218  * Get the action ID associated with a VLC key code, if any.
219  */
220 static
221 vlc_key_t vlc_TranslateKey (const vlc_object_t *obj, uint_fast32_t keycode)
222 {
223     struct hotkey k = { .psz_action = NULL, .i_key = keycode, .i_action = 0 };
224     const struct hotkey *key;
225
226     key = bsearch (&k, obj->p_libvlc->p_hotkeys, libvlc_actions_count,
227                    sizeof (*key), keycmp);
228     return (key != NULL) ? key->i_action : ACTIONID_NONE;
229 }
230
231 static int vlc_key_to_action (vlc_object_t *libvlc, const char *varname,
232                               vlc_value_t prevkey, vlc_value_t curkey, void *d)
233 {
234     (void)varname;
235     (void)prevkey;
236     (void)d;
237
238     vlc_key_t action = vlc_TranslateKey (libvlc, curkey.i_int);
239     if (!action)
240         return VLC_SUCCESS;
241     return var_SetInteger (libvlc, "key-action", action);
242 }
243
244
245 int vlc_InitActions (libvlc_int_t *libvlc)
246 {
247     struct hotkey *keys;
248
249     var_Create (libvlc, "key-pressed", VLC_VAR_INTEGER);
250     var_Create (libvlc, "key-action", VLC_VAR_INTEGER);
251
252     keys = malloc ((libvlc_actions_count + 1) * sizeof (*keys));
253     if (keys == NULL)
254     {
255         libvlc->p_hotkeys = NULL;
256         return VLC_ENOMEM;
257     }
258
259     /* Initialize from configuration */
260     for (size_t i = 0; i < libvlc_actions_count; i++)
261     {
262         keys[i].psz_action = libvlc_actions[i].name;
263         keys[i].i_key = var_InheritInteger (libvlc, libvlc_actions[i].name );
264         keys[i].i_action = libvlc_actions[i].value;
265 #ifndef NDEBUG
266         if (i > 0
267          && strcmp (libvlc_actions[i-1].name, libvlc_actions[i].name) >= 0)
268         {
269             msg_Err (libvlc, "%s and %s are not ordered properly",
270                      libvlc_actions[i-1].name, libvlc_actions[i].name);
271             abort ();
272         }
273 #endif
274     }
275     qsort (keys, libvlc_actions_count, sizeof (*keys), keycmp);
276
277     keys[libvlc_actions_count].psz_action = NULL;
278     keys[libvlc_actions_count].i_key = 0;
279     keys[libvlc_actions_count].i_action = 0;
280
281     libvlc->p_hotkeys = keys;
282     var_AddCallback (libvlc, "key-pressed", vlc_key_to_action, NULL);
283     return VLC_SUCCESS;
284 }
285
286 void vlc_DeinitActions (libvlc_int_t *libvlc)
287 {
288     if (unlikely(libvlc->p_hotkeys == NULL))
289         return;
290     var_DelCallback (libvlc, "key-pressed", vlc_key_to_action, NULL);
291     free ((void *)libvlc->p_hotkeys);
292 }
293
294
295 static int actcmp(const void *key, const void *ent)
296 {
297     const struct action *act = ent;
298     return strcmp(key, act->name);
299 }
300
301 /**
302  * Get the action ID from the action name in the configuration subsystem.
303  * @return the action ID or ACTIONID_NONE on error.
304  */
305 vlc_key_t vlc_GetActionId(const char *name)
306 {
307     const struct action *act;
308
309     act = bsearch(name, libvlc_actions, libvlc_actions_count, sizeof(*act),
310                   actcmp);
311     return (act != NULL) ? act->value : ACTIONID_NONE;
312 }