diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index c8fbf901..31172667 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -19,7 +19,7 @@ use { acceleration::AccelProfile, capability::Capability, clickmethod::ClickMethod, }, keyboard::{ - Keymap, + Group, Keymap, mods::{Modifiers, RELEASE}, syms::KeySym, }, @@ -1385,6 +1385,23 @@ impl ConfigClient { keymap } + pub fn keymap_from_names( + &self, + rules: Option<&str>, + model: Option<&str>, + groups: Option<&[Group<'_>]>, + options: Option<&[&str]>, + ) -> Keymap { + let res = self.send_with_response(&ClientMessage::KeymapFromNames { + rules, + model, + groups: groups.map(|v| v.to_vec()), + options: options.map(|v| v.to_vec()), + }); + get_response!(res, Keymap(0), KeymapFromNames { keymap }); + keymap + } + pub fn set_ei_socket_enabled(&self, enabled: bool) { self.send(&ClientMessage::SetEiSocketEnabled { enabled }) } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index f9e1889c..1b89c8b9 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -7,7 +7,7 @@ use { FocusFollowsMouseMode, InputDevice, LayerDirection, Seat, SwitchEvent, Timeline, acceleration::AccelProfile, capability::Capability, clickmethod::ClickMethod, }, - keyboard::{Keymap, mods::Modifiers, syms::KeySym}, + keyboard::{Group, Keymap, mods::Modifiers, syms::KeySym}, logging::LogLevel, theme::{BarPosition, Color, colors::Colorable, sized::Resizable}, timer::Timer, @@ -820,6 +820,12 @@ pub enum ClientMessage<'a> { connector: Connector, use_native_gamut: bool, }, + KeymapFromNames { + rules: Option<&'a str>, + model: Option<&'a str>, + groups: Option>>, + options: Option>, + }, } #[derive(Serialize, Deserialize, Debug)] @@ -1067,6 +1073,9 @@ pub enum Response { GetBarPosition { position: BarPosition, }, + KeymapFromNames { + keymap: Keymap, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/keyboard/mod.rs b/jay-config/src/keyboard/mod.rs index 8630926c..95a51b1f 100644 --- a/jay-config/src/keyboard/mod.rs +++ b/jay-config/src/keyboard/mod.rs @@ -70,6 +70,15 @@ impl Keymap { } } +/// An RMLVO group consisting of a layout and a variant. +#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct Group<'a> { + /// The layout of the group. + pub layout: &'a str, + /// The variant of the group. Can be an empty string. + pub variant: &'a str, +} + /// Parses a keymap. /// /// The returned keymap can later be used to set the keymap of a seat. If the keymap cannot @@ -109,3 +118,39 @@ impl Keymap { pub fn parse_keymap(keymap: &str) -> Keymap { get!(Keymap::INVALID).parse_keymap(keymap) } + +/// Creates a keymap from RMLVO names. +/// +/// If a parameter is not given, a value from the environment or a default is used: +/// +/// | name | default | +/// | ---------------------- | ---------------------- | +/// | `XKB_DEFAULT_RULES` | `evdev` | +/// | `XKB_DEFAULT_MODEL` | `pc105` | +/// | `XKB_DEFAULT_LAYOUT` | `us` | +/// | `XKB_DEFAULT_VARIANTS` | | +/// | `XKB_DEFAULT_OPTIONS` | | +/// +/// `XKB_DEFAULT_LAYOUT` and `XKB_DEFAULT_VARIANTS` are parsed into the `groups` parameter like this example: +/// ``` +/// XKB_DEFAULT_LAYOUT = "us,il,ru,de,jp" +/// XKB_DEFAULT_VARIANTS = ",,phonetic,neo" +/// ``` +/// produces: +/// ``` +/// [ +/// Group { layout: "us", variant: "" }, +/// Group { layout: "il", variant: "" }, +/// Group { layout: "ru", variant: "phonetic" }, +/// Group { layout: "de", variant: "neo" }, +/// Group { layout: "jp", variant: "" }, +/// ] +/// ``` +pub fn keymap_from_names( + rules: Option<&str>, + model: Option<&str>, + groups: Option<&[Group<'_>]>, + options: Option<&[&str]>, +) -> Keymap { + get!(Keymap::INVALID).keymap_from_names(rules, model, groups, options) +} diff --git a/src/config/handler.rs b/src/config/handler.rs index 639b8cfc..4625fe35 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -65,7 +65,7 @@ use { CLICK_METHOD_BUTTON_AREAS, CLICK_METHOD_CLICKFINGER, CLICK_METHOD_NONE, ClickMethod, }, }, - keyboard::{Keymap, mods::Modifiers, syms::KeySym}, + keyboard::{Group, Keymap, mods::Modifiers, syms::KeySym}, logging::LogLevel, theme::{BarPosition, colors::Colorable, sized::Resizable}, timer::Timer as JayTimer, @@ -316,6 +316,39 @@ impl ConfigProxyHandler { res } + fn handle_keymap_from_names( + &self, + rules: Option<&str>, + model: Option<&str>, + groups: Option>>, + options: Option>, + ) -> Result<(), CphError> { + let kbvm_groups = groups.map(|groups| { + groups + .iter() + .map(|g| kbvm::xkb::rmlvo::Group { + layout: g.layout, + variant: g.variant, + }) + .collect::>() + }); + let (keymap, res) = match self.state.kb_ctx.keymap_from_names( + rules, + model, + kbvm_groups.as_deref(), + options.as_deref(), + ) { + Ok(keymap) => { + let id = Keymap(self.id()); + self.keymaps.set(id, keymap); + (id, Ok(())) + } + Err(e) => (Keymap::INVALID, Err(CphError::ParseKeymapError(e))), + }; + self.respond(Response::KeymapFromNames { keymap }); + res + } + fn handle_get_connectors( &self, dev: Option, @@ -3332,6 +3365,14 @@ impl ConfigProxyHandler { } => self .handle_connector_set_use_native_gamut(connector, use_native_gamut) .wrn("connector_set_use_native_gamut")?, + ClientMessage::KeymapFromNames { + rules, + model, + groups, + options, + } => self + .handle_keymap_from_names(rules, model, groups, options) + .wrn("keymap_from_names")?, } Ok(()) } diff --git a/src/kbvm.rs b/src/kbvm.rs index 969a8846..1b751711 100644 --- a/src/kbvm.rs +++ b/src/kbvm.rs @@ -92,6 +92,25 @@ impl KbvmContext { .ctx .keymap_from_bytes(WriteToLog, None, keymap) .map_err(KbvmError::CouldNotParseKeymap)?; + let id = KbvmMapId(*blake3::hash(keymap).as_bytes()); + self.create_keymap(id, map) + } + + pub fn keymap_from_names( + &self, + rules: Option<&str>, + model: Option<&str>, + groups: Option<&[xkb::rmlvo::Group<'_>]>, + options: Option<&[&str]>, + ) -> Result, KbvmError> { + let map = self + .ctx + .keymap_from_names(WriteToLog, rules, model, groups, options); + let id = KbvmMapId(*blake3::hash(map.format().to_string().as_bytes()).as_bytes()); + self.create_keymap(id, map) + } + + fn create_keymap(&self, id: KbvmMapId, map: Keymap) -> Result, KbvmError> { let mut has_indicators = false; let mut num_lock = None; let mut caps_lock = None; @@ -111,7 +130,7 @@ impl KbvmContext { } let builder = map.to_builder(); Ok(Rc::new(KbvmMap { - id: KbvmMapId(*blake3::hash(keymap).as_bytes()), + id, state_machine: builder.build_state_machine(), map: create_keymap_memfd(&map, false).map_err(KbvmError::KeymapMemfd)?, xwayland_map: create_keymap_memfd(&map, true).map_err(KbvmError::KeymapMemfd)?,