diff --git a/Cargo.toml b/Cargo.toml index 0d556fe..13c6723 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/weft-build-meta", "crates/weft-compositor"] +members = ["crates/weft-build-meta", "crates/weft-compositor", "crates/weft-servo-shell"] resolver = "2" [workspace.package] diff --git a/crates/weft-servo-shell/Cargo.toml b/crates/weft-servo-shell/Cargo.toml new file mode 100644 index 0000000..3edc4fc --- /dev/null +++ b/crates/weft-servo-shell/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "weft-servo-shell" +version.workspace = true +edition.workspace = true +rust-version.workspace = true + +[[bin]] +name = "weft-servo-shell" +path = "src/main.rs" + +[dependencies] +anyhow = "1.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } diff --git a/crates/weft-servo-shell/src/main.rs b/crates/weft-servo-shell/src/main.rs new file mode 100644 index 0000000..c040b46 --- /dev/null +++ b/crates/weft-servo-shell/src/main.rs @@ -0,0 +1,65 @@ +use std::path::PathBuf; + +use anyhow::Context; + +fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")), + ) + .init(); + + run() +} + +fn run() -> anyhow::Result<()> { + let wayland_display = std::env::var("WAYLAND_DISPLAY") + .context("WAYLAND_DISPLAY not set; weft-compositor must be running")?; + + tracing::info!(socket = %wayland_display, "connecting to Wayland compositor"); + + let html_path = system_ui_html_path()?; + tracing::info!(path = %html_path.display(), "system UI entry point located"); + + embed_servo(&wayland_display, &html_path) +} + +fn system_ui_html_path() -> anyhow::Result { + if let Ok(p) = std::env::var("WEFT_SYSTEM_UI_HTML") { + return Ok(PathBuf::from(p)); + } + + let packaged = PathBuf::from("/packages/system/servo-shell/active/share/weft/system-ui.html"); + if packaged.exists() { + return Ok(packaged); + } + + anyhow::bail!( + "system-ui.html not found; set WEFT_SYSTEM_UI_HTML or install the servo-shell package" + ) +} + +fn embed_servo(_wayland_display: &str, _html_path: &std::path::Path) -> anyhow::Result<()> { + // Wave 4 skeleton entry point. + // + // Full implementation requires completion of the items in + // docs/architecture/winit-wayland-audit.md before production readiness, + // and the following integration work: + // + // 1. Add servo git dependency (not on crates.io; requires building Servo) + // 2. Implement servo::EmbedderMethods and servo::WindowMethods for the + // WEFT Wayland surface (winit + EGL, or smithay-client-toolkit directly) + // 3. Call servo::Servo::new() with the window and embedder + // 4. Load the system UI via servo::ServoUrl::parse(html_path) + // 5. Run the Servo event loop, forwarding Wayland events from winit + // + // The Servo dependency is intentionally absent from Cargo.toml at this stage. + // It requires a git dependency on github.com/servo/servo which embeds + // SpiderMonkey (GeckoMedia) and has a multi-minute build time. It is added + // when the embedder contract is ready. + anyhow::bail!( + "Servo embedding not yet implemented; \ + see docs/architecture/winit-wayland-audit.md for gap assessment" + ) +} diff --git a/docs/architecture/winit-wayland-audit.md b/docs/architecture/winit-wayland-audit.md new file mode 100644 index 0000000..950e339 --- /dev/null +++ b/docs/architecture/winit-wayland-audit.md @@ -0,0 +1,125 @@ +# winit Wayland Input Audit + +Audit of winit's Wayland backend against WEFT OS system shell input requirements. +Required by Wave 4 gate: Servo Wayland input audit result assessed. + +Source: blueprint Section 11, GAP 1. Servo version audited: main branch (2025). +winit version audited: 0.30.x (smithay-client-toolkit backend). + +--- + +## Audit Scope + +WEFT requires correct and reliable keyboard, mouse, touch, and IME input for +the system shell. Input regressions are system-level failures because there is +no fallback input path. + +Servo delegates all windowing and input to winit. winit's Wayland backend uses +smithay-client-toolkit (sctk) as the protocol implementation layer. + +--- + +## Findings + +### Keyboard input + +**Status: FUNCTIONAL with known limitation** + +Basic key events, modifiers, and repeat work correctly via xkb. +The `xdg_keyboard_shortcuts_inhibit` protocol is not implemented in winit's +Wayland backend, so system keyboard shortcuts (e.g. Alt+F4) cannot be +inhibited by client surfaces. This affects the system shell if it needs to +handle those key combinations before the compositor does. + +Relevant winit issue: https://github.com/rust-windowing/winit/issues/2787 (open). + +### Pointer input + +**Status: FUNCTIONAL** + +Button, scroll, and motion events work correctly. `zwp_relative_pointer_v1` +(relative motion for pointer locking) is implemented. `zwp_pointer_constraints_v1` +(locked/confined pointer) is implemented in winit 0.30+. +Frame-accurate pointer position via `wl_pointer.frame` is handled. + +### Touch input + +**Status: PARTIAL** + +Single-touch is functional. Multi-touch slots are tracked via `wl_touch` protocol. +Gesture recognition is not implemented in winit — gestures from the compositor +(`zwp_pointer_gestures_v1`) are not consumed. This affects swipe/pinch gesture +handling in the system shell. + +Relevant winit issue: not filed as of audit date. + +### IME (Input Method Editor) + +**Status: INCOMPLETE** + +`zwp_text_input_v3` is implemented in sctk 0.18+ but winit's integration is +incomplete. Specifically: +- Pre-edit text display is not forwarded to the application's IME event stream + in all cases. +- `done` events with surrounding text are not always handled correctly. + +This means CJK and other IME-dependent input in the system shell HTML will not +work correctly. + +Relevant sctk issue: https://github.com/Smithay/client-toolkit/issues/605 (open). + +### Frame pacing (vsync alignment) + +**Status: NOT IMPLEMENTED** + +winit does not implement `wp_presentation_time` (the Wayland presentation +feedback protocol). Frame timing is based on `wl_callback` only. This means +Servo cannot align rendering to compositor vsync, causing frame pacing issues +on variable-refresh-rate displays and tearing on fixed-refresh displays. + +This must be fixed before the system shell is suitable for production use. +Relevant Servo issue: not filed as of audit date. + +### DMA-BUF surface sharing + +**Status: UNVERIFIED** + +The Servo → WebRender → wgpu → wl_surface pipeline on Wayland may or may not +use `zwp_linux_dmabuf_v1` for zero-copy buffer sharing. This audit did not +test it under the WEFT compositor (requires QEMU or real hardware). +Must be verified when DRM backend testing is available. + +--- + +## Assessment + +| Input area | Status | Blocks Wave 4 skeleton? | +|---------------------|-------------|-------------------------| +| Keyboard (basic) | Functional | No | +| Keyboard shortcuts | Gap | No (deferred) | +| Pointer | Functional | No | +| Touch (single) | Functional | No | +| Touch (gestures) | Gap | No (deferred) | +| IME | Incomplete | No (system shell uses minimal JS) | +| Frame pacing | Not impl. | No (deferred, required before GA) | +| DMA-BUF | Unverified | No (requires hardware test) | + +None of the identified gaps block the Wave 4 skeleton or initial integration. +They block production readiness, as documented in the blueprint. + +**Gate decision**: Wave 4 may proceed. The gaps above are tracked as known +work items, not blocking conditions for skeleton implementation. + +--- + +## Required Follow-up + +Before WEFT OS reaches GA: + +1. Contribute `wp_presentation_time` support to winit (or contribute to Servo + to work around it via the compositor's presentation feedback). +2. Contribute `zwp_text_input_v3` fix to sctk and winit for correct IME. +3. File and track a winit issue for `zwp_pointer_gestures_v1`. +4. Verify DMA-BUF path under the WEFT DRM compositor (requires hardware). +5. File issues for all confirmed gaps in the Servo and winit issue trackers + per the blueprint contribution workflow. diff --git a/infra/shell/system-ui.html b/infra/shell/system-ui.html new file mode 100644 index 0000000..2040d80 --- /dev/null +++ b/infra/shell/system-ui.html @@ -0,0 +1,167 @@ + + + + + + WEFT Desktop + + + + + + + + + + + + + + + + + + + diff --git a/infra/systemd/servo-shell.service b/infra/systemd/servo-shell.service new file mode 100644 index 0000000..63c748f --- /dev/null +++ b/infra/systemd/servo-shell.service @@ -0,0 +1,17 @@ +[Unit] +Description=WEFT OS Servo Shell +Documentation=https://github.com/weft-os/weft +Requires=weft-compositor.service +After=weft-compositor.service + +[Service] +Type=simple +ExecStart=/packages/system/servo-shell/active/bin/weft-servo-shell +Restart=on-failure +RestartSec=2 +# WAYLAND_DISPLAY is exported by weft-compositor after sd_notify(READY=1). +# Downstream services that need the shell ready must declare +# After=servo-shell.service and a suitable readiness mechanism. + +[Install] +WantedBy=graphical.target diff --git a/scripts/wsl-check.sh b/scripts/wsl-check.sh index 2a59e17..3379be3 100644 --- a/scripts/wsl-check.sh +++ b/scripts/wsl-check.sh @@ -31,13 +31,13 @@ export PKG_CONFIG_PATH="$FAKE_PC_DIR:/usr/lib64/pkgconfig:/usr/share/pkgconfig" cd "$PROJECT" echo "" -echo "==> cargo check -p weft-compositor" -cargo check -p weft-compositor 2>&1 +echo "==> cargo check --workspace" +cargo check --workspace 2>&1 echo "" -echo "==> cargo clippy -p weft-compositor -- -D warnings" -cargo clippy -p weft-compositor -- -D warnings 2>&1 +echo "==> cargo clippy --workspace -- -D warnings" +cargo clippy --workspace -- -D warnings 2>&1 echo "" -echo "==> cargo fmt --check -p weft-compositor" -cargo fmt --check -p weft-compositor 2>&1 +echo "==> cargo fmt --check --all" +cargo fmt --check --all 2>&1 echo "" echo "ALL DONE"