From 8e75ac6b8cb07d5e81a7e5aebd3f1086837891ec Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 10 Oct 2024 12:20:57 +0100 Subject: [PATCH 1/3] transition: avoid opacity change for fly-out/slide-out presets Set opacity to window-raw-opacity-before instead of to 1. Signed-off-by: Yuxuan Shui --- data/animation_presets.conf | 19 ++- src/transition/generated/script_templates.c | 152 ++++++++++++-------- 2 files changed, 107 insertions(+), 64 deletions(-) diff --git a/data/animation_presets.conf b/data/animation_presets.conf index 3e099743b1..b305ae9ff2 100644 --- a/data/animation_presets.conf +++ b/data/animation_presets.conf @@ -71,7 +71,11 @@ slide-out = { crop-y = "window-y"; crop-width = "window-width"; crop-height = "window-height"; - opacity = 1; + opacity = { + start = "window-raw-opacity-before"; + end = "window-raw-opacity-before"; + duration = "placeholder0"; + }; blur-opacity = "opacity"; shadow-opacity = "opacity"; *knobs = { @@ -124,9 +128,13 @@ fly-out = { offset-y = "v-timing * (1 - placeholder3)"; shadow-offset-x = "offset-x"; shadow-offset-y = "offset-y"; - opacity = 1; - shadow-opacity = 1; - blur-opacity = 1; + opacity = { + start = "window-raw-opacity-before"; + end = "window-raw-opacity-before"; + duration = "placeholder0"; + }; + shadow-opacity = "opacity"; + blur-opacity = "opacity"; *knobs = { duration = 0.2; direction = (0, ["up", "down", "left", "right"]); @@ -151,9 +159,6 @@ fly-in = { offset-y = "v-timing * (1 - placeholder3)"; shadow-offset-x = "offset-x"; shadow-offset-y = "offset-y"; - opacity = 1; - shadow-opacity = 1; - blur-opacity = 1; *knobs = { duration = 0.2; direction = (0, ["up", "down", "left", "right"]); diff --git a/src/transition/generated/script_templates.c b/src/transition/generated/script_templates.c index 2a34f39377..73133c504f 100644 --- a/src/transition/generated/script_templates.c +++ b/src/transition/generated/script_templates.c @@ -445,7 +445,7 @@ static bool win_script_preset__appear(struct win_script *output, config_setting_ } static struct script *script_template__slide_out(int *output_slots) { static const struct instruction instrs[] = { - {.type = INST_BRANCH_ONCE, .rel = 42}, + {.type = INST_BRANCH_ONCE, .rel = 55}, {.type = INST_LOAD, .slot = 15}, {.type = INST_LOAD, .slot = 14}, {.type = INST_OP, .op = OP_SUB}, @@ -490,11 +490,27 @@ static struct script *script_template__slide_out(int *output_slots) { {.type = INST_STORE, .slot = 7}, {.type = INST_LOAD_CTX, .ctx = 24}, {.type = INST_STORE, .slot = 8}, + {.type = INST_LOAD, .slot = 18}, + {.type = INST_LOAD, .slot = 17}, + {.type = INST_OP, .op = OP_SUB}, + {.type = INST_LOAD, .slot = 12}, + {.type = INST_IMM, .imm = 0x0p+0}, + {.type = INST_OP, .op = OP_SUB}, + {.type = INST_LOAD, .slot = 19}, + {.type = INST_OP, .op = OP_DIV}, + { + .type = INST_CURVE, + .curve = {.type = CURVE_LINEAR}, + }, + {.type = INST_OP, .op = OP_MUL}, + {.type = INST_LOAD, .slot = 17}, + {.type = INST_OP, .op = OP_ADD}, + {.type = INST_STORE, .slot = 9}, {.type = INST_LOAD, .slot = 9}, {.type = INST_STORE, .slot = 10}, {.type = INST_LOAD, .slot = 9}, {.type = INST_STORE, .slot = 11}, - {.type = INST_BRANCH_ONCE, .rel = 17}, + {.type = INST_BRANCH_ONCE, .rel = 21}, {.type = INST_HALT}, {.type = INST_IMM, .imm = 0x0p+0}, {.type = INST_STORE_OVER_NAN, .slot = 14}, @@ -508,9 +524,13 @@ static struct script *script_template__slide_out(int *output_slots) { {.type = INST_OP, .op = OP_MUL}, {.type = INST_OP, .op = OP_ADD}, {.type = INST_STORE, .slot = 15}, - {.type = INST_IMM, .imm = 0x1p+0}, - {.type = INST_STORE, .slot = 9}, - {.type = INST_BRANCH, .rel = -55}, + {.type = INST_LOAD_CTX, .ctx = 64}, + {.type = INST_STORE_OVER_NAN, .slot = 17}, + {.type = INST_LOAD_CTX, .ctx = 1073741824}, + {.type = INST_STORE, .slot = 19}, + {.type = INST_LOAD_CTX, .ctx = 64}, + {.type = INST_STORE, .slot = 18}, + {.type = INST_BRANCH, .rel = -72}, {.type = INST_IMM, .imm = 0x0p+0}, {.type = INST_STORE, .slot = 13}, {.type = INST_LOAD, .slot = 16}, @@ -519,12 +539,18 @@ static struct script *script_template__slide_out(int *output_slots) { {.type = INST_LOAD, .slot = 13}, {.type = INST_OP, .op = OP_MAX}, {.type = INST_STORE, .slot = 13}, + {.type = INST_LOAD, .slot = 19}, + {.type = INST_IMM, .imm = 0x0p+0}, + {.type = INST_OP, .op = OP_ADD}, + {.type = INST_LOAD, .slot = 13}, + {.type = INST_OP, .op = OP_MAX}, + {.type = INST_STORE, .slot = 13}, {.type = INST_HALT}, }; struct script *ret = malloc(offsetof(struct script, instrs) + sizeof(instrs)); ret->len = ARR_SIZE(instrs); ret->elapsed_slot = 12; - ret->n_slots = 17; + ret->n_slots = 20; ret->stack_size = 3; ret->vars = NULL; ret->overrides = NULL; @@ -606,6 +632,11 @@ static struct script *script_template__slide_out(int *output_slots) { *override = (struct overridable_slot){.name = strdup("v-timing"), .slot = 14}; HASH_ADD_STR(ret->overrides, name, override); } + { + struct overridable_slot *override = malloc(sizeof(*override)); + *override = (struct overridable_slot){.name = strdup("opacity"), .slot = 17}; + HASH_ADD_STR(ret->overrides, name, override); + } output_slots[0] = 1; output_slots[1] = 2; output_slots[2] = 3; @@ -872,7 +903,7 @@ static bool win_script_preset__slide_in(struct win_script *output, config_settin } static struct script *script_template__fly_out(int *output_slots) { static const struct instruction instrs[] = { - {.type = INST_BRANCH_ONCE, .rel = 30}, + {.type = INST_BRANCH_ONCE, .rel = 47}, {.type = INST_LOAD, .slot = 11}, {.type = INST_LOAD, .slot = 10}, {.type = INST_OP, .op = OP_SUB}, @@ -909,6 +940,26 @@ static struct script *script_template__fly_out(int *output_slots) { {.type = INST_STORE, .slot = 3}, {.type = INST_LOAD, .slot = 2}, {.type = INST_STORE, .slot = 4}, + {.type = INST_LOAD, .slot = 14}, + {.type = INST_LOAD, .slot = 13}, + {.type = INST_OP, .op = OP_SUB}, + {.type = INST_LOAD, .slot = 8}, + {.type = INST_IMM, .imm = 0x0p+0}, + {.type = INST_OP, .op = OP_SUB}, + {.type = INST_LOAD, .slot = 15}, + {.type = INST_OP, .op = OP_DIV}, + { + .type = INST_CURVE, + .curve = {.type = CURVE_LINEAR}, + }, + {.type = INST_OP, .op = OP_MUL}, + {.type = INST_LOAD, .slot = 13}, + {.type = INST_OP, .op = OP_ADD}, + {.type = INST_STORE, .slot = 5}, + {.type = INST_LOAD, .slot = 5}, + {.type = INST_STORE, .slot = 6}, + {.type = INST_LOAD, .slot = 5}, + {.type = INST_STORE, .slot = 7}, {.type = INST_BRANCH_ONCE, .rel = 29}, {.type = INST_HALT}, {.type = INST_IMM, .imm = 0x0p+0}, @@ -931,13 +982,13 @@ static struct script *script_template__fly_out(int *output_slots) { {.type = INST_OP, .op = OP_MUL}, {.type = INST_OP, .op = OP_ADD}, {.type = INST_STORE, .slot = 11}, - {.type = INST_IMM, .imm = 0x1p+0}, - {.type = INST_STORE, .slot = 5}, - {.type = INST_IMM, .imm = 0x1p+0}, - {.type = INST_STORE, .slot = 6}, - {.type = INST_IMM, .imm = 0x1p+0}, - {.type = INST_STORE, .slot = 7}, - {.type = INST_BRANCH, .rel = -55}, + {.type = INST_LOAD_CTX, .ctx = 64}, + {.type = INST_STORE_OVER_NAN, .slot = 13}, + {.type = INST_LOAD_CTX, .ctx = 1073741824}, + {.type = INST_STORE, .slot = 15}, + {.type = INST_LOAD_CTX, .ctx = 64}, + {.type = INST_STORE, .slot = 14}, + {.type = INST_BRANCH, .rel = -72}, {.type = INST_IMM, .imm = 0x0p+0}, {.type = INST_STORE, .slot = 9}, {.type = INST_LOAD, .slot = 12}, @@ -946,12 +997,18 @@ static struct script *script_template__fly_out(int *output_slots) { {.type = INST_LOAD, .slot = 9}, {.type = INST_OP, .op = OP_MAX}, {.type = INST_STORE, .slot = 9}, + {.type = INST_LOAD, .slot = 15}, + {.type = INST_IMM, .imm = 0x0p+0}, + {.type = INST_OP, .op = OP_ADD}, + {.type = INST_LOAD, .slot = 9}, + {.type = INST_OP, .op = OP_MAX}, + {.type = INST_STORE, .slot = 9}, {.type = INST_HALT}, }; struct script *ret = malloc(offsetof(struct script, instrs) + sizeof(instrs)); ret->len = ARR_SIZE(instrs); ret->elapsed_slot = 8; - ret->n_slots = 13; + ret->n_slots = 16; ret->stack_size = 3; ret->vars = NULL; ret->overrides = NULL; @@ -1009,6 +1066,11 @@ static struct script *script_template__fly_out(int *output_slots) { *override = (struct overridable_slot){.name = strdup("v-timing"), .slot = 10}; HASH_ADD_STR(ret->overrides, name, override); } + { + struct overridable_slot *override = malloc(sizeof(*override)); + *override = (struct overridable_slot){.name = strdup("opacity"), .slot = 13}; + HASH_ADD_STR(ret->overrides, name, override); + } output_slots[0] = 1; output_slots[1] = 2; output_slots[2] = 3; @@ -1088,12 +1150,12 @@ static struct script *script_template__fly_in(int *output_slots) { static const struct instruction instrs[] = { {.type = INST_BRANCH_ONCE, .rel = 30}, {.type = INST_IMM, .imm = 0x0p+0}, - {.type = INST_LOAD, .slot = 10}, + {.type = INST_LOAD, .slot = 7}, {.type = INST_OP, .op = OP_SUB}, - {.type = INST_LOAD, .slot = 8}, + {.type = INST_LOAD, .slot = 5}, {.type = INST_IMM, .imm = 0x0p+0}, {.type = INST_OP, .op = OP_SUB}, - {.type = INST_LOAD, .slot = 11}, + {.type = INST_LOAD, .slot = 8}, {.type = INST_OP, .op = OP_DIV}, { .type = INST_CURVE, @@ -1106,7 +1168,7 @@ static struct script *script_template__fly_in(int *output_slots) { .cy = 0x1.0147ae147ae16p+1}}, }, {.type = INST_OP, .op = OP_MUL}, - {.type = INST_LOAD, .slot = 10}, + {.type = INST_LOAD, .slot = 7}, {.type = INST_OP, .op = OP_ADD}, {.type = INST_STORE, .slot = 0}, {.type = INST_LOAD, .slot = 0}, @@ -1123,7 +1185,7 @@ static struct script *script_template__fly_in(int *output_slots) { {.type = INST_STORE, .slot = 3}, {.type = INST_LOAD, .slot = 2}, {.type = INST_STORE, .slot = 4}, - {.type = INST_BRANCH_ONCE, .rel = 27}, + {.type = INST_BRANCH_ONCE, .rel = 21}, {.type = INST_HALT}, {.type = INST_LOAD_CTX, .ctx = 24}, {.type = INST_LOAD_CTX, .ctx = 1073741832}, @@ -1140,30 +1202,24 @@ static struct script *script_template__fly_in(int *output_slots) { {.type = INST_LOAD_CTX, .ctx = 1073741840}, {.type = INST_OP, .op = OP_MUL}, {.type = INST_OP, .op = OP_ADD}, - {.type = INST_STORE_OVER_NAN, .slot = 10}, + {.type = INST_STORE_OVER_NAN, .slot = 7}, {.type = INST_LOAD_CTX, .ctx = 1073741824}, - {.type = INST_STORE, .slot = 11}, - {.type = INST_IMM, .imm = 0x1p+0}, - {.type = INST_STORE, .slot = 5}, - {.type = INST_IMM, .imm = 0x1p+0}, - {.type = INST_STORE, .slot = 6}, - {.type = INST_IMM, .imm = 0x1p+0}, - {.type = INST_STORE, .slot = 7}, - {.type = INST_BRANCH, .rel = -53}, + {.type = INST_STORE, .slot = 8}, + {.type = INST_BRANCH, .rel = -47}, {.type = INST_IMM, .imm = 0x0p+0}, - {.type = INST_STORE, .slot = 9}, - {.type = INST_LOAD, .slot = 11}, + {.type = INST_STORE, .slot = 6}, + {.type = INST_LOAD, .slot = 8}, {.type = INST_IMM, .imm = 0x0p+0}, {.type = INST_OP, .op = OP_ADD}, - {.type = INST_LOAD, .slot = 9}, + {.type = INST_LOAD, .slot = 6}, {.type = INST_OP, .op = OP_MAX}, - {.type = INST_STORE, .slot = 9}, + {.type = INST_STORE, .slot = 6}, {.type = INST_HALT}, }; struct script *ret = malloc(offsetof(struct script, instrs) + sizeof(instrs)); ret->len = ARR_SIZE(instrs); - ret->elapsed_slot = 8; - ret->n_slots = 12; + ret->elapsed_slot = 5; + ret->n_slots = 9; ret->stack_size = 3; ret->vars = NULL; ret->overrides = NULL; @@ -1198,36 +1254,18 @@ static struct script *script_template__fly_in(int *output_slots) { .name = strdup("shadow-offset-y"), .slot = 4, .index = 4}; HASH_ADD_STR(ret->vars, name, var); } - { - struct variable_allocation *var = malloc(sizeof(*var)); - *var = (struct variable_allocation){ - .name = strdup("opacity"), .slot = 5, .index = 5}; - HASH_ADD_STR(ret->vars, name, var); - } - { - struct variable_allocation *var = malloc(sizeof(*var)); - *var = (struct variable_allocation){ - .name = strdup("shadow-opacity"), .slot = 6, .index = 6}; - HASH_ADD_STR(ret->vars, name, var); - } - { - struct variable_allocation *var = malloc(sizeof(*var)); - *var = (struct variable_allocation){ - .name = strdup("blur-opacity"), .slot = 7, .index = 7}; - HASH_ADD_STR(ret->vars, name, var); - } { struct overridable_slot *override = malloc(sizeof(*override)); - *override = (struct overridable_slot){.name = strdup("v-timing"), .slot = 10}; + *override = (struct overridable_slot){.name = strdup("v-timing"), .slot = 7}; HASH_ADD_STR(ret->overrides, name, override); } output_slots[0] = 1; output_slots[1] = 2; output_slots[2] = 3; output_slots[3] = 4; - output_slots[4] = 5; - output_slots[5] = 7; - output_slots[6] = 6; + output_slots[4] = -1; + output_slots[5] = -1; + output_slots[6] = -1; output_slots[7] = -1; output_slots[8] = -1; output_slots[9] = -1; From c57b705ec438a205d05d7c6f200770839058a225 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Tue, 8 Oct 2024 19:14:09 +0100 Subject: [PATCH 2/3] core: make sure we don't sleep on events, again Once again we found ourselves going into sleep while there are X events in libxcb's buffer. This time it's because both vblank_handle_x_events and x_poll_for_events can read from the X connection. This is introduced as part of commit d617d0ba4f. Before that, we were only calling xcb_poll_for_queued_events after vblank_handle_x_events. By changing that to a xcb_poll_for_events (and later to x_poll_for_events, which calls xcb_poll_for_events), we could read additional vblank events into the buffer. These events will not be handled because vblank_handle_x_events has already be called, so we will be going into sleep with these events in the buffer. Since vblank events driver the frame render loop, this causes indefinitely long delay between frames. Related-to: #1345 #1330 Signed-off-by: Yuxuan Shui --- src/picom.c | 65 +++++++++++++++++++++++++++++++++------------------- src/vblank.c | 14 +++++++---- src/vblank.h | 8 ++++++- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/picom.c b/src/picom.c index f59adfd503..1056234b13 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1451,31 +1451,48 @@ static void unredirect(session_t *ps) { static void handle_x_events(struct session *ps) { bool wm_was_consistent = wm_is_consistent(ps->wm); - if (ps->vblank_scheduler) { - vblank_handle_x_events(ps->vblank_scheduler); - } + while (true) { + // Flush because if we go into sleep when there is still requests + // in the outgoing buffer, they will not be sent for an indefinite + // amount of time. Use XFlush here too, we might still use some + // Xlib functions because OpenGL. + // + // Also note, `xcb_flush`/`XFlush` may _read_ more events from the server + // (yes, this is ridiculous, I know). But since we are polling for events + // in a loop, this should be fine - if we read events here, they will be + // handled below; and if some requests is sent later in this loop, which + // means some events must have been received, we will loop once more and + // get here to flush them. + XFlush(ps->c.dpy); + xcb_flush(ps->c.c); + + // We have to check for vblank events (i.e. special xcb events) and normal + // events in a loop. This is because both `xcb_poll_for_event` and + // `xcb_poll_for_special_event` will read data from the X connection and + // put it in a buffer. So whichever one we call last, say + // `xcb_poll_for_special_event`, will read data into the buffer that might + // contain events that `xcb_poll_for_event` should handle, and vice versa. + // This causes us to go into sleep with events in the buffer. + // + // We have to keep calling both of them until neither of them return any + // events. + bool has_events = false; + if (ps->vblank_scheduler) { + has_events = vblank_handle_x_events(ps->vblank_scheduler) == + VBLANK_HANDLE_X_EVENTS_OK; + } + + xcb_generic_event_t *ev; + while ((ev = x_poll_for_event(&ps->c))) { + has_events = true; + ev_handle(ps, (xcb_generic_event_t *)ev); + free(ev); + }; - // Flush because if we go into sleep when there is still requests in the - // outgoing buffer, they will not be sent for an indefinite amount of - // time. Use XFlush here too, we might still use some Xlib functions - // because OpenGL. - // - // Also note, after we have flushed here, we won't flush again in this - // function before going into sleep. This is because `xcb_flush`/`XFlush` - // may _read_ more events from the server (yes, this is ridiculous, I - // know). And we can't have that, see the comments above this function. - // - // This means if functions called ev_handle need to send some events, - // they need to carefully make sure those events are flushed, one way or - // another. - XFlush(ps->c.dpy); - xcb_flush(ps->c.c); - - xcb_generic_event_t *ev; - while ((ev = x_poll_for_event(&ps->c))) { - ev_handle(ps, (xcb_generic_event_t *)ev); - free(ev); - }; + if (!has_events) { + break; + } + } int err = xcb_connection_has_error(ps->c.c); if (err) { log_fatal("X11 server connection broke (error %d)", err); diff --git a/src/vblank.c b/src/vblank.c index 057f7d4058..e63b0a51cb 100644 --- a/src/vblank.c +++ b/src/vblank.c @@ -69,7 +69,7 @@ struct vblank_scheduler_ops { bool (*init)(struct vblank_scheduler *self); void (*deinit)(struct vblank_scheduler *self); bool (*schedule)(struct vblank_scheduler *self); - bool (*handle_x_events)(struct vblank_scheduler *self); + enum vblank_handle_x_events_result (*handle_x_events)(struct vblank_scheduler *self); }; static void @@ -444,10 +444,14 @@ static void handle_present_complete_notify(struct present_vblank_scheduler *self ev_timer_start(self->base.loop, &self->callback_timer); } -static bool handle_present_events(struct vblank_scheduler *base) { +static enum vblank_handle_x_events_result +handle_present_events(struct vblank_scheduler *base) { auto self = (struct present_vblank_scheduler *)base; xcb_present_generic_event_t *ev; + enum vblank_handle_x_events_result result = VBLANK_HANDLE_X_EVENTS_NO_EVENTS; while ((ev = (void *)xcb_poll_for_special_event(base->c->c, self->event))) { + result = VBLANK_HANDLE_X_EVENTS_OK; + if (ev->event != self->event_id) { // This event doesn't have the right event context, it's not meant // for us. @@ -460,7 +464,7 @@ static bool handle_present_events(struct vblank_scheduler *base) { next: free(ev); } - return true; + return result; } static const struct vblank_scheduler_ops vblank_scheduler_ops[LAST_VBLANK_SCHEDULER] = { @@ -571,11 +575,11 @@ vblank_scheduler_new(struct ev_loop *loop, struct x_connection *c, xcb_window_t return self; } -bool vblank_handle_x_events(struct vblank_scheduler *self) { +enum vblank_handle_x_events_result vblank_handle_x_events(struct vblank_scheduler *self) { assert(self->type < LAST_VBLANK_SCHEDULER); auto fn = vblank_scheduler_ops[self->type].handle_x_events; if (fn != NULL) { return fn(self); } - return true; + return VBLANK_HANDLE_X_EVENTS_NO_EVENTS; } diff --git a/src/vblank.h b/src/vblank.h index a1916dc405..83bda69daa 100644 --- a/src/vblank.h +++ b/src/vblank.h @@ -29,6 +29,12 @@ enum vblank_callback_action { VBLANK_CALLBACK_DONE, }; +enum vblank_handle_x_events_result { + VBLANK_HANDLE_X_EVENTS_OK, + VBLANK_HANDLE_X_EVENTS_ERROR, + VBLANK_HANDLE_X_EVENTS_NO_EVENTS, +}; + typedef enum vblank_callback_action (*vblank_callback_t)(struct vblank_event *event, void *user_data); @@ -47,4 +53,4 @@ vblank_scheduler_new(struct ev_loop *loop, struct x_connection *c, xcb_window_t enum vblank_scheduler_type type, bool use_realtime_scheduling); void vblank_scheduler_free(struct vblank_scheduler *); -bool vblank_handle_x_events(struct vblank_scheduler *self); +enum vblank_handle_x_events_result vblank_handle_x_events(struct vblank_scheduler *self); From 240c269888d7322840010094bb0413332a695e33 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 10 Oct 2024 12:25:03 +0100 Subject: [PATCH 3/3] misc: update README.md Signed-off-by: Yuxuan Shui --- CHANGELOG.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa74996084..d2cdf3eadc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,23 @@ -# v12 +# 12.2 (2024-Oct-10) + +## Improvements + +* fly-out/slide-out animation presets no longer cause jumps in window opacity + +## Bug fixes + +* Random delays before screen is updated (#1345 #1330) +* Fix building on 32-bit systems (#1346) +* Fix blank screen on 32-bit systems +* Fix fly-in/fly-out animation presets so they work with directions other than up and left + +# v12.1 (2024-Sep-29) + +## Bug fixes + +* picom stops rendering correctly after monitor configuration changes (#1338, thanks to @Suyooo) + +# v12 (2024-Sep-27) ## Bug fixes