1 /****************************************************************************
2  * Copyright 2018,2020 Thomas E. Dickey                                     *
3  * Copyright 2008-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 
30 /****************************************************************************
31  *  Author: Juergen Pfeifer                                                 *
32  *     and: Thomas E. Dickey                                                *
33  ****************************************************************************/
34 
35 /*
36  * TODO - GetMousePos(POINT * result) from ntconio.c
37  * TODO - implement nodelay
38  * TODO - improve screen-repainting performance, using implied wraparound to reduce write's
39  * TODO - make it optional whether screen is restored or not when non-buffered
40  */
41 
42 #include <curses.priv.h>
43 
44 #ifdef _WIN32
45 #include <tchar.h>
46 #else
47 #include <windows.h>
48 #include <wchar.h>
49 #endif
50 
51 #include <io.h>
52 
53 #define PSAPI_VERSION 2
54 #include <psapi.h>
55 
56 #define CUR TerminalType(my_term).
57 
58 MODULE_ID("$Id: win_driver.c,v 1.63 2020/02/02 23:34:34 tom Exp $")
59 
60 #ifndef __GNUC__
61 #  error We need GCC to compile for MinGW
62 #endif
63 
64 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
65 
66 #define EXP_OPTIMIZE 0
67 
68 #define array_length(a) (sizeof(a)/sizeof(a[0]))
69 
70 static bool InitConsole(void);
71 static bool okConsoleHandle(TERMINAL_CONTROL_BLOCK *);
72 
73 #define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC))
74 #define SetSP()     assert(TCB->csp != 0); sp = TCB->csp; (void) sp
75 
76 #define GenMap(vKey,key) MAKELONG(key, vKey)
77 
78 #define AdjustY() (CON.buffered ? 0 : (int) CON.SBI.srWindow.Top)
79 
80 #if USE_WIDEC_SUPPORT
81 #define write_screen WriteConsoleOutputW
82 #define read_screen  ReadConsoleOutputW
83 #else
84 #define write_screen WriteConsoleOutput
85 #define read_screen  ReadConsoleOutput
86 #endif
87 
88 static const LONG keylist[] =
89 {
90     GenMap(VK_PRIOR, KEY_PPAGE),
91     GenMap(VK_NEXT, KEY_NPAGE),
92     GenMap(VK_END, KEY_END),
93     GenMap(VK_HOME, KEY_HOME),
94     GenMap(VK_LEFT, KEY_LEFT),
95     GenMap(VK_UP, KEY_UP),
96     GenMap(VK_RIGHT, KEY_RIGHT),
97     GenMap(VK_DOWN, KEY_DOWN),
98     GenMap(VK_DELETE, KEY_DC),
99     GenMap(VK_INSERT, KEY_IC)
100 };
101 static const LONG ansi_keys[] =
102 {
103     GenMap(VK_PRIOR, 'I'),
104     GenMap(VK_NEXT, 'Q'),
105     GenMap(VK_END, 'O'),
106     GenMap(VK_HOME, 'H'),
107     GenMap(VK_LEFT, 'K'),
108     GenMap(VK_UP, 'H'),
109     GenMap(VK_RIGHT, 'M'),
110     GenMap(VK_DOWN, 'P'),
111     GenMap(VK_DELETE, 'S'),
112     GenMap(VK_INSERT, 'R')
113 };
114 #define N_INI ((int)array_length(keylist))
115 #define FKEYS 24
116 #define MAPSIZE (FKEYS + N_INI)
117 #define NUMPAIRS 64
118 
119 /*   A process can only have a single console, so it's safe
120      to maintain all the information about it in a single
121      static structure.
122  */
123 static struct {
124     BOOL initialized;
125     BOOL buffered;
126     BOOL window_only;
127     BOOL progMode;
128     BOOL isMinTTY;
129     BOOL isTermInfoConsole;
130     HANDLE out;
131     HANDLE inp;
132     HANDLE hdl;
133     HANDLE lastOut;
134     int numButtons;
135     DWORD ansi_map[MAPSIZE];
136     DWORD map[MAPSIZE];
137     DWORD rmap[MAPSIZE];
138     WORD pairs[NUMPAIRS];
139     COORD origin;
140     CHAR_INFO *save_screen;
141     COORD save_size;
142     SMALL_RECT save_region;
143     CONSOLE_SCREEN_BUFFER_INFO SBI;
144     CONSOLE_SCREEN_BUFFER_INFO save_SBI;
145     CONSOLE_CURSOR_INFO save_CI;
146 } CON;
147 
148 static BOOL console_initialized = FALSE;
149 
150 static WORD
151 MapColor(bool fore, int color)
152 {
153     static const int _cmap[] =
154     {0, 4, 2, 6, 1, 5, 3, 7};
155     int a;
156     if (color < 0 || color > 7)
157 	a = fore ? 7 : 0;
158     else
159 	a = _cmap[color];
160     if (!fore)
161 	a = a << 4;
162     return (WORD) a;
163 }
164 
165 #define RevAttr(attr) \
166 	       (WORD) (((attr) & 0xff00) | \
167 		      ((((attr) & 0x07) << 4) | \
168 		       (((attr) & 0x70) >> 4)))
169 
170 static WORD
171 MapAttr(WORD res, attr_t ch)
172 {
173     if (ch & A_COLOR) {
174 	int p;
175 
176 	p = PairNumber(ch);
177 	if (p > 0 && p < NUMPAIRS) {
178 	    WORD a;
179 	    a = CON.pairs[p];
180 	    res = (WORD) ((res & 0xff00) | a);
181 	}
182     }
183 
184     if (ch & A_REVERSE) {
185 	res = RevAttr(res);
186     }
187 
188     if (ch & A_STANDOUT) {
189 	res = RevAttr(res) | BACKGROUND_INTENSITY;
190     }
191 
192     if (ch & A_BOLD)
193 	res |= FOREGROUND_INTENSITY;
194 
195     if (ch & A_DIM)
196 	res |= BACKGROUND_INTENSITY;
197 
198     return res;
199 }
200 
201 #if 0				/* def TRACE */
202 static void
203 dump_screen(const char *fn, int ln)
204 {
205     int max_cells = (CON.SBI.dwSize.Y * (1 + CON.SBI.dwSize.X)) + 1;
206     char output[max_cells];
207     CHAR_INFO save_screen[max_cells];
208     COORD save_size;
209     SMALL_RECT save_region;
210     COORD bufferCoord;
211 
212     T(("dump_screen %s@%d", fn, ln));
213 
214     save_region.Top = CON.SBI.srWindow.Top;
215     save_region.Left = CON.SBI.srWindow.Left;
216     save_region.Bottom = CON.SBI.srWindow.Bottom;
217     save_region.Right = CON.SBI.srWindow.Right;
218 
219     save_size.X = (SHORT) (save_region.Right - save_region.Left + 1);
220     save_size.Y = (SHORT) (save_region.Bottom - save_region.Top + 1);
221 
222     bufferCoord.X = bufferCoord.Y = 0;
223 
224     if (read_screen(CON.hdl,
225 		    save_screen,
226 		    save_size,
227 		    bufferCoord,
228 		    &save_region)) {
229 	int i, j;
230 	int ij = 0;
231 	int k = 0;
232 
233 	for (i = save_region.Top; i <= save_region.Bottom; ++i) {
234 	    for (j = save_region.Left; j <= save_region.Right; ++j) {
235 		output[k++] = save_screen[ij++].Char.AsciiChar;
236 	    }
237 	    output[k++] = '\n';
238 	}
239 	output[k] = 0;
240 
241 	T(("DUMP: %d,%d - %d,%d",
242 	   save_region.Top,
243 	   save_region.Left,
244 	   save_region.Bottom,
245 	   save_region.Right));
246 	T(("%s", output));
247     }
248 }
249 
250 #else
251 #define dump_screen(fn,ln)	/* nothing */
252 #endif
253 
254 #if USE_WIDEC_SUPPORT
255 /*
256  * TODO: support surrogate pairs
257  * TODO: support combining characters
258  * TODO: support acsc
259  * TODO: _nc_wacs should be part of sp.
260  */
261 static BOOL
262 con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
263 {
264     int actual = 0;
265     CHAR_INFO ci[limit];
266     COORD loc, siz;
267     SMALL_RECT rec;
268     int i;
269     cchar_t ch;
270     SCREEN *sp;
271 
272     AssertTCB();
273     SetSP();
274 
275     for (i = actual = 0; i < limit; i++) {
276 	ch = str[i];
277 	if (isWidecExt(ch))
278 	    continue;
279 	ci[actual].Char.UnicodeChar = CharOf(ch);
280 	ci[actual].Attributes = MapAttr(CON.SBI.wAttributes,
281 					AttrOf(ch));
282 	if (AttrOf(ch) & A_ALTCHARSET) {
283 	    if (_nc_wacs) {
284 		int which = CharOf(ch);
285 		if (which > 0
286 		    && which < ACS_LEN
287 		    && CharOf(_nc_wacs[which]) != 0) {
288 		    ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
289 		} else {
290 		    ci[actual].Char.UnicodeChar = ' ';
291 		}
292 	    }
293 	}
294 	++actual;
295     }
296 
297     loc.X = (SHORT) 0;
298     loc.Y = (SHORT) 0;
299     siz.X = (SHORT) actual;
300     siz.Y = 1;
301 
302     rec.Left = (SHORT) x;
303     rec.Top = (SHORT) (y + AdjustY());
304     rec.Right = (SHORT) (x + limit - 1);
305     rec.Bottom = rec.Top;
306 
307     return write_screen(CON.hdl, ci, siz, loc, &rec);
308 }
309 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
310 #else
311 static BOOL
312 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
313 {
314     CHAR_INFO ci[n];
315     COORD loc, siz;
316     SMALL_RECT rec;
317     int i;
318     chtype ch;
319     SCREEN *sp;
320 
321     AssertTCB();
322     SetSP();
323 
324     for (i = 0; i < n; i++) {
325 	ch = str[i];
326 	ci[i].Char.AsciiChar = ChCharOf(ch);
327 	ci[i].Attributes = MapAttr(CON.SBI.wAttributes,
328 				   ChAttrOf(ch));
329 	if (ChAttrOf(ch) & A_ALTCHARSET) {
330 	    if (sp->_acs_map)
331 		ci[i].Char.AsciiChar =
332 		ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
333 	}
334     }
335 
336     loc.X = (short) 0;
337     loc.Y = (short) 0;
338     siz.X = (short) n;
339     siz.Y = 1;
340 
341     rec.Left = (short) x;
342     rec.Top = (short) y;
343     rec.Right = (short) (x + n - 1);
344     rec.Bottom = rec.Top;
345 
346     return write_screen(CON.hdl, ci, siz, loc, &rec);
347 }
348 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
349 #endif
350 
351 #if EXP_OPTIMIZE
352 /*
353  * Comparing new/current screens, determine the last column-index for a change
354  * beginning on the given row,col position.  Unlike a serial terminal, there is
355  * no cost for "moving" the "cursor" on the line as we update it.
356  */
357 static int
358 find_end_of_change(SCREEN *sp, int row, int col)
359 {
360     int result = col;
361     struct ldat *curdat = CurScreen(sp)->_line + row;
362     struct ldat *newdat = NewScreen(sp)->_line + row;
363 
364     while (col <= newdat->lastchar) {
365 #if USE_WIDEC_SUPPORT
366 	if (isWidecExt(curdat->text[col]) || isWidecExt(newdat->text[col])) {
367 	    result = col;
368 	} else if (memcmp(&curdat->text[col],
369 			  &newdat->text[col],
370 			  sizeof(curdat->text[0]))) {
371 	    result = col;
372 	} else {
373 	    break;
374 	}
375 #else
376 	if (curdat->text[col] != newdat->text[col]) {
377 	    result = col;
378 	} else {
379 	    break;
380 	}
381 #endif
382 	++col;
383     }
384     return result;
385 }
386 
387 /*
388  * Given a row,col position at the end of a change-chunk, look for the
389  * beginning of the next change-chunk.
390  */
391 static int
392 find_next_change(SCREEN *sp, int row, int col)
393 {
394     struct ldat *curdat = CurScreen(sp)->_line + row;
395     struct ldat *newdat = NewScreen(sp)->_line + row;
396     int result = newdat->lastchar + 1;
397 
398     while (++col <= newdat->lastchar) {
399 #if USE_WIDEC_SUPPORT
400 	if (isWidecExt(curdat->text[col]) != isWidecExt(newdat->text[col])) {
401 	    result = col;
402 	    break;
403 	} else if (memcmp(&curdat->text[col],
404 			  &newdat->text[col],
405 			  sizeof(curdat->text[0]))) {
406 	    result = col;
407 	    break;
408 	}
409 #else
410 	if (curdat->text[col] != newdat->text[col]) {
411 	    result = col;
412 	    break;
413 	}
414 #endif
415     }
416     return result;
417 }
418 
419 #define EndChange(first) \
420 	find_end_of_change(sp, y, first)
421 #define NextChange(last) \
422 	find_next_change(sp, y, last)
423 
424 #endif /* EXP_OPTIMIZE */
425 
426 #define MARK_NOCHANGE(win,row) \
427 		win->_line[row].firstchar = _NOCHANGE; \
428 		win->_line[row].lastchar  = _NOCHANGE
429 
430 static void
431 selectActiveHandle(void)
432 {
433     if (CON.lastOut != CON.hdl) {
434 	CON.lastOut = CON.hdl;
435 	SetConsoleActiveScreenBuffer(CON.lastOut);
436     }
437 }
438 
439 static bool
440 restore_original_screen(void)
441 {
442     COORD bufferCoord;
443     bool result = FALSE;
444     SMALL_RECT save_region = CON.save_region;
445 
446     T(("... restoring %s", CON.window_only ? "window" : "entire buffer"));
447 
448     bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0);
449     bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0);
450 
451     if (write_screen(CON.hdl,
452 		     CON.save_screen,
453 		     CON.save_size,
454 		     bufferCoord,
455 		     &save_region)) {
456 	result = TRUE;
457 	mvcur(-1, -1, LINES - 2, 0);
458 	T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)",
459 	   CON.save_size.Y,
460 	   CON.save_size.X,
461 	   save_region.Top,
462 	   save_region.Left,
463 	   save_region.Bottom,
464 	   save_region.Right));
465     } else {
466 	T(("... restore original screen contents err"));
467     }
468     return result;
469 }
470 
471 static const char *
472 wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
473 {
474     (void) TCB;
475     return "win32console";
476 }
477 
478 static int
479 wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
480 {
481     int result = ERR;
482     int y, nonempty, n, x0, x1, Width, Height;
483     SCREEN *sp;
484 
485     T((T_CALLED("win32con::wcon_doupdate(%p)"), TCB));
486     if (okConsoleHandle(TCB)) {
487 	SetSP();
488 
489 	Width = screen_columns(sp);
490 	Height = screen_lines(sp);
491 	nonempty = min(Height, NewScreen(sp)->_maxy + 1);
492 
493 	T(("... %dx%d clear cur:%d new:%d",
494 	   Height, Width,
495 	   CurScreen(sp)->_clear,
496 	   NewScreen(sp)->_clear));
497 
498 	if (SP_PARM->_endwin == ewSuspend) {
499 
500 	    T(("coming back from shell mode"));
501 	    NCURSES_SP_NAME(reset_prog_mode) (NCURSES_SP_ARG);
502 
503 	    NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG);
504 	    NCURSES_SP_NAME(_nc_screen_resume) (NCURSES_SP_ARG);
505 	    SP_PARM->_mouse_resume(SP_PARM);
506 
507 	    SP_PARM->_endwin = ewRunning;
508 	}
509 
510 	if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
511 	    int x;
512 #if USE_WIDEC_SUPPORT
513 	    cchar_t empty[Width];
514 	    wchar_t blank[2] =
515 	    {
516 		L' ', L'\0'
517 	    };
518 
519 	    for (x = 0; x < Width; x++)
520 		setcchar(&empty[x], blank, 0, 0, 0);
521 #else
522 	    chtype empty[Width];
523 
524 	    for (x = 0; x < Width; x++)
525 		empty[x] = ' ';
526 #endif
527 
528 	    for (y = 0; y < nonempty; y++) {
529 		con_write(TCB, y, 0, empty, Width);
530 		memcpy(empty,
531 		       CurScreen(sp)->_line[y].text,
532 		       (size_t) Width * sizeof(empty[0]));
533 	    }
534 	    CurScreen(sp)->_clear = FALSE;
535 	    NewScreen(sp)->_clear = FALSE;
536 	    touchwin(NewScreen(sp));
537 	    T(("... cleared %dx%d lines @%d of screen", nonempty, Width,
538 	       AdjustY()));
539 	}
540 
541 	for (y = 0; y < nonempty; y++) {
542 	    x0 = NewScreen(sp)->_line[y].firstchar;
543 	    if (x0 != _NOCHANGE) {
544 #if EXP_OPTIMIZE
545 		int x2;
546 		int limit = NewScreen(sp)->_line[y].lastchar;
547 		while ((x1 = EndChange(x0)) <= limit) {
548 		    while ((x2 = NextChange(x1)) <= limit && x2 <= (x1 + 2)) {
549 			x1 = x2;
550 		    }
551 		    n = x1 - x0 + 1;
552 		    memcpy(&CurScreen(sp)->_line[y].text[x0],
553 			   &NewScreen(sp)->_line[y].text[x0],
554 			   n * sizeof(CurScreen(sp)->_line[y].text[x0]));
555 		    con_write(TCB,
556 			      y,
557 			      x0,
558 			      &CurScreen(sp)->_line[y].text[x0], n);
559 		    x0 = NextChange(x1);
560 		}
561 
562 		/* mark line changed successfully */
563 		if (y <= NewScreen(sp)->_maxy) {
564 		    MARK_NOCHANGE(NewScreen(sp), y);
565 		}
566 		if (y <= CurScreen(sp)->_maxy) {
567 		    MARK_NOCHANGE(CurScreen(sp), y);
568 		}
569 #else
570 		x1 = NewScreen(sp)->_line[y].lastchar;
571 		n = x1 - x0 + 1;
572 		if (n > 0) {
573 		    memcpy(&CurScreen(sp)->_line[y].text[x0],
574 			   &NewScreen(sp)->_line[y].text[x0],
575 			   (size_t) n * sizeof(CurScreen(sp)->_line[y].text[x0]));
576 		    con_write(TCB,
577 			      y,
578 			      x0,
579 			      &CurScreen(sp)->_line[y].text[x0], n);
580 
581 		    /* mark line changed successfully */
582 		    if (y <= NewScreen(sp)->_maxy) {
583 			MARK_NOCHANGE(NewScreen(sp), y);
584 		    }
585 		    if (y <= CurScreen(sp)->_maxy) {
586 			MARK_NOCHANGE(CurScreen(sp), y);
587 		    }
588 		}
589 #endif
590 	    }
591 	}
592 
593 	/* put everything back in sync */
594 	for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
595 	    MARK_NOCHANGE(NewScreen(sp), y);
596 	}
597 	for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
598 	    MARK_NOCHANGE(CurScreen(sp), y);
599 	}
600 
601 	if (!NewScreen(sp)->_leaveok) {
602 	    CurScreen(sp)->_curx = NewScreen(sp)->_curx;
603 	    CurScreen(sp)->_cury = NewScreen(sp)->_cury;
604 
605 	    TCB->drv->td_hwcur(TCB,
606 			       0, 0,
607 			       CurScreen(sp)->_cury, CurScreen(sp)->_curx);
608 	}
609 	selectActiveHandle();
610 	result = OK;
611     }
612     returnCode(result);
613 }
614 
615 static bool
616 wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
617 	       const char *tname,
618 	       int *errret GCC_UNUSED)
619 {
620     bool code = FALSE;
621 
622     T((T_CALLED("win32con::wcon_CanHandle(%p)"), TCB));
623 
624     assert((TCB != 0) && (tname != 0));
625 
626     TCB->magic = WINMAGIC;
627 
628     if (tname == 0 || *tname == 0)
629 	code = TRUE;
630     else if (tname != 0 && *tname == '#') {
631 	/*
632 	 * Use "#" (a character which cannot begin a terminal's name) to
633 	 * select specific driver from the table.
634 	 *
635 	 * In principle, we could have more than one non-terminfo driver,
636 	 * e.g., "win32gui".
637 	 */
638 	size_t n = strlen(tname + 1);
639 	if (n != 0
640 	    && ((strncmp(tname + 1, "win32console", n) == 0)
641 		|| (strncmp(tname + 1, "win32con", n) == 0))) {
642 	    code = TRUE;
643 	}
644     } else if (tname != 0 && stricmp(tname, "unknown") == 0) {
645 	code = TRUE;
646     }
647 
648     /*
649      * This is intentional, to avoid unnecessary breakage of applications
650      * using <term.h> symbols.
651      */
652     if (code && (TerminalType(&TCB->term).Booleans == 0)) {
653 	_nc_init_termtype(&TerminalType(&TCB->term));
654 #if NCURSES_EXT_NUMBERS
655 	_nc_export_termtype2(&TCB->term.type, &TerminalType(&TCB->term));
656 #endif
657     }
658 
659     if (!code) {
660 	if (_nc_mingw_isconsole(0))
661 	    CON.isTermInfoConsole = TRUE;
662     }
663     returnBool(code);
664 }
665 
666 static int
667 wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
668 		 int beepFlag)
669 {
670     SCREEN *sp;
671     int res = ERR;
672 
673     int high = (CON.SBI.srWindow.Bottom - CON.SBI.srWindow.Top + 1);
674     int wide = (CON.SBI.srWindow.Right - CON.SBI.srWindow.Left + 1);
675     int max_cells = (high * wide);
676     int i;
677 
678     CHAR_INFO this_screen[max_cells];
679     CHAR_INFO that_screen[max_cells];
680     COORD this_size;
681     SMALL_RECT this_region;
682     COORD bufferCoord;
683 
684     if (okConsoleHandle(TCB)) {
685 	SetSP();
686 	this_region.Top = CON.SBI.srWindow.Top;
687 	this_region.Left = CON.SBI.srWindow.Left;
688 	this_region.Bottom = CON.SBI.srWindow.Bottom;
689 	this_region.Right = CON.SBI.srWindow.Right;
690 
691 	this_size.X = (SHORT) wide;
692 	this_size.Y = (SHORT) high;
693 
694 	bufferCoord.X = this_region.Left;
695 	bufferCoord.Y = this_region.Top;
696 
697 	if (!beepFlag &&
698 	    read_screen(CON.hdl,
699 			this_screen,
700 			this_size,
701 			bufferCoord,
702 			&this_region)) {
703 
704 	    memcpy(that_screen, this_screen, sizeof(that_screen));
705 
706 	    for (i = 0; i < max_cells; i++) {
707 		that_screen[i].Attributes = RevAttr(that_screen[i].Attributes);
708 	    }
709 
710 	    write_screen(CON.hdl, that_screen, this_size, bufferCoord, &this_region);
711 	    Sleep(200);
712 	    write_screen(CON.hdl, this_screen, this_size, bufferCoord, &this_region);
713 
714 	} else {
715 	    MessageBeep(MB_ICONWARNING);	/* MB_OK might be better */
716 	}
717 	res = OK;
718     }
719     return res;
720 }
721 
722 static int
723 wcon_print(TERMINAL_CONTROL_BLOCK * TCB,
724 	   char *data GCC_UNUSED,
725 	   int len GCC_UNUSED)
726 {
727     SCREEN *sp;
728 
729     AssertTCB();
730     SetSP();
731 
732     return ERR;
733 }
734 
735 static int
736 wcon_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
737 		   int fg GCC_UNUSED,
738 		   int bg GCC_UNUSED)
739 {
740     SCREEN *sp;
741     int code = ERR;
742 
743     AssertTCB();
744     SetSP();
745 
746     return (code);
747 }
748 
749 static bool
750 get_SBI(void)
751 {
752     bool rc = FALSE;
753     if (GetConsoleScreenBufferInfo(CON.hdl, &(CON.SBI))) {
754 	T(("GetConsoleScreenBufferInfo"));
755 	T(("... buffer(X:%d Y:%d)",
756 	   CON.SBI.dwSize.X,
757 	   CON.SBI.dwSize.Y));
758 	T(("... window(X:%d Y:%d)",
759 	   CON.SBI.dwMaximumWindowSize.X,
760 	   CON.SBI.dwMaximumWindowSize.Y));
761 	T(("... cursor(X:%d Y:%d)",
762 	   CON.SBI.dwCursorPosition.X,
763 	   CON.SBI.dwCursorPosition.Y));
764 	T(("... display(Top:%d Bottom:%d Left:%d Right:%d)",
765 	   CON.SBI.srWindow.Top,
766 	   CON.SBI.srWindow.Bottom,
767 	   CON.SBI.srWindow.Left,
768 	   CON.SBI.srWindow.Right));
769 	if (CON.buffered) {
770 	    CON.origin.X = 0;
771 	    CON.origin.Y = 0;
772 	} else {
773 	    CON.origin.X = CON.SBI.srWindow.Left;
774 	    CON.origin.Y = CON.SBI.srWindow.Top;
775 	}
776 	rc = TRUE;
777     } else {
778 	T(("GetConsoleScreenBufferInfo ERR"));
779     }
780     return rc;
781 }
782 
783 static void
784 wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
785 	      int fore,
786 	      int color,
787 	      int (*outc) (SCREEN *, int) GCC_UNUSED)
788 {
789     if (okConsoleHandle(TCB)) {
790 	WORD a = MapColor(fore, color);
791 	a |= (WORD) ((CON.SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f));
792 	SetConsoleTextAttribute(CON.hdl, a);
793 	get_SBI();
794     }
795 }
796 
797 static bool
798 wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB)
799 {
800     bool res = FALSE;
801 
802     if (okConsoleHandle(TCB)) {
803 	WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
804 	SetConsoleTextAttribute(CON.hdl, a);
805 	get_SBI();
806 	res = TRUE;
807     }
808     return res;
809 }
810 
811 static bool
812 wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
813 {
814     int result = FALSE;
815     SCREEN *sp;
816 
817     AssertTCB();
818     SetSP();
819 
820     return result;
821 }
822 
823 static int
824 wcon_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
825 {
826     int result = ERR;
827 
828     T((T_CALLED("win32con::wcon_size(%p)"), TCB));
829 
830     if (okConsoleHandle(TCB) &&
831 	Lines != NULL &&
832 	Cols != NULL) {
833 	if (CON.buffered) {
834 	    *Lines = (int) (CON.SBI.dwSize.Y);
835 	    *Cols = (int) (CON.SBI.dwSize.X);
836 	} else {
837 	    *Lines = (int) (CON.SBI.srWindow.Bottom + 1 -
838 			    CON.SBI.srWindow.Top);
839 	    *Cols = (int) (CON.SBI.srWindow.Right + 1 -
840 			   CON.SBI.srWindow.Left);
841 	}
842 	result = OK;
843     }
844     returnCode(result);
845 }
846 
847 static int
848 wcon_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
849 	     int l GCC_UNUSED,
850 	     int c GCC_UNUSED)
851 {
852     AssertTCB();
853     return ERR;
854 }
855 
856 static int
857 wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
858 {
859     DWORD dwFlag = 0;
860     tcflag_t iflag;
861     tcflag_t lflag;
862     int result = ERR;
863 
864     if (buf != NULL && okConsoleHandle(TCB)) {
865 
866 	if (setFlag) {
867 	    iflag = buf->c_iflag;
868 	    lflag = buf->c_lflag;
869 
870 	    GetConsoleMode(CON.inp, &dwFlag);
871 
872 	    if (lflag & ICANON)
873 		dwFlag |= ENABLE_LINE_INPUT;
874 	    else
875 		dwFlag &= (DWORD) (~ENABLE_LINE_INPUT);
876 
877 	    if (lflag & ECHO)
878 		dwFlag |= ENABLE_ECHO_INPUT;
879 	    else
880 		dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT);
881 
882 	    if (iflag & BRKINT)
883 		dwFlag |= ENABLE_PROCESSED_INPUT;
884 	    else
885 		dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT);
886 
887 	    dwFlag |= ENABLE_MOUSE_INPUT;
888 
889 	    buf->c_iflag = iflag;
890 	    buf->c_lflag = lflag;
891 	    SetConsoleMode(CON.inp, dwFlag);
892 	    TCB->term.Nttyb = *buf;
893 	} else {
894 	    iflag = TCB->term.Nttyb.c_iflag;
895 	    lflag = TCB->term.Nttyb.c_lflag;
896 	    GetConsoleMode(CON.inp, &dwFlag);
897 
898 	    if (dwFlag & ENABLE_LINE_INPUT)
899 		lflag |= ICANON;
900 	    else
901 		lflag &= (tcflag_t) (~ICANON);
902 
903 	    if (dwFlag & ENABLE_ECHO_INPUT)
904 		lflag |= ECHO;
905 	    else
906 		lflag &= (tcflag_t) (~ECHO);
907 
908 	    if (dwFlag & ENABLE_PROCESSED_INPUT)
909 		iflag |= BRKINT;
910 	    else
911 		iflag &= (tcflag_t) (~BRKINT);
912 
913 	    TCB->term.Nttyb.c_iflag = iflag;
914 	    TCB->term.Nttyb.c_lflag = lflag;
915 
916 	    *buf = TCB->term.Nttyb;
917 	}
918 	result = OK;
919     }
920     return result;
921 }
922 
923 #define MIN_WIDE 80
924 #define MIN_HIGH 24
925 
926 /*
927  * In "normal" mode, reset the buffer- and window-sizes back to their original values.
928  */
929 static void
930 set_scrollback(bool normal, CONSOLE_SCREEN_BUFFER_INFO * info)
931 {
932     SMALL_RECT rect;
933     COORD coord;
934     bool changed = FALSE;
935 
936     T((T_CALLED("win32con::set_scrollback(%s)"),
937        (normal
938 	? "normal"
939 	: "application")));
940 
941     T(("... SBI.srWindow %d,%d .. %d,%d",
942        info->srWindow.Top,
943        info->srWindow.Left,
944        info->srWindow.Bottom,
945        info->srWindow.Right));
946     T(("... SBI.dwSize %dx%d",
947        info->dwSize.Y,
948        info->dwSize.X));
949 
950     if (normal) {
951 	rect = info->srWindow;
952 	coord = info->dwSize;
953 	if (memcmp(info, &CON.SBI, sizeof(*info)) != 0) {
954 	    changed = TRUE;
955 	    CON.SBI = *info;
956 	}
957     } else {
958 	int high = info->srWindow.Bottom - info->srWindow.Top + 1;
959 	int wide = info->srWindow.Right - info->srWindow.Left + 1;
960 
961 	if (high < MIN_HIGH) {
962 	    T(("... height %d < %d", high, MIN_HIGH));
963 	    high = MIN_HIGH;
964 	    changed = TRUE;
965 	}
966 	if (wide < MIN_WIDE) {
967 	    T(("... width %d < %d", wide, MIN_WIDE));
968 	    wide = MIN_WIDE;
969 	    changed = TRUE;
970 	}
971 
972 	rect.Left =
973 	    rect.Top = 0;
974 	rect.Right = (SHORT) (wide - 1);
975 	rect.Bottom = (SHORT) (high - 1);
976 
977 	coord.X = (SHORT) wide;
978 	coord.Y = (SHORT) high;
979 
980 	if (info->dwSize.Y != high ||
981 	    info->dwSize.X != wide ||
982 	    info->srWindow.Top != 0 ||
983 	    info->srWindow.Left != 0) {
984 	    changed = TRUE;
985 	}
986 
987     }
988 
989     if (changed) {
990 	T(("... coord %d,%d", coord.Y, coord.X));
991 	T(("... rect %d,%d - %d,%d",
992 	   rect.Top, rect.Left,
993 	   rect.Bottom, rect.Right));
994 	SetConsoleScreenBufferSize(CON.hdl, coord);	/* dwSize */
995 	SetConsoleWindowInfo(CON.hdl, TRUE, &rect);	/* srWindow */
996 	get_SBI();
997     }
998     returnVoid;
999 }
1000 
1001 static int
1002 wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
1003 {
1004     SCREEN *sp;
1005     TERMINAL *_term = (TERMINAL *) TCB;
1006     int code = ERR;
1007 
1008     if (okConsoleHandle(TCB)) {
1009 	sp = TCB->csp;
1010 
1011 	T((T_CALLED("win32con::wcon_mode(%p, prog=%d, def=%d)"),
1012 	   TCB, progFlag, defFlag));
1013 
1014 	CON.progMode = progFlag;
1015 	CON.lastOut = progFlag ? CON.hdl : CON.out;
1016 	SetConsoleActiveScreenBuffer(CON.lastOut);
1017 
1018 	if (progFlag) /* prog mode */  {
1019 	    if (defFlag) {
1020 		if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
1021 		    _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS);
1022 		    code = OK;
1023 		}
1024 	    } else {
1025 		/* reset_prog_mode */
1026 		if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
1027 		    if (sp) {
1028 			if (sp->_keypad_on)
1029 			    _nc_keypad(sp, TRUE);
1030 		    }
1031 		    if (!CON.buffered) {
1032 			set_scrollback(FALSE, &CON.SBI);
1033 		    }
1034 		    code = OK;
1035 		}
1036 	    }
1037 	    T(("... buffered:%d, clear:%d", CON.buffered, CurScreen(sp)->_clear));
1038 	} else {		/* shell mode */
1039 	    if (defFlag) {
1040 		/* def_shell_mode */
1041 		if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
1042 		    code = OK;
1043 		}
1044 	    } else {
1045 		/* reset_shell_mode */
1046 		if (sp) {
1047 		    _nc_keypad(sp, FALSE);
1048 		    NCURSES_SP_NAME(_nc_flush) (sp);
1049 		}
1050 		code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb));
1051 		if (!CON.buffered) {
1052 		    set_scrollback(TRUE, &CON.save_SBI);
1053 		    if (!restore_original_screen())
1054 			code = ERR;
1055 		}
1056 		SetConsoleCursorInfo(CON.hdl, &CON.save_CI);
1057 	    }
1058 	}
1059 
1060     }
1061     returnCode(code);
1062 }
1063 
1064 static void
1065 wcon_screen_init(SCREEN *sp GCC_UNUSED)
1066 {
1067 }
1068 
1069 static void
1070 wcon_wrap(SCREEN *sp GCC_UNUSED)
1071 {
1072 }
1073 
1074 static int
1075 rkeycompare(const void *el1, const void *el2)
1076 {
1077     WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
1078     WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
1079 
1080     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
1081 }
1082 
1083 static int
1084 keycompare(const void *el1, const void *el2)
1085 {
1086     WORD key1 = HIWORD((*((const LONG *) el1)));
1087     WORD key2 = HIWORD((*((const LONG *) el2)));
1088 
1089     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
1090 }
1091 
1092 static int
1093 MapKey(WORD vKey)
1094 {
1095     WORD nKey = 0;
1096     void *res;
1097     LONG key = GenMap(vKey, 0);
1098     int code = -1;
1099 
1100     res = bsearch(&key,
1101 		  CON.map,
1102 		  (size_t) (N_INI + FKEYS),
1103 		  sizeof(keylist[0]),
1104 		  keycompare);
1105     if (res) {
1106 	key = *((LONG *) res);
1107 	nKey = LOWORD(key);
1108 	code = (int) (nKey & 0x7fff);
1109 	if (nKey & 0x8000)
1110 	    code = -code;
1111     }
1112     return code;
1113 }
1114 
1115 static int
1116 AnsiKey(WORD vKey)
1117 {
1118     WORD nKey = 0;
1119     void *res;
1120     LONG key = GenMap(vKey, 0);
1121     int code = -1;
1122 
1123     res = bsearch(&key,
1124 		  CON.ansi_map,
1125 		  (size_t) (N_INI + FKEYS),
1126 		  sizeof(keylist[0]),
1127 		  keycompare);
1128     if (res) {
1129 	key = *((LONG *) res);
1130 	nKey = LOWORD(key);
1131 	code = (int) (nKey & 0x7fff);
1132 	if (nKey & 0x8000)
1133 	    code = -code;
1134     }
1135     return code;
1136 }
1137 
1138 static void
1139 wcon_release(TERMINAL_CONTROL_BLOCK * TCB)
1140 {
1141     T((T_CALLED("win32con::wcon_release(%p)"), TCB));
1142 
1143     AssertTCB();
1144     if (TCB->prop)
1145 	free(TCB->prop);
1146 
1147     returnVoid;
1148 }
1149 
1150 static bool
1151 read_screen_data(void)
1152 {
1153     bool result = FALSE;
1154     COORD bufferCoord;
1155     size_t want;
1156 
1157     CON.save_size.X = (SHORT) (CON.save_region.Right
1158 			       - CON.save_region.Left + 1);
1159     CON.save_size.Y = (SHORT) (CON.save_region.Bottom
1160 			       - CON.save_region.Top + 1);
1161 
1162     want = (size_t) (CON.save_size.X * CON.save_size.Y);
1163 
1164     if ((CON.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
1165 	bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0);
1166 	bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0);
1167 
1168 	T(("... reading console %s %dx%d into %d,%d - %d,%d at %d,%d",
1169 	   CON.window_only ? "window" : "buffer",
1170 	   CON.save_size.Y, CON.save_size.X,
1171 	   CON.save_region.Top,
1172 	   CON.save_region.Left,
1173 	   CON.save_region.Bottom,
1174 	   CON.save_region.Right,
1175 	   bufferCoord.Y,
1176 	   bufferCoord.X));
1177 
1178 	if (read_screen(CON.hdl,
1179 			CON.save_screen,
1180 			CON.save_size,
1181 			bufferCoord,
1182 			&CON.save_region)) {
1183 	    result = TRUE;
1184 	} else {
1185 	    T((" error %#lx", (unsigned long) GetLastError()));
1186 	    FreeAndNull(CON.save_screen);
1187 	}
1188     }
1189 
1190     return result;
1191 }
1192 
1193 /*
1194  * Attempt to save the screen contents.  PDCurses does this if
1195  * PDC_RESTORE_SCREEN is set, giving the same visual appearance on
1196  * restoration as if the library had allocated a console buffer.  MSDN
1197  * says that the data which can be read is limited to 64Kb (and may be
1198  * less).
1199  */
1200 static bool
1201 save_original_screen(void)
1202 {
1203     bool result = FALSE;
1204 
1205     CON.save_region.Top = 0;
1206     CON.save_region.Left = 0;
1207     CON.save_region.Bottom = (SHORT) (CON.SBI.dwSize.Y - 1);
1208     CON.save_region.Right = (SHORT) (CON.SBI.dwSize.X - 1);
1209 
1210     if (read_screen_data()) {
1211 	result = TRUE;
1212     } else {
1213 
1214 	CON.save_region.Top = CON.SBI.srWindow.Top;
1215 	CON.save_region.Left = CON.SBI.srWindow.Left;
1216 	CON.save_region.Bottom = CON.SBI.srWindow.Bottom;
1217 	CON.save_region.Right = CON.SBI.srWindow.Right;
1218 
1219 	CON.window_only = TRUE;
1220 
1221 	if (read_screen_data()) {
1222 	    result = TRUE;
1223 	}
1224     }
1225 
1226     T(("... save original screen contents %s", result ? "ok" : "err"));
1227     return result;
1228 }
1229 
1230 static void
1231 wcon_init(TERMINAL_CONTROL_BLOCK * TCB)
1232 {
1233     T((T_CALLED("win32con::wcon_init(%p)"), TCB));
1234 
1235     AssertTCB();
1236 
1237     if (TCB) {
1238 	if (!InitConsole()) {
1239 	    returnVoid;
1240 	}
1241 
1242 	TCB->info.initcolor = TRUE;
1243 	TCB->info.canchange = FALSE;
1244 	TCB->info.hascolor = TRUE;
1245 	TCB->info.caninit = TRUE;
1246 
1247 	TCB->info.maxpairs = NUMPAIRS;
1248 	TCB->info.maxcolors = 8;
1249 	TCB->info.numlabels = 0;
1250 	TCB->info.labelwidth = 0;
1251 	TCB->info.labelheight = 0;
1252 	TCB->info.nocolorvideo = 1;
1253 	TCB->info.tabsize = 8;
1254 
1255 	TCB->info.numbuttons = CON.numButtons;
1256 	TCB->info.defaultPalette = _nc_cga_palette;
1257 
1258     }
1259     returnVoid;
1260 }
1261 
1262 static void
1263 wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB,
1264 	      int pair,
1265 	      int f,
1266 	      int b)
1267 {
1268     SCREEN *sp;
1269 
1270     if (okConsoleHandle(TCB)) {
1271 	SetSP();
1272 
1273 	if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
1274 	    && (b >= 0) && (b < 8)) {
1275 	    CON.pairs[pair] = MapColor(true, f) | MapColor(false, b);
1276 	}
1277     }
1278 }
1279 
1280 static void
1281 wcon_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
1282 	       int color GCC_UNUSED,
1283 	       int r GCC_UNUSED,
1284 	       int g GCC_UNUSED,
1285 	       int b GCC_UNUSED)
1286 {
1287     SCREEN *sp;
1288 
1289     AssertTCB();
1290     SetSP();
1291 }
1292 
1293 static void
1294 wcon_do_color(TERMINAL_CONTROL_BLOCK * TCB,
1295 	      int old_pair GCC_UNUSED,
1296 	      int pair GCC_UNUSED,
1297 	      int reverse GCC_UNUSED,
1298 	      int (*outc) (SCREEN *, int) GCC_UNUSED
1299 )
1300 {
1301     SCREEN *sp;
1302 
1303     AssertTCB();
1304     SetSP();
1305 }
1306 
1307 static void
1308 wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
1309 {
1310     SCREEN *sp;
1311 
1312     if (okConsoleHandle(TCB)) {
1313 	SetSP();
1314 
1315 	sp->_mouse_type = M_TERM_DRIVER;
1316     }
1317 }
1318 
1319 static int
1320 wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB,
1321 	       int delay
1322 	       EVENTLIST_2nd(_nc_eventlist * evl))
1323 {
1324     int rc = 0;
1325     SCREEN *sp;
1326 
1327     if (okConsoleHandle(TCB)) {
1328 	SetSP();
1329 
1330 	if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1331 	    rc = TW_MOUSE;
1332 	} else {
1333 	    rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
1334 					  TWAIT_MASK,
1335 					  delay,
1336 					  (int *) 0
1337 					  EVENTLIST_2nd(evl));
1338 	}
1339     }
1340 
1341     return rc;
1342 }
1343 
1344 static int
1345 wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
1346 	   int yold GCC_UNUSED, int xold GCC_UNUSED,
1347 	   int y, int x)
1348 {
1349     int ret = ERR;
1350     if (okConsoleHandle(TCB)) {
1351 	COORD loc;
1352 	loc.X = (short) x;
1353 	loc.Y = (short) (y + AdjustY());
1354 	SetConsoleCursorPosition(CON.hdl, loc);
1355 	ret = OK;
1356     }
1357     return ret;
1358 }
1359 
1360 static void
1361 wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
1362 	     int labnum GCC_UNUSED,
1363 	     char *text GCC_UNUSED)
1364 {
1365     SCREEN *sp;
1366 
1367     AssertTCB();
1368     SetSP();
1369 }
1370 
1371 static void
1372 wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1373 		  int OnFlag GCC_UNUSED)
1374 {
1375     SCREEN *sp;
1376 
1377     AssertTCB();
1378     SetSP();
1379 }
1380 
1381 static chtype
1382 wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1383 {
1384     chtype res = A_NORMAL;
1385     res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1386     return res;
1387 }
1388 
1389 static void
1390 wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1391 {
1392     SCREEN *sp;
1393 
1394     AssertTCB();
1395     SetSP();
1396 }
1397 
1398 static void
1399 wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1400 	     chtype *real_map GCC_UNUSED,
1401 	     chtype *fake_map GCC_UNUSED)
1402 {
1403 #define DATA(a,b) { a, b }
1404     static struct {
1405 	int acs_code;
1406 	int use_code;
1407     } table[] = {
1408 	DATA('a', 0xb1),	/* ACS_CKBOARD  */
1409 	    DATA('f', 0xf8),	/* ACS_DEGREE   */
1410 	    DATA('g', 0xf1),	/* ACS_PLMINUS  */
1411 	    DATA('j', 0xd9),	/* ACS_LRCORNER */
1412 	    DATA('l', 0xda),	/* ACS_ULCORNER */
1413 	    DATA('k', 0xbf),	/* ACS_URCORNER */
1414 	    DATA('m', 0xc0),	/* ACS_LLCORNER */
1415 	    DATA('n', 0xc5),	/* ACS_PLUS     */
1416 	    DATA('q', 0xc4),	/* ACS_HLINE    */
1417 	    DATA('t', 0xc3),	/* ACS_LTEE     */
1418 	    DATA('u', 0xb4),	/* ACS_RTEE     */
1419 	    DATA('v', 0xc1),	/* ACS_BTEE     */
1420 	    DATA('w', 0xc2),	/* ACS_TTEE     */
1421 	    DATA('x', 0xb3),	/* ACS_VLINE    */
1422 	    DATA('y', 0xf3),	/* ACS_LEQUAL   */
1423 	    DATA('z', 0xf2),	/* ACS_GEQUAL   */
1424 	    DATA('0', 0xdb),	/* ACS_BLOCK    */
1425 	    DATA('{', 0xe3),	/* ACS_PI       */
1426 	    DATA('}', 0x9c),	/* ACS_STERLING */
1427 	    DATA(',', 0xae),	/* ACS_LARROW   */
1428 	    DATA('+', 0xaf),	/* ACS_RARROW   */
1429 	    DATA('~', 0xf9),	/* ACS_BULLET   */
1430     };
1431 #undef DATA
1432     unsigned n;
1433 
1434     SCREEN *sp;
1435     if (okConsoleHandle(TCB)) {
1436 	SetSP();
1437 
1438 	for (n = 0; n < SIZEOF(table); ++n) {
1439 	    real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET;
1440 	    if (sp != 0)
1441 		sp->_screen_acs_map[table[n].acs_code] = TRUE;
1442 	}
1443     }
1444 }
1445 
1446 static ULONGLONG
1447 tdiff(FILETIME fstart, FILETIME fend)
1448 {
1449     ULARGE_INTEGER ustart;
1450     ULARGE_INTEGER uend;
1451     ULONGLONG diff;
1452 
1453     ustart.LowPart = fstart.dwLowDateTime;
1454     ustart.HighPart = fstart.dwHighDateTime;
1455     uend.LowPart = fend.dwLowDateTime;
1456     uend.HighPart = fend.dwHighDateTime;
1457 
1458     diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1459     return diff;
1460 }
1461 
1462 static int
1463 Adjust(int milliseconds, int diff)
1464 {
1465     if (milliseconds != INFINITY) {
1466 	milliseconds -= diff;
1467 	if (milliseconds < 0)
1468 	    milliseconds = 0;
1469     }
1470     return milliseconds;
1471 }
1472 
1473 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1474 		     FROM_LEFT_2ND_BUTTON_PRESSED | \
1475 		     FROM_LEFT_3RD_BUTTON_PRESSED | \
1476 		     FROM_LEFT_4TH_BUTTON_PRESSED | \
1477 		     RIGHTMOST_BUTTON_PRESSED)
1478 
1479 static int
1480 decode_mouse(SCREEN *sp, int mask)
1481 {
1482     int result = 0;
1483 
1484     (void) sp;
1485     assert(sp && console_initialized);
1486 
1487     if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1488 	result |= BUTTON1_PRESSED;
1489     if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1490 	result |= BUTTON2_PRESSED;
1491     if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1492 	result |= BUTTON3_PRESSED;
1493     if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1494 	result |= BUTTON4_PRESSED;
1495 
1496     if (mask & RIGHTMOST_BUTTON_PRESSED) {
1497 	switch (CON.numButtons) {
1498 	case 1:
1499 	    result |= BUTTON1_PRESSED;
1500 	    break;
1501 	case 2:
1502 	    result |= BUTTON2_PRESSED;
1503 	    break;
1504 	case 3:
1505 	    result |= BUTTON3_PRESSED;
1506 	    break;
1507 	case 4:
1508 	    result |= BUTTON4_PRESSED;
1509 	    break;
1510 	}
1511     }
1512 
1513     return result;
1514 }
1515 
1516 static int
1517 console_twait(
1518 		 SCREEN *sp,
1519 		 HANDLE fd,
1520 		 int mode,
1521 		 int milliseconds,
1522 		 int *timeleft
1523 		 EVENTLIST_2nd(_nc_eventlist * evl))
1524 {
1525     INPUT_RECORD inp_rec;
1526     BOOL b;
1527     DWORD nRead = 0, rc = (DWORD) (-1);
1528     int code = 0;
1529     FILETIME fstart;
1530     FILETIME fend;
1531     int diff;
1532     bool isImmed = (milliseconds == 0);
1533 
1534 #ifdef NCURSES_WGETCH_EVENTS
1535     (void) evl;			/* TODO: implement wgetch-events */
1536 #endif
1537 
1538 #define CONSUME() ReadConsoleInput(fd,&inp_rec,1,&nRead)
1539 
1540     assert(sp);
1541 
1542     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1543 		      milliseconds, mode));
1544 
1545     if (milliseconds < 0)
1546 	milliseconds = INFINITY;
1547 
1548     memset(&inp_rec, 0, sizeof(inp_rec));
1549 
1550     while (true) {
1551 	GetSystemTimeAsFileTime(&fstart);
1552 	rc = WaitForSingleObject(fd, (DWORD) milliseconds);
1553 	GetSystemTimeAsFileTime(&fend);
1554 	diff = (int) tdiff(fstart, fend);
1555 	milliseconds = Adjust(milliseconds, diff);
1556 
1557 	if (!isImmed && milliseconds <= 0)
1558 	    break;
1559 
1560 	if (rc == WAIT_OBJECT_0) {
1561 	    if (mode) {
1562 		b = GetNumberOfConsoleInputEvents(fd, &nRead);
1563 		if (b && nRead > 0) {
1564 		    b = PeekConsoleInput(fd, &inp_rec, 1, &nRead);
1565 		    if (b && nRead > 0) {
1566 			switch (inp_rec.EventType) {
1567 			case KEY_EVENT:
1568 			    if (mode & TW_INPUT) {
1569 				WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1570 				char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1571 
1572 				if (inp_rec.Event.KeyEvent.bKeyDown) {
1573 				    if (0 == ch) {
1574 					int nKey = MapKey(vk);
1575 					if (nKey < 0) {
1576 					    CONSUME();
1577 					    continue;
1578 					}
1579 				    }
1580 				    code = TW_INPUT;
1581 				    goto end;
1582 				} else {
1583 				    CONSUME();
1584 				}
1585 			    }
1586 			    continue;
1587 			case MOUSE_EVENT:
1588 			    if (decode_mouse(sp,
1589 					     (inp_rec.Event.MouseEvent.dwButtonState
1590 					      & BUTTON_MASK)) == 0) {
1591 				CONSUME();
1592 			    } else if (mode & TW_MOUSE) {
1593 				code = TW_MOUSE;
1594 				goto end;
1595 			    }
1596 			    continue;
1597 			    /* e.g., FOCUS_EVENT */
1598 			default:
1599 			    CONSUME();
1600 			    selectActiveHandle();
1601 			    continue;
1602 			}
1603 		    }
1604 		}
1605 	    }
1606 	    continue;
1607 	} else {
1608 	    if (rc != WAIT_TIMEOUT) {
1609 		code = -1;
1610 		break;
1611 	    } else {
1612 		code = 0;
1613 		break;
1614 	    }
1615 	}
1616     }
1617   end:
1618 
1619     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1620 		      code, errno, milliseconds));
1621 
1622     if (timeleft)
1623 	*timeleft = milliseconds;
1624 
1625     return code;
1626 }
1627 
1628 static int
1629 wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,
1630 	   int mode,
1631 	   int milliseconds,
1632 	   int *timeleft
1633 	   EVENTLIST_2nd(_nc_eventlist * evl))
1634 {
1635     SCREEN *sp;
1636     int code = 0;
1637 
1638     if (okConsoleHandle(TCB)) {
1639 	SetSP();
1640 
1641 	code = console_twait(sp,
1642 			     CON.inp,
1643 			     mode,
1644 			     milliseconds,
1645 			     timeleft EVENTLIST_2nd(evl));
1646     }
1647     return code;
1648 }
1649 
1650 static bool
1651 handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer)
1652 {
1653     MEVENT work;
1654     bool result = FALSE;
1655 
1656     assert(sp);
1657 
1658     sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1659     sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1660 
1661     /*
1662      * We're only interested if the button is pressed or released.
1663      * FIXME: implement continuous event-tracking.
1664      */
1665     if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1666 
1667 	memset(&work, 0, sizeof(work));
1668 
1669 	if (sp->_drv_mouse_new_buttons) {
1670 
1671 	    work.bstate |= (mmask_t) decode_mouse(sp, sp->_drv_mouse_new_buttons);
1672 
1673 	} else {
1674 
1675 	    /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1676 	    work.bstate |= (mmask_t) (decode_mouse(sp,
1677 						   sp->_drv_mouse_old_buttons)
1678 				      >> 1);
1679 
1680 	    result = TRUE;
1681 	}
1682 
1683 	work.x = mer.dwMousePosition.X;
1684 	work.y = mer.dwMousePosition.Y - AdjustY();
1685 
1686 	sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1687 	sp->_drv_mouse_tail += 1;
1688     }
1689 
1690     return result;
1691 }
1692 
1693 static int
1694 wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1695 {
1696     SCREEN *sp;
1697     int n = -1;
1698 
1699     T((T_CALLED("win32con::wcon_read(%p)"), TCB));
1700 
1701     assert(buf);
1702     if (okConsoleHandle(TCB)) {
1703 	SetSP();
1704 
1705 	n = _nc_mingw_console_read(sp, CON.inp, buf);
1706     }
1707     returnCode(n);
1708 }
1709 
1710 static int
1711 wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1712 {
1713     T((T_CALLED("win32con::wcon_nap(%p, %d)"), TCB, ms));
1714     Sleep((DWORD) ms);
1715     returnCode(OK);
1716 }
1717 
1718 static int
1719 wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int mode)
1720 {
1721     int res = -1;
1722 
1723     T((T_CALLED("win32con:wcon_cursorSet(%d)"), mode));
1724     if (okConsoleHandle(TCB)) {
1725 	CONSOLE_CURSOR_INFO this_CI = CON.save_CI;
1726 	switch (mode) {
1727 	case 0:
1728 	    this_CI.bVisible = FALSE;
1729 	    break;
1730 	case 1:
1731 	    break;
1732 	case 2:
1733 	    this_CI.dwSize = 100;
1734 	    break;
1735 	}
1736 	SetConsoleCursorInfo(CON.hdl, &this_CI);
1737     }
1738     returnCode(res);
1739 }
1740 
1741 static bool
1742 wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int keycode)
1743 {
1744     WORD nKey;
1745     void *res;
1746     bool found = FALSE;
1747     LONG key = GenMap(0, (WORD) keycode);
1748 
1749     T((T_CALLED("win32con::wcon_kyExist(%d)"), keycode));
1750     res = bsearch(&key,
1751 		  CON.rmap,
1752 		  (size_t) (N_INI + FKEYS),
1753 		  sizeof(keylist[0]),
1754 		  rkeycompare);
1755     if (res) {
1756 	key = *((LONG *) res);
1757 	nKey = LOWORD(key);
1758 	if (!(nKey & 0x8000))
1759 	    found = TRUE;
1760     }
1761     returnCode(found);
1762 }
1763 
1764 static int
1765 wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1766 {
1767     SCREEN *sp;
1768     int code = ERR;
1769 
1770     T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag));
1771 
1772     if (okConsoleHandle(TCB)) {
1773 	SetSP();
1774 
1775 	if (sp) {
1776 	    code = OK;
1777 	}
1778     }
1779     returnCode(code);
1780 }
1781 
1782 static int
1783 wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB,
1784 	   int keycode,
1785 	   int flag)
1786 {
1787     int code = ERR;
1788     SCREEN *sp;
1789     WORD nKey;
1790     WORD vKey;
1791     void *res;
1792     LONG key = GenMap(0, (WORD) keycode);
1793 
1794     T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag));
1795 
1796     if (okConsoleHandle(TCB)) {
1797 	SetSP();
1798 
1799 	if (sp) {
1800 	    res = bsearch(&key,
1801 			  CON.rmap,
1802 			  (size_t) (N_INI + FKEYS),
1803 			  sizeof(keylist[0]),
1804 			  rkeycompare);
1805 	    if (res) {
1806 		key = *((LONG *) res);
1807 		vKey = HIWORD(key);
1808 		nKey = (LOWORD(key)) & 0x7fff;
1809 		if (!flag)
1810 		    nKey |= 0x8000;
1811 		*(LONG *) res = GenMap(vKey, nKey);
1812 	    }
1813 	}
1814     }
1815     returnCode(code);
1816 }
1817 
1818 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1819     FALSE,
1820 	wcon_name,		/* Name */
1821 	wcon_CanHandle,		/* CanHandle */
1822 	wcon_init,		/* init */
1823 	wcon_release,		/* release */
1824 	wcon_size,		/* size */
1825 	wcon_sgmode,		/* sgmode */
1826 	wcon_conattr,		/* conattr */
1827 	wcon_mvcur,		/* hwcur */
1828 	wcon_mode,		/* mode */
1829 	wcon_rescol,		/* rescol */
1830 	wcon_rescolors,		/* rescolors */
1831 	wcon_setcolor,		/* color */
1832 	wcon_dobeepflash,	/* DoBeepFlash */
1833 	wcon_initpair,		/* initpair */
1834 	wcon_initcolor,		/* initcolor */
1835 	wcon_do_color,		/* docolor */
1836 	wcon_initmouse,		/* initmouse */
1837 	wcon_testmouse,		/* testmouse */
1838 	wcon_setfilter,		/* setfilter */
1839 	wcon_hwlabel,		/* hwlabel */
1840 	wcon_hwlabelOnOff,	/* hwlabelOnOff */
1841 	wcon_doupdate,		/* update */
1842 	wcon_defaultcolors,	/* defaultcolors */
1843 	wcon_print,		/* print */
1844 	wcon_size,		/* getsize */
1845 	wcon_setsize,		/* setsize */
1846 	wcon_initacs,		/* initacs */
1847 	wcon_screen_init,	/* scinit */
1848 	wcon_wrap,		/* scexit */
1849 	wcon_twait,		/* twait */
1850 	wcon_read,		/* read */
1851 	wcon_nap,		/* nap */
1852 	wcon_kpad,		/* kpad */
1853 	wcon_keyok,		/* kyOk */
1854 	wcon_kyExist,		/* kyExist */
1855 	wcon_cursorSet		/* cursorSet */
1856 };
1857 
1858 /* --------------------------------------------------------- */
1859 
1860 static HANDLE
1861 get_handle(int fd)
1862 {
1863     intptr_t value = _get_osfhandle(fd);
1864     return (HANDLE) value;
1865 }
1866 
1867 #if WINVER >= 0x0600
1868 /*   This function tests, whether or not the ncurses application
1869      is running as a descendant of MSYS2/cygwin mintty terminal
1870      application. mintty doesn't use Windows Console for it's screen
1871      I/O, so the native Windows _isatty doesn't recognize it as
1872      character device. But we can discover we are at the end of an
1873      Pipe and can query to server side of the pipe, looking whether
1874      or not this is mintty.
1875  */
1876 static int
1877 _ismintty(int fd, LPHANDLE pMinTTY)
1878 {
1879     HANDLE handle = get_handle(fd);
1880     DWORD dw;
1881     int code = 0;
1882 
1883     T((T_CALLED("win32con::_ismintty(%d, %p)"), fd, pMinTTY));
1884 
1885     if (handle != INVALID_HANDLE_VALUE) {
1886 	dw = GetFileType(handle);
1887 	if (dw == FILE_TYPE_PIPE) {
1888 	    if (GetNamedPipeInfo(handle, 0, 0, 0, 0)) {
1889 		ULONG pPid;
1890 		/* Requires NT6 */
1891 		if (GetNamedPipeServerProcessId(handle, &pPid)) {
1892 		    TCHAR buf[MAX_PATH];
1893 		    DWORD len = 0;
1894 		    /* These security attributes may allow us to
1895 		       create a remote thread in mintty to manipulate
1896 		       the terminal state remotely */
1897 		    HANDLE pHandle = OpenProcess(
1898 						    PROCESS_CREATE_THREAD
1899 						    | PROCESS_QUERY_INFORMATION
1900 						    | PROCESS_VM_OPERATION
1901 						    | PROCESS_VM_WRITE
1902 						    | PROCESS_VM_READ,
1903 						    FALSE,
1904 						    pPid);
1905 		    if (pMinTTY)
1906 			*pMinTTY = INVALID_HANDLE_VALUE;
1907 		    if (pHandle != INVALID_HANDLE_VALUE) {
1908 			if ((len = GetProcessImageFileName(
1909 							      pHandle,
1910 							      buf,
1911 							      (DWORD)
1912 							      array_length(buf)))) {
1913 			    TCHAR *pos = _tcsrchr(buf, _T('\\'));
1914 			    if (pos) {
1915 				pos++;
1916 				if (_tcsnicmp(pos, _TEXT("mintty.exe"), 10)
1917 				    == 0) {
1918 				    if (pMinTTY)
1919 					*pMinTTY = pHandle;
1920 				    code = 1;
1921 				}
1922 			    }
1923 			}
1924 		    }
1925 		}
1926 	    }
1927 	}
1928     }
1929     returnCode(code);
1930 }
1931 #endif
1932 
1933 /*   Borrowed from ansicon project.
1934      Check whether or not an I/O handle is associated with
1935      a Windows console.
1936 */
1937 static BOOL
1938 IsConsoleHandle(HANDLE hdl)
1939 {
1940     DWORD dwFlag = 0;
1941     BOOL result;
1942 
1943     if (!GetConsoleMode(hdl, &dwFlag)) {
1944 	result = (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL);
1945     } else {
1946 	result = (int) (dwFlag & ENABLE_PROCESSED_OUTPUT);
1947     }
1948     return result;
1949 }
1950 
1951 /*   Our replacement for the systems _isatty to include also
1952      a test for mintty. This is called from the NC_ISATTY macro
1953      defined in curses.priv.h
1954  */
1955 int
1956 _nc_mingw_isatty(int fd)
1957 {
1958     int result = 0;
1959 
1960 #ifdef __MING32__
1961 #define SysISATTY(fd) _isatty(fd)
1962 #else
1963 #define SysISATTY(fd) isatty(fd)
1964 #endif
1965     if (SysISATTY(fd)) {
1966 	result = 1;
1967     } else {
1968 #if WINVER >= 0x0600
1969 	result = _ismintty(fd, NULL);
1970 #endif
1971     }
1972     return result;
1973 }
1974 
1975 /*   This is used when running in terminfo mode to discover,
1976      whether or not the "terminal" is actually a Windows
1977      Console. It's the responsibility of the console to deal
1978      with the terminal escape sequences that are sent by
1979      terminfo.
1980  */
1981 int
1982 _nc_mingw_isconsole(int fd)
1983 {
1984     HANDLE hdl = get_handle(fd);
1985     int code = 0;
1986 
1987     T((T_CALLED("win32con::_nc_mingw_isconsole(%d)"), fd));
1988 
1989     code = (int) IsConsoleHandle(hdl);
1990 
1991     returnCode(code);
1992 }
1993 
1994 #define TC_PROLOGUE(fd) \
1995     SCREEN *sp;                                               \
1996     TERMINAL *term = 0;                                       \
1997     int code = ERR;                                           \
1998     if (_nc_screen_chain == 0)                                \
1999         return 0;                                             \
2000     for (each_screen(sp)) {                                   \
2001         if (sp->_term && (sp->_term->Filedes == fd)) {        \
2002             term = sp->_term;                                 \
2003             break;                                            \
2004         }                                                     \
2005     }                                                         \
2006     assert(term != 0)
2007 
2008 int
2009 _nc_mingw_tcsetattr(
2010 		       int fd,
2011 		       int optional_action GCC_UNUSED,
2012 		       const struct termios *arg)
2013 {
2014     TC_PROLOGUE(fd);
2015 
2016     if (_nc_mingw_isconsole(fd)) {
2017 	DWORD dwFlag = 0;
2018 	HANDLE ofd = get_handle(fd);
2019 	if (ofd != INVALID_HANDLE_VALUE) {
2020 	    if (arg) {
2021 		if (arg->c_lflag & ICANON)
2022 		    dwFlag |= ENABLE_LINE_INPUT;
2023 		else
2024 		    dwFlag = dwFlag & (DWORD) (~ENABLE_LINE_INPUT);
2025 
2026 		if (arg->c_lflag & ECHO)
2027 		    dwFlag = dwFlag | ENABLE_ECHO_INPUT;
2028 		else
2029 		    dwFlag = dwFlag & (DWORD) (~ENABLE_ECHO_INPUT);
2030 
2031 		if (arg->c_iflag & BRKINT)
2032 		    dwFlag |= ENABLE_PROCESSED_INPUT;
2033 		else
2034 		    dwFlag = dwFlag & (DWORD) (~ENABLE_PROCESSED_INPUT);
2035 	    }
2036 	    dwFlag |= ENABLE_MOUSE_INPUT;
2037 	    SetConsoleMode(ofd, dwFlag);
2038 	    code = OK;
2039 	}
2040     }
2041     if (arg)
2042 	term->Nttyb = *arg;
2043 
2044     return code;
2045 }
2046 
2047 int
2048 _nc_mingw_tcgetattr(int fd, struct termios *arg)
2049 {
2050     TC_PROLOGUE(fd);
2051 
2052     if (_nc_mingw_isconsole(fd)) {
2053 	if (arg)
2054 	    *arg = term->Nttyb;
2055     }
2056     return code;
2057 }
2058 
2059 int
2060 _nc_mingw_tcflush(int fd, int queue)
2061 {
2062     TC_PROLOGUE(fd);
2063     (void) term;
2064 
2065     if (_nc_mingw_isconsole(fd)) {
2066 	if (queue == TCIFLUSH) {
2067 	    BOOL b = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
2068 	    if (!b)
2069 		return (int) GetLastError();
2070 	}
2071     }
2072     return code;
2073 }
2074 
2075 int
2076 _nc_mingw_testmouse(
2077 		       SCREEN *sp,
2078 		       HANDLE fd,
2079 		       int delay
2080 		       EVENTLIST_2nd(_nc_eventlist * evl))
2081 {
2082     int rc = 0;
2083 
2084     assert(sp);
2085 
2086     if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
2087 	rc = TW_MOUSE;
2088     } else {
2089 	rc = console_twait(sp,
2090 			   fd,
2091 			   TWAIT_MASK,
2092 			   delay,
2093 			   (int *) 0
2094 			   EVENTLIST_2nd(evl));
2095     }
2096     return rc;
2097 }
2098 
2099 int
2100 _nc_mingw_console_read(
2101 			  SCREEN *sp,
2102 			  HANDLE fd,
2103 			  int *buf)
2104 {
2105     int rc = -1;
2106     INPUT_RECORD inp_rec;
2107     BOOL b;
2108     DWORD nRead;
2109     WORD vk;
2110 
2111     assert(sp);
2112     assert(buf);
2113 
2114     memset(&inp_rec, 0, sizeof(inp_rec));
2115 
2116     T((T_CALLED("_nc_mingw_console_read(%p)"), sp));
2117 
2118     while ((b = ReadConsoleInput(fd, &inp_rec, 1, &nRead))) {
2119 	if (b && nRead > 0) {
2120 	    if (rc < 0)
2121 		rc = 0;
2122 	    rc = rc + (int) nRead;
2123 	    if (inp_rec.EventType == KEY_EVENT) {
2124 		if (!inp_rec.Event.KeyEvent.bKeyDown)
2125 		    continue;
2126 		*buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
2127 		vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
2128 		/*
2129 		 * There are 24 virtual function-keys, and typically
2130 		 * 12 function-keys on a keyboard.  Use the shift-modifier
2131 		 * to provide the remaining 12 keys.
2132 		 */
2133 		if (vk >= VK_F1 && vk <= VK_F12) {
2134 		    if (inp_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) {
2135 			vk = (WORD) (vk + 12);
2136 		    }
2137 		}
2138 		if (*buf == 0) {
2139 		    int key = MapKey(vk);
2140 		    if (key < 0)
2141 			continue;
2142 		    if (sp->_keypad_on) {
2143 			*buf = key;
2144 		    } else {
2145 			ungetch('\0');
2146 			*buf = AnsiKey(vk);
2147 		    }
2148 		}
2149 		break;
2150 	    } else if (inp_rec.EventType == MOUSE_EVENT) {
2151 		if (handle_mouse(sp,
2152 				 inp_rec.Event.MouseEvent)) {
2153 		    *buf = KEY_MOUSE;
2154 		    break;
2155 		}
2156 	    }
2157 	    continue;
2158 	}
2159     }
2160     returnCode(rc);
2161 }
2162 
2163 static bool
2164 InitConsole(void)
2165 {
2166     /* initialize once, or not at all */
2167     if (!console_initialized) {
2168 	int i;
2169 	DWORD num_buttons;
2170 	WORD a;
2171 	BOOL buffered = TRUE;
2172 	BOOL b;
2173 
2174 	START_TRACE();
2175 	if (_nc_mingw_isatty(0)) {
2176 	    CON.isMinTTY = TRUE;
2177 	}
2178 
2179 	for (i = 0; i < (N_INI + FKEYS); i++) {
2180 	    if (i < N_INI) {
2181 		CON.rmap[i] = CON.map[i] =
2182 		    (DWORD) keylist[i];
2183 		CON.ansi_map[i] = (DWORD) ansi_keys[i];
2184 	    } else {
2185 		CON.rmap[i] = CON.map[i] =
2186 		    (DWORD) GenMap((VK_F1 + (i - N_INI)),
2187 				   (KEY_F(1) + (i - N_INI)));
2188 		CON.ansi_map[i] =
2189 		    (DWORD) GenMap((VK_F1 + (i - N_INI)),
2190 				   (';' + (i - N_INI)));
2191 	    }
2192 	}
2193 	qsort(CON.ansi_map,
2194 	      (size_t) (MAPSIZE),
2195 	      sizeof(keylist[0]),
2196 	      keycompare);
2197 	qsort(CON.map,
2198 	      (size_t) (MAPSIZE),
2199 	      sizeof(keylist[0]),
2200 	      keycompare);
2201 	qsort(CON.rmap,
2202 	      (size_t) (MAPSIZE),
2203 	      sizeof(keylist[0]),
2204 	      rkeycompare);
2205 
2206 	if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
2207 	    CON.numButtons = (int) num_buttons;
2208 	} else {
2209 	    CON.numButtons = 1;
2210 	}
2211 
2212 	a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
2213 	for (i = 0; i < NUMPAIRS; i++)
2214 	    CON.pairs[i] = a;
2215 
2216 	CON.inp = GetStdHandle(STD_INPUT_HANDLE);
2217 	CON.out = GetStdHandle(STD_OUTPUT_HANDLE);
2218 
2219 	b = AllocConsole();
2220 
2221 	if (!b)
2222 	    b = AttachConsole(ATTACH_PARENT_PROCESS);
2223 
2224 	if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
2225 	    T(("... will not buffer console"));
2226 	    buffered = FALSE;
2227 	    CON.hdl = CON.out;
2228 	} else {
2229 	    T(("... creating console buffer"));
2230 	    CON.hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
2231 						0,
2232 						NULL,
2233 						CONSOLE_TEXTMODE_BUFFER,
2234 						NULL);
2235 	}
2236 
2237 	if (CON.hdl != INVALID_HANDLE_VALUE) {
2238 	    CON.buffered = buffered;
2239 	    get_SBI();
2240 	    CON.save_SBI = CON.SBI;
2241 	    if (!buffered) {
2242 		save_original_screen();
2243 		set_scrollback(FALSE, &CON.SBI);
2244 	    }
2245 	    GetConsoleCursorInfo(CON.hdl, &CON.save_CI);
2246 	    T(("... initial cursor is %svisible, %d%%",
2247 	       (CON.save_CI.bVisible ? "" : "not-"),
2248 	       (int) CON.save_CI.dwSize));
2249 	}
2250 
2251 	console_initialized = TRUE;
2252     }
2253     return (CON.hdl != INVALID_HANDLE_VALUE);
2254 }
2255 
2256 static bool
2257 okConsoleHandle(TERMINAL_CONTROL_BLOCK * TCB)
2258 {
2259     return ((TCB != 0) &&
2260 	    (TCB->magic == WINMAGIC) &&
2261 	    InitConsole());
2262 }
2263 
2264 /*
2265  * While a constructor would ensure that this module is initialized, that will
2266  * interfere with applications that may combine this with GUI interfaces.
2267  */
2268 #if 0
2269 static
2270 __attribute__((constructor))
2271      void _enter_console(void)
2272 {
2273     (void) InitConsole();
2274 }
2275 #endif
2276