Fix infinite loop introduced in [9142f0c6]
[vlc.git] / 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 #include <vlc_common.h>
33 #include <vlc_keys.h>
34 #include "configuration.h"
35
36 typedef struct key_descriptor_s
37 {
38     const char *psz_key_string;
39     uint32_t i_key_code;
40 } key_descriptor_t;
41
42 static const struct key_descriptor_s vlc_modifiers[] =
43 {
44     { "Alt", KEY_MODIFIER_ALT },
45     { "Shift", KEY_MODIFIER_SHIFT },
46     { "Ctrl", KEY_MODIFIER_CTRL },
47     { "Meta", KEY_MODIFIER_META },
48     { "Command", KEY_MODIFIER_COMMAND }
49 };
50 enum { vlc_num_modifiers=sizeof(vlc_modifiers)
51                         /sizeof(struct key_descriptor_s) };
52
53 static const struct key_descriptor_s vlc_keys[] =
54 {
55     { "Unset", KEY_UNSET },
56     { "Space", ' ' },
57     { "Left", KEY_LEFT },
58     { "Right", KEY_RIGHT },
59     { "Up", KEY_UP },
60     { "Down", KEY_DOWN },
61     { "Enter", KEY_ENTER },
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     { "Esc", KEY_ESC },
80     { "Page Up", KEY_PAGEUP },
81     { "Page Down", KEY_PAGEDOWN },
82     { "Tab", KEY_TAB },
83     { "Backspace", KEY_BACKSPACE },
84     { "Browser Back", KEY_BROWSER_BACK },
85     { "Browser Forward", KEY_BROWSER_FORWARD },
86     { "Browser Refresh", KEY_BROWSER_REFRESH },
87     { "Browser Stop", KEY_BROWSER_STOP },
88     { "Browser Search", KEY_BROWSER_SEARCH },
89     { "Browser Favorites", KEY_BROWSER_FAVORITES },
90     { "Browser Home", KEY_BROWSER_HOME },
91     { "Volume Mute", KEY_VOLUME_MUTE },
92     { "Volume Down", KEY_VOLUME_DOWN },
93     { "Volume Up", KEY_VOLUME_UP },
94     { "Media Next Track", KEY_MEDIA_NEXT_TRACK },
95     { "Media Prev Track", KEY_MEDIA_PREV_TRACK },
96     { "Media Stop", KEY_MEDIA_STOP },
97     { "Media Play Pause", KEY_MEDIA_PLAY_PAUSE },
98     { "Mouse Wheel Up", KEY_MOUSEWHEELUP },
99     { "Mouse Wheel Down", KEY_MOUSEWHEELDOWN },
100     { "Mouse Wheel Left", KEY_MOUSEWHEELLEFT },
101     { "Mouse Wheel Right", KEY_MOUSEWHEELRIGHT },
102 };
103 enum { vlc_num_keys=sizeof(vlc_keys)/sizeof(struct key_descriptor_s) };
104
105 static int cmpkey (const void *key, const void *elem)
106 {
107     return ((uintptr_t)key) - ((key_descriptor_t *)elem)->i_key_code;
108 }
109
110 /* Convert Unicode code point to UTF-8 */
111 static char *utf8_cp (uint_fast32_t cp, char *buf)
112 {
113     if (cp < (1 << 7))
114     {
115         buf[1] = 0;
116         buf[0] = cp;
117     }
118     else if (cp < (1 << 11))
119     {
120         buf[2] = 0;
121         buf[1] = 0x80 | (cp & 0x3F);
122         cp >>= 6;
123         buf[0] = 0xC0 | cp;
124     }
125     else if (cp < (1 << 16))
126     {
127         buf[3] = 0;
128         buf[2] = 0x80 | (cp & 0x3F);
129         cp >>= 6;
130         buf[1] = 0x80 | (cp & 0x3F);
131         cp >>= 6;
132         buf[0] = 0xE0 | cp;
133     }
134     else if (cp < (1 << 21))
135     {
136         buf[4] = 0;
137         buf[3] = 0x80 | (cp & 0x3F);
138         cp >>= 6;
139         buf[2] = 0x80 | (cp & 0x3F);
140         cp >>= 6;
141         buf[1] = 0x80 | (cp & 0x3F);
142         cp >>= 6;
143         buf[0] = 0xE0 | cp;
144     }
145     else
146         return NULL;
147     return buf;
148 }
149
150 /* Convert UTF-8 to Unicode code point */
151 static uint_fast32_t cp_utf8 (const char *utf8)
152 {
153     uint8_t f = utf8[0];
154     size_t l = strlen (utf8);
155
156     if (f < 0x80) /* ASCII (7 bits) */
157         return f;
158     if (f < 0xC0 || l < 2) /* bad */
159         return 0;
160     if (f < 0xE0) /* two bytes (11 bits) */
161         return ((f & 0x1F) << 6) | (utf8[1] & 0x3F);
162     if (l < 3) /* bad */
163         return 0;
164     if (f < 0xF0) /* three bytes (16 bits) */
165         return ((f & 0x0F) << 12) | ((utf8[1] & 0x3F) << 6)
166                | (utf8[2] & 0x3F);
167     if (l < 4)
168         return 0;
169     if (f < 0xF8) /* four bytes (21 bits) */
170         return ((f & 0x07) << 18) | ((utf8[1] & 0x3F) << 12)
171                | ((utf8[2] & 0x3F) << 6) | (utf8[3] & 0x3F);
172     return 0;
173 }
174
175 char *KeyToString (uint_fast32_t sym)
176 {
177     key_descriptor_t *d;
178
179     d = bsearch ((void *)(uintptr_t)sym, vlc_keys, vlc_num_keys,
180                  sizeof (vlc_keys[0]), cmpkey);
181     if (d)
182         return strdup (d->psz_key_string);
183
184     char buf[5];
185     if (utf8_cp (sym, buf))
186         return strdup (buf);
187
188     return NULL;
189 }
190
191 uint_fast32_t StringToKey (char *name)
192 {
193     for (size_t i = 0; i < vlc_num_keys; i++)
194         if (!strcmp (vlc_keys[i].psz_key_string, name))
195             return vlc_keys[i].i_key_code;
196
197     return cp_utf8 (name);
198 }
199
200 uint_fast32_t ConfigStringToKey (const char *name)
201 {
202     uint_fast32_t mods = 0;
203
204     for (;;)
205     {
206         const char *psz_parser = strchr (name, '-');
207         if (psz_parser == NULL || psz_parser == name)
208             break;
209
210         for (size_t i = 0; i < vlc_num_modifiers; i++)
211         {
212             if (!strncasecmp (vlc_modifiers[i].psz_key_string, name,
213                               strlen (vlc_modifiers[i].psz_key_string)))
214             {
215                 mods |= vlc_modifiers[i].i_key_code;
216             }
217         }
218         name = psz_parser + 1;
219     }
220
221     for (size_t i = 0; i < vlc_num_keys; i++)
222         if (!strcasecmp( vlc_keys[i].psz_key_string, name))
223             return vlc_keys[i].i_key_code | mods;
224
225     return cp_utf8 (name) | mods;
226 }
227
228 char *ConfigKeyToString (uint_fast32_t i_key)
229 {
230     // Worst case appears to be 45 characters:
231     // "Command-Meta-Ctrl-Shift-Alt-Browser Favorites"
232     char *psz_key = malloc (64);
233     if (!psz_key)
234         return NULL;
235
236     char *p = psz_key, *psz_end = psz_key + 54;
237     *p = '\0';
238
239     for (size_t i = 0; i < vlc_num_modifiers; i++)
240     {
241         if (i_key & vlc_modifiers[i].i_key_code)
242         {
243             p += snprintf (p, psz_end - p, "%s-",
244                            vlc_modifiers[i].psz_key_string);
245         }
246     }
247
248     key_descriptor_t *d;
249     char buf[5];
250
251     i_key &= ~KEY_MODIFIER;
252     d = bsearch ((void *)(uintptr_t)i_key, vlc_keys, vlc_num_keys,
253                  sizeof (vlc_keys[0]), cmpkey);
254     if (d)
255         p += snprintf (p, psz_end - p, "%s", d->psz_key_string);
256     else if (utf8_cp (i_key, buf))
257         p += snprintf (p, psz_end - p, "%s", buf);
258     else
259         return NULL;
260
261     return psz_key;
262 }