diff --git a/CMakeLists.txt b/CMakeLists.txt index 524bace..f8ffe0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,26 @@ cmake_minimum_required(VERSION 3.12) project(lunarwm VERSION 0.0.1 LANGUAGES C CXX) +execute_process(COMMAND pkg-config --variable=pkgdatadir wayland-protocols + OUTPUT_VARIABLE WAYLAND_PROTOCOLS + OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND pkg-config --variable=wayland_scanner wayland-scanner + OUTPUT_VARIABLE WAYLAND_SCANNER + OUTPUT_STRIP_TRAILING_WHITESPACE) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-protocol.h + COMMAND ${WAYLAND_SCANNER} server-header + ${WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml + ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-protocol.h + MAIN_DEPENDENCY ${WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml + COMMENT "Generating xdg-shell-protocol.h" +) + +add_custom_target(xdg_shell_proto + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-protocol.h +) + set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -28,28 +48,32 @@ pkg_check_modules(X11 REQUIRED x11) pkg_check_modules(wayland REQUIRED wayland-server wayland-protocols) include_directories( - ${wayland_INCLUDE_DIRS} - ${X11_INCLUDE_DIRS} - ${GLM_INCLUDE_DIRS} - ${Wlroots_INCLUDE_DIRS} + ${wayland_INCLUDE_DIRS} + ${X11_INCLUDE_DIRS} + ${GLM_INCLUDE_DIRS} + ${Wlroots_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} ) set(SOURCES - src/wlr/backend.cpp - src/wlr/output.cpp - src/wlr/openxr_gl.cpp + src/wlr/backend.cpp + src/wlr/output.cpp + src/wlr/openxr_gl.cpp - src/compositor.cpp - src/main.cpp + src/compositor.cpp + src/main.cpp + + ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-protocol.h ) add_executable(${PROJECT_NAME} ${SOURCES}) target_link_libraries(${PROJECT_NAME} - OpenGL::GL - openxr_loader - ${wayland_LIBRARIES} - ${X11_LIBRARIES} - ${GLM_LIBRARIES} - ${Wlroots_LIBRARIES} + OpenGL::GL + openxr_loader + ${wayland_LIBRARIES} + ${X11_LIBRARIES} + ${GLM_LIBRARIES} + ${Wlroots_LIBRARIES} ) + diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..e9f27e4 --- /dev/null +++ b/protocol/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,407 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (layer, size, anchor, exclusive zone, + margin, interactivity) is double-buffered, and will be applied at the + time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthogonal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area with other + surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + + + + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Keyboard interactivity is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + + + + + Requests an edge for the exclusive zone to apply. The exclusive + edge will be automatically deduced from anchor points when possible, + but when the surface is anchored to a corner, it will be necessary + to set it explicitly to disambiguate, as it is not possible to deduce + which one of the two corner edges should be used. + + The edge must be one the surface is anchored to, otherwise the + invalid_exclusive_edge protocol error will be raised. + + + + + diff --git a/src/compositor.cpp b/src/compositor.cpp index 5f2f726..189f765 100644 --- a/src/compositor.cpp +++ b/src/compositor.cpp @@ -1,230 +1,109 @@ #include "compositor.h" -#include "common.h" #include -// wlroots is a C library; wrap headers in extern "C" extern "C" { #include +#include #include + +wlr_scene* wlr_scene_create(); } -#include "wlr/openxr_gl.h" #include -#include #include -#include #include #include #include #include -void Compositor::create_instance() +void handle_new_output(struct wl_listener* l, void* d) { - // Wow, can't believe this compositor uses AI! - XrApplicationInfo ai { - .applicationName = {}, - .applicationVersion = 1, - .engineName = {}, - .engineVersion = 1, - .apiVersion = XR_API_VERSION_1_0, - }; - - std::strncpy( - ai.applicationName, "LunarWM", XR_MAX_APPLICATION_NAME_SIZE - 1); - std::strncpy(ai.engineName, "LunarWM Engine", XR_MAX_ENGINE_NAME_SIZE - 1); - - std::vector const instance_extensions { - XR_EXT_DEBUG_UTILS_EXTENSION_NAME, - XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, - }; - - std::vector extension_props; - { // Populate extension_props - uint32_t len = 0; - OPENXR_CHECK( - xrEnumerateInstanceExtensionProperties, nullptr, 0, &len, nullptr); - extension_props.resize(len, { XR_TYPE_EXTENSION_PROPERTIES }); - OPENXR_CHECK(xrEnumerateInstanceExtensionProperties, nullptr, len, &len, - extension_props.data()); - } - - // Check that we have all our requested extensions available - for (auto const& requested_instance_extension : instance_extensions) { - bool found = false; - - for (auto const& eprop : extension_props) { - if (strcmp(requested_instance_extension, eprop.extensionName) - == 0) { - found = true; - break; - } - } - - if (!found) { - throw std::runtime_error(std::format( - "Failed to find extension: {}", requested_instance_extension)); - } - } - - { // Create our instance - XrInstanceCreateInfo ci { - .type = XR_TYPE_INSTANCE_CREATE_INFO, - .next = nullptr, - .createFlags = 0, - .applicationInfo = ai, - .enabledApiLayerCount = 0, - .enabledApiLayerNames = nullptr, - .enabledExtensionCount - = static_cast(instance_extensions.size()), - .enabledExtensionNames = instance_extensions.data(), - }; - - OPENXR_CHECK(xrCreateInstance, &ci, &m_xr_instance); - } + (void)l; + (void)d; } -void Compositor::destroy_instance() +void handle_new_input(struct wl_listener* l, void* d) { - OPENXR_CHECK(xrDestroyInstance, m_xr_instance); + (void)l; + (void)d; } -void Compositor::create_session() +void handle_xdg_toplevel(struct wl_listener* l, void* d) { - XrSystemId system_id; + (void)l; + (void)d; +} - { // Get system ID. - XrSystemGetInfo system_gi { XR_TYPE_SYSTEM_GET_INFO }; - system_gi.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; - OPENXR_CHECK(xrGetSystem, m_xr_instance, &system_gi, &system_id); +void handle_xdg_popup(struct wl_listener* l, void* d) +{ + (void)l; + (void)d; +} - // NOTE: Might be useful later. - XrSystemProperties system_properties = { XR_TYPE_SYSTEM_PROPERTIES }; - OPENXR_CHECK(xrGetSystemProperties, m_xr_instance, system_id, - &system_properties); - } - - { // Get GLX graphics requirements - PFN_xrGetOpenGLGraphicsRequirementsKHR xr_get_gl_req = nullptr; - xrGetInstanceProcAddr(m_xr_instance, - "xrGetOpenGLGraphicsRequirementsKHR", - reinterpret_cast(&xr_get_gl_req)); - - XrGraphicsRequirementsOpenGLKHR gl_req { - XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR - }; - OPENXR_CHECK(xr_get_gl_req, m_xr_instance, system_id, &gl_req); - - std::println("OpenGL requirements: min=0x{:X}, max=0x{:X}", - gl_req.minApiVersionSupported, gl_req.maxApiVersionSupported); - } - - XrGraphicsBindingOpenGLXlibKHR graphics_binding {}; - - { // Xlib stuff - Display* display = XOpenDisplay(nullptr); - if (!display) - throw std::runtime_error("Failed to open X display"); - - int screen = DefaultScreen(display); - - static int visual_attribs[] = { - 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* fbc - = glXChooseFBConfig(display, screen, visual_attribs, &fbcount); - if (!fbc) - throw std::runtime_error("No GLXFBConfig found"); - - // just grab first config - GLXFBConfig best_fbc = fbc[0]; - XFree(fbc); - - XVisualInfo* vi = glXGetVisualFromFBConfig(display, best_fbc); - if (!vi) - throw std::runtime_error("Failed to get XVisualInfo"); - - Window root = RootWindow(display, screen); - - XSetWindowAttributes swa; - swa.colormap = XCreateColormap(display, root, vi->visual, AllocNone); - swa.event_mask = ExposureMask | KeyPressMask; - - Window win = XCreateWindow(display, root, 0, 0, 800, 600, 0, vi->depth, - InputOutput, vi->visual, CWColormap | CWEventMask, &swa); - XMapWindow(display, win); - XStoreName(display, win, "LunarWM OpenXR"); - - GLXContext glc = glXCreateNewContext( - display, best_fbc, GLX_RGBA_TYPE, nullptr, True); - if (!glc) - throw std::runtime_error("Failed to create GLX context"); - - if (!glXMakeContextCurrent(display, win, win, glc)) - throw std::runtime_error("glXMakeContextCurrent failed"); - - // fill in OpenXR graphics binding - graphics_binding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR; - graphics_binding.next = nullptr; - graphics_binding.xDisplay = display; - graphics_binding.visualid = vi->visualid; - graphics_binding.glxFBConfig = best_fbc; - graphics_binding.glxDrawable = win; - graphics_binding.glxContext = glc; - } - - { // Create session - XrSessionCreateInfo ci { XR_TYPE_SESSION_CREATE_INFO }; - ci.next = &graphics_binding; - ci.systemId = system_id; - ci.createFlags = 0; - OPENXR_CHECK(xrCreateSession, m_xr_instance, &ci, &m_session); - } - // --- wlroots integration: create Wayland display and OpenXR wlroots - // backend --- initialize wlroots logging +Compositor::Compositor() +{ + // init logging + display + event loop wlr_log_init(WLR_DEBUG, nullptr); - // create Wayland display m_display = wl_display_create(); - if (!m_display) { - throw std::runtime_error("Failed to create Wayland display"); - } - // get event loop m_event_loop = wl_display_get_event_loop(m_display); - if (!m_event_loop) { - wl_display_destroy(m_display); - throw std::runtime_error("Failed to get Wayland event loop"); - } - // create the OpenXR wlroots backend + + // openxr backend m_backend = wlr_openxr_backend_create(m_display, m_event_loop); - if (!m_backend || !wlr_backend_is_openxr(m_backend)) { - wl_display_destroy(m_display); - throw std::runtime_error("Failed to create OpenXR wlroots backend"); - } + 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); } -void Compositor::destroy_session() +Compositor::~Compositor() { - // destroy wlroots backend and Wayland display if (m_backend) { wlr_backend_destroy(m_backend); m_backend = nullptr; @@ -233,70 +112,23 @@ void Compositor::destroy_session() wl_display_destroy(m_display); m_display = nullptr; } - // destroy OpenXR session - OPENXR_CHECK(xrDestroySession, m_session); -} - -void Compositor::poll_events() -{ - XrEventDataBuffer event_data { XR_TYPE_EVENT_DATA_BUFFER }; - auto xr_poll_events = [&]() -> bool { - event_data = { XR_TYPE_EVENT_DATA_BUFFER }; - return xrPollEvent(m_xr_instance, &event_data) == XR_SUCCESS; - }; - - while (xr_poll_events()) { - switch (event_data.type) { - case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { - XrEventDataSessionStateChanged* session_state_changed - = reinterpret_cast( - &event_data); - if (session_state_changed->session != m_session) { - std::println(std::cerr, - "XrEventDataSessionStateChanged for unknown Session"); - break; - } - if (session_state_changed->state == XR_SESSION_STATE_READY) { - XrSessionBeginInfo session_begin_info { - XR_TYPE_SESSION_BEGIN_INFO - }; - session_begin_info.primaryViewConfigurationType - = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; - OPENXR_CHECK(xrBeginSession, m_session, &session_begin_info); - m_session_running = true; - } - if (session_state_changed->state == XR_SESSION_STATE_STOPPING) { - OPENXR_CHECK(xrEndSession, m_session); - m_session_running = false; - } - if (session_state_changed->state == XR_SESSION_STATE_EXITING - || session_state_changed->state - == XR_SESSION_STATE_LOSS_PENDING) { - m_session_running = false; - m_running = false; - } - m_session_state = session_state_changed->state; - break; - } - default: - break; - } - } } void Compositor::run() { - // start the wlroots backend (this also begins XrSession) + 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"); } - while (m_running) { - this->poll_events(); - // dispatch Wayland events - wl_event_loop_dispatch(m_event_loop, 0); - if (m_session_running) { - // TODO: Render frame (per-eye rendering via OpenXR swapchain) - } - } + setenv("WAYLAND_DISPLAY", socket, true); + + wlr_log( + WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); + + wl_display_run(m_display); } diff --git a/src/compositor.h b/src/compositor.h index 90569d9..2847749 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -1,46 +1,49 @@ #pragma once -#include +#include +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +} +#include "wlr/openxr_gl.h" -// Forward declarations for wlroots integration -struct wl_display; -struct wl_event_loop; -struct wlr_backend; +struct wlr_scene; struct Compositor { - Compositor() - { - this->create_instance(); - this->create_session(); - } - - ~Compositor() - { - this->destroy_session(); - this->destroy_instance(); - } + Compositor(); + ~Compositor(); void run(); - void stop() { m_running = false; } + void stop() { wl_display_terminate(m_display); } + + wl_listener m_new_output; private: - void create_instance(); - void destroy_instance(); - - void create_session(); - void destroy_session(); - - void poll_events(); - - bool m_running = true; - - // OpenXR related stuff - XrInstance m_xr_instance; - bool m_session_running = false; - XrSession m_session = {}; - XrSessionState m_session_state = XR_SESSION_STATE_UNKNOWN; // 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; }; diff --git a/src/wlr/openxr_gl.cpp b/src/wlr/openxr_gl.cpp index d620393..8af3070 100644 --- a/src/wlr/openxr_gl.cpp +++ b/src/wlr/openxr_gl.cpp @@ -148,7 +148,7 @@ static bool backend_start(wlr_backend* backend) ai.applicationVersion = 1; std::strncpy(ai.engineName, "LunarWM", XR_MAX_ENGINE_NAME_SIZE - 1); ai.engineVersion = 1; - ai.apiVersion = XR_CURRENT_API_VERSION; + ai.apiVersion = XR_API_VERSION_1_0; char const* exts[] = { XR_EXT_DEBUG_UTILS_EXTENSION_NAME,