1/****************************************************************************
2 * Copyright 2018-2021,2023 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#define CONTROL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
59
60MODULE_ID("$Id: win_driver.c,v 1.74 2023/09/16 16:27:44 tom Exp $")
61
62#define TypeAlloca(type,count) (type*) _alloca(sizeof(type) * (size_t) (count))
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
70static bool InitConsole(void);
71static 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/* *INDENT-OFF* */
88static 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};
101static 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/* *INDENT-ON* */
115#define N_INI ((int)array_length(keylist))
116#define FKEYS 24
117#define MAPSIZE (FKEYS + N_INI)
118#define NUMPAIRS 64
119
120/*   A process can only have a single console, so it is safe
121     to maintain all the information about it in a single
122     static structure.
123 */
124static struct {
125    BOOL initialized;
126    BOOL buffered;
127    BOOL window_only;
128    BOOL progMode;
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
148static BOOL console_initialized = FALSE;
149
150static WORD
151MapColor(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
170static WORD
171MapAttr(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 */
202static void
203dump_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 */
261static BOOL
262con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
263{
264    int actual = 0;
265    CHAR_INFO *ci = TypeAlloca(CHAR_INFO, 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
311static BOOL
312con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
313{
314    CHAR_INFO *ci = TypeAlloca(CHAR_INFO, 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 */
357static int
358find_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 */
391static int
392find_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
430static void
431selectActiveHandle(void)
432{
433    if (CON.lastOut != CON.hdl) {
434	CON.lastOut = CON.hdl;
435	SetConsoleActiveScreenBuffer(CON.lastOut);
436    }
437}
438
439static bool
440restore_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
471static const char *
472wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
473{
474    (void) TCB;
475    return "win32console";
476}
477
478static int
479wcon_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 = TypeAlloca(cchar_t, 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 = TypeAlloca(chtype, 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#ifdef __MING32__
616#define SysISATTY(fd) _isatty(fd)
617#else
618#define SysISATTY(fd) isatty(fd)
619#endif
620
621static bool
622wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
623	       const char *tname,
624	       int *errret GCC_UNUSED)
625{
626    bool code = FALSE;
627
628    T((T_CALLED("win32con::wcon_CanHandle(%p)"), TCB));
629
630    assert((TCB != 0) && (tname != 0));
631
632    TCB->magic = WINMAGIC;
633
634    if (tname == 0 || *tname == 0)
635	code = TRUE;
636    else if (tname != 0 && *tname == '#') {
637	/*
638	 * Use "#" (a character which cannot begin a terminal's name) to
639	 * select specific driver from the table.
640	 *
641	 * In principle, we could have more than one non-terminfo driver,
642	 * e.g., "win32gui".
643	 */
644	size_t n = strlen(tname + 1);
645	if (n != 0
646	    && ((strncmp(tname + 1, "win32console", n) == 0)
647		|| (strncmp(tname + 1, "win32con", n) == 0))) {
648	    code = TRUE;
649	}
650    } else if (tname != 0 && stricmp(tname, "unknown") == 0) {
651	code = TRUE;
652    } else if (SysISATTY(TCB->term.Filedes)) {
653	code = TRUE;
654    }
655
656    /*
657     * This is intentional, to avoid unnecessary breakage of applications
658     * using <term.h> symbols.
659     */
660    if (code && (TerminalType(&TCB->term).Booleans == 0)) {
661	_nc_init_termtype(&TerminalType(&TCB->term));
662#if NCURSES_EXT_NUMBERS
663	_nc_export_termtype2(&TCB->term.type, &TerminalType(&TCB->term));
664#endif
665    }
666
667    if (!code) {
668	if (_nc_mingw_isconsole(0))
669	    CON.isTermInfoConsole = TRUE;
670    }
671    returnBool(code);
672}
673
674static int
675wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
676		 int beepFlag)
677{
678    SCREEN *sp;
679    int res = ERR;
680
681    int high = (CON.SBI.srWindow.Bottom - CON.SBI.srWindow.Top + 1);
682    int wide = (CON.SBI.srWindow.Right - CON.SBI.srWindow.Left + 1);
683    int max_cells = (high * wide);
684    int i;
685
686    CHAR_INFO *this_screen = TypeAlloca(CHAR_INFO, max_cells);
687    CHAR_INFO *that_screen = TypeAlloca(CHAR_INFO, max_cells);
688    COORD this_size;
689    SMALL_RECT this_region;
690    COORD bufferCoord;
691
692    if (okConsoleHandle(TCB)) {
693	SetSP();
694	this_region.Top = CON.SBI.srWindow.Top;
695	this_region.Left = CON.SBI.srWindow.Left;
696	this_region.Bottom = CON.SBI.srWindow.Bottom;
697	this_region.Right = CON.SBI.srWindow.Right;
698
699	this_size.X = (SHORT) wide;
700	this_size.Y = (SHORT) high;
701
702	bufferCoord.X = this_region.Left;
703	bufferCoord.Y = this_region.Top;
704
705	if (!beepFlag &&
706	    read_screen(CON.hdl,
707			this_screen,
708			this_size,
709			bufferCoord,
710			&this_region)) {
711
712	    memcpy(that_screen,
713		   this_screen,
714		   sizeof(CHAR_INFO) * (size_t) max_cells);
715
716	    for (i = 0; i < max_cells; i++) {
717		that_screen[i].Attributes = RevAttr(that_screen[i].Attributes);
718	    }
719
720	    write_screen(CON.hdl, that_screen, this_size, bufferCoord, &this_region);
721	    Sleep(200);
722	    write_screen(CON.hdl, this_screen, this_size, bufferCoord, &this_region);
723
724	} else {
725	    MessageBeep(MB_ICONWARNING);	/* MB_OK might be better */
726	}
727	res = OK;
728    }
729    return res;
730}
731
732static int
733wcon_print(TERMINAL_CONTROL_BLOCK * TCB,
734	   char *data GCC_UNUSED,
735	   int len GCC_UNUSED)
736{
737    SCREEN *sp;
738
739    AssertTCB();
740    SetSP();
741
742    return ERR;
743}
744
745static int
746wcon_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
747		   int fg GCC_UNUSED,
748		   int bg GCC_UNUSED)
749{
750    SCREEN *sp;
751    int code = ERR;
752
753    AssertTCB();
754    SetSP();
755
756    return (code);
757}
758
759static bool
760get_SBI(void)
761{
762    bool rc = FALSE;
763    if (GetConsoleScreenBufferInfo(CON.hdl, &(CON.SBI))) {
764	T(("GetConsoleScreenBufferInfo"));
765	T(("... buffer(X:%d Y:%d)",
766	   CON.SBI.dwSize.X,
767	   CON.SBI.dwSize.Y));
768	T(("... window(X:%d Y:%d)",
769	   CON.SBI.dwMaximumWindowSize.X,
770	   CON.SBI.dwMaximumWindowSize.Y));
771	T(("... cursor(X:%d Y:%d)",
772	   CON.SBI.dwCursorPosition.X,
773	   CON.SBI.dwCursorPosition.Y));
774	T(("... display(Top:%d Bottom:%d Left:%d Right:%d)",
775	   CON.SBI.srWindow.Top,
776	   CON.SBI.srWindow.Bottom,
777	   CON.SBI.srWindow.Left,
778	   CON.SBI.srWindow.Right));
779	if (CON.buffered) {
780	    CON.origin.X = 0;
781	    CON.origin.Y = 0;
782	} else {
783	    CON.origin.X = CON.SBI.srWindow.Left;
784	    CON.origin.Y = CON.SBI.srWindow.Top;
785	}
786	rc = TRUE;
787    } else {
788	T(("GetConsoleScreenBufferInfo ERR"));
789    }
790    return rc;
791}
792
793static void
794wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
795	      int fore,
796	      int color,
797	      int (*outc) (SCREEN *, int) GCC_UNUSED)
798{
799    if (okConsoleHandle(TCB)) {
800	WORD a = MapColor(fore, color);
801	a |= (WORD) ((CON.SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f));
802	SetConsoleTextAttribute(CON.hdl, a);
803	get_SBI();
804    }
805}
806
807static bool
808wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB)
809{
810    bool res = FALSE;
811
812    if (okConsoleHandle(TCB)) {
813	WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
814	SetConsoleTextAttribute(CON.hdl, a);
815	get_SBI();
816	res = TRUE;
817    }
818    return res;
819}
820
821static bool
822wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
823{
824    int result = FALSE;
825    SCREEN *sp;
826
827    AssertTCB();
828    SetSP();
829
830    return result;
831}
832
833static int
834wcon_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
835{
836    int result = ERR;
837
838    T((T_CALLED("win32con::wcon_size(%p)"), TCB));
839
840    if (okConsoleHandle(TCB) &&
841	Lines != NULL &&
842	Cols != NULL) {
843	if (CON.buffered) {
844	    *Lines = (int) (CON.SBI.dwSize.Y);
845	    *Cols = (int) (CON.SBI.dwSize.X);
846	} else {
847	    *Lines = (int) (CON.SBI.srWindow.Bottom + 1 -
848			    CON.SBI.srWindow.Top);
849	    *Cols = (int) (CON.SBI.srWindow.Right + 1 -
850			   CON.SBI.srWindow.Left);
851	}
852	result = OK;
853    }
854    returnCode(result);
855}
856
857static int
858wcon_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
859	     int l GCC_UNUSED,
860	     int c GCC_UNUSED)
861{
862    AssertTCB();
863    return ERR;
864}
865
866static int
867wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
868{
869    DWORD dwFlag = 0;
870    tcflag_t iflag;
871    tcflag_t lflag;
872    int result = ERR;
873
874    if (buf != NULL && okConsoleHandle(TCB)) {
875
876	if (setFlag) {
877	    iflag = buf->c_iflag;
878	    lflag = buf->c_lflag;
879
880	    GetConsoleMode(CON.inp, &dwFlag);
881
882	    if (lflag & ICANON)
883		dwFlag |= ENABLE_LINE_INPUT;
884	    else
885		dwFlag &= (DWORD) (~ENABLE_LINE_INPUT);
886
887	    if (lflag & ECHO)
888		dwFlag |= ENABLE_ECHO_INPUT;
889	    else
890		dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT);
891
892	    if (iflag & BRKINT)
893		dwFlag |= ENABLE_PROCESSED_INPUT;
894	    else
895		dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT);
896
897	    dwFlag |= ENABLE_MOUSE_INPUT;
898
899	    buf->c_iflag = iflag;
900	    buf->c_lflag = lflag;
901	    SetConsoleMode(CON.inp, dwFlag);
902	    TCB->term.Nttyb = *buf;
903	} else {
904	    iflag = TCB->term.Nttyb.c_iflag;
905	    lflag = TCB->term.Nttyb.c_lflag;
906	    GetConsoleMode(CON.inp, &dwFlag);
907
908	    if (dwFlag & ENABLE_LINE_INPUT)
909		lflag |= ICANON;
910	    else
911		lflag &= (tcflag_t) (~ICANON);
912
913	    if (dwFlag & ENABLE_ECHO_INPUT)
914		lflag |= ECHO;
915	    else
916		lflag &= (tcflag_t) (~ECHO);
917
918	    if (dwFlag & ENABLE_PROCESSED_INPUT)
919		iflag |= BRKINT;
920	    else
921		iflag &= (tcflag_t) (~BRKINT);
922
923	    TCB->term.Nttyb.c_iflag = iflag;
924	    TCB->term.Nttyb.c_lflag = lflag;
925
926	    *buf = TCB->term.Nttyb;
927	}
928	result = OK;
929    }
930    return result;
931}
932
933#define MIN_WIDE 80
934#define MIN_HIGH 24
935
936/*
937 * In "normal" mode, reset the buffer- and window-sizes back to their original values.
938 */
939static void
940set_scrollback(bool normal, CONSOLE_SCREEN_BUFFER_INFO * info)
941{
942    SMALL_RECT rect;
943    COORD coord;
944    bool changed = FALSE;
945
946    T((T_CALLED("win32con::set_scrollback(%s)"),
947       (normal
948	? "normal"
949	: "application")));
950
951    T(("... SBI.srWindow %d,%d .. %d,%d",
952       info->srWindow.Top,
953       info->srWindow.Left,
954       info->srWindow.Bottom,
955       info->srWindow.Right));
956    T(("... SBI.dwSize %dx%d",
957       info->dwSize.Y,
958       info->dwSize.X));
959
960    if (normal) {
961	rect = info->srWindow;
962	coord = info->dwSize;
963	if (memcmp(info, &CON.SBI, sizeof(*info)) != 0) {
964	    changed = TRUE;
965	    CON.SBI = *info;
966	}
967    } else {
968	int high = info->srWindow.Bottom - info->srWindow.Top + 1;
969	int wide = info->srWindow.Right - info->srWindow.Left + 1;
970
971	if (high < MIN_HIGH) {
972	    T(("... height %d < %d", high, MIN_HIGH));
973	    high = MIN_HIGH;
974	    changed = TRUE;
975	}
976	if (wide < MIN_WIDE) {
977	    T(("... width %d < %d", wide, MIN_WIDE));
978	    wide = MIN_WIDE;
979	    changed = TRUE;
980	}
981
982	rect.Left =
983	    rect.Top = 0;
984	rect.Right = (SHORT) (wide - 1);
985	rect.Bottom = (SHORT) (high - 1);
986
987	coord.X = (SHORT) wide;
988	coord.Y = (SHORT) high;
989
990	if (info->dwSize.Y != high ||
991	    info->dwSize.X != wide ||
992	    info->srWindow.Top != 0 ||
993	    info->srWindow.Left != 0) {
994	    changed = TRUE;
995	}
996
997    }
998
999    if (changed) {
1000	T(("... coord %d,%d", coord.Y, coord.X));
1001	T(("... rect %d,%d - %d,%d",
1002	   rect.Top, rect.Left,
1003	   rect.Bottom, rect.Right));
1004	SetConsoleScreenBufferSize(CON.hdl, coord);	/* dwSize */
1005	SetConsoleWindowInfo(CON.hdl, TRUE, &rect);	/* srWindow */
1006	get_SBI();
1007    }
1008    returnVoid;
1009}
1010
1011static int
1012wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
1013{
1014    SCREEN *sp;
1015    TERMINAL *_term = (TERMINAL *) TCB;
1016    int code = ERR;
1017
1018    if (okConsoleHandle(TCB)) {
1019	sp = TCB->csp;
1020
1021	T((T_CALLED("win32con::wcon_mode(%p, prog=%d, def=%d)"),
1022	   TCB, progFlag, defFlag));
1023
1024	CON.progMode = progFlag;
1025	CON.lastOut = progFlag ? CON.hdl : CON.out;
1026	SetConsoleActiveScreenBuffer(CON.lastOut);
1027
1028	if (progFlag) /* prog mode */  {
1029	    if (defFlag) {
1030		if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
1031		    _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS);
1032		    code = OK;
1033		}
1034	    } else {
1035		/* reset_prog_mode */
1036		if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
1037		    if (sp) {
1038			if (sp->_keypad_on)
1039			    _nc_keypad(sp, TRUE);
1040		    }
1041		    if (!CON.buffered) {
1042			set_scrollback(FALSE, &CON.SBI);
1043		    }
1044		    code = OK;
1045		}
1046	    }
1047	    T(("... buffered:%d, clear:%d", CON.buffered, CurScreen(sp)->_clear));
1048	} else {		/* shell mode */
1049	    if (defFlag) {
1050		/* def_shell_mode */
1051		if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
1052		    code = OK;
1053		}
1054	    } else {
1055		/* reset_shell_mode */
1056		if (sp) {
1057		    _nc_keypad(sp, FALSE);
1058		    NCURSES_SP_NAME(_nc_flush) (sp);
1059		}
1060		code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb));
1061		if (!CON.buffered) {
1062		    set_scrollback(TRUE, &CON.save_SBI);
1063		    if (!restore_original_screen())
1064			code = ERR;
1065		}
1066		SetConsoleCursorInfo(CON.hdl, &CON.save_CI);
1067	    }
1068	}
1069
1070    }
1071    returnCode(code);
1072}
1073
1074static void
1075wcon_screen_init(SCREEN *sp GCC_UNUSED)
1076{
1077}
1078
1079static void
1080wcon_wrap(SCREEN *sp GCC_UNUSED)
1081{
1082}
1083
1084static int
1085rkeycompare(const void *el1, const void *el2)
1086{
1087    WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
1088    WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
1089
1090    return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
1091}
1092
1093static int
1094keycompare(const void *el1, const void *el2)
1095{
1096    WORD key1 = HIWORD((*((const LONG *) el1)));
1097    WORD key2 = HIWORD((*((const LONG *) el2)));
1098
1099    return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
1100}
1101
1102static int
1103MapKey(WORD vKey)
1104{
1105    WORD nKey = 0;
1106    void *res;
1107    LONG key = GenMap(vKey, 0);
1108    int code = -1;
1109
1110    res = bsearch(&key,
1111		  CON.map,
1112		  (size_t) (N_INI + FKEYS),
1113		  sizeof(keylist[0]),
1114		  keycompare);
1115    if (res) {
1116	key = *((LONG *) res);
1117	nKey = LOWORD(key);
1118	code = (int) (nKey & 0x7fff);
1119	if (nKey & 0x8000)
1120	    code = -code;
1121    }
1122    return code;
1123}
1124
1125static int
1126AnsiKey(WORD vKey)
1127{
1128    WORD nKey = 0;
1129    void *res;
1130    LONG key = GenMap(vKey, 0);
1131    int code = -1;
1132
1133    res = bsearch(&key,
1134		  CON.ansi_map,
1135		  (size_t) (N_INI + FKEYS),
1136		  sizeof(keylist[0]),
1137		  keycompare);
1138    if (res) {
1139	key = *((LONG *) res);
1140	nKey = LOWORD(key);
1141	code = (int) (nKey & 0x7fff);
1142	if (nKey & 0x8000)
1143	    code = -code;
1144    }
1145    return code;
1146}
1147
1148static void
1149wcon_release(TERMINAL_CONTROL_BLOCK * TCB)
1150{
1151    T((T_CALLED("win32con::wcon_release(%p)"), TCB));
1152
1153    AssertTCB();
1154    if (TCB->prop)
1155	free(TCB->prop);
1156
1157    returnVoid;
1158}
1159
1160static bool
1161read_screen_data(void)
1162{
1163    bool result = FALSE;
1164    COORD bufferCoord;
1165    size_t want;
1166
1167    CON.save_size.X = (SHORT) (CON.save_region.Right
1168			       - CON.save_region.Left + 1);
1169    CON.save_size.Y = (SHORT) (CON.save_region.Bottom
1170			       - CON.save_region.Top + 1);
1171
1172    want = (size_t) (CON.save_size.X * CON.save_size.Y);
1173
1174    if ((CON.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
1175	bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0);
1176	bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0);
1177
1178	T(("... reading console %s %dx%d into %d,%d - %d,%d at %d,%d",
1179	   CON.window_only ? "window" : "buffer",
1180	   CON.save_size.Y, CON.save_size.X,
1181	   CON.save_region.Top,
1182	   CON.save_region.Left,
1183	   CON.save_region.Bottom,
1184	   CON.save_region.Right,
1185	   bufferCoord.Y,
1186	   bufferCoord.X));
1187
1188	if (read_screen(CON.hdl,
1189			CON.save_screen,
1190			CON.save_size,
1191			bufferCoord,
1192			&CON.save_region)) {
1193	    result = TRUE;
1194	} else {
1195	    T((" error %#lx", (unsigned long) GetLastError()));
1196	    FreeAndNull(CON.save_screen);
1197	}
1198    }
1199
1200    return result;
1201}
1202
1203/*
1204 * Attempt to save the screen contents.  PDCurses does this if
1205 * PDC_RESTORE_SCREEN is set, giving the same visual appearance on
1206 * restoration as if the library had allocated a console buffer.  MSDN
1207 * says that the data which can be read is limited to 64Kb (and may be
1208 * less).
1209 */
1210static bool
1211save_original_screen(void)
1212{
1213    bool result = FALSE;
1214
1215    CON.save_region.Top = 0;
1216    CON.save_region.Left = 0;
1217    CON.save_region.Bottom = (SHORT) (CON.SBI.dwSize.Y - 1);
1218    CON.save_region.Right = (SHORT) (CON.SBI.dwSize.X - 1);
1219
1220    if (read_screen_data()) {
1221	result = TRUE;
1222    } else {
1223
1224	CON.save_region.Top = CON.SBI.srWindow.Top;
1225	CON.save_region.Left = CON.SBI.srWindow.Left;
1226	CON.save_region.Bottom = CON.SBI.srWindow.Bottom;
1227	CON.save_region.Right = CON.SBI.srWindow.Right;
1228
1229	CON.window_only = TRUE;
1230
1231	if (read_screen_data()) {
1232	    result = TRUE;
1233	}
1234    }
1235
1236    T(("... save original screen contents %s", result ? "ok" : "err"));
1237    return result;
1238}
1239
1240static void
1241wcon_init(TERMINAL_CONTROL_BLOCK * TCB)
1242{
1243    T((T_CALLED("win32con::wcon_init(%p)"), TCB));
1244
1245    AssertTCB();
1246
1247    if (TCB) {
1248	if (!InitConsole()) {
1249	    returnVoid;
1250	}
1251
1252	TCB->info.initcolor = TRUE;
1253	TCB->info.canchange = FALSE;
1254	TCB->info.hascolor = TRUE;
1255	TCB->info.caninit = TRUE;
1256
1257	TCB->info.maxpairs = NUMPAIRS;
1258	TCB->info.maxcolors = 8;
1259	TCB->info.numlabels = 0;
1260	TCB->info.labelwidth = 0;
1261	TCB->info.labelheight = 0;
1262	TCB->info.nocolorvideo = 1;
1263	TCB->info.tabsize = 8;
1264
1265	TCB->info.numbuttons = CON.numButtons;
1266	TCB->info.defaultPalette = _nc_cga_palette;
1267
1268    }
1269    returnVoid;
1270}
1271
1272static void
1273wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB,
1274	      int pair,
1275	      int f,
1276	      int b)
1277{
1278    SCREEN *sp;
1279
1280    if (okConsoleHandle(TCB)) {
1281	SetSP();
1282
1283	if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
1284	    && (b >= 0) && (b < 8)) {
1285	    CON.pairs[pair] = MapColor(true, f) | MapColor(false, b);
1286	}
1287    }
1288}
1289
1290static void
1291wcon_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
1292	       int color GCC_UNUSED,
1293	       int r GCC_UNUSED,
1294	       int g GCC_UNUSED,
1295	       int b GCC_UNUSED)
1296{
1297    SCREEN *sp;
1298
1299    AssertTCB();
1300    SetSP();
1301}
1302
1303static void
1304wcon_do_color(TERMINAL_CONTROL_BLOCK * TCB,
1305	      int old_pair GCC_UNUSED,
1306	      int pair GCC_UNUSED,
1307	      int reverse GCC_UNUSED,
1308	      int (*outc) (SCREEN *, int) GCC_UNUSED
1309)
1310{
1311    SCREEN *sp;
1312
1313    AssertTCB();
1314    SetSP();
1315}
1316
1317static void
1318wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
1319{
1320    SCREEN *sp;
1321
1322    if (okConsoleHandle(TCB)) {
1323	SetSP();
1324
1325	sp->_mouse_type = M_TERM_DRIVER;
1326    }
1327}
1328
1329static int
1330wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB,
1331	       int delay
1332	       EVENTLIST_2nd(_nc_eventlist * evl))
1333{
1334    int rc = 0;
1335    SCREEN *sp;
1336
1337    if (okConsoleHandle(TCB)) {
1338	SetSP();
1339
1340	if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1341	    rc = TW_MOUSE;
1342	} else {
1343	    rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
1344					  TWAIT_MASK,
1345					  delay,
1346					  (int *) 0
1347					  EVENTLIST_2nd(evl));
1348	}
1349    }
1350
1351    return rc;
1352}
1353
1354static int
1355wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
1356	   int yold GCC_UNUSED, int xold GCC_UNUSED,
1357	   int y, int x)
1358{
1359    int ret = ERR;
1360    if (okConsoleHandle(TCB)) {
1361	COORD loc;
1362	loc.X = (short) x;
1363	loc.Y = (short) (y + AdjustY());
1364	SetConsoleCursorPosition(CON.hdl, loc);
1365	ret = OK;
1366    }
1367    return ret;
1368}
1369
1370static void
1371wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
1372	     int labnum GCC_UNUSED,
1373	     char *text GCC_UNUSED)
1374{
1375    SCREEN *sp;
1376
1377    AssertTCB();
1378    SetSP();
1379}
1380
1381static void
1382wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1383		  int OnFlag GCC_UNUSED)
1384{
1385    SCREEN *sp;
1386
1387    AssertTCB();
1388    SetSP();
1389}
1390
1391static chtype
1392wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1393{
1394    chtype res = A_NORMAL;
1395    res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1396    return res;
1397}
1398
1399static void
1400wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1401{
1402    SCREEN *sp;
1403
1404    AssertTCB();
1405    SetSP();
1406}
1407
1408static void
1409wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1410	     chtype *real_map GCC_UNUSED,
1411	     chtype *fake_map GCC_UNUSED)
1412{
1413#define DATA(a,b) { a, b }
1414    static struct {
1415	int acs_code;
1416	int use_code;
1417    } table[] = {
1418	DATA('a', 0xb1),	/* ACS_CKBOARD  */
1419	    DATA('f', 0xf8),	/* ACS_DEGREE   */
1420	    DATA('g', 0xf1),	/* ACS_PLMINUS  */
1421	    DATA('j', 0xd9),	/* ACS_LRCORNER */
1422	    DATA('l', 0xda),	/* ACS_ULCORNER */
1423	    DATA('k', 0xbf),	/* ACS_URCORNER */
1424	    DATA('m', 0xc0),	/* ACS_LLCORNER */
1425	    DATA('n', 0xc5),	/* ACS_PLUS     */
1426	    DATA('q', 0xc4),	/* ACS_HLINE    */
1427	    DATA('t', 0xc3),	/* ACS_LTEE     */
1428	    DATA('u', 0xb4),	/* ACS_RTEE     */
1429	    DATA('v', 0xc1),	/* ACS_BTEE     */
1430	    DATA('w', 0xc2),	/* ACS_TTEE     */
1431	    DATA('x', 0xb3),	/* ACS_VLINE    */
1432	    DATA('y', 0xf3),	/* ACS_LEQUAL   */
1433	    DATA('z', 0xf2),	/* ACS_GEQUAL   */
1434	    DATA('0', 0xdb),	/* ACS_BLOCK    */
1435	    DATA('{', 0xe3),	/* ACS_PI       */
1436	    DATA('}', 0x9c),	/* ACS_STERLING */
1437	    DATA(',', 0xae),	/* ACS_LARROW   */
1438	    DATA('+', 0xaf),	/* ACS_RARROW   */
1439	    DATA('~', 0xf9),	/* ACS_BULLET   */
1440    };
1441#undef DATA
1442    unsigned n;
1443
1444    SCREEN *sp;
1445    if (okConsoleHandle(TCB)) {
1446	SetSP();
1447
1448	for (n = 0; n < SIZEOF(table); ++n) {
1449	    real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET;
1450	    if (sp != 0)
1451		sp->_screen_acs_map[table[n].acs_code] = TRUE;
1452	}
1453    }
1454}
1455
1456static ULONGLONG
1457tdiff(FILETIME fstart, FILETIME fend)
1458{
1459    ULARGE_INTEGER ustart;
1460    ULARGE_INTEGER uend;
1461    ULONGLONG diff;
1462
1463    ustart.LowPart = fstart.dwLowDateTime;
1464    ustart.HighPart = fstart.dwHighDateTime;
1465    uend.LowPart = fend.dwLowDateTime;
1466    uend.HighPart = fend.dwHighDateTime;
1467
1468    diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1469    return diff;
1470}
1471
1472static int
1473Adjust(int milliseconds, int diff)
1474{
1475    if (milliseconds != INFINITY) {
1476	milliseconds -= diff;
1477	if (milliseconds < 0)
1478	    milliseconds = 0;
1479    }
1480    return milliseconds;
1481}
1482
1483#define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1484		     FROM_LEFT_2ND_BUTTON_PRESSED | \
1485		     FROM_LEFT_3RD_BUTTON_PRESSED | \
1486		     FROM_LEFT_4TH_BUTTON_PRESSED | \
1487		     RIGHTMOST_BUTTON_PRESSED)
1488
1489static mmask_t
1490decode_mouse(SCREEN *sp, int mask)
1491{
1492    mmask_t result = 0;
1493
1494    (void) sp;
1495    assert(sp && console_initialized);
1496
1497    if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1498	result |= BUTTON1_PRESSED;
1499    if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1500	result |= BUTTON2_PRESSED;
1501    if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1502	result |= BUTTON3_PRESSED;
1503    if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1504	result |= BUTTON4_PRESSED;
1505
1506    if (mask & RIGHTMOST_BUTTON_PRESSED) {
1507	switch (CON.numButtons) {
1508	case 1:
1509	    result |= BUTTON1_PRESSED;
1510	    break;
1511	case 2:
1512	    result |= BUTTON2_PRESSED;
1513	    break;
1514	case 3:
1515	    result |= BUTTON3_PRESSED;
1516	    break;
1517	case 4:
1518	    result |= BUTTON4_PRESSED;
1519	    break;
1520	}
1521    }
1522
1523    return result;
1524}
1525
1526static int
1527console_twait(
1528		 SCREEN *sp,
1529		 HANDLE fd,
1530		 int mode,
1531		 int milliseconds,
1532		 int *timeleft
1533		 EVENTLIST_2nd(_nc_eventlist * evl))
1534{
1535    INPUT_RECORD inp_rec;
1536    BOOL b;
1537    DWORD nRead = 0, rc = (DWORD) (-1);
1538    int code = 0;
1539    FILETIME fstart;
1540    FILETIME fend;
1541    int diff;
1542    bool isImmed = (milliseconds == 0);
1543
1544#ifdef NCURSES_WGETCH_EVENTS
1545    (void) evl;			/* TODO: implement wgetch-events */
1546#endif
1547
1548#define CONSUME() ReadConsoleInput(fd,&inp_rec,1,&nRead)
1549
1550    assert(sp);
1551
1552    TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1553		      milliseconds, mode));
1554
1555    if (milliseconds < 0)
1556	milliseconds = INFINITY;
1557
1558    memset(&inp_rec, 0, sizeof(inp_rec));
1559
1560    while (true) {
1561	GetSystemTimeAsFileTime(&fstart);
1562	rc = WaitForSingleObject(fd, (DWORD) milliseconds);
1563	GetSystemTimeAsFileTime(&fend);
1564	diff = (int) tdiff(fstart, fend);
1565	milliseconds = Adjust(milliseconds, diff);
1566
1567	if (!isImmed && milliseconds <= 0)
1568	    break;
1569
1570	if (rc == WAIT_OBJECT_0) {
1571	    if (mode) {
1572		b = GetNumberOfConsoleInputEvents(fd, &nRead);
1573		if (b && nRead > 0) {
1574		    b = PeekConsoleInput(fd, &inp_rec, 1, &nRead);
1575		    if (b && nRead > 0) {
1576			switch (inp_rec.EventType) {
1577			case KEY_EVENT:
1578			    if (mode & TW_INPUT) {
1579				WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1580				char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1581
1582				if (inp_rec.Event.KeyEvent.bKeyDown) {
1583				    if (0 == ch) {
1584					int nKey = MapKey(vk);
1585					if (nKey < 0) {
1586					    CONSUME();
1587					    continue;
1588					}
1589				    }
1590				    code = TW_INPUT;
1591				    goto end;
1592				} else {
1593				    CONSUME();
1594				}
1595			    }
1596			    continue;
1597			case MOUSE_EVENT:
1598			    if (decode_mouse(sp,
1599					     (inp_rec.Event.MouseEvent.dwButtonState
1600					      & BUTTON_MASK)) == 0) {
1601				CONSUME();
1602			    } else if (mode & TW_MOUSE) {
1603				code = TW_MOUSE;
1604				goto end;
1605			    }
1606			    continue;
1607			    /* e.g., FOCUS_EVENT */
1608			default:
1609			    CONSUME();
1610			    selectActiveHandle();
1611			    continue;
1612			}
1613		    }
1614		}
1615	    }
1616	    continue;
1617	} else {
1618	    if (rc != WAIT_TIMEOUT) {
1619		code = -1;
1620		break;
1621	    } else {
1622		code = 0;
1623		break;
1624	    }
1625	}
1626    }
1627  end:
1628
1629    TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1630		      code, errno, milliseconds));
1631
1632    if (timeleft)
1633	*timeleft = milliseconds;
1634
1635    return code;
1636}
1637
1638static int
1639wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,
1640	   int mode,
1641	   int milliseconds,
1642	   int *timeleft
1643	   EVENTLIST_2nd(_nc_eventlist * evl))
1644{
1645    SCREEN *sp;
1646    int code = 0;
1647
1648    if (okConsoleHandle(TCB)) {
1649	SetSP();
1650
1651	code = console_twait(sp,
1652			     CON.inp,
1653			     mode,
1654			     milliseconds,
1655			     timeleft EVENTLIST_2nd(evl));
1656    }
1657    return code;
1658}
1659
1660static bool
1661handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer)
1662{
1663    MEVENT work;
1664    bool result = FALSE;
1665
1666    assert(sp);
1667
1668    sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1669    sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1670
1671    /*
1672     * We're only interested if the button is pressed or released.
1673     * FIXME: implement continuous event-tracking.
1674     */
1675    if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1676
1677	memset(&work, 0, sizeof(work));
1678
1679	if (sp->_drv_mouse_new_buttons) {
1680
1681	    work.bstate |= decode_mouse(sp, sp->_drv_mouse_new_buttons);
1682
1683	} else {
1684
1685	    /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1686	    work.bstate |= (decode_mouse(sp,
1687					 sp->_drv_mouse_old_buttons)
1688			    >> 1);
1689
1690	    result = TRUE;
1691	}
1692
1693	work.x = mer.dwMousePosition.X;
1694	work.y = mer.dwMousePosition.Y - AdjustY();
1695
1696	sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1697	sp->_drv_mouse_tail += 1;
1698    }
1699
1700    return result;
1701}
1702
1703static int
1704wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1705{
1706    SCREEN *sp;
1707    int n = -1;
1708
1709    T((T_CALLED("win32con::wcon_read(%p)"), TCB));
1710
1711    assert(buf);
1712    if (okConsoleHandle(TCB)) {
1713	SetSP();
1714
1715	n = _nc_mingw_console_read(sp, CON.inp, buf);
1716    }
1717    returnCode(n);
1718}
1719
1720static int
1721wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1722{
1723    T((T_CALLED("win32con::wcon_nap(%p, %d)"), TCB, ms));
1724    Sleep((DWORD) ms);
1725    returnCode(OK);
1726}
1727
1728static int
1729wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int mode)
1730{
1731    int res = -1;
1732
1733    T((T_CALLED("win32con:wcon_cursorSet(%d)"), mode));
1734    if (okConsoleHandle(TCB)) {
1735	CONSOLE_CURSOR_INFO this_CI = CON.save_CI;
1736	switch (mode) {
1737	case 0:
1738	    this_CI.bVisible = FALSE;
1739	    break;
1740	case 1:
1741	    break;
1742	case 2:
1743	    this_CI.dwSize = 100;
1744	    break;
1745	}
1746	SetConsoleCursorInfo(CON.hdl, &this_CI);
1747    }
1748    returnCode(res);
1749}
1750
1751static bool
1752wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int keycode)
1753{
1754    WORD nKey;
1755    void *res;
1756    bool found = FALSE;
1757    LONG key = GenMap(0, (WORD) keycode);
1758
1759    T((T_CALLED("win32con::wcon_kyExist(%d)"), keycode));
1760    res = bsearch(&key,
1761		  CON.rmap,
1762		  (size_t) (N_INI + FKEYS),
1763		  sizeof(keylist[0]),
1764		  rkeycompare);
1765    if (res) {
1766	key = *((LONG *) res);
1767	nKey = LOWORD(key);
1768	if (!(nKey & 0x8000))
1769	    found = TRUE;
1770    }
1771    returnCode(found);
1772}
1773
1774static int
1775wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1776{
1777    SCREEN *sp;
1778    int code = ERR;
1779
1780    T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag));
1781
1782    if (okConsoleHandle(TCB)) {
1783	SetSP();
1784
1785	if (sp) {
1786	    code = OK;
1787	}
1788    }
1789    returnCode(code);
1790}
1791
1792static int
1793wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB,
1794	   int keycode,
1795	   int flag)
1796{
1797    int code = ERR;
1798    SCREEN *sp;
1799    WORD nKey;
1800    WORD vKey;
1801    void *res;
1802    LONG key = GenMap(0, (WORD) keycode);
1803
1804    T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag));
1805
1806    if (okConsoleHandle(TCB)) {
1807	SetSP();
1808
1809	if (sp) {
1810	    res = bsearch(&key,
1811			  CON.rmap,
1812			  (size_t) (N_INI + FKEYS),
1813			  sizeof(keylist[0]),
1814			  rkeycompare);
1815	    if (res) {
1816		key = *((LONG *) res);
1817		vKey = HIWORD(key);
1818		nKey = (LOWORD(key)) & 0x7fff;
1819		if (!flag)
1820		    nKey |= 0x8000;
1821		*(LONG *) res = GenMap(vKey, nKey);
1822	    }
1823	}
1824    }
1825    returnCode(code);
1826}
1827
1828NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1829    FALSE,
1830	wcon_name,		/* Name */
1831	wcon_CanHandle,		/* CanHandle */
1832	wcon_init,		/* init */
1833	wcon_release,		/* release */
1834	wcon_size,		/* size */
1835	wcon_sgmode,		/* sgmode */
1836	wcon_conattr,		/* conattr */
1837	wcon_mvcur,		/* hwcur */
1838	wcon_mode,		/* mode */
1839	wcon_rescol,		/* rescol */
1840	wcon_rescolors,		/* rescolors */
1841	wcon_setcolor,		/* color */
1842	wcon_dobeepflash,	/* DoBeepFlash */
1843	wcon_initpair,		/* initpair */
1844	wcon_initcolor,		/* initcolor */
1845	wcon_do_color,		/* docolor */
1846	wcon_initmouse,		/* initmouse */
1847	wcon_testmouse,		/* testmouse */
1848	wcon_setfilter,		/* setfilter */
1849	wcon_hwlabel,		/* hwlabel */
1850	wcon_hwlabelOnOff,	/* hwlabelOnOff */
1851	wcon_doupdate,		/* update */
1852	wcon_defaultcolors,	/* defaultcolors */
1853	wcon_print,		/* print */
1854	wcon_size,		/* getsize */
1855	wcon_setsize,		/* setsize */
1856	wcon_initacs,		/* initacs */
1857	wcon_screen_init,	/* scinit */
1858	wcon_wrap,		/* scexit */
1859	wcon_twait,		/* twait */
1860	wcon_read,		/* read */
1861	wcon_nap,		/* nap */
1862	wcon_kpad,		/* kpad */
1863	wcon_keyok,		/* kyOk */
1864	wcon_kyExist,		/* kyExist */
1865	wcon_cursorSet		/* cursorSet */
1866};
1867
1868/* --------------------------------------------------------- */
1869
1870static HANDLE
1871get_handle(int fd)
1872{
1873    intptr_t value = _get_osfhandle(fd);
1874    return (HANDLE) value;
1875}
1876
1877#if WINVER >= 0x0600
1878/*   This function tests, whether or not the ncurses application
1879     is running as a descendant of MSYS2/cygwin mintty terminal
1880     application. mintty doesn't use Windows Console for its screen
1881     I/O, so the native Windows _isatty doesn't recognize it as
1882     character device. But we can discover we are at the end of an
1883     Pipe and can query to server side of the pipe, looking whether
1884     or not this is mintty.
1885 */
1886static int
1887_ismintty(int fd, LPHANDLE pMinTTY)
1888{
1889    HANDLE handle = get_handle(fd);
1890    DWORD dw;
1891    int code = 0;
1892
1893    T((T_CALLED("win32con::_ismintty(%d, %p)"), fd, pMinTTY));
1894
1895    if (handle != INVALID_HANDLE_VALUE) {
1896	dw = GetFileType(handle);
1897	if (dw == FILE_TYPE_PIPE) {
1898	    if (GetNamedPipeInfo(handle, 0, 0, 0, 0)) {
1899		ULONG pPid;
1900		/* Requires NT6 */
1901		if (GetNamedPipeServerProcessId(handle, &pPid)) {
1902		    TCHAR buf[MAX_PATH];
1903		    DWORD len = 0;
1904		    /* These security attributes may allow us to
1905		       create a remote thread in mintty to manipulate
1906		       the terminal state remotely */
1907		    HANDLE pHandle = OpenProcess(
1908						    PROCESS_CREATE_THREAD
1909						    | PROCESS_QUERY_INFORMATION
1910						    | PROCESS_VM_OPERATION
1911						    | PROCESS_VM_WRITE
1912						    | PROCESS_VM_READ,
1913						    FALSE,
1914						    pPid);
1915		    if (pMinTTY)
1916			*pMinTTY = INVALID_HANDLE_VALUE;
1917		    if (pHandle != INVALID_HANDLE_VALUE) {
1918			if ((len = GetProcessImageFileName(
1919							      pHandle,
1920							      buf,
1921							      (DWORD)
1922							      array_length(buf)))) {
1923			    TCHAR *pos = _tcsrchr(buf, _T('\\'));
1924			    if (pos) {
1925				pos++;
1926				if (_tcsnicmp(pos, _TEXT("mintty.exe"), 10)
1927				    == 0) {
1928				    if (pMinTTY)
1929					*pMinTTY = pHandle;
1930				    code = 1;
1931				}
1932			    }
1933			}
1934		    }
1935		}
1936	    }
1937	}
1938    }
1939    returnCode(code);
1940}
1941#endif
1942
1943/*   Borrowed from ansicon project.
1944     Check whether or not an I/O handle is associated with
1945     a Windows console.
1946*/
1947static BOOL
1948IsConsoleHandle(HANDLE hdl)
1949{
1950    DWORD dwFlag = 0;
1951    BOOL result;
1952
1953    if (!GetConsoleMode(hdl, &dwFlag)) {
1954	result = (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL);
1955    } else {
1956	result = (int) (dwFlag & ENABLE_PROCESSED_OUTPUT);
1957    }
1958    return result;
1959}
1960
1961/*   Our replacement for the systems _isatty to include also
1962     a test for mintty. This is called from the NC_ISATTY macro
1963     defined in curses.priv.h
1964 */
1965int
1966_nc_mingw_isatty(int fd)
1967{
1968    int result = 0;
1969
1970    if (SysISATTY(fd)) {
1971	result = 1;
1972    } else {
1973#if WINVER >= 0x0600
1974	result = _ismintty(fd, NULL);
1975#endif
1976    }
1977    return result;
1978}
1979
1980/*   This is used when running in terminfo mode to discover,
1981     whether or not the "terminal" is actually a Windows
1982     Console. It is the responsibility of the console to deal
1983     with the terminal escape sequences that are sent by
1984     terminfo.
1985 */
1986int
1987_nc_mingw_isconsole(int fd)
1988{
1989    HANDLE hdl = get_handle(fd);
1990    int code = 0;
1991
1992    T((T_CALLED("win32con::_nc_mingw_isconsole(%d)"), fd));
1993
1994    code = (int) IsConsoleHandle(hdl);
1995
1996    returnCode(code);
1997}
1998
1999#define TC_PROLOGUE(fd) \
2000    SCREEN *sp;                                               \
2001    TERMINAL *term = 0;                                       \
2002    int code = ERR;                                           \
2003    if (_nc_screen_chain == 0)                                \
2004        return 0;                                             \
2005    for (each_screen(sp)) {                                   \
2006        if (sp->_term && (sp->_term->Filedes == fd)) {        \
2007            term = sp->_term;                                 \
2008            break;                                            \
2009        }                                                     \
2010    }                                                         \
2011    assert(term != 0)
2012
2013int
2014_nc_mingw_tcsetattr(
2015		       int fd,
2016		       int optional_action GCC_UNUSED,
2017		       const struct termios *arg)
2018{
2019    TC_PROLOGUE(fd);
2020
2021    if (_nc_mingw_isconsole(fd)) {
2022	DWORD dwFlag = 0;
2023	HANDLE ofd = get_handle(fd);
2024	if (ofd != INVALID_HANDLE_VALUE) {
2025	    if (arg) {
2026		if (arg->c_lflag & ICANON)
2027		    dwFlag |= ENABLE_LINE_INPUT;
2028		else
2029		    dwFlag = dwFlag & (DWORD) (~ENABLE_LINE_INPUT);
2030
2031		if (arg->c_lflag & ECHO)
2032		    dwFlag = dwFlag | ENABLE_ECHO_INPUT;
2033		else
2034		    dwFlag = dwFlag & (DWORD) (~ENABLE_ECHO_INPUT);
2035
2036		if (arg->c_iflag & BRKINT)
2037		    dwFlag |= ENABLE_PROCESSED_INPUT;
2038		else
2039		    dwFlag = dwFlag & (DWORD) (~ENABLE_PROCESSED_INPUT);
2040	    }
2041	    dwFlag |= ENABLE_MOUSE_INPUT;
2042	    SetConsoleMode(ofd, dwFlag);
2043	    code = OK;
2044	}
2045    }
2046    if (arg)
2047	term->Nttyb = *arg;
2048
2049    return code;
2050}
2051
2052int
2053_nc_mingw_tcgetattr(int fd, struct termios *arg)
2054{
2055    TC_PROLOGUE(fd);
2056
2057    if (_nc_mingw_isconsole(fd)) {
2058	if (arg)
2059	    *arg = term->Nttyb;
2060    }
2061    return code;
2062}
2063
2064int
2065_nc_mingw_tcflush(int fd, int queue)
2066{
2067    TC_PROLOGUE(fd);
2068    (void) term;
2069
2070    if (_nc_mingw_isconsole(fd)) {
2071	if (queue == TCIFLUSH) {
2072	    BOOL b = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
2073	    if (!b)
2074		return (int) GetLastError();
2075	}
2076    }
2077    return code;
2078}
2079
2080int
2081_nc_mingw_testmouse(
2082		       SCREEN *sp,
2083		       HANDLE fd,
2084		       int delay
2085		       EVENTLIST_2nd(_nc_eventlist * evl))
2086{
2087    int rc = 0;
2088
2089    assert(sp);
2090
2091    if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
2092	rc = TW_MOUSE;
2093    } else {
2094	rc = console_twait(sp,
2095			   fd,
2096			   TWAIT_MASK,
2097			   delay,
2098			   (int *) 0
2099			   EVENTLIST_2nd(evl));
2100    }
2101    return rc;
2102}
2103
2104int
2105_nc_mingw_console_read(
2106			  SCREEN *sp,
2107			  HANDLE fd,
2108			  int *buf)
2109{
2110    int rc = -1;
2111    INPUT_RECORD inp_rec;
2112    BOOL b;
2113    DWORD nRead;
2114    WORD vk;
2115
2116    assert(sp);
2117    assert(buf);
2118
2119    memset(&inp_rec, 0, sizeof(inp_rec));
2120
2121    T((T_CALLED("_nc_mingw_console_read(%p)"), sp));
2122
2123    while ((b = ReadConsoleInput(fd, &inp_rec, 1, &nRead))) {
2124	if (b && nRead > 0) {
2125	    if (rc < 0)
2126		rc = 0;
2127	    rc = rc + (int) nRead;
2128	    if (inp_rec.EventType == KEY_EVENT) {
2129		if (!inp_rec.Event.KeyEvent.bKeyDown)
2130		    continue;
2131		*buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
2132		vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
2133		/*
2134		 * There are 24 virtual function-keys (defined in winuser.h),
2135		 * and typically 12 function-keys on a keyboard.  Use the
2136		 * shift-modifier to provide the remaining keys.
2137		 */
2138		if (vk >= VK_F1 && vk <= VK_F12) {
2139		    if (inp_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) {
2140			vk = (WORD) (vk + 12);
2141		    }
2142		}
2143		if (*buf == 0) {
2144		    int key = MapKey(vk);
2145		    if (key < 0)
2146			continue;
2147		    if (sp->_keypad_on) {
2148			*buf = key;
2149		    } else {
2150			ungetch('\0');
2151			*buf = AnsiKey(vk);
2152		    }
2153		} else if (vk == VK_BACK) {
2154		    if (!(inp_rec.Event.KeyEvent.dwControlKeyState
2155			  & (SHIFT_PRESSED | CONTROL_PRESSED))) {
2156			*buf = KEY_BACKSPACE;
2157		    }
2158		}
2159		break;
2160	    } else if (inp_rec.EventType == MOUSE_EVENT) {
2161		if (handle_mouse(sp,
2162				 inp_rec.Event.MouseEvent)) {
2163		    *buf = KEY_MOUSE;
2164		    break;
2165		}
2166	    }
2167	    continue;
2168	}
2169    }
2170    returnCode(rc);
2171}
2172
2173static bool
2174InitConsole(void)
2175{
2176    /* initialize once, or not at all */
2177    if (!console_initialized) {
2178	int i;
2179	DWORD num_buttons;
2180	WORD a;
2181	BOOL buffered = TRUE;
2182	BOOL b;
2183
2184	START_TRACE();
2185
2186	for (i = 0; i < (N_INI + FKEYS); i++) {
2187	    if (i < N_INI) {
2188		CON.rmap[i] = CON.map[i] =
2189		    (DWORD) keylist[i];
2190		CON.ansi_map[i] = (DWORD) ansi_keys[i];
2191	    } else {
2192		CON.rmap[i] = CON.map[i] =
2193		    (DWORD) GenMap((VK_F1 + (i - N_INI)),
2194				   (KEY_F(1) + (i - N_INI)));
2195		CON.ansi_map[i] =
2196		    (DWORD) GenMap((VK_F1 + (i - N_INI)),
2197				   (';' + (i - N_INI)));
2198	    }
2199	}
2200	qsort(CON.ansi_map,
2201	      (size_t) (MAPSIZE),
2202	      sizeof(keylist[0]),
2203	      keycompare);
2204	qsort(CON.map,
2205	      (size_t) (MAPSIZE),
2206	      sizeof(keylist[0]),
2207	      keycompare);
2208	qsort(CON.rmap,
2209	      (size_t) (MAPSIZE),
2210	      sizeof(keylist[0]),
2211	      rkeycompare);
2212
2213	if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
2214	    CON.numButtons = (int) num_buttons;
2215	} else {
2216	    CON.numButtons = 1;
2217	}
2218
2219	a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
2220	for (i = 0; i < NUMPAIRS; i++)
2221	    CON.pairs[i] = a;
2222
2223	b = AllocConsole();
2224
2225	if (!b)
2226	    b = AttachConsole(ATTACH_PARENT_PROCESS);
2227
2228	CON.inp = GetDirectHandle("CONIN$", FILE_SHARE_READ);
2229	CON.out = GetDirectHandle("CONOUT$", FILE_SHARE_WRITE);
2230
2231	if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
2232	    T(("... will not buffer console"));
2233	    buffered = FALSE;
2234	    CON.hdl = CON.out;
2235	} else {
2236	    T(("... creating console buffer"));
2237	    CON.hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
2238						FILE_SHARE_READ | FILE_SHARE_WRITE,
2239						NULL,
2240						CONSOLE_TEXTMODE_BUFFER,
2241						NULL);
2242	}
2243
2244	if (CON.hdl != INVALID_HANDLE_VALUE) {
2245	    CON.buffered = buffered;
2246	    get_SBI();
2247	    CON.save_SBI = CON.SBI;
2248	    if (!buffered) {
2249		save_original_screen();
2250		set_scrollback(FALSE, &CON.SBI);
2251	    }
2252	    GetConsoleCursorInfo(CON.hdl, &CON.save_CI);
2253	    T(("... initial cursor is %svisible, %d%%",
2254	       (CON.save_CI.bVisible ? "" : "not-"),
2255	       (int) CON.save_CI.dwSize));
2256	}
2257
2258	console_initialized = TRUE;
2259    }
2260    return (CON.hdl != INVALID_HANDLE_VALUE);
2261}
2262
2263static bool
2264okConsoleHandle(TERMINAL_CONTROL_BLOCK * TCB)
2265{
2266    return ((TCB != 0) &&
2267	    (TCB->magic == WINMAGIC) &&
2268	    InitConsole());
2269}
2270
2271/*
2272 * While a constructor would ensure that this module is initialized, that will
2273 * interfere with applications that may combine this with GUI interfaces.
2274 */
2275#if 0
2276static
2277__attribute__((constructor))
2278     void _enter_console(void)
2279{
2280    (void) InitConsole();
2281}
2282#endif
2283