* removed unused variable
[fvwm.git] / fvwm / events.c
blob71486649e82d4fcbc76e27412f637f6b92aabf97
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
113 ** LASTEvent is the number of X events defined - it should be defined
114 ** in X.h (to be like 35), but since extension (eg SHAPE) events are
115 ** numbered beyond LASTEvent, we need to use a bigger number than the
116 ** default, so let's undefine the default and use 256 instead.
118 #undef LASTEvent
119 #define LASTEvent 256
121 #define CR_MOVERESIZE_MASK (CWX | CWY | CWWidth | CWHeight | CWBorderWidth)
123 /* ---------------------------- local macros ------------------------------- */
125 /* ---------------------------- imports ------------------------------------ */
127 extern void StartupStuff(void);
129 /* ---------------------------- included code files ------------------------ */
131 /* ---------------------------- local types -------------------------------- */
133 typedef void (*PFEH)(const evh_args_t *ea);
135 typedef struct
137 Window w;
138 Bool do_return_true;
139 Bool do_return_true_cr;
140 unsigned long cr_value_mask;
141 Bool ret_does_match;
142 unsigned long ret_type;
143 } check_if_event_args;
145 typedef struct
147 unsigned do_forbid_function : 1;
148 unsigned do_focus : 1;
149 unsigned do_swallow_click : 1;
150 unsigned do_raise : 1;
151 } hfrc_ret_t;
153 /* ---------------------------- forward declarations ----------------------- */
155 /* ---------------------------- local variables ---------------------------- */
157 static int Button = 0;
158 static const FvwmWindow *xcrossing_last_grab_window = NULL;
159 STROKE_CODE(static int send_motion);
160 STROKE_CODE(static char sequence[STROKE_MAX_SEQUENCE + 1]);
161 static PFEH EventHandlerJumpTable[LASTEvent];
163 /* ---------------------------- exported variables (globals) --------------- */
165 int last_event_type = 0;
166 Window PressedW = None;
168 /* ---------------------------- local functions ---------------------------- */
170 static void fake_map_unmap_notify(const FvwmWindow *fw, int event_type)
172 XEvent client_event;
173 XWindowAttributes winattrs = {0};
175 if (!XGetWindowAttributes(dpy, FW_W(fw), &winattrs))
177 return;
179 XSelectInput(
180 dpy, FW_W(fw),
181 winattrs.your_event_mask & ~StructureNotifyMask);
182 client_event.type = event_type;
183 client_event.xmap.display = dpy;
184 client_event.xmap.event = FW_W(fw);
185 client_event.xmap.window = FW_W(fw);
186 switch (event_type)
188 case MapNotify:
189 client_event.xmap.override_redirect = False;
190 break;
191 case UnmapNotify:
192 client_event.xunmap.from_configure = False;
193 break;
194 default:
195 /* not possible if called correctly */
196 break;
198 FSendEvent(
199 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
200 XSelectInput(dpy, FW_W(fw), winattrs.your_event_mask);
201 XFlush(dpy);
203 return;
206 static Bool test_map_request(
207 Display *display, XEvent *event, XPointer arg)
209 check_if_event_args *cie_args;
210 Bool rc;
212 cie_args = (check_if_event_args *)arg;
213 cie_args->ret_does_match = False;
214 if (event->type == MapRequest &&
215 event->xmaprequest.window == cie_args->w)
217 cie_args->ret_type = MapRequest;
218 cie_args->ret_does_match = True;
219 rc = cie_args->do_return_true;
221 else
223 cie_args->ret_type = 0;
224 rc = False;
227 /* Yes, it is correct that this function always returns False. */
228 return rc;
231 Bool test_button_event(
232 Display *display, XEvent *event, XPointer arg)
234 if (event->type == ButtonPress || event->type == ButtonRelease)
236 return True;
239 return False;
242 Bool test_typed_window_event(
243 Display *display, XEvent *event, XPointer arg)
245 test_typed_window_event_args *ta = (test_typed_window_event_args *)arg;
247 if (event->xany.window == ta->w &&
248 event->xany.type == ta->event_type)
250 return True;
253 return False;
256 static Bool test_resizing_event(
257 Display *display, XEvent *event, XPointer arg)
259 check_if_event_args *cie_args;
260 Bool rc;
262 cie_args = (check_if_event_args *)arg;
263 cie_args->ret_does_match = False;
264 if (event->xany.window != cie_args->w)
266 return False;
268 rc = False;
269 switch (event->type)
271 case ConfigureRequest:
272 if ((event->xconfigurerequest.value_mask &
273 cie_args->cr_value_mask) != 0)
275 cie_args->ret_type = ConfigureRequest;
276 cie_args->ret_does_match = True;
277 rc = cie_args->do_return_true_cr;
279 break;
280 case PropertyNotify:
281 if (event->xproperty.atom == XA_WM_NORMAL_HINTS)
283 cie_args->ret_type = PropertyNotify;
284 cie_args->ret_does_match = True;
285 rc = cie_args->do_return_true;
287 default:
288 break;
291 /* Yes, it is correct that this function may always returns False. */
292 return rc;
295 static inline void __handle_cr_on_unmanaged(XConfigureRequestEvent *cre)
297 XWindowChanges xwc;
298 unsigned long xwcm;
300 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
301 xwc.x = cre->x;
302 xwc.y = cre->y;
303 xwc.width = cre->width;
304 xwc.height = cre->height;
305 xwc.border_width = cre->border_width;
306 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
308 return;
311 static inline void __handle_cr_on_icon(
312 XConfigureRequestEvent *cre, FvwmWindow *fw)
314 XWindowChanges xwc;
315 unsigned long xwcm;
317 xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
318 xwc.x = cre->x;
319 xwc.y = cre->y;
320 xwc.width = cre->width;
321 xwc.height = cre->height;
322 xwc.border_width = cre->border_width;
323 if (FW_W_ICON_PIXMAP(fw) == cre->window)
325 int bw;
327 if (cre->value_mask & CWBorderWidth)
329 fw->icon_border_width = cre->border_width;
331 bw = fw->icon_border_width;
332 if ((cre->value_mask & (CWWidth | CWHeight)) ==
333 (CWWidth | CWHeight))
335 set_icon_picture_size(
336 fw, cre->width + 2 * bw, cre->height + 2 * bw);
339 set_icon_position(fw, cre->x, cre->y);
340 broadcast_icon_geometry(fw, False);
341 XConfigureWindow(dpy, cre->window, xwcm, &xwc);
342 if (cre->window != FW_W_ICON_PIXMAP(fw) &&
343 FW_W_ICON_PIXMAP(fw) != None)
345 rectangle g;
347 get_icon_picture_geometry(fw, &g);
348 xwc.x = g.x;
349 xwc.y = g.y;
350 xwcm = cre->value_mask & (CWX | CWY);
351 XConfigureWindow(
352 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
354 if (FW_W_ICON_TITLE(fw) != None)
356 rectangle g;
358 get_icon_title_geometry(fw, &g);
359 xwc.x = g.x;
360 xwc.y = g.y;
361 xwcm = cre->value_mask & (CWX | CWY);
362 XConfigureWindow(
363 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
366 return;
369 static inline void __handle_cr_on_shaped(FvwmWindow *fw)
371 /* suppress compiler warnings w/o shape extension */
372 int i = 0;
373 unsigned int u = 0;
374 Bool b = False;
375 int boundingShaped;
377 if (FShapeQueryExtents(
378 dpy, FW_W(fw), &boundingShaped, &i, &i, &u, &u, &b,
379 &i, &i, &u, &u))
381 fw->wShaped = boundingShaped;
383 else
385 fw->wShaped = 0;
388 return;
391 static inline void __handle_cr_restack(
392 int *ret_do_send_event, XConfigureRequestEvent *cre, FvwmWindow *fw)
394 XWindowChanges xwc;
395 unsigned long xwcm;
396 FvwmWindow *fw2 = NULL;
398 if (cre->value_mask & CWSibling)
400 if (XFindContext(
401 dpy, cre->above, FvwmContext,
402 (caddr_t *)&fw2) == XCNOENT)
404 fw2 = NULL;
406 if (fw2 == fw)
408 fw2 = NULL;
411 if (cre->detail != Above && cre->detail != Below)
413 HandleUnusualStackmodes(
414 cre->detail, fw, cre->window, fw2, cre->above);
416 /* only allow clients to restack windows within their layer */
417 else if (fw2 == NULL || compare_window_layers(fw2, fw) != 0)
419 switch (cre->detail)
421 case Above:
422 RaiseWindow(fw, True);
423 break;
424 case Below:
425 LowerWindow(fw, True);
426 break;
429 else
431 xwc.sibling = FW_W_FRAME(fw2);
432 xwc.stack_mode = cre->detail;
433 xwcm = CWSibling | CWStackMode;
434 XConfigureWindow(dpy, FW_W_FRAME(fw), xwcm, &xwc);
436 /* Maintain the condition that icon windows are stacked
437 * immediately below their frame
438 * 1. for fw */
439 xwc.sibling = FW_W_FRAME(fw);
440 xwc.stack_mode = Below;
441 xwcm = CWSibling | CWStackMode;
442 if (FW_W_ICON_TITLE(fw) != None)
444 XConfigureWindow(
445 dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
447 if (FW_W_ICON_PIXMAP(fw) != None)
449 XConfigureWindow(
450 dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
452 /* 2. for fw2 */
453 if (cre->detail == Below)
455 xwc.sibling = FW_W_FRAME(fw2);
456 xwc.stack_mode = Below;
457 xwcm = CWSibling | CWStackMode;
458 if (FW_W_ICON_TITLE(fw2) != None)
460 XConfigureWindow(
461 dpy, FW_W_ICON_TITLE(fw2), xwcm, &xwc);
463 if (FW_W_ICON_PIXMAP(fw2) != None)
465 XConfigureWindow(
466 dpy, FW_W_ICON_PIXMAP(fw2), xwcm,
467 &xwc);
470 /* Maintain the stacking order ring */
471 if (cre->detail == Above)
473 remove_window_from_stack_ring(fw);
474 add_window_to_stack_ring_after(
475 fw, get_prev_window_in_stack_ring(fw2));
477 else /* cre->detail == Below */
479 remove_window_from_stack_ring(fw);
480 add_window_to_stack_ring_after(fw, fw2);
482 BroadcastRestackThisWindow(fw);
484 /* srt (28-Apr-2001): Tk needs a ConfigureNotify event after a
485 * raise, otherwise it would hang for two seconds */
486 *ret_do_send_event = 1;
488 return;
491 static inline void __cr_get_static_position(
492 rectangle *ret_g, FvwmWindow *fw, XConfigureRequestEvent *cre,
493 size_borders *b)
495 if (cre->value_mask & CWX)
497 ret_g->x = cre->x - b->top_left.width;
499 else
501 ret_g->x = fw->g.frame.x;
503 if (cre->value_mask & CWY)
505 ret_g->y = cre->y - b->top_left.height;
507 else
509 ret_g->y = fw->g.frame.y;
512 return;
515 static inline void __cr_get_grav_position(
516 rectangle *ret_g, FvwmWindow *fw, XConfigureRequestEvent *cre,
517 size_borders *b)
519 int grav_x;
520 int grav_y;
522 gravity_get_offsets(fw->hints.win_gravity, &grav_x, &grav_y);
523 if (cre->value_mask & CWX)
525 ret_g->x = cre->x - ((grav_x + 1) * b->total_size.width) / 2;
527 else
529 ret_g->x = fw->g.frame.x;
531 if (cre->value_mask & CWY)
533 ret_g->y = cre->y - ((grav_y + 1) * b->total_size.height) / 2;
535 else
537 ret_g->y = fw->g.frame.y;
540 return;
543 /* Try to detect whether the application uses the ICCCM way of moving its
544 * window or the traditional way, always assuming StaticGravity. */
545 static inline void __cr_detect_icccm_move(
546 FvwmWindow *fw, XConfigureRequestEvent *cre, size_borders *b)
548 rectangle grav_g;
549 rectangle static_g;
550 rectangle dg_g;
551 rectangle ds_g;
552 int mx;
553 int my;
554 int m;
555 int w;
556 int h;
557 int has_x;
558 int has_y;
560 if (CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
562 if (Scr.bo.do_debug_cr_motion_method == 1)
564 fprintf(
565 stderr,
566 "_cdim: --- already detected (pid %d) %p"
567 " '%s'\n", HAS_EWMH_WM_PID(fw), fw,
568 fw->visible_name);
570 return;
572 if (HAS_EWMH_WM_PID(fw))
574 if (Scr.bo.do_debug_cr_motion_method == 1)
576 fprintf(
577 stderr,"_cdim: +++ has ewmh_wm_pid: icccm"
578 " %p '%s'\n", fw, fw->visible_name);
580 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
581 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
582 return;
584 if (fw->ewmh_window_type != EWMH_WINDOW_TYPE_NONE_ID)
586 if (Scr.bo.do_debug_cr_motion_method == 1)
588 fprintf(
589 stderr, "_cdim: +++ has ewmh_window_type:"
590 " icccm %p '%s'\n", fw,
591 fw->visible_name);
593 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
594 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
595 return;
597 if (FShapesSupported && fw->wShaped)
599 if (Scr.bo.do_debug_cr_motion_method == 1)
601 fprintf(
602 stderr, "_cdim: --- shaped window %p "
603 "'%s'\n", fw, fw->visible_name);
605 /* no detection for shaped windows */
606 return;
608 if (fw->hints.win_gravity == StaticGravity)
610 if (Scr.bo.do_debug_cr_motion_method == 1)
612 fprintf(
613 stderr, "_cdim: --- using StaticGravity"
614 " %p '%s'\n", fw, fw->visible_name);
616 return;
618 if (fw->hints.win_gravity == StaticGravity)
620 if (Scr.bo.do_debug_cr_motion_method == 1)
622 fprintf(
623 stderr, "_cdim: --- using StaticGravity"
624 " %p '%s'\n", fw, fw->visible_name);
626 return;
628 has_x = (cre->value_mask & CWX);
629 has_y = (cre->value_mask & CWY);
630 if (!has_x && !has_y)
632 if (Scr.bo.do_debug_cr_motion_method == 1)
634 fprintf(
635 stderr, "_cdim: --- not moved %p '%s'\n",
636 fw, fw->visible_name);
638 return;
640 __cr_get_grav_position(&grav_g, fw, cre, b);
641 __cr_get_static_position(&static_g, fw, cre, b);
642 if (static_g.x == grav_g.x)
644 /* both methods have the same result; ignore */
645 has_x = 0;
647 if (static_g.y == grav_g.y)
649 /* both methods have the same result; ignore */
650 has_y = 0;
652 if (!has_x && !has_y)
654 if (Scr.bo.do_debug_cr_motion_method == 1)
656 fprintf(
657 stderr, "_cdim: --- not moved %p '%s'\n",
658 fw, fw->visible_name);
660 return;
662 dg_g.x = grav_g.x - fw->g.frame.x;
663 dg_g.y = grav_g.y - fw->g.frame.y;
664 ds_g.x = static_g.x - fw->g.frame.x;
665 ds_g.y = static_g.y - fw->g.frame.y;
666 if (Scr.bo.do_debug_cr_motion_method == 1)
668 fprintf(
669 stderr, "s %3d/%3d %2d/%2d, g %3d/%3d %2d/%2d: ",
670 static_g.x, static_g.y, ds_g.x, ds_g.y, grav_g.x,
671 grav_g.y, dg_g.x, dg_g.y);
673 /* check full screen */
674 if ((cre->value_mask & (CWX | CWY)) == (CWX | CWY) &&
675 (has_x || has_y) &&
676 cre->width == Scr.MyDisplayWidth &&
677 cre->height == Scr.MyDisplayHeight)
679 if (grav_g.x == -b->top_left.width &&
680 grav_g.y == -b->top_left.height)
682 /* Window is fullscreen using the ICCCM way. */
683 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
684 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
685 if (Scr.bo.do_debug_cr_motion_method == 1)
687 fprintf(
688 stderr, "+++ fullscreen icccm %p"
689 " '%s'\n", fw, fw->visible_name);
691 return;
693 else if (static_g.x == -b->top_left.width &&
694 static_g.y == -b->top_left.height)
696 /* Window is fullscreen using the traditional way. */
697 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
698 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
699 if (Scr.bo.do_debug_cr_motion_method == 1)
701 fprintf(
702 stderr, "+++ fullscreen traditional"
703 " %p '%s'\n", fw,
704 fw->visible_name);
706 return;
709 /* check travelling across the screen */
710 if (has_x && dg_g.x == 0 && ds_g.x != 0 &&
711 has_y && dg_g.y == 0 && ds_g.y != 0)
713 /* The traditional way causes a shift by the border width or
714 * height. Use ICCCM way. */
715 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
716 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
717 if (Scr.bo.do_debug_cr_motion_method == 1)
719 fprintf(
720 stderr, "+++ travelling icccm %p '%s'\n",
721 fw, fw->visible_name);
723 return;
725 if (has_x && dg_g.x != 0 && ds_g.x == 0 &&
726 has_y && dg_g.y != 0 && ds_g.y == 0)
728 /* The ICCCM way causes a shift by the border width or height.
729 * Use traditional way. */
730 SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
731 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
732 if (Scr.bo.do_debug_cr_motion_method == 1)
734 fprintf(
735 stderr, "+++ travelling traditional %p"
736 " '%s'\n", fw, fw->visible_name);
738 return;
740 /* check placement near border */
741 w = (cre->value_mask & CWWidth) ?
742 cre->width + b->total_size.width : fw->g.frame.width;
743 h = (cre->value_mask & CWHeight) ?
744 cre->height + b->total_size.height : fw->g.frame.height;
745 if (!has_x)
747 mx = CR_MOTION_METHOD_AUTO;
749 else if (static_g.x == 0 || static_g.x + w == Scr.MyDisplayWidth)
751 mx = CR_MOTION_METHOD_STATIC_GRAV;
753 else if (grav_g.x == 0 || grav_g.x + w == Scr.MyDisplayWidth)
755 mx = CR_MOTION_METHOD_USE_GRAV;
757 else
759 mx = CR_MOTION_METHOD_AUTO;
761 if (!has_y)
763 my = CR_MOTION_METHOD_AUTO;
765 else if (static_g.y == 0 || static_g.y + h == Scr.MyDisplayHeight)
767 my = CR_MOTION_METHOD_STATIC_GRAV;
769 else if (grav_g.y == 0 || grav_g.y + h == Scr.MyDisplayHeight)
771 my = CR_MOTION_METHOD_USE_GRAV;
773 else
775 my = CR_MOTION_METHOD_AUTO;
777 m = (mx != CR_MOTION_METHOD_AUTO) ? mx : my;
778 if (m != CR_MOTION_METHOD_AUTO)
780 /* Window was placed next to the display border. */
781 if (m == my || my == CR_MOTION_METHOD_AUTO)
783 SET_CR_MOTION_METHOD(fw, m);
784 SET_CR_MOTION_METHOD_DETECTED(fw, 1);
785 if (Scr.bo.do_debug_cr_motion_method == 1)
787 fprintf(
788 stderr, "+++ near border %s %p "
789 "'%s'\n", (m ==
790 CR_MOTION_METHOD_USE_GRAV)
791 ? "icccm" : "traditional", fw,
792 fw->visible_name);
794 return;
797 if (Scr.bo.do_debug_cr_motion_method == 1)
799 fprintf(
800 stderr, "--- not detected %p '%s'\n", fw,
801 fw->visible_name);
804 return;
807 #define EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
808 /* This is not a good idea because this interferes with changes in the size
809 * hints of the window. However, it is impossible to be completely safe here.
810 * For example, if the client changes the size inc, then resizes the size of
811 * its window and then changes the size inc again - all in one batch - then
812 * the WM will read the *second* size inc upon the *first* event and use the
813 * wrong one in the ConfigureRequest calculations. */
814 /* dv (31 Mar 2002): The code now handles these situations, so enable it
815 * again. */
816 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
817 static inline int __merge_cr_moveresize(
818 const evh_args_t *ea, XConfigureRequestEvent *cre, FvwmWindow *fw,
819 size_borders *b)
821 int cn_count = 0;
822 XEvent e;
823 XConfigureRequestEvent *ecre;
824 check_if_event_args args;
826 args.w = cre->window;
827 args.do_return_true = False;
828 args.do_return_true_cr = True;
829 args.cr_value_mask = CR_MOVERESIZE_MASK;
830 args.ret_does_match = False;
831 args.ret_type = 0;
832 #if 0
833 /* free some CPU */
834 /* dv (7 May 2002): No, it's better to not reschedule processes here
835 * because some funny applications (XMMS, GTK) seem to expect that
836 * ConfigureRequests are handled instantly or they freak out. */
837 usleep(1);
838 #endif
839 for (cn_count = 0; 1; )
841 unsigned long vma;
842 unsigned long vmo;
843 unsigned long xm;
844 unsigned long ym;
845 evh_args_t ea2;
846 exec_context_changes_t ecc;
848 FCheckPeekIfEvent(
849 dpy, &e, test_resizing_event, (XPointer)&args);
850 ecre = &e.xconfigurerequest;
851 if (args.ret_does_match == False)
853 break;
855 else if (args.ret_type == PropertyNotify)
857 /* Can't merge events with a PropertyNotify in
858 * between. The event is still on the queue. */
859 break;
861 else if (args.ret_type != ConfigureRequest)
863 /* not good. unselected event type! */
864 continue;
866 /* Event was not yet removed from the queue but stored in e. */
867 xm = CWX | CWWidth;
868 ym = CWY | CWHeight;
869 vma = cre->value_mask & ecre->value_mask;
870 vmo = cre->value_mask | ecre->value_mask;
871 if (((vma & xm) == 0 && (vmo & xm) == xm) ||
872 ((vma & ym) == 0 && (vmo & ym) == ym))
874 /* can't merge events since location of window might
875 * get screwed up. */
876 break;
878 /* Finally remove the event from the queue */
879 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
880 /* partially handle the event */
881 ecre->value_mask &= ~args.cr_value_mask;
882 ea2.exc = exc_clone_context(ea->exc, &ecc, ECC_ETRIGGER);
883 HandleConfigureRequest(&ea2);
884 exc_destroy_context(ea2.exc);
885 /* collect the size/position changes */
886 if (ecre->value_mask & CWX)
888 cre->x = ecre->x;
890 if (ecre->value_mask & CWY)
892 cre->y = ecre->y;
894 if (ecre->value_mask & CWWidth)
896 cre->width = ecre->width;
898 if (ecre->value_mask & CWHeight)
900 cre->height = ecre->height;
902 if (ecre->value_mask & CWBorderWidth)
904 cre->border_width = ecre->border_width;
906 cre->value_mask |= (ecre->value_mask & CR_MOVERESIZE_MASK);
907 cn_count++;
910 return cn_count;
912 #endif
914 static inline int __handle_cr_on_client(
915 int *ret_do_send_event, XConfigureRequestEvent cre,
916 const evh_args_t *ea, FvwmWindow *fw, Bool force)
918 rectangle new_g;
919 rectangle d_g;
920 size_rect constr_dim;
921 size_rect oldnew_dim;
922 size_borders b;
923 int cn_count = 0;
925 if (ea)
927 cre = ea->exc->x.etrigger->xconfigurerequest;
929 if ((cre.value_mask & (CWWidth | CWHeight | CWX | CWY)) == 0)
931 return 0;
934 get_window_borders(fw, &b);
935 #ifdef EXPERIMENTAL_ANTI_RACE_CONDITION_CODE
936 /* Merge all pending ConfigureRequests for the window into a single
937 * event. However, we can not do this if the window uses the motion
938 * method autodetection because the merged event might confuse the
939 * detection code. */
940 if (ea && CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO)
942 cn_count = __merge_cr_moveresize(ea, &cre, fw, &b);
944 #endif
945 #if 0
946 fprintf(stderr,
947 "cre: %d(%d) %d(%d) %d(%d)x%d(%d) fw 0x%08x w 0x%08x "
948 "ew 0x%08x '%s'\n",
949 cre.x, (int)(cre.value_mask & CWX),
950 cre.y, (int)(cre.value_mask & CWY),
951 cre.width, (int)(cre.value_mask & CWWidth),
952 cre.height, (int)(cre.value_mask & CWHeight),
953 (int)FW_W_FRAME(fw), (int)FW_W(fw), (int)cre.window,
954 (fw->name.name) ? fw->name.name : "");
955 #endif
956 /* Don't modify frame_g fields before calling SetupWindow! */
957 memset(&d_g, 0, sizeof(d_g));
959 if (HAS_NEW_WM_NORMAL_HINTS(fw))
961 /* get the latest size hints */
962 XSync(dpy, 0);
963 GetWindowSizeHints(fw);
964 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
966 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMaxSize))
968 /* Java workaround */
969 if (cre.height > fw->hints.max_height &&
970 fw->hints.max_height <= BROKEN_MAXSIZE_LIMIT)
972 fw->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
973 cre.value_mask |= CWHeight;
975 if (cre.width > fw->hints.max_width &&
976 fw->hints.max_width <= BROKEN_MAXSIZE_LIMIT)
978 fw->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
979 cre.value_mask |= CWWidth;
982 if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMinSize))
984 if (cre.width < fw->hints.min_width &&
985 fw->hints.min_width >= BROKEN_MINSIZE_LIMIT)
987 fw->hints.min_width = 1;
988 cre.value_mask |= CWWidth;
990 if (cre.height < fw->hints.min_height &&
991 fw->hints.min_height >= BROKEN_MINSIZE_LIMIT)
993 fw->hints.min_height = 1;
994 cre.value_mask |= CWHeight;
997 if (IS_SHADED(fw) ||
998 !is_function_allowed(F_MOVE, NULL, fw, False, False))
1000 /* forbid shaded applications to move their windows */
1001 cre.value_mask &= ~(CWX | CWY);
1002 /* resend the old geometry */
1003 *ret_do_send_event = 1;
1005 if (IS_MAXIMIZED(fw))
1007 /* dont allow clients to resize maximized windows */
1008 cre.value_mask &= ~(CWWidth | CWHeight);
1009 /* resend the old geometry */
1010 *ret_do_send_event = 1;
1011 d_g.width = 0;
1012 d_g.height = 0;
1014 else if (!is_function_allowed(F_RESIZE, NULL, fw, False, False))
1016 cre.value_mask &= ~(CWWidth | CWHeight);
1017 *ret_do_send_event = 1;
1020 if (cre.value_mask & CWBorderWidth)
1022 /* for restoring */
1023 fw->attr_backup.border_width = cre.border_width;
1025 if (!force && CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO)
1027 __cr_detect_icccm_move(fw, &cre, &b);
1029 if (!(cre.value_mask & (CWX | CWY)))
1031 /* nothing */
1033 else if ((force ||
1034 CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_USE_GRAV) &&
1035 fw->hints.win_gravity != StaticGravity)
1037 int ref_x;
1038 int ref_y;
1039 int grav_x;
1040 int grav_y;
1042 gravity_get_offsets(fw->hints.win_gravity, &grav_x, &grav_y);
1043 if (cre.value_mask & CWX)
1045 ref_x = cre.x -
1046 ((grav_x + 1) * b.total_size.width) / 2;
1047 d_g.x = ref_x - fw->g.frame.x;
1049 if (cre.value_mask & CWY)
1051 ref_y = cre.y -
1052 ((grav_y + 1) * b.total_size.height) / 2;
1053 d_g.y = ref_y - fw->g.frame.y;
1056 else /* ..._USE_GRAV or ..._AUTO */
1058 /* default: traditional cr handling */
1059 if (cre.value_mask & CWX)
1061 d_g.x = cre.x - fw->g.frame.x - b.top_left.width;
1063 if (cre.value_mask & CWY)
1065 d_g.y = cre.y - fw->g.frame.y - b.top_left.height;
1068 if (cre.value_mask & CWHeight)
1070 if (cre.height <
1071 (WINDOW_FREAKED_OUT_SIZE - b.total_size.height))
1073 d_g.height = cre.height -
1074 (fw->g.frame.height - b.total_size.height);
1076 else
1078 /* Ignore height changes to astronomically large
1079 * windows (needed for XEmacs 20.4); don't care if the
1080 * window is shaded here - we won't use 'height' in
1081 * this case anyway.
1082 * Inform the buggy app about the size that *we* want
1084 d_g.height = 0;
1085 *ret_do_send_event = 1;
1088 if (cre.value_mask & CWWidth)
1090 if (cre.width < (WINDOW_FREAKED_OUT_SIZE - b.total_size.width))
1092 d_g.width = cre.width -
1093 (fw->g.frame.width - b.total_size.width);
1095 else
1097 d_g.width = 0;
1098 *ret_do_send_event = 1;
1102 /* SetupWindow (x,y) are the location of the upper-left outer corner
1103 * and are passed directly to XMoveResizeWindow (frame). The
1104 * (width,height) are the inner size of the frame. The inner width is
1105 * the same as the requested client window width; the inner height is
1106 * the same as the requested client window height plus any title bar
1107 * slop. */
1108 new_g = fw->g.frame;
1109 if (IS_SHADED(fw))
1111 new_g.width = fw->g.normal.width;
1112 new_g.height = fw->g.normal.height;
1114 oldnew_dim.width = new_g.width + d_g.width;
1115 oldnew_dim.height = new_g.height + d_g.height;
1116 constr_dim.width = oldnew_dim.width;
1117 constr_dim.height = oldnew_dim.height;
1118 constrain_size(
1119 fw, NULL, &constr_dim.width, &constr_dim.height, 0, 0,
1120 CS_UPDATE_MAX_DEFECT);
1121 d_g.width += (constr_dim.width - oldnew_dim.width);
1122 d_g.height += (constr_dim.height - oldnew_dim.height);
1123 if ((cre.value_mask & CWX) && d_g.width)
1125 new_g.x = fw->g.frame.x + d_g.x;
1126 new_g.width = fw->g.frame.width + d_g.width;
1128 else if ((cre.value_mask & CWX) && !d_g.width)
1130 new_g.x = fw->g.frame.x + d_g.x;
1132 else if (!(cre.value_mask & CWX) && d_g.width)
1134 gravity_resize(fw->hints.win_gravity, &new_g, d_g.width, 0);
1136 if ((cre.value_mask & CWY) && d_g.height)
1138 new_g.y = fw->g.frame.y + d_g.y;
1139 new_g.height = fw->g.frame.height + d_g.height;
1141 else if ((cre.value_mask & CWY) && !d_g.height)
1143 new_g.y = fw->g.frame.y + d_g.y;
1145 else if (!(cre.value_mask & CWY) && d_g.height)
1147 gravity_resize(fw->hints.win_gravity, &new_g, 0, d_g.height);
1150 if (new_g.x == fw->g.frame.x && new_g.y == fw->g.frame.y &&
1151 new_g.width == fw->g.frame.width &&
1152 new_g.height == fw->g.frame.height)
1154 /* Window will not be moved or resized; send a synthetic
1155 * ConfigureNotify. */
1156 *ret_do_send_event = 1;
1158 else if ((cre.value_mask & CWX) || (cre.value_mask & CWY) ||
1159 d_g.width || d_g.height)
1161 if (IS_SHADED(fw))
1163 get_shaded_geometry(fw, &new_g, &new_g);
1165 frame_setup_window_app_request(
1166 fw, new_g.x, new_g.y, new_g.width, new_g.height,
1167 False);
1168 /* make sure the window structure has the new position */
1169 update_absolute_geometry(fw);
1170 maximize_adjust_offset(fw);
1171 GNOME_SetWinArea(fw);
1173 else if (DO_FORCE_NEXT_CR(fw))
1175 *ret_do_send_event = 1;
1177 SET_FORCE_NEXT_CR(fw, 0);
1178 SET_FORCE_NEXT_PN(fw, 0);
1180 return cn_count;
1183 void __handle_configure_request(
1184 XConfigureRequestEvent cre, const evh_args_t *ea, FvwmWindow *fw,
1185 Bool force)
1187 int do_send_event = 0;
1188 int cn_count = 0;
1190 /* According to the July 27, 1988 ICCCM draft, we should ignore size
1191 * and position fields in the WM_NORMAL_HINTS property when we map a
1192 * window. Instead, we'll read the current geometry. Therefore, we
1193 * should respond to configuration requests for windows which have
1194 * never been mapped. */
1195 if (fw == NULL)
1197 __handle_cr_on_unmanaged(&cre);
1198 return;
1200 if (cre.window == FW_W_ICON_TITLE(fw) ||
1201 cre.window == FW_W_ICON_PIXMAP(fw))
1203 __handle_cr_on_icon(&cre, fw);
1205 if (FShapesSupported)
1207 __handle_cr_on_shaped(fw);
1209 if (fw != NULL && cre.window == FW_W(fw))
1211 cn_count = __handle_cr_on_client(
1212 &do_send_event, cre, ea, fw, force);
1214 /* Stacking order change requested. Handle this *after* geometry
1215 * changes, since we need the new geometry in occlusion calculations */
1216 if ((cre.value_mask & CWStackMode) &&
1217 (!DO_IGNORE_RESTACK(fw) || force))
1219 __handle_cr_restack(&do_send_event, &cre, fw);
1221 #if 1
1222 /* This causes some ddd windows not to be drawn properly. Reverted back
1223 * to the old method in frame_setup_window. */
1224 /* domivogt (15-Oct-1999): enabled this to work around buggy apps that
1225 * ask for a nonsense height and expect that they really get it. */
1226 if (cn_count == 0 && do_send_event)
1228 cn_count = 1;
1230 else if (cn_count > 0)
1232 do_send_event = 1;
1234 for ( ; cn_count > 0; cn_count--)
1236 SendConfigureNotify(
1237 fw, fw->g.frame.x, fw->g.frame.y, fw->g.frame.width,
1238 fw->g.frame.height, 0, True);
1240 if (do_send_event)
1242 XFlush(dpy);
1244 #endif
1246 return;
1249 static Bool __predicate_button_click(
1250 Display *display, XEvent *event, XPointer arg)
1252 if (event->type == ButtonPress || event->type == ButtonRelease)
1254 return True;
1257 return False;
1260 /* Helper function for __handle_focus_raise_click(). */
1261 static Bool __test_for_motion(int x0, int y0)
1263 int x;
1264 int y;
1265 unsigned int mask;
1266 XEvent e;
1268 /* Query the pointer to do this. We can't check for events here since
1269 * the events are still needed if the pointer moves. */
1271 /* However, some special mouse (e.g., a touchpad with the
1272 * synaptic driver) may handle a double click in a special way
1273 * (for dragging through short touching and holding down the
1274 * finger on the touchpad). Bascially, when you execute a
1275 * double click the first button release is queued after the
1276 * second _physical_ mouse release happen. It seems that
1277 * FQueryPointer may not work as expected: it does not see
1278 * that the button is released on a double click. So, we need
1279 * to check for a button press in the future to avoid a fvwm
1280 * lockup! (olicha 2004-01-31) */
1282 for (x = x0, y = y0; FQueryPointer(
1283 dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
1284 &x, &y, &mask) == True; usleep(20000))
1286 if ((mask & DEFAULT_ALL_BUTTONS_MASK) == 0)
1288 /* all buttons are released */
1289 return False;
1291 else if (abs(x - x0) >= Scr.MoveThreshold ||
1292 abs(y - y0) >= Scr.MoveThreshold)
1294 /* the pointer has moved */
1295 return True;
1297 if (FCheckPeekIfEvent(dpy, &e, __predicate_button_click, NULL))
1299 /* click in the future */
1300 return False;
1302 else
1304 /* The predicate procedure finds no match, no event
1305 * has been removed from the queue and XFlush was
1306 * called. Nothing to do */
1310 /* pointer has moved off screen */
1311 return True;
1314 /* Helper function for __handle_focus_raise_click(). */
1315 static void __check_click_to_focus_or_raise(
1316 hfrc_ret_t *ret_args, const exec_context_t *exc)
1318 FvwmWindow * const fw = exc->w.fw;
1319 const XEvent *te = exc->x.etrigger;
1320 struct
1322 unsigned is_client_click : 1;
1323 unsigned is_focused : 1;
1324 } f;
1326 f.is_focused = !!focus_is_focused(fw);
1327 f.is_client_click = (exc->w.wcontext == C_WINDOW ||
1328 exc->w.wcontext == C_EWMH_DESKTOP);
1329 /* check if we need to raise and/or focus the window */
1330 ret_args->do_focus = focus_query_click_to_focus(fw, exc->w.wcontext);
1331 if (f.is_client_click && !ret_args->do_focus &&
1332 !f.is_focused && FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
1333 !fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
1335 /* Give the window a chance to to take focus itself */
1336 ret_args->do_focus = 1;
1338 if (ret_args->do_focus && focus_is_focused(fw))
1340 ret_args->do_focus = 0;
1342 ret_args->do_raise =
1343 focus_query_click_to_raise(fw, f.is_focused, exc->w.wcontext);
1344 #define EXPERIMENTAL_ROU_HANDLING_V2
1345 #ifdef EXPERIMENTAL_ROU_HANDLING_V2
1346 /* RBW -- Dang! This works without the one in HandleEnterNotify! */
1347 if (ret_args->do_raise && is_on_top_of_layer_and_above_unmanaged(fw))
1348 #else
1349 if (ret_args->do_raise && is_on_top_of_layer(fw))
1350 #endif
1352 ret_args->do_raise = 0;
1354 if ((ret_args->do_focus &&
1355 FP_DO_IGNORE_FOCUS_CLICK_MOTION(FW_FOCUS_POLICY(fw))) ||
1356 (ret_args->do_raise &&
1357 FP_DO_IGNORE_RAISE_CLICK_MOTION(FW_FOCUS_POLICY(fw))))
1359 /* Pass further events to the application and check if a button
1360 * release or motion event occurs next. If we don't do this
1361 * here, the pointer will seem to be frozen in
1362 * __test_for_motion(). */
1363 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1364 if (__test_for_motion(te->xbutton.x_root, te->xbutton.y_root))
1366 /* the pointer was moved, process event normally */
1367 ret_args->do_focus = 0;
1368 ret_args->do_raise = 0;
1371 if (ret_args->do_focus || ret_args->do_raise)
1373 if (!((ret_args->do_focus &&
1374 FP_DO_ALLOW_FUNC_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1375 (ret_args->do_raise &&
1376 FP_DO_ALLOW_FUNC_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1378 ret_args->do_forbid_function = 1;
1380 if (!((ret_args->do_focus &&
1381 FP_DO_PASS_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1382 (ret_args->do_raise &&
1383 FP_DO_PASS_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1385 ret_args->do_swallow_click = 1;
1389 return;
1392 /* Finds out if the click on a window must be used to focus or raise it. */
1393 static void __handle_focus_raise_click(
1394 hfrc_ret_t *ret_args, const exec_context_t *exc)
1396 memset(ret_args, 0, sizeof(*ret_args));
1397 if (exc->w.fw == NULL)
1399 return;
1401 /* check for proper click button and modifiers*/
1402 if (FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) != 0 &&
1403 !(FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) &
1404 (1 << (exc->x.etrigger->xbutton.button - 1))))
1406 /* wrong button, handle click normally */
1407 return;
1409 else if (FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw)) !=
1410 FPOL_ANY_MODIFIER &&
1411 MaskUsedModifiers(
1412 FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw))) !=
1413 MaskUsedModifiers(exc->x.etrigger->xbutton.state))
1415 /* right button but wrong modifiers, handle click normally */
1416 return;
1418 else
1420 __check_click_to_focus_or_raise(ret_args, exc);
1423 return;
1426 /* Helper function for HandleButtonPress */
1427 static Bool __is_bpress_window_handled(const exec_context_t *exc)
1429 Window eventw;
1430 const XEvent *te = exc->x.etrigger;
1432 if (exc->w.fw == NULL)
1434 if ((te->xbutton.window != Scr.Root ||
1435 te->xbutton.subwindow != None) &&
1436 !is_pan_frame(te->xbutton.window))
1438 /* Ignore events in unmanaged windows or subwindows of
1439 * a client */
1440 return False;
1442 else
1444 return True;
1447 eventw = (te->xbutton.subwindow != None &&
1448 te->xany.window != FW_W(exc->w.fw)) ?
1449 te->xbutton.subwindow : te->xany.window;
1450 if (is_frame_hide_window(eventw) || eventw == FW_W_FRAME(exc->w.fw))
1452 return False;
1454 if (!XGetGeometry(
1455 dpy, eventw, &JunkRoot, &JunkX, &JunkY,
1456 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
1457 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1459 /* The window has already died. */
1460 return False;
1463 return True;
1466 /* Helper function for __handle_bpress_on_managed */
1467 static Bool __handle_click_to_focus(const exec_context_t *exc)
1469 fpol_set_focus_by_t set_by;
1471 switch (exc->w.wcontext)
1473 case C_WINDOW:
1474 case C_EWMH_DESKTOP:
1475 set_by = FOCUS_SET_BY_CLICK_CLIENT;
1476 break;
1477 case C_ICON:
1478 set_by = FOCUS_SET_BY_CLICK_ICON;
1479 break;
1480 default:
1481 set_by = FOCUS_SET_BY_CLICK_DECOR;
1482 break;
1484 SetFocusWindow(exc->w.fw, True, set_by);
1485 focus_grab_buttons(exc->w.fw);
1486 if (focus_is_focused(exc->w.fw) && !IS_ICONIFIED(exc->w.fw))
1488 border_draw_decorations(
1489 exc->w.fw, PART_ALL, True, True, CLEAR_ALL, NULL,
1490 NULL);
1493 return focus_is_focused(exc->w.fw);
1496 /* Helper function for __handle_bpress_on_managed */
1497 static Bool __handle_click_to_raise(const exec_context_t *exc)
1499 Bool rc = False;
1500 int is_focused;
1502 is_focused = focus_is_focused(exc->w.fw);
1503 if (focus_query_click_to_raise(exc->w.fw, is_focused, True))
1505 rc = True;
1508 return rc;
1511 /* Helper function for HandleButtonPress */
1512 static void __handle_bpress_stroke(void)
1514 STROKE_CODE(stroke_init());
1515 STROKE_CODE(send_motion = True);
1517 return;
1520 /* Helper function for __handle_bpress_on_managed */
1521 static Bool __handle_bpress_action(
1522 const exec_context_t *exc, char *action)
1524 window_parts part;
1525 Bool do_force;
1526 Bool rc = False;
1528 if (!action || *action == 0)
1530 PressedW = None;
1531 return False;
1533 /* draw pressed in decorations */
1534 part = border_context_to_parts(exc->w.wcontext);
1535 do_force = (part & PART_TITLEBAR) ? True : False;
1536 border_draw_decorations(
1537 exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1538 CLEAR_ALL, NULL, NULL);
1539 /* execute the action */
1540 if (IS_ICONIFIED(exc->w.fw))
1542 /* release the pointer since it can't do harm over an icon */
1543 XAllowEvents(dpy, AsyncPointer, CurrentTime);
1545 execute_function(NULL, exc, action, 0);
1546 if (exc->w.wcontext != C_WINDOW && exc->w.wcontext != C_NO_CONTEXT)
1548 WaitForButtonsUp(True);
1549 rc = True;
1551 /* redraw decorations */
1552 PressedW = None;
1553 if (check_if_fvwm_window_exists(exc->w.fw))
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);
1562 return rc;
1565 /* Handles button presses on the root window. */
1566 static void __handle_bpress_on_root(const exec_context_t *exc)
1568 char *action;
1570 PressedW = None;
1571 __handle_bpress_stroke();
1572 /* search for an appropriate mouse binding */
1573 action = CheckBinding(
1574 Scr.AllBindings, STROKE_ARG(0) exc->x.etrigger->xbutton.button,
1575 exc->x.etrigger->xbutton.state, GetUnusedModifiers(), C_ROOT,
1576 BIND_BUTTONPRESS, NULL, NULL);
1577 if (action && *action)
1579 const exec_context_t *exc2;
1580 exec_context_changes_t ecc;
1582 ecc.w.wcontext = C_ROOT;
1583 exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
1584 execute_function(NULL, exc2, action, 0);
1585 exc_destroy_context(exc2);
1586 WaitForButtonsUp(True);
1588 else
1590 /* do gnome buttonpress forwarding if win == root */
1591 GNOME_ProxyButtonEvent(exc->x.etrigger);
1594 return;
1597 /* Handles button presses on unmanaged windows */
1598 static void __handle_bpress_on_unmanaged(const exec_context_t *exc)
1600 /* Pass the event to the application. */
1601 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1602 XFlush(dpy);
1604 return;
1607 /* Handles button presses on managed windows */
1608 static void __handle_bpress_on_managed(const exec_context_t *exc)
1610 char *action;
1611 hfrc_ret_t f;
1612 FvwmWindow * const fw = exc->w.fw;
1613 XEvent *e;
1615 e = exc->x.etrigger;
1616 /* Now handle click to focus and click to raise. */
1617 __handle_focus_raise_click(&f, exc);
1618 PressedW = (f.do_forbid_function) ? None : exc->w.w;
1619 if (f.do_focus)
1621 if (!__handle_click_to_focus(exc))
1623 /* Window didn't accept the focus; pass the click to
1624 * the application. */
1625 f.do_swallow_click = 0;
1628 if (f.do_raise)
1630 if (__handle_click_to_raise(exc) == True)
1632 /* We can't raise the window immediately because the
1633 * action bound to the click might be "Lower" or
1634 * "RaiseLower". So mark the window as scheduled to be
1635 * raised after the binding is executed. Functions that
1636 * modify the stacking order will reset this flag. */
1637 SET_SCHEDULED_FOR_RAISE(fw, 1);
1640 /* handle bindings */
1641 if (!f.do_forbid_function)
1643 /* stroke bindings */
1644 __handle_bpress_stroke();
1645 /* mouse bindings */
1646 action = CheckBinding(
1647 Scr.AllBindings, STROKE_ARG(0) e->xbutton.button,
1648 e->xbutton.state, GetUnusedModifiers(),
1649 exc->w.wcontext, BIND_BUTTONPRESS, &fw->class,
1650 fw->name.name);
1651 if (__handle_bpress_action(exc, action))
1653 f.do_swallow_click = 1;
1656 /* raise the window */
1657 if (IS_SCHEDULED_FOR_RAISE(fw))
1659 /* Now that we know the action did not restack the window we
1660 * can raise it.
1661 * dv (10-Aug-2002): We can safely raise the window after
1662 * redrawing it since all the decorations are drawn in the
1663 * window background and no Expose event is generated. */
1664 RaiseWindow(fw, False);
1665 SET_SCHEDULED_FOR_RAISE(fw, 0);
1667 /* clean up */
1668 if (!f.do_swallow_click)
1670 /* pass the click to the application */
1671 XAllowEvents(dpy, ReplayPointer, CurrentTime);
1672 XFlush(dpy);
1674 else if (f.do_focus || f.do_raise)
1676 WaitForButtonsUp(True);
1679 return;
1682 /* restore focus stolen by unmanaged */
1683 static void __refocus_stolen_focus_win(const evh_args_t *ea)
1685 FOCUS_SET(Scr.StolenFocusWin);
1686 ea->exc->x.etrigger->xfocus.window = Scr.StolenFocusWin;
1687 ea->exc->x.etrigger->type = FocusIn;
1688 Scr.UnknownWinFocused = None;
1689 Scr.StolenFocusWin = None;
1690 dispatch_event(ea->exc->x.etrigger);
1692 return;
1695 /* ---------------------------- event handlers ----------------------------- */
1697 void HandleButtonPress(const evh_args_t *ea)
1699 DBUG("HandleButtonPress", "Routine Entered");
1701 GrabEm(CRS_NONE, GRAB_PASSIVE);
1702 if (__is_bpress_window_handled(ea->exc) == False)
1704 __handle_bpress_on_unmanaged(ea->exc);
1706 else if (ea->exc->w.fw != NULL)
1708 __handle_bpress_on_managed(ea->exc);
1710 else
1712 __handle_bpress_on_root(ea->exc);
1714 UngrabEm(GRAB_PASSIVE);
1716 return;
1719 #ifdef HAVE_STROKE
1720 void HandleButtonRelease(const evh_args_t *ea)
1722 char *action;
1723 char *name;
1724 int real_modifier;
1725 const XEvent *te = ea->exc->x.etrigger;
1726 XClassHint *class;
1728 DBUG("HandleButtonRelease", "Routine Entered");
1729 send_motion = False;
1730 stroke_trans (sequence);
1731 DBUG("HandleButtonRelease",sequence);
1732 /* Allows modifier to work (Only R context works here). */
1733 real_modifier = te->xbutton.state - (1 << (7 + te->xbutton.button));
1734 if (ea->exc->w.fw == NULL)
1736 class = NULL;
1737 name = NULL;
1739 else
1741 class = &ea->exc->w.fw->class;
1742 name = ea->exc->w.fw->name.name;
1744 /* need to search for an appropriate stroke binding */
1745 action = CheckBinding(
1746 Scr.AllBindings, sequence, te->xbutton.button, real_modifier,
1747 GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE,
1748 class, name);
1749 /* got a match, now process it */
1750 if (action != NULL && (action[0] != 0))
1752 execute_function(NULL, ea->exc, action, 0);
1753 WaitForButtonsUp(True);
1755 else
1758 * do gnome buttonpress forwarding if win == root
1760 if (Scr.Root == te->xany.window)
1762 GNOME_ProxyButtonEvent(te);
1766 return;
1768 #endif /* HAVE_STROKE */
1770 void HandleClientMessage(const evh_args_t *ea)
1772 const XEvent *te = ea->exc->x.etrigger;
1773 FvwmWindow * const fw = ea->exc->w.fw;
1775 DBUG("HandleClientMessage", "Routine Entered");
1777 /* Process GNOME and EWMH Messages */
1778 if (GNOME_ProcessClientMessage(ea->exc))
1780 return;
1782 else if (EWMH_ProcessClientMessage(ea->exc))
1784 return;
1787 /* handle deletion of tear out menus */
1788 if (fw && IS_TEAR_OFF_MENU(fw) && te->xclient.format == 32 &&
1789 te->xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
1791 menu_close_tear_off_menu(fw);
1792 return;
1795 if (te->xclient.message_type == _XA_WM_CHANGE_STATE &&
1796 fw && te->xclient.data.l[0] == IconicState && !IS_ICONIFIED(fw))
1798 const exec_context_t *exc;
1799 exec_context_changes_t ecc;
1801 ecc.w.wcontext = C_WINDOW;
1802 exc = exc_clone_context(ea->exc, &ecc, ECC_WCONTEXT);
1803 execute_function(NULL, exc, "Iconify", 0);
1804 exc_destroy_context(exc);
1805 return;
1808 /* FIXME: Is this safe enough ? I guess if clients behave
1809 * according to ICCCM and send these messages only if they
1810 * grabbed the pointer, it is OK */
1812 extern Atom _XA_WM_COLORMAP_NOTIFY;
1813 if (te->xclient.message_type == _XA_WM_COLORMAP_NOTIFY)
1815 set_client_controls_colormaps(te->xclient.data.l[1]);
1816 return;
1820 /* CKH - if we get here, it was an unknown client message, so send
1821 * it to the client if it was in a window we know about. I'm not so
1822 * sure this should be done or not, since every other window manager
1823 * I've looked at doesn't. But it might be handy for a free drag and
1824 * drop setup being developed for Linux. */
1825 if (fw)
1827 if (te->xclient.window != FW_W(fw))
1829 XEvent e;
1831 e = *te;
1832 e.xclient.window = FW_W(fw);
1833 FSendEvent(dpy, FW_W(fw), False, NoEventMask, &e);
1838 void HandleColormapNotify(const evh_args_t *ea)
1840 colormap_handle_colormap_notify(ea);
1842 return;
1845 void HandleConfigureRequest(const evh_args_t *ea)
1847 const XEvent *te = ea->exc->x.etrigger;
1848 XConfigureRequestEvent cre;
1849 FvwmWindow *fw = ea->exc->w.fw;
1851 DBUG("HandleConfigureRequest", "Routine Entered");
1853 cre = te->xconfigurerequest;
1854 /* te->xany.window is te->.xconfigurerequest.parent, so the context
1855 * window may be wrong. */
1856 if (XFindContext(dpy, cre.window, FvwmContext, (caddr_t *)&fw) ==
1857 XCNOENT)
1859 fw = NULL;
1862 __handle_configure_request(cre, ea, fw, False);
1865 void HandleDestroyNotify(const evh_args_t *ea)
1867 DBUG("HandleDestroyNotify", "Routine Entered");
1869 destroy_window(ea->exc->w.fw);
1870 EWMH_ManageKdeSysTray(
1871 ea->exc->x.etrigger->xdestroywindow.window,
1872 ea->exc->x.etrigger->type);
1873 EWMH_WindowDestroyed();
1874 GNOME_SetClientList();
1876 return;
1879 #define DEBUG_ENTERNOTIFY 0
1880 #if DEBUG_ENTERNOTIFY
1881 static int ecount=0;
1882 #define ENTER_DBG(x) fprintf x;
1883 #else
1884 #define ENTER_DBG(x)
1885 #endif
1886 void HandleEnterNotify(const evh_args_t *ea)
1888 const XEnterWindowEvent *ewp;
1889 XEvent d;
1890 FvwmWindow *sf;
1891 static Bool is_initial_ungrab_pending = True;
1892 Bool is_tear_off_menu;
1893 const XEvent *te = ea->exc->x.etrigger;
1894 FvwmWindow * const fw = ea->exc->w.fw;
1896 DBUG("HandleEnterNotify", "Routine Entered");
1897 ewp = &te->xcrossing;
1898 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)"));
1900 if (ewp->window == Scr.Root && ewp->subwindow == None &&
1901 ewp->detail == NotifyInferior && ewp->mode == NotifyNormal)
1903 BroadcastPacket(
1904 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1905 (long)NULL);
1907 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
1909 if (fw && !IS_ICONIFIED(fw) && ewp->window == FW_W(fw))
1911 InstallWindowColormaps(fw);
1913 else
1915 /* make sure its for one of our windows */
1916 /* handle a subwindow cmap */
1917 InstallWindowColormaps(NULL);
1920 else if (!fw)
1922 EnterSubWindowColormap(ewp->window);
1924 if (Scr.flags.is_wire_frame_displayed)
1926 ENTER_DBG((stderr, "en: exit: iwfd\n"));
1927 /* Ignore EnterNotify events while a window is resized or moved
1928 * as a wire frame; otherwise the window list may be screwed
1929 * up. */
1930 return;
1932 if (fw)
1934 if (ewp->window != FW_W_FRAME(fw) &&
1935 ewp->window != FW_W_PARENT(fw) &&
1936 ewp->window != FW_W(fw) &&
1937 ewp->window != FW_W_ICON_TITLE(fw) &&
1938 ewp->window != FW_W_ICON_PIXMAP(fw))
1940 /* Ignore EnterNotify that received by any of the sub
1941 * windows that don't handle this event. unclutter
1942 * triggers these events sometimes, re focusing an
1943 * unfocused window under the pointer */
1944 ENTER_DBG((stderr, "en: exit: funny window\n"));
1945 return;
1948 if (Scr.focus_in_pending_window != NULL)
1950 ENTER_DBG((stderr, "en: exit: fipw\n"));
1951 /* Ignore EnterNotify event while we are waiting for a window to
1952 * receive focus via Focus or FlipFocus commands. */
1953 focus_grab_buttons(fw);
1954 return;
1956 if (ewp->mode == NotifyGrab)
1958 ENTER_DBG((stderr, "en: exit: NotifyGrab\n"));
1959 return;
1961 else if (ewp->mode == NotifyNormal)
1963 ENTER_DBG((stderr, "en: NotifyNormal\n"));
1964 if (ewp->detail == NotifyNonlinearVirtual &&
1965 ewp->focus == False && ewp->subwindow != None)
1967 /* This takes care of some buggy apps that forget that
1968 * one of their dialog subwindows has the focus after
1969 * popping up a selection list several times (ddd,
1970 * netscape). I'm not convinced that this does not
1971 * break something else. */
1972 ENTER_DBG((stderr, "en: NN: refreshing focus\n"));
1973 refresh_focus(fw);
1976 else if (ewp->mode == NotifyUngrab)
1978 ENTER_DBG((stderr, "en: NotifyUngrab\n"));
1979 /* Ignore events generated by grabbing or ungrabbing the
1980 * pointer. However, there is no way to prevent the client
1981 * application from handling this event and, for example,
1982 * grabbing the focus. This will interfere with functions that
1983 * transferred the focus to a different window. */
1984 if (is_initial_ungrab_pending)
1986 ENTER_DBG((stderr, "en: NU: initial ungrab pending (lgw = NULL)\n"));
1987 is_initial_ungrab_pending = False;
1988 xcrossing_last_grab_window = NULL;
1990 else
1992 if (ewp->detail == NotifyNonlinearVirtual &&
1993 ewp->focus == False && ewp->subwindow != None)
1995 /* see comment above */
1996 ENTER_DBG((stderr, "en: NU: refreshing focus\n"));
1997 refresh_focus(fw);
1999 if (fw && fw == xcrossing_last_grab_window)
2001 ENTER_DBG((stderr, "en: exit: NU: is last grab window\n"));
2002 if (ewp->window == FW_W_FRAME(fw) ||
2003 ewp->window == FW_W_ICON_TITLE(fw) ||
2004 ewp->window == FW_W_ICON_PIXMAP(fw))
2006 ENTER_DBG((stderr, "en: exit: NU: last grab window = NULL\n"));
2007 xcrossing_last_grab_window = NULL;
2009 focus_grab_buttons(fw);
2011 return;
2013 else if (fw)
2015 if (ewp->window != FW_W_FRAME(fw) &&
2016 ewp->window != FW_W_ICON_TITLE(fw) &&
2017 ewp->window != FW_W_ICON_PIXMAP(fw))
2019 ENTER_DBG((stderr, "en: exit: NU: not frame window\n"));
2020 focus_grab_buttons(fw);
2021 return;
2026 if (fw)
2028 is_initial_ungrab_pending = False;
2031 /* look for a matching leaveNotify which would nullify this EnterNotify
2034 * RBW - if we're in startup, this is a coerced focus, so we don't
2035 * want to save the event time, or exit prematurely.
2037 * Ignore LeaveNotify events for tear out menus - handled by menu code
2039 is_tear_off_menu =
2040 (fw && IS_TEAR_OFF_MENU(fw) && ewp->window == FW_W(fw));
2041 if (!fFvwmInStartup && !is_tear_off_menu &&
2042 FCheckTypedWindowEvent(dpy, ewp->window, LeaveNotify, &d))
2044 if (d.xcrossing.mode == NotifyNormal &&
2045 d.xcrossing.detail != NotifyInferior)
2047 ENTER_DBG((stderr, "en: exit: found LeaveNotify\n"));
2048 return;
2052 if (ewp->window == Scr.Root)
2054 FvwmWindow *lf = get_last_screen_focus_window();
2056 if (!Scr.flags.is_pointer_on_this_screen)
2058 Scr.flags.is_pointer_on_this_screen = 1;
2059 if (lf && lf != &Scr.FvwmRoot &&
2060 !FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(lf)))
2062 SetFocusWindow(lf, True, FOCUS_SET_FORCE);
2064 else if (lf != &Scr.FvwmRoot)
2066 ForceDeleteFocus();
2068 else
2070 /* This was the first EnterNotify event for the
2071 * root window - ignore */
2073 set_last_screen_focus_window(NULL);
2075 else if (!(sf = get_focus_window()) ||
2076 FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2078 DeleteFocus(True);
2080 else if (
2081 Scr.UnknownWinFocused != None && sf != NULL &&
2082 FW_W(sf) == Scr.StolenFocusWin)
2084 __refocus_stolen_focus_win(ea);
2086 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2088 InstallWindowColormaps(NULL);
2090 focus_grab_buttons(lf);
2091 return;
2093 else
2095 Scr.flags.is_pointer_on_this_screen = 1;
2098 /* An EnterEvent in one of the PanFrameWindows activates the Paging or
2099 an EdgeCommand. */
2100 if (is_pan_frame(ewp->window))
2102 char *edge_command = NULL;
2104 if (
2105 Scr.UnknownWinFocused != None &&
2106 (sf = get_focus_window()) != NULL &&
2107 FW_W(sf) == Scr.StolenFocusWin)
2109 __refocus_stolen_focus_win(ea);
2111 /* check for edge commands */
2112 if (ewp->window == Scr.PanFrameTop.win)
2114 edge_command = Scr.PanFrameTop.command;
2116 else if (ewp->window == Scr.PanFrameBottom.win)
2118 edge_command = Scr.PanFrameBottom.command;
2120 else if (ewp->window == Scr.PanFrameLeft.win)
2122 edge_command = Scr.PanFrameLeft.command;
2124 else if (ewp->window == Scr.PanFrameRight.win)
2126 edge_command = Scr.PanFrameRight.command;
2128 if (edge_command && ewp->mode == NotifyUngrab &&
2129 ewp->detail == NotifyAncestor)
2131 /* nothing */
2133 else if (edge_command)
2135 execute_function(NULL, ea->exc, edge_command, 0);
2137 else
2139 /* no edge command for this pan frame - so we do
2140 * HandlePaging */
2141 int delta_x = 0;
2142 int delta_y = 0;
2143 XEvent e;
2145 /* this was in the HandleMotionNotify before, HEDU */
2146 Scr.flags.is_pointer_on_this_screen = 1;
2147 e = *te;
2148 HandlePaging(
2149 &e, Scr.EdgeScrollX, Scr.EdgeScrollY, &JunkX,
2150 &JunkY, &delta_x, &delta_y, True, True, False);
2151 return;
2154 if (!fw)
2156 return;
2158 if (IS_EWMH_DESKTOP(FW_W(fw)))
2160 BroadcastPacket(
2161 MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
2162 (long)NULL);
2163 return;
2165 if (ewp->window == FW_W_FRAME(fw) ||
2166 ewp->window == FW_W_ICON_TITLE(fw) ||
2167 ewp->window == FW_W_ICON_PIXMAP(fw))
2169 BroadcastPacket(
2170 MX_ENTER_WINDOW, 3, (long)FW_W(fw),
2171 (long)FW_W_FRAME(fw), (unsigned long)fw);
2173 sf = get_focus_window();
2174 if (sf && fw != sf && FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2176 ENTER_DBG((stderr, "en: delete focus\n"));
2177 DeleteFocus(True);
2179 focus_grab_buttons(fw);
2180 if (FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
2182 ENTER_DBG((stderr, "en: set mousey focus\n"));
2183 if (ewp->window == FW_W(fw))
2185 /* Event is for the client window...*/
2186 #ifndef EXPERIMENTAL_ROU_HANDLING_V2
2187 /* RBW -- This may still be needed at times, I'm not
2188 *sure yet. */
2189 SetFocusWindowClientEntered(
2190 fw, True, FOCUS_SET_BY_ENTER);
2191 #else
2192 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2193 #endif
2195 else
2197 /* Event is for the frame...*/
2198 SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2201 else if (focus_is_focused(fw) && focus_does_accept_input_focus(fw))
2203 /* We have to refresh the focus window here in case we left the
2204 * focused fvwm window. Motif apps may lose the input focus
2205 * otherwise. But do not try to refresh the focus of
2206 * applications that want to handle it themselves. */
2207 focus_force_refresh_focus(fw);
2209 else if (sf != fw)
2211 /* Give the window a chance to grab the buttons needed for
2212 * raise-on-click */
2213 focus_grab_buttons(sf);
2215 if (
2216 Scr.UnknownWinFocused != None && sf != NULL &&
2217 FW_W(sf) == Scr.StolenFocusWin)
2219 __refocus_stolen_focus_win(ea);
2221 /* We get an EnterNotify with mode == UnGrab when fvwm releases the
2222 * grab held during iconification. We have to ignore this, or icon
2223 * title will be initially raised. */
2224 if (IS_ICONIFIED(fw) && (ewp->mode == NotifyNormal) &&
2225 (ewp->window == FW_W_ICON_PIXMAP(fw) ||
2226 ewp->window == FW_W_ICON_TITLE(fw)) &&
2227 FW_W_ICON_PIXMAP(fw) != None)
2229 SET_ICON_ENTERED(fw, 1);
2230 DrawIconWindow(fw, True, False, False, False, NULL);
2232 /* Check for tear off menus */
2233 if (is_tear_off_menu)
2235 menu_enter_tear_off_menu(ea->exc);
2238 return;
2241 void HandleExpose(const evh_args_t *ea)
2243 XEvent e;
2244 FvwmWindow * const fw = ea->exc->w.fw;
2246 e = *ea->exc->x.etrigger;
2247 #if 0
2248 /* This doesn't work well. Sometimes, the expose count is zero although
2249 * dozens of expose events are pending. This happens all the time
2250 * during a shading animation. Simply flush expose events
2251 * unconditionally. */
2252 if (e.xexpose.count != 0)
2254 flush_accumulate_expose(e.xexpose.window, &e);
2256 #else
2257 flush_accumulate_expose(e.xexpose.window, &e);
2258 #endif
2259 if (fw == NULL)
2261 return;
2263 if (e.xany.window == FW_W_ICON_TITLE(fw) ||
2264 e.xany.window == FW_W_ICON_PIXMAP(fw))
2266 DrawIconWindow(fw, True, True, False, False, &e);
2267 return;
2269 else if (IS_TEAR_OFF_MENU(fw) && e.xany.window == FW_W(fw))
2271 /* refresh the contents of the torn out menu */
2272 menu_expose(&e, NULL);
2275 return;
2278 void HandleFocusIn(const evh_args_t *ea)
2280 XEvent d;
2281 Window w = None;
2282 Window focus_w = None;
2283 Window focus_fw = None;
2284 Pixel fc = 0;
2285 Pixel bc = 0;
2286 FvwmWindow *ffw_old = get_focus_window();
2287 FvwmWindow *sf;
2288 Bool do_force_broadcast = False;
2289 Bool is_unmanaged_focused = False;
2290 static Window last_focus_w = None;
2291 static Window last_focus_fw = None;
2292 static Bool was_nothing_ever_focused = True;
2293 FvwmWindow *fw = ea->exc->w.fw;
2295 DBUG("HandleFocusIn", "Routine Entered");
2297 Scr.focus_in_pending_window = NULL;
2298 /* This is a hack to make the PointerKey command work */
2299 if (ea->exc->x.etrigger->xfocus.detail != NotifyPointer)
2301 /**/
2302 w = ea->exc->x.etrigger->xany.window;
2304 while (FCheckTypedEvent(dpy, FocusIn, &d))
2306 /* dito */
2307 if (d.xfocus.detail != NotifyPointer)
2309 /**/
2310 w = d.xany.window;
2313 /* dito */
2314 if (w == None)
2316 return;
2318 /**/
2319 if (XFindContext(dpy, w, FvwmContext, (caddr_t *) &fw) == XCNOENT)
2321 fw = NULL;
2324 Scr.UnknownWinFocused = None;
2325 if (!fw)
2327 if (w != Scr.NoFocusWin)
2329 Scr.UnknownWinFocused = w;
2330 Scr.StolenFocusWin =
2331 (ffw_old != NULL) ? FW_W(ffw_old) : None;
2332 focus_w = w;
2333 is_unmanaged_focused = True;
2335 /* Only show a non-focused window as focused,
2336 * if the focus is on unmanaged and flickering qt dialogs
2337 * workaround is on. */
2338 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2339 !is_unmanaged_focused)
2341 border_draw_decorations(
2342 Scr.Hilite, PART_ALL, False, True, CLEAR_ALL,
2343 NULL, NULL);
2344 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2346 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2348 InstallWindowColormaps(Scr.Hilite);
2350 else
2352 InstallWindowColormaps(NULL);
2356 /* Not very useful if no window that fvwm and its modules know
2357 * about has the focus. */
2358 fc = GetColor(DEFAULT_FORE_COLOR);
2359 bc = GetColor(DEFAULT_BACK_COLOR);
2361 else if (fw != Scr.Hilite ||
2362 /* domivogt (16-May-2000): This check is necessary to force
2363 * sending a M_FOCUS_CHANGE packet after an unmanaged window
2364 * was focused. Otherwise fvwm would believe that Scr.Hilite
2365 * was still focused and not send any info to the modules. */
2366 last_focus_fw == None ||
2367 IS_FOCUS_CHANGE_BROADCAST_PENDING(fw))
2369 do_force_broadcast = IS_FOCUS_CHANGE_BROADCAST_PENDING(fw);
2370 SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 0);
2371 if (fw != Scr.Hilite)
2373 border_draw_decorations(
2374 fw, PART_ALL, True, True, CLEAR_ALL, NULL,
2375 NULL);
2377 focus_w = FW_W(fw);
2378 focus_fw = FW_W_FRAME(fw);
2379 fc = fw->hicolors.fore;
2380 bc = fw->hicolors.back;
2381 set_focus_window(fw);
2382 if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2384 if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2386 InstallWindowColormaps(Scr.Hilite);
2388 else
2390 InstallWindowColormaps(NULL);
2394 else
2396 return;
2398 if (was_nothing_ever_focused || last_focus_fw == None ||
2399 focus_w != last_focus_w || focus_fw != last_focus_fw ||
2400 do_force_broadcast)
2402 if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2403 !is_unmanaged_focused)
2405 BroadcastPacket(
2406 M_FOCUS_CHANGE, 5, (long)focus_w,
2407 (long)focus_fw,
2408 (unsigned long)IsLastFocusSetByMouse(),
2409 (long)fc, (long)bc);
2410 EWMH_SetActiveWindow(focus_w);
2412 last_focus_w = focus_w;
2413 last_focus_fw = focus_fw;
2414 was_nothing_ever_focused = False;
2416 if ((sf = get_focus_window()) != ffw_old)
2418 focus_grab_buttons(sf);
2419 focus_grab_buttons(ffw_old);
2422 return;
2425 void HandleFocusOut(const evh_args_t *ea)
2427 if (Scr.UnknownWinFocused != None && Scr.StolenFocusWin != None &&
2428 ea->exc->x.etrigger->xfocus.window == Scr.UnknownWinFocused)
2430 __refocus_stolen_focus_win(ea);
2433 return;
2436 void __handle_key(const evh_args_t *ea, Bool is_press)
2438 char *action;
2439 FvwmWindow *sf;
2440 KeyCode kc;
2441 int kcontext;
2442 const XEvent *te = ea->exc->x.etrigger;
2443 const FvwmWindow * const fw = ea->exc->w.fw;
2444 Bool is_second_binding;
2445 const XClassHint *winClass1, *winClass2;
2446 XClassHint tmp;
2447 char *name1, *name2;
2448 const exec_context_t *exc;
2449 exec_context_changes_t ecc;
2451 PressedW = None;
2453 /* Here's a real hack - some systems have two keys with the
2454 * same keysym and different keycodes. This converts all
2455 * the cases to one keycode. */
2456 kc = XKeysymToKeycode(dpy, XKeycodeToKeysym(dpy, te->xkey.keycode, 0));
2458 /* Check if there is something bound to the key */
2460 sf = get_focus_window();
2461 if (sf == NULL)
2463 tmp.res_name = tmp.res_class = name1 = "root";
2464 winClass1 = &tmp;
2465 kcontext = C_ROOT;
2467 else
2469 winClass1 = &sf->class;
2470 name1 = sf->name.name;
2471 kcontext = (sf == fw ? ea->exc->w.wcontext : C_WINDOW);
2474 if (fw == NULL)
2476 tmp.res_name = tmp.res_class = name2 = "root";
2477 winClass2 = &tmp;
2479 else
2481 winClass2 = &fw->class;
2482 name2 = fw->name.name;
2484 /* Searching the binding list with a different 'type' value
2485 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
2486 * The different context value does though. */
2487 action = CheckTwoBindings(
2488 &is_second_binding, Scr.AllBindings, STROKE_ARG(0) kc,
2489 te->xkey.state, GetUnusedModifiers(), kcontext, BIND_KEYPRESS,
2490 winClass1, name1, ea->exc->w.wcontext, BIND_PKEYPRESS,
2491 winClass2, name2);
2493 if (action != NULL)
2495 if (!is_press)
2497 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2498 return;
2500 exc = ea->exc;
2501 if (is_second_binding == False)
2503 ecc.w.fw = sf;
2504 ecc.w.wcontext = kcontext;
2505 exc = exc_clone_context(
2506 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
2508 execute_function(NULL, exc, action, 0);
2509 if (is_second_binding == False)
2511 exc_destroy_context(exc);
2513 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2514 return;
2517 /* if we get here, no function key was bound to the key. Send it
2518 * to the client if it was in a window we know about. */
2519 sf = get_focus_window();
2520 if (sf && te->xkey.window != FW_W(sf))
2522 XEvent e;
2524 e = *te;
2525 e.xkey.window = FW_W(sf);
2526 FSendEvent(
2527 dpy, e.xkey.window, False,
2528 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2530 else if (fw && te->xkey.window != FW_W(fw))
2532 XEvent e;
2534 e = *te;
2535 e.xkey.window = FW_W(fw);
2536 FSendEvent(
2537 dpy, e.xkey.window, False,
2538 (is_press)? KeyPressMask:KeyReleaseMask, &e);
2540 XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2542 return;
2545 void HandleKeyPress(const evh_args_t *ea)
2547 __handle_key(ea, True);
2550 void HandleKeyRelease(const evh_args_t *ea)
2552 __handle_key(ea, False);
2555 void HandleLeaveNotify(const evh_args_t *ea)
2557 const XEvent *te = ea->exc->x.etrigger;
2558 FvwmWindow * const fw = ea->exc->w.fw;
2560 DBUG("HandleLeaveNotify", "Routine Entered");
2562 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)"));
2563 /* Ignore LeaveNotify events while a window is resized or moved as a
2564 * wire frame; otherwise the window list may be screwed up. */
2565 if (Scr.flags.is_wire_frame_displayed)
2567 return;
2569 if (te->xcrossing.mode != NotifyNormal)
2571 /* Ignore events generated by grabbing or ungrabbing the
2572 * pointer. However, there is no way to prevent the client
2573 * application from handling this event and, for example,
2574 * grabbing the focus. This will interfere with functions that
2575 * transferred the focus to a different window. It is
2576 * necessary to check for LeaveNotify events on the client
2577 * window too in case buttons are not grabbed on it. */
2578 if (te->xcrossing.mode == NotifyGrab && fw &&
2579 (te->xcrossing.window == FW_W_FRAME(fw) ||
2580 te->xcrossing.window == FW_W(fw) ||
2581 te->xcrossing.window == FW_W_ICON_TITLE(fw) ||
2582 te->xcrossing.window == FW_W_ICON_PIXMAP(fw)))
2584 ENTER_DBG((stderr, "ln: *** lgw = 0x%08x\n", (int)fw));
2585 xcrossing_last_grab_window = fw;
2587 #ifdef FOCUS_EXPANDS_TITLE
2588 if (fw && IS_ICONIFIED(fw))
2590 SET_ICON_ENTERED(fw, 0);
2591 DrawIconWindow(
2592 fw, True, False, False, False, NULL);
2594 #endif
2595 return;
2597 /* CDE-like behaviour of raising the icon title if the icon
2598 gets the focus (in particular if the cursor is over the icon) */
2599 if (fw && IS_ICONIFIED(fw))
2601 SET_ICON_ENTERED(fw,0);
2602 DrawIconWindow(fw, True, False, False, False, NULL);
2605 /* An LeaveEvent in one of the PanFrameWindows activates
2606 an EdgeLeaveCommand. */
2607 if (is_pan_frame(te->xcrossing.window))
2609 char *edge_command_leave = NULL;
2611 /* check for edge commands */
2612 if (te->xcrossing.window == Scr.PanFrameTop.win)
2614 edge_command_leave = Scr.PanFrameTop.command_leave;
2616 else if (te->xcrossing.window == Scr.PanFrameBottom.win)
2618 edge_command_leave = Scr.PanFrameBottom.command_leave;
2620 else if (te->xcrossing.window == Scr.PanFrameLeft.win)
2622 edge_command_leave = Scr.PanFrameLeft.command_leave;
2624 else if (te->xcrossing.window == Scr.PanFrameRight.win)
2626 edge_command_leave = Scr.PanFrameRight.command_leave;
2628 if (edge_command_leave && te->xcrossing.mode == NotifyUngrab &&
2629 te->xcrossing.detail == NotifyAncestor)
2631 /* nothing */
2633 else if (edge_command_leave)
2635 execute_function(NULL, ea->exc, edge_command_leave, 0);
2640 /* If we leave the root window, then we're really moving
2641 * another screen on a multiple screen display, and we
2642 * need to de-focus and unhighlight to make sure that we
2643 * don't end up with more than one highlighted window at a time */
2644 if (te->xcrossing.window == Scr.Root &&
2645 /* domivogt (16-May-2000): added this test because somehow fvwm
2646 * sometimes gets a LeaveNotify on the root window although it is
2647 * single screen. */
2648 Scr.NumberOfScreens > 1)
2650 if (te->xcrossing.mode == NotifyNormal)
2652 if (te->xcrossing.detail != NotifyInferior)
2654 FvwmWindow *sf = get_focus_window();
2656 Scr.flags.is_pointer_on_this_screen = 0;
2657 set_last_screen_focus_window(sf);
2658 if (sf != NULL)
2660 DeleteFocus(True);
2662 if (Scr.Hilite != NULL)
2664 border_draw_decorations(
2665 Scr.Hilite, PART_ALL, False,
2666 True, CLEAR_ALL, NULL, NULL);
2671 else
2673 /* handle a subwindow cmap */
2674 LeaveSubWindowColormap(te->xany.window);
2676 if (fw != NULL &&
2677 (te->xcrossing.window == FW_W_FRAME(fw) ||
2678 te->xcrossing.window == FW_W_ICON_TITLE(fw) ||
2679 te->xcrossing.window == FW_W_ICON_PIXMAP(fw)))
2681 BroadcastPacket(
2682 MX_LEAVE_WINDOW, 3, (long)FW_W(fw),
2683 (long)FW_W_FRAME(fw), (unsigned long)fw);
2686 return;
2689 void HandleMapNotify(const evh_args_t *ea)
2691 Bool is_on_this_page = False;
2692 const XEvent *te = ea->exc->x.etrigger;
2693 FvwmWindow * const fw = ea->exc->w.fw;
2695 DBUG("HandleMapNotify", "Routine Entered");
2697 if (!fw)
2699 if (te->xmap.override_redirect == True &&
2700 te->xmap.window != Scr.NoFocusWin)
2702 XSelectInput(dpy, te->xmap.window, XEVMASK_ORW);
2703 XFlush(dpy);
2704 Scr.UnknownWinFocused = te->xmap.window;
2706 return;
2708 if (te->xmap.window == FW_W_FRAME(fw))
2710 /* Now that we know the frame is mapped after capturing the
2711 * window we do not need StructureNotifyMask events anymore. */
2712 XSelectInput(dpy, FW_W_FRAME(fw), XEVMASK_FRAMEW);
2713 XFlush(dpy);
2715 /* Except for identifying over-ride redirect window mappings, we
2716 * don't need or want windows associated with the
2717 * SubstructureNotifyMask */
2718 if (te->xmap.event != te->xmap.window)
2720 return;
2722 SET_MAP_PENDING(fw, 0);
2723 /* don't map if the event was caused by a de-iconify */
2724 if (IS_ICONIFY_PENDING(fw))
2726 return;
2729 /* Make sure at least part of window is on this page before giving it
2730 * focus... */
2731 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2734 * Need to do the grab to avoid race condition of having server send
2735 * MapNotify to client before the frame gets mapped; this is bad because
2736 * the client would think that the window has a chance of being viewable
2737 * when it really isn't.
2739 MyXGrabServer (dpy);
2740 if (FW_W_ICON_TITLE(fw))
2742 XUnmapWindow(dpy, FW_W_ICON_TITLE(fw));
2744 if (FW_W_ICON_PIXMAP(fw) != None)
2746 XUnmapWindow(dpy, FW_W_ICON_PIXMAP(fw));
2748 XMapSubwindows(dpy, FW_W_FRAME(fw));
2749 if (fw->Desk == Scr.CurrentDesk)
2751 XMapWindow(dpy, FW_W_FRAME(fw));
2753 if (IS_ICONIFIED(fw))
2755 BroadcastPacket(
2756 M_DEICONIFY, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2757 (unsigned long)fw);
2759 else
2761 BroadcastPacket(
2762 M_MAP, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2763 (unsigned long)fw);
2766 if (is_on_this_page &&
2767 focus_query_open_grab_focus(fw, get_focus_window()) == True)
2769 SetFocusWindow(fw, True, FOCUS_SET_FORCE);
2771 border_draw_decorations(
2772 fw, PART_ALL, (fw == get_focus_window()) ? True : False, True,
2773 CLEAR_ALL, NULL, NULL);
2774 MyXUngrabServer (dpy);
2775 SET_MAPPED(fw, 1);
2776 SET_ICONIFIED(fw, 0);
2777 SET_ICON_UNMAPPED(fw, 0);
2778 if (DO_ICONIFY_AFTER_MAP(fw))
2780 initial_window_options_t win_opts;
2782 /* finally, if iconification was requested before the window
2783 * was mapped, request it now. */
2784 memset(&win_opts, 0, sizeof(win_opts));
2785 Iconify(fw, &win_opts);
2786 SET_ICONIFY_AFTER_MAP(fw, 0);
2788 focus_grab_buttons_on_layer(fw->layer);
2790 return;
2793 void HandleMappingNotify(const evh_args_t *ea)
2795 XRefreshKeyboardMapping(&ea->exc->x.etrigger->xmapping);
2797 return;
2800 void HandleMapRequest(const evh_args_t *ea)
2802 DBUG("HandleMapRequest", "Routine Entered");
2804 if (fFvwmInStartup)
2806 /* Just map the damn thing, decorations are added later
2807 * in CaptureAllWindows. */
2808 XMapWindow(dpy, ea->exc->x.etrigger->xmaprequest.window);
2809 return;
2811 HandleMapRequestKeepRaised(ea, None, NULL, NULL);
2813 return;
2816 void HandleMapRequestKeepRaised(
2817 const evh_args_t *ea, Window KeepRaised, FvwmWindow *ReuseWin,
2818 initial_window_options_t *win_opts)
2820 Bool is_on_this_page = False;
2821 Bool is_new_window = False;
2822 FvwmWindow *tmp;
2823 FvwmWindow *sf;
2824 initial_window_options_t win_opts_bak;
2825 Window ew;
2826 FvwmWindow *fw;
2828 if (win_opts == NULL)
2830 memset(&win_opts_bak, 0, sizeof(win_opts_bak));
2831 win_opts = &win_opts_bak;
2833 ew = ea->exc->w.w;
2834 if (ReuseWin == NULL)
2836 Window pw;
2838 pw = ea->exc->x.etrigger->xmaprequest.parent;
2839 if (XFindContext(dpy, ew, FvwmContext, (caddr_t *)&fw) ==
2840 XCNOENT)
2842 fw = NULL;
2844 if (fw != NULL && IS_MAP_PENDING(fw))
2846 /* The window is already going to be mapped, no need to
2847 * do that twice */
2848 return;
2851 else
2853 fw = ReuseWin;
2856 if (fw == NULL && EWMH_IsKdeSysTrayWindow(ew))
2858 /* This means that the window is swallowed by kicker and that
2859 * kicker restart or exit. As we should assume that kicker
2860 * restart we should return here, if not we go into trouble
2861 * ... */
2862 return;
2864 if (!win_opts->flags.do_override_ppos)
2866 XFlush(dpy);
2869 /* If the window has never been mapped before ... */
2870 if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
2872 /* Add decorations. */
2873 fw = AddWindow(ea->exc, ReuseWin, win_opts);
2874 if (fw == AW_NO_WINDOW)
2876 return;
2878 else if (fw == AW_UNMANAGED)
2880 XMapWindow(dpy, ew);
2881 return;
2883 is_new_window = True;
2886 * Make sure at least part of window is on this page
2887 * before giving it focus...
2889 is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2890 if (KeepRaised != None)
2892 XRaiseWindow(dpy, KeepRaised);
2894 /* If it's not merely iconified, and we have hints, use them. */
2896 if (IS_ICONIFIED(fw))
2898 /* If no hints, or currently an icon, just "deiconify" */
2899 DeIconify(fw);
2901 else if (IS_MAPPED(fw))
2903 /* the window is already mapped - fake a MapNotify event */
2904 fake_map_unmap_notify(fw, MapNotify);
2906 else
2908 int state;
2910 if (fw->wmhints && (fw->wmhints->flags & StateHint))
2912 state = fw->wmhints->initial_state;
2914 else
2916 state = NormalState;
2918 if (win_opts->initial_state != DontCareState)
2920 state = win_opts->initial_state;
2923 switch (state)
2925 case DontCareState:
2926 case NormalState:
2927 case InactiveState:
2928 default:
2929 MyXGrabServer(dpy);
2930 if (fw->Desk == Scr.CurrentDesk)
2932 Bool do_grab_focus;
2934 SET_MAP_PENDING(fw, 1);
2935 XMapWindow(dpy, FW_W_FRAME(fw));
2936 XMapWindow(dpy, FW_W(fw));
2937 SetMapStateProp(fw, NormalState);
2938 if (Scr.flags.is_map_desk_in_progress)
2940 do_grab_focus = False;
2942 else if (!is_on_this_page)
2944 do_grab_focus = False;
2946 else if (focus_query_open_grab_focus(
2947 fw, get_focus_window()) ==
2948 True)
2950 do_grab_focus = True;
2952 else
2954 do_grab_focus = False;
2956 if (do_grab_focus)
2958 SetFocusWindow(
2959 fw, True, FOCUS_SET_FORCE);
2961 else
2963 /* make sure the old focused window
2964 * still has grabbed all necessary
2965 * buttons. */
2966 focus_grab_buttons(
2967 get_focus_window());
2970 else
2972 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
2973 /* nope, this is forbidden by the ICCCM2 */
2974 XMapWindow(dpy, FW_W(fw));
2975 SetMapStateProp(fw, NormalState);
2976 #else
2977 /* Since we will not get a MapNotify, set the
2978 * IS_MAPPED flag manually. */
2979 SET_MAPPED(fw, 1);
2980 SetMapStateProp(fw, IconicState);
2981 /* fake that the window was mapped to allow
2982 * modules to swallow it */
2983 BroadcastPacket(
2984 M_MAP, 3, (long)FW_W(fw),
2985 (long)FW_W_FRAME(fw),
2986 (unsigned long)fw);
2987 #endif
2989 MyXUngrabServer(dpy);
2990 break;
2992 case IconicState:
2993 if (is_new_window)
2995 /* the window will not be mapped - fake a
2996 * MapNotify and an UnmapNotify event. Can't
2997 * remember exactly why this is necessary, but
2998 * probably something w/ (de)iconify state
2999 * confusion. */
3000 fake_map_unmap_notify(fw, MapNotify);
3001 fake_map_unmap_notify(fw, UnmapNotify);
3003 if (win_opts->flags.is_iconified_by_parent ||
3004 ((tmp = get_transientfor_fvwmwindow(fw)) &&
3005 IS_ICONIFIED(tmp)))
3007 win_opts->flags.is_iconified_by_parent = 0;
3008 SET_ICONIFIED_BY_PARENT(fw, 1);
3010 if (USE_ICON_POSITION_HINT(fw) && fw->wmhints &&
3011 (fw->wmhints->flags & IconPositionHint))
3013 win_opts->default_icon_x = fw->wmhints->icon_x;
3014 win_opts->default_icon_y = fw->wmhints->icon_y;
3016 Iconify(fw, win_opts);
3017 break;
3020 if (IS_SHADED(fw))
3022 BroadcastPacket(
3023 M_WINDOWSHADE, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
3024 (unsigned long)fw);
3026 /* If the newly mapped window overlaps the focused window, make sure
3027 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3028 sf = get_focus_window();
3029 if (sf != NULL)
3031 focus_grab_buttons(sf);
3033 if (win_opts->flags.is_menu)
3035 SET_MAPPED(fw, 1);
3036 SET_MAP_PENDING(fw, 0);
3038 EWMH_SetClientList();
3039 EWMH_SetClientListStacking();
3040 GNOME_SetClientList();
3042 return;
3045 #ifdef HAVE_STROKE
3046 void HandleMotionNotify(const evh_args_t *ea)
3048 DBUG("HandleMotionNotify", "Routine Entered");
3050 if (send_motion == True)
3052 stroke_record(
3053 ea->exc->x.etrigger->xmotion.x,
3054 ea->exc->x.etrigger->xmotion.y);
3057 return;
3059 #endif /* HAVE_STROKE */
3061 void HandlePropertyNotify(const evh_args_t *ea)
3063 Bool OnThisPage = False;
3064 Bool has_icon_changed = False;
3065 Bool has_icon_pixmap_hint_changed = False;
3066 Bool has_icon_window_hint_changed = False;
3067 FlocaleNameString new_name = { NoName, NULL };
3068 int old_wmhints_flags;
3069 const XEvent *te = ea->exc->x.etrigger;
3070 char *urgency_action = NULL;
3071 FvwmWindow * const fw = ea->exc->w.fw;
3073 DBUG("HandlePropertyNotify", "Routine Entered");
3075 if (te->xproperty.window == Scr.Root &&
3076 te->xproperty.state == PropertyNewValue &&
3077 (te->xproperty.atom == _XA_XSETROOT_ID ||
3078 te->xproperty.atom == _XA_XROOTPMAP_ID))
3080 /* background change */
3081 /* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3082 * no property notify?). _XA_XROOTPMAP_ID is used by Esetroot
3083 * compatible program: the problem here is that with some
3084 * Esetroot compatible program we get the message _before_ the
3085 * background change. This is fixed with Esetroot 9.2 (not yet
3086 * released, 2002-01-14) */
3088 /* update icon window with some alpha and tear-off menu */
3089 FvwmWindow *t;
3091 for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3093 int cs;
3094 int t_cs = -1;
3095 int b_cs = t->icon_background_cs;
3096 Bool draw_picture = False;
3097 Bool draw_title = False;
3099 /* redraw ParentRelative tear-off menu */
3100 menu_redraw_transparent_tear_off_menu(t, True);
3102 if (!IS_ICONIFIED(t) || IS_ICON_SUPPRESSED(t))
3104 continue;
3106 if (Scr.Hilite == t)
3108 if (t->icon_title_cs_hi >= 0)
3110 t_cs = cs = t->icon_title_cs_hi;
3112 else
3114 cs = t->cs_hi;
3117 else
3119 if (t->icon_title_cs >= 0)
3121 t_cs = cs = t->icon_title_cs;
3123 else
3125 cs = t->cs;
3128 if (t->icon_alphaPixmap != None ||
3129 (cs >= 0 &&
3130 Colorset[cs].icon_alpha_percent < 100) ||
3131 CSET_IS_TRANSPARENT_PR(b_cs) ||
3132 (!IS_ICON_SHAPED(t) &&
3133 t->icon_background_padding > 0))
3135 draw_picture = True;
3137 if (CSET_IS_TRANSPARENT_PR(t_cs))
3139 draw_title = True;
3141 if (draw_title || draw_picture)
3143 DrawIconWindow(
3144 t, draw_title, draw_picture, False,
3145 draw_picture, NULL);
3148 if (te->xproperty.atom == _XA_XROOTPMAP_ID)
3150 update_root_transparent_colorset(te->xproperty.atom);
3152 BroadcastPropertyChange(
3153 MX_PROPERTY_CHANGE_BACKGROUND, 0, 0, "");
3154 return;
3157 if (!fw)
3159 return;
3161 if (XGetGeometry(
3162 dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3163 (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
3164 (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth) == 0)
3166 return;
3170 * Make sure at least part of window is on this page
3171 * before giving it focus...
3173 OnThisPage = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3175 switch (te->xproperty.atom)
3177 case XA_WM_TRANSIENT_FOR:
3178 flush_property_notify(XA_WM_TRANSIENT_FOR, FW_W(fw));
3179 if (setup_transientfor(fw) == True)
3181 RaiseWindow(fw, False);
3183 break;
3185 case XA_WM_NAME:
3186 flush_property_notify(XA_WM_NAME, FW_W(fw));
3187 if (HAS_EWMH_WM_NAME(fw))
3189 return;
3191 FlocaleGetNameProperty(XGetWMName, dpy, FW_W(fw), &new_name);
3192 if (new_name.name == NULL)
3194 FlocaleFreeNameProperty(&new_name);
3195 return;
3197 if (strlen(new_name.name) > MAX_WINDOW_NAME_LEN)
3199 /* limit to prevent hanging X server */
3200 (new_name.name)[MAX_WINDOW_NAME_LEN] = 0;
3202 if (fw->name.name && strcmp(new_name.name, fw->name.name) == 0)
3204 /* migo: some apps update their names every second */
3205 /* griph: make sure we don't free the property if it
3206 is THE same name */
3207 if (new_name.name != fw->name.name)
3209 FlocaleFreeNameProperty(&new_name);
3211 return;
3214 free_window_names(fw, True, False);
3215 fw->name = new_name;
3216 SET_NAME_CHANGED(fw, 1);
3217 if (fw->name.name == NULL)
3219 fw->name.name = NoName; /* must not happen */
3221 setup_visible_name(fw, False);
3222 BroadcastWindowIconNames(fw, True, False);
3224 /* fix the name in the title bar */
3225 if (!IS_ICONIFIED(fw))
3227 border_draw_decorations(
3228 fw, PART_TITLE, (Scr.Hilite == fw), True,
3229 CLEAR_ALL, NULL, NULL);
3231 EWMH_SetVisibleName(fw, False);
3233 * if the icon name is NoName, set the name of the icon to be
3234 * the same as the window
3236 if (!WAS_ICON_NAME_PROVIDED(fw))
3238 fw->icon_name = fw->name;
3239 setup_visible_name(fw, True);
3240 BroadcastWindowIconNames(fw, False, True);
3241 RedoIconName(fw);
3243 break;
3245 case XA_WM_ICON_NAME:
3246 flush_property_notify(XA_WM_ICON_NAME, FW_W(fw));
3247 if (HAS_EWMH_WM_ICON_NAME(fw))
3249 return;
3251 FlocaleGetNameProperty(
3252 XGetWMIconName, dpy, FW_W(fw), &new_name);
3253 if (new_name.name == NULL)
3255 FlocaleFreeNameProperty(&new_name);
3256 return;
3258 if (new_name.name && strlen(new_name.name) > MAX_ICON_NAME_LEN)
3260 /* limit to prevent hanging X server */
3261 (new_name.name)[MAX_ICON_NAME_LEN] = 0;
3263 if (fw->icon_name.name &&
3264 strcmp(new_name.name, fw->icon_name.name) == 0)
3266 /* migo: some apps update their names every second */
3267 /* griph: make sure we don't free the property if it
3268 is THE same name */
3269 if (new_name.name != fw->icon_name.name)
3271 FlocaleFreeNameProperty(&new_name);
3273 return;
3276 free_window_names(fw, False, True);
3277 fw->icon_name = new_name;
3278 SET_WAS_ICON_NAME_PROVIDED(fw, 1);
3279 if (fw->icon_name.name == NULL)
3281 /* currently never happens */
3282 fw->icon_name.name = fw->name.name;
3283 SET_WAS_ICON_NAME_PROVIDED(fw, 0);
3285 setup_visible_name(fw, True);
3286 BroadcastWindowIconNames(fw, False, True);
3287 RedoIconName(fw);
3288 EWMH_SetVisibleName(fw, True);
3289 break;
3291 case XA_WM_HINTS:
3292 flush_property_notify(XA_WM_HINTS, FW_W(fw));
3293 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3294 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3296 old_wmhints_flags = 0;
3297 if (fw->wmhints)
3299 old_wmhints_flags = fw->wmhints->flags;
3300 XFree ((char *) fw->wmhints);
3302 setup_wm_hints(fw);
3303 if (fw->wmhints == NULL)
3305 return;
3309 * rebuild icon if the client either provides an icon
3310 * pixmap or window or has reset the hints to `no icon'.
3312 if ((fw->wmhints->flags & IconPixmapHint) ||
3313 (old_wmhints_flags & IconPixmapHint))
3315 ICON_DBG((stderr, "hpn: iph changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconPixmapHint), fw->name));
3316 has_icon_pixmap_hint_changed = True;
3318 if ((fw->wmhints->flags & IconWindowHint) ||
3319 (old_wmhints_flags & IconWindowHint))
3321 ICON_DBG((stderr, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconWindowHint), fw->name));
3322 has_icon_window_hint_changed = True;
3323 SET_USE_EWMH_ICON(fw, False);
3325 increase_icon_hint_count(fw);
3326 if (has_icon_window_hint_changed ||
3327 has_icon_pixmap_hint_changed)
3329 if (ICON_OVERRIDE_MODE(fw) == ICON_OVERRIDE)
3331 ICON_DBG((stderr, "hpn: icon override '%s'\n", fw->name));
3332 has_icon_changed = False;
3334 else if (ICON_OVERRIDE_MODE(fw) ==
3335 NO_ACTIVE_ICON_OVERRIDE)
3337 if (has_icon_pixmap_hint_changed)
3339 if (WAS_ICON_HINT_PROVIDED(fw) ==
3340 ICON_HINT_MULTIPLE)
3342 ICON_DBG((stderr, "hpn: using further iph '%s'\n", fw->name));
3343 has_icon_changed = True;
3345 else if (fw->icon_bitmap_file ==
3346 NULL ||
3347 fw->icon_bitmap_file ==
3348 Scr.DefaultIcon)
3350 ICON_DBG((stderr, "hpn: using first iph '%s'\n", fw->name));
3351 has_icon_changed = True;
3353 else
3355 /* ignore the first icon pixmap
3356 * hint if the application did
3357 * not provide it from the
3358 * start */
3359 ICON_DBG((stderr, "hpn: first iph ignored '%s'\n", fw->name));
3360 has_icon_changed = False;
3363 else if (has_icon_window_hint_changed)
3365 ICON_DBG((stderr, "hpn: using iwh '%s'\n", fw->name));
3366 has_icon_changed = True;
3368 else
3370 ICON_DBG((stderr, "hpn: iwh not changed, hint ignored '%s'\n", fw->name));
3371 has_icon_changed = False;
3374 else /* NO_ICON_OVERRIDE */
3376 ICON_DBG((stderr, "hpn: using hint '%s'\n", fw->name));
3377 has_icon_changed = True;
3380 if (USE_EWMH_ICON(fw))
3382 has_icon_changed = False;
3385 if (has_icon_changed)
3387 ICON_DBG((stderr, "hpn: icon changed '%s'\n", fw->name));
3388 /* Okay, the icon hint has changed and style
3389 * options tell us to honour this change. Now
3390 * let's see if we have to use the application
3391 * provided pixmap or window (if any), the icon
3392 * file provided by the window's style or the
3393 * default style's icon. */
3394 if (fw->icon_bitmap_file == Scr.DefaultIcon)
3396 fw->icon_bitmap_file = NULL;
3398 if (!fw->icon_bitmap_file &&
3399 !(fw->wmhints->flags &
3400 (IconPixmapHint|IconWindowHint)))
3402 fw->icon_bitmap_file =
3403 (Scr.DefaultIcon) ?
3404 Scr.DefaultIcon : NULL;
3406 fw->iconPixmap = (Window)NULL;
3407 ChangeIconPixmap(fw);
3411 /* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3412 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3413 * Treat urgency changes by calling user-settable functions.
3414 * These could e.g. deiconify and raise the window or
3415 * temporarily change the decor. */
3416 if (!(old_wmhints_flags & XUrgencyHint) &&
3417 (fw->wmhints->flags & XUrgencyHint))
3419 urgency_action = "Function UrgencyFunc";
3421 if ((old_wmhints_flags & XUrgencyHint) &&
3422 !(fw->wmhints->flags & XUrgencyHint))
3424 urgency_action = "Function UrgencyDoneFunc";
3426 if (urgency_action)
3428 const exec_context_t *exc;
3429 exec_context_changes_t ecc;
3431 ecc.w.fw = fw;
3432 ecc.w.wcontext = C_WINDOW;
3433 exc = exc_clone_context(
3434 ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
3435 execute_function(NULL, exc, urgency_action, 0);
3436 exc_destroy_context(exc);
3438 break;
3439 case XA_WM_NORMAL_HINTS:
3440 /* just mark wm normal hints as changed and look them up when
3441 * the next ConfigureRequest w/ x, y, width or height set
3442 * arrives. */
3443 SET_HAS_NEW_WM_NORMAL_HINTS(fw, 1);
3444 break;
3445 default:
3446 if (te->xproperty.atom == _XA_WM_PROTOCOLS)
3448 FetchWmProtocols (fw);
3450 else if (te->xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
3452 FetchWmColormapWindows (fw); /* frees old data */
3453 ReInstallActiveColormap();
3455 else if (te->xproperty.atom == _XA_WM_STATE)
3457 if (fw && OnThisPage && focus_is_focused(fw) &&
3458 FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
3460 /* refresh the focus - why? */
3461 focus_force_refresh_focus(fw);
3464 else
3466 EWMH_ProcessPropertyNotify(ea->exc);
3468 break;
3472 void HandleReparentNotify(const evh_args_t *ea)
3474 const XEvent *te = ea->exc->x.etrigger;
3475 FvwmWindow * const fw = ea->exc->w.fw;
3477 if (!fw)
3479 return;
3481 if (te->xreparent.parent == Scr.Root)
3483 /* Ignore reparenting to the root window. In some cases these
3484 * events are selected although the window is no longer
3485 * managed. */
3486 return;
3488 if (te->xreparent.parent != FW_W_FRAME(fw))
3490 /* window was reparented by someone else, destroy the frame */
3491 SetMapStateProp(fw, WithdrawnState);
3492 EWMH_RestoreInitialStates(fw, te->type);
3493 if (!IS_TEAR_OFF_MENU(fw))
3495 XRemoveFromSaveSet(dpy, te->xreparent.window);
3496 XSelectInput(dpy, te->xreparent.window, NoEventMask);
3498 else
3500 XSelectInput(dpy, te->xreparent.window, XEVMASK_MENUW);
3502 discard_events(XEVMASK_FRAMEW);
3503 destroy_window(fw);
3504 EWMH_ManageKdeSysTray(te->xreparent.window, te->type);
3505 EWMH_WindowDestroyed();
3508 return;
3511 void HandleSelectionRequest(const evh_args_t *ea)
3513 icccm2_handle_selection_request(ea->exc->x.etrigger);
3515 return;
3518 void HandleSelectionClear(const evh_args_t *ea)
3520 icccm2_handle_selection_clear();
3522 return;
3525 void HandleShapeNotify(const evh_args_t *ea)
3527 FvwmWindow * const fw = ea->exc->w.fw;
3529 DBUG("HandleShapeNotify", "Routine Entered");
3531 if (FShapesSupported)
3533 const FShapeEvent *sev =
3534 (const FShapeEvent *)(ea->exc->x.etrigger);
3536 if (!fw)
3538 return;
3540 if (sev->kind != FShapeBounding)
3542 return;
3544 frame_setup_shape(
3545 fw, fw->g.frame.width, fw->g.frame.height, sev->shaped);
3546 GNOME_SetWinArea(fw);
3547 EWMH_SetFrameStrut(fw);
3548 if (!IS_ICONIFIED(fw))
3550 border_redraw_decorations(fw);
3554 return;
3557 void HandleUnmapNotify(const evh_args_t *ea)
3559 int dstx, dsty;
3560 Window dumwin;
3561 XEvent dummy;
3562 XEvent map_event;
3563 const XEvent *te = ea->exc->x.etrigger;
3564 int weMustUnmap;
3565 Bool focus_grabbed;
3566 Bool must_return = False;
3567 Bool do_map = False;
3568 FvwmWindow * const fw = ea->exc->w.fw;
3569 Window pw;
3570 Window cw;
3572 DBUG("HandleUnmapNotify", "Routine Entered");
3574 /* Don't ignore events as described below. */
3575 if (te->xunmap.event != te->xunmap.window &&
3576 (te->xunmap.event != Scr.Root || !te->xunmap.send_event))
3578 must_return = True;
3582 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3583 * to WithdrawnState should send a synthetic UnmapNotify with the
3584 * event field set to (pseudo-)root, in case the window is already
3585 * unmapped (which is the case for fvwm for IconicState).
3586 * Unfortunately, we looked for the FvwmContext using that field, so
3587 * try the window field also. */
3588 weMustUnmap = 0;
3589 if (!fw)
3591 weMustUnmap = 1;
3592 if (XFindContext(
3593 dpy, te->xunmap.window, FvwmContext,
3594 (caddr_t *)&fw) == XCNOENT)
3596 return;
3599 cw = FW_W(fw);
3600 pw = FW_W_PARENT(fw);
3601 if (te->xunmap.window == FW_W_FRAME(fw))
3603 SET_ICONIFY_PENDING(fw , 0);
3604 return;
3606 if (must_return)
3608 return;
3611 if (weMustUnmap)
3613 Bool is_map_request_pending;
3614 check_if_event_args args;
3616 args.w = te->xunmap.window;
3617 args.do_return_true = False;
3618 args.do_return_true_cr = False;
3619 /* Using FCheckTypedWindowEvent() does not work here. I don't
3620 * have the slightest idea why, but using FCheckIfEvent() with
3621 * the appropriate predicate procedure works fine. */
3622 FCheckIfEvent(dpy, &dummy, test_map_request, (XPointer)&args);
3623 /* Unfortunately, there is no procedure in X that simply tests
3624 * if an event of a certain type in on the queue without
3625 * waiting and without removing it from the queue.
3626 * XCheck...Event() does not wait but removes the event while
3627 * XPeek...() does not remove the event but waits. To solve
3628 * this, the predicate procedure sets a flag in the passed in
3629 * structure and returns False unconditionally. */
3630 is_map_request_pending = (args.ret_does_match == True);
3631 if (!is_map_request_pending)
3633 XUnmapWindow(dpy, te->xunmap.window);
3636 if (fw == Scr.Hilite)
3638 Scr.Hilite = NULL;
3640 focus_grabbed = focus_query_close_release_focus(fw);
3641 restore_focus_after_unmap(fw, False);
3642 if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
3644 return;
3648 * The program may have unmapped the client window, from either
3649 * NormalState or IconicState. Handle the transition to WithdrawnState.
3651 * We need to reparent the window back to the root (so that fvwm exiting
3652 * won't cause it to get mapped) and then throw away all state (pretend
3653 * that we've received a DestroyNotify).
3655 if (!FCheckTypedWindowEvent(
3656 dpy, te->xunmap.window, DestroyNotify, &dummy) &&
3657 XTranslateCoordinates(
3658 dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
3659 &dumwin))
3661 MyXGrabServer(dpy);
3662 SetMapStateProp(fw, WithdrawnState);
3663 EWMH_RestoreInitialStates(fw, te->type);
3664 if (FCheckTypedWindowEvent(
3665 dpy, te->xunmap.window, ReparentNotify, &dummy))
3667 if (fw->attr_backup.border_width)
3669 XSetWindowBorderWidth(
3670 dpy, te->xunmap.window,
3671 fw->attr_backup.border_width);
3673 if ((!IS_ICON_SUPPRESSED(fw))&&
3674 (fw->wmhints &&
3675 (fw->wmhints->flags & IconWindowHint)))
3677 XUnmapWindow(dpy, fw->wmhints->icon_window);
3680 else
3682 RestoreWithdrawnLocation(fw, False, Scr.Root);
3684 if (!IS_TEAR_OFF_MENU(fw))
3686 XRemoveFromSaveSet(dpy, te->xunmap.window);
3687 XSelectInput(dpy, te->xunmap.window, NoEventMask);
3689 XSync(dpy, 0);
3690 MyXUngrabServer(dpy);
3691 if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
3693 /* the client tried to map the window again while it
3694 * was still inside the decoration windows */
3695 do_map = True;
3698 destroy_window(fw);
3699 if (focus_grabbed == True)
3701 CoerceEnterNotifyOnCurrentWindow();
3703 EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
3704 EWMH_WindowDestroyed();
3705 GNOME_SetClientList();
3706 if (do_map == True)
3708 map_event.xmaprequest.window = cw;
3709 map_event.xmaprequest.parent = Scr.Root;
3710 dispatch_event(&map_event);
3711 /* note: we really should handle all map and unmap notify
3712 * events for that window in a loop here */
3715 return;
3718 void HandleVisibilityNotify(const evh_args_t *ea)
3720 FvwmWindow * const fw = ea->exc->w.fw;
3722 DBUG("HandleVisibilityNotify", "Routine Entered");
3724 if (fw && ea->exc->x.etrigger->xvisibility.window == FW_W_FRAME(fw))
3726 switch (ea->exc->x.etrigger->xvisibility.state)
3728 case VisibilityUnobscured:
3729 SET_FULLY_VISIBLE(fw, 1);
3730 SET_PARTIALLY_VISIBLE(fw, 1);
3731 break;
3732 case VisibilityPartiallyObscured:
3733 SET_FULLY_VISIBLE(fw, 0);
3734 SET_PARTIALLY_VISIBLE(fw, 1);
3735 break;
3736 default:
3737 SET_FULLY_VISIBLE(fw, 0);
3738 SET_PARTIALLY_VISIBLE(fw, 0);
3739 break;
3741 /* Make sure the button grabs are up to date */
3742 focus_grab_buttons(fw);
3745 return;
3748 /* ---------------------------- interface functions ------------------------ */
3750 /* Inform a client window of its geometry.
3752 * The input (frame) geometry will be translated to client geometry
3753 * before sending. */
3754 void SendConfigureNotify(
3755 FvwmWindow *fw, int x, int y, int w, int h, int bw,
3756 Bool send_for_frame_too)
3758 XEvent client_event;
3759 size_borders b;
3761 if (!fw || IS_SHADED(fw))
3763 return;
3765 client_event.type = ConfigureNotify;
3766 client_event.xconfigure.display = dpy;
3767 client_event.xconfigure.event = FW_W(fw);
3768 client_event.xconfigure.window = FW_W(fw);
3769 get_window_borders(fw, &b);
3770 client_event.xconfigure.x = x + b.top_left.width;
3771 client_event.xconfigure.y = y + b.top_left.height;
3772 client_event.xconfigure.width = w - b.total_size.width;
3773 client_event.xconfigure.height = h - b.total_size.height;
3774 client_event.xconfigure.border_width = bw;
3775 client_event.xconfigure.above = FW_W_FRAME(fw);
3776 client_event.xconfigure.override_redirect = False;
3777 #if 0
3778 fprintf(stderr,
3779 "send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x '%s'\n",
3780 client_event.xconfigure.x, client_event.xconfigure.y,
3781 client_event.xconfigure.width, client_event.xconfigure.height,
3782 (int)FW_W_FRAME(fw), (int)FW_W(fw),
3783 (int)client_event.xconfigure.window,
3784 (fw->name.name) ? fw->name.name : "");
3785 #endif
3786 FSendEvent(
3787 dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
3788 if (send_for_frame_too)
3790 /* This is for buggy tk, which waits for the real
3791 * ConfigureNotify on frame instead of the synthetic one on w.
3792 * The geometry data in the event will not be correct for the
3793 * frame, but tk doesn't look at that data anyway. */
3794 client_event.xconfigure.event = FW_W_FRAME(fw);
3795 client_event.xconfigure.window = FW_W_FRAME(fw);
3796 FSendEvent(
3797 dpy, FW_W_FRAME(fw), False, StructureNotifyMask,
3798 &client_event);
3801 return;
3805 ** Procedure:
3806 ** InitEventHandlerJumpTable
3808 void InitEventHandlerJumpTable(void)
3810 int i;
3812 for (i=0; i<LASTEvent; i++)
3814 EventHandlerJumpTable[i] = NULL;
3816 EventHandlerJumpTable[Expose] = HandleExpose;
3817 EventHandlerJumpTable[DestroyNotify] = HandleDestroyNotify;
3818 EventHandlerJumpTable[MapRequest] = HandleMapRequest;
3819 EventHandlerJumpTable[MapNotify] = HandleMapNotify;
3820 EventHandlerJumpTable[UnmapNotify] = HandleUnmapNotify;
3821 EventHandlerJumpTable[ButtonPress] = HandleButtonPress;
3822 EventHandlerJumpTable[EnterNotify] = HandleEnterNotify;
3823 EventHandlerJumpTable[LeaveNotify] = HandleLeaveNotify;
3824 EventHandlerJumpTable[FocusIn] = HandleFocusIn;
3825 EventHandlerJumpTable[FocusOut] = HandleFocusOut;
3826 EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
3827 EventHandlerJumpTable[ClientMessage] = HandleClientMessage;
3828 EventHandlerJumpTable[PropertyNotify] = HandlePropertyNotify;
3829 EventHandlerJumpTable[KeyPress] = HandleKeyPress;
3830 EventHandlerJumpTable[KeyRelease] = HandleKeyRelease;
3831 EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
3832 EventHandlerJumpTable[ColormapNotify] = HandleColormapNotify;
3833 if (FShapesSupported)
3835 EventHandlerJumpTable[FShapeEventBase+FShapeNotify] =
3836 HandleShapeNotify;
3838 EventHandlerJumpTable[SelectionClear] = HandleSelectionClear;
3839 EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
3840 EventHandlerJumpTable[ReparentNotify] = HandleReparentNotify;
3841 EventHandlerJumpTable[MappingNotify] = HandleMappingNotify;
3842 STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
3843 STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
3844 #ifdef MOUSE_DROPPINGS
3845 STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
3846 #else /* no MOUSE_DROPPINGS */
3847 STROKE_CODE(stroke_init());
3848 #endif /* MOUSE_DROPPINGS */
3850 return;
3853 /* handle a single X event */
3854 void dispatch_event(XEvent *e)
3856 Window w = e->xany.window;
3857 FvwmWindow *fw;
3859 DBUG("dispatch_event", "Routine Entered");
3861 XFlush(dpy);
3862 if (w == Scr.Root)
3864 switch (e->type)
3866 case ButtonPress:
3867 case ButtonRelease:
3868 if (e->xbutton.subwindow != None)
3870 w = e->xbutton.subwindow;
3872 case MapRequest:
3873 w = e->xmaprequest.window;
3874 break;
3875 default:
3876 break;
3879 if (w == Scr.Root ||
3880 XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
3882 fw = NULL;
3884 last_event_type = e->type;
3885 if (EventHandlerJumpTable[e->type])
3887 evh_args_t ea;
3888 exec_context_changes_t ecc;
3889 Window dummyw;
3891 ecc.type = EXCT_EVENT;
3892 ecc.x.etrigger = e;
3893 ecc.w.wcontext = GetContext(&fw, fw, e, &dummyw);
3894 ecc.w.w = w;
3895 ecc.w.fw = fw;
3896 ea.exc = exc_create_context(
3897 &ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W |
3898 ECC_WCONTEXT);
3899 (*EventHandlerJumpTable[e->type])(&ea);
3900 exc_destroy_context(ea.exc);
3903 #ifdef C_ALLOCA
3904 /* If we're using the C version of alloca, see if anything needs to be
3905 * freed up.
3907 alloca(0);
3908 #endif
3909 DBUG("dispatch_event", "Leaving Routine");
3911 return;
3914 /* ewmh configure request */
3915 void events_handle_configure_request(
3916 XConfigureRequestEvent cre, FvwmWindow *fw, Bool force)
3918 __handle_configure_request(cre, NULL, fw, force);
3921 void HandleEvents(void)
3923 XEvent ev;
3925 DBUG("HandleEvents", "Routine Entered");
3926 STROKE_CODE(send_motion = False);
3927 while (!isTerminated)
3929 last_event_type = 0;
3930 if (Scr.flags.is_window_scheduled_for_destroy)
3932 destroy_scheduled_windows();
3934 if (Scr.flags.do_need_window_update)
3936 flush_window_updates();
3938 if (My_XNextEvent(dpy, &ev))
3940 dispatch_event(&ev);
3942 if (Scr.flags.do_need_style_list_update)
3944 simplify_style_list();
3948 return;
3953 * Waits for next X or module event, fires off startup routines when startup
3954 * modules have finished or after a timeout if the user has specified a
3955 * command line module that doesn't quit or gets stuck.
3958 int My_XNextEvent(Display *dpy, XEvent *event)
3960 fd_set in_fdset, out_fdset;
3961 int num_fd;
3962 fmodule *module;
3963 fmodule_input *input;
3964 static struct timeval timeout;
3965 static struct timeval *timeoutP = &timeout;
3967 DBUG("My_XNextEvent", "Routine Entered");
3969 /* include this next bit if HandleModuleInput() gets called anywhere
3970 * else with queueing turned on. Because this routine is the only
3971 * place that queuing is on _and_ ExecuteCommandQueue is always called
3972 * immediately after it is impossible for there to be anything in the
3973 * queue at this point */
3974 #if 0
3975 /* execute any commands queued up */
3976 DBUG("My_XNextEvent", "executing module comand queue");
3977 ExecuteCommandQueue();
3978 #endif
3980 /* check for any X events already queued up.
3981 * Side effect: this does an XFlush if no events are queued
3982 * Make sure nothing between here and the select causes further
3983 * X requests to be sent or the select may block even though
3984 * there are events in the queue */
3985 if (FPending(dpy))
3987 DBUG(
3988 "My_XNextEvent", "taking care of queued up events"
3989 " & returning (1)");
3990 FNextEvent(dpy, event);
3991 return 1;
3994 /* The SIGCHLD signal is sent every time one of our child processes
3995 * dies, and the SIGCHLD handler now reaps them automatically. We
3996 * should therefore never see a zombie */
3997 #if 0
3998 DBUG("My_XNextEvent", "no X events waiting - about to reap children");
3999 /* Zap all those zombies! */
4000 /* If we get to here, then there are no X events waiting to be
4001 * processed. Just take a moment to check for dead children. */
4002 ReapChildren();
4003 #endif
4005 /* check for termination of all startup modules */
4006 if (fFvwmInStartup)
4008 module = module_get_next(NULL);
4009 for (; module != NULL; module = module_get_next(module))
4011 if (MOD_IS_CMDLINE(module) == 1)
4013 break;
4016 if (module == NULL)
4018 /* last module */
4019 DBUG(
4020 "My_XNextEvent",
4021 "Starting up after command lines modules");
4022 /* set an infinite timeout to stop ticking */
4023 timeoutP = NULL;
4024 /* This may cause X requests to be sent */
4025 StartupStuff();
4027 return 0; /* so return without select()ing */
4031 /* Some signals can interrupt us while we wait for any action
4032 * on our descriptors. While some of these signals may be asking
4033 * fvwm to die, some might be harmless. Harmless interruptions
4034 * mean we have to start waiting all over again ... */
4037 int ms;
4038 Bool is_waiting_for_scheduled_command = False;
4039 static struct timeval *old_timeoutP = NULL;
4041 /* The timeouts become undefined whenever the select returns,
4042 * and so we have to reinitialise them */
4043 ms = squeue_get_next_ms();
4044 if (ms == 0)
4046 /* run scheduled commands */
4047 squeue_execute();
4048 ms = squeue_get_next_ms();
4049 /* should not happen anyway.
4050 * get_next_schedule_queue_ms() can't return 0 after a
4051 * call to execute_schedule_queue(). */
4052 if (ms == 0)
4054 ms = 1;
4057 if (ms < 0)
4059 timeout.tv_sec = 42;
4060 timeout.tv_usec = 0;
4062 else
4064 /* scheduled commands are pending - don't wait too
4065 * long */
4066 timeout.tv_sec = ms / 1000;
4067 timeout.tv_usec = 1000 * (ms % 1000);
4068 old_timeoutP = timeoutP;
4069 timeoutP = &timeout;
4070 is_waiting_for_scheduled_command = True;
4073 FD_ZERO(&in_fdset);
4074 FD_ZERO(&out_fdset);
4075 FD_SET(x_fd, &in_fdset);
4077 /* nothing is done here if fvwm was compiled without session
4078 * support */
4079 if (sm_fd >= 0)
4081 FD_SET(sm_fd, &in_fdset);
4084 module = module_get_next(NULL);
4085 for (; module != NULL; module = module_get_next(module))
4087 FD_SET(MOD_READFD(module), &in_fdset);
4089 if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
4091 FD_SET(MOD_WRITEFD(module), &out_fdset);
4095 DBUG("My_XNextEvent", "waiting for module input/output");
4096 num_fd = fvwmSelect(
4097 fvwmlib_max_fd, &in_fdset, &out_fdset, 0, timeoutP);
4098 if (is_waiting_for_scheduled_command)
4100 timeoutP = old_timeoutP;
4103 /* Express route out of FVWM ... */
4104 if (isTerminated)
4106 return 0;
4108 } while (num_fd < 0);
4110 if (num_fd > 0)
4112 /* Check for module input. */
4113 module = module_get_next(NULL);
4114 for (; module != NULL; module = module_get_next(module))
4116 if (FD_ISSET(MOD_READFD(module), &in_fdset))
4118 input = module_receive(module);
4119 /* enqueue the received command */
4120 module_input_enqueue(input);
4122 if (
4123 MOD_WRITEFD(module) >= 0 &&
4124 FD_ISSET(MOD_WRITEFD(module), &out_fdset))
4126 DBUG("My_XNextEvent",
4127 "calling FlushMessageQueue");
4128 FlushMessageQueue(module);
4132 /* cleanup dead modules */
4133 module_cleanup();
4135 /* execute any commands queued up */
4136 DBUG("My_XNextEvent", "executing module comand queue");
4137 ExecuteCommandQueue();
4139 /* nothing is done here if fvwm was compiled without session
4140 * support */
4141 if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
4143 ProcessICEMsgs();
4147 else
4149 /* select has timed out, things must have calmed down so let's
4150 * decorate */
4151 if (fFvwmInStartup)
4153 fvwm_msg(ERR, "My_XNextEvent",
4154 "Some command line modules have not quit, "
4155 "Starting up after timeout.\n");
4156 StartupStuff();
4157 timeoutP = NULL; /* set an infinite timeout to stop
4158 * ticking */
4159 reset_style_changes();
4160 Scr.flags.do_need_window_update = 0;
4162 /* run scheduled commands if necessary */
4163 squeue_execute();
4166 /* check for X events again, rather than return 0 and get called again
4168 if (FPending(dpy))
4170 DBUG("My_XNextEvent",
4171 "taking care of queued up events & returning (2)");
4172 FNextEvent(dpy,event);
4173 return 1;
4176 DBUG("My_XNextEvent", "leaving My_XNextEvent");
4177 return 0;
4182 * Procedure:
4183 * Find the Fvwm context for the event.
4186 int GetContext(FvwmWindow **ret_fw, FvwmWindow *t, const XEvent *e, Window *w)
4188 int context;
4189 Window win;
4190 Window subw = None;
4191 int x = 0;
4192 int y = 0;
4193 Bool is_key_event = False;
4195 win = e->xany.window;
4196 context = C_NO_CONTEXT;
4197 switch (e->type)
4199 case KeyPress:
4200 case KeyRelease:
4201 x = e->xkey.x;
4202 y = e->xkey.y;
4203 subw = e->xkey.subwindow;
4204 if (win == Scr.Root && subw != None)
4206 /* Translate root coordinates into subwindow
4207 * coordinates. Necessary for key bindings that work
4208 * over unfocused windows. */
4209 win = subw;
4210 XTranslateCoordinates(
4211 dpy, Scr.Root, subw, x, y, &x, &y, &subw);
4212 XFindContext(dpy, win, FvwmContext, (caddr_t *) &t);
4214 is_key_event = True;
4215 /* fall through */
4216 case ButtonPress:
4217 case ButtonRelease:
4218 if (!is_key_event)
4220 x = e->xbutton.x;
4221 y = e->xbutton.y;
4222 subw = e->xbutton.subwindow;
4224 if (t && win == FW_W_FRAME(t) && subw != None)
4226 /* Translate frame coordinates into subwindow
4227 * coordinates. */
4228 win = subw;
4229 XTranslateCoordinates(
4230 dpy, FW_W_FRAME(t), subw, x, y, &x, &y, &subw);
4231 if (win == FW_W_PARENT(t))
4233 win = subw;
4234 XTranslateCoordinates(
4235 dpy, FW_W_PARENT(t), subw, x, y, &x,
4236 &y, &subw);
4239 break;
4240 default:
4241 XFindContext(dpy, win, FvwmContext, (caddr_t *)&t);
4242 break;
4244 if (ret_fw != NULL)
4246 *ret_fw = t;
4248 if (!t)
4250 return C_ROOT;
4252 *w = win;
4253 if (*w == Scr.NoFocusWin)
4255 return C_ROOT;
4257 if (subw != None)
4259 if (win == FW_W_PARENT(t))
4261 *w = subw;
4264 if (*w == Scr.Root)
4266 return C_ROOT;
4268 context = frame_window_id_to_context(t, *w, &Button);
4270 return context;
4275 * Removes expose events for a specific window from the queue
4278 int flush_expose(Window w)
4280 XEvent dummy;
4281 int i=0;
4283 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4285 i++;
4288 return i;
4291 /* same as above, but merges the expose rectangles into a single big one */
4292 int flush_accumulate_expose(Window w, XEvent *e)
4294 XEvent dummy;
4295 int i = 0;
4296 int x1 = e->xexpose.x;
4297 int y1 = e->xexpose.y;
4298 int x2 = x1 + e->xexpose.width;
4299 int y2 = y1 + e->xexpose.height;
4301 while (FCheckTypedWindowEvent(dpy, w, Expose, &dummy))
4303 x1 = min(x1, dummy.xexpose.x);
4304 y1 = min(y1, dummy.xexpose.y);
4305 x2 = max(x2, dummy.xexpose.x + dummy.xexpose.width);
4306 y2 = max(y2, dummy.xexpose.y + dummy.xexpose.height);
4307 i++;
4309 e->xexpose.x = x1;
4310 e->xexpose.y = y1;
4311 e->xexpose.width = x2 - x1;
4312 e->xexpose.height = y2 - y1;
4314 return i;
4319 * Removes all expose events from the queue and does the necessary redraws
4322 void handle_all_expose(void)
4324 void *saved_event;
4325 XEvent evdummy;
4327 saved_event = fev_save_event();
4328 FPending(dpy);
4329 while (FCheckMaskEvent(dpy, ExposureMask, &evdummy))
4331 dispatch_event(&evdummy);
4333 fev_restore_event(saved_event);
4335 return;
4338 /* CoerceEnterNotifyOnCurrentWindow()
4339 * Pretends to get a HandleEnterNotify on the window that the pointer
4340 * currently is in so that the focus gets set correctly from the beginning.
4341 * Note that this presently only works if the current window is not
4342 * click_to_focus; I think that that behaviour is correct and desirable.
4343 * --11/08/97 gjb */
4344 void CoerceEnterNotifyOnCurrentWindow(void)
4346 Window child;
4347 Window root;
4348 Bool f;
4349 evh_args_t ea;
4350 exec_context_changes_t ecc;
4351 XEvent e;
4352 FvwmWindow *fw;
4354 f = FQueryPointer(
4355 dpy, Scr.Root, &root, &child, &e.xcrossing.x_root,
4356 &e.xcrossing.y_root, &e.xcrossing.x, &e.xcrossing.y,
4357 &JunkMask);
4358 if (f == False || child == None)
4360 return;
4362 e.xcrossing.type = EnterNotify;
4363 e.xcrossing.window = child;
4364 e.xcrossing.subwindow = None;
4365 e.xcrossing.mode = NotifyNormal;
4366 e.xcrossing.detail = NotifyAncestor;
4367 e.xcrossing.same_screen = True;
4368 if (XFindContext(dpy, child, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4370 fw = NULL;
4372 else
4374 XTranslateCoordinates(
4375 dpy, Scr.Root, child, e.xcrossing.x_root,
4376 e.xcrossing.y_root, &JunkX, &JunkY, &child);
4377 if (child == FW_W_PARENT(fw))
4379 child = FW_W(fw);
4381 if (child != None)
4383 e.xany.window = child;
4386 e.xcrossing.focus = (fw == get_focus_window()) ? True : False;
4387 ecc.type = EXCT_NULL;
4388 ecc.x.etrigger = &e;
4389 ea.exc = exc_create_context(&ecc, ECC_TYPE | ECC_ETRIGGER);
4390 HandleEnterNotify(&ea);
4391 exc_destroy_context(ea.exc);
4393 return;
4396 /* This function discards all queued up ButtonPress, ButtonRelease and
4397 * ButtonMotion events. */
4398 int discard_events(long event_mask)
4400 XEvent e;
4401 int count;
4403 XSync(dpy, 0);
4404 for (count = 0; FCheckMaskEvent(dpy, event_mask, &e); count++)
4406 /* nothing */
4409 return count;
4412 /* This function discards all queued up ButtonPress, ButtonRelease and
4413 * ButtonMotion events. */
4414 int discard_window_events(Window w, long event_mask)
4416 XEvent e;
4417 int count;
4419 XSync(dpy, 0);
4420 for (count = 0; FCheckWindowEvent(dpy, w, event_mask, &e); count++)
4422 /* nothing */
4425 return count;
4428 /* Similar function for certain types of PropertyNotify. */
4429 int flush_property_notify(Atom atom, Window w)
4431 XEvent e;
4432 int count;
4433 test_typed_window_event_args args;
4435 XSync(dpy, 0);
4436 args.w = w;
4437 args.event_type = PropertyNotify;
4438 for (count = 0;
4439 FCheckPeekIfEvent(
4440 dpy, &e, test_typed_window_event, (XPointer)&args);
4441 count++)
4443 Bool rc;
4445 if (e.xproperty.atom != atom)
4447 break;
4449 /* remove the event from the queue */
4450 rc = FCheckIfEvent(
4451 dpy, &e, test_typed_window_event, (XPointer)&args);
4454 return count;
4457 /* Wait for all mouse buttons to be released
4458 * This can ease some confusion on the part of the user sometimes
4460 * Discard superflous button events during this wait period. */
4461 void WaitForButtonsUp(Bool do_handle_expose)
4463 unsigned int mask;
4464 unsigned int bmask;
4465 long evmask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
4466 KeyPressMask|KeyReleaseMask;
4467 int count;
4468 int use_wait_cursor;
4469 XEvent e;
4471 if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
4472 &JunkX, &JunkY, &mask) == False)
4474 /* pointer is on a different screen - that's okay here */
4476 mask &= DEFAULT_ALL_BUTTONS_MASK;
4477 if (mask == 0)
4479 return;
4481 if (do_handle_expose)
4483 evmask |= ExposureMask;
4485 GrabEm(None, GRAB_NORMAL);
4486 for (count = 0, use_wait_cursor = 0; mask != 0; count++)
4488 /* handle expose events */
4489 XAllowEvents(dpy, SyncPointer, CurrentTime);
4490 if (FCheckMaskEvent(dpy, evmask, &e))
4492 switch (e.type)
4494 case ButtonRelease:
4495 if (e.xbutton.button <=
4496 NUMBER_OF_MOUSE_BUTTONS)
4498 bmask = (Button1Mask <<
4499 (e.xbutton.button - 1));
4500 mask = e.xbutton.state & ~bmask;
4502 break;
4503 case Expose:
4504 dispatch_event(&e);
4505 break;
4506 default:
4507 break;
4510 else
4512 if (FQueryPointer(
4513 dpy, Scr.Root, &JunkRoot, &JunkChild,
4514 &JunkX, &JunkY, &JunkX, &JunkY, &mask) ==
4515 False)
4517 /* pointer is on a different screen - that's
4518 * okay here */
4520 mask &= DEFAULT_ALL_BUTTONS_MASK;
4521 usleep(1);
4523 if (use_wait_cursor == 0 && count == 20)
4525 GrabEm(CRS_WAIT, GRAB_NORMAL);
4526 use_wait_cursor = 1;
4529 UngrabEm(GRAB_NORMAL);
4530 if (use_wait_cursor)
4532 UngrabEm(GRAB_NORMAL);
4533 XFlush(dpy);
4536 return;
4539 void sync_server(int toggle)
4541 static Bool synced = False;
4543 if (toggle == -1)
4545 toggle = (synced == False);
4547 if (toggle == 1)
4549 synced = True;
4551 else
4553 synced = False;
4555 XSynchronize(dpy, synced);
4556 XFlush(dpy);
4558 return;
4561 Bool is_resizing_event_pending(
4562 FvwmWindow *fw)
4564 XEvent e;
4565 check_if_event_args args;
4567 args.w = FW_W(fw);
4568 args.do_return_true = False;
4569 args.do_return_true_cr = False;
4570 args.cr_value_mask = 0;
4571 args.ret_does_match = False;
4572 args.ret_type = 0;
4573 FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
4575 return args.ret_does_match;
4578 /* ---------------------------- builtin commands --------------------------- */
4580 void CMD_XSynchronize(F_CMD_ARGS)
4582 int toggle;
4584 toggle = ParseToggleArgument(action, NULL, -1, 0);
4585 sync_server(toggle);
4587 return;
4590 void CMD_XSync(F_CMD_ARGS)
4592 XSync(dpy, 0);
4594 return;