mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
respect application cursor keys for arrow (DECCKM)
This fixes the arrow keys in htop.
This commit is contained in:
@ -980,6 +980,29 @@ fn keyCallback(
|
||||
win.io_thread.wakeup.send() catch {};
|
||||
},
|
||||
|
||||
.cursor_key => |ck| {
|
||||
// We send a different sequence depending on if we're
|
||||
// in cursor keys mode. We're in "normal" mode if cursor
|
||||
// keys mdoe is NOT set.
|
||||
const normal = normal: {
|
||||
win.renderer_state.mutex.lock();
|
||||
defer win.renderer_state.mutex.unlock();
|
||||
break :normal !win.io.terminal.modes.cursor_keys;
|
||||
};
|
||||
|
||||
if (normal) {
|
||||
_ = win.io_thread.mailbox.push(.{
|
||||
.write_stable = ck.normal,
|
||||
}, .{ .forever = {} });
|
||||
} else {
|
||||
_ = win.io_thread.mailbox.push(.{
|
||||
.write_stable = ck.application,
|
||||
}, .{ .forever = {} });
|
||||
}
|
||||
|
||||
win.io_thread.wakeup.send() catch {};
|
||||
},
|
||||
|
||||
.copy_to_clipboard => {
|
||||
// We can read from the renderer state without holding
|
||||
// the lock because only we will write to this field.
|
||||
|
@ -190,12 +190,31 @@ pub const Config = struct {
|
||||
.{ .paste_from_clipboard = {} },
|
||||
);
|
||||
|
||||
try result.keybind.set.put(alloc, .{ .key = .up }, .{ .csi = "A" });
|
||||
try result.keybind.set.put(alloc, .{ .key = .down }, .{ .csi = "B" });
|
||||
try result.keybind.set.put(alloc, .{ .key = .right }, .{ .csi = "C" });
|
||||
try result.keybind.set.put(alloc, .{ .key = .left }, .{ .csi = "D" });
|
||||
try result.keybind.set.put(alloc, .{ .key = .home }, .{ .csi = "H" });
|
||||
try result.keybind.set.put(alloc, .{ .key = .end }, .{ .csi = "F" });
|
||||
try result.keybind.set.put(alloc, .{ .key = .up }, .{ .cursor_key = .{
|
||||
.normal = "\x1b[A",
|
||||
.application = "\x1bOA",
|
||||
} });
|
||||
try result.keybind.set.put(alloc, .{ .key = .down }, .{ .cursor_key = .{
|
||||
.normal = "\x1b[B",
|
||||
.application = "\x1bOB",
|
||||
} });
|
||||
try result.keybind.set.put(alloc, .{ .key = .right }, .{ .cursor_key = .{
|
||||
.normal = "\x1b[C",
|
||||
.application = "\x1bOC",
|
||||
} });
|
||||
try result.keybind.set.put(alloc, .{ .key = .left }, .{ .cursor_key = .{
|
||||
.normal = "\x1b[D",
|
||||
.application = "\x1bOD",
|
||||
} });
|
||||
try result.keybind.set.put(alloc, .{ .key = .home }, .{ .cursor_key = .{
|
||||
.normal = "\x1b[H",
|
||||
.application = "\x1bOH",
|
||||
} });
|
||||
try result.keybind.set.put(alloc, .{ .key = .end }, .{ .cursor_key = .{
|
||||
.normal = "\x1b[F",
|
||||
.application = "\x1bOF",
|
||||
} });
|
||||
|
||||
try result.keybind.set.put(alloc, .{ .key = .page_up }, .{ .csi = "5~" });
|
||||
try result.keybind.set.put(alloc, .{ .key = .page_down }, .{ .csi = "6~" });
|
||||
|
||||
|
@ -15,6 +15,7 @@ action: Action,
|
||||
|
||||
pub const Error = error{
|
||||
InvalidFormat,
|
||||
InvalidAction,
|
||||
};
|
||||
|
||||
/// Parse the format "ctrl+a=csi:A" into a binding. The format is
|
||||
@ -101,6 +102,9 @@ pub fn parse(input: []const u8) !Binding {
|
||||
break :action @unionInit(Action, field.name, param);
|
||||
},
|
||||
|
||||
// Cursor keys can't be set currently
|
||||
Action.CursorKey => return Error.InvalidAction,
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@ -127,6 +131,10 @@ pub const Action = union(enum) {
|
||||
/// without the CSI header ("ESC ]" or "\x1b]").
|
||||
csi: []const u8,
|
||||
|
||||
/// Send data to the pty depending on whether cursor key mode is
|
||||
/// enabled ("application") or disabled ("normal").
|
||||
cursor_key: CursorKey,
|
||||
|
||||
/// Copy and paste.
|
||||
copy_to_clipboard: void,
|
||||
paste_from_clipboard: void,
|
||||
@ -152,6 +160,11 @@ pub const Action = union(enum) {
|
||||
|
||||
/// Quit ghostty
|
||||
quit: void,
|
||||
|
||||
pub const CursorKey = struct {
|
||||
normal: []const u8,
|
||||
application: []const u8,
|
||||
};
|
||||
};
|
||||
|
||||
/// Trigger is the associated key state that can trigger an action.
|
||||
|
@ -72,6 +72,7 @@ previous_char: ?u21 = null,
|
||||
modes: packed struct {
|
||||
const Self = @This();
|
||||
|
||||
cursor_keys: bool = false, // 1
|
||||
reverse_colors: bool = false, // 5,
|
||||
origin: bool = false, // 6
|
||||
autowrap: bool = true, // 7
|
||||
|
@ -42,6 +42,17 @@ pub const RenditionAspect = enum(u16) {
|
||||
/// values correspond to the `?`-prefixed modes, since those are the ones
|
||||
/// of primary interest. The enum value is the mode value.
|
||||
pub const Mode = enum(u16) {
|
||||
/// This control function selects the sequences the arrow keys send.
|
||||
/// You can use the four arrow keys to move the cursor through the current
|
||||
/// page or to send special application commands.
|
||||
///
|
||||
/// If the DECCKM function is set, then the arrow keys send application
|
||||
/// sequences to the host.
|
||||
///
|
||||
/// If the DECCKM function is reset, then the arrow keys send ANSI cursor
|
||||
/// sequences to the host.
|
||||
cursor_keys = 1,
|
||||
|
||||
/// Change terminal wide between 80 and 132 column mode. When set
|
||||
/// (with ?40 set), resizes terminal to 132 columns and keeps it that
|
||||
/// wide. When unset, resizes to 80 columns.
|
||||
|
@ -627,6 +627,10 @@ const StreamHandler = struct {
|
||||
|
||||
pub fn setMode(self: *StreamHandler, mode: terminal.Mode, enabled: bool) !void {
|
||||
switch (mode) {
|
||||
.cursor_keys => {
|
||||
self.terminal.modes.cursor_keys = enabled;
|
||||
},
|
||||
|
||||
.reverse_colors => {
|
||||
self.terminal.modes.reverse_colors = enabled;
|
||||
|
||||
|
Reference in New Issue
Block a user