2
* Window-handling utility funcs
10
#include <X11/Xatom.h>
12
#include "add_window.h" // NoName
13
#include "ctwm_atoms.h"
14
#include "decorations.h"
19
#include "win_utils.h"
23
* Fill in size hints for a window from WM_NORMAL_HINTS prop.
25
* Formerly in add_window.c
28
GetWindowSizeHints(TwmWindow *tmp)
31
XSizeHints *hints = &tmp->hints;
33
if(!XGetWMNormalHints(dpy, tmp->w, hints, &supplied)) {
37
if(hints->flags & PResizeInc) {
38
if(hints->width_inc == 0) {
41
if(hints->height_inc == 0) {
42
hints->height_inc = 1;
46
if(!(supplied & PWinGravity) && (hints->flags & USPosition)) {
47
static int gravs[] = { SouthEastGravity, SouthWestGravity,
48
NorthEastGravity, NorthWestGravity
50
int right = tmp->attr.x + tmp->attr.width + 2 * tmp->old_bw;
51
int bottom = tmp->attr.y + tmp->attr.height + 2 * tmp->old_bw;
53
gravs[((Scr->rooth - bottom <
54
tmp->title_height + 2 * tmp->frame_bw3D) ? 0 : 2) |
55
((Scr->rootw - right <
56
tmp->title_height + 2 * tmp->frame_bw3D) ? 0 : 1)];
57
hints->flags |= PWinGravity;
60
/* Check for min size < max size */
61
if((hints->flags & (PMinSize | PMaxSize)) == (PMinSize | PMaxSize)) {
62
if(hints->max_width < hints->min_width) {
63
if(hints->max_width > 0) {
64
hints->min_width = hints->max_width;
66
else if(hints->min_width > 0) {
67
hints->max_width = hints->min_width;
70
hints->max_width = hints->min_width = 1;
74
if(hints->max_height < hints->min_height) {
75
if(hints->max_height > 0) {
76
hints->min_height = hints->max_height;
78
else if(hints->min_height > 0) {
79
hints->max_height = hints->min_height;
82
hints->max_height = hints->min_height = 1;
90
* Fill in info from WM_PROTOCOLS property
92
* Formerly in add_window.c
95
FetchWmProtocols(TwmWindow *tmp)
97
unsigned long flags = 0L;
98
Atom *protocols = NULL;
101
if(XGetWMProtocols(dpy, tmp->w, &protocols, &n)) {
105
for(i = 0, ap = protocols; i < n; i++, ap++) {
106
if(*ap == XA_WM_TAKE_FOCUS) {
107
flags |= DoesWmTakeFocus;
109
if(*ap == XA_WM_SAVE_YOURSELF) {
110
flags |= DoesWmSaveYourself;
112
if(*ap == XA_WM_DELETE_WINDOW) {
113
flags |= DoesWmDeleteWindow;
120
tmp->protocols = flags;
125
* Figure signs for calculating location offsets for a window dependent
128
* Depending on how its gravity is set, offsets to window coordinates for
129
* e.g. border widths may need to move either down (right) or up (left).
130
* Or possibly not at all. So we write multipliers into passed vars for
133
* Formerly in add_window.c
136
GetGravityOffsets(TwmWindow *tmp, int *xp, int *yp)
138
static struct _gravity_offset {
140
} gravity_offsets[] = {
141
[ForgetGravity] = { 0, 0 },
142
[NorthWestGravity] = { -1, -1 },
143
[NorthGravity] = { 0, -1 },
144
[NorthEastGravity] = { 1, -1 },
145
[WestGravity] = { -1, 0 },
146
[CenterGravity] = { 0, 0 },
147
[EastGravity] = { 1, 0 },
148
[SouthWestGravity] = { -1, 1 },
149
[SouthGravity] = { 0, 1 },
150
[SouthEastGravity] = { 1, 1 },
151
[StaticGravity] = { 0, 0 },
153
int g = ((tmp->hints.flags & PWinGravity)
154
? tmp->hints.win_gravity : NorthWestGravity);
156
if(g < ForgetGravity || g > StaticGravity) {
160
*xp = gravity_offsets[g].x;
161
*yp = gravity_offsets[g].y;
167
* Finds the TwmWindow structure associated with a Window (if any), or
170
* This is a relatively cheap function since it does not involve
171
* communication with the server. Probably faster than walking the list
172
* of TwmWindows, since the lookup is by a hash table.
174
* Formerly in add_window.c
177
GetTwmWindow(Window w)
182
stat = XFindContext(dpy, w, TwmContext, (XPointer *)&twmwin);
183
if(stat == XCNOENT) {
191
/***********************************************************************
194
* GetWMPropertyString - Get Window Manager text property and
195
* convert it to a string.
198
* (char *) - pointer to the malloc'd string or NULL
201
* w - the id of the window whose property is to be retrieved
202
* prop - property atom (typically WM_NAME or WM_ICON_NAME)
204
***********************************************************************
209
GetWMPropertyString(Window w, Atom prop)
211
XTextProperty text_prop;
214
unsigned char *stringptr;
215
int status, len = -1;
217
(void)XGetTextProperty(dpy, w, &text_prop, prop);
218
if(text_prop.value != NULL) {
219
if(text_prop.encoding == XA_STRING
220
|| text_prop.encoding == XA_COMPOUND_TEXT) {
221
/* property is encoded as compound text - convert to locale string */
222
status = XmbTextPropertyToTextList(dpy, &text_prop,
223
&text_list, &text_list_count);
224
if(text_list_count == 0) {
227
else if(text_list == NULL) {
230
else if(text_list [0] == NULL) {
233
else if(status < 0 || text_list_count < 0) {
235
case XConverterNotFound:
237
"%s: Converter not found; unable to convert property %s of window ID %lx.\n",
238
ProgramName, XGetAtomName(dpy, prop), w);
242
"%s: Insufficient memory; unable to convert property %s of window ID %lx.\n",
243
ProgramName, XGetAtomName(dpy, prop), w);
245
case XLocaleNotSupported:
247
"%s: Locale not supported; unable to convert property %s of window ID %lx.\n",
248
ProgramName, XGetAtomName(dpy, prop), w);
253
don't call XFreeStringList - text_list appears to have
254
invalid address if status is bad
255
XFreeStringList(text_list);
259
len = strlen(text_list[0]);
260
stringptr = memcpy(malloc(len + 1), text_list[0], len + 1);
261
XFreeStringList(text_list);
265
/* property is encoded in a format we don't understand */
267
"%s: Encoding not STRING or COMPOUND_TEXT; unable to decode property %s of window ID %lx.\n",
268
ProgramName, XGetAtomName(dpy, prop), w);
271
XFree(text_prop.value);
282
* Cleanup something stored that we got from the above originally.
287
FreeWMPropertyString(char *prop)
289
if(prop && (char *)prop != NoName) {
296
* Window mapped on some virtual screen?
301
visible(const TwmWindow *tmp_win)
303
return (tmp_win->vs != NULL);
308
* Various code paths do a dance of "mask off notifications of event type
309
* ; do something that triggers that event (but we're doing it, so we
310
* don't need the notification) ; restore previous mask". So have some
311
* util funcs to make it more visually obvious.
314
* long prev_mask = mask_out_event(w, PropertyChangeMask);
315
* do_something_that_changes_properties();
316
* restore_mask(prev_mask);
318
* We're cheating a little with the -1 return on mask_out_event(), as
319
* that's theoretically valid for the data type. It's not as far as I
320
* can tell for X or us though; having all the bits set (well, I guess
321
* I'm assuming 2s-complement too) is pretty absurd, and there are only
322
* 25 defined bits in Xlib, so even on 32-bit systems, it shouldn't fill
326
mask_out_event(Window w, long ignore_event)
328
XWindowAttributes wattr;
330
/* Get current mask */
331
if(XGetWindowAttributes(dpy, w, &wattr) == 0) {
336
* If we're ignoring nothing, nothing to do. This is probably not
337
* strictly speaking a useful thing to ask for in general, but it's
338
* the right thing for us to do if we're asked to do nothing.
340
if(ignore_event == 0) {
341
return wattr.your_event_mask;
345
return mask_out_event_mask(w, ignore_event, wattr.your_event_mask);
349
mask_out_event_mask(Window w, long ignore_event, long curmask)
351
/* Set to the current, minus what we're wanting to ignore */
352
XSelectInput(dpy, w, (curmask & ~ignore_event));
354
/* Return what it was */
359
restore_mask(Window w, long restore)
361
return XSelectInput(dpy, w, restore);
366
* Setting and getting WM_STATE property.
368
* x-ref ICCCM section 4.1.3.1
369
* https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1
371
* XXX These should probably be named more alike, as they're
375
SetMapStateProp(TwmWindow *tmp_win, int state)
377
unsigned long data[2]; /* "suggested" by ICCCM version 1 */
379
data[0] = (unsigned long) state;
380
data[1] = (unsigned long)(tmp_win->iconify_by_unmapping ? None :
381
(tmp_win->icon ? tmp_win->icon->w : None));
383
XChangeProperty(dpy, tmp_win->w, XA_WM_STATE, XA_WM_STATE, 32,
384
PropModeReplace, (unsigned char *) data, 2);
389
GetWMState(Window w, int *statep, Window *iwp)
393
unsigned long nitems, bytesafter;
394
unsigned long *datap = NULL;
397
if(XGetWindowProperty(dpy, w, XA_WM_STATE, 0L, 2L, False, XA_WM_STATE,
398
&actual_type, &actual_format, &nitems, &bytesafter,
399
(unsigned char **) &datap) != Success || !datap) {
403
if(nitems <= 2) { /* "suggested" by ICCCM version 1 */
404
*statep = (int) datap[0];
405
*iwp = (Window) datap[1];
415
* Display a window's position in the dimensions window. This is used
416
* during various window positioning (during new window popups, moves,
419
* This reuses the same window for the position as is used during
420
* resizing for the dimesions of the window in DisplaySize(). The
421
* innards of the funcs can probably be collapsed together a little, and
422
* the higher-level knowledge of Scr->SizeWindow (e.g., for unmapping
423
* after ths op is done) should probably be encapsulated a bit better.
426
DisplayPosition(const TwmWindow *_unused_tmp_win, int x, int y)
440
sprintf(str, " %c%-4d %c%-4d ", signx, x, signy, y);
441
XRaiseWindow(dpy, Scr->SizeWindow);
443
Draw3DBorder(Scr->SizeWindow, 0, 0,
444
Scr->SizeStringOffset + Scr->SizeStringWidth + SIZE_HINDENT,
445
Scr->SizeFont.height + SIZE_VINDENT * 2,
446
2, Scr->DefaultC, off, false, false);
448
FB(Scr->DefaultC.fore, Scr->DefaultC.back);
449
XmbDrawImageString(dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
450
Scr->NormalGC, Scr->SizeStringOffset,
451
Scr->SizeFont.ascent + SIZE_VINDENT , str, 13);
456
* Various funcs for adjusting coordinates for windows based on
459
* XXX In desperate need of better commenting.
462
TryToPack(TwmWindow *tmp_win, int *x, int *y)
467
int winw = tmp_win->frame_width + 2 * tmp_win->frame_bw;
468
int winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
472
for(t = Scr->FirstWindow; t != NULL; t = t->next) {
476
if(t->winbox != tmp_win->winbox) {
479
if(t->vs != tmp_win->vs) {
486
w = t->frame_width + 2 * t->frame_bw;
487
h = t->frame_height + 2 * t->frame_bw;
488
if(newx >= t->frame_x + w) {
491
if(newy >= t->frame_y + h) {
494
if(newx + winw <= t->frame_x) {
497
if(newy + winh <= t->frame_y) {
501
if(newx + Scr->MovePackResistance > t->frame_x + w) { /* left */
502
newx = MAX(newx, t->frame_x + w);
505
if(newx + winw < t->frame_x + Scr->MovePackResistance) { /* right */
506
newx = MIN(newx, t->frame_x - winw);
509
if(newy + Scr->MovePackResistance > t->frame_y + h) { /* top */
510
newy = MAX(newy, t->frame_y + h);
513
if(newy + winh < t->frame_y + Scr->MovePackResistance) { /* bottom */
514
newy = MIN(newy, t->frame_y - winh);
524
* Directionals for TryToPush_be(). These differ from the specs for
525
* jump/pack/fill in functions. because there's an indeterminate option.
534
static void TryToPush_be(TwmWindow *tmp_win, int x, int y, PushDirection dir);
537
TryToPush(TwmWindow *tmp_win, int x, int y)
539
TryToPush_be(tmp_win, x, y, PD_ANY);
543
TryToPush_be(TwmWindow *tmp_win, int x, int y, PushDirection dir)
546
int newx, newy, ndir;
549
int winw = tmp_win->frame_width + 2 * tmp_win->frame_bw;
550
int winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
552
for(t = Scr->FirstWindow; t != NULL; t = t->next) {
556
if(t->winbox != tmp_win->winbox) {
559
if(t->vs != tmp_win->vs) {
566
w = t->frame_width + 2 * t->frame_bw;
567
h = t->frame_height + 2 * t->frame_bw;
568
if(x >= t->frame_x + w) {
571
if(y >= t->frame_y + h) {
574
if(x + winw <= t->frame_x) {
577
if(y + winh <= t->frame_y) {
582
if((dir == PD_ANY || dir == PD_LEFT) &&
583
(x + Scr->MovePackResistance > t->frame_x + w)) {
589
else if((dir == PD_ANY || dir == PD_RIGHT) &&
590
(x + winw < t->frame_x + Scr->MovePackResistance)) {
596
else if((dir == PD_ANY || dir == PD_TOP) &&
597
(y + Scr->MovePackResistance > t->frame_y + h)) {
603
else if((dir == PD_ANY || dir == PD_BOTTOM) &&
604
(y + winh < t->frame_y + Scr->MovePackResistance)) {
611
TryToPush_be(t, newx, newy, ndir);
612
TryToPack(t, &newx, &newy);
613
ConstrainByBorders(tmp_win,
614
&newx, t->frame_width + 2 * t->frame_bw,
615
&newy, t->frame_height + 2 * t->frame_bw);
616
SetupWindow(t, newx, newy, t->frame_width, t->frame_height, -1);
623
TryToGrid(TwmWindow *tmp_win, int *x, int *y)
625
int w = tmp_win->frame_width + 2 * tmp_win->frame_bw;
626
int h = tmp_win->frame_height + 2 * tmp_win->frame_bw;
627
int grav = ((tmp_win->hints.flags & PWinGravity)
628
? tmp_win->hints.win_gravity : NorthWestGravity);
633
case NorthWestGravity :
637
*x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
639
*y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid
642
case NorthEastGravity :
644
*x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
645
Scr->XMoveGrid) - w + Scr->BorderLeft;
646
*y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) *
647
Scr->YMoveGrid + Scr->BorderTop;
649
case SouthWestGravity :
651
*x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
653
*y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid)
654
- h + Scr->BorderTop;
656
case SouthEastGravity :
657
*x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
658
Scr->XMoveGrid) - w + Scr->BorderLeft;
659
*y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) *
660
Scr->YMoveGrid) - h + Scr->BorderTop;
668
* Functions related to keeping windows from being placed off-screen (or
669
* off-screen too far). Involved in handling of params like DontMoveOff
670
* and MoveOffResistance, etc.
672
static void ConstrainLeftTop(int *value, int border);
673
static void ConstrainRightBottom(int *value, int size1, int border, int size2);
676
ConstrainByBorders1(int *left, int width, int *top, int height)
678
ConstrainRightBottom(left, width, Scr->BorderRight, Scr->rootw);
679
ConstrainLeftTop(left, Scr->BorderLeft);
680
ConstrainRightBottom(top, height, Scr->BorderBottom, Scr->rooth);
681
ConstrainLeftTop(top, Scr->BorderTop);
685
ConstrainByBorders(TwmWindow *twmwin, int *left, int width,
686
int *top, int height)
689
XWindowAttributes attr;
690
XGetWindowAttributes(dpy, twmwin->winbox->window, &attr);
691
ConstrainRightBottom(left, width, 0, attr.width);
692
ConstrainLeftTop(left, 0);
693
ConstrainRightBottom(top, height, 0, attr.height);
694
ConstrainLeftTop(top, 0);
697
ConstrainByBorders1(left, width, top, height);
702
ConstrainLeftTop(int *value, int border)
704
if(*value < border) {
705
if(Scr->MoveOffResistance < 0 ||
706
*value > border - Scr->MoveOffResistance) {
709
else if(Scr->MoveOffResistance > 0 &&
710
*value <= border - Scr->MoveOffResistance) {
711
*value = *value + Scr->MoveOffResistance;
717
ConstrainRightBottom(int *value, int size1, int border, int size2)
719
if(*value + size1 > size2 - border) {
720
if(Scr->MoveOffResistance < 0 ||
721
*value + size1 < size2 - border + Scr->MoveOffResistance) {
722
*value = size2 - size1 - border;
724
else if(Scr->MoveOffResistance > 0 &&
725
*value + size1 >= size2 - border + Scr->MoveOffResistance) {
726
*value = *value - Scr->MoveOffResistance;