command/map: use flags.zig, cleanup

This commit is contained in:
Isaac Freund 2022-12-28 21:56:42 +01:00
parent eed7d94557
commit 2be9ac05d6
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
3 changed files with 64 additions and 87 deletions

View File

@ -25,24 +25,23 @@ const util = @import("util.zig");
keysym: xkb.Keysym, keysym: xkb.Keysym,
modifiers: wlr.Keyboard.ModifierMask, modifiers: wlr.Keyboard.ModifierMask,
command_args: []const [:0]const u8, command_args: []const [:0]const u8,
options: Options,
// This is set for mappings with layout-pinning pub const Options = struct {
// If set, the layout with this index is always used to translate the given keycode /// When set to true the mapping will be executed on key release rather than on press
layout_index: ?u32, release: bool,
/// When set to true the mapping will be executed repeatedly while key is pressed
/// When set to true the mapping will be executed on key release rather than on press repeat: bool,
release: bool, // This is set for mappings with layout-pinning
// If set, the layout with this index is always used to translate the given keycode
/// When set to true the mapping will be executed repeatedly while key is pressed layout_index: ?u32,
repeat: bool, };
pub fn init( pub fn init(
keysym: xkb.Keysym, keysym: xkb.Keysym,
modifiers: wlr.Keyboard.ModifierMask, modifiers: wlr.Keyboard.ModifierMask,
release: bool,
repeat: bool,
layout_index: ?u32,
command_args: []const []const u8, command_args: []const []const u8,
options: Options,
) !Self { ) !Self {
const owned_args = try util.gpa.alloc([:0]u8, command_args.len); const owned_args = try util.gpa.alloc([:0]u8, command_args.len);
errdefer util.gpa.free(owned_args); errdefer util.gpa.free(owned_args);
@ -53,10 +52,8 @@ pub fn init(
return Self{ return Self{
.keysym = keysym, .keysym = keysym,
.modifiers = modifiers, .modifiers = modifiers,
.release = release,
.repeat = repeat,
.layout_index = layout_index,
.command_args = owned_args, .command_args = owned_args,
.options = options,
}; };
} }
@ -73,14 +70,14 @@ pub fn match(
released: bool, released: bool,
xkb_state: *xkb.State, xkb_state: *xkb.State,
) bool { ) bool {
if (released != self.release) return false; if (released != self.options.release) return false;
const keymap = xkb_state.getKeymap(); const keymap = xkb_state.getKeymap();
// If the mapping has no pinned layout, use the active layout. // If the mapping has no pinned layout, use the active layout.
// It doesn't matter if the index is out of range, since xkbcommon // It doesn't matter if the index is out of range, since xkbcommon
// will fall back to the active layout if so. // will fall back to the active layout if so.
const layout_index = self.layout_index orelse xkb_state.keyGetLayout(keycode); const layout_index = self.options.layout_index orelse xkb_state.keyGetLayout(keycode);
// Get keysyms from the base layer, as if modifiers didn't change keysyms. // Get keysyms from the base layer, as if modifiers didn't change keysyms.
// E.g. pressing `Super+Shift 1` does not translate to `Super Exclam`. // E.g. pressing `Super+Shift 1` does not translate to `Super Exclam`.

View File

@ -395,7 +395,7 @@ pub fn handleMapping(
var handled = false; var handled = false;
for (modes.items[self.mode_id].mappings.items) |*mapping| { for (modes.items[self.mode_id].mappings.items) |*mapping| {
if (mapping.match(keycode, modifiers, released, xkb_state)) { if (mapping.match(keycode, modifiers, released, xkb_state)) {
if (mapping.repeat) { if (mapping.options.repeat) {
self.repeating_mapping = mapping; self.repeating_mapping = mapping;
self.mapping_repeat_timer.timerUpdate(server.config.repeat_delay) catch { self.mapping_repeat_timer.timerUpdate(server.config.repeat_delay) catch {
log.err("failed to update mapping repeat timer", .{}); log.err("failed to update mapping repeat timer", .{});

View File

@ -20,6 +20,7 @@ const mem = std.mem;
const meta = std.meta; const meta = std.meta;
const wlr = @import("wlroots"); const wlr = @import("wlroots");
const xkb = @import("xkbcommon"); const xkb = @import("xkbcommon");
const flags = @import("flags");
const c = @import("../c.zig"); const c = @import("../c.zig");
const server = &@import("../main.zig").server; const server = &@import("../main.zig").server;
@ -41,16 +42,29 @@ pub fn map(
args: []const [:0]const u8, args: []const [:0]const u8,
out: *?[]const u8, out: *?[]const u8,
) Error!void { ) Error!void {
const optionals = try parseOptionalArgs(args[1..]); const result = flags.parser([:0]const u8, &.{
// offset caused by optional arguments .{ .name = "-release", .kind = .boolean },
const offset = optionals.i; .{ .name = "-repeat", .kind = .boolean },
if (args.len - offset < 5) return Error.NotEnoughArguments; .{ .name = "-layout", .kind = .arg },
}).parse(args[1..]) catch {
return error.InvalidValue;
};
if (result.args.len < 4) return Error.NotEnoughArguments;
if (optionals.release and optionals.repeat) return Error.ConflictingOptions; if (result.flags.@"-release" and result.flags.@"-repeat") return Error.ConflictingOptions;
const mode_raw = args[1 + offset]; const layout_index = blk: {
const modifiers_raw = args[2 + offset]; if (result.flags.@"-layout") |layout_raw| {
const keysym_raw = args[3 + offset]; break :blk try fmt.parseInt(u32, layout_raw, 10);
} else {
break :blk null;
}
};
const mode_raw = result.args[0];
const modifiers_raw = result.args[1];
const keysym_raw = result.args[2];
const command = result.args[3..];
const mode_id = try modeNameToId(mode_raw, out); const mode_id = try modeNameToId(mode_raw, out);
const modifiers = try parseModifiers(modifiers_raw, out); const modifiers = try parseModifiers(modifiers_raw, out);
@ -61,18 +75,20 @@ pub fn map(
const new = try Mapping.init( const new = try Mapping.init(
keysym, keysym,
modifiers, modifiers,
optionals.release, command,
optionals.repeat, .{
optionals.layout_index, .release = result.flags.@"-release",
args[4 + offset ..], .repeat = result.flags.@"-repeat",
.layout_index = layout_index,
},
); );
errdefer new.deinit(); errdefer new.deinit();
if (mappingExists(mode_mappings, modifiers, keysym, optionals.release)) |current| { if (mappingExists(mode_mappings, modifiers, keysym, result.flags.@"-release")) |current| {
mode_mappings.items[current].deinit(); mode_mappings.items[current].deinit();
mode_mappings.items[current] = new; mode_mappings.items[current] = new;
// Warn user if they overwrote an existing keybinding using riverctl. // Warn user if they overwrote an existing keybinding using riverctl.
const opts = if (optionals.release) "-release " else ""; const opts = if (result.flags.@"-release") "-release " else "";
out.* = try fmt.allocPrint( out.* = try fmt.allocPrint(
util.gpa, util.gpa,
"overwrote an existing keybinding: {s} {s}{s} {s}", "overwrote an existing keybinding: {s} {s}{s} {s}",
@ -185,7 +201,9 @@ fn mappingExists(
release: bool, release: bool,
) ?usize { ) ?usize {
for (mappings.items) |mapping, i| { for (mappings.items) |mapping, i| {
if (meta.eql(mapping.modifiers, modifiers) and mapping.keysym == keysym and mapping.release == release) { if (meta.eql(mapping.modifiers, modifiers) and
mapping.keysym == keysym and mapping.options.release == release)
{
return i; return i;
} }
} }
@ -321,68 +339,30 @@ fn parseSwitchState(
} }
} }
const OptionalArgsContainer = struct {
i: usize,
release: bool,
repeat: bool,
layout_index: ?u32,
};
/// Parses optional args (such as -release) and return the index of the first argument that is
/// not an optional argument
/// Returns an OptionalArgsContainer with the settings set according to the args
fn parseOptionalArgs(args: []const []const u8) !OptionalArgsContainer {
// Set to defaults
var parsed = OptionalArgsContainer{
// i is the number of arguments consumed
.i = 0,
.release = false,
.repeat = false,
.layout_index = null,
};
var j: usize = 0;
while (j < args.len) : (j += 1) {
if (mem.eql(u8, args[j], "-release")) {
parsed.release = true;
parsed.i += 1;
} else if (mem.eql(u8, args[j], "-repeat")) {
parsed.repeat = true;
parsed.i += 1;
} else if (mem.eql(u8, args[j], "-layout")) {
j += 1;
if (j == args.len) return Error.NotEnoughArguments;
// To keep things simple here, we do not check if the layout index
// is out of range. We rely on xkbcommon to handle this case:
// xkbcommon will simply use the active layout instead, leaving
// this option without effect
parsed.layout_index = try std.fmt.parseInt(u32, args[j], 10);
parsed.i += 2;
} else {
// Break if the arg is not an option
break;
}
}
return parsed;
}
/// Remove a mapping from a given mode /// Remove a mapping from a given mode
/// ///
/// Example: /// Example:
/// unmap normal Mod4+Shift Return /// unmap normal Mod4+Shift Return
pub fn unmap(seat: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void { pub fn unmap(seat: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void {
const optionals = try parseOptionalArgs(args[1..]); const result = flags.parser([:0]const u8, &.{
// offset caused by optional arguments .{ .name = "-release", .kind = .boolean },
const offset = optionals.i; }).parse(args[1..]) catch {
if (args.len - offset < 4) return Error.NotEnoughArguments; return error.InvalidValue;
};
if (result.args.len < 3) return Error.NotEnoughArguments;
if (result.args.len > 3) return Error.TooManyArguments;
const mode_id = try modeNameToId(args[1 + offset], out); const mode_id = try modeNameToId(result.args[0], out);
const modifiers = try parseModifiers(args[2 + offset], out); const modifiers = try parseModifiers(result.args[1], out);
const keysym = try parseKeysym(args[3 + offset], out); const keysym = try parseKeysym(result.args[2], out);
const mode_mappings = &server.config.modes.items[mode_id].mappings; const mode_mappings = &server.config.modes.items[mode_id].mappings;
const mapping_idx = mappingExists(mode_mappings, modifiers, keysym, optionals.release) orelse return; const mapping_idx = mappingExists(
mode_mappings,
modifiers,
keysym,
result.flags.@"-release",
) orelse return;
// Repeating mappings borrow the Mapping directly. To prevent a possible // Repeating mappings borrow the Mapping directly. To prevent a possible
// crash if the Mapping ArrayList is reallocated, stop any currently // crash if the Mapping ArrayList is reallocated, stop any currently