Skip to content

Commit

Permalink
add meta modifier handling #2522
Browse files Browse the repository at this point in the history
Handle Meta modifier in Kitty and XTMODKEYS. Add Meta
indicator to notcurses-input. Update man page. Add
NCKEY_META_{SHIFT, CTRL, ALT, META} constants. Add
"modifiers" field to ncinput struct. Add inline
functions for testing modifiers. Remove special-casing
in Kitty protocol that capitalized all lowercase ASCII
when ctrl was pressed; we don't do this for XTMODKEYS.
  • Loading branch information
dankamongmen committed Jan 4, 2022
1 parent efddf0b commit 53fbbae
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 46 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ rearrangements of Notcurses.
* 3.0.4 (not yet released)
* We now use level 2 of `XTMODKEYS`, providing better differentiation
of keyboard modifiers.
* Added `ncinput_shift_p()`, `ncinput_alt_p()`, `ncinput_ctrl_p()`,
and `ncinput_meta_p()` to test for various modifiers in `ncinput`s.

* 3.0.3 (2022-01-02)
* No user-visible changes to the API, but Sixel quantization has been
Expand Down
1 change: 1 addition & 0 deletions doc/man/man1/notcurses-input.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ of modifier indicators:
* 'A'/'a': Alt was or was not pressed.
* 'C'/'c': Ctrl was or was not pressed.
* 'S'/'s': Shift was or was not pressed.
* 'M'/'m': Meta was or was not pressed.
* 'L'/'R'/'P'/'u': Key was a release, repeat, press, or of unknown type.

# OPTIONS
Expand Down
21 changes: 19 additions & 2 deletions doc/man/man3/notcurses_input.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ typedef struct ncinput {
EVTYPE_RELEASE,
} evtype;
int ypx, xpx; // pixel offsets within cell, -1 for undefined
unsigned modifiers;
} ncinput;

