feat: libxkbcommon layouts
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -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",
|
||||||
|
|||||||
13
Cargo.toml
13
Cargo.toml
@@ -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 = []
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)?;
|
||||||
|
|||||||
126
src/gui/mod.rs
126
src/gui/mod.rs
@@ -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 {
|
||||||
self.canvas.controls.push(Control {
|
KeyCapType::Regular => {
|
||||||
rect: if i == 0 {
|
let render: ControlRenderer<D, S> = Control::render_text_centered;
|
||||||
Rect {
|
let rect = Rect {
|
||||||
x: x + 4.,
|
x,
|
||||||
y: y + (self.font_size as f32) + 4.,
|
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,
|
w,
|
||||||
h,
|
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)]
|
||||||
}
|
}
|
||||||
} else {
|
KeyCapType::Reversed => {
|
||||||
Rect {
|
let render: ControlRenderer<D, S> = Control::render_text_centered;
|
||||||
x: x + w * 0.5,
|
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.,
|
y: y + h - (self.font_size as f32) + 4.,
|
||||||
w,
|
w,
|
||||||
h,
|
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),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
text: Arc::from(item.as_str()),
|
|
||||||
fg_color: self.fg_color,
|
for (idx, (render, rect, alpha)) in renders.into_iter().enumerate() {
|
||||||
|
if idx >= label.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.canvas.controls.push(Control {
|
||||||
|
rect,
|
||||||
|
text: Arc::from(label[idx].as_str()),
|
||||||
|
fg_color: self.fg_color * alpha,
|
||||||
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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
142
src/hid/wayland.rs
Normal 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
16
src/hid/x11.rs
Normal 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 })
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user