feat(terminal): report mouse events to terminal applications

This commit is contained in:
CJ van den Berg 2026-02-27 23:13:02 +01:00
parent df5c426383
commit b1e13f036d
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
2 changed files with 92 additions and 23 deletions

View file

@ -30,8 +30,8 @@
.hash = "fuzzig-0.1.1-Ji0xivxIAQBD0g8O_NV_0foqoPf3elsg9Sc3pNfdVH4D",
},
.vaxis = .{
.url = "git+https://github.com/neurocyte/libvaxis?ref=main#9c8e0a7d61c75ea6aee3b69761acfb36899709e6",
.hash = "vaxis-0.5.1-BWNV_LC4CQDtTTjkRwC90yjbqAtv2AnDiRpU0e8c_BtF",
.url = "git+https://github.com/neurocyte/libvaxis?ref=main#db8eee5fc5e5d1b942e57b7645b2b1e39a3e37f1",
.hash = "vaxis-0.5.1-BWNV_LHUCQBX3VsvEzYYeHuShpG6o1br7Vc4h-wxQjJZ",
},
.zeit = .{
.url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88",

View file

@ -113,29 +113,98 @@ pub fn receive(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool {
tui.need_render(@src());
return true;
}
if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON1), tp.more }) or
try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON2), tp.more }) or
try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON3), tp.more }))
switch (tui.set_focus_by_mouse_event()) {
.changed => return true,
.same, .notfound => {},
};
if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON4), tp.more })) {
if (self.vt.vt.scroll(3)) tui.need_render(@src());
return true;
}
if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON5), tp.more })) {
if (self.vt.vt.scroll(-3)) tui.need_render(@src());
return true;
// Mouse button press - set focus first, then forward to terminal if reporting is on
{
var btn: i64 = 0;
var col: i64 = 0;
var row: i64 = 0;
var xoffset: i64 = 0;
var yoffset: i64 = 0;
if (try m.match(.{ "B", input.event.press, tp.extract(&btn), tp.any, tp.extract(&col), tp.extract(&row), tp.extract(&xoffset), tp.extract(&yoffset) }) or
try m.match(.{ "B", input.event.release, tp.extract(&btn), tp.any, tp.extract(&col), tp.extract(&row), tp.extract(&xoffset), tp.extract(&yoffset) }))
{
const button: vaxis.Mouse.Button = @enumFromInt(btn);
const is_press = try m.match(.{ "B", input.event.press, tp.more });
// Set focus on left/middle/right button press
if (is_press) switch (button) {
.left, .middle, .right => switch (tui.set_focus_by_mouse_event()) {
.changed => return true,
.same, .notfound => {},
},
// Scroll wheel: forward to vt if reporting active, else scroll scrollback
.wheel_up => {
if (self.vt.vt.mode.mouse == .none) {
if (self.vt.vt.scroll(3)) tui.need_render(@src());
return true;
}
},
.wheel_down => {
if (self.vt.vt.mode.mouse == .none) {
if (self.vt.vt.scroll(-3)) tui.need_render(@src());
return true;
}
},
else => {},
};
// Forward to vt if terminal mouse reporting is active
if (self.focused and self.vt.vt.mode.mouse != .none) {
const rel = self.plane.abs_yx_to_rel(@intCast(row), @intCast(col));
const mouse_event: vaxis.Mouse = .{
.col = @intCast(rel[1]),
.row = @intCast(rel[0]),
.xoffset = @intCast(xoffset),
.yoffset = @intCast(yoffset),
.button = button,
.mods = .{},
.type = if (is_press) .press else .release,
};
self.vt.vt.update(.{ .mouse = mouse_event }) catch {};
tui.need_render(@src());
return true;
}
return false;
}
// Mouse drag
if (try m.match(.{ "D", input.event.press, tp.extract(&btn), tp.any, tp.extract(&col), tp.extract(&row), tp.extract(&xoffset), tp.extract(&yoffset) })) {
if (self.focused and self.vt.vt.mode.mouse != .none) {
const rel = self.plane.abs_yx_to_rel(@intCast(row), @intCast(col));
const mouse_event: vaxis.Mouse = .{
.col = @intCast(rel[1]),
.row = @intCast(rel[0]),
.xoffset = @intCast(xoffset),
.yoffset = @intCast(yoffset),
.button = @enumFromInt(btn),
.mods = .{},
.type = .drag,
};
self.vt.vt.update(.{ .mouse = mouse_event }) catch {};
tui.need_render(@src());
return true;
}
return false;
}
// Mouse motion (no button held)
if (try m.match(.{ "M", tp.extract(&col), tp.extract(&row), tp.extract(&xoffset), tp.extract(&yoffset) })) {
if (self.focused and self.vt.vt.mode.mouse == .any_event) {
const rel = self.plane.abs_yx_to_rel(@intCast(row), @intCast(col));
const mouse_event: vaxis.Mouse = .{
.col = @intCast(rel[1]),
.row = @intCast(rel[0]),
.xoffset = @intCast(xoffset),
.yoffset = @intCast(yoffset),
.button = .none,
.mods = .{},
.type = .motion,
};
self.vt.vt.update(.{ .mouse = mouse_event }) catch {};
tui.need_render(@src());
return true;
}
return false;
}
}
if (!(try m.match(.{ "I", tp.more })
// or
// try m.match(.{ "B", tp.more }) or
// try m.match(.{ "D", tp.more }) or
// try m.match(.{ "M", tp.more })
))
if (!(try m.match(.{ "I", tp.more })))
return false;
if (!self.focused) return false;