cvsimport
[fvwm.git] / fvwm / events.c
blobc1218ad50c34f16652b54603f124a924e67b76cb
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 current_g;
930 rectangle new_g;
931 rectangle d_g;
932 size_rect constr_dim;
933 size_rect oldnew_dim;
934 size_borders b;
935 int cn_count = 0;
936 int gravity;
938 if (ea)
940 cre = ea->exc->x.etrigger->xconfigurerequest;
942 if ((cre.value_mask & (CWWidth | CWHeight | CWX | CWY)) == 0)
944 return 0;
947 get_window_borders(fw, &b);
948 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
949 /* Merge all pending ConfigureRequests for the window into a single
950 * event. However, we can not do this if the window uses the motion
951 * method autodetection because the merged event might confuse the
952 * detection code. */
953 if (ea && CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
955 cn_count = __merge_cr_moveresize(ea, &cre, fw, &b);
957 #endif
958 #if 0
959 fprintf(stderr,
960 "cre: %d(%d) %d(%d) %d(%d)x%d(%d) fw 0x%08x w 0x%08x "
961 "ew 0x%08x '%s'\n",
962 cre.x, (int)(cre.value_mask & CWX),
963 cre.y, (int)(cre.value_mask & CWY),
964 cre.width, (int)(cre.value_mask & CWWidth),
965 cre.height, (int)(cre.value_mask & CWHeight),
966 (int)FW_W_FRAME(fw), (int)FW_W(fw), (int)cre.window,
967 (fw->name.name) ? fw->name.name : "");
968 #endif
969 /* Don't modify frame_g fields before calling SetupWindow! */
970 memset(&d_g, 0, sizeof(d_g));
972 if (HAS_NEW_WM_NORMAL_HINTS(fw))
974 /* get the latest size hints */
975 XSync(dpy, 0);
976 GetWindowSizeHints(fw);
977 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
979 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMaxSize))
981 /* Java workaround */
982 if (cre.height > fw->hints.max_height &&
983 fw->hints.max_height <= BROKEN_MAXSIZE_LIMIT)
985 fw->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
986 cre.value_mask |= CWHeight;
988 if (cre.width > fw->hints.max_width &&
989 fw->hints.max_width <= BROKEN_MAXSIZE_LIMIT)
991 fw->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
992 cre.value_mask |= CWWidth;
995 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMinSize))
997 if (cre.width < fw->hints.min_width &&
998 fw->hints.min_width >= BROKEN_MINSIZE_LIMIT)
1000 fw->hints.min_width = 1;
1001 cre.value_mask |= CWWidth;
1003 if (cre.height < fw->hints.min_height &&
1004 fw->hints.min_height >= BROKEN_MINSIZE_LIMIT)
1006 fw->hints.min_height = 1;
1007 cre.value_mask |= CWHeight;
1010 if (IS_SHADED(fw) ||
1011 !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM, False))
1013 /* forbid shaded applications to move their windows */
1014 cre.value_mask &= ~(CWX | CWY);
1015 /* resend the old geometry */
1016 *ret_do_send_event = 1;
1018 if (IS_MAXIMIZED(fw))
1020 /* dont allow clients to resize maximized windows */
1021 cre.value_mask &= ~(CWWidth | CWHeight);
1022 /* resend the old geometry */
1023 *ret_do_send_event = 1;
1024 d_g.width = 0;
1025 d_g.height = 0;
1027 else if (
1028 !is_function_allowed(
1029 F_RESIZE, NULL, fw, RQORIG_PROGRAM, False))
1031 cre.value_mask &= ~(CWWidth | CWHeight);
1032 *ret_do_send_event = 1;
1035 if (cre.value_mask & CWBorderWidth)
1037 /* for restoring */
1038 fw->attr_backup.border_width = cre.border_width;
1040 if (!force && CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO)
1042 __cr_detect_icccm_move(fw, &cre, &b);
1044 if (force_gravity > ForgetGravity && force_gravity <= StaticGravity)
1046 gravity = force_gravity;
1048 else
1050 gravity = fw->hints.win_gravity;
1052 if (IS_SHADED(fw))
1054 direction_t gravity_dir;
1056 get_unshaded_geometry(fw, &current_g);
1057 /* the shade direction overrides the window's gravity */
1058 gravity_dir = gravity_grav_to_dir(gravity);
1059 gravity_dir = gravity_override_dir(
1060 gravity_dir, SHADED_DIR(fw));
1061 gravity = gravity_dir_to_grav(gravity_dir);
1063 else
1065 current_g = fw->g.frame;
1067 if (!(cre.value_mask & (CWX | CWY)))
1069 /* nothing */
1071 else if ((force ||
1072 CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_USE_GRAV) &&
1073 gravity != StaticGravity)
1075 int ref_x;
1076 int ref_y;
1077 int grav_x;
1078 int grav_y;
1080 gravity_get_offsets(gravity, &grav_x, &grav_y);
1081 if (cre.value_mask & CWX)
1083 ref_x = cre.x -
1084 ((grav_x + 1) * b.total_size.width) / 2;
1085 d_g.x = ref_x - current_g.x;
1087 if (cre.value_mask & CWY)
1089 ref_y = cre.y -
1090 ((grav_y + 1) * b.total_size.height) / 2;
1091 d_g.y = ref_y - current_g.y;
1094 else /* ..._USE_GRAV or ..._AUTO */
1096 /* default: traditional cr handling */
1097 if (cre.value_mask & CWX)
1099 d_g.x = cre.x - current_g.x - b.top_left.width;
1101 if (cre.value_mask & CWY)
1103 d_g.y = cre.y - current_g.y - b.top_left.height;
1107 if (cre.value_mask & CWHeight)
1109 if (cre.height <
1110 (WINDOW_FREAKED_OUT_SIZE - b.total_size.height))
1112 d_g.height = cre.height -
1113 (current_g.height - b.total_size.height);
1115 else
1117 /* Ignore height changes to astronomically large
1118 * windows (needed for XEmacs 20.4); don't care if the
1119 * window is shaded here - we won't use 'height' in
1120 * this case anyway.
1121 * Inform the buggy app about the size that *we* want
1123 d_g.height = 0;
1124 *ret_do_send_event = 1;
1127 if (cre.value_mask & CWWidth)
1129 if (cre.width < (WINDOW_FREAKED_OUT_SIZE - b.total_size.width))
1131 d_g.width = cre.width -
1132 (current_g.width - b.total_size.width);
1134 else
1136 d_g.width = 0;
1137 *ret_do_send_event = 1;
1141 /* SetupWindow (x,y) are the location of the upper-left outer corner
1142 * and are passed directly to XMoveResizeWindow (frame). The
1143 * (width,height) are the inner size of the frame. The inner width is
1144 * the same as the requested client window width; the inner height is
1145 * the same as the requested client window height plus any title bar
1146 * slop. */
1147 new_g = current_g;
1148 oldnew_dim.width = new_g.width + d_g.width;
1149 oldnew_dim.height = new_g.height + d_g.height;
1150 constr_dim.width = oldnew_dim.width;
1151 constr_dim.height = oldnew_dim.height;
1152 constrain_size(
1153 fw, NULL, &constr_dim.width, &constr_dim.height, 0, 0,
1154 CS_UPDATE_MAX_DEFECT);
1155 d_g.width += (constr_dim.width - oldnew_dim.width);
1156 d_g.height += (constr_dim.height - oldnew_dim.height);
1157 if ((cre.value_mask & CWX) && d_g.width)
1159 new_g.x = current_g.x + d_g.x;
1160 new_g.width = current_g.width + d_g.width;
1162 else if ((cre.value_mask & CWX) && !d_g.width)
1164 new_g.x = current_g.x + d_g.x;
1166 else if (!(cre.value_mask & CWX) && d_g.width)
1168 gravity_resize(gravity, &new_g, d_g.width, 0);
1170 if ((cre.value_mask & CWY) && d_g.height)
1172 new_g.y = current_g.y + d_g.y;
1173 new_g.height = current_g.height + d_g.height;
1175 else if ((cre.value_mask & CWY) && !d_g.height)
1177 new_g.y = current_g.y + d_g.y;
1179 else if (!(cre.value_mask & CWY) && d_g.height)
1181 gravity_resize(gravity, &new_g, 0, d_g.height);
1184 if (new_g.x == current_g.x && new_g.y == current_g.y &&
1185 new_g.width == current_g.width &&
1186 new_g.height == current_g.height)
1188 /* Window will not be moved or resized; send a synthetic
1189 * ConfigureNotify. */
1190 *ret_do_send_event = 1;
1192 else if ((cre.value_mask & CWX) || (cre.value_mask & CWY) ||
1193 d_g.width || d_g.height)
1195 if (IS_SHADED(fw))
1197 fw->g.normal = new_g;
1198 get_shaded_geometry(fw, &new_g, &new_g);
1200 frame_setup_window_app_request(
1201 fw, new_g.x, new_g.y, new_g.width, new_g.height,
1202 False);
1203 /* make sure the window structure has the new position */
1204 update_absolute_geometry(fw);
1205 maximize_adjust_offset(fw);
1206 GNOME_SetWinArea(fw);
1208 else if (DO_FORCE_NEXT_CR(fw))
1210 *ret_do_send_event = 1;
1212 SET_FORCE_NEXT_CR(fw, 0);
1213 SET_FORCE_NEXT_PN(fw, 0);
1215 return cn_count;
1218 void __handle_configure_request(
1219 XConfigureRequestEvent cre, const evh_args_t *ea, FvwmWindow *fw,
1220 Bool force, int force_gravity)
1222 int do_send_event = 0;
1223 int cn_count = 0;
1225 /* According to the July 27, 1988 ICCCM draft, we should ignore size
1226 * and position fields in the WM_NORMAL_HINTS property when we map a
1227 * window. Instead, we'll read the current geometry. Therefore, we
1228 * should respond to configuration requests for windows which have
1229 * never been mapped. */
1230 if (fw == NULL)
1232 __handle_cr_on_unmanaged(&cre);
1233 return;
1235 if (cre.window == FW_W_ICON_TITLE(fw) ||
1236 cre.window == FW_W_ICON_PIXMAP(fw))
1238 __handle_cr_on_icon(&cre, fw);
1240 if (FShapesSupported)
1242 __handle_cr_on_shaped(fw);
1244 if (fw != NULL && cre.window == FW_W(fw))
1246 cn_count = __handle_cr_on_client(
1247 &do_send_event, cre, ea, fw, force, force_gravity);
1249 /* Stacking order change requested. Handle this *after* geometry
1250 * changes, since we need the new geometry in occlusion calculations */
1251 if ((cre.value_mask & CWStackMode) &&
1252 (!DO_IGNORE_RESTACK(fw) || force))
1254 __handle_cr_restack(&do_send_event, &cre, fw);
1256 #if 1
1257 /* This causes some ddd windows not to be drawn properly. Reverted back
1258 * to the old method in frame_setup_window. */
1259 /* domivogt (15-Oct-1999): enabled this to work around buggy apps that
1260 * ask for a nonsense height and expect that they really get it. */
1261 if (cn_count == 0 && do_send_event)
1263 cn_count = 1;
1265 else if (cn_count > 0)
1267 do_send_event = 1;
1269 for ( ; cn_count > 0; cn_count--)
1271 SendConfigureNotify(
1272 fw, fw->g.frame.x, fw->g.frame.y, fw->g.frame.width,
1273 fw->g.frame.height, 0, True);
1275 if (do_send_event)
1277 XFlush(dpy);
1279 #endif
1281 return;
1284 static Bool __predicate_button_click(
1285 Display *display, XEvent *event, XPointer arg)
1287 if (event->type == ButtonPress || event->type == ButtonRelease)
1289 return True;
1292 return False;
1295 /* Helper function for __handle_focus_raise_click(). */
1296 static Bool __test_for_motion(int x0, int y0)
1298 int x;
1299 int y;
1300 unsigned int mask;
1301 XEvent e;
1303 /* Query the pointer to do this. We can't check for events here since
1304 * the events are still needed if the pointer moves. */
1306 /* However, some special mouse (e.g., a touchpad with the
1307 * synaptic driver) may handle a double click in a special way
1308 * (for dragging through short touching and holding down the
1309 * finger on the touchpad). Bascially, when you execute a
1310 * double click the first button release is queued after the
1311 * second _physical_ mouse release happen. It seems that
1312 * FQueryPointer may not work as expected: it does not see
1313 * that the button is released on a double click. So, we need
1314 * to check for a button press in the future to avoid a fvwm
1315 * lockup! (olicha 2004-01-31) */
1317 for (x = x0, y = y0; FQueryPointer(
1318 dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
1319 &x, &y, &mask) == True; usleep(20000))
1321 if ((mask & DEFAULT_ALL_BUTTONS_MASK) == 0)
1323 /* all buttons are released */
1324 return False;
1326 else if (abs(x - x0) >= Scr.MoveThreshold ||
1327 abs(y - y0) >= Scr.MoveThreshold)
1329 /* the pointer has moved */
1330 return True;
1332 if (FCheckPeekIfEvent(dpy, &e, __predicate_button_click, NULL))
1334 /* click in the future */
1335 return False;
1337 else
1339 /* The predicate procedure finds no match, no event
1340 * has been removed from the queue and XFlush was
1341 * called. Nothing to do */
1345 /* pointer has moved off screen */
1346 return True;
1349 /* Helper function for __handle_focus_raise_click(). */
1350 static void __check_click_to_focus_or_raise(
1351 hfrc_ret_t *ret_args, const exec_context_t *exc)
1353 FvwmWindow * const fw = exc->w.fw;
1354 const XEvent *te = exc->x.etrigger;
1355 struct
1357 unsigned is_client_click : 1;
1358 unsigned is_focused : 1;
1359 } f;
1361 f.is_focused = !!focus_is_focused(fw);
1362 f.is_client_click = (exc->w.wcontext == C_WINDOW ||
1363 exc->w.wcontext == C_EWMH_DESKTOP);
1364 /* check if we need to raise and/or focus the window */
1365 ret_args->do_focus = focus_query_click_to_focus(fw, exc->w.wcontext);
1366 if (f.is_client_click && !ret_args->do_focus &&
1367 !f.is_focused && FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
1368 !fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
1370 /* Give the window a chance to to take focus itself */
1371 ret_args->do_focus = 1;
1373 if (ret_args->do_focus && focus_is_focused(fw))
1375 ret_args->do_focus = 0;
1377 ret_args->do_raise =
1378 focus_query_click_to_raise(fw, f.is_focused, exc->w.wcontext);
1379 #define EXPERIMENTAL_ROU_HANDLING_V2
1380 #ifdef EXPERIMENTAL_ROU_HANDLING_V2
1381 /* RBW -- Dang! This works without the one in HandleEnterNotify! */
1382 if (ret_args->do_raise && is_on_top_of_layer_and_above_unmanaged(fw))
1383 #else
1384 if (ret_args->do_raise && is_on_top_of_layer(fw))
1385 #endif
1387 ret_args->do_raise = 0;
1389 if ((ret_args->do_focus &&
1390 FP_DO_IGNORE_FOCUS_CLICK_MOTION(FW_FOCUS_POLICY(fw))) ||
1391 (ret_args->do_raise &&
1392 FP_DO_IGNORE_RAISE_CLICK_MOTION(FW_FOCUS_POLICY(fw))))
1394 /* Pass further events to the application and check if a button
1395 * release or motion event occurs next. If we don't do this
1396 * here, the pointer will seem to be frozen in
1397 * __test_for_motion(). */
1398 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1399 if (__test_for_motion(te->xbutton.x_root, te->xbutton.y_root))
1401 /* the pointer was moved, process event normally */
1402 ret_args->do_focus = 0;
1403 ret_args->do_raise = 0;
1406 if (ret_args->do_focus || ret_args->do_raise)
1408 if (!((ret_args->do_focus &&
1409 FP_DO_ALLOW_FUNC_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1410 (ret_args->do_raise &&
1411 FP_DO_ALLOW_FUNC_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1413 ret_args->do_forbid_function = 1;
1415 if (!((ret_args->do_focus &&
1416 FP_DO_PASS_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1417 (ret_args->do_raise &&
1418 FP_DO_PASS_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1420 ret_args->do_swallow_click = 1;
1424 return;
1427 /* Finds out if the click on a window must be used to focus or raise it. */
1428 static void __handle_focus_raise_click(
1429 hfrc_ret_t *ret_args, const exec_context_t *exc)
1431 memset(ret_args, 0, sizeof(*ret_args));
1432 if (exc->w.fw == NULL)
1434 return;
1436 /* check for proper click button and modifiers*/
1437 if (FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) != 0 &&
1438 !(FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) &
1439 (1 << (exc->x.etrigger->xbutton.button - 1))))
1441 /* wrong button, handle click normally */
1442 return;
1444 else if (FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw)) !=
1445 FPOL_ANY_MODIFIER &&
1446 MaskUsedModifiers(
1447 FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw))) !=
1448 MaskUsedModifiers(exc->x.etrigger->xbutton.state))
1450 /* right button but wrong modifiers, handle click normally */
1451 return;
1453 else
1455 __check_click_to_focus_or_raise(ret_args, exc);
1458 return;
1461 /* Helper function for HandleButtonPress */
1462 static Bool __is_bpress_window_handled(const exec_context_t *exc)
1464 Window eventw;
1465 const XEvent *te = exc->x.etrigger;
1467 if (exc->w.fw == NULL)
1469 if ((te->xbutton.window != Scr.Root ||
1470 te->xbutton.subwindow != None) &&
1471 !is_pan_frame(te->xbutton.window))
1473 /* Ignore events in unmanaged windows or subwindows of
1474 * a client */
1475 return False;
1477 else
1479 return True;
1482 eventw = (te->xbutton.subwindow != None &&
1483 te->xany.window != FW_W(exc->w.fw)) ?
1484 te->xbutton.subwindow : te->xany.window;
1485 if (is_frame_hide_window(eventw) || eventw == FW_W_FRAME(exc->w.fw))
1487 return False;
1489 if (!XGetGeometry(
1490 dpy, eventw, &JunkRoot, &JunkX, &JunkY,
1491 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
1492 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1494 /* The window has already died. */
1495 return False;
1498 return True;
1501 /* Helper function for __handle_bpress_on_managed */
1502 static Bool __handle_click_to_focus(const exec_context_t *exc)
1504 fpol_set_focus_by_t set_by;
1506 switch (exc->w.wcontext)
1508 case C_WINDOW:
1509 case C_EWMH_DESKTOP:
1510 set_by = FOCUS_SET_BY_CLICK_CLIENT;
1511 break;
1512 case C_ICON:
1513 set_by = FOCUS_SET_BY_CLICK_ICON;
1514 break;
1515 default:
1516 set_by = FOCUS_SET_BY_CLICK_DECOR;
1517 break;
1519 SetFocusWindow(exc->w.fw, True, set_by);
1520 focus_grab_buttons(exc->w.fw);
1521 if (focus_is_focused(exc->w.fw) && !IS_ICONIFIED(exc->w.fw))
1523 border_draw_decorations(
1524 exc->w.fw, PART_ALL, True, True, CLEAR_ALL, NULL,
1525 NULL);
1528 return focus_is_focused(exc->w.fw);
1531 /* Helper function for __handle_bpress_on_managed */
1532 static Bool __handle_click_to_raise(const exec_context_t *exc)
1534 Bool rc = False;
1535 int is_focused;
1537 is_focused = focus_is_focused(exc->w.fw);
1538 if (focus_query_click_to_raise(exc->w.fw, is_focused, True))
1540 rc = True;
1543 return rc;
1546 /* Helper function for HandleButtonPress */
1547 static void __handle_bpress_stroke(void)
1549 STROKE_CODE(stroke_init());
1550 STROKE_CODE(send_motion = True);
1552 return;
1555 /* Helper function for __handle_bpress_on_managed */
1556 static Bool __handle_bpress_action(
1557 const exec_context_t *exc, char *action)
1559 window_parts part;
1560 Bool do_force;
1561 Bool rc = False;
1563 if (!action || *action == 0)
1565 PressedW = None;
1566 return False;
1568 /* draw pressed in decorations */
1569 part = border_context_to_parts(exc->w.wcontext);
1570 do_force = (part & PART_TITLEBAR) ? True : False;
1571 border_draw_decorations(
1572 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1573 CLEAR_ALL, NULL, NULL);
1574 /* execute the action */
1575 if (IS_ICONIFIED(exc->w.fw))
1577 /* release the pointer since it can't do harm over an icon */
1578 XAllowEvents(dpy, AsyncPointer, CurrentTime);
1580 execute_function(NULL, exc, action, 0);
1581 if (exc->w.wcontext != C_WINDOW && exc->w.wcontext != C_NO_CONTEXT)
1583 WaitForButtonsUp(True);
1584 rc = True;
1586 /* redraw decorations */
1587 PressedW = None;
1588 if (check_if_fvwm_window_exists(exc->w.fw))
1590 part = border_context_to_parts(exc->w.wcontext);
1591 do_force = (part & PART_TITLEBAR) ? True : False;
1592 border_draw_decorations(
1593 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1594 CLEAR_ALL, NULL, NULL);
1597 return rc;
1600 /* Handles button presses on the root window. */
1601 static void __handle_bpress_on_root(const exec_context_t *exc)
1603 char *action;
1605 PressedW = None;
1606 __handle_bpress_stroke();
1607 /* search for an appropriate mouse binding */
1608 action = CheckBinding(
1609 Scr.AllBindings, STROKE_ARG(0) exc->x.etrigger->xbutton.button,
1610 exc->x.etrigger->xbutton.state, GetUnusedModifiers(), C_ROOT,
1611 BIND_BUTTONPRESS, NULL, NULL);
1612 if (action && *action)
1614 const exec_context_t *exc2;
1615 exec_context_changes_t ecc;
1617 ecc.w.wcontext = C_ROOT;
1618 exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
1619 execute_function(NULL, exc2, action, 0);
1620 exc_destroy_context(exc2);
1621 WaitForButtonsUp(True);
1623 else
1625 /* do gnome buttonpress forwarding if win == root */
1626 GNOME_ProxyButtonEvent(exc->x.etrigger);
1629 return;
1632 /* Handles button presses on unmanaged windows */
1633 static void __handle_bpress_on_unmanaged(const exec_context_t *exc)
1635 /* Pass the event to the application. */
1636 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1637 XFlush(dpy);
1639 return;
1642 /* Handles button presses on managed windows */
1643 static void __handle_bpress_on_managed(const exec_context_t *exc)
1645 char *action;
1646 hfrc_ret_t f;
1647 FvwmWindow * const fw = exc->w.fw;
1648 XEvent *e;
1650 e = exc->x.etrigger;
1651 /* Now handle click to focus and click to raise. */
1652 __handle_focus_raise_click(&f, exc);
1653 PressedW = (f.do_forbid_function) ? None : exc->w.w;
1654 if (f.do_focus)
1656 if (!__handle_click_to_focus(exc))
1658 /* Window didn't accept the focus; pass the click to
1659 * the application. */
1660 f.do_swallow_click = 0;
1663 if (f.do_raise)
1665 if (__handle_click_to_raise(exc) == True)
1667 /* We can't raise the window immediately because the
1668 * action bound to the click might be "Lower" or
1669 * "RaiseLower". So mark the window as scheduled to be
1670 * raised after the binding is executed. Functions that
1671 * modify the stacking order will reset this flag. */
1672 SET_SCHEDULED_FOR_RAISE(fw, 1);
1675 /* handle bindings */
1676 if (!f.do_forbid_function)
1678 /* stroke bindings */
1679 __handle_bpress_stroke();
1680 /* mouse bindings */
1681 action = CheckBinding(
1682 Scr.AllBindings, STROKE_ARG(0) e->xbutton.button,
1683 e->xbutton.state, GetUnusedModifiers(),
1684 exc->w.wcontext, BIND_BUTTONPRESS, &fw->class,
1685 fw->name.name);
1686 if (__handle_bpress_action(exc, action))
1688 f.do_swallow_click = 1;
1691 /* raise the window */
1692 if (IS_SCHEDULED_FOR_RAISE(fw))
1694 /* Now that we know the action did not restack the window we
1695 * can raise it.
1696 * dv (10-Aug-2002): We can safely raise the window after
1697 * redrawing it since all the decorations are drawn in the
1698 * window background and no Expose event is generated. */
1699 RaiseWindow(fw, False);
1700 SET_SCHEDULED_FOR_RAISE(fw, 0);
1702 /* clean up */
1703 if (!f.do_swallow_click)
1705 /* pass the click to the application */
1706 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1707 XFlush(dpy);
1709 else if (f.do_focus || f.do_raise)
1711 WaitForButtonsUp(True);
1714 return;
1717 /* restore focus stolen by unmanaged */
1718 static void __refocus_stolen_focus_win(const evh_args_t *ea)
1720 FOCUS_SET(Scr.StolenFocusWin);
1721 ea->exc->x.etrigger->xfocus.window = Scr.StolenFocusWin;
1722 ea->exc->x.etrigger->type = FocusIn;
1723 Scr.UnknownWinFocused = None;
1724 Scr.StolenFocusWin = None;
1725 dispatch_event(ea->exc->x.etrigger);
1727 return;
1730 /* ---------------------------- event handlers ----------------------------- */
1732 void HandleButtonPress(const evh_args_t *ea)
1734 DBUG("HandleButtonPress", "Routine Entered");
1736 GrabEm(CRS_NONE, GRAB_PASSIVE);
1737 if (__is_bpress_window_handled(ea->exc) == False)
1739 __handle_bpress_on_unmanaged(ea->exc);
1741 else if (ea->exc->w.fw != NULL)
1743 __handle_bpress_on_managed(ea->exc);
1745 else
1747 __handle_bpress_on_root(ea->exc);
1749 UngrabEm(GRAB_PASSIVE);
1751 return;
1754 #ifdef HAVE_STROKE
1755 void HandleButtonRelease(const evh_args_t *ea)
1757 char *action;
1758 char *name;
1759 int real_modifier;
1760 const XEvent *te = ea->exc->x.etrigger;
1761 XClassHint *class;
1763 DBUG("HandleButtonRelease", "Routine Entered");
1764 send_motion = False;
1765 stroke_trans (sequence);
1766 DBUG("HandleButtonRelease",sequence);
1767 /* Allows modifier to work (Only R context works here). */
1768 real_modifier = te->xbutton.state - (1 << (7 + te->xbutton.button));
1769 if (ea->exc->w.fw == NULL)
1771 class = NULL;
1772 name = NULL;
1774 else
1776 class = &ea->exc->w.fw->class;
1777 name = ea->exc->w.fw->name.name;
1779 /* need to search for an appropriate stroke binding */
1780 action = CheckBinding(
1781 Scr.AllBindings, sequence, te->xbutton.button, real_modifier,
1782 GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE,
1783 class, name);
1784 /* got a match, now process it */
1785 if (action != NULL && (action[0] != 0))
1787 execute_function(NULL, ea->exc, action, 0);
1788 WaitForButtonsUp(True);
1790 else
1793 * do gnome buttonpress forwarding if win == root
1795 if (Scr.Root == te->xany.window)
1797 GNOME_ProxyButtonEvent(te);
1801 return;
1803 #endif /* HAVE_STROKE */
1805 void HandleClientMessage(const evh_args_t *ea)
1807 const XEvent *te = ea->exc->x.etrigger;
1808 FvwmWindow * const fw = ea->exc->w.fw;
1810 DBUG("HandleClientMessage", "Routine Entered");
1812 /* Process GNOME and EWMH Messages */
1813 if (GNOME_ProcessClientMessage(ea->exc))
1815 return;
1817 else if (EWMH_ProcessClientMessage(ea->exc))
1819 return;
1822 /* handle deletion of tear out menus */
1823 if (fw && IS_TEAR_OFF_MENU(fw) && te->xclient.format == 32 &&
1824 te->xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
1826 menu_close_tear_off_menu(fw);
1827 return;
1830 if (te->xclient.message_type == _XA_WM_CHANGE_STATE &&
1831 fw && te->xclient.data.l[0] == IconicState && !IS_ICONIFIED(fw))
1833 const exec_context_t *exc;
1834 exec_context_changes_t ecc;
1836 ecc.w.wcontext = C_WINDOW;
1837 exc = exc_clone_context(ea->exc, &ecc, ECC_WCONTEXT);
1838 execute_function(NULL, exc, "Iconify", 0);
1839 exc_destroy_context(exc);
1840 return;
1843 /* FIXME: Is this safe enough ? I guess if clients behave
1844 * according to ICCCM and send these messages only if they
1845 * grabbed the pointer, it is OK */
1847 extern Atom _XA_WM_COLORMAP_NOTIFY;
1848 if (te->xclient.message_type == _XA_WM_COLORMAP_NOTIFY)
1850 set_client_controls_colormaps(te->xclient.data.l[1]);
1851 return;
1855 /* CKH - if we get here, it was an unknown client message, so send
1856 * it to the client if it was in a window we know about. I'm not so
1857 * sure this should be done or not, since every other window manager
1858 * I've looked at doesn't. But it might be handy for a free drag and
1859 * drop setup being developed for Linux. */
1860 /* TA: 20091231 - But this confuses QT Drag and Drop since it handles
1861 * processing XSendEvents in an odd order. For now, workaround this
1862 * by using a BugOpts option.
1864 if (fw)
1866 if ((!Scr.bo.do_enable_qt_drag_n_drop_workaround) &&
1867 (te->xclient.window != FW_W(fw)))
1869 XEvent e;
1871 e = *te;
1872 e.xclient.window = FW_W(fw);
1873 FSendEvent(dpy, FW_W(fw), False, NoEventMask, &e);
1878 void HandleColormapNotify(const evh_args_t *ea)
1880 colormap_handle_colormap_notify(ea);
1882 return;
1885 void HandleConfigureRequest(const evh_args_t *ea)
1887 const XEvent *te = ea->exc->x.etrigger;
1888 XConfigureRequestEvent cre;
1889 FvwmWindow *fw = ea->exc->w.fw;
1891 DBUG("HandleConfigureRequest", "Routine Entered");
1893 cre = te->xconfigurerequest;
1894 /* te->xany.window is te->.xconfigurerequest.parent, so the context
1895 * window may be wrong. */
1896 if (XFindContext(dpy, cre.window, FvwmContext, (caddr_t *)&fw) ==
1897 XCNOENT)
1899 fw = NULL;
1901 __handle_configure_request(cre, ea, fw, False, ForgetGravity);
1903 return;
1906 void HandleDestroyNotify(const evh_args_t *ea)
1908 DBUG("HandleDestroyNotify", "Routine Entered");
1910 destroy_window(ea->exc->w.fw);
1911 EWMH_ManageKdeSysTray(
1912 ea->exc->x.etrigger->xdestroywindow.window,
1913 ea->exc->x.etrigger->type);
1914 EWMH_WindowDestroyed();
1915 GNOME_SetClientList();
1917 return;
1920 #define DEBUG_ENTERNOTIFY 0
1921 #if DEBUG_ENTERNOTIFY
1922 static int ecount=0;
1923 #define ENTER_DBG(x) fprintf x;
1924 #else
1925 #define ENTER_DBG(x)
1926 #endif
1927 void HandleEnterNotify(const evh_args_t *ea)
1929 const XEnterWindowEvent *ewp;
1930 XEvent d;
1931 FvwmWindow *sf;
1932 static Bool is_initial_ungrab_pending = True;
1933 Bool is_tear_off_menu;
1934 const XEvent *te = ea->exc->x.etrigger;
1935 FvwmWindow * const fw = ea->exc->w.fw;
1937 DBUG("HandleEnterNotify", "Routine Entered");
1938 ewp = &te->xcrossing;
1939 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)"));
1941 if (
1942 ewp->window == Scr.Root &&
1943 ewp->detail == NotifyInferior && ewp->mode == NotifyNormal)
1945 /* pointer left subwindow */
1946 BroadcastPacket(
1947 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1948 (long)NULL);
1950 else if (
1951 ewp->window == Scr.Root &&
1952 ewp->detail == NotifyNonlinearVirtual)
1954 /* pointer entered screen */
1955 BroadcastPacket(
1956 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1957 (long)NULL);
1959 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
1961 if (fw && !IS_ICONIFIED(fw) && ewp->window == FW_W(fw))
1963 InstallWindowColormaps(fw);
1965 else
1967 /* make sure its for one of our windows */
1968 /* handle a subwindow cmap */
1969 InstallWindowColormaps(NULL);
1972 else if (!fw)
1974 EnterSubWindowColormap(ewp->window);
1976 if (Scr.flags.is_wire_frame_displayed)
1978 ENTER_DBG((stderr, "en: exit: iwfd\n"));
1979 /* Ignore EnterNotify events while a window is resized or moved
1980 * as a wire frame; otherwise the window list may be screwed
1981 * up. */
1982 return;
1984 if (fw)
1986 if (ewp->window != FW_W_FRAME(fw) &&
1987 ewp->window != FW_W_PARENT(fw) &&
1988 ewp->window != FW_W(fw) &&
1989 ewp->window != FW_W_ICON_TITLE(fw) &&
1990 ewp->window != FW_W_ICON_PIXMAP(fw))
1992 /* Ignore EnterNotify that received by any of the sub
1993 * windows that don't handle this event. unclutter
1994 * triggers these events sometimes, re focusing an
1995 * unfocused window under the pointer */
1996 ENTER_DBG((stderr, "en: exit: funny window\n"));
1997 return;
2000 if (Scr.focus_in_pending_window != NULL)
2002 ENTER_DBG((stderr, "en: exit: fipw\n"));
2003 /* Ignore EnterNotify event while we are waiting for a window to
2004 * receive focus via Focus or FlipFocus commands. */
2005 focus_grab_buttons(fw);
2006 return;
2008 if (ewp->mode == NotifyGrab)
2010 ENTER_DBG((stderr, "en: exit: NotifyGrab\n"));
2011 return;
2013 else if (ewp->mode == NotifyNormal)
2015 ENTER_DBG((stderr, "en: NotifyNormal\n"));
2016 if (ewp->detail == NotifyNonlinearVirtual &&
2017 ewp->focus == False && ewp->subwindow != None)
2019 /* This takes care of some buggy apps that forget that
2020 * one of their dialog subwindows has the focus after
2021 * popping up a selection list several times (ddd,
2022 * netscape). I'm not convinced that this does not
2023 * break something else. */
2024 ENTER_DBG((stderr, "en: NN: refreshing focus\n"));
2025 refresh_focus(fw);
2028 else if (ewp->mode == NotifyUngrab)
2030 ENTER_DBG((stderr, "en: NotifyUngrab\n"));
2031 /* Ignore events generated by grabbing or ungrabbing the
2032 * pointer. However, there is no way to prevent the client
2033 * application from handling this event and, for example,
2034 * grabbing the focus. This will interfere with functions that
2035 * transferred the focus to a different window. */
2036 if (is_initial_ungrab_pending)
2038 ENTER_DBG((stderr, "en: NU: initial ungrab pending (lgw = NULL)\n"));
2039 is_initial_ungrab_pending = False;
2040 xcrossing_last_grab_window = NULL;
2042 else
2044 if (ewp->detail == NotifyNonlinearVirtual &&
2045 ewp->focus == False && ewp->subwindow != None)
2047 /* see comment above */
2048 ENTER_DBG((stderr, "en: NU: refreshing focus\n"));
2049 refresh_focus(fw);
2051 if (fw && fw == xcrossing_last_grab_window)
2053 ENTER_DBG((stderr, "en: exit: NU: is last grab window\n"));
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: last grab window = NULL\n"));
2059 xcrossing_last_grab_window = NULL;
2061 focus_grab_buttons(fw);
2063 return;
2065 else if (fw)
2067 if (ewp->window != FW_W_FRAME(fw) &&
2068 ewp->window != FW_W_ICON_TITLE(fw) &&
2069 ewp->window != FW_W_ICON_PIXMAP(fw))
2071 ENTER_DBG((stderr, "en: exit: NU: not frame window\n"));
2072 focus_grab_buttons(fw);
2073 return;
2078 if (fw)
2080 is_initial_ungrab_pending = False;
2083 /* look for a matching leaveNotify which would nullify this EnterNotify
2086 * RBW - if we're in startup, this is a coerced focus, so we don't
2087 * want to save the event time, or exit prematurely.
2089 * Ignore LeaveNotify events for tear out menus - handled by menu code
2091 is_tear_off_menu =
2092 (fw && IS_TEAR_OFF_MENU(fw) && ewp->window == FW_W(fw));
2093 if (!fFvwmInStartup && !is_tear_off_menu &&
2094 FCheckTypedWindowEvent(dpy, ewp->window, LeaveNotify, &d))
2096 if (d.xcrossing.mode == NotifyNormal &&
2097 d.xcrossing.detail != NotifyInferior)
2099 ENTER_DBG((stderr, "en: exit: found LeaveNotify\n"));
2100 return;
2104 if (ewp->window == Scr.Root)
2106 FvwmWindow *lf = get_last_screen_focus_window();
2108 if (!Scr.flags.is_pointer_on_this_screen)
2110 Scr.flags.is_pointer_on_this_screen = 1;
2111 if (lf && lf != &Scr.FvwmRoot &&
2112 !FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(lf)))
2114 SetFocusWindow(lf, True, FOCUS_SET_FORCE);
2116 else if (lf != &Scr.FvwmRoot)
2118 ForceDeleteFocus();
2120 else
2122 /* This was the first EnterNotify event for the
2123 * root window - ignore */
2125 set_last_screen_focus_window(NULL);
2127 else if (!(sf = get_focus_window()) ||
2128 FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2130 DeleteFocus(True);
2132 else if (
2133 Scr.UnknownWinFocused != None && sf != NULL &&
2134 FW_W(sf) == Scr.StolenFocusWin)
2136 __refocus_stolen_focus_win(ea);
2138 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2140 InstallWindowColormaps(NULL);
2142 focus_grab_buttons(lf);
2143 return;
2145 else
2147 Scr.flags.is_pointer_on_this_screen = 1;
2150 /* An EnterEvent in one of the PanFrameWindows activates the Paging or
2151 an EdgeCommand. */
2152 if (is_pan_frame(ewp->window))
2154 char *edge_command = NULL;
2156 if (
2157 Scr.UnknownWinFocused != None &&
2158 (sf = get_focus_window()) != NULL &&
2159 FW_W(sf) == Scr.StolenFocusWin)
2161 __refocus_stolen_focus_win(ea);
2163 /* check for edge commands */
2164 if (ewp->window == Scr.PanFrameTop.win)
2166 edge_command = Scr.PanFrameTop.command;
2168 else if (ewp->window == Scr.PanFrameBottom.win)
2170 edge_command = Scr.PanFrameBottom.command;
2172 else if (ewp->window == Scr.PanFrameLeft.win)
2174 edge_command = Scr.PanFrameLeft.command;
2176 else if (ewp->window == Scr.PanFrameRight.win)
2178 edge_command = Scr.PanFrameRight.command;
2180 if (edge_command && ewp->mode == NotifyUngrab &&
2181 ewp->detail == NotifyAncestor)
2183 /* nothing */
2185 else if (edge_command)
2187 execute_function(NULL, ea->exc, edge_command, 0);
2189 else
2191 /* no edge command for this pan frame - so we do
2192 * HandlePaging */
2193 int delta_x = 0;
2194 int delta_y = 0;
2195 XEvent e;
2197 /* this was in the HandleMotionNotify before, HEDU */
2198 Scr.flags.is_pointer_on_this_screen = 1;
2199 e = *te;
2200 HandlePaging(
2201 &e, Scr.EdgeScrollX, Scr.EdgeScrollY, &JunkX,
2202 &JunkY, &delta_x, &delta_y, True, True, False,
2203 Scr.ScrollDelay);
2204 return;
2207 if (!fw)
2209 return;
2211 if (IS_EWMH_DESKTOP(FW_W(fw)))
2213 BroadcastPacket(
2214 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
2215 (long)NULL);
2216 return;
2218 if (ewp->window == FW_W_FRAME(fw) ||
2219 ewp->window == FW_W_ICON_TITLE(fw) ||
2220 ewp->window == FW_W_ICON_PIXMAP(fw))
2222 BroadcastPacket(
2223 MX_ENTER_WINDOW, 3, (long)FW_W(fw),
2224 (long)FW_W_FRAME(fw), (unsigned long)fw);
2226 sf = get_focus_window();
2227 if (sf && fw != sf && FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2229 ENTER_DBG((stderr, "en: delete focus\n"));
2230 DeleteFocus(True);
2232 focus_grab_buttons(fw);
2233 if (FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
2235 ENTER_DBG((stderr, "en: set mousey focus\n"));
2236 if (ewp->window == FW_W(fw))
2238 /* Event is for the client window...*/
2239 #ifndef EXPERIMENTAL_ROU_HANDLING_V2
2240 /* RBW -- This may still be needed at times, I'm not
2241 *sure yet. */
2242 SetFocusWindowClientEntered(
2243 fw, True, FOCUS_SET_BY_ENTER);
2244 #else
2245 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2246 #endif
2248 else
2250 /* Event is for the frame...*/
2251 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2254 else if (focus_is_focused(fw) && focus_does_accept_input_focus(fw))
2256 /* We have to refresh the focus window here in case we left the
2257 * focused fvwm window. Motif apps may lose the input focus
2258 * otherwise. But do not try to refresh the focus of
2259 * applications that want to handle it themselves. */
2260 focus_force_refresh_focus(fw);
2262 else if (sf != fw)
2264 /* Give the window a chance to grab the buttons needed for
2265 * raise-on-click */
2266 focus_grab_buttons(sf);
2268 if (
2269 Scr.UnknownWinFocused != None && sf != NULL &&
2270 FW_W(sf) == Scr.StolenFocusWin)
2272 __refocus_stolen_focus_win(ea);
2274 /* We get an EnterNotify with mode == UnGrab when fvwm releases the
2275 * grab held during iconification. We have to ignore this, or icon
2276 * title will be initially raised. */
2277 if (IS_ICONIFIED(fw) && (ewp->mode == NotifyNormal) &&
2278 (ewp->window == FW_W_ICON_PIXMAP(fw) ||
2279 ewp->window == FW_W_ICON_TITLE(fw)) &&
2280 FW_W_ICON_PIXMAP(fw) != None)
2282 SET_ICON_ENTERED(fw, 1);
2283 DrawIconWindow(fw, True, False, False, False, NULL);
2285 /* Check for tear off menus */
2286 if (is_tear_off_menu)
2288 menu_enter_tear_off_menu(ea->exc);
2291 return;
2294 void HandleExpose(const evh_args_t *ea)
2296 XEvent e;
2297 FvwmWindow * const fw = ea->exc->w.fw;
2299 e = *ea->exc->x.etrigger;
2300 #if 0
2301 /* This doesn't work well. Sometimes, the expose count is zero although
2302 * dozens of expose events are pending. This happens all the time
2303 * during a shading animation. Simply flush expose events
2304 * unconditionally. */
2305 if (e.xexpose.count != 0)
2307 flush_accumulate_expose(e.xexpose.window, &e);
2309 #else
2310 flush_accumulate_expose(e.xexpose.window, &e);
2311 #endif
2312 if (fw == NULL)
2314 return;
2316 if (e.xany.window == FW_W_ICON_TITLE(fw) ||
2317 e.xany.window == FW_W_ICON_PIXMAP(fw))
2319 DrawIconWindow(fw, True, True, False, False, &e);
2320 return;
2322 else if (IS_TEAR_OFF_MENU(fw) && e.xany.window == FW_W(fw))
2324 /* refresh the contents of the torn out menu */
2325 menu_expose(&e, NULL);
2328 return;
2331 void HandleFocusIn(const evh_args_t *ea)
2333 XEvent d;
2334 Window w = None;
2335 Window focus_w = None;
2336 Window focus_fw = None;
2337 Pixel fc = 0;
2338 Pixel bc = 0;
2339 FvwmWindow *ffw_old = get_focus_window();
2340 FvwmWindow *sf;
2341 Bool do_force_broadcast = False;
2342 Bool is_unmanaged_focused = False;
2343 static Window last_focus_w = None;
2344 static Window last_focus_fw = None;
2345 static Bool was_nothing_ever_focused = True;
2346 FvwmWindow *fw = ea->exc->w.fw;
2348 DBUG("HandleFocusIn", "Routine Entered");
2350 Scr.focus_in_pending_window = NULL;
2351 /* This is a hack to make the PointerKey command work */
2352 if (ea->exc->x.etrigger->xfocus.detail != NotifyPointer)
2354 /**/
2355 w = ea->exc->x.etrigger->xany.window;
2357 while (FCheckTypedEvent(dpy, FocusIn, &d))
2359 /* dito */
2360 if (d.xfocus.detail != NotifyPointer)
2362 /**/
2363 w = d.xany.window;
2366 /* dito */
2367 if (w == None)
2369 return;
2371 /**/
2372 if (XFindContext(dpy, w, FvwmContext, (caddr_t *) &fw) == XCNOENT)
2374 fw = NULL;
2377 Scr.UnknownWinFocused = None;
2378 if (!fw)
2380 if (w != Scr.NoFocusWin)
2382 Scr.UnknownWinFocused = w;
2383 Scr.StolenFocusWin =
2384 (ffw_old != NULL) ? FW_W(ffw_old) : None;
2385 focus_w = w;
2386 is_unmanaged_focused = True;
2388 /* Only show a non-focused window as focused,
2389 * if the focus is on unmanaged and flickering qt dialogs
2390 * workaround is on. */
2391 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2392 !is_unmanaged_focused)
2394 border_draw_decorations(
2395 Scr.Hilite, PART_ALL, False, True, CLEAR_ALL,
2396 NULL, NULL);
2397 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2399 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2401 InstallWindowColormaps(Scr.Hilite);
2403 else
2405 InstallWindowColormaps(NULL);
2409 /* Not very useful if no window that fvwm and its modules know
2410 * about has the focus. */
2411 fc = GetColor(DEFAULT_FORE_COLOR);
2412 bc = GetColor(DEFAULT_BACK_COLOR);
2414 else if (fw != Scr.Hilite ||
2415 /* domivogt (16-May-2000): This check is necessary to force
2416 * sending a M_FOCUS_CHANGE packet after an unmanaged window
2417 * was focused. Otherwise fvwm would believe that Scr.Hilite
2418 * was still focused and not send any info to the modules. */
2419 last_focus_fw == None ||
2420 IS_FOCUS_CHANGE_BROADCAST_PENDING(fw) ||
2421 fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
2423 do_force_broadcast = IS_FOCUS_CHANGE_BROADCAST_PENDING(fw);
2424 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 0);
2425 if (fw != Scr.Hilite &&
2426 fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
2428 border_draw_decorations(
2429 fw, PART_ALL, True, True, CLEAR_ALL, NULL,
2430 NULL);
2432 focus_w = FW_W(fw);
2433 focus_fw = FW_W_FRAME(fw);
2434 fc = fw->hicolors.fore;
2435 bc = fw->hicolors.back;
2436 set_focus_window(fw);
2437 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2439 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2441 InstallWindowColormaps(Scr.Hilite);
2443 else
2445 InstallWindowColormaps(NULL);
2449 else
2451 return;
2453 if (was_nothing_ever_focused || last_focus_fw == None ||
2454 focus_w != last_focus_w || focus_fw != last_focus_fw ||
2455 do_force_broadcast)
2457 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2458 !is_unmanaged_focused)
2460 BroadcastPacket(
2461 M_FOCUS_CHANGE, 5, (long)focus_w,
2462 (long)focus_fw,
2463 (unsigned long)IsLastFocusSetByMouse(),
2464 (long)fc, (long)bc);
2465 EWMH_SetActiveWindow(focus_w);
2467 last_focus_w = focus_w;
2468 last_focus_fw = focus_fw;
2469 was_nothing_ever_focused = False;
2471 if ((sf = get_focus_window()) != ffw_old)
2473 focus_grab_buttons(sf);
2474 focus_grab_buttons(ffw_old);
2477 return;
2480 void HandleFocusOut(const evh_args_t *ea)
2482 if (Scr.UnknownWinFocused != None && Scr.StolenFocusWin != None &&
2483 ea->exc->x.etrigger->xfocus.window == Scr.UnknownWinFocused)
2485 __refocus_stolen_focus_win(ea);
2488 return;
2491 void __handle_key(const evh_args_t *ea, Bool is_press)
2493 char *action;
2494 FvwmWindow *sf;
2495 KeyCode kc;
2496 int kcontext;
2497 const XEvent *te = ea->exc->x.etrigger;
2498 const FvwmWindow * const fw = ea->exc->w.fw;
2499 Bool is_second_binding;
2500 const XClassHint *winClass1, *winClass2;
2501 XClassHint tmp;
2502 char *name1, *name2;
2503 const exec_context_t *exc;
2504 exec_context_changes_t ecc;
2506 PressedW = None;
2508 /* Here's a real hack - some systems have two keys with the
2509 * same keysym and different keycodes. This converts all
2510 * the cases to one keycode. */
2511 kc = XKeysymToKeycode(dpy, XKeycodeToKeysym(dpy, te->xkey.keycode, 0));
2513 /* Check if there is something bound to the key */
2515 sf = get_focus_window();
2516 if (sf == NULL)
2518 tmp.res_name = tmp.res_class = name1 = "root";
2519 winClass1 = &tmp;
2520 kcontext = C_ROOT;
2522 else
2524 winClass1 = &sf->class;
2525 name1 = sf->name.name;
2526 kcontext = (sf == fw ? ea->exc->w.wcontext : C_WINDOW);
2529 if (fw == NULL)
2531 tmp.res_name = tmp.res_class = name2 = "root";
2532 winClass2 = &tmp;
2534 else
2536 winClass2 = &fw->class;
2537 name2 = fw->name.name;
2539 /* Searching the binding list with a different 'type' value
2540 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
2541 * The different context value does though. */
2542 action = CheckTwoBindings(
2543 &is_second_binding, Scr.AllBindings, STROKE_ARG(0) kc,
2544 te->xkey.state, GetUnusedModifiers(), kcontext, BIND_KEYPRESS,
2545 winClass1, name1, ea->exc->w.wcontext, BIND_PKEYPRESS,
2546 winClass2, name2);
2548 if (action != NULL)
2550 if (!is_press)
2552 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2553 return;
2555 exc = ea->exc;
2556 if (is_second_binding == False)
2558 ecc.w.fw = sf;
2559 ecc.w.wcontext = kcontext;
2560 exc = exc_clone_context(
2561 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
2563 execute_function(NULL, exc, action, 0);
2564 if (is_second_binding == False)
2566 exc_destroy_context(exc);
2568 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2569 return;
2572 /* if we get here, no function key was bound to the key. Send it
2573 * to the client if it was in a window we know about. */
2574 sf = get_focus_window();
2575 if (sf && te->xkey.window != FW_W(sf))
2577 XEvent e;
2579 e = *te;
2580 e.xkey.window = FW_W(sf);
2581 FSendEvent(
2582 dpy, e.xkey.window, False,
2583 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2585 else if (fw && te->xkey.window != FW_W(fw))
2587 XEvent e;
2589 e = *te;
2590 e.xkey.window = FW_W(fw);
2591 FSendEvent(
2592 dpy, e.xkey.window, False,
2593 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2595 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2597 return;
2600 void HandleKeyPress(const evh_args_t *ea)
2602 __handle_key(ea, True);
2605 void HandleKeyRelease(const evh_args_t *ea)
2607 __handle_key(ea, False);
2610 void HandleLeaveNotify(const evh_args_t *ea)
2612 const XLeaveWindowEvent *lwp;
2613 const XEvent *te = ea->exc->x.etrigger;
2614 FvwmWindow * const fw = ea->exc->w.fw;
2616 DBUG("HandleLeaveNotify", "Routine Entered");
2618 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)"));
2619 lwp = &te->xcrossing;
2620 if (
2621 lwp->window == Scr.Root &&
2622 lwp->detail == NotifyInferior && lwp->mode == NotifyNormal)
2624 /* pointer entered subwindow */
2625 BroadcastPacket(
2626 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2627 (long)NULL);
2629 else if (
2630 lwp->window == Scr.Root &&
2631 lwp->detail == NotifyNonlinearVirtual)
2633 /* pointer left screen */
2634 BroadcastPacket(
2635 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2636 (long)NULL);
2638 /* Ignore LeaveNotify events while a window is resized or moved as a
2639 * wire frame; otherwise the window list may be screwed up. */
2640 if (Scr.flags.is_wire_frame_displayed)
2642 return;
2644 if (lwp->mode != NotifyNormal)
2646 /* Ignore events generated by grabbing or ungrabbing the
2647 * pointer. However, there is no way to prevent the client
2648 * application from handling this event and, for example,
2649 * grabbing the focus. This will interfere with functions that
2650 * transferred the focus to a different window. It is
2651 * necessary to check for LeaveNotify events on the client
2652 * window too in case buttons are not grabbed on it. */
2653 if (lwp->mode == NotifyGrab && fw &&
2654 (lwp->window == FW_W_FRAME(fw) ||
2655 lwp->window == FW_W(fw) ||
2656 lwp->window == FW_W_ICON_TITLE(fw) ||
2657 lwp->window == FW_W_ICON_PIXMAP(fw)))
2659 ENTER_DBG((stderr, "ln: *** lgw = 0x%08x\n", (int)fw));
2660 xcrossing_last_grab_window = fw;
2662 #ifdef FOCUS_EXPANDS_TITLE
2663 if (fw && IS_ICONIFIED(fw))
2665 SET_ICON_ENTERED(fw, 0);
2666 DrawIconWindow(
2667 fw, True, False, False, False, NULL);
2669 #endif
2670 return;
2672 /* CDE-like behaviour of raising the icon title if the icon
2673 gets the focus (in particular if the cursor is over the icon) */
2674 if (fw && IS_ICONIFIED(fw))
2676 SET_ICON_ENTERED(fw,0);
2677 DrawIconWindow(fw, True, False, False, False, NULL);
2680 /* An LeaveEvent in one of the PanFrameWindows activates
2681 an EdgeLeaveCommand. */
2682 if (is_pan_frame(lwp->window))
2684 char *edge_command_leave = NULL;
2686 /* check for edge commands */
2687 if (lwp->window == Scr.PanFrameTop.win)
2689 edge_command_leave = Scr.PanFrameTop.command_leave;
2691 else if (lwp->window == Scr.PanFrameBottom.win)
2693 edge_command_leave = Scr.PanFrameBottom.command_leave;
2695 else if (lwp->window == Scr.PanFrameLeft.win)
2697 edge_command_leave = Scr.PanFrameLeft.command_leave;
2699 else if (lwp->window == Scr.PanFrameRight.win)
2701 edge_command_leave = Scr.PanFrameRight.command_leave;
2703 if (edge_command_leave && lwp->mode == NotifyUngrab &&
2704 lwp->detail == NotifyAncestor)
2706 /* nothing */
2708 else if (edge_command_leave)
2710 execute_function(NULL, ea->exc, edge_command_leave, 0);
2715 /* If we leave the root window, then we're really moving
2716 * another screen on a multiple screen display, and we
2717 * need to de-focus and unhighlight to make sure that we
2718 * don't end up with more than one highlighted window at a time */
2719 if (lwp->window == Scr.Root &&
2720 /* domivogt (16-May-2000): added this test because somehow fvwm
2721 * sometimes gets a LeaveNotify on the root window although it is
2722 * single screen. */
2723 Scr.NumberOfScreens > 1)
2725 if (lwp->mode == NotifyNormal)
2727 if (lwp->detail != NotifyInferior)
2729 FvwmWindow *sf = get_focus_window();
2731 Scr.flags.is_pointer_on_this_screen = 0;
2732 set_last_screen_focus_window(sf);
2733 if (sf != NULL)
2735 DeleteFocus(True);
2737 if (Scr.Hilite != NULL)
2739 border_draw_decorations(
2740 Scr.Hilite, PART_ALL, False,
2741 True, CLEAR_ALL, NULL, NULL);
2746 else
2748 /* handle a subwindow cmap */
2749 LeaveSubWindowColormap(te->xany.window);
2751 if (fw != NULL &&
2752 (lwp->window == FW_W_FRAME(fw) ||
2753 lwp->window == FW_W_ICON_TITLE(fw) ||
2754 lwp->window == FW_W_ICON_PIXMAP(fw)))
2756 BroadcastPacket(
2757 MX_LEAVE_WINDOW, 3, (long)FW_W(fw),
2758 (long)FW_W_FRAME(fw), (unsigned long)fw);
2761 return;
2764 void HandleMapNotify(const evh_args_t *ea)
2766 Bool is_on_this_page = False;
2767 const XEvent *te = ea->exc->x.etrigger;
2768 FvwmWindow * const fw = ea->exc->w.fw;
2770 DBUG("HandleMapNotify", "Routine Entered");
2772 if (!fw)
2774 if (te->xmap.override_redirect == True &&
2775 te->xmap.window != Scr.NoFocusWin)
2777 XSelectInput(dpy, te->xmap.window, XEVMASK_ORW);
2778 XFlush(dpy);
2779 Scr.UnknownWinFocused = te->xmap.window;
2781 return;
2783 if (te->xmap.window == FW_W_FRAME(fw))
2785 /* Now that we know the frame is mapped after capturing the
2786 * window we do not need StructureNotifyMask events anymore. */
2787 XSelectInput(dpy, FW_W_FRAME(fw), XEVMASK_FRAMEW);
2788 XFlush(dpy);
2790 /* Except for identifying over-ride redirect window mappings, we
2791 * don't need or want windows associated with the
2792 * SubstructureNotifyMask */
2793 if (te->xmap.event != te->xmap.window)
2795 return;
2797 SET_MAP_PENDING(fw, 0);
2798 /* don't map if the event was caused by a de-iconify */
2799 if (IS_ICONIFY_PENDING(fw))
2801 return;
2804 /* Make sure at least part of window is on this page before giving it
2805 * focus... */
2806 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2809 * Need to do the grab to avoid race condition of having server send
2810 * MapNotify to client before the frame gets mapped; this is bad because
2811 * the client would think that the window has a chance of being viewable
2812 * when it really isn't.
2814 MyXGrabServer (dpy);
2815 if (FW_W_ICON_TITLE(fw))
2817 XUnmapWindow(dpy, FW_W_ICON_TITLE(fw));
2819 if (FW_W_ICON_PIXMAP(fw) != None)
2821 XUnmapWindow(dpy, FW_W_ICON_PIXMAP(fw));
2823 XMapSubwindows(dpy, FW_W_FRAME(fw));
2824 if (fw->Desk == Scr.CurrentDesk)
2826 XMapWindow(dpy, FW_W_FRAME(fw));
2828 if (IS_ICONIFIED(fw))
2830 BroadcastPacket(
2831 M_DEICONIFY, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2832 (unsigned long)fw);
2834 else
2836 BroadcastPacket(
2837 M_MAP, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2838 (unsigned long)fw);
2841 if (is_on_this_page &&
2842 focus_query_open_grab_focus(fw, get_focus_window()) == True)
2844 SetFocusWindow(fw, True, FOCUS_SET_FORCE);
2846 border_draw_decorations(
2847 fw, PART_ALL, (fw == get_focus_window()) ? True : False, True,
2848 CLEAR_ALL, NULL, NULL);
2849 MyXUngrabServer (dpy);
2850 SET_MAPPED(fw, 1);
2851 SET_ICONIFIED(fw, 0);
2852 SET_ICON_UNMAPPED(fw, 0);
2853 if (DO_ICONIFY_AFTER_MAP(fw))
2855 initial_window_options_t win_opts;
2857 /* finally, if iconification was requested before the window
2858 * was mapped, request it now. */
2859 memset(&win_opts, 0, sizeof(win_opts));
2860 Iconify(fw, &win_opts);
2861 SET_ICONIFY_AFTER_MAP(fw, 0);
2863 focus_grab_buttons_on_layer(fw->layer);
2865 return;
2868 void HandleMappingNotify(const evh_args_t *ea)
2870 XRefreshKeyboardMapping(&ea->exc->x.etrigger->xmapping);
2872 return;
2875 void HandleMapRequest(const evh_args_t *ea)
2877 DBUG("HandleMapRequest", "Routine Entered");
2879 if (fFvwmInStartup)
2881 /* Just map the damn thing, decorations are added later
2882 * in CaptureAllWindows. */
2883 XMapWindow(dpy, ea->exc->x.etrigger->xmaprequest.window);
2884 return;
2886 HandleMapRequestKeepRaised(ea, None, NULL, NULL);
2888 return;
2891 void HandleMapRequestKeepRaised(
2892 const evh_args_t *ea, Window KeepRaised, FvwmWindow *ReuseWin,
2893 initial_window_options_t *win_opts)
2895 Bool is_on_this_page = False;
2896 Bool is_new_window = False;
2897 FvwmWindow *tmp;
2898 FvwmWindow *sf;
2899 initial_window_options_t win_opts_bak;
2900 Window ew;
2901 FvwmWindow *fw;
2902 extern Bool Restarting;
2903 const char *initial_map_command;
2905 initial_map_command = NULL;
2906 if (win_opts == NULL)
2908 memset(&win_opts_bak, 0, sizeof(win_opts_bak));
2909 win_opts = &win_opts_bak;
2911 ew = ea->exc->w.w;
2912 if (ReuseWin == NULL)
2914 Window pw;
2916 pw = ea->exc->x.etrigger->xmaprequest.parent;
2917 if (XFindContext(dpy, ew, FvwmContext, (caddr_t *)&fw) ==
2918 XCNOENT)
2920 fw = NULL;
2922 if (fw != NULL && IS_MAP_PENDING(fw))
2924 /* The window is already going to be mapped, no need to
2925 * do that twice */
2926 return;
2929 else
2931 fw = ReuseWin;
2934 if (fw == NULL && EWMH_IsKdeSysTrayWindow(ew))
2936 /* This means that the window is swallowed by kicker and that
2937 * kicker restart or exit. As we should assume that kicker
2938 * restart we should return here, if not we go into trouble
2939 * ... */
2940 return;
2942 if (!win_opts->flags.do_override_ppos)
2944 XFlush(dpy);
2947 /* If the window has never been mapped before ... */
2948 if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
2950 check_if_event_args args;
2951 XEvent dummy;
2953 args.w = ew;
2954 args.do_return_true = True;
2955 args.do_return_true_cr = False;
2956 if (
2957 FCheckIfEvent(
2958 dpy, &dummy, test_withdraw_request,
2959 (XPointer)&args)) {
2960 /* The window is moved back to the WithdrawnState
2961 * immideately. Don't map it.
2963 * However, send make sure that a WM_STATE
2964 * PropertyNotify event is sent to the window.
2965 * QT needs this.
2967 Atom atype;
2968 int aformat;
2969 unsigned long nitems, bytes_remain;
2970 unsigned char *prop;
2972 if (
2973 XGetWindowProperty(
2974 dpy, ew, _XA_WM_STATE, 0L, 3L, False,
2975 _XA_WM_STATE, &atype, &aformat,
2976 &nitems,&bytes_remain,&prop)
2977 == Success)
2979 if (prop != NULL)
2981 XFree(prop);
2982 XDeleteProperty(dpy, ew, _XA_WM_STATE);
2984 else
2986 XPropertyEvent ev;
2987 ev.type = PropertyNotify;
2988 ev.display = dpy;
2989 ev.window = ew;
2990 ev.atom = _XA_WM_STATE;
2991 ev.time = fev_get_evtime();
2992 ev.state = PropertyDelete;
2993 FSendEvent(
2994 dpy, ew, True,
2995 PropertyChangeMask,
2996 (XEvent*)&ev);
3000 return;
3003 /* Add decorations. */
3004 fw = AddWindow(
3005 &initial_map_command, ea->exc, ReuseWin, win_opts);
3006 if (fw == AW_NO_WINDOW)
3008 return;
3010 else if (fw == AW_UNMANAGED)
3012 XMapWindow(dpy, ew);
3013 return;
3015 is_new_window = True;
3018 * Make sure at least part of window is on this page
3019 * before giving it focus...
3021 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3022 if (KeepRaised != None)
3024 XRaiseWindow(dpy, KeepRaised);
3026 /* If it's not merely iconified, and we have hints, use them. */
3028 if (IS_ICONIFIED(fw))
3030 /* If no hints, or currently an icon, just "deiconify" */
3031 DeIconify(fw);
3033 else if (IS_MAPPED(fw))
3035 /* the window is already mapped - fake a MapNotify event */
3036 fake_map_unmap_notify(fw, MapNotify);
3038 else
3040 int state;
3042 if (fw->wmhints && (fw->wmhints->flags & StateHint))
3044 state = fw->wmhints->initial_state;
3046 else
3048 state = NormalState;
3050 if (win_opts->initial_state != DontCareState)
3052 state = win_opts->initial_state;
3055 switch (state)
3057 case DontCareState:
3058 case NormalState:
3059 case InactiveState:
3060 default:
3061 MyXGrabServer(dpy);
3062 if (fw->Desk == Scr.CurrentDesk)
3064 Bool do_grab_focus;
3066 SET_MAP_PENDING(fw, 1);
3067 XMapWindow(dpy, FW_W_FRAME(fw));
3068 XMapWindow(dpy, FW_W(fw));
3069 SetMapStateProp(fw, NormalState);
3070 if (Scr.flags.is_map_desk_in_progress)
3072 do_grab_focus = False;
3074 else if (!is_on_this_page)
3076 do_grab_focus = False;
3078 else if (focus_query_open_grab_focus(
3079 fw, get_focus_window()) ==
3080 True)
3082 do_grab_focus = True;
3084 else
3086 do_grab_focus = False;
3088 if (do_grab_focus)
3090 SetFocusWindow(
3091 fw, True, FOCUS_SET_FORCE);
3093 else
3095 /* make sure the old focused window
3096 * still has grabbed all necessary
3097 * buttons. */
3098 focus_grab_buttons(
3099 get_focus_window());
3102 else
3104 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
3105 /* nope, this is forbidden by the ICCCM2 */
3106 XMapWindow(dpy, FW_W(fw));
3107 SetMapStateProp(fw, NormalState);
3108 #else
3109 /* Since we will not get a MapNotify, set the
3110 * IS_MAPPED flag manually. */
3111 SET_MAPPED(fw, 1);
3112 SetMapStateProp(fw, IconicState);
3113 /* fake that the window was mapped to allow
3114 * modules to swallow it */
3115 BroadcastPacket(
3116 M_MAP, 3, (long)FW_W(fw),
3117 (long)FW_W_FRAME(fw),
3118 (unsigned long)fw);
3119 #endif
3121 /* TA: 20090125: We *have* to handle
3122 * InitialMapCommand here and not in AddWindow() to
3123 * allow for correct timings when the window is truly
3124 * mapped. (c.f. things like Iconify.)
3127 /* TA: 20091212: But only do this when we're *not*
3128 * restarting -- the window is still mapped, but gets
3129 * recaptured -- we don't want to trigger this event
3130 * again. Otherwise we end up toggling the state of
3131 * the window in situations where the
3132 * InitialMapCommand is Iconify or Maximize, for
3133 * instance.
3135 if ((initial_map_command != NULL) &&
3136 (!Restarting && Scr.flags.are_windows_captured))
3138 execute_function_override_window(
3139 NULL, ea->exc,
3140 (char *)initial_map_command, 0, fw);
3142 MyXUngrabServer(dpy);
3143 break;
3145 case IconicState:
3146 if (is_new_window)
3148 /* the window will not be mapped - fake a
3149 * MapNotify and an UnmapNotify event. Can't
3150 * remember exactly why this is necessary, but
3151 * probably something w/ (de)iconify state
3152 * confusion. */
3153 fake_map_unmap_notify(fw, MapNotify);
3154 fake_map_unmap_notify(fw, UnmapNotify);
3156 if (win_opts->flags.is_iconified_by_parent ||
3157 ((tmp = get_transientfor_fvwmwindow(fw)) &&
3158 IS_ICONIFIED(tmp)))
3160 win_opts->flags.is_iconified_by_parent = 0;
3161 SET_ICONIFIED_BY_PARENT(fw, 1);
3163 if (USE_ICON_POSITION_HINT(fw) && fw->wmhints &&
3164 (fw->wmhints->flags & IconPositionHint))
3166 win_opts->default_icon_x = fw->wmhints->icon_x;
3167 win_opts->default_icon_y = fw->wmhints->icon_y;
3169 Iconify(fw, win_opts);
3170 break;
3173 if (IS_SHADED(fw))
3175 BroadcastPacket(
3176 M_WINDOWSHADE, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
3177 (unsigned long)fw);
3179 /* If the newly mapped window overlaps the focused window, make sure
3180 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3181 sf = get_focus_window();
3182 if (sf != NULL)
3184 focus_grab_buttons(sf);
3186 if (win_opts->flags.is_menu)
3188 SET_MAPPED(fw, 1);
3189 SET_MAP_PENDING(fw, 0);
3191 EWMH_SetClientList();
3192 EWMH_SetClientListStacking();
3193 GNOME_SetClientList();
3195 return;
3198 #ifdef HAVE_STROKE
3199 void HandleMotionNotify(const evh_args_t *ea)
3201 DBUG("HandleMotionNotify", "Routine Entered");
3203 if (send_motion == True)
3205 stroke_record(
3206 ea->exc->x.etrigger->xmotion.x,
3207 ea->exc->x.etrigger->xmotion.y);
3210 return;
3212 #endif /* HAVE_STROKE */
3214 void HandlePropertyNotify(const evh_args_t *ea)
3216 Bool OnThisPage = False;
3217 Bool has_icon_changed = False;
3218 Bool has_icon_pixmap_hint_changed = False;
3219 Bool has_icon_window_hint_changed = False;
3220 FlocaleNameString new_name = { NoName, NULL };
3221 int old_wmhints_flags;
3222 const XEvent *te = ea->exc->x.etrigger;
3223 char *urgency_action = NULL;
3224 FvwmWindow * const fw = ea->exc->w.fw;
3226 DBUG("HandlePropertyNotify", "Routine Entered");
3228 if (te->xproperty.window == Scr.Root &&
3229 te->xproperty.state == PropertyNewValue &&
3230 (te->xproperty.atom == _XA_XSETROOT_ID ||
3231 te->xproperty.atom == _XA_XROOTPMAP_ID))
3233 /* background change */
3234 /* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3235 * no property notify?). _XA_XROOTPMAP_ID is used by Esetroot
3236 * compatible program: the problem here is that with some
3237 * Esetroot compatible program we get the message _before_ the
3238 * background change. This is fixed with Esetroot 9.2 (not yet
3239 * released, 2002-01-14) */
3241 /* update icon window with some alpha and tear-off menu */
3242 FvwmWindow *t;
3244 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3246 int cs;
3247 int t_cs = -1;
3248 int b_cs = t->icon_background_cs;
3249 Bool draw_picture = False;
3250 Bool draw_title = False;
3252 /* redraw ParentRelative tear-off menu */
3253 menu_redraw_transparent_tear_off_menu(t, True);
3255 if (!IS_ICONIFIED(t) || IS_ICON_SUPPRESSED(t))
3257 continue;
3259 if (Scr.Hilite == t)
3261 if (t->icon_title_cs_hi >= 0)
3263 t_cs = cs = t->icon_title_cs_hi;
3265 else
3267 cs = t->cs_hi;
3270 else
3272 if (t->icon_title_cs >= 0)
3274 t_cs = cs = t->icon_title_cs;
3276 else
3278 cs = t->cs;
3281 if (t->icon_alphaPixmap != None ||
3282 (cs >= 0 &&
3283 Colorset[cs].icon_alpha_percent < 100) ||
3284 CSET_IS_TRANSPARENT_PR(b_cs) ||
3285 (!IS_ICON_SHAPED(t) &&
3286 t->icon_background_padding > 0))
3288 draw_picture = True;
3290 if (CSET_IS_TRANSPARENT_PR(t_cs))
3292 draw_title = True;
3294 if (draw_title || draw_picture)
3296 DrawIconWindow(
3297 t, draw_title, draw_picture, False,
3298 draw_picture, NULL);
3301 if (te->xproperty.atom == _XA_XROOTPMAP_ID)
3303 update_root_transparent_colorset(te->xproperty.atom);
3305 BroadcastPropertyChange(
3306 MX_PROPERTY_CHANGE_BACKGROUND, 0, 0, "");
3307 return;
3310 if (!fw)
3312 return;
3314 if (XGetGeometry(
3315 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3316 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
3317 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
3319 return;
3323 * Make sure at least part of window is on this page
3324 * before giving it focus...
3326 OnThisPage = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3328 switch (te->xproperty.atom)
3330 case XA_WM_TRANSIENT_FOR:
3331 flush_property_notify(XA_WM_TRANSIENT_FOR, FW_W(fw));
3332 if (setup_transientfor(fw) == True)
3334 RaiseWindow(fw, False);
3336 break;
3338 case XA_WM_NAME:
3339 flush_property_notify(XA_WM_NAME, FW_W(fw));
3340 if (HAS_EWMH_WM_NAME(fw))
3342 return;
3344 FlocaleGetNameProperty(XGetWMName, dpy, FW_W(fw), &new_name);
3345 if (new_name.name == NULL)
3347 FlocaleFreeNameProperty(&new_name);
3348 return;
3350 if (strlen(new_name.name) > MAX_WINDOW_NAME_LEN)
3352 /* limit to prevent hanging X server */
3353 (new_name.name)[MAX_WINDOW_NAME_LEN] = 0;
3355 if (fw->name.name && strcmp(new_name.name, fw->name.name) == 0)
3357 /* migo: some apps update their names every second */
3358 /* griph: make sure we don't free the property if it
3359 is THE same name */
3360 if (new_name.name != fw->name.name)
3362 FlocaleFreeNameProperty(&new_name);
3364 return;
3367 free_window_names(fw, True, False);
3368 fw->name = new_name;
3369 SET_NAME_CHANGED(fw, 1);
3370 if (fw->name.name == NULL)
3372 fw->name.name = NoName; /* must not happen */
3374 setup_visible_name(fw, False);
3375 BroadcastWindowIconNames(fw, True, False);
3377 /* fix the name in the title bar */
3378 if (!IS_ICONIFIED(fw))
3380 border_draw_decorations(
3381 fw, PART_TITLE, (Scr.Hilite == fw), True,
3382 CLEAR_ALL, NULL, NULL);
3384 EWMH_SetVisibleName(fw, False);
3386 * if the icon name is NoName, set the name of the icon to be
3387 * the same as the window
3389 if (!WAS_ICON_NAME_PROVIDED(fw))
3391 fw->icon_name = fw->name;
3392 setup_visible_name(fw, True);
3393 BroadcastWindowIconNames(fw, False, True);
3394 RedoIconName(fw);
3396 break;
3398 case XA_WM_ICON_NAME:
3399 flush_property_notify(XA_WM_ICON_NAME, FW_W(fw));
3400 if (HAS_EWMH_WM_ICON_NAME(fw))
3402 return;
3404 FlocaleGetNameProperty(
3405 XGetWMIconName, dpy, FW_W(fw), &new_name);
3406 if (new_name.name == NULL)
3408 FlocaleFreeNameProperty(&new_name);
3409 return;
3411 if (new_name.name && strlen(new_name.name) > MAX_ICON_NAME_LEN)
3413 /* limit to prevent hanging X server */
3414 (new_name.name)[MAX_ICON_NAME_LEN] = 0;
3416 if (fw->icon_name.name &&
3417 strcmp(new_name.name, fw->icon_name.name) == 0)
3419 /* migo: some apps update their names every second */
3420 /* griph: make sure we don't free the property if it
3421 is THE same name */
3422 if (new_name.name != fw->icon_name.name)
3424 FlocaleFreeNameProperty(&new_name);
3426 return;
3429 free_window_names(fw, False, True);
3430 fw->icon_name = new_name;
3431 SET_WAS_ICON_NAME_PROVIDED(fw, 1);
3432 if (fw->icon_name.name == NULL)
3434 /* currently never happens */
3435 fw->icon_name.name = fw->name.name;
3436 SET_WAS_ICON_NAME_PROVIDED(fw, 0);
3438 setup_visible_name(fw, True);
3439 BroadcastWindowIconNames(fw, False, True);
3440 RedoIconName(fw);
3441 EWMH_SetVisibleName(fw, True);
3442 break;
3444 case XA_WM_HINTS:
3445 flush_property_notify(XA_WM_HINTS, FW_W(fw));
3446 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3447 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3449 old_wmhints_flags = 0;
3450 if (fw->wmhints)
3452 old_wmhints_flags = fw->wmhints->flags;
3453 XFree ((char *) fw->wmhints);
3455 setup_wm_hints(fw);
3456 if (fw->wmhints == NULL)
3458 return;
3462 * rebuild icon if the client either provides an icon
3463 * pixmap or window or has reset the hints to `no icon'.
3465 if ((fw->wmhints->flags & IconPixmapHint) ||
3466 (old_wmhints_flags & IconPixmapHint))
3468 ICON_DBG((stderr, "hpn: iph changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconPixmapHint), fw->name));
3469 has_icon_pixmap_hint_changed = True;
3471 if ((fw->wmhints->flags & IconWindowHint) ||
3472 (old_wmhints_flags & IconWindowHint))
3474 ICON_DBG((stderr, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconWindowHint), fw->name));
3475 has_icon_window_hint_changed = True;
3476 SET_USE_EWMH_ICON(fw, False);
3478 increase_icon_hint_count(fw);
3479 if (has_icon_window_hint_changed ||
3480 has_icon_pixmap_hint_changed)
3482 if (ICON_OVERRIDE_MODE(fw) == ICON_OVERRIDE)
3484 ICON_DBG((stderr, "hpn: icon override '%s'\n", fw->name));
3485 has_icon_changed = False;
3487 else if (ICON_OVERRIDE_MODE(fw) ==
3488 NO_ACTIVE_ICON_OVERRIDE)
3490 if (has_icon_pixmap_hint_changed)
3492 if (WAS_ICON_HINT_PROVIDED(fw) ==
3493 ICON_HINT_MULTIPLE)
3495 ICON_DBG((stderr, "hpn: using further iph '%s'\n", fw->name));
3496 has_icon_changed = True;
3498 else if (fw->icon_bitmap_file ==
3499 NULL ||
3500 fw->icon_bitmap_file ==
3501 Scr.DefaultIcon)
3503 ICON_DBG((stderr, "hpn: using first iph '%s'\n", fw->name));
3504 has_icon_changed = True;
3506 else
3508 /* ignore the first icon pixmap
3509 * hint if the application did
3510 * not provide it from the
3511 * start */
3512 ICON_DBG((stderr, "hpn: first iph ignored '%s'\n", fw->name));
3513 has_icon_changed = False;
3516 else if (has_icon_window_hint_changed)
3518 ICON_DBG((stderr, "hpn: using iwh '%s'\n", fw->name));
3519 has_icon_changed = True;
3521 else
3523 ICON_DBG((stderr, "hpn: iwh not changed, hint ignored '%s'\n", fw->name));
3524 has_icon_changed = False;
3527 else /* NO_ICON_OVERRIDE */
3529 ICON_DBG((stderr, "hpn: using hint '%s'\n", fw->name));
3530 has_icon_changed = True;
3533 if (USE_EWMH_ICON(fw))
3535 has_icon_changed = False;
3538 if (has_icon_changed)
3540 ICON_DBG((stderr, "hpn: icon changed '%s'\n", fw->name));
3541 /* Okay, the icon hint has changed and style
3542 * options tell us to honour this change. Now
3543 * let's see if we have to use the application
3544 * provided pixmap or window (if any), the icon
3545 * file provided by the window's style or the
3546 * default style's icon. */
3547 if (fw->icon_bitmap_file == Scr.DefaultIcon)
3549 fw->icon_bitmap_file = NULL;
3551 if (!fw->icon_bitmap_file &&
3552 !(fw->wmhints->flags &
3553 (IconPixmapHint|IconWindowHint)))
3555 fw->icon_bitmap_file =
3556 (Scr.DefaultIcon) ?
3557 Scr.DefaultIcon : NULL;
3559 fw->iconPixmap = (Window)NULL;
3560 ChangeIconPixmap(fw);
3564 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3565 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3566 * Treat urgency changes by calling user-settable functions.
3567 * These could e.g. deiconify and raise the window or
3568 * temporarily change the decor. */
3569 if (!(old_wmhints_flags & XUrgencyHint) &&
3570 (fw->wmhints->flags & XUrgencyHint))
3572 urgency_action = "Function UrgencyFunc";
3574 if ((old_wmhints_flags & XUrgencyHint) &&
3575 !(fw->wmhints->flags & XUrgencyHint))
3577 urgency_action = "Function UrgencyDoneFunc";
3579 if (urgency_action)
3581 const exec_context_t *exc;
3582 exec_context_changes_t ecc;
3584 ecc.w.fw = fw;
3585 ecc.w.wcontext = C_WINDOW;
3586 exc = exc_clone_context(
3587 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
3588 execute_function(NULL, exc, urgency_action, 0);
3589 exc_destroy_context(exc);
3591 break;
3592 case XA_WM_NORMAL_HINTS:
3593 /* just mark wm normal hints as changed and look them up when
3594 * the next ConfigureRequest w/ x, y, width or height set
3595 * arrives. */
3596 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 1);
3597 break;
3598 default:
3599 if (te->xproperty.atom == _XA_WM_PROTOCOLS)
3601 FetchWmProtocols (fw);
3603 else if (te->xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
3605 FetchWmColormapWindows (fw); /* frees old data */
3606 ReInstallActiveColormap();
3608 else if (te->xproperty.atom == _XA_WM_STATE)
3610 if (fw && OnThisPage && focus_is_focused(fw) &&
3611 FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
3613 /* refresh the focus - why? */
3614 focus_force_refresh_focus(fw);
3617 else
3619 EWMH_ProcessPropertyNotify(ea->exc);
3621 break;
3625 void HandleReparentNotify(const evh_args_t *ea)
3627 const XEvent *te = ea->exc->x.etrigger;
3628 FvwmWindow * const fw = ea->exc->w.fw;
3630 if (!fw)
3632 return;
3634 if (te->xreparent.parent == Scr.Root)
3636 /* Ignore reparenting to the root window. In some cases these
3637 * events are selected although the window is no longer
3638 * managed. */
3639 return;
3641 if (te->xreparent.parent != FW_W_FRAME(fw))
3643 /* window was reparented by someone else, destroy the frame */
3644 SetMapStateProp(fw, WithdrawnState);
3645 EWMH_RestoreInitialStates(fw, te->type);
3646 if (!IS_TEAR_OFF_MENU(fw))
3648 XRemoveFromSaveSet(dpy, te->xreparent.window);
3649 XSelectInput(dpy, te->xreparent.window, NoEventMask);
3651 else
3653 XSelectInput(dpy, te->xreparent.window, XEVMASK_MENUW);
3655 discard_events(XEVMASK_FRAMEW);
3656 destroy_window(fw);
3657 EWMH_ManageKdeSysTray(te->xreparent.window, te->type);
3658 EWMH_WindowDestroyed();
3661 return;
3664 void HandleSelectionRequest(const evh_args_t *ea)
3666 icccm2_handle_selection_request(ea->exc->x.etrigger);
3668 return;
3671 void HandleSelectionClear(const evh_args_t *ea)
3673 icccm2_handle_selection_clear();
3675 return;
3678 void HandleShapeNotify(const evh_args_t *ea)
3680 FvwmWindow * const fw = ea->exc->w.fw;
3682 DBUG("HandleShapeNotify", "Routine Entered");
3684 if (FShapesSupported)
3686 const FShapeEvent *sev =
3687 (const FShapeEvent *)(ea->exc->x.etrigger);
3689 if (!fw)
3691 return;
3693 if (sev->kind != FShapeBounding)
3695 return;
3697 frame_setup_shape(
3698 fw, fw->g.frame.width, fw->g.frame.height, sev->shaped);
3699 GNOME_SetWinArea(fw);
3700 EWMH_SetFrameStrut(fw);
3701 if (!IS_ICONIFIED(fw))
3703 border_redraw_decorations(fw);
3707 return;
3710 void HandleUnmapNotify(const evh_args_t *ea)
3712 int dstx, dsty;
3713 Window dumwin;
3714 XEvent dummy;
3715 XEvent map_event;
3716 const XEvent *te = ea->exc->x.etrigger;
3717 int weMustUnmap;
3718 Bool focus_grabbed;
3719 Bool must_return = False;
3720 Bool do_map = False;
3721 FvwmWindow * const fw = ea->exc->w.fw;
3722 Window pw;
3723 Window cw;
3725 DBUG("HandleUnmapNotify", "Routine Entered");
3727 /* Don't ignore events as described below. */
3728 if (te->xunmap.event != te->xunmap.window &&
3729 (te->xunmap.event != Scr.Root || !te->xunmap.send_event))
3731 must_return = True;
3735 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3736 * to WithdrawnState should send a synthetic UnmapNotify with the
3737 * event field set to (pseudo-)root, in case the window is already
3738 * unmapped (which is the case for fvwm for IconicState).
3739 * Unfortunately, we looked for the FvwmContext using that field, so
3740 * try the window field also. */
3741 weMustUnmap = 0;
3742 if (!fw)
3744 weMustUnmap = 1;
3745 if (XFindContext(
3746 dpy, te->xunmap.window, FvwmContext,
3747 (caddr_t *)&fw) == XCNOENT)
3749 return;
3752 cw = FW_W(fw);
3753 pw = FW_W_PARENT(fw);
3754 if (te->xunmap.window == FW_W_FRAME(fw))
3756 SET_ICONIFY_PENDING(fw , 0);
3757 return;
3759 if (must_return)
3761 return;
3764 if (weMustUnmap)
3766 Bool is_map_request_pending;
3767 check_if_event_args args;
3769 args.w = te->xunmap.window;
3770 args.do_return_true = False;
3771 args.do_return_true_cr = False;
3772 /* Using FCheckTypedWindowEvent() does not work here. I don't
3773 * have the slightest idea why, but using FCheckIfEvent() with
3774 * the appropriate predicate procedure works fine. */
3775 FCheckIfEvent(dpy, &dummy, test_map_request, (XPointer)&args);
3776 /* Unfortunately, there is no procedure in X that simply tests
3777 * if an event of a certain type in on the queue without
3778 * waiting and without removing it from the queue.
3779 * XCheck...Event() does not wait but removes the event while
3780 * XPeek...() does not remove the event but waits. To solve
3781 * this, the predicate procedure sets a flag in the passed in
3782 * structure and returns False unconditionally. */
3783 is_map_request_pending = (args.ret_does_match == True);
3784 if (!is_map_request_pending)
3786 XUnmapWindow(dpy, te->xunmap.window);
3789 if (fw == Scr.Hilite)
3791 Scr.Hilite = NULL;
3793 focus_grabbed = focus_query_close_release_focus(fw);
3794 restore_focus_after_unmap(fw, False);
3795 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
3797 return;
3801 * The program may have unmapped the client window, from either
3802 * NormalState or IconicState. Handle the transition to WithdrawnState.
3804 * We need to reparent the window back to the root (so that fvwm exiting
3805 * won't cause it to get mapped) and then throw away all state (pretend
3806 * that we've received a DestroyNotify).
3808 if (!FCheckTypedWindowEvent(
3809 dpy, te->xunmap.window, DestroyNotify, &dummy) &&
3810 XTranslateCoordinates(
3811 dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
3812 &dumwin))
3814 MyXGrabServer(dpy);
3815 SetMapStateProp(fw, WithdrawnState);
3816 EWMH_RestoreInitialStates(fw, te->type);
3817 if (FCheckTypedWindowEvent(
3818 dpy, te->xunmap.window, ReparentNotify, &dummy))
3820 if (fw->attr_backup.border_width)
3822 XSetWindowBorderWidth(
3823 dpy, te->xunmap.window,
3824 fw->attr_backup.border_width);
3826 if ((!IS_ICON_SUPPRESSED(fw))&&
3827 (fw->wmhints &&
3828 (fw->wmhints->flags & IconWindowHint)))
3830 XUnmapWindow(dpy, fw->wmhints->icon_window);
3833 else
3835 RestoreWithdrawnLocation(fw, False, Scr.Root);
3837 if (!IS_TEAR_OFF_MENU(fw))
3839 XRemoveFromSaveSet(dpy, te->xunmap.window);
3840 XSelectInput(dpy, te->xunmap.window, NoEventMask);
3842 XSync(dpy, 0);
3843 MyXUngrabServer(dpy);
3844 if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
3846 /* the client tried to map the window again while it
3847 * was still inside the decoration windows */
3848 do_map = True;
3851 destroy_window(fw);
3852 if (focus_grabbed == True)
3854 CoerceEnterNotifyOnCurrentWindow();
3856 EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
3857 EWMH_WindowDestroyed();
3858 GNOME_SetClientList();
3859 if (do_map == True)
3861 map_event.xmaprequest.window = cw;
3862 map_event.xmaprequest.parent = Scr.Root;
3863 dispatch_event(&map_event);
3864 /* note: we really should handle all map and unmap notify
3865 * events for that window in a loop here */
3868 return;
3871 void HandleVisibilityNotify(const evh_args_t *ea)
3873 FvwmWindow * const fw = ea->exc->w.fw;
3875 DBUG("HandleVisibilityNotify", "Routine Entered");
3877 if (fw && ea->exc->x.etrigger->xvisibility.window == FW_W_FRAME(fw))
3879 switch (ea->exc->x.etrigger->xvisibility.state)
3881 case VisibilityUnobscured:
3882 SET_FULLY_VISIBLE(fw, 1);
3883 SET_PARTIALLY_VISIBLE(fw, 1);
3884 break;
3885 case VisibilityPartiallyObscured:
3886 SET_FULLY_VISIBLE(fw, 0);
3887 SET_PARTIALLY_VISIBLE(fw, 1);
3888 break;
3889 default:
3890 SET_FULLY_VISIBLE(fw, 0);
3891 SET_PARTIALLY_VISIBLE(fw, 0);
3892 break;
3894 /* Make sure the button grabs are up to date */
3895 focus_grab_buttons(fw);
3898 return;
3901 /* ---------------------------- interface functions ------------------------ */
3903 /* Inform a client window of its geometry.
3905 * The input (frame) geometry will be translated to client geometry
3906 * before sending. */
3907 void SendConfigureNotify(
3908 FvwmWindow *fw, int x, int y, int w, int h, int bw,
3909 Bool send_for_frame_too)
3911 XEvent client_event;
3912 size_borders b;
3914 if (!fw || IS_SHADED(fw))
3916 return;
3918 client_event.type = ConfigureNotify;
3919 client_event.xconfigure.display = dpy;
3920 client_event.xconfigure.event = FW_W(fw);
3921 client_event.xconfigure.window = FW_W(fw);
3922 get_window_borders(fw, &b);
3923 client_event.xconfigure.x = x + b.top_left.width;
3924 client_event.xconfigure.y = y + b.top_left.height;
3925 client_event.xconfigure.width = w - b.total_size.width;
3926 client_event.xconfigure.height = h - b.total_size.height;
3927 client_event.xconfigure.border_width = bw;
3928 client_event.xconfigure.above = FW_W_FRAME(fw);
3929 client_event.xconfigure.override_redirect = False;
3930 #if 0
3931 fprintf(stderr,
3932 "send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x '%s'\n",
3933 client_event.xconfigure.x, client_event.xconfigure.y,
3934 client_event.xconfigure.width, client_event.xconfigure.height,
3935 (int)FW_W_FRAME(fw), (int)FW_W(fw),
3936 (int)client_event.xconfigure.window,
3937 (fw->name.name) ? fw->name.name : "");
3938 #endif
3939 FSendEvent(
3940 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
3941 if (send_for_frame_too)
3943 /* This is for buggy tk, which waits for the real
3944 * ConfigureNotify on frame instead of the synthetic one on w.
3945 * The geometry data in the event will not be correct for the
3946 * frame, but tk doesn't look at that data anyway. */
3947 client_event.xconfigure.event = FW_W_FRAME(fw);
3948 client_event.xconfigure.window = FW_W_FRAME(fw);
3949 FSendEvent(
3950 dpy, FW_W_FRAME(fw), False, StructureNotifyMask,
3951 &client_event);
3954 return;
3957 /* Add an event group to the event handler */
3958 int register_event_group(int event_base, int event_count, PFEH *jump_table)
3960 /* insert into the list */
3961 event_group_t *group;
3962 event_group_t *position = base_event_group;
3963 event_group_t *prev_position = NULL;
3965 while (
3966 position != NULL &&
3967 position->base + position->count < event_base)
3969 prev_position = position;
3970 position = position->next;
3972 if ((position != NULL && position->base < event_base + event_count))
3974 /* there is already an event group registered at the specified
3975 * event range, or the base is before the base X events */
3977 return 1;
3979 /* create the group structure (these are not freed until fvwm exits) */
3980 group = (event_group_t*)safemalloc(sizeof(event_group_t));
3981 group->base = event_base;
3982 group->count = event_count;
3983 group->jump_table = jump_table;
3984 group->next = position;
3985 if (prev_position != NULL)
3987 prev_position->next = group;
3989 else
3991 base_event_group = group;
3994 return 0;
3998 ** Procedure:
3999 ** InitEventHandlerJumpTable
4001 void InitEventHandlerJumpTable(void)
4003 static PFEH EventHandlerJumpTable[LASTEvent];
4004 int i;
4006 for (i=0; i<LASTEvent; i++)
4008 EventHandlerJumpTable[i] = NULL;
4010 EventHandlerJumpTable[Expose] = HandleExpose;
4011 EventHandlerJumpTable[DestroyNotify] = HandleDestroyNotify;
4012 EventHandlerJumpTable[MapRequest] = HandleMapRequest;
4013 EventHandlerJumpTable[MapNotify] = HandleMapNotify;
4014 EventHandlerJumpTable[UnmapNotify] = HandleUnmapNotify;
4015 EventHandlerJumpTable[ButtonPress] = HandleButtonPress;
4016 EventHandlerJumpTable[EnterNotify] = HandleEnterNotify;
4017 EventHandlerJumpTable[LeaveNotify] = HandleLeaveNotify;
4018 EventHandlerJumpTable[FocusIn] = HandleFocusIn;
4019 EventHandlerJumpTable[FocusOut] = HandleFocusOut;
4020 EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
4021 EventHandlerJumpTable[ClientMessage] = HandleClientMessage;
4022 EventHandlerJumpTable[PropertyNotify] = HandlePropertyNotify;
4023 EventHandlerJumpTable[KeyPress] = HandleKeyPress;
4024 EventHandlerJumpTable[KeyRelease] = HandleKeyRelease;
4025 EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
4026 EventHandlerJumpTable[ColormapNotify] = HandleColormapNotify;
4027 EventHandlerJumpTable[SelectionClear] = HandleSelectionClear;
4028 EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
4029 EventHandlerJumpTable[ReparentNotify] = HandleReparentNotify;
4030 EventHandlerJumpTable[MappingNotify] = HandleMappingNotify;
4031 STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
4032 STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
4033 #ifdef MOUSE_DROPPINGS
4034 STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
4035 #else /* no MOUSE_DROPPINGS */
4036 STROKE_CODE(stroke_init());
4037 #endif /* MOUSE_DROPPINGS */
4038 if (register_event_group(0, LASTEvent, EventHandlerJumpTable))
4040 /* should never happen */
4041 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4042 "Faild to initialize event handlers");
4043 exit(1);
4045 if (FShapesSupported)
4047 static PFEH shape_jump_table[FShapeNumberEvents];
4049 for (i = 0; i < FShapeNumberEvents; i++)
4051 shape_jump_table[i] = NULL;
4053 shape_jump_table[FShapeNotify] = HandleShapeNotify;
4054 if (
4055 register_event_group(
4056 FShapeEventBase, FShapeNumberEvents,
4057 shape_jump_table))
4059 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4060 "Faild to init Shape event handler");
4065 return;
4068 /* handle a single X event */
4069 void dispatch_event(XEvent *e)
4071 Window w = e->xany.window;
4072 FvwmWindow *fw;
4073 event_group_t *event_group;
4075 DBUG("dispatch_event", "Routine Entered");
4077 XFlush(dpy);
4078 if (w == Scr.Root)
4080 switch (e->type)
4082 case ButtonPress:
4083 case ButtonRelease:
4084 if (e->xbutton.subwindow != None)
4086 w = e->xbutton.subwindow;
4088 case MapRequest:
4089 w = e->xmaprequest.window;
4090 break;
4091 default:
4092 break;
4095 if (w == Scr.Root ||
4096 XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4098 fw = NULL;
4100 last_event_type = e->type;
4101 event_group = base_event_group;
4102 while (
4103 event_group != NULL &&
4104 event_group->base + event_group->count < e->type)
4106 event_group = event_group->next;
4109 if (
4110 event_group != NULL &&
4111 e->type - event_group->base < event_group->count &&
4112 event_group->jump_table[e->type - event_group->base] != NULL)
4114 evh_args_t ea;
4115 exec_context_changes_t ecc;
4116 Window dummyw;
4118 ecc.type = EXCT_EVENT;
4119 ecc.x.etrigger = e;
4120 ecc.w.wcontext = GetContext(&fw, fw, e, &dummyw);
4121 ecc.w.w = w;
4122 ecc.w.fw = fw;
4123 ea.exc = exc_create_context(
4124 &ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W |
4125 ECC_WCONTEXT);
4126 (*event_group->jump_table[e->type - event_group->base])(&ea);
4127 exc_destroy_context(ea.exc);
4130 #ifdef C_ALLOCA
4131 /* If we're using the C version of alloca, see if anything needs to be
4132 * freed up.
4134 alloca(0);
4135 #endif
4136 DBUG("dispatch_event", "Leaving Routine");
4138 return;
4141 /* ewmh configure request */
4142 void events_handle_configure_request(
4143 XConfigureRequestEvent cre, FvwmWindow *fw, Bool force,
4144 int force_gravity)
4146 __handle_configure_request(cre, NULL, fw, force, force_gravity);
4148 return;
4151 void HandleEvents(void)
4153 XEvent ev;
4155 DBUG("HandleEvents", "Routine Entered");
4156 STROKE_CODE(send_motion = False);
4157 while (!isTerminated)
4159 last_event_type = 0;
4160 if (Scr.flags.is_window_scheduled_for_destroy)
4162 destroy_scheduled_windows();
4164 if (Scr.flags.do_need_window_update)
4166 flush_window_updates();
4168 if (My_XNextEvent(dpy, &ev))
4170 dispatch_event(&ev);
4172 if (Scr.flags.do_need_style_list_update)
4174 simplify_style_list();
4178 return;
4183 * Waits for next X or module event, fires off startup routines when startup
4184 * modules have finished or after a timeout if the user has specified a
4185 * command line module that doesn't quit or gets stuck.
4188 int My_XNextEvent(Display *dpy, XEvent *event)
4190 fd_set in_fdset, out_fdset;
4191 int num_fd;
4192 fmodule_list_itr moditr;
4193 fmodule *module;
4194 fmodule_input *input;
4195 static struct timeval timeout;
4196 static struct timeval *timeoutP = &timeout;
4198 DBUG("My_XNextEvent", "Routine Entered");
4200 /* check for any X events already queued up.
4201 * Side effect: this does an XFlush if no events are queued
4202 * Make sure nothing between here and the select causes further
4203 * X requests to be sent or the select may block even though
4204 * there are events in the queue */
4205 if (FPending(dpy))
4207 DBUG(
4208 "My_XNextEvent", "taking care of queued up events"
4209 " & returning (1)");
4210 FNextEvent(dpy, event);
4211 return 1;
4214 /* check for termination of all startup modules */
4215 if (fFvwmInStartup)
4217 module_list_itr_init(&moditr);
4218 module = module_list_itr_next(&moditr);
4219 for (; module != NULL; module = module_list_itr_next(&moditr))
4221 if (MOD_IS_CMDLINE(module) == 1)
4223 break;
4226 module_cleanup();
4227 if (module == NULL)
4229 /* last module */
4230 DBUG(
4231 "My_XNextEvent",
4232 "Starting up after command lines modules");
4233 /* set an infinite timeout to stop ticking */
4234 timeoutP = NULL;
4235 /* This may cause X requests to be sent */
4236 StartupStuff();
4238 return 0; /* so return without select()ing */
4242 /* Some signals can interrupt us while we wait for any action
4243 * on our descriptors. While some of these signals may be asking
4244 * fvwm to die, some might be harmless. Harmless interruptions
4245 * mean we have to start waiting all over again ... */
4248 int ms;
4249 Bool is_waiting_for_scheduled_command = False;
4250 static struct timeval *old_timeoutP = NULL;
4252 /* The timeouts become undefined whenever the select returns,
4253 * and so we have to reinitialise them */
4254 ms = squeue_get_next_ms();
4255 if (ms == 0)
4257 /* run scheduled commands */
4258 squeue_execute();
4259 ms = squeue_get_next_ms();
4260 /* should not happen anyway.
4261 * get_next_schedule_queue_ms() can't return 0 after a
4262 * call to execute_schedule_queue(). */
4263 if (ms == 0)
4265 ms = 1;
4268 if (ms < 0)
4270 timeout.tv_sec = 42;
4271 timeout.tv_usec = 0;
4273 else
4275 /* scheduled commands are pending - don't wait too
4276 * long */
4277 timeout.tv_sec = ms / 1000;
4278 timeout.tv_usec = 1000 * (ms % 1000);
4279 old_timeoutP = timeoutP;
4280 timeoutP = &timeout;
4281 is_waiting_for_scheduled_command = True;
4284 FD_ZERO(&in_fdset);
4285 FD_ZERO(&out_fdset);
4286 FD_SET(x_fd, &in_fdset);
4288 /* nothing is done here if fvwm was compiled without session
4289 * support */
4290 if (sm_fd >= 0)
4292 FD_SET(sm_fd, &in_fdset);
4295 module_list_itr_init(&moditr);
4296 while ( (module = module_list_itr_next(&moditr)) != NULL)
4298 FD_SET(MOD_READFD(module), &in_fdset);
4300 if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
4302 FD_SET(MOD_WRITEFD(module), &out_fdset);
4306 DBUG("My_XNextEvent", "waiting for module input/output");
4307 num_fd = fvwmSelect(
4308 fvwmlib_max_fd, &in_fdset, &out_fdset, 0, timeoutP);
4309 if (is_waiting_for_scheduled_command)
4311 timeoutP = old_timeoutP;
4314 /* Express route out of fvwm ... */
4315 if (isTerminated)
4317 return 0;
4319 } while (num_fd < 0);
4321 if (num_fd > 0)
4323 /* Check for module input. */
4324 module_list_itr_init(&moditr);
4325 while ( (module = module_list_itr_next(&moditr)) != NULL)
4327 if (FD_ISSET(MOD_READFD(module), &in_fdset))
4329 input = module_receive(module);
4330 /* enqueue the received command */
4331 module_input_enqueue(input);
4333 if (
4334 MOD_WRITEFD(module) >= 0 &&
4335 FD_ISSET(MOD_WRITEFD(module), &out_fdset))
4337 DBUG("My_XNextEvent",
4338 "calling FlushMessageQueue");
4339 FlushMessageQueue(module);
4343 /* execute any commands queued up */
4344 DBUG("My_XNextEvent", "executing module comand queue");
4345 ExecuteCommandQueue();
4347 /* cleanup dead modules */
4348 module_cleanup();
4350 /* nothing is done here if fvwm was compiled without session
4351 * support */
4352 if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
4354 ProcessICEMsgs();
4358 else
4360 /* select has timed out, things must have calmed down so let's
4361 * decorate */
4362 if (fFvwmInStartup)
4364 fvwm_msg(ERR, "My_XNextEvent",
4365 "Some command line modules have not quit, "
4366 "Starting up after timeout.\n");
4367 StartupStuff();
4368 timeoutP = NULL; /* set an infinite timeout to stop
4369 * ticking */
4370 reset_style_changes();
4371 Scr.flags.do_need_window_update = 0;
4373 /* run scheduled commands if necessary */
4374 squeue_execute();
4377 /* check for X events again, rather than return 0 and get called again
4379 if (FPending(dpy))
4381 DBUG("My_XNextEvent",
4382 "taking care of queued up events & returning (2)");
4383 FNextEvent(dpy,event);
4384 return 1;
4387 DBUG("My_XNextEvent", "leaving My_XNextEvent");
4388 return 0;
4393 * Procedure:
4394 * Find the Fvwm context for the event.
4397 int GetContext(FvwmWindow **ret_fw, FvwmWindow *t, const XEvent *e, Window *w)
4399 int context;
4400 Window win;
4401 Window subw = None;
4402 int x = 0;
4403 int y = 0;
4404 Bool is_key_event = False;
4406 win = e->xany.window;
4407 context = C_NO_CONTEXT;
4408 switch (e->type)
4410 case KeyPress:
4411 case KeyRelease:
4412 x = e->xkey.x;
4413 y = e->xkey.y;
4414 subw = e->xkey.subwindow;
4415 if (win == Scr.Root && subw != None)
4417 /* Translate root coordinates into subwindow
4418 * coordinates. Necessary for key bindings that work
4419 * over unfocused windows. */
4420 win = subw;
4421 XTranslateCoordinates(
4422 dpy, Scr.Root, subw, x, y, &x, &y, &subw);
4423 XFindContext(dpy, win, FvwmContext, (caddr_t *) &t);
4425 is_key_event = True;
4426 /* fall through */
4427 case ButtonPress:
4428 case ButtonRelease:
4429 if (!is_key_event)
4431 x = e->xbutton.x;
4432 y = e->xbutton.y;
4433 subw = e->xbutton.subwindow;
4435 if (t && win == FW_W_FRAME(t) && subw != None)
4437 /* Translate frame coordinates into subwindow
4438 * coordinates. */
4439 win = subw;
4440 XTranslateCoordinates(
4441 dpy, FW_W_FRAME(t), subw, x, y, &x, &y, &subw);
4442 if (win == FW_W_PARENT(t))
4444 win = subw;
4445 XTranslateCoordinates(
4446 dpy, FW_W_PARENT(t), subw, x, y, &x,
4447 &y, &subw);
4450 break;
4451 default:
4452 XFindContext(dpy, win, FvwmContext, (caddr_t *)&t);
4453 break;
4455 if (ret_fw != NULL)
4457 *ret_fw = t;
4459 if (!t)
4461 return C_ROOT;
4463 *w = win;
4464 if (*w == Scr.NoFocusWin)
4466 return C_ROOT;
4468 if (subw != None)
4470 if (win == FW_W_PARENT(t))
4472 *w = subw;
4475 if (*w == Scr.Root)
4477 return C_ROOT;
4479 context = frame_window_id_to_context(t, *w, &Button);
4481 return context;
4486 * Removes expose events for a specific window from the queue
4489 int flush_expose(Window w)
4491 XEvent dummy;
4492 int i=0;
4494 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4496 i++;
4499 return i;
4502 /* same as above, but merges the expose rectangles into a single big one */
4503 int flush_accumulate_expose(Window w, XEvent *e)
4505 XEvent dummy;
4506 int i = 0;
4507 int x1 = e->xexpose.x;
4508 int y1 = e->xexpose.y;
4509 int x2 = x1 + e->xexpose.width;
4510 int y2 = y1 + e->xexpose.height;
4512 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4514 x1 = min(x1, dummy.xexpose.x);
4515 y1 = min(y1, dummy.xexpose.y);
4516 x2 = max(x2, dummy.xexpose.x + dummy.xexpose.width);
4517 y2 = max(y2, dummy.xexpose.y + dummy.xexpose.height);
4518 i++;
4520 e->xexpose.x = x1;
4521 e->xexpose.y = y1;
4522 e->xexpose.width = x2 - x1;
4523 e->xexpose.height = y2 - y1;
4525 return i;
4530 * Removes all expose events from the queue and does the necessary redraws
4533 void handle_all_expose(void)
4535 void *saved_event;
4536 XEvent evdummy;
4538 saved_event = fev_save_event();
4539 FPending(dpy);
4540 while (FCheckMaskEvent(dpy, ExposureMask, &evdummy))
4542 dispatch_event(&evdummy);
4544 fev_restore_event(saved_event);
4546 return;
4549 /* CoerceEnterNotifyOnCurrentWindow()
4550 * Pretends to get a HandleEnterNotify on the window that the pointer
4551 * currently is in so that the focus gets set correctly from the beginning.
4552 * Note that this presently only works if the current window is not
4553 * click_to_focus; I think that that behaviour is correct and desirable.
4554 * --11/08/97 gjb */
4555 void CoerceEnterNotifyOnCurrentWindow(void)
4557 Window child;
4558 Window root;
4559 Bool f;
4560 evh_args_t ea;
4561 exec_context_changes_t ecc;
4562 XEvent e;
4563 FvwmWindow *fw;
4565 f = FQueryPointer(
4566 dpy, Scr.Root, &root, &child, &e.xcrossing.x_root,
4567 &e.xcrossing.y_root, &e.xcrossing.x, &e.xcrossing.y,
4568 &JunkMask);
4569 if (f == False || child == None)
4571 return;
4573 e.xcrossing.type = EnterNotify;
4574 e.xcrossing.window = child;
4575 e.xcrossing.subwindow = None;
4576 e.xcrossing.mode = NotifyNormal;
4577 e.xcrossing.detail = NotifyAncestor;
4578 e.xcrossing.same_screen = True;
4579 if (XFindContext(dpy, child, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4581 fw = NULL;
4583 else
4585 XTranslateCoordinates(
4586 dpy, Scr.Root, child, e.xcrossing.x_root,
4587 e.xcrossing.y_root, &JunkX, &JunkY, &child);
4588 if (child == FW_W_PARENT(fw))
4590 child = FW_W(fw);
4592 if (child != None)
4594 e.xany.window = child;
4597 e.xcrossing.focus = (fw == get_focus_window()) ? True : False;
4598 ecc.type = EXCT_NULL;
4599 ecc.x.etrigger = &e;
4600 ea.exc = exc_create_context(&ecc, ECC_TYPE | ECC_ETRIGGER);
4601 HandleEnterNotify(&ea);
4602 exc_destroy_context(ea.exc);
4604 return;
4607 /* This function discards all queued up ButtonPress, ButtonRelease and
4608 * ButtonMotion events. */
4609 int discard_events(long event_mask)
4611 XEvent e;
4612 int count;
4614 XSync(dpy, 0);
4615 for (count = 0; FCheckMaskEvent(dpy, event_mask, &e); count++)
4617 /* nothing */
4620 return count;
4623 /* This function discards all queued up ButtonPress, ButtonRelease and
4624 * ButtonMotion events. */
4625 int discard_window_events(Window w, long event_mask)
4627 XEvent e;
4628 int count;
4630 XSync(dpy, 0);
4631 for (count = 0; FCheckWindowEvent(dpy, w, event_mask, &e); count++)
4633 /* nothing */
4636 return count;
4639 /* Similar function for certain types of PropertyNotify. */
4640 int flush_property_notify(Atom atom, Window w)
4642 XEvent e;
4643 int count;
4644 test_typed_window_event_args args;
4646 count = 0;
4647 XSync(dpy, 0);
4648 args.w = w;
4649 args.atom = atom;
4650 args.event_type = PropertyNotify;
4652 /* Get rid of the events. */
4653 while (FCheckIfEvent(dpy, &e, test_typed_window_event, (XPointer)&args))
4654 count++;
4656 return count;
4659 /* Wait for all mouse buttons to be released
4660 * This can ease some confusion on the part of the user sometimes
4662 * Discard superflous button events during this wait period. */
4663 void WaitForButtonsUp(Bool do_handle_expose)
4665 unsigned int mask;
4666 unsigned int bmask;
4667 long evmask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
4668 KeyPressMask|KeyReleaseMask;
4669 int count;
4670 int use_wait_cursor;
4671 XEvent e;
4673 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
4674 &JunkX, &JunkY, &mask) == False)
4676 /* pointer is on a different screen - that's okay here */
4678 mask &= DEFAULT_ALL_BUTTONS_MASK;
4679 if (mask == 0)
4681 return;
4683 if (do_handle_expose)
4685 evmask |= ExposureMask;
4687 GrabEm(None, GRAB_NORMAL);
4688 for (count = 0, use_wait_cursor = 0; mask != 0; count++)
4690 /* handle expose events */
4691 XAllowEvents(dpy, SyncPointer, CurrentTime);
4692 if (FCheckMaskEvent(dpy, evmask, &e))
4694 switch (e.type)
4696 case ButtonRelease:
4697 if (e.xbutton.button <=
4698 NUMBER_OF_MOUSE_BUTTONS)
4700 bmask = (Button1Mask <<
4701 (e.xbutton.button - 1));
4702 mask = e.xbutton.state & ~bmask;
4704 break;
4705 case Expose:
4706 dispatch_event(&e);
4707 break;
4708 default:
4709 break;
4712 else
4714 if (FQueryPointer(
4715 dpy, Scr.Root, &JunkRoot, &JunkChild,
4716 &JunkX, &JunkY, &JunkX, &JunkY, &mask) ==
4717 False)
4719 /* pointer is on a different screen - that's
4720 * okay here */
4722 mask &= DEFAULT_ALL_BUTTONS_MASK;
4723 usleep(1);
4725 if (use_wait_cursor == 0 && count == 20)
4727 GrabEm(CRS_WAIT, GRAB_NORMAL);
4728 use_wait_cursor = 1;
4731 UngrabEm(GRAB_NORMAL);
4732 if (use_wait_cursor)
4734 UngrabEm(GRAB_NORMAL);
4735 XFlush(dpy);
4738 return;
4741 void sync_server(int toggle)
4743 static Bool synced = False;
4745 if (toggle == -1)
4747 toggle = (synced == False);
4749 if (toggle == 1)
4751 synced = True;
4753 else
4755 synced = False;
4757 XSynchronize(dpy, synced);
4758 XFlush(dpy);
4760 return;
4763 Bool is_resizing_event_pending(
4764 FvwmWindow *fw)
4766 XEvent e;
4767 check_if_event_args args;
4769 args.w = FW_W(fw);
4770 args.do_return_true = False;
4771 args.do_return_true_cr = False;
4772 args.cr_value_mask = 0;
4773 args.ret_does_match = False;
4774 args.ret_type = 0;
4775 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
4777 return args.ret_does_match;
4780 /* ---------------------------- builtin commands --------------------------- */
4782 void CMD_XSynchronize(F_CMD_ARGS)
4784 int toggle;
4786 toggle = ParseToggleArgument(action, NULL, -1, 0);
4787 sync_server(toggle);
4789 return;
4792 void CMD_XSync(F_CMD_ARGS)
4794 XSync(dpy, 0);
4796 return;