diff --git a/src/Surface.zig b/src/Surface.zig index a90212123..f01e49b96 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1516,6 +1516,12 @@ fn mouseShiftCapture(self: *const Surface, lock: bool) bool { if (lock) self.renderer_state.mutex.lock(); defer if (lock) self.renderer_state.mutex.unlock(); + + // If thet terminal explicitly requests it then we always allow it + // since we processed never/always at this point. + if (self.io.terminal.flags.mouse_shift_capture) return true; + + // Otherwise, go with the user's preference return switch (self.config.mouse_shift_capture) { .false => false, .true => true, diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 67dfcc310..caea93437 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -102,6 +102,11 @@ flags: packed struct { /// this was called so we have to track it separately. mouse_event: MouseEvents = .none, mouse_format: MouseFormat = .x10, + + /// Set via the XTSHIFTESCAPE sequence. If true (XTSHIFTESCAPE = 1) + /// then we want to capture the shift key for the mouse protocol + /// if the configuration allows it. + mouse_shift_capture: bool = false, } = .{}, /// The event types that can be reported for mouse-related activities. diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index 5d6f5cdae..d3bc372a1 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -815,6 +815,30 @@ pub fn Stream(comptime Handler: type) type { } }, + // XTSHIFTESCAPE + '>' => if (@hasDecl(T, "setMouseShiftCapture")) capture: { + const capture = switch (action.params.len) { + 0 => false, + 1 => switch (action.params[0]) { + 0 => false, + 1 => true, + else => { + log.warn("invalid XTSHIFTESCAPE command: {}", .{action}); + break :capture; + }, + }, + else => { + log.warn("invalid XTSHIFTESCAPE command: {}", .{action}); + break :capture; + }, + }; + + try self.handler.setMouseShiftCapture(capture); + } else log.warn( + "unimplemented CSI callback: {}", + .{action}, + ), + else => log.warn( "unknown CSI s with intermediate: {}", .{action}, @@ -1521,3 +1545,26 @@ test "stream: DECSCUSR without space" { try s.nextSlice("\x1B[1q"); try testing.expect(s.handler.style == null); } + +test "stream: XTSHIFTESCAPE" { + const H = struct { + escape: ?bool = null, + + pub fn setMouseShiftCapture(self: *@This(), v: bool) !void { + self.escape = v; + } + }; + + var s: Stream(H) = .{ .handler = .{} }; + try s.nextSlice("\x1B[>2s"); + try testing.expect(s.handler.escape == null); + + try s.nextSlice("\x1B[>s"); + try testing.expect(s.handler.escape.? == false); + + try s.nextSlice("\x1B[>0s"); + try testing.expect(s.handler.escape.? == false); + + try s.nextSlice("\x1B[>1s"); + try testing.expect(s.handler.escape.? == true); +} diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index 0938bae5a..85bb0d49c 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -1521,6 +1521,10 @@ const StreamHandler = struct { } } + pub fn setMouseShiftCapture(self: *StreamHandler, v: bool) !void { + self.terminal.flags.mouse_shift_capture = v; + } + pub fn setAttribute(self: *StreamHandler, attr: terminal.Attribute) !void { switch (attr) { .unknown => |unk| log.warn("unimplemented or unknown SGR attribute: {any}", .{unk}),