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