@@ -8,11 +8,19 @@ set(CMAKE_CXX_STANDARD 23)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_MODULE_STD 1)
|
set(CMAKE_CXX_MODULE_STD 1)
|
||||||
|
|
||||||
|
add_compile_definitions(
|
||||||
|
WLR_USE_UNSTABLE
|
||||||
|
XR_USE_PLATFORM_EGL
|
||||||
|
XR_USE_GRAPHICS_API_OPENGL_ES
|
||||||
|
)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
|
|
||||||
pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET GLOBAL wayland-server)
|
pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET GLOBAL wayland-server)
|
||||||
pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl)
|
pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl)
|
||||||
pkg_check_modules(GLES2 REQUIRED IMPORTED_TARGET glesv2)
|
pkg_check_modules(GLES2 REQUIRED IMPORTED_TARGET glesv2)
|
||||||
|
pkg_check_modules(WLROOTS REQUIRED IMPORTED_TARGET wlroots-0.19)
|
||||||
|
pkg_check_modules(OPENXR REQUIRED IMPORTED_TARGET openxr)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME})
|
add_executable(${PROJECT_NAME})
|
||||||
target_sources(${PROJECT_NAME} PUBLIC
|
target_sources(${PROJECT_NAME} PUBLIC
|
||||||
@@ -20,16 +28,14 @@ target_sources(${PROJECT_NAME} PUBLIC
|
|||||||
)
|
)
|
||||||
target_sources(${PROJECT_NAME} PUBLIC FILE_SET CXX_MODULES FILES
|
target_sources(${PROJECT_NAME} PUBLIC FILE_SET CXX_MODULES FILES
|
||||||
src/Math.cppm
|
src/Math.cppm
|
||||||
src/wl/Shm.cppm
|
src/Compositor.cppm
|
||||||
src/wl/Subsurface.cppm
|
|
||||||
src/wl/Subcompositor.cppm
|
|
||||||
src/wl/Region.cppm
|
|
||||||
src/wl/Compositor.cppm
|
|
||||||
src/LunarWM.cppm
|
src/LunarWM.cppm
|
||||||
)
|
)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||||
PkgConfig::WAYLAND
|
PkgConfig::WAYLAND
|
||||||
PkgConfig::EGL
|
PkgConfig::EGL
|
||||||
PkgConfig::GLES2
|
PkgConfig::GLES2
|
||||||
|
PkgConfig::WLROOTS
|
||||||
|
PkgConfig::OPENXR
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -46,6 +46,8 @@
|
|||||||
xorg.xcbutilrenderutil
|
xorg.xcbutilrenderutil
|
||||||
xorg.xcbutilwm
|
xorg.xcbutilwm
|
||||||
xorg.xcbutilerrors
|
xorg.xcbutilerrors
|
||||||
|
wlroots
|
||||||
|
vulkan-loader
|
||||||
|
|
||||||
libffi
|
libffi
|
||||||
wayland
|
wayland
|
||||||
|
|||||||
3
src/Compositor.cppm
Normal file
3
src/Compositor.cppm
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module;
|
||||||
|
|
||||||
|
export module LunarWM.Compositor;
|
||||||
507
src/LunarWM.cppm
507
src/LunarWM.cppm
@@ -1,153 +1,472 @@
|
|||||||
module;
|
module;
|
||||||
|
|
||||||
#include <poll.h>
|
#include <cassert>
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
#include <EGL/eglext.h>
|
#include <EGL/eglext.h>
|
||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <wayland-server.h>
|
#include <GLES2/gl2ext.h>
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
#include <openxr/openxr_platform.h>
|
||||||
|
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
#include <wayland-egl.h>
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <wlr/backend/wayland.h>
|
||||||
|
#include <wlr/render/allocator.h>
|
||||||
|
#include <wlr/render/gles2.h>
|
||||||
|
#include <wlr/types/wlr_compositor.h>
|
||||||
|
#include <wlr/types/wlr_data_device.h>
|
||||||
|
#include <wlr/types/wlr_subcompositor.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include <wlr/render/egl.h>
|
||||||
|
}
|
||||||
|
|
||||||
export module LunarWM.LunarWM;
|
export module LunarWM.LunarWM;
|
||||||
|
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
import LunarWM.wl.Subcompositor;
|
|
||||||
import LunarWM.wl.Shm;
|
|
||||||
|
|
||||||
namespace LunarWM {
|
namespace LunarWM {
|
||||||
|
|
||||||
export struct LunarWM {
|
export struct LunarWM {
|
||||||
LunarWM();
|
LunarWM() = default;
|
||||||
~LunarWM();
|
~LunarWM();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct {
|
void init_wayland();
|
||||||
wl_display *display = nullptr;
|
void init_xr();
|
||||||
wl_event_loop *event_loop = nullptr;
|
|
||||||
std::string socket;
|
|
||||||
|
|
||||||
std::unique_ptr<wl::Shm> shm;
|
void poll_events_xr();
|
||||||
wl_global *subcompositor = nullptr;
|
|
||||||
|
bool m_initialized{};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
wl_display *display{};
|
||||||
|
wl_event_loop *event_loop{};
|
||||||
|
|
||||||
|
wlr_backend *backend{};
|
||||||
|
wlr_renderer *renderer{};
|
||||||
|
|
||||||
|
wlr_egl *egl{};
|
||||||
|
EGLDisplay egl_display;
|
||||||
|
EGLContext egl_context;
|
||||||
|
EGLConfig egl_config;
|
||||||
|
|
||||||
|
wlr_allocator *allocator{};
|
||||||
|
wlr_compositor *compositor{};
|
||||||
|
wlr_subcompositor *subcompositor{};
|
||||||
|
wlr_data_device_manager *data_device_manager{};
|
||||||
|
|
||||||
|
wlr_seat *seat{};
|
||||||
|
wl_list keyboards;
|
||||||
|
wl_listener new_input_listener{};
|
||||||
} m_wayland;
|
} m_wayland;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
EGLDisplay display = EGL_NO_DISPLAY;
|
std::optional<XrInstance> instance;
|
||||||
EGLConfig config;
|
std::optional<XrSystemId> system_id;
|
||||||
EGLContext context;
|
XrSession session = XR_NULL_HANDLE;
|
||||||
} m_egl;
|
XrSessionState session_state = XR_SESSION_STATE_UNKNOWN;
|
||||||
|
} m_xr;
|
||||||
|
|
||||||
bool m_running = true;
|
bool m_running{};
|
||||||
|
bool m_session_running{};
|
||||||
|
bool m_session_state{};
|
||||||
};
|
};
|
||||||
|
|
||||||
LunarWM::LunarWM() {
|
void LunarWM::init() {
|
||||||
{ // Wayland
|
this->init_wayland();
|
||||||
m_wayland.display = wl_display_create();
|
this->init_xr();
|
||||||
if (!m_wayland.display)
|
|
||||||
throw std::runtime_error("Failed to create wayland display");
|
|
||||||
|
|
||||||
auto const socket = wl_display_add_socket_auto(m_wayland.display);
|
m_initialized = true;
|
||||||
if (!socket)
|
}
|
||||||
throw std::runtime_error("Failed to add socket");
|
|
||||||
m_wayland.socket = socket;
|
|
||||||
setenv("WAYLAND_DISPLAY", m_wayland.socket.c_str(), 1);
|
|
||||||
|
|
||||||
m_wayland.event_loop = wl_display_get_event_loop(m_wayland.display);
|
void LunarWM::init_wayland() {
|
||||||
if (!m_wayland.event_loop)
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
throw std::runtime_error("Failed to get display event loop");
|
|
||||||
|
m_wayland.display = wl_display_create();
|
||||||
|
if (!m_wayland.display) {
|
||||||
|
throw std::runtime_error("Failed to create wayland display");
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // EGL
|
m_wayland.event_loop = wl_display_get_event_loop(m_wayland.display);
|
||||||
m_egl.display = eglGetPlatformDisplay(EGL_PLATFORM_SURFACELESS_MESA,
|
if (!m_wayland.event_loop) {
|
||||||
EGL_DEFAULT_DISPLAY, nullptr);
|
throw std::runtime_error("Failed to get wayland event loop");
|
||||||
bool ret = eglInitialize(m_egl.display, nullptr, nullptr);
|
}
|
||||||
if (ret != EGL_TRUE)
|
|
||||||
throw std::runtime_error("eglInitialize failed");
|
|
||||||
|
|
||||||
// clang-format off
|
m_wayland.backend = wlr_backend_autocreate(m_wayland.event_loop, nullptr);
|
||||||
EGLint attribs[] {
|
if (!m_wayland.backend) {
|
||||||
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
throw std::runtime_error("Failed to create wlroots backend");
|
||||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
}
|
||||||
EGL_NONE
|
|
||||||
|
setenv("WLR_RENDERER", "gles2", 1);
|
||||||
|
m_wayland.renderer = wlr_renderer_autocreate(m_wayland.backend);
|
||||||
|
if (!m_wayland.renderer) {
|
||||||
|
throw std::runtime_error("Failed to create wlroots renderer");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wayland.egl = wlr_gles2_renderer_get_egl(m_wayland.renderer);
|
||||||
|
if (!m_wayland.egl) {
|
||||||
|
throw std::runtime_error("Failed to get egl information from renderer");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wayland.egl_display = wlr_egl_get_display(m_wayland.egl);
|
||||||
|
m_wayland.egl_context = wlr_egl_get_context(m_wayland.egl);
|
||||||
|
m_wayland.egl_config = EGL_NO_CONFIG_KHR;
|
||||||
|
|
||||||
|
if (!wlr_renderer_init_wl_display(m_wayland.renderer, m_wayland.display)) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to initialize renderer with wayland display");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wayland.allocator =
|
||||||
|
wlr_allocator_autocreate(m_wayland.backend, m_wayland.renderer);
|
||||||
|
if (!m_wayland.allocator) {
|
||||||
|
throw std::runtime_error("Failed to create wlroots allocator");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wayland.compositor =
|
||||||
|
wlr_compositor_create(m_wayland.display, 5, m_wayland.renderer);
|
||||||
|
if (!m_wayland.compositor) {
|
||||||
|
throw std::runtime_error("Failed to create wlroots compositor");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wayland.subcompositor = wlr_subcompositor_create(m_wayland.display);
|
||||||
|
if (!m_wayland.subcompositor) {
|
||||||
|
throw std::runtime_error("Failed to create wlroots subcompositor");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wayland.data_device_manager =
|
||||||
|
wlr_data_device_manager_create(m_wayland.display);
|
||||||
|
if (!m_wayland.data_device_manager) {
|
||||||
|
throw std::runtime_error("Failed to create wlroots data device manager");
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_init(&m_wayland.keyboards);
|
||||||
|
|
||||||
|
m_wayland.new_input_listener.notify = [](wl_listener *listener, void *data) {
|
||||||
|
auto wm = reinterpret_cast<LunarWM *>(
|
||||||
|
wl_container_of(listener, static_cast<LunarWM *>(nullptr),
|
||||||
|
m_wayland.new_input_listener));
|
||||||
|
auto dev = reinterpret_cast<wlr_input_device *>(data);
|
||||||
|
if (dev->type == WLR_INPUT_DEVICE_KEYBOARD) {
|
||||||
|
// FIXME: Implement
|
||||||
|
} else if (dev->type == WLR_INPUT_DEVICE_POINTER) {
|
||||||
|
// FIXME: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t caps = WL_SEAT_CAPABILITY_POINTER;
|
||||||
|
if (!wl_list_empty(&wm->m_wayland.keyboards)) {
|
||||||
|
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
|
||||||
|
}
|
||||||
|
assert(wm->m_wayland.seat);
|
||||||
|
wlr_seat_set_capabilities(wm->m_wayland.seat, caps);
|
||||||
|
};
|
||||||
|
wl_signal_add(&m_wayland.backend->events.new_input,
|
||||||
|
&m_wayland.new_input_listener);
|
||||||
|
m_wayland.seat = wlr_seat_create(m_wayland.display, "seat0");
|
||||||
|
if (!m_wayland.seat) {
|
||||||
|
throw std::runtime_error("Failed to create wlroots seat");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LunarWM::init_xr() {
|
||||||
|
XrApplicationInfo app_info{
|
||||||
|
.applicationVersion = 1,
|
||||||
|
.engineVersion = 1,
|
||||||
|
.apiVersion = XR_CURRENT_API_VERSION,
|
||||||
|
};
|
||||||
|
strncpy(app_info.applicationName, "LunarWM", XR_MAX_APPLICATION_NAME_SIZE);
|
||||||
|
strncpy(app_info.engineName, "LunarWM Engine", XR_MAX_ENGINE_NAME_SIZE);
|
||||||
|
|
||||||
|
std::vector<std::string> instance_extensions {
|
||||||
|
XR_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
||||||
|
XR_MNDX_EGL_ENABLE_EXTENSION_NAME,
|
||||||
|
XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME,
|
||||||
|
};
|
||||||
|
std::vector<std::string> apiLayers;
|
||||||
|
std::vector<char const *> active_instance_extensions;
|
||||||
|
std::vector<char const *> activeAPILayers;
|
||||||
|
|
||||||
|
uint32_t apiLayerCount = 0;
|
||||||
|
std::vector<XrApiLayerProperties> apiLayerProperties;
|
||||||
|
if (xrEnumerateApiLayerProperties(0, &apiLayerCount, nullptr) != XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to enumerate API layer properties");
|
||||||
|
}
|
||||||
|
apiLayerProperties.resize(apiLayerCount, {XR_TYPE_API_LAYER_PROPERTIES});
|
||||||
|
if (xrEnumerateApiLayerProperties(apiLayerCount, &apiLayerCount, apiLayerProperties.data()) != XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to enumerate API layer properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &requestLayer : apiLayers) {
|
||||||
|
for (auto &layerProperty : apiLayerProperties) {
|
||||||
|
if (strcmp(requestLayer.c_str(), layerProperty.layerName) != 0) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
activeAPILayers.push_back(requestLayer.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t extensionCount = 0;
|
||||||
|
std::vector<XrExtensionProperties> extensionProperties;
|
||||||
|
if (xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionCount, nullptr) != XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to enumerate OpenXR instance extension properties");
|
||||||
|
}
|
||||||
|
extensionProperties.resize(extensionCount, {XR_TYPE_EXTENSION_PROPERTIES});
|
||||||
|
if (xrEnumerateInstanceExtensionProperties(nullptr, extensionCount, &extensionCount, extensionProperties.data()) != XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to enumerate OpenXR instance extension properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &requestedInstanceExtension : instance_extensions) {
|
||||||
|
bool found = false;
|
||||||
|
for (auto &extensionProperty : extensionProperties) {
|
||||||
|
if (strcmp(requestedInstanceExtension.c_str(), extensionProperty.extensionName) != 0) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
active_instance_extensions.push_back(requestedInstanceExtension.c_str());
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
throw std::runtime_error(std::format("Failed to find OpenXR instance extension: {}", requestedInstanceExtension));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
XrInstanceCreateInfo ci {
|
||||||
|
.type = XR_TYPE_INSTANCE_CREATE_INFO,
|
||||||
|
.next = NULL,
|
||||||
|
.createFlags = 0,
|
||||||
|
.applicationInfo = app_info,
|
||||||
|
.enabledApiLayerCount = static_cast<std::uint32_t>(activeAPILayers.size()),
|
||||||
|
.enabledApiLayerNames = activeAPILayers.data(),
|
||||||
|
.enabledExtensionCount = static_cast<std::uint32_t>(active_instance_extensions.size()),
|
||||||
|
.enabledExtensionNames = active_instance_extensions.data(),
|
||||||
};
|
};
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
EGLint num_configs;
|
XrInstance instance;
|
||||||
ret =
|
if (xrCreateInstance(&ci, &instance) != XR_SUCCESS) {
|
||||||
eglChooseConfig(m_egl.display, attribs, &m_egl.config, 1, &num_configs);
|
throw std::runtime_error("Failed to create OpenXR instance");
|
||||||
if (!num_configs || ret != EGL_TRUE)
|
}
|
||||||
throw std::runtime_error("eglChooseConfig failed");
|
m_xr.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
ret = eglBindAPI(EGL_OPENGL_ES_API);
|
{
|
||||||
if (ret != EGL_TRUE)
|
XrSystemGetInfo gi {
|
||||||
throw std::runtime_error("eglBindAPI failed");
|
.type = XR_TYPE_SYSTEM_GET_INFO,
|
||||||
|
.next = nullptr,
|
||||||
// clang-format off
|
|
||||||
EGLint ctx_attribs[] {
|
|
||||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
};
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
m_egl.context =
|
XrFormFactor factors[] {
|
||||||
eglCreateContext(m_egl.display, m_egl.config, nullptr, ctx_attribs);
|
XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY,
|
||||||
if (m_egl.context == EGL_NO_CONTEXT)
|
XR_FORM_FACTOR_HANDHELD_DISPLAY,
|
||||||
throw std::runtime_error("eglCreateContext failed");
|
};
|
||||||
|
for (auto const factor : factors) {
|
||||||
|
gi.formFactor = factor;
|
||||||
|
XrSystemId system_id;
|
||||||
|
if (xrGetSystem(*m_xr.instance, &gi, &system_id) == XR_SUCCESS) {
|
||||||
|
m_xr.system_id = system_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EGLint pb_attribs[]{EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
|
if (!m_xr.system_id) {
|
||||||
EGLSurface pb =
|
throw std::runtime_error("Failed to find valid form factor");
|
||||||
eglCreatePbufferSurface(m_egl.display, m_egl.config, pb_attribs);
|
}
|
||||||
if (pb == EGL_NO_SURFACE)
|
}
|
||||||
throw std::runtime_error("eglCreatePbufferSurface failed");
|
|
||||||
|
|
||||||
if (eglMakeCurrent(m_egl.display, pb, pb, m_egl.context) != EGL_TRUE)
|
XrGraphicsRequirementsOpenGLESKHR reqs {
|
||||||
throw std::runtime_error("eglMakeCurrent failed");
|
.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR,
|
||||||
|
.next = nullptr,
|
||||||
|
};
|
||||||
|
PFN_xrGetOpenGLESGraphicsRequirementsKHR xrGetOpenGLESGraphicsRequirementsKHR;
|
||||||
|
xrGetInstanceProcAddr(*m_xr.instance, "xrGetOpenGLESGraphicsRequirementsKHR", (PFN_xrVoidFunction*)&xrGetOpenGLESGraphicsRequirementsKHR);
|
||||||
|
if (xrGetOpenGLESGraphicsRequirementsKHR(*m_xr.instance, *m_xr.system_id, &reqs) != XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to get GLES graphics requirements");
|
||||||
|
}
|
||||||
|
printf("OpenGL ES range: %d.%d.%d – %d.%d.%d\n",
|
||||||
|
XR_VERSION_MAJOR(reqs.minApiVersionSupported),
|
||||||
|
XR_VERSION_MINOR(reqs.minApiVersionSupported),
|
||||||
|
XR_VERSION_PATCH(reqs.minApiVersionSupported),
|
||||||
|
XR_VERSION_MAJOR(reqs.maxApiVersionSupported),
|
||||||
|
XR_VERSION_MINOR(reqs.maxApiVersionSupported),
|
||||||
|
XR_VERSION_PATCH(reqs.maxApiVersionSupported));
|
||||||
|
|
||||||
std::println("GL ES version: {}",
|
glEnable(GL_DEBUG_OUTPUT_KHR);
|
||||||
reinterpret_cast<const char *>(glGetString(GL_VERSION)));
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
|
||||||
}
|
|
||||||
|
|
||||||
{ // Wayland part 2: Electric boogaloo
|
{
|
||||||
m_wayland.shm = std::make_unique<wl::Shm>(m_wayland.display);
|
XrGraphicsBindingEGLMNDX gbind = {
|
||||||
|
.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX,
|
||||||
|
.next = nullptr,
|
||||||
|
.getProcAddress = eglGetProcAddress,
|
||||||
|
.display = m_wayland.egl_display,
|
||||||
|
.config = m_wayland.egl_config,
|
||||||
|
.context = m_wayland.egl_context,
|
||||||
|
};
|
||||||
|
|
||||||
m_wayland.subcompositor = wl::subcompositor_create(m_wayland.display);
|
XrSessionCreateInfo ci {
|
||||||
if (!m_wayland.subcompositor)
|
.type = XR_TYPE_SESSION_CREATE_INFO,
|
||||||
throw std::runtime_error("Failed to create subcompositor");
|
.next = &gbind,
|
||||||
}
|
.createFlags = 0,
|
||||||
|
.systemId = *m_xr.system_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (xrCreateSession(*m_xr.instance, &ci, &m_xr.session) != XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create OpenXR session");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LunarWM::poll_events_xr() {
|
||||||
|
XrEventDataBuffer event_data{XR_TYPE_EVENT_DATA_BUFFER};
|
||||||
|
auto XrPollEvents = [&]() -> bool {
|
||||||
|
event_data = {XR_TYPE_EVENT_DATA_BUFFER};
|
||||||
|
return xrPollEvent(*m_xr.instance, &event_data) == XR_SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (XrPollEvents()) {
|
||||||
|
switch (event_data.type) {
|
||||||
|
case XR_TYPE_EVENT_DATA_EVENTS_LOST: {
|
||||||
|
XrEventDataEventsLost *eventsLost = reinterpret_cast<XrEventDataEventsLost *>(&event_data);
|
||||||
|
wlr_log(WLR_INFO, "OPENXR: Events Lost: %d", eventsLost->lostEventCount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: {
|
||||||
|
XrEventDataInstanceLossPending *instanceLossPending = reinterpret_cast<XrEventDataInstanceLossPending *>(&event_data);
|
||||||
|
wlr_log(WLR_INFO, "OPENXR: Instance Loss Pending at: %ld", instanceLossPending->lossTime);
|
||||||
|
m_session_running = false;
|
||||||
|
m_running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: {
|
||||||
|
XrEventDataInteractionProfileChanged *interactionProfileChanged = reinterpret_cast<XrEventDataInteractionProfileChanged *>(&event_data);
|
||||||
|
wlr_log(WLR_INFO, "OPENXR: Interaction Profile changed for Session: %p", interactionProfileChanged->session);
|
||||||
|
if (interactionProfileChanged->session != m_xr.session) {
|
||||||
|
wlr_log(WLR_ERROR, "XrEventDataInteractionProfileChanged for unknown Session");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: {
|
||||||
|
XrEventDataReferenceSpaceChangePending *referenceSpaceChangePending = reinterpret_cast<XrEventDataReferenceSpaceChangePending *>(&event_data);
|
||||||
|
wlr_log(WLR_INFO, "OPENXR: Reference Space Change pending for Session: %p", referenceSpaceChangePending->session);
|
||||||
|
if (referenceSpaceChangePending->session != m_xr.session) {
|
||||||
|
wlr_log(WLR_ERROR, "XrEventDataReferenceSpaceChangePending for unknown Session");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
|
||||||
|
XrEventDataSessionStateChanged *sessionStateChanged = reinterpret_cast<XrEventDataSessionStateChanged *>(&event_data);
|
||||||
|
if (sessionStateChanged->session != m_xr.session) {
|
||||||
|
wlr_log(WLR_ERROR, "XrEventDataSessionStateChanged for unknown Session");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessionStateChanged->state == XR_SESSION_STATE_READY) {
|
||||||
|
XrSessionBeginInfo sessionBeginInfo{XR_TYPE_SESSION_BEGIN_INFO};
|
||||||
|
sessionBeginInfo.primaryViewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
||||||
|
if (xrBeginSession(m_xr.session, &sessionBeginInfo) != XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to begin session");
|
||||||
|
}
|
||||||
|
m_session_running = true;
|
||||||
|
}
|
||||||
|
if (sessionStateChanged->state == XR_SESSION_STATE_STOPPING) {
|
||||||
|
if (xrEndSession(m_xr.session) != XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to end session");
|
||||||
|
}
|
||||||
|
m_session_running = false;
|
||||||
|
}
|
||||||
|
if (sessionStateChanged->state == XR_SESSION_STATE_EXITING) {
|
||||||
|
m_session_running = false;
|
||||||
|
m_running = false;
|
||||||
|
}
|
||||||
|
if (sessionStateChanged->state == XR_SESSION_STATE_LOSS_PENDING) {
|
||||||
|
m_session_running = false;
|
||||||
|
m_running = false;
|
||||||
|
}
|
||||||
|
m_xr.session_state = sessionStateChanged->state;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LunarWM::~LunarWM() {
|
LunarWM::~LunarWM() {
|
||||||
{ // Wayland second initialization block
|
assert(m_initialized);
|
||||||
if (m_wayland.subcompositor)
|
|
||||||
wl_global_destroy(m_wayland.subcompositor);
|
puts("1");
|
||||||
if (m_wayland.shm)
|
|
||||||
m_wayland.shm.reset();
|
if (m_xr.session != XR_NULL_HANDLE) {
|
||||||
|
xrDestroySession(m_xr.session);
|
||||||
|
}
|
||||||
|
puts("2");
|
||||||
|
|
||||||
|
if (m_xr.instance) {
|
||||||
|
xrDestroyInstance(*m_xr.instance);
|
||||||
|
}
|
||||||
|
puts("3");
|
||||||
|
|
||||||
|
wl_list_remove(&m_wayland.keyboards);
|
||||||
|
|
||||||
|
puts("4");
|
||||||
|
wl_display_destroy_clients(m_wayland.display);
|
||||||
|
if (!m_wayland.allocator) {
|
||||||
|
wlr_allocator_destroy(m_wayland.allocator);
|
||||||
}
|
}
|
||||||
{ // EGL
|
puts("5");
|
||||||
if (m_egl.display != EGL_NO_DISPLAY)
|
if (!m_wayland.renderer) {
|
||||||
eglDestroyContext(m_egl.display, m_egl.context);
|
wlr_renderer_destroy(m_wayland.renderer);
|
||||||
}
|
}
|
||||||
{ // Wayland
|
puts("6");
|
||||||
if (m_wayland.display)
|
if (!m_wayland.backend) {
|
||||||
wl_display_destroy(m_wayland.display);
|
wlr_backend_destroy(m_wayland.backend);
|
||||||
|
}
|
||||||
|
puts("7");
|
||||||
|
if (!m_wayland.display) {
|
||||||
|
wl_display_destroy(m_wayland.display);
|
||||||
}
|
}
|
||||||
std::println("bai bai~!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LunarWM::run() {
|
void LunarWM::run() {
|
||||||
std::println("Running wayland compositor on WAYLAND_DISPLAY={}.",
|
if (!wlr_backend_start(m_wayland.backend)) {
|
||||||
m_wayland.socket);
|
throw std::runtime_error("Failed to start backend");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const *socket = wl_display_add_socket_auto(m_wayland.display);
|
||||||
|
if (!socket) {
|
||||||
|
throw std::runtime_error("Failed to add wayland socket to display");
|
||||||
|
}
|
||||||
|
|
||||||
|
setenv("WAYLAND_DISPLAY", socket, true);
|
||||||
|
wlr_log(WLR_INFO, "Running compositor on WAYLAND_DISPLAY=%s", socket);
|
||||||
|
|
||||||
|
m_running = true;
|
||||||
while (m_running) {
|
while (m_running) {
|
||||||
if (wl_event_loop_dispatch(m_wayland.event_loop, 0) < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
wl_display_flush_clients(m_wayland.display);
|
wl_display_flush_clients(m_wayland.display);
|
||||||
|
wl_event_loop_dispatch(m_wayland.event_loop, 0);
|
||||||
|
|
||||||
|
poll_events_xr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LunarWM::terminate() { m_running = false; }
|
void LunarWM::terminate() {
|
||||||
|
wlr_log(WLR_INFO, "Stopping compositor");
|
||||||
|
m_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace LunarWM
|
} // namespace LunarWM
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ std::unique_ptr<LunarWM::LunarWM> g_comp;
|
|||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
g_comp = std::make_unique<LunarWM::LunarWM>();
|
g_comp = std::make_unique<LunarWM::LunarWM>();
|
||||||
|
g_comp->init();
|
||||||
std::signal(SIGINT, [](int) { g_comp->terminate(); });
|
std::signal(SIGINT, [](int) { g_comp->terminate(); });
|
||||||
g_comp->run();
|
g_comp->run();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <wayland-server.h>
|
|
||||||
|
|
||||||
export module LunarWM.wl.Compositor;
|
|
||||||
|
|
||||||
import LunarWM.wl.Region;
|
|
||||||
|
|
||||||
namespace LunarWM {
|
|
||||||
|
|
||||||
namespace wl {
|
|
||||||
|
|
||||||
void create_surface(wl_client *client, wl_resource *resource, uint32_t id) {
|
|
||||||
// FIXME: Implement.
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_region(wl_client *client, wl_resource *resource, uint32_t id) {
|
|
||||||
if (!Region::make(client, wl_resource_get_version(resource), id))
|
|
||||||
wl_resource_post_no_memory(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_compositor_interface const compositor_impl{
|
|
||||||
.create_surface = create_surface,
|
|
||||||
.create_region = create_region,
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace wl
|
|
||||||
|
|
||||||
} // namespace LunarWM
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <wayland-server.h>
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
export module LunarWM.wl.Region;
|
|
||||||
|
|
||||||
import std;
|
|
||||||
|
|
||||||
import LunarWM.Math;
|
|
||||||
|
|
||||||
namespace LunarWM {
|
|
||||||
|
|
||||||
namespace wl {
|
|
||||||
|
|
||||||
export struct Region {
|
|
||||||
Math::Box<int32_t> extents;
|
|
||||||
std::vector<Math::Rect<int32_t>> rects;
|
|
||||||
|
|
||||||
Region() = default;
|
|
||||||
Region(int32_t x, int32_t y, int32_t width, int32_t height) {
|
|
||||||
union_rect(x, y, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
static wl_resource *make(wl_client *client, uint32_t version, uint32_t id);
|
|
||||||
|
|
||||||
void union_rect(int32_t x, int32_t y, int32_t width, int32_t height);
|
|
||||||
};
|
|
||||||
|
|
||||||
Region operator-(Region const &lhs, Region const &rhs) {
|
|
||||||
Region result;
|
|
||||||
|
|
||||||
for (const auto &src_rect : lhs.rects) {
|
|
||||||
std::vector<Math::Rect<int32_t>> fragments = {src_rect};
|
|
||||||
|
|
||||||
// Subtract every rhs rectangle
|
|
||||||
for (const auto &clip_rect : rhs.rects) {
|
|
||||||
std::vector<Math::Rect<int32_t>> new_fragments;
|
|
||||||
for (const auto &frag : fragments) {
|
|
||||||
auto pieces = Math::subtract_rect(frag, clip_rect);
|
|
||||||
new_fragments.insert(new_fragments.end(), pieces.begin(), pieces.end());
|
|
||||||
}
|
|
||||||
fragments = std::move(new_fragments);
|
|
||||||
if (fragments.empty())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &frag : fragments) {
|
|
||||||
result.union_rect(frag.x(), frag.y(), frag.w(), frag.h());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Region::union_rect(int32_t x, int32_t y, int32_t width, int32_t height) {
|
|
||||||
Math::Rect<int32_t> new_rect(x, y, width, height);
|
|
||||||
|
|
||||||
rects.push_back(new_rect);
|
|
||||||
|
|
||||||
if (rects.size() == 1) {
|
|
||||||
extents = Math::Box(new_rect);
|
|
||||||
} else {
|
|
||||||
extents.left() = std::min(extents.left(), new_rect.left());
|
|
||||||
extents.top() = std::min(extents.top(), new_rect.top());
|
|
||||||
extents.right() = std::max(extents.right(), new_rect.right());
|
|
||||||
extents.bottom() = std::max(extents.bottom(), new_rect.bottom());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wl_add(wl_client *client, wl_resource *resource, int32_t x, int32_t y,
|
|
||||||
int32_t width, int32_t height) {
|
|
||||||
auto region = reinterpret_cast<Region *>(wl_resource_get_user_data(resource));
|
|
||||||
region->union_rect(x, y, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wl_subtract(wl_client *client, wl_resource *resource, int32_t x, int32_t y,
|
|
||||||
int32_t width, int32_t height) {
|
|
||||||
auto region = reinterpret_cast<Region *>(wl_resource_get_user_data(resource));
|
|
||||||
*region = *region - Region(x, y, width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_region_interface const region_impl{
|
|
||||||
.destroy = resource_destroy,
|
|
||||||
.add = wl_add,
|
|
||||||
.subtract = wl_subtract,
|
|
||||||
};
|
|
||||||
|
|
||||||
void region_destroy(wl_resource *resource) {
|
|
||||||
auto region = reinterpret_cast<Region *>(wl_resource_get_user_data(resource));
|
|
||||||
delete region;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_resource *Region::make(wl_client *client, uint32_t version, uint32_t id) {
|
|
||||||
Region *region = new Region();
|
|
||||||
if (!region)
|
|
||||||
throw std::runtime_error("Out of memory");
|
|
||||||
wl_resource *res =
|
|
||||||
wl_resource_create(client, &wl_region_interface, version, id);
|
|
||||||
if (!res) {
|
|
||||||
free(region);
|
|
||||||
throw std::runtime_error("Failed to create wayland resource");
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_resource_set_implementation(res, ®ion_impl, region, ®ion_destroy);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace wl
|
|
||||||
|
|
||||||
} // namespace LunarWM
|
|
||||||
245
src/wl/Shm.cppm
245
src/wl/Shm.cppm
@@ -1,245 +0,0 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstring>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <wayland-server.h>
|
|
||||||
|
|
||||||
export module LunarWM.wl.Shm;
|
|
||||||
|
|
||||||
import std;
|
|
||||||
|
|
||||||
namespace LunarWM {
|
|
||||||
|
|
||||||
namespace wl {
|
|
||||||
|
|
||||||
export struct Shm {
|
|
||||||
Shm(wl_display *display);
|
|
||||||
~Shm();
|
|
||||||
|
|
||||||
wl_global *global = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace wl
|
|
||||||
|
|
||||||
} // namespace LunarWM
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using LunarWM::wl::Shm;
|
|
||||||
|
|
||||||
struct Pool {
|
|
||||||
wl_resource *res = nullptr;
|
|
||||||
Shm *shm = nullptr;
|
|
||||||
void *data = nullptr;
|
|
||||||
size_t size = 0;
|
|
||||||
unsigned refs = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void unref_pool(Pool *p) {
|
|
||||||
if (--p->refs == 0) {
|
|
||||||
if (p->data && p->data != MAP_FAILED)
|
|
||||||
::munmap(p->data, p->size);
|
|
||||||
delete p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Buffer {
|
|
||||||
wl_resource *res = nullptr;
|
|
||||||
Pool *pool = nullptr;
|
|
||||||
GLuint tex = 0;
|
|
||||||
int32_t w = 0;
|
|
||||||
int32_t h = 0;
|
|
||||||
int32_t stride = 0;
|
|
||||||
uint32_t fmt = WL_SHM_FORMAT_ARGB8888;
|
|
||||||
int32_t off = 0;
|
|
||||||
|
|
||||||
void *pixels() const { return static_cast<uint8_t *>(pool->data) + off; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __NetBSD__
|
|
||||||
void *remap(void *oldp, size_t oldsz, size_t newsz) {
|
|
||||||
return ::mremap(oldp, oldsz, nullptr, newsz, 0);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void *remap(void *oldp, size_t oldsz, size_t newsz) {
|
|
||||||
return ::mremap(oldp, oldsz, newsz, MREMAP_MAYMOVE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void buf_req_destroy(wl_client *, wl_resource *r) { ::wl_resource_destroy(r); }
|
|
||||||
|
|
||||||
void buf_resource_destroy(wl_resource *r) {
|
|
||||||
auto *b = static_cast<Buffer *>(::wl_resource_get_user_data(r));
|
|
||||||
|
|
||||||
if (b->tex)
|
|
||||||
::glDeleteTextures(1, &b->tex);
|
|
||||||
|
|
||||||
unref_pool(b->pool);
|
|
||||||
delete b;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wl_buffer_interface buffer_impl{.destroy = buf_req_destroy};
|
|
||||||
|
|
||||||
void pool_req_destroy(wl_client *, wl_resource *r) { ::wl_resource_destroy(r); }
|
|
||||||
|
|
||||||
void pool_resource_destroy(wl_resource *r) {
|
|
||||||
auto *p = static_cast<Pool *>(::wl_resource_get_user_data(r));
|
|
||||||
unref_pool(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pool_req_resize(wl_client *, wl_resource *r, int32_t new_sz) {
|
|
||||||
auto *p = static_cast<Pool *>(::wl_resource_get_user_data(r));
|
|
||||||
|
|
||||||
void *d = remap(p->data, p->size, new_sz);
|
|
||||||
if (d == MAP_FAILED) {
|
|
||||||
::wl_resource_post_error(r, WL_SHM_ERROR_INVALID_FD, "mremap failed: %s",
|
|
||||||
::strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
p->data = d;
|
|
||||||
p->size = new_sz;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pool_req_create_buf(wl_client *client, wl_resource *pool_res, uint32_t id,
|
|
||||||
int32_t off, int32_t w, int32_t h, int32_t stride,
|
|
||||||
uint32_t fmt) {
|
|
||||||
auto *p = static_cast<Pool *>(::wl_resource_get_user_data(pool_res));
|
|
||||||
|
|
||||||
if (off < 0 || static_cast<size_t>(off) > p->size) {
|
|
||||||
::wl_resource_post_error(pool_res, WL_SHM_ERROR_INVALID_STRIDE,
|
|
||||||
"offset out of bounds");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *b = new Buffer{};
|
|
||||||
if (!b) {
|
|
||||||
::wl_resource_post_no_memory(pool_res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
b->pool = p;
|
|
||||||
b->w = w;
|
|
||||||
b->h = h;
|
|
||||||
b->stride = stride;
|
|
||||||
b->fmt = fmt;
|
|
||||||
b->off = off;
|
|
||||||
++p->refs;
|
|
||||||
|
|
||||||
b->res = ::wl_resource_create(client, &wl_buffer_interface, 1, id);
|
|
||||||
if (!b->res) {
|
|
||||||
::wl_resource_post_no_memory(pool_res);
|
|
||||||
unref_pool(p);
|
|
||||||
delete b;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
::wl_resource_set_implementation(b->res, &buffer_impl, b,
|
|
||||||
&buf_resource_destroy);
|
|
||||||
|
|
||||||
::glGenTextures(1, &b->tex);
|
|
||||||
::glBindTexture(GL_TEXTURE_2D, b->tex);
|
|
||||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
||||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
|
||||||
|
|
||||||
#ifndef GL_BGRA_EXT
|
|
||||||
#define GL_BGRA_EXT 0x80E1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
GLenum pfmt = (fmt == WL_SHM_FORMAT_XRGB8888 || fmt == WL_SHM_FORMAT_ARGB8888)
|
|
||||||
? GL_BGRA_EXT
|
|
||||||
: GL_RGBA; // fallback
|
|
||||||
|
|
||||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, pfmt, GL_UNSIGNED_BYTE,
|
|
||||||
b->pixels());
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wl_shm_pool_interface shm_pool_impl{
|
|
||||||
.create_buffer = pool_req_create_buf,
|
|
||||||
.destroy = pool_req_destroy,
|
|
||||||
.resize = pool_req_resize,
|
|
||||||
};
|
|
||||||
|
|
||||||
void create_pool(wl_client *client, wl_resource *res, uint32_t id, int32_t fd,
|
|
||||||
int32_t size) {
|
|
||||||
auto *shm = static_cast<Shm *>(::wl_resource_get_user_data(res));
|
|
||||||
|
|
||||||
auto *p = new Pool{};
|
|
||||||
if (!p) {
|
|
||||||
::wl_resource_post_no_memory(res);
|
|
||||||
::close(fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->shm = shm;
|
|
||||||
|
|
||||||
p->res = ::wl_resource_create(client, &wl_shm_pool_interface,
|
|
||||||
::wl_resource_get_version(res), id);
|
|
||||||
if (!p->res) {
|
|
||||||
::wl_resource_post_no_memory(res);
|
|
||||||
delete p;
|
|
||||||
::close(fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
::wl_resource_set_implementation(p->res, &shm_pool_impl, p,
|
|
||||||
&pool_resource_destroy);
|
|
||||||
|
|
||||||
p->data = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (p->data == MAP_FAILED) {
|
|
||||||
::wl_resource_post_error(res, WL_SHM_ERROR_INVALID_FD, "mmap failed: %s",
|
|
||||||
::strerror(errno));
|
|
||||||
::wl_resource_destroy(p->res);
|
|
||||||
delete p;
|
|
||||||
::close(fd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->size = size;
|
|
||||||
::close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wl_shm_interface shm_impl{
|
|
||||||
.create_pool = create_pool,
|
|
||||||
};
|
|
||||||
|
|
||||||
void bind_shm(wl_client *client, void *data, uint32_t version, uint32_t id) {
|
|
||||||
auto *shm = static_cast<Shm *>(data);
|
|
||||||
|
|
||||||
wl_resource *r = ::wl_resource_create(client, &wl_shm_interface, version, id);
|
|
||||||
if (!r) {
|
|
||||||
::wl_client_post_no_memory(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
::wl_resource_set_implementation(r, &shm_impl, shm, nullptr);
|
|
||||||
|
|
||||||
::wl_shm_send_format(r, WL_SHM_FORMAT_XRGB8888);
|
|
||||||
::wl_shm_send_format(r, WL_SHM_FORMAT_ARGB8888);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace LunarWM {
|
|
||||||
|
|
||||||
namespace wl {
|
|
||||||
|
|
||||||
Shm::Shm(wl_display *display) {
|
|
||||||
this->global =
|
|
||||||
::wl_global_create(display, &wl_shm_interface, 1, this, &bind_shm);
|
|
||||||
if (!this->global)
|
|
||||||
throw std::runtime_error("wl_global_create failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
Shm::~Shm() {
|
|
||||||
if (this->global)
|
|
||||||
::wl_global_destroy(this->global);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace wl
|
|
||||||
|
|
||||||
} // namespace LunarWM
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <wayland-server.h>
|
|
||||||
|
|
||||||
export module LunarWM.wl.Subcompositor;
|
|
||||||
|
|
||||||
import LunarWM.wl.Subsurface;
|
|
||||||
|
|
||||||
namespace LunarWM {
|
|
||||||
|
|
||||||
namespace wl {
|
|
||||||
|
|
||||||
export wl_global *subcompositor_create(struct wl_display *display);
|
|
||||||
|
|
||||||
void get_subsurface(wl_client *client, wl_resource *res, uint32_t id,
|
|
||||||
wl_resource *surface, wl_resource *parent) {
|
|
||||||
auto subsurface = Subsurface::make(client, wl_resource_get_version(res), id);
|
|
||||||
if (!subsurface) {
|
|
||||||
wl_resource_post_no_memory(res);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_subcompositor_interface const subcompositor_impl = {
|
|
||||||
.destroy = resource_destroy,
|
|
||||||
.get_subsurface = get_subsurface,
|
|
||||||
};
|
|
||||||
|
|
||||||
void bind_subcompositor(wl_client *client, void *data, uint32_t version,
|
|
||||||
uint32_t id) {
|
|
||||||
struct wl_resource *resource;
|
|
||||||
|
|
||||||
resource =
|
|
||||||
wl_resource_create(client, &wl_subcompositor_interface, version, id);
|
|
||||||
if (!resource) {
|
|
||||||
wl_client_post_no_memory(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wl_resource_set_implementation(resource, &subcompositor_impl, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_global *subcompositor_create(struct wl_display *display) {
|
|
||||||
return wl_global_create(display, &wl_subcompositor_interface, 1, NULL,
|
|
||||||
&bind_subcompositor);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace wl
|
|
||||||
|
|
||||||
} // namespace LunarWM
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
module;
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <wayland-server.h>
|
|
||||||
|
|
||||||
export module LunarWM.wl.Subsurface;
|
|
||||||
|
|
||||||
import std;
|
|
||||||
|
|
||||||
namespace LunarWM {
|
|
||||||
|
|
||||||
namespace wl {
|
|
||||||
|
|
||||||
export struct Subsurface {
|
|
||||||
Subsurface() = delete;
|
|
||||||
|
|
||||||
static Subsurface *make(wl_client *client, uint32_t version, uint32_t id);
|
|
||||||
static void destroy(wl_resource *res);
|
|
||||||
|
|
||||||
struct wl_resource *resource;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_position(wl_client *client, wl_resource *resource, int32_t x,
|
|
||||||
int32_t y) {
|
|
||||||
// TODO: Implement.
|
|
||||||
}
|
|
||||||
|
|
||||||
void place_above(wl_client *client, wl_resource *resource,
|
|
||||||
wl_resource *sibling_resource) {
|
|
||||||
// TODO: Implement.
|
|
||||||
}
|
|
||||||
|
|
||||||
void place_below(wl_client *client, wl_resource *resource,
|
|
||||||
wl_resource *sibling_resource) {
|
|
||||||
// TODO: Implement.
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_sync(wl_client *client, wl_resource *resource) {
|
|
||||||
// TODO: Implement.
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_desync(wl_client *client, wl_resource *resource) {
|
|
||||||
// TODO: Implement.
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_subsurface_interface const subsurface_impl{
|
|
||||||
.destroy = resource_destroy,
|
|
||||||
.set_position = set_position,
|
|
||||||
.place_above = place_above,
|
|
||||||
.place_below = place_below,
|
|
||||||
.set_sync = set_sync,
|
|
||||||
.set_desync = set_desync,
|
|
||||||
};
|
|
||||||
|
|
||||||
void Subsurface::destroy(wl_resource *res) {
|
|
||||||
free(wl_resource_get_user_data(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
Subsurface *Subsurface::make(wl_client *client, uint32_t version, uint32_t id) {
|
|
||||||
Subsurface *subsurface =
|
|
||||||
reinterpret_cast<Subsurface *>(malloc(sizeof(Subsurface)));
|
|
||||||
subsurface->resource =
|
|
||||||
wl_resource_create(client, &wl_subsurface_interface, version, id);
|
|
||||||
if (!subsurface->resource) {
|
|
||||||
free(subsurface);
|
|
||||||
throw std::runtime_error("wl_resource_create(subsurface) failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_resource_set_implementation(subsurface->resource, &subsurface_impl,
|
|
||||||
subsurface, &Subsurface::destroy);
|
|
||||||
return subsurface;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace wl
|
|
||||||
|
|
||||||
} // namespace LunarWM
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#include <wayland-server.h>
|
|
||||||
|
|
||||||
static void resource_destroy(struct wl_client *client,
|
|
||||||
struct wl_resource *resource) {
|
|
||||||
wl_resource_destroy(resource);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user