1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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/js0 "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;
}
};
|