NEWS/Changelog for previous commit.
[fvwm.git] / fvwm / conditional.c
blob3b6732c8a8842534a20274bb4a05a612bf34a304
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
18 * This module is all original code
19 * by Rob Nation
20 * Copyright 1993, Robert Nation
21 * You may use this code for any purpose, as long as the original
22 * copyright remains in the source code and all documentation
25 /* ---------------------------- included header files ---------------------- */
27 #include "config.h"
29 #include <stdio.h>
30 #include <math.h>
32 #include "libs/fvwmlib.h"
33 #include "libs/Parse.h"
34 #include "libs/wild.h"
35 #include "libs/FScreen.h"
36 #include "libs/charmap.h"
37 #include "libs/wcontext.h"
38 #include "fvwm.h"
39 #include "externs.h"
40 #include "execcontext.h"
41 #include "functions.h"
42 #include "conditional.h"
43 #include "misc.h"
44 #include "screen.h"
45 #include "update.h"
46 #include "style.h"
47 #include "focus.h"
48 #include "geometry.h"
49 #include "stack.h"
50 #include "commands.h"
51 #include "decorations.h"
52 #include "virtual.h"
54 /* ---------------------------- local definitions -------------------------- */
56 /* ---------------------------- local macros ------------------------------- */
58 /* ---------------------------- imports ------------------------------------ */
60 /* ---------------------------- included code files ------------------------ */
62 /* ---------------------------- local types -------------------------------- */
64 /* ---------------------------- forward declarations ----------------------- */
66 /* ---------------------------- local variables ---------------------------- */
68 /* ---------------------------- exported variables (globals) --------------- */
70 /* ---------------------------- local functions ---------------------------- */
74 * Direction = 1 ==> "Next" operation
75 * Direction = -1 ==> "Previous" operation
76 * Direction = 0 ==> operation on current window (returns pass or fail)
79 static FvwmWindow *Circulate(
80 FvwmWindow *sf, char *action, int Direction, char **restofline)
82 int pass = 0;
83 FvwmWindow *fw, *found = NULL;
84 WindowConditionMask mask;
85 char *flags;
87 /* Create window mask */
88 flags = CreateFlagString(action, restofline);
89 DefaultConditionMask(&mask);
90 if (Direction == 0)
91 { /* override for Current [] */
92 mask.my_flags.use_circulate_hit = 1;
93 mask.my_flags.use_circulate_hit_icon = 1;
94 mask.my_flags.use_circulate_hit_shaded = 1;
96 CreateConditionMask(flags, &mask);
97 if (flags)
99 free(flags);
101 if (sf == NULL || Direction == 0)
103 sf = get_focus_window();
105 if (sf != NULL)
107 if (Direction > 0)
109 fw = sf->prev;
111 else if (Direction < 0)
113 fw = sf->next;
115 else
117 fw = sf;
120 else
122 fw = NULL;
123 if (Direction == 0)
125 FreeConditionMask(&mask);
126 return NULL;
130 for (pass = 0; pass < 3 && !found; pass++)
132 while (fw && !found && fw != &Scr.FvwmRoot)
134 /* Make CirculateUp and CirculateDown take args. by
135 * Y.NOMURA */
136 if (MatchesConditionMask(fw, &mask))
138 found = fw;
140 else
142 if (Direction > 0)
144 fw = fw->prev;
146 else
148 fw = fw->next;
151 if (Direction == 0)
153 FreeConditionMask(&mask);
154 return found;
157 if (fw == NULL || fw == &Scr.FvwmRoot)
159 if (Direction > 0)
161 /* Go to end of list */
162 for (fw = Scr.FvwmRoot.next; fw && fw->next;
163 fw = fw->next)
165 /* nop */
168 else
170 /* Go to top of list */
171 fw = Scr.FvwmRoot.next;
175 FreeConditionMask(&mask);
177 return found;
180 static void circulate_cmd(
181 F_CMD_ARGS, int new_context, int circ_dir, Bool do_use_found,
182 Bool do_exec_on_match)
184 FvwmWindow *found;
185 char *restofline;
187 found = Circulate(exc->w.fw, action, circ_dir, &restofline);
188 if (cond_rc != NULL)
190 cond_rc->rc = (found == NULL) ? COND_RC_NO_MATCH : COND_RC_OK;
192 if ((!found == !do_exec_on_match) && restofline)
194 const exec_context_t *exc2;
195 exec_context_changes_t ecc;
196 int flags;
198 ecc.w.fw = (do_use_found == True) ? found : NULL;
199 if (found != NULL)
201 ecc.w.w = FW_W(found);
202 flags = FUNC_DONT_DEFER;
204 else
206 ecc.w.w = None;
207 flags = 0;
209 ecc.w.wcontext = new_context;
210 exc2 = exc_clone_context(
211 exc, &ecc, ECC_FW | ECC_W | ECC_WCONTEXT);
212 execute_function(cond_rc, exc2, restofline, flags);
213 exc_destroy_context(exc2);
216 return;
219 static void select_cmd(F_CMD_ARGS)
221 char *restofline;
222 char *flags;
223 WindowConditionMask mask;
224 FvwmWindow * const fw = exc->w.fw;
226 if (!fw || IS_EWMH_DESKTOP(FW_W(fw)))
228 if (cond_rc != NULL)
230 cond_rc->rc = COND_RC_ERROR;
232 return;
234 flags = CreateFlagString(action, &restofline);
235 DefaultConditionMask(&mask);
236 mask.my_flags.use_circulate_hit = 1;
237 mask.my_flags.use_circulate_hit_icon = 1;
238 mask.my_flags.use_circulate_hit_shaded = 1;
239 CreateConditionMask(flags, &mask);
240 if (flags)
242 free(flags);
244 if (MatchesConditionMask(fw, &mask) && restofline)
246 if (cond_rc != NULL)
248 cond_rc->rc = COND_RC_OK;
250 execute_function_override_wcontext(
251 cond_rc, exc, restofline, 0, C_WINDOW);
253 else if (cond_rc != NULL)
255 cond_rc->rc = COND_RC_NO_MATCH;
257 FreeConditionMask(&mask);
259 return;
262 static Bool cond_check_access(char *file, int type, Bool im)
264 char *full_file;
265 char *path = NULL;
267 if (!file || *file == 0)
269 return False;
271 if (file[0] == '/')
273 if (access(file, type) == 0)
275 return True;
277 else
279 return False;
282 if (type != X_OK && im == False)
284 return False;
286 if (im == False)
288 path = getenv("PATH");
290 else
292 path = PictureGetImagePath();
294 if (path == NULL || *path == 0)
296 return False;
298 full_file = searchPath(path, file, NULL, type);
299 if (full_file)
301 free(full_file);
302 return True;
304 return False;
307 /* ---------------------------- interface functions ------------------------ */
310 * Parses the flag string and returns the text between [ ] or ( )
311 * characters. The start of the rest of the line is put in restptr.
312 * Note that the returned string is allocated here and it must be
313 * freed when it is not needed anymore.
314 * NOTE - exported via .h
316 char *CreateFlagString(char *string, char **restptr)
318 char *retval;
319 char *c;
320 char *start;
321 char closeopt;
322 int length;
324 c = string;
325 while (isspace((unsigned char)*c) && (*c != 0))
327 c++;
331 if (*c == '[' || *c == '(')
333 char *d;
335 /* Get the text between [ ] or ( ) */
336 if (*c == '[')
338 closeopt = ']';
340 else
342 closeopt = ')';
344 c++;
345 start = c;
346 length = 0;
347 while (*c != closeopt)
349 if (*c == 0)
351 fvwm_msg(ERR, "CreateFlagString",
352 "Conditionals require closing "
353 "parenthesis");
354 *restptr = NULL;
355 return NULL;
358 /* skip quoted string */
359 d = SkipQuote(c, NULL, NULL, NULL);
360 length += d - c;
361 c = d;
364 /* We must allocate a new string because we null terminate the
365 * string between the [ ] or ( ) characters. */
366 retval = safemalloc(length + 1);
367 strncpy(retval, start, length);
368 retval[length] = 0;
370 *restptr = c + 1;
372 else
374 retval = NULL;
375 *restptr = c;
378 return retval;
382 * The name_condition field of the mask is allocated in CreateConditionMask.
383 * It must be freed.
384 * NOTE - exported via .h
386 void FreeConditionMask(WindowConditionMask *mask)
388 struct name_condition *pp,*pp2;
389 struct namelist *p,*p2;
391 for (pp=mask->name_condition; pp; )
393 /* One malloc() is done for all the name strings.
394 The string is tokenised & the name fields point to
395 different parts of the one string.
396 The start of the string is the first name in the string
397 which is actually the last node in the linked list. */
398 for (p=pp->namelist; p; )
400 p2=p->next;
401 if(!p2)
403 free(p->name);
405 free(p);
406 p=p2;
408 pp2=pp->next;
409 free(pp);
410 pp=pp2;
414 /* Assign the default values for the window mask
415 * NOTE - exported via .h */
416 void DefaultConditionMask(WindowConditionMask *mask)
418 memset(mask, 0, sizeof(WindowConditionMask));
419 /* -2 means no layer condition, -1 means current */
420 mask->layer = -2;
422 return;
426 * Note that this function allocates the name field of the mask struct.
427 * FreeConditionMask must be called for the mask when the mask is discarded.
428 * NOTE - exported via .h
430 void CreateConditionMask(char *flags, WindowConditionMask *mask)
432 char *allocated_condition;
433 char *next_condition;
434 char *condition;
435 char *tmp;
436 unsigned int state;
438 if (flags == NULL)
440 return;
443 /* Next parse the flags in the string. */
444 next_condition = GetNextFullOption(flags, &allocated_condition);
445 condition = PeekToken(allocated_condition, &tmp);
447 while (condition)
449 char *cond;
450 int on;
452 cond = condition;
453 on = 1;
454 if (*cond == '!')
456 on = 0;
457 cond++;
459 if (StrEquals(cond,"AcceptsFocus"))
461 mask->my_flags.do_accept_focus = on;
462 mask->my_flags.use_do_accept_focus = 1;
464 else if (StrEquals(cond,"Focused"))
466 mask->my_flags.needs_focus =
467 (on) ? NEEDS_TRUE : NEEDS_FALSE;
469 else if (StrEquals(cond,"HasPointer"))
471 mask->my_flags.needs_pointer =
472 (on) ? NEEDS_TRUE : NEEDS_FALSE;
474 else if (StrEquals(cond,"Iconic"))
476 SET_ICONIFIED(mask, on);
477 SETM_ICONIFIED(mask, 1);
479 else if (StrEquals(cond,"Visible"))
481 SET_PARTIALLY_VISIBLE(mask, on);
482 SETM_PARTIALLY_VISIBLE(mask, 1);
484 else if (StrEquals(cond,"Overlapped"))
486 mask->my_flags.needs_overlapped = on;
487 mask->my_flags.do_check_overlapped = 1;
489 else if (StrEquals(cond,"PlacedByButton"))
491 int button;
492 int button_mask;
494 if (sscanf(tmp, "%d", &button) &&
495 (button >= 1 &&
496 button <= NUMBER_OF_EXTENDED_MOUSE_BUTTONS))
498 tmp = SkipNTokens(tmp, 1);
499 button_mask = (1<<(button-1));
501 else
503 button_mask =
504 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS) - 1;
506 if (on)
508 if (mask->placed_by_button_mask &
509 mask->placed_by_button_set_mask &
510 ~button_mask)
512 fvwm_msg(WARN, "PlacedByButton",
513 "Condition always False.");
515 mask->placed_by_button_mask |= button_mask;
517 else
519 mask->placed_by_button_mask &= ~button_mask;
521 mask->placed_by_button_set_mask |= button_mask;
523 else if (StrEquals(cond,"PlacedByButton3"))
525 if (on)
527 if (mask->placed_by_button_mask &
528 mask->placed_by_button_set_mask & ~(1<<2))
530 fvwm_msg(WARN, "PlacedByButton3",
531 "Condition always False.");
533 mask->placed_by_button_mask |= (1<<2);
535 else
537 mask->placed_by_button_mask &= ~(1<<2);
539 mask->placed_by_button_set_mask |= (1<<2);
541 else if (StrEquals(cond,"Raised"))
543 SET_FULLY_VISIBLE(mask, on);
544 SETM_FULLY_VISIBLE(mask, 1);
546 else if (StrEquals(cond,"Sticky"))
548 SET_STICKY_ACROSS_PAGES(mask, on);
549 SET_STICKY_ACROSS_DESKS(mask, on);
550 SETM_STICKY_ACROSS_PAGES(mask, 1);
551 SETM_STICKY_ACROSS_DESKS(mask, 1);
553 else if (StrEquals(cond,"StickyAcrossPages"))
555 SET_STICKY_ACROSS_PAGES(mask, on);
556 SETM_STICKY_ACROSS_PAGES(mask, 1);
558 else if (StrEquals(cond,"StickyAcrossDesks"))
560 SET_STICKY_ACROSS_DESKS(mask, on);
561 SETM_STICKY_ACROSS_DESKS(mask, 1);
563 else if (StrEquals(cond,"StickyIcon"))
565 SET_ICON_STICKY_ACROSS_PAGES(mask, on);
566 SET_ICON_STICKY_ACROSS_DESKS(mask, on);
567 SETM_ICON_STICKY_ACROSS_PAGES(mask, 1);
568 SETM_ICON_STICKY_ACROSS_DESKS(mask, 1);
570 else if (StrEquals(cond,"StickyAcrossPagesIcon"))
572 SET_ICON_STICKY_ACROSS_PAGES(mask, on);
573 SETM_ICON_STICKY_ACROSS_PAGES(mask, 1);
575 else if (StrEquals(cond,"StickyAcrossDesksIcon"))
577 SET_ICON_STICKY_ACROSS_DESKS(mask, on);
578 SETM_ICON_STICKY_ACROSS_DESKS(mask, 1);
580 else if (StrEquals(cond,"Maximized"))
582 SET_MAXIMIZED(mask, on);
583 SETM_MAXIMIZED(mask, 1);
585 else if (StrEquals(cond,"FixedSize"))
587 /* don't set mask here, because we make the test here
588 (and don't compare against window's mask)
589 by checking allowed function */
590 SET_SIZE_FIXED(mask, on);
591 SETM_SIZE_FIXED(mask, 1);
593 else if (StrEquals(cond, "FixedPosition"))
595 SET_FIXED(mask, on);
596 SETM_FIXED(mask, 1);
598 else if (StrEquals(cond,"HasHandles"))
600 SET_HAS_HANDLES(mask, on);
601 SETM_HAS_HANDLES(mask, 1);
603 else if (StrEquals(cond,"Iconifiable"))
605 SET_IS_UNICONIFIABLE(mask, !on);
606 SETM_IS_UNICONIFIABLE(mask, 1);
608 else if (StrEquals(cond,"Maximizable"))
610 SET_IS_UNMAXIMIZABLE(mask, !on);
611 SETM_IS_UNMAXIMIZABLE(mask, 1);
613 else if (StrEquals(cond,"Closable"))
615 SET_IS_UNCLOSABLE(mask, !on);
616 SETM_IS_UNCLOSABLE(mask, 1);
618 else if (StrEquals(cond,"Shaded"))
620 SET_SHADED(mask, on);
621 SETM_SHADED(mask, 1);
623 else if (StrEquals(cond,"Transient"))
625 SET_TRANSIENT(mask, on);
626 SETM_TRANSIENT(mask, 1);
628 else if (StrEquals(cond,"PlacedByFvwm"))
630 SET_PLACED_BY_FVWM(mask, on);
631 SETM_PLACED_BY_FVWM(mask, 1);
633 else if (StrEquals(cond,"CurrentDesk"))
635 mask->my_flags.needs_current_desk = on;
636 mask->my_flags.do_check_desk = 1;
638 else if (StrEquals(cond,"CurrentPage"))
640 mask->my_flags.needs_current_desk_and_page = on;
641 mask->my_flags.do_check_desk_and_page = 1;
643 else if (StrEquals(cond,"CurrentGlobalPage"))
645 mask->my_flags.needs_current_desk_and_global_page = on;
646 mask->my_flags.do_check_desk_and_global_page = 1;
648 else if (StrEquals(cond,"CurrentPageAnyDesk") ||
649 StrEquals(cond,"CurrentScreen"))
651 mask->my_flags.needs_current_page = on;
652 mask->my_flags.do_check_page = 1;
654 else if (StrEquals(cond,"AnyScreen"))
656 mask->my_flags.do_not_check_screen = on;
658 else if (StrEquals(cond,"CurrentGlobalPageAnyDesk"))
660 mask->my_flags.needs_current_global_page = on;
661 mask->my_flags.do_check_global_page = 1;
663 else if (StrEquals(cond,"CirculateHit"))
665 mask->my_flags.use_circulate_hit = on;
667 else if (StrEquals(cond,"CirculateHitIcon"))
669 mask->my_flags.use_circulate_hit_icon = on;
671 else if (StrEquals(cond,"CirculateHitShaded"))
673 mask->my_flags.use_circulate_hit_shaded = on;
675 else if (StrEquals(cond,"State"))
677 if (sscanf(tmp, "%u", &state) &&
678 state >= 0 && state <= 31)
680 state = (1 << state);
681 if (on)
683 SET_USER_STATES(mask, state);
685 else
687 CLEAR_USER_STATES(mask, state);
689 SETM_USER_STATES(mask, state);
690 tmp = SkipNTokens(tmp, 1);
693 else if (StrEquals(condition, "Layer"))
695 if (sscanf(tmp, "%d", &mask->layer))
697 tmp = SkipNTokens(tmp, 1);
698 if (mask->layer < 0)
700 /* silently ignore invalid layers */
701 mask->layer = -2;
704 else
706 /* needs current layer */
707 mask->layer = -1;
709 mask->my_flags.needs_same_layer = on;
711 else
713 struct name_condition *pp;
714 struct namelist *p;
715 char *condp = safestrdup(cond);
717 pp = (struct name_condition *)
718 safemalloc(sizeof(struct name_condition));
719 pp->invert = (!on ? True : False);
720 pp->namelist = NULL;
721 pp->next = mask->name_condition;
722 mask->name_condition = pp;
723 for (;;)
725 p = (struct namelist *)
726 safemalloc(sizeof(struct namelist));
727 p->name=condp;
728 p->next=pp->namelist;
729 pp->namelist=p;
730 while(*condp && *condp != '|')
732 condp++;
734 if(!*condp)
736 break;
738 *condp++='\0';
742 if (tmp && *tmp)
744 fvwm_msg(OLD, "CreateConditionMask",
745 "Use comma instead of whitespace to "
746 "separate conditions");
748 else
750 if (allocated_condition != NULL)
752 free(allocated_condition);
753 allocated_condition = NULL;
755 if (next_condition && *next_condition)
757 next_condition = GetNextFullOption(
758 next_condition, &allocated_condition);
760 tmp = allocated_condition;
762 condition = PeekToken(tmp, &tmp);
765 return;
769 * Checks whether the given window matches the mask created with
770 * CreateConditionMask.
771 * NOTE - exported via .h
773 Bool MatchesConditionMask(FvwmWindow *fw, WindowConditionMask *mask)
775 int does_match;
776 int is_on_desk;
777 int is_on_page;
778 int is_on_global_page;
779 FvwmWindow *sf = get_focus_window();
780 struct name_condition *pp;
781 struct namelist *p;
782 char *name;
784 /* match FixedSize conditional */
785 /* special treatment for FixedSize, because more than just
786 the is_size_fixed flag makes a window unresizable (width and height
787 hints etc.) */
788 if (IS_SIZE_FIXED(mask) &&
789 mask->flag_mask.common.s.is_size_fixed &&
790 is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, False))
792 return False;
794 if (!IS_SIZE_FIXED(mask) &&
795 mask->flag_mask.common.s.is_size_fixed &&
796 !is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, False))
798 return False;
800 if (IS_FIXED(mask) &&
801 mask->flag_mask.common.s.is_fixed &&
802 is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
804 return False;
806 if (!IS_FIXED(mask) &&
807 mask->flag_mask.common.s.is_fixed &&
808 !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
810 return False;
812 if (IS_UNICONIFIABLE(mask) &&
813 mask->flag_mask.common.s.is_uniconifiable &&
814 is_function_allowed(F_ICONIFY, NULL, fw, RQORIG_PROGRAM_US, False))
816 return False;
818 if (
819 !IS_UNICONIFIABLE(mask) &&
820 mask->flag_mask.common.s.is_uniconifiable &&
821 !is_function_allowed(
822 F_ICONIFY, NULL, fw, RQORIG_PROGRAM_US, False))
824 return False;
826 if (
827 IS_UNMAXIMIZABLE(mask) &&
828 mask->flag_mask.common.s.is_unmaximizable &&
829 is_function_allowed(
830 F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
832 return False;
834 if (
835 !IS_UNMAXIMIZABLE(mask) &&
836 mask->flag_mask.common.s.is_unmaximizable &&
837 !is_function_allowed(
838 F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
840 return False;
842 if (
843 IS_UNCLOSABLE(mask) &&
844 mask->flag_mask.common.s.is_unclosable &&
845 (is_function_allowed(
846 F_CLOSE, NULL, fw, RQORIG_PROGRAM_US,False) ||
847 is_function_allowed(
848 F_DELETE, NULL, fw, RQORIG_PROGRAM_US,False) ||
849 is_function_allowed(
850 F_DESTROY, NULL, fw, RQORIG_PROGRAM_US, False)))
852 return False;
854 if (
855 !IS_UNCLOSABLE(mask) &&
856 mask->flag_mask.common.s.is_unclosable &&
857 (!is_function_allowed(
858 F_CLOSE, NULL, fw, RQORIG_PROGRAM_US,False) &&
859 !is_function_allowed(
860 F_DELETE, NULL, fw, RQORIG_PROGRAM_US,False) &&
861 !is_function_allowed(
862 F_DESTROY, NULL, fw, RQORIG_PROGRAM_US,False)))
864 return False;
866 if (!blockcmpmask((char *)&(fw->flags), (char *)&(mask->flags),
867 (char *)&(mask->flag_mask), sizeof(fw->flags)))
869 return False;
871 if (!mask->my_flags.use_circulate_hit && DO_SKIP_CIRCULATE(fw))
873 return False;
875 if (!mask->my_flags.use_circulate_hit_icon && IS_ICONIFIED(fw) &&
876 DO_SKIP_ICON_CIRCULATE(fw))
878 return False;
880 if (!mask->my_flags.use_circulate_hit_shaded && IS_SHADED(fw) &&
881 DO_SKIP_SHADED_CIRCULATE(fw))
883 return False;
885 if (IS_ICONIFIED(fw) && IS_TRANSIENT(fw) && IS_ICONIFIED_BY_PARENT(fw))
887 return False;
890 /* desk and page matching */
891 is_on_desk = 1;
892 if (mask->my_flags.do_check_desk ||
893 mask->my_flags.do_check_desk_and_page ||
894 mask->my_flags.do_check_desk_and_global_page)
896 is_on_desk = (fw->Desk == Scr.CurrentDesk);
898 is_on_page = 1;
899 if (mask->my_flags.do_check_page ||
900 mask->my_flags.do_check_desk_and_page)
902 if (FScreenIsEnabled() && !mask->my_flags.do_not_check_screen)
904 is_on_page = !!FScreenIsRectangleOnScreen(
905 NULL, FSCREEN_CURRENT, &(fw->g.frame));
907 else
909 is_on_page = !!IsRectangleOnThisPage(
910 &(fw->g.frame), Scr.CurrentDesk);
913 is_on_global_page = 1;
914 if (mask->my_flags.do_check_global_page ||
915 mask->my_flags.do_check_desk_and_global_page)
917 is_on_global_page = !!IsRectangleOnThisPage(
918 &(fw->g.frame), Scr.CurrentDesk);
921 if (mask->my_flags.do_check_desk_and_page)
923 int is_on_desk_and_page;
925 is_on_desk_and_page = (is_on_desk && is_on_page);
926 if (mask->my_flags.needs_current_desk_and_page !=
927 is_on_desk_and_page)
929 return False;
932 else if (mask->my_flags.do_check_desk_and_global_page)
934 int is_on_desk_and_global_page;
936 is_on_desk_and_global_page = (is_on_desk && is_on_global_page);
937 if (mask->my_flags.needs_current_desk_and_global_page !=
938 is_on_desk_and_global_page)
940 return False;
943 if (mask->my_flags.do_check_desk &&
944 mask->my_flags.needs_current_desk != is_on_desk)
946 return False;
948 if (mask->my_flags.do_check_page)
950 if (mask->my_flags.needs_current_page != is_on_page)
952 return False;
955 else if (mask->my_flags.do_check_global_page)
957 if (mask->my_flags.needs_current_global_page !=
958 is_on_global_page)
960 return False;
964 for (pp = mask->name_condition; pp; pp = pp->next)
966 does_match = 0;
967 for (p = pp->namelist; p; p = p->next)
969 name=p->name;
970 does_match |= matchWildcards(name, fw->name.name);
971 does_match |= matchWildcards(name, fw->icon_name.name);
972 if(fw->class.res_class)
973 does_match |= matchWildcards(name,
974 fw->class.res_class);
975 if(fw->class.res_name)
976 does_match |= matchWildcards(name,
977 fw->class.res_name);
979 if(( pp->invert && does_match) ||
980 (!pp->invert && !does_match))
982 return False;
986 if (mask->layer == -1 && sf)
988 int is_same_layer;
990 is_same_layer = (fw->layer == sf->layer);
991 if (mask->my_flags.needs_same_layer != is_same_layer)
993 return False;
996 if (mask->layer >= 0)
998 int is_same_layer;
1000 is_same_layer = (fw->layer == mask->layer);
1001 if (mask->my_flags.needs_same_layer != is_same_layer)
1003 return False;
1006 if (mask->placed_by_button_set_mask)
1008 if (!((mask->placed_by_button_set_mask &
1009 (1<<(fw->placed_by_button - 1))) ==
1010 (mask->placed_by_button_set_mask &
1011 mask->placed_by_button_mask)))
1013 return False;
1016 if (GET_USER_STATES(mask) !=
1017 (mask->flag_mask.common.user_states & GET_USER_STATES(fw)))
1019 return False;
1021 if (mask->my_flags.use_do_accept_focus)
1023 Bool f;
1025 f = focus_does_accept_input_focus(fw);
1026 if (fw && !FP_DO_FOCUS_BY_FUNCTION(FW_FOCUS_POLICY(fw)))
1028 f = False;
1030 else if (fw && FP_IS_LENIENT(FW_FOCUS_POLICY(fw)))
1032 f = True;
1034 if (!f != !mask->my_flags.do_accept_focus)
1036 return False;
1039 if (mask->my_flags.needs_focus != NEEDS_ANY)
1041 int is_focused;
1043 is_focused = (fw == get_focus_window());
1044 if (!is_focused && mask->my_flags.needs_focus == NEEDS_TRUE)
1046 return False;
1048 else if (is_focused &&
1049 mask->my_flags.needs_focus == NEEDS_FALSE)
1051 return False;
1054 if (mask->my_flags.needs_pointer != NEEDS_ANY)
1056 int has_pointer;
1057 FvwmWindow *t;
1059 t = get_pointer_fvwm_window();
1060 if (t != NULL && t == fw)
1062 has_pointer = 1;
1064 else
1066 has_pointer = 0;
1068 if (!has_pointer && mask->my_flags.needs_pointer == NEEDS_TRUE)
1070 return False;
1072 else if (has_pointer &&
1073 mask->my_flags.needs_pointer == NEEDS_FALSE)
1075 return False;
1078 if (mask->my_flags.do_check_overlapped)
1080 int is_o;
1082 is_o = (is_on_top_of_layer(fw) == False);
1083 if (is_o != mask->my_flags.needs_overlapped)
1085 return False;
1089 return True;
1092 static void direction_cmd(F_CMD_ARGS, Bool is_scan)
1094 rectangle my_g;
1095 rectangle his_g;
1096 int my_cx;
1097 int my_cy;
1098 int his_cx;
1099 int his_cy;
1100 int cross = 0;
1101 int offset = 0;
1102 int distance = 0;
1103 int cycle = False;
1104 int forward = False;
1105 int score;
1106 int best_cross = 0;
1107 int best_score;
1108 int worst_score = -1;
1109 FvwmWindow *tfw;
1110 FvwmWindow *fw_best;
1111 int dir;
1112 int dir2;
1113 Bool right_handed=False;
1114 char *flags;
1115 char *restofline;
1116 char *tmp;
1117 float tx;
1118 float ty;
1119 WindowConditionMask mask;
1120 Bool is_pointer_relative;
1121 FvwmWindow * const fw = exc->w.fw;
1123 /* Parse the direction. */
1124 tmp = PeekToken(action, &action);
1125 if (StrEquals(tmp, "FromPointer"))
1127 is_pointer_relative = True;
1128 tmp = PeekToken(action, &action);
1130 else
1132 is_pointer_relative = False;
1134 dir = gravity_parse_dir_argument(tmp, NULL, -1);
1135 if (dir == -1 || dir > DIR_ALL_MASK)
1137 fvwm_msg(ERR, "Direction", "Invalid direction %s",
1138 (tmp) ? tmp : "");
1139 if (cond_rc != NULL)
1141 cond_rc->rc = COND_RC_ERROR;
1143 return;
1145 if (is_scan)
1147 cycle = True;
1148 tmp = PeekToken(action, &action);
1149 if ( ! tmp )
1151 fvwm_msg(
1152 ERR, "Direction", "Missing minor direction %s",
1153 (tmp) ? tmp : "");
1154 if (cond_rc != NULL)
1156 cond_rc->rc = COND_RC_ERROR;
1158 return;
1160 dir2 = gravity_parse_dir_argument(tmp, NULL, -1);
1161 /* if enum direction_t changes, this is trashed. */
1162 if (dir2 == -1 || dir2 > DIR_NW ||
1163 (dir < 4) != (dir2 < 4) || (abs(dir - dir2) & 1) != 1)
1165 fvwm_msg(
1166 ERR, "Direction", "Invalid minor direction %s",
1167 (tmp) ? tmp : "");
1168 if (cond_rc != NULL)
1170 cond_rc->rc = COND_RC_ERROR;
1172 return;
1174 else if (dir2 - dir == 1 || dir2 - dir == -3)
1176 right_handed=True;
1180 /* Create the mask for flags */
1181 flags = CreateFlagString(action, &restofline);
1182 if (!restofline)
1184 if (flags)
1186 free(flags);
1188 if (cond_rc != NULL)
1190 cond_rc->rc = COND_RC_NO_MATCH;
1192 return;
1194 DefaultConditionMask(&mask);
1195 CreateConditionMask(flags, &mask);
1196 if (flags)
1198 free(flags);
1201 /* If there is a focused window, use that as a starting point.
1202 * Otherwise we use the pointer as a starting point. */
1203 if (fw && is_pointer_relative == False)
1205 get_visible_window_or_icon_geometry(fw, &my_g);
1206 my_cx = my_g.x + my_g.width / 2;
1207 my_cy = my_g.y + my_g.height / 2;
1209 else
1211 if (FQueryPointer(
1212 dpy, Scr.Root, &JunkRoot, &JunkChild, &my_g.x,
1213 &my_g.y, &JunkX, &JunkY, &JunkMask) == False)
1215 /* pointer is on a different screen */
1216 my_g.x = 0;
1217 my_g.y = 0;
1219 my_g.width = 1;
1220 my_g.height = 1;
1221 my_cx = my_g.x;
1222 my_cy = my_g.y;
1225 /* Next we iterate through all windows and choose the closest one in
1226 * the wanted direction. */
1227 fw_best = NULL;
1228 best_score = -1;
1229 for (tfw = Scr.FvwmRoot.next; tfw != NULL; tfw = tfw->next)
1231 /* Skip every window that does not match conditionals. Also
1232 * skip the currently focused window. That would be too
1233 * close. :) */
1234 if (tfw == fw || !MatchesConditionMask(tfw, &mask))
1236 continue;
1239 /* Calculate relative location of the window. */
1240 get_visible_window_or_icon_geometry(tfw, &his_g);
1241 his_g.x -= my_cx;
1242 his_g.y -= my_cy;
1243 his_cx = his_g.x + his_g.width / 2;
1244 his_cy = his_g.y + his_g.height / 2;
1246 if (dir > DIR_MAJOR_MASK && dir <= DIR_MINOR_MASK)
1248 int tx;
1249 /* Rotate the diagonals 45 degrees counterclockwise. To
1250 * do this, multiply the matrix /+h +h\ with the vector
1251 * (x y). \-h +h/
1252 * h = sqrt(0.5). We can set h := 1 since absolute
1253 * distance doesn't * matter here. */
1254 tx = his_cx + his_cy;
1255 his_cy = -his_cx + his_cy;
1256 his_cx = tx;
1258 /* Arrange so that distance and offset are positive in desired
1259 * direction. */
1260 switch (dir)
1262 case DIR_S:
1263 case DIR_SW:
1264 forward = True;
1265 case DIR_N:
1266 case DIR_NE:
1267 cross = -his_cx;
1268 offset = (his_cx < 0) ? -his_cx : his_cx;
1269 distance = (dir == DIR_N || dir == DIR_NE) ?
1270 -his_cy : his_cy;
1271 break;
1272 case DIR_E: /* E */
1273 case DIR_SE: /* SE */
1274 forward = True;
1275 case DIR_W: /* W */
1276 case DIR_NW: /* NW */
1277 cross = his_cy;
1278 offset = (his_cy < 0) ? -his_cy : his_cy;
1279 distance = (dir == DIR_W || dir == DIR_NW) ?
1280 -his_cx : his_cx;
1281 break;
1282 case DIR_C:
1283 offset = 0;
1284 tx = (float)his_cx;
1285 ty = (float)his_cy;
1286 distance = (int)sqrt(tx * tx + ty * ty);
1287 break;
1290 if (cycle)
1292 offset=0;
1294 else if (distance < 0)
1296 /* Target must be in given direction. */
1297 continue;
1299 else if (distance == 0 && dir != DIR_C)
1301 continue;
1304 /* Calculate score for this window. The smaller the better. */
1305 score = distance + offset;
1306 if (!right_handed)
1308 cross= -cross;
1310 if (cycle)
1312 int ordered = (forward == (cross < best_cross));
1314 if (distance < 0 && best_score == -1 &&
1315 (score < worst_score ||
1316 (score == worst_score && ordered)))
1318 fw_best = tfw;
1319 worst_score = score;
1320 best_cross = cross;
1322 if (score == 0 && forward == (cross < 0) &&
1323 dir != DIR_C)
1325 continue;
1327 if (distance >= 0 &&
1328 (best_score == -1 || score < best_score ||
1329 (score == best_score && ordered)))
1331 fw_best = tfw;
1332 best_score = score;
1333 best_cross = cross;
1336 else
1338 /* windows more than 45 degrees off the direction are
1339 * heavily penalized and will only be chosen if nothing
1340 * else within a million pixels */
1341 if (offset > distance)
1343 score += 1000000;
1345 if (best_score == -1 || score < best_score ||
1346 (score == best_score && dir == DIR_C))
1348 fw_best = tfw;
1349 best_score = score;
1352 } /* for */
1354 if (fw_best)
1356 if (cond_rc != NULL)
1358 cond_rc->rc = COND_RC_OK;
1360 execute_function_override_window(
1361 cond_rc, exc, restofline, 0, fw_best);
1363 else if (cond_rc != NULL)
1365 cond_rc->rc = COND_RC_NO_MATCH;
1367 FreeConditionMask(&mask);
1369 return;
1372 static int __rc_matches_rcstring_consume(
1373 char **ret_rest, cond_rc_t *cond_rc, char *action)
1375 cond_rc_enum match_rc;
1376 char *orig_flags;
1377 char *flags;
1378 int is_not_reversed = 1;
1379 int ret;
1381 /* Create window mask */
1382 orig_flags = CreateFlagString(action, ret_rest);
1383 flags = orig_flags;
1384 if (flags == NULL)
1386 match_rc = COND_RC_NO_MATCH;
1388 else
1390 if (*flags == '!')
1392 is_not_reversed = 0;
1393 flags++;
1395 if (StrEquals(flags, "1") || StrEquals(flags, "match"))
1397 match_rc = COND_RC_OK;
1399 else if (StrEquals(flags, "0") || StrEquals(flags, "nomatch"))
1401 match_rc = COND_RC_NO_MATCH;
1403 else if (StrEquals(flags, "-1") || StrEquals(flags, "error"))
1405 match_rc = COND_RC_ERROR;
1407 else if (StrEquals(flags, "-2") || StrEquals(flags, "break"))
1409 match_rc = COND_RC_BREAK;
1411 else
1413 match_rc = COND_RC_NO_MATCH;
1414 /* Does anyone check for other numerical returncode
1415 * values? If so, this might have to be changed. */
1416 fprintf(
1417 stderr, "Unrecognised condition \"%s\" in"
1418 " TestRc command.\n", flags);
1421 if (orig_flags != NULL)
1423 free(orig_flags);
1425 ret = ((cond_rc->rc == match_rc) == is_not_reversed);
1427 return ret;
1430 /* ---------------------------- builtin commands --------------------------- */
1432 void CMD_Prev(F_CMD_ARGS)
1434 circulate_cmd(F_PASS_ARGS, C_WINDOW, -1, True, True);
1436 return;
1439 void CMD_Next(F_CMD_ARGS)
1441 circulate_cmd(F_PASS_ARGS, C_WINDOW, 1, True, True);
1443 return;
1446 void CMD_None(F_CMD_ARGS)
1448 circulate_cmd(F_PASS_ARGS, C_ROOT, 1, False, False);
1449 /* invert return code */
1450 switch (cond_rc->rc)
1452 case COND_RC_OK:
1453 cond_rc->rc = COND_RC_NO_MATCH;
1454 break;
1455 case COND_RC_NO_MATCH:
1456 cond_rc->rc = COND_RC_OK;
1457 break;
1458 default:
1459 break;
1462 return;
1465 void CMD_Any(F_CMD_ARGS)
1467 circulate_cmd(F_PASS_ARGS, exc->w.wcontext, 1, False, True);
1469 return;
1472 void CMD_Current(F_CMD_ARGS)
1474 circulate_cmd(F_PASS_ARGS, C_WINDOW, 0, True, True);
1476 return;
1479 void CMD_PointerWindow(F_CMD_ARGS)
1481 exec_context_changes_t ecc;
1483 ecc.w.fw = get_pointer_fvwm_window();
1484 exc = exc_clone_context(exc, &ecc, ECC_FW);
1485 select_cmd(F_PASS_ARGS);
1486 exc_destroy_context(exc);
1488 return;
1491 void CMD_ThisWindow(F_CMD_ARGS)
1493 select_cmd(F_PASS_ARGS);
1495 return;
1498 void CMD_Pick(F_CMD_ARGS)
1500 select_cmd(F_PASS_ARGS);
1502 return;
1505 void CMD_All(F_CMD_ARGS)
1507 FvwmWindow *t, **g;
1508 char *restofline;
1509 WindowConditionMask mask;
1510 char *flags;
1511 int num, i;
1512 Bool does_any_window_match = False;
1513 char *token;
1514 Bool do_reverse = False;
1515 Bool use_stack = False;
1517 while (True) /* break when a non-option is found */
1519 token = PeekToken(action, &restofline);
1520 if (StrEquals(token, "Reverse"))
1522 if (!*restofline)
1524 /* if not any more actions, then Reverse
1525 * probably is some user function, so ignore
1526 * it and do the old behaviour */
1527 break;
1529 else
1531 do_reverse = True;
1532 action = restofline;
1535 else if (StrEquals(token, "UseStack"))
1537 if (!*restofline)
1539 /* if not any more actions, then UseStack
1540 * probably is some user function, so ignore
1541 * it and do the old behaviour */
1542 break;
1544 else
1546 use_stack = True;
1547 action = restofline;
1550 else
1552 /* No more options -- continue with flags and
1553 * commands */
1554 break;
1558 flags = CreateFlagString(action, &restofline);
1559 DefaultConditionMask(&mask);
1560 mask.my_flags.use_circulate_hit = 1;
1561 mask.my_flags.use_circulate_hit_icon = 1;
1562 mask.my_flags.use_circulate_hit_shaded = 1;
1563 CreateConditionMask(flags, &mask);
1564 if (flags)
1566 free(flags);
1569 num = 0;
1570 for (t = Scr.FvwmRoot.next; t; t = t->next)
1572 num++;
1574 g = (FvwmWindow **)safemalloc(num * sizeof(FvwmWindow *));
1575 num = 0;
1576 if (!use_stack)
1578 for (t = Scr.FvwmRoot.next; t; t = t->next)
1580 if (MatchesConditionMask(t, &mask))
1582 g[num++] = t;
1583 does_any_window_match = True;
1587 else
1589 for (t = Scr.FvwmRoot.stack_next; t && t != &Scr.FvwmRoot;
1590 t = t->stack_next)
1592 if (MatchesConditionMask(t, &mask))
1594 g[num++] = t;
1595 does_any_window_match = True;
1599 if (do_reverse)
1601 for (i = num-1; i >= 0; i--)
1603 execute_function_override_window(
1604 cond_rc, exc, restofline, 0, g[i]);
1607 else
1609 for (i = 0; i < num; i++)
1611 execute_function_override_window(
1612 cond_rc, exc, restofline, 0, g[i]);
1615 if (cond_rc != NULL && cond_rc->rc != COND_RC_BREAK)
1617 cond_rc->rc = (does_any_window_match == False) ?
1618 COND_RC_NO_MATCH : COND_RC_OK;
1620 free(g);
1621 FreeConditionMask(&mask);
1623 return;
1627 * Execute a function to the closest window in the given
1628 * direction.
1630 void CMD_Direction(F_CMD_ARGS)
1632 direction_cmd(F_PASS_ARGS,False);
1635 void CMD_ScanForWindow(F_CMD_ARGS)
1637 direction_cmd(F_PASS_ARGS,True);
1640 void CMD_WindowId(F_CMD_ARGS)
1642 FvwmWindow *t;
1643 char *token;
1644 char *naction;
1645 unsigned long win;
1646 Bool use_condition = False;
1647 Bool use_screenroot = False;
1648 WindowConditionMask mask;
1649 char *flags, *restofline;
1651 /* Get window ID */
1652 action = GetNextToken(action, &token);
1654 if (token && StrEquals(token, "root"))
1656 int screen = Scr.screen;
1658 free(token);
1659 token = PeekToken(action, &naction);
1660 if (!token || GetIntegerArguments(token, NULL, &screen, 1) != 1)
1662 screen = Scr.screen;
1664 else
1666 action = naction;
1668 use_screenroot = True;
1669 if (screen < 0 || screen >= Scr.NumberOfScreens)
1671 screen = 0;
1673 win = XRootWindow(dpy, screen);
1674 if (win == None)
1676 if (cond_rc != NULL)
1678 cond_rc->rc = COND_RC_ERROR;
1680 return;
1683 else if (token)
1685 /* SunOS doesn't have strtoul */
1686 win = (unsigned long)strtol(token, NULL, 0);
1687 free(token);
1689 else
1691 win = 0;
1694 /* Look for condition - CreateFlagString returns NULL if no '(' or '['
1696 if (!use_screenroot)
1698 flags = CreateFlagString(action, &restofline);
1699 if (flags)
1701 /* Create window mask */
1702 use_condition = True;
1703 DefaultConditionMask(&mask);
1705 /* override for Current [] */
1706 mask.my_flags.use_circulate_hit = 1;
1707 mask.my_flags.use_circulate_hit_icon = 1;
1708 mask.my_flags.use_circulate_hit_icon = 1;
1710 CreateConditionMask(flags, &mask);
1711 free(flags);
1713 /* Relocate action */
1714 action = restofline;
1718 /* Search windows */
1719 for (t = Scr.FvwmRoot.next; t; t = t->next)
1721 if (FW_W(t) == win)
1723 /* do it if no conditions or the conditions match */
1724 if (action && (!use_condition ||
1725 MatchesConditionMask(t, &mask)))
1727 if (cond_rc != NULL)
1729 cond_rc->rc = COND_RC_OK;
1731 execute_function_override_window(
1732 cond_rc, exc, action, 0, t);
1734 else if (cond_rc != NULL)
1736 cond_rc->rc = COND_RC_NO_MATCH;
1738 break;
1741 if (!t)
1743 /* The window is not managed by fvwm. Still some functions may
1744 * work on it. */
1745 if (use_condition)
1747 if (cond_rc != NULL)
1749 cond_rc->rc = COND_RC_ERROR;
1752 else if (XGetGeometry(
1753 dpy, win, &JunkRoot, &JunkX, &JunkY,
1754 (unsigned int*)&JunkWidth,
1755 (unsigned int*)&JunkHeight,
1756 (unsigned int*)&JunkBW,
1757 (unsigned int*)&JunkDepth) != 0)
1759 if (cond_rc != NULL)
1761 cond_rc->rc = COND_RC_OK;
1763 if (action != NULL)
1765 const exec_context_t *exc2;
1766 exec_context_changes_t ecc;
1768 ecc.w.fw = NULL;
1769 ecc.w.w = win;
1770 ecc.w.wcontext = C_UNMANAGED;
1771 exc2 = exc_clone_context(
1772 exc, &ecc,
1773 ECC_FW | ECC_W | ECC_WCONTEXT);
1774 execute_function(
1775 cond_rc, exc2, action,
1776 FUNC_IS_UNMANAGED);
1777 exc_destroy_context(exc2);
1780 else
1782 /* window id does not exist */
1783 if (cond_rc != NULL)
1785 cond_rc->rc = COND_RC_ERROR;
1790 /* Tidy up */
1791 if (use_condition)
1793 FreeConditionMask(&mask);
1796 return;
1799 void CMD_TestRc(F_CMD_ARGS)
1801 char *rest;
1803 if (cond_rc == NULL)
1805 /* useless if no return code to compare to is given */
1806 return;
1808 if (__rc_matches_rcstring_consume(&rest, cond_rc, action) &&
1809 rest != NULL)
1811 /* execute the command in root window context; overwrite the
1812 * return code with the return code of the command */
1813 execute_function(cond_rc, exc, rest, 0);
1816 return;
1819 void CMD_Break(F_CMD_ARGS)
1821 int rc;
1823 if (cond_rc == NULL)
1825 return;
1827 rc = GetIntegerArguments(action, &action, &cond_rc->break_levels, 1);
1828 if (rc != 1 || cond_rc->break_levels <= 0)
1830 cond_rc->break_levels = -1;
1832 cond_rc->rc = COND_RC_BREAK;
1834 return;
1837 void CMD_NoWindow(F_CMD_ARGS)
1839 execute_function_override_window(cond_rc, exc, action, 0, NULL);
1841 return;
1844 /* ver() - convert a version string to a floating-point number that
1845 * can be used to compare different versions.
1846 * ie. converts "2.5.11" to 2005011 */
1847 static int ver (char *str)
1849 char *n;
1850 int v;
1852 str = DoPeekToken(str, &n, NULL, ".", NULL);
1853 if (!n)
1855 return -1.0;
1857 v = atoi(n) * 1000000;
1858 str = DoPeekToken(str, &n, NULL, ".", NULL);
1859 if (!n)
1861 return -1.0;
1863 v += atoi(n) * 1000;
1864 str = DoPeekToken(str, &n, NULL, ".", NULL);
1865 if (!n)
1867 return -1.0;
1869 v += atoi(n);
1871 return v;
1874 /* match_version() - compare $version against this version of fvwm
1875 * using the operator specified by $operator. */
1876 static Bool match_version(char *version, char *operator)
1878 static int fvwm_version = -1;
1879 const int v = ver(version);
1881 if (fvwm_version < 0)
1883 char *tmp = safestrdup(VERSION);
1884 fvwm_version = ver(tmp);
1885 free(tmp);
1887 if (v < 0)
1889 fprintf(
1890 stderr, "match_version: Invalid version: %s\n",
1891 version);
1892 return False;
1894 if (strcmp(operator, ">=") == 0)
1896 return fvwm_version >= v;
1898 else if (strcmp(operator, ">") == 0)
1900 return fvwm_version > v;
1902 else if (strcmp(operator, "<=") == 0)
1904 return fvwm_version <= v;
1906 else if (strcmp(operator, "<") == 0)
1908 return fvwm_version < v;
1910 else if (strcmp(operator, "==") == 0)
1912 return (v == fvwm_version);
1914 else if (strcmp(operator, "!=") == 0)
1916 return (v != fvwm_version);
1918 else
1920 fprintf(
1921 stderr, "match_version: Invalid operator: %s\n",
1922 operator);
1925 return False;
1928 void CMD_Test(F_CMD_ARGS)
1930 char *restofline;
1931 char *flags;
1932 char *condition;
1933 char *flags_ptr;
1934 int match;
1935 int error;
1937 flags = CreateFlagString(action, &restofline);
1939 /* Next parse the flags in the string. */
1940 flags_ptr = flags;
1941 flags_ptr = GetNextSimpleOption(flags_ptr, &condition);
1943 match = 1;
1944 error = 0;
1945 while (condition)
1947 char *cond;
1948 int reverse;
1950 cond = condition;
1951 reverse = 0;
1952 if (*cond == '!')
1954 reverse = 1;
1955 cond++;
1957 if (StrEquals(cond, "True"))
1959 match = 1;
1961 else if (StrEquals(cond, "False"))
1963 match = 0;
1965 else if (StrEquals(cond, "Version"))
1967 char *pattern;
1968 flags_ptr = GetNextSimpleOption(flags_ptr, &pattern);
1969 if (pattern)
1971 char *ver;
1972 flags_ptr = GetNextSimpleOption(
1973 flags_ptr, &ver);
1974 if (ver == NULL)
1976 match = matchWildcards(
1977 pattern, VERSION);
1979 else
1981 match = match_version(ver, pattern);
1982 free(ver);
1984 free(pattern);
1986 else
1988 error = 1;
1991 else if (StrEquals(cond, "Start"))
1993 match = exc->type == EXCT_INIT ||
1994 exc->type == EXCT_RESTART;
1996 else if (StrEquals(cond, "Init"))
1998 match = exc->type == EXCT_INIT;
2000 else if (StrEquals(cond, "Restart"))
2002 match = exc->type == EXCT_RESTART;
2004 else if (StrEquals(cond, "Exit"))
2006 match = exc->type == EXCT_QUIT ||
2007 exc->type == EXCT_TORESTART;
2009 else if (StrEquals(cond, "Quit"))
2011 match = exc->type == EXCT_QUIT;
2013 else if (StrEquals(cond, "ToRestart"))
2015 match = exc->type == EXCT_TORESTART;
2017 else if (StrEquals(cond, "x") || StrEquals(cond, "r") ||
2018 StrEquals(cond, "w") || StrEquals(cond, "f") ||
2019 StrEquals(cond, "i"))
2021 char *pattern;
2022 int type = X_OK;
2023 Bool im = 0;
2025 switch(cond[0])
2027 case 'X':
2028 case 'x':
2029 type = X_OK;
2030 break;
2031 case 'R':
2032 case 'r':
2033 type = R_OK;
2034 break;
2035 case 'W':
2036 case 'w':
2037 type = W_OK;
2038 break;
2039 case 'f':
2040 case 'F':
2041 type = F_OK;
2042 break;
2043 case 'i':
2044 case 'I':
2045 im = True;
2046 type = R_OK;
2047 break;
2048 default:
2049 /* cannot happen */
2050 break;
2052 flags_ptr = GetNextSimpleOption(flags_ptr, &pattern);
2053 if (pattern)
2055 match = cond_check_access(pattern, type, im);
2056 free(pattern);
2058 else
2060 error = 1;
2063 else if (StrEquals(cond, "EnvIsSet"))
2065 char *var_name;
2067 flags_ptr = GetNextSimpleOption(flags_ptr, &var_name);
2068 if (var_name)
2070 const char *value = getenv(var_name);
2072 match = (value != NULL) ? 1 : 0;
2074 else
2076 error = 1;
2079 else if (StrEquals(cond, "EnvMatch"))
2081 char *var_name;
2083 flags_ptr = GetNextSimpleOption(flags_ptr, &var_name);
2084 if (var_name)
2086 const char *value = getenv(var_name);
2087 char *pattern;
2088 /* unfortunately, GetNextSimpleOption is
2089 * broken, does not accept quoted empty "" */
2090 flags_ptr = GetNextSimpleOption(
2091 flags_ptr, &pattern);
2092 if (!value)
2094 value = "";
2096 if (pattern)
2098 match =
2099 /* include empty string case */
2100 (!pattern[0] && !value[0]) ||
2101 matchWildcards(pattern, value);
2103 else
2105 error = 1;
2108 else
2110 error = 1;
2113 else if (StrEquals(cond, "EdgeIsActive"))
2115 direction_t dir= DIR_NONE;
2116 char *dirname;
2117 char *next;
2118 next = GetNextSimpleOption(flags_ptr, &dirname);
2119 if (dirname)
2121 dir = gravity_parse_dir_argument(
2122 dirname, NULL, DIR_NONE);
2123 if (dir == DIR_NONE)
2125 if (!StrEquals(dirname, "Any"))
2127 next = flags_ptr;
2130 else if (dir > DIR_MAJOR_MASK)
2132 error = 1;
2134 free(dirname);
2137 if (!error)
2139 if (((dir == DIR_W || dir == DIR_NONE) &&
2140 Scr.PanFrameLeft.isMapped) ||
2141 ((dir == DIR_N || dir == DIR_NONE) &&
2142 Scr.PanFrameTop.isMapped) ||
2143 ((dir == DIR_S || dir == DIR_NONE) &&
2144 Scr.PanFrameBottom.isMapped) ||
2145 ((dir == DIR_E || dir == DIR_NONE) &&
2146 Scr.PanFrameRight.isMapped))
2148 match = 1;
2150 else
2152 match = 0;
2155 flags_ptr = next;
2157 else if (StrEquals(cond, "EdgeHasPointer"))
2159 int x,y;
2160 Window win;
2161 direction_t dir = DIR_NONE;
2162 char *dirname;
2163 char *next;
2164 next = GetNextSimpleOption(flags_ptr, &dirname);
2165 if (dirname)
2167 dir = gravity_parse_dir_argument(
2168 dirname, NULL, DIR_NONE);
2169 if (dir == DIR_NONE)
2171 if (!StrEquals(dirname, "Any"))
2173 next = flags_ptr;
2176 else if (dir > DIR_MAJOR_MASK)
2178 error = 1;
2180 free(dirname);
2183 if (!error)
2185 if (FQueryPointer(
2186 dpy, Scr.Root, &JunkRoot, &win,
2187 &JunkX, &JunkY, &x, &y, &JunkMask)
2188 == False)
2190 /* pointer is on a different screen */
2191 match = 0;
2193 else if (is_pan_frame(win))
2195 if (dir == DIR_NONE ||
2196 (dir == DIR_N &&
2197 win == Scr.PanFrameTop.win) ||
2198 (dir == DIR_S &&
2199 win == Scr.PanFrameBottom.win) ||
2200 (dir == DIR_E &&
2201 win == Scr.PanFrameRight.win) ||
2202 (dir == DIR_W &&
2203 win == Scr.PanFrameLeft.win))
2205 match = 1;
2207 else
2209 match = 0;
2212 else
2214 match = 0;
2217 flags_ptr = next;
2219 else
2221 /* unrecognized condition */
2222 error = 1;
2223 fprintf(
2224 stderr, "Unrecognised condition \"%s\" in"
2225 " Test command.\n", cond);
2228 if (reverse)
2230 match = !match;
2232 free(condition);
2233 if (error || !match)
2235 break;
2237 flags_ptr = GetNextSimpleOption(flags_ptr, &condition);
2240 if (flags != NULL)
2242 free(flags);
2244 if (!error && match)
2246 execute_function(cond_rc, exc, restofline, 0);
2248 if (cond_rc != NULL)
2250 if (error)
2252 cond_rc->rc = COND_RC_ERROR;
2254 else if (match)
2256 cond_rc->rc = COND_RC_OK;
2258 else
2260 cond_rc->rc = COND_RC_NO_MATCH;
2264 return;