mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
term.ui: handle middle mouse button events and modifier keys (#6821)
This commit is contained in:
parent
5069be04a2
commit
6b47c61fe4
@ -12,6 +12,12 @@ fn event(e &tui.Event, x voidptr) {
|
|||||||
app.tui.write('V term.input event viewer (press `esc` to exit)\n\n')
|
app.tui.write('V term.input event viewer (press `esc` to exit)\n\n')
|
||||||
app.tui.write('$e')
|
app.tui.write('$e')
|
||||||
app.tui.write('\n\nRaw event bytes: "${e.utf8.bytes().hex()}" = ${e.utf8.bytes()}')
|
app.tui.write('\n\nRaw event bytes: "${e.utf8.bytes().hex()}" = ${e.utf8.bytes()}')
|
||||||
|
if e.modifiers != 0 {
|
||||||
|
app.tui.write('\nModifiers: $e.modifiers = ')
|
||||||
|
if e.modifiers & tui.ctrl != 0 { app.tui.write('ctrl. ') }
|
||||||
|
if e.modifiers & tui.shift != 0 { app.tui.write('shift ') }
|
||||||
|
if e.modifiers & tui.alt != 0 { app.tui.write('alt. ') }
|
||||||
|
}
|
||||||
app.tui.flush()
|
app.tui.flush()
|
||||||
|
|
||||||
if e.typ == .key_down && e.code == .escape { exit(0) }
|
if e.typ == .key_down && e.code == .escape { exit(0) }
|
||||||
@ -28,5 +34,5 @@ app.tui = tui.init(
|
|||||||
frame_rate: 60
|
frame_rate: 60
|
||||||
)
|
)
|
||||||
|
|
||||||
println('V term.input event viewer (press `esc` to exit)\n\n')
|
println('V term.ui event viewer (press `esc` to exit)\n\n')
|
||||||
app.tui.run()
|
app.tui.run()
|
||||||
|
@ -165,6 +165,10 @@ fn event(event &tui.Event, x voidptr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.mouse_scroll {
|
.mouse_scroll {
|
||||||
|
app.mouse_pos = {
|
||||||
|
x: event.x
|
||||||
|
y: event.y
|
||||||
|
}
|
||||||
if event.direction == .down {
|
if event.direction == .down {
|
||||||
app.inc_size()
|
app.inc_size()
|
||||||
} else {
|
} else {
|
||||||
@ -175,17 +179,17 @@ fn event(event &tui.Event, x voidptr) {
|
|||||||
match event.code {
|
match event.code {
|
||||||
.f1, ._1 {
|
.f1, ._1 {
|
||||||
oevent := *event
|
oevent := *event
|
||||||
nevent := { oevent | button: tui.MouseButton.primary, x: app.mouse_pos.x , y: app.mouse_pos.y }
|
nevent := { oevent | button: tui.MouseButton.left, x: app.mouse_pos.x , y: app.mouse_pos.y }
|
||||||
app.paint(nevent)
|
app.paint(nevent)
|
||||||
}
|
}
|
||||||
.f2, ._2 {
|
.f2, ._2 {
|
||||||
oevent := *event
|
oevent := *event
|
||||||
nevent := { oevent | button: tui.MouseButton.secondary, x: app.mouse_pos.x , y: app.mouse_pos.y }
|
nevent := { oevent | button: tui.MouseButton.right, x: app.mouse_pos.x , y: app.mouse_pos.y }
|
||||||
app.paint(nevent)
|
app.paint(nevent)
|
||||||
}
|
}
|
||||||
.space {
|
.space {
|
||||||
oevent := *event
|
oevent := *event
|
||||||
nevent := { oevent | button: tui.MouseButton.tertiary, x: app.mouse_pos.x , y: app.mouse_pos.y }
|
nevent := { oevent | button: tui.MouseButton.middle, x: app.mouse_pos.x , y: app.mouse_pos.y }
|
||||||
app.paint(nevent)
|
app.paint(nevent)
|
||||||
}
|
}
|
||||||
.j, .down {
|
.j, .down {
|
||||||
@ -283,8 +287,8 @@ fn (mut app App) set_pixel(x_ int, y_ int, c tui.Color) {
|
|||||||
fn (mut app App) paint(event &tui.Event) {
|
fn (mut app App) paint(event &tui.Event) {
|
||||||
x_start, y_start := int(f32((event.x - 1) / 2) - app.size / 2 + 1), event.y - app.size / 2
|
x_start, y_start := int(f32((event.x - 1) / 2) - app.size / 2 + 1), event.y - app.size / 2
|
||||||
color := match event.button {
|
color := match event.button {
|
||||||
.primary { app.primary_color }
|
.left { app.primary_color }
|
||||||
.secondary { app.secondary_color }
|
.right { app.secondary_color }
|
||||||
else { app.bg_color }
|
else { app.bg_color }
|
||||||
}
|
}
|
||||||
for x in x_start .. x_start + app.size {
|
for x in x_start .. x_start + app.size {
|
||||||
@ -375,7 +379,7 @@ fn (mut app App) current_color(x int, y int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut app App) draw_footer() {
|
fn (mut app App) draw_footer() {
|
||||||
ww, wh := app.tui.window_width, app.tui.window_height
|
_, wh := app.tui.window_width, app.tui.window_height
|
||||||
app.tui.horizontal_separator(wh - 4)
|
app.tui.horizontal_separator(wh - 4)
|
||||||
for i, color_row in colors {
|
for i, color_row in colors {
|
||||||
for j, color in color_row {
|
for j, color in color_row {
|
||||||
@ -441,9 +445,9 @@ fn (mut app App) footer_click(event &tui.Event) {
|
|||||||
idx := footer_y * 19 - 6 + event.x / 3
|
idx := footer_y * 19 - 6 + event.x / 3
|
||||||
if idx < 0 || idx > 56 { return }
|
if idx < 0 || idx > 56 { return }
|
||||||
color := colors[idx / 19][idx % 19]
|
color := colors[idx / 19][idx % 19]
|
||||||
if event.button == .primary {
|
if event.button == .left {
|
||||||
app.primary_color = color
|
app.primary_color = color
|
||||||
} else {
|
} else if event.button == .right {
|
||||||
app.secondary_color = color
|
app.secondary_color = color
|
||||||
}
|
}
|
||||||
app.show_msg('set $event.button.str().to_lower() color idx: $idx', 1)
|
app.show_msg('set $event.button.str().to_lower() color idx: $idx', 1)
|
||||||
|
@ -123,9 +123,9 @@ pub enum Direction {
|
|||||||
|
|
||||||
pub enum MouseButton {
|
pub enum MouseButton {
|
||||||
unknown
|
unknown
|
||||||
primary
|
left
|
||||||
secondary
|
middle
|
||||||
tertiary
|
right
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum EventType {
|
pub enum EventType {
|
||||||
|
@ -9,15 +9,6 @@ pub fn init(cfg Config) &Context {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
read_buf: []byte{ cap: cfg.buffer_size }
|
read_buf: []byte{ cap: cfg.buffer_size }
|
||||||
}
|
}
|
||||||
ctx.save_title()
|
|
||||||
|
|
||||||
if cfg.hide_cursor {
|
|
||||||
print('\x1b[?25l')
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.window_title != '' {
|
|
||||||
print('\x1b]0;$cfg.window_title\x07')
|
|
||||||
}
|
|
||||||
|
|
||||||
// lmao
|
// lmao
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -51,6 +51,9 @@ fn restore_terminal_state() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut ctx Context) termios_setup() {
|
fn (mut ctx Context) termios_setup() {
|
||||||
|
// store the current title, so restore_terminal_state can get it back
|
||||||
|
ctx.save_title()
|
||||||
|
|
||||||
mut termios := get_termios()
|
mut termios := get_termios()
|
||||||
|
|
||||||
if ctx.cfg.capture_events {
|
if ctx.cfg.capture_events {
|
||||||
@ -62,11 +65,20 @@ fn (mut ctx Context) termios_setup() {
|
|||||||
// Set raw input mode by unsetting ICANON and ECHO
|
// Set raw input mode by unsetting ICANON and ECHO
|
||||||
termios.c_lflag &= ~u32(C.ICANON | C.ECHO)
|
termios.c_lflag &= ~u32(C.ICANON | C.ECHO)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.cfg.hide_cursor {
|
||||||
|
print('\x1b[?25l')
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.cfg.window_title != '' {
|
||||||
|
print('\x1b]0;$ctx.cfg.window_title\x07')
|
||||||
|
}
|
||||||
|
|
||||||
// Prevent stdin from blocking by making its read time 0
|
// Prevent stdin from blocking by making its read time 0
|
||||||
termios.c_cc[C.VTIME] = 0
|
termios.c_cc[C.VTIME] = 0
|
||||||
termios.c_cc[C.VMIN] = 0
|
termios.c_cc[C.VMIN] = 0
|
||||||
C.tcsetattr(C.STDIN_FILENO, C.TCSAFLUSH, &termios)
|
C.tcsetattr(C.STDIN_FILENO, C.TCSAFLUSH, &termios)
|
||||||
print('\x1b[?1003h\x1b[?1015h\x1b[?1006h')
|
print('\x1b[?1003h\x1b[?1006h')
|
||||||
|
|
||||||
ctx.termios = termios
|
ctx.termios = termios
|
||||||
|
|
||||||
@ -78,7 +90,6 @@ fn (mut ctx Context) termios_setup() {
|
|||||||
os.signal(C.SIGCONT, fn () {
|
os.signal(C.SIGCONT, fn () {
|
||||||
mut c := ctx_ptr
|
mut c := ctx_ptr
|
||||||
if c != 0 {
|
if c != 0 {
|
||||||
c.save_title()
|
|
||||||
c.termios_setup()
|
c.termios_setup()
|
||||||
c.window_height, c.window_width = get_terminal_size()
|
c.window_height, c.window_width = get_terminal_size()
|
||||||
mut event := &Event{
|
mut event := &Event{
|
||||||
@ -260,31 +271,43 @@ fn escape_sequence(buf_ string) (&Event, int) {
|
|||||||
// Mouse events
|
// Mouse events
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
||||||
// TODO: rxvt uses different escape sequences for mouse events :/
|
// Documentation: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||||
if buf.len > 2 && buf[1] == `<` {
|
if buf.len > 2 && buf[1] == `<` {
|
||||||
split := buf[2..].split(';')
|
split := buf[2..].split(';')
|
||||||
if split.len < 3 { return &Event(0), 0 }
|
if split.len < 3 { return &Event(0), 0 }
|
||||||
|
|
||||||
typ, x, y := split[0].int(), split[1].int(), split[2].int()
|
typ, x, y := split[0].int(), split[1].int(), split[2].int()
|
||||||
|
lo := typ & 0b00011
|
||||||
|
hi := typ & 0b11100
|
||||||
|
|
||||||
|
mut modifiers := u32(0)
|
||||||
|
if hi & 4 != 0 { modifiers |= shift }
|
||||||
|
if hi & 8 != 0 { modifiers |= alt }
|
||||||
|
if hi & 16 != 0 { modifiers |= ctrl }
|
||||||
|
|
||||||
match typ {
|
match typ {
|
||||||
0, 2 {
|
0...31 {
|
||||||
last := buf[buf.len - 1]
|
last := buf[buf.len - 1]
|
||||||
button := if typ == 0 { MouseButton.primary } else { MouseButton.secondary }
|
button := if lo < 3 { MouseButton(lo + 1) } else { MouseButton.unknown }
|
||||||
event := if last == `M` { EventType.mouse_down } else { EventType.mouse_up }
|
event := if last == `m` || lo == 3 { EventType.mouse_up } else { EventType.mouse_down }
|
||||||
return &Event{ typ: event, x: x, y: y, button: button, utf8: single }, end
|
|
||||||
|
return &Event{ typ: event, x: x, y: y, button: button, modifiers: modifiers utf8: single }, end
|
||||||
}
|
}
|
||||||
32, 34 {
|
32...63 {
|
||||||
button := if typ == 32 { MouseButton.primary } else { MouseButton.secondary }
|
button, event := if lo < 3 {
|
||||||
return &Event{ typ: .mouse_drag, x: x, y: y, button: button, utf8: single }, end
|
MouseButton(lo + 1), EventType.mouse_drag
|
||||||
|
} else {
|
||||||
|
MouseButton.unknown, EventType.mouse_move
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Event{ typ: event, x: x, y: y, button: button, modifiers: modifiers, utf8: single }, end
|
||||||
}
|
}
|
||||||
35 {
|
64...95 {
|
||||||
return &Event{ typ: .mouse_move, x: x, y: y, utf8: single }, end
|
direction := if typ & 1 == 0 { Direction.down } else { Direction.up }
|
||||||
|
return &Event{ typ: .mouse_scroll, x: x, y: y, direction: direction, modifiers: modifiers, utf8: single }, end
|
||||||
|
} else {
|
||||||
|
return &Event{ typ: .unknown, utf8: single }, end
|
||||||
}
|
}
|
||||||
64, 65 {
|
|
||||||
direction := if typ == 64 { Direction.down } else { Direction.up }
|
|
||||||
return &Event{ typ: .mouse_scroll, x: x, y: y, direction: direction, utf8: single }, end
|
|
||||||
} else {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +343,7 @@ fn escape_sequence(buf_ string) (&Event, int) {
|
|||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if buf.len == 5 && buf[0] == `[` && buf[1] == `1` && buf[2] == `;` {
|
if buf.len == 5 && buf[0] == `[` && buf[1].is_digit() && buf[2] == `;` {
|
||||||
// code = KeyCode(buf[4] + 197)
|
// code = KeyCode(buf[4] + 197)
|
||||||
modifiers = match buf[3] {
|
modifiers = match buf[3] {
|
||||||
`2` { shift }
|
`2` { shift }
|
||||||
@ -333,20 +356,25 @@ fn escape_sequence(buf_ string) (&Event, int) {
|
|||||||
else { modifiers } // probably unreachable? idk, terminal events are strange
|
else { modifiers } // probably unreachable? idk, terminal events are strange
|
||||||
}
|
}
|
||||||
|
|
||||||
code = match buf[4] {
|
if buf[1] == `1` {
|
||||||
`A` { KeyCode.up }
|
code = match buf[4] {
|
||||||
`B` { KeyCode.down }
|
`A` { KeyCode.up }
|
||||||
`C` { KeyCode.right }
|
`B` { KeyCode.down }
|
||||||
`D` { KeyCode.left }
|
`C` { KeyCode.right }
|
||||||
`F` { KeyCode.end }
|
`D` { KeyCode.left }
|
||||||
`H` { KeyCode.home }
|
`F` { KeyCode.end }
|
||||||
`P` { KeyCode.f1 }
|
`H` { KeyCode.home }
|
||||||
`Q` { KeyCode.f2 }
|
`P` { KeyCode.f1 }
|
||||||
`R` { KeyCode.f3 }
|
`Q` { KeyCode.f2 }
|
||||||
`S` { KeyCode.f4 }
|
`R` { KeyCode.f3 }
|
||||||
else { code }
|
`S` { KeyCode.f4 }
|
||||||
|
else { code }
|
||||||
|
}
|
||||||
|
} else if buf[1] == `5` {
|
||||||
|
code = KeyCode.page_up
|
||||||
|
} else if buf[1] == `6` {
|
||||||
|
code = KeyCode.page_down
|
||||||
}
|
}
|
||||||
// && buf[3] >= `2` && buf[3] <= `8` && buf[4] >= `A` && buf[4] <= `D`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Event{ typ: .key_down, code: code, utf8: single, modifiers: modifiers }, end
|
return &Event{ typ: .key_down, code: code, utf8: single, modifiers: modifiers }, end
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
module ui
|
module ui
|
||||||
|
|
||||||
|
import os
|
||||||
import strings
|
import strings
|
||||||
|
|
||||||
pub struct Color {
|
pub struct Color {
|
||||||
@ -18,6 +19,7 @@ pub fn (c Color) hex() string {
|
|||||||
const (
|
const (
|
||||||
bsu = '\x1bP=1s\x1b\\'
|
bsu = '\x1bP=1s\x1b\\'
|
||||||
esu = '\x1bP=2s\x1b\\'
|
esu = '\x1bP=2s\x1b\\'
|
||||||
|
vno_bsu_esu = os.getenv('VNO_BSU_ESU').len>0
|
||||||
)
|
)
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
@ -32,9 +34,13 @@ pub fn (mut ctx Context) flush() {
|
|||||||
// TODO
|
// TODO
|
||||||
} $else {
|
} $else {
|
||||||
// TODO: Diff the previous frame against this one, and only render things that changed?
|
// TODO: Diff the previous frame against this one, and only render things that changed?
|
||||||
C.write(C.STDOUT_FILENO, bsu.str, bsu.len)
|
if vno_bsu_esu {
|
||||||
C.write(C.STDOUT_FILENO, ctx.print_buf.data, ctx.print_buf.len)
|
C.write(C.STDOUT_FILENO, ctx.print_buf.data, ctx.print_buf.len)
|
||||||
C.write(C.STDOUT_FILENO, esu.str, esu.len)
|
} else {
|
||||||
|
C.write(C.STDOUT_FILENO, bsu.str, bsu.len)
|
||||||
|
C.write(C.STDOUT_FILENO, ctx.print_buf.data, ctx.print_buf.len)
|
||||||
|
C.write(C.STDOUT_FILENO, esu.str, esu.len)
|
||||||
|
}
|
||||||
ctx.print_buf.clear()
|
ctx.print_buf.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user