diff --git a/include/common/scene-helpers.h b/include/common/scene-helpers.h index 4eddf98c..98c1bbd6 100644 --- a/include/common/scene-helpers.h +++ b/include/common/scene-helpers.h @@ -2,10 +2,13 @@ #ifndef LABWC_SCENE_HELPERS_H #define LABWC_SCENE_HELPERS_H +#include + struct wlr_scene_node; struct wlr_scene_rect; struct wlr_scene_tree; struct wlr_surface; +struct wlr_scene_output; struct wlr_scene_rect *lab_wlr_scene_get_rect(struct wlr_scene_node *node); struct wlr_scene_tree *lab_scene_tree_from_node(struct wlr_scene_node *node); @@ -18,4 +21,7 @@ struct wlr_surface *lab_wlr_surface_from_node(struct wlr_scene_node *node); */ struct wlr_scene_node *lab_wlr_scene_get_prev_node(struct wlr_scene_node *node); +/* A variant of wlr_scene_output_commit() that respects wlr_output->pending */ +bool lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output); + #endif /* LABWC_SCENE_HELPERS_H */ diff --git a/include/labwc.h b/include/labwc.h index efd12d86..f3f0a2a7 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -339,6 +339,7 @@ struct output { struct wl_listener destroy; struct wl_listener frame; + struct wl_listener request_state; bool leased; }; diff --git a/meson.build b/meson.build index 22b99da4..a80bf565 100644 --- a/meson.build +++ b/meson.build @@ -68,6 +68,7 @@ glib = dependency('glib-2.0') cairo = dependency('cairo') pangocairo = dependency('pangocairo') input = dependency('libinput', version: '>=1.14') +pixman = dependency('pixman-1') math = cc.find_library('m') png = dependency('libpng') svg = dependency('librsvg-2.0', version: '>=2.46', required: false) @@ -111,6 +112,7 @@ labwc_deps = [ drm, pangocairo, input, + pixman, math, png, ] diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index 815bde3a..7f9a7d0b 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include +#include #include "common/scene-helpers.h" struct wlr_scene_rect * @@ -45,3 +47,38 @@ lab_wlr_scene_get_prev_node(struct wlr_scene_node *node) } return prev; } + +/* + * This is a copy of wlr_scene_output_commit() + * as it doesn't use the pending state at all. + */ +bool +lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output) +{ + assert(scene_output); + struct wlr_output *wlr_output = scene_output->output; + struct wlr_output_state *state = &wlr_output->pending; + + if (!wlr_output->needs_frame && !pixman_region32_not_empty( + &scene_output->damage_ring.current)) { + return false; + } + if (!wlr_scene_output_build_state(scene_output, state, NULL)) { + wlr_log(WLR_ERROR, "Failed to build output state for %s", + wlr_output->name); + return false; + } + if (!wlr_output_commit(wlr_output)) { + wlr_log(WLR_ERROR, "Failed to commit output %s", + wlr_output->name); + return false; + } + /* + * FIXME: Remove the following line as soon as + * https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4253 + * is merged. At that point wlr_scene handles damage tracking internally + * again. + */ + wlr_damage_ring_rotate(&scene_output->damage_ring); + return true; +} diff --git a/src/output.c b/src/output.c index 4b2e6c90..f3e639c5 100644 --- a/src/output.c +++ b/src/output.c @@ -18,6 +18,7 @@ #include #include "common/macros.h" #include "common/mem.h" +#include "common/scene-helpers.h" #include "labwc.h" #include "layers.h" #include "node.h" @@ -31,12 +32,11 @@ output_frame_notify(struct wl_listener *listener, void *data) if (!output_is_usable(output)) { return; } - - wlr_scene_output_commit(output->scene_output, NULL); - - struct timespec now = { 0 }; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_output_send_frame_done(output->scene_output, &now); + if (lab_wlr_scene_output_commit(output->scene_output)) { + struct timespec now = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &now); + wlr_scene_output_send_frame_done(output->scene_output, &now); + } } static void @@ -48,6 +48,7 @@ output_destroy_notify(struct wl_listener *listener, void *data) wl_list_remove(&output->link); wl_list_remove(&output->frame.link); wl_list_remove(&output->destroy.link); + wl_list_remove(&output->request_state.link); for (size_t i = 0; i < ARRAY_SIZE(output->layer_tree); i++) { wlr_scene_node_destroy(&output->layer_tree[i]->node); @@ -70,6 +71,44 @@ output_destroy_notify(struct wl_listener *listener, void *data) free(output); } +static void +output_request_state_notify(struct wl_listener *listener, void *data) +{ + /* This ensures nested backends can be resized */ + struct output *output = wl_container_of(listener, output, request_state); + const struct wlr_output_event_request_state *event = data; + struct wlr_output_state *pending = &output->wlr_output->pending; + + if (!pending->committed) { + /* No pending changes, just use the supplied state as new pending */ + wlr_output_state_copy(pending, event->state); + wlr_output_schedule_frame(output->wlr_output); + return; + } + + if (event->state->committed == WLR_OUTPUT_STATE_MODE) { + /* Only the resolution has changed, apply to pending */ + switch (event->state->mode_type) { + case WLR_OUTPUT_STATE_MODE_FIXED: + wlr_output_set_mode(output->wlr_output, event->state->mode); + break; + case WLR_OUTPUT_STATE_MODE_CUSTOM: + wlr_output_set_custom_mode(output->wlr_output, + event->state->custom_mode.width, + event->state->custom_mode.height, + event->state->custom_mode.refresh); + break; + } + wlr_output_schedule_frame(output->wlr_output); + return; + } + + /* Fallback path for everything that we didn't handle above */ + if (!wlr_output_commit_state(output->wlr_output, event->state)) { + wlr_log(WLR_ERROR, "Backend requested a new state that could not be applied"); + } +} + static void do_output_layout_change(struct server *server); static bool @@ -180,6 +219,9 @@ new_output_notify(struct wl_listener *listener, void *data) output->frame.notify = output_frame_notify; wl_signal_add(&wlr_output->events.frame, &output->frame); + output->request_state.notify = output_request_state_notify; + wl_signal_add(&wlr_output->events.request_state, &output->request_state); + wl_list_init(&output->regions); /*