diff --git a/doc/man/man3/notcurses_input.3.md b/doc/man/man3/notcurses_input.3.md index f9b087d0b..b30833bb7 100644 --- a/doc/man/man3/notcurses_input.3.md +++ b/doc/man/man3/notcurses_input.3.md @@ -37,6 +37,7 @@ typedef struct ncinput { ncintype_e evtype; unsigned modifiers;// bitmask over NCKEY_MOD_* int ypx, xpx; // pixel offsets within cell, -1 for undefined + char utf8_eff[5]; // Effective utf8 representation, taking modifier keys into account } ncinput; @@ -242,6 +243,12 @@ issues are resolved. You can determine whether the protocol is in use by examining the output of **notcurses-info(1)**. If the **kbd** property is indicated, you're using the Kitty protocol. +Note that utf_eff will always return the effective text value of the key, +taking into account any meta keys pressed. (So shift-a will always return +'A' in this field, regardless of if the Kitty keyboard disambiguation +protocol is used.) This field generally only can be trusted on +**NCTYPE_PRESS** and **NCTYPE_REPEAT** events, however. + Mouse events in the left margins will never be delivered to the application (as is intended), but mouse events in the bottom and right margins sometimes can be if the event occurs prior to a window resize. diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 784241cf7..34328591c 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -1214,6 +1214,7 @@ typedef struct ncinput { ncintype_e evtype; unsigned modifiers;// bitmask over NCKEY_MOD_* int ypx, xpx; // pixel offsets within cell, -1 for undefined + char utf8_eff[5]; // Effective utf8 representation, taking modifier keys into account } ncinput; static inline bool diff --git a/src/lib/in.c b/src/lib/in.c index 8dc97890e..b60db5ac5 100644 --- a/src/lib/in.c +++ b/src/lib/in.c @@ -797,7 +797,7 @@ kitty_functional(uint32_t val){ } static void -kitty_kbd(inputctx* ictx, int val, int mods, int evtype){ +kitty_kbd_txt(inputctx* ictx, int val, int mods, unsigned *txt, int evtype){ assert(evtype >= 0); assert(mods >= 0); assert(val > 0); @@ -830,9 +830,33 @@ kitty_kbd(inputctx* ictx, int val, int mods, int evtype){ tni.evtype = NCTYPE_UNKNOWN; break; } + // Validate the txt array. It needs to be a non-zero-length set of up to 4 bytes. + int txt_valid = 0; + if(txt){ + for (int i = 0 ; i<4 ; i++){ + if(txt[i]==0) break; + if(txt[i]>255){ + txt_valid = 0; + break; + } + txt_valid = 1; + } + } + //note: if we don't populate .utf8_eff here, it will be set to what becomes .utf8 in + //internal_get(). + if(txt_valid){ + for(int i=0 ; i<4 ; i++){ + tni.utf8_eff[i] = (char)txt[i]; + } + } load_ncinput(ictx, &tni); } +static void +kitty_kbd(inputctx* ictx, int val, int mods, int evtype){ + kitty_kbd_txt(ictx, val, mods, NULL, evtype); +} + static int kitty_cb_simple(inputctx* ictx){ unsigned val = amata_next_numeric(&ictx->amata, "\x1b[", 'u'); @@ -849,6 +873,66 @@ kitty_cb(inputctx* ictx){ return 2; } +static int +kitty_cb_atxtn(inputctx* ictx, int n, int with_event){ + unsigned txt[5]={0}; + unsigned val = amata_next_numeric(&ictx->amata, "\x1b[", ';'); + unsigned ev = 0; + unsigned mods = 0; + if (with_event) { + mods = amata_next_numeric(&ictx->amata, "", ':'); + ev = amata_next_numeric(&ictx->amata, "", ';'); + } else { + mods = amata_next_numeric(&ictx->amata, "", ';'); + } + for (int i = 0; iamata, "", (i==n-1)?'u':';'); + } + kitty_kbd_txt(ictx, val, mods, txt, ev); + return 2; +} + +static int +kitty_cb_atxt1(inputctx* ictx){ + return kitty_cb_atxtn(ictx, 1, 0); +} + +static int +kitty_cb_atxt2(inputctx* ictx){ + return kitty_cb_atxtn(ictx, 2, 0); +} + +static int +kitty_cb_atxt3(inputctx* ictx){ + return kitty_cb_atxtn(ictx, 3, 0); +} + +static int +kitty_cb_atxt4(inputctx* ictx){ + return kitty_cb_atxtn(ictx, 4, 0); +} + + +static int +kitty_cb_complex_atxt1(inputctx* ictx){ + return kitty_cb_atxtn(ictx, 1, 1); +} + +static int +kitty_cb_complex_atxt2(inputctx* ictx){ + return kitty_cb_atxtn(ictx, 2, 1); +} + +static int +kitty_cb_complex_atxt3(inputctx* ictx){ + return kitty_cb_atxtn(ictx, 3, 1); +} + +static int +kitty_cb_complex_atxt4(inputctx* ictx){ + return kitty_cb_atxtn(ictx, 4, 1); +} + static uint32_t legacy_functional(uint32_t id){ switch(id){ @@ -1702,7 +1786,15 @@ build_cflow_automaton(inputctx* ictx){ { "[\\Nu", kitty_cb_simple, }, { "[\\N;\\N~", wezterm_cb, }, { "[\\N;\\Nu", kitty_cb, }, + { "[\\N;\\N;\\Nu", kitty_cb_atxt1, }, + { "[\\N;\\N;\\N;\\Nu", kitty_cb_atxt2, }, + { "[\\N;\\N;\\N;\\N;\\Nu", kitty_cb_atxt3, }, + { "[\\N;\\N;\\N;\\N;\\N;\\Nu", kitty_cb_atxt4, }, { "[\\N;\\N:\\Nu", kitty_cb_complex, }, + { "[\\N;\\N:\\N;\\Nu", kitty_cb_complex_atxt1, }, + { "[\\N;\\N:\\N;\\N;\\Nu", kitty_cb_complex_atxt2, }, + { "[\\N;\\N:\\N;\\N;\\N;\\Nu", kitty_cb_complex_atxt3, }, + { "[\\N;\\N:\\N;\\N;\\N;\\N;\\Nu", kitty_cb_complex_atxt4, }, { "[\\N;\\N;\\N~", xtmodkey_cb, }, { "[\\N;\\N:\\N~", kitty_cb_functional, }, { "[1;\\NP", legacy_cb_f1, }, @@ -2617,6 +2709,9 @@ internal_get(inputctx* ictx, const struct timespec* ts, ncinput* ni){ if(notcurses_ucs32_to_utf8(&ni->id, 1, (unsigned char*)ni->utf8, sizeof(ni->utf8)) < 0){ ni->utf8[0] = 0; } + if(ni->utf8_eff[0] == 0){ + memcpy(ni->utf8_eff, ni->utf8, sizeof(ni->utf8_eff)); + } } if(++ictx->iread == ictx->isize){ ictx->iread = 0; diff --git a/src/lib/reader.c b/src/lib/reader.c index e92052164..7dedbe057 100644 --- a/src/lib/reader.c +++ b/src/lib/reader.c @@ -400,12 +400,8 @@ bool ncreader_offer_input(ncreader* n, const ncinput* ni){ }else if(nckey_synthesized_p(ni->id)){ return false; } - // FIXME need to collect full EGCs - char wbuf[WCHAR_MAX_UTF8BYTES + 1]; - // FIXME breaks for wint_t < 32bits - if(snprintf(wbuf, sizeof(wbuf), "%lc", (wint_t)ni->id) < (int)sizeof(wbuf)){ - ncreader_write_egc(n, wbuf); - } + + ncreader_write_egc(n, ni->utf8_eff); return true; } diff --git a/src/lib/termdesc.c b/src/lib/termdesc.c index 7d2db3f83..c5da936df 100644 --- a/src/lib/termdesc.c +++ b/src/lib/termdesc.c @@ -351,9 +351,9 @@ init_terminfo_esc(tinfo* ti, const char* name, escape_e idx, #define KITTYQUERY #endif -// request kitty keyboard protocol features 1, 2, and 8, first pushing current. +// request kitty keyboard protocol features 1, 2, 8 and 16, first pushing current. // see https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement -#define KKBDSUPPORT "\x1b[=11u" +#define KKBDSUPPORT "\x1b[=27u" // the kitty keyboard protocol allows unambiguous, complete identification of // input events. this queries for the level of support. we want to do this