1 // SPDX-License-Identifier: MPL-2.0
2 // Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
3 #include <stdbool.h>
4 #include <stdlib.h>
5 
6 #include <X11/Xutil.h>
7 #include <pixman.h>
8 #include <xcb/composite.h>
9 #include <xcb/damage.h>
10 #include <xcb/glx.h>
11 #include <xcb/render.h>
12 #include <xcb/sync.h>
13 #include <xcb/xcb.h>
14 #include <xcb/xcb_renderutil.h>
15 #include <xcb/xfixes.h>
16 
17 #include "atom.h"
18 #ifdef CONFIG_OPENGL
19 #include "backend/gl/glx.h"
20 #endif
21 #include "common.h"
22 #include "compiler.h"
23 #include "kernel.h"
24 #include "log.h"
25 #include "region.h"
26 #include "utils.h"
27 #include "x.h"
28 
29 /**
30  * Get a specific attribute of a window.
31  *
32  * Returns a blank structure if the returned type and format does not
33  * match the requested type and format.
34  *
35  * @param ps current session
36  * @param w window
37  * @param atom atom of attribute to fetch
38  * @param length length to read
39  * @param rtype atom of the requested type
40  * @param rformat requested format
41  * @return a <code>winprop_t</code> structure containing the attribute
42  *    and number of items. A blank one on failure.
43  */
x_get_prop_with_offset(const session_t * ps,xcb_window_t w,xcb_atom_t atom,int offset,int length,xcb_atom_t rtype,int rformat)44 winprop_t x_get_prop_with_offset(const session_t *ps, xcb_window_t w, xcb_atom_t atom,
45                                  int offset, int length, xcb_atom_t rtype, int rformat) {
46 	xcb_get_property_reply_t *r = xcb_get_property_reply(
47 	    ps->c,
48 	    xcb_get_property(ps->c, 0, w, atom, rtype, to_u32_checked(offset),
49 	                     to_u32_checked(length)),
50 	    NULL);
51 
52 	if (r && xcb_get_property_value_length(r) &&
53 	    (rtype == XCB_GET_PROPERTY_TYPE_ANY || r->type == rtype) &&
54 	    (!rformat || r->format == rformat) &&
55 	    (r->format == 8 || r->format == 16 || r->format == 32)) {
56 		auto len = xcb_get_property_value_length(r);
57 		return (winprop_t){
58 		    .ptr = xcb_get_property_value(r),
59 		    .nitems = (ulong)(len / (r->format / 8)),
60 		    .type = r->type,
61 		    .format = r->format,
62 		    .r = r,
63 		};
64 	}
65 
66 	free(r);
67 	return (winprop_t){
68 	    .ptr = NULL, .nitems = 0, .type = XCB_GET_PROPERTY_TYPE_ANY, .format = 0};
69 }
70 
71 /**
72  * Get the value of a type-<code>xcb_window_t</code> property of a window.
73  *
74  * @return the value if successful, 0 otherwise
75  */
wid_get_prop_window(session_t * ps,xcb_window_t wid,xcb_atom_t aprop)76 xcb_window_t wid_get_prop_window(session_t *ps, xcb_window_t wid, xcb_atom_t aprop) {
77 	// Get the attribute
78 	xcb_window_t p = XCB_NONE;
79 	winprop_t prop = x_get_prop(ps, wid, aprop, 1L, XCB_ATOM_WINDOW, 32);
80 
81 	// Return it
82 	if (prop.nitems) {
83 		p = (xcb_window_t)*prop.p32;
84 	}
85 
86 	free_winprop(&prop);
87 
88 	return p;
89 }
90 
91 /**
92  * Get the value of a text property of a window.
93  */
wid_get_text_prop(session_t * ps,xcb_window_t wid,xcb_atom_t prop,char *** pstrlst,int * pnstr)94 bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
95                        int *pnstr) {
96 	XTextProperty text_prop = {NULL, XCB_NONE, 0, 0};
97 
98 	if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value))
99 		return false;
100 
101 	if (Success != XmbTextPropertyToTextList(ps->dpy, &text_prop, pstrlst, pnstr) ||
102 	    !*pnstr) {
103 		*pnstr = 0;
104 		if (*pstrlst)
105 			XFreeStringList(*pstrlst);
106 		XFree(text_prop.value);
107 		return false;
108 	}
109 
110 	XFree(text_prop.value);
111 	return true;
112 }
113 
114 // A cache of pict formats. We assume they don't change during the lifetime
115 // of this program
116 static thread_local xcb_render_query_pict_formats_reply_t *g_pictfmts = NULL;
117 
x_get_server_pictfmts(xcb_connection_t * c)118 static inline void x_get_server_pictfmts(xcb_connection_t *c) {
119 	if (g_pictfmts)
120 		return;
121 	xcb_generic_error_t *e = NULL;
122 	// Get window picture format
123 	g_pictfmts =
124 	    xcb_render_query_pict_formats_reply(c, xcb_render_query_pict_formats(c), &e);
125 	if (e || !g_pictfmts) {
126 		log_fatal("failed to get pict formats\n");
127 		abort();
128 	}
129 }
130 
131 const xcb_render_pictforminfo_t *
x_get_pictform_for_visual(xcb_connection_t * c,xcb_visualid_t visual)132 x_get_pictform_for_visual(xcb_connection_t *c, xcb_visualid_t visual) {
133 	x_get_server_pictfmts(c);
134 
135 	xcb_render_pictvisual_t *pv = xcb_render_util_find_visual_format(g_pictfmts, visual);
136 	for (xcb_render_pictforminfo_iterator_t i =
137 	         xcb_render_query_pict_formats_formats_iterator(g_pictfmts);
138 	     i.rem; xcb_render_pictforminfo_next(&i)) {
139 		if (i.data->id == pv->format) {
140 			return i.data;
141 		}
142 	}
143 	return NULL;
144 }
145 
x_get_visual_for_pictfmt(xcb_render_query_pict_formats_reply_t * r,xcb_render_pictformat_t fmt)146 static xcb_visualid_t attr_pure x_get_visual_for_pictfmt(xcb_render_query_pict_formats_reply_t *r,
147                                                          xcb_render_pictformat_t fmt) {
148 	for (auto screen = xcb_render_query_pict_formats_screens_iterator(r); screen.rem;
149 	     xcb_render_pictscreen_next(&screen)) {
150 		for (auto depth = xcb_render_pictscreen_depths_iterator(screen.data);
151 		     depth.rem; xcb_render_pictdepth_next(&depth)) {
152 			for (auto pv = xcb_render_pictdepth_visuals_iterator(depth.data);
153 			     pv.rem; xcb_render_pictvisual_next(&pv)) {
154 				if (pv.data->format == fmt) {
155 					return pv.data->visual;
156 				}
157 			}
158 		}
159 	}
160 	return XCB_NONE;
161 }
162 
x_get_visual_for_standard(xcb_connection_t * c,xcb_pict_standard_t std)163 xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std) {
164 	x_get_server_pictfmts(c);
165 
166 	auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
167 
168 	return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
169 }
170 
x_get_visual_depth(xcb_connection_t * c,xcb_visualid_t visual)171 int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) {
172 	auto setup = xcb_get_setup(c);
173 	for (auto screen = xcb_setup_roots_iterator(setup); screen.rem;
174 	     xcb_screen_next(&screen)) {
175 		for (auto depth = xcb_screen_allowed_depths_iterator(screen.data);
176 		     depth.rem; xcb_depth_next(&depth)) {
177 			const int len = xcb_depth_visuals_length(depth.data);
178 			const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
179 			for (int i = 0; i < len; i++) {
180 				if (visual == visuals[i].visual_id) {
181 					return depth.data->depth;
182 				}
183 			}
184 		}
185 	}
186 	return -1;
187 }
188 
189 xcb_render_picture_t
x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t * c,const xcb_render_pictforminfo_t * pictfmt,xcb_pixmap_t pixmap,uint32_t valuemask,const xcb_render_create_picture_value_list_t * attr)190 x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *c,
191                                          const xcb_render_pictforminfo_t *pictfmt,
192                                          xcb_pixmap_t pixmap, uint32_t valuemask,
193                                          const xcb_render_create_picture_value_list_t *attr) {
194 	void *buf = NULL;
195 	if (attr) {
196 		xcb_render_create_picture_value_list_serialize(&buf, valuemask, attr);
197 		if (!buf) {
198 			log_error("failed to serialize picture attributes");
199 			return XCB_NONE;
200 		}
201 	}
202 
203 	xcb_render_picture_t tmp_picture = x_new_id(c);
204 	xcb_generic_error_t *e =
205 	    xcb_request_check(c, xcb_render_create_picture_checked(
206 	                             c, tmp_picture, pixmap, pictfmt->id, valuemask, buf));
207 	free(buf);
208 	if (e) {
209 		log_error_x_error(e, "failed to create picture");
210 		return XCB_NONE;
211 	}
212 	return tmp_picture;
213 }
214 
215 xcb_render_picture_t
x_create_picture_with_visual_and_pixmap(xcb_connection_t * c,xcb_visualid_t visual,xcb_pixmap_t pixmap,uint32_t valuemask,const xcb_render_create_picture_value_list_t * attr)216 x_create_picture_with_visual_and_pixmap(xcb_connection_t *c, xcb_visualid_t visual,
217                                         xcb_pixmap_t pixmap, uint32_t valuemask,
218                                         const xcb_render_create_picture_value_list_t *attr) {
219 	const xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(c, visual);
220 	return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
221 }
222 
223 xcb_render_picture_t
x_create_picture_with_standard_and_pixmap(xcb_connection_t * c,xcb_pict_standard_t standard,xcb_pixmap_t pixmap,uint32_t valuemask,const xcb_render_create_picture_value_list_t * attr)224 x_create_picture_with_standard_and_pixmap(xcb_connection_t *c, xcb_pict_standard_t standard,
225                                           xcb_pixmap_t pixmap, uint32_t valuemask,
226                                           const xcb_render_create_picture_value_list_t *attr) {
227 	x_get_server_pictfmts(c);
228 
229 	auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
230 	assert(pictfmt);
231 	return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
232 }
233 
234 /**
235  * Create an picture.
236  */
237 xcb_render_picture_t
x_create_picture_with_pictfmt(xcb_connection_t * c,xcb_drawable_t d,int w,int h,const xcb_render_pictforminfo_t * pictfmt,uint32_t valuemask,const xcb_render_create_picture_value_list_t * attr)238 x_create_picture_with_pictfmt(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
239                               const xcb_render_pictforminfo_t *pictfmt, uint32_t valuemask,
240                               const xcb_render_create_picture_value_list_t *attr) {
241 	uint8_t depth = pictfmt->depth;
242 
243 	xcb_pixmap_t tmp_pixmap = x_create_pixmap(c, depth, d, w, h);
244 	if (!tmp_pixmap)
245 		return XCB_NONE;
246 
247 	xcb_render_picture_t picture = x_create_picture_with_pictfmt_and_pixmap(
248 	    c, pictfmt, tmp_pixmap, valuemask, attr);
249 
250 	xcb_free_pixmap(c, tmp_pixmap);
251 
252 	return picture;
253 }
254 
255 xcb_render_picture_t
x_create_picture_with_visual(xcb_connection_t * c,xcb_drawable_t d,int w,int h,xcb_visualid_t visual,uint32_t valuemask,const xcb_render_create_picture_value_list_t * attr)256 x_create_picture_with_visual(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
257                              xcb_visualid_t visual, uint32_t valuemask,
258                              const xcb_render_create_picture_value_list_t *attr) {
259 	auto pictfmt = x_get_pictform_for_visual(c, visual);
260 	return x_create_picture_with_pictfmt(c, d, w, h, pictfmt, valuemask, attr);
261 }
262 
x_fetch_region(xcb_connection_t * c,xcb_xfixes_region_t r,pixman_region32_t * res)263 bool x_fetch_region(xcb_connection_t *c, xcb_xfixes_region_t r, pixman_region32_t *res) {
264 	xcb_generic_error_t *e = NULL;
265 	xcb_xfixes_fetch_region_reply_t *xr =
266 	    xcb_xfixes_fetch_region_reply(c, xcb_xfixes_fetch_region(c, r), &e);
267 	if (!xr) {
268 		log_error_x_error(e, "Failed to fetch rectangles");
269 		return false;
270 	}
271 
272 	int nrect = xcb_xfixes_fetch_region_rectangles_length(xr);
273 	auto b = ccalloc(nrect, pixman_box32_t);
274 	xcb_rectangle_t *xrect = xcb_xfixes_fetch_region_rectangles(xr);
275 	for (int i = 0; i < nrect; i++) {
276 		b[i] = (pixman_box32_t){.x1 = xrect[i].x,
277 		                        .y1 = xrect[i].y,
278 		                        .x2 = xrect[i].x + xrect[i].width,
279 		                        .y2 = xrect[i].y + xrect[i].height};
280 	}
281 	bool ret = pixman_region32_init_rects(res, b, nrect);
282 	free(b);
283 	free(xr);
284 	return ret;
285 }
286 
x_set_picture_clip_region(xcb_connection_t * c,xcb_render_picture_t pict,int16_t clip_x_origin,int16_t clip_y_origin,const region_t * reg)287 void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict,
288                                int16_t clip_x_origin, int16_t clip_y_origin,
289                                const region_t *reg) {
290 	int nrects;
291 	const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
292 	auto xrects = ccalloc(nrects, xcb_rectangle_t);
293 	for (int i = 0; i < nrects; i++)
294 		xrects[i] = (xcb_rectangle_t){
295 		    .x = to_i16_checked(rects[i].x1),
296 		    .y = to_i16_checked(rects[i].y1),
297 		    .width = to_u16_checked(rects[i].x2 - rects[i].x1),
298 		    .height = to_u16_checked(rects[i].y2 - rects[i].y1),
299 		};
300 
301 	xcb_generic_error_t *e = xcb_request_check(
302 	    c, xcb_render_set_picture_clip_rectangles_checked(
303 	           c, pict, clip_x_origin, clip_y_origin, to_u32_checked(nrects), xrects));
304 	if (e) {
305 		log_error_x_error(e, "Failed to set clip region");
306 		free(e);
307 	}
308 	free(xrects);
309 	return;
310 }
311 
x_clear_picture_clip_region(xcb_connection_t * c,xcb_render_picture_t pict)312 void x_clear_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict) {
313 	xcb_render_change_picture_value_list_t v = {.clipmask = XCB_NONE};
314 	xcb_generic_error_t *e = xcb_request_check(
315 	    c, xcb_render_change_picture(c, pict, XCB_RENDER_CP_CLIP_MASK, &v));
316 	if (e) {
317 		log_error_x_error(e, "failed to clear clip region");
318 		free(e);
319 	}
320 	return;
321 }
322 
323 enum { XSyncBadCounter = 0,
324        XSyncBadAlarm = 1,
325        XSyncBadFence = 2,
326 };
327 
328 /**
329  * Convert a X11 error to string
330  *
331  * @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
332  *         for multiple calls to this function,
333  */
334 static const char *
_x_strerror(unsigned long serial,uint8_t major,uint16_t minor,uint8_t error_code)335 _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) {
336 	session_t *const ps = ps_g;
337 
338 	int o = 0;
339 	const char *name = "Unknown";
340 
341 #define CASESTRRET(s)                                                                    \
342 	case s:                                                                          \
343 		name = #s;                                                               \
344 		break
345 
346 #define CASESTRRET2(s)                                                                   \
347 	case XCB_##s: name = #s; break
348 
349 	// TODO separate error code out from session_t
350 	o = error_code - ps->xfixes_error;
351 	switch (o) { CASESTRRET2(XFIXES_BAD_REGION); }
352 
353 	o = error_code - ps->damage_error;
354 	switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); }
355 
356 	o = error_code - ps->render_error;
357 	switch (o) {
358 		CASESTRRET2(RENDER_PICT_FORMAT);
359 		CASESTRRET2(RENDER_PICTURE);
360 		CASESTRRET2(RENDER_PICT_OP);
361 		CASESTRRET2(RENDER_GLYPH_SET);
362 		CASESTRRET2(RENDER_GLYPH);
363 	}
364 
365 	if (ps->glx_exists) {
366 		o = error_code - ps->glx_error;
367 		switch (o) {
368 			CASESTRRET2(GLX_BAD_CONTEXT);
369 			CASESTRRET2(GLX_BAD_CONTEXT_STATE);
370 			CASESTRRET2(GLX_BAD_DRAWABLE);
371 			CASESTRRET2(GLX_BAD_PIXMAP);
372 			CASESTRRET2(GLX_BAD_CONTEXT_TAG);
373 			CASESTRRET2(GLX_BAD_CURRENT_WINDOW);
374 			CASESTRRET2(GLX_BAD_RENDER_REQUEST);
375 			CASESTRRET2(GLX_BAD_LARGE_REQUEST);
376 			CASESTRRET2(GLX_UNSUPPORTED_PRIVATE_REQUEST);
377 			CASESTRRET2(GLX_BAD_FB_CONFIG);
378 			CASESTRRET2(GLX_BAD_PBUFFER);
379 			CASESTRRET2(GLX_BAD_CURRENT_DRAWABLE);
380 			CASESTRRET2(GLX_BAD_WINDOW);
381 			CASESTRRET2(GLX_GLX_BAD_PROFILE_ARB);
382 		}
383 	}
384 
385 	if (ps->xsync_exists) {
386 		o = error_code - ps->xsync_error;
387 		switch (o) {
388 			CASESTRRET(XSyncBadCounter);
389 			CASESTRRET(XSyncBadAlarm);
390 			CASESTRRET(XSyncBadFence);
391 		}
392 	}
393 
394 	switch (error_code) {
395 		CASESTRRET2(ACCESS);
396 		CASESTRRET2(ALLOC);
397 		CASESTRRET2(ATOM);
398 		CASESTRRET2(COLORMAP);
399 		CASESTRRET2(CURSOR);
400 		CASESTRRET2(DRAWABLE);
401 		CASESTRRET2(FONT);
402 		CASESTRRET2(G_CONTEXT);
403 		CASESTRRET2(ID_CHOICE);
404 		CASESTRRET2(IMPLEMENTATION);
405 		CASESTRRET2(LENGTH);
406 		CASESTRRET2(MATCH);
407 		CASESTRRET2(NAME);
408 		CASESTRRET2(PIXMAP);
409 		CASESTRRET2(REQUEST);
410 		CASESTRRET2(VALUE);
411 		CASESTRRET2(WINDOW);
412 	}
413 
414 #undef CASESTRRET
415 #undef CASESTRRET2
416 
417 	thread_local static char buffer[256];
418 	snprintf(buffer, sizeof(buffer), "X error %d %s request %d minor %d serial %lu",
419 	         error_code, name, major, minor, serial);
420 	return buffer;
421 }
422 
423 /**
424  * Log a X11 error
425  */
x_print_error(unsigned long serial,uint8_t major,uint16_t minor,uint8_t error_code)426 void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) {
427 	log_debug("%s", _x_strerror(serial, major, minor, error_code));
428 }
429 
430 /*
431  * Convert a xcb_generic_error_t to a string that describes the error
432  *
433  * @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
434  *         for multiple calls to this function,
435  */
x_strerror(xcb_generic_error_t * e)436 const char *x_strerror(xcb_generic_error_t *e) {
437 	if (!e) {
438 		return "No error";
439 	}
440 	return _x_strerror(e->full_sequence, e->major_code, e->minor_code, e->error_code);
441 }
442 
443 /**
444  * Create a pixmap and check that creation succeeded.
445  */
x_create_pixmap(xcb_connection_t * c,uint8_t depth,xcb_drawable_t drawable,int width,int height)446 xcb_pixmap_t x_create_pixmap(xcb_connection_t *c, uint8_t depth, xcb_drawable_t drawable,
447                              int width, int height) {
448 	xcb_pixmap_t pix = x_new_id(c);
449 	xcb_void_cookie_t cookie = xcb_create_pixmap_checked(
450 	    c, depth, pix, drawable, to_u16_checked(width), to_u16_checked(height));
451 	xcb_generic_error_t *err = xcb_request_check(c, cookie);
452 	if (err == NULL)
453 		return pix;
454 
455 	log_error_x_error(err, "Failed to create pixmap");
456 	free(err);
457 	return XCB_NONE;
458 }
459 
460 /**
461  * Validate a pixmap.
462  *
463  * Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
464  * are better ways.
465  */
x_validate_pixmap(xcb_connection_t * c,xcb_pixmap_t pixmap)466 bool x_validate_pixmap(xcb_connection_t *c, xcb_pixmap_t pixmap) {
467 	if (pixmap == XCB_NONE) {
468 		return false;
469 	}
470 
471 	auto r = xcb_get_geometry_reply(c, xcb_get_geometry(c, pixmap), NULL);
472 	if (!r) {
473 		return false;
474 	}
475 
476 	bool ret = r->width && r->height;
477 	free(r);
478 	return ret;
479 }
480 /// Names of root window properties that could point to a pixmap of
481 /// background.
482 static const char *background_props_str[] = {
483     "_XROOTPMAP_ID",
484     "_XSETROOT_ID",
485     0,
486 };
487 
x_get_root_back_pixmap(session_t * ps)488 xcb_pixmap_t x_get_root_back_pixmap(session_t *ps) {
489 	xcb_pixmap_t pixmap = XCB_NONE;
490 
491 	// Get the values of background attributes
492 	for (int p = 0; background_props_str[p]; p++) {
493 		xcb_atom_t prop_atom = get_atom(ps->atoms, background_props_str[p]);
494 		winprop_t prop = x_get_prop(ps, ps->root, prop_atom, 1, XCB_ATOM_PIXMAP, 32);
495 		if (prop.nitems) {
496 			pixmap = (xcb_pixmap_t)*prop.p32;
497 			free_winprop(&prop);
498 			break;
499 		}
500 		free_winprop(&prop);
501 	}
502 
503 	return pixmap;
504 }
505 
x_is_root_back_pixmap_atom(session_t * ps,xcb_atom_t atom)506 bool x_is_root_back_pixmap_atom(session_t *ps, xcb_atom_t atom) {
507 	for (int p = 0; background_props_str[p]; p++) {
508 		xcb_atom_t prop_atom = get_atom(ps->atoms, background_props_str[p]);
509 		if (prop_atom == atom)
510 			return true;
511 	}
512 	return false;
513 }
514 
515 /**
516  * Synchronizes a X Render drawable to ensure all pending painting requests
517  * are completed.
518  */
x_fence_sync(xcb_connection_t * c,xcb_sync_fence_t f)519 bool x_fence_sync(xcb_connection_t *c, xcb_sync_fence_t f) {
520 	// TODO(richardgv): If everybody just follows the rules stated in X Sync
521 	// prototype, we need only one fence per screen, but let's stay a bit
522 	// cautious right now
523 
524 	auto e = xcb_request_check(c, xcb_sync_trigger_fence_checked(c, f));
525 	if (e) {
526 		log_error_x_error(e, "Failed to trigger the fence");
527 		goto err;
528 	}
529 
530 	e = xcb_request_check(c, xcb_sync_await_fence_checked(c, 1, &f));
531 	if (e) {
532 		log_error_x_error(e, "Failed to await on a fence");
533 		goto err;
534 	}
535 
536 	e = xcb_request_check(c, xcb_sync_reset_fence_checked(c, f));
537 	if (e) {
538 		log_error_x_error(e, "Failed to reset the fence");
539 		goto err;
540 	}
541 	return true;
542 
543 err:
544 	free(e);
545 	return false;
546 }
547 
548 // xcb-render specific macros
549 #define XFIXED_TO_DOUBLE(value) (((double)(value)) / 65536)
550 #define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t)(((double)(value)) * 65536))
551 
552 /**
553  * Convert a struct conv to a X picture convolution filter, normalizing the kernel
554  * in the process. Allow the caller to specify the element at the center of the kernel,
555  * for compatibility with legacy code.
556  *
557  * @param[in] kernel the convolution kernel
558  * @param[in] center the element to put at the center of the matrix
559  * @param[inout] ret pointer to an array of `size`, if `size` is too small, more space
560  *                   will be allocated, and `*ret` will be updated
561  * @param[inout] size size of the array pointed to by `ret`, in number of elements
562  * @return number of elements filled into `*ret`
563  */
x_create_convolution_kernel(const conv * kernel,double center,struct x_convolution_kernel ** ret)564 void x_create_convolution_kernel(const conv *kernel, double center,
565                                  struct x_convolution_kernel **ret) {
566 	assert(ret);
567 	if (!*ret || (*ret)->capacity < kernel->w * kernel->h + 2) {
568 		free(*ret);
569 		*ret =
570 		    cvalloc(sizeof(struct x_convolution_kernel) +
571 		            (size_t)(kernel->w * kernel->h + 2) * sizeof(xcb_render_fixed_t));
572 		(*ret)->capacity = kernel->w * kernel->h + 2;
573 	}
574 
575 	(*ret)->size = kernel->w * kernel->h + 2;
576 
577 	auto buf = (*ret)->kernel;
578 	buf[0] = DOUBLE_TO_XFIXED(kernel->w);
579 	buf[1] = DOUBLE_TO_XFIXED(kernel->h);
580 
581 	double sum = center;
582 	for (int i = 0; i < kernel->w * kernel->h; i++) {
583 		if (i == kernel->w * kernel->h / 2) {
584 			continue;
585 		}
586 		sum += kernel->data[i];
587 	}
588 
589 	// Note for floating points a / b != a * (1 / b), but this shouldn't have any real
590 	// impact on the result
591 	double factor = sum != 0 ? 1.0 / sum : 1;
592 	for (int i = 0; i < kernel->w * kernel->h; i++) {
593 		buf[i + 2] = DOUBLE_TO_XFIXED(kernel->data[i] * factor);
594 	}
595 
596 	buf[kernel->h / 2 * kernel->w + kernel->w / 2 + 2] =
597 	    DOUBLE_TO_XFIXED(center * factor);
598 }
599 
600 /// Generate a search criteria for fbconfig from a X visual.
601 /// Returns {-1, -1, -1, -1, -1, 0} on failure
x_get_visual_info(xcb_connection_t * c,xcb_visualid_t visual)602 struct xvisual_info x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual) {
603 	auto pictfmt = x_get_pictform_for_visual(c, visual);
604 	auto depth = x_get_visual_depth(c, visual);
605 	if (!pictfmt || depth == -1) {
606 		log_error("Invalid visual %#03x", visual);
607 		return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
608 	}
609 	if (pictfmt->type != XCB_RENDER_PICT_TYPE_DIRECT) {
610 		log_error("We cannot handle non-DirectColor visuals. Report an "
611 		          "issue if you see this error message.");
612 		return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
613 	}
614 
615 	int red_size = popcntul(pictfmt->direct.red_mask),
616 	    blue_size = popcntul(pictfmt->direct.blue_mask),
617 	    green_size = popcntul(pictfmt->direct.green_mask),
618 	    alpha_size = popcntul(pictfmt->direct.alpha_mask);
619 
620 	return (struct xvisual_info){
621 	    .red_size = red_size,
622 	    .green_size = green_size,
623 	    .blue_size = blue_size,
624 	    .alpha_size = alpha_size,
625 	    .visual_depth = depth,
626 	    .visual = visual,
627 	};
628 }
629 
x_screen_of_display(xcb_connection_t * c,int screen)630 xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen) {
631 	xcb_screen_iterator_t iter;
632 
633 	iter = xcb_setup_roots_iterator(xcb_get_setup(c));
634 	for (; iter.rem; --screen, xcb_screen_next(&iter))
635 		if (screen == 0)
636 			return iter.data;
637 
638 	return NULL;
639 }
640