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