Add 'effective utf8' field to ncinput struct. This field will contain the utf8 emitted by a key with all modifiers taken into account.

This commit is contained in:
Jeroen Domburg 2023-01-20 15:51:47 +08:00 committed by nick black
parent eac79ee680
commit d8cb625b9a
5 changed files with 108 additions and 9 deletions

View File

@ -37,6 +37,7 @@ typedef struct ncinput {
ncintype_e evtype; ncintype_e evtype;
unsigned modifiers;// bitmask over NCKEY_MOD_* unsigned modifiers;// bitmask over NCKEY_MOD_*
int ypx, xpx; // pixel offsets within cell, -1 for undefined int ypx, xpx; // pixel offsets within cell, -1 for undefined
char utf8_eff[5]; // Effective utf8 representation, taking modifier keys into account
} ncinput; } 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 by examining the output of **notcurses-info(1)**. If the **kbd** property
is indicated, you're using the Kitty protocol. 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 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 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. sometimes can be if the event occurs prior to a window resize.

View File

@ -1214,6 +1214,7 @@ typedef struct ncinput {
ncintype_e evtype; ncintype_e evtype;
unsigned modifiers;// bitmask over NCKEY_MOD_* unsigned modifiers;// bitmask over NCKEY_MOD_*
int ypx, xpx; // pixel offsets within cell, -1 for undefined int ypx, xpx; // pixel offsets within cell, -1 for undefined
char utf8_eff[5]; // Effective utf8 representation, taking modifier keys into account
} ncinput; } ncinput;
static inline bool static inline bool

View File

@ -797,7 +797,7 @@ kitty_functional(uint32_t val){
} }
static void 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(evtype >= 0);
assert(mods >= 0); assert(mods >= 0);
assert(val > 0); assert(val > 0);
@ -830,9 +830,33 @@ kitty_kbd(inputctx* ictx, int val, int mods, int evtype){
tni.evtype = NCTYPE_UNKNOWN; tni.evtype = NCTYPE_UNKNOWN;
break; 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); 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 static int
kitty_cb_simple(inputctx* ictx){ kitty_cb_simple(inputctx* ictx){
unsigned val = amata_next_numeric(&ictx->amata, "\x1b[", 'u'); unsigned val = amata_next_numeric(&ictx->amata, "\x1b[", 'u');
@ -849,6 +873,66 @@ kitty_cb(inputctx* ictx){
return 2; 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; i<n; i++) {
txt[i] = amata_next_numeric(&ictx->amata, "", (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 static uint32_t
legacy_functional(uint32_t id){ legacy_functional(uint32_t id){
switch(id){ switch(id){
@ -1702,7 +1786,15 @@ build_cflow_automaton(inputctx* ictx){
{ "[\\Nu", kitty_cb_simple, }, { "[\\Nu", kitty_cb_simple, },
{ "[\\N;\\N~", wezterm_cb, }, { "[\\N;\\N~", wezterm_cb, },
{ "[\\N;\\Nu", kitty_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:\\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~", xtmodkey_cb, },
{ "[\\N;\\N:\\N~", kitty_cb_functional, }, { "[\\N;\\N:\\N~", kitty_cb_functional, },
{ "[1;\\NP", legacy_cb_f1, }, { "[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){ if(notcurses_ucs32_to_utf8(&ni->id, 1, (unsigned char*)ni->utf8, sizeof(ni->utf8)) < 0){
ni->utf8[0] = 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){ if(++ictx->iread == ictx->isize){
ictx->iread = 0; ictx->iread = 0;

View File

@ -400,12 +400,8 @@ bool ncreader_offer_input(ncreader* n, const ncinput* ni){
}else if(nckey_synthesized_p(ni->id)){ }else if(nckey_synthesized_p(ni->id)){
return false; return false;
} }
// FIXME need to collect full EGCs
char wbuf[WCHAR_MAX_UTF8BYTES + 1]; ncreader_write_egc(n, ni->utf8_eff);
// FIXME breaks for wint_t < 32bits
if(snprintf(wbuf, sizeof(wbuf), "%lc", (wint_t)ni->id) < (int)sizeof(wbuf)){
ncreader_write_egc(n, wbuf);
}
return true; return true;
} }

View File

@ -351,9 +351,9 @@ init_terminfo_esc(tinfo* ti, const char* name, escape_e idx,
#define KITTYQUERY #define KITTYQUERY
#endif #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 // 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 // the kitty keyboard protocol allows unambiguous, complete identification of
// input events. this queries for the level of support. we want to do this // input events. this queries for the level of support. we want to do this