Commit graph

16 commits

Author SHA1 Message Date
34359acf3f feat(servo-embed): wire Servo deps and share Wayland surface with shell client
- Add servo/winit/softbuffer as optional deps in weft-servo-shell and
  weft-app-shell Cargo.toml, gated on servo-embed feature
- Replace ShellClient::connect() and connect_as_app() with
  connect_with_display() and connect_as_app_with_display(), using
  Backend::from_foreign_display to share the winit wl_display connection
- Move ShellClient construction into resumed() in both embedders after
  winit window and wl_surface are available
- Pass actual wl_surface to create_window instead of None
- Fix pre-existing field name bug: wayland-scanner generates _type for
  the reserved keyword arg name=type, not r#type
2026-03-12 15:16:17 +01:00
4d0089a107 feat: appd IPC relay, WIT interfaces, UI kit, gesture routing, and CI hardening
- weft-appd: per-session IPC socket paths; bidirectional Wasm-HTML JSON relay
  via spawn_ipc_relay; SO_PEERCRED UID check on Unix socket connections;
  PanelGesture request and NavigationGesture broadcast for compositor gestures
- weft-runtime: weft:app/ipc, weft:app/fetch, weft:app/notifications WIT
  interfaces; IpcState non-blocking Unix socket host functions; ureq-backed
  net:fetch host function (net-fetch feature); notify-send notifications host
- weft-file-portal: spawn a thread per accepted connection for concurrent access
- weft-app-shell: weft-system:// URL translation; WEFT UI Kit UserScript
  injection; resolve_weft_system_url helper
- weft-servo-shell: forward compositor navigation gestures to weft-appd
  WebSocket as PanelGesture; WEFT UI Kit UserScript injection
- infra/shell: weft-ui-kit.js with 11 custom elements (weft-button, weft-card,
  weft-dialog, weft-icon, weft-list, weft-list-item, weft-menu, weft-menu-item,
  weft-progress, weft-input, weft-label); system-ui.html handles
  NAVIGATION_GESTURE messages and dispatches weft:navigation-gesture CustomEvent
- infra/systemd: add missing env vars to weft-appd.service; correct
  servo-shell.service binary path and system-ui.html argument
- .github/workflows/ci.yml: exclude weft-servo-shell and weft-app-shell from
  cross-platform job; add them to linux-only job with libsystemd-dev dependency
2026-03-12 12:49:45 +01:00
ed5a69bb74 feat(servo-shell): per-app WebView lifecycle driven by appd events (servo-embed only)
Task 10 -- App WebView lifecycle.

appd_ws module (servo-embed gated):
  Background thread connects to the appd WebSocket on startup.
  Sends QUERY_RUNNING to receive initial running sessions.
  Translates LAUNCH_ACK -> AppdCmd::Launch and APP_STATE stopped
  -> AppdCmd::Stop, then wakes the winit event loop via the
  shared EventLoopWaker.

embedder changes:
  App struct gains app_rx (mpsc receiver), app_webviews
  (HashMap<session_id, WebView>), active_session, and a stored
  rendering_context used when creating app WebViews later.

  create_app_webview(): resolves weft-app://<app_id>/index.html
  to a file URL, creates a dedicated UserContentManager with the
  weftIpc bridge injected (includes window.weftSessionId), builds
  and registers a new WebView.

  about_to_wait() drains app_rx: creates WebViews for Launch
  commands, removes and clears active_session for Stop commands.

  active_webview() returns the active-session WebView when one
  exists, falling back to the system-ui WebView. Rendering,
  keyboard, and mouse events all route through active_webview().

  Resize propagates to both the system WebView and all app WebViews.

  run() creates the mpsc channel and spawns the appd listener
  before entering the winit event loop.
2026-03-11 17:59:12 +01:00
1b93f1c825 feat: weft-file-portal -- sandboxed file access broker
New crate. Per-session file proxy that gates filesystem access to an
explicit allowlist of paths passed at startup.

Usage: weft-file-portal <socket_path> [--allow <path>]...

Listens on a Unix domain socket. Each connection receives newline-
delimited JSON requests and returns newline-delimited JSON responses.
File content is base64-encoded. Operations: read, write, list.
Empty allowlist rejects all requests; paths checked with starts_with.

7 unit tests covering access control, read/write roundtrip, and list.
2026-03-11 15:52:33 +01:00
97ea969075 feat: weft-mount-helper -- setuid helper for EROFS+dm-verity mounts
New crate: weft-mount-helper. A privileged helper binary that sets up
dm-verity devices and mounts EROFS images for app isolation.

Commands:
  mount <img> <hash_dev> <root_hash> <mountpoint>
    - opens a named dm-verity device via veritysetup open
    - mounts the resulting /dev/mapper/<name> as EROFS read-only
    - cleans up the dm device if mount fails
  umount <mountpoint>
    - unmounts the mountpoint
    - closes the dm-verity device via veritysetup close

Device naming: derives a stable name from the mountpoint path, limited
to 31 chars (DM limit), always prefixed weft-. Root check reads
/proc/self/status euid rather than using unsafe libc calls.

Tests: device_name_sanitizes_path, device_name_truncates_long_paths.
2026-03-11 15:43:59 +01:00
afffe29090 docs: update README to accurately reflect current implementation state 2026-03-11 12:59:24 +01:00
ffae164747 feat(pack): add weft-pack package validator tool
New crate: weft-pack — command-line tool for validating WEFT application
package directories against the app-package-format spec.

src/main.rs:
- check <dir>: loads wapp.toml, validates app ID format, verifies
  package.name is non-empty and <=64 chars, confirms runtime.module and
  ui.entry files exist. Prints 'OK' on success or the list of errors.
- info <dir>: prints all manifest fields to stdout.
- load_manifest(): reads and parses wapp.toml with toml crate.
- is_valid_app_id(): enforces reverse-domain convention (>=3 components,
  each starting with a lowercase letter, digits allowed, no hyphens or
  uppercase).

Tests (5):
- app_id_valid: accepts well-formed reverse-domain IDs.
- app_id_invalid: rejects two-component, uppercase, hyphen, empty IDs.
- check_package_missing_manifest: error when wapp.toml is absent.
- check_package_valid: full happy-path with real temp files.
- check_package_invalid_app_id: error on a hyphenated app ID.

New deps: toml 0.8, serde 1 (derive).
Added weft-pack to workspace Cargo.toml; wsl-test.sh extended.
2026-03-11 09:40:34 +01:00
f38f2eef76 feat(runtime): add weft-runtime crate skeleton
New crate: weft-runtime — the child process spawned by weft-appd to
execute WEFT application packages.

src/main.rs:
- Parses CLI arguments: <app_id> <session_id> (as per the supervisor
  contract in runtime.rs).
- resolve_package(): searches user store
  (~/.local/share/weft/apps/<app_id>) then system store
  (/usr/share/weft/apps/<app_id>) for a wapp.toml manifest. Overridden
  by WEFT_APP_STORE env var.
- Verifies app.wasm exists in the resolved package directory.
- Stubs Wasmtime execution with a TODO comment; prints 'READY' to
  stdout and exits cleanly so weft-appd's supervisor can complete the
  session lifecycle during development and integration testing.

Tests (2):
- package_store_roots_includes_system_path: system store path present.
- package_store_roots_uses_weft_app_store_when_set: WEFT_APP_STORE
  override replaces default search list.

Also:
- Added weft-runtime to workspace Cargo.toml members.
- wsl-test.sh: added cargo test -p weft-runtime.
2026-03-11 09:27:30 +01:00
7cebac4188 feat(appd): add WebSocket UI endpoint for Servo shell integration
Implements the weft-appd WebSocket server that allows the system-ui.html
page running inside Servo to send requests and receive push notifications
without requiring custom SpiderMonkey bindings.

ws.rs — WebSocket connection handler:
- Accepts a tokio TcpStream, performs WebSocket handshake via
  tokio-tungstenite accept_async.
- Reads JSON Text frames, deserializes as Request (serde_json), calls
  dispatch(), sends Response as JSON Text.
