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