]> git.decadent.org.uk Git - ion3.git/blob - ioncore/conf-bindings.c
56d679e6dd54458a4e4b305bb203e0f6191f3bc4
[ion3.git] / ioncore / conf-bindings.c
1 /*
2  * ion/ioncore/conf-bindings.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
14 #define XK_MISCELLANY
15 #include <X11/keysymdef.h>
16
17 #include <libtu/map.h>
18
19 #include "common.h"
20 #include "binding.h"
21 #include <libextl/readconfig.h>
22 #include "global.h"
23 #include <libextl/extl.h>
24 #include "conf-bindings.h"
25 #include "bindmaps.h"
26
27
28 /*{{{ parse_keybut */
29
30
31 #define MOD5_NDX 7
32
33 static StringIntMap state_map[]={
34     {"Shift",       ShiftMask},
35     {"Lock",        LockMask},
36     {"Control",     ControlMask},
37     {"Mod1",        Mod1Mask},
38     {"Mod2",        Mod2Mask},
39     {"Mod3",        Mod3Mask},
40     {"Mod4",        Mod4Mask},
41     {"Mod5",        Mod5Mask},
42     {"AnyModifier", AnyModifier},
43     {"NoModifier",  0},
44     {NULL,          0},
45 };
46
47 static StringIntMap button_map[]={
48     {"Button1",     Button1},
49     {"Button2",     Button2},
50     {"Button3",     Button3},
51     {"Button4",     Button4},
52     {"Button5",     Button5},
53     {"Button6",     6},
54     {"Button7",     7},
55     {"AnyButton",   AnyButton},
56     {NULL,          0},
57 };
58
59
60 bool ioncore_parse_keybut(const char *str, uint *mod_ret, uint *ksb_ret,
61                           bool button, bool init_any)
62 {
63     char *str2, *p, *p2;
64     int keysym=NoSymbol, i;
65     bool ret=FALSE;
66     
67     *ksb_ret=NoSymbol;
68     *mod_ret=(init_any && !button ? AnyModifier : 0);
69     
70     str2=scopy(str);
71     
72     if(str2==NULL)
73         return FALSE;
74
75     p=str2;
76     
77     while(*p!='\0'){
78         p2=strchr(p, '+');
79         
80         if(p2!=NULL)
81             *p2='\0';
82         
83         if(!button){
84             keysym=XStringToKeysym(p);
85 #ifdef CF_SUN_F1X_REMAP
86             if(keysym==XK_F11)
87                 keysym=XK_SunF36;
88             else if(keysym==XK_F12)
89                 keysym=XK_SunF37;
90 #endif
91         }
92         
93         if(!button && keysym!=NoSymbol){
94             int tmp;
95             if(*ksb_ret!=NoSymbol){
96                 warn_obj(str, TR("Insane key combination."));
97                 break;
98             }
99             if(XKeysymToKeycode(ioncore_g.dpy, keysym)==0){
100                 warn_obj(str, TR("Could not convert keysym to keycode."));
101                 break;
102             }
103             *ksb_ret=keysym;
104         }else{
105             i=stringintmap_ndx(state_map, p);
106
107             if(i<0){
108                 i=stringintmap_ndx(button_map, p);
109                 
110                 if(i<0){
111                     warn(TR("Unknown button \"%s\"."), p);
112                     break;
113                 }
114             
115                 if(!button || *ksb_ret!=NoSymbol){
116                     warn_obj(str, TR("Insane button combination."));
117                     break;
118                 }
119                 *ksb_ret=button_map[i].value;
120             }else{
121                 if(*mod_ret==AnyModifier){
122                     if(!init_any){
123                         warn_obj(str, TR("Insane modifier combination."));
124                         break;
125                     }else{
126                         *mod_ret=state_map[i].value;
127                     }
128                 }else{
129                     if(*mod_ret!=0 && state_map[i].value==AnyModifier){
130                         warn_obj(str, TR("Insane modifier combination."));
131                         break;
132                     }else{
133                         *mod_ret|=state_map[i].value;
134                     }
135                 }
136             }
137         }
138
139         if(p2==NULL){
140             ret=TRUE;
141             break;
142         }
143         
144         p=p2+1;
145     }
146
147     free(str2);
148     
149     return ret;
150 }
151
152 #undef BUTTON1_NDX
153
154
155 /*}}}*/
156
157
158 /*{{{ bindmap_defbindings */
159
160
161 static bool do_action(WBindmap *bindmap, const char *str,
162                       ExtlFn func, uint act, uint mod, uint ksb,
163                       int area, bool wr)
164 {
165     WBinding binding;
166     
167     if(wr && mod==0){
168         warn(TR("Can not wait on modifiers when no modifiers set in \"%s\"."),
169              str);
170         wr=FALSE;
171     }
172     
173     binding.wait=wr;
174     binding.act=act;
175     binding.state=mod;
176     binding.ksb=ksb;
177     binding.kcb=(act==BINDING_KEYPRESS ? XKeysymToKeycode(ioncore_g.dpy, ksb) : ksb);
178     binding.area=area;
179     binding.submap=NULL;
180     
181     if(func!=extl_fn_none()){
182         binding.func=extl_ref_fn(func);
183         if(bindmap_add_binding(bindmap, &binding))
184             return TRUE;
185         extl_unref_fn(binding.func);
186         warn(TR("Unable to add binding %s."), str);
187     }else{
188         binding.func=func;
189         if(bindmap_remove_binding(bindmap, &binding))
190             return TRUE;
191         warn(TR("Unable to remove binding %s."), str);
192     }
193
194     return FALSE;
195 }
196
197
198 static bool do_submap(WBindmap *bindmap, const char *str,
199                       ExtlTab subtab, uint action, uint mod, uint ksb)
200 {
201     WBinding binding, *bnd;
202     uint kcb;
203
204     if(action!=BINDING_KEYPRESS)
205         return FALSE;
206     
207     kcb=XKeysymToKeycode(ioncore_g.dpy, ksb);
208     bnd=bindmap_lookup_binding(bindmap, action, mod, kcb);
209     
210     if(bnd!=NULL && bnd->submap!=NULL && bnd->state==mod)
211         return bindmap_defbindings(bnd->submap, subtab, TRUE);
212
213     binding.wait=FALSE;
214     binding.act=BINDING_KEYPRESS;
215     binding.state=mod;
216     binding.ksb=ksb;
217     binding.kcb=kcb;
218     binding.area=0;
219     binding.func=extl_fn_none();
220     binding.submap=create_bindmap();
221     
222     if(binding.submap==NULL)
223         return FALSE;
224
225     if(bindmap_add_binding(bindmap, &binding))
226         return bindmap_defbindings(binding.submap, subtab, TRUE);
227
228     binding_deinit(&binding);
229     
230     warn(TR("Unable to add submap for binding %s."), str);
231     
232     return FALSE;
233 }
234
235
236 static StringIntMap action_map[]={
237     {"kpress", BINDING_KEYPRESS},
238     {"mpress", BINDING_BUTTONPRESS},
239     {"mclick", BINDING_BUTTONCLICK},
240     {"mdblclick", BINDING_BUTTONDBLCLICK},
241     {"mdrag", BINDING_BUTTONMOTION},
242     {NULL, 0}
243 };
244
245
246 static bool do_entry(WBindmap *bindmap, ExtlTab tab, 
247                      const StringIntMap *areamap, bool init_any)
248 {
249     bool ret=FALSE;
250     char *action_str=NULL, *ksb_str=NULL, *area_str=NULL;
251     int action=0;
252     uint ksb=0, mod=0;
253     WBinding *bnd=NULL;
254     ExtlTab subtab;
255     ExtlFn func;
256     bool wr=FALSE;
257     int area=0;
258     
259     if(!extl_table_gets_s(tab, "action", &action_str)){
260         warn(TR("Binding type not set."));
261         goto fail;
262     }
263
264     if(strcmp(action_str, "kpress_wait")==0){
265         action=BINDING_KEYPRESS;
266         wr=TRUE;
267     }else{
268         action=stringintmap_value(action_map, action_str, -1);
269         if(action<0){
270             warn(TR("Unknown binding type \"%s\"."), action_str);
271             goto fail;
272         }
273     }
274
275     if(!extl_table_gets_s(tab, "kcb", &ksb_str))
276         goto fail;
277
278     if(!ioncore_parse_keybut(ksb_str, &mod, &ksb,
279                              (action!=BINDING_KEYPRESS && action!=-1), 
280                              init_any)){
281         goto fail;
282     }
283     
284     if(extl_table_gets_t(tab, "submap", &subtab)){
285         ret=do_submap(bindmap, ksb_str, subtab, action, mod, ksb);
286         extl_unref_table(subtab);
287     }else{
288         if(areamap!=NULL){
289             if(extl_table_gets_s(tab, "area", &area_str)){
290                 area=stringintmap_value(areamap, area_str, -1);
291                 if(area<0){
292                     warn(TR("Unknown area \"%s\" for binding %s."),
293                          area_str, ksb_str);
294                     area=0;
295                 }
296             }
297         }
298         
299         if(!extl_table_gets_f(tab, "func", &func)){
300             /*warn("Function for binding %s not set/nil/undefined.", ksb_str);
301             goto fail;*/
302             func=extl_fn_none();
303         }
304         ret=do_action(bindmap, ksb_str, func, action, mod, ksb, area, wr);
305         if(!ret)
306             extl_unref_fn(func);
307     }
308     
309 fail:
310     if(action_str!=NULL)
311         free(action_str);
312     if(ksb_str!=NULL)
313         free(ksb_str);
314     if(area_str!=NULL)
315         free(area_str);
316     return ret;
317 }
318
319
320 bool bindmap_defbindings(WBindmap *bindmap, ExtlTab tab, bool submap)
321 {
322     int i, n, nok=0;
323     ExtlTab ent;
324     
325     n=extl_table_get_n(tab);
326     
327     for(i=1; i<=n; i++){
328         if(extl_table_geti_t(tab, i, &ent)){
329             nok+=do_entry(bindmap, ent, bindmap->areamap, submap);
330             extl_unref_table(ent);
331             continue;
332         }
333         warn(TR("Unable to get bindmap entry %d."), i);
334     }
335     return (nok!=0);
336 }
337
338
339 /*}}}*/
340
341
342 /*{{{ bindmap_getbindings */
343
344
345 static char *get_mods(uint state)
346 {
347     char *ret=NULL;
348     int i;
349     
350     if(state==AnyModifier){
351         ret=scopy("AnyModifier+");
352     }else{
353         ret=scopy("");
354         for(i=0; i<=MOD5_NDX; i++){
355             if(ret==NULL)
356                 break;
357             if((int)(state&state_map[i].value)==state_map[i].value){
358                 char *ret2=ret;
359                 ret=scat3(ret, state_map[i].string, "+");
360                 free(ret2);
361             }
362         }
363     }
364     
365     return ret;
366 }
367
368
369 static char *get_key(char *mods, uint ksb)
370 {
371     const char *s=XKeysymToString(ksb);
372     char *ret=NULL;
373     
374     if(s==NULL){
375         warn(TR("Unable to convert keysym to string."));
376         return NULL;
377     }
378     
379     return scat(mods, s);
380 }
381
382
383 static char *get_button(char *mods, uint ksb)
384 {
385     const char *s=stringintmap_key(button_map, ksb, NULL);
386     char *ret=NULL;
387     
388     if(s==NULL){
389         warn(TR("Unable to convert button to string."));
390         return NULL;
391     }
392     
393     return scat(mods, s);
394 }
395
396
397 static bool get_kpress(WBindmap *bindmap, WBinding *b, ExtlTab t)
398 {
399     char *mods;
400     char *key;
401     
402     if(b->wait)
403         extl_table_sets_s(t, "action", "kpress_wait");
404     else
405         extl_table_sets_s(t, "action", "kpress");
406     
407     mods=get_mods(b->state);
408     
409     if(mods==NULL)
410         return FALSE;
411     
412     key=get_key(mods, b->ksb);
413
414     free(mods);
415     
416     if(key==NULL)
417         return FALSE;
418     
419     extl_table_sets_s(t, "kcb", key);
420     
421     free(key);
422     
423     if(b->submap!=NULL){
424         ExtlTab stab=bindmap_getbindings(b->submap);
425         extl_table_sets_t(t, "submap", stab);
426     }else{
427         extl_table_sets_f(t, "func", b->func);
428     }
429     
430     return TRUE;
431 }
432
433
434 static bool get_mact(WBindmap *bindmap, WBinding *b, ExtlTab t)
435 {
436     char *mods;
437     char *button;
438     
439     extl_table_sets_s(t, "action", stringintmap_key(action_map, b->act, NULL));
440     
441     mods=get_mods(b->state);
442     
443     if(mods==NULL)
444         return FALSE;
445     
446     button=get_button(mods, b->ksb);
447
448     free(mods);
449     
450     if(button==NULL)
451         return FALSE;
452     
453     extl_table_sets_s(t, "kcb", button);
454     
455     free(button);
456     
457     if(b->area!=0 && bindmap->areamap!=NULL)
458         extl_table_sets_s(t, "area", 
459                           stringintmap_key(bindmap->areamap, b->area, NULL));
460
461     extl_table_sets_f(t, "func", b->func);
462     
463     return TRUE;
464 }
465
466
467 static ExtlTab getbinding(WBindmap *bindmap, WBinding *b)
468 {
469     ExtlTab t=extl_create_table();
470     
471     if(b->act==BINDING_KEYPRESS){
472         if(get_kpress(bindmap, b, t))
473             return t;
474     }else{
475         if(get_mact(bindmap, b, t))
476             return t;
477     }
478     
479     return extl_unref_table(t);
480 }
481
482
483 ExtlTab bindmap_getbindings(WBindmap *bindmap)
484 {
485     Rb_node node;
486     WBinding *b;
487     ExtlTab tab;
488     ExtlTab btab;
489     int n=0;
490     
491     tab=extl_create_table();
492     
493     FOR_ALL_BINDINGS(b, node, bindmap->bindings){
494         btab=getbinding(bindmap, b);
495         extl_table_seti_t(tab, n+1, btab);
496         extl_unref_table(btab);
497         n++;
498     }
499     
500     return tab;
501 }
502
503
504 /*}}}*/
505