From cacc98616657305f8c9c499f232fb7bd226e0de0 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Tue, 13 Sep 2022 12:05:21 +1000 Subject: [PATCH] map-pointer: allow running arbitrary commands --- completions/fish/riverctl.fish | 2 +- completions/zsh/_riverctl | 2 +- doc/riverctl.1.scd | 8 +++-- example/init | 3 ++ river/Cursor.zig | 4 +++ river/PointerMapping.zig | 53 +++++++++++++++++++++++++++++++++- river/Seat.zig | 9 ++++-- river/command/map.zig | 33 ++++++++++----------- 8 files changed, 89 insertions(+), 25 deletions(-) diff --git a/completions/fish/riverctl.fish b/completions/fish/riverctl.fish index aabf99c..f24f32a 100644 --- a/completions/fish/riverctl.fish +++ b/completions/fish/riverctl.fish @@ -44,7 +44,7 @@ complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'send-to-previous complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'declare-mode' -d 'Create a new mode' complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'enter-mode' -d 'Switch to given mode if it exists' complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'map' -d 'Run command when key is pressed while modifiers are held down and in the specified mode' -complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'map-pointer' -d 'Move or resize views when button and modifers are held down while in the specified mode' +complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'map-pointer' -d 'Move or resize views or run command when button and modifers are held down while in the specified mode' complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'map-switch ' -d 'Run command when river receives a switch event in the specified mode' complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'unmap' -d 'Remove the mapping defined by the arguments' complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'unmap-pointer' -d 'Remove the pointer mapping defined by the arguments' diff --git a/completions/zsh/_riverctl b/completions/zsh/_riverctl index f5cd125..f303e5e 100644 --- a/completions/zsh/_riverctl +++ b/completions/zsh/_riverctl @@ -38,7 +38,7 @@ _riverctl_subcommands() 'declare-mode:Create a new mode' 'enter-mode:Switch to given mode if it exists' 'map:Run command when key is pressed while modifiers are held down and in the specified mode' - 'map-pointer:Move or resize views when button and modifiers are held down while in the specified mode' + 'map-pointer:Move or resize views or run command when button and modifiers are held down while in the specified mode' 'map-switch:Run command when river receives a switch event in the specified mode' 'unmap:Remove the mapping defined by the arguments' 'unmap-pointer:Remove the pointer mapping defined by the arguments' diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index d936308..b75fcae 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -212,9 +212,10 @@ A complete list may be found in _/usr/include/linux/input-event-codes.h_ - _key_: an XKB keysym name as described above - _command_: any command that may be run with riverctl -*map-pointer* _mode_ _modifiers_ _button_ _action_ - Move or resize views when _button_ and _modifiers_ are held down - while in the specified _mode_. +*map-pointer* _mode_ _modifiers_ _button_ _action_|_command_ + Move or resize views or run _command_ when _button_ and _modifiers_ are held + down while in the specified _mode_. The view under the cursor will be + focused. - _mode_: name of the mode for which to create the mapping - _modifiers_: one or more of the modifiers listed above, separated @@ -223,6 +224,7 @@ A complete list may be found in _/usr/include/linux/input-event-codes.h_ - _action_: one of the following values: - move-view - resize-view + - _command_: any command that may be run with riverctl *map-switch* _mode_ *lid*|*tablet* _state_ _command_ Run _command_ when river receives a certain switch event. diff --git a/example/init b/example/init index b95a180..08b7cb1 100755 --- a/example/init +++ b/example/init @@ -71,6 +71,9 @@ riverctl map-pointer normal Super BTN_LEFT move-view # Super + Right Mouse Button to resize views riverctl map-pointer normal Super BTN_RIGHT resize-view +# Super + Middle Mouse Button to toggle float +riverctl map-pointer normal Super BTN_MIDDLE toggle-float + for i in $(seq 1 9) do tags=$((1 << ($i - 1))) diff --git a/river/Cursor.zig b/river/Cursor.zig index b09dbff..e99f8e3 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -528,6 +528,10 @@ fn handlePointerMapping(self: *Self, event: *wlr.Pointer.event.Button, view: *Vi switch (mapping.action) { .move => if (!fullscreen) self.enterMode(.move, view), .resize => if (!fullscreen) self.enterMode(.resize, view), + .command => |args| { + self.seat.focus(view); + self.seat.runCommand(args); + }, } break true; } diff --git a/river/PointerMapping.zig b/river/PointerMapping.zig index 6a796bd..bae2cbd 100644 --- a/river/PointerMapping.zig +++ b/river/PointerMapping.zig @@ -14,13 +14,64 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +const Self = @This(); + +const std = @import("std"); const wlr = @import("wlroots"); -pub const Action = enum { +const util = @import("util.zig"); + +pub const ActionType = enum { move, resize, + command, +}; + +pub const Action = union(ActionType) { + move: void, + resize: void, + command: []const [:0]const u8, }; event_code: u32, modifiers: wlr.Keyboard.ModifierMask, action: Action, +arena: std.heap.ArenaAllocator, + +pub fn init( + event_code: u32, + modifiers: wlr.Keyboard.ModifierMask, + action_type: ActionType, + command_args: []const [:0]const u8, +) !Self { + var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(util.gpa); + errdefer arena.deinit(); + + const action: Action = switch (action_type) { + ActionType.move => Action.move, + ActionType.resize => Action.resize, + ActionType.command => blk: { + const allocator: std.mem.Allocator = arena.allocator(); + + var owned_args = try std.ArrayListUnmanaged([:0]const u8).initCapacity(allocator, command_args.len); + + for (command_args) |arg| { + const owned = try allocator.dupeZ(u8, arg); + owned_args.appendAssumeCapacity(owned); + } + + break :blk Action{ .command = owned_args.toOwnedSlice(allocator) }; + }, + }; + + return Self{ + .event_code = event_code, + .modifiers = modifiers, + .action = action, + .arena = arena, + }; +} + +pub fn deinit(self: *Self) void { + self.arena.deinit(); +} diff --git a/river/Seat.zig b/river/Seat.zig index 1a4b7e6..892ba14 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -434,7 +434,7 @@ pub fn handleSwitchMapping( } } -fn runCommand(self: *Self, args: []const [:0]const u8) void { +pub fn runCommand(self: *Self, args: []const [:0]const u8) void { var out: ?[]const u8 = null; defer if (out) |s| util.gpa.free(s); command.run(self, args, &out) catch |err| { @@ -442,7 +442,12 @@ fn runCommand(self: *Self, args: []const [:0]const u8) void { command.Error.Other => out.?, else => command.errToMsg(err), }; - std.log.scoped(.command).err("{s}: {s}", .{ args[0], failure_message }); + + if (args.len == 0) { + std.log.scoped(.command).err("{s}", .{failure_message}); + } else { + std.log.scoped(.command).err("{s}: {s}", .{ args[0], failure_message }); + } return; }; if (out) |s| { diff --git a/river/command/map.zig b/river/command/map.zig index 4a33503..f321531 100644 --- a/river/command/map.zig +++ b/river/command/map.zig @@ -130,33 +130,31 @@ pub fn mapPointer( out: *?[]const u8, ) Error!void { if (args.len < 5) return Error.NotEnoughArguments; - if (args.len > 5) return Error.TooManyArguments; const mode_id = try modeNameToId(args[1], out); const modifiers = try parseModifiers(args[2], out); const event_code = try parseEventCode(args[3], out); const action = if (mem.eql(u8, args[4], "move-view")) - PointerMapping.Action.move + PointerMapping.ActionType.move else if (mem.eql(u8, args[4], "resize-view")) - PointerMapping.Action.resize - else { - out.* = try fmt.allocPrint( - util.gpa, - "invalid pointer action {s}, must be move-view or resize-view", - .{args[4]}, - ); - return Error.Other; - }; + PointerMapping.ActionType.resize + else + PointerMapping.ActionType.command; - const new = PointerMapping{ - .event_code = event_code, - .modifiers = modifiers, - .action = action, - }; + if (action != PointerMapping.ActionType.command and args.len > 5) return Error.TooManyArguments; + + var new = try PointerMapping.init( + event_code, + modifiers, + action, + args[4..], + ); + errdefer new.deinit(); const mode_pointer_mappings = &server.config.modes.items[mode_id].pointer_mappings; if (pointerMappingExists(mode_pointer_mappings, modifiers, event_code)) |current| { + mode_pointer_mappings.items[current].deinit(); mode_pointer_mappings.items[current] = new; } else { try mode_pointer_mappings.append(util.gpa, new); @@ -428,5 +426,6 @@ pub fn unmapPointer(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Err const mode_pointer_mappings = &server.config.modes.items[mode_id].pointer_mappings; const mapping_idx = pointerMappingExists(mode_pointer_mappings, modifiers, event_code) orelse return; - _ = mode_pointer_mappings.swapRemove(mapping_idx); + var mapping = mode_pointer_mappings.swapRemove(mapping_idx); + mapping.deinit(); }