backend/drm: keep removed modes allocated if they are in use

After a fixed mode is removed from an output, it remains the current or
pending mode for an output until the compositor requests a different
mode. This change ensures that removed modes are not freed until the
compositor has had the opportunity to know that the mode was removed,
and in the case of the current or pending mode, that the mode is no
longer the current or pending mode.
This commit is contained in:
Daniel Playfair Cal 2021-01-10 14:46:16 +11:00
parent b7aa6ee176
commit a055f23b3b
2 changed files with 46 additions and 7 deletions

View file

@ -1255,7 +1255,13 @@ static bool update_modes(drmModeConnector *drm_conn,
previous_drm_mode->wlr_mode.preferred ? "(preferred)" : "");
wl_list_remove(&previous_drm_mode->wlr_mode.link);
free(previous_drm_mode);
// Free the removed mode unless it is the current or pending mode
if (previous_mode != wlr_conn->output.current_mode &&
!(wlr_conn->output.pending.committed & WLR_OUTPUT_STATE_MODE &&
previous_mode != wlr_conn->output.pending.mode)) {
free(previous_drm_mode);
}
}
if (drm_conn->count_modes > wl_list_length(&wlr_conn->output.modes)) {

View file

@ -154,19 +154,47 @@ void wlr_output_enable(struct wlr_output *output, bool enable) {
output->pending.enabled = enable;
}
static void output_state_clear_mode(struct wlr_output_state *state) {
if (!(state->committed & WLR_OUTPUT_STATE_MODE)) {
static void wlr_output_free_mode_if_not_used(struct wlr_output *output,
struct wlr_output_mode *mode) {
if (output->current_mode == mode) {
return;
}
state->mode = NULL;
if (output->pending.committed & WLR_OUTPUT_STATE_MODE
&& output->pending.mode == mode) {
return;
}
state->committed &= ~WLR_OUTPUT_STATE_MODE;
struct wlr_output_mode *available_mode;
bool found = false;
wl_list_for_each(available_mode, &output->modes, link) {
if (available_mode == mode) {
found = true;
break;
}
}
if (!found) {
free(mode);
}
}
static void output_state_clear_mode(struct wlr_output *output) {
if (!(output->pending.committed & WLR_OUTPUT_STATE_MODE)) {
return;
}
struct wlr_output_mode *old_mode = output->pending.mode;
output->pending.mode = NULL;
output->pending.committed &= ~WLR_OUTPUT_STATE_MODE;
wlr_output_free_mode_if_not_used(output, old_mode);
}
void wlr_output_set_mode(struct wlr_output *output,
struct wlr_output_mode *mode) {
output_state_clear_mode(&output->pending);
output_state_clear_mode(output);
if (output->current_mode == mode) {
return;
@ -179,7 +207,7 @@ void wlr_output_set_mode(struct wlr_output *output,
void wlr_output_set_custom_mode(struct wlr_output *output, int32_t width,
int32_t height, int32_t refresh) {
output_state_clear_mode(&output->pending);
output_state_clear_mode(output);
if (output->width == width && output->height == height &&
output->refresh == refresh) {
@ -195,13 +223,18 @@ void wlr_output_set_custom_mode(struct wlr_output *output, int32_t width,
void wlr_output_update_mode(struct wlr_output *output,
struct wlr_output_mode *mode) {
struct wlr_output_mode *old_mode = output->current_mode;
output->current_mode = mode;
if (mode != NULL) {
wlr_output_update_custom_mode(output, mode->width, mode->height,
mode->refresh);
} else {
wlr_output_update_custom_mode(output, 0, 0, 0);
}
wlr_output_free_mode_if_not_used(output, old_mode);
}
void wlr_output_update_custom_mode(struct wlr_output *output, int32_t width,