libvaxis icon indicating copy to clipboard operation
libvaxis copied to clipboard

add forward_delete key on macos

Open reykjalin opened this issue 1 month ago • 1 comments

Noticed this while working on Fönn. For whatever reason pressing Cmd + backspace on macOS is encoded like so:

.{
    .codepoint = 117, // == 0x75
    .text = null,
    .shifted_codepoint = null,
    .base_layout_codepoint = null,
    .mods = .{
        // note that `ctrl` is `true`, not `super`. Another oddity here.
        .shift = false, .alt = false, .ctrl = true,
        .super = false, .hyper = false, .meta = false,
        .caps_lock = false, .num_lock = false
    }
}

I don't know why macOS does this, but a quick lookup led me to /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h (on modern macOS systems as I understand it, the path has changed over the years) which has this section of code:

/* keycodes for keys that are independent of keyboard layout*/
enum {
  // bunch of kVK_ prefixed keys...

  kVK_ForwardDelete             = 0x75,

  // ...
};

Having this keycode makes it easier to read the input handling code, so I figured I'd contribute, but let me know if this is something you'd like to keep or not rockorager.


I'm not quite convinced that the compile time error for trying to use this on non-macos systems is needed, but I figured it'd be better to err on the side of caution here instead of adding a new constant that may or may not be universal.

I think it's macOS specific since it's part of Apple's Carbon framework.

reykjalin avatar Nov 15 '25 22:11 reykjalin

I assume you got this encoding in Ghostty? I was curious about the encoding and Ghostty has a default keybind to send \x15 on Cmd+Backspace...

When I tried in Kitty I get the "correct" encoding: CSI 127 ; 9 u. Can you try in Kitty to see if you get the correct key reported (IE cmd + backspace)?

rockorager avatar Nov 23 '25 13:11 rockorager

Oh yeah, interesting 🤔 I get 0x7f + super when I test this in Kitty but 0x75 + ctrl when I run this in Ghostty. Any idea why that is? Maybe it's part of the "native to macOS" approach? They want to replicate some key binding that's used in native applications?

You can drop this into the examples directory in libvaxis and run it to see what I'm seeing if you'd like:

const std = @import("std");
const builtin = @import("builtin");
const vaxis = @import("vaxis");

var debug_allocator: std.heap.DebugAllocator(.{}) = .init;

const Event = union(enum) {
    key_press: vaxis.Key,
    winsize: vaxis.Winsize,
    focus_in,
};

pub fn main() !void {
    const gpa, const is_debug = gpa: {
        break :gpa switch (builtin.mode) {
            .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
            .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
        };
    };
    defer if (is_debug) {
        _ = debug_allocator.deinit();
    };

    var buffer: [1024]u8 = undefined;
    var tty = try vaxis.Tty.init(&buffer);
    defer tty.deinit();

    var vx = try vaxis.init(gpa, .{});
    defer vx.deinit(gpa, tty.writer());

    var loop: vaxis.Loop(Event) = .{
        .tty = &tty,
        .vaxis = &vx,
    };
    try loop.init();

    try loop.start();
    defer loop.stop();

    try vx.enterAltScreen(tty.writer());
    try vx.queryTerminal(tty.writer(), 1 * std.time.ns_per_s);

    var codepoint: u21 = 0;
    var mods: vaxis.Key.Modifiers = .{};

    while (true) {
        const event = loop.nextEvent();

        switch (event) {
            .key_press => |key| {
                if (key.matches('c', .{ .ctrl = true })) {
                    break;
                }

                codepoint = key.codepoint;
                mods = key.mods;
            },
            .winsize => |ws| try vx.resize(gpa, tty.writer(), ws),
            else => {},
        }

        const win = vx.window();
        win.clear();

        const text = try std.fmt.allocPrint(gpa, "codepoint: 0x{x}\nmods: {}", .{ codepoint, mods });
        defer gpa.free(text);
        _ = win.printSegment(.{ .text = text }, .{});

        try vx.render(tty.writer());
    }
}

reykjalin avatar Dec 16 '25 03:12 reykjalin