diff --git a/src/backend/backend.c b/src/backend/backend.c index 6cfe7fb..f5b5479 100644 --- a/src/backend/backend.c +++ b/src/backend/backend.c @@ -197,6 +197,8 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { // region will be because of blur, we assume the worst case here. // That is, the damaged window is at the bottom of the stack, and // all other windows have semi-transparent background + // + // TODO(yshui): maybe we don't need to resize reg_damage, only reg_paint? int resize_factor = 1; if (t) { resize_factor = t->stacking_rank; diff --git a/src/backend/xrender/xrender.c b/src/backend/xrender/xrender.c index e85a85f..4381977 100644 --- a/src/backend/xrender/xrender.c +++ b/src/backend/xrender/xrender.c @@ -594,25 +594,28 @@ static void present(backend_t *base, const region_t *region) { uint16_t region_width = to_u16_checked(extent->x2 - extent->x1), region_height = to_u16_checked(extent->y2 - extent->y1); - // compose() sets clip region on the back buffer, so clear it first - x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]); - // limit the region of update x_set_picture_clip_region(base->c, xd->back[2], 0, 0, region); if (xd->vsync) { + // compose() sets clip region on the back buffer, so clear it first + x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]); + // Update the back buffer first, then present xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2], XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0, 0, orig_x, orig_y, region_width, region_height); + auto xregion = x_create_region(base->c, region); + // Make sure we got reply from PresentPixmap before waiting for events, // to avoid deadlock auto e = xcb_request_check( base->c, xcb_present_pixmap_checked( xd->base.c, xd->target_win, - xd->back_pixmap[xd->curr_back], 0, XCB_NONE, XCB_NONE, 0, + xd->back_pixmap[xd->curr_back], 0, XCB_NONE, xregion, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL)); + x_destroy_region(base->c, xregion); if (e) { log_error("Failed to present pixmap"); free(e); diff --git a/src/common.h b/src/common.h index cb02da3..f1e5d8a 100644 --- a/src/common.h +++ b/src/common.h @@ -36,9 +36,9 @@ #include #include #include -#include #include #include +#include #include "uthash_extra.h" #ifdef CONFIG_OPENGL @@ -55,11 +55,11 @@ #include "backend/driver.h" #include "compiler.h" #include "config.h" +#include "list.h" #include "region.h" +#include "render.h" #include "types.h" #include "utils.h" -#include "list.h" -#include "render.h" #include "win_defs.h" #include "x.h" @@ -83,10 +83,17 @@ struct glx_session; struct atom; struct conv; -typedef struct _ignore { - struct _ignore *next; +enum pending_reply_action { + PENDING_REPLY_ACTION_IGNORE, + PENDING_REPLY_ACTION_ABORT, + PENDING_REPLY_ACTION_DEBUG_ABORT, +}; + +typedef struct pending_reply { + struct pending_reply *next; unsigned long sequence; -} ignore_t; + enum pending_reply_action action; +} pending_reply_t; #ifdef CONFIG_OPENGL #ifdef DEBUG_GLX_DEBUG_CONTEXT @@ -262,18 +269,18 @@ typedef struct session { /// Time of last window animation step. In milliseconds. long animation_time; // TODO(dccsillag) turn into `long long`, like fade_time /// Head pointer of the error ignore linked list. - ignore_t *ignore_head; + pending_reply_t *pending_reply_head; /// Pointer to the next member of tail element of the error /// ignore linked list. - ignore_t **ignore_tail; + pending_reply_t **pending_reply_tail; // Cached blur convolution kernels. struct x_convolution_kernel **blur_kerns_cache; /// If we should quit - bool quit:1; + bool quit : 1; // TODO(yshui) use separate flags for dfferent kinds of updates so we don't // waste our time. /// Whether there are pending updates, like window creation, etc. - bool pending_updates:1; + bool pending_updates : 1; // === Expose event related === /// Pointer to an array of XRectangle-s of exposed region. @@ -474,25 +481,33 @@ static inline bool bkend_use_glx(session_t *ps) { return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend; } -static void set_ignore(session_t *ps, unsigned long sequence) { - if (ps->o.show_all_xerrors) - return; - - auto i = cmalloc(ignore_t); - if (!i) - return; +static void +set_reply_action(session_t *ps, uint32_t sequence, enum pending_reply_action action) { + auto i = cmalloc(pending_reply_t); + if (!i) { + abort(); + } i->sequence = sequence; i->next = 0; - *ps->ignore_tail = i; - ps->ignore_tail = &i->next; + i->action = action; + *ps->pending_reply_tail = i; + ps->pending_reply_tail = &i->next; } /** * Ignore X errors caused by given X request. */ static inline void set_ignore_cookie(session_t *ps, xcb_void_cookie_t cookie) { - set_ignore(ps, cookie.sequence); + if (ps->o.show_all_xerrors) { + return; + } + + set_reply_action(ps, cookie.sequence, PENDING_REPLY_ACTION_IGNORE); +} + +static inline void set_cant_fail_cookie(session_t *ps, xcb_void_cookie_t cookie) { + set_reply_action(ps, cookie.sequence, PENDING_REPLY_ACTION_ABORT); } /** diff --git a/src/dbus.c b/src/dbus.c index 8b17b30..2b17341 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -1435,7 +1435,10 @@ static bool cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *ms continue; } char *tmp = NULL; - asprintf(&tmp, "\n", w->id); + if (asprintf(&tmp, "\n", w->id) < 0) { + log_fatal("Failed to allocate memory."); + abort(); + } mstrextend(&ret, tmp); free(tmp); } diff --git a/src/event.c b/src/event.c index f84b595..12d2477 100644 --- a/src/event.c +++ b/src/event.c @@ -121,11 +121,13 @@ static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) { CASESTRRET(ClientMessage); } - if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) + if (ps->damage_event + XCB_DAMAGE_NOTIFY == ev->response_type) { return "Damage"; + } - if (ps->shape_exists && ev->response_type == ps->shape_event) + if (ps->shape_exists && ev->response_type == ps->shape_event) { return "ShapeNotify"; + } if (ps->xsync_exists) { int o = ev->response_type - ps->xsync_event; @@ -672,7 +674,7 @@ ev_selection_clear(session_t *ps, xcb_selection_clear_event_t attr_unused *ev) { void ev_handle(session_t *ps, xcb_generic_event_t *ev) { if ((ev->response_type & 0x7f) != KeymapNotify) { - discard_ignore(ps, ev->full_sequence); + discard_pending(ps, ev->full_sequence); } xcb_window_t wid = ev_window(ps, ev); @@ -704,8 +706,11 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) { // missing sequence numbers. // // We only need the low 16 bits + uint16_t seq = ev->sequence; ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->dpy) & 0xffff); proc(ps->dpy, &dummy, (xEvent *)ev); + // Restore the sequence number + ev->sequence = seq; } // XXX redraw needs to be more fine grained diff --git a/src/log.c b/src/log.c index 0b663e7..8a494b9 100644 --- a/src/log.c +++ b/src/log.c @@ -256,7 +256,7 @@ static void file_logger_write(struct log_target *tgt, const char *str, size_t le static void file_logger_writev(struct log_target *tgt, const struct iovec *vec, int vcnt) { auto f = (struct file_logger *)tgt; fflush(f->f); - writev(fileno(f->f), vec, vcnt); + ssize_t _ attr_unused = writev(fileno(f->f), vec, vcnt); } static void file_logger_destroy(struct log_target *tgt) { diff --git a/src/picom.c b/src/picom.c index 39837d8..cdbebf5 100644 --- a/src/picom.c +++ b/src/picom.c @@ -282,14 +282,14 @@ static bool run_fade(session_t *ps, struct managed_win **_w, long long steps) { // === Error handling === -void discard_ignore(session_t *ps, unsigned long sequence) { - while (ps->ignore_head) { - if (sequence > ps->ignore_head->sequence) { - ignore_t *next = ps->ignore_head->next; - free(ps->ignore_head); - ps->ignore_head = next; - if (!ps->ignore_head) { - ps->ignore_tail = &ps->ignore_head; +void discard_pending(session_t *ps, uint32_t sequence) { + while (ps->pending_reply_head) { + if (sequence > ps->pending_reply_head->sequence) { + auto next = ps->pending_reply_head->next; + free(ps->pending_reply_head); + ps->pending_reply_head = next; + if (!ps->pending_reply_head) { + ps->pending_reply_tail = &ps->pending_reply_head; } } else { break; @@ -297,13 +297,28 @@ void discard_ignore(session_t *ps, unsigned long sequence) { } } -static int should_ignore(session_t *ps, unsigned long sequence) { +static void handle_error(session_t *ps, xcb_generic_error_t *ev) { if (ps == NULL) { // Do not ignore errors until the session has been initialized - return false; + return; } - discard_ignore(ps, sequence); - return ps->ignore_head && ps->ignore_head->sequence == sequence; + discard_pending(ps, ev->full_sequence); + if (ps->pending_reply_head && ps->pending_reply_head->sequence == ev->full_sequence) { + if (ps->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 (ps->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); } // === Windows === @@ -1136,9 +1151,13 @@ void root_damaged(session_t *ps) { * Xlib error handler function. */ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) { - if (!should_ignore(ps_g, ev->serial)) { - x_print_error(ev->serial, ev->request_code, ev->minor_code, ev->error_code); - } + // 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; + handle_error(ps_g, &xcb_err); return 0; } @@ -1146,9 +1165,7 @@ static int xerror(Display attr_unused *dpy, XErrorEvent *ev) { * XCB error handler function. */ void ev_xcb_error(session_t *ps, xcb_generic_error_t *err) { - if (!should_ignore(ps, err->sequence)) { - x_print_error(err->sequence, err->major_code, err->minor_code, err->error_code); - } + handle_error(ps, err); } /** @@ -1864,8 +1881,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy, .alpha_picts = NULL, .fade_time = 0L, .animation_time = 0L, - .ignore_head = NULL, - .ignore_tail = NULL, + .pending_reply_head = NULL, + .pending_reply_tail = NULL, .quit = false, .expose_rects = NULL, @@ -1927,7 +1944,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy, ps->loop = EV_DEFAULT; pixman_region32_init(&ps->screen_reg); - ps->ignore_tail = &ps->ignore_head; + ps->pending_reply_tail = &ps->pending_reply_head; ps->o.show_all_xerrors = all_xerrors; @@ -2528,26 +2545,28 @@ static void session_destroy(session_t *ps) { // Free ignore linked list { - ignore_t *next = NULL; - for (ignore_t *ign = ps->ignore_head; ign; ign = next) { + pending_reply_t *next = NULL; + for (auto ign = ps->pending_reply_head; ign; ign = next) { next = ign->next; free(ign); } // Reset head and tail - ps->ignore_head = NULL; - ps->ignore_tail = &ps->ignore_head; + ps->pending_reply_head = NULL; + ps->pending_reply_tail = &ps->pending_reply_head; } // Free tgt_{buffer,picture} and root_picture - if (ps->tgt_buffer.pict == ps->tgt_picture) + if (ps->tgt_buffer.pict == ps->tgt_picture) { ps->tgt_buffer.pict = XCB_NONE; + } - if (ps->tgt_picture == ps->root_picture) + if (ps->tgt_picture == ps->root_picture) { ps->tgt_picture = XCB_NONE; - else + } else { free_picture(ps->c, &ps->tgt_picture); + } free_picture(ps->c, &ps->root_picture); free_paint(ps, &ps->tgt_buffer); @@ -2759,7 +2778,11 @@ int main(int argc, char **argv) { // Notify the parent that we are done. This might cause the parent // to quit, so only do this after setsid() int tmp = 1; - write(pfds[1], &tmp, sizeof tmp); + if (write(pfds[1], &tmp, sizeof tmp) != sizeof tmp) { + log_fatal("Failed to notify parent process"); + ret_code = 1; + break; + } close(pfds[1]); // We only do this once need_fork = false; diff --git a/src/picom.h b/src/picom.h index 7ee289b..b5a1e8a 100644 --- a/src/picom.h +++ b/src/picom.h @@ -46,7 +46,7 @@ void cxinerama_upd_scrs(session_t *ps); void queue_redraw(session_t *ps); -void discard_ignore(session_t *ps, unsigned long sequence); +void discard_pending(session_t *ps, uint32_t sequence); void set_root_flags(session_t *ps, uint64_t flags); diff --git a/src/utils.c b/src/utils.c index 8a27f39..a1114a0 100644 --- a/src/utils.c +++ b/src/utils.c @@ -4,6 +4,7 @@ #include "compiler.h" #include "string_utils.h" +#include "test.h" #include "utils.h" /// Report allocation failure without allocating memory @@ -26,7 +27,7 @@ void report_allocation_failure(const char *func, const char *file, unsigned int {.iov_base = (void *)msg2, .iov_len = sizeof(msg2) - 1}, }; - writev(STDERR_FILENO, v, ARR_SIZE(v)); + ssize_t _ attr_unused = writev(STDERR_FILENO, v, ARR_SIZE(v)); abort(); unreachable; @@ -36,8 +37,7 @@ void report_allocation_failure(const char *func, const char *file, unsigned int /// Calculates next closest power of two of 32bit integer n /// ref: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 /// -int next_power_of_two(int n) -{ +int next_power_of_two(int n) { n--; n |= n >> 1; n |= n >> 2; @@ -48,4 +48,199 @@ int next_power_of_two(int n) return n; } +/// Track the rolling maximum of a stream of integers. +struct rolling_max { + /// A priority queue holding the indices of the maximum element candidates. + /// The head of the queue is the index of the maximum element. + /// The indices in the queue are the "original" indices. + /// + /// There are only `capacity` elements in `elem`, all previous elements are + /// discarded. But the discarded elements' indices are not forgotten, that's why + /// it's called the "original" indices. + int *p; + int p_head, np; + + /// The elemets + int *elem; + int elem_head, nelem; + + int window_size; +}; + +void rolling_max_destroy(struct rolling_max *rm) { + free(rm->elem); + free(rm->p); + free(rm); +} + +struct rolling_max *rolling_max_new(int size) { + auto rm = ccalloc(1, struct rolling_max); + if (!rm) { + return NULL; + } + + rm->p = ccalloc(size, int); + rm->elem = ccalloc(size, int); + rm->window_size = size; + if (!rm->p || !rm->elem) { + goto err; + } + + return rm; + +err: + rolling_max_destroy(rm); + return NULL; +} + +void rolling_max_reset(struct rolling_max *rm) { + rm->p_head = 0; + rm->np = 0; + rm->nelem = 0; + rm->elem_head = 0; +} + +void rolling_max_push(struct rolling_max *rm, int val) { +#define IDX(n) ((n) % rm->window_size) + if (rm->nelem == rm->window_size) { + auto old_head = rm->elem_head; + // Discard the oldest element. + // rm->elem.pop_front(); + rm->nelem--; + rm->elem_head = IDX(rm->elem_head + 1); + + // Remove discarded element from the priority queue too. + assert(rm->np); + if (rm->p[rm->p_head] == old_head) { + // rm->p.pop_front() + rm->p_head = IDX(rm->p_head + 1); + rm->np--; + } + } + + // Add the new element to the queue. + // rm->elem.push_back(val) + rm->elem[IDX(rm->elem_head + rm->nelem)] = val; + rm->nelem++; + + // Update the prority queue. + // Remove all elements smaller than the new element from the queue. Because + // the new element will become the maximum element before them, and since they + // come b1efore the new element, they will have been popped before the new + // element, so they will never become the maximum element. + while (rm->np) { + int p_tail = IDX(rm->p_head + rm->np - 1); + if (rm->elem[rm->p[p_tail]] > val) { + break; + } + // rm->p.pop_back() + rm->np--; + } + // Add the new element to the end of the queue. + // rm->p.push_back(rm->start_index + rm->nelem - 1) + rm->p[IDX(rm->p_head + rm->np)] = IDX(rm->elem_head + rm->nelem - 1); + rm->np++; +#undef IDX +} + +int rolling_max_get_max(struct rolling_max *rm) { + if (rm->np == 0) { + return INT_MIN; + } + return rm->elem[rm->p[rm->p_head]]; +} + +TEST_CASE(rolling_max_test) { +#define NELEM 15 + auto rm = rolling_max_new(3); + const int data[NELEM] = {1, 2, 3, 1, 4, 5, 2, 3, 6, 5, 4, 3, 2, 0, 0}; + const int expected_max[NELEM] = {1, 2, 3, 3, 4, 5, 5, 5, 6, 6, 6, 5, 4, 3, 2}; + int max[NELEM] = {0}; + for (int i = 0; i < NELEM; i++) { + rolling_max_push(rm, data[i]); + max[i] = rolling_max_get_max(rm); + } + TEST_TRUE(memcmp(max, expected_max, sizeof(max)) == 0); +#undef NELEM +} + +/// A rolling average of a stream of integers. +struct rolling_avg { + /// The sum of the elements in the window. + int64_t sum; + + /// The elements in the window. + int *elem; + int head, nelem; + + int window_size; +}; + +struct rolling_avg *rolling_avg_new(int size) { + auto rm = ccalloc(1, struct rolling_avg); + if (!rm) { + return NULL; + } + + rm->elem = ccalloc(size, int); + rm->window_size = size; + if (!rm->elem) { + free(rm); + return NULL; + } + + return rm; +} + +void rolling_avg_destroy(struct rolling_avg *rm) { + free(rm->elem); + free(rm); +} + +void rolling_avg_reset(struct rolling_avg *ra) { + ra->sum = 0; + ra->nelem = 0; + ra->head = 0; +} + +void rolling_avg_push(struct rolling_avg *ra, int val) { + if (ra->nelem == ra->window_size) { + // Discard the oldest element. + // rm->elem.pop_front(); + ra->sum -= ra->elem[ra->head % ra->window_size]; + ra->nelem--; + ra->head = (ra->head + 1) % ra->window_size; + } + + // Add the new element to the queue. + // rm->elem.push_back(val) + ra->elem[(ra->head + ra->nelem) % ra->window_size] = val; + ra->sum += val; + ra->nelem++; +} + +double rolling_avg_get_avg(struct rolling_avg *ra) { + if (ra->nelem == 0) { + return 0; + } + return (double)ra->sum / (double)ra->nelem; +} + +TEST_CASE(rolling_avg_test) { +#define NELEM 15 + auto rm = rolling_avg_new(3); + const int data[NELEM] = {1, 2, 3, 1, 4, 5, 2, 3, 6, 5, 4, 3, 2, 0, 0}; + const double expected_avg[NELEM] = { + 1, 1.5, 2, 2, 8.0 / 3.0, 10.0 / 3.0, 11.0 / 3.0, 10.0 / 3.0, + 11.0 / 3.0, 14.0 / 3.0, 5, 4, 3, 5.0 / 3.0, 2.0 / 3.0}; + double avg[NELEM] = {0}; + for (int i = 0; i < NELEM; i++) { + rolling_avg_push(rm, data[i]); + avg[i] = rolling_avg_get_avg(rm); + } + for (int i = 0; i < NELEM; i++) { + TEST_EQUAL(avg[i], expected_avg[i]); + } +} + // vim: set noet sw=8 ts=8 : diff --git a/src/utils.h b/src/utils.h index a632107..271d882 100644 --- a/src/utils.h +++ b/src/utils.h @@ -303,6 +303,21 @@ static inline void free_charpp(char **str) { /// int next_power_of_two(int n); +struct rolling_max; + +struct rolling_max *rolling_max_new(int window_size); +void rolling_max_free(struct rolling_max *rm); +void rolling_max_reset(struct rolling_max *rm); +void rolling_max_push(struct rolling_max *rm, int val); +int rolling_max_get_max(struct rolling_max *rm); + +struct rolling_avg; +struct rolling_avg *rolling_avg_new(int window_size); +void rolling_avg_free(struct rolling_avg *ra); +void rolling_avg_reset(struct rolling_avg *ra); +void rolling_avg_push(struct rolling_avg *ra, int val); +double rolling_avg_get_avg(struct rolling_avg *ra); + // Some versions of the Android libc do not have timespec_get(), use // clock_gettime() instead. #ifdef __ANDROID__ diff --git a/src/x.c b/src/x.c index 795211d..42bee3f 100644 --- a/src/x.c +++ b/src/x.c @@ -388,6 +388,41 @@ bool x_fetch_region(xcb_connection_t *c, xcb_xfixes_region_t r, pixman_region32_ return ret; } +uint32_t x_create_region(xcb_connection_t *c, const region_t *reg) { + if (!reg) { + return XCB_NONE; + } + + int nrects; + // 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); + 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); + bool success = + XCB_AWAIT_VOID(xcb_xfixes_create_region, c, ret, to_u32_checked(nrects), xrects); + free(xrects); + if (!success) { + return XCB_NONE; + } + return ret; +} + +void x_destroy_region(xcb_connection_t *c, xcb_xfixes_region_t r) { + if (r != XCB_NONE) { + xcb_xfixes_destroy_region(c, r); + } +} + void 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) { @@ -414,9 +449,10 @@ void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict, } void x_clear_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict) { + assert(pict != XCB_NONE); xcb_render_change_picture_value_list_t v = {.clipmask = XCB_NONE}; xcb_generic_error_t *e = xcb_request_check( - c, xcb_render_change_picture(c, pict, XCB_RENDER_CP_CLIP_MASK, &v)); + c, xcb_render_change_picture_checked(c, pict, XCB_RENDER_CP_CLIP_MASK, &v)); if (e) { log_error_x_error(e, "failed to clear clip region"); free(e); @@ -527,8 +563,16 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c /** * Log a X11 error */ +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)); + } +} + void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code) { - log_debug("%s", _x_strerror(serial, major, minor, error_code)); + x_log_error(LOG_LEVEL_DEBUG, serial, major, minor, error_code); } /* diff --git a/src/x.h b/src/x.h index 3b8787c..78efea3 100644 --- a/src/x.h +++ b/src/x.h @@ -216,6 +216,12 @@ x_create_picture_with_visual(xcb_connection_t *, xcb_drawable_t, int w, int h, /// Fetch a X region and store it in a pixman region bool x_fetch_region(xcb_connection_t *, xcb_xfixes_region_t r, region_t *res); +/// Create a X region from a pixman region +uint32_t x_create_region(xcb_connection_t *c, const region_t *reg); + +/// Destroy a X region +void x_destroy_region(xcb_connection_t *c, uint32_t region); + void x_set_picture_clip_region(xcb_connection_t *, xcb_render_picture_t, int16_t clip_x_origin, int16_t clip_y_origin, const region_t *); @@ -225,6 +231,8 @@ void x_clear_picture_clip_region(xcb_connection_t *, xcb_render_picture_t pict); * Log a X11 error */ void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_code); +void x_log_error(enum log_level level, unsigned long serial, uint8_t major, + uint16_t minor, uint8_t error_code); /* * Convert a xcb_generic_error_t to a string that describes the error