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