diff --git a/backend/headless/output.c b/backend/headless/output.c index f4719a3c..a6c97dec 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -71,7 +71,7 @@ static bool output_commit(struct wlr_output *wlr_output, .commit_seq = wlr_output->commit_seq + 1, .presented = true, }; - wlr_output_send_present(wlr_output, &present_event); + output_defer_present(wlr_output, present_event); wl_event_source_timer_update(output->frame_timer, output->frame_delay); } diff --git a/backend/wayland/output.c b/backend/wayland/output.c index e51be4c5..2e5c39f5 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -590,7 +590,7 @@ static bool output_commit(struct wlr_output *wlr_output, .commit_seq = wlr_output->commit_seq + 1, .presented = true, }; - wlr_output_send_present(wlr_output, &present_event); + output_defer_present(wlr_output, present_event); } } diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index b885fd08..e1aea1be 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -21,4 +21,6 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, int dst_width, int dst_height, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y); +void output_defer_present(struct wlr_output *output, struct wlr_output_event_present event); + #endif diff --git a/types/output/output.c b/types/output/output.c index 278ecb9e..a9a0ccb2 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -927,6 +927,46 @@ void wlr_output_send_present(struct wlr_output *output, wl_signal_emit_mutable(&output->events.present, event); } +struct deferred_present_event { + struct wlr_output *output; + struct wl_event_source *idle_source; + struct wlr_output_event_present event; + struct wl_listener output_destroy; +}; + +static void deferred_present_event_destroy(struct deferred_present_event *deferred) { + wl_list_remove(&deferred->output_destroy.link); + free(deferred); +} + +static void deferred_present_event_handle_idle(void *data) { + struct deferred_present_event *deferred = data; + wlr_output_send_present(deferred->output, &deferred->event); + deferred_present_event_destroy(deferred); +} + +static void deferred_present_event_handle_output_destroy(struct wl_listener *listener, void *data) { + struct deferred_present_event *deferred = wl_container_of(listener, deferred, output_destroy); + wl_event_source_remove(deferred->idle_source); + deferred_present_event_destroy(deferred); +} + +void output_defer_present(struct wlr_output *output, struct wlr_output_event_present event) { + struct deferred_present_event *deferred = calloc(1, sizeof(struct wlr_output_event_present)); + if (!deferred) { + return; + } + *deferred = (struct deferred_present_event){ + .output = output, + .event = event, + }; + deferred->output_destroy.notify = deferred_present_event_handle_output_destroy; + wl_signal_add(&output->events.destroy, &deferred->output_destroy); + + struct wl_event_loop *ev = wl_display_get_event_loop(output->display); + deferred->idle_source = wl_event_loop_add_idle(ev, deferred_present_event_handle_idle, deferred); +} + void wlr_output_send_request_state(struct wlr_output *output, const struct wlr_output_state *state) { uint32_t unchanged = output_compare_state(output, state);