1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2020 Dmitry Kozlyuk
9 #include "cmdline_private.h"
11 /* Missing from some MinGW-w64 distributions. */
12 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
13 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
16 #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
17 #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
21 terminal_adjust(struct cmdline *cl)
26 ZeroMemory(&cl->oldterm, sizeof(cl->oldterm));
28 /* Detect console input, set it up and make it emulate VT100. */
29 handle = GetStdHandle(STD_INPUT_HANDLE);
30 if (GetConsoleMode(handle, &mode)) {
31 cl->oldterm.is_console_input = 1;
32 cl->oldterm.input_mode = mode;
35 ENABLE_LINE_INPUT | /* no line buffering */
36 ENABLE_ECHO_INPUT | /* no echo */
37 ENABLE_PROCESSED_INPUT | /* pass Ctrl+C to program */
38 ENABLE_MOUSE_INPUT | /* no mouse events */
39 ENABLE_WINDOW_INPUT); /* no window resize events */
40 mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
41 SetConsoleMode(handle, mode);
44 /* Detect console output and make it emulate VT100. */
45 handle = GetStdHandle(STD_OUTPUT_HANDLE);
46 if (GetConsoleMode(handle, &mode)) {
47 cl->oldterm.is_console_output = 1;
48 cl->oldterm.output_mode = mode;
50 mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
51 mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
52 SetConsoleMode(handle, mode);
57 terminal_restore(const struct cmdline *cl)
59 if (cl->oldterm.is_console_input) {
60 HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
61 SetConsoleMode(handle, cl->oldterm.input_mode);
64 if (cl->oldterm.is_console_output) {
65 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
66 SetConsoleMode(handle, cl->oldterm.output_mode);
71 cmdline_is_key_down(const INPUT_RECORD *record)
73 return (record->EventType == KEY_EVENT) &&
74 record->Event.KeyEvent.bKeyDown;
78 cmdline_poll_char_console(HANDLE handle)
83 if (!PeekConsoleInput(handle, &record, 1, &events)) {
84 /* Simulate poll(3) behavior on EOF. */
85 return (GetLastError() == ERROR_HANDLE_EOF) ? 1 : -1;
88 if ((events == 0) || !cmdline_is_key_down(&record))
95 cmdline_poll_char_file(struct cmdline *cl, HANDLE handle)
97 DWORD type = GetFileType(handle);
99 /* Since console is handled by cmdline_poll_char_console(),
100 * this is either a serial port or input handle had been replaced.
102 if (type == FILE_TYPE_CHAR)
103 return cmdline_poll_char_console(handle);
105 /* PeekNamedPipe() can handle all pipes and also sockets. */
106 if (type == FILE_TYPE_PIPE) {
108 if (!PeekNamedPipe(handle, NULL, 0, NULL, &bytes_avail, NULL))
109 return (GetLastError() == ERROR_BROKEN_PIPE) ? 1 : -1;
110 return bytes_avail ? 1 : 0;
113 /* There is no straightforward way to peek a file in Windows
114 * I/O model. Read the byte, if it is not the end of file,
115 * buffer it for subsequent read. This will not work with
116 * a file being appended and probably some other edge cases.
118 if (type == FILE_TYPE_DISK) {
122 ret = _read(cl->s_in, &c, sizeof(c));
124 cl->repeat_count = 1;
125 cl->repeated_char = c;
130 /* GetFileType() failed or file of unknown type,
131 * which we do not know how to peek anyway.
137 cmdline_poll_char(struct cmdline *cl)
139 HANDLE handle = (HANDLE)_get_osfhandle(cl->s_in);
140 return cl->oldterm.is_console_input ?
141 cmdline_poll_char_console(handle) :
142 cmdline_poll_char_file(cl, handle);
146 cmdline_read_char(struct cmdline *cl, char *c)
150 KEY_EVENT_RECORD *key;
153 if (!cl->oldterm.is_console_input)
154 return _read(cl->s_in, c, 1);
156 /* Return repeated strokes from previous event. */
157 if (cl->repeat_count > 0) {
158 *c = cl->repeated_char;
163 handle = (HANDLE)_get_osfhandle(cl->s_in);
164 key = &record.Event.KeyEvent;
166 if (!ReadConsoleInput(handle, &record, 1, &events)) {
167 if (GetLastError() == ERROR_HANDLE_EOF) {
173 } while (!cmdline_is_key_down(&record));
175 *c = key->uChar.AsciiChar;
177 /* Save repeated strokes from a single event. */
178 if (key->wRepeatCount > 1) {
179 cl->repeated_char = *c;
180 cl->repeat_count = key->wRepeatCount - 1;
187 cmdline_vdprintf(int fd, const char *format, va_list op)
196 file = _fdopen(copy, "a");
202 ret = vfprintf(file, format, op);
204 fclose(file); /* also closes copy */