From 162b8a690c7e7338d6e1e5c27472cf10f1af6ee3 Mon Sep 17 00:00:00 2001 From: Arnaud Fontaine Date: Sat, 10 May 2008 23:30:20 +0100 Subject: [PATCH] [all] Implement an atom cache in xutil as an ordered linked-list --- awesome.c | 3 +- client.c | 51 +++++++++++++++++----- common/xutil.c | 114 +++++++++++++++++++++++++++++++++++++++++-------- common/xutil.h | 63 ++++++++++++++++++++++----- event.c | 5 ++- rules.c | 8 +++- structs.h | 2 + window.c | 23 +++++++--- 8 files changed, 219 insertions(+), 50 deletions(-) diff --git a/awesome.c b/awesome.c index 228dd4f2..c4892f29 100644 --- a/awesome.c +++ b/awesome.c @@ -386,7 +386,8 @@ main(int argc, char **argv) xutil_getlockmask(globalconf.connection, globalconf.keysyms, &globalconf.numlockmask, &globalconf.shiftlockmask, &globalconf.capslockmask); - /* init EWMH atoms */ + /* init Atoms cache and then EWMH atoms */ + atom_cache_list_init(&globalconf.atoms); ewmh_init_atoms(); /* init screens struct */ diff --git a/client.c b/client.c index 0eb8af68..9eab2bc3 100644 --- a/client.c +++ b/client.c @@ -60,8 +60,11 @@ client_loadprops(client_t * c, int screen) for(tag = globalconf.screens[screen].tags; tag; tag = tag->next) ntags++; - if(xutil_gettextprop(globalconf.connection, c->win, - xutil_intern_atom(globalconf.connection, "_AWESOME_PROPERTIES"), + if(xutil_gettextprop(globalconf.connection, c->win, &globalconf.atoms, + xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "_AWESOME_PROPERTIES")), &prop)) { for(i = 0, tag = globalconf.screens[screen].tags; tag && i < ntags && prop[i]; i++, tag = tag->next) @@ -90,13 +93,19 @@ static bool client_isprotodel(xcb_window_t win) { uint32_t i, n; + xcb_atom_t wm_delete_win_atom; xcb_atom_t *protocols; bool ret = false; if(xcb_get_wm_protocols(globalconf.connection, win, &n, &protocols)) { + wm_delete_win_atom = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "WM_DELETE_WINDOW")); + for(i = 0; !ret && i < n; i++) - if(protocols[i] == xutil_intern_atom(globalconf.connection, "WM_DELETE_WINDOW")) + if(protocols[i] == wm_delete_win_atom) ret = true; p_delete(&protocols); } @@ -188,11 +197,18 @@ client_updatetitle(client_t *c) { char *name; - if(!xutil_gettextprop(globalconf.connection, c->win, - xutil_intern_atom(globalconf.connection, "_NET_WM_NAME"), + if(!xutil_gettextprop(globalconf.connection, c->win, &globalconf.atoms, + xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "_NET_WM_NAME")), &name)) - if(!xutil_gettextprop(globalconf.connection, c->win, - xutil_intern_atom(globalconf.connection, "WM_NAME"), + if(!xutil_gettextprop(globalconf.connection, c->win, &globalconf.atoms, + xutil_intern_atom_reply(globalconf.connection, + &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "WM_NAME")), &name)) return; @@ -687,7 +703,10 @@ client_saveprops(client_t *c) prop[++i] = '\0'; xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, c->win, - xutil_intern_atom(globalconf.connection, "_AWESOME_PROPERTIES"), + xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "_AWESOME_PROPERTIES")), STRING, 8, i, (unsigned char *) prop); p_delete(&prop); @@ -871,7 +890,11 @@ uicb_client_settrans(int screen __attribute__ ((unused)), char *arg) prop_r = xcb_get_property_reply(globalconf.connection, xcb_get_property_unchecked(globalconf.connection, false, sel->win, - xutil_intern_atom(globalconf.connection, "_NET_WM_WINDOW_OPACITY"), + xutil_intern_atom_reply(globalconf.connection, + &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "_NET_WM_WINDOW_OPACITY")), CARDINAL, 0, 1), NULL); @@ -1048,10 +1071,16 @@ client_kill(client_t *c) ev.response_type = XCB_CLIENT_MESSAGE; ev.window = c->win; - ev.type = xutil_intern_atom(globalconf.connection, "WM_PROTOCOLS"); + ev.type = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "WM_PROTOCOLS")); ev.format = 32; - ev.data.data32[0] = xutil_intern_atom(globalconf.connection, "WM_DELETE_WINDOW"); + ev.data.data32[0] = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "WM_DELETE_WINDOW")); ev.data.data32[1] = XCB_CURRENT_TIME; xcb_send_event(globalconf.connection, false, c->win, diff --git a/common/xutil.c b/common/xutil.c index 7fda19ff..0ca4f0cf 100644 --- a/common/xutil.c +++ b/common/xutil.c @@ -33,13 +33,14 @@ /** Get the string value of an atom. * \param conn X connection * \param w window + * \param atoms atoms cache * \param atom the atom * \param text buffer to fill * \return true on sucess, falsse on failure */ bool -xutil_gettextprop(xcb_connection_t *conn, xcb_window_t w, xcb_atom_t atom, - char **text) +xutil_gettextprop(xcb_connection_t *conn, xcb_window_t w, xutil_atom_cache_t **atoms, + xcb_atom_t atom, char **text) { xcb_get_property_cookie_t prop_c; xcb_get_property_reply_t *prop_r; @@ -67,7 +68,9 @@ xutil_gettextprop(xcb_connection_t *conn, xcb_window_t w, xcb_atom_t atom, * string or utf8 string. At the moment it doesn't handle * COMPOUND_TEXT and multibyte but it's not needed... */ if(prop_r->type == STRING || - prop_r->type == xutil_intern_atom(conn, "UTF8_STRING")) + prop_r->type == xutil_intern_atom_reply(conn, atoms, + xutil_intern_atom(conn, atoms, + "UTF8_STRING"))) { *text = p_new(char, prop_r->value_len + 1); /* use memcpy() because prop_val may not be \0 terminated */ @@ -152,29 +155,106 @@ xutil_get_transient_for_hint(xcb_connection_t *c, xcb_window_t win, return true; } -/** Get an internal atom. +/** Send an unchecked InternAtom request if it is not already in the + * cache, in the second case it stores the cache entry (an ordered + * linked-list) * \param c X connection + * \param atoms atoms cache * \param property atom name - * \return an brand new xcb_atom_t + * \return a request structure + */ +xutil_intern_atom_request_t +xutil_intern_atom(xcb_connection_t *c, xutil_atom_cache_t **atoms, + const char *name) +{ + xutil_intern_atom_request_t atom_req; + xutil_atom_cache_t *atom_next; + int cmp_cache; + + atom_req.name = strdup(name); + + /* Check if this atom is present in the cache ordered + * linked-list */ + for(atom_next = *atoms; + atom_next && (cmp_cache = a_strcmp(name, atom_next->name)) >= 0; + atom_next = atom_cache_list_next(NULL, atom_next)) + if(cmp_cache == 0) + { + atom_req.cache_hit = true; + atom_req.cache = atom_next; + return atom_req; + } + + /* Otherwise send an InternAtom request to the server */ + atom_req.cache_hit = false; + atom_req.cookie = xcb_intern_atom_unchecked(c, false, a_strlen(name), + name); + + return atom_req; +} + +/** Treat the reply which may be a cache entry or a reply from + * InternAtom request (cookie), in the second case, add the atom to + * the cache + * \param c X connection + * \param atoms atoms cache + * \param atom_req atom request + * \return a brand new xcb_atom_t */ xcb_atom_t -xutil_intern_atom(xcb_connection_t *c, const char *property) +xutil_intern_atom_reply(xcb_connection_t *c, xutil_atom_cache_t **atoms, + xutil_intern_atom_request_t atom_req) { - xcb_atom_t atom = 0; - xcb_intern_atom_reply_t *r_atom; + xcb_intern_atom_reply_t *atom_rep; + xutil_atom_cache_t *atom_cache, *atom_next; - if((r_atom = xcb_intern_atom_reply(c, - xcb_intern_atom_unchecked(c, - false, - a_strlen(property), - property), - NULL))) + /* If the atom is present in the cache, just returns the + * atom... */ + if(atom_req.cache_hit) + return atom_req.cache->atom; + + /* Get the reply from InternAtom request */ + if((atom_rep = xcb_intern_atom_reply(c, atom_req.cookie, NULL)) == NULL) + return 0; + + /* Create a new atom cache entry */ + atom_cache = p_new(xutil_atom_cache_t, 1); + atom_cache->atom = atom_rep->atom; + atom_cache->name = atom_req.name; + + /* Add the entry in the list at the beginning of the cache list */ + if(*atoms == NULL || a_strcmp(atom_req.name, (*atoms)->name) < 0) + atom_cache_list_push(atoms, atom_cache); + /* Otherwise insert it at the proper position in the cache list + * according to its name */ + else { - atom = r_atom->atom; - p_delete(&r_atom); + for(atom_next = *atoms; + atom_next && atom_next->next && a_strcmp(atom_req.name, atom_next->next->name) > 0; + atom_next = atom_cache_list_next(NULL, atom_next)); + + atom_cache->prev = atom_next; + atom_cache->next = atom_next->next; + + if(atom_next->next) + atom_next->next->prev = atom_cache; + + atom_next->next = atom_cache; } - return atom; + p_delete(&atom_rep); + + return atom_cache->atom; +} + +/* Delete a cache entry + * \param entry cache entry + */ +void +xutil_atom_cache_delete(xutil_atom_cache_t **entry) +{ + p_delete(&(*entry)->name); + p_delete(entry); } class_hint_t * diff --git a/common/xutil.h b/common/xutil.h index 7c28a6ce..d06fff27 100644 --- a/common/xutil.h +++ b/common/xutil.h @@ -31,9 +31,7 @@ /* XCB doesn't provide keysyms definition */ #include -bool xutil_gettextprop(xcb_connection_t *, xcb_window_t, xcb_atom_t, char **); -void xutil_getlockmask(xcb_connection_t *, xcb_key_symbols_t *, - unsigned int *, unsigned int *, unsigned int *); +#include "common/list.h" /* See http://tronche.com/gui/x/xlib/appendix/b/ for values */ #define CURSOR_FLEUR 52 @@ -81,7 +79,7 @@ void xutil_getlockmask(xcb_connection_t *, xcb_key_symbols_t *, /* Common function defined in Xlib but not in XCB */ bool xutil_get_transient_for_hint(xcb_connection_t *, xcb_window_t, xcb_window_t *); -typedef struct _class_hint_t +typedef struct { char *res_name; char *res_class; @@ -97,18 +95,61 @@ typedef struct class_hint_t *xutil_get_class_hint(xcb_connection_t *, xcb_window_t); -/* Equivalent call to XInternAtom - * - * WARNING: should not be used in loop, in this case, it should send - * the queries first and then treat the answer as late as possible) - */ -xcb_atom_t xutil_intern_atom(xcb_connection_t *, const char *); +/** Cache entry */ +typedef struct xutil_atom_cache_t xutil_atom_cache_t; +struct xutil_atom_cache_t +{ + /** Atom X identifier */ + xcb_atom_t atom; + /** Atom name */ + char *name; + /** Next and previous atom cache entries */ + xutil_atom_cache_t *prev, *next; +}; + +/** InternAtom request data structure which may hold the cookie if the + * atom is not already present in the cache */ +typedef struct +{ + /* Cache hit */ + bool cache_hit; + /* Atom string name */ + char *name; + union + { + /* Cookie of the InternAtom request */ + xcb_intern_atom_cookie_t cookie; + /* Cache entry */ + xutil_atom_cache_t *cache; + }; +} xutil_intern_atom_request_t; + +/* InternATom request which relies on a cache stored as a ordered + * linked-list */ +xutil_intern_atom_request_t xutil_intern_atom(xcb_connection_t *, xutil_atom_cache_t **, + const char *); + +/** Treat reply from InternAtom request */ +xcb_atom_t xutil_intern_atom_reply(xcb_connection_t *, xutil_atom_cache_t **, + xutil_intern_atom_request_t); + +/** Delete a entry in the cache */ +void xutil_atom_cache_delete(xutil_atom_cache_t **); + +/** Cache list utils functions */ +DO_SLIST(xutil_atom_cache_t, atom_cache, xutil_atom_cache_delete) + +bool xutil_gettextprop(xcb_connection_t *, xcb_window_t, xutil_atom_cache_t **, + xcb_atom_t, char **); + +void xutil_getlockmask(xcb_connection_t *, xcb_key_symbols_t *, + unsigned int *, unsigned int *, unsigned int *); /** Set the same handler for all errors */ void xutil_set_error_handler_catch_all(xcb_event_handlers_t *, xcb_generic_error_handler_t, void *); -typedef struct xutil_error_t +typedef struct { uint8_t request_code; char *request_label; diff --git a/event.c b/event.c index 6498d08c..04405b68 100644 --- a/event.c +++ b/event.c @@ -520,7 +520,10 @@ event_handle_propertynotify(void *data __attribute__ ((unused)), client_updatewmhints(c); if(ev->atom == WM_NAME - || ev->atom == xutil_intern_atom(globalconf.connection, "_NET_WM_NAME")) + || ev->atom == xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "_NET_WM_NAME"))) client_updatetitle(c); } diff --git a/rules.c b/rules.c index adca0686..19c3dbc9 100644 --- a/rules.c +++ b/rules.c @@ -83,8 +83,12 @@ rule_matching_client(client_t *c) if(!ret && r->xprop && r->xpropval_r - && xutil_gettextprop(globalconf.connection, c->win, - xutil_intern_atom(globalconf.connection, r->xprop), + && xutil_gettextprop(globalconf.connection, c->win, &globalconf.atoms, + xutil_intern_atom_reply(globalconf.connection, + &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + r->xprop)), &buf)) ret = !regexec(r->xpropval_r, buf, 1, &tmp, 0); diff --git a/structs.h b/structs.h index 8c7b428c..68fd4514 100644 --- a/structs.h +++ b/structs.h @@ -380,6 +380,8 @@ struct AwesomeConf char *argv; /** Last XMotionEvent coords */ int pointer_x, pointer_y; + /** Atoms cache */ + xutil_atom_cache_t *atoms; }; #endif diff --git a/window.c b/window.c index 29a118f7..7fec3b29 100644 --- a/window.c +++ b/window.c @@ -41,11 +41,13 @@ void window_setstate(xcb_window_t win, long state) { long data[] = { state, XCB_NONE }; + const xcb_atom_t wm_state_atom = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "WM_STATE")); xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, win, - xutil_intern_atom(globalconf.connection, "WM_STATE"), - xutil_intern_atom(globalconf.connection, "WM_STATE"), 32, - 2, data); + wm_state_atom, wm_state_atom, 32, 2, data); } /** Get a window state (WM_STATE). @@ -58,7 +60,10 @@ window_getstate(xcb_window_t w) long result = -1; unsigned char *p = NULL; xcb_get_property_cookie_t prop_c; - xcb_atom_t wm_state_atom = xutil_intern_atom(globalconf.connection, "WM_STATE"); + xcb_atom_t wm_state_atom = xutil_intern_atom_reply(globalconf.connection, &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "WM_STATE")); xcb_get_property_reply_t *prop_r; prop_c = xcb_get_property_unchecked(globalconf.connection, false, w, @@ -224,17 +229,21 @@ void window_settrans(xcb_window_t win, double opacity) { unsigned int real_opacity = 0xffffffff; + const xcb_atom_t wopacity_atom = xutil_intern_atom_reply(globalconf.connection, + &globalconf.atoms, + xutil_intern_atom(globalconf.connection, + &globalconf.atoms, + "_NET_WM_WINDOW_OPACITY")); if(opacity >= 0 && opacity <= 1) { real_opacity = opacity * 0xffffffff; xcb_change_property(globalconf.connection, XCB_PROP_MODE_REPLACE, win, - xutil_intern_atom(globalconf.connection, "_NET_WM_WINDOW_OPACITY"), - CARDINAL, 32, 1L, &real_opacity); + wopacity_atom, CARDINAL, 32, 1L, &real_opacity); } else xcb_delete_property(globalconf.connection, win, - xutil_intern_atom(globalconf.connection, "_NET_WM_WINDOW_OPACITY")); + wopacity_atom); } // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80