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