1 /*  wmspec.c-- support for the wm-spec Hints
2  *
3  *  Window Maker window manager
4  *
5  *  Copyright (c) 1998-2003 Alfredo K. Kojima
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License along
18  *  with this program; if not, write to the Free Software Foundation, Inc.,
19  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 /*
23  * TODO
24  * ----
25  *
26  * This file needs to be checked for all calls to XGetWindowProperty() and
27  * proper checks need to be made on the returned values. Only checking for
28  * return to be Success is not enough. -Dan
29  */
30 
31 #include "wconfig.h"
32 
33 #include <X11/Xlib.h>
34 #include <X11/Xatom.h>
35 #include <string.h>
36 
37 #include <WINGs/WUtil.h>
38 #include "WindowMaker.h"
39 #include "window.h"
40 #include "screen.h"
41 #include "workspace.h"
42 #include "framewin.h"
43 #include "actions.h"
44 #include "client.h"
45 #include "appicon.h"
46 #include "wmspec.h"
47 #include "icon.h"
48 #include "stacking.h"
49 #include "xinerama.h"
50 #include "properties.h"
51 
52 
53 /* Root Window Properties */
54 static Atom net_supported;
55 static Atom net_client_list;
56 static Atom net_client_list_stacking;
57 static Atom net_number_of_desktops;
58 static Atom net_desktop_geometry;
59 static Atom net_desktop_viewport;
60 static Atom net_current_desktop;
61 static Atom net_desktop_names;
62 static Atom net_active_window;
63 static Atom net_workarea;
64 static Atom net_supporting_wm_check;
65 static Atom net_virtual_roots;	/* N/A */
66 static Atom net_desktop_layout;	/* XXX */
67 static Atom net_showing_desktop;
68 
69 /* Other Root Window Messages */
70 static Atom net_close_window;
71 static Atom net_moveresize_window;	/* TODO */
72 static Atom net_wm_moveresize;	/* TODO */
73 
74 /* Application Window Properties */
75 static Atom net_wm_name;
76 static Atom net_wm_visible_name;	/* TODO (unnecessary?) */
77 static Atom net_wm_icon_name;
78 static Atom net_wm_visible_icon_name;	/* TODO (unnecessary?) */
79 static Atom net_wm_desktop;
80 static Atom net_wm_window_type;
81 static Atom net_wm_window_type_desktop;
82 static Atom net_wm_window_type_dock;
83 static Atom net_wm_window_type_toolbar;
84 static Atom net_wm_window_type_menu;
85 static Atom net_wm_window_type_utility;
86 static Atom net_wm_window_type_splash;
87 static Atom net_wm_window_type_dialog;
88 static Atom net_wm_window_type_dropdown_menu;
89 static Atom net_wm_window_type_popup_menu;
90 static Atom net_wm_window_type_tooltip;
91 static Atom net_wm_window_type_notification;
92 static Atom net_wm_window_type_combo;
93 static Atom net_wm_window_type_dnd;
94 static Atom net_wm_window_type_normal;
95 static Atom net_wm_state;
96 static Atom net_wm_state_modal;	/* XXX: what is this?!? */
97 static Atom net_wm_state_sticky;
98 static Atom net_wm_state_maximized_vert;
99 static Atom net_wm_state_maximized_horz;
100 static Atom net_wm_state_shaded;
101 static Atom net_wm_state_skip_taskbar;
102 static Atom net_wm_state_skip_pager;
103 static Atom net_wm_state_hidden;
104 static Atom net_wm_state_fullscreen;
105 static Atom net_wm_state_above;
106 static Atom net_wm_state_below;
107 static Atom net_wm_allowed_actions;
108 static Atom net_wm_action_move;
109 static Atom net_wm_action_resize;
110 static Atom net_wm_action_minimize;
111 static Atom net_wm_action_shade;
112 static Atom net_wm_action_stick;
113 static Atom net_wm_action_maximize_horz;
114 static Atom net_wm_action_maximize_vert;
115 static Atom net_wm_action_fullscreen;
116 static Atom net_wm_action_change_desktop;
117 static Atom net_wm_action_close;
118 static Atom net_wm_strut;
119 static Atom net_wm_strut_partial;	/* TODO: doesn't really fit into the current strut scheme */
120 static Atom net_wm_icon_geometry;	/* FIXME: should work together with net_wm_handled_icons, gnome-panel-2.2.0.1 doesn't use _NET_WM_HANDLED_ICONS, thus present situation. */
121 static Atom net_wm_icon;
122 static Atom net_wm_pid;		/* TODO */
123 static Atom net_wm_handled_icons;	/* FIXME: see net_wm_icon_geometry */
124 static Atom net_wm_window_opacity;
125 
126 static Atom net_frame_extents;
127 
128 /* Window Manager Protocols */
129 static Atom net_wm_ping;	/* TODO */
130 
131 static Atom utf8_string;
132 
133 typedef struct {
134 	char *name;
135 	Atom *atom;
136 } atomitem_t;
137 
138 static atomitem_t atomNames[] = {
139 	{"_NET_SUPPORTED", &net_supported},
140 	{"_NET_CLIENT_LIST", &net_client_list},
141 	{"_NET_CLIENT_LIST_STACKING", &net_client_list_stacking},
142 	{"_NET_NUMBER_OF_DESKTOPS", &net_number_of_desktops},
143 	{"_NET_DESKTOP_GEOMETRY", &net_desktop_geometry},
144 	{"_NET_DESKTOP_VIEWPORT", &net_desktop_viewport},
145 	{"_NET_CURRENT_DESKTOP", &net_current_desktop},
146 	{"_NET_DESKTOP_NAMES", &net_desktop_names},
147 	{"_NET_ACTIVE_WINDOW", &net_active_window},
148 	{"_NET_WORKAREA", &net_workarea},
149 	{"_NET_SUPPORTING_WM_CHECK", &net_supporting_wm_check},
150 	{"_NET_VIRTUAL_ROOTS", &net_virtual_roots},
151 	{"_NET_DESKTOP_LAYOUT", &net_desktop_layout},
152 	{"_NET_SHOWING_DESKTOP", &net_showing_desktop},
153 
154 	{"_NET_CLOSE_WINDOW", &net_close_window},
155 	{"_NET_MOVERESIZE_WINDOW", &net_moveresize_window},
156 	{"_NET_WM_MOVERESIZE", &net_wm_moveresize},
157 
158 	{"_NET_WM_NAME", &net_wm_name},
159 	{"_NET_WM_VISIBLE_NAME", &net_wm_visible_name},
160 	{"_NET_WM_ICON_NAME", &net_wm_icon_name},
161 	{"_NET_WM_VISIBLE_ICON_NAME", &net_wm_visible_icon_name},
162 	{"_NET_WM_DESKTOP", &net_wm_desktop},
163 	{"_NET_WM_WINDOW_TYPE", &net_wm_window_type},
164 	{"_NET_WM_WINDOW_TYPE_DESKTOP", &net_wm_window_type_desktop},
165 	{"_NET_WM_WINDOW_TYPE_DOCK", &net_wm_window_type_dock},
166 	{"_NET_WM_WINDOW_TYPE_TOOLBAR", &net_wm_window_type_toolbar},
167 	{"_NET_WM_WINDOW_TYPE_MENU", &net_wm_window_type_menu},
168 	{"_NET_WM_WINDOW_TYPE_UTILITY", &net_wm_window_type_utility},
169 	{"_NET_WM_WINDOW_TYPE_SPLASH", &net_wm_window_type_splash},
170 	{"_NET_WM_WINDOW_TYPE_DIALOG", &net_wm_window_type_dialog},
171 	{"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", &net_wm_window_type_dropdown_menu},
172 	{"_NET_WM_WINDOW_TYPE_POPUP_MENU", &net_wm_window_type_popup_menu},
173 	{"_NET_WM_WINDOW_TYPE_TOOLTIP", &net_wm_window_type_tooltip},
174 	{"_NET_WM_WINDOW_TYPE_NOTIFICATION", &net_wm_window_type_notification},
175 	{"_NET_WM_WINDOW_TYPE_COMBO", &net_wm_window_type_combo},
176 	{"_NET_WM_WINDOW_TYPE_DND", &net_wm_window_type_dnd},
177 	{"_NET_WM_WINDOW_TYPE_NORMAL", &net_wm_window_type_normal},
178 	{"_NET_WM_STATE", &net_wm_state},
179 	{"_NET_WM_STATE_MODAL", &net_wm_state_modal},
180 	{"_NET_WM_STATE_STICKY", &net_wm_state_sticky},
181 	{"_NET_WM_STATE_MAXIMIZED_VERT", &net_wm_state_maximized_vert},
182 	{"_NET_WM_STATE_MAXIMIZED_HORZ", &net_wm_state_maximized_horz},
183 	{"_NET_WM_STATE_SHADED", &net_wm_state_shaded},
184 	{"_NET_WM_STATE_SKIP_TASKBAR", &net_wm_state_skip_taskbar},
185 	{"_NET_WM_STATE_SKIP_PAGER", &net_wm_state_skip_pager},
186 	{"_NET_WM_STATE_HIDDEN", &net_wm_state_hidden},
187 	{"_NET_WM_STATE_FULLSCREEN", &net_wm_state_fullscreen},
188 	{"_NET_WM_STATE_ABOVE", &net_wm_state_above},
189 	{"_NET_WM_STATE_BELOW", &net_wm_state_below},
190 	{"_NET_WM_ALLOWED_ACTIONS", &net_wm_allowed_actions},
191 	{"_NET_WM_ACTION_MOVE", &net_wm_action_move},
192 	{"_NET_WM_ACTION_RESIZE", &net_wm_action_resize},
193 	{"_NET_WM_ACTION_MINIMIZE", &net_wm_action_minimize},
194 	{"_NET_WM_ACTION_SHADE", &net_wm_action_shade},
195 	{"_NET_WM_ACTION_STICK", &net_wm_action_stick},
196 	{"_NET_WM_ACTION_MAXIMIZE_HORZ", &net_wm_action_maximize_horz},
197 	{"_NET_WM_ACTION_MAXIMIZE_VERT", &net_wm_action_maximize_vert},
198 	{"_NET_WM_ACTION_FULLSCREEN", &net_wm_action_fullscreen},
199 	{"_NET_WM_ACTION_CHANGE_DESKTOP", &net_wm_action_change_desktop},
200 	{"_NET_WM_ACTION_CLOSE", &net_wm_action_close},
201 	{"_NET_WM_STRUT", &net_wm_strut},
202 	{"_NET_WM_STRUT_PARTIAL", &net_wm_strut_partial},
203 	{"_NET_WM_ICON_GEOMETRY", &net_wm_icon_geometry},
204 	{"_NET_WM_ICON", &net_wm_icon},
205 	{"_NET_WM_PID", &net_wm_pid},
206 	{"_NET_WM_HANDLED_ICONS", &net_wm_handled_icons},
207 	{"_NET_WM_WINDOW_OPACITY", &net_wm_window_opacity},
208 
209 	{"_NET_FRAME_EXTENTS", &net_frame_extents},
210 
211 	{"_NET_WM_PING", &net_wm_ping},
212 
213 	{"UTF8_STRING", &utf8_string},
214 };
215 
216 #define _NET_WM_STATE_ADD 1
217 #define _NET_WM_STATE_TOGGLE 2
218 
219 #if 0
220 /*
221  * These constant provide information on the kind of window move/resize when
222  * it is initiated by the application instead of by WindowMaker. They are
223  * parameter for the client message _NET_WM_MOVERESIZE, as defined by the
224  * FreeDesktop wm-spec standard:
225  *   http://standards.freedesktop.org/wm-spec/1.5/ar01s04.html
226  *
227  * Today, WindowMaker does not support this at all (the corresponding Atom
228  * is not added to the list in setSupportedHints), probably because there is
229  * nothing it needs to do about it, the application is assumed to know what
230  * it is doing, and WindowMaker won't get in the way.
231  *
232  * The definition of the constants (taken from the standard) are disabled to
233  * avoid a spurious warning (-Wunused-macros).
234  */
235 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
236 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
237 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
238 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
239 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
240 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
241 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
242 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
243 #define _NET_WM_MOVERESIZE_MOVE              8	/* movement only */
244 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9	/* size via keyboard */
245 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10	/* move via keyboard */
246 #endif
247 
248 static void observer(void *self, WMNotification *notif);
249 static void wsobserver(void *self, WMNotification *notif);
250 
251 static void updateClientList(WScreen *scr);
252 static void updateClientListStacking(WScreen *scr, WWindow *);
253 
254 static void updateWorkspaceNames(WScreen *scr);
255 static void updateCurrentWorkspace(WScreen *scr);
256 static void updateWorkspaceCount(WScreen *scr);
257 static void wNETWMShowingDesktop(WScreen *scr, Bool show);
258 
259 typedef struct NetData {
260 	WScreen *scr;
261 	WReservedArea *strut;
262 	WWindow **show_desktop;
263 } NetData;
264 
setSupportedHints(WScreen * scr)265 static void setSupportedHints(WScreen *scr)
266 {
267 	Atom atom[wlengthof(atomNames)];
268 	int i = 0;
269 
270 	/* set supported hints list */
271 	/* XXX: extend this !!! */
272 
273 	atom[i++] = net_client_list;
274 	atom[i++] = net_client_list_stacking;
275 	atom[i++] = net_number_of_desktops;
276 	atom[i++] = net_desktop_geometry;
277 	atom[i++] = net_desktop_viewport;
278 	atom[i++] = net_current_desktop;
279 	atom[i++] = net_desktop_names;
280 	atom[i++] = net_active_window;
281 	atom[i++] = net_workarea;
282 	atom[i++] = net_supporting_wm_check;
283 	atom[i++] = net_showing_desktop;
284 #if 0
285 	atom[i++] = net_wm_moveresize;
286 #endif
287 	atom[i++] = net_wm_desktop;
288 	atom[i++] = net_wm_window_type;
289 	atom[i++] = net_wm_window_type_desktop;
290 	atom[i++] = net_wm_window_type_dock;
291 	atom[i++] = net_wm_window_type_toolbar;
292 	atom[i++] = net_wm_window_type_menu;
293 	atom[i++] = net_wm_window_type_utility;
294 	atom[i++] = net_wm_window_type_splash;
295 	atom[i++] = net_wm_window_type_dialog;
296 	atom[i++] = net_wm_window_type_dropdown_menu;
297 	atom[i++] = net_wm_window_type_popup_menu;
298 	atom[i++] = net_wm_window_type_tooltip;
299 	atom[i++] = net_wm_window_type_notification;
300 	atom[i++] = net_wm_window_type_combo;
301 	atom[i++] = net_wm_window_type_dnd;
302 	atom[i++] = net_wm_window_type_normal;
303 
304 	atom[i++] = net_wm_state;
305 	/*    atom[i++] = net_wm_state_modal; *//* XXX: not sure where/when to use it. */
306 	atom[i++] = net_wm_state_sticky;
307 	atom[i++] = net_wm_state_shaded;
308 	atom[i++] = net_wm_state_maximized_horz;
309 	atom[i++] = net_wm_state_maximized_vert;
310 	atom[i++] = net_wm_state_skip_taskbar;
311 	atom[i++] = net_wm_state_skip_pager;
312 	atom[i++] = net_wm_state_hidden;
313 	atom[i++] = net_wm_state_fullscreen;
314 	atom[i++] = net_wm_state_above;
315 	atom[i++] = net_wm_state_below;
316 
317 	atom[i++] = net_wm_allowed_actions;
318 	atom[i++] = net_wm_action_move;
319 	atom[i++] = net_wm_action_resize;
320 	atom[i++] = net_wm_action_minimize;
321 	atom[i++] = net_wm_action_shade;
322 	atom[i++] = net_wm_action_stick;
323 	atom[i++] = net_wm_action_maximize_horz;
324 	atom[i++] = net_wm_action_maximize_vert;
325 	atom[i++] = net_wm_action_fullscreen;
326 	atom[i++] = net_wm_action_change_desktop;
327 	atom[i++] = net_wm_action_close;
328 
329 	atom[i++] = net_wm_strut;
330 	atom[i++] = net_wm_icon_geometry;
331 	atom[i++] = net_wm_icon;
332 	atom[i++] = net_wm_handled_icons;
333 	atom[i++] = net_wm_window_opacity;
334 
335 	atom[i++] = net_frame_extents;
336 
337 	atom[i++] = net_wm_name;
338 	atom[i++] = net_wm_icon_name;
339 
340 	XChangeProperty(dpy, scr->root_win, net_supported, XA_ATOM, 32, PropModeReplace, (unsigned char *)atom, i);
341 
342 	/* set supporting wm hint */
343 	XChangeProperty(dpy, scr->root_win, net_supporting_wm_check, XA_WINDOW, 32,
344 			PropModeReplace, (unsigned char *)&scr->info_window, 1);
345 
346 	XChangeProperty(dpy, scr->info_window, net_supporting_wm_check, XA_WINDOW,
347 			32, PropModeReplace, (unsigned char *)&scr->info_window, 1);
348 }
349 
wNETWMUpdateDesktop(WScreen * scr)350 void wNETWMUpdateDesktop(WScreen *scr)
351 {
352 	long *views, sizes[2];
353 	int count, i;
354 
355 	if (scr->workspace_count == 0)
356 		return;
357 
358 	count = scr->workspace_count * 2;
359 	views = wmalloc(sizeof(long) * count);
360 	sizes[0] = scr->scr_width;
361 	sizes[1] = scr->scr_height;
362 
363 	for (i = 0; i < scr->workspace_count; i++) {
364 		views[2 * i + 0] = 0;
365 		views[2 * i + 1] = 0;
366 	}
367 
368 	XChangeProperty(dpy, scr->root_win, net_desktop_geometry, XA_CARDINAL, 32,
369 			PropModeReplace, (unsigned char *)sizes, 2);
370 
371 	XChangeProperty(dpy, scr->root_win, net_desktop_viewport, XA_CARDINAL, 32,
372 			PropModeReplace, (unsigned char *)views, count);
373 
374 	wfree(views);
375 }
376 
wNETWMGetCurrentDesktopFromHint(WScreen * scr)377 int wNETWMGetCurrentDesktopFromHint(WScreen *scr)
378 {
379 	int count;
380 	unsigned char *prop;
381 
382 	prop = PropGetCheckProperty(scr->root_win, net_current_desktop, XA_CARDINAL, 0, 1, &count);
383 	if (prop) {
384 		int desktop = *(long *)prop;
385 		XFree(prop);
386 		return desktop;
387 	}
388 	return -1;
389 }
390 
makeRImageFromARGBData(unsigned long * data)391 static RImage *makeRImageFromARGBData(unsigned long *data)
392 {
393 	int size, width, height, i;
394 	RImage *image;
395 	unsigned char *imgdata;
396 	unsigned long pixel;
397 
398 	width = data[0];
399 	height = data[1];
400 	size = width * height;
401 
402 	if (size == 0)
403 		return NULL;
404 
405 	image = RCreateImage(width, height, True);
406 
407 	for (imgdata = image->data, i = 2; i < size + 2; i++, imgdata += 4) {
408 		pixel = data[i];
409 		imgdata[3] = (pixel >> 24) & 0xff;	/* A */
410 		imgdata[0] = (pixel >> 16) & 0xff;	/* R */
411 		imgdata[1] = (pixel >> 8) & 0xff;	/* G */
412 		imgdata[2] = (pixel >> 0) & 0xff;	/* B */
413 	}
414 
415 	return image;
416 }
417 
418 /* Find the best icon to be used by Window Maker for appicon/miniwindows. */
findBestIcon(unsigned long * data,unsigned long items)419 static RImage *findBestIcon(unsigned long *data, unsigned long items)
420 {
421 	int wanted;
422 	int dx, dy, d;
423 	int sx, sy, size;
424 	int best_d, largest;
425 	unsigned long i;
426 	unsigned long *icon;
427 	RImage *src_image, *ret_image;
428 	double f;
429 
430 	if (wPreferences.enforce_icon_margin) {
431 
432 		/* better use only 75% of icon_size. For 64x64 this means 48x48
433 		 * This leaves room around the icon for the miniwindow title and
434 		 * results in better overall aesthetics -Dan */
435 		wanted = (int)((double)wPreferences.icon_size * 0.75 + 0.5);
436 
437 		/* the size should be a multiple of 4 */
438 		wanted = (wanted >> 2) << 2;
439 
440 	} else {
441 
442 		/* This is the "old" approach, which tries to find the largest
443 		 * icon that still fits into icon_size. */
444 		wanted = wPreferences.icon_size;
445 
446 	}
447 
448 	/* try to find an icon which is close to the wanted size, but not larger */
449 	icon = NULL;
450 	best_d = wanted * wanted * 2;
451 	for (i = 0L; i < items - 1;) {
452 
453 		/* get the current icon's size */
454 		sx = (int)data[i];
455 		sy = (int)data[i + 1];
456 		if ((sx < 1) || (sy < 1))
457 			break;
458 		size = sx * sy + 2;
459 
460 		/* check the size difference if it's not too large */
461 		if ((sx <= wanted) && (sy <= wanted)) {
462 			dx = wanted - sx;
463 			dy = wanted - sy;
464 			d = (dx * dx) + (dy * dy);
465 			if (d < best_d) {
466 				icon = &data[i];
467 				best_d = d;
468 			}
469 		}
470 
471 		i += size;
472 	}
473 
474 	/* if an icon has been found, no transformation is needed */
475 	if (icon)
476 		return makeRImageFromARGBData(icon);
477 
478 	/* We need to scale down an icon. Find the largest one, for it usually
479 	 * looks better to scale down a large image by a large scale than a
480 	 * small image by a small scale. */
481 	largest = 0;
482 	for (i = 0L; i < items - 1;) {
483 		size = (int)data[i] * (int)data[i + 1];
484 		if (size == 0)
485 			break;
486 		if (size > largest) {
487 			icon = &data[i];
488 			largest = size;
489 		}
490 		i += size + 2;
491 	}
492 
493 	/* give up if there's no icon to work with */
494 	if (!icon)
495 		return NULL;
496 
497 	/* create a scaled down version of the icon */
498 	src_image = makeRImageFromARGBData(icon);
499 	if (src_image->width > src_image->height) {
500 		f = (double)wanted / (double)src_image->width;
501 		ret_image = RScaleImage(src_image, wanted, (int)(f * (double)(src_image->height)));
502 	} else {
503 		f = (double)wanted / (double)src_image->height;
504 		ret_image = RScaleImage(src_image, (int)(f * (double)src_image->width), wanted);
505 	}
506 	RReleaseImage(src_image);
507 	return ret_image;
508 }
509 
get_window_image_from_x11(Window window)510 RImage *get_window_image_from_x11(Window window)
511 {
512 	RImage *image;
513 	Atom type;
514 	int format;
515 	unsigned long items, rest;
516 	unsigned long *property;
517 
518 	/* Get the icon from X11 Window */
519 	if (XGetWindowProperty(dpy, window, net_wm_icon, 0L, LONG_MAX,
520 			       False, XA_CARDINAL, &type, &format, &items, &rest,
521 			       (unsigned char **)&property) != Success || !property)
522 		return NULL;
523 
524 	if (type != XA_CARDINAL || format != 32 || items < 2) {
525 		XFree(property);
526 		return NULL;
527 	}
528 
529 	/* Find the best icon */
530 	image = findBestIcon(property, items);
531 	XFree(property);
532 	if (!image)
533 		return NULL;
534 
535 	/* Resize the image to the correct value */
536 	image = wIconValidateIconSize(image, wPreferences.icon_size);
537 
538 	return image;
539 }
540 
updateIconImage(WWindow * wwin)541 static void updateIconImage(WWindow *wwin)
542 {
543 	/* Remove the icon image from X11 */
544 	if (wwin->net_icon_image)
545 		RReleaseImage(wwin->net_icon_image);
546 
547 	/* Save the icon in the X11 icon */
548 	wwin->net_icon_image = get_window_image_from_x11(wwin->client_win);
549 
550 	/* Refresh the Window Icon */
551 	if (wwin->icon)
552 		wIconUpdate(wwin->icon);
553 
554 	/* Refresh the application icon */
555 	WApplication *app = wApplicationOf(wwin->main_window);
556 	if (app && app->app_icon) {
557 		wIconUpdate(app->app_icon->icon);
558 		wAppIconPaint(app->app_icon);
559 	}
560 }
561 
updateWindowOpacity(WWindow * wwin)562 static void updateWindowOpacity(WWindow *wwin)
563 {
564 	Atom type;
565 	int format;
566 	unsigned long items, rest;
567 	unsigned long *property;
568 
569 	if (!wwin->frame)
570 		return;
571 
572 	/* We don't care about this ourselves, but other programs need us to copy
573 	 * this to the frame window. */
574 	if (XGetWindowProperty(dpy, wwin->client_win, net_wm_window_opacity, 0L, 1L,
575 				False, XA_CARDINAL, &type, &format, &items, &rest,
576 				(unsigned char **)&property) != Success)
577 		return;
578 
579 	if (type == None) {
580 		XDeleteProperty(dpy, wwin->frame->core->window, net_wm_window_opacity);
581 	} else if (type == XA_CARDINAL && format == 32 && items == 1 && property) {
582 		XChangeProperty(dpy, wwin->frame->core->window, net_wm_window_opacity,
583 				XA_CARDINAL, 32, PropModeReplace, (unsigned char *)property, 1L);
584 	}
585 
586 	if (property)
587 		XFree(property);
588 }
589 
updateShowDesktop(WScreen * scr,Bool show)590 static void updateShowDesktop(WScreen *scr, Bool show)
591 {
592 	long foo;
593 
594 	foo = (show == True);
595 	XChangeProperty(dpy, scr->root_win, net_showing_desktop, XA_CARDINAL, 32,
596 			PropModeReplace, (unsigned char *)&foo, 1);
597 }
598 
wNETWMShowingDesktop(WScreen * scr,Bool show)599 static void wNETWMShowingDesktop(WScreen *scr, Bool show)
600 {
601 	if (show && scr->netdata->show_desktop == NULL) {
602 		WWindow *tmp, **wins;
603 		int i = 0;
604 
605 		wins = (WWindow **) wmalloc(sizeof(WWindow *) * (scr->window_count + 1));
606 
607 		tmp = scr->focused_window;
608 		while (tmp) {
609 			if (!tmp->flags.hidden && !tmp->flags.miniaturized && !WFLAGP(tmp, skip_window_list)) {
610 
611 				wins[i++] = tmp;
612 				tmp->flags.skip_next_animation = 1;
613 				tmp->flags.net_show_desktop = 1;
614 				wIconifyWindow(tmp);
615 			}
616 
617 			tmp = tmp->prev;
618 		}
619 		wins[i++] = NULL;
620 
621 		scr->netdata->show_desktop = wins;
622 		updateShowDesktop(scr, True);
623 	} else if (scr->netdata->show_desktop != NULL) {
624 		/* FIXME: get rid of workspace flashing ! */
625 		int ws = scr->current_workspace;
626 		WWindow **tmp;
627 		for (tmp = scr->netdata->show_desktop; *tmp; ++tmp) {
628 			wDeiconifyWindow(*tmp);
629 			(*tmp)->flags.net_show_desktop = 0;
630 		}
631 		if (ws != scr->current_workspace)
632 			wWorkspaceChange(scr, ws);
633 		wfree(scr->netdata->show_desktop);
634 		scr->netdata->show_desktop = NULL;
635 		updateShowDesktop(scr, False);
636 	}
637 }
638 
wNETWMInitStuff(WScreen * scr)639 void wNETWMInitStuff(WScreen *scr)
640 {
641 	NetData *data;
642 	int i;
643 
644 #ifdef DEBUG_WMSPEC
645 	wmessage("wNETWMInitStuff");
646 #endif
647 
648 #ifdef HAVE_XINTERNATOMS
649 	{
650 		Atom atoms[wlengthof(atomNames)];
651 		char *names[wlengthof(atomNames)];
652 
653 		for (i = 0; i < wlengthof(atomNames); ++i)
654 			names[i] = atomNames[i].name;
655 
656 		XInternAtoms(dpy, &names[0], wlengthof(atomNames), False, atoms);
657 		for (i = 0; i < wlengthof(atomNames); ++i)
658 			*atomNames[i].atom = atoms[i];
659 
660 	}
661 #else
662 	for (i = 0; i < wlengthof(atomNames); i++)
663 		*atomNames[i].atom = XInternAtom(dpy, atomNames[i].name, False);
664 #endif
665 
666 	data = wmalloc(sizeof(NetData));
667 	data->scr = scr;
668 	data->strut = NULL;
669 	data->show_desktop = NULL;
670 
671 	scr->netdata = data;
672 
673 	setSupportedHints(scr);
674 
675 	WMAddNotificationObserver(observer, data, WMNManaged, NULL);
676 	WMAddNotificationObserver(observer, data, WMNUnmanaged, NULL);
677 	WMAddNotificationObserver(observer, data, WMNChangedWorkspace, NULL);
678 	WMAddNotificationObserver(observer, data, WMNChangedState, NULL);
679 	WMAddNotificationObserver(observer, data, WMNChangedFocus, NULL);
680 	WMAddNotificationObserver(observer, data, WMNChangedStacking, NULL);
681 	WMAddNotificationObserver(observer, data, WMNChangedName, NULL);
682 
683 	WMAddNotificationObserver(wsobserver, data, WMNWorkspaceCreated, NULL);
684 	WMAddNotificationObserver(wsobserver, data, WMNWorkspaceDestroyed, NULL);
685 	WMAddNotificationObserver(wsobserver, data, WMNWorkspaceChanged, NULL);
686 	WMAddNotificationObserver(wsobserver, data, WMNWorkspaceNameChanged, NULL);
687 
688 	updateClientList(scr);
689 	updateClientListStacking(scr, NULL);
690 	updateWorkspaceCount(scr);
691 	updateWorkspaceNames(scr);
692 	updateShowDesktop(scr, False);
693 
694 	wScreenUpdateUsableArea(scr);
695 }
696 
wNETWMCleanup(WScreen * scr)697 void wNETWMCleanup(WScreen *scr)
698 {
699 	int i;
700 
701 	for (i = 0; i < wlengthof(atomNames); i++)
702 		XDeleteProperty(dpy, scr->root_win, *atomNames[i].atom);
703 }
704 
wNETWMUpdateActions(WWindow * wwin,Bool del)705 void wNETWMUpdateActions(WWindow *wwin, Bool del)
706 {
707 	Atom action[10];	/* nr of actions atoms defined */
708 	int i = 0;
709 
710 	if (del) {
711 		XDeleteProperty(dpy, wwin->client_win, net_wm_allowed_actions);
712 		return;
713 	}
714 
715 	if (IS_MOVABLE(wwin))
716 		action[i++] = net_wm_action_move;
717 
718 	if (IS_RESIZABLE(wwin))
719 		action[i++] = net_wm_action_resize;
720 
721 	if (!WFLAGP(wwin, no_miniaturizable))
722 		action[i++] = net_wm_action_minimize;
723 
724 	if (!WFLAGP(wwin, no_shadeable))
725 		action[i++] = net_wm_action_shade;
726 
727 	/*    if (!WFLAGP(wwin, no_stickable)) */
728 	action[i++] = net_wm_action_stick;
729 
730 	/*    if (!(WFLAGP(wwin, no_maximizeable) & MAX_HORIZONTAL)) */
731 	if (IS_RESIZABLE(wwin))
732 		action[i++] = net_wm_action_maximize_horz;
733 
734 	/*    if (!(WFLAGP(wwin, no_maximizeable) & MAX_VERTICAL)) */
735 	if (IS_RESIZABLE(wwin))
736 		action[i++] = net_wm_action_maximize_vert;
737 
738 	/*    if (!WFLAGP(wwin, no_fullscreen)) */
739 	action[i++] = net_wm_action_fullscreen;
740 
741 	/*    if (!WFLAGP(wwin, no_change_desktop)) */
742 	action[i++] = net_wm_action_change_desktop;
743 
744 	if (!WFLAGP(wwin, no_closable))
745 		action[i++] = net_wm_action_close;
746 
747 	XChangeProperty(dpy, wwin->client_win, net_wm_allowed_actions,
748 			XA_ATOM, 32, PropModeReplace, (unsigned char *)action, i);
749 }
750 
wNETWMUpdateWorkarea(WScreen * scr)751 void wNETWMUpdateWorkarea(WScreen *scr)
752 {
753 	WArea total_usable;
754 	int nb_workspace;
755 
756 	if (!scr->netdata) {
757 		/* If the _NET_xxx were not initialised, it not necessary to do anything */
758 		return;
759 	}
760 
761 	if (!scr->usableArea) {
762 		/* If we don't have any info, we fall back on using the complete screen area */
763 		total_usable.x1 = 0;
764 		total_usable.y1 = 0;
765 		total_usable.x2 = scr->scr_width;
766 		total_usable.y2 = scr->scr_height;
767 
768 	} else {
769 		int i;
770 
771 		/*
772 		 * the _NET_WORKAREA is supposed to contain the total area of the screen that
773 		 * is usable, so we merge the areas from all xrandr sub-screens
774 		 */
775 		total_usable = scr->usableArea[0];
776 
777 		for (i = 1; i < wXineramaHeads(scr); i++) {
778 			/* The merge is not subtle because _NET_WORKAREA does not need more */
779 			if (scr->usableArea[i].x1 < total_usable.x1)
780 				total_usable.x1 = scr->usableArea[i].x1;
781 
782 			if (scr->usableArea[i].y1 < total_usable.y1)
783 				total_usable.y1 = scr->usableArea[i].y1;
784 
785 			if (scr->usableArea[i].x2 > total_usable.x2)
786 				total_usable.x2 = scr->usableArea[i].x2;
787 
788 			if (scr->usableArea[i].y2 > total_usable.y2)
789 				total_usable.y2 = scr->usableArea[i].y2;
790 		}
791 
792 	}
793 
794 	/* We are expected to repeat the information for each workspace */
795 	if (scr->workspace_count == 0)
796 		nb_workspace = 1;
797 	else
798 		nb_workspace = scr->workspace_count;
799 
800 	{
801 		long property_value[nb_workspace * 4];
802 		int i;
803 
804 		for (i = 0; i < nb_workspace; i++) {
805 			property_value[4 * i + 0] = total_usable.x1;
806 			property_value[4 * i + 1] = total_usable.y1;
807 			property_value[4 * i + 2] = total_usable.x2 - total_usable.x1;
808 			property_value[4 * i + 3] = total_usable.y2 - total_usable.y1;
809 		}
810 
811 		XChangeProperty(dpy, scr->root_win, net_workarea, XA_CARDINAL, 32, PropModeReplace,
812 		                (unsigned char *) property_value, nb_workspace * 4);
813 	}
814 }
815 
wNETWMGetUsableArea(WScreen * scr,int head,WArea * area)816 Bool wNETWMGetUsableArea(WScreen *scr, int head, WArea *area)
817 {
818 	WReservedArea *cur;
819 	WMRect rect;
820 
821 	if (!scr->netdata || !scr->netdata->strut)
822 		return False;
823 
824 	area->x1 = area->y1 = area->x2 = area->y2 = 0;
825 
826 	for (cur = scr->netdata->strut; cur; cur = cur->next) {
827 		WWindow *wwin = wWindowFor(cur->window);
828 		if (wWindowTouchesHead(wwin, head)) {
829 			if (cur->area.x1 > area->x1)
830 				area->x1 = cur->area.x1;
831 			if (cur->area.y1 > area->y1)
832 				area->y1 = cur->area.y1;
833 			if (cur->area.x2 > area->x2)
834 				area->x2 = cur->area.x2;
835 			if (cur->area.y2 > area->y2)
836 				area->y2 = cur->area.y2;
837 		}
838 	}
839 
840 	if (area->x1 == 0 && area->x2 == 0 && area->y1 == 0 && area->y2 == 0)
841 		return False;
842 
843 	rect = wGetRectForHead(scr, head);
844 
845 	area->x1 = rect.pos.x + area->x1;
846 	area->x2 = rect.pos.x + rect.size.width - area->x2;
847 	area->y1 = rect.pos.y + area->y1;
848 	area->y2 = rect.pos.y + rect.size.height - area->y2;
849 
850 	return True;
851 }
852 
updateClientList(WScreen * scr)853 static void updateClientList(WScreen *scr)
854 {
855 	WWindow *wwin;
856 	Window *windows;
857 	int count;
858 
859 	windows = (Window *) wmalloc(sizeof(Window) * (scr->window_count + 1));
860 
861 	count = 0;
862 	wwin = scr->focused_window;
863 	while (wwin) {
864 		windows[count++] = wwin->client_win;
865 		wwin = wwin->prev;
866 	}
867 	XChangeProperty(dpy, scr->root_win, net_client_list, XA_WINDOW, 32,
868 			PropModeReplace, (unsigned char *)windows, count);
869 
870 	wfree(windows);
871 	XFlush(dpy);
872 }
873 
updateClientListStacking(WScreen * scr,WWindow * wwin_excl)874 static void updateClientListStacking(WScreen *scr, WWindow *wwin_excl)
875 {
876 	WWindow *wwin;
877 	Window *client_list, *client_list_reverse;
878 	int client_count, i;
879 	WCoreWindow *tmp;
880 	WMBagIterator iter;
881 
882 	/* update client list */
883 	i = scr->window_count + 1;
884 	client_list = (Window *) wmalloc(sizeof(Window) * i);
885 	client_list_reverse = (Window *) wmalloc(sizeof(Window) * i);
886 
887 	client_count = 0;
888 	WM_ETARETI_BAG(scr->stacking_list, tmp, iter) {
889 		while (tmp) {
890 			wwin = wWindowFor(tmp->window);
891 			/* wwin_excl is a window to exclude from the list
892 			   (e.g. it's now unmanaged) */
893 			if (wwin && (wwin != wwin_excl))
894 				client_list[client_count++] = wwin->client_win;
895 			tmp = tmp->stacking->under;
896 		}
897 	}
898 
899 	for (i = 0; i < client_count; i++) {
900 		Window w = client_list[client_count - i - 1];
901 		client_list_reverse[i] = w;
902 	}
903 
904 	XChangeProperty(dpy, scr->root_win, net_client_list_stacking, XA_WINDOW, 32,
905 			PropModeReplace, (unsigned char *)client_list_reverse, client_count);
906 
907 	wfree(client_list);
908 	wfree(client_list_reverse);
909 
910 	XFlush(dpy);
911 }
912 
updateWorkspaceCount(WScreen * scr)913 static void updateWorkspaceCount(WScreen *scr)
914 {				/* changeable */
915 	long count;
916 
917 	count = scr->workspace_count;
918 
919 	XChangeProperty(dpy, scr->root_win, net_number_of_desktops, XA_CARDINAL,
920 			32, PropModeReplace, (unsigned char *)&count, 1);
921 }
922 
updateCurrentWorkspace(WScreen * scr)923 static void updateCurrentWorkspace(WScreen *scr)
924 {				/* changeable */
925 	long count;
926 
927 	count = scr->current_workspace;
928 
929 	XChangeProperty(dpy, scr->root_win, net_current_desktop, XA_CARDINAL, 32,
930 			PropModeReplace, (unsigned char *)&count, 1);
931 }
932 
updateWorkspaceNames(WScreen * scr)933 static void updateWorkspaceNames(WScreen *scr)
934 {
935 	char buf[MAX_WORKSPACES * (MAX_WORKSPACENAME_WIDTH + 1)], *pos;
936 	unsigned int i, len, curr_size;
937 
938 	pos = buf;
939 	len = 0;
940 	for (i = 0; i < scr->workspace_count; i++) {
941 		curr_size = strlen(scr->workspaces[i]->name);
942 		strcpy(pos, scr->workspaces[i]->name);
943 		pos += (curr_size + 1);
944 		len += (curr_size + 1);
945 	}
946 
947 	XChangeProperty(dpy, scr->root_win, net_desktop_names, utf8_string, 8,
948 			PropModeReplace, (unsigned char *)buf, len);
949 }
950 
updateFocusHint(WScreen * scr)951 static void updateFocusHint(WScreen *scr)
952 {				/* changeable */
953 	Window window;
954 
955 	if (!scr->focused_window || !scr->focused_window->flags.focused)
956 		window = None;
957 	else
958 		window = scr->focused_window->client_win;
959 
960 	XChangeProperty(dpy, scr->root_win, net_active_window, XA_WINDOW, 32,
961 			PropModeReplace, (unsigned char *)&window, 1);
962 }
963 
updateWorkspaceHint(WWindow * wwin,Bool fake,Bool del)964 static void updateWorkspaceHint(WWindow *wwin, Bool fake, Bool del)
965 {
966 	long l;
967 
968 	if (del) {
969 		XDeleteProperty(dpy, wwin->client_win, net_wm_desktop);
970 	} else {
971 		l = ((fake || IS_OMNIPRESENT(wwin)) ? -1 : wwin->frame->workspace);
972 		XChangeProperty(dpy, wwin->client_win, net_wm_desktop, XA_CARDINAL,
973 				32, PropModeReplace, (unsigned char *)&l, 1);
974 	}
975 }
976 
updateStateHint(WWindow * wwin,Bool changedWorkspace,Bool del)977 static void updateStateHint(WWindow *wwin, Bool changedWorkspace, Bool del)
978 {				/* changeable */
979 	if (del) {
980 		XDeleteProperty(dpy, wwin->client_win, net_wm_state);
981 	} else {
982 		Atom state[15];	/* nr of defined state atoms */
983 		int i = 0;
984 
985 		if (changedWorkspace || (wPreferences.sticky_icons && !IS_OMNIPRESENT(wwin)))
986 			updateWorkspaceHint(wwin, False, False);
987 
988 		if (IS_OMNIPRESENT(wwin))
989 			state[i++] = net_wm_state_sticky;
990 		if (wwin->flags.shaded)
991 			state[i++] = net_wm_state_shaded;
992 		if (wwin->flags.maximized & MAX_HORIZONTAL)
993 			state[i++] = net_wm_state_maximized_horz;
994 		if (wwin->flags.maximized & MAX_VERTICAL)
995 			state[i++] = net_wm_state_maximized_vert;
996 		if (WFLAGP(wwin, skip_window_list))
997 			state[i++] = net_wm_state_skip_taskbar;
998 		if (wwin->flags.net_skip_pager)
999 			state[i++] = net_wm_state_skip_pager;
1000 		if ((wwin->flags.hidden || wwin->flags.miniaturized) && !wwin->flags.net_show_desktop) {
1001 			state[i++] = net_wm_state_hidden;
1002 			state[i++] = net_wm_state_skip_pager;
1003 
1004 			if (wwin->flags.miniaturized && wPreferences.sticky_icons) {
1005 				if (!IS_OMNIPRESENT(wwin))
1006 					updateWorkspaceHint(wwin, True, False);
1007 				state[i++] = net_wm_state_sticky;
1008 			}
1009 		}
1010 		if (WFLAGP(wwin, sunken))
1011 			state[i++] = net_wm_state_below;
1012 		if (WFLAGP(wwin, floating))
1013 			state[i++] = net_wm_state_above;
1014 		if (wwin->flags.fullscreen)
1015 			state[i++] = net_wm_state_fullscreen;
1016 
1017 		XChangeProperty(dpy, wwin->client_win, net_wm_state, XA_ATOM, 32,
1018 				PropModeReplace, (unsigned char *)state, i);
1019 	}
1020 }
1021 
updateStrut(WScreen * scr,Window w,Bool adding)1022 static Bool updateStrut(WScreen *scr, Window w, Bool adding)
1023 {
1024 	WReservedArea *area;
1025 	Bool hasState = False;
1026 
1027 	if (adding) {
1028 		Atom type_ret;
1029 		int fmt_ret;
1030 		unsigned long nitems_ret, bytes_after_ret;
1031 		long *data = NULL;
1032 
1033 		if ((XGetWindowProperty(dpy, w, net_wm_strut, 0, 4, False,
1034 				       XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
1035 				       &bytes_after_ret, (unsigned char **)&data) == Success && data) ||
1036 		    ((XGetWindowProperty(dpy, w, net_wm_strut_partial, 0, 12, False,
1037 				       XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
1038 				       &bytes_after_ret, (unsigned char **)&data) == Success && data))) {
1039 
1040 			/* XXX: This is strictly incorrect in the case of net_wm_strut_partial...
1041 			 * Discard the start and end properties from the partial strut and treat it as
1042 			 * a (deprecated) strut.
1043 			 * This means we are marking the whole width or height of the screen as
1044 			 * reserved, which is not necessarily what the strut defines.  However for the
1045 			 * purposes of determining placement or maximization it's probably good enough.
1046 			 */
1047 			area = (WReservedArea *) wmalloc(sizeof(WReservedArea));
1048 			area->area.x1 = data[0];
1049 			area->area.x2 = data[1];
1050 			area->area.y1 = data[2];
1051 			area->area.y2 = data[3];
1052 
1053 			area->window = w;
1054 
1055 			area->next = scr->netdata->strut;
1056 			scr->netdata->strut = area;
1057 
1058 			XFree(data);
1059 			hasState = True;
1060 		}
1061 	} else {
1062 		/* deleting */
1063 		area = scr->netdata->strut;
1064 
1065 		if (area) {
1066 			if (area->window == w) {
1067 				scr->netdata->strut = area->next;
1068 				wfree(area);
1069 				hasState = True;
1070 			} else {
1071 				while (area->next && area->next->window != w)
1072 					area = area->next;
1073 
1074 				if (area->next) {
1075 					WReservedArea *next;
1076 
1077 					next = area->next->next;
1078 					wfree(area->next);
1079 					area->next = next;
1080 
1081 					hasState = True;
1082 				}
1083 			}
1084 		}
1085 	}
1086 
1087 	return hasState;
1088 }
1089 
getWindowLayer(WWindow * wwin)1090 static int getWindowLayer(WWindow *wwin)
1091 {
1092 	int layer = WMNormalLevel;
1093 
1094 	if (wwin->type == net_wm_window_type_desktop) {
1095 		layer = WMDesktopLevel;
1096 	} else if (wwin->type == net_wm_window_type_dock) {
1097 		layer = WMDockLevel;
1098 	} else if (wwin->type == net_wm_window_type_toolbar) {
1099 		layer = WMMainMenuLevel;
1100 	} else if (wwin->type == net_wm_window_type_menu) {
1101 		layer = WMSubmenuLevel;
1102 	} else if (wwin->type == net_wm_window_type_utility) {
1103 	} else if (wwin->type == net_wm_window_type_splash) {
1104 	} else if (wwin->type == net_wm_window_type_dialog) {
1105 		if (wwin->transient_for) {
1106 			WWindow *parent = wWindowFor(wwin->transient_for);
1107 			if (parent && parent->flags.fullscreen)
1108 				layer = WMFullscreenLevel;
1109 		}
1110 		/* //layer = WMPopUpLevel; // this seems a bad idea -Dan */
1111 	} else if (wwin->type == net_wm_window_type_dropdown_menu) {
1112 		layer = WMSubmenuLevel;
1113 	} else if (wwin->type == net_wm_window_type_popup_menu) {
1114 		layer = WMSubmenuLevel;
1115 	} else if (wwin->type == net_wm_window_type_tooltip) {
1116 	} else if (wwin->type == net_wm_window_type_notification) {
1117 		layer = WMPopUpLevel;
1118 	} else if (wwin->type == net_wm_window_type_combo) {
1119 		layer = WMSubmenuLevel;
1120 	} else if (wwin->type == net_wm_window_type_dnd) {
1121 	} else if (wwin->type == net_wm_window_type_normal) {
1122 	}
1123 
1124 	if (wwin->client_flags.sunken && WMSunkenLevel < layer)
1125 		layer = WMSunkenLevel;
1126 	if (wwin->client_flags.floating && WMFloatingLevel > layer)
1127 		layer = WMFloatingLevel;
1128 
1129 	return layer;
1130 }
1131 
doStateAtom(WWindow * wwin,Atom state,int set,Bool init)1132 static void doStateAtom(WWindow *wwin, Atom state, int set, Bool init)
1133 {
1134 	if (state == net_wm_state_sticky) {
1135 		if (set == _NET_WM_STATE_TOGGLE)
1136 			set = !IS_OMNIPRESENT(wwin);
1137 
1138 		if (set != wwin->flags.omnipresent)
1139 			wWindowSetOmnipresent(wwin, set);
1140 
1141 	} else if (state == net_wm_state_shaded) {
1142 		if (set == _NET_WM_STATE_TOGGLE)
1143 			set = !wwin->flags.shaded;
1144 
1145 		if (init) {
1146 			wwin->flags.shaded = set;
1147 		} else {
1148 			if (set)
1149 				wShadeWindow(wwin);
1150 			else
1151 				wUnshadeWindow(wwin);
1152 		}
1153 	} else if (state == net_wm_state_skip_taskbar) {
1154 		if (set == _NET_WM_STATE_TOGGLE)
1155 			set = !wwin->client_flags.skip_window_list;
1156 
1157 		wwin->client_flags.skip_window_list = set;
1158 	} else if (state == net_wm_state_skip_pager) {
1159 		if (set == _NET_WM_STATE_TOGGLE)
1160 			set = !wwin->flags.net_skip_pager;
1161 
1162 		wwin->flags.net_skip_pager = set;
1163 	} else if (state == net_wm_state_maximized_vert) {
1164 		if (set == _NET_WM_STATE_TOGGLE)
1165 			set = !(wwin->flags.maximized & MAX_VERTICAL);
1166 
1167 		if (init) {
1168 			wwin->flags.maximized |= (set ? MAX_VERTICAL : 0);
1169 		} else {
1170 			if (set)
1171 				wMaximizeWindow(wwin, wwin->flags.maximized | MAX_VERTICAL,
1172 						wGetHeadForWindow(wwin));
1173 			else
1174 				wMaximizeWindow(wwin, wwin->flags.maximized & ~MAX_VERTICAL,
1175 						wGetHeadForWindow(wwin));
1176 		}
1177 	} else if (state == net_wm_state_maximized_horz) {
1178 		if (set == _NET_WM_STATE_TOGGLE)
1179 			set = !(wwin->flags.maximized & MAX_HORIZONTAL);
1180 
1181 		if (init) {
1182 			wwin->flags.maximized |= (set ? MAX_HORIZONTAL : 0);
1183 		} else {
1184 			if (set)
1185 				wMaximizeWindow(wwin, wwin->flags.maximized | MAX_HORIZONTAL,
1186 						wGetHeadForWindow(wwin));
1187 			else
1188 				wMaximizeWindow(wwin, wwin->flags.maximized & ~MAX_HORIZONTAL,
1189 						wGetHeadForWindow(wwin));
1190 		}
1191 	} else if (state == net_wm_state_hidden) {
1192 		if (set == _NET_WM_STATE_TOGGLE)
1193 			set = !(wwin->flags.miniaturized);
1194 
1195 		if (init) {
1196 			wwin->flags.miniaturized = set;
1197 		} else {
1198 			if (set)
1199 				wIconifyWindow(wwin);
1200 			else
1201 				wDeiconifyWindow(wwin);
1202 		}
1203 	} else if (state == net_wm_state_fullscreen) {
1204 		if (set == _NET_WM_STATE_TOGGLE)
1205 			set = !(wwin->flags.fullscreen);
1206 
1207 		if (init) {
1208 			wwin->flags.fullscreen = set;
1209 		} else {
1210 			if (set)
1211 				wFullscreenWindow(wwin);
1212 			else
1213 				wUnfullscreenWindow(wwin);
1214 		}
1215 	} else if (state == net_wm_state_above) {
1216 		if (set == _NET_WM_STATE_TOGGLE)
1217 			set = !(wwin->client_flags.floating);
1218 
1219 		if (init) {
1220 			wwin->client_flags.floating = set;
1221 		} else {
1222 			wwin->client_flags.floating = set;
1223 			ChangeStackingLevel(wwin->frame->core, getWindowLayer(wwin));
1224 		}
1225 
1226 	} else if (state == net_wm_state_below) {
1227 		if (set == _NET_WM_STATE_TOGGLE)
1228 			set = !(wwin->client_flags.sunken);
1229 
1230 		if (init) {
1231 			wwin->client_flags.sunken = set;
1232 		} else {
1233 			wwin->client_flags.sunken = set;
1234 			ChangeStackingLevel(wwin->frame->core, getWindowLayer(wwin));
1235 		}
1236 
1237 	} else {
1238 #ifdef DEBUG_WMSPEC
1239 		wmessage("doStateAtom unknown atom %s set %d", XGetAtomName(dpy, state), set);
1240 #endif
1241 	}
1242 }
1243 
removeIcon(WWindow * wwin)1244 static void removeIcon(WWindow *wwin)
1245 {
1246 	if (wwin->icon == NULL)
1247 		return;
1248 	if (wwin->flags.miniaturized && wwin->icon->mapped) {
1249 		XUnmapWindow(dpy, wwin->icon->core->window);
1250 		RemoveFromStackList(wwin->icon->core);
1251 		wIconDestroy(wwin->icon);
1252 		wwin->icon = NULL;
1253 	}
1254 }
1255 
handleWindowType(WWindow * wwin,Atom type,int * layer)1256 static Bool handleWindowType(WWindow *wwin, Atom type, int *layer)
1257 {
1258 	Bool ret = True;
1259 
1260 	if (type == net_wm_window_type_desktop) {
1261 		wwin->client_flags.no_titlebar = 1;
1262 		wwin->client_flags.no_resizable = 1;
1263 		wwin->client_flags.no_miniaturizable = 1;
1264 		wwin->client_flags.no_border = 1;
1265 		wwin->client_flags.no_resizebar = 1;
1266 		wwin->client_flags.no_shadeable = 1;
1267 		wwin->client_flags.no_movable = 1;
1268 		wwin->client_flags.omnipresent = 1;
1269 		wwin->client_flags.skip_window_list = 1;
1270 		wwin->client_flags.skip_switchpanel = 1;
1271 		wwin->client_flags.dont_move_off = 1;
1272 		wwin->client_flags.no_appicon = 1;
1273 		wwin->flags.net_skip_pager = 1;
1274 		wwin->frame_x = 0;
1275 		wwin->frame_y = 0;
1276 	} else if (type == net_wm_window_type_dock) {
1277 		wwin->client_flags.no_titlebar = 1;
1278 		wwin->client_flags.no_resizable = 1;
1279 		wwin->client_flags.no_miniaturizable = 1;
1280 		wwin->client_flags.no_border = 1;	/* XXX: really not a single decoration. */
1281 		wwin->client_flags.no_resizebar = 1;
1282 		wwin->client_flags.no_shadeable = 1;
1283 		wwin->client_flags.no_movable = 1;
1284 		wwin->client_flags.omnipresent = 1;
1285 		wwin->client_flags.skip_window_list = 1;
1286 		wwin->client_flags.skip_switchpanel = 1;
1287 		wwin->client_flags.dont_move_off = 1;
1288 		wwin->flags.net_skip_pager = 1;
1289 	} else if (type == net_wm_window_type_toolbar ||
1290 	           type == net_wm_window_type_menu) {
1291 		wwin->client_flags.no_resizable = 1;
1292 		wwin->client_flags.no_miniaturizable = 1;
1293 		wwin->client_flags.no_resizebar = 1;
1294 		wwin->client_flags.no_shadeable = 1;
1295 		wwin->client_flags.skip_window_list = 1;
1296 		wwin->client_flags.skip_switchpanel = 1;
1297 		wwin->client_flags.dont_move_off = 1;
1298 		wwin->client_flags.no_appicon = 1;
1299 	} else if (type == net_wm_window_type_dropdown_menu ||
1300 			type == net_wm_window_type_popup_menu ||
1301 			type == net_wm_window_type_combo) {
1302 		wwin->client_flags.no_titlebar = 1;
1303 		wwin->client_flags.no_resizable = 1;
1304 		wwin->client_flags.no_miniaturizable = 1;
1305 		wwin->client_flags.no_resizebar = 1;
1306 		wwin->client_flags.no_shadeable = 1;
1307 		wwin->client_flags.skip_window_list = 1;
1308 		wwin->client_flags.skip_switchpanel = 1;
1309 		wwin->client_flags.dont_move_off = 1;
1310 		wwin->client_flags.no_appicon = 1;
1311 	} else if (type == net_wm_window_type_utility) {
1312 		wwin->client_flags.no_appicon = 1;
1313 	} else if (type == net_wm_window_type_splash) {
1314 		wwin->client_flags.no_titlebar = 1;
1315 		wwin->client_flags.no_resizable = 1;
1316 		wwin->client_flags.no_miniaturizable = 1;
1317 		wwin->client_flags.no_resizebar = 1;
1318 		wwin->client_flags.no_shadeable = 1;
1319 		wwin->client_flags.no_movable = 1;
1320 		wwin->client_flags.skip_window_list = 1;
1321 		wwin->client_flags.skip_switchpanel = 1;
1322 		wwin->client_flags.dont_move_off = 1;
1323 		wwin->client_flags.no_appicon = 1;
1324 		wwin->flags.net_skip_pager = 1;
1325 	} else if (type == net_wm_window_type_dialog) {
1326 		/* These also seem a bad idea in our context -Dan
1327 		   // wwin->client_flags.skip_window_list = 1;
1328 		   // wwin->client_flags.no_appicon = 1;
1329 		 */
1330 	} else if (wwin->type == net_wm_window_type_tooltip) {
1331 		wwin->client_flags.no_titlebar = 1;
1332 		wwin->client_flags.no_resizable = 1;
1333 		wwin->client_flags.no_miniaturizable = 1;
1334 		wwin->client_flags.no_resizebar = 1;
1335 		wwin->client_flags.no_shadeable = 1;
1336 		wwin->client_flags.no_movable = 1;
1337 		wwin->client_flags.skip_window_list = 1;
1338 		wwin->client_flags.skip_switchpanel = 1;
1339 		wwin->client_flags.dont_move_off = 1;
1340 		wwin->client_flags.no_appicon = 1;
1341 		wwin->client_flags.no_focusable = 1;
1342 		wwin->flags.net_skip_pager = 1;
1343 	} else if (wwin->type == net_wm_window_type_notification) {
1344 		wwin->client_flags.no_titlebar = 1;
1345 		wwin->client_flags.no_resizable = 1;
1346 		wwin->client_flags.no_miniaturizable = 1;
1347 		wwin->client_flags.no_border = 1;
1348 		wwin->client_flags.no_resizebar = 1;
1349 		wwin->client_flags.no_shadeable = 1;
1350 		wwin->client_flags.no_movable = 1;
1351 		wwin->client_flags.omnipresent = 1;
1352 		wwin->client_flags.skip_window_list = 1;
1353 		wwin->client_flags.skip_switchpanel = 1;
1354 		wwin->client_flags.dont_move_off = 1;
1355 		wwin->client_flags.no_hide_others= 1;
1356 		wwin->client_flags.no_appicon = 1;
1357 		wwin->client_flags.no_focusable = 1;
1358 		wwin->flags.net_skip_pager = 1;
1359 	} else if (wwin->type == net_wm_window_type_dnd) {
1360 		wwin->client_flags.no_titlebar = 1;
1361 		wwin->client_flags.no_resizable = 1;
1362 		wwin->client_flags.no_miniaturizable = 1;
1363 		wwin->client_flags.no_border = 1;
1364 		wwin->client_flags.no_resizebar = 1;
1365 		wwin->client_flags.no_shadeable = 1;
1366 		wwin->client_flags.no_movable = 1;
1367 		wwin->client_flags.skip_window_list = 1;
1368 		wwin->client_flags.skip_switchpanel = 1;
1369 		wwin->client_flags.dont_move_off = 1;
1370 		wwin->client_flags.no_appicon = 1;
1371 		wwin->flags.net_skip_pager = 1;
1372 	} else if (type == net_wm_window_type_normal) {
1373 	} else {
1374 		ret = False;
1375 	}
1376 
1377 	/* Restore decoration if the user has enabled the
1378 	 * IgnoreDecorationChanges option */
1379 	if (WFLAGP(wwin, ignore_decoration_changes)) {
1380 		wwin->client_flags.no_titlebar = 0;
1381 		wwin->client_flags.no_resizable = 0;
1382 		wwin->client_flags.no_miniaturizable = 0;
1383 		wwin->client_flags.no_resizebar = 0;
1384 		wwin->client_flags.no_border = 0;
1385 		wwin->client_flags.no_movable = 0;
1386 	}
1387 
1388 	wwin->type = type;
1389 	*layer = getWindowLayer(wwin);
1390 
1391 	return ret;
1392 }
1393 
wNETWMPositionSplash(WWindow * wwin,int * x,int * y,int width,int height)1394 void wNETWMPositionSplash(WWindow *wwin, int *x, int *y, int width, int height)
1395 {
1396 	if (wwin->type == net_wm_window_type_splash) {
1397 		WScreen *scr = wwin->screen_ptr;
1398 		WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
1399 		*x = rect.pos.x + (rect.size.width - width) / 2;
1400 		*y = rect.pos.y + (rect.size.height - height) / 2;
1401 	}
1402 }
1403 
updateWindowType(WWindow * wwin)1404 static void updateWindowType(WWindow *wwin)
1405 {
1406 	Atom type_ret;
1407 	int fmt_ret, layer;
1408 	unsigned long nitems_ret, bytes_after_ret;
1409 	long *data = NULL;
1410 
1411 	if (XGetWindowProperty(dpy, wwin->client_win, net_wm_window_type, 0, 1,
1412 			       False, XA_ATOM, &type_ret, &fmt_ret, &nitems_ret,
1413 			       &bytes_after_ret, (unsigned char **)&data) == Success && data) {
1414 
1415 		int i;
1416 		Atom *type = (Atom *) data;
1417 		for (i = 0; i < nitems_ret; ++i) {
1418 			if (handleWindowType(wwin, type[i], &layer))
1419 				break;
1420 		}
1421 		XFree(data);
1422 	}
1423 
1424 	if (wwin->frame != NULL) {
1425 		ChangeStackingLevel(wwin->frame->core, layer);
1426 		wwin->frame->flags.need_texture_change = 1;
1427 		wWindowConfigureBorders(wwin);
1428 		wFrameWindowPaint(wwin->frame);
1429 		wNETWMUpdateActions(wwin, False);
1430 	}
1431 }
1432 
wNETWMCheckClientHints(WWindow * wwin,int * layer,int * workspace)1433 void wNETWMCheckClientHints(WWindow *wwin, int *layer, int *workspace)
1434 {
1435 	Atom type_ret;
1436 	int fmt_ret, i;
1437 	unsigned long nitems_ret, bytes_after_ret;
1438 	long *data = NULL;
1439 
1440 	if (XGetWindowProperty(dpy, wwin->client_win, net_wm_desktop, 0, 1, False,
1441 			       XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
1442 			       &bytes_after_ret, (unsigned char **)&data) == Success && data) {
1443 
1444 		long desktop = *data;
1445 		XFree(data);
1446 
1447 		if (desktop == -1)
1448 			wwin->client_flags.omnipresent = 1;
1449 		else
1450 			*workspace = desktop;
1451 	}
1452 
1453 	if (XGetWindowProperty(dpy, wwin->client_win, net_wm_state, 0, 1, False,
1454 			       XA_ATOM, &type_ret, &fmt_ret, &nitems_ret,
1455 			       &bytes_after_ret, (unsigned char **)&data) == Success && data) {
1456 
1457 		Atom *state = (Atom *) data;
1458 		for (i = 0; i < nitems_ret; ++i)
1459 			doStateAtom(wwin, state[i], _NET_WM_STATE_ADD, True);
1460 
1461 		XFree(data);
1462 	}
1463 
1464 	if (XGetWindowProperty(dpy, wwin->client_win, net_wm_window_type, 0, 1, False,
1465 			       XA_ATOM, &type_ret, &fmt_ret, &nitems_ret,
1466 			       &bytes_after_ret, (unsigned char **)&data) == Success && data) {
1467 
1468 		Atom *type = (Atom *) data;
1469 		for (i = 0; i < nitems_ret; ++i) {
1470 			if (handleWindowType(wwin, type[i], layer))
1471 				break;
1472 		}
1473 		XFree(data);
1474 	}
1475 
1476 	wNETWMUpdateActions(wwin, False);
1477 	updateStrut(wwin->screen_ptr, wwin->client_win, False);
1478 	updateStrut(wwin->screen_ptr, wwin->client_win, True);
1479 
1480 	wScreenUpdateUsableArea(wwin->screen_ptr);
1481 }
1482 
updateNetIconInfo(WWindow * wwin)1483 static Bool updateNetIconInfo(WWindow *wwin)
1484 {
1485 	Atom type_ret;
1486 	int fmt_ret;
1487 	unsigned long nitems_ret, bytes_after_ret;
1488 	long *data = NULL;
1489 	Bool hasState = False;
1490 	Bool old_state = wwin->flags.net_handle_icon;
1491 
1492 	if (XGetWindowProperty(dpy, wwin->client_win, net_wm_handled_icons, 0, 1, False,
1493 			       XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
1494 			       &bytes_after_ret, (unsigned char **)&data) == Success && data) {
1495 		long handled = *data;
1496 		wwin->flags.net_handle_icon = (handled != 0);
1497 		XFree(data);
1498 		hasState = True;
1499 
1500 	} else {
1501 		wwin->flags.net_handle_icon = False;
1502 	}
1503 
1504 	if (XGetWindowProperty(dpy, wwin->client_win, net_wm_icon_geometry, 0, 4, False,
1505 			       XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
1506 			       &bytes_after_ret, (unsigned char **)&data) == Success && data) {
1507 
1508 #ifdef NETWM_PROPER
1509 		if (wwin->flags.net_handle_icon)
1510 #else
1511 		wwin->flags.net_handle_icon = True;
1512 #endif
1513 		{
1514 			wwin->icon_x = data[0];
1515 			wwin->icon_y = data[1];
1516 			wwin->icon_w = data[2];
1517 			wwin->icon_h = data[3];
1518 		}
1519 
1520 		XFree(data);
1521 		hasState = True;
1522 
1523 	} else {
1524 		wwin->flags.net_handle_icon = False;
1525 	}
1526 
1527 	if (wwin->flags.miniaturized && old_state != wwin->flags.net_handle_icon) {
1528 		if (wwin->flags.net_handle_icon) {
1529 			removeIcon(wwin);
1530 		} else {
1531 			wwin->flags.miniaturized = False;
1532 			wwin->flags.skip_next_animation = True;
1533 			wIconifyWindow(wwin);
1534 		}
1535 	}
1536 
1537 	return hasState;
1538 }
1539 
wNETWMCheckInitialClientState(WWindow * wwin)1540 void wNETWMCheckInitialClientState(WWindow *wwin)
1541 {
1542 #ifdef DEBUG_WMSPEC
1543 	wmessage("wNETWMCheckInitialClientState");
1544 #endif
1545 
1546 	wNETWMShowingDesktop(wwin->screen_ptr, False);
1547 
1548 	updateWindowType(wwin);
1549 	updateNetIconInfo(wwin);
1550 	updateIconImage(wwin);
1551 }
1552 
wNETWMCheckInitialFrameState(WWindow * wwin)1553 void wNETWMCheckInitialFrameState(WWindow *wwin)
1554 {
1555 #ifdef DEBUG_WMSPEC
1556 	wmessage("wNETWMCheckInitialFrameState");
1557 #endif
1558 
1559 	updateWindowOpacity(wwin);
1560 }
1561 
handleDesktopNames(WScreen * scr)1562 static void handleDesktopNames(WScreen *scr)
1563 {
1564 	unsigned long nitems_ret, bytes_after_ret;
1565 	char *data, *names[32];
1566 	int fmt_ret, i, n;
1567 	Atom type_ret;
1568 
1569 	if (XGetWindowProperty(dpy, scr->root_win, net_desktop_names, 0, 1, False,
1570 			       utf8_string, &type_ret, &fmt_ret, &nitems_ret,
1571 			       &bytes_after_ret, (unsigned char **)&data) != Success)
1572 		return;
1573 
1574 	if (data == NULL)
1575 		return;
1576 
1577 	if (type_ret != utf8_string || fmt_ret != 8)
1578 		return;
1579 
1580 	n = 0;
1581 	names[n] = data;
1582 	for (i = 0; i < nitems_ret; i++) {
1583 		if (data[i] == 0) {
1584 			n++;
1585 			names[n] = &data[i];
1586 		} else if (*names[n] == 0) {
1587 			names[n] = &data[i];
1588 			wWorkspaceRename(scr, n, names[n]);
1589 		}
1590 	}
1591 }
1592 
wNETWMProcessClientMessage(XClientMessageEvent * event)1593 Bool wNETWMProcessClientMessage(XClientMessageEvent *event)
1594 {
1595 	WScreen *scr;
1596 	WWindow *wwin;
1597 
1598 #ifdef DEBUG_WMSPEC
1599 	wmessage("processClientMessage type %s", XGetAtomName(dpy, event->message_type));
1600 #endif
1601 
1602 	scr = wScreenForWindow(event->window);
1603 	if (scr) {
1604 		/* generic client messages */
1605 		if (event->message_type == net_current_desktop) {
1606 			wWorkspaceChange(scr, event->data.l[0]);
1607 			return True;
1608 
1609 		} else if (event->message_type == net_number_of_desktops) {
1610 			long value;
1611 
1612 			value = event->data.l[0];
1613 			if (value > scr->workspace_count) {
1614 				wWorkspaceMake(scr, value - scr->workspace_count);
1615 			} else if (value < scr->workspace_count) {
1616 				int i;
1617 				Bool rebuild = False;
1618 
1619 				for (i = scr->workspace_count - 1; i >= value; i--) {
1620 					if (!wWorkspaceDelete(scr, i)) {
1621 						rebuild = True;
1622 						break;
1623 					}
1624 				}
1625 
1626 				if (rebuild)
1627 					updateWorkspaceCount(scr);
1628 			}
1629 			return True;
1630 
1631 		} else if (event->message_type == net_showing_desktop) {
1632 			wNETWMShowingDesktop(scr, event->data.l[0]);
1633 			return True;
1634 
1635 		} else if (event->message_type == net_desktop_names) {
1636 			handleDesktopNames(scr);
1637 			return True;
1638 		}
1639 	}
1640 
1641 	/* window specific client messages */
1642 
1643 	wwin = wWindowFor(event->window);
1644 	if (!wwin)
1645 		return False;
1646 
1647 	if (event->message_type == net_active_window) {
1648 		/*
1649 		 * Satisfy a client's focus request only if
1650 		 * - request comes from a pager, or
1651 		 * - it's explicitly allowed in Advanced Options, or
1652 		 * - giving the client the focus does not cause a change in
1653 		 *   the active workspace (XXX: or the active head if Xinerama)
1654 		 */
1655 		if (wwin->frame->workspace == wwin->screen_ptr->current_workspace /* No workspace change */
1656 		    || event->data.l[0] == 2 /* Requested by pager */
1657 		    || WFLAGP(wwin, focus_across_wksp) /* Explicitly allowed */) {
1658 				wNETWMShowingDesktop(scr, False);
1659 				wMakeWindowVisible(wwin);
1660 		}
1661 		return True;
1662 
1663 	} else if (event->message_type == net_close_window) {
1664 		if (!WFLAGP(wwin, no_closable)) {
1665 			if (wwin->protocols.DELETE_WINDOW)
1666 				wClientSendProtocol(wwin, w_global.atom.wm.delete_window,
1667 										  w_global.timestamp.last_event);
1668 		}
1669 		return True;
1670 
1671 	} else if (event->message_type == net_wm_state) {
1672 		int maximized = wwin->flags.maximized;
1673 		long set = event->data.l[0];
1674 
1675 #ifdef DEBUG_WMSPEC
1676 		wmessage("net_wm_state set %ld a1 %s a2 %s", set,
1677 		       XGetAtomName(dpy, event->data.l[1]), XGetAtomName(dpy, event->data.l[2]));
1678 #endif
1679 
1680 		doStateAtom(wwin, (Atom) event->data.l[1], set, False);
1681 		if (event->data.l[2])
1682 			doStateAtom(wwin, (Atom) event->data.l[2], set, False);
1683 
1684 		if (wwin->flags.maximized != maximized) {
1685 			if (!wwin->flags.maximized) {
1686 				wwin->flags.maximized = maximized;
1687 				wUnmaximizeWindow(wwin);
1688 			} else {
1689 				wMaximizeWindow(wwin, wwin->flags.maximized,
1690 						wGetHeadForWindow(wwin));
1691 			}
1692 		}
1693 		updateStateHint(wwin, False, False);
1694 		return True;
1695 
1696 	} else if (event->message_type == net_wm_handled_icons || event->message_type == net_wm_icon_geometry) {
1697 		updateNetIconInfo(wwin);
1698 		return True;
1699 
1700 	} else if (event->message_type == net_wm_desktop) {
1701 		long desktop = event->data.l[0];
1702 
1703 		if (desktop == -1) {
1704 			wWindowSetOmnipresent(wwin, True);
1705 		} else {
1706 			if (IS_OMNIPRESENT(wwin))
1707 				wWindowSetOmnipresent(wwin, False);
1708 			wWindowChangeWorkspace(wwin, desktop);
1709 		}
1710 		return True;
1711 	}
1712 
1713 	return False;
1714 }
1715 
wNETWMCheckClientHintChange(WWindow * wwin,XPropertyEvent * event)1716 void wNETWMCheckClientHintChange(WWindow *wwin, XPropertyEvent *event)
1717 {
1718 #ifdef DEBUG_WMSPEC
1719 	wmessage("clientHintChange type %s", XGetAtomName(dpy, event->atom));
1720 #endif
1721 
1722 	if (event->atom == net_wm_strut || event->atom == net_wm_strut_partial) {
1723 		updateStrut(wwin->screen_ptr, wwin->client_win, False);
1724 		updateStrut(wwin->screen_ptr, wwin->client_win, True);
1725 		wScreenUpdateUsableArea(wwin->screen_ptr);
1726 	} else if (event->atom == net_wm_handled_icons || event->atom == net_wm_icon_geometry) {
1727 		updateNetIconInfo(wwin);
1728 	} else if (event->atom == net_wm_window_type) {
1729 		updateWindowType(wwin);
1730 	} else if (event->atom == net_wm_name) {
1731 		char *name = wNETWMGetWindowName(wwin->client_win);
1732 		wWindowUpdateName(wwin, name);
1733 		if (name)
1734 			wfree(name);
1735 	} else if (event->atom == net_wm_icon_name) {
1736 		if (wwin->icon) {
1737 			wIconChangeTitle(wwin->icon, wwin);
1738 			wIconPaint(wwin->icon);
1739 		}
1740 	} else if (event->atom == net_wm_icon) {
1741 		updateIconImage(wwin);
1742 	} else if (event->atom == net_wm_window_opacity) {
1743 		updateWindowOpacity(wwin);
1744 	}
1745 }
1746 
wNETWMGetPidForWindow(Window window)1747 int wNETWMGetPidForWindow(Window window)
1748 {
1749 	Atom type_ret;
1750 	int fmt_ret;
1751 	unsigned long nitems_ret, bytes_after_ret;
1752 	long *data = NULL;
1753 	int pid;
1754 
1755 	if (XGetWindowProperty(dpy, window, net_wm_pid, 0, 1, False,
1756 			       XA_CARDINAL, &type_ret, &fmt_ret, &nitems_ret,
1757 			       &bytes_after_ret, (unsigned char **)&data) == Success && data) {
1758 		pid = *data;
1759 		XFree(data);
1760 	} else {
1761 		pid = 0;
1762 	}
1763 
1764 	return pid;
1765 }
1766 
wNETWMGetWindowName(Window window)1767 char *wNETWMGetWindowName(Window window)
1768 {
1769 	char *name;
1770 	char *ret;
1771 	int size;
1772 
1773 	name = (char *)PropGetCheckProperty(window, net_wm_name, utf8_string, 0, 0, &size);
1774 	if (name) {
1775 		ret = wstrndup(name, size);
1776 		XFree(name);
1777 	} else {
1778 		ret = NULL;
1779 	}
1780 
1781 	return ret;
1782 }
1783 
wNETWMGetIconName(Window window)1784 char *wNETWMGetIconName(Window window)
1785 {
1786 	char *name;
1787 	char *ret;
1788 	int size;
1789 
1790 	name = (char *)PropGetCheckProperty(window, net_wm_icon_name, utf8_string, 0, 0, &size);
1791 	if (name) {
1792 		ret = wstrndup(name, size);
1793 		XFree(name);
1794 	} else {
1795 		ret = NULL;
1796 	}
1797 
1798 	return ret;
1799 }
1800 
observer(void * self,WMNotification * notif)1801 static void observer(void *self, WMNotification *notif)
1802 {
1803 	WWindow *wwin = (WWindow *) WMGetNotificationObject(notif);
1804 	const char *name = WMGetNotificationName(notif);
1805 	void *data = WMGetNotificationClientData(notif);
1806 	NetData *ndata = (NetData *) self;
1807 
1808 	if (strcmp(name, WMNManaged) == 0 && wwin) {
1809 		updateClientList(wwin->screen_ptr);
1810 		updateClientListStacking(wwin->screen_ptr, NULL);
1811 		updateStateHint(wwin, True, False);
1812 
1813 		updateStrut(wwin->screen_ptr, wwin->client_win, False);
1814 		updateStrut(wwin->screen_ptr, wwin->client_win, True);
1815 		wScreenUpdateUsableArea(wwin->screen_ptr);
1816 	} else if (strcmp(name, WMNUnmanaged) == 0 && wwin) {
1817 		updateClientList(wwin->screen_ptr);
1818 		updateClientListStacking(wwin->screen_ptr, wwin);
1819 		updateWorkspaceHint(wwin, False, True);
1820 		updateStateHint(wwin, False, True);
1821 		wNETWMUpdateActions(wwin, True);
1822 
1823 		updateStrut(wwin->screen_ptr, wwin->client_win, False);
1824 		wScreenUpdateUsableArea(wwin->screen_ptr);
1825 	} else if (strcmp(name, WMNResetStacking) == 0 && wwin) {
1826 		updateClientListStacking(wwin->screen_ptr, NULL);
1827 		updateStateHint(wwin, False, False);
1828 	} else if (strcmp(name, WMNChangedStacking) == 0 && wwin) {
1829 		updateClientListStacking(wwin->screen_ptr, NULL);
1830 		updateStateHint(wwin, False, False);
1831 	} else if (strcmp(name, WMNChangedFocus) == 0) {
1832 		updateFocusHint(ndata->scr);
1833 	} else if (strcmp(name, WMNChangedWorkspace) == 0 && wwin) {
1834 		updateWorkspaceHint(wwin, False, False);
1835 		updateStateHint(wwin, True, False);
1836 	} else if (strcmp(name, WMNChangedState) == 0 && wwin) {
1837 		updateStateHint(wwin, !strcmp(data, "omnipresent"), False);
1838 	}
1839 }
1840 
wsobserver(void * self,WMNotification * notif)1841 static void wsobserver(void *self, WMNotification *notif)
1842 {
1843 	WScreen *scr = (WScreen *) WMGetNotificationObject(notif);
1844 	const char *name = WMGetNotificationName(notif);
1845 
1846 	/* Parameter not used, but tell the compiler that it is ok */
1847 	(void) self;
1848 
1849 	if (strcmp(name, WMNWorkspaceCreated) == 0) {
1850 		updateWorkspaceCount(scr);
1851 		updateWorkspaceNames(scr);
1852 		wNETWMUpdateWorkarea(scr);
1853 	} else if (strcmp(name, WMNWorkspaceDestroyed) == 0) {
1854 		updateWorkspaceCount(scr);
1855 		updateWorkspaceNames(scr);
1856 		wNETWMUpdateWorkarea(scr);
1857 	} else if (strcmp(name, WMNWorkspaceChanged) == 0) {
1858 		updateCurrentWorkspace(scr);
1859 	} else if (strcmp(name, WMNWorkspaceNameChanged) == 0) {
1860 		updateWorkspaceNames(scr);
1861 	}
1862 }
1863 
wNETFrameExtents(WWindow * wwin)1864 void wNETFrameExtents(WWindow *wwin)
1865 {
1866 	long extents[4] = { 0, 0, 0, 0 };
1867 
1868 	/* The extents array describes dimensions which are not
1869 	 * part of the client window.  In our case that means
1870 	 * widths of the border and heights of the titlebar and resizebar.
1871 	 *
1872 	 * Index 0 = left
1873 	 *       1 = right
1874 	 *       2 = top
1875 	 *       3 = bottom
1876 	 */
1877 	if (wwin->frame->titlebar)
1878 		extents[2] = wwin->frame->titlebar->height;
1879 	if (wwin->frame->resizebar)
1880 		extents[3] = wwin->frame->resizebar->height;
1881 	if (HAS_BORDER(wwin)) {
1882 		extents[0] += wwin->screen_ptr->frame_border_width;
1883 		extents[1] += wwin->screen_ptr->frame_border_width;
1884 		extents[2] += wwin->screen_ptr->frame_border_width;
1885 		extents[3] += wwin->screen_ptr->frame_border_width;
1886 	}
1887 
1888 	XChangeProperty(dpy, wwin->client_win, net_frame_extents, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) extents, 4);
1889 }
1890 
wNETCleanupFrameExtents(WWindow * wwin)1891 void wNETCleanupFrameExtents(WWindow *wwin)
1892 {
1893 	XDeleteProperty(dpy, wwin->client_win, net_frame_extents);
1894 }
1895