aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-27 15:09:02 +0200
committeruser <user@clank>2026-08-27 15:09:02 +0200
commit2ee881452aa4b19701a071f0333a15265e3b314e (patch)
tree82fc4fc20ebcff8c27fbebe170534a9bc9213f6e /include
parentmacos-remote-build: tolerate notarization ticket propagation lag (diff)
downloadslosh-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 'include')
-rw-r--r--include/app.h5
-rw-r--r--include/app_internal.h5
-rw-r--r--include/config.h6
-rw-r--r--include/slosh.h4
4 files changed, 18 insertions, 2 deletions
diff --git a/include/app.h b/include/app.h
index 4b8d21a..3333d6d 100644
--- a/include/app.h
+++ b/include/app.h
@@ -33,6 +33,11 @@ size_t app_toast_count(app_t *a);
* over the frame just composed — or -1 when nothing does. A property of that
* frame, so it is asked after composing one. */
int app_next_deadline_ms(app_t *a);
+/* Advance what runs on that clock without an event behind it — today, the
+ * auto-scroll of a selection drag held past a pane's edge. Both front ends
+ * call it from their loops; true means something changed and a repaint is
+ * owed. */
+bool app_tick(app_t *a);
/* Text the session has copied, and the copy the client has not been told
* about yet (which the front end sends on as OSC 52). Caller frees the take. */
diff --git a/include/app_internal.h b/include/app_internal.h
index 806e1bb..e8ad542 100644
--- a/include/app_internal.h
+++ b/include/app_internal.h
@@ -200,6 +200,11 @@ struct app {
uint32_t c_v[2];
size_t c_vedge[2];
size_t c_nv;
+ /* DRAG_SELECT: when the next auto-scroll step may fire. A selection held
+ * past the pane's top or bottom edge scrolls on this clock (app_tick), so
+ * reaching for text outside the viewport is the drag continuing rather
+ * than a dead stop at the border. */
+ int64_t scroll_due;
} drag;
graphics_t *gfx; /* kitty images, and what the client has been told */
diff --git a/include/config.h b/include/config.h
index e1e71a8..00add4e 100644
--- a/include/config.h
+++ b/include/config.h
@@ -219,7 +219,11 @@ typedef struct {
/* How long the logo splash stays over a freshly attached screen, 0 for
* never. Attach-only: a headless or scripted session has nobody to greet. */
uint16_t splash_ms;
- uint16_t hover_delay_ms; /* how long the pointer must rest to arm a guide */
+ uint16_t hover_delay_ms; /* how long the pointer must rest to arm a guide */
+ /* How often a selection drag held past a pane's top or bottom edge scrolls
+ * another step. The step size is how far past the edge the pointer is, so
+ * this is the metronome and the hand is the throttle. */
+ uint16_t select_scroll_ms;
uint16_t double_click_ms; /* how close two clicks must be to be a double */
/* What a double-click's word stops at, listed as *separators* rather than as
* word characters: anything unlisted belongs to a word, so text nobody
diff --git a/include/slosh.h b/include/slosh.h
index 45392af..c0dbeba 100644
--- a/include/slosh.h
+++ b/include/slosh.h
@@ -280,7 +280,9 @@ typedef struct {
typedef void (*pane_gfx_fn)(pane_t *p, const pane_gfx_t *g, void *ud);
size_t pane_graphics(pane_t *p, pane_gfx_fn cb, void *ud);
-/* Selection, in the pane's viewport coordinates. */
+/* Selection, in the pane's viewport coordinates. The anchor start() takes is
+ * kept as a tracked grid ref, so extend() keeps meaning the cell that was
+ * pressed even after the viewport has scrolled under the drag. */
void pane_select_start(pane_t *p, uint16_t x, uint16_t y);
void pane_select_extend(pane_t *p, uint16_t x, uint16_t y);
void pane_select_clear(pane_t *p);