From f7cc5ccdd61da6e82e79d0ac5e2c3814df0ed4a4 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 11 Oct 2023 21:38:52 -0700 Subject: [PATCH] config: add mouse-shift-capture configuration --- src/Surface.zig | 42 ++++++++++++++++++++++++++++++++++++++---- src/config.zig | 1 + src/config/Config.zig | 30 ++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/Surface.zig b/src/Surface.zig index 1bdad82f8..a90212123 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -142,6 +142,7 @@ const DerivedConfig = struct { confirm_close_surface: bool, mouse_interval: u64, mouse_hide_while_typing: bool, + mouse_shift_capture: configpkg.MouseShiftCapture, macos_non_native_fullscreen: configpkg.NonNativeFullscreen, macos_option_as_alt: configpkg.OptionAsAlt, window_padding_x: u32, @@ -162,6 +163,7 @@ const DerivedConfig = struct { .confirm_close_surface = config.@"confirm-close-surface", .mouse_interval = config.@"click-repeat-interval" * 1_000_000, // 500ms .mouse_hide_while_typing = config.@"mouse-hide-while-typing", + .mouse_shift_capture = config.@"mouse-shift-capture", .macos_non_native_fullscreen = config.@"macos-non-native-fullscreen", .macos_option_as_alt = config.@"macos-option-as-alt", .window_padding_x = config.@"window-padding-x", @@ -1501,6 +1503,26 @@ fn mouseReport( try self.io_thread.wakeup.notify(); } +/// Returns true if the shift modifier is allowed to be captured by modifier +/// events. It is up to the caller to still verify it is a situation in which +/// shift capture makes sense (i.e. left button, mouse click, etc.) +fn mouseShiftCapture(self: *const Surface, lock: bool) bool { + // Handle our never/always case where we don't need a lock. + switch (self.config.mouse_shift_capture) { + .never => return false, + .always => return true, + .false, .true => {}, + } + + if (lock) self.renderer_state.mutex.lock(); + defer if (lock) self.renderer_state.mutex.unlock(); + return switch (self.config.mouse_shift_capture) { + .false => false, + .true => true, + .never, .always => unreachable, // handled earlier + }; +} + pub fn mouseButtonCallback( self: *Surface, action: input.MouseButtonState, @@ -1519,11 +1541,20 @@ pub fn mouseButtonCallback( // Always show the mouse again if it is hidden if (self.mouse.hidden) self.showMouse(); + // This is set to true if the terminal is allowed to capture the shift + // modifer. Note we can do this more efficiently probably with less + // locking/unlocking but clicking isn't that frequent enough to be a + // bottleneck. + const shift_capture = self.mouseShiftCapture(true); + // Shift-click continues the previous mouse state if we have a selection. // cursorPosCallback will also do a mouse report so we don't need to do any // of the logic below. if (button == .left and action == .press) { - if (mods.shift and self.mouse.left_click_count > 0) { + if (mods.shift and + self.mouse.left_click_count > 0 and + !shift_capture) + { // Checking for selection requires the renderer state mutex which // sucks but this should be pretty rare of an event so it won't // cause a ton of contention. @@ -1546,8 +1577,9 @@ pub fn mouseButtonCallback( self.renderer_state.mutex.lock(); defer self.renderer_state.mutex.unlock(); if (self.io.terminal.flags.mouse_event != .none) report: { - // Shift overrides mouse "grabbing" in the window, taken from Kitty. - if (mods.shift) break :report; + // If we have shift-pressed and we aren't allowed to capture it, + // then we do not do a mouse report. + if (mods.shift and button == .left and !shift_capture) break :report; // In any other mouse button scenario without shift pressed we // clear the selection since the underlying application can handle @@ -1682,7 +1714,9 @@ pub fn cursorPosCallback( // Do a mouse report if (self.io.terminal.flags.mouse_event != .none) report: { // Shift overrides mouse "grabbing" in the window, taken from Kitty. - if (self.mouse.mods.shift) break :report; + if (self.mouse.mods.shift and + self.mouse.click_state[@intFromEnum(input.MouseButton.left)] == .press and + !self.mouseShiftCapture(false)) break :report; // We use the first mouse button we find pressed in order to report // since the spec (afaict) does not say... diff --git a/src/config.zig b/src/config.zig index 7115b04a6..a6f4113f0 100644 --- a/src/config.zig +++ b/src/config.zig @@ -6,6 +6,7 @@ pub const Config = @import("config/Config.zig"); // Field types pub const CopyOnSelect = Config.CopyOnSelect; pub const Keybinds = Config.Keybinds; +pub const MouseShiftCapture = Config.MouseShiftCapture; pub const NonNativeFullscreen = Config.NonNativeFullscreen; pub const OptionAsAlt = Config.OptionAsAlt; diff --git a/src/config/Config.zig b/src/config/Config.zig index 12c235f6c..c21564f42 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -197,6 +197,28 @@ palette: Palette = .{}, /// cursor is over the active terminal surface. @"mouse-hide-while-typing": bool = false, +/// Determines whether running programs can detect the shift key pressed +/// with a mouse click. Typically, the shift key is used to extend mouse +/// selection. +/// +/// The default value of "false" means that the shift key is not sent +/// with the mouse protocol and will extend the selection. This value +/// can be conditionally overridden by the running program with the +/// XTSHIFTESCAPE sequence. +/// +/// The value "true" means that the shift key is sent with the mouse +/// protocol but the running program can override this behavior with +/// XTSHIFTESCAPE. +/// +/// The value "never" is the same as "false" but the running program +/// cannot override this behavior with XTSHIFTESCAPE. The value "always" +/// is the same as "true" but the running program cannot override this +/// behavior with XTSHIFTESCAPE. +/// +/// If you always want shift to extend mouse selection even if the +/// program requests otherwise, set this to "never". +@"mouse-shift-capture": MouseShiftCapture = .false, + /// The opacity level (opposite of transparency) of the background. /// A value of 1 is fully opaque and a value of 0 is fully transparent. /// A value less than 0 or greater than 1 will be clamped to the nearest @@ -1930,3 +1952,11 @@ pub const GtkSingleInstance = enum { false, true, }; + +/// See mouse-shift-capture +pub const MouseShiftCapture = enum { + false, + true, + always, + never, +};