diff --git a/src/Surface.zig b/src/Surface.zig index ef1d394a0..44232fed6 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -90,12 +90,6 @@ padding: renderer.Padding, /// the lifetime of. This makes updating config at runtime easier. config: DerivedConfig, -/// Set to true for a single GLFW key/char callback cycle to cause the -/// char callback to ignore. GLFW seems to always do key followed by char -/// callbacks so we abuse that here. This is to solve an issue where commands -/// like such as "control-v" will write a "v" even if they're intercepted. -ignore_char: bool = false, - /// This is set to true if our IO thread notifies us our child exited. /// This is used to determine if we need to confirm, hold open, etc. child_exited: bool = false, @@ -986,12 +980,6 @@ pub fn charCallback(self: *Surface, codepoint: u21) !void { } else |_| {} } - // Ignore if requested. See field docs for more information. - if (self.ignore_char) { - self.ignore_char = false; - return; - } - // Critical area { self.renderer_state.mutex.lock(); @@ -1048,10 +1036,6 @@ pub fn keyCallback( } else |_| {} } - // Reset the ignore char setting. If we didn't handle the char - // by here, we aren't going to get it so we just reset this. - self.ignore_char = false; - if (action == .press or action == .repeat) { // Mods for bindings never include caps/num lock. const binding_mods = mods: { @@ -1080,12 +1064,7 @@ pub fn keyCallback( if (binding_action_) |binding_action| { //log.warn("BINDING ACTION={}", .{binding_action}); try self.performBindingAction(binding_action); - - // Bindings always result in us ignoring the char if printable - self.ignore_char = true; - - // No matter what, if there is a binding then we are done. - return self.ignore_char; + return true; } // Handle non-printables @@ -1143,18 +1122,6 @@ pub fn keyCallback( }; }; if (char > 0) { - // We are handling this char so don't allow charCallback to do - // anything. Normally it shouldn't because charCallback should not - // be called for control characters. But, we found a scenario where - // it does: https://github.com/mitchellh/ghostty/issues/267 - // - // In case that URL goes away: on macOS, after typing a dead - // key sequence, macOS would call `insertText` with control - // characters. Prior to calling a dead key sequence, it would - // not. I don't know. It doesn't matter, this is more correct - // anyways. - self.ignore_char = true; - // Ask our IO thread to write the data var data: termio.Message.WriteReq.Small.Array = undefined; data[0] = @intCast(char); @@ -1176,10 +1143,12 @@ pub fn keyCallback( log.warn("error scrolling to bottom err={}", .{err}); }; } + + return true; } } - return self.ignore_char; + return false; } pub fn focusCallback(self: *Surface, focused: bool) !void { diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index 47b4f52b3..22d205b45 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -441,15 +441,6 @@ pub const Surface = struct { // If we consume the key then we want to reset the dead key state. if (consumed) { self.keymap_state = .{}; - - // This is kloodge right now to reset the surface ignore_char - // state. We should refactor the API contract with the surface - // to be that if we consume a key then we don't call the char - // callback. - // - // If you don't do this, then after a consumed char a pure - // char event will be ignored. i.e. an emoji keyboard entry. - self.core_surface.charCallback(0) catch {}; return; } } diff --git a/src/apprt/glfw.zig b/src/apprt/glfw.zig index 6b0edc22a..a5cfcc12a 100644 --- a/src/apprt/glfw.zig +++ b/src/apprt/glfw.zig @@ -282,6 +282,10 @@ pub const Surface = struct { /// A core surface core_surface: CoreSurface, + /// This is set to true when keyCallback consumes the input, suppressing + /// the charCallback from being fired. + key_consumed: bool = false, + pub const Options = struct {}; /// Initialize the surface into the given self pointer. This gives a @@ -586,6 +590,13 @@ pub const Surface = struct { defer tracy.end(); const core_win = window.getUserPointer(CoreSurface) orelse return; + + // If our keyCallback consumed the key input, don't emit a char. + if (core_win.rt_surface.key_consumed) { + core_win.rt_surface.key_consumed = false; + return; + } + core_win.charCallback(codepoint) catch |err| { log.err("error in char callback err={}", .{err}); return; @@ -601,6 +612,11 @@ pub const Surface = struct { ) void { _ = scancode; + const core_win = window.getUserPointer(CoreSurface) orelse return; + + // Reset our consumption state + core_win.rt_surface.key_consumed = false; + const tracy = trace(@src()); defer tracy.end(); @@ -739,8 +755,12 @@ pub const Surface = struct { // TODO: we need to do mapped keybindings - const core_win = window.getUserPointer(CoreSurface) orelse return; - _ = core_win.keyCallback(action, key, key, mods) catch |err| { + core_win.rt_surface.key_consumed = core_win.keyCallback( + action, + key, + key, + mods, + ) catch |err| { log.err("error in key callback err={}", .{err}); return; }; diff --git a/src/apprt/gtk.zig b/src/apprt/gtk.zig index e202b8778..6966cf9ee 100644 --- a/src/apprt/gtk.zig +++ b/src/apprt/gtk.zig @@ -1214,15 +1214,6 @@ pub const Surface = struct { // If we consume the key then we want to reset the dead key state. if (consumed) { c.gtk_im_context_reset(self.im_context); - - // This is kloodge right now to reset the surface ignore_char - // state. We should refactor the API contract with the surface - // to be that if we consume a key then we don't call the char - // callback. - // - // If you don't do this, then after a consumed char a pure - // char event will be ignored. i.e. an emoji keyboard entry. - self.core_surface.charCallback(0) catch {}; return 1; } }