mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
224 lines
6.4 KiB
C
224 lines
6.4 KiB
C
#if 0
|
||
/*─────────────────────────────────────────────────────────────────╗
|
||
│ To the extent possible under law, Justine Tunney has waived │
|
||
│ all copyright and related or neighboring rights to this file, │
|
||
│ as it is written in the following disclaimers: │
|
||
│ • http://unlicense.org/ │
|
||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||
╚─────────────────────────────────────────────────────────────────*/
|
||
#endif
|
||
#include "dsp/tty/tty.h"
|
||
#include "libc/assert.h"
|
||
#include "libc/calls/calls.h"
|
||
#include "libc/calls/internal.h"
|
||
#include "libc/calls/struct/sigaction.h"
|
||
#include "libc/calls/struct/sigset.h"
|
||
#include "libc/calls/termios.h"
|
||
#include "libc/ctype.h"
|
||
#include "libc/errno.h"
|
||
#include "libc/log/check.h"
|
||
#include "libc/log/log.h"
|
||
#include "libc/runtime/runtime.h"
|
||
#include "libc/sock/select.h"
|
||
#include "libc/stdio/dprintf.h"
|
||
#include "libc/stdio/stdio.h"
|
||
#include "libc/str/str.h"
|
||
#include "libc/sysv/consts/exit.h"
|
||
#include "libc/sysv/consts/fileno.h"
|
||
#include "libc/sysv/consts/o.h"
|
||
#include "libc/sysv/consts/sig.h"
|
||
#include "libc/sysv/consts/termios.h"
|
||
#include "libc/x/xsigaction.h"
|
||
|
||
#define CTRL(C) ((C) ^ 0b01000000)
|
||
#define WRITE(FD, SLIT) write(FD, SLIT, strlen(SLIT))
|
||
#define ENABLE_SAFE_PASTE "\e[?2004h"
|
||
#define ENABLE_MOUSE_TRACKING "\e[?1000;1002;1015;1006h"
|
||
#define DISABLE_MOUSE_TRACKING "\e[?1000;1002;1015;1006l"
|
||
#define PROBE_DISPLAY_SIZE "\e7\e[9979;9979H\e[6n\e8"
|
||
|
||
char code[512];
|
||
int infd, outfd;
|
||
struct winsize wsize;
|
||
struct termios oldterm;
|
||
volatile bool killed, resized, resurrected;
|
||
|
||
void OnKilled(int sig) {
|
||
killed = true;
|
||
}
|
||
|
||
void OnResize(int sig) {
|
||
resized = true;
|
||
}
|
||
|
||
void OnResurrect(int sig) {
|
||
resized = true;
|
||
resurrected = true;
|
||
}
|
||
|
||
void RestoreTty(void) {
|
||
WRITE(outfd, DISABLE_MOUSE_TRACKING);
|
||
tcsetattr(outfd, TCSANOW, &oldterm);
|
||
}
|
||
|
||
int EnableRawMode(void) {
|
||
static bool once;
|
||
struct termios t;
|
||
if (!once) {
|
||
if (!tcgetattr(outfd, &oldterm)) {
|
||
atexit(RestoreTty);
|
||
} else {
|
||
perror("tcgetattr");
|
||
}
|
||
once = true;
|
||
}
|
||
memcpy(&t, &oldterm, sizeof(t));
|
||
|
||
t.c_cc[VMIN] = 1;
|
||
t.c_cc[VTIME] = 0;
|
||
|
||
// emacs does the following to remap ctrl-c to ctrl-g in termios
|
||
// t.c_cc[VINTR] = CTRL('G');
|
||
// it can be restored using
|
||
// (set-quit-char (logxor ?C 0100))
|
||
// but we are able to polyfill the remapping on windows
|
||
// please note this is a moot point b/c ISIG is cleared
|
||
|
||
t.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON |
|
||
IGNBRK | BRKINT);
|
||
t.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL);
|
||
t.c_cflag &= ~(CSIZE | PARENB);
|
||
t.c_oflag |= OPOST | ONLCR;
|
||
t.c_cflag |= CS8;
|
||
t.c_iflag |= IUTF8;
|
||
|
||
if (tcsetattr(outfd, TCSANOW, &t)) {
|
||
perror("tcsetattr");
|
||
}
|
||
|
||
/* WRITE(outfd, ENABLE_MOUSE_TRACKING); */
|
||
/* WRITE(outfd, ENABLE_SAFE_PASTE); */
|
||
/* WRITE(outfd, PROBE_DISPLAY_SIZE); */
|
||
return 0;
|
||
}
|
||
|
||
void GetTtySize(void) {
|
||
if (tcgetwinsize(outfd, &wsize) != -1) {
|
||
dprintf(outfd, "termios says terminal size is %hu×%hu\r\n", wsize.ws_col,
|
||
wsize.ws_row);
|
||
} else {
|
||
perror("tcgetwinsize");
|
||
}
|
||
}
|
||
|
||
const char *describemouseevent(int e) {
|
||
static char buf[64];
|
||
buf[0] = 0;
|
||
if (e & 0x10) {
|
||
strcat(buf, " ctrl");
|
||
}
|
||
if (e & 0x40) {
|
||
strcat(buf, " wheel");
|
||
if (e & 0x01) {
|
||
strcat(buf, " down");
|
||
} else {
|
||
strcat(buf, " up");
|
||
}
|
||
} else {
|
||
switch (e & 3) {
|
||
case 0:
|
||
strcat(buf, " left");
|
||
break;
|
||
case 1:
|
||
strcat(buf, " middle");
|
||
break;
|
||
case 2:
|
||
strcat(buf, " right");
|
||
break;
|
||
default:
|
||
__builtin_unreachable();
|
||
}
|
||
if (e & 0x20) {
|
||
strcat(buf, " drag");
|
||
} else if (e & 0x04) {
|
||
strcat(buf, " up");
|
||
} else {
|
||
strcat(buf, " down");
|
||
}
|
||
}
|
||
return buf + 1;
|
||
}
|
||
|
||
// change the code above to enable ISIG if you want to trigger this
|
||
// then press ctrl-c or ctrl-\ in your pseudoteletypewriter console
|
||
void OnSignalThatWontEintrRead(int sig) {
|
||
dprintf(outfd, "got %s (read()ing will SA_RESTART; try CTRL-D to exit)\n",
|
||
strsignal(sig));
|
||
}
|
||
|
||
void OnSignalThatWillEintrRead(int sig) {
|
||
dprintf(outfd, "got %s (read() operation will be aborted)\n", strsignal(sig));
|
||
}
|
||
|
||
int main(int argc, char *argv[]) {
|
||
|
||
// // emacs sends this to enable decckm mode
|
||
// WRITE(1, "\e[?1049h\e[22;0;0t\e[?12;25h\e[?1h\e=");
|
||
|
||
int e, c, y, x, n, yn, xn;
|
||
infd = 0;
|
||
outfd = 1;
|
||
infd = outfd = open("/dev/tty", O_RDWR);
|
||
signal(SIGINT, OnSignalThatWontEintrRead);
|
||
sigaction(SIGQUIT,
|
||
&(struct sigaction){.sa_handler = OnSignalThatWillEintrRead}, 0);
|
||
sigaction(SIGTERM, &(struct sigaction){.sa_handler = OnKilled}, 0);
|
||
sigaction(SIGWINCH, &(struct sigaction){.sa_handler = OnResize}, 0);
|
||
sigaction(SIGCONT, &(struct sigaction){.sa_handler = OnResurrect}, 0);
|
||
EnableRawMode();
|
||
GetTtySize();
|
||
while (!killed) {
|
||
if (resurrected) {
|
||
dprintf(outfd, "WE LIVE AGAIN ");
|
||
resurrected = false;
|
||
}
|
||
if (resized) {
|
||
dprintf(outfd, "SIGWINCH ");
|
||
GetTtySize();
|
||
resized = false;
|
||
}
|
||
bzero(code, sizeof(code));
|
||
if ((n = read(infd, code, sizeof(code))) == -1) {
|
||
if (errno == EINTR) {
|
||
dprintf(outfd, "read() was interrupted\n");
|
||
continue;
|
||
}
|
||
perror("read");
|
||
exit(1);
|
||
}
|
||
if (!n) {
|
||
dprintf(outfd, "got stdin eof\n");
|
||
exit(0);
|
||
}
|
||
dprintf(outfd, "%`'.*s (got %d) ", n, code, n);
|
||
if (iscntrl(code[0]) && !code[1]) {
|
||
dprintf(outfd, "is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0]));
|
||
if (code[0] == CTRL('C') || code[0] == CTRL('D'))
|
||
break;
|
||
} else if (startswith(code, "\e[") && endswith(code, "R")) {
|
||
yn = 1, xn = 1;
|
||
sscanf(code, "\e[%d;%dR", &yn, &xn);
|
||
dprintf(outfd, "inband signalling says terminal size is %d×%d\r\n", xn,
|
||
yn);
|
||
} else if (startswith(code, "\e[<") &&
|
||
(endswith(code, "m") || endswith(code, "M"))) {
|
||
e = 0, y = 1, x = 1;
|
||
sscanf(code, "\e[<%d;%d;%d%c", &e, &y, &x, &c);
|
||
dprintf(outfd, "mouse %s at %d×%d\r\n",
|
||
describemouseevent(e | (c == 'm') << 2), x, y);
|
||
} else {
|
||
dprintf(outfd, "\r\n");
|
||
}
|
||
}
|
||
return 0;
|
||
}
|