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