Remove everything, going rustacean

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-06-29 19:32:27 +03:00
parent 1fb48e2426
commit fdafccbdad
15 changed files with 0 additions and 1216 deletions

View File

@@ -1,15 +0,0 @@
#pragma once
#include <format>
#define OPENXR_CHECK(fn_call, ...) \
do { \
XrResult result = fn_call(__VA_ARGS__); \
if (result != XR_SUCCESS) { \
std::array<char, XR_MAX_RESULT_STRING_SIZE> msg {}; \
xrResultToString(nullptr, result, msg.data()); \
throw std::runtime_error( \
std::format("OpenXR call '{}' failed (code {}): {}", #fn_call, \
static_cast<int>(result), msg.data())); \
} \
} while (0)

View File

@@ -1,134 +0,0 @@
#include "compositor.h"
#include <wayland-server.h>
extern "C" {
#include <wlr/backend.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
wlr_scene* wlr_scene_create();
}
#include <cstring>
#include <print>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
void handle_new_output(struct wl_listener* l, void* d)
{
(void)l;
(void)d;
}
void handle_new_input(struct wl_listener* l, void* d)
{
(void)l;
(void)d;
}
void handle_xdg_toplevel(struct wl_listener* l, void* d)
{
(void)l;
(void)d;
}
void handle_xdg_popup(struct wl_listener* l, void* d)
{
(void)l;
(void)d;
}
Compositor::Compositor()
{
// init logging + display + event loop
wlr_log_init(WLR_DEBUG, nullptr);
m_display = wl_display_create();
m_event_loop = wl_display_get_event_loop(m_display);
// openxr backend
m_backend = wlr_openxr_backend_create(m_display, m_event_loop);
if (!m_backend)
throw std::runtime_error("backend fail");
// renderer + allocator
m_renderer = wlr_renderer_autocreate(m_backend);
if (!m_renderer)
throw std::runtime_error("Failed to create wlr_renderer.");
wlr_renderer_init_wl_display(m_renderer, m_display);
m_allocator = wlr_allocator_autocreate(m_backend, m_renderer);
if (!m_allocator)
throw std::runtime_error("Failed to create wlr_allocator.");
// compositor/subcompositor/data-device
m_compositor = wlr_compositor_create(m_display, 4, m_renderer);
if (!m_compositor)
throw std::runtime_error("Failed to create wlr_compositor.");
m_subcompositor = wlr_subcompositor_create(m_display);
if (!m_subcompositor)
throw std::runtime_error("Failed to create wlr_subcompositor.");
m_data_device = wlr_data_device_manager_create(m_display);
if (!m_data_device)
throw std::runtime_error("Failed to create wlr_data_device_manager.");
// scene + output layout
m_scene = wlr_scene_create();
if (!m_scene)
throw std::runtime_error("Failed to create wlr_scene.");
m_output_layout = wlr_output_layout_create(m_display);
if (!m_output_layout)
throw std::runtime_error("Failed to create wlr_output_layout.");
// seat for input
m_seat = wlr_seat_create(m_display, "seat0");
if (!m_seat)
throw std::runtime_error("Failed to create wlr_seat.");
// xdg shell
m_xdg_shell = wlr_xdg_shell_create(m_display, 3);
if (!m_xdg_shell)
throw std::runtime_error("Failed to create wlr_xdg_shell.");
m_new_xdg_toplevel.notify = handle_xdg_toplevel;
wl_signal_add(&m_xdg_shell->events.new_toplevel, &m_new_xdg_toplevel);
m_new_xdg_popup.notify = handle_xdg_popup;
wl_signal_add(&m_xdg_shell->events.new_popup, &m_new_xdg_popup);
// hook up backend signals
m_new_output.notify = handle_new_output;
wl_signal_add(&m_backend->events.new_output, &m_new_output);
m_new_input.notify = handle_new_input;
wl_signal_add(&m_backend->events.new_input, &m_new_input);
}
Compositor::~Compositor()
{
if (m_backend) {
wlr_backend_destroy(m_backend);
m_backend = nullptr;
}
if (m_display) {
wl_display_destroy(m_display);
m_display = nullptr;
}
}
void Compositor::run()
{
char const* socket = wl_display_add_socket_auto(m_display);
if (!socket) {
throw std::runtime_error("Failed to create socket");
}
if (!wlr_backend_start(m_backend)) {
throw std::runtime_error("Failed to start wlroots backend");
}
setenv("WAYLAND_DISPLAY", socket, true);
wlr_log(
WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
wl_display_run(m_display);
}

View File

@@ -1,49 +0,0 @@
#pragma once
#include <wayland-server.h>
extern "C" {
#include <wlr/backend.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
}
#include "wlr/openxr_gl.h"
struct wlr_scene;
struct Compositor {
Compositor();
~Compositor();
void run();
void stop() { wl_display_terminate(m_display); }
wl_listener m_new_output;
private:
// wlroots integration
wl_display* m_display = nullptr;
wl_event_loop* m_event_loop = nullptr;
wlr_backend* m_backend = nullptr;
wlr_renderer* m_renderer = nullptr;
wlr_allocator* m_allocator = nullptr;
wlr_compositor* m_compositor = nullptr;
wlr_subcompositor* m_subcompositor = nullptr;
wlr_data_device_manager* m_data_device = nullptr;
wlr_scene* m_scene = nullptr;
wlr_output_layout* m_output_layout = nullptr;
wlr_xdg_shell* m_xdg_shell = nullptr;
wlr_seat* m_seat = nullptr;
wl_listener m_new_input;
wl_listener m_new_xdg_toplevel;
wl_listener m_new_xdg_popup;
};

View File

@@ -1,16 +0,0 @@
#include "compositor.h"
#include <csignal>
#include <memory>
#include <print>
std::unique_ptr<Compositor> g_c;
int main(void)
{
g_c = std::make_unique<Compositor>();
std::signal(SIGINT, [](int) { g_c->stop(); });
g_c->run();
std::println("bai bai~!");
}

View File

@@ -1,20 +0,0 @@
#include "openxr_gl.h"
extern "C" {
#include <wayland-server-core.h>
}
extern "C" {
struct wlr_backend* wlr_my_backend_create(
struct wl_event_loop* loop, char const* name)
{
(void)name;
return wlr_openxr_backend_create(nullptr, loop);
}
bool wlr_backend_is_mybackend(struct wlr_backend* backend)
{
return wlr_backend_is_openxr(backend);
}
}

View File

@@ -1,359 +0,0 @@
#include "openxr_gl.h"
extern "C" {
#include <wayland-server-core.h>
#include <wlr/backend/interface.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
}
#include <cstring>
#include <memory>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>
#include <vector>
// Internal backend definition
struct openxr_backend {
wlr_backend base;
wl_display* display;
wl_event_loop* event_loop;
XrInstance instance {};
XrSession session {};
XrSpace app_space { XR_NULL_HANDLE };
XrSwapchain swapchain { XR_NULL_HANDLE };
int32_t width = 0, height = 0;
std::vector<XrSwapchainImageOpenGLKHR> swapchain_images;
std::vector<GLuint> framebuffers;
bool started = false;
struct wlr_output* output = nullptr;
};
static void output_destroy(struct wlr_output* wlr_output) { (void)wlr_output; }
static bool output_test(
struct wlr_output* wlr_output, const struct wlr_output_state* state)
{
(void)wlr_output;
(void)state;
return true;
}
static bool output_commit(
struct wlr_output* wlr_output, const struct wlr_output_state* state)
{
// Retrieve our backend
auto* xr = reinterpret_cast<openxr_backend*>(wlr_output->backend);
(void)state;
// Wait for frame
XrFrameState frameState { XR_TYPE_FRAME_STATE };
xrWaitFrame(xr->session, nullptr, &frameState);
xrBeginFrame(xr->session, nullptr);
// Locate views
uint32_t viewCount = static_cast<uint32_t>(xr->framebuffers.size());
std::vector<XrView> views(viewCount, { XR_TYPE_VIEW });
XrViewLocateInfo viewLocInfo { XR_TYPE_VIEW_LOCATE_INFO };
viewLocInfo.viewConfigurationType
= XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
viewLocInfo.displayTime = frameState.predictedDisplayTime;
viewLocInfo.space = xr->app_space;
XrViewState viewState { XR_TYPE_VIEW_STATE };
uint32_t viewCountOutput;
xrLocateViews(xr->session, &viewLocInfo, &viewState, viewCount,
&viewCountOutput, views.data());
// Prepare projection views
std::vector<XrCompositionLayerProjectionView> projViews(
viewCountOutput, { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW });
for (uint32_t i = 0; i < viewCountOutput; ++i) {
// Acquire swapchain image
XrSwapchainImageAcquireInfo acqInfo {
XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO
};
uint32_t imgIndex;
xrAcquireSwapchainImage(xr->swapchain, &acqInfo, &imgIndex);
XrSwapchainImageWaitInfo waitInfo { XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
waitInfo.timeout = XR_INFINITE_DURATION;
xrWaitSwapchainImage(xr->swapchain, &waitInfo);
// Bind framebuffer and clear
glBindFramebuffer(GL_FRAMEBUFFER, xr->framebuffers[imgIndex]);
glViewport(0, 0, xr->width, xr->height);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// TODO: render scene (e.g., wlr_scene_render)
// Release swapchain image
{
XrSwapchainImageReleaseInfo relInfo {
XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO
};
xrReleaseSwapchainImage(xr->swapchain, &relInfo);
}
// Setup projection view
projViews[i].pose = views[i].pose;
projViews[i].fov = views[i].fov;
projViews[i].subImage.swapchain = xr->swapchain;
projViews[i].subImage.imageArrayIndex = imgIndex;
// Set image rectangle offset and extent
projViews[i].subImage.imageRect.offset.x = 0;
projViews[i].subImage.imageRect.offset.y = 0;
projViews[i].subImage.imageRect.extent.width = xr->width;
projViews[i].subImage.imageRect.extent.height = xr->height;
}
// Unbind
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// End frame
XrCompositionLayerProjection layer { XR_TYPE_COMPOSITION_LAYER_PROJECTION };
layer.space = xr->app_space;
layer.viewCount = static_cast<uint32_t>(projViews.size());
layer.views = projViews.data();
XrCompositionLayerBaseHeader const* layers[]
= { reinterpret_cast<XrCompositionLayerBaseHeader const*>(&layer) };
XrFrameEndInfo frameEndInfo { XR_TYPE_FRAME_END_INFO };
frameEndInfo.displayTime = frameState.predictedDisplayTime;
frameEndInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
frameEndInfo.layerCount = 1;
frameEndInfo.layers = layers;
xrEndFrame(xr->session, &frameEndInfo);
return true;
}
static const struct wlr_drm_format_set* output_get_primary_formats(
struct wlr_output* wlr_output, uint32_t buffer_caps)
{
(void)wlr_output;
(void)buffer_caps;
return NULL;
}
static const struct wlr_output_impl output_impl = {
.destroy = output_destroy,
.test = output_test,
.commit = output_commit,
.get_primary_formats = output_get_primary_formats,
};
static bool backend_start(wlr_backend* backend)
{
auto* xr = reinterpret_cast<openxr_backend*>(backend);
if (xr->started)
return true;
XrApplicationInfo ai {};
std::strncpy(
ai.applicationName, "LunarWM", XR_MAX_APPLICATION_NAME_SIZE - 1);
ai.applicationVersion = 1;
std::strncpy(ai.engineName, "LunarWM", XR_MAX_ENGINE_NAME_SIZE - 1);
ai.engineVersion = 1;
ai.apiVersion = XR_API_VERSION_1_0;
char const* exts[] = {
XR_EXT_DEBUG_UTILS_EXTENSION_NAME,
XR_KHR_OPENGL_ENABLE_EXTENSION_NAME,
};
XrInstanceCreateInfo ic { XR_TYPE_INSTANCE_CREATE_INFO };
ic.applicationInfo = ai;
ic.enabledExtensionCount = sizeof(exts) / sizeof(exts[0]);
ic.enabledExtensionNames = exts;
if (xrCreateInstance(&ic, &xr->instance) != XR_SUCCESS) {
wlr_log(WLR_ERROR, "Failed to create OpenXR instance");
return false;
}
XrSystemGetInfo sgi { XR_TYPE_SYSTEM_GET_INFO };
sgi.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
XrSystemId system_id;
if (xrGetSystem(xr->instance, &sgi, &system_id) != XR_SUCCESS) {
wlr_log(WLR_ERROR, "xrGetSystem failed");
return false;
}
PFN_xrGetOpenGLGraphicsRequirementsKHR get_reqs;
xrGetInstanceProcAddr(xr->instance, "xrGetOpenGLGraphicsRequirementsKHR",
reinterpret_cast<PFN_xrVoidFunction*>(&get_reqs));
XrGraphicsRequirementsOpenGLKHR gl_reqs {
XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR
};
get_reqs(xr->instance, system_id, &gl_reqs);
Display* dpy = XOpenDisplay(nullptr);
if (!dpy) {
wlr_log(WLR_ERROR, "Failed to open X display");
return false;
}
int screen = DefaultScreen(dpy);
static int vis_attrs[] = { GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE,
GLX_WINDOW_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE,
GLX_TRUE_COLOR, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8,
GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, True, None };
int fbcount;
GLXFBConfig* fbcs = glXChooseFBConfig(dpy, screen, vis_attrs, &fbcount);
if (!fbcs || !fbcount) {
wlr_log(WLR_ERROR, "No GLXFBConfig found");
return false;
}
GLXFBConfig fbc = fbcs[0];
XFree(fbcs);
XVisualInfo* vi = glXGetVisualFromFBConfig(dpy, fbc);
Window root = RootWindow(dpy, screen);
XSetWindowAttributes swa;
swa.colormap = XCreateColormap(dpy, root, vi->visual, AllocNone);
swa.event_mask = ExposureMask;
Window win = XCreateWindow(dpy, root, 0, 0, 16, 16, 0, vi->depth,
InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
GLXContext ctx
= glXCreateNewContext(dpy, fbc, GLX_RGBA_TYPE, nullptr, True);
glXMakeContextCurrent(dpy, win, win, ctx);
XrGraphicsBindingOpenGLXlibKHR bind {
XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR
};
bind.xDisplay = dpy;
bind.visualid = vi->visualid;
bind.glxFBConfig = fbc;
bind.glxDrawable = win;
bind.glxContext = ctx;
XrSessionCreateInfo sci { XR_TYPE_SESSION_CREATE_INFO };
sci.next = &bind;
sci.systemId = system_id;
if (xrCreateSession(xr->instance, &sci, &xr->session) != XR_SUCCESS) {
wlr_log(WLR_ERROR, "xrCreateSession failed");
return false;
}
// Create reference space
XrReferenceSpaceCreateInfo spaceInfo {
XR_TYPE_REFERENCE_SPACE_CREATE_INFO
};
spaceInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
spaceInfo.poseInReferenceSpace = { { 0, 0, 0, 1 }, { 0, 0, 0 } };
if (xrCreateReferenceSpace(xr->session, &spaceInfo, &xr->app_space)
!= XR_SUCCESS) {
wlr_log(WLR_ERROR, "xrCreateReferenceSpace failed");
return false;
}
// Get recommended view configuration
uint32_t viewCount;
xrEnumerateViewConfigurationViews(xr->instance, system_id,
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 0, &viewCount, nullptr);
std::vector<XrViewConfigurationView> viewConfigs(
viewCount, { XR_TYPE_VIEW_CONFIGURATION_VIEW });
xrEnumerateViewConfigurationViews(xr->instance, system_id,
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, viewCount, &viewCount,
viewConfigs.data());
xr->width = viewConfigs[0].recommendedImageRectWidth;
xr->height = viewConfigs[0].recommendedImageRectHeight;
// Create swapchain
uint32_t formatCount;
xrEnumerateSwapchainFormats(xr->session, 0, &formatCount, nullptr);
std::vector<int64_t> formats(formatCount);
xrEnumerateSwapchainFormats(
xr->session, formatCount, &formatCount, formats.data());
int64_t swapFormat = formats.empty() ? 0 : formats[0];
XrSwapchainCreateInfo swapchain_ci { XR_TYPE_SWAPCHAIN_CREATE_INFO };
swapchain_ci.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
swapchain_ci.format = swapFormat;
swapchain_ci.sampleCount = 1;
swapchain_ci.width = xr->width;
swapchain_ci.height = xr->height;
swapchain_ci.faceCount = 1;
swapchain_ci.arraySize = viewCount;
swapchain_ci.mipCount = 1;
if (xrCreateSwapchain(xr->session, &swapchain_ci, &xr->swapchain)
!= XR_SUCCESS) {
wlr_log(WLR_ERROR, "xrCreateSwapchain failed");
return false;
}
// Enumerate swapchain images
uint32_t imageCount;
xrEnumerateSwapchainImages(xr->swapchain, 0, &imageCount, nullptr);
xr->swapchain_images.resize(
imageCount, { XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR });
xrEnumerateSwapchainImages(xr->swapchain, imageCount, &imageCount,
reinterpret_cast<XrSwapchainImageBaseHeader*>(
xr->swapchain_images.data()));
// Create framebuffers for each image
xr->framebuffers.resize(imageCount);
for (uint32_t i = 0; i < imageCount; ++i) {
GLuint fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, xr->swapchain_images[i].image, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
xr->framebuffers[i] = fbo;
}
xr->output = static_cast<wlr_output*>(calloc(1, sizeof(wlr_output)));
if (xr->output) {
wlr_output_init(
xr->output, &xr->base, &output_impl, xr->event_loop, nullptr);
wlr_output_set_name(xr->output, "OpenXR");
wlr_output_create_global(xr->output, xr->display);
}
xr->started = true;
wlr_log(WLR_INFO, "OpenXR backend started");
return true;
}
static void backend_destroy(wlr_backend* backend)
{
auto* xr = reinterpret_cast<openxr_backend*>(backend);
// destroy swapchain and framebuffers
if (xr->swapchain) {
xrDestroySwapchain(xr->swapchain);
xr->swapchain = XR_NULL_HANDLE;
}
for (auto fbo : xr->framebuffers) {
glDeleteFramebuffers(1, &fbo);
}
// destroy reference space
if (xr->app_space) {
xrDestroySpace(xr->app_space);
xr->app_space = XR_NULL_HANDLE;
}
// destroy XR session and instance
if (xr->session) {
xrDestroySession(xr->session);
}
if (xr->instance) {
xrDestroyInstance(xr->instance);
}
free(xr);
}
static int backend_get_drm_fd(wlr_backend* backend)
{
(void)backend;
return -1;
}
static wlr_backend_impl const backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_drm_fd = backend_get_drm_fd,
};
wlr_backend* wlr_openxr_backend_create(wl_display* display, wl_event_loop* loop)
{
openxr_backend* b = static_cast<openxr_backend*>(calloc(1, sizeof(*b)));
if (!b)
return nullptr;
b->display = display;
b->event_loop = loop;
wlr_backend_init(&b->base, &backend_impl);
return &b->base;
}
bool wlr_backend_is_openxr(wlr_backend* backend)
{
return backend->impl == &backend_impl;
}

View File

@@ -1,18 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
struct wlr_backend;
struct wl_display;
struct wl_event_loop;
struct wlr_backend* wlr_openxr_backend_create(
struct wl_display* display, struct wl_event_loop* loop);
bool wlr_backend_is_openxr(struct wlr_backend* backend);
#ifdef __cplusplus
}
#endif

View File

@@ -1,91 +0,0 @@
extern "C" {
#include <wlr/backend/interface.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
}
extern "C" {
static void parse_setup(struct wlr_output* output)
{
(void)output;
// TODO: parse your backends setup (e.g. native display info)
}
static bool output_set_custom_mode(struct wlr_output* wlr_output, int32_t width,
int32_t height, int32_t refresh)
{
(void)wlr_output;
(void)width;
(void)height;
(void)refresh;
// Intentionally left blank
return true;
}
static void output_destroy(struct wlr_output* wlr_output) { (void)wlr_output; }
static bool output_test(
struct wlr_output* wlr_output, const struct wlr_output_state* state)
{
(void)wlr_output;
(void)state;
// TODO: check if this state is supported
return true;
}
static bool output_commit(
struct wlr_output* wlr_output, const struct wlr_output_state* state)
{
(void)wlr_output;
(void)state;
// TODO: apply enable/disable, mode, buffer, etc.
return true;
}
static bool output_set_cursor(struct wlr_output* wlr_output,
struct wlr_buffer* buffer, int32_t hotspot_x, int32_t hotspot_y)
{
(void)wlr_output;
(void)buffer;
(void)hotspot_x;
(void)hotspot_y;
// TODO: upload cursor image
return false;
}
static bool output_move_cursor(struct wlr_output* wlr_output, int x, int y)
{
(void)wlr_output;
(void)x;
(void)y;
// TODO: move cursor to x, y
return true;
}
static const struct wlr_drm_format_set* output_get_primary_formats(
struct wlr_output* wlr_output, uint32_t buffer_caps)
{
(void)wlr_output;
(void)buffer_caps;
// TODO: return DRM/SHM formats your backend supports
return NULL;
}
static const struct wlr_output_impl output_impl = {
.set_cursor = output_set_cursor,
.move_cursor = output_move_cursor,
.destroy = output_destroy,
.test = output_test,
.commit = output_commit,
.get_primary_formats = output_get_primary_formats,
};
struct wlr_output* wlr_my_output_create(struct wlr_backend* backend)
{
(void)backend;
// TODO: alloc + init your output struct and call
// wlr_output_init(&output->base, backend, &output_impl, …)
return NULL;
}
}