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
58MODULE_ID("$Id: win_driver.c,v 1.66 2020/03/01 00:18:49 tom Exp $")
59
60#define TypeAlloca(type,count) (type*) _alloca(sizeof(type) * (size_t) (count))
61
62#define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
63
64#define EXP_OPTIMIZE 0
65
66#define array_length(a) (sizeof(a)/sizeof(a[0]))
67
68static bool InitConsole(void);
69static bool okConsoleHandle(TERMINAL_CONTROL_BLOCK *);
70
71#define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC))
72#define SetSP()     assert(TCB->csp != 0); sp = TCB->csp; (void) sp
73
74#define GenMap(vKey,key) MAKELONG(key, vKey)
75
76#define AdjustY() (CON.buffered ? 0 : (int) CON.SBI.srWindow.Top)
77
78#if USE_WIDEC_SUPPORT
79#define write_screen WriteConsoleOutputW
80#define read_screen  ReadConsoleOutputW
81#else
82#define write_screen WriteConsoleOutput
83#define read_screen  ReadConsoleOutput
84#endif
85
86static const LONG keylist[] =
87{
88    GenMap(VK_PRIOR, KEY_PPAGE),
89    GenMap(VK_NEXT, KEY_NPAGE),
90    GenMap(VK_END, KEY_END),
91    GenMap(VK_HOME, KEY_HOME),
92    GenMap(VK_LEFT, KEY_LEFT),
93    GenMap(VK_UP, KEY_UP),
94    GenMap(VK_RIGHT, KEY_RIGHT),
95    GenMap(VK_DOWN, KEY_DOWN),
96    GenMap(VK_DELETE, KEY_DC),
97    GenMap(VK_INSERT, KEY_IC)
98};
99static const LONG ansi_keys[] =
100{
101    GenMap(VK_PRIOR, 'I'),
102    GenMap(VK_NEXT, 'Q'),
103    GenMap(VK_END, 'O'),
104    GenMap(VK_HOME, 'H'),
105    GenMap(VK_LEFT, 'K'),
106    GenMap(VK_UP, 'H'),
107    GenMap(VK_RIGHT, 'M'),
108    GenMap(VK_DOWN, 'P'),
109    GenMap(VK_DELETE, 'S'),
110    GenMap(VK_INSERT, 'R')
111};
112#define N_INI ((int)array_length(keylist))
113#define FKEYS 24
114#define MAPSIZE (FKEYS + N_INI)
115#define NUMPAIRS 64
116
117/*   A process can only have a single console, so it's safe
118     to maintain all the information about it in a single
119     static structure.
120 */
121static struct {
122    BOOL initialized;
123    BOOL buffered;
124    BOOL window_only;
125    BOOL progMode;
126    BOOL isMinTTY;
127    BOOL isTermInfoConsole;
128    HANDLE out;
129    HANDLE inp;
130    HANDLE hdl;
131    HANDLE lastOut;
132    int numButtons;
133    DWORD ansi_map[MAPSIZE];
134    DWORD map[MAPSIZE];
135    DWORD rmap[MAPSIZE];
136    WORD pairs[NUMPAIRS];
137    COORD origin;
138    CHAR_INFO *save_screen;
139    COORD save_size;
140    SMALL_RECT save_region;
141    CONSOLE_SCREEN_BUFFER_INFO SBI;
142    CONSOLE_SCREEN_BUFFER_INFO save_SBI;
143    CONSOLE_CURSOR_INFO save_CI;
144} CON;
145
146static BOOL console_initialized = FALSE;
147
148static WORD
149MapColor(bool fore, int color)
150{
151    static const int _cmap[] =
152    {0, 4, 2, 6, 1, 5, 3, 7};
153    int a;
154    if (color < 0 || color > 7)
155	a = fore ? 7 : 0;
156    else
157	a = _cmap[color];
158    if (!fore)
159	a = a << 4;
160    return (WORD) a;
161}
162
163#define RevAttr(attr) \
164	       (WORD) (((attr) & 0xff00) | \
165		      ((((attr) & 0x07) << 4) | \
166		       (((attr) & 0x70) >> 4)))
167
168static WORD
169MapAttr(WORD res, attr_t ch)
170{
171    if (ch & A_COLOR) {
172	int p;
173
174	p = PairNumber(ch);
175	if (p > 0 && p < NUMPAIRS) {
176	    WORD a;
177	    a = CON.pairs[p];
178	    res = (WORD) ((res & 0xff00) | a);
179	}
180    }
181
182    if (ch & A_REVERSE) {
183	res = RevAttr(res);
184    }
185
186    if (ch & A_STANDOUT) {
187	res = RevAttr(res) | BACKGROUND_INTENSITY;
188    }
189
190    if (ch & A_BOLD)
191	res |= FOREGROUND_INTENSITY;
192
193    if (ch & A_DIM)
194	res |= BACKGROUND_INTENSITY;
195
196    return res;
197}
198
199#if 0				/* def TRACE */
200static void
201dump_screen(const char *fn, int ln)
202{
203    int max_cells = (CON.SBI.dwSize.Y * (1 + CON.SBI.dwSize.X)) + 1;
204    char output[max_cells];
205    CHAR_INFO save_screen[max_cells];
206    COORD save_size;
207    SMALL_RECT save_region;
208    COORD bufferCoord;
209
210    T(("dump_screen %s@%d", fn, ln));
211
212    save_region.Top = CON.SBI.srWindow.Top;
213    save_region.Left = CON.SBI.srWindow.Left;
214    save_region.Bottom = CON.SBI.srWindow.Bottom;
215    save_region.Right = CON.SBI.srWindow.Right;
216
217    save_size.X = (SHORT) (save_region.Right - save_region.Left + 1);
218    save_size.Y = (SHORT) (save_region.Bottom - save_region.Top + 1);
219
220    bufferCoord.X = bufferCoord.Y = 0;
221
222    if (read_screen(CON.hdl,
223		    save_screen,
224		    save_size,
225		    bufferCoord,
226		    &save_region)) {
227	int i, j;
228	int ij = 0;
229	int k = 0;
230
231	for (i = save_region.Top; i <= save_region.Bottom; ++i) {
232	    for (j = save_region.Left; j <= save_region.Right; ++j) {
233		output[k++] = save_screen[ij++].Char.AsciiChar;
234	    }
235	    output[k++] = '\n';
236	}
237	output[k] = 0;
238
239	T(("DUMP: %d,%d - %d,%d",
240	   save_region.Top,
241	   save_region.Left,
242	   save_region.Bottom,
243	   save_region.Right));
244	T(("%s", output));
245    }
246}
247
248#else
249#define dump_screen(fn,ln)	/* nothing */
250#endif
251
252#if USE_WIDEC_SUPPORT
253/*
254 * TODO: support surrogate pairs
255 * TODO: support combining characters
256 * TODO: support acsc
257 * TODO: _nc_wacs should be part of sp.
258 */
259static BOOL
260con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
261{
262    int actual = 0;
263    CHAR_INFO *ci = TypeAlloca(CHAR_INFO, limit);
264    COORD loc, siz;
265    SMALL_RECT rec;
266    int i;
267    cchar_t ch;
268    SCREEN *sp;
269
270    AssertTCB();
271    SetSP();
272
273    for (i = actual = 0; i < limit; i++) {
274	ch = str[i];
275	if (isWidecExt(ch))
276	    continue;
277	ci[actual].Char.UnicodeChar = CharOf(ch);
278	ci[actual].Attributes = MapAttr(CON.SBI.wAttributes,
279					AttrOf(ch));
280	if (AttrOf(ch) & A_ALTCHARSET) {
281	    if (_nc_wacs) {
282		int which = CharOf(ch);
283		if (which > 0
284		    && which < ACS_LEN
285		    && CharOf(_nc_wacs[which]) != 0) {
286		    ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
287		} else {
288		    ci[actual].Char.UnicodeChar = ' ';
289		}
290	    }
291	}
292	++actual;
293    }
294
295    loc.X = (SHORT) 0;
296    loc.Y = (SHORT) 0;
297    siz.X = (SHORT) actual;
298    siz.Y = 1;
299
300    rec.Left = (SHORT) x;
301    rec.Top = (SHORT) (y + AdjustY());
302    rec.Right = (SHORT) (x + limit - 1);
303    rec.Bottom = rec.Top;
304
305    return write_screen(CON.hdl, ci, siz, loc, &rec);
306}
307#define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
308#else
309static BOOL
310con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
311{
312    CHAR_INFO *ci = TypeAlloca(CHAR_INFO, n);
313    COORD loc, siz;
314    SMALL_RECT rec;
315    int i;
316    chtype ch;
317    SCREEN *sp;
318
319    AssertTCB();
320    SetSP();
321
322    for (i = 0; i < n; i++) {
323	ch = str[i];
324	ci[i].Char.AsciiChar = ChCharOf(ch);
325	ci[i].Attributes = MapAttr(CON.SBI.wAttributes,
326				   ChAttrOf(ch));
327	if (ChAttrOf(ch) & A_ALTCHARSET) {
328	    if (sp->_acs_map)
329		ci[i].Char.AsciiChar =
330		ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
331	}
332    }
333
334    loc.X = (short) 0;
335    loc.Y = (short) 0;
336    siz.X = (short) n;
337    siz.Y = 1;
338
339    rec.Left = (short) x;
340    rec.Top = (short) y;
341    rec.Right = (short) (x + n - 1);
342    rec.Bottom = rec.Top;
343
344    return write_screen(CON.hdl, ci, siz, loc, &rec);
345}
346#define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
347#endif
348
349#if EXP_OPTIMIZE
350/*
351 * Comparing new/current screens, determine the last column-index for a change
352 * beginning on the given row,col position.  Unlike a serial terminal, there is
353 * no cost for "moving" the "cursor" on the line as we update it.
354 */
355static int
356find_end_of_change(SCREEN *sp, int row, int col)
357{
358    int result = col;
359    struct ldat *curdat = CurScreen(sp)->_line + row;
360    struct ldat *newdat = NewScreen(sp)->_line + row;
361
362    while (col <= newdat->lastchar) {
363#if USE_WIDEC_SUPPORT
364	if (isWidecExt(curdat->text[col]) || isWidecExt(newdat->text[col])) {
365	    result = col;
366	} else if (memcmp(&curdat->text[col],
367			  &newdat->text[col],
368			  sizeof(curdat->text[0]))) {
369	    result = col;
370	} else {
371	    break;
372	}
373#else
374	if (curdat->text[col] != newdat->text[col]) {
375	    result = col;
376	} else {
377	    break;
378	}
379#endif
380	++col;
381    }
382    return result;
383}
384
385/*
386 * Given a row,col position at the end of a change-chunk, look for the
387 * beginning of the next change-chunk.
388 */
389static int
390find_next_change(SCREEN *sp, int row, int col)
391{
392    struct ldat *curdat = CurScreen(sp)->_line + row;
393    struct ldat *newdat = NewScreen(sp)->_line + row;
394    int result = newdat->lastchar + 1;
395
396    while (++col <= newdat->lastchar) {
397#if USE_WIDEC_SUPPORT
398	if (isWidecExt(curdat->text[col]) != isWidecExt(newdat->text[col])) {
399	    result = col;
400	    break;
401	} else if (memcmp(&curdat->text[col],
402			  &newdat->text[col],
403			  sizeof(curdat->text[0]))) {
404	    result = col;
405	    break;
406	}
407#else
408	if (curdat->text[col] != newdat->text[col]) {
409	    result = col;
410	    break;
411	}
412#endif
413    }
414    return result;
415}
416
417#define EndChange(first) \
418	find_end_of_change(sp, y, first)
419#define NextChange(last) \
420	find_next_change(sp, y, last)
421
422#endif /* EXP_OPTIMIZE */
423
424#define MARK_NOCHANGE(win,row) \
425		win->_line[row].firstchar = _NOCHANGE; \
426		win->_line[row].lastchar  = _NOCHANGE
427
428static void
429selectActiveHandle(void)
430{
431    if (CON.lastOut != CON.hdl) {
432	CON.lastOut = CON.hdl;
433	SetConsoleActiveScreenBuffer(CON.lastOut);
434    }
435}
436
437static bool
438restore_original_screen(void)
439{
440    COORD bufferCoord;
441    bool result = FALSE;
442    SMALL_RECT save_region = CON.save_region;
443
444    T(("... restoring %s", CON.window_only ? "window" : "entire buffer"));
445
446    bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0);
447    bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0);
448
449    if (write_screen(CON.hdl,
450		     CON.save_screen,
451		     CON.save_size,
452		     bufferCoord,
453		     &save_region)) {
454	result = TRUE;
455	mvcur(-1, -1, LINES - 2, 0);
456	T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)",
457	   CON.save_size.Y,
458	   CON.save_size.X,
459	   save_region.Top,
460	   save_region.Left,
461	   save_region.Bottom,
462	   save_region.Right));
463    } else {
464	T(("... restore original screen contents err"));
465    }
466    return result;
467}
468
469static const char *
470wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
471{
472    (void) TCB;
473    return "win32console";
474}
475
476static int
477wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
478{
479    int result = ERR;
480    int y, nonempty, n, x0, x1, Width, Height;
481    SCREEN *sp;
482
483    T((T_CALLED("win32con::wcon_doupdate(%p)"), TCB));
484    if (okConsoleHandle(TCB)) {
485	SetSP();
486
487	Width = screen_columns(sp);
488	Height = screen_lines(sp);
489	nonempty = min(Height, NewScreen(sp)->_maxy + 1);
490
491	T(("... %dx%d clear cur:%d new:%d",
492	   Height, Width,
493	   CurScreen(sp)->_clear,
494	   NewScreen(sp)->_clear));
495
496	if (SP_PARM->_endwin == ewSuspend) {
497
498	    T(("coming back from shell mode"));
499	    NCURSES_SP_NAME(reset_prog_mode) (NCURSES_SP_ARG);
500
501	    NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG);
502	    NCURSES_SP_NAME(_nc_screen_resume) (NCURSES_SP_ARG);
503	    SP_PARM->_mouse_resume(SP_PARM);
504
505	    SP_PARM->_endwin = ewRunning;
506	}
507
508	if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
509	    int x;
510#if USE_WIDEC_SUPPORT
511	    cchar_t *empty = TypeAlloca(cchar_t, Width);
512	    wchar_t blank[2] =
513	    {
514		L' ', L'\0'
515	    };
516
517	    for (x = 0; x < Width; x++)
518		setcchar(&empty[x], blank, 0, 0, 0);
519#else
520	    chtype *empty = TypeAlloca(chtype, Width);
521
522	    for (x = 0; x < Width; x++)
523		empty[x] = ' ';
524#endif
525
526	    for (y = 0; y < nonempty; y++) {
527		con_write(TCB, y, 0, empty, Width);
528		memcpy(empty,
529		       CurScreen(sp)->_line[y].text,
530		       (size_t) Width * sizeof(empty[0]));
531	    }
532	    CurScreen(sp)->_clear = FALSE;
533	    NewScreen(sp)->_clear = FALSE;
534	    touchwin(NewScreen(sp));
535	    T(("... cleared %dx%d lines @%d of screen", nonempty, Width,
536	       AdjustY()));
537	}
538
539	for (y = 0; y < nonempty; y++) {
540	    x0 = NewScreen(sp)->_line[y].firstchar;
541	    if (x0 != _NOCHANGE) {
542#if EXP_OPTIMIZE
543		int x2;
544		int limit = NewScreen(sp)->_line[y].lastchar;
545		while ((x1 = EndChange(x0)) <= limit) {
546		    while ((x2 = NextChange(x1)) <= limit && x2 <= (x1 + 2)) {
547			x1 = x2;
548		    }
549		    n = x1 - x0 + 1;
550		    memcpy(&CurScreen(sp)->_line[y].text[x0],
551			   &NewScreen(sp)->_line[y].text[x0],
552			   n * sizeof(CurScreen(sp)->_line[y].text[x0]));
553		    con_write(TCB,
554			      y,
555			      x0,
556			      &CurScreen(sp)->_line[y].text[x0], n);
557		    x0 = NextChange(x1);
558		}
559
560		/* mark line changed successfully */
561		if (y <= NewScreen(sp)->_maxy) {
562		    MARK_NOCHANGE(NewScreen(sp), y);
563		}
564		if (y <= CurScreen(sp)->_maxy) {
565		    MARK_NOCHANGE(CurScreen(sp), y);
566		}
567#else
568		x1 = NewScreen(sp)->_line[y].lastchar;
569		n = x1 - x0 + 1;
570		if (n > 0) {
571		    memcpy(&CurScreen(sp)->_line[y].text[x0],
572			   &NewScreen(sp)->_line[y].text[x0],
573			   (size_t) n * sizeof(CurScreen(sp)->_line[y].text[x0]));
574		    con_write(TCB,
575			      y,
576			      x0,
577			      &CurScreen(sp)->_line[y].text[x0], n);
578
579		    /* mark line changed successfully */
580		    if (y <= NewScreen(sp)->_maxy) {
581			MARK_NOCHANGE(NewScreen(sp), y);
582		    }
583		    if (y <= CurScreen(sp)->_maxy) {
584			MARK_NOCHANGE(CurScreen(sp), y);
585		    }
586		}
587#endif
588	    }
589	}
590
591	/* put everything back in sync */
592	for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
593	    MARK_NOCHANGE(NewScreen(sp), y);
594	}
595	for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
596	    MARK_NOCHANGE(CurScreen(sp), y);
597	}
598
599	if (!NewScreen(sp)->_leaveok) {
600	    CurScreen(sp)->_curx = NewScreen(sp)->_curx;
601	    CurScreen(sp)->_cury = NewScreen(sp)->_cury;
602
603	    TCB->drv->td_hwcur(TCB,
604			       0, 0,
605			       CurScreen(sp)->_cury, CurScreen(sp)->_curx);
606	}
607	selectActiveHandle();
608	result = OK;
609    }
610    returnCode(result);
611}
612
613static bool
614wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
615	       const char *tname,
616	       int *errret GCC_UNUSED)
617{
618    bool code = FALSE;
619
620    T((T_CALLED("win32con::wcon_CanHandle(%p)"), TCB));
621
622    assert((TCB != 0) && (tname != 0));
623
624    TCB->magic = WINMAGIC;
625
626    if (tname == 0 || *tname == 0)
627	code = TRUE;
628    else if (tname != 0 && *tname == '#') {
629	/*
630	 * Use "#" (a character which cannot begin a terminal's name) to
631	 * select specific driver from the table.
632	 *
633	 * In principle, we could have more than one non-terminfo driver,
634	 * e.g., "win32gui".
635	 */
636	size_t n = strlen(tname + 1);
637	if (n != 0
638	    && ((strncmp(tname + 1, "win32console", n) == 0)
639		|| (strncmp(tname + 1, "win32con", n) == 0))) {
640	    code = TRUE;
641	}
642    } else if (tname != 0 && stricmp(tname, "unknown") == 0) {
643	code = TRUE;
644    }
645
646    /*
647     * This is intentional, to avoid unnecessary breakage of applications
648     * using <term.h> symbols.
649     */
650    if (code && (TerminalType(&TCB->term).Booleans == 0)) {
651	_nc_init_termtype(&TerminalType(&TCB->term));
652#if NCURSES_EXT_NUMBERS
653	_nc_export_termtype2(&TCB->term.type, &TerminalType(&TCB->term));
654#endif
655    }
656
657    if (!code) {
658	if (_nc_mingw_isconsole(0))
659	    CON.isTermInfoConsole = TRUE;
660    }
661    returnBool(code);
662}
663
664static int
665wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
666		 int beepFlag)
667{
668    SCREEN *sp;
669    int res = ERR;
670
671    int high = (CON.SBI.srWindow.Bottom - CON.SBI.srWindow.Top + 1);
672    int wide = (CON.SBI.srWindow.Right - CON.SBI.srWindow.Left + 1);
673    int max_cells = (high * wide);
674    int i;
675
676    CHAR_INFO *this_screen = TypeAlloca(CHAR_INFO, max_cells);
677    CHAR_INFO *that_screen = TypeAlloca(CHAR_INFO, max_cells);
678    COORD this_size;
679    SMALL_RECT this_region;
680    COORD bufferCoord;
681
682    if (okConsoleHandle(TCB)) {
683	SetSP();
684	this_region.Top = CON.SBI.srWindow.Top;
685	this_region.Left = CON.SBI.srWindow.Left;
686	this_region.Bottom = CON.SBI.srWindow.Bottom;
687	this_region.Right = CON.SBI.srWindow.Right;
688
689	this_size.X = (SHORT) wide;
690	this_size.Y = (SHORT) high;
691
692	bufferCoord.X = this_region.Left;
693	bufferCoord.Y = this_region.Top;
694
695	if (!beepFlag &&
696	    read_screen(CON.hdl,
697			this_screen,
698			this_size,
699			bufferCoord,
700			&this_region)) {
701
702	    memcpy(that_screen,
703		   this_screen,
704		   sizeof(CHAR_INFO) * (size_t) max_cells);
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
722static int
723wcon_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
735static int
736wcon_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
749static bool
750get_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
783static void
784wcon_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
797static bool
798wcon_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
811static bool
812wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
813{
814    int result = FALSE;
815    SCREEN *sp;
816
817    AssertTCB();
818    SetSP();
819
820    return result;
821}
822
823static int
824wcon_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
847static int
848wcon_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
856static int
857wcon_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 */
929static void
930set_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
1001static int
1002wcon_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
1064static void
1065wcon_screen_init(SCREEN *sp GCC_UNUSED)
1066{
1067}
1068
1069static void
1070wcon_wrap(SCREEN *sp GCC_UNUSED)
1071{
1072}
1073
1074static int
1075rkeycompare(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
1083static int
1084keycompare(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
1092static int
1093MapKey(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
1115static int
1116AnsiKey(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
1138static void
1139wcon_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
1150static bool
1151read_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 */
1200static bool
1201save_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
1230static void
1231wcon_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
1262static void
1263wcon_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
1280static void
1281wcon_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
1293static void
1294wcon_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
1307static void
1308wcon_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
1319static int
1320wcon_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
1344static int
1345wcon_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
1360static void
1361wcon_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
1371static void
1372wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1373		  int OnFlag GCC_UNUSED)
1374{
1375    SCREEN *sp;
1376
1377    AssertTCB();
1378    SetSP();
1379}
1380
1381static chtype
1382wcon_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
1389static void
1390wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1391{
1392    SCREEN *sp;
1393
1394    AssertTCB();
1395    SetSP();
1396}
1397
1398static void
1399wcon_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
1446static ULONGLONG
1447tdiff(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
1462static int
1463Adjust(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
1479static int
1480decode_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
1516static int
1517console_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
1628static int
1629wcon_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
1650static bool
1651handle_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
1693static int
1694wcon_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
1710static int
1711wcon_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
1718static int
1719wcon_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
1741static bool
1742wcon_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
1764static int
1765wcon_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
1782static int
1783wcon_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
1818NCURSES_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
1860static HANDLE
1861get_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 */
1876static 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*/
1937static BOOL
1938IsConsoleHandle(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 */
1955int
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 */
1981int
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
2008int
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
2047int
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
2059int
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
2075int
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
2099int
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
2163static bool
2164InitConsole(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
2256static bool
2257okConsoleHandle(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
2269static
2270__attribute__((constructor))
2271     void _enter_console(void)
2272{
2273    (void) InitConsole();
2274}
2275#endif
2276