~ctwm/ctwm/trunk

523.1.1 by Matthew Fuller
Inagurate a win_utils.c with GetWindowSizeHints().
1
/*
2
 * Window-handling utility funcs
3
 */
4
5
#include "ctwm.h"
6
7
#include <stdio.h>
523.1.10 by Matthew Fuller
Move {Get,Free}WMPropertyString into win_utils.
8
#include <stdlib.h>
9
10
#include <X11/Xatom.h>
11
12
#include "add_window.h" // NoName
523.1.2 by Matthew Fuller
Move FetchWmProtocols() over to win_utils as well.
13
#include "ctwm_atoms.h"
523.1.23 by Matthew Fuller
Move DisplayPosition() off into win_utils as well. Stick some
14
#include "drawing.h"
539.1.18 by Matthew Fuller
WarpToWindow() also has no relation to menus and is broadly used
15
#include "events.h"
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
16
#include "event_internal.h" // Temp?
615.1.22 by Matthew Fuller
Add StrictWinNameEncoding support code.
17
#ifdef EWMH
18
# include "ewmh_atoms.h"
19
#endif
523.1.20 by Matthew Fuller
Getting and setting WM_STATE props is pretty obviously a window util,
20
#include "icons.h"
625.1.1 by Matthew Fuller
Extract the adjustment of wmhints stuff into a separate function for
21
#include "list.h"
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
22
#include "occupation.h"
23
#include "otp.h"
614.1.1 by Maxime Soulé
First step of xrandr integration
24
#include "r_area.h"
614.1.27 by Maxime Soulé
Monitor edge detection reworked: corrects some 1-pixel-wide-problems
25
#include "r_area_list.h"
614.2.4 by Matthew Fuller
r_layout.h includes r_structs.h, so including it in screen.h
26
#include "r_layout.h"
523.1.1 by Matthew Fuller
Inagurate a win_utils.c with GetWindowSizeHints().
27
#include "screen.h"
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
28
#include "util.h"
538.1.3 by Matthew Fuller
Rename the decorations*.h to win_decorations*.h.
29
#include "win_decorations.h"
539.1.18 by Matthew Fuller
WarpToWindow() also has no relation to menus and is broadly used
30
#include "win_ops.h"
523.1.1 by Matthew Fuller
Inagurate a win_utils.c with GetWindowSizeHints().
31
#include "win_utils.h"
539.1.18 by Matthew Fuller
WarpToWindow() also has no relation to menus and is broadly used
32
#include "workspace_utils.h"
523.1.1 by Matthew Fuller
Inagurate a win_utils.c with GetWindowSizeHints().
33
34
35
/*
36
 * Fill in size hints for a window from WM_NORMAL_HINTS prop.
37
 *
38
 * Formerly in add_window.c
39
 */
40
void
41
GetWindowSizeHints(TwmWindow *tmp)
42
{
43
	long supplied = 0;
44
	XSizeHints *hints = &tmp->hints;
45
46
	if(!XGetWMNormalHints(dpy, tmp->w, hints, &supplied)) {
47
		hints->flags = 0;
48
	}
49
50
	if(hints->flags & PResizeInc) {
51
		if(hints->width_inc == 0) {
52
			hints->width_inc = 1;
53
		}
54
		if(hints->height_inc == 0) {
55
			hints->height_inc = 1;
56
		}
57
	}
58
59
	if(!(supplied & PWinGravity) && (hints->flags & USPosition)) {
60
		static int gravs[] = { SouthEastGravity, SouthWestGravity,
61
		                       NorthEastGravity, NorthWestGravity
62
		                     };
63
		int right =  tmp->attr.x + tmp->attr.width + 2 * tmp->old_bw;
64
		int bottom = tmp->attr.y + tmp->attr.height + 2 * tmp->old_bw;
65
		hints->win_gravity =
66
		        gravs[((Scr->rooth - bottom <
67
		                tmp->title_height + 2 * tmp->frame_bw3D) ? 0 : 2) |
68
		              ((Scr->rootw - right   <
69
		                tmp->title_height + 2 * tmp->frame_bw3D) ? 0 : 1)];
70
		hints->flags |= PWinGravity;
71
	}
72
73
	/* Check for min size < max size */
74
	if((hints->flags & (PMinSize | PMaxSize)) == (PMinSize | PMaxSize)) {
75
		if(hints->max_width < hints->min_width) {
76
			if(hints->max_width > 0) {
77
				hints->min_width = hints->max_width;
78
			}
79
			else if(hints->min_width > 0) {
80
				hints->max_width = hints->min_width;
81
			}
82
			else {
83
				hints->max_width = hints->min_width = 1;
84
			}
85
		}
86
87
		if(hints->max_height < hints->min_height) {
88
			if(hints->max_height > 0) {
89
				hints->min_height = hints->max_height;
90
			}
91
			else if(hints->min_height > 0) {
92
				hints->max_height = hints->min_height;
93
			}
94
			else {
95
				hints->max_height = hints->min_height = 1;
96
			}
97
		}
98
	}
99
}
523.1.2 by Matthew Fuller
Move FetchWmProtocols() over to win_utils as well.
100
101
102
/*
103
 * Fill in info from WM_PROTOCOLS property
104
 *
105
 * Formerly in add_window.c
106
 */
107
void
108
FetchWmProtocols(TwmWindow *tmp)
109
{
110
	unsigned long flags = 0L;
111
	Atom *protocols = NULL;
112
	int n;
113
114
	if(XGetWMProtocols(dpy, tmp->w, &protocols, &n)) {
115
		int i;
116
		Atom *ap;
117
118
		for(i = 0, ap = protocols; i < n; i++, ap++) {
119
			if(*ap == XA_WM_TAKE_FOCUS) {
120
				flags |= DoesWmTakeFocus;
121
			}
122
			if(*ap == XA_WM_SAVE_YOURSELF) {
123
				flags |= DoesWmSaveYourself;
124
			}
125
			if(*ap == XA_WM_DELETE_WINDOW) {
126
				flags |= DoesWmDeleteWindow;
127
			}
128
		}
129
		if(protocols) {
130
			XFree(protocols);
131
		}
132
	}
133
	tmp->protocols = flags;
134
}
523.1.3 by Matthew Fuller
Move GetGravityOffsets() into win_utils and give it a more descriptive
135
136
137
/*
138
 * Figure signs for calculating location offsets for a window dependent
139
 * on its gravity.
140
 *
141
 * Depending on how its gravity is set, offsets to window coordinates for
142
 * e.g. border widths may need to move either down (right) or up (left).
143
 * Or possibly not at all.  So we write multipliers into passed vars for
144
 * callers.
145
 *
146
 * Formerly in add_window.c
147
 */
148
void
149
GetGravityOffsets(TwmWindow *tmp, int *xp, int *yp)
150
{
151
	static struct _gravity_offset {
152
		int x, y;
523.1.4 by Matthew Fuller
Use C99 designated initializer instead of hopefully-correct comments.
153
	} gravity_offsets[] = {
154
		[ForgetGravity]    = {  0,  0 },
155
		[NorthWestGravity] = { -1, -1 },
156
		[NorthGravity]     = {  0, -1 },
157
		[NorthEastGravity] = {  1, -1 },
158
		[WestGravity]      = { -1,  0 },
159
		[CenterGravity]    = {  0,  0 },
160
		[EastGravity]      = {  1,  0 },
161
		[SouthWestGravity] = { -1,  1 },
162
		[SouthGravity]     = {  0,  1 },
163
		[SouthEastGravity] = {  1,  1 },
164
		[StaticGravity]    = {  0,  0 },
523.1.3 by Matthew Fuller
Move GetGravityOffsets() into win_utils and give it a more descriptive
165
	};
166
	int g = ((tmp->hints.flags & PWinGravity)
167
	         ? tmp->hints.win_gravity : NorthWestGravity);
168
169
	if(g < ForgetGravity || g > StaticGravity) {
170
		*xp = *yp = 0;
171
	}
172
	else {
173
		*xp = gravity_offsets[g].x;
174
		*yp = gravity_offsets[g].y;
175
	}
176
}
523.1.5 by Matthew Fuller
GetTwmWindow() is pretty obvious _utils fodder.
177
178
179
/*
180
 * Finds the TwmWindow structure associated with a Window (if any), or
181
 * NULL.
182
 *
183
 * This is a relatively cheap function since it does not involve
184
 * communication with the server. Probably faster than walking the list
185
 * of TwmWindows, since the lookup is by a hash table.
523.1.9 by Matthew Fuller
Mark up GetTwmWindow() provenance.
186
 *
187
 * Formerly in add_window.c
523.1.5 by Matthew Fuller
GetTwmWindow() is pretty obvious _utils fodder.
188
 */
