Module: Tuile::Mouse
- Defined in:
- lib/tuile/mouse.rb,
lib/tuile/mouse/router.rb,
sig/tuile.rbs
Overview
The mouse: the events, their parser, and the tracking levels Screen#run_event_loop asks the terminal for. Who gets one is Router's story.
Four of the five classes are what Mouse.parse reads off the wire; DragEvent is
the router's own, a move while something holds the grab. Each is a
Data.define including the Event marker — no inheritance, so a case
matches either one class or Mouse::Event for all of them. Enter and exit
have no class at all: nothing is parsed, they are the difference between two
hovered chains.
Coordinates are screen-absolute and 0-based everywhere.
Defined Under Namespace
Modules: Event Classes: DownEvent, DragEvent, MoveEvent, Router, ScrollEvent, UpEvent
Constant Summary collapse
- LEVELS =
The
capture_mouse:levels, each a strict superset of the one before, and each unlocking one tier of events::clicks(mode 1000) gives down, up and scroll;:drag(1002) adds drag;:hover(1003) adds move, enter and exit (R_mouse_reporting). %i[clicks drag hover].freeze
- MODES =
Returns the DEC private mode each level sets.
{ clicks: 1000, drag: 1002, hover: 1003 }.freeze
- SGR_MODE =
The SGR encoding mode, requested alongside whichever MODES rung the level picked — encoding and reporting are orthogonal modes. A terminal that does not understand it ignores the DECSET and keeps sending X10 (
R_mouse_reporting). 1006- MODIFIER_BITS =
Button code layout, shared by both encodings:
button | 4 shift | 8 meta | 16 ctrl | 32 motion | 64 wheel(R_mouse_reporting). 4 | 8 | 16
- X10_PREFIX =
Returns the X10 report prefix, followed by three biased bytes.
"\e[M"- SGR_PREFIX =
Returns the SGR report prefix, followed by a SGR_REPORT body.
"\e[<"- SGR_REPORT =
\e[<Cb;x;ythenMfor a press andmfor a release — decimal and uncapped, where X10 packs each coordinate into a byte and dies past 223. /\A\e\[<(\d+);(\d+);(\d+)([Mm])\z/
Class Method Summary collapse
-
.level(capture_mouse) ⇒ Symbol?
Normalizes a
capture_mouse:argument to a level. -
.parse(key) ⇒ Mouse::Event?
Parses a mouse report in either wire encoding into one of DownEvent, UpEvent, ScrollEvent or MoveEvent.
-
.report?(key) ⇒ Boolean
Whether
keyis a mouse report — the X10\e[Mprefix or the SGR\e[<one, regardless of length. Mouse.parse is the place that validates the full shape and raises on malformed input. -
.start_tracking(level) ⇒ String
The escape asking for that level, SGR encoding included:.
-
.stop_tracking(level) ⇒ String
@param
level— one of LEVELS.
Class Method Details
.level(capture_mouse) ⇒ Symbol?
Normalizes a capture_mouse: argument to a level.
@param capture_mouse — false, true (== :clicks), or one of LEVELS.
@return — the level; nil when tracking is off.
131 132 133 134 135 136 137 138 139 140 |
# File 'lib/tuile/mouse.rb', line 131 def level(capture_mouse) case capture_mouse when false, nil then nil when true then :clicks when *LEVELS then capture_mouse else raise ArgumentError, "capture_mouse: expected true, false or one of #{LEVELS}, got #{capture_mouse.inspect}" end end |
.parse(key) ⇒ Mouse::Event?
Parses a mouse report in either wire encoding into one of DownEvent, UpEvent, ScrollEvent or MoveEvent. Modifier bits are ignored.
Mouse.parse("\e[M !\"") # => X10: DownEvent[:left, 0, 0]
Mouse.parse("\e[<0;1;1M") # => SGR: DownEvent[:left, 0, 0]
Mouse.parse("\e[<0;1;1m") # => SGR: UpEvent[0, 0]
Which encoding arrived is invisible in the result, deliberately: SGR
names the button on a release and X10 cannot, so the button is dropped
(D_mouse_dispatch).
A prefix without a whole well-formed report raises rather than truncates: it is always a bug in the upstream key-reader — a short read lost the tail, a long one over-consumed into the next escape sequence — and swallowing it corrupts the next getkey, surfacing as garbled keystrokes in focused inputs rather than as a parser failure pointing at the cause.
@param key — key read via Keys.getkey
@return — nil if key is not a mouse report, or reports
a wheel button beyond the four directions.
182 183 184 185 186 |
# File 'lib/tuile/mouse.rb', line 182 def parse(key) return nil unless report?(key) key.start_with?(SGR_PREFIX) ? parse_sgr(key) : parse_x10(key) end |
.report?(key) ⇒ Boolean
Whether key is a mouse report — the X10 \e[M prefix or the SGR
\e[< one, regardless of length. parse is the place that validates
the full shape and raises on malformed input.
@param key — key read via Keys.getkey
159 |
# File 'lib/tuile/mouse.rb', line 159 def report?(key) = key.start_with?(X10_PREFIX, SGR_PREFIX) |
.start_tracking(level) ⇒ String
The escape asking for that level, SGR encoding included:
Mouse.start_tracking(:clicks) # => "\e[?1006h\e[?1000h"
@param level — one of LEVELS.
@return — the escape enabling that level.
148 |
# File 'lib/tuile/mouse.rb', line 148 def start_tracking(level) = "\e[?#{SGR_MODE}h\e[?#{MODES.fetch(level)}h" |