Manpage formatting
[notion.git] / ioncore / binding.c
blob8f3a69e70a5cbed149efaa754086a39db9ebd915
1 /*
2 * ion/ioncore/binding.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
10 #include "common.h"
11 #include "event.h"
12 #include "binding.h"
13 #include "global.h"
14 #include <libtu/objp.h>
15 #include "regbind.h"
16 #include <libextl/extl.h>
19 #ifndef CF_NO_LOCK_HACK
20 #define CF_HACK_IGNORE_EVIL_LOCKS
21 #endif
23 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
24 #define XK_MISCELLANY
25 #include <X11/keysymdef.h>
26 #endif
29 /* */
32 #define N_MODS 8
34 static const uint modmasks[N_MODS]={
35 ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask,
36 Mod4Mask, Mod5Mask
39 static XModifierKeymap *modmap=NULL;
41 #define KNOWN_MODIFIERS_MASK (ShiftMask|LockMask|ControlMask|Mod1Mask|\
42 Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
44 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
46 #define N_EVILLOCKS 3
47 #define N_LOOKUPEVIL 2
49 static uint evillockmasks[N_EVILLOCKS]={
50 0, 0, LockMask
53 static const KeySym evillocks[N_LOOKUPEVIL]={
54 XK_Num_Lock, XK_Scroll_Lock
57 static uint evilignoremask=LockMask;
59 static void lookup_evil_locks();
61 static void evil_grab_key(Display *display, uint keycode, uint modifiers,
62 Window grab_window, bool owner_events,
63 int pointer_mode, int keyboard_mode);
65 static void evil_grab_button(Display *display, uint button, uint modifiers,
66 Window grab_window, bool owner_events,
67 uint event_mask, int pointer_mode,
68 int keyboard_mode, Window confine_to,
69 Cursor cursor);
71 static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
72 Window grab_window);
74 static void evil_ungrab_button(Display *display, uint button, uint modifiers,
75 Window grab_window);
77 #endif
80 #define CVAL(A, B, V) ( A->V < B->V ? -1 : (A->V > B->V ? 1 : 0))
82 static int compare_bindings(const WBinding *a, const WBinding *b)
84 int r=CVAL(a, b, act);
85 if(r==0){
86 r=CVAL(a, b, kcb);
87 if(r==0){
88 r=CVAL(a, b, state);
89 if(r==0){
90 r=CVAL(a, b, area);
94 return r;
97 /* This is only used for searching AnyKey etc. */
98 static int compare_bindings_any(const WBinding *a, const WBinding *b)
100 int r=compare_bindings(a, b);
102 if(r==0)
103 r=CVAL(a, b, ksb);
105 return r;
108 #undef CVAL
111 bool init_bindmap(WBindmap *bindmap)
113 bindmap->rbind_list=NULL;
114 bindmap->areamap=NULL;
115 bindmap->nbindings=0;
116 bindmap->bindings=make_rb();
117 if(bindmap->bindings==NULL){
118 warn_err();
119 return FALSE;
121 return TRUE;
125 WBindmap *create_bindmap()
127 WBindmap *bindmap=ALLOC(WBindmap);
129 if(bindmap==NULL){
130 warn_err();
131 return NULL;
134 if(!init_bindmap(bindmap)){
135 free(bindmap);
136 return NULL;
139 return bindmap;
143 void binding_deinit(WBinding *binding)
145 if(binding->submap!=NULL){
146 bindmap_destroy(binding->submap);
147 binding->submap=NULL;
150 binding->func=extl_unref_fn(binding->func);
154 static void do_destroy_binding(WBinding *binding)
156 assert(binding!=NULL);
157 binding_deinit(binding);
158 free(binding);
162 static void bindmap_deinit(WBindmap *bindmap)
164 WBinding *b=NULL;
165 Rb_node node=NULL;
167 while(bindmap->rbind_list!=NULL){
168 region_remove_bindmap(bindmap->rbind_list->reg,
169 bindmap);
172 if(bindmap->bindings==NULL)
173 return;
175 FOR_ALL_BINDINGS(b, node, bindmap->bindings){
176 do_destroy_binding((WBinding*)rb_val(node));
177 bindmap->nbindings--;
180 assert(bindmap->nbindings==0);
182 rb_free_tree(bindmap->bindings);
183 bindmap->bindings=NULL;
187 void bindmap_destroy(WBindmap *bindmap)
189 bindmap_deinit(bindmap);
190 free(bindmap);
194 static void free_map(Rb_node map)
196 Rb_node node;
197 WBinding *b;
199 FOR_ALL_BINDINGS(b, node, map)
200 free(b);
202 rb_free_tree(map);
206 void bindmap_refresh(WBindmap *bindmap)
208 WRegBindingInfo *rbind;
209 Rb_node newtree, node;
210 WBinding *b, *b2;
212 if(bindmap->bindings==NULL)
213 return;
215 newtree=make_rb();
217 if(newtree==NULL){
218 warn_err();
219 return;
222 FOR_ALL_BINDINGS(b, node, bindmap->bindings){
223 b2=ALLOC(WBinding);
224 if(b2==NULL){
225 warn_err();
226 free_map(newtree);
227 return;
230 *b2=*b;
232 if(b->act==BINDING_KEYPRESS){
233 for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
234 rbind_binding_removed(rbind, b, bindmap);
235 b2->kcb=XKeysymToKeycode(ioncore_g.dpy, b->ksb);
238 if(!rb_insertg(newtree, b2, b2, (Rb_compfn*)compare_bindings)){
239 warn_err();
240 free(b2);
241 free_map(newtree);
242 return;
246 free_map(bindmap->bindings);
247 bindmap->bindings=newtree;
249 FOR_ALL_BINDINGS(b, node, bindmap->bindings){
250 if(b->act!=BINDING_KEYPRESS)
251 continue;
252 for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
253 rbind_binding_added(rbind, b, bindmap);
254 if(b->submap!=NULL)
255 bindmap_refresh(b->submap);
260 bool bindmap_add_binding(WBindmap *bindmap, const WBinding *b)
262 WRegBindingInfo *rbind=NULL;
263 WBinding *binding=NULL;
264 Rb_node node=NULL;
265 int found=0;
267 /* Handle adding the binding */
268 binding=ALLOC(WBinding);
270 if(binding==NULL){
271 warn_err();
272 return FALSE;
275 memcpy(binding, b, sizeof(*b));
277 node=rb_find_gkey_n(bindmap->bindings, binding,
278 (Rb_compfn*)compare_bindings, &found);
280 if(found){
281 if(!rb_insert_a(node, binding, binding)){
282 free(binding);
283 return FALSE;
285 do_destroy_binding((WBinding*)rb_val(node));
286 rb_delete_node(node);
287 bindmap->nbindings--;
288 }else{
289 if(!rb_insertg(bindmap->bindings, binding, binding,
290 (Rb_compfn*)compare_bindings)){
291 free(binding);
292 return FALSE;
296 bindmap->nbindings++;
298 for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
299 rbind_binding_added(rbind, binding, bindmap);
301 return TRUE;
305 bool bindmap_remove_binding(WBindmap *bindmap, const WBinding *b)
307 WRegBindingInfo *rbind=NULL;
308 WBinding *binding=NULL;
309 Rb_node node=NULL;
310 int found=0;
312 if(bindmap->bindings==NULL)
313 return FALSE;
315 node=rb_find_gkey_n(bindmap->bindings, b, (Rb_compfn*)compare_bindings,
316 &found);
318 if(!found)
319 return FALSE;
321 binding=(WBinding*)rb_val(node);
323 for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
324 rbind_binding_removed(rbind, binding, bindmap);
326 do_destroy_binding(binding);
327 rb_delete_node(node);
329 bindmap->nbindings--;
331 return TRUE;
335 void ioncore_init_bindings()
337 modmap=XGetModifierMapping(ioncore_g.dpy);
339 assert(modmap!=NULL);
341 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
342 lookup_evil_locks();
343 #endif
347 void ioncore_update_modmap()
349 XModifierKeymap *nm=XGetModifierMapping(ioncore_g.dpy);
351 if(nm!=NULL){
352 XFreeModifiermap(modmap);
353 modmap=nm;
358 /* */
361 void binding_grab_on(const WBinding *binding, Window win)
363 if(binding->act==BINDING_KEYPRESS && binding->kcb!=0){
364 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
365 XGrabKey(ioncore_g.dpy, binding->kcb, binding->state, win,
366 True, GrabModeAsync, GrabModeAsync);
367 #else
368 evil_grab_key(ioncore_g.dpy, binding->kcb, binding->state, win,
369 True, GrabModeAsync, GrabModeAsync);
370 #endif
373 if(binding->act!=BINDING_BUTTONPRESS &&
374 binding->act!=BINDING_BUTTONCLICK &&
375 binding->act!=BINDING_BUTTONDBLCLICK &&
376 binding->act!=BINDING_BUTTONMOTION)
377 return;
379 if(binding->state==0 || binding->area!=0)
380 return;
382 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
383 XGrabButton(ioncore_g.dpy, binding->kcb, binding->state, win,
384 True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync,
385 None, None);
386 #else
387 evil_grab_button(ioncore_g.dpy, binding->kcb, binding->state, win,
388 True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync,
389 None, None);
390 #endif
394 void binding_ungrab_on(const WBinding *binding, Window win)
396 if(binding->act==BINDING_KEYPRESS && binding->kcb!=0){
397 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
398 XUngrabKey(ioncore_g.dpy, binding->kcb, binding->state, win);
399 #else
400 evil_ungrab_key(ioncore_g.dpy, binding->kcb, binding->state, win);
401 #endif
404 if(binding->act!=BINDING_BUTTONPRESS &&
405 binding->act!=BINDING_BUTTONCLICK &&
406 binding->act!=BINDING_BUTTONDBLCLICK &&
407 binding->act!=BINDING_BUTTONMOTION)
408 return;
410 if(binding->state==0 || binding->area!=0)
411 return;
413 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
414 XUngrabButton(ioncore_g.dpy, binding->kcb, binding->state, win);
415 #else
416 evil_ungrab_button(ioncore_g.dpy, binding->kcb, binding->state, win);
417 #endif
421 /* */
424 static WBinding *search_binding(WBindmap *bindmap, WBinding *binding)
426 Rb_node node;
427 int found=0;
429 if(bindmap->bindings==NULL)
430 return NULL;
432 node=rb_find_gkey_n(bindmap->bindings, binding,
433 (Rb_compfn*)compare_bindings, &found);
435 if(found==0)
436 return NULL;
438 return (WBinding*)rb_val(node);
442 static WBinding *search_binding_any(WBindmap *bindmap, WBinding *binding)
444 Rb_node node;
445 int found=0;
447 if(bindmap->bindings==NULL)
448 return NULL;
450 node=rb_find_gkey_n(bindmap->bindings, binding,
451 (Rb_compfn*)compare_bindings_any, &found);
453 if(found==0)
454 return NULL;
456 return (WBinding*)rb_val(node);
460 static WBinding *do_bindmap_lookup_binding(WBindmap *bindmap,
461 int act, uint state,
462 uint kcb, int area)
464 WBinding *binding, tmp;
466 if(bindmap->nbindings==0)
467 return NULL;
469 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
470 state&=~evilignoremask;
471 #endif
472 state&=KNOWN_MODIFIERS_MASK;
474 tmp.act=act;
475 tmp.kcb=kcb;
476 tmp.state=state;
477 tmp.area=area;
479 binding=search_binding(bindmap, &tmp);
481 if(BINDING_IS_PSEUDO(act)){
482 /* No use trying anything else */
483 return binding;
486 if(binding==NULL){
487 tmp.state=AnyModifier;
488 binding=search_binding(bindmap, &tmp);
490 if(binding==NULL){
491 tmp.state=state;
492 tmp.kcb=0;
493 tmp.ksb=(act==BINDING_KEYPRESS ? AnyKey : AnyButton);
495 binding=search_binding_any(bindmap, &tmp);
497 if(binding==NULL){
498 tmp.state=AnyModifier;
499 binding=search_binding_any(bindmap, &tmp);
504 return binding;
508 WBinding *bindmap_lookup_binding(WBindmap *bindmap,
509 int act, uint state, uint kcb)
511 return do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
515 WBinding *bindmap_lookup_binding_area(WBindmap *bindmap,
516 int act, uint state, uint kcb, int area)
518 WBinding *binding;
520 binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, area);
522 if(binding==NULL)
523 binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
525 return binding;
530 * A dirty hack to deal with (==ignore) evil locking modifier keys.
534 int ioncore_unmod(int state, int keycode)
536 int j;
538 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
539 state&=~evilignoremask;
540 #endif
541 state&=KNOWN_MODIFIERS_MASK;
543 for(j=0; j<N_MODS*modmap->max_keypermod; j++){
544 if(modmap->modifiermap[j]==keycode)
545 return state&~modmasks[j/modmap->max_keypermod];
548 return state;
552 int ioncore_modstate()
554 char keys[32];
555 int state=0;
556 int j;
558 XQueryKeymap(ioncore_g.dpy, keys);
560 for(j=0; j<N_MODS*modmap->max_keypermod; j++){
561 int a=(modmap->modifiermap[j]&7);
562 int b=(modmap->modifiermap[j]>>3);
563 if(b<32){
564 if(keys[b]&(1<<a))
565 state|=modmasks[j/modmap->max_keypermod];
569 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
570 state&=~evilignoremask;
571 #endif
572 state&=KNOWN_MODIFIERS_MASK;
574 return state;
578 bool ioncore_ismod(int keycode)
580 int j;
582 for(j=0; j<N_MODS*modmap->max_keypermod; j++){
583 if(modmap->modifiermap[j]==keycode)
584 return TRUE;
587 return FALSE;
591 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
593 static void lookup_evil_locks()
595 uint keycodes[N_LOOKUPEVIL];
596 int i, j;
598 for(i=0; i<N_LOOKUPEVIL; i++)
599 keycodes[i]=XKeysymToKeycode(ioncore_g.dpy, evillocks[i]);
601 for(j=0; j<N_MODS*modmap->max_keypermod; j++){
602 for(i=0; i<N_LOOKUPEVIL; i++){
603 if(keycodes[i]==None)
604 continue;
605 if(modmap->modifiermap[j]==keycodes[i]){
606 evillockmasks[i]=modmasks[j/modmap->max_keypermod];
607 evilignoremask|=evillockmasks[i];
614 static void evil_grab_key(Display *display, uint keycode, uint modifiers,
615 Window grab_window, bool owner_events,
616 int pointer_mode, int keyboard_mode)
618 uint mods;
619 int i, j;
621 XGrabKey(display, keycode, modifiers, grab_window, owner_events,
622 pointer_mode, keyboard_mode);
624 if(modifiers==AnyModifier)
625 return;
627 for(i=0; i<N_EVILLOCKS; i++){
628 if(evillockmasks[i]==0)
629 continue;
630 mods=modifiers;
631 for(j=i; j<N_EVILLOCKS; j++){
632 if(evillockmasks[j]==0)
633 continue;
634 mods|=evillockmasks[j];
635 XGrabKey(display, keycode, mods,
636 grab_window, owner_events, pointer_mode, keyboard_mode);
637 if(i==j)
638 continue;
639 XGrabKey(display, keycode,
640 modifiers|evillockmasks[i]|evillockmasks[j],
641 grab_window, owner_events, pointer_mode, keyboard_mode);
647 static void evil_grab_button(Display *display, uint button, uint modifiers,
648 Window grab_window, bool owner_events,
649 uint event_mask, int pointer_mode,
650 int keyboard_mode, Window confine_to,
651 Cursor cursor)
653 uint mods;
654 int i, j;
656 XGrabButton(display, button, modifiers,
657 grab_window, owner_events, event_mask, pointer_mode,
658 keyboard_mode, confine_to, cursor);
660 if(modifiers==AnyModifier)
661 return;
663 for(i=0; i<N_EVILLOCKS; i++){
664 if(evillockmasks[i]==0)
665 continue;
666 mods=modifiers;
667 for(j=i; j<N_EVILLOCKS; j++){
668 if(evillockmasks[j]==0)
669 continue;
670 mods|=evillockmasks[j];
671 XGrabButton(display, button, mods,
672 grab_window, owner_events, event_mask, pointer_mode,
673 keyboard_mode, confine_to, cursor);
674 if(i==j)
675 continue;
676 XGrabButton(display, button,
677 modifiers|evillockmasks[i]|evillockmasks[j],
678 grab_window, owner_events, event_mask, pointer_mode,
679 keyboard_mode, confine_to, cursor);
685 static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
686 Window grab_window)
688 uint mods;
689 int i, j;
691 XUngrabKey(display, keycode, modifiers, grab_window);
693 if(modifiers==AnyModifier)
694 return;
696 for(i=0; i<N_EVILLOCKS; i++){
697 if(evillockmasks[i]==0)
698 continue;
699 mods=modifiers;
700 for(j=i; j<N_EVILLOCKS; j++){
701 if(evillockmasks[j]==0)
702 continue;
703 mods|=evillockmasks[j];
704 XUngrabKey(display, keycode, mods, grab_window);
705 if(i==j)
706 continue;
707 XUngrabKey(display, keycode,
708 modifiers|evillockmasks[i]|evillockmasks[j],
709 grab_window);
715 static void evil_ungrab_button(Display *display, uint button, uint modifiers,
716 Window grab_window)
718 uint mods;
719 int i, j;
721 XUngrabButton(display, button, modifiers, grab_window);
723 if(modifiers==AnyModifier)
724 return;
726 for(i=0; i<N_EVILLOCKS; i++){
727 if(evillockmasks[i]==0)
728 continue;
729 mods=modifiers;
730 for(j=i; j<N_EVILLOCKS; j++){
731 if(evillockmasks[j]==0)
732 continue;
733 mods|=evillockmasks[j];
734 XUngrabButton(display, button, mods, grab_window);
735 if(i==j)
736 continue;
737 XUngrabButton(display, button,
738 modifiers|evillockmasks[i]|evillockmasks[j],
739 grab_window);
745 #endif /* CF_HACK_IGNORE_EVIL_LOCKS */