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.
6 *
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.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, see: <http://www.gnu.org/licenses/>
14 */
15
16 /* ---------------------------- included header files ---------------------- */
17
18 #include "config.h"
19
20 #include <stdio.h>
21 #include <math.h>
22
23 #include "libs/fvwmlib.h"
24 #include "libs/Parse.h"
25 #include "libs/wild.h"
26 #include "libs/FScreen.h"
27 #include "libs/charmap.h"
28 #include "libs/wcontext.h"
29 #include "fvwm.h"
30 #include "externs.h"
31 #include "execcontext.h"
32 #include "functions.h"
33 #include "conditional.h"
34 #include "misc.h"
35 #include "screen.h"
36 #include "update.h"
37 #include "style.h"
38 #include "focus.h"
39 #include "geometry.h"
40 #include "stack.h"
41 #include "commands.h"
42 #include "decorations.h"
43 #include "virtual.h"
44 #include "infostore.h"
45
46 /* ---------------------------- local definitions -------------------------- */
47
48 /* ---------------------------- local macros ------------------------------- */
49
50 /* ---------------------------- imports ------------------------------------ */
51
52 /* ---------------------------- included code files ------------------------ */
53
54 /* ---------------------------- local types -------------------------------- */
55
56 /* ---------------------------- forward declarations ----------------------- */
57
58 /* ---------------------------- local variables ---------------------------- */
59
60 /* ---------------------------- exported variables (globals) --------------- */
61
62 /* ---------------------------- local functions ---------------------------- */
63
64 /*
65 *
66 * Direction = 1 ==> "Next" operation
67 * Direction = -1 ==> "Previous" operation
68 * Direction = 0 ==> operation on current window (returns pass or fail)
69 *
70 */
Circulate(FvwmWindow * sf,char * action,int Direction,char ** restofline)71 static FvwmWindow *Circulate(
72 FvwmWindow *sf, char *action, int Direction, char **restofline)
73 {
74 int pass = 0;
75 FvwmWindow *fw, *found = NULL;
76 WindowConditionMask mask;
77 char *flags;
78
79 /* Create window mask */
80 flags = CreateFlagString(action, restofline);
81 DefaultConditionMask(&mask);
82 if (Direction == 0)
83 { /* override for Current [] */
84 mask.my_flags.use_circulate_hit = 1;
85 mask.my_flags.use_circulate_hit_icon = 1;
86 mask.my_flags.use_circulate_hit_shaded = 1;
87 }
88 CreateConditionMask(flags, &mask);
89 if (flags)
90 {
91 free(flags);
92 }
93 if (sf == NULL || Direction == 0)
94 {
95 sf = get_focus_window();
96 }
97 if (sf != NULL)
98 {
99 if (Direction > 0)
100 {
101 fw = sf->prev;
102 }
103 else if (Direction < 0)
104 {
105 fw = sf->next;
106 }
107 else
108 {
109 fw = sf;
110 }
111 }
112 else
113 {
114 fw = NULL;
115 if (Direction == 0)
116 {
117 FreeConditionMask(&mask);
118 return NULL;
119 }
120 }
121
122 for (pass = 0; pass < 3 && !found; pass++)
123 {
124 while (fw && !found && fw != &Scr.FvwmRoot)
125 {
126 /* Make CirculateUp and CirculateDown take args. by
127 * Y.NOMURA */
128 if (MatchesConditionMask(fw, &mask))
129 {
130 found = fw;
131 }
132 else
133 {
134 if (Direction > 0)
135 {
136 fw = fw->prev;
137 }
138 else
139 {
140 fw = fw->next;
141 }
142 }
143 if (Direction == 0)
144 {
145 FreeConditionMask(&mask);
146 return found;
147 }
148 }
149 if (fw == NULL || fw == &Scr.FvwmRoot)
150 {
151 if (Direction > 0)
152 {
153 /* Go to end of list */
154 for (fw = Scr.FvwmRoot.next; fw && fw->next;
155 fw = fw->next)
156 {
157 /* nop */
158 }
159 }
160 else
161 {
162 /* Go to top of list */
163 fw = Scr.FvwmRoot.next;
164 }
165 }
166 }
167 FreeConditionMask(&mask);
168
169 return found;
170 }
171
circulate_cmd(F_CMD_ARGS,int new_context,int circ_dir,Bool do_use_found,Bool do_exec_on_match)172 static void circulate_cmd(
173 F_CMD_ARGS, int new_context, int circ_dir, Bool do_use_found,
174 Bool do_exec_on_match)
175 {
176 FvwmWindow *found;
177 char *restofline;
178
179 found = Circulate(exc->w.fw, action, circ_dir, &restofline);
180 if (cond_rc != NULL)
181 {
182 cond_rc->rc = (found == NULL) ? COND_RC_NO_MATCH : COND_RC_OK;
183 }
184 if ((!found == !do_exec_on_match) && restofline)
185 {
186 const exec_context_t *exc2;
187 exec_context_changes_t ecc;
188 int flags;
189
190 ecc.w.fw = (do_use_found == True) ? found : NULL;
191 if (found != NULL)
192 {
193 ecc.w.w = FW_W(found);
194 flags = FUNC_DONT_DEFER;
195 }
196 else
197 {
198 ecc.w.w = None;
199 flags = 0;
200 }
201 ecc.w.wcontext = new_context;
202 exc2 = exc_clone_context(
203 exc, &ecc, ECC_FW | ECC_W | ECC_WCONTEXT);
204 execute_function(cond_rc, exc2, restofline, flags);
205 exc_destroy_context(exc2);
206 }
207
208 return;
209 }
210
select_cmd(F_CMD_ARGS)211 static void select_cmd(F_CMD_ARGS)
212 {
213 char *restofline;
214 char *flags;
215 WindowConditionMask mask;
216 FvwmWindow * const fw = exc->w.fw;
217
218 if (!fw || IS_EWMH_DESKTOP(FW_W(fw)))
219 {
220 if (cond_rc != NULL)
221 {
222 cond_rc->rc = COND_RC_ERROR;
223 }
224 return;
225 }
226 flags = CreateFlagString(action, &restofline);
227 DefaultConditionMask(&mask);
228 mask.my_flags.use_circulate_hit = 1;
229 mask.my_flags.use_circulate_hit_icon = 1;
230 mask.my_flags.use_circulate_hit_shaded = 1;
231 CreateConditionMask(flags, &mask);
232 if (flags)
233 {
234 free(flags);
235 }
236 if (MatchesConditionMask(fw, &mask) && restofline)
237 {
238 if (cond_rc != NULL)
239 {
240 cond_rc->rc = COND_RC_OK;
241 }
242 execute_function_override_wcontext(
243 cond_rc, exc, restofline, 0, C_WINDOW);
244 }
245 else if (cond_rc != NULL)
246 {
247 cond_rc->rc = COND_RC_NO_MATCH;
248 }
249 FreeConditionMask(&mask);
250
251 return;
252 }
253
cond_check_access(char * file,int type,Bool im)254 static Bool cond_check_access(char *file, int type, Bool im)
255 {
256 char *full_file;
257 char *path = NULL;
258
259 if (!file || *file == 0)
260 {
261 return False;
262 }
263 if (file[0] == '/')
264 {
265 if (access(file, type) == 0)
266 {
267 return True;
268 }
269 else
270 {
271 return False;
272 }
273 }
274 if (type != X_OK && im == False)
275 {
276 return False;
277 }
278 if (im == False)
279 {
280 path = getenv("PATH");
281 }
282 else
283 {
284 path = PictureGetImagePath();
285 }
286 if (path == NULL || *path == 0)
287 {
288 return False;
289 }
290 full_file = searchPath(path, file, NULL, type);
291 if (full_file)
292 {
293 free(full_file);
294 return True;
295 }
296 return False;
297 }
298
299 /* ---------------------------- interface functions ------------------------ */
300
301 /*
302 * Parses the flag string and returns the text between [ ] or ( )
303 * characters. The start of the rest of the line is put in restptr.
304 * Note that the returned string is allocated here and it must be
305 * freed when it is not needed anymore.
306 * NOTE - exported via .h
307 */
CreateFlagString(char * string,char ** restptr)308 char *CreateFlagString(char *string, char **restptr)
309 {
310 char *retval;
311 char *c;
312 char *start;
313 char closeopt;
314 int length;
315
316 c = string;
317 while (isspace((unsigned char)*c) && (*c != 0))
318 {
319 c++;
320 }
321
322
323 if (*c == '[' || *c == '(')
324 {
325 char *d;
326
327 /* Get the text between [ ] or ( ) */
328 if (*c == '[')
329 {
330 closeopt = ']';
331 }
332 else
333 {
334 closeopt = ')';
335 }
336 c++;
337 start = c;
338 length = 0;
339 while (*c != closeopt)
340 {
341 if (*c == 0)
342 {
343 fvwm_msg(ERR, "CreateFlagString",
344 "Conditionals require closing "
345 "parenthesis");
346 *restptr = NULL;
347 return NULL;
348 }
349
350 /* skip quoted string */
351 d = SkipQuote(c, NULL, NULL, NULL);
352 length += d - c;
353 c = d;
354 }
355
356 /* We must allocate a new string because we null terminate the
357 * string between the [ ] or ( ) characters. */
358 retval = safemalloc(length + 1);
359 strncpy(retval, start, length);
360 retval[length] = 0;
361
362 *restptr = c + 1;
363 }
364 else
365 {
366 retval = NULL;
367 *restptr = c;
368 }
369
370 return retval;
371 }
372
373 /*
374 * The name_condition field of the mask is allocated in CreateConditionMask.
375 * It must be freed.
376 * NOTE - exported via .h
377 */
FreeConditionMask(WindowConditionMask * mask)378 void FreeConditionMask(WindowConditionMask *mask)
379 {
380 struct name_condition *pp,*pp2;
381 struct namelist *p,*p2;
382
383 for (pp=mask->name_condition; pp; )
384 {
385 /* One malloc() is done for all the name strings.
386 The string is tokenised & the name fields point to
387 different parts of the one string.
388 The start of the string is the first name in the string
389 which is actually the last node in the linked list. */
390 for (p=pp->namelist; p; )
391 {
392 p2=p->next;
393 if(!p2)
394 {
395 free(p->name);
396 }
397 free(p);
398 p=p2;
399 }
400 pp2=pp->next;
401 free(pp);
402 pp=pp2;
403 }
404 }
405
406 /* Assign the default values for the window mask
407 * NOTE - exported via .h */
DefaultConditionMask(WindowConditionMask * mask)408 void DefaultConditionMask(WindowConditionMask *mask)
409 {
410 memset(mask, 0, sizeof(WindowConditionMask));
411 /* -2 means no layer condition, -1 means current */
412 mask->layer = -2;
413
414 return;
415 }
416
417 /*
418 * Note that this function allocates the name field of the mask struct.
419 * FreeConditionMask must be called for the mask when the mask is discarded.
420 * NOTE - exported via .h
421 */
CreateConditionMask(char * flags,WindowConditionMask * mask)422 void CreateConditionMask(char *flags, WindowConditionMask *mask)
423 {
424 char *allocated_condition;
425 char *next_condition;
426 char *condition;
427 char *tmp;
428 unsigned int state;
429
430 if (flags == NULL)
431 {
432 return;
433 }
434
435 /* Next parse the flags in the string. */
436 next_condition = GetNextFullOption(flags, &allocated_condition);
437 condition = PeekToken(allocated_condition, &tmp);
438
439 while (condition)
440 {
441 char *cond;
442 int on;
443
444 cond = condition;
445 on = 1;
446 if (*cond == '!')
447 {
448 on = 0;
449 cond++;
450 }
451 if (StrEquals(cond,"AcceptsFocus"))
452 {
453 mask->my_flags.do_accept_focus = on;
454 mask->my_flags.use_do_accept_focus = 1;
455 }
456 else if (StrEquals(cond,"Focused"))
457 {
458 mask->my_flags.needs_focus =
459 (on) ? NEEDS_TRUE : NEEDS_FALSE;
460 }
461 else if (StrEquals(cond,"HasPointer"))
462 {
463 mask->my_flags.needs_pointer =
464 (on) ? NEEDS_TRUE : NEEDS_FALSE;
465 }
466 else if (StrEquals(cond,"Iconic"))
467 {
468 SET_ICONIFIED(mask, on);
469 SETM_ICONIFIED(mask, 1);
470 }
471 else if (StrEquals(cond,"Visible"))
472 {
473 SET_PARTIALLY_VISIBLE(mask, on);
474 SETM_PARTIALLY_VISIBLE(mask, 1);
475 }
476 else if (StrEquals(cond,"Overlapped"))
477 {
478 mask->my_flags.needs_overlapped = on;
479 mask->my_flags.do_check_overlapped = 1;
480 }
481 else if (StrEquals(cond,"PlacedByButton"))
482 {
483 int button;
484 int button_mask;
485
486 if (sscanf(tmp, "%d", &button) &&
487 (button >= 1 &&
488 button <= NUMBER_OF_EXTENDED_MOUSE_BUTTONS))
489 {
490 tmp = SkipNTokens(tmp, 1);
491 button_mask = (1<<(button-1));
492 }
493 else
494 {
495 button_mask =
496 (1<<NUMBER_OF_EXTENDED_MOUSE_BUTTONS) - 1;
497 }
498 if (on)
499 {
500 if (mask->placed_by_button_mask &
501 mask->placed_by_button_set_mask &
502 ~button_mask)
503 {
504 fvwm_msg(WARN, "PlacedByButton",
505 "Condition always False.");
506 }
507 mask->placed_by_button_mask |= button_mask;
508 }
509 else
510 {
511 mask->placed_by_button_mask &= ~button_mask;
512 }
513 mask->placed_by_button_set_mask |= button_mask;
514 }
515 else if (StrEquals(cond,"PlacedByButton3"))
516 {
517 if (on)
518 {
519 if (mask->placed_by_button_mask &
520 mask->placed_by_button_set_mask & ~(1<<2))
521 {
522 fvwm_msg(WARN, "PlacedByButton3",
523 "Condition always False.");
524 }
525 mask->placed_by_button_mask |= (1<<2);
526 }
527 else
528 {
529 mask->placed_by_button_mask &= ~(1<<2);
530 }
531 mask->placed_by_button_set_mask |= (1<<2);
532 }
533 else if (StrEquals(cond,"Raised"))
534 {
535 SET_FULLY_VISIBLE(mask, on);
536 SETM_FULLY_VISIBLE(mask, 1);
537 }
538 else if (StrEquals(cond,"Sticky"))
539 {
540 SET_STICKY_ACROSS_PAGES(mask, on);
541 SET_STICKY_ACROSS_DESKS(mask, on);
542 SETM_STICKY_ACROSS_PAGES(mask, 1);
543 SETM_STICKY_ACROSS_DESKS(mask, 1);
544 }
545 else if (StrEquals(cond,"StickyAcrossPages"))
546 {
547 SET_STICKY_ACROSS_PAGES(mask, on);
548 SETM_STICKY_ACROSS_PAGES(mask, 1);
549 }
550 else if (StrEquals(cond,"StickyAcrossDesks"))
551 {
552 SET_STICKY_ACROSS_DESKS(mask, on);
553 SETM_STICKY_ACROSS_DESKS(mask, 1);
554 }
555 else if (StrEquals(cond,"StickyIcon"))
556 {
557 SET_ICON_STICKY_ACROSS_PAGES(mask, on);
558 SET_ICON_STICKY_ACROSS_DESKS(mask, on);
559 SETM_ICON_STICKY_ACROSS_PAGES(mask, 1);
560 SETM_ICON_STICKY_ACROSS_DESKS(mask, 1);
561 }
562 else if (StrEquals(cond,"StickyAcrossPagesIcon"))
563 {
564 SET_ICON_STICKY_ACROSS_PAGES(mask, on);
565 SETM_ICON_STICKY_ACROSS_PAGES(mask, 1);
566 }
567 else if (StrEquals(cond,"StickyAcrossDesksIcon"))
568 {
569 SET_ICON_STICKY_ACROSS_DESKS(mask, on);
570 SETM_ICON_STICKY_ACROSS_DESKS(mask, 1);
571 }
572 else if (StrEquals(cond,"Maximized"))
573 {
574 SET_MAXIMIZED(mask, on);
575 SETM_MAXIMIZED(mask, 1);
576 }
577 else if (StrEquals(cond, "Fullscreen"))
578 {
579 mask->my_flags.do_check_fullscreen = on;
580 }
581 else if (StrEquals(cond,"FixedSize"))
582 {
583 /* don't set mask here, because we make the test here
584 (and don't compare against window's mask)
585 by checking allowed function */
586 SET_SIZE_FIXED(mask, on);
587 SETM_SIZE_FIXED(mask, 1);
588 }
589 else if (StrEquals(cond, "FixedPosition"))
590 {
591 SET_FIXED(mask, on);
592 SETM_FIXED(mask, 1);
593 }
594 else if (StrEquals(cond,"HasHandles"))
595 {
596 SET_HAS_HANDLES(mask, on);
597 SETM_HAS_HANDLES(mask, 1);
598 }
599 else if (StrEquals(cond,"Iconifiable"))
600 {
601 SET_IS_UNICONIFIABLE(mask, !on);
602 SETM_IS_UNICONIFIABLE(mask, 1);
603 }
604 else if (StrEquals(cond,"Maximizable"))
605 {
606 SET_IS_UNMAXIMIZABLE(mask, !on);
607 SETM_IS_UNMAXIMIZABLE(mask, 1);
608 }
609 else if (StrEquals(cond,"Closable"))
610 {
611 SET_IS_UNCLOSABLE(mask, !on);
612 SETM_IS_UNCLOSABLE(mask, 1);
613 }
614 else if (StrEquals(cond,"Shaded"))
615 {
616 SET_SHADED(mask, on);
617 SETM_SHADED(mask, 1);
618 }
619 else if (StrEquals(cond,"Transient"))
620 {
621 SET_TRANSIENT(mask, on);
622 SETM_TRANSIENT(mask, 1);
623 }
624 else if (StrEquals(cond,"PlacedByFvwm"))
625 {
626 SET_PLACED_BY_FVWM(mask, on);
627 SETM_PLACED_BY_FVWM(mask, 1);
628 }
629 else if (StrEquals(cond,"CurrentDesk"))
630 {
631 mask->my_flags.needs_current_desk = on;
632 mask->my_flags.do_check_desk = 1;
633 }
634 else if (StrEquals(cond,"CurrentPage"))
635 {
636 mask->my_flags.needs_current_desk_and_page = on;
637 mask->my_flags.do_check_desk_and_page = 1;
638 }
639 else if (StrEquals(cond,"CurrentGlobalPage"))
640 {
641 mask->my_flags.needs_current_desk_and_global_page = on;
642 mask->my_flags.do_check_desk_and_global_page = 1;
643 }
644 else if (StrEquals(cond,"CurrentPageAnyDesk") ||
645 StrEquals(cond,"CurrentScreen"))
646 {
647 mask->my_flags.needs_current_page = on;
648 mask->my_flags.do_check_page = 1;
649 }
650 else if (StrEquals(cond,"AnyScreen"))
651 {
652 mask->my_flags.do_not_check_screen = on;
653 }
654 else if (StrEquals(cond,"CurrentGlobalPageAnyDesk"))
655 {
656 mask->my_flags.needs_current_global_page = on;
657 mask->my_flags.do_check_global_page = 1;
658 }
659 else if (StrEquals(cond,"CirculateHit"))
660 {
661 mask->my_flags.use_circulate_hit = on;
662 }
663 else if (StrEquals(cond,"CirculateHitIcon"))
664 {
665 mask->my_flags.use_circulate_hit_icon = on;
666 }
667 else if (StrEquals(cond,"CirculateHitShaded"))
668 {
669 mask->my_flags.use_circulate_hit_shaded = on;
670 }
671 else if (StrEquals(cond,"State"))
672 {
673 if (sscanf(tmp, "%u", &state) && state <= 31)
674 {
675 state = (1 << state);
676 if (on)
677 {
678 SET_USER_STATES(mask, state);
679 }
680 else
681 {
682 CLEAR_USER_STATES(mask, state);
683 }
684 SETM_USER_STATES(mask, state);
685 tmp = SkipNTokens(tmp, 1);
686 }
687 }
688 else if (StrEquals(cond, "Layer"))
689 {
690 if (sscanf(tmp, "%d", &mask->layer))
691 {
692 tmp = SkipNTokens(tmp, 1);
693 if (mask->layer < 0)
694 {
695 /* silently ignore invalid layers */
696 mask->layer = -2;
697 }
698 }
699 else
700 {
701 /* needs current layer */
702 mask->layer = -1;
703 }
704 mask->my_flags.needs_same_layer = on;
705 }
706 else if (StrEquals(cond, "Desk"))
707 {
708 if (sscanf(tmp, "%d", &mask->desk)) {
709 tmp = SkipNTokens(tmp, 1);
710 }
711 mask->my_flags.do_check_cond_desk = on;
712 }
713 else if (StrEquals(cond, "Screen"))
714 {
715 if (sscanf(tmp, "%d", &mask->screen)) {
716 tmp = SkipNTokens(tmp, 1);
717 }
718 mask->my_flags.do_check_screen = 1;
719
720 if (!on)
721 mask->my_flags.do_not_check_screen = 1;
722 }
723 else
724 {
725 struct name_condition *pp;
726 struct namelist *p;
727 char *condp = safestrdup(cond);
728
729 pp = (struct name_condition *)
730 safemalloc(sizeof(struct name_condition));
731 pp->invert = (!on ? True : False);
732 pp->namelist = NULL;
733 pp->next = mask->name_condition;
734 mask->name_condition = pp;
735 for (;;)
736 {
737 p = (struct namelist *)
738 safemalloc(sizeof(struct namelist));
739 p->name=condp;
740 p->next=pp->namelist;
741 pp->namelist=p;
742 while(*condp && *condp != '|')
743 {
744 condp++;
745 }
746 if(!*condp)
747 {
748 break;
749 }
750 *condp++='\0';
751 }
752 }
753
754 if (tmp && *tmp)
755 {
756 fvwm_msg(OLD, "CreateConditionMask",
757 "Use comma instead of whitespace to "
758 "separate conditions");
759 }
760 else
761 {
762 if (allocated_condition != NULL)
763 {
764 free(allocated_condition);
765 allocated_condition = NULL;
766 }
767 if (next_condition && *next_condition)
768 {
769 next_condition = GetNextFullOption(
770 next_condition, &allocated_condition);
771 }
772 tmp = allocated_condition;
773 }
774 condition = PeekToken(tmp, &tmp);
775 }
776
777 return;
778 }
779
780 /*
781 * Checks whether the given window matches the mask created with
782 * CreateConditionMask.
783 * NOTE - exported via .h
784 */
MatchesConditionMask(FvwmWindow * fw,WindowConditionMask * mask)785 Bool MatchesConditionMask(FvwmWindow *fw, WindowConditionMask *mask)
786 {
787 int does_match;
788 int is_on_desk;
789 int is_on_page;
790 int is_on_global_page;
791 FvwmWindow *sf = get_focus_window();
792 struct name_condition *pp;
793 struct namelist *p;
794 char *name;
795
796 /* match FixedSize conditional */
797 /* special treatment for FixedSize, because more than just
798 the is_size_fixed flag makes a window unresizable (width and height
799 hints etc.) */
800 if (IS_SIZE_FIXED(mask) &&
801 mask->flag_mask.common.s.is_size_fixed &&
802 is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, False))
803 {
804 return False;
805 }
806 if (!IS_SIZE_FIXED(mask) &&
807 mask->flag_mask.common.s.is_size_fixed &&
808 !is_function_allowed(F_RESIZE, NULL, fw, RQORIG_PROGRAM_US, False))
809 {
810 return False;
811 }
812 if (IS_FIXED(mask) &&
813 mask->flag_mask.common.s.is_fixed &&
814 is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
815 {
816 return False;
817 }
818 if (!IS_FIXED(mask) &&
819 mask->flag_mask.common.s.is_fixed &&
820 !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM_US, False))
821 {
822 return False;
823 }
824 if (IS_UNICONIFIABLE(mask) &&
825 mask->flag_mask.common.s.is_uniconifiable &&
826 is_function_allowed(F_ICONIFY, NULL, fw, RQORIG_PROGRAM_US, False))
827 {
828 return False;
829 }
830 if (
831 !IS_UNICONIFIABLE(mask) &&
832 mask->flag_mask.common.s.is_uniconifiable &&
833 !is_function_allowed(
834 F_ICONIFY, NULL, fw, RQORIG_PROGRAM_US, False))
835 {
836 return False;
837 }
838 if (
839 IS_UNMAXIMIZABLE(mask) &&
840 mask->flag_mask.common.s.is_unmaximizable &&
841 is_function_allowed(
842 F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
843 {
844 return False;
845 }
846 if (
847 !IS_UNMAXIMIZABLE(mask) &&
848 mask->flag_mask.common.s.is_unmaximizable &&
849 !is_function_allowed(
850 F_MAXIMIZE, NULL, fw, RQORIG_PROGRAM_US, False))
851 {
852 return False;
853 }
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)))
863 {
864 return False;
865 }
866 if (
867 !IS_UNCLOSABLE(mask) &&
868 mask->flag_mask.common.s.is_unclosable &&
869 (!is_function_allowed(
870 F_CLOSE, NULL, fw, RQORIG_PROGRAM_US,False) &&
871 !is_function_allowed(
872 F_DELETE, NULL, fw, RQORIG_PROGRAM_US,False) &&
873 !is_function_allowed(
874 F_DESTROY, NULL, fw, RQORIG_PROGRAM_US,False)))
875 {
876 return False;
877 }
878 if (!blockcmpmask((char *)&(fw->flags), (char *)&(mask->flags),
879 (char *)&(mask->flag_mask), sizeof(fw->flags)))
880 {
881 return False;
882 }
883 if (!mask->my_flags.use_circulate_hit && DO_SKIP_CIRCULATE(fw))
884 {
885 return False;
886 }
887 if (!mask->my_flags.use_circulate_hit_icon && IS_ICONIFIED(fw) &&
888 DO_SKIP_ICON_CIRCULATE(fw))
889 {
890 return False;
891 }
892 if (!mask->my_flags.use_circulate_hit_shaded && IS_SHADED(fw) &&
893 DO_SKIP_SHADED_CIRCULATE(fw))
894 {
895 return False;
896 }
897 if (IS_ICONIFIED(fw) && IS_TRANSIENT(fw) && IS_ICONIFIED_BY_PARENT(fw))
898 {
899 return False;
900 }
901
902 /* desk and page matching */
903 is_on_desk = 1;
904 if (mask->my_flags.do_check_desk ||
905 mask->my_flags.do_check_desk_and_page ||
906 mask->my_flags.do_check_desk_and_global_page)
907 {
908 is_on_desk = (fw->Desk == Scr.CurrentDesk);
909 }
910 is_on_page = 1;
911 if (mask->my_flags.do_check_page ||
912 mask->my_flags.do_check_desk_and_page)
913 {
914 if (FScreenIsEnabled() && !mask->my_flags.do_not_check_screen)
915 {
916 is_on_page = !!FScreenIsRectangleOnScreen(
917 NULL, FSCREEN_CURRENT, &(fw->g.frame));
918 }
919 else
920 {
921 is_on_page = !!IsRectangleOnThisPage(
922 &(fw->g.frame), Scr.CurrentDesk);
923 }
924 }
925 is_on_global_page = 1;
926 if (mask->my_flags.do_check_global_page ||
927 mask->my_flags.do_check_desk_and_global_page)
928 {
929 is_on_global_page = !!IsRectangleOnThisPage(
930 &(fw->g.frame), Scr.CurrentDesk);
931 }
932
933 if (mask->my_flags.do_check_desk_and_page)
934 {
935 int is_on_desk_and_page;
936
937 is_on_desk_and_page = (is_on_desk && is_on_page);
938 if (mask->my_flags.needs_current_desk_and_page !=
939 is_on_desk_and_page)
940 {
941 return False;
942 }
943 }
944 else if (mask->my_flags.do_check_desk_and_global_page)
945 {
946 int is_on_desk_and_global_page;
947
948 is_on_desk_and_global_page = (is_on_desk && is_on_global_page);
949 if (mask->my_flags.needs_current_desk_and_global_page !=
950 is_on_desk_and_global_page)
951 {
952 return False;
953 }
954 }
955 if (mask->my_flags.do_check_desk &&
956 mask->my_flags.needs_current_desk != is_on_desk)
957 {
958 return False;
959 }
960 if (mask->my_flags.do_check_page)
961 {
962 if (mask->my_flags.needs_current_page != is_on_page)
963 {
964 return False;
965 }
966 }
967 else if (mask->my_flags.do_check_global_page)
968 {
969 if (mask->my_flags.needs_current_global_page !=
970 is_on_global_page)
971 {
972 return False;
973 }
974 }
975
976 for (pp = mask->name_condition; pp; pp = pp->next)
977 {
978 does_match = 0;
979 for (p = pp->namelist; p; p = p->next)
980 {
981 name=p->name;
982 does_match |= matchWildcards(name, fw->name.name);
983 does_match |= matchWildcards(name, fw->icon_name.name);
984 if(fw->class.res_class)
985 does_match |= matchWildcards(name,
986 fw->class.res_class);
987 if(fw->class.res_name)
988 does_match |= matchWildcards(name,
989 fw->class.res_name);
990 }
991 if(( pp->invert && does_match) ||
992 (!pp->invert && !does_match))
993 {
994 return False;
995 }
996 }
997
998 if (mask->my_flags.do_check_fullscreen)
999 {
1000 return IS_EWMH_FULLSCREEN(fw);
1001 }
1002
1003 if (mask->layer == -1 && sf)
1004 {
1005 int is_same_layer;
1006
1007 is_same_layer = (fw->layer == sf->layer);
1008 if (mask->my_flags.needs_same_layer != is_same_layer)
1009 {
1010 return False;
1011 }
1012 }
1013 if (mask->layer >= 0)
1014 {
1015 int is_same_layer;
1016
1017 is_same_layer = (fw->layer == mask->layer);
1018 if (mask->my_flags.needs_same_layer != is_same_layer)
1019 {
1020 return False;
1021 }
1022 }
1023 if (mask->placed_by_button_set_mask)
1024 {
1025 if (!((mask->placed_by_button_set_mask &
1026 (1<<(fw->placed_by_button - 1))) ==
1027 (mask->placed_by_button_set_mask &
1028 mask->placed_by_button_mask)))
1029 {
1030 return False;
1031 }
1032 }
1033 if (GET_USER_STATES(mask) !=
1034 (mask->flag_mask.common.user_states & GET_USER_STATES(fw)))
1035 {
1036 return False;
1037 }
1038 if (mask->my_flags.use_do_accept_focus)
1039 {
1040 Bool f;
1041
1042 f = focus_does_accept_input_focus(fw);
1043 if (fw && !FP_DO_FOCUS_BY_FUNCTION(FW_FOCUS_POLICY(fw)))
1044 {
1045 f = False;
1046 }
1047 else if (fw && FP_IS_LENIENT(FW_FOCUS_POLICY(fw)))
1048 {
1049 f = True;
1050 }
1051 if (!f != !mask->my_flags.do_accept_focus)
1052 {
1053 return False;
1054 }
1055 }
1056 if (mask->my_flags.needs_focus != NEEDS_ANY)
1057 {
1058 int is_focused;
1059
1060 is_focused = (fw == get_focus_window());
1061 if (!is_focused && mask->my_flags.needs_focus == NEEDS_TRUE)
1062 {
1063 return False;
1064 }
1065 else if (is_focused &&
1066 mask->my_flags.needs_focus == NEEDS_FALSE)
1067 {
1068 return False;
1069 }
1070 }
1071 if (mask->my_flags.needs_pointer != NEEDS_ANY)
1072 {
1073 int has_pointer;
1074 FvwmWindow *t;
1075
1076 t = get_pointer_fvwm_window();
1077 if (t != NULL && t == fw)
1078 {
1079 has_pointer = 1;
1080 }
1081 else
1082 {
1083 has_pointer = 0;
1084 }
1085 if (!has_pointer && mask->my_flags.needs_pointer == NEEDS_TRUE)
1086 {
1087 return False;
1088 }
1089 else if (has_pointer &&
1090 mask->my_flags.needs_pointer == NEEDS_FALSE)
1091 {
1092 return False;
1093 }
1094 }
1095 if (mask->my_flags.do_check_overlapped)
1096 {
1097 int is_o;
1098
1099 is_o = (is_on_top_of_layer(fw) == False);
1100 if (is_o != mask->my_flags.needs_overlapped)
1101 {
1102 return False;
1103 }
1104 }
1105 if (mask->my_flags.do_check_cond_desk)
1106 {
1107 if (fw->Desk == mask->desk)
1108 return True;
1109 else
1110 return False;
1111
1112 if (fw->Desk != mask->desk)
1113 return True;
1114 else
1115 return False;
1116 }
1117 if (mask->my_flags.do_check_screen)
1118 {
1119 rectangle g;
1120 int scr;
1121
1122 get_unshaded_geometry(fw, &g);
1123 scr = FScreenOfPointerXY(g.x, g.y);
1124
1125 if (mask->my_flags.do_not_check_screen) {
1126 /* Negation of (!screen n) specified. */
1127 return (scr != mask->screen);
1128 } else
1129 return (scr == mask->screen);
1130 }
1131
1132 return True;
1133 }
1134
direction_cmd(F_CMD_ARGS,Bool is_scan)1135 static void direction_cmd(F_CMD_ARGS, Bool is_scan)
1136 {
1137 rectangle my_g;
1138 rectangle his_g;
1139 int my_cx;
1140 int my_cy;
1141 int his_cx;
1142 int his_cy;
1143 int cross = 0;
1144 int offset = 0;
1145 int distance = 0;
1146 int cycle = False;
1147 int forward = False;
1148 int score;
1149 int best_cross = 0;
1150 int best_score;
1151 int worst_score = -1;
1152 FvwmWindow *tfw;
1153 FvwmWindow *fw_best;
1154 int dir;
1155 int dir2;
1156 Bool right_handed=False;
1157 char *flags;
1158 char *restofline;
1159 char *tmp;
1160 float tx;
1161 float ty;
1162 WindowConditionMask mask;
1163 Bool is_pointer_relative;
1164 FvwmWindow * const fw = exc->w.fw;
1165
1166 /* Parse the direction. */
1167 tmp = PeekToken(action, &action);
1168 if (StrEquals(tmp, "FromPointer"))
1169 {
1170 is_pointer_relative = True;
1171 tmp = PeekToken(action, &action);
1172 }
1173 else
1174 {
1175 is_pointer_relative = False;
1176 }
1177 dir = gravity_parse_dir_argument(tmp, NULL, -1);
1178 if (dir == -1 || dir > DIR_ALL_MASK)
1179 {
1180 fvwm_msg(ERR, "Direction", "Invalid direction %s",
1181 (tmp) ? tmp : "");
1182 if (cond_rc != NULL)
1183 {
1184 cond_rc->rc = COND_RC_ERROR;
1185 }
1186 return;
1187 }
1188 if (is_scan)
1189 {
1190 cycle = True;
1191 tmp = PeekToken(action, &action);
1192 if ( ! tmp )
1193 {
1194 fvwm_msg(
1195 ERR, "Direction", "Missing minor direction %s",
1196 (tmp) ? tmp : "");
1197 if (cond_rc != NULL)
1198 {
1199 cond_rc->rc = COND_RC_ERROR;
1200 }
1201 return;
1202 }
1203 dir2 = gravity_parse_dir_argument(tmp, NULL, -1);
1204 /* if enum direction_t changes, this is trashed. */
1205 if (dir2 == -1 || dir2 > DIR_NW ||
1206 (dir < 4) != (dir2 < 4) || (abs(dir - dir2) & 1) != 1)
1207 {
1208 fvwm_msg(
1209 ERR, "Direction", "Invalid minor direction %s",
1210 (tmp) ? tmp : "");
1211 if (cond_rc != NULL)
1212 {
1213 cond_rc->rc = COND_RC_ERROR;
1214 }
1215 return;
1216 }
1217 else if (dir2 - dir == 1 || dir2 - dir == -3)
1218 {
1219 right_handed=True;
1220 }
1221 }
1222
1223 /* Create the mask for flags */
1224 flags = CreateFlagString(action, &restofline);
1225 if (!restofline)
1226 {
1227 if (flags)
1228 {
1229 free(flags);
1230 }
1231 if (cond_rc != NULL)
1232 {
1233 cond_rc->rc = COND_RC_NO_MATCH;
1234 }
1235 return;
1236 }
1237 DefaultConditionMask(&mask);
1238 CreateConditionMask(flags, &mask);
1239 if (flags)
1240 {
1241 free(flags);
1242 }
1243
1244 /* If there is a focused window, use that as a starting point.
1245 * Otherwise we use the pointer as a starting point. */
1246 if (fw && is_pointer_relative == False)
1247 {
1248 get_visible_window_or_icon_geometry(fw, &my_g);
1249 my_cx = my_g.x + my_g.width / 2;
1250 my_cy = my_g.y + my_g.height / 2;
1251 }
1252 else
1253 {
1254 if (FQueryPointer(
1255 dpy, Scr.Root, &JunkRoot, &JunkChild, &my_g.x,
1256 &my_g.y, &JunkX, &JunkY, &JunkMask) == False)
1257 {
1258 /* pointer is on a different screen */
1259 my_g.x = 0;
1260 my_g.y = 0;
1261 }
1262 my_g.width = 1;
1263 my_g.height = 1;
1264 my_cx = my_g.x;
1265 my_cy = my_g.y;
1266 }
1267
1268 /* Next we iterate through all windows and choose the closest one in
1269 * the wanted direction. */
1270 fw_best = NULL;
1271 best_score = -1;
1272 for (tfw = Scr.FvwmRoot.next; tfw != NULL; tfw = tfw->next)
1273 {
1274 /* Skip every window that does not match conditionals. Also
1275 * skip the currently focused window. That would be too
1276 * close. :) */
1277 if (tfw == fw || !MatchesConditionMask(tfw, &mask))
1278 {
1279 continue;
1280 }
1281
1282 /* Calculate relative location of the window. */
1283 get_visible_window_or_icon_geometry(tfw, &his_g);
1284 his_g.x -= my_cx;
1285 his_g.y -= my_cy;
1286 his_cx = his_g.x + his_g.width / 2;
1287 his_cy = his_g.y + his_g.height / 2;
1288
1289 if (dir > DIR_MAJOR_MASK && dir <= DIR_MINOR_MASK)
1290 {
1291 int tx;
1292 /* Rotate the diagonals 45 degrees counterclockwise. To
1293 * do this, multiply the matrix /+h +h\ with the vector
1294 * (x y). \-h +h/
1295 * h = sqrt(0.5). We can set h := 1 since absolute
1296 * distance doesn't * matter here. */
1297 tx = his_cx + his_cy;
1298 his_cy = -his_cx + his_cy;
1299 his_cx = tx;
1300 }
1301 /* Arrange so that distance and offset are positive in desired
1302 * direction. */
1303 switch (dir)
1304 {
1305 case DIR_S:
1306 case DIR_SW:
1307 forward = True;
1308 case DIR_N:
1309 case DIR_NE:
1310 cross = -his_cx;
1311 offset = (his_cx < 0) ? -his_cx : his_cx;
1312 distance = (dir == DIR_N || dir == DIR_NE) ?
1313 -his_cy : his_cy;
1314 break;
1315 case DIR_E: /* E */
1316 case DIR_SE: /* SE */
1317 forward = True;
1318 case DIR_W: /* W */
1319 case DIR_NW: /* NW */
1320 cross = his_cy;
1321 offset = (his_cy < 0) ? -his_cy : his_cy;
1322 distance = (dir == DIR_W || dir == DIR_NW) ?
1323 -his_cx : his_cx;
1324 break;
1325 case DIR_C:
1326 offset = 0;
1327 tx = (float)his_cx;
1328 ty = (float)his_cy;
1329 distance = (int)sqrt(tx * tx + ty * ty);
1330 break;
1331 }
1332
1333 if (cycle)
1334 {
1335 offset=0;
1336 }
1337 else if (distance < 0)
1338 {
1339 /* Target must be in given direction. */
1340 continue;
1341 }
1342 else if (distance == 0 && dir != DIR_C)
1343 {
1344 continue;
1345 }
1346
1347 /* Calculate score for this window. The smaller the better. */
1348 score = distance + offset;
1349 if (!right_handed)
1350 {
1351 cross= -cross;
1352 }
1353 if (cycle)
1354 {
1355 int ordered = (forward == (cross < best_cross));
1356
1357 if (distance < 0 && best_score == -1 &&
1358 (score < worst_score ||
1359 (score == worst_score && ordered)))
1360 {
1361 fw_best = tfw;
1362 worst_score = score;
1363 best_cross = cross;
1364 }
1365 if (score == 0 && forward == (cross < 0) &&
1366 dir != DIR_C)
1367 {
1368 continue;
1369 }
1370 if (distance >= 0 &&
1371 (best_score == -1 || score < best_score ||
1372 (score == best_score && ordered)))
1373 {
1374 fw_best = tfw;
1375 best_score = score;
1376 best_cross = cross;
1377 }
1378 }
1379 else
1380 {
1381 /* windows more than 45 degrees off the direction are
1382 * heavily penalized and will only be chosen if nothing
1383 * else within a million pixels */
1384 if (offset > distance)
1385 {
1386 score += 1000000;
1387 }
1388 if (best_score == -1 || score < best_score ||
1389 (score == best_score && dir == DIR_C))
1390 {
1391 fw_best = tfw;
1392 best_score = score;
1393 }
1394 }
1395 } /* for */
1396
1397 if (fw_best)
1398 {
1399 if (cond_rc != NULL)
1400 {
1401 cond_rc->rc = COND_RC_OK;
1402 }
1403 execute_function_override_window(
1404 cond_rc, exc, restofline, 0, fw_best);
1405 }
1406 else if (cond_rc != NULL)
1407 {
1408 cond_rc->rc = COND_RC_NO_MATCH;
1409 }
1410 FreeConditionMask(&mask);
1411
1412 return;
1413 }
1414
__rc_matches_rcstring_consume(char ** ret_rest,cond_rc_t * cond_rc,char * action)1415 static int __rc_matches_rcstring_consume(
1416 char **ret_rest, cond_rc_t *cond_rc, char *action)
1417 {
1418 cond_rc_enum match_rc;
1419 char *orig_flags;
1420 char *flags;
1421 int is_not_reversed = 1;
1422 int ret;
1423
1424 /* Create window mask */
1425 orig_flags = CreateFlagString(action, ret_rest);
1426 flags = orig_flags;
1427 if (flags == NULL)
1428 {
1429 match_rc = COND_RC_NO_MATCH;
1430 }
1431 else
1432 {
1433 if (*flags == '!')
1434 {
1435 is_not_reversed = 0;
1436 flags++;
1437 }
1438 if (StrEquals(flags, "1") || StrEquals(flags, "match"))
1439 {
1440 match_rc = COND_RC_OK;
1441 }
1442 else if (StrEquals(flags, "0") || StrEquals(flags, "nomatch"))
1443 {
1444 match_rc = COND_RC_NO_MATCH;
1445 }
1446 else if (StrEquals(flags, "-1") || StrEquals(flags, "error"))
1447 {
1448 match_rc = COND_RC_ERROR;
1449 }
1450 else if (StrEquals(flags, "-2") || StrEquals(flags, "break"))
1451 {
1452 match_rc = COND_RC_BREAK;
1453 }
1454 else
1455 {
1456 match_rc = COND_RC_NO_MATCH;
1457 /* Does anyone check for other numerical returncode
1458 * values? If so, this might have to be changed. */
1459 fprintf(
1460 stderr, "Unrecognised condition \"%s\" in"
1461 " TestRc command.\n", flags);
1462 }
1463 }
1464 if (orig_flags != NULL)
1465 {
1466 free(orig_flags);
1467 }
1468 ret = ((cond_rc->rc == match_rc) == is_not_reversed);
1469
1470 return ret;
1471 }
1472
1473 /* ---------------------------- builtin commands --------------------------- */
1474
CMD_Prev(F_CMD_ARGS)1475 void CMD_Prev(F_CMD_ARGS)
1476 {
1477 circulate_cmd(F_PASS_ARGS, C_WINDOW, -1, True, True);
1478
1479 return;
1480 }
1481
CMD_Next(F_CMD_ARGS)1482 void CMD_Next(F_CMD_ARGS)
1483 {
1484 circulate_cmd(F_PASS_ARGS, C_WINDOW, 1, True, True);
1485
1486 return;
1487 }
1488
CMD_None(F_CMD_ARGS)1489 void CMD_None(F_CMD_ARGS)
1490 {
1491 circulate_cmd(F_PASS_ARGS, C_ROOT, 1, False, False);
1492 /* invert return code */
1493 switch (cond_rc->rc)
1494 {
1495 case COND_RC_OK:
1496 cond_rc->rc = COND_RC_NO_MATCH;
1497 break;
1498 case COND_RC_NO_MATCH:
1499 cond_rc->rc = COND_RC_OK;
1500 break;
1501 default:
1502 break;
1503 }
1504
1505 return;
1506 }
1507
CMD_Any(F_CMD_ARGS)1508 void CMD_Any(F_CMD_ARGS)
1509 {
1510 circulate_cmd(F_PASS_ARGS, exc->w.wcontext, 1, False, True);
1511
1512 return;
1513 }
1514
CMD_Current(F_CMD_ARGS)1515 void CMD_Current(F_CMD_ARGS)
1516 {
1517 circulate_cmd(F_PASS_ARGS, C_WINDOW, 0, True, True);
1518
1519 return;
1520 }
1521
CMD_PointerWindow(F_CMD_ARGS)1522 void CMD_PointerWindow(F_CMD_ARGS)
1523 {
1524 exec_context_changes_t ecc;
1525
1526 ecc.w.fw = get_pointer_fvwm_window();
1527 exc = exc_clone_context(exc, &ecc, ECC_FW);
1528 select_cmd(F_PASS_ARGS);
1529 exc_destroy_context(exc);
1530
1531 return;
1532 }
1533
CMD_ThisWindow(F_CMD_ARGS)1534 void CMD_ThisWindow(F_CMD_ARGS)
1535 {
1536 select_cmd(F_PASS_ARGS);
1537
1538 return;
1539 }
1540
CMD_Pick(F_CMD_ARGS)1541 void CMD_Pick(F_CMD_ARGS)
1542 {
1543 select_cmd(F_PASS_ARGS);
1544
1545 return;
1546 }
1547
CMD_All(F_CMD_ARGS)1548 void CMD_All(F_CMD_ARGS)
1549 {
1550 FvwmWindow *t, **g;
1551 char *restofline;
1552 WindowConditionMask mask;
1553 char *flags;
1554 int num, i;
1555 Bool does_any_window_match = False;
1556 char *token;
1557 Bool do_reverse = False;
1558 Bool use_stack = False;
1559
1560 while (True) /* break when a non-option is found */
1561 {
1562 token = PeekToken(action, &restofline);
1563 if (StrEquals(token, "Reverse"))
1564 {
1565 if (!*restofline)
1566 {
1567 /* if not any more actions, then Reverse
1568 * probably is some user function, so ignore
1569 * it and do the old behaviour */
1570 break;
1571 }
1572 else
1573 {
1574 do_reverse = True;
1575 action = restofline;
1576 }
1577 }
1578 else if (StrEquals(token, "UseStack"))
1579 {
1580 if (!*restofline)
1581 {
1582 /* if not any more actions, then UseStack
1583 * probably is some user function, so ignore
1584 * it and do the old behaviour */
1585 break;
1586 }
1587 else
1588 {
1589 use_stack = True;
1590 action = restofline;
1591 }
1592 }
1593 else
1594 {
1595 /* No more options -- continue with flags and
1596 * commands */
1597 break;
1598 }
1599 }
1600
1601 flags = CreateFlagString(action, &restofline);
1602 DefaultConditionMask(&mask);
1603 mask.my_flags.use_circulate_hit = 1;
1604 mask.my_flags.use_circulate_hit_icon = 1;
1605 mask.my_flags.use_circulate_hit_shaded = 1;
1606 CreateConditionMask(flags, &mask);
1607 if (flags)
1608 {
1609 free(flags);
1610 }
1611
1612 num = 0;
1613 for (t = Scr.FvwmRoot.next; t; t = t->next)
1614 {
1615 num++;
1616 }
1617 g = (FvwmWindow **)safemalloc(num * sizeof(FvwmWindow *));
1618 num = 0;
1619 if (!use_stack)
1620 {
1621 for (t = Scr.FvwmRoot.next; t; t = t->next)
1622 {
1623 if (MatchesConditionMask(t, &mask))
1624 {
1625 g[num++] = t;
1626 does_any_window_match = True;
1627 }
1628 }
1629 }
1630 else
1631 {
1632 for (t = Scr.FvwmRoot.stack_next; t && t != &Scr.FvwmRoot;
1633 t = t->stack_next)
1634 {
1635 if (MatchesConditionMask(t, &mask))
1636 {
1637 g[num++] = t;
1638 does_any_window_match = True;
1639 }
1640 }
1641 }
1642 if (do_reverse)
1643 {
1644 for (i = num-1; i >= 0; i--)
1645 {
1646 execute_function_override_window(
1647 cond_rc, exc, restofline, 0, g[i]);
1648 }
1649 }
1650 else
1651 {
1652 for (i = 0; i < num; i++)
1653 {
1654 execute_function_override_window(
1655 cond_rc, exc, restofline, 0, g[i]);
1656 }
1657 }
1658 if (cond_rc != NULL && cond_rc->rc != COND_RC_BREAK)
1659 {
1660 cond_rc->rc = (does_any_window_match == False) ?
1661 COND_RC_NO_MATCH : COND_RC_OK;
1662 }
1663 free(g);
1664 FreeConditionMask(&mask);
1665
1666 return;
1667 }
1668
1669 /*
1670 * Execute a function to the closest window in the given
1671 * direction.
1672 */
CMD_Direction(F_CMD_ARGS)1673 void CMD_Direction(F_CMD_ARGS)
1674 {
1675 direction_cmd(F_PASS_ARGS,False);
1676 }
1677
CMD_ScanForWindow(F_CMD_ARGS)1678 void CMD_ScanForWindow(F_CMD_ARGS)
1679 {
1680 direction_cmd(F_PASS_ARGS,True);
1681 }
1682
CMD_WindowId(F_CMD_ARGS)1683 void CMD_WindowId(F_CMD_ARGS)
1684 {
1685 FvwmWindow *t;
1686 char *token;
1687 char *naction;
1688 unsigned long win;
1689 Bool use_condition = False;
1690 Bool use_screenroot = False;
1691 WindowConditionMask mask;
1692 char *flags, *restofline;
1693
1694 /* Get window ID */
1695 action = GetNextToken(action, &token);
1696
1697 if (token && StrEquals(token, "root"))
1698 {
1699 int screen = Scr.screen;
1700
1701 free(token);
1702 token = PeekToken(action, &naction);
1703 if (!token || GetIntegerArguments(token, NULL, &screen, 1) != 1)
1704 {
1705 screen = Scr.screen;
1706 }
1707 else
1708 {
1709 action = naction;
1710 }
1711 use_screenroot = True;
1712 if (screen < 0 || screen >= Scr.NumberOfScreens)
1713 {
1714 screen = 0;
1715 }
1716 win = XRootWindow(dpy, screen);
1717 if (win == None)
1718 {
1719 if (cond_rc != NULL)
1720 {
1721 cond_rc->rc = COND_RC_ERROR;
1722 }
1723 return;
1724 }
1725 }
1726 else if (token)
1727 {
1728 /* SunOS doesn't have strtoul */
1729 win = (unsigned long)strtol(token, NULL, 0);
1730 free(token);
1731 }
1732 else
1733 {
1734 win = 0;
1735 }
1736
1737 /* Look for condition - CreateFlagString returns NULL if no '(' or '['
1738 */
1739 if (!use_screenroot)
1740 {
1741 flags = CreateFlagString(action, &restofline);
1742 if (flags)
1743 {
1744 /* Create window mask */
1745 use_condition = True;
1746 DefaultConditionMask(&mask);
1747
1748 /* override for Current [] */
1749 mask.my_flags.use_circulate_hit = 1;
1750 mask.my_flags.use_circulate_hit_icon = 1;
1751 mask.my_flags.use_circulate_hit_icon = 1;
1752
1753 CreateConditionMask(flags, &mask);
1754 free(flags);
1755
1756 /* Relocate action */
1757 action = restofline;
1758 }
1759 }
1760
1761 /* Search windows */
1762 for (t = Scr.FvwmRoot.next; t; t = t->next)
1763 {
1764 if (FW_W(t) == win)
1765 {
1766 /* do it if no conditions or the conditions match */
1767 if (action && (!use_condition ||
1768 MatchesConditionMask(t, &mask)))
1769 {
1770 if (cond_rc != NULL)
1771 {
1772 cond_rc->rc = COND_RC_OK;
1773 }
1774 execute_function_override_window(
1775 cond_rc, exc, action, 0, t);
1776 }
1777 else if (cond_rc != NULL)
1778 {
1779 cond_rc->rc = COND_RC_NO_MATCH;
1780 }
1781 break;
1782 }
1783 }
1784 if (!t)
1785 {
1786 /* The window is not managed by fvwm. Still some functions may
1787 * work on it. */
1788 if (use_condition)
1789 {
1790 if (cond_rc != NULL)
1791 {
1792 cond_rc->rc = COND_RC_ERROR;
1793 }
1794 }
1795 else if (XGetGeometry(
1796 dpy, win, &JunkRoot, &JunkX, &JunkY,
1797 (unsigned int*)&JunkWidth,
1798 (unsigned int*)&JunkHeight,
1799 (unsigned int*)&JunkBW,
1800 (unsigned int*)&JunkDepth) != 0)
1801 {
1802 if (cond_rc != NULL)
1803 {
1804 cond_rc->rc = COND_RC_OK;
1805 }
1806 if (action != NULL)
1807 {
1808 const exec_context_t *exc2;
1809 exec_context_changes_t ecc;
1810
1811 ecc.w.fw = NULL;
1812 ecc.w.w = win;
1813 ecc.w.wcontext = C_UNMANAGED;
1814 exc2 = exc_clone_context(
1815 exc, &ecc,
1816 ECC_FW | ECC_W | ECC_WCONTEXT);
1817 execute_function(
1818 cond_rc, exc2, action,
1819 FUNC_IS_UNMANAGED);
1820 exc_destroy_context(exc2);
1821 }
1822 }
1823 else
1824 {
1825 /* window id does not exist */
1826 if (cond_rc != NULL)
1827 {
1828 cond_rc->rc = COND_RC_ERROR;
1829 }
1830 }
1831 }
1832
1833 /* Tidy up */
1834 if (use_condition)
1835 {
1836 FreeConditionMask(&mask);
1837 }
1838
1839 return;
1840 }
1841
CMD_TestRc(F_CMD_ARGS)1842 void CMD_TestRc(F_CMD_ARGS)
1843 {
1844 char *rest;
1845
1846 if (cond_rc == NULL)
1847 {
1848 /* useless if no return code to compare to is given */
1849 return;
1850 }
1851 if (__rc_matches_rcstring_consume(&rest, cond_rc, action) &&
1852 rest != NULL)
1853 {
1854 /* execute the command in root window context; overwrite the
1855 * return code with the return code of the command */
1856 execute_function(cond_rc, exc, rest, 0);
1857 }
1858
1859 return;
1860 }
1861
CMD_Break(F_CMD_ARGS)1862 void CMD_Break(F_CMD_ARGS)
1863 {
1864 int rc;
1865
1866 if (cond_rc == NULL)
1867 {
1868 return;
1869 }
1870 rc = GetIntegerArguments(action, &action, &cond_rc->break_levels, 1);
1871 if (rc != 1 || cond_rc->break_levels <= 0)
1872 {
1873 cond_rc->break_levels = -1;
1874 }
1875 cond_rc->rc = COND_RC_BREAK;
1876
1877 return;
1878 }
1879
CMD_NoWindow(F_CMD_ARGS)1880 void CMD_NoWindow(F_CMD_ARGS)
1881 {
1882 execute_function_override_window(cond_rc, exc, action, 0, NULL);
1883
1884 return;
1885 }
1886
1887 /* ver() - convert a version string to a floating-point number that
1888 * can be used to compare different versions.
1889 * ie. converts "2.5.11" to 2005011 */
ver(char * str)1890 static int ver (char *str)
1891 {
1892 char *n;
1893 int v;
1894
1895 str = DoPeekToken(str, &n, NULL, ".", NULL);
1896 if (!n)
1897 {
1898 return -1.0;
1899 }
1900 v = atoi(n) * 1000000;
1901 str = DoPeekToken(str, &n, NULL, ".", NULL);
1902 if (!n)
1903 {
1904 return -1.0;
1905 }
1906 v += atoi(n) * 1000;
1907 str = DoPeekToken(str, &n, NULL, ".", NULL);
1908 if (!n)
1909 {
1910 return -1.0;
1911 }
1912 v += atoi(n);
1913
1914 return v;
1915 }
1916
1917 /* match_version() - compare $version against this version of fvwm
1918 * using the operator specified by $operator. */
match_version(char * version,char * operator)1919 static Bool match_version(char *version, char *operator)
1920 {
1921 static int fvwm_version = -1;
1922 const int v = ver(version);
1923
1924 if (fvwm_version < 0)
1925 {
1926 char *tmp = safestrdup(VERSION);
1927 fvwm_version = ver(tmp);
1928 free(tmp);
1929 }
1930 if (v < 0)
1931 {
1932 fprintf(
1933 stderr, "match_version: Invalid version: %s\n",
1934 version);
1935 return False;
1936 }
1937 if (strcmp(operator, ">=") == 0)
1938 {
1939 return fvwm_version >= v;
1940 }
1941 else if (strcmp(operator, ">") == 0)
1942 {
1943 return fvwm_version > v;
1944 }
1945 else if (strcmp(operator, "<=") == 0)
1946 {
1947 return fvwm_version <= v;
1948 }
1949 else if (strcmp(operator, "<") == 0)
1950 {
1951 return fvwm_version < v;
1952 }
1953 else if (strcmp(operator, "==") == 0)
1954 {
1955 return (v == fvwm_version);
1956 }
1957 else if (strcmp(operator, "!=") == 0)
1958 {
1959 return (v != fvwm_version);
1960 }
1961 else
1962 {
1963 fprintf(
1964 stderr, "match_version: Invalid operator: %s\n",
1965 operator);
1966 }
1967
1968 return False;
1969 }
1970
CMD_Test(F_CMD_ARGS)1971 void CMD_Test(F_CMD_ARGS)
1972 {
1973 char *restofline;
1974 char *flags;
1975 char *condition;
1976 char *flags_ptr;
1977 int match;
1978 int error;
1979
1980 flags = CreateFlagString(action, &restofline);
1981
1982 /* Next parse the flags in the string. */
1983 flags_ptr = flags;
1984 flags_ptr = GetNextSimpleOption(flags_ptr, &condition);
1985
1986 match = 1;
1987 error = 0;
1988 while (condition)
1989 {
1990 char *cond;
1991 int reverse;
1992
1993 cond = condition;
1994 reverse = 0;
1995 if (*cond == '!')
1996 {
1997 reverse = 1;
1998 cond++;
1999 }
2000 if (StrEquals(cond, "True"))
2001 {
2002 match = 1;
2003 }
2004 else if (StrEquals(cond, "False"))
2005 {
2006 match = 0;
2007 }
2008 else if (StrEquals(cond, "Version"))
2009 {
2010 char *pattern;
2011 flags_ptr = GetNextSimpleOption(flags_ptr, &pattern);
2012 if (pattern)
2013 {
2014 char *ver;
2015 flags_ptr = GetNextSimpleOption(
2016 flags_ptr, &ver);
2017 if (ver == NULL)
2018 {
2019 match = matchWildcards(
2020 pattern, VERSION);
2021 }
2022 else
2023 {
2024 match = match_version(ver, pattern);
2025 free(ver);
2026 }
2027 free(pattern);
2028 }
2029 else
2030 {
2031 error = 1;
2032 }
2033 }
2034 else if (StrEquals(cond, "Start"))
2035 {
2036 match = exc->type == EXCT_INIT ||
2037 exc->type == EXCT_RESTART;
2038 }
2039 else if (StrEquals(cond, "Init"))
2040 {
2041 match = exc->type == EXCT_INIT;
2042 }
2043 else if (StrEquals(cond, "Restart"))
2044 {
2045 match = exc->type == EXCT_RESTART;
2046 }
2047 else if (StrEquals(cond, "Exit"))
2048 {
2049 match = exc->type == EXCT_QUIT ||
2050 exc->type == EXCT_TORESTART;
2051 }
2052 else if (StrEquals(cond, "Quit"))
2053 {
2054 match = exc->type == EXCT_QUIT;
2055 }
2056 else if (StrEquals(cond, "ToRestart"))
2057 {
2058 match = exc->type == EXCT_TORESTART;
2059 }
2060 else if (StrEquals(cond, "x") || StrEquals(cond, "r") ||
2061 StrEquals(cond, "w") || StrEquals(cond, "f") ||
2062 StrEquals(cond, "i"))
2063 {
2064 char *pattern;
2065 int type = X_OK;
2066 Bool im = 0;
2067
2068 switch(cond[0])
2069 {
2070 case 'X':
2071 case 'x':
2072 type = X_OK;
2073 break;
2074 case 'R':
2075 case 'r':
2076 type = R_OK;
2077 break;
2078 case 'W':
2079 case 'w':
2080 type = W_OK;
2081 break;
2082 case 'f':
2083 case 'F':
2084 type = F_OK;
2085 break;
2086 case 'i':
2087 case 'I':
2088 im = True;
2089 type = R_OK;
2090 break;
2091 default:
2092 /* cannot happen */
2093 break;
2094 }
2095 flags_ptr = GetNextSimpleOption(flags_ptr, &pattern);
2096 if (pattern)
2097 {
2098 match = cond_check_access(pattern, type, im);
2099 free(pattern);
2100 }
2101 else
2102 {
2103 error = 1;
2104 }
2105 }
2106 else if (StrEquals(cond, "EnvIsSet"))
2107 {
2108 char *var_name;
2109
2110 flags_ptr = GetNextSimpleOption(flags_ptr, &var_name);
2111 if (var_name)
2112 {
2113 const char *value = getenv(var_name);
2114
2115 match = (value != NULL) ? 1 : 0;
2116 }
2117 else
2118 {
2119 error = 1;
2120 }
2121 }
2122 else if (StrEquals(cond, "EnvMatch"))
2123 {
2124 char *var_name;
2125
2126 flags_ptr = GetNextSimpleOption(flags_ptr, &var_name);
2127 if (var_name)
2128 {
2129 const char *value;
2130 if ( (strlen(var_name) > 10) && (memcmp(var_name,"infostore.",10) == 0) )
2131 {
2132 value = get_metainfo_value(var_name+10);
2133 }
2134 else
2135 {
2136 value = getenv(var_name);
2137 }
2138 char *pattern;
2139 /* unfortunately, GetNextSimpleOption is
2140 * broken, does not accept quoted empty ""
2141 *
2142 * DV (2-Sep-2014): It is *not* broken. The
2143 * parsing functions never return empty tokens
2144 * by design.
2145 */
2146 flags_ptr = GetNextSimpleOption(
2147 flags_ptr, &pattern);
2148 if (!value)
2149 {
2150 value = "";
2151 }
2152 if (pattern)
2153 {
2154 match =
2155 /* include empty string case */
2156 (!pattern[0] && !value[0]) ||
2157 matchWildcards(pattern, value);
2158 }
2159 else
2160 {
2161 error = 1;
2162 }
2163 }
2164 else
2165 {
2166 error = 1;
2167 }
2168 }
2169 else if (StrEquals(cond, "EdgeIsActive"))
2170 {
2171 direction_t dir= DIR_NONE;
2172 char *dirname;
2173 char *next;
2174 next = GetNextSimpleOption(flags_ptr, &dirname);
2175 if (dirname)
2176 {
2177 dir = gravity_parse_dir_argument(
2178 dirname, NULL, DIR_NONE);
2179 if (dir == DIR_NONE)
2180 {
2181 if (!StrEquals(dirname, "Any"))
2182 {
2183 next = flags_ptr;
2184 }
2185 }
2186 else if (dir > DIR_MAJOR_MASK)
2187 {
2188 error = 1;
2189 }
2190 free(dirname);
2191 }
2192
2193 if (!error)
2194 {
2195 if (((dir == DIR_W || dir == DIR_NONE) &&
2196 Scr.PanFrameLeft.isMapped) ||
2197 ((dir == DIR_N || dir == DIR_NONE) &&
2198 Scr.PanFrameTop.isMapped) ||
2199 ((dir == DIR_S || dir == DIR_NONE) &&
2200 Scr.PanFrameBottom.isMapped) ||
2201 ((dir == DIR_E || dir == DIR_NONE) &&
2202 Scr.PanFrameRight.isMapped))
2203 {
2204 match = 1;
2205 }
2206 else
2207 {
2208 match = 0;
2209 }
2210 }
2211 flags_ptr = next;
2212 }
2213 else if (StrEquals(cond, "EdgeHasPointer"))
2214 {
2215 int x,y;
2216 Window win;
2217 direction_t dir = DIR_NONE;
2218 char *dirname;
2219 char *next;
2220 next = GetNextSimpleOption(flags_ptr, &dirname);
2221 if (dirname)
2222 {
2223 dir = gravity_parse_dir_argument(
2224 dirname, NULL, DIR_NONE);
2225 if (dir == DIR_NONE)
2226 {
2227 if (!StrEquals(dirname, "Any"))
2228 {
2229 next = flags_ptr;
2230 }
2231 }
2232 else if (dir > DIR_MAJOR_MASK)
2233 {
2234 error = 1;
2235 }
2236 free(dirname);
2237 }
2238
2239 if (!error)
2240 {
2241 if (FQueryPointer(
2242 dpy, Scr.Root, &JunkRoot, &win,
2243 &JunkX, &JunkY, &x, &y, &JunkMask)
2244 == False)
2245 {
2246 /* pointer is on a different screen */
2247 match = 0;
2248 }
2249 else if (is_pan_frame(win))
2250 {
2251 if (dir == DIR_NONE ||
2252 (dir == DIR_N &&
2253 win == Scr.PanFrameTop.win) ||
2254 (dir == DIR_S &&
2255 win == Scr.PanFrameBottom.win) ||
2256 (dir == DIR_E &&
2257 win == Scr.PanFrameRight.win) ||
2258 (dir == DIR_W &&
2259 win == Scr.PanFrameLeft.win))
2260 {
2261 match = 1;
2262 }
2263 else
2264 {
2265 match = 0;
2266 }
2267 }
2268 else
2269 {
2270 match = 0;
2271 }
2272 }
2273 flags_ptr = next;
2274 }
2275 else
2276 {
2277 /* unrecognized condition */
2278 error = 1;
2279 fprintf(
2280 stderr, "Unrecognised condition \"%s\" in"
2281 " Test command.\n", cond);
2282 }
2283
2284 if (reverse)
2285 {
2286 match = !match;
2287 }
2288 free(condition);
2289 if (error || !match)
2290 {
2291 break;
2292 }
2293 flags_ptr = GetNextSimpleOption(flags_ptr, &condition);
2294 }
2295
2296 if (flags != NULL)
2297 {
2298 free(flags);
2299 }
2300 if (!error && match)
2301 {
2302 execute_function(cond_rc, exc, restofline, 0);
2303 }
2304 if (cond_rc != NULL)
2305 {
2306 if (error)
2307 {
2308 cond_rc->rc = COND_RC_ERROR;
2309 }
2310 else if (match)
2311 {
2312 cond_rc->rc = COND_RC_OK;
2313 }
2314 else
2315 {
2316 cond_rc->rc = COND_RC_NO_MATCH;
2317 }
2318 }
2319
2320 return;
2321 }
2322