build: man path is now configurable
[awesome.git] / ewmh.c
blobe9952855b72a4e6e06cb2edf93125f2bf211a6f8
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 "titlebar.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_ICON_NAME,
62 _NET_WM_VISIBLE_ICON_NAME,
63 _NET_WM_DESKTOP,
64 _NET_WM_WINDOW_TYPE,
65 _NET_WM_WINDOW_TYPE_NORMAL,
66 _NET_WM_WINDOW_TYPE_DESKTOP,
67 _NET_WM_WINDOW_TYPE_DOCK,
68 _NET_WM_WINDOW_TYPE_SPLASH,
69 _NET_WM_WINDOW_TYPE_DIALOG,
70 _NET_WM_ICON,
71 _NET_WM_PID,
72 _NET_WM_STATE,
73 _NET_WM_STATE_STICKY,
74 _NET_WM_STATE_SKIP_TASKBAR,
75 _NET_WM_STATE_FULLSCREEN,
76 _NET_WM_STATE_ABOVE,
77 _NET_WM_STATE_BELOW,
78 _NET_WM_STATE_MODAL,
79 _NET_WM_STATE_HIDDEN,
80 _NET_WM_STATE_DEMANDS_ATTENTION
82 int i;
84 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
85 xscreen->root, _NET_SUPPORTED, ATOM, 32,
86 countof(atom), atom);
88 /* create our own window */
89 father = xcb_generate_id(globalconf.connection);
90 xcb_create_window(globalconf.connection, xscreen->root_depth,
91 father, xscreen->root, -1, -1, 1, 1, 0,
92 XCB_COPY_FROM_PARENT, xscreen->root_visual, 0, NULL);
94 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
95 xscreen->root, _NET_SUPPORTING_WM_CHECK, WINDOW, 32,
96 1, &father);
98 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
99 father, _NET_SUPPORTING_WM_CHECK, WINDOW, 32,
100 1, &father);
102 /* set the window manager name */
103 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
104 father, _NET_WM_NAME, UTF8_STRING, 8, 7, "awesome");
106 /* set the window manager PID */
107 i = getpid();
108 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
109 father, _NET_WM_PID, CARDINAL, 32, 1, &i);
112 void
113 ewmh_update_net_client_list(int phys_screen)
115 xcb_window_t *wins;
116 client_t *c;
117 int n = 0;
119 for(c = globalconf.clients; c; c = c->next)
120 n++;
122 wins = p_new(xcb_window_t, n);
124 for(n = 0, c = globalconf.clients; c; c = c->next, n++)
125 if(c->phys_screen == phys_screen)
126 wins[n] = c->win;
128 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
129 xutil_screen_get(globalconf.connection, phys_screen)->root,
130 _NET_CLIENT_LIST, WINDOW, 32, n, wins);
132 p_delete(&wins);
135 /** Set the client list in stacking order, bottom to top.
136 * \param phys_screen The physical screen id.
138 void
139 ewmh_update_net_client_list_stacking(int phys_screen)
141 xcb_window_t *wins;
142 client_node_t *c;
143 int n = 0;
145 for(c = globalconf.stack; c; c = c->next)
146 n++;
148 wins = p_new(xcb_window_t, n);
150 for(n = 0, c = *client_node_list_last(&globalconf.stack); c; c = c->prev, n++)
151 if(c->client->phys_screen == phys_screen)
152 wins[n] = c->client->win;
154 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
155 xutil_screen_get(globalconf.connection, phys_screen)->root,
156 _NET_CLIENT_LIST_STACKING, WINDOW, 32, n, wins);
158 p_delete(&wins);
161 void
162 ewmh_update_net_numbers_of_desktop(int phys_screen)
164 uint32_t count = globalconf.screens[phys_screen].tags.len;
166 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
167 xutil_screen_get(globalconf.connection, phys_screen)->root,
168 _NET_NUMBER_OF_DESKTOPS, CARDINAL, 32, 1, &count);
171 void
172 ewmh_update_net_current_desktop(int phys_screen)
174 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
175 uint32_t count = 0;
176 tag_t **curtags = tags_get_current(phys_screen);
178 while(count < (uint32_t) tags->len && tags->tab[count] != curtags[0])
179 count++;
181 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
182 xutil_screen_get(globalconf.connection, phys_screen)->root,
183 _NET_CURRENT_DESKTOP, CARDINAL, 32, 1, &count);
185 p_delete(&curtags);
188 void
189 ewmh_update_net_desktop_names(int phys_screen)
191 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
192 buffer_t buf;
194 buffer_inita(&buf, BUFSIZ);
196 for(int i = 0; i < tags->len; i++)
198 buffer_adds(&buf, tags->tab[i]->name);
199 buffer_addc(&buf, '\0');
202 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
203 xutil_screen_get(globalconf.connection, phys_screen)->root,
204 _NET_DESKTOP_NAMES, UTF8_STRING, 8, buf.len, buf.s);
205 buffer_wipe(&buf);
208 /** Update the work area space for each physical screen and each desktop.
209 * \param phys_screen The physical screen id.
211 void
212 ewmh_update_workarea(int phys_screen)
214 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
215 uint32_t *area = p_alloca(uint32_t, tags->len * 4);
216 area_t geom = screen_area_get(&globalconf.screens[phys_screen].geometry,
217 globalconf.screens[phys_screen].statusbar,
218 &globalconf.screens[phys_screen].padding);
221 for(int i = 0; i < tags->len; i++)
223 area[4 * i + 0] = geom.x;
224 area[4 * i + 1] = geom.y;
225 area[4 * i + 2] = geom.width;
226 area[4 * i + 3] = geom.height;
229 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
230 xutil_screen_get(globalconf.connection, phys_screen)->root,
231 _NET_WORKAREA, CARDINAL, 32, tags->len * 4, area);
234 void
235 ewmh_update_net_active_window(int phys_screen)
237 xcb_window_t win;
239 if(globalconf.screen_focus->client_focus
240 && globalconf.screen_focus->client_focus->phys_screen == phys_screen)
241 win = globalconf.screen_focus->client_focus->win;
242 else
243 win = XCB_NONE;
245 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
246 xutil_screen_get(globalconf.connection, phys_screen)->root,
247 _NET_ACTIVE_WINDOW, WINDOW, 32, 1, &win);
250 static void
251 ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set)
253 if(state == _NET_WM_STATE_STICKY)
255 tag_array_t *tags = &globalconf.screens[c->screen].tags;
256 for(int i = 0; i < tags->len; i++)
257 tag_client(c, tags->tab[i]);
259 else if(state == _NET_WM_STATE_SKIP_TASKBAR)
261 if(set == _NET_WM_STATE_REMOVE)
263 c->skiptb = false;
264 c->skip = false;
266 else if(set == _NET_WM_STATE_ADD)
268 c->skiptb = true;
269 c->skip = true;
272 else if(state == _NET_WM_STATE_FULLSCREEN)
274 area_t geometry = c->geometry;
275 if(set == _NET_WM_STATE_REMOVE)
277 /* restore geometry */
278 geometry = c->m_geometry;
279 /* restore borders and titlebar */
280 if(c->titlebar && c->titlebar->sw && (c->titlebar->position = c->titlebar->oldposition))
281 xcb_map_window(globalconf.connection, c->titlebar->sw->window);
282 c->noborder = false;
283 c->ismax = false;
284 client_setlayer(c, c->oldlayer);
285 client_setborder(c, c->oldborder);
286 client_setfloating(c, c->wasfloating);
288 else if(set == _NET_WM_STATE_ADD)
290 geometry = screen_area_get(&globalconf.screens[c->screen].geometry,
291 NULL,
292 &globalconf.screens[c->screen].padding);
293 /* save geometry */
294 c->m_geometry = c->geometry;
295 c->wasfloating = c->isfloating;
296 /* disable titlebar and borders */
297 if(c->titlebar && c->titlebar->sw && (c->titlebar->oldposition = c->titlebar->position))
299 xcb_unmap_window(globalconf.connection, c->titlebar->sw->window);
300 c->titlebar->position = Off;
302 c->ismax = true;
303 c->oldborder = c->border;
304 client_setborder(c, 0);
305 c->noborder = true;
306 c->oldlayer = c->layer;
307 client_setlayer(c, LAYER_FULLSCREEN);
308 client_setfloating(c, true);
310 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
311 client_resize(c, geometry, false);
313 else if(state == _NET_WM_STATE_ABOVE)
315 if(set == _NET_WM_STATE_REMOVE)
316 client_setlayer(c, c->oldlayer);
317 else if(set == _NET_WM_STATE_ADD)
319 c->oldlayer = c->layer;
320 client_setlayer(c, LAYER_ABOVE);
323 else if(state == _NET_WM_STATE_BELOW)
325 if(set == _NET_WM_STATE_REMOVE)
326 client_setlayer(c, c->oldlayer);
327 else if(set == _NET_WM_STATE_ADD)
329 c->oldlayer = c->layer;
330 client_setlayer(c, LAYER_BELOW);
333 else if(state == _NET_WM_STATE_MODAL)
335 if(set == _NET_WM_STATE_REMOVE)
336 client_setlayer(c, c->oldlayer);
337 else if(set == _NET_WM_STATE_ADD)
339 c->oldlayer = c->layer;
340 client_setlayer(c, LAYER_MODAL);
343 else if(state == _NET_WM_STATE_HIDDEN)
345 if(set == _NET_WM_STATE_REMOVE)
346 c->ishidden = false;
347 else if(set == _NET_WM_STATE_ADD)
348 c->ishidden = true;
349 globalconf.screens[c->screen].need_arrange = true;
351 else if(state == _NET_WM_STATE_DEMANDS_ATTENTION)
353 if(set == _NET_WM_STATE_REMOVE)
354 c->isurgent = false;
355 else if(set == _NET_WM_STATE_ADD)
357 c->isurgent = true;
358 /* execute hook */
359 luaA_client_userdata_new(globalconf.L, c);
360 luaA_dofunction(globalconf.L, globalconf.hooks.urgent, 1, 0);
361 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
366 static void
367 ewmh_process_window_type_atom(client_t *c, xcb_atom_t state)
369 if(state == _NET_WM_WINDOW_TYPE_NORMAL)
371 /* do nothing. this is REALLY IMPORTANT */
373 else if(state == _NET_WM_WINDOW_TYPE_DOCK
374 || state == _NET_WM_WINDOW_TYPE_SPLASH)
376 c->skip = true;
377 c->isfixed = true;
378 if(c->titlebar && c->titlebar->position && c->titlebar->sw)
380 xcb_unmap_window(globalconf.connection, c->titlebar->sw->window);
381 c->titlebar->position = Off;
383 client_setborder(c, 0);
384 c->noborder = true;
385 client_setlayer(c, LAYER_ABOVE);
386 client_setfloating(c, true);
388 else if(state == _NET_WM_WINDOW_TYPE_DIALOG)
390 client_setlayer(c, LAYER_MODAL);
391 client_setfloating(c, true);
393 else if(state == _NET_WM_WINDOW_TYPE_DESKTOP)
395 tag_array_t *tags = &globalconf.screens[c->screen].tags;
396 c->noborder = true;
397 c->isfixed = true;
398 c->skip = true;
399 client_setlayer(c, LAYER_DESKTOP);
400 for(int i = 0; i < tags->len; i++)
401 tag_client(c, tags->tab[i]);
406 ewmh_process_client_message(xcb_client_message_event_t *ev)
408 client_t *c;
409 int screen;
411 if(ev->type == _NET_CURRENT_DESKTOP)
412 for(screen = 0;
413 screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
414 screen++)
416 if(ev->window == xutil_screen_get(globalconf.connection, screen)->root)
417 tag_view_only_byindex(screen, ev->data.data32[0]);
419 else if(ev->type == _NET_CLOSE_WINDOW)
421 if((c = client_getbywin(ev->window)))
422 client_kill(c);
424 else if(ev->type == _NET_WM_DESKTOP)
426 if((c = client_getbywin(ev->window)))
428 tag_array_t *tags = &globalconf.screens[c->screen].tags;
430 if(ev->data.data32[0] == 0xffffffff)
431 for(int i = 0; i < tags->len; i++)
432 tag_client(c, tags->tab[i]);
433 else
434 for(int i = 0; i < tags->len; i++)
435 if((int)ev->data.data32[0] == i)
436 tag_client(c, tags->tab[i]);
437 else
438 untag_client(c, tags->tab[i]);
441 else if(ev->type == _NET_WM_STATE)
443 if((c = client_getbywin(ev->window)))
445 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[1], ev->data.data32[0]);
446 if(ev->data.data32[2])
447 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[2],
448 ev->data.data32[0]);
452 return 0;
455 void
456 ewmh_check_client_hints(client_t *c)
458 xcb_atom_t *state;
459 void *data = NULL;
460 int desktop;
461 xcb_get_property_cookie_t c0, c1, c2;
462 xcb_get_property_reply_t *reply;
464 /* Send the GetProperty requests which will be processed later */
465 c0 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
466 _NET_WM_DESKTOP, XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
468 c1 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
469 _NET_WM_STATE, ATOM, 0, UINT32_MAX);
471 c2 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
472 _NET_WM_WINDOW_TYPE, ATOM, 0, UINT32_MAX);
474 reply = xcb_get_property_reply(globalconf.connection, c0, NULL);
475 if(reply && reply->value_len && (data = xcb_get_property_value(reply)))
477 tag_array_t *tags = &globalconf.screens[c->screen].tags;
479 desktop = *(uint32_t *) data;
480 if(desktop == -1)
481 for(int i = 0; i < tags->len; i++)
482 tag_client(c, tags->tab[i]);
483 else
484 for(int i = 0; i < tags->len; i++)
485 if(desktop == i)
486 tag_client(c, tags->tab[i]);
487 else
488 untag_client(c, tags->tab[i]);
491 p_delete(&reply);
493 reply = xcb_get_property_reply(globalconf.connection, c1, NULL);
494 if(reply && (data = xcb_get_property_value(reply)))
496 state = (xcb_atom_t *) data;
497 for(int i = 0; i < xcb_get_property_value_length(reply); i++)
498 ewmh_process_state_atom(c, state[i], _NET_WM_STATE_ADD);
501 p_delete(&reply);
503 reply = xcb_get_property_reply(globalconf.connection, c2, NULL);
504 if(reply && (data = xcb_get_property_value(reply)))
506 state = (xcb_atom_t *) data;
507 for(int i = 0; i < xcb_get_property_value_length(reply); i++)
508 ewmh_process_window_type_atom(c, state[i]);
511 p_delete(&reply);
514 /** Get NET_WM_ICON.
515 * \param w The window.
516 * \return A netwm_icon_t structure which must be deleted after usage.
518 netwm_icon_t *
519 ewmh_window_icon_get(xcb_window_t w)
521 double alpha;
522 netwm_icon_t *icon;
523 int size, i;
524 uint32_t *data;
525 unsigned char *imgdata;
526 xcb_get_property_reply_t *r;
528 r = xcb_get_property_reply(globalconf.connection,
529 xcb_get_property_unchecked(globalconf.connection, false, w,
530 _NET_WM_ICON, CARDINAL, 0, UINT32_MAX),
531 NULL);
532 if(!r || r->type != CARDINAL || r->format != 32 || r->length < 2 ||
533 !(data = (uint32_t *) xcb_get_property_value(r)))
535 p_delete(&r);
536 return NULL;
539 icon = p_new(netwm_icon_t, 1);
541 icon->width = data[0];
542 icon->height = data[1];
543 size = icon->width * icon->height;
545 if(!size)
547 p_delete(&icon);
548 p_delete(&r);
549 return NULL;
552 icon->image = p_new(unsigned char, size * 4);
553 for(imgdata = icon->image, i = 2; i < size + 2; i++, imgdata += 4)
555 imgdata[3] = (data[i] >> 24) & 0xff; /* A */
556 alpha = imgdata[3] / 255.0;
557 imgdata[2] = ((data[i] >> 16) & 0xff) * alpha; /* R */
558 imgdata[1] = ((data[i] >> 8) & 0xff) * alpha; /* G */
559 imgdata[0] = (data[i] & 0xff) * alpha; /* B */
562 p_delete(&r);
564 return icon;
568 * Restart awesome.
570 void
571 ewmh_restart(void)
573 client_t *c;
575 for(c = globalconf.clients; c; c = c->next)
576 client_unban(c);
578 xcb_aux_sync(globalconf.connection);
579 xcb_disconnect(globalconf.connection);
581 a_exec(globalconf.argv);
584 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80