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);