output: Minimize interaction with output after destroy

When an output is destroyed, we go through the process of disabling it.
This includes evacuating all content away from the output, which can
lead to various modifications to the scene. With the scene_output still
present, this can lead to things like output_enter events being emitted
for the output currently being destroyed.

Ensure that the scene output is destroyed first and that the output is
immediately considered disabled.

References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3974
This commit is contained in:
Kenny Levinsen 2025-05-06 01:35:05 +02:00 committed by Simon Ser
parent 88c7b4a7eb
commit 005924f260
2 changed files with 15 additions and 14 deletions

View file

@ -422,13 +422,6 @@ void force_modeset(void) {
}
static void begin_destroy(struct sway_output *output) {
if (output->enabled) {
output_disable(output);
}
output_begin_destroy(output);
wl_list_remove(&output->link);
wl_list_remove(&output->layout_destroy.link);
wl_list_remove(&output->destroy.link);
@ -436,8 +429,17 @@ static void begin_destroy(struct sway_output *output) {
wl_list_remove(&output->frame.link);
wl_list_remove(&output->request_state.link);
// Remove the scene_output first to ensure that the scene does not emit
// events for this output.
wlr_scene_output_destroy(output->scene_output);
output->scene_output = NULL;
if (output->enabled) {
output_disable(output);
}
output_begin_destroy(output);
wl_list_remove(&output->link);
output->wlr_output->data = NULL;
output->wlr_output = NULL;

View file

@ -205,11 +205,8 @@ static void output_evacuate(struct sway_output *output) {
return;
}
struct sway_output *fallback_output = NULL;
if (root->outputs->length > 1) {
if (root->outputs->length > 0) {
fallback_output = root->outputs->items[0];
if (fallback_output == output) {
fallback_output = root->outputs->items[1];
}
}
while (output->workspaces->length) {
@ -289,11 +286,13 @@ void output_disable(struct sway_output *output) {
sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name);
wl_signal_emit_mutable(&output->events.disable, output);
output_evacuate(output);
// Remove the output now to avoid interacting with it during e.g.,
// transactions, as the output might be physically removed with the scene
// output destroyed.
list_del(root->outputs, index);
output->enabled = false;
output_evacuate(output);
}
void output_begin_destroy(struct sway_output *output) {