189
TwmWindow *
190
GetTwmWindow(Window w)
191
{
192
	TwmWindow *twmwin;
193
	int stat;
194
195
	stat = XFindContext(dpy, w, TwmContext, (XPointer *)&twmwin);
196
	if(stat == XCNOENT) {
197
		twmwin = NULL;
198
	}
199
200
	return twmwin;
201
}
523.1.10 by Matthew Fuller
Move {Get,Free}WMPropertyString into win_utils.
202
203
204
/***********************************************************************
205
 *
206
 *  Procedure:
207
 *      GetWMPropertyString - Get Window Manager text property and
208
 *                              convert it to a string.
209
 *
210
 *  Returned Value:
211
 *      (char *) - pointer to the malloc'd string or NULL
212
 *
213
 *  Inputs:
214
 *      w       - the id of the window whose property is to be retrieved
215
 *      prop    - property atom (typically WM_NAME or WM_ICON_NAME)
216
 *
217
 ***********************************************************************
218
 *
219
 * Formerly in util.c
220
 */
544.1.5 by Matthew Fuller
Switch GetWMPropertyString() over to returning char * rather than
221
char *
523.1.10 by Matthew Fuller
Move {Get,Free}WMPropertyString into win_utils.
222
GetWMPropertyString(Window w, Atom prop)
223
{
224
	XTextProperty       text_prop;
544.1.5 by Matthew Fuller
Switch GetWMPropertyString() over to returning char * rather than
225
	char                *stringptr;
523.1.10 by Matthew Fuller
Move {Get,Free}WMPropertyString into win_utils.
226
544.1.2 by Matthew Fuller
GCC some remaining (void) return casts.
227
	XGetTextProperty(dpy, w, &text_prop, prop);
615.1.10 by Matthew Fuller
Invert this test; if there's nothing, all we're gonna do is return the
228
	if(text_prop.value == NULL) {
229
		return NULL;
230
	}
231
615.1.11 by Matthew Fuller
Reindent after sense swap.
232
	if(text_prop.encoding == XA_STRING
615.1.12 by Matthew Fuller
Enable accepting UTF8_STRING for the WM_NAME (and related) properties.
233
	                || text_prop.encoding == XA_UTF8_STRING
615.1.11 by Matthew Fuller
Reindent after sense swap.
234
	                || text_prop.encoding == XA_COMPOUND_TEXT) {
235
		/* property is encoded as compound text - convert to locale string */
615.1.17 by Matthew Fuller
Move vars to the inner scope where they're used.
236
		char **text_list;
237
		int  text_list_count;
615.1.19 by Matthew Fuller
Split up declaring and using this var for coming changes.
238
		int status;
239
615.1.22 by Matthew Fuller
Add StrictWinNameEncoding support code.
240
		/* Check historical strictness */
241
		if(Scr->StrictWinNameEncoding) {
242
			bool fail = false;
243
615.1.53 by Matthew Fuller
Add stricture checks for the _ICON_NAME properties too.
244
			if((prop == XA_WM_NAME || prop == XA_WM_ICON_NAME)
615.1.22 by Matthew Fuller
Add StrictWinNameEncoding support code.
245
			                && text_prop.encoding != XA_STRING
246
			                && text_prop.encoding != XA_COMPOUND_TEXT) {
247
				fail = true;
248
			}
249
250
#ifdef EWMH
615.1.53 by Matthew Fuller
Add stricture checks for the _ICON_NAME properties too.
251
			if((prop == XA__NET_WM_NAME || prop == XA__NET_WM_ICON_NAME)
615.1.22 by Matthew Fuller
Add StrictWinNameEncoding support code.
252
			                && text_prop.encoding != XA_UTF8_STRING) {
253
				fail = true;
254
			}
255
#endif // EWMH
256
257
			if(fail) {
258
				fprintf(stderr, "%s: Invalid encoding for property %s "
259
				        "of window 0x%lx\n", ProgramName,
260
				        XGetAtomName(dpy, prop), w);
261
				XFree(text_prop.value);
262
				return NULL;
263
			}
264
		}
265
615.1.19 by Matthew Fuller
Split up declaring and using this var for coming changes.
266
267
		status = XmbTextPropertyToTextList(dpy, &text_prop, &text_list,
268
		                                   &text_list_count);
615.1.16 by Matthew Fuller
Compress down these error cases into an early return.
269
		if(text_list_count == 0
270
		                || text_list == NULL
271
		                || text_list[0] == NULL) {
615.1.18 by Matthew Fuller
Comment these branches.
272
			// Got nothing
615.1.16 by Matthew Fuller
Compress down these error cases into an early return.
273
			XFree(text_prop.value);
274
			return NULL;
615.1.11 by Matthew Fuller
Reindent after sense swap.
275
		}
276
		else if(status < 0 || text_list_count < 0) {
615.1.18 by Matthew Fuller
Comment these branches.
277
			// Got an error statuf
615.1.11 by Matthew Fuller
Reindent after sense swap.
278
			switch(status) {
279
				case XConverterNotFound:
280
					fprintf(stderr,
281
					        "%s: Converter not found; unable to convert property %s of window ID %lx.\n",
282
					        ProgramName, XGetAtomName(dpy, prop), w);
283
					break;
284
				case XNoMemory:
285
					fprintf(stderr,
286
					        "%s: Insufficient memory; unable to convert property %s of window ID %lx.\n",
287
					        ProgramName, XGetAtomName(dpy, prop), w);
288
					break;
289
				case XLocaleNotSupported:
290
					fprintf(stderr,
291
					        "%s: Locale not supported; unable to convert property %s of window ID %lx.\n",
292
					        ProgramName, XGetAtomName(dpy, prop), w);
293
					break;
294
			}
295
			stringptr = NULL;
296
			/*
297
			   don't call XFreeStringList - text_list appears to have
298
			   invalid address if status is bad
299
			   XFreeStringList(text_list);
300
			*/
523.1.10 by Matthew Fuller
Move {Get,Free}WMPropertyString into win_utils.
301
		}
302
		else {
615.1.18 by Matthew Fuller
Comment these branches.
303
			// Actually got the data!
615.1.11 by Matthew Fuller
Reindent after sense swap.
304
			stringptr = strdup(text_list[0]);
305
			XFreeStringList(text_list);
523.1.10 by Matthew Fuller
Move {Get,Free}WMPropertyString into win_utils.
306
		}
615.1.11 by Matthew Fuller
Reindent after sense swap.
307
	}
308
	else {
309
		/* property is encoded in a format we don't understand */
310
		fprintf(stderr,
311
		        "%s: Encoding not STRING or COMPOUND_TEXT; unable to decode property %s of window ID %lx.\n",
312
		        ProgramName, XGetAtomName(dpy, prop), w);
313
		stringptr = NULL;
314
	}
315
	XFree(text_prop.value);
523.1.10 by Matthew Fuller
Move {Get,Free}WMPropertyString into win_utils.
316
317
	return stringptr;
318
}
319
320
321
/*
322
 * Cleanup something stored that we got from the above originally.
323
 *
324
 * Formerly in util.c
325
 */
