command/map: use flags.zig, cleanup
This commit is contained in:
parent
eed7d94557
commit
2be9ac05d6
@ -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`.
|
||||||
|
@ -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", .{});
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user