Compare commits

...

15 Commits

Author SHA1 Message Date
Conrad Irwin
a42d0b4c8b Correcter docs 2025-07-11 12:32:13 -06:00
Conrad Irwin
64a9d6afd0 Docs 2025-07-11 12:12:38 -06:00
Conrad Irwin
b097d3c5eb Check X11/Wayland at runtime, fix shift 2025-07-11 11:51:03 -06:00
Sergei Surovtsev
36c893b2a4 rm key_us 2025-07-10 16:16:23 +03:00
Sergei Surovtsev
b969800bd6 KeyStroke::key_en => key 2025-07-09 22:34:45 +03:00
Sergei Surovtsev
40297ff92a KeyStroke::key_en => key 2025-07-09 20:14:56 +03:00
Sergei Surovtsev
4fcc871613 mac impl 2025-07-08 14:00:33 +03:00
Sergei Surovtsev
047b80deb0 remove windows key_en impl 2025-07-08 13:59:00 +03:00
Sergei Surovtsev
23e3da4420 change KeyStroke::key_en type from String to char; mac impl 2025-07-08 13:42:50 +03:00
Sergei Surovtsev
fefe0dfb80 remove keyboard_types 2025-07-08 12:50:20 +03:00
Sergei Surovtsev
095d7800b1 windows fix 2025-07-08 12:49:22 +03:00
Sergei Surovtsev
18234ae965 mac impl todo 2025-07-08 12:16:40 +03:00
Sergei Surovtsev
5b3e2ef862 windows impl 2025-07-08 12:15:19 +03:00
Sergei Surovtsev
73ddd894d9 proper handing of keycodes wrt platform keycodes 2025-07-07 22:25:35 +03:00
Sergei Surovtsev
a4d2ef9551 langauge-independent hotkeys for x11 2025-07-07 21:54:16 +03:00
5 changed files with 108 additions and 13 deletions

View File

