mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-06-01 21:18:38 +03:00
macos: use the new self-hosted translation
This commit is contained in:
@ -349,7 +349,15 @@ extension Ghostty {
|
||||
let action = event.isARepeat ? GHOSTTY_ACTION_REPEAT : GHOSTTY_ACTION_PRESS
|
||||
keyAction(action, event: event)
|
||||
|
||||
self.interpretKeyEvents([event])
|
||||
// We specifically DO NOT call interpretKeyEvents because ghostty_surface_key
|
||||
// automatically handles all key translation, and we don't handle any commands
|
||||
// currently.
|
||||
//
|
||||
// It is possible that in the future we'll have to modify ghostty_surface_key
|
||||
// and the embedding API so that we can call this because macOS needs to do
|
||||
// some things with certain keys. I'm not sure. For now this works.
|
||||
//
|
||||
// self.interpretKeyEvents([event])
|
||||
}
|
||||
|
||||
override func keyUp(with event: NSEvent) {
|
||||
@ -359,28 +367,7 @@ extension Ghostty {
|
||||
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent) {
|
||||
guard let surface = self.surface else { return }
|
||||
let mods = Self.translateFlags(event.modifierFlags)
|
||||
let unmapped_key = Self.keycodes[event.keyCode] ?? GHOSTTY_KEY_INVALID
|
||||
|
||||
ghostty_surface_key2(surface, action, UInt32(event.keyCode), mods)
|
||||
|
||||
// We translate the key to the localized keyboard layout. However, we only support
|
||||
// ASCII characters to make our translation easier across platforms. This is something
|
||||
// we want to make a lot more robust in the future, so this will hopefully change.
|
||||
// For now, this makes most keyboard layouts work, and for those that don't, they can
|
||||
// use physical keycode mappings.
|
||||
let key = {
|
||||
if let str = event.characters(byApplyingModifiers: .init(rawValue: 0)) {
|
||||
if str.utf8.count == 1, let firstByte = str.utf8.first {
|
||||
if let translatedKey = Self.ascii[firstByte] {
|
||||
return translatedKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unmapped_key
|
||||
}()
|
||||
|
||||
ghostty_surface_key(surface, action, key, unmapped_key, mods)
|
||||
}
|
||||
|
||||
// MARK: Menu Handlers
|
||||
|
@ -395,6 +395,81 @@ pub const Surface = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn key2Callback(
|
||||
self: *Surface,
|
||||
action: input.Action,
|
||||
keycode: u32,
|
||||
mods: input.Mods,
|
||||
) !void {
|
||||
if (action != .press) return;
|
||||
|
||||
// Translate our key using the keymap for our localized keyboard layout.
|
||||
var buf: [128]u8 = undefined;
|
||||
const result = try self.app.keymap.translate(
|
||||
&buf,
|
||||
&self.keymap_state,
|
||||
@intCast(keycode),
|
||||
mods,
|
||||
);
|
||||
|
||||
log.warn("TRANSLATE: action={} keycode={x} dead={} key={any} key_str={s} mods={}", .{
|
||||
action,
|
||||
keycode,
|
||||
result.composing,
|
||||
result.text,
|
||||
result.text,
|
||||
mods,
|
||||
});
|
||||
|
||||
// If this is a dead key, then we're composing a character and
|
||||
// we end processing here. We don't process keybinds for dead keys.
|
||||
if (result.composing) {
|
||||
// TODO: we ultimately want to update some surface state so that
|
||||
// we can show the user that we're in dead key mode and the
|
||||
// precomposed character. For now, we can just ignore and that
|
||||
// is not incorrect behavior.
|
||||
log.warn("dead key mode, currently composing", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to get the physical unmapped key to process keybinds.
|
||||
const physical_key = keycode: for (input.keycodes.entries) |entry| {
|
||||
if (entry.native == keycode) break :keycode entry.key;
|
||||
} else .invalid;
|
||||
|
||||
// If the resulting text has length 1 then we can take its key
|
||||
// and attempt to translate it to a key enum and call the key callback.
|
||||
// If the length is greater than 1 then we're going to call the
|
||||
// charCallback.
|
||||
const key = if (result.text.len == 1) key: {
|
||||
// A completed key. If the length of the key is one then we can
|
||||
// attempt to translate it to a key enum and call the key callback.
|
||||
break :key input.Key.fromASCII(result.text[0]) orelse physical_key;
|
||||
} else .invalid;
|
||||
|
||||
// If both keys are invalid then we won't call the key callback. But
|
||||
// if either one is valid, we want to give it a chance.
|
||||
if (key != .invalid or physical_key != .invalid) {
|
||||
self.core_surface.keyCallback(action, key, physical_key, mods) catch |err| {
|
||||
log.err("error in key callback err={}", .{err});
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
// Next, we want to call the char callback with each codepoint.
|
||||
const view = std.unicode.Utf8View.init(result.text) catch |err| {
|
||||
log.warn("cannot build utf8 view over input: {}", .{err});
|
||||
return;
|
||||
};
|
||||
var it = view.iterator();
|
||||
while (it.nextCodepoint()) |cp| {
|
||||
self.core_surface.charCallback(cp) catch |err| {
|
||||
log.err("error in char callback err={}", .{err});
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn charCallback(self: *Surface, cp_: u32) void {
|
||||
const cp = std.math.cast(u21, cp_) orelse return;
|
||||
self.core_surface.charCallback(cp) catch |err| {
|
||||
@ -573,27 +648,14 @@ pub const CAPI = struct {
|
||||
keycode: u32,
|
||||
c_mods: c_int,
|
||||
) void {
|
||||
if (action != .press) return;
|
||||
|
||||
var buf: [128]u8 = undefined;
|
||||
const mods: input.Mods = @bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(c_mods)))));
|
||||
const result = surface.app.keymap.translate(
|
||||
&buf,
|
||||
&surface.keymap_state,
|
||||
@intCast(keycode),
|
||||
mods,
|
||||
) catch |err| {
|
||||
log.err("TRANSLATE error translating key err={}", .{err});
|
||||
return;
|
||||
};
|
||||
|
||||
log.warn("TRANSLATE: action={} keycode={x} dead={} key={any} key_str={s}", .{
|
||||
surface.key2Callback(
|
||||
action,
|
||||
keycode,
|
||||
result.composing,
|
||||
result.text,
|
||||
result.text,
|
||||
});
|
||||
@bitCast(@as(u8, @truncate(@as(c_uint, @bitCast(c_mods))))),
|
||||
) catch |err| {
|
||||
log.err("error processing key event err={}", .{err});
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
/// Tell the surface that it needs to schedule a render
|
||||
|
@ -2,6 +2,7 @@ const std = @import("std");
|
||||
|
||||
pub usingnamespace @import("input/mouse.zig");
|
||||
pub usingnamespace @import("input/key.zig");
|
||||
pub const keycodes = @import("input/keycodes.zig");
|
||||
pub const Binding = @import("input/Binding.zig");
|
||||
pub const Keymap = @import("input/Keymap.zig");
|
||||
pub const SplitDirection = Binding.Action.SplitDirection;
|
||||
|
@ -180,4 +180,67 @@ pub const Key = enum(c_int) {
|
||||
|
||||
// To support more keys (there are obviously more!) add them here
|
||||
// and ensure the mapping is up to date in the Window key handler.
|
||||
|
||||
/// Converts an ASCII character to a key, if possible. This returns
|
||||
/// null if the character is unknown.
|
||||
///
|
||||
/// Note that this can't distinguish between physical keys, i.e. '0'
|
||||
/// may be from the number row or the keypad, but it always maps
|
||||
/// to '.zero'.
|
||||
///
|
||||
/// This is what we want, we awnt people to create keybindings that
|
||||
/// are independent of the physical key.
|
||||
pub fn fromASCII(ch: u8) ?Key {
|
||||
return switch (ch) {
|
||||
'a' => .a,
|
||||
'b' => .b,
|
||||
'c' => .c,
|
||||
'd' => .d,
|
||||
'e' => .e,
|
||||
'f' => .f,
|
||||
'g' => .g,
|
||||
'h' => .h,
|
||||
'i' => .i,
|
||||
'j' => .j,
|
||||
'k' => .k,
|
||||
'l' => .l,
|
||||
'm' => .m,
|
||||
'n' => .n,
|
||||
'o' => .o,
|
||||
'p' => .p,
|
||||
'q' => .q,
|
||||
'r' => .r,
|
||||
's' => .s,
|
||||
't' => .t,
|
||||
'u' => .u,
|
||||
'v' => .v,
|
||||
'w' => .w,
|
||||
'x' => .x,
|
||||
'y' => .y,
|
||||
'z' => .z,
|
||||
'0' => .zero,
|
||||
'1' => .one,
|
||||
'2' => .two,
|
||||
'3' => .three,
|
||||
'4' => .four,
|
||||
'5' => .five,
|
||||
'6' => .six,
|
||||
'7' => .seven,
|
||||
'8' => .eight,
|
||||
'9' => .nine,
|
||||
';' => .semicolon,
|
||||
' ' => .space,
|
||||
'\'' => .apostrophe,
|
||||
',' => .comma,
|
||||
'`' => .grave_accent,
|
||||
'.' => .period,
|
||||
'/' => .slash,
|
||||
'-' => .minus,
|
||||
'=' => .equal,
|
||||
'[' => .left_bracket,
|
||||
']' => .right_bracket,
|
||||
'\\' => .backslash,
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user