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 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 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 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 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 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 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