git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitd24f26123ce82131ce62ca001dc463f88a8e2b22
parent193f8f8df2
authorMarten Ringwelski <[email protected]>
date2020-09-15 13:46
Implement map -release

 doc/riverctl.1.scd    |  6 ++++--
 river/Keyboard.zig    | 36 +++++++++++++++++++-----------------
 river/Mapping.zig     |  5 +++++
 river/Seat.zig        |  4 ++--
 river/command/map.zig | 51 +++++++++++++++++++++++++++++++++++++++++++--------
 5 files changed, 73 insertions(+), 29 deletions(-)

diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index f233ad1..8dbb5bb 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -123,9 +123,11 @@ that tag 1 through 9 are visible.
 	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.
 
-*map* _mode_ _modifiers_ _key_ _command_
+*map* [-release] _mode_ _modifiers_ _key_ _command_
 	_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:
 
 	- Shift
diff --git a/river/Keyboard.zig b/river/Keyboard.zig
index f5c065f..6044257 100644
--- a/river/Keyboard.zig
+++ b/river/Keyboard.zig
@@ -103,29 +103,31 @@ fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     var handled = false;
     // TODO: These modifiers aren't properly handled, see sway's code
     const modifiers = c.wlr_keyboard_get_modifiers(wlr_keyboard);
-    if (event.state == .WLR_KEY_PRESSED) {
-        var i: usize = 0;
-        while (i < translated_keysyms_len) : (i += 1) {
-            if (self.handleBuiltinMapping(translated_keysyms.?[i])) {
+    const released = event.state == .WLR_KEY_RELEASED;
+
+    var i: usize = 0;
+    while (i < translated_keysyms_len) : (i += 1) {
+        // Handle builtin mapping only when keys are pressed
+        if (!released and self.handleBuiltinMapping(translated_keysyms.?[i])) {
+            handled = true;
+            break;
+        } else if (self.seat.handleMapping(translated_keysyms.?[i], modifiers, released)) {
+            handled = true;
+            break;
+        }
+    }
+    if (!handled) {
+        i = 0;
+        while (i < raw_keysyms_len) : (i += 1) {
+            // Handle builtin mapping only when keys are pressed
+            if (!released and self.handleBuiltinMapping(raw_keysyms.?[i])) {
                 handled = true;
                 break;
-            } else if (self.seat.handleMapping(translated_keysyms.?[i], modifiers)) {
+            } else if (self.seat.handleMapping(raw_keysyms.?[i], modifiers, released)) {
                 handled = true;
                 break;
             }
         }
-        if (!handled) {
-            i = 0;
-            while (i < raw_keysyms_len) : (i += 1) {
-                if (self.handleBuiltinMapping(raw_keysyms.?[i])) {
-                    handled = true;
-                    break;
-                } else if (self.seat.handleMapping(raw_keysyms.?[i], modifiers)) {
-                    handled = true;
-                    break;
-                }
-            }
-        }
     }
 
     if (!handled) {
diff --git a/river/Mapping.zig b/river/Mapping.zig
index 3d80624..c253693 100644
--- a/river/Mapping.zig
+++ b/river/Mapping.zig
@@ -25,10 +25,14 @@ keysym: c.xkb_keysym_t,
 modifiers: u32,
 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(
     allocator: *std.mem.Allocator,
     keysym: c.xkb_keysym_t,
     modifiers: u32,
+    release: bool,
     command_args: []const []const u8,
 ) !Self {
     const owned_args = try allocator.alloc([]u8, command_args.len);
@@ -40,6 +44,7 @@ pub fn init(
     return Self{
         .keysym = keysym,
         .modifiers = modifiers,
+        .release = release,
         .command_args = owned_args,
     };
 }
diff --git a/river/Seat.zig b/river/Seat.zig
index 2527466..59f9eab 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -262,10 +262,10 @@ pub fn handleViewUnmap(self: *Self, view: *View) void {
 
 /// Handle any user-defined mapping for the passed keysym and modifiers
 /// 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;
     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
             const args = mapping.command_args;
             var out: ?[]const u8 = null;
diff --git a/river/command/map.zig b/river/command/map.zig
index d21cb33..9d5c137 100644
--- a/river/command/map.zig
+++ b/river/command/map.zig
@@ -50,20 +50,23 @@ pub fn map(
     args: []const []const u8,
     out: *?[]const u8,
 ) 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 modifiers = try parseModifiers(allocator, args[2], out);
+    const mode_id = try modeNameToId(allocator, seat, args[1 + offset], out);
+    const modifiers = try parseModifiers(allocator, args[2 + offset], out);
 
     // 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);
     const keysym = c.xkb_keysym_from_name(keysym_name, .XKB_KEYSYM_CASE_INSENSITIVE);
     if (keysym == c.XKB_KEY_NoSymbol) {
         out.* = try std.fmt.allocPrint(
             allocator,
             "invalid keysym '{}'",
-            .{args[3]},
+            .{args[3 + offset]},
         );
         return Error.Other;
     }
@@ -71,17 +74,17 @@ pub fn map(
     // Check if the mapping already exists
     const mode_mappings = &seat.input_manager.server.config.modes.items[mode_id].mappings;
     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(
                 allocator,
                 "a mapping for modifiers '{}' and keysym '{}' already exists",
-                .{ args[2], args[3] },
+                .{ args[2 + offset], args[3 + offset] },
             );
             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
@@ -176,3 +179,35 @@ fn parseModifiers(allocator: *std.mem.Allocator, modifiers_str: []const u8, out:
     }
     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;
+}