326
void
327
FreeWMPropertyString(char *prop)
328
{
544.1.3 by Matthew Fuller
Don't need to cast this to the type it already is.
329
	if(prop && prop != NoName) {
523.1.10 by Matthew Fuller
Move {Get,Free}WMPropertyString into win_utils.
330
		free(prop);
331
	}
332
}
523.1.16 by Matthew Fuller
Move visible() into win_utils.
333
334
335
/*
336
 * Window mapped on some virtual screen?
337
 *
338
 * Formerly in util.c
339
 */
340
bool
341
visible(const TwmWindow *tmp_win)
342
{
343
	return (tmp_win->vs != NULL);
344
}
523.1.18 by Matthew Fuller
Move the masking funcs into win_util and GC util.h from the files only
345
346
347
/*
348
 * Various code paths do a dance of "mask off notifications of event type
349
 * ; do something that triggers that event (but we're doing it, so we
350
 * don't need the notification) ; restore previous mask".  So have some
351
 * util funcs to make it more visually obvious.
352
 *
353
 * e.g.:
354
 *     long prev_mask = mask_out_event(w, PropertyChangeMask);
355
 *     do_something_that_changes_properties();
356
 *     restore_mask(prev_mask);
357
 *
358
 * We're cheating a little with the -1 return on mask_out_event(), as
359
 * that's theoretically valid for the data type.  It's not as far as I
360
 * can tell for X or us though; having all the bits set (well, I guess
361
 * I'm assuming 2s-complement too) is pretty absurd, and there are only
362
 * 25 defined bits in Xlib, so even on 32-bit systems, it shouldn't fill
363
 * up long.
364
 */
365
long
366
mask_out_event(Window w, long ignore_event)
367
{
368
	XWindowAttributes wattr;
369
370
	/* Get current mask */
371
	if(XGetWindowAttributes(dpy, w, &wattr) == 0) {
372
		return -1;
373
	}
374
375
	/*
376
	 * If we're ignoring nothing, nothing to do.  This is probably not
377
	 * strictly speaking a useful thing to ask for in general, but it's
378
	 * the right thing for us to do if we're asked to do nothing.
379
	 */
380
	if(ignore_event == 0) {
381
		return wattr.your_event_mask;
382
	}
383
384
	/* Delegate */
385
	return mask_out_event_mask(w, ignore_event, wattr.your_event_mask);
386
}
387
388
long
389
mask_out_event_mask(Window w, long ignore_event, long curmask)
390
{
391
	/* Set to the current, minus what we're wanting to ignore */
392
	XSelectInput(dpy, w, (curmask & ~ignore_event));
393
394
	/* Return what it was */
395
	return curmask;
396
}
397
398
int
399
restore_mask(Window w, long restore)
400
{
401
	return XSelectInput(dpy, w, restore);
402
}
523.1.20 by Matthew Fuller
Getting and setting WM_STATE props is pretty obviously a window util,
403
404
405
/*
406
 * Setting and getting WM_STATE property.
407
 *
408
 * x-ref ICCCM section 4.1.3.1
409
 * https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1
523.1.21 by Matthew Fuller
Leave comment asking for renaming.
410
 *
411
 * XXX These should probably be named more alike, as they're
412
 * complementary ops.
523.1.20 by Matthew Fuller
Getting and setting WM_STATE props is pretty obviously a window util,
413
 */
414
void
415
SetMapStateProp(TwmWindow *tmp_win, int state)
416
{
417
	unsigned long data[2];              /* "suggested" by ICCCM version 1 */
418
419
	data[0] = (unsigned long) state;
420
	data[1] = (unsigned long)(tmp_win->iconify_by_unmapping ? None :
421
	                          (tmp_win->icon ? tmp_win->icon->w : None));
422
423
	XChangeProperty(dpy, tmp_win->w, XA_WM_STATE, XA_WM_STATE, 32,
424
	                PropModeReplace, (unsigned char *) data, 2);
425
}
426
427
428
bool
429
GetWMState(Window w, int *statep, Window *iwp)
430
{
431
	Atom actual_type;
432
	int actual_format;
433
	unsigned long nitems, bytesafter;
434
	unsigned long *datap = NULL;
435
	bool retval = false;
436
437
	if(XGetWindowProperty(dpy, w, XA_WM_STATE, 0L, 2L, False, XA_WM_STATE,
438
	                      &actual_type, &actual_format, &nitems, &bytesafter,
439
	                      (unsigned char **) &datap) != Success || !datap) {
440
		return false;
441
	}
442
443
	if(nitems <= 2) {                   /* "suggested" by ICCCM version 1 */
444
		*statep = (int) datap[0];
445
		*iwp = (Window) datap[1];
446
		retval = true;
447
	}
448
449
	XFree(datap);
450
	return retval;
451
}
523.1.23 by Matthew Fuller
Move DisplayPosition() off into win_utils as well. Stick some
452
453
454
/*
455
 * Display a window's position in the dimensions window.  This is used
456
 * during various window positioning (during new window popups, moves,
457
 * etc).
458
 *
459
 * This reuses the same window for the position as is used during
460
 * resizing for the dimesions of the window in DisplaySize().  The
461
 * innards of the funcs can probably be collapsed together a little, and
462
 * the higher-level knowledge of Scr->SizeWindow (e.g., for unmapping
463
 * after ths op is done) should probably be encapsulated a bit better.
464
 */
465
void
466
DisplayPosition(const TwmWindow *_unused_tmp_win, int x, int y)
467
{
468
	char str [100];
469
	char signx = '+';
470
	char signy = '+';
471
472
	if(x < 0) {
473
		x = -x;
474
		signx = '-';
475
	}
476
	if(y < 0) {
477
		y = -y;
478
		signy = '-';
479
	}
480
	sprintf(str, " %c%-4d %c%-4d ", signx, x, signy, y);
481
	XRaiseWindow(dpy, Scr->SizeWindow);
482
483
	Draw3DBorder(Scr->SizeWindow, 0, 0,
484
	             Scr->SizeStringOffset + Scr->SizeStringWidth + SIZE_HINDENT,
485
	             Scr->SizeFont.height + SIZE_VINDENT * 2,
486
	             2, Scr->DefaultC, off, false, false);
487
488
	FB(Scr->DefaultC.fore, Scr->DefaultC.back);
489
	XmbDrawImageString(dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
490
	                   Scr->NormalGC, Scr->SizeStringOffset,
551 by Matthew Fuller
make indent
491
	                   Scr->SizeFont.ascent + SIZE_VINDENT, str, 13);
523.1.23 by Matthew Fuller
Move DisplayPosition() off into win_utils as well. Stick some
492
}
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
493
614.1.28 by Maxime Soulé
If CenterFeedbackWindow, feedback window centered in current monitor
494
void
495
MoveResizeSizeWindow(int x, int y, unsigned int width, unsigned int height)
496
{
497
	XResizeWindow(dpy, Scr->SizeWindow, width, height);
498
499
	if(Scr->CenterFeedbackWindow) {
500
		RArea monitor = RLayoutGetAreaAtXY(Scr->BorderedLayout, x, y);
501
502
		XMoveWindow(dpy, Scr->SizeWindow,
503
		            monitor.x + monitor.width / 2 - width / 2,
504
		            monitor.y + monitor.height / 2 - height / 2);
505
	}
506
}
507
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
508
509
/*
510
 * Various funcs for adjusting coordinates for windows based on
511
 * resistances etc.
512
 *
513
 * XXX In desperate need of better commenting.
514
 */
