ncurses 5.9 - patch 20120922
[ncurses.git] / ncurses / win32con / win_driver.c
1 /****************************************************************************
2  * Copyright (c) 1998-2010,2012 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  ****************************************************************************/
32
33 /*
34  * TODO - GetMousePos(POINT * result) from ntconio.c
35  * TODO - implement nodelay
36  */
37
38 #include <curses.priv.h>
39 #define CUR my_term.type.
40
41 MODULE_ID("$Id: win_driver.c,v 1.14 2012/09/22 19:15:14 tom Exp $")
42
43 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
44
45 #define AssertTCB() assert(TCB!=0 && TCB->magic==WINMAGIC)
46 #define SetSP() assert(TCB->csp!=0); sp = TCB->csp; (void) sp
47
48 #define GenMap(vKey,key) MAKELONG(key, vKey)
49
50 static const LONG keylist[] =
51 {
52     GenMap(VK_PRIOR, KEY_PPAGE),
53     GenMap(VK_NEXT, KEY_NPAGE),
54     GenMap(VK_END, KEY_END),
55     GenMap(VK_HOME, KEY_HOME),
56     GenMap(VK_LEFT, KEY_LEFT),
57     GenMap(VK_UP, KEY_UP),
58     GenMap(VK_RIGHT, KEY_RIGHT),
59     GenMap(VK_DOWN, KEY_DOWN),
60     GenMap(VK_DELETE, KEY_DC),
61     GenMap(VK_INSERT, KEY_IC)
62 };
63 #define N_INI ((int)(sizeof(keylist)/sizeof(keylist[0])))
64 #define FKEYS 24
65 #define MAPSIZE (FKEYS + N_INI)
66 #define NUMPAIRS 64
67
68 typedef struct props {
69     CONSOLE_SCREEN_BUFFER_INFO SBI;
70     bool progMode;
71     DWORD map[MAPSIZE];
72     DWORD rmap[MAPSIZE];
73     WORD pairs[NUMPAIRS];
74 } Properties;
75
76 #define PropOf(TCB) ((Properties*)TCB->prop)
77
78 int
79 _nc_mingw_ioctl(int fd GCC_UNUSED,
80                 long int request GCC_UNUSED,
81                 struct termios *arg GCC_UNUSED)
82 {
83     return 0;
84     endwin();
85     fprintf(stderr, "TERMINFO currently not supported on Windows.\n");
86     exit(1);
87 }
88
89 static WORD
90 MapColor(bool fore, int color)
91 {
92     static const int _cmap[] =
93     {0, 4, 2, 6, 1, 5, 3, 7};
94     int a;
95     if (color < 0 || color > 7)
96         a = fore ? 7 : 0;
97     else
98         a = _cmap[color];
99     if (!fore)
100         a = a << 4;
101     return (WORD) a;
102 }
103
104 static WORD
105 MapAttr(TERMINAL_CONTROL_BLOCK * TCB, WORD res, attr_t ch)
106 {
107     if (ch & A_COLOR) {
108         int p;
109         SCREEN *sp;
110
111         AssertTCB();
112         SetSP();
113         p = PairNumber(ch);
114         if (p > 0 && p < NUMPAIRS && TCB != 0 && sp != 0) {
115             WORD a;
116             a = PropOf(TCB)->pairs[p];
117             res = (res & 0xff00) | a;
118         }
119     }
120
121     if (ch & A_REVERSE)
122         res = ((res & 0xff00) | (((res & 0x07) << 4) | ((res & 0x70) >> 4)));
123
124     if (ch & A_STANDOUT)
125         res = ((res & 0xff00) | (((res & 0x07) << 4) | ((res & 0x70) >> 4))
126                | BACKGROUND_INTENSITY);
127
128     if (ch & A_BOLD)
129         res |= FOREGROUND_INTENSITY;
130
131     if (ch & A_DIM)
132         res |= BACKGROUND_INTENSITY;
133
134     return res;
135 }
136
137 #if USE_WIDEC_SUPPORT
138 /*
139  * TODO: support surrogate pairs
140  * TODO: support combining characters
141  * TODO: support acsc
142  * TODO: check wcwidth of base character, fill if needed for double-width
143  * TODO: _nc_wacs should be part of sp.
144  */
145 static BOOL
146 con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int n)
147 {
148     CHAR_INFO ci[n];
149     COORD loc, siz;
150     SMALL_RECT rec;
151     int i;
152     cchar_t ch;
153     SCREEN *sp;
154
155     AssertTCB();
156
157     if (TCB == 0 || InvalidConsoleHandle(TCB->hdl))
158         return FALSE;
159
160     SetSP();
161
162     for (i = 0; i < n; i++) {
163         ch = str[i];
164         ci[i].Char.UnicodeChar = CharOf(ch);
165         ci[i].Attributes = MapAttr(TCB,
166                                    PropOf(TCB)->SBI.wAttributes,
167                                    AttrOf(ch));
168         if (AttrOf(ch) & A_ALTCHARSET) {
169             if (_nc_wacs) {
170                 int which = CharOf(ch);
171                 if (which > 0
172                     && which < ACS_LEN
173                     && CharOf(_nc_wacs[which]) != 0) {
174                     ci[i].Char.UnicodeChar = CharOf(_nc_wacs[which]);
175                 } else {
176                     ci[i].Char.UnicodeChar = ' ';
177                 }
178             }
179         }
180     }
181
182     loc.X = (short) 0;
183     loc.Y = (short) 0;
184     siz.X = (short) n;
185     siz.Y = 1;
186
187     rec.Left = (short) x;
188     rec.Top = (short) y;
189     rec.Right = (short) (x + n - 1);
190     rec.Bottom = rec.Top;
191
192     return WriteConsoleOutputW(TCB->hdl, ci, siz, loc, &rec);
193 }
194 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
195 #else
196 static BOOL
197 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
198 {
199     CHAR_INFO ci[n];
200     COORD loc, siz;
201     SMALL_RECT rec;
202     int i;
203     chtype ch;
204     SCREEN *sp;
205
206     AssertTCB();
207
208     if (TCB == 0 || InvalidConsoleHandle(TCB->hdl))
209         return FALSE;
210
211     SetSP();
212
213     for (i = 0; i < n; i++) {
214         ch = str[i];
215         ci[i].Char.AsciiChar = ChCharOf(ch);
216         ci[i].Attributes = MapAttr(TCB,
217                                    PropOf(TCB)->SBI.wAttributes,
218                                    ChAttrOf(ch));
219         if (ChAttrOf(ch) & A_ALTCHARSET) {
220             if (sp->_acs_map)
221                 ci[i].Char.AsciiChar =
222                     ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
223         }
224     }
225
226     loc.X = (short) 0;
227     loc.Y = (short) 0;
228     siz.X = (short) n;
229     siz.Y = 1;
230
231     rec.Left = (short) x;
232     rec.Top = (short) y;
233     rec.Right = (short) (x + n - 1);
234     rec.Bottom = rec.Top;
235
236     return WriteConsoleOutput(TCB->hdl, ci, siz, loc, &rec);
237 }
238 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
239 #endif
240
241 #define MARK_NOCHANGE(win,row) \
242                 win->_line[row].firstchar = _NOCHANGE; \
243                 win->_line[row].lastchar  = _NOCHANGE
244
245 static int
246 drv_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
247 {
248     int y, nonempty, n, x0, x1, Width, Height;
249     SCREEN *sp;
250
251     AssertTCB();
252     SetSP();
253
254     Width = screen_columns(sp);
255     Height = screen_lines(sp);
256     nonempty = min(Height, NewScreen(sp)->_maxy + 1);
257
258     if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
259         int x;
260 #if USE_WIDEC_SUPPORT
261         cchar_t empty[Width];
262         wchar_t blank[2] =
263         {
264             L' ', L'\0'
265         };
266
267         for (x = 0; x < Width; x++)
268             setcchar(&empty[x], blank, 0, 0, 0);
269 #else
270         chtype empty[Width];
271
272         for (x = 0; x < Width; x++)
273             empty[x] = ' ';
274 #endif
275
276         for (y = 0; y < nonempty; y++) {
277             con_write(TCB, y, 0, empty, Width);
278             memcpy(empty,
279                    CurScreen(sp)->_line[y].text,
280                    Width * sizeof(empty[0]));
281         }
282         CurScreen(sp)->_clear = FALSE;
283         NewScreen(sp)->_clear = FALSE;
284         touchwin(NewScreen(sp));
285     }
286
287     for (y = 0; y < nonempty; y++) {
288         x0 = NewScreen(sp)->_line[y].firstchar;
289         if (x0 != _NOCHANGE) {
290             x1 = NewScreen(sp)->_line[y].lastchar;
291             n = x1 - x0 + 1;
292             if (n > 0) {
293                 memcpy(&CurScreen(sp)->_line[y].text[x0],
294                        &NewScreen(sp)->_line[y].text[x0],
295                        n * sizeof(CurScreen(sp)->_line[y].text[x0]));
296                 con_write(TCB,
297                           y,
298                           x0,
299                           &CurScreen(sp)->_line[y].text[x0], n);
300
301                 /* mark line changed successfully */
302                 if (y <= NewScreen(sp)->_maxy) {
303                     MARK_NOCHANGE(NewScreen(sp), y);
304                 }
305                 if (y <= CurScreen(sp)->_maxy) {
306                     MARK_NOCHANGE(CurScreen(sp), y);
307                 }
308             }
309         }
310     }
311
312     /* put everything back in sync */
313     for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
314         MARK_NOCHANGE(NewScreen(sp), y);
315     }
316     for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
317         MARK_NOCHANGE(CurScreen(sp), y);
318     }
319
320     if (!NewScreen(sp)->_leaveok) {
321         CurScreen(sp)->_curx = NewScreen(sp)->_curx;
322         CurScreen(sp)->_cury = NewScreen(sp)->_cury;
323
324         TCB->drv->hwcur(TCB, 0, 0, CurScreen(sp)->_cury, CurScreen(sp)->_curx);
325     }
326     SetConsoleActiveScreenBuffer(TCB->hdl);
327     return OK;
328 }
329
330 static bool
331 drv_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
332               const char *tname,
333               int *errret GCC_UNUSED)
334 {
335     bool code = FALSE;
336
337     T((T_CALLED("win32con::drv_CanHandle(%p)"), TCB));
338
339     assert(TCB != 0);
340     assert(tname != 0);
341
342     TCB->magic = WINMAGIC;
343     if (*tname == 0 || *tname == 0 || *tname == '#') {
344         code = TRUE;
345     } else {
346         TERMINAL my_term;
347         int status;
348
349         code = FALSE;
350 #if (USE_DATABASE || USE_TERMCAP)
351         status = _nc_setup_tinfo(tname, &my_term.type);
352 #else
353         status = TGETENT_NO;
354 #endif
355         if (status != TGETENT_YES) {
356             const TERMTYPE *fallback = _nc_fallback(tname);
357
358             if (fallback) {
359                 my_term.type = *fallback;
360                 status = TGETENT_YES;
361             } else if (!strcmp(tname, "unknown")) {
362                 code = TRUE;
363             }
364         }
365         if (status == TGETENT_YES) {
366             if (generic_type || hard_copy)
367                 code = TRUE;
368         }
369     }
370
371     if (code) {
372         if ((TCB->term.type.Booleans) == 0) {
373             _nc_init_entry(&(TCB->term.type));
374         }
375     }
376
377     returnBool(code);
378 }
379
380 static int
381 drv_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
382                 int beepFlag GCC_UNUSED)
383 {
384     SCREEN *sp;
385     int res = ERR;
386
387     AssertTCB();
388     SetSP();
389
390     return res;
391 }
392
393 static int
394 drv_print(TERMINAL_CONTROL_BLOCK * TCB,
395           char *data GCC_UNUSED,
396           int len GCC_UNUSED)
397 {
398     SCREEN *sp;
399
400     AssertTCB();
401     SetSP();
402
403     return ERR;
404 }
405
406 static int
407 drv_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
408                   int fg GCC_UNUSED,
409                   int bg GCC_UNUSED)
410 {
411     SCREEN *sp;
412     int code = ERR;
413
414     AssertTCB();
415     SetSP();
416
417     return (code);
418 }
419
420 static void
421 drv_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
422              int fore,
423              int color,
424              int (*outc) (SCREEN *, int) GCC_UNUSED)
425 {
426     AssertTCB();
427
428     if (TCB && !InvalidConsoleHandle(TCB->hdl)) {
429         WORD a = MapColor(fore, color);
430         a = ((PropOf(TCB)->SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f)) | a;
431         SetConsoleTextAttribute(TCB->hdl, a);
432         GetConsoleScreenBufferInfo(TCB->hdl, &(PropOf(TCB)->SBI));
433     }
434 }
435
436 static bool
437 drv_rescol(TERMINAL_CONTROL_BLOCK * TCB)
438 {
439     bool res = FALSE;
440
441     AssertTCB();
442     if (TCB && !InvalidConsoleHandle(TCB->hdl)) {
443         WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
444         SetConsoleTextAttribute(TCB->hdl, a);
445         GetConsoleScreenBufferInfo(TCB->hdl, &(PropOf(TCB)->SBI));
446         res = TRUE;
447     }
448     return res;
449 }
450
451 static bool
452 drv_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
453 {
454     int result = FALSE;
455     SCREEN *sp;
456
457     AssertTCB();
458     SetSP();
459
460     return result;
461 }
462
463 static int
464 drv_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
465 {
466     AssertTCB();
467
468     if (TCB == NULL || Lines == NULL || Cols == NULL || InvalidConsoleHandle(TCB->hdl))
469         return ERR;
470
471     *Lines = (int) (PropOf(TCB)->SBI.dwSize.Y);
472     *Cols = (int) (PropOf(TCB)->SBI.dwSize.X);
473     return OK;
474 }
475
476 static int
477 drv_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
478             int l GCC_UNUSED,
479             int c GCC_UNUSED)
480 {
481     AssertTCB();
482     return ERR;
483 }
484
485 static int
486 drv_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
487 {
488     DWORD dwFlag = 0;
489     tcflag_t iflag;
490     tcflag_t lflag;
491
492     AssertTCB();
493
494     if (TCB == 0 || buf == NULL)
495         return ERR;
496
497     if (setFlag) {
498         iflag = buf->c_iflag;
499         lflag = buf->c_lflag;
500
501         GetConsoleMode(TCB->inp, &dwFlag);
502
503         if (lflag & ICANON)
504             dwFlag |= ENABLE_LINE_INPUT;
505         else
506             dwFlag &= ~ENABLE_LINE_INPUT;
507
508         if (lflag & ECHO)
509             dwFlag |= ENABLE_ECHO_INPUT;
510         else
511             dwFlag &= ~ENABLE_ECHO_INPUT;
512
513         if (iflag & BRKINT)
514             dwFlag |= ENABLE_PROCESSED_INPUT;
515         else
516             dwFlag &= ~ENABLE_PROCESSED_INPUT;
517
518         dwFlag |= ENABLE_MOUSE_INPUT;
519
520         buf->c_iflag = iflag;
521         buf->c_lflag = lflag;
522         SetConsoleMode(TCB->inp, dwFlag);
523         TCB->term.Nttyb = *buf;
524     } else {
525         iflag = TCB->term.Nttyb.c_iflag;
526         lflag = TCB->term.Nttyb.c_lflag;
527         GetConsoleMode(TCB->inp, &dwFlag);
528
529         if (dwFlag & ENABLE_LINE_INPUT)
530             lflag |= ICANON;
531         else
532             lflag &= ~ICANON;
533
534         if (dwFlag & ENABLE_ECHO_INPUT)
535             lflag |= ECHO;
536         else
537             lflag &= ~ECHO;
538
539         if (dwFlag & ENABLE_PROCESSED_INPUT)
540             iflag |= BRKINT;
541         else
542             iflag &= ~BRKINT;
543
544         TCB->term.Nttyb.c_iflag = iflag;
545         TCB->term.Nttyb.c_lflag = lflag;
546
547         *buf = TCB->term.Nttyb;
548     }
549     return OK;
550 }
551
552 static int
553 drv_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
554 {
555     SCREEN *sp;
556     TERMINAL *_term = (TERMINAL *) TCB;
557     int code = ERR;
558
559     AssertTCB();
560     sp = TCB->csp;
561
562     PropOf(TCB)->progMode = progFlag;
563     SetConsoleActiveScreenBuffer(progFlag ? TCB->hdl : TCB->out);
564
565     if (progFlag) /* prog mode */  {
566         if (defFlag) {
567             if ((drv_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
568                 _term->Nttyb.c_oflag &= ~OFLAGS_TABS;
569                 code = OK;
570             }
571         } else {
572             /* reset_prog_mode */
573             if (drv_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
574                 if (sp) {
575                     if (sp->_keypad_on)
576                         _nc_keypad(sp, TRUE);
577                     NC_BUFFERED(sp, TRUE);
578                 }
579                 code = OK;
580             }
581         }
582     } else {                    /* shell mode */
583         if (defFlag) {
584             /* def_shell_mode */
585             if (drv_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
586                 code = OK;
587             }
588         } else {
589             /* reset_shell_mode */
590             if (sp) {
591                 _nc_keypad(sp, FALSE);
592                 NCURSES_SP_NAME(_nc_flush) (sp);
593                 NC_BUFFERED(sp, FALSE);
594             }
595             code = drv_sgmode(TCB, TRUE, &(_term->Ottyb));
596         }
597     }
598
599     return (code);
600 }
601
602 static void
603 drv_screen_init(SCREEN *sp GCC_UNUSED)
604 {
605 }
606
607 static void
608 drv_wrap(SCREEN *sp GCC_UNUSED)
609 {
610 }
611
612 static int
613 rkeycompare(const void *el1, const void *el2)
614 {
615     WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
616     WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
617
618     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
619 }
620
621 static int
622 keycompare(const void *el1, const void *el2)
623 {
624     WORD key1 = HIWORD((*((const LONG *) el1)));
625     WORD key2 = HIWORD((*((const LONG *) el2)));
626
627     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
628 }
629
630 static int
631 MapKey(TERMINAL_CONTROL_BLOCK * TCB, WORD vKey)
632 {
633     WORD nKey = 0;
634     void *res;
635     LONG key = GenMap(vKey, 0);
636     int code = -1;
637
638     AssertTCB();
639
640     res = bsearch(&key,
641                   PropOf(TCB)->map,
642                   (size_t) (N_INI + FKEYS),
643                   sizeof(keylist[0]),
644                   keycompare);
645     if (res) {
646         key = *((LONG *) res);
647         nKey = LOWORD(key);
648         code = (int) (nKey & 0x7fff);
649         if (nKey & 0x8000)
650             code = -code;
651     }
652     return code;
653 }
654
655 static void
656 drv_release(TERMINAL_CONTROL_BLOCK * TCB)
657 {
658     T((T_CALLED("win32con::drv_release(%p)"), TCB));
659
660     AssertTCB();
661     if (TCB->prop)
662         free(TCB->prop);
663
664     returnVoid;
665 }
666
667 static void
668 drv_init(TERMINAL_CONTROL_BLOCK * TCB)
669 {
670     DWORD num_buttons;
671
672     T((T_CALLED("win32con::drv_init(%p)"), TCB));
673
674     AssertTCB();
675
676     if (TCB) {
677         BOOL b = AllocConsole();
678         WORD a;
679         int i;
680
681         if (!b)
682             b = AttachConsole(ATTACH_PARENT_PROCESS);
683
684         TCB->inp = GetStdHandle(STD_INPUT_HANDLE);
685         TCB->out = GetStdHandle(STD_OUTPUT_HANDLE);
686
687         if (getenv("NCGDB"))
688             TCB->hdl = TCB->out;
689         else
690             TCB->hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
691                                                  0,
692                                                  NULL,
693                                                  CONSOLE_TEXTMODE_BUFFER,
694                                                  NULL);
695
696         if (!InvalidConsoleHandle(TCB->hdl)) {
697             TCB->prop = typeCalloc(Properties, 1);
698             GetConsoleScreenBufferInfo(TCB->hdl, &(PropOf(TCB)->SBI));
699         }
700
701         TCB->info.initcolor = TRUE;
702         TCB->info.canchange = FALSE;
703         TCB->info.hascolor = TRUE;
704         TCB->info.caninit = TRUE;
705
706         TCB->info.maxpairs = NUMPAIRS;
707         TCB->info.maxcolors = 8;
708         TCB->info.numlabels = 0;
709         TCB->info.labelwidth = 0;
710         TCB->info.labelheight = 0;
711         TCB->info.nocolorvideo = 1;
712         TCB->info.tabsize = 8;
713
714         if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
715             T(("mouse has %ld buttons", num_buttons));
716             TCB->info.numbuttons = num_buttons;
717         } else {
718             TCB->info.numbuttons = 1;
719         }
720
721         TCB->info.defaultPalette = _nc_cga_palette;
722
723         for (i = 0; i < (N_INI + FKEYS); i++) {
724             if (i < N_INI)
725                 PropOf(TCB)->rmap[i] = PropOf(TCB)->map[i] = keylist[i];
726             else
727                 PropOf(TCB)->rmap[i] = PropOf(TCB)->map[i] =
728                     GenMap((VK_F1 + (i - N_INI)), (KEY_F(1) + (i - N_INI)));
729         }
730         qsort(PropOf(TCB)->map,
731               (size_t) (MAPSIZE),
732               sizeof(keylist[0]),
733               keycompare);
734         qsort(PropOf(TCB)->rmap,
735               (size_t) (MAPSIZE),
736               sizeof(keylist[0]),
737               rkeycompare);
738
739         a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
740         for (i = 0; i < NUMPAIRS; i++)
741             PropOf(TCB)->pairs[i] = a;
742     }
743     returnVoid;
744 }
745
746 static void
747 drv_initpair(TERMINAL_CONTROL_BLOCK * TCB,
748              int pair,
749              int f,
750              int b)
751 {
752     SCREEN *sp;
753
754     AssertTCB();
755     SetSP();
756
757     if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
758         && (b >= 0) && (b < 8)) {
759         PropOf(TCB)->pairs[pair] = MapColor(true, f) | MapColor(false, b);
760     }
761 }
762
763 static void
764 drv_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
765               int color GCC_UNUSED,
766               int r GCC_UNUSED,
767               int g GCC_UNUSED,
768               int b GCC_UNUSED)
769 {
770     SCREEN *sp;
771
772     AssertTCB();
773     SetSP();
774 }
775
776 static void
777 drv_do_color(TERMINAL_CONTROL_BLOCK * TCB,
778              int old_pair GCC_UNUSED,
779              int pair GCC_UNUSED,
780              int reverse GCC_UNUSED,
781              int (*outc) (SCREEN *, int) GCC_UNUSED
782 )
783 {
784     SCREEN *sp;
785
786     AssertTCB();
787     SetSP();
788 }
789
790 static void
791 drv_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
792 {
793     SCREEN *sp;
794
795     AssertTCB();
796     SetSP();
797
798     sp->_mouse_type = M_TERM_DRIVER;
799 }
800
801 static int
802 drv_testmouse(TERMINAL_CONTROL_BLOCK * TCB, int delay)
803 {
804     int rc = 0;
805     SCREEN *sp;
806
807     AssertTCB();
808     SetSP();
809
810     if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
811         rc = TW_MOUSE;
812     } else {
813         rc = TCBOf(sp)->drv->twait(TCBOf(sp),
814                                    TWAIT_MASK,
815                                    delay,
816                                    (int *) 0
817                                    EVENTLIST_2nd(evl));
818     }
819
820     return rc;
821 }
822
823 static int
824 drv_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
825           int yold GCC_UNUSED, int xold GCC_UNUSED,
826           int y, int x)
827 {
828     int ret = ERR;
829     if (TCB && !InvalidConsoleHandle(TCB->hdl)) {
830         COORD loc;
831         loc.X = (short) x;
832         loc.Y = (short) y;
833         SetConsoleCursorPosition(TCB->hdl, loc);
834         ret = OK;
835     }
836     return ret;
837 }
838
839 static void
840 drv_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
841             int labnum GCC_UNUSED,
842             char *text GCC_UNUSED)
843 {
844     SCREEN *sp;
845
846     AssertTCB();
847     SetSP();
848 }
849
850 static void
851 drv_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
852                  int OnFlag GCC_UNUSED)
853 {
854     SCREEN *sp;
855
856     AssertTCB();
857     SetSP();
858 }
859
860 static chtype
861 drv_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
862 {
863     chtype res = A_NORMAL;
864     res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
865     return res;
866 }
867
868 static void
869 drv_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
870 {
871     SCREEN *sp;
872
873     AssertTCB();
874     SetSP();
875 }
876
877 static void
878 drv_initacs(TERMINAL_CONTROL_BLOCK * TCB,
879             chtype *real_map GCC_UNUSED,
880             chtype *fake_map GCC_UNUSED)
881 {
882 #define DATA(a,b) { a, b }
883     static struct {
884         int acs_code;
885         int use_code;
886     } table[] = {
887         DATA('a', 0xb1),        /* ACS_CKBOARD  */
888             DATA('f', 0xf8),    /* ACS_DEGREE   */
889             DATA('g', 0xf1),    /* ACS_PLMINUS  */
890             DATA('j', 0xd9),    /* ACS_LRCORNER */
891             DATA('l', 0xda),    /* ACS_ULCORNER */
892             DATA('k', 0xbf),    /* ACS_URCORNER */
893             DATA('m', 0xc0),    /* ACS_LLCORNER */
894             DATA('n', 0xc5),    /* ACS_PLUS     */
895             DATA('q', 0xc4),    /* ACS_HLINE    */
896             DATA('t', 0xc3),    /* ACS_LTEE     */
897             DATA('u', 0xb4),    /* ACS_RTEE     */
898             DATA('v', 0xc1),    /* ACS_BTEE     */
899             DATA('w', 0xc2),    /* ACS_TTEE     */
900             DATA('x', 0xb3),    /* ACS_VLINE    */
901             DATA('y', 0xf3),    /* ACS_LEQUAL   */
902             DATA('z', 0xf2),    /* ACS_GEQUAL   */
903             DATA('0', 0xdb),    /* ACS_BLOCK    */
904             DATA('{', 0xe3),    /* ACS_PI       */
905             DATA('}', 0x9c),    /* ACS_STERLING */
906             DATA(',', 0xae),    /* ACS_LARROW   */
907             DATA('+', 0xaf),    /* ACS_RARROW   */
908             DATA('~', 0xf9),    /* ACS_BULLET   */
909     };
910 #undef DATA
911     unsigned n;
912
913     SCREEN *sp;
914     AssertTCB();
915     SetSP();
916
917     for (n = 0; n < SIZEOF(table); ++n) {
918         real_map[table[n].acs_code] = table[n].use_code | A_ALTCHARSET;
919         if (sp != 0)
920             sp->_screen_acs_map[table[n].acs_code] = TRUE;
921     }
922 }
923
924 static ULONGLONG
925 tdiff(FILETIME fstart, FILETIME fend)
926 {
927     ULARGE_INTEGER ustart;
928     ULARGE_INTEGER uend;
929     ULONGLONG diff;
930
931     ustart.LowPart = fstart.dwLowDateTime;
932     ustart.HighPart = fstart.dwHighDateTime;
933     uend.LowPart = fend.dwLowDateTime;
934     uend.HighPart = fend.dwHighDateTime;
935
936     diff = (uend.QuadPart - ustart.QuadPart) / 10000;
937     return diff;
938 }
939
940 static int
941 Adjust(int milliseconds, int diff)
942 {
943     if (milliseconds == INFINITY)
944         return milliseconds;
945     milliseconds -= diff;
946     if (milliseconds < 0)
947         milliseconds = 0;
948     return milliseconds;
949 }
950
951 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
952                      FROM_LEFT_2ND_BUTTON_PRESSED | \
953                      FROM_LEFT_3RD_BUTTON_PRESSED | \
954                      FROM_LEFT_4TH_BUTTON_PRESSED | \
955                      RIGHTMOST_BUTTON_PRESSED)
956
957 static int
958 decode_mouse(TERMINAL_CONTROL_BLOCK * TCB, int mask)
959 {
960     SCREEN *sp;
961     int result = 0;
962
963     AssertTCB();
964     SetSP();
965
966     if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
967         result |= BUTTON1_PRESSED;
968     if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
969         result |= BUTTON2_PRESSED;
970     if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
971         result |= BUTTON3_PRESSED;
972     if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
973         result |= BUTTON4_PRESSED;
974
975     if (mask & RIGHTMOST_BUTTON_PRESSED) {
976         switch (TCB->info.numbuttons) {
977         case 1:
978             result |= BUTTON1_PRESSED;
979             break;
980         case 2:
981             result |= BUTTON2_PRESSED;
982             break;
983         case 3:
984             result |= BUTTON3_PRESSED;
985             break;
986         case 4:
987             result |= BUTTON4_PRESSED;
988             break;
989         }
990     }
991
992     return result;
993 }
994
995 static int
996 drv_twait(TERMINAL_CONTROL_BLOCK * TCB,
997           int mode,
998           int milliseconds,
999           int *timeleft
1000           EVENTLIST_2nd(_nc_eventlist * evl))
1001 {
1002     SCREEN *sp;
1003     INPUT_RECORD inp_rec;
1004     BOOL b;
1005     DWORD nRead = 0, rc = -1;
1006     int code = 0;
1007     FILETIME fstart;
1008     FILETIME fend;
1009     int diff;
1010     bool isImmed = (milliseconds == 0);
1011
1012 #define CONSUME() ReadConsoleInput(TCB->inp,&inp_rec,1,&nRead)
1013
1014     AssertTCB();
1015     SetSP();
1016
1017     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1018                       milliseconds, mode));
1019
1020     if (milliseconds < 0)
1021         milliseconds = INFINITY;
1022
1023     memset(&inp_rec, 0, sizeof(inp_rec));
1024
1025     while (true) {
1026         GetSystemTimeAsFileTime(&fstart);
1027         rc = WaitForSingleObject(TCB->inp, milliseconds);
1028         GetSystemTimeAsFileTime(&fend);
1029         diff = (int) tdiff(fstart, fend);
1030         milliseconds = Adjust(milliseconds, diff);
1031
1032         if (!isImmed && milliseconds == 0)
1033             break;
1034
1035         if (rc == WAIT_OBJECT_0) {
1036             if (mode) {
1037                 b = GetNumberOfConsoleInputEvents(TCB->inp, &nRead);
1038                 if (b && nRead > 0) {
1039                     b = PeekConsoleInput(TCB->inp, &inp_rec, 1, &nRead);
1040                     if (b && nRead > 0) {
1041                         switch (inp_rec.EventType) {
1042                         case KEY_EVENT:
1043                             if (mode & TW_INPUT) {
1044                                 WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1045                                 char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1046
1047                                 if (inp_rec.Event.KeyEvent.bKeyDown) {
1048                                     if (0 == ch) {
1049                                         int nKey = MapKey(TCB, vk);
1050                                         if ((nKey < 0) || FALSE == sp->_keypad_on) {
1051                                             CONSUME();
1052                                             continue;
1053                                         }
1054                                     }
1055                                     code = TW_INPUT;
1056                                     goto end;
1057                                 } else {
1058                                     CONSUME();
1059                                 }
1060                             }
1061                             continue;
1062                         case MOUSE_EVENT:
1063                             if (decode_mouse(TCB,
1064                                              (inp_rec.Event.MouseEvent.dwButtonState
1065                                               & BUTTON_MASK)) == 0) {
1066                                 CONSUME();
1067                             } else if (mode & TW_MOUSE) {
1068                                 code = TW_MOUSE;
1069                                 goto end;
1070                             }
1071                             continue;
1072                         default:
1073                             SetConsoleActiveScreenBuffer(!PropOf(TCB)->progMode ?
1074                                                          TCB->hdl : TCB->out);
1075                             continue;
1076                         }
1077                     }
1078                 }
1079             }
1080             continue;
1081         } else {
1082             if (rc != WAIT_TIMEOUT) {
1083                 code = -1;
1084                 break;
1085             } else {
1086                 code = 0;
1087                 break;
1088             }
1089         }
1090     }
1091   end:
1092
1093     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1094                       code, errno, milliseconds));
1095
1096     if (timeleft)
1097         *timeleft = milliseconds;
1098
1099     return code;
1100 }
1101
1102 static bool
1103 handle_mouse(TERMINAL_CONTROL_BLOCK * TCB, MOUSE_EVENT_RECORD mer)
1104 {
1105     SCREEN *sp;
1106     MEVENT work;
1107     bool result = FALSE;
1108
1109     AssertTCB();
1110     SetSP();
1111
1112     sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1113     sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1114
1115     /*
1116      * We're only interested if the button is pressed or released.
1117      * FIXME: implement continuous event-tracking.
1118      */
1119     if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1120
1121         memset(&work, 0, sizeof(work));
1122
1123         if (sp->_drv_mouse_new_buttons) {
1124
1125             work.bstate |= decode_mouse(TCB, sp->_drv_mouse_new_buttons);
1126
1127         } else {
1128
1129             /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1130             work.bstate |= (decode_mouse(TCB, sp->_drv_mouse_old_buttons) >> 1);
1131
1132             result = TRUE;
1133         }
1134
1135         work.x = mer.dwMousePosition.X;
1136         work.y = mer.dwMousePosition.Y;
1137
1138         sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1139         sp->_drv_mouse_tail += 1;
1140     }
1141
1142     return result;
1143 }
1144
1145 static int
1146 drv_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1147 {
1148     SCREEN *sp;
1149     int n = 1;
1150     INPUT_RECORD inp_rec;
1151     BOOL b;
1152     DWORD nRead;
1153     WORD vk;
1154
1155     AssertTCB();
1156     assert(buf);
1157     SetSP();
1158
1159     memset(&inp_rec, 0, sizeof(inp_rec));
1160
1161     T((T_CALLED("win32con::drv_read(%p)"), TCB));
1162     while ((b = ReadConsoleInput(TCB->inp, &inp_rec, 1, &nRead))) {
1163         if (b && nRead > 0) {
1164             if (inp_rec.EventType == KEY_EVENT) {
1165                 if (!inp_rec.Event.KeyEvent.bKeyDown)
1166                     continue;
1167                 *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
1168                 vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1169                 if (*buf == 0) {
1170                     if (sp->_keypad_on) {
1171                         *buf = MapKey(TCB, vk);
1172                         if (0 > (*buf))
1173                             continue;
1174                         else
1175                             break;
1176                     } else
1177                         continue;
1178                 } else {        /* *buf != 0 */
1179                     break;
1180                 }
1181             } else if (inp_rec.EventType == MOUSE_EVENT) {
1182                 if (handle_mouse(TCB, inp_rec.Event.MouseEvent)) {
1183                     *buf = KEY_MOUSE;
1184                     break;
1185                 }
1186             }
1187             continue;
1188         }
1189     }
1190     returnCode(n);
1191 }
1192
1193 static int
1194 drv_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1195 {
1196     Sleep(ms);
1197     return OK;
1198 }
1199
1200 static bool
1201 drv_kyExist(TERMINAL_CONTROL_BLOCK * TCB, int keycode)
1202 {
1203     SCREEN *sp;
1204     WORD nKey;
1205     void *res;
1206     bool found = FALSE;
1207     LONG key = GenMap(0, (WORD) keycode);
1208
1209     AssertTCB();
1210     SetSP();
1211
1212     AssertTCB();
1213
1214     res = bsearch(&key,
1215                   PropOf(TCB)->rmap,
1216                   (size_t) (N_INI + FKEYS),
1217                   sizeof(keylist[0]),
1218                   rkeycompare);
1219     if (res) {
1220         key = *((LONG *) res);
1221         nKey = LOWORD(key);
1222         if (!(nKey & 0x8000))
1223             found = TRUE;
1224     }
1225     return found;
1226 }
1227
1228 static int
1229 drv_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1230 {
1231     SCREEN *sp;
1232     int code = ERR;
1233
1234     AssertTCB();
1235     sp = TCB->csp;
1236
1237     if (sp) {
1238         code = OK;
1239     }
1240     return code;
1241 }
1242
1243 static int
1244 drv_keyok(TERMINAL_CONTROL_BLOCK * TCB, int keycode, int flag)
1245 {
1246     int code = ERR;
1247     SCREEN *sp;
1248     WORD nKey;
1249     WORD vKey;
1250     void *res;
1251     LONG key = GenMap(0, (WORD) keycode);
1252
1253     AssertTCB();
1254     SetSP();
1255
1256     if (sp) {
1257         res = bsearch(&key,
1258                       PropOf(TCB)->rmap,
1259                       (size_t) (N_INI + FKEYS),
1260                       sizeof(keylist[0]),
1261                       rkeycompare);
1262         if (res) {
1263             key = *((LONG *) res);
1264             vKey = HIWORD(key);
1265             nKey = (LOWORD(key)) & 0x7fff;
1266             if (!flag)
1267                 nKey |= 0x8000;
1268             *(LONG *) res = GenMap(vKey, nKey);
1269         }
1270     }
1271     return code;
1272 }
1273
1274 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1275     FALSE,
1276         drv_CanHandle,          /* CanHandle */
1277         drv_init,               /* init */
1278         drv_release,            /* release */
1279         drv_size,               /* size */
1280         drv_sgmode,             /* sgmode */
1281         drv_conattr,            /* conattr */
1282         drv_mvcur,              /* hwcur */
1283         drv_mode,               /* mode */
1284         drv_rescol,             /* rescol */
1285         drv_rescolors,          /* rescolors */
1286         drv_setcolor,           /* color */
1287         drv_dobeepflash,        /* DoBeepFlash */
1288         drv_initpair,           /* initpair */
1289         drv_initcolor,          /* initcolor */
1290         drv_do_color,           /* docolor */
1291         drv_initmouse,          /* initmouse */
1292         drv_testmouse,          /* testmouse */
1293         drv_setfilter,          /* setfilter */
1294         drv_hwlabel,            /* hwlabel */
1295         drv_hwlabelOnOff,       /* hwlabelOnOff */
1296         drv_doupdate,           /* update */
1297         drv_defaultcolors,      /* defaultcolors */
1298         drv_print,              /* print */
1299         drv_size,               /* getsize */
1300         drv_setsize,            /* setsize */
1301         drv_initacs,            /* initacs */
1302         drv_screen_init,        /* scinit */
1303         drv_wrap,               /* scexit */
1304         drv_twait,              /* twait */
1305         drv_read,               /* read */
1306         drv_nap,                /* nap */
1307         drv_kpad,               /* kpad */
1308         drv_keyok,              /* kyOk */
1309         drv_kyExist             /* kyExist */
1310 };