event_handlers.c revision 0bbfda8a
1/*
2 *       Copyright 1988 by Evans & Sutherland Computer Corporation,
3 *                          Salt Lake City, Utah
4 *  Portions Copyright 1989 by the Massachusetts Institute of Technology
5 *                        Cambridge, Massachusetts
6 *
7 * Copyright 1992 Claude Lecommandeur.
8 */
9
10/***********************************************************************
11 *
12 * $XConsortium: events.c,v 1.182 91/07/17 13:59:14 dave Exp $
13 *
14 * twm event handling
15 *
16 * 17-Nov-87 Thomas E. LaStrange                File created
17 *
18 * Do the necessary modification to be integrated in ctwm.
19 * Can no longer be used for the standard twm.
20 *
21 * 22-April-92 Claude Lecommandeur.
22 *
23 *
24 ***********************************************************************/
25
26#include "ctwm.h"
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <sys/time.h>
31
32#include <X11/Xatom.h>
33#include <X11/extensions/shape.h>
34
35#include "add_window.h"
36#include "animate.h"
37#include "clicktofocus.h"
38#include "colormaps.h"
39#include "ctwm_atoms.h"
40#include "events.h"
41#include "event_handlers.h"
42#include "event_internal.h"
43#include "event_names.h"
44#include "functions.h"
45#include "functions_defs.h"
46#include "gram.tab.h"
47#include "iconmgr.h"
48#include "icons.h"
49#include "image.h"
50#include "list.h"
51#include "occupation.h"
52#include "otp.h"
53#include "parse.h"
54#include "screen.h"
55#include "util.h"
56#include "vscreen.h"
57#include "win_decorations.h"
58#include "win_iconify.h"
59#include "win_ops.h"
60#include "win_regions.h"
61#include "win_resize.h"
62#include "win_utils.h"
63#include "workspace_manager.h"
64#include "workspace_utils.h"
65
66
67static void do_key_menu(MenuRoot *menu,         /* menu to pop up */
68                        Window w);             /* invoking window or None */
69
70/* Only called from HandleFocusChange() */
71static void HandleFocusIn(void);
72static void HandleFocusOut(void);
73
74/*
75 * This currently needs to live in the broader scope because of how it's
76 * used in deferred function handling.
77 */
78static char *Action;
79
80static TwmWindow *ButtonWindow; /* button press window structure */
81
82static void SendTakeFocusMessage(TwmWindow *tmp, Time timestamp);
83
84
85static unsigned int set_mask_ignore(unsigned int modifier)
86{
87	modifier &= ~Scr->IgnoreModifier;
88
89	return modifier;
90}
91
92
93/***********************************************************************
94 *
95 *  Procedure:
96 *      HandleColormapNotify - colormap notify event handler
97 *
98 * This procedure handles both a client changing its own colormap, and
99 * a client explicitly installing its colormap itself (only the window
100 * manager should do that, so we must set it correctly).
101 *
102 ***********************************************************************
103 */
104
105void HandleColormapNotify(void)
106{
107	XColormapEvent *cevent = (XColormapEvent *) &Event;
108	ColormapWindow *cwin, **cwins;
109	TwmColormap *cmap;
110	int lost, won, n, number_cwins;
111
112	/*    if (! Tmp_win) return; */
113	if(XFindContext(dpy, cevent->window, ColormapContext,
114	                (XPointer *)&cwin) == XCNOENT) {
115		return;
116	}
117	cmap = cwin->colormap;
118
119	if(cevent->new) {
120		if(XFindContext(dpy, cevent->colormap, ColormapContext,
121		                (XPointer *)&cwin->colormap) == XCNOENT) {
122			cwin->colormap = CreateTwmColormap(cevent->colormap);
123		}
124		else {
125			cwin->colormap->refcnt++;
126		}
127
128		cmap->refcnt--;
129
130		if(cevent->state == ColormapUninstalled) {
131			cmap->state &= ~CM_INSTALLED;
132		}
133		else {
134			cmap->state |= CM_INSTALLED;
135		}
136
137		if(cmap->state & CM_INSTALLABLE) {
138			InstallColormaps(ColormapNotify, NULL);
139		}
140
141		if(cmap->refcnt == 0) {
142			XDeleteContext(dpy, cmap->c, ColormapContext);
143			free(cmap);
144		}
145
146		return;
147	}
148
149	if(cevent->state == ColormapUninstalled &&
150	                (cmap->state & CM_INSTALLABLE)) {
151		if(!(cmap->state & CM_INSTALLED)) {
152			return;
153		}
154		cmap->state &= ~CM_INSTALLED;
155
156		if(!ColortableThrashing) {
157			ColortableThrashing = true;
158			XSync(dpy, 0);
159		}
160
161		if(cevent->serial >= Scr->cmapInfo.first_req) {
162			number_cwins = Scr->cmapInfo.cmaps->number_cwins;
163
164			/*
165			 * Find out which colortables collided.
166			 */
167
168			cwins = Scr->cmapInfo.cmaps->cwins;
169			for(lost = won = -1, n = 0;
170			                (lost == -1 || won == -1) && n < number_cwins;
171			                n++) {
172				if(lost == -1 && cwins[n] == cwin) {
173					lost = n;   /* This is the window which lost its colormap */
174					continue;
175				}
176
177				if(won == -1 &&
178				                cwins[n]->colormap->install_req == cevent->serial) {
179					won = n;    /* This is the window whose colormap caused */
180					continue;   /* the de-install of the previous colormap */
181				}
182			}
183
184			/*
185			** Cases are:
186			** Both the request and the window were found:
187			**          One of the installs made honoring the WM_COLORMAP
188			**          property caused another of the colormaps to be
189			**          de-installed, just mark the scoreboard.
190			**
191			** Only the request was found:
192			**          One of the installs made honoring the WM_COLORMAP
193			**          property caused a window not in the WM_COLORMAP
194			**          list to lose its map.  This happens when the map
195			**          it is losing is one which is trying to be installed,
196			**          but is getting getting de-installed by another map
197			**          in this case, we'll get a scoreable event later,
198			**          this one is meaningless.
199			**
200			** Neither the request nor the window was found:
201			**          Somebody called installcolormap, but it doesn't
202			**          affect the WM_COLORMAP windows.  This case will
203			**          probably never occur.
204			**
205			** Only the window was found:
206			**          One of the WM_COLORMAP windows lost its colormap
207			**          but it wasn't one of the requests known.  This is
208			**          probably because someone did an "InstallColormap".
209			**          The colormap policy is "enforced" by re-installing
210			**          the colormaps which are believed to be correct.
211			*/
212
213			if(won != -1) {
214				if(lost != -1) {
215					/* lower diagonal index calculation */
216					if(lost > won) {
217						n = lost * (lost - 1) / 2 + won;
218					}
219					else {
220						n = won * (won - 1) / 2 + lost;
221					}
222					Scr->cmapInfo.cmaps->scoreboard[n] = 1;
223				}
224				else {
225					/*
226					** One of the cwin installs caused one of the cwin
227					** colormaps to be de-installed, so I'm sure to get an
228					** UninstallNotify for the cwin I know about later.
229					** I haven't got it yet, or the test of CM_INSTALLED
230					** above would have failed.  Turning the CM_INSTALLED
231					** bit back on makes sure we get back here to score
232					** the collision.
233					*/
234					cmap->state |= CM_INSTALLED;
235				}
236			}
237			else if(lost != -1) {
238				InstallColormaps(ColormapNotify, NULL);
239			}
240			else {
241				ColortableThrashing = false; /* Gross Hack for HP WABI. CL. */
242			}
243		}
244	}
245
246	else if(cevent->state == ColormapUninstalled) {
247		cmap->state &= ~CM_INSTALLED;
248	}
249
250	else if(cevent->state == ColormapInstalled) {
251		cmap->state |= CM_INSTALLED;
252	}
253}
254
255
256/*
257 * LastFocusEvent -- skip over focus in/out events for this
258 *              window.
259 */
260
261static XEvent *LastFocusEvent(Window w, XEvent *first)
262{
263	static XEvent current;
264	XEvent *last, new;
265
266	new = *first;
267	last = NULL;
268
269	do {
270		if((new.type == FocusIn || new.type == FocusOut)
271		                && new.xfocus.mode == NotifyNormal
272		                && (new.xfocus.detail == NotifyNonlinear
273		                    || new.xfocus.detail == NotifyPointer
274		                    || new.xfocus.detail == NotifyAncestor
275		                    || (new.xfocus.detail == NotifyNonlinearVirtual)
276		                   )) {
277			current = new;
278			last = &current;
279		}
280
281#ifdef TRACE_FOCUS
282		fprintf(stderr, "%s(): Focus%s 0x%x mode=%d, detail=%d\n",
283		        __func__, new.xfocus.type == FocusIn ? "In" : "Out",
284		        Tmp_win, new.xfocus.mode, new.xfocus.detail);
285#endif
286
287	}
288	while(XCheckWindowEvent(dpy, w, FocusChangeMask, &new));
289	return last;
290}
291
292
293/*
294 * Focus change handlers.
295 *
296 * Depending on how events get called, these are sometimes redundant, as
297 * the Enter event handler does practically all of this anyway.  But
298 * there are presumably ways we can wind up Focus'ing a window without
299 * Enter'ing it as well.
300 *
301 * It's also a little convoluted how these wind up getting called.  With
302 * most events, we call a handler, then handle that event.  However, with
303 * focus, we troll through our list of pending Focus-related events for
304 * the window and just handle the last one, since some could pile up
305 * fast.  That means that, even if we get called for a FocusIn event,
306 * there might be a FocusOut later in the queue, and _that_'s the one we
307 * pick up and handle, and we discard the rest [for that window].  So,
308 * the event handling code calls a single entry point for both types, and
309 * then it figures out which backend handler to actually fire.
310 */
311void
312HandleFocusChange(void)
313{
314	XEvent *event;
315
316	/* If there's no event window, nothing to do */
317	if(!Tmp_win) {
318		return;
319	}
320
321	/*
322	 * Consume all the focus events for the window we're called about and
323	 * grab the last one to process.
324	 *
325	 * XXX It should be guaranteed that the window in the X event in our
326	 * global Event is the same as Tmp_win->w as the event dispatcher
327	 * sets it so.  Maybe we should do both checks on the same var for
328	 * consistency though?
329	 *
330	 * It's not immediately clear how this can wind up returning nothing,
331	 * but if it does, we don't have anything to do either.
332	 */
333	event = LastFocusEvent(Event.xany.window, &Event);
334	if(event == NULL) {
335		return;
336	}
337
338	/*
339	 * Icon managers don't do anything with focus events on themselves,
340	 * so just skip back if this is one.  Done after LastFocusEvent()
341	 * call for efficiency, so we don't fall into this func multiple
342	 * times if multiple events are queued for it.
343	 */
344	if(Tmp_win->isiconmgr) {
345		return;
346	}
347
348#ifdef TRACE_FOCUS
349	fprintf(stderr, "HandleFocus%s(): 0x%x (0x%x, 0x%x), mode=%d, "
350	        "detail=%d\n",
351	        (event->type == FocusIn ? "In" : "Out"),
352	        Tmp_win, Tmp_win->w, event->window, event->mode,
353	        event->detail);
354#endif
355
356	/* And call actual handler */
357	if(event->type == FocusIn) {
358		HandleFocusIn();
359	}
360	else {
361		HandleFocusOut();
362	}
363}
364
365
366static void
367HandleFocusIn(void)
368{
369	if(! Tmp_win->wmhints->input) {
370		return;
371	}
372	if(Scr->Focus == Tmp_win) {
373		return;
374	}
375
376#ifdef EWMH
377	// Handle focus-dependent re-stacking of what we're moving out of.
378	if(Scr->Focus && OtpIsFocusDependent(Scr->Focus)) {
379		OtpUnfocusWindow(Scr->Focus);
380		// NULL's Scr->Focus
381	}
382#endif
383
384	if(Tmp_win->AutoSqueeze && Tmp_win->squeezed) {
385		AutoSqueeze(Tmp_win);
386	}
387	SetFocusVisualAttributes(Tmp_win, true);
388
389#ifdef EWMH
390	// Handle focus-dependent re-stacking of what we're moving in to.
391	if(Tmp_win && OtpIsFocusDependent(Tmp_win)) {
392		OtpFocusWindow(Tmp_win);
393		// Sets Scr->Focus
394	}
395#endif
396
397	// Redundant in EWMH case
398	Scr->Focus = Tmp_win;
399}
400
401
402static void
403HandleFocusOut(void)
404{
405	if(Scr->Focus != Tmp_win) {
406		return;
407	}
408	if(Scr->SloppyFocus) {
409		return;
410	}
411	if(Tmp_win->AutoSqueeze && !Tmp_win->squeezed) {
412		AutoSqueeze(Tmp_win);
413	}
414	SetFocusVisualAttributes(Tmp_win, false);
415
416#ifdef EWMH
417	/*
418	 * X-ref HandleFocusIn() comment.  FocusOut is only leaving a window,
419	 * not entering a new one, so there's only one we may need to
420	 * restack.
421	 */
422	if(Scr->Focus && OtpIsFocusDependent(Scr->Focus)) {
423		OtpUnfocusWindow(Scr->Focus);
424		// NULL's Scr->Focus
425	}
426#endif
427
428	// Redundant in EWMH case
429	Scr->Focus = NULL;
430}
431
432
433
434/*
435 * Only sent if SubstructureNotifyMask is selected on the (root) window.
436 */
437void HandleCirculateNotify()
438{
439	VirtualScreen *vs;
440#ifdef DEBUG_EVENTS
441	fprintf(stderr, "HandleCirculateNotify\n");
442	fprintf(stderr, "event=%x window=%x place=%d\n",
443	        (unsigned)Event.xcirculate.event,
444	        (unsigned)Event.xcirculate.window,
445	        Event.xcirculate.place);
446#endif
447
448	for(vs = Scr->vScreenList; vs; vs = vs->next) {
449		if(Event.xcirculate.event == vs->window) {
450			TwmWindow *twm_win = GetTwmWindow(Event.xcirculate.window);
451
452			if(twm_win) {
453				WinType wt;
454
455				if(Event.xcirculate.window == twm_win->frame) {
456					wt = WinWin;
457				}
458				else if(twm_win->icon &&
459				                Event.xcirculate.window == twm_win->icon->w) {
460					wt = IconWin;
461				}
462				else {
463					return;
464				}
465
466				OtpHandleCirculateNotify(vs,
467				                         twm_win, wt,
468				                         Event.xcirculate.place);
469			}
470		}
471	}
472}
473
474/***********************************************************************
475 *
476 *  Procedure:
477 *      HandleVisibilityNotify - visibility notify event handler
478 *
479 * This routine keeps track of visibility events so that colormap
480 * installation can keep the maximum number of useful colormaps
481 * installed at one time.
482 *
483 ***********************************************************************
484 */
485
486void HandleVisibilityNotify(void)
487{
488	XVisibilityEvent *vevent = (XVisibilityEvent *) &Event;
489	ColormapWindow *cwin;
490	TwmColormap *cmap;
491
492	if(XFindContext(dpy, vevent->window, ColormapContext,
493	                (XPointer *)&cwin) == XCNOENT) {
494		return;
495	}
496
497	/*
498	 * when Saber complains about retreiving an <int> from an <unsigned int>
499	 * just type "touch vevent->state" and "cont"
500	 */
501	cmap = cwin->colormap;
502	if((cmap->state & CM_INSTALLABLE) &&
503	                vevent->state != cwin->visibility &&
504	                (vevent->state == VisibilityFullyObscured ||
505	                 cwin->visibility == VisibilityFullyObscured) &&
506	                cmap->w == cwin->w) {
507		cwin->visibility = vevent->state;
508		InstallWindowColormaps(VisibilityNotify, NULL);
509	}
510	else {
511		cwin->visibility = vevent->state;
512	}
513}
514
515
516/***********************************************************************
517 *
518 *  Procedure:
519 *      HandleKeyRelease - key release event handler
520 *
521 ***********************************************************************
522 */
523
524void HandleKeyRelease(void)
525{
526	if(Tmp_win == Scr->currentvs->wsw->twm_win) {
527		WMgrHandleKeyReleaseEvent(Scr->currentvs, &Event);
528	}
529}
530
531
532
533/*
534 * HandleKeyPress - key press event handler
535 *
536 * When a key is pressed, we may do various things with it.  If we're in
537 * a menu, various keybindings move around in it, others get silently
538 * ignored.  Else, we look through the various bindings set in the config
539 * file and invoke whatever should be.  If none of that matches, and it
540 * seems like some window should have focus, pass the event down to that
541 * window.
542 */
543void HandleKeyPress(void)
544{
545	/*
546	 * If the Info window (f.identify/f.version) is currently up, any key
547	 * press will drop it away.
548	 */
549	if(Scr->InfoWindow.mapped) {
550		XUnmapWindow(dpy, Scr->InfoWindow.win);
551		Scr->InfoWindow.mapped = false;
552	}
553
554
555	/*
556	 * If a menu is up, we interpret various keys as moving around or
557	 * doing things in the menu.  No other key bindings or usages are
558	 * considered.
559	 */
560	if(ActiveMenu != NULL) {
561		MenuItem *item;
562		char *keynam;
563		KeySym keysym;
564		Window junkW;
565
566		item = NULL;
567
568		/* What key was pressed? */
569		keysym = XLookupKeysym((XKeyEvent *) &Event, 0);
570		if(! keysym) {
571			return;
572		}
573		keynam = XKeysymToString(keysym);
574		if(! keynam) {
575			return;
576		}
577
578
579		/*
580		 * Initial handling of the various keystrokes.  Most keys are
581		 * completely handled here; we only descend out into later for
582		 * for Return/Right keys that do invoke-y stuff on menu entries.
583		 */
584		if(keysym == XK_Down || keysym == XK_space) {
585			/*
586			 * Down or Spacebar moves us to the next entry in the menu,
587			 * looping back around to the top when it falls off the
588			 * bottom.
589			 *
590			 * Start with our X and (current+height)Y, then wrap around
591			 * to the top (Y)/into the menu (X) as necessary.
592			 */
593			int xx = Event.xkey.x;
594			int yy = Event.xkey.y + Scr->EntryHeight;
595			int wx, wy;
596			XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy, &wx, &wy, &junkW);
597			if((wy < 0) || (wy > ActiveMenu->height)) {
598				yy -= (wy - (Scr->EntryHeight / 2) - 2);
599			}
600			if((wx < 0) || (wx > ActiveMenu->width)) {
601				xx -= (wx - (ActiveMenu->width / 2));
602			}
603
604			/*
605			 * Move the pointer there.  We'll get a Motion notify from
606			 * the X server as a result, which will fall into the loop in
607			 * UpdateMenu() and handle re-highlighting etc.
608			 */
609			XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
610			             ActiveMenu->width, ActiveMenu->height, xx, yy);
611			return;
612		}
613		else if(keysym == XK_Up || keysym == XK_BackSpace) {
614			/*
615			 * Up/Backspace move up an entry, with details similar in
616			 * reverse to the above.
617			 */
618			int xx = Event.xkey.x;
619			int yy = Event.xkey.y - Scr->EntryHeight;
620			int wx, wy;
621			XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy, &wx, &wy, &junkW);
622			if((wy < 0) || (wy > ActiveMenu->height)) {
623				yy -= (wy - ActiveMenu->height + (Scr->EntryHeight / 2) + 2);
624			}
625			if((wx < 0) || (wx > ActiveMenu->width)) {
626				xx -= (wx - (ActiveMenu->width / 2));
627			}
628			XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
629			             ActiveMenu->width, ActiveMenu->height, xx, yy);
630			return;
631		}
632		else if(keysym == XK_Right || keysym == XK_Return) {
633			/*
634			 * Right/Return mean we're invoking some entry item, so we
635			 * take note of where we are for activating at the end of
636			 * this set of conditionals.
637			 *
638			 * Follow this down into the following if(item) block for
639			 * details, particularly in the subtle differences between
640			 * Right and Return on f.menu entries.
641			 */
642			item = ActiveItem;
643		}
644		else if(keysym == XK_Left || keysym == XK_Escape) {
645			/*
646			 * Left/Escape back up to a higher menu level, or out totally
647			 * from the top.
648			 */
649			MenuRoot *menu;
650
651			/* Leave pinned menus alone though */
652			if(ActiveMenu->pinned) {
653				return;
654			}
655
656			/* Top-level?  Clear out and leave menu mode totally. */
657			if(!ActiveMenu->prev || MenuDepth == 1) {
658				PopDownMenu();
659				XUngrabPointer(dpy, CurrentTime);
660				return;
661			}
662
663			/*
664			 * We're in a sub level.  Figure out various stuff for where
665			 * we are and where we should be in the up-level, clear out
666			 * the windows for this level, and warp us up there.
667			 */
668			int xx = Event.xkey.x;
669			int yy = Event.xkey.y;
670			int wx, wy;
671			menu = ActiveMenu->prev;
672			XTranslateCoordinates(dpy, Scr->Root, menu->w, xx, yy, &wx, &wy, &junkW);
673			xx -= (wx - (menu->width / 2));
674			if(menu->lastactive)
675				yy -= (wy - menu->lastactive->item_num * Scr->EntryHeight -
676				       (Scr->EntryHeight / 2) - 2);
677			else {
678				yy -= (wy - (Scr->EntryHeight / 2) - 2);
679			}
680			XUnmapWindow(dpy, ActiveMenu->w);
681			if(Scr->Shadow) {
682				XUnmapWindow(dpy, ActiveMenu->shadow);
683			}
684			XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
685			             menu->width, menu->height, xx, yy);
686			return;
687		}
688		else if(strlen(keynam) == 1) {
689			/*
690			 * This would mean pressing a more normal (e.g., letter/num)
691			 * key.  These find the first entry starting with a matching
692			 * character and jump to it.
693			 */
694			MenuItem *startitem;
695			int xx = Event.xkey.x;
696			int yy = Event.xkey.y;
697			int wx, wy;
698
699			startitem = ActiveItem ? ActiveItem : ActiveMenu->first;
700			item = startitem->next;
701			if(item == NULL) {
702				item = ActiveMenu->first;
703			}
704			unsigned int keymod = (Event.xkey.state & mods_used);
705			keymod = set_mask_ignore(keymod);
706
707			while(item != startitem) {
708				bool matched = false;
709				size_t offset = 0;
710				switch(item->item [0]) {
711					case '^' :
712						if((keymod & ControlMask) &&
713						                (keynam [0] == Tolower(item->item [1]))) {
714							matched = true;
715						}
716						break;
717					case '~' :
718						if((keymod & Mod1Mask) &&
719						                (keynam [0] == Tolower(item->item [1]))) {
720							matched = true;
721						}
722						break;
723					case ' ' :
724						offset = 1;
725					default :
726						if(((Scr->IgnoreCaseInMenuSelection) &&
727						                (keynam [0] == Tolower(item->item [offset]))) ||
728
729						                ((keymod & ShiftMask) && Isupper(item->item [offset]) &&
730						                 (keynam [0] == Tolower(item->item [offset]))) ||
731
732						                (!(keymod & ShiftMask) && Islower(item->item [offset]) &&
733						                 (keynam [0] == item->item [offset]))) {
734							matched = true;
735						}
736						break;
737				}
738				if(matched) {
739					break;
740				}
741				item = item->next;
742				if(item == NULL) {
743					item = ActiveMenu->first;
744				}
745			}
746			if(item == startitem) {
747				return;
748			}
749			wx = ActiveMenu->width / 2;
750			wy = (item->item_num * Scr->EntryHeight) + (Scr->EntryHeight / 2) + 2;
751			XTranslateCoordinates(dpy, ActiveMenu->w, Scr->Root, wx, wy, &xx, &yy, &junkW);
752			XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
753			             ActiveMenu->width, ActiveMenu->height, xx, yy);
754			return;
755		}
756		else {
757			/* Other keys get ignored */
758			return;
759		}
760
761
762		/*
763		 * So if we get here, the key pressed was a Right/Return on an
764		 * entry to select it (chosen entry now in item).  Every other
765		 * case is have been completely handled in the block above and
766		 * would have already returned.
767		 *
768		 * So item should always be the entry we just tried to invoke.
769		 * I'm not sure how it could be empty, but if it is, we just hop
770		 * ourselves out of the menu.  Otherwise, we do whatever we want
771		 * to do with the entry type we're on.
772		 */
773		if(item) {
774			switch(item->func) {
775				/* f.nop and f.title, we just silently let pass */
776				case 0 :
777				case F_TITLE :
778					break;
779
780				/* If it's a f.menu, there's more magic to do */
781				case F_MENU: {
782					/*
783					 * Return is treated separately from Right.  It
784					 * "invokes" the menu item, which immediately calls
785					 * whatever the default menu entry is (which may be
786					 * nothing).
787					 */
788					if(!strcmp(keynam, "Return")) {
789						if(ActiveMenu == Scr->Workspaces) {
790							/*
791							 * f.menu "TwmWorkspaces".  The "invocation"
792							 * of this jumps to the workspace in
793							 * question, as if it were a default entry of
794							 * f.gotoworkspace.
795							 *
796							 * XXX Grody magic.  Maybe this should be
797							 * unwound to a default entry...
798							 */
799							PopDownMenu();
800							XUngrabPointer(dpy, CurrentTime);
801							GotoWorkSpaceByName(Scr->currentvs, item->action + 8);
802						}
803						else {
804							/*
805							 * Calling the f.menu handler invokes the
806							 * default action.  We handle popping out of
807							 * menus ourselves.
808							 */
809							ExecuteFunction(item->func, item->action,
810							                ButtonWindow ? ButtonWindow->frame : None,
811							                ButtonWindow, &Event, Context, false);
812							PopDownMenu();
813						}
814
815						/*
816						 * Whatever invocation Return does is done, so we
817						 * are too.
818						 */
819						return;
820					}
821
822					/*
823					 * Right arrow causes opening up a sub-f.menu.  Open
824					 * it up in the appropriate place, [re-]set
825					 * highlights, and call do_key_menu() to do a lot of
826					 * the internals of it.
827					 */
828					int xx = Event.xkey.x;
829					int yy = Event.xkey.y;
830					int wx, wy;
831					XTranslateCoordinates(dpy, Scr->Root, ActiveMenu->w, xx, yy,
832					                      &wx, &wy, &junkW);
833					if(ActiveItem) {
834						ActiveItem->state = 0;
835						PaintEntry(ActiveMenu, ActiveItem, false);
836						ActiveItem = NULL;
837					}
838					xx -= (wx - ActiveMenu->width);
839					yy -= (wy - item->item_num * Scr->EntryHeight - (Scr->EntryHeight / 2) - 2);
840					Event.xkey.x_root = xx;
841					Event.xkey.y_root = yy;
842					XWarpPointer(dpy, Scr->Root, Scr->Root, Event.xkey.x, Event.xkey.y,
843					             ActiveMenu->width, ActiveMenu->height, xx, yy);
844					if(ActiveMenu == Scr->Workspaces) {
845						CurrentSelectedWorkspace = item->item;
846					}
847					do_key_menu(item->sub, None);
848					CurrentSelectedWorkspace = NULL;
849					break;
850				}
851
852				/*
853				 * Any other f.something.  Pop down the menu (unless
854				 * we're trying to pin it up), and invoke the function.
855				 */
856				default :
857					if(item->func != F_PIN) {
858						PopDownMenu();
859					}
860					ExecuteFunction(item->func, item->action,
861					                ButtonWindow ? ButtonWindow->frame : None,
862					                ButtonWindow, &Event, Context, false);
863			}
864
865			/* Done whatever invocation of the entry we need */
866		}
867		else {
868			/* Was no item; pop out of the menu */
869			PopDownMenu();
870			XUngrabPointer(dpy, CurrentTime);
871		}
872
873		/*
874		 * We're done handling the keypress in a menu, so there's nothing
875		 * else to do.
876		 */
877		return;
878	}
879
880
881	/*
882	 * Not in a menu, so we loop through our various bindings.  First,
883	 * figure out what context we're in.  This goes in a global var,
884	 * presumably because stuff way down the chain of invoking some item
885	 * may need to refer up to it.
886	 */
887	Context = C_NO_CONTEXT;
888	if(Event.xany.window == Scr->Root) {
889		if(AlternateContext) {
890			XUngrabPointer(dpy, CurrentTime);
891			XUngrabKeyboard(dpy, CurrentTime);
892			AlternateContext = false;
893			Context = C_ALTERNATE;
894		}
895		else if(AlternateKeymap && Event.xkey.subwindow) {
896			Tmp_win = GetTwmWindow(Event.xkey.subwindow);
897			if(Tmp_win) {
898				Event.xany.window = Tmp_win->w;
899			}
900		}
901		else {
902			Context = C_ROOT;
903		}
904	}
905	if(Tmp_win) {
906		if(0) {
907			/* Dummy to simplify constructions of else if's */
908		}
909#ifdef EWMH_DESKTOP_ROOT
910		else if(Tmp_win->ewmhWindowType == wt_Desktop) {
911			fprintf(stderr, "HandleKeyPress: wt_Desktop -> C_ROOT\n");
912			Context = C_ROOT;
913		}
914#endif
915		else if(Event.xany.window == Tmp_win->title_w) {
916			Context = C_TITLE;
917		}
918		else if(Event.xany.window == Tmp_win->w) {
919			Context = C_WINDOW;
920		}
921		else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w)) {
922			Context = C_ICON;
923		}
924		else if(Event.xany.window == Tmp_win->frame) {
925			Context = C_FRAME;
926		}
927		else if(Tmp_win->iconmanagerlist) {
928			if(Event.xany.window == Tmp_win->iconmanagerlist->w ||
929			                Event.xany.window == Tmp_win->iconmanagerlist->icon) {
930				Context = C_ICONMGR;
931			}
932		}
933		if(Tmp_win->iswspmgr) {
934			Context = C_WORKSPACE;
935		}
936	}
937
938	/*
939	 * We've figured out the Context.  Now see what modifiers we might
940	 * have set...
941	 */
942	unsigned int modifier = (Event.xkey.state | AlternateKeymap) & mods_used;
943	modifier = set_mask_ignore(modifier);
944	if(AlternateKeymap) {
945		XUngrabPointer(dpy, CurrentTime);
946		XUngrabKeyboard(dpy, CurrentTime);
947		AlternateKeymap = 0;
948	}
949
950
951	/*
952	 * Loop over our key bindings and do its thing if we find a matching
953	 * one.
954	 */
955	for(FuncKey *key = Scr->FuncKeyRoot.next; key != NULL; key = key->next) {
956		/*
957		 * Is this what we're trying to invoke?  Gotta be the right key,
958		 * and right modifier; those are easy.
959		 *
960		 * Context is tougher; that has to match what we're expecting as
961		 * well, except in the case of C_NAME, which we always have to
962		 * check to see if it'll match any windows.  So if we have the
963		 * right key and modifier, and it's a C_NAME context, it's a
964		 * "maybe" match and we have to go through the checks.
965		 */
966		if(key->keycode != Event.xkey.keycode ||
967		                key->mods != modifier ||
968		                (key->cont != Context && key->cont != C_NAME)) {
969			/* Nope, not yet */
970			continue;
971		}
972
973		/* 'k, it's a match (or potential match, in C_NAME case) */
974
975		/*
976		 * Weed out the functions that don't make sense to execute from a
977		 * key press
978		 *
979		 * TODO: add keyboard moving/resizing of windows.
980		 */
981		if(key->func == F_MOVE || key->func == F_RESIZE) {
982			return;
983		}
984
985		if(key->cont != C_NAME) {
986			/* Normal context binding; do what it wants */
987			if(key->func == F_MENU) {
988				/*
989				 * f.menu doesn't call the f.menu handler; we directly
990				 * do_key_menu() to pull it up.
991				 *
992				 * Note this is "we called f.menu from a keybinding", not
993				 * "we hit f.menu inside a menu we had up"; that's above.
994				 */
995				ButtonWindow = Tmp_win;
996				do_key_menu(key->menu, (Window) None);
997			}
998			else {
999#ifdef EWMH_DESKTOP_ROOT
1000				if(Context == C_ROOT && Tmp_win != NULL) {
1001					Context = C_WINDOW;
1002					fprintf(stderr, "HandleKeyPress: wt_Desktop -> C_WINDOW\n");
1003				}
1004#endif /* EWMH */
1005				ExecuteFunction(key->func, key->action, Event.xany.window,
1006				                Tmp_win, &Event, Context, false);
1007				if(!AlternateKeymap && !AlternateContext) {
1008					XUngrabPointer(dpy, CurrentTime);
1009				}
1010			}
1011			return;
1012		}
1013		else {
1014			/*
1015			 * By-name binding (i.e., quoted string for the context
1016			 * argument in config; see the manual).  Find windows
1017			 * matching that name and invoke on them, if any.
1018			 *
1019			 * This is the 'maybe' case above; we don't know whether this
1020			 * does something until we try it.  If we don't get a match,
1021			 * we loop back around and keep going through our functions
1022			 * until we do.
1023			 */
1024			bool matched = false;
1025			const size_t len = strlen(key->win_name);
1026
1027			/* try and match the name first */
1028			for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL;
1029			                Tmp_win = Tmp_win->next) {
1030				if(!strncmp(key->win_name, Tmp_win->name, len)) {
1031					matched = true;
1032					ExecuteFunction(key->func, key->action, Tmp_win->frame,
1033					                Tmp_win, &Event, C_FRAME, false);
1034					if(!AlternateKeymap && !AlternateContext) {
1035						XUngrabPointer(dpy, CurrentTime);
1036					}
1037				}
1038			}
1039
1040			/* now try the res_name */
1041			if(!matched) {
1042				for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL;
1043				                Tmp_win = Tmp_win->next) {
1044					if(!strncmp(key->win_name, Tmp_win->class.res_name, len)) {
1045						matched = true;
1046						ExecuteFunction(key->func, key->action, Tmp_win->frame,
1047						                Tmp_win, &Event, C_FRAME, false);
1048						if(!AlternateKeymap && !AlternateContext) {
1049							XUngrabPointer(dpy, CurrentTime);
1050						}
1051					}
1052				}
1053			}
1054
1055			/* now try the res_class */
1056			if(!matched) {
1057				for(Tmp_win = Scr->FirstWindow; Tmp_win != NULL;
1058				                Tmp_win = Tmp_win->next) {
1059					if(!strncmp(key->win_name, Tmp_win->class.res_class, len)) {
1060						matched = true;
1061						ExecuteFunction(key->func, key->action, Tmp_win->frame,
1062						                Tmp_win, &Event, C_FRAME, false);
1063						if(!AlternateKeymap && !AlternateContext) {
1064							XUngrabPointer(dpy, CurrentTime);
1065						}
1066					}
1067				}
1068			}
1069
1070			/*
1071			 * If we wound up invoking something, we're done, so return.
1072			 * If we didn't, we fall through to the next loop through our
1073			 * defined bindings.
1074			 *
1075			 * By-name bindings are unique in this; normal contexts
1076			 * couldn't have multiple matches, so that side of things
1077			 * finishes when it deals with its found match.  But with
1078			 * by-name we could have multiple bindings of a given
1079			 * button/modifier with different names, so we have to go
1080			 * back around to the next run through the for() loop.
1081			 */
1082			if(matched) {
1083				return;
1084			}
1085		} // regular context or by-name?
1086	} // Loop over all bindings
1087
1088
1089	/*
1090	 * If we get here, no function key was bound to the key.  Send it to
1091	 * the client if it was in a window we know about.  Mostly this
1092	 * doesn't happen; clients with focus get their events more directly,
1093	 * but special cases may cause this.
1094	 */
1095	if(Tmp_win) {
1096		if(Context == C_WORKSPACE) {
1097			WMgrHandleKeyPressEvent(Scr->currentvs, &Event);
1098			return;
1099		}
1100		if(Context == C_ICON ||
1101		                Context == C_FRAME ||
1102		                Context == C_TITLE ||
1103		                Context == C_ICONMGR) {
1104			Event.xkey.window = Tmp_win->w;
1105			XSendEvent(dpy, Tmp_win->w, False, KeyPressMask, &Event);
1106		}
1107	}
1108
1109
1110	/* And done */
1111}
1112
1113
1114
1115/***********************************************************************
1116 *
1117 *  Procedure:
1118 *      HandlePropertyNotify - property notify event handler
1119 *
1120 ***********************************************************************
1121 */
1122
1123void HandlePropertyNotify(void)
1124{
1125	Atom actual = None;
1126	int actual_format;
1127	unsigned long nitems, bytesafter;
1128	unsigned long valuemask;            /* mask for create windows */
1129	XSetWindowAttributes attributes;    /* attributes for create windows */
1130	Pixmap pm;
1131	Icon *icon;
1132
1133
1134	/* watch for standard colormap changes */
1135	if(Event.xproperty.window == Scr->Root) {
1136
1137		if(Event.xproperty.atom == XA_WM_CURRENTWORKSPACE) {
1138			unsigned char *prop;
1139			switch(Event.xproperty.state) {
1140				case PropertyNewValue:
1141					if(XGetWindowProperty(dpy, Scr->Root, XA_WM_CURRENTWORKSPACE,
1142					                      0L, 200L, False, XA_STRING, &actual, &actual_format,
1143					                      &nitems, &bytesafter, &prop) == Success) {
1144						if(nitems == 0) {
1145							return;
1146						}
1147						GotoWorkSpaceByName(Scr->vScreenList, (char *)prop);
1148						XFree(prop);
1149					}
1150					return;
1151
1152				default:
1153					return;
1154			}
1155		}
1156		switch(Event.xproperty.state) {
1157			case PropertyNewValue: {
1158				XStandardColormap *maps = NULL;
1159				int nmaps;
1160
1161				if(XGetRGBColormaps(dpy, Scr->Root, &maps, &nmaps,
1162				                    Event.xproperty.atom)) {
1163					/* if got one, then replace any existing entry */
1164					InsertRGBColormap(Event.xproperty.atom, maps, nmaps, true);
1165				}
1166				return;
1167			}
1168
1169			case PropertyDelete:
1170				RemoveRGBColormap(Event.xproperty.atom);
1171				return;
1172		}
1173	}
1174
1175	if(!Tmp_win) {
1176		return;        /* unknown window */
1177	}
1178
1179#define MAX_NAME_LEN 200L               /* truncate to this many */
1180
1181	switch(Event.xproperty.atom) {
1182		case XA_WM_NAME: {
1183			char *prop = GetWMPropertyString(Tmp_win->w, XA_WM_NAME);
1184			if(prop == NULL) {
1185				// Clear
1186				FreeWMPropertyString(Tmp_win->names.wm_name);
1187				Tmp_win->names.wm_name = NULL;
1188				apply_window_name(Tmp_win);
1189				return;
1190			}
1191
1192			if(Tmp_win->names.wm_name != NULL
1193			                && strcmp(Tmp_win->names.wm_name, prop) == 0) {
1194				/* No change, just free and skip out */
1195				free(prop);
1196				return;
1197			}
1198
1199			/* It's changing, free the old and bring in the new */
1200			FreeWMPropertyString(Tmp_win->names.wm_name);
1201			Tmp_win->names.wm_name = prop;
1202
1203			/* Kick the reset process */
1204			apply_window_name(Tmp_win);
1205
1206			break;
1207		}
1208
1209		case XA_WM_ICON_NAME: {
1210			char *prop = GetWMPropertyString(Tmp_win->w, XA_WM_ICON_NAME);
1211			if(prop == NULL) {
1212				// Clear
1213				FreeWMPropertyString(Tmp_win->names.wm_icon_name);
1214				Tmp_win->names.wm_icon_name = NULL;
1215				apply_window_icon_name(Tmp_win);
1216				return;
1217			}
1218
1219			/* No change?  Nothing to do. */
1220			if(Tmp_win->names.wm_icon_name != NULL
1221			                && strcmp(Tmp_win->names.wm_icon_name, prop) == 0) {
1222				free(prop);
1223				return;
1224			}
1225
1226			/* It's changing, free the old and bring in the new */
1227			FreeWMPropertyString(Tmp_win->names.wm_icon_name);
1228			Tmp_win->names.wm_icon_name = prop;
1229
1230			/* And show the new */
1231			apply_window_icon_name(Tmp_win);
1232
1233			break;
1234		}
1235
1236		case XA_WM_HINTS: {
1237			{
1238				XWMHints *nhints = XGetWMHints(dpy, Event.xany.window);
1239				if(!nhints) {
1240					/*
1241					 * I guess this means that window removed the
1242					 * property completely.  Just keep using what we
1243					 * already have for it though; we gotta have
1244					 * something, and whatever it last said is probably
1245					 * more reasonable than getting completely new
1246					 * synthetic hints anyway.
1247					 */
1248					break;
1249				}
1250
1251				XFree(Tmp_win->wmhints);
1252				Tmp_win->wmhints = munge_wmhints(Tmp_win, nhints);
1253			}
1254
1255			icon = Tmp_win->icon;
1256
1257			/*
1258			 * If there already is an icon found in a way that has priority
1259			 * over these hints, disable the flags and remove them from
1260			 * consideration, now and in the future.
1261			 */
1262			if(Tmp_win->forced ||
1263			                (icon && icon->match == match_net_wm_icon)) {
1264				Tmp_win->wmhints->flags &= ~(IconWindowHint | IconPixmapHint | IconMaskHint);
1265			}
1266
1267			if(Tmp_win->wmhints->flags & IconWindowHint) {
1268				if(icon && icon->w) {
1269					int icon_x, icon_y;
1270
1271					/*
1272					 * There's already an icon window.
1273					 * Try to find out where it is; if we succeed, move the new
1274					 * window to where the old one is.
1275					 */
1276					if(XGetGeometry(dpy, icon->w, &JunkRoot, &icon_x,
1277					                &icon_y, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth)) {
1278						/*
1279						 * Move the new icon window to where the old one was.
1280						 */
1281						XMoveWindow(dpy, Tmp_win->wmhints->icon_window, icon_x,
1282						            icon_y);
1283					}
1284
1285					/*
1286					 * If the window is iconic, map the new icon window.
1287					 */
1288					if(Tmp_win->isicon) {
1289						XMapWindow(dpy, Tmp_win->wmhints->icon_window);
1290					}
1291
1292					/*
1293					 * Now, if the old window isn't ours, unmap it, otherwise
1294					 * just get rid of it completely.
1295					 */
1296					if(icon->w_not_ours) {
1297						if(icon->w != Tmp_win->wmhints->icon_window) {
1298							XUnmapWindow(dpy, icon->w);
1299						}
1300					}
1301					else {
1302						XDestroyWindow(dpy, icon->w);
1303					}
1304
1305					/*
1306					 * The new icon window isn't our window, so note that fact
1307					 * so that we don't treat it as ours.
1308					 */
1309					icon->w_not_ours = true;
1310
1311					/*
1312					 * Now make the new window the icon window for this window,
1313					 * and set it up to work as such (select for key presses
1314					 * and button presses/releases, set up the contexts for it,
1315					 * and define the cursor for it).
1316					 */
1317					icon->w = Tmp_win->wmhints->icon_window;
1318					XSelectInput(dpy, icon->w,
1319					             KeyPressMask | ButtonPressMask | ButtonReleaseMask);
1320					XSaveContext(dpy, icon->w, TwmContext, (XPointer)Tmp_win);
1321					XSaveContext(dpy, icon->w, ScreenContext, (XPointer)Scr);
1322					XDefineCursor(dpy, icon->w, Scr->IconCursor);
1323				}
1324			}
1325
1326			if(icon && icon->w &&
1327			                (Tmp_win->wmhints->flags & IconPixmapHint)) {
1328				int x;
1329				unsigned int IconDepth;
1330
1331				if(!XGetGeometry(dpy, Tmp_win->wmhints->icon_pixmap, &JunkRoot,
1332				                 &JunkX, &JunkY, (unsigned int *)&icon->width,
1333				                 (unsigned int *)&icon->height, &JunkBW,
1334				                 &IconDepth)) {
1335					return;
1336				}
1337
1338				pm = XCreatePixmap(dpy, Scr->Root, icon->width,
1339				                   icon->height, Scr->d_depth);
1340
1341				FB(icon->iconc.fore, icon->iconc.back);
1342
1343				if(IconDepth == Scr->d_depth)
1344					XCopyArea(dpy, Tmp_win->wmhints->icon_pixmap, pm, Scr->NormalGC,
1345					          0, 0, icon->width, icon->height, 0, 0);
1346				else
1347					XCopyPlane(dpy, Tmp_win->wmhints->icon_pixmap, pm, Scr->NormalGC,
1348					           0, 0, icon->width, icon->height, 0, 0, 1);
1349
1350				if(icon->image) {
1351					/* Release the existing Image: it may be a shared one (UnknownIcon) */
1352					ReleaseIconImage(icon);
1353					/* conjure up a new Image */
1354					Image *image = AllocImage();
1355					image->pixmap = pm;
1356					image->width  = icon->width;
1357					image->height = icon->height;
1358					icon->image = image;
1359					icon->match = match_icon_pixmap_hint;
1360				}
1361
1362				valuemask = CWBackPixmap;
1363				attributes.background_pixmap = pm;
1364
1365				if(icon->bm_w) {
1366					XDestroyWindow(dpy, icon->bm_w);
1367				}
1368
1369				x = GetIconOffset(icon);
1370				icon->bm_w =
1371				        XCreateWindow(dpy, icon->w, x, 0,
1372				                      icon->width,
1373				                      icon->height,
1374				                      0, Scr->d_depth,
1375				                      CopyFromParent, Scr->d_visual,
1376				                      valuemask, &attributes);
1377
1378				if(!(Tmp_win->wmhints->flags & IconMaskHint)) {
1379					XRectangle rect;
1380
1381					rect.x      = x;
1382					rect.y      = 0;
1383					rect.width  = icon->width;
1384					rect.height = icon->height;
1385					XShapeCombineRectangles(dpy, icon->w, ShapeBounding, 0,
1386					                        0, &rect, 1, ShapeUnion, 0);
1387				}
1388				XMapSubwindows(dpy, icon->w);
1389				RedoIconName(Tmp_win);
1390			}
1391			if(icon && icon->w &&
1392			                (Tmp_win->wmhints->flags & IconMaskHint) &&
1393			                icon->match == match_icon_pixmap_hint) {
1394				/* Only set the mask if the pixmap came from a WM_HINTS too,
1395				 * for easier resource management.
1396				 */
1397				int x;
1398				Pixmap mask;
1399				GC gc;
1400				unsigned int IconWidth, IconHeight, IconDepth;
1401
1402				if(!XGetGeometry(dpy, Tmp_win->wmhints->icon_mask, &JunkRoot,
1403				                 &JunkX, &JunkY, &IconWidth, &IconHeight, &JunkBW,
1404				                 &IconDepth)) {
1405					return;
1406				}
1407				if(IconDepth != 1) {
1408					return;
1409				}
1410
1411				mask = XCreatePixmap(dpy, Scr->Root, IconWidth, IconHeight, 1);
1412				if(!mask) {
1413					return;
1414				}
1415				gc = XCreateGC(dpy, mask, 0, NULL);
1416				if(!gc) {
1417					return;
1418				}
1419				XCopyArea(dpy, Tmp_win->wmhints->icon_mask, mask, gc,
1420				          0, 0, IconWidth, IconHeight, 0, 0);
1421				XFreeGC(dpy, gc);
1422				x = GetIconOffset(icon);
1423				XShapeCombineMask(dpy, icon->bm_w, ShapeBounding, 0, 0, mask,
1424				                  ShapeSet);
1425				XShapeCombineMask(dpy, icon->w,    ShapeBounding, x, 0, mask,
1426				                  ShapeSet);
1427				if(icon->image) {
1428					if(icon->image->mask) {
1429						XFreePixmap(dpy, icon->image->mask);
1430					}
1431					icon->image->mask = mask;
1432					RedoIconName(Tmp_win);
1433				}
1434			}
1435			if(Tmp_win->wmhints->flags & IconPixmapHint) {
1436				AutoPopupMaybe(Tmp_win);
1437			}
1438
1439			break;
1440		}
1441
1442		case XA_WM_NORMAL_HINTS: {
1443			GetWindowSizeHints(Tmp_win);
1444			break;
1445		}
1446		default: {
1447			if(Event.xproperty.atom == XA_WM_COLORMAP_WINDOWS) {
1448				FetchWmColormapWindows(Tmp_win);    /* frees old data */
1449				break;
1450			}
1451			else if(Event.xproperty.atom == XA_WM_PROTOCOLS) {
1452				FetchWmProtocols(Tmp_win);
1453				break;
1454			}
1455			else if(Event.xproperty.atom == XA_WM_OCCUPATION) {
1456				unsigned char *prop;
1457				if(XGetWindowProperty(dpy, Tmp_win->w, Event.xproperty.atom, 0L, MAX_NAME_LEN,
1458				                      False,
1459				                      XA_STRING, &actual, &actual_format, &nitems,
1460				                      &bytesafter, &prop) != Success ||
1461				                actual == None) {
1462					return;
1463				}
1464				ChangeOccupation(Tmp_win, GetMaskFromProperty(prop, nitems));
1465				XFree(prop);
1466			}
1467			else if(Event.xproperty.atom == XA_CTWM_WM_NAME) {
1468				char *prop = GetWMPropertyString(Tmp_win->w, XA_CTWM_WM_NAME);
1469				if(prop == NULL) {
1470					// Clearing
1471					FreeWMPropertyString(Tmp_win->names.ctwm_wm_name);
1472					Tmp_win->names.ctwm_wm_name = NULL;
1473					apply_window_name(Tmp_win);
1474					return;
1475				}
1476
1477				if(Tmp_win->names.ctwm_wm_name != NULL
1478				                && strcmp(Tmp_win->names.ctwm_wm_name,
1479				                          prop) == 0) {
1480					/* No change, just free and skip out */
1481					free(prop);
1482					return;
1483				}
1484
1485				/* It's changing, free the old and bring in the new */
1486				FreeWMPropertyString(Tmp_win->names.ctwm_wm_name);
1487				Tmp_win->names.ctwm_wm_name = prop;
1488
1489				/* Kick the reset process */
1490				apply_window_name(Tmp_win);
1491
1492				return;
1493			}
1494			else if(Event.xproperty.atom == XA_CTWM_WM_ICON_NAME) {
1495				char *prop = GetWMPropertyString(Tmp_win->w,
1496				                                 XA_CTWM_WM_ICON_NAME);
1497				if(prop == NULL) {
1498					// Clearing
1499					FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name);
1500					Tmp_win->names.ctwm_wm_icon_name = NULL;
1501					apply_window_icon_name(Tmp_win);
1502					return;
1503				}
1504
1505				if(Tmp_win->names.ctwm_wm_icon_name != NULL
1506				                && strcmp(Tmp_win->names.ctwm_wm_icon_name,
1507				                          prop) == 0) {
1508					/* No change, just free and skip out */
1509					free(prop);
1510					return;
1511				}
1512
1513				/* It's changing, free the old and bring in the new */
1514				FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name);
1515				Tmp_win->names.ctwm_wm_icon_name = prop;
1516
1517				/* Kick the reset process */
1518				apply_window_icon_name(Tmp_win);
1519
1520				break;
1521			}
1522#ifdef EWMH
1523			else if(EwmhHandlePropertyNotify(&Event.xproperty, Tmp_win)) {
1524				/* event handled */
1525			}
1526#endif /* EWMH */
1527			break;
1528		}
1529	}
1530}
1531
1532
1533/***********************************************************************
1534 *
1535 *  Procedure:
1536 *      HandleClientMessage - client message event handler
1537 *
1538 ***********************************************************************
1539 */
1540
1541void HandleClientMessage(void)
1542{
1543
1544	if(Event.xclient.message_type == XA_WM_CHANGE_STATE) {
1545		if(Tmp_win != NULL) {
1546			if(Event.xclient.data.l[0] == IconicState && !Tmp_win->isicon) {
1547				XEvent button;
1548				XQueryPointer(dpy, Scr->Root, &JunkRoot, &JunkChild,
1549				              &(button.xmotion.x_root),
1550				              &(button.xmotion.y_root),
1551				              &JunkX, &JunkY, &JunkMask);
1552
1553				ExecuteFunction(F_ICONIFY, NULL, Event.xany.window,
1554				                Tmp_win, &button, FRAME, false);
1555				XUngrabPointer(dpy, CurrentTime);
1556			}
1557		}
1558		return;
1559	}
1560
1561#ifdef EWMH
1562	if(EwmhClientMessage(&Event.xclient)) {
1563		return;
1564	}
1565#endif
1566
1567	else if((Event.xclient.message_type == XA_WM_PROTOCOLS) &&
1568	                (Event.xclient.data.l[0] == XA_WM_END_OF_ANIMATION)) {
1569		if(Animating > 0) {
1570			Animating--;
1571		}
1572		else {
1573			fprintf(stderr, "!! end of unknown animation !!\n");
1574		}
1575	}
1576}
1577
1578
1579/***********************************************************************
1580 *
1581 *  Procedure:
1582 *      HandleExpose - expose event handler
1583 *
1584 ***********************************************************************
1585 */
1586
1587static void flush_expose(Window w);
1588
1589void HandleExpose(void)
1590{
1591	MenuRoot *tmp;
1592	VirtualScreen *vs;
1593
1594	if(XFindContext(dpy, Event.xany.window, MenuContext, (XPointer *)&tmp) == 0) {
1595		PaintMenu(tmp, &Event);
1596		return;
1597	}
1598
1599	if(Event.xexpose.count != 0) {
1600		return;
1601	}
1602
1603	if(Event.xany.window == Scr->InfoWindow.win && Scr->InfoWindow.mapped) {
1604		draw_info_window();
1605		flush_expose(Event.xany.window);
1606	}
1607	else if(Tmp_win != NULL) {
1608		if(Scr->use3Dborders && (Event.xany.window == Tmp_win->frame)) {
1609			PaintBorders(Tmp_win, ((Tmp_win == Scr->Focus) ? true : false));
1610			flush_expose(Event.xany.window);
1611			return;
1612		}
1613		else if(Event.xany.window == Tmp_win->title_w) {
1614			PaintTitle(Tmp_win);
1615			flush_expose(Event.xany.window);
1616			return;
1617		}
1618		else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w) &&
1619		                ! Scr->NoIconTitlebar &&
1620		                ! LookInList(Scr->NoIconTitle, Tmp_win->name, &Tmp_win->class)) {
1621			PaintIcon(Tmp_win);
1622			flush_expose(Event.xany.window);
1623			return;
1624		}
1625		else if(Tmp_win->titlebuttons) {
1626			int i;
1627			TBWindow *tbw;
1628			Window w = Event.xany.window;
1629			int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1630
1631			/*
1632			 * This looks an awful lot like a manual reimplementation of
1633			 * PaintTitleButtons().  It's not quite though, it's just
1634			 * looking up one button to paint it.  And it would be a
1635			 * little grody trying to shoehorn it in.
1636			 */
1637			for(i = 0, tbw = Tmp_win->titlebuttons; i < nb; i++, tbw++) {
1638				if(w == tbw->window) {
1639					PaintTitleButton(Tmp_win, tbw);
1640					flush_expose(tbw->window);
1641					return;
1642				}
1643			}
1644		}
1645		for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
1646			if(Tmp_win == vs->wsw->twm_win) {
1647				WMgrHandleExposeEvent(vs, &Event);
1648				flush_expose(Event.xany.window);
1649				return;
1650			}
1651		}
1652		if(Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) {
1653			/*
1654			 * The occupyWindow has a bunch of sub-windows for each
1655			 * button in it, and each of them wind up getting Expose
1656			 * events kicked for them.  The upshot is that we re-paint
1657			 * the occupy window once for itself, and then once for each
1658			 * button inside it.  We'll always get one for the window
1659			 * itself, so just paint it for that one, and ignore the rest
1660			 * of the events.
1661			 *
1662			 * XXX Maybe a better solution is just to mask off Expose
1663			 * events for the other windows...
1664			 */
1665			if(Event.xany.window == Scr->workSpaceMgr.occupyWindow->w) {
1666				PaintOccupyWindow();
1667				flush_expose(Event.xany.window);
1668			}
1669			return;
1670		}
1671		else if(Tmp_win->iconmanagerlist) {
1672			WList *iconmanagerlist = Tmp_win->iconmanagerlist;
1673
1674			if(Event.xany.window == iconmanagerlist->w) {
1675				DrawIconManagerIconName(Tmp_win);
1676				flush_expose(Event.xany.window);
1677				return;
1678			}
1679			else if(Event.xany.window == iconmanagerlist->icon) {
1680				ShowIconifiedIcon(Tmp_win);
1681				flush_expose(Event.xany.window);
1682				return;
1683			}
1684		}
1685	}
1686}
1687
1688
1689static void remove_window_from_ring(TwmWindow *tmp)
1690{
1691	TwmWindow *prev = tmp->ring.prev, *next = tmp->ring.next;
1692
1693	if(enter_win == tmp) {
1694		enter_flag = false;
1695		enter_win = NULL;
1696	}
1697	if(raise_win == Tmp_win) {
1698		raise_win = NULL;
1699	}
1700	if(leave_win == tmp) {
1701		leave_flag = false;
1702		leave_win = NULL;
1703	}
1704	if(lower_win == Tmp_win) {
1705		lower_win = NULL;
1706	}
1707
1708	/*
1709	 * 1. Unlink window
1710	 * 2. If window was only thing in ring, null out ring
1711	 * 3. If window was ring leader, set to next (or null)
1712	 */
1713	if(prev) {
1714		prev->ring.next = next;
1715	}
1716	if(next) {
1717		next->ring.prev = prev;
1718	}
1719	if(Scr->Ring == tmp) {
1720		Scr->Ring = (next != tmp ? next : NULL);
1721	}
1722
1723	if(!Scr->Ring || Scr->RingLeader == tmp) {
1724		Scr->RingLeader = Scr->Ring;
1725	}
1726}
1727
1728
1729/***********************************************************************
1730 *
1731 *  Procedure:
1732 *      HandleDestroyNotify - DestroyNotify event handler
1733 *
1734 ***********************************************************************
1735 */
1736
1737void HandleDestroyNotify(void)
1738{
1739	/*
1740	 * Warning, this is also called by HandleUnmapNotify; if it ever needs to
1741	 * look at the event, HandleUnmapNotify will have to mash the UnmapNotify
1742	 * into a DestroyNotify.
1743	 */
1744
1745	if(Tmp_win == NULL) {
1746		return;
1747	}
1748
1749	RemoveWindowFromRegion(Tmp_win);
1750
1751	if(Tmp_win->icon != NULL) {
1752		OtpRemove(Tmp_win, IconWin);
1753	}
1754	OtpRemove(Tmp_win, WinWin);
1755
1756#ifdef EWMH
1757	/* Remove the old window from the EWMH client list */
1758	EwmhDeleteClientWindow(Tmp_win);
1759	EwmhSet_NET_CLIENT_LIST_STACKING();
1760#endif /* EWMH */
1761	if(Tmp_win == Scr->Focus) {
1762		Scr->Focus = NULL;
1763		FocusOnRoot();
1764	}
1765	if(Scr->SaveWorkspaceFocus) {
1766		struct WorkSpace *ws;
1767		for(ws = Scr->workSpaceMgr.workSpaceList; ws != NULL; ws = ws->next) {
1768			if(ws->save_focus == Tmp_win) {
1769				ws->save_focus = NULL;
1770			}
1771		}
1772	}
1773	XDeleteContext(dpy, Tmp_win->w, TwmContext);
1774	XDeleteContext(dpy, Tmp_win->w, ScreenContext);
1775	XDeleteContext(dpy, Tmp_win->frame, TwmContext);
1776	XDeleteContext(dpy, Tmp_win->frame, ScreenContext);
1777	if(Tmp_win->icon && Tmp_win->icon->w) {
1778		XDeleteContext(dpy, Tmp_win->icon->w, TwmContext);
1779		XDeleteContext(dpy, Tmp_win->icon->w, ScreenContext);
1780	}
1781	if(Tmp_win->title_height) {
1782		int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1783
1784		XDeleteContext(dpy, Tmp_win->title_w, TwmContext);
1785		XDeleteContext(dpy, Tmp_win->title_w, ScreenContext);
1786		if(Tmp_win->hilite_wl) {
1787			XDeleteContext(dpy, Tmp_win->hilite_wl, TwmContext);
1788			XDeleteContext(dpy, Tmp_win->hilite_wl, ScreenContext);
1789		}
1790		if(Tmp_win->hilite_wr) {
1791			XDeleteContext(dpy, Tmp_win->hilite_wr, TwmContext);
1792			XDeleteContext(dpy, Tmp_win->hilite_wr, ScreenContext);
1793		}
1794		if(Tmp_win->lolite_wr) {
1795			XDeleteContext(dpy, Tmp_win->lolite_wr, TwmContext);
1796			XDeleteContext(dpy, Tmp_win->lolite_wr, ScreenContext);
1797		}
1798		if(Tmp_win->lolite_wl) {
1799			XDeleteContext(dpy, Tmp_win->lolite_wl, TwmContext);
1800			XDeleteContext(dpy, Tmp_win->lolite_wl, ScreenContext);
1801		}
1802		if(Tmp_win->titlebuttons) {
1803			int i;
1804
1805			for(i = 0; i < nb; i++) {
1806				XDeleteContext(dpy, Tmp_win->titlebuttons[i].window,
1807				               TwmContext);
1808				XDeleteContext(dpy, Tmp_win->titlebuttons[i].window,
1809				               ScreenContext);
1810			}
1811		}
1812		/*
1813		 * The hilite_wl etc windows don't need to be XDestroyWindow()ed
1814		 * since that will happen when the parent is destroyed (??)
1815		 */
1816	}
1817
1818	if(Scr->cmapInfo.cmaps == &Tmp_win->cmaps) {
1819		InstallColormaps(DestroyNotify, &Scr->RootColormaps);
1820	}
1821
1822	/*
1823	 * TwmWindows contain the following pointers
1824	 *
1825	 *     1.  (obsolete)
1826	 *     2.  name
1827	 *     3.  icon_name
1828	 *     4.  wmhints
1829	 *     5.  class.res_name
1830	 *     6.  class.res_class
1831	 *     7.  list
1832	 *     8.  iconmgrp
1833	 *     9.  cwins
1834	 *     10. titlebuttons
1835	 *     11. window ring
1836	 *     12. squeeze_info (delete if squeeze_info_copied)
1837	 *     13. HiliteImage
1838	 *     14. iconslist
1839	 */
1840	WMapRemoveWindow(Tmp_win);
1841	if(Tmp_win->gray) {
1842		XFreePixmap(dpy, Tmp_win->gray);
1843	}
1844
1845	/*
1846	 * According to the manual page, the following destroys all child windows
1847	 * of the frame too, which is most of the windows we're concerned with, so
1848	 * anything related to them must be done before here.
1849	 * Icons are not child windows.
1850	 */
1851	XDestroyWindow(dpy, Tmp_win->frame);
1852	DeleteIconsList(Tmp_win);                                   /* 14 */
1853	if(Tmp_win->icon) {
1854		Icon *icon = Tmp_win->icon;
1855		if(icon->w && !icon->w_not_ours) {
1856			IconDown(Tmp_win);
1857		}
1858		DeleteIcon(icon);
1859		Tmp_win->icon = NULL;
1860	}
1861	Tmp_win->occupation = 0;
1862	RemoveIconManager(Tmp_win);                                 /* 7 */
1863	if(Scr->FirstWindow == Tmp_win) {
1864		Scr->FirstWindow = Tmp_win->next;
1865	}
1866	if(Tmp_win->prev != NULL) {
1867		Tmp_win->prev->next = Tmp_win->next;
1868	}
1869	if(Tmp_win->next != NULL) {
1870		Tmp_win->next->prev = Tmp_win->prev;
1871	}
1872	if(Tmp_win->auto_raise) {
1873		Scr->NumAutoRaises--;
1874	}
1875	if(Tmp_win->auto_lower) {
1876		Scr->NumAutoLowers--;
1877	}
1878
1879	FreeWMPropertyString(Tmp_win->names.ctwm_wm_name); // 2
1880	FreeWMPropertyString(Tmp_win->names.wm_name);      // 2
1881	FreeWMPropertyString(Tmp_win->names.ctwm_wm_icon_name); // 3
1882	FreeWMPropertyString(Tmp_win->names.wm_icon_name); // 3
1883#ifdef EWMH
1884	FreeWMPropertyString(Tmp_win->names.net_wm_name);      // 2
1885	FreeWMPropertyString(Tmp_win->names.net_wm_icon_name); // 3
1886#endif
1887
1888	XFree(Tmp_win->wmhints);                                    /* 4 */
1889	if(Tmp_win->class.res_name && Tmp_win->class.res_name != NoName) { /* 5 */
1890		XFree(Tmp_win->class.res_name);
1891	}
1892	if(Tmp_win->class.res_class && Tmp_win->class.res_class != NoName) { /* 6 */
1893		XFree(Tmp_win->class.res_class);
1894	}
1895	free_cwins(Tmp_win);                                        /* 9 */
1896	if(Tmp_win->titlebuttons) {                                 /* 10 */
1897		free(Tmp_win->titlebuttons);
1898		Tmp_win->titlebuttons = NULL;
1899	}
1900
1901	remove_window_from_ring(Tmp_win);                           /* 11 */
1902	if(Tmp_win->squeeze_info_copied) {                          /* 12 */
1903		free(Tmp_win->squeeze_info);
1904		Tmp_win->squeeze_info = NULL;
1905	}
1906	DeleteHighlightWindows(Tmp_win);                            /* 13 */
1907
1908	free(Tmp_win);
1909	Tmp_win = NULL;
1910
1911	if(Scr->ClickToFocus || Scr->SloppyFocus) {
1912		set_last_window(Scr->currentvs->wsw->currentwspc);
1913	}
1914}
1915
1916
1917void
1918HandleCreateNotify(void)
1919{
1920#ifdef DEBUG_EVENTS
1921	fprintf(stderr, "CreateNotify w = 0x%x\n",
1922	        (unsigned)Event.xcreatewindow.window);
1923	fflush(stderr);
1924	XBell(dpy, 0);
1925	XSync(dpy, 0);
1926#endif
1927}
1928
1929
1930/***********************************************************************
1931 *
1932 *  Procedure:
1933 *      HandleMapRequest - MapRequest event handler
1934 *
1935 ***********************************************************************
1936 */
1937
1938void HandleMapRequest(void)
1939{
1940	int zoom_save;
1941
1942	Event.xany.window = Event.xmaprequest.window;
1943	Tmp_win = GetTwmWindow(Event.xany.window);
1944
1945	/* If the window has never been mapped before ... */
1946	if(Tmp_win == NULL) {
1947		/* Add decorations. */
1948		VirtualScreen *vs = Scr->currentvs;
1949
1950		Tmp_win = AddWindow(Event.xany.window,
1951		                    AWT_NORMAL,
1952		                    NULL,
1953		                    vs);
1954		if(Tmp_win == NULL) {
1955			return;
1956		}
1957#ifdef EWMH
1958		/* add the new window to the EWMH client list */
1959		EwmhAddClientWindow(Tmp_win);
1960		EwmhSet_NET_CLIENT_LIST_STACKING();
1961
1962		/* Tell it whatever we think of it */
1963		EwmhSet_NET_WM_STATE(Tmp_win, EWMH_STATE_ALL);
1964#endif /* EWMH */
1965	}
1966	else {
1967		/*
1968		 * If the window has been unmapped by the client, it won't be listed
1969		 * in the icon manager.  Add it again, if requested.
1970		 */
1971		if(Tmp_win->iconmanagerlist == NULL) {
1972			AddIconManager(Tmp_win);
1973		}
1974	}
1975
1976	if(Tmp_win->isiconmgr) {
1977		return;
1978	}
1979	if(Tmp_win->squeezed) {
1980		return;
1981	}
1982
1983	if(Scr->WindowMask) {
1984		XRaiseWindow(dpy, Scr->WindowMask);
1985	}
1986
1987	/* If it's not merely iconified, and we have hints, use them. */
1988	if(! Tmp_win->isicon) {
1989		int state;
1990		Window icon;
1991
1992		state = NormalState;
1993		/* use WM_STATE if enabled */
1994		if(!(RestartPreviousState && GetWMState(Tmp_win->w, &state, &icon) &&
1995		                (state == NormalState || state == IconicState || state == InactiveState))) {
1996			if(Tmp_win->wmhints->flags & StateHint) {
1997				state = Tmp_win->wmhints->initial_state;
1998			}
1999		}
2000		switch(state) {
2001			case DontCareState:
2002			case NormalState:
2003			case ZoomState:
2004				if(Tmp_win->StartSqueezed) {
2005					Squeeze(Tmp_win);
2006				}
2007				else {
2008					XMapWindow(dpy, Tmp_win->w);
2009				}
2010				XMapWindow(dpy, Tmp_win->frame);
2011				SetMapStateProp(Tmp_win, NormalState);
2012				SetRaiseWindow(Tmp_win);
2013				Tmp_win->mapped = true;
2014				if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
2015					SetFocus(Tmp_win, CurrentTime);
2016				}
2017				/* kai */
2018				if(Scr->AutoFocusToTransients &&
2019				                Tmp_win->istransient &&
2020				                Tmp_win->wmhints->input) {
2021					SetFocus(Tmp_win, CurrentTime);
2022				}
2023				break;
2024
2025			case InactiveState:
2026				if(!OCCUPY(Tmp_win, Scr->currentvs->wsw->currentwspc) &&
2027				                HandlingEvents && /* to avoid warping during startup */
2028				                LookInList(Scr->WarpOnDeIconify, Tmp_win->name, &Tmp_win->class)) {
2029					if(!Scr->NoRaiseDeicon) {
2030						OtpRaise(Tmp_win, WinWin);
2031					}
2032					AddToWorkSpace(Scr->currentvs->wsw->currentwspc->name, Tmp_win);
2033				}
2034				Tmp_win->mapped = true;
2035				if(Tmp_win->UnmapByMovingFarAway) {
2036					XMoveWindow(dpy, Tmp_win->frame, Scr->rootw + 1, Scr->rooth + 1);
2037					XMapWindow(dpy, Tmp_win->w);
2038					XMapWindow(dpy, Tmp_win->frame);
2039				}
2040				if(Tmp_win->StartSqueezed) {
2041					Squeeze(Tmp_win);
2042				}
2043				break;
2044
2045			case IconicState:
2046				zoom_save = Scr->DoZoom;
2047				Scr->DoZoom = false;
2048				Iconify(Tmp_win, -100, -100);
2049				Scr->DoZoom = zoom_save;
2050				break;
2051		}
2052	}
2053	/* If no hints, or currently an icon, just "deiconify" */
2054	else {
2055		if(!OCCUPY(Tmp_win, Scr->currentvs->wsw->currentwspc) &&
2056		                LookInList(Scr->WarpOnDeIconify, Tmp_win->name, &Tmp_win->class)) {
2057			AddToWorkSpace(Scr->currentvs->wsw->currentwspc->name, Tmp_win);
2058		}
2059		if(1/*OCCUPY (Tmp_win, Scr->workSpaceMgr.activeWSPC)*/) {
2060			if(Tmp_win->StartSqueezed) {
2061				Squeeze(Tmp_win);
2062			}
2063			DeIconify(Tmp_win);
2064			SetRaiseWindow(Tmp_win);
2065		}
2066		else {
2067			Tmp_win->mapped = true;
2068		}
2069	}
2070	if(Tmp_win->mapped) {
2071		WMapMapWindow(Tmp_win);
2072	}
2073	MaybeAnimate = true;
2074}
2075
2076
2077/***********************************************************************
2078 *
2079 *  Procedure:
2080 *      HandleMapNotify - MapNotify event handler
2081 *
2082 ***********************************************************************
2083 */
2084
2085void HandleMapNotify(void)
2086{
2087	if(Tmp_win == NULL) {
2088		return;
2089	}
2090
2091	/*
2092	 * Need to do the grab to avoid race condition of having server send
2093	 * MapNotify to client before the frame gets mapped; this is bad because
2094	 * the client would think that the window has a chance of being viewable
2095	 * when it really isn't.
2096	 */
2097
2098	XGrabServer(dpy);
2099	if(Tmp_win->icon && Tmp_win->icon->w) {
2100		XUnmapWindow(dpy, Tmp_win->icon->w);
2101	}
2102	if(Tmp_win->title_w) {
2103		XMapSubwindows(dpy, Tmp_win->title_w);
2104	}
2105	XMapSubwindows(dpy, Tmp_win->frame);
2106	if(Scr->Focus != Tmp_win && Tmp_win->hilite_wl) {
2107		XUnmapWindow(dpy, Tmp_win->hilite_wl);
2108	}
2109	if(Scr->Focus != Tmp_win && Tmp_win->hilite_wr) {
2110		XUnmapWindow(dpy, Tmp_win->hilite_wr);
2111	}
2112	if(Scr->Focus == Tmp_win && Tmp_win->lolite_wl) {
2113		XUnmapWindow(dpy, Tmp_win->lolite_wl);
2114	}
2115	if(Scr->Focus == Tmp_win && Tmp_win->lolite_wr) {
2116		XUnmapWindow(dpy, Tmp_win->lolite_wr);
2117	}
2118
2119	XMapWindow(dpy, Tmp_win->frame);
2120	XUngrabServer(dpy);
2121	XFlush(dpy);
2122	Tmp_win->mapped = true;
2123	Tmp_win->isicon = false;
2124	Tmp_win->icon_on = false;
2125}
2126
2127
2128/***********************************************************************
2129 *
2130 *  Procedure:
2131 *      HandleUnmapNotify - UnmapNotify event handler
2132 *
2133 ***********************************************************************
2134 */
2135
2136void HandleUnmapNotify(void)
2137{
2138	int dstx, dsty;
2139	Window dumwin;
2140
2141	/*
2142	 * The July 27, 1988 ICCCM spec states that a client wishing to switch
2143	 * to WithdrawnState should send a synthetic UnmapNotify with the
2144	 * event field set to (pseudo-)root, in case the window is already
2145	 * unmapped (which is the case for twm for IconicState).  Unfortunately,
2146	 * we looked for the TwmContext using that field, so try the window
2147	 * field also.
2148	 */
2149	if(Tmp_win == NULL) {
2150		Event.xany.window = Event.xunmap.window;
2151		Tmp_win = GetTwmWindow(Event.xany.window);
2152	}
2153
2154	if(Tmp_win == NULL || Event.xunmap.window == Tmp_win->frame ||
2155	                (Tmp_win->icon && Event.xunmap.window == Tmp_win->icon->w) ||
2156	                (!Tmp_win->mapped && !Tmp_win->isicon)) {
2157		return;
2158	}
2159	/*
2160	    if (Tmp_win == NULL || (!Tmp_win->mapped && !Tmp_win->isicon))
2161	        return;
2162	*/
2163	/*
2164	 * The program may have unmapped the client window, from either
2165	 * NormalState or IconicState.  Handle the transition to WithdrawnState.
2166	 *
2167	 * We need to reparent the window back to the root (so that twm exiting
2168	 * won't cause it to get mapped) and then throw away all state (pretend
2169	 * that we've received a DestroyNotify).
2170	 */
2171	/* Is it the correct behaviour ???
2172	    XDeleteProperty (dpy, Tmp_win->w, XA_WM_OCCUPATION);
2173	*/
2174#ifdef EWMH
2175	EwmhUnmapNotify(Tmp_win);
2176#endif /* EWMH */
2177	XGrabServer(dpy);
2178	if(XTranslateCoordinates(dpy, Event.xunmap.window, Tmp_win->attr.root,
2179	                         0, 0, &dstx, &dsty, &dumwin)) {
2180		XEvent ev;
2181		Bool reparented = XCheckTypedWindowEvent(dpy, Event.xunmap.window,
2182		                  ReparentNotify, &ev);
2183		SetMapStateProp(Tmp_win, WithdrawnState);
2184		if(reparented) {
2185			if(Tmp_win->old_bw) XSetWindowBorderWidth(dpy,
2186				                Event.xunmap.window,
2187				                Tmp_win->old_bw);
2188			if(Tmp_win->wmhints->flags & IconWindowHint) {
2189				XUnmapWindow(dpy, Tmp_win->wmhints->icon_window);
2190			}
2191		}
2192		else {
2193			XReparentWindow(dpy, Event.xunmap.window, Tmp_win->attr.root,
2194			                dstx, dsty);
2195			RestoreWithdrawnLocation(Tmp_win);
2196		}
2197		XRemoveFromSaveSet(dpy, Event.xunmap.window);
2198		XSelectInput(dpy, Event.xunmap.window, NoEventMask);
2199		HandleDestroyNotify();          /* do not need to mash event before */
2200	} /* else window no longer exists and we'll get a destroy notify */
2201	XUngrabServer(dpy);
2202	XFlush(dpy);
2203}
2204
2205
2206/***********************************************************************
2207 *
2208 *  Procedure:
2209 *      HandleMotionNotify - MotionNotify event handler
2210 *
2211 ***********************************************************************
2212 */
2213
2214void HandleMotionNotify(void)
2215{
2216	if(ResizeWindow != (Window) 0) {
2217		XQueryPointer(dpy, Event.xany.window,
2218		              &(Event.xmotion.root), &JunkChild,
2219		              &(Event.xmotion.x_root), &(Event.xmotion.y_root),
2220		              &(Event.xmotion.x), &(Event.xmotion.y),
2221		              &JunkMask);
2222
2223		FixRootEvent(&Event);
2224		/* Set WindowMoved appropriately so that f.deltastop will
2225		   work with resize as well as move. */
2226		if(abs(Event.xmotion.x - ResizeOrigX) >= Scr->MoveDelta
2227		                || abs(Event.xmotion.y - ResizeOrigY) >= Scr->MoveDelta) {
2228			WindowMoved = true;
2229		}
2230
2231		Tmp_win = GetTwmWindow(ResizeWindow);
2232		if(Tmp_win && Tmp_win->winbox) {
2233			XTranslateCoordinates(dpy, Scr->Root, Tmp_win->winbox->window,
2234			                      Event.xmotion.x_root, Event.xmotion.y_root,
2235			                      &(Event.xmotion.x_root), &(Event.xmotion.y_root), &JunkChild);
2236		}
2237		DoResize(Event.xmotion.x_root, Event.xmotion.y_root, Tmp_win);
2238	}
2239	else if(Scr->BorderCursors && Tmp_win && Event.xany.window == Tmp_win->frame) {
2240		SetBorderCursor(Tmp_win, Event.xmotion.x, Event.xmotion.y);
2241	}
2242}
2243
2244
2245/***********************************************************************
2246 *
2247 *  Procedure:
2248 *      HandleButtonRelease - ButtonRelease event handler
2249 *
2250 ***********************************************************************
2251 */
2252void HandleButtonRelease(void)
2253{
2254	int xl, yt, w, h;
2255	unsigned mask;
2256
2257	if(Scr->InfoWindow.mapped) {  /* delete info box on 2nd button release */
2258		if(Context == C_IDENTIFY) {
2259			XUnmapWindow(dpy, Scr->InfoWindow.win);
2260			Scr->InfoWindow.mapped = false;
2261			Context = C_NO_CONTEXT;
2262		}
2263	}
2264
2265	if(DragWindow != None) {
2266		MoveOutline(Scr->XineramaRoot, 0, 0, 0, 0, 0, 0);
2267
2268		Tmp_win = GetTwmWindow(DragWindow);
2269		if(Tmp_win->winbox) {
2270			XTranslateCoordinates(dpy, Scr->Root, Tmp_win->winbox->window,
2271			                      Event.xbutton.x_root, Event.xbutton.y_root,
2272			                      &(Event.xbutton.x_root), &(Event.xbutton.y_root), &JunkChild);
2273		}
2274		if(DragWindow == Tmp_win->frame) {
2275			xl = Event.xbutton.x_root - DragX - Tmp_win->frame_bw;
2276			yt = Event.xbutton.y_root - DragY - Tmp_win->frame_bw;
2277			w = DragWidth + 2 * Tmp_win->frame_bw;
2278			h = DragHeight + 2 * Tmp_win->frame_bw;
2279		}
2280		else {
2281			xl = Event.xbutton.x_root - DragX - DragBW;
2282			yt = Event.xbutton.y_root - DragY - DragBW;
2283			w = DragWidth + 2 * DragBW;
2284			h = DragHeight + 2 * DragBW;
2285		}
2286
2287		if(ConstMove) {
2288			if(ConstMoveDir == MOVE_HORIZ) {
2289				yt = ConstMoveY;
2290			}
2291
2292			if(ConstMoveDir == MOVE_VERT) {
2293				xl = ConstMoveX;
2294			}
2295
2296			if(ConstMoveDir == MOVE_NONE) {
2297				yt = ConstMoveY;
2298				xl = ConstMoveX;
2299			}
2300		}
2301
2302		if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
2303			TryToGrid(Tmp_win, &xl, &yt);
2304		}
2305		if(MoveFunction == F_MOVEPUSH &&
2306		                Scr->OpaqueMove &&
2307		                DragWindow == Tmp_win->frame) {
2308			TryToPush(Tmp_win, xl, yt);
2309		}
2310		if(MoveFunction == F_MOVEPACK ||
2311		                (MoveFunction == F_MOVEPUSH &&
2312		                 DragWindow == Tmp_win->frame)) {
2313			TryToPack(Tmp_win, &xl, &yt);
2314		}
2315		if(Scr->DontMoveOff && MoveFunction != F_FORCEMOVE) {
2316			ConstrainByBorders(Tmp_win, &xl, w, &yt, h);
2317		}
2318
2319		CurrentDragX = xl;
2320		CurrentDragY = yt;
2321		/*
2322		 * sometimes getScreenOf() replies with the wrong window when moving
2323		 * y to a negative number.  Need to figure out why... [XXX]
2324		 * It seems to be because the first XTranslateCoordinates() doesn't
2325		 * translate the coordinates to be relative to XineramaRoot.
2326		 * That in turn is probably because a window is visible in a ws or
2327		 * vs where it shouldn't (inconsistent with its occupation).
2328		 * A problem of this kind was fixed with f.adoptwindow but remains
2329		 * with f.hypermove.
2330		 * As a result, the window remains in the original vs/ws and
2331		 * is irretrievably moved out of view. [Rhialto]
2332		 */
2333		if(xl < 0 || yt < 0 || xl > Scr->rootw || yt > Scr->rooth) {
2334			int odestx, odesty;
2335			int destx, desty;
2336			Window cr;
2337			VirtualScreen *newvs;
2338
2339			XTranslateCoordinates(dpy, Tmp_win->vs->window,
2340			                      Scr->XineramaRoot, xl, yt, &odestx, &odesty, &cr);
2341
2342			newvs = findIfVScreenOf(odestx, odesty);
2343
2344			if(newvs && newvs->wsw && newvs->wsw->currentwspc) {
2345				XTranslateCoordinates(dpy, Scr->XineramaRoot,
2346				                      newvs->window, odestx, odesty,
2347				                      &destx, &desty, &cr);
2348				AddToWorkSpace(newvs->wsw->currentwspc->name, Tmp_win);
2349				RemoveFromWorkSpace(Tmp_win->vs->wsw->currentwspc->name, Tmp_win);
2350				xl = destx;
2351				yt = desty;
2352			}
2353		}
2354		if(DragWindow == Tmp_win->frame) {
2355			SetupWindow(Tmp_win, xl, yt,
2356			            Tmp_win->frame_width, Tmp_win->frame_height, -1);
2357		}
2358		else {
2359			XMoveWindow(dpy, DragWindow, xl, yt);
2360			if(DragWindow == Tmp_win->icon->w) {
2361				Tmp_win->icon->w_x = xl;
2362				Tmp_win->icon->w_y = yt;
2363			}
2364		}
2365
2366		if(!Scr->NoRaiseMove) {  /* && !Scr->OpaqueMove)    opaque already did */
2367			if(DragWindow == Tmp_win->frame) {
2368				OtpRaise(Tmp_win, WinWin);
2369			}
2370			else if(Tmp_win->icon && DragWindow == Tmp_win->icon->w) {
2371				OtpRaise(Tmp_win, IconWin);
2372			}
2373			else {
2374				fprintf(stderr, "ERROR -- events.c:2815\n");
2375			}
2376		}
2377
2378		if(!Scr->OpaqueMove) {
2379			UninstallRootColormap();
2380		}
2381		else {
2382			XSync(dpy, 0);
2383		}
2384
2385		if(Scr->NumAutoRaises) {
2386			enter_flag = true;
2387			enter_win = NULL;
2388			raise_win = ((DragWindow == Tmp_win->frame && !Scr->NoRaiseMove)
2389			             ? Tmp_win : NULL);
2390		}
2391
2392		/* CCC equivalent code for auto lower not needed? */
2393
2394#if 0
2395		if(Scr->NumAutoLowers) {
2396			leave_flag = true;
2397			leave_win = NULL;
2398			lower_win = ((DragWindow == Tmp_win->frame)
2399			             ? Tmp_win : NULL);
2400		}
2401#endif
2402
2403		DragWindow = (Window) 0;
2404		ConstMove = false;
2405	}
2406
2407	if(ResizeWindow != (Window) 0) {
2408		EndResize();
2409	}
2410
2411	if(ActiveMenu != NULL && RootFunction == 0) {
2412		if(ActiveItem) {
2413			int func = ActiveItem->func;
2414			Action = ActiveItem->action;
2415			switch(func) {
2416				case F_TITLE:
2417					if(Scr->StayUpMenus)   {
2418						ButtonPressed = -1;
2419						if(Scr->WarpToDefaultMenuEntry && ActiveMenu->defaultitem) {
2420							WarpCursorToDefaultEntry(ActiveMenu);
2421						}
2422						return;
2423					}
2424					break;
2425				case F_MOVE:
2426				case F_FORCEMOVE:
2427				case F_DESTROY:
2428				case F_DELETE:
2429				case F_DELETEORDESTROY:
2430					ButtonPressed = -1;
2431					break;
2432				case F_CIRCLEUP:
2433				case F_CIRCLEDOWN:
2434				case F_REFRESH:
2435				case F_WARPTOSCREEN:
2436					PopDownMenu();
2437					break;
2438				default:
2439					break;
2440			}
2441			if(func != F_PIN && func != F_MENU) {
2442				PopDownMenu();
2443			}
2444			ExecuteFunction(func, Action,
2445			                ButtonWindow ? ButtonWindow->frame : None,
2446			                ButtonWindow, &Event, Context, true);
2447			Context = C_NO_CONTEXT;
2448			ButtonWindow = NULL;
2449
2450			/* if we are not executing a defered command, then take down the
2451			 * menu
2452			 */
2453			if(ActiveMenu) {
2454				PopDownMenu();
2455			}
2456		}
2457		else if(Scr->StayUpMenus && !ActiveMenu->entered) {
2458			ButtonPressed = -1;
2459			if(Scr->WarpToDefaultMenuEntry && ActiveMenu->defaultitem) {
2460				WarpCursorToDefaultEntry(ActiveMenu);
2461			}
2462			return;
2463		}
2464		else {
2465			PopDownMenu();
2466		}
2467	}
2468
2469	mask = (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask);
2470	switch(Event.xbutton.button) {
2471		case Button1:
2472			mask &= ~Button1Mask;
2473			break;
2474		case Button2:
2475			mask &= ~Button2Mask;
2476			break;
2477		case Button3:
2478			mask &= ~Button3Mask;
2479			break;
2480		case Button4:
2481			mask &= ~Button4Mask;
2482			break;
2483		case Button5:
2484			mask &= ~Button5Mask;
2485			break;
2486	}
2487
2488	if(RootFunction != 0 ||
2489	                ResizeWindow != None ||
2490	                DragWindow != None) {
2491		ButtonPressed = -1;
2492	}
2493
2494	if(AlternateKeymap || AlternateContext) {
2495		ButtonPressed = -1;
2496		return;
2497	}
2498
2499	if(RootFunction == 0 &&
2500	                (Event.xbutton.state & mask) == 0 &&
2501	                DragWindow == None &&
2502	                ResizeWindow == None) {
2503		XUngrabPointer(dpy, CurrentTime);
2504		XUngrabServer(dpy);
2505		XFlush(dpy);
2506		EventHandler[EnterNotify] = HandleEnterNotify;
2507		EventHandler[LeaveNotify] = HandleLeaveNotify;
2508		ButtonPressed = -1;
2509		if(DownIconManager) {
2510			DownIconManager->down = false;
2511			if(Scr->Highlight) {
2512				DrawIconManagerBorder(DownIconManager, false);
2513			}
2514			DownIconManager = NULL;
2515		}
2516		Cancel = false;
2517	}
2518}
2519
2520
2521/*
2522 * Pop up a submenu as a result of moving the mouse right on its entry.
2523 */
2524static void do_menu(MenuRoot *menu,     /* menu to pop up */
2525                    Window w)          /* invoking window or None */
2526{
2527	int x = Event.xbutton.x_root;
2528	int y = Event.xbutton.y_root;
2529	bool center;
2530
2531	if(!Scr->NoGrabServer) {
2532		XGrabServer(dpy);
2533	}
2534	if(w) {
2535		int h = Scr->TBInfo.width - Scr->TBInfo.border;
2536		Window child;
2537
2538		XTranslateCoordinates(dpy, w, Scr->Root, 0, h, &x, &y, &child);
2539		center = false;
2540	}
2541	else {
2542		center = true;
2543	}
2544	if(PopUpMenu(menu, x, y, center)) {
2545		UpdateMenu();
2546	}
2547	else {
2548		XBell(dpy, 0);
2549	}
2550}
2551
2552
2553/*
2554 * Pop up a submenu as a result of hitting the Right arrow key while on
2555 * its entry.  We should try folding these two together a bit more.
2556 */
2557static void do_key_menu(MenuRoot *menu,         /* menu to pop up */
2558                        Window w)              /* invoking window or None */
2559{
2560	int x = Event.xkey.x_root;
2561	int y = Event.xkey.y_root;
2562	bool center;
2563
2564	/* I don't think this is necessary.
2565	    if (!Scr->NoGrabServer) XGrabServer(dpy);
2566	*/
2567	if(w) {
2568		int h = Scr->TBInfo.width - Scr->TBInfo.border;
2569		Window child;
2570
2571		XTranslateCoordinates(dpy, w, Scr->Root, 0, h, &x, &y, &child);
2572		center = false;
2573	}
2574	else {
2575		center = true;
2576	}
2577	if(PopUpMenu(menu, x, y, center)) {
2578		/*
2579		 * Note: UpdateMenu() has the internal re-capture of the event
2580		 * loop to handle in-menu stuff, so this won't actually return
2581		 * until we somehow exit out of that [sub]menu.
2582		 */
2583		UpdateMenu();
2584	}
2585	else {
2586		XBell(dpy, 0);
2587	}
2588
2589}
2590
2591
2592/***********************************************************************
2593 *
2594 *  Procedure:
2595 *      HandleButtonPress - ButtonPress event handler
2596 *
2597 ***********************************************************************
2598 */
2599void HandleButtonPress(void)
2600{
2601	unsigned int modifier;
2602	Cursor cur;
2603	MenuRoot *mr;
2604	FuncButton *tmp = 0;
2605	int func = 0;
2606	Window w;
2607
2608
2609	/* pop down the menu, if any */
2610
2611	if(XFindContext(dpy, Event.xbutton.window, MenuContext,
2612	                (XPointer *) &mr) != XCSUCCESS) {
2613		mr = NULL;
2614	}
2615	if(ActiveMenu && (! ActiveMenu->pinned) &&
2616	                (Event.xbutton.subwindow != ActiveMenu->w)) {
2617		PopDownMenu();
2618		return;
2619	}
2620	if((ActiveMenu != NULL) && (RootFunction != 0) && (mr != ActiveMenu)) {
2621		PopDownMenu();
2622	}
2623
2624	XSync(dpy, 0);
2625	/* XXX - remove? */
2626
2627	/* want menus if we have info box */
2628	if(ButtonPressed != -1 && !Scr->InfoWindow.mapped) {
2629		/* we got another butt press in addition to one still held
2630		 * down, we need to cancel the operation we were doing
2631		 */
2632		Cancel = true;
2633		CurrentDragX = origDragX;
2634		CurrentDragY = origDragY;
2635		if(!menuFromFrameOrWindowOrTitlebar) {
2636			if(Scr->OpaqueMove && DragWindow != None) {
2637				XMoveWindow(dpy, DragWindow, origDragX, origDragY);
2638			}
2639			else {
2640				MoveOutline(Scr->Root, 0, 0, 0, 0, 0, 0);
2641			}
2642		}
2643		XUnmapWindow(dpy, Scr->SizeWindow);
2644		if(!Scr->OpaqueMove) {
2645			UninstallRootColormap();
2646		}
2647		ResizeWindow = None;
2648		DragWindow = None;
2649		cur = LeftButt;
2650		if(Event.xbutton.button == Button2) {
2651			cur = MiddleButt;
2652		}
2653		else if(Event.xbutton.button >= Button3) {
2654			cur = RightButt;
2655		}
2656
2657		XGrabPointer(dpy, Scr->Root, True,
2658		             ButtonReleaseMask | ButtonPressMask,
2659		             GrabModeAsync, GrabModeAsync,
2660		             Scr->Root, cur, CurrentTime);
2661
2662		return;
2663	}
2664	else {
2665		ButtonPressed = Event.xbutton.button;
2666	}
2667
2668	if((ActiveMenu != NULL) && (ActiveMenu->pinned)) {
2669		if(Event.xbutton.window == ActiveMenu->w) {
2670			modifier = (Event.xbutton.state & mods_used);
2671			modifier = set_mask_ignore(modifier);
2672			if((ActiveItem && (ActiveItem->func == F_TITLE)) || (modifier == Mod1Mask)) {
2673				MoveMenu(&Event);
2674				/*ButtonPressed = -1;*/
2675			}
2676		}
2677		Context = C_ROOT;
2678		return;
2679	}
2680
2681	if(ResizeWindow != None ||
2682	                DragWindow != None  ||
2683	                ActiveMenu != NULL) {
2684		return;
2685	}
2686
2687	/* check the title bar buttons */
2688	if(Tmp_win && Tmp_win->title_height && Tmp_win->titlebuttons) {
2689		int i;
2690		TBWindow *tbw;
2691		TitleButtonFunc *tbf;
2692		int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
2693
2694		modifier = Event.xbutton.state & mods_used;
2695		modifier = set_mask_ignore(modifier);
2696
2697		for(i = 0, tbw = Tmp_win->titlebuttons; i < nb; i++, tbw++) {
2698			if(Event.xany.window == tbw->window) {
2699				for(tbf = tbw->info->funs; tbf; tbf = tbf->next) {
2700					if(tbf->num == ButtonPressed
2701					                && tbf->mods == modifier) {
2702						switch(tbf->func) {
2703							/*
2704							 * Opening up a menu doesn't use the f.menu
2705							 * handler, we use our do_menu(); x-ref
2706							 * comments in the handler for details.
2707							 */
2708							case F_MENU :
2709								Context = C_TITLE;
2710								ButtonWindow = Tmp_win;
2711								do_menu(tbf->menuroot, tbw->window);
2712								break;
2713
2714							default :
2715								ExecuteFunction(tbf->func, tbf->action,
2716								                Event.xany.window, Tmp_win,
2717								                &Event, C_TITLE, false);
2718						}
2719						return;
2720					}
2721				}
2722			}
2723		}
2724	}
2725
2726	Context = C_NO_CONTEXT;
2727
2728	if(Event.xany.window == Scr->InfoWindow.win) {
2729		Context = C_IDENTIFY;
2730	}
2731
2732	if(Event.xany.window == Scr->Root) {
2733		if(AlternateContext) {
2734			XUngrabPointer(dpy, CurrentTime);
2735			XUngrabKeyboard(dpy, CurrentTime);
2736			AlternateContext = false;
2737			Context = C_ALTERNATE;
2738		}
2739		else if(AlternateKeymap && Event.xbutton.subwindow) {
2740			int dx, dy;
2741			Window child;
2742
2743			w = Event.xbutton.subwindow;
2744			Tmp_win = GetTwmWindow(w);
2745			if(Tmp_win) {
2746				Event.xany.window    = Tmp_win->frame;
2747				XTranslateCoordinates(dpy, Scr->Root, Tmp_win->frame,
2748				                      Event.xbutton.x, Event.xbutton.y, &dx, &dy, &child);
2749				Event.xbutton.x = dx;
2750				Event.xbutton.x = dy;
2751				Event.xbutton.subwindow = child;
2752			}
2753		}
2754		else {
2755			Context = C_ROOT;
2756		}
2757	}
2758	if(Tmp_win) {
2759		if(Tmp_win->iconmanagerlist && (RootFunction != 0) &&
2760		                ((Event.xany.window == Tmp_win->iconmanagerlist->icon) ||
2761		                 (Event.xany.window == Tmp_win->iconmanagerlist->w))) {
2762			int x, y;
2763
2764			Tmp_win = Tmp_win->iconmanagerlist->iconmgr->twm_win;
2765			XTranslateCoordinates(dpy, Event.xany.window, Tmp_win->w,
2766			                      Event.xbutton.x, Event.xbutton.y,
2767			                      &x, &y, &JunkChild);
2768
2769			Event.xbutton.x = x - Tmp_win->frame_bw3D;
2770			Event.xbutton.y = y - Tmp_win->title_height - Tmp_win->frame_bw3D;
2771			Event.xany.window = Tmp_win->w;
2772			Context = C_WINDOW;
2773		}
2774#ifdef EWMH_DESKTOP_ROOT
2775		else if(Tmp_win->ewmhWindowType == wt_Desktop) {
2776			fprintf(stderr, "HandleButtonPress: wt_Desktop -> C_ROOT\n");
2777			Context = C_ROOT;
2778		}
2779#endif
2780		else if(Event.xany.window == Tmp_win->title_w) {
2781			if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
2782				SetFocus(Tmp_win, CurrentTime);
2783			}
2784			Context = C_TITLE;
2785		}
2786		else if(Event.xany.window == Tmp_win->w) {
2787			if(Scr->ClickToFocus || Scr->RaiseOnClick) {
2788				if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
2789					SetFocus(Tmp_win, CurrentTime);
2790				}
2791				if(Scr->RaiseOnClick) {
2792					OtpRaise(Tmp_win, WinWin);
2793					WMapRaise(Tmp_win);
2794				}
2795				XSync(dpy, 0);
2796				XAllowEvents(dpy, ReplayPointer, CurrentTime);
2797				XSync(dpy, 0);
2798				ButtonPressed = -1;
2799				return;
2800			}
2801			else {
2802				printf("ERROR! ERROR! ERROR! YOU SHOULD NOT BE HERE!!!\n");
2803				Context = C_WINDOW;
2804			}
2805		}
2806		else if(Tmp_win->icon && (Event.xany.window == Tmp_win->icon->w)) {
2807			Context = C_ICON;
2808		}
2809		else if(Event.xany.window == Tmp_win->frame) {
2810			Window chwin;
2811
2812			/* since we now place a button grab on the frame instead
2813			 * of the window, (see GrabButtons() in add_window.c), we
2814			 * need to figure out where the pointer exactly is before
2815			 * assigning Context.  If the pointer is on the application
2816			 * window we will change the event structure to look as if
2817			 * it came from the application window.
2818			 */
2819			if(Event.xbutton.subwindow == Tmp_win->w) {
2820				XTranslateCoordinates(dpy, Event.xany.window, Tmp_win->w,
2821				                      Event.xbutton.x, Event.xbutton.y,
2822				                      &Event.xbutton.x, &Event.xbutton.y,
2823				                      &chwin);
2824				Event.xbutton.window = Tmp_win->w;
2825
2826				if(Tmp_win->iswinbox && chwin) {
2827					int x, y;
2828					XTranslateCoordinates(dpy, Tmp_win->w, chwin,
2829					                      Event.xbutton.x, Event.xbutton.y,
2830					                      &x, &y, &chwin);
2831					if(chwin && (Tmp_win = GetTwmWindow(chwin))) {
2832						Event.xany.window = chwin;
2833						Event.xbutton.x   = x;
2834						Event.xbutton.y   = y;
2835					}
2836				}
2837				Context = C_WINDOW;
2838			}
2839			else if(Event.xbutton.subwindow
2840			                && (Event.xbutton.subwindow == Tmp_win->title_w)) {
2841				Context = C_TITLE;
2842			}
2843			else {
2844				Context = C_FRAME;
2845			}
2846			if(Scr->ClickToFocus && Tmp_win->wmhints->input) {
2847				SetFocus(Tmp_win, CurrentTime);
2848			}
2849		}
2850		else if(Tmp_win->iswspmgr ||
2851		                (Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win)) {
2852			/*Context = C_WINDOW; probably a typo */
2853			Context = C_WORKSPACE;
2854		}
2855		else if(Tmp_win->iconmanagerlist) {
2856			if((Event.xany.window == Tmp_win->iconmanagerlist->icon) ||
2857			                (Event.xany.window == Tmp_win->iconmanagerlist->w)) {
2858				Tmp_win->iconmanagerlist->down = true;
2859				if(Scr->Highlight) {
2860					DrawIconManagerBorder(Tmp_win->iconmanagerlist, false);
2861				}
2862				DownIconManager = Tmp_win->iconmanagerlist;
2863				Context = C_ICONMGR;
2864			}
2865		}
2866	}
2867
2868	/* this section of code checks to see if we were in the middle of
2869	 * a command executed from a menu
2870	 */
2871	if(RootFunction != 0) {
2872		if(Event.xany.window == Scr->Root) {
2873			Window win;
2874			int x, y;
2875
2876			/* if the window was the Root, we don't know for sure it
2877			 * it was the root.  We must check to see if it happened to be
2878			 * inside of a client that was getting button press events.
2879			 */
2880			XTranslateCoordinates(dpy, Scr->Root, Scr->Root,
2881			                      Event.xbutton.x,
2882			                      Event.xbutton.y,
2883			                      &x, &y, &Event.xany.window);
2884
2885			if(Event.xany.window != 0 &&
2886			                (Tmp_win = GetTwmWindow(Event.xany.window))) {
2887				if(Tmp_win->iswinbox) {
2888					XTranslateCoordinates(dpy, Scr->Root, Event.xany.window,
2889					                      x, y, &x, &y, &win);
2890					XTranslateCoordinates(dpy, Event.xany.window, win,
2891					                      x, y, &x, &y, &win);
2892					if(win != 0) {
2893						Event.xany.window = win;
2894					}
2895				}
2896			}
2897			if(Event.xany.window == 0 ||
2898			                !(Tmp_win = GetTwmWindow(Event.xany.window))) {
2899				RootFunction = 0;
2900				XBell(dpy, 0);
2901				return;
2902			}
2903			XTranslateCoordinates(dpy, Scr->Root, Event.xany.window,
2904			                      Event.xbutton.x,
2905			                      Event.xbutton.y,
2906			                      &x, &y, &JunkChild);
2907
2908			Event.xbutton.x = x;
2909			Event.xbutton.y = y;
2910			Context = C_WINDOW;
2911		}
2912		else if(mr != NULL) {
2913			RootFunction = 0;
2914			XBell(dpy, 0);
2915			return;
2916		}
2917
2918		/* make sure we are not trying to move an identify window */
2919		if(Event.xany.window != Scr->InfoWindow.win) {
2920			/*
2921			 * X-ref comment at top of file about Action; this is where
2922			 * we need to use its broader lifespan.
2923			 */
2924			ExecuteFunction(RootFunction, Action, Event.xany.window,
2925			                Tmp_win, &Event, Context, false);
2926		}
2927
2928		RootFunction = 0;
2929		return;
2930	}
2931
2932	ButtonWindow = Tmp_win;
2933
2934	/* if we get to here, we have to execute a function or pop up a
2935	 * menu
2936	 */
2937	modifier = (Event.xbutton.state | AlternateKeymap) & mods_used;
2938	modifier = set_mask_ignore(modifier);
2939	if(AlternateKeymap) {
2940		XUngrabPointer(dpy, CurrentTime);
2941		XUngrabKeyboard(dpy, CurrentTime);
2942		AlternateKeymap = 0;
2943	}
2944	if((Context == C_NO_CONTEXT) || (Context == C_IDENTIFY)) {
2945		return;
2946	}
2947
2948	RootFunction = 0;
2949
2950	/* see if there already is a key defined for this context */
2951	for(tmp = Scr->FuncButtonRoot.next; tmp != NULL; tmp = tmp->next) {
2952		if((tmp->num  == Event.xbutton.button) &&
2953		                (tmp->cont == Context) && (tmp->mods == modifier)) {
2954			break;
2955		}
2956	}
2957	if(tmp) {
2958		func = tmp->func;
2959		switch(func) {
2960			/*
2961			 * f.menu isn't invoked, it's handle magically.  Other funcs
2962			 * we just invoke.  X-ref the f.menu handler for details.
2963			 */
2964			case F_MENU :
2965				do_menu(tmp->menu, (Window) None);
2966				break;
2967
2968			default :
2969				if(func != 0) {
2970					Action = tmp->item ? tmp->item->action : NULL;
2971#ifdef EWMH_DESKTOP_ROOT
2972					if(Context == C_ROOT && Tmp_win != NULL) {
2973						Context = C_WINDOW;
2974						fprintf(stderr, "HandleButtonPress: wt_Desktop -> C_WINDOW\n");
2975					}
2976#endif /* EWMH */
2977					ExecuteFunction(func,
2978					                Action, Event.xany.window, Tmp_win, &Event, Context, false);
2979				}
2980		}
2981	}
2982	else {
2983		if(Tmp_win == Scr->currentvs->wsw->twm_win) {
2984			WMgrHandleButtonEvent(Scr->currentvs, &Event);
2985			return;
2986		}
2987	}
2988	if(Tmp_win == Scr->workSpaceMgr.occupyWindow->twm_win) {
2989		OccupyHandleButtonEvent(&Event);
2990	}
2991	else if(func == 0 && Scr->DefaultFunction.func != 0) {
2992		if(Scr->DefaultFunction.func == F_MENU) {
2993			do_menu(Scr->DefaultFunction.menu, (Window) None);
2994		}
2995		else {
2996			Action = Scr->DefaultFunction.item ?
2997			         Scr->DefaultFunction.item->action : NULL;
2998			ExecuteFunction(Scr->DefaultFunction.func, Action,
2999			                Event.xany.window, Tmp_win, &Event, Context, false);
3000		}
3001	}
3002}
3003
3004
3005/***********************************************************************
3006 *
3007 *  Procedure:
3008 *      HENQueueScanner - EnterNotify event q scanner
3009 *
3010 *      Looks at the queued events and determines if any matching
3011 *      LeaveNotify events or EnterEvents deriving from the
3012 *      termination of a grab are behind this event to allow
3013 *      skipping of unnecessary processing.
3014 *
3015 ***********************************************************************
3016 */
3017
3018typedef struct HENScanArgs {
3019	Window w;           /* Window we are currently entering */
3020	Bool leaves;        /* Any LeaveNotifies found for this window */
3021	Bool inferior;      /* Was NotifyInferior the mode for LeaveNotify */
3022	Bool enters;        /* Any EnterNotify events with NotifyUngrab */
3023} HENScanArgs;
3024
3025/* ARGSUSED*/
3026static Bool HENQueueScanner(Display *display, XEvent *ev, char *_args)
3027{
3028	HENScanArgs *args = (void *)_args;
3029
3030	if(ev->type == LeaveNotify) {
3031		if(ev->xcrossing.window == args->w &&
3032		                ev->xcrossing.mode == NotifyNormal) {
3033			args->leaves = True;
3034			/*
3035			 * Only the last event found matters for the Inferior field.
3036			 */
3037			args->inferior =
3038			        (ev->xcrossing.detail == NotifyInferior);
3039		}
3040	}
3041	else if(ev->type == EnterNotify) {
3042		if(ev->xcrossing.mode == NotifyUngrab) {
3043			args->enters = True;
3044		}
3045	}
3046
3047	return (False);
3048}
3049
3050
3051/***********************************************************************
3052 *
3053 *  Procedure:
3054 *      HandleEnterNotify - EnterNotify event handler
3055 *
3056 ***********************************************************************
3057 */
3058
3059void HandleEnterNotify(void)
3060{
3061	MenuRoot *mr, *tmp;
3062	XEnterWindowEvent *ewp = &Event.xcrossing;
3063	HENScanArgs scanArgs;
3064	XEvent dummy;
3065	VirtualScreen *vs;
3066
3067	/*
3068	 * if we aren't in the middle of menu processing
3069	 */
3070	if(!ActiveMenu) {
3071		/*
3072		 * We're not interested in pseudo Enter/Leave events generated
3073		 * from grab initiations.
3074		 */
3075		if(ewp->mode == NotifyGrab) {
3076			return;
3077		}
3078
3079		/*
3080		 * Scan for Leave and Enter Notify events to see if we can avoid some
3081		 * unnecessary processing.
3082		 */
3083		scanArgs.w = ewp->window;
3084		scanArgs.leaves = scanArgs.enters = False;
3085		XCheckIfEvent(dpy, &dummy, HENQueueScanner, (void *) &scanArgs);
3086
3087		/*
3088		 * if entering root window, restore twm default colormap so that
3089		 * titlebars are legible
3090		 */
3091		if(ewp->window == Scr->Root) {
3092			Window forus_ret;
3093			int focus_rev;
3094
3095			if(!scanArgs.leaves && !scanArgs.enters) {
3096				InstallColormaps(EnterNotify, &Scr->RootColormaps);
3097			}
3098			if(! Scr->FocusRoot) {
3099				return;
3100			}
3101			XGetInputFocus(dpy, &forus_ret, &focus_rev);
3102			if((forus_ret != PointerRoot) && (forus_ret != None)) {
3103				SetFocus(NULL, Event.xcrossing.time);
3104			}
3105			return;
3106		}
3107		for(vs = Scr->vScreenList; vs != NULL; vs = vs->next) {
3108			if(ewp->window == vs->window) {
3109				Scr->Root  = vs->window;
3110				Scr->rootx = Scr->crootx + vs->x;
3111				Scr->rooty = Scr->crooty + vs->y;
3112				Scr->rootw = vs->w;
3113				Scr->rooth = vs->h;
3114				Scr->currentvs = vs;
3115#if 0
3116				fprintf(stderr, "entering new vs : 0x%x, 0x%x, %d, %d, %d, %d\n",
3117				        vs, Scr->Root, vs->x, vs->y, vs->w, vs->h);
3118#endif
3119				return;
3120			}
3121		}
3122
3123		/* Handle RaiseDelay, if any.....
3124		 */
3125		if(RaiseDelay > 0) {
3126			if(Tmp_win && Tmp_win->auto_raise &&
3127			                (!Tmp_win->iconmanagerlist ||
3128			                 Tmp_win->iconmanagerlist->w != ewp->window)) {
3129				ColormapWindow *cwin;
3130				static struct timeval tout, timeout = {0, 12500};
3131
3132				if(XFindContext(dpy, Tmp_win->w, ColormapContext,
3133				                (XPointer *)&cwin) == XCNOENT) {
3134					cwin = NULL;
3135				}
3136
3137				if((ewp->detail != NotifyInferior
3138				                || Tmp_win->frame == ewp->window)
3139				                && (!cwin || cwin->visibility != VisibilityUnobscured)) {
3140					int x, y, px, py, d, i;
3141					Window w;
3142
3143					XQueryPointer(dpy, Scr->Root, &w, &w, &px, &py,
3144					              &d, &d, (unsigned int *)&d);
3145
3146					/* The granularity of RaiseDelay is about 25 ms.
3147					 * The timeout variable is set to 12.5 ms since we
3148					 * pass this way twice each time a twm window is
3149					 * entered.
3150					 */
3151					for(i = 25; i < RaiseDelay; i += 25) {
3152						tout = timeout;
3153						select(0, NULL, NULL, NULL, &tout);
3154						/* Did we leave this window already? */
3155						scanArgs.w = ewp->window;
3156						scanArgs.leaves = scanArgs.enters = False;
3157						XCheckIfEvent(dpy, &dummy, HENQueueScanner,
3158						              (void *) &scanArgs);
3159						if(scanArgs.leaves && !scanArgs.inferior) {
3160							return;
3161						}
3162
3163						XQueryPointer(dpy, Scr->Root, &w, &w, &x, &y,
3164						              &d, &d, (unsigned int *)&d);
3165
3166						/* Has the pointer moved?  If so reset the loop cnt.
3167						 * We want the pointer to be still for RaiseDelay
3168						 * milliseconds before terminating the loop
3169						 */
3170						if(x != px || y != py) {
3171							i = 0;
3172							px = x;
3173							py = y;
3174						}
3175					}
3176				}
3177			}
3178
3179			/*
3180			 * Scan for Leave and Enter Notify events to see if we can avoid some
3181			 * unnecessary processing.
3182			 */
3183			scanArgs.w = ewp->window;
3184			scanArgs.leaves = scanArgs.enters = False;
3185			XCheckIfEvent(dpy, &dummy, HENQueueScanner, (void *) &scanArgs);
3186
3187			/*
3188			 * if entering root window, restore twm default colormap so that
3189			 * titlebars are legible
3190			 */
3191			if(ewp->window == Scr->Root) {
3192				if(!scanArgs.leaves && !scanArgs.enters) {
3193					InstallColormaps(EnterNotify, &Scr->RootColormaps);
3194				}
3195				return;
3196			}
3197		}
3198		/* End of RaiseDelay modification. */
3199
3200		/*
3201		 * if we have an event for a specific one of our windows
3202		 */
3203		if(Tmp_win) {
3204			/*
3205			 * If currently in PointerRoot mode (indicated by FocusRoot), then
3206			 * focus on this window
3207			 */
3208			if(Scr->FocusRoot && (!scanArgs.leaves || scanArgs.inferior)) {
3209				bool accinput;
3210
3211				if(Scr->ShrinkIconTitles &&
3212				                Tmp_win->icon &&
3213				                ewp->window == Tmp_win->icon->w &&
3214				                ewp->detail != NotifyInferior) {
3215					if(Scr->AutoRaiseIcons) {
3216						OtpRaise(Tmp_win, IconWin);
3217					}
3218					ExpandIconTitle(Tmp_win);
3219					return;
3220				}
3221
3222				if(Tmp_win->iconmanagerlist) {
3223					CurrentIconManagerEntry(Tmp_win->iconmanagerlist);
3224				}
3225
3226				accinput = Tmp_win->mapped && Tmp_win->wmhints->input;
3227				if(Tmp_win->iconmanagerlist &&
3228				                ewp->window == Tmp_win->iconmanagerlist->w &&
3229				                !accinput &&
3230				                Tmp_win->iconmanagerlist->iconmgr &&
3231				                Tmp_win->iconmanagerlist->iconmgr->twm_win) {
3232					SetFocus(Tmp_win->iconmanagerlist->iconmgr->twm_win,
3233					         CurrentTime);
3234					return;
3235				}
3236
3237				if(Tmp_win->mapped) {
3238					/*
3239					 * unhighlight old focus window
3240					 */
3241
3242					/*
3243					 * If entering the frame or the icon manager, then do
3244					 * "window activation things":
3245					 *
3246					 *     1.  <highlighting is not done here any more>
3247					 *     2.  install frame colormap
3248					 *     3.  <frame and highlight border not set here>
3249					 *     4.  focus on client window to forward typing
3250					 *     4a. same as 4 but for icon mgr w/with NoTitleFocus
3251					 *     5.  send WM_TAKE_FOCUS if requested
3252					 */
3253					if(Scr->BorderCursors && ewp->window == Tmp_win->frame) {
3254						SetBorderCursor(Tmp_win, ewp->x, ewp->y);
3255					}
3256					if(ewp->window == Tmp_win->frame ||
3257					                (Scr->IconManagerFocus &&
3258					                 Tmp_win->iconmanagerlist &&
3259					                 ewp->window == Tmp_win->iconmanagerlist->w)) {
3260
3261						if(!scanArgs.leaves && !scanArgs.enters) {
3262							InstallColormaps(EnterNotify,       /* 2 */
3263							                 &Scr->RootColormaps);
3264						}
3265
3266						/*
3267						 * Event is in the frame or the icon mgr:
3268						 *
3269						 * "4" -- TitleFocus is set: windows should get
3270						 *        focus as long as they accept input.
3271						 *
3272						 * "4a" - If TitleFocus is not set, windows should get
3273						 *        the focus if the event was in the icon mgr
3274						 *        (as long as they accept input).
3275						 *
3276						 */
3277
3278						/* If the window takes input... */
3279						if(Tmp_win->wmhints->input) {
3280
3281							/* if 4 or 4a, focus on the window */
3282							if(Scr->TitleFocus ||
3283							                (Tmp_win->iconmanagerlist &&
3284							                 (Tmp_win->iconmanagerlist->w == ewp->window))) {
3285								SetFocus(Tmp_win, ewp->time);
3286							}
3287						}
3288
3289						if(Scr->TitleFocus &&
3290						                (Tmp_win->protocols & DoesWmTakeFocus)) {   /* 5 */
3291
3292							/* for both locally or globally active */
3293							SendTakeFocusMessage(Tmp_win, ewp->time);
3294						}
3295						else if(!Scr->TitleFocus
3296						                && Tmp_win->wmhints->input
3297						                && Event.xcrossing.focus) {
3298							SynthesiseFocusIn(Tmp_win->w);
3299						}
3300
3301					}
3302					else if(ewp->window == Tmp_win->w) {
3303						/*
3304						 * If we are entering the application window, install
3305						 * its colormap(s).
3306						 */
3307						if(Scr->BorderCursors) {
3308							SetBorderCursor(Tmp_win, -1000, -1000);
3309						}
3310						if(!scanArgs.leaves || scanArgs.inferior) {
3311							InstallWindowColormaps(EnterNotify, Tmp_win);
3312						}
3313
3314						if(Event.xcrossing.focus) {
3315							SynthesiseFocusIn(Tmp_win->w);
3316						}
3317
3318						/* must deal with WM_TAKE_FOCUS clients now, if
3319						   we're not in TitleFocus mode */
3320
3321						if(!(Scr->TitleFocus) &&
3322						                (Tmp_win->protocols & DoesWmTakeFocus)) {
3323
3324							/* locally active clients need help from WM
3325							   to get the input focus */
3326
3327							if(Tmp_win->wmhints->input) {
3328								SetFocus(Tmp_win, ewp->time);
3329							}
3330
3331							/* for both locally & globally active clnts */
3332
3333							SendTakeFocusMessage(Tmp_win, ewp->time);
3334						}
3335					}
3336				}                       /* end if Tmp_win->mapped */
3337				if(ewp->window == Tmp_win->wmhints->icon_window &&
3338				                (!scanArgs.leaves || scanArgs.inferior)) {
3339					InstallWindowColormaps(EnterNotify, Tmp_win);
3340				}
3341			}                           /* end if FocusRoot */
3342			else if(Scr->BorderCursors && (ewp->window == Tmp_win->w)) {
3343				SetBorderCursor(Tmp_win, -1000, -1000);
3344			}
3345			/*
3346			 * If this window is to be autoraised, mark it so
3347			 */
3348			if(Tmp_win->auto_raise) {
3349				enter_win = Tmp_win;
3350				if(enter_flag == false) {
3351					AutoRaiseWindow(Tmp_win);
3352				}
3353			}
3354			else if(enter_flag && raise_win == Tmp_win) {
3355				enter_win = Tmp_win;
3356			}
3357			/*
3358			 * set ring leader
3359			 */
3360			if(Tmp_win->ring.next && (!enter_flag || raise_win == enter_win)) {
3361				Scr->RingLeader = Tmp_win;
3362			}
3363			XSync(dpy, 0);
3364			return;
3365		}                               /* end if Tmp_win */
3366	}                                   /* end if !ActiveMenu */
3367
3368	/*
3369	 * Find the menu that we are dealing with now; punt if unknown
3370	 */
3371	if(XFindContext(dpy, ewp->window, MenuContext, (XPointer *)&mr) != XCSUCCESS) {
3372		return;
3373	}
3374
3375	if(! ActiveMenu && mr->pinned && (RootFunction == 0)) {
3376		PopUpMenu(mr, 0, 0, false);
3377		Context = C_ROOT;
3378		UpdateMenu();
3379		return;
3380	}
3381	mr->entered = true;
3382	if(RootFunction == 0) {
3383		for(tmp = ActiveMenu; tmp; tmp = tmp->prev) {
3384			if(tmp == mr) {
3385				break;
3386			}
3387		}
3388		if(! tmp) {
3389			return;
3390		}
3391
3392		for(tmp = ActiveMenu; tmp != mr; tmp = tmp->prev) {
3393			if(tmp->pinned) {
3394				break;
3395			}
3396			HideMenu(tmp);
3397			MenuDepth--;
3398		}
3399		UninstallRootColormap();
3400
3401		if(ActiveItem) {
3402			ActiveItem->state = 0;
3403			PaintEntry(ActiveMenu, ActiveItem, false);
3404		}
3405		ActiveItem = NULL;
3406		ActiveMenu = mr;
3407		if(1/*Scr->StayUpMenus*/) {
3408			int i, x, y, x_root, y_root, entry;
3409			MenuItem *mi;
3410
3411			XQueryPointer(dpy, ActiveMenu->w, &JunkRoot, &JunkChild, &x_root, &y_root,
3412			              &x, &y, &JunkMask);
3413			if((x > 0) && (y > 0) && (x < ActiveMenu->width) && (y < ActiveMenu->height)) {
3414				entry = y / Scr->EntryHeight;
3415				for(i = 0, mi = ActiveMenu->first; mi != NULL; i++, mi = mi->next) {
3416					if(i == entry) {
3417						break;
3418					}
3419				}
3420				if(mi) {
3421					ActiveItem = mi;
3422					ActiveItem->state = 1;
3423					PaintEntry(ActiveMenu, ActiveItem, false);
3424				}
3425			}
3426		}
3427		if(ActiveMenu->pinned) {
3428			XUngrabPointer(dpy, CurrentTime);
3429		}
3430	}
3431	return;
3432}
3433
3434
3435/***********************************************************************
3436 *
3437 *  Procedure:
3438 *      HLNQueueScanner - LeaveNotify event q scanner
3439 *
3440 *      Looks at the queued events and determines if any
3441 *      EnterNotify events are behind this event to allow
3442 *      skipping of unnecessary processing.
3443 *
3444 ***********************************************************************
3445 */
3446
3447typedef struct HLNScanArgs {
3448	Window w;           /* The window getting the LeaveNotify */
3449	Bool enters;        /* Any EnterNotify event at all */
3450	Bool matches;       /* Any matching EnterNotify events */
3451} HLNScanArgs;
3452
3453/* ARGSUSED*/
3454static Bool HLNQueueScanner(Display *display, XEvent *ev, char *_args)
3455{
3456	HLNScanArgs *args = (void *)_args;
3457
3458	if(ev->type == EnterNotify && ev->xcrossing.mode != NotifyGrab) {
3459		args->enters = True;
3460		if(ev->xcrossing.window == args->w) {
3461			args->matches = True;
3462		}
3463	}
3464
3465	return (False);
3466}
3467
3468
3469/***********************************************************************
3470 *
3471 *  Procedure:
3472 *      HandleLeaveNotify - LeaveNotify event handler
3473 *
3474 ***********************************************************************
3475 */
3476
3477void HandleLeaveNotify(void)
3478{
3479	bool inicon;
3480
3481	if(ActiveMenu && ActiveMenu->pinned
3482	                && (Event.xcrossing.window == ActiveMenu->w)) {
3483		PopDownMenu();
3484	}
3485
3486	if(Tmp_win == NULL) {
3487		/* No window to be Leave'ing, so nothing much to do... */
3488		return;
3489	}
3490
3491	/*
3492	 * We're not interested in pseudo Enter/Leave events generated
3493	 * from grab initiations and terminations.
3494	 */
3495	if(Event.xcrossing.mode != NotifyNormal) {
3496		return;
3497	}
3498
3499	if(Scr->ShrinkIconTitles &&
3500	                Tmp_win->icon &&
3501	                Event.xcrossing.window == Tmp_win->icon->w &&
3502	                Event.xcrossing.detail != NotifyInferior) {
3503		ShrinkIconTitle(Tmp_win);
3504		return;
3505	}
3506
3507	// Are we Leave'ing the icon manager entry for the Tmp_win in
3508	// question, or some other part of the window itself?
3509	inicon = (Tmp_win->iconmanagerlist &&
3510	          Tmp_win->iconmanagerlist->w == Event.xcrossing.window);
3511
3512	if(Scr->RingLeader && Scr->RingLeader == Tmp_win &&
3513	                (Event.xcrossing.detail != NotifyInferior &&
3514	                 Event.xcrossing.window != Tmp_win->w)) {
3515#ifdef DEBUG
3516		fprintf(stderr,
3517		        "HandleLeaveNotify: Event.xcrossing.window %x != Tmp_win->w %x\n",
3518		        Event.xcrossing.window, Tmp_win->w);
3519#endif
3520		if(!inicon) {
3521			if(Event.xcrossing.window != Tmp_win->frame /*was: Tmp_win->mapped*/) {
3522				Tmp_win->ring.cursor_valid = false;
3523#ifdef DEBUG
3524				fprintf(stderr, "HandleLeaveNotify: cursor_valid = false\n");
3525#endif
3526			}
3527			else {          /* Event.xcrossing.window == Tmp_win->frame */
3528				Tmp_win->ring.cursor_valid = true;
3529				Tmp_win->ring.curs_x = (Event.xcrossing.x_root -
3530				                        Tmp_win->frame_x);
3531				Tmp_win->ring.curs_y = (Event.xcrossing.y_root -
3532				                        Tmp_win->frame_y);
3533#ifdef DEBUG
3534				fprintf(stderr,
3535				        "HandleLeaveNotify: cursor_valid = true; x = %d (%d-%d), y = %d (%d-%d)\n",
3536				        Tmp_win->ring.curs_x, Event.xcrossing.x_root, Tmp_win->frame_x,
3537				        Tmp_win->ring.curs_y, Event.xcrossing.y_root, Tmp_win->frame_y);
3538#endif
3539			}
3540		}
3541		Scr->RingLeader = NULL;
3542	}
3543
3544
3545	/*
3546	 * Are we moving focus based on the leave?  There are 2 steps to
3547	 * this:
3548	 * - Scr->FocusRoot is our "are we automatically changing focus
3549	 *   based on the leave" flag.  This gets unset when ClickToFocus
3550	 *   is config'd or a window is f.focus'd.
3551	 * - Then we check the detail for the focus leaving.  We're only
3552	 *   getting here for normal entry/exits.  Most cases outside of
3553	 *   the icon manager peek ahead in the event queue for any
3554	 *   Enter's, and don't do anything if there are; if there were,
3555	 *   we'd be moving the focus when we get them, so no point doing
3556	 *   it twice.  So the remainder here assumes there aren't any
3557	 *   Enters waiting.
3558	 *
3559	 *   See
3560	 *   <https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html#Normal_EntryExit_Events>
3561	 *   for details of the cases.  So, when do we want to un-set the
3562	 *   focus?  Let's look at each doc'd value...
3563	 *   - NotifyAncestor means we're leaving a subwindow for its
3564	 *     parent.  That means the root, so, yep, we want to yield
3565	 *     focus up to it.
3566	 *   - NotifyNonLinear means we're leaving a window for something
3567	 *     that isn't a parent or child.  So we should probably yield
3568	 *     focus in this case too.
3569	 *   - NotifyInferior means we're leaving a window for a
3570	 *     subwindow of it.  From the WM perspective, that means
3571	 *     we're leaving focus where it was.
3572	 *   - NotifyVirtual means we're in the middle of an ascending
3573	 *     sequence.  Nothing to do; the Ancestor handling already
3574	 *     did the job.
3575	 *   - NotifyNonLinearVirtual is another "in the middle" case, so
3576	 *     we skip handling there too; the endpoints will do
3577	 *     whatever's necessary.
3578	 */
3579	if(Scr->FocusRoot
3580	                && Event.xcrossing.detail != NotifyInferior
3581	                && Event.xcrossing.detail != NotifyVirtual
3582	                && Event.xcrossing.detail != NotifyNonlinearVirtual
3583	  ) {
3584		HLNScanArgs scanArgs;
3585		XEvent dummy;
3586
3587		/*
3588		 * Scan for EnterNotify events to see if we can avoid some
3589		 * unnecessary processing.
3590		 */
3591		scanArgs.w = Event.xcrossing.window;
3592		scanArgs.enters = scanArgs.matches = False;
3593		XCheckIfEvent(dpy, &dummy, HLNQueueScanner,
3594		              (char *) &scanArgs);
3595
3596		if((inicon && Scr->IconManagerFocus)
3597		                || (Event.xcrossing.window == Tmp_win->frame
3598		                    && !scanArgs.matches)
3599		  ) {
3600			// Defocusing window because we moved out of its entry in an
3601			// icon manager, or because we moved out of its frame.
3602
3603			// Nothing to do if we were in the icon manager, and the
3604			// window's either unmapped or doesn't accept input.  XXX Is
3605			// the inicon flag needed here?  If it's not mapped, we
3606			// presumably couldn't have gotten a Leave on its frame
3607			// anyway, and if it's not accepting input, we probably don't
3608			// need to focus out anyway?  Left conditional because this
3609			// matches historical behavior prior to some rework here, but
3610			// revisit.
3611			if(inicon && (!Tmp_win->mapped || !Tmp_win->wmhints->input)) {
3612				return;
3613			}
3614
3615			// Shift away focus
3616			if(Scr->TitleFocus || Tmp_win->protocols & DoesWmTakeFocus) {
3617				SetFocus(NULL, Event.xcrossing.time);
3618			}
3619
3620			// If we're in the icon manager, we need to take a FocusOut
3621			// event for the window, since it wouldn't have gotten one.
3622			// If we're in the frame, we fake one anyway as historical
3623			// code says "pretend there was a focus out as sometimes we
3624			// don't get one".
3625			if(Event.xcrossing.focus) {
3626				SynthesiseFocusOut(Tmp_win->w);
3627			}
3628		}
3629		else if(Event.xcrossing.window == Tmp_win->w && !scanArgs.enters) {
3630			// Flipping colormaps around because we moved out of the
3631			// window.
3632			InstallColormaps(LeaveNotify, &Scr->RootColormaps);
3633		}
3634	}
3635
3636	/* Autolower modification. */
3637	if(Tmp_win->auto_lower) {
3638		leave_win = Tmp_win;
3639		if(leave_flag == false) {
3640			AutoLowerWindow(Tmp_win);
3641		}
3642	}
3643	else if(leave_flag && lower_win == Tmp_win) {
3644		leave_win = Tmp_win;
3645	}
3646
3647	XSync(dpy, 0);
3648	return;
3649}
3650
3651
3652/***********************************************************************
3653 *
3654 *  Procedure:
3655 *      HandleConfigureRequest - ConfigureRequest event handler
3656 *
3657 ***********************************************************************
3658 */
3659
3660void HandleConfigureRequest(void)
3661{
3662	XWindowChanges xwc;
3663	unsigned long xwcm;
3664	int x, y, width, height, bw;
3665	int gravx, gravy;
3666	XConfigureRequestEvent *cre = &Event.xconfigurerequest;
3667	bool sendEvent;
3668
3669#ifdef DEBUG_EVENTS
3670	fprintf(stderr, "ConfigureRequest\n");
3671	if(cre->value_mask & CWX) {
3672		fprintf(stderr, "  x = %d\n", cre->x);
3673	}
3674	if(cre->value_mask & CWY) {
3675		fprintf(stderr, "  y = %d\n", cre->y);
3676	}
3677	if(cre->value_mask & CWWidth) {
3678		fprintf(stderr, "  width = %d\n", cre->width);
3679	}
3680	if(cre->value_mask & CWHeight) {
3681		fprintf(stderr, "  height = %d\n", cre->height);
3682	}
3683	if(cre->value_mask & CWSibling) {
3684		fprintf(stderr, "  above = 0x%x\n", (unsigned)cre->above);
3685	}
3686	if(cre->value_mask & CWStackMode) {
3687		fprintf(stderr, "  stack = %d\n", cre->detail);
3688	}
3689#endif
3690
3691	/*
3692	 * Event.xany.window is Event.xconfigurerequest.parent, so Tmp_win will
3693	 * be wrong
3694	 */
3695	Event.xany.window = cre->window;    /* mash parent field */
3696	Tmp_win = GetTwmWindow(cre->window);
3697
3698	/*
3699	 * According to the July 27, 1988 ICCCM draft, we should ignore size and
3700	 * position fields in the WM_NORMAL_HINTS property when we map a window.
3701	 * Instead, we'll read the current geometry.  Therefore, we should respond
3702	 * to configuration requests for windows which have never been mapped.
3703	 */
3704	if(!Tmp_win || (Tmp_win->icon && (Tmp_win->icon->w == cre->window))) {
3705		xwcm = cre->value_mask &
3706		       (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
3707		xwc.x = cre->x;
3708		xwc.y = cre->y;
3709		xwc.width = cre->width;
3710		xwc.height = cre->height;
3711		xwc.border_width = cre->border_width;
3712		XConfigureWindow(dpy, Event.xany.window, xwcm, &xwc);
3713		return;
3714	}
3715
3716	sendEvent = false;
3717	if((cre->value_mask & CWStackMode) && Tmp_win->stackmode) {
3718		TwmWindow *otherwin;
3719
3720		if(cre->value_mask & CWSibling) {
3721			otherwin = GetTwmWindow(cre->above);
3722			if(otherwin) {
3723				OtpForcePlacement(Tmp_win, cre->detail, otherwin);
3724			}
3725			else {
3726				fprintf(stderr, "XConfigureRequest: unkown otherwin\n");
3727			}
3728		}
3729		else {
3730			switch(cre->detail) {
3731				case TopIf:
3732				case Above:
3733					OtpRaise(Tmp_win, WinWin);
3734					break;
3735				case BottomIf:
3736				case Below:
3737					OtpLower(Tmp_win, WinWin);
3738					break;
3739				case Opposite:
3740					OtpRaiseLower(Tmp_win, WinWin);
3741					break;
3742				default:
3743					;
3744			}
3745		}
3746		sendEvent = true;
3747	}
3748
3749
3750	/* Don't modify frame_XXX fields before calling SetupWindow! */
3751	x = Tmp_win->frame_x;
3752	y = Tmp_win->frame_y;
3753	width = Tmp_win->frame_width;
3754	height = Tmp_win->frame_height;
3755	bw = Tmp_win->frame_bw;
3756
3757	/*
3758	 * Section 4.1.5 of the ICCCM states that the (x,y) coordinates in the
3759	 * configure request are for the upper-left outer corner of the window.
3760	 * This means that we need to adjust for the additional title height as
3761	 * well as for any border width changes that we decide to allow.  The
3762	 * current window gravity is to be used in computing the adjustments, just
3763	 * as when initially locating the window.  Note that if we do decide to
3764	 * allow border width changes, we will need to send the synthetic
3765	 * ConfigureNotify event.
3766	 */
3767	GetGravityOffsets(Tmp_win, &gravx, &gravy);
3768
3769	if(cre->value_mask & CWBorderWidth) {
3770		int bwdelta = cre->border_width - Tmp_win->old_bw;  /* posit growth */
3771		if(bwdelta && Scr->ClientBorderWidth) {   /* if change allowed */
3772			x += gravx * bwdelta;       /* change default values only */
3773			y += gravy * bwdelta;       /* ditto */
3774			bw = cre->border_width;
3775			if(Tmp_win->title_height) {
3776				height += bwdelta;
3777			}
3778			x += (gravx < 0) ? bwdelta : -bwdelta;
3779			y += (gravy < 0) ? bwdelta : -bwdelta;
3780		}
3781		Tmp_win->old_bw = cre->border_width;  /* for restoring */
3782	}
3783
3784	if((cre->value_mask & CWX)) {       /* override even if border change */
3785		x = cre->x - bw;
3786		x -= ((gravx < 0) ? 0 : Tmp_win->frame_bw3D);
3787	}
3788	if((cre->value_mask & CWY)) {
3789		y = cre->y - ((gravy < 0) ? 0 : Tmp_win->title_height) - bw;
3790		y -= ((gravy < 0) ? 0 : Tmp_win->frame_bw3D);
3791	}
3792
3793	if(cre->value_mask & CWWidth) {
3794		width = cre->width + 2 * Tmp_win->frame_bw3D;
3795	}
3796	if(cre->value_mask & CWHeight) {
3797		height = cre->height + Tmp_win->title_height + 2 * Tmp_win->frame_bw3D;
3798	}
3799
3800	if(width != Tmp_win->frame_width || height != Tmp_win->frame_height) {
3801		unzoom(Tmp_win);
3802	}
3803
3804	/* Workaround for Java 1.4 bug that freezes the application whenever
3805	 * a new window is displayed. (When UsePPosition is on and either
3806	 * UseThreeDBorders or BorderWidth 0 is set.)
3807	 */
3808	if(!bw) {
3809		sendEvent = true;
3810	}
3811
3812	/*
3813	 * SetupWindow (x,y) are the location of the upper-left outer corner and
3814	 * are passed directly to XMoveResizeWindow (frame).  The (width,height)
3815	 * are the inner size of the frame.  The inner width is the same as the
3816	 * requested client window width; the inner height is the same as the
3817	 * requested client window height plus any title bar slop.
3818	 */
3819#ifdef DEBUG_EVENTS
3820	fprintf(stderr, "SetupFrame(x=%d, y=%d, width=%d, height=%d, bw=%d)\n",
3821	        x, y, width, height, bw);
3822#endif
3823	SetupFrame(Tmp_win, x, y, width, height, bw, sendEvent);
3824}
3825
3826
3827/***********************************************************************
3828 *
3829 *  Procedure:
3830 *      HandleShapeNotify - shape notification event handler
3831 *
3832 ***********************************************************************
3833 */
3834void
3835HandleShapeNotify(void)
3836{
3837	XShapeEvent     *sev = (XShapeEvent *) &Event;
3838
3839	if(Tmp_win == NULL) {
3840		return;
3841	}
3842	if(sev->kind != ShapeBounding) {
3843		return;
3844	}
3845	if(!Tmp_win->wShaped && sev->shaped) {
3846		XShapeCombineMask(dpy, Tmp_win->frame, ShapeClip, 0, 0, None,
3847		                  ShapeSet);
3848	}
3849	Tmp_win->wShaped = sev->shaped;
3850	SetFrameShape(Tmp_win);
3851}
3852
3853/***********************************************************************
3854 *
3855 *  Procedure:
3856 *      HandleSelectionClear - selection lost event handler
3857 *
3858 ***********************************************************************
3859 */
3860#ifdef EWMH
3861void
3862HandleSelectionClear(void)
3863{
3864	XSelectionClearEvent    *sev = (XSelectionClearEvent *) &Event;
3865
3866	if(sev->window == Scr->icccm_Window) {
3867		EwmhSelectionClear(sev);
3868	}
3869}
3870#endif
3871
3872
3873/***********************************************************************
3874 *
3875 *  Procedure:
3876 *      HandleUnknown - unknown event handler
3877 *
3878 ***********************************************************************
3879 */
3880
3881void HandleUnknown(void)
3882{
3883#ifdef DEBUG_EVENTS
3884	fprintf(stderr, "HandleUnknown: Event.type = %d\n", Event.type);
3885#endif
3886}
3887
3888
3889static void flush_expose(Window w)
3890{
3891	XEvent dummy;
3892
3893	while(XCheckTypedWindowEvent(dpy, w, Expose, &dummy)) {
3894		/* nada */;
3895	}
3896}
3897
3898
3899/* Util func used a few times above */
3900static void
3901SendTakeFocusMessage(TwmWindow *tmp, Time timestamp)
3902{
3903	send_clientmessage(tmp->w, XA_WM_TAKE_FOCUS, timestamp);
3904}
3905