614.1.12 by Maxime Soulé
f.fill, f.pack & f.jump* functions detect inter-monitors edges
515
static void
614.1.93 by Matthew Fuller
const-ify the callback func for RAreaListForeach().
516
_tryToPack(RArea *final, const RArea *cur_win)
614.1.27 by Maxime Soulé
Monitor edge detection reworked: corrects some 1-pixel-wide-problems
517
{
518
	if(final->x >= cur_win->x + cur_win->width) {
519
		return;
520
	}
521
	if(final->y >= cur_win->y + cur_win->height) {
522
		return;
523
	}
524
	if(final->x + final->width <= cur_win->x) {
525
		return;
526
	}
527
	if(final->y + final->height <= cur_win->y) {
528
		return;
529
	}
530
531
	if(final->x + Scr->MovePackResistance > cur_win->x +
532
	                cur_win->width) {  /* left */
533
		final->x = MAX(final->x, cur_win->x + cur_win->width);
534
		return;
535
	}
536
	if(final->x + final->width < cur_win->x +
537
	                Scr->MovePackResistance) {  /* right */
538
		final->x = MIN(final->x, cur_win->x - final->width);
539
		return;
540
	}
541
	if(final->y + Scr->MovePackResistance > cur_win->y +
542
	                cur_win->height) {  /* top */
543
		final->y = MAX(final->y, cur_win->y + cur_win->height);
544
		return;
545
	}
546
	if(final->y + final->height < cur_win->y +
547
	                Scr->MovePackResistance) {  /* bottom */
548
		final->y = MIN(final->y, cur_win->y - final->height);
549
	}
550
}
551
614.1.63 by Matthew Fuller
The RAreaListForeach() callback's return value is used as a boolean,
552
static bool
614.1.93 by Matthew Fuller
const-ify the callback func for RAreaListForeach().
553
_tryToPackVsEachMonitor(const RArea *monitor_area, void *vfinal)
614.1.27 by Maxime Soulé
Monitor edge detection reworked: corrects some 1-pixel-wide-problems
554
{
555
	_tryToPack((RArea *)vfinal, monitor_area);
614.1.63 by Matthew Fuller
The RAreaListForeach() callback's return value is used as a boolean,
556
	return false;
614.1.12 by Maxime Soulé
f.fill, f.pack & f.jump* functions detect inter-monitors edges
557
}
558
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
559
void
560
TryToPack(TwmWindow *tmp_win, int *x, int *y)
561
{
562
	TwmWindow   *t;
614.1.12 by Maxime Soulé
f.fill, f.pack & f.jump* functions detect inter-monitors edges
563
	RArea cur_win;
614.1.27 by Maxime Soulé
Monitor edge detection reworked: corrects some 1-pixel-wide-problems
564
	RArea final = RAreaNew(*x, *y,
565
	                       tmp_win->frame_width  + 2 * tmp_win->frame_bw,
566
	                       tmp_win->frame_height + 2 * tmp_win->frame_bw);
614.1.12 by Maxime Soulé
f.fill, f.pack & f.jump* functions detect inter-monitors edges
567
568
	/* Global layout is not a single rectangle, check against the
569
	 * monitor borders */
570
	if(Scr->BorderedLayout->horiz->len > 1) {
614.1.27 by Maxime Soulé
Monitor edge detection reworked: corrects some 1-pixel-wide-problems
571
		RAreaListForeach(
572
		        Scr->BorderedLayout->monitors, _tryToPackVsEachMonitor, &final);
614.1.12 by Maxime Soulé
f.fill, f.pack & f.jump* functions detect inter-monitors edges
573
	}
574
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
575
	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
576
		if(t == tmp_win) {
577
			continue;
578
		}
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
579
#ifdef WINBOX
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
580
		if(t->winbox != tmp_win->winbox) {
581
			continue;
582
		}
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
583
#endif
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
584
		if(t->vs != tmp_win->vs) {
585
			continue;
586
		}
587
		if(!t->mapped) {
588
			continue;
589
		}
590
614.1.25 by Maxime Soulé
RArea struct is never malloc()ed anymore
591
		cur_win = RAreaNew(t->frame_x, t->frame_y,
592
		                   t->frame_width  + 2 * t->frame_bw,
593
		                   t->frame_height + 2 * t->frame_bw);
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
594
614.1.27 by Maxime Soulé
Monitor edge detection reworked: corrects some 1-pixel-wide-problems
595
		_tryToPack(&final, &cur_win);
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
596
	}
614.1.27 by Maxime Soulé
Monitor edge detection reworked: corrects some 1-pixel-wide-problems
597
598
	*x = final.x;
599
	*y = final.y;
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
600
}
601
602
603
/*
604
 * Directionals for TryToPush_be().  These differ from the specs for
605
 * jump/pack/fill in functions. because there's an indeterminate option.
606
 */
607
typedef enum {
608
	PD_ANY,
609
	PD_BOTTOM,
610
	PD_LEFT,
611
	PD_RIGHT,
612
	PD_TOP,
613
} PushDirection;
614
static void TryToPush_be(TwmWindow *tmp_win, int x, int y, PushDirection dir);
615
616
void
617
TryToPush(TwmWindow *tmp_win, int x, int y)
618
{
619
	TryToPush_be(tmp_win, x, y, PD_ANY);
620
}
621
622
static void
623
TryToPush_be(TwmWindow *tmp_win, int x, int y, PushDirection dir)
624
{
625
	TwmWindow   *t;
626
	int         newx, newy, ndir;
627
	bool        move;
628
	int         w, h;
629
	int         winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
630
	int         winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
631
632
	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
633
		if(t == tmp_win) {
634
			continue;
635
		}
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
636
#ifdef WINBOX
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
637
		if(t->winbox != tmp_win->winbox) {
638
			continue;
639
		}
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
640
#endif
523.1.24 by Matthew Fuller
Move these Pack/Push/Grid funcs into win_utils, since they're about
641
		if(t->vs != tmp_win->vs) {
642
			continue;
643
		}
644
		if(!t->mapped) {
645
			continue;
646
		}
647
648
		w = t->frame_width  + 2 * t->frame_bw;
649
		h = t->frame_height + 2 * t->frame_bw;
650
		if(x >= t->frame_x + w) {
651
			continue;
652
		}
653
		if(y >= t->frame_y + h) {
654
			continue;
655
		}
656
		if(x + winw <= t->frame_x) {
657
			continue;
658
		}
659
		if(y + winh <= t->frame_y) {
660
			continue;
661
		}
662
663
		move = false;
664
		if((dir == PD_ANY || dir == PD_LEFT) &&
665
		                (x + Scr->MovePackResistance > t->frame_x + w)) {
666
			newx = x - w;
667
			newy = t->frame_y;
668
			ndir = PD_LEFT;
669
			move = true;
670
		}
671
		else if((dir == PD_ANY || dir == PD_RIGHT) &&
672
		                (x + winw < t->frame_x + Scr->MovePackResistance)) {
673
			newx = x + winw;
674
			newy = t->frame_y;
675
			ndir = PD_RIGHT;
676
			move = true;
677
		}
678
		else if((dir == PD_ANY || dir == PD_TOP) &&
679
		                (y + Scr->MovePackResistance > t->frame_y + h)) {
680
			newx = t->frame_x;
681
			newy = y - h;
682
			ndir = PD_TOP;
683
			move = true;
684
		}
685
		else if((dir == PD_ANY || dir == PD_BOTTOM) &&
686
		                (y + winh < t->frame_y + Scr->MovePackResistance)) {
687
			newx = t->frame_x;
688
			newy = y + winh;
689
			ndir = PD_BOTTOM;
690
			move = true;
691
		}
692
		if(move) {
693
			TryToPush_be(t, newx, newy, ndir);
694
			TryToPack(t, &newx, &newy);
695
			ConstrainByBorders(tmp_win,
696
			                   &newx, t->frame_width  + 2 * t->frame_bw,
697
			                   &newy, t->frame_height + 2 * t->frame_bw);
698
			SetupWindow(t, newx, newy, t->frame_width, t->frame_height, -1);
699
		}
700
	}
