2018-10-03 17:14:51 -04:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
|
2020-10-21 20:39:51 -04:00
|
|
|
#include <stdalign.h>
|
2018-09-29 14:07:39 -04:00
|
|
|
#include <stdbool.h>
|
2019-01-20 16:15:20 -05:00
|
|
|
#include <stdlib.h>
|
2018-09-29 14:07:39 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
#include <X11/Xlib-xcb.h>
|
2018-12-30 03:00:22 -05:00
|
|
|
#include <X11/Xutil.h>
|
2019-03-10 08:34:37 -04:00
|
|
|
#include <pixman.h>
|
2019-01-20 16:15:20 -05:00
|
|
|
#include <xcb/composite.h>
|
|
|
|
#include <xcb/damage.h>
|
2023-07-05 00:54:44 -04:00
|
|
|
#include <xcb/dpms.h>
|
2020-03-31 00:59:44 -04:00
|
|
|
#include <xcb/glx.h>
|
2023-07-05 00:53:21 -04:00
|
|
|
#include <xcb/present.h>
|
2023-01-13 06:27:45 -05:00
|
|
|
#include <xcb/randr.h>
|
2019-01-20 16:15:20 -05:00
|
|
|
#include <xcb/render.h>
|
2019-03-10 08:34:37 -04:00
|
|
|
#include <xcb/sync.h>
|
|
|
|
#include <xcb/xcb.h>
|
|
|
|
#include <xcb/xcb_renderutil.h>
|
|
|
|
#include <xcb/xfixes.h>
|
2018-09-06 14:17:26 -04:00
|
|
|
|
2019-06-06 02:37:48 -04:00
|
|
|
#include "atom.h"
|
2019-10-12 09:29:10 -04:00
|
|
|
#ifdef CONFIG_OPENGL
|
2019-03-10 08:34:37 -04:00
|
|
|
#include "backend/gl/glx.h"
|
2019-10-12 09:29:10 -04:00
|
|
|
#endif
|
2018-09-06 14:17:26 -04:00
|
|
|
#include "common.h"
|
2019-03-10 08:34:37 -04:00
|
|
|
#include "compiler.h"
|
2019-02-17 18:47:46 -05:00
|
|
|
#include "kernel.h"
|
2018-12-15 12:53:17 -05:00
|
|
|
#include "log.h"
|
2019-03-10 08:34:37 -04:00
|
|
|
#include "region.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "x.h"
|
2018-09-06 14:17:26 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
// === Error handling ===
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Xlib error handler function.
|
|
|
|
*/
|
|
|
|
static int xerror(Display attr_unused *dpy, XErrorEvent *ev) {
|
|
|
|
if (!ps_g) {
|
|
|
|
// Do not ignore errors until the session has been initialized
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fake a xcb error, fill in just enough information
|
|
|
|
xcb_generic_error_t xcb_err;
|
|
|
|
xcb_err.full_sequence = (uint32_t)ev->serial;
|
|
|
|
xcb_err.major_code = ev->request_code;
|
|
|
|
xcb_err.minor_code = ev->minor_code;
|
|
|
|
xcb_err.error_code = ev->error_code;
|
|
|
|
x_handle_error(&ps_g->c, &xcb_err);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void x_discard_pending(struct x_connection *c, uint32_t sequence) {
|
|
|
|
while (c->pending_reply_head && sequence > c->pending_reply_head->sequence) {
|
|
|
|
auto next = c->pending_reply_head->next;
|
|
|
|
free(c->pending_reply_head);
|
|
|
|
c->pending_reply_head = next;
|
|
|
|
}
|
|
|
|
if (!c->pending_reply_head) {
|
|
|
|
c->pending_reply_tail = &c->pending_reply_head;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev) {
|
|
|
|
x_discard_pending(c, ev->full_sequence);
|
|
|
|
if (c->pending_reply_head && c->pending_reply_head->sequence == ev->full_sequence) {
|
|
|
|
if (c->pending_reply_head->action != PENDING_REPLY_ACTION_IGNORE) {
|
|
|
|
x_log_error(LOG_LEVEL_ERROR, ev->full_sequence, ev->major_code,
|
|
|
|
ev->minor_code, ev->error_code);
|
|
|
|
}
|
|
|
|
switch (c->pending_reply_head->action) {
|
|
|
|
case PENDING_REPLY_ACTION_ABORT:
|
|
|
|
log_fatal("An unrecoverable X error occurred, aborting...");
|
|
|
|
abort();
|
|
|
|
case PENDING_REPLY_ACTION_DEBUG_ABORT: assert(false); break;
|
|
|
|
case PENDING_REPLY_ACTION_IGNORE: break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
x_log_error(LOG_LEVEL_WARN, ev->full_sequence, ev->major_code, ev->minor_code,
|
|
|
|
ev->error_code);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Initialize x_connection struct from an Xlib Display.
|
|
|
|
///
|
|
|
|
/// Note this function doesn't take ownership of the Display, the caller is still
|
|
|
|
/// responsible for closing it after `free_x_connection` is called.
|
|
|
|
void x_connection_init(struct x_connection *c, Display *dpy) {
|
|
|
|
c->dpy = dpy;
|
|
|
|
c->c = XGetXCBConnection(dpy);
|
|
|
|
c->pending_reply_tail = &c->pending_reply_head;
|
|
|
|
c->previous_xerror_handler = XSetErrorHandler(xerror);
|
|
|
|
|
|
|
|
c->screen = DefaultScreen(dpy);
|
|
|
|
c->screen_info = x_screen_of_display(c, c->screen);
|
|
|
|
}
|
|
|
|
|
2018-09-06 14:17:26 -04:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2023-06-29 00:39:36 -04:00
|
|
|
winprop_t x_get_prop_with_offset(const struct x_connection *c, xcb_window_t w, xcb_atom_t atom,
|
2020-01-18 12:26:53 -05:00
|
|
|
int offset, int length, xcb_atom_t rtype, int rformat) {
|
2019-03-10 08:34:37 -04:00
|
|
|
xcb_get_property_reply_t *r = xcb_get_property_reply(
|
2023-06-29 00:39:36 -04:00
|
|
|
c->c,
|
|
|
|
xcb_get_property(c->c, 0, w, atom, rtype, to_u32_checked(offset),
|
2019-03-30 05:07:21 -04:00
|
|
|
to_u32_checked(length)),
|
|
|
|
NULL);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
if (r && xcb_get_property_value_length(r) &&
|
|
|
|
(rtype == XCB_GET_PROPERTY_TYPE_ANY || r->type == rtype) &&
|
|
|
|
(!rformat || r->format == rformat) &&
|
|
|
|
(r->format == 8 || r->format == 16 || r->format == 32)) {
|
2019-03-30 05:07:21 -04:00
|
|
|
auto len = xcb_get_property_value_length(r);
|
2019-03-10 08:34:37 -04:00
|
|
|
return (winprop_t){
|
|
|
|
.ptr = xcb_get_property_value(r),
|
2019-03-30 05:07:21 -04:00
|
|
|
.nitems = (ulong)(len / (r->format / 8)),
|
2019-03-10 08:34:37 -04:00
|
|
|
.type = r->type,
|
|
|
|
.format = r->format,
|
|
|
|
.r = r,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
free(r);
|
|
|
|
return (winprop_t){
|
|
|
|
.ptr = NULL, .nitems = 0, .type = XCB_GET_PROPERTY_TYPE_ANY, .format = 0};
|
2018-09-06 14:17:26 -04:00
|
|
|
}
|
|
|
|
|
2020-11-30 09:51:26 -05:00
|
|
|
/// Get the type, format and size in bytes of a window's specific attribute.
|
2023-06-29 00:39:36 -04:00
|
|
|
winprop_info_t x_get_prop_info(const struct x_connection *c, xcb_window_t w, xcb_atom_t atom) {
|
2020-11-30 09:51:26 -05:00
|
|
|
xcb_generic_error_t *e = NULL;
|
|
|
|
auto r = xcb_get_property_reply(
|
2023-06-29 00:39:36 -04:00
|
|
|
c->c, xcb_get_property(c->c, 0, w, atom, XCB_ATOM_ANY, 0, 0), &e);
|
2020-11-30 09:51:26 -05:00
|
|
|
if (!r) {
|
|
|
|
log_debug_x_error(e, "Failed to get property info for window %#010x", w);
|
|
|
|
free(e);
|
|
|
|
return (winprop_info_t){
|
|
|
|
.type = XCB_GET_PROPERTY_TYPE_ANY, .format = 0, .length = 0};
|
|
|
|
}
|
|
|
|
|
|
|
|
winprop_info_t winprop_info = {
|
|
|
|
.type = r->type, .format = r->format, .length = r->bytes_after};
|
|
|
|
free(r);
|
|
|
|
|
|
|
|
return winprop_info;
|
|
|
|
}
|
|
|
|
|
2018-09-06 14:17:26 -04:00
|
|
|
/**
|
2018-12-27 15:45:38 -05:00
|
|
|
* Get the value of a type-<code>xcb_window_t</code> property of a window.
|
2018-09-06 14:17:26 -04:00
|
|
|
*
|
|
|
|
* @return the value if successful, 0 otherwise
|
|
|
|
*/
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_atom_t aprop) {
|
2019-03-10 08:34:37 -04:00
|
|
|
// Get the attribute
|
|
|
|
xcb_window_t p = XCB_NONE;
|
2020-12-26 02:51:03 -05:00
|
|
|
winprop_t prop = x_get_prop(c, wid, aprop, 1L, XCB_ATOM_WINDOW, 32);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
// Return it
|
|
|
|
if (prop.nitems) {
|
2019-03-30 05:07:21 -04:00
|
|
|
p = (xcb_window_t)*prop.p32;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2018-09-06 14:17:26 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
free_winprop(&prop);
|
2018-09-06 14:17:26 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
return p;
|
2018-09-06 14:17:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the value of a text property of a window.
|
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
|
|
|
|
int *pnstr) {
|
2020-10-21 20:39:51 -04:00
|
|
|
assert(ps->server_grabbed);
|
2023-06-29 00:39:36 -04:00
|
|
|
auto prop_info = x_get_prop_info(&ps->c, wid, prop);
|
2020-11-30 09:51:26 -05:00
|
|
|
auto type = prop_info.type;
|
|
|
|
auto format = prop_info.format;
|
|
|
|
auto length = prop_info.length;
|
2020-10-21 20:39:51 -04:00
|
|
|
|
|
|
|
if (type == XCB_ATOM_NONE) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return false;
|
2020-10-21 20:39:51 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2020-10-21 20:39:51 -04:00
|
|
|
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);
|
2019-03-10 08:34:37 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-10-21 20:39:51 -04:00
|
|
|
if (format != 8) {
|
|
|
|
log_warn("Text property %d of window %#010x has unexpected format: %d",
|
|
|
|
prop, wid, format);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-11-30 09:51:26 -05:00
|
|
|
xcb_generic_error_t *e = NULL;
|
|
|
|
auto word_count = (length + 4 - 1) / 4;
|
|
|
|
auto r = xcb_get_property_reply(
|
2023-06-29 00:39:36 -04:00
|
|
|
ps->c.c, xcb_get_property(ps->c.c, 0, wid, prop, type, 0, word_count), &e);
|
2020-10-21 20:39:51 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-10-23 10:14:17 -04:00
|
|
|
if (nstr == 0) {
|
|
|
|
// The property is set to an empty string, in that case, we return one
|
|
|
|
// string
|
|
|
|
char **strlst = malloc(sizeof(char *));
|
|
|
|
strlst[0] = "";
|
|
|
|
*pnstr = 1;
|
|
|
|
*pstrlst = strlst;
|
|
|
|
free(r);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-10-21 20:39:51 -04:00
|
|
|
// 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);
|
2019-03-10 08:34:37 -04:00
|
|
|
return true;
|
2018-09-06 14:17:26 -04:00
|
|
|
}
|
2018-09-23 14:10:46 -04:00
|
|
|
|
2019-02-03 10:14:14 -05:00
|
|
|
// A cache of pict formats. We assume they don't change during the lifetime
|
2019-10-23 14:27:30 -04:00
|
|
|
// of this program
|
2019-02-03 10:14:14 -05:00
|
|
|
static thread_local xcb_render_query_pict_formats_reply_t *g_pictfmts = NULL;
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
static inline void x_get_server_pictfmts(struct x_connection *c) {
|
2020-10-21 20:39:51 -04:00
|
|
|
if (g_pictfmts) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return;
|
2020-10-21 20:39:51 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
xcb_generic_error_t *e = NULL;
|
|
|
|
// Get window picture format
|
2023-06-29 00:39:36 -04:00
|
|
|
g_pictfmts = xcb_render_query_pict_formats_reply(
|
|
|
|
c->c, xcb_render_query_pict_formats(c->c), &e);
|
2019-03-10 08:34:37 -04:00
|
|
|
if (e || !g_pictfmts) {
|
|
|
|
log_fatal("failed to get pict formats\n");
|
|
|
|
abort();
|
|
|
|
}
|
2018-09-23 14:10:46 -04:00
|
|
|
}
|
|
|
|
|
2019-02-06 19:18:47 -05:00
|
|
|
const xcb_render_pictforminfo_t *
|
2023-06-29 00:39:36 -04:00
|
|
|
x_get_pictform_for_visual(struct x_connection *c, xcb_visualid_t visual) {
|
2019-03-10 08:34:37 -04:00
|
|
|
x_get_server_pictfmts(c);
|
|
|
|
|
|
|
|
xcb_render_pictvisual_t *pv = xcb_render_util_find_visual_format(g_pictfmts, visual);
|
|
|
|
for (xcb_render_pictforminfo_iterator_t i =
|
|
|
|
xcb_render_query_pict_formats_formats_iterator(g_pictfmts);
|
|
|
|
i.rem; xcb_render_pictforminfo_next(&i)) {
|
|
|
|
if (i.data->id == pv->format) {
|
|
|
|
return i.data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
2018-09-23 14:10:46 -04:00
|
|
|
}
|
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
static xcb_visualid_t attr_pure x_get_visual_for_pictfmt(xcb_render_query_pict_formats_reply_t *r,
|
|
|
|
xcb_render_pictformat_t fmt) {
|
|
|
|
for (auto screen = xcb_render_query_pict_formats_screens_iterator(r); screen.rem;
|
|
|
|
xcb_render_pictscreen_next(&screen)) {
|
|
|
|
for (auto depth = xcb_render_pictscreen_depths_iterator(screen.data);
|
|
|
|
depth.rem; xcb_render_pictdepth_next(&depth)) {
|
|
|
|
for (auto pv = xcb_render_pictdepth_visuals_iterator(depth.data);
|
|
|
|
pv.rem; xcb_render_pictvisual_next(&pv)) {
|
|
|
|
if (pv.data->format == fmt) {
|
|
|
|
return pv.data->visual;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return XCB_NONE;
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
|
2019-03-10 08:34:37 -04:00
|
|
|
x_get_server_pictfmts(c);
|
2019-02-26 18:52:37 -05:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
|
2019-02-26 18:52:37 -05:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
|
2020-08-19 14:22:17 -04:00
|
|
|
xcb_render_pictformat_t
|
2023-06-29 00:39:36 -04:00
|
|
|
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
|
2020-08-19 14:22:17 -04:00
|
|
|
x_get_server_pictfmts(c);
|
|
|
|
|
|
|
|
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
|
|
|
|
|
|
|
|
return pictfmt->id;
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
int x_get_visual_depth(struct x_connection *c, xcb_visualid_t visual) {
|
|
|
|
auto setup = xcb_get_setup(c->c);
|
2019-03-10 08:34:37 -04:00
|
|
|
for (auto screen = xcb_setup_roots_iterator(setup); screen.rem;
|
|
|
|
xcb_screen_next(&screen)) {
|
|
|
|
for (auto depth = xcb_screen_allowed_depths_iterator(screen.data);
|
|
|
|
depth.rem; xcb_depth_next(&depth)) {
|
|
|
|
const int len = xcb_depth_visuals_length(depth.data);
|
|
|
|
const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
if (visual == visuals[i].visual_id) {
|
|
|
|
return depth.data->depth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
2019-02-03 11:48:52 -05:00
|
|
|
}
|
|
|
|
|
2018-09-23 14:10:46 -04:00
|
|
|
xcb_render_picture_t
|
2023-06-29 00:39:36 -04:00
|
|
|
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *c,
|
2019-03-10 08:34:37 -04:00
|
|
|
const xcb_render_pictforminfo_t *pictfmt,
|
2019-03-30 05:07:21 -04:00
|
|
|
xcb_pixmap_t pixmap, uint32_t valuemask,
|
2019-03-10 08:34:37 -04:00
|
|
|
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) {
|
|
|
|
log_error("failed to serialize picture attributes");
|
|
|
|
return XCB_NONE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-08 00:49:39 -04:00
|
|
|
xcb_render_picture_t tmp_picture = x_new_id(c);
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_generic_error_t *e = xcb_request_check(
|
|
|
|
c->c, xcb_render_create_picture_checked(c->c, tmp_picture, pixmap,
|
|
|
|
pictfmt->id, valuemask, buf));
|
2019-03-10 08:34:37 -04:00
|
|
|
free(buf);
|
|
|
|
if (e) {
|
2020-03-27 20:15:45 -04:00
|
|
|
log_error_x_error(e, "failed to create picture");
|
2022-12-21 15:19:51 -05:00
|
|
|
free(e);
|
2019-03-10 08:34:37 -04:00
|
|
|
return XCB_NONE;
|
|
|
|
}
|
|
|
|
return tmp_picture;
|
2018-09-23 14:10:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
xcb_render_picture_t
|
2023-06-29 00:39:36 -04:00
|
|
|
x_create_picture_with_visual_and_pixmap(struct x_connection *c, xcb_visualid_t visual,
|
2019-03-30 05:07:21 -04:00
|
|
|
xcb_pixmap_t pixmap, uint32_t valuemask,
|
2019-03-10 08:34:37 -04:00
|
|
|
const xcb_render_create_picture_value_list_t *attr) {
|
|
|
|
const xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(c, visual);
|
|
|
|
return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
|
2018-09-23 14:10:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
xcb_render_picture_t
|
2023-06-29 00:39:36 -04:00
|
|
|
x_create_picture_with_standard_and_pixmap(struct x_connection *c, xcb_pict_standard_t standard,
|
2019-03-30 05:07:21 -04:00
|
|
|
xcb_pixmap_t pixmap, uint32_t valuemask,
|
2019-03-10 08:34:37 -04:00
|
|
|
const xcb_render_create_picture_value_list_t *attr) {
|
|
|
|
x_get_server_pictfmts(c);
|
|
|
|
|
|
|
|
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
|
|
|
|
assert(pictfmt);
|
|
|
|
return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
|
2018-09-23 14:10:46 -04:00
|
|
|
}
|
|
|
|
|
2020-08-19 14:22:17 -04:00
|
|
|
xcb_render_picture_t
|
2023-06-29 00:39:36 -04:00
|
|
|
x_create_picture_with_standard(struct x_connection *c, int w, int h,
|
2020-08-19 14:22:17 -04:00
|
|
|
xcb_pict_standard_t standard, uint32_t valuemask,
|
|
|
|
const xcb_render_create_picture_value_list_t *attr) {
|
|
|
|
x_get_server_pictfmts(c);
|
|
|
|
|
|
|
|
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
|
|
|
|
assert(pictfmt);
|
2023-06-29 00:39:36 -04:00
|
|
|
return x_create_picture_with_pictfmt(c, w, h, pictfmt, valuemask, attr);
|
2020-08-19 14:22:17 -04:00
|
|
|
}
|
|
|
|
|
2018-09-23 14:10:46 -04:00
|
|
|
/**
|
|
|
|
* Create an picture.
|
|
|
|
*/
|
|
|
|
xcb_render_picture_t
|
2023-06-29 00:39:36 -04:00
|
|
|
x_create_picture_with_pictfmt(struct x_connection *c, int w, int h,
|
2019-03-30 05:07:21 -04:00
|
|
|
const xcb_render_pictforminfo_t *pictfmt, uint32_t valuemask,
|
2019-03-10 08:34:37 -04:00
|
|
|
const xcb_render_create_picture_value_list_t *attr) {
|
2019-03-30 05:07:21 -04:00
|
|
|
uint8_t depth = pictfmt->depth;
|
2018-09-23 14:10:46 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_pixmap_t tmp_pixmap = x_create_pixmap(c, depth, w, h);
|
2020-10-21 20:39:51 -04:00
|
|
|
if (!tmp_pixmap) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return XCB_NONE;
|
2020-10-21 20:39:51 -04:00
|
|
|
}
|
2018-09-23 14:10:46 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
xcb_render_picture_t picture = x_create_picture_with_pictfmt_and_pixmap(
|
|
|
|
c, pictfmt, tmp_pixmap, valuemask, attr);
|
2018-09-30 15:53:52 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
set_cant_fail_cookie(c, xcb_free_pixmap(c->c, tmp_pixmap));
|
2018-09-23 14:10:46 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
return picture;
|
2018-09-23 14:10:46 -04:00
|
|
|
}
|
2018-09-29 18:36:53 -04:00
|
|
|
|
2018-12-21 18:44:42 -05:00
|
|
|
xcb_render_picture_t
|
2023-06-29 00:39:36 -04:00
|
|
|
x_create_picture_with_visual(struct x_connection *c, int w, int h, xcb_visualid_t visual,
|
|
|
|
uint32_t valuemask,
|
2019-03-10 08:34:37 -04:00
|
|
|
const xcb_render_create_picture_value_list_t *attr) {
|
|
|
|
auto pictfmt = x_get_pictform_for_visual(c, visual);
|
2023-06-29 00:39:36 -04:00
|
|
|
return x_create_picture_with_pictfmt(c, w, h, pictfmt, valuemask, attr);
|
2018-12-21 18:44:42 -05:00
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
bool x_fetch_region(struct x_connection *c, xcb_xfixes_region_t r, pixman_region32_t *res) {
|
2019-03-10 08:34:37 -04:00
|
|
|
xcb_generic_error_t *e = NULL;
|
|
|
|
xcb_xfixes_fetch_region_reply_t *xr =
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_xfixes_fetch_region_reply(c->c, xcb_xfixes_fetch_region(c->c, r), &e);
|
2019-03-10 08:34:37 -04:00
|
|
|
if (!xr) {
|
2020-03-27 20:15:45 -04:00
|
|
|
log_error_x_error(e, "Failed to fetch rectangles");
|
2019-03-10 08:34:37 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nrect = xcb_xfixes_fetch_region_rectangles_length(xr);
|
|
|
|
auto b = ccalloc(nrect, pixman_box32_t);
|
|
|
|
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;
|
2018-09-29 18:36:53 -04:00
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
uint32_t x_create_region(struct x_connection *c, const region_t *reg) {
|
2022-12-14 02:37:27 -05:00
|
|
|
if (!reg) {
|
|
|
|
return XCB_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nrects;
|
2022-12-14 08:57:09 -05:00
|
|
|
// In older pixman versions, pixman_region32_rectangles doesn't take const
|
|
|
|
// region_t, instead of dealing with this version difference, just suppress the
|
|
|
|
// warning.
|
|
|
|
const pixman_box32_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
|
2022-12-14 02:37:27 -05:00
|
|
|
auto xrects = ccalloc(nrects, xcb_rectangle_t);
|
|
|
|
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_xfixes_region_t ret = x_new_id(c);
|
2023-06-29 00:39:36 -04:00
|
|
|
bool success = XCB_AWAIT_VOID(xcb_xfixes_create_region, c->c, ret,
|
|
|
|
to_u32_checked(nrects), xrects);
|
2022-12-14 02:37:27 -05:00
|
|
|
free(xrects);
|
|
|
|
if (!success) {
|
|
|
|
return XCB_NONE;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
void x_destroy_region(struct x_connection *c, xcb_xfixes_region_t r) {
|
2022-12-14 02:37:27 -05:00
|
|
|
if (r != XCB_NONE) {
|
2023-06-29 00:39:36 -04:00
|
|
|
set_debug_cant_fail_cookie(c, xcb_xfixes_destroy_region(c->c, r));
|
2022-12-14 02:37:27 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
void x_set_picture_clip_region(struct x_connection *c, xcb_render_picture_t pict,
|
2019-03-30 05:07:21 -04:00
|
|
|
int16_t clip_x_origin, int16_t clip_y_origin,
|
|
|
|
const region_t *reg) {
|
2019-03-10 08:34:37 -04:00
|
|
|
int nrects;
|
|
|
|
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
|
|
|
|
auto xrects = ccalloc(nrects, xcb_rectangle_t);
|
2020-10-21 20:39:51 -04:00
|
|
|
for (int i = 0; i < nrects; i++) {
|
2019-03-10 08:34:37 -04:00
|
|
|
xrects[i] = (xcb_rectangle_t){
|
2019-03-30 05:07:21 -04:00
|
|
|
.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),
|
2019-03-10 08:34:37 -04:00
|
|
|
};
|
2020-10-21 20:39:51 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_generic_error_t *e =
|
|
|
|
xcb_request_check(c->c, xcb_render_set_picture_clip_rectangles_checked(
|
|
|
|
c->c, pict, clip_x_origin, clip_y_origin,
|
|
|
|
to_u32_checked(nrects), xrects));
|
2020-03-27 20:15:45 -04:00
|
|
|
if (e) {
|
|
|
|
log_error_x_error(e, "Failed to set clip region");
|
|
|
|
free(e);
|
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
free(xrects);
|
2018-09-29 18:36:53 -04:00
|
|
|
}
|
2018-09-30 09:37:21 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
void x_clear_picture_clip_region(struct x_connection *c, xcb_render_picture_t pict) {
|
2022-12-18 14:23:06 -05:00
|
|
|
assert(pict != XCB_NONE);
|
2019-03-10 08:34:37 -04:00
|
|
|
xcb_render_change_picture_value_list_t v = {.clipmask = XCB_NONE};
|
|
|
|
xcb_generic_error_t *e = xcb_request_check(
|
2023-06-29 00:39:36 -04:00
|
|
|
c->c, xcb_render_change_picture_checked(c->c, pict, XCB_RENDER_CP_CLIP_MASK, &v));
|
2020-03-27 20:15:45 -04:00
|
|
|
if (e) {
|
|
|
|
log_error_x_error(e, "failed to clear clip region");
|
|
|
|
free(e);
|
|
|
|
}
|
2018-12-21 18:44:42 -05:00
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
/**
|
|
|
|
* Destroy a <code>Picture</code>.
|
|
|
|
*
|
|
|
|
* Picture must be valid.
|
|
|
|
*/
|
|
|
|
void x_free_picture(struct x_connection *c, xcb_render_picture_t p) {
|
|
|
|
assert(p != XCB_NONE);
|
|
|
|
auto cookie = xcb_render_free_picture(c->c, p);
|
2023-07-03 22:42:10 -04:00
|
|
|
set_debug_cant_fail_cookie(c, cookie);
|
2023-06-29 00:39:36 -04:00
|
|
|
}
|
|
|
|
|
2020-12-26 02:39:21 -05:00
|
|
|
enum {
|
|
|
|
XSyncBadCounter = 0,
|
|
|
|
XSyncBadAlarm = 1,
|
|
|
|
XSyncBadFence = 2,
|
2018-12-30 02:06:47 -05:00
|
|
|
};
|
|
|
|
|
2018-09-30 09:37:21 -04:00
|
|
|
/**
|
2020-03-27 19:07:19 -04:00
|
|
|
* Convert a X11 error to string
|
2018-09-30 09:37:21 -04:00
|
|
|
*
|
2020-03-27 19:07:19 -04:00
|
|
|
* @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
|
|
|
|
* for multiple calls to this function,
|
2018-09-30 09:37:21 -04:00
|
|
|
*/
|
2020-03-27 19:07:19 -04:00
|
|
|
static const char *
|
|
|
|
_x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) {
|
2019-03-10 08:34:37 -04:00
|
|
|
session_t *const ps = ps_g;
|
|
|
|
|
|
|
|
int o = 0;
|
|
|
|
const char *name = "Unknown";
|
|
|
|
|
2020-03-31 00:46:28 -04:00
|
|
|
#define CASESTRRET(s) \
|
|
|
|
case s: \
|
|
|
|
name = #s; \
|
|
|
|
break
|
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
#define CASESTRRET2(s) \
|
2020-03-31 00:46:28 -04:00
|
|
|
case XCB_##s: name = #s; break
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2020-08-30 10:25:58 -04:00
|
|
|
// TODO(yshui) separate error code out from session_t
|
2019-03-10 08:34:37 -04:00
|
|
|
o = error_code - ps->xfixes_error;
|
2020-03-31 00:46:28 -04:00
|
|
|
switch (o) { CASESTRRET2(XFIXES_BAD_REGION); }
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
o = error_code - ps->damage_error;
|
2020-03-31 00:46:28 -04:00
|
|
|
switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); }
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
o = error_code - ps->render_error;
|
|
|
|
switch (o) {
|
2020-03-31 00:46:28 -04:00
|
|
|
CASESTRRET2(RENDER_PICT_FORMAT);
|
|
|
|
CASESTRRET2(RENDER_PICTURE);
|
|
|
|
CASESTRRET2(RENDER_PICT_OP);
|
|
|
|
CASESTRRET2(RENDER_GLYPH_SET);
|
|
|
|
CASESTRRET2(RENDER_GLYPH);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2018-09-30 09:37:21 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
if (ps->glx_exists) {
|
|
|
|
o = error_code - ps->glx_error;
|
|
|
|
switch (o) {
|
2020-03-31 00:59:44 -04:00
|
|
|
CASESTRRET2(GLX_BAD_CONTEXT);
|
|
|
|
CASESTRRET2(GLX_BAD_CONTEXT_STATE);
|
|
|
|
CASESTRRET2(GLX_BAD_DRAWABLE);
|
|
|
|
CASESTRRET2(GLX_BAD_PIXMAP);
|
|
|
|
CASESTRRET2(GLX_BAD_CONTEXT_TAG);
|
|
|
|
CASESTRRET2(GLX_BAD_CURRENT_WINDOW);
|
|
|
|
CASESTRRET2(GLX_BAD_RENDER_REQUEST);
|
|
|
|
CASESTRRET2(GLX_BAD_LARGE_REQUEST);
|
|
|
|
CASESTRRET2(GLX_UNSUPPORTED_PRIVATE_REQUEST);
|
|
|
|
CASESTRRET2(GLX_BAD_FB_CONFIG);
|
|
|
|
CASESTRRET2(GLX_BAD_PBUFFER);
|
|
|
|
CASESTRRET2(GLX_BAD_CURRENT_DRAWABLE);
|
|
|
|
CASESTRRET2(GLX_BAD_WINDOW);
|
|
|
|
CASESTRRET2(GLX_GLX_BAD_PROFILE_ARB);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
}
|
2018-09-30 09:37:21 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
if (ps->xsync_exists) {
|
|
|
|
o = error_code - ps->xsync_error;
|
|
|
|
switch (o) {
|
2020-03-31 00:46:28 -04:00
|
|
|
CASESTRRET(XSyncBadCounter);
|
|
|
|
CASESTRRET(XSyncBadAlarm);
|
|
|
|
CASESTRRET(XSyncBadFence);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (error_code) {
|
2020-03-31 00:46:28 -04:00
|
|
|
CASESTRRET2(ACCESS);
|
|
|
|
CASESTRRET2(ALLOC);
|
|
|
|
CASESTRRET2(ATOM);
|
|
|
|
CASESTRRET2(COLORMAP);
|
|
|
|
CASESTRRET2(CURSOR);
|
|
|
|
CASESTRRET2(DRAWABLE);
|
|
|
|
CASESTRRET2(FONT);
|
|
|
|
CASESTRRET2(G_CONTEXT);
|
|
|
|
CASESTRRET2(ID_CHOICE);
|
|
|
|
CASESTRRET2(IMPLEMENTATION);
|
|
|
|
CASESTRRET2(LENGTH);
|
|
|
|
CASESTRRET2(MATCH);
|
|
|
|
CASESTRRET2(NAME);
|
|
|
|
CASESTRRET2(PIXMAP);
|
|
|
|
CASESTRRET2(REQUEST);
|
|
|
|
CASESTRRET2(VALUE);
|
|
|
|
CASESTRRET2(WINDOW);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2018-09-30 09:37:21 -04:00
|
|
|
|
2020-03-31 00:46:28 -04:00
|
|
|
#undef CASESTRRET
|
2018-09-30 09:37:21 -04:00
|
|
|
#undef CASESTRRET2
|
|
|
|
|
2020-03-27 19:07:19 -04:00
|
|
|
thread_local static char buffer[256];
|
|
|
|
snprintf(buffer, sizeof(buffer), "X error %d %s request %d minor %d serial %lu",
|
|
|
|
error_code, name, major, minor, serial);
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log a X11 error
|
|
|
|
*/
|
2022-12-14 09:23:55 -05:00
|
|
|
void x_log_error(enum log_level level, unsigned long serial, uint8_t major,
|
|
|
|
uint16_t minor, uint8_t error_code) {
|
|
|
|
if (unlikely(level >= log_get_level_tls())) {
|
|
|
|
log_printf(tls_logger, level, __func__, "%s",
|
|
|
|
_x_strerror(serial, major, minor, error_code));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 19:07:19 -04:00
|
|
|
void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) {
|
2022-12-14 09:23:55 -05:00
|
|
|
x_log_error(LOG_LEVEL_DEBUG, serial, major, minor, error_code);
|
2020-03-27 19:07:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert a xcb_generic_error_t to a string that describes the error
|
|
|
|
*
|
|
|
|
* @return a pointer to a string. this pointer shouldn NOT be freed, same buffer is used
|
|
|
|
* for multiple calls to this function,
|
|
|
|
*/
|
|
|
|
const char *x_strerror(xcb_generic_error_t *e) {
|
2020-04-05 16:45:51 -04:00
|
|
|
if (!e) {
|
|
|
|
return "No error";
|
|
|
|
}
|
2020-03-27 19:07:19 -04:00
|
|
|
return _x_strerror(e->full_sequence, e->major_code, e->minor_code, e->error_code);
|
2018-09-30 09:37:21 -04:00
|
|
|
}
|
2018-09-30 15:53:52 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a pixmap and check that creation succeeded.
|
|
|
|
*/
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_pixmap_t x_create_pixmap(struct x_connection *c, uint8_t depth, int width, int height) {
|
2019-04-08 00:49:39 -04:00
|
|
|
xcb_pixmap_t pix = x_new_id(c);
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_void_cookie_t cookie =
|
|
|
|
xcb_create_pixmap_checked(c->c, depth, pix, c->screen_info->root,
|
|
|
|
to_u16_checked(width), to_u16_checked(height));
|
|
|
|
xcb_generic_error_t *err = xcb_request_check(c->c, cookie);
|
2020-10-21 20:39:51 -04:00
|
|
|
if (err == NULL) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return pix;
|
2020-10-21 20:39:51 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2020-03-27 20:15:45 -04:00
|
|
|
log_error_x_error(err, "Failed to create pixmap");
|
2019-03-10 08:34:37 -04:00
|
|
|
free(err);
|
|
|
|
return XCB_NONE;
|
2018-09-30 15:53:52 -04:00
|
|
|
}
|
2018-12-15 16:11:41 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Validate a pixmap.
|
|
|
|
*
|
|
|
|
* Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
|
|
|
|
* are better ways.
|
|
|
|
*/
|
2023-06-29 00:39:36 -04:00
|
|
|
bool x_validate_pixmap(struct x_connection *c, xcb_pixmap_t pixmap) {
|
2019-03-10 08:34:37 -04:00
|
|
|
if (pixmap == XCB_NONE) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
auto r = xcb_get_geometry_reply(c->c, xcb_get_geometry(c->c, pixmap), NULL);
|
2019-03-10 08:34:37 -04:00
|
|
|
if (!r) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ret = r->width && r->height;
|
|
|
|
free(r);
|
|
|
|
return ret;
|
2018-12-15 16:11:41 -05:00
|
|
|
}
|
2018-12-21 18:44:42 -05:00
|
|
|
|
2023-06-18 09:09:29 -04:00
|
|
|
/// We don't use the _XSETROOT_ID root window property as a source of the background
|
|
|
|
/// pixmap because it most likely points to a dummy pixmap used to keep the colormap
|
|
|
|
/// associated with the background pixmap alive but we listen for it's changes and update
|
|
|
|
/// the background pixmap accordingly.
|
|
|
|
///
|
|
|
|
/// For details on the _XSETROOT_ID root window property and it's usage see:
|
|
|
|
/// https://metacpan.org/pod/X11::Protocol::XSetRoot#_XSETROOT_ID
|
|
|
|
/// https://gitlab.freedesktop.org/xorg/app/xsetroot/-/blob/435d35409768de7cbc2c47a6322192dd4b480545/xsetroot.c#L318-352
|
|
|
|
/// https://github.com/ImageMagick/ImageMagick/blob/d04a47227637dbb3af9231b0107ccf9677bf985e/MagickCore/xwindow.c#L9203-L9260
|
|
|
|
/// https://github.com/ImageMagick/ImageMagick/blob/d04a47227637dbb3af9231b0107ccf9677bf985e/MagickCore/xwindow.c#L1853-L1922
|
|
|
|
/// https://www.fvwm.org/Archive/Manpages/fvwm-root.html
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_pixmap_t x_get_root_back_pixmap(struct x_connection *c, struct atom *atoms) {
|
2019-03-10 08:34:37 -04:00
|
|
|
xcb_pixmap_t pixmap = XCB_NONE;
|
|
|
|
|
2023-06-18 08:45:06 -04:00
|
|
|
xcb_atom_t root_back_pixmap_atoms[] = {atoms->a_XROOTPMAP_ID, atoms->aESETROOT_PMAP_ID};
|
|
|
|
for (size_t i = 0; i < ARR_SIZE(root_back_pixmap_atoms); i++) {
|
|
|
|
winprop_t prop =
|
2023-06-29 00:39:36 -04:00
|
|
|
x_get_prop(c, c->screen_info->root, root_back_pixmap_atoms[i], 1,
|
|
|
|
XCB_ATOM_PIXMAP, 32);
|
2019-03-10 08:34:37 -04:00
|
|
|
if (prop.nitems) {
|
2019-03-30 05:07:21 -04:00
|
|
|
pixmap = (xcb_pixmap_t)*prop.p32;
|
2019-03-10 08:34:37 -04:00
|
|
|
free_winprop(&prop);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free_winprop(&prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pixmap;
|
2018-12-21 18:44:42 -05:00
|
|
|
}
|
|
|
|
|
2020-12-26 02:51:03 -05:00
|
|
|
bool x_is_root_back_pixmap_atom(struct atom *atoms, xcb_atom_t atom) {
|
2023-06-18 08:32:15 -04:00
|
|
|
return atom == atoms->a_XROOTPMAP_ID || atom == atoms->aESETROOT_PMAP_ID ||
|
|
|
|
atom == atoms->a_XSETROOT_ID;
|
2018-12-21 18:44:42 -05:00
|
|
|
}
|
2018-12-30 02:06:47 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Synchronizes a X Render drawable to ensure all pending painting requests
|
|
|
|
* are completed.
|
|
|
|
*/
|
2023-06-29 00:39:36 -04:00
|
|
|
bool x_fence_sync(struct x_connection *c, xcb_sync_fence_t f) {
|
2019-03-10 08:34:37 -04:00
|
|
|
// TODO(richardgv): If everybody just follows the rules stated in X Sync
|
|
|
|
// prototype, we need only one fence per screen, but let's stay a bit
|
|
|
|
// cautious right now
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
auto e = xcb_request_check(c->c, xcb_sync_trigger_fence_checked(c->c, f));
|
2019-03-10 08:34:37 -04:00
|
|
|
if (e) {
|
2020-03-27 20:15:45 -04:00
|
|
|
log_error_x_error(e, "Failed to trigger the fence");
|
|
|
|
goto err;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
e = xcb_request_check(c->c, xcb_sync_await_fence_checked(c->c, 1, &f));
|
2019-03-10 08:34:37 -04:00
|
|
|
if (e) {
|
2020-03-27 20:15:45 -04:00
|
|
|
log_error_x_error(e, "Failed to await on a fence");
|
|
|
|
goto err;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
e = xcb_request_check(c->c, xcb_sync_reset_fence_checked(c->c, f));
|
2019-03-10 08:34:37 -04:00
|
|
|
if (e) {
|
2020-03-27 20:15:45 -04:00
|
|
|
log_error_x_error(e, "Failed to reset the fence");
|
|
|
|
goto err;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
return true;
|
2020-03-27 20:15:45 -04:00
|
|
|
|
|
|
|
err:
|
|
|
|
free(e);
|
|
|
|
return false;
|
2018-12-30 02:06:47 -05:00
|
|
|
}
|
2019-02-17 18:47:46 -05:00
|
|
|
|
2023-07-09 11:39:44 -04:00
|
|
|
void x_request_vblank_event(struct x_connection *c, xcb_window_t window, uint64_t msc) {
|
2023-12-18 04:33:17 -05:00
|
|
|
auto cookie = xcb_present_notify_msc(c->c, window, 0, msc, 1, 0);
|
2023-07-09 11:39:44 -04:00
|
|
|
set_cant_fail_cookie(c, cookie);
|
2023-07-05 00:53:21 -04:00
|
|
|
}
|
|
|
|
|
2023-07-05 00:54:44 -04:00
|
|
|
static inline bool dpms_screen_is_off(xcb_dpms_info_reply_t *info) {
|
|
|
|
// state is a bool indicating whether dpms is enabled
|
|
|
|
return info->state && (info->power_level != XCB_DPMS_DPMS_MODE_ON);
|
|
|
|
}
|
|
|
|
|
2023-07-09 11:39:44 -04:00
|
|
|
bool x_check_dpms_status(struct x_connection *c, bool *screen_is_off) {
|
|
|
|
auto r = xcb_dpms_info_reply(c->c, xcb_dpms_info(c->c), NULL);
|
2023-07-05 00:54:44 -04:00
|
|
|
if (!r) {
|
2023-07-09 11:39:44 -04:00
|
|
|
log_error("Failed to query DPMS status.");
|
|
|
|
return false;
|
2023-07-05 00:54:44 -04:00
|
|
|
}
|
|
|
|
auto now_screen_is_off = dpms_screen_is_off(r);
|
2023-07-09 11:39:44 -04:00
|
|
|
if (*screen_is_off != now_screen_is_off) {
|
2023-07-05 00:54:44 -04:00
|
|
|
log_debug("Screen is now %s", now_screen_is_off ? "off" : "on");
|
2023-07-09 11:39:44 -04:00
|
|
|
*screen_is_off = now_screen_is_off;
|
2023-07-05 00:54:44 -04:00
|
|
|
}
|
|
|
|
free(r);
|
2023-07-09 11:39:44 -04:00
|
|
|
return true;
|
2023-07-05 00:54:44 -04:00
|
|
|
}
|
|
|
|
|
2019-02-17 18:47:46 -05:00
|
|
|
/**
|
2019-02-20 11:43:42 -05:00
|
|
|
* Convert a struct conv to a X picture convolution filter, normalizing the kernel
|
|
|
|
* in the process. Allow the caller to specify the element at the center of the kernel,
|
|
|
|
* for compatibility with legacy code.
|
2019-02-17 18:47:46 -05:00
|
|
|
*
|
2019-02-20 11:43:42 -05:00
|
|
|
* @param[in] kernel the convolution kernel
|
|
|
|
* @param[in] center the element to put at the center of the matrix
|
|
|
|
* @param[inout] ret pointer to an array of `size`, if `size` is too small, more space
|
|
|
|
* will be allocated, and `*ret` will be updated
|
|
|
|
* @param[inout] size size of the array pointed to by `ret`, in number of elements
|
|
|
|
* @return number of elements filled into `*ret`
|
2019-02-17 18:47:46 -05:00
|
|
|
*/
|
2019-06-06 02:37:48 -04:00
|
|
|
void x_create_convolution_kernel(const conv *kernel, double center,
|
|
|
|
struct x_convolution_kernel **ret) {
|
|
|
|
assert(ret);
|
|
|
|
if (!*ret || (*ret)->capacity < kernel->w * kernel->h + 2) {
|
|
|
|
free(*ret);
|
|
|
|
*ret =
|
|
|
|
cvalloc(sizeof(struct x_convolution_kernel) +
|
|
|
|
(size_t)(kernel->w * kernel->h + 2) * sizeof(xcb_render_fixed_t));
|
|
|
|
(*ret)->capacity = kernel->w * kernel->h + 2;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2019-06-06 02:37:48 -04:00
|
|
|
|
|
|
|
(*ret)->size = kernel->w * kernel->h + 2;
|
|
|
|
|
|
|
|
auto buf = (*ret)->kernel;
|
2019-03-10 08:34:37 -04:00
|
|
|
buf[0] = DOUBLE_TO_XFIXED(kernel->w);
|
|
|
|
buf[1] = DOUBLE_TO_XFIXED(kernel->h);
|
2019-06-06 02:37:48 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
double sum = center;
|
|
|
|
for (int i = 0; i < kernel->w * kernel->h; i++) {
|
2019-07-07 14:51:42 -04:00
|
|
|
if (i == kernel->w * kernel->h / 2) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
sum += kernel->data[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note for floating points a / b != a * (1 / b), but this shouldn't have any real
|
|
|
|
// impact on the result
|
|
|
|
double factor = sum != 0 ? 1.0 / sum : 1;
|
|
|
|
for (int i = 0; i < kernel->w * kernel->h; i++) {
|
|
|
|
buf[i + 2] = DOUBLE_TO_XFIXED(kernel->data[i] * factor);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[kernel->h / 2 * kernel->w + kernel->w / 2 + 2] =
|
|
|
|
DOUBLE_TO_XFIXED(center * factor);
|
2019-02-17 18:47:46 -05:00
|
|
|
}
|
2019-02-26 18:52:37 -05:00
|
|
|
|
|
|
|
/// Generate a search criteria for fbconfig from a X visual.
|
2019-03-30 05:07:21 -04:00
|
|
|
/// Returns {-1, -1, -1, -1, -1, 0} on failure
|
2023-06-29 00:39:36 -04:00
|
|
|
struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t visual) {
|
2019-02-26 18:52:37 -05:00
|
|
|
auto pictfmt = x_get_pictform_for_visual(c, visual);
|
|
|
|
auto depth = x_get_visual_depth(c, visual);
|
|
|
|
if (!pictfmt || depth == -1) {
|
|
|
|
log_error("Invalid visual %#03x", visual);
|
2019-03-30 05:07:21 -04:00
|
|
|
return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
if (pictfmt->type != XCB_RENDER_PICT_TYPE_DIRECT) {
|
2019-10-23 14:27:30 -04:00
|
|
|
log_error("We cannot handle non-DirectColor visuals. Report an "
|
2019-02-26 18:52:37 -05:00
|
|
|
"issue if you see this error message.");
|
2019-03-30 05:07:21 -04:00
|
|
|
return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
|
2019-02-26 18:52:37 -05:00
|
|
|
}
|
|
|
|
|
2020-10-07 14:35:55 -04:00
|
|
|
int red_size = popcntul(pictfmt->direct.red_mask),
|
|
|
|
blue_size = popcntul(pictfmt->direct.blue_mask),
|
|
|
|
green_size = popcntul(pictfmt->direct.green_mask),
|
|
|
|
alpha_size = popcntul(pictfmt->direct.alpha_mask);
|
2019-02-26 18:52:37 -05:00
|
|
|
|
|
|
|
return (struct xvisual_info){
|
|
|
|
.red_size = red_size,
|
|
|
|
.green_size = green_size,
|
|
|
|
.blue_size = blue_size,
|
|
|
|
.alpha_size = alpha_size,
|
|
|
|
.visual_depth = depth,
|
|
|
|
.visual = visual,
|
|
|
|
};
|
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
xcb_screen_t *x_screen_of_display(struct x_connection *c, int screen) {
|
2019-03-30 05:07:21 -04:00
|
|
|
xcb_screen_iterator_t iter;
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
iter = xcb_setup_roots_iterator(xcb_get_setup(c->c));
|
2020-10-21 20:39:51 -04:00
|
|
|
for (; iter.rem; --screen, xcb_screen_next(&iter)) {
|
|
|
|
if (screen == 0) {
|
2019-03-30 05:07:21 -04:00
|
|
|
return iter.data;
|
2020-10-21 20:39:51 -04:00
|
|
|
}
|
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2023-01-13 06:27:45 -05:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
void x_update_monitors(struct x_connection *c, struct x_monitors *m) {
|
|
|
|
x_free_monitor_info(m);
|
2023-01-13 06:27:45 -05:00
|
|
|
|
|
|
|
xcb_randr_get_monitors_reply_t *r = xcb_randr_get_monitors_reply(
|
2023-06-29 00:39:36 -04:00
|
|
|
c->c, xcb_randr_get_monitors(c->c, c->screen_info->root, true), NULL);
|
2023-01-13 06:27:45 -05:00
|
|
|
if (!r) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
m->count = xcb_randr_get_monitors_monitors_length(r);
|
|
|
|
m->regions = ccalloc(m->count, region_t);
|
2023-01-13 06:27:45 -05:00
|
|
|
xcb_randr_monitor_info_iterator_t monitor_info_it =
|
|
|
|
xcb_randr_get_monitors_monitors_iterator(r);
|
|
|
|
for (int i = 0; monitor_info_it.rem; xcb_randr_monitor_info_next(&monitor_info_it)) {
|
|
|
|
xcb_randr_monitor_info_t *mi = monitor_info_it.data;
|
2023-06-29 00:39:36 -04:00
|
|
|
pixman_region32_init_rect(&m->regions[i++], mi->x, mi->y, mi->width, mi->height);
|
2023-01-13 06:27:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
free(r);
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
void x_free_monitor_info(struct x_monitors *m) {
|
|
|
|
if (m->regions) {
|
|
|
|
for (int i = 0; i < m->count; i++) {
|
|
|
|
pixman_region32_fini(&m->regions[i]);
|
2023-01-13 06:27:45 -05:00
|
|
|
}
|
2023-06-29 00:39:36 -04:00
|
|
|
free(m->regions);
|
|
|
|
m->regions = NULL;
|
2023-01-13 06:27:45 -05:00
|
|
|
}
|
2023-06-29 00:39:36 -04:00
|
|
|
m->count = 0;
|
2023-01-13 06:27:45 -05:00
|
|
|
}
|