1 /*
2 * Uses the Win32 console API.
3 *
4 * $Id: ntconio.c,v 1.106 2020/01/17 23:11:00 tom Exp $
5 */
6
7 #include "estruct.h"
8 #include "edef.h"
9 #include "chgdfunc.h"
10
11 #include <process.h>
12
13 /*
14 * Define this if you want to kernel fault win95 when ctrl brk is pressed.
15 */
16 #undef DONT_USE_ON_WIN95
17
18 #define MAX_CURBLK_HEIGHT 100 /* max cursor block height (%) */
19
20 #define NROW 128 /* Max Screen size. */
21 #define NCOL 256 /* Edit if you want to. */
22 #define NOKYMAP (-1)
23
24 #define ABS(x) (((x) < 0) ? -(x) : (x))
25
26 /* CHAR_INFO and KEY_EVENT_RECORD use the same struct member */
27 #ifdef UNICODE
28 #define WHICH_CHAR UnicodeChar
29 #else
30 #define WHICH_CHAR AsciiChar
31 #endif
32
33 #define DFT_BCOLOR C_BLACK
34 #define DFT_FCOLOR ((ncolors >= 8) ? 7 : (ncolors - 1))
35
36 #define ForeColor(f) (ctrans[((f) < 0 ? DFT_FCOLOR : (f))] & (NCOLORS-1))
37 #define BackColor(b) (ctrans[((b) < 0 ? DFT_BCOLOR : (b))] & (NCOLORS-1))
38 #define AttrColor(b,f) ((WORD)((BackColor(b) << 4) | ForeColor(f)))
39
40 static HANDLE hConsoleOutput; /* handle to the console display */
41 static HANDLE hOldConsoleOutput; /* handle to the old console display */
42 static HANDLE hConsoleInput;
43 static CONSOLE_SCREEN_BUFFER_INFO csbi;
44 static CONSOLE_CURSOR_INFO origcci;
45 static BOOL origcci_ok;
46 static WORD originalAttribute;
47 static WORD currentAttribute;
48 static int icursor; /* T -> enable insertion cursor */
49 static int icursor_cmdmode; /* cmd mode cursor height */
50 static int icursor_insmode; /* insertion mode cursor height */
51 static int chgd_cursor; /* must restore cursor height on exit */
52 static ENC_CHOICES my_encoding = enc_DEFAULT;
53
54 static int conemu = 0; /* whether running under conemu */
55 static int cattr = 0; /* current attributes */
56 static int cfcolor = -1; /* current foreground color */
57 static int cbcolor = -1; /* current background color */
58 static int nfcolor = -1; /* normal foreground color */
59 static int nbcolor = -1; /* normal background color */
60 static int rvcolor = 0; /* nonzero if we reverse colors */
61 static int crow = -1; /* current row */
62 static int ccol = -1; /* current col */
63 static int keyboard_open = FALSE; /* keyboard is open */
64 static int keyboard_was_closed = TRUE;
65
66 #ifdef VAL_AUTOCOLOR
67 static int ac_active = FALSE; /* autocolor active */
68 #endif
69
70 /* ansi to ibm color translation table */
71 static const char *initpalettestr = "0 4 2 6 1 5 3 7 8 12 10 14 9 13 11 15";
72 /* black, red, green, yellow, blue, magenta, cyan, white */
73
74 static W32_CHAR linebuf[NCOL];
75 static int bufpos = 0;
76
77 /* Add state variables for console vile's autoscroll feature. */
78 static int buttondown = FALSE; /* is the left mouse button currently down? */
79 static RECT client_rect; /* writable portion of editor's window */
80 static HANDLE hAsMutex; /* autoscroll mutex handle (prevents worker
81 * thread and main thread from updating the
82 * display at the same time).
83 */
84 static WINDOW *mouse_wp; /* vile window ptr during mouse operations. */
85 static int row_height; /* pixels per row within client_rect */
86 #define AS_TMOUT 2000 /* hAsMutex timeout period (2 sec) */
87
88 #ifdef GVAL_VIDEO
89 static WORD
AttrVideo(int b,int f)90 AttrVideo(int b, int f)
91 {
92 WORD result;
93 if (conemu) {
94 if (cattr & VABOLD) {
95 result = ((WORD) ((12 << 4) | ForeColor(f)));
96 TRACE2(("bold AttrVideo(%d,%d) = %04x\n", f, b, result));
97 return result;
98 }
99 if (cattr & VAITAL) {
100 result = ((WORD) ((13 << 4) | ForeColor(f)));
101 TRACE2(("ital AttrVideo(%d,%d) = %04x\n", f, b, result));
102 return result;
103 }
104 }
105 if (rvcolor) {
106 result = ((WORD) ((ForeColor(f) << 4) | BackColor(b)));
107 TRACE2(("rev AttrVideo(%d,%d) = %04x\n", f, b, result));
108 } else {
109 result = ((WORD) ((BackColor(b) << 4) | ForeColor(f)));
110 TRACE2(("AttrVideo(%d,%d) = %04x\n", f, b, result));
111 }
112 return result;
113 }
114 #undef AttrColor
115 #define AttrColor(b,f) AttrVideo(b,f)
116 #endif
117
118 static void
set_current_attr(void)119 set_current_attr(void)
120 {
121 currentAttribute = AttrColor(cbcolor, cfcolor);
122 TRACE2(("set_current_attr %04x\n", currentAttribute));
123 }
124
125 static void
show_cursor(BOOL visible,int percent)126 show_cursor(BOOL visible, int percent)
127 {
128 CONSOLE_CURSOR_INFO cci;
129
130 /*
131 * if a 100% block height is fed back into the win32 console routines
132 * on a win9x/winme host, the cursor is turned off. win32 bug?
133 */
134 if (percent < 0)
135 percent = 0;
136 if (percent >= MAX_CURBLK_HEIGHT)
137 percent = (MAX_CURBLK_HEIGHT - 1);
138
139 cci.bVisible = visible;
140 cci.dwSize = percent;
141 SetConsoleCursorInfo(hConsoleOutput, &cci);
142 }
143
144 #if OPT_ICURSOR
145 static void
nticursor(int cmode)146 nticursor(int cmode)
147 {
148 if (icursor) {
149 show_cursor(TRUE, (cmode == 0) ? icursor_cmdmode : icursor_insmode);
150 }
151 }
152 #endif
153
154 #if OPT_TITLE
155 static void
ntconio_title(const char * title)156 ntconio_title(const char *title)
157 { /* set the current window title */
158 if (title != 0)
159 w32_set_console_title(title);
160 }
161 #endif
162
163 static void
scflush(void)164 scflush(void)
165 {
166 if (bufpos) {
167 COORD coordCursor;
168 DWORD written;
169
170 coordCursor.X = (SHORT) ccol;
171 coordCursor.Y = (SHORT) crow;
172 TRACE2(("scflush %04x [%d,%d]%.*s\n",
173 currentAttribute, crow, ccol, bufpos, linebuf));
174 WriteConsoleOutputCharacter(
175 hConsoleOutput, linebuf, bufpos,
176 coordCursor, &written
177 );
178 FillConsoleOutputAttribute(
179 hConsoleOutput, currentAttribute,
180 bufpos, coordCursor, &written
181 );
182 ccol += bufpos;
183 bufpos = 0;
184 }
185 }
186
187 #if OPT_COLOR
188 static void
ntconio_fcol(int color)189 ntconio_fcol(int color)
190 { /* set the current output color */
191 TRACE2(("ntconio_fcol(%d)\n", color));
192 scflush();
193 nfcolor = cfcolor = color;
194 set_current_attr();
195 }
196
197 static void
ntconio_bcol(int color)198 ntconio_bcol(int color)
199 { /* set the current background color */
200 TRACE2(("ntconio_bcol(%d)\n", color));
201 scflush();
202 nbcolor = cbcolor = color;
203 set_current_attr();
204 }
205 #endif
206
207 static void
ntconio_flush(void)208 ntconio_flush(void)
209 {
210 COORD coordCursor;
211
212 scflush();
213 coordCursor.X = (SHORT) ccol;
214 coordCursor.Y = (SHORT) crow;
215 SetConsoleCursorPosition(hConsoleOutput, coordCursor);
216 }
217
218 static void
ntconio_move(int row,int col)219 ntconio_move(int row, int col)
220 {
221 scflush();
222 crow = row;
223 ccol = col;
224 }
225
226 static void
erase_at(COORD coordCursor,int length)227 erase_at(COORD coordCursor, int length)
228 {
229 W32_CHAR blank = ' ';
230 DWORD written;
231
232 FillConsoleOutputCharacter(
233 hConsoleOutput, blank, length,
234 coordCursor, &written
235 );
236 FillConsoleOutputAttribute(
237 hConsoleOutput, currentAttribute, length,
238 coordCursor, &written
239 );
240 }
241
242 /* erase to the end of the line */
243 static void
ntconio_eeol(void)244 ntconio_eeol(void)
245 {
246 COORD coordCursor;
247 int length;
248
249 scflush();
250 length = csbi.dwMaximumWindowSize.X - ccol;
251 coordCursor.X = (SHORT) ccol;
252 coordCursor.Y = (SHORT) crow;
253 TRACE2(("ntconio_eeol [%d,%d] erase %d with %04x\n", crow, ccol, length, currentAttribute));
254 erase_at(coordCursor, length);
255 }
256
257 /*
258 * Move 'n' lines starting at 'from' to 'to'
259 *
260 * OPT_PRETTIER_SCROLL is prettier but slower -- it scrolls a line at a time
261 * instead of all at once.
262 */
263
264 /* move howmany lines starting at from to to */
265 static void
ntconio_scroll(int from,int to,int n)266 ntconio_scroll(int from, int to, int n)
267 {
268 SMALL_RECT sRect;
269 COORD dest;
270 CHAR_INFO fill;
271 int scroll_pause;
272
273 scflush();
274 if (to == from)
275 return;
276 #if OPT_PRETTIER_SCROLL
277 if (ABS(from - to) > 1) {
278 ntconio_scroll(from, (from < to) ? to - 1 : to + 1, n);
279 if (from < to)
280 from = to - 1;
281 else
282 from = to + 1;
283 }
284 #endif
285 fill.Char.WHICH_CHAR = ' ';
286 fill.Attributes = currentAttribute;
287
288 sRect.Left = 0;
289 sRect.Top = (SHORT) from;
290 sRect.Right = (SHORT) (csbi.dwMaximumWindowSize.X - 1);
291 sRect.Bottom = (SHORT) (from + n - 1);
292
293 dest.X = 0;
294 dest.Y = (SHORT) to;
295
296 ScrollConsoleScreenBuffer(hConsoleOutput, &sRect, NULL, dest, &fill);
297 if ((scroll_pause = global_g_val(GVAL_SCROLLPAUSE)) > 0) {
298 /*
299 * If the user has cheap video HW (1 MB or less) and
300 * there's a busy background app (say, dev studio), then
301 * the console version of vile can exhibit serious repaint
302 * problems when the display is rapidly scrolled. By
303 * inserting a user-defined sleep after the scroll, the
304 * video HW has a chance to properly paint before the
305 * next scroll operation.
306 */
307
308 Sleep(scroll_pause);
309 }
310 #if !OPT_PRETTIER_SCROLL
311 if (ABS(from - to) > n) {
312 DWORD cnt;
313 COORD coordCursor;
314
315 coordCursor.X = 0;
316 if (to > from) {
317 coordCursor.Y = (SHORT) (from + n);
318 cnt = to - from - n;
319 } else {
320 coordCursor.Y = (SHORT) (to + n);
321 cnt = from - to - n;
322 }
323 cnt *= csbi.dwMaximumWindowSize.X;
324 erase_at(coordCursor, cnt);
325 }
326 #endif
327 }
328
329 #if OPT_FLASH
330 static void
flash_display(void)331 flash_display(void)
332 {
333 DWORD length = term.cols * term.rows;
334 DWORD got;
335 WORD *buf1;
336 WORD *buf2;
337
338 static COORD origin;
339
340 if ((buf1 = typeallocn(WORD, length)) != 0
341 && (buf2 = typeallocn(WORD, length)) != 0) {
342 ReadConsoleOutputAttribute(hConsoleOutput, buf1, length, origin, &got);
343 ReadConsoleOutputAttribute(hConsoleOutput, buf2, length, origin, &got);
344 for (got = 0; got < length; got++) {
345 buf2[got] ^= (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
346 }
347 WriteConsoleOutputAttribute(hConsoleOutput, buf2, length, origin, &got);
348 Sleep(200);
349 WriteConsoleOutputAttribute(hConsoleOutput, buf1, length, origin, &got);
350 free(buf1);
351 free(buf2);
352 }
353 }
354 #endif
355
356 static void
ntconio_beep(void)357 ntconio_beep(void)
358 {
359 #if OPT_FLASH
360 if (global_g_val(GMDFLASH)) {
361 flash_display();
362 return;
363 }
364 #endif
365 MessageBeep(0xffffffff);
366 }
367
368 /*
369 * vile very rarely generates any of the ASCII printing control characters
370 * except for a few hand coded routines but we have to support them anyway.
371 */
372
373 /* put a character at the current position in the current colors */
374 static void
ntconio_putc(int ch)375 ntconio_putc(int ch)
376 {
377 #define maybe_flush() \
378 if (bufpos >= ((int) sizeof(linebuf) - 2)) \
379 scflush()
380
381 if (ch >= ' ') {
382
383 /* This is an optimization for the most common case. */
384 maybe_flush();
385 linebuf[bufpos++] = (W32_CHAR) ch;
386
387 } else {
388
389 switch (ch) {
390
391 case '\b':
392 scflush();
393 if (ccol)
394 ccol--;
395 break;
396
397 case '\a':
398 ntconio_beep();
399 break;
400
401 case '\t':
402 do {
403 maybe_flush();
404 linebuf[bufpos++] = ' ';
405 } while ((ccol + bufpos) % 8 != 0);
406 break;
407
408 case '\r':
409 scflush();
410 ccol = 0;
411 break;
412
413 case '\n':
414 scflush();
415 if (crow < csbi.dwMaximumWindowSize.Y - 1) {
416 crow++;
417 } else {
418 ntconio_scroll(1, 0, csbi.dwMaximumWindowSize.Y - 1);
419 }
420 break;
421
422 default:
423 linebuf[bufpos++] = (W32_CHAR) ch;
424 break;
425 }
426 }
427 maybe_flush();
428 }
429
430 static void
ntconio_eeop(void)431 ntconio_eeop(void)
432 {
433 DWORD cnt;
434 COORD coordCursor;
435
436 scflush();
437 coordCursor.X = (SHORT) ccol;
438 coordCursor.Y = (SHORT) crow;
439 cnt = csbi.dwMaximumWindowSize.X - ccol
440 + (csbi.dwMaximumWindowSize.Y - crow - 1)
441 * csbi.dwMaximumWindowSize.X;
442 TRACE2(("ntconio_eeop [%d,%d] erase %d with %04x\n", crow, ccol, cnt, currentAttribute));
443 erase_at(coordCursor, cnt);
444 }
445
446 static void
ntconio_rev(UINT attr)447 ntconio_rev(UINT attr)
448 { /* change video state */
449 scflush();
450 cattr = attr;
451 cbcolor = nbcolor;
452 cfcolor = nfcolor;
453 rvcolor = (global_g_val(GVAL_VIDEO) & VAREV) ? 1 : 0;
454 attr &= (VASPCOL | VACOLOR | VABOLD | VAITAL | VASEL | VAREV);
455
456 TRACE2(("ntconio_rev(%04x) f=%d, b=%d\n", attr, cfcolor, cbcolor));
457
458 if (attr) {
459 if (attr & VASPCOL)
460 cfcolor = (VCOLORNUM(attr) & (NCOLORS - 1));
461 else if (attr & VACOLOR)
462 cfcolor = ((VCOLORNUM(attr)) & (NCOLORS - 1));
463
464 if (cfcolor == ENUM_UNKNOWN)
465 cfcolor = DFT_FCOLOR;
466 if (cbcolor == ENUM_UNKNOWN)
467 cbcolor = DFT_BCOLOR;
468
469 if (attr == VABOLD) {
470 cfcolor |= FOREGROUND_INTENSITY;
471 }
472 if (attr == VAITAL) {
473 cbcolor |= BACKGROUND_INTENSITY;
474 }
475
476 if (attr & (VASEL | VAREV)) { /* reverse video? */
477 rvcolor ^= 1;
478 }
479
480 TRACE2(("...ntconio_rev(%04x) f=%d, b=%d\n", attr, cfcolor, cbcolor));
481 }
482 set_current_attr();
483 }
484
485 static int
ntconio_cres(const char * res)486 ntconio_cres(const char *res)
487 { /* change screen resolution */
488 (void) res;
489 scflush();
490 return 0;
491 }
492
493 static BOOL WINAPI
nthandler(DWORD ctrl_type)494 nthandler(DWORD ctrl_type)
495 {
496 switch (ctrl_type) {
497 case CTRL_CLOSE_EVENT:
498 case CTRL_LOGOFF_EVENT:
499 case CTRL_SHUTDOWN_EVENT:
500 imdying(1);
501 break;
502 }
503 return TRUE;
504 }
505
506 static void
ntconio_set_encoding(ENC_CHOICES code)507 ntconio_set_encoding(ENC_CHOICES code)
508 {
509 my_encoding = code;
510 }
511
512 static ENC_CHOICES
ntconio_get_encoding(void)513 ntconio_get_encoding(void)
514 {
515 return my_encoding;
516 }
517
518 static void
ntconio_open(void)519 ntconio_open(void)
520 {
521 CONSOLE_CURSOR_INFO newcci;
522 BOOL newcci_ok;
523
524 TRACE((T_CALLED "ntconio_open\n"));
525
526 conemu = getenv("ConEmuPID") != NULL;
527 set_colors(NCOLORS);
528 set_palette(initpalettestr);
529
530 hOldConsoleOutput = 0;
531
532 hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
533 TRACE(("hConsoleOutput %p\n", hConsoleOutput));
534
535 origcci_ok = GetConsoleCursorInfo(hConsoleOutput, &origcci);
536
537 GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
538 TRACE(("GetConsoleScreenBufferInfo X:%d Y:%d Top:%d Bottom:%d Left:%d Right:%d\n",
539 csbi.dwMaximumWindowSize.X,
540 csbi.dwMaximumWindowSize.Y,
541 csbi.srWindow.Top,
542 csbi.srWindow.Bottom,
543 csbi.srWindow.Left,
544 csbi.srWindow.Right));
545
546 TRACE(("...compare height %d vs %d\n",
547 csbi.dwMaximumWindowSize.Y,
548 csbi.srWindow.Bottom - csbi.srWindow.Top + 1));
549 TRACE(("...compare width %d vs %d\n",
550 csbi.dwMaximumWindowSize.X,
551 csbi.srWindow.Right - csbi.srWindow.Left + 1));
552
553 if (csbi.dwMaximumWindowSize.Y !=
554 csbi.srWindow.Bottom - csbi.srWindow.Top + 1
555 || csbi.dwMaximumWindowSize.X !=
556 csbi.srWindow.Right - csbi.srWindow.Left + 1) {
557
558 TRACE(("..creating alternate screen buffer\n"));
559 hOldConsoleOutput = hConsoleOutput;
560 hConsoleOutput = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
561 0, NULL,
562 CONSOLE_TEXTMODE_BUFFER, NULL);
563 SetConsoleActiveScreenBuffer(hConsoleOutput);
564
565 GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
566 TRACE(("GetConsoleScreenBufferInfo X:%d Y:%d Top:%d Bottom:%d Left:%d Right:%d\n",
567 csbi.dwMaximumWindowSize.X,
568 csbi.dwMaximumWindowSize.Y,
569 csbi.srWindow.Top,
570 csbi.srWindow.Bottom,
571 csbi.srWindow.Left,
572 csbi.srWindow.Right));
573
574 newcci_ok = GetConsoleCursorInfo(hConsoleOutput, &newcci);
575 if (newcci_ok && origcci_ok && newcci.dwSize != origcci.dwSize) {
576 /*
577 * Ensure that user's cursor size prefs are carried forward
578 * in the newly created console.
579 */
580 show_cursor(TRUE, origcci.dwSize);
581 }
582 }
583
584 originalAttribute = csbi.wAttributes;
585
586 crow = csbi.dwCursorPosition.Y;
587 ccol = csbi.dwCursorPosition.X;
588
589 nfcolor = cfcolor = gfcolor;
590 nbcolor = cbcolor = gbcolor;
591 set_current_attr();
592
593 newscreensize(csbi.dwMaximumWindowSize.Y, csbi.dwMaximumWindowSize.X);
594
595 hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
596 TRACE(("hConsoleInput %p\n", hConsoleInput));
597
598 SetConsoleCtrlHandler(nthandler, TRUE);
599 ntconio_set_encoding(enc_DEFAULT);
600 returnVoid();
601 }
602
603 static void
ntconio_close(void)604 ntconio_close(void)
605 {
606 TRACE((T_CALLED "ntconio_close\n"));
607 if (chgd_cursor) {
608 /* restore cursor */
609 show_cursor(TRUE, origcci.dwSize);
610 }
611 scflush();
612 ntconio_move(term.rows - 1, 0);
613 currentAttribute = originalAttribute;
614 ntconio_eeol();
615 ntconio_flush();
616 set_current_attr();
617
618 SetConsoleTextAttribute(hConsoleOutput, originalAttribute);
619 if (hOldConsoleOutput) {
620 TRACE(("...restoring screen buffer\n"));
621 SetConsoleActiveScreenBuffer(hOldConsoleOutput);
622 CloseHandle(hConsoleOutput);
623 }
624 SetConsoleCtrlHandler(nthandler, FALSE);
625 SetConsoleMode(hConsoleInput, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
626 keyboard_open = FALSE;
627 returnVoid();
628 }
629
630 static void
ntconio_kopen(void)631 ntconio_kopen(void)
632 { /* open the keyboard */
633 TRACE((T_CALLED "ntconio_kopen (open:%d, was-closed:%d)\n",
634 keyboard_open, keyboard_was_closed));
635 if (!keyboard_open) {
636 if (hConsoleOutput) {
637 SetConsoleActiveScreenBuffer(hConsoleOutput);
638 }
639 keyboard_open = TRUE;
640 #ifdef DONT_USE_ON_WIN95
641 SetConsoleCtrlHandler(NULL, TRUE);
642 #endif
643 SetConsoleMode(hConsoleInput, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
644 }
645 returnVoid();
646 }
647
648 static void
ntconio_kclose(void)649 ntconio_kclose(void)
650 { /* close the keyboard */
651 TRACE((T_CALLED "ntconio_kclose\n"));
652 if (keyboard_open) {
653 keyboard_open = FALSE;
654 keyboard_was_closed = TRUE;
655 if (hOldConsoleOutput) {
656 SetConsoleActiveScreenBuffer(hOldConsoleOutput);
657 }
658 #ifdef DONT_USE_ON_WIN95
659 SetConsoleCtrlHandler(NULL, FALSE);
660 #endif
661 }
662 returnVoid();
663 }
664
665 #define isModified(state) (state & \
666 (LEFT_CTRL_PRESSED \
667 | RIGHT_CTRL_PRESSED \
668 | LEFT_ALT_PRESSED \
669 | RIGHT_ALT_PRESSED \
670 | SHIFT_PRESSED))
671
672 static int
modified_key(int key,DWORD state)673 modified_key(int key, DWORD state)
674 {
675 key |= mod_KEY;
676 if (state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
677 key |= mod_CTRL;
678 if (state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
679 key |= mod_ALT;
680 if (state & SHIFT_PRESSED)
681 key |= mod_SHIFT;
682
683 return key;
684 }
685
686 static struct {
687 int windows;
688 int vile;
689 } keyxlate[] = {
690 /* *INDENT-OFF* */
691 { VK_NEXT, KEY_Next },
692 { VK_PRIOR, KEY_Prior },
693 { VK_END, KEY_End },
694 { VK_HOME, KEY_Home },
695 { VK_LEFT, KEY_Left },
696 { VK_RIGHT, KEY_Right },
697 { VK_UP, KEY_Up },
698 { VK_DOWN, KEY_Down },
699 { VK_INSERT, KEY_Insert },
700 { VK_DELETE, KEY_Delete },
701 { VK_HELP, KEY_Help },
702 { VK_SELECT, KEY_Select },
703 #if 0
704 /* Merely pressing the Alt key generates a VK_MENU key event. */
705 { VK_MENU, KEY_Menu },
706 #endif
707 { VK_F1, KEY_F1 },
708 { VK_F2, KEY_F2 },
709 { VK_F3, KEY_F3 },
710 { VK_F4, KEY_F4 },
711 { VK_F5, KEY_F5 },
712 { VK_F6, KEY_F6 },
713 { VK_F7, KEY_F7 },
714 { VK_F8, KEY_F8 },
715 { VK_F9, KEY_F9 },
716 { VK_F10, KEY_F10 },
717 { VK_F11, KEY_F11 },
718 { VK_F12, KEY_F12 },
719 { VK_F13, KEY_F13 },
720 { VK_F14, KEY_F14 },
721 { VK_F15, KEY_F15 },
722 { VK_F16, KEY_F16 },
723 { VK_F17, KEY_F17 },
724 { VK_F18, KEY_F18 },
725 { VK_F19, KEY_F19 },
726 { VK_F20, KEY_F20 },
727 { VK_F21, KEY_F21 },
728 { VK_F22, KEY_F22 },
729 { VK_F23, KEY_F23 },
730 { VK_F24, KEY_F24 },
731 /* winuser.h stops with VK_F24 */
732 /* Allow ^-6 to invoke the alternate-buffer command, a la Unix. */
733 { '6', '6' },
734 /* Support recognition of ^@ */
735 { '2', '2' },
736 /* *INDENT-ON* */
737
738 };
739
740 static int savedChar;
741 static int saveCount = 0;
742
743 static int
decode_key_event(INPUT_RECORD * irp)744 decode_key_event(INPUT_RECORD * irp)
745 {
746 DWORD state = irp->Event.KeyEvent.dwControlKeyState;
747 int key;
748 int i;
749
750 if (!irp->Event.KeyEvent.bKeyDown)
751 return (NOKYMAP);
752
753 TRACE(("decode_key_event(%c=%02x, Virtual=%#x,%#x, State=%#lx)\n",
754 irp->Event.KeyEvent.uChar.WHICH_CHAR,
755 irp->Event.KeyEvent.uChar.WHICH_CHAR,
756 irp->Event.KeyEvent.wVirtualKeyCode,
757 irp->Event.KeyEvent.wVirtualScanCode,
758 irp->Event.KeyEvent.dwControlKeyState));
759
760 if ((key = irp->Event.KeyEvent.uChar.WHICH_CHAR) != 0) {
761 if (isCntrl(key)) {
762 DWORD cstate = state & ~(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED);
763 if (isModified(cstate))
764 key = modified_key(key, cstate);
765 }
766 return key;
767 }
768
769 key = irp->Event.KeyEvent.wVirtualKeyCode;
770 if ((state & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0
771 && (state & (RIGHT_CTRL_PRESSED
772 | LEFT_CTRL_PRESSED
773 | SHIFT_PRESSED)) == state) {
774 /*
775 * Control-shift-6 is control/^, control/~ or control/`.
776 */
777 if (key == '6'
778 || key == '^'
779 || key == '\036') {
780 TRACE(("...decode_key_event ^^\n"));
781 return '\036';
782 }
783 }
784
785 key = NOKYMAP;
786 for (i = 0; i < (int) TABLESIZE(keyxlate); i++) {
787 if (keyxlate[i].windows == irp->Event.KeyEvent.wVirtualKeyCode) {
788
789 /*
790 * Add the modifiers that we recognize. Specifically, we don't
791 * care about ENHANCED_KEY, since we already have portable
792 * pageup/pagedown and arrow key bindings that would be lost if we
793 * used the Win32-only definition.
794 */
795 if (isModified(state)) {
796 key = modified_key(keyxlate[i].vile, state);
797 if (keyxlate[i].vile == '2') {
798 if ((key & mod_CTRL) && ((key & mod_ALT) == 0)) {
799 /* either ^2 or ^@, => nul char */
800
801 key = 0;
802 } else if ((key & mod_SHIFT) &&
803 ((key & (mod_ALT | mod_CTRL)) == 0)) {
804 /*
805 * this will be mapped to '@' if we let Windows do
806 * the translation
807 */
808
809 key = NOKYMAP;
810 }
811 }
812 } else
813 key = keyxlate[i].vile;
814 TRACE(("... %#x -> %#x\n", irp->Event.KeyEvent.wVirtualKeyCode, key));
815 break;
816 }
817 }
818
819 return key;
820 }
821
822 static int
MouseClickSetPos(COORD * result,int * onmode)823 MouseClickSetPos(COORD * result, int *onmode)
824 {
825 WINDOW *wp;
826
827 TRACE(("GETC:setcursor(%d, %d)\n", result->Y, result->X));
828
829 /*
830 * If we're getting a button-down in a window, allow it to begin a
831 * selection. A button-down on its modeline will allow resizing the
832 * window.
833 */
834 *onmode = FALSE;
835 if ((wp = row2window(result->Y)) != 0) {
836 if (result->Y == mode_row(wp)) {
837 *onmode = TRUE;
838 return TRUE;
839 }
840 return setcursor(result->Y, result->X);
841 }
842 return FALSE;
843 }
844
845 /*
846 * Shrink a window by dragging the modeline
847 */
848 static int
adjust_window(WINDOW * wp,COORD * current,COORD * latest)849 adjust_window(WINDOW *wp, COORD * current, COORD * latest)
850 {
851 if (latest->Y == mode_row(wp)) {
852 if (current->Y != latest->Y) {
853 WINDOW *save_wp = curwp;
854 set_curwp(wp);
855 shrinkwind(FALSE, latest->Y - current->Y);
856 set_curwp(save_wp);
857 update(TRUE);
858 }
859 *latest = *current;
860 return TRUE;
861 }
862 return FALSE;
863 }
864
865 /*
866 * Return the current mouse position scaled in character cells, with the
867 * understanding that the cursor might not be in the client rect (due to a
868 * captured mouse). The right way to do this is to call GetCursorPos() and
869 * convert that info to a character cell location by performing some simple
870 * computations based on the current font geometry, as is done in ntwinio.c .
871 * However, I've not found a way to obtain the font used in a command
872 * prompt's (aka DOS box's) client area (yes, I did try GetTextMetrics()).
873 * Without font info, other mechanisms are required.
874 *
875 * Note: GetMousePos() only returns accurate Y coordinate info, because
876 * that's all the info AutoScroll needs to make its decision.
877 */
878 static void
GetMousePos(POINT * result)879 GetMousePos(POINT * result)
880 {
881 HWND hwnd;
882
883 hwnd = GetVileWindow();
884 GetCursorPos(result);
885 ScreenToClient(hwnd, result);
886 if (result->y < client_rect.top) {
887 /*
888 * mouse is above the editor's client area, return a suitable
889 * character cell location that properly triggers autoscroll
890 */
891
892 result->y = -1;
893 } else if (result->y > client_rect.bottom) {
894 /*
895 * mouse is below the editor's client area, return a suitable
896 * character cell location that properly triggers autoscroll
897 */
898
899 result->y = term.rows;
900 } else if (result->x < client_rect.left || result->x > client_rect.right) {
901 /*
902 * dragged mouse out of client rectangle on either the left or
903 * right side. don't know where the cursor really is, but it's
904 * easy to forestall autoscroll by returning a legit mouse
905 * coordinate within the current editor window.
906 */
907
908 result->y = mouse_wp->w_toprow;
909 } else {
910 /* cursor within client area. */
911
912 result->y /= row_height;
913 }
914 }
915
916 // Get current mouse position. If the mouse is above/below the current
917 // window then scroll the window down/up proportionally to the time the LMB
918 // is held down. This function is called from autoscroll_thread() when the
919 // mouse is captured and a wipe selection is active.
920 static void
AutoScroll(WINDOW * wp)921 AutoScroll(WINDOW *wp)
922 {
923 #define DVSR 10
924 #define INCR 6
925 #define TRIGGER (DVSR + INCR)
926
927 POINT current;
928 int Scroll = 0;
929 static int ScrollCount = 0, Throttle = INCR;
930
931 GetMousePos(¤t);
932
933 if (wp == 0)
934 return;
935
936 // Determine if we are above or below the window,
937 // and if so, how far...
938 if (current.y < wp->w_toprow) {
939 // Above the window
940 // Scroll = wp->w_toprow - current.y;
941 Scroll = 1;
942 }
943 if (current.y > mode_row(wp)) {
944 // Below
945 // Scroll = current.y - mode_row(wp);
946 // Scroll *= -1;
947 Scroll = -1;
948 }
949 if (Scroll) {
950 int row;
951 if (Scroll > 0) {
952 row = wp->w_toprow;
953 } else {
954 row = mode_row(wp) - 1;
955 }
956
957 // Scroll the pre-determined amount, ensuring at least one line of
958 // window movement per timer tick. Note also that ScrollScale is
959 // signed, so it will be negative if we want to scroll down.
960 mvupwind(TRUE, Scroll * max(ScrollCount, TRIGGER) / (Throttle + DVSR));
961
962 // Set the cursor. Column doesn't really matter, it will
963 // get updated as soon as we get back into the window...
964 if (setcursor(row, 0)) {
965 sel_extend(TRUE, TRUE);
966 }
967 (void) update(TRUE);
968 ScrollCount++;
969 if (ScrollCount > TRIGGER && Throttle > 0 && ScrollCount % INCR == 0)
970 Throttle--;
971 } else {
972 // Reset counters
973 Throttle = INCR;
974 ScrollCount = 0;
975 }
976 #undef DVSR
977 #undef INCR
978 #undef TRIGGER
979 }
980
981 static void
halt_autoscroll_thread(void)982 halt_autoscroll_thread(void)
983 {
984 buttondown = FALSE;
985 (void) ReleaseCapture(); /* Release captured mouse */
986
987 /* Wait for autoscroll thread to exit screen processing */
988 (void) WaitForSingleObject(hAsMutex, AS_TMOUT);
989 (void) CloseHandle(hAsMutex);
990 }
991
992 static void
autoscroll_thread(void * unused)993 autoscroll_thread(void *unused)
994 {
995 DWORD status;
996
997 (void) unused;
998 for_ever {
999 status = WaitForSingleObject(hAsMutex, AS_TMOUT);
1000 if (!buttondown) {
1001 (void) ReleaseMutex(hAsMutex);
1002 break; /* button no longer held down, die */
1003 }
1004 if (status == WAIT_ABANDONED) {
1005 /* main thread closed thread handle or ??? */
1006
1007 (void) ReleaseMutex(hAsMutex);
1008 break;
1009 }
1010 if (status == WAIT_OBJECT_0) {
1011 /* thread got mutex and "owns" the display. */
1012
1013 AutoScroll(mouse_wp);
1014 }
1015 (void) ReleaseMutex(hAsMutex);
1016 Sleep(25); /* Don't hog the processor */
1017 }
1018 }
1019
1020 /*
1021 * FUNCTION
1022 * mousemove(int *sel_pending,
1023 * POINT *first,
1024 * POINT *current,
1025 * MARK *lmbdn_mark,
1026 * int rect_rgn)
1027 *
1028 * sel_pending - Boolean, T -> client has recorded a left mouse button (LMB)
1029 * click, and so, a selection is pending.
1030 *
1031 * first - editor row/col coordinates where LMB was initially recorded.
1032 *
1033 * latest - during the mouse move, assuming the LMB is still down,
1034 * "latest" tracks the cursor position wrt window resizing
1035 * operations (via a modeline drag).
1036 *
1037 * current - current cursor row/col coordinates.
1038 *
1039 * lmbdn_mark - editor MARK when "LMB down" was initially recorded.
1040 *
1041 * rect_rgn - Boolean, T -> user wants rectangular region selection.
1042 *
1043 * DESCRIPTION
1044 * Using several state variables, this function handles all the semantics
1045 * of a left mouse button "MOVE" event. The semantics are as follows:
1046 *
1047 * 1) This function will not be called unless the LMB is down and the
1048 * cursor is not being used to drage the mode line (enforced by caller).
1049 * 2) a LMB move within the current editor window selects a region of text.
1050 * Later, when the user releases the LMB, that text is yanked to the
1051 * unnamed register (the yank code is not handled in this function).
1052 *
1053 * RETURNS
1054 * None
1055 */
1056 static void
mousemove(int * sel_pending,COORD * first,COORD * current,MARK * lmbdn_mark,int rect_rgn)1057 mousemove(int *sel_pending,
1058 COORD * first,
1059 COORD * current,
1060 MARK *lmbdn_mark,
1061 int rect_rgn)
1062 {
1063 int dummy;
1064
1065 if (WaitForSingleObject(hAsMutex, AS_TMOUT) == WAIT_OBJECT_0) {
1066 if (*sel_pending) {
1067 /*
1068 * Selection pending. If the mouse has moved at least one char,
1069 * start a selection.
1070 */
1071
1072 if (MouseClickSetPos(current, &dummy)) {
1073 /* ignore mouse jitter */
1074
1075 if (current->X != first->X || current->Y != first->Y) {
1076 *sel_pending = FALSE;
1077 DOT = *lmbdn_mark;
1078 (void) sel_begin();
1079 (void) update(TRUE);
1080 } else {
1081 (void) ReleaseMutex(hAsMutex);
1082 return;
1083 }
1084 }
1085 }
1086 if (mouse_wp != row2window(current->Y)) {
1087 /*
1088 * mouse moved into a different editor window or row2window()
1089 * returned a NULL ptr.
1090 */
1091
1092 (void) ReleaseMutex(hAsMutex);
1093 return;
1094 }
1095 if (!setcursor(current->Y, current->X)) {
1096 (void) ReleaseMutex(hAsMutex);
1097 return;
1098 }
1099 if (rect_rgn)
1100 (void) sel_setshape(rgn_RECTANGLE);
1101 if (sel_extend(TRUE, TRUE))
1102 (void) update(TRUE);
1103 (void) ReleaseMutex(hAsMutex);
1104 }
1105 /*
1106 * Else either the worker thread abandoned the mutex (not possible as
1107 * currently coded) or timed out. If the latter, something is
1108 * hung--don't do anything.
1109 */
1110 }
1111
1112 static void
handle_mouse_event(MOUSE_EVENT_RECORD mer)1113 handle_mouse_event(MOUSE_EVENT_RECORD mer)
1114 {
1115 static DWORD lastclick = 0;
1116 static int clicks = 0;
1117
1118 int onmode = FALSE;
1119 COORD current, first, latest;
1120 MARK lmbdn_mark; /* left mouse button down here */
1121 int sel_pending = 0, state;
1122 DWORD thisclick;
1123 UINT clicktime = GetDoubleClickTime();
1124
1125 memset(&first, 0, sizeof(first));
1126 memset(&latest, 0, sizeof(latest));
1127 memset(¤t, 0, sizeof(current));
1128 memset(&lmbdn_mark, 0, sizeof(lmbdn_mark));
1129 buttondown = FALSE;
1130 for_ever {
1131 current = mer.dwMousePosition;
1132 switch (mer.dwEventFlags) {
1133 case 0:
1134 state = mer.dwButtonState;
1135 if (state == 0) { /* button released */
1136 thisclick = GetTickCount();
1137 TRACE(("CLICK %ld/%ld\n", lastclick, thisclick));
1138 if (thisclick - lastclick < clicktime) {
1139 clicks++;
1140 TRACE(("MOUSE CLICKS %d\n", clicks));
1141 } else {
1142 clicks = 0;
1143 }
1144 lastclick = thisclick;
1145
1146 switch (clicks) {
1147 case 1:
1148 on_double_click();
1149 break;
1150 case 2:
1151 on_triple_click();
1152 break;
1153 }
1154
1155 if (buttondown) {
1156 int dummy;
1157
1158 halt_autoscroll_thread();
1159
1160 /* Finalize cursor position. */
1161 (void) MouseClickSetPos(¤t, &dummy);
1162 if (!(onmode || sel_pending))
1163 sel_yank(0);
1164 }
1165 return;
1166 }
1167 if (state & FROM_LEFT_1ST_BUTTON_PRESSED) {
1168 if (MouseClickSetPos(¤t, &onmode)) {
1169 first = latest = current;
1170 lmbdn_mark = DOT;
1171 sel_pending = FALSE;
1172 mouse_wp = row2window(latest.Y);
1173 if (onmode) {
1174 buttondown = TRUE;
1175 sel_release();
1176 update(TRUE);
1177 } else {
1178 HWND hwnd;
1179
1180 (void) update(TRUE); /* possible wdw change */
1181 buttondown = FALSE; /* until all inits are successful */
1182
1183 /* Capture mouse to console vile's window handle. */
1184 hwnd = GetVileWindow();
1185 (void) SetCapture(hwnd);
1186
1187 /* Compute pixel height of each row on screen. */
1188 (void) GetClientRect(hwnd, &client_rect);
1189 row_height = client_rect.bottom / term.rows;
1190
1191 /*
1192 * Create mutex to ensure that main thread and worker
1193 * thread don't update display at the same time.
1194 */
1195 if ((hAsMutex = CreateMutex(0, FALSE, 0)) == NULL)
1196 mlforce("[Can't create autoscroll mutex]");
1197 else {
1198 /*
1199 * Setup a worker thread to act as a pseudo
1200 * timer that kicks off autoscroll when
1201 * necessary.
1202 */
1203
1204 if (_beginthread(autoscroll_thread,
1205 0,
1206 NULL) == (unsigned long) -1) {
1207 (void) CloseHandle(hAsMutex);
1208 mlforce("[Can't create autoscroll thread]");
1209 } else
1210 sel_pending = buttondown = TRUE;
1211 }
1212 if (!buttondown)
1213 (void) ReleaseCapture();
1214 }
1215 }
1216 } else if (state & FROM_LEFT_2ND_BUTTON_PRESSED) {
1217 if (MouseClickSetPos(¤t, &onmode)
1218 && !onmode) {
1219 sel_yank(0);
1220 sel_release();
1221 paste_selection();
1222 (void) update(TRUE);
1223 }
1224 return;
1225 } else {
1226 if (MouseClickSetPos(¤t, &onmode)
1227 && onmode) {
1228 sel_release();
1229 update(TRUE);
1230 } else {
1231 kbd_alarm();
1232 }
1233 }
1234 break;
1235
1236 case MOUSE_MOVED:
1237 if (!buttondown)
1238 return;
1239 if (onmode) {
1240 /* on mode line, resize window (if possible). */
1241
1242 if (!adjust_window(mouse_wp, ¤t, &latest)) {
1243 /*
1244 * left mouse button still down, but cursor moved off mode
1245 * line. Update latest to keep track of cursor in case
1246 * it wanders back on the mode line.
1247 */
1248
1249 latest = current;
1250 }
1251 } else {
1252 mousemove(&sel_pending,
1253 &first,
1254 ¤t,
1255 &lmbdn_mark,
1256 (mer.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
1257 );
1258 }
1259 break;
1260
1261 #ifdef MOUSE_WHEELED
1262 case MOUSE_WHEELED:
1263 /*
1264 * Trial and error experimentation shows that dwButtonState
1265 * has its high bit set when the wheel moves back and not
1266 * set otherwise.
1267 */
1268 mvupwind(TRUE, ((long) mer.dwButtonState < 0) ? -3 : 3);
1269 update(TRUE);
1270 return;
1271 #endif /* MOUSE_WHEELED */
1272 }
1273
1274 for_ever {
1275 INPUT_RECORD ir;
1276 DWORD nr;
1277 int key;
1278
1279 if (!ReadConsoleInput(hConsoleInput, &ir, 1, &nr))
1280 imdying(0);
1281 switch (ir.EventType) {
1282 case KEY_EVENT:
1283 key = decode_key_event(&ir);
1284 if (key == ESC) {
1285 if (buttondown)
1286 halt_autoscroll_thread();
1287 sel_release();
1288 (void) update(TRUE);
1289 return;
1290 }
1291 continue;
1292
1293 case MOUSE_EVENT:
1294 mer = ir.Event.MouseEvent;
1295 break;
1296 }
1297 break;
1298 }
1299 }
1300 }
1301
1302 static int
ntconio_getch(void)1303 ntconio_getch(void)
1304 {
1305 INPUT_RECORD ir;
1306 DWORD nr;
1307 int key;
1308 #ifdef VAL_AUTOCOLOR
1309 int milli_ac, orig_milli_ac;
1310 #endif
1311
1312 TRACE((T_CALLED "ntconio_getch saveCount:%d\n", saveCount));
1313
1314 if (saveCount > 0) {
1315 saveCount--;
1316 returnCode(savedChar);
1317 }
1318 #ifdef VAL_AUTOCOLOR
1319 orig_milli_ac = global_b_val(VAL_AUTOCOLOR);
1320 #endif
1321 for_ever {
1322 #ifdef VAL_AUTOCOLOR
1323 milli_ac = orig_milli_ac;
1324 while (milli_ac > 0) {
1325 if (PeekConsoleInput(hConsoleInput, &ir, 1, &nr) == 0) {
1326 TRACE(("PeekConsoleInput failed\n"));
1327 break; /* ?? system call failed ?? */
1328 }
1329 TRACE(("PeekConsoleInput nr %ld\n", nr));
1330 if (nr > 0)
1331 break; /* something in the queue */
1332 Sleep(20); /* sleep a bit, but be responsive to keybd input */
1333 milli_ac -= 20;
1334 }
1335 if (orig_milli_ac && milli_ac <= 0) {
1336 ac_active = TRUE;
1337 autocolor();
1338 ac_active = FALSE;
1339 }
1340 #endif
1341 if (!ReadConsoleInput(hConsoleInput, &ir, 1, &nr))
1342 imdying(0);
1343 switch (ir.EventType) {
1344
1345 case KEY_EVENT:
1346 key = decode_key_event(&ir);
1347 if (key == NOKYMAP)
1348 continue;
1349 if (ir.Event.KeyEvent.wRepeatCount > 1) {
1350 saveCount = ir.Event.KeyEvent.wRepeatCount - 1;
1351 savedChar = key;
1352 }
1353 returnCode(key);
1354
1355 case WINDOW_BUFFER_SIZE_EVENT:
1356 GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
1357 newscreensize(
1358 ir.Event.WindowBufferSizeEvent.dwSize.Y,
1359 ir.Event.WindowBufferSizeEvent.dwSize.X
1360 );
1361 continue;
1362
1363 case MOUSE_EVENT:
1364 handle_mouse_event(ir.Event.MouseEvent);
1365 continue;
1366
1367 }
1368 }
1369 }
1370
1371 /*
1372 * The function `kbhit' returns true if there are *any* input records
1373 * available. We need to define our own type ahead routine because
1374 * otherwise events which we will discard (like pressing or releasing
1375 * the Shift key) can block screen updates because `ntconio_getch' won't
1376 * return until a ordinary key event occurs.
1377 */
1378
1379 static int
ntconio_typahead(void)1380 ntconio_typahead(void)
1381 {
1382 INPUT_RECORD ir;
1383 DWORD nr;
1384 int key;
1385
1386 if (!keyboard_open)
1387 return 0;
1388 #ifdef VAL_AUTOCOLOR
1389 if (ac_active) {
1390 /*
1391 * Came here during an autocolor operation. Do nothing, in an
1392 * attempt to avoid a keyboard lockup (editor loop) that occurs on
1393 * rare occasions (not reproducible).
1394 */
1395
1396 return (0);
1397 }
1398 #endif
1399 if (saveCount > 0)
1400 return 1;
1401
1402 for_ever {
1403 if (!PeekConsoleInput(hConsoleInput, &ir, 1, &nr))
1404 return 0;
1405
1406 if (nr == 0)
1407 break;
1408
1409 switch (ir.EventType) {
1410
1411 case KEY_EVENT:
1412 key = decode_key_event(&ir);
1413 if (key < 0) {
1414 ReadConsoleInput(hConsoleInput, &ir, 1, &nr);
1415 continue;
1416 }
1417 return 1;
1418
1419 default:
1420 /* Ignore type-ahead for non-keyboard events. */
1421 return 0;
1422 }
1423 }
1424
1425 return 0;
1426 }
1427
1428 void
ntcons_reopen(void)1429 ntcons_reopen(void)
1430 {
1431 /* If we are coming back from a shell command, pick up the current window
1432 * size, since that may have changed due to a 'mode con' command. Run
1433 * this after the 'pressreturn()' call, since otherwise that gets lost
1434 * by side-effects of this code.
1435 */
1436 if (keyboard_was_closed) {
1437 CONSOLE_SCREEN_BUFFER_INFO temp;
1438 keyboard_was_closed = FALSE;
1439 GetConsoleScreenBufferInfo(hConsoleOutput, &temp);
1440 newscreensize(temp.dwMaximumWindowSize.Y, temp.dwMaximumWindowSize.X);
1441 }
1442 }
1443
1444 #if OPT_ICURSOR
1445
1446 /* supported syntax is described in chgd_icursor() */
1447 static int
parse_icursor_string(char * str,int * revert_cursor)1448 parse_icursor_string(char *str, int *revert_cursor)
1449 {
1450 int failed, rc = TRUE, tmp = 0;
1451 char *pinsmode, *pcmdmode;
1452
1453 *revert_cursor = FALSE;
1454 pinsmode = str;
1455 if ((pcmdmode = strchr(pinsmode, ',')) != NULL) {
1456 /* vl_atoul won't like the delimiter... */
1457
1458 tmp = *pcmdmode;
1459 *pcmdmode = '\0';
1460 }
1461 icursor_insmode = (int) vl_atoul(pinsmode, 10, &failed);
1462 if (pcmdmode)
1463 *pcmdmode = (char) tmp; /* delimiter restored */
1464 if (failed)
1465 return (FALSE);
1466 if (pcmdmode) {
1467 icursor_cmdmode = (int) vl_atoul(pcmdmode + 1, 10, &failed);
1468 if (failed)
1469 return (FALSE);
1470 } else {
1471 /* block mode syntax */
1472
1473 icursor_cmdmode = icursor_insmode;
1474 }
1475
1476 /* semantic chks */
1477 if (icursor_insmode == 0) {
1478 if (!pcmdmode)
1479 *revert_cursor = TRUE;
1480 else
1481 rc = FALSE; /* 0% insmode cursor block heights not valid */
1482 return (rc);
1483 }
1484 if (icursor_cmdmode == 0)
1485 rc = FALSE; /* 0% cmdmode cursor block heights not valid */
1486 else {
1487 if (icursor_cmdmode > MAX_CURBLK_HEIGHT ||
1488 icursor_insmode > MAX_CURBLK_HEIGHT) {
1489 rc = FALSE;
1490 }
1491 }
1492 return (rc);
1493 }
1494
1495 /*
1496 * user changed icursor mode
1497 *
1498 * Insertion cursor mode is a string that may be used to either set a fixed
1499 * block cursor height or set the block cursor heights in insertion and
1500 * command mode. Supported syntax:
1501 *
1502 * "<fixed_block_height>"
1503 *
1504 * or
1505 *
1506 * "<insmode_height>,<cmdmode_height>"
1507 *
1508 * The valid range of <fixed_block_cursor_height> is 0-100. Specifying 0
1509 * forces the editor to revert to the cursor height in effect when the
1510 * editor was invoked.
1511 *
1512 * The valid range of <insmode_height> and <cmdmode_height> is 1-100.
1513 */
1514 int
chgd_icursor(BUFFER * bp,VALARGS * args,int glob_vals,int testing)1515 chgd_icursor(BUFFER *bp, VALARGS * args, int glob_vals, int testing)
1516 {
1517 (void) bp;
1518 (void) glob_vals;
1519
1520 if (!testing) {
1521 int revert_cursor;
1522 char *val = args->global->vp->p;
1523
1524 if (!parse_icursor_string(val, &revert_cursor)) {
1525 mlforce("[invalid icursor syntax]");
1526 return (FALSE);
1527 }
1528 if (!revert_cursor) {
1529 chgd_cursor = TRUE;
1530 icursor = (icursor_insmode != icursor_cmdmode);
1531 if (icursor)
1532 term.icursor(insertmode);
1533 else {
1534 /* just set a block cursor */
1535 show_cursor(TRUE, icursor_cmdmode);
1536 }
1537 } else {
1538 /*
1539 * user wants to disable previous changes made to cursor,
1540 * thereby reverting to cursor height in effect when the editor
1541 * was invoked.
1542 */
1543
1544 if (chgd_cursor) {
1545 /*
1546 * NB -- don't reset "chgd_cursor" here. special cleanup
1547 * is required in ntconio_close().
1548 */
1549
1550 if (origcci_ok)
1551 show_cursor(TRUE, origcci.dwSize);
1552 }
1553 }
1554 }
1555 return (TRUE);
1556 }
1557 #endif
1558
1559 /*
1560 * Standard terminal interface dispatch table. None of the fields point into
1561 * "termio" code.
1562 */
1563
1564 TERM term =
1565 {
1566 NROW,
1567 NROW,
1568 NCOL,
1569 NCOL,
1570 ntconio_set_encoding,
1571 ntconio_get_encoding,
1572 ntconio_open,
1573 ntconio_close,
1574 ntconio_kopen,
1575 ntconio_kclose,
1576 nullterm_clean,
1577 nullterm_unclean,
1578 nullterm_openup,
1579 ntconio_getch,
1580 ntconio_putc,
1581 ntconio_typahead,
1582 ntconio_flush,
1583 ntconio_move,
1584 ntconio_eeol,
1585 ntconio_eeop,
1586 ntconio_beep,
1587 ntconio_rev,
1588 ntconio_cres,
1589 #if OPT_COLOR
1590 ntconio_fcol,
1591 ntconio_bcol,
1592 set_ctrans,
1593 #else
1594 nullterm_setfore,
1595 nullterm_setback,
1596 nullterm_setpal,
1597 #endif
1598 nullterm_setccol,
1599 ntconio_scroll,
1600 nullterm_pflush,
1601 #if OPT_ICURSOR
1602 nticursor,
1603 #else
1604 nullterm_icursor,
1605 #endif
1606 #if OPT_TITLE
1607 ntconio_title,
1608 #else
1609 nullterm_settitle,
1610 #endif
1611 nullterm_watchfd,
1612 nullterm_unwatchfd,
1613 nullterm_cursorvis,
1614 nullterm_mopen,
1615 nullterm_mclose,
1616 nullterm_mevent,
1617 };
1618