diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-01 19:34:40 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-01 19:34:40 +0200 |
| commit | 9b9758e5ee2564b04685eadf2b9ce23dd1060b86 (patch) | |
| tree | 57fce9e715510ec70df70dc399b4440f3480fd77 /CONTEXT.md | |
| parent | fix pi-lens warnings: unwraps, shell SC2046, dup code (fmtbuf/scripts/hexdump... (diff) | |
| download | rs-dos-9b9758e5ee2564b04685eadf2b9ce23dd1060b86.tar.gz rs-dos-9b9758e5ee2564b04685eadf2b9ce23dd1060b86.zip | |
rsnix v0.4.0: GUI input foundation — PS/2 mouse + scheduler message queues
Mouse (IRQ12, stream mode): ByteRing generalized for both PS/2 devices,
ISR routes data-port bytes by the controller's aux bit; mouse_init with
bounded-ACK handshake (boots without a mouse); packet decoder with
bit-3 resync, screen-relative deltas, buttons. Exposed as /dev/mouse,
the `mouse` stream command, and `make test-mouse`.
Message queues (ADR 0003's GUI-era primitive): every task gets a static
16-slot TaskMsg ring; send/recv/recv_blocking with direct wake + tick
scan safety net for the lost-wakeup race; `msgrecv`/`send` commands,
`mouse <id>` routes real IRQ12 events into a task's queue, jobs shows
q=. `make test-msg` + `make test-disk` regression pass.
Diffstat (limited to '')
| -rw-r--r-- | CONTEXT.md | 73 |
1 files changed, 72 insertions, 1 deletions
| @@ -113,7 +113,9 @@ Workspace version is `a.b.c` (single version for all crates): | |||
| 113 | 113 | ||
| 114 | Bump the workspace version in the root `Cargo.toml` when a change of | 114 | Bump the workspace version in the root `Cargo.toml` when a change of |
| 115 | that class lands — do not leave the version to drift. Current: | 115 | that class lands — do not leave the version to drift. Current: |
| 116 | `0.2.0`; the multitasking work lands as a `b` bump (`0.3.0`). | 116 | `0.4.0` — the GUI input foundation (PS/2 mouse + scheduler message |
| 117 | queues) landed as a `b` bump; the GUI era itself is reserved for the | ||
| 118 | `a` bump (1.0.0). | ||
| 117 | 119 | ||
| 118 | ## Project layout | 120 | ## Project layout |
| 119 | 121 | ||
| @@ -404,6 +406,75 @@ Workspace of four crates, all built for the `x86_64-unknown-uefi` target: | |||
| 404 | `bg overflow` dies on the guard page with the machine intact; full | 406 | `bg overflow` dies on the guard page with the machine intact; full |
| 405 | regression (rescan/til/klog/grammar/pager) re-passes. | 407 | regression (rescan/til/klog/grammar/pager) re-passes. |
| 406 | 408 | ||
| 409 | ✅ **PS/2 mouse** — auxiliary-channel driver on IRQ12, stream mode | ||
| 410 | (3-byte packets), the GUI's second input device: | ||
| 411 | |||
| 412 | - `interrupts`: the keyboard byte ring is generalised to `ByteRing`; | ||
| 413 | IRQ12 (vector 44) gets its own ring and stub. Both PS/2 handlers | ||
| 414 | route each data-port byte by the controller status register's aux | ||
| 415 | bit (bit 5), so keyboard and mouse never steal each other's bytes | ||
| 416 | even when both are pending at once. PIC unmask now covers IRQ2 | ||
| 417 | (cascade) and IRQ12 (slave bit 4). | ||
| 418 | - `ps2`: `mouse_init()` enables the aux port, sets config bits | ||
| 419 | IRQ12+aux-clock (preserving the keyboard bit), then `set defaults` | ||
| 420 | (100 Hz / 4 counts/mm / 1:1) + `enable reporting` via the 0xD4 | ||
| 421 | channel — each waits for the device ACK with a bounded timeout so | ||
| 422 | a machine without a mouse boots normally (log: `ps2: mouse not | ||
| 423 | present`). `try_poll_mouse()` reassembles packets in main | ||
| 424 | context from the raw byte ring; the packet-start marker (bit 3) | ||
| 425 | resyncs the stream after any lost byte. Events are **screen- | ||
| 426 | relative** (`dx` + = right, `dy` + = down — the PS/2 Y-up wire | ||
| 427 | convention is negated), buttons decoded; 9-bit sign extension via | ||
| 428 | the packet sign bits. | ||
| 429 | - `fs`: `/dev/mouse` char device — `cat /dev/mouse` drains up to 32 | ||
| 430 | pending events (`dx=+N dy=+N buttons=L-R`), reports | ||
| 431 | `mouse: no PS/2 mouse` / `mouse: no events` otherwise. | ||
| 432 | - shell: `mouse` command streams live events (10 ms poll via | ||
| 433 | `sleep_ms`) until ^C. | ||
| 434 | |||
| 435 | Verified in QEMU (`make test-mouse`, headless, via the monitor's | ||
| 436 | `mouse_move`/`mouse_button` HMP commands): boot line `ps2: mouse | ||
| 437 | ready`, `/dev/mouse` drains a pre-consumer move (`8,4`), the live | ||
| 438 | stream decodes `mouse_move 20 7` → `dx=+20 dy=+7 buttons=---`, | ||
| 439 | `mouse_button 1` → `buttons=L--`, a held-button move | ||
| 440 | (`-5,-3` while L down) → `dx=-5 dy=-3 buttons=L--`, release, | ||
| 441 | and ^C stops the stream. `make test-disk` regression re-passes. | ||
| 442 | |||
| 443 | ✅ **Scheduler message queues (the GUI-era primitive from ADR 0003)** — | ||
| 444 | every task gets a static 16-slot ring of `TaskMsg` (4 KB in .bss, | ||
| 445 | no ISR-time allocation): | ||
| 446 | |||
| 447 | - `TaskMsg` = `Key(KeyEvent)` | `Mouse(MouseEvent)` | `Timer` | | ||
| 448 | `User(u64)` — input uses the same decoded types the PS/2 drivers | ||
| 449 | produce, so window input routing is literally "post what the | ||
| 450 | console would have gotten"; `User(u64)` is the generic escape | ||
| 451 | hatch. `KeyEvent`/`MouseEvent` derive Copy for ring storage. | ||
| 452 | - `sched::send(id, msg)` — posts from any context (task, shell, ISR) | ||
| 453 | under the scheduler lock; wakes a Blocked task straight to Ready; | ||
| 454 | errors cleanly on a gone task or a full queue (input is lossy | ||
| 455 | under extreme load by design). `recv()` pops without blocking; | ||
| 456 | `recv_blocking()` marks the task Blocked and yields. **Lost-wakeup | ||
| 457 | safety net**: the tick scan also wakes Blocked tasks whose queue is | ||
| 458 | non-empty, so a task that blocked *after* its message arrived (or | ||
| 459 | whose poster never woke it) is picked up within one 10 ms tick. | ||
| 460 | - shell: `msgrecv` spawns a task that blocks on recv and logs every | ||
| 461 | message to serial (`msgrecv[2]: Key(F3)` …); `send <id> <type> | ||
| 462 | [args]` posts `user <n>` / `timer` / `mouse <dx> <dy> <btn>` | ||
| 463 | (1=L 2=R 4=M) / `key <name>` (enter esc tab space bksp arrows | ||
| 464 | home end pgup pgdn del ctrl-c f1..f12, or a single char); `jobs` | ||
| 465 | now shows `q=` (pending message count). | ||
| 466 | - **Real-input routing, proven**: `mouse <id>` (angle brackets) streams | ||
| 467 | decoded IRQ12 events into that task's queue instead of the console | ||
| 468 | — the exact loop the window manager will run. `msgrecv` + `mouse 2` | ||
| 469 | - `mouse_move 20 7` showed `msgrecv[2]: Mouse(+20,+7 …)` in QEMU. | ||
| 470 | |||
| 471 | Verified in QEMU (`make test-msg`): all six message types delivered | ||
| 472 | in order (user, timer, key enter/ctrl-c/f3, synthesized mouse), | ||
| 473 | real routed mouse moves (including a held-button move), the 17th | ||
| 474 | send against a 16-cap queue reports `queue full` (tested against | ||
| 475 | the shell's never-consumed queue), and `jobs` shows q=0 + task back | ||
| 476 | to `blocked` after draining. `make test-disk` regression re-passes. | ||
| 477 | |||
| 407 | ## Known issues / perf notes | 478 | ## Known issues / perf notes |
| 408 | 479 | ||
| 409 | - **Scroll cost** — scrolling still shifts the whole framebuffer (a | 480 | - **Scroll cost** — scrolling still shifts the whole framebuffer (a |