701
}
702
703
704
void
705
TryToGrid(TwmWindow *tmp_win, int *x, int *y)
706
{
707
	int w    = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
708
	int h    = tmp_win->frame_height + 2 * tmp_win->frame_bw;
709
	int grav = ((tmp_win->hints.flags & PWinGravity)
710
	            ? tmp_win->hints.win_gravity : NorthWestGravity);
711
712
	switch(grav) {
713
		case ForgetGravity :
714
		case StaticGravity :
715
		case NorthWestGravity :
716
		case NorthGravity :
717
		case WestGravity :
718
		case CenterGravity :
719
			*x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
720
			     + Scr->BorderLeft;
721
			*y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid
722
			     + Scr->BorderTop;
723
			break;
724
		case NorthEastGravity :
725
		case EastGravity :
726
			*x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
727
			      Scr->XMoveGrid) - w + Scr->BorderLeft;
728
			*y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) *
729
			     Scr->YMoveGrid + Scr->BorderTop;
730
			break;
731
		case SouthWestGravity :
732
		case SouthGravity :
733
			*x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
734
			     + Scr->BorderLeft;
735
			*y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid)
736
			     - h + Scr->BorderTop;
737
			break;
738
		case SouthEastGravity :
739
			*x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
740
			      Scr->XMoveGrid) - w + Scr->BorderLeft;
741
			*y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) *
742
			      Scr->YMoveGrid) - h + Scr->BorderTop;
743
			break;
744
	}
745
}
523.1.26 by Matthew Fuller
Move these Constrain funcs to keeping windows on-screen to win_utils.
746
747
748
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
749
#ifdef WINBOX
523.1.26 by Matthew Fuller
Move these Constrain funcs to keeping windows on-screen to win_utils.
750
/*
751
 * Functions related to keeping windows from being placed off-screen (or
752
 * off-screen too far).  Involved in handling of params like DontMoveOff
753
 * and MoveOffResistance, etc.
754
 */
755
static void ConstrainLeftTop(int *value, int border);
756
static void ConstrainRightBottom(int *value, int size1, int border, int size2);
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
757
#endif
523.1.26 by Matthew Fuller
Move these Constrain funcs to keeping windows on-screen to win_utils.
758
614.1.2 by Maxime Soulé
Menus are now clipped using layout
759
bool
760
ConstrainByLayout(RLayout *layout, int move_off_res, int *left, int width,
761
                  int *top, int height)
523.1.26 by Matthew Fuller
Move these Constrain funcs to keeping windows on-screen to win_utils.
762
{
614.1.27 by Maxime Soulé
Monitor edge detection reworked: corrects some 1-pixel-wide-problems
763
	RArea area = RAreaNew(*left, *top, width, height);
614.1.3 by Maxime Soulé
Be more C standard conservative
764
	int limit;
614.1.2 by Maxime Soulé
Menus are now clipped using layout
765
	bool clipped = false;
614.1.1 by Maxime Soulé
First step of xrandr integration
766
614.1.3 by Maxime Soulé
Be more C standard conservative
767
	limit = RLayoutFindBottomEdge(layout, &area) - height + 1;
614.1.1 by Maxime Soulé
First step of xrandr integration
768
	if(area.y > limit) {
614.1.2 by Maxime Soulé
Menus are now clipped using layout
769
		if(move_off_res >= 0 && area.y >= limit + move_off_res) {
770
			area.y -= move_off_res;
614.1.1 by Maxime Soulé
First step of xrandr integration
771
		}
772
		else {
773
			area.y = limit;
614.1.2 by Maxime Soulé
Menus are now clipped using layout
774
			clipped = true;
614.1.1 by Maxime Soulé
First step of xrandr integration
775
		}
776
	}
777
614.1.2 by Maxime Soulé
Menus are now clipped using layout
778
	limit = RLayoutFindRightEdge(layout, &area) - width + 1;
614.1.1 by Maxime Soulé
First step of xrandr integration
779
	if(area.x > limit) {
614.1.2 by Maxime Soulé
Menus are now clipped using layout
780
		if(move_off_res >= 0 && area.x >= limit + move_off_res) {
781
			area.x -= move_off_res;
614.1.1 by Maxime Soulé
First step of xrandr integration
782
		}
783
		else {
784
			area.x = limit;
614.1.2 by Maxime Soulé
Menus are now clipped using layout
785
			clipped = true;
614.1.1 by Maxime Soulé
First step of xrandr integration
786
		}
787
	}
788
614.1.2 by Maxime Soulé
Menus are now clipped using layout
789
	limit = RLayoutFindLeftEdge(layout, &area);
614.1.1 by Maxime Soulé
First step of xrandr integration
790
	if(area.x < limit) {
614.1.2 by Maxime Soulé
Menus are now clipped using layout
791
		if(move_off_res >= 0 && area.x <= limit - move_off_res) {
792
			area.x += move_off_res;
614.1.1 by Maxime Soulé
First step of xrandr integration
793
		}
794
		else {
795
			area.x = limit;
614.1.2 by Maxime Soulé
Menus are now clipped using layout
796
			clipped = true;
614.1.1 by Maxime Soulé
First step of xrandr integration
797
		}
798
	}
799
614.1.2 by Maxime Soulé
Menus are now clipped using layout
800
	limit = RLayoutFindTopEdge(layout, &area);
614.1.1 by Maxime Soulé
First step of xrandr integration
801
	if(area.y < limit) {
614.1.2 by Maxime Soulé
Menus are now clipped using layout
802
		if(move_off_res >= 0 && area.y <= limit - move_off_res) {
803
			area.y += move_off_res;
614.1.1 by Maxime Soulé
First step of xrandr integration
804
		}
805
		else {
806
			area.y = limit;
614.1.2 by Maxime Soulé
Menus are now clipped using layout
807
			clipped = true;
614.1.1 by Maxime Soulé
First step of xrandr integration
808
		}
809
	}
810
811
	*left = area.x;
812
	*top = area.y;
614.1.2 by Maxime Soulé
Menus are now clipped using layout
813
814
	return clipped;
815
}
816
817
void
818
ConstrainByBorders1(int *left, int width, int *top, int height)
819
{
820
	ConstrainByLayout(Scr->BorderedLayout, Scr->MoveOffResistance,
821
	                  left, width, top, height);
523.1.26 by Matthew Fuller
Move these Constrain funcs to keeping windows on-screen to win_utils.
822
}
823
824
void
825
ConstrainByBorders(TwmWindow *twmwin, int *left, int width,
826
                   int *top, int height)
