1 /**
2  * @file key.c
3  * @author Joe Wingbermuehle
4  * @date 2004-2006
5  *
6  * @brief Key binding functions.
7  *
8  */
9 
10 #include "jwm.h"
11 #include "key.h"
12 
13 #include "client.h"
14 #include "clientlist.h"
15 #include "command.h"
16 #include "error.h"
17 #include "misc.h"
18 #include "root.h"
19 #include "tray.h"
20 
21 #define MASK_NONE    0
22 #define MASK_SHIFT   (1 << ShiftMapIndex)
23 #define MASK_LOCK    (1 << LockMapIndex)
24 #define MASK_CTRL    (1 << ControlMapIndex)
25 #define MASK_MOD1    (1 << Mod1MapIndex)
26 #define MASK_MOD2    (1 << Mod2MapIndex)
27 #define MASK_MOD3    (1 << Mod3MapIndex)
28 #define MASK_MOD4    (1 << Mod4MapIndex)
29 #define MASK_MOD5    (1 << Mod5MapIndex)
30 
31 typedef struct ModifierNode {
32    char           name;
33    unsigned int   mask;
34 } ModifierNode;
35 
36 static ModifierNode modifiers[] = {
37 
38    { 'C',   MASK_CTRL      },
39    { 'S',   MASK_SHIFT     },
40    { 'A',   MASK_MOD1      },
41    { '1',   MASK_MOD1      },
42    { '2',   MASK_MOD2      },
43    { '3',   MASK_MOD3      },
44    { '4',   MASK_MOD4      },
45    { '5',   MASK_MOD5      },
46    { 0,     MASK_NONE      }
47 
48 };
49 
50 typedef struct KeyNode {
51 
52    /* These are filled in when the configuration file is parsed */
53    int key;
54    unsigned int state;
55    KeySym symbol;
56    char *command;
57    struct KeyNode *next;
58 
59    /* This is filled in by StartupKeys if it isn't already set. */
60    KeyCode code;
61 
62 } KeyNode;
63 
64 typedef struct LockNode {
65    KeySym symbol;
66    unsigned int mask;
67 } LockNode;
68 
69 static LockNode lockMods[] = {
70    { XK_Caps_Lock,   0 },
71    { XK_Num_Lock,    0 }
72 };
73 
74 static KeyNode *bindings;
75 unsigned int lockMask;
76 
77 static unsigned int GetModifierMask(XModifierKeymap *modmap, KeySym key);
78 static KeySym ParseKeyString(const char *str);
79 static char ShouldGrab(KeyType key);
80 static void GrabKey(KeyNode *np, Window win);
81 
82 /** Initialize key data. */
InitializeKeys(void)83 void InitializeKeys(void)
84 {
85    bindings = NULL;
86    lockMask = 0;
87 }
88 
89 /** Startup key bindings. */
StartupKeys(void)90 void StartupKeys(void)
91 {
92 
93    XModifierKeymap *modmap;
94    KeyNode *np;
95    TrayType *tp;
96    int x;
97 
98    /* Get the keys that we don't care about (num lock, etc). */
99    modmap = JXGetModifierMapping(display);
100    for(x = 0; x < sizeof(lockMods) / sizeof(lockMods[0]); x++) {
101       lockMods[x].mask = GetModifierMask(modmap, lockMods[x].symbol);
102       lockMask |= lockMods[x].mask;
103    }
104    JXFreeModifiermap(modmap);
105 
106    /* Look up and grab the keys. */
107    for(np = bindings; np; np = np->next) {
108 
109       /* Determine the key code. */
110       if(!np->code) {
111          np->code = JXKeysymToKeycode(display, np->symbol);
112       }
113 
114       /* Grab the key if needed. */
115       if(ShouldGrab(np->key)) {
116 
117          /* Grab on the root. */
118          GrabKey(np, rootWindow);
119 
120          /* Grab on the trays. */
121          for(tp = GetTrays(); tp; tp = tp->next) {
122             GrabKey(np, tp->window);
123          }
124 
125       }
126 
127    }
128 
129 }
130 
131 /** Shutdown key bindings. */
ShutdownKeys(void)132 void ShutdownKeys(void)
133 {
134 
135    ClientNode *np;
136    TrayType *tp;
137    unsigned int layer;
138 
139    /* Ungrab keys on client windows. */
140    for(layer = 0; layer < LAYER_COUNT; layer++) {
141       for(np = nodes[layer]; np; np = np->next) {
142          JXUngrabKey(display, AnyKey, AnyModifier, np->window);
143       }
144    }
145 
146    /* Ungrab keys on trays, only really needed if we are restarting. */
147    for(tp = GetTrays(); tp; tp = tp->next) {
148       JXUngrabKey(display, AnyKey, AnyModifier, tp->window);
149    }
150 
151    /* Ungrab keys on the root. */
152    JXUngrabKey(display, AnyKey, AnyModifier, rootWindow);
153 
154 }
155 
156 /** Destroy key data. */
DestroyKeys(void)157 void DestroyKeys(void)
158 {
159    KeyNode *np;
160    while(bindings) {
161       np = bindings->next;
162       if(bindings->command) {
163          Release(bindings->command);
164       }
165       Release(bindings);
166       bindings = np;
167    }
168 }
169 
170 /** Grab a key. */
GrabKey(KeyNode * np,Window win)171 void GrabKey(KeyNode *np, Window win)
172 {
173    unsigned int x;
174    unsigned int index, maxIndex;
175    unsigned int mask;
176 
177    /* Don't attempt to grab if there is nothing to grab. */
178    if(!np->code) {
179       return;
180    }
181 
182    /* Grab for each lock modifier. */
183    maxIndex = 1 << (sizeof(lockMods) / sizeof(lockMods[0]));
184    for(index = 0; index < maxIndex; index++) {
185 
186       /* Compute the modifier mask. */
187       mask = 0;
188       for(x = 0; x < sizeof(lockMods) / sizeof(lockMods[0]); x++) {
189          if(index & (1 << x)) {
190             mask |= lockMods[x].mask;
191          }
192       }
193       mask |= np->state;
194 
195       /* Grab the key. */
196       JXGrabKey(display, np->code, mask, win,
197          True, GrabModeAsync, GrabModeAsync);
198 
199    }
200 
201 }
202 
203 /** Get the key action from an event. */
GetKey(const XKeyEvent * event)204 KeyType GetKey(const XKeyEvent *event)
205 {
206 
207    KeyNode *np;
208    unsigned int state;
209 
210    /* Remove modifiers we don't care about from the state. */
211    state = event->state & ~lockMask;
212 
213    /* Loop looking for a matching key binding. */
214    for(np = bindings; np; np = np->next) {
215       if(np->state == state && np->code == event->keycode) {
216          return np->key;
217       }
218    }
219 
220    return KEY_NONE;
221 
222 }
223 
224 /** Run a command invoked from a key binding. */
RunKeyCommand(const XKeyEvent * event)225 void RunKeyCommand(const XKeyEvent *event)
226 {
227 
228    KeyNode *np;
229    unsigned int state;
230 
231    /* Remove the lock key modifiers. */
232    state = event->state & ~lockMask;
233 
234    for(np = bindings; np; np = np->next) {
235       if(np->state == state && np->code == event->keycode) {
236          RunCommand(np->command);
237          return;
238       }
239    }
240 
241 }
242 
243 /** Show a root menu caused by a key binding. */
ShowKeyMenu(const XKeyEvent * event)244 void ShowKeyMenu(const XKeyEvent *event)
245 {
246 
247    KeyNode *np;
248    unsigned int state;
249 
250    /* Remove the lock key modifiers. */
251    state = event->state & ~lockMask;
252 
253    for(np = bindings; np; np = np->next) {
254       if(np->state == state && np->code == event->keycode) {
255          const int button = GetRootMenuIndexFromString(np->command);
256          if(JLIKELY(button >= 0)) {
257             ShowRootMenu(button, -1, -1, 1);
258          }
259          return;
260       }
261    }
262 
263 }
264 
265 /** Determine if a key should be grabbed on client windows. */
ShouldGrab(KeyType key)266 char ShouldGrab(KeyType key)
267 {
268    switch(key & 0xFF) {
269    case KEY_NEXT:
270    case KEY_NEXTSTACK:
271    case KEY_PREV:
272    case KEY_PREVSTACK:
273    case KEY_CLOSE:
274    case KEY_MIN:
275    case KEY_MAX:
276    case KEY_SHADE:
277    case KEY_STICK:
278    case KEY_MOVE:
279    case KEY_RESIZE:
280    case KEY_ROOT:
281    case KEY_WIN:
282    case KEY_DESKTOP:
283    case KEY_RDESKTOP:
284    case KEY_LDESKTOP:
285    case KEY_DDESKTOP:
286    case KEY_UDESKTOP:
287    case KEY_SHOWDESK:
288    case KEY_SHOWTRAY:
289    case KEY_EXEC:
290    case KEY_RESTART:
291    case KEY_EXIT:
292    case KEY_FULLSCREEN:
293    case KEY_SENDR:
294    case KEY_SENDL:
295    case KEY_SENDU:
296    case KEY_SENDD:
297    case KEY_MAXTOP:
298    case KEY_MAXBOTTOM:
299    case KEY_MAXLEFT:
300    case KEY_MAXRIGHT:
301    case KEY_MAXV:
302    case KEY_MAXH:
303    case KEY_RESTORE:
304       return 1;
305    default:
306       return 0;
307    }
308 }
309 
310 /** Get the modifier mask for a key. */
GetModifierMask(XModifierKeymap * modmap,KeySym key)311 unsigned int GetModifierMask(XModifierKeymap *modmap, KeySym key) {
312 
313    KeyCode temp;
314    int x;
315 
316    temp = JXKeysymToKeycode(display, key);
317    if(JUNLIKELY(temp == 0)) {
318       Warning(_("Specified KeySym is not defined for any KeyCode"));
319    }
320    for(x = 0; x < 8 * modmap->max_keypermod; x++) {
321       if(modmap->modifiermap[x] == temp) {
322          return 1 << (x / modmap->max_keypermod);
323       }
324    }
325 
326    Warning(_("modifier not found for keysym 0x%0x"), key);
327 
328    return 0;
329 
330 }
331 
332 /** Parse a modifier mask string. */
ParseModifierString(const char * str)333 unsigned int ParseModifierString(const char *str)
334 {
335    unsigned int mask;
336    unsigned int x, y;
337    char found;
338 
339    if(!str) {
340       return MASK_NONE;
341    }
342 
343    mask = MASK_NONE;
344    for(x = 0; str[x]; x++) {
345 
346       found = 0;
347       for(y = 0; modifiers[y].name; y++) {
348          if(modifiers[y].name == str[x]) {
349             mask |= modifiers[y].mask;
350             found = 1;
351             break;
352          }
353       }
354 
355       if(JUNLIKELY(!found)) {
356          Warning(_("invalid modifier: \"%c\""), str[x]);
357       }
358 
359    }
360 
361    return mask;
362 
363 }
364 
365 /** Parse a key string. */
ParseKeyString(const char * str)366 KeySym ParseKeyString(const char *str)
367 {
368    KeySym symbol;
369    symbol = JXStringToKeysym(str);
370    if(JUNLIKELY(symbol == NoSymbol)) {
371       Warning(_("invalid key symbol: \"%s\""), str);
372    }
373    return symbol;
374 }
375 
376 /** Insert a key binding. */
InsertBinding(KeyType key,const char * modifiers,const char * stroke,const char * code,const char * command)377 void InsertBinding(KeyType key, const char *modifiers,
378                    const char *stroke, const char *code,
379                    const char *command)
380 {
381 
382    KeyNode *np;
383    unsigned int mask;
384    char *temp;
385    KeySym sym;
386 
387    mask = ParseModifierString(modifiers);
388 
389    if(stroke && strlen(stroke) > 0) {
390       int offset;
391 
392       for(offset = 0; stroke[offset]; offset++) {
393          if(stroke[offset] == '#') {
394 
395             temp = CopyString(stroke);
396 
397             for(temp[offset] = '1'; temp[offset] <= '9'; temp[offset]++) {
398 
399                sym = ParseKeyString(temp);
400                if(sym == NoSymbol) {
401                   Release(temp);
402                   return;
403                }
404 
405                np = Allocate(sizeof(KeyNode));
406                np->next = bindings;
407                bindings = np;
408 
409                np->key = key | ((temp[offset] - '1' + 1) << 8);
410                np->state = mask;
411                np->symbol = sym;
412                np->command = NULL;
413                np->code = 0;
414 
415             }
416 
417             Release(temp);
418 
419             return;
420          }
421       }
422 
423       sym = ParseKeyString(stroke);
424       if(sym == NoSymbol) {
425          return;
426       }
427 
428       np = Allocate(sizeof(KeyNode));
429       np->next = bindings;
430       bindings = np;
431 
432       np->key = key;
433       np->state = mask;
434       np->symbol = sym;
435       np->command = CopyString(command);
436       np->code = 0;
437 
438    } else if(code && strlen(code) > 0) {
439 
440       np = Allocate(sizeof(KeyNode));
441       np->next = bindings;
442       bindings = np;
443 
444       np->key = key;
445       np->state = mask;
446       np->symbol = NoSymbol;
447       np->command = CopyString(command);
448       np->code = atoi(code);
449 
450    } else {
451 
452       Warning(_("neither key nor keycode specified for Key"));
453       np = NULL;
454 
455    }
456 
457 }
458 
459 /** Validate key bindings. */
ValidateKeys(void)460 void ValidateKeys(void)
461 {
462    KeyNode *kp;
463    for(kp = bindings; kp; kp = kp->next) {
464       if((kp->key & 0xFF) == KEY_ROOT && kp->command) {
465          const int bindex = GetRootMenuIndexFromString(kp->command);
466          if(JUNLIKELY(!IsRootMenuDefined(bindex))) {
467             Warning(_("key binding: root menu \"%s\" not defined"),
468                     kp->command);
469          }
470       }
471    }
472 }
473 
474