From 64dccb248d9fd29eb245d3b64553e83ec432470e Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Sun, 4 Jan 2026 22:26:55 -0500 Subject: [PATCH] basic mousemap stuff, probably needs to be changed --- src/Cursor.zig | 91 +++++++++++++++++++----------------------- src/Server.zig | 1 + src/lua/Input.zig | 59 +++++++++++++++++++++++++++ src/types/Mousemap.zig | 52 ++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 49 deletions(-) create mode 100644 src/types/Mousemap.zig diff --git a/src/Cursor.zig b/src/Cursor.zig index d0180f8..c1ff698 100644 --- a/src/Cursor.zig +++ b/src/Cursor.zig @@ -13,7 +13,6 @@ const Utils = @import("Utils.zig"); const c = @import("C.zig").c; const server = &@import("main.zig").server; -const linux = std.os.linux; wlr_cursor: *wlr.Cursor, x_cursor_manager: *wlr.XcursorManager, @@ -26,7 +25,7 @@ frame: wl.Listener(*wlr.Cursor) = .init(handleFrame), hold_begin: wl.Listener(*wlr.Pointer.event.HoldBegin) = .init(handleHoldBegin), hold_end: wl.Listener(*wlr.Pointer.event.HoldEnd) = .init(handleHoldEnd), -mode: enum { passthrough, move, resize } = .passthrough, +mode: enum { normal, drag } = .normal, // Drag information drag: struct { @@ -80,8 +79,9 @@ pub fn deinit(self: *Cursor) void { pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { server.events.exec("PointerMotion", .{self.wlr_cursor.x, self.wlr_cursor.y}); + switch (self.mode) { - .passthrough => { + .normal => { const output = server.seat.focused_output; // Exit the switch if no focused output exists if (output == null) return; @@ -100,6 +100,8 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { switch (surfaceAtResult.?.scene_node_data.*) { .view => { + // TODO: If serious performance issues arise from this, we need to be able to disable unused hooks + // Perhaps after the config is executed, if no callbacks are present on this hook, we disable it entirely server.events.exec("ViewPointerMotion", .{surfaceAtResult.?.scene_node_data.view.id, self.wlr_cursor.x, self.wlr_cursor.y}); }, .layer_surface => { }, @@ -109,55 +111,46 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void { server.seat.wlr_seat.pointerNotifyEnter(surfaceAtResult.?.surface, surfaceAtResult.?.sx, surfaceAtResult.?.sy); server.seat.wlr_seat.pointerNotifyMotion(time_msec, surfaceAtResult.?.sx, surfaceAtResult.?.sy); }, - .move => { // TODO: Have these behave more like pointer motion - if(self.drag.view) |view| { - view.scene_tree.node.setPosition( - // TODO: add a lua option to configure the behavior of this, by - // default it will be the following: - @as(c_int, @intFromFloat(self.wlr_cursor.x)) - self.drag.view_offset_x.?, - @as(c_int, @intFromFloat(self.wlr_cursor.y)) - self.drag.view_offset_y.? - // and the user should be able to configure if it clamps or not - ); - } - }, - .resize => { - // Fix this resize - const view: *View = blk: { - if(server.seat.focused_surface) |fs| { - if(fs != .view) return; - break :blk fs.view; - } else { return; } - }; + .drag => { - _ = view.xdg_toplevel.setSize( - // TODO: configure the min and max using lua? - std.math.clamp(@as(c_int, @as(i32, @intFromFloat(self.wlr_cursor.x)) - view.scene_tree.node.x), 10, std.math.maxInt(i32)), - std.math.clamp(@as(c_int, @as(i32, @intFromFloat(self.wlr_cursor.y)) - view.scene_tree.node.y), 10, std.math.maxInt(i32)) - ); - }, + // @hook PointerDragMotion + // @param button string // TODO Translate a button to a string or smth + // @param state string - "pressed" or "released" + // @param time_msecs number // TODO idk what the hell msecs is + server.events.exec("PointerDragMotion", .{ + self.wlr_cursor.x, + self.wlr_cursor.y, + + self.drag.start_x, + self.drag.start_y, + self.drag.view.?.id, + self.drag.view_offset_x, + self.drag.view_offset_y + }); + } } } // --------- WLR Cursor event handlers --------- fn handleMotion( - _: *wl.Listener(*wlr.Pointer.event.Motion), - event: *wlr.Pointer.event.Motion, +_: *wl.Listener(*wlr.Pointer.event.Motion), +event: *wlr.Pointer.event.Motion, ) void { server.cursor.wlr_cursor.move(event.device, event.delta_x, event.delta_y); server.cursor.processCursorMotion(event.time_msec); } fn handleMotionAbsolute( - _: *wl.Listener(*wlr.Pointer.event.MotionAbsolute), - event: *wlr.Pointer.event.MotionAbsolute, +_: *wl.Listener(*wlr.Pointer.event.MotionAbsolute), +event: *wlr.Pointer.event.MotionAbsolute, ) void { server.cursor.wlr_cursor.warpAbsolute(event.device, event.x, event.y); server.cursor.processCursorMotion(event.time_msec); } fn handleButton( - listener: *wl.Listener(*wlr.Pointer.event.Button), - event: *wlr.Pointer.event.Button +listener: *wl.Listener(*wlr.Pointer.event.Button), +event: *wlr.Pointer.event.Button ) void { const cursor: *Cursor = @fieldParentPtr("button", listener); @@ -166,7 +159,7 @@ fn handleButton( // @hook PointerButtonPress // TODO Probably change this name // @param button string // TODO Translate a button to a string or smth // @param state string - "pressed" or "released" - // @param time_msecs number // TODO idk what the hell msecs is + // @param time_msecs number const state = if (event.state == .pressed) "pressed" else "released"; server.events.exec("PointerButtonPress", .{event.button, state, event.time_msec}); @@ -198,7 +191,7 @@ fn handleButton( } }, .released => { - cursor.mode = .passthrough; + cursor.mode = .normal; if(cursor.drag.view) |view| { _ = view.xdg_toplevel.setResizing(false); @@ -215,8 +208,8 @@ fn handleButton( } fn handleHoldBegin( - listener: *wl.Listener(*wlr.Pointer.event.HoldBegin), - event: *wlr.Pointer.event.HoldBegin +listener: *wl.Listener(*wlr.Pointer.event.HoldBegin), +event: *wlr.Pointer.event.HoldBegin ) void { _ = listener; _ = event; @@ -224,8 +217,8 @@ fn handleHoldBegin( } fn handleHoldEnd( - listener: *wl.Listener(*wlr.Pointer.event.HoldEnd), - event: *wlr.Pointer.event.HoldEnd +listener: *wl.Listener(*wlr.Pointer.event.HoldEnd), +event: *wlr.Pointer.event.HoldEnd ) void { _ = listener; _ = event; @@ -233,17 +226,17 @@ fn handleHoldEnd( } fn handleAxis( - _: *wl.Listener(*wlr.Pointer.event.Axis), - event: *wlr.Pointer.event.Axis, +_: *wl.Listener(*wlr.Pointer.event.Axis), +event: *wlr.Pointer.event.Axis, ) void { server.seat.wlr_seat.pointerNotifyAxis( - event.time_msec, - event.orientation, - event.delta, - event.delta_discrete, - event.source, - event.relative_direction, - ); + event.time_msec, + event.orientation, + event.delta, + event.delta_discrete, + event.source, + event.relative_direction, +); } fn handleFrame(_: *wl.Listener(*wlr.Cursor), _: *wlr.Cursor) void { diff --git a/src/Server.zig b/src/Server.zig index 51b5a8e..bb44cda 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -45,6 +45,7 @@ cursor: Cursor, // Lua data keymaps: std.AutoHashMap(u64, Keymap), +mousemaps: std.AutoHashMap(u64, Mousemap), hooks: std.AutoHashMap(i32, *Hook), events: Events, remote_lua_clients: std.DoublyLinkedList, diff --git a/src/lua/Input.zig b/src/lua/Input.zig index 896de35..d5a0f44 100644 --- a/src/lua/Input.zig +++ b/src/lua/Input.zig @@ -62,6 +62,43 @@ pub fn add_keymap(L: *zlua.Lua) i32 { return 1; } +/// ---Create a new mousemap +/// ---@param string modifiers +/// ---@param string button name (ex. "left", "right") +/// ---@param table options +pub fn add_mousemap(L: *zlua.Lua) i32 { + var mousemap: Mousemap = undefined; + // mousemap.options.repeat = true; + + const mod = L.checkString(1); + mousemap.modifier = parse_modkeys(mod); + + const button = L.checkString(2); + mousemap.keycode = xkb.Keysym.fromName(button, .no_flags); + + _ = L.pushString("press"); + _ = L.getTable(3); + if (L.isFunction(-1)) { + keymap.options.lua_press_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic(); + } + + _ = L.pushString("release"); + _ = L.getTable(3); + if (L.isFunction(-1)) { + keymap.options.lua_release_ref_idx = L.ref(zlua.registry_index) catch Utils.oomPanic(); + } + + _ = L.pushString("repeat"); + _ = L.getTable(3); + keymap.options.repeat = L.isNil(-1) or L.toBoolean(-1); + + const hash = Keymap.hash(mousemap.modifier, mousemap.keycode); + server.mousemaps.put(hash, mousemap) catch Utils.oomPanic(); + + L.pushNil(); + return 1; +} + /// ---Remove an existing keymap /// ---@param string modifiers /// ---@param string keys @@ -83,6 +120,27 @@ pub fn del_keymap(L: *zlua.Lua) i32 { return 1; } +/// ---Remove an existing mousemap +/// ---@param string modifiers +/// ---@param string button +pub fn del_mousemap(L: *zlua.Lua) i32 { + L.checkType(1, .string); + L.checkType(2, .string); + + var mousemap: Mousemap = undefined; + const mod = L.checkString(1); + + mousemap.modifier = parse_modkeys(mod); + + const button = L.checkString(2); + + mousemap.keycode = xkb.Keysym.fromName(button, .no_flags); + _ = server.mousemaps.remove(Keymap.hash(mousemap.modifier, mousemap.keycode)); + + L.pushNil(); + return 1; +} + /// ---Get the repeat information /// ---@return integer[2] pub fn get_repeat_info(L: *zlua.Lua) i32 { @@ -110,3 +168,4 @@ pub fn set_repeat_info(L: *zlua.Lua) i32 { server.seat.keyboard_group.keyboard.setRepeatInfo(rate, delay); return 0; } + diff --git a/src/types/Mousemap.zig b/src/types/Mousemap.zig new file mode 100644 index 0000000..735d9e9 --- /dev/null +++ b/src/types/Mousemap.zig @@ -0,0 +1,52 @@ +//! This is a simple way to define a mousemap. To keep hashing consistent the +//! hash is generated here. +const Mousemap = @This(); + +const std = @import("std"); + +const xkb = @import("xkbcommon"); +const wlr = @import("wlroots"); +const zlua = @import("zlua"); + +const RemoteLua = @import("../RemoteLua.zig"); +const Lua = &@import("../main.zig").lua; + +modifier: wlr.Keyboard.ModifierMask, +keycode: xkb.Keysym, +options: struct { + /// This is the location of the on press lua function in the lua registry + lua_press_ref_idx: i32, + /// This is the location of the on release lua function in the lua registry + lua_release_ref_idx: i32, + /// THis is the location of the on drag lua function in the lua registry + lua_drag_ref_idx: i32, +}, + +pub const MousemapState = enum { press, drag, release }; + +pub fn callback(self: *const Mousemap, state: MousemapState) void { + const lua_ref_idx = if (release) self.options.lua_release_ref_idx else self.options.lua_press_ref_idx; + const lua_ref_idx = switch(state) { + .press => self.options.lua_press_ref_idx, + .release => self.options.lua_release_ref_idx, + .drag => self.options.lua_drag_ref_idx + }; + + const t = Lua.state.rawGetIndex(zlua.registry_index, lua_ref_idx); + if (t != zlua.LuaType.function) { + RemoteLua.sendNewLogEntry("Failed to call keybind, it doesn't have a callback."); + Lua.state.pop(1); + return; + } + + Lua.state.protectedCall(.{ .args = 0, .results = 0 }) catch { + RemoteLua.sendNewLogEntry(Lua.state.toString(-1) catch unreachable); + }; + 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); +}