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.
6  *
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.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, see: <http://www.gnu.org/licenses/>
14  */
15 /* This module is based on Twm, but has been siginificantly modified
16  * by Rob Nation
17  */
18 /*
19  *       Copyright 1988 by Evans & Sutherland Computer Corporation,
20  *                          Salt Lake City, Utah
21  *  Portions Copyright 1989 by the Massachusetts Institute of Technology
22  *                        Cambridge, Massachusetts
23  *
24  *                           All Rights Reserved
25  *
26  *    Permission to use, copy, modify, and distribute this software and
27  *    its documentation  for  any  purpose  and  without  fee is hereby
28  *    granted, provided that the above copyright notice appear  in  all
29  *    copies and that both  that  copyright  notice  and  this  permis-
30  *    sion  notice appear in supporting  documentation,  and  that  the
31  *    names of Evans & Sutherland and M.I.T. not be used in advertising
32  *    in publicity pertaining to distribution of the  software  without
33  *    specific, written prior permission.
34  *
35  *    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD
36  *    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-
37  *    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR
38  *    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-
39  *    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA
40  *    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
41  *    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE
42  *    OR PERFORMANCE OF THIS SOFTWARE.
43  */
44 
45 /* ---------------------------- included header files ---------------------- */
46 
47 #include "config.h"
48 
49 #if HAVE_SYS_BSDTYPES_H
50 #include <sys/bsdtypes.h>
51 #endif
52 
53 #include <stdio.h>
54 #include <unistd.h>
55 #include <assert.h>
56 #include <X11/Xatom.h>
57 
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 "ewmh.h"
90 #include "update.h"
91 #include "style.h"
92 #include "stack.h"
93 #include "geometry.h"
94 #include "focus.h"
95 #include "virtual.h"
96 #include "decorations.h"
97 #include "schedule.h"
98 #include "menus.h"
99 #include "colormaps.h"
100 #include "colorset.h"
101 #ifdef HAVE_STROKE
102 #include "stroke.h"
103 #endif /* HAVE_STROKE */
104 
105 /* ---------------------------- local definitions -------------------------- */
106 
107 #ifndef XUrgencyHint
108 #define XUrgencyHint            (1L << 8)
109 #endif
110 
111 #define CR_MOVERESIZE_MASK (CWX | CWY | CWWidth | CWHeight | CWBorderWidth)
112 
113 #define DEBUG_GLOBALLY_ACTIVE 1
114 
115 #define MAX_NUM_WEED_EVENT_TYPES 40
116 
117 /* ---------------------------- local macros ------------------------------- */
118 
119 /* ---------------------------- imports ------------------------------------ */
120 
121 extern void StartupStuff(void);
122 
123 /* ---------------------------- included code files ------------------------ */
124 
125 /* ---------------------------- local types -------------------------------- */
126 
127 typedef void (*PFEH)(const evh_args_t *ea);
128 
129 typedef struct
130 {
131 	Window w;
132 	Bool do_return_true;
133 	Bool do_return_true_cr;
134 	unsigned long cr_value_mask;
135 	Bool ret_does_match;
136 	unsigned long ret_type;
137 } check_if_event_args;
138 
139 typedef struct
140 {
141 	unsigned do_forbid_function : 1;
142 	unsigned do_focus : 1;
143 	unsigned do_swallow_click : 1;
144 	unsigned do_raise : 1;
145 } hfrc_ret_t;
146 
147 typedef struct event_group
148 {
149 	int base;
150 	int count;
151 	PFEH *jump_table;
152 	struct event_group *next;
153 } event_group_t;
154 
155 typedef struct
156 {
157 	int num_event_types;
158 	int event_types[MAX_NUM_WEED_EVENT_TYPES];
159 } _weed_event_type_arg;
160 
161 typedef struct
162 {
163 	long event_mask;
164 } _weed_window_mask_events_arg;
165 
166 typedef struct
167 {
168 	Window w;
169 	XEvent *last_cr_event;
170 	int count;
171 } _merge_cr_args;
172 
173 /* ---------------------------- forward declarations ----------------------- */
174 
175 /* ---------------------------- local variables ---------------------------- */
176 
177 static int Button = 0;
178 static const FvwmWindow *xcrossing_last_grab_window = NULL;
179 STROKE_CODE(static int send_motion);
180 STROKE_CODE(static char sequence[STROKE_MAX_SEQUENCE + 1]);
181 static event_group_t *base_event_group = NULL;
182 
183 /* ---------------------------- exported variables (globals) --------------- */
184 
185 int last_event_type = 0;
186 Window PressedW = None;
187 
188 /* ---------------------------- local functions ---------------------------- */
189 
fake_map_unmap_notify(const FvwmWindow * fw,int event_type)190 static void fake_map_unmap_notify(const FvwmWindow *fw, int event_type)
191 {
192 	XEvent client_event;
193 	XWindowAttributes winattrs = {0};
194 
195 	if (!XGetWindowAttributes(dpy, FW_W(fw), &winattrs))
196 	{
197 		return;
198 	}
199 	XSelectInput(
200 		dpy, FW_W(fw),
201 		winattrs.your_event_mask & ~StructureNotifyMask);
202 	client_event.type = event_type;
203 	client_event.xmap.display = dpy;
204 	client_event.xmap.event = FW_W(fw);
205 	client_event.xmap.window = FW_W(fw);
206 	switch (event_type)
207 	{
208 	case MapNotify:
209 		client_event.xmap.override_redirect = False;
210 		break;
211 	case UnmapNotify:
212 		client_event.xunmap.from_configure = False;
213 		break;
214 	default:
215 		/* not possible if called correctly */
216 		break;
217 	}
218 	FSendEvent(
219 		dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
220 	XSelectInput(dpy, FW_W(fw), winattrs.your_event_mask);
221 	XFlush(dpy);
222 
223 	return;
224 }
225 
_pred_weed_accumulate_expose(Display * display,XEvent * ev,XPointer arg)226 static int _pred_weed_accumulate_expose(
227 	Display *display, XEvent *ev, XPointer arg)
228 {
229 	XEvent *em = (XEvent *)arg;
230 
231 	if (ev->type != Expose)
232 	{
233 		return 0;
234 	}
235 	{
236 		int x0;
237 		int x1;
238 
239 		x1 = max(
240 			ev->xexpose.x + ev->xexpose.width,
241 			em->xexpose.x + em->xexpose.width);
242 		x0 = min(em->xexpose.x, ev->xexpose.x);
243 		em->xexpose.x = x0;
244 		em->xexpose.width = x1 - x0;
245 	}
246 	{
247 		int y0;
248 		int y1;
249 
250 		y1 = max(
251 			ev->xexpose.y + ev->xexpose.height,
252 			em->xexpose.y + em->xexpose.height);
253 		y0 = min(em->xexpose.y, ev->xexpose.y);
254 		em->xexpose.y = y0;
255 		em->xexpose.height = y1 - y0;
256 	}
257 
258 	return 1;
259 }
260 
_pred_weed_handle_expose(Display * display,XEvent * event,XPointer arg)261 static int _pred_weed_handle_expose(
262 	Display *display, XEvent *event, XPointer arg)
263 {
264 	if (event->type == Expose)
265 	{
266 		dispatch_event(event);
267 		return 1;
268 	}
269 	else
270 	{
271 		return 0;
272 	}
273 }
274 
_pred_weed_event_type(Display * display,XEvent * event,XPointer arg)275 static int _pred_weed_event_type(
276 	Display *display, XEvent *event, XPointer arg)
277 {
278 	_weed_event_type_arg *args = (_weed_event_type_arg *)arg;
279 	int i;
280 
281 	for (i = 0; i < args->num_event_types; i++)
282 	{
283 		if (event->type == args->event_types[i])
284 		{
285 			/* invalidate event and continue weeding */
286 			return 1;
287 		}
288 	}
289 
290 	/* keep event and continue weeding */
291 	return 0;
292 }
293 
_pred_flush_property_notify_weed(Display * display,XEvent * event,XPointer arg)294 static int _pred_flush_property_notify_weed(
295 	Display *display, XEvent *event, XPointer arg)
296 {
297 	flush_property_notify_args *args =
298 		(flush_property_notify_args *)arg;
299 	int does_match_window;
300 
301 	does_match_window = (
302 		FEV_HAS_EVENT_WINDOW(event->type) &&
303 		event->xany.window == args->w) ? 1 : 0;
304 	if (
305 		does_match_window &&
306 		event->type == args->event_type &&
307 		event->xproperty.atom == args->atom)
308 	{
309 		/* invalidate event and continue weeding */
310 		return 1;
311 	}
312 	else if (
313 		args->do_stop_at_event_type &&
314 		event->type == args->stop_at_event_type && (
315 			!FEV_HAS_EVENT_WINDOW(args->stop_at_event_type) ||
316 			does_match_window))
317 	{
318 		/* keep event and stop weeding */
319 		return 2;
320 	}
321 
322 	/* keep event and continue weeding */
323 	return 0;
324 }
325 
test_resizing_event(Display * display,XEvent * event,XPointer arg)326 static Bool test_resizing_event(
327 	Display *display, XEvent *event, XPointer arg)
328 {
329 	check_if_event_args *cie_args;
330 	Bool rc;
331 
332 	cie_args = (check_if_event_args *)arg;
333 	cie_args->ret_does_match = False;
334 	if (event->xany.window != cie_args->w)
335 	{
336 		return False;
337 	}
338 	rc = False;
339 	switch (event->type)
340 	{
341 	case ConfigureRequest:
342 		if ((event->xconfigurerequest.value_mask &
343 		     cie_args->cr_value_mask) != 0)
344 		{
345 			cie_args->ret_type = ConfigureRequest;
346 			cie_args->ret_does_match = True;
347 			rc = cie_args->do_return_true_cr;
348 		}
349 		break;
350 	case PropertyNotify:
351 		if (event->xproperty.atom == XA_WM_NORMAL_HINTS)
352 		{
353 			cie_args->ret_type = PropertyNotify;
354 			cie_args->ret_does_match = True;
355 			rc = cie_args->do_return_true;
356 		}
357 	default:
358 		break;
359 	}
360 
361 	/* Yes, it is correct that this function may always returns False. */
362 	return rc;
363 }
364 
_handle_cr_on_unmanaged(XEvent * e)365 static inline void _handle_cr_on_unmanaged(XEvent *e)
366 {
367 	XConfigureRequestEvent *cre = &e->xconfigurerequest;
368 	XWindowChanges xwc;
369 	unsigned long xwcm;
370 
371 	xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
372 	xwc.x = cre->x;
373 	xwc.y = cre->y;
374 	xwc.width = cre->width;
375 	xwc.height = cre->height;
376 	xwc.border_width = cre->border_width;
377 	XConfigureWindow(dpy, cre->window, xwcm, &xwc);
378 
379 	return;
380 }
381 
_handle_cr_on_icon(XEvent * e,FvwmWindow * fw)382 static inline void _handle_cr_on_icon(XEvent *e, FvwmWindow *fw)
383 {
384 	XConfigureRequestEvent *cre = &e->xconfigurerequest;
385 	XWindowChanges xwc;
386 	unsigned long xwcm;
387 
388 	xwcm = (cre->value_mask & CR_MOVERESIZE_MASK);
389 	xwc.x = cre->x;
390 	xwc.y = cre->y;
391 	xwc.width = cre->width;
392 	xwc.height = cre->height;
393 	xwc.border_width = cre->border_width;
394 	if (FW_W_ICON_PIXMAP(fw) == cre->window)
395 	{
396 		int bw;
397 
398 		if (cre->value_mask & CWBorderWidth)
399 		{
400 			fw->icon_border_width = cre->border_width;
401 		}
402 		bw = fw->icon_border_width;
403 		if ((cre->value_mask & (CWWidth | CWHeight)) ==
404 		    (CWWidth | CWHeight))
405 		{
406 			set_icon_picture_size(
407 				fw, cre->width + 2 * bw, cre->height + 2 * bw);
408 		}
409 	}
410 	set_icon_position(fw, cre->x, cre->y);
411 	broadcast_icon_geometry(fw, False);
412 	XConfigureWindow(dpy, cre->window, xwcm, &xwc);
413 	if (cre->window != FW_W_ICON_PIXMAP(fw) &&
414 	    FW_W_ICON_PIXMAP(fw) != None)
415 	{
416 		rectangle g;
417 
418 		get_icon_picture_geometry(fw, &g);
419 		xwc.x = g.x;
420 		xwc.y = g.y;
421 		xwcm = cre->value_mask & (CWX | CWY);
422 		XConfigureWindow(
423 			dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
424 	}
425 	if (FW_W_ICON_TITLE(fw) != None)
426 	{
427 		rectangle g;
428 
429 		get_icon_title_geometry(fw, &g);
430 		xwc.x = g.x;
431 		xwc.y = g.y;
432 		xwcm = cre->value_mask & (CWX | CWY);
433 		XConfigureWindow(
434 			dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
435 	}
436 
437 	return;
438 }
439 
_handle_cr_on_shaped(FvwmWindow * fw)440 static inline void _handle_cr_on_shaped(FvwmWindow *fw)
441 {
442 	/* suppress compiler warnings w/o shape extension */
443 	int i;
444 	unsigned int u;
445 	Bool b;
446 	int boundingShaped;
447 
448 	SUPPRESS_UNUSED_VAR_WARNING(i);
449 	SUPPRESS_UNUSED_VAR_WARNING(u);
450 	SUPPRESS_UNUSED_VAR_WARNING(b);
451 	if (FShapeQueryExtents(
452 		    dpy, FW_W(fw), &boundingShaped, &i, &i, &u, &u, &b,
453 		    &i, &i, &u, &u))
454 	{
455 		fw->wShaped = boundingShaped;
456 	}
457 	else
458 	{
459 		fw->wShaped = 0;
460 	}
461 
462 	return;
463 }
464 
_handle_cr_restack(int * ret_do_send_event,XEvent * e,FvwmWindow * fw)465 static inline void _handle_cr_restack(
466 	int *ret_do_send_event, XEvent *e, FvwmWindow *fw)
467 {
468 	XConfigureRequestEvent *cre = &e->xconfigurerequest;
469 	XWindowChanges xwc;
470 	unsigned long xwcm;
471 	FvwmWindow *fw2 = NULL;
472 
473 	if (cre->value_mask & CWSibling)
474 	{
475 		if (XFindContext(
476 			    dpy, cre->above, FvwmContext,
477 			    (caddr_t *)&fw2) == XCNOENT)
478 		{
479 			fw2 = NULL;
480 		}
481 		if (fw2 == fw)
482 		{
483 			fw2 = NULL;
484 		}
485 	}
486 	if (cre->detail != Above && cre->detail != Below)
487 	{
488 		HandleUnusualStackmodes(
489 			cre->detail, fw, cre->window, fw2, cre->above);
490 	}
491 	/* only allow clients to restack windows within their layer */
492 	else if (fw2 == NULL || compare_window_layers(fw2, fw) != 0)
493 	{
494 		switch (cre->detail)
495 		{
496 		case Above:
497 			RaiseWindow(fw, True);
498 			break;
499 		case Below:
500 			LowerWindow(fw, True);
501 			break;
502 		}
503 	}
504 	else
505 	{
506 		xwc.sibling = FW_W_FRAME(fw2);
507 		xwc.stack_mode = cre->detail;
508 		xwcm = CWSibling | CWStackMode;
509 		XConfigureWindow(dpy, FW_W_FRAME(fw), xwcm, &xwc);
510 
511 		/* Maintain the condition that icon windows are stacked
512 		 * immediately below their frame
513 		 * 1. for fw */
514 		xwc.sibling = FW_W_FRAME(fw);
515 		xwc.stack_mode = Below;
516 		xwcm = CWSibling | CWStackMode;
517 		if (FW_W_ICON_TITLE(fw) != None)
518 		{
519 			XConfigureWindow(
520 				dpy, FW_W_ICON_TITLE(fw), xwcm, &xwc);
521 		}
522 		if (FW_W_ICON_PIXMAP(fw) != None)
523 		{
524 			XConfigureWindow(
525 				dpy, FW_W_ICON_PIXMAP(fw), xwcm, &xwc);
526 		}
527 		/* 2. for fw2 */
528 		if (cre->detail == Below)
529 		{
530 			xwc.sibling = FW_W_FRAME(fw2);
531 			xwc.stack_mode = Below;
532 			xwcm = CWSibling | CWStackMode;
533 			if (FW_W_ICON_TITLE(fw2) != None)
534 			{
535 				XConfigureWindow(
536 					dpy, FW_W_ICON_TITLE(fw2), xwcm, &xwc);
537 			}
538 			if (FW_W_ICON_PIXMAP(fw2) != None)
539 			{
540 				XConfigureWindow(
541 					dpy, FW_W_ICON_PIXMAP(fw2), xwcm,
542 					&xwc);
543 			}
544 		}
545 		/* Maintain the stacking order ring */
546 		if (cre->detail == Above)
547 		{
548 			remove_window_from_stack_ring(fw);
549 			add_window_to_stack_ring_after(
550 				fw, get_prev_window_in_stack_ring(fw2));
551 		}
552 		else /* cre->detail == Below */
553 		{
554 			remove_window_from_stack_ring(fw);
555 			add_window_to_stack_ring_after(fw, fw2);
556 		}
557 	        BroadcastRestackThisWindow(fw);
558 	}
559 	/* srt (28-Apr-2001): Tk needs a ConfigureNotify event after a
560 	 * raise, otherwise it would hang for two seconds */
561 	*ret_do_send_event = 1;
562 
563 	return;
564 }
565 
_cr_get_static_position(rectangle * ret_g,FvwmWindow * fw,XEvent * e,size_borders * b)566 static inline void _cr_get_static_position(
567 	rectangle *ret_g, FvwmWindow *fw, XEvent *e, size_borders *b)
568 {
569 	XConfigureRequestEvent *cre = &e->xconfigurerequest;
570 
571 	if (cre->value_mask & CWX)
572 	{
573 		ret_g->x = cre->x - b->top_left.width;
574 	}
575 	else
576 	{
577 		ret_g->x = fw->g.frame.x;
578 	}
579 	if (cre->value_mask & CWY)
580 	{
581 		ret_g->y = cre->y - b->top_left.height;
582 	}
583 	else
584 	{
585 		ret_g->y = fw->g.frame.y;
586 	}
587 
588 	return;
589 }
590 
_cr_get_grav_position(rectangle * ret_g,FvwmWindow * fw,XEvent * e,size_borders * b)591 static inline void _cr_get_grav_position(
592 	rectangle *ret_g, FvwmWindow *fw, XEvent *e, size_borders *b)
593 {
594 	XConfigureRequestEvent *cre = &e->xconfigurerequest;
595 	int grav_x;
596 	int grav_y;
597 
598 	gravity_get_offsets(fw->hints.win_gravity, &grav_x, &grav_y);
599 	if (cre->value_mask & CWX)
600 	{
601 		ret_g->x = cre->x - ((grav_x + 1) * b->total_size.width) / 2;
602 	}
603 	else
604 	{
605 		ret_g->x = fw->g.frame.x;
606 	}
607 	if (cre->value_mask & CWY)
608 	{
609 		ret_g->y = cre->y - ((grav_y + 1) * b->total_size.height) / 2;
610 	}
611 	else
612 	{
613 		ret_g->y = fw->g.frame.y;
614 	}
615 
616 	return;
617 }
618 
619 /* Try to detect whether the application uses the ICCCM way of moving its
620  * window or the traditional way, always assuming StaticGravity. */
_cr_detect_icccm_move(FvwmWindow * fw,XEvent * e,size_borders * b)621 static inline void _cr_detect_icccm_move(
622 	FvwmWindow *fw, XEvent *e, size_borders *b)
623 {
624 	XConfigureRequestEvent *cre = &e->xconfigurerequest;
625 	rectangle grav_g;
626 	rectangle static_g;
627 	rectangle dg_g;
628 	rectangle ds_g;
629 	int mx;
630 	int my;
631 	int m;
632 	int w;
633 	int h;
634 	int has_x;
635 	int has_y;
636 
637 	if (CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
638 	{
639 		if (Scr.bo.do_debug_cr_motion_method == 1)
640 		{
641 			fprintf(
642 				stderr,
643 				"_cdim: --- already detected (pid %d) %p"
644 				" '%s'\n", HAS_EWMH_WM_PID(fw), fw,
645 				fw->visible_name);
646 		}
647 		return;
648 	}
649 	if (HAS_EWMH_WM_PID(fw))
650 	{
651 		if (Scr.bo.do_debug_cr_motion_method == 1)
652 		{
653 			fprintf(
654 				stderr,"_cdim: +++ has ewmh_wm_pid: icccm"
655 				" %p '%s'\n", fw, fw->visible_name);
656 		}
657 		SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
658 		SET_CR_MOTION_METHOD_DETECTED(fw, 1);
659 		return;
660 	}
661 	if (fw->ewmh_window_type != EWMH_WINDOW_TYPE_NONE_ID)
662 	{
663 		if (Scr.bo.do_debug_cr_motion_method == 1)
664 		{
665 			fprintf(
666 				stderr, "_cdim: +++ has ewmh_window_type:"
667 				" icccm %p '%s'\n", fw,
668 				fw->visible_name);
669 		}
670 		SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
671 		SET_CR_MOTION_METHOD_DETECTED(fw, 1);
672 		return;
673 	}
674 	if (FShapesSupported && fw->wShaped)
675 	{
676 		if (Scr.bo.do_debug_cr_motion_method == 1)
677 		{
678 			fprintf(
679 				stderr, "_cdim: --- shaped window %p "
680 				"'%s'\n", fw, fw->visible_name);
681 		}
682 		/* no detection for shaped windows */
683 		return;
684 	}
685 	if (fw->hints.win_gravity == StaticGravity)
686 	{
687 		if (Scr.bo.do_debug_cr_motion_method == 1)
688 		{
689 			fprintf(
690 				stderr, "_cdim: --- using StaticGravity"
691 				" %p '%s'\n", fw, fw->visible_name);
692 		}
693 		return;
694 	}
695 	has_x = (cre->value_mask & CWX);
696 	has_y = (cre->value_mask & CWY);
697 	if (!has_x && !has_y)
698 	{
699 		if (Scr.bo.do_debug_cr_motion_method == 1)
700 		{
701 			fprintf(
702 				stderr, "_cdim: --- not moved %p '%s'\n",
703 				fw, fw->visible_name);
704 		}
705 		return;
706 	}
707 	_cr_get_grav_position(&grav_g, fw, e, b);
708 	_cr_get_static_position(&static_g, fw, e, b);
709 	if (static_g.x == grav_g.x)
710  	{
711 		/* both methods have the same result; ignore */
712 		has_x = 0;
713 	}
714 	if (static_g.y == grav_g.y)
715 	{
716 		/* both methods have the same result; ignore */
717 		has_y = 0;
718 	}
719 	if (!has_x && !has_y)
720 	{
721 		if (Scr.bo.do_debug_cr_motion_method == 1)
722 		{
723 			fprintf(
724 				stderr, "_cdim: --- not moved %p '%s'\n",
725 				fw, fw->visible_name);
726 		}
727 		return;
728 	}
729 	dg_g.x = grav_g.x - fw->g.frame.x;
730 	dg_g.y = grav_g.y - fw->g.frame.y;
731 	ds_g.x = static_g.x - fw->g.frame.x;
732 	ds_g.y = static_g.y - fw->g.frame.y;
733 	if (Scr.bo.do_debug_cr_motion_method == 1)
734 	{
735 		fprintf(
736 			stderr, "s %3d/%3d %2d/%2d, g %3d/%3d %2d/%2d: ",
737 			static_g.x, static_g.y, ds_g.x, ds_g.y, grav_g.x,
738 			grav_g.y, dg_g.x, dg_g.y);
739 	}
740 	/* check full screen */
741 	if ((cre->value_mask & (CWX | CWY)) == (CWX | CWY) &&
742 	    (has_x || has_y) &&
743 	    cre->width == Scr.MyDisplayWidth &&
744 	    cre->height == Scr.MyDisplayHeight)
745 	{
746 		if (grav_g.x == -b->top_left.width &&
747 		    grav_g.y == -b->top_left.height)
748 		{
749 			/* Window is fullscreen using the ICCCM way. */
750 			SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
751 			SET_CR_MOTION_METHOD_DETECTED(fw, 1);
752 			if (Scr.bo.do_debug_cr_motion_method == 1)
753 			{
754 				fprintf(
755 					stderr, "+++ fullscreen icccm %p"
756 					" '%s'\n", fw, fw->visible_name);
757 			}
758 			return;
759 		}
760 		else if (static_g.x == -b->top_left.width &&
761 			 static_g.y == -b->top_left.height)
762 		{
763 			/* Window is fullscreen using the traditional way. */
764 			SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
765 			SET_CR_MOTION_METHOD_DETECTED(fw, 1);
766 			if (Scr.bo.do_debug_cr_motion_method == 1)
767 			{
768 				fprintf(
769 					stderr, "+++ fullscreen traditional"
770 					" %p '%s'\n", fw,
771 					fw->visible_name);
772 			}
773 			return;
774 		}
775 	}
776 	/* check travelling across the screen */
777 	if (has_x && dg_g.x == 0 && ds_g.x != 0 &&
778 	    has_y && dg_g.y == 0 && ds_g.y != 0)
779 	{
780 		/* The traditional way causes a shift by the border width or
781 		 * height.  Use ICCCM way. */
782 		SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_USE_GRAV);
783 		SET_CR_MOTION_METHOD_DETECTED(fw, 1);
784 		if (Scr.bo.do_debug_cr_motion_method == 1)
785 		{
786 			fprintf(
787 				stderr, "+++ travelling icccm %p '%s'\n",
788 				fw, fw->visible_name);
789 		}
790 		return;
791 	}
792 	if (has_x && dg_g.x != 0 && ds_g.x == 0 &&
793 	    has_y && dg_g.y != 0 && ds_g.y == 0)
794 	{
795 		/* The ICCCM way causes a shift by the border width or height.
796 		 * Use traditional way. */
797 		SET_CR_MOTION_METHOD(fw, CR_MOTION_METHOD_STATIC_GRAV);
798 		SET_CR_MOTION_METHOD_DETECTED(fw, 1);
799 		if (Scr.bo.do_debug_cr_motion_method == 1)
800 		{
801 			fprintf(
802 				stderr, "+++ travelling traditional %p"
803 				" '%s'\n", fw, fw->visible_name);
804 		}
805 		return;
806 	}
807 	/* check placement near border */
808 	w = (cre->value_mask & CWWidth) ?
809 		cre->width + b->total_size.width : fw->g.frame.width;
810 	h = (cre->value_mask & CWHeight) ?
811 		cre->height + b->total_size.height : fw->g.frame.height;
812 	if (!has_x)
813 	{
814 		mx = CR_MOTION_METHOD_AUTO;
815 	}
816 	else if (static_g.x == 0 || static_g.x + w == Scr.MyDisplayWidth)
817 	{
818 		mx = CR_MOTION_METHOD_STATIC_GRAV;
819 	}
820 	else if (grav_g.x == 0 || grav_g.x + w == Scr.MyDisplayWidth)
821 	{
822 		mx = CR_MOTION_METHOD_USE_GRAV;
823 	}
824 	else
825 	{
826 		mx = CR_MOTION_METHOD_AUTO;
827 	}
828 	if (!has_y)
829 	{
830 		my = CR_MOTION_METHOD_AUTO;
831 	}
832 	else if (static_g.y == 0 || static_g.y + h == Scr.MyDisplayHeight)
833 	{
834 		my = CR_MOTION_METHOD_STATIC_GRAV;
835 	}
836 	else if (grav_g.y == 0 || grav_g.y + h == Scr.MyDisplayHeight)
837 	{
838 		my = CR_MOTION_METHOD_USE_GRAV;
839 	}
840 	else
841 	{
842 		my = CR_MOTION_METHOD_AUTO;
843 	}
844 	m = (mx != CR_MOTION_METHOD_AUTO) ? mx : my;
845 	if (m != CR_MOTION_METHOD_AUTO)
846 	{
847 		/* Window was placed next to the display border. */
848 		if (m == my || my == CR_MOTION_METHOD_AUTO)
849 		{
850 			SET_CR_MOTION_METHOD(fw, m);
851 			SET_CR_MOTION_METHOD_DETECTED(fw, 1);
852 			if (Scr.bo.do_debug_cr_motion_method == 1)
853 			{
854 				fprintf(
855 					stderr, "+++ near border %s %p "
856 					"'%s'\n", (m ==
857 						   CR_MOTION_METHOD_USE_GRAV)
858 					? "icccm" : "traditional", fw,
859 					fw->visible_name);
860 			}
861 			return;
862 		}
863 	}
864 	if (Scr.bo.do_debug_cr_motion_method == 1)
865 	{
866 		fprintf(
867 			stderr, "--- not detected %p '%s'\n", fw,
868 			fw->visible_name);
869 	}
870 
871 	return;
872 }
873 
874 /* This is not a good idea because this interferes with changes in the size
875  * hints of the window.  However, it is impossible to be completely safe here.
876  * For example, if the client changes the size inc, then resizes the size of
877  * its window and then changes the size inc again - all in one batch - then
878  * the WM will read the *second* size inc upon the *first* event and use the
879  * wrong one in the ConfigureRequest calculations. */
880 /* dv (31 Mar 2002): The code now handles these situations, so enable it
881  * again. */
_pred_merge_cr(Display * display,XEvent * event,XPointer arg)882 static int _pred_merge_cr(Display *display, XEvent *event, XPointer arg)
883 {
884 	_merge_cr_args *args = (_merge_cr_args *)arg;
885 
886 	switch (event->type)
887 	{
888 	case ConfigureRequest:
889 	{
890 		XConfigureRequestEvent *ecr = &event->xconfigurerequest;
891 		XConfigureRequestEvent *lcr =
892 			&args->last_cr_event->xconfigurerequest;
893 
894 		if (event->xconfigurerequest.window != args->w)
895 		{
896 			/* no match, keep looking */
897 			return 0;
898 		}
899 		/* collect the size/position changes */
900 		if (lcr->value_mask & CWX)
901 		{
902 			ecr->x = lcr->x;
903 		}
904 		if (lcr->value_mask & CWY)
905 		{
906 			ecr->y = lcr->y;
907 		}
908 		if (lcr->value_mask & CWWidth)
909 		{
910 			ecr->width = lcr->width;
911 		}
912 		if (lcr->value_mask & CWHeight)
913 		{
914 			ecr->height = lcr->height;
915 		}
916 		if (lcr->value_mask & CWBorderWidth)
917 		{
918 			ecr->border_width = lcr->border_width;
919 		}
920 		/* add to new event and remove from old event */
921 		ecr->value_mask |= (lcr->value_mask & CR_MOVERESIZE_MASK);
922 		lcr->value_mask &= ~CR_MOVERESIZE_MASK;
923 		if (lcr->value_mask == 0)
924 		{
925 			/* The event has no useful contents anymore. */
926 			FEV_INVALIDATE_EVENT(args->last_cr_event);
927 		}
928 		args->last_cr_event = event;
929 		args->count++;
930 
931 		/* don't drop the current event and continue weeding */
932 		return 0;
933 	}
934 	case PropertyNotify:
935 		if (
936 			event->xproperty.window == args->w &&
937 			event->xproperty.atom != XA_WM_NORMAL_HINTS)
938 		{
939 			/* ConfigureRequest events cannot be merged past
940 			 * changes of the size hints. */
941 			/* don't merge and stop weeding */
942 			return 2;
943 		}
944 		else
945 		{
946 			/* PropertyNotify for another window, or the changed
947 			 * property does not interfere with merging. */
948 			/* keep looking */
949 			return 0;
950 		}
951 	default:
952 		/* Other events do not interfer with merging. */
953 		/* keep looking */
954 		return 0;
955 	}
956 }
957 
_merge_cr_moveresize(const evh_args_t * ea,XEvent * ev,FvwmWindow * fw,size_borders * b)958 static inline int _merge_cr_moveresize(
959 	const evh_args_t *ea, XEvent *ev, FvwmWindow *fw, size_borders *b)
960 {
961 	_merge_cr_args args;
962 
963 	memset(&args, 0, sizeof(args));
964 	args.w = ev->xconfigurerequest.window;
965 	args.last_cr_event = ev;
966 	FWeedIfEvents(dpy, _pred_merge_cr, (XPointer)&args);
967 
968 #if 1 /*!!!*/
969 	if (args.count > 0)
970 	{
971 		fprintf(stderr, "%s: merged %d cr events\n", __func__, args.count);
972 	}
973 #endif
974 	/* use the count from the structure, not the return value of
975 	 * FWeedIfEvents() because the predicate has a different way of weeding
976 	 * and the return value is always zero. */
977 	return args.count;
978 }
979 
_handle_cr_on_client(int * ret_do_send_event,XEvent * e,const evh_args_t * ea,FvwmWindow * fw,Bool force,int force_gravity)980 static inline int _handle_cr_on_client(
981 	int *ret_do_send_event, XEvent *e, const evh_args_t *ea,
982 	FvwmWindow *fw, Bool force, int force_gravity)
983 {
984 	XConfigureRequestEvent *cre = &e->xconfigurerequest;
985 	rectangle current_g;
986 	rectangle new_g;
987 	rectangle d_g;
988 	size_rect constr_dim;
989 	size_rect oldnew_dim;
990 	size_borders b;
991 	int gravity;
992 
993 	if (ea)
994 	{
995 		cre = &ea->exc->x.etrigger->xconfigurerequest;
996 	}
997 	if (cre->value_mask & CWBorderWidth)
998 	{
999 		/* for restoring */
1000 		fw->attr_backup.border_width = cre->border_width;
1001 	}
1002 	if ((cre->value_mask & (CWWidth | CWHeight | CWX | CWY)) == 0)
1003 	{
1004 		return 0;
1005 	}
1006 
1007 	get_window_borders(fw, &b);
1008 	/* Merge all pending ConfigureRequests for the window into a single
1009 	 * event.  However, we can not do this if the window uses the motion
1010 	 * method autodetection because the merged event might confuse the
1011 	 * detection code. */
1012 	if (ea && CR_MOTION_METHOD(fw) != CR_MOTION_METHOD_AUTO)
1013 	{
1014 		int count;
1015 
1016 		count = _merge_cr_moveresize(ea, e, fw, &b);
1017 		if (count > 0)
1018 		{
1019 			/* the event has been merged into a later one, do
1020 			 * nothing */
1021 			return 0;
1022 		}
1023 	}
1024 #if 0
1025 	fprintf(stderr,
1026 		"cre: %d(%d) %d(%d) %d(%d)x%d(%d) fw 0x%08x w 0x%08x "
1027 		"ew 0x%08x  '%s'\n",
1028 		cre->x, (int)(cre->value_mask & CWX),
1029 		cre->y, (int)(cre->value_mask & CWY),
1030 		cre->width, (int)(cre->value_mask & CWWidth),
1031 		cre->height, (int)(cre->value_mask & CWHeight),
1032 		(int)FW_W_FRAME(fw), (int)FW_W(fw), (int)cre->window,
1033 		(fw->name.name) ? fw->name.name : "");
1034 #endif
1035 	/* Don't modify frame_g fields before calling SetupWindow! */
1036 	memset(&d_g, 0, sizeof(d_g));
1037 
1038 	if (HAS_NEW_WM_NORMAL_HINTS(fw))
1039 	{
1040 		/* get the latest size hints */
1041 		XSync(dpy, 0);
1042 		GetWindowSizeHints(fw);
1043 		SET_HAS_NEW_WM_NORMAL_HINTS(fw, 0);
1044 	}
1045 	if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMaxSize))
1046 	{
1047 		/* Java workaround */
1048 		if (cre->height > fw->hints.max_height &&
1049 		    fw->hints.max_height <= BROKEN_MAXSIZE_LIMIT)
1050 		{
1051 			fw->hints.max_height = DEFAULT_MAX_MAX_WINDOW_HEIGHT;
1052 			cre->value_mask |= CWHeight;
1053 		}
1054 		if (cre->width > fw->hints.max_width &&
1055 		    fw->hints.max_width <= BROKEN_MAXSIZE_LIMIT)
1056 		{
1057 			fw->hints.max_width = DEFAULT_MAX_MAX_WINDOW_WIDTH;
1058 			cre->value_mask |= CWWidth;
1059 		}
1060 	}
1061 	if (!HAS_OVERRIDE_SIZE_HINTS(fw) && (fw->hints.flags & PMinSize))
1062 	{
1063 		if (cre->width < fw->hints.min_width &&
1064 		    fw->hints.min_width >= BROKEN_MINSIZE_LIMIT)
1065 		{
1066 			fw->hints.min_width = 1;
1067 			cre->value_mask |= CWWidth;
1068 		}
1069 		if (cre->height < fw->hints.min_height &&
1070 		    fw->hints.min_height >= BROKEN_MINSIZE_LIMIT)
1071 		{
1072 			fw->hints.min_height = 1;
1073 			cre->value_mask |= CWHeight;
1074 		}
1075 	}
1076 	if (IS_SHADED(fw) ||
1077 	    !is_function_allowed(F_MOVE, NULL, fw, RQORIG_PROGRAM, False))
1078 	{
1079 		/* forbid shaded applications to move their windows */
1080 		cre->value_mask &= ~(CWX | CWY);
1081 		/* resend the old geometry */
1082 		*ret_do_send_event = 1;
1083 	}
1084 	if (IS_MAXIMIZED(fw))
1085 	{
1086 		/* dont allow clients to resize maximized windows */
1087 		cre->value_mask &= ~(CWWidth | CWHeight);
1088 		/* resend the old geometry */
1089 		*ret_do_send_event = 1;
1090 		d_g.width = 0;
1091 		d_g.height = 0;
1092 	}
1093 	else if (
1094 		!is_function_allowed(
1095 			F_RESIZE, NULL, fw, RQORIG_PROGRAM, False))
1096 	{
1097 		cre->value_mask &= ~(CWWidth | CWHeight);
1098 		*ret_do_send_event = 1;
1099 	}
1100 
1101 	if (!force && CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_AUTO)
1102 	{
1103 		_cr_detect_icccm_move(fw, e, &b);
1104 	}
1105 	if (force_gravity > ForgetGravity && force_gravity <= StaticGravity)
1106 	{
1107 		gravity = force_gravity;
1108 	}
1109 	else
1110 	{
1111 		gravity = fw->hints.win_gravity;
1112 	}
1113 	if (IS_SHADED(fw))
1114 	{
1115 		direction_t gravity_dir;
1116 
1117 		get_unshaded_geometry(fw, &current_g);
1118 		/* the shade direction overrides the window's gravity */
1119 		gravity_dir = gravity_grav_to_dir(gravity);
1120 		gravity_dir = gravity_override_dir(
1121 			gravity_dir, SHADED_DIR(fw));
1122 		gravity = gravity_dir_to_grav(gravity_dir);
1123 	}
1124 	else
1125 	{
1126 		current_g = fw->g.frame;
1127 	}
1128 	if (!(cre->value_mask & (CWX | CWY)))
1129 	{
1130 		/* nothing */
1131 	}
1132 	else if ((force ||
1133 		  CR_MOTION_METHOD(fw) == CR_MOTION_METHOD_USE_GRAV) &&
1134 		 gravity != StaticGravity)
1135 	{
1136 		int ref_x;
1137 		int ref_y;
1138 		int grav_x;
1139 		int grav_y;
1140 
1141 		gravity_get_offsets(gravity, &grav_x, &grav_y);
1142 		if (cre->value_mask & CWX)
1143 		{
1144 			ref_x = cre->x -
1145 				((grav_x + 1) * b.total_size.width) / 2;
1146 			d_g.x = ref_x - current_g.x;
1147 		}
1148 		if (cre->value_mask & CWY)
1149 		{
1150 			ref_y = cre->y -
1151 				((grav_y + 1) * b.total_size.height) / 2;
1152 			d_g.y = ref_y - current_g.y;
1153 		}
1154 	}
1155 	else /* ..._USE_GRAV or ..._AUTO */
1156 	{
1157 		/* default: traditional cr handling */
1158 		if (cre->value_mask & CWX)
1159 		{
1160 			d_g.x = cre->x - current_g.x - b.top_left.width;
1161 		}
1162 		if (cre->value_mask & CWY)
1163 		{
1164 			d_g.y = cre->y - current_g.y - b.top_left.height;
1165 		}
1166 	}
1167 
1168 	if (cre->value_mask & CWHeight)
1169 	{
1170 		if (cre->height <
1171 		    (WINDOW_FREAKED_OUT_SIZE - b.total_size.height))
1172 		{
1173 			d_g.height = cre->height -
1174 				(current_g.height - b.total_size.height);
1175 		}
1176 		else
1177 		{
1178 			/* Ignore height changes to astronomically large
1179 			 * windows (needed for XEmacs 20.4); don't care if the
1180 			 * window is shaded here - we won't use 'height' in
1181 			 * this case anyway.
1182 			 * Inform the buggy app about the size that *we* want
1183 			 */
1184 			d_g.height = 0;
1185 			*ret_do_send_event = 1;
1186 		}
1187 	}
1188 	if (cre->value_mask & CWWidth)
1189 	{
1190 		if (cre->width < (WINDOW_FREAKED_OUT_SIZE - b.total_size.width))
1191 		{
1192 			d_g.width = cre->width -
1193 				(current_g.width - b.total_size.width);
1194 		}
1195 		else
1196 		{
1197 			d_g.width = 0;
1198 			*ret_do_send_event = 1;
1199 		}
1200 	}
1201 
1202 	/* SetupWindow (x,y) are the location of the upper-left outer corner
1203 	 * and are passed directly to XMoveResizeWindow (frame).  The
1204 	 * (width,height) are the inner size of the frame.  The inner width is
1205 	 * the same as the requested client window width; the inner height is
1206 	 * the same as the requested client window height plus any title bar
1207 	 * slop. */
1208 	new_g = current_g;
1209 	oldnew_dim.width = new_g.width + d_g.width;
1210 	oldnew_dim.height = new_g.height + d_g.height;
1211 	constr_dim.width = oldnew_dim.width;
1212 	constr_dim.height = oldnew_dim.height;
1213 	constrain_size(
1214 		fw, NULL, &constr_dim.width, &constr_dim.height, 0, 0,
1215 		CS_UPDATE_MAX_DEFECT);
1216 	d_g.width += (constr_dim.width - oldnew_dim.width);
1217 	d_g.height += (constr_dim.height - oldnew_dim.height);
1218 	if ((cre->value_mask & CWX) && d_g.width)
1219 	{
1220 		new_g.x = current_g.x + d_g.x;
1221 		new_g.width = current_g.width + d_g.width;
1222 	}
1223 	else if ((cre->value_mask & CWX) && !d_g.width)
1224 	{
1225 		new_g.x = current_g.x + d_g.x;
1226 	}
1227 	else if (!(cre->value_mask & CWX) && d_g.width)
1228 	{
1229 		gravity_resize(gravity, &new_g, d_g.width, 0);
1230 	}
1231 	if ((cre->value_mask & CWY) && d_g.height)
1232 	{
1233 		new_g.y = current_g.y + d_g.y;
1234 		new_g.height = current_g.height + d_g.height;
1235 	}
1236 	else if ((cre->value_mask & CWY) && !d_g.height)
1237 	{
1238 		new_g.y = current_g.y + d_g.y;
1239 	}
1240 	else if (!(cre->value_mask & CWY) && d_g.height)
1241 	{
1242 		gravity_resize(gravity, &new_g, 0, d_g.height);
1243 	}
1244 
1245 	if (new_g.x == current_g.x && new_g.y == current_g.y &&
1246 	    new_g.width == current_g.width &&
1247 	    new_g.height == current_g.height)
1248 	{
1249 		/* Window will not be moved or resized; send a synthetic
1250 		 * ConfigureNotify. */
1251 		*ret_do_send_event = 1;
1252 	}
1253 	else if ((cre->value_mask & CWX) || (cre->value_mask & CWY) ||
1254 		 d_g.width || d_g.height)
1255 	{
1256 		if (IS_SHADED(fw))
1257 		{
1258 			fw->g.normal = new_g;
1259 			get_shaded_geometry(fw, &new_g, &new_g);
1260 		}
1261 		frame_setup_window_app_request(
1262 			fw, new_g.x, new_g.y, new_g.width, new_g.height,
1263 			False);
1264 		/* make sure the window structure has the new position */
1265 		update_absolute_geometry(fw);
1266 		maximize_adjust_offset(fw);
1267 	}
1268 	else if (DO_FORCE_NEXT_CR(fw))
1269 	{
1270 		*ret_do_send_event = 1;
1271 	}
1272 	SET_FORCE_NEXT_CR(fw, 0);
1273 	SET_FORCE_NEXT_PN(fw, 0);
1274 
1275 	return 1;
1276 }
1277 
_handle_configure_request(XEvent * e,const evh_args_t * ea,FvwmWindow * fw,Bool force,int force_gravity)1278 void _handle_configure_request(
1279 	XEvent *e, const evh_args_t *ea, FvwmWindow *fw,
1280 	Bool force, int force_gravity)
1281 {
1282 	XConfigureRequestEvent *cre = &e->xconfigurerequest;
1283 	int do_send_event = 0;
1284 	int cn_count = 0;
1285 
1286 	fev_sanitise_configure_request(cre);
1287 	/* According to the July 27, 1988 ICCCM draft, we should ignore size
1288 	 * and position fields in the WM_NORMAL_HINTS property when we map a
1289 	 * window. Instead, we'll read the current geometry.  Therefore, we
1290 	 * should respond to configuration requests for windows which have
1291 	 * never been mapped. */
1292 	if (fw == NULL)
1293 	{
1294 		_handle_cr_on_unmanaged(e);
1295 		return;
1296 	}
1297 	if (cre->window == FW_W_ICON_TITLE(fw) ||
1298 	    cre->window == FW_W_ICON_PIXMAP(fw))
1299 	{
1300 		_handle_cr_on_icon(e, fw);
1301 	}
1302 	if (FShapesSupported)
1303 	{
1304 		_handle_cr_on_shaped(fw);
1305 	}
1306 	if (fw != NULL && cre->window == FW_W(fw))
1307 	{
1308 		cn_count = _handle_cr_on_client(
1309 			&do_send_event, e, ea, fw, force, force_gravity);
1310 	}
1311 	/* Stacking order change requested.  Handle this *after* geometry
1312 	 * changes, since we need the new geometry in occlusion calculations */
1313 	if ((cre->value_mask & CWStackMode) &&
1314 	    (!DO_IGNORE_RESTACK(fw) || force))
1315 	{
1316 		_handle_cr_restack(&do_send_event, e, fw);
1317 	}
1318 #if 1
1319 	/* This causes some ddd windows not to be drawn properly. Reverted back
1320 	 * to the old method in frame_setup_window. */
1321 	/* domivogt (15-Oct-1999): enabled this to work around buggy apps that
1322 	 * ask for a nonsense height and expect that they really get it. */
1323 	if (cn_count == 0 && do_send_event)
1324 	{
1325 		cn_count = 1;
1326 	}
1327 	else if (cn_count > 0)
1328 	{
1329 		do_send_event = 1;
1330 	}
1331 	for ( ; cn_count > 0; cn_count--)
1332 	{
1333 		SendConfigureNotify(
1334 			fw, fw->g.frame.x, fw->g.frame.y, fw->g.frame.width,
1335 			fw->g.frame.height, 0, True);
1336 	}
1337 	if (do_send_event)
1338 	{
1339 		XFlush(dpy);
1340 	}
1341 #endif
1342 
1343 	return;
1344 }
1345 
_pred_button_click(Display * display,XEvent * event,XPointer arg)1346 static Bool _pred_button_click(
1347 	Display *display, XEvent *event, XPointer arg)
1348 {
1349 	if (event->type == ButtonPress || event->type == ButtonRelease)
1350 	{
1351 		return True;
1352 	}
1353 
1354 	return False;
1355 }
1356 
1357 /* Helper function for __handle_focus_raise_click(). */
__test_for_motion(int x0,int y0)1358 static Bool __test_for_motion(int x0, int y0)
1359 {
1360 	int x;
1361 	int y;
1362 	unsigned int mask;
1363 	XEvent e;
1364 
1365 	/* Query the pointer to do this. We can't check for events here since
1366 	 * the events are still needed if the pointer moves. */
1367 
1368 	/* However, some special mouse (e.g., a touchpad with the
1369 	 * synaptic driver) may handle a double click in a special way
1370 	 * (for dragging through short touching and holding down the
1371 	 * finger on the touchpad). Bascially, when you execute a
1372 	 * double click the first button release is queued after the
1373 	 * second _physical_ mouse release happen. It seems that
1374 	 * FQueryPointer may not work as expected: it does not see
1375 	 * that the button is released on a double click.  So, we need
1376 	 * to check for a button press in the future to avoid a fvwm
1377 	 * lockup! (olicha 2004-01-31) */
1378 
1379 	for (x = x0, y = y0; FQueryPointer(
1380 		     dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
1381 		     &x, &y, &mask) == True; usleep(20000))
1382 	{
1383 		if ((mask & DEFAULT_ALL_BUTTONS_MASK) == 0)
1384 		{
1385 			/* all buttons are released */
1386 			return False;
1387 		}
1388 		else if (abs(x - x0) >= Scr.MoveThreshold ||
1389 			 abs(y - y0) >= Scr.MoveThreshold)
1390 		{
1391 			/* the pointer has moved */
1392 			return True;
1393 		}
1394 		if (FCheckPeekIfEvent(dpy, &e, _pred_button_click, NULL))
1395 		{
1396 			/* click in the future */
1397 			return False;
1398 		}
1399 		else
1400 		{
1401 			/* The predicate procedure finds no match, no event
1402 			 * has been removed from the queue and XFlush was
1403 			 * called. Nothing to do */
1404 		}
1405 	}
1406 
1407 	/* pointer has moved off screen */
1408 	return True;
1409 }
1410 
1411 /* Helper function for __handle_focus_raise_click(). */
__check_click_to_focus_or_raise(hfrc_ret_t * ret_args,const exec_context_t * exc)1412 static void __check_click_to_focus_or_raise(
1413 	hfrc_ret_t *ret_args, const exec_context_t *exc)
1414 {
1415 	FvwmWindow * const fw = exc->w.fw;
1416 	const XEvent *te = exc->x.etrigger;
1417 	struct
1418 	{
1419 		unsigned is_client_click : 1;
1420 		unsigned is_focused : 1;
1421 	} f;
1422 
1423 	f.is_focused = !!focus_is_focused(fw);
1424 	f.is_client_click = (exc->w.wcontext == C_WINDOW ||
1425 			     exc->w.wcontext == C_EWMH_DESKTOP);
1426 	/* check if we need to raise and/or focus the window */
1427 	ret_args->do_focus = focus_query_click_to_focus(fw, exc->w.wcontext);
1428 	if (f.is_client_click && !ret_args->do_focus &&
1429 	    !f.is_focused && FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
1430 	    !fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
1431 	{
1432 		/* Give the window a chance to to take focus itself */
1433 		ret_args->do_focus = 1;
1434 	}
1435 	if (ret_args->do_focus && focus_is_focused(fw))
1436 	{
1437 		ret_args->do_focus = 0;
1438 	}
1439  	ret_args->do_raise =
1440 		focus_query_click_to_raise(fw, f.is_focused, exc->w.wcontext);
1441 #define EXPERIMENTAL_ROU_HANDLING_V2
1442 #ifdef EXPERIMENTAL_ROU_HANDLING_V2
1443 /*  RBW -- Dang! This works without the one in HandleEnterNotify!  */
1444 	if (ret_args->do_raise && is_on_top_of_layer_and_above_unmanaged(fw))
1445 #else
1446 	if (ret_args->do_raise && is_on_top_of_layer(fw))
1447 #endif
1448 	{
1449 		ret_args->do_raise = 0;
1450 	}
1451 	if ((ret_args->do_focus &&
1452 	     FP_DO_IGNORE_FOCUS_CLICK_MOTION(FW_FOCUS_POLICY(fw))) ||
1453 	    (ret_args->do_raise &&
1454 	     FP_DO_IGNORE_RAISE_CLICK_MOTION(FW_FOCUS_POLICY(fw))))
1455 	{
1456 		/* Pass further events to the application and check if a button
1457 		 * release or motion event occurs next.  If we don't do this
1458 		 * here, the pointer will seem to be frozen in
1459 		 * __test_for_motion(). */
1460 		XAllowEvents(dpy, ReplayPointer, CurrentTime);
1461 		if (__test_for_motion(te->xbutton.x_root, te->xbutton.y_root))
1462 		{
1463 			/* the pointer was moved, process event normally */
1464 			ret_args->do_focus = 0;
1465 			ret_args->do_raise = 0;
1466 		}
1467 	}
1468 	if (ret_args->do_focus || ret_args->do_raise)
1469 	{
1470 		if (!((ret_args->do_focus &&
1471 		       FP_DO_ALLOW_FUNC_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1472 		      (ret_args->do_raise &&
1473 		       FP_DO_ALLOW_FUNC_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1474 		{
1475 			ret_args->do_forbid_function = 1;
1476 		}
1477 		if (!((ret_args->do_focus &&
1478 		       FP_DO_PASS_FOCUS_CLICK(FW_FOCUS_POLICY(fw))) ||
1479 		      (ret_args->do_raise &&
1480 		       FP_DO_PASS_RAISE_CLICK(FW_FOCUS_POLICY(fw)))))
1481 		{
1482 			ret_args->do_swallow_click = 1;
1483 		}
1484 	}
1485 
1486 	return;
1487 }
1488 
1489 /* Finds out if the click on a window must be used to focus or raise it. */
__handle_focus_raise_click(hfrc_ret_t * ret_args,const exec_context_t * exc)1490 static void __handle_focus_raise_click(
1491 	hfrc_ret_t *ret_args, const exec_context_t *exc)
1492 {
1493 	memset(ret_args, 0, sizeof(*ret_args));
1494 	if (exc->w.fw == NULL)
1495 	{
1496 		return;
1497 	}
1498 	/* check for proper click button and modifiers*/
1499 	if (FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) != 0 &&
1500 	    !(FP_USE_MOUSE_BUTTONS(FW_FOCUS_POLICY(exc->w.fw)) &
1501 	      (1 << (exc->x.etrigger->xbutton.button - 1))))
1502 	{
1503 		/* wrong button, handle click normally */
1504 		return;
1505 	}
1506 	else if (FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw)) !=
1507 		 FPOL_ANY_MODIFIER &&
1508 		 MaskUsedModifiers(
1509 			 FP_USE_MODIFIERS(FW_FOCUS_POLICY(exc->w.fw))) !=
1510 		 MaskUsedModifiers(exc->x.etrigger->xbutton.state))
1511 	{
1512 		/* right button but wrong modifiers, handle click normally */
1513 		return;
1514 	}
1515 	else
1516 	{
1517 		__check_click_to_focus_or_raise(ret_args, exc);
1518 	}
1519 
1520 	return;
1521 }
1522 
1523 /* Helper function for HandleButtonPress */
__is_bpress_window_handled(const exec_context_t * exc)1524 static Bool __is_bpress_window_handled(const exec_context_t *exc)
1525 {
1526 	Window eventw;
1527 	const XEvent *te = exc->x.etrigger;
1528 
1529 	if (exc->w.fw == NULL)
1530 	{
1531 		if ((te->xbutton.window != Scr.Root ||
1532 		     te->xbutton.subwindow != None) &&
1533 		    !is_pan_frame(te->xbutton.window))
1534 		{
1535 			/* Ignore events in unmanaged windows or subwindows of
1536 			 * a client */
1537 			return False;
1538 		}
1539 		else
1540 		{
1541 			return True;
1542 		}
1543 	}
1544 	eventw = (te->xbutton.subwindow != None &&
1545 		  te->xany.window != FW_W(exc->w.fw)) ?
1546 		te->xbutton.subwindow : te->xany.window;
1547 	if (is_frame_hide_window(eventw) || eventw == FW_W_FRAME(exc->w.fw))
1548 	{
1549 		return False;
1550 	}
1551 	if (!XGetGeometry(
1552 		    dpy, eventw, &JunkRoot, &JunkX, &JunkY,
1553 		    (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight,
1554 		    (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth))
1555 	{
1556 		/* The window has already died. */
1557 		return False;
1558 	}
1559 
1560 	return True;
1561 }
1562 
1563 /* Helper function for __handle_bpress_on_managed */
__handle_click_to_focus(const exec_context_t * exc)1564 static Bool __handle_click_to_focus(const exec_context_t *exc)
1565 {
1566 	fpol_set_focus_by_t set_by;
1567 
1568 	switch (exc->w.wcontext)
1569 	{
1570 	case C_WINDOW:
1571 	case C_EWMH_DESKTOP:
1572 		set_by = FOCUS_SET_BY_CLICK_CLIENT;
1573 		break;
1574 	case C_ICON:
1575 		set_by = FOCUS_SET_BY_CLICK_ICON;
1576 		break;
1577 	default:
1578 		set_by = FOCUS_SET_BY_CLICK_DECOR;
1579 		break;
1580 	}
1581 	SetFocusWindow(exc->w.fw, True, set_by);
1582 	focus_grab_buttons(exc->w.fw);
1583 	if (focus_is_focused(exc->w.fw) && !IS_ICONIFIED(exc->w.fw))
1584 	{
1585 		border_draw_decorations(
1586 			exc->w.fw, PART_ALL, True, True, CLEAR_ALL, NULL,
1587 			NULL);
1588 	}
1589 
1590 	return focus_is_focused(exc->w.fw);
1591 }
1592 
1593 /* Helper function for __handle_bpress_on_managed */
__handle_click_to_raise(const exec_context_t * exc)1594 static Bool __handle_click_to_raise(const exec_context_t *exc)
1595 {
1596 	Bool rc = False;
1597 	int is_focused;
1598 
1599 	is_focused = focus_is_focused(exc->w.fw);
1600 	if (focus_query_click_to_raise(exc->w.fw, is_focused, True))
1601 	{
1602 		rc = True;
1603 	}
1604 
1605 	return rc;
1606 }
1607 
1608 /* Helper function for HandleButtonPress */
__handle_bpress_stroke(void)1609 static void __handle_bpress_stroke(void)
1610 {
1611 	STROKE_CODE(stroke_init());
1612 	STROKE_CODE(send_motion = True);
1613 
1614 	return;
1615 }
1616 
1617 /* Helper function for __handle_bpress_on_managed */
__handle_bpress_action(const exec_context_t * exc,char * action)1618 static Bool __handle_bpress_action(
1619 	const exec_context_t *exc, char *action)
1620 {
1621 	window_parts part;
1622 	Bool do_force;
1623 	Bool rc = False;
1624 
1625 	if (!action || *action == 0)
1626 	{
1627 		PressedW = None;
1628 		return False;
1629 	}
1630 	/* draw pressed in decorations */
1631 	part = border_context_to_parts(exc->w.wcontext);
1632 	do_force = (part & PART_TITLEBAR) ? True : False;
1633 	border_draw_decorations(
1634 		exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1635 		CLEAR_ALL, NULL, NULL);
1636 	/* execute the action */
1637 	if (IS_ICONIFIED(exc->w.fw))
1638 	{
1639 		/* release the pointer since it can't do harm over an icon */
1640 		XAllowEvents(dpy, AsyncPointer, CurrentTime);
1641 	}
1642 	execute_function(NULL, exc, action, 0);
1643 	if (exc->w.wcontext != C_WINDOW && exc->w.wcontext != C_NO_CONTEXT)
1644 	{
1645 		WaitForButtonsUp(True);
1646 		rc = True;
1647 	}
1648 	/* redraw decorations */
1649 	PressedW = None;
1650 	if (check_if_fvwm_window_exists(exc->w.fw))
1651 	{
1652 		part = border_context_to_parts(exc->w.wcontext);
1653 		do_force = (part & PART_TITLEBAR) ? True : False;
1654 		border_draw_decorations(
1655 			exc->w.fw, part, (Scr.Hilite == exc->w.fw), do_force,
1656 			CLEAR_ALL, NULL, NULL);
1657 	}
1658 
1659 	return rc;
1660 }
1661 
1662 /* Handles button presses on the root window. */
__handle_bpress_on_root(const exec_context_t * exc)1663 static void __handle_bpress_on_root(const exec_context_t *exc)
1664 {
1665 	char *action;
1666 
1667 	PressedW = None;
1668 	__handle_bpress_stroke();
1669 	/* search for an appropriate mouse binding */
1670 	action = CheckBinding(
1671 		Scr.AllBindings, STROKE_ARG(0) exc->x.etrigger->xbutton.button,
1672 		exc->x.etrigger->xbutton.state, GetUnusedModifiers(), C_ROOT,
1673 		BIND_BUTTONPRESS, NULL, NULL);
1674 	if (action && *action)
1675 	{
1676 		const exec_context_t *exc2;
1677 		exec_context_changes_t ecc;
1678 
1679 		ecc.w.wcontext = C_ROOT;
1680 		exc2 = exc_clone_context(exc, &ecc, ECC_WCONTEXT);
1681 		execute_function(NULL, exc2, action, 0);
1682 		exc_destroy_context(exc2);
1683 		WaitForButtonsUp(True);
1684 	}
1685 
1686 	return;
1687 }
1688 
1689 /* Handles button presses on unmanaged windows */
__handle_bpress_on_unmanaged(const exec_context_t * exc)1690 static void __handle_bpress_on_unmanaged(const exec_context_t *exc)
1691 {
1692 	/* Pass the event to the application. */
1693 	XAllowEvents(dpy, ReplayPointer, CurrentTime);
1694 	XFlush(dpy);
1695 
1696 	return;
1697 }
1698 
1699 /* Handles button presses on managed windows */
__handle_bpress_on_managed(const exec_context_t * exc)1700 static void __handle_bpress_on_managed(const exec_context_t *exc)
1701 {
1702 	char *action;
1703 	hfrc_ret_t f;
1704 	FvwmWindow * const fw = exc->w.fw;
1705 	XEvent *e;
1706 
1707 	e = exc->x.etrigger;
1708 	/* Now handle click to focus and click to raise. */
1709 	__handle_focus_raise_click(&f, exc);
1710 	PressedW = (f.do_forbid_function) ? None : exc->w.w;
1711 	if (f.do_focus)
1712 	{
1713 		if (!__handle_click_to_focus(exc))
1714 		{
1715 			/* Window didn't accept the focus; pass the click to
1716 			 * the application. */
1717 			f.do_swallow_click = 0;
1718 		}
1719 	}
1720 	if (f.do_raise)
1721 	{
1722 		if (__handle_click_to_raise(exc) == True)
1723 		{
1724 			/* We can't raise the window immediately because the
1725 			 * action bound to the click might be "Lower" or
1726 			 * "RaiseLower". So mark the window as scheduled to be
1727 			 * raised after the binding is executed. Functions that
1728 			 * modify the stacking order will reset this flag. */
1729 			SET_SCHEDULED_FOR_RAISE(fw, 1);
1730 		}
1731 	}
1732 	/* handle bindings */
1733 	if (!f.do_forbid_function)
1734 	{
1735 		/* stroke bindings */
1736 		__handle_bpress_stroke();
1737 		/* mouse bindings */
1738 		action = CheckBinding(
1739 			Scr.AllBindings, STROKE_ARG(0) e->xbutton.button,
1740 			e->xbutton.state, GetUnusedModifiers(),
1741 			exc->w.wcontext, BIND_BUTTONPRESS, &fw->class,
1742 			fw->name.name);
1743 		if (__handle_bpress_action(exc, action))
1744 		{
1745 			f.do_swallow_click = 1;
1746 		}
1747 	}
1748 	/* raise the window */
1749 	if (IS_SCHEDULED_FOR_RAISE(fw))
1750 	{
1751 		/* Now that we know the action did not restack the window we
1752 		 * can raise it.
1753 		 * dv (10-Aug-2002):  We can safely raise the window after
1754 		 * redrawing it since all the decorations are drawn in the
1755 		 * window background and no Expose event is generated. */
1756 		RaiseWindow(fw, False);
1757 		SET_SCHEDULED_FOR_RAISE(fw, 0);
1758 	}
1759 	/* clean up */
1760 	if (!f.do_swallow_click)
1761 	{
1762 		/* pass the click to the application */
1763 		XAllowEvents(dpy, ReplayPointer, CurrentTime);
1764 		XFlush(dpy);
1765 	}
1766 	else if (f.do_focus || f.do_raise)
1767 	{
1768 		WaitForButtonsUp(True);
1769 	}
1770 
1771 	return;
1772 }
1773 
1774 /* restore focus stolen by unmanaged */
__refocus_stolen_focus_win(const evh_args_t * ea)1775 static void __refocus_stolen_focus_win(const evh_args_t *ea)
1776 {
1777 	FOCUS_SET(Scr.StolenFocusWin, Scr.StolenFocusFvwmWin);
1778 	ea->exc->x.etrigger->xfocus.window = Scr.StolenFocusWin;
1779 	ea->exc->x.etrigger->type = FocusIn;
1780 	Scr.UnknownWinFocused = None;
1781 	Scr.StolenFocusWin = None;
1782 	Scr.StolenFocusFvwmWin = NULL;
1783 	dispatch_event(ea->exc->x.etrigger);
1784 
1785 	return;
1786 }
1787 
1788 /* ---------------------------- event handlers ----------------------------- */
1789 
HandleButtonPress(const evh_args_t * ea)1790 void HandleButtonPress(const evh_args_t *ea)
1791 {
1792 	DBUG("HandleButtonPress", "Routine Entered");
1793 
1794 	GrabEm(CRS_NONE, GRAB_PASSIVE);
1795 	if (__is_bpress_window_handled(ea->exc) == False)
1796 	{
1797 		__handle_bpress_on_unmanaged(ea->exc);
1798 	}
1799 	else if (ea->exc->w.fw != NULL)
1800 	{
1801 		__handle_bpress_on_managed(ea->exc);
1802 	}
1803 	else
1804 	{
1805 		__handle_bpress_on_root(ea->exc);
1806 	}
1807 	UngrabEm(GRAB_PASSIVE);
1808 
1809 	return;
1810 }
1811 
1812 #ifdef HAVE_STROKE
HandleButtonRelease(const evh_args_t * ea)1813 void HandleButtonRelease(const evh_args_t *ea)
1814 {
1815 	char *action;
1816 	char *name;
1817 	int real_modifier;
1818 	const XEvent *te = ea->exc->x.etrigger;
1819 	XClassHint *class;
1820 
1821 	DBUG("HandleButtonRelease", "Routine Entered");
1822 	send_motion = False;
1823 	stroke_trans (sequence);
1824 	DBUG("HandleButtonRelease",sequence);
1825 	/*  Allows modifier to work (Only R context works here). */
1826 	real_modifier = te->xbutton.state - (1 << (7 + te->xbutton.button));
1827 	if (ea->exc->w.fw == NULL)
1828 	{
1829 		class = NULL;
1830 		name = NULL;
1831 	}
1832 	else
1833 	{
1834 		class = &ea->exc->w.fw->class;
1835 		name = ea->exc->w.fw->name.name;
1836 	}
1837 	/* need to search for an appropriate stroke binding */
1838 	action = CheckBinding(
1839 		Scr.AllBindings, sequence, te->xbutton.button, real_modifier,
1840 		GetUnusedModifiers(), ea->exc->w.wcontext, BIND_STROKE,
1841 		class, name);
1842 	/* got a match, now process it */
1843 	if (action != NULL && (action[0] != 0))
1844 	{
1845 		execute_function(NULL, ea->exc, action, 0);
1846 		WaitForButtonsUp(True);
1847 	}
1848 
1849 	return;
1850 }
1851 #endif /* HAVE_STROKE */
1852 
HandleClientMessage(const evh_args_t * ea)1853 void HandleClientMessage(const evh_args_t *ea)
1854 {
1855 	const XEvent *te = ea->exc->x.etrigger;
1856 	FvwmWindow * const fw = ea->exc->w.fw;
1857 
1858 	DBUG("HandleClientMessage", "Routine Entered");
1859 
1860 	if (EWMH_ProcessClientMessage(ea->exc))
1861 	{
1862 		return;
1863 	}
1864 
1865 	/* handle deletion of tear out menus */
1866 	if (fw && IS_TEAR_OFF_MENU(fw) && te->xclient.format == 32 &&
1867 	    te->xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
1868 	{
1869 		menu_close_tear_off_menu(fw);
1870 		return;
1871 	}
1872 
1873 	if (te->xclient.message_type == _XA_WM_CHANGE_STATE &&
1874 	    fw && te->xclient.data.l[0] == IconicState && !IS_ICONIFIED(fw))
1875 	{
1876 		const exec_context_t *exc;
1877 		exec_context_changes_t ecc;
1878 
1879 		ecc.w.wcontext = C_WINDOW;
1880 		exc = exc_clone_context(ea->exc, &ecc, ECC_WCONTEXT);
1881 		execute_function(NULL, exc, "Iconify", 0);
1882 		exc_destroy_context(exc);
1883 		return;
1884 	}
1885 
1886 	/* FIXME: Is this safe enough ? I guess if clients behave
1887 	 * according to ICCCM and send these messages only if they
1888 	 * grabbed the pointer, it is OK */
1889 	{
1890 		extern Atom _XA_WM_COLORMAP_NOTIFY;
1891 		if (te->xclient.message_type == _XA_WM_COLORMAP_NOTIFY)
1892 		{
1893 			set_client_controls_colormaps(te->xclient.data.l[1]);
1894 			return;
1895 		}
1896 	}
1897 
1898 	/* CKH - if we get here, it was an unknown client message, so send
1899 	 * it to the client if it was in a window we know about.  I'm not so
1900 	 * sure this should be done or not, since every other window manager
1901 	 * I've looked at doesn't.  But it might be handy for a free drag and
1902 	 * drop setup being developed for Linux. */
1903 	/* TA:  20091231 - But this confuses QT Drag and Drop since it handles
1904 	 * processing XSendEvents in an odd order.  For now, workaround this
1905 	 * by using a BugOpts option.
1906 	 */
1907 	if (fw)
1908 	{
1909 		if ((!Scr.bo.do_enable_qt_drag_n_drop_workaround) &&
1910 				(te->xclient.window != FW_W(fw)))
1911 		{
1912 			XEvent e;
1913 
1914 			e = *te;
1915 			e.xclient.window = FW_W(fw);
1916 			FSendEvent(dpy, FW_W(fw), False, NoEventMask, &e);
1917 		}
1918 	}
1919 }
1920 
HandleColormapNotify(const evh_args_t * ea)1921 void HandleColormapNotify(const evh_args_t *ea)
1922 {
1923 	colormap_handle_colormap_notify(ea);
1924 
1925 	return;
1926 }
1927 
HandleConfigureRequest(const evh_args_t * ea)1928 void HandleConfigureRequest(const evh_args_t *ea)
1929 {
1930 	XEvent *te = ea->exc->x.etrigger;
1931 	XConfigureRequestEvent *cre;
1932 	FvwmWindow *fw = ea->exc->w.fw;
1933 
1934 	DBUG("HandleConfigureRequest", "Routine Entered");
1935 
1936 	cre = &te->xconfigurerequest;
1937 	/* te->xany.window is te->.xconfigurerequest.parent, so the context
1938 	 * window may be wrong. */
1939 	if (XFindContext(dpy, cre->window, FvwmContext, (caddr_t *)&fw) ==
1940 	    XCNOENT)
1941 	{
1942 		fw = NULL;
1943 	}
1944 	_handle_configure_request(te, ea, fw, False, ForgetGravity);
1945 
1946 	return;
1947 }
1948 
HandleDestroyNotify(const evh_args_t * ea)1949 void HandleDestroyNotify(const evh_args_t *ea)
1950 {
1951 	DBUG("HandleDestroyNotify", "Routine Entered");
1952 
1953 	destroy_window(ea->exc->w.fw);
1954 	EWMH_ManageKdeSysTray(
1955 		ea->exc->x.etrigger->xdestroywindow.window,
1956 		ea->exc->x.etrigger->type);
1957 	EWMH_WindowDestroyed();
1958 
1959 	return;
1960 }
1961 
1962 #define DEBUG_ENTERNOTIFY 0
1963 #if DEBUG_ENTERNOTIFY
1964 static int ecount=0;
1965 #define ENTER_DBG(x) fprintf x;
1966 #else
1967 #define ENTER_DBG(x)
1968 #endif
HandleEnterNotify(const evh_args_t * ea)1969 void HandleEnterNotify(const evh_args_t *ea)
1970 {
1971 	const XEnterWindowEvent *ewp;
1972 	XEvent d;
1973 	FvwmWindow *sf;
1974 	static Bool is_initial_ungrab_pending = True;
1975 	Bool is_tear_off_menu;
1976 	const XEvent *te = ea->exc->x.etrigger;
1977 	FvwmWindow * const fw = ea->exc->w.fw;
1978 
1979 	DBUG("HandleEnterNotify", "Routine Entered");
1980 	ewp = &te->xcrossing;
1981 ENTER_DBG((stderr, "++++++++ en (%d): fw %p w 0x%08x sw 0x%08x mode 0x%x detail 0x%x '%s'\n", ++ecount, fw, (int)ewp->window, (int)ewp->subwindow, ewp->mode, ewp->detail, fw?fw->visible_name:"(none)"));
1982 
1983 	if (
1984 		ewp->window == Scr.Root &&
1985 		ewp->detail == NotifyInferior && ewp->mode == NotifyNormal)
1986 	{
1987 		/* pointer left subwindow */
1988 		BroadcastPacket(
1989 			MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1990 			(long)NULL);
1991 	}
1992 	else if (
1993 		ewp->window == Scr.Root &&
1994 		ewp->detail == NotifyNonlinearVirtual)
1995 	{
1996 		/* pointer entered screen */
1997 		BroadcastPacket(
1998 			MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
1999 			(long)NULL);
2000 	}
2001 	if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2002 	{
2003 		if (fw && !IS_ICONIFIED(fw) && ewp->window == FW_W(fw))
2004 		{
2005 			InstallWindowColormaps(fw);
2006 		}
2007 		else
2008 		{
2009 			/* make sure its for one of our windows */
2010 			/* handle a subwindow cmap */
2011 			InstallWindowColormaps(NULL);
2012 		}
2013 	}
2014 	else if (!fw)
2015 	{
2016 		EnterSubWindowColormap(ewp->window);
2017 	}
2018 	if (Scr.flags.is_wire_frame_displayed)
2019 	{
2020 ENTER_DBG((stderr, "en: exit: iwfd\n"));
2021 		/* Ignore EnterNotify events while a window is resized or moved
2022 		 * as a wire frame; otherwise the window list may be screwed
2023 		 * up. */
2024 		return;
2025 	}
2026 	if (fw)
2027 	{
2028 		if (ewp->window != FW_W_FRAME(fw) &&
2029 		    ewp->window != FW_W_PARENT(fw) &&
2030 		    ewp->window != FW_W(fw) &&
2031 		    ewp->window != FW_W_ICON_TITLE(fw) &&
2032 		    ewp->window != FW_W_ICON_PIXMAP(fw))
2033 		{
2034 			/* Ignore EnterNotify that received by any of the sub
2035 			 * windows that don't handle this event.  unclutter
2036 			 * triggers these events sometimes, re focusing an
2037 			 * unfocused window under the pointer */
2038 ENTER_DBG((stderr, "en: exit: funny window\n"));
2039 			return;
2040 		}
2041 	}
2042 	if (Scr.focus_in_pending_window != NULL)
2043 	{
2044 ENTER_DBG((stderr, "en: exit: fipw\n"));
2045 		/* Ignore EnterNotify event while we are waiting for a window to
2046 		 * receive focus via Focus or FlipFocus commands. */
2047 		focus_grab_buttons(fw);
2048 		return;
2049 	}
2050 	if (ewp->mode == NotifyGrab)
2051 	{
2052 ENTER_DBG((stderr, "en: exit: NotifyGrab\n"));
2053 		return;
2054 	}
2055 	else if (ewp->mode == NotifyNormal)
2056 	{
2057 ENTER_DBG((stderr, "en: NotifyNormal\n"));
2058 		if (ewp->detail == NotifyNonlinearVirtual &&
2059 		    ewp->focus == False && ewp->subwindow != None)
2060 		{
2061 			/* This takes care of some buggy apps that forget that
2062 			 * one of their dialog subwindows has the focus after
2063 			 * popping up a selection list several times (ddd,
2064 			 * netscape). I'm not convinced that this does not
2065 			 * break something else. */
2066 ENTER_DBG((stderr, "en: NN: refreshing focus\n"));
2067 			refresh_focus(fw);
2068 		}
2069 	}
2070 	else if (ewp->mode == NotifyUngrab)
2071 	{
2072 ENTER_DBG((stderr, "en: NotifyUngrab\n"));
2073 		/* Ignore events generated by grabbing or ungrabbing the
2074 		 * pointer.  However, there is no way to prevent the client
2075 		 * application from handling this event and, for example,
2076 		 * grabbing the focus.  This will interfere with functions that
2077 		 * transferred the focus to a different window. */
2078 		if (is_initial_ungrab_pending)
2079 		{
2080 ENTER_DBG((stderr, "en: NU: initial ungrab pending (lgw = NULL)\n"));
2081 			is_initial_ungrab_pending = False;
2082 			xcrossing_last_grab_window = NULL;
2083 		}
2084 		else
2085 		{
2086 			if (ewp->detail == NotifyNonlinearVirtual &&
2087 			    ewp->focus == False && ewp->subwindow != None)
2088 			{
2089 				/* see comment above */
2090 ENTER_DBG((stderr, "en: NU: refreshing focus\n"));
2091 				refresh_focus(fw);
2092 			}
2093 			if (fw && fw == xcrossing_last_grab_window)
2094 			{
2095 ENTER_DBG((stderr, "en: exit: NU: is last grab window\n"));
2096 				if (ewp->window == FW_W_FRAME(fw) ||
2097 				    ewp->window == FW_W_ICON_TITLE(fw) ||
2098 				    ewp->window == FW_W_ICON_PIXMAP(fw))
2099 				{
2100 ENTER_DBG((stderr, "en: exit: NU: last grab window = NULL\n"));
2101 					xcrossing_last_grab_window = NULL;
2102 				}
2103 				focus_grab_buttons(fw);
2104 
2105 				return;
2106 			}
2107 			else if (fw)
2108 			{
2109 				if (ewp->window != FW_W_FRAME(fw) &&
2110 				    ewp->window != FW_W_ICON_TITLE(fw) &&
2111 				    ewp->window != FW_W_ICON_PIXMAP(fw))
2112 				{
2113 ENTER_DBG((stderr, "en: exit: NU: not frame window\n"));
2114 					focus_grab_buttons(fw);
2115 					return;
2116 				}
2117 			}
2118 		}
2119 	}
2120 	if (fw)
2121 	{
2122 		is_initial_ungrab_pending = False;
2123 	}
2124 
2125 	/* look for a matching leaveNotify which would nullify this EnterNotify
2126 	 */
2127 	/*
2128 	 * RBW - if we're in startup, this is a coerced focus, so we don't
2129 	 * want to save the event time, or exit prematurely.
2130 	 *
2131 	 * Ignore LeaveNotify events for tear out menus - handled by menu code
2132 	 */
2133 	is_tear_off_menu =
2134 		(fw && IS_TEAR_OFF_MENU(fw) && ewp->window == FW_W(fw));
2135 	if (!fFvwmInStartup && !is_tear_off_menu &&
2136 	    FCheckTypedWindowEvent(dpy, ewp->window, LeaveNotify, &d))
2137 	{
2138 		if (d.xcrossing.mode == NotifyNormal &&
2139 		    d.xcrossing.detail != NotifyInferior)
2140 		{
2141 ENTER_DBG((stderr, "en: exit: found LeaveNotify\n"));
2142 			return;
2143 		}
2144 	}
2145 
2146 	if (ewp->window == Scr.Root)
2147 	{
2148 		FvwmWindow *lf = get_last_screen_focus_window();
2149 
2150 		if (!Scr.flags.is_pointer_on_this_screen)
2151 		{
2152 			Scr.flags.is_pointer_on_this_screen = 1;
2153 			if (lf && lf != &Scr.FvwmRoot &&
2154 			    !FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(lf)))
2155 			{
2156 				SetFocusWindow(lf, True, FOCUS_SET_FORCE);
2157 			}
2158 			else if (lf != &Scr.FvwmRoot)
2159 			{
2160 				ForceDeleteFocus();
2161 			}
2162 			else
2163 			{
2164 				/* This was the first EnterNotify event for the
2165 				 * root window - ignore */
2166 			}
2167 			set_last_screen_focus_window(NULL);
2168 		}
2169 		else if (!(sf = get_focus_window()) ||
2170 			 FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2171 		{
2172 			DeleteFocus(True);
2173 		}
2174 		else if (
2175 			Scr.UnknownWinFocused != None && sf != NULL &&
2176 			sf == Scr.StolenFocusFvwmWin)
2177 		{
2178 			__refocus_stolen_focus_win(ea);
2179 		}
2180 		if (Scr.ColormapFocus == COLORMAP_FOLLOWS_MOUSE)
2181 		{
2182 			InstallWindowColormaps(NULL);
2183 		}
2184 		focus_grab_buttons(lf);
2185 		return;
2186 	}
2187 	else
2188 	{
2189 		Scr.flags.is_pointer_on_this_screen = 1;
2190 	}
2191 
2192 	/* An EnterEvent in one of the PanFrameWindows activates the Paging or
2193 	   an EdgeCommand. */
2194 	if (is_pan_frame(ewp->window))
2195 	{
2196 		char *edge_command = NULL;
2197 
2198 		if (
2199 			Scr.UnknownWinFocused != None &&
2200 			(sf = get_focus_window()) != NULL &&
2201 			sf == Scr.StolenFocusFvwmWin)
2202 		{
2203 			__refocus_stolen_focus_win(ea);
2204 		}
2205 		/* check for edge commands */
2206 		if (ewp->window == Scr.PanFrameTop.win)
2207 		{
2208 			edge_command = Scr.PanFrameTop.command;
2209 		}
2210 		else if (ewp->window == Scr.PanFrameBottom.win)
2211 		{
2212 			edge_command = Scr.PanFrameBottom.command;
2213 		}
2214 		else if (ewp->window == Scr.PanFrameLeft.win)
2215 		{
2216 			edge_command = Scr.PanFrameLeft.command;
2217 		}
2218 		else if (ewp->window == Scr.PanFrameRight.win)
2219 		{
2220 			edge_command = Scr.PanFrameRight.command;
2221 		}
2222 		if (edge_command && ewp->mode == NotifyUngrab &&
2223 		    ewp->detail == NotifyAncestor)
2224 		{
2225 			/* nothing */
2226 		}
2227 		else if (edge_command)
2228 		{
2229 			execute_function(NULL, ea->exc, edge_command, 0);
2230 		}
2231 		else
2232 		{
2233 			/* no edge command for this pan frame - so we do
2234 			 * HandlePaging */
2235 			int delta_x = 0;
2236 			int delta_y = 0;
2237 			XEvent e;
2238 
2239 			/* this was in the HandleMotionNotify before, HEDU */
2240 			Scr.flags.is_pointer_on_this_screen = 1;
2241 			e = *te;
2242 			HandlePaging(
2243 				&e, Scr.EdgeScrollX, Scr.EdgeScrollY, &JunkX,
2244 				&JunkY, &delta_x, &delta_y, True, True, False,
2245 				Scr.ScrollDelay);
2246 			return;
2247 		}
2248 	}
2249 	if (!fw)
2250 	{
2251 		return;
2252 	}
2253 	if (IS_EWMH_DESKTOP(FW_W(fw)))
2254 	{
2255 		BroadcastPacket(
2256 			MX_ENTER_WINDOW, 3, (long)Scr.Root, (long)NULL,
2257 			(long)NULL);
2258 		return;
2259 	}
2260 	if (ewp->window == FW_W_FRAME(fw) ||
2261 	    ewp->window == FW_W_ICON_TITLE(fw) ||
2262 	    ewp->window == FW_W_ICON_PIXMAP(fw))
2263 	{
2264 		BroadcastPacket(
2265 			MX_ENTER_WINDOW, 3, (long)FW_W(fw),
2266 			(long)FW_W_FRAME(fw), (unsigned long)fw);
2267 	}
2268 	sf = get_focus_window();
2269 	if (sf && fw != sf && FP_DO_UNFOCUS_LEAVE(FW_FOCUS_POLICY(sf)))
2270 	{
2271 ENTER_DBG((stderr, "en: delete focus\n"));
2272 		DeleteFocus(True);
2273 	}
2274 	focus_grab_buttons(fw);
2275 	if (FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
2276 	{
2277 ENTER_DBG((stderr, "en: set mousey focus\n"));
2278                 if (ewp->window == FW_W(fw))
2279                 {
2280 			/*  Event is for the client window...*/
2281 #ifndef EXPERIMENTAL_ROU_HANDLING_V2
2282 			/*  RBW --  This may still be needed at times, I'm not
2283 			 *sure yet.  */
2284 			SetFocusWindowClientEntered(
2285 				fw, True, FOCUS_SET_BY_ENTER);
2286 #else
2287 			SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2288 #endif
2289                 }
2290                 else
2291                 {
2292 			/*  Event is for the frame...*/
2293 			SetFocusWindow(fw, True, FOCUS_SET_BY_ENTER);
2294                 }
2295 	}
2296 	else if (focus_is_focused(fw) && focus_does_accept_input_focus(fw))
2297 	{
2298 		/* We have to refresh the focus window here in case we left the
2299 		 * focused fvwm window.  Motif apps may lose the input focus
2300 		 * otherwise.  But do not try to refresh the focus of
2301 		 * applications that want to handle it themselves. */
2302 		focus_force_refresh_focus(fw);
2303 	}
2304 	else if (sf != fw)
2305 	{
2306 		/* Give the window a chance to grab the buttons needed for
2307 		 * raise-on-click */
2308 		focus_grab_buttons(sf);
2309 	}
2310 	if (
2311 		Scr.UnknownWinFocused != None && sf != NULL &&
2312 		sf == Scr.StolenFocusFvwmWin)
2313 	{
2314 			__refocus_stolen_focus_win(ea);
2315 	}
2316 	/* We get an EnterNotify with mode == UnGrab when fvwm releases the
2317 	 * grab held during iconification. We have to ignore this, or icon
2318 	 * title will be initially raised. */
2319 	if (IS_ICONIFIED(fw) && (ewp->mode == NotifyNormal) &&
2320 	    (ewp->window == FW_W_ICON_PIXMAP(fw) ||
2321 	     ewp->window == FW_W_ICON_TITLE(fw)) &&
2322 	    FW_W_ICON_PIXMAP(fw) != None)
2323 	{
2324 		SET_ICON_ENTERED(fw, 1);
2325 		DrawIconWindow(fw, True, False, False, False, NULL);
2326 	}
2327 	/* Check for tear off menus */
2328 	if (is_tear_off_menu)
2329 	{
2330 		menu_enter_tear_off_menu(ea->exc);
2331 	}
2332 
2333 	return;
2334 }
2335 
HandleExpose(const evh_args_t * ea)2336 void HandleExpose(const evh_args_t *ea)
2337 {
2338 	XEvent e;
2339 	FvwmWindow * const fw = ea->exc->w.fw;
2340 
2341 	e = *ea->exc->x.etrigger;
2342 #if 0
2343 	/* This doesn't work well. Sometimes, the expose count is zero although
2344 	 * dozens of expose events are pending.  This happens all the time
2345 	 * during a shading animation.  Simply flush expose events
2346 	 * unconditionally. */
2347 	if (e.xexpose.count != 0)
2348 	{
2349 		flush_accumulate_expose(e.xexpose.window, &e);
2350 	}
2351 #else
2352 	flush_accumulate_expose(e.xexpose.window, &e);
2353 #endif
2354 	if (fw == NULL)
2355 	{
2356 		return;
2357 	}
2358 	if (e.xany.window == FW_W_ICON_TITLE(fw) ||
2359 	    e.xany.window == FW_W_ICON_PIXMAP(fw))
2360 	{
2361 		DrawIconWindow(fw, True, True, False, False, &e);
2362 		return;
2363 	}
2364 	else if (IS_TEAR_OFF_MENU(fw) && e.xany.window == FW_W(fw))
2365 	{
2366 		/* refresh the contents of the torn out menu */
2367 		menu_expose(&e, NULL);
2368 	}
2369 
2370 	return;
2371 }
2372 
HandleFocusIn(const evh_args_t * ea)2373 void HandleFocusIn(const evh_args_t *ea)
2374 {
2375 	XEvent d;
2376 	Window w = None;
2377 	Window focus_w = None;
2378 	Window focus_fw = None;
2379 	Pixel fc = 0;
2380 	Pixel bc = 0;
2381 	FvwmWindow *ffw_old = get_focus_window();
2382 	FvwmWindow *sf;
2383 	Bool do_force_broadcast = False;
2384 	Bool is_unmanaged_focused = False;
2385 	static Window last_focus_w = None;
2386 	static Window last_focus_fw = None;
2387 	static Bool was_nothing_ever_focused = True;
2388 	FvwmWindow *fw = ea->exc->w.fw;
2389 
2390 	DBUG("HandleFocusIn", "Routine Entered");
2391 
2392 	Scr.focus_in_pending_window = NULL;
2393 	/* This is a hack to make the PointerKey command work */
2394 	if (ea->exc->x.etrigger->xfocus.detail != NotifyPointer)
2395 	{
2396 		/**/
2397 		w = ea->exc->x.etrigger->xany.window;
2398 	}
2399 	while (FCheckTypedEvent(dpy, FocusIn, &d))
2400 	{
2401 		/* dito */
2402 		if (d.xfocus.detail != NotifyPointer)
2403 		{
2404 			/**/
2405 			w = d.xany.window;
2406 		}
2407 	}
2408 	/* dito */
2409 	if (w == None)
2410 	{
2411 		return;
2412 	}
2413 	/**/
2414 	if (XFindContext(dpy, w, FvwmContext, (caddr_t *) &fw) == XCNOENT)
2415 	{
2416 		fw = NULL;
2417 	}
2418 
2419 	Scr.UnknownWinFocused = None;
2420 	if (
2421 		fw && Scr.focus_in_requested_window != fw &&
2422 		!FP_DO_FOCUS_BY_PROGRAM(FW_FOCUS_POLICY(fw)) &&
2423 		fw->focus_model == FM_GLOBALLY_ACTIVE)
2424 	{
2425 		if (DEBUG_GLOBALLY_ACTIVE)
2426 		{
2427 			fprintf(
2428 				stderr, "prevented globally active fw %p (%s)"
2429 				" from stealing the focus\n", fw,
2430 				fw->name.name);
2431 			fprintf(
2432 				stderr, "window was %p (%s)\n",
2433 				Scr.focus_in_pending_window,
2434 				Scr.focus_in_pending_window ?
2435 				Scr.focus_in_pending_window->name.name : "");
2436 		}
2437 		Scr.focus_in_requested_window = NULL;
2438 		__refocus_stolen_focus_win(ea);
2439 
2440 		return;
2441 	}
2442 	Scr.focus_in_pending_window = NULL;
2443 	if (!fw)
2444 	{
2445 		if (w != Scr.NoFocusWin)
2446 		{
2447 			Scr.UnknownWinFocused = w;
2448 			Scr.StolenFocusWin =
2449 				(ffw_old != NULL) ? FW_W(ffw_old) : None;
2450 			Scr.StolenFocusFvwmWin = ffw_old;
2451 			focus_w = w;
2452 			is_unmanaged_focused = True;
2453 		}
2454 		/* Only show a non-focused window as focused,
2455 		 * if the focus is on unmanaged and flickering qt dialogs
2456 		 * workaround is on. */
2457 		if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2458 		    !is_unmanaged_focused)
2459 		{
2460 			border_draw_decorations(
2461 				Scr.Hilite, PART_ALL, False, True, CLEAR_ALL,
2462 				NULL, NULL);
2463 			if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2464 			{
2465 				if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2466 				{
2467 					InstallWindowColormaps(Scr.Hilite);
2468 				}
2469 				else
2470 				{
2471 					InstallWindowColormaps(NULL);
2472 				}
2473 			}
2474 		}
2475 		/* Not very useful if no window that fvwm and its modules know
2476 		 * about has the focus. */
2477 		fc = GetColor(DEFAULT_FORE_COLOR);
2478 		bc = GetColor(DEFAULT_BACK_COLOR);
2479 	}
2480 	else if (fw != Scr.Hilite ||
2481 		 /* domivogt (16-May-2000): This check is necessary to force
2482 		  * sending a M_FOCUS_CHANGE packet after an unmanaged window
2483 		  * was focused. Otherwise fvwm would believe that Scr.Hilite
2484 		  * was still focused and not send any info to the modules. */
2485 		 last_focus_fw == None ||
2486 		 IS_FOCUS_CHANGE_BROADCAST_PENDING(fw) ||
2487 		 fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
2488 	{
2489 		if (w != Scr.NoFocusWin)
2490 		{
2491 			Scr.StolenFocusWin = (ffw_old != NULL) ?
2492 				FW_W(ffw_old) : None;
2493 			Scr.StolenFocusFvwmWin = ffw_old;
2494 		}
2495 		do_force_broadcast = IS_FOCUS_CHANGE_BROADCAST_PENDING(fw);
2496 		SET_FOCUS_CHANGE_BROADCAST_PENDING(fw, 0);
2497 		if (fw != Scr.Hilite &&
2498 			fpol_query_allow_user_focus(&FW_FOCUS_POLICY(fw)))
2499 		{
2500 			border_draw_decorations(
2501 				fw, PART_ALL, True, True, CLEAR_ALL, NULL,
2502 				NULL);
2503 		}
2504 		focus_w = FW_W(fw);
2505 		focus_fw = FW_W_FRAME(fw);
2506 		fc = fw->hicolors.fore;
2507 		bc = fw->hicolors.back;
2508 		set_focus_window(fw);
2509 		if (Scr.ColormapFocus == COLORMAP_FOLLOWS_FOCUS)
2510 		{
2511 			if ((Scr.Hilite)&&(!IS_ICONIFIED(Scr.Hilite)))
2512 			{
2513 				InstallWindowColormaps(Scr.Hilite);
2514 			}
2515 			else
2516 			{
2517 				InstallWindowColormaps(NULL);
2518 			}
2519 		}
2520 	}
2521 	else
2522 	{
2523 		return;
2524 	}
2525 	if (was_nothing_ever_focused || last_focus_fw == None ||
2526 	    focus_w != last_focus_w || focus_fw != last_focus_fw ||
2527 	    do_force_broadcast)
2528 	{
2529 		if (!Scr.bo.do_enable_flickering_qt_dialogs_workaround ||
2530 		    !is_unmanaged_focused)
2531 		{
2532 			BroadcastPacket(
2533 				M_FOCUS_CHANGE, 5, (long)focus_w,
2534 				(long)focus_fw,
2535 				(unsigned long)IsLastFocusSetByMouse(),
2536 				(long)fc, (long)bc);
2537 			EWMH_SetActiveWindow(focus_w);
2538 		}
2539 		last_focus_w = focus_w;
2540 		last_focus_fw = focus_fw;
2541 		was_nothing_ever_focused = False;
2542 	}
2543 	if ((sf = get_focus_window()) != ffw_old)
2544 	{
2545 		focus_grab_buttons(sf);
2546 		focus_grab_buttons(ffw_old);
2547 	}
2548 
2549 	return;
2550 }
2551 
HandleFocusOut(const evh_args_t * ea)2552 void HandleFocusOut(const evh_args_t *ea)
2553 {
2554 	if (Scr.UnknownWinFocused != None && Scr.StolenFocusWin != None &&
2555 	    ea->exc->x.etrigger->xfocus.window == Scr.UnknownWinFocused)
2556 	{
2557 		__refocus_stolen_focus_win(ea);
2558 	}
2559 
2560 	return;
2561 }
2562 
__handle_key(const evh_args_t * ea,Bool is_press)2563 void __handle_key(const evh_args_t *ea, Bool is_press)
2564 {
2565 	char *action;
2566 	FvwmWindow *sf;
2567 	KeyCode kc;
2568 	int kcontext;
2569 	const XEvent *te = ea->exc->x.etrigger;
2570 	const FvwmWindow * const fw = ea->exc->w.fw;
2571 	Bool is_second_binding;
2572 	const XClassHint *winClass1, *winClass2;
2573 	XClassHint tmp;
2574 	char *name1, *name2;
2575 	const exec_context_t *exc;
2576 	exec_context_changes_t ecc;
2577 
2578 	PressedW = None;
2579 
2580 	/* Here's a real hack - some systems have two keys with the
2581 	 * same keysym and different keycodes. This converts all
2582 	 * the cases to one keycode. */
2583 	kc = XKeysymToKeycode(dpy, fvwm_KeycodeToKeysym(dpy, te->xkey.keycode, 0, 0));
2584 
2585 	/* Check if there is something bound to the key */
2586 
2587 	sf = get_focus_window();
2588 	if (sf == NULL)
2589 	{
2590 		tmp.res_name = tmp.res_class = name1 = "root";
2591 		winClass1 = &tmp;
2592 		kcontext = C_ROOT;
2593 	}
2594 	else
2595 	{
2596 		winClass1 = &sf->class;
2597 		name1 = sf->name.name;
2598 		kcontext = (sf == fw ? ea->exc->w.wcontext : C_WINDOW);
2599 	}
2600 
2601 	if (fw == NULL)
2602 	{
2603 		tmp.res_name = tmp.res_class = name2 = "root";
2604 		winClass2 = &tmp;
2605 	}
2606 	else
2607 	{
2608 		winClass2 = &fw->class;
2609 		name2 = fw->name.name;
2610 	}
2611 	/* Searching the binding list with a different 'type' value
2612 	 * (ie. BIND_KEYPRESS vs BIND_PKEYPRESS) doesn't make a difference.
2613 	 * The different context value does though. */
2614 	action = CheckTwoBindings(
2615 		&is_second_binding, Scr.AllBindings, STROKE_ARG(0) kc,
2616 		te->xkey.state, GetUnusedModifiers(), kcontext, BIND_KEYPRESS,
2617 		winClass1, name1, ea->exc->w.wcontext, BIND_PKEYPRESS,
2618 		winClass2, name2);
2619 
2620 	if (action != NULL)
2621 	{
2622 		if (!is_press)
2623 		{
2624 			XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2625 			return;
2626 		}
2627 		exc = ea->exc;
2628 		if (is_second_binding == False)
2629 		{
2630 			ecc.w.fw = sf;
2631 			ecc.w.wcontext = kcontext;
2632 			exc = exc_clone_context(
2633 				ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
2634 		}
2635 		execute_function(NULL, exc, action, 0);
2636 		if (is_second_binding == False)
2637 		{
2638 			exc_destroy_context(exc);
2639 		}
2640 		XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2641 		return;
2642 	}
2643 
2644 	/* if we get here, no function key was bound to the key.  Send it
2645 	 * to the client if it was in a window we know about. */
2646 	sf = get_focus_window();
2647 	if (sf && te->xkey.window != FW_W(sf))
2648 	{
2649 		XEvent e;
2650 
2651 		e = *te;
2652 		e.xkey.window = FW_W(sf);
2653 		FSendEvent(
2654 			dpy, e.xkey.window, False,
2655 			(is_press)? KeyPressMask:KeyReleaseMask, &e);
2656 	}
2657 	else if (fw && te->xkey.window != FW_W(fw))
2658 	{
2659 		XEvent e;
2660 
2661 		e = *te;
2662 		e.xkey.window = FW_W(fw);
2663 		FSendEvent(
2664 			dpy, e.xkey.window, False,
2665 			(is_press)? KeyPressMask:KeyReleaseMask, &e);
2666 	}
2667 	XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
2668 
2669 	return;
2670 }
2671 
HandleKeyPress(const evh_args_t * ea)2672 void HandleKeyPress(const evh_args_t *ea)
2673 {
2674 	__handle_key(ea, True);
2675 }
2676 
HandleKeyRelease(const evh_args_t * ea)2677 void HandleKeyRelease(const evh_args_t *ea)
2678 {
2679 	__handle_key(ea, False);
2680 }
2681 
HandleLeaveNotify(const evh_args_t * ea)2682 void HandleLeaveNotify(const evh_args_t *ea)
2683 {
2684 	const XLeaveWindowEvent *lwp;
2685 	const XEvent *te = ea->exc->x.etrigger;
2686 	FvwmWindow * const fw = ea->exc->w.fw;
2687 
2688 	DBUG("HandleLeaveNotify", "Routine Entered");
2689 
2690 ENTER_DBG((stderr, "-------- ln (%d): fw %p w 0x%08x sw 0x%08x mode 0x%x detail 0x%x '%s'\n", ++ecount, fw, (int)te->xcrossing.window, (int)te->xcrossing.subwindow, te->xcrossing.mode, te->xcrossing.detail, fw?fw->visible_name:"(none)"));
2691 	lwp = &te->xcrossing;
2692 	if (
2693 		lwp->window == Scr.Root &&
2694 		lwp->detail == NotifyInferior && lwp->mode == NotifyNormal)
2695 	{
2696 		/* pointer entered subwindow */
2697 		BroadcastPacket(
2698 			MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2699 			(long)NULL);
2700 	}
2701 	else if (
2702 		lwp->window == Scr.Root &&
2703 		lwp->detail == NotifyNonlinearVirtual)
2704 	{
2705 		/* pointer left screen */
2706 		BroadcastPacket(
2707 			MX_LEAVE_WINDOW, 3, (long)Scr.Root, (long)NULL,
2708 			(long)NULL);
2709 	}
2710 	/* Ignore LeaveNotify events while a window is resized or moved as a
2711 	 * wire frame; otherwise the window list may be screwed up. */
2712 	if (Scr.flags.is_wire_frame_displayed)
2713 	{
2714 		return;
2715 	}
2716 	if (lwp->mode != NotifyNormal)
2717 	{
2718 		/* Ignore events generated by grabbing or ungrabbing the
2719 		 * pointer.  However, there is no way to prevent the client
2720 		 * application from handling this event and, for example,
2721 		 * grabbing the focus.  This will interfere with functions that
2722 		 * transferred the focus to a different window.  It is
2723 		 * necessary to check for LeaveNotify events on the client
2724 		 * window too in case buttons are not grabbed on it. */
2725 		if (lwp->mode == NotifyGrab && fw &&
2726 		    (lwp->window == FW_W_FRAME(fw) ||
2727 		     lwp->window == FW_W(fw) ||
2728 		     lwp->window == FW_W_ICON_TITLE(fw) ||
2729 		     lwp->window == FW_W_ICON_PIXMAP(fw)))
2730 		{
2731 ENTER_DBG((stderr, "ln: *** lgw = %p\n", fw));
2732 			xcrossing_last_grab_window = fw;
2733 		}
2734 #ifdef FOCUS_EXPANDS_TITLE
2735 		if (fw && IS_ICONIFIED(fw))
2736 		{
2737 			SET_ICON_ENTERED(fw, 0);
2738 			DrawIconWindow(
2739 				fw, True, False, False, False, NULL);
2740 		}
2741 #endif
2742 		return;
2743 	}
2744 	/* CDE-like behaviour of raising the icon title if the icon
2745 	   gets the focus (in particular if the cursor is over the icon) */
2746 	if (fw && IS_ICONIFIED(fw))
2747 	{
2748 		SET_ICON_ENTERED(fw,0);
2749 		DrawIconWindow(fw, True, False, False, False, NULL);
2750 	}
2751 
2752 	/* An LeaveEvent in one of the PanFrameWindows activates
2753 	   an EdgeLeaveCommand. */
2754 	if (is_pan_frame(lwp->window))
2755 	{
2756 		char *edge_command_leave = NULL;
2757 
2758 		/* check for edge commands */
2759 		if (lwp->window == Scr.PanFrameTop.win)
2760 		{
2761 			edge_command_leave = Scr.PanFrameTop.command_leave;
2762 		}
2763 		else if (lwp->window == Scr.PanFrameBottom.win)
2764 		{
2765 			edge_command_leave = Scr.PanFrameBottom.command_leave;
2766 		}
2767 		else if (lwp->window == Scr.PanFrameLeft.win)
2768 		{
2769 			edge_command_leave = Scr.PanFrameLeft.command_leave;
2770 		}
2771 		else if (lwp->window == Scr.PanFrameRight.win)
2772 		{
2773 			edge_command_leave = Scr.PanFrameRight.command_leave;
2774 		}
2775 		if (edge_command_leave && lwp->mode == NotifyUngrab &&
2776 		    lwp->detail == NotifyAncestor)
2777 		{
2778 			/* nothing */
2779 		}
2780 		else if (edge_command_leave)
2781 		{
2782 			execute_function(NULL, ea->exc, edge_command_leave, 0);
2783 		}
2784 	}
2785 
2786 
2787 	/* If we leave the root window, then we're really moving
2788 	 * another screen on a multiple screen display, and we
2789 	 * need to de-focus and unhighlight to make sure that we
2790 	 * don't end up with more than one highlighted window at a time */
2791 	if (lwp->window == Scr.Root &&
2792 	   /* domivogt (16-May-2000): added this test because somehow fvwm
2793 	    * sometimes gets a LeaveNotify on the root window although it is
2794 	    * single screen. */
2795 	    Scr.NumberOfScreens > 1)
2796 	{
2797 		if (lwp->mode == NotifyNormal)
2798 		{
2799 			if (lwp->detail != NotifyInferior)
2800 			{
2801 				FvwmWindow *sf = get_focus_window();
2802 
2803 				Scr.flags.is_pointer_on_this_screen = 0;
2804 				set_last_screen_focus_window(sf);
2805 				if (sf != NULL)
2806 				{
2807 					DeleteFocus(True);
2808 				}
2809 				if (Scr.Hilite != NULL)
2810 				{
2811 					border_draw_decorations(
2812 						Scr.Hilite, PART_ALL, False,
2813 						True, CLEAR_ALL, NULL, NULL);
2814 				}
2815 			}
2816 		}
2817 	}
2818 	else
2819 	{
2820 		/* handle a subwindow cmap */
2821 		LeaveSubWindowColormap(te->xany.window);
2822 	}
2823 	if (fw != NULL &&
2824 	    (lwp->window == FW_W_FRAME(fw) ||
2825 	     lwp->window == FW_W_ICON_TITLE(fw) ||
2826 	     lwp->window == FW_W_ICON_PIXMAP(fw)))
2827 	{
2828 		BroadcastPacket(
2829 			MX_LEAVE_WINDOW, 3, (long)FW_W(fw),
2830 			(long)FW_W_FRAME(fw), (unsigned long)fw);
2831 	}
2832 
2833 	return;
2834 }
2835 
HandleMapNotify(const evh_args_t * ea)2836 void HandleMapNotify(const evh_args_t *ea)
2837 {
2838 	Bool is_on_this_page = False;
2839 	const XEvent *te = ea->exc->x.etrigger;
2840 	FvwmWindow * const fw = ea->exc->w.fw;
2841 
2842 	DBUG("HandleMapNotify", "Routine Entered");
2843 
2844 	if (!fw)
2845 	{
2846 		if (te->xmap.override_redirect == True &&
2847 		    te->xmap.window != Scr.NoFocusWin)
2848 		{
2849 			XSelectInput(dpy, te->xmap.window, XEVMASK_ORW);
2850 			XFlush(dpy);
2851 			Scr.UnknownWinFocused = te->xmap.window;
2852 		}
2853 		return;
2854 	}
2855 	if (te->xmap.window == FW_W_FRAME(fw))
2856 	{
2857 		/* Now that we know the frame is mapped after capturing the
2858 		 * window we do not need StructureNotifyMask events anymore. */
2859 		XSelectInput(dpy, FW_W_FRAME(fw), XEVMASK_FRAMEW);
2860 		XFlush(dpy);
2861 	}
2862 	/* Except for identifying over-ride redirect window mappings, we
2863 	 * don't need or want windows associated with the
2864 	 * SubstructureNotifyMask */
2865 	if (te->xmap.event != te->xmap.window)
2866 	{
2867 		return;
2868 	}
2869 	SET_MAP_PENDING(fw, 0);
2870 	/* don't map if the event was caused by a de-iconify */
2871 	if (IS_ICONIFY_PENDING(fw))
2872 	{
2873 		return;
2874 	}
2875 
2876 	/* Make sure at least part of window is on this page before giving it
2877 	 * focus... */
2878 	is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
2879 
2880 	/*
2881 	 * Need to do the grab to avoid race condition of having server send
2882 	 * MapNotify to client before the frame gets mapped; this is bad because
2883 	 * the client would think that the window has a chance of being viewable
2884 	 * when it really isn't.
2885 	 */
2886 	MyXGrabServer (dpy);
2887 	if (FW_W_ICON_TITLE(fw))
2888 	{
2889 		XUnmapWindow(dpy, FW_W_ICON_TITLE(fw));
2890 	}
2891 	if (FW_W_ICON_PIXMAP(fw) != None)
2892 	{
2893 		XUnmapWindow(dpy, FW_W_ICON_PIXMAP(fw));
2894 	}
2895 	XMapSubwindows(dpy, FW_W_FRAME(fw));
2896 	if (fw->Desk == Scr.CurrentDesk)
2897 	{
2898 		XMapWindow(dpy, FW_W_FRAME(fw));
2899 	}
2900 	if (IS_ICONIFIED(fw))
2901 	{
2902 		BroadcastPacket(
2903 			M_DEICONIFY, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2904 			(unsigned long)fw);
2905 	}
2906 	else
2907 	{
2908 		BroadcastPacket(
2909 			M_MAP, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
2910 			(unsigned long)fw);
2911 	}
2912 
2913 	if (is_on_this_page &&
2914 	    focus_query_open_grab_focus(fw, get_focus_window()) == True)
2915 	{
2916 		SetFocusWindow(fw, True, FOCUS_SET_FORCE);
2917 	}
2918 	border_draw_decorations(
2919 		fw, PART_ALL, (fw == get_focus_window()) ? True : False, True,
2920 		CLEAR_ALL, NULL, NULL);
2921 	MyXUngrabServer (dpy);
2922 	SET_MAPPED(fw, 1);
2923 	SET_ICONIFIED(fw, 0);
2924 	SET_ICON_UNMAPPED(fw, 0);
2925 	if (DO_ICONIFY_AFTER_MAP(fw))
2926 	{
2927 		initial_window_options_t win_opts;
2928 
2929 		/* finally, if iconification was requested before the window
2930 		 * was mapped, request it now. */
2931 		memset(&win_opts, 0, sizeof(win_opts));
2932 		Iconify(fw, &win_opts);
2933 		SET_ICONIFY_AFTER_MAP(fw, 0);
2934 	}
2935 	focus_grab_buttons_on_layer(fw->layer);
2936 
2937 	return;
2938 }
2939 
HandleMappingNotify(const evh_args_t * ea)2940 void HandleMappingNotify(const evh_args_t *ea)
2941 {
2942 	XRefreshKeyboardMapping(&ea->exc->x.etrigger->xmapping);
2943 
2944 	return;
2945 }
2946 
HandleMapRequest(const evh_args_t * ea)2947 void HandleMapRequest(const evh_args_t *ea)
2948 {
2949 	DBUG("HandleMapRequest", "Routine Entered");
2950 
2951 	if (fFvwmInStartup)
2952 	{
2953 		/* Just map the damn thing, decorations are added later
2954 		 * in CaptureAllWindows. */
2955 		XMapWindow(dpy, ea->exc->x.etrigger->xmaprequest.window);
2956 		return;
2957 	}
2958 	HandleMapRequestKeepRaised(ea, None, NULL, NULL);
2959 
2960 	return;
2961 }
2962 
HandleMapRequestKeepRaised(const evh_args_t * ea,Window KeepRaised,FvwmWindow * ReuseWin,initial_window_options_t * win_opts)2963 void HandleMapRequestKeepRaised(
2964 	const evh_args_t *ea, Window KeepRaised, FvwmWindow *ReuseWin,
2965 	initial_window_options_t *win_opts)
2966 {
2967 	Bool is_on_this_page = False;
2968 	Bool is_new_window = False;
2969 	FvwmWindow *tmp;
2970 	FvwmWindow *sf;
2971 	initial_window_options_t win_opts_bak;
2972 	Window ew;
2973 	FvwmWindow *fw;
2974 	extern Bool Restarting;
2975 	const char *initial_map_command;
2976 
2977 	initial_map_command = NULL;
2978 	if (win_opts == NULL)
2979 	{
2980 		memset(&win_opts_bak, 0, sizeof(win_opts_bak));
2981 		win_opts = &win_opts_bak;
2982 	}
2983 	ew = ea->exc->w.w;
2984 	if (ReuseWin == NULL)
2985 	{
2986 		if (XFindContext(dpy, ew, FvwmContext, (caddr_t *)&fw) ==
2987 		    XCNOENT)
2988 		{
2989 			fw = NULL;
2990 		}
2991 		if (fw != NULL && IS_MAP_PENDING(fw))
2992 		{
2993 			/* The window is already going to be mapped, no need to
2994 			 * do that twice */
2995 			return;
2996 		}
2997 	}
2998 	else
2999 	{
3000 		fw = ReuseWin;
3001 	}
3002 
3003 	if (fw == NULL && EWMH_IsKdeSysTrayWindow(ew))
3004 	{
3005 		/* This means that the window is swallowed by kicker and that
3006 		 * kicker restart or exit. As we should assume that kicker
3007 		 * restart we should return here, if not we go into trouble
3008 		 * ... */
3009 		return;
3010 	}
3011 	if (!win_opts->flags.do_override_ppos)
3012 	{
3013 		XFlush(dpy);
3014 	}
3015 
3016 	/* If the window has never been mapped before ... */
3017 	if (!fw || (fw && DO_REUSE_DESTROYED(fw)))
3018 	{
3019 		/* Add decorations. */
3020 		fw = AddWindow(
3021 			&initial_map_command, ea->exc, ReuseWin, win_opts);
3022 		if (fw == AW_NO_WINDOW)
3023 		{
3024 			return;
3025 		}
3026 		else if (fw == AW_UNMANAGED)
3027 		{
3028 			XMapWindow(dpy, ew);
3029 			return;
3030 		}
3031 		is_new_window = True;
3032 	}
3033 	/*
3034 	 * Make sure at least part of window is on this page
3035 	 * before giving it focus...
3036 	 */
3037 	is_on_this_page = IsRectangleOnThisPage(&(fw->g.frame), fw->Desk);
3038 	if (KeepRaised != None)
3039 	{
3040 		XRaiseWindow(dpy, KeepRaised);
3041 	}
3042 	/* If it's not merely iconified, and we have hints, use them. */
3043 
3044 	if (IS_ICONIFIED(fw))
3045 	{
3046 		/* If no hints, or currently an icon, just "deiconify" */
3047 		DeIconify(fw);
3048 	}
3049 	else if (IS_MAPPED(fw))
3050 	{
3051 		/* the window is already mapped - fake a MapNotify event */
3052 		fake_map_unmap_notify(fw, MapNotify);
3053 	}
3054 	else
3055 	{
3056 		int state;
3057 
3058 		if (fw->wmhints && (fw->wmhints->flags & StateHint))
3059 		{
3060 			state = fw->wmhints->initial_state;
3061 		}
3062 		else
3063 		{
3064 			state = NormalState;
3065 		}
3066 		if (win_opts->initial_state != DontCareState)
3067 		{
3068 			state = win_opts->initial_state;
3069 		}
3070 
3071 		switch (state)
3072 		{
3073 		case DontCareState:
3074 		case NormalState:
3075 		case InactiveState:
3076 		default:
3077 			MyXGrabServer(dpy);
3078 			if (fw->Desk == Scr.CurrentDesk)
3079 			{
3080 				Bool do_grab_focus;
3081 
3082 				SET_MAP_PENDING(fw, 1);
3083 				XMapWindow(dpy, FW_W_FRAME(fw));
3084 				XMapWindow(dpy, FW_W(fw));
3085 				SetMapStateProp(fw, NormalState);
3086 				if (Scr.flags.is_map_desk_in_progress)
3087 				{
3088 					do_grab_focus = False;
3089 				}
3090 				else if (!is_on_this_page)
3091 				{
3092 					do_grab_focus = False;
3093 				}
3094 				else if (focus_query_open_grab_focus(
3095 						 fw, get_focus_window()) ==
3096 					 True)
3097 				{
3098 					do_grab_focus = True;
3099 				}
3100 				else
3101 				{
3102 					do_grab_focus = False;
3103 				}
3104 				if (do_grab_focus)
3105 				{
3106 					SetFocusWindow(
3107 						fw, True, FOCUS_SET_FORCE);
3108 				}
3109 				else
3110 				{
3111 					/* make sure the old focused window
3112 					 * still has grabbed all necessary
3113 					 * buttons. */
3114 					focus_grab_buttons(
3115 						get_focus_window());
3116 				}
3117 			}
3118 			else
3119 			{
3120 #ifndef ICCCM2_UNMAP_WINDOW_PATCH
3121 				/* nope, this is forbidden by the ICCCM2 */
3122 				XMapWindow(dpy, FW_W(fw));
3123 				SetMapStateProp(fw, NormalState);
3124 #else
3125 				/* Since we will not get a MapNotify, set the
3126 				 * IS_MAPPED flag manually. */
3127 				SET_MAPPED(fw, 1);
3128 				SetMapStateProp(fw, IconicState);
3129 				/* fake that the window was mapped to allow
3130 				 * modules to swallow it */
3131 				BroadcastPacket(
3132 					M_MAP, 3, (long)FW_W(fw),
3133 					(long)FW_W_FRAME(fw),
3134 					(unsigned long)fw);
3135 #endif
3136 			}
3137 			/* TA:  20090125:  We *have* to handle
3138 			 * InitialMapCommand here and not in AddWindow() to
3139 			 * allow for correct timings when the window is truly
3140 			 * mapped. (c.f. things like Iconify.)
3141 			 */
3142 
3143 			/* TA:  20091212:  But only do this when we're *not*
3144 			 * restarting -- the window is still mapped, but gets
3145 			 * recaptured -- we don't want to trigger this event
3146 			 * again.  Otherwise we end up toggling the state of
3147 			 * the window in situations where the
3148 			 * InitialMapCommand is Iconify or Maximize, for
3149 			 * instance.
3150 			 */
3151 			if ((initial_map_command != NULL) &&
3152 			   (!Restarting && Scr.flags.are_windows_captured))
3153 			{
3154 				execute_function_override_window(
3155 					NULL, ea->exc,
3156 					(char *)initial_map_command, 0, fw);
3157 			}
3158 			MyXUngrabServer(dpy);
3159 			break;
3160 
3161 		case IconicState:
3162 			if (is_new_window)
3163 			{
3164 				/* the window will not be mapped - fake a
3165 				 * MapNotify and an UnmapNotify event.  Can't
3166 				 * remember exactly why this is necessary, but
3167 				 * probably something w/ (de)iconify state
3168 				 * confusion. */
3169 				fake_map_unmap_notify(fw, MapNotify);
3170 				fake_map_unmap_notify(fw, UnmapNotify);
3171 			}
3172 			if (win_opts->flags.is_iconified_by_parent ||
3173 			    ((tmp = get_transientfor_fvwmwindow(fw)) &&
3174 			     IS_ICONIFIED(tmp)))
3175 			{
3176 				win_opts->flags.is_iconified_by_parent = 0;
3177 				SET_ICONIFIED_BY_PARENT(fw, 1);
3178 			}
3179 			if (USE_ICON_POSITION_HINT(fw) && fw->wmhints &&
3180 			    (fw->wmhints->flags & IconPositionHint))
3181 			{
3182 				win_opts->default_icon_x = fw->wmhints->icon_x;
3183 				win_opts->default_icon_y = fw->wmhints->icon_y;
3184 			}
3185 			Iconify(fw, win_opts);
3186 			break;
3187 		}
3188 	}
3189 	if (IS_SHADED(fw))
3190 	{
3191 		BroadcastPacket(
3192 			M_WINDOWSHADE, 3, (long)FW_W(fw), (long)FW_W_FRAME(fw),
3193 			(unsigned long)fw);
3194 	}
3195 	/* If the newly mapped window overlaps the focused window, make sure
3196 	 * ClickToFocusRaises and MouseFocusClickRaises work again. */
3197 	sf = get_focus_window();
3198 	if (sf != NULL)
3199 	{
3200 		focus_grab_buttons(sf);
3201 	}
3202 	if (win_opts->flags.is_menu)
3203 	{
3204 		SET_MAPPED(fw, 1);
3205 		SET_MAP_PENDING(fw, 0);
3206 	}
3207 	EWMH_SetClientList();
3208 	EWMH_SetClientListStacking();
3209 
3210 	return;
3211 }
3212 
3213 #ifdef HAVE_STROKE
HandleMotionNotify(const evh_args_t * ea)3214 void HandleMotionNotify(const evh_args_t *ea)
3215 {
3216 	DBUG("HandleMotionNotify", "Routine Entered");
3217 
3218 	if (send_motion == True)
3219 	{
3220 		stroke_record(
3221 			ea->exc->x.etrigger->xmotion.x,
3222 			ea->exc->x.etrigger->xmotion.y);
3223 	}
3224 
3225 	return;
3226 }
3227 #endif /* HAVE_STROKE */
3228 
HandlePropertyNotify(const evh_args_t * ea)3229 void HandlePropertyNotify(const evh_args_t *ea)
3230 {
3231 	Bool has_icon_changed = False;
3232 	Bool has_icon_pixmap_hint_changed = False;
3233 	Bool has_icon_window_hint_changed = False;
3234         /* NoName is an extern pointer to a constant "Untitled".
3235            See lib/Flocale.c FlocaleFreeNameProperty
3236            to see how this initialization causes a problem: */
3237 	FlocaleNameString new_name = { NoName, NULL };
3238 	int old_wmhints_flags;
3239 	const XEvent *te = ea->exc->x.etrigger;
3240 	char *urgency_action = NULL;
3241 	FvwmWindow * const fw = ea->exc->w.fw;
3242 
3243 	DBUG("HandlePropertyNotify", "Routine Entered");
3244 
3245 	if (te->xproperty.window == Scr.Root &&
3246 	    te->xproperty.state == PropertyNewValue &&
3247 	    (te->xproperty.atom == _XA_XSETROOT_ID ||
3248 	     te->xproperty.atom == _XA_XROOTPMAP_ID))
3249 	{
3250 		/* background change */
3251 		/* _XA_XSETROOT_ID is used by fvwm-root, xli and more (xv sends
3252 		 * no property  notify?).  _XA_XROOTPMAP_ID is used by Esetroot
3253 		 * compatible program: the problem here is that with some
3254 		 * Esetroot compatible program we get the message _before_ the
3255 		 * background change. This is fixed with Esetroot 9.2 (not yet
3256 		 * released, 2002-01-14) */
3257 
3258 		/* update icon window with some alpha and tear-off menu */
3259 		FvwmWindow *t;
3260 
3261 		for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
3262 		{
3263 			int cs;
3264 			int t_cs = -1;
3265 			int b_cs = t->icon_background_cs;
3266 			Bool draw_picture = False;
3267 			Bool draw_title = False;
3268 
3269 			/* redraw ParentRelative tear-off menu */
3270 			menu_redraw_transparent_tear_off_menu(t, True);
3271 
3272 			if (!IS_ICONIFIED(t) || IS_ICON_SUPPRESSED(t))
3273 			{
3274 				continue;
3275 			}
3276 			if (Scr.Hilite == t)
3277 			{
3278 				if (t->icon_title_cs_hi >= 0)
3279 				{
3280 					t_cs = cs = t->icon_title_cs_hi;
3281 				}
3282 				else
3283 				{
3284 					cs = t->cs_hi;
3285 				}
3286 			}
3287 			else
3288 			{
3289 				if (t->icon_title_cs >= 0)
3290 				{
3291 					t_cs = cs = t->icon_title_cs;
3292 				}
3293 				else
3294 				{
3295 					cs = t->cs;
3296 				}
3297 			}
3298 			if (t->icon_alphaPixmap != None ||
3299 			    (cs >= 0 &&
3300 			     Colorset[cs].icon_alpha_percent < 100) ||
3301 			    CSET_IS_TRANSPARENT_PR(b_cs) ||
3302 			    (!IS_ICON_SHAPED(t) &&
3303 			     t->icon_background_padding > 0))
3304 			{
3305 				draw_picture = True;
3306 			}
3307 			if (CSET_IS_TRANSPARENT_PR(t_cs))
3308 			{
3309 				draw_title = True;
3310 			}
3311 			if (draw_title || draw_picture)
3312 			{
3313 				DrawIconWindow(
3314 					t, draw_title, draw_picture, False,
3315 					draw_picture, NULL);
3316 			}
3317 		}
3318 		if (te->xproperty.atom == _XA_XROOTPMAP_ID)
3319 		{
3320 			update_root_transparent_colorset(te->xproperty.atom);
3321 		}
3322 		BroadcastPropertyChange(
3323 			MX_PROPERTY_CHANGE_BACKGROUND, 0, 0, "");
3324 		return;
3325 	}
3326 
3327 	if (!fw)
3328 	{
3329 		return;
3330 	}
3331 	switch (te->xproperty.atom)
3332 	{
3333 	case XA_WM_TRANSIENT_FOR:
3334 	{
3335 		if (setup_transientfor(fw) == True)
3336 		{
3337 			RaiseWindow(fw, False);
3338 		}
3339 		break;
3340 	}
3341 	case XA_WM_NAME:
3342 	{
3343 		int changed_names;
3344 
3345 		flush_property_notify_stop_at_event_type(
3346 			te->xproperty.atom, FW_W(fw), 0, 0);
3347 		if (XGetGeometry(
3348 			    dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3349 			    (unsigned int*)&JunkWidth,
3350 			    (unsigned int*)&JunkHeight,
3351 			    (unsigned int*)&JunkBW,
3352 			    (unsigned int*)&JunkDepth) == 0)
3353 		{
3354 			/* Window does not exist anymore. */
3355 			return;
3356 		}
3357 		if (HAS_EWMH_WM_NAME(fw))
3358 		{
3359 			return;
3360 		}
3361 		FlocaleGetNameProperty(XGetWMName, dpy, FW_W(fw), &new_name);
3362 		if (new_name.name == NULL)
3363 		{
3364 			FlocaleFreeNameProperty(&new_name);
3365 			return;
3366 		}
3367 		if (strlen(new_name.name) > MAX_WINDOW_NAME_LEN)
3368 		{
3369 			/* limit to prevent hanging X server */
3370 			(new_name.name)[MAX_WINDOW_NAME_LEN] = 0;
3371 		}
3372 		if (fw->name.name && strcmp(new_name.name, fw->name.name) == 0)
3373 		{
3374 			/* migo: some apps update their names every second */
3375 			/* griph: make sure we don't free the property if it
3376 			   is THE same name */
3377 			if (new_name.name != fw->name.name)
3378 			{
3379 				FlocaleFreeNameProperty(&new_name);
3380 			}
3381 			return;
3382 		}
3383 
3384 		free_window_names(fw, True, False);
3385 		fw->name = new_name;
3386 		SET_NAME_CHANGED(fw, 1);
3387 		if (fw->name.name == NULL)
3388 		{
3389 			fw->name.name = NoName; /* must not happen */
3390 		}
3391 		changed_names = 1;
3392 		/*
3393 		 * if the icon name is NoName, set the name of the icon to be
3394 		 * the same as the window
3395 		 */
3396 		if (!WAS_ICON_NAME_PROVIDED(fw)
3397 #if 0
3398 		    /* dje, reported as causing various dumps.
3399 		       I tried to debug, but so far haven't even figured out
3400 		       how to exercise this logic. Mov 9, 2013. */
3401 		    || (fw->icon_name.name &&
3402 			(fw->icon_name.name != fw->name.name))
3403 #endif
3404 )
3405 		{
3406 			fw->icon_name = fw->name;
3407 			changed_names |= 2;
3408 		}
3409 		update_window_names(fw, changed_names);
3410 		break;
3411 	}
3412 	case XA_WM_ICON_NAME:
3413 	{
3414 		flush_property_notify_stop_at_event_type(
3415 			te->xproperty.atom, FW_W(fw), 0, 0);
3416 		if (XGetGeometry(
3417 			    dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3418 			    (unsigned int*)&JunkWidth,
3419 			    (unsigned int*)&JunkHeight,
3420 			    (unsigned int*)&JunkBW,
3421 			    (unsigned int*)&JunkDepth) == 0)
3422 		{
3423 			/* Window does not exist anymore. */
3424 			return;
3425 		}
3426 		if (HAS_EWMH_WM_ICON_NAME(fw))
3427 		{
3428 			return;
3429 		}
3430 		FlocaleGetNameProperty(
3431 			XGetWMIconName, dpy, FW_W(fw), &new_name);
3432 		if (new_name.name == NULL)
3433 		{
3434 			FlocaleFreeNameProperty(&new_name);
3435 			return;
3436 		}
3437 		if (new_name.name && strlen(new_name.name) > MAX_ICON_NAME_LEN)
3438 		{
3439 			/* limit to prevent hanging X server */
3440 			(new_name.name)[MAX_ICON_NAME_LEN] = 0;
3441 		}
3442 		if (fw->icon_name.name &&
3443 			strcmp(new_name.name, fw->icon_name.name) == 0)
3444 		{
3445 			/* migo: some apps update their names every second */
3446 			/* griph: make sure we don't free the property if it
3447 			   is THE same name */
3448 			if (new_name.name != fw->icon_name.name)
3449 			{
3450 				FlocaleFreeNameProperty(&new_name);
3451 			}
3452 			return;
3453 		}
3454 
3455 		free_window_names(fw, False, True);
3456 		fw->icon_name = new_name;
3457 		SET_WAS_ICON_NAME_PROVIDED(fw, 1);
3458 		if (fw->icon_name.name == NULL)
3459 		{
3460 			/* currently never happens */
3461 			fw->icon_name.name = fw->name.name;
3462 			SET_WAS_ICON_NAME_PROVIDED(fw, 0);
3463 		}
3464 		update_window_names(fw, 2);
3465 		break;
3466 	}
3467 	case XA_WM_HINTS:
3468 	{
3469 		flush_property_notify_stop_at_event_type(
3470 			te->xproperty.atom, FW_W(fw), 0, 0);
3471 		if (XGetGeometry(
3472 			    dpy, FW_W(fw), &JunkRoot, &JunkX, &JunkY,
3473 			    (unsigned int*)&JunkWidth,
3474 			    (unsigned int*)&JunkHeight,
3475 			    (unsigned int*)&JunkBW,
3476 			    (unsigned int*)&JunkDepth) == 0)
3477 		{
3478 			/* Window does not exist anymore. */
3479 			return;
3480 		}
3481 		/* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3482 		 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3483 		 */
3484 		old_wmhints_flags = 0;
3485 		if (fw->wmhints)
3486 		{
3487 			old_wmhints_flags = fw->wmhints->flags;
3488 			XFree ((char *) fw->wmhints);
3489 		}
3490 		setup_wm_hints(fw);
3491 		if (fw->wmhints == NULL)
3492 		{
3493 			return;
3494 		}
3495 
3496 		/*
3497 		 * rebuild icon if the client either provides an icon
3498 		 * pixmap or window or has reset the hints to `no icon'.
3499 		 */
3500 		if ((fw->wmhints->flags & IconPixmapHint) ||
3501 		    (old_wmhints_flags & IconPixmapHint))
3502 		{
3503 ICON_DBG((stderr, "hpn: iph changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconPixmapHint), fw->name.name));
3504 			has_icon_pixmap_hint_changed = True;
3505 		}
3506 		if ((fw->wmhints->flags & IconWindowHint) ||
3507 		    (old_wmhints_flags & IconWindowHint))
3508 		{
3509 ICON_DBG((stderr, "hpn: iwh changed (%d) '%s'\n", !!(int)(fw->wmhints->flags & IconWindowHint), fw->name.name));
3510 			has_icon_window_hint_changed = True;
3511 			SET_USE_EWMH_ICON(fw, False);
3512 		}
3513 		increase_icon_hint_count(fw);
3514 		if (has_icon_window_hint_changed ||
3515 		    has_icon_pixmap_hint_changed)
3516 		{
3517 			if (ICON_OVERRIDE_MODE(fw) == ICON_OVERRIDE)
3518 			{
3519 ICON_DBG((stderr, "hpn: icon override '%s'\n", fw->name.name));
3520 				has_icon_changed = False;
3521 			}
3522 			else if (ICON_OVERRIDE_MODE(fw) ==
3523 				 NO_ACTIVE_ICON_OVERRIDE)
3524 			{
3525 				if (has_icon_pixmap_hint_changed)
3526 				{
3527 					if (WAS_ICON_HINT_PROVIDED(fw) ==
3528 					    ICON_HINT_MULTIPLE)
3529 					{
3530 ICON_DBG((stderr, "hpn: using further iph '%s'\n", fw->name.name));
3531 						has_icon_changed = True;
3532 					}
3533 					else  if (fw->icon_bitmap_file ==
3534 						  NULL ||
3535 						  fw->icon_bitmap_file ==
3536 						  Scr.DefaultIcon)
3537 					{
3538 ICON_DBG((stderr, "hpn: using first iph '%s'\n", fw->name.name));
3539 						has_icon_changed = True;
3540 					}
3541 					else
3542 					{
3543 						/* ignore the first icon pixmap
3544 						 * hint if the application did
3545 						 * not provide it from the
3546 						 * start */
3547 ICON_DBG((stderr, "hpn: first iph ignored '%s'\n", fw->name.name));
3548 						has_icon_changed = False;
3549 					}
3550 				}
3551 				else if (has_icon_window_hint_changed)
3552 				{
3553 ICON_DBG((stderr, "hpn: using iwh '%s'\n", fw->name.name));
3554 					has_icon_changed = True;
3555 				}
3556 				else
3557 				{
3558 ICON_DBG((stderr, "hpn: iwh not changed, hint ignored '%s'\n", fw->name.name));
3559 					has_icon_changed = False;
3560 				}
3561 			}
3562 			else /* NO_ICON_OVERRIDE */
3563 			{
3564 ICON_DBG((stderr, "hpn: using hint '%s'\n", fw->name.name));
3565 				has_icon_changed = True;
3566 			}
3567 
3568 			if (USE_EWMH_ICON(fw))
3569 			{
3570 				has_icon_changed = False;
3571 			}
3572 
3573 			if (has_icon_changed)
3574 			{
3575 ICON_DBG((stderr, "hpn: icon changed '%s'\n", fw->name.name));
3576 				/* Okay, the icon hint has changed and style
3577 				 * options tell us to honour this change.  Now
3578 				 * let's see if we have to use the application
3579 				 * provided pixmap or window (if any), the icon
3580 				 * file provided by the window's style or the
3581 				 * default style's icon. */
3582 				if (fw->icon_bitmap_file == Scr.DefaultIcon)
3583 				{
3584 					fw->icon_bitmap_file = NULL;
3585 				}
3586 				if (!fw->icon_bitmap_file &&
3587 				    !(fw->wmhints->flags &
3588 				      (IconPixmapHint|IconWindowHint)))
3589 				{
3590 					fw->icon_bitmap_file =
3591 						(Scr.DefaultIcon) ?
3592 						Scr.DefaultIcon : NULL;
3593 				}
3594 				fw->iconPixmap = (Window)NULL;
3595 				ChangeIconPixmap(fw);
3596 			}
3597 		}
3598 
3599 		/* clasen@mathematik.uni-freiburg.de - 02/01/1998 - new -
3600 		 * the urgency flag is an ICCCM 2.0 addition to the WM_HINTS.
3601 		 * Treat urgency changes by calling user-settable functions.
3602 		 * These could e.g. deiconify and raise the window or
3603 		 * temporarily change the decor. */
3604 		if (!(old_wmhints_flags & XUrgencyHint) &&
3605 		    (fw->wmhints->flags & XUrgencyHint))
3606 		{
3607 			urgency_action = "Function UrgencyFunc";
3608 		}
3609 		if ((old_wmhints_flags & XUrgencyHint) &&
3610 		    !(fw->wmhints->flags & XUrgencyHint))
3611 		{
3612 			urgency_action = "Function UrgencyDoneFunc";
3613 		}
3614 		if (urgency_action)
3615 		{
3616 			const exec_context_t *exc;
3617 			exec_context_changes_t ecc;
3618 
3619 			ecc.w.fw = fw;
3620 			ecc.w.wcontext = C_WINDOW;
3621 			exc = exc_clone_context(
3622 				ea->exc, &ecc, ECC_FW | ECC_WCONTEXT);
3623 			execute_function(NULL, exc, urgency_action, 0);
3624 			exc_destroy_context(exc);
3625 		}
3626 		break;
3627 	}
3628 	case XA_WM_NORMAL_HINTS:
3629 		/* just mark wm normal hints as changed and look them up when
3630 		 * the next ConfigureRequest w/ x, y, width or height set
3631 		 * arrives. */
3632 		SET_HAS_NEW_WM_NORMAL_HINTS(fw, 1);
3633 
3634 		/* TA:  20120317:  Always set the size hints here, regardless
3635 		 * of them possibly being modified by a ConfigureNotify
3636 		 * request, due to XSizeHints disallowing resize -- FVWM would
3637 		 * always use old values if the application decided to toggle
3638 		 * such things, and FVWM would then never resize the window.
3639 		 *
3640 		 * Note that SET_HAS_NEW_WM_NORMAL_HINTS being set here to
3641 		 * true is still valid.
3642 		 */
3643 		GetWindowSizeHintsWithCheck(fw, 1);
3644 		break;
3645 	default:
3646 		if (te->xproperty.atom == _XA_WM_PROTOCOLS)
3647 		{
3648 			FetchWmProtocols (fw);
3649 		}
3650 		else if (te->xproperty.atom == _XA_WM_COLORMAP_WINDOWS)
3651 		{
3652 			FetchWmColormapWindows (fw);    /* frees old data */
3653 			ReInstallActiveColormap();
3654 		}
3655 		else if (te->xproperty.atom == _XA_WM_STATE)
3656 		{
3657 			/*
3658 			 * Make sure at least part of window is on this page
3659 			 * before giving it focus...
3660 			 */
3661 			Bool is_on_this_page;
3662 
3663 			is_on_this_page = IsRectangleOnThisPage(
3664 				&(fw->g.frame), fw->Desk);
3665 			if (fw && is_on_this_page == True &&
3666 			    focus_is_focused(fw) &&
3667 			    FP_DO_FOCUS_ENTER(FW_FOCUS_POLICY(fw)))
3668 			{
3669 				/* refresh the focus - why? */
3670 				focus_force_refresh_focus(fw);
3671 			}
3672 		}
3673 		else
3674 		{
3675 			EWMH_ProcessPropertyNotify(ea->exc);
3676 		}
3677 		break;
3678 	}
3679 }
3680 
HandleReparentNotify(const evh_args_t * ea)3681 void HandleReparentNotify(const evh_args_t *ea)
3682 {
3683 	const XEvent *te = ea->exc->x.etrigger;
3684 	FvwmWindow * const fw = ea->exc->w.fw;
3685 
3686 	if (!fw)
3687 	{
3688 		return;
3689 	}
3690 	if (te->xreparent.parent == Scr.Root)
3691 	{
3692 		/* Ignore reparenting to the root window.  In some cases these
3693 		 * events are selected although the window is no longer
3694 		 * managed. */
3695 		return;
3696 	}
3697 	if (te->xreparent.parent != FW_W_FRAME(fw))
3698 	{
3699 		/* window was reparented by someone else, destroy the frame */
3700 		SetMapStateProp(fw, WithdrawnState);
3701 		EWMH_RestoreInitialStates(fw, te->type);
3702 		if (!IS_TEAR_OFF_MENU(fw))
3703 		{
3704 			XRemoveFromSaveSet(dpy, te->xreparent.window);
3705 			XSelectInput(dpy, te->xreparent.window, NoEventMask);
3706 		}
3707 		else
3708 		{
3709 			XSelectInput(dpy, te->xreparent.window, XEVMASK_MENUW);
3710 		}
3711 		XSync(dpy, 0);
3712 		FWeedIfWindowEvents(dpy, FW_W_FRAME(fw), NULL, NULL);
3713 		destroy_window(fw);
3714 		EWMH_ManageKdeSysTray(te->xreparent.window, te->type);
3715 		EWMH_WindowDestroyed();
3716 	}
3717 
3718 	return;
3719 }
3720 
HandleSelectionRequest(const evh_args_t * ea)3721 void HandleSelectionRequest(const evh_args_t *ea)
3722 {
3723 	icccm2_handle_selection_request(ea->exc->x.etrigger);
3724 
3725 	return;
3726 }
3727 
HandleSelectionClear(const evh_args_t * ea)3728 void HandleSelectionClear(const evh_args_t *ea)
3729 {
3730 	icccm2_handle_selection_clear();
3731 
3732 	return;
3733 }
3734 
HandleShapeNotify(const evh_args_t * ea)3735 void HandleShapeNotify(const evh_args_t *ea)
3736 {
3737 	FvwmWindow * const fw = ea->exc->w.fw;
3738 
3739 	DBUG("HandleShapeNotify", "Routine Entered");
3740 
3741 	if (FShapesSupported)
3742 	{
3743 		const FShapeEvent *sev =
3744 			(const FShapeEvent *)(ea->exc->x.etrigger);
3745 
3746 		if (!fw)
3747 		{
3748 			return;
3749 		}
3750 		if (sev->kind != FShapeBounding)
3751 		{
3752 			return;
3753 		}
3754 		frame_setup_shape(
3755 			fw, fw->g.frame.width, fw->g.frame.height, sev->shaped);
3756 		EWMH_SetFrameStrut(fw);
3757 		if (!IS_ICONIFIED(fw))
3758 		{
3759 			border_redraw_decorations(fw);
3760 		}
3761 	}
3762 
3763 	return;
3764 }
3765 
HandleUnmapNotify(const evh_args_t * ea)3766 void HandleUnmapNotify(const evh_args_t *ea)
3767 {
3768 	int dstx, dsty;
3769 	Window dumwin;
3770 	XEvent dummy;
3771 	XEvent map_event;
3772 	const XEvent *te = ea->exc->x.etrigger;
3773 	Bool focus_grabbed;
3774 	Bool must_return = False;
3775 	Bool do_map = False;
3776 	FvwmWindow * const fw = ea->exc->w.fw;
3777 	Window pw;
3778 	Window cw;
3779 
3780 	DBUG("HandleUnmapNotify", "Routine Entered");
3781 
3782 	if (te->xunmap.event != te->xunmap.window
3783 	    && (te->xunmap.event != Scr.Root || !te->xunmap.send_event))
3784 	{
3785 		/* Nothing to do except updating some states. */
3786 		must_return = True;
3787 	}
3788 
3789 	/*
3790 	 * The July 27, 1988 ICCCM spec states that a client wishing to switch
3791 	 * to WithdrawnState should send a synthetic UnmapNotify with the
3792 	 * event field set to (pseudo-)root, in case the window is already
3793 	 * unmapped (which is the case for fvwm for IconicState).
3794 	 * Unfortunately, we looked for the FvwmContext using that field, so
3795 	 * try the window field also. */
3796 	if (!fw)
3797 	{
3798 		if (XFindContext(
3799 			    dpy, te->xunmap.window, FvwmContext,
3800 			    (caddr_t *)&fw) == XCNOENT)
3801 		{
3802 			return;
3803 		}
3804 	}
3805 	cw = FW_W(fw);
3806 	pw = FW_W_PARENT(fw);
3807 	if (te->xunmap.window == FW_W_FRAME(fw))
3808 	{
3809 		SET_ICONIFY_PENDING(fw , 0);
3810 		return;
3811 	}
3812 	if (must_return)
3813 	{
3814 		return;
3815 	}
3816 
3817 	if (fw ==  Scr.Hilite)
3818 	{
3819 		Scr.Hilite = NULL;
3820 	}
3821 	focus_grabbed = focus_query_close_release_focus(fw);
3822 	restore_focus_after_unmap(fw, False);
3823 	if (!IS_MAPPED(fw) && !IS_ICONIFIED(fw))
3824 	{
3825 		return;
3826 	}
3827 
3828 	/*
3829 	 * The program may have unmapped the client window, from either
3830 	 * NormalState or IconicState.  Handle the transition to WithdrawnState.
3831 	 *
3832 	 * We need to reparent the window back to the root (so that fvwm exiting
3833 	 * won't cause it to get mapped) and then throw away all state (pretend
3834 	 * that we've received a DestroyNotify).
3835 	 */
3836 	if (!FCheckTypedWindowEvent(
3837 		    dpy, te->xunmap.window, DestroyNotify, &dummy) &&
3838 	    XTranslateCoordinates(
3839 		    dpy, te->xunmap.window, Scr.Root, 0, 0, &dstx, &dsty,
3840 		    &dumwin))
3841 	{
3842 		MyXGrabServer(dpy);
3843 		SetMapStateProp(fw, WithdrawnState);
3844 		EWMH_RestoreInitialStates(fw, te->type);
3845 		if (FCheckTypedWindowEvent(
3846 			    dpy, te->xunmap.window, ReparentNotify, &dummy))
3847 		{
3848 			if (fw->attr_backup.border_width)
3849 			{
3850 				XSetWindowBorderWidth(
3851 					dpy, te->xunmap.window,
3852 					fw->attr_backup.border_width);
3853 			}
3854 			if ((!IS_ICON_SUPPRESSED(fw))&&
3855 			   (fw->wmhints &&
3856 			    (fw->wmhints->flags & IconWindowHint)))
3857 			{
3858 				XUnmapWindow(dpy, fw->wmhints->icon_window);
3859 			}
3860 		}
3861 		else
3862 		{
3863 			RestoreWithdrawnLocation(fw, False, Scr.Root);
3864 		}
3865 		if (!IS_TEAR_OFF_MENU(fw))
3866 		{
3867 			XRemoveFromSaveSet(dpy, te->xunmap.window);
3868 			XSelectInput(dpy, te->xunmap.window, NoEventMask);
3869 		}
3870 		XSync(dpy, 0);
3871 		MyXUngrabServer(dpy);
3872 		if (FCheckTypedWindowEvent(dpy, pw, MapRequest, &map_event))
3873 		{
3874 			/* the client tried to map the window again while it
3875 			 * was still inside the decoration windows */
3876 		        do_map = True;
3877 		}
3878 	}
3879 	destroy_window(fw);
3880 	if (focus_grabbed == True)
3881 	{
3882 		CoerceEnterNotifyOnCurrentWindow();
3883 	}
3884 	EWMH_ManageKdeSysTray(te->xunmap.window, te->type);
3885 	EWMH_WindowDestroyed();
3886 	if (do_map == True)
3887 	{
3888 		map_event.xmaprequest.window = cw;
3889 		map_event.xmaprequest.parent = Scr.Root;
3890 		dispatch_event(&map_event);
3891 		/* note: we really should handle all map and unmap notify
3892 		 * events for that window in a loop here */
3893 	}
3894 
3895 	return;
3896 }
3897 
HandleVisibilityNotify(const evh_args_t * ea)3898 void HandleVisibilityNotify(const evh_args_t *ea)
3899 {
3900 	FvwmWindow * const fw = ea->exc->w.fw;
3901 
3902 	DBUG("HandleVisibilityNotify", "Routine Entered");
3903 
3904 	if (fw && ea->exc->x.etrigger->xvisibility.window == FW_W_FRAME(fw))
3905 	{
3906 		switch (ea->exc->x.etrigger->xvisibility.state)
3907 		{
3908 		case VisibilityUnobscured:
3909 			SET_FULLY_VISIBLE(fw, 1);
3910 			SET_PARTIALLY_VISIBLE(fw, 1);
3911 			break;
3912 		case VisibilityPartiallyObscured:
3913 			SET_FULLY_VISIBLE(fw, 0);
3914 			SET_PARTIALLY_VISIBLE(fw, 1);
3915 			break;
3916 		default:
3917 			SET_FULLY_VISIBLE(fw, 0);
3918 			SET_PARTIALLY_VISIBLE(fw, 0);
3919 			break;
3920 		}
3921 		/* Make sure the button grabs are up to date */
3922 		focus_grab_buttons(fw);
3923 	}
3924 
3925 	return;
3926 }
3927 
3928 /* ---------------------------- interface functions ------------------------ */
3929 
3930 /* Inform a client window of its geometry.
3931  *
3932  *  The input (frame) geometry will be translated to client geometry
3933  *  before sending. */
SendConfigureNotify(FvwmWindow * fw,int x,int y,int w,int h,int bw,Bool send_for_frame_too)3934 void SendConfigureNotify(
3935 	FvwmWindow *fw, int x, int y, int w, int h, int bw,
3936 	Bool send_for_frame_too)
3937 {
3938 	XEvent client_event;
3939 	size_borders b;
3940 
3941 	if (!fw || IS_SHADED(fw))
3942 	{
3943 		return;
3944 	}
3945 	client_event.type = ConfigureNotify;
3946 	client_event.xconfigure.display = dpy;
3947 	client_event.xconfigure.event = FW_W(fw);
3948 	client_event.xconfigure.window = FW_W(fw);
3949 	get_window_borders(fw, &b);
3950 	client_event.xconfigure.x = x + b.top_left.width;
3951 	client_event.xconfigure.y = y + b.top_left.height;
3952 	client_event.xconfigure.width = w - b.total_size.width;
3953 	client_event.xconfigure.height = h - b.total_size.height;
3954 	client_event.xconfigure.border_width = bw;
3955 	client_event.xconfigure.above = FW_W_FRAME(fw);
3956 	client_event.xconfigure.override_redirect = False;
3957 #if 0
3958 	fprintf(stderr,
3959 		"send cn: %d %d %dx%d fw 0x%08x w 0x%08x ew 0x%08x  '%s'\n",
3960 		client_event.xconfigure.x, client_event.xconfigure.y,
3961 		client_event.xconfigure.width, client_event.xconfigure.height,
3962 		(int)FW_W_FRAME(fw), (int)FW_W(fw),
3963 		(int)client_event.xconfigure.window,
3964 		(fw->name.name) ? fw->name.name : "");
3965 #endif
3966 	fev_sanitise_configure_notify(&client_event.xconfigure);
3967 	FSendEvent(
3968 		dpy, FW_W(fw), False, StructureNotifyMask, &client_event);
3969 	if (send_for_frame_too)
3970 	{
3971 		/* This is for buggy tk, which waits for the real
3972 		 * ConfigureNotify on frame instead of the synthetic one on w.
3973 		 * The geometry data in the event will not be correct for the
3974 		 * frame, but tk doesn't look at that data anyway. */
3975 		client_event.xconfigure.event = FW_W_FRAME(fw);
3976 		client_event.xconfigure.window = FW_W_FRAME(fw);
3977 		FSendEvent(
3978 			dpy, FW_W_FRAME(fw), False, StructureNotifyMask,
3979 			&client_event);
3980 	}
3981 
3982 	return;
3983 }
3984 
3985 /* Add an event group to the event handler */
register_event_group(int event_base,int event_count,PFEH * jump_table)3986 int register_event_group(int event_base, int event_count, PFEH *jump_table)
3987 {
3988 	/* insert into the list */
3989 	event_group_t *group;
3990 	event_group_t *position = base_event_group;
3991 	event_group_t *prev_position = NULL;
3992 
3993 	while (
3994 		position != NULL &&
3995 		position->base + position->count < event_base)
3996 	{
3997 		prev_position = position;
3998 		position = position->next;
3999 	}
4000 	if ((position != NULL && position->base < event_base + event_count))
4001 	{
4002 		/* there is already an event group registered at the specified
4003 		 * event range, or the base is before the base X events */
4004 
4005 		return 1;
4006 	}
4007 	/* create the group structure (these are not freed until fvwm exits) */
4008 	group = (event_group_t*)safemalloc(sizeof(event_group_t));
4009 	group->base = event_base;
4010 	group->count = event_count;
4011 	group->jump_table = jump_table;
4012 	group->next = position;
4013 	if (prev_position != NULL)
4014 	{
4015 		prev_position->next = group;
4016 	}
4017 	else
4018 	{
4019 		base_event_group = group;
4020 	}
4021 
4022 	return 0;
4023 }
4024 
4025 /*
4026 ** Procedure:
4027 **   InitEventHandlerJumpTable
4028 */
InitEventHandlerJumpTable(void)4029 void InitEventHandlerJumpTable(void)
4030 {
4031 	static PFEH EventHandlerJumpTable[LASTEvent];
4032 	int i;
4033 
4034 	for (i=0; i<LASTEvent; i++)
4035 	{
4036 		EventHandlerJumpTable[i] = NULL;
4037 	}
4038 	EventHandlerJumpTable[Expose] =           HandleExpose;
4039 	EventHandlerJumpTable[DestroyNotify] =    HandleDestroyNotify;
4040 	EventHandlerJumpTable[MapRequest] =       HandleMapRequest;
4041 	EventHandlerJumpTable[MapNotify] =        HandleMapNotify;
4042 	EventHandlerJumpTable[UnmapNotify] =      HandleUnmapNotify;
4043 	EventHandlerJumpTable[ButtonPress] =      HandleButtonPress;
4044 	EventHandlerJumpTable[EnterNotify] =      HandleEnterNotify;
4045 	EventHandlerJumpTable[LeaveNotify] =      HandleLeaveNotify;
4046 	EventHandlerJumpTable[FocusIn] =          HandleFocusIn;
4047 	EventHandlerJumpTable[FocusOut] =         HandleFocusOut;
4048 	EventHandlerJumpTable[ConfigureRequest] = HandleConfigureRequest;
4049 	EventHandlerJumpTable[ClientMessage] =    HandleClientMessage;
4050 	EventHandlerJumpTable[PropertyNotify] =   HandlePropertyNotify;
4051 	EventHandlerJumpTable[KeyPress] =         HandleKeyPress;
4052 	EventHandlerJumpTable[KeyRelease] =       HandleKeyRelease;
4053 	EventHandlerJumpTable[VisibilityNotify] = HandleVisibilityNotify;
4054 	EventHandlerJumpTable[ColormapNotify] =   HandleColormapNotify;
4055 	EventHandlerJumpTable[SelectionClear]   = HandleSelectionClear;
4056 	EventHandlerJumpTable[SelectionRequest] = HandleSelectionRequest;
4057 	EventHandlerJumpTable[ReparentNotify] =   HandleReparentNotify;
4058 	EventHandlerJumpTable[MappingNotify] =    HandleMappingNotify;
4059 	STROKE_CODE(EventHandlerJumpTable[ButtonRelease] = HandleButtonRelease);
4060 	STROKE_CODE(EventHandlerJumpTable[MotionNotify] = HandleMotionNotify);
4061 #ifdef MOUSE_DROPPINGS
4062 	STROKE_CODE(stroke_init(dpy,DefaultRootWindow(dpy)));
4063 #else /* no MOUSE_DROPPINGS */
4064 	STROKE_CODE(stroke_init());
4065 #endif /* MOUSE_DROPPINGS */
4066 	if (register_event_group(0, LASTEvent, EventHandlerJumpTable))
4067 	{
4068 		/* should never happen */
4069 		fvwm_msg(ERR, "InitEventHandlerJumpTable",
4070 			 "Failed to initialize event handlers");
4071 		exit(1);
4072 	}
4073 	if (FShapesSupported)
4074 	{
4075 		static PFEH shape_jump_table[FShapeNumberEvents];
4076 
4077 		for (i = 0; i < FShapeNumberEvents; i++)
4078 		{
4079 			shape_jump_table[i] = NULL;
4080 		}
4081 		shape_jump_table[FShapeNotify] = HandleShapeNotify;
4082 		if (
4083 			register_event_group(
4084 				FShapeEventBase, FShapeNumberEvents,
4085 				shape_jump_table))
4086 		{
4087 			fvwm_msg(ERR, "InitEventHandlerJumpTable",
4088 				 "Failed to init Shape event handler");
4089 		}
4090 	}
4091 
4092 
4093 	return;
4094 }
4095 
4096 /* handle a single X event */
dispatch_event(XEvent * e)4097 void dispatch_event(XEvent *e)
4098 {
4099 	Window w = e->xany.window;
4100 	FvwmWindow *fw;
4101 	event_group_t *event_group;
4102 
4103 	DBUG("dispatch_event", "Routine Entered");
4104 
4105 	XFlush(dpy);
4106 	if (w == Scr.Root)
4107 	{
4108 		switch (e->type)
4109 		{
4110 		case ButtonPress:
4111 		case ButtonRelease:
4112 			if (e->xbutton.subwindow != None)
4113 			{
4114 				w = e->xbutton.subwindow;
4115 			}
4116 		case MapRequest:
4117 			w = e->xmaprequest.window;
4118 			break;
4119 		default:
4120 			break;
4121 		}
4122 	}
4123 	if (w == Scr.Root ||
4124 	    XFindContext(dpy, w, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4125 	{
4126 		fw = NULL;
4127 	}
4128 	if (
4129 		fw != NULL && IS_SCHEDULED_FOR_DESTROY(fw) &&
4130 		e->type != DestroyNotify)
4131 	{
4132 		return;
4133 	}
4134 	last_event_type = e->type;
4135 	event_group = base_event_group;
4136 	while (
4137 		event_group != NULL &&
4138 		event_group->base + event_group->count < e->type)
4139 	{
4140 		event_group = event_group->next;
4141 	}
4142 
4143 	if (
4144 		event_group != NULL &&
4145 		e->type - event_group->base < event_group->count &&
4146 		event_group->jump_table[e->type - event_group->base] != NULL)
4147 	{
4148 		evh_args_t ea;
4149 		exec_context_changes_t ecc;
4150 		Window dummyw;
4151 
4152 		ecc.type = EXCT_EVENT;
4153 		ecc.x.etrigger = e;
4154 		ecc.w.wcontext = GetContext(&fw, fw, e, &dummyw);
4155 		ecc.w.w = w;
4156 		ecc.w.fw = fw;
4157 		ea.exc = exc_create_context(
4158 			&ecc, ECC_TYPE | ECC_ETRIGGER | ECC_FW | ECC_W |
4159 			ECC_WCONTEXT);
4160 		(*event_group->jump_table[e->type - event_group->base])(&ea);
4161 		exc_destroy_context(ea.exc);
4162 	}
4163 
4164 #ifdef C_ALLOCA
4165 	/* If we're using the C version of alloca, see if anything needs to be
4166 	 * freed up.
4167 	 */
4168 	alloca(0);
4169 #endif
4170 	DBUG("dispatch_event", "Leaving Routine");
4171 
4172 	return;
4173 }
4174 
4175 /* ewmh configure request */
events_handle_configure_request(XEvent * cre,FvwmWindow * fw,Bool force,int force_gravity)4176 void events_handle_configure_request(
4177 	XEvent *cre, FvwmWindow *fw, Bool force, int force_gravity)
4178 {
4179 	_handle_configure_request(cre, NULL, fw, force, force_gravity);
4180 
4181 	return;
4182 }
4183 
HandleEvents(void)4184 void HandleEvents(void)
4185 {
4186 	XEvent ev;
4187 
4188 	DBUG("HandleEvents", "Routine Entered");
4189 	STROKE_CODE(send_motion = False);
4190 	while (!isTerminated)
4191 	{
4192 		last_event_type = 0;
4193 		if (Scr.flags.is_window_scheduled_for_destroy)
4194 		{
4195 			destroy_scheduled_windows();
4196 		}
4197 		if (Scr.flags.do_need_window_update)
4198 		{
4199 			flush_window_updates();
4200 		}
4201 		if (My_XNextEvent(dpy, &ev))
4202 		{
4203 			/* DV (19-Sep-2014): We mark events as invalid by
4204 			 * setting the send_event field to a bogus value in a
4205 			 * predicate procedure.  It's unsure whether this works
4206 			 * reliably.  */
4207 			if (FEV_IS_EVENT_INVALID(ev))
4208 			{
4209 				continue;
4210 			}
4211 			dispatch_event(&ev);
4212 		}
4213 		if (Scr.flags.do_need_style_list_update)
4214 		{
4215 			simplify_style_list();
4216 		}
4217 	}
4218 
4219 	return;
4220 }
4221 
4222 /*
4223  *
4224  * Waits for next X or module event, fires off startup routines when startup
4225  * modules have finished or after a timeout if the user has specified a
4226  * command line module that doesn't quit or gets stuck.
4227  *
4228  */
My_XNextEvent(Display * dpy,XEvent * event)4229 int My_XNextEvent(Display *dpy, XEvent *event)
4230 {
4231 	fd_set in_fdset, out_fdset;
4232 	int num_fd;
4233 	fmodule_list_itr moditr;
4234 	fmodule *module;
4235 	fmodule_input *input;
4236 	static struct timeval timeout;
4237 	static struct timeval *timeoutP = &timeout;
4238 
4239 	DBUG("My_XNextEvent", "Routine Entered");
4240 
4241 	/* check for any X events already queued up.
4242 	 * Side effect: this does an XFlush if no events are queued
4243 	 * Make sure nothing between here and the select causes further
4244 	 * X requests to be sent or the select may block even though
4245 	 * there are events in the queue */
4246 	if (FPending(dpy))
4247 	{
4248 		DBUG(
4249 			"My_XNextEvent", "taking care of queued up events"
4250 			" & returning (1)");
4251 		FNextEvent(dpy, event);
4252 		return 1;
4253 	}
4254 
4255 	/* check for termination of all startup modules */
4256 	if (fFvwmInStartup)
4257 	{
4258 		module_list_itr_init(&moditr);
4259 		module = module_list_itr_next(&moditr);
4260 		for (; module != NULL; module = module_list_itr_next(&moditr))
4261 		{
4262 			if (MOD_IS_CMDLINE(module) == 1)
4263 			{
4264 				break;
4265 			}
4266 		}
4267 		module_cleanup();
4268 		if (module == NULL)
4269 		{
4270 			/* last module */
4271 			DBUG(
4272 				"My_XNextEvent",
4273 				"Starting up after command lines modules");
4274 			/* set an infinite timeout to stop ticking */
4275 			timeoutP = NULL;
4276 			/* This may cause X requests to be sent */
4277 			StartupStuff();
4278 
4279 			return 0; /* so return without select()ing */
4280 		}
4281 	}
4282 
4283 	/* Some signals can interrupt us while we wait for any action
4284 	 * on our descriptors. While some of these signals may be asking
4285 	 * fvwm to die, some might be harmless. Harmless interruptions
4286 	 * mean we have to start waiting all over again ... */
4287 	do
4288 	{
4289 		int ms;
4290 		Bool is_waiting_for_scheduled_command = False;
4291 		static struct timeval *old_timeoutP = NULL;
4292 
4293 		/* The timeouts become undefined whenever the select returns,
4294 		 * and so we have to reinitialise them */
4295 		ms = squeue_get_next_ms();
4296 		if (ms == 0)
4297 		{
4298 			/* run scheduled commands */
4299 			squeue_execute();
4300 			ms = squeue_get_next_ms();
4301 			/* should not happen anyway.
4302 			 * get_next_schedule_queue_ms() can't return 0 after a
4303 			 * call to execute_schedule_queue(). */
4304 			if (ms == 0)
4305 			{
4306 				ms = 1;
4307 			}
4308 		}
4309 		if (ms < 0)
4310 		{
4311 			timeout.tv_sec = 42;
4312 			timeout.tv_usec = 0;
4313 		}
4314 		else
4315 		{
4316 			/* scheduled commands are pending - don't wait too
4317 			 * long */
4318 			timeout.tv_sec = ms / 1000;
4319 			timeout.tv_usec = 1000 * (ms % 1000);
4320 			old_timeoutP = timeoutP;
4321 			timeoutP = &timeout;
4322 			is_waiting_for_scheduled_command = True;
4323 		}
4324 
4325 		FD_ZERO(&in_fdset);
4326 		FD_ZERO(&out_fdset);
4327 		FD_SET(x_fd, &in_fdset);
4328 
4329 		/* nothing is done here if fvwm was compiled without session
4330 		 * support */
4331 		if (sm_fd >= 0)
4332 		{
4333 			FD_SET(sm_fd, &in_fdset);
4334 		}
4335 
4336 		module_list_itr_init(&moditr);
4337 		while ( (module = module_list_itr_next(&moditr)) != NULL)
4338 		{
4339 			FD_SET(MOD_READFD(module), &in_fdset);
4340 
4341 			if (!FQUEUE_IS_EMPTY(&MOD_PIPEQUEUE(module)))
4342 			{
4343 				FD_SET(MOD_WRITEFD(module), &out_fdset);
4344 			}
4345 		}
4346 
4347 		DBUG("My_XNextEvent", "waiting for module input/output");
4348 		num_fd = fvwmSelect(
4349 			fvwmlib_max_fd, &in_fdset, &out_fdset, 0, timeoutP);
4350 		if (is_waiting_for_scheduled_command)
4351 		{
4352 			timeoutP = old_timeoutP;
4353 		}
4354 
4355 		/* Express route out of fvwm ... */
4356 		if (isTerminated)
4357 		{
4358 			return 0;
4359 		}
4360 	} while (num_fd < 0);
4361 
4362 	if (num_fd > 0)
4363 	{
4364 		/* Check for module input. */
4365 		module_list_itr_init(&moditr);
4366 		while ( (module = module_list_itr_next(&moditr)) != NULL)
4367 		{
4368 			if (FD_ISSET(MOD_READFD(module), &in_fdset))
4369 			{
4370 				input = module_receive(module);
4371 				/* enqueue the received command */
4372 				module_input_enqueue(input);
4373 			}
4374 			if (
4375 				MOD_WRITEFD(module) >= 0 &&
4376 				FD_ISSET(MOD_WRITEFD(module), &out_fdset))
4377 			{
4378 				DBUG("My_XNextEvent",
4379 				     "calling FlushMessageQueue");
4380 				FlushMessageQueue(module);
4381 			}
4382 		}
4383 
4384 		/* execute any commands queued up */
4385 		DBUG("My_XNextEvent", "executing module comand queue");
4386 		ExecuteCommandQueue();
4387 
4388 		/* cleanup dead modules */
4389 		module_cleanup();
4390 
4391 		/* nothing is done here if fvwm was compiled without session
4392 		 * support */
4393 		if ((sm_fd >= 0) && (FD_ISSET(sm_fd, &in_fdset)))
4394 		{
4395 			ProcessICEMsgs();
4396 		}
4397 
4398 	}
4399 	else
4400 	{
4401 		/* select has timed out, things must have calmed down so let's
4402 		 * decorate */
4403 		if (fFvwmInStartup)
4404 		{
4405 			fvwm_msg(ERR, "My_XNextEvent",
4406 				 "Some command line modules have not quit, "
4407 				 "Starting up after timeout.\n");
4408 			StartupStuff();
4409 			timeoutP = NULL; /* set an infinite timeout to stop
4410 					  * ticking */
4411 			reset_style_changes();
4412 			Scr.flags.do_need_window_update = 0;
4413 		}
4414 		/* run scheduled commands if necessary */
4415 		squeue_execute();
4416 	}
4417 
4418 	/* check for X events again, rather than return 0 and get called again
4419 	 */
4420 	if (FPending(dpy))
4421 	{
4422 		DBUG("My_XNextEvent",
4423 		     "taking care of queued up events & returning (2)");
4424 		FNextEvent(dpy,event);
4425 		return 1;
4426 	}
4427 
4428 	DBUG("My_XNextEvent", "leaving My_XNextEvent");
4429 	return 0;
4430 }
4431 
4432 /*
4433  *
4434  *  Procedure:
4435  *      Find the Fvwm context for the event.
4436  *
4437  */
GetContext(FvwmWindow ** ret_fw,FvwmWindow * t,const XEvent * e,Window * w)4438 int GetContext(FvwmWindow **ret_fw, FvwmWindow *t, const XEvent *e, Window *w)
4439 {
4440 	int context;
4441 	Window win;
4442 	Window subw = None;
4443 	int x = 0;
4444 	int y = 0;
4445 	Bool is_key_event = False;
4446 
4447 	win = e->xany.window;
4448 	context = C_NO_CONTEXT;
4449 	switch (e->type)
4450 	{
4451 	case KeyPress:
4452 	case KeyRelease:
4453 		x = e->xkey.x;
4454 		y = e->xkey.y;
4455 		subw = e->xkey.subwindow;
4456 		if (win == Scr.Root && subw != None)
4457 		{
4458 			/* Translate root coordinates into subwindow
4459 			 * coordinates.  Necessary for key bindings that work
4460 			 * over unfocused windows. */
4461 			win = subw;
4462 			XTranslateCoordinates(
4463 				dpy, Scr.Root, subw, x, y, &x, &y, &subw);
4464 			XFindContext(dpy, win, FvwmContext, (caddr_t *) &t);
4465 		}
4466 		is_key_event = True;
4467 		/* fall through */
4468 	case ButtonPress:
4469 	case ButtonRelease:
4470 		if (!is_key_event)
4471 		{
4472 			x = e->xbutton.x;
4473 			y = e->xbutton.y;
4474 			subw = e->xbutton.subwindow;
4475 		}
4476 		if (t && win == FW_W_FRAME(t) && subw != None)
4477 		{
4478 			/* Translate frame coordinates into subwindow
4479 			 * coordinates. */
4480 			win = subw;
4481 			XTranslateCoordinates(
4482 				dpy, FW_W_FRAME(t), subw, x, y, &x, &y, &subw);
4483 			if (win == FW_W_PARENT(t))
4484 			{
4485 				win = subw;
4486 				XTranslateCoordinates(
4487 					dpy, FW_W_PARENT(t), subw, x, y, &x,
4488 					&y, &subw);
4489 			}
4490 		}
4491 		break;
4492 	default:
4493 		XFindContext(dpy, win, FvwmContext, (caddr_t *)&t);
4494 		break;
4495 	}
4496 	if (ret_fw != NULL)
4497 	{
4498 		*ret_fw = t;
4499 	}
4500 	if (!t)
4501 	{
4502 		return C_ROOT;
4503 	}
4504 	*w = win;
4505 	if (*w == Scr.NoFocusWin)
4506 	{
4507 		return C_ROOT;
4508 	}
4509 	if (subw != None)
4510 	{
4511 		if (win == FW_W_PARENT(t))
4512 		{
4513 			*w = subw;
4514 		}
4515 	}
4516 	if (*w == Scr.Root)
4517 	{
4518 		return C_ROOT;
4519 	}
4520 	context = frame_window_id_to_context(t, *w, &Button);
4521 
4522 	return context;
4523 }
4524 
4525 /* Drops all expose events for the given window from the input queue and merges
4526  * the expose rectangles into a single big one (*e). */
flush_accumulate_expose(Window w,XEvent * e)4527 void flush_accumulate_expose(Window w, XEvent *e)
4528 {
4529 	FWeedIfWindowEvents(dpy, w, _pred_weed_accumulate_expose, (XPointer)e);
4530 
4531 	return;
4532 }
4533 
4534 /*
4535  *
4536  * Removes all expose events from the queue and does the necessary redraws
4537  *
4538  */
handle_all_expose(void)4539 void handle_all_expose(void)
4540 {
4541 	void *saved_event;
4542 
4543 	saved_event = fev_save_event();
4544 	FPending(dpy);
4545 	FWeedIfEvents(dpy, _pred_weed_handle_expose, NULL);
4546 	fev_restore_event(saved_event);
4547 
4548 	return;
4549 }
4550 
4551 /* CoerceEnterNotifyOnCurrentWindow()
4552  * Pretends to get a HandleEnterNotify on the window that the pointer
4553  * currently is in so that the focus gets set correctly from the beginning.
4554  * Note that this presently only works if the current window is not
4555  * click_to_focus;  I think that that behaviour is correct and desirable.
4556  * --11/08/97 gjb */
CoerceEnterNotifyOnCurrentWindow(void)4557 void CoerceEnterNotifyOnCurrentWindow(void)
4558 {
4559 	Window child;
4560 	Window root;
4561 	Bool f;
4562 	evh_args_t ea;
4563 	exec_context_changes_t ecc;
4564 	XEvent e;
4565 	FvwmWindow *fw;
4566 
4567 	f = FQueryPointer(
4568 		dpy, Scr.Root, &root, &child, &e.xcrossing.x_root,
4569 		&e.xcrossing.y_root, &e.xcrossing.x, &e.xcrossing.y,
4570 		&JunkMask);
4571 	if (f == False || child == None)
4572 	{
4573 		return;
4574 	}
4575 	e.xcrossing.type = EnterNotify;
4576 	e.xcrossing.window = child;
4577 	e.xcrossing.subwindow = None;
4578 	e.xcrossing.mode = NotifyNormal;
4579 	e.xcrossing.detail = NotifyAncestor;
4580 	e.xcrossing.same_screen = True;
4581 	if (XFindContext(dpy, child, FvwmContext, (caddr_t *)&fw) == XCNOENT)
4582 	{
4583 		fw = NULL;
4584 	}
4585 	else
4586 	{
4587 		XTranslateCoordinates(
4588 			dpy, Scr.Root, child, e.xcrossing.x_root,
4589 			e.xcrossing.y_root, &JunkX, &JunkY, &child);
4590 		if (child == FW_W_PARENT(fw))
4591 		{
4592 			child = FW_W(fw);
4593 		}
4594 		if (child != None)
4595 		{
4596 			e.xany.window = child;
4597 		}
4598 	}
4599 	e.xcrossing.focus = (fw == get_focus_window()) ? True : False;
4600 	ecc.type = EXCT_NULL;
4601 	ecc.x.etrigger = &e;
4602 	ea.exc = exc_create_context(&ecc, ECC_TYPE | ECC_ETRIGGER);
4603 	HandleEnterNotify(&ea);
4604 	exc_destroy_context(ea.exc);
4605 
4606 	return;
4607 }
4608 
4609 /* This function discards all queued up events selected by the mask. */
discard_typed_events(int num_event_types,int * event_types)4610 int discard_typed_events(int num_event_types, int *event_types)
4611 {
4612 	_weed_event_type_arg args;
4613 	int count;
4614 	int i;
4615 
4616 	XSync(dpy, 0);
4617 	assert(num_event_types <= MAX_NUM_WEED_EVENT_TYPES);
4618 	args.num_event_types = num_event_types;
4619 	for (i = 0; i < num_event_types; i++)
4620 	{
4621 		args.event_types[i] = event_types[i];
4622 	}
4623 	count = FWeedIfEvents(dpy, _pred_weed_event_type, (XPointer)&args);
4624 
4625 	return count;
4626 }
4627 
4628 /* Similar function for certain types of PropertyNotify. */
flush_property_notify_stop_at_event_type(Atom atom,Window w,char do_stop_at_event_type,int stop_at_event_type)4629 int flush_property_notify_stop_at_event_type(
4630 	Atom atom, Window w, char do_stop_at_event_type,
4631 	int stop_at_event_type)
4632 {
4633 	flush_property_notify_args args;
4634 
4635 	XSync(dpy, 0);
4636 	args.w = w;
4637 	args.atom = atom;
4638 	args.event_type = PropertyNotify;
4639 	args.stop_at_event_type = stop_at_event_type;
4640 	args.do_stop_at_event_type = do_stop_at_event_type;
4641 	FWeedIfEvents(
4642 		dpy, _pred_flush_property_notify_weed, (XPointer)&args);
4643 
4644 	return 0;
4645 }
4646 
4647 /* Wait for all mouse buttons to be released
4648  * This can ease some confusion on the part of the user sometimes
4649  *
4650  * Discard superflous button events during this wait period. */
WaitForButtonsUp(Bool do_handle_expose)4651 void WaitForButtonsUp(Bool do_handle_expose)
4652 {
4653 	unsigned int mask;
4654 	unsigned int bmask;
4655 	long evmask = ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|
4656 		KeyPressMask|KeyReleaseMask;
4657 	int count;
4658 	int use_wait_cursor;
4659 	XEvent e;
4660 
4661 	if (FQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX, &JunkY,
4662 			  &JunkX, &JunkY, &mask) == False)
4663 	{
4664 		/* pointer is on a different screen - that's okay here */
4665 	}
4666 	mask &= DEFAULT_ALL_BUTTONS_MASK;
4667 	if (mask == 0)
4668 	{
4669 		return;
4670 	}
4671 	if (do_handle_expose)
4672 	{
4673 		evmask |= ExposureMask;
4674 	}
4675 	GrabEm(None, GRAB_NORMAL);
4676 	for (count = 0, use_wait_cursor = 0; mask != 0; count++)
4677 	{
4678 		/* handle expose events */
4679 		XAllowEvents(dpy, SyncPointer, CurrentTime);
4680 		if (FCheckMaskEvent(dpy, evmask, &e))
4681 		{
4682 			switch (e.type)
4683 			{
4684 			case ButtonRelease:
4685 				if (e.xbutton.button <=
4686 				    NUMBER_OF_MOUSE_BUTTONS)
4687 				{
4688 					bmask = (Button1Mask <<
4689 						 (e.xbutton.button - 1));
4690 					mask = e.xbutton.state & ~bmask;
4691 				}
4692 				break;
4693 			case Expose:
4694 				dispatch_event(&e);
4695 				break;
4696 			default:
4697 				break;
4698 			}
4699 		}
4700 		else
4701 		{
4702 			if (FQueryPointer(
4703 				    dpy, Scr.Root, &JunkRoot, &JunkChild,
4704 				    &JunkX, &JunkY, &JunkX, &JunkY, &mask) ==
4705 			    False)
4706 			{
4707 				/* pointer is on a different screen - that's
4708 				 * okay here */
4709 			}
4710 			mask &= DEFAULT_ALL_BUTTONS_MASK;
4711 			usleep(1);
4712 		}
4713 		if (use_wait_cursor == 0 && count == 20)
4714 		{
4715 			GrabEm(CRS_WAIT, GRAB_NORMAL);
4716 			use_wait_cursor = 1;
4717 		}
4718 	}
4719 	UngrabEm(GRAB_NORMAL);
4720 	if (use_wait_cursor)
4721 	{
4722 		UngrabEm(GRAB_NORMAL);
4723 		XFlush(dpy);
4724 	}
4725 
4726 	return;
4727 }
4728 
sync_server(int toggle)4729 void sync_server(int toggle)
4730 {
4731 	static Bool synced = False;
4732 
4733 	if (toggle == -1)
4734 	{
4735 		toggle = (synced == False);
4736 	}
4737 	if (toggle == 1)
4738 	{
4739 		synced = True;
4740 	}
4741 	else
4742 	{
4743 		synced = False;
4744 	}
4745 	XSynchronize(dpy, synced);
4746 	XFlush(dpy);
4747 
4748 	return;
4749 }
4750 
is_resizing_event_pending(FvwmWindow * fw)4751 Bool is_resizing_event_pending(FvwmWindow *fw)
4752 {
4753 	XEvent e;
4754 	check_if_event_args args;
4755 
4756 	args.w = FW_W(fw);
4757 	args.do_return_true = False;
4758 	args.do_return_true_cr = False;
4759 	args.cr_value_mask = 0;
4760 	args.ret_does_match = False;
4761 	args.ret_type = 0;
4762 	FCheckIfEvent(dpy, &e, test_resizing_event, (XPointer)&args);
4763 
4764 	return args.ret_does_match;
4765 }
4766 
4767 /* ---------------------------- builtin commands --------------------------- */
4768 
CMD_XSynchronize(F_CMD_ARGS)4769 void CMD_XSynchronize(F_CMD_ARGS)
4770 {
4771 	int toggle;
4772 
4773 	toggle = ParseToggleArgument(action, NULL, -1, 0);
4774 	sync_server(toggle);
4775 
4776 	return;
4777 }
4778 
CMD_XSync(F_CMD_ARGS)4779 void CMD_XSync(F_CMD_ARGS)
4780 {
4781 	XSync(dpy, 0);
4782 
4783 	return;
4784 }
4785