mirror of
https://github.com/yshui/picom.git
synced 2024-11-11 13:51:02 -05:00
50e2259404
This was a dubious "fix" for a Nvidia driver problem. The problem was never fully understood, and the then developers took a shotgun approach and implemented xsync fences as a fix. Which somehow fixed the problem. Again, I don't see any indication that the developers understood why this "fix" worked. (for details, see chjj/compton#152 and chjj/compton#181) The driver problem should have been fixed almost 5 years ago. So this shouldn't be needed anymore. In addition the way compton uses xsync fences is apparently wrong according to the xsync spec (fences are attached to screen, but compton uses them as if they were attached to drawables). So, I will try removing it and see if anyone will complain. If there are real concrete reasons why fences are needed, it will be brought back. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
369 lines
9.8 KiB
C
369 lines
9.8 KiB
C
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
|
|
#include <stdbool.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xlib.h>
|
|
#include <xcb/xcb_renderutil.h>
|
|
#include <xcb/xfixes.h>
|
|
#include <pixman.h>
|
|
|
|
#include "common.h"
|
|
#include "x.h"
|
|
|
|
/**
|
|
* Get a specific attribute of a window.
|
|
*
|
|
* Returns a blank structure if the returned type and format does not
|
|
* match the requested type and format.
|
|
*
|
|
* @param ps current session
|
|
* @param w window
|
|
* @param atom atom of attribute to fetch
|
|
* @param length length to read
|
|
* @param rtype atom of the requested type
|
|
* @param rformat requested format
|
|
* @return a <code>winprop_t</code> structure containing the attribute
|
|
* and number of items. A blank one on failure.
|
|
*/
|
|
winprop_t
|
|
wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset,
|
|
long length, Atom rtype, int rformat) {
|
|
Atom type = None;
|
|
int format = 0;
|
|
unsigned long nitems = 0, after = 0;
|
|
unsigned char *data = NULL;
|
|
|
|
if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length,
|
|
False, rtype, &type, &format, &nitems, &after, &data)
|
|
&& nitems && (AnyPropertyType == type || type == rtype)
|
|
&& (!rformat || format == rformat)
|
|
&& (8 == format || 16 == format || 32 == format)) {
|
|
return (winprop_t) {
|
|
.data.p8 = data,
|
|
.nitems = nitems,
|
|
.type = type,
|
|
.format = format,
|
|
};
|
|
}
|
|
|
|
cxfree(data);
|
|
|
|
return (winprop_t) {
|
|
.data.p8 = NULL,
|
|
.nitems = 0,
|
|
.type = AnyPropertyType,
|
|
.format = 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get the value of a type-<code>Window</code> property of a window.
|
|
*
|
|
* @return the value if successful, 0 otherwise
|
|
*/
|
|
Window
|
|
wid_get_prop_window(session_t *ps, Window wid, Atom aprop) {
|
|
// Get the attribute
|
|
Window p = None;
|
|
winprop_t prop = wid_get_prop(ps, wid, aprop, 1L, XCB_ATOM_WINDOW, 32);
|
|
|
|
// Return it
|
|
if (prop.nitems) {
|
|
p = *prop.data.p32;
|
|
}
|
|
|
|
free_winprop(&prop);
|
|
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* Get the value of a text property of a window.
|
|
*/
|
|
bool wid_get_text_prop(session_t *ps, Window wid, Atom prop,
|
|
char ***pstrlst, int *pnstr) {
|
|
XTextProperty text_prop = { NULL, 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);
|
|
cxfree(text_prop.value);
|
|
return false;
|
|
}
|
|
|
|
cxfree(text_prop.value);
|
|
return true;
|
|
}
|
|
|
|
static inline void x_get_server_pictfmts(session_t *ps) {
|
|
if (ps->pictfmts)
|
|
return;
|
|
xcb_generic_error_t *e = NULL;
|
|
// Get window picture format
|
|
ps->pictfmts =
|
|
xcb_render_query_pict_formats_reply(ps->c,
|
|
xcb_render_query_pict_formats(ps->c), &e);
|
|
if (e || !ps->pictfmts) {
|
|
printf_errf("(): failed to get pict formats\n");
|
|
abort();
|
|
}
|
|
}
|
|
|
|
xcb_render_pictforminfo_t *x_get_pictform_for_visual(session_t *ps, xcb_visualid_t visual) {
|
|
if (!ps->pictfmts)
|
|
x_get_server_pictfmts(ps);
|
|
|
|
xcb_render_pictvisual_t *pv = xcb_render_util_find_visual_format(ps->pictfmts, visual);
|
|
for(xcb_render_pictforminfo_iterator_t i =
|
|
xcb_render_query_pict_formats_formats_iterator(ps->pictfmts); i.rem;
|
|
xcb_render_pictforminfo_next(&i)) {
|
|
if (i.data->id == pv->format) {
|
|
return i.data;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
xcb_render_picture_t
|
|
x_create_picture_with_pictfmt_and_pixmap(
|
|
session_t *ps, xcb_render_pictforminfo_t * pictfmt,
|
|
xcb_pixmap_t pixmap, unsigned long valuemask,
|
|
const xcb_render_create_picture_value_list_t *attr)
|
|
{
|
|
void *buf = NULL;
|
|
if (attr) {
|
|
xcb_render_create_picture_value_list_serialize(&buf, valuemask, attr);
|
|
if (!buf) {
|
|
printf_errf("(): failed to serialize picture attributes");
|
|
return None;
|
|
}
|
|
}
|
|
|
|
xcb_render_picture_t tmp_picture = xcb_generate_id(ps->c);
|
|
xcb_generic_error_t *e =
|
|
xcb_request_check(ps->c, xcb_render_create_picture_checked(ps->c, tmp_picture,
|
|
pixmap, pictfmt->id, valuemask, buf));
|
|
free(buf);
|
|
if (e) {
|
|
printf_errf("(): failed to create picture");
|
|
return None;
|
|
}
|
|
return tmp_picture;
|
|
}
|
|
|
|
xcb_render_picture_t
|
|
x_create_picture_with_visual_and_pixmap(
|
|
session_t *ps, xcb_visualid_t visual,
|
|
xcb_pixmap_t pixmap, unsigned long valuemask,
|
|
const xcb_render_create_picture_value_list_t *attr)
|
|
{
|
|
xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(ps, visual);
|
|
return x_create_picture_with_pictfmt_and_pixmap(ps, pictfmt, pixmap, valuemask, attr);
|
|
}
|
|
|
|
xcb_render_picture_t
|
|
x_create_picture_with_standard_and_pixmap(
|
|
session_t *ps, xcb_pict_standard_t standard,
|
|
xcb_pixmap_t pixmap, unsigned long valuemask,
|
|
const xcb_render_create_picture_value_list_t *attr)
|
|
{
|
|
if (!ps->pictfmts)
|
|
x_get_server_pictfmts(ps);
|
|
|
|
xcb_render_pictforminfo_t *pictfmt =
|
|
xcb_render_util_find_standard_format(ps->pictfmts, standard);
|
|
assert(pictfmt);
|
|
return x_create_picture_with_pictfmt_and_pixmap(ps, pictfmt, pixmap, valuemask, attr);
|
|
}
|
|
|
|
/**
|
|
* Create an picture.
|
|
*/
|
|
xcb_render_picture_t
|
|
x_create_picture(session_t *ps, int wid, int hei,
|
|
xcb_render_pictforminfo_t *pictfmt, unsigned long valuemask,
|
|
const xcb_render_create_picture_value_list_t *attr)
|
|
{
|
|
if (!pictfmt)
|
|
pictfmt = x_get_pictform_for_visual(ps, ps->vis);
|
|
|
|
if (!pictfmt) {
|
|
printf_errf("(): default visual is invalid");
|
|
abort();
|
|
}
|
|
|
|
int depth = pictfmt->depth;
|
|
|
|
xcb_pixmap_t tmp_pixmap = x_create_pixmap(ps, depth, ps->root, wid, hei);
|
|
if (!tmp_pixmap)
|
|
return None;
|
|
|
|
xcb_render_picture_t picture =
|
|
x_create_picture_with_pictfmt_and_pixmap(ps, pictfmt, tmp_pixmap, valuemask, attr);
|
|
|
|
xcb_free_pixmap(ps->c, tmp_pixmap);
|
|
|
|
return picture;
|
|
}
|
|
|
|
bool x_fetch_region(session_t *ps, xcb_xfixes_region_t r, pixman_region32_t *res) {
|
|
xcb_generic_error_t *e = NULL;
|
|
xcb_xfixes_fetch_region_reply_t *xr = xcb_xfixes_fetch_region_reply(ps->c,
|
|
xcb_xfixes_fetch_region(ps->c, r), &e);
|
|
if (!xr) {
|
|
printf_errf("(): failed to fetch rectangles");
|
|
return false;
|
|
}
|
|
|
|
int nrect = xcb_xfixes_fetch_region_rectangles_length(xr);
|
|
pixman_box32_t *b = calloc(nrect, sizeof *b);
|
|
xcb_rectangle_t *xrect = xcb_xfixes_fetch_region_rectangles(xr);
|
|
for (int i = 0; i < nrect; i++) {
|
|
b[i] = (pixman_box32_t) {
|
|
.x1 = xrect[i].x,
|
|
.y1 = xrect[i].y,
|
|
.x2 = xrect[i].x + xrect[i].width,
|
|
.y2 = xrect[i].y + xrect[i].height
|
|
};
|
|
}
|
|
bool ret = pixman_region32_init_rects(res, b, nrect);
|
|
free(b);
|
|
free(xr);
|
|
return ret;
|
|
}
|
|
|
|
void x_set_picture_clip_region(session_t *ps, xcb_render_picture_t pict,
|
|
int clip_x_origin, int clip_y_origin, const region_t *reg) {
|
|
int nrects;
|
|
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
|
|
xcb_rectangle_t *xrects = calloc(nrects, sizeof *xrects);
|
|
for (int i = 0; i < nrects; i++)
|
|
xrects[i] = (xcb_rectangle_t){
|
|
.x = rects[i].x1,
|
|
.y = rects[i].y1,
|
|
.width = rects[i].x2 - rects[i].x1,
|
|
.height = rects[i].y2 - rects[i].y1,
|
|
};
|
|
|
|
xcb_generic_error_t *e =
|
|
xcb_request_check(ps->c, xcb_render_set_picture_clip_rectangles_checked(ps->c, pict,
|
|
clip_x_origin, clip_y_origin, nrects, xrects));
|
|
if (e)
|
|
printf_errf("(): failed to set clip region");
|
|
free(e);
|
|
free(xrects);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* X11 error handler function.
|
|
*
|
|
* XXX consider making this error to string
|
|
*/
|
|
void
|
|
x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_code) {
|
|
session_t * const ps = ps_g;
|
|
|
|
int o = 0;
|
|
const char *name = "Unknown";
|
|
|
|
if (major == ps->composite_opcode
|
|
&& minor == XCB_COMPOSITE_REDIRECT_SUBWINDOWS) {
|
|
fprintf(stderr, "Another composite manager is already running "
|
|
"(and does not handle _NET_WM_CM_Sn correctly)\n");
|
|
exit(1);
|
|
}
|
|
|
|
#define CASESTRRET2(s) case s: name = #s; break
|
|
|
|
o = error_code - ps->xfixes_error;
|
|
switch (o) {
|
|
CASESTRRET2(XCB_XFIXES_BAD_REGION);
|
|
}
|
|
|
|
o = error_code - ps->damage_error;
|
|
switch (o) {
|
|
CASESTRRET2(XCB_DAMAGE_BAD_DAMAGE);
|
|
}
|
|
|
|
o = error_code - ps->render_error;
|
|
switch (o) {
|
|
CASESTRRET2(XCB_RENDER_PICT_FORMAT);
|
|
CASESTRRET2(XCB_RENDER_PICTURE);
|
|
CASESTRRET2(XCB_RENDER_PICT_OP);
|
|
CASESTRRET2(XCB_RENDER_GLYPH_SET);
|
|
CASESTRRET2(XCB_RENDER_GLYPH);
|
|
}
|
|
|
|
#ifdef CONFIG_OPENGL
|
|
if (ps->glx_exists) {
|
|
o = error_code - ps->glx_error;
|
|
switch (o) {
|
|
CASESTRRET2(GLX_BAD_SCREEN);
|
|
CASESTRRET2(GLX_BAD_ATTRIBUTE);
|
|
CASESTRRET2(GLX_NO_EXTENSION);
|
|
CASESTRRET2(GLX_BAD_VISUAL);
|
|
CASESTRRET2(GLX_BAD_CONTEXT);
|
|
CASESTRRET2(GLX_BAD_VALUE);
|
|
CASESTRRET2(GLX_BAD_ENUM);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
switch (error_code) {
|
|
CASESTRRET2(BadAccess);
|
|
CASESTRRET2(BadAlloc);
|
|
CASESTRRET2(BadAtom);
|
|
CASESTRRET2(BadColor);
|
|
CASESTRRET2(BadCursor);
|
|
CASESTRRET2(BadDrawable);
|
|
CASESTRRET2(BadFont);
|
|
CASESTRRET2(BadGC);
|
|
CASESTRRET2(BadIDChoice);
|
|
CASESTRRET2(BadImplementation);
|
|
CASESTRRET2(BadLength);
|
|
CASESTRRET2(BadMatch);
|
|
CASESTRRET2(BadName);
|
|
CASESTRRET2(BadPixmap);
|
|
CASESTRRET2(BadRequest);
|
|
CASESTRRET2(BadValue);
|
|
CASESTRRET2(BadWindow);
|
|
}
|
|
|
|
#undef CASESTRRET2
|
|
|
|
print_timestamp(ps);
|
|
{
|
|
char buf[BUF_LEN] = "";
|
|
XGetErrorText(ps->dpy, error_code, buf, BUF_LEN);
|
|
printf("error %4d %-12s request %4d minor %4d serial %6lu: \"%s\"\n",
|
|
error_code, name, major,
|
|
minor, serial, buf);
|
|
}
|
|
|
|
// print_backtrace();
|
|
}
|
|
|
|
/**
|
|
* Create a pixmap and check that creation succeeded.
|
|
*/
|
|
xcb_pixmap_t
|
|
x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height) {
|
|
xcb_pixmap_t pix = xcb_generate_id(ps->c);
|
|
xcb_void_cookie_t cookie = xcb_create_pixmap_checked(ps->c, depth, pix, drawable, width, height);
|
|
xcb_generic_error_t *err = xcb_request_check(ps->c, cookie);
|
|
if (err == NULL)
|
|
return pix;
|
|
|
|
printf_err("Failed to create pixmap:");
|
|
ev_xcb_error(ps, err);
|
|
free(err);
|
|
return XCB_NONE;
|
|
}
|