const std = @import("std"); const c = @cImport({ @cInclude("linux/input.h"); @cInclude("libevdev/libevdev.h"); @cInclude("libevdev/libevdev-uinput.h"); @cInclude("errno.h"); }); const CapsLockDown = c.input_event{ .type = c.EV_KEY, .code = c.KEY_CAPSLOCK, .value = 1, .time = undefined }; const CapsLockUp = c.input_event{ .type = c.EV_KEY, .code = c.KEY_CAPSLOCK, .value = 0, .time = undefined }; const LShiftDown = c.input_event{ .type = c.EV_KEY, .code = c.KEY_LEFTSHIFT, .value = 1, .time = undefined }; const LShiftUp = c.input_event{ .type = c.EV_KEY, .code = c.KEY_LEFTSHIFT, .value = 0, .time = undefined }; const RShiftDown = c.input_event{ .type = c.EV_KEY, .code = c.KEY_RIGHTSHIFT, .value = 1, .time = undefined }; const RShiftUp = c.input_event{ .type = c.EV_KEY, .code = c.KEY_RIGHTSHIFT, .value = 0, .time = undefined }; var uinput: *align(8) c.libevdev_uinput = undefined; fn event_equal(e1: *const c.input_event, e2: *const c.input_event) bool { return (e1.type == e2.type) and (e1.code == e2.code) and (e1.value == e2.value); } fn inputDeviceOpenAndGrab(dev_name: []const u8) !?*c.libevdev { const dev_fd = try std.fs.openFileAbsolute(dev_name, .{}); const dev = c.libevdev_new(); if (c.libevdev_set_fd(dev, dev_fd.handle) < 0) { return error.libevdevCreateFail; } if (c.libevdev_grab(dev, c.LIBEVDEV_GRAB) < 0) { return error.libevdevGrabFail; } if (c.libevdev_uinput_create_from_device(@ptrCast(dev), c.LIBEVDEV_UINPUT_OPEN_MANAGED, @ptrCast(uinput)) < 0) { return error.libevdevUinputCreateFail; } return dev; } fn uinputCreate(dev: *c.libevdev) !?*c.libevdev_uinput { if (c.libevdev_uinput_create_from_device(@ptrCast(dev), c.LIBEVDEV_UINPUT_OPEN_MANAGED, @ptrCast(uinput)) < 0) { return error.libevdevUinputCreateFail; } return uinput; } fn uinputWrite(uinput_dev: *c.libevdev_uinput, event: *const c.input_event) !void { if (c.libevdev_uinput_write_event(@ptrCast(uinput_dev), event.type, event.code, event.value) < 0) { return error.writeFailed; } } fn uinputWriteKey(uinput_dev: *c.libevdev_uinput, key: u16) !void { const pressKey = c.input_event{ .type = c.EV_KEY, .code = key, .value = 1, .time = undefined }; const releaseKey = c.input_event{ .type = c.EV_KEY, .code = key, .value = 0, .time = undefined }; const sync = c.input_event{ .type = c.EV_SYN, .code = c.SYN_REPORT, .value = 0, .time = undefined }; std.time.sleep(20000); try uinputWrite(uinput_dev, &pressKey); try uinputWrite(uinput_dev, &sync); std.time.sleep(20000); try uinputWrite(uinput_dev, &releaseKey); } pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); defer _ = gpa.deinit(); const args = try std.process.argsAlloc(allocator); if (args.len != 2) { std.debug.print("error: expected one argument", .{}); std.os.exit(1); } const dev_name = args[1]; const dev = try inputDeviceOpenAndGrab(dev_name); //const uinput_dev = try uinputCreate(dev.?); const uinput_dev = uinput; var write_esc_lshift: bool = undefined; var write_esc_rshift: bool = undefined; var is_lshift_down: bool = false; var is_rshift_down: bool = false; outer: while (true) { var event: c.input_event = undefined; var rc = c.libevdev_next_event(dev, c.LIBEVDEV_READ_FLAG_NORMAL | c.LIBEVDEV_READ_FLAG_BLOCKING, @ptrCast(&event)); while (rc == c.LIBEVDEV_READ_STATUS_SYNC) { rc = c.libevdev_next_event(dev, c.LIBEVDEV_READ_FLAG_SYNC, @ptrCast(&event)); } if (rc == -c.EAGAIN) { continue :outer; } if (rc != c.LIBEVDEV_READ_STATUS_SUCCESS) { break :outer; } if (event.type == c.EV_MSC and event.code == c.MSC_SCAN) { continue :outer; } if (event.type != c.EV_KEY) { try uinputWrite(uinput_dev, &event); continue :outer; } write_esc_lshift = event_equal(&event, &LShiftUp) and is_lshift_down; write_esc_rshift = event_equal(&event, &RShiftUp) and is_rshift_down; is_lshift_down = event_equal(&event, &LShiftDown); is_rshift_down = event_equal(&event, &RShiftDown); if (event_equal(&event, &CapsLockUp) or event_equal(&event, &CapsLockDown)) { event.code = c.KEY_LEFTMETA; } try uinputWrite(uinput_dev, &event); if (write_esc_lshift) { write_esc_lshift = false; try uinputWriteKey(uinput_dev, c.KEY_ESC); } if (write_esc_rshift) { write_esc_rshift = false; try uinputWriteKey(uinput_dev, c.KEY_ESC); } } }