Fossil SCM

fossil-scm / extsrc / linenoise-win32.c
Source Blame History 379 lines
6782c57… stephan 1
6782c57… stephan 2 /* this code is not standalone
6782c57… stephan 3 * it is included into linenoise.c
6782c57… stephan 4 * for windows.
6782c57… stephan 5 * It is deliberately kept separate so that
6782c57… stephan 6 * applications that have no need for windows
6782c57… stephan 7 * support can omit this
6782c57… stephan 8 */
6782c57… stephan 9 static DWORD orig_consolemode = 0;
6782c57… stephan 10
6782c57… stephan 11 static int flushOutput(struct current *current);
6782c57… stephan 12 static void outputNewline(struct current *current);
6782c57… stephan 13
6782c57… stephan 14 static void refreshStart(struct current *current)
6782c57… stephan 15 {
6782c57… stephan 16 (void)current;
6782c57… stephan 17 }
6782c57… stephan 18
6782c57… stephan 19 static void refreshEnd(struct current *current)
6782c57… stephan 20 {
6782c57… stephan 21 (void)current;
6782c57… stephan 22 }
6782c57… stephan 23
6782c57… stephan 24 static void refreshStartChars(struct current *current)
6782c57… stephan 25 {
6782c57… stephan 26 assert(current->output == NULL);
6782c57… stephan 27 /* We accumulate all output here */
6782c57… stephan 28 current->output = sb_alloc();
6782c57… stephan 29 #ifdef USE_UTF8
6782c57… stephan 30 current->ubuflen = 0;
6782c57… stephan 31 #endif
6782c57… stephan 32 }
6782c57… stephan 33
6782c57… stephan 34 static void refreshNewline(struct current *current)
6782c57… stephan 35 {
6782c57… stephan 36 DRL("<nl>");
6782c57… stephan 37 outputNewline(current);
6782c57… stephan 38 }
6782c57… stephan 39
6782c57… stephan 40 static void refreshEndChars(struct current *current)
6782c57… stephan 41 {
6782c57… stephan 42 assert(current->output);
6782c57… stephan 43 flushOutput(current);
6782c57… stephan 44 sb_free(current->output);
6782c57… stephan 45 current->output = NULL;
6782c57… stephan 46 }
6782c57… stephan 47
6782c57… stephan 48 static int enableRawMode(struct current *current) {
6782c57… stephan 49 DWORD n;
6782c57… stephan 50 INPUT_RECORD irec;
6782c57… stephan 51
6782c57… stephan 52 current->outh = GetStdHandle(STD_OUTPUT_HANDLE);
6782c57… stephan 53 current->inh = GetStdHandle(STD_INPUT_HANDLE);
6782c57… stephan 54
6782c57… stephan 55 if (!PeekConsoleInput(current->inh, &irec, 1, &n)) {
6782c57… stephan 56 return -1;
6782c57… stephan 57 }
6782c57… stephan 58 if (getWindowSize(current) != 0) {
6782c57… stephan 59 return -1;
6782c57… stephan 60 }
6782c57… stephan 61 if (GetConsoleMode(current->inh, &orig_consolemode)) {
6782c57… stephan 62 SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT);
6782c57… stephan 63 }
6782c57… stephan 64 #ifdef USE_UTF8
6782c57… stephan 65 /* XXX is this the right thing to do? */
6782c57… stephan 66 SetConsoleCP(65001);
6782c57… stephan 67 #endif
6782c57… stephan 68 return 0;
6782c57… stephan 69 }
6782c57… stephan 70
6782c57… stephan 71 static void disableRawMode(struct current *current)
6782c57… stephan 72 {
6782c57… stephan 73 SetConsoleMode(current->inh, orig_consolemode);
6782c57… stephan 74 }
6782c57… stephan 75
6782c57… stephan 76 void linenoiseClearScreen(void)
6782c57… stephan 77 {
6782c57… stephan 78 /* XXX: This is ugly. Should just have the caller pass a handle */
6782c57… stephan 79 struct current current;
6782c57… stephan 80
6782c57… stephan 81 current.outh = GetStdHandle(STD_OUTPUT_HANDLE);
6782c57… stephan 82
6782c57… stephan 83 if (getWindowSize(&current) == 0) {
6782c57… stephan 84 COORD topleft = { 0, 0 };
6782c57… stephan 85 DWORD n;
6782c57… stephan 86
6782c57… stephan 87 FillConsoleOutputCharacter(current.outh, ' ',
6782c57… stephan 88 current.cols * current.rows, topleft, &n);
6782c57… stephan 89 FillConsoleOutputAttribute(current.outh,
6782c57… stephan 90 FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN,
6782c57… stephan 91 current.cols * current.rows, topleft, &n);
6782c57… stephan 92 SetConsoleCursorPosition(current.outh, topleft);
6782c57… stephan 93 }
6782c57… stephan 94 }
6782c57… stephan 95
6782c57… stephan 96 static void cursorToLeft(struct current *current)
6782c57… stephan 97 {
6782c57… stephan 98 COORD pos;
6782c57… stephan 99 DWORD n;
6782c57… stephan 100
6782c57… stephan 101 pos.X = 0;
6782c57… stephan 102 pos.Y = (SHORT)current->y;
6782c57… stephan 103
6782c57… stephan 104 FillConsoleOutputAttribute(current->outh,
6782c57… stephan 105 FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n);
6782c57… stephan 106 current->x = 0;
6782c57… stephan 107 }
6782c57… stephan 108
6782c57… stephan 109 #ifdef USE_UTF8
6782c57… stephan 110 static void flush_ubuf(struct current *current)
6782c57… stephan 111 {
6782c57… stephan 112 COORD pos;
6782c57… stephan 113 DWORD nwritten;
6782c57… stephan 114 pos.Y = (SHORT)current->y;
6782c57… stephan 115 pos.X = (SHORT)current->x;
6782c57… stephan 116 SetConsoleCursorPosition(current->outh, pos);
6782c57… stephan 117 WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0);
6782c57… stephan 118 current->x += current->ubufcols;
6782c57… stephan 119 current->ubuflen = 0;
6782c57… stephan 120 current->ubufcols = 0;
6782c57… stephan 121 }
6782c57… stephan 122
6782c57… stephan 123 static void add_ubuf(struct current *current, int ch)
6782c57… stephan 124 {
6782c57… stephan 125 /* This code originally by: Author: Mark E. Davis, 1994. */
6782c57… stephan 126 static const int halfShift = 10; /* used for shifting by 10 bits */
6782c57… stephan 127
6782c57… stephan 128 static const DWORD halfBase = 0x0010000UL;
6782c57… stephan 129 static const DWORD halfMask = 0x3FFUL;
6782c57… stephan 130
6782c57… stephan 131 #define UNI_SUR_HIGH_START 0xD800
6782c57… stephan 132 #define UNI_SUR_HIGH_END 0xDBFF
6782c57… stephan 133 #define UNI_SUR_LOW_START 0xDC00
6782c57… stephan 134 #define UNI_SUR_LOW_END 0xDFFF
6782c57… stephan 135
6782c57… stephan 136 #define UNI_MAX_BMP 0x0000FFFF
6782c57… stephan 137
6782c57… stephan 138 if (ch > UNI_MAX_BMP) {
6782c57… stephan 139 /* convert from unicode to utf16 surrogate pairs
6782c57… stephan 140 * There is always space for one extra word in ubuf
6782c57… stephan 141 */
6782c57… stephan 142 ch -= halfBase;
6782c57… stephan 143 current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START);
6782c57… stephan 144 current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START);
6782c57… stephan 145 }
6782c57… stephan 146 else {
6782c57… stephan 147 current->ubuf[current->ubuflen++] = ch;
6782c57… stephan 148 }
6782c57… stephan 149 current->ubufcols += utf8_width(ch);
6782c57… stephan 150 if (current->ubuflen >= UBUF_MAX_CHARS) {
6782c57… stephan 151 flush_ubuf(current);
6782c57… stephan 152 }
6782c57… stephan 153 }
6782c57… stephan 154 #endif
6782c57… stephan 155
6782c57… stephan 156 static int flushOutput(struct current *current)
6782c57… stephan 157 {
6782c57… stephan 158 const char *pt = sb_str(current->output);
6782c57… stephan 159 int len = sb_len(current->output);
6782c57… stephan 160
6782c57… stephan 161 #ifdef USE_UTF8
6782c57… stephan 162 /* convert utf8 in current->output into utf16 in current->ubuf
6782c57… stephan 163 */
6782c57… stephan 164 while (len) {
6782c57… stephan 165 int ch;
6782c57… stephan 166 int n = utf8_tounicode(pt, &ch);
6782c57… stephan 167
6782c57… stephan 168 pt += n;
6782c57… stephan 169 len -= n;
6782c57… stephan 170
6782c57… stephan 171 add_ubuf(current, ch);
6782c57… stephan 172 }
6782c57… stephan 173 flush_ubuf(current);
6782c57… stephan 174 #else
6782c57… stephan 175 DWORD nwritten;
6782c57… stephan 176 COORD pos;
6782c57… stephan 177
6782c57… stephan 178 pos.Y = (SHORT)current->y;
6782c57… stephan 179 pos.X = (SHORT)current->x;
6782c57… stephan 180
6782c57… stephan 181 SetConsoleCursorPosition(current->outh, pos);
6782c57… stephan 182 WriteConsoleA(current->outh, pt, len, &nwritten, 0);
6782c57… stephan 183
6782c57… stephan 184 current->x += len;
6782c57… stephan 185 #endif
6782c57… stephan 186
6782c57… stephan 187 sb_clear(current->output);
6782c57… stephan 188
6782c57… stephan 189 return 0;
6782c57… stephan 190 }
6782c57… stephan 191
6782c57… stephan 192 static int outputChars(struct current *current, const char *buf, int len)
6782c57… stephan 193 {
6782c57… stephan 194 if (len < 0) {
6782c57… stephan 195 len = strlen(buf);
6782c57… stephan 196 }
6782c57… stephan 197 assert(current->output);
6782c57… stephan 198
6782c57… stephan 199 sb_append_len(current->output, buf, len);
6782c57… stephan 200
6782c57… stephan 201 return 0;
6782c57… stephan 202 }
6782c57… stephan 203
6782c57… stephan 204 static void outputNewline(struct current *current)
6782c57… stephan 205 {
6782c57… stephan 206 /* On the last row output a newline to force a scroll */
6782c57… stephan 207 if (current->y + 1 == current->rows) {
6782c57… stephan 208 outputChars(current, "\n", 1);
6782c57… stephan 209 }
6782c57… stephan 210 flushOutput(current);
6782c57… stephan 211 current->x = 0;
6782c57… stephan 212 current->y++;
6782c57… stephan 213 }
6782c57… stephan 214
6782c57… stephan 215 static void setOutputHighlight(struct current *current, const int *props, int nprops)
6782c57… stephan 216 {
6782c57… stephan 217 int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
6782c57… stephan 218 int bold = 0;
6782c57… stephan 219 int reverse = 0;
6782c57… stephan 220 int i;
6782c57… stephan 221
6782c57… stephan 222 for (i = 0; i < nprops; i++) {
6782c57… stephan 223 switch (props[i]) {
6782c57… stephan 224 case 0:
6782c57… stephan 225 colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
6782c57… stephan 226 bold = 0;
6782c57… stephan 227 reverse = 0;
6782c57… stephan 228 break;
6782c57… stephan 229 case 1:
6782c57… stephan 230 bold = FOREGROUND_INTENSITY;
6782c57… stephan 231 break;
6782c57… stephan 232 case 7:
6782c57… stephan 233 reverse = 1;
6782c57… stephan 234 break;
6782c57… stephan 235 case 30:
6782c57… stephan 236 colour = 0;
6782c57… stephan 237 break;
6782c57… stephan 238 case 31:
6782c57… stephan 239 colour = FOREGROUND_RED;
6782c57… stephan 240 break;
6782c57… stephan 241 case 32:
6782c57… stephan 242 colour = FOREGROUND_GREEN;
6782c57… stephan 243 break;
6782c57… stephan 244 case 33:
6782c57… stephan 245 colour = FOREGROUND_RED | FOREGROUND_GREEN;
6782c57… stephan 246 break;
6782c57… stephan 247 case 34:
6782c57… stephan 248 colour = FOREGROUND_BLUE;
6782c57… stephan 249 break;
6782c57… stephan 250 case 35:
6782c57… stephan 251 colour = FOREGROUND_RED | FOREGROUND_BLUE;
6782c57… stephan 252 break;
6782c57… stephan 253 case 36:
6782c57… stephan 254 colour = FOREGROUND_BLUE | FOREGROUND_GREEN;
6782c57… stephan 255 break;
6782c57… stephan 256 case 37:
6782c57… stephan 257 colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN;
6782c57… stephan 258 break;
6782c57… stephan 259 }
6782c57… stephan 260 }
6782c57… stephan 261
6782c57… stephan 262 flushOutput(current);
6782c57… stephan 263
6782c57… stephan 264 if (reverse) {
6782c57… stephan 265 SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY);
6782c57… stephan 266 }
6782c57… stephan 267 else {
6782c57… stephan 268 SetConsoleTextAttribute(current->outh, colour | bold);
6782c57… stephan 269 }
6782c57… stephan 270 }
6782c57… stephan 271
6782c57… stephan 272 static void eraseEol(struct current *current)
6782c57… stephan 273 {
6782c57… stephan 274 COORD pos;
6782c57… stephan 275 DWORD n;
6782c57… stephan 276
6782c57… stephan 277 pos.X = (SHORT) current->x;
6782c57… stephan 278 pos.Y = (SHORT) current->y;
6782c57… stephan 279
6782c57… stephan 280 FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n);
6782c57… stephan 281 }
6782c57… stephan 282
6782c57… stephan 283 static void setCursorXY(struct current *current)
6782c57… stephan 284 {
6782c57… stephan 285 COORD pos;
6782c57… stephan 286
6782c57… stephan 287 pos.X = (SHORT) current->x;
6782c57… stephan 288 pos.Y = (SHORT) current->y;
6782c57… stephan 289
6782c57… stephan 290 SetConsoleCursorPosition(current->outh, pos);
6782c57… stephan 291 }
6782c57… stephan 292
6782c57… stephan 293
6782c57… stephan 294 static void setCursorPos(struct current *current, int x)
6782c57… stephan 295 {
6782c57… stephan 296 current->x = x;
6782c57… stephan 297 setCursorXY(current);
6782c57… stephan 298 }
6782c57… stephan 299
6782c57… stephan 300 static void cursorUp(struct current *current, int n)
6782c57… stephan 301 {
6782c57… stephan 302 current->y -= n;
6782c57… stephan 303 setCursorXY(current);
6782c57… stephan 304 }
6782c57… stephan 305
6782c57… stephan 306 static void cursorDown(struct current *current, int n)
6782c57… stephan 307 {
6782c57… stephan 308 current->y += n;
6782c57… stephan 309 setCursorXY(current);
6782c57… stephan 310 }
6782c57… stephan 311
6782c57… stephan 312 static int fd_read(struct current *current)
6782c57… stephan 313 {
6782c57… stephan 314 while (1) {
6782c57… stephan 315 INPUT_RECORD irec;
6782c57… stephan 316 DWORD n;
6782c57… stephan 317 if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) {
6782c57… stephan 318 break;
6782c57… stephan 319 }
6782c57… stephan 320 if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) {
6782c57… stephan 321 break;
6782c57… stephan 322 }
6782c57… stephan 323 if (irec.EventType == KEY_EVENT) {
6782c57… stephan 324 KEY_EVENT_RECORD *k = &irec.Event.KeyEvent;
6782c57… stephan 325 if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) {
6782c57… stephan 326 if (k->dwControlKeyState & ENHANCED_KEY) {
6782c57… stephan 327 switch (k->wVirtualKeyCode) {
6782c57… stephan 328 case VK_LEFT:
6782c57… stephan 329 return SPECIAL_LEFT;
6782c57… stephan 330 case VK_RIGHT:
6782c57… stephan 331 return SPECIAL_RIGHT;
6782c57… stephan 332 case VK_UP:
6782c57… stephan 333 return SPECIAL_UP;
6782c57… stephan 334 case VK_DOWN:
6782c57… stephan 335 return SPECIAL_DOWN;
6782c57… stephan 336 case VK_INSERT:
6782c57… stephan 337 return SPECIAL_INSERT;
6782c57… stephan 338 case VK_DELETE:
6782c57… stephan 339 return SPECIAL_DELETE;
6782c57… stephan 340 case VK_HOME:
6782c57… stephan 341 return SPECIAL_HOME;
6782c57… stephan 342 case VK_END:
6782c57… stephan 343 return SPECIAL_END;
6782c57… stephan 344 case VK_PRIOR:
6782c57… stephan 345 return SPECIAL_PAGE_UP;
6782c57… stephan 346 case VK_NEXT:
6782c57… stephan 347 return SPECIAL_PAGE_DOWN;
6782c57… stephan 348 case VK_RETURN:
6782c57… stephan 349 return k->uChar.UnicodeChar;
6782c57… stephan 350 }
6782c57… stephan 351 }
6782c57… stephan 352 /* Note that control characters are already translated in AsciiChar */
6782c57… stephan 353 else if (k->wVirtualKeyCode == VK_CONTROL)
6782c57… stephan 354 continue;
6782c57… stephan 355 else {
6782c57… stephan 356 return k->uChar.UnicodeChar;
6782c57… stephan 357 }
6782c57… stephan 358 }
6782c57… stephan 359 }
6782c57… stephan 360 }
6782c57… stephan 361 return -1;
6782c57… stephan 362 }
6782c57… stephan 363
6782c57… stephan 364 static int getWindowSize(struct current *current)
6782c57… stephan 365 {
6782c57… stephan 366 CONSOLE_SCREEN_BUFFER_INFO info;
6782c57… stephan 367 if (!GetConsoleScreenBufferInfo(current->outh, &info)) {
6782c57… stephan 368 return -1;
6782c57… stephan 369 }
6782c57… stephan 370 current->cols = info.dwSize.X;
6782c57… stephan 371 current->rows = info.dwSize.Y;
6782c57… stephan 372 if (current->cols <= 0 || current->rows <= 0) {
6782c57… stephan 373 current->cols = 80;
6782c57… stephan 374 return -1;
6782c57… stephan 375 }
6782c57… stephan 376 current->y = info.dwCursorPosition.Y;
6782c57… stephan 377 current->x = info.dwCursorPosition.X;
6782c57… stephan 378 return 0;
6782c57… stephan 379 }

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button