Propagate Layer changes via Style command on-the-fly.
[fvwm.git] / fvwm / events.c
blob32bb77d5584d6875eebcffbf5dff5f49060e36b9
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 &&
275 event->xproperty.atom == ta->atom)
277 return True;
280 return False;
283 static Bool test_resizing_event(
284 Display *display, XEvent *event, XPointer arg)
286 check_if_event_args *cie_args;
287 Bool rc;
289 cie_args = (check_if_event_args *)arg;
290 cie_args->ret_does_match = False;
291 if (event->xany.window != cie_args->w)
293 return False;
295 rc = False;
296 switch (event->type)
298 case ConfigureRequest:
299 if ((event->xconfigurerequest.value_mask &
300 cie_args->cr_value_mask) != 0)
302 cie_args->ret_type = ConfigureRequest;
303 cie_args->ret_does_match = True;
304 rc = cie_args->do_return_true_cr;
306 break;
307 case PropertyNotify:
308 if (event->xproperty.atom == XA_WM_NORMAL_HINTS)
310 cie_args->ret_type = PropertyNotify;
311 cie_args->ret_does_match = True;
312 rc = cie_args->do_return_true;
314 default:
315 break;
318 /* Yes, it is correct that this function may always returns False. */
319 return rc;
322 static inline void __handle_cr_on_unmanaged(XConfigureRequestEvent *cre)
324 XWindowChanges xwc;
325 unsigned long xwcm;
327 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
328 xwc.x = cre->x;
329 xwc.y = cre->y;
330 xwc.width = cre->width;
331 xwc.height = cre->height;
332 xwc.border_width = cre->border_width;
333 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
335 return;
338 static inline void __handle_cr_on_icon(
339 XConfigureRequestEvent *cre, FvwmWindow *fw)
341 XWindowChanges xwc;
342 unsigned long xwcm;
344 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
345 xwc.x = cre->x;
346 xwc.y = cre->y;
347 xwc.width = cre->width;
348 xwc.height = cre->height;
349 xwc.border_width = cre->border_width;
350 if (FW_W_ICON_PIXMAP(fw) == cre->window)
352 int bw;
354 if (cre->value_mask & CWBorderWidth)
356 fw->icon_border_width = cre->border_width;
358 bw = fw->icon_border_width;
359 if ((cre->value_mask & (CWWidth | CWHeight)) ==
360 (CWWidth | CWHeight))
362 set_icon_picture_size(
363 fw, cre->width + 2 * bw, cre->height + 2 * bw);
366 set_icon_position(fw, cre->x, cre->y);
367 broadcast_icon_geometry(fw, False);
368 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
369 if (cre->window != FW_W_ICON_PIXMAP(fw) &&
370 FW_W_ICON_PIXMAP(fw) != None)
372 rectangle g;
374 get_icon_picture_geometry(fw, &g);
375 xwc.x = g.x;
376 xwc.y = g.y;
377 xwcm = cre->value_mask & (CWX | CWY);
378 XConfigureWindow(
379 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
381 if (FW_W_ICON_TITLE(fw) != None)
383 rectangle g;
385 get_icon_title_geometry(fw, &g);
386 xwc.x = g.x;
387 xwc.y = g.y;
388 xwcm = cre->value_mask & (CWX | CWY);
389 XConfigureWindow(
390 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
393 return;
396 static inline void __handle_cr_on_shaped(FvwmWindow *fw)
398 /* suppress compiler warnings w/o shape extension */
399 int i = 0;
400 unsigned int u = 0;
401 Bool b = False;
402 int boundingShaped;
404 if (FShapeQueryExtents(
405 dpy, FW_W(fw), &boundingShaped, &i, &i, &u, &u, &b,
406 &i, &i, &u, &u))
408 fw->wShaped = boundingShaped;
410 else
412 fw->wShaped = 0;
415 return;
418 static inline void __handle_cr_restack(
419 int *ret_do_send_event, XConfigureRequestEvent *cre, FvwmWindow *fw)
421 XWindowChanges xwc;
422 unsigned long xwcm;
423 FvwmWindow *fw2 = NULL;
425 if (cre->value_mask & CWSibling)
427 if (XFindContext(
428 dpy, cre->above, FvwmContext,
429 (caddr_t *)&fw2) == XCNOENT)
431 fw2 = NULL;
433 if (fw2 == fw)
435 fw2 = NULL;
438 if (cre->detail != Above && cre->detail != Below)
440 HandleUnusualStackmodes(
441 cre->detail, fw, cre->window, fw2, cre->above);
443 /* only allow clients to restack windows within their layer */
444 else if (fw2 == NULL || compare_window_layers(fw2, fw) != 0)
446 switch (cre->detail)
448 case Above:
449 RaiseWindow(fw, True);
450 break;
451 case Below:
452 LowerWindow(fw, True);
453 break;
456 else
458 xwc.sibling = FW_W_FRAME(fw2);
459 xwc.stack_mode = cre->detail;
460 xwcm = CWSibling | CWStackMode;
461 XConfigureWindow(dpy, FW_W_FRAME(fw), xwcm, &xwc);
463 /* Maintain the condition that icon windows are stacked
464 * immediately below their frame
465 * 1. for fw */
466 xwc.sibling = FW_W_FRAME(fw);
467 xwc.stack_mode = Below;
468 xwcm = CWSibling | CWStackMode;
469 if (FW_W_ICON_TITLE(fw) != None)
471 XConfigureWindow(
472 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
474 if (FW_W_ICON_PIXMAP(fw) != None)
476 XConfigureWindow(
477 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
479 /* 2. for fw2 */
480 if (cre->detail == Below)
482 xwc.sibling = FW_W_FRAME(fw2);
483 xwc.stack_mode = Below;
484 xwcm = CWSibling | CWStackMode;
485 if (FW_W_ICON_TITLE(fw2) != None)
487 XConfigureWindow(
488 dpy, FW_W_ICON_TITLE(fw2), xwcm, &xwc);
490 if (FW_W_ICON_PIXMAP(fw2) != None)
492 XConfigureWindow(
493 dpy, FW_W_ICON_PIXMAP(fw2), xwcm,
494 &xwc);
497 /* Maintain the stacking order ring */
498 if (cre->detail == Above)
500 remove_window_from_stack_ring(fw);
501 add_window_to_stack_ring_after(
502 fw, get_prev_window_in_stack_ring(fw2));
504 else /* cre->detail == Below */
506 remove_window_from_stack_ring(fw);
507 add_window_to_stack_ring_after(fw, fw2);
509 BroadcastRestackThisWindow(fw);
511 /* srt (28-Apr-2001): Tk needs a ConfigureNotify event after a
512 * raise, otherwise it would hang for two seconds */
513 *ret_do_send_event = 1;
515 return;
518 static inline void __cr_get_static_position(
519 rectangle *ret_g, FvwmWindow *fw, XConfigureRequestEvent *cre,
520 size_borders *b)
522 if (cre->value_mask & CWX)
524 ret_g->x = cre->x - b->top_left.width;
526 else
528 ret_g->x = fw->g.frame.x;
530 if (cre->value_mask & CWY)
532 ret_g->y = cre->y - b->top_left.height;
534 else
536 ret_g->y = fw->g.frame.y;
539 return;
542 static inline void __cr_get_grav_position(
543 rectangle *ret_g, FvwmWindow *fw, XConfigureRequestEvent *cre,
544 size_borders *b)
546 int grav_x;
547 int grav_y;
549 gravity_get_offsets(fw->hints.win_gravity, &grav_x, &grav_y);
550 if (cre->value_mask & CWX)
552 ret_g->x = cre->x - ((grav_x + 1) * b->total_size.width) / 2;
554 else
556 ret_g->x = fw->g.frame.x;
558 if (cre->value_mask & CWY)
560 ret_g->y = cre->y - ((grav_y + 1) * b->total_size.height) / 2;
562 else
564 ret_g->y = fw->g.frame.y;
567 return;
570 /* Try to detect whether the application uses the ICCCM way of moving its
571 * window or the traditional way, always assuming StaticGravity. */
572 static inline void __cr_detect_icccm_move(
573 FvwmWindow *fw, XConfigureRequestEvent *cre, size_borders *b)
575 rectangle grav_g;
576 rectangle static_g;
577 rectangle dg_g;
578 rectangle ds_g;
579 int mx;
580 int my;
581 int m;
582 int w;
583 int h;
584 int has_x;
585 int has_y;
587 if (CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
589 if (Scr.bo.do_debug_cr_motion_method == 1)
591 fprintf(
592 stderr,
593 "_cdim: --- already detected (pid %d) %p"
594 " '%s'\n", HAS_EWMH_WM_PID(fw), fw,
595 fw->visible_name);
597 return;
599 if (HAS_EWMH_WM_PID(fw))
601 if (Scr.bo.do_debug_cr_motion_method == 1)
603 fprintf(
604 stderr,"_cdim: +++ has ewmh_wm_pid: icccm"
605 " %p '%s'\n", fw, fw->visible_name);
607 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
608 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
609 return;
611 if (fw->ewmh_window_type != EWMH_WINDOW_TYPE_NONE_ID)
613 if (Scr.bo.do_debug_cr_motion_method == 1)
615 fprintf(
616 stderr, "_cdim: +++ has ewmh_window_type:"
617 " icccm %p '%s'\n", fw,
618 fw->visible_name);
620 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
621 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
622 return;
624 if (FShapesSupported && fw->wShaped)
626 if (Scr.bo.do_debug_cr_motion_method == 1)
628 fprintf(
629 stderr, "_cdim: --- shaped window %p "
630 "'%s'\n", fw, fw->visible_name);
632 /* no detection for shaped windows */
633 return;
635 if (fw->hints.win_gravity == StaticGravity)
637 if (Scr.bo.do_debug_cr_motion_method == 1)
639 fprintf(
640 stderr, "_cdim: --- using StaticGravity"
641 " %p '%s'\n", fw, fw->visible_name);
643 return;
645 has_x = (cre->value_mask & CWX);
646 has_y = (cre->value_mask & CWY);
647 if (!has_x && !has_y)
649 if (Scr.bo.do_debug_cr_motion_method == 1)
651 fprintf(
652 stderr, "_cdim: --- not moved %p '%s'\n",
653 fw, fw->visible_name);
655 return;
657 __cr_get_grav_position(&grav_g, fw, cre, b);
658 __cr_get_static_position(&static_g, fw, cre, b);
659 if (static_g.x == grav_g.x)
661 /* both methods have the same result; ignore */
662 has_x = 0;
664 if (static_g.y == grav_g.y)
666 /* both methods have the same result; ignore */
667 has_y = 0;
669 if (!has_x && !has_y)
671 if (Scr.bo.do_debug_cr_motion_method == 1)
673 fprintf(
674 stderr, "_cdim: --- not moved %p '%s'\n",
675 fw, fw->visible_name);
677 return;
679 dg_g.x = grav_g.x - fw->g.frame.x;
680 dg_g.y = grav_g.y - fw->g.frame.y;
681 ds_g.x = static_g.x - fw->g.frame.x;
682 ds_g.y = static_g.y - fw->g.frame.y;
683 if (Scr.bo.do_debug_cr_motion_method == 1)
685 fprintf(
686 stderr, "s %3d/%3d %2d/%2d, g %3d/%3d %2d/%2d: ",
687 static_g.x, static_g.y, ds_g.x, ds_g.y, grav_g.x,
688 grav_g.y, dg_g.x, dg_g.y);
690 /* check full screen */
691 if ((cre->value_mask & (CWX | CWY)) == (CWX | CWY) &&
692 (has_x || has_y) &&
693 cre->width == Scr.MyDisplayWidth &&
694 cre->height == Scr.MyDisplayHeight)
696 if (grav_g.x == -b->top_left.width &&
697 grav_g.y == -b->top_left.height)
699 /* Window is fullscreen using the ICCCM way. */
700 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
701 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
702 if (Scr.bo.do_debug_cr_motion_method == 1)
704 fprintf(
705 stderr, "+++ fullscreen icccm %p"
706 " '%s'\n", fw, fw->visible_name);
708 return;
710 else if (static_g.x == -b->top_left.width &&
711 static_g.y == -b->top_left.height)
713 /* Window is fullscreen using the traditional way. */
714 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
715 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
716 if (Scr.bo.do_debug_cr_motion_method == 1)
718 fprintf(
719 stderr, "+++ fullscreen traditional"
720 " %p '%s'\n", fw,
721 fw->visible_name);
723 return;
726 /* check travelling across the screen */
727 if (has_x && dg_g.x == 0 && ds_g.x != 0 &&
728 has_y && dg_g.y == 0 && ds_g.y != 0)
730 /* The traditional way causes a shift by the border width or
731 * height. Use ICCCM way. */
732 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
733 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
734 if (Scr.bo.do_debug_cr_motion_method == 1)
736 fprintf(
737 stderr, "+++ travelling icccm %p '%s'\n",
738 fw, fw->visible_name);
740 return;
742 if (has_x && dg_g.x != 0 && ds_g.x == 0 &&
743 has_y && dg_g.y != 0 && ds_g.y == 0)
745 /* The ICCCM way causes a shift by the border width or height.
746 * Use traditional way. */
747 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
748 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
749 if (Scr.bo.do_debug_cr_motion_method == 1)
751 fprintf(
752 stderr, "+++ travelling traditional %p"
753 " '%s'\n", fw, fw->visible_name);
755 return;
757 /* check placement near border */
758 w = (cre->value_mask & CWWidth) ?
759 cre->width + b->total_size.width : fw->g.frame.width;
760 h = (cre->value_mask & CWHeight) ?
761 cre->height + b->total_size.height : fw->g.frame.height;
762 if (!has_x)
764 mx = CR_MOTION_METHOD_AUTO;
766 else if (static_g.x == 0 || static_g.x + w == Scr.MyDisplayWidth)
768 mx = CR_MOTION_METHOD_STATIC_GRAV;
770 else if (grav_g.x == 0 || grav_g.x + w == Scr.MyDisplayWidth)
772 mx = CR_MOTION_METHOD_USE_GRAV;
774 else
776 mx = CR_MOTION_METHOD_AUTO;
778 if (!has_y)
780 my = CR_MOTION_METHOD_AUTO;
782 else if (static_g.y == 0 || static_g.y + h == Scr.MyDisplayHeight)
784 my = CR_MOTION_METHOD_STATIC_GRAV;
786 else if (grav_g.y == 0 || grav_g.y + h == Scr.MyDisplayHeight)
788 my = CR_MOTION_METHOD_USE_GRAV;
790 else
792 my = CR_MOTION_METHOD_AUTO;
794 m = (mx != CR_MOTION_METHOD_AUTO) ? mx : my;
795 if (m != CR_MOTION_METHOD_AUTO)
797 /* Window was placed next to the display border. */
798 if (m == my || my == CR_MOTION_METHOD_AUTO)
800 SET_CR_MOTION_METHOD(fw, m);
801 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
802 if (Scr.bo.do_debug_cr_motion_method == 1)
804 fprintf(
805 stderr, "+++ near border %s %p "
806 "'%s'\n", (m ==
807 CR_MOTION_METHOD_USE_GRAV)
808 ? "icccm" : "traditional", fw,
809 fw->visible_name);
811 return;
814 if (Scr.bo.do_debug_cr_motion_method == 1)
816 fprintf(
817 stderr, "--- not detected %p '%s'\n", fw,
818 fw->visible_name);
821 return;
824 #define EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
825 /* This is not a good idea because this interferes with changes in the size
826 * hints of the window. However, it is impossible to be completely safe here.
827 * For example, if the client changes the size inc, then resizes the size of
828 * its window and then changes the size inc again - all in one batch - then
829 * the WM will read the *second* size inc upon the *first* event and use the
830 * wrong one in the ConfigureRequest calculations. */
831 /* dv (31 Mar 2002): The code now handles these situations, so enable it
832 * again. */
833 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
834 static inline int __merge_cr_moveresize(
835 const evh_args_t *ea, XConfigureRequestEvent *cre, FvwmWindow *fw,
836 size_borders *b)
838 int cn_count = 0;
839 XEvent e;
840 XConfigureRequestEvent *ecre;
841 check_if_event_args args;
843 args.w = cre->window;
844 args.do_return_true = False;
845 args.do_return_true_cr = True;
846 args.cr_value_mask = CR_MOVERESIZE_MASK;
847 args.ret_does_match = False;
848 args.ret_type = 0;
850 for (cn_count = 0; 1; )
852 unsigned long vma;
853 unsigned long vmo;
854 unsigned long xm;
855 unsigned long ym;
856 evh_args_t ea2;
857 exec_context_changes_t ecc;
859 FCheckPeekIfEvent(
860 dpy, &e, test_resizing_event, (XPointer)&args);
861 ecre = &e.xconfigurerequest;
862 if (args.ret_does_match == False)
864 break;
866 else if (args.ret_type == PropertyNotify)
868 /* Can't merge events with a PropertyNotify in
869 * between. The event is still on the queue. */
870 break;
872 else if (args.ret_type != ConfigureRequest)
874 /* not good. unselected event type! */
875 continue;
877 /* Event was not yet removed from the queue but stored in e. */
878 xm = CWX | CWWidth;
879 ym = CWY | CWHeight;
880 vma = cre->value_mask & ecre->value_mask;
881 vmo = cre->value_mask | ecre->value_mask;
882 if (((vma & xm) == 0 && (vmo & xm) == xm) ||
883 ((vma & ym) == 0 && (vmo & ym) == ym))
885 /* can't merge events since location of window might
886 * get screwed up. */
887 break;
889 /* Finally remove the event from the queue */
890 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
891 /* partially handle the event */
892 ecre->value_mask &= ~args.cr_value_mask;
893 ea2.exc = exc_clone_context(ea->exc, &ecc, ECC_ETRIGGER);
894 HandleConfigureRequest(&ea2);
895 exc_destroy_context(ea2.exc);
896 /* collect the size/position changes */
897 if (ecre->value_mask & CWX)
899 cre->x = ecre->x;
901 if (ecre->value_mask & CWY)
903 cre->y = ecre->y;
905 if (ecre->value_mask & CWWidth)
907 cre->width = ecre->width;
909 if (ecre->value_mask & CWHeight)
911 cre->height = ecre->height;
913 if (ecre->value_mask & CWBorderWidth)
915 cre->border_width = ecre->border_width;
917 cre->value_mask |= (ecre->value_mask & CR_MOVERESIZE_MASK);
918 cn_count++;
921 return cn_count;
923 #endif
925 static inline int __handle_cr_on_client(
926 int *ret_do_send_event, XConfigureRequestEvent cre,
927 const evh_args_t *ea, FvwmWindow *fw, Bool force, int force_gravity)
929 rectangle new_g;
930 rectangle d_g;
931 size_rect constr_dim;
932 size_rect oldnew_dim;
933 size_borders b;
934 int cn_count = 0;
935 int gravity;
937 if (ea)
939 cre = ea->exc->x.etrigger->xconfigurerequest;
941 if ((cre.value_mask & (CWWidth | CWHeight | CWX | CWY)) == 0)
943 return 0;
946 get_window_borders(fw, &b);
947 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
948 /* Merge all pending ConfigureRequests for the window into a single
949 * event. However, we can not do this if the window uses the motion
950 * method autodetection because the merged event might confuse the
951 * detection code. */
952 if (ea && CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
954 cn_count = __merge_cr_moveresize(ea, &cre, fw, &b);
956 #endif
957 #if 0
958 fprintf(stderr,
959 "cre: %d(%d) %d(%d) %d(%d)x%d(%d) fw 0x%08x w 0x%08x "
960 "ew 0x%08x '%s'\n",
961 cre.x, (int)(cre.value_mask & CWX),
962 cre.y, (int)(cre.value_mask & CWY),
963 cre.width, (int)(cre.value_mask & CWWidth),
964 cre.height, (int)(cre.value_mask & CWHeight),
965 (int)FW_W_FRAME(fw), (int)FW_W(fw), (int)cre.window,
966 (fw->name.name) ? fw->name.name : "");
967 #endif
968 /* Don't modify frame_g fields before calling SetupWindow! */
969 memset(&d_g, 0, sizeof(d_g));
971 if (HAS_NEW_WM_NORMAL_HINTS(fw))
973 /* get the latest size hints */
974 XSync(dpy, 0);
975 GetWindowSizeHints(fw);
976 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
978 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMaxSize))
980 /* Java workaround */
981 if (cre.height > fw->hints.max_height &&
982 fw->hints.max_height <= BROKEN_MAXSIZE_LIMIT)
984 fw->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
985 cre.value_mask |= CWHeight;
987 if (cre.width > fw->hints.max_width &&
988 fw->hints.max_width <= BROKEN_MAXSIZE_LIMIT)
990 fw->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
991 cre.value_mask |= CWWidth;
994 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMinSize))
996 if (cre.width < fw->hints.min_width &&
997 fw->hints.min_width >= BROKEN_MINSIZE_LIMIT)
999 fw->hints.min_width = 1;
1000 cre.value_mask |= CWWidth;
1002 if (cre.height < fw->hints.min_height &&
1003 fw->hints.min_height >= BROKEN_MINSIZE_LIMIT)
1005 fw->hints.min_height = 1;
1006 cre.value_mask |= CWHeight;
1009 if (IS_SHADED(fw) ||
1010 !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM, False))
1012 /* forbid shaded applications to move their windows */
1013 cre.value_mask &= ~(CWX | CWY);
1014 /* resend the old geometry */
1015 *ret_do_send_event = 1;
1017 if (IS_MAXIMIZED(fw))
1019 /* dont allow clients to resize maximized windows */
1020 cre.value_mask &= ~(CWWidth | CWHeight);
1021 /* resend the old geometry */
1022 *ret_do_send_event = 1;
1023 d_g.width = 0;
1024 d_g.height = 0;
1026 else if (
1027 !is_function_allowed(
1028 F_RESIZE, NULL, fw, RQORIG_PROGRAM, False))
1030 cre.value_mask &= ~(CWWidth | CWHeight);
1031 *ret_do_send_event = 1;
1034 if (cre.value_mask & CWBorderWidth)
1036 /* for restoring */
1037 fw->attr_backup.border_width = cre.border_width;
1039 if (!force && CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO)
1041 __cr_detect_icccm_move(fw, &cre, &b);
1043 if (force_gravity > ForgetGravity && force_gravity <= StaticGravity)
1045 gravity = force_gravity;
1047 else
1049 gravity = fw->hints.win_gravity;
1051 if (!(cre.value_mask & (CWX | CWY)))
1053 /* nothing */
1055 else if ((force ||
1056 CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_USE_GRAV) &&
1057 gravity != StaticGravity)
1059 int ref_x;
1060 int ref_y;
1061 int grav_x;
1062 int grav_y;
1064 gravity_get_offsets(gravity, &grav_x, &grav_y);
1065 if (cre.value_mask & CWX)
1067 ref_x = cre.x -
1068 ((grav_x + 1) * b.total_size.width) / 2;
1069 d_g.x = ref_x - fw->g.frame.x;
1071 if (cre.value_mask & CWY)
1073 ref_y = cre.y -
1074 ((grav_y + 1) * b.total_size.height) / 2;
1075 d_g.y = ref_y - fw->g.frame.y;
1078 else /* ..._USE_GRAV or ..._AUTO */
1080 /* default: traditional cr handling */
1081 if (cre.value_mask & CWX)
1083 d_g.x = cre.x - fw->g.frame.x - b.top_left.width;
1085 if (cre.value_mask & CWY)
1087 d_g.y = cre.y - fw->g.frame.y - b.top_left.height;
1090 if (cre.value_mask & CWHeight)
1092 if (cre.height <
1093 (WINDOW_FREAKED_OUT_SIZE - b.total_size.height))
1095 d_g.height = cre.height -
1096 (fw->g.frame.height - b.total_size.height);
1098 else
1100 /* Ignore height changes to astronomically large
1101 * windows (needed for XEmacs 20.4); don't care if the
1102 * window is shaded here - we won't use 'height' in
1103 * this case anyway.
1104 * Inform the buggy app about the size that *we* want
1106 d_g.height = 0;
1107 *ret_do_send_event = 1;
1110 if (cre.value_mask & CWWidth)
1112 if (cre.width < (WINDOW_FREAKED_OUT_SIZE - b.total_size.width))
1114 d_g.width = cre.width -
1115 (fw->g.frame.width - b.total_size.width);
1117 else
1119 d_g.width = 0;
1120 *ret_do_send_event = 1;
1124 /* SetupWindow (x,y) are the location of the upper-left outer corner
1125 * and are passed directly to XMoveResizeWindow (frame). The
1126 * (width,height) are the inner size of the frame. The inner width is
1127 * the same as the requested client window width; the inner height is
1128 * the same as the requested client window height plus any title bar
1129 * slop. */
1130 new_g = fw->g.frame;
1131 if (IS_SHADED(fw))
1133 new_g.width = fw->g.normal.width;
1134 new_g.height = fw->g.normal.height;
1136 oldnew_dim.width = new_g.width + d_g.width;
1137 oldnew_dim.height = new_g.height + d_g.height;
1138 constr_dim.width = oldnew_dim.width;
1139 constr_dim.height = oldnew_dim.height;
1140 constrain_size(
1141 fw, NULL, &constr_dim.width, &constr_dim.height, 0, 0,
1142 CS_UPDATE_MAX_DEFECT);
1143 d_g.width += (constr_dim.width - oldnew_dim.width);
1144 d_g.height += (constr_dim.height - oldnew_dim.height);
1145 if ((cre.value_mask & CWX) && d_g.width)
1147 new_g.x = fw->g.frame.x + d_g.x;
1148 new_g.width = fw->g.frame.width + d_g.width;
1150 else if ((cre.value_mask & CWX) && !d_g.width)
1152 new_g.x = fw->g.frame.x + d_g.x;
1154 else if (!(cre.value_mask & CWX) && d_g.width)
1156 gravity_resize(gravity, &new_g, d_g.width, 0);
1158 if ((cre.value_mask & CWY) && d_g.height)
1160 new_g.y = fw->g.frame.y + d_g.y;
1161 new_g.height = fw->g.frame.height + d_g.height;
1163 else if ((cre.value_mask & CWY) && !d_g.height)
1165 new_g.y = fw->g.frame.y + d_g.y;
1167 else if (!(cre.value_mask & CWY) && d_g.height)
1169 gravity_resize(gravity, &new_g, 0, d_g.height);
1172 if (new_g.x == fw->g.frame.x && new_g.y == fw->g.frame.y &&
1173 new_g.width == fw->g.frame.width &&
1174 new_g.height == fw->g.frame.height)
1176 /* Window will not be moved or resized; send a synthetic
1177 * ConfigureNotify. */
1178 *ret_do_send_event = 1;
1180 else if ((cre.value_mask & CWX) || (cre.value_mask & CWY) ||
1181 d_g.width || d_g.height)
1183 if (IS_SHADED(fw))
1185 get_shaded_geometry(fw, &new_g, &new_g);
1187 frame_setup_window_app_request(
1188 fw, new_g.x, new_g.y, new_g.width, new_g.height,
1189 False);
1190 /* make sure the window structure has the new position */
1191 update_absolute_geometry(fw);
1192 maximize_adjust_offset(fw);
1193 GNOME_SetWinArea(fw);
1195 else if (DO_FORCE_NEXT_CR(fw))
1197 *ret_do_send_event = 1;
1199 SET_FORCE_NEXT_CR(fw, 0);
1200 SET_FORCE_NEXT_PN(fw, 0);
1202 return cn_count;
1205 void __handle_configure_request(
1206 XConfigureRequestEvent cre, const evh_args_t *ea, FvwmWindow *fw,
1207 Bool force, int force_gravity)
1209 int do_send_event = 0;
1210 int cn_count = 0;
1212 /* According to the July 27, 1988 ICCCM draft, we should ignore size
1213 * and position fields in the WM_NORMAL_HINTS property when we map a
1214 * window. Instead, we'll read the current geometry. Therefore, we
1215 * should respond to configuration requests for windows which have
1216 * never been mapped. */
1217 if (fw == NULL)
1219 __handle_cr_on_unmanaged(&cre);
1220 return;
1222 if (cre.window == FW_W_ICON_TITLE(fw) ||
1223 cre.window == FW_W_ICON_PIXMAP(fw))
1225 __handle_cr_on_icon(&cre, fw);
1227 if (FShapesSupported)
1229 __handle_cr_on_shaped(fw);
1231 if (fw != NULL && cre.window == FW_W(fw))
1233 cn_count = __handle_cr_on_client(
1234 &do_send_event, cre, ea, fw, force, force_gravity);
1236 /* Stacking order change requested. Handle this *after* geometry
1237 * changes, since we need the new geometry in occlusion calculations */
1238 if ((cre.value_mask & CWStackMode) &&
1239 (!DO_IGNORE_RESTACK(fw) || force))
1241 __handle_cr_restack(&do_send_event, &cre, fw);
1243 #if 1
1244 /* This causes some ddd windows not to be drawn properly. Reverted back
1245 * to the old method in frame_setup_window. */
1246 /* domivogt (15-Oct-1999): enabled this to work around buggy apps that
1247 * ask for a nonsense height and expect that they really get it. */
1248 if (cn_count == 0 && do_send_event)
1250 cn_count = 1;
1252 else if (cn_count > 0)
1254 do_send_event = 1;
1256 for ( ; cn_count > 0; cn_count--)
1258 SendConfigureNotify(
1259 fw, fw->g.frame.x, fw->g.frame.y, fw->g.frame.width,
1260 fw->g.frame.height, 0, True);
1262 if (do_send_event)
1264 XFlush(dpy);
1266 #endif
1268 return;
1271 static Bool __predicate_button_click(
1272 Display *display, XEvent *event, XPointer arg)
1274 if (event->type == ButtonPress || event->type == ButtonRelease)
1276 return True;
1279 return False;
1282 /* Helper function for __handle_focus_raise_click(). */
1283 static Bool __test_for_motion(int x0, int y0)
1285 int x;
1286 int y;
1287 unsigned int mask;
1288 XEvent e;
1290 /* Query the pointer to do this. We can't check for events here since
1291 * the events are still needed if the pointer moves. */
1293 /* However, some special mouse (e.g., a touchpad with the
1294 * synaptic driver) may handle a double click in a special way
1295 * (for dragging through short touching and holding down the
1296 * finger on the touchpad). Bascially, when you execute a
1297 * double click the first button release is queued after the
1298 * second _physical_ mouse release happen. It seems that
1299 * FQueryPointer may not work as expected: it does not see
1300 * that the button is released on a double click. So, we need
1301 * to check for a button press in the future to avoid a fvwm
1302 * lockup! (olicha 2004-01-31) */
1304 for (x = x0, y = y0; FQueryPointer(
1305 dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
1306 &x, &y, &mask) == True; usleep(20000))
1308 if ((mask & DEFAULT_ALL_BUTTONS_MASK) == 0)
1310 /* all buttons are released */
1311 return False;
1313 else if (abs(x - x0) >= Scr.MoveThreshold ||
1314 abs(y - y0) >= Scr.MoveThreshold)
1316 /* the pointer has moved */
1317 return True;
1319 if (FCheckPeekIfEvent(dpy, &e, __predicate_button_click, NULL))
1321 /* click in the future */
1322 return False;
1324 else
1326 /* The predicate procedure finds no match, no event
1327 * has been removed from the queue and XFlush was
1328 * called. Nothing to do */
1332 /* pointer has moved off screen */
1333 return True;
1336 /* Helper function for __handle_focus_raise_click(). */
1337 static void __check_click_to_focus_or_raise(
1338 hfrc_ret_t *ret_args, const exec_context_t *exc)
1340 FvwmWindow * const fw = exc->w.fw;
1341 const XEvent *te = exc->x.etrigger;
1342 struct
1344 unsigned is_client_click : 1;
1345 unsigned is_focused : 1;
1346 } f;
1348 f.is_focused = !!focus_is_focused(fw);
1349 f.is_client_click = (exc->w.wcontext == C_WINDOW ||
1350 exc->w.wcontext == C_EWMH_DESKTOP);
1351 /* check if we need to raise and/or focus the window */
1352 ret_args->do_focus = focus_query_click_to_focus(fw, exc->w.wcontext);
1353 if (f.is_client_click && !ret_args->do_focus &&
1354 !f.is_focused && FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
1355 !fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
1357 /* Give the window a chance to to take focus itself */
1358 ret_args->do_focus = 1;
1360 if (ret_args->do_focus && focus_is_focused(fw))
1362 ret_args->do_focus = 0;
1364 ret_args->do_raise =
1365 focus_query_click_to_raise(fw, f.is_focused, exc->w.wcontext);
1366 #define EXPERIMENTAL_ROU_HANDLING_V2
1367 #ifdef EXPERIMENTAL_ROU_HANDLING_V2
1368 /* RBW -- Dang! This works without the one in HandleEnterNotify! */
1369 if (ret_args->do_raise && is_on_top_of_layer_and_above_unmanaged(fw))
1370 #else
1371 if (ret_args->do_raise && is_on_top_of_layer(fw))
1372 #endif
1374 ret_args->do_raise = 0;
1376 if ((ret_args->do_focus &&
1377 FP_DO_IGNORE_FOCUS_CLICK_MOTION(FW_FOCUS_POLICY(fw))) ||
1378 (ret_args->do_raise &&
1379 FP_DO_IGNORE_RAISE_CLICK_MOTION(FW_FOCUS_POLICY(fw))))
1381 /* Pass further events to the application and check if a button
1382 * release or motion event occurs next. If we don't do this
1383 * here, the pointer will seem to be frozen in
1384 * __test_for_motion(). */
1385 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1386 if (__test_for_motion(te->xbutton.x_root, te->xbutton.y_root))
1388 /* the pointer was moved, process event normally */
1389 ret_args->do_focus = 0;
1390 ret_args->do_raise = 0;
1393 if (ret_args->do_focus || ret_args->do_raise)
1395 if (!((ret_args->do_focus &&
1396 FP_DO_ALLOW_FUNC_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1397 (ret_args->do_raise &&
1398 FP_DO_ALLOW_FUNC_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1400 ret_args->do_forbid_function = 1;
1402 if (!((ret_args->do_focus &&
1403 FP_DO_PASS_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1404 (ret_args->do_raise &&
1405 FP_DO_PASS_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1407 ret_args->do_swallow_click = 1;
1411 return;
1414 /* Finds out if the click on a window must be used to focus or raise it. */
1415 static void __handle_focus_raise_click(
1416 hfrc_ret_t *ret_args, const exec_context_t *exc)
1418 memset(ret_args, 0, sizeof(*ret_args));
1419 if (exc->w.fw == NULL)
1421 return;
1423 /* check for proper click button and modifiers*/
1424 if (FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) != 0 &&
1425 !(FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) &
1426 (1 << (exc->x.etrigger->xbutton.button - 1))))
1428 /* wrong button, handle click normally */
1429 return;
1431 else if (FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw)) !=
1432 FPOL_ANY_MODIFIER &&
1433 MaskUsedModifiers(
1434 FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw))) !=
1435 MaskUsedModifiers(exc->x.etrigger->xbutton.state))
1437 /* right button but wrong modifiers, handle click normally */
1438 return;
1440 else
1442 __check_click_to_focus_or_raise(ret_args, exc);
1445 return;
1448 /* Helper function for HandleButtonPress */
1449 static Bool __is_bpress_window_handled(const exec_context_t *exc)
1451 Window eventw;
1452 const XEvent *te = exc->x.etrigger;
1454 if (exc->w.fw == NULL)
1456 if ((te->xbutton.window != Scr.Root ||
1457 te->xbutton.subwindow != None) &&
1458 !is_pan_frame(te->xbutton.window))
1460 /* Ignore events in unmanaged windows or subwindows of
1461 * a client */
1462 return False;
1464 else
1466 return True;
1469 eventw = (te->xbutton.subwindow != None &&
1470 te->xany.window != FW_W(exc->w.fw)) ?
1471 te->xbutton.subwindow : te->xany.window;
1472 if (is_frame_hide_window(eventw) || eventw == FW_W_FRAME(exc->w.fw))
1474 return False;
1476 if (!XGetGeometry(
1477 dpy, eventw, &JunkRoot, &JunkX, &JunkY,
1478 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
1479 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1481 /* The window has already died. */
1482 return False;
1485 return True;
1488 /* Helper function for __handle_bpress_on_managed */
1489 static Bool __handle_click_to_focus(const exec_context_t *exc)
1491 fpol_set_focus_by_t set_by;
1493 switch (exc->w.wcontext)
1495 case C_WINDOW:
1496 case C_EWMH_DESKTOP:
1497 set_by = FOCUS_SET_BY_CLICK_CLIENT;
1498 break;
1499 case C_ICON:
1500 set_by = FOCUS_SET_BY_CLICK_ICON;
1501 break;
1502 default:
1503 set_by = FOCUS_SET_BY_CLICK_DECOR;
1504 break;
1506 SetFocusWindow(exc->w.fw, True, set_by);
1507 focus_grab_buttons(exc->w.fw);
1508 if (focus_is_focused(exc->w.fw) && !IS_ICONIFIED(exc->w.fw))
1510 border_draw_decorations(
1511 exc->w.fw, PART_ALL, True, True, CLEAR_ALL, NULL,
1512 NULL);
1515 return focus_is_focused(exc->w.fw);
1518 /* Helper function for __handle_bpress_on_managed */
1519 static Bool __handle_click_to_raise(const exec_context_t *exc)
1521 Bool rc = False;
1522 int is_focused;
1524 is_focused = focus_is_focused(exc->w.fw);
1525 if (focus_query_click_to_raise(exc->w.fw, is_focused, True))
1527 rc = True;
1530 return rc;
1533 /* Helper function for HandleButtonPress */
1534 static void __handle_bpress_stroke(void)
1536 STROKE_CODE(stroke_init());
1537 STROKE_CODE(send_motion = True);
1539 return;
1542 /* Helper function for __handle_bpress_on_managed */
1543 static Bool __handle_bpress_action(
1544 const exec_context_t *exc, char *action)
1546 window_parts part;
1547 Bool do_force;
1548 Bool rc = False;
1550 if (!action || *action == 0)
1552 PressedW = None;
1553 return False;
1555 /* draw pressed in decorations */
1556 part = border_context_to_parts(exc->w.wcontext);
1557 do_force = (part & PART_TITLEBAR) ? True : False;
1558 border_draw_decorations(
1559 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1560 CLEAR_ALL, NULL, NULL);
1561 /* execute the action */
1562 if (IS_ICONIFIED(exc->w.fw))
1564 /* release the pointer since it can't do harm over an icon */
1565 XAllowEvents(dpy, AsyncPointer, CurrentTime);
1567 execute_function(NULL, exc, action, 0);
1568 if (exc->w.wcontext != C_WINDOW && exc->w.wcontext != C_NO_CONTEXT)
1570 WaitForButtonsUp(True);
1571 rc = True;
1573 /* redraw decorations */
1574 PressedW = None;
1575 if (check_if_fvwm_window_exists(exc->w.fw))
1577 part = border_context_to_parts(exc->w.wcontext);
1578 do_force = (part & PART_TITLEBAR) ? True : False;
1579 border_draw_decorations(
1580 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1581 CLEAR_ALL, NULL, NULL);
1584 return rc;
1587 /* Handles button presses on the root window. */
1588 static void __handle_bpress_on_root(const exec_context_t *exc)
1590 char *action;
1592 PressedW = None;
1593 __handle_bpress_stroke();
1594 /* search for an appropriate mouse binding */
1595 action = CheckBinding(
1596 Scr.AllBindings, STROKE_ARG(0) exc->x.etrigger->xbutton.button,
1597 exc->x.etrigger->xbutton.state, GetUnusedModifiers(), C_ROOT,
1598 BIND_BUTTONPRESS, NULL, NULL);
1599 if (action && *action)
1601 const exec_context_t *exc2;
1602 exec_context_changes_t ecc;
1604 ecc.w.wcontext = C_ROOT;
1605 exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
1606 execute_function(NULL, exc2, action, 0);
1607 exc_destroy_context(exc2);
1608 WaitForButtonsUp(True);
1610 else
1612 /* do gnome buttonpress forwarding if win == root */
1613 GNOME_ProxyButtonEvent(exc->x.etrigger);
1616 return;
1619 /* Handles button presses on unmanaged windows */
1620 static void __handle_bpress_on_unmanaged(const exec_context_t *exc)
1622 /* Pass the event to the application. */
1623 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1624 XFlush(dpy);
1626 return;
1629 /* Handles button presses on managed windows */
1630 static void __handle_bpress_on_managed(const exec_context_t *exc)
1632 char *action;
1633 hfrc_ret_t f;
1634 FvwmWindow * const fw = exc->w.fw;
1635 XEvent *e;
1637 e = exc->x.etrigger;
1638 /* Now handle click to focus and click to raise. */
1639 __handle_focus_raise_click(&f, exc);
1640 PressedW = (f.do_forbid_function) ? None : exc->w.w;
1641 if (f.do_focus)
1643 if (!__handle_click_to_focus(exc))
1645 /* Window didn't accept the focus; pass the click to
1646 * the application. */
1647 f.do_swallow_click = 0;
1650 if (f.do_raise)
1652 if (__handle_click_to_raise(exc) == True)
1654 /* We can't raise the window immediately because the
1655 * action bound to the click might be "Lower" or
1656 * "RaiseLower". So mark the window as scheduled to be
1657 * raised after the binding is executed. Functions that
1658 * modify the stacking order will reset this flag. */
1659 SET_SCHEDULED_FOR_RAISE(fw, 1);
1662 /* handle bindings */
1663 if (!f.do_forbid_function)
1665 /* stroke bindings */
1666 __handle_bpress_stroke();
1667 /* mouse bindings */
1668 action = CheckBinding(
1669 Scr.AllBindings, STROKE_ARG(0) e->xbutton.button,
1670 e->xbutton.state, GetUnusedModifiers(),
1671 exc->w.wcontext, BIND_BUTTONPRESS, &fw->class,
1672 fw->name.name);
1673 if (__handle_bpress_action(exc, action))
1675 f.do_swallow_click = 1;
1678 /* raise the window */
1679 if (IS_SCHEDULED_FOR_RAISE(fw))
1681 /* Now that we know the action did not restack the window we
1682 * can raise it.
1683 * dv (10-Aug-2002): We can safely raise the window after
1684 * redrawing it since all the decorations are drawn in the
1685 * window background and no Expose event is generated. */
1686 RaiseWindow(fw, False);
1687 SET_SCHEDULED_FOR_RAISE(fw, 0);
1689 /* clean up */
1690 if (!f.do_swallow_click)
1692 /* pass the click to the application */
1693 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1694 XFlush(dpy);
1696 else if (f.do_focus || f.do_raise)
1698 WaitForButtonsUp(True);
1701 return;
1704 /* restore focus stolen by unmanaged */
1705 static void __refocus_stolen_focus_win(const evh_args_t *ea)
1707 FOCUS_SET(Scr.StolenFocusWin);
1708 ea->exc->x.etrigger->xfocus.window = Scr.StolenFocusWin;
1709 ea->exc->x.etrigger->type = FocusIn;
1710 Scr.UnknownWinFocused = None;
1711 Scr.StolenFocusWin = None;
1712 dispatch_event(ea->exc->x.etrigger);
1714 return;
1717 /* ---------------------------- event handlers ----------------------------- */
1719 void HandleButtonPress(const evh_args_t *ea)
1721 DBUG("HandleButtonPress", "Routine Entered");
1723 GrabEm(CRS_NONE, GRAB_PASSIVE);
1724 if (__is_bpress_window_handled(ea->exc) == False)
1726 __handle_bpress_on_unmanaged(ea->exc);
1728 else if (ea->exc->w.fw != NULL)
1730 __handle_bpress_on_managed(ea->exc);
1732 else
1734 __handle_bpress_on_root(ea->exc);
1736 UngrabEm(GRAB_PASSIVE);
1738 return;
1741 #ifdef HAVE_STROKE
1742 void HandleButtonRelease(const evh_args_t *ea)
1744 char *action;
1745 char *name;
1746 int real_modifier;
1747 const XEvent *te = ea->exc->x.etrigger;
1748 XClassHint *class;
1750 DBUG("HandleButtonRelease", "Routine Entered");
1751 send_motion = False;
1752 stroke_trans (sequence);
1753 DBUG("HandleButtonRelease",sequence);
1754 /* Allows modifier to work (Only R context works here). */
1755 real_modifier = te->xbutton.state - (1 << (7 + te->xbutton.button));
1756 if (ea->exc->w.fw == NULL)
1758 class = NULL;
1759 name = NULL;
1761 else
1763 class = &ea->exc->w.fw->class;
1764 name = ea->exc->w.fw->name.name;
1766 /* need to search for an appropriate stroke binding */
1767 action = CheckBinding(
1768 Scr.AllBindings, sequence, te->xbutton.button, real_modifier,
1769 GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE,
1770 class, name);
1771 /* got a match, now process it */
1772 if (action != NULL && (action[0] != 0))
1774 execute_function(NULL, ea->exc, action, 0);
1775 WaitForButtonsUp(True);
1777 else
1780 * do gnome buttonpress forwarding if win == root
1782 if (Scr.Root == te->xany.window)
1784 GNOME_ProxyButtonEvent(te);
1788 return;
1790 #endif /* HAVE_STROKE */
1792 void HandleClientMessage(const evh_args_t *ea)
1794 const XEvent *te = ea->exc->x.etrigger;
1795 FvwmWindow * const fw = ea->exc->w.fw;
1797 DBUG("HandleClientMessage", "Routine Entered");
1799 /* Process GNOME and EWMH Messages */
1800 if (GNOME_ProcessClientMessage(ea->exc))
1802 return;
1804 else if (EWMH_ProcessClientMessage(ea->exc))
1806 return;
1809 /* handle deletion of tear out menus */
1810 if (fw && IS_TEAR_OFF_MENU(fw) && te->xclient.format == 32 &&
1811 te->xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
1813 menu_close_tear_off_menu(fw);
1814 return;
1817 if (te->xclient.message_type == _XA_WM_CHANGE_STATE &&
1818 fw && te->xclient.data.l[0] == IconicState && !IS_ICONIFIED(fw))
1820 const exec_context_t *exc;
1821 exec_context_changes_t ecc;
1823 ecc.w.wcontext = C_WINDOW;
1824 exc = exc_clone_context(ea->exc, &ecc, ECC_WCONTEXT);
1825 execute_function(NULL, exc, "Iconify", 0);
1826 exc_destroy_context(exc);
1827 return;
1830 /* FIXME: Is this safe enough ? I guess if clients behave
1831 * according to ICCCM and send these messages only if they
1832 * grabbed the pointer, it is OK */
1834 extern Atom _XA_WM_COLORMAP_NOTIFY;
1835 if (te->xclient.message_type == _XA_WM_COLORMAP_NOTIFY)
1837 set_client_controls_colormaps(te->xclient.data.l[1]);
1838 return;
1842 /* CKH - if we get here, it was an unknown client message, so send
1843 * it to the client if it was in a window we know about. I'm not so
1844 * sure this should be done or not, since every other window manager
1845 * I've looked at doesn't. But it might be handy for a free drag and
1846 * drop setup being developed for Linux. */
1847 /* TA: 20091231 - But this confuses QT Drag and Drop since it handles
1848 * processing XSendEvents in an odd order. For now, workaround this
1849 * by using a BugOpts option.
1851 if (fw)
1853 if ((!Scr.bo.do_enable_qt_drag_n_drop_workaround) &&
1854 (te->xclient.window != FW_W(fw)))
1856 XEvent e;
1858 e = *te;
1859 e.xclient.window = FW_W(fw);
1860 FSendEvent(dpy, FW_W(fw), False, NoEventMask, &e);
1865 void HandleColormapNotify(const evh_args_t *ea)
1867 colormap_handle_colormap_notify(ea);
1869 return;
1872 void HandleConfigureRequest(const evh_args_t *ea)
1874 const XEvent *te = ea->exc->x.etrigger;
1875 XConfigureRequestEvent cre;
1876 FvwmWindow *fw = ea->exc->w.fw;
1878 DBUG("HandleConfigureRequest", "Routine Entered");
1880 cre = te->xconfigurerequest;
1881 /* te->xany.window is te->.xconfigurerequest.parent, so the context
1882 * window may be wrong. */
1883 if (XFindContext(dpy, cre.window, FvwmContext, (caddr_t *)&fw) ==
1884 XCNOENT)
1886 fw = NULL;
1888 __handle_configure_request(cre, ea, fw, False, ForgetGravity);
1890 return;
1893 void HandleDestroyNotify(const evh_args_t *ea)
1895 DBUG("HandleDestroyNotify", "Routine Entered");
1897 destroy_window(ea->exc->w.fw);
1898 EWMH_ManageKdeSysTray(
1899 ea->exc->x.etrigger->xdestroywindow.window,
1900 ea->exc->x.etrigger->type);
1901 EWMH_WindowDestroyed();
1902 GNOME_SetClientList();
1904 return;
1907 #define DEBUG_ENTERNOTIFY 0
1908 #if DEBUG_ENTERNOTIFY
1909 static int ecount=0;
1910 #define ENTER_DBG(x) fprintf x;
1911 #else
1912 #define ENTER_DBG(x)
1913 #endif
1914 void HandleEnterNotify(const evh_args_t *ea)
1916 const XEnterWindowEvent *ewp;
1917 XEvent d;
1918 FvwmWindow *sf;
1919 static Bool is_initial_ungrab_pending = True;
1920 Bool is_tear_off_menu;
1921 const XEvent *te = ea->exc->x.etrigger;
1922 FvwmWindow * const fw = ea->exc->w.fw;
1924 DBUG("HandleEnterNotify", "Routine Entered");
1925 ewp = &te->xcrossing;
1926 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)"));
1928 if (
1929 ewp->window == Scr.Root &&
1930 ewp->detail == NotifyInferior && ewp->mode == NotifyNormal)
1932 /* pointer left subwindow */
1933 BroadcastPacket(
1934 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1935 (long)NULL);
1937 else if (
1938 ewp->window == Scr.Root &&
1939 ewp->detail == NotifyNonlinearVirtual)
1941 /* pointer entered screen */
1942 BroadcastPacket(
1943 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1944 (long)NULL);
1946 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
1948 if (fw && !IS_ICONIFIED(fw) && ewp->window == FW_W(fw))
1950 InstallWindowColormaps(fw);
1952 else
1954 /* make sure its for one of our windows */
1955 /* handle a subwindow cmap */
1956 InstallWindowColormaps(NULL);
1959 else if (!fw)
1961 EnterSubWindowColormap(ewp->window);
1963 if (Scr.flags.is_wire_frame_displayed)
1965 ENTER_DBG((stderr, "en: exit: iwfd\n"));
1966 /* Ignore EnterNotify events while a window is resized or moved
1967 * as a wire frame; otherwise the window list may be screwed
1968 * up. */
1969 return;
1971 if (fw)
1973 if (ewp->window != FW_W_FRAME(fw) &&
1974 ewp->window != FW_W_PARENT(fw) &&
1975 ewp->window != FW_W(fw) &&
1976 ewp->window != FW_W_ICON_TITLE(fw) &&
1977 ewp->window != FW_W_ICON_PIXMAP(fw))
1979 /* Ignore EnterNotify that received by any of the sub
1980 * windows that don't handle this event. unclutter
1981 * triggers these events sometimes, re focusing an
1982 * unfocused window under the pointer */
1983 ENTER_DBG((stderr, "en: exit: funny window\n"));
1984 return;
1987 if (Scr.focus_in_pending_window != NULL)
1989 ENTER_DBG((stderr, "en: exit: fipw\n"));
1990 /* Ignore EnterNotify event while we are waiting for a window to
1991 * receive focus via Focus or FlipFocus commands. */
1992 focus_grab_buttons(fw);
1993 return;
1995 if (ewp->mode == NotifyGrab)
1997 ENTER_DBG((stderr, "en: exit: NotifyGrab\n"));
1998 return;
2000 else if (ewp->mode == NotifyNormal)
2002 ENTER_DBG((stderr, "en: NotifyNormal\n"));
2003 if (ewp->detail == NotifyNonlinearVirtual &&
2004 ewp->focus == False && ewp->subwindow != None)
2006 /* This takes care of some buggy apps that forget that
2007 * one of their dialog subwindows has the focus after
2008 * popping up a selection list several times (ddd,
2009 * netscape). I'm not convinced that this does not
2010 * break something else. */
2011 ENTER_DBG((stderr, "en: NN: refreshing focus\n"));
2012 refresh_focus(fw);
2015 else if (ewp->mode == NotifyUngrab)
2017 ENTER_DBG((stderr, "en: NotifyUngrab\n"));
2018 /* Ignore events generated by grabbing or ungrabbing the
2019 * pointer. However, there is no way to prevent the client
2020 * application from handling this event and, for example,
2021 * grabbing the focus. This will interfere with functions that
2022 * transferred the focus to a different window. */
2023 if (is_initial_ungrab_pending)
2025 ENTER_DBG((stderr, "en: NU: initial ungrab pending (lgw = NULL)\n"));
2026 is_initial_ungrab_pending = False;
2027 xcrossing_last_grab_window = NULL;
2029 else
2031 if (ewp->detail == NotifyNonlinearVirtual &&
2032 ewp->focus == False && ewp->subwindow != None)
2034 /* see comment above */
2035 ENTER_DBG((stderr, "en: NU: refreshing focus\n"));
2036 refresh_focus(fw);
2038 if (fw && fw == xcrossing_last_grab_window)
2040 ENTER_DBG((stderr, "en: exit: NU: is last grab window\n"));
2041 if (ewp->window == FW_W_FRAME(fw) ||
2042 ewp->window == FW_W_ICON_TITLE(fw) ||
2043 ewp->window == FW_W_ICON_PIXMAP(fw))
2045 ENTER_DBG((stderr, "en: exit: NU: last grab window = NULL\n"));
2046 xcrossing_last_grab_window = NULL;
2048 focus_grab_buttons(fw);
2050 return;
2052 else if (fw)
2054 if (ewp->window != FW_W_FRAME(fw) &&
2055 ewp->window != FW_W_ICON_TITLE(fw) &&
2056 ewp->window != FW_W_ICON_PIXMAP(fw))
2058 ENTER_DBG((stderr, "en: exit: NU: not frame window\n"));
2059 focus_grab_buttons(fw);
2060 return;
2065 if (fw)
2067 is_initial_ungrab_pending = False;
2070 /* look for a matching leaveNotify which would nullify this EnterNotify
2073 * RBW - if we're in startup, this is a coerced focus, so we don't
2074 * want to save the event time, or exit prematurely.
2076 * Ignore LeaveNotify events for tear out menus - handled by menu code
2078 is_tear_off_menu =
2079 (fw && IS_TEAR_OFF_MENU(fw) && ewp->window == FW_W(fw));
2080 if (!fFvwmInStartup && !is_tear_off_menu &&
2081 FCheckTypedWindowEvent(dpy, ewp->window, LeaveNotify, &d))
2083 if (d.xcrossing.mode == NotifyNormal &&
2084 d.xcrossing.detail != NotifyInferior)
2086 ENTER_DBG((stderr, "en: exit: found LeaveNotify\n"));
2087 return;
2091 if (ewp->window == Scr.Root)
2093 FvwmWindow *lf = get_last_screen_focus_window();
2095 if (!Scr.flags.is_pointer_on_this_screen)
2097 Scr.flags.is_pointer_on_this_screen = 1;
2098 if (lf && lf != &Scr.FvwmRoot &&
2099 !FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(lf)))
2101 SetFocusWindow(lf, True, FOCUS_SET_FORCE);
2103 else if (lf != &Scr.FvwmRoot)
2105 ForceDeleteFocus();
2107 else
2109 /* This was the first EnterNotify event for the
2110 * root window - ignore */
2112 set_last_screen_focus_window(NULL);
2114 else if (!(sf = get_focus_window()) ||
2115 FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2117 DeleteFocus(True);
2119 else if (
2120 Scr.UnknownWinFocused != None && sf != NULL &&
2121 FW_W(sf) == Scr.StolenFocusWin)
2123 __refocus_stolen_focus_win(ea);
2125 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2127 InstallWindowColormaps(NULL);
2129 focus_grab_buttons(lf);
2130 return;
2132 else
2134 Scr.flags.is_pointer_on_this_screen = 1;
2137 /* An EnterEvent in one of the PanFrameWindows activates the Paging or
2138 an EdgeCommand. */
2139 if (is_pan_frame(ewp->window))
2141 char *edge_command = NULL;
2143 if (
2144 Scr.UnknownWinFocused != None &&
2145 (sf = get_focus_window()) != NULL &&
2146 FW_W(sf) == Scr.StolenFocusWin)
2148 __refocus_stolen_focus_win(ea);
2150 /* check for edge commands */
2151 if (ewp->window == Scr.PanFrameTop.win)
2153 edge_command = Scr.PanFrameTop.command;
2155 else if (ewp->window == Scr.PanFrameBottom.win)
2157 edge_command = Scr.PanFrameBottom.command;
2159 else if (ewp->window == Scr.PanFrameLeft.win)
2161 edge_command = Scr.PanFrameLeft.command;
2163 else if (ewp->window == Scr.PanFrameRight.win)
2165 edge_command = Scr.PanFrameRight.command;
2167 if (edge_command && ewp->mode == NotifyUngrab &&
2168 ewp->detail == NotifyAncestor)
2170 /* nothing */
2172 else if (edge_command)
2174 execute_function(NULL, ea->exc, edge_command, 0);
2176 else
2178 /* no edge command for this pan frame - so we do
2179 * HandlePaging */
2180 int delta_x = 0;
2181 int delta_y = 0;
2182 XEvent e;
2184 /* this was in the HandleMotionNotify before, HEDU */
2185 Scr.flags.is_pointer_on_this_screen = 1;
2186 e = *te;
2187 HandlePaging(
2188 &e, Scr.EdgeScrollX, Scr.EdgeScrollY, &JunkX,
2189 &JunkY, &delta_x, &delta_y, True, True, False,
2190 Scr.ScrollDelay);
2191 return;
2194 if (!fw)
2196 return;
2198 if (IS_EWMH_DESKTOP(FW_W(fw)))
2200 BroadcastPacket(
2201 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
2202 (long)NULL);
2203 return;
2205 if (ewp->window == FW_W_FRAME(fw) ||
2206 ewp->window == FW_W_ICON_TITLE(fw) ||
2207 ewp->window == FW_W_ICON_PIXMAP(fw))
2209 BroadcastPacket(
2210 MX_ENTER_WINDOW, 3, (long)FW_W(fw),
2211 (long)FW_W_FRAME(fw), (unsigned long)fw);
2213 sf = get_focus_window();
2214 if (sf && fw != sf && FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2216 ENTER_DBG((stderr, "en: delete focus\n"));
2217 DeleteFocus(True);
2219 focus_grab_buttons(fw);
2220 if (FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
2222 ENTER_DBG((stderr, "en: set mousey focus\n"));
2223 if (ewp->window == FW_W(fw))
2225 /* Event is for the client window...*/
2226 #ifndef EXPERIMENTAL_ROU_HANDLING_V2
2227 /* RBW -- This may still be needed at times, I'm not
2228 *sure yet. */
2229 SetFocusWindowClientEntered(
2230 fw, True, FOCUS_SET_BY_ENTER);
2231 #else
2232 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2233 #endif
2235 else
2237 /* Event is for the frame...*/
2238 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2241 else if (focus_is_focused(fw) && focus_does_accept_input_focus(fw))
2243 /* We have to refresh the focus window here in case we left the
2244 * focused fvwm window. Motif apps may lose the input focus
2245 * otherwise. But do not try to refresh the focus of
2246 * applications that want to handle it themselves. */
2247 focus_force_refresh_focus(fw);
2249 else if (sf != fw)
2251 /* Give the window a chance to grab the buttons needed for
2252 * raise-on-click */
2253 focus_grab_buttons(sf);
2255 if (
2256 Scr.UnknownWinFocused != None && sf != NULL &&
2257 FW_W(sf) == Scr.StolenFocusWin)
2259 __refocus_stolen_focus_win(ea);
2261 /* We get an EnterNotify with mode == UnGrab when fvwm releases the
2262 * grab held during iconification. We have to ignore this, or icon
2263 * title will be initially raised. */
2264 if (IS_ICONIFIED(fw) && (ewp->mode == NotifyNormal) &&
2265 (ewp->window == FW_W_ICON_PIXMAP(fw) ||
2266 ewp->window == FW_W_ICON_TITLE(fw)) &&
2267 FW_W_ICON_PIXMAP(fw) != None)
2269 SET_ICON_ENTERED(fw, 1);
2270 DrawIconWindow(fw, True, False, False, False, NULL);
2272 /* Check for tear off menus */
2273 if (is_tear_off_menu)
2275 menu_enter_tear_off_menu(ea->exc);
2278 return;
2281 void HandleExpose(const evh_args_t *ea)
2283 XEvent e;
2284 FvwmWindow * const fw = ea->exc->w.fw;
2286 e = *ea->exc->x.etrigger;
2287 #if 0
2288 /* This doesn't work well. Sometimes, the expose count is zero although
2289 * dozens of expose events are pending. This happens all the time
2290 * during a shading animation. Simply flush expose events
2291 * unconditionally. */
2292 if (e.xexpose.count != 0)
2294 flush_accumulate_expose(e.xexpose.window, &e);
2296 #else
2297 flush_accumulate_expose(e.xexpose.window, &e);
2298 #endif
2299 if (fw == NULL)
2301 return;
2303 if (e.xany.window == FW_W_ICON_TITLE(fw) ||
2304 e.xany.window == FW_W_ICON_PIXMAP(fw))
2306 DrawIconWindow(fw, True, True, False, False, &e);
2307 return;
2309 else if (IS_TEAR_OFF_MENU(fw) && e.xany.window == FW_W(fw))
2311 /* refresh the contents of the torn out menu */
2312 menu_expose(&e, NULL);
2315 return;
2318 void HandleFocusIn(const evh_args_t *ea)
2320 XEvent d;
2321 Window w = None;
2322 Window focus_w = None;
2323 Window focus_fw = None;
2324 Pixel fc = 0;
2325 Pixel bc = 0;
2326 FvwmWindow *ffw_old = get_focus_window();
2327 FvwmWindow *sf;
2328 Bool do_force_broadcast = False;
2329 Bool is_unmanaged_focused = False;
2330 static Window last_focus_w = None;
2331 static Window last_focus_fw = None;
2332 static Bool was_nothing_ever_focused = True;
2333 FvwmWindow *fw = ea->exc->w.fw;
2335 DBUG("HandleFocusIn", "Routine Entered");
2337 Scr.focus_in_pending_window = NULL;
2338 /* This is a hack to make the PointerKey command work */
2339 if (ea->exc->x.etrigger->xfocus.detail != NotifyPointer)
2341 /**/
2342 w = ea->exc->x.etrigger->xany.window;
2344 while (FCheckTypedEvent(dpy, FocusIn, &d))
2346 /* dito */
2347 if (d.xfocus.detail != NotifyPointer)
2349 /**/
2350 w = d.xany.window;
2353 /* dito */
2354 if (w == None)
2356 return;
2358 /**/
2359 if (XFindContext(dpy, w, FvwmContext, (caddr_t *) &fw) == XCNOENT)
2361 fw = NULL;
2364 Scr.UnknownWinFocused = None;
2365 if (!fw)
2367 if (w != Scr.NoFocusWin)
2369 Scr.UnknownWinFocused = w;
2370 Scr.StolenFocusWin =
2371 (ffw_old != NULL) ? FW_W(ffw_old) : None;
2372 focus_w = w;
2373 is_unmanaged_focused = True;
2375 /* Only show a non-focused window as focused,
2376 * if the focus is on unmanaged and flickering qt dialogs
2377 * workaround is on. */
2378 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2379 !is_unmanaged_focused)
2381 border_draw_decorations(
2382 Scr.Hilite, PART_ALL, False, True, CLEAR_ALL,
2383 NULL, NULL);
2384 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2386 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2388 InstallWindowColormaps(Scr.Hilite);
2390 else
2392 InstallWindowColormaps(NULL);
2396 /* Not very useful if no window that fvwm and its modules know
2397 * about has the focus. */
2398 fc = GetColor(DEFAULT_FORE_COLOR);
2399 bc = GetColor(DEFAULT_BACK_COLOR);
2401 else if (fw != Scr.Hilite ||
2402 /* domivogt (16-May-2000): This check is necessary to force
2403 * sending a M_FOCUS_CHANGE packet after an unmanaged window
2404 * was focused. Otherwise fvwm would believe that Scr.Hilite
2405 * was still focused and not send any info to the modules. */
2406 last_focus_fw == None ||
2407 IS_FOCUS_CHANGE_BROADCAST_PENDING(fw))
2409 do_force_broadcast = IS_FOCUS_CHANGE_BROADCAST_PENDING(fw);
2410 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 0);
2411 if (fw != Scr.Hilite)
2413 border_draw_decorations(
2414 fw, PART_ALL, True, True, CLEAR_ALL, NULL,
2415 NULL);
2417 focus_w = FW_W(fw);
2418 focus_fw = FW_W_FRAME(fw);
2419 fc = fw->hicolors.fore;
2420 bc = fw->hicolors.back;
2421 set_focus_window(fw);
2422 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2424 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2426 InstallWindowColormaps(Scr.Hilite);
2428 else
2430 InstallWindowColormaps(NULL);
2434 else
2436 return;
2438 if (was_nothing_ever_focused || last_focus_fw == None ||
2439 focus_w != last_focus_w || focus_fw != last_focus_fw ||
2440 do_force_broadcast)
2442 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2443 !is_unmanaged_focused)
2445 BroadcastPacket(
2446 M_FOCUS_CHANGE, 5, (long)focus_w,
2447 (long)focus_fw,
2448 (unsigned long)IsLastFocusSetByMouse(),
2449 (long)fc, (long)bc);
2450 EWMH_SetActiveWindow(focus_w);
2452 last_focus_w = focus_w;
2453 last_focus_fw = focus_fw;
2454 was_nothing_ever_focused = False;
2456 if ((sf = get_focus_window()) != ffw_old)
2458 focus_grab_buttons(sf);
2459 focus_grab_buttons(ffw_old);
2462 return;
2465 void HandleFocusOut(const evh_args_t *ea)
2467 if (Scr.UnknownWinFocused != None && Scr.StolenFocusWin != None &&
2468 ea->exc->x.etrigger->xfocus.window == Scr.UnknownWinFocused)
2470 __refocus_stolen_focus_win(ea);
2473 return;
2476 void __handle_key(const evh_args_t *ea, Bool is_press)
2478 char *action;
2479 FvwmWindow *sf;
2480 KeyCode kc;
2481 int kcontext;
2482 const XEvent *te = ea->exc->x.etrigger;
2483 const FvwmWindow * const fw = ea->exc->w.fw;
2484 Bool is_second_binding;
2485 const XClassHint *winClass1, *winClass2;
2486 XClassHint tmp;
2487 char *name1, *name2;
2488 const exec_context_t *exc;
2489 exec_context_changes_t ecc;
2491 PressedW = None;
2493 /* Here's a real hack - some systems have two keys with the
2494 * same keysym and different keycodes. This converts all
2495 * the cases to one keycode. */
2496 kc = XKeysymToKeycode(dpy, XKeycodeToKeysym(dpy, te->xkey.keycode, 0));
2498 /* Check if there is something bound to the key */
2500 sf = get_focus_window();
2501 if (sf == NULL)
2503 tmp.res_name = tmp.res_class = name1 = "root";
2504 winClass1 = &tmp;
2505 kcontext = C_ROOT;
2507 else
2509 winClass1 = &sf->class;
2510 name1 = sf->name.name;
2511 kcontext = (sf == fw ? ea->exc->w.wcontext : C_WINDOW);
2514 if (fw == NULL)
2516 tmp.res_name = tmp.res_class = name2 = "root";
2517 winClass2 = &tmp;
2519 else
2521 winClass2 = &fw->class;
2522 name2 = fw->name.name;
2524 /* Searching the binding list with a different 'type' value
2525 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
2526 * The different context value does though. */
2527 action = CheckTwoBindings(
2528 &is_second_binding, Scr.AllBindings, STROKE_ARG(0) kc,
2529 te->xkey.state, GetUnusedModifiers(), kcontext, BIND_KEYPRESS,
2530 winClass1, name1, ea->exc->w.wcontext, BIND_PKEYPRESS,
2531 winClass2, name2);
2533 if (action != NULL)
2535 if (!is_press)
2537 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2538 return;
2540 exc = ea->exc;
2541 if (is_second_binding == False)
2543 ecc.w.fw = sf;
2544 ecc.w.wcontext = kcontext;
2545 exc = exc_clone_context(
2546 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
2548 execute_function(NULL, exc, action, 0);
2549 if (is_second_binding == False)
2551 exc_destroy_context(exc);
2553 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2554 return;
2557 /* if we get here, no function key was bound to the key. Send it
2558 * to the client if it was in a window we know about. */
2559 sf = get_focus_window();
2560 if (sf && te->xkey.window != FW_W(sf))
2562 XEvent e;
2564 e = *te;
2565 e.xkey.window = FW_W(sf);
2566 FSendEvent(
2567 dpy, e.xkey.window, False,
2568 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2570 else if (fw && te->xkey.window != FW_W(fw))
2572 XEvent e;
2574 e = *te;
2575 e.xkey.window = FW_W(fw);
2576 FSendEvent(
2577 dpy, e.xkey.window, False,
2578 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2580 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2582 return;
2585 void HandleKeyPress(const evh_args_t *ea)
2587 __handle_key(ea, True);
2590 void HandleKeyRelease(const evh_args_t *ea)
2592 __handle_key(ea, False);
2595 void HandleLeaveNotify(const evh_args_t *ea)
2597 const XLeaveWindowEvent *lwp;
2598 const XEvent *te = ea->exc->x.etrigger;
2599 FvwmWindow * const fw = ea->exc->w.fw;
2601 DBUG("HandleLeaveNotify", "Routine Entered");
2603 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)"));
2604 lwp = &te->xcrossing;
2605 if (
2606 lwp->window == Scr.Root &&
2607 lwp->detail == NotifyInferior && lwp->mode == NotifyNormal)
2609 /* pointer entered subwindow */
2610 BroadcastPacket(
2611 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2612 (long)NULL);
2614 else if (
2615 lwp->window == Scr.Root &&
2616 lwp->detail == NotifyNonlinearVirtual)
2618 /* pointer left screen */
2619 BroadcastPacket(
2620 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2621 (long)NULL);
2623 /* Ignore LeaveNotify events while a window is resized or moved as a
2624 * wire frame; otherwise the window list may be screwed up. */
2625 if (Scr.flags.is_wire_frame_displayed)
2627 return;
2629 if (lwp->mode != NotifyNormal)
2631 /* Ignore events generated by grabbing or ungrabbing the
2632 * pointer. However, there is no way to prevent the client
2633 * application from handling this event and, for example,
2634 * grabbing the focus. This will interfere with functions that
2635 * transferred the focus to a different window. It is
2636 * necessary to check for LeaveNotify events on the client
2637 * window too in case buttons are not grabbed on it. */
2638 if (lwp->mode == NotifyGrab && fw &&
2639 (lwp->window == FW_W_FRAME(fw) ||
2640 lwp->window == FW_W(fw) ||
2641 lwp->window == FW_W_ICON_TITLE(fw) ||
2642 lwp->window == FW_W_ICON_PIXMAP(fw)))
2644 ENTER_DBG((stderr, "ln: *** lgw = 0x%08x\n", (int)fw));
2645 xcrossing_last_grab_window = fw;
2647 #ifdef FOCUS_EXPANDS_TITLE
2648 if (fw && IS_ICONIFIED(fw))
2650 SET_ICON_ENTERED(fw, 0);
2651 DrawIconWindow(
2652 fw, True, False, False, False, NULL);
2654 #endif
2655 return;
2657 /* CDE-like behaviour of raising the icon title if the icon
2658 gets the focus (in particular if the cursor is over the icon) */
2659 if (fw && IS_ICONIFIED(fw))
2661 SET_ICON_ENTERED(fw,0);
2662 DrawIconWindow(fw, True, False, False, False, NULL);
2665 /* An LeaveEvent in one of the PanFrameWindows activates
2666 an EdgeLeaveCommand. */
2667 if (is_pan_frame(lwp->window))
2669 char *edge_command_leave = NULL;
2671 /* check for edge commands */
2672 if (lwp->window == Scr.PanFrameTop.win)
2674 edge_command_leave = Scr.PanFrameTop.command_leave;
2676 else if (lwp->window == Scr.PanFrameBottom.win)
2678 edge_command_leave = Scr.PanFrameBottom.command_leave;
2680 else if (lwp->window == Scr.PanFrameLeft.win)
2682 edge_command_leave = Scr.PanFrameLeft.command_leave;
2684 else if (lwp->window == Scr.PanFrameRight.win)
2686 edge_command_leave = Scr.PanFrameRight.command_leave;
2688 if (edge_command_leave && lwp->mode == NotifyUngrab &&
2689 lwp->detail == NotifyAncestor)
2691 /* nothing */
2693 else if (edge_command_leave)
2695 execute_function(NULL, ea->exc, edge_command_leave, 0);
2700 /* If we leave the root window, then we're really moving
2701 * another screen on a multiple screen display, and we
2702 * need to de-focus and unhighlight to make sure that we
2703 * don't end up with more than one highlighted window at a time */
2704 if (lwp->window == Scr.Root &&
2705 /* domivogt (16-May-2000): added this test because somehow fvwm
2706 * sometimes gets a LeaveNotify on the root window although it is
2707 * single screen. */
2708 Scr.NumberOfScreens > 1)
2710 if (lwp->mode == NotifyNormal)
2712 if (lwp->detail != NotifyInferior)
2714 FvwmWindow *sf = get_focus_window();
2716 Scr.flags.is_pointer_on_this_screen = 0;
2717 set_last_screen_focus_window(sf);
2718 if (sf != NULL)
2720 DeleteFocus(True);
2722 if (Scr.Hilite != NULL)
2724 border_draw_decorations(
2725 Scr.Hilite, PART_ALL, False,
2726 True, CLEAR_ALL, NULL, NULL);
2731 else
2733 /* handle a subwindow cmap */
2734 LeaveSubWindowColormap(te->xany.window);
2736 if (fw != NULL &&
2737 (lwp->window == FW_W_FRAME(fw) ||
2738 lwp->window == FW_W_ICON_TITLE(fw) ||
2739 lwp->window == FW_W_ICON_PIXMAP(fw)))
2741 BroadcastPacket(
2742 MX_LEAVE_WINDOW, 3, (long)FW_W(fw),
2743 (long)FW_W_FRAME(fw), (unsigned long)fw);
2746 return;
2749 void HandleMapNotify(const evh_args_t *ea)
2751 Bool is_on_this_page = False;
2752 const XEvent *te = ea->exc->x.etrigger;
2753 FvwmWindow * const fw = ea->exc->w.fw;
2755 DBUG("HandleMapNotify", "Routine Entered");
2757 if (!fw)
2759 if (te->xmap.override_redirect == True &&
2760 te->xmap.window != Scr.NoFocusWin)
2762 XSelectInput(dpy, te->xmap.window, XEVMASK_ORW);
2763 XFlush(dpy);
2764 Scr.UnknownWinFocused = te->xmap.window;
2766 return;
2768 if (te->xmap.window == FW_W_FRAME(fw))
2770 /* Now that we know the frame is mapped after capturing the
2771 * window we do not need StructureNotifyMask events anymore. */
2772 XSelectInput(dpy, FW_W_FRAME(fw), XEVMASK_FRAMEW);
2773 XFlush(dpy);
2775 /* Except for identifying over-ride redirect window mappings, we
2776 * don't need or want windows associated with the
2777 * SubstructureNotifyMask */
2778 if (te->xmap.event != te->xmap.window)
2780 return;
2782 SET_MAP_PENDING(fw, 0);
2783 /* don't map if the event was caused by a de-iconify */
2784 if (IS_ICONIFY_PENDING(fw))
2786 return;
2789 /* Make sure at least part of window is on this page before giving it
2790 * focus... */
2791 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2794 * Need to do the grab to avoid race condition of having server send
2795 * MapNotify to client before the frame gets mapped; this is bad because
2796 * the client would think that the window has a chance of being viewable
2797 * when it really isn't.
2799 MyXGrabServer (dpy);
2800 if (FW_W_ICON_TITLE(fw))
2802 XUnmapWindow(dpy, FW_W_ICON_TITLE(fw));
2804 if (FW_W_ICON_PIXMAP(fw) != None)
2806 XUnmapWindow(dpy, FW_W_ICON_PIXMAP(fw));
2808 XMapSubwindows(dpy, FW_W_FRAME(fw));
2809 if (fw->Desk == Scr.CurrentDesk)
2811 XMapWindow(dpy, FW_W_FRAME(fw));
2813 if (IS_ICONIFIED(fw))
2815 BroadcastPacket(
2816 M_DEICONIFY, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2817 (unsigned long)fw);
2819 else
2821 BroadcastPacket(
2822 M_MAP, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2823 (unsigned long)fw);
2826 if (is_on_this_page &&
2827 focus_query_open_grab_focus(fw, get_focus_window()) == True)
2829 SetFocusWindow(fw, True, FOCUS_SET_FORCE);
2831 border_draw_decorations(
2832 fw, PART_ALL, (fw == get_focus_window()) ? True : False, True,
2833 CLEAR_ALL, NULL, NULL);
2834 MyXUngrabServer (dpy);
2835 SET_MAPPED(fw, 1);
2836 SET_ICONIFIED(fw, 0);
2837 SET_ICON_UNMAPPED(fw, 0);
2838 if (DO_ICONIFY_AFTER_MAP(fw))
2840 initial_window_options_t win_opts;
2842 /* finally, if iconification was requested before the window
2843 * was mapped, request it now. */
2844 memset(&win_opts, 0, sizeof(win_opts));
2845 Iconify(fw, &win_opts);
2846 SET_ICONIFY_AFTER_MAP(fw, 0);
2848 focus_grab_buttons_on_layer(fw->layer);
2850 return;
2853 void HandleMappingNotify(const evh_args_t *ea)
2855 XRefreshKeyboardMapping(&ea->exc->x.etrigger->xmapping);
2857 return;
2860 void HandleMapRequest(const evh_args_t *ea)
2862 DBUG("HandleMapRequest", "Routine Entered");
2864 if (fFvwmInStartup)
2866 /* Just map the damn thing, decorations are added later
2867 * in CaptureAllWindows. */
2868 XMapWindow(dpy, ea->exc->x.etrigger->xmaprequest.window);
2869 return;
2871 HandleMapRequestKeepRaised(ea, None, NULL, NULL);
2873 return;
2876 void HandleMapRequestKeepRaised(
2877 const evh_args_t *ea, Window KeepRaised, FvwmWindow *ReuseWin,
2878 initial_window_options_t *win_opts)
2880 Bool is_on_this_page = False;
2881 Bool is_new_window = False;
2882 FvwmWindow *tmp;
2883 FvwmWindow *sf;
2884 initial_window_options_t win_opts_bak;
2885 Window ew;
2886 FvwmWindow *fw;
2887 extern Bool Restarting;
2888 const char *initial_map_command;
2890 initial_map_command = NULL;
2891 if (win_opts == NULL)
2893 memset(&win_opts_bak, 0, sizeof(win_opts_bak));
2894 win_opts = &win_opts_bak;
2896 ew = ea->exc->w.w;
2897 if (ReuseWin == NULL)
2899 Window pw;
2901 pw = ea->exc->x.etrigger->xmaprequest.parent;
2902 if (XFindContext(dpy, ew, FvwmContext, (caddr_t *)&fw) ==
2903 XCNOENT)
2905 fw = NULL;
2907 if (fw != NULL && IS_MAP_PENDING(fw))
2909 /* The window is already going to be mapped, no need to
2910 * do that twice */
2911 return;
2914 else
2916 fw = ReuseWin;
2919 if (fw == NULL && EWMH_IsKdeSysTrayWindow(ew))
2921 /* This means that the window is swallowed by kicker and that
2922 * kicker restart or exit. As we should assume that kicker
2923 * restart we should return here, if not we go into trouble
2924 * ... */
2925 return;
2927 if (!win_opts->flags.do_override_ppos)
2929 XFlush(dpy);
2932 /* If the window has never been mapped before ... */
2933 if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
2935 check_if_event_args args;
2936 XEvent dummy;
2938 args.w = ew;
2939 args.do_return_true = True;
2940 args.do_return_true_cr = False;
2941 if (
2942 FCheckIfEvent(
2943 dpy, &dummy, test_withdraw_request,
2944 (XPointer)&args)) {
2945 /* The window is moved back to the WithdrawnState
2946 * immideately. Don't map it.
2948 * However, send make sure that a WM_STATE
2949 * PropertyNotify event is sent to the window.
2950 * QT needs this.
2952 Atom atype;
2953 int aformat;
2954 unsigned long nitems, bytes_remain;
2955 unsigned char *prop;
2957 if (
2958 XGetWindowProperty(
2959 dpy, ew, _XA_WM_STATE, 0L, 3L, False,
2960 _XA_WM_STATE, &atype, &aformat,
2961 &nitems,&bytes_remain,&prop)
2962 == Success)
2964 if (prop != NULL)
2966 XFree(prop);
2967 XDeleteProperty(dpy, ew, _XA_WM_STATE);
2969 else
2971 XPropertyEvent ev;
2972 ev.type = PropertyNotify;
2973 ev.display = dpy;
2974 ev.window = ew;
2975 ev.atom = _XA_WM_STATE;
2976 ev.time = fev_get_evtime();
2977 ev.state = PropertyDelete;
2978 FSendEvent(
2979 dpy, ew, True,
2980 PropertyChangeMask,
2981 (XEvent*)&ev);
2985 return;
2988 /* Add decorations. */
2989 fw = AddWindow(
2990 &initial_map_command, ea->exc, ReuseWin, win_opts);
2991 if (fw == AW_NO_WINDOW)
2993 return;
2995 else if (fw == AW_UNMANAGED)
2997 XMapWindow(dpy, ew);
2998 return;
3000 is_new_window = True;
3003 * Make sure at least part of window is on this page
3004 * before giving it focus...
3006 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3007 if (KeepRaised != None)
3009 XRaiseWindow(dpy, KeepRaised);
3011 /* If it's not merely iconified, and we have hints, use them. */
3013 if (IS_ICONIFIED(fw))
3015 /* If no hints, or currently an icon, just "deiconify" */
3016 DeIconify(fw);
3018 else if (IS_MAPPED(fw))
3020 /* the window is already mapped - fake a MapNotify event */
3021 fake_map_unmap_notify(fw, MapNotify);
3023 else
3025 int state;
3027 if (fw->wmhints && (fw->wmhints->flags & StateHint))
3029 state = fw->wmhints->initial_state;
3031 else
3033 state = NormalState;
3035 if (win_opts->initial_state != DontCareState)
3037 state = win_opts->initial_state;
3040 switch (state)
3042 case DontCareState:
3043 case NormalState:
3044 case InactiveState:
3045 default:
3046 MyXGrabServer(dpy);
3047 if (fw->Desk == Scr.CurrentDesk)
3049 Bool do_grab_focus;
3051 SET_MAP_PENDING(fw, 1);
3052 XMapWindow(dpy, FW_W_FRAME(fw));
3053 XMapWindow(dpy, FW_W(fw));
3054 SetMapStateProp(fw, NormalState);
3055 if (Scr.flags.is_map_desk_in_progress)
3057 do_grab_focus = False;
3059 else if (!is_on_this_page)
3061 do_grab_focus = False;
3063 else if (focus_query_open_grab_focus(
3064 fw, get_focus_window()) ==
3065 True)
3067 do_grab_focus = True;
3069 else
3071 do_grab_focus = False;
3073 if (do_grab_focus)
3075 SetFocusWindow(
3076 fw, True, FOCUS_SET_FORCE);
3078 else
3080 /* make sure the old focused window
3081 * still has grabbed all necessary
3082 * buttons. */
3083 focus_grab_buttons(
3084 get_focus_window());
3087 else
3089 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
3090 /* nope, this is forbidden by the ICCCM2 */
3091 XMapWindow(dpy, FW_W(fw));
3092 SetMapStateProp(fw, NormalState);
3093 #else
3094 /* Since we will not get a MapNotify, set the
3095 * IS_MAPPED flag manually. */
3096 SET_MAPPED(fw, 1);
3097 SetMapStateProp(fw, IconicState);
3098 /* fake that the window was mapped to allow
3099 * modules to swallow it */
3100 BroadcastPacket(
3101 M_MAP, 3, (long)FW_W(fw),
3102 (long)FW_W_FRAME(fw),
3103 (unsigned long)fw);
3104 #endif
3106 /* TA: 20090125: We *have* to handle
3107 * InitialMapCommand here and not in AddWindow() to
3108 * allow for correct timings when the window is truly
3109 * mapped. (c.f. things like Iconify.)
3112 /* TA: 20091212: But only do this when we're *not*
3113 * restarting -- the window is still mapped, but gets
3114 * recaptured -- we don't want to trigger this event
3115 * again. Otherwise we end up toggling the state of
3116 * the window in situations where the
3117 * InitialMapCommand is Iconify or Maximize, for
3118 * instance.
3120 if ((initial_map_command != NULL) &&
3121 (!Restarting && Scr.flags.are_windows_captured))
3123 execute_function_override_window(
3124 NULL, ea->exc,
3125 (char *)initial_map_command, 0, fw);
3127 MyXUngrabServer(dpy);
3128 break;
3130 case IconicState:
3131 if (is_new_window)
3133 /* the window will not be mapped - fake a
3134 * MapNotify and an UnmapNotify event. Can't
3135 * remember exactly why this is necessary, but
3136 * probably something w/ (de)iconify state
3137 * confusion. */
3138 fake_map_unmap_notify(fw, MapNotify);
3139 fake_map_unmap_notify(fw, UnmapNotify);
3141 if (win_opts->flags.is_iconified_by_parent ||
3142 ((tmp = get_transientfor_fvwmwindow(fw)) &&
3143 IS_ICONIFIED(tmp)))
3145 win_opts->flags.is_iconified_by_parent = 0;
3146 SET_ICONIFIED_BY_PARENT(fw, 1);
3148 if (USE_ICON_POSITION_HINT(fw) && fw->wmhints &&
3149 (fw->wmhints->flags & IconPositionHint))
3151 win_opts->default_icon_x = fw->wmhints->icon_x;
3152 win_opts->default_icon_y = fw->wmhints->icon_y;
3154 Iconify(fw, win_opts);
3155 break;
3158 if (IS_SHADED(fw))
3160 BroadcastPacket(
3161 M_WINDOWSHADE, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
3162 (unsigned long)fw);
3164 /* If the newly mapped window overlaps the focused window, make sure
3165 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3166 sf = get_focus_window();
3167 if (sf != NULL)
3169 focus_grab_buttons(sf);
3171 if (win_opts->flags.is_menu)
3173 SET_MAPPED(fw, 1);
3174 SET_MAP_PENDING(fw, 0);
3176 EWMH_SetClientList();
3177 EWMH_SetClientListStacking();
3178 GNOME_SetClientList();
3180 return;
3183 #ifdef HAVE_STROKE
3184 void HandleMotionNotify(const evh_args_t *ea)
3186 DBUG("HandleMotionNotify", "Routine Entered");
3188 if (send_motion == True)
3190 stroke_record(
3191 ea->exc->x.etrigger->xmotion.x,
3192 ea->exc->x.etrigger->xmotion.y);
3195 return;
3197 #endif /* HAVE_STROKE */
3199 void HandlePropertyNotify(const evh_args_t *ea)
3201 Bool OnThisPage = False;
3202 Bool has_icon_changed = False;
3203 Bool has_icon_pixmap_hint_changed = False;
3204 Bool has_icon_window_hint_changed = False;
3205 FlocaleNameString new_name = { NoName, NULL };
3206 int old_wmhints_flags;
3207 const XEvent *te = ea->exc->x.etrigger;
3208 char *urgency_action = NULL;
3209 FvwmWindow * const fw = ea->exc->w.fw;
3211 DBUG("HandlePropertyNotify", "Routine Entered");
3213 if (te->xproperty.window == Scr.Root &&
3214 te->xproperty.state == PropertyNewValue &&
3215 (te->xproperty.atom == _XA_XSETROOT_ID ||
3216 te->xproperty.atom == _XA_XROOTPMAP_ID))
3218 /* background change */
3219 /* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3220 * no property notify?). _XA_XROOTPMAP_ID is used by Esetroot
3221 * compatible program: the problem here is that with some
3222 * Esetroot compatible program we get the message _before_ the
3223 * background change. This is fixed with Esetroot 9.2 (not yet
3224 * released, 2002-01-14) */
3226 /* update icon window with some alpha and tear-off menu */
3227 FvwmWindow *t;
3229 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3231 int cs;
3232 int t_cs = -1;
3233 int b_cs = t->icon_background_cs;
3234 Bool draw_picture = False;
3235 Bool draw_title = False;
3237 /* redraw ParentRelative tear-off menu */
3238 menu_redraw_transparent_tear_off_menu(t, True);
3240 if (!IS_ICONIFIED(t) || IS_ICON_SUPPRESSED(t))
3242 continue;
3244 if (Scr.Hilite == t)
3246 if (t->icon_title_cs_hi >= 0)
3248 t_cs = cs = t->icon_title_cs_hi;
3250 else
3252 cs = t->cs_hi;
3255 else
3257 if (t->icon_title_cs >= 0)
3259 t_cs = cs = t->icon_title_cs;
3261 else
3263 cs = t->cs;
3266 if (t->icon_alphaPixmap != None ||
3267 (cs >= 0 &&
3268 Colorset[cs].icon_alpha_percent < 100) ||
3269 CSET_IS_TRANSPARENT_PR(b_cs) ||
3270 (!IS_ICON_SHAPED(t) &&
3271 t->icon_background_padding > 0))
3273 draw_picture = True;
3275 if (CSET_IS_TRANSPARENT_PR(t_cs))
3277 draw_title = True;
3279 if (draw_title || draw_picture)
3281 DrawIconWindow(
3282 t, draw_title, draw_picture, False,
3283 draw_picture, NULL);
3286 if (te->xproperty.atom == _XA_XROOTPMAP_ID)
3288 update_root_transparent_colorset(te->xproperty.atom);
3290 BroadcastPropertyChange(
3291 MX_PROPERTY_CHANGE_BACKGROUND, 0, 0, "");
3292 return;
3295 if (!fw)
3297 return;
3299 if (XGetGeometry(
3300 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3301 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
3302 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
3304 return;
3308 * Make sure at least part of window is on this page
3309 * before giving it focus...
3311 OnThisPage = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3313 switch (te->xproperty.atom)
3315 case XA_WM_TRANSIENT_FOR:
3316 flush_property_notify(XA_WM_TRANSIENT_FOR, FW_W(fw));
3317 if (setup_transientfor(fw) == True)
3319 RaiseWindow(fw, False);
3321 break;
3323 case XA_WM_NAME:
3324 flush_property_notify(XA_WM_NAME, FW_W(fw));
3325 if (HAS_EWMH_WM_NAME(fw))
3327 return;
3329 FlocaleGetNameProperty(XGetWMName, dpy, FW_W(fw), &new_name);
3330 if (new_name.name == NULL)
3332 FlocaleFreeNameProperty(&new_name);
3333 return;
3335 if (strlen(new_name.name) > MAX_WINDOW_NAME_LEN)
3337 /* limit to prevent hanging X server */
3338 (new_name.name)[MAX_WINDOW_NAME_LEN] = 0;
3340 if (fw->name.name && strcmp(new_name.name, fw->name.name) == 0)
3342 /* migo: some apps update their names every second */
3343 /* griph: make sure we don't free the property if it
3344 is THE same name */
3345 if (new_name.name != fw->name.name)
3347 FlocaleFreeNameProperty(&new_name);
3349 return;
3352 free_window_names(fw, True, False);
3353 fw->name = new_name;
3354 SET_NAME_CHANGED(fw, 1);
3355 if (fw->name.name == NULL)
3357 fw->name.name = NoName; /* must not happen */
3359 setup_visible_name(fw, False);
3360 BroadcastWindowIconNames(fw, True, False);
3362 /* fix the name in the title bar */
3363 if (!IS_ICONIFIED(fw))
3365 border_draw_decorations(
3366 fw, PART_TITLE, (Scr.Hilite == fw), True,
3367 CLEAR_ALL, NULL, NULL);
3369 EWMH_SetVisibleName(fw, False);
3371 * if the icon name is NoName, set the name of the icon to be
3372 * the same as the window
3374 if (!WAS_ICON_NAME_PROVIDED(fw))
3376 fw->icon_name = fw->name;
3377 setup_visible_name(fw, True);
3378 BroadcastWindowIconNames(fw, False, True);
3379 RedoIconName(fw);
3381 break;
3383 case XA_WM_ICON_NAME:
3384 flush_property_notify(XA_WM_ICON_NAME, FW_W(fw));
3385 if (HAS_EWMH_WM_ICON_NAME(fw))
3387 return;
3389 FlocaleGetNameProperty(
3390 XGetWMIconName, dpy, FW_W(fw), &new_name);
3391 if (new_name.name == NULL)
3393 FlocaleFreeNameProperty(&new_name);
3394 return;
3396 if (new_name.name && strlen(new_name.name) > MAX_ICON_NAME_LEN)
3398 /* limit to prevent hanging X server */
3399 (new_name.name)[MAX_ICON_NAME_LEN] = 0;
3401 if (fw->icon_name.name &&
3402 strcmp(new_name.name, fw->icon_name.name) == 0)
3404 /* migo: some apps update their names every second */
3405 /* griph: make sure we don't free the property if it
3406 is THE same name */
3407 if (new_name.name != fw->icon_name.name)
3409 FlocaleFreeNameProperty(&new_name);
3411 return;
3414 free_window_names(fw, False, True);
3415 fw->icon_name = new_name;
3416 SET_WAS_ICON_NAME_PROVIDED(fw, 1);
3417 if (fw->icon_name.name == NULL)
3419 /* currently never happens */
3420 fw->icon_name.name = fw->name.name;
3421 SET_WAS_ICON_NAME_PROVIDED(fw, 0);
3423 setup_visible_name(fw, True);
3424 BroadcastWindowIconNames(fw, False, True);
3425 RedoIconName(fw);
3426 EWMH_SetVisibleName(fw, True);
3427 break;
3429 case XA_WM_HINTS:
3430 flush_property_notify(XA_WM_HINTS, FW_W(fw));
3431 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3432 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3434 old_wmhints_flags = 0;
3435 if (fw->wmhints)
3437 old_wmhints_flags = fw->wmhints->flags;
3438 XFree ((char *) fw->wmhints);
3440 setup_wm_hints(fw);
3441 if (fw->wmhints == NULL)
3443 return;
3447 * rebuild icon if the client either provides an icon
3448 * pixmap or window or has reset the hints to `no icon'.
3450 if ((fw->wmhints->flags & IconPixmapHint) ||
3451 (old_wmhints_flags & IconPixmapHint))
3453 ICON_DBG((stderr, "hpn: iph changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconPixmapHint), fw->name));
3454 has_icon_pixmap_hint_changed = True;
3456 if ((fw->wmhints->flags & IconWindowHint) ||
3457 (old_wmhints_flags & IconWindowHint))
3459 ICON_DBG((stderr, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconWindowHint), fw->name));
3460 has_icon_window_hint_changed = True;
3461 SET_USE_EWMH_ICON(fw, False);
3463 increase_icon_hint_count(fw);
3464 if (has_icon_window_hint_changed ||
3465 has_icon_pixmap_hint_changed)
3467 if (ICON_OVERRIDE_MODE(fw) == ICON_OVERRIDE)
3469 ICON_DBG((stderr, "hpn: icon override '%s'\n", fw->name));
3470 has_icon_changed = False;
3472 else if (ICON_OVERRIDE_MODE(fw) ==
3473 NO_ACTIVE_ICON_OVERRIDE)
3475 if (has_icon_pixmap_hint_changed)
3477 if (WAS_ICON_HINT_PROVIDED(fw) ==
3478 ICON_HINT_MULTIPLE)
3480 ICON_DBG((stderr, "hpn: using further iph '%s'\n", fw->name));
3481 has_icon_changed = True;
3483 else if (fw->icon_bitmap_file ==
3484 NULL ||
3485 fw->icon_bitmap_file ==
3486 Scr.DefaultIcon)
3488 ICON_DBG((stderr, "hpn: using first iph '%s'\n", fw->name));
3489 has_icon_changed = True;
3491 else
3493 /* ignore the first icon pixmap
3494 * hint if the application did
3495 * not provide it from the
3496 * start */
3497 ICON_DBG((stderr, "hpn: first iph ignored '%s'\n", fw->name));
3498 has_icon_changed = False;
3501 else if (has_icon_window_hint_changed)
3503 ICON_DBG((stderr, "hpn: using iwh '%s'\n", fw->name));
3504 has_icon_changed = True;
3506 else
3508 ICON_DBG((stderr, "hpn: iwh not changed, hint ignored '%s'\n", fw->name));
3509 has_icon_changed = False;
3512 else /* NO_ICON_OVERRIDE */
3514 ICON_DBG((stderr, "hpn: using hint '%s'\n", fw->name));
3515 has_icon_changed = True;
3518 if (USE_EWMH_ICON(fw))
3520 has_icon_changed = False;
3523 if (has_icon_changed)
3525 ICON_DBG((stderr, "hpn: icon changed '%s'\n", fw->name));
3526 /* Okay, the icon hint has changed and style
3527 * options tell us to honour this change. Now
3528 * let's see if we have to use the application
3529 * provided pixmap or window (if any), the icon
3530 * file provided by the window's style or the
3531 * default style's icon. */
3532 if (fw->icon_bitmap_file == Scr.DefaultIcon)
3534 fw->icon_bitmap_file = NULL;
3536 if (!fw->icon_bitmap_file &&
3537 !(fw->wmhints->flags &
3538 (IconPixmapHint|IconWindowHint)))
3540 fw->icon_bitmap_file =
3541 (Scr.DefaultIcon) ?
3542 Scr.DefaultIcon : NULL;
3544 fw->iconPixmap = (Window)NULL;
3545 ChangeIconPixmap(fw);
3549 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3550 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3551 * Treat urgency changes by calling user-settable functions.
3552 * These could e.g. deiconify and raise the window or
3553 * temporarily change the decor. */
3554 if (!(old_wmhints_flags & XUrgencyHint) &&
3555 (fw->wmhints->flags & XUrgencyHint))
3557 urgency_action = "Function UrgencyFunc";
3559 if ((old_wmhints_flags & XUrgencyHint) &&
3560 !(fw->wmhints->flags & XUrgencyHint))
3562 urgency_action = "Function UrgencyDoneFunc";
3564 if (urgency_action)
3566 const exec_context_t *exc;
3567 exec_context_changes_t ecc;
3569 ecc.w.fw = fw;
3570 ecc.w.wcontext = C_WINDOW;
3571 exc = exc_clone_context(
3572 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
3573 execute_function(NULL, exc, urgency_action, 0);
3574 exc_destroy_context(exc);
3576 break;
3577 case XA_WM_NORMAL_HINTS:
3578 /* just mark wm normal hints as changed and look them up when
3579 * the next ConfigureRequest w/ x, y, width or height set
3580 * arrives. */
3581 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 1);
3582 break;
3583 default:
3584 if (te->xproperty.atom == _XA_WM_PROTOCOLS)
3586 FetchWmProtocols (fw);
3588 else if (te->xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
3590 FetchWmColormapWindows (fw); /* frees old data */
3591 ReInstallActiveColormap();
3593 else if (te->xproperty.atom == _XA_WM_STATE)
3595 if (fw && OnThisPage && focus_is_focused(fw) &&
3596 FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
3598 /* refresh the focus - why? */
3599 focus_force_refresh_focus(fw);
3602 else
3604 EWMH_ProcessPropertyNotify(ea->exc);
3606 break;
3610 void HandleReparentNotify(const evh_args_t *ea)
3612 const XEvent *te = ea->exc->x.etrigger;
3613 FvwmWindow * const fw = ea->exc->w.fw;
3615 if (!fw)
3617 return;
3619 if (te->xreparent.parent == Scr.Root)
3621 /* Ignore reparenting to the root window. In some cases these
3622 * events are selected although the window is no longer
3623 * managed. */
3624 return;
3626 if (te->xreparent.parent != FW_W_FRAME(fw))
3628 /* window was reparented by someone else, destroy the frame */
3629 SetMapStateProp(fw, WithdrawnState);
3630 EWMH_RestoreInitialStates(fw, te->type);
3631 if (!IS_TEAR_OFF_MENU(fw))
3633 XRemoveFromSaveSet(dpy, te->xreparent.window);
3634 XSelectInput(dpy, te->xreparent.window, NoEventMask);
3636 else
3638 XSelectInput(dpy, te->xreparent.window, XEVMASK_MENUW);
3640 discard_events(XEVMASK_FRAMEW);
3641 destroy_window(fw);
3642 EWMH_ManageKdeSysTray(te->xreparent.window, te->type);
3643 EWMH_WindowDestroyed();
3646 return;
3649 void HandleSelectionRequest(const evh_args_t *ea)
3651 icccm2_handle_selection_request(ea->exc->x.etrigger);
3653 return;
3656 void HandleSelectionClear(const evh_args_t *ea)
3658 icccm2_handle_selection_clear();
3660 return;
3663 void HandleShapeNotify(const evh_args_t *ea)
3665 FvwmWindow * const fw = ea->exc->w.fw;
3667 DBUG("HandleShapeNotify", "Routine Entered");
3669 if (FShapesSupported)
3671 const FShapeEvent *sev =
3672 (const FShapeEvent *)(ea->exc->x.etrigger);
3674 if (!fw)
3676 return;
3678 if (sev->kind != FShapeBounding)
3680 return;
3682 frame_setup_shape(
3683 fw, fw->g.frame.width, fw->g.frame.height, sev->shaped);
3684 GNOME_SetWinArea(fw);
3685 EWMH_SetFrameStrut(fw);
3686 if (!IS_ICONIFIED(fw))
3688 border_redraw_decorations(fw);
3692 return;
3695 void HandleUnmapNotify(const evh_args_t *ea)
3697 int dstx, dsty;
3698 Window dumwin;
3699 XEvent dummy;
3700 XEvent map_event;
3701 const XEvent *te = ea->exc->x.etrigger;
3702 int weMustUnmap;
3703 Bool focus_grabbed;
3704 Bool must_return = False;
3705 Bool do_map = False;
3706 FvwmWindow * const fw = ea->exc->w.fw;
3707 Window pw;
3708 Window cw;
3710 DBUG("HandleUnmapNotify", "Routine Entered");
3712 /* Don't ignore events as described below. */
3713 if (te->xunmap.event != te->xunmap.window &&
3714 (te->xunmap.event != Scr.Root || !te->xunmap.send_event))
3716 must_return = True;
3720 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3721 * to WithdrawnState should send a synthetic UnmapNotify with the
3722 * event field set to (pseudo-)root, in case the window is already
3723 * unmapped (which is the case for fvwm for IconicState).
3724 * Unfortunately, we looked for the FvwmContext using that field, so
3725 * try the window field also. */
3726 weMustUnmap = 0;
3727 if (!fw)
3729 weMustUnmap = 1;
3730 if (XFindContext(
3731 dpy, te->xunmap.window, FvwmContext,
3732 (caddr_t *)&fw) == XCNOENT)
3734 return;
3737 cw = FW_W(fw);
3738 pw = FW_W_PARENT(fw);
3739 if (te->xunmap.window == FW_W_FRAME(fw))
3741 SET_ICONIFY_PENDING(fw , 0);
3742 return;
3744 if (must_return)
3746 return;
3749 if (weMustUnmap)
3751 Bool is_map_request_pending;
3752 check_if_event_args args;
3754 args.w = te->xunmap.window;
3755 args.do_return_true = False;
3756 args.do_return_true_cr = False;
3757 /* Using FCheckTypedWindowEvent() does not work here. I don't
3758 * have the slightest idea why, but using FCheckIfEvent() with
3759 * the appropriate predicate procedure works fine. */
3760 FCheckIfEvent(dpy, &dummy, test_map_request, (XPointer)&args);
3761 /* Unfortunately, there is no procedure in X that simply tests
3762 * if an event of a certain type in on the queue without
3763 * waiting and without removing it from the queue.
3764 * XCheck...Event() does not wait but removes the event while
3765 * XPeek...() does not remove the event but waits. To solve
3766 * this, the predicate procedure sets a flag in the passed in
3767 * structure and returns False unconditionally. */
3768 is_map_request_pending = (args.ret_does_match == True);
3769 if (!is_map_request_pending)
3771 XUnmapWindow(dpy, te->xunmap.window);
3774 if (fw == Scr.Hilite)
3776 Scr.Hilite = NULL;
3778 focus_grabbed = focus_query_close_release_focus(fw);
3779 restore_focus_after_unmap(fw, False);
3780 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
3782 return;
3786 * The program may have unmapped the client window, from either
3787 * NormalState or IconicState. Handle the transition to WithdrawnState.
3789 * We need to reparent the window back to the root (so that fvwm exiting
3790 * won't cause it to get mapped) and then throw away all state (pretend
3791 * that we've received a DestroyNotify).
3793 if (!FCheckTypedWindowEvent(
3794 dpy, te->xunmap.window, DestroyNotify, &dummy) &&
3795 XTranslateCoordinates(
3796 dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
3797 &dumwin))
3799 MyXGrabServer(dpy);
3800 SetMapStateProp(fw, WithdrawnState);
3801 EWMH_RestoreInitialStates(fw, te->type);
3802 if (FCheckTypedWindowEvent(
3803 dpy, te->xunmap.window, ReparentNotify, &dummy))
3805 if (fw->attr_backup.border_width)
3807 XSetWindowBorderWidth(
3808 dpy, te->xunmap.window,
3809 fw->attr_backup.border_width);
3811 if ((!IS_ICON_SUPPRESSED(fw))&&
3812 (fw->wmhints &&
3813 (fw->wmhints->flags & IconWindowHint)))
3815 XUnmapWindow(dpy, fw->wmhints->icon_window);
3818 else
3820 RestoreWithdrawnLocation(fw, False, Scr.Root);
3822 if (!IS_TEAR_OFF_MENU(fw))
3824 XRemoveFromSaveSet(dpy, te->xunmap.window);
3825 XSelectInput(dpy, te->xunmap.window, NoEventMask);
3827 XSync(dpy, 0);
3828 MyXUngrabServer(dpy);
3829 if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
3831 /* the client tried to map the window again while it
3832 * was still inside the decoration windows */
3833 do_map = True;
3836 destroy_window(fw);
3837 if (focus_grabbed == True)
3839 CoerceEnterNotifyOnCurrentWindow();
3841 EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
3842 EWMH_WindowDestroyed();
3843 GNOME_SetClientList();
3844 if (do_map == True)
3846 map_event.xmaprequest.window = cw;
3847 map_event.xmaprequest.parent = Scr.Root;
3848 dispatch_event(&map_event);
3849 /* note: we really should handle all map and unmap notify
3850 * events for that window in a loop here */
3853 return;
3856 void HandleVisibilityNotify(const evh_args_t *ea)
3858 FvwmWindow * const fw = ea->exc->w.fw;
3860 DBUG("HandleVisibilityNotify", "Routine Entered");
3862 if (fw && ea->exc->x.etrigger->xvisibility.window == FW_W_FRAME(fw))
3864 switch (ea->exc->x.etrigger->xvisibility.state)
3866 case VisibilityUnobscured:
3867 SET_FULLY_VISIBLE(fw, 1);
3868 SET_PARTIALLY_VISIBLE(fw, 1);
3869 break;
3870 case VisibilityPartiallyObscured:
3871 SET_FULLY_VISIBLE(fw, 0);
3872 SET_PARTIALLY_VISIBLE(fw, 1);
3873 break;
3874 default:
3875 SET_FULLY_VISIBLE(fw, 0);
3876 SET_PARTIALLY_VISIBLE(fw, 0);
3877 break;
3879 /* Make sure the button grabs are up to date */
3880 focus_grab_buttons(fw);
3883 return;
3886 /* ---------------------------- interface functions ------------------------ */
3888 /* Inform a client window of its geometry.
3890 * The input (frame) geometry will be translated to client geometry
3891 * before sending. */
3892 void SendConfigureNotify(
3893 FvwmWindow *fw, int x, int y, int w, int h, int bw,
3894 Bool send_for_frame_too)
3896 XEvent client_event;
3897 size_borders b;
3899 if (!fw || IS_SHADED(fw))
3901 return;
3903 client_event.type = ConfigureNotify;
3904 client_event.xconfigure.display = dpy;
3905 client_event.xconfigure.event = FW_W(fw);
3906 client_event.xconfigure.window = FW_W(fw);
3907 get_window_borders(fw, &b);
3908 client_event.xconfigure.x = x + b.top_left.width;
3909 client_event.xconfigure.y = y + b.top_left.height;
3910 client_event.xconfigure.width = w - b.total_size.width;
3911 client_event.xconfigure.height = h - b.total_size.height;
3912 client_event.xconfigure.border_width = bw;
3913 client_event.xconfigure.above = FW_W_FRAME(fw);
3914 client_event.xconfigure.override_redirect = False;
3915 #if 0
3916 fprintf(stderr,
3917 "send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x '%s'\n",
3918 client_event.xconfigure.x, client_event.xconfigure.y,
3919 client_event.xconfigure.width, client_event.xconfigure.height,
3920 (int)FW_W_FRAME(fw), (int)FW_W(fw),
3921 (int)client_event.xconfigure.window,
3922 (fw->name.name) ? fw->name.name : "");
3923 #endif
3924 FSendEvent(
3925 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
3926 if (send_for_frame_too)
3928 /* This is for buggy tk, which waits for the real
3929 * ConfigureNotify on frame instead of the synthetic one on w.
3930 * The geometry data in the event will not be correct for the
3931 * frame, but tk doesn't look at that data anyway. */
3932 client_event.xconfigure.event = FW_W_FRAME(fw);
3933 client_event.xconfigure.window = FW_W_FRAME(fw);
3934 FSendEvent(
3935 dpy, FW_W_FRAME(fw), False, StructureNotifyMask,
3936 &client_event);
3939 return;
3942 /* Add an event group to the event handler */
3943 int register_event_group(int event_base, int event_count, PFEH *jump_table)
3945 /* insert into the list */
3946 event_group_t *group;
3947 event_group_t *position = base_event_group;
3948 event_group_t *prev_position = NULL;
3950 while (
3951 position != NULL &&
3952 position->base + position->count < event_base)
3954 prev_position = position;
3955 position = position->next;
3957 if ((position != NULL && position->base < event_base + event_count))
3959 /* there is already an event group registered at the specified
3960 * event range, or the base is before the base X events */
3962 return 1;
3964 /* create the group structure (these are not freed until fvwm exits) */
3965 group = (event_group_t*)safemalloc(sizeof(event_group_t));
3966 group->base = event_base;
3967 group->count = event_count;
3968 group->jump_table = jump_table;
3969 group->next = position;
3970 if (prev_position != NULL)
3972 prev_position->next = group;
3974 else
3976 base_event_group = group;
3979 return 0;
3983 ** Procedure:
3984 ** InitEventHandlerJumpTable
3986 void InitEventHandlerJumpTable(void)
3988 static PFEH EventHandlerJumpTable[LASTEvent];
3989 int i;
3991 for (i=0; i<LASTEvent; i++)
3993 EventHandlerJumpTable[i] = NULL;
3995 EventHandlerJumpTable[Expose] = HandleExpose;
3996 EventHandlerJumpTable[DestroyNotify] = HandleDestroyNotify;
3997 EventHandlerJumpTable[MapRequest] = HandleMapRequest;
3998 EventHandlerJumpTable[MapNotify] = HandleMapNotify;
3999 EventHandlerJumpTable[UnmapNotify] = HandleUnmapNotify;
4000 EventHandlerJumpTable[ButtonPress] = HandleButtonPress;
4001 EventHandlerJumpTable[EnterNotify] = HandleEnterNotify;
4002 EventHandlerJumpTable[LeaveNotify] = HandleLeaveNotify;
4003 EventHandlerJumpTable[FocusIn] = HandleFocusIn;
4004 EventHandlerJumpTable[FocusOut] = HandleFocusOut;
4005 EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
4006 EventHandlerJumpTable[ClientMessage] = HandleClientMessage;
4007 EventHandlerJumpTable[PropertyNotify] = HandlePropertyNotify;
4008 EventHandlerJumpTable[KeyPress] = HandleKeyPress;
4009 EventHandlerJumpTable[KeyRelease] = HandleKeyRelease;
4010 EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
4011 EventHandlerJumpTable[ColormapNotify] = HandleColormapNotify;
4012 EventHandlerJumpTable[SelectionClear] = HandleSelectionClear;
4013 EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
4014 EventHandlerJumpTable[ReparentNotify] = HandleReparentNotify;
4015 EventHandlerJumpTable[MappingNotify] = HandleMappingNotify;
4016 STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
4017 STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
4018 #ifdef MOUSE_DROPPINGS
4019 STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
4020 #else /* no MOUSE_DROPPINGS */
4021 STROKE_CODE(stroke_init());
4022 #endif /* MOUSE_DROPPINGS */
4023 if (register_event_group(0, LASTEvent, EventHandlerJumpTable))
4025 /* should never happen */
4026 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4027 "Faild to initialize event handlers");
4028 exit(1);
4030 if (FShapesSupported)
4032 static PFEH shape_jump_table[FShapeNumberEvents];
4034 for (i = 0; i < FShapeNumberEvents; i++)
4036 shape_jump_table[i] = NULL;
4038 shape_jump_table[FShapeNotify] = HandleShapeNotify;
4039 if (
4040 register_event_group(
4041 FShapeEventBase, FShapeNumberEvents,
4042 shape_jump_table))
4044 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4045 "Faild to init Shape event handler");
4050 return;
4053 /* handle a single X event */
4054 void dispatch_event(XEvent *e)
4056 Window w = e->xany.window;
4057 FvwmWindow *fw;
4058 event_group_t *event_group;
4060 DBUG("dispatch_event", "Routine Entered");
4062 XFlush(dpy);
4063 if (w == Scr.Root)
4065 switch (e->type)
4067 case ButtonPress:
4068 case ButtonRelease:
4069 if (e->xbutton.subwindow != None)
4071 w = e->xbutton.subwindow;
4073 case MapRequest:
4074 w = e->xmaprequest.window;
4075 break;
4076 default:
4077 break;
4080 if (w == Scr.Root ||
4081 XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4083 fw = NULL;
4085 last_event_type = e->type;
4086 event_group = base_event_group;
4087 while (
4088 event_group != NULL &&
4089 event_group->base + event_group->count < e->type)
4091 event_group = event_group->next;
4094 if (
4095 event_group != NULL &&
4096 e->type - event_group->base < event_group->count &&
4097 event_group->jump_table[e->type - event_group->base] != NULL)
4099 evh_args_t ea;
4100 exec_context_changes_t ecc;
4101 Window dummyw;
4103 ecc.type = EXCT_EVENT;
4104 ecc.x.etrigger = e;
4105 ecc.w.wcontext = GetContext(&fw, fw, e, &dummyw);
4106 ecc.w.w = w;
4107 ecc.w.fw = fw;
4108 ea.exc = exc_create_context(
4109 &ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W |
4110 ECC_WCONTEXT);
4111 (*event_group->jump_table[e->type - event_group->base])(&ea);
4112 exc_destroy_context(ea.exc);
4115 #ifdef C_ALLOCA
4116 /* If we're using the C version of alloca, see if anything needs to be
4117 * freed up.
4119 alloca(0);
4120 #endif
4121 DBUG("dispatch_event", "Leaving Routine");
4123 return;
4126 /* ewmh configure request */
4127 void events_handle_configure_request(
4128 XConfigureRequestEvent cre, FvwmWindow *fw, Bool force,
4129 int force_gravity)
4131 __handle_configure_request(cre, NULL, fw, force, force_gravity);
4133 return;
4136 void HandleEvents(void)
4138 XEvent ev;
4140 DBUG("HandleEvents", "Routine Entered");
4141 STROKE_CODE(send_motion = False);
4142 while (!isTerminated)
4144 last_event_type = 0;
4145 if (Scr.flags.is_window_scheduled_for_destroy)
4147 destroy_scheduled_windows();
4149 if (Scr.flags.do_need_window_update)
4151 flush_window_updates();
4153 if (My_XNextEvent(dpy, &ev))
4155 dispatch_event(&ev);
4157 if (Scr.flags.do_need_style_list_update)
4159 simplify_style_list();
4163 return;
4168 * Waits for next X or module event, fires off startup routines when startup
4169 * modules have finished or after a timeout if the user has specified a
4170 * command line module that doesn't quit or gets stuck.
4173 int My_XNextEvent(Display *dpy, XEvent *event)
4175 fd_set in_fdset, out_fdset;
4176 int num_fd;
4177 fmodule_list_itr moditr;
4178 fmodule *module;
4179 fmodule_input *input;
4180 static struct timeval timeout;
4181 static struct timeval *timeoutP = &timeout;
4183 DBUG("My_XNextEvent", "Routine Entered");
4185 /* check for any X events already queued up.
4186 * Side effect: this does an XFlush if no events are queued
4187 * Make sure nothing between here and the select causes further
4188 * X requests to be sent or the select may block even though
4189 * there are events in the queue */
4190 if (FPending(dpy))
4192 DBUG(
4193 "My_XNextEvent", "taking care of queued up events"
4194 " & returning (1)");
4195 FNextEvent(dpy, event);
4196 return 1;
4199 /* check for termination of all startup modules */
4200 if (fFvwmInStartup)
4202 module_list_itr_init(&moditr);
4203 module = module_list_itr_next(&moditr);
4204 for (; module != NULL; module = module_list_itr_next(&moditr))
4206 if (MOD_IS_CMDLINE(module) == 1)
4208 break;
4211 module_cleanup();
4212 if (module == NULL)
4214 /* last module */
4215 DBUG(
4216 "My_XNextEvent",
4217 "Starting up after command lines modules");
4218 /* set an infinite timeout to stop ticking */
4219 timeoutP = NULL;
4220 /* This may cause X requests to be sent */
4221 StartupStuff();
4223 return 0; /* so return without select()ing */
4227 /* Some signals can interrupt us while we wait for any action
4228 * on our descriptors. While some of these signals may be asking
4229 * fvwm to die, some might be harmless. Harmless interruptions
4230 * mean we have to start waiting all over again ... */
4233 int ms;
4234 Bool is_waiting_for_scheduled_command = False;
4235 static struct timeval *old_timeoutP = NULL;
4237 /* The timeouts become undefined whenever the select returns,
4238 * and so we have to reinitialise them */
4239 ms = squeue_get_next_ms();
4240 if (ms == 0)
4242 /* run scheduled commands */
4243 squeue_execute();
4244 ms = squeue_get_next_ms();
4245 /* should not happen anyway.
4246 * get_next_schedule_queue_ms() can't return 0 after a
4247 * call to execute_schedule_queue(). */
4248 if (ms == 0)
4250 ms = 1;
4253 if (ms < 0)
4255 timeout.tv_sec = 42;
4256 timeout.tv_usec = 0;
4258 else
4260 /* scheduled commands are pending - don't wait too
4261 * long */
4262 timeout.tv_sec = ms / 1000;
4263 timeout.tv_usec = 1000 * (ms % 1000);
4264 old_timeoutP = timeoutP;
4265 timeoutP = &timeout;
4266 is_waiting_for_scheduled_command = True;
4269 FD_ZERO(&in_fdset);
4270 FD_ZERO(&out_fdset);
4271 FD_SET(x_fd, &in_fdset);
4273 /* nothing is done here if fvwm was compiled without session
4274 * support */
4275 if (sm_fd >= 0)
4277 FD_SET(sm_fd, &in_fdset);
4280 module_list_itr_init(&moditr);
4281 while ( (module = module_list_itr_next(&moditr)) != NULL)
4283 FD_SET(MOD_READFD(module), &in_fdset);
4285 if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
4287 FD_SET(MOD_WRITEFD(module), &out_fdset);
4291 DBUG("My_XNextEvent", "waiting for module input/output");
4292 num_fd = fvwmSelect(
4293 fvwmlib_max_fd, &in_fdset, &out_fdset, 0, timeoutP);
4294 if (is_waiting_for_scheduled_command)
4296 timeoutP = old_timeoutP;
4299 /* Express route out of fvwm ... */
4300 if (isTerminated)
4302 return 0;
4304 } while (num_fd < 0);
4306 if (num_fd > 0)
4308 /* Check for module input. */
4309 module_list_itr_init(&moditr);
4310 while ( (module = module_list_itr_next(&moditr)) != NULL)
4312 if (FD_ISSET(MOD_READFD(module), &in_fdset))
4314 input = module_receive(module);
4315 /* enqueue the received command */
4316 module_input_enqueue(input);
4318 if (
4319 MOD_WRITEFD(module) >= 0 &&
4320 FD_ISSET(MOD_WRITEFD(module), &out_fdset))
4322 DBUG("My_XNextEvent",
4323 "calling FlushMessageQueue");
4324 FlushMessageQueue(module);
4328 /* execute any commands queued up */
4329 DBUG("My_XNextEvent", "executing module comand queue");
4330 ExecuteCommandQueue();
4332 /* cleanup dead modules */
4333 module_cleanup();
4335 /* nothing is done here if fvwm was compiled without session
4336 * support */
4337 if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
4339 ProcessICEMsgs();
4343 else
4345 /* select has timed out, things must have calmed down so let's
4346 * decorate */
4347 if (fFvwmInStartup)
4349 fvwm_msg(ERR, "My_XNextEvent",
4350 "Some command line modules have not quit, "
4351 "Starting up after timeout.\n");
4352 StartupStuff();
4353 timeoutP = NULL; /* set an infinite timeout to stop
4354 * ticking */
4355 reset_style_changes();
4356 Scr.flags.do_need_window_update = 0;
4358 /* run scheduled commands if necessary */
4359 squeue_execute();
4362 /* check for X events again, rather than return 0 and get called again
4364 if (FPending(dpy))
4366 DBUG("My_XNextEvent",
4367 "taking care of queued up events & returning (2)");
4368 FNextEvent(dpy,event);
4369 return 1;
4372 DBUG("My_XNextEvent", "leaving My_XNextEvent");
4373 return 0;
4378 * Procedure:
4379 * Find the Fvwm context for the event.
4382 int GetContext(FvwmWindow **ret_fw, FvwmWindow *t, const XEvent *e, Window *w)
4384 int context;
4385 Window win;
4386 Window subw = None;
4387 int x = 0;
4388 int y = 0;
4389 Bool is_key_event = False;
4391 win = e->xany.window;
4392 context = C_NO_CONTEXT;
4393 switch (e->type)
4395 case KeyPress:
4396 case KeyRelease:
4397 x = e->xkey.x;
4398 y = e->xkey.y;
4399 subw = e->xkey.subwindow;
4400 if (win == Scr.Root && subw != None)
4402 /* Translate root coordinates into subwindow
4403 * coordinates. Necessary for key bindings that work
4404 * over unfocused windows. */
4405 win = subw;
4406 XTranslateCoordinates(
4407 dpy, Scr.Root, subw, x, y, &x, &y, &subw);
4408 XFindContext(dpy, win, FvwmContext, (caddr_t *) &t);
4410 is_key_event = True;
4411 /* fall through */
4412 case ButtonPress:
4413 case ButtonRelease:
4414 if (!is_key_event)
4416 x = e->xbutton.x;
4417 y = e->xbutton.y;
4418 subw = e->xbutton.subwindow;
4420 if (t && win == FW_W_FRAME(t) && subw != None)
4422 /* Translate frame coordinates into subwindow
4423 * coordinates. */
4424 win = subw;
4425 XTranslateCoordinates(
4426 dpy, FW_W_FRAME(t), subw, x, y, &x, &y, &subw);
4427 if (win == FW_W_PARENT(t))
4429 win = subw;
4430 XTranslateCoordinates(
4431 dpy, FW_W_PARENT(t), subw, x, y, &x,
4432 &y, &subw);
4435 break;
4436 default:
4437 XFindContext(dpy, win, FvwmContext, (caddr_t *)&t);
4438 break;
4440 if (ret_fw != NULL)
4442 *ret_fw = t;
4444 if (!t)
4446 return C_ROOT;
4448 *w = win;
4449 if (*w == Scr.NoFocusWin)
4451 return C_ROOT;
4453 if (subw != None)
4455 if (win == FW_W_PARENT(t))
4457 *w = subw;
4460 if (*w == Scr.Root)
4462 return C_ROOT;
4464 context = frame_window_id_to_context(t, *w, &Button);
4466 return context;
4471 * Removes expose events for a specific window from the queue
4474 int flush_expose(Window w)
4476 XEvent dummy;
4477 int i=0;
4479 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4481 i++;
4484 return i;
4487 /* same as above, but merges the expose rectangles into a single big one */
4488 int flush_accumulate_expose(Window w, XEvent *e)
4490 XEvent dummy;
4491 int i = 0;
4492 int x1 = e->xexpose.x;
4493 int y1 = e->xexpose.y;
4494 int x2 = x1 + e->xexpose.width;
4495 int y2 = y1 + e->xexpose.height;
4497 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4499 x1 = min(x1, dummy.xexpose.x);
4500 y1 = min(y1, dummy.xexpose.y);
4501 x2 = max(x2, dummy.xexpose.x + dummy.xexpose.width);
4502 y2 = max(y2, dummy.xexpose.y + dummy.xexpose.height);
4503 i++;
4505 e->xexpose.x = x1;
4506 e->xexpose.y = y1;
4507 e->xexpose.width = x2 - x1;
4508 e->xexpose.height = y2 - y1;
4510 return i;
4515 * Removes all expose events from the queue and does the necessary redraws
4518 void handle_all_expose(void)
4520 void *saved_event;
4521 XEvent evdummy;
4523 saved_event = fev_save_event();
4524 FPending(dpy);
4525 while (FCheckMaskEvent(dpy, ExposureMask, &evdummy))
4527 dispatch_event(&evdummy);
4529 fev_restore_event(saved_event);
4531 return;
4534 /* CoerceEnterNotifyOnCurrentWindow()
4535 * Pretends to get a HandleEnterNotify on the window that the pointer
4536 * currently is in so that the focus gets set correctly from the beginning.
4537 * Note that this presently only works if the current window is not
4538 * click_to_focus; I think that that behaviour is correct and desirable.
4539 * --11/08/97 gjb */
4540 void CoerceEnterNotifyOnCurrentWindow(void)
4542 Window child;
4543 Window root;
4544 Bool f;
4545 evh_args_t ea;
4546 exec_context_changes_t ecc;
4547 XEvent e;
4548 FvwmWindow *fw;
4550 f = FQueryPointer(
4551 dpy, Scr.Root, &root, &child, &e.xcrossing.x_root,
4552 &e.xcrossing.y_root, &e.xcrossing.x, &e.xcrossing.y,
4553 &JunkMask);
4554 if (f == False || child == None)
4556 return;
4558 e.xcrossing.type = EnterNotify;
4559 e.xcrossing.window = child;
4560 e.xcrossing.subwindow = None;
4561 e.xcrossing.mode = NotifyNormal;
4562 e.xcrossing.detail = NotifyAncestor;
4563 e.xcrossing.same_screen = True;
4564 if (XFindContext(dpy, child, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4566 fw = NULL;
4568 else
4570 XTranslateCoordinates(
4571 dpy, Scr.Root, child, e.xcrossing.x_root,
4572 e.xcrossing.y_root, &JunkX, &JunkY, &child);
4573 if (child == FW_W_PARENT(fw))
4575 child = FW_W(fw);
4577 if (child != None)
4579 e.xany.window = child;
4582 e.xcrossing.focus = (fw == get_focus_window()) ? True : False;
4583 ecc.type = EXCT_NULL;
4584 ecc.x.etrigger = &e;
4585 ea.exc = exc_create_context(&ecc, ECC_TYPE | ECC_ETRIGGER);
4586 HandleEnterNotify(&ea);
4587 exc_destroy_context(ea.exc);
4589 return;
4592 /* This function discards all queued up ButtonPress, ButtonRelease and
4593 * ButtonMotion events. */
4594 int discard_events(long event_mask)
4596 XEvent e;
4597 int count;
4599 XSync(dpy, 0);
4600 for (count = 0; FCheckMaskEvent(dpy, event_mask, &e); count++)
4602 /* nothing */
4605 return count;
4608 /* This function discards all queued up ButtonPress, ButtonRelease and
4609 * ButtonMotion events. */
4610 int discard_window_events(Window w, long event_mask)
4612 XEvent e;
4613 int count;
4615 XSync(dpy, 0);
4616 for (count = 0; FCheckWindowEvent(dpy, w, event_mask, &e); count++)
4618 /* nothing */
4621 return count;
4624 /* Similar function for certain types of PropertyNotify. */
4625 int flush_property_notify(Atom atom, Window w)
4627 XEvent e;
4628 int count;
4629 test_typed_window_event_args args;
4631 count = 0;
4632 XSync(dpy, 0);
4633 args.w = w;
4634 args.atom = atom;
4635 args.event_type = PropertyNotify;
4637 /* Get rid of the events. */
4638 while (FCheckIfEvent(dpy, &e, test_typed_window_event, (XPointer)&args))
4639 count++;
4641 return count;
4644 /* Wait for all mouse buttons to be released
4645 * This can ease some confusion on the part of the user sometimes
4647 * Discard superflous button events during this wait period. */
4648 void WaitForButtonsUp(Bool do_handle_expose)
4650 unsigned int mask;
4651 unsigned int bmask;
4652 long evmask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
4653 KeyPressMask|KeyReleaseMask;
4654 int count;
4655 int use_wait_cursor;
4656 XEvent e;
4658 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
4659 &JunkX, &JunkY, &mask) == False)
4661 /* pointer is on a different screen - that's okay here */
4663 mask &= DEFAULT_ALL_BUTTONS_MASK;
4664 if (mask == 0)
4666 return;
4668 if (do_handle_expose)
4670 evmask |= ExposureMask;
4672 GrabEm(None, GRAB_NORMAL);
4673 for (count = 0, use_wait_cursor = 0; mask != 0; count++)
4675 /* handle expose events */
4676 XAllowEvents(dpy, SyncPointer, CurrentTime);
4677 if (FCheckMaskEvent(dpy, evmask, &e))
4679 switch (e.type)
4681 case ButtonRelease:
4682 if (e.xbutton.button <=
4683 NUMBER_OF_MOUSE_BUTTONS)
4685 bmask = (Button1Mask <<
4686 (e.xbutton.button - 1));
4687 mask = e.xbutton.state & ~bmask;
4689 break;
4690 case Expose:
4691 dispatch_event(&e);
4692 break;
4693 default:
4694 break;
4697 else
4699 if (FQueryPointer(
4700 dpy, Scr.Root, &JunkRoot, &JunkChild,
4701 &JunkX, &JunkY, &JunkX, &JunkY, &mask) ==
4702 False)
4704 /* pointer is on a different screen - that's
4705 * okay here */
4707 mask &= DEFAULT_ALL_BUTTONS_MASK;
4708 usleep(1);
4710 if (use_wait_cursor == 0 && count == 20)
4712 GrabEm(CRS_WAIT, GRAB_NORMAL);
4713 use_wait_cursor = 1;
4716 UngrabEm(GRAB_NORMAL);
4717 if (use_wait_cursor)
4719 UngrabEm(GRAB_NORMAL);
4720 XFlush(dpy);
4723 return;
4726 void sync_server(int toggle)
4728 static Bool synced = False;
4730 if (toggle == -1)
4732 toggle = (synced == False);
4734 if (toggle == 1)
4736 synced = True;
4738 else
4740 synced = False;
4742 XSynchronize(dpy, synced);
4743 XFlush(dpy);
4745 return;
4748 Bool is_resizing_event_pending(
4749 FvwmWindow *fw)
4751 XEvent e;
4752 check_if_event_args args;
4754 args.w = FW_W(fw);
4755 args.do_return_true = False;
4756 args.do_return_true_cr = False;
4757 args.cr_value_mask = 0;
4758 args.ret_does_match = False;
4759 args.ret_type = 0;
4760 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
4762 return args.ret_does_match;
4765 /* ---------------------------- builtin commands --------------------------- */
4767 void CMD_XSynchronize(F_CMD_ARGS)
4769 int toggle;
4771 toggle = ParseToggleArgument(action, NULL, -1, 0);
4772 sync_server(toggle);
4774 return;
4777 void CMD_XSync(F_CMD_ARGS)
4779 XSync(dpy, 0);
4781 return;