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