10bbfda8aSnia/*
20bbfda8aSnia * Copyright 2014 Olaf Seibert
30bbfda8aSnia */
40bbfda8aSnia
50bbfda8aSnia/*
60bbfda8aSnia * Implements some of the Extended Window Manager Hints, as (extremely
70bbfda8aSnia * poorly) documented at
80bbfda8aSnia * http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html .
90bbfda8aSnia * In fact, the wiki page that refers to that as being the current version
100bbfda8aSnia * (http://www.freedesktop.org/wiki/Specifications/wm-spec/)
110bbfda8aSnia * neglects to tell us there are newer versions 1.4 and 1.5 at
120bbfda8aSnia * http://standards.freedesktop.org/wm-spec/wm-spec-1.5.html
130bbfda8aSnia * (which has a listable directory that I discovered by accident).
140bbfda8aSnia * The same wiki page also has lots of dead links to a CVS repository.
150bbfda8aSnia * Nevertheless, it is where one ends up if one starts at
160bbfda8aSnia * http://www.freedesktop.org/wiki/Specifications/ .
170bbfda8aSnia *
180bbfda8aSnia * EWMH is an extension to the ICCCM (Inter-Client Communication
190bbfda8aSnia * Conventions Manual).
200bbfda8aSnia * http://tronche.com/gui/x/icccm/
210bbfda8aSnia *
220bbfda8aSnia * To fill in lots of details, the source code of other window managers
230bbfda8aSnia * has been consulted.
240bbfda8aSnia */
250bbfda8aSnia
260bbfda8aSnia#include "ctwm.h"
270bbfda8aSnia
280bbfda8aSnia#include <stdio.h>
290bbfda8aSnia#include <stdlib.h>
300bbfda8aSnia#include <time.h>
310bbfda8aSnia#include <stdint.h>
320bbfda8aSnia#include <assert.h>
330bbfda8aSnia
340bbfda8aSnia#include <X11/Xatom.h>
350bbfda8aSnia#include <X11/extensions/shape.h>
360bbfda8aSnia
370bbfda8aSnia#include "ctwm_atoms.h"
38b18c2d1eSnia#include "ctwm_shutdown.h"
390bbfda8aSnia#include "ewmh_atoms.h"
400bbfda8aSnia#include "screen.h"
410bbfda8aSnia#include "events.h"
420bbfda8aSnia#include "event_handlers.h"
430bbfda8aSnia#include "functions_defs.h"
440bbfda8aSnia#include "icons.h"
450bbfda8aSnia#include "otp.h"
460bbfda8aSnia#include "image.h"
470bbfda8aSnia#include "list.h"
480bbfda8aSnia#include "functions.h"
490bbfda8aSnia#include "occupation.h"
50b18c2d1eSnia#include "r_layout.h"
51b18c2d1eSnia#include "util.h"
520bbfda8aSnia#include "vscreen.h"
530bbfda8aSnia#include "win_iconify.h"
540bbfda8aSnia#include "win_ops.h"
550bbfda8aSnia#include "win_resize.h"
560bbfda8aSnia#include "win_utils.h"
570bbfda8aSnia#include "workspace_utils.h"
580bbfda8aSnia
590bbfda8aSnia/* #define DEBUG_EWMH */
600bbfda8aSnia
610bbfda8aSniaAtom XEWMHAtom[NUM_EWMH_XATOMS];
620bbfda8aSnia
630bbfda8aSnia#define NET_WM_STATE_REMOVE        0    /* remove/unset property */
640bbfda8aSnia#define NET_WM_STATE_ADD           1    /* add/set property */
650bbfda8aSnia#define NET_WM_STATE_TOGGLE        2    /* toggle property  */
660bbfda8aSnia
670bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
680bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_TOP          1
690bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
700bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
710bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
720bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
730bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
740bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_LEFT         7
750bbfda8aSnia#define _NET_WM_MOVERESIZE_MOVE              8   /* movement only */
760bbfda8aSnia#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9   /* size via keyboard */
770bbfda8aSnia#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10   /* move via keyboard */
780bbfda8aSnia#define _NET_WM_MOVERESIZE_CANCEL           11   /* cancel operation */
790bbfda8aSnia
800bbfda8aSniastatic Image *ExtractIcon(ScreenInfo *scr, unsigned long *prop, int width,
810bbfda8aSnia                          int height);
820bbfda8aSniastatic void EwmhClientMessage_NET_WM_DESKTOP(XClientMessageEvent *msg);
830bbfda8aSniastatic void EwmhClientMessage_NET_WM_STATE(XClientMessageEvent *msg);
840bbfda8aSniastatic void EwmhClientMessage_NET_ACTIVE_WINDOW(XClientMessageEvent *msg);
850bbfda8aSniastatic void EwmhClientMessage_NET_WM_MOVERESIZE(XClientMessageEvent *msg);
86b18c2d1eSniastatic void EwmhClientMessage_NET_CLOSE_WINDOW(XClientMessageEvent *msg);
870bbfda8aSniastatic XEvent synth_btnevent_for_moveresize(TwmWindow *twm_win);
880bbfda8aSniastatic unsigned long EwmhGetWindowProperty(Window w, Atom name, Atom type);
890bbfda8aSniastatic void EwmhGetStrut(TwmWindow *twm_win, bool update);
900bbfda8aSniastatic void EwmhRemoveStrut(TwmWindow *twm_win);
910bbfda8aSniastatic void EwmhSet_NET_WORKAREA(ScreenInfo *scr);
920bbfda8aSniastatic int EwmhGet_NET_WM_STATE(TwmWindow *twm_win);
930bbfda8aSniastatic void EwmhClientMessage_NET_WM_STATEchange(TwmWindow *twm_win, int change,
940bbfda8aSnia                int newVal);
950bbfda8aSnia
960bbfda8aSnia#define ALL_WORKSPACES  0xFFFFFFFFU
970bbfda8aSnia
980bbfda8aSniastatic void SendPropertyMessage(Window to, Window about,
990bbfda8aSnia                                Atom messagetype,
1000bbfda8aSnia                                long l0, long l1, long l2, long l3, long l4,
1010bbfda8aSnia                                long mask)
1020bbfda8aSnia{
1030bbfda8aSnia	XEvent e;
1040bbfda8aSnia
1050bbfda8aSnia	e.xclient.type = ClientMessage;
1060bbfda8aSnia	e.xclient.message_type = messagetype;
1070bbfda8aSnia	e.xclient.display = dpy;
1080bbfda8aSnia	e.xclient.window = about;
1090bbfda8aSnia	e.xclient.format = 32;
1100bbfda8aSnia	e.xclient.data.l[0] = l0;
1110bbfda8aSnia	e.xclient.data.l[1] = l1;
1120bbfda8aSnia	e.xclient.data.l[2] = l2;
1130bbfda8aSnia	e.xclient.data.l[3] = l3;
1140bbfda8aSnia	e.xclient.data.l[4] = l4;
1150bbfda8aSnia
1160bbfda8aSnia	XSendEvent(dpy, to, False, mask, &e);
1170bbfda8aSnia}
1180bbfda8aSnia
1190bbfda8aSniastatic void EwmhInitAtoms(void)
1200bbfda8aSnia{
1210bbfda8aSnia	XInternAtoms(dpy, XEWMHAtomNames, NUM_EWMH_XATOMS, False, XEWMHAtom);
1220bbfda8aSnia}
1230bbfda8aSnia
1240bbfda8aSniastatic bool caughtError;
1250bbfda8aSnia
1260bbfda8aSniastatic int CatchError(Display *display, XErrorEvent *event)
1270bbfda8aSnia{
1280bbfda8aSnia	caughtError = true;
1290bbfda8aSnia	return 0;
1300bbfda8aSnia}
1310bbfda8aSnia
1320bbfda8aSniavoid EwmhInit(void)
1330bbfda8aSnia{
1340bbfda8aSnia	EwmhInitAtoms();
1350bbfda8aSnia}
1360bbfda8aSnia
1370bbfda8aSnia/*
1380bbfda8aSnia * Force-generate some event, so that we know the current time.
1390bbfda8aSnia *
1400bbfda8aSnia * Suggested in the ICCCM:
1410bbfda8aSnia * http://tronche.com/gui/x/icccm/sec-2.html#s-2.1
1420bbfda8aSnia */
1430bbfda8aSnia
1440bbfda8aSniastatic void GenerateTimestamp(ScreenInfo *scr)
1450bbfda8aSnia{
1460bbfda8aSnia	XEvent event;
1470bbfda8aSnia	struct timespec tosleep;
1480bbfda8aSnia	int timeout = 200;          /* 0.2 seconds in ms */
1490bbfda8aSnia	int found;
1500bbfda8aSnia
1510bbfda8aSnia	/* Sleep in 10ms chunks */
1520bbfda8aSnia	tosleep.tv_sec  = 0;
1530bbfda8aSnia	tosleep.tv_nsec = (10 * 1000 * 1000);
1540bbfda8aSnia
1550bbfda8aSnia	if(EventTime > 0) {
1560bbfda8aSnia		return;
1570bbfda8aSnia	}
1580bbfda8aSnia
1590bbfda8aSnia	XChangeProperty(dpy, scr->icccm_Window,
1600bbfda8aSnia	                XA_WM_CLASS, XA_STRING,
1610bbfda8aSnia	                8, PropModeAppend, NULL, 0);
1620bbfda8aSnia
1630bbfda8aSnia	while(timeout > 0) {
1640bbfda8aSnia		found = XCheckTypedWindowEvent(dpy, scr->icccm_Window, PropertyNotify, &event);
1650bbfda8aSnia		if(found) {
1660bbfda8aSnia			break;
1670bbfda8aSnia		}
1680bbfda8aSnia		nanosleep(&tosleep, NULL);
1690bbfda8aSnia		timeout -= 10;
1700bbfda8aSnia	}
1710bbfda8aSnia
1720bbfda8aSnia	if(found) {
1730bbfda8aSnia#ifdef DEBUG_EWMH
1740bbfda8aSnia		fprintf(stderr, "GenerateTimestamp: time = %ld, timeout left = %d\n",
1750bbfda8aSnia		        event.xproperty.time, timeout);
1760bbfda8aSnia#endif /* DEBUG_EWMH */
1770bbfda8aSnia		if(EventTime < event.xproperty.time) {
1780bbfda8aSnia			EventTime = event.xproperty.time;
1790bbfda8aSnia		}
1800bbfda8aSnia	}
1810bbfda8aSnia}
1820bbfda8aSnia
1830bbfda8aSnia/*
1840bbfda8aSnia * Perform the "replace the window manager" protocol, as vaguely hinted
1850bbfda8aSnia * at by the ICCCM section 4.3.
1860bbfda8aSnia * http://tronche.com/gui/x/icccm/sec-4.html#s-4.3
1870bbfda8aSnia *
1880bbfda8aSnia * We do want to run through this even if we're not running with
1890bbfda8aSnia * --replace ourselves, because it also sets up the bits for other
1900bbfda8aSnia * invocations to --replace us.
1910bbfda8aSnia *
1920bbfda8aSnia * TODO: convert the selection to atom VERSION.
1930bbfda8aSnia */
1940bbfda8aSniastatic bool EwmhReplaceWM(ScreenInfo *scr)
1950bbfda8aSnia{
1960bbfda8aSnia	char atomname[32];
1970bbfda8aSnia	Atom wmAtom;
1980bbfda8aSnia	Window selectionOwner;
1990bbfda8aSnia
2000bbfda8aSnia	snprintf(atomname, sizeof(atomname), "WM_S%d", scr->screen);
2010bbfda8aSnia	wmAtom = XInternAtom(dpy, atomname, False);
2020bbfda8aSnia
2030bbfda8aSnia	selectionOwner = XGetSelectionOwner(dpy, wmAtom);
2040bbfda8aSnia	if(selectionOwner == scr->icccm_Window) {
2050bbfda8aSnia		selectionOwner = None;
2060bbfda8aSnia	}
2070bbfda8aSnia
2080bbfda8aSnia	if(selectionOwner != None) {
2090bbfda8aSnia		XErrorHandler oldHandler;
2100bbfda8aSnia
2110bbfda8aSnia		/*
2120bbfda8aSnia		 * Check if that owner still exists, and if it does, we want
2130bbfda8aSnia		 * StructureNotify-kind events from it.
2140bbfda8aSnia		 */
2150bbfda8aSnia		caughtError = false;
2160bbfda8aSnia		oldHandler = XSetErrorHandler(CatchError);
2170bbfda8aSnia
2180bbfda8aSnia		XSelectInput(dpy, selectionOwner, StructureNotifyMask);
2190bbfda8aSnia		XSync(dpy, False);
2200bbfda8aSnia
2210bbfda8aSnia		XSetErrorHandler(oldHandler);
2220bbfda8aSnia
2230bbfda8aSnia		if(caughtError) {
2240bbfda8aSnia			selectionOwner = None;
2250bbfda8aSnia		}
2260bbfda8aSnia	}
2270bbfda8aSnia
2280bbfda8aSnia	/*
2290bbfda8aSnia	 * If something else is owning things and we're not running
2300bbfda8aSnia	 * --replace, give up now and return failure.
2310bbfda8aSnia	 */
2320bbfda8aSnia	if(selectionOwner != None && !CLarg.ewmh_replace) {
2330bbfda8aSnia#ifdef DEBUG_EWMH
2340bbfda8aSnia		fprintf(stderr, "A window manager is already running on screen %d\n",
2350bbfda8aSnia		        scr->screen);
2360bbfda8aSnia#endif
2370bbfda8aSnia		return false;
2380bbfda8aSnia	}
2390bbfda8aSnia
2400bbfda8aSnia	/* We're asked to --replace, so try to do so */
2410bbfda8aSnia	XSetSelectionOwner(dpy, wmAtom, scr->icccm_Window, CurrentTime);
2420bbfda8aSnia
2430bbfda8aSnia	if(XGetSelectionOwner(dpy, wmAtom) != scr->icccm_Window) {
2440bbfda8aSnia		fprintf(stderr, "Did not get window manager selection on screen %d\n",
2450bbfda8aSnia		        scr->screen);
2460bbfda8aSnia		return false;
2470bbfda8aSnia	}
2480bbfda8aSnia
2490bbfda8aSnia	/*
2500bbfda8aSnia	 * If there was a previous selection owner, wait for it
2510bbfda8aSnia	 * to go away.
2520bbfda8aSnia	 */
2530bbfda8aSnia
2540bbfda8aSnia	if(selectionOwner != None) {
2550bbfda8aSnia		int timeout = 10 * 1000;        /* 10 seconds in ms */
2560bbfda8aSnia		XEvent event;
2570bbfda8aSnia		struct timespec tosleep;
2580bbfda8aSnia
2590bbfda8aSnia		/* Sleep in 100ms chunks */
2600bbfda8aSnia		tosleep.tv_sec  = 0;
2610bbfda8aSnia		tosleep.tv_nsec = (100 * 1000 * 1000);
2620bbfda8aSnia
2630bbfda8aSnia		while(timeout > 0) {
2640bbfda8aSnia
2650bbfda8aSnia			int found = XCheckTypedWindowEvent(dpy, selectionOwner, DestroyNotify, &event);
2660bbfda8aSnia			if(found) {
2670bbfda8aSnia				break;
2680bbfda8aSnia			}
2690bbfda8aSnia			nanosleep(&tosleep, NULL);
2700bbfda8aSnia			timeout -= 100;
2710bbfda8aSnia		}
2720bbfda8aSnia
2730bbfda8aSnia		if(timeout <= 0) {
2740bbfda8aSnia			fprintf(stderr, "Timed out waiting for other window manager "
2750bbfda8aSnia			        "on screen %d to quit\n",
2760bbfda8aSnia			        scr->screen);
2770bbfda8aSnia			return false;
2780bbfda8aSnia		}
2790bbfda8aSnia	}
2800bbfda8aSnia
2810bbfda8aSnia	/*
2820bbfda8aSnia	 * Send a message to confirm we're now managing the screen.
2830bbfda8aSnia	 * ICCCM, end of chapter 2, section "Manager Selections".
2840bbfda8aSnia	 * http://tronche.com/gui/x/icccm/sec-2.html#s-2.8
2850bbfda8aSnia	 *
2860bbfda8aSnia	 * ICCCM says StructureNotifyMask,
2870bbfda8aSnia	 * OpenBox says SubstructureNotifyMask.
2880bbfda8aSnia	 */
2890bbfda8aSnia
2900bbfda8aSnia	GenerateTimestamp(scr);
2910bbfda8aSnia
2920bbfda8aSnia	SendPropertyMessage(scr->XineramaRoot, scr->XineramaRoot,
2930bbfda8aSnia	                    XA_MANAGER, EventTime, wmAtom, scr->icccm_Window, 0, 0,
2940bbfda8aSnia	                    StructureNotifyMask);
2950bbfda8aSnia
2960bbfda8aSnia	return true;
2970bbfda8aSnia}
2980bbfda8aSnia
2990bbfda8aSnia/*
3000bbfda8aSnia * This function is called very early in initialisation.
3010bbfda8aSnia *
3020bbfda8aSnia * Only scr->screen and scr->XineramaRoot are valid: we want to know if
3030bbfda8aSnia * it makes sense to continue with the full initialisation.
3040bbfda8aSnia *
3050bbfda8aSnia * Create the ICCCM window that owns the WM_Sn selection.
3060bbfda8aSnia */
3070bbfda8aSniabool EwmhInitScreenEarly(ScreenInfo *scr)
3080bbfda8aSnia{
3090bbfda8aSnia	XSetWindowAttributes attrib;
3100bbfda8aSnia
3110bbfda8aSnia#ifdef DEBUG_EWMH
3120bbfda8aSnia	fprintf(stderr, "EwmhInitScreenEarly: XCreateWindow\n");
3130bbfda8aSnia#endif
3140bbfda8aSnia	attrib.event_mask = PropertyChangeMask;
3150bbfda8aSnia	attrib.override_redirect = True;
3160bbfda8aSnia	scr->icccm_Window = XCreateWindow(dpy, scr->XineramaRoot,
3170bbfda8aSnia	                                  -100, -100, 1, 1, 0,
3180bbfda8aSnia	                                  CopyFromParent, InputOutput,
3190bbfda8aSnia	                                  CopyFromParent,
3200bbfda8aSnia	                                  CWEventMask | CWOverrideRedirect,
3210bbfda8aSnia	                                  &attrib);
3220bbfda8aSnia
3230bbfda8aSnia	XMapWindow(dpy, scr->icccm_Window);
3240bbfda8aSnia	XLowerWindow(dpy, scr->icccm_Window);
3250bbfda8aSnia
3260bbfda8aSnia#ifdef DEBUG_EWMH
3270bbfda8aSnia	fprintf(stderr, "EwmhInitScreenEarly: call EwmhReplaceWM\n");
3280bbfda8aSnia#endif
3290bbfda8aSnia	if(!EwmhReplaceWM(scr)) {
3300bbfda8aSnia		XDestroyWindow(dpy, scr->icccm_Window);
3310bbfda8aSnia		scr->icccm_Window = None;
3320bbfda8aSnia
3330bbfda8aSnia#ifdef DEBUG_EWMH
3340bbfda8aSnia		fprintf(stderr, "EwmhInitScreenEarly: return false\n");
3350bbfda8aSnia#endif
3360bbfda8aSnia		return false;
3370bbfda8aSnia	}
3380bbfda8aSnia
3390bbfda8aSnia#ifdef DEBUG_EWMH
3400bbfda8aSnia	fprintf(stderr, "EwmhInitScreenEarly: return true\n");
3410bbfda8aSnia#endif
3420bbfda8aSnia	return true;
3430bbfda8aSnia}
3440bbfda8aSnia
3450bbfda8aSnia/*
3460bbfda8aSnia * This initialisation is called late, when scr has been set up
3470bbfda8aSnia * completely.
3480bbfda8aSnia */
3490bbfda8aSniavoid EwmhInitScreenLate(ScreenInfo *scr)
3500bbfda8aSnia{
3510bbfda8aSnia	long data[2];
3520bbfda8aSnia
3530bbfda8aSnia	/* Set _NET_SUPPORTING_WM_CHECK on root window */
3540bbfda8aSnia	data[0] = scr->icccm_Window;
3550bbfda8aSnia	XChangeProperty(dpy, scr->XineramaRoot,
3560bbfda8aSnia	                XA__NET_SUPPORTING_WM_CHECK, XA_WINDOW,
3570bbfda8aSnia	                32, PropModeReplace,
3580bbfda8aSnia	                (unsigned char *)data, 1);
3590bbfda8aSnia
3600bbfda8aSnia	/*
3610bbfda8aSnia	 * Set properties on the window;
3620bbfda8aSnia	 * this also belongs with _NET_SUPPORTING_WM_CHECK
3630bbfda8aSnia	 */
3640bbfda8aSnia	XChangeProperty(dpy, scr->icccm_Window,
3650bbfda8aSnia	                XA__NET_WM_NAME, XA_UTF8_STRING,
3660bbfda8aSnia	                8, PropModeReplace,
3670bbfda8aSnia	                (unsigned char *)"ctwm", 4);
3680bbfda8aSnia
3690bbfda8aSnia	data[0] = scr->icccm_Window;
3700bbfda8aSnia	XChangeProperty(dpy, scr->icccm_Window,
3710bbfda8aSnia	                XA__NET_SUPPORTING_WM_CHECK, XA_WINDOW,
3720bbfda8aSnia	                32, PropModeReplace,
3730bbfda8aSnia	                (unsigned char *)data, 1);
3740bbfda8aSnia
3750bbfda8aSnia	/*
3760bbfda8aSnia	 * Add supported properties to the root window.
3770bbfda8aSnia	 */
3780bbfda8aSnia	data[0] = 0;
3790bbfda8aSnia	data[1] = 0;
3800bbfda8aSnia	XChangeProperty(dpy, scr->XineramaRoot,
3810bbfda8aSnia	                XA__NET_DESKTOP_VIEWPORT, XA_CARDINAL,
3820bbfda8aSnia	                32, PropModeReplace,
3830bbfda8aSnia	                (unsigned char *)data, 2);
3840bbfda8aSnia
3850bbfda8aSnia	data[0] = scr->rootw;
3860bbfda8aSnia	data[1] = scr->rooth;
3870bbfda8aSnia	XChangeProperty(dpy, scr->XineramaRoot,
3880bbfda8aSnia	                XA__NET_DESKTOP_GEOMETRY, XA_CARDINAL,
3890bbfda8aSnia	                32, PropModeReplace,
3900bbfda8aSnia	                (unsigned char *)data, 2);
3910bbfda8aSnia
3920bbfda8aSnia	EwmhSet_NET_WORKAREA(scr);
3930bbfda8aSnia
3940bbfda8aSnia	if(scr->workSpaceManagerActive) {
3950bbfda8aSnia		data[0] = scr->workSpaceMgr.count;
3960bbfda8aSnia	}
3970bbfda8aSnia	else {
3980bbfda8aSnia		data[0] = 1;
3990bbfda8aSnia	}
4000bbfda8aSnia
4010bbfda8aSnia	XChangeProperty(dpy, scr->XineramaRoot,
4020bbfda8aSnia	                XA__NET_NUMBER_OF_DESKTOPS, XA_CARDINAL,
4030bbfda8aSnia	                32, PropModeReplace,
4040bbfda8aSnia	                (unsigned char *)data, 1);
4050bbfda8aSnia
4060bbfda8aSnia	if(scr->workSpaceManagerActive) {
4070bbfda8aSnia		/* TODO: this is for the first Virtual Screen only... */
4080bbfda8aSnia		/*data[0] = scr->workSpaceMgr.workSpaceWindowList->currentwspc->number; */
4090bbfda8aSnia		data[0] = 0;
4100bbfda8aSnia	}
4110bbfda8aSnia	else {
4120bbfda8aSnia		data[0] = 0;
4130bbfda8aSnia	}
4140bbfda8aSnia	XChangeProperty(dpy, scr->XineramaRoot,
4150bbfda8aSnia	                XA__NET_CURRENT_DESKTOP, XA_CARDINAL,
4160bbfda8aSnia	                32, PropModeReplace,
4170bbfda8aSnia	                (unsigned char *)data, 1);
4180bbfda8aSnia
4190bbfda8aSnia	EwmhSet_NET_SHOWING_DESKTOP(0);
4200bbfda8aSnia
4210bbfda8aSnia	long supported[30];
4220bbfda8aSnia	int i = 0;
4230bbfda8aSnia
4240bbfda8aSnia	supported[i++] = XA__NET_SUPPORTING_WM_CHECK;
4250bbfda8aSnia	supported[i++] = XA__NET_DESKTOP_VIEWPORT;
4260bbfda8aSnia	supported[i++] = XA__NET_NUMBER_OF_DESKTOPS;
4270bbfda8aSnia	supported[i++] = XA__NET_CURRENT_DESKTOP;
4280bbfda8aSnia	supported[i++] = XA__NET_DESKTOP_GEOMETRY;
4290bbfda8aSnia	supported[i++] = XA__NET_WM_ICON;
4300bbfda8aSnia	supported[i++] = XA__NET_WM_DESKTOP;
4310bbfda8aSnia	supported[i++] = XA__NET_CLIENT_LIST;
4320bbfda8aSnia	supported[i++] = XA__NET_CLIENT_LIST_STACKING;
4330bbfda8aSnia	supported[i++] = XA__NET_WM_WINDOW_TYPE;
4340bbfda8aSnia	supported[i++] = XA__NET_WM_WINDOW_TYPE_NORMAL;
4350bbfda8aSnia	supported[i++] = XA__NET_WM_WINDOW_TYPE_DESKTOP;
4360bbfda8aSnia	supported[i++] = XA__NET_WM_WINDOW_TYPE_DOCK;
4370bbfda8aSnia	supported[i++] = XA__NET_WM_STRUT;
4380bbfda8aSnia	supported[i++] = XA__NET_WM_STRUT_PARTIAL;
4390bbfda8aSnia	supported[i++] = XA__NET_SHOWING_DESKTOP;
4400bbfda8aSnia	supported[i++] = XA__NET_WM_STATE;
4410bbfda8aSnia	supported[i++] = XA__NET_WM_STATE_MAXIMIZED_VERT;
4420bbfda8aSnia	supported[i++] = XA__NET_WM_STATE_MAXIMIZED_HORZ;
4430bbfda8aSnia	supported[i++] = XA__NET_WM_STATE_FULLSCREEN;
4440bbfda8aSnia	supported[i++] = XA__NET_ACTIVE_WINDOW;
4450bbfda8aSnia	supported[i++] = XA__NET_WORKAREA;
4460bbfda8aSnia	supported[i++] = XA__NET_WM_MOVERESIZE;
4470bbfda8aSnia	supported[i++] = XA__NET_WM_STATE_SHADED;
4480bbfda8aSnia	supported[i++] = XA__NET_WM_STATE_ABOVE;
4490bbfda8aSnia	supported[i++] = XA__NET_WM_STATE_BELOW;
450b18c2d1eSnia	supported[i++] = XA__NET_CLOSE_WINDOW;
4510bbfda8aSnia
4520bbfda8aSnia	XChangeProperty(dpy, scr->XineramaRoot,
4530bbfda8aSnia	                XA__NET_SUPPORTED, XA_ATOM,
4540bbfda8aSnia	                32, PropModeReplace,
4550bbfda8aSnia	                (unsigned char *)supported, i);
4560bbfda8aSnia}
4570bbfda8aSnia
458b18c2d1eSnia
459b18c2d1eSnia#ifdef VSCREEN
4600bbfda8aSnia/*
4610bbfda8aSnia * Set up the _NET_VIRTUAL_ROOTS property, which indicates that we're
4620bbfda8aSnia * using virtual root windows.
4630bbfda8aSnia * This applies only if we have multiple virtual screens.
4640bbfda8aSnia *
4650bbfda8aSnia * Also record this as a supported atom in _NET_SUPPORTED.
4660bbfda8aSnia *
4670bbfda8aSnia * Really, our virtual screens (with their virtual root windows) don't quite
4680bbfda8aSnia * fit in the EWMH idiom. Several root window properties (such as
4690bbfda8aSnia * _NET_CURRENT_DESKTOP) are more appropriate on the virtual root windows. But
4700bbfda8aSnia * that is not where other clients would look for them.
4710bbfda8aSnia *
4720bbfda8aSnia * The idea seems to be that the virtual roots as used for workspaces (desktops
4730bbfda8aSnia * in EWMH terminology) are only mapped one at a time.
4740bbfda8aSnia */
4750bbfda8aSniavoid EwmhInitVirtualRoots(ScreenInfo *scr)
4760bbfda8aSnia{
4770bbfda8aSnia	int numVscreens = scr->numVscreens;
4780bbfda8aSnia
4790bbfda8aSnia	if(numVscreens > 1) {
4800bbfda8aSnia		long *data;
4810bbfda8aSnia		long d0;
4820bbfda8aSnia		VirtualScreen *vs;
4830bbfda8aSnia		int i;
4840bbfda8aSnia
4850bbfda8aSnia		data = calloc(numVscreens, sizeof(long));
4860bbfda8aSnia
4870bbfda8aSnia		for(vs = scr->vScreenList, i = 0;
4880bbfda8aSnia		                vs != NULL && i < numVscreens;
4890bbfda8aSnia		                vs = vs->next, i++) {
4900bbfda8aSnia			data[i] = vs->window;
4910bbfda8aSnia		}
4920bbfda8aSnia
4930bbfda8aSnia		XChangeProperty(dpy, scr->XineramaRoot,
4940bbfda8aSnia		                XA__NET_VIRTUAL_ROOTS, XA_WINDOW,
4950bbfda8aSnia		                32, PropModeReplace,
4960bbfda8aSnia		                (unsigned char *)data, numVscreens);
4970bbfda8aSnia
4980bbfda8aSnia		/* This might fail, but what can we do about it? */
4990bbfda8aSnia
5000bbfda8aSnia		free(data);
5010bbfda8aSnia
5020bbfda8aSnia		d0 = XA__NET_VIRTUAL_ROOTS;
5030bbfda8aSnia		XChangeProperty(dpy, scr->XineramaRoot,
5040bbfda8aSnia		                XA__NET_SUPPORTED, XA_ATOM,
5050bbfda8aSnia		                32, PropModeAppend,
5060bbfda8aSnia		                (unsigned char *)&d0, 1);
5070bbfda8aSnia	}
5080bbfda8aSnia}
509b18c2d1eSnia#endif
510b18c2d1eSnia
5110bbfda8aSnia
5120bbfda8aSniastatic void EwmhTerminateScreen(ScreenInfo *scr)
5130bbfda8aSnia{
5140bbfda8aSnia	XDeleteProperty(dpy, scr->XineramaRoot, XA__NET_SUPPORTED);
5150bbfda8aSnia
5160bbfda8aSnia	/*
5170bbfda8aSnia	 * Don't delete scr->icccm_Window; let it be deleted automatically
5180bbfda8aSnia	 * when we terminate the X server connection. A replacement window
5190bbfda8aSnia	 * manager may want to start working immediately after it has
5200bbfda8aSnia	 * disappeared.
5210bbfda8aSnia	 */
5220bbfda8aSnia}
5230bbfda8aSnia
5240bbfda8aSnia/*
5250bbfda8aSnia * Clear everything that needs to be cleared before we exit.
5260bbfda8aSnia */
5270bbfda8aSnia
5280bbfda8aSniavoid EwmhTerminate(void)
5290bbfda8aSnia{
5300bbfda8aSnia	int scrnum;
5310bbfda8aSnia	ScreenInfo *scr;
5320bbfda8aSnia
5330bbfda8aSnia	for(scrnum = 0; scrnum < NumScreens; scrnum++) {
5340bbfda8aSnia		if((scr = ScreenList[scrnum]) == NULL) {
5350bbfda8aSnia			continue;
5360bbfda8aSnia		}
5370bbfda8aSnia		EwmhTerminateScreen(scr);
5380bbfda8aSnia	}
5390bbfda8aSnia}
5400bbfda8aSnia
5410bbfda8aSnia/*
5420bbfda8aSnia * Event handler: lost the WM_Sn selection
5430bbfda8aSnia * (that's the only selection we have).
5440bbfda8aSnia */
5450bbfda8aSnia
5460bbfda8aSniavoid EwmhSelectionClear(XSelectionClearEvent *sev)
5470bbfda8aSnia{
5480bbfda8aSnia#ifdef DEBUG_EWMH
5490bbfda8aSnia	fprintf(stderr, "sev->window = %x\n", (unsigned)sev->window);
5500bbfda8aSnia#endif
551b18c2d1eSnia	DoShutdown();
5520bbfda8aSnia}
5530bbfda8aSnia
5540bbfda8aSnia/*
5550bbfda8aSnia * When accepting client messages to the root window,
5560bbfda8aSnia * be accepting and accept both the real root window and the
5570bbfda8aSnia * current virtual screen.
5580bbfda8aSnia *
5590bbfda8aSnia * Should perhaps also accept any other virtual screen.
5600bbfda8aSnia */
5610bbfda8aSniabool EwmhClientMessage(XClientMessageEvent *msg)
5620bbfda8aSnia{
5630bbfda8aSnia	if(msg->format != 32) {
5640bbfda8aSnia		return false;
5650bbfda8aSnia	}
5660bbfda8aSnia
5670bbfda8aSnia	/* Messages regarding any window */
5680bbfda8aSnia	if(msg->message_type == XA__NET_WM_DESKTOP) {
5690bbfda8aSnia		EwmhClientMessage_NET_WM_DESKTOP(msg);
5700bbfda8aSnia		return true;
5710bbfda8aSnia	}
5720bbfda8aSnia	else if(msg->message_type == XA__NET_WM_STATE) {
5730bbfda8aSnia		EwmhClientMessage_NET_WM_STATE(msg);
5740bbfda8aSnia		return true;
5750bbfda8aSnia	}
5760bbfda8aSnia	else if(msg->message_type == XA__NET_ACTIVE_WINDOW) {
5770bbfda8aSnia		EwmhClientMessage_NET_ACTIVE_WINDOW(msg);
5780bbfda8aSnia		return true;
5790bbfda8aSnia	}
5800bbfda8aSnia	else if(msg->message_type == XA__NET_WM_MOVERESIZE) {
5810bbfda8aSnia		EwmhClientMessage_NET_WM_MOVERESIZE(msg);
5820bbfda8aSnia		return true;
5830bbfda8aSnia	}
584b18c2d1eSnia	else if(msg->message_type == XA__NET_CLOSE_WINDOW) {
585b18c2d1eSnia		EwmhClientMessage_NET_CLOSE_WINDOW(msg);
586b18c2d1eSnia		return true;
587b18c2d1eSnia	}
5880bbfda8aSnia
5890bbfda8aSnia	/* Messages regarding the root window */
5900bbfda8aSnia	if(msg->window != Scr->XineramaRoot &&
5910bbfda8aSnia	                msg->window != Scr->Root) {
5920bbfda8aSnia#ifdef DEBUG_EWMH
5930bbfda8aSnia		fprintf(stderr, "Received unrecognized client message: %s\n",
5940bbfda8aSnia		        XGetAtomName(dpy, msg->message_type));
5950bbfda8aSnia#endif
5960bbfda8aSnia		return false;
5970bbfda8aSnia	}
5980bbfda8aSnia
5990bbfda8aSnia	if(msg->message_type == XA__NET_CURRENT_DESKTOP) {
6000bbfda8aSnia		GotoWorkSpaceByNumber(Scr->currentvs, msg->data.l[0]);
6010bbfda8aSnia		return true;
6020bbfda8aSnia	}
6030bbfda8aSnia	else if(msg->message_type == XA__NET_SHOWING_DESKTOP) {
6040bbfda8aSnia		ShowBackground(Scr->currentvs, msg->data.l[0] ? 1 : 0);
605b18c2d1eSnia		return true;
6060bbfda8aSnia	}
6070bbfda8aSnia	else {
6080bbfda8aSnia#ifdef DEBUG_EWMH
6090bbfda8aSnia		fprintf(stderr, "Received unrecognized client message about root window: %s\n",
6100bbfda8aSnia		        XGetAtomName(dpy, msg->message_type));
6110bbfda8aSnia#endif
6120bbfda8aSnia	}
6130bbfda8aSnia
6140bbfda8aSnia	return false;
6150bbfda8aSnia}
6160bbfda8aSnia
6170bbfda8aSnia/*
6180bbfda8aSnia * The format of the _NET_WM_ICON property is
6190bbfda8aSnia *
6200bbfda8aSnia * [0] width
6210bbfda8aSnia * [1] height
6220bbfda8aSnia *     height repetitions of
6230bbfda8aSnia *         row, which is
6240bbfda8aSnia *              width repetitions of
6250bbfda8aSnia *                      pixel: ARGB
6260bbfda8aSnia * repeat for next size.
6270bbfda8aSnia *
6280bbfda8aSnia * Some icons can be 256x256 CARDINALs which is 65536 CARDINALS!
6290bbfda8aSnia * Therefore we fetch in pieces and skip the pixels of large icons
6300bbfda8aSnia * until needed.
6310bbfda8aSnia *
6320bbfda8aSnia * First scan all sizes. Keep a record of the closest smaller and larger
6330bbfda8aSnia * size. At the end, choose from one of those.
6340bbfda8aSnia * Finally, go and fetch the pixel data.
6350bbfda8aSnia */
6360bbfda8aSnia
6370bbfda8aSniaImage *EwmhGetIcon(ScreenInfo *scr, TwmWindow *twm_win)
6380bbfda8aSnia{
6390bbfda8aSnia	int fetch_offset;
6400bbfda8aSnia	Atom actual_type;
6410bbfda8aSnia	int actual_format;
6420bbfda8aSnia	unsigned long nitems, bytes_after;
6430bbfda8aSnia	unsigned long *prop;
6440bbfda8aSnia
6450bbfda8aSnia	int wanted_area;
6460bbfda8aSnia	int smaller, larger;
6470bbfda8aSnia	int offset;
6480bbfda8aSnia	int smaller_offset, larger_offset;
6490bbfda8aSnia	int i;
6500bbfda8aSnia
651b18c2d1eSnia	int width, height;
6520bbfda8aSnia
6530bbfda8aSnia	fetch_offset = 0;
6540bbfda8aSnia	if(XGetWindowProperty(dpy, twm_win->w, XA__NET_WM_ICON,
6550bbfda8aSnia	                      fetch_offset, 8 * 1024, False, XA_CARDINAL,
6560bbfda8aSnia	                      &actual_type, &actual_format, &nitems,
6570bbfda8aSnia	                      &bytes_after, (unsigned char **)&prop) != Success || nitems == 0) {
6580bbfda8aSnia		return NULL;
6590bbfda8aSnia	}
6600bbfda8aSnia
6610bbfda8aSnia	if(actual_format != 32) {
6620bbfda8aSnia		XFree(prop);
6630bbfda8aSnia		return NULL;
6640bbfda8aSnia	}
6650bbfda8aSnia
6660bbfda8aSnia#ifdef DEBUG_EWMH
6670bbfda8aSnia	fprintf(stderr, "_NET_WM_ICON data fetched\n");
6680bbfda8aSnia#endif
6690bbfda8aSnia	/*
6700bbfda8aSnia	 * Usually the icons are square, but that is not a rule.
6710bbfda8aSnia	 * So we measure the area instead.
6720bbfda8aSnia	 *
6730bbfda8aSnia	 * Approach wanted size from both directions and at the end,
6740bbfda8aSnia	 * choose the "nearest".
6750bbfda8aSnia	 */
6760bbfda8aSnia	wanted_area = Scr->PreferredIconWidth * Scr->PreferredIconHeight;
6770bbfda8aSnia	smaller = 0;
6780bbfda8aSnia	larger = 999999;
6790bbfda8aSnia	smaller_offset = -1;
6800bbfda8aSnia	larger_offset = -1;
6810bbfda8aSnia	i = 0;
6820bbfda8aSnia
6830bbfda8aSnia	for(;;) {
6840bbfda8aSnia		offset = i;
6850bbfda8aSnia
6860bbfda8aSnia		int w = prop[i++];
6870bbfda8aSnia		int h = prop[i++];
6880bbfda8aSnia		int size = w * h;
6890bbfda8aSnia
690b18c2d1eSnia		const int area = w * h;
6910bbfda8aSnia
6920bbfda8aSnia#ifdef DEBUG_EWMH
6930bbfda8aSnia		fprintf(stderr, "[%d+%d] w=%d h=%d\n", fetch_offset, offset, w, h);
6940bbfda8aSnia#endif
6950bbfda8aSnia
6960bbfda8aSnia
6970bbfda8aSnia		if(area == wanted_area) {
6980bbfda8aSnia#ifdef DEBUG_EWMH
6990bbfda8aSnia			fprintf(stderr, "exact match [%d+%d=%d] w=%d h=%d\n", fetch_offset, offset,
7000bbfda8aSnia			        fetch_offset + offset, w, h);
7010bbfda8aSnia#endif /* DEBUG_EWMH */
7020bbfda8aSnia			smaller_offset = fetch_offset + offset;
7030bbfda8aSnia			smaller = area;
7040bbfda8aSnia			larger_offset = -1;
7050bbfda8aSnia			break;
7060bbfda8aSnia		}
7070bbfda8aSnia		else if(area < wanted_area) {
7080bbfda8aSnia			if(area > smaller) {
7090bbfda8aSnia#ifdef DEBUG_EWMH
7100bbfda8aSnia				fprintf(stderr, "increase smaller, was [%d]\n", smaller_offset);
7110bbfda8aSnia#endif /* DEBUG_EWMH */
7120bbfda8aSnia				smaller = area;
7130bbfda8aSnia				smaller_offset = fetch_offset + offset;
7140bbfda8aSnia			}
7150bbfda8aSnia		}
7160bbfda8aSnia		else {   /* area > wanted_area */
7170bbfda8aSnia			if(area < larger) {
7180bbfda8aSnia#ifdef DEBUG_EWMH
7190bbfda8aSnia				fprintf(stderr, "decrease larger, was [%d]\n", larger_offset);
7200bbfda8aSnia#endif /* DEBUG_EWMH */
7210bbfda8aSnia				larger = area;
7220bbfda8aSnia				larger_offset = fetch_offset + offset;
7230bbfda8aSnia			}
7240bbfda8aSnia		}
7250bbfda8aSnia
7260bbfda8aSnia		if(i + size + 2 > nitems) {
7270bbfda8aSnia#ifdef DEBUG_EWMH
7280bbfda8aSnia			fprintf(stderr, "not enough data: %d + %d > %ld \n", i, size, nitems);
7290bbfda8aSnia#endif /* DEBUG_EWMH */
7300bbfda8aSnia
7310bbfda8aSnia			if(i + size + 2 <= nitems + bytes_after / 4) {
7320bbfda8aSnia				/* we can fetch some more... */
7330bbfda8aSnia				XFree(prop);
7340bbfda8aSnia				fetch_offset += i + size;
7350bbfda8aSnia				if(XGetWindowProperty(dpy, twm_win->w, XA__NET_WM_ICON,
7360bbfda8aSnia				                      fetch_offset, 8 * 1024, False, XA_CARDINAL,
7370bbfda8aSnia				                      &actual_type, &actual_format, &nitems,
7380bbfda8aSnia				                      &bytes_after, (unsigned char **)&prop) != Success) {
7390bbfda8aSnia					continue;
7400bbfda8aSnia				}
7410bbfda8aSnia				i = 0;
7420bbfda8aSnia				continue;
7430bbfda8aSnia			}
7440bbfda8aSnia			break;
7450bbfda8aSnia		}
7460bbfda8aSnia		i += size;
7470bbfda8aSnia	}
7480bbfda8aSnia
7490bbfda8aSnia	/*
7500bbfda8aSnia	 * Choose which icon approximates our desired size best.
7510bbfda8aSnia	 */
752b18c2d1eSnia	int area = 0;
753b18c2d1eSnia	ALLOW_DEAD_STORE(area); // all branches below init it
7540bbfda8aSnia
7550bbfda8aSnia	if(smaller_offset >= 0) {
7560bbfda8aSnia		if(larger_offset >= 0) {
7570bbfda8aSnia			/* choose the nearest */
7580bbfda8aSnia#ifdef DEBUG_EWMH
7590bbfda8aSnia			fprintf(stderr, "choose nearest %d %d\n", smaller, larger);
7600bbfda8aSnia#endif /* DEBUG_EWMH */
7610bbfda8aSnia			if((double)larger / wanted_area > (double)wanted_area / smaller) {
7620bbfda8aSnia				offset = smaller_offset;
7630bbfda8aSnia				area = smaller;
7640bbfda8aSnia			}
7650bbfda8aSnia			else {
7660bbfda8aSnia				offset = larger_offset;
7670bbfda8aSnia				area = larger;
7680bbfda8aSnia			}
7690bbfda8aSnia		}
7700bbfda8aSnia		else {
7710bbfda8aSnia			/* choose smaller */
7720bbfda8aSnia#ifdef DEBUG_EWMH
7730bbfda8aSnia			fprintf(stderr, "choose smaller (only) %d\n", smaller);
7740bbfda8aSnia#endif /* DEBUG_EWMH */
7750bbfda8aSnia			offset = smaller_offset;
7760bbfda8aSnia			area = smaller;
7770bbfda8aSnia		}
7780bbfda8aSnia	}
7790bbfda8aSnia	else if(larger_offset >= 0) {
7800bbfda8aSnia		/* choose larger */
7810bbfda8aSnia#ifdef DEBUG_EWMH
7820bbfda8aSnia		fprintf(stderr, "choose larger (only) %d\n", larger);
7830bbfda8aSnia#endif /* DEBUG_EWMH */
7840bbfda8aSnia		offset = larger_offset;
7850bbfda8aSnia		area = larger;
7860bbfda8aSnia	}
7870bbfda8aSnia	else {
7880bbfda8aSnia		/* no icons found at all? */
7890bbfda8aSnia#ifdef DEBUG_EWMH
7900bbfda8aSnia		fprintf(stderr, "nothing to choose from\n");
7910bbfda8aSnia#endif /* DEBUG_EWMH */
7920bbfda8aSnia		XFree(prop);
7930bbfda8aSnia		return NULL;
7940bbfda8aSnia	}
7950bbfda8aSnia
7960bbfda8aSnia	/*
7970bbfda8aSnia	 * Now fetch the pixels.
7980bbfda8aSnia	 */
7990bbfda8aSnia
8000bbfda8aSnia#ifdef DEBUG_EWMH
8010bbfda8aSnia	fprintf(stderr, "offset = %d fetch_offset = %d\n", offset, fetch_offset);
8020bbfda8aSnia	fprintf(stderr, "offset + 2 + area = %d fetch_offset + nitems = %ld\n",
8030bbfda8aSnia	        offset + 2 + area, fetch_offset + nitems);
8040bbfda8aSnia#endif /* DEBUG_EWMH */
8050bbfda8aSnia	if(offset < fetch_offset ||
8060bbfda8aSnia	                offset + 2 + area > fetch_offset + nitems) {
8070bbfda8aSnia		XFree(prop);
8080bbfda8aSnia		fetch_offset = offset;
8090bbfda8aSnia#ifdef DEBUG_EWMH
8100bbfda8aSnia		fprintf(stderr, "refetching from %d\n", fetch_offset);
8110bbfda8aSnia#endif /* DEBUG_EWMH */
8120bbfda8aSnia		if(XGetWindowProperty(dpy, twm_win->w, XA__NET_WM_ICON,
8130bbfda8aSnia		                      fetch_offset, 2 + area, False, XA_CARDINAL,
8140bbfda8aSnia		                      &actual_type, &actual_format, &nitems,
8150bbfda8aSnia		                      &bytes_after, (unsigned char **)&prop) != Success) {
8160bbfda8aSnia			return NULL;
8170bbfda8aSnia		}
8180bbfda8aSnia	}
8190bbfda8aSnia
8200bbfda8aSnia	i = offset - fetch_offset;
8210bbfda8aSnia	width = prop[i++];
8220bbfda8aSnia	height = prop[i++];
8230bbfda8aSnia#ifdef DEBUG_EWMH
8240bbfda8aSnia	fprintf(stderr, "Chosen [%d] w=%d h=%d area=%d\n", offset, width, height, area);
8250bbfda8aSnia#endif /* DEBUG_EWMH */
8260bbfda8aSnia	assert(width * height == area);
8270bbfda8aSnia
8280bbfda8aSnia	Image *image = ExtractIcon(scr, &prop[i], width, height);
8290bbfda8aSnia
8300bbfda8aSnia	XFree(prop);
8310bbfda8aSnia
8320bbfda8aSnia	return image;
8330bbfda8aSnia}
8340bbfda8aSnia
8350bbfda8aSniastatic uint16_t *buffer_16bpp;
8360bbfda8aSniastatic uint32_t *buffer_32bpp;
8370bbfda8aSnia
8380bbfda8aSniastatic void convert_for_16(int w, int x, int y, int argb)
8390bbfda8aSnia{
8400bbfda8aSnia	int r = (argb >> 16) & 0xFF;
8410bbfda8aSnia	int g = (argb >>  8) & 0xFF;
8420bbfda8aSnia	int b = (argb >>  0) & 0xFF;
8430bbfda8aSnia	buffer_16bpp [y * w + x] = ((r >> 3) << 11) + ((g >> 2) << 5) + (b >> 3);
8440bbfda8aSnia}
8450bbfda8aSnia
8460bbfda8aSniastatic void convert_for_32(int w, int x, int y, int argb)
8470bbfda8aSnia{
8480bbfda8aSnia	buffer_32bpp [y * w + x] = argb & 0x00FFFFFF;
8490bbfda8aSnia}
8500bbfda8aSnia
8510bbfda8aSniastatic Image *ExtractIcon(ScreenInfo *scr, unsigned long *prop, int width,
8520bbfda8aSnia                          int height)
8530bbfda8aSnia{
8540bbfda8aSnia	XImage *ximage;
8550bbfda8aSnia	void (*store_data)(int w, int x, int y, int argb);
8560bbfda8aSnia	int x, y, transparency;
8570bbfda8aSnia	int rowbytes;
8580bbfda8aSnia	unsigned char *maskbits;
8590bbfda8aSnia
8600bbfda8aSnia	GC gc;
8610bbfda8aSnia	Pixmap pixret;
8620bbfda8aSnia	Pixmap mask;
8630bbfda8aSnia	Image *image;
8640bbfda8aSnia	int i;
8650bbfda8aSnia
8660bbfda8aSnia	ximage = NULL;
8670bbfda8aSnia
8680bbfda8aSnia	/** XXX sort of duplicated from util.c:LoadJpegImage() */
8690bbfda8aSnia	if(scr->d_depth == 16) {
8700bbfda8aSnia		store_data = convert_for_16;
8710bbfda8aSnia		buffer_16bpp = malloc(width * height * sizeof(buffer_16bpp[0]));
8720bbfda8aSnia		buffer_32bpp = NULL;
8730bbfda8aSnia		ximage = XCreateImage(dpy, CopyFromParent, scr->d_depth, ZPixmap, 0,
8740bbfda8aSnia		                      (char *) buffer_16bpp, width, height, 16, width * 2);
8750bbfda8aSnia	}
8760bbfda8aSnia	else if(scr->d_depth == 24 || scr->d_depth == 32) {
8770bbfda8aSnia		store_data = convert_for_32;
8780bbfda8aSnia		buffer_32bpp = malloc(width * height * sizeof(buffer_32bpp[0]));
8790bbfda8aSnia		buffer_16bpp = NULL;
8800bbfda8aSnia		ximage = XCreateImage(dpy, CopyFromParent, scr->d_depth, ZPixmap, 0,
8810bbfda8aSnia		                      (char *) buffer_32bpp, width, height, 32, width * 4);
8820bbfda8aSnia	}
8830bbfda8aSnia	else {
8840bbfda8aSnia#ifdef DEBUG_EWMH
8850bbfda8aSnia		fprintf(stderr, "Screen unsupported depth for 32-bit icon: %d\n", scr->d_depth);
8860bbfda8aSnia#endif /* DEBUG_EWMH */
8870bbfda8aSnia		XFree(prop);
8880bbfda8aSnia		return NULL;
8890bbfda8aSnia	}
8900bbfda8aSnia	if(ximage == NULL) {
8910bbfda8aSnia#ifdef DEBUG_EWMH
8920bbfda8aSnia		fprintf(stderr, "cannot create image for icon\n");
8930bbfda8aSnia#endif /* DEBUG_EWMH */
8940bbfda8aSnia		XFree(prop);
8950bbfda8aSnia		return NULL;
8960bbfda8aSnia	}
8970bbfda8aSnia
8980bbfda8aSnia	transparency = 0;
8990bbfda8aSnia	rowbytes = (width + 7) / 8;
9000bbfda8aSnia	maskbits = calloc(height, rowbytes);
9010bbfda8aSnia
9020bbfda8aSnia	/*
9030bbfda8aSnia	 * Copy all ARGB pixels to the pixmap (the RGB part), and the bitmap (the
9040bbfda8aSnia	 * Alpha, or opaqueness part). If any pixels are transparent, we're going
9050bbfda8aSnia	 * to need a shape.
9060bbfda8aSnia	 */
9070bbfda8aSnia	i = 0;
9080bbfda8aSnia	for(y = 0; y < height; y++) {
9090bbfda8aSnia		for(x = 0; x < width; x++) {
9100bbfda8aSnia			unsigned long argb = prop[i++];
9110bbfda8aSnia			store_data(width, x, y, argb);
9120bbfda8aSnia			int opaque = ((argb >> 24) & 0xFF) >= 0x80; /* arbitrary cutoff */
9130bbfda8aSnia			if(opaque) {
9140bbfda8aSnia				maskbits [rowbytes * y + (x / 8)] |= 0x01 << (x % 8);
9150bbfda8aSnia			}
9160bbfda8aSnia			else {
9170bbfda8aSnia				transparency = 1;
9180bbfda8aSnia			}
9190bbfda8aSnia		}
9200bbfda8aSnia	}
9210bbfda8aSnia
9220bbfda8aSnia	gc = DefaultGC(dpy, scr->screen);
9230bbfda8aSnia	pixret = XCreatePixmap(dpy, scr->Root, width, height, scr->d_depth);
9240bbfda8aSnia	XPutImage(dpy, pixret, gc, ximage, 0, 0, 0, 0, width, height);
9250bbfda8aSnia	XDestroyImage(ximage);  /* also frees buffer_{16,32}bpp */
9260bbfda8aSnia	ximage = NULL;
9270bbfda8aSnia
9280bbfda8aSnia	mask = None;
9290bbfda8aSnia	if(transparency) {
9300bbfda8aSnia		mask = XCreatePixmapFromBitmapData(dpy, scr->Root, (char *)maskbits,
9310bbfda8aSnia		                                   width, height, 1, 0, 1);
9320bbfda8aSnia	}
9330bbfda8aSnia	free(maskbits);
9340bbfda8aSnia
9350bbfda8aSnia	image = AllocImage();
9360bbfda8aSnia	image->width  = width;
9370bbfda8aSnia	image->height = height;
9380bbfda8aSnia	image->pixmap = pixret;
9390bbfda8aSnia	image->mask   = mask;
9400bbfda8aSnia
9410bbfda8aSnia	return image;
9420bbfda8aSnia}
9430bbfda8aSnia
9440bbfda8aSnia/*
9450bbfda8aSnia * Handle a PropertyNotify on _NET_WM_ICON.
9460bbfda8aSnia */
9470bbfda8aSniastatic void EwmhHandle_NET_WM_ICONNotify(XPropertyEvent *event,
9480bbfda8aSnia                TwmWindow *twm_win)
9490bbfda8aSnia{
9500bbfda8aSnia	unsigned long valuemask;                /* mask for create windows */
9510bbfda8aSnia	XSetWindowAttributes attributes;        /* attributes for create windows */
9520bbfda8aSnia	Icon *icon = twm_win->icon;
9530bbfda8aSnia	int x;
9540bbfda8aSnia
9550bbfda8aSnia#ifdef DEBUG_EWMH
9560bbfda8aSnia	fprintf(stderr, "EwmhHandlePropertyNotify: NET_WM_ICON\n");
9570bbfda8aSnia#endif /* DEBUG_EWMH */
9580bbfda8aSnia	/*
9590bbfda8aSnia	 * If there is no icon yet, we'll look at this property
9600bbfda8aSnia	 * later, if and when we do create an icon.
9610bbfda8aSnia	 */
9620bbfda8aSnia	if(!icon || icon->match != match_net_wm_icon) {
9630bbfda8aSnia#ifdef DEBUG_EWMH
9640bbfda8aSnia		fprintf(stderr, "no icon, or not match_net_wm_icon\n");
9650bbfda8aSnia#endif /* DEBUG_EWMH */
9660bbfda8aSnia		return;
9670bbfda8aSnia	}
9680bbfda8aSnia
9690bbfda8aSnia	Image *image = EwmhGetIcon(Scr, twm_win);
9700bbfda8aSnia
9710bbfda8aSnia	/* TODO: de-duplicate with handling of XA_WM_HINTS */
9720bbfda8aSnia	{
9730bbfda8aSnia		Image *old_image = icon->image;
9740bbfda8aSnia		icon->image = image;
9750bbfda8aSnia		FreeImage(old_image);
9760bbfda8aSnia	}
9770bbfda8aSnia
9780bbfda8aSnia
9790bbfda8aSnia	if(twm_win->icon->bm_w) {
9800bbfda8aSnia		XDestroyWindow(dpy, twm_win->icon->bm_w);
9810bbfda8aSnia	}
9820bbfda8aSnia
9830bbfda8aSnia	valuemask = CWBackPixmap;
9840bbfda8aSnia	attributes.background_pixmap = image->pixmap;
9850bbfda8aSnia
9860bbfda8aSnia	x = GetIconOffset(twm_win->icon);
9870bbfda8aSnia	twm_win->icon->bm_w =
9880bbfda8aSnia	        XCreateWindow(dpy, twm_win->icon->w, x, 0,
9890bbfda8aSnia	                      twm_win->icon->width,
9900bbfda8aSnia	                      twm_win->icon->height,
9910bbfda8aSnia	                      0, Scr->d_depth,
9920bbfda8aSnia	                      CopyFromParent, Scr->d_visual,
9930bbfda8aSnia	                      valuemask, &attributes);
9940bbfda8aSnia
9950bbfda8aSnia	if(image->mask) {
9960bbfda8aSnia		XShapeCombineMask(dpy, twm_win->icon->bm_w, ShapeBounding, 0, 0, image->mask,
9970bbfda8aSnia		                  ShapeSet);
9980bbfda8aSnia		XShapeCombineMask(dpy, twm_win->icon->w,    ShapeBounding, x, 0, image->mask,
9990bbfda8aSnia		                  ShapeSet);
10000bbfda8aSnia	}
10010bbfda8aSnia	else {
10020bbfda8aSnia		XRectangle rect;
10030bbfda8aSnia
10040bbfda8aSnia		rect.x      = x;
10050bbfda8aSnia		rect.y      = 0;
10060bbfda8aSnia		rect.width  = twm_win->icon->width;
10070bbfda8aSnia		rect.height = twm_win->icon->height;
10080bbfda8aSnia		XShapeCombineRectangles(dpy, twm_win->icon->w, ShapeBounding, 0,
10090bbfda8aSnia		                        0, &rect, 1, ShapeUnion, 0);
10100bbfda8aSnia	}
10110bbfda8aSnia	XMapSubwindows(dpy, twm_win->icon->w);
10120bbfda8aSnia	RedoIconName(twm_win);
10130bbfda8aSnia}
10140bbfda8aSnia
10150bbfda8aSnia/*
10160bbfda8aSnia * Handle a PropertyNotify on _NET_WM_STRUT(PARTIAL).
10170bbfda8aSnia */
10180bbfda8aSniastatic void EwmhHandle_NET_WM_STRUTNotify(XPropertyEvent *event,
10190bbfda8aSnia                TwmWindow *twm_win)
10200bbfda8aSnia{
10210bbfda8aSnia	EwmhGetStrut(twm_win, true);
10220bbfda8aSnia}
10230bbfda8aSnia
10240bbfda8aSnia/*
10250bbfda8aSnia * Handle a _NET_WM_STATE ClientMessage.
10260bbfda8aSnia */
10270bbfda8aSniastatic int atomToFlag(Atom a)
10280bbfda8aSnia{
10290bbfda8aSnia#ifdef DEBUG_EWMH
10300bbfda8aSnia# define CRWARN(x) fprintf(stderr, "atomToFlag: ignoring " #x "\n")
10310bbfda8aSnia#else
10320bbfda8aSnia# define CRWARN(x) (void)0
10330bbfda8aSnia#endif
10340bbfda8aSnia#define CHKNRET(st) \
10350bbfda8aSnia        if(a == XA__NET_WM_##st) { \
10360bbfda8aSnia                if(LookInNameList(Scr->EWMHIgnore, #st)) { \
10370bbfda8aSnia                        CRWARN(st); \
10380bbfda8aSnia                        return 0; \
10390bbfda8aSnia                } \
10400bbfda8aSnia                return EWMH_##st; \
10410bbfda8aSnia        }
10420bbfda8aSnia
10430bbfda8aSnia	/* Check (potentially ignoring) various flags we know */
10440bbfda8aSnia	CHKNRET(STATE_MAXIMIZED_VERT);
10450bbfda8aSnia	CHKNRET(STATE_MAXIMIZED_HORZ);
10460bbfda8aSnia	CHKNRET(STATE_FULLSCREEN);
10470bbfda8aSnia	CHKNRET(STATE_SHADED);
10480bbfda8aSnia	CHKNRET(STATE_ABOVE);
10490bbfda8aSnia	CHKNRET(STATE_BELOW);
10500bbfda8aSnia
10510bbfda8aSnia#undef CHKNRET
10520bbfda8aSnia#undef CRWARN
10530bbfda8aSnia
10540bbfda8aSnia	/* Else we don't recognize it */
10550bbfda8aSnia	return 0;
10560bbfda8aSnia}
10570bbfda8aSnia
10580bbfda8aSnia
10590bbfda8aSnia/*
10600bbfda8aSnia * Handle the NET_WM_STATE client message.
10610bbfda8aSnia * It can change 1 or 2 values represented in NET_WM_STATE.
10620bbfda8aSnia * The second change is allowed
10630bbfda8aSnia * specifically for (re)setting horizontal and vertical maximalisation in
10640bbfda8aSnia * one go. Treat that as a special case.
10650bbfda8aSnia */
10660bbfda8aSniastatic void EwmhClientMessage_NET_WM_STATE(XClientMessageEvent *msg)
10670bbfda8aSnia{
10680bbfda8aSnia	Window w = msg->window;
10690bbfda8aSnia	TwmWindow *twm_win;
10700bbfda8aSnia	int change, change1, change2, newValue;
10710bbfda8aSnia
10720bbfda8aSnia	twm_win = GetTwmWindow(w);
10730bbfda8aSnia
10740bbfda8aSnia	if(twm_win == NULL) {
10750bbfda8aSnia		return;
10760bbfda8aSnia	}
10770bbfda8aSnia
10780bbfda8aSnia	/*
10790bbfda8aSnia	 * Due to EWMHIgnore, it's possible to wind up with change1=0 and
10800bbfda8aSnia	 * change2=something, so swap 'em if that happens.
10810bbfda8aSnia	 */
10820bbfda8aSnia	change1 = atomToFlag(msg->data.l[1]);
10830bbfda8aSnia	change2 = atomToFlag(msg->data.l[2]);
10840bbfda8aSnia	if(change1 == 0 && change2 != 0) {
10850bbfda8aSnia		change1 = change2;
10860bbfda8aSnia		change2 = 0;
10870bbfda8aSnia	}
10880bbfda8aSnia	change = change1 | change2;
10890bbfda8aSnia
10900bbfda8aSnia	switch(msg->data.l[0]) {
10910bbfda8aSnia		case NET_WM_STATE_REMOVE:
10920bbfda8aSnia#ifdef DEBUG_EWMH
10930bbfda8aSnia			printf("NET_WM_STATE_REMOVE: ");
10940bbfda8aSnia#endif
10950bbfda8aSnia			newValue = 0;
10960bbfda8aSnia			break;
10970bbfda8aSnia		case NET_WM_STATE_ADD:
10980bbfda8aSnia#ifdef DEBUG_EWMH
10990bbfda8aSnia			printf("NET_WM_STATE_ADD: ");
11000bbfda8aSnia#endif
11010bbfda8aSnia			newValue = change;
11020bbfda8aSnia			break;
11030bbfda8aSnia		case NET_WM_STATE_TOGGLE:
11040bbfda8aSnia#ifdef DEBUG_EWMH
11050bbfda8aSnia			printf("NET_WM_STATE_TOGGLE: ");
11060bbfda8aSnia#endif
11070bbfda8aSnia			newValue = ~twm_win->ewmhFlags & change;
11080bbfda8aSnia			break;
11090bbfda8aSnia		default:
11100bbfda8aSnia#ifdef DEBUG_EWMH
11110bbfda8aSnia			printf("invalid operation in NET_WM_STATE: %ld\n", msg->data.l[0]);
11120bbfda8aSnia#endif
11130bbfda8aSnia			return;
11140bbfda8aSnia	}
11150bbfda8aSnia#ifdef DEBUG_EWMH
11160bbfda8aSnia	printf("%s and %s\n", XGetAtomName(dpy, msg->data.l[1]),
11170bbfda8aSnia	       XGetAtomName(dpy, msg->data.l[2]));
11180bbfda8aSnia#endif
11190bbfda8aSnia
11200bbfda8aSnia	/*
11210bbfda8aSnia	 * Special-case the horizontal and vertical zoom.
11220bbfda8aSnia	 * You can turn them both on or both off, but no other combinations
11230bbfda8aSnia	 * are done as a unit.
11240bbfda8aSnia	 */
11250bbfda8aSnia	if(change == (EWMH_STATE_MAXIMIZED_VERT | EWMH_STATE_MAXIMIZED_HORZ) &&
11260bbfda8aSnia	                (newValue == 0 || newValue == change)) {
11270bbfda8aSnia		EwmhClientMessage_NET_WM_STATEchange(twm_win, change, newValue);
11280bbfda8aSnia	}
11290bbfda8aSnia	else {
11300bbfda8aSnia		EwmhClientMessage_NET_WM_STATEchange(twm_win, change1, newValue & change1);
11310bbfda8aSnia		if(change2 != 0) {
11320bbfda8aSnia			EwmhClientMessage_NET_WM_STATEchange(twm_win, change2, newValue & change2);
11330bbfda8aSnia		}
11340bbfda8aSnia	}
11350bbfda8aSnia}
11360bbfda8aSnia
11370bbfda8aSnia/*
11380bbfda8aSnia * change - bitmask of settings that possibly change. Only one bit is
11390bbfda8aSnia *                      set in this, with the possible exception of
11400bbfda8aSnia *                      *MAXIMIZED_{VERT,HORIZ} which can be set together.
11410bbfda8aSnia * newValue - bits with the new values; only valid bits are the ones
11420bbfda8aSnia *                      in change.
11430bbfda8aSnia */
11440bbfda8aSniastatic void EwmhClientMessage_NET_WM_STATEchange(TwmWindow *twm_win, int change,
11450bbfda8aSnia                int newValue)
11460bbfda8aSnia{
11470bbfda8aSnia	/* Now check what we need to change */
11480bbfda8aSnia
11490bbfda8aSnia	if(change & (EWMH_STATE_MAXIMIZED_VERT | EWMH_STATE_MAXIMIZED_HORZ |
11500bbfda8aSnia	                EWMH_STATE_FULLSCREEN)) {
11510bbfda8aSnia		int newZoom = ZOOM_NONE;
11520bbfda8aSnia
11530bbfda8aSnia		switch(newValue) {
11540bbfda8aSnia			case 0:
11550bbfda8aSnia				newZoom = twm_win->zoomed;      /* turn off whatever zoom */
11560bbfda8aSnia				break;
11570bbfda8aSnia			case EWMH_STATE_MAXIMIZED_VERT:
11580bbfda8aSnia				newZoom = F_ZOOM;
11590bbfda8aSnia				break;
11600bbfda8aSnia			case EWMH_STATE_MAXIMIZED_HORZ:
11610bbfda8aSnia				newZoom = F_HORIZOOM;
11620bbfda8aSnia				break;
11630bbfda8aSnia			case EWMH_STATE_MAXIMIZED_HORZ | EWMH_STATE_MAXIMIZED_VERT:
11640bbfda8aSnia				newZoom = F_FULLZOOM;
11650bbfda8aSnia				break;
11660bbfda8aSnia			case EWMH_STATE_FULLSCREEN:
11670bbfda8aSnia				newZoom = F_FULLSCREENZOOM;
11680bbfda8aSnia				break;
11690bbfda8aSnia		}
11700bbfda8aSnia		fullzoom(twm_win, newZoom);
11710bbfda8aSnia	}
11720bbfda8aSnia	else if(change & EWMH_STATE_SHADED) {
11730bbfda8aSnia#ifdef DEBUG_EWMH
11740bbfda8aSnia		printf("EWMH_STATE_SHADED: newValue: %d old: %d\n", newValue,
11750bbfda8aSnia		       twm_win->ewmhFlags & EWMH_STATE_SHADED);
11760bbfda8aSnia#endif
11770bbfda8aSnia		if((twm_win->ewmhFlags & EWMH_STATE_SHADED) ^ newValue) {
11780bbfda8aSnia			/* Toggle the shade/squeeze state */
11790bbfda8aSnia#ifdef DEBUG_EWMH
11800bbfda8aSnia			printf("EWMH_STATE_SHADED: change it\n");
11810bbfda8aSnia#endif
11820bbfda8aSnia			Squeeze(twm_win);
11830bbfda8aSnia		}
11840bbfda8aSnia	}
11850bbfda8aSnia	else if(change & (EWMH_STATE_ABOVE | EWMH_STATE_BELOW)) {
11860bbfda8aSnia		/*
11870bbfda8aSnia		 * Other changes call into ctwm code, which in turn calls back to
11880bbfda8aSnia		 * EWMH code to update the ewmhFlags and the property.  This one
11890bbfda8aSnia		 * we handle completely internally.
11900bbfda8aSnia		 */
11910bbfda8aSnia		unsigned omask = 0, oval = 0;
11920bbfda8aSnia		const int prepri = OtpEffectivePriority(twm_win);
11930bbfda8aSnia
11940bbfda8aSnia		/* Which bits are we changing and what to? */
11950bbfda8aSnia#define DOBIT(fld) do { \
11960bbfda8aSnia          if(change   & EWMH_STATE_##fld) { omask |= OTP_AFLAG_##fld; } \
11970bbfda8aSnia          if(newValue & EWMH_STATE_##fld) { oval  |= OTP_AFLAG_##fld; } \
11980bbfda8aSnia        } while(0)
11990bbfda8aSnia
12000bbfda8aSnia		DOBIT(ABOVE);
12010bbfda8aSnia		DOBIT(BELOW);
12020bbfda8aSnia
12030bbfda8aSnia#undef DOBIT
12040bbfda8aSnia
12050bbfda8aSnia		/* Update OTP as necessary */
12060bbfda8aSnia		OtpSetAflagMask(twm_win, omask, oval);
12070bbfda8aSnia		if(OtpEffectivePriority(twm_win) != prepri) {
12080bbfda8aSnia			OtpRestackWindow(twm_win);
12090bbfda8aSnia		}
12100bbfda8aSnia
12110bbfda8aSnia		/* Set the EWMH property back on the window */
12120bbfda8aSnia		EwmhSet_NET_WM_STATE(twm_win, change);
12130bbfda8aSnia	}
12140bbfda8aSnia}
12150bbfda8aSnia
12160bbfda8aSnia/*
12170bbfda8aSnia * Handle the _NET_ACTIVE_WINDOW client message.
12180bbfda8aSnia * Pagers would send such a message to "activate" a window.
12190bbfda8aSnia *
12200bbfda8aSnia * What does "activate" really mean? It isn't properly described.
12210bbfda8aSnia *
12220bbfda8aSnia * Let's presume that it means that the window is de-iconified and gets
12230bbfda8aSnia * focus.  The mouse may be moved to it (but not all window button apps
12240bbfda8aSnia * do that).  But is it always raised or should that depend on the
12250bbfda8aSnia * RaiseOnWarp option?
12260bbfda8aSnia */
12270bbfda8aSniastatic void EwmhClientMessage_NET_ACTIVE_WINDOW(XClientMessageEvent *msg)
12280bbfda8aSnia{
12290bbfda8aSnia	Window w = msg->window;
12300bbfda8aSnia	TwmWindow *twm_win;
12310bbfda8aSnia
12320bbfda8aSnia	twm_win = GetTwmWindow(w);
12330bbfda8aSnia
12340bbfda8aSnia	if(twm_win == NULL) {
12350bbfda8aSnia		return;
12360bbfda8aSnia	}
12370bbfda8aSnia
12380bbfda8aSnia	if(!twm_win->mapped) {
12390bbfda8aSnia		DeIconify(twm_win);
12400bbfda8aSnia	}
12410bbfda8aSnia#if 0
12420bbfda8aSnia	WarpToWindow(twm_win, Scr->RaiseOnWarp /* True ? */);
12430bbfda8aSnia#else
12440bbfda8aSnia	/*
12450bbfda8aSnia	 * Keep the mouse pointer where it is (typically the dock).
12460bbfda8aSnia	 * WarpToWindow() would change the current workspace if needed to go
12470bbfda8aSnia	 * to the window. But pagers would only send this message for
12480bbfda8aSnia	 * windows in the current workspace, I expect.
12490bbfda8aSnia	 */
12500bbfda8aSnia	if(Scr->RaiseOnWarp) {
12510bbfda8aSnia		AutoRaiseWindow(twm_win);
12520bbfda8aSnia	}
12530bbfda8aSnia	SetFocus(twm_win, msg->data.l[1]);
12540bbfda8aSnia#endif
12550bbfda8aSnia}
12560bbfda8aSnia
12570bbfda8aSnia/*
12580bbfda8aSnia * Ugly implementation of _NET_WM_MOVERESIZE.
12590bbfda8aSnia *
12600bbfda8aSnia * window = window to be moved or resized
12610bbfda8aSnia * message_type = _NET_WM_MOVERESIZE
12620bbfda8aSnia * format = 32
12630bbfda8aSnia * data.l[0] = x_root
12640bbfda8aSnia * data.l[1] = y_root
12650bbfda8aSnia * data.l[2] = direction
12660bbfda8aSnia * data.l[3] = button
12670bbfda8aSnia * data.l[4] = source indication
12680bbfda8aSnia */
12690bbfda8aSniastatic void EwmhClientMessage_NET_WM_MOVERESIZE(XClientMessageEvent *msg)
12700bbfda8aSnia{
12710bbfda8aSnia	TwmWindow *twm_win;
12720bbfda8aSnia
12730bbfda8aSnia	twm_win = GetTwmWindow(msg->window);
12740bbfda8aSnia
12750bbfda8aSnia	if(twm_win == NULL) {
12760bbfda8aSnia		return;
12770bbfda8aSnia	}
12780bbfda8aSnia
12790bbfda8aSnia	if(!twm_win->mapped) {
12800bbfda8aSnia		DeIconify(twm_win);
12810bbfda8aSnia	}
12820bbfda8aSnia
12830bbfda8aSnia	switch(msg->data.l[2]) {
12840bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_TOPLEFT:
12850bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_TOP:
12860bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT:
12870bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_RIGHT:
12880bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT:
12890bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_BOTTOM:
12900bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT:
12910bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_LEFT:
12920bbfda8aSnia		case _NET_WM_MOVERESIZE_SIZE_KEYBOARD: {
12930bbfda8aSnia			/* Fake up as if we triggered f.resize on it */
12940bbfda8aSnia			XEvent xevent = synth_btnevent_for_moveresize(twm_win);
12950bbfda8aSnia
12960bbfda8aSnia			/*
12970bbfda8aSnia			 * The resize won't actually start until we cross the cursor
12980bbfda8aSnia			 * over a border.  Perhaps we should find the nearest corner,
12990bbfda8aSnia			 * and pre-warp the cursor there?  That may be less friendly
13000bbfda8aSnia			 * for the user, since it might not be as predictable, and
13010bbfda8aSnia			 * having the cursor zoom off without warning is probably a
13020bbfda8aSnia			 * little surprising...
13030bbfda8aSnia			 */
13040bbfda8aSnia			ExecuteFunction(F_RESIZE, "", twm_win->frame, twm_win,
13050bbfda8aSnia			                &xevent, C_WINDOW, false);
13060bbfda8aSnia
13070bbfda8aSnia			/*
13080bbfda8aSnia			 * It's not guaranteed that this actually began as a button
13090bbfda8aSnia			 * press, but our implementation is expecting the button to
13100bbfda8aSnia			 * be held down all through the move, so that
13110bbfda8aSnia			 * ExecuteFunction() won't actually end until there's a
13120bbfda8aSnia			 * button release of some kind, which will trigger
13130bbfda8aSnia			 * HandleButtonRelease() properly.  So we don't need to call
13140bbfda8aSnia			 * it ourselves.
13150bbfda8aSnia			 */
13160bbfda8aSnia
13170bbfda8aSnia			break;
13180bbfda8aSnia		}
13190bbfda8aSnia		case _NET_WM_MOVERESIZE_MOVE:
13200bbfda8aSnia		case _NET_WM_MOVERESIZE_MOVE_KEYBOARD: {
13210bbfda8aSnia			/* Fake up as if we triggered f.move on it */
13220bbfda8aSnia			XEvent xevent = synth_btnevent_for_moveresize(twm_win);
13230bbfda8aSnia			ExecuteFunction(F_MOVE, "", twm_win->frame, twm_win,
13240bbfda8aSnia			                &xevent, C_WINDOW, false);
13250bbfda8aSnia
13260bbfda8aSnia			/* X-ref HandleButtonRelease() discussion above */
13270bbfda8aSnia			break;
13280bbfda8aSnia		}
13290bbfda8aSnia		case _NET_WM_MOVERESIZE_CANCEL:
13300bbfda8aSnia			/*
13310bbfda8aSnia			 * TODO: check if the twm_win is the same.
13320bbfda8aSnia			 * TODO: check how to make this actually work.
13330bbfda8aSnia			 *
13340bbfda8aSnia			 * As currently implemented, I don't believe we ever need to
13350bbfda8aSnia			 * do anything here.  All the needed cleanup should happen in
13360bbfda8aSnia			 * our ButtonRelease handler.
13370bbfda8aSnia			 */
13380bbfda8aSnia			break;
13390bbfda8aSnia	}
13400bbfda8aSnia}
13410bbfda8aSnia
13420bbfda8aSniastatic XEvent
13430bbfda8aSniasynth_btnevent_for_moveresize(TwmWindow *twm_win)
13440bbfda8aSnia{
13450bbfda8aSnia	XEvent xevent;
13460bbfda8aSnia	Window root = twm_win->parent_vs->window;
13470bbfda8aSnia	Window child = twm_win->w;
13480bbfda8aSnia	int x_root = twm_win->frame_x;
13490bbfda8aSnia	int y_root = twm_win->frame_y;
13500bbfda8aSnia	int x_win  = 0;
13510bbfda8aSnia	int y_win  = 0;
13520bbfda8aSnia	unsigned int dummy_mask;
13530bbfda8aSnia
13540bbfda8aSnia	/* Find the pointer */
13550bbfda8aSnia	XQueryPointer(dpy, twm_win->frame, &root, &child, &x_root, &y_root,
13560bbfda8aSnia	              &x_win, &y_win, &dummy_mask);
13570bbfda8aSnia
13580bbfda8aSnia	/* Synthesize a button event */
13590bbfda8aSnia	xevent.type = ButtonPress;
13600bbfda8aSnia	xevent.xbutton.root = root;
13610bbfda8aSnia	xevent.xbutton.window = child;
13620bbfda8aSnia	xevent.xbutton.x_root = x_root;
13630bbfda8aSnia	xevent.xbutton.y_root = y_root;
13640bbfda8aSnia	xevent.xbutton.x = x_win;
13650bbfda8aSnia	xevent.xbutton.y = y_win;
13660bbfda8aSnia	xevent.xbutton.time = EventTime;
13670bbfda8aSnia
13680bbfda8aSnia	return xevent;
13690bbfda8aSnia}
13700bbfda8aSnia
13710bbfda8aSnia
1372b18c2d1eSnia/*
1373b18c2d1eSnia * Implementation of _NET_CLOSE_WINDOW
1374b18c2d1eSnia *
1375b18c2d1eSnia * window = window to be closed
1376b18c2d1eSnia * message_type = _NET_CLOSE_WINDOW
1377b18c2d1eSnia * format = 32
1378b18c2d1eSnia * data.l[0] = timestamp
1379b18c2d1eSnia * data.l[1] = source indication:
1380b18c2d1eSnia *             0 Clients that support only older version of this spec
1381b18c2d1eSnia *             1 for normal applications, and
1382b18c2d1eSnia *             2 for pagers and other Clients that represent direct user actions
1383b18c2d1eSnia * data.l[2] = 0
1384b18c2d1eSnia * data.l[3] = 0
1385b18c2d1eSnia * data.l[4] = 0
1386b18c2d1eSnia */
1387b18c2d1eSniastatic void EwmhClientMessage_NET_CLOSE_WINDOW(XClientMessageEvent *msg)
1388b18c2d1eSnia{
1389b18c2d1eSnia	TwmWindow *tmp_win;
1390b18c2d1eSnia
1391b18c2d1eSnia	tmp_win = GetTwmWindow(msg->window);
1392b18c2d1eSnia
1393b18c2d1eSnia	if(tmp_win != NULL) {
1394b18c2d1eSnia		ButtonPressed = -1;
1395b18c2d1eSnia		ExecuteFunction(F_DELETE, NULL, msg->window, tmp_win,
1396b18c2d1eSnia		                (XEvent *)msg, C_NO_CONTEXT, 0);
1397b18c2d1eSnia	}
1398b18c2d1eSnia}
1399b18c2d1eSnia
14000bbfda8aSnia/*
14010bbfda8aSnia * Handle any PropertyNotify.
14020bbfda8aSnia */
14030bbfda8aSniaint EwmhHandlePropertyNotify(XPropertyEvent *event, TwmWindow *twm_win)
14040bbfda8aSnia{
14050bbfda8aSnia	if(event->atom == XA__NET_WM_ICON) {
14060bbfda8aSnia		EwmhHandle_NET_WM_ICONNotify(event, twm_win);
14070bbfda8aSnia		return 1;
14080bbfda8aSnia	}
14090bbfda8aSnia	else if(event->atom == XA__NET_WM_STRUT_PARTIAL ||
14100bbfda8aSnia	                event->atom == XA__NET_WM_STRUT) {
14110bbfda8aSnia		EwmhHandle_NET_WM_STRUTNotify(event, twm_win);
14120bbfda8aSnia		return 1;
14130bbfda8aSnia	}
14140bbfda8aSnia	else if(event->atom == XA__NET_WM_NAME) {
14150bbfda8aSnia		char *prop = GetWMPropertyString(twm_win->w, XA__NET_WM_NAME);
14160bbfda8aSnia		if(prop == NULL) {
14170bbfda8aSnia			FreeWMPropertyString(twm_win->names.net_wm_name);
14180bbfda8aSnia			twm_win->names.net_wm_name = NULL;
14190bbfda8aSnia			apply_window_name(twm_win);
14200bbfda8aSnia			return 1;
14210bbfda8aSnia		}
14220bbfda8aSnia
14230bbfda8aSnia		if(twm_win->names.net_wm_name != NULL
14240bbfda8aSnia		                && strcmp(twm_win->names.net_wm_name, prop) == 0) {
14250bbfda8aSnia			/* No change, just free and skip out */
14260bbfda8aSnia			free(prop);
14270bbfda8aSnia			return 1;
14280bbfda8aSnia		}
14290bbfda8aSnia
14300bbfda8aSnia		/* It's changing, free the old and bring in the new */
14310bbfda8aSnia		FreeWMPropertyString(twm_win->names.net_wm_name);
14320bbfda8aSnia		twm_win->names.net_wm_name = prop;
14330bbfda8aSnia
14340bbfda8aSnia		/* Kick the reset process */
14350bbfda8aSnia		apply_window_name(twm_win);
14360bbfda8aSnia		return 1;
14370bbfda8aSnia	}
14380bbfda8aSnia	else if(event->atom == XA__NET_WM_ICON_NAME) {
14390bbfda8aSnia		char *prop = GetWMPropertyString(twm_win->w, XA__NET_WM_ICON_NAME);
14400bbfda8aSnia		if(prop == NULL) {
14410bbfda8aSnia			FreeWMPropertyString(twm_win->names.net_wm_icon_name);
14420bbfda8aSnia			twm_win->names.net_wm_icon_name = NULL;
14430bbfda8aSnia			apply_window_icon_name(twm_win);
14440bbfda8aSnia			return 1;
14450bbfda8aSnia		}
14460bbfda8aSnia
14470bbfda8aSnia		if(twm_win->names.net_wm_icon_name != NULL
14480bbfda8aSnia		                && strcmp(twm_win->names.net_wm_icon_name, prop) == 0) {
14490bbfda8aSnia			/* No change, just free and skip out */
14500bbfda8aSnia			free(prop);
14510bbfda8aSnia			return 1;
14520bbfda8aSnia		}
14530bbfda8aSnia
14540bbfda8aSnia		/* It's changing, free the old and bring in the new */
14550bbfda8aSnia		FreeWMPropertyString(twm_win->names.net_wm_icon_name);
14560bbfda8aSnia		twm_win->names.net_wm_icon_name = prop;
14570bbfda8aSnia
14580bbfda8aSnia		/* Kick the reset process */
14590bbfda8aSnia		apply_window_icon_name(twm_win);
14600bbfda8aSnia		return 1;
14610bbfda8aSnia	}
14620bbfda8aSnia
14630bbfda8aSnia	return 0;
14640bbfda8aSnia}
14650bbfda8aSnia
14660bbfda8aSnia/*
14670bbfda8aSnia * Set the _NET_WM_DESKTOP property for the current workspace.
14680bbfda8aSnia */
14690bbfda8aSniavoid EwmhSet_NET_WM_DESKTOP(TwmWindow *twm_win)
14700bbfda8aSnia{
14710bbfda8aSnia	WorkSpace *ws;
14720bbfda8aSnia
14730bbfda8aSnia	VirtualScreen *vs = twm_win->vs;
14740bbfda8aSnia	if(vs != NULL) {
14750bbfda8aSnia		ws = vs->wsw->currentwspc;
14760bbfda8aSnia	}
14770bbfda8aSnia	else {
14780bbfda8aSnia		ws = NULL;
14790bbfda8aSnia	}
14800bbfda8aSnia
14810bbfda8aSnia	EwmhSet_NET_WM_DESKTOP_ws(twm_win, ws);
14820bbfda8aSnia}
14830bbfda8aSnia
14840bbfda8aSnia/*
14850bbfda8aSnia * Set the _NET_WM_DESKTOP property for the given workspace.
14860bbfda8aSnia */
14870bbfda8aSniavoid EwmhSet_NET_WM_DESKTOP_ws(TwmWindow *twm_win, WorkSpace *ws)
14880bbfda8aSnia{
14890bbfda8aSnia	unsigned long workspaces[MAXWORKSPACE];
14900bbfda8aSnia	int n = 0;
14910bbfda8aSnia
14920bbfda8aSnia	if(!Scr->workSpaceManagerActive) {
14930bbfda8aSnia		workspaces[n++] = 0;
14940bbfda8aSnia	}
14950bbfda8aSnia	else if(twm_win->occupation == fullOccupation) {
14960bbfda8aSnia		workspaces[n++] = ALL_WORKSPACES;
14970bbfda8aSnia	}
14980bbfda8aSnia	else {
14990bbfda8aSnia		/*
15000bbfda8aSnia		 * Our windows can occupy multiple workspaces ("virtual desktops" in
15010bbfda8aSnia		 * EWMH terminology) at once. Extend the _NET_WM_DESKTOP property
15020bbfda8aSnia		 * by setting it to multiple CARDINALs if this occurs.
15030bbfda8aSnia		 * Put the currently visible workspace (if any) first, since typical
15040bbfda8aSnia		 * pager apps don't know about this.
15050bbfda8aSnia		 */
15060bbfda8aSnia		int occupation = twm_win->occupation;
15070bbfda8aSnia
15080bbfda8aSnia		/*
15090bbfda8aSnia		 * Set visible workspace number.
15100bbfda8aSnia		 */
15110bbfda8aSnia		if(ws != NULL) {
15120bbfda8aSnia			int wsn = ws->number;
15130bbfda8aSnia
15140bbfda8aSnia			workspaces[n++] = wsn;
15150bbfda8aSnia			occupation &= ~(1 << wsn);
15160bbfda8aSnia		}
15170bbfda8aSnia
15180bbfda8aSnia		/*
15190bbfda8aSnia		 * Set any other workspace numbers.
15200bbfda8aSnia		 */
15210bbfda8aSnia		if(occupation != 0) {
15220bbfda8aSnia			int i;
15230bbfda8aSnia			int mask = 1;
15240bbfda8aSnia
15250bbfda8aSnia			for(i = 0; i < MAXWORKSPACE; i++) {
15260bbfda8aSnia				if(occupation & mask) {
15270bbfda8aSnia					workspaces[n++] = i;
15280bbfda8aSnia				}
15290bbfda8aSnia				mask <<= 1;
15300bbfda8aSnia			}
15310bbfda8aSnia		}
15320bbfda8aSnia	}
15330bbfda8aSnia
15340bbfda8aSnia	XChangeProperty(dpy, twm_win->w,
15350bbfda8aSnia	                XA__NET_WM_DESKTOP, XA_CARDINAL,
15360bbfda8aSnia	                32, PropModeReplace,
15370bbfda8aSnia	                (unsigned char *)workspaces, n);
15380bbfda8aSnia}
15390bbfda8aSnia
15400bbfda8aSnia
15410bbfda8aSniastatic unsigned long EwmhGetWindowProperty(Window w, Atom name, Atom type)
15420bbfda8aSnia{
15430bbfda8aSnia	Atom actual_type;
15440bbfda8aSnia	int actual_format;
15450bbfda8aSnia	unsigned long nitems, bytes_after;
15460bbfda8aSnia	unsigned long *prop;
15470bbfda8aSnia	unsigned long value;
15480bbfda8aSnia
15490bbfda8aSnia	if(XGetWindowProperty(dpy, w, name,
15500bbfda8aSnia	                      0, 1, False, type,
15510bbfda8aSnia	                      &actual_type, &actual_format, &nitems,
15520bbfda8aSnia	                      &bytes_after, (unsigned char **)&prop) != Success) {
15530bbfda8aSnia		return 0;
15540bbfda8aSnia	}
15550bbfda8aSnia
15560bbfda8aSnia	if(actual_format == 32) {
15570bbfda8aSnia		if(nitems >= 1) {
15580bbfda8aSnia			value = prop[0];
15590bbfda8aSnia		}
15600bbfda8aSnia		else {
15610bbfda8aSnia			value = 0;
15620bbfda8aSnia		}
15630bbfda8aSnia	}
15640bbfda8aSnia	else {
15650bbfda8aSnia		value = 0;
15660bbfda8aSnia	}
15670bbfda8aSnia
15680bbfda8aSnia	XFree(prop);
15690bbfda8aSnia	return value;
15700bbfda8aSnia}
15710bbfda8aSnia
15720bbfda8aSnia/*
15730bbfda8aSnia * Simple function to get multiple properties of format 32.
15740bbfda8aSnia * If it fails, returns NULL, and *nitems_return == 0.
15750bbfda8aSnia */
15760bbfda8aSnia
15770bbfda8aSniastatic unsigned long *EwmhGetWindowProperties(Window w, Atom name, Atom type,
15780bbfda8aSnia                unsigned long *nitems_return)
15790bbfda8aSnia{
15800bbfda8aSnia	Atom actual_type;
15810bbfda8aSnia	int actual_format;
15820bbfda8aSnia	unsigned long bytes_after;
15830bbfda8aSnia	unsigned long *prop;
15840bbfda8aSnia
15850bbfda8aSnia	if(XGetWindowProperty(dpy, w, name,
15860bbfda8aSnia	                      0, 8192, False, type,
15870bbfda8aSnia	                      &actual_type, &actual_format, nitems_return,
15880bbfda8aSnia	                      &bytes_after, (unsigned char **)&prop) != Success) {
15890bbfda8aSnia		*nitems_return = 0;
15900bbfda8aSnia		return NULL;
15910bbfda8aSnia	}
15920bbfda8aSnia
15930bbfda8aSnia	if(actual_format != 32) {
15940bbfda8aSnia		XFree(prop);
15950bbfda8aSnia		prop = NULL;
15960bbfda8aSnia		*nitems_return = 0;
15970bbfda8aSnia	}
15980bbfda8aSnia
15990bbfda8aSnia	return prop;
16000bbfda8aSnia}
16010bbfda8aSnia
16020bbfda8aSniaint EwmhGetOccupation(TwmWindow *twm_win)
16030bbfda8aSnia{
16040bbfda8aSnia	unsigned long nitems;
16050bbfda8aSnia	unsigned long *prop;
16060bbfda8aSnia	int occupation;
16070bbfda8aSnia
16080bbfda8aSnia	occupation = 0;
16090bbfda8aSnia
16100bbfda8aSnia	prop = EwmhGetWindowProperties(twm_win->w,
16110bbfda8aSnia	                               XA__NET_WM_DESKTOP, XA_CARDINAL, &nitems);
16120bbfda8aSnia
16130bbfda8aSnia	if(prop) {
16140bbfda8aSnia		int i;
16150bbfda8aSnia		for(i = 0; i < nitems; i++) {
16160bbfda8aSnia			unsigned int val = prop[i];
16170bbfda8aSnia			if(val == ALL_WORKSPACES) {
16180bbfda8aSnia				occupation = fullOccupation;
16190bbfda8aSnia			}
16200bbfda8aSnia			else if(val < Scr->workSpaceMgr.count) {
16210bbfda8aSnia				occupation |= 1 << val;
16220bbfda8aSnia			}
16230bbfda8aSnia			else {
16240bbfda8aSnia				occupation |= 1 << (Scr->workSpaceMgr.count - 1);
16250bbfda8aSnia			}
16260bbfda8aSnia		}
16270bbfda8aSnia
16280bbfda8aSnia		occupation &= fullOccupation;
16290bbfda8aSnia
16300bbfda8aSnia		XFree(prop);
16310bbfda8aSnia	}
16320bbfda8aSnia
16330bbfda8aSnia	return occupation;
16340bbfda8aSnia}
16350bbfda8aSnia
16360bbfda8aSnia/*
16370bbfda8aSnia * The message to change the desktop of a window doesn't recognize
16380bbfda8aSnia * that it may be in more than one.
16390bbfda8aSnia *
16400bbfda8aSnia * Therefore the following heuristic is applied:
16410bbfda8aSnia * - the window is removed from the workspace where it is visible (if any);
16420bbfda8aSnia * - it is added to the given workspace;
16430bbfda8aSnia * - other occupation bits are left unchanged.
16440bbfda8aSnia *
16450bbfda8aSnia * If asked to put it on a too high numbered workspace, put it on
16460bbfda8aSnia * the highest possible.
16470bbfda8aSnia */
16480bbfda8aSniastatic void EwmhClientMessage_NET_WM_DESKTOP(XClientMessageEvent *msg)
16490bbfda8aSnia{
16500bbfda8aSnia	Window w = msg->window;
16510bbfda8aSnia	TwmWindow *twm_win;
16520bbfda8aSnia	int occupation;
16530bbfda8aSnia	VirtualScreen *vs;
16540bbfda8aSnia	unsigned int val;
16550bbfda8aSnia
16560bbfda8aSnia	twm_win = GetTwmWindow(w);
16570bbfda8aSnia
16580bbfda8aSnia	if(twm_win == NULL) {
16590bbfda8aSnia		return;
16600bbfda8aSnia	}
16610bbfda8aSnia
16620bbfda8aSnia	occupation = twm_win->occupation;
16630bbfda8aSnia
16640bbfda8aSnia	/* Remove from visible workspace */
16650bbfda8aSnia	if((vs = twm_win->vs) != NULL) {
16660bbfda8aSnia		occupation &= ~(1 << vs->wsw->currentwspc->number);
16670bbfda8aSnia	}
16680bbfda8aSnia
16690bbfda8aSnia	val = (unsigned int)msg->data.l[0];
16700bbfda8aSnia
16710bbfda8aSnia	/* Add to requested workspace (or to all) */
16720bbfda8aSnia	if(val == ALL_WORKSPACES) {
16730bbfda8aSnia		occupation = fullOccupation;
16740bbfda8aSnia	}
16750bbfda8aSnia	else if(val < Scr->workSpaceMgr.count) {
16760bbfda8aSnia		occupation |= 1 << val;
16770bbfda8aSnia	}
16780bbfda8aSnia	else {
16790bbfda8aSnia		occupation |= 1 << (Scr->workSpaceMgr.count - 1);
16800bbfda8aSnia	}
16810bbfda8aSnia
16820bbfda8aSnia	ChangeOccupation(twm_win, occupation);
16830bbfda8aSnia}
16840bbfda8aSnia
16850bbfda8aSnia/*
16860bbfda8aSnia * Delete all properties that should be removed from a withdrawn
16870bbfda8aSnia * window.
16880bbfda8aSnia */
16890bbfda8aSniavoid EwmhUnmapNotify(TwmWindow *twm_win)
16900bbfda8aSnia{
16910bbfda8aSnia	XDeleteProperty(dpy, twm_win->w, XA__NET_WM_DESKTOP);
16920bbfda8aSnia}
16930bbfda8aSnia
16940bbfda8aSnia/*
16950bbfda8aSnia * Add a new window to _NET_CLIENT_LIST.
16960bbfda8aSnia * Newer windows are always added at the end.
16970bbfda8aSnia *
16980bbfda8aSnia * Look at new_win->iconmanagerlist as an optimization for
16990bbfda8aSnia * !LookInList(Scr->IconMgrNoShow, new_win->name, &new_win->class)).
17000bbfda8aSnia */
17010bbfda8aSniavoid EwmhAddClientWindow(TwmWindow *new_win)
17020bbfda8aSnia{
17030bbfda8aSnia	if(Scr->ewmh_CLIENT_LIST_size == 0) {
17040bbfda8aSnia		return;
17050bbfda8aSnia	}
17060bbfda8aSnia	if(new_win->iconmanagerlist != NULL &&
17070bbfda8aSnia	                !new_win->iswspmgr &&
17080bbfda8aSnia	                !new_win->isiconmgr) {
17090bbfda8aSnia		Scr->ewmh_CLIENT_LIST_used++;
17100bbfda8aSnia		if(Scr->ewmh_CLIENT_LIST_used > Scr->ewmh_CLIENT_LIST_size) {
17110bbfda8aSnia			long *tp;
17120bbfda8aSnia			int tsz = Scr->ewmh_CLIENT_LIST_size;
17130bbfda8aSnia
17140bbfda8aSnia			Scr->ewmh_CLIENT_LIST_size *= 2;
17150bbfda8aSnia			tp = realloc(Scr->ewmh_CLIENT_LIST,
17160bbfda8aSnia			             sizeof(long) * Scr->ewmh_CLIENT_LIST_size);
17170bbfda8aSnia			if(tp == NULL) {
17180bbfda8aSnia				Scr->ewmh_CLIENT_LIST_size = tsz;
17190bbfda8aSnia				fprintf(stderr, "Unable to allocate memory for EWMH client list.\n");
17200bbfda8aSnia				return;
17210bbfda8aSnia			}
17220bbfda8aSnia			Scr->ewmh_CLIENT_LIST = tp;
17230bbfda8aSnia		}
17240bbfda8aSnia		if(Scr->ewmh_CLIENT_LIST) {
17250bbfda8aSnia			Scr->ewmh_CLIENT_LIST[Scr->ewmh_CLIENT_LIST_used - 1] = new_win->w;
17260bbfda8aSnia		}
17270bbfda8aSnia		else {
17280bbfda8aSnia			Scr->ewmh_CLIENT_LIST_size = 0;
17290bbfda8aSnia			fprintf(stderr, "Unable to allocate memory for EWMH client list.\n");
17300bbfda8aSnia			return;
17310bbfda8aSnia		}
17320bbfda8aSnia		XChangeProperty(dpy, Scr->Root, XA__NET_CLIENT_LIST, XA_WINDOW, 32,
17330bbfda8aSnia		                PropModeReplace, (unsigned char *)Scr->ewmh_CLIENT_LIST,
17340bbfda8aSnia		                Scr->ewmh_CLIENT_LIST_used);
17350bbfda8aSnia	}
17360bbfda8aSnia}
17370bbfda8aSnia
17380bbfda8aSniavoid EwmhDeleteClientWindow(TwmWindow *old_win)
17390bbfda8aSnia{
17400bbfda8aSnia	int i;
17410bbfda8aSnia
17420bbfda8aSnia	if(old_win->ewmhFlags & EWMH_HAS_STRUT) {
17430bbfda8aSnia		EwmhRemoveStrut(old_win);
17440bbfda8aSnia	}
17450bbfda8aSnia
17460bbfda8aSnia	/*
17470bbfda8aSnia	 * Remove the window from _NET_CLIENT_LIST.
17480bbfda8aSnia	 */
17490bbfda8aSnia	if(Scr->ewmh_CLIENT_LIST_size == 0) {
17500bbfda8aSnia		return;
17510bbfda8aSnia	}
17520bbfda8aSnia	for(i = Scr->ewmh_CLIENT_LIST_used - 1; i >= 0; i--) {
17530bbfda8aSnia		if(Scr->ewmh_CLIENT_LIST[i] == old_win->w) {
17540bbfda8aSnia			memmove(&Scr->ewmh_CLIENT_LIST[i],
17550bbfda8aSnia			        &Scr->ewmh_CLIENT_LIST[i + 1],
17560bbfda8aSnia			        (Scr->ewmh_CLIENT_LIST_used - 1 - i) * sizeof(Scr->ewmh_CLIENT_LIST[0]));
17570bbfda8aSnia			Scr->ewmh_CLIENT_LIST_used--;
17580bbfda8aSnia			if(Scr->ewmh_CLIENT_LIST_used &&
17590bbfda8aSnia			                (Scr->ewmh_CLIENT_LIST_used * 3) < Scr->ewmh_CLIENT_LIST_size) {
17600bbfda8aSnia				Scr->ewmh_CLIENT_LIST_size /= 2;
17610bbfda8aSnia				Scr->ewmh_CLIENT_LIST = realloc(Scr->ewmh_CLIENT_LIST,
17620bbfda8aSnia				                                sizeof((Scr->ewmh_CLIENT_LIST[0])) * Scr->ewmh_CLIENT_LIST_size);
17630bbfda8aSnia				/* memory shrinking, shouldn't have problems */
17640bbfda8aSnia			}
17650bbfda8aSnia			break;
17660bbfda8aSnia		}
17670bbfda8aSnia	}
17680bbfda8aSnia	/* If window was not found, there is no need to update the property. */
17690bbfda8aSnia	if(i >= 0) {
17700bbfda8aSnia		XChangeProperty(dpy, Scr->Root, XA__NET_CLIENT_LIST, XA_WINDOW, 32,
17710bbfda8aSnia		                PropModeReplace, (unsigned char *)Scr->ewmh_CLIENT_LIST,
17720bbfda8aSnia		                Scr->ewmh_CLIENT_LIST_used);
17730bbfda8aSnia	}
17740bbfda8aSnia}
17750bbfda8aSnia
17760bbfda8aSnia/*
17770bbfda8aSnia * Similar to EwmhAddClientWindow() and EwmhDeleteClientWindow(),
17780bbfda8aSnia * but the windows are in stacking order.
17790bbfda8aSnia * Therefore we look at the OTPs, which are by definition in the correct order.
17800bbfda8aSnia */
17810bbfda8aSnia
17820bbfda8aSniavoid EwmhSet_NET_CLIENT_LIST_STACKING(void)
17830bbfda8aSnia{
17840bbfda8aSnia	int size;
17850bbfda8aSnia	unsigned long *prop;
17860bbfda8aSnia	TwmWindow *twm_win;
17870bbfda8aSnia	int i;
17880bbfda8aSnia
17890bbfda8aSnia	/* Expect the same number of windows as in the _NET_CLIENT_LIST */
17900bbfda8aSnia	size = Scr->ewmh_CLIENT_LIST_used + 10;
17910bbfda8aSnia	prop = calloc(size, sizeof(unsigned long));
17920bbfda8aSnia	if(prop == NULL) {
17930bbfda8aSnia		return;
17940bbfda8aSnia	}
17950bbfda8aSnia
17960bbfda8aSnia	i = 0;
17970bbfda8aSnia	for(twm_win = OtpBottomWin();
17980bbfda8aSnia	                twm_win != NULL;
17990bbfda8aSnia	                twm_win = OtpNextWinUp(twm_win)) {
18000bbfda8aSnia		if(twm_win->iconmanagerlist != NULL &&
18010bbfda8aSnia		                !twm_win->iswspmgr &&
18020bbfda8aSnia		                !twm_win->isiconmgr) {
18030bbfda8aSnia			prop[i] = twm_win->w;
18040bbfda8aSnia			i++;
18050bbfda8aSnia			if(i > size) {
18060bbfda8aSnia				fprintf(stderr, "Too many stacked windows\n");
18070bbfda8aSnia				break;
18080bbfda8aSnia			}
18090bbfda8aSnia		}
18100bbfda8aSnia	}
18110bbfda8aSnia
18120bbfda8aSnia	if(i != Scr->ewmh_CLIENT_LIST_used) {
18130bbfda8aSnia		fprintf(stderr, "Incorrect number of stacked windows: %d (expected %d)\n",
18140bbfda8aSnia		        i, Scr->ewmh_CLIENT_LIST_used);
18150bbfda8aSnia	}
18160bbfda8aSnia
18170bbfda8aSnia	XChangeProperty(dpy, Scr->Root, XA__NET_CLIENT_LIST_STACKING, XA_WINDOW, 32,
18180bbfda8aSnia	                PropModeReplace, (unsigned char *)prop, i);
18190bbfda8aSnia
18200bbfda8aSnia	free(prop);
18210bbfda8aSnia}
18220bbfda8aSnia
18230bbfda8aSniavoid EwmhSet_NET_ACTIVE_WINDOW(Window w)
18240bbfda8aSnia{
18250bbfda8aSnia	unsigned long prop[1];
18260bbfda8aSnia
18270bbfda8aSnia	prop[0] = w;
18280bbfda8aSnia
18290bbfda8aSnia	XChangeProperty(dpy, Scr->Root, XA__NET_ACTIVE_WINDOW, XA_WINDOW, 32,
18300bbfda8aSnia	                PropModeReplace, (unsigned char *)prop, 1);
18310bbfda8aSnia}
18320bbfda8aSnia
18330bbfda8aSnia/*
18340bbfda8aSnia * Get window properties as relevant when the window is initially mapped.
18350bbfda8aSnia *
18360bbfda8aSnia * So far, only NET_WM_WINDOW_TYPE and _NET_WM_STRUT_PARTIAL.
18370bbfda8aSnia * In particular, most of the initial value of _NET_WM_STATE is ignored. TODO.
18380bbfda8aSnia *
18390bbfda8aSnia * Also do any generic initialisation needed to EWMH-specific fields
18400bbfda8aSnia * in a TwmWindow.
18410bbfda8aSnia */
18420bbfda8aSniavoid EwmhGetProperties(TwmWindow *twm_win)
18430bbfda8aSnia{
18440bbfda8aSnia	twm_win->ewmhFlags = 0;
18450bbfda8aSnia
18460bbfda8aSnia	Atom type = EwmhGetWindowProperty(twm_win->w, XA__NET_WM_WINDOW_TYPE, XA_ATOM);
18470bbfda8aSnia
18480bbfda8aSnia	if(type == XA__NET_WM_WINDOW_TYPE_DESKTOP) {
18490bbfda8aSnia		twm_win->ewmhWindowType = wt_Desktop;
18500bbfda8aSnia	}
18510bbfda8aSnia	else if(type == XA__NET_WM_WINDOW_TYPE_DOCK) {
18520bbfda8aSnia		twm_win->ewmhWindowType = wt_Dock;
18530bbfda8aSnia	}
18540bbfda8aSnia	else {
18550bbfda8aSnia		twm_win->ewmhWindowType = wt_Normal;
18560bbfda8aSnia	}
18570bbfda8aSnia	EwmhGetStrut(twm_win, false);
18580bbfda8aSnia	/* Only the 3 listed states are supported for now */
18590bbfda8aSnia	twm_win->ewmhFlags |= EwmhGet_NET_WM_STATE(twm_win) &
18600bbfda8aSnia	                      (EWMH_STATE_ABOVE | EWMH_STATE_BELOW | EWMH_STATE_SHADED);
18610bbfda8aSnia}
18620bbfda8aSnia
18630bbfda8aSnia/* Only used in initially mapping a window */
18640bbfda8aSniaint EwmhGetInitPriority(TwmWindow *twm_win)
18650bbfda8aSnia{
18660bbfda8aSnia	switch(twm_win->ewmhWindowType) {
18670bbfda8aSnia		case wt_Desktop:
18680bbfda8aSnia			return EWMH_PRI_DESKTOP;
18690bbfda8aSnia		case wt_Dock:
18700bbfda8aSnia			return EWMH_PRI_DOCK;
18710bbfda8aSnia		default:
18720bbfda8aSnia			return 0;
18730bbfda8aSnia	}
18740bbfda8aSnia}
18750bbfda8aSnia
18760bbfda8aSniabool EwmhHasBorder(TwmWindow *twm_win)
18770bbfda8aSnia{
18780bbfda8aSnia	switch(twm_win->ewmhWindowType) {
18790bbfda8aSnia		case wt_Desktop:
18800bbfda8aSnia		case wt_Dock:
18810bbfda8aSnia			return false;
18820bbfda8aSnia		default:
18830bbfda8aSnia			return true;
18840bbfda8aSnia	}
18850bbfda8aSnia}
18860bbfda8aSnia
18870bbfda8aSniabool EwmhHasTitle(TwmWindow *twm_win)
18880bbfda8aSnia{
18890bbfda8aSnia	switch(twm_win->ewmhWindowType) {
18900bbfda8aSnia		case wt_Desktop:
18910bbfda8aSnia		case wt_Dock:
18920bbfda8aSnia			return false;
18930bbfda8aSnia		default:
18940bbfda8aSnia			return true;
18950bbfda8aSnia	}
18960bbfda8aSnia}
18970bbfda8aSnia
18980bbfda8aSniabool EwmhOnWindowRing(TwmWindow *twm_win)
18990bbfda8aSnia{
19000bbfda8aSnia	switch(twm_win->ewmhWindowType) {
19010bbfda8aSnia		case wt_Desktop:
19020bbfda8aSnia		case wt_Dock:
19030bbfda8aSnia			return false;
19040bbfda8aSnia		default:
19050bbfda8aSnia			return true;
19060bbfda8aSnia	}
19070bbfda8aSnia}
19080bbfda8aSnia
19090bbfda8aSnia/*
19100bbfda8aSnia * Recalculate the effective border values from the remembered struts.
19110bbfda8aSnia * Interestingly it is not documented how to do that.
19120bbfda8aSnia * Usually only one dock is present on each side, so it shouldn't matter
19130bbfda8aSnia * too much, but I presume that maximizing the values is the thing to do.
19140bbfda8aSnia */
19150bbfda8aSniastatic void EwmhRecalculateStrut(void)
19160bbfda8aSnia{
19170bbfda8aSnia	int left   = 0;
19180bbfda8aSnia	int right  = 0;
19190bbfda8aSnia	int top    = 0;
19200bbfda8aSnia	int bottom = 0;
19210bbfda8aSnia	EwmhStrut *strut = Scr->ewmhStruts;
19220bbfda8aSnia
19230bbfda8aSnia	while(strut != NULL) {
19240bbfda8aSnia		left   = max(left,   strut->left);
19250bbfda8aSnia		right  = max(right,  strut->right);
19260bbfda8aSnia		top    = max(top,    strut->top);
19270bbfda8aSnia		bottom = max(bottom, strut->bottom);
19280bbfda8aSnia
19290bbfda8aSnia		strut  = strut->next;
19300bbfda8aSnia	}
19310bbfda8aSnia
19320bbfda8aSnia	Scr->BorderLeft   = left;
19330bbfda8aSnia	Scr->BorderRight  = right;
19340bbfda8aSnia	Scr->BorderTop    = top;
19350bbfda8aSnia	Scr->BorderBottom = bottom;
19360bbfda8aSnia
1937b18c2d1eSnia	// Bordered layout may have changed
1938b18c2d1eSnia	Scr->BorderedLayout = RLayoutCopyCropped(Scr->Layout,
1939b18c2d1eSnia	                      left, right, top, bottom);
1940b18c2d1eSnia	if(Scr->BorderedLayout == NULL) {
1941b18c2d1eSnia		Scr->BorderedLayout = Scr->Layout;        // nothing to crop
1942b18c2d1eSnia	}
1943b18c2d1eSnia
19440bbfda8aSnia	EwmhSet_NET_WORKAREA(Scr);
19450bbfda8aSnia}
19460bbfda8aSnia/*
19470bbfda8aSnia * Check _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT.
19480bbfda8aSnia * These are basically automatic settings for Border{Left,Right,Top,Bottom}.
19490bbfda8aSnia *
19500bbfda8aSnia * If any values are found, collect them in a list of strut values
19510bbfda8aSnia * belonging to Scr.  When a window is added or removed that has struts,
19520bbfda8aSnia * the new effective value must be calculated.  The expectation is that
19530bbfda8aSnia * at most a handful of windows will have struts.
19540bbfda8aSnia *
19550bbfda8aSnia * If update is true, this is called as an update for an existing window.
19560bbfda8aSnia */
19570bbfda8aSniastatic void EwmhGetStrut(TwmWindow *twm_win, bool update)
19580bbfda8aSnia{
19590bbfda8aSnia	unsigned long nitems;
19600bbfda8aSnia	unsigned long *prop;
19610bbfda8aSnia	EwmhStrut *strut;
19620bbfda8aSnia
19630bbfda8aSnia	prop = EwmhGetWindowProperties(twm_win->w,
19640bbfda8aSnia	                               XA__NET_WM_STRUT_PARTIAL, XA_CARDINAL,
19650bbfda8aSnia	                               &nitems);
19660bbfda8aSnia	if(prop == NULL) {
19670bbfda8aSnia		prop = EwmhGetWindowProperties(twm_win->w,
19680bbfda8aSnia		                               XA__NET_WM_STRUT, XA_CARDINAL,  &nitems);
19690bbfda8aSnia		if(prop == NULL) {
19700bbfda8aSnia			return;
19710bbfda8aSnia		}
19720bbfda8aSnia	}
19730bbfda8aSnia
19740bbfda8aSnia
19750bbfda8aSnia	if(nitems < 4) {
19760bbfda8aSnia#ifdef DEBUG_EWMH
19770bbfda8aSnia		/* This happens a lot, despite returning Success ??? */
19780bbfda8aSnia		printf("struts: prop = %p, nitems = %ld\n", prop, nitems);
19790bbfda8aSnia#endif
19800bbfda8aSnia		XFree(prop);
19810bbfda8aSnia		return;
19820bbfda8aSnia	}
19830bbfda8aSnia#ifdef DEBUG_EWMH
19840bbfda8aSnia	printf("struts: left %ld, right %ld, top %ld, bottom %ld\n",
19850bbfda8aSnia	       prop[0], prop[1], prop[2], prop[3]);
19860bbfda8aSnia#endif
19870bbfda8aSnia
19880bbfda8aSnia	/*
19890bbfda8aSnia	 * If there were no struts before, the user configured margins are set
19900bbfda8aSnia	 * in Border{Left,Right,Top,Bottom}. In order not to lose those values
19910bbfda8aSnia	 * when recalculating them, convert them to struts for a dummy window.
19920bbfda8aSnia	 */
19930bbfda8aSnia
19940bbfda8aSnia	if(Scr->ewmhStruts == NULL &&
19950bbfda8aSnia	                (Scr->BorderLeft |
19960bbfda8aSnia	                 Scr->BorderRight |
19970bbfda8aSnia	                 Scr->BorderTop |
19980bbfda8aSnia	                 Scr->BorderBottom) != 0) {
19990bbfda8aSnia		strut = calloc(1, sizeof(EwmhStrut));
20000bbfda8aSnia		if(strut == NULL) {
20010bbfda8aSnia			XFree(prop);
20020bbfda8aSnia			return;
20030bbfda8aSnia		}
20040bbfda8aSnia
20050bbfda8aSnia		strut->next   = NULL;
20060bbfda8aSnia		strut->win    = NULL;
20070bbfda8aSnia		strut->left   = Scr->BorderLeft;
20080bbfda8aSnia		strut->right  = Scr->BorderRight;
20090bbfda8aSnia		strut->top    = Scr->BorderTop;
20100bbfda8aSnia		strut->bottom = Scr->BorderBottom;
20110bbfda8aSnia
20120bbfda8aSnia		Scr->ewmhStruts = strut;
20130bbfda8aSnia	}
20140bbfda8aSnia
20150bbfda8aSnia	strut = NULL;
20160bbfda8aSnia
20170bbfda8aSnia	/*
20180bbfda8aSnia	 * Find the struts of the window that we're supposed to be updating.
20190bbfda8aSnia	 * If not found, there is no problem: we'll just allocate a new
20200bbfda8aSnia	 * record.
20210bbfda8aSnia	 */
20220bbfda8aSnia
20230bbfda8aSnia	if(update) {
20240bbfda8aSnia		strut = Scr->ewmhStruts;
20250bbfda8aSnia
20260bbfda8aSnia		while(strut != NULL) {
20270bbfda8aSnia			if(strut->win == twm_win) {
20280bbfda8aSnia				break;
20290bbfda8aSnia			}
20300bbfda8aSnia			strut = strut->next;
20310bbfda8aSnia		}
20320bbfda8aSnia	}
20330bbfda8aSnia
20340bbfda8aSnia	/*
20350bbfda8aSnia	 * If needed, allocate a new struts record and link it in.
20360bbfda8aSnia	 */
20370bbfda8aSnia	if(strut == NULL) {
20380bbfda8aSnia		strut = calloc(1, sizeof(EwmhStrut));
20390bbfda8aSnia		if(strut == NULL) {
20400bbfda8aSnia			XFree(prop);
20410bbfda8aSnia			return;
20420bbfda8aSnia		}
20430bbfda8aSnia
20440bbfda8aSnia		strut->next = Scr->ewmhStruts;
20450bbfda8aSnia		Scr->ewmhStruts = strut;
20460bbfda8aSnia	}
20470bbfda8aSnia
20480bbfda8aSnia	strut->win    = twm_win;
20490bbfda8aSnia	strut->left   = prop[0];
20500bbfda8aSnia	strut->right  = prop[1];
20510bbfda8aSnia	strut->top    = prop[2];
20520bbfda8aSnia	strut->bottom = prop[3];
20530bbfda8aSnia
20540bbfda8aSnia	XFree(prop);
20550bbfda8aSnia
20560bbfda8aSnia	/*
20570bbfda8aSnia	 * Mark this window as having contributed some struts.
20580bbfda8aSnia	 * This can be checked and undone when the window is deleted.
20590bbfda8aSnia	 */
20600bbfda8aSnia	twm_win->ewmhFlags |= EWMH_HAS_STRUT;
20610bbfda8aSnia
20620bbfda8aSnia	EwmhRecalculateStrut();
20630bbfda8aSnia}
20640bbfda8aSnia
20650bbfda8aSnia/*
20660bbfda8aSnia * Remove the struts associated with the given window from the
20670bbfda8aSnia * remembered list. If found, recalculate the effective borders.
20680bbfda8aSnia */
20690bbfda8aSniastatic void EwmhRemoveStrut(TwmWindow *twm_win)
20700bbfda8aSnia{
20710bbfda8aSnia	EwmhStrut **prev = &Scr->ewmhStruts;
20720bbfda8aSnia	EwmhStrut *strut = Scr->ewmhStruts;
20730bbfda8aSnia
20740bbfda8aSnia	while(strut != NULL) {
20750bbfda8aSnia		if(strut->win == twm_win) {
20760bbfda8aSnia			twm_win->ewmhFlags &= ~EWMH_HAS_STRUT;
20770bbfda8aSnia
20780bbfda8aSnia			*prev = strut->next;
20790bbfda8aSnia			free(strut);
20800bbfda8aSnia
20810bbfda8aSnia			EwmhRecalculateStrut();
20820bbfda8aSnia
20830bbfda8aSnia			break;
20840bbfda8aSnia		}
20850bbfda8aSnia		prev = &strut->next;
20860bbfda8aSnia		strut = strut->next;
20870bbfda8aSnia	}
20880bbfda8aSnia}
20890bbfda8aSnia
2090b18c2d1eSnia
2091b18c2d1eSnia/**
2092b18c2d1eSnia * Set _NET_FRAME_EXTENTS property.
2093b18c2d1eSnia * This tells the client how much space is being taken up by the window
2094b18c2d1eSnia * decorations.  Some clients may need this information to position other
2095b18c2d1eSnia * windows on top of themselves.  e.g., Firefox's form autofill and
2096b18c2d1eSnia * context menu will be positioned a bit wrong (high, by the height of
2097b18c2d1eSnia * the titlebar) without this.
2098b18c2d1eSnia */
2099b18c2d1eSniavoid EwmhSet_NET_FRAME_EXTENTS(TwmWindow *twm_win)
2100b18c2d1eSnia{
2101b18c2d1eSnia	long data[4];
2102b18c2d1eSnia	const long w = twm_win->frame_bw3D + twm_win->frame_bw;
2103b18c2d1eSnia
2104b18c2d1eSnia	data[0] = w; // left
2105b18c2d1eSnia	data[1] = w; // right
2106b18c2d1eSnia	data[2] = twm_win->title_height + w; // top
2107b18c2d1eSnia	data[3] = w; // bottom
2108b18c2d1eSnia
2109b18c2d1eSnia	XChangeProperty(dpy, twm_win->w,
2110b18c2d1eSnia	                XA__NET_FRAME_EXTENTS, XA_CARDINAL,
2111b18c2d1eSnia	                32, PropModeReplace,
2112b18c2d1eSnia	                (unsigned char *)data, 4);
2113b18c2d1eSnia}
2114b18c2d1eSnia
2115b18c2d1eSnia
21160bbfda8aSniavoid EwmhSet_NET_SHOWING_DESKTOP(int state)
21170bbfda8aSnia{
21180bbfda8aSnia	unsigned long prop[1];
21190bbfda8aSnia
21200bbfda8aSnia	prop[0] = state;
21210bbfda8aSnia
21220bbfda8aSnia	XChangeProperty(dpy, Scr->XineramaRoot, XA__NET_SHOWING_DESKTOP, XA_CARDINAL,
21230bbfda8aSnia	                32,
21240bbfda8aSnia	                PropModeReplace, (unsigned char *)prop, 1);
21250bbfda8aSnia}
21260bbfda8aSnia
21270bbfda8aSnia/*
21280bbfda8aSnia * Set _NET_WM_STATE.
21290bbfda8aSnia *
21300bbfda8aSnia * TwmWindow.ewmhFlags keeps track of the atoms that should be in
21310bbfda8aSnia * the list, so that we don't have to fetch or recalculate them all.
21320bbfda8aSnia *
21330bbfda8aSnia * XXX It's not clear that the theoretical performance gain and edge-case
21340bbfda8aSnia * bug avoidance of the 'changes' arg is worth the complexity and
21350bbfda8aSnia * edge-case bug creation it brings.  Consider removing it.
21360bbfda8aSnia */
21370bbfda8aSniavoid EwmhSet_NET_WM_STATE(TwmWindow *twm_win, int changes)
21380bbfda8aSnia{
21390bbfda8aSnia	unsigned long prop[10];
21400bbfda8aSnia	int flags;
21410bbfda8aSnia	int i;
21420bbfda8aSnia
21430bbfda8aSnia	if(changes & EWMH_STATE_MAXIMIZED_VERT) {
21440bbfda8aSnia		int newFlags = 0;
21450bbfda8aSnia
21460bbfda8aSnia		switch(twm_win->zoomed) {
21470bbfda8aSnia			case F_FULLZOOM:
21480bbfda8aSnia				newFlags = EWMH_STATE_MAXIMIZED_VERT |
21490bbfda8aSnia				           EWMH_STATE_MAXIMIZED_HORZ;
21500bbfda8aSnia				break;
21510bbfda8aSnia			case F_ZOOM:
21520bbfda8aSnia			case F_LEFTZOOM:
21530bbfda8aSnia			case F_RIGHTZOOM:
21540bbfda8aSnia				newFlags = EWMH_STATE_MAXIMIZED_VERT;
21550bbfda8aSnia				break;
21560bbfda8aSnia			case F_HORIZOOM:
21570bbfda8aSnia			case F_TOPZOOM:
21580bbfda8aSnia			case F_BOTTOMZOOM:
21590bbfda8aSnia				newFlags = EWMH_STATE_MAXIMIZED_HORZ;
21600bbfda8aSnia				break;
21610bbfda8aSnia			case F_FULLSCREENZOOM:
21620bbfda8aSnia				newFlags = EWMH_STATE_FULLSCREEN;
21630bbfda8aSnia				break;
21640bbfda8aSnia		}
21650bbfda8aSnia
21660bbfda8aSnia		twm_win->ewmhFlags &= ~(EWMH_STATE_MAXIMIZED_VERT |
21670bbfda8aSnia		                        EWMH_STATE_MAXIMIZED_HORZ |
21680bbfda8aSnia		                        EWMH_STATE_FULLSCREEN);
21690bbfda8aSnia		twm_win->ewmhFlags |= newFlags;
21700bbfda8aSnia	}
21710bbfda8aSnia
21720bbfda8aSnia	if(changes & EWMH_STATE_SHADED) {
21730bbfda8aSnia		if(twm_win->squeezed) {
21740bbfda8aSnia			twm_win->ewmhFlags |= EWMH_STATE_SHADED;
21750bbfda8aSnia		}
21760bbfda8aSnia		else {
21770bbfda8aSnia			twm_win->ewmhFlags &= ~EWMH_STATE_SHADED;
21780bbfda8aSnia		}
21790bbfda8aSnia	}
21800bbfda8aSnia
21810bbfda8aSnia	if(changes & (EWMH_STATE_ABOVE | EWMH_STATE_BELOW)) {
21820bbfda8aSnia		int pri = OtpEffectiveDisplayPriority(twm_win);
21830bbfda8aSnia
21840bbfda8aSnia		twm_win->ewmhFlags &= ~(EWMH_STATE_ABOVE | EWMH_STATE_BELOW);
21850bbfda8aSnia
21860bbfda8aSnia		/*
21870bbfda8aSnia		 * If it's a DOCK or DESKTOP, and where we expect those to be, we
21880bbfda8aSnia		 * consider that there's nothing to tell it.  Otherwise, we tell
21890bbfda8aSnia		 * it ABOVE/BELOW based on where it effectively is.
21900bbfda8aSnia		 */
21910bbfda8aSnia		if(twm_win->ewmhWindowType == wt_Dock && pri == EWMH_PRI_DOCK) {
21920bbfda8aSnia			pri = 0;
21930bbfda8aSnia		}
21940bbfda8aSnia		if(twm_win->ewmhWindowType == wt_Desktop && pri == EWMH_PRI_DESKTOP) {
21950bbfda8aSnia			pri = 0;
21960bbfda8aSnia		}
21970bbfda8aSnia
21980bbfda8aSnia		if(pri > 0) {
21990bbfda8aSnia			twm_win->ewmhFlags |= EWMH_STATE_ABOVE;
22000bbfda8aSnia		}
22010bbfda8aSnia		else if(pri < 0) {
22020bbfda8aSnia			twm_win->ewmhFlags |= EWMH_STATE_BELOW;
22030bbfda8aSnia		}
22040bbfda8aSnia	}
22050bbfda8aSnia
22060bbfda8aSnia	flags = twm_win->ewmhFlags;
22070bbfda8aSnia	i = 0;
22080bbfda8aSnia
22090bbfda8aSnia	if(flags & EWMH_STATE_MAXIMIZED_VERT) {
22100bbfda8aSnia		prop[i++] = XA__NET_WM_STATE_MAXIMIZED_VERT;
22110bbfda8aSnia	}
22120bbfda8aSnia	if(flags & EWMH_STATE_MAXIMIZED_HORZ) {
22130bbfda8aSnia		prop[i++] = XA__NET_WM_STATE_MAXIMIZED_HORZ;
22140bbfda8aSnia	}
22150bbfda8aSnia	if(flags & EWMH_STATE_FULLSCREEN) {
22160bbfda8aSnia		prop[i++] = XA__NET_WM_STATE_FULLSCREEN;
22170bbfda8aSnia	}
22180bbfda8aSnia	if(flags & EWMH_STATE_SHADED) {
22190bbfda8aSnia		prop[i++] = XA__NET_WM_STATE_SHADED;
22200bbfda8aSnia	}
22210bbfda8aSnia	if(flags & EWMH_STATE_ABOVE) {
22220bbfda8aSnia		prop[i++] = XA__NET_WM_STATE_ABOVE;
22230bbfda8aSnia	}
22240bbfda8aSnia	if(flags & EWMH_STATE_BELOW) {
22250bbfda8aSnia		prop[i++] = XA__NET_WM_STATE_BELOW;
22260bbfda8aSnia	}
22270bbfda8aSnia
22280bbfda8aSnia	XChangeProperty(dpy, twm_win->w, XA__NET_WM_STATE, XA_ATOM, 32,
22290bbfda8aSnia	                PropModeReplace, (unsigned char *)prop, i);
22300bbfda8aSnia}
22310bbfda8aSnia
22320bbfda8aSnia/*
22330bbfda8aSnia * Get the initial state of _NET_WM_STATE.
22340bbfda8aSnia *
22350bbfda8aSnia * Only some of the flags are supported when initially creating a window.
22360bbfda8aSnia */
22370bbfda8aSniastatic int EwmhGet_NET_WM_STATE(TwmWindow *twm_win)
22380bbfda8aSnia{
22390bbfda8aSnia	int flags = 0;
22400bbfda8aSnia	unsigned long *prop;
22410bbfda8aSnia	unsigned long nitems;
22420bbfda8aSnia	int i;
22430bbfda8aSnia
22440bbfda8aSnia	prop = EwmhGetWindowProperties(twm_win->w, XA__NET_WM_STATE, XA_ATOM, &nitems);
22450bbfda8aSnia
22460bbfda8aSnia	if(prop) {
22470bbfda8aSnia		for(i = 0; i < nitems; i++) {
22480bbfda8aSnia			flags |= atomToFlag(prop[i]);
22490bbfda8aSnia		}
22500bbfda8aSnia
22510bbfda8aSnia		XFree(prop);
22520bbfda8aSnia	}
22530bbfda8aSnia
22540bbfda8aSnia	return flags;
22550bbfda8aSnia}
22560bbfda8aSnia
22570bbfda8aSnia/*
22580bbfda8aSnia * Set _NET_WORKAREA.
22590bbfda8aSnia */
22600bbfda8aSniastatic void EwmhSet_NET_WORKAREA(ScreenInfo *scr)
22610bbfda8aSnia{
22620bbfda8aSnia	unsigned long prop[4];
22630bbfda8aSnia
22640bbfda8aSnia	/* x */ prop[0] = scr->BorderLeft;
22650bbfda8aSnia	/* y */ prop[1] = scr->BorderTop;
22660bbfda8aSnia	/* w */ prop[2] = scr->rootw - scr->BorderLeft - scr->BorderRight;
22670bbfda8aSnia	/* h */ prop[3] = scr->rooth - scr->BorderTop - scr->BorderBottom;
22680bbfda8aSnia
22690bbfda8aSnia	XChangeProperty(dpy, Scr->XineramaRoot, XA__NET_WORKAREA, XA_CARDINAL, 32,
22700bbfda8aSnia	                PropModeReplace, (unsigned char *)prop, 4);
22710bbfda8aSnia}
2272