A modal input mapping library for Zig.
- Zig 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| src | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| LICENSE | ||
| README.md | ||
inputmap
Modal input mapping for Zig. Maps keyboard keys, controller buttons, and analog axes to user-defined actions through a mode-aware state machine.
No dependencies on any UI toolkit, backend, or scripting engine.
Features
- Unified Key enum -- keyboard, punctuation, symbols, navigation, function keys, controller buttons
- Modifier support -- shift, ctrl, alt, cmd as a packed struct
- Multi-key sequences --
g g,space f,ctrl-x ctrl-s - Mode-aware -- bindings scoped to a mode ID, or
nullfor any-mode - Analog axes -- controller sticks and triggers
- Static or dynamic -- comptime-friendly static bindings, or runtime
addBindingwith allocator sendKeysparser -- feed key strings like"abc<esc><cmd-s>H<pad-a>"for scripting and testing
Usage
Static keymap (comptime-friendly)
const im = @import("inputmap");
const Action = enum(u16) { move_left = 1, move_right, save };
const normal: im.ModeId = 0;
const bindings = [_]im.Binding{
.{ .mode = normal, .sequence = &.{.{ .key = .h }}, .action = @intFromEnum(Action.move_left) },
.{ .mode = normal, .sequence = &.{.{ .key = .l }}, .action = @intFromEnum(Action.move_right) },
.{ .mode = null, .sequence = &.{.{ .key = .s, .mods = .{ .cmd = true } }}, .action = @intFromEnum(Action.save) },
};
var km = im.Keymap{ .bindings = &bindings };
switch (km.feed(normal, .{ .key = .h })) {
.dispatched => |action| handleAction(action),
.pending => {}, // prefix of a longer sequence, wait for more input
.no_match => {}, // no binding matches
}
Dynamic keymap (runtime registration)
var km = im.Keymap.init(allocator);
defer km.deinit();
try km.addBinding(0, &.{.{ .key = .h }}, 1);
try km.addBinding(0, &.{ .{ .key = .g }, .{ .key = .g } }, 10);
try km.addBinding(null, &.{.{ .key = .s, .mods = .{ .cmd = true } }}, 20);
// Remove all bindings for an action
_ = km.removeAction(10);
Multi-key sequences
// First 'g' returns .pending, second 'g' returns .dispatched
switch (km.feed(mode, .{ .key = .g })) {
.pending => {}, // show "g..." in status bar
else => {},
}
switch (km.feed(mode, .{ .key = .g })) {
.dispatched => |action| handleAction(action),
else => {},
}
Analog axes
const analog = [_]im.AnalogBinding{
.{ .mode = 0, .axis = .left_x, .action = 100 },
};
var km = im.Keymap{ .bindings = &.{}, .analog_bindings = &analog };
if (km.feedAnalog(mode, .{ .axis = .left_x, .value = 0.8 })) |action| {
handleAction(action);
}
sendKeys (scripting/testing)
im.sendKeys("gg<esc><cmd-s>H<pad-a>", struct {
fn f(input: im.Input) void {
// called once per parsed input
}
}.f);
Format: plain chars (abc), uppercase = shift (H), special keys (<esc>, <enter>, <tab>), modifiers (<cmd-s>, <ctrl-alt-x>), controller (<pad-a>, <pad-lb>), literal angle brackets (<lt>, <gt>).
Zig dependency
Requires Zig 0.16.0 or later.
// build.zig.zon
.inputmap = .{ .path = "../inputmap" },
// build.zig
const inputmap = b.dependency("inputmap", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("inputmap", inputmap.module("inputmap"));
License
MIT