void
persistmonitorstate(Monitor *m)
{
	Client *c;
	unsigned int i;

	setmonitortags(m);
	setmonitorfields(m);

	/* Set client atoms */
	for (i = 1, c = m->clients; c; c = c->next, ++i) {
		c->idx = i;
		persistclientstate(c);
		#if SWALLOW_PATCH
		if (c->swallowing) {
			c->swallowing->idx = i;
			persistclientstate(c->swallowing);
		}
		#endif // SWALLOW_PATCH
	}
}

int
restoremonitorstate(Monitor *m)
{
	return getmonitortags(m) | getmonitorfields(m);
}

void
persistclientstate(Client *c)
{
	setclienttags(c);
	setclientfields(c);
	#if SAVEFLOATS_PATCH
	savewindowfloatposition(c, c->mon);
	#endif // SAVEFLOATS_PATCH
}

int
restoreclientstate(Client *c)
{
	int restored = getclientfields(c);
	getclienttags(c);
	#if SAVEFLOATS_PATCH
	restorewindowfloatposition(c, c->mon ? c->mon : selmon);
	#endif // SAVEFLOATS_PATCH
	return restored;
}

void setmonitorfields(Monitor *m)
{
	#if PERTAG_PATCH
	unsigned int i;
	#endif // PERTAG_PATCH
	char atom[22] = {0};
	Atom monitor_fields;
	#if FLEXTILE_DELUXE_LAYOUT
	unsigned int flextile_deluxe_bitmask;
	#endif // FLEXTILE_DELUXE_LAYOUT

	sprintf(atom, "_DWM_MONITOR_FIELDS_%u", m->num);
	monitor_fields = XInternAtom(dpy, atom, False);

	/* Perists workspace information in 32 bits laid out like this:
	 *
	 * |0|0000|0|0000|0000|0000|0000|0000|000|000
	 * | |    | |    |    |    |    |    |   |-- nmaster
	 * | |    | |    |    |    |    |    |-- nstack
	 * | |    | |    |    |    |    |-- layout
	 * | |    | |    |    |    |-- flextile LAYOUT (split)
	 * | |    | |    |    |-- flextile MASTER
	 * | |    | |    |-- flextile STACK1
	 * | |    | |-- flextile STACK2
	 * | |    |-- flextile mirror layout (indicated by negative layout)
	 * | |
	 * | |-- reserved
	 * |-- showbar
	 */
	#if PERTAG_PATCH
	for (i = 0; i <= NUMTAGS; i++) {
		#if FLEXTILE_DELUXE_LAYOUT
		flextile_deluxe_bitmask = (m->pertag->nstacks[i] & 0x7) << 3;
		if (m->pertag->ltidxs[i][m->pertag->sellts[i]]->arrange == flextile) {
			flextile_deluxe_bitmask |=
				(abs(m->pertag->ltaxis[i][LAYOUT]) & 0xF) << 10 |
				(m->pertag->ltaxis[i][MASTER] & 0xF) << 14 |
				(m->pertag->ltaxis[i][STACK]  & 0xF) << 18 |
				(m->pertag->ltaxis[i][STACK2] & 0xF) << 22 |
				(m->pertag->ltaxis[i][LAYOUT] < 0 ? 1 : 0) << 24;
		}
		#endif // FLEXTILE_DELUXE_L1AYOUT
		uint32_t data[] = {
			#if FLEXTILE_DELUXE_LAYOUT
			flextile_deluxe_bitmask |
			#endif // FLEXTILE_DELUXE_LAYOUT
			(m->pertag->nmasters[i] & 0x7) |
			(getlayoutindex(m->pertag->ltidxs[i][m->pertag->sellts[i]]) & 0xF) << 6 |
			#if PERTAGBAR_PATCH
			m->pertag->showbars[i] << 31
			#else
			m->showbar << 31
			#endif // PERTAGBAR_PATCH
		};

		XChangeProperty(dpy, root, monitor_fields, XA_CARDINAL, 32,
			i ? PropModeAppend : PropModeReplace, (unsigned char *)data, 1);
	}
	#else // !PERTAG_PATCH
	#if FLEXTILE_DELUXE_LAYOUT
	flextile_deluxe_bitmask = (m->nstack & 0x7) << 3;
	if (m->lt[m->sellt]->arrange == flextile) {
		flextile_deluxe_bitmask |=
			(abs(m->ltaxis[LAYOUT]) & 0xF) << 10 |
			(m->ltaxis[MASTER] & 0xF) << 14 |
			(m->ltaxis[STACK]  & 0xF) << 18 |
			(m->ltaxis[STACK2] & 0xF) << 22 |
			(m->ltaxis[LAYOUT] < 0 ? 1 : 0) << 24;
	}
	#endif // FLEXTILE_DELUXE_L1AYOUT
	uint32_t data[] = {
		#if FLEXTILE_DELUXE_LAYOUT
		flextile_deluxe_bitmask |
		#endif // FLEXTILE_DELUXE_LAYOUT
		(m->nmaster & 0x7) |
		(getlayoutindex(m->lt[m->sellt]) & 0xF) << 6 |
		m->showbar << 31
	};

	XChangeProperty(dpy, root, monitor_fields, XA_CARDINAL, 32, PropModeReplace,
		(unsigned char *)data, 1);
	#endif // PERTAG_PATCH
}

