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