aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-09 20:14:03 +0200
committeruser <user@clank>2026-09-09 20:14:03 +0200
commita1e2c1817ac8d967519cd978742f6a59591ff959 (patch)
treeeca5b858d7c60a18d261d5c64fbc3a45ffcce255 /src
parentsearch: a scrollback search bar on /, matches highlighted (diff)
downloadslosh-a1e2c1817ac8d967519cd978742f6a59591ff959.tar.gz
slosh-a1e2c1817ac8d967519cd978742f6a59591ff959.tar.xz
slosh-a1e2c1817ac8d967519cd978742f6a59591ff959.zip
pane: shift consumed by the character is consumed, tell the encoder
935fb7e9 taught the legacy decoder to read the shift out of shifted punctuation, so / and ? could be two keys. But pane_send_key never set the consumed mods on the re-encoded event, and libghostty's kitty path only passes text through as text when no unconsumed modifier remains: a kitty-protocol pane (nvim) received : as CSI 59;2u -- shift+semicolon, no text -- and the command line never opened. :wq! was unreachable. Mark shift consumed when the event carries text, shift is held, and the text is not the unshifted character -- the same call a real terminal makes. Text the shift produced goes through as text; chords whose shift is real (shift+up, ctrl+shift+a, back-tab) keep their escapes, and a legacy pane sees no change.
Diffstat (limited to 'src')
-rw-r--r--src/pane.c23
1 files changed, 23 insertions, 0 deletions
diff --git a/src/pane.c b/src/pane.c
index ecff425..47db104 100644
--- a/src/pane.c
+++ b/src/pane.c
@@ -794,6 +794,29 @@ void pane_send_key(pane_t *p, const input_event_t *ev) {
ev->text_len);
ghostty_key_event_set_unshifted_codepoint(p->kev, ev->unshifted);
+ /* Shift that produced the character is consumed, and the encoder must be
+ * told so: its kitty path only passes text through as text when no
+ * unconsumed modifier remains. Without this, `:` (shift+; in the decoder's
+ * eyes) reaches a kitty-protocol pane as CSI 59;2u -- shift+semicolon, no
+ * text -- and nvim's command line never opens. A real terminal marks the
+ * shift consumed for exactly this case; so do we: text present, shift
+ * held, and the text is not the unshifted character. */
+ GhosttyMods consumed = 0;
+ if (ev->text_len && (ev->mods & MOD_SHIFT)) {
+ const uint8_t *t = (const uint8_t *)ev->text;
+ uint32_t cp = t[0];
+ if ((t[0] & 0xe0) == 0xc0 && ev->text_len >= 2)
+ cp = (uint32_t)(t[0] & 0x1f) << 6 | (t[1] & 0x3f);
+ else if ((t[0] & 0xf0) == 0xe0 && ev->text_len >= 3)
+ cp = (uint32_t)(t[0] & 0x0f) << 12 | (uint32_t)(t[1] & 0x3f) << 6 |
+ (t[2] & 0x3f);
+ else if ((t[0] & 0xf8) == 0xf0 && ev->text_len >= 4)
+ cp = (uint32_t)(t[0] & 0x07) << 18 | (uint32_t)(t[1] & 0x3f) << 12 |
+ (uint32_t)(t[2] & 0x3f) << 6 | (t[3] & 0x3f);
+ if (cp != ev->unshifted) consumed |= GHOSTTY_MODS_SHIFT;
+ }
+ ghostty_key_event_set_consumed_mods(p->kev, consumed);
+
char out[128];
size_t n = 0;
if (ghostty_key_encoder_encode(p->kenc, p->kev, out, sizeof out, &n) ==