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