827
{
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
828
	if(false) {
829
		// Dummy
830
	}
831
#ifdef WINBOX
832
	else if(twmwin->winbox) {
523.1.26 by Matthew Fuller
Move these Constrain funcs to keeping windows on-screen to win_utils.
833
		XWindowAttributes attr;
834
		XGetWindowAttributes(dpy, twmwin->winbox->window, &attr);
835
		ConstrainRightBottom(left, width, 0, attr.width);
836
		ConstrainLeftTop(left, 0);
837
		ConstrainRightBottom(top, height, 0, attr.height);
838
		ConstrainLeftTop(top, 0);
839
	}
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
840
#endif
523.1.26 by Matthew Fuller
Move these Constrain funcs to keeping windows on-screen to win_utils.
841
	else {
842
		ConstrainByBorders1(left, width, top, height);
843
	}
844
}
845
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
846
#ifdef WINBOX
523.1.26 by Matthew Fuller
Move these Constrain funcs to keeping windows on-screen to win_utils.
847
static void
848
ConstrainLeftTop(int *value, int border)
849
{
850
	if(*value < border) {
851
		if(Scr->MoveOffResistance < 0 ||
852
		                *value > border - Scr->MoveOffResistance) {
853
			*value = border;
854
		}
855
		else if(Scr->MoveOffResistance > 0 &&
856
		                *value <= border - Scr->MoveOffResistance) {
857
			*value = *value + Scr->MoveOffResistance;
858
		}
859
	}
860
}
861
862
static void
863
ConstrainRightBottom(int *value, int size1, int border, int size2)
864
{
865
	if(*value + size1 > size2 - border) {
866
		if(Scr->MoveOffResistance < 0 ||
867
		                *value + size1 < size2 - border + Scr->MoveOffResistance) {
868
			*value = size2 - size1 - border;
869
		}
870
		else if(Scr->MoveOffResistance > 0 &&
871
		                *value + size1 >= size2 - border + Scr->MoveOffResistance) {
872
			*value = *value - Scr->MoveOffResistance;
873
		}
874
	}
875
}
700.1.11 by Matthew Fuller
Hide these TwmWindow winbox-related fields behind an ifdef along with
876
#endif
539.1.18 by Matthew Fuller
WarpToWindow() also has no relation to menus and is broadly used
877
878
879
/*
880
 * Zoom over to a particular window.
881
 */
882
void
883
WarpToWindow(TwmWindow *t, bool must_raise)
884
{
885
	int x, y;
886
887
	if(t->ring.cursor_valid) {
888
		x = t->ring.curs_x;
889
		y = t->ring.curs_y;
890
#ifdef DEBUG
891
		fprintf(stderr, "WarpToWindow: cursor_valid; x == %d, y == %d\n", x, y);
892
#endif
893
894
		/*
895
		 * XXX is this correct with 3D borders? Easier check possible?
896
		 * frame_bw is for the left border.
897
		 */
898
		if(x < t->frame_bw) {
899
			x = t->frame_bw;
900
		}
901
		if(x >= t->frame_width + t->frame_bw) {
902
			x  = t->frame_width + t->frame_bw - 1;
903
		}
904
		if(y < t->title_height + t->frame_bw) {
905
			y = t->title_height + t->frame_bw;
906
		}
907
		if(y >= t->frame_height + t->frame_bw) {
908
			y  = t->frame_height + t->frame_bw - 1;
909
		}
910
#ifdef DEBUG
911
		fprintf(stderr, "WarpToWindow: adjusted    ; x := %d, y := %d\n", x, y);
912
#endif
913
	}
914
	else {
915
		x = t->frame_width / 2;
916
		y = t->frame_height / 2;
917
#ifdef DEBUG
918
		fprintf(stderr, "WarpToWindow: middle; x := %d, y := %d\n", x, y);
919
#endif
920
	}
921
#if 0
922
	int dest_x, dest_y;
923
	Window child;
924
925
	/*
926
	 * Check if the proposed position actually is visible. If not, raise the window.
927
	 * "If the coordinates are contained in a mapped
928
	 * child of dest_w, that child is returned to child_return."
929
	 * We'll need to check for the right child window; the frame probably.
930
	 * (What about XXX window boxes?)
931
	 *
932
	 * Alternatively, use XQueryPointer() which returns the root window
933
	 * the pointer is in, but XXX that won't work for VirtualScreens.
934
	 */
935
	if(XTranslateCoordinates(dpy, t->frame, Scr->Root, x, y, &dest_x, &dest_y,
936
	                         &child)) {
937
		if(child != t->frame) {
938
			must_raise = true;
939
		}
940
	}
941
#endif
942
	if(t->auto_raise || must_raise) {
943
		AutoRaiseWindow(t);
944
	}
945
	if(! visible(t)) {
946
		WorkSpace *wlist;
947
948
		for(wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL;
949
		                wlist = wlist->next) {
950
			if(OCCUPY(t, wlist)) {
951
				break;
952
			}
953
		}
954
		if(wlist != NULL) {
955
			GotoWorkSpace(Scr->currentvs, wlist);
956
		}
957
	}
958
959
	XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0, x + t->frame_x, y + t->frame_y);
960
	SetFocus(t, EventTime);
961
962
#ifdef DEBUG
963
	{
964
		Window root_return;
965
		Window child_return;
966
		int root_x_return;
967
		int root_y_return;
968
		int win_x_return;
969
		int win_y_return;
970
		unsigned int mask_return;
971
972
		if(XQueryPointer(dpy, t->frame, &root_return, &child_return, &root_x_return,
973
		                 &root_y_return, &win_x_return, &win_y_return, &mask_return)) {
974
			fprintf(stderr,
975
			        "XQueryPointer: root_return=%x, child_return=%x, root_x_return=%d, root_y_return=%d, win_x_return=%d, win_y_return=%d\n",
976
			        root_return, child_return, root_x_return, root_y_return, win_x_return,
977
			        win_y_return);
978
		}
979
	}
980
#endif
981
}
539.1.22 by Matthew Fuller
send_clientmessage() probably belongs better in win_utils than util.
982
983
984
/*
985
 * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
986
 * client messages will have the following form:
987
 *
988
 *     event type       ClientMessage
989
 *     message type     XA_WM_PROTOCOLS
990
 *     window           tmp->w
991
 *     format           32
992
 *     data[0]          message atom
993
 *     data[1]          time stamp
994
 */
995
void
996
send_clientmessage(Window w, Atom a, Time timestamp)
997
{
998
	XClientMessageEvent ev;
999
1000
	ev.type = ClientMessage;
1001
	ev.window = w;
1002
	ev.message_type = XA_WM_PROTOCOLS;
1003
	ev.format = 32;
1004
	ev.data.l[0] = a;
1005
	ev.data.l[1] = timestamp;
1006
	XSendEvent(dpy, w, False, 0L, (XEvent *) &ev);
1007
}
557.1.4 by Matthew Fuller
Move creating the synthetic hints into a function so we can reuse it.
1008
1009
1010
/*
1011
 * Create synthetic WM_HINTS info for windows.  When a window specifies
1012
 * stuff, we should probably pay attention to it (though we don't
1013
 * always; x-ref comments in AddWindow() especially about focus).
1014
 * However, when it doesn't tell us anything at all, we should assume
1015
 * something useful.  "Window managers are free to assume convenient
1016
 * values for all fields of the WM_HINTS property if a window is mapped
1017
 * without one."  (ICCCM Ch. 4,
1018
 * <https://www.x.org/releases/X11R7.7/doc/xorg-docs/icccm/icccm.html#Client_Properties>).
1019
 *
557.1.9 by Matthew Fuller
Tweak comment.
1020
 * Specifically, we assume it wants us to give it focus.  It's fairly
1021
 * bogus for a window not to tell us anything, but e.g current versions
1022
 * of Chrome do (don't do) just that.  So we better make up something
1023
 * useful.
557.1.4 by Matthew Fuller
Move creating the synthetic hints into a function so we can reuse it.
1024
 *
557.1.9 by Matthew Fuller
Tweak comment.
1025
 * Should probably be some configurability for this, so make the func
1026
 * take the window, even though we don't currently do anything useful
1027
 * with it...
557.1.4 by Matthew Fuller
Move creating the synthetic hints into a function so we can reuse it.
1028
 */
1029
XWMHints *
1030
gen_synthetic_wmhints(TwmWindow *win)
1031
{
1032
	XWMHints *hints;
1033
1034
	hints = XAllocWMHints();
1035
	if(!hints) {
1036
		return NULL;
1037
	}
1038
1039
	/*
1040
	 * Reasonable defaults.  Takes input, in normal state.
1041
	 *
1042
	 * XXX Make configurable?
1043
	 */
1044
	hints->flags = InputHint | StateHint;
1045
	hints->input = True;
1046
	hints->initial_state = NormalState;
1047
1048
	return hints;
1049
}
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
1050
1051
615.1.54 by Matthew Fuller
As long as we're writing these funcs and their comments fresh, might
1052
/**
625.1.1 by Matthew Fuller
Extract the adjustment of wmhints stuff into a separate function for
1053
 * Perform whatever adaptations of WM_HINTS info we do.
1054
 *
1055
 * Most of these relate to focus, but we also fiddle with group
1056
 * membership.
1057
 */
