JWM Source Documentation
key.c
Go to the documentation of this file.
1 
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 
83 void InitializeKeys(void)
84 {
85  bindings = NULL;
86  lockMask = 0;
87 }
88 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 

joewing.net / Projects / JWM