]> ncurses.scripts.mit.edu Git - ncurses.git/blob - ncurses/win32con/win_driver.c
ncurses 6.0 - patch 20171209
[ncurses.git] / ncurses / win32con / win_driver.c
1 /****************************************************************************
2  * Copyright (c) 1998-2016,2017 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 TerminalType(my_term).
56
57 MODULE_ID("$Id: win_driver.c,v 1.59 2017/07/22 21:10:28 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, int delay)
1320 {
1321     int rc = 0;
1322     SCREEN *sp;
1323
1324     if (okConsoleHandle(TCB)) {
1325         SetSP();
1326
1327         if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1328             rc = TW_MOUSE;
1329         } else {
1330             rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
1331                                           TWAIT_MASK,
1332                                           delay,
1333                                           (int *) 0
1334                                           EVENTLIST_2nd(evl));
1335         }
1336     }
1337
1338     return rc;
1339 }
1340
1341 static int
1342 wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
1343            int yold GCC_UNUSED, int xold GCC_UNUSED,
1344            int y, int x)
1345 {
1346     int ret = ERR;
1347     if (okConsoleHandle(TCB)) {
1348         COORD loc;
1349         loc.X = (short) x;
1350         loc.Y = (short) (y + AdjustY());
1351         SetConsoleCursorPosition(CON.hdl, loc);
1352         ret = OK;
1353     }
1354     return ret;
1355 }
1356
1357 static void
1358 wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
1359              int labnum GCC_UNUSED,
1360              char *text GCC_UNUSED)
1361 {
1362     SCREEN *sp;
1363
1364     AssertTCB();
1365     SetSP();
1366 }
1367
1368 static void
1369 wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1370                   int OnFlag GCC_UNUSED)
1371 {
1372     SCREEN *sp;
1373
1374     AssertTCB();
1375     SetSP();
1376 }
1377
1378 static chtype
1379 wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1380 {
1381     chtype res = A_NORMAL;
1382     res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1383     return res;
1384 }
1385
1386 static void
1387 wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1388 {
1389     SCREEN *sp;
1390
1391     AssertTCB();
1392     SetSP();
1393 }
1394
1395 static void
1396 wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1397              chtype *real_map GCC_UNUSED,
1398              chtype *fake_map GCC_UNUSED)
1399 {
1400 #define DATA(a,b) { a, b }
1401     static struct {
1402         int acs_code;
1403         int use_code;
1404     } table[] = {
1405         DATA('a', 0xb1),        /* ACS_CKBOARD  */
1406             DATA('f', 0xf8),    /* ACS_DEGREE   */
1407             DATA('g', 0xf1),    /* ACS_PLMINUS  */
1408             DATA('j', 0xd9),    /* ACS_LRCORNER */
1409             DATA('l', 0xda),    /* ACS_ULCORNER */
1410             DATA('k', 0xbf),    /* ACS_URCORNER */
1411             DATA('m', 0xc0),    /* ACS_LLCORNER */
1412             DATA('n', 0xc5),    /* ACS_PLUS     */
1413             DATA('q', 0xc4),    /* ACS_HLINE    */
1414             DATA('t', 0xc3),    /* ACS_LTEE     */
1415             DATA('u', 0xb4),    /* ACS_RTEE     */
1416             DATA('v', 0xc1),    /* ACS_BTEE     */
1417             DATA('w', 0xc2),    /* ACS_TTEE     */
1418             DATA('x', 0xb3),    /* ACS_VLINE    */
1419             DATA('y', 0xf3),    /* ACS_LEQUAL   */
1420             DATA('z', 0xf2),    /* ACS_GEQUAL   */
1421             DATA('0', 0xdb),    /* ACS_BLOCK    */
1422             DATA('{', 0xe3),    /* ACS_PI       */
1423             DATA('}', 0x9c),    /* ACS_STERLING */
1424             DATA(',', 0xae),    /* ACS_LARROW   */
1425             DATA('+', 0xaf),    /* ACS_RARROW   */
1426             DATA('~', 0xf9),    /* ACS_BULLET   */
1427     };
1428 #undef DATA
1429     unsigned n;
1430
1431     SCREEN *sp;
1432     if (okConsoleHandle(TCB)) {
1433         SetSP();
1434
1435         for (n = 0; n < SIZEOF(table); ++n) {
1436             real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET;
1437             if (sp != 0)
1438                 sp->_screen_acs_map[table[n].acs_code] = TRUE;
1439         }
1440     }
1441 }
1442
1443 static ULONGLONG
1444 tdiff(FILETIME fstart, FILETIME fend)
1445 {
1446     ULARGE_INTEGER ustart;
1447     ULARGE_INTEGER uend;
1448     ULONGLONG diff;
1449
1450     ustart.LowPart = fstart.dwLowDateTime;
1451     ustart.HighPart = fstart.dwHighDateTime;
1452     uend.LowPart = fend.dwLowDateTime;
1453     uend.HighPart = fend.dwHighDateTime;
1454
1455     diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1456     return diff;
1457 }
1458
1459 static int
1460 Adjust(int milliseconds, int diff)
1461 {
1462     if (milliseconds != INFINITY) {
1463         milliseconds -= diff;
1464         if (milliseconds < 0)
1465             milliseconds = 0;
1466     }
1467     return milliseconds;
1468 }
1469
1470 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1471                      FROM_LEFT_2ND_BUTTON_PRESSED | \
1472                      FROM_LEFT_3RD_BUTTON_PRESSED | \
1473                      FROM_LEFT_4TH_BUTTON_PRESSED | \
1474                      RIGHTMOST_BUTTON_PRESSED)
1475
1476 static int
1477 decode_mouse(SCREEN *sp, int mask)
1478 {
1479     int result = 0;
1480
1481     (void) sp;
1482     assert(sp && console_initialized);
1483
1484     if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1485         result |= BUTTON1_PRESSED;
1486     if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1487         result |= BUTTON2_PRESSED;
1488     if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1489         result |= BUTTON3_PRESSED;
1490     if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1491         result |= BUTTON4_PRESSED;
1492
1493     if (mask & RIGHTMOST_BUTTON_PRESSED) {
1494         switch (CON.numButtons) {
1495         case 1:
1496             result |= BUTTON1_PRESSED;
1497             break;
1498         case 2:
1499             result |= BUTTON2_PRESSED;
1500             break;
1501         case 3:
1502             result |= BUTTON3_PRESSED;
1503             break;
1504         case 4:
1505             result |= BUTTON4_PRESSED;
1506             break;
1507         }
1508     }
1509
1510     return result;
1511 }
1512
1513 static int
1514 console_twait(
1515                  SCREEN *sp,
1516                  HANDLE fd,
1517                  int mode,
1518                  int milliseconds,
1519                  int *timeleft
1520                  EVENTLIST_2nd(_nc_eventlist * evl))
1521 {
1522     INPUT_RECORD inp_rec;
1523     BOOL b;
1524     DWORD nRead = 0, rc = (DWORD) (-1);
1525     int code = 0;
1526     FILETIME fstart;
1527     FILETIME fend;
1528     int diff;
1529     bool isImmed = (milliseconds == 0);
1530
1531 #define CONSUME() ReadConsoleInput(fd,&inp_rec,1,&nRead)
1532
1533     assert(sp);
1534
1535     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1536                       milliseconds, mode));
1537
1538     if (milliseconds < 0)
1539         milliseconds = INFINITY;
1540
1541     memset(&inp_rec, 0, sizeof(inp_rec));
1542
1543     while (true) {
1544         GetSystemTimeAsFileTime(&fstart);
1545         rc = WaitForSingleObject(fd, (DWORD) milliseconds);
1546         GetSystemTimeAsFileTime(&fend);
1547         diff = (int) tdiff(fstart, fend);
1548         milliseconds = Adjust(milliseconds, diff);
1549
1550         if (!isImmed && milliseconds <= 0)
1551             break;
1552
1553         if (rc == WAIT_OBJECT_0) {
1554             if (mode) {
1555                 b = GetNumberOfConsoleInputEvents(fd, &nRead);
1556                 if (b && nRead > 0) {
1557                     b = PeekConsoleInput(fd, &inp_rec, 1, &nRead);
1558                     if (b && nRead > 0) {
1559                         switch (inp_rec.EventType) {
1560                         case KEY_EVENT:
1561                             if (mode & TW_INPUT) {
1562                                 WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1563                                 char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1564
1565                                 if (inp_rec.Event.KeyEvent.bKeyDown) {
1566                                     if (0 == ch) {
1567                                         int nKey = MapKey(vk);
1568                                         if (nKey < 0) {
1569                                             CONSUME();
1570                                             continue;
1571                                         }
1572                                     }
1573                                     code = TW_INPUT;
1574                                     goto end;
1575                                 } else {
1576                                     CONSUME();
1577                                 }
1578                             }
1579                             continue;
1580                         case MOUSE_EVENT:
1581                             if (decode_mouse(sp,
1582                                              (inp_rec.Event.MouseEvent.dwButtonState
1583                                               & BUTTON_MASK)) == 0) {
1584                                 CONSUME();
1585                             } else if (mode & TW_MOUSE) {
1586                                 code = TW_MOUSE;
1587                                 goto end;
1588                             }
1589                             continue;
1590                             /* e.g., FOCUS_EVENT */
1591                         default:
1592                             CONSUME();
1593                             selectActiveHandle();
1594                             continue;
1595                         }
1596                     }
1597                 }
1598             }
1599             continue;
1600         } else {
1601             if (rc != WAIT_TIMEOUT) {
1602                 code = -1;
1603                 break;
1604             } else {
1605                 code = 0;
1606                 break;
1607             }
1608         }
1609     }
1610   end:
1611
1612     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1613                       code, errno, milliseconds));
1614
1615     if (timeleft)
1616         *timeleft = milliseconds;
1617
1618     return code;
1619 }
1620
1621 static int
1622 wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,
1623            int mode,
1624            int milliseconds,
1625            int *timeleft
1626            EVENTLIST_2nd(_nc_eventlist * evl))
1627 {
1628     SCREEN *sp;
1629     int code = 0;
1630
1631     if (okConsoleHandle(TCB)) {
1632         SetSP();
1633
1634         code = console_twait(sp,
1635                              CON.inp,
1636                              mode,
1637                              milliseconds,
1638                              timeleft EVENTLIST_2nd(_nc_eventlist * evl));
1639     }
1640     return code;
1641 }
1642
1643 static bool
1644 handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer)
1645 {
1646     MEVENT work;
1647     bool result = FALSE;
1648
1649     assert(sp);
1650
1651     sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1652     sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1653
1654     /*
1655      * We're only interested if the button is pressed or released.
1656      * FIXME: implement continuous event-tracking.
1657      */
1658     if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1659
1660         memset(&work, 0, sizeof(work));
1661
1662         if (sp->_drv_mouse_new_buttons) {
1663
1664             work.bstate |= (mmask_t) decode_mouse(sp, sp->_drv_mouse_new_buttons);
1665
1666         } else {
1667
1668             /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1669             work.bstate |= (mmask_t) (decode_mouse(sp,
1670                                                    sp->_drv_mouse_old_buttons)
1671                                       >> 1);
1672
1673             result = TRUE;
1674         }
1675
1676         work.x = mer.dwMousePosition.X;
1677         work.y = mer.dwMousePosition.Y - AdjustY();
1678
1679         sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1680         sp->_drv_mouse_tail += 1;
1681     }
1682
1683     return result;
1684 }
1685
1686 static int
1687 wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1688 {
1689     SCREEN *sp;
1690     int n = -1;
1691
1692     T((T_CALLED("win32con::wcon_read(%p)"), TCB));
1693
1694     assert(buf);
1695     if (okConsoleHandle(TCB)) {
1696         SetSP();
1697
1698         n = _nc_mingw_console_read(sp, CON.inp, buf);
1699     }
1700     returnCode(n);
1701 }
1702
1703 static int
1704 wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1705 {
1706     T((T_CALLED("win32con::wcon_nap(%p, %d)"), TCB, ms));
1707     Sleep((DWORD) ms);
1708     returnCode(OK);
1709 }
1710
1711 static int
1712 wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int mode)
1713 {
1714     int res = -1;
1715
1716     T((T_CALLED("win32con:wcon_cursorSet(%d)"), mode));
1717     if (okConsoleHandle(TCB)) {
1718         CONSOLE_CURSOR_INFO this_CI = CON.save_CI;
1719         switch (mode) {
1720         case 0:
1721             this_CI.bVisible = FALSE;
1722             break;
1723         case 1:
1724             break;
1725         case 2:
1726             this_CI.dwSize = 100;
1727             break;
1728         }
1729         SetConsoleCursorInfo(CON.hdl, &this_CI);
1730     }
1731     returnCode(res);
1732 }
1733
1734 static bool
1735 wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int keycode)
1736 {
1737     WORD nKey;
1738     void *res;
1739     bool found = FALSE;
1740     LONG key = GenMap(0, (WORD) keycode);
1741
1742     T((T_CALLED("win32con::wcon_kyExist(%d)"), keycode));
1743     res = bsearch(&key,
1744                   CON.rmap,
1745                   (size_t) (N_INI + FKEYS),
1746                   sizeof(keylist[0]),
1747                   rkeycompare);
1748     if (res) {
1749         key = *((LONG *) res);
1750         nKey = LOWORD(key);
1751         if (!(nKey & 0x8000))
1752             found = TRUE;
1753     }
1754     returnCode(found);
1755 }
1756
1757 static int
1758 wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1759 {
1760     SCREEN *sp;
1761     int code = ERR;
1762
1763     T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag));
1764
1765     if (okConsoleHandle(TCB)) {
1766         SetSP();
1767
1768         if (sp) {
1769             code = OK;
1770         }
1771     }
1772     returnCode(code);
1773 }
1774
1775 static int
1776 wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB,
1777            int keycode,
1778            int flag)
1779 {
1780     int code = ERR;
1781     SCREEN *sp;
1782     WORD nKey;
1783     WORD vKey;
1784     void *res;
1785     LONG key = GenMap(0, (WORD) keycode);
1786
1787     T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag));
1788
1789     if (okConsoleHandle(TCB)) {
1790         SetSP();
1791
1792         if (sp) {
1793             res = bsearch(&key,
1794                           CON.rmap,
1795                           (size_t) (N_INI + FKEYS),
1796                           sizeof(keylist[0]),
1797                           rkeycompare);
1798             if (res) {
1799                 key = *((LONG *) res);
1800                 vKey = HIWORD(key);
1801                 nKey = (LOWORD(key)) & 0x7fff;
1802                 if (!flag)
1803                     nKey |= 0x8000;
1804                 *(LONG *) res = GenMap(vKey, nKey);
1805             }
1806         }
1807     }
1808     returnCode(code);
1809 }
1810
1811 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1812     FALSE,
1813         wcon_name,              /* Name */
1814         wcon_CanHandle,         /* CanHandle */
1815         wcon_init,              /* init */
1816         wcon_release,           /* release */
1817         wcon_size,              /* size */
1818         wcon_sgmode,            /* sgmode */
1819         wcon_conattr,           /* conattr */
1820         wcon_mvcur,             /* hwcur */
1821         wcon_mode,              /* mode */
1822         wcon_rescol,            /* rescol */
1823         wcon_rescolors,         /* rescolors */
1824         wcon_setcolor,          /* color */
1825         wcon_dobeepflash,       /* DoBeepFlash */
1826         wcon_initpair,          /* initpair */
1827         wcon_initcolor,         /* initcolor */
1828         wcon_do_color,          /* docolor */
1829         wcon_initmouse,         /* initmouse */
1830         wcon_testmouse,         /* testmouse */
1831         wcon_setfilter,         /* setfilter */
1832         wcon_hwlabel,           /* hwlabel */
1833         wcon_hwlabelOnOff,      /* hwlabelOnOff */
1834         wcon_doupdate,          /* update */
1835         wcon_defaultcolors,     /* defaultcolors */
1836         wcon_print,             /* print */
1837         wcon_size,              /* getsize */
1838         wcon_setsize,           /* setsize */
1839         wcon_initacs,           /* initacs */
1840         wcon_screen_init,       /* scinit */
1841         wcon_wrap,              /* scexit */
1842         wcon_twait,             /* twait */
1843         wcon_read,              /* read */
1844         wcon_nap,               /* nap */
1845         wcon_kpad,              /* kpad */
1846         wcon_keyok,             /* kyOk */
1847         wcon_kyExist,           /* kyExist */
1848         wcon_cursorSet          /* cursorSet */
1849 };
1850
1851 /* --------------------------------------------------------- */
1852
1853 static HANDLE
1854 get_handle(int fd)
1855 {
1856     intptr_t value = _get_osfhandle(fd);
1857     return (HANDLE) value;
1858 }
1859
1860 #if WINVER >= 0x0600
1861 /*   This function tests, whether or not the ncurses application
1862      is running as a descendant of MSYS2/cygwin mintty terminal
1863      application. mintty doesn't use Windows Console for it's screen
1864      I/O, so the native Windows _isatty doesn't recognize it as
1865      character device. But we can discover we are at the end of an
1866      Pipe and can query to server side of the pipe, looking whether
1867      or not this is mintty.
1868  */
1869 static int
1870 _ismintty(int fd, LPHANDLE pMinTTY)
1871 {
1872     HANDLE handle = get_handle(fd);
1873     DWORD dw;
1874     int code = 0;
1875
1876     T((T_CALLED("win32con::_ismintty(%d, %p)"), fd, pMinTTY));
1877
1878     if (handle != INVALID_HANDLE_VALUE) {
1879         dw = GetFileType(handle);
1880         if (dw == FILE_TYPE_PIPE) {
1881             if (GetNamedPipeInfo(handle, 0, 0, 0, 0)) {
1882                 ULONG pPid;
1883                 /* Requires NT6 */
1884                 if (GetNamedPipeServerProcessId(handle, &pPid)) {
1885                     TCHAR buf[MAX_PATH];
1886                     DWORD len = 0;
1887                     /* These security attributes may allow us to
1888                        create a remote thread in mintty to manipulate
1889                        the terminal state remotely */
1890                     HANDLE pHandle = OpenProcess(
1891                                                     PROCESS_CREATE_THREAD
1892                                                     | PROCESS_QUERY_INFORMATION
1893                                                     | PROCESS_VM_OPERATION
1894                                                     | PROCESS_VM_WRITE
1895                                                     | PROCESS_VM_READ,
1896                                                     FALSE,
1897                                                     pPid);
1898                     if (pMinTTY)
1899                         *pMinTTY = INVALID_HANDLE_VALUE;
1900                     if (pHandle != INVALID_HANDLE_VALUE) {
1901                         if ((len = GetProcessImageFileName(
1902                                                               pHandle,
1903                                                               buf,
1904                                                               (DWORD)
1905                                                               array_length(buf)))) {
1906                             TCHAR *pos = _tcsrchr(buf, _T('\\'));
1907                             if (pos) {
1908                                 pos++;
1909                                 if (_tcsnicmp(pos, _TEXT("mintty.exe"), 10)
1910                                     == 0) {
1911                                     if (pMinTTY)
1912                                         *pMinTTY = pHandle;
1913                                     code = 1;
1914                                 }
1915                             }
1916                         }
1917                     }
1918                 }
1919             }
1920         }
1921     }
1922     returnCode(code);
1923 }
1924 #endif
1925
1926 /*   Borrowed from ansicon project.
1927      Check whether or not an I/O handle is associated with
1928      a Windows console.
1929 */
1930 static BOOL
1931 IsConsoleHandle(HANDLE hdl)
1932 {
1933     DWORD dwFlag = 0;
1934     BOOL result;
1935
1936     if (!GetConsoleMode(hdl, &dwFlag)) {
1937         result = (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL);
1938     } else {
1939         result = (int) (dwFlag & ENABLE_PROCESSED_OUTPUT);
1940     }
1941     return result;
1942 }
1943
1944 /*   Our replacement for the systems _isatty to include also
1945      a test for mintty. This is called from the NC_ISATTY macro
1946      defined in curses.priv.h
1947  */
1948 int
1949 _nc_mingw_isatty(int fd)
1950 {
1951     int result = 0;
1952
1953 #ifdef __MING32__
1954 #define SysISATTY(fd) _isatty(fd)
1955 #else
1956 #define SysISATTY(fd) isatty(fd)
1957 #endif
1958     if (SysISATTY(fd)) {
1959         result = 1;
1960     } else {
1961 #if WINVER >= 0x0600
1962         result = _ismintty(fd, NULL);
1963 #endif
1964     }
1965     return result;
1966 }
1967
1968 /*   This is used when running in terminfo mode to discover,
1969      whether or not the "terminal" is actually a Windows
1970      Console. It's the responsibilty of the console to deal
1971      with the terminal escape sequences that are sent by
1972      terminfo.
1973  */
1974 int
1975 _nc_mingw_isconsole(int fd)
1976 {
1977     HANDLE hdl = get_handle(fd);
1978     int code = 0;
1979
1980     T((T_CALLED("win32con::_nc_mingw_isconsole(%d)"), fd));
1981
1982     code = (int) IsConsoleHandle(hdl);
1983
1984     returnCode(code);
1985 }
1986
1987 #define TC_PROLOGUE(fd) \
1988     SCREEN *sp;                                               \
1989     TERMINAL *term = 0;                                       \
1990     int code = ERR;                                           \
1991     if (_nc_screen_chain == 0)                                \
1992         return 0;                                             \
1993     for (each_screen(sp)) {                                   \
1994         if (sp->_term && (sp->_term->Filedes == fd)) {        \
1995             term = sp->_term;                                 \
1996             break;                                            \
1997         }                                                     \
1998     }                                                         \
1999     assert(term != 0)
2000
2001 int
2002 _nc_mingw_tcsetattr(
2003                        int fd,
2004                        int optional_action GCC_UNUSED,
2005                        const struct termios *arg)
2006 {
2007     TC_PROLOGUE(fd);
2008
2009     if (_nc_mingw_isconsole(fd)) {
2010         DWORD dwFlag = 0;
2011         HANDLE ofd = get_handle(fd);
2012         if (ofd != INVALID_HANDLE_VALUE) {
2013             if (arg) {
2014                 if (arg->c_lflag & ICANON)
2015                     dwFlag |= ENABLE_LINE_INPUT;
2016                 else
2017                     dwFlag = dwFlag & (DWORD) (~ENABLE_LINE_INPUT);
2018
2019                 if (arg->c_lflag & ECHO)
2020                     dwFlag = dwFlag | ENABLE_ECHO_INPUT;
2021                 else
2022                     dwFlag = dwFlag & (DWORD) (~ENABLE_ECHO_INPUT);
2023
2024                 if (arg->c_iflag & BRKINT)
2025                     dwFlag |= ENABLE_PROCESSED_INPUT;
2026                 else
2027                     dwFlag = dwFlag & (DWORD) (~ENABLE_PROCESSED_INPUT);
2028             }
2029             dwFlag |= ENABLE_MOUSE_INPUT;
2030             SetConsoleMode(ofd, dwFlag);
2031             code = OK;
2032         }
2033     }
2034     if (arg)
2035         term->Nttyb = *arg;
2036
2037     return code;
2038 }
2039
2040 int
2041 _nc_mingw_tcgetattr(int fd, struct termios *arg)
2042 {
2043     TC_PROLOGUE(fd);
2044
2045     if (_nc_mingw_isconsole(fd)) {
2046         if (arg)
2047             *arg = term->Nttyb;
2048     }
2049     return code;
2050 }
2051
2052 int
2053 _nc_mingw_tcflush(int fd, int queue)
2054 {
2055     TC_PROLOGUE(fd);
2056     (void) term;
2057
2058     if (_nc_mingw_isconsole(fd)) {
2059         if (queue == TCIFLUSH) {
2060             BOOL b = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
2061             if (!b)
2062                 return (int) GetLastError();
2063         }
2064     }
2065     return code;
2066 }
2067
2068 int
2069 _nc_mingw_testmouse(
2070                        SCREEN *sp,
2071                        HANDLE fd,
2072                        int delay)
2073 {
2074     int rc = 0;
2075
2076     assert(sp);
2077
2078     if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
2079         rc = TW_MOUSE;
2080     } else {
2081         rc = console_twait(sp,
2082                            fd,
2083                            TWAIT_MASK,
2084                            delay,
2085                            (int *) 0
2086                            EVENTLIST_2nd(evl));
2087     }
2088     return rc;
2089 }
2090
2091 int
2092 _nc_mingw_console_read(
2093                           SCREEN *sp,
2094                           HANDLE fd,
2095                           int *buf)
2096 {
2097     int rc = -1;
2098     INPUT_RECORD inp_rec;
2099     BOOL b;
2100     DWORD nRead;
2101     WORD vk;
2102
2103     assert(sp);
2104     assert(buf);
2105
2106     memset(&inp_rec, 0, sizeof(inp_rec));
2107
2108     T((T_CALLED("_nc_mingw_console_read(%p)"), sp));
2109
2110     while ((b = ReadConsoleInput(fd, &inp_rec, 1, &nRead))) {
2111         if (b && nRead > 0) {
2112             if (rc < 0)
2113                 rc = 0;
2114             rc = rc + (int) nRead;
2115             if (inp_rec.EventType == KEY_EVENT) {
2116                 if (!inp_rec.Event.KeyEvent.bKeyDown)
2117                     continue;
2118                 *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
2119                 vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
2120                 /*
2121                  * There are 24 virtual function-keys, and typically
2122                  * 12 function-keys on a keyboard.  Use the shift-modifier
2123                  * to provide the remaining 12 keys.
2124                  */
2125                 if (vk >= VK_F1 && vk <= VK_F12) {
2126                     if (inp_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) {
2127                         vk = (WORD) (vk + 12);
2128                     }
2129                 }
2130                 if (*buf == 0) {
2131                     int key = MapKey(vk);
2132                     if (key < 0)
2133                         continue;
2134                     if (sp->_keypad_on) {
2135                         *buf = key;
2136                     } else {
2137                         ungetch('\0');
2138                         *buf = AnsiKey(vk);
2139                     }
2140                 }
2141                 break;
2142             } else if (inp_rec.EventType == MOUSE_EVENT) {
2143                 if (handle_mouse(sp,
2144                                  inp_rec.Event.MouseEvent)) {
2145                     *buf = KEY_MOUSE;
2146                     break;
2147                 }
2148             }
2149             continue;
2150         }
2151     }
2152     returnCode(rc);
2153 }
2154
2155 static bool
2156 InitConsole(void)
2157 {
2158     /* initalize once, or not at all */
2159     if (!console_initialized) {
2160         int i;
2161         DWORD num_buttons;
2162         WORD a;
2163         BOOL buffered = TRUE;
2164         BOOL b;
2165
2166         START_TRACE();
2167         if (_nc_mingw_isatty(0)) {
2168             CON.isMinTTY = TRUE;
2169         }
2170
2171         for (i = 0; i < (N_INI + FKEYS); i++) {
2172             if (i < N_INI) {
2173                 CON.rmap[i] = CON.map[i] =
2174                     (DWORD) keylist[i];
2175                 CON.ansi_map[i] = (DWORD) ansi_keys[i];
2176             } else {
2177                 CON.rmap[i] = CON.map[i] =
2178                     (DWORD) GenMap((VK_F1 + (i - N_INI)),
2179                                    (KEY_F(1) + (i - N_INI)));
2180                 CON.ansi_map[i] =
2181                     (DWORD) GenMap((VK_F1 + (i - N_INI)),
2182                                    (';' + (i - N_INI)));
2183             }
2184         }
2185         qsort(CON.ansi_map,
2186               (size_t) (MAPSIZE),
2187               sizeof(keylist[0]),
2188               keycompare);
2189         qsort(CON.map,
2190               (size_t) (MAPSIZE),
2191               sizeof(keylist[0]),
2192               keycompare);
2193         qsort(CON.rmap,
2194               (size_t) (MAPSIZE),
2195               sizeof(keylist[0]),
2196               rkeycompare);
2197
2198         if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
2199             CON.numButtons = (int) num_buttons;
2200         } else {
2201             CON.numButtons = 1;
2202         }
2203
2204         a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
2205         for (i = 0; i < NUMPAIRS; i++)
2206             CON.pairs[i] = a;
2207
2208         CON.inp = GetStdHandle(STD_INPUT_HANDLE);
2209         CON.out = GetStdHandle(STD_OUTPUT_HANDLE);
2210
2211         b = AllocConsole();
2212
2213         if (!b)
2214             b = AttachConsole(ATTACH_PARENT_PROCESS);
2215
2216         if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
2217             T(("... will not buffer console"));
2218             buffered = FALSE;
2219             CON.hdl = CON.out;
2220         } else {
2221             T(("... creating console buffer"));
2222             CON.hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
2223                                                 0,
2224                                                 NULL,
2225                                                 CONSOLE_TEXTMODE_BUFFER,
2226                                                 NULL);
2227         }
2228
2229         if (CON.hdl != INVALID_HANDLE_VALUE) {
2230             CON.buffered = buffered;
2231             get_SBI();
2232             CON.save_SBI = CON.SBI;
2233             if (!buffered) {
2234                 save_original_screen();
2235                 set_scrollback(FALSE, &CON.SBI);
2236             }
2237             GetConsoleCursorInfo(CON.hdl, &CON.save_CI);
2238             T(("... initial cursor is %svisible, %d%%",
2239                (CON.save_CI.bVisible ? "" : "not-"),
2240                (int) CON.save_CI.dwSize));
2241         }
2242
2243         console_initialized = TRUE;
2244     }
2245     return (CON.hdl != INVALID_HANDLE_VALUE);
2246 }
2247
2248 static bool
2249 okConsoleHandle(TERMINAL_CONTROL_BLOCK * TCB)
2250 {
2251     return ((TCB != 0) &&
2252             (TCB->magic == WINMAGIC) &&
2253             InitConsole());
2254 }
2255
2256 /*
2257  * While a constructor would ensure that this module is initialized, that will
2258  * interfere with applications that may combine this with GUI interfaces.
2259  */
2260 #if 0
2261 static
2262 __attribute__((constructor))
2263      void _enter_console(void)
2264 {
2265     (void) InitConsole();
2266 }
2267 #endif