Skip to content

Terminal: report the mouse to programs that ask for it - #2902

Open
insjang wants to merge 1 commit into
eclipse-platform:masterfrom
insjang:mouse-reporting
Open

insjang wants to merge 1 commit into
eclipse-platform:masterfrom
insjang:mouse-reporting

Conversation

@insjang

@insjang insjang commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Programs that draw their own screen — htop, ncurses menus, and lately CLIs with clickable options and wheel scrolling — ask the terminal for mouse events with DEC modes 1000 (presses and releases), 1002 (also drags) and 1003 (every move), in xterm's legacy encoding or the SGR one (mode 1006). The terminal ignored the request and kept every click for its own selection, so such programs could not be clicked on or scrolled.

  • The canvas hands presses, releases, moves and the wheel to the control while a program has asked for them, with the Alt and Ctrl keys folded into the button as xterm does; a legacy release keeps them too.
  • Shift keeps the mouse for the terminal — the convention every terminal uses to still allow a selection while a program is listening. A button held down drags for the program only if its press went there.
  • A press that went to the program takes the terminal's selection away and becomes the selection anchor, so a later shift-drag starts from where the user last clicked.
  • A release goes wherever its press went, whatever the keyboard state by then, so press and release always reach the same side.
  • A wheel notch is three of the lines SWT counts, however its events split them.
  • Reports are sent as bytes, not through the terminal's text encoding, so a legacy column past 95 stays one byte.

Synchronized output (2026), which was here at first, is in #3066.

Tests: MouseReportTest (both encodings, modifiers, releases, bytes, cells on the screen), TextCanvasMouseUITest (a click the program takes goes no further, Shift keeps it) and mode handling in VT100EmulatorTest.

insjang added a commit to insjang/eclipse.platform that referenced this pull request Sep 3, 2026
Same cross-bundle package-private access problem as mouseReport in
eclipse-platform#2902 -- the test bundle is a separate OSGi bundle, so package-private
static does not reach it even under the same package name.
insjang added a commit to insjang/eclipse.platform that referenced this pull request Sep 28, 2026
Same cross-bundle package-private access problem as mouseReport in
eclipse-platform#2902 -- the test bundle is a separate OSGi bundle, so package-private
static does not reach it even under the same package name.
@insjang
insjang force-pushed the mouse-reporting branch 2 times, most recently from 341beec to ff61a3f Compare September 28, 2026 10:13
insjang added a commit to insjang/eclipse.platform that referenced this pull request Sep 30, 2026
Same cross-bundle package-private access problem as mouseReport in
eclipse-platform#2902 -- the test bundle is a separate OSGi bundle, so package-private
static does not reach it even under the same package name.
@insjang

