event: Prevent seeing banned clients on the visible screen.
[awesome.git] / ewmh.c
blobdc4d322b3a61c4c02a806521f003ef78a19ed63e
1 /*
2 * ewmh.c - EWMH support functions
4 * Copyright © 2007-2009 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"
36 #include "common/buffer.h"
38 extern awesome_t globalconf;
40 #define _NET_WM_STATE_REMOVE 0
41 #define _NET_WM_STATE_ADD 1
42 #define _NET_WM_STATE_TOGGLE 2
44 void
45 ewmh_init(int phys_screen)
47 xcb_window_t father;
48 xcb_screen_t *xscreen = xutil_screen_get(globalconf.connection, phys_screen);
49 xcb_atom_t atom[] =
51 _NET_SUPPORTED,
52 _NET_SUPPORTING_WM_CHECK,
53 _NET_CLIENT_LIST,
54 _NET_CLIENT_LIST_STACKING,
55 _NET_NUMBER_OF_DESKTOPS,
56 _NET_CURRENT_DESKTOP,
57 _NET_DESKTOP_NAMES,
58 _NET_ACTIVE_WINDOW,
59 _NET_WORKAREA,
60 _NET_CLOSE_WINDOW,
61 _NET_WM_NAME,
62 _NET_WM_STRUT_PARTIAL,
63 _NET_WM_ICON_NAME,
64 _NET_WM_VISIBLE_ICON_NAME,
65 _NET_WM_DESKTOP,
66 _NET_WM_WINDOW_TYPE,
67 _NET_WM_WINDOW_TYPE_DESKTOP,
68 _NET_WM_WINDOW_TYPE_DOCK,
69 _NET_WM_WINDOW_TYPE_TOOLBAR,
70 _NET_WM_WINDOW_TYPE_MENU,
71 _NET_WM_WINDOW_TYPE_UTILITY,
72 _NET_WM_WINDOW_TYPE_SPLASH,
73 _NET_WM_WINDOW_TYPE_DIALOG,
74 _NET_WM_WINDOW_TYPE_NORMAL,
75 _NET_WM_ICON,
76 _NET_WM_PID,
77 _NET_WM_STATE,
78 _NET_WM_STATE_STICKY,
79 _NET_WM_STATE_SKIP_TASKBAR,
80 _NET_WM_STATE_FULLSCREEN,
81 _NET_WM_STATE_MAXIMIZED_HORZ,
82 _NET_WM_STATE_MAXIMIZED_VERT,
83 _NET_WM_STATE_ABOVE,
84 _NET_WM_STATE_BELOW,
85 _NET_WM_STATE_MODAL,
86 _NET_WM_STATE_HIDDEN,
87 _NET_WM_STATE_DEMANDS_ATTENTION
89 int i;
91 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
92 xscreen->root, _NET_SUPPORTED, ATOM, 32,
93 countof(atom), atom);
95 /* create our own window */
96 father = xcb_generate_id(globalconf.connection);
97 xcb_create_window(globalconf.connection, xscreen->root_depth,
98 father, xscreen->root, -1, -1, 1, 1, 0,
99 XCB_COPY_FROM_PARENT, xscreen->root_visual, 0, NULL);
101 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
102 xscreen->root, _NET_SUPPORTING_WM_CHECK, WINDOW, 32,
103 1, &father);
105 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
106 father, _NET_SUPPORTING_WM_CHECK, WINDOW, 32,
107 1, &father);
109 /* set the window manager name */
110 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
111 father, _NET_WM_NAME, UTF8_STRING, 8, 7, "awesome");
113 /* set the window manager PID */
114 i = getpid();
115 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
116 father, _NET_WM_PID, CARDINAL, 32, 1, &i);
119 void
120 ewmh_update_net_client_list(int phys_screen)
122 xcb_window_t *wins;
123 client_t *c;
124 int n = 0;
126 for(c = globalconf.clients; c; c = c->next)
127 n++;
129 wins = p_alloca(xcb_window_t, n);
131 for(n = 0, c = globalconf.clients; c; c = c->next, n++)
132 if(c->phys_screen == phys_screen)
133 wins[n] = c->win;
135 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
136 xutil_screen_get(globalconf.connection, phys_screen)->root,
137 _NET_CLIENT_LIST, WINDOW, 32, n, wins);
140 /** Set the client list in stacking order, bottom to top.
141 * \param phys_screen The physical screen id.
143 void
144 ewmh_update_net_client_list_stacking(int phys_screen)
146 xcb_window_t *wins;
147 client_node_t *c;
148 int n = 0;
150 for(c = globalconf.stack; c; c = c->next)
151 n++;
153 wins = p_alloca(xcb_window_t, n);
155 for(n = 0, c = *client_node_list_last(&globalconf.stack); c; c = c->prev, n++)
156 if(c->client->phys_screen == phys_screen)
157 wins[n] = c->client->win;
159 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
160 xutil_screen_get(globalconf.connection, phys_screen)->root,
161 _NET_CLIENT_LIST_STACKING, WINDOW, 32, n, wins);
164 void
165 ewmh_update_net_numbers_of_desktop(int phys_screen)
167 uint32_t count = globalconf.screens[phys_screen].tags.len;
169 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
170 xutil_screen_get(globalconf.connection, phys_screen)->root,
171 _NET_NUMBER_OF_DESKTOPS, CARDINAL, 32, 1, &count);
174 void
175 ewmh_update_net_current_desktop(int phys_screen)
177 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
178 uint32_t count = 0;
179 tag_t **curtags = tags_get_current(phys_screen);
181 while(count < (uint32_t) tags->len && tags->tab[count] != curtags[0])
182 count++;
184 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
185 xutil_screen_get(globalconf.connection, phys_screen)->root,
186 _NET_CURRENT_DESKTOP, CARDINAL, 32, 1, &count);
188 p_delete(&curtags);
191 void
192 ewmh_update_net_desktop_names(int phys_screen)
194 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
195 buffer_t buf;
197 buffer_inita(&buf, BUFSIZ);
199 for(int i = 0; i < tags->len; i++)
201 buffer_adds(&buf, tags->tab[i]->name);
202 buffer_addc(&buf, '\0');
205 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
206 xutil_screen_get(globalconf.connection, phys_screen)->root,
207 _NET_DESKTOP_NAMES, UTF8_STRING, 8, buf.len, buf.s);
208 buffer_wipe(&buf);
211 /** Update the work area space for each physical screen and each desktop.
212 * \param phys_screen The physical screen id.
214 void
215 ewmh_update_workarea(int phys_screen)
217 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
218 uint32_t *area = p_alloca(uint32_t, tags->len * 4);
219 area_t geom = screen_area_get(phys_screen,
220 &globalconf.screens[phys_screen].wiboxes,
221 &globalconf.screens[phys_screen].padding,
222 true);
225 for(int i = 0; i < tags->len; i++)
227 area[4 * i + 0] = geom.x;
228 area[4 * i + 1] = geom.y;
229 area[4 * i + 2] = geom.width;
230 area[4 * i + 3] = geom.height;
233 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
234 xutil_screen_get(globalconf.connection, phys_screen)->root,
235 _NET_WORKAREA, CARDINAL, 32, tags->len * 4, area);
238 void
239 ewmh_update_net_active_window(int phys_screen)
241 xcb_window_t win;
243 if(globalconf.screen_focus->client_focus
244 && globalconf.screen_focus->client_focus->phys_screen == phys_screen)
245 win = globalconf.screen_focus->client_focus->win;
246 else
247 win = XCB_NONE;
249 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
250 xutil_screen_get(globalconf.connection, phys_screen)->root,
251 _NET_ACTIVE_WINDOW, WINDOW, 32, 1, &win);
254 static void
255 ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set)
257 if(state == _NET_WM_STATE_STICKY)
259 if(set == _NET_WM_STATE_REMOVE)
260 client_setsticky(c, false);
261 else if(set == _NET_WM_STATE_ADD)
262 client_setsticky(c, true);
263 else if(set == _NET_WM_STATE_TOGGLE)
264 client_setsticky(c, !c->issticky);
266 else if(state == _NET_WM_STATE_SKIP_TASKBAR)
268 if(set == _NET_WM_STATE_REMOVE)
270 c->skiptb = false;
271 ewmh_client_update_hints(c);
273 else if(set == _NET_WM_STATE_ADD)
275 c->skiptb = true;
276 ewmh_client_update_hints(c);
278 else if(set == _NET_WM_STATE_TOGGLE)
280 c->skiptb = !c->skiptb;
281 ewmh_client_update_hints(c);
284 else if(state == _NET_WM_STATE_FULLSCREEN)
286 if(set == _NET_WM_STATE_REMOVE)
287 client_setfullscreen(c, false);
288 else if(set == _NET_WM_STATE_ADD)
289 client_setfullscreen(c, true);
290 else if(set == _NET_WM_STATE_TOGGLE)
291 client_setfullscreen(c, !c->isfullscreen);
293 else if(state == _NET_WM_STATE_MAXIMIZED_HORZ)
295 if(set == _NET_WM_STATE_REMOVE)
296 client_setmaxhoriz(c, false);
297 else if(set == _NET_WM_STATE_ADD)
298 client_setmaxhoriz(c, true);
299 else if(set == _NET_WM_STATE_TOGGLE)
300 client_setmaxhoriz(c, !c->ismaxhoriz);
302 else if(state == _NET_WM_STATE_MAXIMIZED_VERT)
304 if(set == _NET_WM_STATE_REMOVE)
305 client_setmaxvert(c, false);
306 else if(set == _NET_WM_STATE_ADD)
307 client_setmaxvert(c, true);
308 else if(set == _NET_WM_STATE_TOGGLE)
309 client_setmaxvert(c, !c->ismaxvert);
311 else if(state == _NET_WM_STATE_ABOVE)
313 if(set == _NET_WM_STATE_REMOVE)
314 client_setabove(c, false);
315 else if(set == _NET_WM_STATE_ADD)
316 client_setabove(c, true);
317 else if(set == _NET_WM_STATE_TOGGLE)
318 client_setabove(c, !c->isabove);
320 else if(state == _NET_WM_STATE_BELOW)
322 if(set == _NET_WM_STATE_REMOVE)
323 client_setbelow(c, false);
324 else if(set == _NET_WM_STATE_ADD)
325 client_setbelow(c, true);
326 else if(set == _NET_WM_STATE_TOGGLE)
327 client_setbelow(c, !c->isbelow);
329 else if(state == _NET_WM_STATE_MODAL)
331 if(set == _NET_WM_STATE_REMOVE)
332 client_setmodal(c, false);
333 else if(set == _NET_WM_STATE_ADD)
334 client_setmodal(c, true);
335 else if(set == _NET_WM_STATE_TOGGLE)
336 client_setmodal(c, !c->ismodal);
338 else if(state == _NET_WM_STATE_HIDDEN)
340 if(set == _NET_WM_STATE_REMOVE)
341 client_setminimized(c, false);
342 else if(set == _NET_WM_STATE_ADD)
343 client_setminimized(c, true);
344 else if(set == _NET_WM_STATE_TOGGLE)
345 client_setminimized(c, !c->isminimized);
347 else if(state == _NET_WM_STATE_DEMANDS_ATTENTION)
349 if(set == _NET_WM_STATE_REMOVE)
350 c->isurgent = false;
351 else if(set == _NET_WM_STATE_ADD)
352 c->isurgent = true;
353 else if(set == _NET_WM_STATE_TOGGLE)
354 c->isurgent = !c->isurgent;
356 /* execute hook */
357 hooks_property(c, "urgent");
362 ewmh_process_client_message(xcb_client_message_event_t *ev)
364 client_t *c;
365 int screen;
367 if(ev->type == _NET_CURRENT_DESKTOP)
368 for(screen = 0;
369 screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
370 screen++)
372 if(ev->window == xutil_screen_get(globalconf.connection, screen)->root)
373 tag_view_only_byindex(screen, ev->data.data32[0]);
375 else if(ev->type == _NET_CLOSE_WINDOW)
377 if((c = client_getbywin(ev->window)))
378 client_kill(c);
380 else if(ev->type == _NET_WM_DESKTOP)
382 if((c = client_getbywin(ev->window)))
384 tag_array_t *tags = &globalconf.screens[c->screen].tags;
386 if(ev->data.data32[0] == 0xffffffff)
387 c->issticky = true;
388 else
389 for(int i = 0; i < tags->len; i++)
390 if((int)ev->data.data32[0] == i)
391 tag_client(c, tags->tab[i]);
392 else
393 untag_client(c, tags->tab[i]);
396 else if(ev->type == _NET_WM_STATE)
398 if((c = client_getbywin(ev->window)))
400 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[1], ev->data.data32[0]);
401 if(ev->data.data32[2])
402 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[2],
403 ev->data.data32[0]);
406 else if(ev->type == _NET_ACTIVE_WINDOW)
408 if((c = client_getbywin(ev->window)))
409 client_focus(c);
412 return 0;
415 /** Update client EWMH hints.
416 * \param c The client.
418 void
419 ewmh_client_update_hints(client_t *c)
421 xcb_atom_t state[8]; /* number of defined state atoms */
422 int i = 0;
424 if(c->ismodal)
425 state[i++] = _NET_WM_STATE_MODAL;
426 if(c->isfullscreen)
427 state[i++] = _NET_WM_STATE_FULLSCREEN;
428 if(c->ismaxvert)
429 state[i++] = _NET_WM_STATE_MAXIMIZED_VERT;
430 if(c->ismaxhoriz)
431 state[i++] = _NET_WM_STATE_MAXIMIZED_HORZ;
432 if(c->issticky)
433 state[i++] = _NET_WM_STATE_STICKY;
434 if(c->skiptb)
435 state[i++] = _NET_WM_STATE_SKIP_TASKBAR;
436 if(c->isabove)
437 state[i++] = _NET_WM_STATE_ABOVE;
438 if(c->isbelow)
439 state[i++] = _NET_WM_STATE_BELOW;
440 if(c->isminimized)
441 state[i++] = _NET_WM_STATE_HIDDEN;
442 if(c->isurgent)
443 state[i++] = _NET_WM_STATE_DEMANDS_ATTENTION;
445 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
446 c->win, _NET_WM_STATE, ATOM, 32, i, state);
449 /** Update the client active desktop.
450 * This is "wrong" since it can be on several tags, but EWMH has a strict view
451 * of desktop system so just take the first tag.
452 * \param c The client.
454 void
455 ewmh_client_update_desktop(client_t *c)
457 int i;
458 tag_array_t *tags = &globalconf.screens[c->screen].tags;
460 for(i = 0; i < tags->len; i++)
461 if(is_client_tagged(c, tags->tab[i]))
463 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
464 c->win, _NET_WM_DESKTOP, CARDINAL, 32, 1, &i);
465 return;
469 void
470 ewmh_client_check_hints(client_t *c)
472 xcb_atom_t *state;
473 void *data = NULL;
474 int desktop;
475 xcb_get_property_cookie_t c0, c1, c2;
476 xcb_get_property_reply_t *reply;
478 /* Send the GetProperty requests which will be processed later */
479 c0 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
480 _NET_WM_DESKTOP, XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
482 c1 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
483 _NET_WM_STATE, ATOM, 0, UINT32_MAX);
485 c2 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
486 _NET_WM_WINDOW_TYPE, ATOM, 0, UINT32_MAX);
488 reply = xcb_get_property_reply(globalconf.connection, c0, NULL);
489 if(reply && reply->value_len && (data = xcb_get_property_value(reply)))
491 tag_array_t *tags = &globalconf.screens[c->screen].tags;
493 desktop = *(uint32_t *) data;
494 if(desktop == -1)
495 c->issticky = true;
496 else
497 for(int i = 0; i < tags->len; i++)
498 if(desktop == i)
499 tag_client(c, tags->tab[i]);
500 else
501 untag_client(c, tags->tab[i]);
504 p_delete(&reply);
506 reply = xcb_get_property_reply(globalconf.connection, c1, NULL);
507 if(reply && (data = xcb_get_property_value(reply)))
509 state = (xcb_atom_t *) data;
510 for(int i = 0; i < xcb_get_property_value_length(reply); i++)
511 ewmh_process_state_atom(c, state[i], _NET_WM_STATE_ADD);
514 p_delete(&reply);
516 reply = xcb_get_property_reply(globalconf.connection, c2, NULL);
517 if(reply && (data = xcb_get_property_value(reply)))
519 state = (xcb_atom_t *) data;
520 for(int i = 0; i < xcb_get_property_value_length(reply); i++)
521 if(state[i] == _NET_WM_WINDOW_TYPE_DESKTOP)
522 c->type = MAX(c->type, WINDOW_TYPE_DESKTOP);
523 else if(state[i] == _NET_WM_WINDOW_TYPE_DIALOG)
524 c->type = MAX(c->type, WINDOW_TYPE_DIALOG);
525 else if(state[i] == _NET_WM_WINDOW_TYPE_SPLASH)
526 c->type = MAX(c->type, WINDOW_TYPE_SPLASH);
527 else if(state[i] == _NET_WM_WINDOW_TYPE_DOCK)
528 c->type = MAX(c->type, WINDOW_TYPE_DOCK);
529 else if(state[i] == _NET_WM_WINDOW_TYPE_MENU)
530 c->type = MAX(c->type, WINDOW_TYPE_MENU);
531 else if(state[i] == _NET_WM_WINDOW_TYPE_TOOLBAR)
532 c->type = MAX(c->type, WINDOW_TYPE_TOOLBAR);
533 else if(state[i] == _NET_WM_WINDOW_TYPE_UTILITY)
534 c->type = MAX(c->type, WINDOW_TYPE_UTILITY);
537 p_delete(&reply);
540 /** Update the WM strut of a client.
541 * \param c The client.
543 void
544 ewmh_client_strut_update(client_t *c, xcb_get_property_reply_t *strut_r)
546 void *data;
547 xcb_get_property_reply_t *mstrut_r = NULL;
549 if(!strut_r)
551 xcb_get_property_cookie_t strut_q = xcb_get_property_unchecked(globalconf.connection, false, c->win,
552 _NET_WM_STRUT_PARTIAL, CARDINAL, 0, 12);
553 strut_r = mstrut_r = xcb_get_property_reply(globalconf.connection, strut_q, NULL);
556 if(strut_r
557 && strut_r->value_len
558 && (data = xcb_get_property_value(strut_r)))
560 uint32_t *strut = data;
562 if(c->strut.left != strut[0]
563 || c->strut.right != strut[1]
564 || c->strut.top != strut[2]
565 || c->strut.bottom != strut[3]
566 || c->strut.left_start_y != strut[4]
567 || c->strut.left_end_y != strut[5]
568 || c->strut.right_start_y != strut[6]
569 || c->strut.right_end_y != strut[7]
570 || c->strut.top_start_x != strut[8]
571 || c->strut.top_end_x != strut[9]
572 || c->strut.bottom_start_x != strut[10]
573 || c->strut.bottom_end_x != strut[11])
575 c->strut.left = strut[0];
576 c->strut.right = strut[1];
577 c->strut.top = strut[2];
578 c->strut.bottom = strut[3];
579 c->strut.left_start_y = strut[4];
580 c->strut.left_end_y = strut[5];
581 c->strut.right_start_y = strut[6];
582 c->strut.right_end_y = strut[7];
583 c->strut.top_start_x = strut[8];
584 c->strut.top_end_x = strut[9];
585 c->strut.bottom_start_x = strut[10];
586 c->strut.bottom_end_x = strut[11];
588 client_need_arrange(c);
589 /* All the wiboxes (may) need to be repositioned. */
590 wibox_update_positions();
594 p_delete(&mstrut_r);
597 /** Send request to get NET_WM_ICON (EWMH)
598 * \param w The window.
599 * \return The cookie associated with the request.
601 xcb_get_property_cookie_t
602 ewmh_window_icon_get_unchecked(xcb_window_t w)
604 return xcb_get_property_unchecked(globalconf.connection, false, w,
605 _NET_WM_ICON, CARDINAL, 0, UINT32_MAX);
608 image_t *
609 ewmh_window_icon_from_reply(xcb_get_property_reply_t *r)
611 uint32_t *data;
613 if(!r || r->type != CARDINAL || r->format != 32 || r->length < 2 ||
614 !(data = (uint32_t *) xcb_get_property_value(r)))
615 return NULL;
617 if(data[0] && data[1])
618 return image_new_from_argb32(data[0], data[1], data + 2);
620 return NULL;
623 /** Get NET_WM_ICON.
624 * \param cookie The cookie.
625 * \return A draw_image_t structure which must be deleted after usage.
627 image_t *
628 ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie)
630 xcb_get_property_reply_t *r = xcb_get_property_reply(globalconf.connection, cookie, NULL);
631 image_t *icon = ewmh_window_icon_from_reply(r);
632 p_delete(&r);
633 return icon;
636 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80