mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
input: manage application keypad state with mode 1035
Fixes #1099 We previously applied application keypad mode logic (`ESC=` or mode 66) whenever it was active. However, from looking at the behavior of other terminals (xterm and foot) it appears this isn't correct. For xterm, application keypad mode only applies unconditionally if the keyboard mode is VT220 (`-kt vt220`). For modern terminals, application keypad mode is only applied if mode 1035 is disabled. Mode 1035 is the "ignore numpad state with keypad mode" mode. It defaults to true on terminal startup. If this is true, keypads are always encoded in numerical mode. If this is false, the numlock state will be respected.
This commit is contained in:
@ -1322,6 +1322,7 @@ pub fn keyCallback(
|
|||||||
.alt_esc_prefix = t.modes.get(.alt_esc_prefix),
|
.alt_esc_prefix = t.modes.get(.alt_esc_prefix),
|
||||||
.cursor_key_application = t.modes.get(.cursor_keys),
|
.cursor_key_application = t.modes.get(.cursor_keys),
|
||||||
.keypad_key_application = t.modes.get(.keypad_keys),
|
.keypad_key_application = t.modes.get(.keypad_keys),
|
||||||
|
.ignore_keypad_with_numlock = t.modes.get(.ignore_keypad_with_numlock),
|
||||||
.modify_other_keys_state_2 = t.flags.modify_other_keys_2,
|
.modify_other_keys_state_2 = t.flags.modify_other_keys_2,
|
||||||
.kitty_flags = t.screen.kitty_keyboard.current(),
|
.kitty_flags = t.screen.kitty_keyboard.current(),
|
||||||
};
|
};
|
||||||
|
@ -26,6 +26,7 @@ macos_option_as_alt: config.OptionAsAlt = .false,
|
|||||||
alt_esc_prefix: bool = false,
|
alt_esc_prefix: bool = false,
|
||||||
cursor_key_application: bool = false,
|
cursor_key_application: bool = false,
|
||||||
keypad_key_application: bool = false,
|
keypad_key_application: bool = false,
|
||||||
|
ignore_keypad_with_numlock: bool = false,
|
||||||
modify_other_keys_state_2: bool = false,
|
modify_other_keys_state_2: bool = false,
|
||||||
kitty_flags: KittyFlags = .{},
|
kitty_flags: KittyFlags = .{},
|
||||||
|
|
||||||
@ -245,6 +246,7 @@ fn legacy(
|
|||||||
all_mods,
|
all_mods,
|
||||||
self.cursor_key_application,
|
self.cursor_key_application,
|
||||||
self.keypad_key_application,
|
self.keypad_key_application,
|
||||||
|
self.ignore_keypad_with_numlock,
|
||||||
self.modify_other_keys_state_2,
|
self.modify_other_keys_state_2,
|
||||||
)) |sequence| pc_style: {
|
)) |sequence| pc_style: {
|
||||||
// If we're pressing enter and have UTF-8 text, we probably are
|
// If we're pressing enter and have UTF-8 text, we probably are
|
||||||
@ -416,7 +418,8 @@ fn pcStyleFunctionKey(
|
|||||||
keyval: key.Key,
|
keyval: key.Key,
|
||||||
mods: key.Mods,
|
mods: key.Mods,
|
||||||
cursor_key_application: bool,
|
cursor_key_application: bool,
|
||||||
keypad_key_application: bool,
|
keypad_key_application_req: bool,
|
||||||
|
ignore_keypad_with_numlock: bool,
|
||||||
modify_other_keys: bool, // True if state 2
|
modify_other_keys: bool, // True if state 2
|
||||||
) ?[]const u8 {
|
) ?[]const u8 {
|
||||||
// We only want binding-sensitive mods because lock keys
|
// We only want binding-sensitive mods because lock keys
|
||||||
@ -424,6 +427,24 @@ fn pcStyleFunctionKey(
|
|||||||
// pc-style function keys.
|
// pc-style function keys.
|
||||||
const mods_int = mods.binding().int();
|
const mods_int = mods.binding().int();
|
||||||
|
|
||||||
|
// Keypad application keymode isn't super straightforward.
|
||||||
|
// On xterm, in VT220 mode, numlock alone is enough to trigger
|
||||||
|
// application mode. But in more modern modes, numlock is
|
||||||
|
// ignored by default via mode 1035 (default true). If mode
|
||||||
|
// 1035 is on, we always are in numerical keypad mode. If
|
||||||
|
// mode 1035 is off, we are in application mode if the
|
||||||
|
// proper numlock state is pressed. The numlock state is implicitly
|
||||||
|
// determined based on the keycode sent (i.e. 1 with numlock
|
||||||
|
// on will be kp_end).
|
||||||
|
const keypad_key_application = keypad: {
|
||||||
|
// If we're ignoring keypad then this is always false.
|
||||||
|
// In other words, we're always in numerical keypad mode.
|
||||||
|
if (ignore_keypad_with_numlock) break :keypad false;
|
||||||
|
|
||||||
|
// If we're not ignoring then we enable the desired state.
|
||||||
|
break :keypad keypad_key_application_req;
|
||||||
|
};
|
||||||
|
|
||||||
for (function_keys.keys.get(keyval)) |entry| {
|
for (function_keys.keys.get(keyval)) |entry| {
|
||||||
switch (entry.cursor) {
|
switch (entry.cursor) {
|
||||||
.any => {},
|
.any => {},
|
||||||
@ -1677,6 +1698,70 @@ test "legacy: keypad enter" {
|
|||||||
try testing.expectEqualStrings("\r", actual);
|
try testing.expectEqualStrings("\r", actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "legacy: keypad 1" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
var enc: KeyEncoder = .{
|
||||||
|
.event = .{
|
||||||
|
.key = .kp_1,
|
||||||
|
.mods = .{},
|
||||||
|
.consumed_mods = .{},
|
||||||
|
.utf8 = "1",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const actual = try enc.legacy(&buf);
|
||||||
|
try testing.expectEqualStrings("1", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "legacy: keypad 1 with application keypad" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
var enc: KeyEncoder = .{
|
||||||
|
.event = .{
|
||||||
|
.key = .kp_1,
|
||||||
|
.mods = .{},
|
||||||
|
.consumed_mods = .{},
|
||||||
|
.utf8 = "1",
|
||||||
|
},
|
||||||
|
.keypad_key_application = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const actual = try enc.legacy(&buf);
|
||||||
|
try testing.expectEqualStrings("\x1bOq", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "legacy: keypad 1 with application keypad and numlock" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
var enc: KeyEncoder = .{
|
||||||
|
.event = .{
|
||||||
|
.key = .kp_1,
|
||||||
|
.mods = .{ .num_lock = true },
|
||||||
|
.consumed_mods = .{},
|
||||||
|
.utf8 = "1",
|
||||||
|
},
|
||||||
|
.keypad_key_application = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const actual = try enc.legacy(&buf);
|
||||||
|
try testing.expectEqualStrings("\x1bOq", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "legacy: keypad 1 with application keypad and numlock ignore" {
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
var enc: KeyEncoder = .{
|
||||||
|
.event = .{
|
||||||
|
.key = .kp_1,
|
||||||
|
.mods = .{ .num_lock = false },
|
||||||
|
.consumed_mods = .{},
|
||||||
|
.utf8 = "1",
|
||||||
|
},
|
||||||
|
.keypad_key_application = true,
|
||||||
|
.ignore_keypad_with_numlock = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const actual = try enc.legacy(&buf);
|
||||||
|
try testing.expectEqualStrings("1", actual);
|
||||||
|
}
|
||||||
|
|
||||||
test "legacy: f1" {
|
test "legacy: f1" {
|
||||||
var buf: [128]u8 = undefined;
|
var buf: [128]u8 = undefined;
|
||||||
var enc: KeyEncoder = .{
|
var enc: KeyEncoder = .{
|
||||||
|
@ -206,6 +206,7 @@ const entries: []const ModeEntry = &.{
|
|||||||
.{ .name = "mouse_alternate_scroll", .value = 1007, .default = true },
|
.{ .name = "mouse_alternate_scroll", .value = 1007, .default = true },
|
||||||
.{ .name = "mouse_format_urxvt", .value = 1015 },
|
.{ .name = "mouse_format_urxvt", .value = 1015 },
|
||||||
.{ .name = "mouse_format_sgr_pixels", .value = 1016 },
|
.{ .name = "mouse_format_sgr_pixels", .value = 1016 },
|
||||||
|
.{ .name = "ignore_keypad_with_numlock", .value = 1035, .default = true },
|
||||||
.{ .name = "alt_esc_prefix", .value = 1036, .default = true },
|
.{ .name = "alt_esc_prefix", .value = 1036, .default = true },
|
||||||
.{ .name = "alt_sends_escape", .value = 1039 },
|
.{ .name = "alt_sends_escape", .value = 1039 },
|
||||||
.{ .name = "reverse_wrap_extended", .value = 1045 },
|
.{ .name = "reverse_wrap_extended", .value = 1045 },
|
||||||
|
Reference in New Issue
Block a user