- Subscribes to a broadcast::Receiver<Response> for server-push
  notifications (APP_READY, etc.); forwards to client via select!.
- Handles close frames, partial errors, and lagged broadcast gracefully.

main.rs — server changes:
- broadcast::channel(16) created at startup; WebSocket handlers
  subscribe for push delivery.
- TcpListener bound on 127.0.0.1:7410 (default) or WEFT_APPD_WS_PORT.
- ws_port() / write_ws_port(): port written to
  XDG_RUNTIME_DIR/weft/appd.wsport for runtime discovery.
- WS accept branch added to the main select! loop alongside Unix socket.

ipc.rs — Response and AppStateKind now derive Clone (required by
broadcast::Sender<Response>).

system-ui.html — appd WebSocket client:
- appdConnect(): opens ws://127.0.0.1:<port>/appd with exponential
  backoff reconnect (1s → 16s max).
- On open: sends QUERY_RUNNING to populate taskbar with live sessions.
- handleAppdMessage(): maps LAUNCH_ACK and RUNNING_APPS to taskbar
  entries; APP_READY shows a timed notification; APP_STATE::stopped
  removes the taskbar entry.
- WEFT_APPD_WS_PORT window global overrides the default port.

New deps: tokio-tungstenite 0.24, futures-util 0.3 (sink+std),
serde_json 1.
2026-03-11 09:01:54 +01:00
538eccd4c6 feat(appd): implement IPC server with Unix socket and MessagePack framing
Replaces the skeleton bail with a functional IPC server.

ipc.rs — transport layer:
- Request enum: LaunchApp, TerminateApp, QueryRunning, QueryAppState.
  Serialized with serde MessagePack (rmp-serde, SCREAMING_SNAKE_CASE
  type tag).
- Response enum: LaunchAck, AppReady, RunningApps, AppState, Error.
- AppStateKind: Starting, Running, Stopping, Stopped, NotFound.
- read_frame / write_frame: async 4-byte LE length-prefixed codec over
  any AsyncRead / AsyncWrite.

main.rs — server:
- SessionRegistry: in-memory HashMap<session_id, AppStateKind> with
  monotonic ID counter; launch / terminate / running_ids / state.
- run(): creates socket parent directory, removes stale socket, binds
  UnixListener, sends sd_notify READY=1, then accept-loops with
  ctrl-c / SIGTERM shutdown. Cleans up socket on exit.
- handle_connection(): splits stream into BufReader/BufWriter, reads
  request frames, calls dispatch, writes response frames.
- dispatch(): maps Request variants to SessionRegistry operations;
  returns typed Response. Wasmtime spawning and compositor client
  deferred to later implementation.

New deps: serde (derive), rmp-serde, tokio net/io-util/sync/rt-multi-thread.
2026-03-11 08:25:55 +01:00
2bb657e8fc feat(servo-shell): add weft-shell-protocol client-side binding
Generate client-side protocol types from weft-shell-unstable-v1.xml
using wayland-scanner, following the same module structure as the
compositor server side.

- crates/weft-servo-shell/src/protocols/mod.rs: generate_interfaces!
  inside __interfaces submodule, generate_client_code! at client module
  level, with use wayland_client in scope. Re-exports
  ZweftShellManagerV1 and ZweftShellWindowV1 for use by embed_servo
  once the Wayland connection is established.

- New deps: wayland-client, wayland-backend, wayland-scanner, bitflags
  (version-matched to existing workspace resolution).

The binding compiles but is not yet wired into embed_servo(); that
connection is deferred until the Servo embedder contract is ready.
2026-03-11 08:05:03 +01:00
18f92cc341 feat(compositor): implement weft-shell-protocol server side
Add the WEFT compositor-shell Wayland protocol and wire it into the
compositor state.

Protocol definition:
- protocol/weft-shell-unstable-v1.xml: defines zweft_shell_manager_v1
  (global, bound once by servo-shell) and zweft_shell_window_v1
  (per-window slot). Requests: destroy, create_window,
  update_metadata, set_geometry. Events: configure, focus_changed,
  window_closed, presentation_feedback.

