fix: distinguish AltGraph from ordinary Alt shortcuts

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou committed 2026-10-04 20:48:56 -07:00
1 parent f3ead7e09d
commit 558ca359b8
2 files changed
+51 -5

No files matched your search

+22 -5
View File
@@ -150,10 +150,6 @@ pub fn match_normalized<'a>(key: &str, mods: &[Modifier], b: &'a Bindings) -> Op
pub fn normalize_modifiers(alt: bool, ctrl: bool, shift: bool, command: bool) -> Vec<Modifier> {
// iOS shares Apple's keyboard semantics with macOS — recording dialog
// already treats iOS as `_isMac`, so the matcher must too.
//
// AltGr conflation: `get_modifiers_state` ORs Alt and AltGr, so an
// AltGr+key press satisfies `Modifier::Alt`. Theoretical collision only;
// fix at `get_modifiers_state` if a real bug surfaces.
let mut v = Vec::new();
if cfg!(any(target_os = "macos", target_os = "ios")) {
if command { v.push(Modifier::Primary); }
@@ -333,7 +329,28 @@ pub fn match_event(event: &rdev::Event) -> Option<String> {
let (alt, ctrl, shift, command) =
crate::keyboard::client::get_modifiers_state(false, false, false, false);
let mods = normalize_modifiers(alt, ctrl, shift, command);
match_normalized(&key_name, &mods, &bindings).map(str::to_owned)
let action = match_normalized(&key_name, &mods, &bindings)?;
// AltGraph is text input, not the ordinary Alt recorded in a shortcut.
// Keep the shared modifier snapshot unchanged for remote key encoding.
#[cfg(target_os = "windows")]
if unsafe { super::IS_0X021D_DOWN }
&& super::MODIFIERS_STATE.lock().unwrap().get(&rdev::Key::AltGr) == Some(&true)
{
return None;
}
#[cfg(target_os = "linux")]
if super::MODIFIERS_STATE.lock().unwrap().get(&rdev::Key::AltGr) == Some(&true) {
match crate::platform::linux::right_alt_is_level3_shift() {
Ok(true) => return None,
Ok(false) => {}
Err(err) => {
hbb_common::throttled_log!(std::time::Duration::from_secs(5), warn,
"Cannot identify AltGraph for keyboard shortcuts: {}", err);
return None;
}
}
}
Some(action.to_owned())
}
#[cfg(feature = "flutter")]
+29
View File
@@ -302,6 +302,35 @@ thread_local! {
static DISPLAY: RefCell<*mut c_void> = RefCell::new(unsafe { XOpenDisplay(std::ptr::null())});
}
pub fn right_alt_is_level3_shift() -> ResultType<bool> {
use hbb_common::x11::{keysym, xlib};
const XKB_USE_CORE_KBD: c_uint = 0x0100;
// A fresh connection avoids Xlib's stale keymap cache after a layout switch.
// This query runs only for a matched chord with physical Right Alt held.
let display = unsafe { xlib::XOpenDisplay(std::ptr::null()) };
if display.is_null() {
bail!("X display unavailable");
}
let result = (|| {
let code = rdev::linux_keycode_from_key(rdev::Key::AltGr)
.ok_or_else(|| anyhow!("Right Alt has no X keycode"))?;
let mut state: xlib::XkbStateRec = unsafe { std::mem::zeroed() };
let status = unsafe { xlib::XkbGetState(display, XKB_USE_CORE_KBD, &mut state) };
if status != c_int::from(xlib::Success) {
bail!("XkbGetState failed: {}", status);
}
let symbol = unsafe {
xlib::XkbKeycodeToKeysym(display, code as _, state.group.into(), 0)
};
// Physical Right Alt is ordinary Alt on layouts such as US English.
Ok(matches!(symbol as u32, keysym::XK_ISO_Level3_Shift
| keysym::XK_ISO_Level3_Latch | keysym::XK_ISO_Level3_Lock
| keysym::XK_Mode_switch))
})();
unsafe { xlib::XCloseDisplay(display) };
result
}
// X11 error event structure for the custom error handler.
// See: https://www.x.org/releases/current/doc/libX11/libX11/libX11.html#Using-the-Default-Error-Handlers
#[repr(C)]