diff options
| author | Adam Malczewski <[email protected]> | 2026-06-13 00:17:33 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-13 00:17:33 +0900 |
| commit | 803fd2687a5f6ead0644f9c952bed6e3e4ef7ed9 (patch) | |
| tree | 68d727df9c0f08a7a08c2c464f95d8c82fb8789e /packages/kernel/src/server.cpp | |
| parent | c102a1b67a70149b6f9c9b2cfd8b31ceb52c09b7 (diff) | |
| download | unbox-803fd2687a5f6ead0644f9c952bed6e3e4ef7ed9.tar.gz unbox-803fd2687a5f6ead0644f9c952bed6e3e4ef7ed9.zip | |
Slice 5: real ui substrate + unified input routing + touch-mode; spike retired
The ui substrate is now the extension-facing contract (unbox/kernel/ui.hpp):
Host::ui() -> UiSubstrate::create_surface(spec) -> UiSurface with typed
scalar bindings (int/double/bool/string getters), data-event callbacks
(error-isolated per extension), dirty(), geometry/visibility — RMLUi and
GL stay kernel-private. Production sync: glFinish replaced by
EGL_KHR_fence_sync + 2-deep wlr_swapchain. ui_spike retired (orientation
guard + dirty-cycle coverage live on as substrate tests).
Input: ONE kernel routing path feeds pointer AND touch into ui surfaces
with consume-or-pass semantics and implicit-grab ownership (the consumer
of a press owns the matching release; per touch point too) — fixes
drag-release-over-ui sticking. touch-mode: state machine + debounce +
on_touch_mode_changed notification, NO visual scaling (user decision
after hardware hands-on; dp-ratio stays 1.0; see plan §2).
ext-xdg-shell: GrabMachine generalized to pointer-OR-touch interaction
source (touch titlebar drag works; originating-point pinning); fixed the
seat implicit-grab leak (suppressed release after forwarded press
swallowed all later touch-downs — pointer/touch alternation doctested);
factory renamed create(). ext-layer-shell: on_demand keyboard
interactivity via scene hit resolution. host-bin: --ui-demo extension
(temporary acceptance demo on the public contract, dies in slice 6).
User hands-on verified: same surface by mouse and finger, tap counter,
touch-mode neutrality, no click-through, drag alternation, fuzzel
on_demand. 113 doctest cases green, ASan/UBSan clean (our code), idle
RSS ≈78 MiB. Harness: UX-feel hands-on lesson (ORCHESTRATOR §2.6),
nested-run pkill/setsid notes, touch-mode glossary redefinition.
Diffstat (limited to 'packages/kernel/src/server.cpp')
| -rw-r--r-- | packages/kernel/src/server.cpp | 93 |
1 files changed, 69 insertions, 24 deletions
diff --git a/packages/kernel/src/server.cpp b/packages/kernel/src/server.cpp index e3308de..d02cf78 100644 --- a/packages/kernel/src/server.cpp +++ b/packages/kernel/src/server.cpp @@ -69,12 +69,53 @@ void Server::terminate() { wl_display_terminate(impl_->display); } -auto Server::ui_spike_frame_count() const -> int { - return impl_->ui_spike != nullptr ? impl_->ui_spike->frame_count() : 0; +auto Server::ui_frame_count() const -> int { + return impl_->substrate != nullptr ? impl_->substrate->frame_count() : 0; } -auto Server::ui_spike_orientation() const -> int { - return impl_->ui_spike != nullptr ? impl_->ui_spike->check_orientation() : 0; +auto Server::ui_orientation() const -> int { + return impl_->substrate != nullptr ? impl_->substrate->orientation() : 0; +} + +auto Server::ui_fence_sync_active() const -> bool { + return impl_->substrate != nullptr && impl_->substrate->fence_sync_active(); +} + +void Server::ui_set_touch_override(UiTouchOverride ov) { + if (impl_->substrate == nullptr) { + return; + } + UiSubstrate::TouchModeOverride mapped = UiSubstrate::TouchModeOverride::automatic; + if (ov == UiTouchOverride::force_off) { + mapped = UiSubstrate::TouchModeOverride::force_off; + } else if (ov == UiTouchOverride::force_on) { + mapped = UiSubstrate::TouchModeOverride::force_on; + } + impl_->substrate->set_touch_mode_override(mapped); +} + +// ---- PerExtensionUi (per-extension ui-substrate facade) -------------------- + +auto PerExtensionUi::create_surface(const UiSurfaceSpec& spec) -> std::unique_ptr<UiSurface> { + if (server_->substrate == nullptr) { + return nullptr; + } + wlr_scene_tree* parent = server_->scene_layers[static_cast<std::size_t>(spec.layer)]; + return server_->substrate->create_surface(id_, parent, spec); +} + +auto PerExtensionUi::available() const -> bool { + return server_->substrate != nullptr && server_->substrate->available(); +} + +auto PerExtensionUi::touch_mode() const -> bool { + return server_->substrate != nullptr && server_->substrate->touch_mode(); +} + +void PerExtensionUi::set_touch_mode_override(TouchModeOverride ov) { + if (server_->substrate != nullptr) { + server_->substrate->set_touch_mode_override(ov); + } } // ---- Impl lifecycle -------------------------------------------------------- @@ -169,9 +210,10 @@ void Server::Impl::init() { throw std::runtime_error("failed to start the wlr_backend"); } - if (options.ui_spike) { - start_ui_spike(); - } + // The ui substrate is always built; it reports available()==false on a + // backend with no GL path (headless pixman) and create_surface yields + // nullptr there, so extensions degrade gracefully. Never throws. + start_substrate(); if (!options.startup_cmd.empty()) { if (fork() == 0) { @@ -273,20 +315,22 @@ void Server::Impl::activate_extensions() { } } -void Server::Impl::start_ui_spike() { - if (!wlr_renderer_is_gles2(renderer)) { - wlr_log(WLR_INFO, "ui-spike: renderer is not gles2; spike disabled"); - return; - } - wlr_egl* egl = wlr_gles2_renderer_get_egl(renderer); - if (egl == nullptr) { - wlr_log(WLR_ERROR, "ui-spike: gles2 renderer has no wlr_egl"); - return; +void Server::Impl::start_substrate() { + // The substrate needs the wlr renderer's EGLDisplay for its sibling GLES + // 3.2 context. Only the gles2 renderer exposes one; under pixman (headless + // CI) there is no GL path, so the substrate builds but reports unavailable. + EGLDisplay display_egl = EGL_NO_DISPLAY; + if (wlr_renderer_is_gles2(renderer)) { + if (wlr_egl* egl = wlr_gles2_renderer_get_egl(renderer)) { + display_egl = wlr_egl_get_display(egl); + } + } else { + wlr_log(WLR_INFO, "ui-substrate: renderer is not gles2; substrate unavailable"); } - EGLDisplay display_egl = wlr_egl_get_display(egl); - // The spike sits in the overlay band so it composites above everything. - ui_spike = UiSpike::create(scene_layers[static_cast<std::size_t>(SceneLayer::overlay)], - display_egl, allocator, renderer); + // A data-event/getter throw disables the owning extension via the same + // isolation path the bus uses (Server::Impl is the DisableSink). + substrate = Substrate::create(display_egl, allocator, renderer, + [this](ExtensionId who) { disable(who); }); } void Server::Impl::shutdown() { @@ -301,8 +345,9 @@ void Server::Impl::shutdown() { } extensions.clear(); - // Slice-3 spike: tear down before scene/renderer/allocator die. - ui_spike.reset(); + // The ui substrate owns scene nodes + GL objects on a sibling context and + // borrows scene/renderer/allocator: tear it down before they die. + substrate.reset(); if (display != nullptr) { wl_display_destroy_clients(display); @@ -377,8 +422,8 @@ void Server::Impl::handle_new_output(wlr_output* wlr_output) { outputs.push_back(std::move(owned)); output->frame.connect(wlr_output->events.frame, [this, output](void*) { - if (ui_spike != nullptr) { - ui_spike->tick(); + if (substrate != nullptr) { + substrate->tick_all(); } wlr_scene_output* scene_output = wlr_scene_get_scene_output(scene, output->output); wlr_scene_output_commit(scene_output, nullptr); |
