mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-22 12:14:05 +09:00
tsvm new api: con.setFullscreen
This commit is contained in:
89
CLAUDE.md
89
CLAUDE.md
@@ -556,27 +556,61 @@ arithmetic (no regression outside vtmgr). Applied so far in
|
|||||||
`assets/disk0/tvdos/bin/taut.js` and `assets/disk0/hopper/include/aa.mjs`
|
`assets/disk0/tvdos/bin/taut.js` and `assets/disk0/hopper/include/aa.mjs`
|
||||||
(used by `bb.js`). Any future direct-VRAM app needs the same one-line `vaddr`.
|
(used by `bb.js`). Any future direct-VRAM app needs the same one-line `vaddr`.
|
||||||
|
|
||||||
### Raw-keyboard apps must grab (the `con.grabRawKeyboard` pattern)
|
### Fullscreen apps declare themselves (the `con.setFullscreen` pattern)
|
||||||
|
|
||||||
Fullscreen apps that poll the **raw key snapshot** (`sys.poke(-40,1)` then
|
A **fullscreen app** paints the whole screen and polls the **raw key snapshot**
|
||||||
`sys.peek(-41..-48)`) directly — e.g. the DOOM port's `i_input.mjs` — bypass the
|
(`sys.poke(-40,1)` then `sys.peek(-41..-48)`) directly — e.g. the DOOM port's
|
||||||
pane input ring entirely. But the dispatcher keeps the cooked collector (`-39`)
|
`i_input.mjs`, or `playmov` — bypassing the pane input ring. Two problems arise
|
||||||
on and drains typed chars into the *active* pane's ring every frame. While such
|
only under vtmgr: (1) the dispatcher keeps the cooked collector (`-39`) on and
|
||||||
an app is the active pane, every keystroke piles into a ring it never reads, and
|
drains typed chars into the *active* pane's ring every frame, so while a raw app
|
||||||
floods its parent shell the instant the app exits (no bug outside vtmgr, where
|
is the active pane every keystroke piles into a ring it never reads and floods
|
||||||
`-39` is off while a raw app runs). Fix: the pane bootstrap exposes
|
its parent shell the instant it exits (no bug outside vtmgr, where `-39` is off
|
||||||
`con.grabRawKeyboard()` / `con.releaseRawKeyboard()` (write the active VT number
|
while a raw app runs — `readKey` clears it); (2) a *backgrounded* raw app would
|
||||||
into `CTRL+CTRL_RAW_GRAB_VT`); while the active pane holds the grab the
|
still read the physical snapshot, eating the foreground console's input.
|
||||||
dispatcher discards cooked chars and keeps that pane's ring flushed. `con.getch`
|
|
||||||
self-heals a grab leaked by a crashed app (a cooked reader isn't a grabber).
|
This is now **first-class**: an app declares itself fullscreen in **one line** and
|
||||||
A raw-input app feature-detects (`typeof con.grabRawKeyboard === "function"`) and
|
the right thing happens whether or not vtmgr is present. The API lives on the base
|
||||||
grabs/releases around its fullscreen session — DOOM does it in
|
`con` (JS_INIT.js) so it is always defined — **no feature detection**:
|
||||||
`i_video.mjs` `I_InitGraphics`/`I_ShutdownGraphics` (covers every fullscreen
|
|
||||||
mode; shutdown runs in `wadplayer.js`'s `finally`). Complementary: such an app's
|
- `con.setFullscreen(true)` on entry / `con.setFullscreen(false)` on exit.
|
||||||
poll should also no-op when it's *not* the active VT (compare `VT_CTRL_ADDR`
|
Bare metal: state-only no-op. Under vtmgr (pane override): grabs/releases the
|
||||||
byte 0 to `VT_NUM`) so a backgrounded app doesn't eat the foreground console's
|
dispatcher's cooked-input feed via `CTRL+CTRL_RAW_GRAB_VT` (flush type-ahead on
|
||||||
input — DOOM's `I_PollKeys` does this. Any future raw-key app under vtmgr needs
|
grab; the dispatcher keeps the ring empty while held). `con.getch` self-heals a
|
||||||
both.
|
grab leaked by a crashed app (a cooked reader isn't a grabber).
|
||||||
|
- `con.isActiveConsole()` — true on bare metal; under vtmgr, true only while this
|
||||||
|
pane is the foreground VT. Raw apps that read MMIO directly (keys AND mouse)
|
||||||
|
gate their reads on this so a backgrounded app reads nothing.
|
||||||
|
- `con.poll_keys()` is **auto-guarded**: it returns all-zeros unless
|
||||||
|
`con.isActiveConsole()`, so an app that reads keys through `con.poll_keys()`
|
||||||
|
(e.g. `playmov`, `playtaud`) needs no explicit active check — just the
|
||||||
|
`setFullscreen` declaration.
|
||||||
|
- `input.withEvent()` (TVDOS.SYS, the shared key/mouse event API that reads the
|
||||||
|
raw snapshot for `taut`/`zfm`/`edit`/…) is **also auto-guarded**: when
|
||||||
|
`!con.isActiveConsole()` it zeros the key snapshot and pins the mouse, so a
|
||||||
|
backgrounded `withEvent` app emits no events. Such apps therefore only need the
|
||||||
|
`setFullscreen` declaration too.
|
||||||
|
|
||||||
|
The pane's `con.setFullscreen(true)` claims the grab **only while it is the
|
||||||
|
foreground VT**, so an app may re-assert it every frame (the simplest way to
|
||||||
|
re-establish the grab after launching a sub-program — `taut`/`zfm` do this at the
|
||||||
|
top of their event loop) without a backgrounded app clobbering the active
|
||||||
|
grabber's claim on the single `CTRL_RAW_GRAB_VT` byte. A single up-front claim
|
||||||
|
also survives backgrounding (nobody else clears it; the app re-claims on return).
|
||||||
|
|
||||||
|
`con.grabRawKeyboard()`/`con.releaseRawKeyboard()` remain as deprecated thin
|
||||||
|
aliases for `con.setFullscreen(true/false)`. Consumers:
|
||||||
|
- **DOOM** declares fullscreen in `i_video.mjs` `I_InitGraphics`/`I_ShutdownGraphics`
|
||||||
|
(shutdown runs in `wadplayer.js`'s `finally`) and gates `i_input.mjs` `I_PollKeys`
|
||||||
|
(keys + mouse, read via raw MMIO) on `con.isActiveConsole()`.
|
||||||
|
- **`playmov`**, **`playtaud`** declare fullscreen around their session and read
|
||||||
|
keys through the auto-guarded `con.poll_keys()` (no explicit active check).
|
||||||
|
- **`taut`**, **`zfm`** re-assert `con.setFullscreen(true)` at the top of their
|
||||||
|
`input.withEvent` loop (and release on teardown); the active check is automatic
|
||||||
|
via `input.withEvent`.
|
||||||
|
|
||||||
|
Any future fullscreen app just calls `con.setFullscreen(true/false)`; if it reads
|
||||||
|
keys via `con.poll_keys()` or `input.withEvent()` the active guard is automatic,
|
||||||
|
and only an app that reads MMIO with bespoke code needs `con.isActiveConsole()`.
|
||||||
|
|
||||||
### Files
|
### Files
|
||||||
|
|
||||||
@@ -590,10 +624,17 @@ both.
|
|||||||
- `assets/disk0/AUTOEXEC.BAT`: per-console launch (Korean IME + `command -fancy`)
|
- `assets/disk0/AUTOEXEC.BAT`: per-console launch (Korean IME + `command -fancy`)
|
||||||
- `assets/disk0/tvdos/bin/taut.js`, `assets/disk0/hopper/include/aa.mjs`:
|
- `assets/disk0/tvdos/bin/taut.js`, `assets/disk0/hopper/include/aa.mjs`:
|
||||||
`vaddr` VT-aware direct-VRAM addressing
|
`vaddr` VT-aware direct-VRAM addressing
|
||||||
- `assets/disk0/tvdos/VTMGR.SYS`: `CTRL_RAW_GRAB_VT` flag +
|
- `tsvm_core/src/net/torvald/tsvm/JS_INIT.js`: base (bare-metal) `con.setFullscreen`/
|
||||||
`con.grabRawKeyboard`/`releaseRawKeyboard` (raw-keyboard apps); dispatcher
|
`isFullscreen`/`isActiveConsole` + auto-guarded `con.poll_keys`; grab/release aliases
|
||||||
drain honours it. DOOM consumer: `assets/disk0/home/doom/i_video.mjs`
|
- `assets/disk0/tvdos/VTMGR.SYS`: `CTRL_RAW_GRAB_VT` flag + VT-aware overrides of
|
||||||
(grab/release) + `i_input.mjs` (active-VT poll guard)
|
`con.setFullscreen` (claim gated on being the foreground VT) / `isActiveConsole`
|
||||||
|
- `assets/disk0/tvdos/TVDOS.SYS`: `input.withEvent` auto-guard (zeros keys / pins
|
||||||
|
mouse when `!con.isActiveConsole()`) — covers every `withEvent` app
|
||||||
|
- Consumers: `assets/disk0/home/doom/i_video.mjs` (`setFullscreen` in/out) +
|
||||||
|
`i_input.mjs` (`isActiveConsole` guard for keys+mouse);
|
||||||
|
`assets/disk0/tvdos/bin/playmov.js` + `bin/playtaud.js` (`setFullscreen` +
|
||||||
|
auto-guarded `con.poll_keys`); `bin/taut.js` + `bin/zfm.js` (`setFullscreen`
|
||||||
|
re-asserted at the top of their `input.withEvent` loop)
|
||||||
|
|
||||||
### Gotcha: injectIntChk vs. embedded source
|
### Gotcha: injectIntChk vs. embedded source
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ function generateRandomHashStr(len) {
|
|||||||
|
|
||||||
// define TVDOS
|
// define TVDOS
|
||||||
const _TVDOS = {};
|
const _TVDOS = {};
|
||||||
_TVDOS.VERSION = "1.0";
|
_TVDOS.VERSION = "1.4";
|
||||||
_TVDOS.DRIVES = {}; // Object where key-value pair is <drive-letter> : [serial-port, drive-number]
|
_TVDOS.DRIVES = {}; // Object where key-value pair is <drive-letter> : [serial-port, drive-number]
|
||||||
_TVDOS.DRIVEFS = {}; // filesystem driver for the drive letter
|
_TVDOS.DRIVEFS = {}; // filesystem driver for the drive letter
|
||||||
_TVDOS.DRIVEINFO = {};
|
_TVDOS.DRIVEINFO = {};
|
||||||
@@ -1151,6 +1151,23 @@ input.withEvent = function(callback) {
|
|||||||
let mb = sys.peek(-37) & 0xFF; // bits 0..2 = L/R/M held, bit 6 = wheel up, bit 7 = wheel down
|
let mb = sys.peek(-37) & 0xFF; // bits 0..2 = L/R/M held, bit 6 = wheel up, bit 7 = wheel down
|
||||||
let mouse = [mx, my, mb];
|
let mouse = [mx, my, mb];
|
||||||
|
|
||||||
|
// Under vtmgr, a backgrounded console must not see the physical keyboard or
|
||||||
|
// mouse (another VT is in the foreground). con.isActiveConsole() is true on
|
||||||
|
// bare metal and on the foreground pane, false on a background pane. When
|
||||||
|
// inactive, zero the key snapshot and pin the mouse to its previous state so
|
||||||
|
// the dispatch below emits no key/mouse events (still pacing via sleep). This
|
||||||
|
// makes every input.withEvent app auto-ignore background input — no per-app
|
||||||
|
// active check needed; a fullscreen app only has to call con.setFullscreen().
|
||||||
|
if (con.isActiveConsole && !con.isActiveConsole()) {
|
||||||
|
keys = [0,0,0,0,0,0,0,0];
|
||||||
|
if (inputwork.oldMouse && inputwork.oldMouse.length === 3) {
|
||||||
|
mx = inputwork.oldMouse[0]; my = inputwork.oldMouse[1]; mb = inputwork.oldMouse[2] & 0x07;
|
||||||
|
} else {
|
||||||
|
mb = 0;
|
||||||
|
}
|
||||||
|
mouse = [mx, my, mb];
|
||||||
|
}
|
||||||
|
|
||||||
// --- mouse dispatch ---
|
// --- mouse dispatch ---
|
||||||
let oldMouse = inputwork.oldMouse;
|
let oldMouse = inputwork.oldMouse;
|
||||||
let hasOld = oldMouse && oldMouse.length === 3;
|
let hasOld = oldMouse && oldMouse.length === 3;
|
||||||
|
|||||||
@@ -396,10 +396,11 @@ function queuePop() {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
con.getch = function() {
|
con.getch = function() {
|
||||||
// Reading cooked input means we are NOT a raw-keyboard grabber; drop any
|
// Reading cooked input means we are NOT a fullscreen raw-keyboard owner;
|
||||||
// stale grab this VT left set (e.g. a fullscreen app that crashed without
|
// drop any stale grab this VT left set (e.g. a fullscreen app that crashed
|
||||||
// releasing) so the dispatcher resumes feeding our ring.
|
// without calling setFullscreen(false)) so the dispatcher resumes feeding
|
||||||
if (sys.peek(RAW_GRAB_ADDR) === VT_NUM) sys.poke(RAW_GRAB_ADDR, 0)
|
// our ring.
|
||||||
|
if (sys.peek(RAW_GRAB_ADDR) === VT_NUM) { sys.poke(RAW_GRAB_ADDR, 0); con._fullscreen = false }
|
||||||
while (true) {
|
while (true) {
|
||||||
if (sys.peek(ACTIVE_VT_ADDR) === VT_NUM) {
|
if (sys.peek(ACTIVE_VT_ADDR) === VT_NUM) {
|
||||||
let k = queuePop()
|
let k = queuePop()
|
||||||
@@ -408,22 +409,46 @@ con.getch = function() {
|
|||||||
sys.sleep(20)
|
sys.sleep(20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// A fullscreen app that reads the raw keyboard snapshot (-41..-48) directly,
|
// ── Fullscreen / raw-keyboard session (VT-aware overrides) ─────────────────
|
||||||
// bypassing this ring, must grab so the dispatcher stops piling cooked chars
|
// con.setFullscreen(true) declares this pane a fullscreen raw-keyboard owner:
|
||||||
// into a ring it never drains. Flush any prior type-ahead on grab; the
|
// a fullscreen app reads the raw key snapshot (-41..-48) directly, bypassing
|
||||||
// dispatcher keeps the ring empty while held. Release on exit (or the next
|
// this ring, so it grabs to stop the dispatcher piling cooked chars into a ring
|
||||||
// con.getch self-heals a grab leaked by a crashed app).
|
// it never drains (they'd flood the shell the instant the app exits). Flush any
|
||||||
con.grabRawKeyboard = function() {
|
// prior type-ahead on grab; the dispatcher keeps the ring empty while held.
|
||||||
|
// setFullscreen(false) releases (a leaked grab also self-heals on the next
|
||||||
|
// con.getch). These override JS_INIT's bare-metal no-ops so an app's single
|
||||||
|
// con.setFullscreen(true) call does the right thing under vtmgr too.
|
||||||
|
con.setFullscreen = function(on) {
|
||||||
|
con._fullscreen = !!on
|
||||||
|
if (on) {
|
||||||
|
// Claim the grab only while we are the foreground VT. Apps may re-assert
|
||||||
|
// this every frame (e.g. taut/zfm, to re-establish it after launching a
|
||||||
|
// sub-program); a BACKGROUNDED fullscreen app must not clobber the active
|
||||||
|
// grabber's claim on the shared CTRL_RAW_GRAB_VT byte. The claim persists
|
||||||
|
// across a switch-away (nobody else clears it) and the app re-claims on
|
||||||
|
// return, so a single up-front claim also survives backgrounding.
|
||||||
|
if (sys.peek(ACTIVE_VT_ADDR) === VT_NUM) {
|
||||||
sys.poke(RAW_GRAB_ADDR, VT_NUM)
|
sys.poke(RAW_GRAB_ADDR, VT_NUM)
|
||||||
sys.poke(QUEUE_HEAD_ADDR, sys.peek(QUEUE_TAIL_ADDR))
|
sys.poke(QUEUE_HEAD_ADDR, sys.peek(QUEUE_TAIL_ADDR))
|
||||||
|
}
|
||||||
|
} else if (sys.peek(RAW_GRAB_ADDR) === VT_NUM) {
|
||||||
|
sys.poke(RAW_GRAB_ADDR, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
con.releaseRawKeyboard = function() {
|
con.isFullscreen = function() { return con._fullscreen }
|
||||||
if (sys.peek(RAW_GRAB_ADDR) === VT_NUM) sys.poke(RAW_GRAB_ADDR, 0)
|
// This pane owns the physical keyboard only while it is the foreground VT.
|
||||||
}
|
// con.poll_keys() (inherited from JS_INIT) and direct-MMIO raw-input apps gate
|
||||||
|
// their key/mouse reads on this, so a backgrounded app reads nothing.
|
||||||
|
con.isActiveConsole = function() { return sys.peek(ACTIVE_VT_ADDR) === VT_NUM }
|
||||||
|
// Deprecated aliases kept for older raw-keyboard apps; prefer setFullscreen().
|
||||||
|
con.grabRawKeyboard = function() { con.setFullscreen(true) }
|
||||||
|
con.releaseRawKeyboard = function() { con.setFullscreen(false) }
|
||||||
con.hitterminate = function() { return false }
|
con.hitterminate = function() { return false }
|
||||||
con.hiteof = function() { return false }
|
con.hiteof = function() { return false }
|
||||||
con.resetkeybuf = function() { sys.poke(QUEUE_HEAD_ADDR, sys.peek(QUEUE_TAIL_ADDR)) }
|
con.resetkeybuf = function() { sys.poke(QUEUE_HEAD_ADDR, sys.peek(QUEUE_TAIL_ADDR)) }
|
||||||
con.poll_keys = function() { return [0,0,0,0,0,0,0,0] }
|
// poll_keys is inherited from JS_INIT: it returns zeros unless isActiveConsole()
|
||||||
|
// (overridden above), so a backgrounded pane reads no keys while a foreground
|
||||||
|
// pane reads the physical snapshot — no pane-specific override needed.
|
||||||
|
|
||||||
// ── TVDOS.SYS init flags + BIOS stub ───────────────────────────────────────
|
// ── TVDOS.SYS init flags + BIOS stub ───────────────────────────────────────
|
||||||
globalThis._TVDOS_IS_VT_PANE = true
|
globalThis._TVDOS_IS_VT_PANE = true
|
||||||
|
|||||||
@@ -195,6 +195,13 @@ let dec = null
|
|||||||
let stage = "open" // breadcrumb for the error log
|
let stage = "open" // breadcrumb for the error log
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Fullscreen raw-keyboard app: one declaration. Under vtmgr it grabs the
|
||||||
|
// cooked-input feed (so keystrokes typed at this player don't flood the
|
||||||
|
// shell on exit), and con.poll_keys() in readInput() auto-ignores the
|
||||||
|
// keyboard while this console is backgrounded; on bare metal it is a no-op.
|
||||||
|
// Released in the finally below.
|
||||||
|
con.setFullscreen(true)
|
||||||
|
|
||||||
dec = mediadec.open(fullPath, decOpts)
|
dec = mediadec.open(fullPath, decOpts)
|
||||||
const info = dec.info
|
const info = dec.info
|
||||||
|
|
||||||
@@ -265,8 +272,10 @@ try {
|
|||||||
// triggered so a held key fires once. Quit + ASCII/colour toggles work even
|
// triggered so a held key fires once. Quit + ASCII/colour toggles work even
|
||||||
// without -i; the rest of the transport is interactive-only.
|
// without -i; the rest of the transport is interactive-only.
|
||||||
function readInput() {
|
function readInput() {
|
||||||
sys.poke(-40, 1)
|
// con.poll_keys() returns the raw key snapshot, but yields all-zeros
|
||||||
const key = sys.peek(-41)
|
// while this console is backgrounded under vtmgr, so a backgrounded
|
||||||
|
// player never reacts to the foreground console's typing.
|
||||||
|
const key = con.poll_keys()[0]
|
||||||
if (key == K.BACKSPACE) { quit = true; return }
|
if (key == K.BACKSPACE) { quit = true; return }
|
||||||
if (key && key !== lastKey) {
|
if (key && key !== lastKey) {
|
||||||
if (key == K.A) { if (aa) toggleAscii() } // inert when aa.mjs is absent
|
if (key == K.A) { if (aa) toggleAscii() } // inert when aa.mjs is absent
|
||||||
@@ -350,6 +359,7 @@ catch (e) {
|
|||||||
errorlevel = 1
|
errorlevel = 1
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
con.setFullscreen(false)
|
||||||
if (dec) dec.close()
|
if (dec) dec.close()
|
||||||
if (aa && aaCtx) aa.close(aaCtx)
|
if (aa && aaCtx) aa.close(aaCtx)
|
||||||
if (errorlevel === 0) con.clear()
|
if (errorlevel === 0) con.clear()
|
||||||
|
|||||||
@@ -486,6 +486,11 @@ audio.setSampleBank(0) // restore the bank window to bank 0 after probing
|
|||||||
// ── Console setup ───────────────────────────────────────────────────────────
|
// ── Console setup ───────────────────────────────────────────────────────────
|
||||||
con.curs_set(0)
|
con.curs_set(0)
|
||||||
con.clear()
|
con.clear()
|
||||||
|
// Fullscreen raw-keyboard app: one declaration. Under vtmgr it grabs the
|
||||||
|
// dispatcher's cooked-input feed (so the visualiser's keystrokes don't flood the
|
||||||
|
// shell on exit), and con.poll_keys() below auto-ignores input while this console
|
||||||
|
// is backgrounded; a no-op on bare metal. Released in the finally.
|
||||||
|
con.setFullscreen(true)
|
||||||
|
|
||||||
function mvprn(row, col, ch) { con.mvaddch(row, col, ch) }
|
function mvprn(row, col, ch) { con.mvaddch(row, col, ch) }
|
||||||
function mvtext(row, col, s) { con.move(row, col); print(s) }
|
function mvtext(row, col, s) { con.move(row, col); print(s) }
|
||||||
@@ -1255,8 +1260,10 @@ try {
|
|||||||
// Keyboard polling (mirrors playtad). Backspace exits; Up/Down switch
|
// Keyboard polling (mirrors playtad). Backspace exits; Up/Down switch
|
||||||
// to the previous/next song (wrapping) when the file holds more than
|
// to the previous/next song (wrapping) when the file holds more than
|
||||||
// one song. lastNavKey debounces so each press switches exactly once.
|
// one song. lastNavKey debounces so each press switches exactly once.
|
||||||
sys.poke(-40, 1)
|
// con.poll_keys() returns the raw key snapshot, but all-zeros while this
|
||||||
const rawKey = sys.peek(-41)
|
// console is backgrounded under vtmgr, so we never act on another
|
||||||
|
// console's keys.
|
||||||
|
const rawKey = con.poll_keys()[0]
|
||||||
if (rawKey === 67) stopReq = true
|
if (rawKey === 67) stopReq = true
|
||||||
else if (rawKey !== lastNavKey && song.numSongs > 1) {
|
else if (rawKey !== lastNavKey && song.numSongs > 1) {
|
||||||
if (rawKey === 19) // up = previous song
|
if (rawKey === 19) // up = previous song
|
||||||
@@ -1307,6 +1314,7 @@ catch (e) {
|
|||||||
errorlevel = 1
|
errorlevel = 1
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
con.setFullscreen(false)
|
||||||
audio.stop(PLAYHEAD)
|
audio.stop(PLAYHEAD)
|
||||||
con.move(ROW_BOT_BORDER + 1, 1)
|
con.move(ROW_BOT_BORDER + 1, 1)
|
||||||
con.curs_set(1)
|
con.curs_set(1)
|
||||||
|
|||||||
@@ -6837,6 +6837,12 @@ let exitFlag = false
|
|||||||
let pendingExternalDraw = false
|
let pendingExternalDraw = false
|
||||||
|
|
||||||
while (!exitFlag) {
|
while (!exitFlag) {
|
||||||
|
// Fullscreen app: (re)assert the raw-keyboard grab each frame so cooked chars
|
||||||
|
// never pile into this pane's ring (they'd flood the shell on exit), and so
|
||||||
|
// it is re-established after a sub-editor returns. input.withEvent below is
|
||||||
|
// auto-guarded by con.isActiveConsole(), so a backgrounded editor sees no
|
||||||
|
// input. Both are no-ops on bare metal. Released in the teardown.
|
||||||
|
con.setFullscreen(true)
|
||||||
input.withEvent(event => {
|
input.withEvent(event => {
|
||||||
if (dispatchMouseEvent(event)) return
|
if (dispatchMouseEvent(event)) return
|
||||||
if (event[0] !== "key_down") return
|
if (event[0] !== "key_down") return
|
||||||
@@ -6955,6 +6961,7 @@ while (!exitFlag) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
audio.stop(PLAYHEAD)
|
audio.stop(PLAYHEAD)
|
||||||
|
con.setFullscreen(false)
|
||||||
resetAudioDevice()
|
resetAudioDevice()
|
||||||
sys.free(SCRATCH_PTR)
|
sys.free(SCRATCH_PTR)
|
||||||
font.resetLowRom()
|
font.resetLowRom()
|
||||||
|
|||||||
@@ -1115,6 +1115,12 @@ let firstRunLatch = true
|
|||||||
let pendingPostExecDrain = false
|
let pendingPostExecDrain = false
|
||||||
|
|
||||||
while (!exit) {
|
while (!exit) {
|
||||||
|
// Fullscreen app: (re)assert the raw-keyboard grab each frame so cooked chars
|
||||||
|
// never pile into this pane's ring (they'd flood the shell on exit), and so
|
||||||
|
// it is re-established after a launched program returns. input.withEvent
|
||||||
|
// below is auto-guarded by con.isActiveConsole(); both are no-ops on bare
|
||||||
|
// metal. Released after the loop.
|
||||||
|
con.setFullscreen(true)
|
||||||
input.withEvent(event => {
|
input.withEvent(event => {
|
||||||
|
|
||||||
if (dispatchMouseEvent(event)) {
|
if (dispatchMouseEvent(event)) {
|
||||||
@@ -1160,6 +1166,7 @@ while (!exit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
con.setFullscreen(false)
|
||||||
con.curs_set(1)
|
con.curs_set(1)
|
||||||
con.clear()
|
con.clear()
|
||||||
return 0
|
return 0
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
HopperManifestVersion:1
|
HopperManifestVersion:1
|
||||||
HopperPackageName:tvdos
|
HopperPackageName:tvdos
|
||||||
HopperPackageVersion:1.0.0
|
HopperPackageVersion:1.4.0
|
||||||
HopperPackageMaintainer:CuriousTorvald
|
HopperPackageMaintainer:CuriousTorvald
|
||||||
HopperProvides:tvdos;
|
HopperProvides:tvdos;
|
||||||
HopperRequires:
|
HopperRequires:
|
||||||
|
|||||||
@@ -544,9 +544,43 @@ con.reset_graphics = function() {
|
|||||||
};
|
};
|
||||||
// returns current key-down status
|
// returns current key-down status
|
||||||
con.poll_keys = function() {
|
con.poll_keys = function() {
|
||||||
|
if (!con.isActiveConsole()) return [0,0,0,0,0,0,0,0];
|
||||||
sys.poke(-40, 1);
|
sys.poke(-40, 1);
|
||||||
return [-41,-42,-43,-44,-45,-46,-47,-48].map(it => sys.peek(it));
|
return [-41,-42,-43,-44,-45,-46,-47,-48].map(it => sys.peek(it));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── Fullscreen / raw-keyboard session ────────────────────────────────────────
|
||||||
|
// A "fullscreen app" paints the whole screen and reads the raw keyboard snapshot
|
||||||
|
// (-41..-48) directly, bypassing cooked line input (con.getch). Under the virtual
|
||||||
|
// console manager (vtmgr) such an app must (a) tell the dispatcher to stop feeding
|
||||||
|
// cooked characters into its input ring, and (b) ignore the keyboard while its
|
||||||
|
// console is backgrounded. Both used to require the app to feature-detect and
|
||||||
|
// poke vtmgr's shared memory by hand; they are now first-class con methods so an
|
||||||
|
// app declares itself fullscreen in ONE line and the right thing happens whether
|
||||||
|
// or not vtmgr is present.
|
||||||
|
//
|
||||||
|
// On bare metal (no vtmgr) nothing competes for the keyboard and the active
|
||||||
|
// console is always "this" one, so setFullscreen() is state-only and
|
||||||
|
// isActiveConsole() is always true. The vtmgr pane bootstrap overrides
|
||||||
|
// setFullscreen()/isActiveConsole() (and the guard above in poll_keys()) with
|
||||||
|
// the VT-aware implementations.
|
||||||
|
con._fullscreen = false;
|
||||||
|
// Declare (true) or revoke (false) this app's ownership of the raw keyboard and
|
||||||
|
// full screen. Call con.setFullscreen(true) once when entering a fullscreen mode
|
||||||
|
// and con.setFullscreen(false) on exit. Always safe to call.
|
||||||
|
con.setFullscreen = function(on) {
|
||||||
|
con._fullscreen = !!on;
|
||||||
|
};
|
||||||
|
con.isFullscreen = function() { return con._fullscreen; };
|
||||||
|
// True while this console currently owns the physical keyboard. Always true on
|
||||||
|
// bare metal; under vtmgr it is true only while this pane is the foreground VT.
|
||||||
|
// Raw-input apps gate their direct MMIO key/mouse reads on this so a backgrounded
|
||||||
|
// app does not eat the foreground console's input. (con.poll_keys() already does.)
|
||||||
|
con.isActiveConsole = function() { return true; };
|
||||||
|
// Deprecated aliases kept for older raw-keyboard apps; prefer setFullscreen().
|
||||||
|
con.grabRawKeyboard = function() { con.setFullscreen(true); };
|
||||||
|
con.releaseRawKeyboard = function() { con.setFullscreen(false); };
|
||||||
|
|
||||||
// some utilities functions
|
// some utilities functions
|
||||||
|
|
||||||
// TypedArray re-implementation
|
// TypedArray re-implementation
|
||||||
|
|||||||
Reference in New Issue
Block a user