Skip to content

feat(input): add mode-aware mouse, paste, and focus input - #282

Draft
Alexey Zhokhov (donbeave) wants to merge 7 commits into
microsoft:mainfrom
tailrocks:goal/u07-checked-input
Draft

Alexey Zhokhov (donbeave) wants to merge 7 commits into
microsoft:mainfrom
tailrocks:goal/u07-checked-input

Conversation

@donbeave

@donbeave Alexey Zhokhov (donbeave) commented Oct 25, 2026 •

Copy link
Copy Markdown

Depends-On: none
(Branch is based directly on main f2760592; all commits and touched
files are this feature's own: engine.rs, session.rs, runtime.rs,
terminal/emu.rs, tests/checked_input.rs,
tests/public_session_checked.rs.)

Summary

Adds checked (mode-aware) input paths alongside the legacy raw-write
methods, so tests send mouse, paste, and focus events that match what the
terminal actually negotiated:

  • Session::mouse_checked encodes click/motion/drag reports to exact
    bytes; releases in X10 mode, out-of-bounds coordinates, and
    history-viewport writes fail before anything is written.
  • Session::paste sends bracketed-paste framed text gated on the paste
    mode; embedded terminators are rejected before the write.
  • Session::focus reports focus in/out gated on the focus-event mode.
  • All three are exposed on the public Session and delegated through
    Engine; failures use a typed CheckedInputError so callers can tell
    unsupported encoding/tracking apart from bad coordinates.
  • Legacy mouse and raw-write behavior is unchanged.

Head: 2b2958ee96c7322d7349583c103dc4aa8f55ab97
(Refreshed 2026-10-26 for the amended head; amend of 28ef051c adding
only the Signed-off-by trailer, content identical; working tree clean at
refresh time.)

Testing

Focused suites, run locally with nextest (locked, offline) at head
2b2958ee (2026-10-26), per the push-verifier (amended from 28ef051c
with only the Signed-off-by trailer added; content identical, so results
carry over):

  • cargo nextest run -p tui-test-rs --test checked_input:
    12 tests run, 12 passed
  • cargo nextest run -p tui-test-rs --test public_session_checked:
    4 tests run, 4 passed
  • cargo fmt --check: clean
  • cargo clippy -D warnings: clean

Known limitations

  • Windows execution unverified here (macOS only); the Windows raw-input
    console setup in the fixture still needs a Windows run.
  • Merging needs upstream maintainer review and green CI.

Add Session::mouse_checked, Session::paste, and Session::focus with
typed CheckedInputError (Unsupported/Rejected/Failed) failures. Modes
come from one terminal state lock; validation precedes any byte write.
Raw input and legacy mouse paths are unchanged.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Re-export CheckedInputError and CheckedMouseAction from the engine
module and add thin Engine::mouse_checked/paste/focus delegates. The
session module stays private; no Operation or CLI protocol change.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Cover U07-A (enabled, disabled, and changed modes), U07-B (exact
bytes for click, drag, motion, paste, and focus from a controlled
fixture child), U07-C (unsupported encoding, out-of-bounds, and
history viewport fail before any byte write), and U07-D (legacy
mouse and raw write keep their behavior).

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
…ariants, doc + comment

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
@donbeave

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

… tracking

R10: add delegating mouse_checked, paste, and focus methods on
runtime::Session so external consumers no longer need Engine for the
checked-input path. Covered by tests/public_session_checked.rs, which
drives the public Session type only, including under a non-default
execution context.

R12: track the exact negotiated mouse selector in a private
NegotiatedTracking discriminator instead of folding mode 9 into the
click level. X10 (mode 9) reports button presses only, so a checked
release now fails with Unsupported before writing; enabling an
unsupported selector such as 1001 clears the previous level instead
of leaving it stale. The public MouseMode enum is unchanged.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Mirror the cfg(windows) SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 0)
block from checked_input.rs run_fixture verbatim so the public-session
suite shares one correct platform setup. No behavior change on Linux/macOS.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
@donbeave Alexey Zhokhov (donbeave) changed the title Add mode-aware programmatic input feat(input): add mode-aware mouse, paste, and focus input Oct 26, 2026
mouse_checked, paste, and focus take viewport_input before sampling
and hold it through the completed PTY write, while the terminal-state
lock is released before blocking I/O. A concurrent scroll either waits
for the report or runs first and makes the check observe history;
it can no longer complete first and then permit a stale write.

paste routes through the common input gate plus the keyboard scroll
policy: both framed and raw paths scroll to the bottom first, so the
paste always targets the live screen. Empty input keeps its documented
behavior (framed pair still sent with markers on; nothing written or
scrolled with markers off).

Docs: X10 (mode 9) presses with SGR (1000/1002/1003) across session,
engine, and runtime layers; CheckedInputError::Failed covers
no-session/emulator faults with source messages preserved. Re-export
CheckedInputError/CheckedMouseAction at the crate root and share the
checked-input child fixture between the integration suites.

Probes: checked_mouse/checked_paste_waits_for_blocked_write_before_
scrolling and checked_paste_scrolls_to_bottom_before_writing fail on
the base commit and pass with the gate held.

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Alexey Zhokhov (donbeave) added a commit to tailrocks/tuiscotti that referenced this pull request Oct 27, 2026
Migrate tuiscotti onto the tui-test engine as its terminal runtime.

Upstream (`tui-test`) owns the terminal engine: child processes,
emulation, sessions, input, waiting, rendering, fonts, and pixel
comparison. Tuiscotti keeps the application testing layer: pure-view
capture, versioned samples, frozen baseline checks, explicit approval,
static reports, and the offline CLI (`check` / `accept` / `render` /
`report`, exit codes 0/1/2/3). The previous built-in live engine,
renderer, recorder, and second approval engine are removed, leaving the
two shipping crates (`tuiscotti`, `tuiscotti-cli`).

## Provisional fork dependency

The new `tui-test` APIs this PR needs are still unmerged upstream, so
this branch temporarily depends on a pinned fork via `[patch]`:

- `donbeave/tui-test`, branch `goal/qual-stack-r5`, pinned at
`81da26fa9dc29664a897dc802be816a4432c0618`

The pin is provisional. Once the upstream PRs below merge and a registry
release contains the new APIs, the `[patch]` is removed in favor of the
registry version.

## Upstream PRs (all 11, all open)

| PR | Change | Head SHA | Status |
|----|--------|----------|--------|
|
[microsoft/tui-test#276](microsoft/tui-test#276)
| Explicit read-only snapshot policy |
`fc0150160c25ea83532b18f9507f9f1a155b91e4` | Reworked + verified |
|
[microsoft/tui-test#277](microsoft/tui-test#277)
| RGBA image comparison for pixel equality |
`4fb316aa56c65cc659b4a43ee4921c45e33cecc0` | Approved as-is;
dependency-only, no rework |
|
[microsoft/tui-test#278](microsoft/tui-test#278)
| Owned terminal snapshot API |
`334c474d1c621c373333e9dc24e549623a133570` | Reworked + verified |
|
[microsoft/tui-test#279](microsoft/tui-test#279)
| Raw byte input for Rust sessions |
`3b1439c313e88d0a8867dfbcdadabb28624dc5b5` | Reworked + verified |
|
[microsoft/tui-test#280](microsoft/tui-test#280)
| Public rendering for offline snapshots |
`378036846a4e85174104e0f9c153b0a0f8f84161` | Reworked + verified |
|
[microsoft/tui-test#281](microsoft/tui-test#281)
| Configurable quiet waits and request cancellation |
`103d296494f251921f8cd61d3f6ce2f7e152690d` | Reworked + verified |
|
[microsoft/tui-test#282](microsoft/tui-test#282)
| Mode-aware programmatic input |
`212ba1db26987a45537708e98c82db77c90bb734` | Reworked + verified |
|
[microsoft/tui-test#283](microsoft/tui-test#283)
| Explicit underline colors in rendering |
`fff55c122ae210efbe9a68d1ac8683b1a8856d` | Reworked + verified |
|
[microsoft/tui-test#284](microsoft/tui-test#284)
| Explicit pinned-font rendering |
`2bdaa3f8d3692514a6cb9c1ab844e880ca8d0f23` | Reworked + verified |
|
[microsoft/tui-test#285](microsoft/tui-test#285)
| Combined and wide cell text rendering |
`edf2b525d6f8a5710cd56440545437ff4f146727` | Reworked + verified |
|
[microsoft/tui-test#286](microsoft/tui-test#286)
| Explicit child launch settings |
`d5d9bbab9ac4e8a5e0562175497661b7b1a4b158` | Reworked + verified |

"Reworked + verified" means the PR went through a rework round after
independent review, and its per-task suites plus the integrated stack
re-ran green. #277 needed no changes — its head is unchanged since
review — so it is listed as a pure dependency.

Upstream merge order: #278 before #280 and #281; #280 before #283 and
#284; #284 before #285. #276, #277, #279, #282, and #286 are independent
and can merge in any order.

## Verification (at PR head `a056f8f60bae1bf81429b575076c86aed3e6717b`)

- Tests: 209/209 passing in both the default-features and
no-default-features lanes.
- `cargo fmt --check` clean; `cargo clippy --all-targets -D warnings`
clean (default and no-default features).
- Downstream CI: [run
40408516574](https://github.com/tailrocks/tuiscotti/actions/runs/40408516574)
(in progress, at `a056f8f`); prior run
[40390626472](https://github.com/tailrocks/tuiscotti/actions/runs/40390626472)
success at `9ae97ff`.

## Known limitations

- Windows/macOS runner coverage is still pending; platform-specific
behavior (for example raw-byte PTY paths) is unverified there.
- The upstream PRs currently report only CLA checks; Rust CI on the
upstream side still needs maintainer workflow approval, so upstream-side
proof rests on the recorded per-task suites plus this downstream
integration.
- No crates.io release contains the new APIs yet, so registry adoption
is blocked until the upstream PRs merge and release. The provisional
fork pin above supports merging this PR only; it is not a release path.

## Depends-on / merge order

This PR depends on all eleven upstream PRs merging (in the order noted
above) plus a registry release; only then does the provisional `[patch]`
come out. Nothing in this PR can ship to crates.io before that. Upstream
maintainers: the stack is additive-only, and the pinned fork branch
tracks the exact heads listed above.

---------

Signed-off-by: Alexey Zhokhov <alexey@zhokhov.com>
Signed-off-by: Muse Code <muse-code@meta.com>
Signed-off-by: Muse Code <muse@example.local>
Co-authored-by: Muse Code <muse-code@meta.com>
Co-authored-by: Muse Code <muse@example.local>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant