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