mirror of
https://github.com/marcoallegretti/WEFT_OS.git
synced 2026-03-26 17:03:09 +00:00
feat(servo-shell): add servo-shell skeleton, system UI, service unit, and Wayland input audit
Includes winit Wayland input audit for servo-shell integration planning. New files: - crates/weft-servo-shell/: new workspace member - Cargo.toml: anyhow + tracing deps; no servo dep yet (requires git dependency on github.com/servo/servo with multi-minute build; deferred until embedder contract is confirmed) - src/main.rs: reads WAYLAND_DISPLAY and WEFT_SYSTEM_UI_HTML, locates system-ui.html from packaged path, calls embed_servo() stub that returns a descriptive error explaining the integration work remaining - infra/shell/system-ui.html: system UI document per blueprint Section 5 DOM structure (weft-desktop, weft-wallpaper, weft-taskbar, weft-launcher, weft-notification-center, weft-window); includes clock and launcher toggle - infra/systemd/servo-shell.service: Requires+After weft-compositor.service, Type=simple, Restart=on-failure - docs/architecture/winit-wayland-audit.md: audit of winit 0.30.x Wayland backend against WEFT input requirements; identifies keyboard shortcut inhibit gap, touch gesture gap, IME incomplete (zwp_text_input_v3), frame pacing absent (wp_presentation_time), DMA-BUF unverified; none block initial integration; all tracked as pre-GA work items Modified: - Cargo.toml: add weft-servo-shell to workspace members - scripts/wsl-check.sh: switch to --workspace for all three gates
This commit is contained in:
parent
61bef1a0a7
commit
fc5ada2079
7 changed files with 395 additions and 7 deletions
|
|
@ -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]
|
||||
|
|
|
|||
14
crates/weft-servo-shell/Cargo.toml
Normal file
14
crates/weft-servo-shell/Cargo.toml
Normal file
|
|
@ -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"] }
|
||||
65
crates/weft-servo-shell/src/main.rs
Normal file
65
crates/weft-servo-shell/src/main.rs
Normal file
|
|
@ -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<PathBuf> {
|
||||
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"
|
||||
)
|
||||
}
|
||||
125
docs/architecture/winit-wayland-audit.md
Normal file
125
docs/architecture/winit-wayland-audit.md
Normal file
|
|
@ -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.
|
||||
167
infra/shell/system-ui.html
Normal file
167
infra/shell/system-ui.html
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>WEFT Desktop</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
--surface-bg: rgba(255, 255, 255, 0.07);
|
||||
--surface-border: rgba(255, 255, 255, 0.12);
|
||||
--taskbar-height: 48px;
|
||||
--text-primary: rgba(255, 255, 255, 0.92);
|
||||
--text-secondary: rgba(255, 255, 255, 0.55);
|
||||
--accent: #5b8af5;
|
||||
}
|
||||
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #0f1117;
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
color: var(--text-primary);
|
||||
height: 100dvh;
|
||||
width: 100dvw;
|
||||
}
|
||||
|
||||
weft-desktop {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr var(--taskbar-height);
|
||||
height: 100dvh;
|
||||
width: 100dvw;
|
||||
contain: layout;
|
||||
}
|
||||
|
||||
weft-wallpaper {
|
||||
display: block;
|
||||
grid-row: 1;
|
||||
background: linear-gradient(
|
||||
145deg,
|
||||
#0f1117 0%,
|
||||
#161b2e 40%,
|
||||
#0d2040 70%,
|
||||
#0a1a30 100%
|
||||
);
|
||||
}
|
||||
|
||||
weft-taskbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
grid-row: 2;
|
||||
height: var(--taskbar-height);
|
||||
padding: 0 12px;
|
||||
gap: 8px;
|
||||
background: var(--surface-bg);
|
||||
border-top: 1px solid var(--surface-border);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
weft-taskbar-launcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
weft-taskbar-launcher:hover {
|
||||
background: var(--surface-border);
|
||||
}
|
||||
|
||||
weft-taskbar-clock {
|
||||
margin-left: auto;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
weft-launcher {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
bottom: var(--taskbar-height);
|
||||
background: rgba(10, 12, 20, 0.85);
|
||||
backdrop-filter: blur(32px);
|
||||
-webkit-backdrop-filter: blur(32px);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
weft-launcher:not([hidden]) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
weft-notification-center {
|
||||
position: fixed;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
width: 360px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
z-index: 200;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
weft-window {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<weft-desktop>
|
||||
<weft-wallpaper></weft-wallpaper>
|
||||
<weft-taskbar>
|
||||
<weft-taskbar-launcher id="launcher-btn" title="Apps">
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<rect x="1" y="1" width="6" height="6" rx="1.5"
|
||||
fill="rgba(255,255,255,0.7)" />
|
||||
<rect x="11" y="1" width="6" height="6" rx="1.5"
|
||||
fill="rgba(255,255,255,0.7)" />
|
||||
<rect x="1" y="11" width="6" height="6" rx="1.5"
|
||||
fill="rgba(255,255,255,0.7)" />
|
||||
<rect x="11" y="11" width="6" height="6" rx="1.5"
|
||||
fill="rgba(255,255,255,0.7)" />
|
||||
</svg>
|
||||
</weft-taskbar-launcher>
|
||||
<weft-taskbar-clock id="clock"></weft-taskbar-clock>
|
||||
</weft-taskbar>
|
||||
</weft-desktop>
|
||||
|
||||
<weft-launcher hidden id="launcher"></weft-launcher>
|
||||
<weft-notification-center id="notifications"></weft-notification-center>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var clockEl = document.getElementById('clock');
|
||||
function updateClock() {
|
||||
clockEl.textContent = new Date().toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
}
|
||||
updateClock();
|
||||
setInterval(updateClock, 10000);
|
||||
|
||||
document.getElementById('launcher-btn').addEventListener('click', function () {
|
||||
var launcher = document.getElementById('launcher');
|
||||
if (launcher.hasAttribute('hidden')) {
|
||||
launcher.removeAttribute('hidden');
|
||||
} else {
|
||||
launcher.setAttribute('hidden', '');
|
||||
}
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
17
infra/systemd/servo-shell.service
Normal file
17
infra/systemd/servo-shell.service
Normal file
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue