Fix rendering of FvwmForm windows when initially mapped.
[fvwm.git] / fvwm / events.c
blobb89efae9e2c5f4e718ec58fad77edd59a256b619
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 /* This module is based on Twm, but has been siginificantly modified
17 * by Rob Nation
20 * Copyright 1988 by Evans & Sutherland Computer Corporation,
21 * Salt Lake City, Utah
22 * Portions Copyright 1989 by the Massachusetts Institute of Technology
23 * Cambridge, Massachusetts
25 * All Rights Reserved
27 * Permission to use, copy, modify, and distribute this software and
28 * its documentation for any purpose and without fee is hereby
29 * granted, provided that the above copyright notice appear in all
30 * copies and that both that copyright notice and this permis-
31 * sion notice appear in supporting documentation, and that the
32 * names of Evans & Sutherland and M.I.T. not be used in advertising
33 * in publicity pertaining to distribution of the software without
34 * specific, written prior permission.
36 * EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD
37 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
38 * ABILITY AND FITNESS, IN NO EVENT SHALL EVANS & SUTHERLAND OR
39 * M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAM-
40 * AGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
41 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
42 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
43 * OR PERFORMANCE OF THIS SOFTWARE.
46 /* ---------------------------- included header files ---------------------- */
48 #include "config.h"
50 #if HAVE_SYS_BSDTYPES_H
51 #include <sys/bsdtypes.h>
52 #endif
54 #include <stdio.h>
55 #include <unistd.h>
56 #include <X11/Xatom.h>
58 #include "libs/ftime.h"
59 #include "libs/fvwmlib.h"
60 #include "libs/System.h"
61 #include "libs/Grab.h"
62 #include "libs/Parse.h"
63 #include "libs/ColorUtils.h"
64 #include "libs/FShape.h"
65 #include "libs/PictureBase.h"
66 #include "libs/Colorset.h"
67 #include "libs/charmap.h"
68 #include "libs/wcontext.h"
69 #include "fvwm.h"
70 #include "externs.h"
71 #include "cursor.h"
72 #include "functions.h"
73 #include "commands.h"
74 #include "bindings.h"
75 #include "misc.h"
76 #include "screen.h"
77 #include "events.h"
78 #include "eventhandler.h"
79 #include "eventmask.h"
80 #include "libs/fvwmsignal.h"
81 #include "module_list.h"
82 #include "module_interface.h"
83 #include "session.h"
84 #include "borders.h"
85 #include "frame.h"
86 #include "add_window.h"
87 #include "icccm2.h"
88 #include "icons.h"
89 #include "gnome.h"
90 #include "ewmh.h"
91 #include "update.h"
92 #include "style.h"
93 #include "stack.h"
94 #include "geometry.h"
95 #include "focus.h"
96 #include "virtual.h"
97 #include "decorations.h"
98 #include "schedule.h"
99 #include "menus.h"
100 #include "colormaps.h"
101 #include "colorset.h"
102 #ifdef HAVE_STROKE
103 #include "stroke.h"
104 #endif /* HAVE_STROKE */
106 /* ---------------------------- local definitions -------------------------- */
108 #ifndef XUrgencyHint
109 #define XUrgencyHint (1L << 8)
110 #endif
112 #define CR_MOVERESIZE_MASK (CWX | CWY | CWWidth | CWHeight | CWBorderWidth)
114 /* ---------------------------- local macros ------------------------------- */
116 /* ---------------------------- imports ------------------------------------ */
118 extern void StartupStuff(void);
120 /* ---------------------------- included code files ------------------------ */
122 /* ---------------------------- local types -------------------------------- */
124 typedef void (*PFEH)(const evh_args_t *ea);
126 typedef struct
128 Window w;
129 Bool do_return_true;
130 Bool do_return_true_cr;
131 unsigned long cr_value_mask;
132 Bool ret_does_match;
133 unsigned long ret_type;
134 } check_if_event_args;
136 typedef struct
138 unsigned do_forbid_function : 1;
139 unsigned do_focus : 1;
140 unsigned do_swallow_click : 1;
141 unsigned do_raise : 1;
142 } hfrc_ret_t;
144 typedef struct event_group
146 int base;
147 int count;
148 PFEH *jump_table;
149 struct event_group *next;
150 } event_group_t;
152 /* ---------------------------- forward declarations ----------------------- */
154 /* ---------------------------- local variables ---------------------------- */
156 static int Button = 0;
157 static const FvwmWindow *xcrossing_last_grab_window = NULL;
158 STROKE_CODE(static int send_motion);
159 STROKE_CODE(static char sequence[STROKE_MAX_SEQUENCE + 1]);
160 static event_group_t *base_event_group = NULL;
162 /* ---------------------------- exported variables (globals) --------------- */
164 int last_event_type = 0;
165 Window PressedW = None;
167 /* ---------------------------- local functions ---------------------------- */
169 static void fake_map_unmap_notify(const FvwmWindow *fw, int event_type)
171 XEvent client_event;
172 XWindowAttributes winattrs = {0};
174 if (!XGetWindowAttributes(dpy, FW_W(fw), &winattrs))
176 return;
178 XSelectInput(
179 dpy, FW_W(fw),
180 winattrs.your_event_mask & ~StructureNotifyMask);
181 client_event.type = event_type;
182 client_event.xmap.display = dpy;
183 client_event.xmap.event = FW_W(fw);
184 client_event.xmap.window = FW_W(fw);
185 switch (event_type)
187 case MapNotify:
188 client_event.xmap.override_redirect = False;
189 break;
190 case UnmapNotify:
191 client_event.xunmap.from_configure = False;
192 break;
193 default:
194 /* not possible if called correctly */
195 break;
197 FSendEvent(
198 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
199 XSelectInput(dpy, FW_W(fw), winattrs.your_event_mask);
200 XFlush(dpy);
202 return;
205 static Bool test_map_request(
206 Display *display, XEvent *event, XPointer arg)
208 check_if_event_args *cie_args;
209 Bool rc;
211 cie_args = (check_if_event_args *)arg;
212 cie_args->ret_does_match = False;
213 if (event->type == MapRequest &&
214 event->xmaprequest.window == cie_args->w)
216 cie_args->ret_type = MapRequest;
217 cie_args->ret_does_match = True;
218 rc = cie_args->do_return_true;
220 else
222 cie_args->ret_type = 0;
223 rc = False;
226 /* Yes, it is correct that this function always returns False. */
227 return rc;
230 /* Test for ICCCM2 withdraw requests by syntetic events on the root window */
231 static Bool test_withdraw_request(
232 Display *display, XEvent *event, XPointer arg)
234 check_if_event_args *cie_args;
235 Bool rc;
237 cie_args = (check_if_event_args *)arg;
238 cie_args->ret_does_match = False;
239 if (event->type == UnmapNotify &&
240 event->xunmap.window == cie_args->w &&
241 event->xany.send_event == True &&
242 event->xunmap.event == FW_W(&Scr.FvwmRoot))
244 cie_args->ret_type = UnmapNotify;
245 cie_args->ret_does_match = True;
246 rc = cie_args->do_return_true;
248 else
250 cie_args->ret_type = 0;
251 rc = False;
254 return rc;
257 Bool test_button_event(
258 Display *display, XEvent *event, XPointer arg)
260 if (event->type == ButtonPress || event->type == ButtonRelease)
262 return True;
265 return False;
268 Bool test_typed_window_event(
269 Display *display, XEvent *event, XPointer arg)
271 test_typed_window_event_args *ta = (test_typed_window_event_args *)arg;
273 if (event->xany.window == ta->w &&
274 event->xany.type == ta->event_type)
276 return True;
279 return False;
282 static Bool test_resizing_event(
283 Display *display, XEvent *event, XPointer arg)
285 check_if_event_args *cie_args;
286 Bool rc;
288 cie_args = (check_if_event_args *)arg;
289 cie_args->ret_does_match = False;
290 if (event->xany.window != cie_args->w)
292 return False;
294 rc = False;
295 switch (event->type)
297 case ConfigureRequest:
298 if ((event->xconfigurerequest.value_mask &
299 cie_args->cr_value_mask) != 0)
301 cie_args->ret_type = ConfigureRequest;
302 cie_args->ret_does_match = True;
303 rc = cie_args->do_return_true_cr;
305 break;
306 case PropertyNotify:
307 if (event->xproperty.atom == XA_WM_NORMAL_HINTS)
309 cie_args->ret_type = PropertyNotify;
310 cie_args->ret_does_match = True;
311 rc = cie_args->do_return_true;
313 default:
314 break;
317 /* Yes, it is correct that this function may always returns False. */
318 return rc;
321 static inline void __handle_cr_on_unmanaged(XConfigureRequestEvent *cre)
323 XWindowChanges xwc;
324 unsigned long xwcm;
326 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
327 xwc.x = cre->x;
328 xwc.y = cre->y;
329 xwc.width = cre->width;
330 xwc.height = cre->height;
331 xwc.border_width = cre->border_width;
332 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
334 return;
337 static inline void __handle_cr_on_icon(
338 XConfigureRequestEvent *cre, FvwmWindow *fw)
340 XWindowChanges xwc;
341 unsigned long xwcm;
343 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
344 xwc.x = cre->x;
345 xwc.y = cre->y;
346 xwc.width = cre->width;
347 xwc.height = cre->height;
348 xwc.border_width = cre->border_width;
349 if (FW_W_ICON_PIXMAP(fw) == cre->window)
351 int bw;
353 if (cre->value_mask & CWBorderWidth)
355 fw->icon_border_width = cre->border_width;
357 bw = fw->icon_border_width;
358 if ((cre->value_mask & (CWWidth | CWHeight)) ==
359 (CWWidth | CWHeight))
361 set_icon_picture_size(
362 fw, cre->width + 2 * bw, cre->height + 2 * bw);
365 set_icon_position(fw, cre->x, cre->y);
366 broadcast_icon_geometry(fw, False);
367 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
368 if (cre->window != FW_W_ICON_PIXMAP(fw) &&
369 FW_W_ICON_PIXMAP(fw) != None)
371 rectangle g;
373 get_icon_picture_geometry(fw, &g);
374 xwc.x = g.x;
375 xwc.y = g.y;
376 xwcm = cre->value_mask & (CWX | CWY);
377 XConfigureWindow(
378 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
380 if (FW_W_ICON_TITLE(fw) != None)
382 rectangle g;
384 get_icon_title_geometry(fw, &g);
385 xwc.x = g.x;
386 xwc.y = g.y;
387 xwcm = cre->value_mask & (CWX | CWY);
388 XConfigureWindow(
389 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
392 return;
395 static inline void __handle_cr_on_shaped(FvwmWindow *fw)
397 /* suppress compiler warnings w/o shape extension */
398 int i = 0;
399 unsigned int u = 0;
400 Bool b = False;
401 int boundingShaped;
403 if (FShapeQueryExtents(
404 dpy, FW_W(fw), &boundingShaped, &i, &i, &u, &u, &b,
405 &i, &i, &u, &u))
407 fw->wShaped = boundingShaped;
409 else
411 fw->wShaped = 0;
414 return;
417 static inline void __handle_cr_restack(
418 int *ret_do_send_event, XConfigureRequestEvent *cre, FvwmWindow *fw)
420 XWindowChanges xwc;
421 unsigned long xwcm;
422 FvwmWindow *fw2 = NULL;
424 if (cre->value_mask & CWSibling)
426 if (XFindContext(
427 dpy, cre->above, FvwmContext,
428 (caddr_t *)&fw2) == XCNOENT)
430 fw2 = NULL;
432 if (fw2 == fw)
434 fw2 = NULL;
437 if (cre->detail != Above && cre->detail != Below)
439 HandleUnusualStackmodes(
440 cre->detail, fw, cre->window, fw2, cre->above);
442 /* only allow clients to restack windows within their layer */
443 else if (fw2 == NULL || compare_window_layers(fw2, fw) != 0)
445 switch (cre->detail)
447 case Above:
448 RaiseWindow(fw, True);
449 break;
450 case Below:
451 LowerWindow(fw, True);
452 break;
455 else
457 xwc.sibling = FW_W_FRAME(fw2);
458 xwc.stack_mode = cre->detail;
459 xwcm = CWSibling | CWStackMode;
460 XConfigureWindow(dpy, FW_W_FRAME(fw), xwcm, &xwc);
462 /* Maintain the condition that icon windows are stacked
463 * immediately below their frame
464 * 1. for fw */
465 xwc.sibling = FW_W_FRAME(fw);
466 xwc.stack_mode = Below;
467 xwcm = CWSibling | CWStackMode;
468 if (FW_W_ICON_TITLE(fw) != None)
470 XConfigureWindow(
471 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
473 if (FW_W_ICON_PIXMAP(fw) != None)
475 XConfigureWindow(
476 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
478 /* 2. for fw2 */
479 if (cre->detail == Below)
481 xwc.sibling = FW_W_FRAME(fw2);
482 xwc.stack_mode = Below;
483 xwcm = CWSibling | CWStackMode;
484 if (FW_W_ICON_TITLE(fw2) != None)
486 XConfigureWindow(
487 dpy, FW_W_ICON_TITLE(fw2), xwcm, &xwc);
489 if (FW_W_ICON_PIXMAP(fw2) != None)
491 XConfigureWindow(
492 dpy, FW_W_ICON_PIXMAP(fw2), xwcm,
493 &xwc);
496 /* Maintain the stacking order ring */
497 if (cre->detail == Above)
499 remove_window_from_stack_ring(fw);
500 add_window_to_stack_ring_after(
501 fw, get_prev_window_in_stack_ring(fw2));
503 else /* cre->detail == Below */
505 remove_window_from_stack_ring(fw);
506 add_window_to_stack_ring_after(fw, fw2);
508 BroadcastRestackThisWindow(fw);
510 /* srt (28-Apr-2001): Tk needs a ConfigureNotify event after a
511 * raise, otherwise it would hang for two seconds */
512 *ret_do_send_event = 1;
514 return;
517 static inline void __cr_get_static_position(
518 rectangle *ret_g, FvwmWindow *fw, XConfigureRequestEvent *cre,
519 size_borders *b)
521 if (cre->value_mask & CWX)
523 ret_g->x = cre->x - b->top_left.width;
525 else
527 ret_g->x = fw->g.frame.x;
529 if (cre->value_mask & CWY)
531 ret_g->y = cre->y - b->top_left.height;
533 else
535 ret_g->y = fw->g.frame.y;
538 return;
541 static inline void __cr_get_grav_position(
542 rectangle *ret_g, FvwmWindow *fw, XConfigureRequestEvent *cre,
543 size_borders *b)
545 int grav_x;
546 int grav_y;
548 gravity_get_offsets(fw->hints.win_gravity, &grav_x, &grav_y);
549 if (cre->value_mask & CWX)
551 ret_g->x = cre->x - ((grav_x + 1) * b->total_size.width) / 2;
553 else
555 ret_g->x = fw->g.frame.x;
557 if (cre->value_mask & CWY)
559 ret_g->y = cre->y - ((grav_y + 1) * b->total_size.height) / 2;
561 else
563 ret_g->y = fw->g.frame.y;
566 return;
569 /* Try to detect whether the application uses the ICCCM way of moving its
570 * window or the traditional way, always assuming StaticGravity. */
571 static inline void __cr_detect_icccm_move(
572 FvwmWindow *fw, XConfigureRequestEvent *cre, size_borders *b)
574 rectangle grav_g;
575 rectangle static_g;
576 rectangle dg_g;
577 rectangle ds_g;
578 int mx;
579 int my;
580 int m;
581 int w;
582 int h;
583 int has_x;
584 int has_y;
586 if (CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
588 if (Scr.bo.do_debug_cr_motion_method == 1)
590 fprintf(
591 stderr,
592 "_cdim: --- already detected (pid %d) %p"
593 " '%s'\n", HAS_EWMH_WM_PID(fw), fw,
594 fw->visible_name);
596 return;
598 if (HAS_EWMH_WM_PID(fw))
600 if (Scr.bo.do_debug_cr_motion_method == 1)
602 fprintf(
603 stderr,"_cdim: +++ has ewmh_wm_pid: icccm"
604 " %p '%s'\n", fw, fw->visible_name);
606 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
607 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
608 return;
610 if (fw->ewmh_window_type != EWMH_WINDOW_TYPE_NONE_ID)
612 if (Scr.bo.do_debug_cr_motion_method == 1)
614 fprintf(
615 stderr, "_cdim: +++ has ewmh_window_type:"
616 " icccm %p '%s'\n", fw,
617 fw->visible_name);
619 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
620 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
621 return;
623 if (FShapesSupported && fw->wShaped)
625 if (Scr.bo.do_debug_cr_motion_method == 1)
627 fprintf(
628 stderr, "_cdim: --- shaped window %p "
629 "'%s'\n", fw, fw->visible_name);
631 /* no detection for shaped windows */
632 return;
634 if (fw->hints.win_gravity == StaticGravity)
636 if (Scr.bo.do_debug_cr_motion_method == 1)
638 fprintf(
639 stderr, "_cdim: --- using StaticGravity"
640 " %p '%s'\n", fw, fw->visible_name);
642 return;
644 has_x = (cre->value_mask & CWX);
645 has_y = (cre->value_mask & CWY);
646 if (!has_x && !has_y)
648 if (Scr.bo.do_debug_cr_motion_method == 1)
650 fprintf(
651 stderr, "_cdim: --- not moved %p '%s'\n",
652 fw, fw->visible_name);
654 return;
656 __cr_get_grav_position(&grav_g, fw, cre, b);
657 __cr_get_static_position(&static_g, fw, cre, b);
658 if (static_g.x == grav_g.x)
660 /* both methods have the same result; ignore */
661 has_x = 0;
663 if (static_g.y == grav_g.y)
665 /* both methods have the same result; ignore */
666 has_y = 0;
668 if (!has_x && !has_y)
670 if (Scr.bo.do_debug_cr_motion_method == 1)
672 fprintf(
673 stderr, "_cdim: --- not moved %p '%s'\n",
674 fw, fw->visible_name);
676 return;
678 dg_g.x = grav_g.x - fw->g.frame.x;
679 dg_g.y = grav_g.y - fw->g.frame.y;
680 ds_g.x = static_g.x - fw->g.frame.x;
681 ds_g.y = static_g.y - fw->g.frame.y;
682 if (Scr.bo.do_debug_cr_motion_method == 1)
684 fprintf(
685 stderr, "s %3d/%3d %2d/%2d, g %3d/%3d %2d/%2d: ",
686 static_g.x, static_g.y, ds_g.x, ds_g.y, grav_g.x,
687 grav_g.y, dg_g.x, dg_g.y);
689 /* check full screen */
690 if ((cre->value_mask & (CWX | CWY)) == (CWX | CWY) &&
691 (has_x || has_y) &&
692 cre->width == Scr.MyDisplayWidth &&
693 cre->height == Scr.MyDisplayHeight)
695 if (grav_g.x == -b->top_left.width &&
696 grav_g.y == -b->top_left.height)
698 /* Window is fullscreen using the ICCCM way. */
699 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
700 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
701 if (Scr.bo.do_debug_cr_motion_method == 1)
703 fprintf(
704 stderr, "+++ fullscreen icccm %p"
705 " '%s'\n", fw, fw->visible_name);
707 return;
709 else if (static_g.x == -b->top_left.width &&
710 static_g.y == -b->top_left.height)
712 /* Window is fullscreen using the traditional way. */
713 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
714 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
715 if (Scr.bo.do_debug_cr_motion_method == 1)
717 fprintf(
718 stderr, "+++ fullscreen traditional"
719 " %p '%s'\n", fw,
720 fw->visible_name);
722 return;
725 /* check travelling across the screen */
726 if (has_x && dg_g.x == 0 && ds_g.x != 0 &&
727 has_y && dg_g.y == 0 && ds_g.y != 0)
729 /* The traditional way causes a shift by the border width or
730 * height. Use ICCCM way. */
731 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
732 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
733 if (Scr.bo.do_debug_cr_motion_method == 1)
735 fprintf(
736 stderr, "+++ travelling icccm %p '%s'\n",
737 fw, fw->visible_name);
739 return;
741 if (has_x && dg_g.x != 0 && ds_g.x == 0 &&
742 has_y && dg_g.y != 0 && ds_g.y == 0)
744 /* The ICCCM way causes a shift by the border width or height.
745 * Use traditional way. */
746 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
747 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
748 if (Scr.bo.do_debug_cr_motion_method == 1)
750 fprintf(
751 stderr, "+++ travelling traditional %p"
752 " '%s'\n", fw, fw->visible_name);
754 return;
756 /* check placement near border */
757 w = (cre->value_mask & CWWidth) ?
758 cre->width + b->total_size.width : fw->g.frame.width;
759 h = (cre->value_mask & CWHeight) ?
760 cre->height + b->total_size.height : fw->g.frame.height;
761 if (!has_x)
763 mx = CR_MOTION_METHOD_AUTO;
765 else if (static_g.x == 0 || static_g.x + w == Scr.MyDisplayWidth)
767 mx = CR_MOTION_METHOD_STATIC_GRAV;
769 else if (grav_g.x == 0 || grav_g.x + w == Scr.MyDisplayWidth)
771 mx = CR_MOTION_METHOD_USE_GRAV;
773 else
775 mx = CR_MOTION_METHOD_AUTO;
777 if (!has_y)
779 my = CR_MOTION_METHOD_AUTO;
781 else if (static_g.y == 0 || static_g.y + h == Scr.MyDisplayHeight)
783 my = CR_MOTION_METHOD_STATIC_GRAV;
785 else if (grav_g.y == 0 || grav_g.y + h == Scr.MyDisplayHeight)
787 my = CR_MOTION_METHOD_USE_GRAV;
789 else
791 my = CR_MOTION_METHOD_AUTO;
793 m = (mx != CR_MOTION_METHOD_AUTO) ? mx : my;
794 if (m != CR_MOTION_METHOD_AUTO)
796 /* Window was placed next to the display border. */
797 if (m == my || my == CR_MOTION_METHOD_AUTO)
799 SET_CR_MOTION_METHOD(fw, m);
800 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
801 if (Scr.bo.do_debug_cr_motion_method == 1)
803 fprintf(
804 stderr, "+++ near border %s %p "
805 "'%s'\n", (m ==
806 CR_MOTION_METHOD_USE_GRAV)
807 ? "icccm" : "traditional", fw,
808 fw->visible_name);
810 return;
813 if (Scr.bo.do_debug_cr_motion_method == 1)
815 fprintf(
816 stderr, "--- not detected %p '%s'\n", fw,
817 fw->visible_name);
820 return;
823 #define EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
824 /* This is not a good idea because this interferes with changes in the size
825 * hints of the window. However, it is impossible to be completely safe here.
826 * For example, if the client changes the size inc, then resizes the size of
827 * its window and then changes the size inc again - all in one batch - then
828 * the WM will read the *second* size inc upon the *first* event and use the
829 * wrong one in the ConfigureRequest calculations. */
830 /* dv (31 Mar 2002): The code now handles these situations, so enable it
831 * again. */
832 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
833 static inline int __merge_cr_moveresize(
834 const evh_args_t *ea, XConfigureRequestEvent *cre, FvwmWindow *fw,
835 size_borders *b)
837 int cn_count = 0;
838 XEvent e;
839 XConfigureRequestEvent *ecre;
840 check_if_event_args args;
842 args.w = cre->window;
843 args.do_return_true = False;
844 args.do_return_true_cr = True;
845 args.cr_value_mask = CR_MOVERESIZE_MASK;
846 args.ret_does_match = False;
847 args.ret_type = 0;
849 for (cn_count = 0; 1; )
851 unsigned long vma;
852 unsigned long vmo;
853 unsigned long xm;
854 unsigned long ym;
855 evh_args_t ea2;
856 exec_context_changes_t ecc;
858 FCheckPeekIfEvent(
859 dpy, &e, test_resizing_event, (XPointer)&args);
860 ecre = &e.xconfigurerequest;
861 if (args.ret_does_match == False)
863 break;
865 else if (args.ret_type == PropertyNotify)
867 /* Can't merge events with a PropertyNotify in
868 * between. The event is still on the queue. */
869 break;
871 else if (args.ret_type != ConfigureRequest)
873 /* not good. unselected event type! */
874 continue;
876 /* Event was not yet removed from the queue but stored in e. */
877 xm = CWX | CWWidth;
878 ym = CWY | CWHeight;
879 vma = cre->value_mask & ecre->value_mask;
880 vmo = cre->value_mask | ecre->value_mask;
881 if (((vma & xm) == 0 && (vmo & xm) == xm) ||
882 ((vma & ym) == 0 && (vmo & ym) == ym))
884 /* can't merge events since location of window might
885 * get screwed up. */
886 break;
888 /* Finally remove the event from the queue */
889 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
890 /* partially handle the event */
891 ecre->value_mask &= ~args.cr_value_mask;
892 ea2.exc = exc_clone_context(ea->exc, &ecc, ECC_ETRIGGER);
893 HandleConfigureRequest(&ea2);
894 exc_destroy_context(ea2.exc);
895 /* collect the size/position changes */
896 if (ecre->value_mask & CWX)
898 cre->x = ecre->x;
900 if (ecre->value_mask & CWY)
902 cre->y = ecre->y;
904 if (ecre->value_mask & CWWidth)
906 cre->width = ecre->width;
908 if (ecre->value_mask & CWHeight)
910 cre->height = ecre->height;
912 if (ecre->value_mask & CWBorderWidth)
914 cre->border_width = ecre->border_width;
916 cre->value_mask |= (ecre->value_mask & CR_MOVERESIZE_MASK);
917 cn_count++;
920 return cn_count;
922 #endif
924 static inline int __handle_cr_on_client(
925 int *ret_do_send_event, XConfigureRequestEvent cre,
926 const evh_args_t *ea, FvwmWindow *fw, Bool force, int force_gravity)
928 rectangle new_g;
929 rectangle d_g;
930 size_rect constr_dim;
931 size_rect oldnew_dim;
932 size_borders b;
933 int cn_count = 0;
934 int gravity;
936 if (ea)
938 cre = ea->exc->x.etrigger->xconfigurerequest;
940 if ((cre.value_mask & (CWWidth | CWHeight | CWX | CWY)) == 0)
942 return 0;
945 get_window_borders(fw, &b);
946 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
947 /* Merge all pending ConfigureRequests for the window into a single
948 * event. However, we can not do this if the window uses the motion
949 * method autodetection because the merged event might confuse the
950 * detection code. */
951 if (ea && CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
953 cn_count = __merge_cr_moveresize(ea, &cre, fw, &b);
955 #endif
956 #if 0
957 fprintf(stderr,
958 "cre: %d(%d) %d(%d) %d(%d)x%d(%d) fw 0x%08x w 0x%08x "
959 "ew 0x%08x '%s'\n",
960 cre.x, (int)(cre.value_mask & CWX),
961 cre.y, (int)(cre.value_mask & CWY),
962 cre.width, (int)(cre.value_mask & CWWidth),
963 cre.height, (int)(cre.value_mask & CWHeight),
964 (int)FW_W_FRAME(fw), (int)FW_W(fw), (int)cre.window,
965 (fw->name.name) ? fw->name.name : "");
966 #endif
967 /* Don't modify frame_g fields before calling SetupWindow! */
968 memset(&d_g, 0, sizeof(d_g));
970 if (HAS_NEW_WM_NORMAL_HINTS(fw))
972 /* get the latest size hints */
973 XSync(dpy, 0);
974 GetWindowSizeHints(fw);
975 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
977 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMaxSize))
979 /* Java workaround */
980 if (cre.height > fw->hints.max_height &&
981 fw->hints.max_height <= BROKEN_MAXSIZE_LIMIT)
983 fw->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
984 cre.value_mask |= CWHeight;
986 if (cre.width > fw->hints.max_width &&
987 fw->hints.max_width <= BROKEN_MAXSIZE_LIMIT)
989 fw->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
990 cre.value_mask |= CWWidth;
993 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMinSize))
995 if (cre.width < fw->hints.min_width &&
996 fw->hints.min_width >= BROKEN_MINSIZE_LIMIT)
998 fw->hints.min_width = 1;
999 cre.value_mask |= CWWidth;
1001 if (cre.height < fw->hints.min_height &&
1002 fw->hints.min_height >= BROKEN_MINSIZE_LIMIT)
1004 fw->hints.min_height = 1;
1005 cre.value_mask |= CWHeight;
1008 if (IS_SHADED(fw) ||
1009 !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM, False))
1011 /* forbid shaded applications to move their windows */
1012 cre.value_mask &= ~(CWX | CWY);
1013 /* resend the old geometry */
1014 *ret_do_send_event = 1;
1016 if (IS_MAXIMIZED(fw))
1018 /* dont allow clients to resize maximized windows */
1019 cre.value_mask &= ~(CWWidth | CWHeight);
1020 /* resend the old geometry */
1021 *ret_do_send_event = 1;
1022 d_g.width = 0;
1023 d_g.height = 0;
1025 else if (
1026 !is_function_allowed(
1027 F_RESIZE, NULL, fw, RQORIG_PROGRAM, False))
1029 cre.value_mask &= ~(CWWidth | CWHeight);
1030 *ret_do_send_event = 1;
1033 if (cre.value_mask & CWBorderWidth)
1035 /* for restoring */
1036 fw->attr_backup.border_width = cre.border_width;
1038 if (!force && CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO)
1040 __cr_detect_icccm_move(fw, &cre, &b);
1042 if (force_gravity > ForgetGravity && force_gravity <= StaticGravity)
1044 gravity = force_gravity;
1046 else
1048 gravity = fw->hints.win_gravity;
1050 if (!(cre.value_mask & (CWX | CWY)))
1052 /* nothing */
1054 else if ((force ||
1055 CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_USE_GRAV) &&
1056 gravity != StaticGravity)
1058 int ref_x;
1059 int ref_y;
1060 int grav_x;
1061 int grav_y;
1063 gravity_get_offsets(gravity, &grav_x, &grav_y);
1064 if (cre.value_mask & CWX)
1066 ref_x = cre.x -
1067 ((grav_x + 1) * b.total_size.width) / 2;
1068 d_g.x = ref_x - fw->g.frame.x;
1070 if (cre.value_mask & CWY)
1072 ref_y = cre.y -
1073 ((grav_y + 1) * b.total_size.height) / 2;
1074 d_g.y = ref_y - fw->g.frame.y;
1077 else /* ..._USE_GRAV or ..._AUTO */
1079 /* default: traditional cr handling */
1080 if (cre.value_mask & CWX)
1082 d_g.x = cre.x - fw->g.frame.x - b.top_left.width;
1084 if (cre.value_mask & CWY)
1086 d_g.y = cre.y - fw->g.frame.y - b.top_left.height;
1089 if (cre.value_mask & CWHeight)
1091 if (cre.height <
1092 (WINDOW_FREAKED_OUT_SIZE - b.total_size.height))
1094 d_g.height = cre.height -
1095 (fw->g.frame.height - b.total_size.height);
1097 else
1099 /* Ignore height changes to astronomically large
1100 * windows (needed for XEmacs 20.4); don't care if the
1101 * window is shaded here - we won't use 'height' in
1102 * this case anyway.
1103 * Inform the buggy app about the size that *we* want
1105 d_g.height = 0;
1106 *ret_do_send_event = 1;
1109 if (cre.value_mask & CWWidth)
1111 if (cre.width < (WINDOW_FREAKED_OUT_SIZE - b.total_size.width))
1113 d_g.width = cre.width -
1114 (fw->g.frame.width - b.total_size.width);
1116 else
1118 d_g.width = 0;
1119 *ret_do_send_event = 1;
1123 /* SetupWindow (x,y) are the location of the upper-left outer corner
1124 * and are passed directly to XMoveResizeWindow (frame). The
1125 * (width,height) are the inner size of the frame. The inner width is
1126 * the same as the requested client window width; the inner height is
1127 * the same as the requested client window height plus any title bar
1128 * slop. */
1129 new_g = fw->g.frame;
1130 if (IS_SHADED(fw))
1132 new_g.width = fw->g.normal.width;
1133 new_g.height = fw->g.normal.height;
1135 oldnew_dim.width = new_g.width + d_g.width;
1136 oldnew_dim.height = new_g.height + d_g.height;
1137 constr_dim.width = oldnew_dim.width;
1138 constr_dim.height = oldnew_dim.height;
1139 constrain_size(
1140 fw, NULL, &constr_dim.width, &constr_dim.height, 0, 0,
1141 CS_UPDATE_MAX_DEFECT);
1142 d_g.width += (constr_dim.width - oldnew_dim.width);
1143 d_g.height += (constr_dim.height - oldnew_dim.height);
1144 if ((cre.value_mask & CWX) && d_g.width)
1146 new_g.x = fw->g.frame.x + d_g.x;
1147 new_g.width = fw->g.frame.width + d_g.width;
1149 else if ((cre.value_mask & CWX) && !d_g.width)
1151 new_g.x = fw->g.frame.x + d_g.x;
1153 else if (!(cre.value_mask & CWX) && d_g.width)
1155 gravity_resize(gravity, &new_g, d_g.width, 0);
1157 if ((cre.value_mask & CWY) && d_g.height)
1159 new_g.y = fw->g.frame.y + d_g.y;
1160 new_g.height = fw->g.frame.height + d_g.height;
1162 else if ((cre.value_mask & CWY) && !d_g.height)
1164 new_g.y = fw->g.frame.y + d_g.y;
1166 else if (!(cre.value_mask & CWY) && d_g.height)
1168 gravity_resize(gravity, &new_g, 0, d_g.height);
1171 if (new_g.x == fw->g.frame.x && new_g.y == fw->g.frame.y &&
1172 new_g.width == fw->g.frame.width &&
1173 new_g.height == fw->g.frame.height)
1175 /* Window will not be moved or resized; send a synthetic
1176 * ConfigureNotify. */
1177 *ret_do_send_event = 1;
1179 else if ((cre.value_mask & CWX) || (cre.value_mask & CWY) ||
1180 d_g.width || d_g.height)
1182 if (IS_SHADED(fw))
1184 get_shaded_geometry(fw, &new_g, &new_g);
1186 frame_setup_window_app_request(
1187 fw, new_g.x, new_g.y, new_g.width, new_g.height,
1188 False);
1189 /* make sure the window structure has the new position */
1190 update_absolute_geometry(fw);
1191 maximize_adjust_offset(fw);
1192 GNOME_SetWinArea(fw);
1194 else if (DO_FORCE_NEXT_CR(fw))
1196 *ret_do_send_event = 1;
1198 SET_FORCE_NEXT_CR(fw, 0);
1199 SET_FORCE_NEXT_PN(fw, 0);
1201 return cn_count;
1204 void __handle_configure_request(
1205 XConfigureRequestEvent cre, const evh_args_t *ea, FvwmWindow *fw,
1206 Bool force, int force_gravity)
1208 int do_send_event = 0;
1209 int cn_count = 0;
1211 /* According to the July 27, 1988 ICCCM draft, we should ignore size
1212 * and position fields in the WM_NORMAL_HINTS property when we map a
1213 * window. Instead, we'll read the current geometry. Therefore, we
1214 * should respond to configuration requests for windows which have
1215 * never been mapped. */
1216 if (fw == NULL)
1218 __handle_cr_on_unmanaged(&cre);
1219 return;
1221 if (cre.window == FW_W_ICON_TITLE(fw) ||
1222 cre.window == FW_W_ICON_PIXMAP(fw))
1224 __handle_cr_on_icon(&cre, fw);
1226 if (FShapesSupported)
1228 __handle_cr_on_shaped(fw);
1230 if (fw != NULL && cre.window == FW_W(fw))
1232 cn_count = __handle_cr_on_client(
1233 &do_send_event, cre, ea, fw, force, force_gravity);
1235 /* Stacking order change requested. Handle this *after* geometry
1236 * changes, since we need the new geometry in occlusion calculations */
1237 if ((cre.value_mask & CWStackMode) &&
1238 (!DO_IGNORE_RESTACK(fw) || force))
1240 __handle_cr_restack(&do_send_event, &cre, fw);
1242 #if 1
1243 /* This causes some ddd windows not to be drawn properly. Reverted back
1244 * to the old method in frame_setup_window. */
1245 /* domivogt (15-Oct-1999): enabled this to work around buggy apps that
1246 * ask for a nonsense height and expect that they really get it. */
1247 if (cn_count == 0 && do_send_event)
1249 cn_count = 1;
1251 else if (cn_count > 0)
1253 do_send_event = 1;
1255 for ( ; cn_count > 0; cn_count--)
1257 SendConfigureNotify(
1258 fw, fw->g.frame.x, fw->g.frame.y, fw->g.frame.width,
1259 fw->g.frame.height, 0, True);
1261 if (do_send_event)
1263 XFlush(dpy);
1265 #endif
1267 return;
1270 static Bool __predicate_button_click(
1271 Display *display, XEvent *event, XPointer arg)
1273 if (event->type == ButtonPress || event->type == ButtonRelease)
1275 return True;
1278 return False;
1281 /* Helper function for __handle_focus_raise_click(). */
1282 static Bool __test_for_motion(int x0, int y0)
1284 int x;
1285 int y;
1286 unsigned int mask;
1287 XEvent e;
1289 /* Query the pointer to do this. We can't check for events here since
1290 * the events are still needed if the pointer moves. */
1292 /* However, some special mouse (e.g., a touchpad with the
1293 * synaptic driver) may handle a double click in a special way
1294 * (for dragging through short touching and holding down the
1295 * finger on the touchpad). Bascially, when you execute a
1296 * double click the first button release is queued after the
1297 * second _physical_ mouse release happen. It seems that
1298 * FQueryPointer may not work as expected: it does not see
1299 * that the button is released on a double click. So, we need
1300 * to check for a button press in the future to avoid a fvwm
1301 * lockup! (olicha 2004-01-31) */
1303 for (x = x0, y = y0; FQueryPointer(
1304 dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
1305 &x, &y, &mask) == True; usleep(20000))
1307 if ((mask & DEFAULT_ALL_BUTTONS_MASK) == 0)
1309 /* all buttons are released */
1310 return False;
1312 else if (abs(x - x0) >= Scr.MoveThreshold ||
1313 abs(y - y0) >= Scr.MoveThreshold)
1315 /* the pointer has moved */
1316 return True;
1318 if (FCheckPeekIfEvent(dpy, &e, __predicate_button_click, NULL))
1320 /* click in the future */
1321 return False;
1323 else
1325 /* The predicate procedure finds no match, no event
1326 * has been removed from the queue and XFlush was
1327 * called. Nothing to do */
1331 /* pointer has moved off screen */
1332 return True;
1335 /* Helper function for __handle_focus_raise_click(). */
1336 static void __check_click_to_focus_or_raise(
1337 hfrc_ret_t *ret_args, const exec_context_t *exc)
1339 FvwmWindow * const fw = exc->w.fw;
1340 const XEvent *te = exc->x.etrigger;
1341 struct
1343 unsigned is_client_click : 1;
1344 unsigned is_focused : 1;
1345 } f;
1347 f.is_focused = !!focus_is_focused(fw);
1348 f.is_client_click = (exc->w.wcontext == C_WINDOW ||
1349 exc->w.wcontext == C_EWMH_DESKTOP);
1350 /* check if we need to raise and/or focus the window */
1351 ret_args->do_focus = focus_query_click_to_focus(fw, exc->w.wcontext);
1352 if (f.is_client_click && !ret_args->do_focus &&
1353 !f.is_focused && FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
1354 !fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
1356 /* Give the window a chance to to take focus itself */
1357 ret_args->do_focus = 1;
1359 if (ret_args->do_focus && focus_is_focused(fw))
1361 ret_args->do_focus = 0;
1363 ret_args->do_raise =
1364 focus_query_click_to_raise(fw, f.is_focused, exc->w.wcontext);
1365 #define EXPERIMENTAL_ROU_HANDLING_V2
1366 #ifdef EXPERIMENTAL_ROU_HANDLING_V2
1367 /* RBW -- Dang! This works without the one in HandleEnterNotify! */
1368 if (ret_args->do_raise && is_on_top_of_layer_and_above_unmanaged(fw))
1369 #else
1370 if (ret_args->do_raise && is_on_top_of_layer(fw))
1371 #endif
1373 ret_args->do_raise = 0;
1375 if ((ret_args->do_focus &&
1376 FP_DO_IGNORE_FOCUS_CLICK_MOTION(FW_FOCUS_POLICY(fw))) ||
1377 (ret_args->do_raise &&
1378 FP_DO_IGNORE_RAISE_CLICK_MOTION(FW_FOCUS_POLICY(fw))))
1380 /* Pass further events to the application and check if a button
1381 * release or motion event occurs next. If we don't do this
1382 * here, the pointer will seem to be frozen in
1383 * __test_for_motion(). */
1384 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1385 if (__test_for_motion(te->xbutton.x_root, te->xbutton.y_root))
1387 /* the pointer was moved, process event normally */
1388 ret_args->do_focus = 0;
1389 ret_args->do_raise = 0;
1392 if (ret_args->do_focus || ret_args->do_raise)
1394 if (!((ret_args->do_focus &&
1395 FP_DO_ALLOW_FUNC_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1396 (ret_args->do_raise &&
1397 FP_DO_ALLOW_FUNC_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1399 ret_args->do_forbid_function = 1;
1401 if (!((ret_args->do_focus &&
1402 FP_DO_PASS_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1403 (ret_args->do_raise &&
1404 FP_DO_PASS_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1406 ret_args->do_swallow_click = 1;
1410 return;
1413 /* Finds out if the click on a window must be used to focus or raise it. */
1414 static void __handle_focus_raise_click(
1415 hfrc_ret_t *ret_args, const exec_context_t *exc)
1417 memset(ret_args, 0, sizeof(*ret_args));
1418 if (exc->w.fw == NULL)
1420 return;
1422 /* check for proper click button and modifiers*/
1423 if (FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) != 0 &&
1424 !(FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) &
1425 (1 << (exc->x.etrigger->xbutton.button - 1))))
1427 /* wrong button, handle click normally */
1428 return;
1430 else if (FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw)) !=
1431 FPOL_ANY_MODIFIER &&
1432 MaskUsedModifiers(
1433 FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw))) !=
1434 MaskUsedModifiers(exc->x.etrigger->xbutton.state))
1436 /* right button but wrong modifiers, handle click normally */
1437 return;
1439 else
1441 __check_click_to_focus_or_raise(ret_args, exc);
1444 return;
1447 /* Helper function for HandleButtonPress */
1448 static Bool __is_bpress_window_handled(const exec_context_t *exc)
1450 Window eventw;
1451 const XEvent *te = exc->x.etrigger;
1453 if (exc->w.fw == NULL)
1455 if ((te->xbutton.window != Scr.Root ||
1456 te->xbutton.subwindow != None) &&
1457 !is_pan_frame(te->xbutton.window))
1459 /* Ignore events in unmanaged windows or subwindows of
1460 * a client */
1461 return False;
1463 else
1465 return True;
1468 eventw = (te->xbutton.subwindow != None &&
1469 te->xany.window != FW_W(exc->w.fw)) ?
1470 te->xbutton.subwindow : te->xany.window;
1471 if (is_frame_hide_window(eventw) || eventw == FW_W_FRAME(exc->w.fw))
1473 return False;
1475 if (!XGetGeometry(
1476 dpy, eventw, &JunkRoot, &JunkX, &JunkY,
1477 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
1478 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1480 /* The window has already died. */
1481 return False;
1484 return True;
1487 /* Helper function for __handle_bpress_on_managed */
1488 static Bool __handle_click_to_focus(const exec_context_t *exc)
1490 fpol_set_focus_by_t set_by;
1492 switch (exc->w.wcontext)
1494 case C_WINDOW:
1495 case C_EWMH_DESKTOP:
1496 set_by = FOCUS_SET_BY_CLICK_CLIENT;
1497 break;
1498 case C_ICON:
1499 set_by = FOCUS_SET_BY_CLICK_ICON;
1500 break;
1501 default:
1502 set_by = FOCUS_SET_BY_CLICK_DECOR;
1503 break;
1505 SetFocusWindow(exc->w.fw, True, set_by);
1506 focus_grab_buttons(exc->w.fw);
1507 if (focus_is_focused(exc->w.fw) && !IS_ICONIFIED(exc->w.fw))
1509 border_draw_decorations(
1510 exc->w.fw, PART_ALL, True, True, CLEAR_ALL, NULL,
1511 NULL);
1514 return focus_is_focused(exc->w.fw);
1517 /* Helper function for __handle_bpress_on_managed */
1518 static Bool __handle_click_to_raise(const exec_context_t *exc)
1520 Bool rc = False;
1521 int is_focused;
1523 is_focused = focus_is_focused(exc->w.fw);
1524 if (focus_query_click_to_raise(exc->w.fw, is_focused, True))
1526 rc = True;
1529 return rc;
1532 /* Helper function for HandleButtonPress */
1533 static void __handle_bpress_stroke(void)
1535 STROKE_CODE(stroke_init());
1536 STROKE_CODE(send_motion = True);
1538 return;
1541 /* Helper function for __handle_bpress_on_managed */
1542 static Bool __handle_bpress_action(
1543 const exec_context_t *exc, char *action)
1545 window_parts part;
1546 Bool do_force;
1547 Bool rc = False;
1549 if (!action || *action == 0)
1551 PressedW = None;
1552 return False;
1554 /* draw pressed in decorations */
1555 part = border_context_to_parts(exc->w.wcontext);
1556 do_force = (part & PART_TITLEBAR) ? True : False;
1557 border_draw_decorations(
1558 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1559 CLEAR_ALL, NULL, NULL);
1560 /* execute the action */
1561 if (IS_ICONIFIED(exc->w.fw))
1563 /* release the pointer since it can't do harm over an icon */
1564 XAllowEvents(dpy, AsyncPointer, CurrentTime);
1566 execute_function(NULL, exc, action, 0);
1567 if (exc->w.wcontext != C_WINDOW && exc->w.wcontext != C_NO_CONTEXT)
1569 WaitForButtonsUp(True);
1570 rc = True;
1572 /* redraw decorations */
1573 PressedW = None;
1574 if (check_if_fvwm_window_exists(exc->w.fw))
1576 part = border_context_to_parts(exc->w.wcontext);
1577 do_force = (part & PART_TITLEBAR) ? True : False;
1578 border_draw_decorations(
1579 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1580 CLEAR_ALL, NULL, NULL);
1583 return rc;
1586 /* Handles button presses on the root window. */
1587 static void __handle_bpress_on_root(const exec_context_t *exc)
1589 char *action;
1591 PressedW = None;
1592 __handle_bpress_stroke();
1593 /* search for an appropriate mouse binding */
1594 action = CheckBinding(
1595 Scr.AllBindings, STROKE_ARG(0) exc->x.etrigger->xbutton.button,
1596 exc->x.etrigger->xbutton.state, GetUnusedModifiers(), C_ROOT,
1597 BIND_BUTTONPRESS, NULL, NULL);
1598 if (action && *action)
1600 const exec_context_t *exc2;
1601 exec_context_changes_t ecc;
1603 ecc.w.wcontext = C_ROOT;
1604 exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
1605 execute_function(NULL, exc2, action, 0);
1606 exc_destroy_context(exc2);
1607 WaitForButtonsUp(True);
1609 else
1611 /* do gnome buttonpress forwarding if win == root */
1612 GNOME_ProxyButtonEvent(exc->x.etrigger);
1615 return;
1618 /* Handles button presses on unmanaged windows */
1619 static void __handle_bpress_on_unmanaged(const exec_context_t *exc)
1621 /* Pass the event to the application. */
1622 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1623 XFlush(dpy);
1625 return;
1628 /* Handles button presses on managed windows */
1629 static void __handle_bpress_on_managed(const exec_context_t *exc)
1631 char *action;
1632 hfrc_ret_t f;
1633 FvwmWindow * const fw = exc->w.fw;
1634 XEvent *e;
1636 e = exc->x.etrigger;
1637 /* Now handle click to focus and click to raise. */
1638 __handle_focus_raise_click(&f, exc);
1639 PressedW = (f.do_forbid_function) ? None : exc->w.w;
1640 if (f.do_focus)
1642 if (!__handle_click_to_focus(exc))
1644 /* Window didn't accept the focus; pass the click to
1645 * the application. */
1646 f.do_swallow_click = 0;
1649 if (f.do_raise)
1651 if (__handle_click_to_raise(exc) == True)
1653 /* We can't raise the window immediately because the
1654 * action bound to the click might be "Lower" or
1655 * "RaiseLower". So mark the window as scheduled to be
1656 * raised after the binding is executed. Functions that
1657 * modify the stacking order will reset this flag. */
1658 SET_SCHEDULED_FOR_RAISE(fw, 1);
1661 /* handle bindings */
1662 if (!f.do_forbid_function)
1664 /* stroke bindings */
1665 __handle_bpress_stroke();
1666 /* mouse bindings */
1667 action = CheckBinding(
1668 Scr.AllBindings, STROKE_ARG(0) e->xbutton.button,
1669 e->xbutton.state, GetUnusedModifiers(),
1670 exc->w.wcontext, BIND_BUTTONPRESS, &fw->class,
1671 fw->name.name);
1672 if (__handle_bpress_action(exc, action))
1674 f.do_swallow_click = 1;
1677 /* raise the window */
1678 if (IS_SCHEDULED_FOR_RAISE(fw))
1680 /* Now that we know the action did not restack the window we
1681 * can raise it.
1682 * dv (10-Aug-2002): We can safely raise the window after
1683 * redrawing it since all the decorations are drawn in the
1684 * window background and no Expose event is generated. */
1685 RaiseWindow(fw, False);
1686 SET_SCHEDULED_FOR_RAISE(fw, 0);
1688 /* clean up */
1689 if (!f.do_swallow_click)
1691 /* pass the click to the application */
1692 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1693 XFlush(dpy);
1695 else if (f.do_focus || f.do_raise)
1697 WaitForButtonsUp(True);
1700 return;
1703 /* restore focus stolen by unmanaged */
1704 static void __refocus_stolen_focus_win(const evh_args_t *ea)
1706 FOCUS_SET(Scr.StolenFocusWin);
1707 ea->exc->x.etrigger->xfocus.window = Scr.StolenFocusWin;
1708 ea->exc->x.etrigger->type = FocusIn;
1709 Scr.UnknownWinFocused = None;
1710 Scr.StolenFocusWin = None;
1711 dispatch_event(ea->exc->x.etrigger);
1713 return;
1716 /* ---------------------------- event handlers ----------------------------- */
1718 void HandleButtonPress(const evh_args_t *ea)
1720 DBUG("HandleButtonPress", "Routine Entered");
1722 GrabEm(CRS_NONE, GRAB_PASSIVE);
1723 if (__is_bpress_window_handled(ea->exc) == False)
1725 __handle_bpress_on_unmanaged(ea->exc);
1727 else if (ea->exc->w.fw != NULL)
1729 __handle_bpress_on_managed(ea->exc);
1731 else
1733 __handle_bpress_on_root(ea->exc);
1735 UngrabEm(GRAB_PASSIVE);
1737 return;
1740 #ifdef HAVE_STROKE
1741 void HandleButtonRelease(const evh_args_t *ea)
1743 char *action;
1744 char *name;
1745 int real_modifier;
1746 const XEvent *te = ea->exc->x.etrigger;
1747 XClassHint *class;
1749 DBUG("HandleButtonRelease", "Routine Entered");
1750 send_motion = False;
1751 stroke_trans (sequence);
1752 DBUG("HandleButtonRelease",sequence);
1753 /* Allows modifier to work (Only R context works here). */
1754 real_modifier = te->xbutton.state - (1 << (7 + te->xbutton.button));
1755 if (ea->exc->w.fw == NULL)
1757 class = NULL;
1758 name = NULL;
1760 else
1762 class = &ea->exc->w.fw->class;
1763 name = ea->exc->w.fw->name.name;
1765 /* need to search for an appropriate stroke binding */
1766 action = CheckBinding(
1767 Scr.AllBindings, sequence, te->xbutton.button, real_modifier,
1768 GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE,
1769 class, name);
1770 /* got a match, now process it */
1771 if (action != NULL && (action[0] != 0))
1773 execute_function(NULL, ea->exc, action, 0);
1774 WaitForButtonsUp(True);
1776 else
1779 * do gnome buttonpress forwarding if win == root
1781 if (Scr.Root == te->xany.window)
1783 GNOME_ProxyButtonEvent(te);
1787 return;
1789 #endif /* HAVE_STROKE */
1791 void HandleClientMessage(const evh_args_t *ea)
1793 const XEvent *te = ea->exc->x.etrigger;
1794 FvwmWindow * const fw = ea->exc->w.fw;
1796 DBUG("HandleClientMessage", "Routine Entered");
1798 /* Process GNOME and EWMH Messages */
1799 if (GNOME_ProcessClientMessage(ea->exc))
1801 return;
1803 else if (EWMH_ProcessClientMessage(ea->exc))
1805 return;
1808 /* handle deletion of tear out menus */
1809 if (fw && IS_TEAR_OFF_MENU(fw) && te->xclient.format == 32 &&
1810 te->xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
1812 menu_close_tear_off_menu(fw);
1813 return;
1816 if (te->xclient.message_type == _XA_WM_CHANGE_STATE &&
1817 fw && te->xclient.data.l[0] == IconicState && !IS_ICONIFIED(fw))
1819 const exec_context_t *exc;
1820 exec_context_changes_t ecc;
1822 ecc.w.wcontext = C_WINDOW;
1823 exc = exc_clone_context(ea->exc, &ecc, ECC_WCONTEXT);
1824 execute_function(NULL, exc, "Iconify", 0);
1825 exc_destroy_context(exc);
1826 return;
1829 /* FIXME: Is this safe enough ? I guess if clients behave
1830 * according to ICCCM and send these messages only if they
1831 * grabbed the pointer, it is OK */
1833 extern Atom _XA_WM_COLORMAP_NOTIFY;
1834 if (te->xclient.message_type == _XA_WM_COLORMAP_NOTIFY)
1836 set_client_controls_colormaps(te->xclient.data.l[1]);
1837 return;
1841 /* CKH - if we get here, it was an unknown client message, so send
1842 * it to the client if it was in a window we know about. I'm not so
1843 * sure this should be done or not, since every other window manager
1844 * I've looked at doesn't. But it might be handy for a free drag and
1845 * drop setup being developed for Linux. */
1846 if (fw)
1848 if (te->xclient.window != FW_W(fw))
1850 XEvent e;
1852 e = *te;
1853 e.xclient.window = FW_W(fw);
1854 FSendEvent(dpy, FW_W(fw), False, NoEventMask, &e);
1859 void HandleColormapNotify(const evh_args_t *ea)
1861 colormap_handle_colormap_notify(ea);
1863 return;
1866 void HandleConfigureRequest(const evh_args_t *ea)
1868 const XEvent *te = ea->exc->x.etrigger;
1869 XConfigureRequestEvent cre;
1870 FvwmWindow *fw = ea->exc->w.fw;
1872 DBUG("HandleConfigureRequest", "Routine Entered");
1874 cre = te->xconfigurerequest;
1875 /* te->xany.window is te->.xconfigurerequest.parent, so the context
1876 * window may be wrong. */
1877 if (XFindContext(dpy, cre.window, FvwmContext, (caddr_t *)&fw) ==
1878 XCNOENT)
1880 fw = NULL;
1882 __handle_configure_request(cre, ea, fw, False, ForgetGravity);
1884 return;
1887 void HandleDestroyNotify(const evh_args_t *ea)
1889 DBUG("HandleDestroyNotify", "Routine Entered");
1891 destroy_window(ea->exc->w.fw);
1892 EWMH_ManageKdeSysTray(
1893 ea->exc->x.etrigger->xdestroywindow.window,
1894 ea->exc->x.etrigger->type);
1895 EWMH_WindowDestroyed();
1896 GNOME_SetClientList();
1898 return;
1901 #define DEBUG_ENTERNOTIFY 0
1902 #if DEBUG_ENTERNOTIFY
1903 static int ecount=0;
1904 #define ENTER_DBG(x) fprintf x;
1905 #else
1906 #define ENTER_DBG(x)
1907 #endif
1908 void HandleEnterNotify(const evh_args_t *ea)
1910 const XEnterWindowEvent *ewp;
1911 XEvent d;
1912 FvwmWindow *sf;
1913 static Bool is_initial_ungrab_pending = True;
1914 Bool is_tear_off_menu;
1915 const XEvent *te = ea->exc->x.etrigger;
1916 FvwmWindow * const fw = ea->exc->w.fw;
1918 DBUG("HandleEnterNotify", "Routine Entered");
1919 ewp = &te->xcrossing;
1920 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)"));
1922 if (
1923 ewp->window == Scr.Root &&
1924 ewp->detail == NotifyInferior && ewp->mode == NotifyNormal)
1926 /* pointer left subwindow */
1927 BroadcastPacket(
1928 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1929 (long)NULL);
1931 else if (
1932 ewp->window == Scr.Root &&
1933 ewp->detail == NotifyNonlinearVirtual)
1935 /* pointer entered screen */
1936 BroadcastPacket(
1937 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1938 (long)NULL);
1940 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
1942 if (fw && !IS_ICONIFIED(fw) && ewp->window == FW_W(fw))
1944 InstallWindowColormaps(fw);
1946 else
1948 /* make sure its for one of our windows */
1949 /* handle a subwindow cmap */
1950 InstallWindowColormaps(NULL);
1953 else if (!fw)
1955 EnterSubWindowColormap(ewp->window);
1957 if (Scr.flags.is_wire_frame_displayed)
1959 ENTER_DBG((stderr, "en: exit: iwfd\n"));
1960 /* Ignore EnterNotify events while a window is resized or moved
1961 * as a wire frame; otherwise the window list may be screwed
1962 * up. */
1963 return;
1965 if (fw)
1967 if (ewp->window != FW_W_FRAME(fw) &&
1968 ewp->window != FW_W_PARENT(fw) &&
1969 ewp->window != FW_W(fw) &&
1970 ewp->window != FW_W_ICON_TITLE(fw) &&
1971 ewp->window != FW_W_ICON_PIXMAP(fw))
1973 /* Ignore EnterNotify that received by any of the sub
1974 * windows that don't handle this event. unclutter
1975 * triggers these events sometimes, re focusing an
1976 * unfocused window under the pointer */
1977 ENTER_DBG((stderr, "en: exit: funny window\n"));
1978 return;
1981 if (Scr.focus_in_pending_window != NULL)
1983 ENTER_DBG((stderr, "en: exit: fipw\n"));
1984 /* Ignore EnterNotify event while we are waiting for a window to
1985 * receive focus via Focus or FlipFocus commands. */
1986 focus_grab_buttons(fw);
1987 return;
1989 if (ewp->mode == NotifyGrab)
1991 ENTER_DBG((stderr, "en: exit: NotifyGrab\n"));
1992 return;
1994 else if (ewp->mode == NotifyNormal)
1996 ENTER_DBG((stderr, "en: NotifyNormal\n"));
1997 if (ewp->detail == NotifyNonlinearVirtual &&
1998 ewp->focus == False && ewp->subwindow != None)
2000 /* This takes care of some buggy apps that forget that
2001 * one of their dialog subwindows has the focus after
2002 * popping up a selection list several times (ddd,
2003 * netscape). I'm not convinced that this does not
2004 * break something else. */
2005 ENTER_DBG((stderr, "en: NN: refreshing focus\n"));
2006 refresh_focus(fw);
2009 else if (ewp->mode == NotifyUngrab)
2011 ENTER_DBG((stderr, "en: NotifyUngrab\n"));
2012 /* Ignore events generated by grabbing or ungrabbing the
2013 * pointer. However, there is no way to prevent the client
2014 * application from handling this event and, for example,
2015 * grabbing the focus. This will interfere with functions that
2016 * transferred the focus to a different window. */
2017 if (is_initial_ungrab_pending)
2019 ENTER_DBG((stderr, "en: NU: initial ungrab pending (lgw = NULL)\n"));
2020 is_initial_ungrab_pending = False;
2021 xcrossing_last_grab_window = NULL;
2023 else
2025 if (ewp->detail == NotifyNonlinearVirtual &&
2026 ewp->focus == False && ewp->subwindow != None)
2028 /* see comment above */
2029 ENTER_DBG((stderr, "en: NU: refreshing focus\n"));
2030 refresh_focus(fw);
2032 if (fw && fw == xcrossing_last_grab_window)
2034 ENTER_DBG((stderr, "en: exit: NU: is last grab window\n"));
2035 if (ewp->window == FW_W_FRAME(fw) ||
2036 ewp->window == FW_W_ICON_TITLE(fw) ||
2037 ewp->window == FW_W_ICON_PIXMAP(fw))
2039 ENTER_DBG((stderr, "en: exit: NU: last grab window = NULL\n"));
2040 xcrossing_last_grab_window = NULL;
2042 focus_grab_buttons(fw);
2044 return;
2046 else if (fw)
2048 if (ewp->window != FW_W_FRAME(fw) &&
2049 ewp->window != FW_W_ICON_TITLE(fw) &&
2050 ewp->window != FW_W_ICON_PIXMAP(fw))
2052 ENTER_DBG((stderr, "en: exit: NU: not frame window\n"));
2053 focus_grab_buttons(fw);
2054 return;
2059 if (fw)
2061 is_initial_ungrab_pending = False;
2064 /* look for a matching leaveNotify which would nullify this EnterNotify
2067 * RBW - if we're in startup, this is a coerced focus, so we don't
2068 * want to save the event time, or exit prematurely.
2070 * Ignore LeaveNotify events for tear out menus - handled by menu code
2072 is_tear_off_menu =
2073 (fw && IS_TEAR_OFF_MENU(fw) && ewp->window == FW_W(fw));
2074 if (!fFvwmInStartup && !is_tear_off_menu &&
2075 FCheckTypedWindowEvent(dpy, ewp->window, LeaveNotify, &d))
2077 if (d.xcrossing.mode == NotifyNormal &&
2078 d.xcrossing.detail != NotifyInferior)
2080 ENTER_DBG((stderr, "en: exit: found LeaveNotify\n"));
2081 return;
2085 if (ewp->window == Scr.Root)
2087 FvwmWindow *lf = get_last_screen_focus_window();
2089 if (!Scr.flags.is_pointer_on_this_screen)
2091 Scr.flags.is_pointer_on_this_screen = 1;
2092 if (lf && lf != &Scr.FvwmRoot &&
2093 !FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(lf)))
2095 SetFocusWindow(lf, True, FOCUS_SET_FORCE);
2097 else if (lf != &Scr.FvwmRoot)
2099 ForceDeleteFocus();
2101 else
2103 /* This was the first EnterNotify event for the
2104 * root window - ignore */
2106 set_last_screen_focus_window(NULL);
2108 else if (!(sf = get_focus_window()) ||
2109 FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2111 DeleteFocus(True);
2113 else if (
2114 Scr.UnknownWinFocused != None && sf != NULL &&
2115 FW_W(sf) == Scr.StolenFocusWin)
2117 __refocus_stolen_focus_win(ea);
2119 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2121 InstallWindowColormaps(NULL);
2123 focus_grab_buttons(lf);
2124 return;
2126 else
2128 Scr.flags.is_pointer_on_this_screen = 1;
2131 /* An EnterEvent in one of the PanFrameWindows activates the Paging or
2132 an EdgeCommand. */
2133 if (is_pan_frame(ewp->window))
2135 char *edge_command = NULL;
2137 if (
2138 Scr.UnknownWinFocused != None &&
2139 (sf = get_focus_window()) != NULL &&
2140 FW_W(sf) == Scr.StolenFocusWin)
2142 __refocus_stolen_focus_win(ea);
2144 /* check for edge commands */
2145 if (ewp->window == Scr.PanFrameTop.win)
2147 edge_command = Scr.PanFrameTop.command;
2149 else if (ewp->window == Scr.PanFrameBottom.win)
2151 edge_command = Scr.PanFrameBottom.command;
2153 else if (ewp->window == Scr.PanFrameLeft.win)
2155 edge_command = Scr.PanFrameLeft.command;
2157 else if (ewp->window == Scr.PanFrameRight.win)
2159 edge_command = Scr.PanFrameRight.command;
2161 if (edge_command && ewp->mode == NotifyUngrab &&
2162 ewp->detail == NotifyAncestor)
2164 /* nothing */
2166 else if (edge_command)
2168 execute_function(NULL, ea->exc, edge_command, 0);
2170 else
2172 /* no edge command for this pan frame - so we do
2173 * HandlePaging */
2174 int delta_x = 0;
2175 int delta_y = 0;
2176 XEvent e;
2178 /* this was in the HandleMotionNotify before, HEDU */
2179 Scr.flags.is_pointer_on_this_screen = 1;
2180 e = *te;
2181 HandlePaging(
2182 &e, Scr.EdgeScrollX, Scr.EdgeScrollY, &JunkX,
2183 &JunkY, &delta_x, &delta_y, True, True, False,
2184 Scr.ScrollDelay);
2185 return;
2188 if (!fw)
2190 return;
2192 if (IS_EWMH_DESKTOP(FW_W(fw)))
2194 BroadcastPacket(
2195 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
2196 (long)NULL);
2197 return;
2199 if (ewp->window == FW_W_FRAME(fw) ||
2200 ewp->window == FW_W_ICON_TITLE(fw) ||
2201 ewp->window == FW_W_ICON_PIXMAP(fw))
2203 BroadcastPacket(
2204 MX_ENTER_WINDOW, 3, (long)FW_W(fw),
2205 (long)FW_W_FRAME(fw), (unsigned long)fw);
2207 sf = get_focus_window();
2208 if (sf && fw != sf && FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2210 ENTER_DBG((stderr, "en: delete focus\n"));
2211 DeleteFocus(True);
2213 focus_grab_buttons(fw);
2214 if (FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
2216 ENTER_DBG((stderr, "en: set mousey focus\n"));
2217 if (ewp->window == FW_W(fw))
2219 /* Event is for the client window...*/
2220 #ifndef EXPERIMENTAL_ROU_HANDLING_V2
2221 /* RBW -- This may still be needed at times, I'm not
2222 *sure yet. */
2223 SetFocusWindowClientEntered(
2224 fw, True, FOCUS_SET_BY_ENTER);
2225 #else
2226 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2227 #endif
2229 else
2231 /* Event is for the frame...*/
2232 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2235 else if (focus_is_focused(fw) && focus_does_accept_input_focus(fw))
2237 /* We have to refresh the focus window here in case we left the
2238 * focused fvwm window. Motif apps may lose the input focus
2239 * otherwise. But do not try to refresh the focus of
2240 * applications that want to handle it themselves. */
2241 focus_force_refresh_focus(fw);
2243 else if (sf != fw)
2245 /* Give the window a chance to grab the buttons needed for
2246 * raise-on-click */
2247 focus_grab_buttons(sf);
2249 if (
2250 Scr.UnknownWinFocused != None && sf != NULL &&
2251 FW_W(sf) == Scr.StolenFocusWin)
2253 __refocus_stolen_focus_win(ea);
2255 /* We get an EnterNotify with mode == UnGrab when fvwm releases the
2256 * grab held during iconification. We have to ignore this, or icon
2257 * title will be initially raised. */
2258 if (IS_ICONIFIED(fw) && (ewp->mode == NotifyNormal) &&
2259 (ewp->window == FW_W_ICON_PIXMAP(fw) ||
2260 ewp->window == FW_W_ICON_TITLE(fw)) &&
2261 FW_W_ICON_PIXMAP(fw) != None)
2263 SET_ICON_ENTERED(fw, 1);
2264 DrawIconWindow(fw, True, False, False, False, NULL);
2266 /* Check for tear off menus */
2267 if (is_tear_off_menu)
2269 menu_enter_tear_off_menu(ea->exc);
2272 return;
2275 void HandleExpose(const evh_args_t *ea)
2277 XEvent e;
2278 FvwmWindow * const fw = ea->exc->w.fw;
2280 e = *ea->exc->x.etrigger;
2281 #if 0
2282 /* This doesn't work well. Sometimes, the expose count is zero although
2283 * dozens of expose events are pending. This happens all the time
2284 * during a shading animation. Simply flush expose events
2285 * unconditionally. */
2286 if (e.xexpose.count != 0)
2288 flush_accumulate_expose(e.xexpose.window, &e);
2290 #else
2291 flush_accumulate_expose(e.xexpose.window, &e);
2292 #endif
2293 if (fw == NULL)
2295 return;
2297 if (e.xany.window == FW_W_ICON_TITLE(fw) ||
2298 e.xany.window == FW_W_ICON_PIXMAP(fw))
2300 DrawIconWindow(fw, True, True, False, False, &e);
2301 return;
2303 else if (IS_TEAR_OFF_MENU(fw) && e.xany.window == FW_W(fw))
2305 /* refresh the contents of the torn out menu */
2306 menu_expose(&e, NULL);
2309 return;
2312 void HandleFocusIn(const evh_args_t *ea)
2314 XEvent d;
2315 Window w = None;
2316 Window focus_w = None;
2317 Window focus_fw = None;
2318 Pixel fc = 0;
2319 Pixel bc = 0;
2320 FvwmWindow *ffw_old = get_focus_window();
2321 FvwmWindow *sf;
2322 Bool do_force_broadcast = False;
2323 Bool is_unmanaged_focused = False;
2324 static Window last_focus_w = None;
2325 static Window last_focus_fw = None;
2326 static Bool was_nothing_ever_focused = True;
2327 FvwmWindow *fw = ea->exc->w.fw;
2329 DBUG("HandleFocusIn", "Routine Entered");
2331 Scr.focus_in_pending_window = NULL;
2332 /* This is a hack to make the PointerKey command work */
2333 if (ea->exc->x.etrigger->xfocus.detail != NotifyPointer)
2335 /**/
2336 w = ea->exc->x.etrigger->xany.window;
2338 while (FCheckTypedEvent(dpy, FocusIn, &d))
2340 /* dito */
2341 if (d.xfocus.detail != NotifyPointer)
2343 /**/
2344 w = d.xany.window;
2347 /* dito */
2348 if (w == None)
2350 return;
2352 /**/
2353 if (XFindContext(dpy, w, FvwmContext, (caddr_t *) &fw) == XCNOENT)
2355 fw = NULL;
2358 Scr.UnknownWinFocused = None;
2359 if (!fw)
2361 if (w != Scr.NoFocusWin)
2363 Scr.UnknownWinFocused = w;
2364 Scr.StolenFocusWin =
2365 (ffw_old != NULL) ? FW_W(ffw_old) : None;
2366 focus_w = w;
2367 is_unmanaged_focused = True;
2369 /* Only show a non-focused window as focused,
2370 * if the focus is on unmanaged and flickering qt dialogs
2371 * workaround is on. */
2372 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2373 !is_unmanaged_focused)
2375 border_draw_decorations(
2376 Scr.Hilite, PART_ALL, False, True, CLEAR_ALL,
2377 NULL, NULL);
2378 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2380 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2382 InstallWindowColormaps(Scr.Hilite);
2384 else
2386 InstallWindowColormaps(NULL);
2390 /* Not very useful if no window that fvwm and its modules know
2391 * about has the focus. */
2392 fc = GetColor(DEFAULT_FORE_COLOR);
2393 bc = GetColor(DEFAULT_BACK_COLOR);
2395 else if (fw != Scr.Hilite ||
2396 /* domivogt (16-May-2000): This check is necessary to force
2397 * sending a M_FOCUS_CHANGE packet after an unmanaged window
2398 * was focused. Otherwise fvwm would believe that Scr.Hilite
2399 * was still focused and not send any info to the modules. */
2400 last_focus_fw == None ||
2401 IS_FOCUS_CHANGE_BROADCAST_PENDING(fw))
2403 do_force_broadcast = IS_FOCUS_CHANGE_BROADCAST_PENDING(fw);
2404 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 0);
2405 if (fw != Scr.Hilite)
2407 border_draw_decorations(
2408 fw, PART_ALL, True, True, CLEAR_ALL, NULL,
2409 NULL);
2411 focus_w = FW_W(fw);
2412 focus_fw = FW_W_FRAME(fw);
2413 fc = fw->hicolors.fore;
2414 bc = fw->hicolors.back;
2415 set_focus_window(fw);
2416 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2418 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2420 InstallWindowColormaps(Scr.Hilite);
2422 else
2424 InstallWindowColormaps(NULL);
2428 else
2430 return;
2432 if (was_nothing_ever_focused || last_focus_fw == None ||
2433 focus_w != last_focus_w || focus_fw != last_focus_fw ||
2434 do_force_broadcast)
2436 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2437 !is_unmanaged_focused)
2439 BroadcastPacket(
2440 M_FOCUS_CHANGE, 5, (long)focus_w,
2441 (long)focus_fw,
2442 (unsigned long)IsLastFocusSetByMouse(),
2443 (long)fc, (long)bc);
2444 EWMH_SetActiveWindow(focus_w);
2446 last_focus_w = focus_w;
2447 last_focus_fw = focus_fw;
2448 was_nothing_ever_focused = False;
2450 if ((sf = get_focus_window()) != ffw_old)
2452 focus_grab_buttons(sf);
2453 focus_grab_buttons(ffw_old);
2456 return;
2459 void HandleFocusOut(const evh_args_t *ea)
2461 if (Scr.UnknownWinFocused != None && Scr.StolenFocusWin != None &&
2462 ea->exc->x.etrigger->xfocus.window == Scr.UnknownWinFocused)
2464 __refocus_stolen_focus_win(ea);
2467 return;
2470 void __handle_key(const evh_args_t *ea, Bool is_press)
2472 char *action;
2473 FvwmWindow *sf;
2474 KeyCode kc;
2475 int kcontext;
2476 const XEvent *te = ea->exc->x.etrigger;
2477 const FvwmWindow * const fw = ea->exc->w.fw;
2478 Bool is_second_binding;
2479 const XClassHint *winClass1, *winClass2;
2480 XClassHint tmp;
2481 char *name1, *name2;
2482 const exec_context_t *exc;
2483 exec_context_changes_t ecc;
2485 PressedW = None;
2487 /* Here's a real hack - some systems have two keys with the
2488 * same keysym and different keycodes. This converts all
2489 * the cases to one keycode. */
2490 kc = XKeysymToKeycode(dpy, XKeycodeToKeysym(dpy, te->xkey.keycode, 0));
2492 /* Check if there is something bound to the key */
2494 sf = get_focus_window();
2495 if (sf == NULL)
2497 tmp.res_name = tmp.res_class = name1 = "root";
2498 winClass1 = &tmp;
2499 kcontext = C_ROOT;
2501 else
2503 winClass1 = &sf->class;
2504 name1 = sf->name.name;
2505 kcontext = (sf == fw ? ea->exc->w.wcontext : C_WINDOW);
2508 if (fw == NULL)
2510 tmp.res_name = tmp.res_class = name2 = "root";
2511 winClass2 = &tmp;
2513 else
2515 winClass2 = &fw->class;
2516 name2 = fw->name.name;
2518 /* Searching the binding list with a different 'type' value
2519 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
2520 * The different context value does though. */
2521 action = CheckTwoBindings(
2522 &is_second_binding, Scr.AllBindings, STROKE_ARG(0) kc,
2523 te->xkey.state, GetUnusedModifiers(), kcontext, BIND_KEYPRESS,
2524 winClass1, name1, ea->exc->w.wcontext, BIND_PKEYPRESS,
2525 winClass2, name2);
2527 if (action != NULL)
2529 if (!is_press)
2531 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2532 return;
2534 exc = ea->exc;
2535 if (is_second_binding == False)
2537 ecc.w.fw = sf;
2538 ecc.w.wcontext = kcontext;
2539 exc = exc_clone_context(
2540 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
2542 execute_function(NULL, exc, action, 0);
2543 if (is_second_binding == False)
2545 exc_destroy_context(exc);
2547 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2548 return;
2551 /* if we get here, no function key was bound to the key. Send it
2552 * to the client if it was in a window we know about. */
2553 sf = get_focus_window();
2554 if (sf && te->xkey.window != FW_W(sf))
2556 XEvent e;
2558 e = *te;
2559 e.xkey.window = FW_W(sf);
2560 FSendEvent(
2561 dpy, e.xkey.window, False,
2562 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2564 else if (fw && te->xkey.window != FW_W(fw))
2566 XEvent e;
2568 e = *te;
2569 e.xkey.window = FW_W(fw);
2570 FSendEvent(
2571 dpy, e.xkey.window, False,
2572 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2574 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2576 return;
2579 void HandleKeyPress(const evh_args_t *ea)
2581 __handle_key(ea, True);
2584 void HandleKeyRelease(const evh_args_t *ea)
2586 __handle_key(ea, False);
2589 void HandleLeaveNotify(const evh_args_t *ea)
2591 const XLeaveWindowEvent *lwp;
2592 const XEvent *te = ea->exc->x.etrigger;
2593 FvwmWindow * const fw = ea->exc->w.fw;
2595 DBUG("HandleLeaveNotify", "Routine Entered");
2597 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)"));
2598 lwp = &te->xcrossing;
2599 if (
2600 lwp->window == Scr.Root &&
2601 lwp->detail == NotifyInferior && lwp->mode == NotifyNormal)
2603 /* pointer entered subwindow */
2604 BroadcastPacket(
2605 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2606 (long)NULL);
2608 else if (
2609 lwp->window == Scr.Root &&
2610 lwp->detail == NotifyNonlinearVirtual)
2612 /* pointer left screen */
2613 BroadcastPacket(
2614 MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2615 (long)NULL);
2617 /* Ignore LeaveNotify events while a window is resized or moved as a
2618 * wire frame; otherwise the window list may be screwed up. */
2619 if (Scr.flags.is_wire_frame_displayed)
2621 return;
2623 if (lwp->mode != NotifyNormal)
2625 /* Ignore events generated by grabbing or ungrabbing the
2626 * pointer. However, there is no way to prevent the client
2627 * application from handling this event and, for example,
2628 * grabbing the focus. This will interfere with functions that
2629 * transferred the focus to a different window. It is
2630 * necessary to check for LeaveNotify events on the client
2631 * window too in case buttons are not grabbed on it. */
2632 if (lwp->mode == NotifyGrab && fw &&
2633 (lwp->window == FW_W_FRAME(fw) ||
2634 lwp->window == FW_W(fw) ||
2635 lwp->window == FW_W_ICON_TITLE(fw) ||
2636 lwp->window == FW_W_ICON_PIXMAP(fw)))
2638 ENTER_DBG((stderr, "ln: *** lgw = 0x%08x\n", (int)fw));
2639 xcrossing_last_grab_window = fw;
2641 #ifdef FOCUS_EXPANDS_TITLE
2642 if (fw && IS_ICONIFIED(fw))
2644 SET_ICON_ENTERED(fw, 0);
2645 DrawIconWindow(
2646 fw, True, False, False, False, NULL);
2648 #endif
2649 return;
2651 /* CDE-like behaviour of raising the icon title if the icon
2652 gets the focus (in particular if the cursor is over the icon) */
2653 if (fw && IS_ICONIFIED(fw))
2655 SET_ICON_ENTERED(fw,0);
2656 DrawIconWindow(fw, True, False, False, False, NULL);
2659 /* An LeaveEvent in one of the PanFrameWindows activates
2660 an EdgeLeaveCommand. */
2661 if (is_pan_frame(lwp->window))
2663 char *edge_command_leave = NULL;
2665 /* check for edge commands */
2666 if (lwp->window == Scr.PanFrameTop.win)
2668 edge_command_leave = Scr.PanFrameTop.command_leave;
2670 else if (lwp->window == Scr.PanFrameBottom.win)
2672 edge_command_leave = Scr.PanFrameBottom.command_leave;
2674 else if (lwp->window == Scr.PanFrameLeft.win)
2676 edge_command_leave = Scr.PanFrameLeft.command_leave;
2678 else if (lwp->window == Scr.PanFrameRight.win)
2680 edge_command_leave = Scr.PanFrameRight.command_leave;
2682 if (edge_command_leave && lwp->mode == NotifyUngrab &&
2683 lwp->detail == NotifyAncestor)
2685 /* nothing */
2687 else if (edge_command_leave)
2689 execute_function(NULL, ea->exc, edge_command_leave, 0);
2694 /* If we leave the root window, then we're really moving
2695 * another screen on a multiple screen display, and we
2696 * need to de-focus and unhighlight to make sure that we
2697 * don't end up with more than one highlighted window at a time */
2698 if (lwp->window == Scr.Root &&
2699 /* domivogt (16-May-2000): added this test because somehow fvwm
2700 * sometimes gets a LeaveNotify on the root window although it is
2701 * single screen. */
2702 Scr.NumberOfScreens > 1)
2704 if (lwp->mode == NotifyNormal)
2706 if (lwp->detail != NotifyInferior)
2708 FvwmWindow *sf = get_focus_window();
2710 Scr.flags.is_pointer_on_this_screen = 0;
2711 set_last_screen_focus_window(sf);
2712 if (sf != NULL)
2714 DeleteFocus(True);
2716 if (Scr.Hilite != NULL)
2718 border_draw_decorations(
2719 Scr.Hilite, PART_ALL, False,
2720 True, CLEAR_ALL, NULL, NULL);
2725 else
2727 /* handle a subwindow cmap */
2728 LeaveSubWindowColormap(te->xany.window);
2730 if (fw != NULL &&
2731 (lwp->window == FW_W_FRAME(fw) ||
2732 lwp->window == FW_W_ICON_TITLE(fw) ||
2733 lwp->window == FW_W_ICON_PIXMAP(fw)))
2735 BroadcastPacket(
2736 MX_LEAVE_WINDOW, 3, (long)FW_W(fw),
2737 (long)FW_W_FRAME(fw), (unsigned long)fw);
2740 return;
2743 void HandleMapNotify(const evh_args_t *ea)
2745 Bool is_on_this_page = False;
2746 const XEvent *te = ea->exc->x.etrigger;
2747 FvwmWindow * const fw = ea->exc->w.fw;
2749 DBUG("HandleMapNotify", "Routine Entered");
2751 if (!fw)
2753 if (te->xmap.override_redirect == True &&
2754 te->xmap.window != Scr.NoFocusWin)
2756 XSelectInput(dpy, te->xmap.window, XEVMASK_ORW);
2757 XFlush(dpy);
2758 Scr.UnknownWinFocused = te->xmap.window;
2760 return;
2762 if (te->xmap.window == FW_W_FRAME(fw))
2764 /* Now that we know the frame is mapped after capturing the
2765 * window we do not need StructureNotifyMask events anymore. */
2766 XSelectInput(dpy, FW_W_FRAME(fw), XEVMASK_FRAMEW);
2767 XFlush(dpy);
2769 /* Except for identifying over-ride redirect window mappings, we
2770 * don't need or want windows associated with the
2771 * SubstructureNotifyMask */
2772 if (te->xmap.event != te->xmap.window)
2774 return;
2776 SET_MAP_PENDING(fw, 0);
2777 /* don't map if the event was caused by a de-iconify */
2778 if (IS_ICONIFY_PENDING(fw))
2780 return;
2783 /* Make sure at least part of window is on this page before giving it
2784 * focus... */
2785 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2788 * Need to do the grab to avoid race condition of having server send
2789 * MapNotify to client before the frame gets mapped; this is bad because
2790 * the client would think that the window has a chance of being viewable
2791 * when it really isn't.
2793 MyXGrabServer (dpy);
2794 if (FW_W_ICON_TITLE(fw))
2796 XUnmapWindow(dpy, FW_W_ICON_TITLE(fw));
2798 if (FW_W_ICON_PIXMAP(fw) != None)
2800 XUnmapWindow(dpy, FW_W_ICON_PIXMAP(fw));
2802 XMapSubwindows(dpy, FW_W_FRAME(fw));
2803 if (fw->Desk == Scr.CurrentDesk)
2805 XMapWindow(dpy, FW_W_FRAME(fw));
2807 if (IS_ICONIFIED(fw))
2809 BroadcastPacket(
2810 M_DEICONIFY, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2811 (unsigned long)fw);
2813 else
2815 BroadcastPacket(
2816 M_MAP, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2817 (unsigned long)fw);
2820 if (is_on_this_page &&
2821 focus_query_open_grab_focus(fw, get_focus_window()) == True)
2823 SetFocusWindow(fw, True, FOCUS_SET_FORCE);
2825 border_draw_decorations(
2826 fw, PART_ALL, (fw == get_focus_window()) ? True : False, True,
2827 CLEAR_ALL, NULL, NULL);
2828 MyXUngrabServer (dpy);
2829 SET_MAPPED(fw, 1);
2830 SET_ICONIFIED(fw, 0);
2831 SET_ICON_UNMAPPED(fw, 0);
2832 if (DO_ICONIFY_AFTER_MAP(fw))
2834 initial_window_options_t win_opts;
2836 /* finally, if iconification was requested before the window
2837 * was mapped, request it now. */
2838 memset(&win_opts, 0, sizeof(win_opts));
2839 Iconify(fw, &win_opts);
2840 SET_ICONIFY_AFTER_MAP(fw, 0);
2842 focus_grab_buttons_on_layer(fw->layer);
2844 return;
2847 void HandleMappingNotify(const evh_args_t *ea)
2849 XRefreshKeyboardMapping(&ea->exc->x.etrigger->xmapping);
2851 return;
2854 void HandleMapRequest(const evh_args_t *ea)
2856 DBUG("HandleMapRequest", "Routine Entered");
2858 if (fFvwmInStartup)
2860 /* Just map the damn thing, decorations are added later
2861 * in CaptureAllWindows. */
2862 XMapWindow(dpy, ea->exc->x.etrigger->xmaprequest.window);
2863 return;
2865 HandleMapRequestKeepRaised(ea, None, NULL, NULL);
2867 return;
2870 void HandleMapRequestKeepRaised(
2871 const evh_args_t *ea, Window KeepRaised, FvwmWindow *ReuseWin,
2872 initial_window_options_t *win_opts)
2874 Bool is_on_this_page = False;
2875 Bool is_new_window = False;
2876 FvwmWindow *tmp;
2877 FvwmWindow *sf;
2878 initial_window_options_t win_opts_bak;
2879 Window ew;
2880 FvwmWindow *fw;
2881 extern Bool Restarting;
2882 const char *initial_map_command;
2884 initial_map_command = NULL;
2885 if (win_opts == NULL)
2887 memset(&win_opts_bak, 0, sizeof(win_opts_bak));
2888 win_opts = &win_opts_bak;
2890 ew = ea->exc->w.w;
2891 if (ReuseWin == NULL)
2893 Window pw;
2895 pw = ea->exc->x.etrigger->xmaprequest.parent;
2896 if (XFindContext(dpy, ew, FvwmContext, (caddr_t *)&fw) ==
2897 XCNOENT)
2899 fw = NULL;
2901 if (fw != NULL && IS_MAP_PENDING(fw))
2903 /* The window is already going to be mapped, no need to
2904 * do that twice */
2905 return;
2908 else
2910 fw = ReuseWin;
2913 if (fw == NULL && EWMH_IsKdeSysTrayWindow(ew))
2915 /* This means that the window is swallowed by kicker and that
2916 * kicker restart or exit. As we should assume that kicker
2917 * restart we should return here, if not we go into trouble
2918 * ... */
2919 return;
2921 if (!win_opts->flags.do_override_ppos)
2923 XFlush(dpy);
2926 /* If the window has never been mapped before ... */
2927 if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
2929 check_if_event_args args;
2930 XEvent dummy;
2932 args.w = ew;
2933 args.do_return_true = True;
2934 args.do_return_true_cr = False;
2935 if (
2936 FCheckIfEvent(
2937 dpy, &dummy, test_withdraw_request,
2938 (XPointer)&args)) {
2939 /* The window is moved back to the WithdrawnState
2940 * immideately. Don't map it.
2942 * However, send make sure that a WM_STATE
2943 * PropertyNotify event is sent to the window.
2944 * QT needs this.
2946 Atom atype;
2947 int aformat;
2948 unsigned long nitems, bytes_remain;
2949 unsigned char *prop;
2951 if (
2952 XGetWindowProperty(
2953 dpy, ew, _XA_WM_STATE, 0L, 3L, False,
2954 _XA_WM_STATE, &atype, &aformat,
2955 &nitems,&bytes_remain,&prop)
2956 == Success)
2958 if (prop != NULL)
2960 XFree(prop);
2961 XDeleteProperty(dpy, ew, _XA_WM_STATE);
2963 else
2965 XPropertyEvent ev;
2966 ev.type = PropertyNotify;
2967 ev.display = dpy;
2968 ev.window = ew;
2969 ev.atom = _XA_WM_STATE;
2970 ev.time = fev_get_evtime();
2971 ev.state = PropertyDelete;
2972 FSendEvent(
2973 dpy, ew, True,
2974 PropertyChangeMask,
2975 (XEvent*)&ev);
2979 return;
2982 /* Add decorations. */
2983 fw = AddWindow(
2984 &initial_map_command, ea->exc, ReuseWin, win_opts);
2985 if (fw == AW_NO_WINDOW)
2987 return;
2989 else if (fw == AW_UNMANAGED)
2991 XMapWindow(dpy, ew);
2992 return;
2994 is_new_window = True;
2997 * Make sure at least part of window is on this page
2998 * before giving it focus...
3000 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3001 if (KeepRaised != None)
3003 XRaiseWindow(dpy, KeepRaised);
3005 /* If it's not merely iconified, and we have hints, use them. */
3007 if (IS_ICONIFIED(fw))
3009 /* If no hints, or currently an icon, just "deiconify" */
3010 DeIconify(fw);
3012 else if (IS_MAPPED(fw))
3014 /* the window is already mapped - fake a MapNotify event */
3015 fake_map_unmap_notify(fw, MapNotify);
3017 else
3019 int state;
3021 if (fw->wmhints && (fw->wmhints->flags & StateHint))
3023 state = fw->wmhints->initial_state;
3025 else
3027 state = NormalState;
3029 if (win_opts->initial_state != DontCareState)
3031 state = win_opts->initial_state;
3034 switch (state)
3036 case DontCareState:
3037 case NormalState:
3038 case InactiveState:
3039 default:
3040 MyXGrabServer(dpy);
3041 if (fw->Desk == Scr.CurrentDesk)
3043 Bool do_grab_focus;
3045 SET_MAP_PENDING(fw, 1);
3046 XMapWindow(dpy, FW_W_FRAME(fw));
3047 XMapWindow(dpy, FW_W(fw));
3048 SetMapStateProp(fw, NormalState);
3049 if (Scr.flags.is_map_desk_in_progress)
3051 do_grab_focus = False;
3053 else if (!is_on_this_page)
3055 do_grab_focus = False;
3057 else if (focus_query_open_grab_focus(
3058 fw, get_focus_window()) ==
3059 True)
3061 do_grab_focus = True;
3063 else
3065 do_grab_focus = False;
3067 if (do_grab_focus)
3069 SetFocusWindow(
3070 fw, True, FOCUS_SET_FORCE);
3072 else
3074 /* make sure the old focused window
3075 * still has grabbed all necessary
3076 * buttons. */
3077 focus_grab_buttons(
3078 get_focus_window());
3081 else
3083 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
3084 /* nope, this is forbidden by the ICCCM2 */
3085 XMapWindow(dpy, FW_W(fw));
3086 SetMapStateProp(fw, NormalState);
3087 #else
3088 /* Since we will not get a MapNotify, set the
3089 * IS_MAPPED flag manually. */
3090 SET_MAPPED(fw, 1);
3091 SetMapStateProp(fw, IconicState);
3092 /* fake that the window was mapped to allow
3093 * modules to swallow it */
3094 BroadcastPacket(
3095 M_MAP, 3, (long)FW_W(fw),
3096 (long)FW_W_FRAME(fw),
3097 (unsigned long)fw);
3098 #endif
3100 /* TA: 20090125: We *have* to handle
3101 * InitialMapCommand here and not in AddWindow() to
3102 * allow for correct timings when the window is truly
3103 * mapped. (c.f. things like Iconify.)
3106 /* TA: 20091212: But only do this when we're *not*
3107 * restarting -- the window is still mapped, but gets
3108 * recaptured -- we don't want to trigger this event
3109 * again. Otherwise we end up toggling the state of
3110 * the window in situations where the
3111 * InitialMapCommand is Iconify or Maximize, for
3112 * instance.
3114 if ((initial_map_command != NULL) &&
3115 (!Restarting && Scr.flags.are_windows_captured))
3117 execute_function_override_window(
3118 NULL, ea->exc,
3119 (char *)initial_map_command, 0, fw);
3121 MyXUngrabServer(dpy);
3122 break;
3124 case IconicState:
3125 if (is_new_window)
3127 /* the window will not be mapped - fake a
3128 * MapNotify and an UnmapNotify event. Can't
3129 * remember exactly why this is necessary, but
3130 * probably something w/ (de)iconify state
3131 * confusion. */
3132 fake_map_unmap_notify(fw, MapNotify);
3133 fake_map_unmap_notify(fw, UnmapNotify);
3135 if (win_opts->flags.is_iconified_by_parent ||
3136 ((tmp = get_transientfor_fvwmwindow(fw)) &&
3137 IS_ICONIFIED(tmp)))
3139 win_opts->flags.is_iconified_by_parent = 0;
3140 SET_ICONIFIED_BY_PARENT(fw, 1);
3142 if (USE_ICON_POSITION_HINT(fw) && fw->wmhints &&
3143 (fw->wmhints->flags & IconPositionHint))
3145 win_opts->default_icon_x = fw->wmhints->icon_x;
3146 win_opts->default_icon_y = fw->wmhints->icon_y;
3148 Iconify(fw, win_opts);
3149 break;
3152 if (IS_SHADED(fw))
3154 BroadcastPacket(
3155 M_WINDOWSHADE, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
3156 (unsigned long)fw);
3158 /* If the newly mapped window overlaps the focused window, make sure
3159 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3160 sf = get_focus_window();
3161 if (sf != NULL)
3163 focus_grab_buttons(sf);
3165 if (win_opts->flags.is_menu)
3167 SET_MAPPED(fw, 1);
3168 SET_MAP_PENDING(fw, 0);
3170 EWMH_SetClientList();
3171 EWMH_SetClientListStacking();
3172 GNOME_SetClientList();
3174 return;
3177 #ifdef HAVE_STROKE
3178 void HandleMotionNotify(const evh_args_t *ea)
3180 DBUG("HandleMotionNotify", "Routine Entered");
3182 if (send_motion == True)
3184 stroke_record(
3185 ea->exc->x.etrigger->xmotion.x,
3186 ea->exc->x.etrigger->xmotion.y);
3189 return;
3191 #endif /* HAVE_STROKE */
3193 void HandlePropertyNotify(const evh_args_t *ea)
3195 Bool OnThisPage = False;
3196 Bool has_icon_changed = False;
3197 Bool has_icon_pixmap_hint_changed = False;
3198 Bool has_icon_window_hint_changed = False;
3199 FlocaleNameString new_name = { NoName, NULL };
3200 int old_wmhints_flags;
3201 const XEvent *te = ea->exc->x.etrigger;
3202 char *urgency_action = NULL;
3203 FvwmWindow * const fw = ea->exc->w.fw;
3205 DBUG("HandlePropertyNotify", "Routine Entered");
3207 if (te->xproperty.window == Scr.Root &&
3208 te->xproperty.state == PropertyNewValue &&
3209 (te->xproperty.atom == _XA_XSETROOT_ID ||
3210 te->xproperty.atom == _XA_XROOTPMAP_ID))
3212 /* background change */
3213 /* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3214 * no property notify?). _XA_XROOTPMAP_ID is used by Esetroot
3215 * compatible program: the problem here is that with some
3216 * Esetroot compatible program we get the message _before_ the
3217 * background change. This is fixed with Esetroot 9.2 (not yet
3218 * released, 2002-01-14) */
3220 /* update icon window with some alpha and tear-off menu */
3221 FvwmWindow *t;
3223 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3225 int cs;
3226 int t_cs = -1;
3227 int b_cs = t->icon_background_cs;
3228 Bool draw_picture = False;
3229 Bool draw_title = False;
3231 /* redraw ParentRelative tear-off menu */
3232 menu_redraw_transparent_tear_off_menu(t, True);
3234 if (!IS_ICONIFIED(t) || IS_ICON_SUPPRESSED(t))
3236 continue;
3238 if (Scr.Hilite == t)
3240 if (t->icon_title_cs_hi >= 0)
3242 t_cs = cs = t->icon_title_cs_hi;
3244 else
3246 cs = t->cs_hi;
3249 else
3251 if (t->icon_title_cs >= 0)
3253 t_cs = cs = t->icon_title_cs;
3255 else
3257 cs = t->cs;
3260 if (t->icon_alphaPixmap != None ||
3261 (cs >= 0 &&
3262 Colorset[cs].icon_alpha_percent < 100) ||
3263 CSET_IS_TRANSPARENT_PR(b_cs) ||
3264 (!IS_ICON_SHAPED(t) &&
3265 t->icon_background_padding > 0))
3267 draw_picture = True;
3269 if (CSET_IS_TRANSPARENT_PR(t_cs))
3271 draw_title = True;
3273 if (draw_title || draw_picture)
3275 DrawIconWindow(
3276 t, draw_title, draw_picture, False,
3277 draw_picture, NULL);
3280 if (te->xproperty.atom == _XA_XROOTPMAP_ID)
3282 update_root_transparent_colorset(te->xproperty.atom);
3284 BroadcastPropertyChange(
3285 MX_PROPERTY_CHANGE_BACKGROUND, 0, 0, "");
3286 return;
3289 if (!fw)
3291 return;
3293 if (XGetGeometry(
3294 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3295 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
3296 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
3298 return;
3302 * Make sure at least part of window is on this page
3303 * before giving it focus...
3305 OnThisPage = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3307 switch (te->xproperty.atom)
3309 case XA_WM_TRANSIENT_FOR:
3310 flush_property_notify(XA_WM_TRANSIENT_FOR, FW_W(fw));
3311 if (setup_transientfor(fw) == True)
3313 RaiseWindow(fw, False);
3315 break;
3317 case XA_WM_NAME:
3318 flush_property_notify(XA_WM_NAME, FW_W(fw));
3319 if (HAS_EWMH_WM_NAME(fw))
3321 return;
3323 FlocaleGetNameProperty(XGetWMName, dpy, FW_W(fw), &new_name);
3324 if (new_name.name == NULL)
3326 FlocaleFreeNameProperty(&new_name);
3327 return;
3329 if (strlen(new_name.name) > MAX_WINDOW_NAME_LEN)
3331 /* limit to prevent hanging X server */
3332 (new_name.name)[MAX_WINDOW_NAME_LEN] = 0;
3334 if (fw->name.name && strcmp(new_name.name, fw->name.name) == 0)
3336 /* migo: some apps update their names every second */
3337 /* griph: make sure we don't free the property if it
3338 is THE same name */
3339 if (new_name.name != fw->name.name)
3341 FlocaleFreeNameProperty(&new_name);
3343 return;
3346 free_window_names(fw, True, False);
3347 fw->name = new_name;
3348 SET_NAME_CHANGED(fw, 1);
3349 if (fw->name.name == NULL)
3351 fw->name.name = NoName; /* must not happen */
3353 setup_visible_name(fw, False);
3354 BroadcastWindowIconNames(fw, True, False);
3356 /* fix the name in the title bar */
3357 if (!IS_ICONIFIED(fw))
3359 border_draw_decorations(
3360 fw, PART_TITLE, (Scr.Hilite == fw), True,
3361 CLEAR_ALL, NULL, NULL);
3363 EWMH_SetVisibleName(fw, False);
3365 * if the icon name is NoName, set the name of the icon to be
3366 * the same as the window
3368 if (!WAS_ICON_NAME_PROVIDED(fw))
3370 fw->icon_name = fw->name;
3371 setup_visible_name(fw, True);
3372 BroadcastWindowIconNames(fw, False, True);
3373 RedoIconName(fw);
3375 break;
3377 case XA_WM_ICON_NAME:
3378 flush_property_notify(XA_WM_ICON_NAME, FW_W(fw));
3379 if (HAS_EWMH_WM_ICON_NAME(fw))
3381 return;
3383 FlocaleGetNameProperty(
3384 XGetWMIconName, dpy, FW_W(fw), &new_name);
3385 if (new_name.name == NULL)
3387 FlocaleFreeNameProperty(&new_name);
3388 return;
3390 if (new_name.name && strlen(new_name.name) > MAX_ICON_NAME_LEN)
3392 /* limit to prevent hanging X server */
3393 (new_name.name)[MAX_ICON_NAME_LEN] = 0;
3395 if (fw->icon_name.name &&
3396 strcmp(new_name.name, fw->icon_name.name) == 0)
3398 /* migo: some apps update their names every second */
3399 /* griph: make sure we don't free the property if it
3400 is THE same name */
3401 if (new_name.name != fw->icon_name.name)
3403 FlocaleFreeNameProperty(&new_name);
3405 return;
3408 free_window_names(fw, False, True);
3409 fw->icon_name = new_name;
3410 SET_WAS_ICON_NAME_PROVIDED(fw, 1);
3411 if (fw->icon_name.name == NULL)
3413 /* currently never happens */
3414 fw->icon_name.name = fw->name.name;
3415 SET_WAS_ICON_NAME_PROVIDED(fw, 0);
3417 setup_visible_name(fw, True);
3418 BroadcastWindowIconNames(fw, False, True);
3419 RedoIconName(fw);
3420 EWMH_SetVisibleName(fw, True);
3421 break;
3423 case XA_WM_HINTS:
3424 flush_property_notify(XA_WM_HINTS, FW_W(fw));
3425 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3426 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3428 old_wmhints_flags = 0;
3429 if (fw->wmhints)
3431 old_wmhints_flags = fw->wmhints->flags;
3432 XFree ((char *) fw->wmhints);
3434 setup_wm_hints(fw);
3435 if (fw->wmhints == NULL)
3437 return;
3441 * rebuild icon if the client either provides an icon
3442 * pixmap or window or has reset the hints to `no icon'.
3444 if ((fw->wmhints->flags & IconPixmapHint) ||
3445 (old_wmhints_flags & IconPixmapHint))
3447 ICON_DBG((stderr, "hpn: iph changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconPixmapHint), fw->name));
3448 has_icon_pixmap_hint_changed = True;
3450 if ((fw->wmhints->flags & IconWindowHint) ||
3451 (old_wmhints_flags & IconWindowHint))
3453 ICON_DBG((stderr, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconWindowHint), fw->name));
3454 has_icon_window_hint_changed = True;
3455 SET_USE_EWMH_ICON(fw, False);
3457 increase_icon_hint_count(fw);
3458 if (has_icon_window_hint_changed ||
3459 has_icon_pixmap_hint_changed)
3461 if (ICON_OVERRIDE_MODE(fw) == ICON_OVERRIDE)
3463 ICON_DBG((stderr, "hpn: icon override '%s'\n", fw->name));
3464 has_icon_changed = False;
3466 else if (ICON_OVERRIDE_MODE(fw) ==
3467 NO_ACTIVE_ICON_OVERRIDE)
3469 if (has_icon_pixmap_hint_changed)
3471 if (WAS_ICON_HINT_PROVIDED(fw) ==
3472 ICON_HINT_MULTIPLE)
3474 ICON_DBG((stderr, "hpn: using further iph '%s'\n", fw->name));
3475 has_icon_changed = True;
3477 else if (fw->icon_bitmap_file ==
3478 NULL ||
3479 fw->icon_bitmap_file ==
3480 Scr.DefaultIcon)
3482 ICON_DBG((stderr, "hpn: using first iph '%s'\n", fw->name));
3483 has_icon_changed = True;
3485 else
3487 /* ignore the first icon pixmap
3488 * hint if the application did
3489 * not provide it from the
3490 * start */
3491 ICON_DBG((stderr, "hpn: first iph ignored '%s'\n", fw->name));
3492 has_icon_changed = False;
3495 else if (has_icon_window_hint_changed)
3497 ICON_DBG((stderr, "hpn: using iwh '%s'\n", fw->name));
3498 has_icon_changed = True;
3500 else
3502 ICON_DBG((stderr, "hpn: iwh not changed, hint ignored '%s'\n", fw->name));
3503 has_icon_changed = False;
3506 else /* NO_ICON_OVERRIDE */
3508 ICON_DBG((stderr, "hpn: using hint '%s'\n", fw->name));
3509 has_icon_changed = True;
3512 if (USE_EWMH_ICON(fw))
3514 has_icon_changed = False;
3517 if (has_icon_changed)
3519 ICON_DBG((stderr, "hpn: icon changed '%s'\n", fw->name));
3520 /* Okay, the icon hint has changed and style
3521 * options tell us to honour this change. Now
3522 * let's see if we have to use the application
3523 * provided pixmap or window (if any), the icon
3524 * file provided by the window's style or the
3525 * default style's icon. */
3526 if (fw->icon_bitmap_file == Scr.DefaultIcon)
3528 fw->icon_bitmap_file = NULL;
3530 if (!fw->icon_bitmap_file &&
3531 !(fw->wmhints->flags &
3532 (IconPixmapHint|IconWindowHint)))
3534 fw->icon_bitmap_file =
3535 (Scr.DefaultIcon) ?
3536 Scr.DefaultIcon : NULL;
3538 fw->iconPixmap = (Window)NULL;
3539 ChangeIconPixmap(fw);
3543 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3544 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3545 * Treat urgency changes by calling user-settable functions.
3546 * These could e.g. deiconify and raise the window or
3547 * temporarily change the decor. */
3548 if (!(old_wmhints_flags & XUrgencyHint) &&
3549 (fw->wmhints->flags & XUrgencyHint))
3551 urgency_action = "Function UrgencyFunc";
3553 if ((old_wmhints_flags & XUrgencyHint) &&
3554 !(fw->wmhints->flags & XUrgencyHint))
3556 urgency_action = "Function UrgencyDoneFunc";
3558 if (urgency_action)
3560 const exec_context_t *exc;
3561 exec_context_changes_t ecc;
3563 ecc.w.fw = fw;
3564 ecc.w.wcontext = C_WINDOW;
3565 exc = exc_clone_context(
3566 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
3567 execute_function(NULL, exc, urgency_action, 0);
3568 exc_destroy_context(exc);
3570 break;
3571 case XA_WM_NORMAL_HINTS:
3572 /* just mark wm normal hints as changed and look them up when
3573 * the next ConfigureRequest w/ x, y, width or height set
3574 * arrives. */
3575 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 1);
3576 break;
3577 default:
3578 if (te->xproperty.atom == _XA_WM_PROTOCOLS)
3580 FetchWmProtocols (fw);
3582 else if (te->xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
3584 FetchWmColormapWindows (fw); /* frees old data */
3585 ReInstallActiveColormap();
3587 else if (te->xproperty.atom == _XA_WM_STATE)
3589 if (fw && OnThisPage && focus_is_focused(fw) &&
3590 FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
3592 /* refresh the focus - why? */
3593 focus_force_refresh_focus(fw);
3596 else
3598 EWMH_ProcessPropertyNotify(ea->exc);
3600 break;
3604 void HandleReparentNotify(const evh_args_t *ea)
3606 const XEvent *te = ea->exc->x.etrigger;
3607 FvwmWindow * const fw = ea->exc->w.fw;
3609 if (!fw)
3611 return;
3613 if (te->xreparent.parent == Scr.Root)
3615 /* Ignore reparenting to the root window. In some cases these
3616 * events are selected although the window is no longer
3617 * managed. */
3618 return;
3620 if (te->xreparent.parent != FW_W_FRAME(fw))
3622 /* window was reparented by someone else, destroy the frame */
3623 SetMapStateProp(fw, WithdrawnState);
3624 EWMH_RestoreInitialStates(fw, te->type);
3625 if (!IS_TEAR_OFF_MENU(fw))
3627 XRemoveFromSaveSet(dpy, te->xreparent.window);
3628 XSelectInput(dpy, te->xreparent.window, NoEventMask);
3630 else
3632 XSelectInput(dpy, te->xreparent.window, XEVMASK_MENUW);
3634 discard_events(XEVMASK_FRAMEW);
3635 destroy_window(fw);
3636 EWMH_ManageKdeSysTray(te->xreparent.window, te->type);
3637 EWMH_WindowDestroyed();
3640 return;
3643 void HandleSelectionRequest(const evh_args_t *ea)
3645 icccm2_handle_selection_request(ea->exc->x.etrigger);
3647 return;
3650 void HandleSelectionClear(const evh_args_t *ea)
3652 icccm2_handle_selection_clear();
3654 return;
3657 void HandleShapeNotify(const evh_args_t *ea)
3659 FvwmWindow * const fw = ea->exc->w.fw;
3661 DBUG("HandleShapeNotify", "Routine Entered");
3663 if (FShapesSupported)
3665 const FShapeEvent *sev =
3666 (const FShapeEvent *)(ea->exc->x.etrigger);
3668 if (!fw)
3670 return;
3672 if (sev->kind != FShapeBounding)
3674 return;
3676 frame_setup_shape(
3677 fw, fw->g.frame.width, fw->g.frame.height, sev->shaped);
3678 GNOME_SetWinArea(fw);
3679 EWMH_SetFrameStrut(fw);
3680 if (!IS_ICONIFIED(fw))
3682 border_redraw_decorations(fw);
3686 return;
3689 void HandleUnmapNotify(const evh_args_t *ea)
3691 int dstx, dsty;
3692 Window dumwin;
3693 XEvent dummy;
3694 XEvent map_event;
3695 const XEvent *te = ea->exc->x.etrigger;
3696 int weMustUnmap;
3697 Bool focus_grabbed;
3698 Bool must_return = False;
3699 Bool do_map = False;
3700 FvwmWindow * const fw = ea->exc->w.fw;
3701 Window pw;
3702 Window cw;
3704 DBUG("HandleUnmapNotify", "Routine Entered");
3706 /* Don't ignore events as described below. */
3707 if (te->xunmap.event != te->xunmap.window &&
3708 (te->xunmap.event != Scr.Root || !te->xunmap.send_event))
3710 must_return = True;
3714 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3715 * to WithdrawnState should send a synthetic UnmapNotify with the
3716 * event field set to (pseudo-)root, in case the window is already
3717 * unmapped (which is the case for fvwm for IconicState).
3718 * Unfortunately, we looked for the FvwmContext using that field, so
3719 * try the window field also. */
3720 weMustUnmap = 0;
3721 if (!fw)
3723 weMustUnmap = 1;
3724 if (XFindContext(
3725 dpy, te->xunmap.window, FvwmContext,
3726 (caddr_t *)&fw) == XCNOENT)
3728 return;
3731 cw = FW_W(fw);
3732 pw = FW_W_PARENT(fw);
3733 if (te->xunmap.window == FW_W_FRAME(fw))
3735 SET_ICONIFY_PENDING(fw , 0);
3736 return;
3738 if (must_return)
3740 return;
3743 if (weMustUnmap)
3745 Bool is_map_request_pending;
3746 check_if_event_args args;
3748 args.w = te->xunmap.window;
3749 args.do_return_true = False;
3750 args.do_return_true_cr = False;
3751 /* Using FCheckTypedWindowEvent() does not work here. I don't
3752 * have the slightest idea why, but using FCheckIfEvent() with
3753 * the appropriate predicate procedure works fine. */
3754 FCheckIfEvent(dpy, &dummy, test_map_request, (XPointer)&args);
3755 /* Unfortunately, there is no procedure in X that simply tests
3756 * if an event of a certain type in on the queue without
3757 * waiting and without removing it from the queue.
3758 * XCheck...Event() does not wait but removes the event while
3759 * XPeek...() does not remove the event but waits. To solve
3760 * this, the predicate procedure sets a flag in the passed in
3761 * structure and returns False unconditionally. */
3762 is_map_request_pending = (args.ret_does_match == True);
3763 if (!is_map_request_pending)
3765 XUnmapWindow(dpy, te->xunmap.window);
3768 if (fw == Scr.Hilite)
3770 Scr.Hilite = NULL;
3772 focus_grabbed = focus_query_close_release_focus(fw);
3773 restore_focus_after_unmap(fw, False);
3774 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
3776 return;
3780 * The program may have unmapped the client window, from either
3781 * NormalState or IconicState. Handle the transition to WithdrawnState.
3783 * We need to reparent the window back to the root (so that fvwm exiting
3784 * won't cause it to get mapped) and then throw away all state (pretend
3785 * that we've received a DestroyNotify).
3787 if (!FCheckTypedWindowEvent(
3788 dpy, te->xunmap.window, DestroyNotify, &dummy) &&
3789 XTranslateCoordinates(
3790 dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
3791 &dumwin))
3793 MyXGrabServer(dpy);
3794 SetMapStateProp(fw, WithdrawnState);
3795 EWMH_RestoreInitialStates(fw, te->type);
3796 if (FCheckTypedWindowEvent(
3797 dpy, te->xunmap.window, ReparentNotify, &dummy))
3799 if (fw->attr_backup.border_width)
3801 XSetWindowBorderWidth(
3802 dpy, te->xunmap.window,
3803 fw->attr_backup.border_width);
3805 if ((!IS_ICON_SUPPRESSED(fw))&&
3806 (fw->wmhints &&
3807 (fw->wmhints->flags & IconWindowHint)))
3809 XUnmapWindow(dpy, fw->wmhints->icon_window);
3812 else
3814 RestoreWithdrawnLocation(fw, False, Scr.Root);
3816 if (!IS_TEAR_OFF_MENU(fw))
3818 XRemoveFromSaveSet(dpy, te->xunmap.window);
3819 XSelectInput(dpy, te->xunmap.window, NoEventMask);
3821 XSync(dpy, 0);
3822 MyXUngrabServer(dpy);
3823 if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
3825 /* the client tried to map the window again while it
3826 * was still inside the decoration windows */
3827 do_map = True;
3830 destroy_window(fw);
3831 if (focus_grabbed == True)
3833 CoerceEnterNotifyOnCurrentWindow();
3835 EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
3836 EWMH_WindowDestroyed();
3837 GNOME_SetClientList();
3838 if (do_map == True)
3840 map_event.xmaprequest.window = cw;
3841 map_event.xmaprequest.parent = Scr.Root;
3842 dispatch_event(&map_event);
3843 /* note: we really should handle all map and unmap notify
3844 * events for that window in a loop here */
3847 return;
3850 void HandleVisibilityNotify(const evh_args_t *ea)
3852 FvwmWindow * const fw = ea->exc->w.fw;
3854 DBUG("HandleVisibilityNotify", "Routine Entered");
3856 if (fw && ea->exc->x.etrigger->xvisibility.window == FW_W_FRAME(fw))
3858 switch (ea->exc->x.etrigger->xvisibility.state)
3860 case VisibilityUnobscured:
3861 SET_FULLY_VISIBLE(fw, 1);
3862 SET_PARTIALLY_VISIBLE(fw, 1);
3863 break;
3864 case VisibilityPartiallyObscured:
3865 SET_FULLY_VISIBLE(fw, 0);
3866 SET_PARTIALLY_VISIBLE(fw, 1);
3867 break;
3868 default:
3869 SET_FULLY_VISIBLE(fw, 0);
3870 SET_PARTIALLY_VISIBLE(fw, 0);
3871 break;
3873 /* Make sure the button grabs are up to date */
3874 focus_grab_buttons(fw);
3877 return;
3880 /* ---------------------------- interface functions ------------------------ */
3882 /* Inform a client window of its geometry.
3884 * The input (frame) geometry will be translated to client geometry
3885 * before sending. */
3886 void SendConfigureNotify(
3887 FvwmWindow *fw, int x, int y, int w, int h, int bw,
3888 Bool send_for_frame_too)
3890 XEvent client_event;
3891 size_borders b;
3893 if (!fw || IS_SHADED(fw))
3895 return;
3897 client_event.type = ConfigureNotify;
3898 client_event.xconfigure.display = dpy;
3899 client_event.xconfigure.event = FW_W(fw);
3900 client_event.xconfigure.window = FW_W(fw);
3901 get_window_borders(fw, &b);
3902 client_event.xconfigure.x = x + b.top_left.width;
3903 client_event.xconfigure.y = y + b.top_left.height;
3904 client_event.xconfigure.width = w - b.total_size.width;
3905 client_event.xconfigure.height = h - b.total_size.height;
3906 client_event.xconfigure.border_width = bw;
3907 client_event.xconfigure.above = FW_W_FRAME(fw);
3908 client_event.xconfigure.override_redirect = False;
3909 #if 0
3910 fprintf(stderr,
3911 "send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x '%s'\n",
3912 client_event.xconfigure.x, client_event.xconfigure.y,
3913 client_event.xconfigure.width, client_event.xconfigure.height,
3914 (int)FW_W_FRAME(fw), (int)FW_W(fw),
3915 (int)client_event.xconfigure.window,
3916 (fw->name.name) ? fw->name.name : "");
3917 #endif
3918 FSendEvent(
3919 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
3920 if (send_for_frame_too)
3922 /* This is for buggy tk, which waits for the real
3923 * ConfigureNotify on frame instead of the synthetic one on w.
3924 * The geometry data in the event will not be correct for the
3925 * frame, but tk doesn't look at that data anyway. */
3926 client_event.xconfigure.event = FW_W_FRAME(fw);
3927 client_event.xconfigure.window = FW_W_FRAME(fw);
3928 FSendEvent(
3929 dpy, FW_W_FRAME(fw), False, StructureNotifyMask,
3930 &client_event);
3933 return;
3936 /* Add an event group to the event handler */
3937 int register_event_group(int event_base, int event_count, PFEH *jump_table)
3939 /* insert into the list */
3940 event_group_t *group;
3941 event_group_t *position = base_event_group;
3942 event_group_t *prev_position = NULL;
3944 while (
3945 position != NULL &&
3946 position->base + position->count < event_base)
3948 prev_position = position;
3949 position = position->next;
3951 if ((position != NULL && position->base < event_base + event_count))
3953 /* there is already an event group registered at the specified
3954 * event range, or the base is before the base X events */
3956 return 1;
3958 /* create the group structure (these are not freed until fvwm exits) */
3959 group = (event_group_t*)safemalloc(sizeof(event_group_t));
3960 group->base = event_base;
3961 group->count = event_count;
3962 group->jump_table = jump_table;
3963 group->next = position;
3964 if (prev_position != NULL)
3966 prev_position->next = group;
3968 else
3970 base_event_group = group;
3973 return 0;
3977 ** Procedure:
3978 ** InitEventHandlerJumpTable
3980 void InitEventHandlerJumpTable(void)
3982 static PFEH EventHandlerJumpTable[LASTEvent];
3983 int i;
3985 for (i=0; i<LASTEvent; i++)
3987 EventHandlerJumpTable[i] = NULL;
3989 EventHandlerJumpTable[Expose] = HandleExpose;
3990 EventHandlerJumpTable[DestroyNotify] = HandleDestroyNotify;
3991 EventHandlerJumpTable[MapRequest] = HandleMapRequest;
3992 EventHandlerJumpTable[MapNotify] = HandleMapNotify;
3993 EventHandlerJumpTable[UnmapNotify] = HandleUnmapNotify;
3994 EventHandlerJumpTable[ButtonPress] = HandleButtonPress;
3995 EventHandlerJumpTable[EnterNotify] = HandleEnterNotify;
3996 EventHandlerJumpTable[LeaveNotify] = HandleLeaveNotify;
3997 EventHandlerJumpTable[FocusIn] = HandleFocusIn;
3998 EventHandlerJumpTable[FocusOut] = HandleFocusOut;
3999 EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
4000 EventHandlerJumpTable[ClientMessage] = HandleClientMessage;
4001 EventHandlerJumpTable[PropertyNotify] = HandlePropertyNotify;
4002 EventHandlerJumpTable[KeyPress] = HandleKeyPress;
4003 EventHandlerJumpTable[KeyRelease] = HandleKeyRelease;
4004 EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
4005 EventHandlerJumpTable[ColormapNotify] = HandleColormapNotify;
4006 EventHandlerJumpTable[SelectionClear] = HandleSelectionClear;
4007 EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
4008 EventHandlerJumpTable[ReparentNotify] = HandleReparentNotify;
4009 EventHandlerJumpTable[MappingNotify] = HandleMappingNotify;
4010 STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
4011 STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
4012 #ifdef MOUSE_DROPPINGS
4013 STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
4014 #else /* no MOUSE_DROPPINGS */
4015 STROKE_CODE(stroke_init());
4016 #endif /* MOUSE_DROPPINGS */
4017 if (register_event_group(0, LASTEvent, EventHandlerJumpTable))
4019 /* should never happen */
4020 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4021 "Faild to initialize event handlers");
4022 exit(1);
4024 if (FShapesSupported)
4026 static PFEH shape_jump_table[FShapeNumberEvents];
4028 for (i = 0; i < FShapeNumberEvents; i++)
4030 shape_jump_table[i] = NULL;
4032 shape_jump_table[FShapeNotify] = HandleShapeNotify;
4033 if (
4034 register_event_group(
4035 FShapeEventBase, FShapeNumberEvents,
4036 shape_jump_table))
4038 fvwm_msg(ERR, "InitEventHandlerJumpTable",
4039 "Faild to init Shape event handler");
4044 return;
4047 /* handle a single X event */
4048 void dispatch_event(XEvent *e)
4050 Window w = e->xany.window;
4051 FvwmWindow *fw;
4052 event_group_t *event_group;
4054 DBUG("dispatch_event", "Routine Entered");
4056 XFlush(dpy);
4057 if (w == Scr.Root)
4059 switch (e->type)
4061 case ButtonPress:
4062 case ButtonRelease:
4063 if (e->xbutton.subwindow != None)
4065 w = e->xbutton.subwindow;
4067 case MapRequest:
4068 w = e->xmaprequest.window;
4069 break;
4070 default:
4071 break;
4074 if (w == Scr.Root ||
4075 XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4077 fw = NULL;
4079 last_event_type = e->type;
4080 event_group = base_event_group;
4081 while (
4082 event_group != NULL &&
4083 event_group->base + event_group->count < e->type)
4085 event_group = event_group->next;
4088 if (
4089 event_group != NULL &&
4090 e->type - event_group->base < event_group->count &&
4091 event_group->jump_table[e->type - event_group->base] != NULL)
4093 evh_args_t ea;
4094 exec_context_changes_t ecc;
4095 Window dummyw;
4097 ecc.type = EXCT_EVENT;
4098 ecc.x.etrigger = e;
4099 ecc.w.wcontext = GetContext(&fw, fw, e, &dummyw);
4100 ecc.w.w = w;
4101 ecc.w.fw = fw;
4102 ea.exc = exc_create_context(
4103 &ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W |
4104 ECC_WCONTEXT);
4105 (*event_group->jump_table[e->type - event_group->base])(&ea);
4106 exc_destroy_context(ea.exc);
4109 #ifdef C_ALLOCA
4110 /* If we're using the C version of alloca, see if anything needs to be
4111 * freed up.
4113 alloca(0);
4114 #endif
4115 DBUG("dispatch_event", "Leaving Routine");
4117 return;
4120 /* ewmh configure request */
4121 void events_handle_configure_request(
4122 XConfigureRequestEvent cre, FvwmWindow *fw, Bool force,
4123 int force_gravity)
4125 __handle_configure_request(cre, NULL, fw, force, force_gravity);
4127 return;
4130 void HandleEvents(void)
4132 XEvent ev;
4134 DBUG("HandleEvents", "Routine Entered");
4135 STROKE_CODE(send_motion = False);
4136 while (!isTerminated)
4138 last_event_type = 0;
4139 if (Scr.flags.is_window_scheduled_for_destroy)
4141 destroy_scheduled_windows();
4143 if (Scr.flags.do_need_window_update)
4145 flush_window_updates();
4147 if (My_XNextEvent(dpy, &ev))
4149 dispatch_event(&ev);
4151 if (Scr.flags.do_need_style_list_update)
4153 simplify_style_list();
4157 return;
4162 * Waits for next X or module event, fires off startup routines when startup
4163 * modules have finished or after a timeout if the user has specified a
4164 * command line module that doesn't quit or gets stuck.
4167 int My_XNextEvent(Display *dpy, XEvent *event)
4169 fd_set in_fdset, out_fdset;
4170 int num_fd;
4171 fmodule_list_itr moditr;
4172 fmodule *module;
4173 fmodule_input *input;
4174 static struct timeval timeout;
4175 static struct timeval *timeoutP = &timeout;
4177 DBUG("My_XNextEvent", "Routine Entered");
4179 /* check for any X events already queued up.
4180 * Side effect: this does an XFlush if no events are queued
4181 * Make sure nothing between here and the select causes further
4182 * X requests to be sent or the select may block even though
4183 * there are events in the queue */
4184 if (FPending(dpy))
4186 DBUG(
4187 "My_XNextEvent", "taking care of queued up events"
4188 " & returning (1)");
4189 FNextEvent(dpy, event);
4190 return 1;
4193 /* check for termination of all startup modules */
4194 if (fFvwmInStartup)
4196 module_list_itr_init(&moditr);
4197 module = module_list_itr_next(&moditr);
4198 for (; module != NULL; module = module_list_itr_next(&moditr))
4200 if (MOD_IS_CMDLINE(module) == 1)
4202 break;
4205 module_cleanup();
4206 if (module == NULL)
4208 /* last module */
4209 DBUG(
4210 "My_XNextEvent",
4211 "Starting up after command lines modules");
4212 /* set an infinite timeout to stop ticking */
4213 timeoutP = NULL;
4214 /* This may cause X requests to be sent */
4215 StartupStuff();
4217 return 0; /* so return without select()ing */
4221 /* Some signals can interrupt us while we wait for any action
4222 * on our descriptors. While some of these signals may be asking
4223 * fvwm to die, some might be harmless. Harmless interruptions
4224 * mean we have to start waiting all over again ... */
4227 int ms;
4228 Bool is_waiting_for_scheduled_command = False;
4229 static struct timeval *old_timeoutP = NULL;
4231 /* The timeouts become undefined whenever the select returns,
4232 * and so we have to reinitialise them */
4233 ms = squeue_get_next_ms();
4234 if (ms == 0)
4236 /* run scheduled commands */
4237 squeue_execute();
4238 ms = squeue_get_next_ms();
4239 /* should not happen anyway.
4240 * get_next_schedule_queue_ms() can't return 0 after a
4241 * call to execute_schedule_queue(). */
4242 if (ms == 0)
4244 ms = 1;
4247 if (ms < 0)
4249 timeout.tv_sec = 42;
4250 timeout.tv_usec = 0;
4252 else
4254 /* scheduled commands are pending - don't wait too
4255 * long */
4256 timeout.tv_sec = ms / 1000;
4257 timeout.tv_usec = 1000 * (ms % 1000);
4258 old_timeoutP = timeoutP;
4259 timeoutP = &timeout;
4260 is_waiting_for_scheduled_command = True;
4263 FD_ZERO(&in_fdset);
4264 FD_ZERO(&out_fdset);
4265 FD_SET(x_fd, &in_fdset);
4267 /* nothing is done here if fvwm was compiled without session
4268 * support */
4269 if (sm_fd >= 0)
4271 FD_SET(sm_fd, &in_fdset);
4274 module_list_itr_init(&moditr);
4275 while ( (module = module_list_itr_next(&moditr)) != NULL)
4277 FD_SET(MOD_READFD(module), &in_fdset);
4279 if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
4281 FD_SET(MOD_WRITEFD(module), &out_fdset);
4285 DBUG("My_XNextEvent", "waiting for module input/output");
4286 num_fd = fvwmSelect(
4287 fvwmlib_max_fd, &in_fdset, &out_fdset, 0, timeoutP);
4288 if (is_waiting_for_scheduled_command)
4290 timeoutP = old_timeoutP;
4293 /* Express route out of fvwm ... */
4294 if (isTerminated)
4296 return 0;
4298 } while (num_fd < 0);
4300 if (num_fd > 0)
4302 /* Check for module input. */
4303 module_list_itr_init(&moditr);
4304 while ( (module = module_list_itr_next(&moditr)) != NULL)
4306 if (FD_ISSET(MOD_READFD(module), &in_fdset))
4308 input = module_receive(module);
4309 /* enqueue the received command */
4310 module_input_enqueue(input);
4312 if (
4313 MOD_WRITEFD(module) >= 0 &&
4314 FD_ISSET(MOD_WRITEFD(module), &out_fdset))
4316 DBUG("My_XNextEvent",
4317 "calling FlushMessageQueue");
4318 FlushMessageQueue(module);
4322 /* execute any commands queued up */
4323 DBUG("My_XNextEvent", "executing module comand queue");
4324 ExecuteCommandQueue();
4326 /* cleanup dead modules */
4327 module_cleanup();
4329 /* nothing is done here if fvwm was compiled without session
4330 * support */
4331 if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
4333 ProcessICEMsgs();
4337 else
4339 /* select has timed out, things must have calmed down so let's
4340 * decorate */
4341 if (fFvwmInStartup)
4343 fvwm_msg(ERR, "My_XNextEvent",
4344 "Some command line modules have not quit, "
4345 "Starting up after timeout.\n");
4346 StartupStuff();
4347 timeoutP = NULL; /* set an infinite timeout to stop
4348 * ticking */
4349 reset_style_changes();
4350 Scr.flags.do_need_window_update = 0;
4352 /* run scheduled commands if necessary */
4353 squeue_execute();
4356 /* check for X events again, rather than return 0 and get called again
4358 if (FPending(dpy))
4360 DBUG("My_XNextEvent",
4361 "taking care of queued up events & returning (2)");
4362 FNextEvent(dpy,event);
4363 return 1;
4366 DBUG("My_XNextEvent", "leaving My_XNextEvent");
4367 return 0;
4372 * Procedure:
4373 * Find the Fvwm context for the event.
4376 int GetContext(FvwmWindow **ret_fw, FvwmWindow *t, const XEvent *e, Window *w)
4378 int context;
4379 Window win;
4380 Window subw = None;
4381 int x = 0;
4382 int y = 0;
4383 Bool is_key_event = False;
4385 win = e->xany.window;
4386 context = C_NO_CONTEXT;
4387 switch (e->type)
4389 case KeyPress:
4390 case KeyRelease:
4391 x = e->xkey.x;
4392 y = e->xkey.y;
4393 subw = e->xkey.subwindow;
4394 if (win == Scr.Root && subw != None)
4396 /* Translate root coordinates into subwindow
4397 * coordinates. Necessary for key bindings that work
4398 * over unfocused windows. */
4399 win = subw;
4400 XTranslateCoordinates(
4401 dpy, Scr.Root, subw, x, y, &x, &y, &subw);
4402 XFindContext(dpy, win, FvwmContext, (caddr_t *) &t);
4404 is_key_event = True;
4405 /* fall through */
4406 case ButtonPress:
4407 case ButtonRelease:
4408 if (!is_key_event)
4410 x = e->xbutton.x;
4411 y = e->xbutton.y;
4412 subw = e->xbutton.subwindow;
4414 if (t && win == FW_W_FRAME(t) && subw != None)
4416 /* Translate frame coordinates into subwindow
4417 * coordinates. */
4418 win = subw;
4419 XTranslateCoordinates(
4420 dpy, FW_W_FRAME(t), subw, x, y, &x, &y, &subw);
4421 if (win == FW_W_PARENT(t))
4423 win = subw;
4424 XTranslateCoordinates(
4425 dpy, FW_W_PARENT(t), subw, x, y, &x,
4426 &y, &subw);
4429 break;
4430 default:
4431 XFindContext(dpy, win, FvwmContext, (caddr_t *)&t);
4432 break;
4434 if (ret_fw != NULL)
4436 *ret_fw = t;
4438 if (!t)
4440 return C_ROOT;
4442 *w = win;
4443 if (*w == Scr.NoFocusWin)
4445 return C_ROOT;
4447 if (subw != None)
4449 if (win == FW_W_PARENT(t))
4451 *w = subw;
4454 if (*w == Scr.Root)
4456 return C_ROOT;
4458 context = frame_window_id_to_context(t, *w, &Button);
4460 return context;
4465 * Removes expose events for a specific window from the queue
4468 int flush_expose(Window w)
4470 XEvent dummy;
4471 int i=0;
4473 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4475 i++;
4478 return i;
4481 /* same as above, but merges the expose rectangles into a single big one */
4482 int flush_accumulate_expose(Window w, XEvent *e)
4484 XEvent dummy;
4485 int i = 0;
4486 int x1 = e->xexpose.x;
4487 int y1 = e->xexpose.y;
4488 int x2 = x1 + e->xexpose.width;
4489 int y2 = y1 + e->xexpose.height;
4491 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4493 x1 = min(x1, dummy.xexpose.x);
4494 y1 = min(y1, dummy.xexpose.y);
4495 x2 = max(x2, dummy.xexpose.x + dummy.xexpose.width);
4496 y2 = max(y2, dummy.xexpose.y + dummy.xexpose.height);
4497 i++;
4499 e->xexpose.x = x1;
4500 e->xexpose.y = y1;
4501 e->xexpose.width = x2 - x1;
4502 e->xexpose.height = y2 - y1;
4504 return i;
4509 * Removes all expose events from the queue and does the necessary redraws
4512 void handle_all_expose(void)
4514 void *saved_event;
4515 XEvent evdummy;
4517 saved_event = fev_save_event();
4518 FPending(dpy);
4519 while (FCheckMaskEvent(dpy, ExposureMask, &evdummy))
4521 dispatch_event(&evdummy);
4523 fev_restore_event(saved_event);
4525 return;
4528 /* CoerceEnterNotifyOnCurrentWindow()
4529 * Pretends to get a HandleEnterNotify on the window that the pointer
4530 * currently is in so that the focus gets set correctly from the beginning.
4531 * Note that this presently only works if the current window is not
4532 * click_to_focus; I think that that behaviour is correct and desirable.
4533 * --11/08/97 gjb */
4534 void CoerceEnterNotifyOnCurrentWindow(void)
4536 Window child;
4537 Window root;
4538 Bool f;
4539 evh_args_t ea;
4540 exec_context_changes_t ecc;
4541 XEvent e;
4542 FvwmWindow *fw;
4544 f = FQueryPointer(
4545 dpy, Scr.Root, &root, &child, &e.xcrossing.x_root,
4546 &e.xcrossing.y_root, &e.xcrossing.x, &e.xcrossing.y,
4547 &JunkMask);
4548 if (f == False || child == None)
4550 return;
4552 e.xcrossing.type = EnterNotify;
4553 e.xcrossing.window = child;
4554 e.xcrossing.subwindow = None;
4555 e.xcrossing.mode = NotifyNormal;
4556 e.xcrossing.detail = NotifyAncestor;
4557 e.xcrossing.same_screen = True;
4558 if (XFindContext(dpy, child, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4560 fw = NULL;
4562 else
4564 XTranslateCoordinates(
4565 dpy, Scr.Root, child, e.xcrossing.x_root,
4566 e.xcrossing.y_root, &JunkX, &JunkY, &child);
4567 if (child == FW_W_PARENT(fw))
4569 child = FW_W(fw);
4571 if (child != None)
4573 e.xany.window = child;
4576 e.xcrossing.focus = (fw == get_focus_window()) ? True : False;
4577 ecc.type = EXCT_NULL;
4578 ecc.x.etrigger = &e;
4579 ea.exc = exc_create_context(&ecc, ECC_TYPE | ECC_ETRIGGER);
4580 HandleEnterNotify(&ea);
4581 exc_destroy_context(ea.exc);
4583 return;
4586 /* This function discards all queued up ButtonPress, ButtonRelease and
4587 * ButtonMotion events. */
4588 int discard_events(long event_mask)
4590 XEvent e;
4591 int count;
4593 XSync(dpy, 0);
4594 for (count = 0; FCheckMaskEvent(dpy, event_mask, &e); count++)
4596 /* nothing */
4599 return count;
4602 /* This function discards all queued up ButtonPress, ButtonRelease and
4603 * ButtonMotion events. */
4604 int discard_window_events(Window w, long event_mask)
4606 XEvent e;
4607 int count;
4609 XSync(dpy, 0);
4610 for (count = 0; FCheckWindowEvent(dpy, w, event_mask, &e); count++)
4612 /* nothing */
4615 return count;
4618 /* Similar function for certain types of PropertyNotify. */
4619 int flush_property_notify(Atom atom, Window w)
4621 XEvent e;
4622 int count;
4623 test_typed_window_event_args args;
4625 XSync(dpy, 0);
4626 args.w = w;
4627 args.event_type = PropertyNotify;
4628 for (count = 0;
4629 FCheckPeekIfEvent(
4630 dpy, &e, test_typed_window_event, (XPointer)&args);
4631 count++)
4633 Bool rc;
4635 if (e.xproperty.atom != atom)
4637 break;
4639 /* remove the event from the queue */
4640 rc = FCheckIfEvent(
4641 dpy, &e, test_typed_window_event, (XPointer)&args);
4644 return count;
4647 /* Wait for all mouse buttons to be released
4648 * This can ease some confusion on the part of the user sometimes
4650 * Discard superflous button events during this wait period. */
4651 void WaitForButtonsUp(Bool do_handle_expose)
4653 unsigned int mask;
4654 unsigned int bmask;
4655 long evmask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
4656 KeyPressMask|KeyReleaseMask;
4657 int count;
4658 int use_wait_cursor;
4659 XEvent e;
4661 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
4662 &JunkX, &JunkY, &mask) == False)
4664 /* pointer is on a different screen - that's okay here */
4666 mask &= DEFAULT_ALL_BUTTONS_MASK;
4667 if (mask == 0)
4669 return;
4671 if (do_handle_expose)
4673 evmask |= ExposureMask;
4675 GrabEm(None, GRAB_NORMAL);
4676 for (count = 0, use_wait_cursor = 0; mask != 0; count++)
4678 /* handle expose events */
4679 XAllowEvents(dpy, SyncPointer, CurrentTime);
4680 if (FCheckMaskEvent(dpy, evmask, &e))
4682 switch (e.type)
4684 case ButtonRelease:
4685 if (e.xbutton.button <=
4686 NUMBER_OF_MOUSE_BUTTONS)
4688 bmask = (Button1Mask <<
4689 (e.xbutton.button - 1));
4690 mask = e.xbutton.state & ~bmask;
4692 break;
4693 case Expose:
4694 dispatch_event(&e);
4695 break;
4696 default:
4697 break;
4700 else
4702 if (FQueryPointer(
4703 dpy, Scr.Root, &JunkRoot, &JunkChild,
4704 &JunkX, &JunkY, &JunkX, &JunkY, &mask) ==
4705 False)
4707 /* pointer is on a different screen - that's
4708 * okay here */
4710 mask &= DEFAULT_ALL_BUTTONS_MASK;
4711 usleep(1);
4713 if (use_wait_cursor == 0 && count == 20)
4715 GrabEm(CRS_WAIT, GRAB_NORMAL);
4716 use_wait_cursor = 1;
4719 UngrabEm(GRAB_NORMAL);
4720 if (use_wait_cursor)
4722 UngrabEm(GRAB_NORMAL);
4723 XFlush(dpy);
4726 return;
4729 void sync_server(int toggle)
4731 static Bool synced = False;
4733 if (toggle == -1)
4735 toggle = (synced == False);
4737 if (toggle == 1)
4739 synced = True;
4741 else
4743 synced = False;
4745 XSynchronize(dpy, synced);
4746 XFlush(dpy);
4748 return;
4751 Bool is_resizing_event_pending(
4752 FvwmWindow *fw)
4754 XEvent e;
4755 check_if_event_args args;
4757 args.w = FW_W(fw);
4758 args.do_return_true = False;
4759 args.do_return_true_cr = False;
4760 args.cr_value_mask = 0;
4761 args.ret_does_match = False;
4762 args.ret_type = 0;
4763 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
4765 return args.ret_does_match;
4768 /* ---------------------------- builtin commands --------------------------- */
4770 void CMD_XSynchronize(F_CMD_ARGS)
4772 int toggle;
4774 toggle = ParseToggleArgument(action, NULL, -1, 0);
4775 sync_server(toggle);
4777 return;
4780 void CMD_XSync(F_CMD_ARGS)
4782 XSync(dpy, 0);
4784 return;