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"
37 extern awesome_t globalconf
;
39 #define _NET_WM_STATE_REMOVE 0
40 #define _NET_WM_STATE_ADD 1
41 #define _NET_WM_STATE_TOGGLE 2
44 ewmh_init(int phys_screen
)
47 xcb_screen_t
*xscreen
= xutil_screen_get(globalconf
.connection
, phys_screen
);
51 _NET_SUPPORTING_WM_CHECK
,
53 _NET_CLIENT_LIST_STACKING
,
54 _NET_NUMBER_OF_DESKTOPS
,
62 _NET_WM_VISIBLE_ICON_NAME
,
65 _NET_WM_WINDOW_TYPE_NORMAL
,
66 _NET_WM_WINDOW_TYPE_DESKTOP
,
67 _NET_WM_WINDOW_TYPE_DOCK
,
68 _NET_WM_WINDOW_TYPE_SPLASH
,
69 _NET_WM_WINDOW_TYPE_DIALOG
,
74 _NET_WM_STATE_SKIP_TASKBAR
,
75 _NET_WM_STATE_FULLSCREEN
,
80 _NET_WM_STATE_DEMANDS_ATTENTION
84 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
85 xscreen
->root
, _NET_SUPPORTED
, ATOM
, 32,
88 /* create our own window */
89 father
= xcb_generate_id(globalconf
.connection
);
90 xcb_create_window(globalconf
.connection
, xscreen
->root_depth
,
91 father
, xscreen
->root
, -1, -1, 1, 1, 0,
92 XCB_COPY_FROM_PARENT
, xscreen
->root_visual
, 0, NULL
);
94 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
95 xscreen
->root
, _NET_SUPPORTING_WM_CHECK
, WINDOW
, 32,
98 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
99 father
, _NET_SUPPORTING_WM_CHECK
, WINDOW
, 32,
102 /* set the window manager name */
103 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
104 father
, _NET_WM_NAME
, UTF8_STRING
, 8, 7, "awesome");
106 /* set the window manager PID */
108 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
109 father
, _NET_WM_PID
, CARDINAL
, 32, 1, &i
);
113 ewmh_update_net_client_list(int phys_screen
)
119 for(c
= globalconf
.clients
; c
; c
= c
->next
)
122 wins
= p_new(xcb_window_t
, n
);
124 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
, n
++)
125 if(c
->phys_screen
== phys_screen
)
128 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
129 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
130 _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.
139 ewmh_update_net_client_list_stacking(int phys_screen
)
145 for(c
= globalconf
.stack
; c
; c
= c
->next
)
148 wins
= p_new(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
);
162 ewmh_update_net_numbers_of_desktop(int phys_screen
)
164 uint32_t count
= globalconf
.screens
[phys_screen
].tags
.len
;
166 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
167 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
168 _NET_NUMBER_OF_DESKTOPS
, CARDINAL
, 32, 1, &count
);
172 ewmh_update_net_current_desktop(int phys_screen
)
174 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
176 tag_t
**curtags
= tags_get_current(phys_screen
);
178 while(count
< (uint32_t) tags
->len
&& tags
->tab
[count
] != curtags
[0])
181 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
182 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
183 _NET_CURRENT_DESKTOP
, CARDINAL
, 32, 1, &count
);
189 ewmh_update_net_desktop_names(int phys_screen
)
191 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
194 buffer_inita(&buf
, BUFSIZ
);
196 for(int i
= 0; i
< tags
->len
; i
++)
198 buffer_adds(&buf
, tags
->tab
[i
]->name
);
199 buffer_addc(&buf
, '\0');
202 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
203 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
204 _NET_DESKTOP_NAMES
, UTF8_STRING
, 8, buf
.len
, buf
.s
);
208 /** Update the work area space for each physical screen and each desktop.
209 * \param phys_screen The physical screen id.
212 ewmh_update_workarea(int phys_screen
)
214 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
215 uint32_t *area
= p_alloca(uint32_t, tags
->len
* 4);
216 area_t geom
= screen_area_get(&globalconf
.screens
[phys_screen
].geometry
,
217 globalconf
.screens
[phys_screen
].statusbar
,
218 &globalconf
.screens
[phys_screen
].padding
);
221 for(int i
= 0; i
< tags
->len
; i
++)
223 area
[4 * i
+ 0] = geom
.x
;
224 area
[4 * i
+ 1] = geom
.y
;
225 area
[4 * i
+ 2] = geom
.width
;
226 area
[4 * i
+ 3] = geom
.height
;
229 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
230 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
231 _NET_WORKAREA
, CARDINAL
, 32, tags
->len
* 4, area
);
235 ewmh_update_net_active_window(int phys_screen
)
239 if(globalconf
.screen_focus
->client_focus
240 && globalconf
.screen_focus
->client_focus
->phys_screen
== phys_screen
)
241 win
= globalconf
.screen_focus
->client_focus
->win
;
245 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
246 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
247 _NET_ACTIVE_WINDOW
, WINDOW
, 32, 1, &win
);
251 ewmh_process_state_atom(client_t
*c
, xcb_atom_t state
, int set
)
253 if(state
== _NET_WM_STATE_STICKY
)
255 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
256 for(int i
= 0; i
< tags
->len
; i
++)
257 tag_client(c
, tags
->tab
[i
]);
259 else if(state
== _NET_WM_STATE_SKIP_TASKBAR
)
261 if(set
== _NET_WM_STATE_REMOVE
)
266 else if(set
== _NET_WM_STATE_ADD
)
272 else if(state
== _NET_WM_STATE_FULLSCREEN
)
274 area_t geometry
= c
->geometry
;
275 if(set
== _NET_WM_STATE_REMOVE
)
277 /* restore geometry */
278 geometry
= c
->m_geometry
;
279 /* restore borders and titlebar */
280 if(c
->titlebar
&& c
->titlebar
->sw
&& (c
->titlebar
->position
= c
->titlebar
->oldposition
))
281 xcb_map_window(globalconf
.connection
, c
->titlebar
->sw
->window
);
284 client_setlayer(c
, c
->oldlayer
);
285 client_setborder(c
, c
->oldborder
);
286 client_setfloating(c
, c
->wasfloating
);
288 else if(set
== _NET_WM_STATE_ADD
)
290 geometry
= screen_area_get(&globalconf
.screens
[c
->screen
].geometry
,
292 &globalconf
.screens
[c
->screen
].padding
);
294 c
->m_geometry
= c
->geometry
;
295 c
->wasfloating
= c
->isfloating
;
296 /* disable titlebar and borders */
297 if(c
->titlebar
&& c
->titlebar
->sw
&& (c
->titlebar
->oldposition
= c
->titlebar
->position
))
299 xcb_unmap_window(globalconf
.connection
, c
->titlebar
->sw
->window
);
300 c
->titlebar
->position
= Off
;
303 c
->oldborder
= c
->border
;
304 client_setborder(c
, 0);
306 c
->oldlayer
= c
->layer
;
307 client_setlayer(c
, LAYER_FULLSCREEN
);
308 client_setfloating(c
, true);
310 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
311 client_resize(c
, geometry
, false);
313 else if(state
== _NET_WM_STATE_ABOVE
)
315 if(set
== _NET_WM_STATE_REMOVE
)
316 client_setlayer(c
, c
->oldlayer
);
317 else if(set
== _NET_WM_STATE_ADD
)
319 c
->oldlayer
= c
->layer
;
320 client_setlayer(c
, LAYER_ABOVE
);
323 else if(state
== _NET_WM_STATE_BELOW
)
325 if(set
== _NET_WM_STATE_REMOVE
)
326 client_setlayer(c
, c
->oldlayer
);
327 else if(set
== _NET_WM_STATE_ADD
)
329 c
->oldlayer
= c
->layer
;
330 client_setlayer(c
, LAYER_BELOW
);
333 else if(state
== _NET_WM_STATE_MODAL
)
335 if(set
== _NET_WM_STATE_REMOVE
)
336 client_setlayer(c
, c
->oldlayer
);
337 else if(set
== _NET_WM_STATE_ADD
)
339 c
->oldlayer
= c
->layer
;
340 client_setlayer(c
, LAYER_MODAL
);
343 else if(state
== _NET_WM_STATE_HIDDEN
)
345 if(set
== _NET_WM_STATE_REMOVE
)
347 else if(set
== _NET_WM_STATE_ADD
)
349 globalconf
.screens
[c
->screen
].need_arrange
= true;
351 else if(state
== _NET_WM_STATE_DEMANDS_ATTENTION
)
353 if(set
== _NET_WM_STATE_REMOVE
)
355 else if(set
== _NET_WM_STATE_ADD
)
359 luaA_client_userdata_new(globalconf
.L
, c
);
360 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.urgent
, 1, 0);
361 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
367 ewmh_process_window_type_atom(client_t
*c
, xcb_atom_t state
)
369 if(state
== _NET_WM_WINDOW_TYPE_NORMAL
)
371 /* do nothing. this is REALLY IMPORTANT */
373 else if(state
== _NET_WM_WINDOW_TYPE_DOCK
374 || state
== _NET_WM_WINDOW_TYPE_SPLASH
)
378 if(c
->titlebar
&& c
->titlebar
->position
&& c
->titlebar
->sw
)
380 xcb_unmap_window(globalconf
.connection
, c
->titlebar
->sw
->window
);
381 c
->titlebar
->position
= Off
;
383 client_setborder(c
, 0);
385 client_setlayer(c
, LAYER_ABOVE
);
386 client_setfloating(c
, true);
388 else if(state
== _NET_WM_WINDOW_TYPE_DIALOG
)
390 client_setlayer(c
, LAYER_MODAL
);
391 client_setfloating(c
, true);
393 else if(state
== _NET_WM_WINDOW_TYPE_DESKTOP
)
395 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
399 client_setlayer(c
, LAYER_DESKTOP
);
400 for(int i
= 0; i
< tags
->len
; i
++)
401 tag_client(c
, tags
->tab
[i
]);
406 ewmh_process_client_message(xcb_client_message_event_t
*ev
)
411 if(ev
->type
== _NET_CURRENT_DESKTOP
)
413 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
416 if(ev
->window
== xutil_screen_get(globalconf
.connection
, screen
)->root
)
417 tag_view_only_byindex(screen
, ev
->data
.data32
[0]);
419 else if(ev
->type
== _NET_CLOSE_WINDOW
)
421 if((c
= client_getbywin(ev
->window
)))
424 else if(ev
->type
== _NET_WM_DESKTOP
)
426 if((c
= client_getbywin(ev
->window
)))
428 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
430 if(ev
->data
.data32
[0] == 0xffffffff)
431 for(int i
= 0; i
< tags
->len
; i
++)
432 tag_client(c
, tags
->tab
[i
]);
434 for(int i
= 0; i
< tags
->len
; i
++)
435 if((int)ev
->data
.data32
[0] == i
)
436 tag_client(c
, tags
->tab
[i
]);
438 untag_client(c
, tags
->tab
[i
]);
441 else if(ev
->type
== _NET_WM_STATE
)
443 if((c
= client_getbywin(ev
->window
)))
445 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[1], ev
->data
.data32
[0]);
446 if(ev
->data
.data32
[2])
447 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[2],
456 ewmh_check_client_hints(client_t
*c
)
461 xcb_get_property_cookie_t c0
, c1
, c2
;
462 xcb_get_property_reply_t
*reply
;
464 /* Send the GetProperty requests which will be processed later */
465 c0
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
466 _NET_WM_DESKTOP
, XCB_GET_PROPERTY_TYPE_ANY
, 0, 1);
468 c1
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
469 _NET_WM_STATE
, ATOM
, 0, UINT32_MAX
);
471 c2
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
472 _NET_WM_WINDOW_TYPE
, ATOM
, 0, UINT32_MAX
);
474 reply
= xcb_get_property_reply(globalconf
.connection
, c0
, NULL
);
475 if(reply
&& reply
->value_len
&& (data
= xcb_get_property_value(reply
)))
477 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
479 desktop
= *(uint32_t *) data
;
481 for(int i
= 0; i
< tags
->len
; i
++)
482 tag_client(c
, tags
->tab
[i
]);
484 for(int i
= 0; i
< tags
->len
; i
++)
486 tag_client(c
, tags
->tab
[i
]);
488 untag_client(c
, tags
->tab
[i
]);
493 reply
= xcb_get_property_reply(globalconf
.connection
, c1
, NULL
);
494 if(reply
&& (data
= xcb_get_property_value(reply
)))
496 state
= (xcb_atom_t
*) data
;
497 for(int i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
498 ewmh_process_state_atom(c
, state
[i
], _NET_WM_STATE_ADD
);
503 reply
= xcb_get_property_reply(globalconf
.connection
, c2
, NULL
);
504 if(reply
&& (data
= xcb_get_property_value(reply
)))
506 state
= (xcb_atom_t
*) data
;
507 for(int i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
508 ewmh_process_window_type_atom(c
, state
[i
]);
515 * \param w The window.
516 * \return A netwm_icon_t structure which must be deleted after usage.
519 ewmh_window_icon_get(xcb_window_t w
)
525 unsigned char *imgdata
;
526 xcb_get_property_reply_t
*r
;
528 r
= xcb_get_property_reply(globalconf
.connection
,
529 xcb_get_property_unchecked(globalconf
.connection
, false, w
,
530 _NET_WM_ICON
, CARDINAL
, 0, UINT32_MAX
),
532 if(!r
|| r
->type
!= CARDINAL
|| r
->format
!= 32 || r
->length
< 2 ||
533 !(data
= (uint32_t *) xcb_get_property_value(r
)))
539 icon
= p_new(netwm_icon_t
, 1);
541 icon
->width
= data
[0];
542 icon
->height
= data
[1];
543 size
= icon
->width
* icon
->height
;
552 icon
->image
= p_new(unsigned char, size
* 4);
553 for(imgdata
= icon
->image
, i
= 2; i
< size
+ 2; i
++, imgdata
+= 4)
555 imgdata
[3] = (data
[i
] >> 24) & 0xff; /* A */
556 alpha
= imgdata
[3] / 255.0;
557 imgdata
[2] = ((data
[i
] >> 16) & 0xff) * alpha
; /* R */
558 imgdata
[1] = ((data
[i
] >> 8) & 0xff) * alpha
; /* G */
559 imgdata
[0] = (data
[i
] & 0xff) * alpha
; /* B */
575 for(c
= globalconf
.clients
; c
; c
= c
->next
)
578 xcb_aux_sync(globalconf
.connection
);
579 xcb_disconnect(globalconf
.connection
);
581 a_exec(globalconf
.argv
);
584 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80