wlr-output-management: Send custom modes to clients

Since commit 5567aefb, fixed modes haven't been automatically generated
for custom modes, so the output management implementation needs to be
able to handle them directly. To avoid polluting the mode list, only a
single custom mode can be listed at a time and will be removed when a
fixed mode is set.

Closes: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3744
This commit is contained in:
Väinö Mäkelä 2023-11-01 08:51:18 +02:00 committed by Simon Ser
parent 26eac25d7f
commit 5de9e1a99d

View file

@ -32,7 +32,7 @@ static struct wlr_output_head_v1 *head_from_resource(
static const struct zwlr_output_mode_v1_interface output_mode_impl; static const struct zwlr_output_mode_v1_interface output_mode_impl;
// Can return NULL if the mode is inert // Can return NULL if the mode is custom or inert
static struct wlr_output_mode *mode_from_resource( static struct wlr_output_mode *mode_from_resource(
struct wl_resource *resource) { struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, assert(wl_resource_instance_of(resource,
@ -86,6 +86,28 @@ static struct wlr_output_head_v1 *head_create(
return head; return head;
} }
static void head_destroy_custom_mode_resources(struct wlr_output_head_v1 *head) {
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &head->mode_resources) {
if (wl_resource_get_user_data(resource) != NULL) {
continue;
}
zwlr_output_mode_v1_send_finished(resource);
wl_list_remove(wl_resource_get_link(resource));
wl_list_init(wl_resource_get_link(resource));
}
}
static bool head_has_custom_mode_resources(const struct wlr_output_head_v1 *head) {
struct wl_resource *resource;
wl_resource_for_each(resource, &head->mode_resources) {
if (wl_resource_get_user_data(resource) == NULL) {
return true;
}
}
return false;
}
static void config_head_destroy( static void config_head_destroy(
struct wlr_output_configuration_head_v1 *config_head) { struct wlr_output_configuration_head_v1 *config_head) {
@ -161,12 +183,12 @@ static void config_head_handle_set_mode(struct wl_client *client,
return; return;
} }
// Mode can be NULL if the output doesn't support modes (in which case we // Mode can be NULL if the output uses a custom mode (in which case we
// expose only one "virtual" mode, the current mode) // expose a virtual mode with user data set to NULL).
struct wlr_output_mode *mode = mode_from_resource(mode_resource); struct wlr_output_mode *mode = mode_from_resource(mode_resource);
struct wlr_output *output = config_head->state.output; struct wlr_output *output = config_head->state.output;
bool found = (mode == NULL && wl_list_empty(&output->modes)); bool found = mode == NULL;
struct wlr_output_mode *m; struct wlr_output_mode *m;
wl_list_for_each(m, &output->modes, link) { wl_list_for_each(m, &output->modes, link) {
if (mode == m) { if (mode == m) {
@ -737,9 +759,6 @@ static void head_send_state(struct wlr_output_head_v1 *head,
} }
if (state & HEAD_STATE_MODE) { if (state & HEAD_STATE_MODE) {
assert(head->state.mode != NULL ||
wl_list_empty(&head->state.output->modes));
bool found = false; bool found = false;
struct wl_resource *mode_resource; struct wl_resource *mode_resource;
wl_resource_for_each(mode_resource, &head->mode_resources) { wl_resource_for_each(mode_resource, &head->mode_resources) {
@ -847,8 +866,8 @@ static void manager_send_head(struct wlr_output_manager_v1 *manager,
head_send_mode(head, head_resource, mode); head_send_mode(head, head_resource, mode);
} }
if (wl_list_empty(&output->modes)) { if (output->current_mode == NULL) {
// Output doesn't support modes. Send a virtual one. // Output doesn't have a fixed mode set. Send a virtual one.
head_send_mode(head, head_resource, NULL); head_send_mode(head, head_resource, NULL);
} }
@ -907,6 +926,15 @@ static bool manager_update_head(struct wlr_output_manager_v1 *manager,
} }
} }
if (next->mode == NULL && !head_has_custom_mode_resources(head)) {
struct wl_resource *resource;
wl_resource_for_each(resource, &head->resources) {
head_send_mode(head, resource, NULL);
}
} else if (next->mode != NULL) {
head_destroy_custom_mode_resources(head);
}
if (state != 0) { if (state != 0) {
*current = *next; *current = *next;