From dc37370a66132d9b6675708ef9d52e955974b976 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 22 Oct 2020 01:39:51 +0100 Subject: [PATCH] x: remove the last bit of Xlib dependency Of course, we still use GLX, so we can't completely remove Xlib yet. But this removes all Xlib uses outside of the backends. This drops support for COMPOUND_TEXT Xorg strings, so people how wants multilingual support has to use UTF8, which should be fine since most of the applications support that. Signed-off-by: Yuxuan Shui --- .clang-tidy | 3 ++ src/atom.c | 3 +- src/atom.h | 15 +++++-- src/c2.c | 11 +++-- src/compiler.h | 8 ++++ src/picom.c | 63 ++++++++++++++++++++++++----- src/render.c | 2 +- src/win.c | 29 ++++++-------- src/x.c | 106 +++++++++++++++++++++++++++++++++++++++---------- src/x.h | 6 +++ 10 files changed, 186 insertions(+), 60 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index b521d6e8..6699a438 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -14,3 +14,6 @@ Checks: > -readability-magic-numbers AnalyzeTemporaryDtors: false FormatStyle: file +CheckOptions: + - key: readability-magic-numbers.IgnoredIntegerValues + value: 4;8;16;24;32;1;2;3;4096 diff --git a/src/atom.c b/src/atom.c index 3fef7994..0272dc83 100644 --- a/src/atom.c +++ b/src/atom.c @@ -30,7 +30,8 @@ struct atom *init_atoms(xcb_connection_t *c) { auto atoms = ccalloc(1, struct atom); atoms->c = new_cache((void *)c, atom_getter, NULL); #define ATOM_GET(x) atoms->a##x = (xcb_atom_t)(intptr_t)cache_get(atoms->c, #x, NULL) - LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST); + LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1); + LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2); #undef ATOM_GET return atoms; } diff --git a/src/atom.h b/src/atom.h index 9f661833..6f4eae69 100644 --- a/src/atom.h +++ b/src/atom.h @@ -8,7 +8,7 @@ // clang-format off // Splitted into 2 lists because of the limitation of our macros -#define ATOM_LIST \ +#define ATOM_LIST1 \ _NET_WM_WINDOW_OPACITY, \ _NET_FRAME_EXTENTS, \ WM_STATE, \ @@ -16,12 +16,16 @@ _NET_WM_PID, \ WM_NAME, \ WM_CLASS, \ + WM_ICON_NAME, \ WM_TRANSIENT_FOR, \ WM_WINDOW_ROLE, \ WM_CLIENT_LEADER, \ + WM_CLIENT_MACHINE, \ _NET_ACTIVE_WINDOW, \ _COMPTON_SHADOW, \ - _NET_WM_WINDOW_TYPE, \ + _NET_WM_WINDOW_TYPE + +#define ATOM_LIST2 \ _NET_WM_WINDOW_TYPE_DESKTOP, \ _NET_WM_WINDOW_TYPE_DOCK, \ _NET_WM_WINDOW_TYPE_TOOLBAR, \ @@ -38,14 +42,17 @@ _NET_WM_WINDOW_TYPE_DND, \ _NET_WM_STATE, \ _NET_WM_STATE_FULLSCREEN, \ - _NET_WM_BYPASS_COMPOSITOR + _NET_WM_BYPASS_COMPOSITOR, \ + UTF8_STRING, \ + C_STRING // clang-format on #define ATOM_DEF(x) xcb_atom_t a##x struct atom { struct cache *c; - LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST); + LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST1); + LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST2); }; struct atom *init_atoms(xcb_connection_t *); diff --git a/src/c2.c b/src/c2.c index 29561f31..e3949c92 100644 --- a/src/c2.c +++ b/src/c2.c @@ -1406,8 +1406,9 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w tgt_free = strdup(strlst[idx]); tgt = tgt_free; } - if (strlst) - XFreeStringList(strlst); + if (strlst) { + free(strlst); + } } if (tgt) { @@ -1464,10 +1465,7 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w // Free the string after usage, if necessary if (tgt_free) { - if (C2_L_TATOM == pleaf->type) - XFree(tgt_free); - else - free(tgt_free); + free(tgt_free); } } break; default: assert(0); break; @@ -1557,6 +1555,7 @@ static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_p */ bool c2_match(session_t *ps, const struct managed_win *w, const c2_lptr_t *condlst, void **pdata) { + assert(ps->server_grabbed); // Then go through the whole linked list for (; condlst; condlst = condlst->next) { if (c2_match_once(ps, w, condlst->ptr)) { diff --git a/src/compiler.h b/src/compiler.h index 550507fd..f146bd21 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -6,6 +6,7 @@ #include #endif +// clang-format off #define auto __auto_type #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) @@ -78,6 +79,12 @@ # define attr_malloc #endif +#if __has_attribute(fallthrough) +# define fallthrough() __attribute__((fallthrough)) +#else +# define fallthrough() +#endif + #if __STDC_VERSION__ >= 201112L # define attr_noret _Noreturn #else @@ -107,6 +114,7 @@ #else # define thread_local _Pragma("GCC error \"No thread local storage support\"") __error__ #endif +// clang-format on typedef unsigned long ulong; typedef unsigned int uint; diff --git a/src/picom.c b/src/picom.c index cf17b947..83ac42d4 100644 --- a/src/picom.c +++ b/src/picom.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -918,7 +919,7 @@ void opts_set_no_fading_openclose(session_t *ps, bool newval) { #endif /** - * Register us with the compositor selection (_NET_WM_CM_S) + * Setup window properties, then register us with the compositor selection (_NET_WM_CM_S) * * @return 0 if success, 1 if compositor already running, -1 if error. */ @@ -936,15 +937,56 @@ static int register_cm(session_t *ps) { return -1; } - { - XClassHint *h = XAllocClassHint(); - if (h) { - h->res_name = "picom"; - h->res_class = "picom"; + const xcb_atom_t prop_atoms[] = { + ps->atoms->aWM_NAME, + ps->atoms->a_NET_WM_NAME, + ps->atoms->aWM_ICON_NAME, + }; + + const bool prop_is_utf8[] = {false, true, false}; + + // Set names and classes + for (size_t i = 0; i < ARR_SIZE(prop_atoms); i++) { + e = xcb_request_check( + ps->c, xcb_change_property_checked( + ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win, prop_atoms[i], + prop_is_utf8[i] ? ps->atoms->aUTF8_STRING : XCB_ATOM_STRING, + 8, strlen("picom"), "picom")); + if (e) { + log_error_x_error(e, "Failed to set window property %d", + prop_atoms[i]); + free(e); + } + } + + const char picom_class[] = "picom\0picom"; + e = xcb_request_check( + ps->c, xcb_change_property_checked(ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win, + ps->atoms->aWM_CLASS, XCB_ATOM_STRING, 8, + ARR_SIZE(picom_class), picom_class)); + if (e) { + log_error_x_error(e, "Failed to set the WM_CLASS property"); + free(e); + } + + // Set WM_CLIENT_MACHINE. As per EWMH, because we set _NET_WM_PID, we must also + // set WM_CLIENT_MACHINE. + { + char hostname[HOST_NAME_MAX]; + if (gethostname(hostname, sizeof(hostname)) == 0) { + e = xcb_request_check( + ps->c, xcb_change_property_checked( + ps->c, XCB_PROP_MODE_REPLACE, ps->reg_win, + ps->atoms->aWM_CLIENT_MACHINE, XCB_ATOM_STRING, 8, + (uint32_t)strlen(hostname), hostname)); + if (e) { + log_error_x_error(e, "Failed to set the WM_CLIENT_MACHINE" + " property"); + free(e); + } + } else { + log_error_errno("Failed to get hostname"); } - Xutf8SetWMProperties(ps->dpy, ps->reg_win, "picom", "picom", NULL, 0, - NULL, NULL, h); - XFree(h); } // Set _NET_WM_PID @@ -997,8 +1039,9 @@ static int register_cm(session_t *ps) { * Write PID to a file. */ static inline bool write_pid(session_t *ps) { - if (!ps->o.write_pid_path) + if (!ps->o.write_pid_path) { return true; + } FILE *f = fopen(ps->o.write_pid_path, "w"); if (unlikely(!f)) { diff --git a/src/render.c b/src/render.c index 3c991fc7..deb2fa48 100644 --- a/src/render.c +++ b/src/render.c @@ -1054,7 +1054,7 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) { glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width, ps->root_height, 0, 1.0, false, false, ®ion, NULL); - // falls through + fallthrough(); case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break; #endif default: assert(0); diff --git a/src/win.c b/src/win.c index 99e79679..64f93185 100644 --- a/src/win.c +++ b/src/win.c @@ -535,28 +535,20 @@ static bool attr_pure win_has_rounded_corners(const struct managed_win *w) { } int win_update_name(session_t *ps, struct managed_win *w) { - XTextProperty text_prop = {NULL, XCB_NONE, 0, 0}; char **strlst = NULL; int nstr = 0; - if (!w->client_win) + if (!w->client_win) { return 0; + } if (!(wid_get_text_prop(ps, w->client_win, ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) { log_trace("(%#010x): _NET_WM_NAME unset, falling back to WM_NAME.", w->client_win); - if (!(XGetWMName(ps->dpy, w->client_win, &text_prop) && text_prop.value)) { + if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_NAME, &strlst, &nstr)) { return -1; } - if (Success != XmbTextPropertyToTextList(ps->dpy, &text_prop, &strlst, &nstr) || - !nstr || !strlst) { - if (strlst) - XFreeStringList(strlst); - XFree(text_prop.value); - return -1; - } - XFree(text_prop.value); } int ret = 0; @@ -566,7 +558,7 @@ int win_update_name(session_t *ps, struct managed_win *w) { w->name = strdup(strlst[0]); } - XFreeStringList(strlst); + free(strlst); log_trace("(%#010x): client = %#010x, name = \"%s\", " "ret = %d", @@ -578,8 +570,9 @@ static int win_update_role(session_t *ps, struct managed_win *w) { char **strlst = NULL; int nstr = 0; - if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) + if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) { return -1; + } int ret = 0; if (!w->role || strcmp(w->role, strlst[0]) != 0) { @@ -588,7 +581,7 @@ static int win_update_role(session_t *ps, struct managed_win *w) { w->role = strdup(strlst[0]); } - XFreeStringList(strlst); + free(strlst); log_trace("(%#010x): client = %#010x, role = \"%s\", " "ret = %d", @@ -1577,16 +1570,18 @@ bool win_update_class(session_t *ps, struct managed_win *w) { w->class_general = NULL; // Retrieve the property string list - if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_CLASS, &strlst, &nstr)) + if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_CLASS, &strlst, &nstr)) { return false; + } // Copy the strings if successful w->class_instance = strdup(strlst[0]); - if (nstr > 1) + if (nstr > 1) { w->class_general = strdup(strlst[1]); + } - XFreeStringList(strlst); + free(strlst); log_trace("(%#010x): client = %#010x, " "instance = \"%s\", general = \"%s\"", diff --git a/src/x.c b/src/x.c index 9db8e210..aef9005a 100644 --- a/src/x.c +++ b/src/x.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright (c) 2018 Yuxuan Shui +#include #include #include @@ -93,21 +94,79 @@ xcb_window_t wid_get_prop_window(session_t *ps, xcb_window_t wid, xcb_atom_t apr */ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst, int *pnstr) { - XTextProperty text_prop = {NULL, XCB_NONE, 0, 0}; - - if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value)) - return false; - - if (Success != XmbTextPropertyToTextList(ps->dpy, &text_prop, pstrlst, pnstr) || - !*pnstr) { - *pnstr = 0; - if (*pstrlst) - XFreeStringList(*pstrlst); - XFree(text_prop.value); + assert(ps->server_grabbed); + xcb_generic_error_t *e = NULL; + auto r = xcb_get_property_reply( + ps->c, xcb_get_property(ps->c, 0, wid, prop, XCB_ATOM_ANY, 0, 0), &e); + if (!r) { + log_debug_x_error(e, "Failed to get window property for %#010x", wid); + free(e); return false; } - XFree(text_prop.value); + auto type = r->type; + auto format = r->format; + auto length = r->bytes_after; + free(r); + + if (type == XCB_ATOM_NONE) { + return false; + } + + if (type != XCB_ATOM_STRING && type != ps->atoms->aUTF8_STRING && + type != ps->atoms->aC_STRING) { + log_warn("Text property %d of window %#010x has unsupported type: %d", + prop, wid, type); + return false; + } + + if (format != 8) { + log_warn("Text property %d of window %#010x has unexpected format: %d", + prop, wid, format); + return false; + } + + r = xcb_get_property_reply( + ps->c, xcb_get_property(ps->c, 0, wid, prop, type, 0, length), &e); + if (!r) { + log_debug_x_error(e, "Failed to get window property for %#010x", wid); + free(e); + return false; + } + + assert(length == (uint32_t)xcb_get_property_value_length(r)); + + void *data = xcb_get_property_value(r); + unsigned int nstr = 0; + uint32_t current_offset = 0; + while (current_offset < length) { + current_offset += + (uint32_t)strnlen(data + current_offset, length - current_offset) + 1; + nstr += 1; + } + + // Allocate the pointers and the strings together + void *buf = NULL; + if (posix_memalign(&buf, alignof(char *), length + sizeof(char *) * nstr + 1) != 0) { + abort(); + } + + char *strlst = buf + sizeof(char *) * nstr; + memcpy(strlst, xcb_get_property_value(r), length); + strlst[length] = '\0'; // X strings aren't guaranteed to be null terminated + + char **ret = buf; + current_offset = 0; + nstr = 0; + while (current_offset < length) { + ret[nstr] = strlst + current_offset; + current_offset += (uint32_t)strlen(strlst + current_offset) + 1; + nstr += 1; + } + + *pnstr = to_int_checked(nstr); + *pstrlst = ret; + free(r); return true; } @@ -116,8 +175,9 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ** static thread_local xcb_render_query_pict_formats_reply_t *g_pictfmts = NULL; static inline void x_get_server_pictfmts(xcb_connection_t *c) { - if (g_pictfmts) + if (g_pictfmts) { return; + } xcb_generic_error_t *e = NULL; // Get window picture format g_pictfmts = @@ -261,8 +321,9 @@ x_create_picture_with_pictfmt(xcb_connection_t *c, xcb_drawable_t d, int w, int uint8_t depth = pictfmt->depth; xcb_pixmap_t tmp_pixmap = x_create_pixmap(c, depth, d, w, h); - if (!tmp_pixmap) + if (!tmp_pixmap) { return XCB_NONE; + } xcb_render_picture_t picture = x_create_picture_with_pictfmt_and_pixmap( c, pictfmt, tmp_pixmap, valuemask, attr); @@ -310,13 +371,14 @@ void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict, int nrects; const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects); auto xrects = ccalloc(nrects, xcb_rectangle_t); - for (int i = 0; i < nrects; i++) + for (int i = 0; i < nrects; i++) { xrects[i] = (xcb_rectangle_t){ .x = to_i16_checked(rects[i].x1), .y = to_i16_checked(rects[i].y1), .width = to_u16_checked(rects[i].x2 - rects[i].x1), .height = to_u16_checked(rects[i].y2 - rects[i].y1), }; + } xcb_generic_error_t *e = xcb_request_check( c, xcb_render_set_picture_clip_rectangles_checked( @@ -326,7 +388,6 @@ void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict, free(e); } free(xrects); - return; } void x_clear_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict) { @@ -337,7 +398,6 @@ void x_clear_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict) log_error_x_error(e, "failed to clear clip region"); free(e); } - return; } enum { XSyncBadCounter = 0, @@ -469,8 +529,9 @@ xcb_pixmap_t x_create_pixmap(xcb_connection_t *c, uint8_t depth, xcb_drawable_t xcb_void_cookie_t cookie = xcb_create_pixmap_checked( c, depth, pix, drawable, to_u16_checked(width), to_u16_checked(height)); xcb_generic_error_t *err = xcb_request_check(c, cookie); - if (err == NULL) + if (err == NULL) { return pix; + } log_error_x_error(err, "Failed to create pixmap"); free(err); @@ -526,8 +587,9 @@ xcb_pixmap_t x_get_root_back_pixmap(session_t *ps) { bool x_is_root_back_pixmap_atom(session_t *ps, xcb_atom_t atom) { for (int p = 0; background_props_str[p]; p++) { xcb_atom_t prop_atom = get_atom(ps->atoms, background_props_str[p]); - if (prop_atom == atom) + if (prop_atom == atom) { return true; + } } return false; } @@ -651,9 +713,11 @@ xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen) { xcb_screen_iterator_t iter; iter = xcb_setup_roots_iterator(xcb_get_setup(c)); - for (; iter.rem; --screen, xcb_screen_next(&iter)) - if (screen == 0) + for (; iter.rem; --screen, xcb_screen_next(&iter)) { + if (screen == 0) { return iter.data; + } + } return NULL; } diff --git a/src/x.h b/src/x.h index fb57ce30..426752ed 100644 --- a/src/x.h +++ b/src/x.h @@ -73,6 +73,8 @@ struct xvisual_info { __r; \ }) +#define log_debug_x_error(e, fmt, ...) \ + LOG(DEBUG, fmt " (%s)", ##__VA_ARGS__, x_strerror(e)) #define log_error_x_error(e, fmt, ...) \ LOG(ERROR, fmt " (%s)", ##__VA_ARGS__, x_strerror(e)) #define log_fatal_x_error(e, fmt, ...) \ @@ -145,6 +147,10 @@ xcb_window_t wid_get_prop_window(session_t *ps, xcb_window_t wid, xcb_atom_t apr /** * Get the value of a text property of a window. + * + * @param[out] pstrlst Out parameter for an array of strings, caller needs to free this + * array + * @param[out] pnstr Number of strings in the array */ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst, int *pnstr);