int
getlayoutindex(const Layout *layout)
{
	int i;

	for (i = 0; i < LENGTH(layouts) && &layouts[i] != layout; i++);
	if (i == LENGTH(layouts))
		i = 0;
	return i;
}

int
getmonitorfields(Monitor *m)
{
	int di, layout_index;
	#if PERTAG_PATCH
	unsigned int i, restored = 0;
	unsigned int tags = m->tagset[m->seltags] << 1;
	#endif // PERTAG_PATCH
	unsigned long dl, nitems;
	unsigned char *p = NULL;
	char atom[22] = {0};
	Atom da, state = None;

	sprintf(atom, "_DWM_MONITOR_FIELDS_%u", m->num);
	Atom dwm_monitor = XInternAtom(dpy, atom, False);
	if (!dwm_monitor)
		return 0;

	#if PERTAG_PATCH
	for (i = 0; i <= NUMTAGS; i++) {
		if (!(XGetWindowProperty(dpy, root, dwm_monitor, i, (NUMTAGS + 1) * sizeof dl,
				False, AnyPropertyType, &da, &di, &nitems, &dl, &p) == Success && p)) {
			break;
		}

		if (!nitems) {
			XFree(p);
			break;
		}

		/* See bit layout in the persistmonitorstate function */
		state = *(Atom *)p;

		m->pertag->nmasters[i] = state & 0x7;
		layout_index = (state >> 6) & 0xF;
		if (layout_index < LENGTH(layouts))
			m->pertag->ltidxs[i][m->pertag->sellts[i]] = &layouts[layout_index];
		#if FLEXTILE_DELUXE_LAYOUT
		m->pertag->nstacks[i] = (state >> 3) & 0x7;
		if (m->pertag->ltidxs[i][m->pertag->sellts[i]]->arrange == flextile) {
			m->pertag->ltaxis[i][LAYOUT] = (state >> 10) & 0xF;
			m->pertag->ltaxis[i][MASTER] = (state >> 14) & 0xF;
			m->pertag->ltaxis[i][STACK]  = (state >> 18) & 0xF;
			m->pertag->ltaxis[i][STACK2] = (state >> 22) & 0xF;
			if (state >> 24 & 0x1) {
				m->pertag->ltaxis[i][LAYOUT] *= -1;
			}
		}
		#endif // FLEXTILE_DELUXE_LAYOUT
		#if PERTAGBAR_PATCH
		m->pertag->showbars[i] = (state >> 31) & 0x1;
		#endif // PERTAGBAR_PATCH

		if (!restored && i && (tags & (1 << i))) {
			m->nmaster = m->pertag->nmasters[i];
			m->sellt = m->pertag->sellts[i];
			m->lt[m->sellt] = m->pertag->ltidxs[i][m->sellt];
			#if FLEXTILE_DELUXE_LAYOUT
			m->nstack = m->pertag->nstacks[i];
			if (m->lt[m->sellt]->arrange == flextile) {
				m->ltaxis[LAYOUT] = m->pertag->ltaxis[i][LAYOUT];
				m->ltaxis[MASTER] = m->pertag->ltaxis[i][MASTER];
				m->ltaxis[STACK]  = m->pertag->ltaxis[i][STACK];
				m->ltaxis[STACK2] = m->pertag->ltaxis[i][STACK2];
			}
			#endif // FLEXTILE_DELUXE_LAYOUT
			#if PERTAGBAR_PATCH
			m->showbar = m->pertag->showbars[i];
			#else
			m->showbar = (state >> 31) & 0x1;
			#endif // PERTAGBAR_PATCH
			restored = 1;
		}

		XFree(p);
	}

	return restored;
	#else // !PERTAG_PATCH
	if (!(XGetWindowProperty(dpy, root, dwm_monitor, 0L, sizeof dl,
			False, AnyPropertyType, &da, &di, &nitems, &dl, &p) == Success && p)) {
		return 0;
	}

	if (nitems) {
		state = *(Atom *)p;

		/* See bit layout in the persistmonitorstate function */
		m->nmaster = state & 0x7;
		#if FLEXTILE_DELUXE_LAYOUT
		m->nstack = (state >> 3) & 0x7;
		#endif // FLEXTILE_DELUXE_LAYOUT
		layout_index = (state >> 6) & 0xF;
		if (layout_index < LENGTH(layouts))
			m->lt[m->sellt] = &layouts[layout_index];
		#if FLEXTILE_DELUXE_LAYOUT
		if (m->lt[m->sellt]->arrange == flextile) {
			m->ltaxis[LAYOUT] = (state >> 10) & 0xF;
			m->ltaxis[MASTER] = (state >> 14) & 0xF;
			m->ltaxis[STACK]  = (state >> 18) & 0xF;
			m->ltaxis[STACK2] = (state >> 22) & 0xF;
		}
		#endif // FLEXTILE_DELUXE_LAYOUT
		m->showbar = (state >> 31) & 0x1;
	}

	XFree(p);
	return 1;
	#endif // PERTAG_PATCH
}

void
setmonitortags(Monitor *m)
{
	char atom[22] = {0};
	Atom monitor_tags;

	sprintf(atom, "_DWM_MONITOR_TAGS_%u", m->num);
	monitor_tags = XInternAtom(dpy, atom, False);

	uint32_t data[] = { m->tagset[m->seltags] };
	XChangeProperty(dpy, root, monitor_tags, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1);
}

int
getmonitortags(Monitor *m)
{
	int di;
	unsigned long dl, nitems;
	unsigned char *p = NULL;
	char atom[22] = {0};
	Atom da, monitor_tags = None, tags;

	sprintf(atom, "_DWM_MONITOR_TAGS_%u", m->num);
	monitor_tags = XInternAtom(dpy, atom, False);

	if (!(XGetWindowProperty(dpy, root, monitor_tags, 0L, sizeof dl,
			False, AnyPropertyType, &da, &di, &nitems, &dl, &p) == Success && p)) {
		return 0;
	}

	if (nitems) {
		tags = *(Atom *)p;
		m->tagset[m->seltags] = tags & TAGMASK;
	}

	XFree(p);
	return 1;
}

void
setclientfields(Client *c)
{
	/* Perists client information in 32 bits laid out like this:
	 *
	 * |00000000|00000|0|0|0|0|0|0|0|0|00000000|000
	 * |        |     | | | | | | | | |        |-- monitor index
	 * |        |     | | | | | | | | |-- client index
	 * |        |     | | | | | | | |-- isfloating
	 * |        |     | | | | | | |-- ispermanent
	 * |        |     | | | | | |-- isterminal
	 * |        |     | | | | |-- noswallow
	 * |        |     | | | |-- issteam
	 * |        |     | | |-- issticky
	 * |        |     | |-- fakefullscreen
	 * |        |     |-- isfreesize
	 * |        |
	 * |        |-- reserved
	 * |-- scratchkey (for scratchpads)
	 */
	uint32_t data[] = {
		(c->mon->num & 0x7)
		| (c->idx & 0xFF) << 3
		| (c->isfloating & 0x1) << 11
		#if ISPERMANENT_PATCH
		| (c->ispermanent & 0x1) << 12
		#endif // ISPERMANENT_PATCH
		#if SWALLOW_PATCH
		| (c->isterminal & 0x1) << 13
		| (c->noswallow & 0x1) << 14
		#endif // SWALLOW_PATCH
		#if STEAM_PATCH
		| (c->issteam & 0x1) << 15
		#endif // STEAM_PATCH
		#if STICKY_PATCH
		| (c->issticky & 0x1) << 16
		#endif // STICKY_PATCH
		#if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH
		| (c->fakefullscreen & 0x1) << 17
		#endif // FAKEFULLSCREEN_CLIENT_PATCH
		#if SIZEHINTS_ISFREESIZE_PATCH
		| (c->isfreesize & 0x1) << 18
		#endif // SIZEHINTS_ISFREESIZE_PATCH
		#if RENAMED_SCRATCHPADS_PATCH
		| (c->scratchkey & 0xFF) << 24
		#endif // RENAMED_SCRATCHPADS_PATCH
	};
	XChangeProperty(dpy, c->win, clientatom[ClientFields], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1);
}

