From ac1b1cd393b99fd5302fef32544e2e0d6f349cbf Mon Sep 17 00:00:00 2001 From: William Swanson Date: Sun, 18 Nov 2012 22:36:15 -0800 Subject: [PATCH util] Create the atom-list sub-library --- README | 1 + src/Makefile.am | 5 +- src/atom_list.c | 52 ++++++++++++++ src/xcb_atom_list.h | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/xcb_map.h | 134 ++++++++++++++++++++++++++++++++++++ src/xcb_util.h | 1 + 6 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 src/atom_list.c create mode 100644 src/xcb_atom_list.h create mode 100644 src/xcb_map.h diff --git a/README b/README index 7c87666..f69e23d 100644 --- a/README +++ b/README @@ -27,6 +27,7 @@ XCB util module provides the following library: - aux: Convenient access to connection setup and some core requests. - atom: Standard core X atom constants and atom caching. + - atom-list: Macros for automatically generating lists of atoms. - event: Some utilities that have little to do with events any more. You can obtain the latest development versions of XCB util using diff --git a/src/Makefile.am b/src/Makefile.am index 2545ad6..e678fa2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,12 +6,15 @@ AM_CFLAGS = $(CWARNFLAGS) libxcb_util_la_LIBADD = $(XCB_LIBS) libxcb_util_la_LDFLAGS = -version-info 1:0:0 -no-undefined libxcb_util_la_SOURCES = \ + atom_list.c \ atoms.c \ event.c \ xcb_aux.c xcbinclude_HEADERS = \ xcb_util.h \ + xcb_atom_list.h \ xcb_atom.h \ xcb_aux.h \ - xcb_event.h + xcb_event.h \ + xcb_map.h diff --git a/src/atom_list.c b/src/atom_list.c new file mode 100644 index 0000000..c2e10c1 --- /dev/null +++ b/src/atom_list.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 William Swanson + * + * 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. + */ + +#include "xcb_atom_list.h" +#include + +uint8_t +xcb_atom_list_init_reply (xcb_connection_t *c, + xcb_intern_atom_cookie_t cookies[], + xcb_atom_t out[], + unsigned count, + xcb_generic_error_t **e) +{ + unsigned i; + + for (i = 0; i < count; ++i) + { + xcb_intern_atom_reply_t *r = xcb_intern_atom_reply (c, cookies[i], e); + if (!r) + return 0; + + out[i] = r->atom; + free (r); + } + + return 1; +} diff --git a/src/xcb_atom_list.h b/src/xcb_atom_list.h new file mode 100644 index 0000000..98f9d2a --- /dev/null +++ b/src/xcb_atom_list.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2012 William Swanson + * + * 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. + */ + +#ifndef __XCB_ATOM_LIST_H__ +#define __XCB_ATOM_LIST_H__ + +/** @file xcb_atom_list.h + * + * Helper macros for automatically creating atom-list structures and their + * associated initialization functions. + * + * To use these macros, first define a list of atoms to include in the + * structure: + * + * #define MY_ATOMS WM_PROTOCOLS, WM_DELETE_WINDOW + * + * Next, declare the atom-list structure and implement the helper functions + * using the XCB_ATOM_LIST macros and the list defined above: + * + * XCB_ATOM_LIST_DECLARE (my_atoms, MY_ATOMS); + * XCB_ATOM_LIST_DEFINE (my_atoms, MY_ATOMS); + * + * These can both go in the same source file, or the declaration can move to + * a header file if necessary. The `my_atoms` parameter acts as a prefix + * for the the various structure and function names these macros declare. + * + * Finally, in your main function or other suitable location, simply call the + * initialization function and its reply handler: + * + * my_atoms_init_cookie_t cookie; + * my_atoms_t atoms; + * + * cookie = my_atoms_init (connection); + * if (!my_atoms_init_reply (connection, &cookie, &atoms, NULL); + * { + * ... error handling ... + * } + * + * At this point, all the atoms in the list now exist and are available by + * name, like `atoms.WM_PROTOCOLS`. + */ + +#include "xcb_map.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Internal helper macros: */ +#define XCB_ATOM_LIST__MEMBER(x) xcb_atom_t x; +#define XCB_ATOM_LIST__STRING(x) #x +#define XCB_ATOM_LIST__STRLEN(x) sizeof(#x) - 1, + +/** + * Counts the number of items in an atom-list structure. + */ +#define XCB_ATOM_LIST_COUNT(type) sizeof(type)/sizeof(xcb_atom_t) + +/** + * Declares an atom-list structure and its associated initialization + * functions. + * + * The atom-list structure has the name `name_t`, where "name" is the base + * name passed into this macro. Each atom appears by name as a structure + * member. + * + * The initialization function has the following prototype: + * + * name_init_cookie_t + * name_init (xcb_connection_t *c); + * + * This function returns a cookie structure, which should be passed to the + * following response function to actually retrieve the atoms: + * + * uint8_t + * name_init_reply (xcb_connection_t *c, + * name_init_cookie_t *cookie, + * name_t *out, + * xcb_generic_error_t **e); + * + * The return value is non-zero for success or 0 for failure. + * + * @param name The base name for the declared types and functions + * @param ... The atoms to include in the list + */ +#define XCB_ATOM_LIST_DECLARE(name, ...) \ + \ +typedef struct name##_t { \ + XCB__MAP(XCB_ATOM_LIST__MEMBER, __VA_ARGS__) \ +} name##_t; \ + \ +typedef struct name##_cookie_t { \ + xcb_intern_atom_cookie_t cookies[XCB_ATOM_LIST_COUNT (name##_t)]; \ +} name##_init_cookie_t; \ + \ +name##_init_cookie_t \ +name##_init (xcb_connection_t *c); \ + \ +uint8_t \ +name##_init_reply (xcb_connection_t *c, \ + name##_init_cookie_t *cookie, \ + name##_t *out, \ + xcb_generic_error_t **e); + +/** + * Implements the atom-list initialization functions. These implementations + * should be placed in a compiled source file. + * + * @see XCB_ATOM_LIST_DECLARE + * + * @param name The base name for the declared types and functions + * @param ... The atoms to include in the list + */ +#define XCB_ATOM_LIST_DEFINE(name, ...) \ + \ +name##_init_cookie_t \ +name##_init (xcb_connection_t *c) \ +{ \ + name##_init_cookie_t out; \ + char const *string = XCB__MAP (XCB_ATOM_LIST__STRING, __VA_ARGS__); \ + unsigned const sizes[] = {XCB__MAP (XCB_ATOM_LIST__STRLEN, __VA_ARGS__) 0}; \ + unsigned i; \ + \ + for (i = 0; i < XCB_ATOM_LIST_COUNT (name##_t); ++i) \ + { \ + out.cookies[i] = xcb_intern_atom(c, 0, sizes[i], string); \ + string += sizes[i]; \ + } \ + \ + return out; \ +} \ + \ +uint8_t \ +name##_init_reply (xcb_connection_t *c, \ + name##_init_cookie_t *cookie, \ + name##_t *out, \ + xcb_generic_error_t **e) \ +{ \ + return xcb_atom_list_init_reply (c, cookie->cookies, (xcb_atom_t*)out, \ + XCB_ATOM_LIST_COUNT (name##_t), e); \ +} + +/** + * Extracts a list of atoms from a list of request cookies. + * + * Rather than call this function directly, consider using the XCB_ATOM_LIST + * macros to automatically create the appropriate wrappers. + * + * @see XCB_ATOM_LIST_DECLARE + * + * @param c The server connection + * @param cookies A list of cookies from the request function + * @param out The output atom array + * @param count The number of cookies in the array + * @param e A pointer to receive any error messages + * @return Non-zero for success, or 0 for failure + */ +uint8_t +xcb_atom_list_init_reply (xcb_connection_t *c, + xcb_intern_atom_cookie_t cookies[], + xcb_atom_t out[], + unsigned count, + xcb_generic_error_t **e); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/xcb_map.h b/src/xcb_map.h new file mode 100644 index 0000000..66e20f9 --- /dev/null +++ b/src/xcb_map.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2012 William Swanson + * + * 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. + */ + +#ifndef __XCB_MAP_H__ +#define __XCB_MAP_H__ + +/* +The goal is to create a macro which performs some operation each element of +a list. Doing that requires recursion, though, which the C preprocessor +doesn't allow. Fortunately, there is a workaround. + +First, we need a technique for emitting something that looks like a macro +call, but isn't yet: +*/ + +#define XCB__MAP_OUT + +/* +Imagine we have the following macros: + + #define A(x) x B XCB__MAP_OUT (x) + #define B(x) x A XCB__MAP_OUT (x) + +Evaluating the macro `A (blah)` produces the output text: + + blah B (blah) + +The preprocessor doesn't see any recursion, since the `B (blah)` call is +just plain text at this point, and `B` isn't even the name of the current +macro. Feeding this text back into the preprocessor expands the call, +producing the output: + + blah blah A (blah) + +Evaluating the output a third time expands the `A (blah)` macro, carrying +the recursion full-circle. The recursion continues as long as the caller +continues to feed the output text back into the preprocessor. + +To perform these repeated evaluations, the following `XCB__EVAL` macro passes +its arguments down a tree of macro calls: +*/ + +#define XCB__EVAL0(...) __VA_ARGS__ +#define XCB__EVAL1(...) XCB__EVAL0 (XCB__EVAL0 (XCB__EVAL0 (__VA_ARGS__))) +#define XCB__EVAL2(...) XCB__EVAL1 (XCB__EVAL1 (XCB__EVAL1 (__VA_ARGS__))) +#define XCB__EVAL3(...) XCB__EVAL2 (XCB__EVAL2 (XCB__EVAL2 (__VA_ARGS__))) +#define XCB__EVAL4(...) XCB__EVAL3 (XCB__EVAL3 (XCB__EVAL3 (__VA_ARGS__))) +#define XCB__EVAL(...) XCB__EVAL4 (XCB__EVAL4 (XCB__EVAL4 (__VA_ARGS__))) + +/* +Each level multiplies the effort of the level before, evaluating the input +365 times in total. In other words, calling `XCB__EVAL (A (blah))` would +produce 365 copies of the word `blah`, followed by a final un-evaluated `B +(blah)`. This provides the basic framework for recursion, at least within a +certain stack depth. + +The next challenge is to stop the recursion when it reaches the end of the +list. + +The basic idea is to emit the following macro name instead of the normal +recursive macro when the time comes to quit: +*/ + +#define XCB__MAP_END(...) + +/* +Evaluating this macro does nothing, which ends the recursion. + +To actually select between the two macros, the following `XCB__MAP_NEXT` +macro compares a single list item against the special end-of-list marker +`()`. The macro returns `XCB__MAP_END` if the item matches, or the `next` +parameter if the item is anything else: +*/ + +#define XCB__MAP_GET_END() 0, XCB__MAP_END +#define XCB__MAP_NEXT0(item, next, ...) next XCB__MAP_OUT +#define XCB__MAP_NEXT1(item, next) XCB__MAP_NEXT0 (item, next, 0) +#define XCB__MAP_NEXT(item, next) XCB__MAP_NEXT1 (XCB__MAP_GET_END item, next) + +/* +This macro works by placing the item next to the `XCB__MAP_GET_END` macro. +If doing that forms a macro call, everything moves over by a slot in the +`XCB__MAP_NEXT0` parameter list, changing the output. The `XCB__MAP_OUT` +trick prevents the preprocessor from evaluating the final result. + +With these pieces in place, it is now possible to implement useful versions +of the `A` and `B` macros from the example above: +*/ + +#define XCB__MAP0(f, x, peek, ...) f(x) XCB__MAP_NEXT (peek, XCB__MAP1) (f, peek, __VA_ARGS__) +#define XCB__MAP1(f, x, peek, ...) f(x) XCB__MAP_NEXT (peek, XCB__MAP0) (f, peek, __VA_ARGS__) + +/* +These macros apply the operation `f` to the current list item `x`. They then +examine the next list item, `peek`, to see if they should continue or not. + +The final step is to tie everything together in a top-level `XCB__MAP` macro: +*/ + +#define XCB__MAP(f, ...) XCB__EVAL (XCB__MAP1 (f, __VA_ARGS__, (), 0)) + +/* +This macro places a `()` marker on the end of the list, as well as an extra +`0` for ANSI compliance (otherwise, the last iteration would have an illegal +0-length list). It then passes the whole thing through `XCB__EVAL` and +returns the result. +*/ + +#endif diff --git a/src/xcb_util.h b/src/xcb_util.h index 0f06f1b..2a13ce4 100644 --- a/src/xcb_util.h +++ b/src/xcb_util.h @@ -1,6 +1,7 @@ #ifndef __XCB_UTIL_H__ #define __XCB_UTIL_H__ +#include #include #include #include -- 1.8.0