1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2020 Dmitry Kozlyuk
3 */
4
5 #include <io.h>
6
7 #include "cmdline_private.h"
8
9 /* Missing from some MinGW-w64 distributions. */
10 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
11 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
12 #endif
13
14 #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
15 #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
16 #endif
17
18 void
terminal_adjust(struct cmdline * cl)19 terminal_adjust(struct cmdline *cl)
20 {
21 HANDLE handle;
22 DWORD mode;
23
24 ZeroMemory(&cl->oldterm, sizeof(cl->oldterm));
25
26 /* Detect console input, set it up and make it emulate VT100. */
27 handle = GetStdHandle(STD_INPUT_HANDLE);
28 if (GetConsoleMode(handle, &mode)) {
29 cl->oldterm.is_console_input = 1;
30 cl->oldterm.input_mode = mode;
31
32 mode &= ~(
33 ENABLE_LINE_INPUT | /* no line buffering */
34 ENABLE_ECHO_INPUT | /* no echo */
35 ENABLE_PROCESSED_INPUT | /* pass Ctrl+C to program */
36 ENABLE_MOUSE_INPUT | /* no mouse events */
37 ENABLE_WINDOW_INPUT); /* no window resize events */
38 mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
39 SetConsoleMode(handle, mode);
40 }
41
42 /* Detect console output and make it emulate VT100. */
43 handle = GetStdHandle(STD_OUTPUT_HANDLE);
44 if (GetConsoleMode(handle, &mode)) {
45 cl->oldterm.is_console_output = 1;
46 cl->oldterm.output_mode = mode;
47
48 mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
49 mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
50 SetConsoleMode(handle, mode);
51 }
52 }
53
54 void
terminal_restore(const struct cmdline * cl)55 terminal_restore(const struct cmdline *cl)
56 {
57 if (cl->oldterm.is_console_input) {
58 HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
59 SetConsoleMode(handle, cl->oldterm.input_mode);
60 }
61
62 if (cl->oldterm.is_console_output) {
63 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
64 SetConsoleMode(handle, cl->oldterm.output_mode);
65 }
66 }
67
68 static int
cmdline_is_key_down(const INPUT_RECORD * record)69 cmdline_is_key_down(const INPUT_RECORD *record)
70 {
71 return (record->EventType == KEY_EVENT) &&
72 record->Event.KeyEvent.bKeyDown;
73 }
74
75 ssize_t
cmdline_read_char(struct cmdline * cl,char * c)76 cmdline_read_char(struct cmdline *cl, char *c)
77 {
78 HANDLE handle;
79 INPUT_RECORD record;
80 KEY_EVENT_RECORD *key;
81 DWORD events;
82
83 if (!cl->oldterm.is_console_input)
84 return _read(cl->s_in, c, 1);
85
86 /* Return repeated strokes from previous event. */
87 if (cl->repeat_count > 0) {
88 *c = cl->repeated_char;
89 cl->repeat_count--;
90 return 1;
91 }
92
93 handle = (HANDLE)_get_osfhandle(cl->s_in);
94 key = &record.Event.KeyEvent;
95 do {
96 if (!ReadConsoleInput(handle, &record, 1, &events)) {
97 if (GetLastError() == ERROR_HANDLE_EOF) {
98 *c = EOF;
99 return 0;
100 }
101 return -1;
102 }
103 } while (!cmdline_is_key_down(&record));
104
105 *c = key->uChar.AsciiChar;
106
107 /* Save repeated strokes from a single event. */
108 if (key->wRepeatCount > 1) {
109 cl->repeated_char = *c;
110 cl->repeat_count = key->wRepeatCount - 1;
111 }
112
113 return 1;
114 }
115
116 int
cmdline_vdprintf(int fd,const char * format,va_list op)117 cmdline_vdprintf(int fd, const char *format, va_list op)
118 {
119 int copy, ret;
120 FILE *file;
121
122 copy = _dup(fd);
123 if (copy < 0)
124 return -1;
125
126 file = _fdopen(copy, "a");
127 if (file == NULL) {
128 _close(copy);
129 return -1;
130 }
131
132 ret = vfprintf(file, format, op);
133
134 fclose(file); /* also closes copy */
135
136 return ret;
137 }
138
139 void
cmdline_cancel(struct cmdline * cl)140 cmdline_cancel(struct cmdline *cl)
141 {
142 if (!cl)
143 return;
144
145 /* force the outstanding read on console to exit */
146 if (cl->oldterm.is_console_input) {
147 HANDLE handle = (HANDLE)_get_osfhandle(cl->s_in);
148
149 CancelIoEx(handle, NULL);
150 }
151 }
152