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