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