client: Expose group windows.
[awesome.git] / ewmh.c
bloba4c241da2ddd23e749703571d59fae608433d0dd
1 /*
2 * ewmh.c - EWMH support functions
4 * Copyright © 2007-2008 Julien Danjou <julien@danjou.info>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <sys/types.h>
23 #include <unistd.h>
25 #include <xcb/xcb.h>
26 #include <xcb/xcb_atom.h>
28 #include "ewmh.h"
29 #include "tag.h"
30 #include "screen.h"
31 #include "client.h"
32 #include "widget.h"
33 #include "cnode.h"
34 #include "wibox.h"
35 #include "common/atoms.h"
37 extern awesome_t globalconf;
39 #define _NET_WM_STATE_REMOVE 0
40 #define _NET_WM_STATE_ADD 1
41 #define _NET_WM_STATE_TOGGLE 2
43 void
44 ewmh_init(int phys_screen)
46 xcb_window_t father;
47 xcb_screen_t *xscreen = xutil_screen_get(globalconf.connection, phys_screen);
48 xcb_atom_t atom[] =
50 _NET_SUPPORTED,
51 _NET_SUPPORTING_WM_CHECK,
52 _NET_CLIENT_LIST,
53 _NET_CLIENT_LIST_STACKING,
54 _NET_NUMBER_OF_DESKTOPS,
55 _NET_CURRENT_DESKTOP,
56 _NET_DESKTOP_NAMES,
57 _NET_ACTIVE_WINDOW,
58 _NET_WORKAREA,
59 _NET_CLOSE_WINDOW,
60 _NET_WM_NAME,
61 _NET_WM_STRUT_PARTIAL,
62 _NET_WM_ICON_NAME,
63 _NET_WM_VISIBLE_ICON_NAME,
64 _NET_WM_DESKTOP,
65 _NET_WM_WINDOW_TYPE,
66 _NET_WM_WINDOW_TYPE_DESKTOP,
67 _NET_WM_WINDOW_TYPE_DOCK,
68 _NET_WM_WINDOW_TYPE_TOOLBAR,
69 _NET_WM_WINDOW_TYPE_MENU,
70 _NET_WM_WINDOW_TYPE_UTILITY,
71 _NET_WM_WINDOW_TYPE_SPLASH,
72 _NET_WM_WINDOW_TYPE_DIALOG,
73 _NET_WM_WINDOW_TYPE_NORMAL,
74 _NET_WM_ICON,
75 _NET_WM_PID,
76 _NET_WM_STATE,
77 _NET_WM_STATE_STICKY,
78 _NET_WM_STATE_SKIP_TASKBAR,
79 _NET_WM_STATE_FULLSCREEN,
80 _NET_WM_STATE_MAXIMIZED_HORZ,
81 _NET_WM_STATE_MAXIMIZED_VERT,
82 _NET_WM_STATE_ABOVE,
83 _NET_WM_STATE_BELOW,
84 _NET_WM_STATE_MODAL,
85 _NET_WM_STATE_HIDDEN,
86 _NET_WM_STATE_DEMANDS_ATTENTION
88 int i;
90 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
91 xscreen->root, _NET_SUPPORTED, ATOM, 32,
92 countof(atom), atom);
94 /* create our own window */
95 father = xcb_generate_id(globalconf.connection);
96 xcb_create_window(globalconf.connection, xscreen->root_depth,
97 father, xscreen->root, -1, -1, 1, 1, 0,
98 XCB_COPY_FROM_PARENT, xscreen->root_visual, 0, NULL);
100 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
101 xscreen->root, _NET_SUPPORTING_WM_CHECK, WINDOW, 32,
102 1, &father);
104 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
105 father, _NET_SUPPORTING_WM_CHECK, WINDOW, 32,
106 1, &father);
108 /* set the window manager name */
109 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
110 father, _NET_WM_NAME, UTF8_STRING, 8, 7, "awesome");
112 /* set the window manager PID */
113 i = getpid();
114 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
115 father, _NET_WM_PID, CARDINAL, 32, 1, &i);
118 void
119 ewmh_update_net_client_list(int phys_screen)
121 xcb_window_t *wins;
122 client_t *c;
123 int n = 0;
125 for(c = globalconf.clients; c; c = c->next)
126 n++;
128 wins = p_alloca(xcb_window_t, n);
130 for(n = 0, c = globalconf.clients; c; c = c->next, n++)
131 if(c->phys_screen == phys_screen)
132 wins[n] = c->win;
134 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
135 xutil_screen_get(globalconf.connection, phys_screen)->root,
136 _NET_CLIENT_LIST, WINDOW, 32, n, wins);
139 /** Set the client list in stacking order, bottom to top.
140 * \param phys_screen The physical screen id.
142 void
143 ewmh_update_net_client_list_stacking(int phys_screen)
145 xcb_window_t *wins;
146 client_node_t *c;
147 int n = 0;
149 for(c = globalconf.stack; c; c = c->next)
150 n++;
152 wins = p_alloca(xcb_window_t, n);
154 for(n = 0, c = *client_node_list_last(&globalconf.stack); c; c = c->prev, n++)
155 if(c->client->phys_screen == phys_screen)
156 wins[n] = c->client->win;
158 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
159 xutil_screen_get(globalconf.connection, phys_screen)->root,
160 _NET_CLIENT_LIST_STACKING, WINDOW, 32, n, wins);
163 void
164 ewmh_update_net_numbers_of_desktop(int phys_screen)
166 uint32_t count = globalconf.screens[phys_screen].tags.len;
168 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
169 xutil_screen_get(globalconf.connection, phys_screen)->root,
170 _NET_NUMBER_OF_DESKTOPS, CARDINAL, 32, 1, &count);
173 void
174 ewmh_update_net_current_desktop(int phys_screen)
176 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
177 uint32_t count = 0;
178 tag_t **curtags = tags_get_current(phys_screen);
180 while(count < (uint32_t) tags->len && tags->tab[count] != curtags[0])
181 count++;
183 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
184 xutil_screen_get(globalconf.connection, phys_screen)->root,
185 _NET_CURRENT_DESKTOP, CARDINAL, 32, 1, &count);
187 p_delete(&curtags);
190 void
191 ewmh_update_net_desktop_names(int phys_screen)
193 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
194 buffer_t buf;
196 buffer_inita(&buf, BUFSIZ);
198 for(int i = 0; i < tags->len; i++)
200 buffer_adds(&buf, tags->tab[i]->name);
201 buffer_addc(&buf, '\0');
204 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
205 xutil_screen_get(globalconf.connection, phys_screen)->root,
206 _NET_DESKTOP_NAMES, UTF8_STRING, 8, buf.len, buf.s);
207 buffer_wipe(&buf);
210 /** Update the work area space for each physical screen and each desktop.
211 * \param phys_screen The physical screen id.
213 void
214 ewmh_update_workarea(int phys_screen)
216 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
217 uint32_t *area = p_alloca(uint32_t, tags->len * 4);
218 area_t geom = screen_area_get(phys_screen,
219 &globalconf.screens[phys_screen].wiboxes,
220 &globalconf.screens[phys_screen].padding,
221 true);
224 for(int i = 0; i < tags->len; i++)
226 area[4 * i + 0] = geom.x;
227 area[4 * i + 1] = geom.y;
228 area[4 * i + 2] = geom.width;
229 area[4 * i + 3] = geom.height;
232 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
233 xutil_screen_get(globalconf.connection, phys_screen)->root,
234 _NET_WORKAREA, CARDINAL, 32, tags->len * 4, area);
237 void
238 ewmh_update_net_active_window(int phys_screen)
240 xcb_window_t win;
242 if(globalconf.screen_focus->client_focus
243 && globalconf.screen_focus->client_focus->phys_screen == phys_screen)
244 win = globalconf.screen_focus->client_focus->win;
245 else
246 win = XCB_NONE;
248 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
249 xutil_screen_get(globalconf.connection, phys_screen)->root,
250 _NET_ACTIVE_WINDOW, WINDOW, 32, 1, &win);
253 static void
254 ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set)
256 if(state == _NET_WM_STATE_STICKY)
258 if(set == _NET_WM_STATE_REMOVE)
259 client_setsticky(c, false);
260 else if(set == _NET_WM_STATE_ADD)
261 client_setsticky(c, true);
262 else if(set == _NET_WM_STATE_TOGGLE)
263 client_setsticky(c, !c->issticky);
265 else if(state == _NET_WM_STATE_SKIP_TASKBAR)
267 if(set == _NET_WM_STATE_REMOVE)
269 c->skiptb = false;
270 ewmh_client_update_hints(c);
272 else if(set == _NET_WM_STATE_ADD)
274 c->skiptb = true;
275 ewmh_client_update_hints(c);
277 else if(set == _NET_WM_STATE_TOGGLE)
279 c->skiptb = !c->skiptb;
280 ewmh_client_update_hints(c);
283 else if(state == _NET_WM_STATE_FULLSCREEN)
285 if(set == _NET_WM_STATE_REMOVE)
286 client_setfullscreen(c, false);
287 else if(set == _NET_WM_STATE_ADD)
288 client_setfullscreen(c, true);
289 else if(set == _NET_WM_STATE_TOGGLE)
290 client_setfullscreen(c, !c->isfullscreen);
292 else if(state == _NET_WM_STATE_MAXIMIZED_HORZ)
294 if(set == _NET_WM_STATE_REMOVE)
295 client_setmaxhoriz(c, false);
296 else if(set == _NET_WM_STATE_ADD)
297 client_setmaxhoriz(c, true);
298 else if(set == _NET_WM_STATE_TOGGLE)
299 client_setmaxhoriz(c, !c->ismaxhoriz);
301 else if(state == _NET_WM_STATE_MAXIMIZED_VERT)
303 if(set == _NET_WM_STATE_REMOVE)
304 client_setmaxvert(c, false);
305 else if(set == _NET_WM_STATE_ADD)
306 client_setmaxvert(c, true);
307 else if(set == _NET_WM_STATE_TOGGLE)
308 client_setmaxvert(c, !c->ismaxvert);
310 else if(state == _NET_WM_STATE_ABOVE)
312 if(set == _NET_WM_STATE_REMOVE)
313 client_setabove(c, false);
314 else if(set == _NET_WM_STATE_ADD)
315 client_setabove(c, true);
316 else if(set == _NET_WM_STATE_TOGGLE)
317 client_setabove(c, !c->isabove);
319 else if(state == _NET_WM_STATE_BELOW)
321 if(set == _NET_WM_STATE_REMOVE)
322 client_setbelow(c, false);
323 else if(set == _NET_WM_STATE_ADD)
324 client_setbelow(c, true);
325 else if(set == _NET_WM_STATE_TOGGLE)
326 client_setbelow(c, !c->isbelow);
328 else if(state == _NET_WM_STATE_MODAL)
330 if(set == _NET_WM_STATE_REMOVE)
331 client_setmodal(c, false);
332 else if(set == _NET_WM_STATE_ADD)
333 client_setmodal(c, true);
334 else if(set == _NET_WM_STATE_TOGGLE)
335 client_setmodal(c, !c->ismodal);
337 else if(state == _NET_WM_STATE_HIDDEN)
339 if(set == _NET_WM_STATE_REMOVE)
340 client_setminimized(c, false);
341 else if(set == _NET_WM_STATE_ADD)
342 client_setminimized(c, true);
343 else if(set == _NET_WM_STATE_TOGGLE)
344 client_setminimized(c, !c->isminimized);
346 else if(state == _NET_WM_STATE_DEMANDS_ATTENTION)
348 if(set == _NET_WM_STATE_REMOVE)
349 c->isurgent = false;
350 else if(set == _NET_WM_STATE_ADD)
351 c->isurgent = true;
352 else if(set == _NET_WM_STATE_TOGGLE)
353 c->isurgent = !c->isurgent;
355 /* execute hook */
356 hooks_property(c, "urgent");
361 ewmh_process_client_message(xcb_client_message_event_t *ev)
363 client_t *c;
364 int screen;
366 if(ev->type == _NET_CURRENT_DESKTOP)
367 for(screen = 0;
368 screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
369 screen++)
371 if(ev->window == xutil_screen_get(globalconf.connection, screen)->root)
372 tag_view_only_byindex(screen, ev->data.data32[0]);
374 else if(ev->type == _NET_CLOSE_WINDOW)
376 if((c = client_getbywin(ev->window)))
377 client_kill(c);
379 else if(ev->type == _NET_WM_DESKTOP)
381 if((c = client_getbywin(ev->window)))
383 tag_array_t *tags = &globalconf.screens[c->screen].tags;
385 if(ev->data.data32[0] == 0xffffffff)
386 c->issticky = true;
387 else
388 for(int i = 0; i < tags->len; i++)
389 if((int)ev->data.data32[0] == i)
390 tag_client(c, tags->tab[i]);
391 else
392 untag_client(c, tags->tab[i]);
395 else if(ev->type == _NET_WM_STATE)
397 if((c = client_getbywin(ev->window)))
399 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[1], ev->data.data32[0]);
400 if(ev->data.data32[2])
401 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[2],
402 ev->data.data32[0]);
406 return 0;
409 /** Update client EWMH hints.
410 * \param c The client.
412 void
413 ewmh_client_update_hints(client_t *c)
415 xcb_atom_t state[8]; /* number of defined state atoms */
416 int i = 0;
418 if(c->ismodal)
419 state[i++] = _NET_WM_STATE_MODAL;
420 if(c->isfullscreen)
421 state[i++] = _NET_WM_STATE_FULLSCREEN;
422 if(c->ismaxvert)
423 state[i++] = _NET_WM_STATE_MAXIMIZED_VERT;
424 if(c->ismaxhoriz)
425 state[i++] = _NET_WM_STATE_MAXIMIZED_HORZ;
426 if(c->issticky)
427 state[i++] = _NET_WM_STATE_STICKY;
428 if(c->skiptb)
429 state[i++] = _NET_WM_STATE_SKIP_TASKBAR;
430 if(c->isabove)
431 state[i++] = _NET_WM_STATE_ABOVE;
432 if(c->isbelow)
433 state[i++] = _NET_WM_STATE_BELOW;
434 if(c->isminimized)
435 state[i++] = _NET_WM_STATE_HIDDEN;
436 if(c->isurgent)
437 state[i++] = _NET_WM_STATE_DEMANDS_ATTENTION;
439 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
440 c->win, _NET_WM_STATE, ATOM, 32, i, state);
443 void
444 ewmh_client_check_hints(client_t *c)
446 xcb_atom_t *state;
447 void *data = NULL;
448 int desktop;
449 xcb_get_property_cookie_t c0, c1, c2;
450 xcb_get_property_reply_t *reply;
452 /* Send the GetProperty requests which will be processed later */
453 c0 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
454 _NET_WM_DESKTOP, XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
456 c1 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
457 _NET_WM_STATE, ATOM, 0, UINT32_MAX);
459 c2 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
460 _NET_WM_WINDOW_TYPE, ATOM, 0, UINT32_MAX);
462 reply = xcb_get_property_reply(globalconf.connection, c0, NULL);
463 if(reply && reply->value_len && (data = xcb_get_property_value(reply)))
465 tag_array_t *tags = &globalconf.screens[c->screen].tags;
467 desktop = *(uint32_t *) data;
468 if(desktop == -1)
469 c->issticky = true;
470 else
471 for(int i = 0; i < tags->len; i++)
472 if(desktop == i)
473 tag_client(c, tags->tab[i]);
474 else
475 untag_client(c, tags->tab[i]);
478 p_delete(&reply);
480 reply = xcb_get_property_reply(globalconf.connection, c1, NULL);
481 if(reply && (data = xcb_get_property_value(reply)))
483 state = (xcb_atom_t *) data;
484 for(int i = 0; i < xcb_get_property_value_length(reply); i++)
485 ewmh_process_state_atom(c, state[i], _NET_WM_STATE_ADD);
488 p_delete(&reply);
490 reply = xcb_get_property_reply(globalconf.connection, c2, NULL);
491 if(reply && (data = xcb_get_property_value(reply)))
493 state = (xcb_atom_t *) data;
494 for(int i = 0; i < xcb_get_property_value_length(reply); i++)
495 if(state[i] == _NET_WM_WINDOW_TYPE_DESKTOP)
496 c->type = MAX(c->type, WINDOW_TYPE_DESKTOP);
497 else if(state[i] == _NET_WM_WINDOW_TYPE_DIALOG)
498 c->type = MAX(c->type, WINDOW_TYPE_DIALOG);
499 else if(state[i] == _NET_WM_WINDOW_TYPE_SPLASH)
500 c->type = MAX(c->type, WINDOW_TYPE_SPLASH);
501 else if(state[i] == _NET_WM_WINDOW_TYPE_DOCK)
502 c->type = MAX(c->type, WINDOW_TYPE_DOCK);
503 else if(state[i] == _NET_WM_WINDOW_TYPE_MENU)
504 c->type = MAX(c->type, WINDOW_TYPE_MENU);
505 else if(state[i] == _NET_WM_WINDOW_TYPE_TOOLBAR)
506 c->type = MAX(c->type, WINDOW_TYPE_TOOLBAR);
507 else if(state[i] == _NET_WM_WINDOW_TYPE_UTILITY)
508 c->type = MAX(c->type, WINDOW_TYPE_UTILITY);
511 p_delete(&reply);
514 /** Update the WM strut of a client.
515 * \param c The client.
517 void
518 ewmh_client_strut_update(client_t *c, xcb_get_property_reply_t *strut_r)
520 void *data;
521 xcb_get_property_reply_t *mstrut_r = NULL;
523 if(!strut_r)
525 xcb_get_property_cookie_t strut_q = xcb_get_property_unchecked(globalconf.connection, false, c->win,
526 _NET_WM_STRUT_PARTIAL, CARDINAL, 0, 12);
527 strut_r = mstrut_r = xcb_get_property_reply(globalconf.connection, strut_q, NULL);
530 if(strut_r
531 && strut_r->value_len
532 && (data = xcb_get_property_value(strut_r)))
534 uint32_t *strut = data;
536 if(c->strut.left != strut[0]
537 || c->strut.right != strut[1]
538 || c->strut.top != strut[2]
539 || c->strut.bottom != strut[3]
540 || c->strut.left_start_y != strut[4]
541 || c->strut.left_end_y != strut[5]
542 || c->strut.right_start_y != strut[6]
543 || c->strut.right_end_y != strut[7]
544 || c->strut.top_start_x != strut[8]
545 || c->strut.top_end_x != strut[9]
546 || c->strut.bottom_start_x != strut[10]
547 || c->strut.bottom_end_x != strut[11])
549 c->strut.left = strut[0];
550 c->strut.right = strut[1];
551 c->strut.top = strut[2];
552 c->strut.bottom = strut[3];
553 c->strut.left_start_y = strut[4];
554 c->strut.left_end_y = strut[5];
555 c->strut.right_start_y = strut[6];
556 c->strut.right_end_y = strut[7];
557 c->strut.top_start_x = strut[8];
558 c->strut.top_end_x = strut[9];
559 c->strut.bottom_start_x = strut[10];
560 c->strut.bottom_end_x = strut[11];
562 client_need_arrange(c);
563 /* All the wiboxes (may) need to be repositioned */
564 for(int screen = 0; screen < globalconf.nscreen; screen++)
565 for(int i = 0; i < globalconf.screens[screen].wiboxes.len; i++)
567 wibox_t *s = globalconf.screens[screen].wiboxes.tab[i];
568 wibox_position_update(s);
573 p_delete(&mstrut_r);
576 /** Send request to get NET_WM_ICON (EWMH)
577 * \param w The window.
578 * \return The cookie associated with the request.
580 xcb_get_property_cookie_t
581 ewmh_window_icon_get_unchecked(xcb_window_t w)
583 return xcb_get_property_unchecked(globalconf.connection, false, w,
584 _NET_WM_ICON, CARDINAL, 0, UINT32_MAX);
587 image_t *
588 ewmh_window_icon_from_reply(xcb_get_property_reply_t *r)
590 uint32_t *data;
592 if(!r || r->type != CARDINAL || r->format != 32 || r->length < 2 ||
593 !(data = (uint32_t *) xcb_get_property_value(r)))
594 return NULL;
596 if(data[0] && data[1])
597 return image_new_from_argb32(data[0], data[1], data + 2);
599 return NULL;
602 /** Get NET_WM_ICON.
603 * \param cookie The cookie.
604 * \return A draw_image_t structure which must be deleted after usage.
606 image_t *
607 ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie)
609 xcb_get_property_reply_t *r = xcb_get_property_reply(globalconf.connection, cookie, NULL);
610 image_t *icon = ewmh_window_icon_from_reply(r);
611 p_delete(&r);
612 return icon;
615 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80