mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Rewrite Windows console input handling
This change removes our use of ENABLE_VIRTUAL_TERMINAL_INPUT (which isn't very good) in favor of having read() translate Windows Console input events to ANSI/XTERM sequences by hand. This makes it possible to capture important keystrokes (e.g. ctrl-space) that weren't possible before. Most importantly this change also removes the stdin/sigwinch worker threads, which never really worked that well. Interactive TTY sessions will now work reliably when a Cosmo process spawns or forks another Cosmo process, e.g. unbourne.com launching emacs.com.
This commit is contained in:
parent
ececec4c94
commit
d6c2830850
27 changed files with 635 additions and 464 deletions
|
@ -28,8 +28,6 @@
|
|||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/x/xsigaction.h"
|
||||
|
||||
__static_yoink("WinMainStdin");
|
||||
|
||||
#define CTRL(C) ((C) ^ 0b01000000)
|
||||
#define WRITE(FD, SLIT) write(FD, SLIT, strlen(SLIT))
|
||||
#define ENABLE_SAFE_PASTE "\e[?2004h"
|
||||
|
@ -69,7 +67,7 @@ int rawmode(void) {
|
|||
memcpy(&t, &oldterm, sizeof(t));
|
||||
|
||||
t.c_cc[VMIN] = 1;
|
||||
t.c_cc[VTIME] = 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');
|
||||
|
|
14
libc/calls/console.internal.h
Normal file
14
libc/calls/console.internal.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_CONSOLE_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_CONSOLE_INTERNAL_H_
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/nt/struct/inputrecord.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int CountConsoleInputBytes(int64_t);
|
||||
int ConvertConsoleInputToAnsi(const struct NtInputRecord *, char[hasatleast 32],
|
||||
uint16_t *, struct Fd *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_CONSOLE_INTERNAL_H_ */
|
|
@ -16,17 +16,41 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/struct/inputrecord.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows int64_t __resolve_stdin_handle(int64_t handle) {
|
||||
if (handle == g_fds.stdin.handle) {
|
||||
if (g_fds.stdin.inisem) {
|
||||
ReleaseSemaphore(g_fds.stdin.inisem, 1, 0);
|
||||
int CountConsoleInputBytes(int64_t handle) {
|
||||
char buf[32];
|
||||
int rc, e = errno;
|
||||
uint16_t utf16hs = 0;
|
||||
uint32_t i, n, count;
|
||||
struct NtInputRecord records[64];
|
||||
if (PeekConsoleInput(handle, records, ARRAYLEN(records), &n)) {
|
||||
for (rc = i = 0; i < n; ++i) {
|
||||
count = ConvertConsoleInputToAnsi(records + i, buf, &utf16hs, 0);
|
||||
if (count == -1) {
|
||||
unassert(errno == ENODATA);
|
||||
if (!rc) {
|
||||
rc = -1;
|
||||
} else {
|
||||
errno = e;
|
||||
}
|
||||
break;
|
||||
}
|
||||
rc += count;
|
||||
}
|
||||
handle = g_fds.stdin.reader;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
return handle;
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
@ -31,6 +32,7 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
|
@ -54,10 +56,6 @@
|
|||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
__static_yoink("WinMainStdin");
|
||||
#endif
|
||||
|
||||
/* Maximum number of unicast addresses handled for each interface */
|
||||
#define MAX_UNICAST_ADDR 32
|
||||
#define MAX_NAME_CLASH ((int)('z' - 'a')) /* Allow a..z */
|
||||
|
@ -93,13 +91,14 @@ static int ioctl_default(int fd, unsigned long request, void *arg) {
|
|||
}
|
||||
|
||||
static int ioctl_fionread(int fd, uint32_t *arg) {
|
||||
int rc;
|
||||
uint32_t cm;
|
||||
int64_t handle;
|
||||
uint32_t avail;
|
||||
int rc, e = errno;
|
||||
if (!IsWindows()) {
|
||||
return sys_ioctl(fd, FIONREAD, arg);
|
||||
} else if (__isfdopen(fd)) {
|
||||
handle = __resolve_stdin_handle(g_fds.p[fd].handle);
|
||||
handle = g_fds.p[fd].handle;
|
||||
if (g_fds.p[fd].kind == kFdSocket) {
|
||||
if ((rc = _weaken(__sys_ioctlsocket_nt)(handle, FIONREAD, arg)) != -1) {
|
||||
return rc;
|
||||
|
@ -113,6 +112,13 @@ static int ioctl_fionread(int fd, uint32_t *arg) {
|
|||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
} else if (GetConsoleMode(handle, &cm)) {
|
||||
avail = CountConsoleInputBytes(handle);
|
||||
if (avail == -1u && errno == ENODATA) {
|
||||
errno = e;
|
||||
avail = 0;
|
||||
}
|
||||
return avail;
|
||||
} else {
|
||||
return eopnotsupp();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/files.h"
|
||||
|
@ -47,8 +49,6 @@
|
|||
|
||||
#ifdef __x86_64__
|
||||
|
||||
__static_yoink("WinMainStdin");
|
||||
|
||||
/*
|
||||
* Polls on the New Technology.
|
||||
*
|
||||
|
@ -59,8 +59,8 @@ __static_yoink("WinMainStdin");
|
|||
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
||||
const sigset_t *sigmask) {
|
||||
bool ok;
|
||||
uint32_t avail;
|
||||
sigset_t oldmask;
|
||||
uint32_t avail, cm;
|
||||
struct sys_pollfd_nt pipefds[8];
|
||||
struct sys_pollfd_nt sockfds[64];
|
||||
int pipeindices[ARRAYLEN(pipefds)];
|
||||
|
@ -98,7 +98,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
}
|
||||
} else if (pn < ARRAYLEN(pipefds)) {
|
||||
pipeindices[pn] = i;
|
||||
pipefds[pn].handle = __resolve_stdin_handle(g_fds.p[fds[i].fd].handle);
|
||||
pipefds[pn].handle = g_fds.p[fds[i].fd].handle;
|
||||
pipefds[pn].events = 0;
|
||||
pipefds[pn].revents = 0;
|
||||
switch (g_fds.p[fds[i].fd].flags & O_ACCMODE) {
|
||||
|
@ -151,6 +151,19 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
} else {
|
||||
pipefds[i].revents |= POLLERR;
|
||||
}
|
||||
} else if (GetConsoleMode(pipefds[i].handle, &cm)) {
|
||||
int e = errno;
|
||||
avail = CountConsoleInputBytes(pipefds[i].handle);
|
||||
if (avail > 0) {
|
||||
pipefds[i].revents |= POLLIN;
|
||||
} else if (avail == -1u) {
|
||||
if (errno == ENODATA) {
|
||||
pipefds[i].revents |= POLLIN;
|
||||
} else {
|
||||
pipefds[i].revents |= POLLERR;
|
||||
}
|
||||
errno = e;
|
||||
}
|
||||
} else {
|
||||
// we have no way of polling if a non-socket is readable yet
|
||||
// therefore we assume that if it can happen it shall happen
|
||||
|
|
|
@ -17,32 +17,39 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/wincrash.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/enum/vk.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/inputrecord.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
|
@ -50,33 +57,385 @@
|
|||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
__static_yoink("WinMainStdin");
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
|
||||
static const struct {
|
||||
uint16_t vk;
|
||||
uint32_t normal_str;
|
||||
uint32_t shift_str;
|
||||
uint32_t ctrl_str;
|
||||
uint32_t shift_ctrl_str;
|
||||
} kVirtualKey[] = {
|
||||
#define SW(s) W4(s "\0\0")
|
||||
#define W4(s) (s[3] + 0u) << 24 | s[2] << 16 | s[1] << 8 | s[0]
|
||||
#define VK(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \
|
||||
{ vk, SW(normal_str), SW(shift_str), SW(ctrl_str), SW(shift_ctrl_str) }
|
||||
VK(kNtVkInsert, "2~", "2;2~", "2;5~", "2;6~"),
|
||||
VK(kNtVkEnd, "4~", "4;2~", "4;5~", "4;6~"),
|
||||
VK(kNtVkDown, "B", "1;2B", "1;5B", "1;6B"),
|
||||
VK(kNtVkNext, "6~", "6;2~", "6;5~", "6;6~"),
|
||||
VK(kNtVkLeft, "D", "1;2D", "1;5D", "1;6D"),
|
||||
VK(kNtVkClear, "G", "1;2G", "1;5G", "1;6G"),
|
||||
VK(kNtVkRight, "C", "1;2C", "1;5C", "1;6C"),
|
||||
VK(kNtVkUp, "A", "1;2A", "1;5A", "1;6A"),
|
||||
VK(kNtVkHome, "1~", "1;2~", "1;5~", "1;6~"),
|
||||
VK(kNtVkPrior, "5~", "5;2~", "5;5~", "5;6~"),
|
||||
VK(kNtVkDelete, "3~", "3;2~", "3;5~", "3;6~"),
|
||||
VK(kNtVkNumpad0, "2~", "2;2~", "2;5~", "2;6~"),
|
||||
VK(kNtVkNumpad1, "4~", "4;2~", "4;5~", "4;6~"),
|
||||
VK(kNtVkNumpad2, "B", "1;2B", "1;5B", "1;6B"),
|
||||
VK(kNtVkNumpad3, "6~", "6;2~", "6;5~", "6;6~"),
|
||||
VK(kNtVkNumpad4, "D", "1;2D", "1;5D", "1;6D"),
|
||||
VK(kNtVkNumpad5, "G", "1;2G", "1;5G", "1;6G"),
|
||||
VK(kNtVkNumpad6, "C", "1;2C", "1;5C", "1;6C"),
|
||||
VK(kNtVkNumpad7, "A", "1;2A", "1;5A", "1;6A"),
|
||||
VK(kNtVkNumpad8, "1~", "1;2~", "1;5~", "1;6~"),
|
||||
VK(kNtVkNumpad9, "5~", "5;2~", "5;5~", "5;6~"),
|
||||
VK(kNtVkDecimal, "3~", "3;2~", "3;5~", "3;6~"),
|
||||
VK(kNtVkF1, "[A", "23~", "11^", "23^"),
|
||||
VK(kNtVkF2, "[B", "24~", "12^", "24^"),
|
||||
VK(kNtVkF3, "[C", "25~", "13^", "25^"),
|
||||
VK(kNtVkF4, "[D", "26~", "14^", "26^"),
|
||||
VK(kNtVkF5, "[E", "28~", "15^", "28^"),
|
||||
VK(kNtVkF6, "17~", "29~", "17^", "29^"),
|
||||
VK(kNtVkF7, "18~", "31~", "18^", "31^"),
|
||||
VK(kNtVkF8, "19~", "32~", "19^", "32^"),
|
||||
VK(kNtVkF9, "20~", "33~", "20^", "33^"),
|
||||
VK(kNtVkF10, "21~", "34~", "21^", "34^"),
|
||||
VK(kNtVkF11, "23~", "23$", "23^", "23@"),
|
||||
VK(kNtVkF12, "24~", "24$", "24^", "24@"),
|
||||
#undef VK
|
||||
#undef W4
|
||||
#undef SW
|
||||
};
|
||||
|
||||
static textwindows int ProcessSignal(int sig, struct Fd *f) {
|
||||
if (f) {
|
||||
if (_weaken(__sig_raise)) {
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
_weaken(__sig_raise)(sig, SI_KERNEL);
|
||||
pthread_mutex_lock(&f->lock);
|
||||
if (!(__sighandflags[sig] & SA_RESTART)) {
|
||||
return eintr();
|
||||
}
|
||||
} else if (sig != SIGWINCH) {
|
||||
TerminateThisProcess(sig);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows uint32_t GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
||||
for (int i = 0; i < ARRAYLEN(kVirtualKey); ++i) {
|
||||
if (kVirtualKey[i].vk == vk) {
|
||||
if (shift && ctrl) {
|
||||
return kVirtualKey[i].shift_ctrl_str;
|
||||
} else if (shift) {
|
||||
return kVirtualKey[i].shift_str;
|
||||
} else if (ctrl) {
|
||||
return kVirtualKey[i].ctrl_str;
|
||||
} else {
|
||||
return kVirtualKey[i].normal_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
|
||||
static textwindows void EchoTerminalInput(struct Fd *f, char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
if (f->kind == kFdConsole) {
|
||||
hOutput = f->extra;
|
||||
} else {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoRaw) {
|
||||
WriteFile(hOutput, p, n, 0, 0);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
|
||||
char ctl[2];
|
||||
ctl[0] = '^';
|
||||
ctl[1] = p[i] ^ 0100;
|
||||
WriteFile(hOutput, ctl, 2, 0, 0);
|
||||
} else {
|
||||
WriteFile(hOutput, p + i, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p,
|
||||
uint16_t *utf16hs, struct Fd *f) {
|
||||
|
||||
uint16_t c = r->Event.KeyEvent.uChar.UnicodeChar;
|
||||
uint16_t vk = r->Event.KeyEvent.wVirtualKeyCode;
|
||||
uint16_t cks = r->Event.KeyEvent.dwControlKeyState;
|
||||
|
||||
// ignore keyup events
|
||||
if (!r->Event.KeyEvent.bKeyDown && (!c || vk != kNtVkMenu)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ignore numpad being used to compose a character
|
||||
if ((cks & kNtLeftAltPressed) && !(cks & kNtEnhancedKey) &&
|
||||
(vk == kNtVkInsert || vk == kNtVkEnd || vk == kNtVkDown ||
|
||||
vk == kNtVkNext || vk == kNtVkLeft || vk == kNtVkClear ||
|
||||
vk == kNtVkRight || vk == kNtVkHome || vk == kNtVkUp ||
|
||||
vk == kNtVkPrior || vk == kNtVkNumpad0 || vk == kNtVkNumpad1 ||
|
||||
vk == kNtVkNumpad2 || vk == kNtVkNumpad3 || vk == kNtVkNumpad4 ||
|
||||
vk == kNtVkNumpad5 || vk == kNtVkNumpad6 || vk == kNtVkNumpad7 ||
|
||||
vk == kNtVkNumpad8 || vk == kNtVkNumpad9)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
|
||||
// process virtual keys
|
||||
if (!c) {
|
||||
uint32_t w;
|
||||
w = GetVirtualKey(vk, !!(cks & kNtShiftPressed),
|
||||
!!(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed)));
|
||||
if (!w) return 0;
|
||||
p[n++] = 033;
|
||||
if (cks & (kNtLeftAltPressed | kNtRightAltPressed)) {
|
||||
p[n++] = 033;
|
||||
}
|
||||
p[n++] = '[';
|
||||
do p[n++] = w;
|
||||
while ((w >>= 8));
|
||||
return n;
|
||||
}
|
||||
|
||||
// translate utf-16 into utf-32
|
||||
if (IsHighSurrogate(c)) {
|
||||
*utf16hs = c;
|
||||
return 0;
|
||||
}
|
||||
if (IsLowSurrogate(c)) {
|
||||
c = MergeUtf16(*utf16hs, c);
|
||||
}
|
||||
|
||||
// enter sends \r in a raw terminals
|
||||
// make it a multics newline instead
|
||||
if (c == '\r' && !(__ttymagic & kFdTtyNoCr2Nl)) {
|
||||
c = '\n';
|
||||
}
|
||||
|
||||
// microsoft doesn't encode ctrl-space (^@) as nul
|
||||
// detecting it is also impossible w/ kNtEnableVirtualTerminalInput
|
||||
if (c == ' ' && (cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))) {
|
||||
c = '\0';
|
||||
}
|
||||
|
||||
// make it possible to distinguish ctrl-h (^H) from backspace (^?)
|
||||
if (c == kNtVkBack) {
|
||||
c = 0177;
|
||||
}
|
||||
|
||||
// handle ctrl-c and ctrl-\, which tcsetattr() is able to remap
|
||||
if (!(__ttymagic & kFdTtyNoIsigs)) {
|
||||
if (c == __vintr && __vintr != _POSIX_VDISABLE) {
|
||||
return ProcessSignal(SIGINT, f);
|
||||
} else if (c == __vquit && __vquit != _POSIX_VDISABLE) {
|
||||
return ProcessSignal(SIGQUIT, f);
|
||||
}
|
||||
}
|
||||
|
||||
// handle ctrl-d the end of file keystroke
|
||||
if (c == __veof && __veof != _POSIX_VDISABLE) {
|
||||
return enodata();
|
||||
}
|
||||
|
||||
// insert esc prefix when alt is held
|
||||
if ((cks & (kNtLeftAltPressed | kNtRightAltPressed)) &&
|
||||
!(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed)) &&
|
||||
r->Event.KeyEvent.bKeyDown) {
|
||||
p[n++] = 033;
|
||||
}
|
||||
|
||||
// convert utf-32 to utf-8
|
||||
uint64_t w = tpenc(c);
|
||||
do p[n++] = w;
|
||||
while ((w >>= 8));
|
||||
return n;
|
||||
}
|
||||
|
||||
// To use the tty mouse events feature:
|
||||
// - write(1, "\e[?1000;1002;1015;1006h") to enable
|
||||
// - write(1, "\e[?1000;1002;1015;1006l") to disable
|
||||
// See o//examples/ttyinfo.com and o//tool/viz/life.com
|
||||
static textwindows int ProcessMouseEvent(const struct NtInputRecord *r, char *b,
|
||||
struct Fd *f) {
|
||||
int e = 0;
|
||||
char *p = b;
|
||||
uint32_t currentbs = f ? f->mousebuttons : 0;
|
||||
uint32_t ev = r->Event.MouseEvent.dwEventFlags;
|
||||
uint32_t bs = r->Event.MouseEvent.dwButtonState;
|
||||
ev &= kNtMouseMoved | kNtMouseWheeled;
|
||||
bs &= kNtFromLeft1stButtonPressed | kNtRightmostButtonPressed;
|
||||
if (ev & kNtMouseWheeled) {
|
||||
// scroll wheel (unnatural mode)
|
||||
if (!(r->Event.MouseEvent.dwControlKeyState &
|
||||
(kNtShiftPressed | kNtLeftCtrlPressed | kNtRightCtrlPressed |
|
||||
kNtLeftAltPressed | kNtRightAltPressed))) {
|
||||
bool isup = ((int)r->Event.MouseEvent.dwButtonState >> 16) > 0;
|
||||
if (__ttymagic & kFdTtyXtMouse) {
|
||||
e = isup ? 80 : 81;
|
||||
goto OutputXtermMouseEvent;
|
||||
} else {
|
||||
// we disable mouse highlighting when the tty is put in raw mode
|
||||
// to mouse wheel events with widely understood vt100 arrow keys
|
||||
*p++ = 033;
|
||||
*p++ = '[';
|
||||
if (isup) {
|
||||
*p++ = 'A'; // \e[A up
|
||||
} else {
|
||||
*p++ = 'B'; // \e[B down
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((bs || currentbs) && (__ttymagic & kFdTtyXtMouse)) {
|
||||
if (bs && (ev & kNtMouseMoved) && currentbs) {
|
||||
e |= 32; // dragging
|
||||
}
|
||||
if ((bs | currentbs) & kNtRightmostButtonPressed) {
|
||||
e |= 2; // right
|
||||
}
|
||||
OutputXtermMouseEvent:
|
||||
*p++ = 033;
|
||||
*p++ = '[';
|
||||
*p++ = '<';
|
||||
p = FormatInt32(p, e);
|
||||
*p++ = ';';
|
||||
p = FormatInt32(p, r->Event.MouseEvent.dwMousePosition.X + 1);
|
||||
*p++ = ';';
|
||||
p = FormatInt32(p, r->Event.MouseEvent.dwMousePosition.Y + 1);
|
||||
if (!bs && currentbs) {
|
||||
*p++ = 'm'; // up
|
||||
} else {
|
||||
*p++ = 'M'; // down
|
||||
}
|
||||
if (f) {
|
||||
f->mousebuttons = bs;
|
||||
}
|
||||
}
|
||||
return p - b;
|
||||
}
|
||||
|
||||
textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
|
||||
char p[hasatleast 32],
|
||||
uint16_t *utf16hs, struct Fd *f) {
|
||||
switch (r->EventType) {
|
||||
case kNtKeyEvent:
|
||||
return ProcessKeyEvent(r, p, utf16hs, f);
|
||||
case kNtMouseEvent:
|
||||
return ProcessMouseEvent(r, p, f);
|
||||
case kNtWindowBufferSizeEvent:
|
||||
return ProcessSignal(SIGWINCH, f);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows ssize_t ReadFromWindowsConsole(struct Fd *f, void *data,
|
||||
size_t size) {
|
||||
ssize_t rc;
|
||||
int e = errno;
|
||||
uint16_t utf16hs = 0;
|
||||
pthread_mutex_lock(&f->lock);
|
||||
for (;;) {
|
||||
if (f->eoftty) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
uint32_t got = MIN(size, f->buflen);
|
||||
uint32_t remain = f->buflen - got;
|
||||
if (got) memcpy(data, f->buf, got);
|
||||
if (remain) memmove(f->buf, f->buf + got, remain);
|
||||
f->buflen = remain;
|
||||
if (got) {
|
||||
rc = got;
|
||||
break;
|
||||
}
|
||||
uint32_t events_available;
|
||||
if (!GetNumberOfConsoleInputEvents(f->handle, &events_available)) {
|
||||
rc = __winerr();
|
||||
break;
|
||||
}
|
||||
if (events_available) {
|
||||
uint32_t n;
|
||||
struct NtInputRecord r;
|
||||
if (!ReadConsoleInput(f->handle, &r, 1, &n)) {
|
||||
rc = __winerr();
|
||||
break;
|
||||
}
|
||||
rc = ConvertConsoleInputToAnsi(&r, f->buf, &utf16hs, f);
|
||||
if (rc == -1) {
|
||||
if (errno == ENODATA) {
|
||||
f->eoftty = true;
|
||||
errno = e;
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f->buflen = rc;
|
||||
} else {
|
||||
if (f->flags & O_NONBLOCK) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
uint32_t ms = __SIG_POLL_INTERVAL_MS;
|
||||
if (__ttymagic & kFdTtyNoBlock) {
|
||||
if (!__vtime) {
|
||||
rc = 0;
|
||||
break;
|
||||
} else {
|
||||
ms = __vtime * 100;
|
||||
}
|
||||
}
|
||||
if ((rc = _check_interrupts(kSigOpRestartable))) {
|
||||
break;
|
||||
}
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, ms);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno == ECANCELED) {
|
||||
rc = ecanceled();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
if (rc > 0 && (__ttymagic & kFdTtyEchoing)) {
|
||||
EchoTerminalInput(f, data, size);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
||||
int64_t offset) {
|
||||
|
||||
// perform the read i/o operation
|
||||
bool32 ok;
|
||||
struct Fd *f;
|
||||
uint32_t got;
|
||||
int64_t handle;
|
||||
uint32_t remain;
|
||||
char *targetdata;
|
||||
uint32_t targetsize;
|
||||
struct PosixThread *pt;
|
||||
|
||||
f = g_fds.p + fd;
|
||||
handle = __resolve_stdin_handle(f->handle);
|
||||
handle = f->handle;
|
||||
pt = _pthread_self();
|
||||
pt->abort_errno = EAGAIN;
|
||||
size = MIN(size, 0x7ffff000);
|
||||
|
||||
bool pwriting = offset != -1;
|
||||
bool seekable = f->kind == kFdFile && GetFileType(handle) == kNtFileTypeDisk;
|
||||
bool nonblock = !!(f->flags & O_NONBLOCK);
|
||||
pt->abort_errno = EAGAIN;
|
||||
|
||||
if (pwriting && !seekable) {
|
||||
return espipe();
|
||||
|
@ -85,33 +444,9 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
|||
offset = 0;
|
||||
}
|
||||
|
||||
uint32_t dwConsoleMode;
|
||||
bool is_console_input =
|
||||
g_fds.stdin.handle
|
||||
? f->handle == g_fds.stdin.handle
|
||||
: !seekable && (f->kind == kFdConsole ||
|
||||
GetConsoleMode(handle, &dwConsoleMode));
|
||||
|
||||
StartOver:
|
||||
size = MIN(size, 0x7ffff000);
|
||||
|
||||
// the caller might be reading a single byte at a time. but we need to
|
||||
// be able to munge three bytes into just 1 e.g. "\342\220\200" → "\0"
|
||||
if (size && f->buflen) {
|
||||
ReturnDataFromBuffer:
|
||||
got = MIN(size, f->buflen);
|
||||
remain = f->buflen - got;
|
||||
if (got) memcpy(data, f->buf, got);
|
||||
if (remain) memmove(f->buf, f->buf + got, remain);
|
||||
f->buflen = remain;
|
||||
return got;
|
||||
}
|
||||
if (is_console_input && size && size < 3 && (__ttymagic & kFdTtyMunging)) {
|
||||
targetdata = f->buf;
|
||||
targetsize = sizeof(f->buf);
|
||||
} else {
|
||||
targetdata = data;
|
||||
targetsize = size;
|
||||
uint32_t cm;
|
||||
if (!seekable && (f->kind == kFdConsole || GetConsoleMode(handle, &cm))) {
|
||||
return ReadFromWindowsConsole(f, data, size);
|
||||
}
|
||||
|
||||
if (!pwriting && seekable) {
|
||||
|
@ -123,7 +458,7 @@ StartOver:
|
|||
.Pointer = offset};
|
||||
// the win32 manual says it's important to *not* put &got here
|
||||
// since for overlapped i/o, we always use GetOverlappedResult
|
||||
ok = ReadFile(handle, targetdata, targetsize, 0, &overlap);
|
||||
ok = ReadFile(handle, data, size, 0, &overlap);
|
||||
if (!ok && GetLastError() == kNtErrorIoPending) {
|
||||
BlockingOperation:
|
||||
if (!nonblock) {
|
||||
|
@ -172,27 +507,6 @@ StartOver:
|
|||
}
|
||||
|
||||
if (ok) {
|
||||
if (is_console_input) {
|
||||
if (__ttymagic & kFdTtyMunging) {
|
||||
switch (_weaken(__munge_terminal_input)(targetdata, &got)) {
|
||||
case DO_NOTHING:
|
||||
break;
|
||||
case DO_RESTART:
|
||||
goto StartOver;
|
||||
case DO_EINTR:
|
||||
return eintr();
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoing) {
|
||||
_weaken(__echo_terminal_input)(f, targetdata, got);
|
||||
}
|
||||
}
|
||||
if (targetdata != data) {
|
||||
f->buflen = got;
|
||||
goto ReturnDataFromBuffer;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
|
|
|
@ -160,13 +160,12 @@ textwindows int __sig_raise(int sig, int sic) {
|
|||
return (flags & SA_RESTART) ? 2 : 1;
|
||||
}
|
||||
|
||||
textwindows void __sig_cancel(struct PosixThread *pt, unsigned flags) {
|
||||
textwindows void __sig_cancel(struct PosixThread *pt) {
|
||||
atomic_int *futex;
|
||||
if (_weaken(WakeByAddressSingle) &&
|
||||
(futex = atomic_load_explicit(&pt->pt_futex, memory_order_acquire))) {
|
||||
_weaken(WakeByAddressSingle)(futex);
|
||||
} else if (!(flags & SA_RESTART) && pt->iohandle > 0) {
|
||||
pt->abort_errno = EINTR;
|
||||
} else if (pt->iohandle > 0) {
|
||||
if (!CancelIoEx(pt->iohandle, pt->ioverlap)) {
|
||||
int err = GetLastError();
|
||||
if (err != kNtErrorNotFound) {
|
||||
|
@ -174,7 +173,6 @@ textwindows void __sig_cancel(struct PosixThread *pt, unsigned flags) {
|
|||
}
|
||||
}
|
||||
} else if (pt->pt_flags & PT_INSEMAPHORE) {
|
||||
pt->abort_errno = EINTR;
|
||||
if (!ReleaseSemaphore(pt->semaphore, 1, 0)) {
|
||||
STRACE("ReleaseSemaphore() failed w/ %d", GetLastError());
|
||||
}
|
||||
|
@ -259,8 +257,9 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
|||
STRACE("SetThreadContext failed w/ %d", GetLastError());
|
||||
return ESRCH;
|
||||
}
|
||||
ResumeThread(th); // doesn't actually resume if doing blocking i/o
|
||||
__sig_cancel(pt, flags); // we can wake it up immediately by canceling it
|
||||
ResumeThread(th); // doesn't actually resume if doing blocking i/o
|
||||
pt->abort_errno = EINTR;
|
||||
__sig_cancel(pt); // we can wake it up immediately by canceling it
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ int __sig_check(void);
|
|||
int __sig_kill(struct PosixThread *, int, int);
|
||||
int __sig_mask(int, const sigset_t *, sigset_t *);
|
||||
int __sig_raise(int, int);
|
||||
void __sig_cancel(struct PosixThread *, unsigned);
|
||||
void __sig_cancel(struct PosixThread *);
|
||||
void __sig_generate(int, int);
|
||||
void __sig_init(void);
|
||||
|
||||
|
|
|
@ -507,9 +507,6 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) {
|
|||
once = true;
|
||||
}
|
||||
}
|
||||
if (IsWindows() && !rc && sig == SIGWINCH) {
|
||||
_init_sigwinch(); // lazy b/c sigwinch is otherwise ignored
|
||||
}
|
||||
}
|
||||
STRACE("sigaction(%G, %s, [%s]) → %d% m", sig, DescribeSigaction(0, act),
|
||||
DescribeSigaction(rc, oldact), rc);
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/struct/consolescreenbufferinfoex.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/tls2.internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
intptr_t __sigwinch_thread;
|
||||
static unsigned __sigwinch_size;
|
||||
static atomic_uint __sigwinch_once;
|
||||
static struct CosmoTib __sigwinch_tls;
|
||||
|
||||
static textwindows unsigned __get_console_size(void) {
|
||||
unsigned res = -1u;
|
||||
__fds_lock();
|
||||
for (int fd = 1; fd < 10; ++fd) {
|
||||
intptr_t hConsoleOutput;
|
||||
if (g_fds.p[fd].kind == kFdConsole) {
|
||||
hConsoleOutput = g_fds.p[fd].extra;
|
||||
} else {
|
||||
hConsoleOutput = g_fds.p[fd].handle;
|
||||
}
|
||||
struct NtConsoleScreenBufferInfoEx sr = {.cbSize = sizeof(sr)};
|
||||
if (GetConsoleScreenBufferInfoEx(hConsoleOutput, &sr)) {
|
||||
unsigned short yn = sr.srWindow.Bottom - sr.srWindow.Top + 1;
|
||||
unsigned short xn = sr.srWindow.Right - sr.srWindow.Left + 1;
|
||||
res = (unsigned)yn << 16 | xn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
__fds_unlock();
|
||||
return res;
|
||||
}
|
||||
|
||||
static textwindows dontinstrument uint32_t __sigwinch_worker(void *arg) {
|
||||
__bootstrap_tls(&__sigwinch_tls, __builtin_frame_address(0));
|
||||
for (;;) {
|
||||
unsigned old = __sigwinch_size;
|
||||
unsigned neu = __get_console_size();
|
||||
if (neu != old) {
|
||||
__sigwinch_size = neu;
|
||||
__sig_generate(SIGWINCH, SI_KERNEL);
|
||||
}
|
||||
SleepEx(25, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static textwindows void __sigwinch_init(void) {
|
||||
__enable_threads();
|
||||
__sigwinch_size = __get_console_size();
|
||||
__sigwinch_thread = CreateThread(0, 65536, __sigwinch_worker, 0,
|
||||
kNtStackSizeParamIsAReservation, 0);
|
||||
}
|
||||
|
||||
textwindows void _init_sigwinch(void) {
|
||||
cosmo_once(&__sigwinch_once, __sigwinch_init);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -24,18 +24,22 @@ COSMOPOLITAN_C_START_
|
|||
#define kFdTtyMunging 4 /* enable input / output remappings */
|
||||
#define kFdTtyNoCr2Nl 8 /* don't map \r → \n (a.k.a !ICRNL) */
|
||||
#define kFdTtyNoIsigs 16
|
||||
#define kFdTtyNoBlock 32
|
||||
#define kFdTtyXtMouse 64
|
||||
|
||||
struct Fd {
|
||||
char kind;
|
||||
bool eoftty;
|
||||
bool dontclose;
|
||||
char buflen;
|
||||
char buf[4];
|
||||
unsigned char buflen;
|
||||
unsigned flags;
|
||||
unsigned mode;
|
||||
int64_t handle;
|
||||
int64_t extra;
|
||||
int64_t pointer;
|
||||
pthread_mutex_t lock;
|
||||
unsigned char mousebuttons;
|
||||
char buf[32];
|
||||
};
|
||||
|
||||
struct StdinRelay {
|
||||
|
@ -55,11 +59,6 @@ struct Fds {
|
|||
struct StdinRelay stdin;
|
||||
};
|
||||
|
||||
void WinMainStdin(void);
|
||||
int64_t __resolve_stdin_handle(int64_t);
|
||||
int __munge_terminal_input(char *, uint32_t *);
|
||||
void __echo_terminal_input(struct Fd *, char *, size_t);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ */
|
||||
|
|
|
@ -64,9 +64,6 @@ const char *DescribeSigaction(char[256], int, const struct sigaction *);
|
|||
#define DescribeSigaction(rc, sa) DescribeSigaction(alloca(256), rc, sa)
|
||||
|
||||
void _init_onntconsoleevent(void);
|
||||
void _init_sigwinch(void);
|
||||
|
||||
extern intptr_t __sigwinch_thread;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -55,13 +55,14 @@ textwindows int tcgetattr_nt(int fd, struct termios *tio) {
|
|||
|
||||
bzero(tio, sizeof(*tio));
|
||||
|
||||
tio->c_cc[VMIN] = 1;
|
||||
tio->c_cc[VMIN] = !(__ttymagic & kFdTtyNoBlock);
|
||||
tio->c_cc[VEOF] = __veof;
|
||||
tio->c_cc[VTIME] = __vtime;
|
||||
tio->c_cc[VINTR] = __vintr;
|
||||
tio->c_cc[VQUIT] = __vquit;
|
||||
tio->c_cc[VERASE] = CTRL('?');
|
||||
tio->c_cc[VWERASE] = CTRL('W');
|
||||
tio->c_cc[VKILL] = CTRL('U');
|
||||
tio->c_cc[VEOF] = CTRL('D');
|
||||
tio->c_cc[VMIN] = CTRL('A');
|
||||
tio->c_cc[VSTART] = _POSIX_VDISABLE;
|
||||
tio->c_cc[VSTOP] = _POSIX_VDISABLE;
|
||||
|
|
|
@ -45,130 +45,6 @@
|
|||
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows int __munge_terminal_input(char *p, uint32_t *n) {
|
||||
size_t i, j;
|
||||
if (!(__ttymagic & kFdTtyNoCr2Nl)) {
|
||||
for (i = 0; i < *n; ++i) {
|
||||
if (p[i] == '\r') {
|
||||
p[i] = '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
bool delivered = false;
|
||||
bool got_vintr = false;
|
||||
bool got_vquit = false;
|
||||
for (j = i = 0; i < *n; ++i) {
|
||||
/*
|
||||
It's not possible to type Ctrl+Space (aka ^@) into the CMD.EXE
|
||||
console. Ctrl+Z is also problematic, since the Windows Console
|
||||
processes that keystroke into an EOF event, even when we don't
|
||||
enable input processing. These control codes are important for
|
||||
Emacs users. One solution is to install the "Windows Terminal"
|
||||
application from Microsoft Store, type Ctrl+Shift+, to get its
|
||||
settings.json file opened, and add this code to its "actions":
|
||||
|
||||
"actions": [
|
||||
{
|
||||
"command": {
|
||||
"action": "sendInput",
|
||||
"input": "␀"
|
||||
},
|
||||
"keys": "ctrl+space"
|
||||
},
|
||||
{
|
||||
"command": {
|
||||
"action": "sendInput",
|
||||
"input": "␚"
|
||||
},
|
||||
"keys": "ctrl+z"
|
||||
},
|
||||
{
|
||||
"command": {
|
||||
"action": "sendInput",
|
||||
"input": "\u001f"
|
||||
},
|
||||
"keys": "ctrl+-"
|
||||
}
|
||||
],
|
||||
|
||||
Its not possible to configure Windows Terminal to output our
|
||||
control codes. The workaround is to encode control sequences
|
||||
using the "Control Pictures" UNICODE plane, which we'll then
|
||||
translate back from UTF-8 glyphs, into actual control codes.
|
||||
|
||||
Another option Windows users can consider, particularly when
|
||||
using CMD.EXE is installing Microsoft PowerTools whech makes
|
||||
it possible to remap keys then configure ncurses to use them
|
||||
|
||||
https://github.com/microsoft/terminal/issues/2865
|
||||
*/
|
||||
int c;
|
||||
if (i + 3 <= *n && // control pictures ascii nul ␀
|
||||
(p[i + 0] & 255) == 0xe2 && // becomes ^@ a.k.a. ctrl-space
|
||||
(p[i + 1] & 255) == 0x90 && // map the entire unicode plane
|
||||
((p[i + 2] & 255) >= 0x80 && //
|
||||
(p[i + 2] & 255) <= 0x9F)) {
|
||||
c = (p[i + 2] & 255) - 0x80;
|
||||
i += 2;
|
||||
} else {
|
||||
c = p[i] & 255;
|
||||
}
|
||||
if (!(__ttymagic & kFdTtyNoIsigs) && //
|
||||
__vintr != _POSIX_VDISABLE && //
|
||||
c == __vintr) {
|
||||
got_vintr = true;
|
||||
} else if (!(__ttymagic & kFdTtyNoIsigs) && //
|
||||
__vquit != _POSIX_VDISABLE && //
|
||||
c == __vquit) {
|
||||
got_vquit = true;
|
||||
} else {
|
||||
p[j++] = c;
|
||||
}
|
||||
}
|
||||
if (got_vintr) {
|
||||
__sig_raise(SIGINT, SI_KERNEL);
|
||||
delivered |= true;
|
||||
}
|
||||
if (got_vquit) {
|
||||
__sig_raise(SIGQUIT, SI_KERNEL);
|
||||
delivered |= true;
|
||||
}
|
||||
if (*n && !j) {
|
||||
if (delivered) {
|
||||
return DO_EINTR;
|
||||
} else {
|
||||
return DO_RESTART;
|
||||
}
|
||||
}
|
||||
*n = j;
|
||||
return DO_NOTHING;
|
||||
}
|
||||
|
||||
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
|
||||
textwindows void __echo_terminal_input(struct Fd *fd, char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
if (fd->kind == kFdConsole) {
|
||||
hOutput = fd->extra;
|
||||
} else {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoRaw) {
|
||||
WriteFile(hOutput, p, n, 0, 0);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
|
||||
char ctl[2];
|
||||
ctl[0] = '^';
|
||||
ctl[1] = p[i] ^ 0100;
|
||||
WriteFile(hOutput, ctl, 2, 0, 0);
|
||||
} else {
|
||||
WriteFile(hOutput, p + i, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
||||
bool32 ok;
|
||||
int64_t hInput, hOutput;
|
||||
|
@ -198,18 +74,15 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
|||
inmode |= kNtEnableWindowInput;
|
||||
__ttymagic = 0;
|
||||
if (tio->c_lflag & ICANON) {
|
||||
inmode |= kNtEnableLineInput | kNtEnableProcessedInput;
|
||||
inmode |=
|
||||
kNtEnableLineInput | kNtEnableProcessedInput | kNtEnableQuickEditMode;
|
||||
} else {
|
||||
inmode &= ~kNtEnableQuickEditMode;
|
||||
__ttymagic |= kFdTtyMunging;
|
||||
if (tio->c_cc[VMIN] != 1) {
|
||||
STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows");
|
||||
return einval();
|
||||
}
|
||||
if (IsAtLeastWindows10()) {
|
||||
// - keys like f1, up, etc. get turned into \e ansi codes
|
||||
// - totally destroys default console gui (e.g. up arrow)
|
||||
inmode |= kNtEnableVirtualTerminalInput;
|
||||
if (!tio->c_cc[VMIN]) {
|
||||
__ttymagic |= kFdTtyNoBlock;
|
||||
}
|
||||
__vtime = tio->c_cc[VTIME];
|
||||
}
|
||||
if (!(tio->c_iflag & ICRNL)) {
|
||||
__ttymagic |= kFdTtyNoCr2Nl;
|
||||
|
@ -233,6 +106,7 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
|||
if (!(tio->c_lflag & ISIG)) {
|
||||
__ttymagic |= kFdTtyNoIsigs;
|
||||
}
|
||||
__veof = tio->c_cc[VEOF];
|
||||
__vintr = tio->c_cc[VINTR];
|
||||
__vquit = tio->c_cc[VQUIT];
|
||||
if ((tio->c_lflag & ISIG) && //
|
||||
|
@ -261,11 +135,4 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void tcsetattr_nt_init(void) {
|
||||
if (!getenv("TERM")) {
|
||||
setenv("TERM", "xterm-256color", true);
|
||||
errno = 0; // ignore malloc not linked
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
static textwindows dontinstrument wontreturn void WinStdinThread(void) {
|
||||
char buf[4];
|
||||
struct CosmoTib tls;
|
||||
uint32_t i, got, wrote;
|
||||
__bootstrap_tls(&tls, __builtin_frame_address(0));
|
||||
for (;;) {
|
||||
WaitForSingleObject(g_fds.stdin.inisem, -1u);
|
||||
if (!ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) goto Finish;
|
||||
if (!got) goto Finish;
|
||||
for (i = 0; i < got; i += wrote) {
|
||||
if (!WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) {
|
||||
goto Finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
Finish:
|
||||
CloseHandle(g_fds.stdin.writer);
|
||||
ExitThread(0);
|
||||
}
|
||||
|
||||
textwindows static char16_t *FixCpy(char16_t p[17], uint64_t x, uint8_t k) {
|
||||
while (k > 0) *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15];
|
||||
*p = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
textwindows static char16_t *CreateStdinPipeName(char16_t *a, int64_t h) {
|
||||
char16_t *p = a;
|
||||
const char *q = "\\\\?\\pipe\\cosmo\\stdin\\";
|
||||
while (*q) *p++ = *q++;
|
||||
p = FixCpy(p, h, 64);
|
||||
*p = 0;
|
||||
return a;
|
||||
}
|
||||
|
||||
textwindows void WinMainStdin(void) {
|
||||
uint32_t conmode;
|
||||
char16_t pipename[64];
|
||||
int64_t hStdin, hWriter, hReader, hThread, hSemaphore;
|
||||
if (!SupportsWindows()) return;
|
||||
hStdin = GetStdHandle(kNtStdInputHandle);
|
||||
if (!GetConsoleMode(hStdin, &conmode)) return;
|
||||
CreateStdinPipeName(pipename, hStdin);
|
||||
hWriter = CreateNamedPipe(
|
||||
pipename, kNtPipeAccessOutbound | kNtFileFlagOverlapped,
|
||||
kNtPipeTypeMessage | kNtPipeReadmodeMessage | kNtPipeNowait,
|
||||
kNtPipeUnlimitedInstances, 4096, 4096, 0, 0);
|
||||
if (hWriter == kNtInvalidHandleValue) return;
|
||||
hReader = CreateFile(pipename, kNtGenericRead, 0, 0, kNtOpenExisting,
|
||||
kNtFileFlagOverlapped, 0);
|
||||
if (hReader == kNtInvalidHandleValue) return;
|
||||
hSemaphore = CreateSemaphore(0, 0, 1, 0);
|
||||
if (!hSemaphore) return;
|
||||
hThread = CreateThread(0, 65536, (void *)WinStdinThread, 0,
|
||||
kNtStackSizeParamIsAReservation, 0);
|
||||
if (!hThread) return;
|
||||
g_fds.stdin.handle = hStdin;
|
||||
g_fds.stdin.thread = hThread;
|
||||
g_fds.stdin.reader = hReader;
|
||||
g_fds.stdin.writer = hWriter;
|
||||
g_fds.stdin.inisem = hSemaphore;
|
||||
}
|
|
@ -23,9 +23,12 @@
|
|||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/consolemodeflags.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
|
@ -46,6 +49,13 @@
|
|||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
|
||||
static bool IsMouseModeCommand(int x) {
|
||||
return x == 1000 || // SET_VT200_MOUSE
|
||||
x == 1002 || // SET_BTN_EVENT_MOUSE
|
||||
x == 1006 || // SET_SGR_EXT_MODE_MOUSE
|
||||
x == 1015; // SET_URXVT_EXT_MODE_MOUSE
|
||||
}
|
||||
|
||||
static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
||||
ssize_t offset) {
|
||||
|
||||
|
@ -81,6 +91,76 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
|||
offset = f->pointer;
|
||||
}
|
||||
|
||||
// To use the tty mouse events feature:
|
||||
// - write(1, "\e[?1000;1002;1015;1006h") to enable
|
||||
// - write(1, "\e[?1000;1002;1015;1006l") to disable
|
||||
// See o//examples/ttyinfo.com and o//tool/viz/life.com
|
||||
uint32_t cm;
|
||||
if (!seekable && (f->kind == kFdConsole || GetConsoleMode(handle, &cm))) {
|
||||
int64_t hin;
|
||||
if (f->kind == kFdConsole) {
|
||||
hin = f->handle;
|
||||
} else {
|
||||
hin = GetStdHandle(kNtStdInputHandle);
|
||||
}
|
||||
if (GetConsoleMode(hin, &cm)) {
|
||||
int t = 0;
|
||||
unsigned x;
|
||||
bool m = false;
|
||||
char *p = data;
|
||||
uint32_t cm2 = cm;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
switch (t) {
|
||||
case 0:
|
||||
if (p[i] == 033) {
|
||||
t = 1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (p[i] == '[') {
|
||||
t = 2;
|
||||
} else {
|
||||
t = 0;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (p[i] == '?') {
|
||||
t = 3;
|
||||
x = 0;
|
||||
} else {
|
||||
t = 0;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if ('0' <= p[i] && p[i] <= '9') {
|
||||
x *= 10;
|
||||
x += p[i] - '0';
|
||||
} else if (p[i] == ';') {
|
||||
m |= IsMouseModeCommand(x);
|
||||
x = 0;
|
||||
} else {
|
||||
m |= IsMouseModeCommand(x);
|
||||
if (p[i] == 'h') {
|
||||
__ttymagic |= kFdTtyXtMouse;
|
||||
cm2 |= kNtEnableMouseInput;
|
||||
cm2 &= kNtEnableQuickEditMode; // precludes mouse events
|
||||
} else if (p[i] == 'l') {
|
||||
__ttymagic &= ~kFdTtyXtMouse;
|
||||
cm2 |= kNtEnableQuickEditMode; // disables mouse too
|
||||
}
|
||||
t = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
if (cm2 != cm) {
|
||||
SetConsoleMode(hin, cm2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0),
|
||||
.Pointer = offset};
|
||||
ok = WriteFile(handle, data, size, 0, &overlap);
|
||||
|
@ -143,7 +223,6 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
|||
_weaken(__sig_raise)(SIGPIPE, SI_KERNEL);
|
||||
return epipe();
|
||||
} else {
|
||||
STRACE("broken pipe");
|
||||
TerminateThisProcess(SIGPIPE);
|
||||
}
|
||||
case kNtErrorAccessDenied: // write doesn't return EACCESS
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/termios.h"
|
||||
#include "libc/calls/struct/termios.internal.h"
|
||||
#include "libc/calls/ttydefaults.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
|
@ -119,8 +120,15 @@ const char *(DescribeTermios)(char buf[N], ssize_t rc,
|
|||
{IEXTEN, "IEXTEN"}, //
|
||||
{EXTPROC, "EXTPROC"}, //
|
||||
};
|
||||
append(", .c_lflag=%s",
|
||||
DescribeFlags(b128, 128, kLocal, ARRAYLEN(kLocal), "", tio->c_lflag));
|
||||
append(", "
|
||||
".c_lflag=%s, "
|
||||
".c_cc[VMIN]=%d, "
|
||||
".c_cc[VTIME]=%d, "
|
||||
".c_cc[VINTR]=CTRL(%#c), "
|
||||
".c_cc[VQUIT]=CTRL(%#c)",
|
||||
DescribeFlags(b128, 128, kLocal, ARRAYLEN(kLocal), "", tio->c_lflag),
|
||||
tio->c_cc[VMIN], tio->c_cc[VTIME], CTRL(tio->c_cc[VINTR]),
|
||||
CTRL(tio->c_cc[VQUIT]));
|
||||
|
||||
append("}");
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
|
|||
}
|
||||
fds->p[1].flags = O_WRONLY | O_APPEND;
|
||||
fds->p[2].flags = O_WRONLY | O_APPEND;
|
||||
__veof = CTRL('D');
|
||||
__vintr = CTRL('C');
|
||||
__vquit = CTRL('\\');
|
||||
}
|
||||
|
|
|
@ -18,15 +18,10 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
|
||||
/**
|
||||
* Controls ANSI prefix for log emissions.
|
||||
*
|
||||
* This should be true in raw tty mode repls.
|
||||
*
|
||||
* @see kprintf(), vflogf(), linenoise()
|
||||
*/
|
||||
char __replmode;
|
||||
char __replstderr;
|
||||
char __ttymagic;
|
||||
char __vintr;
|
||||
char __vquit;
|
||||
unsigned char __replmode;
|
||||
unsigned char __replstderr;
|
||||
unsigned char __ttymagic;
|
||||
unsigned char __veof;
|
||||
unsigned char __vintr;
|
||||
unsigned char __vquit;
|
||||
unsigned char __vtime;
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_NOMULTICS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_NOMULTICS_INTERNAL_H_
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
extern char __replmode;
|
||||
extern char __replstderr;
|
||||
extern char __ttymagic;
|
||||
extern char __vintr;
|
||||
extern char __vquit;
|
||||
extern unsigned char __replmode;
|
||||
extern unsigned char __replstderr;
|
||||
extern unsigned char __ttymagic;
|
||||
extern unsigned char __veof;
|
||||
extern unsigned char __vintr;
|
||||
extern unsigned char __vquit;
|
||||
extern unsigned char __vtime;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -49,8 +49,8 @@ bool32 ReadConsoleInput(int64_t hConsoleInput, struct NtInputRecord *lpBuffer,
|
|||
uint32_t nLength, uint32_t *lpNumberOfEventsRead);
|
||||
bool32 PeekConsoleInput(int64_t hConsoleInput, struct NtInputRecord *lpBuffer,
|
||||
uint32_t nLength, uint32_t *lpNumberOfEventsRead);
|
||||
bool32 GetNumberOfConsoleInputEvent(int64_t hConsoleInput,
|
||||
uint32_t *lpNumberOfEvents);
|
||||
bool32 GetNumberOfConsoleInputEvents(int64_t hConsoleInput,
|
||||
uint32_t *lpNumberOfEvents);
|
||||
bool32 ReadConsoleOutput(int64_t hConsoleOutput, struct NtCharInfo *lpBuffer,
|
||||
struct NtCoord dwBufferSize,
|
||||
struct NtCoord dwBufferCoord,
|
||||
|
|
|
@ -56,7 +56,8 @@ int64_t OpenProcess(uint32_t dwDesiredAccess, bool32 bInheritHandle,
|
|||
uint32_t GetCurrentProcessId(void); /* %gs:0x40 */
|
||||
uint32_t GetEnvironmentVariable(const char16_t *lpName, char16_t *lpBuffer,
|
||||
uint32_t nSize);
|
||||
uint32_t SetEnvironmentVariable(const char16_t *lpName, char16_t *lpValue);
|
||||
uint32_t SetEnvironmentVariable(const char16_t *lpName,
|
||||
const char16_t *lpValue);
|
||||
int32_t SetEnvironmentStrings(char16_t *NewEnvironment);
|
||||
bool32 GetProcessAffinityMask(int64_t hProcess, uint64_t *lpProcessAffinityMask,
|
||||
uint64_t *lpSystemAffinityMask);
|
||||
|
|
|
@ -9,17 +9,35 @@ struct NtKeyEventRecord {
|
|||
uint16_t wVirtualKeyCode;
|
||||
uint16_t wVirtualScanCode;
|
||||
union {
|
||||
int16_t UnicodeChar;
|
||||
uint16_t UnicodeChar;
|
||||
char AsciiChar;
|
||||
} uChar;
|
||||
unsigned int dwControlKeyState;
|
||||
#define kNtRightAltPressed 0x0001
|
||||
#define kNtLeftAltPressed 0x0002
|
||||
#define kNtRightCtrlPressed 0x0004
|
||||
#define kNtLeftCtrlPressed 0x0008
|
||||
#define kNtShiftPressed 0x0010
|
||||
#define kNtNumlockOn 0x0020
|
||||
#define kNtScrolllockOn 0x0040
|
||||
#define kNtCapslockOn 0x0080
|
||||
#define kNtEnhancedKey 0x0100
|
||||
};
|
||||
|
||||
struct NtMouseEventRecord {
|
||||
struct NtCoord dwMousePosition;
|
||||
uint32_t dwButtonState;
|
||||
#define kNtFromLeft1stButtonPressed 0x0001
|
||||
#define kNtRightmostButtonPressed 0x0002
|
||||
#define kNtFromLeft2ndButtonPressed 0x0004
|
||||
#define kNtFromLeft3rdButtonPressed 0x0008
|
||||
#define kNtFromLeft4thButtonPressed 0x0010
|
||||
uint32_t dwControlKeyState;
|
||||
uint32_t dwEventFlags;
|
||||
#define kNtMouseMoved 0x0001
|
||||
#define kNtDoubleClick 0x0002
|
||||
#define kNtMouseWheeled 0x0004
|
||||
#define kNtMouseHwheeled 0x0008
|
||||
};
|
||||
|
||||
struct NtWindowBufferSizeRecord {
|
||||
|
@ -36,6 +54,11 @@ struct NtFocusEventRecord {
|
|||
|
||||
struct NtInputRecord {
|
||||
uint16_t EventType;
|
||||
#define kNtKeyEvent 0x0001
|
||||
#define kNtMouseEvent 0x0002
|
||||
#define kNtWindowBufferSizeEvent 0x0004
|
||||
#define kNtMenuEvent 0x0008
|
||||
#define kNtFocusEvent 0x0010
|
||||
union {
|
||||
struct NtKeyEventRecord KeyEvent;
|
||||
struct NtMouseEventRecord MouseEvent;
|
||||
|
|
|
@ -154,9 +154,6 @@ keywords int sys_execve_nt(const char *program, char *const argv[],
|
|||
PurgeThread(pt->tib->tib_syshand);
|
||||
PurgeHandle(pt->semaphore);
|
||||
}
|
||||
if (_weaken(__sigwinch_thread)) {
|
||||
PurgeThread(*_weaken(__sigwinch_thread));
|
||||
}
|
||||
if (_weaken(__itimer)) {
|
||||
PurgeThread(_weaken(__itimer)->thread);
|
||||
}
|
||||
|
|
|
@ -55,12 +55,14 @@ __msabi extern typeof(FreeEnvironmentStrings) *const __imp_FreeEnvironmentString
|
|||
__msabi extern typeof(GetConsoleMode) *const __imp_GetConsoleMode;
|
||||
__msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId;
|
||||
__msabi extern typeof(GetEnvironmentStrings) *const __imp_GetEnvironmentStringsW;
|
||||
__msabi extern typeof(GetEnvironmentVariable) *const __imp_GetEnvironmentVariableW;
|
||||
__msabi extern typeof(GetFileAttributes) *const __imp_GetFileAttributesW;
|
||||
__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
|
||||
__msabi extern typeof(MapViewOfFileEx) *const __imp_MapViewOfFileEx;
|
||||
__msabi extern typeof(SetConsoleCP) *const __imp_SetConsoleCP;
|
||||
__msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode;
|
||||
__msabi extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP;
|
||||
__msabi extern typeof(SetEnvironmentVariable) *const __imp_SetEnvironmentVariableW;
|
||||
__msabi extern typeof(SetStdHandle) *const __imp_SetStdHandle;
|
||||
__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
|
||||
// clang-format on
|
||||
|
@ -92,8 +94,7 @@ __funline char16_t *MyCommandLine(void) {
|
|||
|
||||
// implements all win32 apis on non-windows hosts
|
||||
static abi long __oops_win32(void) {
|
||||
assert(!"win32 api called on non-windows host");
|
||||
return 0;
|
||||
notpossible;
|
||||
}
|
||||
|
||||
// returns true if utf-8 path is a win32-style path that exists
|
||||
|
@ -131,11 +132,16 @@ static abi wontreturn void WinInit(const char16_t *cmdline) {
|
|||
(intptr_t)v_ntsubsystem == kNtImageSubsystemWindowsCui) {
|
||||
__imp_SetConsoleCP(kNtCpUtf8);
|
||||
__imp_SetConsoleOutputCP(kNtCpUtf8);
|
||||
for (int i = 1; i <= 2; ++i) {
|
||||
for (int i = 0; i <= 2; ++i) {
|
||||
uint32_t m;
|
||||
intptr_t h = __imp_GetStdHandle(kNtStdio[i]);
|
||||
__imp_GetConsoleMode(h, &m);
|
||||
__imp_SetConsoleMode(h, m | kNtEnableVirtualTerminalProcessing);
|
||||
if (!i) {
|
||||
m |= kNtEnableMouseInput | kNtEnableWindowInput;
|
||||
} else {
|
||||
m |= kNtEnableVirtualTerminalProcessing;
|
||||
}
|
||||
__imp_SetConsoleMode(h, m);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,9 +228,6 @@ abi int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
|
|||
_weaken(WinSockInit)();
|
||||
}
|
||||
DeduplicateStdioHandles();
|
||||
if (_weaken(WinMainStdin)) {
|
||||
_weaken(WinMainStdin)();
|
||||
}
|
||||
if (_weaken(WinMainForked)) {
|
||||
_weaken(WinMainForked)();
|
||||
}
|
||||
|
|
|
@ -111,23 +111,27 @@ static void _pthread_cancel_listen(void) {
|
|||
|
||||
static void pthread_cancel_nt(struct PosixThread *pt, intptr_t hThread) {
|
||||
uint32_t old_suspend_count;
|
||||
if ((pt->pt_flags & PT_ASYNC) && !(pt->pt_flags & PT_NOCANCEL)) {
|
||||
if ((old_suspend_count = SuspendThread(hThread)) != -1u) {
|
||||
if (!(pt->pt_flags & PT_NOCANCEL) &&
|
||||
(pt->pt_flags & (PT_ASYNC | PT_MASKED))) {
|
||||
pt->pt_flags |= PT_NOCANCEL;
|
||||
pt->abort_errno = ECANCELED;
|
||||
if ((pt->pt_flags & PT_ASYNC) &&
|
||||
(old_suspend_count = SuspendThread(hThread)) != -1u) {
|
||||
if (!old_suspend_count) {
|
||||
struct NtContext cpu;
|
||||
cpu.ContextFlags = kNtContextControl | kNtContextInteger;
|
||||
if (GetThreadContext(hThread, &cpu)) {
|
||||
pt->pt_flags |= PT_NOCANCEL;
|
||||
cpu.Rip = (uintptr_t)pthread_exit;
|
||||
cpu.Rdi = (uintptr_t)PTHREAD_CANCELED;
|
||||
cpu.Rsp &= -16;
|
||||
*(uintptr_t *)(cpu.Rsp -= sizeof(uintptr_t)) = cpu.Rip;
|
||||
pt->abort_errno = ECANCELED;
|
||||
unassert(SetThreadContext(hThread, &cpu));
|
||||
__sig_cancel(pt, 0);
|
||||
}
|
||||
}
|
||||
ResumeThread(hThread);
|
||||
}
|
||||
__sig_cancel(pt);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,3 +95,15 @@ TEST(fionread, pipe) {
|
|||
ASSERT_SYS(0, 0, close(pfds[1]));
|
||||
ASSERT_SYS(0, 0, close(pfds[0]));
|
||||
}
|
||||
|
||||
TEST(fionread, eof_returnsZeroWithoutError) {
|
||||
char buf[8];
|
||||
int pfds[2];
|
||||
int pending;
|
||||
ASSERT_SYS(0, 0, pipe(pfds));
|
||||
ASSERT_SYS(0, 0, close(pfds[1]));
|
||||
ASSERT_SYS(0, 0, ioctl(pfds[0], FIONREAD, &pending));
|
||||
ASSERT_EQ(0, pending);
|
||||
ASSERT_SYS(0, 0, read(pfds[0], buf, 8));
|
||||
ASSERT_SYS(0, 0, close(pfds[0]));
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue