aboutsummaryrefslogtreecommitdiff
path: root/src/main.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.zig')
-rw-r--r--src/main.zig97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..9969741
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,97 @@
+const std = @import("std");
+const c = @cImport({
+ @cInclude("linux/joystick.h");
+});
+
+pub fn main() !void {
+ const allocator = std.heap.c_allocator;
+ var keybinds = std.ArrayList(Keybind).init(allocator);
+ defer keybinds.deinit();
+
+ var args = std.process.args();
+ defer args.deinit();
+
+ if(!args.skip())
+ usage();
+
+ var input_filepath: []const u8 = "";
+ if(args.next()) |arg| {
+ input_filepath = arg[0..arg.len];
+ } else {
+ std.debug.print("error: missing required argument input_device\n", .{});
+ usage();
+ }
+
+ while(args.next()) |arg| {
+ const keybind = Keybind.parse_string(arg) catch {
+ std.debug.print("error: invalid keybind: {s}\n", .{arg});
+ usage();
+ unreachable;
+ };
+ try keybinds.append(keybind);
+ }
+
+ const event_fd = std.os.open(input_filepath, std.os.O.RDONLY, 0) catch |err| {
+ std.debug.print("error: failed to open: {s}\n", .{input_filepath});
+ return err;
+ };
+ defer std.os.close(event_fd);
+
+ var event: c.js_event = undefined;
+ while(true) {
+ const read_size = try std.os.read(event_fd, std.mem.asBytes(&event));
+ if(read_size != @sizeOf(@TypeOf(event)))
+ continue;
+
+ if(event.type & c.JS_EVENT_BUTTON == 0)
+ continue;
+
+ if(event.value != 1)
+ continue;
+
+ for(keybinds.items) |*keybind| {
+ if(event.number != keybind.key)
+ continue;
+
+ std.debug.print("key pressed: {d}, running command: {s}\n", .{keybind.key, keybind.command});
+ keybind.execute_command(allocator);
+ }
+ }
+}
+
+fn usage() void {
+ std.debug.print(
+ \\usage: zbind <input_device> [binds...]
+ \\ binds buttons on a joystick device (such as a controller) to commands
+ \\
+ \\OPTIONS:
+ \\ binds There may be multiple keybindings in the format key,command
+ \\
+ \\EXAMPLES:
+ \\ zbind /dev/input/by-id/usb-Joystick "0,echo 'hello world' > log.log" "4,script.sh"
+ , .{});
+ std.os.exit(1);
+}
+
+const Keybind = struct {
+ key: u8,
+ command: []const u8,
+
+ pub fn parse_string(str: []const u8) !Keybind {
+ var it = std.mem.splitScalar(u8, str, ',');
+ const key_str = it.next() orelse return error.InvalidSyntax;
+ const command = it.next() orelse return error.InvalidSyntax;
+ const key = try std.fmt.parseInt(u8, key_str, 10);
+ return .{
+ .key = key,
+ .command = command,
+ };
+ }
+
+ pub fn execute_command(self: *Keybind, allocator: std.mem.Allocator) void {
+ var child = std.ChildProcess.init(&.{ "/bin/sh", "-c", self.command }, allocator);
+ child.stdin_behavior = .Ignore;
+ child.spawn() catch return;
+ _ = child.wait() catch return;
+ }
+}; \ No newline at end of file