insjang commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Rebased on master (with #2894 in) on top of the rebased #2901, so this PR shows two commits until #2901 is merged; its own is bb94bf9, the three earlier commits squashed. All terminal tests pass.

How to test, in a runtime Eclipse with this org.eclipse.terminal.control, Terminal view, local shell:

  1. Clicks: run htop and click a process, or a column header to sort.
    Expected: htop selects the process or sorts. Before this change the click only started a terminal selection.
  2. Wheel: in htop, or less with LESS=--mouse, turn the wheel.
    Expected: the program scrolls its list or text.
  3. Drag and modifiers (1002, SGR 1006): mc, drag in a panel; ctrl+click, alt+click.
    Expected: the program sees the drag and the modifiers.
  4. Shift keeps the mouse for the terminal: in htop, shift+drag over some text, then copy.
    Expected: a normal terminal selection, the program does not see the drag.
  5. Focus (1004): printf '\e[?1004h'; cat -v, click another view and back.
    Expected: ^[[O and ^[[I appear. Ctrl+C, then printf '\e[?1004l'.
  6. Reset on a new connection: run printf '\e[?1000h\e[?1004h\e[?2026h', disconnect the terminal, click and switch views while disconnected, connect again.
    Expected: no exception in the Error Log, clicks select text again, and the screen is not frozen.

Unit tests: MouseReportTest (both encodings, modifiers, columns past 223) and the mode handling in VT100EmulatorTest.

@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

    54 files  ± 0      54 suites  ±0   37m 45s ⏱️ -4s
 5 031 tests + 9   5 009 ✅ + 9   22 💤 ±0  0 ❌ ±0 
12 984 runs  +27  12 830 ✅ +27  154 💤 ±0  0 ❌ ±0 

Results for commit 80f3e25. ± Comparison against base commit 30dafa8.

♻️ This comment has been updated with latest results.

@akurtakov akurtakov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, clicking and scrolling in htop, mc and Vim is a big step, and Shift keeping the mouse for the terminal's own selection is the right convention. One scope request inline, plus a question and some small things:

  • Question: while a program listens, a middle-click is reported to it and also pastes (TabFolderManager has its own button-2 listener), and a right-click is reported and also opens the context menu. In Vim with mouse=a a middle-click moves the cursor and pastes. xterm and GNOME Terminal give these clicks to the program alone while it listens, and Shift gets the terminal's behaviour back; should it be the same here?
  • fMouseMode and fSgrMouseEncoding are written by the reader job and read on the display thread, like fBracketedPaste in #2901; please make them volatile too.
  • Nits: @since 5.6 on the new ITerminalControlForText methods names a version that doesn't exist (the bundle is at 1.2, and it's an internal interface, so the tag can go), and fFocusReporting would read better with the other fields at the top.

@insjang

insjang commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Pushed as a3ff4e5:

  • Middle and right click: yes, the same as xterm and GNOME Terminal now. A click the program takes is the program's alone: the canvas reports it in a listener that runs before every other and stops the event there, so the middle-click does not also paste, the right-click opens no context menu (the menu key still does), and neither a double-click nor the copy on release acts on it. Shift gives the terminal's behaviour back, for the wheel too. TextCanvasMouseUITest checks both.
  • fMouseMode and fSgrMouseEncoding are volatile, and so is fApplicationCursorKeys, which the reader job writes the same way. All fields the reader writes on the control are now volatile or under the cursor lock.
  • The @SInCE tags are gone and the fields are at the top.

While going over it I also fixed: positions are now the program's screen, not the view (with history above the screen the view rarely starts on a whole line, so a click near the bottom of a row went one row down), a click over history the user scrolled back to stays with the terminal, a release lost to a dialog no longer leaves the next click half reported, and the legacy encoding reports nothing past column 223, as xterm. On macOS, Shift+wheel arrives as a horizontal scroll, so there history can only be scrolled with the scroll bar while a program listens.

@insjang

insjang commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

The one failing check was SeparateVMTests.testExtensionPointType in the Ant UI tests on macOS ("Console is not closed"), which passed on the previous push and on Linux and Windows here; it looks unrelated to this change, and the macOS job is running again. All terminal tests pass.

@insjang

insjang commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

@akurtakov, a short status so this one does not get lost among the others:

  • Synchronized output is out of this PR, as you asked. It is ready as a PR of its own, and I will open it once the queue is shorter.
  • The two ESC literals are escaped now.
  • The one failing check is SeparateVMTests.testExtensionPointType in the macOS Ant UI tests, which this PR does not touch (details above).

Could you take another look when you have time? After this one I would suggest #3059 (parser rules) and then #2904 (grapheme clusters). If #2904 is too large to review in one go, I can split it into smaller PRs.

@akurtakov

Copy link
Copy Markdown
Member

I'm a bit overloaded right now, I'll get back to your PRs when time permits. Maybe someone else decides to jump int too .

insjang added a commit to insjang/eclipse.platform that referenced this pull request Nov 12, 2026
A program that redraws its screen in many writes can bracket each
screen with CSI ? 2026 h and CSI ? 2026 l. The view polls the text
every 50 ms and used to paint whatever it found, often a screen half
drawn, which shows as flicker and as a cursor jumping over the screen.

While a program is between the two, the canvas model does not look at
the text, and the next poll after the end paints the finished screen.
The guard is in the model's update(), so a scroll or a resize waits
like a poll. A program that never ends a screen, or begins the next one
right after, does not freeze the view: it is held back at most 200 ms
since it last looked. A full reset or a disconnect ends the mode, and
the last poll then shows what the program had drawn. As with DECTCEM,
the flag is kept on the control and copied to the new canvas model
under the same lock when the tab is moved to another view.

Split out of eclipse-platform#2902 as asked in its review.
insjang added a commit to insjang/eclipse.platform that referenced this pull request Nov 12, 2026
A program that redraws its screen in many writes can bracket each
screen with CSI ? 2026 h and CSI ? 2026 l. The view polls the text
every 50 ms and used to paint whatever it found, often a screen half
drawn, which shows as flicker and as a cursor jumping over the screen.

While a program is between the two, the canvas model does not look at
the text, and the next poll after the end paints the finished screen.
The guard is in the model's update(), so a scroll or a resize waits
like a poll. A program that never ends a screen, or begins the next one
right after, does not freeze the view: it is held back at most 200 ms
since it last looked. A full reset or a disconnect ends the mode, and
the last poll then shows what the program had drawn. As with DECTCEM,
the flag is kept on the control and copied to the new canvas model
under the same lock when the tab is moved to another view.

Split out of eclipse-platform#2902 as asked in its review.
@insjang

insjang commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

@akurtakov all checks are green now (the macOS failure was the Ant UI test SeparateVMTests.testExtensionPointType, which passed on the re-run; nothing changed in the code). Your review comments are addressed and synchronized output is out in #3066. @SougandhS, if you have time to look at this one, that would be much appreciated too.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Mouse reporting corrupts legacy bytes under UTF-8 and drops release modifiers, while clearing selection loses the click anchor needed for subsequent Shift-drag.

3 open issues
What changed in this PR

Adds mouse reporting to Eclipse’s terminal so full-screen applications can receive clicks, drags, pointer movement, and wheel events. It builds on the DEC private-mode parsing changes described in #2901.

Changes:

  • Handles mouse modes 1000, 1002, 1003, and SGR encoding mode 1006, including reset behavior.
  • Routes mouse input to applications while preserving Shift-based terminal interaction.
  • Adds encoding, mode-handling, and mouse-event routing tests.
File Description
terminal/​tests/​org.eclipse.terminal.test/​src/​org/​eclipse/​terminal/​test/​AutomatedTestSuite.java Registers mouse UI tests.
terminal/​tests/​org.eclipse.terminal.test/​src/​org/​eclipse/​terminal/​internal/​textcanvas/​TextCanvasMouseUITest.java Tests event consumption and Shift handling.
terminal/​tests/​org.eclipse.terminal.test/​src/​org/​eclipse/​terminal/​internal/​emulator/​VT100EmulatorTest.java Tests mouse modes and reset behavior.
terminal/​tests/​org.eclipse.terminal.test/​src/​org/​eclipse/​terminal/​internal/​emulator/​MouseReportTest.java Tests report encoding and screen coordinates.
terminal/​tests/​org.eclipse.terminal.test/​src/​org/​eclipse/​terminal/​internal/​emulator/​MockTerminalControlForText.java Tracks mouse modes for tests.
terminal/​tests/​org.eclipse.terminal.test/​src/​org/​eclipse/​terminal/​internal/​emulator/​AllTestSuite.java Registers mouse encoding tests.
terminal/​bundles/​org.eclipse.terminal.control/​src/​org/​eclipse/​terminal/​internal/​textcanvas/​TextCanvas.java Routes mouse events and suppresses competing terminal actions.
terminal/​bundles/​org.eclipse.terminal.control/​src/​org/​eclipse/​terminal/​internal/​textcanvas/​GridCanvas.java Offers wheel events before local scrolling.
terminal/​bundles/​org.eclipse.terminal.control/​src/​org/​eclipse/​terminal/​internal/​emulator/​VT100TerminalControl.java Constructs and sends mouse reports.
terminal/​bundles/​org.eclipse.terminal.control/​src/​org/​eclipse/​terminal/​internal/​emulator/​VT100Emulator.java Handles mouse modes and exposes screen height.
terminal/​bundles/​org.eclipse.terminal.control/​src/​org/​eclipse/​terminal/​internal/​control/​impl/​ITerminalControlForText.java Adds mouse-reporting control hooks.

Review effort: Balanced

insjang added a commit to insjang/eclipse.platform that referenced this pull request Nov 13, 2026
A program that redraws its screen in many writes can bracket each
screen with CSI ? 2026 h and CSI ? 2026 l. The view polls the text
every 50 ms and used to paint whatever it found, often a screen half
drawn, which shows as flicker and as a cursor jumping over the screen.

While a program is between the two, the canvas model does not look at
the text, and the next poll after the end paints the finished screen.
The check and the copy of the text are one locked step, so a screen
cannot begin between them, and a scroll or a resize waits like a poll.
A program that never ends a screen, or begins the next one right after, does not freeze the view: it is held back at most 200 ms
since it last looked. A full reset or a disconnect ends the mode, and
the last poll then shows what the program had drawn. As with DECTCEM,
the flag is kept on the control and copied to the new canvas model
under the same lock when the tab is moved to another view.

Split out of eclipse-platform#2902 as asked in its review.
insjang added a commit to insjang/eclipse.platform that referenced this pull request Nov 13, 2026
A program that redraws its screen in many writes can bracket each
screen with CSI ? 2026 h and CSI ? 2026 l. The view polls the text
every 50 ms and used to paint whatever it found, often a screen half
drawn, which shows as flicker and as a cursor jumping over the screen.

While a program is between the two, the canvas model does not look at
the text, and the next poll after the end paints the finished screen.
The check and the copy of the text are one locked step, so a screen
cannot begin between them, and a scroll or a resize waits like a poll.
A program that never ends a screen, or begins the next one right after,
does not freeze the view: it is held back at most 200 ms
since it last looked. A full reset or a disconnect ends the mode, and
the last poll then shows what the program had drawn. As with DECTCEM,
the flag is kept on the control and copied to the new canvas model
under the same lock when the tab is moved to another view.

Split out of eclipse-platform#2902 as asked in its review.
@SougandhS
SougandhS requested a balanced review from Copilot November 13, 2026 06:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The fallback selection anchor is not persisted, causing later Shift-drags to jump, and stale mouse-motion coordinates can suppress movement reports across separate drags.

2 open issues
3 resolved since last review

Review effort: Balanced

insjang added a commit to insjang/eclipse.platform that referenced this pull request Nov 13, 2026
A program that redraws its screen in many writes can bracket each
screen with CSI ? 2026 h and CSI ? 2026 l. The view polls the text
every 50 ms and used to paint whatever it found, often a screen half
drawn, which shows as flicker and as a cursor jumping over the screen.

While a program is between the two, the canvas model does not look at
the text, and the next poll after the end paints the finished screen.
The check and the copy of the text are one locked step, so a screen
cannot begin between them, and a scroll or a resize waits like a poll:
the view moves with the next copy, not over the lines held back.
A program that never ends a screen, or begins the next one right after,
does not freeze the view: it is held back at most 200 ms
since it last looked, on a clock that does not move with the time of
day. A full reset or a disconnect ends the mode, and
the last poll then shows what the program had drawn. As with DECTCEM,
the flag is kept on the control and copied to the new canvas model
under the same lock when the tab is moved to another view.

Split out of eclipse-platform#2902 as asked in its review.
@insjang
insjang force-pushed the mouse-reporting branch 2 times, most recently from 6f77683 to e5574db Compare November 13, 2026 07:45
insjang added a commit to insjang/eclipse.platform that referenced this pull request Nov 13, 2026
A program that redraws its screen in many writes can bracket each
screen with CSI ? 2026 h and CSI ? 2026 l. The view polls the text
every 50 ms and used to paint whatever it found, often a screen half
drawn, which shows as flicker and as a cursor jumping over the screen.

While a program is between the two, the canvas model does not look at
the text, and the next poll after the end paints the finished screen.
The check and the copy of the text are one locked step, so a screen
cannot begin between them, and a scroll or a resize waits like a poll:
the view moves with the next copy, not over the lines held back.
A program that never ends a screen, or begins the next one right after,
does not freeze the view: it is held back at most 200 ms
since it last looked, on a clock that does not move with the time of
day. A full reset or a disconnect ends the mode, and
the last poll then shows what the program had drawn. As with DECTCEM,
the flag is kept on the control and copied to the new canvas model
under the same lock when the tab is moved to another view.

Split out of eclipse-platform#2902 as asked in its review.
@SougandhS
SougandhS requested a balanced review from Copilot November 13, 2026 09:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved issues allow unmatched mouse drags, inconsistent wheel scrolling, and invalid selection anchors after clearing, while advertised focus-reporting and synchronized-output modes remain unsupported.

4 open issues
2 resolved since last review

Review effort: Balanced

@insjang
insjang force-pushed the mouse-reporting branch 2 times, most recently from a7a8562 to 9981af8 Compare November 13, 2026 10:05
Programs that draw their own screen - htop, ncurses menus, and lately
CLIs with clickable options and wheel scrolling - ask the terminal for
mouse events with DEC modes 1000 (presses and releases), 1002 (also
drags) and 1003 (every move), in xterm's legacy encoding or the SGR one
(mode 1006). The terminal ignored the request and kept every click for
its own selection, so such programs could not be clicked on or scrolled.

The canvas now hands presses, releases, moves and the wheel to the
control while a program has asked for them, with the alt and control
keys folded into the button as xterm does. A click the program takes is
the program's alone, as in xterm and GNOME Terminal: the middle-click
does not also paste, the right-click opens no context menu (the menu
key still does), and neither a double-click nor the copy on release
acts on it. The canvas reports it in a listener that runs before every
other and stops the event there. Shift keeps the mouse, the wheel
included, for the terminal, which is the convention every terminal uses
to still allow a selection while a program is listening; a button held
down drags for the program only if its press went there. A wheel notch
is three of the lines SWT counts, however its events split them, the
rest counting toward the next notch in the same direction. Positions are
the program's screen, counted from its first line, not from the top of
the view: with history above the screen the view rarely starts on a
whole line, and a click over the history the user scrolled back to is
the terminal's, not the program's. The legacy encoding reports nothing
past column or row 223, as xterm, rather than bytes it has no room for,
and a release in it keeps the alt and control bits of its press. A
report is sent as bytes, not through the terminal's text encoding, so a
column or row past 95 stays the one byte the legacy encoding means. A
press that went to the program takes the terminal's selection away and
becomes the selection anchor, so a later shift-drag starts from where
the user last clicked rather than from a selection made long before. A release
goes wherever its press went, whatever the keyboard state by then, so
press and release always reach the same side.

This builds on the DEC private mode parsing change (every parameter in
a set/reset), since ncurses turns the mouse on with CSI ? 1006 ; 1000 h.

A reset, which runs before every connection, turns mouse reporting and
SGR mouse encoding off, and nothing is reported while the terminal is
not connected: a program cut off while it listened left the reports on
for the next connection, and a click in between threw from writing to
an output stream that no longer existed. The modes are written by the
reader job and read on the display thread, so they are volatile, as is
application cursor keys, which had the same gap.

MouseReport.mouseReport is public, as the test bundle calls it across
the bundle boundary.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The wheel handler lacks regression coverage for event accumulation, remainder preservation, and remainder resets on direction or mode changes.

1 open issue
4 resolved since last review

Review effort: Balanced

Comment on lines +1574 to +1576
fWheelLines += count;
int notches = Math.abs(fWheelLines) / 3;
fWheelLines %= 3;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants