output: don't auto-configure new outputs during powersave

After e.g. wlopm --off, some external outputs will appear to labwc to
disconnect and reconnect. (I have one HDMI monitor which does this
repeatedly every 10 seconds as it cycles through inputs.) Currently,
these outputs are immediately reconfigured and enabled when they
reconnect, after which point they remain on all night.

For a screensaver/powersave mode that actually works, we need to prevent
this. This change adds logic to not auto-configure new outputs once all
existing outputs are turned off.

Downsides as currently implemented are:

- once reconnected, outputs are logically "disabled", not just "off",
  and wlopm --on cannot re-enable them (wlr-randr or kanshi works).

- this doesn't prevent external clients like kanshi from re-enabling
  outputs immediately when they reconnect, so it's necessary to stop
  kanshi during powersave and restart it afterward.

In spite of the downsides, I've been running with these changes for a
couple weeks with good results.
This commit is contained in:
John Lindgren 2024-10-19 20:13:18 -04:00
parent 893a65d838
commit 8cc8758f11
2 changed files with 33 additions and 1 deletions

View file

@ -330,6 +330,7 @@ struct server {
* do_output_layout_change() must be called explicitly.
*/
int pending_output_layout_change;
bool all_outputs_off;
struct wl_listener renderer_lost;
@ -406,6 +407,7 @@ struct output {
bool leased;
bool gamma_lut_changed;
bool powered_off;
};
#undef LAB_NR_LAYERS

View file

@ -487,7 +487,9 @@ new_output_notify(struct wl_listener *listener, void *data)
wlr_scene_node_raise_to_top(&output->layer_popup_tree->node);
wlr_scene_node_raise_to_top(&output->session_lock_tree->node);
configure_new_output(server, output);
if (!server->all_outputs_off) {
configure_new_output(server, output);
}
do_output_layout_change(server);
}
@ -606,6 +608,9 @@ output_config_apply(struct server *server,
wlr_output_layout_add(server->output_layout, o,
head->state.x, head->state.y);
}
/* Reset power management state when enabling */
output->powered_off = false;
server->all_outputs_off = false;
}
if (need_to_remove) {
@ -1023,6 +1028,27 @@ output_usable_area_scaled(struct output *output)
return usable;
}
static bool
all_outputs_powered_off(struct server *server)
{
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
/*
* In theory we could just check output_is_usable() here
* since the output should be disabled once powered off.
* However, sometimes a disabled output immediately
* disconnects, causing the commit to fail and wlroots
* to think the output is still enabled. Checking the
* explicit powered_off flag ensures that we don't turn
* the output back on if it reconnects shortly after.
*/
if (output_is_usable(output) && !output->powered_off) {
return false;
}
}
return true;
}
void
handle_output_power_manager_set_mode(struct wl_listener *listener, void *data)
{
@ -1036,10 +1062,14 @@ handle_output_power_manager_set_mode(struct wl_listener *listener, void *data)
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
wlr_output_state_set_enabled(&output->pending, false);
output_state_commit(output);
output->powered_off = true;
server->all_outputs_off = all_outputs_powered_off(server);
break;
case ZWLR_OUTPUT_POWER_V1_MODE_ON:
wlr_output_state_set_enabled(&output->pending, true);
output_state_commit(output);
output->powered_off = false;
server->all_outputs_off = false;
/*
* Re-set the cursor image so that the cursor
* isn't invisible on the newly enabled output.