mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-02-28 01:41:07 -05:00
output-group: new helper
wlr_output_group allows compositors to group multiple outputs together and manage them as a single wlr_output. It's useful for output mirroring, and in the future for handling tiled displays.
This commit is contained in:
parent
ace2eda073
commit
ff457e925b
3 changed files with 202 additions and 0 deletions
32
include/wlr/types/wlr_output_group.h
Normal file
32
include/wlr/types/wlr_output_group.h
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* This an unstable interface of wlroots. No guarantees are made regarding the
|
||||||
|
* future consistency of this API.
|
||||||
|
*/
|
||||||
|
#ifndef WLR_USE_UNSTABLE
|
||||||
|
#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WLR_TYPES_WLR_OUTPUT_GROUP_H
|
||||||
|
#define WLR_TYPES_WLR_OUTPUT_GROUP_H
|
||||||
|
|
||||||
|
#include <wlr/types/wlr_output.h>
|
||||||
|
|
||||||
|
struct wlr_output_group_child;
|
||||||
|
|
||||||
|
struct wlr_output_group {
|
||||||
|
struct wlr_output base;
|
||||||
|
|
||||||
|
// Private state
|
||||||
|
|
||||||
|
struct wlr_output *main_output;
|
||||||
|
struct wl_list children; // wlr_output_group_child.link
|
||||||
|
|
||||||
|
struct wl_listener main_output_destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_output_group *wlr_output_group_create(struct wlr_output *main_output);
|
||||||
|
struct wlr_output_group_child *wlr_output_group_add(
|
||||||
|
struct wlr_output_group *group, struct wlr_output *output);
|
||||||
|
void wlr_output_group_child_destroy(struct wlr_output_group_child *child);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -37,6 +37,7 @@ wlr_files += files(
|
||||||
'wlr_linux_dmabuf_v1.c',
|
'wlr_linux_dmabuf_v1.c',
|
||||||
'wlr_matrix.c',
|
'wlr_matrix.c',
|
||||||
'wlr_output_damage.c',
|
'wlr_output_damage.c',
|
||||||
|
'wlr_output_group.c',
|
||||||
'wlr_output_layout.c',
|
'wlr_output_layout.c',
|
||||||
'wlr_output_management_v1.c',
|
'wlr_output_management_v1.c',
|
||||||
'wlr_output_power_management_v1.c',
|
'wlr_output_power_management_v1.c',
|
||||||
|
|
|
||||||
169
types/wlr_output_group.c
Normal file
169
types/wlr_output_group.c
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/types/wlr_output_group.h>
|
||||||
|
|
||||||
|
struct wlr_output_group_child {
|
||||||
|
struct wlr_output *output;
|
||||||
|
struct wl_list link; // wlr_output_group.children
|
||||||
|
|
||||||
|
struct wl_listener output_destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wlr_output_impl output_impl;
|
||||||
|
|
||||||
|
static struct wlr_output_group *group_from_output(struct wlr_output *output) {
|
||||||
|
assert(output->impl == &output_impl);
|
||||||
|
return (struct wlr_output_group *)output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void group_destroy(struct wlr_output *output) {
|
||||||
|
struct wlr_output_group *group = group_from_output(output);
|
||||||
|
|
||||||
|
wl_list_remove(&group->main_output_destroy.link);
|
||||||
|
|
||||||
|
struct wlr_output_group_child *child, *child_tmp;
|
||||||
|
wl_list_for_each_safe(child, child_tmp, &group->children, link) {
|
||||||
|
wlr_output_group_child_destroy(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_apply(struct wlr_output *output,
|
||||||
|
struct wlr_output_state *state) {
|
||||||
|
if (state->committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||||
|
wlr_output_attach_buffer(output, state->buffer);
|
||||||
|
}
|
||||||
|
// TODO: everything else
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool group_commit(struct wlr_output *output) {
|
||||||
|
struct wlr_output_group *group = group_from_output(output);
|
||||||
|
|
||||||
|
output_apply(group->main_output, &output->pending);
|
||||||
|
|
||||||
|
struct wlr_output_group_child *child;
|
||||||
|
wl_list_for_each(child, &group->children, link) {
|
||||||
|
output_apply(child->output, &output->pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: perform a backend-wide commit if possible
|
||||||
|
if (!wlr_output_commit(group->main_output)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_for_each(child, &group->children, link) {
|
||||||
|
if (!wlr_output_commit(child->output)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: update our current state
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
wlr_output_rollback(group->main_output);
|
||||||
|
|
||||||
|
wl_list_for_each(child, &group->children, link) {
|
||||||
|
wlr_output_rollback(child->output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wlr_drm_format_set *group_get_primary_formats(
|
||||||
|
struct wlr_output *output, uint32_t buffer_caps) {
|
||||||
|
struct wlr_output_group *group = group_from_output(output);
|
||||||
|
|
||||||
|
// TODO: intersect primary formats from all children
|
||||||
|
return group->main_output->impl->get_primary_formats(group->main_output, buffer_caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wlr_output_impl output_impl = {
|
||||||
|
.destroy = group_destroy,
|
||||||
|
.commit = group_commit,
|
||||||
|
.get_primary_formats = group_get_primary_formats,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void group_handle_main_output_destroy(struct wl_listener *listener,
|
||||||
|
void *data) {
|
||||||
|
struct wlr_output_group *group =
|
||||||
|
wl_container_of(listener, group, main_output_destroy);
|
||||||
|
wlr_output_destroy(&group->base);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_output_group *wlr_output_group_create(struct wlr_output *main_output) {
|
||||||
|
struct wlr_output_group *group = calloc(1, sizeof(*group));
|
||||||
|
if (group == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_output_init(&group->base, main_output->backend,
|
||||||
|
&output_impl, main_output->display);
|
||||||
|
|
||||||
|
wl_list_init(&group->children);
|
||||||
|
|
||||||
|
group->main_output_destroy.notify = group_handle_main_output_destroy;
|
||||||
|
wl_signal_add(&main_output->events.destroy, &group->main_output_destroy);
|
||||||
|
|
||||||
|
memcpy(&group->base.name, &main_output->name, sizeof(group->base.name));
|
||||||
|
wlr_output_set_description(&group->base, main_output->description);
|
||||||
|
memcpy(&group->base.make, &main_output->make, sizeof(group->base.make));
|
||||||
|
memcpy(&group->base.model, &main_output->model, sizeof(group->base.model));
|
||||||
|
memcpy(&group->base.serial, &main_output->serial, sizeof(group->base.serial));
|
||||||
|
group->base.phys_width = main_output->phys_width;
|
||||||
|
group->base.phys_height = main_output->phys_height;
|
||||||
|
group->base.modes = main_output->modes;
|
||||||
|
group->base.current_mode = main_output->current_mode;
|
||||||
|
group->base.width = main_output->width;
|
||||||
|
group->base.height = main_output->height;
|
||||||
|
group->base.refresh = main_output->refresh;
|
||||||
|
group->base.enabled = main_output->enabled;
|
||||||
|
group->base.scale = main_output->scale;
|
||||||
|
group->base.subpixel = main_output->subpixel;
|
||||||
|
group->base.transform = main_output->transform;
|
||||||
|
group->base.adaptive_sync_status = main_output->adaptive_sync_status;
|
||||||
|
|
||||||
|
// TODO: listen to main output events and pass them through
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void child_handle_output_destroy(struct wl_listener *listener,
|
||||||
|
void *data) {
|
||||||
|
struct wlr_output_group_child *child =
|
||||||
|
wl_container_of(listener, child, output_destroy);
|
||||||
|
wlr_output_group_child_destroy(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_output_group_child *wlr_output_group_add(
|
||||||
|
struct wlr_output_group *group, struct wlr_output *output) {
|
||||||
|
struct wlr_output_group_child *child;
|
||||||
|
wl_list_for_each(child, &group->children, link) {
|
||||||
|
if (child->output == output) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
child = calloc(1, sizeof(*child));
|
||||||
|
if (child == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
child->output = output;
|
||||||
|
wl_list_insert(&group->children, &child->link);
|
||||||
|
|
||||||
|
child->output_destroy.notify = child_handle_output_destroy;
|
||||||
|
wl_signal_add(&output->events.destroy, &child->output_destroy);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wlr_output_group_child_destroy(struct wlr_output_group_child *child) {
|
||||||
|
wl_list_remove(&child->output_destroy.link);
|
||||||
|
wl_list_remove(&child->link);
|
||||||
|
free(child);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue