Skip to content

Commit e6c717a

Browse files
author
Roy Lin
committed
Merge branch 'codex/native-input-conformance'
2 parents 9039839 + 7576939 commit e6c717a

37 files changed

Lines changed: 4620 additions & 267 deletions

.github/workflows/ci.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,15 @@ jobs:
4949
- name: AppKit
5050
os: macos-latest
5151
feature: appkit-native
52+
backend: appkit
5253
- name: GTK4
5354
os: ubuntu-latest
5455
feature: gtk4-native
56+
backend: gtk4
5557
- name: WinUI
5658
os: windows-latest
5759
feature: winui-native
60+
backend: winui
5861
steps:
5962
- name: Checkout
6063
uses: actions/checkout@v5
@@ -85,6 +88,44 @@ jobs:
8588
shell: bash
8689
run: cargo check --locked --all-targets --features "${{ matrix.feature }}"
8790

91+
- name: Generate required native input matrix
92+
shell: bash
93+
run: |
94+
cargo run --locked --bin a3s-gui-native-input-conformance -- manifest "${{ matrix.backend }}" > "native-input-${{ matrix.backend }}.json"
95+
96+
- name: Upload required native input matrix
97+
uses: actions/upload-artifact@v6
98+
with:
99+
name: native-input-requirements-${{ matrix.backend }}
100+
path: native-input-${{ matrix.backend }}.json
101+
if-no-files-found: error
102+
103+
- name: Install Windows App Runtime 1.7 for OS-input smoke
104+
if: runner.os == 'Windows' && github.event_name == 'workflow_dispatch'
105+
shell: pwsh
106+
run: |
107+
$installer = Join-Path $env:RUNNER_TEMP 'WindowsAppRuntimeInstall-x64.exe'
108+
Invoke-WebRequest 'https://aka.ms/windowsappsdk/1.7/1.7.260224002/windowsappruntimeinstall-x64.exe' -OutFile $installer
109+
& $installer --quiet
110+
if ($LASTEXITCODE -ne 0) {
111+
throw "Windows App Runtime installer exited with code $LASTEXITCODE"
112+
}
113+
114+
- name: Capture WinUI button-backed role OS-input smoke evidence
115+
if: runner.os == 'Windows' && github.event_name == 'workflow_dispatch'
116+
shell: pwsh
117+
run: >-
118+
cargo run --locked --no-default-features --features winui-native
119+
--bin a3s-gui-winui-input-smoke -- native-input-winui-smoke.json
120+
121+
- name: Upload WinUI button-backed role OS-input smoke evidence
122+
if: always() && runner.os == 'Windows' && github.event_name == 'workflow_dispatch'
123+
uses: actions/upload-artifact@v6
124+
with:
125+
name: native-input-smoke-winui
126+
path: native-input-winui-smoke.json
127+
if-no-files-found: error
128+
88129
bundle-smoke:
89130
name: Bundle Smoke (${{ matrix.name }})
90131
runs-on: ${{ matrix.os }}

Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ categories = ["gui", "rendering", "accessibility"]
1414
name = "a3s_gui"
1515
path = "src/lib.rs"
1616

17+
[[bin]]
18+
name = "a3s-gui-native-input-conformance"
19+
path = "src/bin/native_input_conformance.rs"
20+
21+
[[bin]]
22+
name = "a3s-gui-winui-input-smoke"
23+
path = "src/bin/winui_input_smoke.rs"
24+
required-features = ["winui-native"]
25+
1726
[[example]]
1827
name = "component_playground"
1928
required-features = ["authoring"]
@@ -178,8 +187,13 @@ gtk4_crate = { package = "gtk4", version = "0.11.4", optional = true }
178187
[target.'cfg(target_os = "windows")'.dependencies]
179188
windows = { version = "0.62.0", optional = true, features = [
180189
"Foundation",
190+
"Win32_System_Com",
191+
"Win32_System_SystemInformation",
192+
"Win32_UI_Controls",
193+
"Win32_UI_Accessibility",
181194
"Win32_Foundation",
182195
"Win32_UI_Input_KeyboardAndMouse",
196+
"Win32_UI_Input_Pointer",
183197
"Win32_UI_Shell",
184198
"Win32_UI_WindowsAndMessaging",
185199
] }

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,18 @@ object graph at the host boundary.
2727
background inertness, nested focus containment/restoration, typed anchored
2828
overlay placement, inherited locale/direction, versioned capability
2929
reporting, shared native event-source state machines, and accessibility
30-
conformance checks.
31-
Role-edge input parity, layout-aware collection page movement, IME/dead-key
30+
conformance checks. Versioned native-input manifests expand every role marked
31+
native into strict OS-automation cases; adapter and headless traces cannot
32+
satisfy that evidence gate.
33+
A WinUI smoke runner records real XAML Button mouse and keyboard input through
34+
`SendInput`, pen and touch through Windows synthetic pointer injection, and
35+
assistive activation through UI Automation `InvokePattern`. It covers
36+
activation, mouse/pen/touch cancellation, keyed-rerender cancellation, and
37+
disabled input for all 14 cases of each button-backed `Button`,
38+
`DisclosureSummary`, `Link`, `ImageMapArea`, and `MenuItem` role (70 of 98
39+
WinUI cases); `ListBoxItem` and `TreeItem` remain outstanding.
40+
Populating the remaining manifest cases with real platform automation,
41+
role-edge input parity, layout-aware collection page movement, IME/dead-key
3242
conformance, measured overlay collision/arrow conformance and scroll locking,
3343
remaining native focus conformance, and locale formatting are still in
3444
progress.
@@ -175,6 +185,7 @@ platform-matched typography, for example `w-[396px]`, `rounded-[12px]`, and
175185
| AppKit native surface | Usable for macOS smoke apps with role-aware press/hover/focus/key translation, controls, menus, close actions, and typed post-mount `autoFocus`. |
176186
| GTK4 native surface | Usable for Linux smoke apps with role-aware press/hover/focus/key translation, controls, menus, dialogs, and scroll containers. |
177187
| WinUI native surface | Usable for Windows smoke apps with role-aware press/hover/focus/key translation, typed programmatic focus and post-mount `autoFocus`, core controls, resize bounds, close actions, and root-window exit. |
188+
| Native input conformance | Versioned requirement/run/report artifacts and a strict verifier are available. The WinUI smoke harness covers all 70 button-backed role cases (70 of 98); full AppKit, GTK4, and WinUI evidence is not complete. |
178189
| Product app shell | Dogfood-ready. Production distribution still needs signed installers and longer real-world focus/input hardening. |
179190

180191
## Roadmap
@@ -255,6 +266,25 @@ denied, library and example tests, cross-platform planning adapters, and diff
255266
whitespace. On macOS, Linux, or Windows, `just native-ci` additionally runs the
256267
matching native feature's library tests and an all-target compile check.
257268

269+
Generate the exact input cases implied by a backend's native capability claims,
270+
then verify an artifact emitted by an operating-system automation runner:
271+
272+
```bash
273+
just native-input-manifest winui
274+
just native-input-conformance path/to/winui-evidence.json
275+
just winui-input-smoke path/to/winui-button-backed-smoke.json
276+
```
277+
278+
The verifier exits nonzero for missing or duplicate cases, incorrect semantic
279+
event order, modality/target mismatches, incomplete environment identity, or
280+
evidence produced only by an adapter kernel or the portable runtime.
281+
The WinUI smoke command opens a real XAML window and emits a partial artifact;
282+
passing that command validates its 70 captured button-backed role cases, not
283+
the full WinUI manifest. It requires a supported Windows desktop session with
284+
the Windows App Runtime 1.7 framework package available. WinUI examples enter
285+
the XAML-owned application lifecycle with `run_winui_application_staged_async`
286+
and keep native layout and input responsive through the asynchronous app loop.
287+
258288
The default feature set includes RSX authoring and the built-in design system.
259289
The runtime/protocol core is independently buildable without SWC or `rsx_ui`:
260290

docs/architecture.md

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,46 @@ The application or host owns ACL parsing and validation, then passes typed,
154154
immutable capability grants across the outer boundary. The GUI core owns the
155155
capability types but must not depend on the ACL parser or its AST.
156156

157+
## Native Input Evidence Boundary
158+
159+
`NativeCapabilities` describes the behavior a backend claims for each role.
160+
`NativeInputConformanceManifestV1` turns native press claims into a canonical
161+
role/scenario matrix, including activation, cancellation, disabled-state, and
162+
keyed-rerender cases for each applicable input modality. This derivation keeps
163+
capability declarations and test scope in one source of truth.
164+
165+
An operating-system runner records semantic events with
166+
`NativeInputConformanceObservationV1::capture` and submits a versioned
167+
`NativeInputConformanceRunV1`. Capture excludes event values and raw key data.
168+
The verifier compares observations against the generated manifest, checks
169+
provenance and environment identity, and produces a serializable
170+
`NativeInputConformanceReportV1`. Headless and adapter-kernel traces are
171+
explicitly ineligible to prove a native claim even when their event sequence is
172+
identical.
173+
174+
File I/O remains outside the GUI core. The
175+
`a3s-gui-native-input-conformance` binary is a thin adapter that prints current
176+
manifests and reads evidence JSON for CI. Native backends own the OS automation
177+
driver and event-loop integration; the portable verifier owns only the shared
178+
semantic contract.
179+
180+
The Windows-only `a3s-gui-winui-input-smoke` harness keeps automation outside
181+
the runtime library. It mounts each button-backed semantic role as a real WinUI
182+
Button, confirms that the mounted native role matches the manifest case,
183+
locates and verifies its enabled state through the OS UI Automation tree,
184+
injects mouse and Enter-key input with `SendInput`, injects pen and touch with
185+
`CreateSyntheticPointerDevice` / `InjectSyntheticPointerInput`, and invokes its
186+
assistive `InvokePattern`. Mouse cancellation releases the real XAML pointer
187+
capture after the injected press; pen and touch cancellation inject Windows'
188+
cancelled-up pointer state. The production WinUI message loop records successful,
189+
cancelled, keyed-rerender, and disabled responses. The harness validates all 70
190+
observations for `Button`, `DisclosureSummary`, `Link`, `ImageMapArea`, and
191+
`MenuItem` with the strict manifest verifier while allowing only `ListBoxItem`
192+
and `TreeItem` to remain `MissingObservation`. Its output therefore remains an
193+
explicitly partial run artifact rather than a conformance report. The harness
194+
requires an interactive Windows desktop and the Windows App Runtime 1.7
195+
framework package used by the WinUI backend's dynamic dependency.
196+
157197
## NativeHost Boundary
158198

159199
Every platform adapter implements the same host operations:
@@ -1003,31 +1043,35 @@ Feature-gated platform executor surfaces:
10031043
its native `Switch` semantic in the IR; with `winio-winui3` 0.4.2, the native
10041044
surface temporarily backs that state with a WinUI `CheckBox` because the
10051045
generated bindings do not expose `ToggleSwitch` yet.
1006-
The WinUI message pump also maps `WM_KEYDOWN`, `WM_KEYUP`,
1007-
`WM_SYSKEYDOWN`, and `WM_SYSKEYUP` records into A3S keyboard events. Since
1008-
`winio-winui3` 0.4.2 does not expose strong `KeyRoutedEvent` registration
1009-
methods, keyboard events target the currently focused A3S node tracked from
1010-
WinUI focus callbacks, falling back to the root surface for window-level
1011-
routes. The same binding version leaves programmatic `Focus` unwrapped; the
1012-
native surface bridges its fixed WinRT ABI from an adjacent public vtable
1013-
method and still uses WinUI focus callbacks as the observable event source.
1046+
WinUI pointer handlers opt into handled routed events so Button-backed
1047+
controls preserve mouse, pen, and touch press phases that XAML class handling
1048+
would otherwise hide. Preview key-down and key-up handlers likewise feed the
1049+
shared keyboard state machine before Button activation, avoiding duplicate
1050+
semantic presses from the later `Click` callback. `winio-winui3` 0.4.2 does
1051+
not generate these registration methods, so the native surface isolates the
1052+
fixed WinRT ABI calls in a small adapter. The same binding version leaves
1053+
programmatic `Focus` unwrapped; that call uses the same isolated ABI approach
1054+
while WinUI focus callbacks remain the observable event source.
10141055
Window close requests are observed through the HWND message path: the surface
10151056
installs a close-event subclass on each WinUI window and enqueues
10161057
`NativeEventKind::Close` when `WM_CLOSE` arrives. Content dialogs register
10171058
WinUI's `Closing` callback and enqueue the same event for dialog `onClose`
10181059
actions while clearing local open-dialog tracking. Draining those native close
10191060
events releases the retained `ShowAsync` operation; programmatic hides are
10201061
suppressed during render-driven teardown.
1021-
`WinUiRuntimeApp` provides the embedded app loop for this backend: it renders
1022-
into `WinUiNativeSurface`, pumps the Windows message queue, drains queued A3S
1023-
native events, runs the application reducer, rerenders the next frame, and
1024-
observes the root WinUI window handle so `run_winui_while` can stop when the
1025-
user closes the surface.
1062+
WinUI surfaces are created inside the XAML-owned `Application::Start`
1063+
lifecycle. `run_winui_application_staged_async` renders and activates the
1064+
first window during `OnLaunched`, then polls the application future through
1065+
`DispatcherQueue` turns after launch returns. `WinUiRuntimeApp` drains queued
1066+
A3S native events, runs the reducer, rerenders the next frame, and observes
1067+
the root window so `run_winui_while_async` can stop when the user closes the
1068+
surface without blocking XAML layout or input dispatch.
10261069
The `winui_controls` example runs the same shared native controls smoke
10271070
frame against real WinUI widgets.
10281071
The `winui_dogfood` example runs the shared task editor and review workflow
10291072
through the same reducer loop and WinUI event queue, including window close
1030-
lifecycle actions and state-driven app loop exit through `run_winui_while`.
1073+
lifecycle actions and state-driven app loop exit through
1074+
`run_winui_while_async`.
10311075
- `gtk4`: maps classes such as `gtk::Button`, `gtk::Entry`,
10321076
`gtk::SearchEntry`, `gtk::PasswordEntry`, `gtk::SpinButton`, and
10331077
`gtk::TextView` into GTK object kinds behind `Gtk4WidgetDriver` and replays

docs/react-aria-native.md

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,68 @@ conservative and role overrides opt into verified behavior. This prevents a
305305
native wrapper, menu model, or logical collection item from being advertised as
306306
interactive merely because another role on that backend is interactive.
307307

308+
## Native Input Evidence Gate
309+
310+
`NativeInputConformanceManifestV1::from_capabilities` expands each role whose
311+
press support is marked `Native` into a machine-readable automation matrix. A
312+
complete press-lifecycle role requires separate mouse and pen activation,
313+
cancellation, and disabled cases, plus keyboard, virtual assistive activation,
314+
and keyed-rerender cancellation. GTK4 and WinUI also require touch activation,
315+
cancellation, and disabled-touch cases. AppKit and GTK4 menu items retain an
316+
explicit terminal-activation exception because their menu models do not expose
317+
the generic view event source. The expected successful activation order follows
318+
React Aria's [`usePress`](https://react-aria.adobe.com/usePress) contract:
319+
press start, press up, press end, then terminal press.
320+
321+
The evidence boundary is intentionally strict:
322+
323+
- `NativeInputConformanceObservationV1::capture` retains only redacted semantic
324+
press events; raw key values and native event payload values are excluded.
325+
- A verifier derives expectations from the current capability manifest rather
326+
than accepting expectations supplied by an evidence file.
327+
- Exactly one observation is required per case. Event order, target identity,
328+
modality, keyboard activation deduplication, and click count are checked.
329+
- Only `OperatingSystemAutomation` evidence with matching OS, OS version,
330+
toolkit version, and automation-driver identity is eligible. Adapter-kernel
331+
and portable-runtime traces remain useful tests but cannot prove native
332+
support.
333+
- The native CI matrix publishes the generated AppKit, GTK4, and WinUI
334+
requirement artifacts so missing platform evidence is explicit and
335+
reviewable.
336+
337+
Generate or verify artifacts with:
338+
339+
```bash
340+
just native-input-manifest appkit
341+
just native-input-manifest gtk4
342+
just native-input-manifest winui
343+
just native-input-conformance path/to/evidence.json
344+
just winui-input-smoke path/to/winui-button-backed-smoke.json
345+
```
346+
347+
The WinUI smoke harness covers the XAML Button-backed `Button`,
348+
`DisclosureSummary`, `Link`, `ImageMapArea`, and `MenuItem` roles. Mouse and
349+
keyboard use Windows `SendInput`, pen and touch use synthetic pointer injection,
350+
and assistive activation uses UI Automation `InvokePattern`. It exercises
351+
successful activation, mouse/pen/touch cancellation, keyed-rerender
352+
cancellation, and disabled input inside the production XAML application
353+
lifecycle. Handled routed pointer events and preview key events feed the same
354+
portable press state machine while the asynchronous dispatcher loop leaves
355+
WinUI layout and input dispatch unblocked.
356+
The strict verifier accepts no semantic defect in the complete 70-case slice,
357+
but the 28 `ListBoxItem` and `TreeItem` WinUI cases remain. The P0 gap is
358+
therefore still open. Native automation drivers still need to execute every
359+
generated case and submit passing run artifacts on real macOS, Linux, and
360+
Windows runners.
361+
308362
## Known Gaps
309363

310364
The following behavior systems are still incomplete or only represented as
311365
props:
312366

313367
| Priority | Area | Required outcome |
314368
| --- | --- | --- |
315-
| P0 | Native input conformance | Add platform-run pointer, keyboard, assistive activation, and cancellation fixtures for every role currently marked native, then close or document remaining menu/item exceptions. |
369+
| P0 | Native input conformance | Populate the generated V1 manifests with platform-run mouse, pen, touch where applicable, keyboard, assistive activation, disabled, cancellation, and keyed-rerender fixtures for every role currently marked native; then close or retain evidence-backed menu/item exceptions. |
316370
| P1 | Event propagation | Add platform-run conformance fixtures for conditional `Stop`/`Continue` across nested native controls. |
317371
| P1 | Focus management | Add platform-run conformance fixtures for post-mount `autoFocus`, nested containment, and restoration. |
318372
| P1 | Collections and selection | Add layout-aware page navigation and complete IME/dead-key typeahead conformance. |
@@ -341,3 +395,5 @@ A behavior is complete only when all of the following are true:
341395
asserted semantically.
342396
5. The public documentation describes the supported contract without implying
343397
broader parity.
398+
6. A real operating-system automation run satisfies the generated versioned
399+
manifest; portable or adapter-only tests do not count as native evidence.

docs/roadmap.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ The following foundations are implemented and are not roadmap placeholders:
8282
versioned `positionOverlay` commands, recovery replay, and AppKit, GTK4,
8383
WinUI, and headless projections
8484
- AppKit, GTK4, and WinUI build/test matrices with real native dogfood surfaces
85+
- versioned native-input requirement, run, and report artifacts derived from
86+
role capabilities; a strict verifier rejects adapter/headless traces as
87+
native evidence, and CI publishes each backend's outstanding matrix
88+
- a Windows OS-input smoke harness that drives real XAML Button-backed
89+
`Button`, `DisclosureSummary`, `Link`, `ImageMapArea`, and `MenuItem` roles
90+
through `SendInput`, synthetic pen/touch injection, and UI Automation,
91+
currently covering all 70 of their cases (70 of 98 required WinUI cases)
8592

8693
## Delivery Dependency
8794

@@ -357,8 +364,9 @@ Target: days 151-180. Status: planned after M5.
357364
surfaces for menus, dialogs, keyboard input, focus, text input, collection
358365
selection, table navigation, close, and recovery flows.
359366
- Generate or validate the documented platform support matrix from backend
360-
declarations and real automation results. Placeholder behavior does not count
361-
as supported.
367+
declarations and real automation results. Feed each platform run through the
368+
generated `NativeInputConformanceManifestV1`; placeholder, adapter-kernel, and
369+
portable-runtime behavior does not count as supported.
362370
- Replace thread park/unpark fallbacks with real AppKit, GLib, and WinUI event
363371
loop wake messages for externally completed work.
364372
- Add native failure injection for partial commit, teardown, fresh-executor

0 commit comments

Comments
 (0)