Fix compilation without libstroke for print_bindings.
[fvwm.git] / fvwm / events.c
blob88ec40b0bdd117f17f3d260cfda73ec46110c795
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 const char *initial_map_command;
2883 initial_map_command = NULL;
2884 if (win_opts == NULL)
2886 memset(&win_opts_bak, 0, sizeof(win_opts_bak));
2887 win_opts = &win_opts_bak;
2889 ew = ea->exc->w.w;
2890 if (ReuseWin == NULL)
2892 Window pw;
2894 pw = ea->exc->x.etrigger->xmaprequest.parent;
2895 if (XFindContext(dpy, ew, FvwmContext, (caddr_t *)&fw) ==
2896 XCNOENT)
2898 fw = NULL;
2900 if (fw != NULL && IS_MAP_PENDING(fw))
2902 /* The window is already going to be mapped, no need to
2903 * do that twice */
2904 return;
2907 else
2909 fw = ReuseWin;
2912 if (fw == NULL && EWMH_IsKdeSysTrayWindow(ew))
2914 /* This means that the window is swallowed by kicker and that
2915 * kicker restart or exit. As we should assume that kicker
2916 * restart we should return here, if not we go into trouble
2917 * ... */
2918 return;
2920 if (!win_opts->flags.do_override_ppos)
2922 XFlush(dpy);
2925 /* If the window has never been mapped before ... */
2926 if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
2928 check_if_event_args args;
2929 XEvent dummy;
2931 args.w = ew;
2932 args.do_return_true = True;
2933 args.do_return_true_cr = False;
2934 if (
2935 FCheckIfEvent(
2936 dpy, &dummy, test_withdraw_request,
2937 (XPointer)&args)) {
2938 /* The window is moved back to the WitdrawnState
2939 * immideately. Don't map it. */
2940 return;
2943 /* Add decorations. */
2944 fw = AddWindow(
2945 &initial_map_command, ea->exc, ReuseWin, win_opts);
2946 if (fw == AW_NO_WINDOW)
2948 return;
2950 else if (fw == AW_UNMANAGED)
2952 XMapWindow(dpy, ew);
2953 return;
2955 is_new_window = True;
2958 * Make sure at least part of window is on this page
2959 * before giving it focus...
2961 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2962 if (KeepRaised != None)
2964 XRaiseWindow(dpy, KeepRaised);
2966 /* If it's not merely iconified, and we have hints, use them. */
2968 if (IS_ICONIFIED(fw))
2970 /* If no hints, or currently an icon, just "deiconify" */
2971 DeIconify(fw);
2973 else if (IS_MAPPED(fw))
2975 /* the window is already mapped - fake a MapNotify event */
2976 fake_map_unmap_notify(fw, MapNotify);
2978 else
2980 int state;
2982 if (fw->wmhints && (fw->wmhints->flags & StateHint))
2984 state = fw->wmhints->initial_state;
2986 else
2988 state = NormalState;
2990 if (win_opts->initial_state != DontCareState)
2992 state = win_opts->initial_state;
2995 switch (state)
2997 case DontCareState:
2998 case NormalState:
2999 case InactiveState:
3000 default:
3001 MyXGrabServer(dpy);
3002 if (fw->Desk == Scr.CurrentDesk)
3004 Bool do_grab_focus;
3006 SET_MAP_PENDING(fw, 1);
3007 XMapWindow(dpy, FW_W_FRAME(fw));
3008 XMapWindow(dpy, FW_W(fw));
3009 SetMapStateProp(fw, NormalState);
3010 if (Scr.flags.is_map_desk_in_progress)
3012 do_grab_focus = False;
3014 else if (!is_on_this_page)
3016 do_grab_focus = False;
3018 else if (focus_query_open_grab_focus(
3019 fw, get_focus_window()) ==
3020 True)
3022 do_grab_focus = True;
3024 else
3026 do_grab_focus = False;
3028 if (do_grab_focus)
3030 SetFocusWindow(
3031 fw, True, FOCUS_SET_FORCE);
3033 else
3035 /* make sure the old focused window
3036 * still has grabbed all necessary
3037 * buttons. */
3038 focus_grab_buttons(
3039 get_focus_window());
3042 else
3044 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
3045 /* nope, this is forbidden by the ICCCM2 */
3046 XMapWindow(dpy, FW_W(fw));
3047 SetMapStateProp(fw, NormalState);
3048 #else
3049 /* Since we will not get a MapNotify, set the
3050 * IS_MAPPED flag manually. */
3051 SET_MAPPED(fw, 1);
3052 SetMapStateProp(fw, IconicState);
3053 /* fake that the window was mapped to allow
3054 * modules to swallow it */
3055 BroadcastPacket(
3056 M_MAP, 3, (long)FW_W(fw),
3057 (long)FW_W_FRAME(fw),
3058 (unsigned long)fw);
3059 #endif
3061 /* TA: 20090125: We *have* to handle
3062 * InitialMapCommand here and not in AddWindow() to
3063 * allow for correct timings when the window is truly
3064 * mapped. (c.f. things like Iconify.)
3066 if (initial_map_command != NULL)
3068 execute_function_override_window(
3069 NULL, ea->exc,
3070 (char *)initial_map_command, 0, fw);
3072 MyXUngrabServer(dpy);
3073 break;
3075 case IconicState:
3076 if (is_new_window)
3078 /* the window will not be mapped - fake a
3079 * MapNotify and an UnmapNotify event. Can't
3080 * remember exactly why this is necessary, but
3081 * probably something w/ (de)iconify state
3082 * confusion. */
3083 fake_map_unmap_notify(fw, MapNotify);
3084 fake_map_unmap_notify(fw, UnmapNotify);
3086 if (win_opts->flags.is_iconified_by_parent ||
3087 ((tmp = get_transientfor_fvwmwindow(fw)) &&
3088 IS_ICONIFIED(tmp)))
3090 win_opts->flags.is_iconified_by_parent = 0;
3091 SET_ICONIFIED_BY_PARENT(fw, 1);
3093 if (USE_ICON_POSITION_HINT(fw) && fw->wmhints &&
3094 (fw->wmhints->flags & IconPositionHint))
3096 win_opts->default_icon_x = fw->wmhints->icon_x;
3097 win_opts->default_icon_y = fw->wmhints->icon_y;
3099 Iconify(fw, win_opts);
3100 break;
3103 if (IS_SHADED(fw))
3105 BroadcastPacket(
3106 M_WINDOWSHADE, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
3107 (unsigned long)fw);
3109 /* If the newly mapped window overlaps the focused window, make sure
3110 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3111 sf = get_focus_window();
3112 if (sf != NULL)
3114 focus_grab_buttons(sf);
3116 if (win_opts->flags.is_menu)
3118 SET_MAPPED(fw, 1);
3119 SET_MAP_PENDING(fw, 0);
3121 EWMH_SetClientList();
3122 EWMH_SetClientListStacking();
3123 GNOME_SetClientList();
3125 return;
3128 #ifdef HAVE_STROKE
3129 void HandleMotionNotify(const evh_args_t *ea)
3131 DBUG("HandleMotionNotify", "Routine Entered");
3133 if (send_motion == True)
3135 stroke_record(
3136 ea->exc->x.etrigger->xmotion.x,
3137 ea->exc->x.etrigger->xmotion.y);
3140 return;
3142 #endif /* HAVE_STROKE */
3144 void HandlePropertyNotify(const evh_args_t *ea)
3146 Bool OnThisPage = False;
3147 Bool has_icon_changed = False;
3148 Bool has_icon_pixmap_hint_changed = False;
3149 Bool has_icon_window_hint_changed = False;
3150 FlocaleNameString new_name = { NoName, NULL };
3151 int old_wmhints_flags;
3152 const XEvent *te = ea->exc->x.etrigger;
3153 char *urgency_action = NULL;
3154 FvwmWindow * const fw = ea->exc->w.fw;
3156 DBUG("HandlePropertyNotify", "Routine Entered");
3158 if (te->xproperty.window == Scr.Root &&
3159 te->xproperty.state == PropertyNewValue &&
3160 (te->xproperty.atom == _XA_XSETROOT_ID ||
3161 te->xproperty.atom == _XA_XROOTPMAP_ID))
3163 /* background change */
3164 /* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3165 * no property notify?). _XA_XROOTPMAP_ID is used by Esetroot
3166 * compatible program: the problem here is that with some
3167 * Esetroot compatible program we get the message _before_ the
3168 * background change. This is fixed with Esetroot 9.2 (not yet
3169 * released, 2002-01-14) */
3171 /* update icon window with some alpha and tear-off menu */
3172 FvwmWindow *t;
3174 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3176 int cs;
3177 int t_cs = -1;
3178 int b_cs = t->icon_background_cs;
3179 Bool draw_picture = False;
3180 Bool draw_title = False;
3182 /* redraw ParentRelative tear-off menu */
3183 menu_redraw_transparent_tear_off_menu(t, True);
3185 if (!IS_ICONIFIED(t) || IS_ICON_SUPPRESSED(t))
3187 continue;
3189 if (Scr.Hilite == t)
3191 if (t->icon_title_cs_hi >= 0)
3193 t_cs = cs = t->icon_title_cs_hi;
3195 else
3197 cs = t->cs_hi;
3200 else
3202 if (t->icon_title_cs >= 0)
3204 t_cs = cs = t->icon_title_cs;
3206 else
3208 cs = t->cs;
3211 if (t->icon_alphaPixmap != None ||
3212 (cs >= 0 &&
3213 Colorset[cs].icon_alpha_percent < 100) ||
3214 CSET_IS_TRANSPARENT_PR(b_cs) ||
3215 (!IS_ICON_SHAPED(t) &&
3216 t->icon_background_padding > 0))
3218 draw_picture = True;
3220 if (CSET_IS_TRANSPARENT_PR(t_cs))
3222 draw_title = True;
3224 if (draw_title || draw_picture)
3226 DrawIconWindow(
3227 t, draw_title, draw_picture, False,
3228 draw_picture, NULL);
3231 if (te->xproperty.atom == _XA_XROOTPMAP_ID)
3233 update_root_transparent_colorset(te->xproperty.atom);
3235 BroadcastPropertyChange(
3236 MX_PROPERTY_CHANGE_BACKGROUND, 0, 0, "");
3237 return;
3240 if (!fw)
3242 return;
3244 if (XGetGeometry(
3245 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3246 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
3247 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
3249 return;
3253 * Make sure at least part of window is on this page
3254 * before giving it focus...
3256 OnThisPage = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3258 switch (te->xproperty.atom)
3260 case XA_WM_TRANSIENT_FOR:
3261 flush_property_notify(XA_WM_TRANSIENT_FOR, FW_W(fw));
3262 if (setup_transientfor(fw) == True)
3264 RaiseWindow(fw, False);
3266 break;
3268 case XA_WM_NAME:
3269 flush_property_notify(XA_WM_NAME, FW_W(fw));
3270 if (HAS_EWMH_WM_NAME(fw))
3272 return;
3274 FlocaleGetNameProperty(XGetWMName, dpy, FW_W(fw), &new_name);
3275 if (new_name.name == NULL)
3277 FlocaleFreeNameProperty(&new_name);
3278 return;
3280 if (strlen(new_name.name) > MAX_WINDOW_NAME_LEN)
3282 /* limit to prevent hanging X server */
3283 (new_name.name)[MAX_WINDOW_NAME_LEN] = 0;
3285 if (fw->name.name && strcmp(new_name.name, fw->name.name) == 0)
3287 /* migo: some apps update their names every second */
3288 /* griph: make sure we don't free the property if it
3289 is THE same name */
3290 if (new_name.name != fw->name.name)
3292 FlocaleFreeNameProperty(&new_name);
3294 return;
3297 free_window_names(fw, True, False);
3298 fw->name = new_name;
3299 SET_NAME_CHANGED(fw, 1);
3300 if (fw->name.name == NULL)
3302 fw->name.name = NoName; /* must not happen */
3304 setup_visible_name(fw, False);
3305 BroadcastWindowIconNames(fw, True, False);
3307 /* fix the name in the title bar */
3308 if (!IS_ICONIFIED(fw))
3310 border_draw_decorations(
3311 fw, PART_TITLE, (Scr.Hilite == fw), True,
3312 CLEAR_ALL, NULL, NULL);
3314 EWMH_SetVisibleName(fw, False);
3316 * if the icon name is NoName, set the name of the icon to be
3317 * the same as the window
3319 if (!WAS_ICON_NAME_PROVIDED(fw))
3321 fw->icon_name = fw->name;
3322 setup_visible_name(fw, True);
3323 BroadcastWindowIconNames(fw, False, True);
3324 RedoIconName(fw);
3326 break;
3328 case XA_WM_ICON_NAME:
3329 flush_property_notify(XA_WM_ICON_NAME, FW_W(fw));
3330 if (HAS_EWMH_WM_ICON_NAME(fw))
3332 return;
3334 FlocaleGetNameProperty(
3335 XGetWMIconName, dpy, FW_W(fw), &new_name);
3336 if (new_name.name == NULL)
3338 FlocaleFreeNameProperty(&new_name);
3339 return;
3341 if (new_name.name && strlen(new_name.name) > MAX_ICON_NAME_LEN)
3343 /* limit to prevent hanging X server */
3344 (new_name.name)[MAX_ICON_NAME_LEN] = 0;
3346 if (fw->icon_name.name &&
3347 strcmp(new_name.name, fw->icon_name.name) == 0)
3349 /* migo: some apps update their names every second */
3350 /* griph: make sure we don't free the property if it
3351 is THE same name */
3352 if (new_name.name != fw->icon_name.name)
3354 FlocaleFreeNameProperty(&new_name);
3356 return;
3359 free_window_names(fw, False, True);
3360 fw->icon_name = new_name;
3361 SET_WAS_ICON_NAME_PROVIDED(fw, 1);
3362 if (fw->icon_name.name == NULL)
3364 /* currently never happens */
3365 fw->icon_name.name = fw->name.name;
3366 SET_WAS_ICON_NAME_PROVIDED(fw, 0);
3368 setup_visible_name(fw, True);
3369 BroadcastWindowIconNames(fw, False, True);
3370 RedoIconName(fw);
3371 EWMH_SetVisibleName(fw, True);
3372 break;
3374 case XA_WM_HINTS:
3375 flush_property_notify(XA_WM_HINTS, FW_W(fw));
3376 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3377 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3379 old_wmhints_flags = 0;
3380 if (fw->wmhints)
3382 old_wmhints_flags = fw->wmhints->flags;
3383 XFree ((char *) fw->wmhints);
3385 setup_wm_hints(fw);
3386 if (fw->wmhints == NULL)
3388 return;
3392 * rebuild icon if the client either provides an icon
3393 * pixmap or window or has reset the hints to `no icon'.
3395 if ((fw->wmhints->flags & IconPixmapHint) ||
3396 (old_wmhints_flags & IconPixmapHint))
3398 ICON_DBG((stderr, "hpn: iph changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconPixmapHint), fw->name));
3399 has_icon_pixmap_hint_changed = True;
3401 if ((fw->wmhints->flags & IconWindowHint) ||
3402 (old_wmhints_flags & IconWindowHint))
3404 ICON_DBG((stderr, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconWindowHint), fw->name));
3405 has_icon_window_hint_changed = True;
3406 SET_USE_EWMH_ICON(fw, False);
3408 increase_icon_hint_count(fw);
3409 if (has_icon_window_hint_changed ||
3410 has_icon_pixmap_hint_changed)
3412 if (ICON_OVERRIDE_MODE(fw) == ICON_OVERRIDE)
3414 ICON_DBG((stderr, "hpn: icon override '%s'\n", fw->name));
3415 has_icon_changed = False;
3417 else if (ICON_OVERRIDE_MODE(fw) ==
3418 NO_ACTIVE_ICON_OVERRIDE)
3420 if (has_icon_pixmap_hint_changed)
3422 if (WAS_ICON_HINT_PROVIDED(fw) ==
3423 ICON_HINT_MULTIPLE)
3425 ICON_DBG((stderr, "hpn: using further iph '%s'\n", fw->name));
3426 has_icon_changed = True;
3428 else if (fw->icon_bitmap_file ==
3429 NULL ||
3430 fw->icon_bitmap_file ==
3431 Scr.DefaultIcon)
3433 ICON_DBG((stderr, "hpn: using first iph '%s'\n", fw->name));
3434 has_icon_changed = True;
3436 else
3438 /* ignore the first icon pixmap
3439 * hint if the application did
3440 * not provide it from the
3441 * start */
3442 ICON_DBG((stderr, "hpn: first iph ignored '%s'\n", fw->name));
3443 has_icon_changed = False;
3446 else if (has_icon_window_hint_changed)
3448 ICON_DBG((stderr, "hpn: using iwh '%s'\n", fw->name));
3449 has_icon_changed = True;
3451 else
3453 ICON_DBG((stderr, "hpn: iwh not changed, hint ignored '%s'\n", fw->name));
3454 has_icon_changed = False;
3457 else /* NO_ICON_OVERRIDE */
3459 ICON_DBG((stderr, "hpn: using hint '%s'\n", fw->name));
3460 has_icon_changed = True;
3463 if (USE_EWMH_ICON(fw))
3465 has_icon_changed = False;
3468 if (has_icon_changed)
3470 ICON_DBG((stderr, "hpn: icon changed '%s'\n", fw->name));
3471 /* Okay, the icon hint has changed and style
3472 * options tell us to honour this change. Now
3473 * let's see if we have to use the application
3474 * provided pixmap or window (if any), the icon
3475 * file provided by the window's style or the
3476 * default style's icon. */
3477 if (fw->icon_bitmap_file == Scr.DefaultIcon)
3479 fw->icon_bitmap_file = NULL;
3481 if (!fw->icon_bitmap_file &&
3482 !(fw->wmhints->flags &
3483 (IconPixmapHint|IconWindowHint)))
3485 fw->icon_bitmap_file =
3486 (Scr.DefaultIcon) ?
3487 Scr.DefaultIcon : NULL;
3489 fw->iconPixmap = (Window)NULL;
3490 ChangeIconPixmap(fw);
3494 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3495 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3496 * Treat urgency changes by calling user-settable functions.
3497 * These could e.g. deiconify and raise the window or
3498 * temporarily change the decor. */
3499 if (!(old_wmhints_flags & XUrgencyHint) &&
3500 (fw->wmhints->flags & XUrgencyHint))
3502 urgency_action = "Function UrgencyFunc";
3504 if ((old_wmhints_flags & XUrgencyHint) &&
3505 !(fw->wmhints->flags & XUrgencyHint))
3507 urgency_action = "Function UrgencyDoneFunc";
3509 if (urgency_action)
3511 const exec_context_t *exc;
3512 exec_context_changes_t ecc;
3514 ecc.w.fw = fw;
3515 ecc.w.wcontext = C_WINDOW;
3516 exc = exc_clone_context(
3517 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
3518 execute_function(NULL, exc, urgency_action, 0);
3519 exc_destroy_context(exc);
3521 break;
3522 case XA_WM_NORMAL_HINTS:
3523 /* just mark wm normal hints as changed and look them up when
3524 * the next ConfigureRequest w/ x, y, width or height set
3525 * arrives. */
3526 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 1);
3527 break;
3528 default:
3529 if (te->xproperty.atom == _XA_WM_PROTOCOLS)
3531 FetchWmProtocols (fw);
3533 else if (te->xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
3535 FetchWmColormapWindows (fw); /* frees old data */
3536 ReInstallActiveColormap();
3538 else if (te->xproperty.atom == _XA_WM_STATE)
3540 if (fw && OnThisPage && focus_is_focused(fw) &&
3541 FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
3543 /* refresh the focus - why? */
3544 focus_force_refresh_focus(fw);
3547 else
3549 EWMH_ProcessPropertyNotify(ea->exc);
3551 break;
3555 void HandleReparentNotify(const evh_args_t *ea)
3557 const XEvent *te = ea->exc->x.etrigger;
3558 FvwmWindow * const fw = ea->exc->w.fw;
3560 if (!fw)
3562 return;
3564 if (te->xreparent.parent == Scr.Root)
3566 /* Ignore reparenting to the root window. In some cases these
3567 * events are selected although the window is no longer
3568 * managed. */
3569 return;
3571 if (te->xreparent.parent != FW_W_FRAME(fw))
3573 /* window was reparented by someone else, destroy the frame */
3574 SetMapStateProp(fw, WithdrawnState);
3575 EWMH_RestoreInitialStates(fw, te->type);
3576 if (!IS_TEAR_OFF_MENU(fw))
3578 XRemoveFromSaveSet(dpy, te->xreparent.window);
3579 XSelectInput(dpy, te->xreparent.window, NoEventMask);
3581 else
3583 XSelectInput(dpy, te->xreparent.window, XEVMASK_MENUW);
3585 discard_events(XEVMASK_FRAMEW);
3586 destroy_window(fw);
3587 EWMH_ManageKdeSysTray(te->xreparent.window, te->type);
3588 EWMH_WindowDestroyed();
3591 return;
3594 void HandleSelectionRequest(const evh_args_t *ea)
3596 icccm2_handle_selection_request(ea->exc->x.etrigger);
3598 return;
3601 void HandleSelectionClear(const evh_args_t *ea)
3603 icccm2_handle_selection_clear();
3605 return;
3608 void HandleShapeNotify(const evh_args_t *ea)
3610 FvwmWindow * const fw = ea->exc->w.fw;
3612 DBUG("HandleShapeNotify", "Routine Entered");
3614 if (FShapesSupported)
3616 const FShapeEvent *sev =
3617 (const FShapeEvent *)(ea->exc->x.etrigger);
3619 if (!fw)
3621 return;
3623 if (sev->kind != FShapeBounding)
3625 return;
3627 frame_setup_shape(
3628 fw, fw->g.frame.width, fw->g.frame.height, sev->shaped);
3629 GNOME_SetWinArea(fw);
3630 EWMH_SetFrameStrut(fw);
3631 if (!IS_ICONIFIED(fw))
3633 border_redraw_decorations(fw);
3637 return;
3640 void HandleUnmapNotify(const evh_args_t *ea)
3642 int dstx, dsty;
3643 Window dumwin;
3644 XEvent dummy;
3645 XEvent map_event;
3646 const XEvent *te = ea->exc->x.etrigger;
3647 int weMustUnmap;
3648 Bool focus_grabbed;
3649 Bool must_return = False;
3650 Bool do_map = False;
3651 FvwmWindow * const fw = ea->exc->w.fw;
3652 Window pw;
3653 Window cw;
3655 DBUG("HandleUnmapNotify", "Routine Entered");
3657 /* Don't ignore events as described below. */
3658 if (te->xunmap.event != te->xunmap.window &&
3659 (te->xunmap.event != Scr.Root || !te->xunmap.send_event))
3661 must_return = True;
3665 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3666 * to WithdrawnState should send a synthetic UnmapNotify with the
3667 * event field set to (pseudo-)root, in case the window is already
3668 * unmapped (which is the case for fvwm for IconicState).
3669 * Unfortunately, we looked for the FvwmContext using that field, so
3670 * try the window field also. */
3671 weMustUnmap = 0;
3672 if (!fw)
3674 weMustUnmap = 1;
3675 if (XFindContext(
3676 dpy, te->xunmap.window, FvwmContext,
3677 (caddr_t *)&fw) == XCNOENT)
3679 return;
3682 cw = FW_W(fw);
3683 pw = FW_W_PARENT(fw);
3684 if (te->xunmap.window == FW_W_FRAME(fw))
3686 SET_ICONIFY_PENDING(fw , 0);
3687 return;
3689 if (must_return)
3691 return;
3694 if (weMustUnmap)
3696 Bool is_map_request_pending;
3697 check_if_event_args args;
3699 args.w = te->xunmap.window;
3700 args.do_return_true = False;
3701 args.do_return_true_cr = False;
3702 /* Using FCheckTypedWindowEvent() does not work here. I don't
3703 * have the slightest idea why, but using FCheckIfEvent() with
3704 * the appropriate predicate procedure works fine. */
3705 FCheckIfEvent(dpy, &dummy, test_map_request, (XPointer)&args);
3706 /* Unfortunately, there is no procedure in X that simply tests
3707 * if an event of a certain type in on the queue without
3708 * waiting and without removing it from the queue.
3709 * XCheck...Event() does not wait but removes the event while
3710 * XPeek...() does not remove the event but waits. To solve
3711 * this, the predicate procedure sets a flag in the passed in
3712 * structure and returns False unconditionally. */
3713 is_map_request_pending = (args.ret_does_match == True);
3714 if (!is_map_request_pending)
3716 XUnmapWindow(dpy, te->xunmap.window);
3719 if (fw == Scr.Hilite)
3721 Scr.Hilite = NULL;
3723 focus_grabbed = focus_query_close_release_focus(fw);
3724 restore_focus_after_unmap(fw, False);
3725 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
3727 return;
3731 * The program may have unmapped the client window, from either
3732 * NormalState or IconicState. Handle the transition to WithdrawnState.
3734 * We need to reparent the window back to the root (so that fvwm exiting
3735 * won't cause it to get mapped) and then throw away all state (pretend
3736 * that we've received a DestroyNotify).
3738 if (!FCheckTypedWindowEvent(
3739 dpy, te->xunmap.window, DestroyNotify, &dummy) &&
3740 XTranslateCoordinates(
3741 dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
3742 &dumwin))
3744 MyXGrabServer(dpy);
3745 SetMapStateProp(fw, WithdrawnState);
3746 EWMH_RestoreInitialStates(fw, te->type);
3747 if (FCheckTypedWindowEvent(
3748 dpy, te->xunmap.window, ReparentNotify, &dummy))
3750 if (fw->attr_backup.border_width)
3752 XSetWindowBorderWidth(
3753 dpy, te->xunmap.window,
3754 fw->attr_backup.border_width);
3756 if ((!IS_ICON_SUPPRESSED(fw))&&
3757 (fw->wmhints &&
3758 (fw->wmhints->flags & IconWindowHint)))
3760 XUnmapWindow(dpy, fw->wmhints->icon_window);
3763 else
3765 RestoreWithdrawnLocation(fw, False, Scr.Root);
3767 if (!IS_TEAR_OFF_MENU(fw))
3769 XRemoveFromSaveSet(dpy, te->xunmap.window);
3770 XSelectInput(dpy, te->xunmap.window, NoEventMask);
3772 XSync(dpy, 0);
3773 MyXUngrabServer(dpy);
3774 if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
3776 /* the client tried to map the window again while it
3777 * was still inside the decoration windows */
3778 do_map = True;
3781 destroy_window(fw);
3782 if (focus_grabbed == True)
3784 CoerceEnterNotifyOnCurrentWindow();
3786 EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
3787 EWMH_WindowDestroyed();
3788 GNOME_SetClientList();
3789 if (do_map == True)
3791 map_event.xmaprequest.window = cw;
3792 map_event.xmaprequest.parent = Scr.Root;
3793 dispatch_event(&map_event);
3794 /* note: we really should handle all map and unmap notify
3795 * events for that window in a loop here */
3798 return;
3801 void HandleVisibilityNotify(const evh_args_t *ea)
3803 FvwmWindow * const fw = ea->exc->w.fw;
3805 DBUG("HandleVisibilityNotify", "Routine Entered");
3807 if (fw && ea->exc->x.etrigger->xvisibility.window == FW_W_FRAME(fw))
3809 switch (ea->exc->x.etrigger->xvisibility.state)
3811 case VisibilityUnobscured:
3812 SET_FULLY_VISIBLE(fw, 1);
3813 SET_PARTIALLY_VISIBLE(fw, 1);
3814 break;
3815 case VisibilityPartiallyObscured:
3816 SET_FULLY_VISIBLE(fw, 0);
3817 SET_PARTIALLY_VISIBLE(fw, 1);
3818 break;
3819 default:
3820 SET_FULLY_VISIBLE(fw, 0);
3821 SET_PARTIALLY_VISIBLE(fw, 0);
3822 break;
3824 /* Make sure the button grabs are up to date */
3825 focus_grab_buttons(fw);
3828 return;
3831 /* ---------------------------- interface functions ------------------------ */
3833 /* Inform a client window of its geometry.
3835 * The input (frame) geometry will be translated to client geometry
3836 * before sending. */
3837 void SendConfigureNotify(
3838 FvwmWindow *fw, int x, int y, int w, int h, int bw,
3839 Bool send_for_frame_too)
3841 XEvent client_event;
3842 size_borders b;
3844 if (!fw || IS_SHADED(fw))
3846 return;
3848 client_event.type = ConfigureNotify;
3849 client_event.xconfigure.display = dpy;
3850 client_event.xconfigure.event = FW_W(fw);
3851 client_event.xconfigure.window = FW_W(fw);
3852 get_window_borders(fw, &b);
3853 client_event.xconfigure.x = x + b.top_left.width;
3854 client_event.xconfigure.y = y + b.top_left.height;
3855 client_event.xconfigure.width = w - b.total_size.width;
3856 client_event.xconfigure.height = h - b.total_size.height;
3857 client_event.xconfigure.border_width = bw;
3858 client_event.xconfigure.above = FW_W_FRAME(fw);
3859 client_event.xconfigure.override_redirect = False;
3860 #if 0
3861 fprintf(stderr,
3862 "send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x '%s'\n",
3863 client_event.xconfigure.x, client_event.xconfigure.y,
3864 client_event.xconfigure.width, client_event.xconfigure.height,
3865 (int)FW_W_FRAME(fw), (int)FW_W(fw),
3866 (int)client_event.xconfigure.window,
3867 (fw->name.name) ? fw->name.name : "");
3868 #endif
3869 FSendEvent(
3870 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
3871 if (send_for_frame_too)
3873 /* This is for buggy tk, which waits for the real
3874 * ConfigureNotify on frame instead of the synthetic one on w.
3875 * The geometry data in the event will not be correct for the
3876 * frame, but tk doesn't look at that data anyway. */
3877 client_event.xconfigure.event = FW_W_FRAME(fw);
3878 client_event.xconfigure.window = FW_W_FRAME(fw);
3879 FSendEvent(
3880 dpy, FW_W_FRAME(fw), False, StructureNotifyMask,
3881 &client_event);
3884 return;
3887 /* Add an event group to the event handler */
3888 int register_event_group(int event_base, int event_count, PFEH *jump_table)
3890 /* insert into the list */
3891 event_group_t *group;
3892 event_group_t *position = base_event_group;
3893 event_group_t *prev_position = NULL;
3895 while (
3896 position != NULL &&
3897 position->base + position->count < event_base)
3899 prev_position = position;
3900 position = position->next;
3902 if ((position != NULL && position->base < event_base + event_count))
3904 /* there is already an event group registered at the specified
3905 * event range, or the base is before the base X events */
3907 return 1;
3909 /* create the group structure (these are not freed until fvwm exits) */
3910 group = (event_group_t*)safemalloc(sizeof(event_group_t));
3911 group->base = event_base;
3912 group->count = event_count;
3913 group->jump_table = jump_table;
3914 group->next = position;
3915 if (prev_position != NULL)
3917 prev_position->next = group;
3919 else
3921 base_event_group = group;
3924 return 0;
3928 ** Procedure:
3929 ** InitEventHandlerJumpTable
3931 void InitEventHandlerJumpTable(void)
3933 static PFEH EventHandlerJumpTable[LASTEvent];
3934 int i;
3936 for (i=0; i<LASTEvent; i++)
3938 EventHandlerJumpTable[i] = NULL;
3940 EventHandlerJumpTable[Expose] = HandleExpose;
3941 EventHandlerJumpTable[DestroyNotify] = HandleDestroyNotify;
3942 EventHandlerJumpTable[MapRequest] = HandleMapRequest;
3943 EventHandlerJumpTable[MapNotify] = HandleMapNotify;
3944 EventHandlerJumpTable[UnmapNotify] = HandleUnmapNotify;
3945 EventHandlerJumpTable[ButtonPress] = HandleButtonPress;
3946 EventHandlerJumpTable[EnterNotify] = HandleEnterNotify;
3947 EventHandlerJumpTable[LeaveNotify] = HandleLeaveNotify;
3948 EventHandlerJumpTable[FocusIn] = HandleFocusIn;
3949 EventHandlerJumpTable[FocusOut] = HandleFocusOut;
3950 EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
3951 EventHandlerJumpTable[ClientMessage] = HandleClientMessage;
3952 EventHandlerJumpTable[PropertyNotify] = HandlePropertyNotify;
3953 EventHandlerJumpTable[KeyPress] = HandleKeyPress;
3954 EventHandlerJumpTable[KeyRelease] = HandleKeyRelease;
3955 EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
3956 EventHandlerJumpTable[ColormapNotify] = HandleColormapNotify;
3957 EventHandlerJumpTable[SelectionClear] = HandleSelectionClear;
3958 EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
3959 EventHandlerJumpTable[ReparentNotify] = HandleReparentNotify;
3960 EventHandlerJumpTable[MappingNotify] = HandleMappingNotify;
3961 STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
3962 STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
3963 #ifdef MOUSE_DROPPINGS
3964 STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
3965 #else /* no MOUSE_DROPPINGS */
3966 STROKE_CODE(stroke_init());
3967 #endif /* MOUSE_DROPPINGS */
3968 if (register_event_group(0, LASTEvent, EventHandlerJumpTable))
3970 /* should never happen */
3971 fvwm_msg(ERR, "InitEventHandlerJumpTable",
3972 "Faild to initialize event handlers");
3973 exit(1);
3975 if (FShapesSupported)
3977 static PFEH shape_jump_table[FShapeNumberEvents];
3979 for (i = 0; i < FShapeNumberEvents; i++)
3981 shape_jump_table[i] = NULL;
3983 shape_jump_table[FShapeNotify] = HandleShapeNotify;
3984 if (
3985 register_event_group(
3986 FShapeEventBase, FShapeNumberEvents,
3987 shape_jump_table))
3989 fvwm_msg(ERR, "InitEventHandlerJumpTable",
3990 "Faild to init Shape event handler");
3995 return;
3998 /* handle a single X event */
3999 void dispatch_event(XEvent *e)
4001 Window w = e->xany.window;
4002 FvwmWindow *fw;
4003 event_group_t *event_group;
4005 DBUG("dispatch_event", "Routine Entered");
4007 XFlush(dpy);
4008 if (w == Scr.Root)
4010 switch (e->type)
4012 case ButtonPress:
4013 case ButtonRelease:
4014 if (e->xbutton.subwindow != None)
4016 w = e->xbutton.subwindow;
4018 case MapRequest:
4019 w = e->xmaprequest.window;
4020 break;
4021 default:
4022 break;
4025 if (w == Scr.Root ||
4026 XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4028 fw = NULL;
4030 last_event_type = e->type;
4031 event_group = base_event_group;
4032 while (
4033 event_group != NULL &&
4034 event_group->base + event_group->count < e->type)
4036 event_group = event_group->next;
4039 if (
4040 event_group != NULL &&
4041 e->type - event_group->base < event_group->count &&
4042 event_group->jump_table[e->type - event_group->base] != NULL)
4044 evh_args_t ea;
4045 exec_context_changes_t ecc;
4046 Window dummyw;
4048 ecc.type = EXCT_EVENT;
4049 ecc.x.etrigger = e;
4050 ecc.w.wcontext = GetContext(&fw, fw, e, &dummyw);
4051 ecc.w.w = w;
4052 ecc.w.fw = fw;
4053 ea.exc = exc_create_context(
4054 &ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W |
4055 ECC_WCONTEXT);
4056 (*event_group->jump_table[e->type - event_group->base])(&ea);
4057 exc_destroy_context(ea.exc);
4060 #ifdef C_ALLOCA
4061 /* If we're using the C version of alloca, see if anything needs to be
4062 * freed up.
4064 alloca(0);
4065 #endif
4066 DBUG("dispatch_event", "Leaving Routine");
4068 return;
4071 /* ewmh configure request */
4072 void events_handle_configure_request(
4073 XConfigureRequestEvent cre, FvwmWindow *fw, Bool force,
4074 int force_gravity)
4076 __handle_configure_request(cre, NULL, fw, force, force_gravity);
4078 return;
4081 void HandleEvents(void)
4083 XEvent ev;
4085 DBUG("HandleEvents", "Routine Entered");
4086 STROKE_CODE(send_motion = False);
4087 while (!isTerminated)
4089 last_event_type = 0;
4090 if (Scr.flags.is_window_scheduled_for_destroy)
4092 destroy_scheduled_windows();
4094 if (Scr.flags.do_need_window_update)
4096 flush_window_updates();
4098 if (My_XNextEvent(dpy, &ev))
4100 dispatch_event(&ev);
4102 if (Scr.flags.do_need_style_list_update)
4104 simplify_style_list();
4108 return;
4113 * Waits for next X or module event, fires off startup routines when startup
4114 * modules have finished or after a timeout if the user has specified a
4115 * command line module that doesn't quit or gets stuck.
4118 int My_XNextEvent(Display *dpy, XEvent *event)
4120 fd_set in_fdset, out_fdset;
4121 int num_fd;
4122 fmodule_list_itr moditr;
4123 fmodule *module;
4124 fmodule_input *input;
4125 static struct timeval timeout;
4126 static struct timeval *timeoutP = &timeout;
4128 DBUG("My_XNextEvent", "Routine Entered");
4130 /* check for any X events already queued up.
4131 * Side effect: this does an XFlush if no events are queued
4132 * Make sure nothing between here and the select causes further
4133 * X requests to be sent or the select may block even though
4134 * there are events in the queue */
4135 if (FPending(dpy))
4137 DBUG(
4138 "My_XNextEvent", "taking care of queued up events"
4139 " & returning (1)");
4140 FNextEvent(dpy, event);
4141 return 1;
4144 /* check for termination of all startup modules */
4145 if (fFvwmInStartup)
4147 module_list_itr_init(&moditr);
4148 module = module_list_itr_next(&moditr);
4149 for (; module != NULL; module = module_list_itr_next(&moditr))
4151 if (MOD_IS_CMDLINE(module) == 1)
4153 break;
4156 module_cleanup();
4157 if (module == NULL)
4159 /* last module */
4160 DBUG(
4161 "My_XNextEvent",
4162 "Starting up after command lines modules");
4163 /* set an infinite timeout to stop ticking */
4164 timeoutP = NULL;
4165 /* This may cause X requests to be sent */
4166 StartupStuff();
4168 return 0; /* so return without select()ing */
4172 /* Some signals can interrupt us while we wait for any action
4173 * on our descriptors. While some of these signals may be asking
4174 * fvwm to die, some might be harmless. Harmless interruptions
4175 * mean we have to start waiting all over again ... */
4178 int ms;
4179 Bool is_waiting_for_scheduled_command = False;
4180 static struct timeval *old_timeoutP = NULL;
4182 /* The timeouts become undefined whenever the select returns,
4183 * and so we have to reinitialise them */
4184 ms = squeue_get_next_ms();
4185 if (ms == 0)
4187 /* run scheduled commands */
4188 squeue_execute();
4189 ms = squeue_get_next_ms();
4190 /* should not happen anyway.
4191 * get_next_schedule_queue_ms() can't return 0 after a
4192 * call to execute_schedule_queue(). */
4193 if (ms == 0)
4195 ms = 1;
4198 if (ms < 0)
4200 timeout.tv_sec = 42;
4201 timeout.tv_usec = 0;
4203 else
4205 /* scheduled commands are pending - don't wait too
4206 * long */
4207 timeout.tv_sec = ms / 1000;
4208 timeout.tv_usec = 1000 * (ms % 1000);
4209 old_timeoutP = timeoutP;
4210 timeoutP = &timeout;
4211 is_waiting_for_scheduled_command = True;
4214 FD_ZERO(&in_fdset);
4215 FD_ZERO(&out_fdset);
4216 FD_SET(x_fd, &in_fdset);
4218 /* nothing is done here if fvwm was compiled without session
4219 * support */
4220 if (sm_fd >= 0)
4222 FD_SET(sm_fd, &in_fdset);
4225 module_list_itr_init(&moditr);
4226 while ( (module = module_list_itr_next(&moditr)) != NULL)
4228 FD_SET(MOD_READFD(module), &in_fdset);
4230 if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
4232 FD_SET(MOD_WRITEFD(module), &out_fdset);
4236 DBUG("My_XNextEvent", "waiting for module input/output");
4237 num_fd = fvwmSelect(
4238 fvwmlib_max_fd, &in_fdset, &out_fdset, 0, timeoutP);
4239 if (is_waiting_for_scheduled_command)
4241 timeoutP = old_timeoutP;
4244 /* Express route out of fvwm ... */
4245 if (isTerminated)
4247 return 0;
4249 } while (num_fd < 0);
4251 if (num_fd > 0)
4253 /* Check for module input. */
4254 module_list_itr_init(&moditr);
4255 while ( (module = module_list_itr_next(&moditr)) != NULL)
4257 if (FD_ISSET(MOD_READFD(module), &in_fdset))
4259 input = module_receive(module);
4260 /* enqueue the received command */
4261 module_input_enqueue(input);
4263 if (
4264 MOD_WRITEFD(module) >= 0 &&
4265 FD_ISSET(MOD_WRITEFD(module), &out_fdset))
4267 DBUG("My_XNextEvent",
4268 "calling FlushMessageQueue");
4269 FlushMessageQueue(module);
4273 /* execute any commands queued up */
4274 DBUG("My_XNextEvent", "executing module comand queue");
4275 ExecuteCommandQueue();
4277 /* cleanup dead modules */
4278 module_cleanup();
4280 /* nothing is done here if fvwm was compiled without session
4281 * support */
4282 if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
4284 ProcessICEMsgs();
4288 else
4290 /* select has timed out, things must have calmed down so let's
4291 * decorate */
4292 if (fFvwmInStartup)
4294 fvwm_msg(ERR, "My_XNextEvent",
4295 "Some command line modules have not quit, "
4296 "Starting up after timeout.\n");
4297 StartupStuff();
4298 timeoutP = NULL; /* set an infinite timeout to stop
4299 * ticking */
4300 reset_style_changes();
4301 Scr.flags.do_need_window_update = 0;
4303 /* run scheduled commands if necessary */
4304 squeue_execute();
4307 /* check for X events again, rather than return 0 and get called again
4309 if (FPending(dpy))
4311 DBUG("My_XNextEvent",
4312 "taking care of queued up events & returning (2)");
4313 FNextEvent(dpy,event);
4314 return 1;
4317 DBUG("My_XNextEvent", "leaving My_XNextEvent");
4318 return 0;
4323 * Procedure:
4324 * Find the Fvwm context for the event.
4327 int GetContext(FvwmWindow **ret_fw, FvwmWindow *t, const XEvent *e, Window *w)
4329 int context;
4330 Window win;
4331 Window subw = None;
4332 int x = 0;
4333 int y = 0;
4334 Bool is_key_event = False;
4336 win = e->xany.window;
4337 context = C_NO_CONTEXT;
4338 switch (e->type)
4340 case KeyPress:
4341 case KeyRelease:
4342 x = e->xkey.x;
4343 y = e->xkey.y;
4344 subw = e->xkey.subwindow;
4345 if (win == Scr.Root && subw != None)
4347 /* Translate root coordinates into subwindow
4348 * coordinates. Necessary for key bindings that work
4349 * over unfocused windows. */
4350 win = subw;
4351 XTranslateCoordinates(
4352 dpy, Scr.Root, subw, x, y, &x, &y, &subw);
4353 XFindContext(dpy, win, FvwmContext, (caddr_t *) &t);
4355 is_key_event = True;
4356 /* fall through */
4357 case ButtonPress:
4358 case ButtonRelease:
4359 if (!is_key_event)
4361 x = e->xbutton.x;
4362 y = e->xbutton.y;
4363 subw = e->xbutton.subwindow;
4365 if (t && win == FW_W_FRAME(t) && subw != None)
4367 /* Translate frame coordinates into subwindow
4368 * coordinates. */
4369 win = subw;
4370 XTranslateCoordinates(
4371 dpy, FW_W_FRAME(t), subw, x, y, &x, &y, &subw);
4372 if (win == FW_W_PARENT(t))
4374 win = subw;
4375 XTranslateCoordinates(
4376 dpy, FW_W_PARENT(t), subw, x, y, &x,
4377 &y, &subw);
4380 break;
4381 default:
4382 XFindContext(dpy, win, FvwmContext, (caddr_t *)&t);
4383 break;
4385 if (ret_fw != NULL)
4387 *ret_fw = t;
4389 if (!t)
4391 return C_ROOT;
4393 *w = win;
4394 if (*w == Scr.NoFocusWin)
4396 return C_ROOT;
4398 if (subw != None)
4400 if (win == FW_W_PARENT(t))
4402 *w = subw;
4405 if (*w == Scr.Root)
4407 return C_ROOT;
4409 context = frame_window_id_to_context(t, *w, &Button);
4411 return context;
4416 * Removes expose events for a specific window from the queue
4419 int flush_expose(Window w)
4421 XEvent dummy;
4422 int i=0;
4424 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4426 i++;
4429 return i;
4432 /* same as above, but merges the expose rectangles into a single big one */
4433 int flush_accumulate_expose(Window w, XEvent *e)
4435 XEvent dummy;
4436 int i = 0;
4437 int x1 = e->xexpose.x;
4438 int y1 = e->xexpose.y;
4439 int x2 = x1 + e->xexpose.width;
4440 int y2 = y1 + e->xexpose.height;
4442 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4444 x1 = min(x1, dummy.xexpose.x);
4445 y1 = min(y1, dummy.xexpose.y);
4446 x2 = max(x2, dummy.xexpose.x + dummy.xexpose.width);
4447 y2 = max(y2, dummy.xexpose.y + dummy.xexpose.height);
4448 i++;
4450 e->xexpose.x = x1;
4451 e->xexpose.y = y1;
4452 e->xexpose.width = x2 - x1;
4453 e->xexpose.height = y2 - y1;
4455 return i;
4460 * Removes all expose events from the queue and does the necessary redraws
4463 void handle_all_expose(void)
4465 void *saved_event;
4466 XEvent evdummy;
4468 saved_event = fev_save_event();
4469 FPending(dpy);
4470 while (FCheckMaskEvent(dpy, ExposureMask, &evdummy))
4472 dispatch_event(&evdummy);
4474 fev_restore_event(saved_event);
4476 return;
4479 /* CoerceEnterNotifyOnCurrentWindow()
4480 * Pretends to get a HandleEnterNotify on the window that the pointer
4481 * currently is in so that the focus gets set correctly from the beginning.
4482 * Note that this presently only works if the current window is not
4483 * click_to_focus; I think that that behaviour is correct and desirable.
4484 * --11/08/97 gjb */
4485 void CoerceEnterNotifyOnCurrentWindow(void)
4487 Window child;
4488 Window root;
4489 Bool f;
4490 evh_args_t ea;
4491 exec_context_changes_t ecc;
4492 XEvent e;
4493 FvwmWindow *fw;
4495 f = FQueryPointer(
4496 dpy, Scr.Root, &root, &child, &e.xcrossing.x_root,
4497 &e.xcrossing.y_root, &e.xcrossing.x, &e.xcrossing.y,
4498 &JunkMask);
4499 if (f == False || child == None)
4501 return;
4503 e.xcrossing.type = EnterNotify;
4504 e.xcrossing.window = child;
4505 e.xcrossing.subwindow = None;
4506 e.xcrossing.mode = NotifyNormal;
4507 e.xcrossing.detail = NotifyAncestor;
4508 e.xcrossing.same_screen = True;
4509 if (XFindContext(dpy, child, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4511 fw = NULL;
4513 else
4515 XTranslateCoordinates(
4516 dpy, Scr.Root, child, e.xcrossing.x_root,
4517 e.xcrossing.y_root, &JunkX, &JunkY, &child);
4518 if (child == FW_W_PARENT(fw))
4520 child = FW_W(fw);
4522 if (child != None)
4524 e.xany.window = child;
4527 e.xcrossing.focus = (fw == get_focus_window()) ? True : False;
4528 ecc.type = EXCT_NULL;
4529 ecc.x.etrigger = &e;
4530 ea.exc = exc_create_context(&ecc, ECC_TYPE | ECC_ETRIGGER);
4531 HandleEnterNotify(&ea);
4532 exc_destroy_context(ea.exc);
4534 return;
4537 /* This function discards all queued up ButtonPress, ButtonRelease and
4538 * ButtonMotion events. */
4539 int discard_events(long event_mask)
4541 XEvent e;
4542 int count;
4544 XSync(dpy, 0);
4545 for (count = 0; FCheckMaskEvent(dpy, event_mask, &e); count++)
4547 /* nothing */
4550 return count;
4553 /* This function discards all queued up ButtonPress, ButtonRelease and
4554 * ButtonMotion events. */
4555 int discard_window_events(Window w, long event_mask)
4557 XEvent e;
4558 int count;
4560 XSync(dpy, 0);
4561 for (count = 0; FCheckWindowEvent(dpy, w, event_mask, &e); count++)
4563 /* nothing */
4566 return count;
4569 /* Similar function for certain types of PropertyNotify. */
4570 int flush_property_notify(Atom atom, Window w)
4572 XEvent e;
4573 int count;
4574 test_typed_window_event_args args;
4576 XSync(dpy, 0);
4577 args.w = w;
4578 args.event_type = PropertyNotify;
4579 for (count = 0;
4580 FCheckPeekIfEvent(
4581 dpy, &e, test_typed_window_event, (XPointer)&args);
4582 count++)
4584 Bool rc;
4586 if (e.xproperty.atom != atom)
4588 break;
4590 /* remove the event from the queue */
4591 rc = FCheckIfEvent(
4592 dpy, &e, test_typed_window_event, (XPointer)&args);
4595 return count;
4598 /* Wait for all mouse buttons to be released
4599 * This can ease some confusion on the part of the user sometimes
4601 * Discard superflous button events during this wait period. */
4602 void WaitForButtonsUp(Bool do_handle_expose)
4604 unsigned int mask;
4605 unsigned int bmask;
4606 long evmask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
4607 KeyPressMask|KeyReleaseMask;
4608 int count;
4609 int use_wait_cursor;
4610 XEvent e;
4612 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
4613 &JunkX, &JunkY, &mask) == False)
4615 /* pointer is on a different screen - that's okay here */
4617 mask &= DEFAULT_ALL_BUTTONS_MASK;
4618 if (mask == 0)
4620 return;
4622 if (do_handle_expose)
4624 evmask |= ExposureMask;
4626 GrabEm(None, GRAB_NORMAL);
4627 for (count = 0, use_wait_cursor = 0; mask != 0; count++)
4629 /* handle expose events */
4630 XAllowEvents(dpy, SyncPointer, CurrentTime);
4631 if (FCheckMaskEvent(dpy, evmask, &e))
4633 switch (e.type)
4635 case ButtonRelease:
4636 if (e.xbutton.button <=
4637 NUMBER_OF_MOUSE_BUTTONS)
4639 bmask = (Button1Mask <<
4640 (e.xbutton.button - 1));
4641 mask = e.xbutton.state & ~bmask;
4643 break;
4644 case Expose:
4645 dispatch_event(&e);
4646 break;
4647 default:
4648 break;
4651 else
4653 if (FQueryPointer(
4654 dpy, Scr.Root, &JunkRoot, &JunkChild,
4655 &JunkX, &JunkY, &JunkX, &JunkY, &mask) ==
4656 False)
4658 /* pointer is on a different screen - that's
4659 * okay here */
4661 mask &= DEFAULT_ALL_BUTTONS_MASK;
4662 usleep(1);
4664 if (use_wait_cursor == 0 && count == 20)
4666 GrabEm(CRS_WAIT, GRAB_NORMAL);
4667 use_wait_cursor = 1;
4670 UngrabEm(GRAB_NORMAL);
4671 if (use_wait_cursor)
4673 UngrabEm(GRAB_NORMAL);
4674 XFlush(dpy);
4677 return;
4680 void sync_server(int toggle)
4682 static Bool synced = False;
4684 if (toggle == -1)
4686 toggle = (synced == False);
4688 if (toggle == 1)
4690 synced = True;
4692 else
4694 synced = False;
4696 XSynchronize(dpy, synced);
4697 XFlush(dpy);
4699 return;
4702 Bool is_resizing_event_pending(
4703 FvwmWindow *fw)
4705 XEvent e;
4706 check_if_event_args args;
4708 args.w = FW_W(fw);
4709 args.do_return_true = False;
4710 args.do_return_true_cr = False;
4711 args.cr_value_mask = 0;
4712 args.ret_does_match = False;
4713 args.ret_type = 0;
4714 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
4716 return args.ret_does_match;
4719 /* ---------------------------- builtin commands --------------------------- */
4721 void CMD_XSynchronize(F_CMD_ARGS)
4723 int toggle;
4725 toggle = ParseToggleArgument(action, NULL, -1, 0);
4726 sync_server(toggle);
4728 return;
4731 void CMD_XSync(F_CMD_ARGS)
4733 XSync(dpy, 0);
4735 return;