From 2a9ba5c8dc21bfd167fc13c46b4d853cb0c70bce Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sat, 3 Mar 2018 10:40:52 +0100 Subject: [PATCH 1/8] xcb errors: log raw values --- xwayland/xwm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index d9899c57..549a3333 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -954,6 +954,12 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm, } } +static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { + wlr_log(L_ERROR, "xcb error: code %d, sequence %d, value %d, opcode %d:%d", + ev->error_code, ev->sequence, ev->bad_value, + ev->minor_opcode, ev->major_opcode); +} + /* This is in xcb/xcb_event.h, but pulling xcb-util just for a constant * others redefine anyway is meh */ @@ -1010,9 +1016,12 @@ static int x11_event_handler(int fd, uint32_t mask, void *data) { case XCB_FOCUS_IN: xwm_handle_focus_in(xwm, (xcb_focus_in_event_t *)event); break; + case 0: + xwm_handle_xcb_error(xwm, (xcb_value_error_t *)event); + break; default: - wlr_log(L_DEBUG, "X11 event: %d", - event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK); + wlr_log(L_DEBUG, "unhandled X11 event: %d", + event->response_type); break; } free(event); From f8428d1063c6420ee43424269149a65f8c33812f Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sat, 3 Mar 2018 11:37:02 +0100 Subject: [PATCH 2/8] xcb errors: optional dependency with improved messages Now message can look like: [xwayland/xwm.c:991] xcb error: op ChangeProperty (no minor), code Window (no extension), value 6291465 instead of this one when the lib is not available: [xwayland/xwm.c:999] xcb error: op 18:0, code 3, sequence 103, value 6291465 The value in case of Window is the window id, so we can tell what function applied on which window which is a good start. The sequence ought to be able to tell us more precisely which invocation it was, but we never log it when calling functions so is useless in practice and no longer logged. --- meson.build | 15 ++++++++++---- meson_options.txt | 1 + xwayland/meson.build | 1 + xwayland/xwm.c | 47 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/meson.build b/meson.build index d5ae7863..1188f318 100644 --- a/meson.build +++ b/meson.build @@ -64,6 +64,7 @@ xcb_xfixes = dependency('xcb-xfixes') xcb_image = dependency('xcb-image') xcb_render = dependency('xcb-render') xcb_icccm = dependency('xcb-icccm', required: false) +xcb_errors = dependency('xcb-errors', required: get_option('enable_xcb_errors') == 'true') x11_xcb = dependency('x11-xcb') libcap = dependency('libcap', required: get_option('enable_libcap') == 'true') systemd = dependency('libsystemd', required: get_option('enable_systemd') == 'true') @@ -78,6 +79,10 @@ if xcb_icccm.found() conf_data.set('WLR_HAS_XCB_ICCCM', true) endif +if xcb_errors.found() and get_option('enable_xcb_errors') != 'false' + conf_data.set('WLR_HAS_XCB_ERRORS', true) +endif + if libcap.found() and get_option('enable_libcap') != 'false' conf_data.set('WLR_HAS_LIBCAP', true) wlr_deps += libcap @@ -164,10 +169,12 @@ summary = [ '----------------', 'wlroots @0@'.format(meson.project_version()), '', - ' libcap: @0@'.format(conf_data.get('WLR_HAS_LIBCAP', false)), - ' systemd: @0@'.format(conf_data.get('WLR_HAS_SYSTEMD', false)), - ' elogind: @0@'.format(conf_data.get('WLR_HAS_ELOGIND', false)), - ' xwayland: @0@'.format(conf_data.get('WLR_HAS_XWAYLAND', false)), + ' libcap: @0@'.format(conf_data.get('WLR_HAS_LIBCAP', false)), + ' systemd: @0@'.format(conf_data.get('WLR_HAS_SYSTEMD', false)), + ' elogind: @0@'.format(conf_data.get('WLR_HAS_ELOGIND', false)), + ' xwayland: @0@'.format(conf_data.get('WLR_HAS_XWAYLAND', false)), + ' xcb-icccm: @0@'.format(conf_data.get('WLR_HAS_XCB_ICCCM', false)), + ' xcb-errors: @0@'.format(conf_data.get('WLR_HAS_XCB_ERRORS', false)), '----------------', '' ] diff --git a/meson_options.txt b/meson_options.txt index 6434b43d..4812b6f8 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,5 @@ option('enable_libcap', type: 'combo', choices: ['auto', 'true', 'false'], value: 'auto', description: 'Enable support for capabilities') option('enable_systemd', type: 'combo', choices: ['auto', 'true', 'false'], value: 'auto', description: 'Enable support for logind') option('enable_elogind', type: 'combo', choices: ['auto', 'true', 'false'], value: 'auto', description: 'Enable support for logind') +option('enable_xcb_errors', type: 'combo', choices: ['auto', 'true', 'false'], value: 'auto', description: 'Use xcb-errors util library') option('enable_xwayland', type: 'boolean', value: true, description: 'Enable support X11 applications') diff --git a/xwayland/meson.build b/xwayland/meson.build index 2ccdf4cb..9d7f3f4a 100644 --- a/xwayland/meson.build +++ b/xwayland/meson.build @@ -15,6 +15,7 @@ lib_wlr_xwayland = static_library( xcb_image, xcb_render, xcb_icccm, + xcb_errors, xkbcommon, pixman, ], diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 549a3333..09f30d3d 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -19,6 +19,9 @@ #ifdef WLR_HAS_XCB_ICCCM #include #endif +#ifdef WLR_HAS_XCB_ERRORS + #include +#endif const char *atom_map[ATOM_LAST] = { "WL_SURFACE_ID", @@ -955,9 +958,47 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm, } static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { - wlr_log(L_ERROR, "xcb error: code %d, sequence %d, value %d, opcode %d:%d", - ev->error_code, ev->sequence, ev->bad_value, - ev->minor_opcode, ev->major_opcode); +#ifdef WLR_HAS_XCB_ERRORS + xcb_errors_context_t *err_ctx; + if (xcb_errors_context_new(xwm->xcb_conn, &err_ctx)) { + wlr_log(L_DEBUG, "xcb error happened, but could not allocate error context"); + goto log_raw; + } + + const char *major_name; + major_name = xcb_errors_get_name_for_major_code(err_ctx, ev->major_opcode); + if (!major_name) { + wlr_log(L_DEBUG, "xcb error happened, but could not get major name"); + xcb_errors_context_free(err_ctx); + goto log_raw; + } + + const char *minor_name; + minor_name = xcb_errors_get_name_for_minor_code(err_ctx, + ev->major_opcode, ev->minor_opcode); + + const char *error_name, *extension; + error_name = xcb_errors_get_name_for_error(err_ctx, ev->error_code, &extension); + if (!error_name) { + wlr_log(L_DEBUG, "xcb error happened, but could not get error name"); + xcb_errors_context_free(err_ctx); + goto log_raw; + } + + wlr_log(L_ERROR, "xcb error: op %s (%s), code %s (%s), value %"PRIu32, + major_name, minor_name ? minor_name : "no minor", + error_name, extension ? extension : "no extension", + ev->bad_value); + + xcb_errors_context_free(err_ctx); + return; +log_raw: +#endif + wlr_log(L_ERROR, + "xcb error: op %"PRIu8":%"PRIu16", code %"PRIu8", sequence %"PRIu16", value %"PRIu32, + ev->major_opcode, ev->minor_opcode, ev->error_code, + ev->sequence, ev->bad_value); + } /* This is in xcb/xcb_event.h, but pulling xcb-util just for a constant From 6f9da97565fd5279809caa1acb9b734a0cee0e93 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sat, 3 Mar 2018 12:00:32 +0100 Subject: [PATCH 3/8] xcb errors: address declare-assign style --- xwayland/xwm.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 09f30d3d..b25ee142 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -965,20 +965,21 @@ static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { goto log_raw; } - const char *major_name; - major_name = xcb_errors_get_name_for_major_code(err_ctx, ev->major_opcode); + const char *major_name = + xcb_errors_get_name_for_major_code(err_ctx, ev->major_opcode); if (!major_name) { wlr_log(L_DEBUG, "xcb error happened, but could not get major name"); xcb_errors_context_free(err_ctx); goto log_raw; } - const char *minor_name; - minor_name = xcb_errors_get_name_for_minor_code(err_ctx, - ev->major_opcode, ev->minor_opcode); + const char *minor_name = + xcb_errors_get_name_for_minor_code(err_ctx, + ev->major_opcode, ev->minor_opcode); - const char *error_name, *extension; - error_name = xcb_errors_get_name_for_error(err_ctx, ev->error_code, &extension); + const char *extension; + const char *error_name = + xcb_errors_get_name_for_error(err_ctx, ev->error_code, &extension); if (!error_name) { wlr_log(L_DEBUG, "xcb error happened, but could not get error name"); xcb_errors_context_free(err_ctx); From 8026cd2a06023e70abf1758093ff15914145ea96 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sat, 3 Mar 2018 16:13:45 +0100 Subject: [PATCH 4/8] xcb error: always log sequence --- xwayland/xwm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index b25ee142..dad6e63d 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -986,10 +986,10 @@ static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { goto log_raw; } - wlr_log(L_ERROR, "xcb error: op %s (%s), code %s (%s), value %"PRIu32, + wlr_log(L_ERROR, "xcb error: op %s (%s), code %s (%s), sequence %"PRIu16", value %"PRIu32, major_name, minor_name ? minor_name : "no minor", error_name, extension ? extension : "no extension", - ev->bad_value); + ev->sequence, ev->bad_value); xcb_errors_context_free(err_ctx); return; From f4817916697c10f9361d0e78aa6d7979db5c850a Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sat, 3 Mar 2018 16:27:26 +0100 Subject: [PATCH 5/8] xcb error: get unhandled events names This provides more friendly debug messages for unhandled events, for example: [xwayland/xwm.c:1033] unhandled X11 event: FocusOut (10) [xwayland/xwm.c:1033] unhandled X11 event: MappingNotify (34) --- xwayland/xwm.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/xwayland/xwm.c b/xwayland/xwm.c index dad6e63d..ca6786db 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1002,6 +1002,33 @@ log_raw: } +static void xwm_handle_unhandled_event(struct wlr_xwm *xwm, xcb_generic_event_t *ev) { +#ifdef WLR_HAS_XCB_ERRORS + xcb_errors_context_t *err_ctx; + if (xcb_errors_context_new(xwm->xcb_conn, &err_ctx)) { + wlr_log(L_DEBUG, "Could not allocate context for unhandled X11 event: %u", + ev->response_type); + return; + } + + const char *extension; + const char *event_name = + xcb_errors_get_name_for_xcb_event(err_ctx, ev, &extension); + if (!event_name) { + wlr_log(L_DEBUG, "no name for unhandled event: %u", + ev->response_type); + xcb_errors_context_free(err_ctx); + return; + } + + wlr_log(L_DEBUG, "unhandled X11 event: %s (%u)", event_name, ev->response_type); + + xcb_errors_context_free(err_ctx); +#else + wlr_log(L_DEBUG, "unhandled X11 event: %u", ev->response_type); +#endif +} + /* This is in xcb/xcb_event.h, but pulling xcb-util just for a constant * others redefine anyway is meh */ @@ -1062,8 +1089,7 @@ static int x11_event_handler(int fd, uint32_t mask, void *data) { xwm_handle_xcb_error(xwm, (xcb_value_error_t *)event); break; default: - wlr_log(L_DEBUG, "unhandled X11 event: %d", - event->response_type); + xwm_handle_unhandled_event(xwm, event); break; } free(event); From 7d1870c6f1c1dd14fe9fe13885021970f0cbdbe8 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sat, 3 Mar 2018 17:03:19 +0100 Subject: [PATCH 6/8] move xwm.h out of include/wlr xwm.h was meant to be private, so move it to include/xwayland/xwm.h We had an ifdef WLR_HAS_XCB_ICCCM in xwayland.h which was easy to move to xwm, it is not safe to use the WLR_HAS_* in the public headers. I checked a few of our current users and none rely on xwm.h being public as expected (rootston, sway, hsroots) --- include/wlr/xwayland.h | 4 ---- include/{wlr => xwayland}/xwm.h | 7 +++++++ meson.build | 1 - xwayland/selection.c | 2 +- xwayland/xwayland.c | 2 +- xwayland/xwm.c | 9 +-------- 6 files changed, 10 insertions(+), 15 deletions(-) rename include/{wlr => xwayland}/xwm.h (95%) diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 0d4b91ed..f34860aa 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -8,10 +8,6 @@ #include #include -#ifdef WLR_HAS_XCB_ICCCM - #include -#endif - struct wlr_xwm; struct wlr_xwayland_cursor; diff --git a/include/wlr/xwm.h b/include/xwayland/xwm.h similarity index 95% rename from include/wlr/xwm.h rename to include/xwayland/xwm.h index 7d518f7e..0ed61d37 100644 --- a/include/wlr/xwm.h +++ b/include/xwayland/xwm.h @@ -5,6 +5,13 @@ #include #include +#ifdef WLR_HAS_XCB_ICCCM + #include +#endif +#ifdef WLR_HAS_XCB_ERRORS + #include +#endif + enum atom_name { WL_SURFACE_ID, WM_DELETE_WINDOW, diff --git a/meson.build b/meson.build index 1188f318..553d2b88 100644 --- a/meson.build +++ b/meson.build @@ -103,7 +103,6 @@ if get_option('enable_xwayland') conf_data.set('WLR_HAS_XWAYLAND', true) else exclude_headers += 'xwayland.h' - exclude_headers += 'xwm.h' endif exclude_headers += 'meson.build' install_subdir('include/wlr', install_dir: 'include', exclude_files: exclude_headers) diff --git a/xwayland/selection.c b/xwayland/selection.c index ffcde4d0..1d390026 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include "xwayland/xwm.h" static const size_t incr_chunk_size = 64 * 1024; diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index 8dffd040..d49dd718 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -18,9 +18,9 @@ #include #include #include -#include #include "sockets.h" #include "util/signal.h" +#include "xwayland/xwm.h" #ifdef __FreeBSD__ static inline int clearenv(void) { diff --git a/xwayland/xwm.c b/xwayland/xwm.c index ca6786db..b4042ad6 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -9,19 +9,12 @@ #include #include #include -#include #include #include #include #include #include "util/signal.h" - -#ifdef WLR_HAS_XCB_ICCCM - #include -#endif -#ifdef WLR_HAS_XCB_ERRORS - #include -#endif +#include "xwayland/xwm.h" const char *atom_map[ATOM_LAST] = { "WL_SURFACE_ID", From d9a724c4a21abd58a6dd73d596ea02d46f68897a Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sat, 3 Mar 2018 17:06:27 +0100 Subject: [PATCH 7/8] xcb errors: init errors context at start `xcb_errors_context_new` is more than just a malloc, it does a few xcb requests so we benefit from not generating a new context everytime --- include/xwayland/xwm.h | 3 +++ xwayland/xwm.c | 42 +++++++++++++++++++----------------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 0ed61d37..e918ac11 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -105,6 +105,9 @@ struct wlr_xwm { struct wl_list unpaired_surfaces; // wlr_xwayland_surface::unpaired_link const xcb_query_extension_reply_t *xfixes; +#ifdef WLR_HAS_XCB_ERRORS + xcb_errors_context_t *errors_context; +#endif struct wl_listener compositor_new_surface; struct wl_listener compositor_destroy; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index b4042ad6..9210033a 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -952,30 +952,24 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm, static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { #ifdef WLR_HAS_XCB_ERRORS - xcb_errors_context_t *err_ctx; - if (xcb_errors_context_new(xwm->xcb_conn, &err_ctx)) { - wlr_log(L_DEBUG, "xcb error happened, but could not allocate error context"); - goto log_raw; - } - const char *major_name = - xcb_errors_get_name_for_major_code(err_ctx, ev->major_opcode); + xcb_errors_get_name_for_major_code(xwm->errors_context, + ev->major_opcode); if (!major_name) { wlr_log(L_DEBUG, "xcb error happened, but could not get major name"); - xcb_errors_context_free(err_ctx); goto log_raw; } const char *minor_name = - xcb_errors_get_name_for_minor_code(err_ctx, + xcb_errors_get_name_for_minor_code(xwm->errors_context, ev->major_opcode, ev->minor_opcode); const char *extension; const char *error_name = - xcb_errors_get_name_for_error(err_ctx, ev->error_code, &extension); + xcb_errors_get_name_for_error(xwm->errors_context, + ev->error_code, &extension); if (!error_name) { wlr_log(L_DEBUG, "xcb error happened, but could not get error name"); - xcb_errors_context_free(err_ctx); goto log_raw; } @@ -984,7 +978,6 @@ static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { error_name, extension ? extension : "no extension", ev->sequence, ev->bad_value); - xcb_errors_context_free(err_ctx); return; log_raw: #endif @@ -997,26 +990,17 @@ log_raw: static void xwm_handle_unhandled_event(struct wlr_xwm *xwm, xcb_generic_event_t *ev) { #ifdef WLR_HAS_XCB_ERRORS - xcb_errors_context_t *err_ctx; - if (xcb_errors_context_new(xwm->xcb_conn, &err_ctx)) { - wlr_log(L_DEBUG, "Could not allocate context for unhandled X11 event: %u", - ev->response_type); - return; - } - const char *extension; const char *event_name = - xcb_errors_get_name_for_xcb_event(err_ctx, ev, &extension); + xcb_errors_get_name_for_xcb_event(xwm->errors_context, + ev, &extension); if (!event_name) { wlr_log(L_DEBUG, "no name for unhandled event: %u", ev->response_type); - xcb_errors_context_free(err_ctx); return; } wlr_log(L_DEBUG, "unhandled X11 event: %s (%u)", event_name, ev->response_type); - - xcb_errors_context_free(err_ctx); #else wlr_log(L_DEBUG, "unhandled X11 event: %u", ev->response_type); #endif @@ -1203,6 +1187,11 @@ void xwm_destroy(struct wlr_xwm *xwm) { if (xwm->event_source) { wl_event_source_remove(xwm->event_source); } +#ifdef WLR_HAS_XCB_ERRORS + if (xwm->errors_context) { + xcb_errors_context_free(xwm->errors_context); + } +#endif struct wlr_xwayland_surface *xsurface, *tmp; wl_list_for_each_safe(xsurface, tmp, &xwm->surfaces, link) { wlr_xwayland_surface_destroy(xsurface); @@ -1439,6 +1428,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) { return NULL; } +#ifdef WLR_HAS_XCB_ERRORS + if (xcb_errors_context_new(xwm->xcb_conn, &xwm->errors_context)) { + wlr_log(L_ERROR, "Could not allocate error context"); + xwm_destroy(xwm); + return NULL; + } +#endif xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(xwm->xcb_conn)); xwm->screen = screen_iterator.data; From 2910972b25a211ecd07c605d4bf93897ef5f715a Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sun, 4 Mar 2018 11:16:14 +0100 Subject: [PATCH 8/8] xwm.h: fix guard ifdef and remove wlr_ prefix from xwm_atoms_contains --- include/xwayland/xwm.h | 6 +++--- xwayland/xwayland.c | 2 +- xwayland/xwm.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index e918ac11..4b15cc84 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -1,5 +1,5 @@ -#ifndef WLR_XWM_H -#define WLR_XWM_H +#ifndef XWAYLAND_XWM_H +#define XWAYLAND_XWM_H #include #include @@ -129,7 +129,7 @@ void xwm_selection_finish(struct wlr_xwm *xwm); void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat); -bool wlr_xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, +bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, size_t num_atoms, enum atom_name needle); #endif diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index d49dd718..765e17d0 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -418,7 +418,7 @@ bool wlr_xwayland_surface_is_unmanaged(const struct wlr_xwayland_surface *surfac }; for (size_t i = 0; i < sizeof(needles) / sizeof(needles[0]); ++i) { - if (wlr_xwm_atoms_contains(surface->xwm, surface->window_type, + if (xwm_atoms_contains(surface->xwm, surface->window_type, surface->window_type_len, needles[i])) { return true; } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 9210033a..8911c553 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -1519,7 +1519,7 @@ void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland_surface *surface, xcb_flush(surface->xwm->xcb_conn); } -bool wlr_xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, +bool xwm_atoms_contains(struct wlr_xwm *xwm, xcb_atom_t *atoms, size_t num_atoms, enum atom_name needle) { xcb_atom_t atom = xwm->atoms[needle];