Merge remote-tracking branch 'blueyed/remember-layout-across-restarts'
[awesome.git] / ewmh.c
1 /*
2 * ewmh.c - EWMH support functions
3 *
4 * Copyright © 2007-2009 Julien Danjou <julien@danjou.info>
5 *
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.
10 *
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.
15 *
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.
19 *
20 */
21
22 #include "ewmh.h"
23 #include "objects/client.h"
24 #include "objects/tag.h"
25 #include "common/atoms.h"
26
27 #include <sys/types.h>
28 #include <unistd.h>
29
30 #include <xcb/xcb.h>
31 #include <xcb/xcb_atom.h>
32
33 #define _NET_WM_STATE_REMOVE 0
34 #define _NET_WM_STATE_ADD 1
35 #define _NET_WM_STATE_TOGGLE 2
36
37 /** Update client EWMH hints.
38 * \param c The client.
39 */
40 static int
41 ewmh_client_update_hints(lua_State *L)
42 {
43 client_t *c = luaA_checkudata(L, 1, &client_class);
44 xcb_atom_t state[10]; /* number of defined state atoms */
45 int i = 0;
46
47 if(c->modal)
48 state[i++] = _NET_WM_STATE_MODAL;
49 if(c->fullscreen)
50 state[i++] = _NET_WM_STATE_FULLSCREEN;
51 if(c->maximized_vertical)
52 state[i++] = _NET_WM_STATE_MAXIMIZED_VERT;
53 if(c->maximized_horizontal)
54 state[i++] = _NET_WM_STATE_MAXIMIZED_HORZ;
55 if(c->sticky)
56 state[i++] = _NET_WM_STATE_STICKY;
57 if(c->skip_taskbar)
58 state[i++] = _NET_WM_STATE_SKIP_TASKBAR;
59 if(c->above)
60 state[i++] = _NET_WM_STATE_ABOVE;
61 if(c->below)
62 state[i++] = _NET_WM_STATE_BELOW;
63 if(c->minimized)
64 state[i++] = _NET_WM_STATE_HIDDEN;
65 if(c->urgent)
66 state[i++] = _NET_WM_STATE_DEMANDS_ATTENTION;
67
68 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
69 c->window, _NET_WM_STATE, XCB_ATOM_ATOM, 32, i, state);
70
71 return 0;
72 }
73
74 static int
75 ewmh_update_net_active_window(lua_State *L)
76 {
77 xcb_window_t win;
78
79 if(globalconf.focus.client)
80 win = globalconf.focus.client->window;
81 else
82 win = XCB_NONE;
83
84 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
85 globalconf.screen->root,
86 _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 32, 1, &win);
87
88 return 0;
89 }
90
91 static int
92 ewmh_update_net_client_list(lua_State *L)
93 {
94 xcb_window_t *wins = p_alloca(xcb_window_t, globalconf.clients.len);
95
96 int n = 0;
97 foreach(client, globalconf.clients)
98 wins[n++] = (*client)->window;
99
100 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
101 globalconf.screen->root,
102 _NET_CLIENT_LIST, XCB_ATOM_WINDOW, 32, n, wins);
103
104 return 0;
105 }
106
107 void
108 ewmh_init(void)
109 {
110 xcb_window_t father;
111 xcb_screen_t *xscreen = globalconf.screen;
112 xcb_atom_t atom[] =
113 {
114 _NET_SUPPORTED,
115 _NET_SUPPORTING_WM_CHECK,
116 _NET_STARTUP_ID,
117 _NET_CLIENT_LIST,
118 _NET_CLIENT_LIST_STACKING,
119 _NET_NUMBER_OF_DESKTOPS,
120 _NET_CURRENT_DESKTOP,
121 _NET_DESKTOP_NAMES,
122 _NET_ACTIVE_WINDOW,
123 _NET_CLOSE_WINDOW,
124 _NET_WM_NAME,
125 _NET_WM_STRUT_PARTIAL,
126 _NET_WM_ICON_NAME,
127 _NET_WM_VISIBLE_ICON_NAME,
128 _NET_WM_DESKTOP,
129 _NET_WM_WINDOW_TYPE,
130 _NET_WM_WINDOW_TYPE_DESKTOP,
131 _NET_WM_WINDOW_TYPE_DOCK,
132 _NET_WM_WINDOW_TYPE_TOOLBAR,
133 _NET_WM_WINDOW_TYPE_MENU,
134 _NET_WM_WINDOW_TYPE_UTILITY,
135 _NET_WM_WINDOW_TYPE_SPLASH,
136 _NET_WM_WINDOW_TYPE_DIALOG,
137 _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
138 _NET_WM_WINDOW_TYPE_POPUP_MENU,
139 _NET_WM_WINDOW_TYPE_TOOLTIP,
140 _NET_WM_WINDOW_TYPE_NOTIFICATION,
141 _NET_WM_WINDOW_TYPE_COMBO,
142 _NET_WM_WINDOW_TYPE_DND,
143 _NET_WM_WINDOW_TYPE_NORMAL,
144 _NET_WM_ICON,
145 _NET_WM_PID,
146 _NET_WM_STATE,
147 _NET_WM_STATE_STICKY,
148 _NET_WM_STATE_SKIP_TASKBAR,
149 _NET_WM_STATE_FULLSCREEN,
150 _NET_WM_STATE_MAXIMIZED_HORZ,
151 _NET_WM_STATE_MAXIMIZED_VERT,
152 _NET_WM_STATE_ABOVE,
153 _NET_WM_STATE_BELOW,
154 _NET_WM_STATE_MODAL,
155 _NET_WM_STATE_HIDDEN,
156 _NET_WM_STATE_DEMANDS_ATTENTION
157 };
158 int i;
159
160 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
161 xscreen->root, _NET_SUPPORTED, XCB_ATOM_ATOM, 32,
162 countof(atom), atom);
163
164 /* create our own window */
165 father = xcb_generate_id(globalconf.connection);
166 xcb_create_window(globalconf.connection, xscreen->root_depth,
167 father, xscreen->root, -1, -1, 1, 1, 0,
168 XCB_COPY_FROM_PARENT, xscreen->root_visual, 0, NULL);
169
170 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
171 xscreen->root, _NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32,
172 1, &father);
173
174 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
175 father, _NET_SUPPORTING_WM_CHECK, XCB_ATOM_WINDOW, 32,
176 1, &father);
177
178 /* set the window manager name */
179 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
180 father, _NET_WM_NAME, UTF8_STRING, 8, 7, "awesome");
181
182 /* set the window manager PID */
183 i = getpid();
184 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
185 father, _NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &i);
186
187
188 luaA_class_connect_signal(globalconf.L, &client_class, "focus", ewmh_update_net_active_window);
189 luaA_class_connect_signal(globalconf.L, &client_class, "unfocus", ewmh_update_net_active_window);
190 luaA_class_connect_signal(globalconf.L, &client_class, "manage", ewmh_update_net_client_list);
191 luaA_class_connect_signal(globalconf.L, &client_class, "unmanage", ewmh_update_net_client_list);
192 luaA_class_connect_signal(globalconf.L, &client_class, "property::modal" , ewmh_client_update_hints);
193 luaA_class_connect_signal(globalconf.L, &client_class, "property::fullscreen" , ewmh_client_update_hints);
194 luaA_class_connect_signal(globalconf.L, &client_class, "property::maximized_horizontal" , ewmh_client_update_hints);
195 luaA_class_connect_signal(globalconf.L, &client_class, "property::maximized_vertical" , ewmh_client_update_hints);
196 luaA_class_connect_signal(globalconf.L, &client_class, "property::sticky" , ewmh_client_update_hints);
197 luaA_class_connect_signal(globalconf.L, &client_class, "property::skip_taskbar" , ewmh_client_update_hints);
198 luaA_class_connect_signal(globalconf.L, &client_class, "property::above" , ewmh_client_update_hints);
199 luaA_class_connect_signal(globalconf.L, &client_class, "property::below" , ewmh_client_update_hints);
200 luaA_class_connect_signal(globalconf.L, &client_class, "property::minimized" , ewmh_client_update_hints);
201 luaA_class_connect_signal(globalconf.L, &client_class, "property::urgent" , ewmh_client_update_hints);
202 }
203
204 /** Set the client list in stacking order, bottom to top.
205 */
206 void
207 ewmh_update_net_client_list_stacking(void)
208 {
209 int n = 0;
210 xcb_window_t *wins = p_alloca(xcb_window_t, globalconf.stack.len);
211
212 foreach(client, globalconf.stack)
213 wins[n++] = (*client)->window;
214
215 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
216 globalconf.screen->root,
217 _NET_CLIENT_LIST_STACKING, XCB_ATOM_WINDOW, 32, n, wins);
218 }
219
220 void
221 ewmh_update_net_numbers_of_desktop(void)
222 {
223 uint32_t count = globalconf.tags.len;
224
225 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
226 globalconf.screen->root,
227 _NET_NUMBER_OF_DESKTOPS, XCB_ATOM_CARDINAL, 32, 1, &count);
228 }
229
230 void
231 ewmh_update_net_current_desktop(void)
232 {
233 uint32_t idx = tags_get_first_selected_index();
234
235 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
236 globalconf.screen->root,
237 _NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &idx);
238 }
239
240 void
241 ewmh_update_net_desktop_names(void)
242 {
243 buffer_t buf;
244
245 buffer_inita(&buf, BUFSIZ);
246
247 foreach(tag, globalconf.tags)
248 {
249 buffer_adds(&buf, tag_get_name(*tag));
250 buffer_addc(&buf, '\0');
251 }
252
253 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
254 globalconf.screen->root,
255 _NET_DESKTOP_NAMES, UTF8_STRING, 8, buf.len, buf.s);
256 buffer_wipe(&buf);
257 }
258
259 static void
260 ewmh_process_state_atom(client_t *c, xcb_atom_t state, int set)
261 {
262 luaA_object_push(globalconf.L, c);
263
264 if(state == _NET_WM_STATE_STICKY)
265 {
266 if(set == _NET_WM_STATE_REMOVE)
267 client_set_sticky(globalconf.L, -1, false);
268 else if(set == _NET_WM_STATE_ADD)
269 client_set_sticky(globalconf.L, -1, true);
270 else if(set == _NET_WM_STATE_TOGGLE)
271 client_set_sticky(globalconf.L, -1, !c->sticky);
272 }
273 else if(state == _NET_WM_STATE_SKIP_TASKBAR)
274 {
275 if(set == _NET_WM_STATE_REMOVE)
276 client_set_skip_taskbar(globalconf.L, -1, false);
277 else if(set == _NET_WM_STATE_ADD)
278 client_set_skip_taskbar(globalconf.L, -1, true);
279 else if(set == _NET_WM_STATE_TOGGLE)
280 client_set_skip_taskbar(globalconf.L, -1, !c->skip_taskbar);
281 }
282 else if(state == _NET_WM_STATE_FULLSCREEN)
283 {
284 if(set == _NET_WM_STATE_REMOVE)
285 client_set_fullscreen(globalconf.L, -1, false);
286 else if(set == _NET_WM_STATE_ADD)
287 client_set_fullscreen(globalconf.L, -1, true);
288 else if(set == _NET_WM_STATE_TOGGLE)
289 client_set_fullscreen(globalconf.L, -1, !c->fullscreen);
290 }
291 else if(state == _NET_WM_STATE_MAXIMIZED_HORZ)
292 {
293 if(set == _NET_WM_STATE_REMOVE)
294 client_set_maximized_horizontal(globalconf.L, -1, false);
295 else if(set == _NET_WM_STATE_ADD)
296 client_set_maximized_horizontal(globalconf.L, -1, true);
297 else if(set == _NET_WM_STATE_TOGGLE)
298 client_set_maximized_horizontal(globalconf.L, -1, !c->maximized_horizontal);
299 }
300 else if(state == _NET_WM_STATE_MAXIMIZED_VERT)
301 {
302 if(set == _NET_WM_STATE_REMOVE)
303 client_set_maximized_vertical(globalconf.L, -1, false);
304 else if(set == _NET_WM_STATE_ADD)
305 client_set_maximized_vertical(globalconf.L, -1, true);
306 else if(set == _NET_WM_STATE_TOGGLE)
307 client_set_maximized_vertical(globalconf.L, -1, !c->maximized_vertical);
308 }
309 else if(state == _NET_WM_STATE_ABOVE)
310 {
311 if(set == _NET_WM_STATE_REMOVE)
312 client_set_above(globalconf.L, -1, false);
313 else if(set == _NET_WM_STATE_ADD)
314 client_set_above(globalconf.L, -1, true);
315 else if(set == _NET_WM_STATE_TOGGLE)
316 client_set_above(globalconf.L, -1, !c->above);
317 }
318 else if(state == _NET_WM_STATE_BELOW)
319 {
320 if(set == _NET_WM_STATE_REMOVE)
321 client_set_below(globalconf.L, -1, false);
322 else if(set == _NET_WM_STATE_ADD)
323 client_set_below(globalconf.L, -1, true);
324 else if(set == _NET_WM_STATE_TOGGLE)
325 client_set_below(globalconf.L, -1, !c->below);
326 }
327 else if(state == _NET_WM_STATE_MODAL)
328 {
329 if(set == _NET_WM_STATE_REMOVE)
330 client_set_modal(globalconf.L, -1, false);
331 else if(set == _NET_WM_STATE_ADD)
332 client_set_modal(globalconf.L, -1, true);
333 else if(set == _NET_WM_STATE_TOGGLE)
334 client_set_modal(globalconf.L, -1, !c->modal);
335 }
336 else if(state == _NET_WM_STATE_HIDDEN)
337 {
338 if(set == _NET_WM_STATE_REMOVE)
339 client_set_minimized(globalconf.L, -1, false);
340 else if(set == _NET_WM_STATE_ADD)
341 client_set_minimized(globalconf.L, -1, true);
342 else if(set == _NET_WM_STATE_TOGGLE)
343 client_set_minimized(globalconf.L, -1, !c->minimized);
344 }
345 else if(state == _NET_WM_STATE_DEMANDS_ATTENTION)
346 {
347 if(set == _NET_WM_STATE_REMOVE)
348 client_set_urgent(globalconf.L, -1, false);
349 else if(set == _NET_WM_STATE_ADD)
350 client_set_urgent(globalconf.L, -1, true);
351 else if(set == _NET_WM_STATE_TOGGLE)
352 client_set_urgent(globalconf.L, -1, !c->urgent);
353 }
354
355 lua_pop(globalconf.L, 1);
356 }
357
358 static void
359 ewmh_process_desktop(client_t *c, uint32_t desktop)
360 {
361 int idx = desktop;
362 if(desktop == 0xffffffff)
363 {
364 luaA_object_push(globalconf.L, c);
365 lua_pushnil(globalconf.L);
366 luaA_object_emit_signal(globalconf.L, -2, "request::tag", 1);
367 /* Pop the client, arguments are already popped */
368 lua_pop(globalconf.L, 1);
369 }
370 else if (idx >= 0 && idx < globalconf.tags.len)
371 {
372 luaA_object_push(globalconf.L, c);
373 luaA_object_push(globalconf.L, globalconf.tags.tab[idx]);
374 luaA_object_emit_signal(globalconf.L, -2, "request::tag", 1);
375 /* Pop the client, arguments are already popped */
376 lua_pop(globalconf.L, 1);
377 }
378 }
379
380 int
381 ewmh_process_client_message(xcb_client_message_event_t *ev)
382 {
383 client_t *c;
384
385 if(ev->type == _NET_CURRENT_DESKTOP)
386 {
387 int idx = ev->data.data32[0];
388 if (idx >= 0 && idx < globalconf.tags.len)
389 {
390 luaA_object_push(globalconf.L, globalconf.tags.tab[idx]);
391 luaA_object_emit_signal(globalconf.L, -1, "request::select", 0);
392 lua_pop(globalconf.L, 1);
393 }
394 }
395 else if(ev->type == _NET_CLOSE_WINDOW)
396 {
397 if((c = client_getbywin(ev->window)))
398 client_kill(c);
399 }
400 else if(ev->type == _NET_WM_DESKTOP)
401 {
402 if((c = client_getbywin(ev->window)))
403 {
404 ewmh_process_desktop(c, ev->data.data32[0]);
405 }
406 }
407 else if(ev->type == _NET_WM_STATE)
408 {
409 if((c = client_getbywin(ev->window)))
410 {
411 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[1], ev->data.data32[0]);
412 if(ev->data.data32[2])
413 ewmh_process_state_atom(c, (xcb_atom_t) ev->data.data32[2],
414 ev->data.data32[0]);
415 }
416 }
417 else if(ev->type == _NET_ACTIVE_WINDOW)
418 {
419 if((c = client_getbywin(ev->window))) {
420 luaA_object_push(globalconf.L, c);
421 luaA_object_emit_signal(globalconf.L, -1, "request::activate", 0);
422 lua_pop(globalconf.L, 1);
423 }
424 }
425
426 return 0;
427 }
428
429 /** Update the client active desktop.
430 * This is "wrong" since it can be on several tags, but EWMH has a strict view
431 * of desktop system so just take the first tag.
432 * \param c The client.
433 */
434 void
435 ewmh_client_update_desktop(client_t *c)
436 {
437 int i;
438
439 for(i = 0; i < globalconf.tags.len; i++)
440 if(is_client_tagged(c, globalconf.tags.tab[i]))
441 {
442 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
443 c->window, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, 32, 1, &i);
444 return;
445 }
446 /* It doesn't have any tags, remove the property */
447 xcb_delete_property(globalconf.connection, c->window, _NET_WM_DESKTOP);
448 }
449
450 /** Update the client struts.
451 * \param window The window to update the struts for.
452 * \param strut The strut type to update the window with.
453 */
454 void
455 ewmh_update_strut(xcb_window_t window, strut_t *strut)
456 {
457 if(window)
458 {
459 const uint32_t state[] =
460 {
461 strut->left,
462 strut->right,
463 strut->top,
464 strut->bottom,
465 strut->left_start_y,
466 strut->left_end_y,
467 strut->right_start_y,
468 strut->right_end_y,
469 strut->top_start_x,
470 strut->top_end_x,
471 strut->bottom_start_x,
472 strut->bottom_end_x
473 };
474
475 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
476 window, _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, countof(state), state);
477 }
478 }
479
480 /** Update the window type.
481 * \param window The window to update.
482 * \param type The new type to set.
483 */
484 void
485 ewmh_update_window_type(xcb_window_t window, uint32_t type)
486 {
487 xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE,
488 window, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 32, 1, &type);
489 }
490
491 void
492 ewmh_client_check_hints(client_t *c)
493 {
494 xcb_atom_t *state;
495 void *data = NULL;
496 xcb_get_property_cookie_t c0, c1, c2;
497 xcb_get_property_reply_t *reply;
498
499 /* Send the GetProperty requests which will be processed later */
500 c0 = xcb_get_property_unchecked(globalconf.connection, false, c->window,
501 _NET_WM_DESKTOP, XCB_GET_PROPERTY_TYPE_ANY, 0, 1);
502
503 c1 = xcb_get_property_unchecked(globalconf.connection, false, c->window,
504 _NET_WM_STATE, XCB_ATOM_ATOM, 0, UINT32_MAX);
505
506 c2 = xcb_get_property_unchecked(globalconf.connection, false, c->window,
507 _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, 0, UINT32_MAX);
508
509 reply = xcb_get_property_reply(globalconf.connection, c0, NULL);
510 if(reply && reply->value_len && (data = xcb_get_property_value(reply)))
511 {
512 ewmh_process_desktop(c, *(uint32_t *) data);
513 }
514
515 p_delete(&reply);
516
517 reply = xcb_get_property_reply(globalconf.connection, c1, NULL);
518 if(reply && (data = xcb_get_property_value(reply)))
519 {
520 state = (xcb_atom_t *) data;
521 for(int i = 0; i < xcb_get_property_value_length(reply) / ssizeof(xcb_atom_t); i++)
522 ewmh_process_state_atom(c, state[i], _NET_WM_STATE_ADD);
523 }
524
525 p_delete(&reply);
526
527 reply = xcb_get_property_reply(globalconf.connection, c2, NULL);
528 if(reply && (data = xcb_get_property_value(reply)))
529 {
530 state = (xcb_atom_t *) data;
531 for(int i = 0; i < xcb_get_property_value_length(reply) / ssizeof(xcb_atom_t); i++)
532 if(state[i] == _NET_WM_WINDOW_TYPE_DESKTOP)
533 c->type = MAX(c->type, WINDOW_TYPE_DESKTOP);
534 else if(state[i] == _NET_WM_WINDOW_TYPE_DIALOG)
535 c->type = MAX(c->type, WINDOW_TYPE_DIALOG);
536 else if(state[i] == _NET_WM_WINDOW_TYPE_SPLASH)
537 c->type = MAX(c->type, WINDOW_TYPE_SPLASH);
538 else if(state[i] == _NET_WM_WINDOW_TYPE_DOCK)
539 c->type = MAX(c->type, WINDOW_TYPE_DOCK);
540 else if(state[i] == _NET_WM_WINDOW_TYPE_MENU)
541 c->type = MAX(c->type, WINDOW_TYPE_MENU);
542 else if(state[i] == _NET_WM_WINDOW_TYPE_TOOLBAR)
543 c->type = MAX(c->type, WINDOW_TYPE_TOOLBAR);
544 else if(state[i] == _NET_WM_WINDOW_TYPE_UTILITY)
545 c->type = MAX(c->type, WINDOW_TYPE_UTILITY);
546 }
547
548 p_delete(&reply);
549 }
550
551 /** Process the WM strut of a client.
552 * \param c The client.
553 * \param strut_r (Optional) An existing reply.
554 */
555 void
556 ewmh_process_client_strut(client_t *c)
557 {
558 void *data;
559 xcb_get_property_reply_t *strut_r;
560
561 xcb_get_property_cookie_t strut_q = xcb_get_property_unchecked(globalconf.connection, false, c->window,
562 _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 0, 12);
563 strut_r = xcb_get_property_reply(globalconf.connection, strut_q, NULL);
564
565 if(strut_r
566 && strut_r->value_len
567 && (data = xcb_get_property_value(strut_r)))
568 {
569 uint32_t *strut = data;
570
571 if(c->strut.left != strut[0]
572 || c->strut.right != strut[1]
573 || c->strut.top != strut[2]
574 || c->strut.bottom != strut[3]
575 || c->strut.left_start_y != strut[4]
576 || c->strut.left_end_y != strut[5]
577 || c->strut.right_start_y != strut[6]
578 || c->strut.right_end_y != strut[7]
579 || c->strut.top_start_x != strut[8]
580 || c->strut.top_end_x != strut[9]
581 || c->strut.bottom_start_x != strut[10]
582 || c->strut.bottom_end_x != strut[11])
583 {
584 c->strut.left = strut[0];
585 c->strut.right = strut[1];
586 c->strut.top = strut[2];
587 c->strut.bottom = strut[3];
588 c->strut.left_start_y = strut[4];
589 c->strut.left_end_y = strut[5];
590 c->strut.right_start_y = strut[6];
591 c->strut.right_end_y = strut[7];
592 c->strut.top_start_x = strut[8];
593 c->strut.top_end_x = strut[9];
594 c->strut.bottom_start_x = strut[10];
595 c->strut.bottom_end_x = strut[11];
596
597 luaA_object_push(globalconf.L, c);
598 luaA_object_emit_signal(globalconf.L, -1, "property::struts", 0);
599 lua_pop(globalconf.L, 1);
600 }
601 }
602
603 p_delete(&strut_r);
604 }
605
606 /** Send request to get NET_WM_ICON (EWMH)
607 * \param w The window.
608 * \return The cookie associated with the request.
609 */
610 xcb_get_property_cookie_t
611 ewmh_window_icon_get_unchecked(xcb_window_t w)
612 {
613 return xcb_get_property_unchecked(globalconf.connection, false, w,
614 _NET_WM_ICON, XCB_ATOM_CARDINAL, 0, UINT32_MAX);
615 }
616
617 static cairo_surface_t *
618 ewmh_window_icon_from_reply(xcb_get_property_reply_t *r)
619 {
620 uint32_t *data;
621 uint64_t len;
622
623 if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32 || r->length < 2)
624 return 0;
625
626 data = (uint32_t *) xcb_get_property_value(r);
627 if (!data)
628 return 0;
629
630 /* Check that the property is as long as it should be, handling integer
631 * overflow. <uint32_t> times <another uint32_t casted to uint64_t> always
632 * fits into an uint64_t and thus this multiplication cannot overflow.
633 */
634 len = data[0] * (uint64_t) data[1];
635 if (!data[0] || !data[1] || len > r->length - 2)
636 return 0;
637
638 return draw_surface_from_data(data[0], data[1], data + 2);
639 }
640
641 /** Get NET_WM_ICON.
642 * \param cookie The cookie.
643 * \return The number of elements on stack.
644 */
645 cairo_surface_t *
646 ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie)
647 {
648 xcb_get_property_reply_t *r = xcb_get_property_reply(globalconf.connection, cookie, NULL);
649 cairo_surface_t *surface = ewmh_window_icon_from_reply(r);
650 p_delete(&r);
651 return surface;
652 }
653
654 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80