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