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.
23 #include <xcb/xcb_atom.h>
24 #include <xcb/xcb_aux.h>
34 extern AwesomeConf globalconf
;
36 static xcb_atom_t net_supported
;
37 static xcb_atom_t net_client_list
;
38 static xcb_atom_t net_number_of_desktops
;
39 static xcb_atom_t net_current_desktop
;
40 static xcb_atom_t net_desktop_names
;
41 static xcb_atom_t net_active_window
;
42 static xcb_atom_t net_close_window
;
43 static xcb_atom_t net_wm_name
;
44 static xcb_atom_t net_wm_icon_name
;
45 static xcb_atom_t net_wm_window_type
;
46 static xcb_atom_t net_wm_window_type_normal
;
47 static xcb_atom_t net_wm_window_type_dock
;
48 static xcb_atom_t net_wm_window_type_splash
;
49 static xcb_atom_t net_wm_window_type_dialog
;
50 static xcb_atom_t net_wm_icon
;
51 static xcb_atom_t net_wm_state
;
52 static xcb_atom_t net_wm_state_sticky
;
53 static xcb_atom_t net_wm_state_skip_taskbar
;
54 static xcb_atom_t net_wm_state_fullscreen
;
55 static xcb_atom_t net_wm_state_above
;
56 static xcb_atom_t net_wm_state_below
;
58 static xcb_atom_t utf8_string
;
66 static AtomItem AtomNames
[] =
68 { "_NET_SUPPORTED", &net_supported
},
69 { "_NET_CLIENT_LIST", &net_client_list
},
70 { "_NET_NUMBER_OF_DESKTOPS", &net_number_of_desktops
},
71 { "_NET_CURRENT_DESKTOP", &net_current_desktop
},
72 { "_NET_DESKTOP_NAMES", &net_desktop_names
},
73 { "_NET_ACTIVE_WINDOW", &net_active_window
},
75 { "_NET_CLOSE_WINDOW", &net_close_window
},
77 { "_NET_WM_NAME", &net_wm_name
},
78 { "_NET_WM_ICON_NAME", &net_wm_icon_name
},
79 { "_NET_WM_WINDOW_TYPE", &net_wm_window_type
},
80 { "_NET_WM_WINDOW_TYPE_NORMAL", &net_wm_window_type_normal
},
81 { "_NET_WM_WINDOW_TYPE_DOCK", &net_wm_window_type_dock
},
82 { "_NET_WM_WINDOW_TYPE_SPLASH", &net_wm_window_type_splash
},
83 { "_NET_WM_WINDOW_TYPE_DIALOG", &net_wm_window_type_dialog
},
84 { "_NET_WM_ICON", &net_wm_icon
},
85 { "_NET_WM_STATE", &net_wm_state
},
86 { "_NET_WM_STATE_STICKY", &net_wm_state_sticky
},
87 { "_NET_WM_STATE_SKIP_TASKBAR", &net_wm_state_skip_taskbar
},
88 { "_NET_WM_STATE_FULLSCREEN", &net_wm_state_fullscreen
},
89 { "_NET_WM_STATE_ABOVE", &net_wm_state_above
},
90 { "_NET_WM_STATE_BELOW", &net_wm_state_below
},
92 { "UTF8_STRING", &utf8_string
},
95 #define ATOM_NUMBER (sizeof(AtomNames)/sizeof(AtomItem))
97 #define _NET_WM_STATE_REMOVE 0
98 #define _NET_WM_STATE_ADD 1
99 #define _NET_WM_STATE_TOGGLE 2
102 ewmh_init_atoms(void)
105 xcb_intern_atom_cookie_t cs
[ATOM_NUMBER
];
106 xcb_intern_atom_reply_t
*r
;
109 * Create the atom and get the reply in a XCB way (e.g. send all
110 * the requests at the same time and then get the replies)
112 for(i
= 0; i
< ATOM_NUMBER
; i
++)
113 cs
[i
] = xcb_intern_atom_unchecked(globalconf
.connection
,
115 strlen(AtomNames
[i
].name
),
118 for(i
= 0; i
< ATOM_NUMBER
; i
++)
120 if(!(r
= xcb_intern_atom_reply(globalconf
.connection
, cs
[i
], NULL
)))
121 /* An error occured, get reply for next atom */
124 *AtomNames
[i
].atom
= r
->atom
;
130 ewmh_set_supported_hints(int phys_screen
)
132 xcb_atom_t atom
[ATOM_NUMBER
];
135 atom
[i
++] = net_supported
;
136 atom
[i
++] = net_client_list
;
137 atom
[i
++] = net_number_of_desktops
;
138 atom
[i
++] = net_current_desktop
;
139 atom
[i
++] = net_desktop_names
;
140 atom
[i
++] = net_active_window
;
142 atom
[i
++] = net_close_window
;
144 atom
[i
++] = net_wm_name
;
145 atom
[i
++] = net_wm_icon_name
;
146 atom
[i
++] = net_wm_window_type
;
147 atom
[i
++] = net_wm_window_type_normal
;
148 atom
[i
++] = net_wm_window_type_dock
;
149 atom
[i
++] = net_wm_window_type_splash
;
150 atom
[i
++] = net_wm_window_type_dialog
;
151 atom
[i
++] = net_wm_icon
;
152 atom
[i
++] = net_wm_state
;
153 atom
[i
++] = net_wm_state_sticky
;
154 atom
[i
++] = net_wm_state_skip_taskbar
;
155 atom
[i
++] = net_wm_state_fullscreen
;
156 atom
[i
++] = net_wm_state_above
;
157 atom
[i
++] = net_wm_state_below
;
159 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
160 xcb_aux_get_screen(globalconf
.connection
, phys_screen
)->root
,
161 net_supported
, ATOM
, 32, i
, atom
);
165 ewmh_update_net_client_list(int phys_screen
)
171 for(c
= globalconf
.clients
; c
; c
= c
->next
)
174 wins
= p_new(xcb_window_t
, n
);
176 for(n
= 0, c
= globalconf
.clients
; c
; c
= c
->next
, n
++)
177 if(c
->phys_screen
== phys_screen
)
180 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
181 xcb_aux_get_screen(globalconf
.connection
, phys_screen
)->root
,
182 net_client_list
, WINDOW
, 32, n
, wins
);
188 ewmh_update_net_numbers_of_desktop(int phys_screen
)
193 for(tag
= globalconf
.screens
[phys_screen
].tags
; tag
; tag
= tag
->next
)
196 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
197 xcb_aux_get_screen(globalconf
.connection
, phys_screen
)->root
,
198 net_number_of_desktops
, CARDINAL
, 32, 1, &count
);
202 ewmh_update_net_current_desktop(int phys_screen
)
205 tag_t
*tag
, **curtags
= tags_get_current(phys_screen
);
207 for(tag
= globalconf
.screens
[phys_screen
].tags
; tag
!= curtags
[0]; tag
= tag
->next
)
210 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
211 xcb_aux_get_screen(globalconf
.connection
, phys_screen
)->root
,
212 net_current_desktop
, CARDINAL
, 32, 1, &count
);
218 ewmh_update_net_desktop_names(int phys_screen
)
220 char buf
[1024], *pos
;
221 ssize_t len
, curr_size
;
226 for(tag
= globalconf
.screens
[phys_screen
].tags
; tag
; tag
= tag
->next
)
228 curr_size
= a_strlen(tag
->name
);
229 a_strcpy(pos
, sizeof(buf
), tag
->name
);
230 pos
+= curr_size
+ 1;
231 len
+= curr_size
+ 1;
234 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
235 xcb_aux_get_screen(globalconf
.connection
, phys_screen
)->root
,
236 net_desktop_names
, utf8_string
, 8, len
, buf
);
240 ewmh_update_net_active_window(int phys_screen
)
243 client_t
*sel
= focus_get_current_client(phys_screen
);
245 win
= sel
? sel
->win
: XCB_NONE
;
247 xcb_change_property(globalconf
.connection
, XCB_PROP_MODE_REPLACE
,
248 xcb_aux_get_screen(globalconf
.connection
, phys_screen
)->root
,
249 net_active_window
, WINDOW
, 32, 1, &win
);
253 ewmh_process_state_atom(client_t
*c
, xcb_atom_t state
, int set
)
255 const uint32_t raise_window_val
= XCB_STACK_MODE_ABOVE
;
257 if(state
== net_wm_state_sticky
)
260 for(tag
= globalconf
.screens
[c
->screen
].tags
; tag
; tag
= tag
->next
)
263 else if(state
== net_wm_state_skip_taskbar
)
265 if(set
== _NET_WM_STATE_REMOVE
)
270 else if(set
== _NET_WM_STATE_ADD
)
276 else if(state
== net_wm_state_fullscreen
)
278 area_t geometry
= c
->geometry
;
279 if(set
== _NET_WM_STATE_REMOVE
)
281 /* restore geometry */
282 geometry
= c
->m_geometry
;
283 /* restore borders and titlebar */
284 titlebar_position_set(&c
->titlebar
, c
->titlebar
.dposition
);
285 c
->border
= c
->oldborder
;
287 client_setfloating(c
, c
->wasfloating
, c
->oldlayer
);
289 else if(set
== _NET_WM_STATE_ADD
)
291 geometry
= screen_get_area(c
->screen
, NULL
, &globalconf
.screens
[c
->screen
].padding
);
293 c
->m_geometry
= c
->geometry
;
294 c
->wasfloating
= c
->isfloating
;
295 /* disable titlebar and borders */
296 titlebar_position_set(&c
->titlebar
, Off
);
297 c
->oldborder
= c
->border
;
300 client_setfloating(c
, true, LAYER_FULLSCREEN
);
302 widget_invalidate_cache(c
->screen
, WIDGET_CACHE_CLIENTS
);
303 client_resize(c
, geometry
, false);
304 xcb_configure_window(globalconf
.connection
, c
->win
, XCB_CONFIG_WINDOW_STACK_MODE
, &raise_window_val
);
306 else if(state
== net_wm_state_above
)
308 if(set
== _NET_WM_STATE_REMOVE
)
310 c
->layer
= c
->oldlayer
;
312 else if(set
== _NET_WM_STATE_ADD
)
314 c
->oldlayer
= c
->layer
;
315 c
->layer
= LAYER_ABOVE
;
318 else if(state
== net_wm_state_below
)
320 if(set
== _NET_WM_STATE_REMOVE
)
322 c
->layer
= c
->oldlayer
;
324 else if(set
== _NET_WM_STATE_ADD
)
326 c
->oldlayer
= c
->layer
;
327 c
->layer
= LAYER_BELOW
;
334 ewmh_process_window_type_atom(client_t
*c
, xcb_atom_t state
)
336 if(state
== net_wm_window_type_normal
)
338 /* do nothing. this is REALLY IMPORTANT */
340 else if(state
== net_wm_window_type_dock
341 || state
== net_wm_window_type_splash
)
346 titlebar_position_set(&c
->titlebar
, Off
);
347 client_setfloating(c
, true, LAYER_ABOVE
);
349 else if (state
== net_wm_window_type_dialog
)
350 client_setfloating(c
, true, LAYER_ABOVE
);
354 ewmh_process_client_message(xcb_client_message_event_t
*ev
)
359 if(ev
->type
== net_current_desktop
)
361 screen
< xcb_setup_roots_length(xcb_get_setup(globalconf
.connection
));
364 if(ev
->window
== xcb_aux_get_screen(globalconf
.connection
, screen
)->root
)
365 tag_view_only_byindex(screen
, ev
->data
.data32
[0]);
368 if(ev
->type
== net_close_window
)
370 if((c
= client_get_bywin(globalconf
.clients
, ev
->window
)))
373 else if(ev
->type
== net_wm_state
)
375 if((c
= client_get_bywin(globalconf
.clients
, ev
->window
)))
377 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[1], ev
->data
.data32
[0]);
378 if(ev
->data
.data32
[2])
379 ewmh_process_state_atom(c
, (xcb_atom_t
) ev
->data
.data32
[2],
386 ewmh_check_client_hints(client_t
*c
)
392 xcb_get_property_cookie_t c1
, c2
;
393 xcb_get_property_reply_t
*reply
;
395 /* Send the GetProperty requests which will be processed later */
396 c1
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
397 net_wm_state
, ATOM
, 0, UINT32_MAX
);
399 c2
= xcb_get_property_unchecked(globalconf
.connection
, false, c
->win
,
400 net_wm_window_type
, ATOM
, 0, UINT32_MAX
);
402 reply
= xcb_get_property_reply(globalconf
.connection
, c1
, NULL
);
403 if(reply
&& (data
= xcb_get_property_value(reply
)))
405 state
= (xcb_atom_t
*) data
;
406 for(i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
407 ewmh_process_state_atom(c
, state
[i
], _NET_WM_STATE_ADD
);
412 reply
= xcb_get_property_reply(globalconf
.connection
, c2
, NULL
);
413 if(reply
&& (data
= xcb_get_property_value(reply
)))
415 state
= (xcb_atom_t
*) data
;
416 for(i
= 0; i
< xcb_get_property_value_length(reply
); i
++)
417 ewmh_process_window_type_atom(c
, state
[i
]);
424 ewmh_get_window_icon(xcb_window_t w
)
430 unsigned char *imgdata
;
431 xcb_get_property_reply_t
*r
;
433 r
= xcb_get_property_reply(globalconf
.connection
,
434 xcb_get_property_unchecked(globalconf
.connection
, false, w
,
435 net_wm_icon
, CARDINAL
, 0, UINT32_MAX
),
437 if(!r
|| r
->type
!= CARDINAL
|| r
->format
!= 32 || r
->length
< 2 ||
438 !(data
= (uint32_t *) xcb_get_property_value(r
)))
444 icon
= p_new(NetWMIcon
, 1);
446 icon
->width
= data
[0];
447 icon
->height
= data
[1];
448 size
= icon
->width
* icon
->height
;
457 icon
->image
= p_new(unsigned char, size
* 4);
458 for(imgdata
= icon
->image
, i
= 2; i
< size
+ 2; i
++, imgdata
+= 4)
460 imgdata
[3] = (data
[i
] >> 24) & 0xff; /* A */
461 alpha
= imgdata
[3] / 255.0;
462 imgdata
[2] = ((data
[i
] >> 16) & 0xff) * alpha
; /* R */
463 imgdata
[1] = ((data
[i
] >> 8) & 0xff) * alpha
; /* G */
464 imgdata
[0] = (data
[i
] & 0xff) * alpha
; /* B */
472 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80