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