-
Notifications
You must be signed in to change notification settings - Fork 93
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #130 from tauu/mouse-support
feat: initial mouse support
- Loading branch information
Showing
23 changed files
with
605 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
enum TerminalMouseButton { | ||
left(id: 0), | ||
|
||
middle(id: 1), | ||
|
||
right(id: 2), | ||
|
||
wheelUp(id: 64 + 4, isWheel: true), | ||
|
||
wheelDown(id: 64 + 5, isWheel: true), | ||
|
||
wheelLeft(id: 64 + 6, isWheel: true), | ||
|
||
wheelRight(id: 64 + 7, isWheel: true), | ||
; | ||
|
||
/// The id that is used to report a button press or release to the terminal. | ||
/// | ||
/// Mouse wheel up / down use button IDs 4 = 0100 (binary) and 5 = 0101 (binary). | ||
/// The bits three and four of the button are transposed by 64 and 128 | ||
/// respectively, when reporting the id of the button and have have to be | ||
/// adjusted correspondingly. | ||
final int id; | ||
|
||
/// Whether this button is a mouse wheel button. | ||
final bool isWheel; | ||
|
||
const TerminalMouseButton({required this.id, this.isWheel = false}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
enum TerminalMouseButtonState { | ||
up, | ||
|
||
down, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import 'package:xterm/src/core/buffer/cell_offset.dart'; | ||
import 'package:xterm/src/core/mouse/button_state.dart'; | ||
import 'package:xterm/src/core/mouse/mode.dart'; | ||
import 'package:xterm/src/core/mouse/button.dart'; | ||
import 'package:xterm/src/core/mouse/reporter.dart'; | ||
import 'package:xterm/src/utils/platform.dart'; | ||
import 'package:xterm/src/core/state.dart'; | ||
|
||
class TerminalMouseEvent { | ||
/// The button that is pressed or released. | ||
final TerminalMouseButton button; | ||
|
||
/// The current state of the button. | ||
final TerminalMouseButtonState buttonState; | ||
|
||
/// The position of button state change. | ||
final CellOffset position; | ||
|
||
/// The state of the terminal. | ||
final TerminalState state; | ||
|
||
/// The platform of the terminal. | ||
final TerminalTargetPlatform platform; | ||
|
||
TerminalMouseEvent({ | ||
required this.button, | ||
required this.buttonState, | ||
required this.position, | ||
required this.state, | ||
required this.platform, | ||
}); | ||
} | ||
|
||
const defaultMouseHandler = CascadeMouseHandler([ | ||
ClickMouseHandler(), | ||
UpDownMouseHandler(), | ||
]); | ||
|
||
abstract class TerminalMouseHandler { | ||
const TerminalMouseHandler(); | ||
|
||
String? call(TerminalMouseEvent event); | ||
} | ||
|
||
class CascadeMouseHandler implements TerminalMouseHandler { | ||
final List<TerminalMouseHandler> _handlers; | ||
|
||
const CascadeMouseHandler(this._handlers); | ||
|
||
@override | ||
String? call(TerminalMouseEvent event) { | ||
for (var handler in _handlers) { | ||
final result = handler(event); | ||
if (result != null) { | ||
return result; | ||
} | ||
} | ||
return null; | ||
} | ||
} | ||
|
||
class ClickMouseHandler implements TerminalMouseHandler { | ||
const ClickMouseHandler(); | ||
|
||
@override | ||
String? call(TerminalMouseEvent event) { | ||
switch (event.state.mouseMode) { | ||
case MouseMode.clickOnly: | ||
// Only clicks and only the first 3 buttons are reported. | ||
if (event.buttonState == TerminalMouseButtonState.down && | ||
(event.button.id < 3)) { | ||
return MouseReporter.report( | ||
event.button, | ||
event.buttonState, | ||
event.position, | ||
event.state.mouseReportMode, | ||
); | ||
} | ||
return null; | ||
case MouseMode.none: | ||
case MouseMode.upDownScroll: | ||
case MouseMode.upDownScrollDrag: | ||
case MouseMode.upDownScrollMove: | ||
return null; | ||
} | ||
} | ||
} | ||
|
||
class UpDownMouseHandler implements TerminalMouseHandler { | ||
const UpDownMouseHandler(); | ||
|
||
@override | ||
String? call(TerminalMouseEvent event) { | ||
switch (event.state.mouseMode) { | ||
case MouseMode.none: | ||
case MouseMode.clickOnly: | ||
return null; | ||
case MouseMode.upDownScroll: | ||
case MouseMode.upDownScrollDrag: | ||
case MouseMode.upDownScrollMove: | ||
// Up events are never reported for mouse wheel buttons. | ||
if (event.button.isWheel && | ||
event.buttonState == TerminalMouseButtonState.up) { | ||
return null; | ||
} | ||
return MouseReporter.report( | ||
event.button, | ||
event.buttonState, | ||
event.position, | ||
event.state.mouseReportMode, | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/// https://terminalguide.namepad.de/mouse/ | ||
enum MouseMode { | ||
none, | ||
|
||
clickOnly, | ||
|
||
upDownScroll, | ||
|
||
upDownScrollDrag, | ||
|
||
upDownScrollMove, | ||
} | ||
|
||
/// https://terminalguide.namepad.de/mouse/ | ||
enum MouseReportMode { | ||
/// The default mouse reporting mode where digits are encoded as bytes with | ||
/// `32 + code`. This mode has a range from 1 to 223. | ||
normal, | ||
|
||
/// When code < 96 this is the same as [normal], otherwise the `code + 32` is | ||
/// encoded as 2 bytes in UTF-8. This mode has a range from 1 to 2015. | ||
utf, | ||
|
||
/// In this mode the code are encoded as 10-based numbers. Tha range is | ||
/// unlimited. | ||
sgr, | ||
|
||
/// Similar to [sgr], the difference is that the button id is encoded as | ||
/// `32 + code`. | ||
urxvt, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import 'package:xterm/src/core/buffer/cell_offset.dart'; | ||
import 'package:xterm/src/core/mouse/mode.dart'; | ||
import 'package:xterm/src/core/mouse/button.dart'; | ||
import 'package:xterm/src/core/mouse/button_state.dart'; | ||
|
||
abstract class MouseReporter { | ||
static String report( | ||
TerminalMouseButton button, | ||
TerminalMouseButtonState state, | ||
CellOffset position, | ||
MouseReportMode reportMode, | ||
) { | ||
// x and y offsets have to be incremented by 1 as the offset if 0-based, | ||
// The position has to be reported using 1-based coordinates. | ||
final x = position.x + 1; | ||
final y = position.y + 1; | ||
switch (reportMode) { | ||
case MouseReportMode.normal: | ||
case MouseReportMode.utf: | ||
// Button ID 3 is used to signal a button release. | ||
final buttonID = state == TerminalMouseButtonState.up ? 3 : button.id; | ||
// The button ID is reported as shifted by 32 to produce a printable | ||
// character. | ||
final btn = String.fromCharCode(32 + buttonID); | ||
// Normal mode only supports a maximum position of 223, while utf | ||
// supports positions up to 2015. Both modes send a null byte if the | ||
// position exceeds that limit. | ||
final col = (reportMode == MouseReportMode.normal && x > 223) || | ||
(reportMode == MouseReportMode.utf && x > 2015) | ||
? '\x00' | ||
: String.fromCharCode(32 + x); | ||
final row = (reportMode == MouseReportMode.normal && y > 223) || | ||
(reportMode == MouseReportMode.utf && y > 2015) | ||
? '\x00' | ||
: String.fromCharCode(32 + y + 1); | ||
return "\x1b[M$btn$col$row"; | ||
case MouseReportMode.sgr: | ||
final buttonID = button.id; | ||
final upDown = state == TerminalMouseButtonState.down ? 'M' : 'm'; | ||
return "\x1b[<$buttonID;$x;$y$upDown"; | ||
case MouseReportMode.urxvt: | ||
// The button ID uses the same id as to report it as in normal mode. | ||
final buttonID = | ||
32 + (state == TerminalMouseButtonState.up ? 3 : button.id); | ||
return "\x1b[$buttonID;$x;${y}M"; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.