Spell 'serialize' with a 'z'
[notion.git] / ioncore / conf-bindings.c
blobd0fefd12ceac872394e9339cb08359859dbffc94
1 /*
2 * ion/ioncore/conf-bindings.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <string.h>
11 #define XK_MISCELLANY
12 #include <X11/keysymdef.h>
14 #ifdef CF_SUN_F1X_REMAP
15 #include <X11/Sunkeysym.h>
16 #endif
18 #include <libtu/map.h>
20 #include "common.h"
21 #include "binding.h"
22 #include <libextl/readconfig.h>
23 #include "global.h"
24 #include <libextl/extl.h>
25 #include "conf-bindings.h"
26 #include "bindmaps.h"
27 #include "ioncore.h"
30 /*{{{ parse_keybut */
33 #define MOD5_NDX 7
35 static StringIntMap state_map[]={
36 {"Shift", ShiftMask},
37 {"Lock", LockMask},
38 {"Control", ControlMask},
39 {"Mod1", Mod1Mask},
40 {"Mod2", Mod2Mask},
41 {"Mod3", Mod3Mask},
42 {"Mod4", Mod4Mask},
43 {"Mod5", Mod5Mask},
44 {"AnyModifier", AnyModifier},
45 {"NoModifier", 0},
46 {NULL, 0},
49 static StringIntMap button_map[]={
50 {"Button1", Button1},
51 {"Button2", Button2},
52 {"Button3", Button3},
53 {"Button4", Button4},
54 {"Button5", Button5},
55 {"Button6", 6},
56 {"Button7", 7},
57 {"AnyButton", AnyButton},
58 {NULL, 0},
62 bool ioncore_parse_keybut(const char *str, uint *mod_ret, uint *ksb_ret,
63 bool button, bool init_any)
65 char *str2, *p, *p2;
66 int keysym=NoSymbol, i;
67 bool ret=FALSE;
69 *ksb_ret=NoSymbol;
70 *mod_ret=(init_any && !button ? AnyModifier : 0);
72 str2=scopy(str);
74 if(str2==NULL)
75 return FALSE;
77 p=str2;
79 while(*p!='\0'){
80 p2=strchr(p, '+');
82 if(p2!=NULL)
83 *p2='\0';
85 if(!button){
86 keysym=XStringToKeysym(p);
87 #ifdef CF_SUN_F1X_REMAP
88 if(keysym==XK_F11)
89 keysym=SunXK_F36;
90 else if(keysym==XK_F12)
91 keysym=SunXK_F37;
92 #endif
95 if(!button && keysym!=NoSymbol){
96 if(*ksb_ret!=NoSymbol){
97 warn_obj(str, TR("Insane key combination."));
98 break;
100 if(XKeysymToKeycode(ioncore_g.dpy, keysym)==0){
101 ioncore_warn_nolog("%s: %s", str,
102 TR("Could not convert keysym to keycode."));
103 break;
105 *ksb_ret=keysym;
106 }else{
107 i=stringintmap_ndx(state_map, p);
109 if(i<0){
110 i=stringintmap_ndx(button_map, p);
112 if(i<0){
113 warn(TR("Unknown button \"%s\"."), p);
114 break;
117 if(!button || *ksb_ret!=NoSymbol){
118 warn_obj(str, TR("Insane button combination."));
119 break;
121 *ksb_ret=button_map[i].value;
122 }else{
123 if(*mod_ret==AnyModifier){
124 if(!init_any){
125 warn_obj(str, TR("Insane modifier combination."));
126 break;
127 }else{
128 *mod_ret=state_map[i].value;
130 }else{
131 if(*mod_ret!=0 && state_map[i].value==AnyModifier){
132 warn_obj(str, TR("Insane modifier combination."));
133 break;
134 }else{
135 *mod_ret|=state_map[i].value;
141 if(p2==NULL){
142 ret=TRUE;
143 break;
146 p=p2+1;
149 free(str2);
151 return ret;
154 #undef BUTTON1_NDX
157 /*}}}*/
160 /*{{{ bindmap_defbindings */
163 static bool do_action(WBindmap *bindmap, const char *str,
164 ExtlFn func, uint act, uint mod, uint ksb,
165 int area, bool wr)
167 WBinding binding;
169 if(wr && mod==0){
170 warn(TR("Can not wait on modifiers when no modifiers set in \"%s\"."),
171 str);
172 wr=FALSE;
175 binding.wait=wr;
176 binding.act=act;
177 binding.state=mod;
178 binding.ksb=ksb;
179 binding.kcb=(act==BINDING_KEYPRESS ? XKeysymToKeycode(ioncore_g.dpy, ksb) : ksb);
180 binding.area=area;
181 binding.submap=NULL;
183 if(func!=extl_fn_none()){
184 binding.func=extl_ref_fn(func);
185 if(bindmap_add_binding(bindmap, &binding))
186 return TRUE;
187 extl_unref_fn(binding.func);
188 warn(TR("Unable to add binding %s."), str);
189 }else{
190 binding.func=func;
191 if(bindmap_remove_binding(bindmap, &binding))
192 return TRUE;
193 /*warn(TR("Unable to remove binding %s."), str);*/
196 return FALSE;
200 static bool do_submap(WBindmap *bindmap, const char *str,
201 ExtlTab subtab, uint action, uint mod, uint ksb)
203 WBinding binding, *bnd;
204 uint kcb=0;
206 if(action!=BINDING_KEYPRESS)
207 return FALSE;
209 kcb=XKeysymToKeycode(ioncore_g.dpy, ksb);
211 bnd=bindmap_lookup_binding(bindmap, action, mod, kcb);
213 if(bnd!=NULL && bnd->submap!=NULL && bnd->state==mod)
214 return bindmap_defbindings(bnd->submap, subtab, TRUE);
216 binding.wait=FALSE;
217 binding.act=BINDING_KEYPRESS;
218 binding.state=mod;
219 binding.ksb=ksb;
220 binding.kcb=kcb;
221 binding.area=0;
222 binding.func=extl_fn_none();
223 binding.submap=create_bindmap();
225 if(binding.submap==NULL)
226 return FALSE;
228 if(bindmap_add_binding(bindmap, &binding))
229 return bindmap_defbindings(binding.submap, subtab, TRUE);
231 binding_deinit(&binding);
233 warn(TR("Unable to add submap for binding %s."), str);
235 return FALSE;
239 static StringIntMap action_map[]={
240 {"kpress", BINDING_KEYPRESS},
241 {"mpress", BINDING_BUTTONPRESS},
242 {"mclick", BINDING_BUTTONCLICK},
243 {"mdblclick", BINDING_BUTTONDBLCLICK},
244 {"mdrag", BINDING_BUTTONMOTION},
245 {"submap_enter", BINDING_SUBMAP_ENTER},
246 {"submap_wait", BINDING_SUBMAP_RELEASEMOD},
247 /*{"submap_leave", BINDING_SUBMAP_LEAVE},*/
248 {NULL, 0}
252 static bool do_entry(WBindmap *bindmap, ExtlTab tab,
253 const StringIntMap *areamap, bool init_any)
255 bool ret=FALSE;
256 char *action_str=NULL, *ksb_str=NULL, *area_str=NULL;
257 int action=0;
258 uint ksb=0, mod=0;
259 ExtlTab subtab;
260 ExtlFn func;
261 bool wr=FALSE;
262 int area=0;
264 if(!extl_table_gets_s(tab, "action", &action_str)){
265 warn(TR("Binding type not set."));
266 goto fail;
269 if(strcmp(action_str, "kpress_wait")==0){
270 action=BINDING_KEYPRESS;
271 wr=TRUE;
272 }else{
273 action=stringintmap_value(action_map, action_str, -1);
274 if(action<0){
275 warn(TR("Unknown binding type \"%s\"."), action_str);
276 goto fail;
280 if(!BINDING_IS_PSEUDO(action)){
281 if(!extl_table_gets_s(tab, "kcb", &ksb_str))
282 goto fail;
284 if(!ioncore_parse_keybut(ksb_str, &mod, &ksb,
285 (action!=BINDING_KEYPRESS && action!=-1),
286 init_any)){
287 goto fail;
291 if(extl_table_gets_t(tab, "submap", &subtab)){
292 ret=do_submap(bindmap, ksb_str, subtab, action, mod, ksb);
293 extl_unref_table(subtab);
294 }else{
295 if(areamap!=NULL){
296 if(extl_table_gets_s(tab, "area", &area_str)){
297 area=stringintmap_value(areamap, area_str, -1);
298 if(area<0){
299 warn(TR("Unknown area \"%s\" for binding %s."),
300 area_str, ksb_str);
301 area=0;
306 if(!extl_table_gets_f(tab, "func", &func)){
307 /*warn("Function for binding %s not set/nil/undefined.", ksb_str);
308 goto fail;*/
309 func=extl_fn_none();
311 ret=do_action(bindmap, ksb_str, func, action, mod, ksb, area, wr);
312 if(!ret)
313 extl_unref_fn(func);
316 fail:
317 if(action_str!=NULL)
318 free(action_str);
319 if(ksb_str!=NULL)
320 free(ksb_str);
321 if(area_str!=NULL)
322 free(area_str);
323 return ret;
327 bool bindmap_defbindings(WBindmap *bindmap, ExtlTab tab, bool submap)
329 int i, n, nok=0;
330 ExtlTab ent;
332 n=extl_table_get_n(tab);
334 for(i=1; i<=n; i++){
335 if(extl_table_geti_t(tab, i, &ent)){
336 nok+=do_entry(bindmap, ent, bindmap->areamap, submap);
337 extl_unref_table(ent);
338 continue;
340 warn(TR("Unable to get bindmap entry %d."), i);
342 return (nok!=0);
346 /*}}}*/
349 /*{{{ bindmap_getbindings */
352 static char *get_mods(uint state)
354 char *ret=NULL;
355 int i;
357 if(state==AnyModifier){
358 ret=scopy("AnyModifier+");
359 }else{
360 ret=scopy("");
361 for(i=0; i<=MOD5_NDX; i++){
362 if(ret==NULL)
363 break;
364 if((int)(state&state_map[i].value)==state_map[i].value){
365 char *ret2=ret;
366 ret=scat3(ret, state_map[i].string, "+");
367 free(ret2);
372 return ret;
376 static char *get_key(char *mods, uint ksb)
378 const char *s=XKeysymToString(ksb);
380 if(s==NULL){
381 warn(TR("Unable to convert keysym to string."));
382 return NULL;
385 return scat(mods, s);
389 static char *get_button(char *mods, uint ksb)
391 const char *s=stringintmap_key(button_map, ksb, NULL);
393 if(s==NULL){
394 warn(TR("Unable to convert button to string."));
395 return NULL;
398 return scat(mods, s);
402 static bool get_kpress(WBindmap *bindmap, WBinding *b, ExtlTab t)
404 char *mods;
405 char *key;
407 if(b->wait)
408 extl_table_sets_s(t, "action", "kpress_wait");
409 else
410 extl_table_sets_s(t, "action", "kpress");
412 mods=get_mods(b->state);
414 if(mods==NULL)
415 return FALSE;
417 key=get_key(mods, b->ksb);
419 free(mods);
421 if(key==NULL)
422 return FALSE;
424 extl_table_sets_s(t, "kcb", key);
426 free(key);
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);
435 return TRUE;
439 static bool get_mact(WBindmap *bindmap, WBinding *b, ExtlTab t)
441 char *mods;
442 char *button;
444 extl_table_sets_s(t, "action", stringintmap_key(action_map, b->act, NULL));
446 mods=get_mods(b->state);
448 if(mods==NULL)
449 return FALSE;
451 button=get_button(mods, b->ksb);
453 free(mods);
455 if(button==NULL)
456 return FALSE;
458 extl_table_sets_s(t, "kcb", button);
460 free(button);
462 if(b->area!=0 && bindmap->areamap!=NULL)
463 extl_table_sets_s(t, "area",
464 stringintmap_key(bindmap->areamap, b->area, NULL));
466 extl_table_sets_f(t, "func", b->func);
468 return TRUE;
472 static ExtlTab getbinding(WBindmap *bindmap, WBinding *b)
474 ExtlTab t=extl_create_table();
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;
484 return extl_unref_table(t);
488 ExtlTab bindmap_getbindings(WBindmap *bindmap)
490 Rb_node node;
491 WBinding *b;
492 ExtlTab tab;
493 ExtlTab btab;
494 int n=0;
496 tab=extl_create_table();
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++;
505 return tab;
509 /*}}}*/