mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
apprt/embedded: proper consumed modifier state for ctrl keys
This commit is contained in:
@ -849,28 +849,8 @@ extension Ghostty {
|
||||
var handled: Bool = false
|
||||
if let list = keyTextAccumulator, list.count > 0 {
|
||||
handled = true
|
||||
|
||||
// This is a hack. libghostty on macOS treats ctrl input as not having
|
||||
// text because some keyboard layouts generate bogus characters for
|
||||
// ctrl+key. libghostty can't tell this is from an IM keyboard giving
|
||||
// us direct values. So, we just remove control.
|
||||
var modifierFlags = event.modifierFlags
|
||||
modifierFlags.remove(.control)
|
||||
if let keyTextEvent = NSEvent.keyEvent(
|
||||
with: .keyDown,
|
||||
location: event.locationInWindow,
|
||||
modifierFlags: modifierFlags,
|
||||
timestamp: event.timestamp,
|
||||
windowNumber: event.windowNumber,
|
||||
context: nil,
|
||||
characters: event.characters ?? "",
|
||||
charactersIgnoringModifiers: event.charactersIgnoringModifiers ?? "",
|
||||
isARepeat: event.isARepeat,
|
||||
keyCode: event.keyCode
|
||||
) {
|
||||
for text in list {
|
||||
_ = keyAction(action, event: keyTextEvent, text: text)
|
||||
}
|
||||
for text in list {
|
||||
_ = keyAction(action, event: event, text: text)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,14 +182,9 @@ pub const App = struct {
|
||||
if (strip) translate_mods.alt = false;
|
||||
}
|
||||
|
||||
// On macOS we strip ctrl because UCKeyTranslate
|
||||
// converts to the masked values (i.e. ctrl+c becomes 3)
|
||||
// and we don't want that behavior.
|
||||
//
|
||||
// We also strip super because its not used for translation
|
||||
// on macos and it results in a bad translation.
|
||||
// We strip super on macOS because its not used for translation
|
||||
// it results in a bad translation.
|
||||
if (comptime builtin.target.isDarwin()) {
|
||||
translate_mods.ctrl = false;
|
||||
translate_mods.super = false;
|
||||
}
|
||||
|
||||
@ -229,6 +224,7 @@ pub const App = struct {
|
||||
const result: input.Keymap.Translation = if (event_text) |text| .{
|
||||
.text = text,
|
||||
.composing = event.composing,
|
||||
.mods = translate_mods,
|
||||
} else try self.keymap.translate(
|
||||
&buf,
|
||||
switch (target) {
|
||||
@ -273,16 +269,12 @@ pub const App = struct {
|
||||
// then we clear the text. We handle non-printables in the
|
||||
// key encoder manual (such as tab, ctrl+c, etc.)
|
||||
if (result.text.len == 1 and result.text[0] < 0x20) {
|
||||
break :translate .{ .composing = false, .text = "" };
|
||||
break :translate .{};
|
||||
}
|
||||
}
|
||||
|
||||
break :translate result;
|
||||
} else .{ .composing = false, .text = "" };
|
||||
|
||||
// UCKeyTranslate always consumes all mods, so if we have any output
|
||||
// then we've consumed our translate mods.
|
||||
const consumed_mods: input.Mods = if (result.text.len > 0) translate_mods else .{};
|
||||
} else .{};
|
||||
|
||||
// We need to always do a translation with no modifiers at all in
|
||||
// order to get the "unshifted_codepoint" for the key event.
|
||||
@ -354,7 +346,7 @@ pub const App = struct {
|
||||
.key = key,
|
||||
.physical_key = physical_key,
|
||||
.mods = mods,
|
||||
.consumed_mods = consumed_mods,
|
||||
.consumed_mods = result.mods,
|
||||
.composing = result.composing,
|
||||
.utf8 = result.text,
|
||||
.unshifted_codepoint = unshifted_codepoint,
|
||||
|
@ -50,10 +50,13 @@ pub const State = struct {
|
||||
pub const Translation = struct {
|
||||
/// The translation result. If this is a dead key state, then this will
|
||||
/// be pre-edit text that can be displayed but will ultimately be replaced.
|
||||
text: []const u8,
|
||||
text: []const u8 = "",
|
||||
|
||||
/// Whether the text is still composing, i.e. this is a dead key state.
|
||||
composing: bool,
|
||||
composing: bool = false,
|
||||
|
||||
/// The mods that were consumed to produce this translation
|
||||
mods: Mods = .{},
|
||||
};
|
||||
|
||||
pub fn init() !Keymap {
|
||||
@ -122,8 +125,18 @@ pub fn translate(
|
||||
out: []u8,
|
||||
state: *State,
|
||||
code: u16,
|
||||
mods: Mods,
|
||||
input_mods: Mods,
|
||||
) !Translation {
|
||||
// On macOS we strip ctrl because UCKeyTranslate
|
||||
// converts to the masked values (i.e. ctrl+c becomes 3)
|
||||
// and we don't want that behavior in Ghostty ever. This makes
|
||||
// this file not a general-purpose keymap implementation.
|
||||
const mods: Mods = mods: {
|
||||
var v = input_mods;
|
||||
v.ctrl = false;
|
||||
break :mods v;
|
||||
};
|
||||
|
||||
// Get the keycode for the space key, using comptime.
|
||||
const code_space: u16 = comptime space: for (codes) |entry| {
|
||||
if (std.mem.eql(u8, entry.code, "Space"))
|
||||
@ -183,7 +196,11 @@ pub fn translate(
|
||||
|
||||
// Convert the utf16 to utf8
|
||||
const len = try std.unicode.utf16leToUtf8(out, char[0..char_count]);
|
||||
return .{ .text = out[0..len], .composing = composing };
|
||||
return .{
|
||||
.text = out[0..len],
|
||||
.composing = composing,
|
||||
.mods = mods,
|
||||
};
|
||||
}
|
||||
|
||||
/// Map to the modifiers format used by the UCKeyTranslate function.
|
||||
|
@ -6,8 +6,9 @@ const Mods = @import("key.zig").Mods;
|
||||
|
||||
pub const State = struct {};
|
||||
pub const Translation = struct {
|
||||
text: []const u8,
|
||||
composing: bool,
|
||||
text: []const u8 = "",
|
||||
composing: bool = false,
|
||||
mods: Mods = .{},
|
||||
};
|
||||
|
||||
pub fn init() !KeymapNoop {
|
||||
|
Reference in New Issue
Block a user