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>
26 #include <xcb/xcb_atom.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 /** Update the desktop geometry.
45 * \param phys_screen The physical screen id.
48 ewmh_update_desktop_geometry(int phys_screen
)
50 area_t geom
= screen_area_get(phys_screen
,
54 uint32_t sizes
[] = { geom
.width
, geom
.height
};
56 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
57 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
58 _NET_DESKTOP_GEOMETRY
, CARDINAL
, 32, countof(sizes
), sizes
);
62 ewmh_init(int phys_screen
)
65 xcb_screen_t
*xscreen
= xutil_screen_get(globalconf
.connection
, phys_screen
);
69 _NET_SUPPORTING_WM_CHECK
,
71 _NET_CLIENT_LIST_STACKING
,
72 _NET_NUMBER_OF_DESKTOPS
,
77 _NET_DESKTOP_GEOMETRY
,
80 _NET_WM_STRUT_PARTIAL
,
82 _NET_WM_VISIBLE_ICON_NAME
,
85 _NET_WM_WINDOW_TYPE_DESKTOP
,
86 _NET_WM_WINDOW_TYPE_DOCK
,
87 _NET_WM_WINDOW_TYPE_TOOLBAR
,
88 _NET_WM_WINDOW_TYPE_MENU
,
89 _NET_WM_WINDOW_TYPE_UTILITY
,
90 _NET_WM_WINDOW_TYPE_SPLASH
,
91 _NET_WM_WINDOW_TYPE_DIALOG
,
92 _NET_WM_WINDOW_TYPE_DROPDOWN_MENU
,
93 _NET_WM_WINDOW_TYPE_POPUP_MENU
,
94 _NET_WM_WINDOW_TYPE_TOOLTIP
,
95 _NET_WM_WINDOW_TYPE_NOTIFICATION
,
96 _NET_WM_WINDOW_TYPE_COMBO
,
97 _NET_WM_WINDOW_TYPE_DND
,
98 _NET_WM_WINDOW_TYPE_NORMAL
,
102 _NET_WM_STATE_STICKY
,
103 _NET_WM_STATE_SKIP_TASKBAR
,
104 _NET_WM_STATE_FULLSCREEN
,
105 _NET_WM_STATE_MAXIMIZED_HORZ
,
106 _NET_WM_STATE_MAXIMIZED_VERT
,
110 _NET_WM_STATE_HIDDEN
,
111 _NET_WM_STATE_DEMANDS_ATTENTION
115 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
116 xscreen
->root
, _NET_SUPPORTED
, ATOM
, 32,
117 countof(atom
), atom
);
119 /* create our own window */
120 father
= xcb_generate_id(globalconf
.connection
);
121 xcb_create_window(globalconf
.connection
, xscreen
->root_depth
,
122 father
, xscreen
->root
, -1, -1, 1, 1, 0,
123 XCB_COPY_FROM_PARENT
, xscreen
->root_visual
, 0, NULL
);
125 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
126 xscreen
->root
, _NET_SUPPORTING_WM_CHECK
, WINDOW
, 32,
129 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
130 father
, _NET_SUPPORTING_WM_CHECK
, WINDOW
, 32,
133 /* set the window manager name */
134 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
135 father
, _NET_WM_NAME
, UTF8_STRING
, 8, 7, "awesome");
137 /* set the window manager PID */
139 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
140 father
, _NET_WM_PID
, CARDINAL
, 32, 1, &i
);
142 ewmh_update_desktop_geometry(phys_screen
);
146 ewmh_update_net_client_list(int phys_screen
)
152 for(c
= globalconf
.clients
; c
; c
= c
->next
)
155 wins
= p_alloca(xcb_window_t
, n
);
157 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
, n
++)
158 if(c
->phys_screen
== phys_screen
)
161 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
162 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
163 _NET_CLIENT_LIST
, WINDOW
, 32, n
, wins
);
166 /** Set the client list in stacking order, bottom to top.
167 * \param phys_screen The physical screen id.
170 ewmh_update_net_client_list_stacking(int phys_screen
)
176 for(c
= globalconf
.stack
; c
; c
= c
->next
)
179 wins
= p_alloca(xcb_window_t
, n
);
181 for(n
= 0, c
= *client_node_list_last(&globalconf
.stack
); c
; c
= c
->prev
, n
++)
182 if(c
->client
->phys_screen
== phys_screen
)
183 wins
[n
] = c
->client
->win
;
185 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
186 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
187 _NET_CLIENT_LIST_STACKING
, WINDOW
, 32, n
, wins
);
191 ewmh_update_net_numbers_of_desktop(int phys_screen
)
193 uint32_t count
= globalconf
.screens
[phys_screen
].tags
.len
;
195 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
196 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
197 _NET_NUMBER_OF_DESKTOPS
, CARDINAL
, 32, 1, &count
);
201 ewmh_update_net_current_desktop(int phys_screen
)
203 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
205 tag_t
**curtags
= tags_get_current(phys_screen
);
207 while(count
< (uint32_t) tags
->len
&& tags
->tab
[count
] != curtags
[0])
210 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
211 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
212 _NET_CURRENT_DESKTOP
, CARDINAL
, 32, 1, &count
);
218 ewmh_update_net_desktop_names(int phys_screen
)
220 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
223 buffer_inita(&buf
, BUFSIZ
);
225 for(int i
= 0; i
< tags
->len
; i
++)
227 buffer_adds(&buf
, tags
->tab
[i
]->name
);
228 buffer_addc(&buf
, '\0');
231 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
232 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
233 _NET_DESKTOP_NAMES
, UTF8_STRING
, 8, buf
.len
, buf
.s
);
237 /** Update the work area space for each physical screen and each desktop.
238 * \param phys_screen The physical screen id.
241 ewmh_update_workarea(int phys_screen
)
243 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
244 uint32_t *area
= p_alloca(uint32_t, tags
->len
* 4);
245 area_t geom
= screen_area_get(phys_screen
,
246 &globalconf
.screens
[phys_screen
].wiboxes
,
247 &globalconf
.screens
[phys_screen
].padding
,
251 for(int i
= 0; i
< tags
->len
; i
++)
253 area
[4 * i
+ 0] = geom
.x
;
254 area
[4 * i
+ 1] = geom
.y
;
255 area
[4 * i
+ 2] = geom
.width
;
256 area
[4 * i
+ 3] = geom
.height
;
259 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
260 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
261 _NET_WORKAREA
, CARDINAL
, 32, tags
->len
* 4, area
);
265 ewmh_update_net_active_window(int phys_screen
)
269 if(globalconf
.screen_focus
->client_focus
270 && globalconf
.screen_focus
->client_focus
->phys_screen
== phys_screen
)
271 win
= globalconf
.screen_focus
->client_focus
->win
;
275 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
276 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
277 _NET_ACTIVE_WINDOW
, WINDOW
, 32, 1, &win
);
281 ewmh_process_state_atom(client_t
*c
, xcb_atom_t state
, int set
)
283 if(state
== _NET_WM_STATE_STICKY
)
285 if(set
== _NET_WM_STATE_REMOVE
)
286 client_setsticky(c
, false);
287 else if(set
== _NET_WM_STATE_ADD
)
288 client_setsticky(c
, true);
289 else if(set
== _NET_WM_STATE_TOGGLE
)
290 client_setsticky(c
, !c
->issticky
);
292 else if(state
== _NET_WM_STATE_SKIP_TASKBAR
)
294 if(set
== _NET_WM_STATE_REMOVE
)
297 ewmh_client_update_hints(c
);
299 else if(set
== _NET_WM_STATE_ADD
)
302 ewmh_client_update_hints(c
);
304 else if(set
== _NET_WM_STATE_TOGGLE
)
306 c
->skiptb
= !c
->skiptb
;
307 ewmh_client_update_hints(c
);
310 else if(state
== _NET_WM_STATE_FULLSCREEN
)
312 if(set
== _NET_WM_STATE_REMOVE
)
313 client_setfullscreen(c
, false);
314 else if(set
== _NET_WM_STATE_ADD
)
315 client_setfullscreen(c
, true);
316 else if(set
== _NET_WM_STATE_TOGGLE
)
317 client_setfullscreen(c
, !c
->isfullscreen
);
319 else if(state
== _NET_WM_STATE_MAXIMIZED_HORZ
)
321 if(set
== _NET_WM_STATE_REMOVE
)
322 client_setmaxhoriz(c
, false);
323 else if(set
== _NET_WM_STATE_ADD
)
324 client_setmaxhoriz(c
, true);
325 else if(set
== _NET_WM_STATE_TOGGLE
)
326 client_setmaxhoriz(c
, !c
->ismaxhoriz
);
328 else if(state
== _NET_WM_STATE_MAXIMIZED_VERT
)
330 if(set
== _NET_WM_STATE_REMOVE
)
331 client_setmaxvert(c
, false);
332 else if(set
== _NET_WM_STATE_ADD
)
333 client_setmaxvert(c
, true);
334 else if(set
== _NET_WM_STATE_TOGGLE
)
335 client_setmaxvert(c
, !c
->ismaxvert
);
337 else if(state
== _NET_WM_STATE_ABOVE
)
339 if(set
== _NET_WM_STATE_REMOVE
)
340 client_setabove(c
, false);
341 else if(set
== _NET_WM_STATE_ADD
)
342 client_setabove(c
, true);
343 else if(set
== _NET_WM_STATE_TOGGLE
)
344 client_setabove(c
, !c
->isabove
);
346 else if(state
== _NET_WM_STATE_BELOW
)
348 if(set
== _NET_WM_STATE_REMOVE
)
349 client_setbelow(c
, false);
350 else if(set
== _NET_WM_STATE_ADD
)
351 client_setbelow(c
, true);
352 else if(set
== _NET_WM_STATE_TOGGLE
)
353 client_setbelow(c
, !c
->isbelow
);
355 else if(state
== _NET_WM_STATE_MODAL
)
357 if(set
== _NET_WM_STATE_REMOVE
)
358 client_setmodal(c
, false);
359 else if(set
== _NET_WM_STATE_ADD
)
360 client_setmodal(c
, true);
361 else if(set
== _NET_WM_STATE_TOGGLE
)
362 client_setmodal(c
, !c
->ismodal
);
364 else if(state
== _NET_WM_STATE_HIDDEN
)
366 if(set
== _NET_WM_STATE_REMOVE
)
367 client_setminimized(c
, false);
368 else if(set
== _NET_WM_STATE_ADD
)
369 client_setminimized(c
, true);
370 else if(set
== _NET_WM_STATE_TOGGLE
)
371 client_setminimized(c
, !c
->isminimized
);
373 else if(state
== _NET_WM_STATE_DEMANDS_ATTENTION
)
375 if(set
== _NET_WM_STATE_REMOVE
)
376 client_seturgent(c
, false);
377 else if(set
== _NET_WM_STATE_ADD
)
378 client_seturgent(c
, true);
379 else if(set
== _NET_WM_STATE_TOGGLE
)
380 client_seturgent(c
, !c
->isurgent
);
385 ewmh_process_client_message(xcb_client_message_event_t
*ev
)
390 if(ev
->type
== _NET_CURRENT_DESKTOP
)
392 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
395 if(ev
->window
== xutil_screen_get(globalconf
.connection
, screen
)->root
)
396 tag_view_only_byindex(screen
, ev
->data
.data32
[0]);
398 else if(ev
->type
== _NET_CLOSE_WINDOW
)
400 if((c
= client_getbywin(ev
->window
)))
403 else if(ev
->type
== _NET_WM_DESKTOP
)
405 if((c
= client_getbywin(ev
->window
)))
407 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
409 if(ev
->data
.data32
[0] == 0xffffffff)
412 for(int i
= 0; i
< tags
->len
; i
++)
413 if((int)ev
->data
.data32
[0] == i
)
414 tag_client(c
, tags
->tab
[i
]);
416 untag_client(c
, tags
->tab
[i
]);
419 else if(ev
->type
== _NET_WM_STATE
)
421 if((c
= client_getbywin(ev
->window
)))
423 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[1], ev
->data
.data32
[0]);
424 if(ev
->data
.data32
[2])
425 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[2],
429 else if(ev
->type
== _NET_ACTIVE_WINDOW
)
431 if((c
= client_getbywin(ev
->window
)))
438 /** Update client EWMH hints.
439 * \param c The client.
442 ewmh_client_update_hints(client_t
*c
)
444 xcb_atom_t state
[10]; /* number of defined state atoms */
448 state
[i
++] = _NET_WM_STATE_MODAL
;
450 state
[i
++] = _NET_WM_STATE_FULLSCREEN
;
452 state
[i
++] = _NET_WM_STATE_MAXIMIZED_VERT
;
454 state
[i
++] = _NET_WM_STATE_MAXIMIZED_HORZ
;
456 state
[i
++] = _NET_WM_STATE_STICKY
;
458 state
[i
++] = _NET_WM_STATE_SKIP_TASKBAR
;
460 state
[i
++] = _NET_WM_STATE_ABOVE
;
462 state
[i
++] = _NET_WM_STATE_BELOW
;
464 state
[i
++] = _NET_WM_STATE_HIDDEN
;
466 state
[i
++] = _NET_WM_STATE_DEMANDS_ATTENTION
;
468 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
469 c
->win
, _NET_WM_STATE
, ATOM
, 32, i
, state
);
472 /** Update the client active desktop.
473 * This is "wrong" since it can be on several tags, but EWMH has a strict view
474 * of desktop system so just take the first tag.
475 * \param c The client.
478 ewmh_client_update_desktop(client_t
*c
)
481 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
483 for(i
= 0; i
< tags
->len
; i
++)
484 if(is_client_tagged(c
, tags
->tab
[i
]))
486 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
487 c
->win
, _NET_WM_DESKTOP
, CARDINAL
, 32, 1, &i
);
492 /** Update the client struts.
493 * \param c The client.
496 ewmh_update_client_strut(client_t
*c
)
504 c
->strut
.left_start_y
,
506 c
->strut
.right_start_y
,
507 c
->strut
.right_end_y
,
508 c
->strut
.top_start_x
,
510 c
->strut
.bottom_start_x
,
511 c
->strut
.bottom_end_x
514 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
515 c
->win
, _NET_WM_STRUT_PARTIAL
, CARDINAL
, 32, countof(state
), state
);
519 ewmh_client_check_hints(client_t
*c
)
524 xcb_get_property_cookie_t c0
, c1
, c2
;
525 xcb_get_property_reply_t
*reply
;
527 /* Send the GetProperty requests which will be processed later */
528 c0
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
529 _NET_WM_DESKTOP
, XCB_GET_PROPERTY_TYPE_ANY
, 0, 1);
531 c1
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
532 _NET_WM_STATE
, ATOM
, 0, UINT32_MAX
);
534 c2
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
535 _NET_WM_WINDOW_TYPE
, ATOM
, 0, UINT32_MAX
);
537 reply
= xcb_get_property_reply(globalconf
.connection
, c0
, NULL
);
538 if(reply
&& reply
->value_len
&& (data
= xcb_get_property_value(reply
)))
540 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
542 desktop
= *(uint32_t *) data
;
546 for(int i
= 0; i
< tags
->len
; i
++)
548 tag_client(c
, tags
->tab
[i
]);
550 untag_client(c
, tags
->tab
[i
]);
555 reply
= xcb_get_property_reply(globalconf
.connection
, c1
, NULL
);
556 if(reply
&& (data
= xcb_get_property_value(reply
)))
558 state
= (xcb_atom_t
*) data
;
559 for(int i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
560 ewmh_process_state_atom(c
, state
[i
], _NET_WM_STATE_ADD
);
565 reply
= xcb_get_property_reply(globalconf
.connection
, c2
, NULL
);
566 if(reply
&& (data
= xcb_get_property_value(reply
)))
568 state
= (xcb_atom_t
*) data
;
569 for(int i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
570 if(state
[i
] == _NET_WM_WINDOW_TYPE_DESKTOP
)
571 c
->type
= MAX(c
->type
, WINDOW_TYPE_DESKTOP
);
572 else if(state
[i
] == _NET_WM_WINDOW_TYPE_DIALOG
)
573 c
->type
= MAX(c
->type
, WINDOW_TYPE_DIALOG
);
574 else if(state
[i
] == _NET_WM_WINDOW_TYPE_SPLASH
)
575 c
->type
= MAX(c
->type
, WINDOW_TYPE_SPLASH
);
576 else if(state
[i
] == _NET_WM_WINDOW_TYPE_DOCK
)
577 c
->type
= MAX(c
->type
, WINDOW_TYPE_DOCK
);
578 else if(state
[i
] == _NET_WM_WINDOW_TYPE_MENU
)
579 c
->type
= MAX(c
->type
, WINDOW_TYPE_MENU
);
580 else if(state
[i
] == _NET_WM_WINDOW_TYPE_TOOLBAR
)
581 c
->type
= MAX(c
->type
, WINDOW_TYPE_TOOLBAR
);
582 else if(state
[i
] == _NET_WM_WINDOW_TYPE_UTILITY
)
583 c
->type
= MAX(c
->type
, WINDOW_TYPE_UTILITY
);
589 /** Process the WM strut of a client.
590 * \param c The client.
593 ewmh_process_client_strut(client_t
*c
, xcb_get_property_reply_t
*strut_r
)
596 xcb_get_property_reply_t
*mstrut_r
= NULL
;
600 xcb_get_property_cookie_t strut_q
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
601 _NET_WM_STRUT_PARTIAL
, CARDINAL
, 0, 12);
602 strut_r
= mstrut_r
= xcb_get_property_reply(globalconf
.connection
, strut_q
, NULL
);
606 && strut_r
->value_len
607 && (data
= xcb_get_property_value(strut_r
)))
609 uint32_t *strut
= data
;
611 if(c
->strut
.left
!= strut
[0]
612 || c
->strut
.right
!= strut
[1]
613 || c
->strut
.top
!= strut
[2]
614 || c
->strut
.bottom
!= strut
[3]
615 || c
->strut
.left_start_y
!= strut
[4]
616 || c
->strut
.left_end_y
!= strut
[5]
617 || c
->strut
.right_start_y
!= strut
[6]
618 || c
->strut
.right_end_y
!= strut
[7]
619 || c
->strut
.top_start_x
!= strut
[8]
620 || c
->strut
.top_end_x
!= strut
[9]
621 || c
->strut
.bottom_start_x
!= strut
[10]
622 || c
->strut
.bottom_end_x
!= strut
[11])
624 c
->strut
.left
= strut
[0];
625 c
->strut
.right
= strut
[1];
626 c
->strut
.top
= strut
[2];
627 c
->strut
.bottom
= strut
[3];
628 c
->strut
.left_start_y
= strut
[4];
629 c
->strut
.left_end_y
= strut
[5];
630 c
->strut
.right_start_y
= strut
[6];
631 c
->strut
.right_end_y
= strut
[7];
632 c
->strut
.top_start_x
= strut
[8];
633 c
->strut
.top_end_x
= strut
[9];
634 c
->strut
.bottom_start_x
= strut
[10];
635 c
->strut
.bottom_end_x
= strut
[11];
637 client_need_arrange(c
);
638 /* All the wiboxes (may) need to be repositioned. */
639 wibox_update_positions();
646 /** Send request to get NET_WM_ICON (EWMH)
647 * \param w The window.
648 * \return The cookie associated with the request.
650 xcb_get_property_cookie_t
651 ewmh_window_icon_get_unchecked(xcb_window_t w
)
653 return xcb_get_property_unchecked(globalconf
.connection
, false, w
,
654 _NET_WM_ICON
, CARDINAL
, 0, UINT32_MAX
);
658 ewmh_window_icon_from_reply(xcb_get_property_reply_t
*r
)
662 if(!r
|| r
->type
!= CARDINAL
|| r
->format
!= 32 || r
->length
< 2 ||
663 !(data
= (uint32_t *) xcb_get_property_value(r
)))
666 if(data
[0] && data
[1])
667 return image_new_from_argb32(data
[0], data
[1], data
+ 2);
673 * \param cookie The cookie.
674 * \return A draw_image_t structure which must be deleted after usage.
677 ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie
)
679 xcb_get_property_reply_t
*r
= xcb_get_property_reply(globalconf
.connection
, cookie
, NULL
);
680 image_t
*icon
= ewmh_window_icon_from_reply(r
);
685 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80