Symptom
Selecting text with the mouse does nothing while a full-screen program (less,
htop, an editor) is on screen: no highlight, no selection, nothing copied.
Double-click and right-click keep working, which makes it look like a gesture
problem — it isn't.
Reproduced on macOS and Linux, with a mouse and a trackpad, on xterm 4.0.0 (the
current release) and on master.
Reproduction
final terminal = Terminal(maxLines: 10000);
final controller = TerminalController();
terminal.resize(80, 24);
terminal.write('\x1b[?1049h'); // alternate screen
for (var row = 0; row < 40; row++) { // paint past the last row => scrolls
terminal.write('row $row\r\n');
}
expect(terminal.buffer.lines[0].attached, isTrue); // FAILS: false
final base = terminal.buffer.createAnchorFromOffset(const CellOffset(0, 23));
final extent = terminal.buffer.createAnchorFromOffset(const CellOffset(16, 23));
controller.setSelection(base, extent);
expect(controller.selection, isNotNull); // FAILS: null
Root cause
Buffer.scrollUp (lib/src/core/buffer/buffer.dart) shifts every line up a
slot through the index setter:
this.lines[i] = this.lines[i + lines];
That attaches the line at i but leaves a stale duplicate reference at
i + lines. _adoptChild (lib/src/utils/circular_buffer.dart) then detaches
whatever occupies the target slot unconditionally:
void _adoptChild(int index, T child) {
final cyclicIndex = _getCyclicIndex(index);
_array[cyclicIndex]?._detach(); // <-- detaches the re-homed line
_array[cyclicIndex] = child.._attach(this, index);
}
So the next iteration's adopt into i + 1 detaches the line that was just
re-homed at i. After a single alternate-screen scroll, effectively every
visible line is detached.
TerminalController.selection returns null when either anchor is detached, so
every drag sets a selection that immediately reads back as nothing. The gesture
layer is fine — the selection pan fires on every drag update and calls
setSelection each time.
Evidence from a running app
A probe on click, printing the clicked cell plus the attachment state:
alt=false cell=(43,24) lineAttached=true anchorAttached=true (a shell selects fine)
alt=true cell=(4,2) lineAttached=false anchorAttached=false (nothing selects)
Cell mapping is correct in both cases; only attachment differs.
Why double-click still appears to work
Only the LAST row stays reachable in practice, which is why a double-click on a
pager's status line looks like it works while dragging anywhere does nothing.
Fix
Detach the previous occupant only when this slot is still its home. _attach
already rewrites _absoluteIndex, so a re-homed item maps to a different slot
and is left alone, while an item genuinely being overwritten (an eviction on a
full list) still maps here and detaches as before.
A branch with the fix and two regression tests is available — happy to open a PR.
Symptom
Selecting text with the mouse does nothing while a full-screen program (
less,htop, an editor) is on screen: no highlight, no selection, nothing copied.Double-click and right-click keep working, which makes it look like a gesture
problem — it isn't.
Reproduced on macOS and Linux, with a mouse and a trackpad, on xterm 4.0.0 (the
current release) and on
master.Reproduction
Root cause
Buffer.scrollUp(lib/src/core/buffer/buffer.dart) shifts every line up aslot through the index setter:
That attaches the line at
ibut leaves a stale duplicate reference ati + lines._adoptChild(lib/src/utils/circular_buffer.dart) then detacheswhatever occupies the target slot unconditionally:
So the next iteration's adopt into
i + 1detaches the line that was justre-homed at
i. After a single alternate-screen scroll, effectively everyvisible line is detached.
TerminalController.selectionreturnsnullwhen either anchor is detached, soevery drag sets a selection that immediately reads back as nothing. The gesture
layer is fine — the selection pan fires on every drag update and calls
setSelectioneach time.Evidence from a running app
A probe on click, printing the clicked cell plus the attachment state:
Cell mapping is correct in both cases; only attachment differs.
Why double-click still appears to work
Only the LAST row stays reachable in practice, which is why a double-click on a
pager's status line looks like it works while dragging anywhere does nothing.
Fix
Detach the previous occupant only when this slot is still its home.
_attachalready rewrites
_absoluteIndex, so a re-homed item maps to a different slotand is left alone, while an item genuinely being overwritten (an eviction on a
full list) still maps here and detaches as before.
A branch with the fix and two regression tests is available — happy to open a PR.