mouse: implement mouse parsing and events

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
This commit is contained in:
Tim Culverhouse 2024-01-31 12:50:00 -06:00
parent ad3ef19b87
commit f3cf7bcfcd
7 changed files with 93 additions and 2 deletions

View file

@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void {
const exe = b.addExecutable(.{
.name = "vaxis",
.root_source_file = .{ .path = "examples/image.zig" },
.root_source_file = .{ .path = "examples/text_input.zig" },
.target = target,
.optimize = optimize,
});

View file

@ -11,6 +11,7 @@ const log = std.log.scoped(.main);
// for a single event loop with exhaustive switching. Booya
const Event = union(enum) {
key_press: vaxis.Key,
mouse: vaxis.Mouse,
winsize: vaxis.Winsize,
focus_in,
focus_out,

View file

@ -13,4 +13,34 @@ pub const Shape = enum {
cell,
};
// TODO: mouse support
pub const Button = enum(u8) {
left,
middle,
right,
none,
wheel_up = 64,
wheel_down = 65,
button_8 = 128,
button_9 = 129,
button_10 = 130,
button_11 = 131,
};
pub const Modifiers = packed struct(u3) {
shift: bool = false,
alt: bool = false,
ctrl: bool = false,
};
pub const Type = enum {
press,
release,
motion,
drag,
};
col: usize,
row: usize,
button: Button,
mods: Modifiers,
type: Type,

View file

@ -2,6 +2,7 @@ const std = @import("std");
const testing = std.testing;
const Event = @import("event.zig").Event;
const Key = @import("Key.zig");
const Mouse = @import("Mouse.zig");
const CodePointIterator = @import("ziglyph").CodePointIterator;
const graphemeBreak = @import("ziglyph").graphemeBreak;
@ -32,6 +33,14 @@ const Sequence = struct {
empty_state: std.StaticBitSet(16) = std.StaticBitSet(16).initEmpty(),
};
const mouse_bits = struct {
const motion: u8 = 0b00100000;
const buttons: u8 = 0b11000011;
const shift: u8 = 0b00000100;
const alt: u8 = 0b00001000;
const ctrl: u8 = 0b00010000;
};
// the state of the parser
const State = enum {
ground,
@ -226,6 +235,49 @@ pub fn parse(self: *Parser, input: []const u8) !Result {
'E' => Key.kp_begin,
'F' => Key.end,
'H' => Key.home,
'M', 'm' => { // mouse event
const priv = seq.private_indicator orelse {
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
return .{ .event = null, .n = i + 1 };
};
if (priv != '<') {
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
return .{ .event = null, .n = i + 1 };
}
if (seq.param_idx != 3) {
log.warn("unhandled csi: CSI {s}", .{input[start + 1 .. i + 1]});
return .{ .event = null, .n = i + 1 };
}
const button: Mouse.Button = @enumFromInt(seq.params[0] & mouse_bits.buttons);
const motion = seq.params[0] & mouse_bits.motion > 0;
const shift = seq.params[0] & mouse_bits.shift > 0;
const alt = seq.params[0] & mouse_bits.alt > 0;
const ctrl = seq.params[0] & mouse_bits.ctrl > 0;
const col: usize = seq.params[1] - 1;
const row: usize = seq.params[2] - 1;
const mouse = Mouse{
.button = button,
.mods = .{
.shift = shift,
.alt = alt,
.ctrl = ctrl,
},
.col = col,
.row = row,
.type = blk: {
if (motion and button != Mouse.Button.none) {
break :blk .drag;
}
if (motion and button == Mouse.Button.none) {
break :blk .motion;
}
if (b == 'm') break :blk .release;
break :blk .press;
},
};
return .{ .event = .{ .mouse = mouse }, .n = i + 1 };
},
'P' => Key.f1,
'Q' => Key.f2,
'R' => Key.f3,

View file

@ -157,6 +157,11 @@ pub fn run(
vx.postEvent(.{ .key_press = mut_key });
}
},
.mouse => |mouse| {
if (@hasField(EventType, "mouse")) {
vx.postEvent(.{ .mouse = mouse });
}
},
.focus_in => {
if (@hasField(EventType, "focus_in")) {
vx.postEvent(.focus_in);

View file

@ -1,8 +1,10 @@
pub const Key = @import("Key.zig");
pub const Mouse = @import("Mouse.zig");
/// The events that Vaxis emits internally
pub const Event = union(enum) {
key_press: Key,
mouse: Mouse,
focus_in,
focus_out,
paste_start,

View file

@ -6,6 +6,7 @@ pub const Cell = cell.Cell;
pub const Style = cell.Style;
pub const Key = @import("Key.zig");
pub const Mouse = @import("Mouse.zig");
pub const Winsize = @import("Tty.zig").Winsize;
pub const widgets = @import("widgets/main.zig");