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 <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui 2020-10-22 01:39:51 +01:00
parent b675e3f19d
commit dc37370a66
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
10 changed files with 186 additions and 60 deletions

View File

@ -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

View File

@ -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;
}

View File

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

View File

@ -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)) {

View File

@ -6,6 +6,7 @@
#include <stdc-predef.h>
#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;

View File

@ -13,6 +13,7 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/sync.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
@ -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)) {

View File

@ -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, &region, NULL);
// falls through
fallthrough();
case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break;
#endif
default: assert(0);

View File

@ -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\"",

106
src/x.c
View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
#include <stdalign.h>
#include <stdbool.h>
#include <stdlib.h>
@ -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;
}

View File

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