From 927dceb071f4d068ea271a3d97c3ece9db0da3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Donk=C3=B3?= Date: Sat, 12 Aug 2023 21:30:55 +0200 Subject: [PATCH] keyboard: add the ability to load layout from file --- completions/bash/riverctl | 1 + completions/fish/riverctl.fish | 1 + completions/zsh/_riverctl | 1 + doc/riverctl.1.scd | 6 ++++++ river/command.zig | 5 +++++ river/command/keyboard.zig | 36 ++++++++++++++++++++++++++++++++++ 6 files changed, 50 insertions(+) diff --git a/completions/bash/riverctl b/completions/bash/riverctl index c756321..ccee6a0 100644 --- a/completions/bash/riverctl +++ b/completions/bash/riverctl @@ -9,6 +9,7 @@ function __riverctl_completion () keyboard-group-add \ keyboard-group-remove \ keyboard-layout \ + keyboard-layout-file \ exit \ focus-output \ focus-view \ diff --git a/completions/fish/riverctl.fish b/completions/fish/riverctl.fish index 9b321f3..7accc4d 100644 --- a/completions/fish/riverctl.fish +++ b/completions/fish/riverctl.fish @@ -77,6 +77,7 @@ complete -c riverctl -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-dest complete -c riverctl -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-add' -d 'Add a keyboard to a keyboard group' complete -c riverctl -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-remove' -d 'Remove a keyboard from a keyboard group' complete -c riverctl -n '__fish_riverctl_complete_arg 1' -a 'keyboard-layout' -d 'Set the keyboard layout' +complete -c riverctl -n '__fish_riverctl_complete_arg 1' -a 'keyboard-layout-file' -d 'Set the keyboard layout from a file.' # Subcommands complete -c riverctl -n '__fish_seen_subcommand_from focus-output send-to-output' -n '__fish_riverctl_complete_arg 2' -a 'next previous' diff --git a/completions/zsh/_riverctl b/completions/zsh/_riverctl index 6467fbc..0c90bf9 100644 --- a/completions/zsh/_riverctl +++ b/completions/zsh/_riverctl @@ -67,6 +67,7 @@ _riverctl_commands() 'keyboard-group-add:Add a keyboard to a keyboard group' 'keyboard-group-remove:Remove a keyboard from a keyboard group' 'keyboard-layout:Set the keyboard layout' + 'keyboard-layout-file:Set the keyboard layout from a file' # Input 'input:Configure input devices' 'list-inputs:List all input devices' diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index b43678b..5553306 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -424,6 +424,12 @@ matches everything while _\*\*_ and the empty string are invalid. "grp:ctrl_space_toggle"). See *xkeyboard-config*(7) for possible values and more information. +*keyboard-layout-file* _path_ + Set the XKB layout for all keyboards from an XKB keymap file at the provided + path. Documentation for the XKB keymap file format can be found at the + following URL: + https://xkbcommon.org/doc/current/keymap-text-format-v1.html + *keyboard-group-create* _group_name_ Create a keyboard group. A keyboard group collects multiple keyboards in a single logical keyboard. This means that all state, like the active diff --git a/river/command.zig b/river/command.zig index dfebe9b..4a2a647 100644 --- a/river/command.zig +++ b/river/command.zig @@ -62,6 +62,7 @@ const command_impls = std.ComptimeStringMap( .{ "keyboard-group-destroy", @import("command/keyboard_group.zig").keyboardGroupDestroy }, .{ "keyboard-group-remove", @import("command/keyboard_group.zig").keyboardGroupRemove }, .{ "keyboard-layout", @import("command/keyboard.zig").keyboardLayout }, + .{ "keyboard-layout-file", @import("command/keyboard.zig").keyboardLayoutFile }, .{ "list-input-configs", @import("command/input.zig").listInputConfigs}, .{ "list-inputs", @import("command/input.zig").listInputs }, .{ "list-rules", @import("command/rule.zig").listRules}, @@ -112,6 +113,8 @@ pub const Error = error{ InvalidOrientation, InvalidRgba, InvalidValue, + CannotReadFile, + CannotParseFile, UnknownOption, ConflictingOptions, OutOfMemory, @@ -155,6 +158,8 @@ pub fn errToMsg(err: Error) [:0]const u8 { Error.InvalidOrientation => "invalid orientation. Must be 'horizontal', or 'vertical'", Error.InvalidRgba => "invalid color format, must be hexadecimal 0xRRGGBB or 0xRRGGBBAA", Error.InvalidValue => "invalid value", + Error.CannotReadFile => "cannot read file", + Error.CannotParseFile => "cannot parse file", Error.OutOfMemory => "out of memory", Error.Other => unreachable, }; diff --git a/river/command/keyboard.zig b/river/command/keyboard.zig index 93f5cb9..c09db1f 100644 --- a/river/command/keyboard.zig +++ b/river/command/keyboard.zig @@ -58,6 +58,42 @@ pub fn keyboardLayout( ) orelse return error.InvalidValue; defer new_keymap.unref(); + applyLayout(new_keymap); +} + +pub fn keyboardLayoutFile( + _: *Seat, + args: []const [:0]const u8, + _: *?[]const u8, +) Error!void { + if (args.len < 2) return Error.NotEnoughArguments; + if (args.len > 2) return Error.TooManyArguments; + + const file = std.fs.cwd().openFile(args[1], .{}) catch return error.CannotReadFile; + defer file.close(); + + // 1 GiB is arbitrarily chosen as an exceedingly large but not infinite upper bound. + const file_bytes = file.readToEndAlloc(util.gpa, 1024 * 1024 * 1024) catch |err| { + switch (err) { + error.FileTooBig, error.OutOfMemory => return error.OutOfMemory, + else => return error.CannotReadFile, + } + }; + defer util.gpa.free(file_bytes); + + const new_keymap = xkb.Keymap.newFromBuffer( + server.config.xkb_context, + file_bytes.ptr, + file_bytes.len, + .text_v1, + .no_flags, + ) orelse return error.CannotParseFile; + defer new_keymap.unref(); + + applyLayout(new_keymap); +} + +fn applyLayout(new_keymap: *xkb.Keymap) void { server.config.keymap.unref(); server.config.keymap = new_keymap.ref();