wlr_output_manager: Introduce new abstraction

This commit is contained in:
Alexander Orzechowski 2024-05-05 13:37:54 -04:00
parent 4fe6a8e857
commit bf3ee92741
3 changed files with 373 additions and 0 deletions

View file

@ -0,0 +1,92 @@
/*
* 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_OUTPUT_MANAGER_H
#define WLR_TYPES_OUTPUT_MANAGER_H
#include <stdbool.h>
#include <wayland-server-core.h>
#include <wlr/render/drm_format_set.h>
struct wlr_renderer;
struct wlr_allocator;
struct wlr_backend;
struct wlr_output;
struct wlr_output_manager_backend {
struct wlr_output_manager *manager;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_backend *backend;
struct wl_list link; // wlr_output_manager.backends
// private state
uint32_t locks;
struct wl_listener backend_destroy;
};
struct wlr_output_manager {
struct wl_list backends; // wlr_output_manager_backend.link
struct wlr_output_manager_backend primary;
};
/**
* Initializes the output given output manager. wlr_output_manager_finish
* must be called to clean up this manager.
*/
bool wlr_output_manager_init(struct wlr_output_manager *manager,
struct wlr_backend *backend);
/**
* Finishes this output_manager and cleans up all its resources including any
* output manager backends.
*/
void wlr_output_manager_finish(struct wlr_output_manager *manager);
/**
* This will return a output_manager backend that will be reference counted.
* wlr_output_manager_unlock_backend is required to be called after the usage
* of this is finished.
*/
struct wlr_output_manager_backend *wlr_output_manager_lock_backend(
struct wlr_output_manager *manager, struct wlr_backend *wlr_backend);
/**
* wlr_output_manager_unlock_backend will unlock any backend returned by
* wlr_output_manager_lock_rendener. The allocator and backend allocated
* may be destroyed when the reference count reaches 0
*/
void wlr_output_manager_unlock_backend(struct wlr_output_manager_backend *backend);
/**
* wlr_output_manager_init_output will automatically initialize the given output.
* This is a helder function that will handle unlocking backends automatically
* upon output destroy
*/
bool wlr_output_manager_init_output(struct wlr_output_manager *manager,
struct wlr_output *output);
/**
* Initializes shm for the given wl_display given the constraints all devices
* on the manager have
*/
bool wlr_output_manager_init_wl_shm(struct wlr_output_manager *manager,
struct wl_display *wl_display);
/**
* Initializes the given wl_display given the constraints all devices
* on the manager have
*/
bool wlr_output_manager_init_wl_display(struct wlr_output_manager *manager,
struct wl_display *wl_display);
#endif

View file

