add_window.c revision 7d8a9cc2
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: add_window.c,v 1.153 91/07/10 13:17:26 dave Exp $
13 *
14 * Add a new window, put the titlbar and other stuff around
15 * the window
16 *
17 * 31-Mar-88 Tom LaStrange        Initial Version.
18 *
19 * Do the necessary modification to be integrated in ctwm.
20 * Can no longer be used for the standard twm.
21 *
22 * 22-April-92 Claude Lecommandeur.
23 *
24 **********************************************************************/
25
26#include "ctwm.h"
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/time.h>
32
33#include <X11/Xatom.h>
34#include <X11/extensions/shape.h>
35
36#include "add_window.h"
37#include "captive.h"
38#include "colormaps.h"
39#include "ctwm_atoms.h"
40#include "functions.h"
41#include "events.h"
42#ifdef EWMH
43# include "ewmh_atoms.h"
44#endif
45#include "gram.tab.h"
46#include "icons.h"
47#include "iconmgr.h"
48#include "image.h"
49#include "list.h"
50#include "mwmhints.h"
51#include "occupation.h"
52#include "otp.h"
53#include "parse.h"
54#include "screen.h"
55#include "session.h"
56#include "util.h"
57#include "vscreen.h"
58#include "windowbox.h"
59#include "win_decorations.h"
60#include "win_ops.h"
61#include "win_regions.h"
62#include "win_resize.h"
63#include "win_utils.h"
64#include "workspace_manager.h"
65
66
67int AddingX;
68int AddingY;
69unsigned int AddingW;
70unsigned int AddingH;
71
72static int PlaceX = -1;
73static int PlaceY = -1;
74static void DealWithNonSensicalGeometries(Display *dpy, Window vroot,
75                TwmWindow *tmp_win);
76
77char NoName[] = "Untitled"; /* name if no name is specified */
78bool resizeWhenAdd;
79
80
81
82/***********************************************************************
83 *
84 *  Procedure:
85 *      AddWindow - add a new window to the twm list
86 *
87 *  Returned Value:
88 *      (TwmWindow *) - pointer to the TwmWindow structure
89 *
90 *  Inputs:
91 *      w       - the window id of the window to add
92 *      wtype   - flag to tell if this is a normal window or some ctwm
93 *                internal one.
94 *
95 *      iconp   - pointer to icon manager struct
96 *
97 ***********************************************************************
98 */
99
100TwmWindow *
101AddWindow(Window w, AWType wtype, IconMgr *iconp, VirtualScreen *vs)
102{
103	TwmWindow *tmp_win;                 /* new twm window structure */
104	bool ask_user;               /* don't know where to put the window */
105	int gravx, gravy;                   /* gravity signs for positioning */
106	int namelen;
107	int bw2;
108	short restore_icon_x, restore_icon_y;
109	bool restore_iconified = false;
110	bool restore_icon_info_present = false;
111	bool restoredFromPrevSession = false;
112	int saved_occupation = 0; /* <== [ Matthew McNeill Feb 1997 ] == */
113	bool random_placed = false;
114	WindowBox *winbox;
115	Window vroot;
116
117#ifdef DEBUG
118	fprintf(stderr, "AddWindow: w = 0x%x\n", w);
119#endif
120
121	/*
122	 * Possibly this window should be in a captive sub-ctwm?  If so, we
123	 * shouldn't mess with it at all.
124	 */
125	if(!CLarg.is_captive && RedirectToCaptive(w)) {
126		/* XXX x-ref comment by SetNoRedirect() */
127		return (NULL);
128	}
129
130
131	/*
132	 * Allocate and initialize our tracking struct
133	 */
134	tmp_win = calloc(1, sizeof(TwmWindow));
135	if(tmp_win == NULL) {
136		fprintf(stderr, "%s: Unable to allocate memory to manage window ID %lx.\n",
137		        ProgramName, w);
138		return NULL;
139	}
140
141	/*
142	 * Some of these initializations are strictly unnecessary, since they
143	 * evaluate out to 0, and calloc() gives us an already zero'd buffer.
144	 * I'm leaving them anyway because a couple unnecessary stores are
145	 * near enough to free considering everything we're doing, that the
146	 * value as documentation stupendously outweighs the cost.
147	 */
148	tmp_win->w = w;
149	tmp_win->zoomed = ZOOM_NONE;
150	tmp_win->isiconmgr = (wtype == AWT_ICON_MANAGER);
151	tmp_win->iconmgrp = iconp;
152	tmp_win->iswspmgr = (wtype == AWT_WORKSPACE_MANAGER);
153	tmp_win->isoccupy = (wtype == AWT_OCCUPY);
154	tmp_win->iswinbox = (wtype == AWT_WINDOWBOX);
155	tmp_win->vs = vs;
156	tmp_win->parent_vs = vs;
157	tmp_win->savevs = NULL;
158	tmp_win->cmaps.number_cwins = 0;
159	tmp_win->savegeometry.width = -1;
160	tmp_win->widthEverChangedByUser = false;
161	tmp_win->heightEverChangedByUser = false;
162	tmp_win->nameChanged = false;
163	tmp_win->squeezed = false;
164	tmp_win->iconified = false;
165	tmp_win->isicon = false;
166	tmp_win->icon_on = false;
167	tmp_win->ring.cursor_valid = false;
168	tmp_win->squeeze_info = NULL;
169	tmp_win->squeeze_info_copied = false;
170
171
172
173	/*
174	 * Fetch a few bits of info about the window from the server, and
175	 * tell the server to tell us about property changes; we'll need to
176	 * know what happens.
177	 *
178	 * It's important that these remain relatively disconnected "early"
179	 * bits; generally, they shouldn't rely on anything but the X Window
180	 * in tmp_win->w to do their stuff.  e.g., anything that relies on
181	 * other values in our ctwm TwmWindow tmp_win (window name, various
182	 * flags, etc) has to come later.
183	 */
184	XSelectInput(dpy, tmp_win->w, PropertyChangeMask);
185	XGetWindowAttributes(dpy, tmp_win->w, &tmp_win->attr);
186	FetchWmProtocols(tmp_win);
187	FetchWmColormapWindows(tmp_win);
188#ifdef EWMH
189	EwmhGetProperties(tmp_win);
190#endif /* EWMH */
191
192
193	/*
194	 * Some other simple early initialization that has to follow those
195	 * bits.
196	 */
197	tmp_win->old_bw = tmp_win->attr.border_width;
198
199
200	/*
201	 * Setup window name and class bits.  A lot of following code starts
202	 * to care about this; in particular, anything looking in our
203	 * name_lists generally goes by the name/class, so we need to get
204	 * these set pretty early in the process.
205	 */
206	tmp_win->names.ctwm_wm_name = GetWMPropertyString(tmp_win->w,
207	                              XA_CTWM_WM_NAME);
208#ifdef EWMH
209	tmp_win->names.net_wm_name = GetWMPropertyString(tmp_win->w,
210	                             XA__NET_WM_NAME);
211#endif
212	tmp_win->names.wm_name = GetWMPropertyString(tmp_win->w, XA_WM_NAME);
213	set_window_name(tmp_win);
214	namelen = strlen(tmp_win->name);
215
216	/* Setup class.  x-ref XXX in ctwm_main() about NoClass */
217	tmp_win->class = NoClass;
218	XGetClassHint(dpy, tmp_win->w, &tmp_win->class);
219	if(tmp_win->class.res_name == NULL) {
220		tmp_win->class.res_name = NoName;
221	}
222	if(tmp_win->class.res_class == NULL) {
223		tmp_win->class.res_class = NoName;
224	}
225
226	/* Grab the icon name too */
227	tmp_win->names.ctwm_wm_icon_name = GetWMPropertyString(tmp_win->w,
228	                                   XA_CTWM_WM_ICON_NAME);
229#ifdef EWMH
230	tmp_win->names.net_wm_icon_name = GetWMPropertyString(tmp_win->w,
231	                                  XA__NET_WM_ICON_NAME);
232#endif
233	tmp_win->names.wm_icon_name = GetWMPropertyString(tmp_win->w,
234	                              XA_WM_ICON_NAME);
235	set_window_icon_name(tmp_win);
236
237
238	/* Convenience macro */
239#define CHKL(lst) IsInList(Scr->lst, tmp_win)
240
241
242	/* Is it a transient?  Or should we ignore that it is? */
243	tmp_win->istransient = XGetTransientForHint(dpy, tmp_win->w,
244	                       &tmp_win->transientfor);
245	if(tmp_win->istransient) {
246		/*
247		 * XXX Should this be looking up transientfor instead of tmp_win?
248		 * It seems like IgnoreTransient {} would list the windows that
249		 * have transients we should ignore, while this condition makes
250		 * it list the transient window names we should ignore.  Probably
251		 * not trivial to fix if that's right, since it might b0rk
252		 * existing configs...
253		 */
254		if(CHKL(IgnoreTransientL)) {
255			tmp_win->istransient = false;
256		}
257	}
258
259
260	/*
261	 * Look up saved X Session info for the window if we have it.
262	 */
263	{
264		short saved_x, saved_y;
265		unsigned short saved_width, saved_height;
266		bool width_ever_changed_by_user;
267		bool height_ever_changed_by_user;
268
269		if(GetWindowConfig(tmp_win,
270		                   &saved_x, &saved_y, &saved_width, &saved_height,
271		                   &restore_iconified, &restore_icon_info_present,
272		                   &restore_icon_x, &restore_icon_y,
273		                   &width_ever_changed_by_user,
274		                   &height_ever_changed_by_user,
275		                   &saved_occupation)) {
276			/* Got saved info, use it */
277			restoredFromPrevSession = true;
278
279			tmp_win->attr.x = saved_x;
280			tmp_win->attr.y = saved_y;
281
282			tmp_win->widthEverChangedByUser = width_ever_changed_by_user;
283			tmp_win->heightEverChangedByUser = height_ever_changed_by_user;
284
285			if(width_ever_changed_by_user) {
286				tmp_win->attr.width = saved_width;
287			}
288
289			if(height_ever_changed_by_user) {
290				tmp_win->attr.height = saved_height;
291			}
292		}
293	}
294
295
296	/*
297	 * Clip window to maximum size (either built-in ceiling, or
298	 * config MaxWindowSize).
299	 *
300	 * Should look at window gravity?
301	 */
302	if(tmp_win->attr.width > Scr->MaxWindowWidth) {
303		tmp_win->attr.width = Scr->MaxWindowWidth;
304	}
305	if(tmp_win->attr.height > Scr->MaxWindowHeight) {
306		tmp_win->attr.height = Scr->MaxWindowHeight;
307	}
308
309
310	/*
311	 * Setup WM_HINTS bits.  If we get nothing, we hardcode an
312	 * assumption.
313	 */
314	tmp_win->wmhints = XGetWMHints(dpy, tmp_win->w);
315	if(!tmp_win->wmhints) {
316		tmp_win->wmhints = gen_synthetic_wmhints(tmp_win);
317		if(!tmp_win->wmhints) {
318			fprintf(stderr, "Failed allocating memory for hints!\n");
319			free(tmp_win); // XXX leaky
320			return NULL;
321		}
322	}
323
324	/*
325	 * Override a few bits with saved stuff from previous session, if we
326	 * have it.
327	 */
328	if(restore_iconified) {
329		tmp_win->wmhints->initial_state = IconicState;
330		tmp_win->wmhints->flags |= StateHint;
331	}
332
333	if(restore_icon_info_present) {
334		tmp_win->wmhints->icon_x = restore_icon_x;
335		tmp_win->wmhints->icon_y = restore_icon_y;
336		tmp_win->wmhints->flags |= IconPositionHint;
337	}
338
339	/* Munge as necessary for other stuff */
340	munge_wmhints(tmp_win, tmp_win->wmhints);
341
342
343	/*
344	 * Various flags that may be screen-wide or window specific.
345	 */
346	tmp_win->highlight = Scr->Highlight && !CHKL(NoHighlight);
347	tmp_win->stackmode = Scr->StackMode && !CHKL(NoStackModeL);
348	tmp_win->titlehighlight = Scr->TitleHighlight && !CHKL(NoTitleHighlight);
349	tmp_win->AlwaysSqueezeToGravity = Scr->AlwaysSqueezeToGravity
350	                                  || CHKL(AlwaysSqueezeToGravityL);
351	tmp_win->DontSetInactive = CHKL(DontSetInactive);
352	tmp_win->AutoSqueeze = CHKL(AutoSqueeze);
353	tmp_win->StartSqueezed =
354#ifdef EWMH
355	        (tmp_win->ewmhFlags & EWMH_STATE_SHADED) ||
356#endif /* EWMH */
357	        CHKL(StartSqueezed);
358
359	tmp_win->auto_raise = Scr->AutoRaiseDefault || CHKL(AutoRaise);
360	if(tmp_win->auto_raise) {
361		Scr->NumAutoRaises++;
362	}
363
364	tmp_win->auto_lower = Scr->AutoLowerDefault || CHKL(AutoLower);
365	if(tmp_win->auto_lower) {
366		Scr->NumAutoLowers++;
367	}
368
369	tmp_win->OpaqueMove = Scr->DoOpaqueMove;
370	if(CHKL(OpaqueMoveList)) {
371		tmp_win->OpaqueMove = true;
372	}
373	else if(CHKL(NoOpaqueMoveList)) {
374		tmp_win->OpaqueMove = false;
375	}
376
377	tmp_win->OpaqueResize = Scr->DoOpaqueResize;
378	if(CHKL(OpaqueResizeList)) {
379		tmp_win->OpaqueResize = true;
380	}
381	else if(CHKL(NoOpaqueResizeList)) {
382		tmp_win->OpaqueResize = false;
383	}
384
385
386	/*
387	 * If a window is listed in IconifyByUnmapping {}, we always iconify
388	 * by unmapping.  Else, if it's DontIconifyByUnmapping {} or is an
389	 * icon manager, we don't i_b_u.  Else, we go with the Scr-wide
390	 * default.
391	 */
392	{
393		bool ibum = CHKL(IconifyByUn);
394		if(!ibum) {
395			if(tmp_win->isiconmgr || CHKL(DontIconify)) {
396				ibum = false; // redundant
397			}
398			else {
399				ibum = Scr->IconifyByUnmapping;
400			}
401		}
402		tmp_win->iconify_by_unmapping = ibum;
403	}
404
405
406	/*
407	 * For transient windows or group members, we copy in UBMFA from its
408	 * parent/leader/etc if we can find it.  Otherwise, it's just whether
409	 * it's in the config list.
410	 */
411	tmp_win->UnmapByMovingFarAway = CHKL(UnmapByMovingFarAway);
412	if(tmp_win->istransient || tmp_win->group) {
413		TwmWindow *t = NULL;
414		if(tmp_win->istransient) {
415			t = GetTwmWindow(tmp_win->transientfor);
416		}
417		if(!t && tmp_win->group) {
418			t = GetTwmWindow(tmp_win->group);
419		}
420		if(t) {
421			tmp_win->UnmapByMovingFarAway = t->UnmapByMovingFarAway;
422		}
423	}
424
425
426	/*
427	 * Link it up into the window ring if we should.  If it's in
428	 * WindowRing {}, we should.  Otherwise, we shouldn't unless
429	 * WindowRingAll is set.  If it is, we still exclude several special
430	 * ctwm windows, stuff in WindowRingExclude {}, and some special EWMH
431	 * settings.
432	 */
433	if(CHKL(WindowRingL) ||
434	                (Scr->WindowRingAll && !tmp_win->iswspmgr
435	                 && !tmp_win->isiconmgr
436#ifdef EWMH
437	                 && EwmhOnWindowRing(tmp_win)
438#endif /* EWMH */
439	                 && !CHKL(WindowRingExcludeL))) {
440		if(Scr->Ring) {
441			tmp_win->ring.next = Scr->Ring->ring.next;
442			if(Scr->Ring->ring.next->ring.prev) {
443				Scr->Ring->ring.next->ring.prev = tmp_win;
444			}
445			Scr->Ring->ring.next = tmp_win;
446			tmp_win->ring.prev = Scr->Ring;
447		}
448		else {
449			tmp_win->ring.next = tmp_win->ring.prev = Scr->Ring = tmp_win;
450		}
451	}
452	else {
453		tmp_win->ring.next = tmp_win->ring.prev = NULL;
454	}
455
456
457	/*
458	 * Setup squeezing info.  We don't bother unless the server has Shape
459	 * available, and the window isn't in our DontSqueezeTitle list.
460	 * Else, we do/not based on the SqueezeTitle setting.  Note that
461	 * "SqueezeTitle" being specified at all squeezes everything; its
462	 * argument list lets you set specific squeeze params for specific
463	 * windows, but other windows still get the default.
464	 *
465	 * Note that this does not have to be freed yet since it is coming
466	 * from the screen list or from default_squeeze.  Places that change
467	 * it [re]set squeeze_info_copied, and then the destroy handler looks
468	 * at that to determine whether to free squeeze_info.
469	 *
470	 * XXX Technically, the HasShape test is redundant, since the config
471	 * file parsing would never set Scr->SqueezeTitle unless HasShape
472	 * were true anyway...
473	 */
474	if(HasShape && Scr->SqueezeTitle && !CHKL(DontSqueezeTitleL)) {
475		tmp_win->squeeze_info = LookInListWin(Scr->SqueezeTitleL, tmp_win);
476		if(!tmp_win->squeeze_info) {
477			static SqueezeInfo default_squeeze = { SIJ_LEFT, 0, 0 };
478			tmp_win->squeeze_info = &default_squeeze;
479		}
480	}
481
482
483	/*
484	 * Motif WM hints are used in setting up border and titlebar bits, so
485	 * put them in a block here to scope the MWM var.
486	 */
487	{
488		MotifWmHints mwmHints;
489		bool have_title;
490
491		GetMWMHints(tmp_win->w, &mwmHints);
492
493		/*
494		 * Figure border bits.  These are all exclusive cases, so it
495		 * winds up being first-match.
496		 *
497		 * - EWMH, MWM hints, and NoBorder{} can tell us to use none.
498		 * - ThreeDBorderWidth means use it and no regular 2d frame_bw.
499		 * - ClientBorderWidth tells us to use the XWindowAttributes
500		 *   border size rather than ours.
501		 * - Else, our BorderWidth is the [2d] border size.
502		 *
503		 * X-ref comments in win_decorations.c:SetBorderCursor() about
504		 * the somewhat differing treatment of 3d vs non-3d border widths
505		 * and their effects on the window coordinates.
506		 */
507		tmp_win->frame_bw3D = Scr->ThreeDBorderWidth;
508		if(
509#ifdef EWMH
510		        !EwmhHasBorder(tmp_win) ||
511#endif /* EWMH */
512		        (mwm_has_border(&mwmHints) == 0) ||
513		        CHKL(NoBorder)) {
514			tmp_win->frame_bw = 0;
515			tmp_win->frame_bw3D = 0;
516		}
517		else if(tmp_win->frame_bw3D != 0) {
518			tmp_win->frame_bw = 0;
519		}
520		else if(Scr->ClientBorderWidth) {
521			tmp_win->frame_bw = tmp_win->old_bw;
522		}
523		else {
524			tmp_win->frame_bw = Scr->BorderWidth;
525		}
526		bw2 = tmp_win->frame_bw * 2;  // Used repeatedly later
527
528
529		/*
530		 * Now, what about the titlebar?
531		 *
532		 * - Default to showing,
533		 * - Then EWMH gets to say no in some special cases,
534		 * - Then MWM can say yes/no (or refuse to say anything),
535		 * - NoTitle (general setting) gets to override all of that,
536		 * - Specific MakeTitle beats general NoTitle,
537		 * - And specific NoTitle overrides MakeTitle.
538		 */
539		have_title = true;
540#ifdef EWMH
541		have_title = EwmhHasTitle(tmp_win);
542#endif /* EWMH */
543		if(mwm_sets_title(&mwmHints)) {
544			have_title = mwm_has_title(&mwmHints);
545		}
546		if(Scr->NoTitlebar) {
547			have_title = false;
548		}
549		if(CHKL(MakeTitle)) {
550			have_title = true;
551		}
552		if(CHKL(NoTitle)) {
553			have_title = false;
554		}
555
556		/*
557		 * Now mark up how big to make it.  title_height sets how tall
558		 * the titlebar is, with magic treating 0 as "don't make a
559		 * titlebar".  We only care about adding frame_bw and never
560		 * frame_bw3D, since the 3d case interprets all the inner
561		 * coordinates differently (x-ref above x-ref).
562		 *
563		 * Transients may not be decorated regardless of the above
564		 * figuring, so handle that here too.
565		 */
566		if(tmp_win->istransient && !Scr->DecorateTransients) {
567			tmp_win->title_height = 0;
568		}
569		else if(have_title) {
570			tmp_win->title_height = Scr->TitleHeight + tmp_win->frame_bw;
571		}
572		else {
573			tmp_win->title_height = 0;
574		}
575	}
576
577
578	/*
579	 * Need the GetWindowAttributes() call and setting ->old_bw and
580	 * ->frame_bw3D for some of the math in looking up the
581	 *  WM_NORMAL_HINTS bits, so now we can do that.
582	 */
583	GetWindowSizeHints(tmp_win);
584
585
586	/* Maybe we're ordering it to start off iconified? */
587	if(CHKL(StartIconified)) {
588		tmp_win->wmhints->initial_state = IconicState;
589		tmp_win->wmhints->flags |= StateHint;
590	}
591
592
593	/*
594	 * Figure gravity bits.  When restoring from a previous session, we
595	 * always use NorthWest gravity.
596	 */
597	if(restoredFromPrevSession) {
598		gravx = gravy = -1;
599	}
600	else {
601		GetGravityOffsets(tmp_win, &gravx, &gravy);
602	}
603
604
605	/* So far that's the end of where we're using this */
606#undef CHKL
607
608
609	/*
610	 * Now we start getting more into the active bits of things.  Start
611	 * figuring out how we'll decide where to position it.  ask_user is
612	 * slightly misnamed, as it doesn't actually mean ask the user, but
613	 * rather whether the user/WM gets to choose or whether the
614	 * application does.  That is, we only case about whether or not
615	 * RandomPlacement if(ask_user==true) anyway.  ask_user=false means
616	 * we just go with what's in the window's XWindowAttributes bits.
617	 *
618	 * We don't even consider overriding the window if:
619	 *
620	 * - It's a transient, or
621	 * - the WM_NORMAL_HINTS property gave us a user-specified position
622	 *   (USPosition), or
623	 * - the hints gave us a a program-specific position (PPosition), and
624	 *   the UsePPosition config param specifies we should use it.
625	 *
626	 * x-ref ICCCM discussion of WM_NORMAL_HINTS for some details on the
627	 * flags
628	 * (https://www.x.org/releases/X11R7.7/doc/xorg-docs/icccm/icccm.html#Client_Properties)
629	 */
630	ask_user = true;
631	if(tmp_win->istransient) {
632		ask_user = false;
633	}
634	else if(tmp_win->hints.flags & USPosition) {
635		ask_user = false;
636	}
637	else if(tmp_win->hints.flags & PPosition) {
638		if(Scr->UsePPosition == PPOS_ON) {
639			ask_user = false;
640		}
641		else if(Scr->UsePPosition == PPOS_NON_ZERO
642		                && (tmp_win->attr.x != 0 || tmp_win->attr.y != 0)) {
643			ask_user = false;
644		}
645	}
646
647
648	/*
649	 * Set the window occupation.  If we pulled previous Session info,
650	 * saved_occupation may have data from it that will be used;
651	 * otherwise it's already zeroed and has no effect.  X-ref XXX
652	 * comment on SetupOccupation() for notes on order of application of
653	 * various sources for occupation.
654	 *
655	 * Note that SetupOccupation() may update tmp_win->{parent_,}vs if
656	 * needed to make the window visible in another vscreen.  It may also
657	 * set tmp_win->vs to NULL if it has no occupation in the current
658	 * workspace.
659	 */
660	SetupOccupation(tmp_win, saved_occupation);
661
662
663	/* Does it go in a window box? */
664	winbox = findWindowBox(tmp_win);
665
666
667	/*
668	 * Set some values for the frame size.
669	 *
670	 * These get redone down below when we create the frame, when they're
671	 * actually useful.  So why bother here?  There is some code down in
672	 * the block where we prompt for a window position that calls some
673	 * functions that need plausible values in them.  However, those code
674	 * blocks calculate and set values themselves, so there shouldn't be
675	 * any actual need for them here.  Left #if'd out for the present in
676	 * case something turns up; this should be GC'd at some point if
677	 * nothing does.
678	 */
679#if 0
680	tmp_win->frame_width  = tmp_win->attr.width  + 2 * tmp_win->frame_bw3D;
681	tmp_win->frame_height = tmp_win->attr.height + 2 * tmp_win->frame_bw3D +
682	                        tmp_win->title_height;
683	ConstrainSize(tmp_win, &tmp_win->frame_width, &tmp_win->frame_height);
684#endif
685
686
687	/*
688	 * See if there's a WindowRegion we should honor.  If so, it'll set
689	 * the X/Y coords, and we'll want to accept them instead of doing our
690	 * own (or the user's) positioning.
691	 *
692	 * This needs the frame_{width,height}.
693	 */
694	if(PlaceWindowInRegion(tmp_win, &(tmp_win->attr.x), &(tmp_win->attr.y))) {
695		ask_user = false;
696	}
697
698
699	/*
700	 * Maybe we have WindowGeometries {} set for it?  If so, we'll take
701	 * that as our specifics too.
702	 */
703	{
704		char *geom = LookInListWin(Scr->WindowGeometries, tmp_win);
705		if(geom) {
706			int mask = XParseGeometry(geom, &tmp_win->attr.x, &tmp_win->attr.y,
707			                          (unsigned int *) &tmp_win->attr.width,
708			                          (unsigned int *) &tmp_win->attr.height);
709
710			if(mask & XNegative) {
711				tmp_win->attr.x += Scr->rootw - tmp_win->attr.width;
712			}
713			if(mask & YNegative) {
714				tmp_win->attr.y += Scr->rooth - tmp_win->attr.height;
715			}
716			ask_user = false;
717		}
718	}
719
720
721	/* Figure up what root window we should be working in */
722	if(tmp_win->parent_vs) {
723		vroot = tmp_win->parent_vs->window;
724	}
725	else {
726		vroot = Scr->Root;      /* never */
727		tmp_win->parent_vs = Scr->currentvs;
728	}
729	if(winbox) {
730		vroot = winbox->window;
731	}
732
733
734	/*
735	 * Handle positioning of the window.  If we're very early in startup
736	 * (setting up ctwm's own windows, taking over windows already on the
737	 * screen), or restoring defined session stuff, or otherwise
738	 * ask_user=false'd above, we just take the already set position
739	 * info.  Otherwise, we handle it via RandomPlacement or user outline
740	 * setting.
741	 *
742	 * XXX Somebody should go through these blocks in more detail,
743	 * they're sure to need further cleaning and commenting.  IWBNI they
744	 * could be encapsulated well enough to move out into separate
745	 * functions, for extra readability...
746	 */
747	if(HandlingEvents && ask_user && !restoredFromPrevSession) {
748		if((Scr->RandomPlacement == RP_ALL) ||
749		                ((Scr->RandomPlacement == RP_UNMAPPED) &&
750		                 ((tmp_win->wmhints->initial_state == IconicState) ||
751		                  (! visible(tmp_win))))) {
752			/* just stick it somewhere */
753
754#ifdef DEBUG
755			fprintf(stderr,
756			        "DEBUG[RandomPlacement]: win: %dx%d+%d+%d, screen: %dx%d, title height: %d, random: +%d+%d\n",
757			        tmp_win->attr.width, tmp_win->attr.height,
758			        tmp_win->attr.x, tmp_win->attr.y,
759			        Scr->rootw, Scr->rooth,
760			        tmp_win->title_height,
761			        PlaceX, PlaceY);
762#endif
763
764			/* Initiallise PlaceX and PlaceY */
765			if(PlaceX < 0 && PlaceY < 0) {
766				if(Scr->RandomDisplacementX >= 0) {
767					PlaceX = Scr->BorderLeft + 5;
768				}
769				else {
770					PlaceX = Scr->rootw - tmp_win->attr.width - Scr->BorderRight - 5;
771				}
772				if(Scr->RandomDisplacementY >= 0) {
773					PlaceY = Scr->BorderTop + 5;
774				}
775				else
776					PlaceY = Scr->rooth - tmp_win->attr.height - tmp_win->title_height
777					         - Scr->BorderBottom - 5;
778			}
779
780			/* For a positive horizontal displacement, if the right edge
781			   of the window would fall outside of the screen, start over
782			   by placing the left edge of the window 5 pixels inside
783			   the left edge of the screen.*/
784			if(Scr->RandomDisplacementX >= 0
785			                && (PlaceX + tmp_win->attr.width
786			                    > Scr->rootw - Scr->BorderRight - 5)) {
787				PlaceX = Scr->BorderLeft + 5;
788			}
789
790			/* For a negative horizontal displacement, if the left edge
791			   of the window would fall outside of the screen, start over
792			   by placing the right edge of the window 5 pixels inside
793			   the right edge of the screen.*/
794			if(Scr->RandomDisplacementX < 0 && PlaceX < Scr->BorderLeft + 5) {
795				PlaceX = Scr->rootw - tmp_win->attr.width - Scr->BorderRight - 5;
796			}
797
798			/* For a positive vertical displacement, if the bottom edge
799			   of the window would fall outside of the screen, start over
800			   by placing the top edge of the window 5 pixels inside the
801			   top edge of the screen.  Because we add the title height
802			   further down, we need to count with it here as well.  */
803			if(Scr->RandomDisplacementY >= 0
804			                && (PlaceY + tmp_win->attr.height + tmp_win->title_height
805			                    > Scr->rooth - Scr->BorderBottom - 5)) {
806				PlaceY = Scr->BorderTop + 5;
807			}
808
809			/* For a negative vertical displacement, if the top edge of
810			   the window would fall outside of the screen, start over by
811			   placing the bottom edge of the window 5 pixels inside the
812			   bottom edge of the screen.  Because we add the title height
813			   further down, we need to count with it here as well.  */
814			if(Scr->RandomDisplacementY < 0 && PlaceY < Scr->BorderTop + 5)
815				PlaceY = Scr->rooth - tmp_win->attr.height - tmp_win->title_height
816				         - Scr->BorderBottom - 5;
817
818			/* Assign the current random placement to the new window, as
819			   a preliminary measure.  Add the title height so things will
820			   look right.  */
821			tmp_win->attr.x = PlaceX;
822			tmp_win->attr.y = PlaceY + tmp_win->title_height;
823
824			/* If the window is not supposed to move off the screen, check
825			   that it's still within the screen, and if not, attempt to
826			   correct the situation. */
827			if(Scr->DontMoveOff) {
828				int available;
829
830#ifdef DEBUG
831				fprintf(stderr,
832				        "DEBUG[DontMoveOff]: win: %dx%d+%d+%d, screen: %dx%d, bw2: %d, bw3D: %d\n",
833				        tmp_win->attr.width, tmp_win->attr.height,
834				        tmp_win->attr.x, tmp_win->attr.y,
835				        Scr->rootw, Scr->rooth,
836				        bw2, tmp_win->frame_bw3D);
837#endif
838
839				/* If the right edge of the window is outside the right edge
840				   of the screen, we need to move the window left.  Note that
841				   this can only happen with windows that are less than 50
842				   pixels less wide than the screen. */
843				if((tmp_win->attr.x + tmp_win->attr.width)  > Scr->rootw) {
844					available = Scr->rootw - tmp_win->attr.width
845					            - 2 * (bw2 + tmp_win->frame_bw3D);
846
847#ifdef DEBUG
848					fprintf(stderr, "DEBUG[DontMoveOff]: availableX: %d\n",
849					        available);
850#endif
851
852					/* If the window is wider than the screen or exactly the width
853					 of the screen, the availability is exactly 0.  The result
854					 will be to have the window placed as much to the left as
855					 possible. */
856					if(available <= 0) {
857						available = 0;
858					}
859
860					/* Place the window exactly between the left and right edge of
861					 the screen when possible.  If available was originally less
862					 than zero, it means the window's left edge will be against
863					 the screen's left edge, and the window's right edge will be
864					 outside the screen.  */
865					tmp_win->attr.x = available / 2;
866				}
867
868				/* If the bottom edge of the window is outside the bottom edge
869				   of the screen, we need to move the window up.  Note that
870				   this can only happen with windows that are less than 50
871				   pixels less tall than the screen.  Don't forget to count
872				   with the title height and the frame widths.  */
873				if((tmp_win->attr.y + tmp_win->attr.height)  > Scr->rooth) {
874					available = Scr->rooth - tmp_win->attr.height
875					            - tmp_win->title_height - 2 * (bw2 + tmp_win->frame_bw3D);
876
877#ifdef DEBUG
878					fprintf(stderr, "DEBUG[DontMoveOff]: availableY: %d\n",
879					        available);
880#endif
881
882					/* If the window is taller than the screen or exactly the
883					 height of the screen, the availability is exactly 0.
884					 The result will be to have the window placed as much to
885					 the top as possible. */
886					if(available <= 0) {
887						available = 0;
888					}
889
890					/* Place the window exactly between the top and bottom edge of
891					 the screen when possible.  If available was originally less
892					 than zero, it means the window's top edge will be against
893					 the screen's top edge, and the window's bottom edge will be
894					 outside the screen.  Again, don't forget to add the title
895					 height.  */
896					tmp_win->attr.y = available / 2 + tmp_win->title_height;
897				}
898
899#ifdef DEBUG
900				fprintf(stderr,
901				        "DEBUG[DontMoveOff]: win: %dx%d+%d+%d, screen: %dx%d\n",
902				        tmp_win->attr.width, tmp_win->attr.height,
903				        tmp_win->attr.x, tmp_win->attr.y,
904				        Scr->rootw, Scr->rooth);
905#endif
906			}
907
908			/* We know that if the window's left edge has moved compared to
909			   PlaceX, it will have moved to the left.  If it was moved less
910			   than 15 pixel either way, change the next "random position"
911			   30 pixels down and right. */
912			if(PlaceX - tmp_win->attr.x < 15
913			                || PlaceY - (tmp_win->attr.y - tmp_win->title_height) < 15) {
914				PlaceX += Scr->RandomDisplacementX;
915				PlaceY += Scr->RandomDisplacementY;
916			}
917
918			random_placed = true;
919		}
920		else if(!(tmp_win->wmhints->flags & StateHint &&
921		                tmp_win->wmhints->initial_state == IconicState)) {
922			/* else prompt */
923			bool firsttime = true;
924			int found = 0;
925			int width, height;
926			XEvent event;
927
928			/* better wait until all the mouse buttons have been
929			 * released.
930			 */
931			while(1) {
932				unsigned int qpmask;
933				Window qproot;
934				int stat;
935
936				XUngrabServer(dpy);
937				XSync(dpy, 0);
938				XGrabServer(dpy);
939
940				qpmask = 0;
941				if(!XQueryPointer(dpy, Scr->Root, &qproot,
942				                  &JunkChild, &JunkX, &JunkY,
943				                  &AddingX, &AddingY, &qpmask)) {
944					qpmask = 0;
945				}
946
947				/* Clear out any but the Button bits */
948				qpmask &= (Button1Mask | Button2Mask | Button3Mask |
949				           Button4Mask | Button5Mask);
950
951				/*
952				 * watch out for changing screens
953				 */
954				if(firsttime) {
955					if(qproot != Scr->Root) {
956						int scrnum;
957						for(scrnum = 0; scrnum < NumScreens; scrnum++) {
958							if(qproot == RootWindow(dpy, scrnum)) {
959								break;
960							}
961						}
962						if(scrnum != NumScreens) {
963							PreviousScreen = scrnum;
964						}
965					}
966					if(Scr->currentvs) {
967						vroot = Scr->currentvs->window;
968					}
969					firsttime = false;
970				}
971				if(winbox) {
972					vroot = winbox->window;
973				}
974
975				/*
976				 * wait for buttons to come up; yuck
977				 */
978				if(qpmask != 0) {
979					continue;
980				}
981
982				/*
983				 * this will cause a warp to the indicated root
984				 */
985				stat = XGrabPointer(dpy, vroot, False,
986				                    ButtonPressMask | ButtonReleaseMask |
987				                    PointerMotionMask | PointerMotionHintMask,
988				                    GrabModeAsync, GrabModeAsync,
989				                    vroot, UpperLeftCursor, CurrentTime);
990				if(stat == GrabSuccess) {
991					break;
992				}
993			}
994
995			{
996				XRectangle ink_rect;
997				XRectangle logical_rect;
998
999				XmbTextExtents(Scr->SizeFont.font_set,
1000				               tmp_win->name, namelen,
1001				               &ink_rect, &logical_rect);
1002				width = SIZE_HINDENT + ink_rect.width;
1003				height = logical_rect.height + SIZE_VINDENT * 2;
1004
1005				XmbTextExtents(Scr->SizeFont.font_set,
1006				               ": ", 2,  NULL, &logical_rect);
1007				Scr->SizeStringOffset = width + logical_rect.width;
1008			}
1009
1010			XResizeWindow(dpy, Scr->SizeWindow, Scr->SizeStringOffset +
1011			              Scr->SizeStringWidth + SIZE_HINDENT, height);
1012			XMapRaised(dpy, Scr->SizeWindow);
1013			InstallRootColormap();
1014			FB(Scr->DefaultC.fore, Scr->DefaultC.back);
1015			XmbDrawImageString(dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
1016			                   Scr->NormalGC, SIZE_HINDENT,
1017			                   SIZE_VINDENT + Scr->SizeFont.ascent,
1018			                   tmp_win->name, namelen);
1019
1020			if(winbox) {
1021				ConstrainedToWinBox(tmp_win, AddingX, AddingY, &AddingX, &AddingY);
1022			}
1023
1024			AddingW = tmp_win->attr.width + bw2 + 2 * tmp_win->frame_bw3D;
1025			AddingH = tmp_win->attr.height + tmp_win->title_height +
1026			          bw2 + 2 * tmp_win->frame_bw3D;
1027			MoveOutline(vroot, AddingX, AddingY, AddingW, AddingH,
1028			            tmp_win->frame_bw, tmp_win->title_height + tmp_win->frame_bw3D);
1029
1030			XmbDrawImageString(dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
1031			                   Scr->NormalGC, width,
1032			                   SIZE_VINDENT + Scr->SizeFont.ascent, ": ", 2);
1033			DisplayPosition(tmp_win, AddingX, AddingY);
1034
1035			/*
1036			 * The TryTo*() and DoResize() calls below rely on having
1037			 * frame_{width,height} set, so set them.
1038			 */
1039			tmp_win->frame_width  = AddingW;
1040			tmp_win->frame_height = AddingH;
1041			/*SetFocus (NULL, CurrentTime);*/
1042			while(1) {
1043				if(Scr->OpenWindowTimeout) {
1044					const int fd = ConnectionNumber(dpy);
1045					while(!XCheckMaskEvent(dpy, ButtonMotionMask | ButtonPressMask, &event)) {
1046						fd_set mask;
1047						struct timeval timeout = {
1048							.tv_sec  = Scr->OpenWindowTimeout,
1049							.tv_usec = 0,
1050						};
1051
1052						FD_ZERO(&mask);
1053						FD_SET(fd, &mask);
1054						found = select(fd + 1, &mask, NULL, NULL, &timeout);
1055						if(found == 0) {
1056							break;
1057						}
1058					}
1059					if(found == 0) {
1060						break;
1061					}
1062				}
1063				else {
1064					found = 1;
1065					XMaskEvent(dpy, ButtonPressMask | PointerMotionMask, &event);
1066				}
1067				if(event.type == MotionNotify) {
1068					/* discard any extra motion events before a release */
1069					while(XCheckMaskEvent(dpy,
1070					                      ButtonMotionMask | ButtonPressMask, &event))
1071						if(event.type == ButtonPress) {
1072							break;
1073						}
1074				}
1075				FixRootEvent(&event);
1076				if(event.type == ButtonPress) {
1077					AddingX = event.xbutton.x_root;
1078					AddingY = event.xbutton.y_root;
1079
1080					/* TryTo*() need tmp_win->frame_{width,height} */
1081					TryToGrid(tmp_win, &AddingX, &AddingY);
1082					if(Scr->PackNewWindows) {
1083						TryToPack(tmp_win, &AddingX, &AddingY);
1084					}
1085
1086					/* DontMoveOff prohibits user from off-screen placement */
1087					if(Scr->DontMoveOff) {
1088						ConstrainByBorders(tmp_win, &AddingX, AddingW, &AddingY, AddingH);
1089					}
1090					break;
1091				}
1092
1093				if(event.type != MotionNotify) {
1094					continue;
1095				}
1096
1097				XQueryPointer(dpy, vroot, &JunkRoot, &JunkChild,
1098				              &JunkX, &JunkY, &AddingX, &AddingY, &JunkMask);
1099
1100				TryToGrid(tmp_win, &AddingX, &AddingY);
1101				if(Scr->PackNewWindows) {
1102					TryToPack(tmp_win, &AddingX, &AddingY);
1103				}
1104				if(Scr->DontMoveOff) {
1105					ConstrainByBorders(tmp_win, &AddingX, AddingW, &AddingY, AddingH);
1106				}
1107				MoveOutline(vroot, AddingX, AddingY, AddingW, AddingH,
1108				            tmp_win->frame_bw, tmp_win->title_height + tmp_win->frame_bw3D);
1109
1110				DisplayPosition(tmp_win, AddingX, AddingY);
1111			}
1112
1113			if(found) {
1114				if(event.xbutton.button == Button2) {
1115					int lastx, lasty;
1116					XRectangle logical_rect;
1117
1118					XmbTextExtents(Scr->SizeFont.font_set,
1119					               ": ", 2,  NULL, &logical_rect);
1120					Scr->SizeStringOffset = width + logical_rect.width;
1121
1122					XResizeWindow(dpy, Scr->SizeWindow, Scr->SizeStringOffset +
1123					              Scr->SizeStringWidth + SIZE_HINDENT, height);
1124
1125					XmbDrawImageString(dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
1126					                   Scr->NormalGC, width,
1127					                   SIZE_VINDENT + Scr->SizeFont.ascent, ": ", 2);
1128
1129					if(0/*Scr->AutoRelativeResize*/) {
1130						int dx = (tmp_win->attr.width / 4);
1131						int dy = (tmp_win->attr.height / 4);
1132
1133#define HALF_AVE_CURSOR_SIZE 8          /* so that it is visible */
1134						if(dx < HALF_AVE_CURSOR_SIZE + Scr->BorderLeft) {
1135							dx = HALF_AVE_CURSOR_SIZE + Scr->BorderLeft;
1136						}
1137						if(dy < HALF_AVE_CURSOR_SIZE + Scr->BorderTop) {
1138							dy = HALF_AVE_CURSOR_SIZE + Scr->BorderTop;
1139						}
1140#undef HALF_AVE_CURSOR_SIZE
1141						dx += (tmp_win->frame_bw + 1);
1142						dy += (bw2 + tmp_win->title_height + 1);
1143						if(AddingX + dx >= Scr->rootw - Scr->BorderRight) {
1144							dx = Scr->rootw - Scr->BorderRight - AddingX - 1;
1145						}
1146						if(AddingY + dy >= Scr->rooth - Scr->BorderBottom) {
1147							dy = Scr->rooth - Scr->BorderBottom - AddingY - 1;
1148						}
1149						if(dx > 0 && dy > 0) {
1150							XWarpPointer(dpy, None, None, 0, 0, 0, 0, dx, dy);
1151						}
1152					}
1153					else {
1154						XWarpPointer(dpy, None, vroot, 0, 0, 0, 0,
1155						             AddingX + AddingW / 2, AddingY + AddingH / 2);
1156					}
1157					AddStartResize(tmp_win, AddingX, AddingY, AddingW, AddingH);
1158
1159					lastx = -10000;
1160					lasty = -10000;
1161					while(1) {
1162						XMaskEvent(dpy,
1163						           ButtonReleaseMask | ButtonMotionMask, &event);
1164
1165						if(event.type == MotionNotify) {
1166							/* discard any extra motion events before a release */
1167							while(XCheckMaskEvent(dpy,
1168							                      ButtonMotionMask | ButtonReleaseMask, &event))
1169								if(event.type == ButtonRelease) {
1170									break;
1171								}
1172						}
1173						FixRootEvent(&event);
1174
1175						if(event.type == ButtonRelease) {
1176							AddEndResize(tmp_win);
1177							break;
1178						}
1179
1180						if(event.type != MotionNotify) {
1181							continue;
1182						}
1183
1184						/*
1185						 * XXX - if we are going to do a loop, we ought to consider
1186						 * using multiple GXxor lines so that we don't need to
1187						 * grab the server.
1188						 */
1189						XQueryPointer(dpy, vroot, &JunkRoot, &JunkChild,
1190						              &JunkX, &JunkY, &AddingX, &AddingY,
1191						              &JunkMask);
1192
1193						if(lastx != AddingX || lasty != AddingY) {
1194							resizeWhenAdd = true;
1195							/*
1196							 * DR() calls SetupWindow(), which uses
1197							 * frame_{width,height}.
1198							 */
1199							DoResize(AddingX, AddingY, tmp_win);
1200							resizeWhenAdd = false;
1201
1202							lastx = AddingX;
1203							lasty = AddingY;
1204						}
1205
1206					}
1207				}
1208				else if(event.xbutton.button == Button3) {
1209					int maxw = Scr->rootw - Scr->BorderRight  - AddingX - bw2;
1210					int maxh = Scr->rooth - Scr->BorderBottom - AddingY - bw2;
1211
1212					/*
1213					 * Make window go to bottom of screen, and clip to right edge.
1214					 * This is useful when popping up large windows and fixed
1215					 * column text windows.
1216					 */
1217					if(AddingW > maxw) {
1218						AddingW = maxw;
1219					}
1220					AddingH = maxh;
1221
1222					ConstrainSize(tmp_win, &AddingW, &AddingH);   /* w/o borders */
1223					AddingW += bw2;
1224					AddingH += bw2;
1225					XMaskEvent(dpy, ButtonReleaseMask, &event);
1226				}
1227				else {
1228					XMaskEvent(dpy, ButtonReleaseMask, &event);
1229				}
1230			}
1231			MoveOutline(vroot, 0, 0, 0, 0, 0, 0);
1232			XUnmapWindow(dpy, Scr->SizeWindow);
1233			UninstallRootColormap();
1234			XUngrabPointer(dpy, CurrentTime);
1235
1236			tmp_win->attr.x = AddingX;
1237			tmp_win->attr.y = AddingY + tmp_win->title_height;
1238			tmp_win->attr.width = AddingW - bw2 - 2 * tmp_win->frame_bw3D;
1239			tmp_win->attr.height = AddingH - tmp_win->title_height -
1240			                       bw2 - 2 * tmp_win->frame_bw3D;
1241
1242			XUngrabServer(dpy);
1243		}
1244	}
1245	else {
1246		/*
1247		 * Put it where asked, mod title bar.  If the gravity is towards
1248		 * the top, move it by the title height.
1249		 */
1250		if(gravy < 0) {
1251			tmp_win->attr.y -= gravy * tmp_win->title_height;
1252		}
1253	}
1254
1255
1256#ifdef DEBUG
1257	fprintf(stderr, "  position window  %d, %d  %dx%d\n",
1258	        tmp_win->attr.x,
1259	        tmp_win->attr.y,
1260	        tmp_win->attr.width,
1261	        tmp_win->attr.height);
1262#endif
1263
1264
1265	/*
1266	 * Possibly need to tweak what it thinks of as its position to
1267	 * account for borders.  XXX Verify conditionalization and math.
1268	 */
1269	if(!Scr->ClientBorderWidth) {
1270		int delta = tmp_win->attr.border_width - tmp_win->frame_bw -
1271		            tmp_win->frame_bw3D;
1272		tmp_win->attr.x += gravx * delta;
1273		tmp_win->attr.y += gravy * delta;
1274	}
1275
1276
1277	/*
1278	 * Init the title width to the window's width.  This will be right as
1279	 * long as you're not SqueezeTitle'ing; if you are, we rejigger it in
1280	 * SetupFrame().
1281	 */
1282	tmp_win->title_width = tmp_win->attr.width;
1283
1284
1285	/*
1286	 * Figure initial screen size of writing out the window name.  This
1287	 * is needed when laying out titlebar bits (down in the call chain
1288	 * inside SetupFrame()).  The event handler updates this when it
1289	 * changes.
1290	 */
1291	{
1292		XRectangle logical_rect;
1293		XmbTextExtents(Scr->TitleBarFont.font_set, tmp_win->name, namelen,
1294		               NULL, &logical_rect);
1295		tmp_win->name_width = logical_rect.width;
1296	}
1297
1298
1299	/* Remove original border if there is one; we make our own now */
1300	if(tmp_win->old_bw) {
1301		XSetWindowBorderWidth(dpy, tmp_win->w, 0);
1302	}
1303
1304
1305	/*
1306	 * Setup various color bits
1307	 */
1308#define SETC(lst, save) GetColorFromList(Scr->lst, tmp_win->name, \
1309                &tmp_win->class, &tmp_win->save)
1310
1311	/* No distinction fore/back for borders in the lists */
1312	tmp_win->borderC.fore = Scr->BorderColorC.fore;
1313	tmp_win->borderC.back = Scr->BorderColorC.back;
1314	SETC(BorderColorL, borderC.fore);
1315	SETC(BorderColorL, borderC.back);
1316
1317	tmp_win->border_tile.fore = Scr->BorderTileC.fore;
1318	tmp_win->border_tile.back = Scr->BorderTileC.back;
1319	SETC(BorderTileForegroundL, border_tile.fore);
1320	SETC(BorderTileBackgroundL, border_tile.back);
1321
1322	tmp_win->title.fore = Scr->TitleC.fore;
1323	tmp_win->title.back = Scr->TitleC.back;
1324	SETC(TitleForegroundL, title.fore);
1325	SETC(TitleBackgroundL, title.back);
1326
1327#undef SETC
1328
1329	/* Shading on 3d bits */
1330	if(Scr->use3Dtitles  && !Scr->BeNiceToColormap) {
1331		GetShadeColors(&tmp_win->title);
1332	}
1333	if(Scr->use3Dborders && !Scr->BeNiceToColormap) {
1334		GetShadeColors(&tmp_win->borderC);
1335		GetShadeColors(&tmp_win->border_tile);
1336	}
1337
1338
1339	/*
1340	 * Following bits are more active, and we want to make sure nothing
1341	 * else gets to do anything with the server while we're doing it.
1342	 *
1343	 * Minor investigations seems to suggest we could pull a number of
1344	 * these things out (mostly to later, but probably some to earlier)
1345	 * so we keep the server grabbed for a shorter period of time.  I'm
1346	 * not putting significant effort into finding out what we could pull
1347	 * out because it's already plenty fast, but there is probably fruit
1348	 * that could be plucked if somebody finds it not so.
1349	 */
1350	XGrabServer(dpy);
1351
1352
1353	/*
1354	 * Make sure the client window still exists.  We don't want to leave an
1355	 * orphan frame window if it doesn't.  Since we now have the server
1356	 * grabbed, the window can't disappear later without having been
1357	 * reparented, so we'll get a DestroyNotify for it.  We won't have
1358	 * gotten one for anything up to here, however.
1359	 */
1360	if(XGetGeometry(dpy, tmp_win->w, &JunkRoot, &JunkX, &JunkY,
1361	                &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth) == 0) {
1362		TwmWindow *prev = tmp_win->ring.prev, *next = tmp_win->ring.next;
1363
1364		if(prev) {
1365			prev->ring.next = next;
1366		}
1367		if(next) {
1368			next->ring.prev = prev;
1369		}
1370		if(Scr->Ring == tmp_win) {
1371			Scr->Ring = (next != tmp_win ? next : NULL);
1372		}
1373		if(!Scr->Ring || Scr->RingLeader == tmp_win) {
1374			Scr->RingLeader = Scr->Ring;
1375		}
1376
1377		/* XXX Leaky as all hell */
1378		free(tmp_win);
1379		XUngrabServer(dpy);
1380		return(NULL);
1381	}
1382
1383
1384	/* Link the window into our list of all the TwmWindow's */
1385	tmp_win->next = Scr->FirstWindow;
1386	if(Scr->FirstWindow != NULL) {
1387		Scr->FirstWindow->prev = tmp_win;
1388	}
1389	tmp_win->prev = NULL;
1390	Scr->FirstWindow = tmp_win;
1391
1392
1393
1394	/*
1395	 * Start creating the other X windows we wrap around it for
1396	 * decorations.  X-ref discussion in win_decorations.c for the
1397	 * details of what they all are and why they're there.
1398	 *
1399	 * XXX Good candidate for moving out into a helper function...
1400	 */
1401
1402
1403	/*
1404	 * First, the frame
1405	 */
1406	{
1407		unsigned long valuemask;
1408		XSetWindowAttributes attributes;
1409
1410		/*
1411		 * Figure size/position.
1412		 */
1413		tmp_win->frame_x = tmp_win->attr.x + tmp_win->old_bw
1414		                   - tmp_win->frame_bw - tmp_win->frame_bw3D;
1415		tmp_win->frame_y = tmp_win->attr.y - tmp_win->title_height
1416		                   + tmp_win->old_bw
1417		                   - tmp_win->frame_bw - tmp_win->frame_bw3D;
1418		tmp_win->frame_width  = tmp_win->attr.width  + 2 * tmp_win->frame_bw3D;
1419		tmp_win->frame_height = tmp_win->attr.height + 2 * tmp_win->frame_bw3D
1420		                        + tmp_win->title_height;
1421
1422		/* Adjust based on hints */
1423		ConstrainSize(tmp_win, &tmp_win->frame_width, &tmp_win->frame_height);
1424
1425		/*
1426		 * Adjust as necessary to keep things on-screen.  If we [ctwm]
1427		 * chose the position, CBB() involves checking things like
1428		 * MoveOffResistance etc to keep it on.
1429		 */
1430		if(random_placed) {
1431			ConstrainByBorders(tmp_win, &tmp_win->frame_x, tmp_win->frame_width,
1432			                   &tmp_win->frame_y, tmp_win->frame_height);
1433		}
1434
1435		/* No matter what, make sure SOME part of the window is on-screen */
1436		if((tmp_win->frame_x > Scr->rootw) ||
1437		                (tmp_win->frame_y > Scr->rooth) ||
1438		                ((int)(tmp_win->frame_x + tmp_win->frame_width)  < 0) ||
1439		                ((int)(tmp_win->frame_y + tmp_win->frame_height) < 0)) {
1440			tmp_win->frame_x = 0;
1441			tmp_win->frame_y = 0;
1442		}
1443
1444		/* May need adjusting for vscreens too */
1445		DealWithNonSensicalGeometries(dpy, vroot, tmp_win);
1446
1447
1448		/*
1449		 * Setup the X attributes for the frame.
1450		 */
1451		valuemask = CWBackPixmap | CWBorderPixel | CWBackPixel
1452		            | CWCursor | CWEventMask;
1453		attributes.background_pixmap = None;
1454		attributes.border_pixel = tmp_win->border_tile.back;
1455		attributes.background_pixel = tmp_win->border_tile.back;
1456		attributes.cursor = Scr->FrameCursor;
1457		attributes.event_mask = (SubstructureRedirectMask
1458		                         | ButtonPressMask | ButtonReleaseMask
1459		                         | EnterWindowMask | LeaveWindowMask
1460		                         | ExposureMask);
1461
1462		/*
1463		 * If we have BorderResizeCursors, we need to know about motions
1464		 * in the window to know when to change (e.g., for corners).
1465		 */
1466		if(Scr->BorderCursors) {
1467			attributes.event_mask |= PointerMotionMask;
1468		}
1469
1470		/*
1471		 * If the real window specified save_under or a specific gravity,
1472		 * set them on the frame too.
1473		 */
1474		if(tmp_win->attr.save_under) {
1475			attributes.save_under = True;
1476			valuemask |= CWSaveUnder;
1477		}
1478		if(tmp_win->hints.flags & PWinGravity) {
1479			attributes.win_gravity = tmp_win->hints.win_gravity;
1480			valuemask |= CWWinGravity;
1481		}
1482
1483
1484		/* And create */
1485		tmp_win->frame = XCreateWindow(dpy, vroot,
1486		                               tmp_win->frame_x, tmp_win->frame_y,
1487		                               tmp_win->frame_width,
1488		                               tmp_win->frame_height,
1489		                               tmp_win->frame_bw,
1490		                               Scr->d_depth, CopyFromParent,
1491		                               Scr->d_visual, valuemask, &attributes);
1492		if(Scr->NameDecorations) {
1493			XStoreName(dpy, tmp_win->frame, "CTWM frame");
1494		}
1495	}
1496
1497
1498	/*
1499	 * Next, the titlebar, if we have one
1500	 */
1501	if(tmp_win->title_height) {
1502		unsigned long valuemask;
1503		XSetWindowAttributes attributes;
1504		int x, y;
1505
1506		/*
1507		 * We need to know about keys/buttons and exposure of the
1508		 * titlebar, for bindings and repaining.  And leave to X server
1509		 * bits about border/background.
1510		 */
1511		valuemask = (CWEventMask | CWDontPropagate
1512		             | CWBorderPixel | CWBackPixel);
1513		attributes.event_mask = (KeyPressMask | ButtonPressMask
1514		                         | ButtonReleaseMask | ExposureMask);
1515		attributes.do_not_propagate_mask = PointerMotionMask;
1516		attributes.border_pixel = tmp_win->borderC.back;
1517		attributes.background_pixel = tmp_win->title.back;
1518
1519
1520		/* Create */
1521		x = y = tmp_win->frame_bw3D - tmp_win->frame_bw;
1522		tmp_win->title_w = XCreateWindow(dpy, tmp_win->frame, x, y,
1523		                                 tmp_win->attr.width,
1524		                                 Scr->TitleHeight, tmp_win->frame_bw,
1525		                                 Scr->d_depth, CopyFromParent,
1526		                                 Scr->d_visual, valuemask, &attributes);
1527		if(Scr->NameDecorations) {
1528			XStoreName(dpy, tmp_win->title_w, "CTWM titlebar");
1529		}
1530	}
1531	else {
1532		tmp_win->title_w = None;
1533		tmp_win->squeeze_info = NULL;
1534	}
1535
1536
1537	/*
1538	 * If we're highlighting borders on focus, we need the pixmap to do
1539	 * it.
1540	 *
1541	 * XXX I'm not at all sure this can't just be global and shared, so
1542	 * we don't have to create one per window...
1543	 */
1544	if(tmp_win->highlight) {
1545		char *which;
1546
1547		if(Scr->use3Dtitles && (Scr->Monochrome != COLOR)) {
1548			which = "black";
1549		}
1550		else {
1551			which = "gray";
1552		}
1553		tmp_win->gray = mk_blackgray_pixmap(which, vroot,
1554		                                    tmp_win->border_tile.fore,
1555		                                    tmp_win->border_tile.back);
1556
1557		tmp_win->hasfocusvisible = true;
1558		SetFocusVisualAttributes(tmp_win, false);
1559	}
1560	else {
1561		tmp_win->gray = None;
1562	}
1563
1564
1565	/*
1566	 * Setup OTP bits for stacking
1567	 */
1568	OtpAdd(tmp_win, WinWin);
1569
1570
1571	/*
1572	 * Setup the stuff inside the titlebar window, if we have it.  If we
1573	 * don't, fake up the coordinates where the titlebar would be for
1574	 * <reasons>.
1575	 */
1576	if(tmp_win->title_w) {
1577		ComputeTitleLocation(tmp_win);
1578		CreateWindowTitlebarButtons(tmp_win);
1579		XMoveWindow(dpy, tmp_win->title_w,
1580		            tmp_win->title_x, tmp_win->title_y);
1581		XDefineCursor(dpy, tmp_win->title_w, Scr->TitleCursor);
1582	}
1583	else {
1584		tmp_win->title_x = tmp_win->frame_bw3D - tmp_win->frame_bw;
1585		tmp_win->title_y = tmp_win->frame_bw3D - tmp_win->frame_bw;
1586	}
1587
1588
1589	/*
1590	 * Setup various events we want to hear about related to this window.
1591	 */
1592	{
1593		unsigned long valuemask;
1594		XSetWindowAttributes attributes;
1595
1596		valuemask = (CWEventMask | CWDontPropagate);
1597		attributes.event_mask = (StructureNotifyMask | PropertyChangeMask
1598		                         | ColormapChangeMask | VisibilityChangeMask
1599		                         | FocusChangeMask
1600		                         | EnterWindowMask | LeaveWindowMask);
1601		attributes.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask
1602		                                   | PointerMotionMask;
1603		XChangeWindowAttributes(dpy, tmp_win->w, valuemask, &attributes);
1604	}
1605
1606
1607	/*
1608	 * Map up the title window if we have one.  As a sub-window of the
1609	 * frame, it'll only actually show up in the screen if the frame
1610	 * does, of course.
1611	 */
1612	if(tmp_win->title_w) {
1613		XMapWindow(dpy, tmp_win->title_w);
1614	}
1615
1616
1617	/*
1618	 * If the server's got Shape, look up info about the window's
1619	 * Shape'ing, and subscribe to notifications about changes in it.
1620	 * Actually, it's only the bounding we care about; the rest is
1621	 * thrown away.
1622	 */
1623	if(HasShape) {
1624		int xws, yws, xbs, ybs;
1625		unsigned wws, hws, wbs, hbs;
1626		int boundingShaped, clipShaped;
1627
1628		XShapeSelectInput(dpy, tmp_win->w, ShapeNotifyMask);
1629		XShapeQueryExtents(dpy, tmp_win->w,
1630		                   &boundingShaped, &xws, &yws, &wws, &hws,
1631		                   &clipShaped, &xbs, &ybs, &wbs, &hbs);
1632		tmp_win->wShaped = boundingShaped;
1633	}
1634
1635
1636	/*
1637	 * If it's a normal window (i.e., not one of ctwm's internal ones),
1638	 * add it to the "save set", which means that even if ctwm disappears
1639	 * without doing any cleanup, it'll still show back up on the screen
1640	 * like normal.  Otherwise, if you kill or segfault ctwm, all the
1641	 * other things you're running get their windows lost.
1642	 *
1643	 * XXX Conditional may be a little on the short side; I'm not sure it
1644	 * catches all of our internals...
1645	 */
1646	if(!(tmp_win->isiconmgr || tmp_win->iswspmgr || tmp_win->isoccupy)) {
1647		XAddToSaveSet(dpy, tmp_win->w);
1648	}
1649
1650
1651	/*
1652	 * Now reparent the real window into our frame.
1653	 */
1654	XReparentWindow(dpy, tmp_win->w, tmp_win->frame, tmp_win->frame_bw3D,
1655	                tmp_win->title_height + tmp_win->frame_bw3D);
1656
1657	/*
1658	 * Reparenting generates an UnmapNotify event, followed by a
1659	 * MapNotify.  Set the map state to false to prevent a transition
1660	 * back to WithdrawnState in HandleUnmapNotify.  ->mapped gets set
1661	 * correctly again in HandleMapNotify.
1662	 */
1663	tmp_win->mapped = false;
1664
1665
1666	/*
1667	 * Call SetupFrame() which does all sorta of magic figuring to set
1668	 * the various coordinates and offsets and whatnot for all the pieces
1669	 * inside our frame.
1670	 */
1671	SetupFrame(tmp_win, tmp_win->frame_x, tmp_win->frame_y,
1672	           tmp_win->frame_width, tmp_win->frame_height, -1, true);
1673
1674
1675	/*
1676	 * Don't setup the icon window and its bits; when the window is
1677	 * iconified the first time, that handler will do what needs to be
1678	 * done for it, so we don't have to.
1679	 */
1680
1681
1682	/*
1683	 * If it's anything other than our own icon manager, setup button/key
1684	 * bindings for it.  For icon managers, this is done for them at the
1685	 * end of CreateIconManagers(), not here.  X-ref comments there and
1686	 * on the function defs below for some discussion about whether it
1687	 * _should_ work this way.
1688	 */
1689	if(!tmp_win->isiconmgr) {
1690		GrabButtons(tmp_win);
1691		GrabKeys(tmp_win);
1692	}
1693
1694
1695	/* Add this window to the appropriate icon manager[s] */
1696	AddIconManager(tmp_win);
1697
1698
1699	/*
1700	 * Stash up info about this TwmWindow and its screen in contexts on
1701	 * the real window and our various decorations around it.  This is
1702	 * how we find out what TwmWindow things like events are happening
1703	 * in.
1704	 */
1705#define SETCTXS(win) do { \
1706                XSaveContext(dpy, win, TwmContext, (XPointer) tmp_win); \
1707                XSaveContext(dpy, win, ScreenContext, (XPointer) Scr); \
1708        } while(0)
1709
1710	/* The real window and our frame */
1711	SETCTXS(tmp_win->w);
1712	SETCTXS(tmp_win->frame);
1713
1714	/* Cram that all into any titlebar [sub]windows too */
1715	if(tmp_win->title_height) {
1716		int i;
1717		int nb = Scr->TBInfo.nleft + Scr->TBInfo.nright;
1718
1719		SETCTXS(tmp_win->title_w);
1720
1721		for(i = 0; i < nb; i++) {
1722			SETCTXS(tmp_win->titlebuttons[i].window);
1723		}
1724		if(tmp_win->hilite_wl) {
1725			SETCTXS(tmp_win->hilite_wl);
1726		}
1727		if(tmp_win->hilite_wr) {
1728			SETCTXS(tmp_win->hilite_wr);
1729		}
1730		if(tmp_win->lolite_wl) {
1731			SETCTXS(tmp_win->lolite_wl);
1732		}
1733		if(tmp_win->lolite_wr) {
1734			SETCTXS(tmp_win->lolite_wr);
1735		}
1736	}
1737
1738#undef SETCTXS
1739
1740	/*
1741	 * OK, that's all we need to do while the server's grabbed.  After
1742	 * this point, other clients might sneak in stuff between our
1743	 * actions, so they can't be considered atomic anymore.
1744	 */
1745	XUngrabServer(dpy);
1746
1747
1748	/*
1749	 * If we were in the middle of a menu activated function that was
1750	 * deferred (x-ref comments on DeferExecution()), re-grab to re-set
1751	 * the special cursor, since we may have reset it above.
1752	 *
1753	 * Why could that possibly happen?  It would require a window coming
1754	 * up and needing to be Add'd in the middle of selecting a window to
1755	 * apply a function to, which is a pretty rare case, but I s'pose not
1756	 * impossible...
1757	 */
1758	if(RootFunction) {
1759		ReGrab();
1760	}
1761
1762
1763	/*
1764	 * Add to the workspace manager.  Unless this IS the workspace
1765	 * manager, in which case that would just be silly.
1766	 */
1767	if(!tmp_win->iswspmgr) {
1768		WMapAddWindow(tmp_win);
1769	}
1770
1771
1772	/*
1773	 * If ths window being created is a new captive [sub-]ctwm, we setup
1774	 * a property on it for unclear reasons.  x-ref comments on the
1775	 * function.
1776	 */
1777	SetPropsIfCaptiveCtwm(tmp_win);
1778
1779
1780	/*
1781	 * Init saved geometry with the current, as if f.savegeometry was
1782	 * called on the window right away.  That way f.restoregeometry can't
1783	 * get confuzzled.
1784	 */
1785	savegeometry(tmp_win);
1786
1787
1788	/*
1789	 * And that's it; we created all the bits!
1790	 */
1791	return tmp_win;
1792}
1793
1794
1795
1796
1797/*
1798 * XXX GrabButtons() and GrabKeys() are in a slightly odd state.  They're
1799 * almost strictly a piece of the window-adding process, which is why
1800 * they're here.  They're not static because the icon manager setup in
1801 * CreateIconManagers() calls them explicitly, because they're also
1802 * explicitly skipped in AddWindow() for icon manager windows.  I'm not
1803 * sure that's necessary; x-ref comment in CIM() about some ideas on the
1804 * matter.
1805 *
1806 * This should be resolved.  Until it is, they're left exported so the
1807 * iconmgr code can all them, and they're left here (rather than moved to
1808 * win_utils) on the guess that it may well be resolvable and so they'd
1809 * stay here and be staticized in the end.
1810 */
1811
1812/***********************************************************************
1813 *
1814 *  Procedure:
1815 *      GrabButtons - grab needed buttons for the window
1816 *
1817 *  Inputs:
1818 *      tmp_win - the twm window structure to use
1819 *
1820 ***********************************************************************
1821 */
1822
1823#define grabbutton(button, modifier, window, pointer_mode) \
1824        XGrabButton (dpy, button, modifier, window,  \
1825                True, ButtonPressMask | ButtonReleaseMask, \
1826                pointer_mode, GrabModeAsync, None,  \
1827                Scr->FrameCursor);
1828
1829void GrabButtons(TwmWindow *tmp_win)
1830{
1831	FuncButton *tmp;
1832	int i;
1833	unsigned int ModifierMask[8] = { ShiftMask, ControlMask, LockMask,
1834	                                 Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask,
1835	                                 Mod5Mask
1836	                               };
1837
1838	for(tmp = Scr->FuncButtonRoot.next; tmp != NULL; tmp = tmp->next) {
1839		if((tmp->cont != C_WINDOW) || (tmp->func == 0)) {
1840			continue;
1841		}
1842		grabbutton(tmp->num, tmp->mods, tmp_win->frame, GrabModeAsync);
1843
1844		for(i = 0 ; i < 8 ; i++) {
1845			if((Scr->IgnoreModifier & ModifierMask [i]) && !(tmp->mods & ModifierMask [i]))
1846				grabbutton(tmp->num, tmp->mods | ModifierMask [i],
1847				           tmp_win->frame, GrabModeAsync);
1848		}
1849	}
1850	if(Scr->ClickToFocus) {
1851		grabbutton(AnyButton, None, tmp_win->w, GrabModeSync);
1852		for(i = 0 ; i < 8 ; i++) {
1853			grabbutton(AnyButton, ModifierMask [i], tmp_win->w, GrabModeSync);
1854		}
1855	}
1856	else if(Scr->RaiseOnClick) {
1857		grabbutton(Scr->RaiseOnClickButton, None, tmp_win->w, GrabModeSync);
1858		for(i = 0 ; i < 8 ; i++) {
1859			grabbutton(Scr->RaiseOnClickButton,
1860			           ModifierMask [i], tmp_win->w, GrabModeSync);
1861		}
1862	}
1863}
1864#undef grabbutton
1865
1866/***********************************************************************
1867 *
1868 *  Procedure:
1869 *      GrabKeys - grab needed keys for the window
1870 *
1871 *  Inputs:
1872 *      tmp_win - the twm window structure to use
1873 *
1874 ***********************************************************************
1875 */
1876
1877#define grabkey(funckey, modifier, window) \
1878        XGrabKey(dpy, funckey->keycode, funckey->mods | modifier, window, True, \
1879                GrabModeAsync, GrabModeAsync);
1880#define ungrabkey(funckey, modifier, window) \
1881        XUngrabKey (dpy, funckey->keycode, funckey->mods | modifier, window);
1882
1883void GrabKeys(TwmWindow *tmp_win)
1884{
1885	FuncKey *tmp;
1886	IconMgr *p;
1887	int i;
1888	unsigned int ModifierMask[8] = { ShiftMask, ControlMask, LockMask,
1889	                                 Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask,
1890	                                 Mod5Mask
1891	                               };
1892
1893	for(tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next) {
1894		switch(tmp->cont) {
1895			case C_WINDOW:
1896				/* case C_WORKSPACE: */
1897#define AltMask (Alt1Mask | Alt2Mask | Alt3Mask | Alt4Mask | Alt5Mask)
1898				if(tmp->mods & AltMask) {
1899					break;
1900				}
1901#undef AltMask
1902
1903				grabkey(tmp, 0, tmp_win->w);
1904
1905				for(i = 0 ; i < 8 ; i++) {
1906					if((Scr->IgnoreModifier & ModifierMask [i]) &&
1907					                !(tmp->mods & ModifierMask [i])) {
1908						grabkey(tmp, ModifierMask [i], tmp_win->w);
1909					}
1910				}
1911				break;
1912
1913			case C_ICON:
1914				if(!tmp_win->icon || tmp_win->icon->w) {
1915					break;
1916				}
1917
1918				grabkey(tmp, 0, tmp_win->icon->w);
1919
1920				for(i = 0 ; i < 8 ; i++) {
1921					if((Scr->IgnoreModifier & ModifierMask [i]) &&
1922					                !(tmp->mods & ModifierMask [i])) {
1923						grabkey(tmp, ModifierMask [i], tmp_win->icon->w);
1924					}
1925				}
1926				break;
1927
1928			case C_TITLE:
1929				if(!tmp_win->title_w) {
1930					break;
1931				}
1932
1933				grabkey(tmp, 0, tmp_win->title_w);
1934
1935				for(i = 0 ; i < 8 ; i++) {
1936					if((Scr->IgnoreModifier & ModifierMask [i]) &&
1937					                !(tmp->mods & ModifierMask [i])) {
1938						grabkey(tmp, ModifierMask [i], tmp_win->title_w);
1939					}
1940				}
1941				break;
1942
1943			case C_NAME:
1944				grabkey(tmp, 0, tmp_win->w);
1945				for(i = 0 ; i < 8 ; i++) {
1946					if((Scr->IgnoreModifier & ModifierMask [i]) &&
1947					                !(tmp->mods & ModifierMask [i])) {
1948						grabkey(tmp, ModifierMask [i], tmp_win->w);
1949					}
1950				}
1951				if(tmp_win->icon && tmp_win->icon->w) {
1952					grabkey(tmp, 0, tmp_win->icon->w);
1953
1954					for(i = 0 ; i < 8 ; i++) {
1955						if((Scr->IgnoreModifier & ModifierMask [i]) &&
1956						                !(tmp->mods & ModifierMask [i])) {
1957							grabkey(tmp, ModifierMask [i], tmp_win->icon->w);
1958						}
1959					}
1960				}
1961				if(tmp_win->title_w) {
1962					grabkey(tmp, 0, tmp_win->title_w);
1963
1964					for(i = 0 ; i < 8 ; i++) {
1965						if((Scr->IgnoreModifier & ModifierMask [i]) &&
1966						                !(tmp->mods & ModifierMask [i])) {
1967							grabkey(tmp, ModifierMask [i], tmp_win->title_w);
1968						}
1969					}
1970				}
1971				break;
1972
1973#ifdef EWMH_DESKTOP_ROOT
1974			case C_ROOT:
1975				if(tmp_win->ewmhWindowType != wt_Desktop) {
1976					break;
1977				}
1978
1979				grabkey(tmp, 0, tmp_win->w);
1980
1981				for(i = 0 ; i < 8 ; i++) {
1982					if((Scr->IgnoreModifier & ModifierMask [i]) &&
1983					                !(tmp->mods & ModifierMask [i])) {
1984						grabkey(tmp, ModifierMask [i], tmp_win->w);
1985					}
1986				}
1987				break;
1988#endif /* EWMH */
1989
1990				/*
1991				case C_ROOT:
1992				    XGrabKey(dpy, tmp->keycode, tmp->mods, Scr->Root, True,
1993				        GrabModeAsync, GrabModeAsync);
1994				    break;
1995				*/
1996		}
1997	}
1998	for(tmp = Scr->FuncKeyRoot.next; tmp != NULL; tmp = tmp->next) {
1999		if(tmp->cont == C_ICONMGR && !Scr->NoIconManagers) {
2000			for(p = Scr->iconmgr; p != NULL; p = p->next) {
2001				ungrabkey(tmp, 0, p->twm_win->w);
2002
2003				for(i = 0 ; i < 8 ; i++) {
2004					if((Scr->IgnoreModifier & ModifierMask [i]) &&
2005					                !(tmp->mods & ModifierMask [i])) {
2006						ungrabkey(tmp, ModifierMask [i], p->twm_win->w);
2007					}
2008				}
2009			}
2010		}
2011	}
2012}
2013#undef grabkey
2014#undef ungrabkey
2015
2016
2017/*
2018 * This is largely for Xinerama support with VirtualScreens.
2019 * In this case, windows may be on something other then the main screen
2020 * on startup, or the mapping may be relative to the right side of the
2021 * screen, which is on a different monitor, which will cause issues with
2022 * the virtual screens.
2023 *
2024 * It probably needs to be congnizant of windows that are actually owned by
2025 * other workspaces, and ignore them (this needs to be revisited), or perhaps
2026 * that functionality is appropriate in AddWindow().  This needs to be dug into
2027 * more deply.
2028 *
2029 * this approach assumes screens that are next to each other horizontally,
2030 * Other possibilities need to be investigated and accounted for.
2031 */
2032static void
2033DealWithNonSensicalGeometries(Display *mydpy, Window vroot,
2034                              TwmWindow *tmp_win)
2035{
2036	Window              vvroot;
2037	int                 x, y;
2038	unsigned int        w, h;
2039	unsigned int        j;
2040	VirtualScreen       *myvs, *vs;
2041	int                 dropx = 0;
2042
2043	if(! vroot) {
2044		return;
2045	}
2046
2047	if(!(XGetGeometry(mydpy, vroot, &vvroot, &x, &y, &w, &h, &j, &j))) {
2048		return;
2049	}
2050
2051	myvs = findIfVScreenOf(x, y);
2052
2053	/*
2054	 * probably need to rethink this  for unmapped vs's.  ugh.
2055	 */
2056	if(!myvs) {
2057		return;
2058	}
2059
2060	for(vs = myvs->next; vs; vs = vs->next) {
2061		dropx += vs->w;
2062	}
2063
2064	for(vs = Scr->vScreenList; vs && vs != myvs; vs = vs->next) {
2065		dropx -= vs->w;
2066	}
2067
2068	if(tmp_win->frame_x > 0 && tmp_win->frame_x >= w) {
2069		tmp_win->frame_x -= abs(dropx);
2070	}
2071	else {
2072	}
2073
2074}
2075