199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson * Copyright (c) 2020 Dmitry Kozlyuk
399a2dd95SBruce Richardson */
499a2dd95SBruce Richardson
599a2dd95SBruce Richardson #include <io.h>
699a2dd95SBruce Richardson
799a2dd95SBruce Richardson #include "cmdline_private.h"
899a2dd95SBruce Richardson
999a2dd95SBruce Richardson /* Missing from some MinGW-w64 distributions. */
1099a2dd95SBruce Richardson #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
1199a2dd95SBruce Richardson #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
1299a2dd95SBruce Richardson #endif
1399a2dd95SBruce Richardson
1499a2dd95SBruce Richardson #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
1599a2dd95SBruce Richardson #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
1699a2dd95SBruce Richardson #endif
1799a2dd95SBruce Richardson
1899a2dd95SBruce Richardson void
terminal_adjust(struct cmdline * cl)1999a2dd95SBruce Richardson terminal_adjust(struct cmdline *cl)
2099a2dd95SBruce Richardson {
2199a2dd95SBruce Richardson HANDLE handle;
2299a2dd95SBruce Richardson DWORD mode;
2399a2dd95SBruce Richardson
2499a2dd95SBruce Richardson ZeroMemory(&cl->oldterm, sizeof(cl->oldterm));
2599a2dd95SBruce Richardson
2699a2dd95SBruce Richardson /* Detect console input, set it up and make it emulate VT100. */
2799a2dd95SBruce Richardson handle = GetStdHandle(STD_INPUT_HANDLE);
2899a2dd95SBruce Richardson if (GetConsoleMode(handle, &mode)) {
2999a2dd95SBruce Richardson cl->oldterm.is_console_input = 1;
3099a2dd95SBruce Richardson cl->oldterm.input_mode = mode;
3199a2dd95SBruce Richardson
3299a2dd95SBruce Richardson mode &= ~(
3399a2dd95SBruce Richardson ENABLE_LINE_INPUT | /* no line buffering */
3499a2dd95SBruce Richardson ENABLE_ECHO_INPUT | /* no echo */
3599a2dd95SBruce Richardson ENABLE_PROCESSED_INPUT | /* pass Ctrl+C to program */
3699a2dd95SBruce Richardson ENABLE_MOUSE_INPUT | /* no mouse events */
3799a2dd95SBruce Richardson ENABLE_WINDOW_INPUT); /* no window resize events */
3899a2dd95SBruce Richardson mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
3999a2dd95SBruce Richardson SetConsoleMode(handle, mode);
4099a2dd95SBruce Richardson }
4199a2dd95SBruce Richardson
4299a2dd95SBruce Richardson /* Detect console output and make it emulate VT100. */
4399a2dd95SBruce Richardson handle = GetStdHandle(STD_OUTPUT_HANDLE);
4499a2dd95SBruce Richardson if (GetConsoleMode(handle, &mode)) {
4599a2dd95SBruce Richardson cl->oldterm.is_console_output = 1;
4699a2dd95SBruce Richardson cl->oldterm.output_mode = mode;
4799a2dd95SBruce Richardson
4899a2dd95SBruce Richardson mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
4999a2dd95SBruce Richardson mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
5099a2dd95SBruce Richardson SetConsoleMode(handle, mode);
5199a2dd95SBruce Richardson }
5299a2dd95SBruce Richardson }
5399a2dd95SBruce Richardson
5499a2dd95SBruce Richardson void
terminal_restore(const struct cmdline * cl)5599a2dd95SBruce Richardson terminal_restore(const struct cmdline *cl)
5699a2dd95SBruce Richardson {
5799a2dd95SBruce Richardson if (cl->oldterm.is_console_input) {
5899a2dd95SBruce Richardson HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
5999a2dd95SBruce Richardson SetConsoleMode(handle, cl->oldterm.input_mode);
6099a2dd95SBruce Richardson }
6199a2dd95SBruce Richardson
6299a2dd95SBruce Richardson if (cl->oldterm.is_console_output) {
6399a2dd95SBruce Richardson HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
6499a2dd95SBruce Richardson SetConsoleMode(handle, cl->oldterm.output_mode);
6599a2dd95SBruce Richardson }
6699a2dd95SBruce Richardson }
6799a2dd95SBruce Richardson
6899a2dd95SBruce Richardson static int
cmdline_is_key_down(const INPUT_RECORD * record)6999a2dd95SBruce Richardson cmdline_is_key_down(const INPUT_RECORD *record)
7099a2dd95SBruce Richardson {
7199a2dd95SBruce Richardson return (record->EventType == KEY_EVENT) &&
7299a2dd95SBruce Richardson record->Event.KeyEvent.bKeyDown;
7399a2dd95SBruce Richardson }
7499a2dd95SBruce Richardson
7599a2dd95SBruce Richardson ssize_t
cmdline_read_char(struct cmdline * cl,char * c)7699a2dd95SBruce Richardson cmdline_read_char(struct cmdline *cl, char *c)
7799a2dd95SBruce Richardson {
7899a2dd95SBruce Richardson HANDLE handle;
7999a2dd95SBruce Richardson INPUT_RECORD record;
8099a2dd95SBruce Richardson KEY_EVENT_RECORD *key;
8199a2dd95SBruce Richardson DWORD events;
8299a2dd95SBruce Richardson
8399a2dd95SBruce Richardson if (!cl->oldterm.is_console_input)
8499a2dd95SBruce Richardson return _read(cl->s_in, c, 1);
8599a2dd95SBruce Richardson
8699a2dd95SBruce Richardson /* Return repeated strokes from previous event. */
8799a2dd95SBruce Richardson if (cl->repeat_count > 0) {
8899a2dd95SBruce Richardson *c = cl->repeated_char;
8999a2dd95SBruce Richardson cl->repeat_count--;
9099a2dd95SBruce Richardson return 1;
9199a2dd95SBruce Richardson }
9299a2dd95SBruce Richardson
9399a2dd95SBruce Richardson handle = (HANDLE)_get_osfhandle(cl->s_in);
9499a2dd95SBruce Richardson key = &record.Event.KeyEvent;
9599a2dd95SBruce Richardson do {
9699a2dd95SBruce Richardson if (!ReadConsoleInput(handle, &record, 1, &events)) {
9799a2dd95SBruce Richardson if (GetLastError() == ERROR_HANDLE_EOF) {
9899a2dd95SBruce Richardson *c = EOF;
9999a2dd95SBruce Richardson return 0;
10099a2dd95SBruce Richardson }
10199a2dd95SBruce Richardson return -1;
10299a2dd95SBruce Richardson }
10399a2dd95SBruce Richardson } while (!cmdline_is_key_down(&record));
10499a2dd95SBruce Richardson
10599a2dd95SBruce Richardson *c = key->uChar.AsciiChar;
10699a2dd95SBruce Richardson
10799a2dd95SBruce Richardson /* Save repeated strokes from a single event. */
10899a2dd95SBruce Richardson if (key->wRepeatCount > 1) {
10999a2dd95SBruce Richardson cl->repeated_char = *c;
11099a2dd95SBruce Richardson cl->repeat_count = key->wRepeatCount - 1;
11199a2dd95SBruce Richardson }
11299a2dd95SBruce Richardson
11399a2dd95SBruce Richardson return 1;
11499a2dd95SBruce Richardson }
11599a2dd95SBruce Richardson
11699a2dd95SBruce Richardson int
cmdline_vdprintf(int fd,const char * format,va_list op)11799a2dd95SBruce Richardson cmdline_vdprintf(int fd, const char *format, va_list op)
11899a2dd95SBruce Richardson {
11999a2dd95SBruce Richardson int copy, ret;
12099a2dd95SBruce Richardson FILE *file;
12199a2dd95SBruce Richardson
12299a2dd95SBruce Richardson copy = _dup(fd);
12399a2dd95SBruce Richardson if (copy < 0)
12499a2dd95SBruce Richardson return -1;
12599a2dd95SBruce Richardson
12699a2dd95SBruce Richardson file = _fdopen(copy, "a");
12799a2dd95SBruce Richardson if (file == NULL) {
12899a2dd95SBruce Richardson _close(copy);
12999a2dd95SBruce Richardson return -1;
13099a2dd95SBruce Richardson }
13199a2dd95SBruce Richardson
13299a2dd95SBruce Richardson ret = vfprintf(file, format, op);
13399a2dd95SBruce Richardson
13499a2dd95SBruce Richardson fclose(file); /* also closes copy */
13599a2dd95SBruce Richardson
13699a2dd95SBruce Richardson return ret;
13799a2dd95SBruce Richardson }
138*f1d0993eSStephen Hemminger
139*f1d0993eSStephen Hemminger void
cmdline_cancel(struct cmdline * cl)140*f1d0993eSStephen Hemminger cmdline_cancel(struct cmdline *cl)
141*f1d0993eSStephen Hemminger {
142*f1d0993eSStephen Hemminger if (!cl)
143*f1d0993eSStephen Hemminger return;
144*f1d0993eSStephen Hemminger
145*f1d0993eSStephen Hemminger /* force the outstanding read on console to exit */
146*f1d0993eSStephen Hemminger if (cl->oldterm.is_console_input) {
147*f1d0993eSStephen Hemminger HANDLE handle = (HANDLE)_get_osfhandle(cl->s_in);
148*f1d0993eSStephen Hemminger
149*f1d0993eSStephen Hemminger CancelIoEx(handle, NULL);
150*f1d0993eSStephen Hemminger }
151*f1d0993eSStephen Hemminger }
152