2019-08-09 23:56:04 +00:00
|
|
|
#include <string.h>
|
2024-02-16 01:03:24 +00:00
|
|
|
#include <uthash.h>
|
2019-05-05 23:34:08 +00:00
|
|
|
#include <xcb/xcb.h>
|
|
|
|
|
|
|
|
#include "atom.h"
|
2024-02-15 21:08:48 +00:00
|
|
|
#include "cache.h"
|
2019-05-05 23:34:08 +00:00
|
|
|
#include "common.h"
|
2024-02-15 21:08:48 +00:00
|
|
|
#include "compiler.h"
|
2019-08-09 23:56:04 +00:00
|
|
|
#include "log.h"
|
2024-02-15 21:08:48 +00:00
|
|
|
#include "utils.h"
|
2019-05-05 23:34:08 +00:00
|
|
|
|
2024-02-16 01:00:50 +00:00
|
|
|
struct atom_entry {
|
|
|
|
struct cache_handle entry;
|
2024-02-16 01:03:24 +00:00
|
|
|
UT_hash_handle hh;
|
2024-02-16 01:00:50 +00:00
|
|
|
xcb_atom_t atom;
|
|
|
|
};
|
|
|
|
|
2024-02-17 01:55:21 +00:00
|
|
|
struct atom_impl {
|
|
|
|
struct atom base;
|
|
|
|
struct cache c;
|
|
|
|
struct atom_entry *atom_to_name;
|
2024-02-17 02:13:06 +00:00
|
|
|
cache_getter_t getter;
|
|
|
|
char *(*name_getter)(xcb_atom_t atom, xcb_connection_t *c);
|
2024-02-17 01:55:21 +00:00
|
|
|
};
|
|
|
|
|
2024-02-16 01:03:24 +00:00
|
|
|
static inline int atom_getter(struct cache *cache, const char *atom_name,
|
2024-02-16 00:31:53 +00:00
|
|
|
struct cache_handle **value, void *user_data) {
|
|
|
|
xcb_connection_t *c = user_data;
|
2024-02-17 01:55:21 +00:00
|
|
|
auto atoms = container_of(cache, struct atom_impl, c);
|
2019-05-05 23:34:08 +00:00
|
|
|
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
|
2024-02-16 00:31:53 +00:00
|
|
|
c, xcb_intern_atom(c, 0, to_u16_checked(strlen(atom_name)), atom_name), NULL);
|
2019-05-05 23:34:08 +00:00
|
|
|
|
|
|
|
xcb_atom_t atom = XCB_NONE;
|
|
|
|
if (reply) {
|
|
|
|
log_debug("Atom %s is %d", atom_name, reply->atom);
|
|
|
|
atom = reply->atom;
|
|
|
|
free(reply);
|
|
|
|
} else {
|
|
|
|
log_error("Failed to intern atoms");
|
2024-02-15 21:08:48 +00:00
|
|
|
return -1;
|
2019-05-05 23:34:08 +00:00
|
|
|
}
|
2024-02-15 21:08:48 +00:00
|
|
|
|
|
|
|
struct atom_entry *entry = ccalloc(1, struct atom_entry);
|
|
|
|
entry->atom = atom;
|
2024-02-16 01:03:24 +00:00
|
|
|
HASH_ADD_INT(atoms->atom_to_name, atom, entry);
|
2024-02-15 21:08:48 +00:00
|
|
|
*value = &entry->entry;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-02-17 02:13:06 +00:00
|
|
|
static inline char *atom_name_getter(xcb_atom_t atom, xcb_connection_t *c) {
|
|
|
|
auto r = xcb_get_atom_name_reply(c, xcb_get_atom_name(c, atom), NULL);
|
|
|
|
if (!r) {
|
|
|
|
log_error("Failed to get atom name");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
char *atom_name = strdup(xcb_get_atom_name_name(r));
|
|
|
|
free(r);
|
|
|
|
return atom_name;
|
|
|
|
}
|
|
|
|
|
2024-02-16 01:03:24 +00:00
|
|
|
static inline int
|
|
|
|
known_atom_getter(struct cache *cache attr_unused, const char *atom_name attr_unused,
|
|
|
|
struct cache_handle **value, void *user_data) {
|
|
|
|
auto atom = *(xcb_atom_t *)user_data;
|
|
|
|
struct atom_entry *entry = ccalloc(1, struct atom_entry);
|
|
|
|
entry->atom = atom;
|
|
|
|
*value = &entry->entry;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void atom_entry_free(struct cache *cache, struct cache_handle *handle) {
|
|
|
|
auto entry = cache_entry(handle, struct atom_entry, entry);
|
2024-02-17 01:55:21 +00:00
|
|
|
auto atoms = container_of(cache, struct atom_impl, c);
|
2024-02-16 01:03:24 +00:00
|
|
|
HASH_DEL(atoms->atom_to_name, entry);
|
2024-02-15 21:08:48 +00:00
|
|
|
free(entry);
|
2019-05-05 23:34:08 +00:00
|
|
|
}
|
|
|
|
|
2024-02-16 00:31:53 +00:00
|
|
|
xcb_atom_t get_atom(struct atom *a, const char *key, xcb_connection_t *c) {
|
|
|
|
struct cache_handle *entry = NULL;
|
2024-02-17 01:55:21 +00:00
|
|
|
auto atoms = container_of(a, struct atom_impl, base);
|
2024-02-17 02:13:06 +00:00
|
|
|
if (cache_get_or_fetch(&atoms->c, key, &entry, c, atoms->getter) < 0) {
|
2024-02-16 00:31:53 +00:00
|
|
|
log_error("Failed to get atom %s", key);
|
|
|
|
return XCB_NONE;
|
|
|
|
}
|
|
|
|
return cache_entry(entry, struct atom_entry, entry)->atom;
|
|
|
|
}
|
|
|
|
|
2024-02-16 01:00:50 +00:00
|
|
|
xcb_atom_t get_atom_cached(struct atom *a, const char *key) {
|
2024-02-17 01:55:21 +00:00
|
|
|
auto atoms = container_of(a, struct atom_impl, base);
|
|
|
|
return cache_entry(cache_get(&atoms->c, key), struct atom_entry, entry)->atom;
|
2024-02-16 01:00:50 +00:00
|
|
|
}
|
|
|
|
|
2024-02-16 01:03:24 +00:00
|
|
|
const char *get_atom_name(struct atom *a, xcb_atom_t atom, xcb_connection_t *c) {
|
|
|
|
struct atom_entry *entry = NULL;
|
2024-02-17 01:55:21 +00:00
|
|
|
auto atoms = container_of(a, struct atom_impl, base);
|
|
|
|
HASH_FIND(hh, atoms->atom_to_name, &atom, sizeof(xcb_atom_t), entry);
|
2024-02-16 01:03:24 +00:00
|
|
|
if (!entry) {
|
2024-02-17 02:13:06 +00:00
|
|
|
char *atom_name = atoms->name_getter(atom, c);
|
|
|
|
if (!atom_name) {
|
2024-02-16 01:03:24 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
struct cache_handle *handle = NULL;
|
2024-02-17 01:55:21 +00:00
|
|
|
cache_get_or_fetch(&atoms->c, atom_name, &handle, &atom, known_atom_getter);
|
2024-02-16 01:03:24 +00:00
|
|
|
entry = cache_entry(handle, struct atom_entry, entry);
|
2024-02-17 02:13:06 +00:00
|
|
|
free(atom_name);
|
2024-02-16 01:03:24 +00:00
|
|
|
}
|
2024-02-17 01:55:21 +00:00
|
|
|
HASH_ADD_INT(atoms->atom_to_name, atom, entry);
|
2024-02-16 01:03:24 +00:00
|
|
|
return entry->entry.key;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *get_atom_name_cached(struct atom *a, xcb_atom_t atom) {
|
|
|
|
struct atom_entry *entry = NULL;
|
2024-02-17 01:55:21 +00:00
|
|
|
auto atoms = container_of(a, struct atom_impl, base);
|
|
|
|
HASH_FIND(hh, atoms->atom_to_name, &atom, sizeof(xcb_atom_t), entry);
|
2024-02-16 01:03:24 +00:00
|
|
|
if (!entry) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return entry->entry.key;
|
|
|
|
}
|
|
|
|
|
2019-05-05 23:34:08 +00:00
|
|
|
/**
|
2019-05-06 00:24:38 +00:00
|
|
|
* Create a new atom structure and fetch all predefined atoms
|
2019-05-05 23:34:08 +00:00
|
|
|
*/
|
|
|
|
struct atom *init_atoms(xcb_connection_t *c) {
|
2024-02-17 01:55:21 +00:00
|
|
|
auto atoms = ccalloc(1, struct atom_impl);
|
2024-02-16 00:31:53 +00:00
|
|
|
atoms->c = CACHE_INIT;
|
2024-02-17 02:13:06 +00:00
|
|
|
atoms->getter = atom_getter;
|
|
|
|
atoms->name_getter = atom_name_getter;
|
2024-02-17 01:55:21 +00:00
|
|
|
#define ATOM_GET(x) atoms->base.a##x = get_atom(&atoms->base, #x, c)
|
2020-10-22 00:39:51 +00:00
|
|
|
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
|
|
|
|
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
|
2019-05-05 23:34:08 +00:00
|
|
|
#undef ATOM_GET
|
2024-02-17 01:55:21 +00:00
|
|
|
return &atoms->base;
|
2019-05-05 23:34:08 +00:00
|
|
|
}
|
2024-02-15 21:08:48 +00:00
|
|
|
|
|
|
|
void destroy_atoms(struct atom *a) {
|
2024-02-17 01:55:21 +00:00
|
|
|
auto atoms = container_of(a, struct atom_impl, base);
|
|
|
|
cache_invalidate_all(&atoms->c, atom_entry_free);
|
2024-02-15 21:08:48 +00:00
|
|
|
free(a);
|
|
|
|
}
|
2024-02-17 02:13:46 +00:00
|
|
|
|
|
|
|
#ifdef UNIT_TEST
|
|
|
|
|
|
|
|
static inline int mock_atom_getter(struct cache *cache, const char *atom_name attr_unused,
|
|
|
|
struct cache_handle **value, void *user_data attr_unused) {
|
|
|
|
auto atoms = container_of(cache, struct atom_impl, c);
|
|
|
|
xcb_atom_t atom = (xcb_atom_t)HASH_COUNT(atoms->atom_to_name) + 1;
|
|
|
|
struct atom_entry *entry = ccalloc(1, struct atom_entry);
|
|
|
|
entry->atom = atom;
|
|
|
|
HASH_ADD_INT(atoms->atom_to_name, atom, entry);
|
|
|
|
*value = &entry->entry;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline char *
|
|
|
|
mock_atom_name_getter(xcb_atom_t atom attr_unused, xcb_connection_t *c attr_unused) {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2024-02-17 02:21:50 +00:00
|
|
|
struct atom *init_mock_atoms(void) {
|
2024-02-17 02:13:46 +00:00
|
|
|
auto atoms = ccalloc(1, struct atom_impl);
|
|
|
|
atoms->c = CACHE_INIT;
|
|
|
|
atoms->getter = mock_atom_getter;
|
|
|
|
atoms->name_getter = mock_atom_name_getter;
|
2024-02-17 02:21:50 +00:00
|
|
|
#define ATOM_GET(x) atoms->base.a##x = get_atom(&atoms->base, #x, NULL)
|
2024-02-17 02:13:46 +00:00
|
|
|
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
|
|
|
|
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
|
|
|
|
#undef ATOM_GET
|
|
|
|
return &atoms->base;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2024-02-17 02:21:50 +00:00
|
|
|
struct atom *init_mock_atoms(void) {
|
2024-02-17 02:13:46 +00:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|