1058
XWMHints *
1059
munge_wmhints(TwmWindow *win, XWMHints *hints)
1060
{
1061
	/*
1062
	 * If we have WM_HINTS, but they don't tell us anything about focus,
1063
	 * force it to true for our purposes.
1064
	 *
1065
	 * CL: Having with not willing focus cause problems with AutoSqueeze
1066
	 * and a few others things. So I suppress it. And the whole focus
1067
	 * thing is buggy anyway.
1068
	 */
625.1.2 by Matthew Fuller
Consistently work on the passed in hints, not what's in the window
1069
	if(!(hints->flags & InputHint)) {
1070
		hints->input = True;
625.1.1 by Matthew Fuller
Extract the adjustment of wmhints stuff into a separate function for
1071
	}
1072
1073
	/*
1074
	 * Now we're expecting to give the window focus if it asked for it
1075
	 * via WM_HINTS, if it didn't say anything one way or the other in
1076
	 * WM_HINTS, or if it didn't give us any WM_HINTS at all.  But if it
1077
	 * explicitly asked not to, we don't give it unless overridden by
1078
	 * config.
1079
	 */
1080
	if(Scr->ForceFocus || IsInList(Scr->ForceFocusL, win)) {
625.1.2 by Matthew Fuller
Consistently work on the passed in hints, not what's in the window
1081
		hints->input = True;
625.1.1 by Matthew Fuller
Extract the adjustment of wmhints stuff into a separate function for
1082
	}
1083
1084
1085
	/* Setup group bits */
625.1.2 by Matthew Fuller
Consistently work on the passed in hints, not what's in the window
1086
	if(hints->flags & WindowGroupHint) {
1087
		win->group = hints->window_group;
625.1.1 by Matthew Fuller
Extract the adjustment of wmhints stuff into a separate function for
1088
		if(win->group) {
1089
			/*
1090
			 * GTK windows often have a spurious "group leader" window which is
1091
			 * never reported to us and therefore does not really exist.  This
1092
			 * is annoying because we treat group members a lot like transient
1093
			 * windows.  Look for that here. It is in fact a duplicate of the
1094
			 * WM_CLIENT_LEADER property.
1095
			 */
1096
			if(win->group != win->w && !GetTwmWindow(win->group)) {
1097
				win->group = 0;
1098
			}
1099
		}
1100
	}
1101
	else {
1102
		win->group = 0;
1103
	}
1104
1105
	return hints;
1106
}
1107
1108
1109
/**
615.1.54 by Matthew Fuller
As long as we're writing these funcs and their comments fresh, might
1110
 * [Re]set a window's name.  This goes over the available naming sources
1111
 * for the window and points the TwmWindow::name at the appropriate one.
1112
 * It may also set a property to signal other EWMH-aware clients when
1113
 * we're naming it a way they can't see themselves.
1114
 *
1115
 * \note This should rarely be called directly; apply_window_name()
1116
 * should be used instead.  It's split out because we need to do this
1117
 * step individually in AddWindow().
1118
 *
1119
 * \note Note also that we never need to worry about freeing the
1120
 * TwmWindow::name; it always points to one of the TwmWindow::names
1121
 * values (which are free'd by the event handler when they change) or to
1122
 * NoName (which is static).  So we can just casually flip it around at
1123
 * will.
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
1124
 */
615.1.25 by Matthew Fuller
Split out setting ->name from doing the stuff that we do after
1125
bool
1126
set_window_name(TwmWindow *win)
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
1127
{
615.1.25 by Matthew Fuller
Split out setting ->name from doing the stuff that we do after
1128
	char *newname = NULL;
615.1.6 by Matthew Fuller
Build infrastructure to choose among multiple possible name sources,
1129
#define TRY(fld) { \
615.1.13 by Matthew Fuller
Add extra {}'s here to make it easier to slot in debug statements.
1130
                if(newname == NULL && win->names.fld != NULL) { \
1131
                        newname = win->names.fld; \
1132
                } \
615.1.6 by Matthew Fuller
Build infrastructure to choose among multiple possible name sources,
1133
        }
615.1.39 by Matthew Fuller
Add places for and handling of some properties for overriding
1134
	TRY(ctwm_wm_name)
615.1.8 by Matthew Fuller
Add net_wm_name into the fields we're trying. Nothing's setting it
1135
#ifdef EWMH
615.1.25 by Matthew Fuller
Split out setting ->name from doing the stuff that we do after
1136
	TRY(net_wm_name)
615.1.8 by Matthew Fuller
Add net_wm_name into the fields we're trying. Nothing's setting it
1137
#endif
615.1.25 by Matthew Fuller
Split out setting ->name from doing the stuff that we do after
1138
	TRY(wm_name)
615.1.6 by Matthew Fuller
Build infrastructure to choose among multiple possible name sources,
1139
#undef TRY
1140
615.1.25 by Matthew Fuller
Split out setting ->name from doing the stuff that we do after
1141
	if(newname == NULL) {
1142
		newname = NoName;
1143
	}
1144
	if(win->name == newname) {
1145
		return false; // Nothing to do
1146
	}
1147
615.1.52 by Matthew Fuller
Implement EWMH _NET_WM_VISIBLE{,_ICON}_NAME, telling external
1148
	// Now we know what to call it
615.1.25 by Matthew Fuller
Split out setting ->name from doing the stuff that we do after
1149
	win->name = newname;
615.1.52 by Matthew Fuller
Implement EWMH _NET_WM_VISIBLE{,_ICON}_NAME, telling external
1150
1151
#ifdef EWMH
1152
	// EWMH says we set an additional property on any windows where what
1153
	// we consider the name isn't what's in _NET_WM_NAME, so pagers etc
1154
	// can call it the same as we do.
1155
	//
1156
	// The parts of the text describing it conflict a little; at one
1157
	// place, it implies this should be set unless we're using
1158
	// _NET_WM_NAME, in another it seems to suggest WM_NAME should be
1159
	// considered applicable too.  I choose to implement it excluding
1160
	// both, so this only gets set if we're overriding either standard
1161
	// naming (probably rare).
1162
	if(win->name != win->names.net_wm_name && win->name != win->names.wm_name) {
1163
		// XXX We're not doing any checking of the encoding here...  I
1164
		// don't see that Xlib helps us any, so we probably have to fall
1165
		// back to iconv?  That came into the base in POSIX 2008, but was
1166
		// in XSI back into the 90's I believe?
1167
		XChangeProperty(dpy, win->w, XA__NET_WM_VISIBLE_NAME, XA_UTF8_STRING,
1168
		                8, PropModeReplace, (unsigned char *)win->name,
1169
		                strlen(win->name));
1170
	}
1171
	else {
1172
		XDeleteProperty(dpy, win->w, XA__NET_WM_VISIBLE_NAME);
1173
	}
1174
#endif // EWMH
1175
1176
	// We set a name
615.1.25 by Matthew Fuller
Split out setting ->name from doing the stuff that we do after
1177
	return true;
1178
}
1179
1180
615.1.54 by Matthew Fuller
As long as we're writing these funcs and their comments fresh, might
1181
/**
615.1.25 by Matthew Fuller
Split out setting ->name from doing the stuff that we do after
1182
 * [Re]set and apply changes to a window's name.  This is called after
1183
 * we've received a new WM_NAME (or other name-setting) property, to
1184
 * update our titlebars, icon managers, etc.
1185
 */
