diff --git a/src/LunarWM.cppm b/src/LunarWM.cppm index b512ba7..3d8945d 100644 --- a/src/LunarWM.cppm +++ b/src/LunarWM.cppm @@ -7,6 +7,7 @@ module; #include #include #include +#include #include #include @@ -800,28 +801,91 @@ private: }; struct Toplevel { - Toplevel(LunarWM *server, wlr_xdg_toplevel *xdg_toplevel) - : server(server) - , xdg_toplevel(xdg_toplevel) + Toplevel(Toplevel const &) = delete; + Toplevel &operator=(Toplevel const &) = delete; + Toplevel(Toplevel &&) = delete; + Toplevel &operator=(Toplevel &&) = delete; + + Toplevel(LunarWM *srv, wlr_xdg_toplevel *xdg) + : server(srv) + , xdg_toplevel(xdg) { - surface = wlr_surface_from_resource(xdg_toplevel->resource); - texture = wlr_surface_get_texture(surface); - gles_texture = gles2_get_texture(texture); - rl_texture.width = texture->width; - rl_texture.height = texture->height; + surface = xdg->base->surface; + assert(surface); + + commit.notify = [](wl_listener *l, void *) { + auto tl = wl_container_of( + l, static_cast(nullptr), commit); + + if (tl->xdg_toplevel->base->initial_commit) { + wlr_xdg_toplevel_set_size(tl->xdg_toplevel, 0, 0); + return; + } + }; + wl_signal_add(&surface->events.commit, &commit); + + destroy.notify = [](wl_listener *l, void *) { + auto tl = wl_container_of(l, (Toplevel*)nullptr, destroy); + + auto &vec = tl->server->m_wayland.toplevels; + vec.erase(std::remove_if(vec.begin(), vec.end(), + [tl](auto &p){ return p.get() == tl; }), vec.end()); + }; + wl_signal_add(&surface->events.destroy, &destroy); } - operator Texture2D &() { return rl_texture; } + void update() { + + texture = wlr_surface_get_texture(surface); + if (!texture) + return; + wlr_gles2_texture_get_attribs(texture, &attribs); + char const *texture_target = "unknown"; + if (attribs.target == GL_TEXTURE_EXTERNAL_OES) { + texture_target = "GL_TEXTURE_EXTERNAL_OES"; + } else if (attribs.target == GL_TEXTURE_2D) { + texture_target = "GL_TEXTURE_2D"; + } + gles_texture = gles2_get_texture(texture); + if (!gles_texture) { + return; + } + rl_texture.id = attribs.tex; + rl_texture.width = texture->width; + rl_texture.height = texture->height; + rl_texture.mipmaps = 1; + rl_texture.format = gles_texture->has_alpha ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : PIXELFORMAT_UNCOMPRESSED_R8G8B8; + + SetTextureFilter(rl_texture, TEXTURE_FILTER_BILINEAR); + SetTextureWrap(rl_texture, TEXTURE_WRAP_CLAMP); + + } + + ~Toplevel() { + wl_list_remove(&commit.link); + wl_list_remove(&destroy.link); + } LunarWM *server {}; + wl_listener commit {}; + wl_listener destroy {}; + wlr_xdg_toplevel *xdg_toplevel {}; wlr_surface *surface {}; wlr_texture *texture {}; + wlr_gles2_texture_attribs attribs {}; wlr_gles2_texture *gles_texture {}; Texture2D rl_texture {}; }; + struct Output { + struct LunarWM *server {}; + struct wlr_output *output {}; + struct wl_listener frame {}; + struct wl_listener destroy {}; + }; + struct { wl_display *display {}; wl_event_loop *event_loop {}; @@ -849,7 +913,10 @@ private: wlr_cursor *cursor {}; - std::vector toplevels; + std::vector> toplevels; + + // Output stuff to update buffers + struct wl_listener new_output_listener {}; } m_wayland; struct { @@ -1180,12 +1247,23 @@ void LunarWM::init_wayland() m_wayland.xdg_shell = wlr_xdg_shell_create(m_wayland.display, 3); - m_wayland.new_xdg_toplevel_listener.notify - = [](wl_listener *listener, void *data) { - LunarWM *server - = wl_container_of(listener, static_cast(nullptr), - m_wayland.new_xdg_toplevel_listener); - }; + m_wayland.new_xdg_toplevel_listener.notify = [](wl_listener *listener, + void *data) { + LunarWM *server + = wl_container_of(listener, static_cast(nullptr), + m_wayland.new_xdg_toplevel_listener); + auto toplevel = reinterpret_cast(data); + + if (toplevel->title) { + wlr_log(WLR_INFO, "Got XDG toplevel title: %s", toplevel->title); + } + if (toplevel->app_id) { + wlr_log(WLR_INFO, "Got XDG toplevel app ID: %s", toplevel->app_id); + } + + server->m_wayland.toplevels.emplace_back( + std::make_unique(server, toplevel)); + }; wl_signal_add(&m_wayland.xdg_shell->events.new_toplevel, &m_wayland.new_xdg_toplevel_listener); @@ -1193,12 +1271,57 @@ void LunarWM::init_wayland() void *data) { LunarWM *server = wl_container_of(listener, static_cast(nullptr), m_wayland.new_xdg_popup_listener); - auto toplevel = reinterpret_cast(data); - - Toplevel tl(server, toplevel); }; wl_signal_add(&m_wayland.xdg_shell->events.new_popup, &m_wayland.new_xdg_popup_listener); + + // Output stuff + wl_signal_add(&m_wayland.backend->events.new_output, &m_wayland.new_output_listener); + m_wayland.new_output_listener.notify = [](wl_listener *listener, void *data) { + wlr_output *output = reinterpret_cast(data); + LunarWM *sample = + wl_container_of(listener, static_cast(nullptr), m_wayland.new_output_listener); + + wlr_output_init_render(output, sample->m_wayland.allocator, sample->m_wayland.renderer); + + auto my_output = new Output; + my_output->output = output; + my_output->server = sample; + wl_signal_add(&output->events.frame, &my_output->frame); + my_output->frame.notify = [](struct wl_listener *listener, void *data) { + Output *sample_output = + wl_container_of(listener, static_cast(nullptr), frame); + LunarWM *sample = sample_output->server; + wlr_output *wlr_output = sample_output->output; + + struct wlr_output_state state; + wlr_output_state_init(&state); + struct wlr_render_pass *pass = wlr_output_begin_render_pass(wlr_output, &state, NULL); + wlr_render_pass_submit(pass); + + wlr_output_commit_state(wlr_output, &state); + wlr_output_state_finish(&state); + }; + wl_signal_add(&output->events.destroy, &my_output->destroy); + my_output->destroy.notify = [](struct wl_listener *listener, void *data) { + struct Output *sample_output = + wl_container_of(listener, static_cast(nullptr), destroy); + wlr_log(WLR_DEBUG, "Output removed"); + wl_list_remove(&sample_output->frame.link); + wl_list_remove(&sample_output->destroy.link); + free(sample_output); + }; + + wlr_output_state state; + wlr_output_state_init(&state); + wlr_output_state_set_enabled(&state, true); + wlr_output_mode *mode = wlr_output_preferred_mode(output); + if (mode != NULL) { + wlr_output_state_set_mode(&state, mode); + } + wlr_output_commit_state(output, &state); + wlr_output_state_finish(&state); + }; } void LunarWM::init_xr() @@ -1785,37 +1908,37 @@ void LunarWM::poll_events_xr() bool LunarWM::render_layer(RenderLayerInfo &info, float dt) { - uint32_t const view_count = (uint32_t)m_xr.view_configuration_views.size(); + // locate views ----------------------------------------------------------- + std::uint32_t view_count = (std::uint32_t)m_xr.view_configuration_views.size(); std::vector views(view_count, { XR_TYPE_VIEW }); - XrViewLocateInfo locInfo { XR_TYPE_VIEW_LOCATE_INFO }; - locInfo.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; - locInfo.displayTime = info.predicted_display_time; - locInfo.space = m_xr.local_space; + XrViewLocateInfo locInfo{ XR_TYPE_VIEW_LOCATE_INFO }; + locInfo.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + locInfo.displayTime = info.predicted_display_time; + locInfo.space = m_xr.local_space; - XrViewState viewState { XR_TYPE_VIEW_STATE }; - uint32_t located = 0; - if (xrLocateViews(m_xr.session, &locInfo, &viewState, view_count, &located, - views.data()) - != XR_SUCCESS + XrViewState viewState{ XR_TYPE_VIEW_STATE }; + std::uint32_t located = 0; + if (xrLocateViews(m_xr.session, &locInfo, &viewState, + view_count, &located, views.data()) != XR_SUCCESS || located != view_count) { wlr_log(WLR_ERROR, "Failed to locate views"); return false; } + // acquire swapchain images ---------------------------------------------- auto &color_sc = m_xr.swapchains.color[0]; auto &depth_sc = m_xr.swapchains.depth[0]; - auto acquire_wait = [](XrSwapchain sc, uint32_t &idx) -> bool { - XrSwapchainImageAcquireInfo ai { XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO }; - if (xrAcquireSwapchainImage(sc, &ai, &idx) != XR_SUCCESS) - return false; - XrSwapchainImageWaitInfo wi { XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO }; + auto acquire_wait = [](XrSwapchain sc, std::uint32_t &idx)->bool { + XrSwapchainImageAcquireInfo ai{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO }; + if (xrAcquireSwapchainImage(sc, &ai, &idx) != XR_SUCCESS) return false; + XrSwapchainImageWaitInfo wi{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO }; wi.timeout = XR_INFINITE_DURATION; return xrWaitSwapchainImage(sc, &wi) == XR_SUCCESS; }; - uint32_t colIdx = 0, depIdx = 0; + std::uint32_t colIdx = 0, depIdx = 0; if (!acquire_wait(color_sc.swapchain, colIdx) || !acquire_wait(depth_sc.swapchain, depIdx)) { wlr_log(WLR_ERROR, "Swap-chain acquire failed"); @@ -1825,78 +1948,89 @@ bool LunarWM::render_layer(RenderLayerInfo &info, float dt) GLuint color_tex = m_xr.get_swapchain_image(color_sc.swapchain, colIdx); GLuint depth_tex = m_xr.get_swapchain_image(depth_sc.swapchain, depIdx); - if (!m_renderer.fbo) - glGenFramebuffers(1, &m_renderer.fbo); + // build FBO -------------------------------------------------------------- + if (!m_renderer.fbo) glGenFramebuffers(1, &m_renderer.fbo); glBindFramebuffer(GL_FRAMEBUFFER, m_renderer.fbo); - rlFramebufferAttach(m_renderer.fbo, color_tex, RL_ATTACHMENT_COLOR_CHANNEL0, - RL_ATTACHMENT_TEXTURE2D, 0); + RL_ATTACHMENT_TEXTURE2D, 0); rlFramebufferAttach(m_renderer.fbo, depth_tex, RL_ATTACHMENT_DEPTH, - RL_ATTACHMENT_TEXTURE2D, 0); + RL_ATTACHMENT_TEXTURE2D, 0); assert(rlFramebufferComplete(m_renderer.fbo)); - uint32_t const eyeW - = m_xr.view_configuration_views[0].recommendedImageRectWidth; - uint32_t const eyeH - = m_xr.view_configuration_views[0].recommendedImageRectHeight; + std::uint32_t eyeW = m_xr.view_configuration_views[0].recommendedImageRectWidth; + std::uint32_t eyeH = m_xr.view_configuration_views[0].recommendedImageRectHeight; - m_renderer.tmp_rt = { m_renderer.fbo, + m_renderer.tmp_rt = { + m_renderer.fbo, { color_tex, (int)(eyeW * view_count), (int)eyeH, 1, -1 }, - { depth_tex, (int)(eyeW * view_count), (int)eyeH, 1, -1 } }; + { depth_tex, (int)(eyeW * view_count), (int)eyeH, 1, -1 } + }; - Matrix projL = xr_projection_matrix(views[0].fov); - Matrix projR = xr_projection_matrix(views[1].fov); - Matrix viewL = MatrixInvert(xr_matrix(views[0].pose)); - Matrix viewR = MatrixInvert(xr_matrix(views[1].pose)); + // head-space view matrix (matches rlOpenXR) ------------------------------ + XrSpaceLocation headLoc{ XR_TYPE_SPACE_LOCATION }; + xrLocateSpace(m_xr.view_space, m_xr.local_space, + info.predicted_display_time, &headLoc); + Matrix headView = MatrixInvert(xr_matrix(headLoc.pose)); + // per-eye projection + view-offset --------------------------------------- + Matrix projR = xr_projection_matrix(views[1].fov); + Matrix projL = xr_projection_matrix(views[0].fov); + Matrix viewOffR = MatrixMultiply(xr_matrix(views[1].pose), headView); + Matrix viewOffL = MatrixMultiply(xr_matrix(views[0].pose), headView); + + // draw ------------------------------------------------------------------- BeginTextureMode(m_renderer.tmp_rt); rlEnableStereoRender(); - rlSetMatrixProjectionStereo(projL, projR); - rlSetMatrixViewOffsetStereo(viewL, viewR); + rlSetMatrixProjectionStereo(projL, projR); // right, left (yes) + rlSetMatrixViewOffsetStereo(viewOffL, viewOffR); glViewport(0, 0, (GLsizei)(eyeW * view_count), (GLsizei)eyeH); ClearBackground(RAYWHITE); + m_renderer.update_camera(*this, m_renderer.camera, info); + BeginMode3D(m_renderer.camera); { - this->render_3d(dt); + render_3d(dt); } EndMode3D(); rlDisableStereoRender(); EndTextureMode(); - XrSwapchainImageReleaseInfo ri { XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO }; + // release swapchain images ---------------------------------------------- + XrSwapchainImageReleaseInfo ri{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO }; xrReleaseSwapchainImage(color_sc.swapchain, &ri); xrReleaseSwapchainImage(depth_sc.swapchain, &ri); - info.layer_projection_views.resize( - view_count, { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW }); - - for (uint32_t i = 0; i < view_count; ++i) { - int32_t const xOff = (int32_t)i * (int32_t)eyeW; + // fill projection layer -------------------------------------------------- + info.layer_projection_views.resize(view_count, + { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW }); + for (std::uint32_t i = 0; i < view_count; ++i) { + int32_t xOff = (int32_t)i * (int32_t)eyeW; auto &pv = info.layer_projection_views[i]; pv.pose = views[i].pose; - pv.fov = views[i].fov; - pv.subImage.swapchain = color_sc.swapchain; - pv.subImage.imageRect - = { { xOff, 0 }, { (int32_t)eyeW, (int32_t)eyeH } }; - pv.subImage.imageArrayIndex = 0; + pv.fov = views[i].fov; + pv.subImage.swapchain = color_sc.swapchain; + pv.subImage.imageRect.offset = { xOff, 0 }; + pv.subImage.imageRect.extent = { (int32_t)eyeW, (int32_t)eyeH }; + pv.subImage.imageArrayIndex = 0; } - info.layer_projection.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION; - info.layer_projection.space = m_xr.local_space; - info.layer_projection.viewCount = view_count; - info.layer_projection.views = info.layer_projection_views.data(); - info.layer_projection.layerFlags - = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT - | XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + info.layer_projection = { + .type = XR_TYPE_COMPOSITION_LAYER_PROJECTION, + .layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT | + XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, + .space = m_xr.local_space, + .viewCount = view_count, + .views = info.layer_projection_views.data(), + }; info.layers.clear(); - info.layers.push_back(reinterpret_cast( - &info.layer_projection)); + info.layers.push_back( + reinterpret_cast(&info.layer_projection)); return true; } @@ -1905,7 +2039,7 @@ LunarWM::~LunarWM() { assert(m_initialized); - if (m_xr.local_space == XR_NULL_HANDLE) { + if (m_xr.local_space != XR_NULL_HANDLE) { xrDestroySpace(m_xr.local_space); } @@ -1935,16 +2069,16 @@ LunarWM::~LunarWM() wl_list_remove(&m_wayland.keyboards); wl_display_destroy_clients(m_wayland.display); - if (!m_wayland.allocator) { + if (m_wayland.allocator) { wlr_allocator_destroy(m_wayland.allocator); } - if (!m_wayland.renderer) { + if (m_wayland.renderer) { wlr_renderer_destroy(m_wayland.renderer); } - if (!m_wayland.backend) { + if (m_wayland.backend) { wlr_backend_destroy(m_wayland.backend); } - if (!m_wayland.display) { + if (m_wayland.display) { wl_display_destroy(m_wayland.display); } } @@ -2047,17 +2181,11 @@ void LunarWM::run() void LunarWM::render_3d(float dt) { - static float animT { 0.0f }; - animT += dt; - DrawGrid(10, 1); - Vector3 forward = Vector3Normalize( - Vector3Subtract(m_renderer.camera.target, m_renderer.camera.position)); - float distance = 5.0f + sinf(animT) * 3.0f; - Vector3 spherePos = Vector3Add( - m_renderer.camera.position, Vector3Scale(forward, distance)); - DrawSphere(spherePos, 0.5f, YELLOW); + for (auto const &tl : m_wayland.toplevels) { + DrawBillboard(m_renderer.camera, tl->rl_texture, { -2, 1, -2 }, 1, WHITE); + } } void LunarWM::terminate()