image: add new image type
[awesome.git] / ewmh.c
blobd6bb4e4e3b181cd49b7a936cd8dc48aa72766f34
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 "statusbar.h"
36 #include "common/atoms.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_NORMAL,
68 _NET_WM_WINDOW_TYPE_DESKTOP,
69 _NET_WM_WINDOW_TYPE_DOCK,
70 _NET_WM_WINDOW_TYPE_SPLASH,
71 _NET_WM_WINDOW_TYPE_DIALOG,
72 _NET_WM_ICON,
73 _NET_WM_PID,
74 _NET_WM_STATE,
75 _NET_WM_STATE_STICKY,
76 _NET_WM_STATE_SKIP_TASKBAR,
77 _NET_WM_STATE_FULLSCREEN,
78 _NET_WM_STATE_ABOVE,
79 _NET_WM_STATE_BELOW,
80 _NET_WM_STATE_MODAL,
81 _NET_WM_STATE_HIDDEN,
82 _NET_WM_STATE_DEMANDS_ATTENTION
84 int i;
86 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
87 xscreen->root, _NET_SUPPORTED, ATOM, 32,
88 countof(atom), atom);
90 /* create our own window */
91 father = xcb_generate_id(globalconf.connection);
92 xcb_create_window(globalconf.connection, xscreen->root_depth,
93 father, xscreen->root, -1, -1, 1, 1, 0,
94 XCB_COPY_FROM_PARENT, xscreen->root_visual, 0, NULL);
96 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
97 xscreen->root, _NET_SUPPORTING_WM_CHECK, WINDOW, 32,
98 1, &father);
100 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
101 father, _NET_SUPPORTING_WM_CHECK, WINDOW, 32,
102 1, &father);
104 /* set the window manager name */
105 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
106 father, _NET_WM_NAME, UTF8_STRING, 8, 7, "awesome");
108 /* set the window manager PID */
109 i = getpid();
110 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
111 father, _NET_WM_PID, CARDINAL, 32, 1, &i);
114 void
115 ewmh_update_net_client_list(int phys_screen)
117 xcb_window_t *wins;
118 client_t *c;
119 int n = 0;
121 for(c = globalconf.clients; c; c = c->next)
122 n++;
124 wins = p_alloca(xcb_window_t, n);
126 for(n = 0, c = globalconf.clients; c; c = c->next, n++)
127 if(c->phys_screen == phys_screen)
128 wins[n] = c->win;
130 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
131 xutil_screen_get(globalconf.connection, phys_screen)->root,
132 _NET_CLIENT_LIST, WINDOW, 32, n, 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_alloca(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);
159 void
160 ewmh_update_net_numbers_of_desktop(int phys_screen)
162 uint32_t count = globalconf.screens[phys_screen].tags.len;
164 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
165 xutil_screen_get(globalconf.connection, phys_screen)->root,
166 _NET_NUMBER_OF_DESKTOPS, CARDINAL, 32, 1, &count);
169 void
170 ewmh_update_net_current_desktop(int phys_screen)
172 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
173 uint32_t count = 0;
174 tag_t **curtags = tags_get_current(phys_screen);
176 while(count < (uint32_t) tags->len && tags->tab[count] != curtags[0])
177 count++;
179 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
180 xutil_screen_get(globalconf.connection, phys_screen)->root,
181 _NET_CURRENT_DESKTOP, CARDINAL, 32, 1, &count);
183 p_delete(&curtags);
186 void
187 ewmh_update_net_desktop_names(int phys_screen)
189 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
190 buffer_t buf;
192 buffer_inita(&buf, BUFSIZ);
194 for(int i = 0; i < tags->len; i++)
196 buffer_adds(&buf, tags->tab[i]->name);
197 buffer_addc(&buf, '\0');
200 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
201 xutil_screen_get(globalconf.connection, phys_screen)->root,
202 _NET_DESKTOP_NAMES, UTF8_STRING, 8, buf.len, buf.s);
203 buffer_wipe(&buf);
206 /** Update the work area space for each physical screen and each desktop.
207 * \param phys_screen The physical screen id.
209 void
210 ewmh_update_workarea(int phys_screen)
212 tag_array_t *tags = &globalconf.screens[phys_screen].tags;
213 uint32_t *area = p_alloca(uint32_t, tags->len * 4);
214 area_t geom = screen_area_get(phys_screen,
215 globalconf.screens[phys_screen].statusbar,
216 &globalconf.screens[phys_screen].padding,
217 true);
220 for(int i = 0; i < tags->len; i++)
222 area[4 * i + 0] = geom.x;
223 area[4 * i + 1] = geom.y;
224 area[4 * i + 2] = geom.width;
225 area[4 * i + 3] = geom.height;
228 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
229 xutil_screen_get(globalconf.connection, phys_screen)->root,
230 _NET_WORKAREA, CARDINAL, 32, tags->len * 4, area);
233 void
234 ewmh_update_net_active_window(int phys_screen)
236 xcb_window_t win;
238 if(globalconf.screen_focus->client_focus
239 && globalconf.screen_focus->client_focus->phys_screen == phys_screen)
240 win = globalconf.screen_focus->client_focus->win;
241 else
242 win = XCB_NONE;
244 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
245 xutil_screen_get(globalconf.connection, phys_screen)->root,
246 _NET_ACTIVE_WINDOW, WINDOW, 32, 1, &win);
249 static void
250 ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set)
252 if(state == _NET_WM_STATE_STICKY)
254 if(set == _NET_WM_STATE_REMOVE)
255 client_setsticky(c, false);
256 else if(set == _NET_WM_STATE_ADD)
257 client_setsticky(c, true);
259 else if(state == _NET_WM_STATE_SKIP_TASKBAR)
261 if(set == _NET_WM_STATE_REMOVE)
262 c->skiptb = false;
263 else if(set == _NET_WM_STATE_ADD)
264 c->skiptb = true;
266 else if(state == _NET_WM_STATE_FULLSCREEN)
268 if(set == _NET_WM_STATE_REMOVE)
269 client_setfullscreen(c, false);
270 else if(set == _NET_WM_STATE_ADD)
271 client_setfullscreen(c, true);
273 else if(state == _NET_WM_STATE_ABOVE)
275 if(set == _NET_WM_STATE_REMOVE)
276 client_setabove(c, false);
277 else if(set == _NET_WM_STATE_ADD)
278 client_setabove(c, true);
280 else if(state == _NET_WM_STATE_BELOW)
282 if(set == _NET_WM_STATE_REMOVE)
283 client_setbelow(c, false);
284 else if(set == _NET_WM_STATE_ADD)
285 client_setbelow(c, true);
287 else if(state == _NET_WM_STATE_MODAL)
289 if(set == _NET_WM_STATE_REMOVE)
290 client_setmodal(c, false);
291 else if(set == _NET_WM_STATE_ADD)
292 client_setmodal(c, true);
294 else if(state == _NET_WM_STATE_HIDDEN)
296 if(set == _NET_WM_STATE_REMOVE)
298 client_need_arrange(c);
299 c->isminimized = false;
300 client_need_arrange(c);
302 else if(set == _NET_WM_STATE_ADD)
304 client_need_arrange(c);
305 c->isminimized = true;
306 client_need_arrange(c);
309 else if(state == _NET_WM_STATE_DEMANDS_ATTENTION)
311 if(set == _NET_WM_STATE_REMOVE)
312 c->isurgent = false;
313 else if(set == _NET_WM_STATE_ADD)
314 c->isurgent = true;
316 /* execute hook */
317 luaA_client_userdata_new(globalconf.L, c);
318 luaA_dofunction(globalconf.L, globalconf.hooks.urgent, 1, 0);
319 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
324 ewmh_process_client_message(xcb_client_message_event_t *ev)
326 client_t *c;
327 int screen;
329 if(ev->type == _NET_CURRENT_DESKTOP)
330 for(screen = 0;
331 screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
332 screen++)
334 if(ev->window == xutil_screen_get(globalconf.connection, screen)->root)
335 tag_view_only_byindex(screen, ev->data.data32[0]);
337 else if(ev->type == _NET_CLOSE_WINDOW)
339 if((c = client_getbywin(ev->window)))
340 client_kill(c);
342 else if(ev->type == _NET_WM_DESKTOP)
344 if((c = client_getbywin(ev->window)))
346 tag_array_t *tags = &globalconf.screens[c->screen].tags;
348 if(ev->data.data32[0] == 0xffffffff)
349 c->issticky = true;
350 else
351 for(int i = 0; i < tags->len; i++)
352 if((int)ev->data.data32[0] == i)
353 tag_client(c, tags->tab[i]);
354 else
355 untag_client(c, tags->tab[i]);
358 else if(ev->type == _NET_WM_STATE)
360 if((c = client_getbywin(ev->window)))
362 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[1], ev->data.data32[0]);
363 if(ev->data.data32[2])
364 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[2],
365 ev->data.data32[0]);
369 return 0;
372 void
373 ewmh_check_client_hints(client_t *c)
375 xcb_atom_t *state;
376 void *data = NULL;
377 int desktop;
378 xcb_get_property_cookie_t c0, c1, c2;
379 xcb_get_property_reply_t *reply;
381 /* Send the GetProperty requests which will be processed later */
382 c0 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
383 _NET_WM_DESKTOP, XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
385 c1 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
386 _NET_WM_STATE, ATOM, 0, UINT32_MAX);
388 c2 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
389 _NET_WM_WINDOW_TYPE, ATOM, 0, UINT32_MAX);
391 reply = xcb_get_property_reply(globalconf.connection, c0, NULL);
392 if(reply && reply->value_len && (data = xcb_get_property_value(reply)))
394 tag_array_t *tags = &globalconf.screens[c->screen].tags;
396 desktop = *(uint32_t *) data;
397 if(desktop == -1)
398 c->issticky = true;
399 else
400 for(int i = 0; i < tags->len; i++)
401 if(desktop == i)
402 tag_client(c, tags->tab[i]);
403 else
404 untag_client(c, tags->tab[i]);
407 p_delete(&reply);
409 reply = xcb_get_property_reply(globalconf.connection, c1, NULL);
410 if(reply && (data = xcb_get_property_value(reply)))
412 state = (xcb_atom_t *) data;
413 for(int i = 0; i < xcb_get_property_value_length(reply); i++)
414 ewmh_process_state_atom(c, state[i], _NET_WM_STATE_ADD);
417 p_delete(&reply);
419 reply = xcb_get_property_reply(globalconf.connection, c2, NULL);
420 if(reply && (data = xcb_get_property_value(reply)))
422 state = (xcb_atom_t *) data;
423 for(int i = 0; i < xcb_get_property_value_length(reply); i++)
424 if(state[i] == _NET_WM_WINDOW_TYPE_DESKTOP)
425 c->type = WINDOW_TYPE_DESKTOP;
426 else if(state[i] == _NET_WM_WINDOW_TYPE_DIALOG)
427 c->type = WINDOW_TYPE_DIALOG;
428 else if(state[i] == _NET_WM_WINDOW_TYPE_SPLASH)
429 c->type = WINDOW_TYPE_SPLASH;
430 else if(state[i] == _NET_WM_WINDOW_TYPE_DOCK)
431 c->type = WINDOW_TYPE_DOCK;
432 else
433 c->type = WINDOW_TYPE_NORMAL;
436 p_delete(&reply);
439 /** Update the WM strut of a client.
440 * \param c The client.
442 void
443 ewmh_client_strut_update(client_t *c)
445 void *data;
446 xcb_get_property_cookie_t strut_q;
447 xcb_get_property_reply_t *strut_r;
449 strut_q = xcb_get_property_unchecked(globalconf.connection, false, c->win,
450 _NET_WM_STRUT_PARTIAL, CARDINAL, 0, 12);
452 strut_r = xcb_get_property_reply(globalconf.connection, strut_q, NULL);
454 if(strut_r
455 && strut_r->value_len
456 && (data = xcb_get_property_value(strut_r)))
458 uint32_t *strut = data;
460 if(c->strut.left != strut[0]
461 || c->strut.right != strut[1]
462 || c->strut.top != strut[2]
463 || c->strut.bottom != strut[3]
464 || c->strut.left_start_y != strut[4]
465 || c->strut.left_end_y != strut[5]
466 || c->strut.right_start_y != strut[6]
467 || c->strut.right_end_y != strut[7]
468 || c->strut.top_start_x != strut[8]
469 || c->strut.top_end_x != strut[9]
470 || c->strut.bottom_start_x != strut[10]
471 || c->strut.bottom_end_x != strut[11])
473 c->strut.left = strut[0];
474 c->strut.right = strut[1];
475 c->strut.top = strut[2];
476 c->strut.bottom = strut[3];
477 c->strut.left_start_y = strut[4];
478 c->strut.left_end_y = strut[5];
479 c->strut.right_start_y = strut[6];
480 c->strut.right_end_y = strut[7];
481 c->strut.top_start_x = strut[8];
482 c->strut.top_end_x = strut[9];
483 c->strut.bottom_start_x = strut[10];
484 c->strut.bottom_end_x = strut[11];
486 client_need_arrange(c);
487 /* All the statusbars (may) need to be repositioned */
488 for(int screen = 0; screen < globalconf.screens_info->nscreen; screen++)
489 for(statusbar_t *s = globalconf.screens[screen].statusbar; s; s = s->next)
490 statusbar_position_update(s);
493 p_delete(&strut_r);
497 /** Send request to get NET_WM_ICON (EWMH)
498 * \param w The window.
499 * \return The cookie associated with the request.
501 xcb_get_property_cookie_t
502 ewmh_window_icon_get_unchecked(xcb_window_t w)
504 return xcb_get_property_unchecked(globalconf.connection, false, w,
505 _NET_WM_ICON, CARDINAL, 0, UINT32_MAX);
508 /** Get NET_WM_ICON.
509 * \param cookie The cookie.
510 * \return A netwm_icon_t structure which must be deleted after usage.
512 netwm_icon_t *
513 ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie)
515 double alpha;
516 netwm_icon_t *icon;
517 int size, i;
518 uint32_t *data;
519 unsigned char *imgdata;
520 xcb_get_property_reply_t *r;
522 r = xcb_get_property_reply(globalconf.connection, cookie, NULL);
523 if(!r || r->type != CARDINAL || r->format != 32 || r->length < 2 ||
524 !(data = (uint32_t *) xcb_get_property_value(r)))
526 p_delete(&r);
527 return NULL;
530 icon = p_new(netwm_icon_t, 1);
532 icon->width = data[0];
533 icon->height = data[1];
534 size = icon->width * icon->height;
536 if(!size)
538 p_delete(&icon);
539 p_delete(&r);
540 return NULL;
543 icon->image = p_new(unsigned char, size * 4);
544 for(imgdata = icon->image, i = 2; i < size + 2; i++, imgdata += 4)
546 imgdata[3] = (data[i] >> 24) & 0xff; /* A */
547 alpha = imgdata[3] / 255.0;
548 imgdata[2] = ((data[i] >> 16) & 0xff) * alpha; /* R */
549 imgdata[1] = ((data[i] >> 8) & 0xff) * alpha; /* G */
550 imgdata[0] = (data[i] & 0xff) * alpha; /* B */
553 p_delete(&r);
555 return icon;
558 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80