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