mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2024-11-22 15:12:26 +00:00
ce3f4c3fe1
The backend doesn't need to handle transform changes, since everything is done in software. In fact, all of the implementations were all identical and just set the transform. We could add support for hardware transforms, but: - This would require a different field (something like hardware_transform) - Not all combinations are possible because there often are hardware limitations - The Wayland protocol isn't ready for this (in particular xdg-output, see [1]) This belongs to a different patch series anyway. [1]: https://patchwork.freedesktop.org/series/52324/
150 lines
4.5 KiB
C
150 lines
4.5 KiB
C
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <wlr/interfaces/wlr_output.h>
|
|
#include <wlr/render/wlr_renderer.h>
|
|
#include <wlr/util/log.h>
|
|
#include "backend/headless.h"
|
|
#include "util/signal.h"
|
|
|
|
static struct wlr_headless_output *headless_output_from_output(
|
|
struct wlr_output *wlr_output) {
|
|
assert(wlr_output_is_headless(wlr_output));
|
|
return (struct wlr_headless_output *)wlr_output;
|
|
}
|
|
|
|
static EGLSurface egl_create_surface(struct wlr_egl *egl, unsigned int width,
|
|
unsigned int height) {
|
|
EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
|
|
|
|
EGLSurface surf = eglCreatePbufferSurface(egl->display, egl->config, attribs);
|
|
if (surf == EGL_NO_SURFACE) {
|
|
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
|
return EGL_NO_SURFACE;
|
|
}
|
|
return surf;
|
|
}
|
|
|
|
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
|
|
int32_t height, int32_t refresh) {
|
|
struct wlr_headless_output *output =
|
|
headless_output_from_output(wlr_output);
|
|
struct wlr_headless_backend *backend = output->backend;
|
|
|
|
if (refresh <= 0) {
|
|
refresh = HEADLESS_DEFAULT_REFRESH;
|
|
}
|
|
|
|
wlr_egl_destroy_surface(&backend->egl, output->egl_surface);
|
|
|
|
output->egl_surface = egl_create_surface(&backend->egl, width, height);
|
|
if (output->egl_surface == EGL_NO_SURFACE) {
|
|
wlr_log(WLR_ERROR, "Failed to recreate EGL surface");
|
|
wlr_output_destroy(wlr_output);
|
|
return false;
|
|
}
|
|
|
|
output->frame_delay = 1000000 / refresh;
|
|
|
|
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
|
|
return true;
|
|
}
|
|
|
|
static bool output_attach_render(struct wlr_output *wlr_output,
|
|
int *buffer_age) {
|
|
struct wlr_headless_output *output =
|
|
headless_output_from_output(wlr_output);
|
|
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
|
buffer_age);
|
|
}
|
|
|
|
static bool output_commit(struct wlr_output *wlr_output) {
|
|
// Nothing needs to be done for pbuffers
|
|
wlr_output_send_present(wlr_output, NULL);
|
|
return true;
|
|
}
|
|
|
|
static void output_destroy(struct wlr_output *wlr_output) {
|
|
struct wlr_headless_output *output =
|
|
headless_output_from_output(wlr_output);
|
|
|
|
wl_list_remove(&output->link);
|
|
|
|
wl_event_source_remove(output->frame_timer);
|
|
|
|
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
|
|
free(output);
|
|
}
|
|
|
|
static const struct wlr_output_impl output_impl = {
|
|
.set_custom_mode = output_set_custom_mode,
|
|
.destroy = output_destroy,
|
|
.attach_render = output_attach_render,
|
|
.commit = output_commit,
|
|
};
|
|
|
|
bool wlr_output_is_headless(struct wlr_output *wlr_output) {
|
|
return wlr_output->impl == &output_impl;
|
|
}
|
|
|
|
static int signal_frame(void *data) {
|
|
struct wlr_headless_output *output = data;
|
|
wlr_output_send_frame(&output->wlr_output);
|
|
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
|
return 0;
|
|
}
|
|
|
|
struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
|
|
unsigned int width, unsigned int height) {
|
|
struct wlr_headless_backend *backend =
|
|
headless_backend_from_backend(wlr_backend);
|
|
|
|
struct wlr_headless_output *output =
|
|
calloc(1, sizeof(struct wlr_headless_output));
|
|
if (output == NULL) {
|
|
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_output");
|
|
return NULL;
|
|
}
|
|
output->backend = backend;
|
|
wlr_output_init(&output->wlr_output, &backend->backend, &output_impl,
|
|
backend->display);
|
|
struct wlr_output *wlr_output = &output->wlr_output;
|
|
|
|
output->egl_surface = egl_create_surface(&backend->egl, width, height);
|
|
if (output->egl_surface == EGL_NO_SURFACE) {
|
|
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
|
goto error;
|
|
}
|
|
|
|
output_set_custom_mode(wlr_output, width, height, 0);
|
|
strncpy(wlr_output->make, "headless", sizeof(wlr_output->make));
|
|
strncpy(wlr_output->model, "headless", sizeof(wlr_output->model));
|
|
snprintf(wlr_output->name, sizeof(wlr_output->name), "HEADLESS-%zd",
|
|
++backend->last_output_num);
|
|
|
|
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
|
NULL)) {
|
|
goto error;
|
|
}
|
|
|
|
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
|
|
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
|
|
wlr_renderer_end(backend->renderer);
|
|
|
|
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
|
|
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
|
|
|
wl_list_insert(&backend->outputs, &output->link);
|
|
|
|
if (backend->started) {
|
|
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
|
wlr_output_update_enabled(wlr_output, true);
|
|
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
|
}
|
|
|
|
return wlr_output;
|
|
|
|
error:
|
|
wlr_output_destroy(&output->wlr_output);
|
|
return NULL;
|
|
}
|