Copy purify.fvwm2rc to /tmp - add instructions in README.
[fvwm.git] / fvwm / functions.c
blobcf907f76b14807144bed7b7aa313800ca1400ab5
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 /* This module is all original code
18 * by Rob Nation
19 * Copyright 1993, Robert Nation
20 * You may use this code for any purpose, as long as the original
21 * copyright remains in the source code and all documentation */
24 * fvwm built-in functions and complex functions
27 /* ---------------------------- included header files ---------------------- */
29 #include "config.h"
31 #include <stdio.h>
33 #include <X11/keysym.h>
35 #include "libs/fvwmlib.h"
36 #include "libs/charmap.h"
37 #include "libs/wcontext.h"
38 #include "libs/Grab.h"
39 #include "libs/Parse.h"
40 #include "libs/Strings.h"
41 #include "libs/Event.h"
42 #include "fvwm.h"
43 #include "externs.h"
44 #include "cursor.h"
45 #include "execcontext.h"
46 #include "functions.h"
47 #include "commands.h"
48 #include "functable.h"
49 #include "events.h"
50 #include "modconf.h"
51 #include "module_list.h"
52 #include "misc.h"
53 #include "screen.h"
54 #include "repeat.h"
55 #include "expand.h"
56 #include "menus.h"
58 /* ---------------------------- local definitions -------------------------- */
60 /* ---------------------------- local macros ------------------------------- */
62 /* ---------------------------- imports ------------------------------------ */
64 /* ---------------------------- included code files ------------------------ */
66 /* ---------------------------- local types -------------------------------- */
68 typedef struct FunctionItem
70 struct FvwmFunction *func; /* the function this item is in */
71 struct FunctionItem *next_item; /* next function item */
72 char condition; /* the character string displayed on
73 * left*/
74 char *action; /* action to be performed */
75 short type; /* type of built in function */
76 FUNC_FLAGS_TYPE flags;
77 } FunctionItem;
79 typedef struct FvwmFunction
81 struct FvwmFunction *next_func; /* next in list of root menus */
82 FunctionItem *first_item; /* first item in function */
83 FunctionItem *last_item; /* last item in function */
84 char *name; /* function name */
85 int use_depth;
86 } FvwmFunction;
88 /* Types of events for the FUNCTION builtin */
89 typedef enum
91 CF_IMMEDIATE = 'i',
92 CF_MOTION = 'm',
93 CF_HOLD = 'h',
94 CF_CLICK = 'c',
95 CF_DOUBLE_CLICK = 'd',
96 CF_TIMEOUT = '-'
97 } cfunc_action_t;
99 /* ---------------------------- forward declarations ----------------------- */
101 static void execute_complex_function(
102 cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
103 Bool *desperate, Bool has_ref_window_moved);
105 /* ---------------------------- local variables ---------------------------- */
107 /* ---------------------------- exported variables (globals) --------------- */
109 /* ---------------------------- local functions ---------------------------- */
111 static int __context_has_window(
112 const exec_context_t *exc, execute_flags_t flags)
114 if (exc->w.fw != NULL)
116 return 1;
118 else if ((flags & FUNC_ALLOW_UNMANAGED) && exc->w.w != None)
120 return 1;
123 return 0;
127 * Defer the execution of a function to the next button press if the context is
128 * C_ROOT
130 * Inputs:
131 * cursor - the cursor to display while waiting
133 static Bool DeferExecution(
134 exec_context_changes_t *ret_ecc, exec_context_change_mask_t *ret_mask,
135 cursor_t cursor, int trigger_evtype, int do_allow_unmanaged)
137 int done;
138 int finished = 0;
139 int just_waiting_for_finish = 0;
140 Window dummy;
141 Window original_w;
142 static XEvent e;
143 Window w;
144 int wcontext;
145 FvwmWindow *fw;
146 int FinishEvent;
148 fw = ret_ecc->w.fw;
149 w = ret_ecc->w.w;
150 original_w = w;
151 wcontext = ret_ecc->w.wcontext;
152 FinishEvent = ((fw != NULL) ? ButtonRelease : ButtonPress);
153 if (wcontext == C_UNMANAGED && do_allow_unmanaged)
155 return False;
157 if (wcontext != C_ROOT && wcontext != C_NO_CONTEXT && fw != NULL &&
158 wcontext != C_EWMH_DESKTOP)
160 if (FinishEvent == ButtonPress ||
161 (FinishEvent == ButtonRelease &&
162 trigger_evtype != ButtonPress))
164 return False;
166 else if (FinishEvent == ButtonRelease)
168 /* We are only waiting until the user releases the
169 * button. Do not change the cursor. */
170 cursor = CRS_NONE;
171 just_waiting_for_finish = 1;
174 if (Scr.flags.are_functions_silent)
176 return True;
178 if (!GrabEm(cursor, GRAB_NORMAL))
180 XBell(dpy, 0);
181 return True;
183 MyXGrabKeyboard(dpy);
184 while (!finished)
186 done = 0;
187 /* block until there is an event */
188 FMaskEvent(
189 dpy, ButtonPressMask | ButtonReleaseMask |
190 ExposureMask | KeyPressMask | VisibilityChangeMask |
191 ButtonMotionMask | PointerMotionMask
192 /* | EnterWindowMask | LeaveWindowMask*/, &e);
194 if (e.type == KeyPress)
196 KeySym keysym = XLookupKeysym(&e.xkey, 0);
197 if (keysym == XK_Escape)
199 ret_ecc->x.etrigger = &e;
200 *ret_mask |= ECC_ETRIGGER;
201 UngrabEm(GRAB_NORMAL);
202 MyXUngrabKeyboard(dpy);
203 return True;
205 Keyboard_shortcuts(&e, NULL, NULL, NULL, FinishEvent);
207 if (e.type == FinishEvent)
209 finished = 1;
211 switch (e.type)
213 case KeyPress:
214 case ButtonPress:
215 if (e.type != FinishEvent)
217 original_w = e.xany.window;
219 done = 1;
220 break;
221 case ButtonRelease:
222 done = 1;
223 break;
224 default:
225 break;
227 if (!done)
229 dispatch_event(&e);
232 MyXUngrabKeyboard(dpy);
233 UngrabEm(GRAB_NORMAL);
234 if (just_waiting_for_finish)
236 return False;
238 w = e.xany.window;
239 ret_ecc->x.etrigger = &e;
240 *ret_mask |= ECC_ETRIGGER | ECC_W | ECC_WCONTEXT;
241 if ((w == Scr.Root || w == Scr.NoFocusWin) &&
242 e.xbutton.subwindow != None)
244 w = e.xbutton.subwindow;
245 e.xany.window = w;
247 if (w == Scr.Root || IS_EWMH_DESKTOP(w))
249 ret_ecc->w.w = w;
250 ret_ecc->w.wcontext = C_ROOT;
251 XBell(dpy, 0);
252 return True;
254 *ret_mask |= ECC_FW;
255 if (XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
257 ret_ecc->w.fw = NULL;
258 ret_ecc->w.w = w;
259 ret_ecc->w.wcontext = C_ROOT;
260 XBell(dpy, 0);
261 return (True);
263 if (w == FW_W_PARENT(fw))
265 w = FW_W(fw);
267 if (original_w == FW_W_PARENT(fw))
269 original_w = FW_W(fw);
271 /* this ugly mess attempts to ensure that the release and press
272 * are in the same window. */
273 if (w != original_w && original_w != Scr.Root &&
274 original_w != None && original_w != Scr.NoFocusWin &&
275 !IS_EWMH_DESKTOP(original_w))
277 if (w != FW_W_FRAME(fw) || original_w != FW_W(fw))
279 ret_ecc->w.fw = fw;
280 ret_ecc->w.w = w;
281 ret_ecc->w.wcontext = C_ROOT;
282 XBell(dpy, 0);
283 return True;
287 if (IS_EWMH_DESKTOP(FW_W(fw)))
289 ret_ecc->w.fw = fw;
290 ret_ecc->w.w = w;
291 ret_ecc->w.wcontext = C_ROOT;
292 XBell(dpy, 0);
293 return True;
295 wcontext = GetContext(NULL, fw, &e, &dummy);
296 ret_ecc->w.fw = fw;
297 ret_ecc->w.w = w;
298 ret_ecc->w.wcontext = C_ROOT;
300 return False;
304 ** do binary search on func list
306 static int func_comp(const void *a, const void *b)
308 return (strcmp((char *)a, ((func_t *)b)->keyword));
311 static const func_t *find_builtin_function(char *func)
313 static int nfuncs = 0;
314 func_t *ret_func;
315 char *temp;
316 char *s;
318 if (!func || func[0] == 0)
320 return NULL;
323 /* since a lot of lines in a typical rc are probably menu/func
324 * continues: */
325 if (func[0]=='+' || (func[0] == ' ' && func[1] == '+'))
327 return &(func_table[0]);
330 temp = safestrdup(func);
331 for (s = temp; *s != 0; s++)
333 if (isupper(*s))
335 *s = tolower(*s);
338 if (nfuncs == 0)
340 for ( ; (func_table[nfuncs]).action != NULL; nfuncs++)
342 /* nothing to do here */
345 ret_func = (func_t *)bsearch(
346 temp, func_table, nfuncs, sizeof(func_t), func_comp);
347 free(temp);
349 return ret_func;
352 static void __execute_function(
353 cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
354 FUNC_FLAGS_TYPE exec_flags, char *args[], Bool has_ref_window_moved)
356 static int func_depth = 0;
357 cond_rc_t *func_rc = NULL;
358 cond_rc_t dummy_rc;
359 Window w;
360 int j;
361 char *function;
362 char *taction;
363 char *trash;
364 char *trash2;
365 char *expaction = NULL;
366 char *arguments[11];
367 const func_t *bif;
368 Bool set_silent;
369 Bool must_free_string = False;
370 Bool must_free_function = False;
371 Bool do_keep_rc = False;
372 /* needed to be able to avoid resize to use moved windows for base */
373 extern Window PressedW;
374 Window dummy_w;
376 if (!action)
378 return;
380 /* ignore whitespace at the beginning of all config lines */
381 action = SkipSpaces(action, NULL, 0);
382 if (!action || action[0] == 0)
384 /* impossibly short command */
385 return;
387 if (action[0] == '#')
389 /* a comment */
390 return;
393 func_depth++;
394 if (func_depth > MAX_FUNCTION_DEPTH)
396 fvwm_msg(
397 ERR, "__execute_function",
398 "Function '%s' called with a depth of %i, "
399 "stopping function execution!",
400 action, func_depth);
401 func_depth--;
402 return;
404 if (args)
406 for (j = 0; j < 11; j++)
408 arguments[j] = args[j];
411 else
413 for (j = 0; j < 11; j++)
415 arguments[j] = NULL;
419 if (exc->w.fw == NULL || IS_EWMH_DESKTOP(FW_W(exc->w.fw)))
421 if (exec_flags & FUNC_IS_UNMANAGED)
423 w = exc->w.w;
425 else
427 w = Scr.Root;
430 else
432 FvwmWindow *tw;
434 w = GetSubwindowFromEvent(dpy, exc->x.elast);
435 if (w == None)
437 w = exc->x.elast->xany.window;
439 tw = NULL;
440 if (w != None)
442 if (XFindContext(
443 dpy, w, FvwmContext, (caddr_t *)&tw) ==
444 XCNOENT)
446 tw = NULL;
449 if (w == None || tw != exc->w.fw)
451 w = FW_W(exc->w.fw);
455 set_silent = False;
456 if (action[0] == '-')
458 exec_flags |= FUNC_DONT_EXPAND_COMMAND;
459 action++;
462 taction = action;
463 /* parse prefixes */
464 trash = PeekToken(taction, &trash2);
465 while (trash)
467 if (StrEquals(trash, PRE_SILENT))
469 if (Scr.flags.are_functions_silent == 0)
471 set_silent = 1;
472 Scr.flags.are_functions_silent = 1;
474 taction = trash2;
475 trash = PeekToken(taction, &trash2);
477 else if (StrEquals(trash, PRE_KEEPRC))
479 do_keep_rc = True;
480 taction = trash2;
481 trash = PeekToken(taction, &trash2);
483 else
485 break;
488 if (taction == NULL)
490 if (set_silent)
492 Scr.flags.are_functions_silent = 0;
494 func_depth--;
495 return;
497 if (cond_rc == NULL || do_keep_rc == True)
499 condrc_init(&dummy_rc);
500 func_rc = &dummy_rc;
502 else
504 func_rc = cond_rc;
507 GetNextToken(taction, &function);
508 if (function)
510 char *tmp = function;
511 function = expand_vars(
512 function, arguments, False, False, func_rc, exc);
513 free(tmp);
515 if (function && function[0] != '*')
517 #if 1
518 /* DV: with this piece of code it is impossible to have a
519 * complex function with embedded whitespace that begins with a
520 * builtin function name, e.g. a function "echo hello". */
521 /* DV: ... and without it some of the complex functions will
522 * fail */
523 char *tmp = function;
525 while (*tmp && !isspace(*tmp))
527 tmp++;
529 *tmp = 0;
530 #endif
531 bif = find_builtin_function(function);
532 must_free_function = True;
534 else
536 bif = NULL;
537 if (function)
539 free(function);
541 function = "";
544 #ifdef USEDECOR
545 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor &&
546 (!bif || !(bif->flags & FUNC_DECOR)))
548 fvwm_msg(
549 ERR, "__execute_function",
550 "Command can not be added to a decor; executing"
551 " command now: '%s'", action);
553 #endif
555 if (!(exec_flags & FUNC_DONT_EXPAND_COMMAND))
557 expaction = expand_vars(
558 taction, arguments, (bif) ?
559 !!(bif->flags & FUNC_ADD_TO) :
560 False, (taction[0] == '*'), func_rc, exc);
561 if (func_depth <= 1)
563 must_free_string = set_repeat_data(
564 expaction, REPEAT_COMMAND, bif);
566 else
568 must_free_string = True;
571 else
573 expaction = taction;
576 #ifdef FVWM_COMMAND_LOG
577 fvwm_msg(INFO, "LOG", "%c: %s", (char)exc->type, expaction);
578 #endif
580 /* Note: the module config command, "*" can not be handled by the
581 * regular command table because there is no required white space after
582 * the asterisk. */
583 if (expaction[0] == '*')
585 #ifdef USEDECOR
586 if (Scr.cur_decor && Scr.cur_decor != &Scr.DefaultDecor)
588 fvwm_msg(
589 WARN, "__execute_function",
590 "Command can not be added to a decor;"
591 " executing command now: '%s'", expaction);
593 #endif
594 /* process a module config command */
595 ModuleConfig(expaction);
597 else
599 const exec_context_t *exc2;
600 exec_context_changes_t ecc;
601 exec_context_change_mask_t mask;
603 mask = (w != exc->w.w) ? ECC_W : 0;
604 ecc.w.fw = exc->w.fw;
605 ecc.w.w = w;
606 ecc.w.wcontext = exc->w.wcontext;
607 if (bif && bif->func_t != F_FUNCTION)
609 char *runaction;
610 Bool rc = False;
612 runaction = SkipNTokens(expaction, 1);
613 if ((bif->flags & FUNC_NEEDS_WINDOW) &&
614 !(exec_flags & FUNC_DONT_DEFER))
616 rc = DeferExecution(
617 &ecc, &mask, bif->cursor,
618 exc->x.elast->type,
619 (bif->flags & FUNC_ALLOW_UNMANAGED));
621 else if ((bif->flags & FUNC_NEEDS_WINDOW) &&
622 !__context_has_window(
623 exc,
624 bif->flags & FUNC_ALLOW_UNMANAGED))
626 /* no context window and not allowed to defer,
627 * skip command */
628 rc = True;
630 if (rc == False)
632 exc2 = exc_clone_context(exc, &ecc, mask);
633 if (has_ref_window_moved &&
634 (bif->func_t == F_ANIMATED_MOVE ||
635 bif->func_t == F_MOVE ||
636 bif->func_t == F_RESIZE))
638 dummy_w = PressedW;
639 PressedW = None;
640 bif->action(func_rc, exc2, runaction);
641 PressedW = dummy_w;
643 else
645 bif->action(func_rc, exc2, runaction);
647 exc_destroy_context(exc2);
650 else
652 Bool desperate = 1;
653 char *runaction;
655 if (bif)
657 /* strip "function" command */
658 runaction = SkipNTokens(expaction, 1);
660 else
662 runaction = expaction;
664 exc2 = exc_clone_context(exc, &ecc, mask);
665 execute_complex_function(
666 func_rc, exc2, runaction, &desperate,
667 has_ref_window_moved);
668 if (!bif && desperate)
670 if (executeModuleDesperate(
671 func_rc, exc, runaction) == NULL &&
672 *function != 0 && !set_silent)
674 fvwm_msg(
675 ERR, "__execute_function",
676 "No such command '%s'",
677 function);
680 exc_destroy_context(exc2);
684 if (set_silent)
686 Scr.flags.are_functions_silent = 0;
688 if (cond_rc != NULL)
690 cond_rc->break_levels = func_rc->break_levels;
692 if (must_free_string)
694 free(expaction);
696 if (must_free_function)
698 free(function);
700 func_depth--;
702 return;
705 /* find_complex_function expects a token as the input. Make sure you have used
706 * GetNextToken before passing a function name to remove quotes */
707 static FvwmFunction *find_complex_function(const char *function_name)
709 FvwmFunction *func;
711 if (function_name == NULL || *function_name == 0)
713 return NULL;
715 func = Scr.functions;
716 while (func != NULL)
718 if (func->name != NULL)
720 if (strcasecmp(function_name, func->name) == 0)
722 return func;
725 func = func->next_func;
728 return NULL;
732 * Builtin which determines if the button press was a click or double click...
733 * Waits Scr.ClickTime, or until it is evident that the user is not
734 * clicking, but is moving the cursor
736 static cfunc_action_t CheckActionType(
737 int x, int y, XEvent *d, Bool may_time_out, Bool is_button_pressed,
738 int *ret_button)
740 int xcurrent,ycurrent,total = 0;
741 Time t0;
742 int dist;
743 Bool do_sleep = False;
745 xcurrent = x;
746 ycurrent = y;
747 t0 = fev_get_evtime();
748 dist = Scr.MoveThreshold;
750 while ((total < Scr.ClickTime &&
751 fev_get_evtime() - t0 < Scr.ClickTime) || !may_time_out)
753 if (!(x - xcurrent <= dist && xcurrent - x <= dist &&
754 y - ycurrent <= dist && ycurrent - y <= dist))
756 return (is_button_pressed) ? CF_MOTION : CF_TIMEOUT;
759 if (do_sleep)
761 usleep(20000);
763 else
765 usleep(1);
766 do_sleep = 1;
768 total += 20;
769 if (FCheckMaskEvent(
770 dpy, ButtonReleaseMask|ButtonMotionMask|
771 PointerMotionMask|ButtonPressMask|ExposureMask, d))
773 do_sleep = 0;
774 switch (d->xany.type)
776 case ButtonRelease:
777 *ret_button = d->xbutton.button;
778 return CF_CLICK;
779 case MotionNotify:
780 if (d->xmotion.same_screen == False)
782 break;
784 if ((d->xmotion.state &
785 DEFAULT_ALL_BUTTONS_MASK) ||
786 !is_button_pressed)
788 xcurrent = d->xmotion.x_root;
789 ycurrent = d->xmotion.y_root;
791 else
793 return CF_CLICK;
795 break;
796 case ButtonPress:
797 *ret_button = d->xbutton.button;
798 if (may_time_out)
800 is_button_pressed = True;
802 break;
803 case Expose:
804 /* must handle expose here so that raising a
805 * window with "I" works */
806 dispatch_event(d);
807 break;
808 default:
809 /* can't happen */
810 break;
815 return (is_button_pressed) ? CF_HOLD : CF_TIMEOUT;
818 static void __run_complex_function_items(
819 cond_rc_t *cond_rc, char cond, FvwmFunction *func,
820 const exec_context_t *exc, char *args[], Bool has_ref_window_moved)
822 char c;
823 FunctionItem *fi;
824 int x0, y0, x, y;
825 extern Window PressedW;
827 if (!(!has_ref_window_moved && PressedW && XTranslateCoordinates(
828 dpy, PressedW , Scr.Root, 0, 0, &x0, &y0,
829 &JunkChild)))
831 x0 = y0 = 0;
834 for (fi = func->first_item; fi != NULL && cond_rc->break_levels == 0; )
836 /* make lower case */
837 c = fi->condition;
838 if (isupper(c))
840 c = tolower(c);
842 if (c == cond)
844 __execute_function(
845 cond_rc, exc, fi->action, FUNC_DONT_DEFER,
846 args, has_ref_window_moved);
847 if (!has_ref_window_moved && PressedW &&
848 XTranslateCoordinates(
849 dpy, PressedW , Scr.Root, 0, 0, &x, &y,
850 &JunkChild))
852 has_ref_window_moved =(x != x0 || y != y0);
855 fi = fi->next_item;
858 return;
861 static void __cf_cleanup(
862 int *depth, char **arguments, cond_rc_t *cond_rc)
864 int i;
866 (*depth)--;
867 if (!(*depth))
869 Scr.flags.is_executing_complex_function = 0;
871 for (i = 0; i < 11; i++)
873 if (arguments[i] != NULL)
875 free(arguments[i]);
878 if (cond_rc->break_levels > 0)
880 cond_rc->break_levels--;
883 return;
886 static void execute_complex_function(
887 cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
888 Bool *desperate, Bool has_ref_window_moved)
890 cond_rc_t tmp_rc;
891 cfunc_action_t type = CF_MOTION;
892 char c;
893 FunctionItem *fi;
894 Bool Persist = False;
895 Bool HaveDoubleClick = False;
896 Bool HaveHold = False;
897 Bool NeedsTarget = False;
898 Bool ImmediateNeedsTarget = False;
899 int do_allow_unmanaged = FUNC_ALLOW_UNMANAGED;
900 int do_allow_unmanaged_immediate = FUNC_ALLOW_UNMANAGED;
901 char *arguments[11], *taction;
902 char *func_name;
903 int x, y ,i;
904 XEvent d;
905 FvwmFunction *func;
906 static int depth = 0;
907 const exec_context_t *exc2;
908 exec_context_changes_t ecc;
909 exec_context_change_mask_t mask;
910 int trigger_evtype;
911 int button;
912 XEvent *te;
914 if (cond_rc == NULL)
916 condrc_init(&tmp_rc);
917 cond_rc = &tmp_rc;
919 cond_rc->rc = COND_RC_OK;
920 mask = 0;
921 d.type = 0;
922 ecc.w.fw = exc->w.fw;
923 ecc.w.w = exc->w.w;
924 ecc.w.wcontext = exc->w.wcontext;
925 /* find_complex_function expects a token, not just a quoted string */
926 func_name = PeekToken(action, &taction);
927 if (!func_name)
929 return;
931 func = find_complex_function(func_name);
932 if (func == NULL)
934 if (*desperate == 0)
936 fvwm_msg(
937 ERR, "ComplexFunction", "No such function %s",
938 action);
940 return;
942 if (!depth)
944 Scr.flags.is_executing_complex_function = 1;
946 depth++;
947 *desperate = 0;
948 /* duplicate the whole argument list for use as '$*' */
949 if (taction)
951 arguments[0] = safestrdup(taction);
952 /* strip trailing newline */
953 if (arguments[0][0])
955 int l= strlen(arguments[0]);
957 if (arguments[0][l - 1] == '\n')
959 arguments[0][l - 1] = 0;
962 /* Get the argument list */
963 for (i = 1; i < 11; i++)
965 taction = GetNextToken(taction, &arguments[i]);
968 else
970 for (i = 0; i < 11; i++)
972 arguments[i] = NULL;
975 /* In case we want to perform an action on a button press, we
976 * need to fool other routines */
977 te = exc->x.elast;
978 if (te->type == ButtonPress)
980 trigger_evtype = ButtonRelease;
982 else
984 trigger_evtype = te->type;
986 func->use_depth++;
988 for (fi = func->first_item; fi != NULL; fi = fi->next_item)
990 if (fi->flags & FUNC_NEEDS_WINDOW)
992 NeedsTarget = True;
993 do_allow_unmanaged &= fi->flags;
994 if (fi->condition == CF_IMMEDIATE)
996 do_allow_unmanaged_immediate &= fi->flags;
997 ImmediateNeedsTarget = True;
998 break;
1003 if (ImmediateNeedsTarget)
1005 if (DeferExecution(
1006 &ecc, &mask, CRS_SELECT, trigger_evtype,
1007 do_allow_unmanaged_immediate))
1009 func->use_depth--;
1010 __cf_cleanup(&depth, arguments, cond_rc);
1011 return;
1013 NeedsTarget = False;
1015 else
1017 ecc.w.w = (ecc.w.fw) ? FW_W_FRAME(ecc.w.fw) : None;
1018 mask |= ECC_W;
1021 /* we have to grab buttons before executing immediate actions because
1022 * these actions can move the window away from the pointer so that a
1023 * button release would go to the application below. */
1024 if (!GrabEm(CRS_NONE, GRAB_NORMAL))
1026 func->use_depth--;
1027 fvwm_msg(
1028 ERR,
1029 "ComplexFunction", "Grab failed in function %s,"
1030 " unable to execute immediate action", action);
1031 __cf_cleanup(&depth, arguments, cond_rc);
1032 return;
1034 exc2 = exc_clone_context(exc, &ecc, mask);
1035 __run_complex_function_items(
1036 cond_rc, CF_IMMEDIATE, func, exc2, arguments,
1037 has_ref_window_moved);
1038 exc_destroy_context(exc2);
1039 for (fi = func->first_item;
1040 fi != NULL && cond_rc->break_levels == 0;
1041 fi = fi->next_item)
1043 /* c is already lowercase here */
1044 c = fi->condition;
1045 switch (c)
1047 case CF_IMMEDIATE:
1048 break;
1049 case CF_DOUBLE_CLICK:
1050 HaveDoubleClick = True;
1051 Persist = True;
1052 break;
1053 case CF_HOLD:
1054 HaveHold = True;
1055 Persist = True;
1056 break;
1057 default:
1058 Persist = True;
1059 break;
1063 if (!Persist || cond_rc->break_levels != 0)
1065 func->use_depth--;
1066 __cf_cleanup(&depth, arguments, cond_rc);
1067 UngrabEm(GRAB_NORMAL);
1068 return;
1071 /* Only defer execution if there is a possibility of needing
1072 * a window to operate on */
1073 if (NeedsTarget)
1075 if (DeferExecution(
1076 &ecc, &mask, CRS_SELECT, trigger_evtype,
1077 do_allow_unmanaged))
1079 func->use_depth--;
1080 __cf_cleanup(&depth, arguments, cond_rc);
1081 UngrabEm(GRAB_NORMAL);
1082 return;
1086 te = (mask & ECC_ETRIGGER) ? ecc.x.etrigger : exc->x.elast;
1087 switch (te->xany.type)
1089 case ButtonPress:
1090 case ButtonRelease:
1091 x = te->xbutton.x_root;
1092 y = te->xbutton.y_root;
1093 button = te->xbutton.button;
1094 /* Take the click which started this fuction off the
1095 * Event queue. -DDN- Dan D Niles dniles@iname.com */
1096 FCheckMaskEvent(dpy, ButtonPressMask, &d);
1097 break;
1098 default:
1099 if (FQueryPointer(
1100 dpy, Scr.Root, &JunkRoot, &JunkChild, &x, &y,
1101 &JunkX, &JunkY, &JunkMask) == False)
1103 /* pointer is on a different screen */
1104 x = 0;
1105 y = 0;
1107 button = 0;
1108 break;
1111 /* Wait and see if we have a click, or a move */
1112 /* wait forever, see if the user releases the button */
1113 type = CheckActionType(x, y, &d, HaveHold, True, &button);
1114 if (type == CF_CLICK)
1116 int button2;
1118 /* If it was a click, wait to see if its a double click */
1119 if (HaveDoubleClick)
1121 type = CheckActionType(
1122 x, y, &d, True, False, &button2);
1123 switch (type)
1125 case CF_HOLD:
1126 case CF_MOTION:
1127 case CF_CLICK:
1128 if (button == button2)
1130 type = CF_DOUBLE_CLICK;
1132 else
1134 type = CF_CLICK;
1136 break;
1137 case CF_TIMEOUT:
1138 type = CF_CLICK;
1139 break;
1140 default:
1141 /* can't happen */
1142 break;
1146 else if (type == CF_TIMEOUT)
1148 type = CF_HOLD;
1151 /* some functions operate on button release instead of presses. These
1152 * gets really weird for complex functions ... */
1153 if (d.type == ButtonPress)
1155 d.type = ButtonRelease;
1156 if (d.xbutton.button > 0 &&
1157 d.xbutton.button <= NUMBER_OF_MOUSE_BUTTONS)
1159 d.xbutton.state &=
1160 (~(Button1Mask >> (d.xbutton.button - 1)));
1164 #ifdef BUGGY_CODE
1165 /* domivogt (11-Apr-2000): The pointer ***must not*** be ungrabbed
1166 * here. If it is, any window that the mouse enters during the
1167 * function will receive MotionNotify events with a button held down!
1168 * The results are unpredictable. E.g. rxvt interprets the
1169 * ButtonMotion as user input to select text. */
1170 UngrabEm(GRAB_NORMAL);
1171 #endif
1172 fev_set_evpos(&d, x, y);
1173 fev_fake_event(&d);
1174 ecc.x.etrigger = &d;
1175 ecc.w.w = (ecc.w.fw) ? FW_W_FRAME(ecc.w.fw) : None;
1176 mask |= ECC_ETRIGGER | ECC_W;
1177 exc2 = exc_clone_context(exc, &ecc, mask);
1178 __run_complex_function_items(
1179 cond_rc, type, func, exc2, arguments, has_ref_window_moved);
1180 exc_destroy_context(exc2);
1181 /* This is the right place to ungrab the pointer (see comment above).
1183 func->use_depth--;
1184 __cf_cleanup(&depth, arguments, cond_rc);
1185 UngrabEm(GRAB_NORMAL);
1187 return;
1191 * create a new FvwmFunction
1193 static FvwmFunction *NewFvwmFunction(const char *name)
1195 FvwmFunction *tmp;
1197 tmp = (FvwmFunction *)safemalloc(sizeof(FvwmFunction));
1198 tmp->next_func = Scr.functions;
1199 tmp->first_item = NULL;
1200 tmp->last_item = NULL;
1201 tmp->name = stripcpy(name);
1202 tmp->use_depth = 0;
1203 Scr.functions = tmp;
1205 return tmp;
1208 static void DestroyFunction(FvwmFunction *func)
1210 FunctionItem *fi,*tmp2;
1211 FvwmFunction *tmp, *prev;
1213 if (func == NULL)
1215 return;
1218 tmp = Scr.functions;
1219 prev = NULL;
1220 while (tmp && tmp != func)
1222 prev = tmp;
1223 tmp = tmp->next_func;
1225 if (tmp != func)
1227 return;
1230 if (func->use_depth != 0)
1232 fvwm_msg(
1233 ERR,"DestroyFunction",
1234 "Function %s is in use (depth %d)", func->name,
1235 func->use_depth);
1236 return;
1239 if (prev == NULL)
1241 Scr.functions = func->next_func;
1243 else
1245 prev->next_func = func->next_func;
1248 free(func->name);
1250 fi = func->first_item;
1251 while (fi != NULL)
1253 tmp2 = fi->next_item;
1254 if (fi->action != NULL)
1256 free(fi->action);
1258 free(fi);
1259 fi = tmp2;
1261 free(func);
1263 return;
1266 /* ---------------------------- interface functions ------------------------ */
1268 Bool functions_is_complex_function(const char *function_name)
1270 if (find_complex_function(function_name) != NULL)
1272 return True;
1275 return False;
1278 void execute_function(
1279 cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
1280 FUNC_FLAGS_TYPE exec_flags)
1282 __execute_function(cond_rc, exc, action, exec_flags, NULL, False);
1284 return;
1287 void execute_function_override_wcontext(
1288 cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
1289 FUNC_FLAGS_TYPE exec_flags, int wcontext)
1291 const exec_context_t *exc2;
1292 exec_context_changes_t ecc;
1294 ecc.w.wcontext = wcontext;
1295 exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
1296 execute_function(cond_rc, exc2, action, exec_flags);
1297 exc_destroy_context(exc2);
1299 return;
1302 void execute_function_override_window(
1303 cond_rc_t *cond_rc, const exec_context_t *exc, char *action,
1304 FUNC_FLAGS_TYPE exec_flags, FvwmWindow *fw)
1306 const exec_context_t *exc2;
1307 exec_context_changes_t ecc;
1309 ecc.w.fw = fw;
1310 if (fw != NULL)
1312 ecc.w.w = FW_W(fw);
1313 ecc.w.wcontext = C_WINDOW;
1314 exec_flags |= FUNC_DONT_DEFER;
1316 else
1318 ecc.w.w = None;
1319 ecc.w.wcontext = C_ROOT;
1321 if (exc != NULL)
1323 exc2 = exc_clone_context(
1324 exc, &ecc, ECC_FW | ECC_W | ECC_WCONTEXT);
1326 else
1328 ecc.type = EXCT_NULL;
1329 exc2 = exc_create_context(
1330 &ecc, ECC_TYPE | ECC_FW | ECC_W | ECC_WCONTEXT);
1332 execute_function(cond_rc, exc2, action, exec_flags);
1333 exc_destroy_context(exc2);
1335 return;
1338 void find_func_t(char *action, short *func_t, unsigned char *flags)
1340 int j, len = 0;
1341 char *endtok = action;
1342 Bool matched;
1343 int mlen;
1345 if (action)
1347 while (*endtok && !isspace((unsigned char)*endtok))
1349 ++endtok;
1351 len = endtok - action;
1352 j=0;
1353 matched = False;
1354 while (!matched && (mlen = strlen(func_table[j].keyword)) > 0)
1356 if (mlen == len &&
1357 strncasecmp(action,func_table[j].keyword,mlen) ==
1360 matched=True;
1361 /* found key word */
1362 if (func_t)
1364 *func_t = func_table[j].func_t;
1366 if (flags)
1368 *flags = func_table[j].flags;
1370 return;
1372 else
1374 j++;
1377 /* No clue what the function is. Just return "BEEP" */
1379 if (func_t)
1381 *func_t = F_BEEP;
1383 if (flags)
1385 *flags = 0;
1388 return;
1393 * add an item to a FvwmFunction
1395 * Inputs:
1396 * func - pointer to the FvwmFunction to add the item
1397 * action - the definition string from the config line
1399 void AddToFunction(FvwmFunction *func, char *action)
1401 FunctionItem *tmp;
1402 char *token = NULL;
1403 char condition;
1405 token = PeekToken(action, &action);
1406 if (!token)
1407 return;
1408 condition = token[0];
1409 if (isupper(condition))
1410 condition = tolower(condition);
1411 if (condition != CF_IMMEDIATE &&
1412 condition != CF_MOTION &&
1413 condition != CF_HOLD &&
1414 condition != CF_CLICK &&
1415 condition != CF_DOUBLE_CLICK)
1417 fvwm_msg(
1418 ERR, "AddToFunction",
1419 "Got '%s' instead of a valid function specifier",
1420 token);
1421 return;
1423 if (token[0] != 0 && token[1] != 0 &&
1424 (find_builtin_function(token) || find_complex_function(token)))
1426 fvwm_msg(
1427 WARN, "AddToFunction",
1428 "Got the command or function name '%s' instead of a"
1429 " function specifier. This may indicate a syntax"
1430 " error in the configuration file. Using %c as the"
1431 " specifier.", token, token[0]);
1433 if (!action)
1435 return;
1437 while (isspace(*action))
1439 action++;
1441 if (*action == 0)
1443 return;
1446 tmp = (FunctionItem *)safemalloc(sizeof(FunctionItem));
1447 tmp->next_item = NULL;
1448 tmp->func = func;
1449 if (func->first_item == NULL)
1451 func->first_item = tmp;
1452 func->last_item = tmp;
1454 else
1456 func->last_item->next_item = tmp;
1457 func->last_item = tmp;
1460 tmp->condition = condition;
1461 tmp->action = stripcpy(action);
1463 find_func_t(tmp->action, NULL, &(tmp->flags));
1465 return;
1468 /* ---------------------------- builtin commands --------------------------- */
1470 void CMD_DestroyFunc(F_CMD_ARGS)
1472 FvwmFunction *func;
1473 char *token;
1475 token = PeekToken(action, NULL);
1476 if (!token)
1478 return;
1480 func = find_complex_function(token);
1481 if (!func)
1483 return;
1485 if (Scr.last_added_item.type == ADDED_FUNCTION)
1487 set_last_added_item(ADDED_NONE, NULL);
1489 DestroyFunction(func);
1491 return;
1494 void CMD_AddToFunc(F_CMD_ARGS)
1496 FvwmFunction *func;
1497 char *token;
1499 action = GetNextToken(action,&token);
1500 if (!token)
1502 return;
1504 func = find_complex_function(token);
1505 if (func == NULL)
1507 func = NewFvwmFunction(token);
1510 /* Set + state to last function */
1511 set_last_added_item(ADDED_FUNCTION, func);
1513 free(token);
1514 AddToFunction(func, action);
1516 return;
1519 void CMD_Plus(F_CMD_ARGS)
1521 if (Scr.last_added_item.type == ADDED_MENU)
1523 add_another_menu_item(action);
1525 else if (Scr.last_added_item.type == ADDED_FUNCTION)
1527 AddToFunction(Scr.last_added_item.item, action);
1529 #ifdef USEDECOR
1530 else if (Scr.last_added_item.type == ADDED_DECOR)
1532 FvwmDecor *tmp = &Scr.DefaultDecor;
1533 for ( ; tmp && tmp != Scr.last_added_item.item; tmp = tmp->next)
1535 /* nothing to do here */
1537 if (!tmp)
1539 return;
1541 AddToDecor(F_PASS_ARGS, tmp);
1543 #endif /* USEDECOR */
1545 return;
1548 void CMD_EchoFuncDefinition(F_CMD_ARGS)
1550 FvwmFunction *func;
1551 const func_t *bif;
1552 FunctionItem *fi;
1553 char *token;
1555 GetNextToken(action, &token);
1556 if (!token)
1558 fvwm_msg(ERR, "EchoFuncDefinition", "Missing argument");
1560 return;
1562 bif = find_builtin_function(token);
1563 if (bif != NULL)
1565 fvwm_msg(
1566 INFO, "EchoFuncDefinition",
1567 "function '%s' is a built in command", token);
1568 free(token);
1570 return;
1572 func = find_complex_function(token);
1573 if (!func)
1575 fvwm_msg(
1576 INFO, "EchoFuncDefinition",
1577 "function '%s' not defined", token);
1578 free(token);
1580 return;
1582 fvwm_msg(
1583 INFO, "EchoFuncDefinition", "definition of function '%s':",
1584 token);
1585 for (fi = func->first_item; fi != NULL; fi = fi->next_item)
1587 fvwm_msg(
1588 INFO, "EchoFuncDefinition", " %c %s", fi->condition,
1589 (fi->action == 0) ? "(null)" : fi->action);
1591 fvwm_msg(INFO, "EchoFuncDefinition", "end of definition");
1592 free(token);
1594 return;
1597 /* dummy commands */
1598 void CMD_Title(F_CMD_ARGS) { }
1599 void CMD_TearMenuOff(F_CMD_ARGS) { }
1600 void CMD_KeepRc(F_CMD_ARGS) { }
1601 void CMD_Silent(F_CMD_ARGS) { }
1602 void CMD_Function(F_CMD_ARGS) { }