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