Repository navigation
fix(terminal): smooth touchpad scrolling + repaint after WebGL context loss - #218
Conversation
…tion Pixel-precision devices (touchpads, high-resolution mice) fire many wheel events per second with sub-line deltaY values, and the old Math.max(1, round) forced EVERY micro-event to a whole line — a gentle two-finger drag became a fast, jerky line-at-a-time jump. Carry the sub-line remainder between events in a per-surface accumulator and emit only whole lines, measured against the REAL cell height rather than a fixed 17px so the mapping tracks the user's font size. Notched wheels (line/page delta modes) keep the at-least-one-line-per-detent snap. The accumulator is dropped on surface reset alongside surfaceMouseModes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cfhwse76gqxU7voD3iGCvt
Chromium force-loses the oldest WebGL context past its ~16 cap, which happens exactly when panes are split/resized and several terminals flip visible at once. Disposing the WebGL addon reactivates xterm's DOM renderer, but nothing repainted the existing buffer into it — the canvas the lost context left behind stayed blank until the next PTY write, which is the "text disappears when I resize/split panes" bug. Force a full terminal.refresh once the DOM renderer is live (next frame, so its layout has settled). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cfhwse76gqxU7voD3iGCvt
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Merged — shipping in v2.9.3. Two genuinely independent fixes in one PR, one commit each, both correct; that's the right way to send this. Scrolling. WebGL context loss. This is the better of the two. The existing One thing I changed on merge: For context: |
#219 promoted auto-open from a module-level runtime value to a persisted pref — the right move, and the Settings toggle plus the config.toml write-through land as merged. Only the default changes here. Defaulting it OFF is a defensible policy for a NEW feature and the wrong one for this one, because the behaviour predates the pref. Prefs persist as whole blocks and a new field is filled from DEFAULTS ({...DEFAULTS, ...stored}), so `false` does not offer anybody a choice: it silently switches auto-open off for every existing install on upgrade, with no note attached. `openLinksExternally` two fields up already documents exactly this argument. A pref meant to give the people it bothers a way out should not take it from the people it doesn't. The Settings hint and en.ts string are corrected to match, and a test pins the default plus the merge that depends on it — the value is one word that reads like an oversight, which is the shape of thing a later tidy-up flips back without noticing. Also drops wheelLineAccum on the PTY-exit path (#218 follow-up). It was cleared only on `wmux:reset-terminal`, but it is keyed by surfaceId and sits beside surfaceMouseModes, whose delete on that same path carries a comment about the map growing one entry per surface for the life of the process. Correct on its own terms too: a fraction of a line owed to a gesture aimed at a dead process should not be paid to whatever opens on that surface next. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mu39594ynryhibh6AfixjX
…ater (#229) After ~2.5 days the reporter's window kept its last frame while every PTY lived on: Responding=True, 0% CPU everywhere, renderer paged down to 3 MB. Killing ONLY the GPU process restored rendering with the same renderer PID and nothing lost. The wedge is Chromium's display compositor on a hybrid NVIDIA/Intel + DisplayLink machine, with no repro; what wmux can own is the remedy. Detector (renderer/utils/gpu-watchdog.ts): requestAnimationFrame runs on BeginFrames from the GPU process, so when it wedges rAF stops while setTimeout keeps going. Three unanswered requests 5 s apart while the document is visible is a report. Misses while hidden are discarded. Decision (main/gpu-watchdog.ts): the renderer never acts. Main restarts the GPU process only when the window is FOCUSED, the OS reports input within 30 s (powerMonitor.getSystemIdleTime), and no restart happened in the last 5 min - Chromium counts each killed GPU process as a crash and drops to software rendering after a few. process.kill on the GPU entry of app.getAppMetrics(); Chromium relaunches it, and the #218 context-loss path repaints every pane. gpu-stall / gpu-restart / gpu-gone go to main.log. Off switch: gpuWatchdog: false in wmux-workspace-prefs. The update prompt's default button is now 'Later': it opens over a terminal the user is typing into, and Enter there installed the update and quit every live session. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NtqBese2n4qh7cZpVx8H5A
Two independent terminal-feel fixes, one commit each:
1. Touchpad scrolling is jerky (fractional-line accumulation)
Pixel-precision devices fire many wheel events per second with tiny sub-line
deltaYvalues, andwheelDeltaToLines'sMath.max(1, round)forced every micro-event to a whole line — a gentle two-finger drag scrolled fast and jerky, line-at-a-time. Fix: a per-surface fractional-line accumulator carries the sub-line remainder between events and emits only whole lines, measured against the real cell height (host rect / rows) instead of a fixed 17px so the mapping tracks the user's font size. Notched wheels (DOM_DELTA_LINE/PAGE) keep the at-least-one-line-per-detent snap. The accumulator is cleaned up on surface reset alongsidesurfaceMouseModes.2. Panes go blank after WebGL context loss
Chromium force-loses the oldest WebGL context past its ~16 cap — which happens exactly when panes are split/resized and several terminals flip visible at once. The existing
onContextLosshandler correctly downgrades to the DOM renderer, but nothing repainted the buffer into it: the canvas the lost context left behind stayed blank until the next PTY write. This is the "text disappears when I resize/split panes" symptom. Fix: force a fullterminal.refresh(0, rows-1)on the next frame once the DOM renderer is live.Testing
npm run typecheckclean;npm test2708 passed. (The one failure on my machine is the pre-existingresolveExistingShellPathpwsh test — no PowerShell 7 installed here; it fails identically on a clean master checkout.)Independent of #217 (both apply cleanly to master in either order).
🤖 Generated with Claude Code
https://claude.ai/code/session_01Cfhwse76gqxU7voD3iGCvt