mpv/osdep/terminal-unix.c

623 lines
19 KiB
C
Raw Normal View History

/*
* Based on GyS-TermIO v2.0 (for GySmail v3) (copyright (C) 1999 A'rpi/ESP-team)
*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <assert.h>
#include <termios.h>
#include <unistd.h>
#include "osdep/io.h"
#include "osdep/threads.h"
#include "osdep/poll_wrapper.h"
#include "common/common.h"
#include "misc/bstr.h"
2013-12-17 01:23:09 +01:00
#include "input/input.h"
#include "input/keycodes.h"
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
#include "misc/ctype.h"
#include "terminal.h"
// Timeout in ms after which the (normally ambiguous) ESC key is detected.
#define ESC_TIMEOUT 100
// Timeout in ms after which the poll for input is aborted. The FG/BG state is
// tested before every wait, and a positive value allows reactivating input
// processing when mpv is brought to the foreground while it was running in the
// background. In such a situation, an infinite timeout (-1) will keep mpv
// waiting for input without realizing the terminal state changed - and thus
// buffer all keypresses until ENTER is pressed.
#define INPUT_TIMEOUT 1000
static struct termios tio_orig;
static int tty_in = -1, tty_out = -1;
enum entry_type {
ENTRY_TYPE_KEY = 0,
ENTRY_TYPE_MOUSE_BUTTON,
ENTRY_TYPE_MOUSE_MOVE,
};
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
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;
// Extend the match length by a certain length, so the contents
// after the match can be processed with custom logic.
int skip;
enum entry_type type;
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
};
static const struct key_entry keys[] = {
{"\010", MP_KEY_BS},
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
{"\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},
{"\033OA", MP_KEY_UP},
{"\033OB", MP_KEY_DOWN},
{"\033OC", MP_KEY_RIGHT},
{"\033OD", MP_KEY_LEFT},
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
{"\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},
// Mouse button inputs. 2 bytes of position information requires special processing.
{"\033[M ", MP_MBTN_LEFT | MP_KEY_STATE_DOWN, .skip = 2, .type = ENTRY_TYPE_MOUSE_BUTTON},
{"\033[M!", MP_MBTN_MID | MP_KEY_STATE_DOWN, .skip = 2, .type = ENTRY_TYPE_MOUSE_BUTTON},
{"\033[M\"", MP_MBTN_RIGHT | MP_KEY_STATE_DOWN, .skip = 2, .type = ENTRY_TYPE_MOUSE_BUTTON},
{"\033[M#", MP_INPUT_RELEASE_ALL, .skip = 2, .type = ENTRY_TYPE_MOUSE_BUTTON},
{"\033[M`", MP_WHEEL_UP, .skip = 2, .type = ENTRY_TYPE_MOUSE_BUTTON},
{"\033[Ma", MP_WHEEL_DOWN, .skip = 2, .type = ENTRY_TYPE_MOUSE_BUTTON},
// Mouse move inputs. No key events should be generated for them.
{"\033[M@", MP_MBTN_LEFT | MP_KEY_STATE_DOWN, .skip = 2, .type = ENTRY_TYPE_MOUSE_MOVE},
{"\033[MA", MP_MBTN_MID | MP_KEY_STATE_DOWN, .skip = 2, .type = ENTRY_TYPE_MOUSE_MOVE},
{"\033[MB", MP_MBTN_RIGHT | MP_KEY_STATE_DOWN, .skip = 2, .type = ENTRY_TYPE_MOUSE_MOVE},
{"\033[MC", MP_INPUT_RELEASE_ALL, .skip = 2, .type = ENTRY_TYPE_MOUSE_MOVE},
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
{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 void process_input(struct input_ctx *input_ctx, bool timeout)
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
{
while (buf.len) {
// Lone ESC is ambiguous, so accept it only after a timeout.
if (timeout &&
((buf.len == 1 && buf.b[0] == '\033') ||
(buf.len > 1 && buf.b[0] == '\033' && buf.b[1] == '\033')))
{
mp_input_put_key(input_ctx, MP_KEY_ESC);
skip_buf(&buf, 1);
}
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
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
int mods = 0;
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
if (buf.b[0] == '\033') {
if (buf.len > 1 && buf.b[1] == '[') {
// Throw away unrecognized mouse CSI sequences.
// Cannot be handled by the loop below since the bytes
// afterwards can be out of that range.
if (buf.len > 2 && buf.b[2] == 'M') {
skip_buf(&buf, buf.len);
continue;
}
// unknown CSI sequence. wait till it completes
for (int i = 2; i < buf.len; i++) {
if (buf.b[i] >= 0x40 && buf.b[i] <= 0x7E) {
skip_buf(&buf, i+1);
continue; // complete - throw it away
}
}
goto read_more; // not yet complete
}
// non-CSI esc sequence
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
skip_buf(&buf, 1);
if (buf.len > 0 && buf.b[0] > 0 && buf.b[0] < 127) {
// meta+normal key
mods |= MP_KEY_MODIFIER_ALT;
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
} else {
// Throw it away. Typically, this will be a complete,
// unsupported sequence, and dropping this will skip it.
skip_buf(&buf, buf.len);
continue;
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
}
}
unsigned char c = buf.b[0];
skip_buf(&buf, 1);
if (c < 32) {
// 1..26 is ^A..^Z, and 27..31 is ^3..^7
c = c <= 26 ? (c + 'a' - 1) : (c + '3' - 27);
mods |= MP_KEY_MODIFIER_CTRL;
}
mp_input_put_key(input_ctx, c | mods);
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
continue;
}
int seq_len = strlen(match->seq) + match->skip;
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
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;
}
// Parse the initially skipped mouse position information.
// The positions are 1-based character cell positions plus 32.
// Treat mouse position as the pixel values at the center of the cell.
if ((match->type == ENTRY_TYPE_MOUSE_BUTTON ||
match->type == ENTRY_TYPE_MOUSE_MOVE) && seq_len >= 6)
{
int num_rows = 80;
int num_cols = 25;
int total_px_width = 0;
int total_px_height = 0;
terminal_get_size2(&num_rows, &num_cols, &total_px_width, &total_px_height);
mp_input_set_mouse_pos(input_ctx,
(buf.b[4] - 32.5) * (total_px_width / num_cols),
(buf.b[5] - 32.5) * (total_px_height / num_rows));
}
if (match->type != ENTRY_TYPE_MOUSE_MOVE)
mp_input_put_key(input_ctx, buf.mods | match->mpkey);
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
skip_buf(&buf, seq_len);
}
read_more: ; /* need more bytes */
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
}
static int getch2_active = 0;
static int getch2_enabled = 0;
static bool read_terminal;
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
static void enable_kx(bool enable)
{
// This check is actually always true, as enable_kx calls are all guarded
// by read_terminal, which is true only if both stdin and stdout are a
// tty. Note that stderr being redirected away has no influence over mpv's
// I/O handling except for disabling the terminal OSD, and thus stderr
// shouldn't be relied on here either.
if (isatty(tty_out)) {
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
char *cmd = enable ? "\033=" : "\033>";
(void)write(tty_out, cmd, strlen(cmd));
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
}
}
static void do_activate_getch2(void)
{
if (getch2_active || !read_terminal)
return;
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
enable_kx(true);
struct termios tio_new;
2024-03-14 02:51:37 +01:00
tcgetattr(tty_in, &tio_new);
tio_new.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tio_new.c_cc[VMIN] = 1;
tio_new.c_cc[VTIME] = 0;
2024-03-14 02:51:37 +01:00
tcsetattr(tty_in, TCSANOW, &tio_new);
getch2_active = 1;
}
static void do_deactivate_getch2(void)
{
if (!getch2_active)
return;
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
enable_kx(false);
tcsetattr(tty_in, TCSANOW, &tio_orig);
getch2_active = 0;
}
// sigaction wrapper
static int setsigaction(int signo, void (*handler) (int),
int flags, bool do_mask)
{
struct sigaction sa;
sa.sa_handler = handler;
if (do_mask)
sigfillset(&sa.sa_mask);
else
sigemptyset(&sa.sa_mask);
sa.sa_flags = flags | SA_RESTART;
return sigaction(signo, &sa, NULL);
}
static void getch2_poll(void)
{
if (!getch2_enabled)
return;
// check if stdin is in the foreground process group
int newstatus = (tcgetpgrp(tty_in) == getpgrp());
// and activate getch2 if it is, deactivate otherwise
if (newstatus)
do_activate_getch2();
else
do_deactivate_getch2();
}
2023-10-21 04:55:41 +02:00
static mp_thread input_thread;
static struct input_ctx *input_ctx;
static int death_pipe[2] = {-1, -1};
enum { PIPE_STOP, PIPE_CONT };
static int stop_cont_pipe[2] = {-1, -1};
static void stop_cont_sighandler(int signum)
{
int saved_errno = errno;
char sig = signum == SIGCONT ? PIPE_CONT : PIPE_STOP;
(void)write(stop_cont_pipe[1], &sig, 1);
errno = saved_errno;
}
static void safe_close(int *p)
{
if (*p >= 0)
close(*p);
*p = -1;
}
static void close_sig_pipes(void)
{
for (int n = 0; n < 2; n++) {
safe_close(&death_pipe[n]);
safe_close(&stop_cont_pipe[n]);
}
}
static void close_tty(void)
{
if (tty_in >= 0 && tty_in != STDIN_FILENO)
close(tty_in);
tty_in = tty_out = -1;
}
static void quit_request_sighandler(int signum)
{
int saved_errno = errno;
(void)write(death_pipe[1], &(char){1}, 1);
errno = saved_errno;
}
2023-10-21 04:55:41 +02:00
static MP_THREAD_VOID terminal_thread(void *ptr)
{
2023-10-21 04:55:41 +02:00
mp_thread_set_name("terminal/input");
bool stdin_ok = read_terminal; // if false, we still wait for SIGTERM
while (1) {
getch2_poll();
struct pollfd fds[3] = {
{ .events = POLLIN, .fd = death_pipe[0] },
{ .events = POLLIN, .fd = stop_cont_pipe[0] },
{ .events = POLLIN, .fd = tty_in }
};
/*
* if the process isn't in foreground process group, then on macos
* polldev() doesn't rest and gets into 100% cpu usage (see issue #11795)
* with read() returning EIO. but we shouldn't quit on EIO either since
* the process might be foregrounded later.
*
* so just avoid poll-ing tty_in when we know the process is not in the
* foreground. there's a small race window, but the timeout will take
* care of it so it's fine.
*/
bool is_fg = tcgetpgrp(tty_in) == getpgrp();
int r = polldev(fds, stdin_ok && is_fg ? 3 : 2, buf.len ? ESC_TIMEOUT : INPUT_TIMEOUT);
if (fds[0].revents) {
do_deactivate_getch2();
break;
}
if (fds[1].revents & POLLIN) {
int8_t c = -1;
(void)read(stop_cont_pipe[0], &c, 1);
if (c == PIPE_STOP) {
do_deactivate_getch2();
if (isatty(STDERR_FILENO)) {
(void)write(STDERR_FILENO, TERM_ESC_RESTORE_CURSOR,
sizeof(TERM_ESC_RESTORE_CURSOR) - 1);
}
// trying to reset SIGTSTP handler to default and raise it will
// result in a race and there's no other way to invoke the
// default handler. so just invoke SIGSTOP since it's
// effectively the same thing.
raise(SIGSTOP);
} else if (c == PIPE_CONT) {
getch2_poll();
}
}
if (fds[2].revents) {
int retval = read(tty_in, &buf.b[buf.len], BUF_LEN - buf.len);
if (!retval || (retval == -1 && errno != EINTR && errno != EAGAIN && errno != EIO))
break; // EOF/closed
if (retval > 0) {
buf.len += retval;
process_input(input_ctx, false);
}
}
if (r == 0)
process_input(input_ctx, true);
}
char c;
bool quit = read(death_pipe[0], &c, 1) == 1 && c == 1;
// Important if we received SIGTERM, rather than regular quit.
if (quit) {
struct mp_cmd *cmd = mp_input_parse_cmd(input_ctx, bstr0("quit 4"), "");
if (cmd)
mp_input_queue_cmd(input_ctx, cmd);
}
2023-10-21 04:55:41 +02:00
MP_THREAD_RETURN();
}
void terminal_setup_getch(struct input_ctx *ictx)
{
if (!getch2_enabled || input_ctx)
return;
if (mp_make_wakeup_pipe(death_pipe) < 0)
return;
// Disable reading from the terminal even if stdout is not a tty, to make
// mpv ... | less
// do the right thing.
read_terminal = isatty(tty_in) && isatty(STDOUT_FILENO);
input_ctx = ictx;
2023-10-21 04:55:41 +02:00
if (mp_thread_create(&input_thread, terminal_thread, NULL)) {
input_ctx = NULL;
close_sig_pipes();
close_tty();
return;
}
setsigaction(SIGINT, quit_request_sighandler, SA_RESETHAND, false);
setsigaction(SIGQUIT, quit_request_sighandler, 0, true);
setsigaction(SIGTERM, quit_request_sighandler, 0, true);
}
void terminal_uninit(void)
{
if (!getch2_enabled)
return;
// restore signals
setsigaction(SIGCONT, SIG_DFL, 0, false);
setsigaction(SIGTSTP, SIG_DFL, 0, false);
setsigaction(SIGINT, SIG_DFL, 0, false);
setsigaction(SIGQUIT, SIG_DFL, 0, false);
setsigaction(SIGTERM, SIG_DFL, 0, false);
setsigaction(SIGTTIN, SIG_DFL, 0, false);
setsigaction(SIGTTOU, SIG_DFL, 0, false);
if (input_ctx) {
(void)write(death_pipe[1], &(char){0}, 1);
2023-10-21 04:55:41 +02:00
mp_thread_join(input_thread);
close_sig_pipes();
input_ctx = NULL;
}
do_deactivate_getch2();
close_tty();
getch2_enabled = 0;
read_terminal = false;
}
bool terminal_in_background(void)
{
return read_terminal && tcgetpgrp(STDERR_FILENO) != getpgrp();
}
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
void terminal_get_size(int *w, int *h)
{
struct winsize ws;
if (ioctl(tty_in, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col)
terminal-unix: new input handling code This is independent of terminfo/termcap, and supports more keys. Originally, the goal was just extending the set of supported key sequences, but since the terminfo stuff actually makes this much harder, and since it's a big blob of bloated legacy crap, just drop it. Instead, use hardcoded tables. It's pretty easy to get on the same level as the old code (with fewer LOC), and we avoid additional error situations, such as mallocs which could fail (the old code just ignores malloc failures). We also try to support some xterm escape sequences, which are in relatively widespread use. (I'm not sure about the urxvt ones.) Trying to deal with xterm shift/ctrl/alt modifiers is probably a bit overcomplicated, and only deals with prefixes - xterm randomly uses prefix sequences for some keys, and suffixes for others (what the heck). Additionally, try to drop unknown escape codes. This basically relies on a trick: in almost 100% of all situations, a read() call will actually return complete sequences (possibly because of pipe semantics and atomic writes from the terminal emulator?), so it's easy to drop unknown sequences. This prevents that they trigger random key bindings as the code interprets the part after ESC as normal keys. This also drops the use of terminfo for sending smkx/rmkx. It seems even vt100 (to which virtually everything non-legacy is reasonably compatible with) supports the codes we hardcode, so it should be fine. This commit actually changes only the code if terminfo/termcap are not found. The next commit will make this code default.
2014-08-21 22:24:33 +02:00
return;
*w = ws.ws_col;
*h = ws.ws_row;
}
void terminal_get_size2(int *rows, int *cols, int *px_width, int *px_height)
{
struct winsize ws;
if (ioctl(tty_in, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col
|| !ws.ws_xpixel || !ws.ws_ypixel)
return;
*rows = ws.ws_row;
*cols = ws.ws_col;
*px_width = ws.ws_xpixel;
*px_height = ws.ws_ypixel;
}
void terminal_set_mouse_input(bool enable)
{
printf(enable ? TERM_ESC_ENABLE_MOUSE : TERM_ESC_DISABLE_MOUSE);
fflush(stdout);
}
2017-06-27 17:38:49 +02:00
void terminal_init(void)
{
assert(!getch2_enabled);
getch2_enabled = 1;
if (mp_make_wakeup_pipe(stop_cont_pipe) < 0) {
getch2_enabled = 0;
return;
}
tty_in = tty_out = open("/dev/tty", O_RDWR | O_CLOEXEC);
if (tty_in < 0) {
tty_in = STDIN_FILENO;
tty_out = STDOUT_FILENO;
}
tcgetattr(tty_in, &tio_orig);
// handlers to fix terminal settings
setsigaction(SIGCONT, stop_cont_sighandler, 0, true);
setsigaction(SIGTSTP, stop_cont_sighandler, 0, true);
setsigaction(SIGTTIN, SIG_IGN, 0, true);
setsigaction(SIGTTOU, SIG_IGN, 0, true);
getch2_poll();
}