Implement map -release

This commit is contained in:
Marten Ringwelski 2020-09-15 13:46:08 +02:00 committed by Isaac Freund
parent 7e02fb679c
commit 52cd871151
5 changed files with 76 additions and 32 deletions

View file

@ -123,9 +123,11 @@ that tag 1 through 9 are visible.
but let the currently focused window in focus. but let the currently focused window in focus.
When set to _strict_ this is not the case. The focus will be updated on every cursor movement. When set to _strict_ this is not the case. The focus will be updated on every cursor movement.
*map* _mode_ _modifiers_ _key_ _command_ *map* [-release] _mode_ _modifiers_ _key_ _command_
_mode_ is either “normal” (the default mode) or a mode created with _mode_ is either “normal” (the default mode) or a mode created with
*declare-mode*. _modifiers_ is a list of one or more of the following *declare-mode*.
If _-release_ is specified the mapping is executed on key release rather than key press.
_modifiers_ is a list of one or more of the following
modifiers separated with a plus sign: modifiers separated with a plus sign:
- Shift - Shift

View file

@ -103,27 +103,29 @@ fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
var handled = false; var handled = false;
// TODO: These modifiers aren't properly handled, see sway's code // TODO: These modifiers aren't properly handled, see sway's code
const modifiers = c.wlr_keyboard_get_modifiers(wlr_keyboard); const modifiers = c.wlr_keyboard_get_modifiers(wlr_keyboard);
if (event.state == .WLR_KEY_PRESSED) { const released = event.state == .WLR_KEY_RELEASED;
var i: usize = 0;
while (i < translated_keysyms_len) : (i += 1) { var i: usize = 0;
if (self.handleBuiltinMapping(translated_keysyms.?[i])) { while (i < translated_keysyms_len) : (i += 1) {
handled = true; // Handle builtin mapping only when keys are pressed
break; if (!released and self.handleBuiltinMapping(translated_keysyms.?[i])) {
} else if (self.seat.handleMapping(translated_keysyms.?[i], modifiers)) { handled = true;
handled = true; break;
break; } else if (self.seat.handleMapping(translated_keysyms.?[i], modifiers, released)) {
} handled = true;
break;
} }
if (!handled) { }
i = 0; if (!handled) {
while (i < raw_keysyms_len) : (i += 1) { i = 0;
if (self.handleBuiltinMapping(raw_keysyms.?[i])) { while (i < raw_keysyms_len) : (i += 1) {
handled = true; // Handle builtin mapping only when keys are pressed
break; if (!released and self.handleBuiltinMapping(raw_keysyms.?[i])) {
} else if (self.seat.handleMapping(raw_keysyms.?[i], modifiers)) { handled = true;
handled = true; break;
break; } else if (self.seat.handleMapping(raw_keysyms.?[i], modifiers, released)) {
} handled = true;
break;
} }
} }
} }

View file

@ -25,10 +25,14 @@ keysym: c.xkb_keysym_t,
modifiers: u32, modifiers: u32,
command_args: []const []const u8, command_args: []const []const u8,
/// When set to true the mapping will be executed on key release rather than on press
release: bool,
pub fn init( pub fn init(
allocator: *std.mem.Allocator, allocator: *std.mem.Allocator,
keysym: c.xkb_keysym_t, keysym: c.xkb_keysym_t,
modifiers: u32, modifiers: u32,
release: bool,
command_args: []const []const u8, command_args: []const []const u8,
) !Self { ) !Self {
const owned_args = try allocator.alloc([]u8, command_args.len); const owned_args = try allocator.alloc([]u8, command_args.len);
@ -40,6 +44,7 @@ pub fn init(
return Self{ return Self{
.keysym = keysym, .keysym = keysym,
.modifiers = modifiers, .modifiers = modifiers,
.release = release,
.command_args = owned_args, .command_args = owned_args,
}; };
} }

View file

@ -262,10 +262,10 @@ pub fn handleViewUnmap(self: *Self, view: *View) void {
/// Handle any user-defined mapping for the passed keysym and modifiers /// Handle any user-defined mapping for the passed keysym and modifiers
/// Returns true if the key was handled /// Returns true if the key was handled
pub fn handleMapping(self: *Self, keysym: c.xkb_keysym_t, modifiers: u32) bool { pub fn handleMapping(self: *Self, keysym: c.xkb_keysym_t, modifiers: u32, released: bool) bool {
const modes = &self.input_manager.server.config.modes; const modes = &self.input_manager.server.config.modes;
for (modes.items[self.mode_id].mappings.items) |mapping| { for (modes.items[self.mode_id].mappings.items) |mapping| {
if (modifiers == mapping.modifiers and keysym == mapping.keysym) { if (modifiers == mapping.modifiers and keysym == mapping.keysym and released == mapping.release) {
// Execute the bound command // Execute the bound command
const args = mapping.command_args; const args = mapping.command_args;
var out: ?[]const u8 = null; var out: ?[]const u8 = null;

View file

@ -50,20 +50,23 @@ pub fn map(
args: []const []const u8, args: []const []const u8,
out: *?[]const u8, out: *?[]const u8,
) Error!void { ) Error!void {
if (args.len < 5) return Error.NotEnoughArguments; const optionals = parseOptionalArgs(args[1..]);
// offset caused by optional arguments
const offset = optionals.i;
if (args.len - offset < 5) return Error.NotEnoughArguments;
const mode_id = try modeNameToId(allocator, seat, args[1], out); const mode_id = try modeNameToId(allocator, seat, args[1 + offset], out);
const modifiers = try parseModifiers(allocator, args[2], out); const modifiers = try parseModifiers(allocator, args[2 + offset], out);
// Parse the keysym // Parse the keysym
const keysym_name = try std.cstr.addNullByte(allocator, args[3]); const keysym_name = try std.cstr.addNullByte(allocator, args[3 + offset]);
defer allocator.free(keysym_name); defer allocator.free(keysym_name);
const keysym = c.xkb_keysym_from_name(keysym_name, .XKB_KEYSYM_CASE_INSENSITIVE); const keysym = c.xkb_keysym_from_name(keysym_name, .XKB_KEYSYM_CASE_INSENSITIVE);
if (keysym == c.XKB_KEY_NoSymbol) { if (keysym == c.XKB_KEY_NoSymbol) {
out.* = try std.fmt.allocPrint( out.* = try std.fmt.allocPrint(
allocator, allocator,
"invalid keysym '{}'", "invalid keysym '{}'",
.{args[3]}, .{args[3 + offset]},
); );
return Error.Other; return Error.Other;
} }
@ -71,17 +74,17 @@ pub fn map(
// Check if the mapping already exists // Check if the mapping already exists
const mode_mappings = &seat.input_manager.server.config.modes.items[mode_id].mappings; const mode_mappings = &seat.input_manager.server.config.modes.items[mode_id].mappings;
for (mode_mappings.items) |existant_mapping| { for (mode_mappings.items) |existant_mapping| {
if (existant_mapping.modifiers == modifiers and existant_mapping.keysym == keysym) { if (existant_mapping.modifiers == modifiers and existant_mapping.keysym == keysym and existant_mapping.release == optionals.release) {
out.* = try std.fmt.allocPrint( out.* = try std.fmt.allocPrint(
allocator, allocator,
"a mapping for modifiers '{}' and keysym '{}' already exists", "a mapping for modifiers '{}' and keysym '{}' already exists",
.{ args[2], args[3] }, .{ args[2 + offset], args[3 + offset] },
); );
return Error.Other; return Error.Other;
} }
} }
try mode_mappings.append(try Mapping.init(util.gpa, keysym, modifiers, args[4..])); try mode_mappings.append(try Mapping.init(util.gpa, keysym, modifiers, optionals.release, args[4 + offset ..]));
} }
/// Create a new pointer mapping for a given mode /// Create a new pointer mapping for a given mode
@ -176,3 +179,35 @@ fn parseModifiers(allocator: *std.mem.Allocator, modifiers_str: []const u8, out:
} }
return modifiers; return modifiers;
} }
const OptionalArgsContainer = struct {
i: usize,
release: bool,
};
/// 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
/// Errors cant occur because it returns as soon as it gets an unknown argument
fn parseOptionalArgs(args: []const []const u8) OptionalArgsContainer {
// Set to defaults
var parsed = OptionalArgsContainer{
// i is the number of arguments consumed
.i = 0,
.release = false,
};
var i: usize = 0;
for (args) |arg| {
if (std.mem.eql(u8, arg, "-release")) {
parsed.release = true;
i += 1;
} else {
// Break if the arg is not an option
parsed.i = i;
break;
}
}
return parsed;
}