@ -60,6 +60,7 @@ wlr_files += files(
'wlr_output_layer.c',
'wlr_output_layout.c',
'wlr_output_management_v1.c',
'wlr_output_manager.c',
'wlr_output_power_management_v1.c',
'wlr_output_swapchain_manager.c',
'wlr_pointer_constraints_v1.c',

280
types/wlr_output_manager.c Normal file
View file

@ -0,0 +1,280 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/backend.h>
#include <wlr/backend/multi.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_shm.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_manager.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/util/log.h>
static void output_manager_backend_finish(
struct wlr_output_manager_backend *backend) {
wlr_allocator_destroy(backend->allocator);
wlr_renderer_destroy(backend->renderer);
wl_list_remove(&backend->backend_destroy.link);
wl_list_remove(&backend->link);
}
static void output_manager_handle_backend_destroy(
struct wl_listener *listener, void *data) {
struct wlr_output_manager_backend *backend =
wl_container_of(listener, backend, backend_destroy);
output_manager_backend_finish(backend);
if (backend == &backend->manager->primary) {
*backend = (struct wlr_output_manager_backend){0};
} else {
free(backend);
}
}
static bool output_manager_backend_init(struct wlr_output_manager *manager,
struct wlr_output_manager_backend *backend, struct wlr_backend *wlr_backend) {
backend->renderer = wlr_renderer_autocreate(wlr_backend);
if (!backend->renderer) {
return false;
}
backend->allocator = wlr_allocator_autocreate(wlr_backend,
manager->primary.renderer);
if (!backend->allocator) {
wlr_renderer_destroy(manager->primary.renderer);
return false;
}
backend->manager = manager;
backend->backend = wlr_backend;
backend->locks = 1;
backend->backend_destroy.notify = output_manager_handle_backend_destroy;
wl_signal_add(&wlr_backend->events.destroy, &backend->backend_destroy);
wl_list_insert(&manager->backends, &backend->link);
return true;
}
struct multi_backend_iterator_data {
struct wlr_output_manager *manager;
bool primary;
};
static void multi_backend_iterator(struct wlr_backend *wlr_backend, void *_data) {
struct multi_backend_iterator_data *data = _data;
// Use the first device as the primary
if (data->primary) {
if (!output_manager_backend_init(data->manager, &data->manager->primary, wlr_backend)) {
return;
}
data->primary = false;
return;
}
struct wlr_output_manager_backend *backend = calloc(1, sizeof(*backend));
if (!backend) {
return;
}
if (!output_manager_backend_init(data->manager, backend, wlr_backend)) {
free(backend);
return;
}
}
bool wlr_output_manager_init(struct wlr_output_manager *manager,
struct wlr_backend *backend) {
*manager = (struct wlr_output_manager){0};
wl_list_init(&manager->backends);
struct multi_backend_iterator_data iter_data = {
.manager = manager,
.primary = true,
};
if (wlr_backend_is_multi(backend)) {
wlr_multi_for_each_backend(backend, multi_backend_iterator, &iter_data);
} else {
multi_backend_iterator(backend, &iter_data);
}
return !wl_list_empty(&manager->backends);
}
void wlr_output_manager_finish(struct wlr_output_manager *manager) {
struct wlr_output_manager_backend *backend;
wl_list_for_each(backend, &manager->backends, link) {
output_manager_backend_finish(backend);
}
}
struct wlr_output_manager_backend *wlr_output_manager_lock_backend(
struct wlr_output_manager *manager, struct wlr_backend *wlr_backend) {
assert(!wlr_backend_is_multi(wlr_backend));
struct wlr_output_manager_backend *backend;
wl_list_for_each(backend, &manager->backends, link) {
if (backend->backend == wlr_backend) {
backend->locks++;
return backend;
}
}
backend = calloc(1, sizeof(*backend));
if (!backend) {
return NULL;
}
if (!output_manager_backend_init(manager, backend, wlr_backend)) {
free(backend);
return NULL;
}
return backend;
}
void wlr_output_manager_unlock_backend(struct wlr_output_manager_backend *backend) {
assert(backend->locks > 0);
backend->locks--;
if (backend->locks != 0) {
return;
}
output_manager_backend_finish(backend);
free(backend);
}
struct output_manager_output {
struct wlr_output_manager_backend *backend;
struct wlr_addon addon;
};
static void manager_output_handle_output_destroy(struct wlr_addon *addon) {
struct output_manager_output *manager_output =
wl_container_of(addon, manager_output, addon);
wlr_addon_finish(&manager_output->addon);
wlr_output_manager_unlock_backend(manager_output->backend);
free(manager_output);
}
static const struct wlr_addon_interface output_addon_impl = {
.name = "wlr_output_manager_output",
.destroy = manager_output_handle_output_destroy,
};
bool wlr_output_manager_init_output(struct wlr_output_manager *manager,
struct wlr_output *output) {
struct output_manager_output *manager_output = calloc(1, sizeof(*manager_output));
if (!manager_output) {
return false;
}
manager_output->backend = wlr_output_manager_lock_backend(
manager, output->backend);
if (!manager_output->backend) {
free(manager_output);
return false;
}
wlr_addon_init(&manager_output->addon, &output->addons, manager, &output_addon_impl);
wlr_output_init_render(output, manager_output->backend->allocator,
manager_output->backend->renderer);
return true;
}
bool wlr_output_manager_init_wl_shm(struct wlr_output_manager *manager,
struct wl_display *wl_display) {
size_t shm_formats_len = 0;
uint32_t *shm_formats = NULL;
struct wlr_output_manager_backend *backend;
wl_list_for_each(backend, &manager->backends, link) {
const struct wlr_drm_format_set *format_set = wlr_renderer_get_texture_formats(
backend->renderer, WLR_BUFFER_CAP_DATA_PTR);
if (format_set == NULL || format_set->len == 0) {
wlr_log(WLR_ERROR, "Failed to initialize wl_shm: "
"cannot get renderer formats");
return NULL;
}
if (!shm_formats) {
shm_formats = malloc(format_set->len * sizeof(uint32_t));
if (!shm_formats) {
wlr_log(WLR_INFO, "Cannot allocate a format set");
return false;
}
for (size_t i = 0; i < format_set->len; i++) {
shm_formats[i] = format_set->formats[i].format;
}
shm_formats_len = format_set->len;
continue;
}
// interset the format lists - null out any formats from the shm_formats
// list when the current renderer doesn't have the format as well.
for (size_t i = 0; i < shm_formats_len; i++) {
if (shm_formats[i] == 0) {
continue;
}
bool found = false;
for (size_t j = 0; j < format_set->len; j++) {
if (format_set->formats[j].format == shm_formats[i]) {
found = true;
break;
}
}
if (!found) {
shm_formats[i] = 0;
}
}
}
// clear out all null formats from the format list
size_t j = 0;
for (size_t i = 0; i < shm_formats_len; i++) {
if (shm_formats[i] != 0) {
shm_formats[j++] = shm_formats[i];
}
}
shm_formats_len = j;
bool ok = wlr_shm_create(wl_display, 1, shm_formats, shm_formats_len);
free(shm_formats);
return ok;
}
bool wlr_output_manager_init_wl_display(struct wlr_output_manager *manager,
struct wl_display *wl_display) {
if (!wlr_output_manager_init_wl_shm(manager, wl_display)) {
return false;
}
struct wlr_renderer *r = manager->primary.renderer;
if (wlr_renderer_get_texture_formats(r, WLR_BUFFER_CAP_DMABUF) != NULL) {
if (wlr_renderer_get_drm_fd(r) >= 0) {
if (wlr_drm_create(wl_display, r) == NULL) {
return false;
}
} else {
wlr_log(WLR_INFO, "Cannot get renderer DRM FD, disabling wl_drm");
}
if (wlr_linux_dmabuf_v1_create_with_renderer(wl_display, 4, r) == NULL) {
return false;
}
}
return true;
}