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
42MODULE_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
57static 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
75typedef 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
89int
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
100static WORD
101MapColor(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
115static WORD
116MapAttr(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 */
156static BOOL
157con_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
209static BOOL
210con_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 */
257static int
258find_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 */
291static int
292find_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
330static void
331selectActiveHandle(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
339static int
340drv_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
459static bool
460drv_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
509static int
510drv_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
522static int
523drv_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
535static int
536drv_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
549static bool
550get_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
584static void
585drv_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
601static bool
602drv_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
616static bool
617drv_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
618{
619    int result = FALSE;
620    SCREEN *sp;
621
622    AssertTCB();
623    SetSP();
624
625    return result;
626}
627
628static int
629drv_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
655static int
656drv_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
664static int
665drv_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
731static int
732drv_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
782static void
783drv_screen_init(SCREEN *sp GCC_UNUSED)
784{
785}
786
787static void
788drv_wrap(SCREEN *sp GCC_UNUSED)
789{
790}
791
792static int
793rkeycompare(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
801static int
802keycompare(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
810static int
811MapKey(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
835static void
836drv_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 */
852static bool
853save_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
933static void
934drv_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
1026static void
1027drv_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
1043static void
1044drv_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
1056static void
1057drv_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
1070static void
1071drv_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
1072{
1073    SCREEN *sp;
1074
1075    AssertTCB();
1076    SetSP();
1077
1078    sp->_mouse_type = M_TERM_DRIVER;
1079}
1080
1081static int
1082drv_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
1103static int
1104drv_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
1120static void
1121drv_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
1131static void
1132drv_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1133		 int OnFlag GCC_UNUSED)
1134{
1135    SCREEN *sp;
1136
1137    AssertTCB();
1138    SetSP();
1139}
1140
1141static chtype
1142drv_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
1149static void
1150drv_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1151{
1152    SCREEN *sp;
1153
1154    AssertTCB();
1155    SetSP();
1156}
1157
1158static void
1159drv_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
1205static ULONGLONG
1206tdiff(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
1221static int
1222Adjust(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
1238static int
1239decode_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
1276static int
1277drv_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
1382static bool
1383handle_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
1428static int
1429drv_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
1476static int
1477drv_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
1484static bool
1485drv_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
1513static int
1514drv_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
1529static int
1530drv_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
1561NCURSES_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