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