1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /*
4 * Copyright (C) 2014 Red Hat
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 *
21 * Written by:
22 * Jasper St. Pierre <jstpierre@mecheye.net>
23 */
24
25 #include "config.h"
26
27 #include "core/meta-accel-parse.h"
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <xkbcommon/xkbcommon.h>
32
33 #include "core/keybindings-private.h"
34
35 /* This is copied from GTK+ and modified to work with mutter's
36 * internal structures. Originating code comes from gtk/gtkaccelgroup.c
37 */
38
39 static inline gboolean
is_alt(const gchar * string)40 is_alt (const gchar *string)
41 {
42 return ((string[0] == '<') &&
43 (string[1] == 'a' || string[1] == 'A') &&
44 (string[2] == 'l' || string[2] == 'L') &&
45 (string[3] == 't' || string[3] == 'T') &&
46 (string[4] == '>'));
47 }
48
49 static inline gboolean
is_ctl(const gchar * string)50 is_ctl (const gchar *string)
51 {
52 return ((string[0] == '<') &&
53 (string[1] == 'c' || string[1] == 'C') &&
54 (string[2] == 't' || string[2] == 'T') &&
55 (string[3] == 'l' || string[3] == 'L') &&
56 (string[4] == '>'));
57 }
58
59 static inline gboolean
is_modx(const gchar * string)60 is_modx (const gchar *string)
61 {
62 return ((string[0] == '<') &&
63 (string[1] == 'm' || string[1] == 'M') &&
64 (string[2] == 'o' || string[2] == 'O') &&
65 (string[3] == 'd' || string[3] == 'D') &&
66 (string[4] >= '1' && string[4] <= '5') &&
67 (string[5] == '>'));
68 }
69
70 static inline gboolean
is_ctrl(const gchar * string)71 is_ctrl (const gchar *string)
72 {
73 return ((string[0] == '<') &&
74 (string[1] == 'c' || string[1] == 'C') &&
75 (string[2] == 't' || string[2] == 'T') &&
76 (string[3] == 'r' || string[3] == 'R') &&
77 (string[4] == 'l' || string[4] == 'L') &&
78 (string[5] == '>'));
79 }
80
81 static inline gboolean
is_shft(const gchar * string)82 is_shft (const gchar *string)
83 {
84 return ((string[0] == '<') &&
85 (string[1] == 's' || string[1] == 'S') &&
86 (string[2] == 'h' || string[2] == 'H') &&
87 (string[3] == 'f' || string[3] == 'F') &&
88 (string[4] == 't' || string[4] == 'T') &&
89 (string[5] == '>'));
90 }
91
92 static inline gboolean
is_shift(const gchar * string)93 is_shift (const gchar *string)
94 {
95 return ((string[0] == '<') &&
96 (string[1] == 's' || string[1] == 'S') &&
97 (string[2] == 'h' || string[2] == 'H') &&
98 (string[3] == 'i' || string[3] == 'I') &&
99 (string[4] == 'f' || string[4] == 'F') &&
100 (string[5] == 't' || string[5] == 'T') &&
101 (string[6] == '>'));
102 }
103
104 static inline gboolean
is_control(const gchar * string)105 is_control (const gchar *string)
106 {
107 return ((string[0] == '<') &&
108 (string[1] == 'c' || string[1] == 'C') &&
109 (string[2] == 'o' || string[2] == 'O') &&
110 (string[3] == 'n' || string[3] == 'N') &&
111 (string[4] == 't' || string[4] == 'T') &&
112 (string[5] == 'r' || string[5] == 'R') &&
113 (string[6] == 'o' || string[6] == 'O') &&
114 (string[7] == 'l' || string[7] == 'L') &&
115 (string[8] == '>'));
116 }
117
118 static inline gboolean
is_meta(const gchar * string)119 is_meta (const gchar *string)
120 {
121 return ((string[0] == '<') &&
122 (string[1] == 'm' || string[1] == 'M') &&
123 (string[2] == 'e' || string[2] == 'E') &&
124 (string[3] == 't' || string[3] == 'T') &&
125 (string[4] == 'a' || string[4] == 'A') &&
126 (string[5] == '>'));
127 }
128
129 static inline gboolean
is_super(const gchar * string)130 is_super (const gchar *string)
131 {
132 return ((string[0] == '<') &&
133 (string[1] == 's' || string[1] == 'S') &&
134 (string[2] == 'u' || string[2] == 'U') &&
135 (string[3] == 'p' || string[3] == 'P') &&
136 (string[4] == 'e' || string[4] == 'E') &&
137 (string[5] == 'r' || string[5] == 'R') &&
138 (string[6] == '>'));
139 }
140
141 static inline gboolean
is_hyper(const gchar * string)142 is_hyper (const gchar *string)
143 {
144 return ((string[0] == '<') &&
145 (string[1] == 'h' || string[1] == 'H') &&
146 (string[2] == 'y' || string[2] == 'Y') &&
147 (string[3] == 'p' || string[3] == 'P') &&
148 (string[4] == 'e' || string[4] == 'E') &&
149 (string[5] == 'r' || string[5] == 'R') &&
150 (string[6] == '>'));
151 }
152
153 static inline gboolean
is_primary(const gchar * string)154 is_primary (const gchar *string)
155 {
156 return ((string[0] == '<') &&
157 (string[1] == 'p' || string[1] == 'P') &&
158 (string[2] == 'r' || string[2] == 'R') &&
159 (string[3] == 'i' || string[3] == 'I') &&
160 (string[4] == 'm' || string[4] == 'M') &&
161 (string[5] == 'a' || string[5] == 'A') &&
162 (string[6] == 'r' || string[6] == 'R') &&
163 (string[7] == 'y' || string[7] == 'Y') &&
164 (string[8] == '>'));
165 }
166
167 static inline gboolean
is_keycode(const gchar * string)168 is_keycode (const gchar *string)
169 {
170 return (string[0] == '0' &&
171 string[1] == 'x' &&
172 g_ascii_isxdigit (string[2]) &&
173 g_ascii_isxdigit (string[3]));
174 }
175
176 static gboolean
accelerator_parse(const gchar * accelerator,MetaKeyCombo * combo)177 accelerator_parse (const gchar *accelerator,
178 MetaKeyCombo *combo)
179 {
180 guint keyval, keycode;
181 MetaVirtualModifier mods;
182 gint len;
183
184 combo->keysym = 0;
185 combo->keycode = 0;
186 combo->modifiers = 0;
187
188 if (accelerator == NULL)
189 return FALSE;
190
191 keyval = 0;
192 keycode = 0;
193 mods = 0;
194 len = strlen (accelerator);
195 while (len)
196 {
197 if (*accelerator == '<')
198 {
199 if (len >= 9 && is_primary (accelerator))
200 {
201 /* Primary is treated the same as Control */
202 accelerator += 9;
203 len -= 9;
204 mods |= META_VIRTUAL_CONTROL_MASK;
205 }
206 else if (len >= 9 && is_control (accelerator))
207 {
208 accelerator += 9;
209 len -= 9;
210 mods |= META_VIRTUAL_CONTROL_MASK;
211 }
212 else if (len >= 7 && is_shift (accelerator))
213 {
214 accelerator += 7;
215 len -= 7;
216 mods |= META_VIRTUAL_SHIFT_MASK;
217 }
218 else if (len >= 6 && is_shft (accelerator))
219 {
220 accelerator += 6;
221 len -= 6;
222 mods |= META_VIRTUAL_SHIFT_MASK;
223 }
224 else if (len >= 6 && is_ctrl (accelerator))
225 {
226 accelerator += 6;
227 len -= 6;
228 mods |= META_VIRTUAL_CONTROL_MASK;
229 }
230 else if (len >= 6 && is_modx (accelerator))
231 {
232 static const guint mod_vals[] = {
233 META_VIRTUAL_ALT_MASK,
234 META_VIRTUAL_MOD2_MASK,
235 META_VIRTUAL_MOD3_MASK,
236 META_VIRTUAL_MOD4_MASK,
237 META_VIRTUAL_MOD5_MASK,
238 };
239
240 len -= 6;
241 accelerator += 4;
242 mods |= mod_vals[*accelerator - '1'];
243 accelerator += 2;
244 }
245 else if (len >= 5 && is_ctl (accelerator))
246 {
247 accelerator += 5;
248 len -= 5;
249 mods |= META_VIRTUAL_CONTROL_MASK;
250 }
251 else if (len >= 5 && is_alt (accelerator))
252 {
253 accelerator += 5;
254 len -= 5;
255 mods |= META_VIRTUAL_ALT_MASK;
256 }
257 else if (len >= 6 && is_meta (accelerator))
258 {
259 accelerator += 6;
260 len -= 6;
261 mods |= META_VIRTUAL_META_MASK;
262 }
263 else if (len >= 7 && is_hyper (accelerator))
264 {
265 accelerator += 7;
266 len -= 7;
267 mods |= META_VIRTUAL_HYPER_MASK;
268 }
269 else if (len >= 7 && is_super (accelerator))
270 {
271 accelerator += 7;
272 len -= 7;
273 mods |= META_VIRTUAL_SUPER_MASK;
274 }
275 else
276 {
277 gchar last_ch;
278
279 last_ch = *accelerator;
280 while (last_ch && last_ch != '>')
281 {
282 last_ch = *accelerator;
283 accelerator += 1;
284 len -= 1;
285 }
286 }
287 }
288 else
289 {
290 if (len >= 4 && is_keycode (accelerator))
291 {
292 keycode = strtoul (accelerator, NULL, 16);
293 goto out;
294 }
295 else if (strcmp (accelerator, "Above_Tab") == 0)
296 {
297 keyval = META_KEY_ABOVE_TAB;
298 goto out;
299 }
300 else
301 {
302 keyval = xkb_keysym_from_name (accelerator, XKB_KEYSYM_CASE_INSENSITIVE);
303 if (keyval == XKB_KEY_NoSymbol)
304 {
305 char *with_xf86 = g_strconcat ("XF86", accelerator, NULL);
306 keyval = xkb_keysym_from_name (with_xf86, XKB_KEYSYM_CASE_INSENSITIVE);
307 g_free (with_xf86);
308
309 if (keyval == XKB_KEY_NoSymbol)
310 return FALSE;
311 }
312 }
313
314 accelerator += len;
315 len -= len;
316 }
317 }
318
319 out:
320 combo->keysym = keyval;
321 combo->keycode = keycode;
322 combo->modifiers = mods;
323 return TRUE;
324 }
325
326 gboolean
meta_parse_accelerator(const char * accel,MetaKeyCombo * combo)327 meta_parse_accelerator (const char *accel,
328 MetaKeyCombo *combo)
329 {
330 g_return_val_if_fail (combo != NULL, FALSE);
331
332 *combo = (MetaKeyCombo) { 0 };
333
334 if (!accel[0] || strcmp (accel, "disabled") == 0)
335 return TRUE;
336
337 return accelerator_parse (accel, combo);
338 }
339
340 gboolean
meta_parse_modifier(const char * accel,MetaVirtualModifier * mask)341 meta_parse_modifier (const char *accel,
342 MetaVirtualModifier *mask)
343 {
344 MetaKeyCombo combo = { 0 };
345
346 g_return_val_if_fail (mask != NULL, FALSE);
347
348 *mask = 0;
349
350 if (accel == NULL || !accel[0] || strcmp (accel, "disabled") == 0)
351 return TRUE;
352
353 if (!accelerator_parse (accel, &combo))
354 return FALSE;
355
356 *mask = combo.modifiers;
357 return TRUE;
358 }
359