#define NCMICE_NO_EVENTS 0
Expand Down Expand Up @@ -62,6 +63,14 @@ typedef struct ncinput {
**int notcurses_linesigs_enable(struct notcurses* ***n***);**
**bool ncinput_shift_p(const struct ncinput* ***n***);**
**bool ncinput_ctrl_p(const struct ncinput* ***n***);**
**bool ncinput_alt_p(const struct ncinput* ***n***);**
**bool ncinput_meta_p(const struct ncinput* ***n***);**
# DESCRIPTION
notcurses supports input from keyboards and mice, and any device that looks
Expand Down Expand Up @@ -100,6 +109,10 @@ input (though not necessarily the same input event).
conversions are enabled by default. **notcurses_linesigs_enable** undoes this
action, but signals in the interim are permanently lost.
**ncinput_shift_p**, **ncinput_ctrl_p**, **ncinput_alt_p**, and
**ncinput_meta_p** test ***n*** to see if the relevant modifier is set.
This is preferably to directly accessing the struct members.
## Mice
For mouse events, the additional fields ***y***, ***x***, ***ypx***, and
Expand Down Expand Up @@ -188,11 +201,15 @@ for input readiness. Instead, use the file descriptor returned by
Notcurses (it is possible that future versions will process input in their own
contexts).
The full list of synthesized events is available in **<notcurses/nckeys.h>**.
In API4, the various **bool** modifier fields will go away, and these statuses
will be merged into the ***modifiers*** bitmask. You are encouraged to use
**ncinput_shift_p** and friends to future-proof your code.
When support is detected, the Kitty keyboard disambiguation protocol will be
requested. This eliminates most of the **BUGS** mentioned below.
The full list of synthesized events is available in **<notcurses/nckeys.h>**.
# BUGS
The Shift key is not indicated in conjunction with typical Unicode text.
Expand Down
6 changes: 6 additions & 0 deletions include/notcurses/nckeys.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ nckey_supppuab_p(uint32_t w){
return w >= 0x100000 && w <= 0x10fffd; // 65,534 codepoints
}

// modifiers bitmask
#define NCKEY_MOD_SHIFT 1
#define NCKEY_MOD_CTRL 2
#define NCKEY_MOD_ALT 4
#define NCKEY_MOD_META 8

#ifdef __cplusplus
} // extern "C"
#endif
Expand Down
22 changes: 22 additions & 0 deletions include/notcurses/notcurses.h
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,7 @@ nckey_mouse_p(uint32_t r){
// An input event. Cell coordinates are currently defined only for mouse
// events. It is not guaranteed that we can set the modifiers for a given
// ncinput. We encompass single Unicode codepoints, not complete EGCs.
// FIXME for abi4, combine the bools into |modifiers|
typedef struct ncinput {
uint32_t id; // Unicode codepoint or synthesized NCKEY event
int y, x; // y/x cell coordinate of event, -1 for undefined
Expand All @@ -1147,9 +1148,30 @@ typedef struct ncinput {
NCTYPE_REPEAT,
NCTYPE_RELEASE,
} evtype;
unsigned modifiers;// bitmask over NCMOD_META
int ypx, xpx; // pixel offsets within cell, -1 for undefined
} ncinput;

static inline bool
ncinput_shift_p(const ncinput* n){
return (n->modifiers & NCKEY_MOD_SHIFT);
}

static inline bool
ncinput_ctrl_p(const ncinput* n){
return (n->modifiers & NCKEY_MOD_CTRL);
}

static inline bool
ncinput_alt_p(const ncinput* n){
return (n->modifiers & NCKEY_MOD_ALT);
}

static inline bool
ncinput_meta_p(const ncinput* n){
return (n->modifiers & NCKEY_MOD_META);
}

// compare two ncinput structs for data equality.
static inline bool
ncinput_equal_p(const ncinput* n1, const ncinput* n2){
Expand Down
8 changes: 6 additions & 2 deletions src/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,12 @@ int input_demo(ncpp::NotCurses* nc) {
break;
}
n->set_fg_rgb8(0xd0, 0xd0, 0xd0);
n->printf("%c%c%c%c ", ni.alt ? 'A' : 'a', ni.ctrl ? 'C' : 'c',
ni.shift ? 'S' : 's', evtype_to_char(&ni));
n->printf("%c%c%c%c%c ",
ncinput_alt_p(&ni) ? 'A' : 'a',
ncinput_ctrl_p(&ni) ? 'C' : 'c',
ncinput_shift_p(&ni) ? 'S' : 's',
ncinput_meta_p(&ni) ? 'M' : 'm',
evtype_to_char(&ni));
if(r < 0x80){
n->set_fg_rgb8(128, 250, 64);
if(n->printf("ASCII: [0x%02x (%03d)] '%lc'", r, r,
Expand Down
4 changes: 3 additions & 1 deletion src/lib/automaton.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,8 @@ int inputctx_add_cflow(automaton* a, const char* seq, triefunc fxn){

// multiple input escapes might map to the same input
int inputctx_add_input_escape(automaton* a, const char* esc, uint32_t special,
unsigned shift, unsigned ctrl, unsigned alt){
unsigned shift, unsigned ctrl, unsigned alt,
unsigned modifiers){
if(esc[0] != NCKEY_ESC || strlen(esc) < 2){ // assume ESC prefix + content
logerror("not an escape (0x%x)", special);
return -1;
Expand All @@ -523,6 +524,7 @@ int inputctx_add_input_escape(automaton* a, const char* esc, uint32_t special,
eptr->ni.alt = alt;
eptr->ni.y = 0;
eptr->ni.x = 0;
eptr->ni.modifiers = modifiers;
logdebug("added 0x%08x to %u", special, esctrie_idx(a, eptr));
}
return 0;
Expand Down
3 changes: 2 additions & 1 deletion src/lib/automaton.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ void input_free_esctrie(automaton *a);

int inputctx_add_input_escape(automaton* a, const char* esc,
uint32_t special, unsigned shift,
unsigned ctrl, unsigned alt);
unsigned ctrl, unsigned alt,
unsigned modifiers);

int inputctx_add_cflow(automaton* a, const char* csi, triefunc fxn)
__attribute__ ((nonnull (1, 2)));
Expand Down
118 changes: 78 additions & 40 deletions src/lib/in.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,25 +134,40 @@ prep_xtmodkeys(inputctx* ictx){
const char* esc;
uint32_t key;
bool shift, ctrl, alt;
unsigned modifiers;
} keys[] = {
{ .esc = "\x1b\x8", .key = NCKEY_BACKSPACE, .alt = 1, },
{ .esc = "\x1b[2P", .key = NCKEY_F01, .shift = 1, },
{ .esc = "\x1b[5P", .key = NCKEY_F01, .ctrl = 1, },
{ .esc = "\x1b[6P", .key = NCKEY_F01, .ctrl = 1, .shift = 1, },
{ .esc = "\x1b[2Q", .key = NCKEY_F02, .shift = 1, },
{ .esc = "\x1b[5Q", .key = NCKEY_F02, .ctrl = 1, },
{ .esc = "\x1b[6Q", .key = NCKEY_F02, .ctrl = 1, .shift = 1, },
{ .esc = "\x1b[2R", .key = NCKEY_F03, .shift = 1, },
{ .esc = "\x1b[5R", .key = NCKEY_F03, .ctrl = 1, },
{ .esc = "\x1b[6R", .key = NCKEY_F03, .ctrl = 1, .shift = 1, },
{ .esc = "\x1b[2S", .key = NCKEY_F04, .shift = 1, },
{ .esc = "\x1b[5S", .key = NCKEY_F04, .ctrl = 1, },
{ .esc = "\x1b[6S", .key = NCKEY_F04, .ctrl = 1, .shift = 1, },
{ .esc = "\x1b\x8", .key = NCKEY_BACKSPACE, .alt = 1,
.modifiers = NCKEY_MOD_ALT, },
{ .esc = "\x1b[2P", .key = NCKEY_F01, .shift = 1,
.modifiers = NCKEY_MOD_SHIFT, },
{ .esc = "\x1b[5P", .key = NCKEY_F01, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = "\x1b[6P", .key = NCKEY_F01, .ctrl = 1, .shift = 1,
.modifiers = NCKEY_MOD_CTRL | NCKEY_MOD_SHIFT, },
{ .esc = "\x1b[2Q", .key = NCKEY_F02, .shift = 1,
.modifiers = NCKEY_MOD_SHIFT, },
{ .esc = "\x1b[5Q", .key = NCKEY_F02, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = "\x1b[6Q", .key = NCKEY_F02, .ctrl = 1, .shift = 1,
.modifiers = NCKEY_MOD_CTRL | NCKEY_MOD_SHIFT, },
{ .esc = "\x1b[2R", .key = NCKEY_F03, .shift = 1,
.modifiers = NCKEY_MOD_SHIFT, },
{ .esc = "\x1b[5R", .key = NCKEY_F03, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = "\x1b[6R", .key = NCKEY_F03, .ctrl = 1, .shift = 1,
.modifiers = NCKEY_MOD_CTRL | NCKEY_MOD_SHIFT, },
{ .esc = "\x1b[2S", .key = NCKEY_F04, .shift = 1,
.modifiers = NCKEY_MOD_SHIFT, },
{ .esc = "\x1b[5S", .key = NCKEY_F04, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = "\x1b[6S", .key = NCKEY_F04, .ctrl = 1, .shift = 1,
.modifiers = NCKEY_MOD_CTRL | NCKEY_MOD_SHIFT, },
{ .esc = NULL, .key = 0, },
}, *k;
for(k = keys ; k->esc ; ++k){
if(inputctx_add_input_escape(&ictx->amata, k->esc, k->key,
k->shift, k->ctrl, k->alt)){
k->shift, k->ctrl, k->alt,
k->modifiers)){
return -1;
}
logdebug("added %u", k->key);
Expand Down Expand Up @@ -337,7 +352,10 @@ prep_special_keys(inputctx* ictx){
logwarn("invalid escape: %s (0x%x)", k->tinfo, k->key);
continue;
}
if(inputctx_add_input_escape(&ictx->amata, seq, k->key, k->shift, k->ctrl, k->alt)){
unsigned modifiers = (k->shift ? NCKEY_MOD_SHIFT : 0)
| (k->alt ? NCKEY_MOD_ALT : 0)
| (k->ctrl ? NCKEY_MOD_CTRL : 0);
if(inputctx_add_input_escape(&ictx->amata, seq, k->key, k->shift, k->ctrl, k->alt, modifiers)){
return -1;
}
logdebug("support for terminfo's %s: %s", k->tinfo, seq);
Expand All @@ -347,7 +365,7 @@ prep_special_keys(inputctx* ictx){
logwarn("no backspace key was defined");
}else{
if(bs[0] == NCKEY_ESC){
if(inputctx_add_input_escape(&ictx->amata, bs, NCKEY_BACKSPACE, 0, 0, 0)){
if(inputctx_add_input_escape(&ictx->amata, bs, NCKEY_BACKSPACE, 0, 0, 0, 0)){
return -1;
}
}else{
Expand Down Expand Up @@ -624,16 +642,21 @@ xtmodkey(inputctx* ictx, int val, int mods){
if(mods == 2 || mods == 4 || mods == 6 || mods == 8 || mods == 10
|| mods == 12 || mods == 14 || mods == 16){
tni.shift = 1;
tni.modifiers |= NCKEY_MOD_SHIFT;
}
if(mods == 5 || mods == 6 || mods == 7 || mods == 8 ||
(mods >= 13 && mods <= 16)){
tni.ctrl = 1;
tni.modifiers |= NCKEY_MOD_CTRL;
}
if(mods == 3 || mods == 4 || mods == 7 || mods == 8 || mods == 11
|| mods == 12 || mods == 15 || mods == 16){
tni.alt = 1;
tni.modifiers |= NCKEY_MOD_ALT;
}
if(mods >= 9 && mods <= 16){
tni.modifiers |= NCKEY_MOD_META;
}
// FIXME 9..16 indicate Meta
load_ncinput(ictx, &tni, 0);
}

Expand Down Expand Up @@ -697,21 +720,26 @@ kitty_kbd(inputctx* ictx, int val, int mods, int evtype){
.alt = mods && !!((mods - 1) & 0x2),
.ctrl = mods && !!((mods - 1) & 0x4),
};
// FIXME decode remaining modifiers through 128
// standard keyboard protocol reports ctrl+ascii as the capital form,
// so (for now) conform when using kitty protocol...
if(tni.shift){
tni.modifiers |= NCKEY_MOD_SHIFT;
}
if(tni.ctrl){
if(tni.id < 128 && islower(tni.id)){
tni.id = toupper(tni.id);
}
if(!tni.alt && !tni.shift){
if(tni.id == 'C'){
synth = SIGINT;
}else if(tni.id == '\\'){
synth = SIGQUIT;
}else if(tni.id == 'Z'){
synth = SIGSTOP;
}
tni.modifiers |= NCKEY_MOD_CTRL;
}
if(tni.alt){
tni.modifiers |= NCKEY_MOD_ALT;
}
if(mods && ((mods - 1) & 0x20)){
tni.modifiers |= NCKEY_MOD_META;
}
// FIXME decode remaining modifiers super, hyper, caps_lock, num_lock
if(tni.modifiers == NCKEY_MOD_CTRL){ // exclude all other modifiers
if(tni.id == 'c'){
synth = SIGINT;
}else if(tni.id == '\\'){
synth = SIGQUIT;
}else if(tni.id == 'z'){
synth = SIGSTOP;
}
}
switch(evtype){
Expand Down Expand Up @@ -1726,19 +1754,23 @@ prep_kitty_special_keys(inputctx* ictx){
const char* esc;
uint32_t key;
bool shift, ctrl, alt;
unsigned modifiers;
} keys[] = {
{ .esc = "\x1b[P", .key = NCKEY_F01, },
{ .esc = "\x1b[Q", .key = NCKEY_F02, },
{ .esc = "\x1b[R", .key = NCKEY_F03, },
{ .esc = "\x1b[S", .key = NCKEY_F04, },
{ .esc = "\x1b[127;2u", .key = NCKEY_BACKSPACE, .shift = 1, },
{ .esc = "\x1b[127;3u", .key = NCKEY_BACKSPACE, .alt = 1, },
{ .esc = "\x1b[127;5u", .key = NCKEY_BACKSPACE, .ctrl = 1, },
{ .esc = "\x1b[127;2u", .key = NCKEY_BACKSPACE, .shift = 1,
.modifiers = NCKEY_MOD_SHIFT, },
{ .esc = "\x1b[127;3u", .key = NCKEY_BACKSPACE, .alt = 1,
.modifiers = NCKEY_MOD_ALT, },
{ .esc = "\x1b[127;5u", .key = NCKEY_BACKSPACE, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = NULL, .key = 0, },
}, *k;
for(k = keys ; k->esc ; ++k){
if(inputctx_add_input_escape(&ictx->amata, k->esc, k->key,
k->shift, k->ctrl, k->alt)){
k->shift, k->ctrl, k->alt, k->modifiers)){
return -1;
}
}
Expand All @@ -1757,15 +1789,20 @@ prep_windows_special_keys(inputctx* ictx){
const char* esc;
uint32_t key;
bool shift, ctrl, alt;
unsigned modifiers;
} keys[] = {
{ .esc = "\x1b[A", .key = NCKEY_UP, },
{ .esc = "\x1b[B", .key = NCKEY_DOWN, },
{ .esc = "\x1b[C", .key = NCKEY_RIGHT, },
{ .esc = "\x1b[D", .key = NCKEY_LEFT, },
{ .esc = "\x1b[1;5A", .key = NCKEY_UP, .ctrl = 1, },
{ .esc = "\x1b[1;5B", .key = NCKEY_DOWN, .ctrl = 1, },
{ .esc = "\x1b[1;5C", .key = NCKEY_RIGHT, .ctrl = 1, },
{ .esc = "\x1b[1;5D", .key = NCKEY_LEFT, .ctrl = 1, },
{ .esc = "\x1b[1;5A", .key = NCKEY_UP, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = "\x1b[1;5B", .key = NCKEY_DOWN, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = "\x1b[1;5C", .key = NCKEY_RIGHT, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = "\x1b[1;5D", .key = NCKEY_LEFT, .ctrl = 1,
.modifiers = NCKEY_MOD_CTRL, },
{ .esc = "\x1b[H", .key = NCKEY_HOME, },
{ .esc = "\x1b[F", .key = NCKEY_END, },
{ .esc = "\x1b[2~", .key = NCKEY_INS, },
Expand All @@ -1788,7 +1825,8 @@ prep_windows_special_keys(inputctx* ictx){
}, *k;
for(k = keys ; k->esc ; ++k){
if(inputctx_add_input_escape(&ictx->amata, k->esc, k->key,
k->shift, k->ctrl, k->alt)){
k->shift, k->ctrl, k->alt,
k->modifiers)){
return -1;
}
logdebug("added %s %u", k->esc, k->key);
Expand Down

0 comments on commit 53fbbae

Please sign in to comment.