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 "statusbar.h"
36 #include "common/atoms.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_NORMAL
,
68 _NET_WM_WINDOW_TYPE_DESKTOP
,
69 _NET_WM_WINDOW_TYPE_DOCK
,
70 _NET_WM_WINDOW_TYPE_SPLASH
,
71 _NET_WM_WINDOW_TYPE_DIALOG
,
76 _NET_WM_STATE_SKIP_TASKBAR
,
77 _NET_WM_STATE_FULLSCREEN
,
82 _NET_WM_STATE_DEMANDS_ATTENTION
86 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
87 xscreen
->root
, _NET_SUPPORTED
, ATOM
, 32,
90 /* create our own window */
91 father
= xcb_generate_id(globalconf
.connection
);
92 xcb_create_window(globalconf
.connection
, xscreen
->root_depth
,
93 father
, xscreen
->root
, -1, -1, 1, 1, 0,
94 XCB_COPY_FROM_PARENT
, xscreen
->root_visual
, 0, NULL
);
96 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
97 xscreen
->root
, _NET_SUPPORTING_WM_CHECK
, WINDOW
, 32,
100 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
101 father
, _NET_SUPPORTING_WM_CHECK
, WINDOW
, 32,
104 /* set the window manager name */
105 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
106 father
, _NET_WM_NAME
, UTF8_STRING
, 8, 7, "awesome");
108 /* set the window manager PID */
110 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
111 father
, _NET_WM_PID
, CARDINAL
, 32, 1, &i
);
115 ewmh_update_net_client_list(int phys_screen
)
121 for(c
= globalconf
.clients
; c
; c
= c
->next
)
124 wins
= p_alloca(xcb_window_t
, n
);
126 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
, n
++)
127 if(c
->phys_screen
== phys_screen
)
130 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
131 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
132 _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_alloca(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
);
160 ewmh_update_net_numbers_of_desktop(int phys_screen
)
162 uint32_t count
= globalconf
.screens
[phys_screen
].tags
.len
;
164 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
165 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
166 _NET_NUMBER_OF_DESKTOPS
, CARDINAL
, 32, 1, &count
);
170 ewmh_update_net_current_desktop(int phys_screen
)
172 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
174 tag_t
**curtags
= tags_get_current(phys_screen
);
176 while(count
< (uint32_t) tags
->len
&& tags
->tab
[count
] != curtags
[0])
179 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
180 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
181 _NET_CURRENT_DESKTOP
, CARDINAL
, 32, 1, &count
);
187 ewmh_update_net_desktop_names(int phys_screen
)
189 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
192 buffer_inita(&buf
, BUFSIZ
);
194 for(int i
= 0; i
< tags
->len
; i
++)
196 buffer_adds(&buf
, tags
->tab
[i
]->name
);
197 buffer_addc(&buf
, '\0');
200 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
201 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
202 _NET_DESKTOP_NAMES
, UTF8_STRING
, 8, buf
.len
, buf
.s
);
206 /** Update the work area space for each physical screen and each desktop.
207 * \param phys_screen The physical screen id.
210 ewmh_update_workarea(int phys_screen
)
212 tag_array_t
*tags
= &globalconf
.screens
[phys_screen
].tags
;
213 uint32_t *area
= p_alloca(uint32_t, tags
->len
* 4);
214 area_t geom
= screen_area_get(phys_screen
,
215 globalconf
.screens
[phys_screen
].statusbar
,
216 &globalconf
.screens
[phys_screen
].padding
,
220 for(int i
= 0; i
< tags
->len
; i
++)
222 area
[4 * i
+ 0] = geom
.x
;
223 area
[4 * i
+ 1] = geom
.y
;
224 area
[4 * i
+ 2] = geom
.width
;
225 area
[4 * i
+ 3] = geom
.height
;
228 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
229 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
230 _NET_WORKAREA
, CARDINAL
, 32, tags
->len
* 4, area
);
234 ewmh_update_net_active_window(int phys_screen
)
238 if(globalconf
.screen_focus
->client_focus
239 && globalconf
.screen_focus
->client_focus
->phys_screen
== phys_screen
)
240 win
= globalconf
.screen_focus
->client_focus
->win
;
244 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
245 xutil_screen_get(globalconf
.connection
, phys_screen
)->root
,
246 _NET_ACTIVE_WINDOW
, WINDOW
, 32, 1, &win
);
250 ewmh_process_state_atom(client_t
*c
, xcb_atom_t state
, int set
)
252 if(state
== _NET_WM_STATE_STICKY
)
254 if(set
== _NET_WM_STATE_REMOVE
)
255 client_setsticky(c
, false);
256 else if(set
== _NET_WM_STATE_ADD
)
257 client_setsticky(c
, true);
259 else if(state
== _NET_WM_STATE_SKIP_TASKBAR
)
261 if(set
== _NET_WM_STATE_REMOVE
)
263 else if(set
== _NET_WM_STATE_ADD
)
266 else if(state
== _NET_WM_STATE_FULLSCREEN
)
268 if(set
== _NET_WM_STATE_REMOVE
)
269 client_setfullscreen(c
, false);
270 else if(set
== _NET_WM_STATE_ADD
)
271 client_setfullscreen(c
, true);
273 else if(state
== _NET_WM_STATE_ABOVE
)
275 if(set
== _NET_WM_STATE_REMOVE
)
276 client_setabove(c
, false);
277 else if(set
== _NET_WM_STATE_ADD
)
278 client_setabove(c
, true);
280 else if(state
== _NET_WM_STATE_BELOW
)
282 if(set
== _NET_WM_STATE_REMOVE
)
283 client_setbelow(c
, false);
284 else if(set
== _NET_WM_STATE_ADD
)
285 client_setbelow(c
, true);
287 else if(state
== _NET_WM_STATE_MODAL
)
289 if(set
== _NET_WM_STATE_REMOVE
)
290 client_setmodal(c
, false);
291 else if(set
== _NET_WM_STATE_ADD
)
292 client_setmodal(c
, true);
294 else if(state
== _NET_WM_STATE_HIDDEN
)
296 if(set
== _NET_WM_STATE_REMOVE
)
298 client_need_arrange(c
);
299 c
->isminimized
= false;
300 client_need_arrange(c
);
302 else if(set
== _NET_WM_STATE_ADD
)
304 client_need_arrange(c
);
305 c
->isminimized
= true;
306 client_need_arrange(c
);
309 else if(state
== _NET_WM_STATE_DEMANDS_ATTENTION
)
311 if(set
== _NET_WM_STATE_REMOVE
)
313 else if(set
== _NET_WM_STATE_ADD
)
317 luaA_client_userdata_new(globalconf
.L
, c
);
318 luaA_dofunction(globalconf
.L
, globalconf
.hooks
.urgent
, 1, 0);
319 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
324 ewmh_process_client_message(xcb_client_message_event_t
*ev
)
329 if(ev
->type
== _NET_CURRENT_DESKTOP
)
331 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
334 if(ev
->window
== xutil_screen_get(globalconf
.connection
, screen
)->root
)
335 tag_view_only_byindex(screen
, ev
->data
.data32
[0]);
337 else if(ev
->type
== _NET_CLOSE_WINDOW
)
339 if((c
= client_getbywin(ev
->window
)))
342 else if(ev
->type
== _NET_WM_DESKTOP
)
344 if((c
= client_getbywin(ev
->window
)))
346 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
348 if(ev
->data
.data32
[0] == 0xffffffff)
351 for(int i
= 0; i
< tags
->len
; i
++)
352 if((int)ev
->data
.data32
[0] == i
)
353 tag_client(c
, tags
->tab
[i
]);
355 untag_client(c
, tags
->tab
[i
]);
358 else if(ev
->type
== _NET_WM_STATE
)
360 if((c
= client_getbywin(ev
->window
)))
362 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[1], ev
->data
.data32
[0]);
363 if(ev
->data
.data32
[2])
364 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[2],
373 ewmh_check_client_hints(client_t
*c
)
378 xcb_get_property_cookie_t c0
, c1
, c2
;
379 xcb_get_property_reply_t
*reply
;
381 /* Send the GetProperty requests which will be processed later */
382 c0
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
383 _NET_WM_DESKTOP
, XCB_GET_PROPERTY_TYPE_ANY
, 0, 1);
385 c1
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
386 _NET_WM_STATE
, ATOM
, 0, UINT32_MAX
);
388 c2
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
389 _NET_WM_WINDOW_TYPE
, ATOM
, 0, UINT32_MAX
);
391 reply
= xcb_get_property_reply(globalconf
.connection
, c0
, NULL
);
392 if(reply
&& reply
->value_len
&& (data
= xcb_get_property_value(reply
)))
394 tag_array_t
*tags
= &globalconf
.screens
[c
->screen
].tags
;
396 desktop
= *(uint32_t *) data
;
400 for(int i
= 0; i
< tags
->len
; i
++)
402 tag_client(c
, tags
->tab
[i
]);
404 untag_client(c
, tags
->tab
[i
]);
409 reply
= xcb_get_property_reply(globalconf
.connection
, c1
, NULL
);
410 if(reply
&& (data
= xcb_get_property_value(reply
)))
412 state
= (xcb_atom_t
*) data
;
413 for(int i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
414 ewmh_process_state_atom(c
, state
[i
], _NET_WM_STATE_ADD
);
419 reply
= xcb_get_property_reply(globalconf
.connection
, c2
, NULL
);
420 if(reply
&& (data
= xcb_get_property_value(reply
)))
422 state
= (xcb_atom_t
*) data
;
423 for(int i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
424 if(state
[i
] == _NET_WM_WINDOW_TYPE_DESKTOP
)
425 c
->type
= WINDOW_TYPE_DESKTOP
;
426 else if(state
[i
] == _NET_WM_WINDOW_TYPE_DIALOG
)
427 c
->type
= WINDOW_TYPE_DIALOG
;
428 else if(state
[i
] == _NET_WM_WINDOW_TYPE_SPLASH
)
429 c
->type
= WINDOW_TYPE_SPLASH
;
430 else if(state
[i
] == _NET_WM_WINDOW_TYPE_DOCK
)
431 c
->type
= WINDOW_TYPE_DOCK
;
433 c
->type
= WINDOW_TYPE_NORMAL
;
439 /** Update the WM strut of a client.
440 * \param c The client.
443 ewmh_client_strut_update(client_t
*c
)
446 xcb_get_property_cookie_t strut_q
;
447 xcb_get_property_reply_t
*strut_r
;
449 strut_q
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
450 _NET_WM_STRUT_PARTIAL
, CARDINAL
, 0, 12);
452 strut_r
= xcb_get_property_reply(globalconf
.connection
, strut_q
, NULL
);
455 && strut_r
->value_len
456 && (data
= xcb_get_property_value(strut_r
)))
458 uint32_t *strut
= data
;
460 if(c
->strut
.left
!= strut
[0]
461 || c
->strut
.right
!= strut
[1]
462 || c
->strut
.top
!= strut
[2]
463 || c
->strut
.bottom
!= strut
[3]
464 || c
->strut
.left_start_y
!= strut
[4]
465 || c
->strut
.left_end_y
!= strut
[5]
466 || c
->strut
.right_start_y
!= strut
[6]
467 || c
->strut
.right_end_y
!= strut
[7]
468 || c
->strut
.top_start_x
!= strut
[8]
469 || c
->strut
.top_end_x
!= strut
[9]
470 || c
->strut
.bottom_start_x
!= strut
[10]
471 || c
->strut
.bottom_end_x
!= strut
[11])
473 c
->strut
.left
= strut
[0];
474 c
->strut
.right
= strut
[1];
475 c
->strut
.top
= strut
[2];
476 c
->strut
.bottom
= strut
[3];
477 c
->strut
.left_start_y
= strut
[4];
478 c
->strut
.left_end_y
= strut
[5];
479 c
->strut
.right_start_y
= strut
[6];
480 c
->strut
.right_end_y
= strut
[7];
481 c
->strut
.top_start_x
= strut
[8];
482 c
->strut
.top_end_x
= strut
[9];
483 c
->strut
.bottom_start_x
= strut
[10];
484 c
->strut
.bottom_end_x
= strut
[11];
486 client_need_arrange(c
);
487 /* All the statusbars (may) need to be repositioned */
488 for(int screen
= 0; screen
< globalconf
.screens_info
->nscreen
; screen
++)
489 for(statusbar_t
*s
= globalconf
.screens
[screen
].statusbar
; s
; s
= s
->next
)
490 statusbar_position_update(s
);
497 /** Send request to get NET_WM_ICON (EWMH)
498 * \param w The window.
499 * \return The cookie associated with the request.
501 xcb_get_property_cookie_t
502 ewmh_window_icon_get_unchecked(xcb_window_t w
)
504 return xcb_get_property_unchecked(globalconf
.connection
, false, w
,
505 _NET_WM_ICON
, CARDINAL
, 0, UINT32_MAX
);
509 * \param cookie The cookie.
510 * \return A netwm_icon_t structure which must be deleted after usage.
513 ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie
)
519 unsigned char *imgdata
;
520 xcb_get_property_reply_t
*r
;
522 r
= xcb_get_property_reply(globalconf
.connection
, cookie
, NULL
);
523 if(!r
|| r
->type
!= CARDINAL
|| r
->format
!= 32 || r
->length
< 2 ||
524 !(data
= (uint32_t *) xcb_get_property_value(r
)))
530 icon
= p_new(netwm_icon_t
, 1);
532 icon
->width
= data
[0];
533 icon
->height
= data
[1];
534 size
= icon
->width
* icon
->height
;
543 icon
->image
= p_new(unsigned char, size
* 4);
544 for(imgdata
= icon
->image
, i
= 2; i
< size
+ 2; i
++, imgdata
+= 4)
546 imgdata
[3] = (data
[i
] >> 24) & 0xff; /* A */
547 alpha
= imgdata
[3] / 255.0;
548 imgdata
[2] = ((data
[i
] >> 16) & 0xff) * alpha
; /* R */
549 imgdata
[1] = ((data
[i
] >> 8) & 0xff) * alpha
; /* G */
550 imgdata
[0] = (data
[i
] & 0xff) * alpha
; /* B */
558 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80