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