0 /*
1 * Support for Extended Window Manager Hints
2 * http://standards.freedesktop.org/wm-spec/wm-spec-latest.html
3 */
4
5 /*
6 * Copyright 2006-2007 Johan Veenhuizen
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 */
26
27 #include <X11/Xatom.h>
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30
31 #include "global.h"
32 #include "hints.h"
33 #include "lib.h"
34 #include "window.h"
35 #include "ewmh.h"
36
37 struct winlist {
38 long *win;
39 int len;
40 int lim;
41 };
42
43 /* EWMH atoms, indexed by the above enum */
44 Atom atom[NATOM];
45
46 /* The EWMH UTF-8 string type */
47 static Atom UTF8_STRING;
48
49 /* List of managed clients, oldest first */
50 static struct winlist clientlist = { NULL, 0, 0 };
51
52 /* Geometry of the workarea (x, y, width, height) */
53 static long workarea[4];
54
55 /* The window used for announcing EWMH support */
56 static Window supportwin = None;
57
58 static void ewmh_init(void);
59 static void ewmh_fini(void);
60 static void ewmh_manage(struct window *);
61 static void ewmh_unmanage(struct window *);
62 static void ewmh_activate(struct window *);
63 static void ewmh_restack(void);
64 static int ewmh_clientmessage(struct window *, XClientMessageEvent *);
65 static void addclient(Window);
66 static void delclient(Window);
67
68 struct hints ewmh_hints = {
69 .init = ewmh_init,
70 .fini = ewmh_fini,
71 .manage = ewmh_manage,
72 .unmanage = ewmh_unmanage,
73 .activate = ewmh_activate,
74 .restack = ewmh_restack,
75 .clientmessage = ewmh_clientmessage,
76 };
77
78 static void ewmh_init(void)
79 {
80 XSetWindowAttributes attr;
81 long ndesk;
82 long curdesk;
83 long geom[2];
84 long viewport[2];
85 long active;
86 long monitor;
87
88 attr.override_redirect = True;
89 supportwin = XCreateWindow(display, root, 0, 0, 1, 1, 0,
90 CopyFromParent, InputOnly, CopyFromParent,
91 CWOverrideRedirect, &attr);
92
93 ndesk = 1;
94 curdesk = 0;
95 geom[0] = DisplayWidth(display, screen);
96 geom[1] = DisplayHeight(display, screen);
97 viewport[0] = 0;
98 viewport[1] = 0;
99 active = None;
100 monitor = 0;
101 workarea[0] = 0; /* x */
102 workarea[1] = 0; /* y */
103 workarea[2] = DisplayWidth(display, screen);
104 workarea[3] = DisplayHeight(display, screen);
105
106 UTF8_STRING = XInternAtom(display, "UTF8_STRING", False);
107
108 atom[NET_SUPPORTED] = XInternAtom(display,
109 "_NET_SUPPORTED", False);
110 atom[NET_CLIENT_LIST] = XInternAtom(display,
111 "_NET_CLIENT_LIST", False);
112 atom[NET_CLIENT_LIST_STACKING] = XInternAtom(display,
113 "_NET_CLIENT_LIST_STACKING", False);
114 atom[NET_NUMBER_OF_DESKTOPS] = XInternAtom(display,
115 "_NET_NUMBER_OF_DESKTOPS", False);
116 atom[NET_DESKTOP_GEOMETRY] = XInternAtom(display,
117 "_NET_DESKTOP_GEOMETRY", False);
118 atom[NET_DESKTOP_VIEWPORT] = XInternAtom(display,
119 "_NET_DESKTOP_VIEWPORT", False);
120 atom[NET_CURRENT_DESKTOP] = XInternAtom(display,
121 "_NET_CURRENT_DESKTOP", False);
122 atom[NET_ACTIVE_WINDOW] = XInternAtom(display,
123 "_NET_ACTIVE_WINDOW", False);
124 atom[NET_WORKAREA] = XInternAtom(display,
125 "_NET_WORKAREA", False);
126 atom[NET_SUPPORTING_WM_CHECK] = XInternAtom(display,
127 "_NET_SUPPORTING_WM_CHECK", False);
128 atom[NET_WM_NAME] = XInternAtom(display,
129 "_NET_WM_NAME", False);
130 atom[NET_CLOSE_WINDOW] = XInternAtom(display,
131 "_NET_CLOSE_WINDOW", False);
132 //atom[NET_WM_FULLSCREEN_MONITORS] = XInternAtom(display,
133 // "_NET_WM_FULLSCREEN_MONITORS", False);
134 atom[NET_WM_WINDOW_TYPE] = XInternAtom(display,
135 "_NET_WM_WINDOW_TYPE", False);
136 atom[NET_WM_WINDOW_TYPE_DESKTOP] = XInternAtom(display,
137 "_NET_WM_WINDOW_TYPE_DESKTOP", False);
138 atom[NET_WM_WINDOW_TYPE_DIALOG] = XInternAtom(display,
139 "_NET_WM_WINDOW_TYPE_DIALOG", False);
140 atom[NET_WM_WINDOW_TYPE_DOCK] = XInternAtom(display,
141 "_NET_WM_WINDOW_TYPE_DOCK", False);
142 atom[NET_WM_WINDOW_TYPE_MENU] = XInternAtom(display,
143 "_NET_WM_WINDOW_TYPE_MENU", False);
144 atom[NET_WM_WINDOW_TYPE_NORMAL] = XInternAtom(display,
145 "_NET_WM_WINDOW_TYPE_NORMAL", False);
146 atom[NET_WM_WINDOW_TYPE_SPLASH] = XInternAtom(display,
147 "_NET_WM_WINDOW_TYPE_SPLASH", False);
148 atom[NET_WM_WINDOW_TYPE_TOOLBAR] = XInternAtom(display,
149 "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
150 atom[NET_WM_WINDOW_TYPE_UTILITY] = XInternAtom(display,
151 "_NET_WM_WINDOW_TYPE_UTILITY", False);
152 atom[NET_WM_STATE] = XInternAtom(display,
153 "_NET_WM_STATE", False);
154 atom[NET_WM_STATE_FULLSCREEN] = XInternAtom(display,
155 "_NET_WM_STATE_FULLSCREEN", False);
156
157
158 XChangeProperty(display, root, atom[NET_CLIENT_LIST],
159 XA_WINDOW, 32, PropModeReplace, NULL, 0);
160 XChangeProperty(display, root, atom[NET_CLIENT_LIST_STACKING],
161 XA_WINDOW, 32, PropModeReplace, NULL, 0);
162 XChangeProperty(display, root, atom[NET_NUMBER_OF_DESKTOPS],
163 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesk, 1);
164 XChangeProperty(display, root, atom[NET_DESKTOP_GEOMETRY],
165 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&geom, 2);
166 XChangeProperty(display, root, atom[NET_DESKTOP_VIEWPORT],
167 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&viewport, 2);
168 XChangeProperty(display, root, atom[NET_CURRENT_DESKTOP],
169 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&curdesk, 1);
170 XChangeProperty(display, root, atom[NET_ACTIVE_WINDOW],
171 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&active, 1);
172 //XChangeProperty(display, root, atom[NET_WM_FULLSCREEN_MONITORS],
173 // XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&monitor, 1);
174 XChangeProperty(display, root, atom[NET_WORKAREA],
175 XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&workarea, 4);
176 XChangeProperty(display, root, atom[NET_SUPPORTING_WM_CHECK],
177 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&supportwin, 1);
178 XChangeProperty(display, supportwin, atom[NET_SUPPORTING_WM_CHECK],
179 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&supportwin, 1);
180 XChangeProperty(display, supportwin, atom[NET_WM_NAME],
181 UTF8_STRING, 8, PropModeReplace,
182 (unsigned char *)"Karmen", 7);
183
184 /* set this last, when everything is set up */
185 XChangeProperty(display, root, atom[NET_SUPPORTED], XA_ATOM, 32,
186 PropModeReplace, (unsigned char *)atom, NELEM(atom));
187 }
188
189 static void ewmh_fini(void)
190 {
191 /* delete this first, before we tear things down */
192 XDeleteProperty(display, root, atom[NET_SUPPORTED]);
193
194 XDeleteProperty(display, root, atom[NET_CLIENT_LIST]);
195 XDeleteProperty(display, root, atom[NET_CLIENT_LIST_STACKING]);
196 XDeleteProperty(display, root, atom[NET_NUMBER_OF_DESKTOPS]);
197 XDeleteProperty(display, root, atom[NET_DESKTOP_GEOMETRY]);
198 XDeleteProperty(display, root, atom[NET_DESKTOP_VIEWPORT]);
199 XDeleteProperty(display, root, atom[NET_CURRENT_DESKTOP]);
200 XDeleteProperty(display, root, atom[NET_ACTIVE_WINDOW]);
201 XDeleteProperty(display, root, atom[NET_WORKAREA]);
202 XDeleteProperty(display, root, atom[NET_SUPPORTING_WM_CHECK]);
203 XDeleteProperty(display, supportwin, atom[NET_SUPPORTING_WM_CHECK]);
204 XDeleteProperty(display, supportwin, atom[NET_WM_NAME]);
205
206 XDestroyWindow(display, supportwin);
207 }
208
209 static void ewmh_manage(struct window *win)
210 {
211 addclient(win->client);
212 XChangeProperty(display, root, atom[NET_CLIENT_LIST],
213 XA_WINDOW, 32, PropModeReplace,
214 (unsigned char *)clientlist.win, clientlist.len);
215 }
216
217 static void ewmh_unmanage(struct window *win)
218 {
219 delclient(win->client);
220 XChangeProperty(display, root, atom[NET_CLIENT_LIST],
221 XA_WINDOW, 32, PropModeReplace,
222 (unsigned char *)clientlist.win, clientlist.len);
223 }
224
225 static void ewmh_activate(struct window *win)
226 {
227 Window w;
228
229 w = win == NULL ? None : win->client;
230 XChangeProperty(display, root, atom[NET_ACTIVE_WINDOW],
231 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
232 }
233
234 static void ewmh_restack(void)
235 {
236 Window *stack, w;
237 int i, j, n;
238
239 get_client_stack(&stack, &n);
240
241 /* reverse the stack */
242 for (i = 0, j = n - 1; i < j; i++, j--) {
243 w = stack[i];
244 stack[i] = stack[j];
245 stack[j] = w;
246 }
247
248 XChangeProperty(display, root, atom[NET_CLIENT_LIST_STACKING],
249 XA_WINDOW, 32, PropModeReplace,
250 (unsigned char *)stack, n);
251
252 FREE(stack);
253 }
254
255 static int ewmh_clientmessage(struct window *win, XClientMessageEvent *ep)
256 {
257 int type = ep->message_type;
258 int format = ep->format;
259
260 if (type == atom[NET_ACTIVE_WINDOW] && format == 32) {
261 set_active_window(win);
262 } else if (type == atom[NET_CLOSE_WINDOW] && format == 32) {
263 delete_window(win);
264 } else
265 return 0;
266
267 return 1;
268 }
269
270 static void addclient(Window w)
271 {
272 if (clientlist.len == clientlist.lim) {
273 clientlist.lim += 32;
274 clientlist.win = REALLOC(clientlist.win,
275 clientlist.lim * sizeof (long));
276 }
277 clientlist.win[clientlist.len++] = (long)w;
278 }
279
280 static void delclient(Window w)
281 {
282 int i;
283
284 if (clientlist.len == 0)
285 return;
286
287 clientlist.len--;
288 for (i = 0; i < clientlist.len && clientlist.win[i] != (long)w; i++)
289 ;
290 if (i < clientlist.len) {
291 for (; i < clientlist.len; i++)
292 clientlist.win[i] = clientlist.win[i + 1];
293 }
294
295 if (clientlist.len == 0) {
296 FREE(clientlist.win);
297 clientlist.win = NULL;
298 clientlist.lim = 0;
299 }
300 }
301