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