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
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 ---------------------- */
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"
40 #include "execcontext.h"
41 #include "functions.h"
42 #include "conditional.h"
51 #include "decorations.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
)
83 FvwmWindow
*fw
, *found
= NULL
;
84 WindowConditionMask mask
;
87 /* Create window mask */
88 flags
= CreateFlagString(action
, restofline
);
89 DefaultConditionMask(&mask
);
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
);
101 if (sf
== NULL
|| Direction
== 0)
103 sf
= get_focus_window();
111 else if (Direction
< 0)
125 FreeConditionMask(&mask
);
130 for (pass
= 0; pass
< 3 && !found
; pass
++)
132 while (fw
&& !found
&& fw
!= &Scr
.FvwmRoot
)
134 /* Make CirculateUp and CirculateDown take args. by
136 if (MatchesConditionMask(fw
, &mask
))
153 FreeConditionMask(&mask
);
157 if (fw
== NULL
|| fw
== &Scr
.FvwmRoot
)
161 /* Go to end of list */
162 for (fw
= Scr
.FvwmRoot
.next
; fw
&& fw
->next
;
170 /* Go to top of list */
171 fw
= Scr
.FvwmRoot
.next
;
175 FreeConditionMask(&mask
);
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
)
187 found
= Circulate(exc
->w
.fw
, action
, circ_dir
, &restofline
);
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
;
198 ecc
.w
.fw
= (do_use_found
== True
) ? found
: NULL
;
201 ecc
.w
.w
= FW_W(found
);
202 flags
= FUNC_DONT_DEFER
;
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
);
219 static void select_cmd(F_CMD_ARGS
)
223 WindowConditionMask mask
;
224 FvwmWindow
* const fw
= exc
->w
.fw
;
226 if (!fw
|| IS_EWMH_DESKTOP(FW_W(fw
)))
230 cond_rc
->rc
= COND_RC_ERROR
;
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
);
244 if (MatchesConditionMask(fw
, &mask
) && restofline
)
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
);
262 static Bool
cond_check_access(char *file
, int type
, Bool im
)
267 if (!file
|| *file
== 0)
273 if (access(file
, type
) == 0)
282 if (type
!= X_OK
&& im
== False
)
288 path
= getenv("PATH");
292 path
= PictureGetImagePath();
294 if (path
== NULL
|| *path
== 0)
298 full_file
= searchPath(path
, file
, NULL
, type
);
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
)
325 while (isspace((unsigned char)*c
) && (*c
!= 0))
331 if (*c
== '[' || *c
== '(')
335 /* Get the text between [ ] or ( ) */
347 while (*c
!= closeopt
)
351 fvwm_msg(ERR
, "CreateFlagString",
352 "Conditionals require closing "
358 /* skip quoted string */
359 d
= SkipQuote(c
, NULL
, NULL
, NULL
);
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
);
382 * The name_condition field of the mask is allocated in CreateConditionMask.
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
; )
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 */
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
;
443 /* Next parse the flags in the string. */
444 next_condition
= GetNextFullOption(flags
, &allocated_condition
);
445 condition
= PeekToken(allocated_condition
, &tmp
);
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"))
494 if (sscanf(tmp
, "%d", &button
) &&
496 button
<= NUMBER_OF_EXTENDED_MOUSE_BUTTONS
))
498 tmp
= SkipNTokens(tmp
, 1);
499 button_mask
= (1<<(button
-1));
504 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS
) - 1;
508 if (mask
->placed_by_button_mask
&
509 mask
->placed_by_button_set_mask
&
512 fvwm_msg(WARN
, "PlacedByButton",
513 "Condition always False.");
515 mask
->placed_by_button_mask
|= button_mask
;
519 mask
->placed_by_button_mask
&= ~button_mask
;
521 mask
->placed_by_button_set_mask
|= button_mask
;
523 else if (StrEquals(cond
,"PlacedByButton3"))
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);
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"))
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
);
683 SET_USER_STATES(mask
, state
);
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);
700 /* silently ignore invalid layers */
706 /* needs current layer */
709 mask
->my_flags
.needs_same_layer
= on
;
713 struct name_condition
*pp
;
715 char *condp
= safestrdup(cond
);
717 pp
= (struct name_condition
*)
718 safemalloc(sizeof(struct name_condition
));
719 pp
->invert
= (!on
? True
: False
);
721 pp
->next
= mask
->name_condition
;
722 mask
->name_condition
= pp
;
725 p
= (struct namelist
*)
726 safemalloc(sizeof(struct namelist
));
728 p
->next
=pp
->namelist
;
730 while(*condp
&& *condp
!= '|')
744 fvwm_msg(OLD
, "CreateConditionMask",
745 "Use comma instead of whitespace to "
746 "separate conditions");
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
);
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
)
778 int is_on_global_page
;
779 FvwmWindow
*sf
= get_focus_window();
780 struct name_condition
*pp
;
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
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
))
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
))
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
))
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
))
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
))
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
))
827 IS_UNMAXIMIZABLE(mask
) &&
828 mask
->flag_mask
.common
.s
.is_unmaximizable
&&
830 F_MAXIMIZE
, NULL
, fw
, RQORIG_PROGRAM_US
, False
))
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
))
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
) ||
848 F_DELETE
, NULL
, fw
, RQORIG_PROGRAM_US
,False
) ||
850 F_DESTROY
, NULL
, fw
, RQORIG_PROGRAM_US
, False
)))
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
)))
866 if (!blockcmpmask((char *)&(fw
->flags
), (char *)&(mask
->flags
),
867 (char *)&(mask
->flag_mask
), sizeof(fw
->flags
)))
871 if (!mask
->my_flags
.use_circulate_hit
&& DO_SKIP_CIRCULATE(fw
))
875 if (!mask
->my_flags
.use_circulate_hit_icon
&& IS_ICONIFIED(fw
) &&
876 DO_SKIP_ICON_CIRCULATE(fw
))
880 if (!mask
->my_flags
.use_circulate_hit_shaded
&& IS_SHADED(fw
) &&
881 DO_SKIP_SHADED_CIRCULATE(fw
))
885 if (IS_ICONIFIED(fw
) && IS_TRANSIENT(fw
) && IS_ICONIFIED_BY_PARENT(fw
))
890 /* desk and page matching */
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
);
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
));
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
!=
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
)
943 if (mask
->my_flags
.do_check_desk
&&
944 mask
->my_flags
.needs_current_desk
!= is_on_desk
)
948 if (mask
->my_flags
.do_check_page
)
950 if (mask
->my_flags
.needs_current_page
!= is_on_page
)
955 else if (mask
->my_flags
.do_check_global_page
)
957 if (mask
->my_flags
.needs_current_global_page
!=
964 for (pp
= mask
->name_condition
; pp
; pp
= pp
->next
)
967 for (p
= pp
->namelist
; p
; p
= p
->next
)
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
,
979 if(( pp
->invert
&& does_match
) ||
980 (!pp
->invert
&& !does_match
))
986 if (mask
->layer
== -1 && sf
)
990 is_same_layer
= (fw
->layer
== sf
->layer
);
991 if (mask
->my_flags
.needs_same_layer
!= is_same_layer
)
996 if (mask
->layer
>= 0)
1000 is_same_layer
= (fw
->layer
== mask
->layer
);
1001 if (mask
->my_flags
.needs_same_layer
!= is_same_layer
)
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
)))
1016 if (GET_USER_STATES(mask
) !=
1017 (mask
->flag_mask
.common
.user_states
& GET_USER_STATES(fw
)))
1021 if (mask
->my_flags
.use_do_accept_focus
)
1025 f
= focus_does_accept_input_focus(fw
);
1026 if (fw
&& !FP_DO_FOCUS_BY_FUNCTION(FW_FOCUS_POLICY(fw
)))
1030 else if (fw
&& FP_IS_LENIENT(FW_FOCUS_POLICY(fw
)))
1034 if (!f
!= !mask
->my_flags
.do_accept_focus
)
1039 if (mask
->my_flags
.needs_focus
!= NEEDS_ANY
)
1043 is_focused
= (fw
== get_focus_window());
1044 if (!is_focused
&& mask
->my_flags
.needs_focus
== NEEDS_TRUE
)
1048 else if (is_focused
&&
1049 mask
->my_flags
.needs_focus
== NEEDS_FALSE
)
1054 if (mask
->my_flags
.needs_pointer
!= NEEDS_ANY
)
1059 t
= get_pointer_fvwm_window();
1060 if (t
!= NULL
&& t
== fw
)
1068 if (!has_pointer
&& mask
->my_flags
.needs_pointer
== NEEDS_TRUE
)
1072 else if (has_pointer
&&
1073 mask
->my_flags
.needs_pointer
== NEEDS_FALSE
)
1078 if (mask
->my_flags
.do_check_overlapped
)
1082 is_o
= (is_on_top_of_layer(fw
) == False
);
1083 if (is_o
!= mask
->my_flags
.needs_overlapped
)
1092 static void direction_cmd(F_CMD_ARGS
, Bool is_scan
)
1104 int forward
= False
;
1108 int worst_score
= -1;
1110 FvwmWindow
*fw_best
;
1113 Bool right_handed
=False
;
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
);
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",
1139 if (cond_rc
!= NULL
)
1141 cond_rc
->rc
= COND_RC_ERROR
;
1148 tmp
= PeekToken(action
, &action
);
1152 ERR
, "Direction", "Missing minor direction %s",
1154 if (cond_rc
!= NULL
)
1156 cond_rc
->rc
= COND_RC_ERROR
;
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)
1166 ERR
, "Direction", "Invalid minor direction %s",
1168 if (cond_rc
!= NULL
)
1170 cond_rc
->rc
= COND_RC_ERROR
;
1174 else if (dir2
- dir
== 1 || dir2
- dir
== -3)
1180 /* Create the mask for flags */
1181 flags
= CreateFlagString(action
, &restofline
);
1188 if (cond_rc
!= NULL
)
1190 cond_rc
->rc
= COND_RC_NO_MATCH
;
1194 DefaultConditionMask(&mask
);
1195 CreateConditionMask(flags
, &mask
);
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;
1212 dpy
, Scr
.Root
, &JunkRoot
, &JunkChild
, &my_g
.x
,
1213 &my_g
.y
, &JunkX
, &JunkY
, &JunkMask
) == False
)
1215 /* pointer is on a different screen */
1225 /* Next we iterate through all windows and choose the closest one in
1226 * the wanted direction. */
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
1234 if (tfw
== fw
|| !MatchesConditionMask(tfw
, &mask
))
1239 /* Calculate relative location of the window. */
1240 get_visible_window_or_icon_geometry(tfw
, &his_g
);
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
)
1249 /* Rotate the diagonals 45 degrees counterclockwise. To
1250 * do this, multiply the matrix /+h +h\ with the vector
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
;
1258 /* Arrange so that distance and offset are positive in desired
1268 offset
= (his_cx
< 0) ? -his_cx
: his_cx
;
1269 distance
= (dir
== DIR_N
|| dir
== DIR_NE
) ?
1273 case DIR_SE
: /* SE */
1276 case DIR_NW
: /* NW */
1278 offset
= (his_cy
< 0) ? -his_cy
: his_cy
;
1279 distance
= (dir
== DIR_W
|| dir
== DIR_NW
) ?
1286 distance
= (int)sqrt(tx
* tx
+ ty
* ty
);
1294 else if (distance
< 0)
1296 /* Target must be in given direction. */
1299 else if (distance
== 0 && dir
!= DIR_C
)
1304 /* Calculate score for this window. The smaller the better. */
1305 score
= distance
+ offset
;
1312 int ordered
= (forward
== (cross
< best_cross
));
1314 if (distance
< 0 && best_score
== -1 &&
1315 (score
< worst_score
||
1316 (score
== worst_score
&& ordered
)))
1319 worst_score
= score
;
1322 if (score
== 0 && forward
== (cross
< 0) &&
1327 if (distance
>= 0 &&
1328 (best_score
== -1 || score
< best_score
||
1329 (score
== best_score
&& ordered
)))
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
)
1345 if (best_score
== -1 || score
< best_score
||
1346 (score
== best_score
&& dir
== DIR_C
))
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
);
1372 static int __rc_matches_rcstring_consume(
1373 char **ret_rest
, cond_rc_t
*cond_rc
, char *action
)
1375 cond_rc_enum match_rc
;
1378 int is_not_reversed
= 1;
1381 /* Create window mask */
1382 orig_flags
= CreateFlagString(action
, ret_rest
);
1386 match_rc
= COND_RC_NO_MATCH
;
1392 is_not_reversed
= 0;
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
;
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. */
1417 stderr
, "Unrecognised condition \"%s\" in"
1418 " TestRc command.\n", flags
);
1421 if (orig_flags
!= NULL
)
1425 ret
= ((cond_rc
->rc
== match_rc
) == is_not_reversed
);
1430 /* ---------------------------- builtin commands --------------------------- */
1432 void CMD_Prev(F_CMD_ARGS
)
1434 circulate_cmd(F_PASS_ARGS
, C_WINDOW
, -1, True
, True
);
1439 void CMD_Next(F_CMD_ARGS
)
1441 circulate_cmd(F_PASS_ARGS
, C_WINDOW
, 1, True
, True
);
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
)
1453 cond_rc
->rc
= COND_RC_NO_MATCH
;
1455 case COND_RC_NO_MATCH
:
1456 cond_rc
->rc
= COND_RC_OK
;
1465 void CMD_Any(F_CMD_ARGS
)
1467 circulate_cmd(F_PASS_ARGS
, exc
->w
.wcontext
, 1, False
, True
);
1472 void CMD_Current(F_CMD_ARGS
)
1474 circulate_cmd(F_PASS_ARGS
, C_WINDOW
, 0, True
, True
);
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
);
1491 void CMD_ThisWindow(F_CMD_ARGS
)
1493 select_cmd(F_PASS_ARGS
);
1498 void CMD_Pick(F_CMD_ARGS
)
1500 select_cmd(F_PASS_ARGS
);
1505 void CMD_All(F_CMD_ARGS
)
1509 WindowConditionMask mask
;
1512 Bool does_any_window_match
= False
;
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"))
1524 /* if not any more actions, then Reverse
1525 * probably is some user function, so ignore
1526 * it and do the old behaviour */
1532 action
= restofline
;
1535 else if (StrEquals(token
, "UseStack"))
1539 /* if not any more actions, then UseStack
1540 * probably is some user function, so ignore
1541 * it and do the old behaviour */
1547 action
= restofline
;
1552 /* No more options -- continue with flags and
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
);
1570 for (t
= Scr
.FvwmRoot
.next
; t
; t
= t
->next
)
1574 g
= (FvwmWindow
**)safemalloc(num
* sizeof(FvwmWindow
*));
1578 for (t
= Scr
.FvwmRoot
.next
; t
; t
= t
->next
)
1580 if (MatchesConditionMask(t
, &mask
))
1583 does_any_window_match
= True
;
1589 for (t
= Scr
.FvwmRoot
.stack_next
; t
&& t
!= &Scr
.FvwmRoot
;
1592 if (MatchesConditionMask(t
, &mask
))
1595 does_any_window_match
= True
;
1601 for (i
= num
-1; i
>= 0; i
--)
1603 execute_function_override_window(
1604 cond_rc
, exc
, restofline
, 0, g
[i
]);
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
;
1621 FreeConditionMask(&mask
);
1627 * Execute a function to the closest window in the given
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
)
1646 Bool use_condition
= False
;
1647 Bool use_screenroot
= False
;
1648 WindowConditionMask mask
;
1649 char *flags
, *restofline
;
1652 action
= GetNextToken(action
, &token
);
1654 if (token
&& StrEquals(token
, "root"))
1656 int screen
= Scr
.screen
;
1659 token
= PeekToken(action
, &naction
);
1660 if (!token
|| GetIntegerArguments(token
, NULL
, &screen
, 1) != 1)
1662 screen
= Scr
.screen
;
1668 use_screenroot
= True
;
1669 if (screen
< 0 || screen
>= Scr
.NumberOfScreens
)
1673 win
= XRootWindow(dpy
, screen
);
1676 if (cond_rc
!= NULL
)
1678 cond_rc
->rc
= COND_RC_ERROR
;
1685 /* SunOS doesn't have strtoul */
1686 win
= (unsigned long)strtol(token
, NULL
, 0);
1694 /* Look for condition - CreateFlagString returns NULL if no '(' or '['
1696 if (!use_screenroot
)
1698 flags
= CreateFlagString(action
, &restofline
);
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
);
1713 /* Relocate action */
1714 action
= restofline
;
1718 /* Search windows */
1719 for (t
= Scr
.FvwmRoot
.next
; t
; t
= t
->next
)
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
;
1743 /* The window is not managed by fvwm. Still some functions may
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
;
1765 const exec_context_t
*exc2
;
1766 exec_context_changes_t ecc
;
1770 ecc
.w
.wcontext
= C_UNMANAGED
;
1771 exc2
= exc_clone_context(
1773 ECC_FW
| ECC_W
| ECC_WCONTEXT
);
1775 cond_rc
, exc2
, action
,
1777 exc_destroy_context(exc2
);
1782 /* window id does not exist */
1783 if (cond_rc
!= NULL
)
1785 cond_rc
->rc
= COND_RC_ERROR
;
1793 FreeConditionMask(&mask
);
1799 void CMD_TestRc(F_CMD_ARGS
)
1803 if (cond_rc
== NULL
)
1805 /* useless if no return code to compare to is given */
1808 if (__rc_matches_rcstring_consume(&rest
, cond_rc
, action
) &&
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);
1819 void CMD_Break(F_CMD_ARGS
)
1823 if (cond_rc
== NULL
)
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
;
1837 void CMD_NoWindow(F_CMD_ARGS
)
1839 execute_function_override_window(cond_rc
, exc
, action
, 0, NULL
);
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
)
1852 str
= DoPeekToken(str
, &n
, NULL
, ".", NULL
);
1857 v
= atoi(n
) * 1000000;
1858 str
= DoPeekToken(str
, &n
, NULL
, ".", NULL
);
1863 v
+= atoi(n
) * 1000;
1864 str
= DoPeekToken(str
, &n
, NULL
, ".", NULL
);
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
);
1890 stderr
, "match_version: Invalid version: %s\n",
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
);
1921 stderr
, "match_version: Invalid operator: %s\n",
1928 void CMD_Test(F_CMD_ARGS
)
1937 flags
= CreateFlagString(action
, &restofline
);
1939 /* Next parse the flags in the string. */
1941 flags_ptr
= GetNextSimpleOption(flags_ptr
, &condition
);
1957 if (StrEquals(cond
, "True"))
1961 else if (StrEquals(cond
, "False"))
1965 else if (StrEquals(cond
, "Version"))
1968 flags_ptr
= GetNextSimpleOption(flags_ptr
, &pattern
);
1972 flags_ptr
= GetNextSimpleOption(
1976 match
= matchWildcards(
1981 match
= match_version(ver
, pattern
);
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"))
2052 flags_ptr
= GetNextSimpleOption(flags_ptr
, &pattern
);
2055 match
= cond_check_access(pattern
, type
, im
);
2063 else if (StrEquals(cond
, "EnvIsSet"))
2067 flags_ptr
= GetNextSimpleOption(flags_ptr
, &var_name
);
2070 const char *value
= getenv(var_name
);
2072 match
= (value
!= NULL
) ? 1 : 0;
2079 else if (StrEquals(cond
, "EnvMatch"))
2083 flags_ptr
= GetNextSimpleOption(flags_ptr
, &var_name
);
2086 const char *value
= getenv(var_name
);
2088 /* unfortunately, GetNextSimpleOption is
2089 * broken, does not accept quoted empty "" */
2090 flags_ptr
= GetNextSimpleOption(
2091 flags_ptr
, &pattern
);
2099 /* include empty string case */
2100 (!pattern
[0] && !value
[0]) ||
2101 matchWildcards(pattern
, value
);
2113 else if (StrEquals(cond
, "EdgeIsActive"))
2115 direction_t dir
= DIR_NONE
;
2118 next
= GetNextSimpleOption(flags_ptr
, &dirname
);
2121 dir
= gravity_parse_dir_argument(
2122 dirname
, NULL
, DIR_NONE
);
2123 if (dir
== DIR_NONE
)
2125 if (!StrEquals(dirname
, "Any"))
2130 else if (dir
> DIR_MAJOR_MASK
)
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
))
2157 else if (StrEquals(cond
, "EdgeHasPointer"))
2161 direction_t dir
= DIR_NONE
;
2164 next
= GetNextSimpleOption(flags_ptr
, &dirname
);
2167 dir
= gravity_parse_dir_argument(
2168 dirname
, NULL
, DIR_NONE
);
2169 if (dir
== DIR_NONE
)
2171 if (!StrEquals(dirname
, "Any"))
2176 else if (dir
> DIR_MAJOR_MASK
)
2186 dpy
, Scr
.Root
, &JunkRoot
, &win
,
2187 &JunkX
, &JunkY
, &x
, &y
, &JunkMask
)
2190 /* pointer is on a different screen */
2193 else if (is_pan_frame(win
))
2195 if (dir
== DIR_NONE
||
2197 win
== Scr
.PanFrameTop
.win
) ||
2199 win
== Scr
.PanFrameBottom
.win
) ||
2201 win
== Scr
.PanFrameRight
.win
) ||
2203 win
== Scr
.PanFrameLeft
.win
))
2221 /* unrecognized condition */
2224 stderr
, "Unrecognised condition \"%s\" in"
2225 " Test command.\n", cond
);
2233 if (error
|| !match
)
2237 flags_ptr
= GetNextSimpleOption(flags_ptr
, &condition
);
2244 if (!error
&& match
)
2246 execute_function(cond_rc
, exc
, restofline
, 0);
2248 if (cond_rc
!= NULL
)
2252 cond_rc
->rc
= COND_RC_ERROR
;
2256 cond_rc
->rc
= COND_RC_OK
;
2260 cond_rc
->rc
= COND_RC_NO_MATCH
;