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>
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
45 ewmh_init(int phys_screen
)
48 xcb_screen_t
*xscreen
= xutil_screen_get(globalconf
.connection
, phys_screen
);
52 _NET_SUPPORTING_WM_CHECK
,
54 _NET_CLIENT_LIST_STACKING
,
55 _NET_NUMBER_OF_DESKTOPS
,
62 _NET_WM_STRUT_PARTIAL
,
64 _NET_WM_VISIBLE_ICON_NAME
,
67 _NET_WM_WINDOW_TYPE_DESKTOP
,
68 _NET_WM_WINDOW_TYPE_DOCK
,
69 _NET_WM_WINDOW_TYPE_TOOLBAR
,
70 _NET_WM_WINDOW_TYPE_MENU
,
71 _NET_WM_WINDOW_TYPE_UTILITY
,
72 _NET_WM_WINDOW_TYPE_SPLASH
,
73 _NET_WM_WINDOW_TYPE_DIALOG
,
74 _NET_WM_WINDOW_TYPE_NORMAL
,
79 _NET_WM_STATE_SKIP_TASKBAR
,
80 _NET_WM_STATE_FULLSCREEN
,
81 _NET_WM_STATE_MAXIMIZED_HORZ
,
82 _NET_WM_STATE_MAXIMIZED_VERT
,
87 _NET_WM_STATE_DEMANDS_ATTENTION
91 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
92 xscreen
->root
, _NET_SUPPORTED
, ATOM
, 32,
95 /* create our own window */
96 father
= xcb_generate_id(globalconf
.connection
);
97 xcb_create_window(globalconf
.connection
, xscreen
->root_depth
,
98 father
, xscreen
->root
, -1, -1, 1, 1, 0,
99 XCB_COPY_FROM_PARENT
, xscreen
->root_visual
, 0, NULL
);
101 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
102 xscreen
->root
, _NET_SUPPORTING_WM_CHECK
, WINDOW
, 32,
105 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
106 father
, _NET_SUPPORTING_WM_CHECK
, WINDOW
, 32,
109 /* set the window manager name */
110 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
111 father
, _NET_WM_NAME
, UTF8_STRING
, 8, 7, "awesome");
113 /* set the window manager PID */
115 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
116 father
, _NET_WM_PID
, CARDINAL
, 32, 1, &i
);
120 ewmh_update_net_client_list(int phys_screen
)
126 for(c
= globalconf
.clients
; c
; c
= c
->next
)
129 wins
= p_alloca(xcb_window_t
, n
);
131 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
, n
++)
132 if(c
->phys_screen
== phys_screen
)
135 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
136 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
137 _NET_CLIENT_LIST
, WINDOW
, 32, n
, wins
);
140 /** Set the client list in stacking order, bottom to top.
141 * \param phys_screen The physical screen id.
144 ewmh_update_net_client_list_stacking(int phys_screen
)
150 for(c
= globalconf
.stack
; c
; c
= c
->next
)
153 wins
= p_alloca(xcb_window_t
, n
);
155 for(n
= 0, c
= *client_node_list_last(&globalconf
.stack
); c
; c
= c
->prev
, n
++)
156 if(c
->client
->phys_screen
== phys_screen
)
157 wins
[n
] = c
->client
->win
;
159 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
160 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
161 _NET_CLIENT_LIST_STACKING
, WINDOW
, 32, n
, wins
);
165 ewmh_update_net_numbers_of_desktop(int phys_screen
)
167 uint32_t count
= globalconf
.screens
[phys_screen
].tags
.len
;
169 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
170 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
171 _NET_NUMBER_OF_DESKTOPS
, CARDINAL
, 32, 1, &count
);
175 ewmh_update_net_current_desktop(int phys_screen
)
177 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
179 tag_t
**curtags
= tags_get_current(phys_screen
);
181 while(count
< (uint32_t) tags
->len
&& tags
->tab
[count
] != curtags
[0])
184 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
185 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
186 _NET_CURRENT_DESKTOP
, CARDINAL
, 32, 1, &count
);
192 ewmh_update_net_desktop_names(int phys_screen
)
194 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
197 buffer_inita(&buf
, BUFSIZ
);
199 for(int i
= 0; i
< tags
->len
; i
++)
201 buffer_adds(&buf
, tags
->tab
[i
]->name
);
202 buffer_addc(&buf
, '\0');
205 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
206 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
207 _NET_DESKTOP_NAMES
, UTF8_STRING
, 8, buf
.len
, buf
.s
);
211 /** Update the work area space for each physical screen and each desktop.
212 * \param phys_screen The physical screen id.
215 ewmh_update_workarea(int phys_screen
)
217 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
218 uint32_t *area
= p_alloca(uint32_t, tags
->len
* 4);
219 area_t geom
= screen_area_get(phys_screen
,
220 &globalconf
.screens
[phys_screen
].wiboxes
,
221 &globalconf
.screens
[phys_screen
].padding
,
225 for(int i
= 0; i
< tags
->len
; i
++)
227 area
[4 * i
+ 0] = geom
.x
;
228 area
[4 * i
+ 1] = geom
.y
;
229 area
[4 * i
+ 2] = geom
.width
;
230 area
[4 * i
+ 3] = geom
.height
;
233 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
234 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
235 _NET_WORKAREA
, CARDINAL
, 32, tags
->len
* 4, area
);
239 ewmh_update_net_active_window(int phys_screen
)
243 if(globalconf
.screen_focus
->client_focus
244 && globalconf
.screen_focus
->client_focus
->phys_screen
== phys_screen
)
245 win
= globalconf
.screen_focus
->client_focus
->win
;
249 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
250 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
251 _NET_ACTIVE_WINDOW
, WINDOW
, 32, 1, &win
);
255 ewmh_process_state_atom(client_t
*c
, xcb_atom_t state
, int set
)
257 if(state
== _NET_WM_STATE_STICKY
)
259 if(set
== _NET_WM_STATE_REMOVE
)
260 client_setsticky(c
, false);
261 else if(set
== _NET_WM_STATE_ADD
)
262 client_setsticky(c
, true);
263 else if(set
== _NET_WM_STATE_TOGGLE
)
264 client_setsticky(c
, !c
->issticky
);
266 else if(state
== _NET_WM_STATE_SKIP_TASKBAR
)
268 if(set
== _NET_WM_STATE_REMOVE
)
271 ewmh_client_update_hints(c
);
273 else if(set
== _NET_WM_STATE_ADD
)
276 ewmh_client_update_hints(c
);
278 else if(set
== _NET_WM_STATE_TOGGLE
)
280 c
->skiptb
= !c
->skiptb
;
281 ewmh_client_update_hints(c
);
284 else if(state
== _NET_WM_STATE_FULLSCREEN
)
286 if(set
== _NET_WM_STATE_REMOVE
)
287 client_setfullscreen(c
, false);
288 else if(set
== _NET_WM_STATE_ADD
)
289 client_setfullscreen(c
, true);
290 else if(set
== _NET_WM_STATE_TOGGLE
)
291 client_setfullscreen(c
, !c
->isfullscreen
);
293 else if(state
== _NET_WM_STATE_MAXIMIZED_HORZ
)
295 if(set
== _NET_WM_STATE_REMOVE
)
296 client_setmaxhoriz(c
, false);
297 else if(set
== _NET_WM_STATE_ADD
)
298 client_setmaxhoriz(c
, true);
299 else if(set
== _NET_WM_STATE_TOGGLE
)
300 client_setmaxhoriz(c
, !c
->ismaxhoriz
);
302 else if(state
== _NET_WM_STATE_MAXIMIZED_VERT
)
304 if(set
== _NET_WM_STATE_REMOVE
)
305 client_setmaxvert(c
, false);
306 else if(set
== _NET_WM_STATE_ADD
)
307 client_setmaxvert(c
, true);
308 else if(set
== _NET_WM_STATE_TOGGLE
)
309 client_setmaxvert(c
, !c
->ismaxvert
);
311 else if(state
== _NET_WM_STATE_ABOVE
)
313 if(set
== _NET_WM_STATE_REMOVE
)
314 client_setabove(c
, false);
315 else if(set
== _NET_WM_STATE_ADD
)
316 client_setabove(c
, true);
317 else if(set
== _NET_WM_STATE_TOGGLE
)
318 client_setabove(c
, !c
->isabove
);
320 else if(state
== _NET_WM_STATE_BELOW
)
322 if(set
== _NET_WM_STATE_REMOVE
)
323 client_setbelow(c
, false);
324 else if(set
== _NET_WM_STATE_ADD
)
325 client_setbelow(c
, true);
326 else if(set
== _NET_WM_STATE_TOGGLE
)
327 client_setbelow(c
, !c
->isbelow
);
329 else if(state
== _NET_WM_STATE_MODAL
)
331 if(set
== _NET_WM_STATE_REMOVE
)
332 client_setmodal(c
, false);
333 else if(set
== _NET_WM_STATE_ADD
)
334 client_setmodal(c
, true);
335 else if(set
== _NET_WM_STATE_TOGGLE
)
336 client_setmodal(c
, !c
->ismodal
);
338 else if(state
== _NET_WM_STATE_HIDDEN
)
340 if(set
== _NET_WM_STATE_REMOVE
)
341 client_setminimized(c
, false);
342 else if(set
== _NET_WM_STATE_ADD
)
343 client_setminimized(c
, true);
344 else if(set
== _NET_WM_STATE_TOGGLE
)
345 client_setminimized(c
, !c
->isminimized
);
347 else if(state
== _NET_WM_STATE_DEMANDS_ATTENTION
)
349 if(set
== _NET_WM_STATE_REMOVE
)
351 else if(set
== _NET_WM_STATE_ADD
)
353 else if(set
== _NET_WM_STATE_TOGGLE
)
354 c
->isurgent
= !c
->isurgent
;
357 hooks_property(c
, "urgent");
362 ewmh_process_client_message(xcb_client_message_event_t
*ev
)
367 if(ev
->type
== _NET_CURRENT_DESKTOP
)
369 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
372 if(ev
->window
== xutil_screen_get(globalconf
.connection
, screen
)->root
)
373 tag_view_only_byindex(screen
, ev
->data
.data32
[0]);
375 else if(ev
->type
== _NET_CLOSE_WINDOW
)
377 if((c
= client_getbywin(ev
->window
)))
380 else if(ev
->type
== _NET_WM_DESKTOP
)
382 if((c
= client_getbywin(ev
->window
)))
384 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
386 if(ev
->data
.data32
[0] == 0xffffffff)
389 for(int i
= 0; i
< tags
->len
; i
++)
390 if((int)ev
->data
.data32
[0] == i
)
391 tag_client(c
, tags
->tab
[i
]);
393 untag_client(c
, tags
->tab
[i
]);
396 else if(ev
->type
== _NET_WM_STATE
)
398 if((c
= client_getbywin(ev
->window
)))
400 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[1], ev
->data
.data32
[0]);
401 if(ev
->data
.data32
[2])
402 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[2],
410 /** Update client EWMH hints.
411 * \param c The client.
414 ewmh_client_update_hints(client_t
*c
)
416 xcb_atom_t state
[8]; /* number of defined state atoms */
420 state
[i
++] = _NET_WM_STATE_MODAL
;
422 state
[i
++] = _NET_WM_STATE_FULLSCREEN
;
424 state
[i
++] = _NET_WM_STATE_MAXIMIZED_VERT
;
426 state
[i
++] = _NET_WM_STATE_MAXIMIZED_HORZ
;
428 state
[i
++] = _NET_WM_STATE_STICKY
;
430 state
[i
++] = _NET_WM_STATE_SKIP_TASKBAR
;
432 state
[i
++] = _NET_WM_STATE_ABOVE
;
434 state
[i
++] = _NET_WM_STATE_BELOW
;
436 state
[i
++] = _NET_WM_STATE_HIDDEN
;
438 state
[i
++] = _NET_WM_STATE_DEMANDS_ATTENTION
;
440 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
441 c
->win
, _NET_WM_STATE
, ATOM
, 32, i
, state
);
445 ewmh_client_check_hints(client_t
*c
)
450 xcb_get_property_cookie_t c0
, c1
, c2
;
451 xcb_get_property_reply_t
*reply
;
453 /* Send the GetProperty requests which will be processed later */
454 c0
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
455 _NET_WM_DESKTOP
, XCB_GET_PROPERTY_TYPE_ANY
, 0, 1);
457 c1
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
458 _NET_WM_STATE
, ATOM
, 0, UINT32_MAX
);
460 c2
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
461 _NET_WM_WINDOW_TYPE
, ATOM
, 0, UINT32_MAX
);
463 reply
= xcb_get_property_reply(globalconf
.connection
, c0
, NULL
);
464 if(reply
&& reply
->value_len
&& (data
= xcb_get_property_value(reply
)))
466 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
468 desktop
= *(uint32_t *) data
;
472 for(int i
= 0; i
< tags
->len
; i
++)
474 tag_client(c
, tags
->tab
[i
]);
476 untag_client(c
, tags
->tab
[i
]);
481 reply
= xcb_get_property_reply(globalconf
.connection
, c1
, NULL
);
482 if(reply
&& (data
= xcb_get_property_value(reply
)))
484 state
= (xcb_atom_t
*) data
;
485 for(int i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
486 ewmh_process_state_atom(c
, state
[i
], _NET_WM_STATE_ADD
);
491 reply
= xcb_get_property_reply(globalconf
.connection
, c2
, NULL
);
492 if(reply
&& (data
= xcb_get_property_value(reply
)))
494 state
= (xcb_atom_t
*) data
;
495 for(int i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
496 if(state
[i
] == _NET_WM_WINDOW_TYPE_DESKTOP
)
497 c
->type
= MAX(c
->type
, WINDOW_TYPE_DESKTOP
);
498 else if(state
[i
] == _NET_WM_WINDOW_TYPE_DIALOG
)
499 c
->type
= MAX(c
->type
, WINDOW_TYPE_DIALOG
);
500 else if(state
[i
] == _NET_WM_WINDOW_TYPE_SPLASH
)
501 c
->type
= MAX(c
->type
, WINDOW_TYPE_SPLASH
);
502 else if(state
[i
] == _NET_WM_WINDOW_TYPE_DOCK
)
503 c
->type
= MAX(c
->type
, WINDOW_TYPE_DOCK
);
504 else if(state
[i
] == _NET_WM_WINDOW_TYPE_MENU
)
505 c
->type
= MAX(c
->type
, WINDOW_TYPE_MENU
);
506 else if(state
[i
] == _NET_WM_WINDOW_TYPE_TOOLBAR
)
507 c
->type
= MAX(c
->type
, WINDOW_TYPE_TOOLBAR
);
508 else if(state
[i
] == _NET_WM_WINDOW_TYPE_UTILITY
)
509 c
->type
= MAX(c
->type
, WINDOW_TYPE_UTILITY
);
515 /** Update the WM strut of a client.
516 * \param c The client.
519 ewmh_client_strut_update(client_t
*c
, xcb_get_property_reply_t
*strut_r
)
522 xcb_get_property_reply_t
*mstrut_r
= NULL
;
526 xcb_get_property_cookie_t strut_q
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
527 _NET_WM_STRUT_PARTIAL
, CARDINAL
, 0, 12);
528 strut_r
= mstrut_r
= xcb_get_property_reply(globalconf
.connection
, strut_q
, NULL
);
532 && strut_r
->value_len
533 && (data
= xcb_get_property_value(strut_r
)))
535 uint32_t *strut
= data
;
537 if(c
->strut
.left
!= strut
[0]
538 || c
->strut
.right
!= strut
[1]
539 || c
->strut
.top
!= strut
[2]
540 || c
->strut
.bottom
!= strut
[3]
541 || c
->strut
.left_start_y
!= strut
[4]
542 || c
->strut
.left_end_y
!= strut
[5]
543 || c
->strut
.right_start_y
!= strut
[6]
544 || c
->strut
.right_end_y
!= strut
[7]
545 || c
->strut
.top_start_x
!= strut
[8]
546 || c
->strut
.top_end_x
!= strut
[9]
547 || c
->strut
.bottom_start_x
!= strut
[10]
548 || c
->strut
.bottom_end_x
!= strut
[11])
550 c
->strut
.left
= strut
[0];
551 c
->strut
.right
= strut
[1];
552 c
->strut
.top
= strut
[2];
553 c
->strut
.bottom
= strut
[3];
554 c
->strut
.left_start_y
= strut
[4];
555 c
->strut
.left_end_y
= strut
[5];
556 c
->strut
.right_start_y
= strut
[6];
557 c
->strut
.right_end_y
= strut
[7];
558 c
->strut
.top_start_x
= strut
[8];
559 c
->strut
.top_end_x
= strut
[9];
560 c
->strut
.bottom_start_x
= strut
[10];
561 c
->strut
.bottom_end_x
= strut
[11];
563 client_need_arrange(c
);
564 /* All the wiboxes (may) need to be repositioned */
565 for(int screen
= 0; screen
< globalconf
.nscreen
; screen
++)
566 for(int i
= 0; i
< globalconf
.screens
[screen
].wiboxes
.len
; i
++)
568 wibox_t
*s
= globalconf
.screens
[screen
].wiboxes
.tab
[i
];
569 wibox_position_update(s
);
577 /** Send request to get NET_WM_ICON (EWMH)
578 * \param w The window.
579 * \return The cookie associated with the request.
581 xcb_get_property_cookie_t
582 ewmh_window_icon_get_unchecked(xcb_window_t w
)
584 return xcb_get_property_unchecked(globalconf
.connection
, false, w
,
585 _NET_WM_ICON
, CARDINAL
, 0, UINT32_MAX
);
589 ewmh_window_icon_from_reply(xcb_get_property_reply_t
*r
)
593 if(!r
|| r
->type
!= CARDINAL
|| r
->format
!= 32 || r
->length
< 2 ||
594 !(data
= (uint32_t *) xcb_get_property_value(r
)))
597 if(data
[0] && data
[1])
598 return image_new_from_argb32(data
[0], data
[1], data
+ 2);
604 * \param cookie The cookie.
605 * \return A draw_image_t structure which must be deleted after usage.
608 ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie
)
610 xcb_get_property_reply_t
*r
= xcb_get_property_reply(globalconf
.connection
, cookie
, NULL
);
611 image_t
*icon
= ewmh_window_icon_from_reply(r
);
616 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80