]> git.decadent.org.uk Git - ion3.git/blob - ioncore/binding.c
Update cfg_kludge_flash for Flash 10
[ion3.git] / ioncore / binding.c
1 /*
2  * ion/ioncore/binding.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
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_any(const WBinding *a, const WBinding *b)
99 {
100     int r=compare_bindings(a, b);
101     
102     if(r==0)
103         r=CVAL(a, b, ksb);
104     
105     return r;
106 }
107                     
108 #undef CVAL
109
110
111 bool init_bindmap(WBindmap *bindmap)
112 {
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;
120     }
121     return TRUE;
122 }
123
124
125 WBindmap *create_bindmap()
126 {
127     WBindmap *bindmap=ALLOC(WBindmap);
128     
129     if(bindmap==NULL){
130         warn_err();
131         return NULL;
132     }
133     
134     if(!init_bindmap(bindmap)){
135         free(bindmap);
136         return NULL;
137     }
138     
139     return bindmap;
140 }
141
142
143 void binding_deinit(WBinding *binding)
144 {
145     if(binding->submap!=NULL){
146         bindmap_destroy(binding->submap);
147         binding->submap=NULL;
148     }
149
150     binding->func=extl_unref_fn(binding->func);
151 }
152
153
154 static void do_destroy_binding(WBinding *binding)
155 {
156     assert(binding!=NULL);
157     binding_deinit(binding);
158     free(binding);
159 }
160
161
162 static void bindmap_deinit(WBindmap *bindmap)
163 {
164     WBinding *b=NULL;
165     Rb_node node=NULL;
166
167     while(bindmap->rbind_list!=NULL){
168         region_remove_bindmap(bindmap->rbind_list->reg,
169                               bindmap);
170     }
171         
172     if(bindmap->bindings==NULL)
173         return;
174     
175     FOR_ALL_BINDINGS(b, node, bindmap->bindings){
176         do_destroy_binding((WBinding*)rb_val(node));
177         bindmap->nbindings--;
178     }
179
180     assert(bindmap->nbindings==0);
181     
182     rb_free_tree(bindmap->bindings);
183     bindmap->bindings=NULL;
184 }
185
186
187 void bindmap_destroy(WBindmap *bindmap)
188 {
189     bindmap_deinit(bindmap);
190     free(bindmap);
191 }
192
193
194 static void free_map(Rb_node map)
195 {
196     Rb_node node;
197     WBinding *b;
198     
199     FOR_ALL_BINDINGS(b, node, map)
200         free(b);
201     
202     rb_free_tree(map);
203 }
204
205
206 void bindmap_refresh(WBindmap *bindmap)
207 {
208     WRegBindingInfo *rbind;
209     Rb_node newtree, node;
210     WBinding *b, *b2;
211     
212     if(bindmap->bindings==NULL)
213         return;
214     
215     newtree=make_rb();
216     
217     if(newtree==NULL){
218         warn_err();
219         return;
220     }
221     
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;
228         }
229         
230         *b2=*b;
231
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);
236         }
237         
238         if(!rb_insertg(newtree, b2, b2, (Rb_compfn*)compare_bindings)){
239             warn_err();
240             free(b2);
241             free_map(newtree);
242             return;
243         }
244     }
245
246     free_map(bindmap->bindings);
247     bindmap->bindings=newtree;
248
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);
256     }
257 }
258
259
260 bool bindmap_add_binding(WBindmap *bindmap, const WBinding *b)
261 {
262     WRegBindingInfo *rbind=NULL;
263     WBinding *binding=NULL;
264     Rb_node node=NULL;
265     int found=0;
266     
267     /* Handle adding the binding */
268     binding=ALLOC(WBinding);
269     
270     if(binding==NULL){
271         warn_err();
272         return FALSE;
273     }
274     
275     memcpy(binding, b, sizeof(*b));
276     
277     node=rb_find_gkey_n(bindmap->bindings, binding,
278                         (Rb_compfn*)compare_bindings, &found);
279     
280     if(found){
281         if(!rb_insert_a(node, binding, binding)){
282             free(binding);
283             return FALSE;
284         }
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;
293         }
294     }
295
296     bindmap->nbindings++;
297     
298     for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
299         rbind_binding_added(rbind, binding, bindmap);
300
301     return TRUE;
302 }
303
304
305 bool bindmap_remove_binding(WBindmap *bindmap, const WBinding *b)
306 {
307     WRegBindingInfo *rbind=NULL;
308     WBinding *binding=NULL;
309     Rb_node node=NULL;
310     int found=0;
311     
312     if(bindmap->bindings==NULL)
313         return FALSE;
314     
315     node=rb_find_gkey_n(bindmap->bindings, b, (Rb_compfn*)compare_bindings,
316                         &found);
317     
318     if(!found)
319         return FALSE;
320     
321     binding=(WBinding*)rb_val(node);
322     
323     for(rbind=bindmap->rbind_list; rbind!=NULL; rbind=rbind->bm_next)
324         rbind_binding_removed(rbind, binding, bindmap);
325
326     do_destroy_binding(binding);
327     rb_delete_node(node);
328     
329     bindmap->nbindings--;
330
331     return TRUE;
332 }
333
334
335 void ioncore_init_bindings()
336 {
337     modmap=XGetModifierMapping(ioncore_g.dpy);
338     
339     assert(modmap!=NULL);
340
341 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
342     lookup_evil_locks();
343 #endif
344 }
345
346
347 void ioncore_update_modmap()
348 {
349     XModifierKeymap *nm=XGetModifierMapping(ioncore_g.dpy);
350     
351     if(nm!=NULL){
352         XFreeModifiermap(modmap);
353         modmap=nm;
354     }
355 }
356
357
358 /* */
359
360
361 void binding_grab_on(const WBinding *binding, Window win)
362 {
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
371     }
372     
373     if(binding->act!=BINDING_BUTTONPRESS &&
374        binding->act!=BINDING_BUTTONCLICK &&
375        binding->act!=BINDING_BUTTONDBLCLICK &&
376        binding->act!=BINDING_BUTTONMOTION)
377         return;
378     
379     if(binding->state==0 || binding->area!=0)
380         return;
381     
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
391 }
392
393
394 void binding_ungrab_on(const WBinding *binding, Window win)
395 {
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
402     }
403     
404     if(binding->act!=BINDING_BUTTONPRESS &&
405        binding->act!=BINDING_BUTTONCLICK &&
406        binding->act!=BINDING_BUTTONDBLCLICK &&
407        binding->act!=BINDING_BUTTONMOTION)
408         return;
409     
410     if(binding->state==0 || binding->area!=0)
411         return;
412
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
418 }
419
420
421 /* */
422
423
424 static WBinding *search_binding(WBindmap *bindmap, WBinding *binding)
425 {
426     Rb_node node;
427     int found=0;
428
429     if(bindmap->bindings==NULL)
430         return NULL;
431     
432     node=rb_find_gkey_n(bindmap->bindings, binding,
433                         (Rb_compfn*)compare_bindings, &found);
434     
435     if(found==0)
436         return NULL;
437     
438     return (WBinding*)rb_val(node);
439 }
440
441
442 static WBinding *search_binding_any(WBindmap *bindmap, WBinding *binding)
443 {
444     Rb_node node;
445     int found=0;
446
447     if(bindmap->bindings==NULL)
448         return NULL;
449     
450     node=rb_find_gkey_n(bindmap->bindings, binding,
451                         (Rb_compfn*)compare_bindings_any, &found);
452     
453     if(found==0)
454         return NULL;
455     
456     return (WBinding*)rb_val(node);
457 }
458
459
460 static WBinding *do_bindmap_lookup_binding(WBindmap *bindmap,
461                                            int act, uint state, 
462                                            uint kcb, int area)
463 {
464     WBinding *binding, tmp;
465
466     if(bindmap->nbindings==0)
467         return NULL;
468
469 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
470     state&=~evilignoremask;
471 #endif
472     state&=KNOWN_MODIFIERS_MASK;
473     
474     tmp.act=act;
475     tmp.kcb=kcb;
476     tmp.state=state;
477     tmp.area=area;
478     
479     binding=search_binding(bindmap, &tmp);
480
481     if(BINDING_IS_PSEUDO(act)){
482         /* No use trying anything else */
483         return binding;
484     }
485     
486     if(binding==NULL){
487         tmp.state=AnyModifier;
488         binding=search_binding(bindmap, &tmp);
489
490         if(binding==NULL){
491             tmp.state=state;
492             tmp.kcb=0;
493             tmp.ksb=(act==BINDING_KEYPRESS ? AnyKey : AnyButton);
494             
495             binding=search_binding_any(bindmap, &tmp);
496
497             if(binding==NULL){
498                 tmp.state=AnyModifier;
499                 binding=search_binding_any(bindmap, &tmp);
500             }
501         }
502     }
503                 
504     return binding;
505 }
506
507
508 WBinding *bindmap_lookup_binding(WBindmap *bindmap,
509                                  int act, uint state, uint kcb)
510 {
511     return do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
512 }
513
514
515 WBinding *bindmap_lookup_binding_area(WBindmap *bindmap,
516                                       int act, uint state, uint kcb, int area)
517 {
518     WBinding *binding;
519     
520     binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, area);
521     
522     if(binding==NULL)
523         binding=do_bindmap_lookup_binding(bindmap, act, state, kcb, 0);
524     
525     return binding;
526 }
527
528     
529 /*
530  * A dirty hack to deal with (==ignore) evil locking modifier keys.
531  */
532
533
534 int ioncore_unmod(int state, int keycode)
535 {
536     int j;
537     
538 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
539     state&=~evilignoremask;
540 #endif
541     state&=KNOWN_MODIFIERS_MASK;
542
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];
546     }
547     
548     return state;
549 }
550
551
552 int ioncore_modstate()
553 {
554         char keys[32];
555         int state=0;
556         int j;
557         
558         XQueryKeymap(ioncore_g.dpy, keys);
559     
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];
566                 }
567     }
568
569 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
570     state&=~evilignoremask;
571 #endif
572     state&=KNOWN_MODIFIERS_MASK;
573     
574     return state;
575 }
576
577
578 bool ioncore_ismod(int keycode)
579 {
580     int j;
581     
582     for(j=0; j<N_MODS*modmap->max_keypermod; j++){
583         if(modmap->modifiermap[j]==keycode)
584             return TRUE;
585     }
586     
587     return FALSE;
588 }
589     
590
591 #ifdef CF_HACK_IGNORE_EVIL_LOCKS
592
593 static void lookup_evil_locks()
594 {
595     uint keycodes[N_LOOKUPEVIL];
596     int i, j;
597     
598     for(i=0; i<N_LOOKUPEVIL; i++)
599         keycodes[i]=XKeysymToKeycode(ioncore_g.dpy, evillocks[i]);
600     
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];
608             }
609         }
610     }
611 }
612
613
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)
617 {
618     uint mods;
619     int i, j;
620     
621     XGrabKey(display, keycode, modifiers, grab_window, owner_events,
622              pointer_mode, keyboard_mode);
623
624     if(modifiers==AnyModifier)
625         return;
626     
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);
642         }
643     }    
644 }
645
646
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)
652 {
653     uint mods;
654     int i, j;
655
656     XGrabButton(display, button, modifiers,
657                 grab_window, owner_events, event_mask, pointer_mode,
658                 keyboard_mode, confine_to, cursor);
659
660     if(modifiers==AnyModifier)
661         return;
662     
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);
680         }            
681     }
682 }
683
684
685 static void evil_ungrab_key(Display *display, uint keycode, uint modifiers,
686                             Window grab_window)
687 {
688     uint mods;
689     int i, j;
690     
691     XUngrabKey(display, keycode, modifiers, grab_window);
692
693     if(modifiers==AnyModifier)
694         return;
695     
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);
710         }
711     }    
712 }
713
714
715 static void evil_ungrab_button(Display *display, uint button, uint modifiers,
716                                Window grab_window)
717 {
718     uint mods;
719     int i, j;
720     
721     XUngrabButton(display, button, modifiers, grab_window);
722
723     if(modifiers==AnyModifier)
724         return;
725     
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);
740         }            
741     }
742     
743 }
744
745 #endif /* CF_HACK_IGNORE_EVIL_LOCKS */
746