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
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 ---------------------- */
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"
45 #include "execcontext.h"
46 #include "functions.h"
48 #include "functable.h"
51 #include "module_list.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
74 char *action
; /* action to be performed */
75 short type
; /* type of built in function */
76 FUNC_FLAGS_TYPE flags
;
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 */
88 /* Types of events for the FUNCTION builtin */
95 CF_DOUBLE_CLICK
= 'd',
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
)
118 else if ((flags
& FUNC_ALLOW_UNMANAGED
) && exc
->w
.w
!= None
)
127 * Defer the execution of a function to the next button press if the context is
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
)
139 int just_waiting_for_finish
= 0;
151 wcontext
= ret_ecc
->w
.wcontext
;
152 FinishEvent
= ((fw
!= NULL
) ? ButtonRelease
: ButtonPress
);
153 if (wcontext
== C_UNMANAGED
&& do_allow_unmanaged
)
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
))
166 else if (FinishEvent
== ButtonRelease
)
168 /* We are only waiting until the user releases the
169 * button. Do not change the cursor. */
171 just_waiting_for_finish
= 1;
174 if (Scr
.flags
.are_functions_silent
)
178 if (!GrabEm(cursor
, GRAB_NORMAL
))
183 MyXGrabKeyboard(dpy
);
187 /* block until there is an event */
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
);
205 Keyboard_shortcuts(&e
, NULL
, NULL
, NULL
, FinishEvent
);
207 if (e
.type
== FinishEvent
)
215 if (e
.type
!= FinishEvent
)
217 original_w
= e
.xany
.window
;
232 MyXUngrabKeyboard(dpy
);
233 UngrabEm(GRAB_NORMAL
);
234 if (just_waiting_for_finish
)
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
;
247 if (w
== Scr
.Root
|| IS_EWMH_DESKTOP(w
))
250 ret_ecc
->w
.wcontext
= C_ROOT
;
255 if (XFindContext(dpy
, w
, FvwmContext
, (caddr_t
*)&fw
) == XCNOENT
)
257 ret_ecc
->w
.fw
= NULL
;
259 ret_ecc
->w
.wcontext
= C_ROOT
;
263 if (w
== FW_W_PARENT(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
))
281 ret_ecc
->w
.wcontext
= C_ROOT
;
287 if (IS_EWMH_DESKTOP(FW_W(fw
)))
291 ret_ecc
->w
.wcontext
= C_ROOT
;
295 wcontext
= GetContext(NULL
, fw
, &e
, &dummy
);
298 ret_ecc
->w
.wcontext
= C_ROOT
;
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;
318 if (!func
|| func
[0] == 0)
323 /* since a lot of lines in a typical rc are probably menu/func
325 if (func
[0]=='+' || (func
[0] == ' ' && func
[1] == '+'))
327 return &(func_table
[0]);
330 temp
= safestrdup(func
);
331 for (s
= temp
; *s
!= 0; s
++)
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
);
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
;
365 char *expaction
= NULL
;
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
;
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 */
387 if (action
[0] == '#')
394 if (func_depth
> MAX_FUNCTION_DEPTH
)
397 ERR
, "__execute_function",
398 "Function '%s' called with a depth of %i, "
399 "stopping function execution!",
406 for (j
= 0; j
< 11; j
++)
408 arguments
[j
] = args
[j
];
413 for (j
= 0; j
< 11; j
++)
419 if (exc
->w
.fw
== NULL
|| IS_EWMH_DESKTOP(FW_W(exc
->w
.fw
)))
421 if (exec_flags
& FUNC_IS_UNMANAGED
)
434 w
= GetSubwindowFromEvent(dpy
, exc
->x
.elast
);
437 w
= exc
->x
.elast
->xany
.window
;
443 dpy
, w
, FvwmContext
, (caddr_t
*)&tw
) ==
449 if (w
== None
|| tw
!= exc
->w
.fw
)
456 if (action
[0] == '-')
458 exec_flags
|= FUNC_DONT_EXPAND_COMMAND
;
464 trash
= PeekToken(taction
, &trash2
);
467 if (StrEquals(trash
, PRE_SILENT
))
469 if (Scr
.flags
.are_functions_silent
== 0)
472 Scr
.flags
.are_functions_silent
= 1;
475 trash
= PeekToken(taction
, &trash2
);
477 else if (StrEquals(trash
, PRE_KEEPRC
))
481 trash
= PeekToken(taction
, &trash2
);
492 Scr
.flags
.are_functions_silent
= 0;
497 if (cond_rc
== NULL
|| do_keep_rc
== True
)
499 condrc_init(&dummy_rc
);
507 GetNextToken(taction
, &function
);
510 char *tmp
= function
;
511 function
= expand_vars(
512 function
, arguments
, False
, False
, func_rc
, exc
);
515 if (function
&& function
[0] != '*')
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
523 char *tmp
= function
;
525 while (*tmp
&& !isspace(*tmp
))
531 bif
= find_builtin_function(function
);
532 must_free_function
= True
;
545 if (Scr
.cur_decor
&& Scr
.cur_decor
!= &Scr
.DefaultDecor
&&
546 (!bif
|| !(bif
->flags
& FUNC_DECOR
)))
549 ERR
, "__execute_function",
550 "Command can not be added to a decor; executing"
551 " command now: '%s'", action
);
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
);
563 must_free_string
= set_repeat_data(
564 expaction
, REPEAT_COMMAND
, bif
);
568 must_free_string
= True
;
576 #ifdef FVWM_COMMAND_LOG
577 fvwm_msg(INFO
, "LOG", "%c: %s", (char)exc
->type
, expaction
);
580 /* Note: the module config command, "*" can not be handled by the
581 * regular command table because there is no required white space after
583 if (expaction
[0] == '*')
586 if (Scr
.cur_decor
&& Scr
.cur_decor
!= &Scr
.DefaultDecor
)
589 WARN
, "__execute_function",
590 "Command can not be added to a decor;"
591 " executing command now: '%s'", expaction
);
594 /* process a module config command */
595 ModuleConfig(expaction
);
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
;
606 ecc
.w
.wcontext
= exc
->w
.wcontext
;
607 if (bif
&& bif
->func_t
!= F_FUNCTION
)
612 runaction
= SkipNTokens(expaction
, 1);
613 if ((bif
->flags
& FUNC_NEEDS_WINDOW
) &&
614 !(exec_flags
& FUNC_DONT_DEFER
))
617 &ecc
, &mask
, bif
->cursor
,
619 (bif
->flags
& FUNC_ALLOW_UNMANAGED
));
621 else if ((bif
->flags
& FUNC_NEEDS_WINDOW
) &&
622 !__context_has_window(
624 bif
->flags
& FUNC_ALLOW_UNMANAGED
))
626 /* no context window and not allowed to defer,
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
))
640 bif
->action(func_rc
, exc2
, runaction
);
645 bif
->action(func_rc
, exc2
, runaction
);
647 exc_destroy_context(exc2
);
657 /* strip "function" command */
658 runaction
= SkipNTokens(expaction
, 1);
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
)
675 ERR
, "__execute_function",
676 "No such command '%s'",
680 exc_destroy_context(exc2
);
686 Scr
.flags
.are_functions_silent
= 0;
690 cond_rc
->break_levels
= func_rc
->break_levels
;
692 if (must_free_string
)
696 if (must_free_function
)
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
)
711 if (function_name
== NULL
|| *function_name
== 0)
715 func
= Scr
.functions
;
718 if (func
->name
!= NULL
)
720 if (strcasecmp(function_name
, func
->name
) == 0)
725 func
= func
->next_func
;
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
,
740 int xcurrent
,ycurrent
,total
= 0;
743 Bool do_sleep
= False
;
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
;
770 dpy
, ButtonReleaseMask
|ButtonMotionMask
|
771 PointerMotionMask
|ButtonPressMask
|ExposureMask
, d
))
774 switch (d
->xany
.type
)
777 *ret_button
= d
->xbutton
.button
;
780 if (d
->xmotion
.same_screen
== False
)
784 if ((d
->xmotion
.state
&
785 DEFAULT_ALL_BUTTONS_MASK
) ||
788 xcurrent
= d
->xmotion
.x_root
;
789 ycurrent
= d
->xmotion
.y_root
;
797 *ret_button
= d
->xbutton
.button
;
800 is_button_pressed
= True
;
804 /* must handle expose here so that raising a
805 * window with "I" works */
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
)
825 extern Window PressedW
;
827 if (!(!has_ref_window_moved
&& PressedW
&& XTranslateCoordinates(
828 dpy
, PressedW
, Scr
.Root
, 0, 0, &x0
, &y0
,
834 for (fi
= func
->first_item
; fi
!= NULL
&& cond_rc
->break_levels
== 0; )
836 /* make lower case */
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
,
852 has_ref_window_moved
=(x
!= x0
|| y
!= y0
);
861 static void __cf_cleanup(
862 int *depth
, char **arguments
, cond_rc_t
*cond_rc
)
869 Scr
.flags
.is_executing_complex_function
= 0;
871 for (i
= 0; i
< 11; i
++)
873 if (arguments
[i
] != NULL
)
878 if (cond_rc
->break_levels
> 0)
880 cond_rc
->break_levels
--;
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
)
891 cfunc_action_t type
= CF_MOTION
;
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
;
906 static int depth
= 0;
907 const exec_context_t
*exc2
;
908 exec_context_changes_t ecc
;
909 exec_context_change_mask_t mask
;
916 condrc_init(&tmp_rc
);
919 cond_rc
->rc
= COND_RC_OK
;
922 ecc
.w
.fw
= exc
->w
.fw
;
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
);
931 func
= find_complex_function(func_name
);
937 ERR
, "ComplexFunction", "No such function %s",
944 Scr
.flags
.is_executing_complex_function
= 1;
948 /* duplicate the whole argument list for use as '$*' */
951 arguments
[0] = safestrdup(taction
);
952 /* strip trailing newline */
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
]);
970 for (i
= 0; i
< 11; i
++)
975 /* In case we want to perform an action on a button press, we
976 * need to fool other routines */
978 if (te
->type
== ButtonPress
)
980 trigger_evtype
= ButtonRelease
;
984 trigger_evtype
= te
->type
;
988 for (fi
= func
->first_item
; fi
!= NULL
; fi
= fi
->next_item
)
990 if (fi
->flags
& FUNC_NEEDS_WINDOW
)
993 do_allow_unmanaged
&= fi
->flags
;
994 if (fi
->condition
== CF_IMMEDIATE
)
996 do_allow_unmanaged_immediate
&= fi
->flags
;
997 ImmediateNeedsTarget
= True
;
1003 if (ImmediateNeedsTarget
)
1006 &ecc
, &mask
, CRS_SELECT
, trigger_evtype
,
1007 do_allow_unmanaged_immediate
))
1010 __cf_cleanup(&depth
, arguments
, cond_rc
);
1013 NeedsTarget
= False
;
1017 ecc
.w
.w
= (ecc
.w
.fw
) ? FW_W_FRAME(ecc
.w
.fw
) : None
;
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
))
1029 "ComplexFunction", "Grab failed in function %s,"
1030 " unable to execute immediate action", action
);
1031 __cf_cleanup(&depth
, arguments
, cond_rc
);
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;
1043 /* c is already lowercase here */
1049 case CF_DOUBLE_CLICK
:
1050 HaveDoubleClick
= True
;
1063 if (!Persist
|| cond_rc
->break_levels
!= 0)
1066 __cf_cleanup(&depth
, arguments
, cond_rc
);
1067 UngrabEm(GRAB_NORMAL
);
1071 /* Only defer execution if there is a possibility of needing
1072 * a window to operate on */
1076 &ecc
, &mask
, CRS_SELECT
, trigger_evtype
,
1077 do_allow_unmanaged
))
1080 __cf_cleanup(&depth
, arguments
, cond_rc
);
1081 UngrabEm(GRAB_NORMAL
);
1086 te
= (mask
& ECC_ETRIGGER
) ? ecc
.x
.etrigger
: exc
->x
.elast
;
1087 switch (te
->xany
.type
)
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
);
1100 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &x
, &y
,
1101 &JunkX
, &JunkY
, &JunkMask
) == False
)
1103 /* pointer is on a different screen */
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
)
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
);
1128 if (button
== button2
)
1130 type
= CF_DOUBLE_CLICK
;
1146 else if (type
== CF_TIMEOUT
)
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
)
1160 (~(Button1Mask
>> (d
.xbutton
.button
- 1)));
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
);
1172 fev_set_evpos(&d
, x
, y
);
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).
1184 __cf_cleanup(&depth
, arguments
, cond_rc
);
1185 UngrabEm(GRAB_NORMAL
);
1191 * create a new FvwmFunction
1193 static FvwmFunction
*NewFvwmFunction(const char *name
)
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
);
1203 Scr
.functions
= tmp
;
1208 static void DestroyFunction(FvwmFunction
*func
)
1210 FunctionItem
*fi
,*tmp2
;
1211 FvwmFunction
*tmp
, *prev
;
1218 tmp
= Scr
.functions
;
1220 while (tmp
&& tmp
!= func
)
1223 tmp
= tmp
->next_func
;
1230 if (func
->use_depth
!= 0)
1233 ERR
,"DestroyFunction",
1234 "Function %s is in use (depth %d)", func
->name
,
1241 Scr
.functions
= func
->next_func
;
1245 prev
->next_func
= func
->next_func
;
1250 fi
= func
->first_item
;
1253 tmp2
= fi
->next_item
;
1254 if (fi
->action
!= NULL
)
1266 /* ---------------------------- interface functions ------------------------ */
1268 Bool
functions_is_complex_function(const char *function_name
)
1270 if (find_complex_function(function_name
) != NULL
)
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
);
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
);
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
;
1313 ecc
.w
.wcontext
= C_WINDOW
;
1314 exec_flags
|= FUNC_DONT_DEFER
;
1319 ecc
.w
.wcontext
= C_ROOT
;
1323 exc2
= exc_clone_context(
1324 exc
, &ecc
, ECC_FW
| ECC_W
| ECC_WCONTEXT
);
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
);
1338 void find_func_t(char *action
, short *func_t
, unsigned char *flags
)
1341 char *endtok
= action
;
1347 while (*endtok
&& !isspace((unsigned char)*endtok
))
1351 len
= endtok
- action
;
1354 while (!matched
&& (mlen
= strlen(func_table
[j
].keyword
)) > 0)
1357 strncasecmp(action
,func_table
[j
].keyword
,mlen
) ==
1361 /* found key word */
1364 *func_t
= func_table
[j
].func_t
;
1368 *flags
= func_table
[j
].flags
;
1377 /* No clue what the function is. Just return "BEEP" */
1393 * add an item to a FvwmFunction
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
)
1405 token
= PeekToken(action
, &action
);
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
)
1418 ERR
, "AddToFunction",
1419 "Got '%s' instead of a valid function specifier",
1423 if (token
[0] != 0 && token
[1] != 0 &&
1424 (find_builtin_function(token
) || find_complex_function(token
)))
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]);
1437 while (isspace(*action
))
1446 tmp
= (FunctionItem
*)safemalloc(sizeof(FunctionItem
));
1447 tmp
->next_item
= NULL
;
1449 if (func
->first_item
== NULL
)
1451 func
->first_item
= tmp
;
1452 func
->last_item
= tmp
;
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
));
1468 /* ---------------------------- builtin commands --------------------------- */
1470 void CMD_DestroyFunc(F_CMD_ARGS
)
1475 token
= PeekToken(action
, NULL
);
1480 func
= find_complex_function(token
);
1485 if (Scr
.last_added_item
.type
== ADDED_FUNCTION
)
1487 set_last_added_item(ADDED_NONE
, NULL
);
1489 DestroyFunction(func
);
1494 void CMD_AddToFunc(F_CMD_ARGS
)
1499 action
= GetNextToken(action
,&token
);
1504 func
= find_complex_function(token
);
1507 func
= NewFvwmFunction(token
);
1510 /* Set + state to last function */
1511 set_last_added_item(ADDED_FUNCTION
, func
);
1514 AddToFunction(func
, action
);
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
);
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 */
1541 AddToDecor(F_PASS_ARGS
, tmp
);
1543 #endif /* USEDECOR */
1548 void CMD_EchoFuncDefinition(F_CMD_ARGS
)
1555 GetNextToken(action
, &token
);
1558 fvwm_msg(ERR
, "EchoFuncDefinition", "Missing argument");
1562 bif
= find_builtin_function(token
);
1566 INFO
, "EchoFuncDefinition",
1567 "function '%s' is a built in command", token
);
1572 func
= find_complex_function(token
);
1576 INFO
, "EchoFuncDefinition",
1577 "function '%s' not defined", token
);
1583 INFO
, "EchoFuncDefinition", "definition of function '%s':",
1585 for (fi
= func
->first_item
; fi
!= NULL
; fi
= fi
->next_item
)
1588 INFO
, "EchoFuncDefinition", " %c %s", fi
->condition
,
1589 (fi
->action
== 0) ? "(null)" : fi
->action
);
1591 fvwm_msg(INFO
, "EchoFuncDefinition", "end of definition");
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
) { }