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