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