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