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