Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Window-handling utility funcs
      3  */
      4 
      5 #include "ctwm.h"
      6 
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 
     10 #include <X11/Xatom.h>
     11 
     12 #include "add_window.h" // NoName
     13 #include "ctwm_atoms.h"
     14 #include "drawing.h"
     15 #include "events.h"
     16 #include "event_internal.h" // Temp?
     17 #ifdef EWMH
     18 # include "ewmh_atoms.h"
     19 #endif
     20 #include "icons.h"
     21 #include "list.h"
     22 #include "occupation.h"
     23 #include "otp.h"
     24 #include "r_area.h"
     25 #include "r_area_list.h"
     26 #include "r_layout.h"
     27 #include "screen.h"
     28 #include "util.h"
     29 #include "win_decorations.h"
     30 #include "win_ops.h"
     31 #include "win_utils.h"
     32 #include "workspace_utils.h"
     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 }
    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 }
    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;
    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 },
    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 }
    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.
    186  *
    187  * Formerly in add_window.c
    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 }
    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  */
    221 char *
    222 GetWMPropertyString(Window w, Atom prop)
    223 {
    224 	XTextProperty       text_prop;
    225 	char                *stringptr;
    226 
    227 	XGetTextProperty(dpy, w, &text_prop, prop);
    228 	if(text_prop.value == NULL) {
    229 		return NULL;
    230 	}
    231 
    232 	if(text_prop.encoding == XA_STRING
    233 	                || text_prop.encoding == XA_UTF8_STRING
    234 	                || text_prop.encoding == XA_COMPOUND_TEXT) {
    235 		/* property is encoded as compound text - convert to locale string */
    236 		char **text_list;
    237 		int  text_list_count;
    238 		int status;
    239 
    240 		/* Check historical strictness */
    241 		if(Scr->StrictWinNameEncoding) {
    242 			bool fail = false;
    243 
    244 			if((prop == XA_WM_NAME || prop == XA_WM_ICON_NAME)
    245 			                && text_prop.encoding != XA_STRING
    246 			                && text_prop.encoding != XA_COMPOUND_TEXT) {
    247 				fail = true;
    248 			}
    249 
    250 #ifdef EWMH
    251 			if((prop == XA__NET_WM_NAME || prop == XA__NET_WM_ICON_NAME)
    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 
    266 
    267 		status = XmbTextPropertyToTextList(dpy, &text_prop, &text_list,
    268 		                                   &text_list_count);
    269 		if(text_list_count == 0
    270 		                || text_list == NULL
    271 		                || text_list[0] == NULL) {
    272 			// Got nothing
    273 			XFree(text_prop.value);
    274 			return NULL;
    275 		}
    276 		else if(status < 0 || text_list_count < 0) {
    277 			// Got an error statuf
    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 			*/
    301 		}
    302 		else {
    303 			// Actually got the data!
    304 			stringptr = strdup(text_list[0]);
    305 			XFreeStringList(text_list);
    306 		}
    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);
    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 {
    329 	if(prop && prop != NoName) {
    330 		free(prop);
    331 	}
    332 }
    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 }
    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 }
    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
    410  *
    411  * XXX These should probably be named more alike, as they're
    412  * complementary ops.
    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 }
    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,
    491 	                   Scr->SizeFont.ascent + SIZE_VINDENT, str, 13);
    492 }
    493 
    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 
    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  */
    515 static void
    516 _tryToPack(RArea *final, const RArea *cur_win)
    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 
    552 static bool
    553 _tryToPackVsEachMonitor(const RArea *monitor_area, void *vfinal)
    554 {
    555 	_tryToPack((RArea *)vfinal, monitor_area);
    556 	return false;
    557 }
    558 
    559 void
    560 TryToPack(TwmWindow *tmp_win, int *x, int *y)
    561 {
    562 	TwmWindow   *t;
    563 	RArea cur_win;
    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);
    567 
    568 	/* Global layout is not a single rectangle, check against the
    569 	 * monitor borders */
    570 	if(Scr->BorderedLayout->horiz->len > 1) {
    571 		RAreaListForeach(
    572 		        Scr->BorderedLayout->monitors, _tryToPackVsEachMonitor, &final);
    573 	}
    574 
    575 	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
    576 		if(t == tmp_win) {
    577 			continue;
    578 		}
    579 #ifdef WINBOX
    580 		if(t->winbox != tmp_win->winbox) {
    581 			continue;
    582 		}
    583 #endif
    584 		if(t->vs != tmp_win->vs) {
    585 			continue;
    586 		}
    587 		if(!t->mapped) {
    588 			continue;
    589 		}
    590 
    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);
    594 
    595 		_tryToPack(&final, &cur_win);
    596 	}
    597 
    598 	*x = final.x;
    599 	*y = final.y;
    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 		}
    636 #ifdef WINBOX
    637 		if(t->winbox != tmp_win->winbox) {
    638 			continue;
    639 		}
    640 #endif
    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 }
    746 
    747 
    748 
    749 #ifdef WINBOX
    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);
    757 #endif
    758 
    759 bool
    760 ConstrainByLayout(RLayout *layout, int move_off_res, int *left, int width,
    761                   int *top, int height)
    762 {
    763 	RArea area = RAreaNew(*left, *top, width, height);
    764 	int limit;
    765 	bool clipped = false;
    766 
    767 	limit = RLayoutFindBottomEdge(layout, &area) - height + 1;
    768 	if(area.y > limit) {
    769 		if(move_off_res >= 0 && area.y >= limit + move_off_res) {
    770 			area.y -= move_off_res;
    771 		}
    772 		else {
    773 			area.y = limit;
    774 			clipped = true;
    775 		}
    776 	}
    777 
    778 	limit = RLayoutFindRightEdge(layout, &area) - width + 1;
    779 	if(area.x > limit) {
    780 		if(move_off_res >= 0 && area.x >= limit + move_off_res) {
    781 			area.x -= move_off_res;
    782 		}
    783 		else {
    784 			area.x = limit;
    785 			clipped = true;
    786 		}
    787 	}
    788 
    789 	limit = RLayoutFindLeftEdge(layout, &area);
    790 	if(area.x < limit) {
    791 		if(move_off_res >= 0 && area.x <= limit - move_off_res) {
    792 			area.x += move_off_res;
    793 		}
    794 		else {
    795 			area.x = limit;
    796 			clipped = true;
    797 		}
    798 	}
    799 
    800 	limit = RLayoutFindTopEdge(layout, &area);
    801 	if(area.y < limit) {
    802 		if(move_off_res >= 0 && area.y <= limit - move_off_res) {
    803 			area.y += move_off_res;
    804 		}
    805 		else {
    806 			area.y = limit;
    807 			clipped = true;
    808 		}
    809 	}
    810 
    811 	*left = area.x;
    812 	*top = area.y;
    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);
    822 }
    823 
    824 void
    825 ConstrainByBorders(TwmWindow *twmwin, int *left, int width,
    826                    int *top, int height)
    827 {
    828 	if(false) {
    829 		// Dummy
    830 	}
    831 #ifdef WINBOX
    832 	else if(twmwin->winbox) {
    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 	}
    840 #endif
    841 	else {
    842 		ConstrainByBorders1(left, width, top, height);
    843 	}
    844 }
    845 
    846 #ifdef WINBOX
    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 }
    876 #endif
    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 }
    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 }
   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  *
   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.
   1024  *
   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...
   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 }
   1050 
   1051 
   1052 /**
   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 	 */
   1069 	if(!(hints->flags & InputHint)) {
   1070 		hints->input = True;
   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)) {
   1081 		hints->input = True;
   1082 	}
   1083 
   1084 
   1085 	/* Setup group bits */
   1086 	if(hints->flags & WindowGroupHint) {
   1087 		win->group = hints->window_group;
   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 /**
   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.
   1124  */
   1125 bool
   1126 set_window_name(TwmWindow *win)
   1127 {
   1128 	char *newname = NULL;
   1129 #define TRY(fld) { \
   1130                 if(newname == NULL && win->names.fld != NULL) { \
   1131                         newname = win->names.fld; \
   1132                 } \
   1133         }
   1134 	TRY(ctwm_wm_name)
   1135 #ifdef EWMH
   1136 	TRY(net_wm_name)
   1137 #endif
   1138 	TRY(wm_name)
   1139 #undef TRY
   1140 
   1141 	if(newname == NULL) {
   1142 		newname = NoName;
   1143 	}
   1144 	if(win->name == newname) {
   1145 		return false; // Nothing to do
   1146 	}
   1147 
   1148 	// Now we know what to call it
   1149 	win->name = newname;
   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
   1177 	return true;
   1178 }
   1179 
   1180 
   1181 /**
   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 
   1196 
   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 	/*
   1264 	 * If we haven't set a separate icon name, we use the window name, so
   1265 	 * we need to update it.
   1266 	 */
   1267 	if(win->names.icon_set == false) {
   1268 		apply_window_icon_name(win);
   1269 	}
   1270 	AutoPopupMaybe(win);
   1271 
   1272 	return;
   1273 }
   1274 
   1275 
   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.
   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         }
   1297 	TRY(ctwm_wm_icon_name)
   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 
   1315 	// A name is chosen
   1316 	win->icon_name = newname;
   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
   1337 	return true;
   1338 }
   1339 
   1340 
   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.
   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 }
   1364