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