Generated code + bindings:
- crates/weft-compositor/src/protocols/mod.rs: uses wayland-scanner
  generate_interfaces! inside a __interfaces sub-module and
  generate_server_code! at the server module level, following the
  wayland-protocols-wlr crate structure. Exports WeftShellState
  (holds the GlobalId) and WeftShellWindowData (per-window user data).

Server-side dispatch (state.rs):
- GlobalDispatch<ZweftShellManagerV1, ()>: binds the global, inits
  each bound resource with unit user data.
- Dispatch<ZweftShellManagerV1, ()>: handles create_window by
  initialising a ZweftShellWindowV1 and sending an initial configure.
- Dispatch<ZweftShellWindowV1, WeftShellWindowData>: handles
  update_metadata (stores advisory data) and set_geometry (echoes
  compositor-adjusted configure back to client).

WeftCompositorState.weft_shell_state initialised in new() alongside
all other protocol globals.

New direct deps in weft-compositor: wayland-scanner, wayland-server,
wayland-backend, bitflags (all version-matched to Smithay 0.7).
2026-03-11 07:59:56 +01:00
c7ad2116a0 feat(appd): add weft-appd skeleton crate and service unit
New crate implementing the application daemon entry point:
- crates/weft-appd/Cargo.toml: tokio (current-thread runtime), anyhow,
  sd-notify, tracing dependencies
- crates/weft-appd/src/main.rs: async run() resolves IPC socket path
  from WEFT_APPD_SOCKET or XDG_RUNTIME_DIR/weft/appd.sock; stubs for
  AppRegistry, IpcServer, CompositorClient, RuntimeSupervisor,
  CapabilityBroker, ResourceController per WEFT-OS-APPD-DESIGN.md;
  sd_notify(READY=1) to be sent after IpcServer bind + CompositorClient
  connect
- infra/systemd/weft-appd.service: Type=notify, Requires+After
  weft-compositor.service, After servo-shell.service

Also fix two winit backend issues that were present in the working tree:
- remove spurious mut on display binding (never mutated after init)
- wrap std::env::set_var in unsafe block (required since Rust 1.80)
2026-03-11 01:13:18 +01:00
8925ebe3df feat(compositor): implement DRM/KMS rendering path
Add full DRM/KMS backend with libseat session, GBM allocation, EGL
display initialisation, and a GpuManager-driven rendering loop.

- drm_device.rs: type aliases and per-device/per-output state structs
 (WeftDrmDevice, WeftOutputSurface, WeftDrmData)
- drm.rs: replace skeleton with complete backend libseat session,
 udev device enumeration, libinput event source, connector scanning
 via smithay-drm-extras DrmScanner, DrmOutputManager initialisation
 per CRTC, VBlank-driven render_output, sd_notify(READY=1)
- state.rs: add drm: Option<WeftDrmData> field; route dmabuf import
 through GPU manager when the DRM path is active
- Cargo.toml: add renderer_multi, use_system_lib Smithay features;
 add smithay-drm-extras and sd-notify Linux dependencies

render_output submits a clear-colour-only frame to establish
the VBlank pipeline. Surface compositing is wired up in a subsequent commit.
2026-03-10 22:32:21 +01:00
feb69be199 feat(compositor): add weft-compositor crate
- Implement WeftCompositorState with all Wayland protocol globals:
 compositor, xdg-shell, layer-shell, shm, dmabuf, output, presentation,
 text-input, input-method, pointer-constraints, cursor-shape, seat.
- Implement process_input_event covering keyboard, pointer (relative +
 absolute), axis, touch, and all gesture types (swipe, pinch, hold).
- Implement Winit backend with damage-tracked rendering loop and frame
 callbacks.
- Add DRM/KMS backend skeleton: libseat session, udev device discovery,
 calloop integration (rendering path deferred).
- Add infra/systemd/weft-compositor.service (Type=notify).
- Split CI into cross-platform and linux-only jobs.
- Exclude weft-compositor from Windows check scripts.
2026-03-10 20:56:35 +01:00
8b65f81396 Initialize Rust workspace and repository metadata 2026-03-10 18:47:06 +01:00