1 /*
2  * calmwm - the calm window manager
3  *
4  * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * $OpenBSD$
19  */
20 
21 #include <sys/types.h>
22 #include "queue.h"
23 
24 #include <err.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "calmwm.h"
33 
34 void
xu_ptr_get(Window win,int * x,int * y)35 xu_ptr_get(Window win, int *x, int *y)
36 {
37 	Window		 w0, w1;
38 	int		 tmp0, tmp1;
39 	unsigned int	 tmp2;
40 
41 	XQueryPointer(X_Dpy, win, &w0, &w1, &tmp0, &tmp1, x, y, &tmp2);
42 }
43 
44 void
xu_ptr_set(Window win,int x,int y)45 xu_ptr_set(Window win, int x, int y)
46 {
47 	XWarpPointer(X_Dpy, None, win, 0, 0, 0, 0, x, y);
48 }
49 
50 int
xu_get_prop(Window win,Atom atm,Atom type,long len,unsigned char ** p)51 xu_get_prop(Window win, Atom atm, Atom type, long len, unsigned char **p)
52 {
53 	Atom		 realtype;
54 	unsigned long	 n, extra;
55 	int		 format;
56 
57 	if (XGetWindowProperty(X_Dpy, win, atm, 0L, len, False, type,
58 	    &realtype, &format, &n, &extra, p) != Success || *p == NULL)
59 		return -1;
60 
61 	if (n == 0)
62 		XFree(*p);
63 
64 	return n;
65 }
66 
67 int
xu_get_strprop(Window win,Atom atm,char ** text)68 xu_get_strprop(Window win, Atom atm, char **text) {
69 	XTextProperty	 prop;
70 	char		**list;
71 	int		 nitems = 0;
72 
73 	*text = NULL;
74 
75 	XGetTextProperty(X_Dpy, win, &prop, atm);
76 	if (!prop.nitems) {
77 		XFree(prop.value);
78 		return 0;
79 	}
80 
81 	if (Xutf8TextPropertyToTextList(X_Dpy, &prop, &list,
82 	    &nitems) == Success && nitems > 0 && *list) {
83 		if (nitems > 1) {
84 			XTextProperty    prop2;
85 			if (Xutf8TextListToTextProperty(X_Dpy, list, nitems,
86 			    XUTF8StringStyle, &prop2) == Success) {
87 				*text = xstrdup((const char *)prop2.value);
88 				XFree(prop2.value);
89 			}
90 		} else {
91 			*text = xstrdup(*list);
92 		}
93 		XFreeStringList(list);
94 	}
95 	XFree(prop.value);
96 
97 	return nitems;
98 }
99 
100 void
xu_send_clientmsg(Window win,Atom proto,Time ts)101 xu_send_clientmsg(Window win, Atom proto, Time ts)
102 {
103 	XClientMessageEvent	 cm;
104 
105 	(void)memset(&cm, 0, sizeof(cm));
106 	cm.type = ClientMessage;
107 	cm.window = win;
108 	cm.message_type = cwmh[WM_PROTOCOLS];
109 	cm.format = 32;
110 	cm.data.l[0] = proto;
111 	cm.data.l[1] = ts;
112 
113 	XSendEvent(X_Dpy, win, False, NoEventMask, (XEvent *)&cm);
114 }
115 
116 void
xu_get_wm_state(Window win,long * state)117 xu_get_wm_state(Window win, long *state)
118 {
119 	long	*p;
120 
121 	*state = -1;
122 	if (xu_get_prop(win, cwmh[WM_STATE], cwmh[WM_STATE], 2L,
123 	    (unsigned char **)&p) > 0) {
124 		*state = *p;
125 		XFree(p);
126 	}
127 }
128 
129 void
xu_set_wm_state(Window win,long state)130 xu_set_wm_state(Window win, long state)
131 {
132 	long	 data[] = { state, None };
133 
134 	XChangeProperty(X_Dpy, win, cwmh[WM_STATE], cwmh[WM_STATE], 32,
135 	    PropModeReplace, (unsigned char *)data, 2);
136 }
137 void
xu_xorcolor(XftColor a,XftColor b,XftColor * r)138 xu_xorcolor(XftColor a, XftColor b, XftColor *r)
139 {
140 	r->pixel = a.pixel ^ b.pixel;
141 	r->color.red = a.color.red ^ b.color.red;
142 	r->color.green = a.color.green ^ b.color.green;
143 	r->color.blue = a.color.blue ^ b.color.blue;
144 	r->color.alpha = 0xffff;
145 }
146 
147 void
xu_atom_init(void)148 xu_atom_init(void)
149 {
150 	char *cwmhints[] = {
151 		"WM_STATE",
152 		"WM_DELETE_WINDOW",
153 		"WM_TAKE_FOCUS",
154 		"WM_PROTOCOLS",
155 		"_MOTIF_WM_HINTS",
156 		"UTF8_STRING",
157 		"WM_CHANGE_STATE",
158 	};
159 	char *ewmhints[] = {
160 		"_NET_SUPPORTED",
161 		"_NET_SUPPORTING_WM_CHECK",
162 		"_NET_ACTIVE_WINDOW",
163 		"_NET_CLIENT_LIST",
164 		"_NET_CLIENT_LIST_STACKING",
165 		"_NET_NUMBER_OF_DESKTOPS",
166 		"_NET_CURRENT_DESKTOP",
167 		"_NET_DESKTOP_VIEWPORT",
168 		"_NET_DESKTOP_GEOMETRY",
169 		"_NET_VIRTUAL_ROOTS",
170 		"_NET_SHOWING_DESKTOP",
171 		"_NET_DESKTOP_NAMES",
172 		"_NET_WORKAREA",
173 		"_NET_WM_NAME",
174 		"_NET_WM_DESKTOP",
175 		"_NET_CLOSE_WINDOW",
176 		"_NET_WM_STATE",
177 		"_NET_WM_STATE_STICKY",
178 		"_NET_WM_STATE_MAXIMIZED_VERT",
179 		"_NET_WM_STATE_MAXIMIZED_HORZ",
180 		"_NET_WM_STATE_HIDDEN",
181 		"_NET_WM_STATE_FULLSCREEN",
182 		"_NET_WM_STATE_DEMANDS_ATTENTION",
183 		"_NET_WM_STATE_SKIP_PAGER",
184 		"_NET_WM_STATE_SKIP_TASKBAR",
185 		"_CWM_WM_STATE_FREEZE",
186 	};
187 
188 	XInternAtoms(X_Dpy, cwmhints, nitems(cwmhints), False, cwmh);
189 	XInternAtoms(X_Dpy, ewmhints, nitems(ewmhints), False, ewmh);
190 }
191 
192 /* Root Window Properties */
193 void
xu_ewmh_net_supported(struct screen_ctx * sc)194 xu_ewmh_net_supported(struct screen_ctx *sc)
195 {
196 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED],
197 	    XA_ATOM, 32, PropModeReplace, (unsigned char *)ewmh, EWMH_NITEMS);
198 }
199 
200 void
xu_ewmh_net_supported_wm_check(struct screen_ctx * sc)201 xu_ewmh_net_supported_wm_check(struct screen_ctx *sc)
202 {
203 	Window	 w;
204 
205 	w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0);
206 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK],
207 	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
208 	XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK],
209 	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
210 	XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME],
211 	    cwmh[UTF8_STRING], 8, PropModeReplace,
212 	    (unsigned char *)Conf.wmname, strlen(Conf.wmname));
213 }
214 
215 void
xu_ewmh_net_desktop_geometry(struct screen_ctx * sc)216 xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
217 {
218 	long	 geom[2] = { sc->view.w, sc->view.h };
219 
220 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY],
221 	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
222 }
223 
224 void
xu_ewmh_net_desktop_viewport(struct screen_ctx * sc)225 xu_ewmh_net_desktop_viewport(struct screen_ctx *sc)
226 {
227 	long	 viewports[2] = {0, 0};
228 
229 	/* We don't support large desktops, so this is (0, 0). */
230 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT],
231 	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
232 }
233 
234 void
xu_ewmh_net_workarea(struct screen_ctx * sc)235 xu_ewmh_net_workarea(struct screen_ctx *sc)
236 {
237 	unsigned long	*workarea;
238 	int		 i, ngroups = Conf.ngroups;
239 
240 	workarea = xreallocarray(NULL, ngroups * 4, sizeof(unsigned long));
241 	for (i = 0; i < ngroups; i++) {
242 		workarea[4 * i + 0] = sc->work.x;
243 		workarea[4 * i + 1] = sc->work.y;
244 		workarea[4 * i + 2] = sc->work.w;
245 		workarea[4 * i + 3] = sc->work.h;
246 	}
247 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA],
248 	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workarea,
249 	    ngroups * 4);
250 	free(workarea);
251 }
252 
253 void
xu_ewmh_net_client_list(struct screen_ctx * sc)254 xu_ewmh_net_client_list(struct screen_ctx *sc)
255 {
256 	struct client_ctx	*cc;
257 	Window			*winlist;
258 	int			 i = 0, j = 0;
259 
260 	TAILQ_FOREACH(cc, &sc->clientq, entry)
261 		i++;
262 	if (i == 0)
263 		return;
264 
265 	winlist = xreallocarray(NULL, i, sizeof(*winlist));
266 	TAILQ_FOREACH(cc, &sc->clientq, entry)
267 		winlist[j++] = cc->win;
268 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST],
269 	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
270 	free(winlist);
271 }
272 
273 void
xu_ewmh_net_client_list_stacking(struct screen_ctx * sc)274 xu_ewmh_net_client_list_stacking(struct screen_ctx *sc)
275 {
276 	struct client_ctx	*cc;
277 	Window			*winlist;
278 	int			 i = 0, j;
279 
280 	TAILQ_FOREACH(cc, &sc->clientq, entry)
281 		i++;
282 	if (i == 0)
283 		return;
284 
285 	j = i;
286 	winlist = xreallocarray(NULL, i, sizeof(*winlist));
287 	TAILQ_FOREACH(cc, &sc->clientq, entry)
288 		winlist[--j] = cc->win;
289 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST_STACKING],
290 	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
291 	free(winlist);
292 }
293 
294 void
xu_ewmh_net_active_window(struct screen_ctx * sc,Window w)295 xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
296 {
297 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW],
298 	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
299 }
300 
301 void
xu_ewmh_net_number_of_desktops(struct screen_ctx * sc)302 xu_ewmh_net_number_of_desktops(struct screen_ctx *sc)
303 {
304 	long	 ndesks = Conf.ngroups;
305 
306 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS],
307 	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
308 }
309 
310 void
xu_ewmh_net_showing_desktop(struct screen_ctx * sc)311 xu_ewmh_net_showing_desktop(struct screen_ctx *sc)
312 {
313 	long	 zero = 0;
314 
315 	/* We don't support `showing desktop' mode, so this is zero.
316 	 * Note that when we hide all groups, or when all groups are
317 	 * hidden we could technically set this later on.
318 	 */
319 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP],
320 	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
321 }
322 
323 void
xu_ewmh_net_virtual_roots(struct screen_ctx * sc)324 xu_ewmh_net_virtual_roots(struct screen_ctx *sc)
325 {
326 	/* We don't support virtual roots, so delete if set by previous wm. */
327 	XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS]);
328 }
329 
330 void
xu_ewmh_net_current_desktop(struct screen_ctx * sc)331 xu_ewmh_net_current_desktop(struct screen_ctx *sc)
332 {
333 	long	 num = sc->group_active->num;
334 
335 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP],
336 	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
337 }
338 
339 void
xu_ewmh_net_desktop_names(struct screen_ctx * sc)340 xu_ewmh_net_desktop_names(struct screen_ctx *sc)
341 {
342 	struct group_ctx	*gc;
343 	char			*p, *q;
344 	unsigned char		*prop_ret;
345 	int			 i = 0, j = 0, nstrings = 0, n = 0;
346 	size_t			 len = 0, tlen, slen;
347 
348 	/* Let group names be overwritten if _NET_DESKTOP_NAMES is set. */
349 
350 	if ((j = xu_get_prop(sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
351 	    cwmh[UTF8_STRING], 0xffffff, (unsigned char **)&prop_ret)) > 0) {
352 		prop_ret[j - 1] = '\0'; /* paranoia */
353 		while (i < j) {
354 			if (prop_ret[i++] == '\0')
355 				nstrings++;
356 		}
357 	}
358 
359 	p = (char *)prop_ret;
360 	while (n < nstrings) {
361 		TAILQ_FOREACH(gc, &sc->groupq, entry) {
362 			if (gc->num == n) {
363 				free(gc->name);
364 				gc->name = xstrdup(p);
365 				p += strlen(p) + 1;
366 				break;
367 			}
368 		}
369 		n++;
370 	}
371 	if (prop_ret != NULL)
372 		XFree(prop_ret);
373 
374 	TAILQ_FOREACH(gc, &sc->groupq, entry)
375 		len += strlen(gc->name) + 1;
376 	q = p = xreallocarray(NULL, len, sizeof(*p));
377 
378 	tlen = len;
379 	TAILQ_FOREACH(gc, &sc->groupq, entry) {
380 		slen = strlen(gc->name) + 1;
381 		(void)strlcpy(q, gc->name, tlen);
382 		tlen -= slen;
383 		q += slen;
384 	}
385 
386 	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES],
387 	    cwmh[UTF8_STRING], 8, PropModeReplace, (unsigned char *)p, len);
388 	free(p);
389 }
390 
391 /* Application Window Properties */
392 int
xu_ewmh_get_net_wm_desktop(struct client_ctx * cc,long * n)393 xu_ewmh_get_net_wm_desktop(struct client_ctx *cc, long *n)
394 {
395 	long		*p;
396 
397 	if (xu_get_prop(cc->win, ewmh[_NET_WM_DESKTOP], XA_CARDINAL, 1L,
398 	    (unsigned char **)&p) <= 0)
399 		return 0;
400 	*n = *p;
401 	XFree(p);
402 	return 1;
403 }
404 
405 void
xu_ewmh_set_net_wm_desktop(struct client_ctx * cc)406 xu_ewmh_set_net_wm_desktop(struct client_ctx *cc)
407 {
408 	long	 num = 0xffffffff;
409 
410 	if (cc->gc)
411 		num = cc->gc->num;
412 
413 	XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP],
414 	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num, 1);
415 }
416 
417 Atom *
xu_ewmh_get_net_wm_state(struct client_ctx * cc,int * n)418 xu_ewmh_get_net_wm_state(struct client_ctx *cc, int *n)
419 {
420 	Atom	*state, *p = NULL;
421 
422 	if ((*n = xu_get_prop(cc->win, ewmh[_NET_WM_STATE], XA_ATOM, 64L,
423 	    (unsigned char **)&p)) <= 0)
424 		return NULL;
425 
426 	state = xreallocarray(NULL, *n, sizeof(Atom));
427 	(void)memcpy(state, p, *n * sizeof(Atom));
428 	XFree((char *)p);
429 
430 	return state;
431 }
432 
433 void
xu_ewmh_handle_net_wm_state_msg(struct client_ctx * cc,int action,Atom first,Atom second)434 xu_ewmh_handle_net_wm_state_msg(struct client_ctx *cc, int action,
435     Atom first, Atom second)
436 {
437 	unsigned int i;
438 	struct handlers {
439 		Atom atom;
440 		int flag;
441 		void (*toggle)(struct client_ctx *);
442 	} handlers[] = {
443 		{ _NET_WM_STATE_STICKY,
444 			CLIENT_STICKY,
445 			client_toggle_sticky },
446 		{ _NET_WM_STATE_MAXIMIZED_VERT,
447 			CLIENT_VMAXIMIZED,
448 			client_toggle_vmaximize },
449 		{ _NET_WM_STATE_MAXIMIZED_HORZ,
450 			CLIENT_HMAXIMIZED,
451 			client_toggle_hmaximize },
452 		{ _NET_WM_STATE_HIDDEN,
453 			CLIENT_HIDDEN,
454 			client_toggle_hidden },
455 		{ _NET_WM_STATE_FULLSCREEN,
456 			CLIENT_FULLSCREEN,
457 			client_toggle_fullscreen },
458 		{ _NET_WM_STATE_DEMANDS_ATTENTION,
459 			CLIENT_URGENCY,
460 			client_urgency },
461 		{ _NET_WM_STATE_SKIP_PAGER,
462 			CLIENT_SKIP_PAGER,
463 			client_toggle_skip_pager},
464 		{ _NET_WM_STATE_SKIP_TASKBAR,
465 			CLIENT_SKIP_TASKBAR,
466 			client_toggle_skip_taskbar},
467 		{ _CWM_WM_STATE_FREEZE,
468 			CLIENT_FREEZE,
469 			client_toggle_freeze },
470 	};
471 
472 	for (i = 0; i < nitems(handlers); i++) {
473 		if (first != ewmh[handlers[i].atom] &&
474 		    second != ewmh[handlers[i].atom])
475 			continue;
476 		switch (action) {
477 		case _NET_WM_STATE_ADD:
478 			if (!(cc->flags & handlers[i].flag))
479 				handlers[i].toggle(cc);
480 			break;
481 		case _NET_WM_STATE_REMOVE:
482 			if (cc->flags & handlers[i].flag)
483 				handlers[i].toggle(cc);
484 			break;
485 		case _NET_WM_STATE_TOGGLE:
486 			handlers[i].toggle(cc);
487 		}
488 	}
489 }
490 
491 void
xu_ewmh_restore_net_wm_state(struct client_ctx * cc)492 xu_ewmh_restore_net_wm_state(struct client_ctx *cc)
493 {
494 	Atom	*atoms;
495 	int	 i, n;
496 
497 	atoms = xu_ewmh_get_net_wm_state(cc, &n);
498 	for (i = 0; i < n; i++) {
499 		if (atoms[i] == ewmh[_NET_WM_STATE_STICKY])
500 			client_toggle_sticky(cc);
501 		if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_VERT])
502 			client_toggle_vmaximize(cc);
503 		if (atoms[i] == ewmh[_NET_WM_STATE_MAXIMIZED_HORZ])
504 			client_toggle_hmaximize(cc);
505 		if (atoms[i] == ewmh[_NET_WM_STATE_HIDDEN])
506 			client_toggle_hidden(cc);
507 		if (atoms[i] == ewmh[_NET_WM_STATE_FULLSCREEN])
508 			client_toggle_fullscreen(cc);
509 		if (atoms[i] == ewmh[_NET_WM_STATE_DEMANDS_ATTENTION])
510 			client_urgency(cc);
511 		if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_PAGER])
512 			client_toggle_skip_pager(cc);
513 		if (atoms[i] == ewmh[_NET_WM_STATE_SKIP_TASKBAR])
514 			client_toggle_skip_taskbar(cc);
515 		if (atoms[i] == ewmh[_CWM_WM_STATE_FREEZE])
516 			client_toggle_freeze(cc);
517 	}
518 	free(atoms);
519 }
520 
521 void
xu_ewmh_set_net_wm_state(struct client_ctx * cc)522 xu_ewmh_set_net_wm_state(struct client_ctx *cc)
523 {
524 	Atom	*atoms, *oatoms;
525 	int	 n, i, j;
526 
527 	oatoms = xu_ewmh_get_net_wm_state(cc, &n);
528 	atoms = xreallocarray(NULL, (n + _NET_WM_STATES_NITEMS), sizeof(Atom));
529 	for (i = j = 0; i < n; i++) {
530 		if (oatoms[i] != ewmh[_NET_WM_STATE_STICKY] &&
531 		    oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_VERT] &&
532 		    oatoms[i] != ewmh[_NET_WM_STATE_MAXIMIZED_HORZ] &&
533 		    oatoms[i] != ewmh[_NET_WM_STATE_HIDDEN] &&
534 		    oatoms[i] != ewmh[_NET_WM_STATE_FULLSCREEN] &&
535 		    oatoms[i] != ewmh[_NET_WM_STATE_DEMANDS_ATTENTION] &&
536 		    oatoms[i] != ewmh[_NET_WM_STATE_SKIP_PAGER] &&
537 		    oatoms[i] != ewmh[_NET_WM_STATE_SKIP_TASKBAR] &&
538 		    oatoms[i] != ewmh[_CWM_WM_STATE_FREEZE])
539 			atoms[j++] = oatoms[i];
540 	}
541 	free(oatoms);
542 	if (cc->flags & CLIENT_STICKY)
543 		atoms[j++] = ewmh[_NET_WM_STATE_STICKY];
544 	if (cc->flags & CLIENT_HIDDEN)
545 		atoms[j++] = ewmh[_NET_WM_STATE_HIDDEN];
546 	if (cc->flags & CLIENT_FULLSCREEN)
547 		atoms[j++] = ewmh[_NET_WM_STATE_FULLSCREEN];
548 	else {
549 		if (cc->flags & CLIENT_VMAXIMIZED)
550 			atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_VERT];
551 		if (cc->flags & CLIENT_HMAXIMIZED)
552 			atoms[j++] = ewmh[_NET_WM_STATE_MAXIMIZED_HORZ];
553 	}
554 	if (cc->flags & CLIENT_URGENCY)
555 		atoms[j++] = ewmh[_NET_WM_STATE_DEMANDS_ATTENTION];
556 	if (cc->flags & CLIENT_SKIP_PAGER)
557 		atoms[j++] = ewmh[_NET_WM_STATE_SKIP_PAGER];
558 	if (cc->flags & CLIENT_SKIP_TASKBAR)
559 		atoms[j++] = ewmh[_NET_WM_STATE_SKIP_TASKBAR];
560 	if (cc->flags & CLIENT_FREEZE)
561 		atoms[j++] = ewmh[_CWM_WM_STATE_FREEZE];
562 	if (j > 0)
563 		XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE],
564 		    XA_ATOM, 32, PropModeReplace, (unsigned char *)atoms, j);
565 	else
566 		XDeleteProperty(X_Dpy, cc->win, ewmh[_NET_WM_STATE]);
567 	free(atoms);
568 }
569