win_utils.c revision 0bbfda8a
10bbfda8aSnia/*
20bbfda8aSnia * Window-handling utility funcs
30bbfda8aSnia */
40bbfda8aSnia
50bbfda8aSnia#include "ctwm.h"
60bbfda8aSnia
70bbfda8aSnia#include <stdio.h>
80bbfda8aSnia#include <stdlib.h>
90bbfda8aSnia
100bbfda8aSnia#include <X11/Xatom.h>
110bbfda8aSnia
120bbfda8aSnia#include "add_window.h" // NoName
130bbfda8aSnia#include "ctwm_atoms.h"
140bbfda8aSnia#include "drawing.h"
150bbfda8aSnia#include "events.h"
160bbfda8aSnia#include "event_internal.h" // Temp?
170bbfda8aSnia#ifdef EWMH
180bbfda8aSnia# include "ewmh_atoms.h"
190bbfda8aSnia#endif
200bbfda8aSnia#include "icons.h"
210bbfda8aSnia#include "list.h"
220bbfda8aSnia#include "occupation.h"
230bbfda8aSnia#include "otp.h"
240bbfda8aSnia#include "screen.h"
250bbfda8aSnia#include "util.h"
260bbfda8aSnia#include "win_decorations.h"
270bbfda8aSnia#include "win_ops.h"
280bbfda8aSnia#include "win_utils.h"
290bbfda8aSnia#include "workspace_utils.h"
300bbfda8aSnia
310bbfda8aSnia
320bbfda8aSnia/*
330bbfda8aSnia * Fill in size hints for a window from WM_NORMAL_HINTS prop.
340bbfda8aSnia *
350bbfda8aSnia * Formerly in add_window.c
360bbfda8aSnia */
370bbfda8aSniavoid
380bbfda8aSniaGetWindowSizeHints(TwmWindow *tmp)
390bbfda8aSnia{
400bbfda8aSnia	long supplied = 0;
410bbfda8aSnia	XSizeHints *hints = &tmp->hints;
420bbfda8aSnia
430bbfda8aSnia	if(!XGetWMNormalHints(dpy, tmp->w, hints, &supplied)) {
440bbfda8aSnia		hints->flags = 0;
450bbfda8aSnia	}
460bbfda8aSnia
470bbfda8aSnia	if(hints->flags & PResizeInc) {
480bbfda8aSnia		if(hints->width_inc == 0) {
490bbfda8aSnia			hints->width_inc = 1;
500bbfda8aSnia		}
510bbfda8aSnia		if(hints->height_inc == 0) {
520bbfda8aSnia			hints->height_inc = 1;
530bbfda8aSnia		}
540bbfda8aSnia	}
550bbfda8aSnia
560bbfda8aSnia	if(!(supplied & PWinGravity) && (hints->flags & USPosition)) {
570bbfda8aSnia		static int gravs[] = { SouthEastGravity, SouthWestGravity,
580bbfda8aSnia		                       NorthEastGravity, NorthWestGravity
590bbfda8aSnia		                     };
600bbfda8aSnia		int right =  tmp->attr.x + tmp->attr.width + 2 * tmp->old_bw;
610bbfda8aSnia		int bottom = tmp->attr.y + tmp->attr.height + 2 * tmp->old_bw;
620bbfda8aSnia		hints->win_gravity =
630bbfda8aSnia		        gravs[((Scr->rooth - bottom <
640bbfda8aSnia		                tmp->title_height + 2 * tmp->frame_bw3D) ? 0 : 2) |
650bbfda8aSnia		              ((Scr->rootw - right   <
660bbfda8aSnia		                tmp->title_height + 2 * tmp->frame_bw3D) ? 0 : 1)];
670bbfda8aSnia		hints->flags |= PWinGravity;
680bbfda8aSnia	}
690bbfda8aSnia
700bbfda8aSnia	/* Check for min size < max size */
710bbfda8aSnia	if((hints->flags & (PMinSize | PMaxSize)) == (PMinSize | PMaxSize)) {
720bbfda8aSnia		if(hints->max_width < hints->min_width) {
730bbfda8aSnia			if(hints->max_width > 0) {
740bbfda8aSnia				hints->min_width = hints->max_width;
750bbfda8aSnia			}
760bbfda8aSnia			else if(hints->min_width > 0) {
770bbfda8aSnia				hints->max_width = hints->min_width;
780bbfda8aSnia			}
790bbfda8aSnia			else {
800bbfda8aSnia				hints->max_width = hints->min_width = 1;
810bbfda8aSnia			}
820bbfda8aSnia		}
830bbfda8aSnia
840bbfda8aSnia		if(hints->max_height < hints->min_height) {
850bbfda8aSnia			if(hints->max_height > 0) {
860bbfda8aSnia				hints->min_height = hints->max_height;
870bbfda8aSnia			}
880bbfda8aSnia			else if(hints->min_height > 0) {
890bbfda8aSnia				hints->max_height = hints->min_height;
900bbfda8aSnia			}
910bbfda8aSnia			else {
920bbfda8aSnia				hints->max_height = hints->min_height = 1;
930bbfda8aSnia			}
940bbfda8aSnia		}
950bbfda8aSnia	}
960bbfda8aSnia}
970bbfda8aSnia
980bbfda8aSnia
990bbfda8aSnia/*
1000bbfda8aSnia * Fill in info from WM_PROTOCOLS property
1010bbfda8aSnia *
1020bbfda8aSnia * Formerly in add_window.c
1030bbfda8aSnia */
1040bbfda8aSniavoid
1050bbfda8aSniaFetchWmProtocols(TwmWindow *tmp)
1060bbfda8aSnia{
1070bbfda8aSnia	unsigned long flags = 0L;
1080bbfda8aSnia	Atom *protocols = NULL;
1090bbfda8aSnia	int n;
1100bbfda8aSnia
1110bbfda8aSnia	if(XGetWMProtocols(dpy, tmp->w, &protocols, &n)) {
1120bbfda8aSnia		int i;
1130bbfda8aSnia		Atom *ap;
1140bbfda8aSnia
1150bbfda8aSnia		for(i = 0, ap = protocols; i < n; i++, ap++) {
1160bbfda8aSnia			if(*ap == XA_WM_TAKE_FOCUS) {
1170bbfda8aSnia				flags |= DoesWmTakeFocus;
1180bbfda8aSnia			}
1190bbfda8aSnia			if(*ap == XA_WM_SAVE_YOURSELF) {
1200bbfda8aSnia				flags |= DoesWmSaveYourself;
1210bbfda8aSnia			}
1220bbfda8aSnia			if(*ap == XA_WM_DELETE_WINDOW) {
1230bbfda8aSnia				flags |= DoesWmDeleteWindow;
1240bbfda8aSnia			}
1250bbfda8aSnia		}
1260bbfda8aSnia		if(protocols) {
1270bbfda8aSnia			XFree(protocols);
1280bbfda8aSnia		}
1290bbfda8aSnia	}
1300bbfda8aSnia	tmp->protocols = flags;
1310bbfda8aSnia}
1320bbfda8aSnia
1330bbfda8aSnia
1340bbfda8aSnia/*
1350bbfda8aSnia * Figure signs for calculating location offsets for a window dependent
1360bbfda8aSnia * on its gravity.
1370bbfda8aSnia *
1380bbfda8aSnia * Depending on how its gravity is set, offsets to window coordinates for
1390bbfda8aSnia * e.g. border widths may need to move either down (right) or up (left).
1400bbfda8aSnia * Or possibly not at all.  So we write multipliers into passed vars for
1410bbfda8aSnia * callers.
1420bbfda8aSnia *
1430bbfda8aSnia * Formerly in add_window.c
1440bbfda8aSnia */
1450bbfda8aSniavoid
1460bbfda8aSniaGetGravityOffsets(TwmWindow *tmp, int *xp, int *yp)
1470bbfda8aSnia{
1480bbfda8aSnia	static struct _gravity_offset {
1490bbfda8aSnia		int x, y;
1500bbfda8aSnia	} gravity_offsets[] = {
1510bbfda8aSnia		[ForgetGravity]    = {  0,  0 },
1520bbfda8aSnia		[NorthWestGravity] = { -1, -1 },
1530bbfda8aSnia		[NorthGravity]     = {  0, -1 },
1540bbfda8aSnia		[NorthEastGravity] = {  1, -1 },
1550bbfda8aSnia		[WestGravity]      = { -1,  0 },
1560bbfda8aSnia		[CenterGravity]    = {  0,  0 },
1570bbfda8aSnia		[EastGravity]      = {  1,  0 },
1580bbfda8aSnia		[SouthWestGravity] = { -1,  1 },
1590bbfda8aSnia		[SouthGravity]     = {  0,  1 },
1600bbfda8aSnia		[SouthEastGravity] = {  1,  1 },
1610bbfda8aSnia		[StaticGravity]    = {  0,  0 },
1620bbfda8aSnia	};
1630bbfda8aSnia	int g = ((tmp->hints.flags & PWinGravity)
1640bbfda8aSnia	         ? tmp->hints.win_gravity : NorthWestGravity);
1650bbfda8aSnia
1660bbfda8aSnia	if(g < ForgetGravity || g > StaticGravity) {
1670bbfda8aSnia		*xp = *yp = 0;
1680bbfda8aSnia	}
1690bbfda8aSnia	else {
1700bbfda8aSnia		*xp = gravity_offsets[g].x;
1710bbfda8aSnia		*yp = gravity_offsets[g].y;
1720bbfda8aSnia	}
1730bbfda8aSnia}
1740bbfda8aSnia
1750bbfda8aSnia
1760bbfda8aSnia/*
1770bbfda8aSnia * Finds the TwmWindow structure associated with a Window (if any), or
1780bbfda8aSnia * NULL.
1790bbfda8aSnia *
1800bbfda8aSnia * This is a relatively cheap function since it does not involve
1810bbfda8aSnia * communication with the server. Probably faster than walking the list
1820bbfda8aSnia * of TwmWindows, since the lookup is by a hash table.
1830bbfda8aSnia *
1840bbfda8aSnia * Formerly in add_window.c
1850bbfda8aSnia */
1860bbfda8aSniaTwmWindow *
1870bbfda8aSniaGetTwmWindow(Window w)
1880bbfda8aSnia{
1890bbfda8aSnia	TwmWindow *twmwin;
1900bbfda8aSnia	int stat;
1910bbfda8aSnia
1920bbfda8aSnia	stat = XFindContext(dpy, w, TwmContext, (XPointer *)&twmwin);
1930bbfda8aSnia	if(stat == XCNOENT) {
1940bbfda8aSnia		twmwin = NULL;
1950bbfda8aSnia	}
1960bbfda8aSnia
1970bbfda8aSnia	return twmwin;
1980bbfda8aSnia}
1990bbfda8aSnia
2000bbfda8aSnia
2010bbfda8aSnia/***********************************************************************
2020bbfda8aSnia *
2030bbfda8aSnia *  Procedure:
2040bbfda8aSnia *      GetWMPropertyString - Get Window Manager text property and
2050bbfda8aSnia *                              convert it to a string.
2060bbfda8aSnia *
2070bbfda8aSnia *  Returned Value:
2080bbfda8aSnia *      (char *) - pointer to the malloc'd string or NULL
2090bbfda8aSnia *
2100bbfda8aSnia *  Inputs:
2110bbfda8aSnia *      w       - the id of the window whose property is to be retrieved
2120bbfda8aSnia *      prop    - property atom (typically WM_NAME or WM_ICON_NAME)
2130bbfda8aSnia *
2140bbfda8aSnia ***********************************************************************
2150bbfda8aSnia *
2160bbfda8aSnia * Formerly in util.c
2170bbfda8aSnia */
2180bbfda8aSniachar *
2190bbfda8aSniaGetWMPropertyString(Window w, Atom prop)
2200bbfda8aSnia{
2210bbfda8aSnia	XTextProperty       text_prop;
2220bbfda8aSnia	char                *stringptr;
2230bbfda8aSnia
2240bbfda8aSnia	XGetTextProperty(dpy, w, &text_prop, prop);
2250bbfda8aSnia	if(text_prop.value == NULL) {
2260bbfda8aSnia		return NULL;
2270bbfda8aSnia	}
2280bbfda8aSnia
2290bbfda8aSnia	if(text_prop.encoding == XA_STRING
2300bbfda8aSnia	                || text_prop.encoding == XA_UTF8_STRING
2310bbfda8aSnia	                || text_prop.encoding == XA_COMPOUND_TEXT) {
2320bbfda8aSnia		/* property is encoded as compound text - convert to locale string */
2330bbfda8aSnia		char **text_list;
2340bbfda8aSnia		int  text_list_count;
2350bbfda8aSnia		int status;
2360bbfda8aSnia
2370bbfda8aSnia		/* Check historical strictness */
2380bbfda8aSnia		if(Scr->StrictWinNameEncoding) {
2390bbfda8aSnia			bool fail = false;
2400bbfda8aSnia
2410bbfda8aSnia			if((prop == XA_WM_NAME || prop == XA_WM_ICON_NAME)
2420bbfda8aSnia			                && text_prop.encoding != XA_STRING
2430bbfda8aSnia			                && text_prop.encoding != XA_COMPOUND_TEXT) {
2440bbfda8aSnia				fail = true;
2450bbfda8aSnia			}
2460bbfda8aSnia
2470bbfda8aSnia#ifdef EWMH
2480bbfda8aSnia			if((prop == XA__NET_WM_NAME || prop == XA__NET_WM_ICON_NAME)
2490bbfda8aSnia			                && text_prop.encoding != XA_UTF8_STRING) {
2500bbfda8aSnia				fail = true;
2510bbfda8aSnia			}
2520bbfda8aSnia#endif // EWMH
2530bbfda8aSnia
2540bbfda8aSnia			if(fail) {
2550bbfda8aSnia				fprintf(stderr, "%s: Invalid encoding for property %s "
2560bbfda8aSnia				        "of window 0x%lx\n", ProgramName,
2570bbfda8aSnia				        XGetAtomName(dpy, prop), w);
2580bbfda8aSnia				XFree(text_prop.value);
2590bbfda8aSnia				return NULL;
2600bbfda8aSnia			}
2610bbfda8aSnia		}
2620bbfda8aSnia
2630bbfda8aSnia
2640bbfda8aSnia		status = XmbTextPropertyToTextList(dpy, &text_prop, &text_list,
2650bbfda8aSnia		                                   &text_list_count);
2660bbfda8aSnia		if(text_list_count == 0
2670bbfda8aSnia		                || text_list == NULL
2680bbfda8aSnia		                || text_list[0] == NULL) {
2690bbfda8aSnia			// Got nothing
2700bbfda8aSnia			XFree(text_prop.value);
2710bbfda8aSnia			return NULL;
2720bbfda8aSnia		}
2730bbfda8aSnia		else if(status < 0 || text_list_count < 0) {
2740bbfda8aSnia			// Got an error statuf
2750bbfda8aSnia			switch(status) {
2760bbfda8aSnia				case XConverterNotFound:
2770bbfda8aSnia					fprintf(stderr,
2780bbfda8aSnia					        "%s: Converter not found; unable to convert property %s of window ID %lx.\n",
2790bbfda8aSnia					        ProgramName, XGetAtomName(dpy, prop), w);
2800bbfda8aSnia					break;
2810bbfda8aSnia				case XNoMemory:
2820bbfda8aSnia					fprintf(stderr,
2830bbfda8aSnia					        "%s: Insufficient memory; unable to convert property %s of window ID %lx.\n",
2840bbfda8aSnia					        ProgramName, XGetAtomName(dpy, prop), w);
2850bbfda8aSnia					break;
2860bbfda8aSnia				case XLocaleNotSupported:
2870bbfda8aSnia					fprintf(stderr,
2880bbfda8aSnia					        "%s: Locale not supported; unable to convert property %s of window ID %lx.\n",
2890bbfda8aSnia					        ProgramName, XGetAtomName(dpy, prop), w);
2900bbfda8aSnia					break;
2910bbfda8aSnia			}
2920bbfda8aSnia			stringptr = NULL;
2930bbfda8aSnia			/*
2940bbfda8aSnia			   don't call XFreeStringList - text_list appears to have
2950bbfda8aSnia			   invalid address if status is bad
2960bbfda8aSnia			   XFreeStringList(text_list);
2970bbfda8aSnia			*/
2980bbfda8aSnia		}
2990bbfda8aSnia		else {
3000bbfda8aSnia			// Actually got the data!
3010bbfda8aSnia			stringptr = strdup(text_list[0]);
3020bbfda8aSnia			XFreeStringList(text_list);
3030bbfda8aSnia		}
3040bbfda8aSnia	}
3050bbfda8aSnia	else {
3060bbfda8aSnia		/* property is encoded in a format we don't understand */
3070bbfda8aSnia		fprintf(stderr,
3080bbfda8aSnia		        "%s: Encoding not STRING or COMPOUND_TEXT; unable to decode property %s of window ID %lx.\n",
3090bbfda8aSnia		        ProgramName, XGetAtomName(dpy, prop), w);
3100bbfda8aSnia		stringptr = NULL;
3110bbfda8aSnia	}
3120bbfda8aSnia	XFree(text_prop.value);
3130bbfda8aSnia
3140bbfda8aSnia	return stringptr;
3150bbfda8aSnia}
3160bbfda8aSnia
3170bbfda8aSnia
3180bbfda8aSnia/*
3190bbfda8aSnia * Cleanup something stored that we got from the above originally.
3200bbfda8aSnia *
3210bbfda8aSnia * Formerly in util.c
3220bbfda8aSnia */
3230bbfda8aSniavoid
3240bbfda8aSniaFreeWMPropertyString(char *prop)
3250bbfda8aSnia{
3260bbfda8aSnia	if(prop && prop != NoName) {
3270bbfda8aSnia		free(prop);
3280bbfda8aSnia	}
3290bbfda8aSnia}
3300bbfda8aSnia
3310bbfda8aSnia
3320bbfda8aSnia/*
3330bbfda8aSnia * Window mapped on some virtual screen?
3340bbfda8aSnia *
3350bbfda8aSnia * Formerly in util.c
3360bbfda8aSnia */
3370bbfda8aSniabool
3380bbfda8aSniavisible(const TwmWindow *tmp_win)
3390bbfda8aSnia{
3400bbfda8aSnia	return (tmp_win->vs != NULL);
3410bbfda8aSnia}
3420bbfda8aSnia
3430bbfda8aSnia
3440bbfda8aSnia/*
3450bbfda8aSnia * Various code paths do a dance of "mask off notifications of event type
3460bbfda8aSnia * ; do something that triggers that event (but we're doing it, so we
3470bbfda8aSnia * don't need the notification) ; restore previous mask".  So have some
3480bbfda8aSnia * util funcs to make it more visually obvious.
3490bbfda8aSnia *
3500bbfda8aSnia * e.g.:
3510bbfda8aSnia *     long prev_mask = mask_out_event(w, PropertyChangeMask);
3520bbfda8aSnia *     do_something_that_changes_properties();
3530bbfda8aSnia *     restore_mask(prev_mask);
3540bbfda8aSnia *
3550bbfda8aSnia * We're cheating a little with the -1 return on mask_out_event(), as
3560bbfda8aSnia * that's theoretically valid for the data type.  It's not as far as I
3570bbfda8aSnia * can tell for X or us though; having all the bits set (well, I guess
3580bbfda8aSnia * I'm assuming 2s-complement too) is pretty absurd, and there are only
3590bbfda8aSnia * 25 defined bits in Xlib, so even on 32-bit systems, it shouldn't fill
3600bbfda8aSnia * up long.
3610bbfda8aSnia */
3620bbfda8aSnialong
3630bbfda8aSniamask_out_event(Window w, long ignore_event)
3640bbfda8aSnia{
3650bbfda8aSnia	XWindowAttributes wattr;
3660bbfda8aSnia
3670bbfda8aSnia	/* Get current mask */
3680bbfda8aSnia	if(XGetWindowAttributes(dpy, w, &wattr) == 0) {
3690bbfda8aSnia		return -1;
3700bbfda8aSnia	}
3710bbfda8aSnia
3720bbfda8aSnia	/*
3730bbfda8aSnia	 * If we're ignoring nothing, nothing to do.  This is probably not
3740bbfda8aSnia	 * strictly speaking a useful thing to ask for in general, but it's
3750bbfda8aSnia	 * the right thing for us to do if we're asked to do nothing.
3760bbfda8aSnia	 */
3770bbfda8aSnia	if(ignore_event == 0) {
3780bbfda8aSnia		return wattr.your_event_mask;
3790bbfda8aSnia	}
3800bbfda8aSnia
3810bbfda8aSnia	/* Delegate */
3820bbfda8aSnia	return mask_out_event_mask(w, ignore_event, wattr.your_event_mask);
3830bbfda8aSnia}
3840bbfda8aSnia
3850bbfda8aSnialong
3860bbfda8aSniamask_out_event_mask(Window w, long ignore_event, long curmask)
3870bbfda8aSnia{
3880bbfda8aSnia	/* Set to the current, minus what we're wanting to ignore */
3890bbfda8aSnia	XSelectInput(dpy, w, (curmask & ~ignore_event));
3900bbfda8aSnia
3910bbfda8aSnia	/* Return what it was */
3920bbfda8aSnia	return curmask;
3930bbfda8aSnia}
3940bbfda8aSnia
3950bbfda8aSniaint
3960bbfda8aSniarestore_mask(Window w, long restore)
3970bbfda8aSnia{
3980bbfda8aSnia	return XSelectInput(dpy, w, restore);
3990bbfda8aSnia}
4000bbfda8aSnia
4010bbfda8aSnia
4020bbfda8aSnia/*
4030bbfda8aSnia * Setting and getting WM_STATE property.
4040bbfda8aSnia *
4050bbfda8aSnia * x-ref ICCCM section 4.1.3.1
4060bbfda8aSnia * https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1
4070bbfda8aSnia *
4080bbfda8aSnia * XXX These should probably be named more alike, as they're
4090bbfda8aSnia * complementary ops.
4100bbfda8aSnia */
4110bbfda8aSniavoid
4120bbfda8aSniaSetMapStateProp(TwmWindow *tmp_win, int state)
4130bbfda8aSnia{
4140bbfda8aSnia	unsigned long data[2];              /* "suggested" by ICCCM version 1 */
4150bbfda8aSnia
4160bbfda8aSnia	data[0] = (unsigned long) state;
4170bbfda8aSnia	data[1] = (unsigned long)(tmp_win->iconify_by_unmapping ? None :
4180bbfda8aSnia	                          (tmp_win->icon ? tmp_win->icon->w : None));
4190bbfda8aSnia
4200bbfda8aSnia	XChangeProperty(dpy, tmp_win->w, XA_WM_STATE, XA_WM_STATE, 32,
4210bbfda8aSnia	                PropModeReplace, (unsigned char *) data, 2);
4220bbfda8aSnia}
4230bbfda8aSnia
4240bbfda8aSnia
4250bbfda8aSniabool
4260bbfda8aSniaGetWMState(Window w, int *statep, Window *iwp)
4270bbfda8aSnia{
4280bbfda8aSnia	Atom actual_type;
4290bbfda8aSnia	int actual_format;
4300bbfda8aSnia	unsigned long nitems, bytesafter;
4310bbfda8aSnia	unsigned long *datap = NULL;
4320bbfda8aSnia	bool retval = false;
4330bbfda8aSnia
4340bbfda8aSnia	if(XGetWindowProperty(dpy, w, XA_WM_STATE, 0L, 2L, False, XA_WM_STATE,
4350bbfda8aSnia	                      &actual_type, &actual_format, &nitems, &bytesafter,
4360bbfda8aSnia	                      (unsigned char **) &datap) != Success || !datap) {
4370bbfda8aSnia		return false;
4380bbfda8aSnia	}
4390bbfda8aSnia
4400bbfda8aSnia	if(nitems <= 2) {                   /* "suggested" by ICCCM version 1 */
4410bbfda8aSnia		*statep = (int) datap[0];
4420bbfda8aSnia		*iwp = (Window) datap[1];
4430bbfda8aSnia		retval = true;
4440bbfda8aSnia	}
4450bbfda8aSnia
4460bbfda8aSnia	XFree(datap);
4470bbfda8aSnia	return retval;
4480bbfda8aSnia}
4490bbfda8aSnia
4500bbfda8aSnia
4510bbfda8aSnia/*
4520bbfda8aSnia * Display a window's position in the dimensions window.  This is used
4530bbfda8aSnia * during various window positioning (during new window popups, moves,
4540bbfda8aSnia * etc).
4550bbfda8aSnia *
4560bbfda8aSnia * This reuses the same window for the position as is used during
4570bbfda8aSnia * resizing for the dimesions of the window in DisplaySize().  The
4580bbfda8aSnia * innards of the funcs can probably be collapsed together a little, and
4590bbfda8aSnia * the higher-level knowledge of Scr->SizeWindow (e.g., for unmapping
4600bbfda8aSnia * after ths op is done) should probably be encapsulated a bit better.
4610bbfda8aSnia */
4620bbfda8aSniavoid
4630bbfda8aSniaDisplayPosition(const TwmWindow *_unused_tmp_win, int x, int y)
4640bbfda8aSnia{
4650bbfda8aSnia	char str [100];
4660bbfda8aSnia	char signx = '+';
4670bbfda8aSnia	char signy = '+';
4680bbfda8aSnia
4690bbfda8aSnia	if(x < 0) {
4700bbfda8aSnia		x = -x;
4710bbfda8aSnia		signx = '-';
4720bbfda8aSnia	}
4730bbfda8aSnia	if(y < 0) {
4740bbfda8aSnia		y = -y;
4750bbfda8aSnia		signy = '-';
4760bbfda8aSnia	}
4770bbfda8aSnia	sprintf(str, " %c%-4d %c%-4d ", signx, x, signy, y);
4780bbfda8aSnia	XRaiseWindow(dpy, Scr->SizeWindow);
4790bbfda8aSnia
4800bbfda8aSnia	Draw3DBorder(Scr->SizeWindow, 0, 0,
4810bbfda8aSnia	             Scr->SizeStringOffset + Scr->SizeStringWidth + SIZE_HINDENT,
4820bbfda8aSnia	             Scr->SizeFont.height + SIZE_VINDENT * 2,
4830bbfda8aSnia	             2, Scr->DefaultC, off, false, false);
4840bbfda8aSnia
4850bbfda8aSnia	FB(Scr->DefaultC.fore, Scr->DefaultC.back);
4860bbfda8aSnia	XmbDrawImageString(dpy, Scr->SizeWindow, Scr->SizeFont.font_set,
4870bbfda8aSnia	                   Scr->NormalGC, Scr->SizeStringOffset,
4880bbfda8aSnia	                   Scr->SizeFont.ascent + SIZE_VINDENT, str, 13);
4890bbfda8aSnia}
4900bbfda8aSnia
4910bbfda8aSnia
4920bbfda8aSnia/*
4930bbfda8aSnia * Various funcs for adjusting coordinates for windows based on
4940bbfda8aSnia * resistances etc.
4950bbfda8aSnia *
4960bbfda8aSnia * XXX In desperate need of better commenting.
4970bbfda8aSnia */
4980bbfda8aSniavoid
4990bbfda8aSniaTryToPack(TwmWindow *tmp_win, int *x, int *y)
5000bbfda8aSnia{
5010bbfda8aSnia	TwmWindow   *t;
5020bbfda8aSnia	int         newx, newy;
5030bbfda8aSnia	int         w, h;
5040bbfda8aSnia	int         winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5050bbfda8aSnia	int         winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5060bbfda8aSnia
5070bbfda8aSnia	newx = *x;
5080bbfda8aSnia	newy = *y;
5090bbfda8aSnia	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
5100bbfda8aSnia		if(t == tmp_win) {
5110bbfda8aSnia			continue;
5120bbfda8aSnia		}
5130bbfda8aSnia		if(t->winbox != tmp_win->winbox) {
5140bbfda8aSnia			continue;
5150bbfda8aSnia		}
5160bbfda8aSnia		if(t->vs != tmp_win->vs) {
5170bbfda8aSnia			continue;
5180bbfda8aSnia		}
5190bbfda8aSnia		if(!t->mapped) {
5200bbfda8aSnia			continue;
5210bbfda8aSnia		}
5220bbfda8aSnia
5230bbfda8aSnia		w = t->frame_width  + 2 * t->frame_bw;
5240bbfda8aSnia		h = t->frame_height + 2 * t->frame_bw;
5250bbfda8aSnia		if(newx >= t->frame_x + w) {
5260bbfda8aSnia			continue;
5270bbfda8aSnia		}
5280bbfda8aSnia		if(newy >= t->frame_y + h) {
5290bbfda8aSnia			continue;
5300bbfda8aSnia		}
5310bbfda8aSnia		if(newx + winw <= t->frame_x) {
5320bbfda8aSnia			continue;
5330bbfda8aSnia		}
5340bbfda8aSnia		if(newy + winh <= t->frame_y) {
5350bbfda8aSnia			continue;
5360bbfda8aSnia		}
5370bbfda8aSnia
5380bbfda8aSnia		if(newx + Scr->MovePackResistance > t->frame_x + w) {  /* left */
5390bbfda8aSnia			newx = MAX(newx, t->frame_x + w);
5400bbfda8aSnia			continue;
5410bbfda8aSnia		}
5420bbfda8aSnia		if(newx + winw < t->frame_x + Scr->MovePackResistance) {  /* right */
5430bbfda8aSnia			newx = MIN(newx, t->frame_x - winw);
5440bbfda8aSnia			continue;
5450bbfda8aSnia		}
5460bbfda8aSnia		if(newy + Scr->MovePackResistance > t->frame_y + h) {  /* top */
5470bbfda8aSnia			newy = MAX(newy, t->frame_y + h);
5480bbfda8aSnia			continue;
5490bbfda8aSnia		}
5500bbfda8aSnia		if(newy + winh < t->frame_y + Scr->MovePackResistance) {  /* bottom */
5510bbfda8aSnia			newy = MIN(newy, t->frame_y - winh);
5520bbfda8aSnia			continue;
5530bbfda8aSnia		}
5540bbfda8aSnia	}
5550bbfda8aSnia	*x = newx;
5560bbfda8aSnia	*y = newy;
5570bbfda8aSnia}
5580bbfda8aSnia
5590bbfda8aSnia
5600bbfda8aSnia/*
5610bbfda8aSnia * Directionals for TryToPush_be().  These differ from the specs for
5620bbfda8aSnia * jump/pack/fill in functions. because there's an indeterminate option.
5630bbfda8aSnia */
5640bbfda8aSniatypedef enum {
5650bbfda8aSnia	PD_ANY,
5660bbfda8aSnia	PD_BOTTOM,
5670bbfda8aSnia	PD_LEFT,
5680bbfda8aSnia	PD_RIGHT,
5690bbfda8aSnia	PD_TOP,
5700bbfda8aSnia} PushDirection;
5710bbfda8aSniastatic void TryToPush_be(TwmWindow *tmp_win, int x, int y, PushDirection dir);
5720bbfda8aSnia
5730bbfda8aSniavoid
5740bbfda8aSniaTryToPush(TwmWindow *tmp_win, int x, int y)
5750bbfda8aSnia{
5760bbfda8aSnia	TryToPush_be(tmp_win, x, y, PD_ANY);
5770bbfda8aSnia}
5780bbfda8aSnia
5790bbfda8aSniastatic void
5800bbfda8aSniaTryToPush_be(TwmWindow *tmp_win, int x, int y, PushDirection dir)
5810bbfda8aSnia{
5820bbfda8aSnia	TwmWindow   *t;
5830bbfda8aSnia	int         newx, newy, ndir;
5840bbfda8aSnia	bool        move;
5850bbfda8aSnia	int         w, h;
5860bbfda8aSnia	int         winw = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
5870bbfda8aSnia	int         winh = tmp_win->frame_height + 2 * tmp_win->frame_bw;
5880bbfda8aSnia
5890bbfda8aSnia	for(t = Scr->FirstWindow; t != NULL; t = t->next) {
5900bbfda8aSnia		if(t == tmp_win) {
5910bbfda8aSnia			continue;
5920bbfda8aSnia		}
5930bbfda8aSnia		if(t->winbox != tmp_win->winbox) {
5940bbfda8aSnia			continue;
5950bbfda8aSnia		}
5960bbfda8aSnia		if(t->vs != tmp_win->vs) {
5970bbfda8aSnia			continue;
5980bbfda8aSnia		}
5990bbfda8aSnia		if(!t->mapped) {
6000bbfda8aSnia			continue;
6010bbfda8aSnia		}
6020bbfda8aSnia
6030bbfda8aSnia		w = t->frame_width  + 2 * t->frame_bw;
6040bbfda8aSnia		h = t->frame_height + 2 * t->frame_bw;
6050bbfda8aSnia		if(x >= t->frame_x + w) {
6060bbfda8aSnia			continue;
6070bbfda8aSnia		}
6080bbfda8aSnia		if(y >= t->frame_y + h) {
6090bbfda8aSnia			continue;
6100bbfda8aSnia		}
6110bbfda8aSnia		if(x + winw <= t->frame_x) {
6120bbfda8aSnia			continue;
6130bbfda8aSnia		}
6140bbfda8aSnia		if(y + winh <= t->frame_y) {
6150bbfda8aSnia			continue;
6160bbfda8aSnia		}
6170bbfda8aSnia
6180bbfda8aSnia		move = false;
6190bbfda8aSnia		if((dir == PD_ANY || dir == PD_LEFT) &&
6200bbfda8aSnia		                (x + Scr->MovePackResistance > t->frame_x + w)) {
6210bbfda8aSnia			newx = x - w;
6220bbfda8aSnia			newy = t->frame_y;
6230bbfda8aSnia			ndir = PD_LEFT;
6240bbfda8aSnia			move = true;
6250bbfda8aSnia		}
6260bbfda8aSnia		else if((dir == PD_ANY || dir == PD_RIGHT) &&
6270bbfda8aSnia		                (x + winw < t->frame_x + Scr->MovePackResistance)) {
6280bbfda8aSnia			newx = x + winw;
6290bbfda8aSnia			newy = t->frame_y;
6300bbfda8aSnia			ndir = PD_RIGHT;
6310bbfda8aSnia			move = true;
6320bbfda8aSnia		}
6330bbfda8aSnia		else if((dir == PD_ANY || dir == PD_TOP) &&
6340bbfda8aSnia		                (y + Scr->MovePackResistance > t->frame_y + h)) {
6350bbfda8aSnia			newx = t->frame_x;
6360bbfda8aSnia			newy = y - h;
6370bbfda8aSnia			ndir = PD_TOP;
6380bbfda8aSnia			move = true;
6390bbfda8aSnia		}
6400bbfda8aSnia		else if((dir == PD_ANY || dir == PD_BOTTOM) &&
6410bbfda8aSnia		                (y + winh < t->frame_y + Scr->MovePackResistance)) {
6420bbfda8aSnia			newx = t->frame_x;
6430bbfda8aSnia			newy = y + winh;
6440bbfda8aSnia			ndir = PD_BOTTOM;
6450bbfda8aSnia			move = true;
6460bbfda8aSnia		}
6470bbfda8aSnia		if(move) {
6480bbfda8aSnia			TryToPush_be(t, newx, newy, ndir);
6490bbfda8aSnia			TryToPack(t, &newx, &newy);
6500bbfda8aSnia			ConstrainByBorders(tmp_win,
6510bbfda8aSnia			                   &newx, t->frame_width  + 2 * t->frame_bw,
6520bbfda8aSnia			                   &newy, t->frame_height + 2 * t->frame_bw);
6530bbfda8aSnia			SetupWindow(t, newx, newy, t->frame_width, t->frame_height, -1);
6540bbfda8aSnia		}
6550bbfda8aSnia	}
6560bbfda8aSnia}
6570bbfda8aSnia
6580bbfda8aSnia
6590bbfda8aSniavoid
6600bbfda8aSniaTryToGrid(TwmWindow *tmp_win, int *x, int *y)
6610bbfda8aSnia{
6620bbfda8aSnia	int w    = tmp_win->frame_width  + 2 * tmp_win->frame_bw;
6630bbfda8aSnia	int h    = tmp_win->frame_height + 2 * tmp_win->frame_bw;
6640bbfda8aSnia	int grav = ((tmp_win->hints.flags & PWinGravity)
6650bbfda8aSnia	            ? tmp_win->hints.win_gravity : NorthWestGravity);
6660bbfda8aSnia
6670bbfda8aSnia	switch(grav) {
6680bbfda8aSnia		case ForgetGravity :
6690bbfda8aSnia		case StaticGravity :
6700bbfda8aSnia		case NorthWestGravity :
6710bbfda8aSnia		case NorthGravity :
6720bbfda8aSnia		case WestGravity :
6730bbfda8aSnia		case CenterGravity :
6740bbfda8aSnia			*x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
6750bbfda8aSnia			     + Scr->BorderLeft;
6760bbfda8aSnia			*y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid
6770bbfda8aSnia			     + Scr->BorderTop;
6780bbfda8aSnia			break;
6790bbfda8aSnia		case NorthEastGravity :
6800bbfda8aSnia		case EastGravity :
6810bbfda8aSnia			*x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
6820bbfda8aSnia			      Scr->XMoveGrid) - w + Scr->BorderLeft;
6830bbfda8aSnia			*y = ((*y - Scr->BorderTop) / Scr->YMoveGrid) *
6840bbfda8aSnia			     Scr->YMoveGrid + Scr->BorderTop;
6850bbfda8aSnia			break;
6860bbfda8aSnia		case SouthWestGravity :
6870bbfda8aSnia		case SouthGravity :
6880bbfda8aSnia			*x = ((*x - Scr->BorderLeft) / Scr->XMoveGrid) * Scr->XMoveGrid
6890bbfda8aSnia			     + Scr->BorderLeft;
6900bbfda8aSnia			*y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) * Scr->YMoveGrid)
6910bbfda8aSnia			     - h + Scr->BorderTop;
6920bbfda8aSnia			break;
6930bbfda8aSnia		case SouthEastGravity :
6940bbfda8aSnia			*x = (((*x + w - Scr->BorderLeft) / Scr->XMoveGrid) *
6950bbfda8aSnia			      Scr->XMoveGrid) - w + Scr->BorderLeft;
6960bbfda8aSnia			*y = (((*y + h - Scr->BorderTop) / Scr->YMoveGrid) *
6970bbfda8aSnia			      Scr->YMoveGrid) - h + Scr->BorderTop;
6980bbfda8aSnia			break;
6990bbfda8aSnia	}
7000bbfda8aSnia}
7010bbfda8aSnia
7020bbfda8aSnia
7030bbfda8aSnia
7040bbfda8aSnia/*
7050bbfda8aSnia * Functions related to keeping windows from being placed off-screen (or
7060bbfda8aSnia * off-screen too far).  Involved in handling of params like DontMoveOff
7070bbfda8aSnia * and MoveOffResistance, etc.
7080bbfda8aSnia */
7090bbfda8aSniastatic void ConstrainLeftTop(int *value, int border);
7100bbfda8aSniastatic void ConstrainRightBottom(int *value, int size1, int border, int size2);
7110bbfda8aSnia
7120bbfda8aSniavoid
7130bbfda8aSniaConstrainByBorders1(int *left, int width, int *top, int height)
7140bbfda8aSnia{
7150bbfda8aSnia	ConstrainRightBottom(left, width, Scr->BorderRight, Scr->rootw);
7160bbfda8aSnia	ConstrainLeftTop(left, Scr->BorderLeft);
7170bbfda8aSnia	ConstrainRightBottom(top, height, Scr->BorderBottom, Scr->rooth);
7180bbfda8aSnia	ConstrainLeftTop(top, Scr->BorderTop);
7190bbfda8aSnia}
7200bbfda8aSnia
7210bbfda8aSniavoid
7220bbfda8aSniaConstrainByBorders(TwmWindow *twmwin, int *left, int width,
7230bbfda8aSnia                   int *top, int height)
7240bbfda8aSnia{
7250bbfda8aSnia	if(twmwin->winbox) {
7260bbfda8aSnia		XWindowAttributes attr;
7270bbfda8aSnia		XGetWindowAttributes(dpy, twmwin->winbox->window, &attr);
7280bbfda8aSnia		ConstrainRightBottom(left, width, 0, attr.width);
7290bbfda8aSnia		ConstrainLeftTop(left, 0);
7300bbfda8aSnia		ConstrainRightBottom(top, height, 0, attr.height);
7310bbfda8aSnia		ConstrainLeftTop(top, 0);
7320bbfda8aSnia	}
7330bbfda8aSnia	else {
7340bbfda8aSnia		ConstrainByBorders1(left, width, top, height);
7350bbfda8aSnia	}
7360bbfda8aSnia}
7370bbfda8aSnia
7380bbfda8aSniastatic void
7390bbfda8aSniaConstrainLeftTop(int *value, int border)
7400bbfda8aSnia{
7410bbfda8aSnia	if(*value < border) {
7420bbfda8aSnia		if(Scr->MoveOffResistance < 0 ||
7430bbfda8aSnia		                *value > border - Scr->MoveOffResistance) {
7440bbfda8aSnia			*value = border;
7450bbfda8aSnia		}
7460bbfda8aSnia		else if(Scr->MoveOffResistance > 0 &&
7470bbfda8aSnia		                *value <= border - Scr->MoveOffResistance) {
7480bbfda8aSnia			*value = *value + Scr->MoveOffResistance;
7490bbfda8aSnia		}
7500bbfda8aSnia	}
7510bbfda8aSnia}
7520bbfda8aSnia
7530bbfda8aSniastatic void
7540bbfda8aSniaConstrainRightBottom(int *value, int size1, int border, int size2)
7550bbfda8aSnia{
7560bbfda8aSnia	if(*value + size1 > size2 - border) {
7570bbfda8aSnia		if(Scr->MoveOffResistance < 0 ||
7580bbfda8aSnia		                *value + size1 < size2 - border + Scr->MoveOffResistance) {
7590bbfda8aSnia			*value = size2 - size1 - border;
7600bbfda8aSnia		}
7610bbfda8aSnia		else if(Scr->MoveOffResistance > 0 &&
7620bbfda8aSnia		                *value + size1 >= size2 - border + Scr->MoveOffResistance) {
7630bbfda8aSnia			*value = *value - Scr->MoveOffResistance;
7640bbfda8aSnia		}
7650bbfda8aSnia	}
7660bbfda8aSnia}
7670bbfda8aSnia
7680bbfda8aSnia
7690bbfda8aSnia/*
7700bbfda8aSnia * Zoom over to a particular window.
7710bbfda8aSnia */
7720bbfda8aSniavoid
7730bbfda8aSniaWarpToWindow(TwmWindow *t, bool must_raise)
7740bbfda8aSnia{
7750bbfda8aSnia	int x, y;
7760bbfda8aSnia
7770bbfda8aSnia	if(t->ring.cursor_valid) {
7780bbfda8aSnia		x = t->ring.curs_x;
7790bbfda8aSnia		y = t->ring.curs_y;
7800bbfda8aSnia#ifdef DEBUG
7810bbfda8aSnia		fprintf(stderr, "WarpToWindow: cursor_valid; x == %d, y == %d\n", x, y);
7820bbfda8aSnia#endif
7830bbfda8aSnia
7840bbfda8aSnia		/*
7850bbfda8aSnia		 * XXX is this correct with 3D borders? Easier check possible?
7860bbfda8aSnia		 * frame_bw is for the left border.
7870bbfda8aSnia		 */
7880bbfda8aSnia		if(x < t->frame_bw) {
7890bbfda8aSnia			x = t->frame_bw;
7900bbfda8aSnia		}
7910bbfda8aSnia		if(x >= t->frame_width + t->frame_bw) {
7920bbfda8aSnia			x  = t->frame_width + t->frame_bw - 1;
7930bbfda8aSnia		}
7940bbfda8aSnia		if(y < t->title_height + t->frame_bw) {
7950bbfda8aSnia			y = t->title_height + t->frame_bw;
7960bbfda8aSnia		}
7970bbfda8aSnia		if(y >= t->frame_height + t->frame_bw) {
7980bbfda8aSnia			y  = t->frame_height + t->frame_bw - 1;
7990bbfda8aSnia		}
8000bbfda8aSnia#ifdef DEBUG
8010bbfda8aSnia		fprintf(stderr, "WarpToWindow: adjusted    ; x := %d, y := %d\n", x, y);
8020bbfda8aSnia#endif
8030bbfda8aSnia	}
8040bbfda8aSnia	else {
8050bbfda8aSnia		x = t->frame_width / 2;
8060bbfda8aSnia		y = t->frame_height / 2;
8070bbfda8aSnia#ifdef DEBUG
8080bbfda8aSnia		fprintf(stderr, "WarpToWindow: middle; x := %d, y := %d\n", x, y);
8090bbfda8aSnia#endif
8100bbfda8aSnia	}
8110bbfda8aSnia#if 0
8120bbfda8aSnia	int dest_x, dest_y;
8130bbfda8aSnia	Window child;
8140bbfda8aSnia
8150bbfda8aSnia	/*
8160bbfda8aSnia	 * Check if the proposed position actually is visible. If not, raise the window.
8170bbfda8aSnia	 * "If the coordinates are contained in a mapped
8180bbfda8aSnia	 * child of dest_w, that child is returned to child_return."
8190bbfda8aSnia	 * We'll need to check for the right child window; the frame probably.
8200bbfda8aSnia	 * (What about XXX window boxes?)
8210bbfda8aSnia	 *
8220bbfda8aSnia	 * Alternatively, use XQueryPointer() which returns the root window
8230bbfda8aSnia	 * the pointer is in, but XXX that won't work for VirtualScreens.
8240bbfda8aSnia	 */
8250bbfda8aSnia	if(XTranslateCoordinates(dpy, t->frame, Scr->Root, x, y, &dest_x, &dest_y,
8260bbfda8aSnia	                         &child)) {
8270bbfda8aSnia		if(child != t->frame) {
8280bbfda8aSnia			must_raise = true;
8290bbfda8aSnia		}
8300bbfda8aSnia	}
8310bbfda8aSnia#endif
8320bbfda8aSnia	if(t->auto_raise || must_raise) {
8330bbfda8aSnia		AutoRaiseWindow(t);
8340bbfda8aSnia	}
8350bbfda8aSnia	if(! visible(t)) {
8360bbfda8aSnia		WorkSpace *wlist;
8370bbfda8aSnia
8380bbfda8aSnia		for(wlist = Scr->workSpaceMgr.workSpaceList; wlist != NULL;
8390bbfda8aSnia		                wlist = wlist->next) {
8400bbfda8aSnia			if(OCCUPY(t, wlist)) {
8410bbfda8aSnia				break;
8420bbfda8aSnia			}
8430bbfda8aSnia		}
8440bbfda8aSnia		if(wlist != NULL) {
8450bbfda8aSnia			GotoWorkSpace(Scr->currentvs, wlist);
8460bbfda8aSnia		}
8470bbfda8aSnia	}
8480bbfda8aSnia
8490bbfda8aSnia	XWarpPointer(dpy, None, Scr->Root, 0, 0, 0, 0, x + t->frame_x, y + t->frame_y);
8500bbfda8aSnia	SetFocus(t, EventTime);
8510bbfda8aSnia
8520bbfda8aSnia#ifdef DEBUG
8530bbfda8aSnia	{
8540bbfda8aSnia		Window root_return;
8550bbfda8aSnia		Window child_return;
8560bbfda8aSnia		int root_x_return;
8570bbfda8aSnia		int root_y_return;
8580bbfda8aSnia		int win_x_return;
8590bbfda8aSnia		int win_y_return;
8600bbfda8aSnia		unsigned int mask_return;
8610bbfda8aSnia
8620bbfda8aSnia		if(XQueryPointer(dpy, t->frame, &root_return, &child_return, &root_x_return,
8630bbfda8aSnia		                 &root_y_return, &win_x_return, &win_y_return, &mask_return)) {
8640bbfda8aSnia			fprintf(stderr,
8650bbfda8aSnia			        "XQueryPointer: root_return=%x, child_return=%x, root_x_return=%d, root_y_return=%d, win_x_return=%d, win_y_return=%d\n",
8660bbfda8aSnia			        root_return, child_return, root_x_return, root_y_return, win_x_return,
8670bbfda8aSnia			        win_y_return);
8680bbfda8aSnia		}
8690bbfda8aSnia	}
8700bbfda8aSnia#endif
8710bbfda8aSnia}
8720bbfda8aSnia
8730bbfda8aSnia
8740bbfda8aSnia/*
8750bbfda8aSnia * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
8760bbfda8aSnia * client messages will have the following form:
8770bbfda8aSnia *
8780bbfda8aSnia *     event type       ClientMessage
8790bbfda8aSnia *     message type     XA_WM_PROTOCOLS
8800bbfda8aSnia *     window           tmp->w
8810bbfda8aSnia *     format           32
8820bbfda8aSnia *     data[0]          message atom
8830bbfda8aSnia *     data[1]          time stamp
8840bbfda8aSnia */
8850bbfda8aSniavoid
8860bbfda8aSniasend_clientmessage(Window w, Atom a, Time timestamp)
8870bbfda8aSnia{
8880bbfda8aSnia	XClientMessageEvent ev;
8890bbfda8aSnia
8900bbfda8aSnia	ev.type = ClientMessage;
8910bbfda8aSnia	ev.window = w;
8920bbfda8aSnia	ev.message_type = XA_WM_PROTOCOLS;
8930bbfda8aSnia	ev.format = 32;
8940bbfda8aSnia	ev.data.l[0] = a;
8950bbfda8aSnia	ev.data.l[1] = timestamp;
8960bbfda8aSnia	XSendEvent(dpy, w, False, 0L, (XEvent *) &ev);
8970bbfda8aSnia}
8980bbfda8aSnia
8990bbfda8aSnia
9000bbfda8aSnia/*
9010bbfda8aSnia * Create synthetic WM_HINTS info for windows.  When a window specifies
9020bbfda8aSnia * stuff, we should probably pay attention to it (though we don't
9030bbfda8aSnia * always; x-ref comments in AddWindow() especially about focus).
9040bbfda8aSnia * However, when it doesn't tell us anything at all, we should assume
9050bbfda8aSnia * something useful.  "Window managers are free to assume convenient
9060bbfda8aSnia * values for all fields of the WM_HINTS property if a window is mapped
9070bbfda8aSnia * without one."  (ICCCM Ch. 4,
9080bbfda8aSnia * <https://www.x.org/releases/X11R7.7/doc/xorg-docs/icccm/icccm.html#Client_Properties>).
9090bbfda8aSnia *
9100bbfda8aSnia * Specifically, we assume it wants us to give it focus.  It's fairly
9110bbfda8aSnia * bogus for a window not to tell us anything, but e.g current versions
9120bbfda8aSnia * of Chrome do (don't do) just that.  So we better make up something
9130bbfda8aSnia * useful.
9140bbfda8aSnia *
9150bbfda8aSnia * Should probably be some configurability for this, so make the func
9160bbfda8aSnia * take the window, even though we don't currently do anything useful
9170bbfda8aSnia * with it...
9180bbfda8aSnia */
9190bbfda8aSniaXWMHints *
9200bbfda8aSniagen_synthetic_wmhints(TwmWindow *win)
9210bbfda8aSnia{
9220bbfda8aSnia	XWMHints *hints;
9230bbfda8aSnia
9240bbfda8aSnia	hints = XAllocWMHints();
9250bbfda8aSnia	if(!hints) {
9260bbfda8aSnia		return NULL;
9270bbfda8aSnia	}
9280bbfda8aSnia
9290bbfda8aSnia	/*
9300bbfda8aSnia	 * Reasonable defaults.  Takes input, in normal state.
9310bbfda8aSnia	 *
9320bbfda8aSnia	 * XXX Make configurable?
9330bbfda8aSnia	 */
9340bbfda8aSnia	hints->flags = InputHint | StateHint;
9350bbfda8aSnia	hints->input = True;
9360bbfda8aSnia	hints->initial_state = NormalState;
9370bbfda8aSnia
9380bbfda8aSnia	return hints;
9390bbfda8aSnia}
9400bbfda8aSnia
9410bbfda8aSnia
9420bbfda8aSnia/**
9430bbfda8aSnia * Perform whatever adaptations of WM_HINTS info we do.
9440bbfda8aSnia *
9450bbfda8aSnia * Most of these relate to focus, but we also fiddle with group
9460bbfda8aSnia * membership.
9470bbfda8aSnia */
9480bbfda8aSniaXWMHints *
9490bbfda8aSniamunge_wmhints(TwmWindow *win, XWMHints *hints)
9500bbfda8aSnia{
9510bbfda8aSnia	/*
9520bbfda8aSnia	 * If we have WM_HINTS, but they don't tell us anything about focus,
9530bbfda8aSnia	 * force it to true for our purposes.
9540bbfda8aSnia	 *
9550bbfda8aSnia	 * CL: Having with not willing focus cause problems with AutoSqueeze
9560bbfda8aSnia	 * and a few others things. So I suppress it. And the whole focus
9570bbfda8aSnia	 * thing is buggy anyway.
9580bbfda8aSnia	 */
9590bbfda8aSnia	if(!(hints->flags & InputHint)) {
9600bbfda8aSnia		hints->input = True;
9610bbfda8aSnia	}
9620bbfda8aSnia
9630bbfda8aSnia	/*
9640bbfda8aSnia	 * Now we're expecting to give the window focus if it asked for it
9650bbfda8aSnia	 * via WM_HINTS, if it didn't say anything one way or the other in
9660bbfda8aSnia	 * WM_HINTS, or if it didn't give us any WM_HINTS at all.  But if it
9670bbfda8aSnia	 * explicitly asked not to, we don't give it unless overridden by
9680bbfda8aSnia	 * config.
9690bbfda8aSnia	 */
9700bbfda8aSnia	if(Scr->ForceFocus || IsInList(Scr->ForceFocusL, win)) {
9710bbfda8aSnia		hints->input = True;
9720bbfda8aSnia	}
9730bbfda8aSnia
9740bbfda8aSnia
9750bbfda8aSnia	/* Setup group bits */
9760bbfda8aSnia	if(hints->flags & WindowGroupHint) {
9770bbfda8aSnia		win->group = hints->window_group;
9780bbfda8aSnia		if(win->group) {
9790bbfda8aSnia			/*
9800bbfda8aSnia			 * GTK windows often have a spurious "group leader" window which is
9810bbfda8aSnia			 * never reported to us and therefore does not really exist.  This
9820bbfda8aSnia			 * is annoying because we treat group members a lot like transient
9830bbfda8aSnia			 * windows.  Look for that here. It is in fact a duplicate of the
9840bbfda8aSnia			 * WM_CLIENT_LEADER property.
9850bbfda8aSnia			 */
9860bbfda8aSnia			if(win->group != win->w && !GetTwmWindow(win->group)) {
9870bbfda8aSnia				win->group = 0;
9880bbfda8aSnia			}
9890bbfda8aSnia		}
9900bbfda8aSnia	}
9910bbfda8aSnia	else {
9920bbfda8aSnia		win->group = 0;
9930bbfda8aSnia	}
9940bbfda8aSnia
9950bbfda8aSnia	return hints;
9960bbfda8aSnia}
9970bbfda8aSnia
9980bbfda8aSnia
9990bbfda8aSnia/**
10000bbfda8aSnia * [Re]set a window's name.  This goes over the available naming sources
10010bbfda8aSnia * for the window and points the TwmWindow::name at the appropriate one.
10020bbfda8aSnia * It may also set a property to signal other EWMH-aware clients when
10030bbfda8aSnia * we're naming it a way they can't see themselves.
10040bbfda8aSnia *
10050bbfda8aSnia * \note This should rarely be called directly; apply_window_name()
10060bbfda8aSnia * should be used instead.  It's split out because we need to do this
10070bbfda8aSnia * step individually in AddWindow().
10080bbfda8aSnia *
10090bbfda8aSnia * \note Note also that we never need to worry about freeing the
10100bbfda8aSnia * TwmWindow::name; it always points to one of the TwmWindow::names
10110bbfda8aSnia * values (which are free'd by the event handler when they change) or to
10120bbfda8aSnia * NoName (which is static).  So we can just casually flip it around at
10130bbfda8aSnia * will.
10140bbfda8aSnia */
10150bbfda8aSniabool
10160bbfda8aSniaset_window_name(TwmWindow *win)
10170bbfda8aSnia{
10180bbfda8aSnia	char *newname = NULL;
10190bbfda8aSnia#define TRY(fld) { \
10200bbfda8aSnia                if(newname == NULL && win->names.fld != NULL) { \
10210bbfda8aSnia                        newname = win->names.fld; \
10220bbfda8aSnia                } \
10230bbfda8aSnia        }
10240bbfda8aSnia	TRY(ctwm_wm_name)
10250bbfda8aSnia#ifdef EWMH
10260bbfda8aSnia	TRY(net_wm_name)
10270bbfda8aSnia#endif
10280bbfda8aSnia	TRY(wm_name)
10290bbfda8aSnia#undef TRY
10300bbfda8aSnia
10310bbfda8aSnia	if(newname == NULL) {
10320bbfda8aSnia		newname = NoName;
10330bbfda8aSnia	}
10340bbfda8aSnia	if(win->name == newname) {
10350bbfda8aSnia		return false; // Nothing to do
10360bbfda8aSnia	}
10370bbfda8aSnia
10380bbfda8aSnia	// Now we know what to call it
10390bbfda8aSnia	win->name = newname;
10400bbfda8aSnia
10410bbfda8aSnia#ifdef EWMH
10420bbfda8aSnia	// EWMH says we set an additional property on any windows where what
10430bbfda8aSnia	// we consider the name isn't what's in _NET_WM_NAME, so pagers etc
10440bbfda8aSnia	// can call it the same as we do.
10450bbfda8aSnia	//
10460bbfda8aSnia	// The parts of the text describing it conflict a little; at one
10470bbfda8aSnia	// place, it implies this should be set unless we're using
10480bbfda8aSnia	// _NET_WM_NAME, in another it seems to suggest WM_NAME should be
10490bbfda8aSnia	// considered applicable too.  I choose to implement it excluding
10500bbfda8aSnia	// both, so this only gets set if we're overriding either standard
10510bbfda8aSnia	// naming (probably rare).
10520bbfda8aSnia	if(win->name != win->names.net_wm_name && win->name != win->names.wm_name) {
10530bbfda8aSnia		// XXX We're not doing any checking of the encoding here...  I
10540bbfda8aSnia		// don't see that Xlib helps us any, so we probably have to fall
10550bbfda8aSnia		// back to iconv?  That came into the base in POSIX 2008, but was
10560bbfda8aSnia		// in XSI back into the 90's I believe?
10570bbfda8aSnia		XChangeProperty(dpy, win->w, XA__NET_WM_VISIBLE_NAME, XA_UTF8_STRING,
10580bbfda8aSnia		                8, PropModeReplace, (unsigned char *)win->name,
10590bbfda8aSnia		                strlen(win->name));
10600bbfda8aSnia	}
10610bbfda8aSnia	else {
10620bbfda8aSnia		XDeleteProperty(dpy, win->w, XA__NET_WM_VISIBLE_NAME);
10630bbfda8aSnia	}
10640bbfda8aSnia#endif // EWMH
10650bbfda8aSnia
10660bbfda8aSnia	// We set a name
10670bbfda8aSnia	return true;
10680bbfda8aSnia}
10690bbfda8aSnia
10700bbfda8aSnia
10710bbfda8aSnia/**
10720bbfda8aSnia * [Re]set and apply changes to a window's name.  This is called after
10730bbfda8aSnia * we've received a new WM_NAME (or other name-setting) property, to
10740bbfda8aSnia * update our titlebars, icon managers, etc.
10750bbfda8aSnia */
10760bbfda8aSniavoid
10770bbfda8aSniaapply_window_name(TwmWindow *win)
10780bbfda8aSnia{
10790bbfda8aSnia	/* [Re]set ->name */
10800bbfda8aSnia	if(set_window_name(win) == false) {
10810bbfda8aSnia		// No change
10820bbfda8aSnia		return;
10830bbfda8aSnia	}
10840bbfda8aSnia	win->nameChanged = true;
10850bbfda8aSnia
10860bbfda8aSnia
10870bbfda8aSnia	/* Update the active name */
10880bbfda8aSnia	{
10890bbfda8aSnia		XRectangle inc_rect;
10900bbfda8aSnia		XRectangle logical_rect;
10910bbfda8aSnia
10920bbfda8aSnia		XmbTextExtents(Scr->TitleBarFont.font_set,
10930bbfda8aSnia		               win->name, strlen(win->name),
10940bbfda8aSnia		               &inc_rect, &logical_rect);
10950bbfda8aSnia		win->name_width = logical_rect.width;
10960bbfda8aSnia	}
10970bbfda8aSnia
10980bbfda8aSnia	/* recompute the priority if necessary */
10990bbfda8aSnia	if(Scr->AutoPriority) {
11000bbfda8aSnia		OtpRecomputePrefs(win);
11010bbfda8aSnia	}
11020bbfda8aSnia
11030bbfda8aSnia	SetupWindow(win, win->frame_x, win->frame_y,
11040bbfda8aSnia	            win->frame_width, win->frame_height, -1);
11050bbfda8aSnia
11060bbfda8aSnia	if(win->title_w) {
11070bbfda8aSnia		XClearArea(dpy, win->title_w, 0, 0, 0, 0, True);
11080bbfda8aSnia	}
11090bbfda8aSnia	if(Scr->AutoOccupy) {
11100bbfda8aSnia		WmgrRedoOccupation(win);
11110bbfda8aSnia	}
11120bbfda8aSnia
11130bbfda8aSnia#if 0
11140bbfda8aSnia	/* Experimental, not yet working. */
11150bbfda8aSnia	{
11160bbfda8aSnia		ColorPair cp;
11170bbfda8aSnia		int f, b;
11180bbfda8aSnia
11190bbfda8aSnia		f = GetColorFromList(Scr->TitleForegroundL, win->name,
11200bbfda8aSnia		                     &win->class, &cp.fore);
11210bbfda8aSnia		b = GetColorFromList(Scr->TitleBackgroundL, win->name,
11220bbfda8aSnia		                     &win->class, &cp.back);
11230bbfda8aSnia		if(f || b) {
11240bbfda8aSnia			if(Scr->use3Dtitles  && !Scr->BeNiceToColormap) {
11250bbfda8aSnia				GetShadeColors(&cp);
11260bbfda8aSnia			}
11270bbfda8aSnia			win->title = cp;
11280bbfda8aSnia		}
11290bbfda8aSnia		f = GetColorFromList(Scr->BorderColorL, win->name,
11300bbfda8aSnia		                     &win->class, &cp.fore);
11310bbfda8aSnia		b = GetColorFromList(Scr->BorderColorL, win->name,
11320bbfda8aSnia		                     &win->class, &cp.back);
11330bbfda8aSnia		if(f || b) {
11340bbfda8aSnia			if(Scr->use3Dborders && !Scr->BeNiceToColormap) {
11350bbfda8aSnia				GetShadeColors(&cp);
11360bbfda8aSnia			}
11370bbfda8aSnia			win->borderC = cp;
11380bbfda8aSnia		}
11390bbfda8aSnia
11400bbfda8aSnia		f = GetColorFromList(Scr->BorderTileForegroundL, win->name,
11410bbfda8aSnia		                     &win->class, &cp.fore);
11420bbfda8aSnia		b = GetColorFromList(Scr->BorderTileBackgroundL, win->name,
11430bbfda8aSnia		                     &win->class, &cp.back);
11440bbfda8aSnia		if(f || b) {
11450bbfda8aSnia			if(Scr->use3Dborders && !Scr->BeNiceToColormap) {
11460bbfda8aSnia				GetShadeColors(&cp);
11470bbfda8aSnia			}
11480bbfda8aSnia			win->border_tile = cp;
11490bbfda8aSnia		}
11500bbfda8aSnia	}
11510bbfda8aSnia#endif
11520bbfda8aSnia
11530bbfda8aSnia	/*
11540bbfda8aSnia	 * If we haven't set a separate icon name, we use the window name, so
11550bbfda8aSnia	 * we need to update it.
11560bbfda8aSnia	 */
11570bbfda8aSnia	if(win->names.icon_set == false) {
11580bbfda8aSnia		apply_window_icon_name(win);
11590bbfda8aSnia	}
11600bbfda8aSnia	AutoPopupMaybe(win);
11610bbfda8aSnia
11620bbfda8aSnia	return;
11630bbfda8aSnia}
11640bbfda8aSnia
11650bbfda8aSnia
11660bbfda8aSnia/**
11670bbfda8aSnia * [Re]set a window's icon name.  As with the window name version in
11680bbfda8aSnia * set_window_name(), this is mostly separate so the AddWindow() process
11690bbfda8aSnia * can call it.
11700bbfda8aSnia *
11710bbfda8aSnia * \note As with TwmWindow::name, we never want to try free()'ing or the
11720bbfda8aSnia * like TwmWindow::icon_name.
11730bbfda8aSnia *
11740bbfda8aSnia * \sa set_window_name() for details; this is just the icon name
11750bbfda8aSnia * equivalent of it.
11760bbfda8aSnia */
11770bbfda8aSniabool
11780bbfda8aSniaset_window_icon_name(TwmWindow *win)
11790bbfda8aSnia{
11800bbfda8aSnia	char *newname = NULL;
11810bbfda8aSnia#define TRY(fld) { \
11820bbfda8aSnia                if(newname == NULL && win->names.fld != NULL) { \
11830bbfda8aSnia                        newname = win->names.fld; \
11840bbfda8aSnia                        win->names.icon_set = true; \
11850bbfda8aSnia                } \
11860bbfda8aSnia        }
11870bbfda8aSnia	TRY(ctwm_wm_icon_name)
11880bbfda8aSnia#ifdef EWMH
11890bbfda8aSnia	TRY(net_wm_icon_name)
11900bbfda8aSnia#endif
11910bbfda8aSnia	TRY(wm_icon_name)
11920bbfda8aSnia#undef TRY
11930bbfda8aSnia
11940bbfda8aSnia	// Our fallback for icon names is the window name.  Flag when we're
11950bbfda8aSnia	// doing that, so the window name handler can know when it needs to
11960bbfda8aSnia	// call us.
11970bbfda8aSnia	if(newname == NULL) {
11980bbfda8aSnia		newname = win->name;
11990bbfda8aSnia		win->names.icon_set = false;
12000bbfda8aSnia	}
12010bbfda8aSnia	if(win->icon_name == newname) {
12020bbfda8aSnia		return false; // Nothing to do
12030bbfda8aSnia	}
12040bbfda8aSnia
12050bbfda8aSnia	// A name is chosen
12060bbfda8aSnia	win->icon_name = newname;
12070bbfda8aSnia
12080bbfda8aSnia#ifdef EWMH
12090bbfda8aSnia	// EWMH asks for _NET_WM_VISIBLE_ICON_NAME in various cases where
12100bbfda8aSnia	// we're not using 'standard' properties' values.  x-ref comments above in
12110bbfda8aSnia	// set_window_name() about the parallel property for the window name
12120bbfda8aSnia	// for various caveats.
12130bbfda8aSnia	if(win->icon_name != win->names.net_wm_icon_name
12140bbfda8aSnia	                && win->icon_name != win->names.wm_icon_name) {
12150bbfda8aSnia		// XXX Still encoding questionable; x-ref above.
12160bbfda8aSnia		XChangeProperty(dpy, win->w, XA__NET_WM_VISIBLE_ICON_NAME,
12170bbfda8aSnia		                XA_UTF8_STRING,
12180bbfda8aSnia		                8, PropModeReplace, (unsigned char *)win->icon_name,
12190bbfda8aSnia		                strlen(win->icon_name));
12200bbfda8aSnia	}
12210bbfda8aSnia	else {
12220bbfda8aSnia		XDeleteProperty(dpy, win->w, XA__NET_WM_VISIBLE_ICON_NAME);
12230bbfda8aSnia	}
12240bbfda8aSnia#endif // EWMH
12250bbfda8aSnia
12260bbfda8aSnia	// Did it
12270bbfda8aSnia	return true;
12280bbfda8aSnia}
12290bbfda8aSnia
12300bbfda8aSnia
12310bbfda8aSnia/**
12320bbfda8aSnia * [Re]set and apply changes to a window's icon name.  This is called
12330bbfda8aSnia * after we've received a new WM_ICON_NAME (or other name-setting)
12340bbfda8aSnia * property, to update our titlebars, icon managers, etc.
12350bbfda8aSnia *
12360bbfda8aSnia * \sa apply_window_name() which does the same for the window title.
12370bbfda8aSnia */
12380bbfda8aSniavoid
12390bbfda8aSniaapply_window_icon_name(TwmWindow *win)
12400bbfda8aSnia{
12410bbfda8aSnia	/* [Re]set ->icon_name */
12420bbfda8aSnia	if(set_window_icon_name(win) == false) {
12430bbfda8aSnia		// No change
12440bbfda8aSnia		return;
12450bbfda8aSnia	}
12460bbfda8aSnia
12470bbfda8aSnia
12480bbfda8aSnia	/* Lot less to do for icons... */
12490bbfda8aSnia	RedoIcon(Tmp_win);
12500bbfda8aSnia	AutoPopupMaybe(Tmp_win);
12510bbfda8aSnia
12520bbfda8aSnia	return;
12530bbfda8aSnia}
1254