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