diff options
Diffstat (limited to 'lib/xcb-util-wm/ewmh/ewmh.c.m4')
-rw-r--r-- | lib/xcb-util-wm/ewmh/ewmh.c.m4 | 1563 |
1 files changed, 1563 insertions, 0 deletions
diff --git a/lib/xcb-util-wm/ewmh/ewmh.c.m4 b/lib/xcb-util-wm/ewmh/ewmh.c.m4 new file mode 100644 index 000000000..b986105f6 --- /dev/null +++ b/lib/xcb-util-wm/ewmh/ewmh.c.m4 @@ -0,0 +1,1563 @@ +/* + * Copyright © 2009-2011 Arnaud Fontaine <arnau@debian.org> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors or + * their institutions shall not be used in advertising or otherwise to + * promote the sale, use or other dealings in this Software without + * prior written authorization from the authors. + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "xcb_ewmh.h" + +#include <string.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <stddef.h> + +#include <xcb/xcb.h> +#include <xcb/xproto.h> + +#include <sys/types.h> + +#define ssizeof(foo) (ssize_t)sizeof(foo) +#define countof(foo) (ssizeof(foo) / ssizeof(foo[0])) + +/** + * @brief The structure used on screen initialization including the + * atoms name and its length + */ +typedef struct { + /** The Atom name length */ + uint8_t name_len; + /** The Atom name string */ + const char *name; + size_t m_offset; +} ewmh_atom_t; + +define(`DO_ENTRY', ` + { sizeof("$1") - 1, "$1", offsetof(xcb_ewmh_connection_t, $1) }ifelse(`$2', , , `,')')dnl + +define(`DO', `DO_ENTRY(`$1', `$2')ifelse(`$2', , , `DO(shift($@))')')dnl + +/** + * @brief List of atoms where each entry contains the Atom name and + * its length + */ +static ewmh_atom_t ewmh_atoms[] = {dnl + include(atomlist.m4)dnl +}; + +#define NB_EWMH_ATOMS countof(ewmh_atoms) + +/** + * Common functions and macro + */ + +#define DO_GET_PROPERTY(fname, property, type, length) \ + xcb_get_property_cookie_t \ + xcb_ewmh_get_##fname(xcb_ewmh_connection_t *ewmh, \ + xcb_window_t window) \ + { \ + return xcb_get_property(ewmh->connection, 0, window, \ + ewmh->property, type, 0, length); \ + } \ + \ + xcb_get_property_cookie_t \ + xcb_ewmh_get_##fname##_unchecked(xcb_ewmh_connection_t *ewmh, \ + xcb_window_t window) \ + { \ + return xcb_get_property_unchecked(ewmh->connection, 0, window, \ + ewmh->property, type, 0, length); \ + } + +#define DO_GET_ROOT_PROPERTY(fname, property, atype, length) \ + xcb_get_property_cookie_t \ + xcb_ewmh_get_##fname(xcb_ewmh_connection_t *ewmh, \ + int screen_nbr) \ + { \ + return xcb_get_property(ewmh->connection, 0, \ + ewmh->screens[screen_nbr]->root, \ + ewmh->property, atype, 0, length); \ + } \ + \ + xcb_get_property_cookie_t \ + xcb_ewmh_get_##fname##_unchecked(xcb_ewmh_connection_t *ewmh, \ + int screen_nbr) \ + { \ + return xcb_get_property_unchecked(ewmh->connection, 0, \ + ewmh->screens[screen_nbr]->root, \ + ewmh->property, atype, 0, \ + length); \ + } + +/** + * Generic function for EWMH atoms with a single value which may + * actually be either WINDOW or CARDINAL + * + * _NET_NUMBER_OF_DESKTOPS, CARDINAL/32 + * _NET_CURRENT_DESKTOP desktop, CARDINAL/32 + * _NET_ACTIVE_WINDOW, WINDOW/32 + * _NET_SUPPORTING_WM_CHECK, WINDOW/32 + * _NET_SHOWING_DESKTOP desktop, CARDINAL/32 + * _NET_WM_DESKTOP desktop, CARDINAL/32 + * _NET_WM_PID CARDINAL/32 + * _NET_WM_USER_TIME CARDINAL/32 + * _NET_WM_USER_TIME_WINDOW WINDOW/32 + */ + +/** + * Macro defining a generic function for reply with a single value, + * considering that the value is 32-bit long (actually only used for + * WINDOW and CARDINAL) + */ +#define DO_REPLY_SINGLE_VALUE(fname, atype, ctype) \ + uint8_t \ + xcb_ewmh_get_##fname##_from_reply(ctype *atom_value, \ + xcb_get_property_reply_t *r) \ + { \ + if(!r || r->type != atype || r->format != 32 || \ + xcb_get_property_value_length(r) != sizeof(ctype)) \ + return 0; \ + \ + *atom_value = *((ctype *) xcb_get_property_value(r)); \ + return 1; \ + } \ + \ + uint8_t \ + xcb_ewmh_get_##fname##_reply(xcb_ewmh_connection_t *ewmh, \ + xcb_get_property_cookie_t cookie, \ + ctype *atom_value, \ + xcb_generic_error_t **e) \ + { \ + xcb_get_property_reply_t *r = \ + xcb_get_property_reply(ewmh->connection, \ + cookie, e); \ + \ + const uint8_t ret = xcb_ewmh_get_##fname##_from_reply(atom_value, r); \ + \ + free(r); \ + return ret; \ + } + +/** Define reply functions for common WINDOW Atom */ +DO_REPLY_SINGLE_VALUE(window, XCB_ATOM_WINDOW, xcb_window_t) + +/** Define reply functions for common CARDINAL Atom */ +DO_REPLY_SINGLE_VALUE(cardinal, XCB_ATOM_CARDINAL, uint32_t) + +#define DO_SINGLE_VALUE(fname, property, atype, ctype) \ + DO_GET_PROPERTY(fname, property, atype, 1L) \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname##_checked(xcb_ewmh_connection_t *ewmh, \ + xcb_window_t window, \ + ctype value) \ + { \ + return xcb_change_property_checked(ewmh->connection, \ + XCB_PROP_MODE_REPLACE, \ + window, ewmh->property, \ + atype, 32, 1, &value); \ + } \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname(xcb_ewmh_connection_t *ewmh, \ + xcb_window_t window, \ + ctype value) \ + { \ + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, \ + window, ewmh->property, atype, 32, 1, \ + &value); \ + } + +#define DO_ROOT_SINGLE_VALUE(fname, property, atype, ctype) \ + DO_GET_ROOT_PROPERTY(fname, property, atype, 1L) \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname##_checked(xcb_ewmh_connection_t *ewmh, \ + int screen_nbr, \ + ctype value) \ + { \ + return xcb_change_property_checked(ewmh->connection, \ + XCB_PROP_MODE_REPLACE, \ + ewmh->screens[screen_nbr]->root, \ + ewmh->property, atype, 32, 1, \ + &value); \ + } \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname(xcb_ewmh_connection_t *ewmh, \ + int screen_nbr, \ + ctype value) \ + { \ + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, \ + ewmh->screens[screen_nbr]->root, \ + ewmh->property, atype, \ + 32, 1, &value); \ + } + +/** + * Generic function for EWMH atoms with a list of values which may be + * actually WINDOW or ATOM. + * + * _NET_SUPPORTED, ATOM[]/32 + * _NET_CLIENT_LIST, WINDOW[]/32 + * _NET_CLIENT_LIST_STACKING, WINDOW[]/32 + * _NET_VIRTUAL_ROOTS, WINDOW[]/32 + * _NET_WM_WINDOW_TYPE, ATOM[]/32 + * _NET_WM_ALLOWED_ACTIONS, ATOM[] + */ + +/** + * Macro defining a generic function for reply containing a list of + * values and also defines a function to wipe the reply. + */ +#define DO_REPLY_LIST_VALUES(fname, atype, ctype) \ + uint8_t \ + xcb_ewmh_get_##fname##_from_reply(xcb_ewmh_get_##fname##_reply_t *data, \ + xcb_get_property_reply_t *r) \ + { \ + if(!r || r->type != atype || r->format != 32) \ + return 0; \ + \ + data->_reply = r; \ + data->fname##_len = xcb_get_property_value_length(data->_reply) / \ + sizeof(ctype); \ + \ + data->fname = (ctype *) xcb_get_property_value(data->_reply); \ + return 1; \ + } \ + \ + uint8_t \ + xcb_ewmh_get_##fname##_reply(xcb_ewmh_connection_t *ewmh, \ + xcb_get_property_cookie_t cookie, \ + xcb_ewmh_get_##fname##_reply_t *data, \ + xcb_generic_error_t **e) \ + { \ + xcb_get_property_reply_t *r = \ + xcb_get_property_reply(ewmh->connection, \ + cookie, e); \ + \ + const uint8_t ret = xcb_ewmh_get_##fname##_from_reply(data, r); \ + \ + /* If the last call was not successful (ret equals to 0), then \ + just free the reply as the data value is not consistent */ \ + if(!ret) \ + free(r); \ + \ + return ret; \ + } \ + \ + void \ + xcb_ewmh_get_##fname##_reply_wipe(xcb_ewmh_get_##fname##_reply_t *data) \ + { \ + free(data->_reply); \ + } + +#define DO_ROOT_LIST_VALUES(fname, property, atype, ctype) \ + DO_GET_ROOT_PROPERTY(fname, property, atype, UINT_MAX) \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname##_checked(xcb_ewmh_connection_t *ewmh, \ + int screen_nbr, \ + uint32_t list_len, \ + ctype *list) \ + { \ + return xcb_change_property_checked(ewmh->connection, \ + XCB_PROP_MODE_REPLACE, \ + ewmh->screens[screen_nbr]->root, \ + ewmh->property, atype, 32, \ + list_len * (sizeof(ctype) >> 2), \ + list); \ + } \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname(xcb_ewmh_connection_t *ewmh, \ + int screen_nbr, \ + uint32_t list_len, \ + ctype *list) \ + { \ + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, \ + ewmh->screens[screen_nbr]->root, \ + ewmh->property, atype, 32, \ + list_len * (sizeof(ctype) >> 2), \ + list); \ + } + +#define DO_LIST_VALUES(fname, property, atype, kind) \ + DO_GET_PROPERTY(fname, property, atype, UINT_MAX) \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname##_checked(xcb_ewmh_connection_t *ewmh, \ + xcb_window_t window, \ + uint32_t list_len, \ + xcb_##kind##_t *list) \ + { \ + return xcb_change_property_checked(ewmh->connection, \ + XCB_PROP_MODE_REPLACE, window, \ + ewmh->property, atype, 32, \ + list_len, list); \ + } \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname(xcb_ewmh_connection_t *ewmh, \ + xcb_window_t window, \ + uint32_t list_len, \ + xcb_##kind##_t *list) \ + { \ + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, \ + window, ewmh->property, atype, 32, \ + list_len, list); \ + } \ + \ + uint8_t \ + xcb_ewmh_get_##fname##_from_reply(xcb_ewmh_get_##kind##s_reply_t *name, \ + xcb_get_property_reply_t *r) \ + { \ + return xcb_ewmh_get_##kind##s_from_reply(name, r); \ + } \ + \ + uint8_t \ + xcb_ewmh_get_##fname##_reply(xcb_ewmh_connection_t *ewmh, \ + xcb_get_property_cookie_t cookie, \ + xcb_ewmh_get_##kind##s_reply_t *name, \ + xcb_generic_error_t **e) \ + { \ + return xcb_ewmh_get_##kind##s_reply(ewmh, cookie, name, e); \ + } + +#define DO_REPLY_STRUCTURE(fname, ctype) \ + uint8_t \ + xcb_ewmh_get_##fname##_from_reply(ctype *out, \ + xcb_get_property_reply_t *r) \ + { \ + if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32 || \ + xcb_get_property_value_length(r) != sizeof(ctype)) \ + return 0; \ + \ + memcpy(out, xcb_get_property_value(r), \ + xcb_get_property_value_length(r)); \ + \ + return 1; \ + } \ + \ + uint8_t \ + xcb_ewmh_get_##fname##_reply(xcb_ewmh_connection_t *ewmh, \ + xcb_get_property_cookie_t cookie, \ + ctype *out, \ + xcb_generic_error_t **e) \ + { \ + xcb_get_property_reply_t *r = \ + xcb_get_property_reply(ewmh->connection, cookie, e); \ + \ + const uint8_t ret = xcb_ewmh_get_##fname##_from_reply(out, r); \ + free(r); \ + return ret; \ + } + +/** + * UTF8_STRING handling + */ + +uint8_t +xcb_ewmh_get_utf8_strings_from_reply(xcb_ewmh_connection_t *ewmh, + xcb_ewmh_get_utf8_strings_reply_t *data, + xcb_get_property_reply_t *r) +{ + if(!r || r->type != ewmh->UTF8_STRING || r->format != 8) + return 0; + + data->_reply = r; + data->strings_len = xcb_get_property_value_length(data->_reply); + data->strings = (char *) xcb_get_property_value(data->_reply); + + return 1; +} + +uint8_t +xcb_ewmh_get_utf8_strings_reply(xcb_ewmh_connection_t *ewmh, + xcb_get_property_cookie_t cookie, + xcb_ewmh_get_utf8_strings_reply_t *data, + xcb_generic_error_t **e) +{ + xcb_get_property_reply_t *r = xcb_get_property_reply(ewmh->connection, + cookie, e); + + const uint8_t ret = xcb_ewmh_get_utf8_strings_from_reply(ewmh, data, r); + + /* If the last call was not successful (ret equals to 0), then just + free the reply as the data value is not consistent */ + if(!ret) + free(r); + + return ret; +} + +void +xcb_ewmh_get_utf8_strings_reply_wipe(xcb_ewmh_get_utf8_strings_reply_t *data) +{ + free(data->_reply); +} + +#define DO_ROOT_UTF8_STRING(fname, property) \ + DO_GET_ROOT_PROPERTY(fname, property, 0, UINT_MAX) \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname(xcb_ewmh_connection_t *ewmh, \ + int screen_nbr, \ + uint32_t strings_len, \ + const char *strings) \ + { \ + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, \ + ewmh->screens[screen_nbr]->root, \ + ewmh->property, ewmh->UTF8_STRING, 8, \ + strings_len, strings); \ + } \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname##_checked(xcb_ewmh_connection_t *ewmh, \ + int screen_nbr, \ + uint32_t strings_len, \ + const char *strings) \ + { \ + return xcb_change_property_checked(ewmh->connection, \ + XCB_PROP_MODE_REPLACE, \ + ewmh->screens[screen_nbr]->root, \ + ewmh->property, \ + ewmh->UTF8_STRING, 8, \ + strings_len, strings); \ + } + +#define DO_UTF8_STRING(fname, property) \ + DO_GET_PROPERTY(fname, property, 0, UINT_MAX) \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname(xcb_ewmh_connection_t *ewmh, \ + xcb_window_t window, \ + uint32_t strings_len, \ + const char *strings) \ + { \ + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, \ + window, ewmh->property, \ + ewmh->UTF8_STRING, 8, strings_len, \ + strings); \ + } \ + \ + xcb_void_cookie_t \ + xcb_ewmh_set_##fname##_checked(xcb_ewmh_connection_t *ewmh, \ + xcb_window_t window, \ + uint32_t strings_len, \ + const char *strings) \ + { \ + return xcb_change_property_checked(ewmh->connection, \ + XCB_PROP_MODE_REPLACE, \ + window, ewmh->property, \ + ewmh->UTF8_STRING, 8, \ + strings_len, strings); \ + } + +/** + * ClientMessage generic function + */ +xcb_void_cookie_t +xcb_ewmh_send_client_message(xcb_connection_t *c, + xcb_window_t window, + xcb_window_t dest, + xcb_atom_t atom, + uint32_t data_len, + const uint32_t *data) +{ + xcb_client_message_event_t ev; + memset(&ev, 0, sizeof(xcb_client_message_event_t)); + + ev.response_type = XCB_CLIENT_MESSAGE; + ev.window = window; + ev.format = 32; + ev.type = atom; + + assert(data_len <= (5 * sizeof(uint32_t))); + + memcpy(ev.data.data32, data, data_len); + + return xcb_send_event(c, 0, dest, XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (char *) &ev); +} + +DO_REPLY_LIST_VALUES(windows, XCB_ATOM_WINDOW, xcb_window_t) +DO_REPLY_LIST_VALUES(atoms, XCB_ATOM_ATOM, xcb_atom_t) + +/** + * Atoms initialisation + */ + +xcb_intern_atom_cookie_t * +xcb_ewmh_init_atoms(xcb_connection_t *c, + xcb_ewmh_connection_t *ewmh) +{ + int screen_nbr, atom_nbr; + + ewmh->connection = c; + + const xcb_setup_t *setup = xcb_get_setup(c); + + ewmh->nb_screens = xcb_setup_roots_length(setup); + if(!ewmh->nb_screens) + return NULL; + + /* Allocate the data structures depending of the number of screens */ + ewmh->screens = malloc(sizeof(xcb_screen_t *) * ewmh->nb_screens); + ewmh->_NET_WM_CM_Sn = malloc(sizeof(xcb_atom_t) * ewmh->nb_screens); + + xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup); + for(screen_iter = xcb_setup_roots_iterator(setup), screen_nbr = 0; screen_iter.rem; + xcb_screen_next(&screen_iter)) + ewmh->screens[screen_nbr++] = screen_iter.data; + + /* _NET_WM_CM_Sn atoms will be treated differently, by adding them + at the end of this array, than other atoms as it depends on the + number of screens */ + xcb_intern_atom_cookie_t *ewmh_cookies = malloc(sizeof(xcb_intern_atom_cookie_t) * + (NB_EWMH_ATOMS + ewmh->nb_screens)); + + /* First, send InternAtom request for all Atoms except _NET_WM_CM_Sn */ + for(atom_nbr = 0; atom_nbr < NB_EWMH_ATOMS; atom_nbr++) + ewmh_cookies[atom_nbr] = xcb_intern_atom(ewmh->connection, 0, + ewmh_atoms[atom_nbr].name_len, + ewmh_atoms[atom_nbr].name); + + /* Then, send InternAtom requests for _NET_WM_CM_Sn and compute + _NET_WM_CM_Sn according to the screen number 'n' */ + for(screen_nbr = 0; screen_nbr < ewmh->nb_screens; screen_nbr++) + { + char wm_cm_sn[32]; + + const int wm_cm_sn_len = snprintf(wm_cm_sn, 32, "_NET_WM_CM_S%d", + screen_nbr); + + assert(wm_cm_sn_len > 0 && wm_cm_sn_len < 32); + + ewmh_cookies[atom_nbr++] = xcb_intern_atom(ewmh->connection, 0, + wm_cm_sn_len, + wm_cm_sn); + } + + return ewmh_cookies; +} + +uint8_t +xcb_ewmh_init_atoms_replies(xcb_ewmh_connection_t *ewmh, + xcb_intern_atom_cookie_t *ewmh_cookies, + xcb_generic_error_t **e) +{ + int atom_nbr; + int screen_nbr = 0; + uint8_t ret = 1; + xcb_intern_atom_reply_t *reply; + + for(atom_nbr = 0; atom_nbr < NB_EWMH_ATOMS + ewmh->nb_screens; atom_nbr++) + if((reply = xcb_intern_atom_reply(ewmh->connection, ewmh_cookies[atom_nbr], e))) + { + if(ret) + { + if(atom_nbr < NB_EWMH_ATOMS) + *((xcb_atom_t *) (((char *) ewmh) + ewmh_atoms[atom_nbr].m_offset)) = reply->atom; + else + ewmh->_NET_WM_CM_Sn[screen_nbr++] = reply->atom; + } + + free(reply); + } + else + ret = 0; + + if(!ret) + xcb_ewmh_connection_wipe(ewmh); + + free(ewmh_cookies); + return ret; +} + +/** + * _NET_SUPPORTED + */ + +DO_ROOT_LIST_VALUES(supported, _NET_SUPPORTED, XCB_ATOM_ATOM, xcb_atom_t) + +/** + * _NET_CLIENT_LIST + * _NET_CLIENT_LIST_STACKING + */ + +DO_ROOT_LIST_VALUES(client_list, _NET_CLIENT_LIST, XCB_ATOM_WINDOW, xcb_window_t) + +DO_ROOT_LIST_VALUES(client_list_stacking, _NET_CLIENT_LIST_STACKING, + XCB_ATOM_WINDOW, xcb_window_t) + +/** + * _NET_NUMBER_OF_DESKTOPS + */ + +DO_ROOT_SINGLE_VALUE(number_of_desktops, _NET_NUMBER_OF_DESKTOPS, + XCB_ATOM_CARDINAL, uint32_t) + +/** + * _NET_DESKTOP_GEOMETRY + */ + +DO_GET_ROOT_PROPERTY(desktop_geometry, _NET_DESKTOP_GEOMETRY, + XCB_ATOM_CARDINAL, 2L) + +xcb_void_cookie_t +xcb_ewmh_set_desktop_geometry(xcb_ewmh_connection_t *ewmh, int screen_nbr, + uint32_t new_width, uint32_t new_height) +{ + const uint32_t data[] = { new_width, new_height }; + + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_DESKTOP_GEOMETRY, XCB_ATOM_CARDINAL, + 32, 2, data); +} + +xcb_void_cookie_t +xcb_ewmh_set_desktop_geometry_checked(xcb_ewmh_connection_t *ewmh, + int screen_nbr, uint32_t new_width, + uint32_t new_height) +{ + const uint32_t data[] = { new_width, new_height }; + + return xcb_change_property_checked(ewmh->connection, XCB_PROP_MODE_REPLACE, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_DESKTOP_GEOMETRY, + XCB_ATOM_CARDINAL, 32, 2, data); +} + +xcb_void_cookie_t +xcb_ewmh_request_change_desktop_geometry(xcb_ewmh_connection_t *ewmh, + int screen_nbr, uint32_t new_width, + uint32_t new_height) +{ + const uint32_t data[] = { new_width, new_height }; + + return xcb_ewmh_send_client_message(ewmh->connection, + ewmh->screens[screen_nbr]->root, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_DESKTOP_GEOMETRY, + sizeof(data), data); +} + +uint8_t +xcb_ewmh_get_desktop_geometry_from_reply(uint32_t *width, uint32_t *height, + xcb_get_property_reply_t *r) +{ + if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32 || + xcb_get_property_value_length(r) != (sizeof(uint32_t) * 2)) + return 0; + + uint32_t *value = (uint32_t *) xcb_get_property_value(r); + + *width = value[0]; + *height = value[1]; + + return 1; +} + +uint8_t +xcb_ewmh_get_desktop_geometry_reply(xcb_ewmh_connection_t *ewmh, + xcb_get_property_cookie_t cookie, + uint32_t *width, uint32_t *height, + xcb_generic_error_t **e) +{ + xcb_get_property_reply_t *r = xcb_get_property_reply(ewmh->connection, cookie, e); + const uint8_t ret = xcb_ewmh_get_desktop_geometry_from_reply(width, height, r); + free(r); + return ret; +} + +/** + * _NET_DESKTOP_VIEWPORT + */ + +DO_ROOT_LIST_VALUES(desktop_viewport, _NET_DESKTOP_VIEWPORT, XCB_ATOM_CARDINAL, + xcb_ewmh_coordinates_t) + +DO_REPLY_LIST_VALUES(desktop_viewport, XCB_ATOM_CARDINAL, + xcb_ewmh_coordinates_t) + +xcb_void_cookie_t +xcb_ewmh_request_change_desktop_viewport(xcb_ewmh_connection_t *ewmh, + int screen_nbr, uint32_t x, + uint32_t y) +{ + const uint32_t data[] = { x, y }; + + return xcb_ewmh_send_client_message(ewmh->connection, + ewmh->screens[screen_nbr]->root, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_DESKTOP_VIEWPORT, + sizeof(data), data); +} + +/** + * _NET_CURRENT_DESKTOP + */ + +DO_ROOT_SINGLE_VALUE(current_desktop, _NET_CURRENT_DESKTOP, XCB_ATOM_CARDINAL, + uint32_t) + +xcb_void_cookie_t +xcb_ewmh_request_change_current_desktop(xcb_ewmh_connection_t *ewmh, + int screen_nbr, uint32_t new_desktop, + xcb_timestamp_t timestamp) +{ + const uint32_t data[] = { new_desktop, timestamp }; + + return xcb_ewmh_send_client_message(ewmh->connection, + ewmh->screens[screen_nbr]->root, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_CURRENT_DESKTOP, + sizeof(data), data); +} + +/** + * _NET_DESKTOP_NAMES + */ +DO_ROOT_UTF8_STRING(desktop_names, _NET_DESKTOP_NAMES) + +/** + * _NET_ACTIVE_WINDOW + */ + +DO_ROOT_SINGLE_VALUE(active_window, _NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, + xcb_window_t) + +xcb_void_cookie_t +xcb_ewmh_request_change_active_window(xcb_ewmh_connection_t *ewmh, + int screen_nbr, + xcb_window_t window_to_activate, + xcb_ewmh_client_source_type_t source_indication, + xcb_timestamp_t timestamp, + xcb_window_t current_active_window) +{ + const uint32_t data[] = { source_indication, timestamp, current_active_window }; + + return xcb_ewmh_send_client_message(ewmh->connection, window_to_activate, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_ACTIVE_WINDOW, sizeof(data), + data); +} + +/** + * _NET_WORKAREA + */ + +DO_ROOT_LIST_VALUES(workarea, _NET_WORKAREA, XCB_ATOM_CARDINAL, + xcb_ewmh_geometry_t) + +DO_REPLY_LIST_VALUES(workarea, XCB_ATOM_CARDINAL, xcb_ewmh_geometry_t) + +/** + * _NET_SUPPORTING_WM_CHECK + */ + +DO_SINGLE_VALUE(supporting_wm_check, _NET_SUPPORTING_WM_CHECK, + XCB_ATOM_WINDOW, xcb_window_t) + +/** + * _NET_VIRTUAL_ROOTS + */ + +DO_ROOT_LIST_VALUES(virtual_roots, _NET_VIRTUAL_ROOTS, XCB_ATOM_WINDOW, + xcb_window_t) + +/** + * _NET_DESKTOP_LAYOUT + */ + +DO_GET_ROOT_PROPERTY(desktop_layout, _NET_DESKTOP_LAYOUT, XCB_ATOM_CARDINAL, 4) +DO_REPLY_STRUCTURE(desktop_layout, xcb_ewmh_get_desktop_layout_reply_t) + +xcb_void_cookie_t +xcb_ewmh_set_desktop_layout(xcb_ewmh_connection_t *ewmh, int screen_nbr, + xcb_ewmh_desktop_layout_orientation_t orientation, + uint32_t columns, uint32_t rows, + xcb_ewmh_desktop_layout_starting_corner_t starting_corner) +{ + const uint32_t data[] = { orientation, columns, rows, starting_corner }; + + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_DESKTOP_LAYOUT, XCB_ATOM_CARDINAL, 32, + countof(data), data); +} + +xcb_void_cookie_t +xcb_ewmh_set_desktop_layout_checked(xcb_ewmh_connection_t *ewmh, int screen_nbr, + xcb_ewmh_desktop_layout_orientation_t orientation, + uint32_t columns, uint32_t rows, + xcb_ewmh_desktop_layout_starting_corner_t starting_corner) +{ + const uint32_t data[] = { orientation, columns, rows, starting_corner }; + + return xcb_change_property_checked(ewmh->connection, XCB_PROP_MODE_REPLACE, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_DESKTOP_LAYOUT, + XCB_ATOM_CARDINAL, 32, countof(data), + data); +} + +/** + * _NET_SHOWING_DESKTOP + */ + +DO_ROOT_SINGLE_VALUE(showing_desktop, _NET_SHOWING_DESKTOP, XCB_ATOM_CARDINAL, + uint32_t) + +/** + * _NET_CLOSE_WINDOW + */ + +xcb_void_cookie_t +xcb_ewmh_request_close_window(xcb_ewmh_connection_t *ewmh, int screen_nbr, + xcb_window_t window_to_close, + xcb_timestamp_t timestamp, + xcb_ewmh_client_source_type_t source_indication) +{ + const uint32_t data[] = { timestamp, source_indication }; + + return xcb_ewmh_send_client_message(ewmh->connection, window_to_close, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_CLOSE_WINDOW, sizeof(data), + data); +} + +/** + * _NET_MOVERESIZE_WINDOW + */ + +/* x, y, width, height may be equal to -1 */ +xcb_void_cookie_t +xcb_ewmh_request_moveresize_window(xcb_ewmh_connection_t *ewmh, int screen_nbr, + xcb_window_t moveresize_window, + xcb_gravity_t gravity, + xcb_ewmh_client_source_type_t source_indication, + xcb_ewmh_moveresize_window_opt_flags_t flags, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height) +{ + const uint32_t data[] = { (gravity | flags | source_indication << 12), + x, y, width, height }; + + return xcb_ewmh_send_client_message(ewmh->connection, moveresize_window, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_MOVERESIZE_WINDOW, + sizeof(data), data); +} + +/** + * _NET_WM_MOVERESIZE + */ + +xcb_void_cookie_t +xcb_ewmh_request_wm_moveresize(xcb_ewmh_connection_t *ewmh, int screen_nbr, + xcb_window_t moveresize_window, + uint32_t x_root, uint32_t y_root, + xcb_ewmh_moveresize_direction_t direction, + xcb_button_index_t button, + xcb_ewmh_client_source_type_t source_indication) +{ + const uint32_t data[] = { x_root, y_root, direction, button, source_indication }; + + return xcb_ewmh_send_client_message(ewmh->connection, moveresize_window, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_WM_MOVERESIZE, sizeof(data), + data); +} + +/** + * _NET_RESTACK_WINDOW + */ + +xcb_void_cookie_t +xcb_ewmh_request_restack_window(xcb_ewmh_connection_t *ewmh, int screen_nbr, + xcb_window_t window_to_restack, + xcb_window_t sibling_window, + xcb_stack_mode_t detail) +{ + const uint32_t data[] = { XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER, sibling_window, + detail }; + + return xcb_ewmh_send_client_message(ewmh->connection, window_to_restack, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_RESTACK_WINDOW, sizeof(data), + data); +} + +/** + * _NET_WM_NAME + */ + +DO_UTF8_STRING(wm_name, _NET_WM_NAME) + +/** + * _NET_WM_VISIBLE_NAME + */ + +DO_UTF8_STRING(wm_visible_name, _NET_WM_VISIBLE_NAME) + +/** + * _NET_WM_ICON_NAME + */ + +DO_UTF8_STRING(wm_icon_name, _NET_WM_ICON_NAME) + +/** + * _NET_WM_VISIBLE_ICON_NAME + */ + +DO_UTF8_STRING(wm_visible_icon_name, _NET_WM_VISIBLE_ICON_NAME) + +/** + * _NET_WM_DESKTOP + */ + +DO_SINGLE_VALUE(wm_desktop, _NET_WM_DESKTOP, XCB_ATOM_CARDINAL, uint32_t) + +xcb_void_cookie_t +xcb_ewmh_request_change_wm_desktop(xcb_ewmh_connection_t *ewmh, int screen_nbr, + xcb_window_t client_window, + uint32_t new_desktop, + xcb_ewmh_client_source_type_t source_indication) +{ + const uint32_t data[] = { new_desktop, source_indication }; + + return xcb_ewmh_send_client_message(ewmh->connection, client_window, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_WM_DESKTOP, sizeof(data), + data); +} + +/** + * _NET_WM_WINDOW_TYPE + * + * TODO: check possible atoms? + */ + +DO_LIST_VALUES(wm_window_type, _NET_WM_WINDOW_TYPE, XCB_ATOM_ATOM, atom) + +/** + * _NET_WM_STATE + * + * TODO: check possible atoms? + */ + +DO_LIST_VALUES(wm_state, _NET_WM_STATE, XCB_ATOM_ATOM, atom) + +xcb_void_cookie_t +xcb_ewmh_request_change_wm_state(xcb_ewmh_connection_t *ewmh, int screen_nbr, + xcb_window_t client_window, + xcb_ewmh_wm_state_action_t action, + xcb_atom_t first_property, + xcb_atom_t second_property, + xcb_ewmh_client_source_type_t source_indication) +{ + const uint32_t data[] = { action, first_property, second_property, + source_indication }; + + return xcb_ewmh_send_client_message(ewmh->connection, client_window, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_WM_STATE, sizeof(data), data); +} + +/** + * _NET_WM_ALLOWED_ACTIONS + * + * TODO: check possible atoms? + */ + +DO_LIST_VALUES(wm_allowed_actions, _NET_WM_ALLOWED_ACTIONS, XCB_ATOM_ATOM, atom) + +/** + * _NET_WM_STRUT + */ + +xcb_void_cookie_t +xcb_ewmh_set_wm_strut(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t left, uint32_t right, + uint32_t top, uint32_t bottom) +{ + const uint32_t data[] = { left, right, top, bottom }; + + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, window, + ewmh->_NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, + countof(data), data); +} + +xcb_void_cookie_t +xcb_ewmh_set_wm_strut_checked(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t left, uint32_t right, + uint32_t top, uint32_t bottom) +{ + const uint32_t data[] = { left, right, top, bottom }; + + return xcb_change_property_checked(ewmh->connection, XCB_PROP_MODE_REPLACE, + window, ewmh->_NET_WM_STRUT, + XCB_ATOM_CARDINAL, 32, countof(data), + data); +} + +DO_GET_PROPERTY(wm_strut, _NET_WM_STRUT, XCB_ATOM_CARDINAL, 4) +DO_REPLY_STRUCTURE(wm_strut, xcb_ewmh_get_extents_reply_t) + +/* + * _NET_WM_STRUT_PARTIAL + */ + +xcb_void_cookie_t +xcb_ewmh_set_wm_strut_partial(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + xcb_ewmh_wm_strut_partial_t wm_strut) +{ + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, window, + ewmh->_NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, + 12, &wm_strut); +} + +xcb_void_cookie_t +xcb_ewmh_set_wm_strut_partial_checked(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + xcb_ewmh_wm_strut_partial_t wm_strut) +{ + return xcb_change_property_checked(ewmh->connection, XCB_PROP_MODE_REPLACE, + window, ewmh->_NET_WM_STRUT_PARTIAL, + XCB_ATOM_CARDINAL, 32, 12, &wm_strut); +} + +DO_GET_PROPERTY(wm_strut_partial, _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 12) +DO_REPLY_STRUCTURE(wm_strut_partial, xcb_ewmh_wm_strut_partial_t) + +/** + * _NET_WM_ICON_GEOMETRY + */ + +xcb_void_cookie_t +xcb_ewmh_set_wm_icon_geometry_checked(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t left, uint32_t right, + uint32_t top, uint32_t bottom) +{ + const uint32_t data[] = { left, right, top, bottom }; + + return xcb_change_property_checked(ewmh->connection, XCB_PROP_MODE_REPLACE, + window, ewmh->_NET_WM_ICON_GEOMETRY, + XCB_ATOM_CARDINAL, 32, countof(data), + data); +} + +xcb_void_cookie_t +xcb_ewmh_set_wm_icon_geometry(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t left, uint32_t right, + uint32_t top, uint32_t bottom) +{ + const uint32_t data[] = { left, right, top, bottom }; + + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, window, + ewmh->_NET_WM_ICON_GEOMETRY, XCB_ATOM_CARDINAL, 32, + countof(data), data); +} + +DO_GET_PROPERTY(wm_icon_geometry, _NET_WM_ICON_GEOMETRY, XCB_ATOM_CARDINAL, 4) +DO_REPLY_STRUCTURE(wm_icon_geometry, xcb_ewmh_geometry_t) + +/** + * _NET_WM_ICON + */ + +static inline void +set_wm_icon_data(uint32_t data[], uint32_t width, uint32_t height, + uint32_t img_len, uint32_t *img) +{ + data[0] = width; + data[1] = height; + + memcpy(data + 2, img, img_len); +} + +xcb_void_cookie_t +xcb_ewmh_append_wm_icon_checked(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t width, uint32_t height, + uint32_t img_len, uint32_t *img) +{ + const uint32_t data_len = img_len + 2; + uint32_t data[data_len]; + + set_wm_icon_data(data, width, height, img_len, img); + + return xcb_ewmh_set_wm_icon_checked(ewmh, XCB_PROP_MODE_APPEND, window, + data_len, data); +} + +xcb_void_cookie_t +xcb_ewmh_append_wm_icon(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t width, uint32_t height, + uint32_t img_len, uint32_t *img) +{ + const uint32_t data_len = img_len + 2; + uint32_t data[data_len]; + + set_wm_icon_data(data, width, height, img_len, img); + + return xcb_ewmh_set_wm_icon(ewmh, XCB_PROP_MODE_APPEND, window, + data_len, data); +} + +DO_GET_PROPERTY(wm_icon, _NET_WM_ICON, XCB_ATOM_CARDINAL, UINT_MAX) + +uint8_t +xcb_ewmh_get_wm_icon_from_reply(xcb_ewmh_get_wm_icon_reply_t *wm_icon, + xcb_get_property_reply_t *r) +{ + if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32) + return 0; + + uint32_t r_value_len = xcb_get_property_value_length(r); + uint32_t *r_value = (uint32_t *) xcb_get_property_value(r); + + /* Find the number of icons in the reply. */ + wm_icon->num_icons = 0; + while(r_value_len > (sizeof(uint32_t) * 2) && r_value && r_value[0] && r_value[1]) + { + /* Check that the property is as long as it should be (in bytes), + handling integer overflow. "+2" to handle the width and height + fields. */ + const uint64_t expected_len = (r_value[0] * (uint64_t) r_value[1] + 2) * 4; + if(expected_len > r_value_len) + break; + + wm_icon->num_icons++; + + /* Find pointer to next icon in the reply. */ + r_value_len -= expected_len; + r_value = (uint32_t *) (((uint8_t *) r_value) + expected_len); + } + + if(!wm_icon->num_icons) + return 0; + + wm_icon->_reply = r; + + return 1; +} + +uint8_t +xcb_ewmh_get_wm_icon_reply(xcb_ewmh_connection_t *ewmh, + xcb_get_property_cookie_t cookie, + xcb_ewmh_get_wm_icon_reply_t *wm_icon, + xcb_generic_error_t **e) +{ + xcb_get_property_reply_t *r = xcb_get_property_reply(ewmh->connection, cookie, e); + const uint8_t ret = xcb_ewmh_get_wm_icon_from_reply(wm_icon, r); + if(!ret) + free(r); + + return ret; +} + +void +xcb_ewmh_get_wm_icon_reply_wipe(xcb_ewmh_get_wm_icon_reply_t *wm_icon) +{ + free(wm_icon->_reply); +} + +xcb_ewmh_wm_icon_iterator_t +xcb_ewmh_get_wm_icon_iterator(const xcb_ewmh_get_wm_icon_reply_t *wm_icon) +{ + xcb_ewmh_wm_icon_iterator_t ret; + + ret.width = 0; + ret.height = 0; + ret.data = NULL; + ret.rem = wm_icon->num_icons; + ret.index = 0; + + if(ret.rem > 0) + { + uint32_t *r_value = (uint32_t *) xcb_get_property_value(wm_icon->_reply); + ret.width = r_value[0]; + ret.height = r_value[1]; + ret.data = &r_value[2]; + } + + return ret; +} + +unsigned int xcb_ewmh_get_wm_icon_length(const xcb_ewmh_get_wm_icon_reply_t *wm_icon) +{ + return wm_icon->num_icons; +} + +void xcb_ewmh_get_wm_icon_next(xcb_ewmh_wm_icon_iterator_t *iterator) +{ + if(iterator->rem <= 1) + { + iterator->index += iterator->rem; + iterator->rem = 0; + iterator->width = 0; + iterator->height = 0; + iterator->data = NULL; + return; + } + + uint64_t icon_len = iterator->width * (uint64_t) iterator->height; + uint32_t *data = iterator->data + icon_len; + + iterator->rem--; + iterator->index++; + iterator->width = data[0]; + iterator->height = data[1]; + iterator->data = &data[2]; +} + +/** + * _NET_WM_PID + */ + +DO_SINGLE_VALUE(wm_pid, _NET_WM_PID, XCB_ATOM_CARDINAL, uint32_t) + +/** + * _NET_WM_HANDLED_ICONS + */ + +DO_SINGLE_VALUE(wm_handled_icons, _NET_WM_HANDLED_ICONS, XCB_ATOM_CARDINAL, + uint32_t) + +/** + * _NET_WM_USER_TIME + */ + +DO_SINGLE_VALUE(wm_user_time, _NET_WM_USER_TIME, XCB_ATOM_CARDINAL, uint32_t) + +/** + * _NET_WM_USER_TIME_WINDOW + */ + +DO_SINGLE_VALUE(wm_user_time_window, _NET_WM_USER_TIME_WINDOW, XCB_ATOM_CARDINAL, + uint32_t) + +/** + * _NET_FRAME_EXTENTS + */ + +xcb_void_cookie_t +xcb_ewmh_set_frame_extents(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t left, uint32_t right, + uint32_t top, uint32_t bottom) +{ + const uint32_t data[] = { left, right, top, bottom }; + + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, window, + ewmh->_NET_FRAME_EXTENTS, XCB_ATOM_CARDINAL, 32, + countof(data), data); +} + +xcb_void_cookie_t +xcb_ewmh_set_frame_extents_checked(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t left, uint32_t right, + uint32_t top, uint32_t bottom) +{ + const uint32_t data[] = { left, right, top, bottom }; + + return xcb_change_property_checked(ewmh->connection, XCB_PROP_MODE_REPLACE, + window, ewmh->_NET_FRAME_EXTENTS, + XCB_ATOM_CARDINAL, 32, countof(data), + data); +} + +DO_GET_PROPERTY(frame_extents, _NET_FRAME_EXTENTS, XCB_ATOM_CARDINAL, 4) +DO_REPLY_STRUCTURE(frame_extents, xcb_ewmh_get_extents_reply_t) + +/** + * _NET_WM_PING + * + * TODO: client resend function? + */ + +xcb_void_cookie_t +xcb_ewmh_send_wm_ping(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + xcb_timestamp_t timestamp) +{ + const uint32_t data[] = { ewmh->_NET_WM_PING, timestamp, window }; + + return xcb_ewmh_send_client_message(ewmh->connection, window, window, + ewmh->WM_PROTOCOLS, sizeof(data), data); +} + +/** + * _NET_WM_SYNC_REQUEST + * _NET_WM_SYNC_REQUEST_COUNTER + */ + +xcb_void_cookie_t +xcb_ewmh_set_wm_sync_request_counter(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + xcb_atom_t wm_sync_request_counter_atom, + uint32_t low, uint32_t high) +{ + const uint32_t data[] = { low, high }; + + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, window, + ewmh->_NET_WM_SYNC_REQUEST_COUNTER, + XCB_ATOM_CARDINAL, 32, + countof(data), data); +} + +xcb_void_cookie_t +xcb_ewmh_set_wm_sync_request_counter_checked(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + xcb_atom_t wm_sync_request_counter_atom, + uint32_t low, uint32_t high) +{ + const uint32_t data[] = { low, high }; + + return xcb_change_property_checked(ewmh->connection, XCB_PROP_MODE_REPLACE, + window, ewmh->_NET_WM_SYNC_REQUEST_COUNTER, + XCB_ATOM_CARDINAL, 32, countof(data), + data); +} + +DO_GET_PROPERTY(wm_sync_request_counter, _NET_WM_SYNC_REQUEST_COUNTER, + XCB_ATOM_CARDINAL, 2) + +uint8_t +xcb_ewmh_get_wm_sync_request_counter_from_reply(uint64_t *counter, + xcb_get_property_reply_t *r) +{ + /* 2 cardinals? */ + if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32 || + xcb_get_property_value_length(r) != sizeof(uint64_t)) + return 0; + + uint32_t *r_value = (uint32_t *) xcb_get_property_value(r); + *counter = (r_value[0] | ((uint64_t) r_value[1]) << 32); + + return 1; +} + +uint8_t +xcb_ewmh_get_wm_sync_request_counter_reply(xcb_ewmh_connection_t *ewmh, + xcb_get_property_cookie_t cookie, + uint64_t *counter, + xcb_generic_error_t **e) +{ + xcb_get_property_reply_t *r = xcb_get_property_reply(ewmh->connection, cookie, e); + const uint8_t ret = xcb_ewmh_get_wm_sync_request_counter_from_reply(counter, r); + free(r); + return ret; +} + +xcb_void_cookie_t +xcb_ewmh_send_wm_sync_request(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + xcb_atom_t wm_protocols_atom, + xcb_atom_t wm_sync_request_atom, + xcb_timestamp_t timestamp, + uint64_t counter) +{ + const uint32_t data[] = { ewmh->_NET_WM_SYNC_REQUEST, timestamp, counter, + counter >> 32 }; + + return xcb_ewmh_send_client_message(ewmh->connection, window, window, + ewmh->WM_PROTOCOLS, sizeof(data), data); +} + +/** + * _NET_WM_FULLSCREEN_MONITORS + */ + +xcb_void_cookie_t +xcb_ewmh_set_wm_fullscreen_monitors(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t top, uint32_t bottom, + uint32_t left, uint32_t right) +{ + const uint32_t data[] = { top, bottom, left, right }; + + return xcb_change_property(ewmh->connection, XCB_PROP_MODE_REPLACE, window, + ewmh->_NET_WM_FULLSCREEN_MONITORS, + XCB_ATOM_CARDINAL, 32, countof(data), data); +} + +xcb_void_cookie_t +xcb_ewmh_set_wm_fullscreen_monitors_checked(xcb_ewmh_connection_t *ewmh, + xcb_window_t window, + uint32_t top, uint32_t bottom, + uint32_t left, uint32_t right) +{ + const uint32_t data[] = { top, bottom, left, right }; + + return xcb_change_property_checked(ewmh->connection, XCB_PROP_MODE_REPLACE, + window, ewmh->_NET_WM_FULLSCREEN_MONITORS, + XCB_ATOM_CARDINAL, 32, countof(data), + data); +} + +DO_GET_PROPERTY(wm_fullscreen_monitors, _NET_WM_FULLSCREEN_MONITORS, + XCB_ATOM_CARDINAL, 4) + +DO_REPLY_STRUCTURE(wm_fullscreen_monitors, + xcb_ewmh_get_wm_fullscreen_monitors_reply_t) + +xcb_void_cookie_t +xcb_ewmh_request_change_wm_fullscreen_monitors(xcb_ewmh_connection_t *ewmh, + int screen_nbr, + xcb_window_t window, + uint32_t top, uint32_t bottom, + uint32_t left, uint32_t right, + xcb_ewmh_client_source_type_t source_indication) +{ + const uint32_t data[] = { top, bottom, left, right, source_indication }; + + return xcb_ewmh_send_client_message(ewmh->connection, window, + ewmh->screens[screen_nbr]->root, + ewmh->_NET_WM_FULLSCREEN_MONITORS, + sizeof(data), data); +} + +/** + * _NET_WM_FULL_PLACEMENT + */ + +/** + * _NET_WM_CM_Sn + */ + +xcb_get_selection_owner_cookie_t +xcb_ewmh_get_wm_cm_owner(xcb_ewmh_connection_t *ewmh, + int screen_nbr) +{ + return xcb_get_selection_owner(ewmh->connection, + ewmh->_NET_WM_CM_Sn[screen_nbr]); +} + +xcb_get_selection_owner_cookie_t +xcb_ewmh_get_wm_cm_owner_unchecked(xcb_ewmh_connection_t *ewmh, + int screen_nbr) +{ + return xcb_get_selection_owner_unchecked(ewmh->connection, + ewmh->_NET_WM_CM_Sn[screen_nbr]); +} + +uint8_t +xcb_ewmh_get_wm_cm_owner_from_reply(xcb_window_t *owner, + xcb_get_selection_owner_reply_t *r) +{ + if(!r) + return 0; + + *owner = r->owner; + free(r); + return 1; +} + +uint8_t +xcb_ewmh_get_wm_cm_owner_reply(xcb_ewmh_connection_t *ewmh, + xcb_get_selection_owner_cookie_t cookie, + xcb_window_t *owner, + xcb_generic_error_t **e) +{ + xcb_get_selection_owner_reply_t *r = + xcb_get_selection_owner_reply(ewmh->connection, cookie, e); + + return xcb_ewmh_get_wm_cm_owner_from_reply(owner, r); +} + +/* TODO: section 2.1, 2.2 */ +static xcb_void_cookie_t +set_wm_cm_owner_client_message(xcb_ewmh_connection_t *ewmh, + int screen_nbr, + xcb_window_t owner, + xcb_timestamp_t timestamp, + uint32_t selection_data1, + uint32_t selection_data2) +{ + xcb_client_message_event_t ev; + memset(&ev, 0, sizeof(xcb_client_message_event_t)); + + ev.response_type = XCB_CLIENT_MESSAGE; + ev.format = 32; + ev.type = ewmh->MANAGER; + ev.data.data32[0] = timestamp; + ev.data.data32[1] = ewmh->_NET_WM_CM_Sn[screen_nbr]; + ev.data.data32[2] = owner; + ev.data.data32[3] = selection_data1; + ev.data.data32[4] = selection_data2; + + return xcb_send_event(ewmh->connection, 0, ewmh->screens[screen_nbr]->root, + XCB_EVENT_MASK_STRUCTURE_NOTIFY, + (char *) &ev); +} + +/* TODO: check both */ +xcb_void_cookie_t +xcb_ewmh_set_wm_cm_owner(xcb_ewmh_connection_t *ewmh, + int screen_nbr, + xcb_window_t owner, + xcb_timestamp_t timestamp, + uint32_t selection_data1, + uint32_t selection_data2) +{ + xcb_set_selection_owner(ewmh->connection, owner, + ewmh->_NET_WM_CM_Sn[screen_nbr], 0); + + return set_wm_cm_owner_client_message(ewmh, screen_nbr, owner, timestamp, + selection_data1, selection_data2); +} + +xcb_void_cookie_t +xcb_ewmh_set_wm_cm_owner_checked(xcb_ewmh_connection_t *ewmh, + int screen_nbr, + xcb_window_t owner, + xcb_timestamp_t timestamp, + uint32_t selection_data1, + uint32_t selection_data2) +{ + xcb_set_selection_owner_checked(ewmh->connection, owner, + ewmh->_NET_WM_CM_Sn[screen_nbr], 0); + + return set_wm_cm_owner_client_message(ewmh, screen_nbr, owner, timestamp, + selection_data1, selection_data2); +} |