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,