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