add(input): mouse support
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 46s

This commit is contained in:
2025-02-17 23:36:27 +01:00
parent c2080ab40f
commit e2f9408850
4 changed files with 81 additions and 2 deletions

View File

@@ -7,6 +7,7 @@ const mergeTaggedUnions = event.mergeTaggedUnions;
const isTaggedUnion = event.isTaggedUnion;
const key = @import("key.zig");
const Mouse = @import("mouse.zig").Mouse;
const Key = key.Key;
const Size = @import("size.zig").Size;
const Queue = @import("queue.zig").Queue;
@@ -95,6 +96,7 @@ pub fn App(comptime E: type) type {
try terminal.saveScreen();
try terminal.enterAltScreen();
try terminal.hideCursor();
try terminal.enableMouseSupport();
// send initial size afterwards
const size = terminal.getTerminalSize();
@@ -104,6 +106,7 @@ pub fn App(comptime E: type) type {
pub fn interrupt(this: *@This()) !void {
this.quit_event.set();
try terminal.disableMouseSupport();
try terminal.exitAltScreen();
try terminal.restoreScreen();
if (this.thread) |thread| {
@@ -115,9 +118,10 @@ pub fn App(comptime E: type) type {
pub fn stop(this: *@This()) !void {
try this.interrupt();
if (this.termios) |*termios| {
try terminal.disableRawMode(termios);
try terminal.disableMouseSupport();
try terminal.showCursor();
try terminal.exitAltScreen();
try terminal.disableRawMode(termios);
try terminal.restoreScreen();
}
this.termios = null;
@@ -171,6 +175,7 @@ pub fn App(comptime E: type) type {
// FIX: I still think that there is a race condition (I'm just waiting 'long' enough)
this.quit_event.timedWait(20 * std.time.ns_per_ms) catch {
const read_bytes = try terminal.read(buf[0..]);
// TODO: `break` should not terminate the reading of the user inputs, but instead only the received faulty input!
// escape key presses
if (buf[0] == 0x1b and read_bytes > 1) {
switch (buf[1]) {
@@ -267,7 +272,47 @@ pub fn App(comptime E: type) type {
},
'I' => this.postEvent(.{ .focus = true }),
'O' => this.postEvent(.{ .focus = false }),
// 'M', 'm' => return parseMouse(sequence), // TODO: parse mouse inputs
'M', 'm' => {
std.debug.assert(sequence.len >= 4);
if (sequence[2] != '<') break;
const delim1 = std.mem.indexOfScalarPos(u8, sequence, 3, ';') orelse break;
const button_mask = std.fmt.parseUnsigned(u16, sequence[3..delim1], 10) catch break;
const delim2 = std.mem.indexOfScalarPos(u8, sequence, delim1 + 1, ';') orelse break;
const px = std.fmt.parseUnsigned(u16, sequence[delim1 + 1 .. delim2], 10) catch break;
const py = std.fmt.parseUnsigned(u16, sequence[delim2 + 1 .. sequence.len - 1], 10) catch break;
const mouse_bits = packed struct {
const motion: u8 = 0b00100000;
const buttons: u8 = 0b11000011;
const shift: u8 = 0b00000100;
const alt: u8 = 0b00001000;
const ctrl: u8 = 0b00010000;
};
const button: Mouse.Button = @enumFromInt(button_mask & mouse_bits.buttons);
const motion = button_mask & mouse_bits.motion > 0;
// const shift = button_mask & mouse_bits.shift > 0;
// const alt = button_mask & mouse_bits.alt > 0;
// const ctrl = button_mask & mouse_bits.ctrl > 0;
const mouse = Mouse{
.button = button,
.col = px -| 1,
.row = py -| 1,
.kind = blk: {
if (motion and button != Mouse.Button.none) {
break :blk .drag;
}
if (motion and button == Mouse.Button.none) {
break :blk .motion;
}
if (sequence[sequence.len - 1] == 'm') break :blk .release;
break :blk .press;
},
};
this.postEvent(.{ .mouse = mouse });
},
'c' => {
// Primary DA (CSI ? Pm c)
},

View File

@@ -3,6 +3,7 @@
const std = @import("std");
const terminal = @import("terminal.zig");
const Mouse = @import("mouse.zig").Mouse;
const Size = @import("size.zig").Size;
const Key = @import("key.zig").Key;
@@ -23,6 +24,8 @@ pub const SystemEvent = union(enum) {
resize: Size,
/// Input key event received from the user
key: Key,
/// Mouse input event
mouse: Mouse,
/// Focus event for mouse interaction
/// TODO: this should instead be a union with a `Size` to derive which container / element the focus meant for
focus: bool,

View File

@@ -1,4 +1,6 @@
//! Keybindings and Modifiers for user input detection and selection.
// TODO: rename this module to 'input' and include the mouse.zig contents as well!
const std = @import("std");
pub const Key = packed struct {

29
src/mouse.zig Normal file
View File

@@ -0,0 +1,29 @@
/// Mouse input detection.
pub const Mouse = packed struct {
col: u16,
row: u16,
button: Button,
kind: Kind,
pub const Button = enum(u8) {
left,
middle,
right,
none,
wheel_up = 64,
wheel_down = 65,
wheel_right = 66,
wheel_left = 67,
button_8 = 128,
button_9 = 129,
button_10 = 130,
button_11 = 131,
};
pub const Kind = enum(u2) {
press,
release,
motion,
drag,
};
};