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 /* This module is based on Twm, but has been siginificantly modified
16 * by Rob Nation
17 */
18 /*
19 * Copyright 1988 by Evans & Sutherland Computer Corporation,
20 * Salt Lake City, Utah
21 * Portions Copyright 1989 by the Massachusetts Institute of Technology
22 * Cambridge, Massachusetts
23 *
24 * All Rights Reserved
25 *
26 * Permission to use, copy, modify, and distribute this software and
27 * its documentation for any purpose and without fee is hereby
28 * granted, provided that the above copyright notice appear in all
29 * copies and that both that copyright notice and this permis-
30 * sion notice appear in supporting documentation, and that the
31 * names of Evans & Sutherland and M.I.T. not be used in advertising
32 * in publicity pertaining to distribution of the software without
33 * specific, written prior permission.
34 *
35 * EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD
36 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
37 * ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND OR
38 * M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM-
39 * AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
40 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
41 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
42 * OR PERFORMANCE OF THIS SOFTWARE.
43 */
44
45 /* ---------------------------- included header files ---------------------- */
46
47 #include "config.h"
48
49 #if HAVE_SYS_BSDTYPES_H
50 #include <sys/bsdtypes.h>
51 #endif
52
53 #include <stdio.h>
54 #include <unistd.h>
55 #include <assert.h>
56 #include <X11/Xatom.h>
57
58 #include "libs/ftime.h"
59 #include "libs/fvwmlib.h"
60 #include "libs/System.h"
61 #include "libs/Grab.h"
62 #include "libs/Parse.h"
63 #include "libs/ColorUtils.h"
64 #include "libs/FShape.h"
65 #include "libs/PictureBase.h"
66 #include "libs/Colorset.h"
67 #include "libs/charmap.h"
68 #include "libs/wcontext.h"
69 #include "fvwm.h"
70 #include "externs.h"
71 #include "cursor.h"
72 #include "functions.h"
73 #include "commands.h"
74 #include "bindings.h"
75 #include "misc.h"
76 #include "screen.h"
77 #include "events.h"
78 #include "eventhandler.h"
79 #include "eventmask.h"
80 #include "libs/fvwmsignal.h"
81 #include "module_list.h"
82 #include "module_interface.h"
83 #include "session.h"
84 #include "borders.h"
85 #include "frame.h"
86 #include "add_window.h"
87 #include "icccm2.h"
88 #include "icons.h"
89 #include "ewmh.h"
90 #include "update.h"
91 #include "style.h"
92 #include "stack.h"
93 #include "geometry.h"
94 #include "focus.h"
95 #include "virtual.h"
96 #include "decorations.h"
97 #include "schedule.h"
98 #include "menus.h"
99 #include "colormaps.h"
100 #include "colorset.h"
101 #ifdef HAVE_STROKE
102 #include "stroke.h"
103 #endif /* HAVE_STROKE */
104
105 /* ---------------------------- local definitions -------------------------- */
106
107 #ifndef XUrgencyHint
108 #define XUrgencyHint (1L << 8)
109 #endif
110
111 #define CR_MOVERESIZE_MASK (CWX | CWY | CWWidth | CWHeight | CWBorderWidth)
112
113 #define DEBUG_GLOBALLY_ACTIVE 1
114
115 #define MAX_NUM_WEED_EVENT_TYPES 40
116
117 /* ---------------------------- local macros ------------------------------- */
118
119 /* ---------------------------- imports ------------------------------------ */
120
121 extern void StartupStuff(void);
122
123 /* ---------------------------- included code files ------------------------ */
124
125 /* ---------------------------- local types -------------------------------- */
126
127 typedef void (*PFEH)(const evh_args_t *ea);
128
129 typedef struct
130 {
131 Window w;
132 Bool do_return_true;
133 Bool do_return_true_cr;
134 unsigned long cr_value_mask;
135 Bool ret_does_match;
136 unsigned long ret_type;
137 } check_if_event_args;
138
139 typedef struct
140 {
141 unsigned do_forbid_function : 1;
142 unsigned do_focus : 1;
143 unsigned do_swallow_click : 1;
144 unsigned do_raise : 1;
145 } hfrc_ret_t;
146
147 typedef struct event_group
148 {
149 int base;
150 int count;
151 PFEH *jump_table;
152 struct event_group *next;
153 } event_group_t;
154
155 typedef struct
156 {
157 int num_event_types;
158 int event_types[MAX_NUM_WEED_EVENT_TYPES];
159 } _weed_event_type_arg;
160
161 typedef struct
162 {
163 long event_mask;
164 } _weed_window_mask_events_arg;
165
166 typedef struct
167 {
168 Window w;
169 XEvent *last_cr_event;
170 int count;
171 } _merge_cr_args;
172
173 /* ---------------------------- forward declarations ----------------------- */
174
175 /* ---------------------------- local variables ---------------------------- */
176
177 static int Button = 0;
178 static const FvwmWindow *xcrossing_last_grab_window = NULL;
179 STROKE_CODE(static int send_motion);
180 STROKE_CODE(static char sequence[STROKE_MAX_SEQUENCE + 1]);
181 static event_group_t *base_event_group = NULL;
182
183 /* ---------------------------- exported variables (globals) --------------- */
184
185 int last_event_type = 0;
186 Window PressedW = None;
187
188 /* ---------------------------- local functions ---------------------------- */
189
fake_map_unmap_notify(const FvwmWindow * fw,int event_type)190 static void fake_map_unmap_notify(const FvwmWindow *fw, int event_type)
191 {
192 XEvent client_event;
193 XWindowAttributes winattrs = {0};
194
195 if (!XGetWindowAttributes(dpy, FW_W(fw), &winattrs))
196 {
197 return;
198 }
199 XSelectInput(
200 dpy, FW_W(fw),
201 winattrs.your_event_mask & ~StructureNotifyMask);
202 client_event.type = event_type;
203 client_event.xmap.display = dpy;
204 client_event.xmap.event = FW_W(fw);
205 client_event.xmap.window = FW_W(fw);
206 switch (event_type)
207 {
208 case MapNotify:
209 client_event.xmap.override_redirect = False;
210 break;
211 case UnmapNotify:
212 client_event.xunmap.from_configure = False;
213 break;
214 default:
215 /* not possible if called correctly */
216 break;
217 }
218 FSendEvent(
219 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
220 XSelectInput(dpy, FW_W(fw), winattrs.your_event_mask);
221 XFlush(dpy);
222
223 return;
224 }
225
_pred_weed_accumulate_expose(Display * display,XEvent * ev,XPointer arg)226 static int _pred_weed_accumulate_expose(
227 Display *display, XEvent *ev, XPointer arg)
228 {
229 XEvent *em = (XEvent *)arg;
230
231 if (ev->type != Expose)
232 {
233 return 0;
234 }
235 {
236 int x0;
237 int x1;
238
239 x1 = max(
240 ev->xexpose.x + ev->xexpose.width,
241 em->xexpose.x + em->xexpose.width);
242 x0 = min(em->xexpose.x, ev->xexpose.x);
243 em->xexpose.x = x0;
244 em->xexpose.width = x1 - x0;
245 }
246 {
247 int y0;
248 int y1;
249
250 y1 = max(
251 ev->xexpose.y + ev->xexpose.height,
252 em->xexpose.y + em->xexpose.height);
253 y0 = min(em->xexpose.y, ev->xexpose.y);
254 em->xexpose.y = y0;
255 em->xexpose.height = y1 - y0;
256 }
257
258 return 1;
259 }
260
_pred_weed_handle_expose(Display * display,XEvent * event,XPointer arg)261 static int _pred_weed_handle_expose(
262 Display *display, XEvent *event, XPointer arg)
263 {
264 if (event->type == Expose)
265 {
266 dispatch_event(event);
267 return 1;
268 }
269 else
270 {
271 return 0;
272 }
273 }
274
_pred_weed_event_type(Display * display,XEvent * event,XPointer arg)275 static int _pred_weed_event_type(
276 Display *display, XEvent *event, XPointer arg)
277 {
278 _weed_event_type_arg *args = (_weed_event_type_arg *)arg;
279 int i;
280
281 for (i = 0; i < args->num_event_types; i++)
282 {
283 if (event->type == args->event_types[i])
284 {
285 /* invalidate event and continue weeding */
286 return 1;
287 }
288 }
289
290 /* keep event and continue weeding */
291 return 0;
292 }
293
_pred_flush_property_notify_weed(Display * display,XEvent * event,XPointer arg)294 static int _pred_flush_property_notify_weed(
295 Display *display, XEvent *event, XPointer arg)
296 {
297 flush_property_notify_args *args =
298 (flush_property_notify_args *)arg;
299 int does_match_window;
300
301 does_match_window = (
302 FEV_HAS_EVENT_WINDOW(event->type) &&
303 event->xany.window == args->w) ? 1 : 0;
304 if (
305 does_match_window &&
306 event->type == args->event_type &&
307 event->xproperty.atom == args->atom)
308 {
309 /* invalidate event and continue weeding */
310 return 1;
311 }
312 else if (
313 args->do_stop_at_event_type &&
314 event->type == args->stop_at_event_type && (
315 !FEV_HAS_EVENT_WINDOW(args->stop_at_event_type) ||
316 does_match_window))
317 {
318 /* keep event and stop weeding */
319 return 2;
320 }
321
322 /* keep event and continue weeding */
323 return 0;
324 }
325
test_resizing_event(Display * display,XEvent * event,XPointer arg)326 static Bool test_resizing_event(
327 Display *display, XEvent *event, XPointer arg)
328 {
329 check_if_event_args *cie_args;
330 Bool rc;
331
332 cie_args = (check_if_event_args *)arg;
333 cie_args->ret_does_match = False;
334 if (event->xany.window != cie_args->w)
335 {
336 return False;
337 }
338 rc = False;
339 switch (event->type)
340 {
341 case ConfigureRequest:
342 if ((event->xconfigurerequest.value_mask &
343 cie_args->cr_value_mask) != 0)
344 {
345 cie_args->ret_type = ConfigureRequest;
346 cie_args->ret_does_match = True;
347 rc = cie_args->do_return_true_cr;
348 }
349 break;
350 case PropertyNotify:
351 if (event->xproperty.atom == XA_WM_NORMAL_HINTS)
352 {
353 cie_args->ret_type = PropertyNotify;
354 cie_args->ret_does_match = True;
355 rc = cie_args->do_return_true;
356 }
357 default:
358 break;
359 }
360
361 /* Yes, it is correct that this function may always returns False. */
362 return rc;
363 }
364
_handle_cr_on_unmanaged(XEvent * e)365 static inline void _handle_cr_on_unmanaged(XEvent *e)
366 {
367 XConfigureRequestEvent *cre = &e->xconfigurerequest;
368 XWindowChanges xwc;
369 unsigned long xwcm;
370
371 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
372 xwc.x = cre->x;
373 xwc.y = cre->y;
374 xwc.width = cre->width;
375 xwc.height = cre->height;
376 xwc.border_width = cre->border_width;
377 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
378
379 return;
380 }
381
_handle_cr_on_icon(XEvent * e,FvwmWindow * fw)382 static inline void _handle_cr_on_icon(XEvent *e, FvwmWindow *fw)
383 {
384 XConfigureRequestEvent *cre = &e->xconfigurerequest;
385 XWindowChanges xwc;
386 unsigned long xwcm;
387
388 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
389 xwc.x = cre->x;
390 xwc.y = cre->y;
391 xwc.width = cre->width;
392 xwc.height = cre->height;
393 xwc.border_width = cre->border_width;
394 if (FW_W_ICON_PIXMAP(fw) == cre->window)
395 {
396 int bw;
397
398 if (cre->value_mask & CWBorderWidth)
399 {
400 fw->icon_border_width = cre->border_width;
401 }
402 bw = fw->icon_border_width;
403 if ((cre->value_mask & (CWWidth | CWHeight)) ==
404 (CWWidth | CWHeight))
405 {
406 set_icon_picture_size(
407 fw, cre->width + 2 * bw, cre->height + 2 * bw);
408 }
409 }
410 set_icon_position(fw, cre->x, cre->y);
411 broadcast_icon_geometry(fw, False);
412 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
413 if (cre->window != FW_W_ICON_PIXMAP(fw) &&
414 FW_W_ICON_PIXMAP(fw) != None)
415 {
416 rectangle g;
417
418 get_icon_picture_geometry(fw, &g);
419 xwc.x = g.x;
420 xwc.y = g.y;
421 xwcm = cre->value_mask & (CWX | CWY);
422 XConfigureWindow(
423 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
424 }
425 if (FW_W_ICON_TITLE(fw) != None)
426 {
427 rectangle g;
428
429 get_icon_title_geometry(fw, &g);
430 xwc.x = g.x;
431 xwc.y = g.y;
432 xwcm = cre->value_mask & (CWX | CWY);
433 XConfigureWindow(
434 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
435 }
436
437 return;
438 }
439
_handle_cr_on_shaped(FvwmWindow * fw)440 static inline void _handle_cr_on_shaped(FvwmWindow *fw)
441 {
442 /* suppress compiler warnings w/o shape extension */
443 int i;
444 unsigned int u;
445 Bool b;
446 int boundingShaped;
447
448 SUPPRESS_UNUSED_VAR_WARNING(i);
449 SUPPRESS_UNUSED_VAR_WARNING(u);
450 SUPPRESS_UNUSED_VAR_WARNING(b);
451 if (FShapeQueryExtents(
452 dpy, FW_W(fw), &boundingShaped, &i, &i, &u, &u, &b,
453 &i, &i, &u, &u))
454 {
455 fw->wShaped = boundingShaped;
456 }
457 else
458 {
459 fw->wShaped = 0;
460 }
461
462 return;
463 }
464
_handle_cr_restack(int * ret_do_send_event,XEvent * e,FvwmWindow * fw)465 static inline void _handle_cr_restack(
466 int *ret_do_send_event, XEvent *e, FvwmWindow *fw)
467 {
468 XConfigureRequestEvent *cre = &e->xconfigurerequest;
469 XWindowChanges xwc;
470 unsigned long xwcm;
471 FvwmWindow *fw2 = NULL;
472
473 if (cre->value_mask & CWSibling)
474 {
475 if (XFindContext(
476 dpy, cre->above, FvwmContext,
477 (caddr_t *)&fw2) == XCNOENT)
478 {
479 fw2 = NULL;
480 }
481 if (fw2 == fw)
482 {
483 fw2 = NULL;
484 }
485 }
486 if (cre->detail != Above && cre->detail != Below)
487 {
488 HandleUnusualStackmodes(
489 cre->detail, fw, cre->window, fw2, cre->above);
490 }
491 /* only allow clients to restack windows within their layer */
492 else if (fw2 == NULL || compare_window_layers(fw2, fw) != 0)
493 {
494 switch (cre->detail)
495 {
496 case Above:
497 RaiseWindow(fw, True);
498 break;
499 case Below:
500 LowerWindow(fw, True);
501 break;
502 }
503 }
504 else
505 {
506 xwc.sibling = FW_W_FRAME(fw2);
507 xwc.stack_mode = cre->detail;
508 xwcm = CWSibling | CWStackMode;
509 XConfigureWindow(dpy, FW_W_FRAME(fw), xwcm, &xwc);
510
511 /* Maintain the condition that icon windows are stacked
512 * immediately below their frame
513 * 1. for fw */
514 xwc.sibling = FW_W_FRAME(fw);
515 xwc.stack_mode = Below;
516 xwcm = CWSibling | CWStackMode;
517 if (FW_W_ICON_TITLE(fw) != None)
518 {
519 XConfigureWindow(
520 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
521 }
522 if (FW_W_ICON_PIXMAP(fw) != None)
523 {
524 XConfigureWindow(
525 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
526 }
527 /* 2. for fw2 */
528 if (cre->detail == Below)
529 {
530 xwc.sibling = FW_W_FRAME(fw2);
531 xwc.stack_mode = Below;
532 xwcm = CWSibling | CWStackMode;
533 if (FW_W_ICON_TITLE(fw2) != None)
534 {
535 XConfigureWindow(
536 dpy, FW_W_ICON_TITLE(fw2), xwcm, &xwc);
537 }
538 if (FW_W_ICON_PIXMAP(fw2) != None)
539 {
540 XConfigureWindow(
541 dpy, FW_W_ICON_PIXMAP(fw2), xwcm,
542 &xwc);
543 }
544 }
545 /* Maintain the stacking order ring */
546 if (cre->detail == Above)
547 {
548 remove_window_from_stack_ring(fw);
549 add_window_to_stack_ring_after(
550 fw, get_prev_window_in_stack_ring(fw2));
551 }
552 else /* cre->detail == Below */
553 {
554 remove_window_from_stack_ring(fw);
555 add_window_to_stack_ring_after(fw, fw2);
556 }
557 BroadcastRestackThisWindow(fw);
558 }
559 /* srt (28-Apr-2001): Tk needs a ConfigureNotify event after a
560 * raise, otherwise it would hang for two seconds */
561 *ret_do_send_event = 1;
562
563 return;
564 }
565
_cr_get_static_position(rectangle * ret_g,FvwmWindow * fw,XEvent * e,size_borders * b)566 static inline void _cr_get_static_position(
567 rectangle *ret_g, FvwmWindow *fw, XEvent *e, size_borders *b)
568 {
569 XConfigureRequestEvent *cre = &e->xconfigurerequest;
570
571 if (cre->value_mask & CWX)
572 {
573 ret_g->x = cre->x - b->top_left.width;
574 }
575 else
576 {
577 ret_g->x = fw->g.frame.x;
578 }
579 if (cre->value_mask & CWY)
580 {
581 ret_g->y = cre->y - b->top_left.height;
582 }
583 else
584 {
585 ret_g->y = fw->g.frame.y;
586 }
587
588 return;
589 }
590
_cr_get_grav_position(rectangle * ret_g,FvwmWindow * fw,XEvent * e,size_borders * b)591 static inline void _cr_get_grav_position(
592 rectangle *ret_g, FvwmWindow *fw, XEvent *e, size_borders *b)
593 {
594 XConfigureRequestEvent *cre = &e->xconfigurerequest;
595 int grav_x;
596 int grav_y;
597
598 gravity_get_offsets(fw->hints.win_gravity, &grav_x, &grav_y);
599 if (cre->value_mask & CWX)
600 {
601 ret_g->x = cre->x - ((grav_x + 1) * b->total_size.width) / 2;
602 }
603 else
604 {
605 ret_g->x = fw->g.frame.x;
606 }
607 if (cre->value_mask & CWY)
608 {
609 ret_g->y = cre->y - ((grav_y + 1) * b->total_size.height) / 2;
610 }
611 else
612 {
613 ret_g->y = fw->g.frame.y;
614 }
615
616 return;
617 }
618
619 /* Try to detect whether the application uses the ICCCM way of moving its
620 * window or the traditional way, always assuming StaticGravity. */
_cr_detect_icccm_move(FvwmWindow * fw,XEvent * e,size_borders * b)621 static inline void _cr_detect_icccm_move(
622 FvwmWindow *fw, XEvent *e, size_borders *b)
623 {
624 XConfigureRequestEvent *cre = &e->xconfigurerequest;
625 rectangle grav_g;
626 rectangle static_g;
627 rectangle dg_g;
628 rectangle ds_g;
629 int mx;
630 int my;
631 int m;
632 int w;
633 int h;
634 int has_x;
635 int has_y;
636
637 if (CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
638 {
639 if (Scr.bo.do_debug_cr_motion_method == 1)
640 {
641 fprintf(
642 stderr,
643 "_cdim: --- already detected (pid %d) %p"
644 " '%s'\n", HAS_EWMH_WM_PID(fw), fw,
645 fw->visible_name);
646 }
647 return;
648 }
649 if (HAS_EWMH_WM_PID(fw))
650 {
651 if (Scr.bo.do_debug_cr_motion_method == 1)
652 {
653 fprintf(
654 stderr,"_cdim: +++ has ewmh_wm_pid: icccm"
655 " %p '%s'\n", fw, fw->visible_name);
656 }
657 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
658 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
659 return;
660 }
661 if (fw->ewmh_window_type != EWMH_WINDOW_TYPE_NONE_ID)
662 {
663 if (Scr.bo.do_debug_cr_motion_method == 1)
664 {
665 fprintf(
666 stderr, "_cdim: +++ has ewmh_window_type:"
667 " icccm %p '%s'\n", fw,
668 fw->visible_name);
669 }
670 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
671 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
672 return;
673 }
674 if (FShapesSupported && fw->wShaped)
675 {
676 if (Scr.bo.do_debug_cr_motion_method == 1)
677 {
678 fprintf(
679 stderr, "_cdim: --- shaped window %p "
680 "'%s'\n", fw, fw->visible_name);
681 }
682 /* no detection for shaped windows */
683 return;
684 }
685 if (fw->hints.win_gravity == StaticGravity)
686 {
687 if (Scr.bo.do_debug_cr_motion_method == 1)
688 {
689 fprintf(
690 stderr, "_cdim: --- using StaticGravity"
691 " %p '%s'\n", fw, fw->visible_name);
692 }
693 return;
694 }
695 has_x = (cre->value_mask & CWX);
696 has_y = (cre->value_mask & CWY);
697 if (!has_x && !has_y)
698 {
699 if (Scr.bo.do_debug_cr_motion_method == 1)
700 {
701 fprintf(
702 stderr, "_cdim: --- not moved %p '%s'\n",
703 fw, fw->visible_name);
704 }
705 return;
706 }
707 _cr_get_grav_position(&grav_g, fw, e, b);
708 _cr_get_static_position(&static_g, fw, e, b);
709 if (static_g.x == grav_g.x)
710 {
711 /* both methods have the same result; ignore */
712 has_x = 0;
713 }
714 if (static_g.y == grav_g.y)
715 {
716 /* both methods have the same result; ignore */
717 has_y = 0;
718 }
719 if (!has_x && !has_y)
720 {
721 if (Scr.bo.do_debug_cr_motion_method == 1)
722 {
723 fprintf(
724 stderr, "_cdim: --- not moved %p '%s'\n",
725 fw, fw->visible_name);
726 }
727 return;
728 }
729 dg_g.x = grav_g.x - fw->g.frame.x;
730 dg_g.y = grav_g.y - fw->g.frame.y;
731 ds_g.x = static_g.x - fw->g.frame.x;
732 ds_g.y = static_g.y - fw->g.frame.y;
733 if (Scr.bo.do_debug_cr_motion_method == 1)
734 {
735 fprintf(
736 stderr, "s %3d/%3d %2d/%2d, g %3d/%3d %2d/%2d: ",
737 static_g.x, static_g.y, ds_g.x, ds_g.y, grav_g.x,
738 grav_g.y, dg_g.x, dg_g.y);
739 }
740 /* check full screen */
741 if ((cre->value_mask & (CWX | CWY)) == (CWX | CWY) &&
742 (has_x || has_y) &&
743 cre->width == Scr.MyDisplayWidth &&
744 cre->height == Scr.MyDisplayHeight)
745 {
746 if (grav_g.x == -b->top_left.width &&
747 grav_g.y == -b->top_left.height)
748 {
749 /* Window is fullscreen using the ICCCM way. */
750 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
751 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
752 if (Scr.bo.do_debug_cr_motion_method == 1)
753 {
754 fprintf(
755 stderr, "+++ fullscreen icccm %p"
756 " '%s'\n", fw, fw->visible_name);
757 }
758 return;
759 }
760 else if (static_g.x == -b->top_left.width &&
761 static_g.y == -b->top_left.height)
762 {
763 /* Window is fullscreen using the traditional way. */
764 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
765 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
766 if (Scr.bo.do_debug_cr_motion_method == 1)
767 {
768 fprintf(
769 stderr, "+++ fullscreen traditional"
770 " %p '%s'\n", fw,
771 fw->visible_name);
772 }
773 return;
774 }
775 }
776 /* check travelling across the screen */
777 if (has_x && dg_g.x == 0 && ds_g.x != 0 &&
778 has_y && dg_g.y == 0 && ds_g.y != 0)
779 {
780 /* The traditional way causes a shift by the border width or
781 * height. Use ICCCM way. */
782 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
783 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
784 if (Scr.bo.do_debug_cr_motion_method == 1)
785 {
786 fprintf(
787 stderr, "+++ travelling icccm %p '%s'\n",
788 fw, fw->visible_name);
789 }
790 return;
791 }
792 if (has_x && dg_g.x != 0 && ds_g.x == 0 &&
793 has_y && dg_g.y != 0 && ds_g.y == 0)
794 {
795 /* The ICCCM way causes a shift by the border width or height.
796 * Use traditional way. */
797 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
798 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
799 if (Scr.bo.do_debug_cr_motion_method == 1)
800 {
801 fprintf(
802 stderr, "+++ travelling traditional %p"
803 " '%s'\n", fw, fw->visible_name);
804 }
805 return;
806 }
807 /* check placement near border */
808 w = (cre->value_mask & CWWidth) ?
809 cre->width + b->total_size.width : fw->g.frame.width;
810 h = (cre->value_mask & CWHeight) ?
811 cre->height + b->total_size.height : fw->g.frame.height;
812 if (!has_x)
813 {
814 mx = CR_MOTION_METHOD_AUTO;
815 }
816 else if (static_g.x == 0 || static_g.x + w == Scr.MyDisplayWidth)
817 {
818 mx = CR_MOTION_METHOD_STATIC_GRAV;
819 }
820 else if (grav_g.x == 0 || grav_g.x + w == Scr.MyDisplayWidth)
821 {
822 mx = CR_MOTION_METHOD_USE_GRAV;
823 }
824 else
825 {
826 mx = CR_MOTION_METHOD_AUTO;
827 }
828 if (!has_y)
829 {
830 my = CR_MOTION_METHOD_AUTO;
831 }
832 else if (static_g.y == 0 || static_g.y + h == Scr.MyDisplayHeight)
833 {
834 my = CR_MOTION_METHOD_STATIC_GRAV;
835 }
836 else if (grav_g.y == 0 || grav_g.y + h == Scr.MyDisplayHeight)
837 {
838 my = CR_MOTION_METHOD_USE_GRAV;
839 }
840 else
841 {
842 my = CR_MOTION_METHOD_AUTO;
843 }
844 m = (mx != CR_MOTION_METHOD_AUTO) ? mx : my;
845 if (m != CR_MOTION_METHOD_AUTO)
846 {
847 /* Window was placed next to the display border. */
848 if (m == my || my == CR_MOTION_METHOD_AUTO)
849 {
850 SET_CR_MOTION_METHOD(fw, m);
851 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
852 if (Scr.bo.do_debug_cr_motion_method == 1)
853 {
854 fprintf(
855 stderr, "+++ near border %s %p "
856 "'%s'\n", (m ==
857 CR_MOTION_METHOD_USE_GRAV)
858 ? "icccm" : "traditional", fw,
859 fw->visible_name);
860 }
861 return;
862 }
863 }
864 if (Scr.bo.do_debug_cr_motion_method == 1)
865 {
866 fprintf(
867 stderr, "--- not detected %p '%s'\n", fw,
868 fw->visible_name);
869 }
870
871 return;
872 }
873
874 /* This is not a good idea because this interferes with changes in the size
875 * hints of the window. However, it is impossible to be completely safe here.
876 * For example, if the client changes the size inc, then resizes the size of
877 * its window and then changes the size inc again - all in one batch - then
878 * the WM will read the *second* size inc upon the *first* event and use the
879 * wrong one in the ConfigureRequest calculations. */
880 /* dv (31 Mar 2002): The code now handles these situations, so enable it
881 * again. */
_pred_merge_cr(Display * display,XEvent * event,XPointer arg)882 static int _pred_merge_cr(Display *display, XEvent *event, XPointer arg)
883 {
884 _merge_cr_args *args = (_merge_cr_args *)arg;
885
886 switch (event->type)
887 {
888 case ConfigureRequest:
889 {
890 XConfigureRequestEvent *ecr = &event->xconfigurerequest;
891 XConfigureRequestEvent *lcr =
892 &args->last_cr_event->xconfigurerequest;
893
894 if (event->xconfigurerequest.window != args->w)
895 {
896 /* no match, keep looking */
897 return 0;
898 }
899 /* collect the size/position changes */
900 if (lcr->value_mask & CWX)
901 {
902 ecr->x = lcr->x;
903 }
904 if (lcr->value_mask & CWY)
905 {
906 ecr->y = lcr->y;
907 }
908 if (lcr->value_mask & CWWidth)
909 {
910 ecr->width = lcr->width;
911 }
912 if (lcr->value_mask & CWHeight)
913 {
914 ecr->height = lcr->height;
915 }
916 if (lcr->value_mask & CWBorderWidth)
917 {
918 ecr->border_width = lcr->border_width;
919 }
920 /* add to new event and remove from old event */
921 ecr->value_mask |= (lcr->value_mask & CR_MOVERESIZE_MASK);
922 lcr->value_mask &= ~CR_MOVERESIZE_MASK;
923 if (lcr->value_mask == 0)
924 {
925 /* The event has no useful contents anymore. */
926 FEV_INVALIDATE_EVENT(args->last_cr_event);
927 }
928 args->last_cr_event = event;
929 args->count++;
930
931 /* don't drop the current event and continue weeding */
932 return 0;
933 }
934 case PropertyNotify:
935 if (
936 event->xproperty.window == args->w &&
937 event->xproperty.atom != XA_WM_NORMAL_HINTS)
938 {
939 /* ConfigureRequest events cannot be merged past
940 * changes of the size hints. */
941 /* don't merge and stop weeding */
942 return 2;
943 }
944 else
945 {
946 /* PropertyNotify for another window, or the changed
947 * property does not interfere with merging. */
948 /* keep looking */
949 return 0;
950 }
951 default:
952 /* Other events do not interfer with merging. */
953 /* keep looking */
954 return 0;
955 }
956 }
957
_merge_cr_moveresize(const evh_args_t * ea,XEvent * ev,FvwmWindow * fw,size_borders * b)958 static inline int _merge_cr_moveresize(
959 const evh_args_t *ea, XEvent *ev, FvwmWindow *fw, size_borders *b)
960 {
961 _merge_cr_args args;
962
963 memset(&args, 0, sizeof(args));
964 args.w = ev->xconfigurerequest.window;
965 args.last_cr_event = ev;
966 FWeedIfEvents(dpy, _pred_merge_cr, (XPointer)&args);
967
968 #if 1 /*!!!*/
969 if (args.count > 0)
970 {
971 fprintf(stderr, "%s: merged %d cr events\n", __func__, args.count);
972 }
973 #endif
974 /* use the count from the structure, not the return value of
975 * FWeedIfEvents() because the predicate has a different way of weeding
976 * and the return value is always zero. */
977 return args.count;
978 }
979
_handle_cr_on_client(int * ret_do_send_event,XEvent * e,const evh_args_t * ea,FvwmWindow * fw,Bool force,int force_gravity)980 static inline int _handle_cr_on_client(
981 int *ret_do_send_event, XEvent *e, const evh_args_t *ea,
982 FvwmWindow *fw, Bool force, int force_gravity)
983 {
984 XConfigureRequestEvent *cre = &e->xconfigurerequest;
985 rectangle current_g;
986 rectangle new_g;
987 rectangle d_g;
988 size_rect constr_dim;
989 size_rect oldnew_dim;
990 size_borders b;
991 int gravity;
992
993 if (ea)
994 {
995 cre = &ea->exc->x.etrigger->xconfigurerequest;
996 }
997 if (cre->value_mask & CWBorderWidth)
998 {
999 /* for restoring */
1000 fw->attr_backup.border_width = cre->border_width;
1001 }
1002 if ((cre->value_mask & (CWWidth | CWHeight | CWX | CWY)) == 0)
1003 {
1004 return 0;
1005 }
1006
1007 get_window_borders(fw, &b);
1008 /* Merge all pending ConfigureRequests for the window into a single
1009 * event. However, we can not do this if the window uses the motion
1010 * method autodetection because the merged event might confuse the
1011 * detection code. */
1012 if (ea && CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
1013 {
1014 int count;
1015
1016 count = _merge_cr_moveresize(ea, e, fw, &b);
1017 if (count > 0)
1018 {
1019 /* the event has been merged into a later one, do
1020 * nothing */
1021 return 0;
1022 }
1023 }
1024 #if 0
1025 fprintf(stderr,
1026 "cre: %d(%d) %d(%d) %d(%d)x%d(%d) fw 0x%08x w 0x%08x "
1027 "ew 0x%08x '%s'\n",
1028 cre->x, (int)(cre->value_mask & CWX),
1029 cre->y, (int)(cre->value_mask & CWY),
1030 cre->width, (int)(cre->value_mask & CWWidth),
1031 cre->height, (int)(cre->value_mask & CWHeight),
1032 (int)FW_W_FRAME(fw), (int)FW_W(fw), (int)cre->window,
1033 (fw->name.name) ? fw->name.name : "");
1034 #endif
1035 /* Don't modify frame_g fields before calling SetupWindow! */
1036 memset(&d_g, 0, sizeof(d_g));
1037
1038 if (HAS_NEW_WM_NORMAL_HINTS(fw))
1039 {
1040 /* get the latest size hints */
1041 XSync(dpy, 0);
1042 GetWindowSizeHints(fw);
1043 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
1044 }
1045 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMaxSize))
1046 {
1047 /* Java workaround */
1048 if (cre->height > fw->hints.max_height &&
1049 fw->hints.max_height <= BROKEN_MAXSIZE_LIMIT)
1050 {
1051 fw->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
1052 cre->value_mask |= CWHeight;
1053 }
1054 if (cre->width > fw->hints.max_width &&
1055 fw->hints.max_width <= BROKEN_MAXSIZE_LIMIT)
1056 {
1057 fw->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
1058 cre->value_mask |= CWWidth;
1059 }
1060 }
1061 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMinSize))
1062 {
1063 if (cre->width < fw->hints.min_width &&
1064 fw->hints.min_width >= BROKEN_MINSIZE_LIMIT)
1065 {
1066 fw->hints.min_width = 1;
1067 cre->value_mask |= CWWidth;
1068 }
1069 if (cre->height < fw->hints.min_height &&
1070 fw->hints.min_height >= BROKEN_MINSIZE_LIMIT)
1071 {
1072 fw->hints.min_height = 1;
1073 cre->value_mask |= CWHeight;
1074 }
1075 }
1076 if (IS_SHADED(fw) ||
1077 !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM, False))
1078 {
1079 /* forbid shaded applications to move their windows */
1080 cre->value_mask &= ~(CWX | CWY);
1081 /* resend the old geometry */
1082 *ret_do_send_event = 1;
1083 }
1084 if (IS_MAXIMIZED(fw))
1085 {
1086 /* dont allow clients to resize maximized windows */
1087 cre->value_mask &= ~(CWWidth | CWHeight);
1088 /* resend the old geometry */
1089 *ret_do_send_event = 1;
1090 d_g.width = 0;
1091 d_g.height = 0;
1092 }
1093 else if (
1094 !is_function_allowed(
1095 F_RESIZE, NULL, fw, RQORIG_PROGRAM, False))
1096 {
1097 cre->value_mask &= ~(CWWidth | CWHeight);
1098 *ret_do_send_event = 1;
1099 }
1100
1101 if (!force && CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO)
1102 {
1103 _cr_detect_icccm_move(fw, e, &b);
1104 }
1105 if (force_gravity > ForgetGravity && force_gravity <= StaticGravity)
1106 {
1107 gravity = force_gravity;
1108 }
1109 else
1110 {
1111 gravity = fw->hints.win_gravity;
1112 }
1113 if (IS_SHADED(fw))
1114 {
1115 direction_t gravity_dir;
1116
1117 get_unshaded_geometry(fw, ¤t_g);
1118 /* the shade direction overrides the window's gravity */
1119 gravity_dir = gravity_grav_to_dir(gravity);
1120 gravity_dir = gravity_override_dir(
1121 gravity_dir, SHADED_DIR(fw));
1122 gravity = gravity_dir_to_grav(gravity_dir);
1123 }
1124 else
1125 {
1126 current_g = fw->g.frame;
1127 }
1128 if (!(cre->value_mask & (CWX | CWY)))
1129 {
1130 /* nothing */
1131 }
1132 else if ((force ||
1133 CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_USE_GRAV) &&
1134 gravity != StaticGravity)
1135 {
1136 int ref_x;
1137 int ref_y;
1138 int grav_x;
1139 int grav_y;
1140
1141 gravity_get_offsets(gravity, &grav_x, &grav_y);
1142 if (cre->value_mask & CWX)
1143 {
1144 ref_x = cre->x -
1145 ((grav_x + 1) * b.total_size.width) / 2;
1146 d_g.x = ref_x - current_g.x;
1147 }
1148 if (cre->value_mask & CWY)
1149 {
1150 ref_y = cre->y -
1151 ((grav_y + 1) * b.total_size.height) / 2;
1152 d_g.y = ref_y - current_g.y;
1153 }
1154 }
1155 else /* ..._USE_GRAV or ..._AUTO */
1156 {
1157 /* default: traditional cr handling */
1158 if (cre->value_mask & CWX)
1159 {
1160 d_g.x = cre->x - current_g.x - b.top_left.width;
1161 }
1162 if (cre->value_mask & CWY)
1163 {
1164 d_g.y = cre->y - current_g.y - b.top_left.height;
1165 }
1166 }
1167
1168 if (cre->value_mask & CWHeight)
1169 {
1170 if (cre->height <
1171 (WINDOW_FREAKED_OUT_SIZE - b.total_size.height))
1172 {
1173 d_g.height = cre->height -
1174 (current_g.height - b.total_size.height);
1175 }
1176 else
1177 {
1178 /* Ignore height changes to astronomically large
1179 * windows (needed for XEmacs 20.4); don't care if the
1180 * window is shaded here - we won't use 'height' in
1181 * this case anyway.
1182 * Inform the buggy app about the size that *we* want
1183 */
1184 d_g.height = 0;
1185 *ret_do_send_event = 1;
1186 }
1187 }
1188 if (cre->value_mask & CWWidth)
1189 {
1190 if (cre->width < (WINDOW_FREAKED_OUT_SIZE - b.total_size.width))
1191 {
1192 d_g.width = cre->width -
1193 (current_g.width - b.total_size.width);
1194 }
1195 else
1196 {
1197 d_g.width = 0;
1198 *ret_do_send_event = 1;
1199 }
1200 }
1201
1202 /* SetupWindow (x,y) are the location of the upper-left outer corner
1203 * and are passed directly to XMoveResizeWindow (frame). The
1204 * (width,height) are the inner size of the frame. The inner width is
1205 * the same as the requested client window width; the inner height is
1206 * the same as the requested client window height plus any title bar
1207 * slop. */
1208 new_g = current_g;
1209 oldnew_dim.width = new_g.width + d_g.width;
1210 oldnew_dim.height = new_g.height + d_g.height;
1211 constr_dim.width = oldnew_dim.width;
1212 constr_dim.height = oldnew_dim.height;
1213 constrain_size(
1214 fw, NULL, &constr_dim.width, &constr_dim.height, 0, 0,
1215 CS_UPDATE_MAX_DEFECT);
1216 d_g.width += (constr_dim.width - oldnew_dim.width);
1217 d_g.height += (constr_dim.height - oldnew_dim.height);
1218 if ((cre->value_mask & CWX) && d_g.width)
1219 {
1220 new_g.x = current_g.x + d_g.x;
1221 new_g.width = current_g.width + d_g.width;
1222 }
1223 else if ((cre->value_mask & CWX) && !d_g.width)
1224 {
1225 new_g.x = current_g.x + d_g.x;
1226 }
1227 else if (!(cre->value_mask & CWX) && d_g.width)
1228 {
1229 gravity_resize(gravity, &new_g, d_g.width, 0);
1230 }
1231 if ((cre->value_mask & CWY) && d_g.height)
1232 {
1233 new_g.y = current_g.y + d_g.y;
1234 new_g.height = current_g.height + d_g.height;
1235 }
1236 else if ((cre->value_mask & CWY) && !d_g.height)
1237 {
1238 new_g.y = current_g.y + d_g.y;
1239 }
1240 else if (!(cre->value_mask & CWY) && d_g.height)
1241 {
1242 gravity_resize(gravity, &new_g, 0, d_g.height);
1243 }
1244
1245 if (new_g.x == current_g.x && new_g.y == current_g.y &&
1246 new_g.width == current_g.width &&
1247 new_g.height == current_g.height)
1248 {
1249 /* Window will not be moved or resized; send a synthetic
1250 * ConfigureNotify. */
1251 *ret_do_send_event = 1;
1252 }
1253 else if ((cre->value_mask & CWX) || (cre->value_mask & CWY) ||
1254 d_g.width || d_g.height)
1255 {
1256 if (IS_SHADED(fw))
1257 {
1258 fw->g.normal = new_g;
1259 get_shaded_geometry(fw, &new_g, &new_g);
1260 }
1261 frame_setup_window_app_request(
1262 fw, new_g.x, new_g.y, new_g.width, new_g.height,
1263 False);
1264 /* make sure the window structure has the new position */
1265 update_absolute_geometry(fw);
1266 maximize_adjust_offset(fw);
1267 }
1268 else if (DO_FORCE_NEXT_CR(fw))
1269 {
1270 *ret_do_send_event = 1;
1271 }
1272 SET_FORCE_NEXT_CR(fw, 0);
1273 SET_FORCE_NEXT_PN(fw, 0);
1274
1275 return 1;
1276 }
1277
_handle_configure_request(XEvent * e,const evh_args_t * ea,FvwmWindow * fw,Bool force,int force_gravity)1278 void _handle_configure_request(
1279 XEvent *e, const evh_args_t *ea, FvwmWindow *fw,
1280 Bool force, int force_gravity)
1281 {
1282 XConfigureRequestEvent *cre = &e->xconfigurerequest;
1283 int do_send_event = 0;
1284 int cn_count = 0;
1285
1286 fev_sanitise_configure_request(cre);
1287 /* According to the July 27, 1988 ICCCM draft, we should ignore size
1288 * and position fields in the WM_NORMAL_HINTS property when we map a
1289 * window. Instead, we'll read the current geometry. Therefore, we
1290 * should respond to configuration requests for windows which have
1291 * never been mapped. */
1292 if (fw == NULL)
1293 {
1294 _handle_cr_on_unmanaged(e);
1295 return;
1296 }
1297 if (cre->window == FW_W_ICON_TITLE(fw) ||
1298 cre->window == FW_W_ICON_PIXMAP(fw))
1299 {
1300 _handle_cr_on_icon(e, fw);
1301 }
1302 if (FShapesSupported)
1303 {
1304 _handle_cr_on_shaped(fw);
1305 }
1306 if (fw != NULL && cre->window == FW_W(fw))
1307 {
1308 cn_count = _handle_cr_on_client(
1309 &do_send_event, e, ea, fw, force, force_gravity);
1310 }
1311 /* Stacking order change requested. Handle this *after* geometry
1312 * changes, since we need the new geometry in occlusion calculations */
1313 if ((cre->value_mask & CWStackMode) &&
1314 (!DO_IGNORE_RESTACK(fw) || force))
1315 {
1316 _handle_cr_restack(&do_send_event, e, fw);
1317 }
1318 #if 1
1319 /* This causes some ddd windows not to be drawn properly. Reverted back
1320 * to the old method in frame_setup_window. */
1321 /* domivogt (15-Oct-1999): enabled this to work around buggy apps that
1322 * ask for a nonsense height and expect that they really get it. */
1323 if (cn_count == 0 && do_send_event)
1324 {
1325 cn_count = 1;
1326 }
1327 else if (cn_count > 0)
1328 {
1329 do_send_event = 1;
1330 }
1331 for ( ; cn_count > 0; cn_count--)
1332 {
1333 SendConfigureNotify(
1334 fw, fw->g.frame.x, fw->g.frame.y, fw->g.frame.width,
1335 fw->g.frame.height, 0, True);
1336 }
1337 if (do_send_event)
1338 {
1339 XFlush(dpy);
1340 }
1341 #endif
1342
1343 return;
1344 }
1345
_pred_button_click(Display * display,XEvent * event,XPointer arg)1346 static Bool _pred_button_click(
1347 Display *display, XEvent *event, XPointer arg)
1348 {
1349 if (event->type == ButtonPress || event->type == ButtonRelease)
1350 {
1351 return True;
1352 }
1353
1354 return False;
1355 }
1356
1357 /* Helper function for __handle_focus_raise_click(). */
__test_for_motion(int x0,int y0)1358 static Bool __test_for_motion(int x0, int y0)
1359 {
1360 int x;
1361 int y;
1362 unsigned int mask;
1363 XEvent e;
1364
1365 /* Query the pointer to do this. We can't check for events here since
1366 * the events are still needed if the pointer moves. */
1367
1368 /* However, some special mouse (e.g., a touchpad with the
1369 * synaptic driver) may handle a double click in a special way
1370 * (for dragging through short touching and holding down the
1371 * finger on the touchpad). Bascially, when you execute a
1372 * double click the first button release is queued after the
1373 * second _physical_ mouse release happen. It seems that
1374 * FQueryPointer may not work as expected: it does not see
1375 * that the button is released on a double click. So, we need
1376 * to check for a button press in the future to avoid a fvwm
1377 * lockup! (olicha 2004-01-31) */
1378
1379 for (x = x0, y = y0; FQueryPointer(
1380 dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
1381 &x, &y, &mask) == True; usleep(20000))
1382 {
1383 if ((mask & DEFAULT_ALL_BUTTONS_MASK) == 0)
1384 {
1385 /* all buttons are released */
1386 return False;
1387 }
1388 else if (abs(x - x0) >= Scr.MoveThreshold ||
1389 abs(y - y0) >= Scr.MoveThreshold)
1390 {
1391 /* the pointer has moved */
1392 return True;
1393 }
1394 if (FCheckPeekIfEvent(dpy, &e, _pred_button_click, NULL))
1395 {
1396 /* click in the future */
1397 return False;
1398 }
1399 else
1400 {
1401 /* The predicate procedure finds no match, no event
1402 * has been removed from the queue and XFlush was
1403 * called. Nothing to do */
1404 }
1405 }
1406
1407 /* pointer has moved off screen */
1408 return True;
1409 }
1410
1411 /* Helper function for __handle_focus_raise_click(). */
__check_click_to_focus_or_raise(hfrc_ret_t * ret_args,const exec_context_t * exc)1412 static void __check_click_to_focus_or_raise(
1413 hfrc_ret_t *ret_args, const exec_context_t *exc)
1414 {
1415 FvwmWindow * const fw = exc->w.fw;
1416 const XEvent *te = exc->x.etrigger;
1417 struct
1418 {
1419 unsigned is_client_click : 1;
1420 unsigned is_focused : 1;
1421 } f;
1422
1423 f.is_focused = !!focus_is_focused(fw);
1424 f.is_client_click = (exc->w.wcontext == C_WINDOW ||
1425 exc->w.wcontext == C_EWMH_DESKTOP);
1426 /* check if we need to raise and/or focus the window */
1427 ret_args->do_focus = focus_query_click_to_focus(fw, exc->w.wcontext);
1428 if (f.is_client_click && !ret_args->do_focus &&
1429 !f.is_focused && FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
1430 !fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
1431 {
1432 /* Give the window a chance to to take focus itself */
1433 ret_args->do_focus = 1;
1434 }
1435 if (ret_args->do_focus && focus_is_focused(fw))
1436 {
1437 ret_args->do_focus = 0;
1438 }
1439 ret_args->do_raise =
1440 focus_query_click_to_raise(fw, f.is_focused, exc->w.wcontext);
1441 #define EXPERIMENTAL_ROU_HANDLING_V2
1442 #ifdef EXPERIMENTAL_ROU_HANDLING_V2
1443 /* RBW -- Dang! This works without the one in HandleEnterNotify! */
1444 if (ret_args->do_raise && is_on_top_of_layer_and_above_unmanaged(fw))
1445 #else
1446 if (ret_args->do_raise && is_on_top_of_layer(fw))
1447 #endif
1448 {
1449 ret_args->do_raise = 0;
1450 }
1451 if ((ret_args->do_focus &&
1452 FP_DO_IGNORE_FOCUS_CLICK_MOTION(FW_FOCUS_POLICY(fw))) ||
1453 (ret_args->do_raise &&
1454 FP_DO_IGNORE_RAISE_CLICK_MOTION(FW_FOCUS_POLICY(fw))))
1455 {
1456 /* Pass further events to the application and check if a button
1457 * release or motion event occurs next. If we don't do this
1458 * here, the pointer will seem to be frozen in
1459 * __test_for_motion(). */
1460 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1461 if (__test_for_motion(te->xbutton.x_root, te->xbutton.y_root))
1462 {
1463 /* the pointer was moved, process event normally */
1464 ret_args->do_focus = 0;
1465 ret_args->do_raise = 0;
1466 }
1467 }
1468 if (ret_args->do_focus || ret_args->do_raise)
1469 {
1470 if (!((ret_args->do_focus &&
1471 FP_DO_ALLOW_FUNC_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1472 (ret_args->do_raise &&
1473 FP_DO_ALLOW_FUNC_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1474 {
1475 ret_args->do_forbid_function = 1;
1476 }
1477 if (!((ret_args->do_focus &&
1478 FP_DO_PASS_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1479 (ret_args->do_raise &&
1480 FP_DO_PASS_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1481 {
1482 ret_args->do_swallow_click = 1;
1483 }
1484 }
1485
1486 return;
1487 }
1488
1489 /* Finds out if the click on a window must be used to focus or raise it. */
__handle_focus_raise_click(hfrc_ret_t * ret_args,const exec_context_t * exc)1490 static void __handle_focus_raise_click(
1491 hfrc_ret_t *ret_args, const exec_context_t *exc)
1492 {
1493 memset(ret_args, 0, sizeof(*ret_args));
1494 if (exc->w.fw == NULL)
1495 {
1496 return;
1497 }
1498 /* check for proper click button and modifiers*/
1499 if (FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) != 0 &&
1500 !(FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) &
1501 (1 << (exc->x.etrigger->xbutton.button - 1))))
1502 {
1503 /* wrong button, handle click normally */
1504 return;
1505 }
1506 else if (FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw)) !=
1507 FPOL_ANY_MODIFIER &&
1508 MaskUsedModifiers(
1509 FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw))) !=
1510 MaskUsedModifiers(exc->x.etrigger->xbutton.state))
1511 {
1512 /* right button but wrong modifiers, handle click normally */
1513 return;
1514 }
1515 else
1516 {
1517 __check_click_to_focus_or_raise(ret_args, exc);
1518 }
1519
1520 return;
1521 }
1522
1523 /* Helper function for HandleButtonPress */
__is_bpress_window_handled(const exec_context_t * exc)1524 static Bool __is_bpress_window_handled(const exec_context_t *exc)
1525 {
1526 Window eventw;
1527 const XEvent *te = exc->x.etrigger;
1528
1529 if (exc->w.fw == NULL)
1530 {
1531 if ((te->xbutton.window != Scr.Root ||
1532 te->xbutton.subwindow != None) &&
1533 !is_pan_frame(te->xbutton.window))
1534 {
1535 /* Ignore events in unmanaged windows or subwindows of
1536 * a client */
1537 return False;
1538 }
1539 else
1540 {
1541 return True;
1542 }
1543 }
1544 eventw = (te->xbutton.subwindow != None &&
1545 te->xany.window != FW_W(exc->w.fw)) ?
1546 te->xbutton.subwindow : te->xany.window;
1547 if (is_frame_hide_window(eventw) || eventw == FW_W_FRAME(exc->w.fw))
1548 {
1549 return False;
1550 }
1551 if (!XGetGeometry(
1552 dpy, eventw, &JunkRoot, &JunkX, &JunkY,
1553 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
1554 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1555 {
1556 /* The window has already died. */
1557 return False;
1558 }
1559
1560 return True;
1561 }
1562
1563 /* Helper function for __handle_bpress_on_managed */
__handle_click_to_focus(const exec_context_t * exc)1564 static Bool __handle_click_to_focus(const exec_context_t *exc)
1565 {
1566 fpol_set_focus_by_t set_by;
1567
1568 switch (exc->w.wcontext)
1569 {
1570 case C_WINDOW:
1571 case C_EWMH_DESKTOP:
1572 set_by = FOCUS_SET_BY_CLICK_CLIENT;
1573 break;
1574 case C_ICON:
1575 set_by = FOCUS_SET_BY_CLICK_ICON;
1576 break;
1577 default:
1578 set_by = FOCUS_SET_BY_CLICK_DECOR;
1579 break;
1580 }
1581 SetFocusWindow(exc->w.fw, True, set_by);
1582 focus_grab_buttons(exc->w.fw);
1583 if (focus_is_focused(exc->w.fw) && !IS_ICONIFIED(exc->w.fw))
1584 {
1585 border_draw_decorations(
1586 exc->w.fw, PART_ALL, True, True, CLEAR_ALL, NULL,
1587 NULL);
1588 }
1589
1590 return focus_is_focused(exc->w.fw);
1591 }
1592
1593 /* Helper function for __handle_bpress_on_managed */
__handle_click_to_raise(const exec_context_t * exc)1594 static Bool __handle_click_to_raise(const exec_context_t *exc)
1595 {
1596 Bool rc = False;
1597 int is_focused;
1598
1599 is_focused = focus_is_focused(exc->w.fw);
1600 if (focus_query_click_to_raise(exc->w.fw, is_focused, True))
1601 {
1602 rc = True;
1603 }
1604
1605 return rc;
1606 }
1607
1608 /* Helper function for HandleButtonPress */
__handle_bpress_stroke(void)1609 static void __handle_bpress_stroke(void)
1610 {
1611 STROKE_CODE(stroke_init());
1612 STROKE_CODE(send_motion = True);
1613
1614 return;
1615 }
1616
1617 /* Helper function for __handle_bpress_on_managed */
__handle_bpress_action(const exec_context_t * exc,char * action)1618 static Bool __handle_bpress_action(
1619 const exec_context_t *exc, char *action)
1620 {
1621 window_parts part;
1622 Bool do_force;
1623 Bool rc = False;
1624
1625 if (!action || *action == 0)
1626 {
1627 PressedW = None;
1628 return False;
1629 }
1630 /* draw pressed in decorations */
1631 part = border_context_to_parts(exc->w.wcontext);
1632 do_force = (part & PART_TITLEBAR) ? True : False;
1633 border_draw_decorations(
1634 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1635 CLEAR_ALL, NULL, NULL);
1636 /* execute the action */
1637 if (IS_ICONIFIED(exc->w.fw))
1638 {
1639 /* release the pointer since it can't do harm over an icon */
1640 XAllowEvents(dpy, AsyncPointer, CurrentTime);
1641 }
1642 execute_function(NULL, exc, action, 0);
1643 if (exc->w.wcontext != C_WINDOW && exc->w.wcontext != C_NO_CONTEXT)
1644 {
1645 WaitForButtonsUp(True);
1646 rc = True;
1647 }
1648 /* redraw decorations */
1649 PressedW = None;
1650 if (check_if_fvwm_window_exists(exc->w.fw))
1651 {
1652 part = border_context_to_parts(exc->w.wcontext);
1653 do_force = (part & PART_TITLEBAR) ? True : False;
1654 border_draw_decorations(
1655 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1656 CLEAR_ALL, NULL, NULL);
1657 }
1658
1659 return rc;
1660 }
1661
1662 /* Handles button presses on the root window. */
__handle_bpress_on_root(const exec_context_t * exc)1663 static void __handle_bpress_on_root(const exec_context_t *exc)
1664 {
1665 char *action;
1666
1667 PressedW = None;
1668 __handle_bpress_stroke();
1669 /* search for an appropriate mouse binding */
1670 action = CheckBinding(
1671 Scr.AllBindings, STROKE_ARG(0) exc->x.etrigger->xbutton.button,
1672 exc->x.etrigger->xbutton.state, GetUnusedModifiers(), C_ROOT,
1673 BIND_BUTTONPRESS, NULL, NULL);
1674 if (action && *action)
1675 {
1676 const exec_context_t *exc2;
1677 exec_context_changes_t ecc;
1678
1679 ecc.w.wcontext = C_ROOT;
1680 exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
1681 execute_function(NULL, exc2, action, 0);
1682 exc_destroy_context(exc2);
1683 WaitForButtonsUp(True);
1684 }
1685
1686 return;
1687 }
1688
1689 /* Handles button presses on unmanaged windows */
__handle_bpress_on_unmanaged(const exec_context_t * exc)1690 static void __handle_bpress_on_unmanaged(const exec_context_t *exc)
1691 {
1692 /* Pass the event to the application. */
1693 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1694 XFlush(dpy);
1695
1696 return;
1697 }
1698
1699 /* Handles button presses on managed windows */
__handle_bpress_on_managed(const exec_context_t * exc)1700 static void __handle_bpress_on_managed(const exec_context_t *exc)
1701 {
1702 char *action;
1703 hfrc_ret_t f;
1704 FvwmWindow * const fw = exc->w.fw;
1705 XEvent *e;
1706
1707 e = exc->x.etrigger;
1708 /* Now handle click to focus and click to raise. */
1709 __handle_focus_raise_click(&f, exc);
1710 PressedW = (f.do_forbid_function) ? None : exc->w.w;
1711 if (f.do_focus)
1712 {
1713 if (!__handle_click_to_focus(exc))
1714 {
1715 /* Window didn't accept the focus; pass the click to
1716 * the application. */
1717 f.do_swallow_click = 0;
1718 }
1719 }
1720 if (f.do_raise)
1721 {
1722 if (__handle_click_to_raise(exc) == True)
1723 {
1724 /* We can't raise the window immediately because the
1725 * action bound to the click might be "Lower" or
1726 * "RaiseLower". So mark the window as scheduled to be
1727 * raised after the binding is executed. Functions that
1728 * modify the stacking order will reset this flag. */
1729 SET_SCHEDULED_FOR_RAISE(fw, 1);
1730 }
1731 }
1732 /* handle bindings */
1733 if (!f.do_forbid_function)
1734 {
1735 /* stroke bindings */
1736 __handle_bpress_stroke();
1737 /* mouse bindings */
1738 action = CheckBinding(
1739 Scr.AllBindings, STROKE_ARG(0) e->xbutton.button,
1740 e->xbutton.state, GetUnusedModifiers(),
1741 exc->w.wcontext, BIND_BUTTONPRESS, &fw->class,
1742 fw->name.name);
1743 if (__handle_bpress_action(exc, action))
1744 {
1745 f.do_swallow_click = 1;
1746 }
1747 }
1748 /* raise the window */
1749 if (IS_SCHEDULED_FOR_RAISE(fw))
1750 {
1751 /* Now that we know the action did not restack the window we
1752 * can raise it.
1753 * dv (10-Aug-2002): We can safely raise the window after
1754 * redrawing it since all the decorations are drawn in the
1755 * window background and no Expose event is generated. */
1756 RaiseWindow(fw, False);
1757 SET_SCHEDULED_FOR_RAISE(fw, 0);
1758 }
1759 /* clean up */
1760 if (!f.do_swallow_click)
1761 {
1762 /* pass the click to the application */
1763 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1764 XFlush(dpy);
1765 }
1766 else if (f.do_focus || f.do_raise)
1767 {
1768 WaitForButtonsUp(True);
1769 }
1770
1771 return;
1772 }
1773
1774 /* restore focus stolen by unmanaged */
__refocus_stolen_focus_win(const evh_args_t * ea)1775 static void __refocus_stolen_focus_win(const evh_args_t *ea)
1776 {
1777 FOCUS_SET(Scr.StolenFocusWin, Scr.StolenFocusFvwmWin);
1778 ea->exc->x.etrigger->xfocus.window = Scr.StolenFocusWin;
1779 ea->exc->x.etrigger->type = FocusIn;
1780 Scr.UnknownWinFocused = None;
1781 Scr.StolenFocusWin = None;
1782 Scr.StolenFocusFvwmWin = NULL;
1783 dispatch_event(ea->exc->x.etrigger);
1784
1785 return;
1786 }
1787
1788 /* ---------------------------- event handlers ----------------------------- */
1789
HandleButtonPress(const evh_args_t * ea)1790 void HandleButtonPress(const evh_args_t *ea)
1791 {
1792 DBUG("HandleButtonPress", "Routine Entered");
1793
1794 GrabEm(CRS_NONE, GRAB_PASSIVE);
1795 if (__is_bpress_window_handled(ea->exc) == False)
1796 {
1797 __handle_bpress_on_unmanaged(ea->exc);
1798 }
1799 else if (ea->exc->w.fw != NULL)
1800 {
1801 __handle_bpress_on_managed(ea->exc);
1802 }
1803 else
1804 {
1805 __handle_bpress_on_root(ea->exc);
1806 }
1807 UngrabEm(GRAB_PASSIVE);
1808
1809 return;
1810 }
1811
1812 #ifdef HAVE_STROKE
HandleButtonRelease(const evh_args_t * ea)1813 void HandleButtonRelease(const evh_args_t *ea)
1814 {
1815 char *action;
1816 char *name;
1817 int real_modifier;
1818 const XEvent *te = ea->exc->x.etrigger;
1819 XClassHint *class;
1820
1821 DBUG("HandleButtonRelease", "Routine Entered");
1822 send_motion = False;
1823 stroke_trans (sequence);
1824 DBUG("HandleButtonRelease",sequence);
1825 /* Allows modifier to work (Only R context works here). */
1826 real_modifier = te->xbutton.state - (1 << (7 + te->xbutton.button));
1827 if (ea->exc->w.fw == NULL)
1828 {
1829 class = NULL;
1830 name = NULL;
1831 }
1832 else
1833 {
1834 class = &ea->exc->w.fw->class;
1835 name = ea->exc->w.fw->name.name;
1836 }
1837 /* need to search for an appropriate stroke binding */
1838 action = CheckBinding(
1839 Scr.AllBindings, sequence, te->xbutton.button, real_modifier,
1840 GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE,
1841 class, name);
1842 /* got a match, now process it */
1843 if (action != NULL && (action[0] != 0))
1844 {
1845 execute_function(NULL, ea->exc, action, 0);
1846 WaitForButtonsUp(True);
1847 }
1848
1849 return;
1850 }
1851 #endif /* HAVE_STROKE */
1852
HandleClientMessage(const evh_args_t * ea)1853 void HandleClientMessage(const evh_args_t *ea)
1854 {
1855 const XEvent *te = ea->exc->x.etrigger;
1856 FvwmWindow * const fw = ea->exc->w.fw;
1857
1858 DBUG("HandleClientMessage", "Routine Entered");
1859
1860 if (EWMH_ProcessClientMessage(ea->exc))
1861 {
1862 return;
1863 }
1864
1865 /* handle deletion of tear out menus */
1866 if (fw && IS_TEAR_OFF_MENU(fw) && te->xclient.format == 32 &&
1867 te->xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
1868 {
1869 menu_close_tear_off_menu(fw);
1870 return;
1871 }
1872
1873 if (te->xclient.message_type == _XA_WM_CHANGE_STATE &&
1874 fw && te->xclient.data.l[0] == IconicState && !IS_ICONIFIED(fw))
1875 {
1876 const exec_context_t *exc;
1877 exec_context_changes_t ecc;
1878
1879 ecc.w.wcontext = C_WINDOW;
1880 exc = exc_clone_context(ea->exc, &ecc, ECC_WCONTEXT);
1881 execute_function(NULL, exc, "Iconify", 0);
1882 exc_destroy_context(exc);
1883 return;
1884 }
1885
1886 /* FIXME: Is this safe enough ? I guess if clients behave
1887 * according to ICCCM and send these messages only if they
1888 * grabbed the pointer, it is OK */
1889 {
1890 extern Atom _XA_WM_COLORMAP_NOTIFY;
1891 if (te->xclient.message_type == _XA_WM_COLORMAP_NOTIFY)
1892 {
1893 set_client_controls_colormaps(te->xclient.data.l[1]);
1894 return;
1895 }
1896 }
1897
1898 /* CKH - if we get here, it was an unknown client message, so send
1899 * it to the client if it was in a window we know about. I'm not so
1900 * sure this should be done or not, since every other window manager
1901 * I've looked at doesn't. But it might be handy for a free drag and
1902 * drop setup being developed for Linux. */
1903 /* TA: 20091231 - But this confuses QT Drag and Drop since it handles
1904 * processing XSendEvents in an odd order. For now, workaround this
1905 * by using a BugOpts option.
1906 */
1907 if (fw)
1908 {
1909 if ((!Scr.bo.do_enable_qt_drag_n_drop_workaround) &&
1910 (te->xclient.window != FW_W(fw)))
1911 {
1912 XEvent e;
1913
1914 e = *te;
1915 e.xclient.window = FW_W(fw);
1916 FSendEvent(dpy, FW_W(fw), False, NoEventMask, &e);
1917 }
1918 }
1919 }
1920
HandleColormapNotify(const evh_args_t * ea)1921 void HandleColormapNotify(const evh_args_t *ea)
1922 {
1923 colormap_handle_colormap_notify(ea);
1924
1925 return;
1926 }
1927
HandleConfigureRequest(const evh_args_t * ea)1928 void HandleConfigureRequest(const evh_args_t *ea)
1929 {
1930 XEvent *te = ea->exc->x.etrigger;
1931 XConfigureRequestEvent *cre;
1932 FvwmWindow *fw = ea->exc->w.fw;
1933
1934 DBUG("HandleConfigureRequest", "Routine Entered");
1935
1936 cre = &te->xconfigurerequest;
1937 /* te->xany.window is te->.xconfigurerequest.parent, so the context
1938 * window may be wrong. */
1939 if (XFindContext(dpy, cre->window, FvwmContext, (caddr_t *)&fw) ==
1940 XCNOENT)
1941 {
1942 fw = NULL;
1943 }
1944 _handle_configure_request(te, ea, fw, False, ForgetGravity);
1945
1946 return;
1947 }
1948
HandleDestroyNotify(const evh_args_t * ea)1949 void HandleDestroyNotify(const evh_args_t *ea)
1950 {
1951 DBUG("HandleDestroyNotify", "Routine Entered");
1952
1953 destroy_window(ea->exc->w.fw);
1954 EWMH_ManageKdeSysTray(
1955 ea->exc->x.etrigger->xdestroywindow.window,
1956 ea->exc->x.etrigger->type);
1957 EWMH_WindowDestroyed();
1958
1959 return;
1960 }
1961
1962 #define DEBUG_ENTERNOTIFY 0
1963 #if DEBUG_ENTERNOTIFY
1964 static int ecount=0;
1965 #define ENTER_DBG(x) fprintf x;
1966 #else
1967 #define ENTER_DBG(x)
1968 #endif
HandleEnterNotify(const evh_args_t * ea)1969 void HandleEnterNotify(const evh_args_t *ea)
1970 {
1971 const XEnterWindowEvent *ewp;
1972 XEvent d;
1973 FvwmWindow *sf;
1974 static Bool is_initial_ungrab_pending = True;
1975 Bool is_tear_off_menu;
1976 const XEvent *te = ea->exc->x.etrigger;
1977 FvwmWindow * const fw = ea->exc->w.fw;
1978
1979 DBUG("HandleEnterNotify", "Routine Entered");
1980 ewp = &te->xcrossing;
1981 ENTER_DBG((stderr, "++++++++ en (%d): fw %p w 0x%08x sw 0x%08x mode 0x%x detail 0x%x '%s'\n", ++ecount, fw, (int)ewp->window, (int)ewp->subwindow, ewp->mode, ewp->detail, fw?fw->visible_name:"(none)"));
1982
1983 if (
1984 ewp->window == Scr.Root &&
1985 ewp->detail == NotifyInferior && ewp->mode == NotifyNormal)
1986 {
1987 /* pointer left subwindow */
1988 BroadcastPacket(
1989 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1990 (long)NULL);
1991 }
1992 else if (
1993 ewp->window == Scr.Root &&
1994 ewp->detail == NotifyNonlinearVirtual)
1995 {
1996 /* pointer entered screen */
1997 BroadcastPacket(
1998 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1999 (long)NULL);
2000 }
2001 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2002 {
2003 if (fw && !IS_ICONIFIED(fw) && ewp->window == FW_W(fw))
2004 {
2005 InstallWindowColormaps(fw);
2006 }
2007 else
2008 {
2009 /* make sure its for one of our windows */
2010 /* handle a subwindow cmap */
2011 InstallWindowColormaps(NULL);
2012 }
2013 }
2014 else if (!fw)
2015 {
2016 EnterSubWindowColormap(ewp->window);
2017 }
2018 if (Scr.flags.is_wire_frame_displayed)
2019 {
2020 ENTER_DBG((stderr, "en: exit: iwfd\n"));
2021 /* Ignore EnterNotify events while a window is resized or moved
2022 * as a wire frame; otherwise the window list may be screwed
2023 * up. */
2024 return;
2025 }
2026 if (fw)
2027 {
2028 if (ewp->window != FW_W_FRAME(fw) &&
2029 ewp->window != FW_W_PARENT(fw) &&
2030 ewp->window != FW_W(fw) &&
2031 ewp->window != FW_W_ICON_TITLE(fw) &&
2032 ewp->window != FW_W_ICON_PIXMAP(fw))
2033 {
2034 /* Ignore EnterNotify that received by any of the sub
2035 * windows that don't handle this event. unclutter
2036 * triggers these events sometimes, re focusing an
2037 * unfocused window under the pointer */
2038 ENTER_DBG((stderr, "en: exit: funny window\n"));
2039 return;
2040 }
2041 }
2042 if (Scr.focus_in_pending_window != NULL)
2043 {
2044 ENTER_DBG((stderr, "en: exit: fipw\n"));
2045 /* Ignore EnterNotify event while we are waiting for a window to
2046 * receive focus via Focus or FlipFocus commands. */
2047 focus_grab_buttons(fw);
2048 return;
2049 }
2050 if (ewp->mode == NotifyGrab)
2051 {
2052 ENTER_DBG((stderr, "en: exit: NotifyGrab\n"));
2053 return;
2054 }
2055 else if (ewp->mode == NotifyNormal)
2056 {
2057 ENTER_DBG((stderr, "en: NotifyNormal\n"));
2058 if (ewp->detail == NotifyNonlinearVirtual &&
2059 ewp->focus == False && ewp->subwindow != None)
2060 {
2061 /* This takes care of some buggy apps that forget that
2062 * one of their dialog subwindows has the focus after
2063 * popping up a selection list several times (ddd,
2064 * netscape). I'm not convinced that this does not
2065 * break something else. */
2066 ENTER_DBG((stderr, "en: NN: refreshing focus\n"));
2067 refresh_focus(fw);
2068 }
2069 }
2070 else if (ewp->mode == NotifyUngrab)
2071 {
2072 ENTER_DBG((stderr, "en: NotifyUngrab\n"));
2073 /* Ignore events generated by grabbing or ungrabbing the
2074 * pointer. However, there is no way to prevent the client
2075 * application from handling this event and, for example,
2076 * grabbing the focus. This will interfere with functions that
2077 * transferred the focus to a different window. */
2078 if (is_initial_ungrab_pending)
2079 {
2080 ENTER_DBG((stderr, "en: NU: initial ungrab pending (lgw = NULL)\n"));
2081 is_initial_ungrab_pending = False;
2082 xcrossing_last_grab_window = NULL;
2083 }
2084 else
2085 {
2086 if (ewp->detail == NotifyNonlinearVirtual &&
2087 ewp->focus == False && ewp->subwindow != None)
2088 {
2089 /* see comment above */
2090 ENTER_DBG((stderr, "en: NU: refreshing focus\n"));
2091 refresh_focus(fw);
2092 }
2093 if (fw && fw == xcrossing_last_grab_window)
2094 {
2095 ENTER_DBG((stderr, "en: exit: NU: is last grab window\n"));
2096 if (ewp->window == FW_W_FRAME(fw) ||
2097 ewp->window == FW_W_ICON_TITLE(fw) ||
2098 ewp->window == FW_W_ICON_PIXMAP(fw))
2099 {
2100 ENTER_DBG((stderr, "en: exit: NU: last grab window = NULL\n"));
2101 xcrossing_last_grab_window = NULL;
2102 }
2103 focus_grab_buttons(fw);
2104
2105 return;
2106 }
2107 else if (fw)
2108 {
2109 if (ewp->window != FW_W_FRAME(fw) &&
2110 ewp->window != FW_W_ICON_TITLE(fw) &&
2111 ewp->window != FW_W_ICON_PIXMAP(fw))
2112 {
2113 ENTER_DBG((stderr, "en: exit: NU: not frame window\n"));
2114 focus_grab_buttons(fw);
2115 return;
2116 }
2117 }
2118 }
2119 }
2120 if (fw)
2121 {
2122 is_initial_ungrab_pending = False;
2123 }
2124
2125 /* look for a matching leaveNotify which would nullify this EnterNotify
2126 */
2127 /*
2128 * RBW - if we're in startup, this is a coerced focus, so we don't
2129 * want to save the event time, or exit prematurely.
2130 *
2131 * Ignore LeaveNotify events for tear out menus - handled by menu code
2132 */
2133 is_tear_off_menu =
2134 (fw && IS_TEAR_OFF_MENU(fw) && ewp->window == FW_W(fw));
2135 if (!fFvwmInStartup && !is_tear_off_menu &&
2136 FCheckTypedWindowEvent(dpy, ewp->window, LeaveNotify, &d))
2137 {
2138 if (d.xcrossing.mode == NotifyNormal &&
2139 d.xcrossing.detail != NotifyInferior)
2140 {
2141 ENTER_DBG((stderr, "en: exit: found LeaveNotify\n"));
2142 return;
2143 }
2144 }
2145
2146 if (ewp->window == Scr.Root)
2147 {
2148 FvwmWindow *lf = get_last_screen_focus_window();
2149
2150 if (!Scr.flags.is_pointer_on_this_screen)
2151 {
2152 Scr.flags.is_pointer_on_this_screen = 1;
2153 if (lf && lf != &Scr.FvwmRoot &&
2154 !FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(lf)))
2155 {
2156 SetFocusWindow(lf, True, FOCUS_SET_FORCE);
2157 }
2158 else if (lf != &Scr.FvwmRoot)
2159 {
2160 ForceDeleteFocus();
2161 }
2162 else
2163 {
2164 /* This was the first EnterNotify event for the
2165 * root window - ignore */
2166 }
2167 set_last_screen_focus_window(NULL);
2168 }
2169 else if (!(sf = get_focus_window()) ||
2170 FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2171 {
2172 DeleteFocus(True);
2173 }
2174 else if (
2175 Scr.UnknownWinFocused != None && sf != NULL &&
2176 sf == Scr.StolenFocusFvwmWin)
2177 {
2178 __refocus_stolen_focus_win(ea);
2179 }
2180 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2181 {
2182 InstallWindowColormaps(NULL);
2183 }
2184 focus_grab_buttons(lf);
2185 return;
2186 }
2187 else
2188 {
2189 Scr.flags.is_pointer_on_this_screen = 1;
2190 }
2191
2192 /* An EnterEvent in one of the PanFrameWindows activates the Paging or
2193 an EdgeCommand. */
2194 if (is_pan_frame(ewp->window))
2195 {
2196 char *edge_command = NULL;
2197
2198 if (
2199 Scr.UnknownWinFocused != None &&
2200 (sf = get_focus_window()) != NULL &&
2201 sf == Scr.StolenFocusFvwmWin)
2202 {
2203 __refocus_stolen_focus_win(ea);
2204 }
2205 /* check for edge commands */
2206 if (ewp->window == Scr.PanFrameTop.win)
2207 {
2208 edge_command = Scr.PanFrameTop.command;
2209 }
2210 else if (ewp->window == Scr.PanFrameBottom.win)
2211 {
2212 edge_command = Scr.PanFrameBottom.command;
2213 }
2214 else if (ewp->window == Scr.PanFrameLeft.win)
2215 {
2216 edge_command = Scr.PanFrameLeft.command;
2217 }
2218 else if (ewp->window == Scr.PanFrameRight.win)
2219 {
2220 edge_command = Scr.PanFrameRight.command;
2221 }
2222 if (edge_command && ewp->mode == NotifyUngrab &&
2223 ewp->detail == NotifyAncestor)
2224 {
2225 /* nothing */
2226 }
2227 else if (edge_command)
2228 {
2229 execute_function(NULL, ea->exc, edge_command, 0);
2230 }
2231 else
2232 {
2233 /* no edge command for this pan frame - so we do
2234 * HandlePaging */
2235 int delta_x = 0;
2236 int delta_y = 0;
2237 XEvent e;
2238
2239 /* this was in the HandleMotionNotify before, HEDU */
2240 Scr.flags.is_pointer_on_this_screen = 1;
2241 e = *te;
2242 HandlePaging(
2243 &e, Scr.EdgeScrollX, Scr.EdgeScrollY, &JunkX,
2244 &JunkY, &delta_x, &delta_y, True, True, False,
2245 Scr.ScrollDelay);
2246 return;
2247 }
2248 }
2249 if (!fw)
2250 {
2251 return;
2252 }
2253 if (IS_EWMH_DESKTOP(FW_W(fw)))
2254 {
2255 BroadcastPacket(
2256 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
2257 (long)NULL);
2258 return;
2259 }
2260 if (ewp->window == FW_W_FRAME(fw) ||
2261 ewp->window == FW_W_ICON_TITLE(fw) ||
2262 ewp->window == FW_W_ICON_PIXMAP(fw))
2263 {
2264 BroadcastPacket(
2265 MX_ENTER_WINDOW, 3, (long)FW_W(fw),
2266 (long)FW_W_FRAME(fw), (unsigned long)fw);
2267 }
2268 sf = get_focus_window();
2269 if (sf && fw != sf && FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2270 {
2271 ENTER_DBG((stderr, "en: delete focus\n"));
2272 DeleteFocus(True);
2273 }
2274 focus_grab_buttons(fw);
2275 if (FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
2276 {
2277 ENTER_DBG((stderr, "en: set mousey focus\n"));
2278 if (ewp->window == FW_W(fw))
2279 {
2280 /* Event is for the client window...*/
2281 #ifndef EXPERIMENTAL_ROU_HANDLING_V2
2282 /* RBW -- This may still be needed at times, I'm not
2283 *sure yet. */
2284 SetFocusWindowClientEntered(
2285 fw, True, FOCUS_SET_BY_ENTER);
2286 #else
2287 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2288 #endif
2289 }
2290 else
2291 {
2292 /* Event is for the frame...*/
2293 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2294 }
2295 }
2296 else if (focus_is_focused(fw) && focus_does_accept_input_focus(fw))
2297 {
2298 /* We have to refresh the focus window here in case we left the
2299 * focused fvwm window. Motif apps may lose the input focus
2300 * otherwise. But do not try to refresh the focus of
2301 * applications that want to handle it themselves. */
2302 focus_force_refresh_focus(fw);
2303 }
2304 else if (sf != fw)
2305 {
2306 /* Give the window a chance to grab the buttons needed for
2307 * raise-on-click */
2308 focus_grab_buttons(sf);
2309 }
2310 if (
2311 Scr.UnknownWinFocused != None && sf != NULL &&
2312 sf == Scr.StolenFocusFvwmWin)
2313 {
2314 __refocus_stolen_focus_win(ea);
2315 }
2316 /* We get an EnterNotify with mode == UnGrab when fvwm releases the
2317 * grab held during iconification. We have to ignore this, or icon
2318 * title will be initially raised. */
2319 if (IS_ICONIFIED(fw) && (ewp->mode == NotifyNormal) &&
2320 (ewp->window == FW_W_ICON_PIXMAP(fw) ||
2321 ewp->window == FW_W_ICON_TITLE(fw)) &&
2322 FW_W_ICON_PIXMAP(fw) != None)
2323 {
2324 SET_ICON_ENTERED(fw, 1);
2325 DrawIconWindow(fw, True, False, False, False, NULL);
2326 }
2327 /* Check for tear off menus */
2328 if (is_tear_off_menu)
2329 {
2330 menu_enter_tear_off_menu(ea->exc);
2331 }
2332
2333 return;
2334 }
2335
HandleExpose(const evh_args_t * ea)2336 void HandleExpose(const evh_args_t *ea)
2337 {
2338 XEvent e;
2339 FvwmWindow * const fw = ea->exc->w.fw;
2340
2341 e = *ea->exc->x.etrigger;
2342 #if 0
2343 /* This doesn't work well. Sometimes, the expose count is zero although
2344 * dozens of expose events are pending. This happens all the time
2345 * during a shading animation. Simply flush expose events
2346 * unconditionally. */
2347 if (e.xexpose.count != 0)
2348 {
2349 flush_accumulate_expose(e.xexpose.window, &e);
2350 }
2351 #else
2352 flush_accumulate_expose(e.xexpose.window, &e);
2353 #endif
2354 if (fw == NULL)
2355 {
2356 return;
2357 }
2358 if (e.xany.window == FW_W_ICON_TITLE(fw) ||
2359 e.xany.window == FW_W_ICON_PIXMAP(fw))
2360 {
2361 DrawIconWindow(fw, True, True, False, False, &e);
2362 return;
2363 }
2364 else if (IS_TEAR_OFF_MENU(fw) && e.xany.window == FW_W(fw))
2365 {
2366 /* refresh the contents of the torn out menu */
2367 menu_expose(&e, NULL);
2368 }
2369
2370 return;
2371 }
2372
HandleFocusIn(const evh_args_t * ea)2373 void HandleFocusIn(const evh_args_t *ea)
2374 {
2375 XEvent d;
2376 Window w = None;
2377 Window focus_w = None;
2378 Window focus_fw = None;
2379 Pixel fc = 0;
2380 Pixel bc = 0;
2381 FvwmWindow *ffw_old = get_focus_window();
2382 FvwmWindow *sf;
2383 Bool do_force_broadcast = False;
2384 Bool is_unmanaged_focused = False;
2385 static Window last_focus_w = None;
2386 static Window last_focus_fw = None;
2387 static Bool was_nothing_ever_focused = True;
2388 FvwmWindow *fw = ea->exc->w.fw;
2389
2390 DBUG("HandleFocusIn", "Routine Entered");
2391
2392 Scr.focus_in_pending_window = NULL;
2393 /* This is a hack to make the PointerKey command work */
2394 if (ea->exc->x.etrigger->xfocus.detail != NotifyPointer)
2395 {
2396 /**/
2397 w = ea->exc->x.etrigger->xany.window;
2398 }
2399 while (FCheckTypedEvent(dpy, FocusIn, &d))
2400 {
2401 /* dito */
2402 if (d.xfocus.detail != NotifyPointer)
2403 {
2404 /**/
2405 w = d.xany.window;
2406 }
2407 }
2408 /* dito */
2409 if (w == None)
2410 {
2411 return;
2412 }
2413 /**/
2414 if (XFindContext(dpy, w, FvwmContext, (caddr_t *) &fw) == XCNOENT)
2415 {
2416 fw = NULL;
2417 }
2418
2419 Scr.UnknownWinFocused = None;
2420 if (
2421 fw && Scr.focus_in_requested_window != fw &&
2422 !FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
2423 fw->focus_model == FM_GLOBALLY_ACTIVE)
2424 {
2425 if (DEBUG_GLOBALLY_ACTIVE)
2426 {
2427 fprintf(
2428 stderr, "prevented globally active fw %p (%s)"
2429 " from stealing the focus\n", fw,
2430 fw->name.name);
2431 fprintf(
2432 stderr, "window was %p (%s)\n",
2433 Scr.focus_in_pending_window,
2434 Scr.focus_in_pending_window ?
2435 Scr.focus_in_pending_window->name.name : "");
2436 }
2437 Scr.focus_in_requested_window = NULL;
2438 __refocus_stolen_focus_win(ea);
2439
2440 return;
2441 }
2442 Scr.focus_in_pending_window = NULL;
2443 if (!fw)
2444 {
2445 if (w != Scr.NoFocusWin)
2446 {
2447 Scr.UnknownWinFocused = w;
2448 Scr.StolenFocusWin =
2449 (ffw_old != NULL) ? FW_W(ffw_old) : None;
2450 Scr.StolenFocusFvwmWin = ffw_old;
2451 focus_w = w;
2452 is_unmanaged_focused = True;
2453 }
2454 /* Only show a non-focused window as focused,
2455 * if the focus is on unmanaged and flickering qt dialogs
2456 * workaround is on. */
2457 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2458 !is_unmanaged_focused)
2459 {
2460 border_draw_decorations(
2461 Scr.Hilite, PART_ALL, False, True, CLEAR_ALL,
2462 NULL, NULL);
2463 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2464 {
2465 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2466 {
2467 InstallWindowColormaps(Scr.Hilite);
2468 }
2469 else
2470 {
2471 InstallWindowColormaps(NULL);
2472 }
2473 }
2474 }
2475 /* Not very useful if no window that fvwm and its modules know
2476 * about has the focus. */
2477 fc = GetColor(DEFAULT_FORE_COLOR);
2478 bc = GetColor(DEFAULT_BACK_COLOR);
2479 }
2480 else if (fw != Scr.Hilite ||
2481 /* domivogt (16-May-2000): This check is necessary to force
2482 * sending a M_FOCUS_CHANGE packet after an unmanaged window
2483 * was focused. Otherwise fvwm would believe that Scr.Hilite
2484 * was still focused and not send any info to the modules. */
2485 last_focus_fw == None ||
2486 IS_FOCUS_CHANGE_BROADCAST_PENDING(fw) ||
2487 fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
2488 {
2489 if (w != Scr.NoFocusWin)
2490 {
2491 Scr.StolenFocusWin = (ffw_old != NULL) ?
2492 FW_W(ffw_old) : None;
2493 Scr.StolenFocusFvwmWin = ffw_old;
2494 }
2495 do_force_broadcast = IS_FOCUS_CHANGE_BROADCAST_PENDING(fw);
2496 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 0);
2497 if (fw != Scr.Hilite &&
2498 fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
2499 {
2500 border_draw_decorations(
2501 fw, PART_ALL, True, True, CLEAR_ALL, NULL,
2502 NULL);
2503 }
2504 focus_w = FW_W(fw);
2505 focus_fw = FW_W_FRAME(fw);
2506 fc = fw->hicolors.fore;
2507 bc = fw->hicolors.back;
2508 set_focus_window(fw);
2509 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2510 {
2511 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2512 {
2513 InstallWindowColormaps(Scr.Hilite);
2514 }
2515 else
2516 {
2517 InstallWindowColormaps(NULL);
2518 }
2519 }
2520 }
2521 else
2522 {
2523 return;
2524 }
2525 if (was_nothing_ever_focused || last_focus_fw == None ||
2526 focus_w != last_focus_w || focus_fw != last_focus_fw ||
2527 do_force_broadcast)
2528 {
2529 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2530 !is_unmanaged_focused)
2531 {
2532 BroadcastPacket(
2533 M_FOCUS_CHANGE, 5, (long)focus_w,
2534 (long)focus_fw,
2535 (unsigned long)IsLastFocusSetByMouse(),
2536 (long)fc, (long)bc);
2537 EWMH_SetActiveWindow(focus_w);
2538 }
2539 last_focus_w = focus_w;
2540 last_focus_fw = focus_fw;
2541 was_nothing_ever_focused = False;
2542 }
2543 if ((sf = get_focus_window()) != ffw_old)
2544 {
2545 focus_grab_buttons(sf);
2546 focus_grab_buttons(ffw_old);
2547 }
2548
2549 return;
2550 }
2551
HandleFocusOut(const evh_args_t * ea)2552 void HandleFocusOut(const evh_args_t *ea)
2553 {
2554 if (Scr.UnknownWinFocused != None && Scr.StolenFocusWin != None &&
2555 ea->exc->x.etrigger->xfocus.window == Scr.UnknownWinFocused)
2556 {
2557 __refocus_stolen_focus_win(ea);
2558 }
2559
2560 return;
2561 }
2562
__handle_key(const evh_args_t * ea,Bool is_press)2563 void __handle_key(const evh_args_t *ea, Bool is_press)
2564 {
2565 char *action;
2566 FvwmWindow *sf;
2567 KeyCode kc;
2568 int kcontext;
2569 const XEvent *te = ea->exc->x.etrigger;
2570 const FvwmWindow * const fw = ea->exc->w.fw;
2571 Bool is_second_binding;
2572 const XClassHint *winClass1, *winClass2;
2573 XClassHint tmp;
2574 char *name1, *name2;
2575 const exec_context_t *exc;
2576 exec_context_changes_t ecc;
2577
2578 PressedW = None;
2579
2580 /* Here's a real hack - some systems have two keys with the
2581 * same keysym and different keycodes. This converts all
2582 * the cases to one keycode. */
2583 kc = XKeysymToKeycode(dpy, fvwm_KeycodeToKeysym(dpy, te->xkey.keycode, 0, 0));
2584
2585 /* Check if there is something bound to the key */
2586
2587 sf = get_focus_window();
2588 if (sf == NULL)
2589 {
2590 tmp.res_name = tmp.res_class = name1 = "root";
2591 winClass1 = &tmp;
2592 kcontext = C_ROOT;
2593 }
2594 else
2595 {
2596 winClass1 = &sf->class;
2597 name1 = sf->name.name;
2598 kcontext = (sf == fw ? ea->exc->w.wcontext : C_WINDOW);
2599 }
2600
2601 if (fw == NULL)
2602 {
2603 tmp.res_name = tmp.res_class = name2 = "root";
2604 winClass2 = &tmp;
2605 }
2606 else
2607 {
2608 winClass2 = &fw->class;
2609 name2 = fw->name.name;
2610 }
2611 /* Searching the binding list with a different 'type' value
2612 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
2613 * The different context value does though. */
2614 action = CheckTwoBindings(
2615 &is_second_binding, Scr.AllBindings, STROKE_ARG(0) kc,
2616 te->xkey.state, GetUnusedModifiers(), kcontext, BIND_KEYPRESS,
2617 winClass1, name1, ea->exc->w.wcontext, BIND_PKEYPRESS,
2618 winClass2, name2);
2619
2620 if (action != NULL)
2621 {
2622 if (!is_press)
2623 {
2624 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2625 return;
2626 }
2627 exc = ea->exc;
2628 if (is_second_binding == False)
2629 {
2630 ecc.w.fw = sf;
2631 ecc.w.wcontext = kcontext;
2632 exc = exc_clone_context(
2633 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
2634 }
2635 execute_function(NULL, exc, action, 0);
2636 if (is_second_binding == False)
2637 {
2638 exc_destroy_context(exc);
2639 }
2640 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2641 return;
2642 }
2643
2644 /* if we get here, no function key was bound to the key. Send it
2645 * to the client if it was in a window we know about. */
2646 sf = get_focus_window();
2647 if (sf && te->xkey.window != FW_W(sf))
2648 {
2649 XEvent e;
2650
2651 e = *te;
2652 e.xkey.window = FW_W(sf);
2653 FSendEvent(
2654 dpy, e.xkey.window, False,
2655 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2656 }
2657 else if (fw && te->xkey.window != FW_W(fw))
2658 {
2659 XEvent e;
2660
2661 e = *te;
2662 e.xkey.window = FW_W(fw);
2663 FSendEvent(
2664 dpy, e.xkey.window, False,
2665 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2666 }
2667 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2668
2669 return;
2670 }
2671
HandleKeyPress(const evh_args_t * ea)2672 void HandleKeyPress(const evh_args_t *ea)
2673 {
2674 __handle_key(ea, True);
2675 }
2676
HandleKeyRelease(const evh_args_t * ea)2677 void HandleKeyRelease(const evh_args_t *ea)
2678 {
2679 __handle_key(ea, False);
2680 }
2681
HandleLeaveNotify(const evh_args_t * ea)2682 void HandleLeaveNotify(const evh_args_t *ea)
2683 {
2684 const XLeaveWindowEvent *lwp;
2685 const XEvent *te = ea->exc->x.etrigger;
2686 FvwmWindow * const fw = ea->exc->w.fw;
2687
2688 DBUG("HandleLeaveNotify", "Routine Entered");
2689
2690 ENTER_DBG((stderr, "-------- ln (%d): fw %p w 0x%08x sw 0x%08x mode 0x%x detail 0x%x '%s'\n", ++ecount, fw, (int)te->xcrossing.window, (int)te->xcrossing.subwindow, te->xcrossing.mode, te->xcrossing.detail, fw?fw->visible_name:"(none)"));
2691 lwp = &te->xcrossing;
2692 if (
2693 lwp->window == Scr.Root &&
2694 lwp->detail == NotifyInferior && lwp->mode == NotifyNormal)
2695 {
2696 /* pointer entered subwindow */
2697 BroadcastPacket(
2698 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2699 (long)NULL);
2700 }
2701 else if (
2702 lwp->window == Scr.Root &&
2703 lwp->detail == NotifyNonlinearVirtual)
2704 {
2705 /* pointer left screen */
2706 BroadcastPacket(
2707 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2708 (long)NULL);
2709 }
2710 /* Ignore LeaveNotify events while a window is resized or moved as a
2711 * wire frame; otherwise the window list may be screwed up. */
2712 if (Scr.flags.is_wire_frame_displayed)
2713 {
2714 return;
2715 }
2716 if (lwp->mode != NotifyNormal)
2717 {
2718 /* Ignore events generated by grabbing or ungrabbing the
2719 * pointer. However, there is no way to prevent the client
2720 * application from handling this event and, for example,
2721 * grabbing the focus. This will interfere with functions that
2722 * transferred the focus to a different window. It is
2723 * necessary to check for LeaveNotify events on the client
2724 * window too in case buttons are not grabbed on it. */
2725 if (lwp->mode == NotifyGrab && fw &&
2726 (lwp->window == FW_W_FRAME(fw) ||
2727 lwp->window == FW_W(fw) ||
2728 lwp->window == FW_W_ICON_TITLE(fw) ||
2729 lwp->window == FW_W_ICON_PIXMAP(fw)))
2730 {
2731 ENTER_DBG((stderr, "ln: *** lgw = %p\n", fw));
2732 xcrossing_last_grab_window = fw;
2733 }
2734 #ifdef FOCUS_EXPANDS_TITLE
2735 if (fw && IS_ICONIFIED(fw))
2736 {
2737 SET_ICON_ENTERED(fw, 0);
2738 DrawIconWindow(
2739 fw, True, False, False, False, NULL);
2740 }
2741 #endif
2742 return;
2743 }
2744 /* CDE-like behaviour of raising the icon title if the icon
2745 gets the focus (in particular if the cursor is over the icon) */
2746 if (fw && IS_ICONIFIED(fw))
2747 {
2748 SET_ICON_ENTERED(fw,0);
2749 DrawIconWindow(fw, True, False, False, False, NULL);
2750 }
2751
2752 /* An LeaveEvent in one of the PanFrameWindows activates
2753 an EdgeLeaveCommand. */
2754 if (is_pan_frame(lwp->window))
2755 {
2756 char *edge_command_leave = NULL;
2757
2758 /* check for edge commands */
2759 if (lwp->window == Scr.PanFrameTop.win)
2760 {
2761 edge_command_leave = Scr.PanFrameTop.command_leave;
2762 }
2763 else if (lwp->window == Scr.PanFrameBottom.win)
2764 {
2765 edge_command_leave = Scr.PanFrameBottom.command_leave;
2766 }
2767 else if (lwp->window == Scr.PanFrameLeft.win)
2768 {
2769 edge_command_leave = Scr.PanFrameLeft.command_leave;
2770 }
2771 else if (lwp->window == Scr.PanFrameRight.win)
2772 {
2773 edge_command_leave = Scr.PanFrameRight.command_leave;
2774 }
2775 if (edge_command_leave && lwp->mode == NotifyUngrab &&
2776 lwp->detail == NotifyAncestor)
2777 {
2778 /* nothing */
2779 }
2780 else if (edge_command_leave)
2781 {
2782 execute_function(NULL, ea->exc, edge_command_leave, 0);
2783 }
2784 }
2785
2786
2787 /* If we leave the root window, then we're really moving
2788 * another screen on a multiple screen display, and we
2789 * need to de-focus and unhighlight to make sure that we
2790 * don't end up with more than one highlighted window at a time */
2791 if (lwp->window == Scr.Root &&
2792 /* domivogt (16-May-2000): added this test because somehow fvwm
2793 * sometimes gets a LeaveNotify on the root window although it is
2794 * single screen. */
2795 Scr.NumberOfScreens > 1)
2796 {
2797 if (lwp->mode == NotifyNormal)
2798 {
2799 if (lwp->detail != NotifyInferior)
2800 {
2801 FvwmWindow *sf = get_focus_window();
2802
2803 Scr.flags.is_pointer_on_this_screen = 0;
2804 set_last_screen_focus_window(sf);
2805 if (sf != NULL)
2806 {
2807 DeleteFocus(True);
2808 }
2809 if (Scr.Hilite != NULL)
2810 {
2811 border_draw_decorations(
2812 Scr.Hilite, PART_ALL, False,
2813 True, CLEAR_ALL, NULL, NULL);
2814 }
2815 }
2816 }
2817 }
2818 else
2819 {
2820 /* handle a subwindow cmap */
2821 LeaveSubWindowColormap(te->xany.window);
2822 }
2823 if (fw != NULL &&
2824 (lwp->window == FW_W_FRAME(fw) ||
2825 lwp->window == FW_W_ICON_TITLE(fw) ||
2826 lwp->window == FW_W_ICON_PIXMAP(fw)))
2827 {
2828 BroadcastPacket(
2829 MX_LEAVE_WINDOW, 3, (long)FW_W(fw),
2830 (long)FW_W_FRAME(fw), (unsigned long)fw);
2831 }
2832
2833 return;
2834 }
2835
HandleMapNotify(const evh_args_t * ea)2836 void HandleMapNotify(const evh_args_t *ea)
2837 {
2838 Bool is_on_this_page = False;
2839 const XEvent *te = ea->exc->x.etrigger;
2840 FvwmWindow * const fw = ea->exc->w.fw;
2841
2842 DBUG("HandleMapNotify", "Routine Entered");
2843
2844 if (!fw)
2845 {
2846 if (te->xmap.override_redirect == True &&
2847 te->xmap.window != Scr.NoFocusWin)
2848 {
2849 XSelectInput(dpy, te->xmap.window, XEVMASK_ORW);
2850 XFlush(dpy);
2851 Scr.UnknownWinFocused = te->xmap.window;
2852 }
2853 return;
2854 }
2855 if (te->xmap.window == FW_W_FRAME(fw))
2856 {
2857 /* Now that we know the frame is mapped after capturing the
2858 * window we do not need StructureNotifyMask events anymore. */
2859 XSelectInput(dpy, FW_W_FRAME(fw), XEVMASK_FRAMEW);
2860 XFlush(dpy);
2861 }
2862 /* Except for identifying over-ride redirect window mappings, we
2863 * don't need or want windows associated with the
2864 * SubstructureNotifyMask */
2865 if (te->xmap.event != te->xmap.window)
2866 {
2867 return;
2868 }
2869 SET_MAP_PENDING(fw, 0);
2870 /* don't map if the event was caused by a de-iconify */
2871 if (IS_ICONIFY_PENDING(fw))
2872 {
2873 return;
2874 }
2875
2876 /* Make sure at least part of window is on this page before giving it
2877 * focus... */
2878 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2879
2880 /*
2881 * Need to do the grab to avoid race condition of having server send
2882 * MapNotify to client before the frame gets mapped; this is bad because
2883 * the client would think that the window has a chance of being viewable
2884 * when it really isn't.
2885 */
2886 MyXGrabServer (dpy);
2887 if (FW_W_ICON_TITLE(fw))
2888 {
2889 XUnmapWindow(dpy, FW_W_ICON_TITLE(fw));
2890 }
2891 if (FW_W_ICON_PIXMAP(fw) != None)
2892 {
2893 XUnmapWindow(dpy, FW_W_ICON_PIXMAP(fw));
2894 }
2895 XMapSubwindows(dpy, FW_W_FRAME(fw));
2896 if (fw->Desk == Scr.CurrentDesk)
2897 {
2898 XMapWindow(dpy, FW_W_FRAME(fw));
2899 }
2900 if (IS_ICONIFIED(fw))
2901 {
2902 BroadcastPacket(
2903 M_DEICONIFY, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2904 (unsigned long)fw);
2905 }
2906 else
2907 {
2908 BroadcastPacket(
2909 M_MAP, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2910 (unsigned long)fw);
2911 }
2912
2913 if (is_on_this_page &&
2914 focus_query_open_grab_focus(fw, get_focus_window()) == True)
2915 {
2916 SetFocusWindow(fw, True, FOCUS_SET_FORCE);
2917 }
2918 border_draw_decorations(
2919 fw, PART_ALL, (fw == get_focus_window()) ? True : False, True,
2920 CLEAR_ALL, NULL, NULL);
2921 MyXUngrabServer (dpy);
2922 SET_MAPPED(fw, 1);
2923 SET_ICONIFIED(fw, 0);
2924 SET_ICON_UNMAPPED(fw, 0);
2925 if (DO_ICONIFY_AFTER_MAP(fw))
2926 {
2927 initial_window_options_t win_opts;
2928
2929 /* finally, if iconification was requested before the window
2930 * was mapped, request it now. */
2931 memset(&win_opts, 0, sizeof(win_opts));
2932 Iconify(fw, &win_opts);
2933 SET_ICONIFY_AFTER_MAP(fw, 0);
2934 }
2935 focus_grab_buttons_on_layer(fw->layer);
2936
2937 return;
2938 }
2939
HandleMappingNotify(const evh_args_t * ea)2940 void HandleMappingNotify(const evh_args_t *ea)
2941 {
2942 XRefreshKeyboardMapping(&ea->exc->x.etrigger->xmapping);
2943
2944 return;
2945 }
2946
HandleMapRequest(const evh_args_t * ea)2947 void HandleMapRequest(const evh_args_t *ea)
2948 {
2949 DBUG("HandleMapRequest", "Routine Entered");
2950
2951 if (fFvwmInStartup)
2952 {
2953 /* Just map the damn thing, decorations are added later
2954 * in CaptureAllWindows. */
2955 XMapWindow(dpy, ea->exc->x.etrigger->xmaprequest.window);
2956 return;
2957 }
2958 HandleMapRequestKeepRaised(ea, None, NULL, NULL);
2959
2960 return;
2961 }
2962
HandleMapRequestKeepRaised(const evh_args_t * ea,Window KeepRaised,FvwmWindow * ReuseWin,initial_window_options_t * win_opts)2963 void HandleMapRequestKeepRaised(
2964 const evh_args_t *ea, Window KeepRaised, FvwmWindow *ReuseWin,
2965 initial_window_options_t *win_opts)
2966 {
2967 Bool is_on_this_page = False;
2968 Bool is_new_window = False;
2969 FvwmWindow *tmp;
2970 FvwmWindow *sf;
2971 initial_window_options_t win_opts_bak;
2972 Window ew;
2973 FvwmWindow *fw;
2974 extern Bool Restarting;
2975 const char *initial_map_command;
2976
2977 initial_map_command = NULL;
2978 if (win_opts == NULL)
2979 {
2980 memset(&win_opts_bak, 0, sizeof(win_opts_bak));
2981 win_opts = &win_opts_bak;
2982 }
2983 ew = ea->exc->w.w;
2984 if (ReuseWin == NULL)
2985 {
2986 if (XFindContext(dpy, ew, FvwmContext, (caddr_t *)&fw) ==
2987 XCNOENT)
2988 {
2989 fw = NULL;
2990 }
2991 if (fw != NULL && IS_MAP_PENDING(fw))
2992 {
2993 /* The window is already going to be mapped, no need to
2994 * do that twice */
2995 return;
2996 }
2997 }
2998 else
2999 {
3000 fw = ReuseWin;
3001 }
3002
3003 if (fw == NULL && EWMH_IsKdeSysTrayWindow(ew))
3004 {
3005 /* This means that the window is swallowed by kicker and that
3006 * kicker restart or exit. As we should assume that kicker
3007 * restart we should return here, if not we go into trouble
3008 * ... */
3009 return;
3010 }
3011 if (!win_opts->flags.do_override_ppos)
3012 {
3013 XFlush(dpy);
3014 }
3015
3016 /* If the window has never been mapped before ... */
3017 if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
3018 {
3019 /* Add decorations. */
3020 fw = AddWindow(
3021 &initial_map_command, ea->exc, ReuseWin, win_opts);
3022 if (fw == AW_NO_WINDOW)
3023 {
3024 return;
3025 }
3026 else if (fw == AW_UNMANAGED)
3027 {
3028 XMapWindow(dpy, ew);
3029 return;
3030 }
3031 is_new_window = True;
3032 }
3033 /*
3034 * Make sure at least part of window is on this page
3035 * before giving it focus...
3036 */
3037 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3038 if (KeepRaised != None)
3039 {
3040 XRaiseWindow(dpy, KeepRaised);
3041 }
3042 /* If it's not merely iconified, and we have hints, use them. */
3043
3044 if (IS_ICONIFIED(fw))
3045 {
3046 /* If no hints, or currently an icon, just "deiconify" */
3047 DeIconify(fw);
3048 }
3049 else if (IS_MAPPED(fw))
3050 {
3051 /* the window is already mapped - fake a MapNotify event */
3052 fake_map_unmap_notify(fw, MapNotify);
3053 }
3054 else
3055 {
3056 int state;
3057
3058 if (fw->wmhints && (fw->wmhints->flags & StateHint))
3059 {
3060 state = fw->wmhints->initial_state;
3061 }
3062 else
3063 {
3064 state = NormalState;
3065 }
3066 if (win_opts->initial_state != DontCareState)
3067 {
3068 state = win_opts->initial_state;
3069 }
3070
3071 switch (state)
3072 {
3073 case DontCareState:
3074 case NormalState:
3075 case InactiveState:
3076 default:
3077 MyXGrabServer(dpy);
3078 if (fw->Desk == Scr.CurrentDesk)
3079 {
3080 Bool do_grab_focus;
3081
3082 SET_MAP_PENDING(fw, 1);
3083 XMapWindow(dpy, FW_W_FRAME(fw));
3084 XMapWindow(dpy, FW_W(fw));
3085 SetMapStateProp(fw, NormalState);
3086 if (Scr.flags.is_map_desk_in_progress)
3087 {
3088 do_grab_focus = False;
3089 }
3090 else if (!is_on_this_page)
3091 {
3092 do_grab_focus = False;
3093 }
3094 else if (focus_query_open_grab_focus(
3095 fw, get_focus_window()) ==
3096 True)
3097 {
3098 do_grab_focus = True;
3099 }
3100 else
3101 {
3102 do_grab_focus = False;
3103 }
3104 if (do_grab_focus)
3105 {
3106 SetFocusWindow(
3107 fw, True, FOCUS_SET_FORCE);
3108 }
3109 else
3110 {
3111 /* make sure the old focused window
3112 * still has grabbed all necessary
3113 * buttons. */
3114 focus_grab_buttons(
3115 get_focus_window());
3116 }
3117 }
3118 else
3119 {
3120 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
3121 /* nope, this is forbidden by the ICCCM2 */
3122 XMapWindow(dpy, FW_W(fw));
3123 SetMapStateProp(fw, NormalState);
3124 #else
3125 /* Since we will not get a MapNotify, set the
3126 * IS_MAPPED flag manually. */
3127 SET_MAPPED(fw, 1);
3128 SetMapStateProp(fw, IconicState);
3129 /* fake that the window was mapped to allow
3130 * modules to swallow it */
3131 BroadcastPacket(
3132 M_MAP, 3, (long)FW_W(fw),
3133 (long)FW_W_FRAME(fw),
3134 (unsigned long)fw);
3135 #endif
3136 }
3137 /* TA: 20090125: We *have* to handle
3138 * InitialMapCommand here and not in AddWindow() to
3139 * allow for correct timings when the window is truly
3140 * mapped. (c.f. things like Iconify.)
3141 */
3142
3143 /* TA: 20091212: But only do this when we're *not*
3144 * restarting -- the window is still mapped, but gets
3145 * recaptured -- we don't want to trigger this event
3146 * again. Otherwise we end up toggling the state of
3147 * the window in situations where the
3148 * InitialMapCommand is Iconify or Maximize, for
3149 * instance.
3150 */
3151 if ((initial_map_command != NULL) &&
3152 (!Restarting && Scr.flags.are_windows_captured))
3153 {
3154 execute_function_override_window(
3155 NULL, ea->exc,
3156 (char *)initial_map_command, 0, fw);
3157 }
3158 MyXUngrabServer(dpy);
3159 break;
3160
3161 case IconicState:
3162 if (is_new_window)
3163 {
3164 /* the window will not be mapped - fake a
3165 * MapNotify and an UnmapNotify event. Can't
3166 * remember exactly why this is necessary, but
3167 * probably something w/ (de)iconify state
3168 * confusion. */
3169 fake_map_unmap_notify(fw, MapNotify);
3170 fake_map_unmap_notify(fw, UnmapNotify);
3171 }
3172 if (win_opts->flags.is_iconified_by_parent ||
3173 ((tmp = get_transientfor_fvwmwindow(fw)) &&
3174 IS_ICONIFIED(tmp)))
3175 {
3176 win_opts->flags.is_iconified_by_parent = 0;
3177 SET_ICONIFIED_BY_PARENT(fw, 1);
3178 }
3179 if (USE_ICON_POSITION_HINT(fw) && fw->wmhints &&
3180 (fw->wmhints->flags & IconPositionHint))
3181 {
3182 win_opts->default_icon_x = fw->wmhints->icon_x;
3183 win_opts->default_icon_y = fw->wmhints->icon_y;
3184 }
3185 Iconify(fw, win_opts);
3186 break;
3187 }
3188 }
3189 if (IS_SHADED(fw))
3190 {
3191 BroadcastPacket(
3192 M_WINDOWSHADE, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
3193 (unsigned long)fw);
3194 }
3195 /* If the newly mapped window overlaps the focused window, make sure
3196 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3197 sf = get_focus_window();
3198 if (sf != NULL)
3199 {
3200 focus_grab_buttons(sf);
3201 }
3202 if (win_opts->flags.is_menu)
3203 {
3204 SET_MAPPED(fw, 1);
3205 SET_MAP_PENDING(fw, 0);
3206 }
3207 EWMH_SetClientList();
3208 EWMH_SetClientListStacking();
3209
3210 return;
3211 }
3212
3213 #ifdef HAVE_STROKE
HandleMotionNotify(const evh_args_t * ea)3214 void HandleMotionNotify(const evh_args_t *ea)
3215 {
3216 DBUG("HandleMotionNotify", "Routine Entered");
3217
3218 if (send_motion == True)
3219 {
3220 stroke_record(
3221 ea->exc->x.etrigger->xmotion.x,
3222 ea->exc->x.etrigger->xmotion.y);
3223 }
3224
3225 return;
3226 }
3227 #endif /* HAVE_STROKE */
3228
HandlePropertyNotify(const evh_args_t * ea)3229 void HandlePropertyNotify(const evh_args_t *ea)
3230 {
3231 Bool has_icon_changed = False;
3232 Bool has_icon_pixmap_hint_changed = False;
3233 Bool has_icon_window_hint_changed = False;
3234 /* NoName is an extern pointer to a constant "Untitled".
3235 See lib/Flocale.c FlocaleFreeNameProperty
3236 to see how this initialization causes a problem: */
3237 FlocaleNameString new_name = { NoName, NULL };
3238 int old_wmhints_flags;
3239 const XEvent *te = ea->exc->x.etrigger;
3240 char *urgency_action = NULL;
3241 FvwmWindow * const fw = ea->exc->w.fw;
3242
3243 DBUG("HandlePropertyNotify", "Routine Entered");
3244
3245 if (te->xproperty.window == Scr.Root &&
3246 te->xproperty.state == PropertyNewValue &&
3247 (te->xproperty.atom == _XA_XSETROOT_ID ||
3248 te->xproperty.atom == _XA_XROOTPMAP_ID))
3249 {
3250 /* background change */
3251 /* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3252 * no property notify?). _XA_XROOTPMAP_ID is used by Esetroot
3253 * compatible program: the problem here is that with some
3254 * Esetroot compatible program we get the message _before_ the
3255 * background change. This is fixed with Esetroot 9.2 (not yet
3256 * released, 2002-01-14) */
3257
3258 /* update icon window with some alpha and tear-off menu */
3259 FvwmWindow *t;
3260
3261 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3262 {
3263 int cs;
3264 int t_cs = -1;
3265 int b_cs = t->icon_background_cs;
3266 Bool draw_picture = False;
3267 Bool draw_title = False;
3268
3269 /* redraw ParentRelative tear-off menu */
3270 menu_redraw_transparent_tear_off_menu(t, True);
3271
3272 if (!IS_ICONIFIED(t) || IS_ICON_SUPPRESSED(t))
3273 {
3274 continue;
3275 }
3276 if (Scr.Hilite == t)
3277 {
3278 if (t->icon_title_cs_hi >= 0)
3279 {
3280 t_cs = cs = t->icon_title_cs_hi;
3281 }
3282 else
3283 {
3284 cs = t->cs_hi;
3285 }
3286 }
3287 else
3288 {
3289 if (t->icon_title_cs >= 0)
3290 {
3291 t_cs = cs = t->icon_title_cs;
3292 }
3293 else
3294 {
3295 cs = t->cs;
3296 }
3297 }
3298 if (t->icon_alphaPixmap != None ||
3299 (cs >= 0 &&
3300 Colorset[cs].icon_alpha_percent < 100) ||
3301 CSET_IS_TRANSPARENT_PR(b_cs) ||
3302 (!IS_ICON_SHAPED(t) &&
3303 t->icon_background_padding > 0))
3304 {
3305 draw_picture = True;
3306 }
3307 if (CSET_IS_TRANSPARENT_PR(t_cs))
3308 {
3309 draw_title = True;
3310 }
3311 if (draw_title || draw_picture)
3312 {
3313 DrawIconWindow(
3314 t, draw_title, draw_picture, False,
3315 draw_picture, NULL);
3316 }
3317 }
3318 if (te->xproperty.atom == _XA_XROOTPMAP_ID)
3319 {
3320 update_root_transparent_colorset(te->xproperty.atom);
3321 }
3322 BroadcastPropertyChange(
3323 MX_PROPERTY_CHANGE_BACKGROUND, 0, 0, "");
3324 return;
3325 }
3326
3327 if (!fw)
3328 {
3329 return;
3330 }
3331 switch (te->xproperty.atom)
3332 {
3333 case XA_WM_TRANSIENT_FOR:
3334 {
3335 if (setup_transientfor(fw) == True)
3336 {
3337 RaiseWindow(fw, False);
3338 }
3339 break;
3340 }
3341 case XA_WM_NAME:
3342 {
3343 int changed_names;
3344
3345 flush_property_notify_stop_at_event_type(
3346 te->xproperty.atom, FW_W(fw), 0, 0);
3347 if (XGetGeometry(
3348 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3349 (unsigned int*)&JunkWidth,
3350 (unsigned int*)&JunkHeight,
3351 (unsigned int*)&JunkBW,
3352 (unsigned int*)&JunkDepth) == 0)
3353 {
3354 /* Window does not exist anymore. */
3355 return;
3356 }
3357 if (HAS_EWMH_WM_NAME(fw))
3358 {
3359 return;
3360 }
3361 FlocaleGetNameProperty(XGetWMName, dpy, FW_W(fw), &new_name);
3362 if (new_name.name == NULL)
3363 {
3364 FlocaleFreeNameProperty(&new_name);
3365 return;
3366 }
3367 if (strlen(new_name.name) > MAX_WINDOW_NAME_LEN)
3368 {
3369 /* limit to prevent hanging X server */
3370 (new_name.name)[MAX_WINDOW_NAME_LEN] = 0;
3371 }
3372 if (fw->name.name && strcmp(new_name.name, fw->name.name) == 0)
3373 {
3374 /* migo: some apps update their names every second */
3375 /* griph: make sure we don't free the property if it
3376 is THE same name */
3377 if (new_name.name != fw->name.name)
3378 {
3379 FlocaleFreeNameProperty(&new_name);
3380 }
3381 return;
3382 }
3383
3384 free_window_names(fw, True, False);
3385 fw->name = new_name;
3386 SET_NAME_CHANGED(fw, 1);
3387 if (fw->name.name == NULL)
3388 {
3389 fw->name.name = NoName; /* must not happen */
3390 }
3391 changed_names = 1;
3392 /*
3393 * if the icon name is NoName, set the name of the icon to be
3394 * the same as the window
3395 */
3396 if (!WAS_ICON_NAME_PROVIDED(fw)
3397 #if 0
3398 /* dje, reported as causing various dumps.
3399 I tried to debug, but so far haven't even figured out
3400 how to exercise this logic. Mov 9, 2013. */
3401 || (fw->icon_name.name &&
3402 (fw->icon_name.name != fw->name.name))
3403 #endif
3404 )
3405 {
3406 fw->icon_name = fw->name;
3407 changed_names |= 2;
3408 }
3409 update_window_names(fw, changed_names);
3410 break;
3411 }
3412 case XA_WM_ICON_NAME:
3413 {
3414 flush_property_notify_stop_at_event_type(
3415 te->xproperty.atom, FW_W(fw), 0, 0);
3416 if (XGetGeometry(
3417 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3418 (unsigned int*)&JunkWidth,
3419 (unsigned int*)&JunkHeight,
3420 (unsigned int*)&JunkBW,
3421 (unsigned int*)&JunkDepth) == 0)
3422 {
3423 /* Window does not exist anymore. */
3424 return;
3425 }
3426 if (HAS_EWMH_WM_ICON_NAME(fw))
3427 {
3428 return;
3429 }
3430 FlocaleGetNameProperty(
3431 XGetWMIconName, dpy, FW_W(fw), &new_name);
3432 if (new_name.name == NULL)
3433 {
3434 FlocaleFreeNameProperty(&new_name);
3435 return;
3436 }
3437 if (new_name.name && strlen(new_name.name) > MAX_ICON_NAME_LEN)
3438 {
3439 /* limit to prevent hanging X server */
3440 (new_name.name)[MAX_ICON_NAME_LEN] = 0;
3441 }
3442 if (fw->icon_name.name &&
3443 strcmp(new_name.name, fw->icon_name.name) == 0)
3444 {
3445 /* migo: some apps update their names every second */
3446 /* griph: make sure we don't free the property if it
3447 is THE same name */
3448 if (new_name.name != fw->icon_name.name)
3449 {
3450 FlocaleFreeNameProperty(&new_name);
3451 }
3452 return;
3453 }
3454
3455 free_window_names(fw, False, True);
3456 fw->icon_name = new_name;
3457 SET_WAS_ICON_NAME_PROVIDED(fw, 1);
3458 if (fw->icon_name.name == NULL)
3459 {
3460 /* currently never happens */
3461 fw->icon_name.name = fw->name.name;
3462 SET_WAS_ICON_NAME_PROVIDED(fw, 0);
3463 }
3464 update_window_names(fw, 2);
3465 break;
3466 }
3467 case XA_WM_HINTS:
3468 {
3469 flush_property_notify_stop_at_event_type(
3470 te->xproperty.atom, FW_W(fw), 0, 0);
3471 if (XGetGeometry(
3472 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3473 (unsigned int*)&JunkWidth,
3474 (unsigned int*)&JunkHeight,
3475 (unsigned int*)&JunkBW,
3476 (unsigned int*)&JunkDepth) == 0)
3477 {
3478 /* Window does not exist anymore. */
3479 return;
3480 }
3481 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3482 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3483 */
3484 old_wmhints_flags = 0;
3485 if (fw->wmhints)
3486 {
3487 old_wmhints_flags = fw->wmhints->flags;
3488 XFree ((char *) fw->wmhints);
3489 }
3490 setup_wm_hints(fw);
3491 if (fw->wmhints == NULL)
3492 {
3493 return;
3494 }
3495
3496 /*
3497 * rebuild icon if the client either provides an icon
3498 * pixmap or window or has reset the hints to `no icon'.
3499 */
3500 if ((fw->wmhints->flags & IconPixmapHint) ||
3501 (old_wmhints_flags & IconPixmapHint))
3502 {
3503 ICON_DBG((stderr, "hpn: iph changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconPixmapHint), fw->name.name));
3504 has_icon_pixmap_hint_changed = True;
3505 }
3506 if ((fw->wmhints->flags & IconWindowHint) ||
3507 (old_wmhints_flags & IconWindowHint))
3508 {
3509 ICON_DBG((stderr, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconWindowHint), fw->name.name));
3510 has_icon_window_hint_changed = True;
3511 SET_USE_EWMH_ICON(fw, False);
3512 }
3513 increase_icon_hint_count(fw);
3514 if (has_icon_window_hint_changed ||
3515 has_icon_pixmap_hint_changed)
3516 {
3517 if (ICON_OVERRIDE_MODE(fw) == ICON_OVERRIDE)
3518 {
3519 ICON_DBG((stderr, "hpn: icon override '%s'\n", fw->name.name));
3520 has_icon_changed = False;
3521 }
3522 else if (ICON_OVERRIDE_MODE(fw) ==
3523 NO_ACTIVE_ICON_OVERRIDE)
3524 {
3525 if (has_icon_pixmap_hint_changed)
3526 {
3527 if (WAS_ICON_HINT_PROVIDED(fw) ==
3528 ICON_HINT_MULTIPLE)
3529 {
3530 ICON_DBG((stderr, "hpn: using further iph '%s'\n", fw->name.name));
3531 has_icon_changed = True;
3532 }
3533 else if (fw->icon_bitmap_file ==
3534 NULL ||
3535 fw->icon_bitmap_file ==
3536 Scr.DefaultIcon)
3537 {
3538 ICON_DBG((stderr, "hpn: using first iph '%s'\n", fw->name.name));
3539 has_icon_changed = True;
3540 }
3541 else
3542 {
3543 /* ignore the first icon pixmap
3544 * hint if the application did
3545 * not provide it from the
3546 * start */
3547 ICON_DBG((stderr, "hpn: first iph ignored '%s'\n", fw->name.name));
3548 has_icon_changed = False;
3549 }
3550 }
3551 else if (has_icon_window_hint_changed)
3552 {
3553 ICON_DBG((stderr, "hpn: using iwh '%s'\n", fw->name.name));
3554 has_icon_changed = True;
3555 }
3556 else
3557 {
3558 ICON_DBG((stderr, "hpn: iwh not changed, hint ignored '%s'\n", fw->name.name));
3559 has_icon_changed = False;
3560 }
3561 }
3562 else /* NO_ICON_OVERRIDE */
3563 {
3564 ICON_DBG((stderr, "hpn: using hint '%s'\n", fw->name.name));
3565 has_icon_changed = True;
3566 }
3567
3568 if (USE_EWMH_ICON(fw))
3569 {
3570 has_icon_changed = False;
3571 }
3572
3573 if (has_icon_changed)
3574 {
3575 ICON_DBG((stderr, "hpn: icon changed '%s'\n", fw->name.name));
3576 /* Okay, the icon hint has changed and style
3577 * options tell us to honour this change. Now
3578 * let's see if we have to use the application
3579 * provided pixmap or window (if any), the icon
3580 * file provided by the window's style or the
3581 * default style's icon. */
3582 if (fw->icon_bitmap_file == Scr.DefaultIcon)
3583 {
3584 fw->icon_bitmap_file = NULL;
3585 }
3586 if (!fw->icon_bitmap_file &&
3587 !(fw->wmhints->flags &
3588 (IconPixmapHint|IconWindowHint)))
3589 {
3590 fw->icon_bitmap_file =
3591 (Scr.DefaultIcon) ?
3592 Scr.DefaultIcon : NULL;
3593 }
3594 fw->iconPixmap = (Window)NULL;
3595 ChangeIconPixmap(fw);
3596 }
3597 }
3598
3599 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3600 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3601 * Treat urgency changes by calling user-settable functions.
3602 * These could e.g. deiconify and raise the window or
3603 * temporarily change the decor. */
3604 if (!(old_wmhints_flags & XUrgencyHint) &&
3605 (fw->wmhints->flags & XUrgencyHint))
3606 {
3607 urgency_action = "Function UrgencyFunc";
3608 }
3609 if ((old_wmhints_flags & XUrgencyHint) &&
3610 !(fw->wmhints->flags & XUrgencyHint))
3611 {
3612 urgency_action = "Function UrgencyDoneFunc";
3613 }
3614 if (urgency_action)
3615 {
3616 const exec_context_t *exc;
3617 exec_context_changes_t ecc;
3618
3619 ecc.w.fw = fw;
3620 ecc.w.wcontext = C_WINDOW;
3621 exc = exc_clone_context(
3622 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
3623 execute_function(NULL, exc, urgency_action, 0);
3624 exc_destroy_context(exc);
3625 }
3626 break;
3627 }
3628 case XA_WM_NORMAL_HINTS:
3629 /* just mark wm normal hints as changed and look them up when
3630 * the next ConfigureRequest w/ x, y, width or height set
3631 * arrives. */
3632 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 1);
3633
3634 /* TA: 20120317: Always set the size hints here, regardless
3635 * of them possibly being modified by a ConfigureNotify
3636 * request, due to XSizeHints disallowing resize -- FVWM would
3637 * always use old values if the application decided to toggle
3638 * such things, and FVWM would then never resize the window.
3639 *
3640 * Note that SET_HAS_NEW_WM_NORMAL_HINTS being set here to
3641 * true is still valid.
3642 */
3643 GetWindowSizeHintsWithCheck(fw, 1);
3644 break;
3645 default:
3646 if (te->xproperty.atom == _XA_WM_PROTOCOLS)
3647 {
3648 FetchWmProtocols (fw);
3649 }
3650 else if (te->xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
3651 {
3652 FetchWmColormapWindows (fw); /* frees old data */
3653 ReInstallActiveColormap();
3654 }
3655 else if (te->xproperty.atom == _XA_WM_STATE)
3656 {
3657 /*
3658 * Make sure at least part of window is on this page
3659 * before giving it focus...
3660 */
3661 Bool is_on_this_page;
3662
3663 is_on_this_page = IsRectangleOnThisPage(
3664 &(fw->g.frame), fw->Desk);
3665 if (fw && is_on_this_page == True &&
3666 focus_is_focused(fw) &&
3667 FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
3668 {
3669 /* refresh the focus - why? */
3670 focus_force_refresh_focus(fw);
3671 }
3672 }
3673 else
3674 {
3675 EWMH_ProcessPropertyNotify(ea->exc);
3676 }
3677 break;
3678 }
3679 }
3680
HandleReparentNotify(const evh_args_t * ea)3681 void HandleReparentNotify(const evh_args_t *ea)
3682 {
3683 const XEvent *te = ea->exc->x.etrigger;
3684 FvwmWindow * const fw = ea->exc->w.fw;
3685
3686 if (!fw)
3687 {
3688 return;
3689 }
3690 if (te->xreparent.parent == Scr.Root)
3691 {
3692 /* Ignore reparenting to the root window. In some cases these
3693 * events are selected although the window is no longer
3694 * managed. */
3695 return;
3696 }
3697 if (te->xreparent.parent != FW_W_FRAME(fw))
3698 {
3699 /* window was reparented by someone else, destroy the frame */
3700 SetMapStateProp(fw, WithdrawnState);
3701 EWMH_RestoreInitialStates(fw, te->type);
3702 if (!IS_TEAR_OFF_MENU(fw))
3703 {
3704 XRemoveFromSaveSet(dpy, te->xreparent.window);
3705 XSelectInput(dpy, te->xreparent.window, NoEventMask);
3706 }
3707 else
3708 {
3709 XSelectInput(dpy, te->xreparent.window, XEVMASK_MENUW);
3710 }
3711 XSync(dpy, 0);
3712 FWeedIfWindowEvents(dpy, FW_W_FRAME(fw), NULL, NULL);
3713 destroy_window(fw);
3714 EWMH_ManageKdeSysTray(te->xreparent.window, te->type);
3715 EWMH_WindowDestroyed();
3716 }
3717
3718 return;
3719 }
3720
HandleSelectionRequest(const evh_args_t * ea)3721 void HandleSelectionRequest(const evh_args_t *ea)
3722 {
3723 icccm2_handle_selection_request(ea->exc->x.etrigger);
3724
3725 return;
3726 }
3727
HandleSelectionClear(const evh_args_t * ea)3728 void HandleSelectionClear(const evh_args_t *ea)
3729 {
3730 icccm2_handle_selection_clear();
3731
3732 return;
3733 }
3734
HandleShapeNotify(const evh_args_t * ea)3735 void HandleShapeNotify(const evh_args_t *ea)
3736 {
3737 FvwmWindow * const fw = ea->exc->w.fw;
3738
3739 DBUG("HandleShapeNotify", "Routine Entered");
3740
3741 if (FShapesSupported)
3742 {
3743 const FShapeEvent *sev =
3744 (const FShapeEvent *)(ea->exc->x.etrigger);
3745
3746 if (!fw)
3747 {
3748 return;
3749 }
3750 if (sev->kind != FShapeBounding)
3751 {
3752 return;
3753 }
3754 frame_setup_shape(
3755 fw, fw->g.frame.width, fw->g.frame.height, sev->shaped);
3756 EWMH_SetFrameStrut(fw);
3757 if (!IS_ICONIFIED(fw))
3758 {
3759 border_redraw_decorations(fw);
3760 }
3761 }
3762
3763 return;
3764 }
3765
HandleUnmapNotify(const evh_args_t * ea)3766 void HandleUnmapNotify(const evh_args_t *ea)
3767 {
3768 int dstx, dsty;
3769 Window dumwin;
3770 XEvent dummy;
3771 XEvent map_event;
3772 const XEvent *te = ea->exc->x.etrigger;
3773 Bool focus_grabbed;
3774 Bool must_return = False;
3775 Bool do_map = False;
3776 FvwmWindow * const fw = ea->exc->w.fw;
3777 Window pw;
3778 Window cw;
3779
3780 DBUG("HandleUnmapNotify", "Routine Entered");
3781
3782 if (te->xunmap.event != te->xunmap.window
3783 && (te->xunmap.event != Scr.Root || !te->xunmap.send_event))
3784 {
3785 /* Nothing to do except updating some states. */
3786 must_return = True;
3787 }
3788
3789 /*
3790 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3791 * to WithdrawnState should send a synthetic UnmapNotify with the
3792 * event field set to (pseudo-)root, in case the window is already
3793 * unmapped (which is the case for fvwm for IconicState).
3794 * Unfortunately, we looked for the FvwmContext using that field, so
3795 * try the window field also. */
3796 if (!fw)
3797 {
3798 if (XFindContext(
3799 dpy, te->xunmap.window, FvwmContext,
3800 (caddr_t *)&fw) == XCNOENT)
3801 {
3802 return;
3803 }
3804 }
3805 cw = FW_W(fw);
3806 pw = FW_W_PARENT(fw);
3807 if (te->xunmap.window == FW_W_FRAME(fw))
3808 {
3809 SET_ICONIFY_PENDING(fw , 0);
3810 return;
3811 }
3812 if (must_return)
3813 {
3814 return;
3815 }
3816
3817 if (fw == Scr.Hilite)
3818 {
3819 Scr.Hilite = NULL;
3820 }
3821 focus_grabbed = focus_query_close_release_focus(fw);
3822 restore_focus_after_unmap(fw, False);
3823 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
3824 {
3825 return;
3826 }
3827
3828 /*
3829 * The program may have unmapped the client window, from either
3830 * NormalState or IconicState. Handle the transition to WithdrawnState.
3831 *
3832 * We need to reparent the window back to the root (so that fvwm exiting
3833 * won't cause it to get mapped) and then throw away all state (pretend
3834 * that we've received a DestroyNotify).
3835 */
3836 if (!FCheckTypedWindowEvent(
3837 dpy, te->xunmap.window, DestroyNotify, &dummy) &&
3838 XTranslateCoordinates(
3839 dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
3840 &dumwin))
3841 {
3842 MyXGrabServer(dpy);
3843 SetMapStateProp(fw, WithdrawnState);
3844 EWMH_RestoreInitialStates(fw, te->type);
3845 if (FCheckTypedWindowEvent(
3846 dpy, te->xunmap.window, ReparentNotify, &dummy))
3847 {
3848 if (fw->attr_backup.border_width)
3849 {
3850 XSetWindowBorderWidth(
3851 dpy, te->xunmap.window,
3852 fw->attr_backup.border_width);
3853 }
3854 if ((!IS_ICON_SUPPRESSED(fw))&&
3855 (fw->wmhints &&
3856 (fw->wmhints->flags & IconWindowHint)))
3857 {
3858 XUnmapWindow(dpy, fw->wmhints->icon_window);
3859 }
3860 }
3861 else
3862 {
3863 RestoreWithdrawnLocation(fw, False, Scr.Root);
3864 }
3865 if (!IS_TEAR_OFF_MENU(fw))
3866 {
3867 XRemoveFromSaveSet(dpy, te->xunmap.window);
3868 XSelectInput(dpy, te->xunmap.window, NoEventMask);
3869 }
3870 XSync(dpy, 0);
3871 MyXUngrabServer(dpy);
3872 if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
3873 {
3874 /* the client tried to map the window again while it
3875 * was still inside the decoration windows */
3876 do_map = True;
3877 }
3878 }
3879 destroy_window(fw);
3880 if (focus_grabbed == True)
3881 {
3882 CoerceEnterNotifyOnCurrentWindow();
3883 }
3884 EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
3885 EWMH_WindowDestroyed();
3886 if (do_map == True)
3887 {
3888 map_event.xmaprequest.window = cw;
3889 map_event.xmaprequest.parent = Scr.Root;
3890 dispatch_event(&map_event);
3891 /* note: we really should handle all map and unmap notify
3892 * events for that window in a loop here */
3893 }
3894
3895 return;
3896 }
3897
HandleVisibilityNotify(const evh_args_t * ea)3898 void HandleVisibilityNotify(const evh_args_t *ea)
3899 {
3900 FvwmWindow * const fw = ea->exc->w.fw;
3901
3902 DBUG("HandleVisibilityNotify", "Routine Entered");
3903
3904 if (fw && ea->exc->x.etrigger->xvisibility.window == FW_W_FRAME(fw))
3905 {
3906 switch (ea->exc->x.etrigger->xvisibility.state)
3907 {
3908 case VisibilityUnobscured:
3909 SET_FULLY_VISIBLE(fw, 1);
3910 SET_PARTIALLY_VISIBLE(fw, 1);
3911 break;
3912 case VisibilityPartiallyObscured:
3913 SET_FULLY_VISIBLE(fw, 0);
3914 SET_PARTIALLY_VISIBLE(fw, 1);
3915 break;
3916 default:
3917 SET_FULLY_VISIBLE(fw, 0);
3918 SET_PARTIALLY_VISIBLE(fw, 0);
3919 break;
3920 }
3921 /* Make sure the button grabs are up to date */
3922 focus_grab_buttons(fw);
3923 }
3924
3925 return;
3926 }
3927
3928 /* ---------------------------- interface functions ------------------------ */
3929
3930 /* Inform a client window of its geometry.
3931 *
3932 * The input (frame) geometry will be translated to client geometry
3933 * before sending. */
SendConfigureNotify(FvwmWindow * fw,int x,int y,int w,int h,int bw,Bool send_for_frame_too)3934 void SendConfigureNotify(
3935 FvwmWindow *fw, int x, int y, int w, int h, int bw,
3936 Bool send_for_frame_too)
3937 {
3938 XEvent client_event;
3939 size_borders b;
3940
3941 if (!fw || IS_SHADED(fw))
3942 {
3943 return;
3944 }
3945 client_event.type = ConfigureNotify;
3946 client_event.xconfigure.display = dpy;
3947 client_event.xconfigure.event = FW_W(fw);
3948 client_event.xconfigure.window = FW_W(fw);
3949 get_window_borders(fw, &b);
3950 client_event.xconfigure.x = x + b.top_left.width;
3951 client_event.xconfigure.y = y + b.top_left.height;
3952 client_event.xconfigure.width = w - b.total_size.width;
3953 client_event.xconfigure.height = h - b.total_size.height;
3954 client_event.xconfigure.border_width = bw;
3955 client_event.xconfigure.above = FW_W_FRAME(fw);
3956 client_event.xconfigure.override_redirect = False;
3957 #if 0
3958 fprintf(stderr,
3959 "send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x '%s'\n",
3960 client_event.xconfigure.x, client_event.xconfigure.y,
3961 client_event.xconfigure.width, client_event.xconfigure.height,
3962 (int)FW_W_FRAME(fw), (int)FW_W(fw),
3963 (int)client_event.xconfigure.window,
3964 (fw->name.name) ? fw->name.name : "");
3965 #endif
3966 fev_sanitise_configure_notify(&client_event.xconfigure);
3967 FSendEvent(
3968 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
3969 if (send_for_frame_too)
3970 {
3971 /* This is for buggy tk, which waits for the real
3972 * ConfigureNotify on frame instead of the synthetic one on w.
3973 * The geometry data in the event will not be correct for the
3974 * frame, but tk doesn't look at that data anyway. */
3975 client_event.xconfigure.event = FW_W_FRAME(fw);
3976 client_event.xconfigure.window = FW_W_FRAME(fw);
3977 FSendEvent(
3978 dpy, FW_W_FRAME(fw), False, StructureNotifyMask,
3979 &client_event);
3980 }
3981
3982 return;
3983 }
3984
3985 /* Add an event group to the event handler */
register_event_group(int event_base,int event_count,PFEH * jump_table)3986 int register_event_group(int event_base, int event_count, PFEH *jump_table)
3987 {
3988 /* insert into the list */
3989 event_group_t *group;
3990 event_group_t *position = base_event_group;
3991 event_group_t *prev_position = NULL;
3992
3993 while (
3994 position != NULL &&
3995 position->base + position->count < event_base)
3996 {
3997 prev_position = position;
3998 position = position->next;
3999 }
4000 if ((position != NULL && position->base < event_base + event_count))
4001 {
4002 /* there is already an event group registered at the specified
4003 * event range, or the base is before the base X events */
4004
4005 return 1;
4006 }
4007 /* create the group structure (these are not freed until fvwm exits) */
4008 group = (event_group_t*)safemalloc(sizeof(event_group_t));
4009 group->base = event_base;
4010 group->count = event_count;
4011 group->jump_table = jump_table;
4012 group->next = position;
4013 if (prev_position != NULL)
4014 {
4015 prev_position->next = group;
4016 }
4017 else
4018 {
4019 base_event_group = group;
4020 }
4021
4022 return 0;
4023 }
4024
4025 /*
4026 ** Procedure:
4027 ** InitEventHandlerJumpTable
4028 */
InitEventHandlerJumpTable(void)4029 void InitEventHandlerJumpTable(void)
4030 {
4031 static PFEH EventHandlerJumpTable[LASTEvent];
4032 int i;
4033
4034 for (i=0; i<LASTEvent; i++)
4035 {
4036 EventHandlerJumpTable[i] = NULL;
4037 }
4038 EventHandlerJumpTable[Expose] = HandleExpose;
4039 EventHandlerJumpTable[DestroyNotify] = HandleDestroyNotify;
4040 EventHandlerJumpTable[MapRequest] = HandleMapRequest;
4041 EventHandlerJumpTable[MapNotify] = HandleMapNotify;
4042 EventHandlerJumpTable[UnmapNotify] = HandleUnmapNotify;
4043 EventHandlerJumpTable[ButtonPress] = HandleButtonPress;
4044 EventHandlerJumpTable[EnterNotify] = HandleEnterNotify;
4045 EventHandlerJumpTable[LeaveNotify] = HandleLeaveNotify;
4046 EventHandlerJumpTable[FocusIn] = HandleFocusIn;
4047 EventHandlerJumpTable[FocusOut] = HandleFocusOut;
4048 EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
4049 EventHandlerJumpTable[ClientMessage] = HandleClientMessage;
4050 EventHandlerJumpTable[PropertyNotify] = HandlePropertyNotify;
4051 EventHandlerJumpTable[KeyPress] = HandleKeyPress;
4052 EventHandlerJumpTable[KeyRelease] = HandleKeyRelease;
4053 EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
4054 EventHandlerJumpTable[ColormapNotify] = HandleColormapNotify;
4055 EventHandlerJumpTable[SelectionClear] = HandleSelectionClear;
4056 EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
4057 EventHandlerJumpTable[ReparentNotify] = HandleReparentNotify;
4058 EventHandlerJumpTable[MappingNotify] = HandleMappingNotify;
4059 STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
4060 STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
4061 #ifdef MOUSE_DROPPINGS
4062 STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
4063 #else /* no MOUSE_DROPPINGS */
4064 STROKE_CODE(stroke_init());
4065 #endif /* MOUSE_DROPPINGS */
4066 if (register_event_group(0, LASTEvent, EventHandlerJumpTable))
4067 {
4068 /* should never happen */
4069 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4070 "Failed to initialize event handlers");
4071 exit(1);
4072 }
4073 if (FShapesSupported)
4074 {
4075 static PFEH shape_jump_table[FShapeNumberEvents];
4076
4077 for (i = 0; i < FShapeNumberEvents; i++)
4078 {
4079 shape_jump_table[i] = NULL;
4080 }
4081 shape_jump_table[FShapeNotify] = HandleShapeNotify;
4082 if (
4083 register_event_group(
4084 FShapeEventBase, FShapeNumberEvents,
4085 shape_jump_table))
4086 {
4087 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4088 "Failed to init Shape event handler");
4089 }
4090 }
4091
4092
4093 return;
4094 }
4095
4096 /* handle a single X event */
dispatch_event(XEvent * e)4097 void dispatch_event(XEvent *e)
4098 {
4099 Window w = e->xany.window;
4100 FvwmWindow *fw;
4101 event_group_t *event_group;
4102
4103 DBUG("dispatch_event", "Routine Entered");
4104
4105 XFlush(dpy);
4106 if (w == Scr.Root)
4107 {
4108 switch (e->type)
4109 {
4110 case ButtonPress:
4111 case ButtonRelease:
4112 if (e->xbutton.subwindow != None)
4113 {
4114 w = e->xbutton.subwindow;
4115 }
4116 case MapRequest:
4117 w = e->xmaprequest.window;
4118 break;
4119 default:
4120 break;
4121 }
4122 }
4123 if (w == Scr.Root ||
4124 XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4125 {
4126 fw = NULL;
4127 }
4128 if (
4129 fw != NULL && IS_SCHEDULED_FOR_DESTROY(fw) &&
4130 e->type != DestroyNotify)
4131 {
4132 return;
4133 }
4134 last_event_type = e->type;
4135 event_group = base_event_group;
4136 while (
4137 event_group != NULL &&
4138 event_group->base + event_group->count < e->type)
4139 {
4140 event_group = event_group->next;
4141 }
4142
4143 if (
4144 event_group != NULL &&
4145 e->type - event_group->base < event_group->count &&
4146 event_group->jump_table[e->type - event_group->base] != NULL)
4147 {
4148 evh_args_t ea;
4149 exec_context_changes_t ecc;
4150 Window dummyw;
4151
4152 ecc.type = EXCT_EVENT;
4153 ecc.x.etrigger = e;
4154 ecc.w.wcontext = GetContext(&fw, fw, e, &dummyw);
4155 ecc.w.w = w;
4156 ecc.w.fw = fw;
4157 ea.exc = exc_create_context(
4158 &ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W |
4159 ECC_WCONTEXT);
4160 (*event_group->jump_table[e->type - event_group->base])(&ea);
4161 exc_destroy_context(ea.exc);
4162 }
4163
4164 #ifdef C_ALLOCA
4165 /* If we're using the C version of alloca, see if anything needs to be
4166 * freed up.
4167 */
4168 alloca(0);
4169 #endif
4170 DBUG("dispatch_event", "Leaving Routine");
4171
4172 return;
4173 }
4174
4175 /* ewmh configure request */
events_handle_configure_request(XEvent * cre,FvwmWindow * fw,Bool force,int force_gravity)4176 void events_handle_configure_request(
4177 XEvent *cre, FvwmWindow *fw, Bool force, int force_gravity)
4178 {
4179 _handle_configure_request(cre, NULL, fw, force, force_gravity);
4180
4181 return;
4182 }
4183
HandleEvents(void)4184 void HandleEvents(void)
4185 {
4186 XEvent ev;
4187
4188 DBUG("HandleEvents", "Routine Entered");
4189 STROKE_CODE(send_motion = False);
4190 while (!isTerminated)
4191 {
4192 last_event_type = 0;
4193 if (Scr.flags.is_window_scheduled_for_destroy)
4194 {
4195 destroy_scheduled_windows();
4196 }
4197 if (Scr.flags.do_need_window_update)
4198 {
4199 flush_window_updates();
4200 }
4201 if (My_XNextEvent(dpy, &ev))
4202 {
4203 /* DV (19-Sep-2014): We mark events as invalid by
4204 * setting the send_event field to a bogus value in a
4205 * predicate procedure. It's unsure whether this works
4206 * reliably. */
4207 if (FEV_IS_EVENT_INVALID(ev))
4208 {
4209 continue;
4210 }
4211 dispatch_event(&ev);
4212 }
4213 if (Scr.flags.do_need_style_list_update)
4214 {
4215 simplify_style_list();
4216 }
4217 }
4218
4219 return;
4220 }
4221
4222 /*
4223 *
4224 * Waits for next X or module event, fires off startup routines when startup
4225 * modules have finished or after a timeout if the user has specified a
4226 * command line module that doesn't quit or gets stuck.
4227 *
4228 */
My_XNextEvent(Display * dpy,XEvent * event)4229 int My_XNextEvent(Display *dpy, XEvent *event)
4230 {
4231 fd_set in_fdset, out_fdset;
4232 int num_fd;
4233 fmodule_list_itr moditr;
4234 fmodule *module;
4235 fmodule_input *input;
4236 static struct timeval timeout;
4237 static struct timeval *timeoutP = &timeout;
4238
4239 DBUG("My_XNextEvent", "Routine Entered");
4240
4241 /* check for any X events already queued up.
4242 * Side effect: this does an XFlush if no events are queued
4243 * Make sure nothing between here and the select causes further
4244 * X requests to be sent or the select may block even though
4245 * there are events in the queue */
4246 if (FPending(dpy))
4247 {
4248 DBUG(
4249 "My_XNextEvent", "taking care of queued up events"
4250 " & returning (1)");
4251 FNextEvent(dpy, event);
4252 return 1;
4253 }
4254
4255 /* check for termination of all startup modules */
4256 if (fFvwmInStartup)
4257 {
4258 module_list_itr_init(&moditr);
4259 module = module_list_itr_next(&moditr);
4260 for (; module != NULL; module = module_list_itr_next(&moditr))
4261 {
4262 if (MOD_IS_CMDLINE(module) == 1)
4263 {
4264 break;
4265 }
4266 }
4267 module_cleanup();
4268 if (module == NULL)
4269 {
4270 /* last module */
4271 DBUG(
4272 "My_XNextEvent",
4273 "Starting up after command lines modules");
4274 /* set an infinite timeout to stop ticking */
4275 timeoutP = NULL;
4276 /* This may cause X requests to be sent */
4277 StartupStuff();
4278
4279 return 0; /* so return without select()ing */
4280 }
4281 }
4282
4283 /* Some signals can interrupt us while we wait for any action
4284 * on our descriptors. While some of these signals may be asking
4285 * fvwm to die, some might be harmless. Harmless interruptions
4286 * mean we have to start waiting all over again ... */
4287 do
4288 {
4289 int ms;
4290 Bool is_waiting_for_scheduled_command = False;
4291 static struct timeval *old_timeoutP = NULL;
4292
4293 /* The timeouts become undefined whenever the select returns,
4294 * and so we have to reinitialise them */
4295 ms = squeue_get_next_ms();
4296 if (ms == 0)
4297 {
4298 /* run scheduled commands */
4299 squeue_execute();
4300 ms = squeue_get_next_ms();
4301 /* should not happen anyway.
4302 * get_next_schedule_queue_ms() can't return 0 after a
4303 * call to execute_schedule_queue(). */
4304 if (ms == 0)
4305 {
4306 ms = 1;
4307 }
4308 }
4309 if (ms < 0)
4310 {
4311 timeout.tv_sec = 42;
4312 timeout.tv_usec = 0;
4313 }
4314 else
4315 {
4316 /* scheduled commands are pending - don't wait too
4317 * long */
4318 timeout.tv_sec = ms / 1000;
4319 timeout.tv_usec = 1000 * (ms % 1000);
4320 old_timeoutP = timeoutP;
4321 timeoutP = &timeout;
4322 is_waiting_for_scheduled_command = True;
4323 }
4324
4325 FD_ZERO(&in_fdset);
4326 FD_ZERO(&out_fdset);
4327 FD_SET(x_fd, &in_fdset);
4328
4329 /* nothing is done here if fvwm was compiled without session
4330 * support */
4331 if (sm_fd >= 0)
4332 {
4333 FD_SET(sm_fd, &in_fdset);
4334 }
4335
4336 module_list_itr_init(&moditr);
4337 while ( (module = module_list_itr_next(&moditr)) != NULL)
4338 {
4339 FD_SET(MOD_READFD(module), &in_fdset);
4340
4341 if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
4342 {
4343 FD_SET(MOD_WRITEFD(module), &out_fdset);
4344 }
4345 }
4346
4347 DBUG("My_XNextEvent", "waiting for module input/output");
4348 num_fd = fvwmSelect(
4349 fvwmlib_max_fd, &in_fdset, &out_fdset, 0, timeoutP);
4350 if (is_waiting_for_scheduled_command)
4351 {
4352 timeoutP = old_timeoutP;
4353 }
4354
4355 /* Express route out of fvwm ... */
4356 if (isTerminated)
4357 {
4358 return 0;
4359 }
4360 } while (num_fd < 0);
4361
4362 if (num_fd > 0)
4363 {
4364 /* Check for module input. */
4365 module_list_itr_init(&moditr);
4366 while ( (module = module_list_itr_next(&moditr)) != NULL)
4367 {
4368 if (FD_ISSET(MOD_READFD(module), &in_fdset))
4369 {
4370 input = module_receive(module);
4371 /* enqueue the received command */
4372 module_input_enqueue(input);
4373 }
4374 if (
4375 MOD_WRITEFD(module) >= 0 &&
4376 FD_ISSET(MOD_WRITEFD(module), &out_fdset))
4377 {
4378 DBUG("My_XNextEvent",
4379 "calling FlushMessageQueue");
4380 FlushMessageQueue(module);
4381 }
4382 }
4383
4384 /* execute any commands queued up */
4385 DBUG("My_XNextEvent", "executing module comand queue");
4386 ExecuteCommandQueue();
4387
4388 /* cleanup dead modules */
4389 module_cleanup();
4390
4391 /* nothing is done here if fvwm was compiled without session
4392 * support */
4393 if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
4394 {
4395 ProcessICEMsgs();
4396 }
4397
4398 }
4399 else
4400 {
4401 /* select has timed out, things must have calmed down so let's
4402 * decorate */
4403 if (fFvwmInStartup)
4404 {
4405 fvwm_msg(ERR, "My_XNextEvent",
4406 "Some command line modules have not quit, "
4407 "Starting up after timeout.\n");
4408 StartupStuff();
4409 timeoutP = NULL; /* set an infinite timeout to stop
4410 * ticking */
4411 reset_style_changes();
4412 Scr.flags.do_need_window_update = 0;
4413 }
4414 /* run scheduled commands if necessary */
4415 squeue_execute();
4416 }
4417
4418 /* check for X events again, rather than return 0 and get called again
4419 */
4420 if (FPending(dpy))
4421 {
4422 DBUG("My_XNextEvent",
4423 "taking care of queued up events & returning (2)");
4424 FNextEvent(dpy,event);
4425 return 1;
4426 }
4427
4428 DBUG("My_XNextEvent", "leaving My_XNextEvent");
4429 return 0;
4430 }
4431
4432 /*
4433 *
4434 * Procedure:
4435 * Find the Fvwm context for the event.
4436 *
4437 */
GetContext(FvwmWindow ** ret_fw,FvwmWindow * t,const XEvent * e,Window * w)4438 int GetContext(FvwmWindow **ret_fw, FvwmWindow *t, const XEvent *e, Window *w)
4439 {
4440 int context;
4441 Window win;
4442 Window subw = None;
4443 int x = 0;
4444 int y = 0;
4445 Bool is_key_event = False;
4446
4447 win = e->xany.window;
4448 context = C_NO_CONTEXT;
4449 switch (e->type)
4450 {
4451 case KeyPress:
4452 case KeyRelease:
4453 x = e->xkey.x;
4454 y = e->xkey.y;
4455 subw = e->xkey.subwindow;
4456 if (win == Scr.Root && subw != None)
4457 {
4458 /* Translate root coordinates into subwindow
4459 * coordinates. Necessary for key bindings that work
4460 * over unfocused windows. */
4461 win = subw;
4462 XTranslateCoordinates(
4463 dpy, Scr.Root, subw, x, y, &x, &y, &subw);
4464 XFindContext(dpy, win, FvwmContext, (caddr_t *) &t);
4465 }
4466 is_key_event = True;
4467 /* fall through */
4468 case ButtonPress:
4469 case ButtonRelease:
4470 if (!is_key_event)
4471 {
4472 x = e->xbutton.x;
4473 y = e->xbutton.y;
4474 subw = e->xbutton.subwindow;
4475 }
4476 if (t && win == FW_W_FRAME(t) && subw != None)
4477 {
4478 /* Translate frame coordinates into subwindow
4479 * coordinates. */
4480 win = subw;
4481 XTranslateCoordinates(
4482 dpy, FW_W_FRAME(t), subw, x, y, &x, &y, &subw);
4483 if (win == FW_W_PARENT(t))
4484 {
4485 win = subw;
4486 XTranslateCoordinates(
4487 dpy, FW_W_PARENT(t), subw, x, y, &x,
4488 &y, &subw);
4489 }
4490 }
4491 break;
4492 default:
4493 XFindContext(dpy, win, FvwmContext, (caddr_t *)&t);
4494 break;
4495 }
4496 if (ret_fw != NULL)
4497 {
4498 *ret_fw = t;
4499 }
4500 if (!t)
4501 {
4502 return C_ROOT;
4503 }
4504 *w = win;
4505 if (*w == Scr.NoFocusWin)
4506 {
4507 return C_ROOT;
4508 }
4509 if (subw != None)
4510 {
4511 if (win == FW_W_PARENT(t))
4512 {
4513 *w = subw;
4514 }
4515 }
4516 if (*w == Scr.Root)
4517 {
4518 return C_ROOT;
4519 }
4520 context = frame_window_id_to_context(t, *w, &Button);
4521
4522 return context;
4523 }
4524
4525 /* Drops all expose events for the given window from the input queue and merges
4526 * the expose rectangles into a single big one (*e). */
flush_accumulate_expose(Window w,XEvent * e)4527 void flush_accumulate_expose(Window w, XEvent *e)
4528 {
4529 FWeedIfWindowEvents(dpy, w, _pred_weed_accumulate_expose, (XPointer)e);
4530
4531 return;
4532 }
4533
4534 /*
4535 *
4536 * Removes all expose events from the queue and does the necessary redraws
4537 *
4538 */
handle_all_expose(void)4539 void handle_all_expose(void)
4540 {
4541 void *saved_event;
4542
4543 saved_event = fev_save_event();
4544 FPending(dpy);
4545 FWeedIfEvents(dpy, _pred_weed_handle_expose, NULL);
4546 fev_restore_event(saved_event);
4547
4548 return;
4549 }
4550
4551 /* CoerceEnterNotifyOnCurrentWindow()
4552 * Pretends to get a HandleEnterNotify on the window that the pointer
4553 * currently is in so that the focus gets set correctly from the beginning.
4554 * Note that this presently only works if the current window is not
4555 * click_to_focus; I think that that behaviour is correct and desirable.
4556 * --11/08/97 gjb */
CoerceEnterNotifyOnCurrentWindow(void)4557 void CoerceEnterNotifyOnCurrentWindow(void)
4558 {
4559 Window child;
4560 Window root;
4561 Bool f;
4562 evh_args_t ea;
4563 exec_context_changes_t ecc;
4564 XEvent e;
4565 FvwmWindow *fw;
4566
4567 f = FQueryPointer(
4568 dpy, Scr.Root, &root, &child, &e.xcrossing.x_root,
4569 &e.xcrossing.y_root, &e.xcrossing.x, &e.xcrossing.y,
4570 &JunkMask);
4571 if (f == False || child == None)
4572 {
4573 return;
4574 }
4575 e.xcrossing.type = EnterNotify;
4576 e.xcrossing.window = child;
4577 e.xcrossing.subwindow = None;
4578 e.xcrossing.mode = NotifyNormal;
4579 e.xcrossing.detail = NotifyAncestor;
4580 e.xcrossing.same_screen = True;
4581 if (XFindContext(dpy, child, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4582 {
4583 fw = NULL;
4584 }
4585 else
4586 {
4587 XTranslateCoordinates(
4588 dpy, Scr.Root, child, e.xcrossing.x_root,
4589 e.xcrossing.y_root, &JunkX, &JunkY, &child);
4590 if (child == FW_W_PARENT(fw))
4591 {
4592 child = FW_W(fw);
4593 }
4594 if (child != None)
4595 {
4596 e.xany.window = child;
4597 }
4598 }
4599 e.xcrossing.focus = (fw == get_focus_window()) ? True : False;
4600 ecc.type = EXCT_NULL;
4601 ecc.x.etrigger = &e;
4602 ea.exc = exc_create_context(&ecc, ECC_TYPE | ECC_ETRIGGER);
4603 HandleEnterNotify(&ea);
4604 exc_destroy_context(ea.exc);
4605
4606 return;
4607 }
4608
4609 /* This function discards all queued up events selected by the mask. */
discard_typed_events(int num_event_types,int * event_types)4610 int discard_typed_events(int num_event_types, int *event_types)
4611 {
4612 _weed_event_type_arg args;
4613 int count;
4614 int i;
4615
4616 XSync(dpy, 0);
4617 assert(num_event_types <= MAX_NUM_WEED_EVENT_TYPES);
4618 args.num_event_types = num_event_types;
4619 for (i = 0; i < num_event_types; i++)
4620 {
4621 args.event_types[i] = event_types[i];
4622 }
4623 count = FWeedIfEvents(dpy, _pred_weed_event_type, (XPointer)&args);
4624
4625 return count;
4626 }
4627
4628 /* Similar function for certain types of PropertyNotify. */
flush_property_notify_stop_at_event_type(Atom atom,Window w,char do_stop_at_event_type,int stop_at_event_type)4629 int flush_property_notify_stop_at_event_type(
4630 Atom atom, Window w, char do_stop_at_event_type,
4631 int stop_at_event_type)
4632 {
4633 flush_property_notify_args args;
4634
4635 XSync(dpy, 0);
4636 args.w = w;
4637 args.atom = atom;
4638 args.event_type = PropertyNotify;
4639 args.stop_at_event_type = stop_at_event_type;
4640 args.do_stop_at_event_type = do_stop_at_event_type;
4641 FWeedIfEvents(
4642 dpy, _pred_flush_property_notify_weed, (XPointer)&args);
4643
4644 return 0;
4645 }
4646
4647 /* Wait for all mouse buttons to be released
4648 * This can ease some confusion on the part of the user sometimes
4649 *
4650 * Discard superflous button events during this wait period. */
WaitForButtonsUp(Bool do_handle_expose)4651 void WaitForButtonsUp(Bool do_handle_expose)
4652 {
4653 unsigned int mask;
4654 unsigned int bmask;
4655 long evmask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
4656 KeyPressMask|KeyReleaseMask;
4657 int count;
4658 int use_wait_cursor;
4659 XEvent e;
4660
4661 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
4662 &JunkX, &JunkY, &mask) == False)
4663 {
4664 /* pointer is on a different screen - that's okay here */
4665 }
4666 mask &= DEFAULT_ALL_BUTTONS_MASK;
4667 if (mask == 0)
4668 {
4669 return;
4670 }
4671 if (do_handle_expose)
4672 {
4673 evmask |= ExposureMask;
4674 }
4675 GrabEm(None, GRAB_NORMAL);
4676 for (count = 0, use_wait_cursor = 0; mask != 0; count++)
4677 {
4678 /* handle expose events */
4679 XAllowEvents(dpy, SyncPointer, CurrentTime);
4680 if (FCheckMaskEvent(dpy, evmask, &e))
4681 {
4682 switch (e.type)
4683 {
4684 case ButtonRelease:
4685 if (e.xbutton.button <=
4686 NUMBER_OF_MOUSE_BUTTONS)
4687 {
4688 bmask = (Button1Mask <<
4689 (e.xbutton.button - 1));
4690 mask = e.xbutton.state & ~bmask;
4691 }
4692 break;
4693 case Expose:
4694 dispatch_event(&e);
4695 break;
4696 default:
4697 break;
4698 }
4699 }
4700 else
4701 {
4702 if (FQueryPointer(
4703 dpy, Scr.Root, &JunkRoot, &JunkChild,
4704 &JunkX, &JunkY, &JunkX, &JunkY, &mask) ==
4705 False)
4706 {
4707 /* pointer is on a different screen - that's
4708 * okay here */
4709 }
4710 mask &= DEFAULT_ALL_BUTTONS_MASK;
4711 usleep(1);
4712 }
4713 if (use_wait_cursor == 0 && count == 20)
4714 {
4715 GrabEm(CRS_WAIT, GRAB_NORMAL);
4716 use_wait_cursor = 1;
4717 }
4718 }
4719 UngrabEm(GRAB_NORMAL);
4720 if (use_wait_cursor)
4721 {
4722 UngrabEm(GRAB_NORMAL);
4723 XFlush(dpy);
4724 }
4725
4726 return;
4727 }
4728
sync_server(int toggle)4729 void sync_server(int toggle)
4730 {
4731 static Bool synced = False;
4732
4733 if (toggle == -1)
4734 {
4735 toggle = (synced == False);
4736 }
4737 if (toggle == 1)
4738 {
4739 synced = True;
4740 }
4741 else
4742 {
4743 synced = False;
4744 }
4745 XSynchronize(dpy, synced);
4746 XFlush(dpy);
4747
4748 return;
4749 }
4750
is_resizing_event_pending(FvwmWindow * fw)4751 Bool is_resizing_event_pending(FvwmWindow *fw)
4752 {
4753 XEvent e;
4754 check_if_event_args args;
4755
4756 args.w = FW_W(fw);
4757 args.do_return_true = False;
4758 args.do_return_true_cr = False;
4759 args.cr_value_mask = 0;
4760 args.ret_does_match = False;
4761 args.ret_type = 0;
4762 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
4763
4764 return args.ret_does_match;
4765 }
4766
4767 /* ---------------------------- builtin commands --------------------------- */
4768
CMD_XSynchronize(F_CMD_ARGS)4769 void CMD_XSynchronize(F_CMD_ARGS)
4770 {
4771 int toggle;
4772
4773 toggle = ParseToggleArgument(action, NULL, -1, 0);
4774 sync_server(toggle);
4775
4776 return;
4777 }
4778
CMD_XSync(F_CMD_ARGS)4779 void CMD_XSync(F_CMD_ARGS)
4780 {
4781 XSync(dpy, 0);
4782
4783 return;
4784 }
4785