feat: libxkbcommon layouts

This commit is contained in:
galister
2024-06-05 19:54:11 +09:00
parent 65d62d79fe
commit 3a3cda7a45
10 changed files with 462 additions and 65 deletions

4
Cargo.lock generated
View File

@@ -4338,7 +4338,9 @@ dependencies = [
"vulkano-shaders", "vulkano-shaders",
"winit", "winit",
"wlx-capture", "wlx-capture",
"xcb",
"xdg", "xdg",
"xkbcommon",
] ]
[[package]] [[package]]
@@ -4379,6 +4381,7 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e75181b5a62b6eeaa72f303d3cef7dbb841e22885bf6d3e66fe23e88c55dc6" checksum = "02e75181b5a62b6eeaa72f303d3cef7dbb841e22885bf6d3e66fe23e88c55dc6"
dependencies = [ dependencies = [
"as-raw-xcb-connection",
"bitflags 1.3.2", "bitflags 1.3.2",
"libc", "libc",
"quick-xml 0.30.0", "quick-xml 0.30.0",
@@ -4412,6 +4415,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
dependencies = [ dependencies = [
"as-raw-xcb-connection",
"libc", "libc",
"memmap2 0.8.0", "memmap2 0.8.0",
"xkeysym", "xkeysym",

View File

@@ -58,13 +58,22 @@ winit = { version = "0.29.15", optional = true }
xdg = "2.5.2" xdg = "2.5.2"
log-panics = { version = "2.1.0", features = ["with-backtrace"] } log-panics = { version = "2.1.0", features = ["with-backtrace"] }
serde_json5 = "0.1.0" serde_json5 = "0.1.0"
xkbcommon = { version = "0.7.0" }
xcb = { version = "1.4.0", optional = true, features = ["as-raw-xcb-connection"] }
[features] [features]
default = ["openvr", "openxr", "osc", "x11", "wayland"] default = ["openvr", "openxr", "osc", "x11", "wayland"]
openvr = ["dep:ovr_overlay", "dep:json"] openvr = ["dep:ovr_overlay", "dep:json"]
openxr = ["dep:openxr", "dep:sysinfo"] openxr = ["dep:openxr", "dep:sysinfo"]
osc = ["dep:rosc"] osc = ["dep:rosc"]
x11 = ["wlx-capture/xshm"] x11 = [
wayland = ["wlx-capture/pipewire", "wlx-capture/wlr"] "dep:xcb",
"wlx-capture/xshm",
"xkbcommon/x11",
]
wayland = ["wlx-capture/pipewire", "wlx-capture/wlr", "xkbcommon/wayland"]
uidev = ["dep:winit"] uidev = ["dep:winit"]
no-dmabuf = [] no-dmabuf = []
xcb = ["dep:xcb"]
as-raw-xcb-connection = []

View File

@@ -11,6 +11,7 @@ use thiserror::Error;
use crate::{ use crate::{
config::AStrSetExt, config::AStrSetExt,
hid::{get_keymap_wl, get_keymap_x11},
overlays::{ overlays::{
anchor::create_anchor, anchor::create_anchor,
keyboard::create_keyboard, keyboard::create_keyboard,
@@ -63,10 +64,14 @@ where
let mut overlays = IdMap::new(); let mut overlays = IdMap::new();
let mut wl = create_wl_client(); let mut wl = create_wl_client();
let keymap;
app.screens.clear(); app.screens.clear();
let data = if let Some(wl) = wl.as_mut() { let data = if let Some(wl) = wl.as_mut() {
keymap = get_keymap_wl().ok();
crate::overlays::screen::create_screens_wayland(wl, app)? crate::overlays::screen::create_screens_wayland(wl, app)?
} else { } else {
keymap = get_keymap_x11().ok();
crate::overlays::screen::create_screens_x11(app)? crate::overlays::screen::create_screens_x11(app)?
}; };
@@ -100,7 +105,7 @@ where
watch.state.want_visible = true; watch.state.want_visible = true;
overlays.insert(watch.state.id, watch); overlays.insert(watch.state.id, watch);
let mut keyboard = create_keyboard(app)?; let mut keyboard = create_keyboard(app, keymap)?;
keyboard.state.show_hide = true; keyboard.state.show_hide = true;
keyboard.state.want_visible = false; keyboard.state.want_visible = false;
overlays.insert(keyboard.state.id, keyboard); overlays.insert(keyboard.state.id, keyboard);

View File

@@ -17,6 +17,7 @@ pub struct FontCache {
struct FontCollection { struct FontCollection {
fonts: Vec<Font>, fonts: Vec<Font>,
cp_map: IdMap<usize, usize>, cp_map: IdMap<usize, usize>,
zero_glyph: Rc<Glyph>,
} }
struct Font { struct Font {
@@ -96,6 +97,14 @@ impl FontCache {
FontCollection { FontCollection {
fonts: Vec::new(), fonts: Vec::new(),
cp_map: IdMap::new(), cp_map: IdMap::new(),
zero_glyph: Rc::new(Glyph {
tex: None,
top: 0.,
left: 0.,
width: 0.,
height: 0.,
advance: size as f32 / 3.,
}),
}, },
); );
} }
@@ -181,7 +190,7 @@ impl FontCache {
let Some(font) = &mut self.collections[size].fonts.get_mut(key) else { let Some(font) = &mut self.collections[size].fonts.get_mut(key) else {
log::warn!("No font found for codepoint: {}", cp); log::warn!("No font found for codepoint: {}", cp);
return Ok(self.collections[size].fonts[0].glyphs[0].clone()); return Ok(self.collections[size].zero_glyph.clone());
}; };
if let Some(glyph) = font.glyphs.get(cp) { if let Some(glyph) = font.glyphs.get(cp) {
@@ -189,18 +198,18 @@ impl FontCache {
} }
if font.face.load_char(cp, LoadFlag::DEFAULT).is_err() { if font.face.load_char(cp, LoadFlag::DEFAULT).is_err() {
return Ok(font.glyphs[0].clone()); return Ok(self.collections[size].zero_glyph.clone());
} }
let glyph = font.face.glyph(); let glyph = font.face.glyph();
if glyph.render_glyph(freetype::RenderMode::Normal).is_err() { if glyph.render_glyph(freetype::RenderMode::Normal).is_err() {
return Ok(font.glyphs[0].clone()); return Ok(self.collections[size].zero_glyph.clone());
} }
let bmp = glyph.bitmap(); let bmp = glyph.bitmap();
let buf = bmp.buffer().to_vec(); let buf = bmp.buffer().to_vec();
if buf.is_empty() { if buf.is_empty() {
return Ok(font.glyphs[0].clone()); return Ok(self.collections[size].zero_glyph.clone());
} }
let metrics = glyph.metrics(); let metrics = glyph.metrics();
@@ -209,7 +218,7 @@ impl FontCache {
Ok(PixelMode::Gray) => Format::R8_UNORM, Ok(PixelMode::Gray) => Format::R8_UNORM,
Ok(PixelMode::Gray2) => Format::R16_SFLOAT, Ok(PixelMode::Gray2) => Format::R16_SFLOAT,
Ok(PixelMode::Gray4) => Format::R32_SFLOAT, Ok(PixelMode::Gray4) => Format::R32_SFLOAT,
_ => return Ok(font.glyphs[0].clone()), _ => return Ok(self.collections[size].zero_glyph.clone()),
}; };
let mut cmd_buffer = graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?; let mut cmd_buffer = graphics.create_command_buffer(CommandBufferUsage::OneTimeSubmit)?;

View File

@@ -171,6 +171,7 @@ impl<D, S> CanvasBuilder<D, S> {
y: f32, y: f32,
w: f32, w: f32,
h: f32, h: f32,
cap_type: KeyCapType,
label: &[String], label: &[String],
) -> &mut Control<D, S> { ) -> &mut Control<D, S> {
let idx = self.canvas.controls.len(); let idx = self.canvas.controls.len();
@@ -184,27 +185,88 @@ impl<D, S> CanvasBuilder<D, S> {
..Control::new() ..Control::new()
}); });
for (i, item) in label.iter().enumerate().take(label.len().min(2)) { let renders = match cap_type {
KeyCapType::Regular => {
let render: ControlRenderer<D, S> = Control::render_text_centered;
let rect = Rect {
x,
y,
w,
h: h - self.font_size as f32,
};
vec![(render, rect, 1f32)]
}
KeyCapType::RegularAltGr => {
let render: ControlRenderer<D, S> = Control::render_text;
let rect0 = Rect {
x: x + 12.,
y: y + (self.font_size as f32) + 12.,
w,
h,
};
let rect1 = Rect {
x: x + w * 0.5 + 12.,
y: y + h - (self.font_size as f32) + 8.,
w,
h,
};
vec![(render, rect0, 1.0), (render, rect1, 0.8)]
}
KeyCapType::Reversed => {
let render: ControlRenderer<D, S> = Control::render_text_centered;
let rect0 = Rect {
x,
y: y + 2.0,
w,
h: h * 0.5,
};
let rect1 = Rect {
x,
y: y + h * 0.5 + 2.0,
w,
h: h * 0.5,
};
vec![(render, rect1, 1.0), (render, rect0, 0.8)]
}
KeyCapType::ReversedAltGr => {
let render: ControlRenderer<D, S> = Control::render_text;
let rect0 = Rect {
x: x + 12.,
y: y + (self.font_size as f32) + 8.,
w,
h,
};
let rect1 = Rect {
x: x + 12.,
y: y + h - (self.font_size as f32) + 4.,
w,
h,
};
let rect2 = Rect {
x: x + w * 0.5 + 8.,
y: y + h - (self.font_size as f32) + 4.,
w,
h,
};
vec![
(render, rect1, 1.0),
(render, rect0, 0.8),
(render, rect2, 0.8),
]
}
};
for (idx, (render, rect, alpha)) in renders.into_iter().enumerate() {
if idx >= label.len() {
break;
}
self.canvas.controls.push(Control { self.canvas.controls.push(Control {
rect: if i == 0 { rect,
Rect { text: Arc::from(label[idx].as_str()),
x: x + 4., fg_color: self.fg_color * alpha,
y: y + (self.font_size as f32) + 4.,
w,
h,
}
} else {
Rect {
x: x + w * 0.5,
y: y + h - (self.font_size as f32) + 4.,
w,
h,
}
},
text: Arc::from(item.as_str()),
fg_color: self.fg_color,
size: self.font_size, size: self.font_size,
on_render_fg: Some(Control::render_text), on_render_fg: Some(render),
..Control::new() ..Control::new()
}); });
} }
@@ -540,6 +602,17 @@ impl<D, S> OverlayBackend for Canvas<D, S> {
fn set_interaction(&mut self, _interaction: Box<dyn InteractionHandler>) {} fn set_interaction(&mut self, _interaction: Box<dyn InteractionHandler>) {}
} }
pub type ControlRenderer<D, S> =
fn(&Control<D, S>, &CanvasData<D>, &mut AppState, &mut WlxCommandBuffer) -> anyhow::Result<()>;
pub type ControlRendererHl<D, S> = fn(
&Control<D, S>,
&CanvasData<D>,
&mut AppState,
&mut WlxCommandBuffer,
Vec4,
) -> anyhow::Result<()>;
pub struct Control<D, S> { pub struct Control<D, S> {
pub state: Option<S>, pub state: Option<S>,
rect: Rect, rect: Rect,
@@ -555,15 +628,9 @@ pub struct Control<D, S> {
pub on_scroll: Option<fn(&mut Self, &mut D, &mut AppState, f32)>, pub on_scroll: Option<fn(&mut Self, &mut D, &mut AppState, f32)>,
pub test_highlight: Option<fn(&Self, &mut D, &mut AppState) -> Option<Vec4>>, pub test_highlight: Option<fn(&Self, &mut D, &mut AppState) -> Option<Vec4>>,
on_render_bg: Option< on_render_bg: Option<ControlRenderer<D, S>>,
fn(&Self, &CanvasData<D>, &mut AppState, &mut WlxCommandBuffer) -> anyhow::Result<()>, on_render_hl: Option<ControlRendererHl<D, S>>,
>, on_render_fg: Option<ControlRenderer<D, S>>,
on_render_hl: Option<
fn(&Self, &CanvasData<D>, &mut AppState, &mut WlxCommandBuffer, Vec4) -> anyhow::Result<()>,
>,
on_render_fg: Option<
fn(&Self, &CanvasData<D>, &mut AppState, &mut WlxCommandBuffer) -> anyhow::Result<()>,
>,
} }
impl<D, S> Control<D, S> { impl<D, S> Control<D, S> {
@@ -762,3 +829,18 @@ impl<D, S> Control<D, S> {
Ok(()) Ok(())
} }
} }
pub enum KeyCapType {
/// Label is in center of keycap
Regular,
/// Label on the top
/// AltGr symbol on bottom
RegularAltGr,
/// Primary symbol on bottom
/// Shift symbol on top
Reversed,
/// Primary symbol on bottom-left
/// Shift symbol on top-left
/// AltGr symbol on bottom-right
ReversedAltGr,
}

View File

@@ -10,6 +10,13 @@ use once_cell::sync::Lazy;
use std::mem::transmute; use std::mem::transmute;
use std::{fs::File, sync::atomic::AtomicBool}; use std::{fs::File, sync::atomic::AtomicBool};
use strum::{EnumIter, EnumString, IntoEnumIterator}; use strum::{EnumIter, EnumString, IntoEnumIterator};
use xkbcommon::xkb;
#[cfg(feature = "wayland")]
mod wayland;
#[cfg(feature = "x11")]
mod x11;
pub static USE_UINPUT: AtomicBool = AtomicBool::new(true); pub static USE_UINPUT: AtomicBool = AtomicBool::new(true);
@@ -487,3 +494,100 @@ pub static MODS_TO_KEYS: Lazy<IdMap<KeyModifier, Vec<VirtualKey>>> = Lazy::new(|
META => vec![VirtualKey::Meta], META => vec![VirtualKey::Meta],
} }
}); });
pub enum KeyType {
Symbol,
NumPad,
Other,
}
macro_rules! key_between {
($key:expr, $start:expr, $end:expr) => {
$key as u32 >= $start as u32 && $key as u32 <= $end as u32
};
}
macro_rules! key_is {
($key:expr, $val:expr) => {
$key as u32 == $val as u32
};
}
pub fn get_key_type(key: VirtualKey) -> KeyType {
if key_between!(key, VirtualKey::N1, VirtualKey::Plus)
|| key_between!(key, VirtualKey::Q, VirtualKey::Oem6)
|| key_between!(key, VirtualKey::A, VirtualKey::Oem3)
|| key_between!(key, VirtualKey::Oem5, VirtualKey::Oem2)
|| key_is!(key, VirtualKey::Oem102)
{
KeyType::Symbol
} else if key_between!(key, VirtualKey::KP_7, VirtualKey::KP_0)
&& !key_is!(key, VirtualKey::KP_Subtract)
&& !key_is!(key, VirtualKey::KP_Add)
{
KeyType::NumPad
} else {
KeyType::Other
}
}
pub struct XkbKeymap {
pub context: xkb::Context,
pub keymap: xkb::Keymap,
}
impl XkbKeymap {
pub fn label_for_key(&self, key: VirtualKey, modifier: KeyModifier) -> String {
let mut state = xkb::State::new(&self.keymap);
if modifier > 0 {
if let Some(mod_key) = MODS_TO_KEYS.get(modifier) {
state.update_key(
xkb::Keycode::from(mod_key[0] as u32),
xkb::KeyDirection::Down,
);
}
}
state.key_get_utf8(xkb::Keycode::from(key as u32))
}
pub fn has_altgr(&self) -> bool {
let state0 = xkb::State::new(&self.keymap);
let mut state1 = xkb::State::new(&self.keymap);
state1.update_key(
xkb::Keycode::from(VirtualKey::Meta as u32),
xkb::KeyDirection::Down,
);
for key in [
VirtualKey::N0,
VirtualKey::N1,
VirtualKey::N2,
VirtualKey::N3,
VirtualKey::N4,
VirtualKey::N5,
VirtualKey::N6,
VirtualKey::N7,
VirtualKey::N8,
VirtualKey::N9,
] {
let sym0 = state0.key_get_one_sym(xkb::Keycode::from(key as u32));
let sym1 = state1.key_get_one_sym(xkb::Keycode::from(key as u32));
if sym0 != sym1 {
return true;
}
}
false
}
}
#[cfg(feature = "wayland")]
pub use wayland::get_keymap_wl;
#[cfg(not(feature = "wayland"))]
pub fn get_keymap_wl() -> anyhow::Result<XkbKeymap> {}
#[cfg(feature = "x11")]
pub use x11::get_keymap_x11;
#[cfg(not(feature = "x11"))]
pub fn get_keymap_x11() -> anyhow::Result<XkbKeymap> {}

142
src/hid/wayland.rs Normal file
View File

@@ -0,0 +1,142 @@
use wlx_capture::wayland::wayland_client::{
globals::{registry_queue_init, GlobalListContents},
protocol::{
wl_keyboard::{self, WlKeyboard},
wl_registry::WlRegistry,
wl_seat::{self, Capability, WlSeat},
},
Connection, Dispatch, Proxy, QueueHandle,
};
use xkbcommon::xkb;
use super::XkbKeymap;
struct WlKeymapHandler {
seat: WlSeat,
keyboard: Option<WlKeyboard>,
keymap: Option<XkbKeymap>,
}
impl Drop for WlKeymapHandler {
fn drop(&mut self) {
if let Some(keyboard) = &self.keyboard {
keyboard.release();
}
self.seat.release();
}
}
pub fn get_keymap_wl() -> anyhow::Result<XkbKeymap> {
let connection = Connection::connect_to_env()?;
let (globals, mut queue) = registry_queue_init::<WlKeymapHandler>(&connection)?;
let qh = queue.handle();
let seat: WlSeat = globals
.bind(&qh, 4..=9, ())
.expect(WlSeat::interface().name);
let mut me = WlKeymapHandler {
seat,
keyboard: None,
keymap: None,
};
// this gets us the wl_seat
let _ = queue.blocking_dispatch(&mut me);
// this gets us the wl_keyboard
let _ = queue.blocking_dispatch(&mut me);
if let Some(keymap) = me.keymap.take() {
Ok(keymap)
} else {
Err(anyhow::anyhow!("Could not load keymap"))
}
}
impl Dispatch<WlRegistry, GlobalListContents> for WlKeymapHandler {
fn event(
_state: &mut Self,
_proxy: &WlRegistry,
_event: <WlRegistry as Proxy>::Event,
_data: &GlobalListContents,
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
}
}
impl Dispatch<WlSeat, ()> for WlKeymapHandler {
fn event(
state: &mut Self,
proxy: &WlSeat,
event: <WlSeat as Proxy>::Event,
_data: &(),
_conn: &Connection,
qhandle: &QueueHandle<Self>,
) {
match event {
wl_seat::Event::Capabilities { capabilities } => {
let capability = capabilities
.into_result()
.unwrap_or(wl_seat::Capability::empty());
if capability.contains(Capability::Keyboard) {
state.keyboard = Some(proxy.get_keyboard(qhandle, ()));
}
}
wl_seat::Event::Name { name } => {
log::debug!("Using WlSeat: {}", name);
}
_ => {}
}
}
}
impl Dispatch<WlKeyboard, ()> for WlKeymapHandler {
fn event(
state: &mut Self,
_proxy: &WlKeyboard,
event: <WlKeyboard as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
match event {
wl_keyboard::Event::Keymap { format, fd, size } => {
let format = format
.into_result()
.unwrap_or(wl_keyboard::KeymapFormat::NoKeymap);
if matches!(format, wl_keyboard::KeymapFormat::XkbV1) {
let context = xkb::Context::new(xkb::CONTEXT_NO_DEFAULT_INCLUDES);
let maybe_keymap = unsafe {
xkb::Keymap::new_from_fd(
&context,
fd,
size as _,
xkb::KEYMAP_FORMAT_TEXT_V1,
xkb::KEYMAP_COMPILE_NO_FLAGS,
)
};
match maybe_keymap {
Ok(Some(keymap)) => {
state.keymap = Some(XkbKeymap { context, keymap });
}
Ok(None) => {
log::error!("Could not load keymap: no keymap");
log::error!("Default layout will be used.");
}
Err(err) => {
log::error!("Could not load keymap: {}", err);
log::error!("Default layout will be used.");
}
}
}
}
wl_keyboard::Event::RepeatInfo { rate, delay } => {
log::debug!("WlKeyboard RepeatInfo rate: {}, delay: {}", rate, delay);
}
_ => {}
}
}
}

16
src/hid/x11.rs Normal file
View File

@@ -0,0 +1,16 @@
use xkbcommon::xkb::{
self,
x11::{get_core_keyboard_device_id, keymap_new_from_device},
};
use super::XkbKeymap;
pub fn get_keymap_x11() -> anyhow::Result<XkbKeymap> {
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let (conn, _) = xcb::Connection::connect(None)?;
let device_id = get_core_keyboard_device_id(&conn);
let keymap = keymap_new_from_device(&context, &conn, device_id, xkb::KEYMAP_COMPILE_NO_FLAGS);
Ok(XkbKeymap { context, keymap })
}

View File

@@ -10,8 +10,11 @@ use crate::{
overlay::{OverlayData, OverlayState}, overlay::{OverlayData, OverlayState},
}, },
config::{self, ConfigType}, config::{self, ConfigType},
gui::{color_parse, CanvasBuilder, Control}, gui::{color_parse, CanvasBuilder, Control, KeyCapType},
hid::{KeyModifier, VirtualKey, ALT, CTRL, KEYS_TO_MODS, META, SHIFT, SUPER}, hid::{
get_key_type, KeyModifier, KeyType, VirtualKey, XkbKeymap, ALT, CTRL, KEYS_TO_MODS, META,
NUM_LOCK, SHIFT, SUPER,
},
state::AppState, state::AppState,
}; };
use glam::{vec2, vec3a, Affine2, Vec4}; use glam::{vec2, vec3a, Affine2, Vec4};
@@ -25,7 +28,10 @@ const AUTO_RELEASE_MODS: [KeyModifier; 5] = [SHIFT, CTRL, ALT, SUPER, META];
pub const KEYBOARD_NAME: &str = "kbd"; pub const KEYBOARD_NAME: &str = "kbd";
pub fn create_keyboard<O>(app: &AppState) -> anyhow::Result<OverlayData<O>> pub fn create_keyboard<O>(
app: &AppState,
keymap: Option<XkbKeymap>,
) -> anyhow::Result<OverlayData<O>>
where where
O: Default, O: Default,
{ {
@@ -53,6 +59,8 @@ where
canvas.font_size = 18; canvas.font_size = 18;
canvas.bg_color = color_parse("#202020").unwrap(); //safe canvas.bg_color = color_parse("#202020").unwrap(); //safe
let has_altgr = keymap.as_ref().map_or(false, |k| k.has_altgr());
let unit_size = size.x / LAYOUT.row_size; let unit_size = size.x / LAYOUT.row_size;
let h = unit_size - 2. * BUTTON_PADDING; let h = unit_size - 2. * BUTTON_PADDING;
@@ -66,8 +74,43 @@ where
let w = unit_size * my_size - 2. * BUTTON_PADDING; let w = unit_size * my_size - 2. * BUTTON_PADDING;
if let Some(key) = LAYOUT.main_layout[row][col].as_ref() { if let Some(key) = LAYOUT.main_layout[row][col].as_ref() {
let mut label = Vec::with_capacity(2);
let mut maybe_state: Option<KeyButtonData> = None; let mut maybe_state: Option<KeyButtonData> = None;
let mut cap_type = KeyCapType::Regular;
if let Ok(vk) = VirtualKey::from_str(key) { if let Ok(vk) = VirtualKey::from_str(key) {
if let Some(keymap) = keymap.as_ref() {
match get_key_type(vk) {
KeyType::Symbol => {
let label0 = keymap.label_for_key(vk, 0);
let label1 = keymap.label_for_key(vk, SHIFT);
if label0.chars().next().map_or(false, |f| f.is_alphabetic()) {
label.push(label1);
if has_altgr {
cap_type = KeyCapType::RegularAltGr;
label.push(keymap.label_for_key(vk, META));
} else {
cap_type = KeyCapType::Regular;
}
} else {
label.push(label0);
label.push(label1);
if has_altgr {
label.push(keymap.label_for_key(vk, META));
cap_type = KeyCapType::ReversedAltGr;
} else {
cap_type = KeyCapType::Reversed;
}
}
}
KeyType::NumPad => {
label.push(keymap.label_for_key(vk, NUM_LOCK));
}
KeyType::Other => {}
}
}
if let Some(mods) = KEYS_TO_MODS.get(vk) { if let Some(mods) = KEYS_TO_MODS.get(vk) {
maybe_state = Some(KeyButtonData::Modifier { maybe_state = Some(KeyButtonData::Modifier {
modifier: *mods, modifier: *mods,
@@ -97,8 +140,10 @@ where
} }
if let Some(state) = maybe_state { if let Some(state) = maybe_state {
let label = LAYOUT.label_for_key(key); if label.is_empty() {
let button = canvas.key_button(x, y, w, h, &label); label = LAYOUT.label_for_key(key);
}
let button = canvas.key_button(x, y, w, h, cap_type, &label);
button.state = Some(state); button.state = Some(state);
button.on_press = Some(key_press); button.on_press = Some(key_press);
button.on_release = Some(key_release); button.on_release = Some(key_release);

View File

@@ -26,7 +26,7 @@ key_sizes:
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1] - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1]
- [1.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.5, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1] - [1.5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.5, 0.5, 1, 1, 1, 0.5, 1, 1, 1, 1]
- [1.75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.25, 4, 1, 1, 1, 1] - [1.75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.25, 4, 1, 1, 1, 1]
- [2.25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.75, 1.5, 1, 1.5, 1, 1, 1, 1] - [1.25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2.75, 1.5, 1, 1.5, 1, 1, 1, 1]
- [1.25, 1.25, 1.25, 6.25, 1.25, 1.25, 1.25, 1.25, 0.5, 1, 1, 1, 0.5, 2, 1, 1] - [1.25, 1.25, 1.25, 6.25, 1.25, 1.25, 1.25, 1.25, 0.5, 1, 1, 1, 0.5, 2, 1, 1]
# The main (blue) layout of the keyboard. # The main (blue) layout of the keyboard.
@@ -40,7 +40,7 @@ main_layout:
- ["Oem3", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9", "N0", "Minus", "Plus", "BackSpace", ~, "Insert", "Home", "Prior", ~, "NumLock", "KP_Divide", "KP_Multiply", "KP_Subtract"] - ["Oem3", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9", "N0", "Minus", "Plus", "BackSpace", ~, "Insert", "Home", "Prior", ~, "NumLock", "KP_Divide", "KP_Multiply", "KP_Subtract"]
- ["Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Oem4", "Oem6", "Oem5", ~, "Delete", "End", "Next", ~, "KP_7", "KP_8", "KP_9", "KP_Add"] - ["Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Oem4", "Oem6", "Oem5", ~, "Delete", "End", "Next", ~, "KP_7", "KP_8", "KP_9", "KP_Add"]
- ["XF86Favorites", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Oem1", "Oem7", "Return", ~, "KP_4", "KP_5", "KP_6", ~] - ["XF86Favorites", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Oem1", "Oem7", "Return", ~, "KP_4", "KP_5", "KP_6", ~]
- ["LShift", "Z", "X", "C", "V", "B", "N", "M", "Comma", "Period", "Oem2", "RShift", ~, "Up", ~, "KP_1", "KP_2", "KP_3", "KP_Enter"] - ["LShift", "Oem102", "Z", "X", "C", "V", "B", "N", "M", "Comma", "Period", "Oem2", "RShift", ~, "Up", ~, "KP_1", "KP_2", "KP_3", "KP_Enter"]
- ["LCtrl", "LSuper", "LAlt", "Space", "Meta", "RSuper", "Menu", "RCtrl", ~, "Left", "Down", "Right", ~, "KP_0", "KP_Decimal", ~] - ["LCtrl", "LSuper", "LAlt", "Space", "Meta", "RSuper", "Menu", "RCtrl", ~, "Left", "Down", "Right", ~, "KP_0", "KP_Decimal", ~]
# Shell commands to be used in a layout. # Shell commands to be used in a layout.
@@ -62,16 +62,6 @@ macros:
# Value: Array of strings. 0th element is the upper row, 1st element is lower row. # Value: Array of strings. 0th element is the upper row, 1st element is lower row.
# For empty labels, use [] (do not use ~) # For empty labels, use [] (do not use ~)
labels: labels:
"N1": ["1", "!"]
"N2": ["2", "@"]
"N3": ["3", "#"]
"N4": ["4", "$"]
"N5": ["5", "%"]
"N6": ["6", "^"]
"N7": ["7", "&"]
"N8": ["8", "*"]
"N9": ["9", "("]
"N0": ["0", ")"]
"Escape": ["Esc"] "Escape": ["Esc"]
"Prior": ["PgUp"] "Prior": ["PgUp"]
"Next": ["PgDn"] "Next": ["PgDn"]
@@ -86,24 +76,15 @@ labels:
"RShift": ["Shift"] "RShift": ["Shift"]
"Insert": ["Ins"] "Insert": ["Ins"]
"Delete": ["Del"] "Delete": ["Del"]
"Minus": ["-", "_"]
"Plus": ["=", "+"]
"BackSpace": ["<<"] "BackSpace": ["<<"]
"Comma": [" ,", "<"]
"Period": [" .", ">"]
"Oem1": [" ;", ":"]
"Oem2": [" /", "?"]
"Oem3": ["`", "~"]
"Oem4": [" [", "{"]
"Oem5": [" \\", "|"]
"Oem6": [" ]", "}"]
"Oem7": [" '", "\""]
"Oem102": [" \\", "|"]
"KP_Divide": [" /"] "KP_Divide": [" /"]
"KP_Add": [" +"] "KP_Add": [" +"]
"KP_Multiply": [" *"] "KP_Multiply": [" *"]
"KP_Decimal": [" ."] "KP_Decimal": [" ."]
"KP_Subtract": [" -"] "KP_Subtract": [" -"]
"KP_Enter": ["Ent"] "KP_Enter": ["Ent"]
"Print": ["Prn"]
"Scroll": ["Scr"]
"Pause": ["Brk"]
"XF86Favorites": ["Rofi"] "XF86Favorites": ["Rofi"]