summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--osdep/terminal-unix.c279
1 files changed, 246 insertions, 33 deletions
diff --git a/osdep/terminal-unix.c b/osdep/terminal-unix.c
index 2bac169f36..78fb3d991c 100644
--- a/osdep/terminal-unix.c
+++ b/osdep/terminal-unix.c
@@ -46,6 +46,7 @@
#include "bstr/bstr.h"
#include "input/input.h"
#include "input/keycodes.h"
+#include "misc/ctype.h"
#include "terminal.h"
#if HAVE_TERMIOS
@@ -53,6 +54,225 @@ static volatile struct termios tio_orig;
static volatile int tio_orig_set;
#endif
+#if !(HAVE_TERMINFO || HAVE_TERMCAP)
+
+struct key_entry {
+ const char *seq;
+ int mpkey;
+ // If this is not NULL, then if seq is matched as unique prefix, the
+ // existing sequence is replaced by the following string. Matching
+ // continues normally, and mpkey is or-ed into the final result.
+ const char *replace;
+};
+
+static const struct key_entry keys[] = {
+ {"\011", MP_KEY_TAB},
+ {"\012", MP_KEY_ENTER},
+ {"\177", MP_KEY_BS},
+
+ {"\033[1~", MP_KEY_HOME},
+ {"\033[2~", MP_KEY_INS},
+ {"\033[3~", MP_KEY_DEL},
+ {"\033[4~", MP_KEY_END},
+ {"\033[5~", MP_KEY_PGUP},
+ {"\033[6~", MP_KEY_PGDWN},
+ {"\033[7~", MP_KEY_HOME},
+ {"\033[8~", MP_KEY_END},
+
+ {"\033[11~", MP_KEY_F+1},
+ {"\033[12~", MP_KEY_F+2},
+ {"\033[13~", MP_KEY_F+3},
+ {"\033[14~", MP_KEY_F+4},
+ {"\033[15~", MP_KEY_F+5},
+ {"\033[17~", MP_KEY_F+6},
+ {"\033[18~", MP_KEY_F+7},
+ {"\033[19~", MP_KEY_F+8},
+ {"\033[20~", MP_KEY_F+9},
+ {"\033[21~", MP_KEY_F+10},
+ {"\033[23~", MP_KEY_F+11},
+ {"\033[24~", MP_KEY_F+12},
+
+ {"\033[A", MP_KEY_UP},
+ {"\033[B", MP_KEY_DOWN},
+ {"\033[C", MP_KEY_RIGHT},
+ {"\033[D", MP_KEY_LEFT},
+ {"\033[E", MP_KEY_KP5},
+ {"\033[F", MP_KEY_END},
+ {"\033[H", MP_KEY_HOME},
+
+ {"\033[[A", MP_KEY_F+1},
+ {"\033[[B", MP_KEY_F+2},
+ {"\033[[C", MP_KEY_F+3},
+ {"\033[[D", MP_KEY_F+4},
+ {"\033[[E", MP_KEY_F+5},
+
+ {"\033OE", MP_KEY_KP5}, // mintty?
+ {"\033OM", MP_KEY_KPENTER},
+ {"\033OP", MP_KEY_F+1},
+ {"\033OQ", MP_KEY_F+2},
+ {"\033OR", MP_KEY_F+3},
+ {"\033OS", MP_KEY_F+4},
+
+ {"\033Oa", MP_KEY_UP | MP_KEY_MODIFIER_CTRL}, // urxvt
+ {"\033Ob", MP_KEY_DOWN | MP_KEY_MODIFIER_CTRL},
+ {"\033Oc", MP_KEY_RIGHT | MP_KEY_MODIFIER_CTRL},
+ {"\033Od", MP_KEY_LEFT | MP_KEY_MODIFIER_CTRL},
+ {"\033Oj", '*'}, // also keypad, but we don't have separate codes for them
+ {"\033Ok", '+'},
+ {"\033Om", '-'},
+ {"\033On", MP_KEY_KPDEC},
+ {"\033Oo", '/'},
+ {"\033Op", MP_KEY_KP0},
+ {"\033Oq", MP_KEY_KP1},
+ {"\033Or", MP_KEY_KP2},
+ {"\033Os", MP_KEY_KP3},
+ {"\033Ot", MP_KEY_KP4},
+ {"\033Ou", MP_KEY_KP5},
+ {"\033Ov", MP_KEY_KP6},
+ {"\033Ow", MP_KEY_KP7},
+ {"\033Ox", MP_KEY_KP8},
+ {"\033Oy", MP_KEY_KP9},
+
+ {"\033[a", MP_KEY_UP | MP_KEY_MODIFIER_SHIFT}, // urxvt
+ {"\033[b", MP_KEY_DOWN | MP_KEY_MODIFIER_SHIFT},
+ {"\033[c", MP_KEY_RIGHT | MP_KEY_MODIFIER_SHIFT},
+ {"\033[d", MP_KEY_LEFT | MP_KEY_MODIFIER_SHIFT},
+ {"\033[2^", MP_KEY_INS | MP_KEY_MODIFIER_CTRL},
+ {"\033[3^", MP_KEY_DEL | MP_KEY_MODIFIER_CTRL},
+ {"\033[5^", MP_KEY_PGUP | MP_KEY_MODIFIER_CTRL},
+ {"\033[6^", MP_KEY_PGDWN | MP_KEY_MODIFIER_CTRL},
+ {"\033[7^", MP_KEY_HOME | MP_KEY_MODIFIER_CTRL},
+ {"\033[8^", MP_KEY_END | MP_KEY_MODIFIER_CTRL},
+
+ {"\033[1;2", MP_KEY_MODIFIER_SHIFT, .replace = "\033["}, // xterm
+ {"\033[1;3", MP_KEY_MODIFIER_ALT, .replace = "\033["},
+ {"\033[1;5", MP_KEY_MODIFIER_CTRL, .replace = "\033["},
+ {"\033[1;4", MP_KEY_MODIFIER_ALT | MP_KEY_MODIFIER_SHIFT, .replace = "\033["},
+ {"\033[1;6", MP_KEY_MODIFIER_CTRL | MP_KEY_MODIFIER_SHIFT, .replace = "\033["},
+ {"\033[1;7", MP_KEY_MODIFIER_CTRL | MP_KEY_MODIFIER_ALT, .replace = "\033["},
+ {"\033[1;8",
+ MP_KEY_MODIFIER_CTRL | MP_KEY_MODIFIER_ALT | MP_KEY_MODIFIER_SHIFT,
+ .replace = "\033["},
+
+ {"\033[29~", MP_KEY_MENU},
+ {"\033[Z", MP_KEY_TAB | MP_KEY_MODIFIER_SHIFT},
+
+ {0}
+};
+
+#define BUF_LEN 256
+
+struct termbuf {
+ unsigned char b[BUF_LEN];
+ int len;
+ int mods;
+};
+
+static void skip_buf(struct termbuf *b, unsigned int count)
+{
+ assert(count <= b->len);
+
+ memmove(&b->b[0], &b->b[count], b->len - count);
+ b->len -= count;
+ b->mods = 0;
+}
+
+static struct termbuf buf;
+
+static bool getch2(struct input_ctx *input_ctx)
+{
+ int retval = read(0, &buf.b[buf.len], BUF_LEN - buf.len);
+ /* Return false on EOF to stop running select() on the FD, as it'd
+ * trigger all the time. Note that it's possible to get temporary
+ * EOF on terminal if the user presses ctrl-d, but that shouldn't
+ * happen if the terminal state change done in getch2_enable()
+ * works.
+ */
+ if (retval == 0)
+ return false;
+ if (retval == -1)
+ return errno != EBADF && errno != EINVAL;
+ buf.len += retval;
+
+ while (buf.len) {
+ int utf8_len = bstr_parse_utf8_code_length(buf.b[0]);
+ if (utf8_len > 1) {
+ if (buf.len < utf8_len)
+ goto read_more;
+
+ mp_input_put_key_utf8(input_ctx, buf.mods, (bstr){buf.b, utf8_len});
+ skip_buf(&buf, utf8_len);
+ continue;
+ }
+
+ const struct key_entry *match = NULL; // may be a partial match
+ for (int n = 0; keys[n].seq; n++) {
+ const struct key_entry *e = &keys[n];
+ if (memcmp(e->seq, buf.b, MPMIN(buf.len, strlen(e->seq))) == 0) {
+ if (match)
+ goto read_more; /* need more bytes to disambiguate */
+ match = e;
+ }
+ }
+
+ if (!match) { // normal or unknown key
+ if (buf.b[0] == '\033') {
+ skip_buf(&buf, 1);
+ if (buf.len > 0 && mp_isalnum(buf.b[0])) { // meta+normal key
+ mp_input_put_key(input_ctx, buf.b[0] | MP_KEY_MODIFIER_ALT);
+ skip_buf(&buf, 1);
+ } else if (buf.len == 1 && buf.b[0] == '\033') {
+ mp_input_put_key(input_ctx, MP_KEY_ESC);
+ skip_buf(&buf, 1);
+ } else {
+ // Throw it away. Typically, this will be a complete,
+ // unsupported sequence, and dropping this will skip it.
+ skip_buf(&buf, buf.len);
+ }
+ } else {
+ mp_input_put_key(input_ctx, buf.b[0]);
+ skip_buf(&buf, 1);
+ }
+ continue;
+ }
+
+ int seq_len = strlen(match->seq);
+ if (seq_len > buf.len)
+ goto read_more; /* partial match */
+
+ if (match->replace) {
+ int rep = strlen(match->replace);
+ assert(rep <= seq_len);
+ memcpy(buf.b, match->replace, rep);
+ memmove(buf.b + rep, buf.b + seq_len, buf.len - seq_len);
+ buf.len = rep + buf.len - seq_len;
+ buf.mods |= match->mpkey;
+ continue;
+ }
+
+ mp_input_put_key(input_ctx, buf.mods | match->mpkey);
+ skip_buf(&buf, seq_len);
+ }
+
+read_more: /* need more bytes */
+ return true;
+}
+
+static void load_termcap(void)
+{
+}
+
+static void enable_kx(bool enable)
+{
+ if (isatty(1)) {
+ char *cmd = enable ? "\033=" : "\033>";
+ printf("%s", cmd);
+ fflush(stdout);
+ }
+}
+
+#else /* terminfo/termcap */
+
typedef struct {
char *cap;
int len;
@@ -68,8 +288,6 @@ typedef struct {
static keycode_map getch2_keys;
-#if HAVE_TERMINFO || HAVE_TERMCAP
-
static char *term_rmkx = NULL;
static char *term_smkx = NULL;
@@ -78,8 +296,6 @@ static char *term_smkx = NULL;
#endif
#include <term.h>
-#endif
-
static keycode_st *keys_push(char *p, int code) {
if (strlen(p) > 8)
return NULL;
@@ -138,8 +354,6 @@ static keycode_st* keys_push_once(char *p, int code) {
return st;
}
-#if HAVE_TERMINFO || HAVE_TERMCAP
-
typedef struct {
char *buf;
char *pos;
@@ -225,10 +439,9 @@ static void termcap_add_extra_f_keys(void) {
}
}
-#endif
-
-static int load_termcap(char *termtype){
-#if HAVE_TERMINFO || HAVE_TERMCAP
+static void load_termcap(void)
+{
+ char *termtype = NULL;
#if HAVE_TERMINFO
use_env(TRUE);
@@ -243,10 +456,10 @@ static int load_termcap(char *termtype){
if (setupterm(termtype, 1, &ret) != OK) {
if (ret < 0) {
printf("Could not access the 'terminfo' data base.\n");
- return 0;
+ return;
} else {
printf("Couldn't use terminal `%s' for input.\n", termtype);
- return 0;
+ return;
}
}
}
@@ -257,10 +470,10 @@ static int load_termcap(char *termtype){
int success = tgetent(term_buffer, termtype);
if (success < 0) {
printf("Could not access the 'termcap' data base.\n");
- return 0;
+ return;
} else if (success == 0) {
printf("Terminal type `%s' is not defined.\n", termtype);
- return 0;
+ return;
}
#endif
ensure_cap(&termcap_buf, 2048);
@@ -299,7 +512,6 @@ static int load_termcap(char *termtype){
termcap_add(keys[i]);
}
termcap_add_extra_f_keys();
-#endif
/* special cases (hardcoded, no need for HAVE_TERMCAP) */
@@ -321,18 +533,13 @@ static int load_termcap(char *termtype){
/* fallback if terminfo and termcap are not available */
keys_push_once("\012", MP_KEY_ENTER);
-
- return getch2_keys.len;
}
-void terminal_get_size(int *w, int *h)
+static void enable_kx(bool enable)
{
- struct winsize ws;
- if (ioctl(0, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col)
- return;
-
- *w = ws.ws_col;
- *h = ws.ws_row;
+ char *cmd = enable ? term_smkx : term_rmkx;
+ if (cmd)
+ tputs(cmd, 1, putchar);
}
#define BUF_LEN 256
@@ -436,6 +643,8 @@ static bool getch2(struct input_ctx *input_ctx)
return true;
}
+#endif /* terminfo/termcap */
+
static int read_keys(void *ctx, int fd)
{
if (getch2(ctx))
@@ -458,10 +667,7 @@ static void do_activate_getch2(void)
if (getch2_active || !isatty(1))
return;
-#if HAVE_TERMINFO || HAVE_TERMCAP
- if (term_smkx)
- tputs(term_smkx, 1, putchar);
-#endif
+ enable_kx(true);
#if HAVE_TERMIOS
struct termios tio_new;
@@ -486,10 +692,7 @@ static void do_deactivate_getch2(void)
if (!getch2_active)
return;
-#if HAVE_TERMINFO || HAVE_TERMCAP
- if (term_rmkx)
- tputs(term_rmkx, 1, putchar);
-#endif
+ enable_kx(false);
#if HAVE_TERMIOS
if (tio_orig_set) {
@@ -598,10 +801,20 @@ bool terminal_in_background(void)
return isatty(2) && tcgetpgrp(2) != getpgrp();
}
+void terminal_get_size(int *w, int *h)
+{
+ struct winsize ws;
+ if (ioctl(0, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col)
+ return;
+
+ *w = ws.ws_col;
+ *h = ws.ws_row;
+}
+
int terminal_init(void)
{
if (isatty(1))
- load_termcap(NULL);
+ load_termcap();
getch2_enable();
return 0;
}