fix(input): carry key and preedit text as bytes like Zig
Zig's KeyEvent.utf8 and the embedded API's key/preedit text are byte slices passed through to key encoding and the pty; the port converted them lossily to UTF-8. - KeyEvent.utf8 is &[u8] end to end (ghostty-input encoder, core, ghostty-lib C API, libghostty-vt key events, GTK callers). Where Zig validates with Utf8View (alternates, modify-other-keys, ctrl CSI-u, associated text, binding unicode lookup) invalid text takes the same "no UTF-8" fallback. - Invalid preedit UTF-8 behaves like Zig's `try Utf8View.init`: the prior preedit is cleared and marked dirty, the callback returns an error (logged by the C API) and no preedit is set. - The inspector key widget shows invalid key text lossily, display only. Test Plan: - cargo test --workspace --no-fail-fast (3407 passed) - cargo test -p ghostty-lib --features test-platform (C-API key text 0xff 0xfe reaches the pty unchanged) - just check-macos (both), just check-windows, just check-wasm
This commit is contained in:
1 parent
5f16c24760
commit
db954441fa
19 files changed
+277
-144
No files matched your search
@@ -486,7 +486,7 @@ impl Harness {
|
||||
Mods::default()
|
||||
},
|
||||
composing: false,
|
||||
utf8,
|
||||
utf8: utf8.as_bytes(),
|
||||
unshifted_codepoint: unshifted,
|
||||
});
|
||||
// GTK reports the same text/codepoint on release.
|
||||
@@ -496,7 +496,7 @@ impl Harness {
|
||||
mods,
|
||||
consumed_mods: Mods::default(),
|
||||
composing: false,
|
||||
utf8,
|
||||
utf8: utf8.as_bytes(),
|
||||
unshifted_codepoint: unshifted,
|
||||
});
|
||||
eff
|
||||
|
||||
@@ -36,7 +36,7 @@ fn preedit_state_and_ime_point() {
|
||||
let h = harness!(Harness::recorder("", &[]));
|
||||
let before = h.surface.ime_point();
|
||||
h.surface
|
||||
.preedit_callback(Some("\u{3042}\u{3044}"))
|
||||
.preedit_callback(Some("\u{3042}\u{3044}".as_bytes()))
|
||||
.unwrap();
|
||||
{
|
||||
let st = h.surface.lock_state();
|
||||
@@ -57,6 +57,20 @@ fn preedit_state_and_ime_point() {
|
||||
assert_eq!(h.recorded(), Vec::<u8>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preedit_with_invalid_utf8_is_an_error_and_clears_prior() {
|
||||
let h = harness!(Harness::recorder("", &[]));
|
||||
// Like Zig's preeditCallback (`try Utf8View.init(text)`): invalid UTF-8
|
||||
// returns an error after the prior preedit has been cleared, and no new
|
||||
// preedit is set. Nothing is written to the pty.
|
||||
h.surface.preedit_callback(Some(b"ab")).unwrap();
|
||||
assert!(h.surface.lock_state().preedit.is_some());
|
||||
assert!(h.surface.preedit_callback(Some(b"a\xffb\xc3")).is_err());
|
||||
assert!(h.surface.lock_state().preedit.is_none());
|
||||
h.settle(50);
|
||||
assert_eq!(h.recorded(), Vec::<u8>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preedit_is_rendered_and_cleared() {
|
||||
let h = harness!(Harness::recorder("", &[]));
|
||||
@@ -71,7 +85,7 @@ fn preedit_is_rendered_and_cleared() {
|
||||
});
|
||||
b
|
||||
};
|
||||
h.surface.preedit_callback(Some("abcd")).unwrap();
|
||||
h.surface.preedit_callback(Some("abcd".as_bytes())).unwrap();
|
||||
h.wait("preedit visible", |h| {
|
||||
let r = region(h, cols.clone(), 0);
|
||||
!r.is_empty() && r != baseline
|
||||
@@ -89,7 +103,7 @@ fn commit_sends_text_and_composing_keys_do_not() {
|
||||
h.key_event(KeyEvent {
|
||||
key: Key::KeyK,
|
||||
composing: true,
|
||||
utf8: "k",
|
||||
utf8: "k".as_bytes(),
|
||||
unshifted_codepoint: 'k' as u32,
|
||||
..KeyEvent::default()
|
||||
});
|
||||
@@ -98,12 +112,12 @@ fn commit_sends_text_and_composing_keys_do_not() {
|
||||
|
||||
// Commit: preedit ends, committed text is sent with no key.
|
||||
h.surface
|
||||
.preedit_callback(Some("\u{3053}\u{3093}"))
|
||||
.preedit_callback(Some("\u{3053}\u{3093}".as_bytes()))
|
||||
.unwrap();
|
||||
h.surface.preedit_callback(None).unwrap();
|
||||
h.key_event(KeyEvent {
|
||||
key: Key::Unidentified,
|
||||
utf8: "\u{3053}\u{3093}\u{306b}\u{3061}\u{306f}",
|
||||
utf8: "\u{3053}\u{3093}\u{306b}\u{3061}\u{306f}".as_bytes(),
|
||||
..KeyEvent::default()
|
||||
});
|
||||
h.expect_input("\u{3053}\u{3093}\u{306b}\u{3061}\u{306f}".as_bytes());
|
||||
@@ -115,7 +129,7 @@ fn enter_with_committed_text_sends_text_not_cr() {
|
||||
let h = harness!(Harness::recorder("", &[]));
|
||||
h.key_event(KeyEvent {
|
||||
key: Key::Enter,
|
||||
utf8: "\u{6f22}",
|
||||
utf8: "\u{6f22}".as_bytes(),
|
||||
..KeyEvent::default()
|
||||
});
|
||||
h.expect_input("\u{6f22}".as_bytes());
|
||||
|
||||
@@ -250,7 +250,7 @@ fn key_release_without_press_is_ignored_in_legacy() {
|
||||
h.key_event(ghostty_input::KeyEvent {
|
||||
action: ghostty_input::key::Action::Release,
|
||||
key: Key::KeyA,
|
||||
utf8: "a",
|
||||
utf8: "a".as_bytes(),
|
||||
..Default::default()
|
||||
});
|
||||
h.type_text("b");
|
||||
|
||||
Reference in new issue
Block a user