1 /****************************************************************************
2  * Copyright 2018,2020 Thomas E. Dickey                                     *
3  * Copyright 2008-2016,2017 Free Software Foundation, Inc.                  *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 
30 /****************************************************************************
31  *  Author: Juergen Pfeifer                                                 *
32  *     and: Thomas E. Dickey                                                *
33  ****************************************************************************/
34 
35 /*
36  * TODO - improve screen-repainting performance, using implied wraparound to reduce write's
37  * TODO - make it optional whether screen is restored or not when non-buffered
38  */
39 
40 #include <curses.priv.h>
41 #ifdef _NC_WINDOWS
42 #if (defined(__MINGW32__) || defined(__MINGW64__))
43 #include <wchar.h>
44 #else
45 #include <tchar.h>
46 #endif
47 #include <io.h>
48 
49 #define CUR TerminalType(my_term).
50 
51 MODULE_ID("$Id: win32_driver.c,v 1.2 2020/11/21 23:35:56 tom Exp $")
52 
53 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
54 #define EXP_OPTIMIZE 0
55 
56 static bool console_initialized = FALSE;
57 
58 #define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC))
59 #define validateConsoleHandle() (AssertTCB() , console_initialized ||\
60                                  (console_initialized=\
61                                   _nc_console_checkinit(TRUE,FALSE)))
62 #define SetSP() assert(TCB->csp != 0); sp = TCB->csp; (void) sp
63 #define AdjustY() (WINCONSOLE.buffered ?\
64                    0 : (int) WINCONSOLE.SBI.srWindow.Top)
65 #define RevAttr(attr) (WORD) (((attr) & 0xff00) |   \
66                               ((((attr) & 0x07) << 4) | \
67                                (((attr) & 0x70) >> 4)))
68 
69 #if USE_WIDEC_SUPPORT
70 #define write_screen WriteConsoleOutputW
71 #define read_screen  ReadConsoleOutputW
72 #else
73 #define write_screen WriteConsoleOutput
74 #define read_screen  ReadConsoleOutput
75 #endif
76 
77 static WORD
78 MapAttr(WORD res, attr_t ch)
79 {
80     if (ch & A_COLOR) {
81 	int p;
82 
83 	p = PairNumber(ch);
84 	if (p > 0 && p < CON_NUMPAIRS) {
85 	    WORD a;
86 	    a = WINCONSOLE.pairs[p];
87 	    res = (WORD) ((res & 0xff00) | a);
88 	}
89     }
90 
91     if (ch & A_REVERSE) {
92 	res = RevAttr(res);
93     }
94 
95     if (ch & A_STANDOUT) {
96 	res = RevAttr(res) | BACKGROUND_INTENSITY;
97     }
98 
99     if (ch & A_BOLD)
100 	res |= FOREGROUND_INTENSITY;
101 
102     if (ch & A_DIM)
103 	res |= BACKGROUND_INTENSITY;
104 
105     return res;
106 }
107 
108 #if 0				/* def TRACE */
109 static void
110 dump_screen(const char *fn, int ln)
111 {
112     int max_cells = (WINCONSOLE.SBI.dwSize.Y *
113 		     (1 + WINCONSOLE.SBI.dwSize.X)) + 1;
114     char output[max_cells];
115     CHAR_INFO save_screen[max_cells];
116     COORD save_size;
117     SMALL_RECT save_region;
118     COORD bufferCoord;
119 
120     T(("dump_screen %s@%d", fn, ln));
121 
122     save_region.Top = WINCONSOLE.SBI.srWindow.Top;
123     save_region.Left = WINCONSOLE.SBI.srWindow.Left;
124     save_region.Bottom = WINCONSOLE.SBI.srWindow.Bottom;
125     save_region.Right = WINCONSOLE.SBI.srWindow.Right;
126 
127     save_size.X = (SHORT) (save_region.Right - save_region.Left + 1);
128     save_size.Y = (SHORT) (save_region.Bottom - save_region.Top + 1);
129 
130     bufferCoord.X = bufferCoord.Y = 0;
131 
132     if (read_screen(WINCONSOLE.hdl,
133 		    save_screen,
134 		    save_size,
135 		    bufferCoord,
136 		    &save_region)) {
137 	int i, j;
138 	int ij = 0;
139 	int k = 0;
140 
141 	for (i = save_region.Top; i <= save_region.Bottom; ++i) {
142 	    for (j = save_region.Left; j <= save_region.Right; ++j) {
143 		output[k++] = save_screen[ij++].Char.AsciiChar;
144 	    }
145 	    output[k++] = '\n';
146 	}
147 	output[k] = 0;
148 
149 	T(("DUMP: %d,%d - %d,%d",
150 	   save_region.Top,
151 	   save_region.Left,
152 	   save_region.Bottom,
153 	   save_region.Right));
154 	T(("%s", output));
155     }
156 }
157 
158 #else
159 #define dump_screen(fn,ln)	/* nothing */
160 #endif
161 
162 #if USE_WIDEC_SUPPORT
163 /*
164  * TODO: support surrogate pairs
165  * TODO: support combining characters
166  * TODO: support acsc
167  * TODO: _nc_wacs should be part of sp.
168  */
169 static BOOL
170 con_write16(TERMINAL_CONTROL_BLOCK * TCB,
171 	    int y, int x, cchar_t *str, int limit)
172 {
173     int actual = 0;
174     CHAR_INFO *ci = TypeAlloca(CHAR_INFO, limit);
175     COORD loc, siz;
176     SMALL_RECT rec;
177     int i;
178     cchar_t ch;
179     SCREEN *sp;
180 
181     AssertTCB();
182     SetSP();
183 
184     for (i = actual = 0; i < limit; i++) {
185 	ch = str[i];
186 	if (isWidecExt(ch))
187 	    continue;
188 	ci[actual].Char.UnicodeChar = CharOf(ch);
189 	ci[actual].Attributes = MapAttr(WINCONSOLE.SBI.wAttributes,
190 					AttrOf(ch));
191 	if (AttrOf(ch) & A_ALTCHARSET) {
192 	    if (_nc_wacs) {
193 		int which = CharOf(ch);
194 		if (which > 0
195 		    && which < ACS_LEN
196 		    && CharOf(_nc_wacs[which]) != 0) {
197 		    ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
198 		} else {
199 		    ci[actual].Char.UnicodeChar = ' ';
200 		}
201 	    }
202 	}
203 	++actual;
204     }
205 
206     loc.X = (SHORT) 0;
207     loc.Y = (SHORT) 0;
208     siz.X = (SHORT) actual;
209     siz.Y = 1;
210 
211     rec.Left = (SHORT) x;
212     rec.Top = (SHORT) (y + AdjustY());
213     rec.Right = (SHORT) (x + limit - 1);
214     rec.Bottom = rec.Top;
215 
216     return write_screen(WINCONSOLE.hdl, ci, siz, loc, &rec);
217 }
218 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
219 #else
220 static BOOL
221 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
222 {
223     CHAR_INFO *ci = TypeAlloca(CHAR_INFO, n);
224     COORD loc, siz;
225     SMALL_RECT rec;
226     int i;
227     chtype ch;
228     SCREEN *sp;
229 
230     AssertTCB();
231     SetSP();
232 
233     for (i = 0; i < n; i++) {
234 	ch = str[i];
235 	ci[i].Char.AsciiChar = ChCharOf(ch);
236 	ci[i].Attributes = MapAttr(WINCONSOLE.SBI.wAttributes,
237 				   ChAttrOf(ch));
238 	if (ChAttrOf(ch) & A_ALTCHARSET) {
239 	    if (sp->_acs_map)
240 		ci[i].Char.AsciiChar =
241 		ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
242 	}
243     }
244 
245     loc.X = (short) 0;
246     loc.Y = (short) 0;
247     siz.X = (short) n;
248     siz.Y = 1;
249 
250     rec.Left = (short) x;
251     rec.Top = (short) y;
252     rec.Right = (short) (x + n - 1);
253     rec.Bottom = rec.Top;
254 
255     return write_screen(WINCONSOLE.hdl, ci, siz, loc, &rec);
256 }
257 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
258 #endif
259 
260 #if EXP_OPTIMIZE
261 /*
262  * Comparing new/current screens, determine the last column-index for a change
263  * beginning on the given row,col position.  Unlike a serial terminal, there is
264  * no cost for "moving" the "cursor" on the line as we update it.
265  */
266 static int
267 find_end_of_change(SCREEN *sp, int row, int col)
268 {
269     int result = col;
270     struct ldat *curdat = CurScreen(sp)->_line + row;
271     struct ldat *newdat = NewScreen(sp)->_line + row;
272 
273     while (col <= newdat->lastchar) {
274 #if USE_WIDEC_SUPPORT
275 	if (isWidecExt(curdat->text[col]) ||
276 	    isWidecExt(newdat->text[col])) {
277 	    result = col;
278 	} else if (memcmp(&curdat->text[col],
279 			  &newdat->text[col],
280 			  sizeof(curdat->text[0]))) {
281 	    result = col;
282 	} else {
283 	    break;
284 	}
285 #else
286 	if (curdat->text[col] != newdat->text[col]) {
287 	    result = col;
288 	} else {
289 	    break;
290 	}
291 #endif
292 	++col;
293     }
294     return result;
295 }
296 
297 /*
298  * Given a row,col position at the end of a change-chunk, look for the
299  * beginning of the next change-chunk.
300  */
301 static int
302 find_next_change(SCREEN *sp, int row, int col)
303 {
304     struct ldat *curdat = CurScreen(sp)->_line + row;
305     struct ldat *newdat = NewScreen(sp)->_line + row;
306     int result = newdat->lastchar + 1;
307 
308     while (++col <= newdat->lastchar) {
309 #if USE_WIDEC_SUPPORT
310 	if (isWidecExt(curdat->text[col]) !=
311 	    isWidecExt(newdat->text[col])) {
312 	    result = col;
313 	    break;
314 	} else if (memcmp(&curdat->text[col],
315 			  &newdat->text[col],
316 			  sizeof(curdat->text[0]))) {
317 	    result = col;
318 	    break;
319 	}
320 #else
321 	if (curdat->text[col] != newdat->text[col]) {
322 	    result = col;
323 	    break;
324 	}
325 #endif
326     }
327     return result;
328 }
329 
330 #define EndChange(first) \
331 	find_end_of_change(sp, y, first)
332 #define NextChange(last)                        \
333 	find_next_change(sp, y, last)
334 
335 #endif /* EXP_OPTIMIZE */
336 
337 #define MARK_NOCHANGE(win,row)                 \
338     win->_line[row].firstchar = _NOCHANGE;     \
339     win->_line[row].lastchar  = _NOCHANGE
340 
341 static bool
342 restore_original_screen(void)
343 {
344     COORD bufferCoord;
345     bool result = FALSE;
346     SMALL_RECT save_region = WINCONSOLE.save_region;
347 
348     T(("... restoring %s", WINCONSOLE.window_only ?
349        "window" : "entire buffer"));
350 
351     bufferCoord.X = (SHORT) (WINCONSOLE.window_only ?
352 			     WINCONSOLE.SBI.srWindow.Left : 0);
353     bufferCoord.Y = (SHORT) (WINCONSOLE.window_only ?
354 			     WINCONSOLE.SBI.srWindow.Top : 0);
355 
356     if (write_screen(WINCONSOLE.hdl,
357 		     WINCONSOLE.save_screen,
358 		     WINCONSOLE.save_size,
359 		     bufferCoord,
360 		     &save_region)) {
361 	result = TRUE;
362 	mvcur(-1, -1, LINES - 2, 0);
363 	T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)",
364 	   WINCONSOLE.save_size.Y,
365 	   WINCONSOLE.save_size.X,
366 	   save_region.Top,
367 	   save_region.Left,
368 	   save_region.Bottom,
369 	   save_region.Right));
370     } else {
371 	T(("... restore original screen contents err"));
372     }
373     return result;
374 }
375 
376 static const char *
377 wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
378 {
379     (void) TCB;
380     return "win32console";
381 }
382 
383 static int
384 wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
385 {
386     int result = ERR;
387     int y, nonempty, n, x0, x1, Width, Height;
388     SCREEN *sp;
389 
390     T((T_CALLED("win32con::wcon_doupdate(%p)"), TCB));
391     if (validateConsoleHandle()) {
392 	SetSP();
393 
394 	Width = screen_columns(sp);
395 	Height = screen_lines(sp);
396 	nonempty = min(Height, NewScreen(sp)->_maxy + 1);
397 
398 	T(("... %dx%d clear cur:%d new:%d",
399 	   Height, Width,
400 	   CurScreen(sp)->_clear,
401 	   NewScreen(sp)->_clear));
402 
403 	if (SP_PARM->_endwin == ewSuspend) {
404 
405 	    T(("coming back from shell mode"));
406 	    NCURSES_SP_NAME(reset_prog_mode) (NCURSES_SP_ARG);
407 
408 	    NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG);
409 	    NCURSES_SP_NAME(_nc_screen_resume) (NCURSES_SP_ARG);
410 	    SP_PARM->_mouse_resume(SP_PARM);
411 
412 	    SP_PARM->_endwin = ewRunning;
413 	}
414 
415 	if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
416 	    int x;
417 #if USE_WIDEC_SUPPORT
418 	    cchar_t *empty = TypeAlloca(cchar_t, Width);
419 	    wchar_t blank[2] =
420 	    {
421 		L' ', L'\0'
422 	    };
423 
424 	    for (x = 0; x < Width; x++)
425 		setcchar(&empty[x], blank, 0, 0, 0);
426 #else
427 	    chtype *empty = TypeAlloca(chtype, Width);
428 
429 	    for (x = 0; x < Width; x++)
430 		empty[x] = ' ';
431 #endif
432 
433 	    for (y = 0; y < nonempty; y++) {
434 		con_write(TCB, y, 0, empty, Width);
435 		memcpy(empty,
436 		       CurScreen(sp)->_line[y].text,
437 		       (size_t) Width * sizeof(empty[0]));
438 	    }
439 	    CurScreen(sp)->_clear = FALSE;
440 	    NewScreen(sp)->_clear = FALSE;
441 	    touchwin(NewScreen(sp));
442 	    T(("... cleared %dx%d lines @%d of screen", nonempty, Width,
443 	       AdjustY()));
444 	}
445 
446 	for (y = 0; y < nonempty; y++) {
447 	    x0 = NewScreen(sp)->_line[y].firstchar;
448 	    if (x0 != _NOCHANGE) {
449 #if EXP_OPTIMIZE
450 		int x2;
451 		int limit = NewScreen(sp)->_line[y].lastchar;
452 		while ((x1 = EndChange(x0)) <= limit) {
453 		    while ((x2 = NextChange(x1)) <=
454 			   limit && x2 <= (x1 + 2)) {
455 			x1 = x2;
456 		    }
457 		    n = x1 - x0 + 1;
458 		    memcpy(&CurScreen(sp)->_line[y].text[x0],
459 			   &NewScreen(sp)->_line[y].text[x0],
460 			   n * sizeof(CurScreen(sp)->_line[y].text[x0]));
461 		    con_write(TCB,
462 			      y,
463 			      x0,
464 			      &CurScreen(sp)->_line[y].text[x0], n);
465 		    x0 = NextChange(x1);
466 		}
467 
468 		/* mark line changed successfully */
469 		if (y <= NewScreen(sp)->_maxy) {
470 		    MARK_NOCHANGE(NewScreen(sp), y);
471 		}
472 		if (y <= CurScreen(sp)->_maxy) {
473 		    MARK_NOCHANGE(CurScreen(sp), y);
474 		}
475 #else
476 		x1 = NewScreen(sp)->_line[y].lastchar;
477 		n = x1 - x0 + 1;
478 		if (n > 0) {
479 		    memcpy(&CurScreen(sp)->_line[y].text[x0],
480 			   &NewScreen(sp)->_line[y].text[x0],
481 			   (size_t) n *
482 			   sizeof(CurScreen(sp)->_line[y].text[x0]));
483 		    con_write(TCB,
484 			      y,
485 			      x0,
486 			      &CurScreen(sp)->_line[y].text[x0], n);
487 
488 		    /* mark line changed successfully */
489 		    if (y <= NewScreen(sp)->_maxy) {
490 			MARK_NOCHANGE(NewScreen(sp), y);
491 		    }
492 		    if (y <= CurScreen(sp)->_maxy) {
493 			MARK_NOCHANGE(CurScreen(sp), y);
494 		    }
495 		}
496 #endif
497 	    }
498 	}
499 
500 	/* put everything back in sync */
501 	for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
502 	    MARK_NOCHANGE(NewScreen(sp), y);
503 	}
504 	for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
505 	    MARK_NOCHANGE(CurScreen(sp), y);
506 	}
507 
508 	if (!NewScreen(sp)->_leaveok) {
509 	    CurScreen(sp)->_curx = NewScreen(sp)->_curx;
510 	    CurScreen(sp)->_cury = NewScreen(sp)->_cury;
511 
512 	    TCB->drv->td_hwcur(TCB,
513 			       0,
514 			       0,
515 			       CurScreen(sp)->_cury,
516 			       CurScreen(sp)->_curx);
517 	}
518 	_nc_console_selectActiveHandle();
519 	result = OK;
520     }
521     returnCode(result);
522 }
523 
524 static bool
525 wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
526 	       const char *tname,
527 	       int *errret GCC_UNUSED)
528 {
529     bool code = FALSE;
530 
531     T((T_CALLED("win32con::wcon_CanHandle(%p)"), TCB));
532 
533     assert((TCB != 0) && (tname != 0));
534 
535     TCB->magic = WINMAGIC;
536 
537     if (tname == 0 || *tname == 0) {
538 	if (!_nc_console_vt_supported())
539 	    code = TRUE;
540     } else if (tname != 0 && *tname == '#') {
541 	/*
542 	 * Use "#" (a character which cannot begin a terminal's name) to
543 	 * select specific driver from the table.
544 	 *
545 	 * In principle, we could have more than one non-terminfo driver,
546 	 * e.g., "win32gui".
547 	 */
548 	size_t n = strlen(tname + 1);
549 	if (n != 0
550 	    && ((strncmp(tname + 1, "win32console", n) == 0)
551 		|| (strncmp(tname + 1, "win32con", n) == 0))) {
552 	    code = TRUE;
553 	}
554     } else if (tname != 0 && stricmp(tname, "unknown") == 0) {
555 	code = TRUE;
556     }
557 
558     /*
559      * This is intentional, to avoid unnecessary breakage of applications
560      * using <term.h> symbols.
561      */
562     if (code && (TerminalType(&TCB->term).Booleans == 0)) {
563 	_nc_init_termtype(&TerminalType(&TCB->term));
564 #if NCURSES_EXT_NUMBERS
565 	_nc_export_termtype2(&TCB->term.type, &TerminalType(&TCB->term));
566 #endif
567     }
568 
569     if (!code) {
570 	if (_nc_console_test(0)) {
571 	    T(("isTermInfoConsole=TRUE"));
572 	    WINCONSOLE.isTermInfoConsole = TRUE;
573 	}
574     }
575     returnBool(code);
576 }
577 
578 static int
579 wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
580 		 int beepFlag)
581 {
582     SCREEN *sp;
583     int res = ERR;
584 
585     int high = (WINCONSOLE.SBI.srWindow.Bottom -
586 		WINCONSOLE.SBI.srWindow.Top + 1);
587     int wide = (WINCONSOLE.SBI.srWindow.Right -
588 		WINCONSOLE.SBI.srWindow.Left + 1);
589     int max_cells = (high * wide);
590     int i;
591 
592     CHAR_INFO *this_screen = TypeAlloca(CHAR_INFO, max_cells);
593     CHAR_INFO *that_screen = TypeAlloca(CHAR_INFO, max_cells);
594     COORD this_size;
595     SMALL_RECT this_region;
596     COORD bufferCoord;
597 
598     if (validateConsoleHandle()) {
599 	SetSP();
600 	this_region.Top = WINCONSOLE.SBI.srWindow.Top;
601 	this_region.Left = WINCONSOLE.SBI.srWindow.Left;
602 	this_region.Bottom = WINCONSOLE.SBI.srWindow.Bottom;
603 	this_region.Right = WINCONSOLE.SBI.srWindow.Right;
604 
605 	this_size.X = (SHORT) wide;
606 	this_size.Y = (SHORT) high;
607 
608 	bufferCoord.X = this_region.Left;
609 	bufferCoord.Y = this_region.Top;
610 
611 	if (!beepFlag &&
612 	    read_screen(WINCONSOLE.hdl,
613 			this_screen,
614 			this_size,
615 			bufferCoord,
616 			&this_region)) {
617 
618 	    memcpy(that_screen,
619 		   this_screen,
620 		   sizeof(CHAR_INFO) * (size_t) max_cells);
621 
622 	    for (i = 0; i < max_cells; i++) {
623 		that_screen[i].Attributes =
624 		    RevAttr(that_screen[i].Attributes);
625 	    }
626 
627 	    write_screen(WINCONSOLE.hdl, that_screen, this_size,
628 			 bufferCoord, &this_region);
629 	    Sleep(200);
630 	    write_screen(WINCONSOLE.hdl, this_screen, this_size,
631 			 bufferCoord, &this_region);
632 
633 	} else {
634 	    MessageBeep(MB_ICONWARNING);	/* MB_OK might be better */
635 	}
636 	res = OK;
637     }
638     return res;
639 }
640 
641 static int
642 wcon_print(TERMINAL_CONTROL_BLOCK * TCB,
643 	   char *data GCC_UNUSED,
644 	   int len GCC_UNUSED)
645 {
646     SCREEN *sp;
647 
648     AssertTCB();
649     SetSP();
650 
651     return ERR;
652 }
653 
654 static int
655 wcon_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
656 		   int fg GCC_UNUSED,
657 		   int bg GCC_UNUSED)
658 {
659     SCREEN *sp;
660     int code = ERR;
661 
662     AssertTCB();
663     SetSP();
664 
665     return (code);
666 }
667 
668 static void
669 wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
670 	      int fore,
671 	      int color,
672 	      int (*outc) (SCREEN *, int) GCC_UNUSED)
673 {
674     (void) TCB;
675     if (validateConsoleHandle()) {
676 	WORD a = _nc_console_MapColor(fore, color);
677 	a |= (WORD) ((WINCONSOLE.SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f));
678 	SetConsoleTextAttribute(WINCONSOLE.hdl, a);
679 	_nc_console_get_SBI();
680     }
681 }
682 
683 static bool
684 wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB)
685 {
686     bool res = FALSE;
687 
688     (void) TCB;
689     if (validateConsoleHandle()) {
690 	WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
691 	SetConsoleTextAttribute(WINCONSOLE.hdl, a);
692 	_nc_console_get_SBI();
693 	res = TRUE;
694     }
695     return res;
696 }
697 
698 static bool
699 wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
700 {
701     int result = FALSE;
702     SCREEN *sp;
703 
704     AssertTCB();
705     SetSP();
706 
707     return result;
708 }
709 
710 static int
711 wcon_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
712 {
713     int result = ERR;
714 
715     T((T_CALLED("win32con::wcon_size(%p)"), TCB));
716 
717     if (validateConsoleHandle() &&
718 	(Lines != NULL) && (Cols != NULL)) {
719 	_nc_console_size(Lines, Cols);
720 	result = OK;
721     }
722     returnCode(result);
723 }
724 
725 static int
726 wcon_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
727 	     int l GCC_UNUSED,
728 	     int c GCC_UNUSED)
729 {
730     AssertTCB();
731     return ERR;
732 }
733 
734 static int
735 wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
736 {
737     int result = ERR;
738 
739     T((T_CALLED("win32con::wcon_sgmode(TCB=(%p),setFlag=%d,TTY=(%p)"),
740        TCB, setFlag, buf));
741     if (buf != NULL && validateConsoleHandle()) {
742 
743 	if (setFlag) {
744 	    _nc_console_setmode(WINCONSOLE.hdl, buf);
745 	    TCB->term.Nttyb = *buf;
746 	} else {
747 	    _nc_console_getmode(WINCONSOLE.hdl, &(TCB->term.Nttyb));
748 	    *buf = TCB->term.Nttyb;
749 	}
750 	result = OK;
751     }
752     returnCode(result);
753 }
754 
755 #define MIN_WIDE 80
756 #define MIN_HIGH 24
757 
758 static int
759 wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
760 {
761     SCREEN *sp;
762     TERMINAL *_term = (TERMINAL *) TCB;
763     int code = ERR;
764 
765     if (validateConsoleHandle()) {
766 	sp = TCB->csp;
767 
768 	T((T_CALLED("win32con::wcon_mode(%p, progFlag=%d, defFlag=%d)"),
769 	   TCB, progFlag, defFlag));
770 
771 	WINCONSOLE.progMode = progFlag;
772 	WINCONSOLE.lastOut = progFlag ? WINCONSOLE.hdl : WINCONSOLE.out;
773 	SetConsoleActiveScreenBuffer(WINCONSOLE.lastOut);
774 
775 	if (progFlag) /* prog mode */  {
776 	    if (defFlag) {
777 		if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
778 		    code = OK;
779 		}
780 	    } else {
781 		/* reset_prog_mode */
782 		if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
783 		    if (sp) {
784 			if (sp->_keypad_on)
785 			    _nc_keypad(sp, TRUE);
786 		    }
787 		    if (!WINCONSOLE.buffered) {
788 			_nc_console_set_scrollback(FALSE, &WINCONSOLE.SBI);
789 		    }
790 		    code = OK;
791 		}
792 	    }
793 	    T(("... buffered:%d, clear:%d",
794 	       WINCONSOLE.buffered, CurScreen(sp)->_clear));
795 	} else {		/* shell mode */
796 	    if (defFlag) {
797 		/* def_shell_mode */
798 		if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
799 		    code = OK;
800 		}
801 	    } else {
802 		/* reset_shell_mode */
803 		if (sp) {
804 		    _nc_keypad(sp, FALSE);
805 		    NCURSES_SP_NAME(_nc_flush) (sp);
806 		}
807 		code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb));
808 		if (!WINCONSOLE.buffered) {
809 		    _nc_console_set_scrollback(TRUE, &WINCONSOLE.save_SBI);
810 		    if (!restore_original_screen())
811 			code = ERR;
812 		}
813 		SetConsoleCursorInfo(WINCONSOLE.hdl, &WINCONSOLE.save_CI);
814 	    }
815 	}
816 
817     }
818     returnCode(code);
819 }
820 
821 static void
822 wcon_screen_init(SCREEN *sp GCC_UNUSED)
823 {
824 }
825 
826 static void
827 wcon_wrap(SCREEN *sp GCC_UNUSED)
828 {
829 }
830 
831 static void
832 wcon_release(TERMINAL_CONTROL_BLOCK * TCB)
833 {
834     T((T_CALLED("win32con::wcon_release(%p)"), TCB));
835 
836     AssertTCB();
837     if (TCB->prop)
838 	free(TCB->prop);
839 
840     returnVoid;
841 }
842 
843 static void
844 wcon_init(TERMINAL_CONTROL_BLOCK * TCB)
845 {
846     T((T_CALLED("win32con::wcon_init(%p)"), TCB));
847 
848     AssertTCB();
849 
850     if (!(console_initialized = _nc_console_checkinit(TRUE, FALSE))) {
851 	returnVoid;
852     }
853 
854     if (TCB) {
855 	TCB->info.initcolor = TRUE;
856 	TCB->info.canchange = FALSE;
857 	TCB->info.hascolor = TRUE;
858 	TCB->info.caninit = TRUE;
859 
860 	TCB->info.maxpairs = CON_NUMPAIRS;
861 	TCB->info.maxcolors = 8;
862 	TCB->info.numlabels = 0;
863 	TCB->info.labelwidth = 0;
864 	TCB->info.labelheight = 0;
865 	TCB->info.nocolorvideo = 1;
866 	TCB->info.tabsize = 8;
867 
868 	TCB->info.numbuttons = WINCONSOLE.numButtons;
869 	TCB->info.defaultPalette = _nc_cga_palette;
870 
871     }
872     returnVoid;
873 }
874 
875 static void
876 wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB,
877 	      int pair,
878 	      int f,
879 	      int b)
880 {
881     SCREEN *sp;
882 
883     if (validateConsoleHandle()) {
884 	SetSP();
885 
886 	if ((pair > 0) && (pair < CON_NUMPAIRS) && (f >= 0) && (f < 8)
887 	    && (b >= 0) && (b < 8)) {
888 	    WINCONSOLE.pairs[pair] =
889 		_nc_console_MapColor(true, f) |
890 		_nc_console_MapColor(false, b);
891 	}
892     }
893 }
894 
895 static void
896 wcon_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
897 	       int color GCC_UNUSED,
898 	       int r GCC_UNUSED,
899 	       int g GCC_UNUSED,
900 	       int b GCC_UNUSED)
901 {
902     SCREEN *sp;
903 
904     AssertTCB();
905     SetSP();
906 }
907 
908 static void
909 wcon_do_color(TERMINAL_CONTROL_BLOCK * TCB,
910 	      int old_pair GCC_UNUSED,
911 	      int pair GCC_UNUSED,
912 	      int reverse GCC_UNUSED,
913 	      int (*outc) (SCREEN *, int) GCC_UNUSED
914 )
915 {
916     SCREEN *sp;
917 
918     AssertTCB();
919     SetSP();
920 }
921 
922 static void
923 wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
924 {
925     SCREEN *sp;
926 
927     if (validateConsoleHandle()) {
928 	SetSP();
929 
930 	sp->_mouse_type = M_TERM_DRIVER;
931     }
932 }
933 
934 static int
935 wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB,
936 	       int delay
937 	       EVENTLIST_2nd(_nc_eventlist * evl))
938 {
939     int rc = 0;
940     SCREEN *sp;
941 
942     if (validateConsoleHandle()) {
943 	SetSP();
944 
945 	if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
946 	    rc = TW_MOUSE;
947 	} else {
948 	    rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
949 					  TWAIT_MASK,
950 					  delay,
951 					  (int *) 0
952 					  EVENTLIST_2nd(evl));
953 	}
954     }
955 
956     return rc;
957 }
958 
959 static int
960 wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
961 	   int yold GCC_UNUSED, int xold GCC_UNUSED,
962 	   int y, int x)
963 {
964     int ret = ERR;
965 
966     (void) TCB;
967     if (validateConsoleHandle()) {
968 	COORD loc;
969 	loc.X = (short) x;
970 	loc.Y = (short) (y + AdjustY());
971 	SetConsoleCursorPosition(WINCONSOLE.hdl, loc);
972 	ret = OK;
973     }
974     return ret;
975 }
976 
977 static void
978 wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
979 	     int labnum GCC_UNUSED,
980 	     char *text GCC_UNUSED)
981 {
982     SCREEN *sp;
983 
984     AssertTCB();
985     SetSP();
986 }
987 
988 static void
989 wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
990 		  int OnFlag GCC_UNUSED)
991 {
992     SCREEN *sp;
993 
994     AssertTCB();
995     SetSP();
996 }
997 
998 static chtype
999 wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1000 {
1001     chtype res = A_NORMAL;
1002     res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1003     return res;
1004 }
1005 
1006 static void
1007 wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1008 {
1009     SCREEN *sp;
1010 
1011     AssertTCB();
1012     SetSP();
1013 }
1014 
1015 static void
1016 wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1017 	     chtype *real_map GCC_UNUSED,
1018 	     chtype *fake_map GCC_UNUSED)
1019 {
1020 #define DATA(a,b) { a, b }
1021     static struct {
1022 	int acs_code;
1023 	int use_code;
1024     } table[] = {
1025 	DATA('a', 0xb1),	/* ACS_CKBOARD  */
1026 	    DATA('f', 0xf8),	/* ACS_DEGREE   */
1027 	    DATA('g', 0xf1),	/* ACS_PLMINUS  */
1028 	    DATA('j', 0xd9),	/* ACS_LRCORNER */
1029 	    DATA('l', 0xda),	/* ACS_ULCORNER */
1030 	    DATA('k', 0xbf),	/* ACS_URCORNER */
1031 	    DATA('m', 0xc0),	/* ACS_LLCORNER */
1032 	    DATA('n', 0xc5),	/* ACS_PLUS     */
1033 	    DATA('q', 0xc4),	/* ACS_HLINE    */
1034 	    DATA('t', 0xc3),	/* ACS_LTEE     */
1035 	    DATA('u', 0xb4),	/* ACS_RTEE     */
1036 	    DATA('v', 0xc1),	/* ACS_BTEE     */
1037 	    DATA('w', 0xc2),	/* ACS_TTEE     */
1038 	    DATA('x', 0xb3),	/* ACS_VLINE    */
1039 	    DATA('y', 0xf3),	/* ACS_LEQUAL   */
1040 	    DATA('z', 0xf2),	/* ACS_GEQUAL   */
1041 	    DATA('0', 0xdb),	/* ACS_BLOCK    */
1042 	    DATA('{', 0xe3),	/* ACS_PI       */
1043 	    DATA('}', 0x9c),	/* ACS_STERLING */
1044 	    DATA(',', 0xae),	/* ACS_LARROW   */
1045 	    DATA('+', 0xaf),	/* ACS_RARROW   */
1046 	    DATA('~', 0xf9),	/* ACS_BULLET   */
1047     };
1048 #undef DATA
1049     unsigned n;
1050 
1051     SCREEN *sp;
1052     if (validateConsoleHandle()) {
1053 	SetSP();
1054 
1055 	for (n = 0; n < SIZEOF(table); ++n) {
1056 	    real_map[table[n].acs_code] =
1057 		(chtype) table[n].use_code | A_ALTCHARSET;
1058 	    if (sp != 0)
1059 		sp->_screen_acs_map[table[n].acs_code] = TRUE;
1060 	}
1061     }
1062 }
1063 
1064 static int
1065 wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,
1066 	   int mode,
1067 	   int milliseconds,
1068 	   int *timeleft
1069 	   EVENTLIST_2nd(_nc_eventlist * evl))
1070 {
1071     SCREEN *sp;
1072     int code = 0;
1073 
1074     if (validateConsoleHandle()) {
1075 	SetSP();
1076 
1077 	code = _nc_console_twait(sp,
1078 				 WINCONSOLE.inp,
1079 				 mode,
1080 				 milliseconds,
1081 				 timeleft EVENTLIST_2nd(evl));
1082     }
1083     return code;
1084 }
1085 
1086 static int
1087 wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1088 {
1089     SCREEN *sp;
1090     int n = -1;
1091 
1092     T((T_CALLED("win32con::wcon_read(%p)"), TCB));
1093 
1094     assert(buf);
1095     if (validateConsoleHandle()) {
1096 	SetSP();
1097 
1098 	n = _nc_console_read(sp, WINCONSOLE.inp, buf);
1099     }
1100     returnCode(n);
1101 }
1102 
1103 static int
1104 wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1105 {
1106     T((T_CALLED("win32con::wcon_nap(%p, %d)"), TCB, ms));
1107     Sleep((DWORD) ms);
1108     returnCode(OK);
1109 }
1110 
1111 static int
1112 wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int mode)
1113 {
1114     int res = -1;
1115 
1116     T((T_CALLED("win32con:wcon_cursorSet(%d)"), mode));
1117     if (validateConsoleHandle()) {
1118 	CONSOLE_CURSOR_INFO this_CI = WINCONSOLE.save_CI;
1119 	switch (mode) {
1120 	case 0:
1121 	    this_CI.bVisible = FALSE;
1122 	    break;
1123 	case 1:
1124 	    break;
1125 	case 2:
1126 	    this_CI.dwSize = 100;
1127 	    break;
1128 	}
1129 	SetConsoleCursorInfo(WINCONSOLE.hdl, &this_CI);
1130     }
1131     returnCode(res);
1132 }
1133 
1134 static bool
1135 wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int keycode)
1136 {
1137     bool found = FALSE;
1138 
1139     T((T_CALLED("win32con::wcon_kyExist(%d)"), keycode));
1140     found = _nc_console_keyExist(keycode);
1141     returnBool(found);
1142 }
1143 
1144 static int
1145 wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1146 {
1147     SCREEN *sp;
1148     int code = ERR;
1149 
1150     T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag));
1151 
1152     if (validateConsoleHandle()) {
1153 	SetSP();
1154 
1155 	if (sp) {
1156 	    code = OK;
1157 	}
1158     }
1159     returnCode(code);
1160 }
1161 
1162 static int
1163 wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB,
1164 	   int keycode,
1165 	   int flag)
1166 {
1167     int code = ERR;
1168     SCREEN *sp;
1169 
1170     T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag));
1171 
1172     if (validateConsoleHandle()) {
1173 	SetSP();
1174 	if (sp) {
1175 	    code = _nc_console_keyok(keycode, flag);
1176 	}
1177     }
1178     returnCode(code);
1179 }
1180 
1181 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1182     FALSE,
1183 	wcon_name,		/* Name          */
1184 	wcon_CanHandle,		/* CanHandle     */
1185 	wcon_init,		/* init          */
1186 	wcon_release,		/* release       */
1187 	wcon_size,		/* size          */
1188 	wcon_sgmode,		/* sgmode        */
1189 	wcon_conattr,		/* conattr       */
1190 	wcon_mvcur,		/* hwcur         */
1191 	wcon_mode,		/* mode          */
1192 	wcon_rescol,		/* rescol        */
1193 	wcon_rescolors,		/* rescolors     */
1194 	wcon_setcolor,		/* color         */
1195 	wcon_dobeepflash,	/* DoBeepFlash   */
1196 	wcon_initpair,		/* initpair      */
1197 	wcon_initcolor,		/* initcolor     */
1198 	wcon_do_color,		/* docolor       */
1199 	wcon_initmouse,		/* initmouse     */
1200 	wcon_testmouse,		/* testmouse     */
1201 	wcon_setfilter,		/* setfilter     */
1202 	wcon_hwlabel,		/* hwlabel       */
1203 	wcon_hwlabelOnOff,	/* hwlabelOnOff  */
1204 	wcon_doupdate,		/* update        */
1205 	wcon_defaultcolors,	/* defaultcolors */
1206 	wcon_print,		/* print         */
1207 	wcon_size,		/* getsize       */
1208 	wcon_setsize,		/* setsize       */
1209 	wcon_initacs,		/* initacs       */
1210 	wcon_screen_init,	/* scinit        */
1211 	wcon_wrap,		/* scexit        */
1212 	wcon_twait,		/* twait         */
1213 	wcon_read,		/* read          */
1214 	wcon_nap,		/* nap           */
1215 	wcon_kpad,		/* kpad          */
1216 	wcon_keyok,		/* kyOk          */
1217 	wcon_kyExist,		/* kyExist       */
1218 	wcon_cursorSet		/* cursorSet     */
1219 };
1220 
1221 #endif /* _NC_WINDOWS */
1222