ncurses 5.9 - patch 20140308
[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 #define CUR my_term.type.
41
42 MODULE_ID("$Id: win_driver.c,v 1.28 2014/03/08 21:44:53 tom Exp $")
43
44 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
45
46 #define EXP_OPTIMIZE 0
47
48 #define okConsoleHandle(TCB) (TCB != 0 && !InvalidConsoleHandle(TCB->hdl))
49
50 #define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC))
51 #define SetSP()     assert(TCB->csp != 0); sp = TCB->csp; (void) sp
52
53 #define GenMap(vKey,key) MAKELONG(key, vKey)
54
55 #define AdjustY(p) ((p)->buffered ? 0 : (int) (p)->SBI.srWindow.Top)
56
57 static const LONG keylist[] =
58 {
59     GenMap(VK_PRIOR, KEY_PPAGE),
60     GenMap(VK_NEXT, KEY_NPAGE),
61     GenMap(VK_END, KEY_END),
62     GenMap(VK_HOME, KEY_HOME),
63     GenMap(VK_LEFT, KEY_LEFT),
64     GenMap(VK_UP, KEY_UP),
65     GenMap(VK_RIGHT, KEY_RIGHT),
66     GenMap(VK_DOWN, KEY_DOWN),
67     GenMap(VK_DELETE, KEY_DC),
68     GenMap(VK_INSERT, KEY_IC)
69 };
70 #define N_INI ((int)(sizeof(keylist)/sizeof(keylist[0])))
71 #define FKEYS 24
72 #define MAPSIZE (FKEYS + N_INI)
73 #define NUMPAIRS 64
74
75 typedef struct props {
76     CONSOLE_SCREEN_BUFFER_INFO SBI;
77     bool progMode;
78     TERM_HANDLE lastOut;
79     DWORD map[MAPSIZE];
80     DWORD rmap[MAPSIZE];
81     WORD pairs[NUMPAIRS];
82     bool buffered;              /* normally allocate console-buffer */
83     bool window_only;           /* ..if not, we save buffer or window-only */
84     COORD origin;
85     CHAR_INFO *save_screen;
86 } Properties;
87
88 #define PropOf(TCB) ((Properties*)TCB->prop)
89
90 int
91 _nc_mingw_ioctl(int fd GCC_UNUSED,
92                 long int request GCC_UNUSED,
93                 struct termios *arg GCC_UNUSED)
94 {
95     return 0;
96     endwin();
97     fprintf(stderr, "TERMINFO currently not supported on Windows.\n");
98     exit(1);
99 }
100
101 static WORD
102 MapColor(bool fore, int color)
103 {
104     static const int _cmap[] =
105     {0, 4, 2, 6, 1, 5, 3, 7};
106     int a;
107     if (color < 0 || color > 7)
108         a = fore ? 7 : 0;
109     else
110         a = _cmap[color];
111     if (!fore)
112         a = a << 4;
113     return (WORD) a;
114 }
115
116 static WORD
117 MapAttr(TERMINAL_CONTROL_BLOCK * TCB, WORD res, attr_t ch)
118 {
119     if (ch & A_COLOR) {
120         int p;
121         SCREEN *sp;
122
123         AssertTCB();
124         SetSP();
125         p = PairNumber(ch);
126         if (p > 0 && p < NUMPAIRS && TCB != 0 && sp != 0) {
127             WORD a;
128             a = PropOf(TCB)->pairs[p];
129             res = (WORD) ((res & 0xff00) | a);
130         }
131     }
132
133     if (ch & A_REVERSE) {
134         res = (WORD) ((res & 0xff00) |
135                       (((res & 0x07) << 4) |
136                        ((res & 0x70) >> 4)));
137     }
138
139     if (ch & A_STANDOUT) {
140         res = (WORD) ((res & 0xff00) |
141                       (((res & 0x07) << 4) |
142                        ((res & 0x70) >> 4)) |
143                       BACKGROUND_INTENSITY);
144     }
145
146     if (ch & A_BOLD)
147         res |= FOREGROUND_INTENSITY;
148
149     if (ch & A_DIM)
150         res |= BACKGROUND_INTENSITY;
151
152     return res;
153 }
154
155 #if USE_WIDEC_SUPPORT
156 /*
157  * TODO: support surrogate pairs
158  * TODO: support combining characters
159  * TODO: support acsc
160  * TODO: check wcwidth of base character, fill if needed for double-width
161  * TODO: _nc_wacs should be part of sp.
162  */
163 static BOOL
164 con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
165 {
166     int actual = 0;
167     CHAR_INFO ci[limit];
168     COORD loc, siz;
169     SMALL_RECT rec;
170     int i;
171     cchar_t ch;
172     SCREEN *sp;
173     Properties *p = PropOf(TCB);
174
175     AssertTCB();
176
177     SetSP();
178
179     for (i = actual = 0; i < limit; i++) {
180         ch = str[i];
181         if (isWidecExt(ch))
182             continue;
183         ci[actual].Char.UnicodeChar = CharOf(ch);
184         ci[actual].Attributes = MapAttr(TCB,
185                                         PropOf(TCB)->SBI.wAttributes,
186                                         AttrOf(ch));
187         if (AttrOf(ch) & A_ALTCHARSET) {
188             if (_nc_wacs) {
189                 int which = CharOf(ch);
190                 if (which > 0
191                     && which < ACS_LEN
192                     && CharOf(_nc_wacs[which]) != 0) {
193                     ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
194                 } else {
195                     ci[actual].Char.UnicodeChar = ' ';
196                 }
197             }
198         }
199         ++actual;
200     }
201
202     loc.X = (short) 0;
203     loc.Y = (short) 0;
204     siz.X = (short) actual;
205     siz.Y = 1;
206
207     rec.Left = (short) x;
208     rec.Top = (SHORT) (y + AdjustY(p));
209     rec.Right = (short) (x + limit - 1);
210     rec.Bottom = rec.Top;
211
212     return WriteConsoleOutputW(TCB->hdl, ci, siz, loc, &rec);
213 }
214 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
215 #else
216 static BOOL
217 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
218 {
219     CHAR_INFO ci[n];
220     COORD loc, siz;
221     SMALL_RECT rec;
222     int i;
223     chtype ch;
224     SCREEN *sp;
225
226     AssertTCB();
227
228     SetSP();
229
230     for (i = 0; i < n; i++) {
231         ch = str[i];
232         ci[i].Char.AsciiChar = ChCharOf(ch);
233         ci[i].Attributes = MapAttr(TCB,
234                                    PropOf(TCB)->SBI.wAttributes,
235                                    ChAttrOf(ch));
236         if (ChAttrOf(ch) & A_ALTCHARSET) {
237             if (sp->_acs_map)
238                 ci[i].Char.AsciiChar =
239                 ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
240         }
241     }
242
243     loc.X = (short) 0;
244     loc.Y = (short) 0;
245     siz.X = (short) n;
246     siz.Y = 1;
247
248     rec.Left = (short) x;
249     rec.Top = (short) y;
250     rec.Right = (short) (x + n - 1);
251     rec.Bottom = rec.Top;
252
253     return WriteConsoleOutput(TCB->hdl, ci, siz, loc, &rec);
254 }
255 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
256 #endif
257
258 #if EXP_OPTIMIZE
259 /*
260  * Comparing new/current screens, determine the last column-index for a change
261  * beginning on the given row,col position.  Unlike a serial terminal, there is
262  * no cost for "moving" the "cursor" on the line as we update it.
263  */
264 static int
265 find_end_of_change(SCREEN *sp, int row, int col)
266 {
267     int result = col;
268     struct ldat *curdat = CurScreen(sp)->_line + row;
269     struct ldat *newdat = NewScreen(sp)->_line + row;
270
271     while (col <= newdat->lastchar) {
272 #if USE_WIDEC_SUPPORT
273         if (isWidecExt(curdat->text[col]) || isWidecExt(newdat->text[col])) {
274             result = col;
275         } else if (memcmp(&curdat->text[col],
276                           &newdat->text[col],
277                           sizeof(curdat->text[0]))) {
278             result = col;
279         } else {
280             break;
281         }
282 #else
283         if (curdat->text[col] != newdat->text[col]) {
284             result = col;
285         } else {
286             break;
287         }
288 #endif
289         ++col;
290     }
291     return result;
292 }
293
294 /*
295  * Given a row,col position at the end of a change-chunk, look for the
296  * beginning of the next change-chunk.
297  */
298 static int
299 find_next_change(SCREEN *sp, int row, int col)
300 {
301     struct ldat *curdat = CurScreen(sp)->_line + row;
302     struct ldat *newdat = NewScreen(sp)->_line + row;
303     int result = newdat->lastchar + 1;
304
305     while (++col <= newdat->lastchar) {
306 #if USE_WIDEC_SUPPORT
307         if (isWidecExt(curdat->text[col]) != isWidecExt(newdat->text[col])) {
308             result = col;
309             break;
310         } else if (memcmp(&curdat->text[col],
311                           &newdat->text[col],
312                           sizeof(curdat->text[0]))) {
313             result = col;
314             break;
315         }
316 #else
317         if (curdat->text[col] != newdat->text[col]) {
318             result = col;
319             break;
320         }
321 #endif
322     }
323     return result;
324 }
325
326 #define EndChange(first) \
327         find_end_of_change(sp, y, first)
328 #define NextChange(last) \
329         find_next_change(sp, y, last)
330
331 #endif /* EXP_OPTIMIZE */
332
333 #define MARK_NOCHANGE(win,row) \
334                 win->_line[row].firstchar = _NOCHANGE; \
335                 win->_line[row].lastchar  = _NOCHANGE
336
337 static void
338 selectActiveHandle(TERMINAL_CONTROL_BLOCK * TCB)
339 {
340     if (PropOf(TCB)->lastOut != TCB->hdl) {
341         PropOf(TCB)->lastOut = TCB->hdl;
342         SetConsoleActiveScreenBuffer(PropOf(TCB)->lastOut);
343     }
344 }
345
346 static bool
347 restore_original_screen(TERMINAL_CONTROL_BLOCK * TCB)
348 {
349     COORD bufferCoord;
350     SMALL_RECT writeRegion;
351     Properties *p = PropOf(TCB);
352     bool result = FALSE;
353
354     if (p->window_only) {
355         writeRegion.Top = p->SBI.srWindow.Top;
356         writeRegion.Left = p->SBI.srWindow.Left;
357         writeRegion.Bottom = p->SBI.srWindow.Bottom;
358         writeRegion.Right = p->SBI.srWindow.Right;
359         T(("... restoring window"));
360     } else {
361         writeRegion.Top = 0;
362         writeRegion.Left = 0;
363         writeRegion.Bottom = (SHORT) (p->SBI.dwSize.Y - 1);
364         writeRegion.Right = (SHORT) (p->SBI.dwSize.X - 1);
365         T(("... restoring entire buffer"));
366     }
367
368     bufferCoord.X = bufferCoord.Y = 0;
369
370     if (WriteConsoleOutput(TCB->hdl,
371                            p->save_screen,
372                            p->SBI.dwSize,
373                            bufferCoord,
374                            &writeRegion)) {
375         result = TRUE;
376         mvcur(-1, -1, LINES - 2, 0);
377     }
378     T(("... restore original screen contents %s", result ? "ok" : "err"));
379     return result;
380 }
381
382 static int
383 drv_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
384 {
385     int result = ERR;
386     int y, nonempty, n, x0, x1, Width, Height;
387     SCREEN *sp;
388
389     AssertTCB();
390     SetSP();
391
392     T((T_CALLED("win32con::drv_doupdate(%p)"), TCB));
393     if (okConsoleHandle(TCB)) {
394
395         Width = screen_columns(sp);
396         Height = screen_lines(sp);
397         nonempty = min(Height, NewScreen(sp)->_maxy + 1);
398
399         if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
400             int x;
401 #if USE_WIDEC_SUPPORT
402             cchar_t empty[Width];
403             wchar_t blank[2] =
404             {
405                 L' ', L'\0'
406             };
407
408             for (x = 0; x < Width; x++)
409                 setcchar(&empty[x], blank, 0, 0, 0);
410 #else
411             chtype empty[Width];
412
413             for (x = 0; x < Width; x++)
414                 empty[x] = ' ';
415 #endif
416
417             for (y = 0; y < nonempty; y++) {
418                 con_write(TCB, y, 0, empty, Width);
419                 memcpy(empty,
420                        CurScreen(sp)->_line[y].text,
421                        (size_t) Width * sizeof(empty[0]));
422             }
423             CurScreen(sp)->_clear = FALSE;
424             NewScreen(sp)->_clear = FALSE;
425             touchwin(NewScreen(sp));
426         }
427
428         for (y = 0; y < nonempty; y++) {
429             x0 = NewScreen(sp)->_line[y].firstchar;
430             if (x0 != _NOCHANGE) {
431 #if EXP_OPTIMIZE
432                 int x2;
433                 int limit = NewScreen(sp)->_line[y].lastchar;
434                 while ((x1 = EndChange(x0)) <= limit) {
435                     while ((x2 = NextChange(x1)) <= limit && x2 <= (x1 + 2)) {
436                         x1 = x2;
437                     }
438                     n = x1 - x0 + 1;
439                     memcpy(&CurScreen(sp)->_line[y].text[x0],
440                            &NewScreen(sp)->_line[y].text[x0],
441                            n * sizeof(CurScreen(sp)->_line[y].text[x0]));
442                     con_write(TCB,
443                               y,
444                               x0,
445                               &CurScreen(sp)->_line[y].text[x0], n);
446                     x0 = NextChange(x1);
447                 }
448
449                 /* mark line changed successfully */
450                 if (y <= NewScreen(sp)->_maxy) {
451                     MARK_NOCHANGE(NewScreen(sp), y);
452                 }
453                 if (y <= CurScreen(sp)->_maxy) {
454                     MARK_NOCHANGE(CurScreen(sp), y);
455                 }
456 #else
457                 x1 = NewScreen(sp)->_line[y].lastchar;
458                 n = x1 - x0 + 1;
459                 if (n > 0) {
460                     memcpy(&CurScreen(sp)->_line[y].text[x0],
461                            &NewScreen(sp)->_line[y].text[x0],
462                            (size_t) n * sizeof(CurScreen(sp)->_line[y].text[x0]));
463                     con_write(TCB,
464                               y,
465                               x0,
466                               &CurScreen(sp)->_line[y].text[x0], n);
467
468                     /* mark line changed successfully */
469                     if (y <= NewScreen(sp)->_maxy) {
470                         MARK_NOCHANGE(NewScreen(sp), y);
471                     }
472                     if (y <= CurScreen(sp)->_maxy) {
473                         MARK_NOCHANGE(CurScreen(sp), y);
474                     }
475                 }
476 #endif
477             }
478         }
479
480         /* put everything back in sync */
481         for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
482             MARK_NOCHANGE(NewScreen(sp), y);
483         }
484         for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
485             MARK_NOCHANGE(CurScreen(sp), y);
486         }
487
488         if (!NewScreen(sp)->_leaveok) {
489             CurScreen(sp)->_curx = NewScreen(sp)->_curx;
490             CurScreen(sp)->_cury = NewScreen(sp)->_cury;
491
492             TCB->drv->td_hwcur(TCB,
493                                0, 0,
494                                CurScreen(sp)->_cury, CurScreen(sp)->_curx);
495         }
496         selectActiveHandle(TCB);
497         result = OK;
498     }
499     returnCode(result);
500 }
501
502 static bool
503 drv_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
504               const char *tname,
505               int *errret GCC_UNUSED)
506 {
507     bool code = FALSE;
508
509     T((T_CALLED("win32con::drv_CanHandle(%p)"), TCB));
510
511     assert(TCB != 0);
512     assert(tname != 0);
513
514     TCB->magic = WINMAGIC;
515     if (*tname == 0 || *tname == 0 || *tname == '#') {
516         code = TRUE;
517     } else {
518         TERMINAL my_term;
519         int status;
520
521         code = FALSE;
522 #if (NCURSES_USE_DATABASE || NCURSES_USE_TERMCAP)
523         status = _nc_setup_tinfo(tname, &my_term.type);
524 #else
525         status = TGETENT_NO;
526 #endif
527         if (status != TGETENT_YES) {
528             const TERMTYPE *fallback = _nc_fallback(tname);
529
530             if (fallback) {
531                 my_term.type = *fallback;
532                 status = TGETENT_YES;
533             } else if (!strcmp(tname, "unknown")) {
534                 code = TRUE;
535             }
536         }
537         if (status == TGETENT_YES) {
538             if (generic_type || hard_copy)
539                 code = TRUE;
540         }
541     }
542
543     if (code) {
544         if ((TCB->term.type.Booleans) == 0) {
545             _nc_init_termtype(&(TCB->term.type));
546         }
547     }
548
549     returnBool(code);
550 }
551
552 static int
553 drv_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
554                 int beepFlag GCC_UNUSED)
555 {
556     SCREEN *sp;
557     int res = ERR;
558
559     AssertTCB();
560     SetSP();
561
562     return res;
563 }
564
565 static int
566 drv_print(TERMINAL_CONTROL_BLOCK * TCB,
567           char *data GCC_UNUSED,
568           int len GCC_UNUSED)
569 {
570     SCREEN *sp;
571
572     AssertTCB();
573     SetSP();
574
575     return ERR;
576 }
577
578 static int
579 drv_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
580                   int fg GCC_UNUSED,
581                   int bg GCC_UNUSED)
582 {
583     SCREEN *sp;
584     int code = ERR;
585
586     AssertTCB();
587     SetSP();
588
589     return (code);
590 }
591
592 static bool
593 get_SBI(TERMINAL_CONTROL_BLOCK * TCB)
594 {
595     bool rc = FALSE;
596     Properties *p = PropOf(TCB);
597     if (GetConsoleScreenBufferInfo(TCB->hdl, &(p->SBI))) {
598         T(("GetConsoleScreenBufferInfo"));
599         T(("... buffer(X:%d Y:%d)",
600            p->SBI.dwSize.X,
601            p->SBI.dwSize.Y));
602         T(("... window(X:%d Y:%d)",
603            p->SBI.dwMaximumWindowSize.X,
604            p->SBI.dwMaximumWindowSize.Y));
605         T(("... cursor(X:%d Y:%d)",
606            p->SBI.dwCursorPosition.X,
607            p->SBI.dwCursorPosition.Y));
608         T(("... display(Top:%d Bottom:%d Left:%d Right:%d)",
609            p->SBI.srWindow.Top,
610            p->SBI.srWindow.Bottom,
611            p->SBI.srWindow.Left,
612            p->SBI.srWindow.Right));
613         if (p->buffered) {
614             p->origin.X = 0;
615             p->origin.Y = 0;
616         } else {
617             p->origin.X = p->SBI.srWindow.Left;
618             p->origin.Y = p->SBI.srWindow.Top;
619         }
620         rc = TRUE;
621     } else {
622         T(("GetConsoleScreenBufferInfo ERR"));
623     }
624     return rc;
625 }
626
627 static void
628 drv_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
629              int fore,
630              int color,
631              int (*outc) (SCREEN *, int) GCC_UNUSED)
632 {
633     AssertTCB();
634
635     if (okConsoleHandle(TCB) &&
636         PropOf(TCB) != 0) {
637         WORD a = MapColor(fore, color);
638         a |= (WORD) ((PropOf(TCB)->SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f));
639         SetConsoleTextAttribute(TCB->hdl, a);
640         get_SBI(TCB);
641     }
642 }
643
644 static bool
645 drv_rescol(TERMINAL_CONTROL_BLOCK * TCB)
646 {
647     bool res = FALSE;
648
649     AssertTCB();
650     if (okConsoleHandle(TCB)) {
651         WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
652         SetConsoleTextAttribute(TCB->hdl, a);
653         get_SBI(TCB);
654         res = TRUE;
655     }
656     return res;
657 }
658
659 static bool
660 drv_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
661 {
662     int result = FALSE;
663     SCREEN *sp;
664
665     AssertTCB();
666     SetSP();
667
668     return result;
669 }
670
671 static int
672 drv_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
673 {
674     int result = ERR;
675
676     AssertTCB();
677
678     T((T_CALLED("win32con::drv_size(%p)"), TCB));
679
680     if (okConsoleHandle(TCB) &&
681         PropOf(TCB) != 0 &&
682         Lines != NULL &&
683         Cols != NULL) {
684         if (PropOf(TCB)->buffered) {
685             *Lines = (int) (PropOf(TCB)->SBI.dwSize.Y);
686             *Cols = (int) (PropOf(TCB)->SBI.dwSize.X);
687         } else {
688             *Lines = (int) (PropOf(TCB)->SBI.srWindow.Bottom + 1 -
689                             PropOf(TCB)->SBI.srWindow.Top);
690             *Cols = (int) (PropOf(TCB)->SBI.srWindow.Right + 1 -
691                            PropOf(TCB)->SBI.srWindow.Left);
692         }
693         result = OK;
694     }
695     returnCode(result);
696 }
697
698 static int
699 drv_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
700             int l GCC_UNUSED,
701             int c GCC_UNUSED)
702 {
703     AssertTCB();
704     return ERR;
705 }
706
707 static int
708 drv_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
709 {
710     DWORD dwFlag = 0;
711     tcflag_t iflag;
712     tcflag_t lflag;
713
714     AssertTCB();
715
716     if (TCB == 0 || buf == NULL)
717         return ERR;
718
719     if (setFlag) {
720         iflag = buf->c_iflag;
721         lflag = buf->c_lflag;
722
723         GetConsoleMode(TCB->inp, &dwFlag);
724
725         if (lflag & ICANON)
726             dwFlag |= ENABLE_LINE_INPUT;
727         else
728             dwFlag &= (DWORD) (~ENABLE_LINE_INPUT);
729
730         if (lflag & ECHO)
731             dwFlag |= ENABLE_ECHO_INPUT;
732         else
733             dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT);
734
735         if (iflag & BRKINT)
736             dwFlag |= ENABLE_PROCESSED_INPUT;
737         else
738             dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT);
739
740         dwFlag |= ENABLE_MOUSE_INPUT;
741
742         buf->c_iflag = iflag;
743         buf->c_lflag = lflag;
744         SetConsoleMode(TCB->inp, dwFlag);
745         TCB->term.Nttyb = *buf;
746     } else {
747         iflag = TCB->term.Nttyb.c_iflag;
748         lflag = TCB->term.Nttyb.c_lflag;
749         GetConsoleMode(TCB->inp, &dwFlag);
750
751         if (dwFlag & ENABLE_LINE_INPUT)
752             lflag |= ICANON;
753         else
754             lflag &= (tcflag_t) (~ICANON);
755
756         if (dwFlag & ENABLE_ECHO_INPUT)
757             lflag |= ECHO;
758         else
759             lflag &= (tcflag_t) (~ECHO);
760
761         if (dwFlag & ENABLE_PROCESSED_INPUT)
762             iflag |= BRKINT;
763         else
764             iflag &= (tcflag_t) (~BRKINT);
765
766         TCB->term.Nttyb.c_iflag = iflag;
767         TCB->term.Nttyb.c_lflag = lflag;
768
769         *buf = TCB->term.Nttyb;
770     }
771     return OK;
772 }
773
774 static int
775 drv_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
776 {
777     SCREEN *sp;
778     TERMINAL *_term = (TERMINAL *) TCB;
779     int code = ERR;
780
781     AssertTCB();
782     sp = TCB->csp;
783
784     T((T_CALLED("win32con::drv_mode(%p, prog=%d, def=%d)"), TCB, progFlag, defFlag));
785     PropOf(TCB)->progMode = progFlag;
786     PropOf(TCB)->lastOut = progFlag ? TCB->hdl : TCB->out;
787     SetConsoleActiveScreenBuffer(PropOf(TCB)->lastOut);
788
789     if (progFlag) /* prog mode */  {
790         if (defFlag) {
791             if ((drv_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
792                 _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS);
793                 code = OK;
794             }
795         } else {
796             /* reset_prog_mode */
797             if (drv_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
798                 if (sp) {
799                     if (sp->_keypad_on)
800                         _nc_keypad(sp, TRUE);
801                 }
802                 code = OK;
803             }
804         }
805     } else {                    /* shell mode */
806         if (defFlag) {
807             /* def_shell_mode */
808             if (drv_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
809                 code = OK;
810             }
811         } else {
812             /* reset_shell_mode */
813             if (sp) {
814                 _nc_keypad(sp, FALSE);
815                 NCURSES_SP_NAME(_nc_flush) (sp);
816             }
817             code = drv_sgmode(TCB, TRUE, &(_term->Ottyb));
818             if (!PropOf(TCB)->buffered) {
819                 if (!restore_original_screen(TCB))
820                     code = ERR;
821             }
822         }
823     }
824
825     return (code);
826 }
827
828 static void
829 drv_screen_init(SCREEN *sp GCC_UNUSED)
830 {
831 }
832
833 static void
834 drv_wrap(SCREEN *sp GCC_UNUSED)
835 {
836 }
837
838 static int
839 rkeycompare(const void *el1, const void *el2)
840 {
841     WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
842     WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
843
844     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
845 }
846
847 static int
848 keycompare(const void *el1, const void *el2)
849 {
850     WORD key1 = HIWORD((*((const LONG *) el1)));
851     WORD key2 = HIWORD((*((const LONG *) el2)));
852
853     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
854 }
855
856 static int
857 MapKey(TERMINAL_CONTROL_BLOCK * TCB, WORD vKey)
858 {
859     WORD nKey = 0;
860     void *res;
861     LONG key = GenMap(vKey, 0);
862     int code = -1;
863
864     AssertTCB();
865
866     res = bsearch(&key,
867                   PropOf(TCB)->map,
868                   (size_t) (N_INI + FKEYS),
869                   sizeof(keylist[0]),
870                   keycompare);
871     if (res) {
872         key = *((LONG *) res);
873         nKey = LOWORD(key);
874         code = (int) (nKey & 0x7fff);
875         if (nKey & 0x8000)
876             code = -code;
877     }
878     return code;
879 }
880
881 static void
882 drv_release(TERMINAL_CONTROL_BLOCK * TCB)
883 {
884     T((T_CALLED("win32con::drv_release(%p)"), TCB));
885
886     AssertTCB();
887     if (TCB->prop)
888         free(TCB->prop);
889
890     returnVoid;
891 }
892
893 /*
894  * Attempt to save the screen contents.  PDCurses does this if
895  * PDC_RESTORE_SCREEN is set, giving the same visual appearance on restoration
896  * as if the library had allocated a console buffer.
897  */
898 static bool
899 save_original_screen(TERMINAL_CONTROL_BLOCK * TCB)
900 {
901     bool result = FALSE;
902     Properties *p = PropOf(TCB);
903     COORD bufferSize;
904     COORD bufferCoord;
905     SMALL_RECT readRegion;
906     size_t want;
907
908     bufferSize.X = p->SBI.dwSize.X;
909     bufferSize.Y = p->SBI.dwSize.Y;
910     want = (size_t) (bufferSize.X * bufferSize.Y);
911
912     if ((p->save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
913         bufferCoord.X = bufferCoord.Y = 0;
914
915         readRegion.Top = 0;
916         readRegion.Left = 0;
917         readRegion.Bottom = (SHORT) (bufferSize.Y - 1);
918         readRegion.Right = (SHORT) (bufferSize.X - 1);
919
920         T(("... reading console buffer %dx%d into %d,%d - %d,%d at %d,%d",
921            bufferSize.Y, bufferSize.X,
922            readRegion.Top,
923            readRegion.Left,
924            readRegion.Bottom,
925            readRegion.Right,
926            bufferCoord.Y,
927            bufferCoord.X));
928
929         if (ReadConsoleOutput(TCB->hdl,
930                               p->save_screen,
931                               bufferSize,
932                               bufferCoord,
933                               &readRegion)) {
934             result = TRUE;
935         } else {
936             T((" error %#lx", (unsigned long) GetLastError()));
937             FreeAndNull(p->save_screen);
938
939             bufferSize.X = (SHORT) (p->SBI.srWindow.Right
940                                     - p->SBI.srWindow.Left + 1);
941             bufferSize.Y = (SHORT) (p->SBI.srWindow.Bottom
942                                     - p->SBI.srWindow.Top + 1);
943             want = (size_t) (bufferSize.X * bufferSize.Y);
944
945             if ((p->save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
946                 bufferCoord.X = bufferCoord.Y = 0;
947
948                 readRegion.Top = p->SBI.srWindow.Top;
949                 readRegion.Left = p->SBI.srWindow.Left;
950                 readRegion.Bottom = p->SBI.srWindow.Bottom;
951                 readRegion.Right = p->SBI.srWindow.Right;
952
953                 T(("... reading console window %dx%d into %d,%d - %d,%d at %d,%d",
954                    bufferSize.Y, bufferSize.X,
955                    readRegion.Top,
956                    readRegion.Left,
957                    readRegion.Bottom,
958                    readRegion.Right,
959                    bufferCoord.Y,
960                    bufferCoord.X));
961
962                 if (ReadConsoleOutput(TCB->hdl,
963                                       p->save_screen,
964                                       bufferSize,
965                                       bufferCoord,
966                                       &readRegion)) {
967                     result = TRUE;
968                     p->window_only = TRUE;
969                 } else {
970                     T((" error %#lx", (unsigned long) GetLastError()));
971                 }
972             }
973         }
974     }
975
976     T(("... save original screen contents %s", result ? "ok" : "err"));
977     return result;
978 }
979
980 static void
981 drv_init(TERMINAL_CONTROL_BLOCK * TCB)
982 {
983     DWORD num_buttons;
984
985     T((T_CALLED("win32con::drv_init(%p)"), TCB));
986
987     AssertTCB();
988
989     if (TCB) {
990         BOOL b = AllocConsole();
991         WORD a;
992         int i;
993         bool buffered = TRUE;
994
995         if (!b)
996             b = AttachConsole(ATTACH_PARENT_PROCESS);
997
998         TCB->inp = GetStdHandle(STD_INPUT_HANDLE);
999         TCB->out = GetStdHandle(STD_OUTPUT_HANDLE);
1000
1001         if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
1002             TCB->hdl = TCB->out;
1003             buffered = FALSE;
1004         } else {
1005             TCB->hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
1006                                                  0,
1007                                                  NULL,
1008                                                  CONSOLE_TEXTMODE_BUFFER,
1009                                                  NULL);
1010         }
1011
1012         if (InvalidConsoleHandle(TCB->hdl)) {
1013             returnVoid;
1014         } else if ((TCB->prop = typeCalloc(Properties, 1)) != 0) {
1015             PropOf(TCB)->buffered = buffered;
1016             PropOf(TCB)->window_only = FALSE;
1017             if (!get_SBI(TCB)) {
1018                 FreeAndNull(TCB->prop);         /* force error in drv_size */
1019                 returnVoid;
1020             }
1021             if (!buffered) {
1022                 if (!save_original_screen(TCB)) {
1023                     FreeAndNull(TCB->prop);     /* force error in drv_size */
1024                     returnVoid;
1025                 }
1026             }
1027         }
1028
1029         TCB->info.initcolor = TRUE;
1030         TCB->info.canchange = FALSE;
1031         TCB->info.hascolor = TRUE;
1032         TCB->info.caninit = TRUE;
1033
1034         TCB->info.maxpairs = NUMPAIRS;
1035         TCB->info.maxcolors = 8;
1036         TCB->info.numlabels = 0;
1037         TCB->info.labelwidth = 0;
1038         TCB->info.labelheight = 0;
1039         TCB->info.nocolorvideo = 1;
1040         TCB->info.tabsize = 8;
1041
1042         if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
1043             T(("mouse has %ld buttons", num_buttons));
1044             TCB->info.numbuttons = (int) num_buttons;
1045         } else {
1046             TCB->info.numbuttons = 1;
1047         }
1048
1049         TCB->info.defaultPalette = _nc_cga_palette;
1050
1051         for (i = 0; i < (N_INI + FKEYS); i++) {
1052             if (i < N_INI)
1053                 PropOf(TCB)->rmap[i] = PropOf(TCB)->map[i] = (DWORD) keylist[i];
1054             else
1055                 PropOf(TCB)->rmap[i] = PropOf(TCB)->map[i] =
1056                     (DWORD) GenMap((VK_F1 + (i - N_INI)), (KEY_F(1) + (i - N_INI)));
1057         }
1058         qsort(PropOf(TCB)->map,
1059               (size_t) (MAPSIZE),
1060               sizeof(keylist[0]),
1061               keycompare);
1062         qsort(PropOf(TCB)->rmap,
1063               (size_t) (MAPSIZE),
1064               sizeof(keylist[0]),
1065               rkeycompare);
1066
1067         a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
1068         for (i = 0; i < NUMPAIRS; i++)
1069             PropOf(TCB)->pairs[i] = a;
1070     }
1071     returnVoid;
1072 }
1073
1074 static void
1075 drv_initpair(TERMINAL_CONTROL_BLOCK * TCB,
1076              int pair,
1077              int f,
1078              int b)
1079 {
1080     SCREEN *sp;
1081
1082     AssertTCB();
1083     SetSP();
1084
1085     if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
1086         && (b >= 0) && (b < 8)) {
1087         PropOf(TCB)->pairs[pair] = MapColor(true, f) | MapColor(false, b);
1088     }
1089 }
1090
1091 static void
1092 drv_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
1093               int color GCC_UNUSED,
1094               int r GCC_UNUSED,
1095               int g GCC_UNUSED,
1096               int b GCC_UNUSED)
1097 {
1098     SCREEN *sp;
1099
1100     AssertTCB();
1101     SetSP();
1102 }
1103
1104 static void
1105 drv_do_color(TERMINAL_CONTROL_BLOCK * TCB,
1106              int old_pair GCC_UNUSED,
1107              int pair GCC_UNUSED,
1108              int reverse GCC_UNUSED,
1109              int (*outc) (SCREEN *, int) GCC_UNUSED
1110 )
1111 {
1112     SCREEN *sp;
1113
1114     AssertTCB();
1115     SetSP();
1116 }
1117
1118 static void
1119 drv_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
1120 {
1121     SCREEN *sp;
1122
1123     AssertTCB();
1124     SetSP();
1125
1126     sp->_mouse_type = M_TERM_DRIVER;
1127 }
1128
1129 static int
1130 drv_testmouse(TERMINAL_CONTROL_BLOCK * TCB, int delay)
1131 {
1132     int rc = 0;
1133     SCREEN *sp;
1134
1135     AssertTCB();
1136     SetSP();
1137
1138     if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1139         rc = TW_MOUSE;
1140     } else {
1141         rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
1142                                       TWAIT_MASK,
1143                                       delay,
1144                                       (int *) 0
1145                                       EVENTLIST_2nd(evl));
1146     }
1147
1148     return rc;
1149 }
1150
1151 static int
1152 drv_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
1153           int yold GCC_UNUSED, int xold GCC_UNUSED,
1154           int y, int x)
1155 {
1156     int ret = ERR;
1157     if (okConsoleHandle(TCB)) {
1158         Properties *p = PropOf(TCB);
1159         COORD loc;
1160         loc.X = (short) x;
1161         loc.Y = (short) (y + AdjustY(p));
1162         SetConsoleCursorPosition(TCB->hdl, loc);
1163         ret = OK;
1164     }
1165     return ret;
1166 }
1167
1168 static void
1169 drv_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
1170             int labnum GCC_UNUSED,
1171             char *text GCC_UNUSED)
1172 {
1173     SCREEN *sp;
1174
1175     AssertTCB();
1176     SetSP();
1177 }
1178
1179 static void
1180 drv_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1181                  int OnFlag GCC_UNUSED)
1182 {
1183     SCREEN *sp;
1184
1185     AssertTCB();
1186     SetSP();
1187 }
1188
1189 static chtype
1190 drv_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1191 {
1192     chtype res = A_NORMAL;
1193     res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1194     return res;
1195 }
1196
1197 static void
1198 drv_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1199 {
1200     SCREEN *sp;
1201
1202     AssertTCB();
1203     SetSP();
1204 }
1205
1206 static void
1207 drv_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1208             chtype *real_map GCC_UNUSED,
1209             chtype *fake_map GCC_UNUSED)
1210 {
1211 #define DATA(a,b) { a, b }
1212     static struct {
1213         int acs_code;
1214         int use_code;
1215     } table[] = {
1216         DATA('a', 0xb1),        /* ACS_CKBOARD  */
1217             DATA('f', 0xf8),    /* ACS_DEGREE   */
1218             DATA('g', 0xf1),    /* ACS_PLMINUS  */
1219             DATA('j', 0xd9),    /* ACS_LRCORNER */
1220             DATA('l', 0xda),    /* ACS_ULCORNER */
1221             DATA('k', 0xbf),    /* ACS_URCORNER */
1222             DATA('m', 0xc0),    /* ACS_LLCORNER */
1223             DATA('n', 0xc5),    /* ACS_PLUS     */
1224             DATA('q', 0xc4),    /* ACS_HLINE    */
1225             DATA('t', 0xc3),    /* ACS_LTEE     */
1226             DATA('u', 0xb4),    /* ACS_RTEE     */
1227             DATA('v', 0xc1),    /* ACS_BTEE     */
1228             DATA('w', 0xc2),    /* ACS_TTEE     */
1229             DATA('x', 0xb3),    /* ACS_VLINE    */
1230             DATA('y', 0xf3),    /* ACS_LEQUAL   */
1231             DATA('z', 0xf2),    /* ACS_GEQUAL   */
1232             DATA('0', 0xdb),    /* ACS_BLOCK    */
1233             DATA('{', 0xe3),    /* ACS_PI       */
1234             DATA('}', 0x9c),    /* ACS_STERLING */
1235             DATA(',', 0xae),    /* ACS_LARROW   */
1236             DATA('+', 0xaf),    /* ACS_RARROW   */
1237             DATA('~', 0xf9),    /* ACS_BULLET   */
1238     };
1239 #undef DATA
1240     unsigned n;
1241
1242     SCREEN *sp;
1243     AssertTCB();
1244     SetSP();
1245
1246     for (n = 0; n < SIZEOF(table); ++n) {
1247         real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET;
1248         if (sp != 0)
1249             sp->_screen_acs_map[table[n].acs_code] = TRUE;
1250     }
1251 }
1252
1253 static ULONGLONG
1254 tdiff(FILETIME fstart, FILETIME fend)
1255 {
1256     ULARGE_INTEGER ustart;
1257     ULARGE_INTEGER uend;
1258     ULONGLONG diff;
1259
1260     ustart.LowPart = fstart.dwLowDateTime;
1261     ustart.HighPart = fstart.dwHighDateTime;
1262     uend.LowPart = fend.dwLowDateTime;
1263     uend.HighPart = fend.dwHighDateTime;
1264
1265     diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1266     return diff;
1267 }
1268
1269 static int
1270 Adjust(int milliseconds, int diff)
1271 {
1272     if (milliseconds == INFINITY)
1273         return milliseconds;
1274     milliseconds -= diff;
1275     if (milliseconds < 0)
1276         milliseconds = 0;
1277     return milliseconds;
1278 }
1279
1280 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1281                      FROM_LEFT_2ND_BUTTON_PRESSED | \
1282                      FROM_LEFT_3RD_BUTTON_PRESSED | \
1283                      FROM_LEFT_4TH_BUTTON_PRESSED | \
1284                      RIGHTMOST_BUTTON_PRESSED)
1285
1286 static int
1287 decode_mouse(TERMINAL_CONTROL_BLOCK * TCB, int mask)
1288 {
1289     SCREEN *sp;
1290     int result = 0;
1291
1292     AssertTCB();
1293     SetSP();
1294
1295     if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1296         result |= BUTTON1_PRESSED;
1297     if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1298         result |= BUTTON2_PRESSED;
1299     if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1300         result |= BUTTON3_PRESSED;
1301     if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1302         result |= BUTTON4_PRESSED;
1303
1304     if (mask & RIGHTMOST_BUTTON_PRESSED) {
1305         switch (TCB->info.numbuttons) {
1306         case 1:
1307             result |= BUTTON1_PRESSED;
1308             break;
1309         case 2:
1310             result |= BUTTON2_PRESSED;
1311             break;
1312         case 3:
1313             result |= BUTTON3_PRESSED;
1314             break;
1315         case 4:
1316             result |= BUTTON4_PRESSED;
1317             break;
1318         }
1319     }
1320
1321     return result;
1322 }
1323
1324 static int
1325 drv_twait(TERMINAL_CONTROL_BLOCK * TCB,
1326           int mode,
1327           int milliseconds,
1328           int *timeleft
1329           EVENTLIST_2nd(_nc_eventlist * evl))
1330 {
1331     SCREEN *sp;
1332     INPUT_RECORD inp_rec;
1333     BOOL b;
1334     DWORD nRead = 0, rc = (DWORD) (-1);
1335     int code = 0;
1336     FILETIME fstart;
1337     FILETIME fend;
1338     int diff;
1339     bool isImmed = (milliseconds == 0);
1340
1341 #define CONSUME() ReadConsoleInput(TCB->inp,&inp_rec,1,&nRead)
1342
1343     AssertTCB();
1344     SetSP();
1345
1346     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1347                       milliseconds, mode));
1348
1349     if (milliseconds < 0)
1350         milliseconds = INFINITY;
1351
1352     memset(&inp_rec, 0, sizeof(inp_rec));
1353
1354     while (true) {
1355         GetSystemTimeAsFileTime(&fstart);
1356         rc = WaitForSingleObject(TCB->inp, (DWORD) milliseconds);
1357         GetSystemTimeAsFileTime(&fend);
1358         diff = (int) tdiff(fstart, fend);
1359         milliseconds = Adjust(milliseconds, diff);
1360
1361         if (!isImmed && milliseconds == 0)
1362             break;
1363
1364         if (rc == WAIT_OBJECT_0) {
1365             if (mode) {
1366                 b = GetNumberOfConsoleInputEvents(TCB->inp, &nRead);
1367                 if (b && nRead > 0) {
1368                     b = PeekConsoleInput(TCB->inp, &inp_rec, 1, &nRead);
1369                     if (b && nRead > 0) {
1370                         switch (inp_rec.EventType) {
1371                         case KEY_EVENT:
1372                             if (mode & TW_INPUT) {
1373                                 WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1374                                 char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1375
1376                                 if (inp_rec.Event.KeyEvent.bKeyDown) {
1377                                     if (0 == ch) {
1378                                         int nKey = MapKey(TCB, vk);
1379                                         if ((nKey < 0) || FALSE == sp->_keypad_on) {
1380                                             CONSUME();
1381                                             continue;
1382                                         }
1383                                     }
1384                                     code = TW_INPUT;
1385                                     goto end;
1386                                 } else {
1387                                     CONSUME();
1388                                 }
1389                             }
1390                             continue;
1391                         case MOUSE_EVENT:
1392                             if (decode_mouse(TCB,
1393                                              (inp_rec.Event.MouseEvent.dwButtonState
1394                                               & BUTTON_MASK)) == 0) {
1395                                 CONSUME();
1396                             } else if (mode & TW_MOUSE) {
1397                                 code = TW_MOUSE;
1398                                 goto end;
1399                             }
1400                             continue;
1401                         default:
1402                             selectActiveHandle(TCB);
1403                             continue;
1404                         }
1405                     }
1406                 }
1407             }
1408             continue;
1409         } else {
1410             if (rc != WAIT_TIMEOUT) {
1411                 code = -1;
1412                 break;
1413             } else {
1414                 code = 0;
1415                 break;
1416             }
1417         }
1418     }
1419   end:
1420
1421     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1422                       code, errno, milliseconds));
1423
1424     if (timeleft)
1425         *timeleft = milliseconds;
1426
1427     return code;
1428 }
1429
1430 static bool
1431 handle_mouse(TERMINAL_CONTROL_BLOCK * TCB, MOUSE_EVENT_RECORD mer)
1432 {
1433     SCREEN *sp;
1434     MEVENT work;
1435     bool result = FALSE;
1436
1437     AssertTCB();
1438     SetSP();
1439
1440     sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1441     sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1442
1443     /*
1444      * We're only interested if the button is pressed or released.
1445      * FIXME: implement continuous event-tracking.
1446      */
1447     if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1448         Properties *p = PropOf(TCB);
1449
1450         memset(&work, 0, sizeof(work));
1451
1452         if (sp->_drv_mouse_new_buttons) {
1453
1454             work.bstate |= (mmask_t) decode_mouse(TCB, sp->_drv_mouse_new_buttons);
1455
1456         } else {
1457
1458             /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1459             work.bstate |= (mmask_t) (decode_mouse(TCB,
1460                                                    sp->_drv_mouse_old_buttons)
1461                                       >> 1);
1462
1463             result = TRUE;
1464         }
1465
1466         work.x = mer.dwMousePosition.X;
1467         work.y = mer.dwMousePosition.Y - AdjustY(p);
1468
1469         sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1470         sp->_drv_mouse_tail += 1;
1471     }
1472
1473     return result;
1474 }
1475
1476 static int
1477 drv_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1478 {
1479     SCREEN *sp;
1480     int n = 1;
1481     INPUT_RECORD inp_rec;
1482     BOOL b;
1483     DWORD nRead;
1484     WORD vk;
1485
1486     AssertTCB();
1487     assert(buf);
1488     SetSP();
1489
1490     memset(&inp_rec, 0, sizeof(inp_rec));
1491
1492     T((T_CALLED("win32con::drv_read(%p)"), TCB));
1493     while ((b = ReadConsoleInput(TCB->inp, &inp_rec, 1, &nRead))) {
1494         if (b && nRead > 0) {
1495             if (inp_rec.EventType == KEY_EVENT) {
1496                 if (!inp_rec.Event.KeyEvent.bKeyDown)
1497                     continue;
1498                 *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
1499                 vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1500                 if (*buf == 0) {
1501                     if (sp->_keypad_on) {
1502                         *buf = MapKey(TCB, vk);
1503                         if (0 > (*buf))
1504                             continue;
1505                         else
1506                             break;
1507                     } else
1508                         continue;
1509                 } else {        /* *buf != 0 */
1510                     break;
1511                 }
1512             } else if (inp_rec.EventType == MOUSE_EVENT) {
1513                 if (handle_mouse(TCB, inp_rec.Event.MouseEvent)) {
1514                     *buf = KEY_MOUSE;
1515                     break;
1516                 }
1517             }
1518             continue;
1519         }
1520     }
1521     returnCode(n);
1522 }
1523
1524 static int
1525 drv_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1526 {
1527     T((T_CALLED("win32con::drv_nap(%p, %d)"), TCB, ms));
1528     Sleep((DWORD) ms);
1529     returnCode(OK);
1530 }
1531
1532 static bool
1533 drv_kyExist(TERMINAL_CONTROL_BLOCK * TCB, int keycode)
1534 {
1535     SCREEN *sp;
1536     WORD nKey;
1537     void *res;
1538     bool found = FALSE;
1539     LONG key = GenMap(0, (WORD) keycode);
1540
1541     AssertTCB();
1542     SetSP();
1543
1544     AssertTCB();
1545
1546     T((T_CALLED("win32con::drv_kyExist(%p, %d)"), TCB, keycode));
1547     res = bsearch(&key,
1548                   PropOf(TCB)->rmap,
1549                   (size_t) (N_INI + FKEYS),
1550                   sizeof(keylist[0]),
1551                   rkeycompare);
1552     if (res) {
1553         key = *((LONG *) res);
1554         nKey = LOWORD(key);
1555         if (!(nKey & 0x8000))
1556             found = TRUE;
1557     }
1558     returnCode(found);
1559 }
1560
1561 static int
1562 drv_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1563 {
1564     SCREEN *sp;
1565     int code = ERR;
1566
1567     AssertTCB();
1568     sp = TCB->csp;
1569
1570     T((T_CALLED("win32con::drv_kpad(%p, %d)"), TCB, flag));
1571     if (sp) {
1572         code = OK;
1573     }
1574     returnCode(code);
1575 }
1576
1577 static int
1578 drv_keyok(TERMINAL_CONTROL_BLOCK * TCB, int keycode, int flag)
1579 {
1580     int code = ERR;
1581     SCREEN *sp;
1582     WORD nKey;
1583     WORD vKey;
1584     void *res;
1585     LONG key = GenMap(0, (WORD) keycode);
1586
1587     AssertTCB();
1588     SetSP();
1589
1590     T((T_CALLED("win32con::drv_keyok(%p, %d, %d)"), TCB, keycode, flag));
1591     if (sp) {
1592         res = bsearch(&key,
1593                       PropOf(TCB)->rmap,
1594                       (size_t) (N_INI + FKEYS),
1595                       sizeof(keylist[0]),
1596                       rkeycompare);
1597         if (res) {
1598             key = *((LONG *) res);
1599             vKey = HIWORD(key);
1600             nKey = (LOWORD(key)) & 0x7fff;
1601             if (!flag)
1602                 nKey |= 0x8000;
1603             *(LONG *) res = GenMap(vKey, nKey);
1604         }
1605     }
1606     returnCode(code);
1607 }
1608
1609 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1610     FALSE,
1611         drv_CanHandle,          /* CanHandle */
1612         drv_init,               /* init */
1613         drv_release,            /* release */
1614         drv_size,               /* size */
1615         drv_sgmode,             /* sgmode */
1616         drv_conattr,            /* conattr */
1617         drv_mvcur,              /* hwcur */
1618         drv_mode,               /* mode */
1619         drv_rescol,             /* rescol */
1620         drv_rescolors,          /* rescolors */
1621         drv_setcolor,           /* color */
1622         drv_dobeepflash,        /* DoBeepFlash */
1623         drv_initpair,           /* initpair */
1624         drv_initcolor,          /* initcolor */
1625         drv_do_color,           /* docolor */
1626         drv_initmouse,          /* initmouse */
1627         drv_testmouse,          /* testmouse */
1628         drv_setfilter,          /* setfilter */
1629         drv_hwlabel,            /* hwlabel */
1630         drv_hwlabelOnOff,       /* hwlabelOnOff */
1631         drv_doupdate,           /* update */
1632         drv_defaultcolors,      /* defaultcolors */
1633         drv_print,              /* print */
1634         drv_size,               /* getsize */
1635         drv_setsize,            /* setsize */
1636         drv_initacs,            /* initacs */
1637         drv_screen_init,        /* scinit */
1638         drv_wrap,               /* scexit */
1639         drv_twait,              /* twait */
1640         drv_read,               /* read */
1641         drv_nap,                /* nap */
1642         drv_kpad,               /* kpad */
1643         drv_keyok,              /* kyOk */
1644         drv_kyExist             /* kyExist */
1645 };