Add "BugOpts QtDragnDropWorkaround"
[fvwm.git] / fvwm / events.c
blob93b480e830c608f049734318a51b55b88dabde69
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 /* This module is based on Twm, but has been siginificantly modified
17 * by Rob Nation
20 * Copyright 1988 by Evans & Sutherland Computer Corporation,
21 * Salt Lake City, Utah
22 * Portions Copyright 1989 by the Massachusetts Institute of Technology
23 * Cambridge, Massachusetts
25 * All Rights Reserved
27 * Permission to use, copy, modify, and distribute this software and
28 * its documentation for any purpose and without fee is hereby
29 * granted, provided that the above copyright notice appear in all
30 * copies and that both that copyright notice and this permis-
31 * sion notice appear in supporting documentation, and that the
32 * names of Evans & Sutherland and M.I.T. not be used in advertising
33 * in publicity pertaining to distribution of the software without
34 * specific, written prior permission.
36 * EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD
37 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
38 * ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND OR
39 * M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM-
40 * AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
41 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
42 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
43 * OR PERFORMANCE OF THIS SOFTWARE.
46 /* ---------------------------- included header files ---------------------- */
48 #include "config.h"
50 #if HAVE_SYS_BSDTYPES_H
51 #include <sys/bsdtypes.h>
52 #endif
54 #include <stdio.h>
55 #include <unistd.h>
56 #include <X11/Xatom.h>
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 "gnome.h"
90 #include "ewmh.h"
91 #include "update.h"
92 #include "style.h"
93 #include "stack.h"
94 #include "geometry.h"
95 #include "focus.h"
96 #include "virtual.h"
97 #include "decorations.h"
98 #include "schedule.h"
99 #include "menus.h"
100 #include "colormaps.h"
101 #include "colorset.h"
102 #ifdef HAVE_STROKE
103 #include "stroke.h"
104 #endif /* HAVE_STROKE */
106 /* ---------------------------- local definitions -------------------------- */
108 #ifndef XUrgencyHint
109 #define XUrgencyHint (1L << 8)
110 #endif
112 #define CR_MOVERESIZE_MASK (CWX | CWY | CWWidth | CWHeight | CWBorderWidth)
114 /* ---------------------------- local macros ------------------------------- */
116 /* ---------------------------- imports ------------------------------------ */
118 extern void StartupStuff(void);
120 /* ---------------------------- included code files ------------------------ */
122 /* ---------------------------- local types -------------------------------- */
124 typedef void (*PFEH)(const evh_args_t *ea);
126 typedef struct
128 Window w;
129 Bool do_return_true;
130 Bool do_return_true_cr;
131 unsigned long cr_value_mask;
132 Bool ret_does_match;
133 unsigned long ret_type;
134 } check_if_event_args;
136 typedef struct
138 unsigned do_forbid_function : 1;
139 unsigned do_focus : 1;
140 unsigned do_swallow_click : 1;
141 unsigned do_raise : 1;
142 } hfrc_ret_t;
144 typedef struct event_group
146 int base;
147 int count;
148 PFEH *jump_table;
149 struct event_group *next;
150 } event_group_t;
152 /* ---------------------------- forward declarations ----------------------- */
154 /* ---------------------------- local variables ---------------------------- */
156 static int Button = 0;
157 static const FvwmWindow *xcrossing_last_grab_window = NULL;
158 STROKE_CODE(static int send_motion);
159 STROKE_CODE(static char sequence[STROKE_MAX_SEQUENCE + 1]);
160 static event_group_t *base_event_group = NULL;
162 /* ---------------------------- exported variables (globals) --------------- */
164 int last_event_type = 0;
165 Window PressedW = None;
167 /* ---------------------------- local functions ---------------------------- */
169 static void fake_map_unmap_notify(const FvwmWindow *fw, int event_type)
171 XEvent client_event;
172 XWindowAttributes winattrs = {0};
174 if (!XGetWindowAttributes(dpy, FW_W(fw), &winattrs))
176 return;
178 XSelectInput(
179 dpy, FW_W(fw),
180 winattrs.your_event_mask & ~StructureNotifyMask);
181 client_event.type = event_type;
182 client_event.xmap.display = dpy;
183 client_event.xmap.event = FW_W(fw);
184 client_event.xmap.window = FW_W(fw);
185 switch (event_type)
187 case MapNotify:
188 client_event.xmap.override_redirect = False;
189 break;
190 case UnmapNotify:
191 client_event.xunmap.from_configure = False;
192 break;
193 default:
194 /* not possible if called correctly */
195 break;
197 FSendEvent(
198 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
199 XSelectInput(dpy, FW_W(fw), winattrs.your_event_mask);
200 XFlush(dpy);
202 return;
205 static Bool test_map_request(
206 Display *display, XEvent *event, XPointer arg)
208 check_if_event_args *cie_args;
209 Bool rc;
211 cie_args = (check_if_event_args *)arg;
212 cie_args->ret_does_match = False;
213 if (event->type == MapRequest &&
214 event->xmaprequest.window == cie_args->w)
216 cie_args->ret_type = MapRequest;
217 cie_args->ret_does_match = True;
218 rc = cie_args->do_return_true;
220 else
222 cie_args->ret_type = 0;
223 rc = False;
226 /* Yes, it is correct that this function always returns False. */
227 return rc;
230 /* Test for ICCCM2 withdraw requests by syntetic events on the root window */
231 static Bool test_withdraw_request(
232 Display *display, XEvent *event, XPointer arg)
234 check_if_event_args *cie_args;
235 Bool rc;
237 cie_args = (check_if_event_args *)arg;
238 cie_args->ret_does_match = False;
239 if (event->type == UnmapNotify &&
240 event->xunmap.window == cie_args->w &&
241 event->xany.send_event == True &&
242 event->xunmap.event == FW_W(&Scr.FvwmRoot))
244 cie_args->ret_type = UnmapNotify;
245 cie_args->ret_does_match = True;
246 rc = cie_args->do_return_true;
248 else
250 cie_args->ret_type = 0;
251 rc = False;
254 return rc;
257 Bool test_button_event(
258 Display *display, XEvent *event, XPointer arg)
260 if (event->type == ButtonPress || event->type == ButtonRelease)
262 return True;
265 return False;
268 Bool test_typed_window_event(
269 Display *display, XEvent *event, XPointer arg)
271 test_typed_window_event_args *ta = (test_typed_window_event_args *)arg;
273 if (event->xany.window == ta->w &&
274 event->xany.type == ta->event_type)
276 return True;
279 return False;
282 static Bool test_resizing_event(
283 Display *display, XEvent *event, XPointer arg)
285 check_if_event_args *cie_args;
286 Bool rc;
288 cie_args = (check_if_event_args *)arg;
289 cie_args->ret_does_match = False;
290 if (event->xany.window != cie_args->w)
292 return False;
294 rc = False;
295 switch (event->type)
297 case ConfigureRequest:
298 if ((event->xconfigurerequest.value_mask &
299 cie_args->cr_value_mask) != 0)
301 cie_args->ret_type = ConfigureRequest;
302 cie_args->ret_does_match = True;
303 rc = cie_args->do_return_true_cr;
305 break;
306 case PropertyNotify:
307 if (event->xproperty.atom == XA_WM_NORMAL_HINTS)
309 cie_args->ret_type = PropertyNotify;
310 cie_args->ret_does_match = True;
311 rc = cie_args->do_return_true;
313 default:
314 break;
317 /* Yes, it is correct that this function may always returns False. */
318 return rc;
321 static inline void __handle_cr_on_unmanaged(XConfigureRequestEvent *cre)
323 XWindowChanges xwc;
324 unsigned long xwcm;
326 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
327 xwc.x = cre->x;
328 xwc.y = cre->y;
329 xwc.width = cre->width;
330 xwc.height = cre->height;
331 xwc.border_width = cre->border_width;
332 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
334 return;
337 static inline void __handle_cr_on_icon(
338 XConfigureRequestEvent *cre, FvwmWindow *fw)
340 XWindowChanges xwc;
341 unsigned long xwcm;
343 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
344 xwc.x = cre->x;
345 xwc.y = cre->y;
346 xwc.width = cre->width;
347 xwc.height = cre->height;
348 xwc.border_width = cre->border_width;
349 if (FW_W_ICON_PIXMAP(fw) == cre->window)
351 int bw;
353 if (cre->value_mask & CWBorderWidth)
355 fw->icon_border_width = cre->border_width;
357 bw = fw->icon_border_width;
358 if ((cre->value_mask & (CWWidth | CWHeight)) ==
359 (CWWidth | CWHeight))
361 set_icon_picture_size(
362 fw, cre->width + 2 * bw, cre->height + 2 * bw);
365 set_icon_position(fw, cre->x, cre->y);
366 broadcast_icon_geometry(fw, False);
367 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
368 if (cre->window != FW_W_ICON_PIXMAP(fw) &&
369 FW_W_ICON_PIXMAP(fw) != None)
371 rectangle g;
373 get_icon_picture_geometry(fw, &g);
374 xwc.x = g.x;
375 xwc.y = g.y;
376 xwcm = cre->value_mask & (CWX | CWY);
377 XConfigureWindow(
378 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
380 if (FW_W_ICON_TITLE(fw) != None)
382 rectangle g;
384 get_icon_title_geometry(fw, &g);
385 xwc.x = g.x;
386 xwc.y = g.y;
387 xwcm = cre->value_mask & (CWX | CWY);
388 XConfigureWindow(
389 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
392 return;
395 static inline void __handle_cr_on_shaped(FvwmWindow *fw)
397 /* suppress compiler warnings w/o shape extension */
398 int i = 0;
399 unsigned int u = 0;
400 Bool b = False;
401 int boundingShaped;
403 if (FShapeQueryExtents(
404 dpy, FW_W(fw), &boundingShaped, &i, &i, &u, &u, &b,
405 &i, &i, &u, &u))
407 fw->wShaped = boundingShaped;
409 else
411 fw->wShaped = 0;
414 return;
417 static inline void __handle_cr_restack(
418 int *ret_do_send_event, XConfigureRequestEvent *cre, FvwmWindow *fw)
420 XWindowChanges xwc;
421 unsigned long xwcm;
422 FvwmWindow *fw2 = NULL;
424 if (cre->value_mask & CWSibling)
426 if (XFindContext(
427 dpy, cre->above, FvwmContext,
428 (caddr_t *)&fw2) == XCNOENT)
430 fw2 = NULL;
432 if (fw2 == fw)
434 fw2 = NULL;
437 if (cre->detail != Above && cre->detail != Below)
439 HandleUnusualStackmodes(
440 cre->detail, fw, cre->window, fw2, cre->above);
442 /* only allow clients to restack windows within their layer */
443 else if (fw2 == NULL || compare_window_layers(fw2, fw) != 0)
445 switch (cre->detail)
447 case Above:
448 RaiseWindow(fw, True);
449 break;
450 case Below:
451 LowerWindow(fw, True);
452 break;
455 else
457 xwc.sibling = FW_W_FRAME(fw2);
458 xwc.stack_mode = cre->detail;
459 xwcm = CWSibling | CWStackMode;
460 XConfigureWindow(dpy, FW_W_FRAME(fw), xwcm, &xwc);
462 /* Maintain the condition that icon windows are stacked
463 * immediately below their frame
464 * 1. for fw */
465 xwc.sibling = FW_W_FRAME(fw);
466 xwc.stack_mode = Below;
467 xwcm = CWSibling | CWStackMode;
468 if (FW_W_ICON_TITLE(fw) != None)
470 XConfigureWindow(
471 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
473 if (FW_W_ICON_PIXMAP(fw) != None)
475 XConfigureWindow(
476 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
478 /* 2. for fw2 */
479 if (cre->detail == Below)
481 xwc.sibling = FW_W_FRAME(fw2);
482 xwc.stack_mode = Below;
483 xwcm = CWSibling | CWStackMode;
484 if (FW_W_ICON_TITLE(fw2) != None)
486 XConfigureWindow(
487 dpy, FW_W_ICON_TITLE(fw2), xwcm, &xwc);
489 if (FW_W_ICON_PIXMAP(fw2) != None)
491 XConfigureWindow(
492 dpy, FW_W_ICON_PIXMAP(fw2), xwcm,
493 &xwc);
496 /* Maintain the stacking order ring */
497 if (cre->detail == Above)
499 remove_window_from_stack_ring(fw);
500 add_window_to_stack_ring_after(
501 fw, get_prev_window_in_stack_ring(fw2));
503 else /* cre->detail == Below */
505 remove_window_from_stack_ring(fw);
506 add_window_to_stack_ring_after(fw, fw2);
508 BroadcastRestackThisWindow(fw);
510 /* srt (28-Apr-2001): Tk needs a ConfigureNotify event after a
511 * raise, otherwise it would hang for two seconds */
512 *ret_do_send_event = 1;
514 return;
517 static inline void __cr_get_static_position(
518 rectangle *ret_g, FvwmWindow *fw, XConfigureRequestEvent *cre,
519 size_borders *b)
521 if (cre->value_mask & CWX)
523 ret_g->x = cre->x - b->top_left.width;
525 else
527 ret_g->x = fw->g.frame.x;
529 if (cre->value_mask & CWY)
531 ret_g->y = cre->y - b->top_left.height;
533 else
535 ret_g->y = fw->g.frame.y;
538 return;
541 static inline void __cr_get_grav_position(
542 rectangle *ret_g, FvwmWindow *fw, XConfigureRequestEvent *cre,
543 size_borders *b)
545 int grav_x;
546 int grav_y;
548 gravity_get_offsets(fw->hints.win_gravity, &grav_x, &grav_y);
549 if (cre->value_mask & CWX)
551 ret_g->x = cre->x - ((grav_x + 1) * b->total_size.width) / 2;
553 else
555 ret_g->x = fw->g.frame.x;
557 if (cre->value_mask & CWY)
559 ret_g->y = cre->y - ((grav_y + 1) * b->total_size.height) / 2;
561 else
563 ret_g->y = fw->g.frame.y;
566 return;
569 /* Try to detect whether the application uses the ICCCM way of moving its
570 * window or the traditional way, always assuming StaticGravity. */
571 static inline void __cr_detect_icccm_move(
572 FvwmWindow *fw, XConfigureRequestEvent *cre, size_borders *b)
574 rectangle grav_g;
575 rectangle static_g;
576 rectangle dg_g;
577 rectangle ds_g;
578 int mx;
579 int my;
580 int m;
581 int w;
582 int h;
583 int has_x;
584 int has_y;
586 if (CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
588 if (Scr.bo.do_debug_cr_motion_method == 1)
590 fprintf(
591 stderr,
592 "_cdim: --- already detected (pid %d) %p"
593 " '%s'\n", HAS_EWMH_WM_PID(fw), fw,
594 fw->visible_name);
596 return;
598 if (HAS_EWMH_WM_PID(fw))
600 if (Scr.bo.do_debug_cr_motion_method == 1)
602 fprintf(
603 stderr,"_cdim: +++ has ewmh_wm_pid: icccm"
604 " %p '%s'\n", fw, fw->visible_name);
606 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
607 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
608 return;
610 if (fw->ewmh_window_type != EWMH_WINDOW_TYPE_NONE_ID)
612 if (Scr.bo.do_debug_cr_motion_method == 1)
614 fprintf(
615 stderr, "_cdim: +++ has ewmh_window_type:"
616 " icccm %p '%s'\n", fw,
617 fw->visible_name);
619 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
620 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
621 return;
623 if (FShapesSupported && fw->wShaped)
625 if (Scr.bo.do_debug_cr_motion_method == 1)
627 fprintf(
628 stderr, "_cdim: --- shaped window %p "
629 "'%s'\n", fw, fw->visible_name);
631 /* no detection for shaped windows */
632 return;
634 if (fw->hints.win_gravity == StaticGravity)
636 if (Scr.bo.do_debug_cr_motion_method == 1)
638 fprintf(
639 stderr, "_cdim: --- using StaticGravity"
640 " %p '%s'\n", fw, fw->visible_name);
642 return;
644 has_x = (cre->value_mask & CWX);
645 has_y = (cre->value_mask & CWY);
646 if (!has_x && !has_y)
648 if (Scr.bo.do_debug_cr_motion_method == 1)
650 fprintf(
651 stderr, "_cdim: --- not moved %p '%s'\n",
652 fw, fw->visible_name);
654 return;
656 __cr_get_grav_position(&grav_g, fw, cre, b);
657 __cr_get_static_position(&static_g, fw, cre, b);
658 if (static_g.x == grav_g.x)
660 /* both methods have the same result; ignore */
661 has_x = 0;
663 if (static_g.y == grav_g.y)
665 /* both methods have the same result; ignore */
666 has_y = 0;
668 if (!has_x && !has_y)
670 if (Scr.bo.do_debug_cr_motion_method == 1)
672 fprintf(
673 stderr, "_cdim: --- not moved %p '%s'\n",
674 fw, fw->visible_name);
676 return;
678 dg_g.x = grav_g.x - fw->g.frame.x;
679 dg_g.y = grav_g.y - fw->g.frame.y;
680 ds_g.x = static_g.x - fw->g.frame.x;
681 ds_g.y = static_g.y - fw->g.frame.y;
682 if (Scr.bo.do_debug_cr_motion_method == 1)
684 fprintf(
685 stderr, "s %3d/%3d %2d/%2d, g %3d/%3d %2d/%2d: ",
686 static_g.x, static_g.y, ds_g.x, ds_g.y, grav_g.x,
687 grav_g.y, dg_g.x, dg_g.y);
689 /* check full screen */
690 if ((cre->value_mask & (CWX | CWY)) == (CWX | CWY) &&
691 (has_x || has_y) &&
692 cre->width == Scr.MyDisplayWidth &&
693 cre->height == Scr.MyDisplayHeight)
695 if (grav_g.x == -b->top_left.width &&
696 grav_g.y == -b->top_left.height)
698 /* Window is fullscreen using the ICCCM way. */
699 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
700 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
701 if (Scr.bo.do_debug_cr_motion_method == 1)
703 fprintf(
704 stderr, "+++ fullscreen icccm %p"
705 " '%s'\n", fw, fw->visible_name);
707 return;
709 else if (static_g.x == -b->top_left.width &&
710 static_g.y == -b->top_left.height)
712 /* Window is fullscreen using the traditional way. */
713 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
714 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
715 if (Scr.bo.do_debug_cr_motion_method == 1)
717 fprintf(
718 stderr, "+++ fullscreen traditional"
719 " %p '%s'\n", fw,
720 fw->visible_name);
722 return;
725 /* check travelling across the screen */
726 if (has_x && dg_g.x == 0 && ds_g.x != 0 &&
727 has_y && dg_g.y == 0 && ds_g.y != 0)
729 /* The traditional way causes a shift by the border width or
730 * height. Use ICCCM way. */
731 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
732 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
733 if (Scr.bo.do_debug_cr_motion_method == 1)
735 fprintf(
736 stderr, "+++ travelling icccm %p '%s'\n",
737 fw, fw->visible_name);
739 return;
741 if (has_x && dg_g.x != 0 && ds_g.x == 0 &&
742 has_y && dg_g.y != 0 && ds_g.y == 0)
744 /* The ICCCM way causes a shift by the border width or height.
745 * Use traditional way. */
746 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
747 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
748 if (Scr.bo.do_debug_cr_motion_method == 1)
750 fprintf(
751 stderr, "+++ travelling traditional %p"
752 " '%s'\n", fw, fw->visible_name);
754 return;
756 /* check placement near border */
757 w = (cre->value_mask & CWWidth) ?
758 cre->width + b->total_size.width : fw->g.frame.width;
759 h = (cre->value_mask & CWHeight) ?
760 cre->height + b->total_size.height : fw->g.frame.height;
761 if (!has_x)
763 mx = CR_MOTION_METHOD_AUTO;
765 else if (static_g.x == 0 || static_g.x + w == Scr.MyDisplayWidth)
767 mx = CR_MOTION_METHOD_STATIC_GRAV;
769 else if (grav_g.x == 0 || grav_g.x + w == Scr.MyDisplayWidth)
771 mx = CR_MOTION_METHOD_USE_GRAV;
773 else
775 mx = CR_MOTION_METHOD_AUTO;
777 if (!has_y)
779 my = CR_MOTION_METHOD_AUTO;
781 else if (static_g.y == 0 || static_g.y + h == Scr.MyDisplayHeight)
783 my = CR_MOTION_METHOD_STATIC_GRAV;
785 else if (grav_g.y == 0 || grav_g.y + h == Scr.MyDisplayHeight)
787 my = CR_MOTION_METHOD_USE_GRAV;
789 else
791 my = CR_MOTION_METHOD_AUTO;
793 m = (mx != CR_MOTION_METHOD_AUTO) ? mx : my;
794 if (m != CR_MOTION_METHOD_AUTO)
796 /* Window was placed next to the display border. */
797 if (m == my || my == CR_MOTION_METHOD_AUTO)
799 SET_CR_MOTION_METHOD(fw, m);
800 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
801 if (Scr.bo.do_debug_cr_motion_method == 1)
803 fprintf(
804 stderr, "+++ near border %s %p "
805 "'%s'\n", (m ==
806 CR_MOTION_METHOD_USE_GRAV)
807 ? "icccm" : "traditional", fw,
808 fw->visible_name);
810 return;
813 if (Scr.bo.do_debug_cr_motion_method == 1)
815 fprintf(
816 stderr, "--- not detected %p '%s'\n", fw,
817 fw->visible_name);
820 return;
823 #define EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
824 /* This is not a good idea because this interferes with changes in the size
825 * hints of the window. However, it is impossible to be completely safe here.
826 * For example, if the client changes the size inc, then resizes the size of
827 * its window and then changes the size inc again - all in one batch - then
828 * the WM will read the *second* size inc upon the *first* event and use the
829 * wrong one in the ConfigureRequest calculations. */
830 /* dv (31 Mar 2002): The code now handles these situations, so enable it
831 * again. */
832 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
833 static inline int __merge_cr_moveresize(
834 const evh_args_t *ea, XConfigureRequestEvent *cre, FvwmWindow *fw,
835 size_borders *b)
837 int cn_count = 0;
838 XEvent e;
839 XConfigureRequestEvent *ecre;
840 check_if_event_args args;
842 args.w = cre->window;
843 args.do_return_true = False;
844 args.do_return_true_cr = True;
845 args.cr_value_mask = CR_MOVERESIZE_MASK;
846 args.ret_does_match = False;
847 args.ret_type = 0;
849 for (cn_count = 0; 1; )
851 unsigned long vma;
852 unsigned long vmo;
853 unsigned long xm;
854 unsigned long ym;
855 evh_args_t ea2;
856 exec_context_changes_t ecc;
858 FCheckPeekIfEvent(
859 dpy, &e, test_resizing_event, (XPointer)&args);
860 ecre = &e.xconfigurerequest;
861 if (args.ret_does_match == False)
863 break;
865 else if (args.ret_type == PropertyNotify)
867 /* Can't merge events with a PropertyNotify in
868 * between. The event is still on the queue. */
869 break;
871 else if (args.ret_type != ConfigureRequest)
873 /* not good. unselected event type! */
874 continue;
876 /* Event was not yet removed from the queue but stored in e. */
877 xm = CWX | CWWidth;
878 ym = CWY | CWHeight;
879 vma = cre->value_mask & ecre->value_mask;
880 vmo = cre->value_mask | ecre->value_mask;
881 if (((vma & xm) == 0 && (vmo & xm) == xm) ||
882 ((vma & ym) == 0 && (vmo & ym) == ym))
884 /* can't merge events since location of window might
885 * get screwed up. */
886 break;
888 /* Finally remove the event from the queue */
889 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
890 /* partially handle the event */
891 ecre->value_mask &= ~args.cr_value_mask;
892 ea2.exc = exc_clone_context(ea->exc, &ecc, ECC_ETRIGGER);
893 HandleConfigureRequest(&ea2);
894 exc_destroy_context(ea2.exc);
895 /* collect the size/position changes */
896 if (ecre->value_mask & CWX)
898 cre->x = ecre->x;
900 if (ecre->value_mask & CWY)
902 cre->y = ecre->y;
904 if (ecre->value_mask & CWWidth)
906 cre->width = ecre->width;
908 if (ecre->value_mask & CWHeight)
910 cre->height = ecre->height;
912 if (ecre->value_mask & CWBorderWidth)
914 cre->border_width = ecre->border_width;
916 cre->value_mask |= (ecre->value_mask & CR_MOVERESIZE_MASK);
917 cn_count++;
920 return cn_count;
922 #endif
924 static inline int __handle_cr_on_client(
925 int *ret_do_send_event, XConfigureRequestEvent cre,
926 const evh_args_t *ea, FvwmWindow *fw, Bool force, int force_gravity)
928 rectangle new_g;
929 rectangle d_g;
930 size_rect constr_dim;
931 size_rect oldnew_dim;
932 size_borders b;
933 int cn_count = 0;
934 int gravity;
936 if (ea)
938 cre = ea->exc->x.etrigger->xconfigurerequest;
940 if ((cre.value_mask & (CWWidth | CWHeight | CWX | CWY)) == 0)
942 return 0;
945 get_window_borders(fw, &b);
946 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
947 /* Merge all pending ConfigureRequests for the window into a single
948 * event. However, we can not do this if the window uses the motion
949 * method autodetection because the merged event might confuse the
950 * detection code. */
951 if (ea && CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
953 cn_count = __merge_cr_moveresize(ea, &cre, fw, &b);
955 #endif
956 #if 0
957 fprintf(stderr,
958 "cre: %d(%d) %d(%d) %d(%d)x%d(%d) fw 0x%08x w 0x%08x "
959 "ew 0x%08x '%s'\n",
960 cre.x, (int)(cre.value_mask & CWX),
961 cre.y, (int)(cre.value_mask & CWY),
962 cre.width, (int)(cre.value_mask & CWWidth),
963 cre.height, (int)(cre.value_mask & CWHeight),
964 (int)FW_W_FRAME(fw), (int)FW_W(fw), (int)cre.window,
965 (fw->name.name) ? fw->name.name : "");
966 #endif
967 /* Don't modify frame_g fields before calling SetupWindow! */
968 memset(&d_g, 0, sizeof(d_g));
970 if (HAS_NEW_WM_NORMAL_HINTS(fw))
972 /* get the latest size hints */
973 XSync(dpy, 0);
974 GetWindowSizeHints(fw);
975 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
977 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMaxSize))
979 /* Java workaround */
980 if (cre.height > fw->hints.max_height &&
981 fw->hints.max_height <= BROKEN_MAXSIZE_LIMIT)
983 fw->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
984 cre.value_mask |= CWHeight;
986 if (cre.width > fw->hints.max_width &&
987 fw->hints.max_width <= BROKEN_MAXSIZE_LIMIT)
989 fw->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
990 cre.value_mask |= CWWidth;
993 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMinSize))
995 if (cre.width < fw->hints.min_width &&
996 fw->hints.min_width >= BROKEN_MINSIZE_LIMIT)
998 fw->hints.min_width = 1;
999 cre.value_mask |= CWWidth;
1001 if (cre.height < fw->hints.min_height &&
1002 fw->hints.min_height >= BROKEN_MINSIZE_LIMIT)
1004 fw->hints.min_height = 1;
1005 cre.value_mask |= CWHeight;
1008 if (IS_SHADED(fw) ||
1009 !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM, False))
1011 /* forbid shaded applications to move their windows */
1012 cre.value_mask &= ~(CWX | CWY);
1013 /* resend the old geometry */
1014 *ret_do_send_event = 1;
1016 if (IS_MAXIMIZED(fw))
1018 /* dont allow clients to resize maximized windows */
1019 cre.value_mask &= ~(CWWidth | CWHeight);
1020 /* resend the old geometry */
1021 *ret_do_send_event = 1;
1022 d_g.width = 0;
1023 d_g.height = 0;
1025 else if (
1026 !is_function_allowed(
1027 F_RESIZE, NULL, fw, RQORIG_PROGRAM, False))
1029 cre.value_mask &= ~(CWWidth | CWHeight);
1030 *ret_do_send_event = 1;
1033 if (cre.value_mask & CWBorderWidth)
1035 /* for restoring */
1036 fw->attr_backup.border_width = cre.border_width;
1038 if (!force && CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO)
1040 __cr_detect_icccm_move(fw, &cre, &b);
1042 if (force_gravity > ForgetGravity && force_gravity <= StaticGravity)
1044 gravity = force_gravity;
1046 else
1048 gravity = fw->hints.win_gravity;
1050 if (!(cre.value_mask & (CWX | CWY)))
1052 /* nothing */
1054 else if ((force ||
1055 CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_USE_GRAV) &&
1056 gravity != StaticGravity)
1058 int ref_x;
1059 int ref_y;
1060 int grav_x;
1061 int grav_y;
1063 gravity_get_offsets(gravity, &grav_x, &grav_y);
1064 if (cre.value_mask & CWX)
1066 ref_x = cre.x -
1067 ((grav_x + 1) * b.total_size.width) / 2;
1068 d_g.x = ref_x - fw->g.frame.x;
1070 if (cre.value_mask & CWY)
1072 ref_y = cre.y -
1073 ((grav_y + 1) * b.total_size.height) / 2;
1074 d_g.y = ref_y - fw->g.frame.y;
1077 else /* ..._USE_GRAV or ..._AUTO */
1079 /* default: traditional cr handling */
1080 if (cre.value_mask & CWX)
1082 d_g.x = cre.x - fw->g.frame.x - b.top_left.width;
1084 if (cre.value_mask & CWY)
1086 d_g.y = cre.y - fw->g.frame.y - b.top_left.height;
1089 if (cre.value_mask & CWHeight)
1091 if (cre.height <
1092 (WINDOW_FREAKED_OUT_SIZE - b.total_size.height))
1094 d_g.height = cre.height -
1095 (fw->g.frame.height - b.total_size.height);
1097 else
1099 /* Ignore height changes to astronomically large
1100 * windows (needed for XEmacs 20.4); don't care if the
1101 * window is shaded here - we won't use 'height' in
1102 * this case anyway.
1103 * Inform the buggy app about the size that *we* want
1105 d_g.height = 0;
1106 *ret_do_send_event = 1;
1109 if (cre.value_mask & CWWidth)
1111 if (cre.width < (WINDOW_FREAKED_OUT_SIZE - b.total_size.width))
1113 d_g.width = cre.width -
1114 (fw->g.frame.width - b.total_size.width);
1116 else
1118 d_g.width = 0;
1119 *ret_do_send_event = 1;
1123 /* SetupWindow (x,y) are the location of the upper-left outer corner
1124 * and are passed directly to XMoveResizeWindow (frame). The
1125 * (width,height) are the inner size of the frame. The inner width is
1126 * the same as the requested client window width; the inner height is
1127 * the same as the requested client window height plus any title bar
1128 * slop. */
1129 new_g = fw->g.frame;
1130 if (IS_SHADED(fw))
1132 new_g.width = fw->g.normal.width;
1133 new_g.height = fw->g.normal.height;
1135 oldnew_dim.width = new_g.width + d_g.width;
1136 oldnew_dim.height = new_g.height + d_g.height;
1137 constr_dim.width = oldnew_dim.width;
1138 constr_dim.height = oldnew_dim.height;
1139 constrain_size(
1140 fw, NULL, &constr_dim.width, &constr_dim.height, 0, 0,
1141 CS_UPDATE_MAX_DEFECT);
1142 d_g.width += (constr_dim.width - oldnew_dim.width);
1143 d_g.height += (constr_dim.height - oldnew_dim.height);
1144 if ((cre.value_mask & CWX) && d_g.width)
1146 new_g.x = fw->g.frame.x + d_g.x;
1147 new_g.width = fw->g.frame.width + d_g.width;
1149 else if ((cre.value_mask & CWX) && !d_g.width)
1151 new_g.x = fw->g.frame.x + d_g.x;
1153 else if (!(cre.value_mask & CWX) && d_g.width)
1155 gravity_resize(gravity, &new_g, d_g.width, 0);
1157 if ((cre.value_mask & CWY) && d_g.height)
1159 new_g.y = fw->g.frame.y + d_g.y;
1160 new_g.height = fw->g.frame.height + d_g.height;
1162 else if ((cre.value_mask & CWY) && !d_g.height)
1164 new_g.y = fw->g.frame.y + d_g.y;
1166 else if (!(cre.value_mask & CWY) && d_g.height)
1168 gravity_resize(gravity, &new_g, 0, d_g.height);
1171 if (new_g.x == fw->g.frame.x && new_g.y == fw->g.frame.y &&
1172 new_g.width == fw->g.frame.width &&
1173 new_g.height == fw->g.frame.height)
1175 /* Window will not be moved or resized; send a synthetic
1176 * ConfigureNotify. */
1177 *ret_do_send_event = 1;
1179 else if ((cre.value_mask & CWX) || (cre.value_mask & CWY) ||
1180 d_g.width || d_g.height)
1182 if (IS_SHADED(fw))
1184 get_shaded_geometry(fw, &new_g, &new_g);
1186 frame_setup_window_app_request(
1187 fw, new_g.x, new_g.y, new_g.width, new_g.height,
1188 False);
1189 /* make sure the window structure has the new position */
1190 update_absolute_geometry(fw);
1191 maximize_adjust_offset(fw);
1192 GNOME_SetWinArea(fw);
1194 else if (DO_FORCE_NEXT_CR(fw))
1196 *ret_do_send_event = 1;
1198 SET_FORCE_NEXT_CR(fw, 0);
1199 SET_FORCE_NEXT_PN(fw, 0);
1201 return cn_count;
1204 void __handle_configure_request(
1205 XConfigureRequestEvent cre, const evh_args_t *ea, FvwmWindow *fw,
1206 Bool force, int force_gravity)
1208 int do_send_event = 0;
1209 int cn_count = 0;
1211 /* According to the July 27, 1988 ICCCM draft, we should ignore size
1212 * and position fields in the WM_NORMAL_HINTS property when we map a
1213 * window. Instead, we'll read the current geometry. Therefore, we
1214 * should respond to configuration requests for windows which have
1215 * never been mapped. */
1216 if (fw == NULL)
1218 __handle_cr_on_unmanaged(&cre);
1219 return;
1221 if (cre.window == FW_W_ICON_TITLE(fw) ||
1222 cre.window == FW_W_ICON_PIXMAP(fw))
1224 __handle_cr_on_icon(&cre, fw);
1226 if (FShapesSupported)
1228 __handle_cr_on_shaped(fw);
1230 if (fw != NULL && cre.window == FW_W(fw))
1232 cn_count = __handle_cr_on_client(
1233 &do_send_event, cre, ea, fw, force, force_gravity);
1235 /* Stacking order change requested. Handle this *after* geometry
1236 * changes, since we need the new geometry in occlusion calculations */
1237 if ((cre.value_mask & CWStackMode) &&
1238 (!DO_IGNORE_RESTACK(fw) || force))
1240 __handle_cr_restack(&do_send_event, &cre, fw);
1242 #if 1
1243 /* This causes some ddd windows not to be drawn properly. Reverted back
1244 * to the old method in frame_setup_window. */
1245 /* domivogt (15-Oct-1999): enabled this to work around buggy apps that
1246 * ask for a nonsense height and expect that they really get it. */
1247 if (cn_count == 0 && do_send_event)
1249 cn_count = 1;
1251 else if (cn_count > 0)
1253 do_send_event = 1;
1255 for ( ; cn_count > 0; cn_count--)
1257 SendConfigureNotify(
1258 fw, fw->g.frame.x, fw->g.frame.y, fw->g.frame.width,
1259 fw->g.frame.height, 0, True);
1261 if (do_send_event)
1263 XFlush(dpy);
1265 #endif
1267 return;
1270 static Bool __predicate_button_click(
1271 Display *display, XEvent *event, XPointer arg)
1273 if (event->type == ButtonPress || event->type == ButtonRelease)
1275 return True;
1278 return False;
1281 /* Helper function for __handle_focus_raise_click(). */
1282 static Bool __test_for_motion(int x0, int y0)
1284 int x;
1285 int y;
1286 unsigned int mask;
1287 XEvent e;
1289 /* Query the pointer to do this. We can't check for events here since
1290 * the events are still needed if the pointer moves. */
1292 /* However, some special mouse (e.g., a touchpad with the
1293 * synaptic driver) may handle a double click in a special way
1294 * (for dragging through short touching and holding down the
1295 * finger on the touchpad). Bascially, when you execute a
1296 * double click the first button release is queued after the
1297 * second _physical_ mouse release happen. It seems that
1298 * FQueryPointer may not work as expected: it does not see
1299 * that the button is released on a double click. So, we need
1300 * to check for a button press in the future to avoid a fvwm
1301 * lockup! (olicha 2004-01-31) */
1303 for (x = x0, y = y0; FQueryPointer(
1304 dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
1305 &x, &y, &mask) == True; usleep(20000))
1307 if ((mask & DEFAULT_ALL_BUTTONS_MASK) == 0)
1309 /* all buttons are released */
1310 return False;
1312 else if (abs(x - x0) >= Scr.MoveThreshold ||
1313 abs(y - y0) >= Scr.MoveThreshold)
1315 /* the pointer has moved */
1316 return True;
1318 if (FCheckPeekIfEvent(dpy, &e, __predicate_button_click, NULL))
1320 /* click in the future */
1321 return False;
1323 else
1325 /* The predicate procedure finds no match, no event
1326 * has been removed from the queue and XFlush was
1327 * called. Nothing to do */
1331 /* pointer has moved off screen */
1332 return True;
1335 /* Helper function for __handle_focus_raise_click(). */
1336 static void __check_click_to_focus_or_raise(
1337 hfrc_ret_t *ret_args, const exec_context_t *exc)
1339 FvwmWindow * const fw = exc->w.fw;
1340 const XEvent *te = exc->x.etrigger;
1341 struct
1343 unsigned is_client_click : 1;
1344 unsigned is_focused : 1;
1345 } f;
1347 f.is_focused = !!focus_is_focused(fw);
1348 f.is_client_click = (exc->w.wcontext == C_WINDOW ||
1349 exc->w.wcontext == C_EWMH_DESKTOP);
1350 /* check if we need to raise and/or focus the window */
1351 ret_args->do_focus = focus_query_click_to_focus(fw, exc->w.wcontext);
1352 if (f.is_client_click && !ret_args->do_focus &&
1353 !f.is_focused && FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
1354 !fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
1356 /* Give the window a chance to to take focus itself */
1357 ret_args->do_focus = 1;
1359 if (ret_args->do_focus && focus_is_focused(fw))
1361 ret_args->do_focus = 0;
1363 ret_args->do_raise =
1364 focus_query_click_to_raise(fw, f.is_focused, exc->w.wcontext);
1365 #define EXPERIMENTAL_ROU_HANDLING_V2
1366 #ifdef EXPERIMENTAL_ROU_HANDLING_V2
1367 /* RBW -- Dang! This works without the one in HandleEnterNotify! */
1368 if (ret_args->do_raise && is_on_top_of_layer_and_above_unmanaged(fw))
1369 #else
1370 if (ret_args->do_raise && is_on_top_of_layer(fw))
1371 #endif
1373 ret_args->do_raise = 0;
1375 if ((ret_args->do_focus &&
1376 FP_DO_IGNORE_FOCUS_CLICK_MOTION(FW_FOCUS_POLICY(fw))) ||
1377 (ret_args->do_raise &&
1378 FP_DO_IGNORE_RAISE_CLICK_MOTION(FW_FOCUS_POLICY(fw))))
1380 /* Pass further events to the application and check if a button
1381 * release or motion event occurs next. If we don't do this
1382 * here, the pointer will seem to be frozen in
1383 * __test_for_motion(). */
1384 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1385 if (__test_for_motion(te->xbutton.x_root, te->xbutton.y_root))
1387 /* the pointer was moved, process event normally */
1388 ret_args->do_focus = 0;
1389 ret_args->do_raise = 0;
1392 if (ret_args->do_focus || ret_args->do_raise)
1394 if (!((ret_args->do_focus &&
1395 FP_DO_ALLOW_FUNC_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1396 (ret_args->do_raise &&
1397 FP_DO_ALLOW_FUNC_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1399 ret_args->do_forbid_function = 1;
1401 if (!((ret_args->do_focus &&
1402 FP_DO_PASS_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1403 (ret_args->do_raise &&
1404 FP_DO_PASS_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1406 ret_args->do_swallow_click = 1;
1410 return;
1413 /* Finds out if the click on a window must be used to focus or raise it. */
1414 static void __handle_focus_raise_click(
1415 hfrc_ret_t *ret_args, const exec_context_t *exc)
1417 memset(ret_args, 0, sizeof(*ret_args));
1418 if (exc->w.fw == NULL)
1420 return;
1422 /* check for proper click button and modifiers*/
1423 if (FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) != 0 &&
1424 !(FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) &
1425 (1 << (exc->x.etrigger->xbutton.button - 1))))
1427 /* wrong button, handle click normally */
1428 return;
1430 else if (FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw)) !=
1431 FPOL_ANY_MODIFIER &&
1432 MaskUsedModifiers(
1433 FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw))) !=
1434 MaskUsedModifiers(exc->x.etrigger->xbutton.state))
1436 /* right button but wrong modifiers, handle click normally */
1437 return;
1439 else
1441 __check_click_to_focus_or_raise(ret_args, exc);
1444 return;
1447 /* Helper function for HandleButtonPress */
1448 static Bool __is_bpress_window_handled(const exec_context_t *exc)
1450 Window eventw;
1451 const XEvent *te = exc->x.etrigger;
1453 if (exc->w.fw == NULL)
1455 if ((te->xbutton.window != Scr.Root ||
1456 te->xbutton.subwindow != None) &&
1457 !is_pan_frame(te->xbutton.window))
1459 /* Ignore events in unmanaged windows or subwindows of
1460 * a client */
1461 return False;
1463 else
1465 return True;
1468 eventw = (te->xbutton.subwindow != None &&
1469 te->xany.window != FW_W(exc->w.fw)) ?
1470 te->xbutton.subwindow : te->xany.window;
1471 if (is_frame_hide_window(eventw) || eventw == FW_W_FRAME(exc->w.fw))
1473 return False;
1475 if (!XGetGeometry(
1476 dpy, eventw, &JunkRoot, &JunkX, &JunkY,
1477 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
1478 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1480 /* The window has already died. */
1481 return False;
1484 return True;
1487 /* Helper function for __handle_bpress_on_managed */
1488 static Bool __handle_click_to_focus(const exec_context_t *exc)
1490 fpol_set_focus_by_t set_by;
1492 switch (exc->w.wcontext)
1494 case C_WINDOW:
1495 case C_EWMH_DESKTOP:
1496 set_by = FOCUS_SET_BY_CLICK_CLIENT;
1497 break;
1498 case C_ICON:
1499 set_by = FOCUS_SET_BY_CLICK_ICON;
1500 break;
1501 default:
1502 set_by = FOCUS_SET_BY_CLICK_DECOR;
1503 break;
1505 SetFocusWindow(exc->w.fw, True, set_by);
1506 focus_grab_buttons(exc->w.fw);
1507 if (focus_is_focused(exc->w.fw) && !IS_ICONIFIED(exc->w.fw))
1509 border_draw_decorations(
1510 exc->w.fw, PART_ALL, True, True, CLEAR_ALL, NULL,
1511 NULL);
1514 return focus_is_focused(exc->w.fw);
1517 /* Helper function for __handle_bpress_on_managed */
1518 static Bool __handle_click_to_raise(const exec_context_t *exc)
1520 Bool rc = False;
1521 int is_focused;
1523 is_focused = focus_is_focused(exc->w.fw);
1524 if (focus_query_click_to_raise(exc->w.fw, is_focused, True))
1526 rc = True;
1529 return rc;
1532 /* Helper function for HandleButtonPress */
1533 static void __handle_bpress_stroke(void)
1535 STROKE_CODE(stroke_init());
1536 STROKE_CODE(send_motion = True);
1538 return;
1541 /* Helper function for __handle_bpress_on_managed */
1542 static Bool __handle_bpress_action(
1543 const exec_context_t *exc, char *action)
1545 window_parts part;
1546 Bool do_force;
1547 Bool rc = False;
1549 if (!action || *action == 0)
1551 PressedW = None;
1552 return False;
1554 /* draw pressed in decorations */
1555 part = border_context_to_parts(exc->w.wcontext);
1556 do_force = (part & PART_TITLEBAR) ? True : False;
1557 border_draw_decorations(
1558 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1559 CLEAR_ALL, NULL, NULL);
1560 /* execute the action */
1561 if (IS_ICONIFIED(exc->w.fw))
1563 /* release the pointer since it can't do harm over an icon */
1564 XAllowEvents(dpy, AsyncPointer, CurrentTime);
1566 execute_function(NULL, exc, action, 0);
1567 if (exc->w.wcontext != C_WINDOW && exc->w.wcontext != C_NO_CONTEXT)
1569 WaitForButtonsUp(True);
1570 rc = True;
1572 /* redraw decorations */
1573 PressedW = None;
1574 if (check_if_fvwm_window_exists(exc->w.fw))
1576 part = border_context_to_parts(exc->w.wcontext);
1577 do_force = (part & PART_TITLEBAR) ? True : False;
1578 border_draw_decorations(
1579 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1580 CLEAR_ALL, NULL, NULL);
1583 return rc;
1586 /* Handles button presses on the root window. */
1587 static void __handle_bpress_on_root(const exec_context_t *exc)
1589 char *action;
1591 PressedW = None;
1592 __handle_bpress_stroke();
1593 /* search for an appropriate mouse binding */
1594 action = CheckBinding(
1595 Scr.AllBindings, STROKE_ARG(0) exc->x.etrigger->xbutton.button,
1596 exc->x.etrigger->xbutton.state, GetUnusedModifiers(), C_ROOT,
1597 BIND_BUTTONPRESS, NULL, NULL);
1598 if (action && *action)
1600 const exec_context_t *exc2;
1601 exec_context_changes_t ecc;
1603 ecc.w.wcontext = C_ROOT;
1604 exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
1605 execute_function(NULL, exc2, action, 0);
1606 exc_destroy_context(exc2);
1607 WaitForButtonsUp(True);
1609 else
1611 /* do gnome buttonpress forwarding if win == root */
1612 GNOME_ProxyButtonEvent(exc->x.etrigger);
1615 return;
1618 /* Handles button presses on unmanaged windows */
1619 static void __handle_bpress_on_unmanaged(const exec_context_t *exc)
1621 /* Pass the event to the application. */
1622 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1623 XFlush(dpy);
1625 return;
1628 /* Handles button presses on managed windows */
1629 static void __handle_bpress_on_managed(const exec_context_t *exc)
1631 char *action;
1632 hfrc_ret_t f;
1633 FvwmWindow * const fw = exc->w.fw;
1634 XEvent *e;
1636 e = exc->x.etrigger;
1637 /* Now handle click to focus and click to raise. */
1638 __handle_focus_raise_click(&f, exc);
1639 PressedW = (f.do_forbid_function) ? None : exc->w.w;
1640 if (f.do_focus)
1642 if (!__handle_click_to_focus(exc))
1644 /* Window didn't accept the focus; pass the click to
1645 * the application. */
1646 f.do_swallow_click = 0;
1649 if (f.do_raise)
1651 if (__handle_click_to_raise(exc) == True)
1653 /* We can't raise the window immediately because the
1654 * action bound to the click might be "Lower" or
1655 * "RaiseLower". So mark the window as scheduled to be
1656 * raised after the binding is executed. Functions that
1657 * modify the stacking order will reset this flag. */
1658 SET_SCHEDULED_FOR_RAISE(fw, 1);
1661 /* handle bindings */
1662 if (!f.do_forbid_function)
1664 /* stroke bindings */
1665 __handle_bpress_stroke();
1666 /* mouse bindings */
1667 action = CheckBinding(
1668 Scr.AllBindings, STROKE_ARG(0) e->xbutton.button,
1669 e->xbutton.state, GetUnusedModifiers(),
1670 exc->w.wcontext, BIND_BUTTONPRESS, &fw->class,
1671 fw->name.name);
1672 if (__handle_bpress_action(exc, action))
1674 f.do_swallow_click = 1;
1677 /* raise the window */
1678 if (IS_SCHEDULED_FOR_RAISE(fw))
1680 /* Now that we know the action did not restack the window we
1681 * can raise it.
1682 * dv (10-Aug-2002): We can safely raise the window after
1683 * redrawing it since all the decorations are drawn in the
1684 * window background and no Expose event is generated. */
1685 RaiseWindow(fw, False);
1686 SET_SCHEDULED_FOR_RAISE(fw, 0);
1688 /* clean up */
1689 if (!f.do_swallow_click)
1691 /* pass the click to the application */
1692 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1693 XFlush(dpy);
1695 else if (f.do_focus || f.do_raise)
1697 WaitForButtonsUp(True);
1700 return;
1703 /* restore focus stolen by unmanaged */
1704 static void __refocus_stolen_focus_win(const evh_args_t *ea)
1706 FOCUS_SET(Scr.StolenFocusWin);
1707 ea->exc->x.etrigger->xfocus.window = Scr.StolenFocusWin;
1708 ea->exc->x.etrigger->type = FocusIn;
1709 Scr.UnknownWinFocused = None;
1710 Scr.StolenFocusWin = None;
1711 dispatch_event(ea->exc->x.etrigger);
1713 return;
1716 /* ---------------------------- event handlers ----------------------------- */
1718 void HandleButtonPress(const evh_args_t *ea)
1720 DBUG("HandleButtonPress", "Routine Entered");
1722 GrabEm(CRS_NONE, GRAB_PASSIVE);
1723 if (__is_bpress_window_handled(ea->exc) == False)
1725 __handle_bpress_on_unmanaged(ea->exc);
1727 else if (ea->exc->w.fw != NULL)
1729 __handle_bpress_on_managed(ea->exc);
1731 else
1733 __handle_bpress_on_root(ea->exc);
1735 UngrabEm(GRAB_PASSIVE);
1737 return;
1740 #ifdef HAVE_STROKE
1741 void HandleButtonRelease(const evh_args_t *ea)
1743 char *action;
1744 char *name;
1745 int real_modifier;
1746 const XEvent *te = ea->exc->x.etrigger;
1747 XClassHint *class;
1749 DBUG("HandleButtonRelease", "Routine Entered");
1750 send_motion = False;
1751 stroke_trans (sequence);
1752 DBUG("HandleButtonRelease",sequence);
1753 /* Allows modifier to work (Only R context works here). */
1754 real_modifier = te->xbutton.state - (1 << (7 + te->xbutton.button));
1755 if (ea->exc->w.fw == NULL)
1757 class = NULL;
1758 name = NULL;
1760 else
1762 class = &ea->exc->w.fw->class;
1763 name = ea->exc->w.fw->name.name;
1765 /* need to search for an appropriate stroke binding */
1766 action = CheckBinding(
1767 Scr.AllBindings, sequence, te->xbutton.button, real_modifier,
1768 GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE,
1769 class, name);
1770 /* got a match, now process it */
1771 if (action != NULL && (action[0] != 0))
1773 execute_function(NULL, ea->exc, action, 0);
1774 WaitForButtonsUp(True);
1776 else
1779 * do gnome buttonpress forwarding if win == root
1781 if (Scr.Root == te->xany.window)
1783 GNOME_ProxyButtonEvent(te);
1787 return;
1789 #endif /* HAVE_STROKE */
1791 void HandleClientMessage(const evh_args_t *ea)
1793 const XEvent *te = ea->exc->x.etrigger;
1794 FvwmWindow * const fw = ea->exc->w.fw;
1796 DBUG("HandleClientMessage", "Routine Entered");
1798 /* Process GNOME and EWMH Messages */
1799 if (GNOME_ProcessClientMessage(ea->exc))
1801 return;
1803 else if (EWMH_ProcessClientMessage(ea->exc))
1805 return;
1808 /* handle deletion of tear out menus */
1809 if (fw && IS_TEAR_OFF_MENU(fw) && te->xclient.format == 32 &&
1810 te->xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
1812 menu_close_tear_off_menu(fw);
1813 return;
1816 if (te->xclient.message_type == _XA_WM_CHANGE_STATE &&
1817 fw && te->xclient.data.l[0] == IconicState && !IS_ICONIFIED(fw))
1819 const exec_context_t *exc;
1820 exec_context_changes_t ecc;
1822 ecc.w.wcontext = C_WINDOW;
1823 exc = exc_clone_context(ea->exc, &ecc, ECC_WCONTEXT);
1824 execute_function(NULL, exc, "Iconify", 0);
1825 exc_destroy_context(exc);
1826 return;
1829 /* FIXME: Is this safe enough ? I guess if clients behave
1830 * according to ICCCM and send these messages only if they
1831 * grabbed the pointer, it is OK */
1833 extern Atom _XA_WM_COLORMAP_NOTIFY;
1834 if (te->xclient.message_type == _XA_WM_COLORMAP_NOTIFY)
1836 set_client_controls_colormaps(te->xclient.data.l[1]);
1837 return;
1841 /* CKH - if we get here, it was an unknown client message, so send
1842 * it to the client if it was in a window we know about. I'm not so
1843 * sure this should be done or not, since every other window manager
1844 * I've looked at doesn't. But it might be handy for a free drag and
1845 * drop setup being developed for Linux. */
1846 /* TA: 20091231 - But this confuses QT Drag and Drop since it handles
1847 * processing XSendEvents in an odd order. For now, workaround this
1848 * by using a BugOpts option.
1850 if (fw)
1852 if ((!Scr.bo.do_enable_qt_drag_n_drop_workaround) &&
1853 (te->xclient.window != FW_W(fw)))
1855 XEvent e;
1857 e = *te;
1858 e.xclient.window = FW_W(fw);
1859 FSendEvent(dpy, FW_W(fw), False, NoEventMask, &e);
1864 void HandleColormapNotify(const evh_args_t *ea)
1866 colormap_handle_colormap_notify(ea);
1868 return;
1871 void HandleConfigureRequest(const evh_args_t *ea)
1873 const XEvent *te = ea->exc->x.etrigger;
1874 XConfigureRequestEvent cre;
1875 FvwmWindow *fw = ea->exc->w.fw;
1877 DBUG("HandleConfigureRequest", "Routine Entered");
1879 cre = te->xconfigurerequest;
1880 /* te->xany.window is te->.xconfigurerequest.parent, so the context
1881 * window may be wrong. */
1882 if (XFindContext(dpy, cre.window, FvwmContext, (caddr_t *)&fw) ==
1883 XCNOENT)
1885 fw = NULL;
1887 __handle_configure_request(cre, ea, fw, False, ForgetGravity);
1889 return;
1892 void HandleDestroyNotify(const evh_args_t *ea)
1894 DBUG("HandleDestroyNotify", "Routine Entered");
1896 destroy_window(ea->exc->w.fw);
1897 EWMH_ManageKdeSysTray(
1898 ea->exc->x.etrigger->xdestroywindow.window,
1899 ea->exc->x.etrigger->type);
1900 EWMH_WindowDestroyed();
1901 GNOME_SetClientList();
1903 return;
1906 #define DEBUG_ENTERNOTIFY 0
1907 #if DEBUG_ENTERNOTIFY
1908 static int ecount=0;
1909 #define ENTER_DBG(x) fprintf x;
1910 #else
1911 #define ENTER_DBG(x)
1912 #endif
1913 void HandleEnterNotify(const evh_args_t *ea)
1915 const XEnterWindowEvent *ewp;
1916 XEvent d;
1917 FvwmWindow *sf;
1918 static Bool is_initial_ungrab_pending = True;
1919 Bool is_tear_off_menu;
1920 const XEvent *te = ea->exc->x.etrigger;
1921 FvwmWindow * const fw = ea->exc->w.fw;
1923 DBUG("HandleEnterNotify", "Routine Entered");
1924 ewp = &te->xcrossing;
1925 ENTER_DBG((stderr, "++++++++ en (%d): fw 0x%08x w 0x%08x sw 0x%08xmode 0x%x detail 0x%x '%s'\n", ++ecount, (int)fw, (int)ewp->window, (int)ewp->subwindow, ewp->mode, ewp->detail, fw?fw->visible_name:"(none)"));
1927 if (
1928 ewp->window == Scr.Root &&
1929 ewp->detail == NotifyInferior && ewp->mode == NotifyNormal)
1931 /* pointer left subwindow */
1932 BroadcastPacket(
1933 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1934 (long)NULL);
1936 else if (
1937 ewp->window == Scr.Root &&
1938 ewp->detail == NotifyNonlinearVirtual)
1940 /* pointer entered screen */
1941 BroadcastPacket(
1942 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1943 (long)NULL);
1945 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
1947 if (fw && !IS_ICONIFIED(fw) && ewp->window == FW_W(fw))
1949 InstallWindowColormaps(fw);
1951 else
1953 /* make sure its for one of our windows */
1954 /* handle a subwindow cmap */
1955 InstallWindowColormaps(NULL);
1958 else if (!fw)
1960 EnterSubWindowColormap(ewp->window);
1962 if (Scr.flags.is_wire_frame_displayed)
1964 ENTER_DBG((stderr, "en: exit: iwfd\n"));
1965 /* Ignore EnterNotify events while a window is resized or moved
1966 * as a wire frame; otherwise the window list may be screwed
1967 * up. */
1968 return;
1970 if (fw)
1972 if (ewp->window != FW_W_FRAME(fw) &&
1973 ewp->window != FW_W_PARENT(fw) &&
1974 ewp->window != FW_W(fw) &&
1975 ewp->window != FW_W_ICON_TITLE(fw) &&
1976 ewp->window != FW_W_ICON_PIXMAP(fw))
1978 /* Ignore EnterNotify that received by any of the sub
1979 * windows that don't handle this event. unclutter
1980 * triggers these events sometimes, re focusing an
1981 * unfocused window under the pointer */
1982 ENTER_DBG((stderr, "en: exit: funny window\n"));
1983 return;
1986 if (Scr.focus_in_pending_window != NULL)
1988 ENTER_DBG((stderr, "en: exit: fipw\n"));
1989 /* Ignore EnterNotify event while we are waiting for a window to
1990 * receive focus via Focus or FlipFocus commands. */
1991 focus_grab_buttons(fw);
1992 return;
1994 if (ewp->mode == NotifyGrab)
1996 ENTER_DBG((stderr, "en: exit: NotifyGrab\n"));
1997 return;
1999 else if (ewp->mode == NotifyNormal)
2001 ENTER_DBG((stderr, "en: NotifyNormal\n"));
2002 if (ewp->detail == NotifyNonlinearVirtual &&
2003 ewp->focus == False && ewp->subwindow != None)
2005 /* This takes care of some buggy apps that forget that
2006 * one of their dialog subwindows has the focus after
2007 * popping up a selection list several times (ddd,
2008 * netscape). I'm not convinced that this does not
2009 * break something else. */
2010 ENTER_DBG((stderr, "en: NN: refreshing focus\n"));
2011 refresh_focus(fw);
2014 else if (ewp->mode == NotifyUngrab)
2016 ENTER_DBG((stderr, "en: NotifyUngrab\n"));
2017 /* Ignore events generated by grabbing or ungrabbing the
2018 * pointer. However, there is no way to prevent the client
2019 * application from handling this event and, for example,
2020 * grabbing the focus. This will interfere with functions that
2021 * transferred the focus to a different window. */
2022 if (is_initial_ungrab_pending)
2024 ENTER_DBG((stderr, "en: NU: initial ungrab pending (lgw = NULL)\n"));
2025 is_initial_ungrab_pending = False;
2026 xcrossing_last_grab_window = NULL;
2028 else
2030 if (ewp->detail == NotifyNonlinearVirtual &&
2031 ewp->focus == False && ewp->subwindow != None)
2033 /* see comment above */
2034 ENTER_DBG((stderr, "en: NU: refreshing focus\n"));
2035 refresh_focus(fw);
2037 if (fw && fw == xcrossing_last_grab_window)
2039 ENTER_DBG((stderr, "en: exit: NU: is last grab window\n"));
2040 if (ewp->window == FW_W_FRAME(fw) ||
2041 ewp->window == FW_W_ICON_TITLE(fw) ||
2042 ewp->window == FW_W_ICON_PIXMAP(fw))
2044 ENTER_DBG((stderr, "en: exit: NU: last grab window = NULL\n"));
2045 xcrossing_last_grab_window = NULL;
2047 focus_grab_buttons(fw);
2049 return;
2051 else if (fw)
2053 if (ewp->window != FW_W_FRAME(fw) &&
2054 ewp->window != FW_W_ICON_TITLE(fw) &&
2055 ewp->window != FW_W_ICON_PIXMAP(fw))
2057 ENTER_DBG((stderr, "en: exit: NU: not frame window\n"));
2058 focus_grab_buttons(fw);
2059 return;
2064 if (fw)
2066 is_initial_ungrab_pending = False;
2069 /* look for a matching leaveNotify which would nullify this EnterNotify
2072 * RBW - if we're in startup, this is a coerced focus, so we don't
2073 * want to save the event time, or exit prematurely.
2075 * Ignore LeaveNotify events for tear out menus - handled by menu code
2077 is_tear_off_menu =
2078 (fw && IS_TEAR_OFF_MENU(fw) && ewp->window == FW_W(fw));
2079 if (!fFvwmInStartup && !is_tear_off_menu &&
2080 FCheckTypedWindowEvent(dpy, ewp->window, LeaveNotify, &d))
2082 if (d.xcrossing.mode == NotifyNormal &&
2083 d.xcrossing.detail != NotifyInferior)
2085 ENTER_DBG((stderr, "en: exit: found LeaveNotify\n"));
2086 return;
2090 if (ewp->window == Scr.Root)
2092 FvwmWindow *lf = get_last_screen_focus_window();
2094 if (!Scr.flags.is_pointer_on_this_screen)
2096 Scr.flags.is_pointer_on_this_screen = 1;
2097 if (lf && lf != &Scr.FvwmRoot &&
2098 !FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(lf)))
2100 SetFocusWindow(lf, True, FOCUS_SET_FORCE);
2102 else if (lf != &Scr.FvwmRoot)
2104 ForceDeleteFocus();
2106 else
2108 /* This was the first EnterNotify event for the
2109 * root window - ignore */
2111 set_last_screen_focus_window(NULL);
2113 else if (!(sf = get_focus_window()) ||
2114 FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2116 DeleteFocus(True);
2118 else if (
2119 Scr.UnknownWinFocused != None && sf != NULL &&
2120 FW_W(sf) == Scr.StolenFocusWin)
2122 __refocus_stolen_focus_win(ea);
2124 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2126 InstallWindowColormaps(NULL);
2128 focus_grab_buttons(lf);
2129 return;
2131 else
2133 Scr.flags.is_pointer_on_this_screen = 1;
2136 /* An EnterEvent in one of the PanFrameWindows activates the Paging or
2137 an EdgeCommand. */
2138 if (is_pan_frame(ewp->window))
2140 char *edge_command = NULL;
2142 if (
2143 Scr.UnknownWinFocused != None &&
2144 (sf = get_focus_window()) != NULL &&
2145 FW_W(sf) == Scr.StolenFocusWin)
2147 __refocus_stolen_focus_win(ea);
2149 /* check for edge commands */
2150 if (ewp->window == Scr.PanFrameTop.win)
2152 edge_command = Scr.PanFrameTop.command;
2154 else if (ewp->window == Scr.PanFrameBottom.win)
2156 edge_command = Scr.PanFrameBottom.command;
2158 else if (ewp->window == Scr.PanFrameLeft.win)
2160 edge_command = Scr.PanFrameLeft.command;
2162 else if (ewp->window == Scr.PanFrameRight.win)
2164 edge_command = Scr.PanFrameRight.command;
2166 if (edge_command && ewp->mode == NotifyUngrab &&
2167 ewp->detail == NotifyAncestor)
2169 /* nothing */
2171 else if (edge_command)
2173 execute_function(NULL, ea->exc, edge_command, 0);
2175 else
2177 /* no edge command for this pan frame - so we do
2178 * HandlePaging */
2179 int delta_x = 0;
2180 int delta_y = 0;
2181 XEvent e;
2183 /* this was in the HandleMotionNotify before, HEDU */
2184 Scr.flags.is_pointer_on_this_screen = 1;
2185 e = *te;
2186 HandlePaging(
2187 &e, Scr.EdgeScrollX, Scr.EdgeScrollY, &JunkX,
2188 &JunkY, &delta_x, &delta_y, True, True, False,
2189 Scr.ScrollDelay);
2190 return;
2193 if (!fw)
2195 return;
2197 if (IS_EWMH_DESKTOP(FW_W(fw)))
2199 BroadcastPacket(
2200 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
2201 (long)NULL);
2202 return;
2204 if (ewp->window == FW_W_FRAME(fw) ||
2205 ewp->window == FW_W_ICON_TITLE(fw) ||
2206 ewp->window == FW_W_ICON_PIXMAP(fw))
2208 BroadcastPacket(
2209 MX_ENTER_WINDOW, 3, (long)FW_W(fw),
2210 (long)FW_W_FRAME(fw), (unsigned long)fw);
2212 sf = get_focus_window();
2213 if (sf && fw != sf && FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2215 ENTER_DBG((stderr, "en: delete focus\n"));
2216 DeleteFocus(True);
2218 focus_grab_buttons(fw);
2219 if (FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
2221 ENTER_DBG((stderr, "en: set mousey focus\n"));
2222 if (ewp->window == FW_W(fw))
2224 /* Event is for the client window...*/
2225 #ifndef EXPERIMENTAL_ROU_HANDLING_V2
2226 /* RBW -- This may still be needed at times, I'm not
2227 *sure yet. */
2228 SetFocusWindowClientEntered(
2229 fw, True, FOCUS_SET_BY_ENTER);
2230 #else
2231 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2232 #endif
2234 else
2236 /* Event is for the frame...*/
2237 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2240 else if (focus_is_focused(fw) && focus_does_accept_input_focus(fw))
2242 /* We have to refresh the focus window here in case we left the
2243 * focused fvwm window. Motif apps may lose the input focus
2244 * otherwise. But do not try to refresh the focus of
2245 * applications that want to handle it themselves. */
2246 focus_force_refresh_focus(fw);
2248 else if (sf != fw)
2250 /* Give the window a chance to grab the buttons needed for
2251 * raise-on-click */
2252 focus_grab_buttons(sf);
2254 if (
2255 Scr.UnknownWinFocused != None && sf != NULL &&
2256 FW_W(sf) == Scr.StolenFocusWin)
2258 __refocus_stolen_focus_win(ea);
2260 /* We get an EnterNotify with mode == UnGrab when fvwm releases the
2261 * grab held during iconification. We have to ignore this, or icon
2262 * title will be initially raised. */
2263 if (IS_ICONIFIED(fw) && (ewp->mode == NotifyNormal) &&
2264 (ewp->window == FW_W_ICON_PIXMAP(fw) ||
2265 ewp->window == FW_W_ICON_TITLE(fw)) &&
2266 FW_W_ICON_PIXMAP(fw) != None)
2268 SET_ICON_ENTERED(fw, 1);
2269 DrawIconWindow(fw, True, False, False, False, NULL);
2271 /* Check for tear off menus */
2272 if (is_tear_off_menu)
2274 menu_enter_tear_off_menu(ea->exc);
2277 return;
2280 void HandleExpose(const evh_args_t *ea)
2282 XEvent e;
2283 FvwmWindow * const fw = ea->exc->w.fw;
2285 e = *ea->exc->x.etrigger;
2286 #if 0
2287 /* This doesn't work well. Sometimes, the expose count is zero although
2288 * dozens of expose events are pending. This happens all the time
2289 * during a shading animation. Simply flush expose events
2290 * unconditionally. */
2291 if (e.xexpose.count != 0)
2293 flush_accumulate_expose(e.xexpose.window, &e);
2295 #else
2296 flush_accumulate_expose(e.xexpose.window, &e);
2297 #endif
2298 if (fw == NULL)
2300 return;
2302 if (e.xany.window == FW_W_ICON_TITLE(fw) ||
2303 e.xany.window == FW_W_ICON_PIXMAP(fw))
2305 DrawIconWindow(fw, True, True, False, False, &e);
2306 return;
2308 else if (IS_TEAR_OFF_MENU(fw) && e.xany.window == FW_W(fw))
2310 /* refresh the contents of the torn out menu */
2311 menu_expose(&e, NULL);
2314 return;
2317 void HandleFocusIn(const evh_args_t *ea)
2319 XEvent d;
2320 Window w = None;
2321 Window focus_w = None;
2322 Window focus_fw = None;
2323 Pixel fc = 0;
2324 Pixel bc = 0;
2325 FvwmWindow *ffw_old = get_focus_window();
2326 FvwmWindow *sf;
2327 Bool do_force_broadcast = False;
2328 Bool is_unmanaged_focused = False;
2329 static Window last_focus_w = None;
2330 static Window last_focus_fw = None;
2331 static Bool was_nothing_ever_focused = True;
2332 FvwmWindow *fw = ea->exc->w.fw;
2334 DBUG("HandleFocusIn", "Routine Entered");
2336 Scr.focus_in_pending_window = NULL;
2337 /* This is a hack to make the PointerKey command work */
2338 if (ea->exc->x.etrigger->xfocus.detail != NotifyPointer)
2340 /**/
2341 w = ea->exc->x.etrigger->xany.window;
2343 while (FCheckTypedEvent(dpy, FocusIn, &d))
2345 /* dito */
2346 if (d.xfocus.detail != NotifyPointer)
2348 /**/
2349 w = d.xany.window;
2352 /* dito */
2353 if (w == None)
2355 return;
2357 /**/
2358 if (XFindContext(dpy, w, FvwmContext, (caddr_t *) &fw) == XCNOENT)
2360 fw = NULL;
2363 Scr.UnknownWinFocused = None;
2364 if (!fw)
2366 if (w != Scr.NoFocusWin)
2368 Scr.UnknownWinFocused = w;
2369 Scr.StolenFocusWin =
2370 (ffw_old != NULL) ? FW_W(ffw_old) : None;
2371 focus_w = w;
2372 is_unmanaged_focused = True;
2374 /* Only show a non-focused window as focused,
2375 * if the focus is on unmanaged and flickering qt dialogs
2376 * workaround is on. */
2377 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2378 !is_unmanaged_focused)
2380 border_draw_decorations(
2381 Scr.Hilite, PART_ALL, False, True, CLEAR_ALL,
2382 NULL, NULL);
2383 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2385 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2387 InstallWindowColormaps(Scr.Hilite);
2389 else
2391 InstallWindowColormaps(NULL);
2395 /* Not very useful if no window that fvwm and its modules know
2396 * about has the focus. */
2397 fc = GetColor(DEFAULT_FORE_COLOR);
2398 bc = GetColor(DEFAULT_BACK_COLOR);
2400 else if (fw != Scr.Hilite ||
2401 /* domivogt (16-May-2000): This check is necessary to force
2402 * sending a M_FOCUS_CHANGE packet after an unmanaged window
2403 * was focused. Otherwise fvwm would believe that Scr.Hilite
2404 * was still focused and not send any info to the modules. */
2405 last_focus_fw == None ||
2406 IS_FOCUS_CHANGE_BROADCAST_PENDING(fw))
2408 do_force_broadcast = IS_FOCUS_CHANGE_BROADCAST_PENDING(fw);
2409 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 0);
2410 if (fw != Scr.Hilite)
2412 border_draw_decorations(
2413 fw, PART_ALL, True, True, CLEAR_ALL, NULL,
2414 NULL);
2416 focus_w = FW_W(fw);
2417 focus_fw = FW_W_FRAME(fw);
2418 fc = fw->hicolors.fore;
2419 bc = fw->hicolors.back;
2420 set_focus_window(fw);
2421 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2423 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2425 InstallWindowColormaps(Scr.Hilite);
2427 else
2429 InstallWindowColormaps(NULL);
2433 else
2435 return;
2437 if (was_nothing_ever_focused || last_focus_fw == None ||
2438 focus_w != last_focus_w || focus_fw != last_focus_fw ||
2439 do_force_broadcast)
2441 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2442 !is_unmanaged_focused)
2444 BroadcastPacket(
2445 M_FOCUS_CHANGE, 5, (long)focus_w,
2446 (long)focus_fw,
2447 (unsigned long)IsLastFocusSetByMouse(),
2448 (long)fc, (long)bc);
2449 EWMH_SetActiveWindow(focus_w);
2451 last_focus_w = focus_w;
2452 last_focus_fw = focus_fw;
2453 was_nothing_ever_focused = False;
2455 if ((sf = get_focus_window()) != ffw_old)
2457 focus_grab_buttons(sf);
2458 focus_grab_buttons(ffw_old);
2461 return;
2464 void HandleFocusOut(const evh_args_t *ea)
2466 if (Scr.UnknownWinFocused != None && Scr.StolenFocusWin != None &&
2467 ea->exc->x.etrigger->xfocus.window == Scr.UnknownWinFocused)
2469 __refocus_stolen_focus_win(ea);
2472 return;
2475 void __handle_key(const evh_args_t *ea, Bool is_press)
2477 char *action;
2478 FvwmWindow *sf;
2479 KeyCode kc;
2480 int kcontext;
2481 const XEvent *te = ea->exc->x.etrigger;
2482 const FvwmWindow * const fw = ea->exc->w.fw;
2483 Bool is_second_binding;
2484 const XClassHint *winClass1, *winClass2;
2485 XClassHint tmp;
2486 char *name1, *name2;
2487 const exec_context_t *exc;
2488 exec_context_changes_t ecc;
2490 PressedW = None;
2492 /* Here's a real hack - some systems have two keys with the
2493 * same keysym and different keycodes. This converts all
2494 * the cases to one keycode. */
2495 kc = XKeysymToKeycode(dpy, XKeycodeToKeysym(dpy, te->xkey.keycode, 0));
2497 /* Check if there is something bound to the key */
2499 sf = get_focus_window();
2500 if (sf == NULL)
2502 tmp.res_name = tmp.res_class = name1 = "root";
2503 winClass1 = &tmp;
2504 kcontext = C_ROOT;
2506 else
2508 winClass1 = &sf->class;
2509 name1 = sf->name.name;
2510 kcontext = (sf == fw ? ea->exc->w.wcontext : C_WINDOW);
2513 if (fw == NULL)
2515 tmp.res_name = tmp.res_class = name2 = "root";
2516 winClass2 = &tmp;
2518 else
2520 winClass2 = &fw->class;
2521 name2 = fw->name.name;
2523 /* Searching the binding list with a different 'type' value
2524 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
2525 * The different context value does though. */
2526 action = CheckTwoBindings(
2527 &is_second_binding, Scr.AllBindings, STROKE_ARG(0) kc,
2528 te->xkey.state, GetUnusedModifiers(), kcontext, BIND_KEYPRESS,
2529 winClass1, name1, ea->exc->w.wcontext, BIND_PKEYPRESS,
2530 winClass2, name2);
2532 if (action != NULL)
2534 if (!is_press)
2536 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2537 return;
2539 exc = ea->exc;
2540 if (is_second_binding == False)
2542 ecc.w.fw = sf;
2543 ecc.w.wcontext = kcontext;
2544 exc = exc_clone_context(
2545 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
2547 execute_function(NULL, exc, action, 0);
2548 if (is_second_binding == False)
2550 exc_destroy_context(exc);
2552 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2553 return;
2556 /* if we get here, no function key was bound to the key. Send it
2557 * to the client if it was in a window we know about. */
2558 sf = get_focus_window();
2559 if (sf && te->xkey.window != FW_W(sf))
2561 XEvent e;
2563 e = *te;
2564 e.xkey.window = FW_W(sf);
2565 FSendEvent(
2566 dpy, e.xkey.window, False,
2567 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2569 else if (fw && te->xkey.window != FW_W(fw))
2571 XEvent e;
2573 e = *te;
2574 e.xkey.window = FW_W(fw);
2575 FSendEvent(
2576 dpy, e.xkey.window, False,
2577 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2579 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2581 return;
2584 void HandleKeyPress(const evh_args_t *ea)
2586 __handle_key(ea, True);
2589 void HandleKeyRelease(const evh_args_t *ea)
2591 __handle_key(ea, False);
2594 void HandleLeaveNotify(const evh_args_t *ea)
2596 const XLeaveWindowEvent *lwp;
2597 const XEvent *te = ea->exc->x.etrigger;
2598 FvwmWindow * const fw = ea->exc->w.fw;
2600 DBUG("HandleLeaveNotify", "Routine Entered");
2602 ENTER_DBG((stderr, "-------- ln (%d): fw 0x%08x w 0x%08x sw 0x%08x mode 0x%x detail 0x%x '%s'\n", ++ecount, (int)fw, (int)te->xcrossing.window, (int)te->xcrossing.subwindow, te->xcrossing.mode, te->xcrossing.detail, fw?fw->visible_name:"(none)"));
2603 lwp = &te->xcrossing;
2604 if (
2605 lwp->window == Scr.Root &&
2606 lwp->detail == NotifyInferior && lwp->mode == NotifyNormal)
2608 /* pointer entered subwindow */
2609 BroadcastPacket(
2610 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2611 (long)NULL);
2613 else if (
2614 lwp->window == Scr.Root &&
2615 lwp->detail == NotifyNonlinearVirtual)
2617 /* pointer left screen */
2618 BroadcastPacket(
2619 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2620 (long)NULL);
2622 /* Ignore LeaveNotify events while a window is resized or moved as a
2623 * wire frame; otherwise the window list may be screwed up. */
2624 if (Scr.flags.is_wire_frame_displayed)
2626 return;
2628 if (lwp->mode != NotifyNormal)
2630 /* Ignore events generated by grabbing or ungrabbing the
2631 * pointer. However, there is no way to prevent the client
2632 * application from handling this event and, for example,
2633 * grabbing the focus. This will interfere with functions that
2634 * transferred the focus to a different window. It is
2635 * necessary to check for LeaveNotify events on the client
2636 * window too in case buttons are not grabbed on it. */
2637 if (lwp->mode == NotifyGrab && fw &&
2638 (lwp->window == FW_W_FRAME(fw) ||
2639 lwp->window == FW_W(fw) ||
2640 lwp->window == FW_W_ICON_TITLE(fw) ||
2641 lwp->window == FW_W_ICON_PIXMAP(fw)))
2643 ENTER_DBG((stderr, "ln: *** lgw = 0x%08x\n", (int)fw));
2644 xcrossing_last_grab_window = fw;
2646 #ifdef FOCUS_EXPANDS_TITLE
2647 if (fw && IS_ICONIFIED(fw))
2649 SET_ICON_ENTERED(fw, 0);
2650 DrawIconWindow(
2651 fw, True, False, False, False, NULL);
2653 #endif
2654 return;
2656 /* CDE-like behaviour of raising the icon title if the icon
2657 gets the focus (in particular if the cursor is over the icon) */
2658 if (fw && IS_ICONIFIED(fw))
2660 SET_ICON_ENTERED(fw,0);
2661 DrawIconWindow(fw, True, False, False, False, NULL);
2664 /* An LeaveEvent in one of the PanFrameWindows activates
2665 an EdgeLeaveCommand. */
2666 if (is_pan_frame(lwp->window))
2668 char *edge_command_leave = NULL;
2670 /* check for edge commands */
2671 if (lwp->window == Scr.PanFrameTop.win)
2673 edge_command_leave = Scr.PanFrameTop.command_leave;
2675 else if (lwp->window == Scr.PanFrameBottom.win)
2677 edge_command_leave = Scr.PanFrameBottom.command_leave;
2679 else if (lwp->window == Scr.PanFrameLeft.win)
2681 edge_command_leave = Scr.PanFrameLeft.command_leave;
2683 else if (lwp->window == Scr.PanFrameRight.win)
2685 edge_command_leave = Scr.PanFrameRight.command_leave;
2687 if (edge_command_leave && lwp->mode == NotifyUngrab &&
2688 lwp->detail == NotifyAncestor)
2690 /* nothing */
2692 else if (edge_command_leave)
2694 execute_function(NULL, ea->exc, edge_command_leave, 0);
2699 /* If we leave the root window, then we're really moving
2700 * another screen on a multiple screen display, and we
2701 * need to de-focus and unhighlight to make sure that we
2702 * don't end up with more than one highlighted window at a time */
2703 if (lwp->window == Scr.Root &&
2704 /* domivogt (16-May-2000): added this test because somehow fvwm
2705 * sometimes gets a LeaveNotify on the root window although it is
2706 * single screen. */
2707 Scr.NumberOfScreens > 1)
2709 if (lwp->mode == NotifyNormal)
2711 if (lwp->detail != NotifyInferior)
2713 FvwmWindow *sf = get_focus_window();
2715 Scr.flags.is_pointer_on_this_screen = 0;
2716 set_last_screen_focus_window(sf);
2717 if (sf != NULL)
2719 DeleteFocus(True);
2721 if (Scr.Hilite != NULL)
2723 border_draw_decorations(
2724 Scr.Hilite, PART_ALL, False,
2725 True, CLEAR_ALL, NULL, NULL);
2730 else
2732 /* handle a subwindow cmap */
2733 LeaveSubWindowColormap(te->xany.window);
2735 if (fw != NULL &&
2736 (lwp->window == FW_W_FRAME(fw) ||
2737 lwp->window == FW_W_ICON_TITLE(fw) ||
2738 lwp->window == FW_W_ICON_PIXMAP(fw)))
2740 BroadcastPacket(
2741 MX_LEAVE_WINDOW, 3, (long)FW_W(fw),
2742 (long)FW_W_FRAME(fw), (unsigned long)fw);
2745 return;
2748 void HandleMapNotify(const evh_args_t *ea)
2750 Bool is_on_this_page = False;
2751 const XEvent *te = ea->exc->x.etrigger;
2752 FvwmWindow * const fw = ea->exc->w.fw;
2754 DBUG("HandleMapNotify", "Routine Entered");
2756 if (!fw)
2758 if (te->xmap.override_redirect == True &&
2759 te->xmap.window != Scr.NoFocusWin)
2761 XSelectInput(dpy, te->xmap.window, XEVMASK_ORW);
2762 XFlush(dpy);
2763 Scr.UnknownWinFocused = te->xmap.window;
2765 return;
2767 if (te->xmap.window == FW_W_FRAME(fw))
2769 /* Now that we know the frame is mapped after capturing the
2770 * window we do not need StructureNotifyMask events anymore. */
2771 XSelectInput(dpy, FW_W_FRAME(fw), XEVMASK_FRAMEW);
2772 XFlush(dpy);
2774 /* Except for identifying over-ride redirect window mappings, we
2775 * don't need or want windows associated with the
2776 * SubstructureNotifyMask */
2777 if (te->xmap.event != te->xmap.window)
2779 return;
2781 SET_MAP_PENDING(fw, 0);
2782 /* don't map if the event was caused by a de-iconify */
2783 if (IS_ICONIFY_PENDING(fw))
2785 return;
2788 /* Make sure at least part of window is on this page before giving it
2789 * focus... */
2790 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2793 * Need to do the grab to avoid race condition of having server send
2794 * MapNotify to client before the frame gets mapped; this is bad because
2795 * the client would think that the window has a chance of being viewable
2796 * when it really isn't.
2798 MyXGrabServer (dpy);
2799 if (FW_W_ICON_TITLE(fw))
2801 XUnmapWindow(dpy, FW_W_ICON_TITLE(fw));
2803 if (FW_W_ICON_PIXMAP(fw) != None)
2805 XUnmapWindow(dpy, FW_W_ICON_PIXMAP(fw));
2807 XMapSubwindows(dpy, FW_W_FRAME(fw));
2808 if (fw->Desk == Scr.CurrentDesk)
2810 XMapWindow(dpy, FW_W_FRAME(fw));
2812 if (IS_ICONIFIED(fw))
2814 BroadcastPacket(
2815 M_DEICONIFY, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2816 (unsigned long)fw);
2818 else
2820 BroadcastPacket(
2821 M_MAP, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2822 (unsigned long)fw);
2825 if (is_on_this_page &&
2826 focus_query_open_grab_focus(fw, get_focus_window()) == True)
2828 SetFocusWindow(fw, True, FOCUS_SET_FORCE);
2830 border_draw_decorations(
2831 fw, PART_ALL, (fw == get_focus_window()) ? True : False, True,
2832 CLEAR_ALL, NULL, NULL);
2833 MyXUngrabServer (dpy);
2834 SET_MAPPED(fw, 1);
2835 SET_ICONIFIED(fw, 0);
2836 SET_ICON_UNMAPPED(fw, 0);
2837 if (DO_ICONIFY_AFTER_MAP(fw))
2839 initial_window_options_t win_opts;
2841 /* finally, if iconification was requested before the window
2842 * was mapped, request it now. */
2843 memset(&win_opts, 0, sizeof(win_opts));
2844 Iconify(fw, &win_opts);
2845 SET_ICONIFY_AFTER_MAP(fw, 0);
2847 focus_grab_buttons_on_layer(fw->layer);
2849 return;
2852 void HandleMappingNotify(const evh_args_t *ea)
2854 XRefreshKeyboardMapping(&ea->exc->x.etrigger->xmapping);
2856 return;
2859 void HandleMapRequest(const evh_args_t *ea)
2861 DBUG("HandleMapRequest", "Routine Entered");
2863 if (fFvwmInStartup)
2865 /* Just map the damn thing, decorations are added later
2866 * in CaptureAllWindows. */
2867 XMapWindow(dpy, ea->exc->x.etrigger->xmaprequest.window);
2868 return;
2870 HandleMapRequestKeepRaised(ea, None, NULL, NULL);
2872 return;
2875 void HandleMapRequestKeepRaised(
2876 const evh_args_t *ea, Window KeepRaised, FvwmWindow *ReuseWin,
2877 initial_window_options_t *win_opts)
2879 Bool is_on_this_page = False;
2880 Bool is_new_window = False;
2881 FvwmWindow *tmp;
2882 FvwmWindow *sf;
2883 initial_window_options_t win_opts_bak;
2884 Window ew;
2885 FvwmWindow *fw;
2886 extern Bool Restarting;
2887 const char *initial_map_command;
2889 initial_map_command = NULL;
2890 if (win_opts == NULL)
2892 memset(&win_opts_bak, 0, sizeof(win_opts_bak));
2893 win_opts = &win_opts_bak;
2895 ew = ea->exc->w.w;
2896 if (ReuseWin == NULL)
2898 Window pw;
2900 pw = ea->exc->x.etrigger->xmaprequest.parent;
2901 if (XFindContext(dpy, ew, FvwmContext, (caddr_t *)&fw) ==
2902 XCNOENT)
2904 fw = NULL;
2906 if (fw != NULL && IS_MAP_PENDING(fw))
2908 /* The window is already going to be mapped, no need to
2909 * do that twice */
2910 return;
2913 else
2915 fw = ReuseWin;
2918 if (fw == NULL && EWMH_IsKdeSysTrayWindow(ew))
2920 /* This means that the window is swallowed by kicker and that
2921 * kicker restart or exit. As we should assume that kicker
2922 * restart we should return here, if not we go into trouble
2923 * ... */
2924 return;
2926 if (!win_opts->flags.do_override_ppos)
2928 XFlush(dpy);
2931 /* If the window has never been mapped before ... */
2932 if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
2934 check_if_event_args args;
2935 XEvent dummy;
2937 args.w = ew;
2938 args.do_return_true = True;
2939 args.do_return_true_cr = False;
2940 if (
2941 FCheckIfEvent(
2942 dpy, &dummy, test_withdraw_request,
2943 (XPointer)&args)) {
2944 /* The window is moved back to the WithdrawnState
2945 * immideately. Don't map it.
2947 * However, send make sure that a WM_STATE
2948 * PropertyNotify event is sent to the window.
2949 * QT needs this.
2951 Atom atype;
2952 int aformat;
2953 unsigned long nitems, bytes_remain;
2954 unsigned char *prop;
2956 if (
2957 XGetWindowProperty(
2958 dpy, ew, _XA_WM_STATE, 0L, 3L, False,
2959 _XA_WM_STATE, &atype, &aformat,
2960 &nitems,&bytes_remain,&prop)
2961 == Success)
2963 if (prop != NULL)
2965 XFree(prop);
2966 XDeleteProperty(dpy, ew, _XA_WM_STATE);
2968 else
2970 XPropertyEvent ev;
2971 ev.type = PropertyNotify;
2972 ev.display = dpy;
2973 ev.window = ew;
2974 ev.atom = _XA_WM_STATE;
2975 ev.time = fev_get_evtime();
2976 ev.state = PropertyDelete;
2977 FSendEvent(
2978 dpy, ew, True,
2979 PropertyChangeMask,
2980 (XEvent*)&ev);
2984 return;
2987 /* Add decorations. */
2988 fw = AddWindow(
2989 &initial_map_command, ea->exc, ReuseWin, win_opts);
2990 if (fw == AW_NO_WINDOW)
2992 return;
2994 else if (fw == AW_UNMANAGED)
2996 XMapWindow(dpy, ew);
2997 return;
2999 is_new_window = True;
3002 * Make sure at least part of window is on this page
3003 * before giving it focus...
3005 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3006 if (KeepRaised != None)
3008 XRaiseWindow(dpy, KeepRaised);
3010 /* If it's not merely iconified, and we have hints, use them. */
3012 if (IS_ICONIFIED(fw))
3014 /* If no hints, or currently an icon, just "deiconify" */
3015 DeIconify(fw);
3017 else if (IS_MAPPED(fw))
3019 /* the window is already mapped - fake a MapNotify event */
3020 fake_map_unmap_notify(fw, MapNotify);
3022 else
3024 int state;
3026 if (fw->wmhints && (fw->wmhints->flags & StateHint))
3028 state = fw->wmhints->initial_state;
3030 else
3032 state = NormalState;
3034 if (win_opts->initial_state != DontCareState)
3036 state = win_opts->initial_state;
3039 switch (state)
3041 case DontCareState:
3042 case NormalState:
3043 case InactiveState:
3044 default:
3045 MyXGrabServer(dpy);
3046 if (fw->Desk == Scr.CurrentDesk)
3048 Bool do_grab_focus;
3050 SET_MAP_PENDING(fw, 1);
3051 XMapWindow(dpy, FW_W_FRAME(fw));
3052 XMapWindow(dpy, FW_W(fw));
3053 SetMapStateProp(fw, NormalState);
3054 if (Scr.flags.is_map_desk_in_progress)
3056 do_grab_focus = False;
3058 else if (!is_on_this_page)
3060 do_grab_focus = False;
3062 else if (focus_query_open_grab_focus(
3063 fw, get_focus_window()) ==
3064 True)
3066 do_grab_focus = True;
3068 else
3070 do_grab_focus = False;
3072 if (do_grab_focus)
3074 SetFocusWindow(
3075 fw, True, FOCUS_SET_FORCE);
3077 else
3079 /* make sure the old focused window
3080 * still has grabbed all necessary
3081 * buttons. */
3082 focus_grab_buttons(
3083 get_focus_window());
3086 else
3088 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
3089 /* nope, this is forbidden by the ICCCM2 */
3090 XMapWindow(dpy, FW_W(fw));
3091 SetMapStateProp(fw, NormalState);
3092 #else
3093 /* Since we will not get a MapNotify, set the
3094 * IS_MAPPED flag manually. */
3095 SET_MAPPED(fw, 1);
3096 SetMapStateProp(fw, IconicState);
3097 /* fake that the window was mapped to allow
3098 * modules to swallow it */
3099 BroadcastPacket(
3100 M_MAP, 3, (long)FW_W(fw),
3101 (long)FW_W_FRAME(fw),
3102 (unsigned long)fw);
3103 #endif
3105 /* TA: 20090125: We *have* to handle
3106 * InitialMapCommand here and not in AddWindow() to
3107 * allow for correct timings when the window is truly
3108 * mapped. (c.f. things like Iconify.)
3111 /* TA: 20091212: But only do this when we're *not*
3112 * restarting -- the window is still mapped, but gets
3113 * recaptured -- we don't want to trigger this event
3114 * again. Otherwise we end up toggling the state of
3115 * the window in situations where the
3116 * InitialMapCommand is Iconify or Maximize, for
3117 * instance.
3119 if ((initial_map_command != NULL) &&
3120 (!Restarting && Scr.flags.are_windows_captured))
3122 execute_function_override_window(
3123 NULL, ea->exc,
3124 (char *)initial_map_command, 0, fw);
3126 MyXUngrabServer(dpy);
3127 break;
3129 case IconicState:
3130 if (is_new_window)
3132 /* the window will not be mapped - fake a
3133 * MapNotify and an UnmapNotify event. Can't
3134 * remember exactly why this is necessary, but
3135 * probably something w/ (de)iconify state
3136 * confusion. */
3137 fake_map_unmap_notify(fw, MapNotify);
3138 fake_map_unmap_notify(fw, UnmapNotify);
3140 if (win_opts->flags.is_iconified_by_parent ||
3141 ((tmp = get_transientfor_fvwmwindow(fw)) &&
3142 IS_ICONIFIED(tmp)))
3144 win_opts->flags.is_iconified_by_parent = 0;
3145 SET_ICONIFIED_BY_PARENT(fw, 1);
3147 if (USE_ICON_POSITION_HINT(fw) && fw->wmhints &&
3148 (fw->wmhints->flags & IconPositionHint))
3150 win_opts->default_icon_x = fw->wmhints->icon_x;
3151 win_opts->default_icon_y = fw->wmhints->icon_y;
3153 Iconify(fw, win_opts);
3154 break;
3157 if (IS_SHADED(fw))
3159 BroadcastPacket(
3160 M_WINDOWSHADE, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
3161 (unsigned long)fw);
3163 /* If the newly mapped window overlaps the focused window, make sure
3164 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3165 sf = get_focus_window();
3166 if (sf != NULL)
3168 focus_grab_buttons(sf);
3170 if (win_opts->flags.is_menu)
3172 SET_MAPPED(fw, 1);
3173 SET_MAP_PENDING(fw, 0);
3175 EWMH_SetClientList();
3176 EWMH_SetClientListStacking();
3177 GNOME_SetClientList();
3179 return;
3182 #ifdef HAVE_STROKE
3183 void HandleMotionNotify(const evh_args_t *ea)
3185 DBUG("HandleMotionNotify", "Routine Entered");
3187 if (send_motion == True)
3189 stroke_record(
3190 ea->exc->x.etrigger->xmotion.x,
3191 ea->exc->x.etrigger->xmotion.y);
3194 return;
3196 #endif /* HAVE_STROKE */
3198 void HandlePropertyNotify(const evh_args_t *ea)
3200 Bool OnThisPage = False;
3201 Bool has_icon_changed = False;
3202 Bool has_icon_pixmap_hint_changed = False;
3203 Bool has_icon_window_hint_changed = False;
3204 FlocaleNameString new_name = { NoName, NULL };
3205 int old_wmhints_flags;
3206 const XEvent *te = ea->exc->x.etrigger;
3207 char *urgency_action = NULL;
3208 FvwmWindow * const fw = ea->exc->w.fw;
3210 DBUG("HandlePropertyNotify", "Routine Entered");
3212 if (te->xproperty.window == Scr.Root &&
3213 te->xproperty.state == PropertyNewValue &&
3214 (te->xproperty.atom == _XA_XSETROOT_ID ||
3215 te->xproperty.atom == _XA_XROOTPMAP_ID))
3217 /* background change */
3218 /* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3219 * no property notify?). _XA_XROOTPMAP_ID is used by Esetroot
3220 * compatible program: the problem here is that with some
3221 * Esetroot compatible program we get the message _before_ the
3222 * background change. This is fixed with Esetroot 9.2 (not yet
3223 * released, 2002-01-14) */
3225 /* update icon window with some alpha and tear-off menu */
3226 FvwmWindow *t;
3228 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3230 int cs;
3231 int t_cs = -1;
3232 int b_cs = t->icon_background_cs;
3233 Bool draw_picture = False;
3234 Bool draw_title = False;
3236 /* redraw ParentRelative tear-off menu */
3237 menu_redraw_transparent_tear_off_menu(t, True);
3239 if (!IS_ICONIFIED(t) || IS_ICON_SUPPRESSED(t))
3241 continue;
3243 if (Scr.Hilite == t)
3245 if (t->icon_title_cs_hi >= 0)
3247 t_cs = cs = t->icon_title_cs_hi;
3249 else
3251 cs = t->cs_hi;
3254 else
3256 if (t->icon_title_cs >= 0)
3258 t_cs = cs = t->icon_title_cs;
3260 else
3262 cs = t->cs;
3265 if (t->icon_alphaPixmap != None ||
3266 (cs >= 0 &&
3267 Colorset[cs].icon_alpha_percent < 100) ||
3268 CSET_IS_TRANSPARENT_PR(b_cs) ||
3269 (!IS_ICON_SHAPED(t) &&
3270 t->icon_background_padding > 0))
3272 draw_picture = True;
3274 if (CSET_IS_TRANSPARENT_PR(t_cs))
3276 draw_title = True;
3278 if (draw_title || draw_picture)
3280 DrawIconWindow(
3281 t, draw_title, draw_picture, False,
3282 draw_picture, NULL);
3285 if (te->xproperty.atom == _XA_XROOTPMAP_ID)
3287 update_root_transparent_colorset(te->xproperty.atom);
3289 BroadcastPropertyChange(
3290 MX_PROPERTY_CHANGE_BACKGROUND, 0, 0, "");
3291 return;
3294 if (!fw)
3296 return;
3298 if (XGetGeometry(
3299 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3300 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
3301 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
3303 return;
3307 * Make sure at least part of window is on this page
3308 * before giving it focus...
3310 OnThisPage = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3312 switch (te->xproperty.atom)
3314 case XA_WM_TRANSIENT_FOR:
3315 flush_property_notify(XA_WM_TRANSIENT_FOR, FW_W(fw));
3316 if (setup_transientfor(fw) == True)
3318 RaiseWindow(fw, False);
3320 break;
3322 case XA_WM_NAME:
3323 flush_property_notify(XA_WM_NAME, FW_W(fw));
3324 if (HAS_EWMH_WM_NAME(fw))
3326 return;
3328 FlocaleGetNameProperty(XGetWMName, dpy, FW_W(fw), &new_name);
3329 if (new_name.name == NULL)
3331 FlocaleFreeNameProperty(&new_name);
3332 return;
3334 if (strlen(new_name.name) > MAX_WINDOW_NAME_LEN)
3336 /* limit to prevent hanging X server */
3337 (new_name.name)[MAX_WINDOW_NAME_LEN] = 0;
3339 if (fw->name.name && strcmp(new_name.name, fw->name.name) == 0)
3341 /* migo: some apps update their names every second */
3342 /* griph: make sure we don't free the property if it
3343 is THE same name */
3344 if (new_name.name != fw->name.name)
3346 FlocaleFreeNameProperty(&new_name);
3348 return;
3351 free_window_names(fw, True, False);
3352 fw->name = new_name;
3353 SET_NAME_CHANGED(fw, 1);
3354 if (fw->name.name == NULL)
3356 fw->name.name = NoName; /* must not happen */
3358 setup_visible_name(fw, False);
3359 BroadcastWindowIconNames(fw, True, False);
3361 /* fix the name in the title bar */
3362 if (!IS_ICONIFIED(fw))
3364 border_draw_decorations(
3365 fw, PART_TITLE, (Scr.Hilite == fw), True,
3366 CLEAR_ALL, NULL, NULL);
3368 EWMH_SetVisibleName(fw, False);
3370 * if the icon name is NoName, set the name of the icon to be
3371 * the same as the window
3373 if (!WAS_ICON_NAME_PROVIDED(fw))
3375 fw->icon_name = fw->name;
3376 setup_visible_name(fw, True);
3377 BroadcastWindowIconNames(fw, False, True);
3378 RedoIconName(fw);
3380 break;
3382 case XA_WM_ICON_NAME:
3383 flush_property_notify(XA_WM_ICON_NAME, FW_W(fw));
3384 if (HAS_EWMH_WM_ICON_NAME(fw))
3386 return;
3388 FlocaleGetNameProperty(
3389 XGetWMIconName, dpy, FW_W(fw), &new_name);
3390 if (new_name.name == NULL)
3392 FlocaleFreeNameProperty(&new_name);
3393 return;
3395 if (new_name.name && strlen(new_name.name) > MAX_ICON_NAME_LEN)
3397 /* limit to prevent hanging X server */
3398 (new_name.name)[MAX_ICON_NAME_LEN] = 0;
3400 if (fw->icon_name.name &&
3401 strcmp(new_name.name, fw->icon_name.name) == 0)
3403 /* migo: some apps update their names every second */
3404 /* griph: make sure we don't free the property if it
3405 is THE same name */
3406 if (new_name.name != fw->icon_name.name)
3408 FlocaleFreeNameProperty(&new_name);
3410 return;
3413 free_window_names(fw, False, True);
3414 fw->icon_name = new_name;
3415 SET_WAS_ICON_NAME_PROVIDED(fw, 1);
3416 if (fw->icon_name.name == NULL)
3418 /* currently never happens */
3419 fw->icon_name.name = fw->name.name;
3420 SET_WAS_ICON_NAME_PROVIDED(fw, 0);
3422 setup_visible_name(fw, True);
3423 BroadcastWindowIconNames(fw, False, True);
3424 RedoIconName(fw);
3425 EWMH_SetVisibleName(fw, True);
3426 break;
3428 case XA_WM_HINTS:
3429 flush_property_notify(XA_WM_HINTS, FW_W(fw));
3430 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3431 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3433 old_wmhints_flags = 0;
3434 if (fw->wmhints)
3436 old_wmhints_flags = fw->wmhints->flags;
3437 XFree ((char *) fw->wmhints);
3439 setup_wm_hints(fw);
3440 if (fw->wmhints == NULL)
3442 return;
3446 * rebuild icon if the client either provides an icon
3447 * pixmap or window or has reset the hints to `no icon'.
3449 if ((fw->wmhints->flags & IconPixmapHint) ||
3450 (old_wmhints_flags & IconPixmapHint))
3452 ICON_DBG((stderr, "hpn: iph changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconPixmapHint), fw->name));
3453 has_icon_pixmap_hint_changed = True;
3455 if ((fw->wmhints->flags & IconWindowHint) ||
3456 (old_wmhints_flags & IconWindowHint))
3458 ICON_DBG((stderr, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconWindowHint), fw->name));
3459 has_icon_window_hint_changed = True;
3460 SET_USE_EWMH_ICON(fw, False);
3462 increase_icon_hint_count(fw);
3463 if (has_icon_window_hint_changed ||
3464 has_icon_pixmap_hint_changed)
3466 if (ICON_OVERRIDE_MODE(fw) == ICON_OVERRIDE)
3468 ICON_DBG((stderr, "hpn: icon override '%s'\n", fw->name));
3469 has_icon_changed = False;
3471 else if (ICON_OVERRIDE_MODE(fw) ==
3472 NO_ACTIVE_ICON_OVERRIDE)
3474 if (has_icon_pixmap_hint_changed)
3476 if (WAS_ICON_HINT_PROVIDED(fw) ==
3477 ICON_HINT_MULTIPLE)
3479 ICON_DBG((stderr, "hpn: using further iph '%s'\n", fw->name));
3480 has_icon_changed = True;
3482 else if (fw->icon_bitmap_file ==
3483 NULL ||
3484 fw->icon_bitmap_file ==
3485 Scr.DefaultIcon)
3487 ICON_DBG((stderr, "hpn: using first iph '%s'\n", fw->name));
3488 has_icon_changed = True;
3490 else
3492 /* ignore the first icon pixmap
3493 * hint if the application did
3494 * not provide it from the
3495 * start */
3496 ICON_DBG((stderr, "hpn: first iph ignored '%s'\n", fw->name));
3497 has_icon_changed = False;
3500 else if (has_icon_window_hint_changed)
3502 ICON_DBG((stderr, "hpn: using iwh '%s'\n", fw->name));
3503 has_icon_changed = True;
3505 else
3507 ICON_DBG((stderr, "hpn: iwh not changed, hint ignored '%s'\n", fw->name));
3508 has_icon_changed = False;
3511 else /* NO_ICON_OVERRIDE */
3513 ICON_DBG((stderr, "hpn: using hint '%s'\n", fw->name));
3514 has_icon_changed = True;
3517 if (USE_EWMH_ICON(fw))
3519 has_icon_changed = False;
3522 if (has_icon_changed)
3524 ICON_DBG((stderr, "hpn: icon changed '%s'\n", fw->name));
3525 /* Okay, the icon hint has changed and style
3526 * options tell us to honour this change. Now
3527 * let's see if we have to use the application
3528 * provided pixmap or window (if any), the icon
3529 * file provided by the window's style or the
3530 * default style's icon. */
3531 if (fw->icon_bitmap_file == Scr.DefaultIcon)
3533 fw->icon_bitmap_file = NULL;
3535 if (!fw->icon_bitmap_file &&
3536 !(fw->wmhints->flags &
3537 (IconPixmapHint|IconWindowHint)))
3539 fw->icon_bitmap_file =
3540 (Scr.DefaultIcon) ?
3541 Scr.DefaultIcon : NULL;
3543 fw->iconPixmap = (Window)NULL;
3544 ChangeIconPixmap(fw);
3548 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3549 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3550 * Treat urgency changes by calling user-settable functions.
3551 * These could e.g. deiconify and raise the window or
3552 * temporarily change the decor. */
3553 if (!(old_wmhints_flags & XUrgencyHint) &&
3554 (fw->wmhints->flags & XUrgencyHint))
3556 urgency_action = "Function UrgencyFunc";
3558 if ((old_wmhints_flags & XUrgencyHint) &&
3559 !(fw->wmhints->flags & XUrgencyHint))
3561 urgency_action = "Function UrgencyDoneFunc";
3563 if (urgency_action)
3565 const exec_context_t *exc;
3566 exec_context_changes_t ecc;
3568 ecc.w.fw = fw;
3569 ecc.w.wcontext = C_WINDOW;
3570 exc = exc_clone_context(
3571 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
3572 execute_function(NULL, exc, urgency_action, 0);
3573 exc_destroy_context(exc);
3575 break;
3576 case XA_WM_NORMAL_HINTS:
3577 /* just mark wm normal hints as changed and look them up when
3578 * the next ConfigureRequest w/ x, y, width or height set
3579 * arrives. */
3580 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 1);
3581 break;
3582 default:
3583 if (te->xproperty.atom == _XA_WM_PROTOCOLS)
3585 FetchWmProtocols (fw);
3587 else if (te->xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
3589 FetchWmColormapWindows (fw); /* frees old data */
3590 ReInstallActiveColormap();
3592 else if (te->xproperty.atom == _XA_WM_STATE)
3594 if (fw && OnThisPage && focus_is_focused(fw) &&
3595 FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
3597 /* refresh the focus - why? */
3598 focus_force_refresh_focus(fw);
3601 else
3603 EWMH_ProcessPropertyNotify(ea->exc);
3605 break;
3609 void HandleReparentNotify(const evh_args_t *ea)
3611 const XEvent *te = ea->exc->x.etrigger;
3612 FvwmWindow * const fw = ea->exc->w.fw;
3614 if (!fw)
3616 return;
3618 if (te->xreparent.parent == Scr.Root)
3620 /* Ignore reparenting to the root window. In some cases these
3621 * events are selected although the window is no longer
3622 * managed. */
3623 return;
3625 if (te->xreparent.parent != FW_W_FRAME(fw))
3627 /* window was reparented by someone else, destroy the frame */
3628 SetMapStateProp(fw, WithdrawnState);
3629 EWMH_RestoreInitialStates(fw, te->type);
3630 if (!IS_TEAR_OFF_MENU(fw))
3632 XRemoveFromSaveSet(dpy, te->xreparent.window);
3633 XSelectInput(dpy, te->xreparent.window, NoEventMask);
3635 else
3637 XSelectInput(dpy, te->xreparent.window, XEVMASK_MENUW);
3639 discard_events(XEVMASK_FRAMEW);
3640 destroy_window(fw);
3641 EWMH_ManageKdeSysTray(te->xreparent.window, te->type);
3642 EWMH_WindowDestroyed();
3645 return;
3648 void HandleSelectionRequest(const evh_args_t *ea)
3650 icccm2_handle_selection_request(ea->exc->x.etrigger);
3652 return;
3655 void HandleSelectionClear(const evh_args_t *ea)
3657 icccm2_handle_selection_clear();
3659 return;
3662 void HandleShapeNotify(const evh_args_t *ea)
3664 FvwmWindow * const fw = ea->exc->w.fw;
3666 DBUG("HandleShapeNotify", "Routine Entered");
3668 if (FShapesSupported)
3670 const FShapeEvent *sev =
3671 (const FShapeEvent *)(ea->exc->x.etrigger);
3673 if (!fw)
3675 return;
3677 if (sev->kind != FShapeBounding)
3679 return;
3681 frame_setup_shape(
3682 fw, fw->g.frame.width, fw->g.frame.height, sev->shaped);
3683 GNOME_SetWinArea(fw);
3684 EWMH_SetFrameStrut(fw);
3685 if (!IS_ICONIFIED(fw))
3687 border_redraw_decorations(fw);
3691 return;
3694 void HandleUnmapNotify(const evh_args_t *ea)
3696 int dstx, dsty;
3697 Window dumwin;
3698 XEvent dummy;
3699 XEvent map_event;
3700 const XEvent *te = ea->exc->x.etrigger;
3701 int weMustUnmap;
3702 Bool focus_grabbed;
3703 Bool must_return = False;
3704 Bool do_map = False;
3705 FvwmWindow * const fw = ea->exc->w.fw;
3706 Window pw;
3707 Window cw;
3709 DBUG("HandleUnmapNotify", "Routine Entered");
3711 /* Don't ignore events as described below. */
3712 if (te->xunmap.event != te->xunmap.window &&
3713 (te->xunmap.event != Scr.Root || !te->xunmap.send_event))
3715 must_return = True;
3719 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3720 * to WithdrawnState should send a synthetic UnmapNotify with the
3721 * event field set to (pseudo-)root, in case the window is already
3722 * unmapped (which is the case for fvwm for IconicState).
3723 * Unfortunately, we looked for the FvwmContext using that field, so
3724 * try the window field also. */
3725 weMustUnmap = 0;
3726 if (!fw)
3728 weMustUnmap = 1;
3729 if (XFindContext(
3730 dpy, te->xunmap.window, FvwmContext,
3731 (caddr_t *)&fw) == XCNOENT)
3733 return;
3736 cw = FW_W(fw);
3737 pw = FW_W_PARENT(fw);
3738 if (te->xunmap.window == FW_W_FRAME(fw))
3740 SET_ICONIFY_PENDING(fw , 0);
3741 return;
3743 if (must_return)
3745 return;
3748 if (weMustUnmap)
3750 Bool is_map_request_pending;
3751 check_if_event_args args;
3753 args.w = te->xunmap.window;
3754 args.do_return_true = False;
3755 args.do_return_true_cr = False;
3756 /* Using FCheckTypedWindowEvent() does not work here. I don't
3757 * have the slightest idea why, but using FCheckIfEvent() with
3758 * the appropriate predicate procedure works fine. */
3759 FCheckIfEvent(dpy, &dummy, test_map_request, (XPointer)&args);
3760 /* Unfortunately, there is no procedure in X that simply tests
3761 * if an event of a certain type in on the queue without
3762 * waiting and without removing it from the queue.
3763 * XCheck...Event() does not wait but removes the event while
3764 * XPeek...() does not remove the event but waits. To solve
3765 * this, the predicate procedure sets a flag in the passed in
3766 * structure and returns False unconditionally. */
3767 is_map_request_pending = (args.ret_does_match == True);
3768 if (!is_map_request_pending)
3770 XUnmapWindow(dpy, te->xunmap.window);
3773 if (fw == Scr.Hilite)
3775 Scr.Hilite = NULL;
3777 focus_grabbed = focus_query_close_release_focus(fw);
3778 restore_focus_after_unmap(fw, False);
3779 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
3781 return;
3785 * The program may have unmapped the client window, from either
3786 * NormalState or IconicState. Handle the transition to WithdrawnState.
3788 * We need to reparent the window back to the root (so that fvwm exiting
3789 * won't cause it to get mapped) and then throw away all state (pretend
3790 * that we've received a DestroyNotify).
3792 if (!FCheckTypedWindowEvent(
3793 dpy, te->xunmap.window, DestroyNotify, &dummy) &&
3794 XTranslateCoordinates(
3795 dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
3796 &dumwin))
3798 MyXGrabServer(dpy);
3799 SetMapStateProp(fw, WithdrawnState);
3800 EWMH_RestoreInitialStates(fw, te->type);
3801 if (FCheckTypedWindowEvent(
3802 dpy, te->xunmap.window, ReparentNotify, &dummy))
3804 if (fw->attr_backup.border_width)
3806 XSetWindowBorderWidth(
3807 dpy, te->xunmap.window,
3808 fw->attr_backup.border_width);
3810 if ((!IS_ICON_SUPPRESSED(fw))&&
3811 (fw->wmhints &&
3812 (fw->wmhints->flags & IconWindowHint)))
3814 XUnmapWindow(dpy, fw->wmhints->icon_window);
3817 else
3819 RestoreWithdrawnLocation(fw, False, Scr.Root);
3821 if (!IS_TEAR_OFF_MENU(fw))
3823 XRemoveFromSaveSet(dpy, te->xunmap.window);
3824 XSelectInput(dpy, te->xunmap.window, NoEventMask);
3826 XSync(dpy, 0);
3827 MyXUngrabServer(dpy);
3828 if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
3830 /* the client tried to map the window again while it
3831 * was still inside the decoration windows */
3832 do_map = True;
3835 destroy_window(fw);
3836 if (focus_grabbed == True)
3838 CoerceEnterNotifyOnCurrentWindow();
3840 EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
3841 EWMH_WindowDestroyed();
3842 GNOME_SetClientList();
3843 if (do_map == True)
3845 map_event.xmaprequest.window = cw;
3846 map_event.xmaprequest.parent = Scr.Root;
3847 dispatch_event(&map_event);
3848 /* note: we really should handle all map and unmap notify
3849 * events for that window in a loop here */
3852 return;
3855 void HandleVisibilityNotify(const evh_args_t *ea)
3857 FvwmWindow * const fw = ea->exc->w.fw;
3859 DBUG("HandleVisibilityNotify", "Routine Entered");
3861 if (fw && ea->exc->x.etrigger->xvisibility.window == FW_W_FRAME(fw))
3863 switch (ea->exc->x.etrigger->xvisibility.state)
3865 case VisibilityUnobscured:
3866 SET_FULLY_VISIBLE(fw, 1);
3867 SET_PARTIALLY_VISIBLE(fw, 1);
3868 break;
3869 case VisibilityPartiallyObscured:
3870 SET_FULLY_VISIBLE(fw, 0);
3871 SET_PARTIALLY_VISIBLE(fw, 1);
3872 break;
3873 default:
3874 SET_FULLY_VISIBLE(fw, 0);
3875 SET_PARTIALLY_VISIBLE(fw, 0);
3876 break;
3878 /* Make sure the button grabs are up to date */
3879 focus_grab_buttons(fw);
3882 return;
3885 /* ---------------------------- interface functions ------------------------ */
3887 /* Inform a client window of its geometry.
3889 * The input (frame) geometry will be translated to client geometry
3890 * before sending. */
3891 void SendConfigureNotify(
3892 FvwmWindow *fw, int x, int y, int w, int h, int bw,
3893 Bool send_for_frame_too)
3895 XEvent client_event;
3896 size_borders b;
3898 if (!fw || IS_SHADED(fw))
3900 return;
3902 client_event.type = ConfigureNotify;
3903 client_event.xconfigure.display = dpy;
3904 client_event.xconfigure.event = FW_W(fw);
3905 client_event.xconfigure.window = FW_W(fw);
3906 get_window_borders(fw, &b);
3907 client_event.xconfigure.x = x + b.top_left.width;
3908 client_event.xconfigure.y = y + b.top_left.height;
3909 client_event.xconfigure.width = w - b.total_size.width;
3910 client_event.xconfigure.height = h - b.total_size.height;
3911 client_event.xconfigure.border_width = bw;
3912 client_event.xconfigure.above = FW_W_FRAME(fw);
3913 client_event.xconfigure.override_redirect = False;
3914 #if 0
3915 fprintf(stderr,
3916 "send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x '%s'\n",
3917 client_event.xconfigure.x, client_event.xconfigure.y,
3918 client_event.xconfigure.width, client_event.xconfigure.height,
3919 (int)FW_W_FRAME(fw), (int)FW_W(fw),
3920 (int)client_event.xconfigure.window,
3921 (fw->name.name) ? fw->name.name : "");
3922 #endif
3923 FSendEvent(
3924 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
3925 if (send_for_frame_too)
3927 /* This is for buggy tk, which waits for the real
3928 * ConfigureNotify on frame instead of the synthetic one on w.
3929 * The geometry data in the event will not be correct for the
3930 * frame, but tk doesn't look at that data anyway. */
3931 client_event.xconfigure.event = FW_W_FRAME(fw);
3932 client_event.xconfigure.window = FW_W_FRAME(fw);
3933 FSendEvent(
3934 dpy, FW_W_FRAME(fw), False, StructureNotifyMask,
3935 &client_event);
3938 return;
3941 /* Add an event group to the event handler */
3942 int register_event_group(int event_base, int event_count, PFEH *jump_table)
3944 /* insert into the list */
3945 event_group_t *group;
3946 event_group_t *position = base_event_group;
3947 event_group_t *prev_position = NULL;
3949 while (
3950 position != NULL &&
3951 position->base + position->count < event_base)
3953 prev_position = position;
3954 position = position->next;
3956 if ((position != NULL && position->base < event_base + event_count))
3958 /* there is already an event group registered at the specified
3959 * event range, or the base is before the base X events */
3961 return 1;
3963 /* create the group structure (these are not freed until fvwm exits) */
3964 group = (event_group_t*)safemalloc(sizeof(event_group_t));
3965 group->base = event_base;
3966 group->count = event_count;
3967 group->jump_table = jump_table;
3968 group->next = position;
3969 if (prev_position != NULL)
3971 prev_position->next = group;
3973 else
3975 base_event_group = group;
3978 return 0;
3982 ** Procedure:
3983 ** InitEventHandlerJumpTable
3985 void InitEventHandlerJumpTable(void)
3987 static PFEH EventHandlerJumpTable[LASTEvent];
3988 int i;
3990 for (i=0; i<LASTEvent; i++)
3992 EventHandlerJumpTable[i] = NULL;
3994 EventHandlerJumpTable[Expose] = HandleExpose;
3995 EventHandlerJumpTable[DestroyNotify] = HandleDestroyNotify;
3996 EventHandlerJumpTable[MapRequest] = HandleMapRequest;
3997 EventHandlerJumpTable[MapNotify] = HandleMapNotify;
3998 EventHandlerJumpTable[UnmapNotify] = HandleUnmapNotify;
3999 EventHandlerJumpTable[ButtonPress] = HandleButtonPress;
4000 EventHandlerJumpTable[EnterNotify] = HandleEnterNotify;
4001 EventHandlerJumpTable[LeaveNotify] = HandleLeaveNotify;
4002 EventHandlerJumpTable[FocusIn] = HandleFocusIn;
4003 EventHandlerJumpTable[FocusOut] = HandleFocusOut;
4004 EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
4005 EventHandlerJumpTable[ClientMessage] = HandleClientMessage;
4006 EventHandlerJumpTable[PropertyNotify] = HandlePropertyNotify;
4007 EventHandlerJumpTable[KeyPress] = HandleKeyPress;
4008 EventHandlerJumpTable[KeyRelease] = HandleKeyRelease;
4009 EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
4010 EventHandlerJumpTable[ColormapNotify] = HandleColormapNotify;
4011 EventHandlerJumpTable[SelectionClear] = HandleSelectionClear;
4012 EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
4013 EventHandlerJumpTable[ReparentNotify] = HandleReparentNotify;
4014 EventHandlerJumpTable[MappingNotify] = HandleMappingNotify;
4015 STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
4016 STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
4017 #ifdef MOUSE_DROPPINGS
4018 STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
4019 #else /* no MOUSE_DROPPINGS */
4020 STROKE_CODE(stroke_init());
4021 #endif /* MOUSE_DROPPINGS */
4022 if (register_event_group(0, LASTEvent, EventHandlerJumpTable))
4024 /* should never happen */
4025 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4026 "Faild to initialize event handlers");
4027 exit(1);
4029 if (FShapesSupported)
4031 static PFEH shape_jump_table[FShapeNumberEvents];
4033 for (i = 0; i < FShapeNumberEvents; i++)
4035 shape_jump_table[i] = NULL;
4037 shape_jump_table[FShapeNotify] = HandleShapeNotify;
4038 if (
4039 register_event_group(
4040 FShapeEventBase, FShapeNumberEvents,
4041 shape_jump_table))
4043 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4044 "Faild to init Shape event handler");
4049 return;
4052 /* handle a single X event */
4053 void dispatch_event(XEvent *e)
4055 Window w = e->xany.window;
4056 FvwmWindow *fw;
4057 event_group_t *event_group;
4059 DBUG("dispatch_event", "Routine Entered");
4061 XFlush(dpy);
4062 if (w == Scr.Root)
4064 switch (e->type)
4066 case ButtonPress:
4067 case ButtonRelease:
4068 if (e->xbutton.subwindow != None)
4070 w = e->xbutton.subwindow;
4072 case MapRequest:
4073 w = e->xmaprequest.window;
4074 break;
4075 default:
4076 break;
4079 if (w == Scr.Root ||
4080 XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4082 fw = NULL;
4084 last_event_type = e->type;
4085 event_group = base_event_group;
4086 while (
4087 event_group != NULL &&
4088 event_group->base + event_group->count < e->type)
4090 event_group = event_group->next;
4093 if (
4094 event_group != NULL &&
4095 e->type - event_group->base < event_group->count &&
4096 event_group->jump_table[e->type - event_group->base] != NULL)
4098 evh_args_t ea;
4099 exec_context_changes_t ecc;
4100 Window dummyw;
4102 ecc.type = EXCT_EVENT;
4103 ecc.x.etrigger = e;
4104 ecc.w.wcontext = GetContext(&fw, fw, e, &dummyw);
4105 ecc.w.w = w;
4106 ecc.w.fw = fw;
4107 ea.exc = exc_create_context(
4108 &ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W |
4109 ECC_WCONTEXT);
4110 (*event_group->jump_table[e->type - event_group->base])(&ea);
4111 exc_destroy_context(ea.exc);
4114 #ifdef C_ALLOCA
4115 /* If we're using the C version of alloca, see if anything needs to be
4116 * freed up.
4118 alloca(0);
4119 #endif
4120 DBUG("dispatch_event", "Leaving Routine");
4122 return;
4125 /* ewmh configure request */
4126 void events_handle_configure_request(
4127 XConfigureRequestEvent cre, FvwmWindow *fw, Bool force,
4128 int force_gravity)
4130 __handle_configure_request(cre, NULL, fw, force, force_gravity);
4132 return;
4135 void HandleEvents(void)
4137 XEvent ev;
4139 DBUG("HandleEvents", "Routine Entered");
4140 STROKE_CODE(send_motion = False);
4141 while (!isTerminated)
4143 last_event_type = 0;
4144 if (Scr.flags.is_window_scheduled_for_destroy)
4146 destroy_scheduled_windows();
4148 if (Scr.flags.do_need_window_update)
4150 flush_window_updates();
4152 if (My_XNextEvent(dpy, &ev))
4154 dispatch_event(&ev);
4156 if (Scr.flags.do_need_style_list_update)
4158 simplify_style_list();
4162 return;
4167 * Waits for next X or module event, fires off startup routines when startup
4168 * modules have finished or after a timeout if the user has specified a
4169 * command line module that doesn't quit or gets stuck.
4172 int My_XNextEvent(Display *dpy, XEvent *event)
4174 fd_set in_fdset, out_fdset;
4175 int num_fd;
4176 fmodule_list_itr moditr;
4177 fmodule *module;
4178 fmodule_input *input;
4179 static struct timeval timeout;
4180 static struct timeval *timeoutP = &timeout;
4182 DBUG("My_XNextEvent", "Routine Entered");
4184 /* check for any X events already queued up.
4185 * Side effect: this does an XFlush if no events are queued
4186 * Make sure nothing between here and the select causes further
4187 * X requests to be sent or the select may block even though
4188 * there are events in the queue */
4189 if (FPending(dpy))
4191 DBUG(
4192 "My_XNextEvent", "taking care of queued up events"
4193 " & returning (1)");
4194 FNextEvent(dpy, event);
4195 return 1;
4198 /* check for termination of all startup modules */
4199 if (fFvwmInStartup)
4201 module_list_itr_init(&moditr);
4202 module = module_list_itr_next(&moditr);
4203 for (; module != NULL; module = module_list_itr_next(&moditr))
4205 if (MOD_IS_CMDLINE(module) == 1)
4207 break;
4210 module_cleanup();
4211 if (module == NULL)
4213 /* last module */
4214 DBUG(
4215 "My_XNextEvent",
4216 "Starting up after command lines modules");
4217 /* set an infinite timeout to stop ticking */
4218 timeoutP = NULL;
4219 /* This may cause X requests to be sent */
4220 StartupStuff();
4222 return 0; /* so return without select()ing */
4226 /* Some signals can interrupt us while we wait for any action
4227 * on our descriptors. While some of these signals may be asking
4228 * fvwm to die, some might be harmless. Harmless interruptions
4229 * mean we have to start waiting all over again ... */
4232 int ms;
4233 Bool is_waiting_for_scheduled_command = False;
4234 static struct timeval *old_timeoutP = NULL;
4236 /* The timeouts become undefined whenever the select returns,
4237 * and so we have to reinitialise them */
4238 ms = squeue_get_next_ms();
4239 if (ms == 0)
4241 /* run scheduled commands */
4242 squeue_execute();
4243 ms = squeue_get_next_ms();
4244 /* should not happen anyway.
4245 * get_next_schedule_queue_ms() can't return 0 after a
4246 * call to execute_schedule_queue(). */
4247 if (ms == 0)
4249 ms = 1;
4252 if (ms < 0)
4254 timeout.tv_sec = 42;
4255 timeout.tv_usec = 0;
4257 else
4259 /* scheduled commands are pending - don't wait too
4260 * long */
4261 timeout.tv_sec = ms / 1000;
4262 timeout.tv_usec = 1000 * (ms % 1000);
4263 old_timeoutP = timeoutP;
4264 timeoutP = &timeout;
4265 is_waiting_for_scheduled_command = True;
4268 FD_ZERO(&in_fdset);
4269 FD_ZERO(&out_fdset);
4270 FD_SET(x_fd, &in_fdset);
4272 /* nothing is done here if fvwm was compiled without session
4273 * support */
4274 if (sm_fd >= 0)
4276 FD_SET(sm_fd, &in_fdset);
4279 module_list_itr_init(&moditr);
4280 while ( (module = module_list_itr_next(&moditr)) != NULL)
4282 FD_SET(MOD_READFD(module), &in_fdset);
4284 if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
4286 FD_SET(MOD_WRITEFD(module), &out_fdset);
4290 DBUG("My_XNextEvent", "waiting for module input/output");
4291 num_fd = fvwmSelect(
4292 fvwmlib_max_fd, &in_fdset, &out_fdset, 0, timeoutP);
4293 if (is_waiting_for_scheduled_command)
4295 timeoutP = old_timeoutP;
4298 /* Express route out of fvwm ... */
4299 if (isTerminated)
4301 return 0;
4303 } while (num_fd < 0);
4305 if (num_fd > 0)
4307 /* Check for module input. */
4308 module_list_itr_init(&moditr);
4309 while ( (module = module_list_itr_next(&moditr)) != NULL)
4311 if (FD_ISSET(MOD_READFD(module), &in_fdset))
4313 input = module_receive(module);
4314 /* enqueue the received command */
4315 module_input_enqueue(input);
4317 if (
4318 MOD_WRITEFD(module) >= 0 &&
4319 FD_ISSET(MOD_WRITEFD(module), &out_fdset))
4321 DBUG("My_XNextEvent",
4322 "calling FlushMessageQueue");
4323 FlushMessageQueue(module);
4327 /* execute any commands queued up */
4328 DBUG("My_XNextEvent", "executing module comand queue");
4329 ExecuteCommandQueue();
4331 /* cleanup dead modules */
4332 module_cleanup();
4334 /* nothing is done here if fvwm was compiled without session
4335 * support */
4336 if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
4338 ProcessICEMsgs();
4342 else
4344 /* select has timed out, things must have calmed down so let's
4345 * decorate */
4346 if (fFvwmInStartup)
4348 fvwm_msg(ERR, "My_XNextEvent",
4349 "Some command line modules have not quit, "
4350 "Starting up after timeout.\n");
4351 StartupStuff();
4352 timeoutP = NULL; /* set an infinite timeout to stop
4353 * ticking */
4354 reset_style_changes();
4355 Scr.flags.do_need_window_update = 0;
4357 /* run scheduled commands if necessary */
4358 squeue_execute();
4361 /* check for X events again, rather than return 0 and get called again
4363 if (FPending(dpy))
4365 DBUG("My_XNextEvent",
4366 "taking care of queued up events & returning (2)");
4367 FNextEvent(dpy,event);
4368 return 1;
4371 DBUG("My_XNextEvent", "leaving My_XNextEvent");
4372 return 0;
4377 * Procedure:
4378 * Find the Fvwm context for the event.
4381 int GetContext(FvwmWindow **ret_fw, FvwmWindow *t, const XEvent *e, Window *w)
4383 int context;
4384 Window win;
4385 Window subw = None;
4386 int x = 0;
4387 int y = 0;
4388 Bool is_key_event = False;
4390 win = e->xany.window;
4391 context = C_NO_CONTEXT;
4392 switch (e->type)
4394 case KeyPress:
4395 case KeyRelease:
4396 x = e->xkey.x;
4397 y = e->xkey.y;
4398 subw = e->xkey.subwindow;
4399 if (win == Scr.Root && subw != None)
4401 /* Translate root coordinates into subwindow
4402 * coordinates. Necessary for key bindings that work
4403 * over unfocused windows. */
4404 win = subw;
4405 XTranslateCoordinates(
4406 dpy, Scr.Root, subw, x, y, &x, &y, &subw);
4407 XFindContext(dpy, win, FvwmContext, (caddr_t *) &t);
4409 is_key_event = True;
4410 /* fall through */
4411 case ButtonPress:
4412 case ButtonRelease:
4413 if (!is_key_event)
4415 x = e->xbutton.x;
4416 y = e->xbutton.y;
4417 subw = e->xbutton.subwindow;
4419 if (t && win == FW_W_FRAME(t) && subw != None)
4421 /* Translate frame coordinates into subwindow
4422 * coordinates. */
4423 win = subw;
4424 XTranslateCoordinates(
4425 dpy, FW_W_FRAME(t), subw, x, y, &x, &y, &subw);
4426 if (win == FW_W_PARENT(t))
4428 win = subw;
4429 XTranslateCoordinates(
4430 dpy, FW_W_PARENT(t), subw, x, y, &x,
4431 &y, &subw);
4434 break;
4435 default:
4436 XFindContext(dpy, win, FvwmContext, (caddr_t *)&t);
4437 break;
4439 if (ret_fw != NULL)
4441 *ret_fw = t;
4443 if (!t)
4445 return C_ROOT;
4447 *w = win;
4448 if (*w == Scr.NoFocusWin)
4450 return C_ROOT;
4452 if (subw != None)
4454 if (win == FW_W_PARENT(t))
4456 *w = subw;
4459 if (*w == Scr.Root)
4461 return C_ROOT;
4463 context = frame_window_id_to_context(t, *w, &Button);
4465 return context;
4470 * Removes expose events for a specific window from the queue
4473 int flush_expose(Window w)
4475 XEvent dummy;
4476 int i=0;
4478 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4480 i++;
4483 return i;
4486 /* same as above, but merges the expose rectangles into a single big one */
4487 int flush_accumulate_expose(Window w, XEvent *e)
4489 XEvent dummy;
4490 int i = 0;
4491 int x1 = e->xexpose.x;
4492 int y1 = e->xexpose.y;
4493 int x2 = x1 + e->xexpose.width;
4494 int y2 = y1 + e->xexpose.height;
4496 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4498 x1 = min(x1, dummy.xexpose.x);
4499 y1 = min(y1, dummy.xexpose.y);
4500 x2 = max(x2, dummy.xexpose.x + dummy.xexpose.width);
4501 y2 = max(y2, dummy.xexpose.y + dummy.xexpose.height);
4502 i++;
4504 e->xexpose.x = x1;
4505 e->xexpose.y = y1;
4506 e->xexpose.width = x2 - x1;
4507 e->xexpose.height = y2 - y1;
4509 return i;
4514 * Removes all expose events from the queue and does the necessary redraws
4517 void handle_all_expose(void)
4519 void *saved_event;
4520 XEvent evdummy;
4522 saved_event = fev_save_event();
4523 FPending(dpy);
4524 while (FCheckMaskEvent(dpy, ExposureMask, &evdummy))
4526 dispatch_event(&evdummy);
4528 fev_restore_event(saved_event);
4530 return;
4533 /* CoerceEnterNotifyOnCurrentWindow()
4534 * Pretends to get a HandleEnterNotify on the window that the pointer
4535 * currently is in so that the focus gets set correctly from the beginning.
4536 * Note that this presently only works if the current window is not
4537 * click_to_focus; I think that that behaviour is correct and desirable.
4538 * --11/08/97 gjb */
4539 void CoerceEnterNotifyOnCurrentWindow(void)
4541 Window child;
4542 Window root;
4543 Bool f;
4544 evh_args_t ea;
4545 exec_context_changes_t ecc;
4546 XEvent e;
4547 FvwmWindow *fw;
4549 f = FQueryPointer(
4550 dpy, Scr.Root, &root, &child, &e.xcrossing.x_root,
4551 &e.xcrossing.y_root, &e.xcrossing.x, &e.xcrossing.y,
4552 &JunkMask);
4553 if (f == False || child == None)
4555 return;
4557 e.xcrossing.type = EnterNotify;
4558 e.xcrossing.window = child;
4559 e.xcrossing.subwindow = None;
4560 e.xcrossing.mode = NotifyNormal;
4561 e.xcrossing.detail = NotifyAncestor;
4562 e.xcrossing.same_screen = True;
4563 if (XFindContext(dpy, child, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4565 fw = NULL;
4567 else
4569 XTranslateCoordinates(
4570 dpy, Scr.Root, child, e.xcrossing.x_root,
4571 e.xcrossing.y_root, &JunkX, &JunkY, &child);
4572 if (child == FW_W_PARENT(fw))
4574 child = FW_W(fw);
4576 if (child != None)
4578 e.xany.window = child;
4581 e.xcrossing.focus = (fw == get_focus_window()) ? True : False;
4582 ecc.type = EXCT_NULL;
4583 ecc.x.etrigger = &e;
4584 ea.exc = exc_create_context(&ecc, ECC_TYPE | ECC_ETRIGGER);
4585 HandleEnterNotify(&ea);
4586 exc_destroy_context(ea.exc);
4588 return;
4591 /* This function discards all queued up ButtonPress, ButtonRelease and
4592 * ButtonMotion events. */
4593 int discard_events(long event_mask)
4595 XEvent e;
4596 int count;
4598 XSync(dpy, 0);
4599 for (count = 0; FCheckMaskEvent(dpy, event_mask, &e); count++)
4601 /* nothing */
4604 return count;
4607 /* This function discards all queued up ButtonPress, ButtonRelease and
4608 * ButtonMotion events. */
4609 int discard_window_events(Window w, long event_mask)
4611 XEvent e;
4612 int count;
4614 XSync(dpy, 0);
4615 for (count = 0; FCheckWindowEvent(dpy, w, event_mask, &e); count++)
4617 /* nothing */
4620 return count;
4623 /* Similar function for certain types of PropertyNotify. */
4624 int flush_property_notify(Atom atom, Window w)
4626 XEvent e;
4627 int count;
4628 test_typed_window_event_args args;
4630 XSync(dpy, 0);
4631 args.w = w;
4632 args.event_type = PropertyNotify;
4633 for (count = 0;
4634 FCheckPeekIfEvent(
4635 dpy, &e, test_typed_window_event, (XPointer)&args);
4636 count++)
4638 Bool rc;
4640 if (e.xproperty.atom != atom)
4642 break;
4644 /* remove the event from the queue */
4645 rc = FCheckIfEvent(
4646 dpy, &e, test_typed_window_event, (XPointer)&args);
4649 return count;
4652 /* Wait for all mouse buttons to be released
4653 * This can ease some confusion on the part of the user sometimes
4655 * Discard superflous button events during this wait period. */
4656 void WaitForButtonsUp(Bool do_handle_expose)
4658 unsigned int mask;
4659 unsigned int bmask;
4660 long evmask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
4661 KeyPressMask|KeyReleaseMask;
4662 int count;
4663 int use_wait_cursor;
4664 XEvent e;
4666 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
4667 &JunkX, &JunkY, &mask) == False)
4669 /* pointer is on a different screen - that's okay here */
4671 mask &= DEFAULT_ALL_BUTTONS_MASK;
4672 if (mask == 0)
4674 return;
4676 if (do_handle_expose)
4678 evmask |= ExposureMask;
4680 GrabEm(None, GRAB_NORMAL);
4681 for (count = 0, use_wait_cursor = 0; mask != 0; count++)
4683 /* handle expose events */
4684 XAllowEvents(dpy, SyncPointer, CurrentTime);
4685 if (FCheckMaskEvent(dpy, evmask, &e))
4687 switch (e.type)
4689 case ButtonRelease:
4690 if (e.xbutton.button <=
4691 NUMBER_OF_MOUSE_BUTTONS)
4693 bmask = (Button1Mask <<
4694 (e.xbutton.button - 1));
4695 mask = e.xbutton.state & ~bmask;
4697 break;
4698 case Expose:
4699 dispatch_event(&e);
4700 break;
4701 default:
4702 break;
4705 else
4707 if (FQueryPointer(
4708 dpy, Scr.Root, &JunkRoot, &JunkChild,
4709 &JunkX, &JunkY, &JunkX, &JunkY, &mask) ==
4710 False)
4712 /* pointer is on a different screen - that's
4713 * okay here */
4715 mask &= DEFAULT_ALL_BUTTONS_MASK;
4716 usleep(1);
4718 if (use_wait_cursor == 0 && count == 20)
4720 GrabEm(CRS_WAIT, GRAB_NORMAL);
4721 use_wait_cursor = 1;
4724 UngrabEm(GRAB_NORMAL);
4725 if (use_wait_cursor)
4727 UngrabEm(GRAB_NORMAL);
4728 XFlush(dpy);
4731 return;
4734 void sync_server(int toggle)
4736 static Bool synced = False;
4738 if (toggle == -1)
4740 toggle = (synced == False);
4742 if (toggle == 1)
4744 synced = True;
4746 else
4748 synced = False;
4750 XSynchronize(dpy, synced);
4751 XFlush(dpy);
4753 return;
4756 Bool is_resizing_event_pending(
4757 FvwmWindow *fw)
4759 XEvent e;
4760 check_if_event_args args;
4762 args.w = FW_W(fw);
4763 args.do_return_true = False;
4764 args.do_return_true_cr = False;
4765 args.cr_value_mask = 0;
4766 args.ret_does_match = False;
4767 args.ret_type = 0;
4768 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
4770 return args.ret_does_match;
4773 /* ---------------------------- builtin commands --------------------------- */
4775 void CMD_XSynchronize(F_CMD_ARGS)
4777 int toggle;
4779 toggle = ParseToggleArgument(action, NULL, -1, 0);
4780 sync_server(toggle);
4782 return;
4785 void CMD_XSync(F_CMD_ARGS)
4787 XSync(dpy, 0);
4789 return;