Allow binding to custom key sequences
And document this feature in the manual page.
This commit is contained in:
parent
9056ef4194
commit
fdb338fe12
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2017 - 2020, Přemysl Eric Janouch <p@janouch.name>
|
Copyright (c) 2017 - 2021, Přemysl Eric Janouch <p@janouch.name>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted.
|
purpose with or without fee is hereby granted.
|
||||||
|
|
14
sdn.1
14
sdn.1
|
@ -98,6 +98,20 @@ normal h parent
|
||||||
normal l choose
|
normal l choose
|
||||||
normal M-f4 quit
|
normal M-f4 quit
|
||||||
.Ed
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Midnight Commander binds the same traversal actions to sequences normally
|
||||||
|
unknown to ncurses, due to them being missing from terminfo.
|
||||||
|
You'll need to define them manually to match your terminal.
|
||||||
|
For rxvt, that would be:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
define C-ppage ^[[5^
|
||||||
|
define C-npage ^[[6^
|
||||||
|
normal C-ppage parent
|
||||||
|
normal C-npage choose
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
Escape characters must be inserted verbatim, e.g., by pressing C-v ESC in vi,
|
||||||
|
or C-q ESC in Emacs.
|
||||||
.Ss Pa look
|
.Ss Pa look
|
||||||
Terminal attributes are accepted in a format similar to that of
|
Terminal attributes are accepted in a format similar to that of
|
||||||
.Xr git-config 1 ,
|
.Xr git-config 1 ,
|
||||||
|
|
24
sdn.cpp
24
sdn.cpp
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// sdn: simple directory navigator
|
// sdn: simple directory navigator
|
||||||
//
|
//
|
||||||
// Copyright (c) 2017 - 2020, Přemysl Eric Janouch <p@janouch.name>
|
// Copyright (c) 2017 - 2021, Přemysl Eric Janouch <p@janouch.name>
|
||||||
//
|
//
|
||||||
// Permission to use, copy, modify, and/or distribute this software for any
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
// purpose with or without fee is hereby granted.
|
// purpose with or without fee is hereby granted.
|
||||||
|
@ -544,6 +544,7 @@ static struct {
|
||||||
|
|
||||||
map<string, wint_t, stringcaseless> name_to_key;
|
map<string, wint_t, stringcaseless> name_to_key;
|
||||||
map<wint_t, string> key_to_name;
|
map<wint_t, string> key_to_name;
|
||||||
|
map<string, wint_t> custom_keys;
|
||||||
string action_names[ACTION_COUNT]; ///< Stylized action names
|
string action_names[ACTION_COUNT]; ///< Stylized action names
|
||||||
|
|
||||||
// Refreshed by reload():
|
// Refreshed by reload():
|
||||||
|
@ -1566,7 +1567,9 @@ fun parse_key (const string &key_name) -> wint_t {
|
||||||
c |= ALT;
|
c |= ALT;
|
||||||
p += 2;
|
p += 2;
|
||||||
}
|
}
|
||||||
if (!strncmp (p, "C-", 2)) {
|
if (g.name_to_key.count (p)) {
|
||||||
|
return c | g.name_to_key.at (p);
|
||||||
|
} else if (!strncmp (p, "C-", 2)) {
|
||||||
p += 2;
|
p += 2;
|
||||||
if (*p < '?' || *p > 'z') {
|
if (*p < '?' || *p > 'z') {
|
||||||
cerr << "bindings: invalid combination: " << key_name << endl;
|
cerr << "bindings: invalid combination: " << key_name << endl;
|
||||||
|
@ -1574,8 +1577,6 @@ fun parse_key (const string &key_name) -> wint_t {
|
||||||
}
|
}
|
||||||
c |= CTRL (toupper (*p));
|
c |= CTRL (toupper (*p));
|
||||||
p += 1;
|
p += 1;
|
||||||
} else if (g.name_to_key.count (p)) {
|
|
||||||
return c | g.name_to_key.at (p);
|
|
||||||
} else {
|
} else {
|
||||||
wchar_t w; mbstate_t mb {};
|
wchar_t w; mbstate_t mb {};
|
||||||
auto len = strlen (p) + 1, res = mbrtowc (&w, p, len, &mb);
|
auto len = strlen (p) + 1, res = mbrtowc (&w, p, len, &mb);
|
||||||
|
@ -1604,7 +1605,9 @@ fun learn_named_key (const string &name, wint_t key) {
|
||||||
fun load_bindings () {
|
fun load_bindings () {
|
||||||
learn_named_key ("space", ' ');
|
learn_named_key ("space", ' ');
|
||||||
learn_named_key ("escape", 0x1b);
|
learn_named_key ("escape", 0x1b);
|
||||||
for (int kc = KEY_MIN; kc < KEY_MAX; kc++) {
|
|
||||||
|
int kc = 0;
|
||||||
|
for (kc = KEY_MIN; kc <= KEY_MAX; kc++) {
|
||||||
const char *name = keyname (kc);
|
const char *name = keyname (kc);
|
||||||
if (!name)
|
if (!name)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1638,11 +1641,18 @@ fun load_bindings () {
|
||||||
if (tokens.empty ())
|
if (tokens.empty ())
|
||||||
continue;
|
continue;
|
||||||
if (tokens.size () < 3) {
|
if (tokens.size () < 3) {
|
||||||
cerr << "bindings: expected: context binding action";
|
cerr << "bindings: expected: define name key-sequence"
|
||||||
|
" | context binding action";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto context = tokens[0], key_name = tokens[1], action = tokens[2];
|
auto context = tokens[0], key_name = tokens[1], action = tokens[2];
|
||||||
|
if (context == "define") {
|
||||||
|
// We haven't run initscr() yet, so define_key() would fail here
|
||||||
|
learn_named_key (key_name, SYM | (g.custom_keys[action] = ++kc));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto m = g_binding_contexts.find (context);
|
auto m = g_binding_contexts.find (context);
|
||||||
if (m == g_binding_contexts.end ()) {
|
if (m == g_binding_contexts.end ()) {
|
||||||
cerr << "bindings: invalid context: " << context << endl;
|
cerr << "bindings: invalid context: " << context << endl;
|
||||||
|
@ -1754,6 +1764,8 @@ int main (int argc, char *argv[]) {
|
||||||
cerr << "cannot initialize screen" << endl;
|
cerr << "cannot initialize screen" << endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
for (const auto &definition_kc : g.custom_keys)
|
||||||
|
define_key (definition_kc.first.c_str (), definition_kc.second);
|
||||||
|
|
||||||
load_colors ();
|
load_colors ();
|
||||||
load_cmdline (argc, argv);
|
load_cmdline (argc, argv);
|
||||||
|
|
Loading…
Reference in New Issue