From eb8fc910e05ef780b5edfd542a7492288f92fa18 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Tue, 12 Dec 2023 16:43:55 -0600 Subject: [PATCH 1/5] gtk: get num_lock state for key events GTK doesn't expose the num_lock state via the key press event, this must be obtained directly from the device. Set the num_lock state in the modifier mask. This also will never show up as a `consumed_mod`, so we don't bother setting it there. Signed-off-by: Tim Culverhouse --- src/apprt/gtk/Surface.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 80180b543..6eb4e73d1 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1376,6 +1376,8 @@ fn keyEvent( else => {}, } + const device = c.gdk_event_get_device(event); + mods.num_lock = c.gdk_device_get_num_lock_state(device) == 1; break :mods mods; }; From a0880bcfed67f6ae5b5e6689fadf9e16e02d73c8 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Wed, 13 Dec 2023 06:53:33 -0600 Subject: [PATCH 2/5] key: add additional keypad keys Add additional keypad keys to the encoding scheme. This allows Ghostty to report KP_HOME and it's relatives. We also always check for a keyval first, so we can report KP_7, etc as opposed to ASCII '7'. --- src/apprt/gtk/Surface.zig | 13 +++++++------ src/apprt/gtk/key.zig | 22 ++++++++++++---------- src/input/function_keys.zig | 10 ++++++++++ src/input/key.zig | 26 ++++++++++++++++++++++++++ src/input/kitty.zig | 12 ++++++++++++ 5 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 6eb4e73d1..4dd2e1245 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1391,6 +1391,13 @@ fn keyEvent( // If we're not in a dead key state, we want to translate our text // to some input.Key. const key = if (!self.im_composing) key: { + // First, try to convert the keyval directly to a key. This allows the + // use of key remapping and identification of keypad numerics (as + // opposed to their ASCII counterparts) + if (gtk_key.keyFromKeyval(keyval)) |key| { + break :key 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. First try plain ASCII. @@ -1416,12 +1423,6 @@ fn keyEvent( } } - // Before using the physical key, try to convert the keyval - // directly to a key. This allows the use of key remapping. - if (gtk_key.keyFromKeyval(keyval)) |key| { - break :key key; - } - // If we have im text then this is invalid. This means that // the keypress generated some character that we don't know about // in our key enum. We don't want to use the physical key because diff --git a/src/apprt/gtk/key.zig b/src/apprt/gtk/key.zig index 2cd71eb1c..1d575ab39 100644 --- a/src/apprt/gtk/key.zig +++ b/src/apprt/gtk/key.zig @@ -190,16 +190,18 @@ const keymap: []const RawEntry = &.{ .{ c.GDK_KEY_KP_Enter, .kp_enter }, .{ c.GDK_KEY_KP_Equal, .kp_equal }, - // These are all just aliases to the non-kp variants because Ghostty - // core doesn't distinguish between them currently. - .{ c.GDK_KEY_KP_Home, .home }, - .{ c.GDK_KEY_KP_End, .end }, - .{ c.GDK_KEY_KP_Page_Up, .page_up }, - .{ c.GDK_KEY_KP_Page_Down, .page_down }, - .{ c.GDK_KEY_KP_Up, .up }, - .{ c.GDK_KEY_KP_Down, .down }, - .{ c.GDK_KEY_KP_Right, .right }, - .{ c.GDK_KEY_KP_Left, .left }, + .{ c.GDK_KEY_KP_Separator, .kp_separator }, + .{ c.GDK_KEY_KP_Left, .kp_left }, + .{ c.GDK_KEY_KP_Right, .kp_right }, + .{ c.GDK_KEY_KP_Up, .kp_up }, + .{ c.GDK_KEY_KP_Down, .kp_down }, + .{ c.GDK_KEY_KP_Page_Up, .kp_page_up }, + .{ c.GDK_KEY_KP_Page_Down, .kp_page_down }, + .{ c.GDK_KEY_KP_Home, .kp_home }, + .{ c.GDK_KEY_KP_End, .kp_end }, + .{ c.GDK_KEY_KP_Insert, .kp_insert }, + .{ c.GDK_KEY_KP_Delete, .kp_delete }, + .{ c.GDK_KEY_KP_Begin, .kp_begin }, .{ c.GDK_KEY_Shift_L, .left_shift }, .{ c.GDK_KEY_Control_L, .left_control }, diff --git a/src/input/function_keys.zig b/src/input/function_keys.zig index 9721130d1..ea98c386c 100644 --- a/src/input/function_keys.zig +++ b/src/input/function_keys.zig @@ -117,6 +117,16 @@ pub const keys = keys: { result.set(.kp_subtract, kpDefault("m") ++ pcStyle("\x1bO{}m")); result.set(.kp_add, kpDefault("k") ++ pcStyle("\x1bO{}k")); result.set(.kp_enter, kpDefault("M") ++ pcStyle("\x1bO{}M") ++ .{.{ .sequence = "\r" }}); + result.set(.kp_up, pcStyle("\x1b[1;{}A") ++ cursorKey("\x1b[A", "\x1bOA")); + result.set(.kp_down, pcStyle("\x1b[1;{}B") ++ cursorKey("\x1b[B", "\x1bOB")); + result.set(.kp_right, pcStyle("\x1b[1;{}C") ++ cursorKey("\x1b[C", "\x1bOC")); + result.set(.kp_left, pcStyle("\x1b[1;{}D") ++ cursorKey("\x1b[D", "\x1bOD")); + result.set(.kp_home, pcStyle("\x1b[1;{}H") ++ cursorKey("\x1b[H", "\x1bOH")); + result.set(.kp_end, pcStyle("\x1b[1;{}F") ++ cursorKey("\x1b[F", "\x1bOF")); + result.set(.kp_insert, pcStyle("\x1b[2;{}~") ++ .{.{ .sequence = "\x1B[2~" }}); + result.set(.kp_delete, pcStyle("\x1b[3;{}~") ++ .{.{ .sequence = "\x1B[3~" }}); + result.set(.kp_page_up, pcStyle("\x1b[5;{}~") ++ .{.{ .sequence = "\x1B[5~" }}); + result.set(.kp_page_down, pcStyle("\x1b[6;{}~") ++ .{.{ .sequence = "\x1B[6~" }}); result.set(.backspace, &.{ // Modify Keys Normal diff --git a/src/input/key.zig b/src/input/key.zig index 2a7795cbd..ce8390091 100644 --- a/src/input/key.zig +++ b/src/input/key.zig @@ -344,6 +344,18 @@ pub const Key = enum(c_int) { kp_add, kp_enter, kp_equal, + kp_separator, + kp_left, + kp_right, + kp_up, + kp_down, + kp_page_up, + kp_page_down, + kp_home, + kp_end, + kp_insert, + kp_delete, + kp_begin, // modifiers left_shift, @@ -552,6 +564,20 @@ pub const Key = enum(c_int) { .kp_add => cimgui.c.ImGuiKey_KeypadAdd, .kp_enter => cimgui.c.ImGuiKey_KeypadEnter, .kp_equal => cimgui.c.ImGuiKey_KeypadEqual, + // We map KP_SEPARATOR to Comma because traditionally a numpad would + // have a numeric separator key. Most modern numpads do not + .kp_separator => cimgui.c.ImGuiKey_Comma, + .kp_left => cimgui.c.ImGuiKey_LeftArrow, + .kp_right => cimgui.c.ImGuiKey_RightArrow, + .kp_up => cimgui.c.ImGuiKey_UpArrow, + .kp_down => cimgui.c.ImGuiKey_DownArrow, + .kp_page_up => cimgui.c.ImGuiKey_PageUp, + .kp_page_down => cimgui.c.ImGuiKey_PageUp, + .kp_home => cimgui.c.ImGuiKey_Home, + .kp_end => cimgui.c.ImGuiKey_End, + .kp_insert => cimgui.c.ImGuiKey_Insert, + .kp_delete => cimgui.c.ImGuiKey_Delete, + .kp_begin => cimgui.c.ImGuiKey_NamedKey_BEGIN, .left_shift => cimgui.c.ImGuiKey_LeftShift, .left_control => cimgui.c.ImGuiKey_LeftCtrl, diff --git a/src/input/kitty.zig b/src/input/kitty.zig index a9b061107..f8faaa154 100644 --- a/src/input/kitty.zig +++ b/src/input/kitty.zig @@ -104,6 +104,18 @@ const raw_entries: []const RawEntry = &.{ .{ .kp_add, 57413, 'u', false }, .{ .kp_enter, 57414, 'u', false }, .{ .kp_equal, 57415, 'u', false }, + .{ .kp_separator, 57416, 'u', false }, + .{ .kp_left, 57417, 'u', false }, + .{ .kp_right, 57418, 'u', false }, + .{ .kp_up, 57419, 'u', false }, + .{ .kp_down, 57420, 'u', false }, + .{ .kp_page_up, 57421, 'u', false }, + .{ .kp_page_down, 57422, 'u', false }, + .{ .kp_home, 57423, 'u', false }, + .{ .kp_end, 57424, 'u', false }, + .{ .kp_insert, 57425, 'u', false }, + .{ .kp_delete, 57426, 'u', false }, + .{ .kp_begin, 57427, 'u', false }, .{ .left_shift, 57441, 'u', true }, .{ .right_shift, 57447, 'u', true }, From 4bd9b1365aeda1ff3e1b7e36998ecc24389a7143 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Wed, 13 Dec 2023 06:57:15 -0600 Subject: [PATCH 3/5] key: add TODO's for media keys Add TODO comments in places where we need to add media keys. --- src/apprt/gtk/key.zig | 2 ++ src/input/key.zig | 2 ++ src/input/kitty.zig | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/apprt/gtk/key.zig b/src/apprt/gtk/key.zig index 1d575ab39..70abd18ca 100644 --- a/src/apprt/gtk/key.zig +++ b/src/apprt/gtk/key.zig @@ -211,4 +211,6 @@ const keymap: []const RawEntry = &.{ .{ c.GDK_KEY_Control_R, .right_control }, .{ c.GDK_KEY_Alt_R, .right_alt }, .{ c.GDK_KEY_Super_R, .right_super }, + + // TODO: media keys }; diff --git a/src/input/key.zig b/src/input/key.zig index ce8390091..adbb4f60a 100644 --- a/src/input/key.zig +++ b/src/input/key.zig @@ -357,6 +357,8 @@ pub const Key = enum(c_int) { kp_delete, kp_begin, + // TODO: media keys + // modifiers left_shift, left_control, diff --git a/src/input/kitty.zig b/src/input/kitty.zig index f8faaa154..bfaa60022 100644 --- a/src/input/kitty.zig +++ b/src/input/kitty.zig @@ -117,6 +117,8 @@ const raw_entries: []const RawEntry = &.{ .{ .kp_delete, 57426, 'u', false }, .{ .kp_begin, 57427, 'u', false }, + // TODO: media keys + .{ .left_shift, 57441, 'u', true }, .{ .right_shift, 57447, 'u', true }, .{ .left_control, 57442, 'u', true }, From c905b08b1e5c87f244689125a012c9782695aacb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 13 Dec 2023 18:40:12 -0800 Subject: [PATCH 4/5] apprt/gtk: slight stylistic change --- src/apprt/gtk/Surface.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 4dd2e1245..e0bd40127 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1333,6 +1333,10 @@ fn keyEvent( // event. const mods = mods: { var mods = translateMods(gtk_mods); + + const device = c.gdk_event_get_device(event); + mods.num_lock = c.gdk_device_get_num_lock_state(device) == 1; + switch (physical_key) { .left_shift => { mods.shift = action == .press; @@ -1376,8 +1380,7 @@ fn keyEvent( else => {}, } - const device = c.gdk_event_get_device(event); - mods.num_lock = c.gdk_device_get_num_lock_state(device) == 1; + break :mods mods; }; From 89348fd73e7660cb8e2361ad585038b0afe482c6 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 13 Dec 2023 18:46:55 -0800 Subject: [PATCH 5/5] include: add new keycodes --- include/ghostty.h | 12 ++++++++++++ src/input/keycodes.zig | 21 ++++++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/include/ghostty.h b/include/ghostty.h index f66f92c47..357d40de5 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -286,6 +286,18 @@ typedef enum { GHOSTTY_KEY_KP_ADD, GHOSTTY_KEY_KP_ENTER, GHOSTTY_KEY_KP_EQUAL, + GHOSTTY_KEY_KP_SEPARATOR, + GHOSTTY_KEY_KP_LEFT, + GHOSTTY_KEY_KP_RIGHT, + GHOSTTY_KEY_KP_UP, + GHOSTTY_KEY_KP_DOWN, + GHOSTTY_KEY_KP_PAGE_UP, + GHOSTTY_KEY_KP_PAGE_DOWN, + GHOSTTY_KEY_KP_HOME, + GHOSTTY_KEY_KP_END, + GHOSTTY_KEY_KP_INSERT, + GHOSTTY_KEY_KP_DELETE, + GHOSTTY_KEY_KP_BEGIN, // modifiers GHOSTTY_KEY_LEFT_SHIFT, diff --git a/src/input/keycodes.zig b/src/input/keycodes.zig index d29158bc9..170739aa9 100644 --- a/src/input/keycodes.zig +++ b/src/input/keycodes.zig @@ -328,18 +328,17 @@ pub const raw_entries: []const RawEntry = &.{ .{ 0x070057, 0x004e, 0x0056, 0x004e, 0x0045, "NumpadAdd" }, .{ 0x070058, 0x0060, 0x0068, 0xe01c, 0x004c, "NumpadEnter" }, - .{ 0x070059, 0x004f, 0x0057, 0x004f, 0x0053, "Numpad1" }, - .{ 0x07005a, 0x0050, 0x0058, 0x0050, 0x0054, "Numpad2" }, - .{ 0x07005b, 0x0051, 0x0059, 0x0051, 0x0055, "Numpad3" }, - .{ 0x07005c, 0x004b, 0x0053, 0x004b, 0x0056, "Numpad4" }, + .{ 0x070059, 0x004f, 0x0057, 0x004f, 0x0053, "Numpad1" }, // +End + .{ 0x07005a, 0x0050, 0x0058, 0x0050, 0x0054, "Numpad2" }, // +Down + .{ 0x07005b, 0x0051, 0x0059, 0x0051, 0x0055, "Numpad3" }, // +PageDown + .{ 0x07005c, 0x004b, 0x0053, 0x004b, 0x0056, "Numpad4" }, // +Left .{ 0x07005d, 0x004c, 0x0054, 0x004c, 0x0057, "Numpad5" }, - .{ 0x07005e, 0x004d, 0x0055, 0x004d, 0x0058, "Numpad6" }, - .{ 0x07005f, 0x0047, 0x004f, 0x0047, 0x0059, "Numpad7" }, - - .{ 0x070060, 0x0048, 0x0050, 0x0048, 0x005b, "Numpad8" }, - .{ 0x070061, 0x0049, 0x0051, 0x0049, 0x005c, "Numpad9" }, - .{ 0x070062, 0x0052, 0x005a, 0x0052, 0x0052, "Numpad0" }, - .{ 0x070063, 0x0053, 0x005b, 0x0053, 0x0041, "NumpadDecimal" }, + .{ 0x07005e, 0x004d, 0x0055, 0x004d, 0x0058, "Numpad6" }, // +Right + .{ 0x07005f, 0x0047, 0x004f, 0x0047, 0x0059, "Numpad7" }, // +Home + .{ 0x070060, 0x0048, 0x0050, 0x0048, 0x005b, "Numpad8" }, // +Up + .{ 0x070061, 0x0049, 0x0051, 0x0049, 0x005c, "Numpad9" }, // +PageUp + .{ 0x070062, 0x0052, 0x005a, 0x0052, 0x0052, "Numpad0" }, // +Insert + .{ 0x070063, 0x0053, 0x005b, 0x0053, 0x0041, "NumpadDecimal" }, // +Delete // USB#070064 is not present on US keyboard. // This key is typically located near LeftShift key. // The keycap varies on international keyboards: