1 /*
2 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3 * Copyright (C) 2004-2021 Kim Woelders
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies of the Software, its documentation and marketing & publicity
14 * materials, and acknowledgment shall be given in the documentation, materials
15 * and software packages that this Software was used.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include "config.h"
25
26 #include <ctype.h>
27 #include <X11/Xlib.h>
28
29 #include "E.h"
30 #include "aclass.h"
31 #include "conf.h"
32 #include "cursors.h"
33 #include "emodule.h"
34 #include "ewins.h"
35 #include "file.h"
36 #include "grabs.h"
37 #include "list.h"
38 #include "timers.h"
39
40 typedef struct _actiontype {
41 char *params;
42 struct _actiontype *next;
43 } ActionType;
44
45 struct _action {
46 char event;
47 char anymodifier;
48 int modifiers;
49 char anybutton;
50 int button;
51 char anykey;
52 EX_KeyCode keycode;
53 char *key_str;
54 char *tooltipstring;
55 ActionType *action;
56 };
57
58 struct _actionclass {
59 dlist_t list;
60 char *name;
61 int num;
62 Action **actions;
63 char *tooltipstring;
64 unsigned int ref_count;
65 char global;
66 };
67
68 static void UnGrabActionKey(Action * aa);
69 static void GrabActionKey(Action * aa);
70
71 static void BindingsSave(void);
72
73 static LIST_HEAD(aclass_list);
74 static LIST_HEAD(aclass_list_global);
75
76 static char mode_action_destroy = 0;
77 static char mode_keybinds_changed = 0;
78
79 static void
RemoveActionType(ActionType * ActionTypeToRemove)80 RemoveActionType(ActionType * ActionTypeToRemove)
81 {
82 ActionType *ptr, *pp;
83
84 ptr = ActionTypeToRemove;
85 while (ptr)
86 {
87 Efree(ptr->params);
88 pp = ptr;
89 ptr = ptr->next;
90 Efree(pp);
91 }
92 }
93
94 Action *
ActionCreate(char event,char anymod,int mod,int anybut,int but,char anykey,const char * key,const char * tooltipstring)95 ActionCreate(char event, char anymod, int mod, int anybut, int but,
96 char anykey, const char *key, const char *tooltipstring)
97 {
98 Action *aa;
99
100 aa = EMALLOC(Action, 1);
101 if (!aa)
102 return NULL;
103 aa->action = NULL;
104 aa->event = event;
105 aa->anymodifier = anymod;
106 aa->modifiers = mod;
107 aa->anybutton = anybut;
108 aa->button = but;
109 aa->anykey = anykey;
110 if (!key || !key[0] || (event != EVENT_KEY_DOWN && event != EVENT_KEY_UP))
111 aa->keycode = 0;
112 else
113 aa->keycode = EKeynameToKeycode(key);
114 aa->key_str = (aa->keycode) ? Estrdup(key) : NULL;
115 aa->tooltipstring =
116 (tooltipstring) ? Estrdup((tooltipstring[0]) ? tooltipstring : "?!?") :
117 NULL;
118
119 return aa;
120 }
121
122 static void
ActionDestroy(Action * aa)123 ActionDestroy(Action * aa)
124 {
125 if (!aa)
126 return;
127
128 if ((aa->event == EVENT_KEY_DOWN) || (aa->event == EVENT_KEY_UP))
129 UnGrabActionKey(aa);
130 if (aa->action)
131 RemoveActionType(aa->action);
132 Efree(aa->tooltipstring);
133 Efree(aa->key_str);
134 Efree(aa);
135 }
136
137 void
ActionAddTo(Action * aa,const char * params)138 ActionAddTo(Action * aa, const char *params)
139 {
140 ActionType *pptr, *ptr, *at;
141
142 if (!aa)
143 return;
144
145 at = EMALLOC(ActionType, 1);
146 if (!at)
147 return;
148
149 at->next = NULL;
150 at->params = (params && *params) ? Estrdup(params) : NULL;
151 if (!aa->action)
152 {
153 aa->action = at;
154 }
155 else
156 {
157 pptr = NULL;
158 ptr = aa->action;
159 while (ptr)
160 {
161 pptr = ptr;
162 ptr = ptr->next;
163 }
164 if (pptr)
165 pptr->next = at;
166 }
167 }
168
169 void
ActionclassAddAction(ActionClass * ac,Action * aa)170 ActionclassAddAction(ActionClass * ac, Action * aa)
171 {
172 if (!ac || !aa)
173 return;
174 ac->num++;
175 ac->actions = EREALLOC(Action *, ac->actions, ac->num);
176 ac->actions[ac->num - 1] = aa;
177 }
178
179 ActionClass *
ActionclassCreate(const char * name,int global)180 ActionclassCreate(const char *name, int global)
181 {
182 ActionClass *ac;
183
184 ac = ECALLOC(ActionClass, 1);
185 if (!ac)
186 return NULL;
187 ac->name = Estrdup(name);
188
189 if (global)
190 {
191 LIST_PREPEND(ActionClass, &aclass_list_global, ac);
192 ac->global = 1;
193 }
194 else
195 {
196 LIST_PREPEND(ActionClass, &aclass_list, ac);
197 }
198
199 return ac;
200 }
201
202 static void
ActionclassEmpty(ActionClass * ac)203 ActionclassEmpty(ActionClass * ac)
204 {
205 int i;
206
207 for (i = 0; i < ac->num; i++)
208 ActionDestroy(ac->actions[i]);
209 ac->num = 0;
210 EFREE_NULL(ac->actions);
211 EFREE_NULL(ac->tooltipstring);
212 }
213
214 static void
ActionclassDestroy(ActionClass * ac)215 ActionclassDestroy(ActionClass * ac)
216 {
217 if (!ac)
218 return;
219
220 if (ac->ref_count > 0)
221 {
222 DialogOK("ActionClass Error!", _("%u references remain"),
223 ac->ref_count);
224 return;
225 }
226
227 LIST_REMOVE(ActionClass, &aclass_list, ac);
228
229 ActionclassEmpty(ac);
230 Efree(ac->name);
231
232 Efree(ac);
233 mode_action_destroy = 1;
234 }
235
236 static int
_ActionclassMatchName(const void * data,const void * match)237 _ActionclassMatchName(const void *data, const void *match)
238 {
239 return strcmp(((const ActionClass *)data)->name, (const char *)match);
240 }
241
242 static ActionClass *
ActionclassFindGlobal(const char * name)243 ActionclassFindGlobal(const char *name)
244 {
245 return LIST_FIND(ActionClass, &aclass_list_global,
246 _ActionclassMatchName, name);
247 }
248
249 ActionClass *
ActionclassFind(const char * name)250 ActionclassFind(const char *name)
251 {
252 if (!name)
253 return NULL;
254 return LIST_FIND(ActionClass, &aclass_list, _ActionclassMatchName, name);
255 }
256
257 static ActionClass *
ActionclassFindAny(const char * name)258 ActionclassFindAny(const char *name)
259 {
260 ActionClass *ac;
261
262 ac = LIST_FIND(ActionClass, &aclass_list_global,
263 _ActionclassMatchName, name);
264 if (ac)
265 return ac;
266 return LIST_FIND(ActionClass, &aclass_list, _ActionclassMatchName, name);
267 }
268
269 int
AclassConfigLoad(FILE * fs)270 AclassConfigLoad(FILE * fs)
271 {
272 int err = 0;
273 ActionClass *ac = NULL;
274 Action *aa = NULL;
275 char s[FILEPATH_LEN_MAX];
276 char s2[FILEPATH_LEN_MAX];
277 char *p2;
278 int i1, i2;
279 char event = 0;
280 char anymod = 0;
281 int mod = 0;
282 int anybut = 0;
283 int but = 0;
284 int first = 1;
285 char anykey = 0;
286 char key[64];
287 char *aclass_tooltipstring = NULL;
288 char *action_tooltipstring = NULL;
289 char global = 0;
290
291 key[0] = '\0';
292
293 while (GetLine(s, sizeof(s), fs))
294 {
295 i1 = ConfigParseline1(s, s2, &p2, NULL);
296 i2 = atoi(s2);
297 switch (i1)
298 {
299 case CONFIG_ACTIONCLASS:
300 err = -1;
301 if (i2 != CONFIG_OPEN)
302 goto done;
303 ac = NULL;
304 aa = NULL;
305 event = 0;
306 anymod = anybut = anykey = 0;
307 mod = 0;
308 but = 0;
309 first = 1;
310 key[0] = '\0';
311 break;
312 case CONFIG_CLOSE:
313 if (ac)
314 ac->tooltipstring =
315 (aclass_tooltipstring) ? Estrdup((aclass_tooltipstring[0]) ?
316 aclass_tooltipstring :
317 "?!?") : NULL;
318 err = 0;
319 goto done;
320
321 case CONFIG_CLASSNAME:
322 ac = ActionclassFindAny(s2);
323 if (ac)
324 {
325 if (!strcmp(s2, "KEYBINDINGS"))
326 mode_keybinds_changed = 1;
327 ActionclassEmpty(ac);
328 }
329 else
330 {
331 ac = ActionclassCreate(s2, 0);
332 }
333 break;
334 case CONFIG_TYPE:
335 if (!ac || i2 == ACLASS_TYPE_ACLASS)
336 break;
337 LIST_REMOVE(ActionClass, &aclass_list, ActionclassFind(ac->name));
338 LIST_PREPEND(ActionClass, &aclass_list_global, ac);
339 global = 1;
340
341 break;
342 case CONFIG_MODIFIER:
343 /* These are the defines that I have listed...
344 * These, therefore, are the ones that I am
345 * going to accept by default.
346 * REMINDER: add and'ing in future!!!!
347 * #define ShiftMask (1<<0)
348 * #define LockMask (1<<1)
349 * #define ControlMask (1<<2)
350 * #define Mod1Mask (1<<3)
351 * #define Mod2Mask (1<<4)
352 * #define Mod3Mask (1<<5)
353 * #define Mod4Mask (1<<6)
354 * #define Mod5Mask (1<<7)
355 */
356 switch (i2)
357 {
358 case MASK_NONE:
359 mod = 0;
360 break;
361 case MASK_SHIFT:
362 mod |= ShiftMask;
363 break;
364 case MASK_LOCK:
365 mod |= LockMask;
366 break;
367 case MASK_CTRL:
368 mod |= ControlMask;
369 break;
370 case MASK_MOD1:
371 mod |= Mod1Mask;
372 break;
373 case MASK_MOD2:
374 mod |= Mod2Mask;
375 break;
376 case MASK_MOD3:
377 mod |= Mod3Mask;
378 break;
379 case MASK_MOD4:
380 mod |= Mod4Mask;
381 break;
382 case MASK_MOD5:
383 mod |= Mod5Mask;
384 break;
385 case MASK_CTRL_ALT:
386 mod |= ControlMask | Mod1Mask;
387 break;
388 case MASK_SHIFT_ALT:
389 mod |= ShiftMask | Mod1Mask;
390 break;
391 case MASK_CTRL_SHIFT:
392 mod |= ShiftMask | ControlMask;
393 break;
394 case MASK_CTRL_SHIFT_ALT:
395 mod |= ShiftMask | ControlMask | Mod1Mask;
396 break;
397 case MASK_SHIFT_META4:
398 mod |= Mod4Mask | ShiftMask;
399 break;
400 case MASK_CTRL_META4:
401 mod |= Mod4Mask | ControlMask;
402 break;
403 case MASK_CTRL_META4_SHIFT:
404 mod |= Mod4Mask | ControlMask | ShiftMask;
405 break;
406 case MASK_SHIFT_META5:
407 mod |= Mod5Mask | ShiftMask;
408 break;
409 case MASK_CTRL_META5:
410 mod |= Mod5Mask | ControlMask;
411 break;
412 case MASK_CTRL_META5_SHIFT:
413 mod |= Mod5Mask | ControlMask | ShiftMask;
414 break;
415 case MASK_WINDOWS_SHIFT:
416 mod |= Mod2Mask | ShiftMask;
417 break;
418 case MASK_WINDOWS_CTRL:
419 mod |= Mod2Mask | ControlMask;
420 break;
421 case MASK_WINDOWS_ALT:
422 mod |= Mod2Mask | Mod1Mask;
423 break;
424 default:
425 break;
426 }
427 break;
428 case CONFIG_ANYMOD:
429 anymod = i2;
430 break;
431 case CONFIG_ANYBUT:
432 anybut = i2;
433 break;
434 case CONFIG_BUTTON:
435 but = i2;
436 break;
437 case CONFIG_ANYKEY:
438 anykey = i2;
439 break;
440 case ACLASS_KEY:
441 STRCPY(key, s2);
442 break;
443 case ACLASS_EVENT_TRIGGER:
444 event = i2;
445 break;
446 case CONFIG_NEXT:
447 mod = 0;
448 anymod = 0;
449 anybut = 0;
450 first = 1;
451 break;
452 case CONFIG_ACTION:
453 if (first)
454 {
455 aa = ActionCreate(event, anymod, mod, anybut, but, anykey,
456 key, action_tooltipstring);
457 /* the correct place to grab an action key */
458 EFREE_NULL(action_tooltipstring);
459 key[0] = '\0';
460 if (global)
461 GrabActionKey(aa);
462 ActionclassAddAction(ac, aa);
463 first = 0;
464 }
465 ActionAddTo(aa, p2);
466 break;
467 case CONFIG_ACTION_TOOLTIP:
468 action_tooltipstring = Estrdupcat2(action_tooltipstring, "\n", p2);
469 break;
470 case CONFIG_TOOLTIP:
471 aclass_tooltipstring = Estrdupcat2(aclass_tooltipstring, "\n", p2);
472 break;
473 default:
474 ConfigParseError("ActionClass", s);
475 break;
476 }
477 }
478
479 if (ac && err)
480 ActionclassDestroy(ac);
481
482 done:
483 Efree(aclass_tooltipstring);
484 Efree(action_tooltipstring);
485
486 return err;
487 }
488
489 static Action *
ActionDecode(const char * line)490 ActionDecode(const char *line)
491 {
492 Action *aa;
493 char ev[16], mod[16], key[128], *s;
494 int len, event, modifiers, button;
495 char anymod, anybut, anykey;
496
497 len = -1;
498 sscanf(line, "%15s %15s %127s %n", ev, mod, key, &len);
499 if (len <= 0)
500 return NULL;
501
502 event = -1;
503 if (!strcmp(ev, "KeyDown"))
504 event = EVENT_KEY_DOWN;
505 else if (!strcmp(ev, "MouseDown"))
506 event = EVENT_MOUSE_DOWN;
507 else if (!strcmp(ev, "KeyUp"))
508 event = EVENT_KEY_UP;
509 else if (!strcmp(ev, "MouseUp"))
510 event = EVENT_MOUSE_UP;
511 else if (!strcmp(ev, "MouseDouble"))
512 event = EVENT_DOUBLE_DOWN;
513 else if (!strcmp(ev, "MouseIn"))
514 event = EVENT_MOUSE_ENTER;
515 else if (!strcmp(ev, "MouseOut"))
516 event = EVENT_MOUSE_LEAVE;
517 else if (!strcmp(ev, "FocusIn"))
518 event = EVENT_FOCUS_IN;
519 else if (!strcmp(ev, "FocusOut"))
520 event = EVENT_FOCUS_OUT;
521
522 anymod = anybut = anykey = 0;
523 button = 0;
524
525 modifiers = 0;
526 for (s = mod; *s; s++)
527 {
528 switch (*s)
529 {
530 case '*':
531 anymod = 1;
532 break;
533 case 'C':
534 modifiers |= ControlMask;
535 break;
536 case 'S':
537 modifiers |= ShiftMask;
538 break;
539 case 'A':
540 case '1':
541 modifiers |= Mod1Mask;
542 break;
543 case '2':
544 modifiers |= Mod2Mask;
545 break;
546 case '3':
547 modifiers |= Mod3Mask;
548 break;
549 case 'W':
550 case '4':
551 modifiers |= Mod4Mask;
552 break;
553 case '5':
554 modifiers |= Mod5Mask;
555 break;
556 }
557 }
558
559 switch (event)
560 {
561 case EVENT_MOUSE_DOWN:
562 case EVENT_MOUSE_UP:
563 case EVENT_DOUBLE_DOWN:
564 case EVENT_MOUSE_ENTER:
565 case EVENT_MOUSE_LEAVE:
566 if (key[0] == '*')
567 anybut = 1;
568 else if (isdigit(key[0]))
569 button = atoi(key);
570 if (!anybut && button == 0)
571 return NULL; /* Invalid */
572 key[0] = '\0';
573 break;
574 }
575
576 aa =
577 ActionCreate(event, anymod, modifiers, anybut, button, anykey, key, NULL);
578 ActionAddTo(aa, line + len);
579
580 return aa;
581 }
582
583 static int
ActionEncode(Action * aa,char * buf,int len)584 ActionEncode(Action * aa, char *buf, int len)
585 {
586 const char *event;
587 char *p, mod[32], btn[32];
588
589 if (!aa || !aa->action)
590 return 0;
591
592 p = mod;
593 if (aa->anymodifier)
594 *p++ = '*';
595 if (aa->modifiers & ControlMask)
596 *p++ = 'C';
597 if (aa->modifiers & ShiftMask)
598 *p++ = 'S';
599 if (aa->modifiers & Mod1Mask)
600 *p++ = 'A';
601 if (aa->modifiers & Mod2Mask)
602 *p++ = '2';
603 if (aa->modifiers & Mod3Mask)
604 *p++ = '3';
605 if (aa->modifiers & Mod4Mask)
606 *p++ = '4';
607 if (aa->modifiers & Mod5Mask)
608 *p++ = '5';
609 if (p == mod)
610 *p++ = '-';
611 *p = '\0';
612
613 switch (aa->event)
614 {
615 default:
616 return 0;
617 case EVENT_KEY_DOWN:
618 event = "KeyDown";
619 goto encode_kb;
620 case EVENT_KEY_UP:
621 event = "KeyUp";
622 goto encode_kb;
623 encode_kb:
624 if (!aa->key_str)
625 return 0;
626 len = Esnprintf(buf, len, "%-7s %4s %8s %s\n", event, mod, aa->key_str,
627 (aa->action->params) ? aa->action->params : "");
628 break;
629
630 case EVENT_MOUSE_DOWN:
631 event = "MouseDown";
632 goto encode_mb;
633 case EVENT_MOUSE_UP:
634 event = "MouseUp";
635 goto encode_mb;
636 case EVENT_DOUBLE_DOWN:
637 event = "MouseDouble";
638 goto encode_mb;
639 case EVENT_MOUSE_ENTER:
640 event = "MouseIn";
641 goto encode_mb;
642 case EVENT_MOUSE_LEAVE:
643 event = "MouseOut";
644 goto encode_mb;
645 encode_mb:
646 if (aa->anybutton)
647 strcpy(btn, "*");
648 else
649 sprintf(btn, "%d", aa->button);
650 len = Esnprintf(buf, len, "%-11s %4s %s %s\n", event, mod, btn,
651 (aa->action->params) ? aa->action->params : "");
652 break;
653
654 #if 0 /* Not implemented */
655 case EVENT_FOCUS_IN:
656 event = "FocusIn";
657 goto encode_fc;
658 case EVENT_FOCUS_OUT:
659 event = "FocusOut";
660 goto encode_fc;
661 encode_fc:
662 break;
663 #endif
664 }
665
666 return len;
667 }
668
669 static int
AclassEncodeTT(const char * str,char * buf,int len)670 AclassEncodeTT(const char *str, char *buf, int len)
671 {
672 char **lst;
673 int i, num, l, nw;
674
675 lst = StrlistFromString(str, '\n', &num);
676 nw = 0;
677 for (i = 0; i < num; i++)
678 {
679 l = Esnprintf(buf, len, "Tooltip %s\n", lst[i]);
680 nw += l;
681 len -= l;
682 buf += l;
683 }
684 StrlistFree(lst, num);
685 return nw;
686 }
687
688 static void
AclassConfigLineParse(char * s,ActionClass ** pac,Action ** paa)689 AclassConfigLineParse(char *s, ActionClass ** pac, Action ** paa)
690 {
691 char prm1[128], prm2[128], prm3[128];
692 ActionClass *ac = *pac;
693 Action *aa = *paa;
694 int len, len2;
695
696 len = strcspn(s, "#\r\n");
697 if (len <= 0)
698 return;
699 s[len] = '\0';
700
701 prm2[0] = prm3[0] = '\0';
702 len2 = 0;
703 len = sscanf(s, "%16s %n%127s %16s", prm1, &len2, prm2, prm3);
704 if (len < 2)
705 return;
706
707 if (!strcmp(prm1, "Aclass"))
708 {
709 if (!strcmp(prm2, "KEYBINDINGS_UNCHANGABLE"))
710 {
711 /* No more "unchangable" keybindings. */
712 ac = ActionclassFindGlobal("KEYBINDINGS");
713 prm2[11] = '\0';
714 }
715 else
716 {
717 ac = ActionclassFindAny(prm2);
718 if (ac)
719 ActionclassEmpty(ac);
720 }
721
722 if (!ac)
723 ac = ActionclassCreate(prm2, prm3[0] == 'g');
724
725 mode_keybinds_changed = 1;
726 aa = NULL;
727 }
728 else if (!strncmp(prm1, "Key", 3) || !strncmp(prm1, "Mouse", 5))
729 {
730 if (!ac)
731 return;
732
733 aa = ActionDecode(s);
734 ActionclassAddAction(ac, aa);
735 GrabActionKey(aa);
736 }
737 else if (!strcmp(prm1, "Tooltip"))
738 {
739 /* FIXME - Multiple line strings may break */
740 if (aa)
741 {
742 aa->tooltipstring = Estrdupcat2(aa->tooltipstring, "\n", s + len2);
743 }
744 else if (ac)
745 {
746 ac->tooltipstring = Estrdupcat2(ac->tooltipstring, "\n", s + len2);
747 }
748 }
749
750 *pac = ac;
751 *paa = aa;
752 }
753
754 static int
AclassConfigLoad2(FILE * fs)755 AclassConfigLoad2(FILE * fs)
756 {
757 char s[FILEPATH_LEN_MAX], *ss;
758 ActionClass *ac = NULL;
759 Action *aa = NULL;
760
761 for (;;)
762 {
763 ss = fgets(s, sizeof(s), fs);
764 if (!ss)
765 break;
766
767 AclassConfigLineParse(s, &ac, &aa);
768 }
769
770 return 0;
771 }
772
773 static void
AclassConfigParseIpc(const char * p)774 AclassConfigParseIpc(const char *p)
775 {
776 char *s, *ss;
777 int len;
778 ActionClass *ac = NULL;
779 Action *aa = NULL;
780
781 ss = s = Estrdup(p);
782 if (!s)
783 return;
784
785 for (;; s += len + 1)
786 {
787 len = strcspn(s, "#\r\n");
788 if (len <= 0)
789 break;
790 s[len] = '\0';
791
792 AclassConfigLineParse(s, &ac, &aa);
793 }
794
795 Efree(ss);
796
797 BindingsSave();
798 }
799
800 static void
AclassConfigLoadConfig(const char * name)801 AclassConfigLoadConfig(const char *name)
802 {
803 ConfigFileLoad(name, NULL, AclassConfigLoad2, 0);
804 }
805
806 static void
AclassConfigWrite(const ActionClass * ac,void (* prf)(const char * fmt,...))807 AclassConfigWrite(const ActionClass * ac, void (*prf)(const char *fmt, ...))
808 {
809 char s[FILEPATH_LEN_MAX];
810 Action *aa;
811 int i, len;
812
813 if (!ac || ac->num <= 0)
814 return;
815
816 prf("Aclass %s %s\n", ac->name, (ac->global)? "global" : "normal");
817 if (ac->tooltipstring)
818 {
819 AclassEncodeTT(ac->tooltipstring, s, sizeof(s));
820 prf(s);
821 }
822 for (i = 0; i < ac->num; i++)
823 {
824 aa = ac->actions[i];
825 len = ActionEncode(aa, s, sizeof(s));
826 if (len <= 0)
827 continue;
828 prf(s);
829 if (aa->tooltipstring)
830 {
831 AclassEncodeTT(aa->tooltipstring, s, sizeof(s));
832 prf(s);
833 }
834 }
835 }
836
837 static FILE *_ac_fs = NULL; /* Ugly! Yeah well... */
838
839 static void
_ac_prf(const char * fmt,...)840 _ac_prf(const char *fmt, ...)
841 {
842 va_list args;
843
844 va_start(args, fmt);
845 vfprintf(_ac_fs, fmt, args);
846 va_end(args);
847 }
848
849 static void
BindingsSave(void)850 BindingsSave(void)
851 {
852 char s[FILEPATH_LEN_MAX], ss[FILEPATH_LEN_MAX];
853 FILE *fs;
854
855 if (!mode_keybinds_changed)
856 return;
857
858 Etmp(ss);
859 fs = fopen(ss, "w");
860 if (!fs)
861 return;
862 _ac_fs = fs;
863
864 AclassConfigWrite(ActionclassFind("BUTTONBINDINGS"), _ac_prf);
865 AclassConfigWrite(ActionclassFind("DESKBINDINGS"), _ac_prf);
866 AclassConfigWrite(ActionclassFindGlobal("KEYBINDINGS"), _ac_prf);
867 AclassConfigWrite(ActionclassFindGlobal("KEYBINDINGS_UNCHANGABLE"), _ac_prf);
868
869 fclose(fs);
870 _ac_fs = NULL;
871
872 Esnprintf(s, sizeof(s), "%s/bindings.cfg", EDirUserConf());
873 E_mv(ss, s);
874 }
875
876 void
ActionclassSetTooltipString(ActionClass * ac,const char * tts)877 ActionclassSetTooltipString(ActionClass * ac, const char *tts)
878 {
879 EFREE_DUP(ac->tooltipstring, tts);
880 }
881
882 ActionClass *
ActionclassAlloc(const char * name)883 ActionclassAlloc(const char *name)
884 {
885 ActionClass *ac;
886
887 if (!name || !name[0])
888 return NULL;
889
890 ac = ActionclassFind(name);
891 if (ac)
892 ac->ref_count++;
893
894 return ac;
895 }
896
897 void
ActionclassFree(ActionClass * ac)898 ActionclassFree(ActionClass * ac)
899 {
900 if (ac)
901 ac->ref_count--;
902 }
903
904 const char *
ActionclassGetName(ActionClass * ac)905 ActionclassGetName(ActionClass * ac)
906 {
907 return (ac) ? ac->name : NULL;
908 }
909
910 const char *
ActionclassGetTooltipString(ActionClass * ac)911 ActionclassGetTooltipString(ActionClass * ac)
912 {
913 return (ac) ? ac->tooltipstring : NULL;
914 }
915
916 int
ActionclassGetActionCount(ActionClass * ac)917 ActionclassGetActionCount(ActionClass * ac)
918 {
919 return (ac) ? ac->num : 0;
920 }
921
922 Action *
ActionclassGetAction(ActionClass * ac,int ix)923 ActionclassGetAction(ActionClass * ac, int ix)
924 {
925 return (ac && ix < ac->num) ? ac->actions[ix] : NULL;
926 }
927
928 const char *
ActionGetTooltipString(Action * aa)929 ActionGetTooltipString(Action * aa)
930 {
931 return (aa) ? aa->tooltipstring : NULL;
932 }
933
934 int
ActionGetEvent(Action * aa)935 ActionGetEvent(Action * aa)
936 {
937 return (aa) ? aa->event : 0;
938 }
939
940 int
ActionGetAnybutton(Action * aa)941 ActionGetAnybutton(Action * aa)
942 {
943 return (aa) ? aa->anybutton : 0;
944 }
945
946 int
ActionGetButton(Action * aa)947 ActionGetButton(Action * aa)
948 {
949 return (aa) ? aa->button : 0;
950 }
951
952 int
ActionGetModifiers(Action * aa)953 ActionGetModifiers(Action * aa)
954 {
955 return (aa) ? aa->modifiers : 0;
956 }
957
958 static void
handleAction(EWin * ewin,ActionType * action)959 handleAction(EWin * ewin, ActionType * action)
960 {
961 if (ewin)
962 ewin->state.in_action = 1;
963 EFunc(ewin, action->params);
964 if (ewin)
965 {
966 if (!EwinFindByPtr(ewin))
967 return; /* ewin has been destroyed */
968 ewin->state.in_action = 0;
969 }
970
971 /* Did we just hose ourselves? if so, we'd best not stick around here */
972 if (mode_action_destroy)
973 return;
974
975 /* If there is another action in this series, (now that
976 * we're sure we didn't already die) perform it
977 */
978 if (action->next)
979 handleAction(ewin, action->next);
980 }
981
982 int
ActionclassEvent(ActionClass * ac,XEvent * ev,EWin * ewin)983 ActionclassEvent(ActionClass * ac, XEvent * ev, EWin * ewin)
984 {
985 EX_KeyCode keycode;
986 int i, type, button, modifiers, ok, mouse, mask, val = 0;
987 Action *aa;
988
989 if (ewin && ewin->state.inhibit_actions)
990 return 0;
991
992 keycode = type = button = modifiers = mouse = 0;
993
994 mask = Mode.masks.mod_key_mask;
995
996 switch (ev->type)
997 {
998 case KeyPress:
999 type = EVENT_KEY_DOWN;
1000 keycode = ev->xkey.keycode;
1001 modifiers = ev->xbutton.state & mask;
1002 mouse = 0;
1003 break;
1004 case KeyRelease:
1005 type = EVENT_KEY_UP;
1006 keycode = ev->xkey.keycode;
1007 modifiers = ev->xbutton.state & mask;
1008 mouse = 0;
1009 break;
1010 case ButtonPress:
1011 button = ev->xbutton.button;
1012 if (Mode.events.double_click && !(button == 4 || button == 5))
1013 type = EVENT_DOUBLE_DOWN;
1014 else
1015 type = EVENT_MOUSE_DOWN;
1016 modifiers = ev->xbutton.state & mask;
1017 mouse = 1;
1018 break;
1019 case ButtonRelease:
1020 type = EVENT_MOUSE_UP;
1021 button = ev->xbutton.button;
1022 modifiers = ev->xbutton.state & mask;
1023 mouse = 1;
1024 break;
1025 case EnterNotify:
1026 type = EVENT_MOUSE_ENTER;
1027 button = -1;
1028 modifiers = ev->xcrossing.state & mask;
1029 mouse = 1;
1030 break;
1031 case LeaveNotify:
1032 /* If frame window, quit if pointer is still inside */
1033 if (ewin && ev->xcrossing.window == EoGetXwin(ewin) &&
1034 (ev->xcrossing.x >= 0 && ev->xcrossing.x < EoGetW(ewin) &&
1035 ev->xcrossing.y >= 0 && ev->xcrossing.y < EoGetH(ewin)))
1036 return 0;
1037 type = EVENT_MOUSE_LEAVE;
1038 button = -1;
1039 modifiers = ev->xcrossing.state & mask;
1040 mouse = 1;
1041 break;
1042 case FocusIn:
1043 type = EVENT_FOCUS_IN;
1044 button = -1;
1045 mouse = 1;
1046 break;
1047 case FocusOut:
1048 type = EVENT_FOCUS_OUT;
1049 button = -1;
1050 mouse = 1;
1051 break;
1052 default:
1053 break;
1054 }
1055
1056 mode_action_destroy = 0;
1057
1058 for (i = 0; i < ac->num; i++)
1059 {
1060 if (!mode_action_destroy)
1061 {
1062 aa = ac->actions[i];
1063 ok = 0;
1064 if ((aa->event == type) && (aa->action))
1065 {
1066 if (mouse)
1067 {
1068 if (button < 0)
1069 {
1070 if (aa->anymodifier)
1071 ok = 1;
1072 else if (aa->modifiers == modifiers)
1073 ok = 1;
1074 }
1075 else
1076 {
1077 if (aa->anymodifier)
1078 {
1079 if (aa->anybutton)
1080 ok = 1;
1081 else if (aa->button == button)
1082 ok = 1;
1083 }
1084 else if (aa->modifiers == modifiers)
1085 {
1086 if (aa->anybutton)
1087 ok = 1;
1088 else if (aa->button == button)
1089 ok = 1;
1090 }
1091 }
1092 }
1093 else
1094 {
1095 if (aa->anymodifier)
1096 {
1097 if (aa->anykey)
1098 ok = 1;
1099 else if (aa->keycode == keycode)
1100 ok = 1;
1101 }
1102 else if (aa->modifiers == modifiers)
1103 {
1104 if (aa->anykey)
1105 ok = 1;
1106 else if (aa->keycode == keycode)
1107 ok = 1;
1108 }
1109 }
1110 if (ok)
1111 {
1112 handleAction(ewin, aa->action);
1113 val = 1;
1114 }
1115 }
1116 }
1117 if (mode_action_destroy)
1118 break;
1119 }
1120
1121 mode_action_destroy = 0;
1122
1123 return val;
1124 }
1125
1126 int
ActionclassesGlobalEvent(XEvent * ev)1127 ActionclassesGlobalEvent(XEvent * ev)
1128 {
1129 ActionClass *ac;
1130 int match;
1131
1132 match = 0;
1133 LIST_FOR_EACH(ActionClass, &aclass_list_global, ac)
1134 match |= ActionclassEvent(ac, ev, GetFocusEwin());
1135
1136 return match;
1137 }
1138
1139 static Timer *ac_reload_timer = NULL;
1140
1141 static int
_ac_reload(void * data __UNUSED__)1142 _ac_reload(void *data __UNUSED__)
1143 {
1144 AclassConfigLoadConfig("bindings.cfg");
1145 ac_reload_timer = NULL;
1146 return 0;
1147 }
1148
1149 void
ActionclassesReload(void)1150 ActionclassesReload(void)
1151 {
1152 TIMER_DEL(ac_reload_timer);
1153 TIMER_ADD(ac_reload_timer, 200, _ac_reload, NULL);
1154 }
1155
1156 /*
1157 * Actions module
1158 */
1159
1160 static void
AclassSighan(int sig,void * prm __UNUSED__)1161 AclassSighan(int sig, void *prm __UNUSED__)
1162 {
1163 switch (sig)
1164 {
1165 case ESIGNAL_INIT:
1166 AclassConfigLoadConfig("bindings.cfg");
1167 break;
1168 }
1169 }
1170
1171 static void
AclassIpc(const char * params)1172 AclassIpc(const char *params)
1173 {
1174 const char *p;
1175 char cmd[128], prm[4096];
1176 int len;
1177 ActionClass *ac;
1178
1179 cmd[0] = prm[0] = '\0';
1180 p = params;
1181 if (p)
1182 {
1183 len = 0;
1184 sscanf(p, "%100s %4000s %n", cmd, prm, &len);
1185 p += len;
1186 }
1187
1188 if (!p || cmd[0] == '\0' || cmd[0] == '?')
1189 {
1190 }
1191 else if (!strncmp(cmd, "kb", 2))
1192 {
1193 if (!strcmp(prm, "set"))
1194 AclassConfigParseIpc(p);
1195 else
1196 AclassConfigWrite(ActionclassFindGlobal("KEYBINDINGS"), IpcPrintf);
1197 }
1198 else if (!strncmp(cmd, "list", 2))
1199 {
1200 if (prm[0] == '\0')
1201 {
1202 IpcPrintf("Normal:\n");
1203 LIST_FOR_EACH(ActionClass, &aclass_list, ac)
1204 IpcPrintf("%s\n", ac->name);
1205 IpcPrintf("Global:\n");
1206 LIST_FOR_EACH(ActionClass, &aclass_list_global, ac)
1207 IpcPrintf("%s\n", ac->name);
1208 }
1209 else if (!strcmp(prm, "all"))
1210 {
1211 LIST_FOR_EACH(ActionClass, &aclass_list, ac)
1212 {
1213 IpcPrintf("\n");
1214 AclassConfigWrite(ac, IpcPrintf);
1215 }
1216 LIST_FOR_EACH(ActionClass, &aclass_list_global, ac)
1217 {
1218 IpcPrintf("\n");
1219 AclassConfigWrite(ac, IpcPrintf);
1220 }
1221 }
1222 else
1223 {
1224 AclassConfigWrite(ActionclassFindAny(prm), IpcPrintf);
1225 }
1226 }
1227 else if (!strcmp(cmd, "load"))
1228 {
1229 if (*prm == '\0')
1230 AclassConfigLoadConfig("bindings.cfg");
1231 else
1232 AclassConfigLoadConfig(prm);
1233 }
1234 }
1235
1236 static const IpcItem AclassIpcArray[] = {
1237 {
1238 AclassIpc,
1239 "aclass", "ac",
1240 "Action class functions",
1241 " aclass kb List key bindings\n"
1242 " aclass kb set ... Set key bindings\n"
1243 " aclass list [name/all] List action class[es]\n"
1244 " aclass load [name] Reload action classes (default is bindings.cfg)\n"}
1245 };
1246
1247 /*
1248 * Module descriptor
1249 */
1250 extern const EModule ModAclass;
1251
1252 const EModule ModAclass = {
1253 "aclass", "ac",
1254 AclassSighan,
1255 MOD_ITEMS(AclassIpcArray),
1256 {0, NULL}
1257 };
1258
1259 void
GrabButtonGrabs(Win win)1260 GrabButtonGrabs(Win win)
1261 {
1262 ActionClass *ac;
1263 int j;
1264 Action *aa;
1265 unsigned int mod, button, mask;
1266
1267 ac = ActionclassFind("BUTTONBINDINGS");
1268 if (!ac)
1269 return;
1270
1271 ac->ref_count++;
1272 for (j = 0; j < ac->num; j++)
1273 {
1274 aa = ac->actions[j];
1275 if ((!aa) || ((aa->event != EVENT_MOUSE_DOWN) &&
1276 (aa->event != EVENT_MOUSE_UP)))
1277 continue;
1278
1279 mod = (aa->anymodifier) ? AnyModifier : aa->modifiers;
1280 button = (aa->anybutton) ? AnyButton : aa->button;
1281 mask = ButtonPressMask | ButtonReleaseMask;
1282
1283 GrabButtonSet(button, mod, win, mask, ECSR_PGRAB, 1);
1284 }
1285 }
1286
1287 void
UnGrabButtonGrabs(Win win)1288 UnGrabButtonGrabs(Win win)
1289 {
1290 ActionClass *ac;
1291 int j;
1292 Action *aa;
1293 unsigned int mod, button;
1294
1295 ac = ActionclassFind("BUTTONBINDINGS");
1296 if (!ac)
1297 return;
1298
1299 ac->ref_count--;
1300 for (j = 0; j < ac->num; j++)
1301 {
1302 aa = ac->actions[j];
1303 if ((!aa) || ((aa->event != EVENT_MOUSE_DOWN)
1304 && (aa->event != EVENT_MOUSE_UP)))
1305 continue;
1306
1307 mod = (aa->anymodifier) ? AnyModifier : aa->modifiers;
1308 button = (aa->anybutton) ? AnyButton : aa->button;
1309
1310 GrabButtonRelease(button, mod, win);
1311 }
1312 }
1313
1314 static void
GrabActionKey(Action * aa)1315 GrabActionKey(Action * aa)
1316 {
1317 int mod;
1318
1319 if (!aa || !aa->keycode)
1320 return;
1321
1322 mod = (aa->anymodifier) ? AnyModifier : aa->modifiers;
1323
1324 GrabKeySet(aa->keycode, mod, VROOT);
1325 }
1326
1327 static void
UnGrabActionKey(Action * aa)1328 UnGrabActionKey(Action * aa)
1329 {
1330 int mod;
1331
1332 if (!aa->keycode)
1333 return;
1334
1335 mod = (aa->anymodifier) ? AnyModifier : aa->modifiers;
1336
1337 GrabKeyRelease(aa->keycode, mod, VROOT);
1338 }
1339