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