Merge remote-tracking branch 'yshui/next' into next
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
55
src/common.h
55
src/common.h
@@ -36,9 +36,9 @@
|
||||
#include <X11/Xlib.h>
|
||||
#include <ev.h>
|
||||
#include <pixman.h>
|
||||
#include <xcb/xproto.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/sync.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#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 <code>next</code> 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 <code>XRectangle</code>-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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1435,7 +1435,10 @@ static bool cdbus_process_windows_root_introspect(session_t *ps, DBusMessage *ms
|
||||
continue;
|
||||
}
|
||||
char *tmp = NULL;
|
||||
asprintf(&tmp, "<node name='%#010x'/>\n", w->id);
|
||||
if (asprintf(&tmp, "<node name='%#010x'/>\n", w->id) < 0) {
|
||||
log_fatal("Failed to allocate memory.");
|
||||
abort();
|
||||
}
|
||||
mstrextend(&ret, tmp);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
11
src/event.c
11
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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
81
src/picom.c
81
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
201
src/utils.c
201
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 :
|
||||
|
||||
15
src/utils.h
15
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__
|
||||
|
||||
48
src/x.c
48
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
8
src/x.h
8
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
|
||||
|
||||
Reference in New Issue
Block a user