summaryrefslogtreecommitdiff
path: root/src/mouse.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/mouse.zig')
-rw-r--r--src/mouse.zig47
1 files changed, 38 insertions, 9 deletions
diff --git a/src/mouse.zig b/src/mouse.zig
index ecb9bb57..41362249 100644
--- a/src/mouse.zig
+++ b/src/mouse.zig
@@ -335,7 +335,14 @@ fn programWheel(p: *Pardes, id: usize, m: Mouse, col: u16, row: u16) bool {
return true;
}
-pub const Grab = struct { id: usize, serial: u32, held: u3 = 0 };
+pub const Grab = struct {
+ id: usize,
+ serial: u32,
+ held: u3 = 0,
+ /// Taken by Shift-B2 or Shift-B3: the Shift chose the program, so its
+ /// reports leave it out.
+ unshift: bool = false,
+};
/// The 0-based cell of a terminal program's grid under screen cell
/// `col`,`row`, or null over chrome (tag, gutter, scrollbar, anything past
@@ -360,8 +367,10 @@ fn tracksMouse(pane: *const Pane) bool {
/// it: presses, releases, drags with a button down (1002) and, under 1003,
/// every move, once per cell. A press it took keeps its drag and release
/// wherever the pointer goes. Tags, gutters and the rest of pardes's
-/// chrome are never its, and Shift keeps pardes's own select, execute and
-/// look. True when the event was the program's.
+/// chrome are never its. B1 is the program's and Shift-B1 pardes's select;
+/// B2 and B3 are pardes's execute and look, and Shift-B2 and Shift-B3 the
+/// program's, reported without the Shift that chose it. True when the
+/// event was the program's.
fn programMouse(p: *Pardes, m: Mouse) bool {
const button: ?panes.terminal.MouseButton = switch (m.button) {
.left => .left,
@@ -372,9 +381,10 @@ fn programMouse(p: *Pardes, m: Mouse) bool {
};
const bit: u3 = if (button) |b| @as(u3, 1) << @intCast(@intFromEnum(b)) else 0;
const at = p.presentation.pointer(p.screen_w, p.screen_h, @min(m.col, p.screen_w -| 1), @min(m.row, p.screen_h -| 1));
- const mods: panes.terminal.MouseMods = .{ .shift = m.shift, .alt = m.alt, .ctrl = m.ctrl };
+ var mods: panes.terminal.MouseMods = .{ .shift = m.shift, .alt = m.alt, .ctrl = m.ctrl };
var buf: [64]u8 = undefined;
if (p.mouse_grab) |*grab| {
+ if (grab.unshift) mods.shift = false;
const pane = p.panes[grab.id] orelse {
p.mouse_grab = null;
return false;
@@ -400,7 +410,10 @@ fn programMouse(p: *Pardes, m: Mouse) bool {
if (grab.held == 0) p.mouse_grab = null;
return true;
}
- if (m.shift or p.drag != .none or p.header_drag or p.header_sweep != null) return false;
+ if (p.drag != .none or p.header_drag or p.header_sweep != null) return false;
+ const shifted_to_program = m.button == .middle or m.button == .right;
+ if (m.shift != shifted_to_program) return false;
+ if (shifted_to_program) mods.shift = false;
const mapped = at orelse return false;
const id = paneAt(p, mapped.col, mapped.row) orelse return false;
const pane = p.panes[id].?;
@@ -415,7 +428,7 @@ fn programMouse(p: *Pardes, m: Mouse) bool {
p.active = id;
pane.focus = .body;
pane.closePrompt();
- p.mouse_grab = .{ .id = id, .serial = pane.serial, .held = bit };
+ p.mouse_grab = .{ .id = id, .serial = pane.serial, .held = bit, .unshift = shifted_to_program };
const bytes = panes.terminal.mouseBytes(pane, &buf, .press, button, mods, cell.col, cell.row, true);
if (bytes.len > 0) p.emitWrite(id, bytes);
return true;
@@ -2033,9 +2046,17 @@ test "a program tracking the mouse gets its clicks, drags and moves over its gri
// 1000 has no drags; the release is reported where it lands.
try std.testing.expectEqualStrings("", try send(p, &out, col, row, .{ .button = .left, .kind = .drag, .dx = 1 }));
try std.testing.expectEqualStrings("\x1b[<0;7;4m", try send(p, &out, col, row, .{ .button = .left, .kind = .release, .dx = 1 }));
- // Right with meta and ctrl: 2 + 8 + 16.
- try std.testing.expectEqualStrings("\x1b[<26;6;4M", try send(p, &out, col, row, .{ .button = .right, .kind = .press, .alt = true, .ctrl = true }));
- try std.testing.expectEqualStrings("\x1b[<2;6;4m", try send(p, &out, col, row, .{ .button = .right, .kind = .release }));
+ // B2 and B3 are pardes's execute and look, as in any pane.
+ try std.testing.expectEqualStrings("", try send(p, &out, col, row, .{ .button = .right, .kind = .press }));
+ try std.testing.expect(p.drag == .select);
+ try std.testing.expectEqualStrings("", try send(p, &out, col, row, .{ .button = .right, .kind = .release }));
+ try std.testing.expect(p.drag == .none);
+ // Shift-B3 is the program's, the Shift that chose it left out: meta
+ // and ctrl, 2 + 8 + 16.
+ try std.testing.expectEqualStrings("\x1b[<26;6;4M", try send(p, &out, col, row, .{ .button = .right, .kind = .press, .shift = true, .alt = true, .ctrl = true }));
+ try std.testing.expectEqualStrings("\x1b[<2;6;4m", try send(p, &out, col, row, .{ .button = .right, .kind = .release, .shift = true }));
+ try std.testing.expectEqualStrings("\x1b[<1;6;4M", try send(p, &out, col, row, .{ .button = .middle, .kind = .press, .shift = true }));
+ try std.testing.expectEqualStrings("\x1b[<1;6;4m", try send(p, &out, col, row, .{ .button = .middle, .kind = .release }));
// Moves are no 1000 program's business: pardes hovers.
try std.testing.expectEqualStrings("", try send(p, &out, col, row, .{ .button = .none, .kind = .motion }));
// Shift: pardes's own sweep, the program hears nothing of it.
@@ -2066,6 +2087,14 @@ test "a program tracking the mouse gets its clicks, drags and moves over its gri
try std.testing.expect(p.mouse_grab == null);
// Over the tag a bare move is pardes's.
try std.testing.expectEqualStrings("", try send(p, &out, col, tag_row, .{ .button = .none, .kind = .motion }));
+ // B2 held for pardes: its sweep's moves are not the program's; held
+ // for the program with Shift, they are its button-2 drags.
+ try std.testing.expectEqualStrings("", try send(p, &out, col, row, .{ .button = .middle, .kind = .press }));
+ try std.testing.expectEqualStrings("", try send(p, &out, col, row, .{ .button = .middle, .kind = .drag, .dx = 1 }));
+ try std.testing.expectEqualStrings("", try send(p, &out, col, row, .{ .button = .middle, .kind = .release, .dx = 1 }));
+ try std.testing.expectEqualStrings("\x1b[<1;6;4M", try send(p, &out, col, row, .{ .button = .middle, .kind = .press, .shift = true }));
+ try std.testing.expectEqualStrings("\x1b[<33;7;4M", try send(p, &out, col, row, .{ .button = .middle, .kind = .drag, .dx = 1, .shift = true }));
+ try std.testing.expectEqualStrings("\x1b[<1;7;4m", try send(p, &out, col, row, .{ .button = .middle, .kind = .release, .dx = 1, .shift = true }));
// Legacy X10 bytes: a release is button 3. (Turning 1003 off turns
// tracking off, as xterm's one tracking mode goes.)