@@ -13,6 +13,9 @@ pub struct Keystroke {
/// key is the character printed on the key that was pressed
/// e.g. for option-s, key is "s"
/// On layouts that do not have ascii keys (e.g. Thai)
/// this will be the ASCII-equivalent character (q instead of ๆ),
/// and the typed character will be present in key_char.
pub key: String,
/// key_char is the character that could have been typed when

View File

@@ -706,12 +706,81 @@ pub(super) fn log_cursor_icon_warning(message: impl std::fmt::Display) {
}
}
#[cfg(any(feature = "wayland", feature = "x11"))]
pub(crate) enum KeycodeSource {
X11,
Wayland,
}
#[cfg(any(feature = "wayland", feature = "x11"))]
impl KeycodeSource {
fn guess_ascii(&self, keycode: Keycode, shift: bool) -> Option<char> {
let raw = match self {
// For historical reasons X11 adds 8 to keycodes
Self::X11 => keycode.raw() - 8,
// For no particular reason, wayland doesn't.
Self::Wayland => keycode.raw(),
};
let c = match (raw, shift) {
(16, _) => 'q',
(17, _) => 'w',
(18, _) => 'e',
(19, _) => 'r',
(20, _) => 't',
(21, _) => 'y',
(22, _) => 'u',
(23, _) => 'i',
(24, _) => 'o',
(25, _) => 'p',
(26, false) => '[',
(26, true) => '{',
(27, false) => ']',
(27, true) => '}',
(30, _) => 'a',
(31, _) => 's',
(32, _) => 'd',
(33, _) => 'f',
(34, _) => 'g',
(35, _) => 'h',
(36, _) => 'j',
(37, _) => 'k',
(38, _) => 'l',
(39, false) => ';',
(39, true) => ':',
(40, false) => '\'',
(40, true) => '"',
(41, false) => '`',
(41, true) => '~',
(43, false) => '\\',
(43, true) => '|',
(44, _) => 'z',
(45, _) => 'x',
(46, _) => 'c',
(47, _) => 'v',
(48, _) => 'b',
(49, _) => 'n',
(50, _) => 'm',
(51, false) => ',',
(51, true) => '>',
(52, false) => '.',
(52, true) => '<',
(53, false) => '/',
(53, true) => '?',
_ => return None,
};
Some(c)
}
}
#[cfg(any(feature = "wayland", feature = "x11"))]
impl crate::Keystroke {
pub(super) fn from_xkb(
state: &State,
mut modifiers: crate::Modifiers,
keycode: Keycode,
source: KeycodeSource,
) -> Self {
let key_utf32 = state.key_get_utf32(keycode);
let key_utf8 = state.key_get_utf8(keycode);
@@ -773,6 +842,8 @@ impl crate::Keystroke {
let name = xkb::keysym_get_name(key_sym).to_lowercase();
if key_sym.is_keypad_key() {
name.replace("kp_", "")
} else if let Some(key_en) = source.guess_ascii(keycode, modifiers.shift) {
String::from(key_en)
} else {
name
}

View File

@@ -69,7 +69,6 @@ use super::{
window::{ImeInput, WaylandWindowStatePtr},
};
use crate::platform::{PlatformWindow, blade::BladeContext};
use crate::{
AnyWindowHandle, Bounds, Capslock, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId,
FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon,
@@ -78,6 +77,10 @@ use crate::{
PlatformInput, PlatformKeyboardLayout, Point, SCROLL_LINES, ScaledPixels, ScrollDelta,
ScrollWheelEvent, Size, TouchPhase, WindowParams, point, px, size,
};
use crate::{
KeycodeSource,
platform::{PlatformWindow, blade::BladeContext},
};
use crate::{
SharedString,
platform::linux::{
@@ -1293,8 +1296,12 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
match key_state {
wl_keyboard::KeyState::Pressed if !keysym.is_modifier_key() => {
let mut keystroke =
Keystroke::from_xkb(&keymap_state, state.modifiers, keycode);
let mut keystroke = Keystroke::from_xkb(
&keymap_state,
state.modifiers,
keycode,
KeycodeSource::Wayland,
);
if let Some(mut compose) = state.compose_state.take() {
compose.feed(keysym);
match compose.status() {
@@ -1379,7 +1386,12 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
}
wl_keyboard::KeyState::Released if !keysym.is_modifier_key() => {
let input = PlatformInput::KeyUp(KeyUpEvent {
keystroke: Keystroke::from_xkb(keymap_state, state.modifiers, keycode),
keystroke: Keystroke::from_xkb(
keymap_state,
state.modifiers,
keycode,
KeycodeSource::Wayland,
),
});
if state.repeat.current_keycode == Some(keycode) {

View File

@@ -1,4 +1,4 @@
use crate::{Capslock, xcb_flush};
use crate::{Capslock, KeycodeSource, xcb_flush};
use core::str;
use std::{
cell::RefCell,
@@ -1037,7 +1037,8 @@ impl X11Client {
xkb_state.latched_layout,
xkb_state.locked_layout,
);
let mut keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
let mut keystroke =
crate::Keystroke::from_xkb(&state.xkb, modifiers, code, KeycodeSource::X11);
let keysym = state.xkb.key_get_one_sym(code);
if keysym.is_modifier_key() {
return Some(());
@@ -1105,7 +1106,8 @@ impl X11Client {
xkb_state.latched_layout,
xkb_state.locked_layout,
);
let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
let keystroke =
crate::Keystroke::from_xkb(&state.xkb, modifiers, code, KeycodeSource::X11);
let keysym = state.xkb.key_get_one_sym(code);
if keysym.is_modifier_key() {
return Some(());
@@ -1329,6 +1331,7 @@ impl X11Client {
&state.xkb,
state.modifiers,
event.detail.into(),
KeycodeSource::X11,
));
let (mut ximc, mut xim_handler) = state.take_xim()?;
drop(state);

View File

@@ -136,17 +136,17 @@ When this happens, and both bindings are active in the current context, Zed will
### Non-QWERTY keyboards
As of Zed 0.162.0, Zed has some support for non-QWERTY keyboards on macOS. Better support for non-QWERTY keyboards on Linux is planned.
Zed's support for non-QWERTY keyboards is still a work in progress.
There are roughly three categories of keyboard to consider:
If your keyboard can type the full ASCII ranges (DVORAK, COLEMAK, etc.) then shortcuts should work as you expect.
Keyboards that support full ASCII (QWERTY, DVORAK, COLEMAK, etc.). On these keyboards bindings are resolved based on the character that would be generated by the key. So to type `cmd-[`, find the key labeled `[` and press it with command.
Otherwise, read on...
Keyboards that are mostly non-ASCII, but support full ASCII when the command key is pressed. For example Cyrillic keyboards, Armenian, Hebrew, etc. On these keyboards bindings are resolved based on the character that would be generated by typing the key with command pressed. So to type `ctrl-a`, find the key that generates `cmd-a`. For these keyboards, keyboard shortcuts are displayed in the app using their ASCII equivalents. If the ASCII-equivalents are not printed on your keyboard, you can use the macOS keyboard viewer and holding down the `cmd` key to find things (though often the ASCII equivalents are in a QWERTY layout).
#### macOS
Finally keyboards that support extended Latin alphabets (usually ISO keyboards) require the most support. For example French AZERTY, German QWERTZ, etc. On these keyboards it is often not possible to type the entire ASCII range without option. To ensure that shortcuts _can_ be typed without option, keyboard shortcuts are mapped to "key equivalents" in the same way as [macOS](). This mapping is defined per layout, and is a compromise between leaving keyboard shortcuts triggered by the same character they are defined with, keeping shortcuts in the same place as a QWERTY layout, and moving shortcuts out of the way of system shortcuts.
On Cyrillic, Hebrew, Armenian, and other keyboards that are mostly non-ASCII; macOS automatically maps keys to the ASCII range when `cmd` is held. Zed takes this a step further and it can always match key-presses against the ASCII layout if any modifier is held down. For example on a Thai layout, pressing `ctrl-ๆ` will match bindings associated with `ctrl-q`.
For example on a German QWERTZ keyboard, the `cmd->` shortcut is moved to `cmd-:` because `cmd->` is the system window switcher and this is where that shortcut is typed on a QWERTY keyboard. `cmd-+` stays the same because + is still typeable without option, and as a result, `cmd-[` and `cmd-]` become `cmd-ö` and `cmd-ä`, moving out of the way of the `+` key.
On keyboards that support extended Latin alphabets (French AZERTY, German QWERTZ, etc.) it is often not possible to type the entire ASCII range without `option`. This introduces an ambiguity, `option-2` produces `@`. To ensure that all the builtin keyboard shortcuts can still be typed on these keyboards we move key-bindings around. For example, shortcuts bound to `@` on QWERTY are moved to `"` on a Spanish layout. This mapping is based on the macOS system defaults and can be seen by running `dev: Open Key Context View` from the command palette.
If you are defining shortcuts in your personal keymap, you can opt into the key equivalent mapping by setting `use_key_equivalents` to `true` in your keymap:
@@ -161,6 +161,12 @@ If you are defining shortcuts in your personal keymap, you can opt into the key
]
```
### Linux
Since v0.196.0 on Linux if the key that you type doesn't produce an ASCII character then we use the QWERTY-layout equivalent key for keyboard shortcuts. This means that many shortcuts can be typed on many layouts. For example, on a Russian keyboard typing `ctrl-ф` will match bindings assigned to `ctrl-a`.
We do not yet move shortcuts around to ensure that all the builtin shortcuts can be typed on every layout; so if there are some ASCII characters that cannot be typed, and your keyboard layout has different ASCII characters on the same keys as would be needed to type them, you may need to add custom key bindings to make this work. We do intend to fix this at some point, and help is very much wanted!
## Tips and tricks
### Disabling a binding