int
getclientfields(Client *c)
{
	Monitor *m;
	Atom fields = getatomprop(c, clientatom[ClientFields], AnyPropertyType);
	if (fields == None)
		return 0;

	/* See bit layout in the setclientfields function */
	for (m = mons; m; m = m->next)
		if (m->num == (fields & 0x7)) {
			c->mon = m;
			break;
		}
	c->idx = (fields >> 3) & 0xFF;
	c->isfloating = (fields >> 11) & 0x1;
	#if ISPERMANENT_PATCH
	c->ispermanent = (fields >> 12) & 0x1;
	#endif // ISPERMANENT_PATCH
	#if SWALLOW_PATCH
	c->isterminal = (fields >> 13) & 0x1;
	c->noswallow = (fields >> 14) & 0x1;
	#endif // SWALLOW_PATCH
	#if STEAM_PATCH
	c->issteam = (fields >> 15) & 0x1;
	#endif // STEAM_PATCH
	#if STICKY_PATCH
	c->issticky = (fields >> 16) & 0x1;
	#endif // STICKY_PATCH
	#if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH
	c->fakefullscreen = (fields >> 17) & 0x1;
	#endif // FAKEFULLSCREEN_CLIENT_PATCH
	#if SIZEHINTS_ISFREESIZE_PATCH
	c->isfreesize = (fields >> 18) & 0x1;
	#endif // SIZEHINTS_ISFREESIZE_PATCH
	#if RENAMED_SCRATCHPADS_PATCH
	c->scratchkey = (fields >> 24) & 0xFF;
	#endif // RENAMED_SCRATCHPADS_PATCH
	return 1;
}

void
setclienttags(Client *c)
{
	uint32_t data[] = { c->tags };
	XChangeProperty(dpy, c->win, clientatom[ClientTags], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1);
}

int
getclienttags(Client *c)
{
	Atom tags = getatomprop(c, clientatom[ClientTags], AnyPropertyType);
	if (tags == None)
		return 0;

	c->tags = tags & TAGMASK;
	return 1;
}

#if SAVEFLOATS_PATCH
void
savewindowfloatposition(Client *c, Monitor *m)
{
	char atom[22] = {0};
	if (c->sfx == -9999)
		return;

	sprintf(atom, "_DWM_FLOATPOS_%u", m->num);
	uint32_t pos[] = { (MAX(c->sfx - m->mx, 0) & 0xffff) | ((MAX(c->sfy - m->my, 0) & 0xffff) << 16) };
	XChangeProperty(dpy, c->win, XInternAtom(dpy, atom, False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pos, 1);

	sprintf(atom, "_DWM_FLOATSIZE_%u", m->num);
	uint32_t size[] = { (c->sfw & 0xffff) | ((c->sfh & 0xffff) << 16) };
	XChangeProperty(dpy, c->win, XInternAtom(dpy, atom, False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)size, 1);

	XSync(dpy, False);
}

int
restorewindowfloatposition(Client *c, Monitor *m)
{
	char atom[22] = {0};
	Atom key, value;
	int x, y, w, h;

	if (m == NULL)
		return 0;

	sprintf(atom, "_DWM_FLOATPOS_%u", m->num);

	key = XInternAtom(dpy, atom, False);
	if (!key)
		return 0;

	value = getatomprop(c, key, AnyPropertyType);
	if (!value)
		return 0;

	x = value & 0xffff;
	y = value >> 16;

	sprintf(atom, "_DWM_FLOATSIZE_%u", m->num);

	key = XInternAtom(dpy, atom, False);
	if (!key)
		return 0;

	value = getatomprop(c, key, AnyPropertyType);
	if (!value)
		return 0;

	w = value & 0xffff;
	h = value >> 16;

	if (w <= 0 || h <= 0) {
		fprintf(stderr, "restorewindowfloatposition: bad float values x = %d, y = %d, w = %d, h = %d for client = %s\n", x, y, w, h, c->name);
		return 0;
	}

	c->sfx = m->mx + x;
	c->sfy = m->my + y;
	c->sfw = w;
	c->sfh = h;

	if (c->isfloating) {
		c->x = c->sfx;
		c->y = c->sfy;
		c->w = c->sfw;
		c->h = c->sfh;
	}

	return 1;
}
#endif // SAVEFLOATS_PATCH
