Improve Windows Console I/O

- Blocking read operations on the Windows Console can now EINTR
- Blocking read operations on Windows pipes now EINTR more reliably
- setitimer() will no longer be inherited across fork() on Windows
- It's now possible to use ECHO when the console is in raw mode
- The ECHOCTL flag now works correctly on the Windows Console
- The ICRNL flag now works correctly on the Windows Console
- pread() and pwrite() will now raise ESPIPE on Windows
- Opening /dev/tty on Windows is improved (untested)
- Overlapped I/O is now implemented in a better way
This commit is contained in:
Justine Tunney 2023-08-08 04:00:29 -07:00
parent decf216655
commit 33d280c8ba
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
34 changed files with 580 additions and 376 deletions

View file

@ -29,57 +29,86 @@
#include "libc/sysv/consts/termios.h"
#include "libc/sysv/errfuns.h"
textwindows int tcsetattr_nt(int ignored, int opt, const struct termios *tio) {
int64_t in, out;
bool32 ok, inok, outok;
textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
bool32 ok;
int ttymagic;
int64_t hInput, hOutput;
uint32_t inmode, outmode;
inok = GetConsoleMode((in = __getfdhandleactual(0)), &inmode);
outok = GetConsoleMode((out = __getfdhandleactual(1)), &outmode);
if (inok | outok) {
if (__isfdkind(fd, kFdConsole)) {
// program manually opened /dev/tty in O_RDWR mode for cmd.exe
hInput = g_fds.p[fd].handle;
hOutput = g_fds.p[fd].extra;
} else if (fd == 0 || fd == 1) {
// otherwise just assume cmd.exe console stdio
// there's no serial port support yet
hInput = g_fds.p[0].handle;
hOutput = g_fds.p[1].handle;
fd = 0;
} else {
STRACE("tcsetattr(fd) must be 0, 1, or open'd /dev/tty");
return enotty();
}
if (GetConsoleMode(hInput, &inmode) && GetConsoleMode(hOutput, &outmode)) {
if (inok) {
if (opt == TCSAFLUSH) {
FlushConsoleInputBuffer(in);
if (opt == TCSAFLUSH) {
FlushConsoleInputBuffer(hInput);
}
inmode &=
~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput);
inmode |= kNtEnableWindowInput;
ttymagic = 0;
if (tio->c_lflag & ICANON) {
inmode |= kNtEnableLineInput;
} else {
ttymagic |= kFdTtyMunging;
if (tio->c_cc[VMIN] != 1) {
STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows");
return einval();
}
inmode &=
~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput);
inmode |= kNtEnableWindowInput;
}
if (!(tio->c_iflag & ICRNL)) {
ttymagic |= kFdTtyNoCr2Nl;
}
if (!(tio->c_lflag & ECHOCTL)) {
ttymagic |= kFdTtyEchoRaw;
}
if (tio->c_lflag & ECHO) {
// "kNtEnableEchoInput can be used only if the
// kNtEnableLineInput mode is also enabled." -MSDN
if (tio->c_lflag & ICANON) {
inmode |= kNtEnableLineInput;
inmode |= kNtEnableEchoInput;
} else {
// If ECHO is enabled in raw mode, then read(0) needs to
// magically write(1) to simulate echoing. This normally
// visualizes control codes, e.g. \r → ^M unless ECHOCTL
// hasn't been specified.
ttymagic |= kFdTtyEchoing;
}
if (tio->c_lflag & ECHO) {
/*
* kNtEnableEchoInput can be used only if the ENABLE_LINE_INPUT mode
* is also enabled. --Quoth MSDN
*/
inmode |= kNtEnableEchoInput | kNtEnableLineInput;
}
if (tio->c_lflag & (IEXTEN | ISIG)) {
inmode |= kNtEnableProcessedInput;
}
if (IsAtLeastWindows10()) {
inmode |= kNtEnableVirtualTerminalInput;
}
ok = SetConsoleMode(in, inmode);
(void)ok;
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", in,
DescribeNtConsoleInFlags(inmode), ok);
}
if (tio->c_lflag & (IEXTEN | ISIG)) {
inmode |= kNtEnableProcessedInput;
}
if (IsAtLeastWindows10()) {
inmode |= kNtEnableVirtualTerminalInput;
}
g_fds.p[fd].ttymagic = ttymagic;
ok = SetConsoleMode(hInput, inmode);
(void)ok;
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hInput,
DescribeNtConsoleInFlags(inmode), ok);
if (outok) {
outmode &= ~(kNtDisableNewlineAutoReturn);
outmode |= kNtEnableProcessedOutput;
if (!(tio->c_oflag & ONLCR)) {
outmode |= kNtDisableNewlineAutoReturn;
}
if (IsAtLeastWindows10()) {
outmode |= kNtEnableVirtualTerminalProcessing;
}
ok = SetConsoleMode(out, outmode);
(void)ok;
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", out,
DescribeNtConsoleOutFlags(outmode), ok);
outmode &= ~kNtDisableNewlineAutoReturn;
outmode |= kNtEnableProcessedOutput;
if (!(tio->c_oflag & ONLCR)) {
outmode |= kNtDisableNewlineAutoReturn;
}
if (IsAtLeastWindows10()) {
outmode |= kNtEnableVirtualTerminalProcessing;
}
ok = SetConsoleMode(hOutput, outmode);
(void)ok;
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hOutput,
DescribeNtConsoleOutFlags(outmode), ok);
return 0;
} else {