[client] Add client_redraw (FS#170)
[awesome.git] / ewmh.c
blob40866fdfcbd268919dca9d232bdc80d4fd89eb2f
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 <xcb/xcb.h>
23 #include <xcb/xcb_atom.h>
24 #include <xcb/xcb_aux.h>
26 #include "ewmh.h"
27 #include "tag.h"
28 #include "focus.h"
29 #include "screen.h"
30 #include "client.h"
31 #include "widget.h"
32 #include "titlebar.h"
34 extern AwesomeConf globalconf;
36 static xcb_atom_t net_supported;
37 static xcb_atom_t net_client_list;
38 static xcb_atom_t net_number_of_desktops;
39 static xcb_atom_t net_current_desktop;
40 static xcb_atom_t net_desktop_names;
41 static xcb_atom_t net_active_window;
42 static xcb_atom_t net_close_window;
43 static xcb_atom_t net_wm_name;
44 static xcb_atom_t net_wm_icon_name;
45 static xcb_atom_t net_wm_window_type;
46 static xcb_atom_t net_wm_window_type_normal;
47 static xcb_atom_t net_wm_window_type_dock;
48 static xcb_atom_t net_wm_window_type_splash;
49 static xcb_atom_t net_wm_window_type_dialog;
50 static xcb_atom_t net_wm_icon;
51 static xcb_atom_t net_wm_state;
52 static xcb_atom_t net_wm_state_sticky;
53 static xcb_atom_t net_wm_state_skip_taskbar;
54 static xcb_atom_t net_wm_state_fullscreen;
55 static xcb_atom_t net_wm_state_above;
56 static xcb_atom_t net_wm_state_below;
58 static xcb_atom_t utf8_string;
60 typedef struct
62 const char *name;
63 xcb_atom_t *atom;
64 } AtomItem;
66 static AtomItem AtomNames[] =
68 { "_NET_SUPPORTED", &net_supported },
69 { "_NET_CLIENT_LIST", &net_client_list },
70 { "_NET_NUMBER_OF_DESKTOPS", &net_number_of_desktops },
71 { "_NET_CURRENT_DESKTOP", &net_current_desktop },
72 { "_NET_DESKTOP_NAMES", &net_desktop_names },
73 { "_NET_ACTIVE_WINDOW", &net_active_window },
75 { "_NET_CLOSE_WINDOW", &net_close_window },
77 { "_NET_WM_NAME", &net_wm_name },
78 { "_NET_WM_ICON_NAME", &net_wm_icon_name },
79 { "_NET_WM_WINDOW_TYPE", &net_wm_window_type },
80 { "_NET_WM_WINDOW_TYPE_NORMAL", &net_wm_window_type_normal },
81 { "_NET_WM_WINDOW_TYPE_DOCK", &net_wm_window_type_dock },
82 { "_NET_WM_WINDOW_TYPE_SPLASH", &net_wm_window_type_splash },
83 { "_NET_WM_WINDOW_TYPE_DIALOG", &net_wm_window_type_dialog },
84 { "_NET_WM_ICON", &net_wm_icon },
85 { "_NET_WM_STATE", &net_wm_state },
86 { "_NET_WM_STATE_STICKY", &net_wm_state_sticky },
87 { "_NET_WM_STATE_SKIP_TASKBAR", &net_wm_state_skip_taskbar },
88 { "_NET_WM_STATE_FULLSCREEN", &net_wm_state_fullscreen },
89 { "_NET_WM_STATE_ABOVE", &net_wm_state_above },
90 { "_NET_WM_STATE_BELOW", &net_wm_state_below },
92 { "UTF8_STRING", &utf8_string },
95 #define ATOM_NUMBER (sizeof(AtomNames)/sizeof(AtomItem))
97 #define _NET_WM_STATE_REMOVE 0
98 #define _NET_WM_STATE_ADD 1
99 #define _NET_WM_STATE_TOGGLE 2
101 void
102 ewmh_init_atoms(void)
104 unsigned int i;
105 xcb_intern_atom_cookie_t cs[ATOM_NUMBER];
106 xcb_intern_atom_reply_t *r;
109 * Create the atom and get the reply in a XCB way (e.g. send all
110 * the requests at the same time and then get the replies)
112 for(i = 0; i < ATOM_NUMBER; i++)
113 cs[i] = xcb_intern_atom_unchecked(globalconf.connection,
114 false,
115 strlen(AtomNames[i].name),
116 AtomNames[i].name);
118 for(i = 0; i < ATOM_NUMBER; i++)
120 if(!(r = xcb_intern_atom_reply(globalconf.connection, cs[i], NULL)))
121 /* An error occured, get reply for next atom */
122 continue;
124 *AtomNames[i].atom = r->atom;
125 p_delete(&r);
129 void
130 ewmh_set_supported_hints(int phys_screen)
132 xcb_atom_t atom[ATOM_NUMBER];
133 int i = 0;
135 atom[i++] = net_supported;
136 atom[i++] = net_client_list;
137 atom[i++] = net_number_of_desktops;
138 atom[i++] = net_current_desktop;
139 atom[i++] = net_desktop_names;
140 atom[i++] = net_active_window;
142 atom[i++] = net_close_window;
144 atom[i++] = net_wm_name;
145 atom[i++] = net_wm_icon_name;
146 atom[i++] = net_wm_window_type;
147 atom[i++] = net_wm_window_type_normal;
148 atom[i++] = net_wm_window_type_dock;
149 atom[i++] = net_wm_window_type_splash;
150 atom[i++] = net_wm_window_type_dialog;
151 atom[i++] = net_wm_icon;
152 atom[i++] = net_wm_state;
153 atom[i++] = net_wm_state_sticky;
154 atom[i++] = net_wm_state_skip_taskbar;
155 atom[i++] = net_wm_state_fullscreen;
156 atom[i++] = net_wm_state_above;
157 atom[i++] = net_wm_state_below;
159 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
160 xcb_aux_get_screen(globalconf.connection, phys_screen)->root,
161 net_supported, ATOM, 32, i, atom);
164 void
165 ewmh_update_net_client_list(int phys_screen)
167 xcb_window_t *wins;
168 client_t *c;
169 int n = 0;
171 for(c = globalconf.clients; c; c = c->next)
172 n++;
174 wins = p_new(xcb_window_t, n);
176 for(n = 0, c = globalconf.clients; c; c = c->next, n++)
177 if(c->phys_screen == phys_screen)
178 wins[n] = c->win;
180 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
181 xcb_aux_get_screen(globalconf.connection, phys_screen)->root,
182 net_client_list, WINDOW, 32, n, wins);
184 p_delete(&wins);
187 void
188 ewmh_update_net_numbers_of_desktop(int phys_screen)
190 uint32_t count = 0;
191 tag_t *tag;
193 for(tag = globalconf.screens[phys_screen].tags; tag; tag = tag->next)
194 count++;
196 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
197 xcb_aux_get_screen(globalconf.connection, phys_screen)->root,
198 net_number_of_desktops, CARDINAL, 32, 1, &count);
201 void
202 ewmh_update_net_current_desktop(int phys_screen)
204 uint32_t count = 0;
205 tag_t *tag, **curtags = tags_get_current(phys_screen);
207 for(tag = globalconf.screens[phys_screen].tags; tag != curtags[0]; tag = tag->next)
208 count++;
210 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
211 xcb_aux_get_screen(globalconf.connection, phys_screen)->root,
212 net_current_desktop, CARDINAL, 32, 1, &count);
214 p_delete(&curtags);
217 void
218 ewmh_update_net_desktop_names(int phys_screen)
220 char buf[1024], *pos;
221 ssize_t len, curr_size;
222 tag_t *tag;
224 pos = buf;
225 len = 0;
226 for(tag = globalconf.screens[phys_screen].tags; tag; tag = tag->next)
228 curr_size = a_strlen(tag->name);
229 a_strcpy(pos, sizeof(buf), tag->name);
230 pos += curr_size + 1;
231 len += curr_size + 1;
234 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
235 xcb_aux_get_screen(globalconf.connection, phys_screen)->root,
236 net_desktop_names, utf8_string, 8, len, buf);
239 void
240 ewmh_update_net_active_window(int phys_screen)
242 xcb_window_t win;
243 client_t *sel = focus_get_current_client(phys_screen);
245 win = sel ? sel->win : XCB_NONE;
247 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
248 xcb_aux_get_screen(globalconf.connection, phys_screen)->root,
249 net_active_window, WINDOW, 32, 1, &win);
252 static void
253 ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set)
255 const uint32_t raise_window_val = XCB_STACK_MODE_ABOVE;
257 if(state == net_wm_state_sticky)
259 tag_t *tag;
260 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next)
261 tag_client(c, tag);
263 else if(state == net_wm_state_skip_taskbar)
265 if(set == _NET_WM_STATE_REMOVE)
267 c->skiptb = false;
268 c->skip = false;
270 else if(set == _NET_WM_STATE_ADD)
272 c->skiptb = true;
273 c->skip = true;
276 else if(state == net_wm_state_fullscreen)
278 area_t geometry = c->geometry;
279 if(set == _NET_WM_STATE_REMOVE)
281 /* restore geometry */
282 geometry = c->m_geometry;
283 /* restore borders and titlebar */
284 titlebar_position_set(&c->titlebar, c->titlebar.dposition);
285 c->border = c->oldborder;
286 c->ismax = false;
287 client_setfloating(c, c->wasfloating, c->oldlayer);
289 else if(set == _NET_WM_STATE_ADD)
291 geometry = screen_get_area(c->screen, NULL, &globalconf.screens[c->screen].padding);
292 /* save geometry */
293 c->m_geometry = c->geometry;
294 c->wasfloating = c->isfloating;
295 /* disable titlebar and borders */
296 titlebar_position_set(&c->titlebar, Off);
297 c->oldborder = c->border;
298 c->border = 0;
299 c->ismax = true;
300 client_setfloating(c, true, LAYER_FULLSCREEN);
302 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
303 client_resize(c, geometry, false);
304 xcb_configure_window(globalconf.connection, c->win, XCB_CONFIG_WINDOW_STACK_MODE, &raise_window_val);
306 else if(state == net_wm_state_above)
308 if(set == _NET_WM_STATE_REMOVE)
310 c->layer = c->oldlayer;
312 else if(set == _NET_WM_STATE_ADD)
314 c->oldlayer = c->layer;
315 c->layer = LAYER_ABOVE;
318 else if(state == net_wm_state_below)
320 if(set == _NET_WM_STATE_REMOVE)
322 c->layer = c->oldlayer;
324 else if(set == _NET_WM_STATE_ADD)
326 c->oldlayer = c->layer;
327 c->layer = LAYER_BELOW;
333 static void
334 ewmh_process_window_type_atom(client_t *c, xcb_atom_t state)
336 if(state == net_wm_window_type_normal)
338 /* do nothing. this is REALLY IMPORTANT */
340 else if(state == net_wm_window_type_dock
341 || state == net_wm_window_type_splash)
343 c->border = 0;
344 c->skip = true;
345 c->isfixed = true;
346 titlebar_position_set(&c->titlebar, Off);
347 client_setfloating(c, true, LAYER_ABOVE);
349 else if (state == net_wm_window_type_dialog)
350 client_setfloating(c, true, LAYER_ABOVE);
353 void
354 ewmh_process_client_message(xcb_client_message_event_t *ev)
356 client_t *c;
357 int screen;
359 if(ev->type == net_current_desktop)
360 for(screen = 0;
361 screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection));
362 screen++)
364 if(ev->window == xcb_aux_get_screen(globalconf.connection, screen)->root)
365 tag_view_only_byindex(screen, ev->data.data32[0]);
368 if(ev->type == net_close_window)
370 if((c = client_get_bywin(globalconf.clients, ev->window)))
371 client_kill(c);
373 else if(ev->type == net_wm_state)
375 if((c = client_get_bywin(globalconf.clients, ev->window)))
377 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[1], ev->data.data32[0]);
378 if(ev->data.data32[2])
379 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[2],
380 ev->data.data32[0]);
385 void
386 ewmh_check_client_hints(client_t *c)
388 xcb_atom_t *state;
389 void *data = NULL;
390 int i;
392 xcb_get_property_cookie_t c1, c2;
393 xcb_get_property_reply_t *reply;
395 /* Send the GetProperty requests which will be processed later */
396 c1 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
397 net_wm_state, ATOM, 0, UINT32_MAX);
399 c2 = xcb_get_property_unchecked(globalconf.connection, false, c->win,
400 net_wm_window_type, ATOM, 0, UINT32_MAX);
402 reply = xcb_get_property_reply(globalconf.connection, c1, NULL);
403 if(reply && (data = xcb_get_property_value(reply)))
405 state = (xcb_atom_t *) data;
406 for(i = 0; i < xcb_get_property_value_length(reply); i++)
407 ewmh_process_state_atom(c, state[i], _NET_WM_STATE_ADD);
410 p_delete(&reply);
412 reply = xcb_get_property_reply(globalconf.connection, c2, NULL);
413 if(reply && (data = xcb_get_property_value(reply)))
415 state = (xcb_atom_t *) data;
416 for(i = 0; i < xcb_get_property_value_length(reply); i++)
417 ewmh_process_window_type_atom(c, state[i]);
420 p_delete(&reply);
423 NetWMIcon *
424 ewmh_get_window_icon(xcb_window_t w)
426 double alpha;
427 NetWMIcon *icon;
428 int size, i;
429 uint32_t *data;
430 unsigned char *imgdata;
431 xcb_get_property_reply_t *r;
433 r = xcb_get_property_reply(globalconf.connection,
434 xcb_get_property_unchecked(globalconf.connection, false, w,
435 net_wm_icon, CARDINAL, 0, UINT32_MAX),
436 NULL);
437 if(!r || r->type != CARDINAL || r->format != 32 || r->length < 2 ||
438 !(data = (uint32_t *) xcb_get_property_value(r)))
440 p_delete(&r);
441 return NULL;
444 icon = p_new(NetWMIcon, 1);
446 icon->width = data[0];
447 icon->height = data[1];
448 size = icon->width * icon->height;
450 if(!size)
452 p_delete(&icon);
453 p_delete(&r);
454 return NULL;
457 icon->image = p_new(unsigned char, size * 4);
458 for(imgdata = icon->image, i = 2; i < size + 2; i++, imgdata += 4)
460 imgdata[3] = (data[i] >> 24) & 0xff; /* A */
461 alpha = imgdata[3] / 255.0;
462 imgdata[2] = ((data[i] >> 16) & 0xff) * alpha; /* R */
463 imgdata[1] = ((data[i] >> 8) & 0xff) * alpha; /* G */
464 imgdata[0] = (data[i] & 0xff) * alpha; /* B */
467 p_delete(&r);
469 return icon;
472 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80