1186
void
1187
apply_window_name(TwmWindow *win)
1188
{
1189
	/* [Re]set ->name */
1190
	if(set_window_name(win) == false) {
1191
		// No change
1192
		return;
1193
	}
1194
	win->nameChanged = true;
1195
615.1.6 by Matthew Fuller
Build infrastructure to choose among multiple possible name sources,
1196
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
1197
	/* Update the active name */
1198
	{
1199
		XRectangle inc_rect;
1200
		XRectangle logical_rect;
1201
1202
		XmbTextExtents(Scr->TitleBarFont.font_set,
1203
		               win->name, strlen(win->name),
1204
		               &inc_rect, &logical_rect);
1205
		win->name_width = logical_rect.width;
1206
	}
1207
1208
	/* recompute the priority if necessary */
1209
	if(Scr->AutoPriority) {
1210
		OtpRecomputePrefs(win);
1211
	}
1212
1213
	SetupWindow(win, win->frame_x, win->frame_y,
1214
	            win->frame_width, win->frame_height, -1);
1215
1216
	if(win->title_w) {
1217
		XClearArea(dpy, win->title_w, 0, 0, 0, 0, True);
1218
	}
1219
	if(Scr->AutoOccupy) {
1220
		WmgrRedoOccupation(win);
1221
	}
1222
1223
#if 0
1224
	/* Experimental, not yet working. */
1225
	{
1226
		ColorPair cp;
1227
		int f, b;
1228
1229
		f = GetColorFromList(Scr->TitleForegroundL, win->name,
1230
		                     &win->class, &cp.fore);
1231
		b = GetColorFromList(Scr->TitleBackgroundL, win->name,
1232
		                     &win->class, &cp.back);
1233
		if(f || b) {
1234
			if(Scr->use3Dtitles  && !Scr->BeNiceToColormap) {
1235
				GetShadeColors(&cp);
1236
			}
1237
			win->title = cp;
1238
		}
1239
		f = GetColorFromList(Scr->BorderColorL, win->name,
1240
		                     &win->class, &cp.fore);
1241
		b = GetColorFromList(Scr->BorderColorL, win->name,
1242
		                     &win->class, &cp.back);
1243
		if(f || b) {
1244
			if(Scr->use3Dborders && !Scr->BeNiceToColormap) {
1245
				GetShadeColors(&cp);
1246
			}
1247
			win->borderC = cp;
1248
		}
1249
1250
		f = GetColorFromList(Scr->BorderTileForegroundL, win->name,
1251
		                     &win->class, &cp.fore);
1252
		b = GetColorFromList(Scr->BorderTileBackgroundL, win->name,
1253
		                     &win->class, &cp.back);
1254
		if(f || b) {
1255
			if(Scr->use3Dborders && !Scr->BeNiceToColormap) {
1256
				GetShadeColors(&cp);
1257
			}
1258
			win->border_tile = cp;
1259
		}
1260
	}
1261
#endif
1262
1263
	/*
615.1.30 by Matthew Fuller
Shift the icon_name setting bits off into functions like we did for
1264
	 * If we haven't set a separate icon name, we use the window name, so
1265
	 * we need to update it.
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
1266
	 */
615.1.30 by Matthew Fuller
Shift the icon_name setting bits off into functions like we did for
1267
	if(win->names.icon_set == false) {
1268
		apply_window_icon_name(win);
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
1269
	}
1270
	AutoPopupMaybe(win);
615.1.5 by Matthew Fuller
Explicit return.
1271
1272
	return;
615.1.4 by Matthew Fuller
Split out all the code to support "window name has changed, do stuff
1273
}
615.1.30 by Matthew Fuller
Shift the icon_name setting bits off into functions like we did for
1274
1275
615.1.54 by Matthew Fuller
As long as we're writing these funcs and their comments fresh, might
1276
/**
1277
 * [Re]set a window's icon name.  As with the window name version in
1278
 * set_window_name(), this is mostly separate so the AddWindow() process
1279
 * can call it.
1280
 *
1281
 * \note As with TwmWindow::name, we never want to try free()'ing or the
1282
 * like TwmWindow::icon_name.
1283
 *
1284
 * \sa set_window_name() for details; this is just the icon name
1285
 * equivalent of it.
615.1.30 by Matthew Fuller
Shift the icon_name setting bits off into functions like we did for
1286
 */
1287
bool
1288
set_window_icon_name(TwmWindow *win)
1289
{
1290
	char *newname = NULL;
1291
#define TRY(fld) { \
1292
                if(newname == NULL && win->names.fld != NULL) { \
1293
                        newname = win->names.fld; \
1294
                        win->names.icon_set = true; \
1295
                } \
1296
        }
615.1.39 by Matthew Fuller
Add places for and handling of some properties for overriding
1297
	TRY(ctwm_wm_icon_name)
615.1.30 by Matthew Fuller
Shift the icon_name setting bits off into functions like we did for
1298
#ifdef EWMH
1299
	TRY(net_wm_icon_name)
1300
#endif
1301
	TRY(wm_icon_name)
1302
#undef TRY
1303
1304
	// Our fallback for icon names is the window name.  Flag when we're
1305
	// doing that, so the window name handler can know when it needs to
1306
	// call us.
1307
	if(newname == NULL) {
1308
		newname = win->name;
1309
		win->names.icon_set = false;
1310
	}
1311
	if(win->icon_name == newname) {
1312
		return false; // Nothing to do
1313
	}
1314
615.1.52 by Matthew Fuller
Implement EWMH _NET_WM_VISIBLE{,_ICON}_NAME, telling external
1315
	// A name is chosen
615.1.30 by Matthew Fuller
Shift the icon_name setting bits off into functions like we did for
1316
	win->icon_name = newname;
615.1.52 by Matthew Fuller
Implement EWMH _NET_WM_VISIBLE{,_ICON}_NAME, telling external
1317
1318
#ifdef EWMH
1319
	// EWMH asks for _NET_WM_VISIBLE_ICON_NAME in various cases where
1320
	// we're not using 'standard' properties' values.  x-ref comments above in
1321
	// set_window_name() about the parallel property for the window name
1322
	// for various caveats.
1323
	if(win->icon_name != win->names.net_wm_icon_name
1324
	                && win->icon_name != win->names.wm_icon_name) {
1325
		// XXX Still encoding questionable; x-ref above.
1326
		XChangeProperty(dpy, win->w, XA__NET_WM_VISIBLE_ICON_NAME,
1327
		                XA_UTF8_STRING,
1328
		                8, PropModeReplace, (unsigned char *)win->icon_name,
1329
		                strlen(win->icon_name));
1330
	}
1331
	else {
1332
		XDeleteProperty(dpy, win->w, XA__NET_WM_VISIBLE_ICON_NAME);
1333
	}
1334
#endif // EWMH
1335
1336
	// Did it
615.1.30 by Matthew Fuller
Shift the icon_name setting bits off into functions like we did for
1337
	return true;
1338
}
1339
1340
615.1.54 by Matthew Fuller
As long as we're writing these funcs and their comments fresh, might
1341
/**
1342
 * [Re]set and apply changes to a window's icon name.  This is called
1343
 * after we've received a new WM_ICON_NAME (or other name-setting)
1344
 * property, to update our titlebars, icon managers, etc.
1345
 *
1346
 * \sa apply_window_name() which does the same for the window title.
615.1.30 by Matthew Fuller
Shift the icon_name setting bits off into functions like we did for
1347
 */
1348
void
1349
apply_window_icon_name(TwmWindow *win)
1350
{
1351
	/* [Re]set ->icon_name */
1352
	if(set_window_icon_name(win) == false) {
1353
		// No change
1354
		return;
1355
	}
1356
1357
1358
	/* Lot less to do for icons... */
1359
	RedoIcon(Tmp_win);
1360
	AutoPopupMaybe(Tmp_win);
1361
1362
	return;
1363
}