]> git.decadent.org.uk Git - ion3.git/blob - ioncore/binding.c
7b80d111776d7572a29d9d8afc7784b2dd17badd
[ion3.git] / ioncore / binding.c
1 /*
2  * ion/ioncore/binding.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
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.
10  */
11
12 #include <string.h>
13 #include "common.h"
14 #include "event.h"
15 #include "binding.h"
16 #include "global.h"
17 #include <libtu/objp.h>
18 #include "regbind.h"
19 #include <libextl/extl.h>
20
21
22 #ifndef CF_NO_LOCK_HACK
23 #define CF_HACK_IGNORE_EVIL_LOCKS
24 #endif
25
26 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
27 #define XK_MISCELLANY
28 #include <X11/keysymdef.h>
29 #endif
30
31
32 /* */
33
34
35 #define N_MODS 8
36
37 static const uint modmasks[N_MODS]={
38     ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask,
39     Mod4Mask, Mod5Mask
40 };
41
42 static XModifierKeymap *modmap=NULL;
43
44 #define KNOWN_MODIFIERS_MASK (ShiftMask|LockMask|ControlMask|Mod1Mask|\
45                               Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)
46
47 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
48
49 #define N_EVILLOCKS 3
50 #define N_LOOKUPEVIL 2
51
52 static uint evillockmasks[N_EVILLOCKS]={
53      0, 0, LockMask
54 };
55
56 static const KeySym evillocks[N_LOOKUPEVIL]={
57     XK_Num_Lock, XK_Scroll_Lock
58 };
59
60 static uint evilignoremask=LockMask;
61
62 static void lookup_evil_locks();
63
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);
67
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,
72                              Cursor cursor);
73
74 static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
75                             Window grab_window);
76
77 static void evil_ungrab_button(Display *display, uint button, uint modifiers,
78                                Window grab_window);
79
80 #endif
81
82
83 #define CVAL(A, B, V) ( A->V < B->V ? -1 : (A->V > B->V ? 1 : 0))
84
85 static int compare_bindings(const WBinding *a, const WBinding *b)
86 {
87     int r=CVAL(a, b, act);
88     if(r==0){
89         r=CVAL(a, b, kcb);
90         if(r==0){
91             r=CVAL(a, b, state);
92             if(r==0){
93                 r=CVAL(a, b, area);
94             }
95         }
96     }
97     return r;
98 }
99
100 /* This is only used for searching AnyKey etc. */
101 static int compare_bindings_ksb(const WBinding *a, const WBinding *b)
102 {
103     int r=CVAL(a, b, act);
104     if(r==0){
105         r=CVAL(a, b, ksb);
106         if(r==0){
107             r=CVAL(a, b, state);
108             if(r==0){
109                 r=CVAL(a, b, area);
110             }
111         }
112     }
113     return r;
114 }
115                     
116 #undef CVAL
117
118
119 bool init_bindmap(WBindmap *bindmap)
120 {
121     bindmap->rbind_list=NULL;
122     bindmap->areamap=NULL;
123     bindmap->nbindings=0;
124     bindmap->bindings=make_rb();
125     if(bindmap->bindings==NULL){
126         warn_err();
127         return FALSE;
128     }
129     return TRUE;
130 }
131
132
133 WBindmap *create_bindmap()
134 {
135     WBindmap *bindmap=ALLOC(WBindmap);
136     
137     if(bindmap==NULL){
138         warn_err();
139         return NULL;
140     }
141     
142     if(!init_bindmap(bindmap)){
143         free(bindmap);
144         return NULL;
145     }
146     
147     return bindmap;
148 }
149
150
151 void binding_deinit(WBinding *binding)
152 {
153     if(binding->submap!=NULL){
154         bindmap_destroy(binding->submap);
155         binding->submap=NULL;
156     }
157
158     binding->func=extl_unref_fn(binding->func);
159 }
160
161
162 static void do_destroy_binding(WBinding *binding)
163 {
164     assert(binding!=NULL);
165     binding_deinit(binding);
166     free(binding);
167 }
168
169
170 static void bindmap_deinit(WBindmap *bindmap)
171 {
172     WBinding *b=NULL;
173     Rb_node node=NULL;
174
175     while(bindmap->rbind_list!=NULL){
176         region_remove_bindmap(bindmap->rbind_list->reg,
177                               bindmap);
178     }
179         
180     if(bindmap->bindings==NULL)
181         return;
182     
183     FOR_ALL_BINDINGS(b, node, bindmap->bindings){
184         do_destroy_binding((WBinding*)rb_val(node));
185         bindmap->nbindings--;
186     }
187
188     assert(bindmap->nbindings==0);
189     
190     rb_free_tree(bindmap->bindings);
191     bindmap->bindings=NULL;
192 }
193
194
195 void bindmap_destroy(WBindmap *bindmap)
196 {
197     bindmap_deinit(bindmap);
198     free(bindmap);
199 }
200
201
202 static void free_map(Rb_node map)
203 {
204     Rb_node node;
205     WBinding *b;
206     
207     FOR_ALL_BINDINGS(b, node, map)
208         free(b);
209     
210     rb_free_tree(map);
211 }
212
213
214 void bindmap_refresh(WBindmap *bindmap)
215 {
216     WRegBindingInfo *rbind;
217     Rb_node newtree, node;
218     WBinding *b, *b2;
219     
220     if(bindmap->bindings==NULL)
221         return;
222     
223     newtree=make_rb();
224     
225     if(newtree==NULL){
226         warn_err();
227         return;
228     }
229     
230     FOR_ALL_BINDINGS(b, node, bindmap->bindings){
231         b2=ALLOC(WBinding);
232         if(b2==NULL){
233             warn_err();
234             free_map(newtree);
235             return;
236         }
237         
238         *b2=*b;
239
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);
244         }
245         
246         if(!rb_insertg(newtree, b2, b2, (Rb_compfn*)compare_bindings)){
247             warn_err();
248             free(b2);
249             free_map(newtree);
250             return;
251         }
252     }
253
254     free_map(bindmap->bindings);
255     bindmap->bindings=newtree;
256
257     FOR_ALL_BINDINGS(b, node, bindmap->bindings){
258         if(b->act!=BINDING_KEYPRESS)
259             continue;
260         for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
261             rbind_binding_added(rbind, b, bindmap);
262         if(b->submap!=NULL)
263             bindmap_refresh(b->submap);
264     }
265 }
266
267
268 bool bindmap_add_binding(WBindmap *bindmap, const WBinding *b)
269 {
270     WRegBindingInfo *rbind=NULL;
271     WBinding *binding=NULL;
272     Rb_node node=NULL;
273     int found=0;
274     
275     /* Handle adding the binding */
276     binding=ALLOC(WBinding);
277     
278     if(binding==NULL){
279         warn_err();
280         return FALSE;
281     }
282     
283     memcpy(binding, b, sizeof(*b));
284     
285     node=rb_find_gkey_n(bindmap->bindings, binding,
286                         (Rb_compfn*)compare_bindings, &found);
287     
288     if(found){
289         if(!rb_insert_a(node, binding, binding)){
290             free(binding);
291             return FALSE;
292         }
293         do_destroy_binding((WBinding*)rb_val(node));
294         rb_delete_node(node);
295         bindmap->nbindings--;
296     }else{
297         if(!rb_insertg(bindmap->bindings, binding, binding, 
298                        (Rb_compfn*)compare_bindings)){
299             free(binding);
300             return FALSE;
301         }
302     }
303
304     bindmap->nbindings++;
305     
306     for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
307         rbind_binding_added(rbind, binding, bindmap);
308
309     return TRUE;
310 }
311
312
313 bool bindmap_remove_binding(WBindmap *bindmap, const WBinding *b)
314 {
315     WRegBindingInfo *rbind=NULL;
316     WBinding *binding=NULL;
317     Rb_node node=NULL;
318     int found=0;
319     
320     if(bindmap->bindings==NULL)
321         return FALSE;
322     
323     node=rb_find_gkey_n(bindmap->bindings, b, (Rb_compfn*)compare_bindings,
324                         &found);
325     
326     if(!found)
327         return FALSE;
328     
329     binding=(WBinding*)rb_val(node);
330     
331     for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
332         rbind_binding_removed(rbind, binding, bindmap);
333
334     do_destroy_binding(binding);
335     rb_delete_node(node);
336     
337     bindmap->nbindings--;
338
339     return TRUE;
340 }
341
342
343 void ioncore_init_bindings()
344 {
345     modmap=XGetModifierMapping(ioncore_g.dpy);
346     
347     assert(modmap!=NULL);
348
349 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
350     lookup_evil_locks();
351 #endif
352 }
353
354
355 void ioncore_update_modmap()
356 {
357     XModifierKeymap *nm=XGetModifierMapping(ioncore_g.dpy);
358     
359     if(nm!=NULL){
360         XFreeModifiermap(modmap);
361         modmap=nm;
362     }
363 }
364
365
366 /* */
367
368
369 void binding_grab_on(const WBinding *binding, Window win)
370 {
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);
375 #else
376         evil_grab_key(ioncore_g.dpy, binding->kcb, binding->state, win,
377                       True, GrabModeAsync, GrabModeAsync);
378 #endif
379     }
380     
381     if(binding->act!=BINDING_BUTTONPRESS &&
382        binding->act!=BINDING_BUTTONCLICK &&
383        binding->act!=BINDING_BUTTONDBLCLICK &&
384        binding->act!=BINDING_BUTTONMOTION)
385         return;
386     
387     if(binding->state==0)
388         return;
389     
390 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
391     XGrabButton(ioncore_g.dpy, binding->kcb, binding->state, win,
392                 True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync,
393                 None, None);
394 #else
395     evil_grab_button(ioncore_g.dpy, binding->kcb, binding->state, win,
396                      True, IONCORE_EVENTMASK_PTRGRAB, GrabModeAsync, GrabModeAsync,
397                      None, None);
398 #endif
399 }
400
401
402 void binding_ungrab_on(const WBinding *binding, Window win)
403 {
404     if(binding->act==BINDING_KEYPRESS){
405 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
406         XUngrabKey(ioncore_g.dpy, binding->kcb, binding->state, win);
407 #else
408         evil_ungrab_key(ioncore_g.dpy, binding->kcb, binding->state, win);
409 #endif
410     }
411     
412     if(binding->act!=BINDING_BUTTONPRESS &&
413        binding->act!=BINDING_BUTTONCLICK &&
414        binding->act!=BINDING_BUTTONDBLCLICK &&
415        binding->act!=BINDING_BUTTONMOTION)
416         return;
417     
418     if(binding->state==0)
419         return;
420
421 #ifndef CF_HACK_IGNORE_EVIL_LOCKS
422     XUngrabButton(ioncore_g.dpy, binding->kcb, binding->state, win);
423 #else
424     evil_ungrab_button(ioncore_g.dpy, binding->kcb, binding->state, win);
425 #endif
426 }
427
428
429 /* */
430
431
432 static WBinding *search_binding(WBindmap *bindmap, WBinding *binding)
433 {
434     Rb_node node;
435     int found=0;
436
437     if(bindmap->bindings==NULL)
438         return NULL;
439     
440     node=rb_find_gkey_n(bindmap->bindings, binding,
441                         (Rb_compfn*)compare_bindings, &found);
442     
443     if(found==0)
444         return NULL;
445     
446     return (WBinding*)rb_val(node);
447 }
448
449
450 static WBinding *search_binding_ksb(WBindmap *bindmap, WBinding *binding)
451 {
452     Rb_node node;
453     int found=0;
454
455     if(bindmap->bindings==NULL)
456         return NULL;
457     
458     node=rb_find_gkey_n(bindmap->bindings, binding,
459                         (Rb_compfn*)compare_bindings_ksb, &found);
460     
461     if(found==0)
462         return NULL;
463     
464     return (WBinding*)rb_val(node);
465 }
466
467
468 static WBinding *do_bindmap_lookup_binding(WBindmap *bindmap,
469                                            int act, uint state, 
470                                            uint kcb, int area)
471 {
472     WBinding *binding, tmp;
473
474     if(bindmap->nbindings==0)
475         return NULL;
476
477 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
478     state&=~evilignoremask;
479 #endif
480     state&=KNOWN_MODIFIERS_MASK;
481     
482     tmp.act=act;
483     tmp.kcb=kcb;
484     tmp.state=state;
485     tmp.area=area;
486     
487     binding=search_binding(bindmap, &tmp);
488
489     if(binding==NULL){
490         tmp.state=AnyModifier;
491         binding=search_binding(bindmap, &tmp);
492
493         if(binding==NULL){
494             tmp.state=state;
495             tmp.ksb=(act==BINDING_KEYPRESS ? AnyKey : AnyButton);
496             
497             binding=search_binding_ksb(bindmap, &tmp);
498
499             if(binding==NULL){
500                 tmp.state=AnyModifier;
501                 binding=search_binding_ksb(bindmap, &tmp);
502             }
503         }
504     }
505                 
506     return binding;
507 }
508
509
510 WBinding *bindmap_lookup_binding(WBindmap *bindmap,
511                                  int act, uint state, uint kcb)
512 {
513     return do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
514 }
515
516
517 WBinding *bindmap_lookup_binding_area(WBindmap *bindmap,
518                                       int act, uint state, uint kcb, int area)
519 {
520     WBinding *binding;
521     
522     binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, area);
523     
524     if(binding==NULL)
525         binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
526     
527     return binding;
528 }
529
530     
531 /*
532  * A dirty hack to deal with (==ignore) evil locking modifier keys.
533  */
534
535
536 int ioncore_unmod(int state, int keycode)
537 {
538     int j;
539     
540 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
541     state&=~evilignoremask;
542 #endif
543
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];
547     }
548     
549     return state;
550 }
551
552
553 int ioncore_modstate()
554 {
555         char keys[32];
556         int state=0;
557         int j;
558         
559         XQueryKeymap(ioncore_g.dpy, keys);
560     
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);
564                 if(b<32){
565                         if(keys[b]&(1<<a))
566                                 state|=modmasks[j/modmap->max_keypermod];
567                 }
568     }
569
570 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
571     state&=~evilignoremask;
572 #endif
573     return state;
574 }
575
576
577 bool ioncore_ismod(int keycode)
578 {
579     int j;
580     
581     for(j=0; j<N_MODS*modmap->max_keypermod; j++){
582         if(modmap->modifiermap[j]==keycode)
583             return TRUE;
584     }
585     
586     return FALSE;
587 }
588     
589
590 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
591
592 static void lookup_evil_locks()
593 {
594     uint keycodes[N_LOOKUPEVIL];
595     int i, j;
596     
597     for(i=0; i<N_LOOKUPEVIL; i++)
598         keycodes[i]=XKeysymToKeycode(ioncore_g.dpy, evillocks[i]);
599     
600     for(j=0; j<N_MODS*modmap->max_keypermod; j++){
601         for(i=0; i<N_LOOKUPEVIL; i++){
602             if(keycodes[i]==None)
603                 continue;
604             if(modmap->modifiermap[j]==keycodes[i]){
605                 evillockmasks[i]=modmasks[j/modmap->max_keypermod];
606                 evilignoremask|=evillockmasks[i];
607             }
608         }
609     }
610 }
611
612
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)
616 {
617     uint mods;
618     int i, j;
619     
620     XGrabKey(display, keycode, modifiers, grab_window, owner_events,
621              pointer_mode, keyboard_mode);
622
623     if(modifiers==AnyModifier)
624         return;
625     
626     for(i=0; i<N_EVILLOCKS; i++){
627         if(evillockmasks[i]==0)
628             continue;
629         mods=modifiers;
630         for(j=i; j<N_EVILLOCKS; j++){
631             if(evillockmasks[j]==0)
632                 continue;            
633             mods|=evillockmasks[j];            
634             XGrabKey(display, keycode, mods,
635                      grab_window, owner_events, pointer_mode, keyboard_mode);
636             if(i==j)
637                 continue;
638             XGrabKey(display, keycode,
639                      modifiers|evillockmasks[i]|evillockmasks[j],
640                      grab_window, owner_events, pointer_mode, keyboard_mode);
641         }
642     }    
643 }
644
645
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,
650                              Cursor cursor)
651 {
652     uint mods;
653     int i, j;
654
655     XGrabButton(display, button, modifiers,
656                 grab_window, owner_events, event_mask, pointer_mode,
657                 keyboard_mode, confine_to, cursor);
658
659     if(modifiers==AnyModifier)
660         return;
661     
662     for(i=0; i<N_EVILLOCKS; i++){
663         if(evillockmasks[i]==0)
664             continue;
665         mods=modifiers;
666         for(j=i; j<N_EVILLOCKS; j++){            
667             if(evillockmasks[j]==0)
668                 continue;            
669             mods|=evillockmasks[j];            
670             XGrabButton(display, button, mods,
671                         grab_window, owner_events, event_mask, pointer_mode,
672                         keyboard_mode, confine_to, cursor);
673             if(i==j)
674                 continue;
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);
679         }            
680     }
681 }
682
683
684 static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
685                             Window grab_window)
686 {
687     uint mods;
688     int i, j;
689     
690     XUngrabKey(display, keycode, modifiers, grab_window);
691
692     if(modifiers==AnyModifier)
693         return;
694     
695     for(i=0; i<N_EVILLOCKS; i++){
696         if(evillockmasks[i]==0)
697             continue;
698         mods=modifiers;
699         for(j=i; j<N_EVILLOCKS; j++){
700             if(evillockmasks[j]==0)
701                 continue;            
702             mods|=evillockmasks[j];            
703             XUngrabKey(display, keycode, mods, grab_window);
704             if(i==j)
705                 continue;
706             XUngrabKey(display, keycode,
707                        modifiers|evillockmasks[i]|evillockmasks[j],
708                        grab_window);
709         }
710     }    
711 }
712
713
714 static void evil_ungrab_button(Display *display, uint button, uint modifiers,
715                                Window grab_window)
716 {
717     uint mods;
718     int i, j;
719     
720     XUngrabButton(display, button, modifiers, grab_window);
721
722     if(modifiers==AnyModifier)
723         return;
724     
725     for(i=0; i<N_EVILLOCKS; i++){
726         if(evillockmasks[i]==0)
727             continue;
728         mods=modifiers;
729         for(j=i; j<N_EVILLOCKS; j++){            
730             if(evillockmasks[j]==0)
731                 continue;            
732             mods|=evillockmasks[j];            
733             XUngrabButton(display, button, mods, grab_window);
734             if(i==j)
735                 continue;
736             XUngrabButton(display, button,
737                           modifiers|evillockmasks[i]|evillockmasks[j], 
738                           grab_window);
739         }            
740     }
741     
742 }
743
744 #endif /* CF_HACK_IGNORE_EVIL_LOCKS */
745