diff --git a/api.lua b/api.lua index 3b8d7fd..cb25ff9 100644 --- a/api.lua +++ b/api.lua @@ -80,4 +80,11 @@ mez.options = { } } +---------------- Keybinds ---------------- +mez.add_keybind("modifier", "keycode", function() + -- callback +end, { + -- additional options +}) + -- Virtual terminal switching diff --git a/runtime/share/mezzaluna/init.lua b/runtime/share/mezzaluna/init.lua index cfef496..f11c827 100644 --- a/runtime/share/mezzaluna/init.lua +++ b/runtime/share/mezzaluna/init.lua @@ -9,3 +9,8 @@ end mez.path.config = mez.fs.joinpath(env_conf, "mez", "init.lua") package.path = package.path..";"..mez.fs.joinpath(env_conf, "mez", "lua", "?.lua") + +-- this is an example +-- mez.api.add_keymap("ctrl", "a", function() +-- print("hello from my keymap") +-- end) diff --git a/src/keyboard.zig b/src/keyboard.zig index 081fefe..1c133bd 100644 --- a/src/keyboard.zig +++ b/src/keyboard.zig @@ -3,6 +3,7 @@ const Keyboard = @This(); const std = @import("std"); const gpa = std.heap.c_allocator; const server = &@import("main.zig").server; +const Keymap = @import("keymap.zig"); const wl = @import("wayland").server.wl; const wlr = @import("wlroots"); @@ -65,17 +66,17 @@ fn handleModifiers(_: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) fn handleKey(_: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void { // Translate libinput keycode -> xkbcommon - // const keycode = event.keycode + 8; + const keycode = event.keycode + 8; - // TODO: lua handle keybinds here - const handled = false; - if (server.keyboard.wlr_keyboard.getModifiers().alt and event.state == .pressed) { - // for (wlr_keyboard.xkb_state.?.keyGetSyms(keycode)) |sym| { - // if (keyboard.server.handleKeybind(sym)) { - // handled = true; - // break; - // } - // } + var handled = false; + const modifiers = server.keyboard.wlr_keyboard.getModifiers(); + for (server.keyboard.wlr_keyboard.xkb_state.?.keyGetSyms(keycode)) |sym| { + if (server.keymaps.get(Keymap.hash(modifiers, sym))) |map| { + if (event.state == .pressed and !map.options.on_release) { + map.callback(); + handled = true; + } + } } if (!handled) { diff --git a/src/keymap.zig b/src/keymap.zig new file mode 100644 index 0000000..c6363d6 --- /dev/null +++ b/src/keymap.zig @@ -0,0 +1,37 @@ +//! This is a simple way to define a keymap. To keep hashing consistent the +//! hash is generated here. +const Keymap = @This(); + +const std = @import("std"); + +const xkb = @import("xkbcommon"); +const wlr = @import("wlroots"); +const zlua = @import("zlua"); + +const Lua = &@import("main.zig").lua; + +modifier: wlr.Keyboard.ModifierMask, +keycode: xkb.Keysym, +/// This is the location of the lua function in the lua registry +lua_ref_idx: i32, +options: struct { + /// if false the callback is called on press + on_release: bool, +}, + +pub fn callback(self: *const Keymap) void { + const t = Lua.state.rawGetIndex(zlua.registry_index, self.lua_ref_idx); + if (t != zlua.LuaType.function) { + std.log.err("Failed to call keybind, it doesn't have a callback.", .{}); + return; + } + Lua.state.pushValue(1); + Lua.state.call(.{ .args = 0, .results = 0 }); + Lua.state.pop(-1); +} + +pub fn hash(modifier: wlr.Keyboard.ModifierMask, keycode: xkb.Keysym) u64 { + const mod_val: u32 = @bitCast(modifier); + const key_val: u32 = @intFromEnum(keycode); + return (@as(u64, mod_val) << 32) | @as(u64, key_val); +} diff --git a/src/lua/api.zig b/src/lua/api.zig new file mode 100644 index 0000000..0f097c6 --- /dev/null +++ b/src/lua/api.zig @@ -0,0 +1,91 @@ +const Api = @This(); + +const std = @import("std"); +const server = &@import("../main.zig").server; +const Keymap = @import("../keymap.zig"); + +const zlua = @import("zlua"); +const xkb = @import("xkbcommon"); +const wlr = @import("wlroots"); + +const gpa = std.heap.c_allocator; + +pub fn add_keymap(L: *zlua.Lua) i32 { + const nargs: i32 = L.getTop(); + if (nargs < 3) { + L.raiseErrorStr("Expected at least three arguments", .{}); + return 0; + } + + // ensure the first three agrs of the correct types + L.checkType(1, .string); + L.checkType(2, .string); + L.checkType(3, .function); + + var keymap: Keymap = undefined; + + const mod = L.toString(1) catch { + L.raiseErrorStr("Lua error check your config", .{}); + return 0; + }; + var it = std.mem.splitScalar(u8, mod, '|'); + var modifiers = wlr.Keyboard.ModifierMask{}; + while (it.next()) |m| { + // TODO: can we generate this at comptime? + if (std.mem.eql(u8, m, "shift")) { + modifiers.shift = true; + } else if (std.mem.eql(u8, m, "caps")) { + modifiers.caps = true; + } else if (std.mem.eql(u8, m, "ctrl")) { + modifiers.ctrl = true; + } else if (std.mem.eql(u8, m, "alt")) { + modifiers.alt = true; + } else if (std.mem.eql(u8, m, "mod2")) { + modifiers.mod2 = true; + } else if (std.mem.eql(u8, m, "mod3")) { + modifiers.mod3 = true; + } else if (std.mem.eql(u8, m, "logo")) { + modifiers.logo = true; + } else if (std.mem.eql(u8, m, "mod5")) { + modifiers.mod5 = true; + } + } + keymap.modifier = modifiers; + + const key = L.toString(2) catch { + L.raiseErrorStr("Lua error check your config", .{}); + return 0; + }; + keymap.keycode = xkb.Keysym.fromName(key, .no_flags); + + L.checkType(3, .function); + keymap.lua_ref_idx = L.ref(zlua.registry_index) catch { + L.raiseErrorStr("Lua error check your config", .{}); + return 0; + }; + + // FIXME: for som reason I can't seem to get this to validate that the 4th + // argument exists unless there's a 5th argument. It doesn't seem to matter + // what type the 5th is just that it's there. + if (nargs == 4) { + // L.checkType(4, .table); + // _ = L.pushString("on_release"); + // _ = L.getTable(4); + // const b = L.toBoolean(-1); + // L.pop(-1); + // L.pop(-1); + } + + const hash = Keymap.hash(keymap.modifier, keymap.keycode); + server.keymaps.put(hash, keymap) catch |err| { + std.log.err("Failed to add keymap to keymaps: {}", .{err}); + return 0; + }; + + return 0; +} + +pub fn get_keybind(L: *zlua.Lua) i32 { + _ = L; + return 0; +} diff --git a/src/lua/lua.zig b/src/lua/lua.zig index 7a3e6cd..e63df2d 100644 --- a/src/lua/lua.zig +++ b/src/lua/lua.zig @@ -6,6 +6,7 @@ const zlua = @import("zlua"); const Bridge = @import("bridge.zig"); const Fs = @import("fs.zig"); +const Api = @import("api.zig"); const gpa = std.heap.c_allocator; @@ -53,10 +54,15 @@ pub fn init(self: *Lua) !void { defer _ = self.state.setField(-2, "path"); } { - const funcs = zlua.fnRegsFromType(Fs); - self.state.newLib(funcs); + const fs_funcs = zlua.fnRegsFromType(Fs); + self.state.newLib(fs_funcs); self.state.setField(-2, "fs"); } + { + const api_funcs = zlua.fnRegsFromType(Api); + self.state.newLib(api_funcs); + self.state.setField(-2, "api"); + } } loadRuntimeDir(self) catch |err| { diff --git a/src/main.zig b/src/main.zig index 586fc6c..e4e77d5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -14,9 +14,9 @@ pub fn main() !void { std.log.info("Starting mezzaluna", .{}); - try lua.init(); try server.init(); defer server.deinit(); + try lua.init(); var buf: [11]u8 = undefined; const socket = try server.wl_server.addSocketAuto(&buf); diff --git a/src/server.zig b/src/server.zig index bf51592..34bb39a 100644 --- a/src/server.zig +++ b/src/server.zig @@ -10,6 +10,7 @@ const Cursor = @import("cursor.zig"); const Keyboard = @import("keyboard.zig"); const Output = @import("output.zig"); const View = @import("view.zig"); +const Keymap = @import("keymap.zig"); const gpa = std.heap.c_allocator; const server = &@import("main.zig").server; @@ -32,6 +33,7 @@ root: Root, seat: Seat, cursor: Cursor, keyboard: Keyboard, +keymaps: std.AutoHashMap(u64, Keymap), // Backend listeners new_input: wl.Listener(*wlr.InputDevice) = .init(handleNewInput), @@ -69,6 +71,7 @@ pub fn init(self: *Server) !void { .seat = undefined, .cursor = undefined, .keyboard = undefined, + .keymaps = .init(gpa), }; try self.renderer.initServer(wl_server);