1 /*
2  *    EWMH atom support. initial implementation borrowed from
3  *    awesome wm, then partially reworked.
4  *
5  *    Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
6  *    Copyright © 2008 Alexander Polakov <polachok@gmail.com>
7  *
8  */
9 
10 #include <regex.h>
11 #include <X11/Xatom.h>
12 #include <X11/Xlib.h>
13 #include <X11/Xproto.h>
14 #include <X11/Xutil.h>
15 #include <X11/Xft/Xft.h>
16 #include "echinus.h"
17 #include "config.h"
18 
19 Atom atom[NATOMS];
20 
21 /* keep in sync with enum in echinus.h */
22 const char *atomnames[NATOMS][1] = {
23 	{ "_NET_CLIENT_LIST"		},
24 	{ "_NET_ACTIVE_WINDOW"		},
25 	{ "_NET_WM_DESKTOP"		},
26 	{ "_NET_NUMBER_OF_DESKTOPS"	},
27 	{ "_NET_DESKTOP_NAMES"		},
28 	{ "_NET_CURRENT_DESKTOP"	},
29 	{ "_ECHINUS_LAYOUT"		},
30 	{ "_NET_WORKAREA"		},
31 	{ "_NET_CLIENT_LIST_STACKING"	},
32 	{ "_NET_WM_WINDOW_OPACITY"	},
33 	{ "_NET_WM_WINDOW_TYPE"		},
34 	{ "_NET_WM_WINDOW_TYPE_DESKTOP"	},
35 	{ "_NET_WM_WINDOW_TYPE_DOCK"	},
36 	{ "_NET_WM_WINDOW_TYPE_DIALOG"	},
37 	{ "_NET_WM_STRUT_PARTIAL"	},
38 	{ "_ECHINUS_SELTAGS"		},
39 	{ "_NET_WM_NAME"		},
40 	{ "_NET_WM_STATE"		},
41 	{ "_NET_WM_STATE_FULLSCREEN"	},
42 	{ "_NET_WM_STATE_MODAL"		},
43 	{ "_NET_WM_STATE_HIDDEN"	},
44 	{ "_NET_SUPPORTING_WM_CHECK"	},
45 	{ "UTF8_STRING"			},
46 	{ "_NET_SUPPORTED"		},
47 	{ "WM_PROTOCOLS"		},
48 	{ "WM_DELETE_WINDOW"		},
49 	{ "WM_NAME"			},
50 	{ "WM_STATE"			},
51 	{ "WM_CHANGE_STATE"		},
52 	{ "WM_TAKE_FOCUS"		},
53 	{ "_MOTIF_WM_HINTS"		},
54 };
55 
56 void
initewmh(void)57 initewmh(void) {
58 	int i;
59 	char name[] = "echinus";
60 	XSetWindowAttributes wa;
61 	Window win;
62 
63 	for (i = 0; i < NATOMS; i++)
64 		atom[i] = XInternAtom(dpy, atomnames[i][0], False);
65 	XChangeProperty(dpy, root,
66 	    atom[Supported], XA_ATOM, 32,
67 	    PropModeReplace, (unsigned char *) atom, NATOMS);
68 
69 	wa.override_redirect = True;
70 	win = XCreateWindow(dpy, root, -100, 0, 1, 1,
71 			0, DefaultDepth(dpy, screen), CopyFromParent,
72 			DefaultVisual(dpy, screen), CWOverrideRedirect, &wa);
73 	XChangeProperty(dpy, win, atom[WindowName], atom[Utf8String], 8,
74 		       	PropModeReplace, (unsigned char*)name, strlen(name));
75 	XChangeProperty(dpy, root, atom[WMCheck], XA_WINDOW, 32,
76 		       	PropModeReplace, (unsigned char*)&win, 1);
77 }
78 
79 void
update_echinus_layout_name(Client * c)80 update_echinus_layout_name(Client *c) {
81 	XChangeProperty(dpy, root, atom[ELayout],
82 	    XA_STRING, 8, PropModeReplace,
83 	    (const unsigned char *) &views[curmontag].layout->symbol, 1L);
84 }
85 
86 void
ewmh_update_net_client_list()87 ewmh_update_net_client_list() {
88 	Window *wins = NULL;
89 	Client *c;
90 	int i, n = 0;
91 
92 	for (c = stack; c; c = c->snext)
93 		n++;
94 	if (!n) {
95 		XChangeProperty(dpy, root, atom[ClientList], XA_WINDOW, 32,
96 			       	PropModeReplace, (unsigned char *) wins, n);
97 		XChangeProperty(dpy, root, atom[ClientListStacking], XA_WINDOW,
98 			       	32, PropModeReplace, (unsigned char *) wins, n);
99 		return;
100 	}
101 	wins = malloc(sizeof(Window) * n);
102 	for (i = 0, c = stack; c; c = c->snext)
103 		wins[i++] = c->win;
104 	XChangeProperty(dpy, root,
105 	    atom[ClientListStacking], XA_WINDOW, 32, PropModeReplace,
106 	    (unsigned char *) wins, n);
107 	for (i = 0, c = clients; c; c = c->next)
108 		wins[i++] = c->win;
109 	XChangeProperty(dpy, root,
110 	    atom[ClientList], XA_WINDOW, 32, PropModeReplace, (unsigned char *) wins, n);
111 	free(wins);
112 	XFlush(dpy);
113 }
114 
115 void
ewmh_update_net_number_of_desktops()116 ewmh_update_net_number_of_desktops() {
117 	XChangeProperty(dpy, root,
118 	    atom[NumberOfDesk], XA_CARDINAL, 32, PropModeReplace,
119 	    (unsigned char *) &ntags, 1);
120 }
121 
122 void
ewmh_update_net_current_desktop()123 ewmh_update_net_current_desktop() {
124 	Monitor *m;
125 	unsigned long *seltags;
126 	unsigned int i;
127 
128 	seltags = emallocz(ntags * sizeof(unsigned long));
129 	for (m = monitors; m != NULL; m = m->next) {
130 		for (i = 0; i < ntags; i++)
131 			seltags[i] |= m->seltags[i];
132 	}
133 	XChangeProperty(dpy, root,
134 	    atom[ESelTags], XA_CARDINAL, 32, PropModeReplace,
135 	    (unsigned char *) seltags, ntags);
136 	XChangeProperty(dpy, root, atom[CurDesk], XA_CARDINAL, 32,
137 	    PropModeReplace, (unsigned char *) &curmontag, 1);
138 	update_echinus_layout_name(NULL);
139 	free(seltags);
140 }
141 
142 void
ewmh_update_net_window_desktop(Client * c)143 ewmh_update_net_window_desktop(Client *c) {
144 	unsigned int i;
145 
146 	for (i = 0; i < ntags && !c->tags[i]; i++);
147 	XChangeProperty(dpy, c->win,
148 	    atom[WindowDesk], XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &i, 1);
149 }
150 
151 void
ewmh_update_net_work_area(Client * c)152 ewmh_update_net_work_area(Client *c) {
153 	unsigned long *geoms;
154 	int i;
155 
156 	geoms = malloc(sizeof(unsigned long)*4*ntags);
157 	for(i = 0; i < ntags; i++) {
158 		geoms[i*4] = 0;
159 		geoms[i*4+1] = 0;
160 		geoms[i*4+2] = DisplayWidth(dpy, screen);
161 		geoms[i*4+3] = DisplayHeight(dpy, screen);
162 	}
163 	XChangeProperty(dpy, root,
164 	    atom[WorkArea], XA_CARDINAL, 32, PropModeReplace, (unsigned char *) geoms, ntags*4);
165 	free(geoms);
166 }
167 
168 void
ewmh_update_net_desktop_names()169 ewmh_update_net_desktop_names() {
170 	char buf[1024], *pos;
171 	unsigned int i;
172 	int len = 0;
173 
174 	pos = buf;
175 	for (i = 0; i < ntags; i++) {
176 		snprintf(pos, strlen(tags[i]) + 1, "%s", tags[i]);
177 		pos += (strlen(tags[i]) + 1);
178 	}
179 	len = pos - buf;
180 
181 	XChangeProperty(dpy, root,
182 	    atom[DeskNames], atom[Utf8String], 8, PropModeReplace,
183 	    (unsigned char *) buf, len);
184 }
185 
186 void
ewmh_update_net_active_window()187 ewmh_update_net_active_window() {
188 	Window win;
189 
190 	win = sel ? sel->win : None;
191 	XChangeProperty(dpy, root,
192 	    atom[ActiveWindow], XA_WINDOW, 32, PropModeReplace,
193 	    (unsigned char *) &win, 1);
194 }
195 
196 void
mwm_process_atom(Client * c)197 mwm_process_atom(Client *c) {
198 	Atom real;
199 	int format;
200 	unsigned char *data = NULL;
201 	CARD32 *hint;
202 	unsigned long n, extra;
203 #define MWM_HINTS_ELEMENTS 5
204 #define MWM_DECOR_ALL(x) ((x) & (1L << 0))
205 #define MWM_DECOR_TITLE(x) ((x) & (1L << 3))
206 #define MWM_DECOR_BORDER(x) ((x) & (1L << 1))
207 #define MWM_HINTS_DECOR(x) ((x) & (1L << 1))
208 	if (XGetWindowProperty(dpy, c->win, atom[MWMHints], 0L, 20L, False,
209 		atom[MWMHints], &real, &format, &n, &extra,
210 		(unsigned char **) &data) == Success && n >= MWM_HINTS_ELEMENTS) {
211 		hint = (CARD32 *) data;
212 		if (MWM_HINTS_DECOR(hint[0]) && !(MWM_DECOR_ALL(hint[2]))) {
213 			c->title = MWM_DECOR_TITLE(hint[2]) ? root : (Window) NULL;
214 			c->border = MWM_DECOR_BORDER(hint[2]) ? style.border : 0;
215 		}
216 	}
217 	XFree(data);
218 }
219 
220 void
ewmh_process_state_atom(Client * c,Atom state,int set)221 ewmh_process_state_atom(Client *c, Atom state, int set) {
222 	CARD32 data[2];
223 
224 	data[1] = None;
225 	if ((state = atom[WindowStateFs])) {
226 		focus(c);
227 		if (set && !c->ismax) {
228 			c->wasfloating = c->isfloating;
229 			c->isfloating = True;
230 			data[0] = state;
231 		} else {
232 			c->isfloating = c->wasfloating;
233 			c->wasfloating = True;
234 			data[0] = None;
235 		}
236 		XChangeProperty(dpy, c->win, atom[WindowState], XA_ATOM, 32,
237 		    PropModeReplace, (unsigned char *) data, 2);
238 		DPRINT;
239 		togglemax(NULL);
240 		arrange(curmonitor());
241 		DPRINTF("%s: x%d y%d w%d h%d\n", c->name, c->x, c->y, c->w, c->h);
242 	}
243 	if (state == atom[WindowStateModal])
244 		focus(c);
245 }
246 
247 void
clientmessage(XEvent * e)248 clientmessage(XEvent *e) {
249 	XClientMessageEvent *ev = &e->xclient;
250 	Client *c;
251 
252 	if (ev->message_type == atom[ActiveWindow]) {
253 		if ((c = getclient(ev->window, clients, ClientWindow))) {
254 				c->isicon = False;
255 				focus(c);
256 				arrange(curmonitor());
257 		}
258 	} else if (ev->message_type == atom[CurDesk]) {
259 		view(tags[ev->data.l[0]]);
260 	} else if (ev->message_type == atom[WindowState]) {
261 		if ((c = getclient(ev->window, clients, ClientWindow))) {
262 			ewmh_process_state_atom(c, (Atom) ev->data.l[1], ev->data.l[0]);
263 			if (ev->data.l[2])
264 				ewmh_process_state_atom(c,
265 				    (Atom) ev->data.l[2], ev->data.l[0]);
266 		}
267 	} else if (ev->message_type == atom[WMChangeState]) {
268 		if ((c = getclient(ev->window, clients, ClientWindow))) {
269 			if (ev->data.l[0] == IconicState) {
270 				focus(c);
271 				iconify(NULL);
272 			}
273 		}
274 	}
275 }
276 
277 void
setopacity(Client * c,unsigned int opacity)278 setopacity(Client *c, unsigned int opacity) {
279 	if (opacity == OPAQUE) {
280 		XDeleteProperty(dpy, c->win, atom[WindowOpacity]);
281 		XDeleteProperty(dpy, c->frame, atom[WindowOpacity]);
282 	} else {
283 		XChangeProperty(dpy, c->win, atom[WindowOpacity],
284 		    XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &opacity, 1L);
285 		XChangeProperty(dpy, c->frame, atom[WindowOpacity],
286 		    XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &opacity, 1L);
287 
288 	}
289 }
290 
291 void *
getatom(Window win,Atom atom,unsigned long * nitems)292 getatom(Window win, Atom atom, unsigned long *nitems) {
293 	int format, status;
294 	unsigned char *ret = NULL;
295 	unsigned long extra;
296 	Atom real;
297 
298 	status = XGetWindowProperty(dpy, win, atom, 0L, 64L, False, AnyPropertyType,
299 			&real, &format, nitems, &extra, (unsigned char **)&ret);
300 	if (status != Success) {
301 		*nitems = 0;
302 		return NULL;
303 	}
304 
305 	return ret;
306 }
307 
308 Bool
checkatom(Window win,Atom bigatom,Atom smallatom)309 checkatom(Window win, Atom bigatom, Atom smallatom) {
310 	Atom *state;
311 	unsigned long i, n;
312 	Bool ret = False;
313 
314 	state = (Atom*)getatom(win, bigatom, &n);
315 	for (i = 0; i < n; i++) {
316 		if (state[i] == smallatom)
317 			ret = True;
318 	}
319 	XFree(state);
320 	return ret;
321 }
322 
323 int
getstruts(Client * c)324 getstruts(Client *c) {
325 	unsigned long *state;
326 	int ret = 0;
327 	Monitor *m;
328 	unsigned long i, n;
329 
330 	if(!(m = clientmonitor(c)))
331 		return ret;
332 
333 	state = (unsigned long*)getatom(c->win, atom[StrutPartial], &n);
334 	if (n) {
335 		for (i = LeftStrut; i < LastStrut; i++)
336 			m->struts[i] = max(state[i], m->struts[i]);
337 		ret = 1;
338 	}
339 	XFree(state);
340 	return ret;
341 }
342 
343 void (*updateatom[]) (Client *) = {
344 	[ClientList] = ewmh_update_net_client_list,
345 	[ActiveWindow] = ewmh_update_net_active_window,
346 	[WindowDesk] = ewmh_update_net_window_desktop,
347 	[NumberOfDesk] = ewmh_update_net_number_of_desktops,
348 	[DeskNames] = ewmh_update_net_desktop_names,
349 	[CurDesk] = ewmh_update_net_current_desktop,
350 	[ELayout] = update_echinus_layout_name,
351 	[WorkArea] = ewmh_update_net_work_area,
352 };
353