2 * ion/ioncore/binding.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
17 #include <libtu/objp.h>
19 #include <libextl/extl.h>
22 #ifndef CF_NO_LOCK_HACK
23 #define CF_HACK_IGNORE_EVIL_LOCKS
26 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
28 #include <X11/keysymdef.h>
37 static const uint modmasks[N_MODS]={
38 ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask,
42 static XModifierKeymap *modmap=NULL;
44 #define KNOWN_MODIFIERS_MASK (ShiftMask|LockMask|ControlMask|Mod1Mask|\
45 Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
47 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
50 #define N_LOOKUPEVIL 2
52 static uint evillockmasks[N_EVILLOCKS]={
56 static const KeySym evillocks[N_LOOKUPEVIL]={
57 XK_Num_Lock, XK_Scroll_Lock
60 static uint evilignoremask=LockMask;
62 static void lookup_evil_locks();
64 static void evil_grab_key(Display *display, uint keycode, uint modifiers,
65 Window grab_window, bool owner_events,
66 int pointer_mode, int keyboard_mode);
68 static void evil_grab_button(Display *display, uint button, uint modifiers,
69 Window grab_window, bool owner_events,
70 uint event_mask, int pointer_mode,
71 int keyboard_mode, Window confine_to,
74 static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
77 static void evil_ungrab_button(Display *display, uint button, uint modifiers,
83 #define CVAL(A, B, V) ( A->V < B->V ? -1 : (A->V > B->V ? 1 : 0))
85 static int compare_bindings(const WBinding *a, const WBinding *b)
87 int r=CVAL(a, b, act);
100 /* This is only used for searching AnyKey etc. */
101 static int compare_bindings_ksb(const WBinding *a, const WBinding *b)
103 int r=CVAL(a, b, act);
119 bool init_bindmap(WBindmap *bindmap)
121 bindmap->rbind_list=NULL;
122 bindmap->areamap=NULL;
123 bindmap->nbindings=0;
124 bindmap->bindings=make_rb();
125 if(bindmap->bindings==NULL){
133 WBindmap *create_bindmap()
135 WBindmap *bindmap=ALLOC(WBindmap);
142 if(!init_bindmap(bindmap)){
151 void binding_deinit(WBinding *binding)
153 if(binding->submap!=NULL){
154 bindmap_destroy(binding->submap);
155 binding->submap=NULL;
158 binding->func=extl_unref_fn(binding->func);
162 static void do_destroy_binding(WBinding *binding)
164 assert(binding!=NULL);
165 binding_deinit(binding);
170 static void bindmap_deinit(WBindmap *bindmap)
175 while(bindmap->rbind_list!=NULL){
176 region_remove_bindmap(bindmap->rbind_list->reg,
180 if(bindmap->bindings==NULL)
183 FOR_ALL_BINDINGS(b, node, bindmap->bindings){
184 do_destroy_binding((WBinding*)rb_val(node));
185 bindmap->nbindings--;
188 assert(bindmap->nbindings==0);
190 rb_free_tree(bindmap->bindings);
191 bindmap->bindings=NULL;
195 void bindmap_destroy(WBindmap *bindmap)
197 bindmap_deinit(bindmap);
202 static void free_map(Rb_node map)
207 FOR_ALL_BINDINGS(b, node, map)
214 void bindmap_refresh(WBindmap *bindmap)
216 WRegBindingInfo *rbind;
217 Rb_node newtree, node;
220 if(bindmap->bindings==NULL)
230 FOR_ALL_BINDINGS(b, node, bindmap->bindings){
240 if(b->act==BINDING_KEYPRESS){
241 for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
242 rbind_binding_removed(rbind, b, bindmap);
243 b2->kcb=XKeysymToKeycode(ioncore_g.dpy, b->ksb);
246 if(!rb_insertg(newtree, b2, b2, (Rb_compfn*)compare_bindings)){
254 free_map(bindmap->bindings);
255 bindmap->bindings=newtree;
257 FOR_ALL_BINDINGS(b, node, bindmap->bindings){
258 if(b->act!=BINDING_KEYPRESS)
260 for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
261 rbind_binding_added(rbind, b, bindmap);
263 bindmap_refresh(b->submap);
268 bool bindmap_add_binding(WBindmap *bindmap, const WBinding *b)
270 WRegBindingInfo *rbind=NULL;
271 WBinding *binding=NULL;
275 /* Handle adding the binding */
276 binding=ALLOC(WBinding);
283 memcpy(binding, b, sizeof(*b));
285 node=rb_find_gkey_n(bindmap->bindings, binding,
286 (Rb_compfn*)compare_bindings, &found);
289 if(!rb_insert_a(node, binding, binding)){
293 do_destroy_binding((WBinding*)rb_val(node));
294 rb_delete_node(node);
295 bindmap->nbindings--;
297 if(!rb_insertg(bindmap->bindings, binding, binding,
298 (Rb_compfn*)compare_bindings)){
304 bindmap->nbindings++;
306 for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
307 rbind_binding_added(rbind, binding, bindmap);
313 bool bindmap_remove_binding(WBindmap *bindmap, const WBinding *b)
315 WRegBindingInfo *rbind=NULL;
316 WBinding *binding=NULL;
320 if(bindmap->bindings==NULL)
323 node=rb_find_gkey_n(bindmap->bindings, b, (Rb_compfn*)compare_bindings,
329 binding=(WBinding*)rb_val(node);
331 for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
332 rbind_binding_removed(rbind, binding, bindmap);
334 do_destroy_binding(binding);
335 rb_delete_node(node);
337 bindmap->nbindings--;
343 void ioncore_init_bindings()
345 modmap=XGetModifierMapping(ioncore_g.dpy);
347 assert(modmap!=NULL);
349 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
355 void ioncore_update_modmap()
357 XModifierKeymap *nm=XGetModifierMapping(ioncore_g.dpy);
360 XFreeModifiermap(modmap);
369 void binding_grab_on(const WBinding *binding, Window win)
371 if(binding->act==BINDING_KEYPRESS && binding->kcb!=0){
372 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
373 XGrabKey(ioncore_g.dpy, binding->kcb, binding->state, win,
374 True, GrabModeAsync, GrabModeAsync);
376 evil_grab_key(ioncore_g.dpy, binding->kcb, binding->state, win,
377 True, GrabModeAsync, GrabModeAsync);
381 if(binding->act!=BINDING_BUTTONPRESS &&
382 binding->act!=BINDING_BUTTONCLICK &&
383 binding->act!=BINDING_BUTTONDBLCLICK &&
384 binding->act!=BINDING_BUTTONMOTION)
387 if(binding->state==0)
390 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
391 XGrabButton(ioncore_g.dpy, binding->kcb, binding->state, win,
392 True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync,
395 evil_grab_button(ioncore_g.dpy, binding->kcb, binding->state, win,
396 True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync,
402 void binding_ungrab_on(const WBinding *binding, Window win)
404 if(binding->act==BINDING_KEYPRESS){
405 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
406 XUngrabKey(ioncore_g.dpy, binding->kcb, binding->state, win);
408 evil_ungrab_key(ioncore_g.dpy, binding->kcb, binding->state, win);
412 if(binding->act!=BINDING_BUTTONPRESS &&
413 binding->act!=BINDING_BUTTONCLICK &&
414 binding->act!=BINDING_BUTTONDBLCLICK &&
415 binding->act!=BINDING_BUTTONMOTION)
418 if(binding->state==0)
421 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
422 XUngrabButton(ioncore_g.dpy, binding->kcb, binding->state, win);
424 evil_ungrab_button(ioncore_g.dpy, binding->kcb, binding->state, win);
432 static WBinding *search_binding(WBindmap *bindmap, WBinding *binding)
437 if(bindmap->bindings==NULL)
440 node=rb_find_gkey_n(bindmap->bindings, binding,
441 (Rb_compfn*)compare_bindings, &found);
446 return (WBinding*)rb_val(node);
450 static WBinding *search_binding_ksb(WBindmap *bindmap, WBinding *binding)
455 if(bindmap->bindings==NULL)
458 node=rb_find_gkey_n(bindmap->bindings, binding,
459 (Rb_compfn*)compare_bindings_ksb, &found);
464 return (WBinding*)rb_val(node);
468 static WBinding *do_bindmap_lookup_binding(WBindmap *bindmap,
472 WBinding *binding, tmp;
474 if(bindmap->nbindings==0)
477 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
478 state&=~evilignoremask;
480 state&=KNOWN_MODIFIERS_MASK;
487 binding=search_binding(bindmap, &tmp);
490 tmp.state=AnyModifier;
491 binding=search_binding(bindmap, &tmp);
495 tmp.ksb=(act==BINDING_KEYPRESS ? AnyKey : AnyButton);
497 binding=search_binding_ksb(bindmap, &tmp);
500 tmp.state=AnyModifier;
501 binding=search_binding_ksb(bindmap, &tmp);
510 WBinding *bindmap_lookup_binding(WBindmap *bindmap,
511 int act, uint state, uint kcb)
513 return do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
517 WBinding *bindmap_lookup_binding_area(WBindmap *bindmap,
518 int act, uint state, uint kcb, int area)
522 binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, area);
525 binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
532 * A dirty hack to deal with (==ignore) evil locking modifier keys.
536 int ioncore_unmod(int state, int keycode)
540 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
541 state&=~evilignoremask;
544 for(j=0; j<N_MODS*modmap->max_keypermod; j++){
545 if(modmap->modifiermap[j]==keycode)
546 return state&~modmasks[j/modmap->max_keypermod];
553 int ioncore_modstate()
559 XQueryKeymap(ioncore_g.dpy, keys);
561 for(j=0; j<N_MODS*modmap->max_keypermod; j++){
562 int a=(modmap->modifiermap[j]&7);
563 int b=(modmap->modifiermap[j]>>3);
566 state|=modmasks[j/modmap->max_keypermod];
570 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
571 state&=~evilignoremask;
577 bool ioncore_ismod(int keycode)
581 for(j=0; j<N_MODS*modmap->max_keypermod; j++){
582 if(modmap->modifiermap[j]==keycode)
590 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
592 static void lookup_evil_locks()
594 uint keycodes[N_LOOKUPEVIL];
597 for(i=0; i<N_LOOKUPEVIL; i++)
598 keycodes[i]=XKeysymToKeycode(ioncore_g.dpy, evillocks[i]);
600 for(j=0; j<N_MODS*modmap->max_keypermod; j++){
601 for(i=0; i<N_LOOKUPEVIL; i++){
602 if(keycodes[i]==None)
604 if(modmap->modifiermap[j]==keycodes[i]){
605 evillockmasks[i]=modmasks[j/modmap->max_keypermod];
606 evilignoremask|=evillockmasks[i];
613 static void evil_grab_key(Display *display, uint keycode, uint modifiers,
614 Window grab_window, bool owner_events,
615 int pointer_mode, int keyboard_mode)
620 XGrabKey(display, keycode, modifiers, grab_window, owner_events,
621 pointer_mode, keyboard_mode);
623 if(modifiers==AnyModifier)
626 for(i=0; i<N_EVILLOCKS; i++){
627 if(evillockmasks[i]==0)
630 for(j=i; j<N_EVILLOCKS; j++){
631 if(evillockmasks[j]==0)
633 mods|=evillockmasks[j];
634 XGrabKey(display, keycode, mods,
635 grab_window, owner_events, pointer_mode, keyboard_mode);
638 XGrabKey(display, keycode,
639 modifiers|evillockmasks[i]|evillockmasks[j],
640 grab_window, owner_events, pointer_mode, keyboard_mode);
646 static void evil_grab_button(Display *display, uint button, uint modifiers,
647 Window grab_window, bool owner_events,
648 uint event_mask, int pointer_mode,
649 int keyboard_mode, Window confine_to,
655 XGrabButton(display, button, modifiers,
656 grab_window, owner_events, event_mask, pointer_mode,
657 keyboard_mode, confine_to, cursor);
659 if(modifiers==AnyModifier)
662 for(i=0; i<N_EVILLOCKS; i++){
663 if(evillockmasks[i]==0)
666 for(j=i; j<N_EVILLOCKS; j++){
667 if(evillockmasks[j]==0)
669 mods|=evillockmasks[j];
670 XGrabButton(display, button, mods,
671 grab_window, owner_events, event_mask, pointer_mode,
672 keyboard_mode, confine_to, cursor);
675 XGrabButton(display, button,
676 modifiers|evillockmasks[i]|evillockmasks[j],
677 grab_window, owner_events, event_mask, pointer_mode,
678 keyboard_mode, confine_to, cursor);
684 static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
690 XUngrabKey(display, keycode, modifiers, grab_window);
692 if(modifiers==AnyModifier)
695 for(i=0; i<N_EVILLOCKS; i++){
696 if(evillockmasks[i]==0)
699 for(j=i; j<N_EVILLOCKS; j++){
700 if(evillockmasks[j]==0)
702 mods|=evillockmasks[j];
703 XUngrabKey(display, keycode, mods, grab_window);
706 XUngrabKey(display, keycode,
707 modifiers|evillockmasks[i]|evillockmasks[j],
714 static void evil_ungrab_button(Display *display, uint button, uint modifiers,
720 XUngrabButton(display, button, modifiers, grab_window);
722 if(modifiers==AnyModifier)
725 for(i=0; i<N_EVILLOCKS; i++){
726 if(evillockmasks[i]==0)
729 for(j=i; j<N_EVILLOCKS; j++){
730 if(evillockmasks[j]==0)
732 mods|=evillockmasks[j];
733 XUngrabButton(display, button, mods, grab_window);
736 XUngrabButton(display, button,
737 modifiers|evillockmasks[i]|evillockmasks[j],
744 #endif /* CF_HACK_IGNORE_EVIL_LOCKS */