diff --git a/include/wlr/types/wlr_output_manager.h b/include/wlr/types/wlr_output_manager.h new file mode 100644 index 000000000..fef00a07e --- /dev/null +++ b/include/wlr/types/wlr_output_manager.h @@ -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 +#include +#include + +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 diff --git a/types/meson.build b/types/meson.build index 032143db6..580b7811f 100644 --- a/types/meson.build +++ b/types/meson.build @@ -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', diff --git a/types/wlr_output_manager.c b/types/wlr_output_manager.c new file mode 100644 index 000000000..45ced4620 --- /dev/null +++ b/types/wlr_output_manager.c @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +}