diff options
| author | user <user@clank> | 2026-08-27 15:09:02 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-27 15:09:02 +0200 |
| commit | 2ee881452aa4b19701a071f0333a15265e3b314e (patch) | |
| tree | 82fc4fc20ebcff8c27fbebe170534a9bc9213f6e /src/server.c | |
| parent | macos-remote-build: tolerate notarization ticket propagation lag (diff) | |
| download | slosh-2ee881452aa4b19701a071f0333a15265e3b314e.tar.gz slosh-2ee881452aa4b19701a071f0333a15265e3b314e.tar.xz slosh-2ee881452aa4b19701a071f0333a15265e3b314e.zip | |
selection: drag past an edge scrolls the viewport under it
Text taller than the viewport was not selectable in one gesture: the
drag stopped dead at the pane's border. Two faults, one visible and one
waiting. The visible one: DRAG_SELECT motion subtracted the content
origin in uint16_t, so a pointer above or left of the pane underflowed,
the grid lookup failed, and the selection froze. The waiting one: the
anchor was a pair of *viewport* coordinates re-resolved on every extend,
so the moment anything scrolled mid-drag the anchor would have drifted
with the viewport, re-anchoring to whatever slid under the press.
The anchor is now a tracked grid ref -- the thing lib-vt documents as
being for 'long-lived anchors such as selections' -- so it follows its
cell across scrolling, pruning and reflow, and only the near end of the
selection moves. If a program flooding output prunes the anchored row
out of scrollback entirely, the selection keeps what it had; there is no
cell left to anchor a new answer to.
Motion clamps into the content rect (a pointer past an edge is pointing
at the nearest cell), and holding past the top or bottom edge scrolls on
a clock rather than on motion, because a resting pointer sends no events
and 'keep going' is exactly what resting it there means. app_tick() does
the step, app_next_deadline_ms() wakes the poll loop for it -- the same
mechanism toasts and hover guides already use -- and both front ends
call the tick, so the behaviour exists under the server and the headless
harness alike. Rows per step is how far past the edge the pointer sits:
one row past creeps, further hurries, so the hand already dragging is
also the throttle. The metronome is select_scroll_ms, a knob so the test
can configure the clock down instead of sleeping.
The test drags to one row above the pane and simply holds: the viewport
walks to the top of scrollback (clamping makes the endpoint exact), the
copy starts at a line that was never on screen with the press, and still
ends at the pressed row -- that last check is the tracked anchor's.
Diffstat (limited to '')
| -rw-r--r-- | src/server.c | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/src/server.c b/src/server.c index 36a55d6..3a7a366 100644 --- a/src/server.c +++ b/src/server.c @@ -706,6 +706,14 @@ int server_run(const char *name, const char *const argv[], uint16_t cols, app_reap(s.app); + /* The event-less work: a selection drag held past a pane's edge scrolls + * on a clock (app_next_deadline_ms is what woke the poll for it), and a + * step that moved anything is a frame the client is owed. */ + if (app_tick(s.app) && !pending_paint) { + pending_paint = true; + next_frame = now_ms(); + } + if (app_detach_requested(s.app)) { app_clear_detach(s.app); drop_display(&s, EXIT_DETACHED); |
