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 - GetMousePos(POINT * result) from ntconio.c
37  * TODO - implement nodelay
38  * TODO - improve screen-repainting performance, using implied wraparound to reduce write's
39  * TODO - make it optional whether screen is restored or not when non-buffered
40  */
41 
42 #include <curses.priv.h>
43 
44 #ifdef _WIN32
45 #include <tchar.h>
46 #else
47 #include <windows.h>
48 #include <wchar.h>
49 #endif
50 
51 #include <io.h>
52 
53 #define PSAPI_VERSION 2
54 #include <psapi.h>
55 
56 #define CUR TerminalType(my_term).
57 
58 MODULE_ID("$Id: win_driver.c,v 1.66 2020/03/01 00:18:49 tom Exp $")
59 
60 #define TypeAlloca(type,count) (type*) _alloca(sizeof(type) * (size_t) (count))
61 
62 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
63 
64 #define EXP_OPTIMIZE 0
65 
66 #define array_length(a) (sizeof(a)/sizeof(a[0]))
67 
68 static bool InitConsole(void);
69 static bool okConsoleHandle(TERMINAL_CONTROL_BLOCK *);
70 
71 #define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC))
72 #define SetSP()     assert(TCB->csp != 0); sp = TCB->csp; (void) sp
73 
74 #define GenMap(vKey,key) MAKELONG(key, vKey)
75 
76 #define AdjustY() (CON.buffered ? 0 : (int) CON.SBI.srWindow.Top)
77 
78 #if USE_WIDEC_SUPPORT
79 #define write_screen WriteConsoleOutputW
80 #define read_screen  ReadConsoleOutputW
81 #else
82 #define write_screen WriteConsoleOutput
83 #define read_screen  ReadConsoleOutput
84 #endif
85 
86 static const LONG keylist[] =
87 {
88     GenMap(VK_PRIOR, KEY_PPAGE),
89     GenMap(VK_NEXT, KEY_NPAGE),
90     GenMap(VK_END, KEY_END),
91     GenMap(VK_HOME, KEY_HOME),
92     GenMap(VK_LEFT, KEY_LEFT),
93     GenMap(VK_UP, KEY_UP),
94     GenMap(VK_RIGHT, KEY_RIGHT),
95     GenMap(VK_DOWN, KEY_DOWN),
96     GenMap(VK_DELETE, KEY_DC),
97     GenMap(VK_INSERT, KEY_IC)
98 };
99 static const LONG ansi_keys[] =
100 {
101     GenMap(VK_PRIOR, 'I'),
102     GenMap(VK_NEXT, 'Q'),
103     GenMap(VK_END, 'O'),
104     GenMap(VK_HOME, 'H'),
105     GenMap(VK_LEFT, 'K'),
106     GenMap(VK_UP, 'H'),
107     GenMap(VK_RIGHT, 'M'),
108     GenMap(VK_DOWN, 'P'),
109     GenMap(VK_DELETE, 'S'),
110     GenMap(VK_INSERT, 'R')
111 };
112 #define N_INI ((int)array_length(keylist))
113 #define FKEYS 24
114 #define MAPSIZE (FKEYS + N_INI)
115 #define NUMPAIRS 64
116 
117 /*   A process can only have a single console, so it's safe
118      to maintain all the information about it in a single
119      static structure.
120  */
121 static struct {
122     BOOL initialized;
123     BOOL buffered;
124     BOOL window_only;
125     BOOL progMode;
126     BOOL isMinTTY;
127     BOOL isTermInfoConsole;
128     HANDLE out;
129     HANDLE inp;
130     HANDLE hdl;
131     HANDLE lastOut;
132     int numButtons;
133     DWORD ansi_map[MAPSIZE];
134     DWORD map[MAPSIZE];
135     DWORD rmap[MAPSIZE];
136     WORD pairs[NUMPAIRS];
137     COORD origin;
138     CHAR_INFO *save_screen;
139     COORD save_size;
140     SMALL_RECT save_region;
141     CONSOLE_SCREEN_BUFFER_INFO SBI;
142     CONSOLE_SCREEN_BUFFER_INFO save_SBI;
143     CONSOLE_CURSOR_INFO save_CI;
144 } CON;
145 
146 static BOOL console_initialized = FALSE;
147 
148 static WORD
MapColor(bool fore,int color)149 MapColor(bool fore, int color)
150 {
151     static const int _cmap[] =
152     {0, 4, 2, 6, 1, 5, 3, 7};
153     int a;
154     if (color < 0 || color > 7)
155 	a = fore ? 7 : 0;
156     else
157 	a = _cmap[color];
158     if (!fore)
159 	a = a << 4;
160     return (WORD) a;
161 }
162 
163 #define RevAttr(attr) \
164 	       (WORD) (((attr) & 0xff00) | \
165 		      ((((attr) & 0x07) << 4) | \
166 		       (((attr) & 0x70) >> 4)))
167 
168 static WORD
MapAttr(WORD res,attr_t ch)169 MapAttr(WORD res, attr_t ch)
170 {
171     if (ch & A_COLOR) {
172 	int p;
173 
174 	p = PairNumber(ch);
175 	if (p > 0 && p < NUMPAIRS) {
176 	    WORD a;
177 	    a = CON.pairs[p];
178 	    res = (WORD) ((res & 0xff00) | a);
179 	}
180     }
181 
182     if (ch & A_REVERSE) {
183 	res = RevAttr(res);
184     }
185 
186     if (ch & A_STANDOUT) {
187 	res = RevAttr(res) | BACKGROUND_INTENSITY;
188     }
189 
190     if (ch & A_BOLD)
191 	res |= FOREGROUND_INTENSITY;
192 
193     if (ch & A_DIM)
194 	res |= BACKGROUND_INTENSITY;
195 
196     return res;
197 }
198 
199 #if 0				/* def TRACE */
200 static void
201 dump_screen(const char *fn, int ln)
202 {
203     int max_cells = (CON.SBI.dwSize.Y * (1 + CON.SBI.dwSize.X)) + 1;
204     char output[max_cells];
205     CHAR_INFO save_screen[max_cells];
206     COORD save_size;
207     SMALL_RECT save_region;
208     COORD bufferCoord;
209 
210     T(("dump_screen %s@%d", fn, ln));
211 
212     save_region.Top = CON.SBI.srWindow.Top;
213     save_region.Left = CON.SBI.srWindow.Left;
214     save_region.Bottom = CON.SBI.srWindow.Bottom;
215     save_region.Right = CON.SBI.srWindow.Right;
216 
217     save_size.X = (SHORT) (save_region.Right - save_region.Left + 1);
218     save_size.Y = (SHORT) (save_region.Bottom - save_region.Top + 1);
219 
220     bufferCoord.X = bufferCoord.Y = 0;
221 
222     if (read_screen(CON.hdl,
223 		    save_screen,
224 		    save_size,
225 		    bufferCoord,
226 		    &save_region)) {
227 	int i, j;
228 	int ij = 0;
229 	int k = 0;
230 
231 	for (i = save_region.Top; i <= save_region.Bottom; ++i) {
232 	    for (j = save_region.Left; j <= save_region.Right; ++j) {
233 		output[k++] = save_screen[ij++].Char.AsciiChar;
234 	    }
235 	    output[k++] = '\n';
236 	}
237 	output[k] = 0;
238 
239 	T(("DUMP: %d,%d - %d,%d",
240 	   save_region.Top,
241 	   save_region.Left,
242 	   save_region.Bottom,
243 	   save_region.Right));
244 	T(("%s", output));
245     }
246 }
247 
248 #else
249 #define dump_screen(fn,ln)	/* nothing */
250 #endif
251 
252 #if USE_WIDEC_SUPPORT
253 /*
254  * TODO: support surrogate pairs
255  * TODO: support combining characters
256  * TODO: support acsc
257  * TODO: _nc_wacs should be part of sp.
258  */
259 static BOOL
con_write16(TERMINAL_CONTROL_BLOCK * TCB,int y,int x,cchar_t * str,int limit)260 con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
261 {
262     int actual = 0;
263     CHAR_INFO *ci = TypeAlloca(CHAR_INFO, limit);
264     COORD loc, siz;
265     SMALL_RECT rec;
266     int i;
267     cchar_t ch;
268     SCREEN *sp;
269 
270     AssertTCB();
271     SetSP();
272 
273     for (i = actual = 0; i < limit; i++) {
274 	ch = str[i];
275 	if (isWidecExt(ch))
276 	    continue;
277 	ci[actual].Char.UnicodeChar = CharOf(ch);
278 	ci[actual].Attributes = MapAttr(CON.SBI.wAttributes,
279 					AttrOf(ch));
280 	if (AttrOf(ch) & A_ALTCHARSET) {
281 	    if (_nc_wacs) {
282 		int which = CharOf(ch);
283 		if (which > 0
284 		    && which < ACS_LEN
285 		    && CharOf(_nc_wacs[which]) != 0) {
286 		    ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
287 		} else {
288 		    ci[actual].Char.UnicodeChar = ' ';
289 		}
290 	    }
291 	}
292 	++actual;
293     }
294 
295     loc.X = (SHORT) 0;
296     loc.Y = (SHORT) 0;
297     siz.X = (SHORT) actual;
298     siz.Y = 1;
299 
300     rec.Left = (SHORT) x;
301     rec.Top = (SHORT) (y + AdjustY());
302     rec.Right = (SHORT) (x + limit - 1);
303     rec.Bottom = rec.Top;
304 
305     return write_screen(CON.hdl, ci, siz, loc, &rec);
306 }
307 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
308 #else
309 static BOOL
con_write8(TERMINAL_CONTROL_BLOCK * TCB,int y,int x,chtype * str,int n)310 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
311 {
312     CHAR_INFO *ci = TypeAlloca(CHAR_INFO, n);
313     COORD loc, siz;
314     SMALL_RECT rec;
315     int i;
316     chtype ch;
317     SCREEN *sp;
318 
319     AssertTCB();
320     SetSP();
321 
322     for (i = 0; i < n; i++) {
323 	ch = str[i];
324 	ci[i].Char.AsciiChar = ChCharOf(ch);
325 	ci[i].Attributes = MapAttr(CON.SBI.wAttributes,
326 				   ChAttrOf(ch));
327 	if (ChAttrOf(ch) & A_ALTCHARSET) {
328 	    if (sp->_acs_map)
329 		ci[i].Char.AsciiChar =
330 		ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
331 	}
332     }
333 
334     loc.X = (short) 0;
335     loc.Y = (short) 0;
336     siz.X = (short) n;
337     siz.Y = 1;
338 
339     rec.Left = (short) x;
340     rec.Top = (short) y;
341     rec.Right = (short) (x + n - 1);
342     rec.Bottom = rec.Top;
343 
344     return write_screen(CON.hdl, ci, siz, loc, &rec);
345 }
346 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
347 #endif
348 
349 #if EXP_OPTIMIZE
350 /*
351  * Comparing new/current screens, determine the last column-index for a change
352  * beginning on the given row,col position.  Unlike a serial terminal, there is
353  * no cost for "moving" the "cursor" on the line as we update it.
354  */
355 static int
find_end_of_change(SCREEN * sp,int row,int col)356 find_end_of_change(SCREEN *sp, int row, int col)
357 {
358     int result = col;
359     struct ldat *curdat = CurScreen(sp)->_line + row;
360     struct ldat *newdat = NewScreen(sp)->_line + row;
361 
362     while (col <= newdat->lastchar) {
363 #if USE_WIDEC_SUPPORT
364 	if (isWidecExt(curdat->text[col]) || isWidecExt(newdat->text[col])) {
365 	    result = col;
366 	} else if (memcmp(&curdat->text[col],
367 			  &newdat->text[col],
368 			  sizeof(curdat->text[0]))) {
369 	    result = col;
370 	} else {
371 	    break;
372 	}
373 #else
374 	if (curdat->text[col] != newdat->text[col]) {
375 	    result = col;
376 	} else {
377 	    break;
378 	}
379 #endif
380 	++col;
381     }
382     return result;
383 }
384 
385 /*
386  * Given a row,col position at the end of a change-chunk, look for the
387  * beginning of the next change-chunk.
388  */
389 static int
find_next_change(SCREEN * sp,int row,int col)390 find_next_change(SCREEN *sp, int row, int col)
391 {
392     struct ldat *curdat = CurScreen(sp)->_line + row;
393     struct ldat *newdat = NewScreen(sp)->_line + row;
394     int result = newdat->lastchar + 1;
395 
396     while (++col <= newdat->lastchar) {
397 #if USE_WIDEC_SUPPORT
398 	if (isWidecExt(curdat->text[col]) != isWidecExt(newdat->text[col])) {
399 	    result = col;
400 	    break;
401 	} else if (memcmp(&curdat->text[col],
402 			  &newdat->text[col],
403 			  sizeof(curdat->text[0]))) {
404 	    result = col;
405 	    break;
406 	}
407 #else
408 	if (curdat->text[col] != newdat->text[col]) {
409 	    result = col;
410 	    break;
411 	}
412 #endif
413     }
414     return result;
415 }
416 
417 #define EndChange(first) \
418 	find_end_of_change(sp, y, first)
419 #define NextChange(last) \
420 	find_next_change(sp, y, last)
421 
422 #endif /* EXP_OPTIMIZE */
423 
424 #define MARK_NOCHANGE(win,row) \
425 		win->_line[row].firstchar = _NOCHANGE; \
426 		win->_line[row].lastchar  = _NOCHANGE
427 
428 static void
selectActiveHandle(void)429 selectActiveHandle(void)
430 {
431     if (CON.lastOut != CON.hdl) {
432 	CON.lastOut = CON.hdl;
433 	SetConsoleActiveScreenBuffer(CON.lastOut);
434     }
435 }
436 
437 static bool
restore_original_screen(void)438 restore_original_screen(void)
439 {
440     COORD bufferCoord;
441     bool result = FALSE;
442     SMALL_RECT save_region = CON.save_region;
443 
444     T(("... restoring %s", CON.window_only ? "window" : "entire buffer"));
445 
446     bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0);
447     bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0);
448 
449     if (write_screen(CON.hdl,
450 		     CON.save_screen,
451 		     CON.save_size,
452 		     bufferCoord,
453 		     &save_region)) {
454 	result = TRUE;
455 	mvcur(-1, -1, LINES - 2, 0);
456 	T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)",
457 	   CON.save_size.Y,
458 	   CON.save_size.X,
459 	   save_region.Top,
460 	   save_region.Left,
461 	   save_region.Bottom,
462 	   save_region.Right));
463     } else {
464 	T(("... restore original screen contents err"));
465     }
466     return result;
467 }
468 
469 static const char *
wcon_name(TERMINAL_CONTROL_BLOCK * TCB)470 wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
471 {
472     (void) TCB;
473     return "win32console";
474 }
475 
476 static int
wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB)477 wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
478 {
479     int result = ERR;
480     int y, nonempty, n, x0, x1, Width, Height;
481     SCREEN *sp;
482 
483     T((T_CALLED("win32con::wcon_doupdate(%p)"), TCB));
484     if (okConsoleHandle(TCB)) {
485 	SetSP();
486 
487 	Width = screen_columns(sp);
488 	Height = screen_lines(sp);
489 	nonempty = min(Height, NewScreen(sp)->_maxy + 1);
490 
491 	T(("... %dx%d clear cur:%d new:%d",
492 	   Height, Width,
493 	   CurScreen(sp)->_clear,
494 	   NewScreen(sp)->_clear));
495 
496 	if (SP_PARM->_endwin == ewSuspend) {
497 
498 	    T(("coming back from shell mode"));
499 	    NCURSES_SP_NAME(reset_prog_mode) (NCURSES_SP_ARG);
500 
501 	    NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG);
502 	    NCURSES_SP_NAME(_nc_screen_resume) (NCURSES_SP_ARG);
503 	    SP_PARM->_mouse_resume(SP_PARM);
504 
505 	    SP_PARM->_endwin = ewRunning;
506 	}
507 
508 	if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
509 	    int x;
510 #if USE_WIDEC_SUPPORT
511 	    cchar_t *empty = TypeAlloca(cchar_t, Width);
512 	    wchar_t blank[2] =
513 	    {
514 		L' ', L'\0'
515 	    };
516 
517 	    for (x = 0; x < Width; x++)
518 		setcchar(&empty[x], blank, 0, 0, 0);
519 #else
520 	    chtype *empty = TypeAlloca(chtype, Width);
521 
522 	    for (x = 0; x < Width; x++)
523 		empty[x] = ' ';
524 #endif
525 
526 	    for (y = 0; y < nonempty; y++) {
527 		con_write(TCB, y, 0, empty, Width);
528 		memcpy(empty,
529 		       CurScreen(sp)->_line[y].text,
530 		       (size_t) Width * sizeof(empty[0]));
531 	    }
532 	    CurScreen(sp)->_clear = FALSE;
533 	    NewScreen(sp)->_clear = FALSE;
534 	    touchwin(NewScreen(sp));
535 	    T(("... cleared %dx%d lines @%d of screen", nonempty, Width,
536 	       AdjustY()));
537 	}
538 
539 	for (y = 0; y < nonempty; y++) {
540 	    x0 = NewScreen(sp)->_line[y].firstchar;
541 	    if (x0 != _NOCHANGE) {
542 #if EXP_OPTIMIZE
543 		int x2;
544 		int limit = NewScreen(sp)->_line[y].lastchar;
545 		while ((x1 = EndChange(x0)) <= limit) {
546 		    while ((x2 = NextChange(x1)) <= limit && x2 <= (x1 + 2)) {
547 			x1 = x2;
548 		    }
549 		    n = x1 - x0 + 1;
550 		    memcpy(&CurScreen(sp)->_line[y].text[x0],
551 			   &NewScreen(sp)->_line[y].text[x0],
552 			   n * sizeof(CurScreen(sp)->_line[y].text[x0]));
553 		    con_write(TCB,
554 			      y,
555 			      x0,
556 			      &CurScreen(sp)->_line[y].text[x0], n);
557 		    x0 = NextChange(x1);
558 		}
559 
560 		/* mark line changed successfully */
561 		if (y <= NewScreen(sp)->_maxy) {
562 		    MARK_NOCHANGE(NewScreen(sp), y);
563 		}
564 		if (y <= CurScreen(sp)->_maxy) {
565 		    MARK_NOCHANGE(CurScreen(sp), y);
566 		}
567 #else
568 		x1 = NewScreen(sp)->_line[y].lastchar;
569 		n = x1 - x0 + 1;
570 		if (n > 0) {
571 		    memcpy(&CurScreen(sp)->_line[y].text[x0],
572 			   &NewScreen(sp)->_line[y].text[x0],
573 			   (size_t) n * sizeof(CurScreen(sp)->_line[y].text[x0]));
574 		    con_write(TCB,
575 			      y,
576 			      x0,
577 			      &CurScreen(sp)->_line[y].text[x0], n);
578 
579 		    /* mark line changed successfully */
580 		    if (y <= NewScreen(sp)->_maxy) {
581 			MARK_NOCHANGE(NewScreen(sp), y);
582 		    }
583 		    if (y <= CurScreen(sp)->_maxy) {
584 			MARK_NOCHANGE(CurScreen(sp), y);
585 		    }
586 		}
587 #endif
588 	    }
589 	}
590 
591 	/* put everything back in sync */
592 	for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
593 	    MARK_NOCHANGE(NewScreen(sp), y);
594 	}
595 	for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
596 	    MARK_NOCHANGE(CurScreen(sp), y);
597 	}
598 
599 	if (!NewScreen(sp)->_leaveok) {
600 	    CurScreen(sp)->_curx = NewScreen(sp)->_curx;
601 	    CurScreen(sp)->_cury = NewScreen(sp)->_cury;
602 
603 	    TCB->drv->td_hwcur(TCB,
604 			       0, 0,
605 			       CurScreen(sp)->_cury, CurScreen(sp)->_curx);
606 	}
607 	selectActiveHandle();
608 	result = OK;
609     }
610     returnCode(result);
611 }
612 
613 static bool
wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,const char * tname,int * errret GCC_UNUSED)614 wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
615 	       const char *tname,
616 	       int *errret GCC_UNUSED)
617 {
618     bool code = FALSE;
619 
620     T((T_CALLED("win32con::wcon_CanHandle(%p)"), TCB));
621 
622     assert((TCB != 0) && (tname != 0));
623 
624     TCB->magic = WINMAGIC;
625 
626     if (tname == 0 || *tname == 0)
627 	code = TRUE;
628     else if (tname != 0 && *tname == '#') {
629 	/*
630 	 * Use "#" (a character which cannot begin a terminal's name) to
631 	 * select specific driver from the table.
632 	 *
633 	 * In principle, we could have more than one non-terminfo driver,
634 	 * e.g., "win32gui".
635 	 */
636 	size_t n = strlen(tname + 1);
637 	if (n != 0
638 	    && ((strncmp(tname + 1, "win32console", n) == 0)
639 		|| (strncmp(tname + 1, "win32con", n) == 0))) {
640 	    code = TRUE;
641 	}
642     } else if (tname != 0 && stricmp(tname, "unknown") == 0) {
643 	code = TRUE;
644     }
645 
646     /*
647      * This is intentional, to avoid unnecessary breakage of applications
648      * using <term.h> symbols.
649      */
650     if (code && (TerminalType(&TCB->term).Booleans == 0)) {
651 	_nc_init_termtype(&TerminalType(&TCB->term));
652 #if NCURSES_EXT_NUMBERS
653 	_nc_export_termtype2(&TCB->term.type, &TerminalType(&TCB->term));
654 #endif
655     }
656 
657     if (!code) {
658 	if (_nc_mingw_isconsole(0))
659 	    CON.isTermInfoConsole = TRUE;
660     }
661     returnBool(code);
662 }
663 
664 static int
wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,int beepFlag)665 wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
666 		 int beepFlag)
667 {
668     SCREEN *sp;
669     int res = ERR;
670 
671     int high = (CON.SBI.srWindow.Bottom - CON.SBI.srWindow.Top + 1);
672     int wide = (CON.SBI.srWindow.Right - CON.SBI.srWindow.Left + 1);
673     int max_cells = (high * wide);
674     int i;
675 
676     CHAR_INFO *this_screen = TypeAlloca(CHAR_INFO, max_cells);
677     CHAR_INFO *that_screen = TypeAlloca(CHAR_INFO, max_cells);
678     COORD this_size;
679     SMALL_RECT this_region;
680     COORD bufferCoord;
681 
682     if (okConsoleHandle(TCB)) {
683 	SetSP();
684 	this_region.Top = CON.SBI.srWindow.Top;
685 	this_region.Left = CON.SBI.srWindow.Left;
686 	this_region.Bottom = CON.SBI.srWindow.Bottom;
687 	this_region.Right = CON.SBI.srWindow.Right;
688 
689 	this_size.X = (SHORT) wide;
690 	this_size.Y = (SHORT) high;
691 
692 	bufferCoord.X = this_region.Left;
693 	bufferCoord.Y = this_region.Top;
694 
695 	if (!beepFlag &&
696 	    read_screen(CON.hdl,
697 			this_screen,
698 			this_size,
699 			bufferCoord,
700 			&this_region)) {
701 
702 	    memcpy(that_screen,
703 		   this_screen,
704 		   sizeof(CHAR_INFO) * (size_t) max_cells);
705 
706 	    for (i = 0; i < max_cells; i++) {
707 		that_screen[i].Attributes = RevAttr(that_screen[i].Attributes);
708 	    }
709 
710 	    write_screen(CON.hdl, that_screen, this_size, bufferCoord, &this_region);
711 	    Sleep(200);
712 	    write_screen(CON.hdl, this_screen, this_size, bufferCoord, &this_region);
713 
714 	} else {
715 	    MessageBeep(MB_ICONWARNING);	/* MB_OK might be better */
716 	}
717 	res = OK;
718     }
719     return res;
720 }
721 
722 static int
wcon_print(TERMINAL_CONTROL_BLOCK * TCB,char * data GCC_UNUSED,int len GCC_UNUSED)723 wcon_print(TERMINAL_CONTROL_BLOCK * TCB,
724 	   char *data GCC_UNUSED,
725 	   int len GCC_UNUSED)
726 {
727     SCREEN *sp;
728 
729     AssertTCB();
730     SetSP();
731 
732     return ERR;
733 }
734 
735 static int
wcon_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,int fg GCC_UNUSED,int bg GCC_UNUSED)736 wcon_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
737 		   int fg GCC_UNUSED,
738 		   int bg GCC_UNUSED)
739 {
740     SCREEN *sp;
741     int code = ERR;
742 
743     AssertTCB();
744     SetSP();
745 
746     return (code);
747 }
748 
749 static bool
get_SBI(void)750 get_SBI(void)
751 {
752     bool rc = FALSE;
753     if (GetConsoleScreenBufferInfo(CON.hdl, &(CON.SBI))) {
754 	T(("GetConsoleScreenBufferInfo"));
755 	T(("... buffer(X:%d Y:%d)",
756 	   CON.SBI.dwSize.X,
757 	   CON.SBI.dwSize.Y));
758 	T(("... window(X:%d Y:%d)",
759 	   CON.SBI.dwMaximumWindowSize.X,
760 	   CON.SBI.dwMaximumWindowSize.Y));
761 	T(("... cursor(X:%d Y:%d)",
762 	   CON.SBI.dwCursorPosition.X,
763 	   CON.SBI.dwCursorPosition.Y));
764 	T(("... display(Top:%d Bottom:%d Left:%d Right:%d)",
765 	   CON.SBI.srWindow.Top,
766 	   CON.SBI.srWindow.Bottom,
767 	   CON.SBI.srWindow.Left,
768 	   CON.SBI.srWindow.Right));
769 	if (CON.buffered) {
770 	    CON.origin.X = 0;
771 	    CON.origin.Y = 0;
772 	} else {
773 	    CON.origin.X = CON.SBI.srWindow.Left;
774 	    CON.origin.Y = CON.SBI.srWindow.Top;
775 	}
776 	rc = TRUE;
777     } else {
778 	T(("GetConsoleScreenBufferInfo ERR"));
779     }
780     return rc;
781 }
782 
783 static void
wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB,int fore,int color,int (* outc)(SCREEN *,int)GCC_UNUSED)784 wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
785 	      int fore,
786 	      int color,
787 	      int (*outc) (SCREEN *, int) GCC_UNUSED)
788 {
789     if (okConsoleHandle(TCB)) {
790 	WORD a = MapColor(fore, color);
791 	a |= (WORD) ((CON.SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f));
792 	SetConsoleTextAttribute(CON.hdl, a);
793 	get_SBI();
794     }
795 }
796 
797 static bool
wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB)798 wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB)
799 {
800     bool res = FALSE;
801 
802     if (okConsoleHandle(TCB)) {
803 	WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
804 	SetConsoleTextAttribute(CON.hdl, a);
805 	get_SBI();
806 	res = TRUE;
807     }
808     return res;
809 }
810 
811 static bool
wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)812 wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
813 {
814     int result = FALSE;
815     SCREEN *sp;
816 
817     AssertTCB();
818     SetSP();
819 
820     return result;
821 }
822 
823 static int
wcon_size(TERMINAL_CONTROL_BLOCK * TCB,int * Lines,int * Cols)824 wcon_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
825 {
826     int result = ERR;
827 
828     T((T_CALLED("win32con::wcon_size(%p)"), TCB));
829 
830     if (okConsoleHandle(TCB) &&
831 	Lines != NULL &&
832 	Cols != NULL) {
833 	if (CON.buffered) {
834 	    *Lines = (int) (CON.SBI.dwSize.Y);
835 	    *Cols = (int) (CON.SBI.dwSize.X);
836 	} else {
837 	    *Lines = (int) (CON.SBI.srWindow.Bottom + 1 -
838 			    CON.SBI.srWindow.Top);
839 	    *Cols = (int) (CON.SBI.srWindow.Right + 1 -
840 			   CON.SBI.srWindow.Left);
841 	}
842 	result = OK;
843     }
844     returnCode(result);
845 }
846 
847 static int
wcon_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,int l GCC_UNUSED,int c GCC_UNUSED)848 wcon_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
849 	     int l GCC_UNUSED,
850 	     int c GCC_UNUSED)
851 {
852     AssertTCB();
853     return ERR;
854 }
855 
856 static int
wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB,int setFlag,TTY * buf)857 wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
858 {
859     DWORD dwFlag = 0;
860     tcflag_t iflag;
861     tcflag_t lflag;
862     int result = ERR;
863 
864     if (buf != NULL && okConsoleHandle(TCB)) {
865 
866 	if (setFlag) {
867 	    iflag = buf->c_iflag;
868 	    lflag = buf->c_lflag;
869 
870 	    GetConsoleMode(CON.inp, &dwFlag);
871 
872 	    if (lflag & ICANON)
873 		dwFlag |= ENABLE_LINE_INPUT;
874 	    else
875 		dwFlag &= (DWORD) (~ENABLE_LINE_INPUT);
876 
877 	    if (lflag & ECHO)
878 		dwFlag |= ENABLE_ECHO_INPUT;
879 	    else
880 		dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT);
881 
882 	    if (iflag & BRKINT)
883 		dwFlag |= ENABLE_PROCESSED_INPUT;
884 	    else
885 		dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT);
886 
887 	    dwFlag |= ENABLE_MOUSE_INPUT;
888 
889 	    buf->c_iflag = iflag;
890 	    buf->c_lflag = lflag;
891 	    SetConsoleMode(CON.inp, dwFlag);
892 	    TCB->term.Nttyb = *buf;
893 	} else {
894 	    iflag = TCB->term.Nttyb.c_iflag;
895 	    lflag = TCB->term.Nttyb.c_lflag;
896 	    GetConsoleMode(CON.inp, &dwFlag);
897 
898 	    if (dwFlag & ENABLE_LINE_INPUT)
899 		lflag |= ICANON;
900 	    else
901 		lflag &= (tcflag_t) (~ICANON);
902 
903 	    if (dwFlag & ENABLE_ECHO_INPUT)
904 		lflag |= ECHO;
905 	    else
906 		lflag &= (tcflag_t) (~ECHO);
907 
908 	    if (dwFlag & ENABLE_PROCESSED_INPUT)
909 		iflag |= BRKINT;
910 	    else
911 		iflag &= (tcflag_t) (~BRKINT);
912 
913 	    TCB->term.Nttyb.c_iflag = iflag;
914 	    TCB->term.Nttyb.c_lflag = lflag;
915 
916 	    *buf = TCB->term.Nttyb;
917 	}
918 	result = OK;
919     }
920     return result;
921 }
922 
923 #define MIN_WIDE 80
924 #define MIN_HIGH 24
925 
926 /*
927  * In "normal" mode, reset the buffer- and window-sizes back to their original values.
928  */
929 static void
set_scrollback(bool normal,CONSOLE_SCREEN_BUFFER_INFO * info)930 set_scrollback(bool normal, CONSOLE_SCREEN_BUFFER_INFO * info)
931 {
932     SMALL_RECT rect;
933     COORD coord;
934     bool changed = FALSE;
935 
936     T((T_CALLED("win32con::set_scrollback(%s)"),
937        (normal
938 	? "normal"
939 	: "application")));
940 
941     T(("... SBI.srWindow %d,%d .. %d,%d",
942        info->srWindow.Top,
943        info->srWindow.Left,
944        info->srWindow.Bottom,
945        info->srWindow.Right));
946     T(("... SBI.dwSize %dx%d",
947        info->dwSize.Y,
948        info->dwSize.X));
949 
950     if (normal) {
951 	rect = info->srWindow;
952 	coord = info->dwSize;
953 	if (memcmp(info, &CON.SBI, sizeof(*info)) != 0) {
954 	    changed = TRUE;
955 	    CON.SBI = *info;
956 	}
957     } else {
958 	int high = info->srWindow.Bottom - info->srWindow.Top + 1;
959 	int wide = info->srWindow.Right - info->srWindow.Left + 1;
960 
961 	if (high < MIN_HIGH) {
962 	    T(("... height %d < %d", high, MIN_HIGH));
963 	    high = MIN_HIGH;
964 	    changed = TRUE;
965 	}
966 	if (wide < MIN_WIDE) {
967 	    T(("... width %d < %d", wide, MIN_WIDE));
968 	    wide = MIN_WIDE;
969 	    changed = TRUE;
970 	}
971 
972 	rect.Left =
973 	    rect.Top = 0;
974 	rect.Right = (SHORT) (wide - 1);
975 	rect.Bottom = (SHORT) (high - 1);
976 
977 	coord.X = (SHORT) wide;
978 	coord.Y = (SHORT) high;
979 
980 	if (info->dwSize.Y != high ||
981 	    info->dwSize.X != wide ||
982 	    info->srWindow.Top != 0 ||
983 	    info->srWindow.Left != 0) {
984 	    changed = TRUE;
985 	}
986 
987     }
988 
989     if (changed) {
990 	T(("... coord %d,%d", coord.Y, coord.X));
991 	T(("... rect %d,%d - %d,%d",
992 	   rect.Top, rect.Left,
993 	   rect.Bottom, rect.Right));
994 	SetConsoleScreenBufferSize(CON.hdl, coord);	/* dwSize */
995 	SetConsoleWindowInfo(CON.hdl, TRUE, &rect);	/* srWindow */
996 	get_SBI();
997     }
998     returnVoid;
999 }
1000 
1001 static int
wcon_mode(TERMINAL_CONTROL_BLOCK * TCB,int progFlag,int defFlag)1002 wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
1003 {
1004     SCREEN *sp;
1005     TERMINAL *_term = (TERMINAL *) TCB;
1006     int code = ERR;
1007 
1008     if (okConsoleHandle(TCB)) {
1009 	sp = TCB->csp;
1010 
1011 	T((T_CALLED("win32con::wcon_mode(%p, prog=%d, def=%d)"),
1012 	   TCB, progFlag, defFlag));
1013 
1014 	CON.progMode = progFlag;
1015 	CON.lastOut = progFlag ? CON.hdl : CON.out;
1016 	SetConsoleActiveScreenBuffer(CON.lastOut);
1017 
1018 	if (progFlag) /* prog mode */  {
1019 	    if (defFlag) {
1020 		if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
1021 		    _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS);
1022 		    code = OK;
1023 		}
1024 	    } else {
1025 		/* reset_prog_mode */
1026 		if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
1027 		    if (sp) {
1028 			if (sp->_keypad_on)
1029 			    _nc_keypad(sp, TRUE);
1030 		    }
1031 		    if (!CON.buffered) {
1032 			set_scrollback(FALSE, &CON.SBI);
1033 		    }
1034 		    code = OK;
1035 		}
1036 	    }
1037 	    T(("... buffered:%d, clear:%d", CON.buffered, CurScreen(sp)->_clear));
1038 	} else {		/* shell mode */
1039 	    if (defFlag) {
1040 		/* def_shell_mode */
1041 		if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
1042 		    code = OK;
1043 		}
1044 	    } else {
1045 		/* reset_shell_mode */
1046 		if (sp) {
1047 		    _nc_keypad(sp, FALSE);
1048 		    NCURSES_SP_NAME(_nc_flush) (sp);
1049 		}
1050 		code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb));
1051 		if (!CON.buffered) {
1052 		    set_scrollback(TRUE, &CON.save_SBI);
1053 		    if (!restore_original_screen())
1054 			code = ERR;
1055 		}
1056 		SetConsoleCursorInfo(CON.hdl, &CON.save_CI);
1057 	    }
1058 	}
1059 
1060     }
1061     returnCode(code);
1062 }
1063 
1064 static void
wcon_screen_init(SCREEN * sp GCC_UNUSED)1065 wcon_screen_init(SCREEN *sp GCC_UNUSED)
1066 {
1067 }
1068 
1069 static void
wcon_wrap(SCREEN * sp GCC_UNUSED)1070 wcon_wrap(SCREEN *sp GCC_UNUSED)
1071 {
1072 }
1073 
1074 static int
rkeycompare(const void * el1,const void * el2)1075 rkeycompare(const void *el1, const void *el2)
1076 {
1077     WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
1078     WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
1079 
1080     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
1081 }
1082 
1083 static int
keycompare(const void * el1,const void * el2)1084 keycompare(const void *el1, const void *el2)
1085 {
1086     WORD key1 = HIWORD((*((const LONG *) el1)));
1087     WORD key2 = HIWORD((*((const LONG *) el2)));
1088 
1089     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
1090 }
1091 
1092 static int
MapKey(WORD vKey)1093 MapKey(WORD vKey)
1094 {
1095     WORD nKey = 0;
1096     void *res;
1097     LONG key = GenMap(vKey, 0);
1098     int code = -1;
1099 
1100     res = bsearch(&key,
1101 		  CON.map,
1102 		  (size_t) (N_INI + FKEYS),
1103 		  sizeof(keylist[0]),
1104 		  keycompare);
1105     if (res) {
1106 	key = *((LONG *) res);
1107 	nKey = LOWORD(key);
1108 	code = (int) (nKey & 0x7fff);
1109 	if (nKey & 0x8000)
1110 	    code = -code;
1111     }
1112     return code;
1113 }
1114 
1115 static int
AnsiKey(WORD vKey)1116 AnsiKey(WORD vKey)
1117 {
1118     WORD nKey = 0;
1119     void *res;
1120     LONG key = GenMap(vKey, 0);
1121     int code = -1;
1122 
1123     res = bsearch(&key,
1124 		  CON.ansi_map,
1125 		  (size_t) (N_INI + FKEYS),
1126 		  sizeof(keylist[0]),
1127 		  keycompare);
1128     if (res) {
1129 	key = *((LONG *) res);
1130 	nKey = LOWORD(key);
1131 	code = (int) (nKey & 0x7fff);
1132 	if (nKey & 0x8000)
1133 	    code = -code;
1134     }
1135     return code;
1136 }
1137 
1138 static void
wcon_release(TERMINAL_CONTROL_BLOCK * TCB)1139 wcon_release(TERMINAL_CONTROL_BLOCK * TCB)
1140 {
1141     T((T_CALLED("win32con::wcon_release(%p)"), TCB));
1142 
1143     AssertTCB();
1144     if (TCB->prop)
1145 	free(TCB->prop);
1146 
1147     returnVoid;
1148 }
1149 
1150 static bool
read_screen_data(void)1151 read_screen_data(void)
1152 {
1153     bool result = FALSE;
1154     COORD bufferCoord;
1155     size_t want;
1156 
1157     CON.save_size.X = (SHORT) (CON.save_region.Right
1158 			       - CON.save_region.Left + 1);
1159     CON.save_size.Y = (SHORT) (CON.save_region.Bottom
1160 			       - CON.save_region.Top + 1);
1161 
1162     want = (size_t) (CON.save_size.X * CON.save_size.Y);
1163 
1164     if ((CON.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
1165 	bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0);
1166 	bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0);
1167 
1168 	T(("... reading console %s %dx%d into %d,%d - %d,%d at %d,%d",
1169 	   CON.window_only ? "window" : "buffer",
1170 	   CON.save_size.Y, CON.save_size.X,
1171 	   CON.save_region.Top,
1172 	   CON.save_region.Left,
1173 	   CON.save_region.Bottom,
1174 	   CON.save_region.Right,
1175 	   bufferCoord.Y,
1176 	   bufferCoord.X));
1177 
1178 	if (read_screen(CON.hdl,
1179 			CON.save_screen,
1180 			CON.save_size,
1181 			bufferCoord,
1182 			&CON.save_region)) {
1183 	    result = TRUE;
1184 	} else {
1185 	    T((" error %#lx", (unsigned long) GetLastError()));
1186 	    FreeAndNull(CON.save_screen);
1187 	}
1188     }
1189 
1190     return result;
1191 }
1192 
1193 /*
1194  * Attempt to save the screen contents.  PDCurses does this if
1195  * PDC_RESTORE_SCREEN is set, giving the same visual appearance on
1196  * restoration as if the library had allocated a console buffer.  MSDN
1197  * says that the data which can be read is limited to 64Kb (and may be
1198  * less).
1199  */
1200 static bool
save_original_screen(void)1201 save_original_screen(void)
1202 {
1203     bool result = FALSE;
1204 
1205     CON.save_region.Top = 0;
1206     CON.save_region.Left = 0;
1207     CON.save_region.Bottom = (SHORT) (CON.SBI.dwSize.Y - 1);
1208     CON.save_region.Right = (SHORT) (CON.SBI.dwSize.X - 1);
1209 
1210     if (read_screen_data()) {
1211 	result = TRUE;
1212     } else {
1213 
1214 	CON.save_region.Top = CON.SBI.srWindow.Top;
1215 	CON.save_region.Left = CON.SBI.srWindow.Left;
1216 	CON.save_region.Bottom = CON.SBI.srWindow.Bottom;
1217 	CON.save_region.Right = CON.SBI.srWindow.Right;
1218 
1219 	CON.window_only = TRUE;
1220 
1221 	if (read_screen_data()) {
1222 	    result = TRUE;
1223 	}
1224     }
1225 
1226     T(("... save original screen contents %s", result ? "ok" : "err"));
1227     return result;
1228 }
1229 
1230 static void
wcon_init(TERMINAL_CONTROL_BLOCK * TCB)1231 wcon_init(TERMINAL_CONTROL_BLOCK * TCB)
1232 {
1233     T((T_CALLED("win32con::wcon_init(%p)"), TCB));
1234 
1235     AssertTCB();
1236 
1237     if (TCB) {
1238 	if (!InitConsole()) {
1239 	    returnVoid;
1240 	}
1241 
1242 	TCB->info.initcolor = TRUE;
1243 	TCB->info.canchange = FALSE;
1244 	TCB->info.hascolor = TRUE;
1245 	TCB->info.caninit = TRUE;
1246 
1247 	TCB->info.maxpairs = NUMPAIRS;
1248 	TCB->info.maxcolors = 8;
1249 	TCB->info.numlabels = 0;
1250 	TCB->info.labelwidth = 0;
1251 	TCB->info.labelheight = 0;
1252 	TCB->info.nocolorvideo = 1;
1253 	TCB->info.tabsize = 8;
1254 
1255 	TCB->info.numbuttons = CON.numButtons;
1256 	TCB->info.defaultPalette = _nc_cga_palette;
1257 
1258     }
1259     returnVoid;
1260 }
1261 
1262 static void
wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB,int pair,int f,int b)1263 wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB,
1264 	      int pair,
1265 	      int f,
1266 	      int b)
1267 {
1268     SCREEN *sp;
1269 
1270     if (okConsoleHandle(TCB)) {
1271 	SetSP();
1272 
1273 	if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
1274 	    && (b >= 0) && (b < 8)) {
1275 	    CON.pairs[pair] = MapColor(true, f) | MapColor(false, b);
1276 	}
1277     }
1278 }
1279 
1280 static void
wcon_initcolor(TERMINAL_CONTROL_BLOCK * TCB,int color GCC_UNUSED,int r GCC_UNUSED,int g GCC_UNUSED,int b GCC_UNUSED)1281 wcon_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
1282 	       int color GCC_UNUSED,
1283 	       int r GCC_UNUSED,
1284 	       int g GCC_UNUSED,
1285 	       int b GCC_UNUSED)
1286 {
1287     SCREEN *sp;
1288 
1289     AssertTCB();
1290     SetSP();
1291 }
1292 
1293 static void
wcon_do_color(TERMINAL_CONTROL_BLOCK * TCB,int old_pair GCC_UNUSED,int pair GCC_UNUSED,int reverse GCC_UNUSED,int (* outc)(SCREEN *,int)GCC_UNUSED)1294 wcon_do_color(TERMINAL_CONTROL_BLOCK * TCB,
1295 	      int old_pair GCC_UNUSED,
1296 	      int pair GCC_UNUSED,
1297 	      int reverse GCC_UNUSED,
1298 	      int (*outc) (SCREEN *, int) GCC_UNUSED
1299 )
1300 {
1301     SCREEN *sp;
1302 
1303     AssertTCB();
1304     SetSP();
1305 }
1306 
1307 static void
wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB)1308 wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
1309 {
1310     SCREEN *sp;
1311 
1312     if (okConsoleHandle(TCB)) {
1313 	SetSP();
1314 
1315 	sp->_mouse_type = M_TERM_DRIVER;
1316     }
1317 }
1318 
1319 static int
wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB,int delay EVENTLIST_2nd (_nc_eventlist * evl))1320 wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB,
1321 	       int delay
1322 	       EVENTLIST_2nd(_nc_eventlist * evl))
1323 {
1324     int rc = 0;
1325     SCREEN *sp;
1326 
1327     if (okConsoleHandle(TCB)) {
1328 	SetSP();
1329 
1330 	if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1331 	    rc = TW_MOUSE;
1332 	} else {
1333 	    rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
1334 					  TWAIT_MASK,
1335 					  delay,
1336 					  (int *) 0
1337 					  EVENTLIST_2nd(evl));
1338 	}
1339     }
1340 
1341     return rc;
1342 }
1343 
1344 static int
wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,int yold GCC_UNUSED,int xold GCC_UNUSED,int y,int x)1345 wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
1346 	   int yold GCC_UNUSED, int xold GCC_UNUSED,
1347 	   int y, int x)
1348 {
1349     int ret = ERR;
1350     if (okConsoleHandle(TCB)) {
1351 	COORD loc;
1352 	loc.X = (short) x;
1353 	loc.Y = (short) (y + AdjustY());
1354 	SetConsoleCursorPosition(CON.hdl, loc);
1355 	ret = OK;
1356     }
1357     return ret;
1358 }
1359 
1360 static void
wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,int labnum GCC_UNUSED,char * text GCC_UNUSED)1361 wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
1362 	     int labnum GCC_UNUSED,
1363 	     char *text GCC_UNUSED)
1364 {
1365     SCREEN *sp;
1366 
1367     AssertTCB();
1368     SetSP();
1369 }
1370 
1371 static void
wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,int OnFlag GCC_UNUSED)1372 wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1373 		  int OnFlag GCC_UNUSED)
1374 {
1375     SCREEN *sp;
1376 
1377     AssertTCB();
1378     SetSP();
1379 }
1380 
1381 static chtype
wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)1382 wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1383 {
1384     chtype res = A_NORMAL;
1385     res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1386     return res;
1387 }
1388 
1389 static void
wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)1390 wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1391 {
1392     SCREEN *sp;
1393 
1394     AssertTCB();
1395     SetSP();
1396 }
1397 
1398 static void
wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,chtype * real_map GCC_UNUSED,chtype * fake_map GCC_UNUSED)1399 wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1400 	     chtype *real_map GCC_UNUSED,
1401 	     chtype *fake_map GCC_UNUSED)
1402 {
1403 #define DATA(a,b) { a, b }
1404     static struct {
1405 	int acs_code;
1406 	int use_code;
1407     } table[] = {
1408 	DATA('a', 0xb1),	/* ACS_CKBOARD  */
1409 	    DATA('f', 0xf8),	/* ACS_DEGREE   */
1410 	    DATA('g', 0xf1),	/* ACS_PLMINUS  */
1411 	    DATA('j', 0xd9),	/* ACS_LRCORNER */
1412 	    DATA('l', 0xda),	/* ACS_ULCORNER */
1413 	    DATA('k', 0xbf),	/* ACS_URCORNER */
1414 	    DATA('m', 0xc0),	/* ACS_LLCORNER */
1415 	    DATA('n', 0xc5),	/* ACS_PLUS     */
1416 	    DATA('q', 0xc4),	/* ACS_HLINE    */
1417 	    DATA('t', 0xc3),	/* ACS_LTEE     */
1418 	    DATA('u', 0xb4),	/* ACS_RTEE     */
1419 	    DATA('v', 0xc1),	/* ACS_BTEE     */
1420 	    DATA('w', 0xc2),	/* ACS_TTEE     */
1421 	    DATA('x', 0xb3),	/* ACS_VLINE    */
1422 	    DATA('y', 0xf3),	/* ACS_LEQUAL   */
1423 	    DATA('z', 0xf2),	/* ACS_GEQUAL   */
1424 	    DATA('0', 0xdb),	/* ACS_BLOCK    */
1425 	    DATA('{', 0xe3),	/* ACS_PI       */
1426 	    DATA('}', 0x9c),	/* ACS_STERLING */
1427 	    DATA(',', 0xae),	/* ACS_LARROW   */
1428 	    DATA('+', 0xaf),	/* ACS_RARROW   */
1429 	    DATA('~', 0xf9),	/* ACS_BULLET   */
1430     };
1431 #undef DATA
1432     unsigned n;
1433 
1434     SCREEN *sp;
1435     if (okConsoleHandle(TCB)) {
1436 	SetSP();
1437 
1438 	for (n = 0; n < SIZEOF(table); ++n) {
1439 	    real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET;
1440 	    if (sp != 0)
1441 		sp->_screen_acs_map[table[n].acs_code] = TRUE;
1442 	}
1443     }
1444 }
1445 
1446 static ULONGLONG
tdiff(FILETIME fstart,FILETIME fend)1447 tdiff(FILETIME fstart, FILETIME fend)
1448 {
1449     ULARGE_INTEGER ustart;
1450     ULARGE_INTEGER uend;
1451     ULONGLONG diff;
1452 
1453     ustart.LowPart = fstart.dwLowDateTime;
1454     ustart.HighPart = fstart.dwHighDateTime;
1455     uend.LowPart = fend.dwLowDateTime;
1456     uend.HighPart = fend.dwHighDateTime;
1457 
1458     diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1459     return diff;
1460 }
1461 
1462 static int
Adjust(int milliseconds,int diff)1463 Adjust(int milliseconds, int diff)
1464 {
1465     if (milliseconds != INFINITY) {
1466 	milliseconds -= diff;
1467 	if (milliseconds < 0)
1468 	    milliseconds = 0;
1469     }
1470     return milliseconds;
1471 }
1472 
1473 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1474 		     FROM_LEFT_2ND_BUTTON_PRESSED | \
1475 		     FROM_LEFT_3RD_BUTTON_PRESSED | \
1476 		     FROM_LEFT_4TH_BUTTON_PRESSED | \
1477 		     RIGHTMOST_BUTTON_PRESSED)
1478 
1479 static int
decode_mouse(SCREEN * sp,int mask)1480 decode_mouse(SCREEN *sp, int mask)
1481 {
1482     int result = 0;
1483 
1484     (void) sp;
1485     assert(sp && console_initialized);
1486 
1487     if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1488 	result |= BUTTON1_PRESSED;
1489     if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1490 	result |= BUTTON2_PRESSED;
1491     if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1492 	result |= BUTTON3_PRESSED;
1493     if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1494 	result |= BUTTON4_PRESSED;
1495 
1496     if (mask & RIGHTMOST_BUTTON_PRESSED) {
1497 	switch (CON.numButtons) {
1498 	case 1:
1499 	    result |= BUTTON1_PRESSED;
1500 	    break;
1501 	case 2:
1502 	    result |= BUTTON2_PRESSED;
1503 	    break;
1504 	case 3:
1505 	    result |= BUTTON3_PRESSED;
1506 	    break;
1507 	case 4:
1508 	    result |= BUTTON4_PRESSED;
1509 	    break;
1510 	}
1511     }
1512 
1513     return result;
1514 }
1515 
1516 static int
console_twait(SCREEN * sp,HANDLE fd,int mode,int milliseconds,int * timeleft EVENTLIST_2nd (_nc_eventlist * evl))1517 console_twait(
1518 		 SCREEN *sp,
1519 		 HANDLE fd,
1520 		 int mode,
1521 		 int milliseconds,
1522 		 int *timeleft
1523 		 EVENTLIST_2nd(_nc_eventlist * evl))
1524 {
1525     INPUT_RECORD inp_rec;
1526     BOOL b;
1527     DWORD nRead = 0, rc = (DWORD) (-1);
1528     int code = 0;
1529     FILETIME fstart;
1530     FILETIME fend;
1531     int diff;
1532     bool isImmed = (milliseconds == 0);
1533 
1534 #ifdef NCURSES_WGETCH_EVENTS
1535     (void) evl;			/* TODO: implement wgetch-events */
1536 #endif
1537 
1538 #define CONSUME() ReadConsoleInput(fd,&inp_rec,1,&nRead)
1539 
1540     assert(sp);
1541 
1542     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1543 		      milliseconds, mode));
1544 
1545     if (milliseconds < 0)
1546 	milliseconds = INFINITY;
1547 
1548     memset(&inp_rec, 0, sizeof(inp_rec));
1549 
1550     while (true) {
1551 	GetSystemTimeAsFileTime(&fstart);
1552 	rc = WaitForSingleObject(fd, (DWORD) milliseconds);
1553 	GetSystemTimeAsFileTime(&fend);
1554 	diff = (int) tdiff(fstart, fend);
1555 	milliseconds = Adjust(milliseconds, diff);
1556 
1557 	if (!isImmed && milliseconds <= 0)
1558 	    break;
1559 
1560 	if (rc == WAIT_OBJECT_0) {
1561 	    if (mode) {
1562 		b = GetNumberOfConsoleInputEvents(fd, &nRead);
1563 		if (b && nRead > 0) {
1564 		    b = PeekConsoleInput(fd, &inp_rec, 1, &nRead);
1565 		    if (b && nRead > 0) {
1566 			switch (inp_rec.EventType) {
1567 			case KEY_EVENT:
1568 			    if (mode & TW_INPUT) {
1569 				WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1570 				char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1571 
1572 				if (inp_rec.Event.KeyEvent.bKeyDown) {
1573 				    if (0 == ch) {
1574 					int nKey = MapKey(vk);
1575 					if (nKey < 0) {
1576 					    CONSUME();
1577 					    continue;
1578 					}
1579 				    }
1580 				    code = TW_INPUT;
1581 				    goto end;
1582 				} else {
1583 				    CONSUME();
1584 				}
1585 			    }
1586 			    continue;
1587 			case MOUSE_EVENT:
1588 			    if (decode_mouse(sp,
1589 					     (inp_rec.Event.MouseEvent.dwButtonState
1590 					      & BUTTON_MASK)) == 0) {
1591 				CONSUME();
1592 			    } else if (mode & TW_MOUSE) {
1593 				code = TW_MOUSE;
1594 				goto end;
1595 			    }
1596 			    continue;
1597 			    /* e.g., FOCUS_EVENT */
1598 			default:
1599 			    CONSUME();
1600 			    selectActiveHandle();
1601 			    continue;
1602 			}
1603 		    }
1604 		}
1605 	    }
1606 	    continue;
1607 	} else {
1608 	    if (rc != WAIT_TIMEOUT) {
1609 		code = -1;
1610 		break;
1611 	    } else {
1612 		code = 0;
1613 		break;
1614 	    }
1615 	}
1616     }
1617   end:
1618 
1619     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1620 		      code, errno, milliseconds));
1621 
1622     if (timeleft)
1623 	*timeleft = milliseconds;
1624 
1625     return code;
1626 }
1627 
1628 static int
wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,int mode,int milliseconds,int * timeleft EVENTLIST_2nd (_nc_eventlist * evl))1629 wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,
1630 	   int mode,
1631 	   int milliseconds,
1632 	   int *timeleft
1633 	   EVENTLIST_2nd(_nc_eventlist * evl))
1634 {
1635     SCREEN *sp;
1636     int code = 0;
1637 
1638     if (okConsoleHandle(TCB)) {
1639 	SetSP();
1640 
1641 	code = console_twait(sp,
1642 			     CON.inp,
1643 			     mode,
1644 			     milliseconds,
1645 			     timeleft EVENTLIST_2nd(evl));
1646     }
1647     return code;
1648 }
1649 
1650 static bool
handle_mouse(SCREEN * sp,MOUSE_EVENT_RECORD mer)1651 handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer)
1652 {
1653     MEVENT work;
1654     bool result = FALSE;
1655 
1656     assert(sp);
1657 
1658     sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1659     sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1660 
1661     /*
1662      * We're only interested if the button is pressed or released.
1663      * FIXME: implement continuous event-tracking.
1664      */
1665     if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1666 
1667 	memset(&work, 0, sizeof(work));
1668 
1669 	if (sp->_drv_mouse_new_buttons) {
1670 
1671 	    work.bstate |= (mmask_t) decode_mouse(sp, sp->_drv_mouse_new_buttons);
1672 
1673 	} else {
1674 
1675 	    /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1676 	    work.bstate |= (mmask_t) (decode_mouse(sp,
1677 						   sp->_drv_mouse_old_buttons)
1678 				      >> 1);
1679 
1680 	    result = TRUE;
1681 	}
1682 
1683 	work.x = mer.dwMousePosition.X;
1684 	work.y = mer.dwMousePosition.Y - AdjustY();
1685 
1686 	sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1687 	sp->_drv_mouse_tail += 1;
1688     }
1689 
1690     return result;
1691 }
1692 
1693 static int
wcon_read(TERMINAL_CONTROL_BLOCK * TCB,int * buf)1694 wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1695 {
1696     SCREEN *sp;
1697     int n = -1;
1698 
1699     T((T_CALLED("win32con::wcon_read(%p)"), TCB));
1700 
1701     assert(buf);
1702     if (okConsoleHandle(TCB)) {
1703 	SetSP();
1704 
1705 	n = _nc_mingw_console_read(sp, CON.inp, buf);
1706     }
1707     returnCode(n);
1708 }
1709 
1710 static int
wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,int ms)1711 wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1712 {
1713     T((T_CALLED("win32con::wcon_nap(%p, %d)"), TCB, ms));
1714     Sleep((DWORD) ms);
1715     returnCode(OK);
1716 }
1717 
1718 static int
wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,int mode)1719 wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int mode)
1720 {
1721     int res = -1;
1722 
1723     T((T_CALLED("win32con:wcon_cursorSet(%d)"), mode));
1724     if (okConsoleHandle(TCB)) {
1725 	CONSOLE_CURSOR_INFO this_CI = CON.save_CI;
1726 	switch (mode) {
1727 	case 0:
1728 	    this_CI.bVisible = FALSE;
1729 	    break;
1730 	case 1:
1731 	    break;
1732 	case 2:
1733 	    this_CI.dwSize = 100;
1734 	    break;
1735 	}
1736 	SetConsoleCursorInfo(CON.hdl, &this_CI);
1737     }
1738     returnCode(res);
1739 }
1740 
1741 static bool
wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,int keycode)1742 wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int keycode)
1743 {
1744     WORD nKey;
1745     void *res;
1746     bool found = FALSE;
1747     LONG key = GenMap(0, (WORD) keycode);
1748 
1749     T((T_CALLED("win32con::wcon_kyExist(%d)"), keycode));
1750     res = bsearch(&key,
1751 		  CON.rmap,
1752 		  (size_t) (N_INI + FKEYS),
1753 		  sizeof(keylist[0]),
1754 		  rkeycompare);
1755     if (res) {
1756 	key = *((LONG *) res);
1757 	nKey = LOWORD(key);
1758 	if (!(nKey & 0x8000))
1759 	    found = TRUE;
1760     }
1761     returnCode(found);
1762 }
1763 
1764 static int
wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB,int flag GCC_UNUSED)1765 wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1766 {
1767     SCREEN *sp;
1768     int code = ERR;
1769 
1770     T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag));
1771 
1772     if (okConsoleHandle(TCB)) {
1773 	SetSP();
1774 
1775 	if (sp) {
1776 	    code = OK;
1777 	}
1778     }
1779     returnCode(code);
1780 }
1781 
1782 static int
wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB,int keycode,int flag)1783 wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB,
1784 	   int keycode,
1785 	   int flag)
1786 {
1787     int code = ERR;
1788     SCREEN *sp;
1789     WORD nKey;
1790     WORD vKey;
1791     void *res;
1792     LONG key = GenMap(0, (WORD) keycode);
1793 
1794     T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag));
1795 
1796     if (okConsoleHandle(TCB)) {
1797 	SetSP();
1798 
1799 	if (sp) {
1800 	    res = bsearch(&key,
1801 			  CON.rmap,
1802 			  (size_t) (N_INI + FKEYS),
1803 			  sizeof(keylist[0]),
1804 			  rkeycompare);
1805 	    if (res) {
1806 		key = *((LONG *) res);
1807 		vKey = HIWORD(key);
1808 		nKey = (LOWORD(key)) & 0x7fff;
1809 		if (!flag)
1810 		    nKey |= 0x8000;
1811 		*(LONG *) res = GenMap(vKey, nKey);
1812 	    }
1813 	}
1814     }
1815     returnCode(code);
1816 }
1817 
1818 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1819     FALSE,
1820 	wcon_name,		/* Name */
1821 	wcon_CanHandle,		/* CanHandle */
1822 	wcon_init,		/* init */
1823 	wcon_release,		/* release */
1824 	wcon_size,		/* size */
1825 	wcon_sgmode,		/* sgmode */
1826 	wcon_conattr,		/* conattr */
1827 	wcon_mvcur,		/* hwcur */
1828 	wcon_mode,		/* mode */
1829 	wcon_rescol,		/* rescol */
1830 	wcon_rescolors,		/* rescolors */
1831 	wcon_setcolor,		/* color */
1832 	wcon_dobeepflash,	/* DoBeepFlash */
1833 	wcon_initpair,		/* initpair */
1834 	wcon_initcolor,		/* initcolor */
1835 	wcon_do_color,		/* docolor */
1836 	wcon_initmouse,		/* initmouse */
1837 	wcon_testmouse,		/* testmouse */
1838 	wcon_setfilter,		/* setfilter */
1839 	wcon_hwlabel,		/* hwlabel */
1840 	wcon_hwlabelOnOff,	/* hwlabelOnOff */
1841 	wcon_doupdate,		/* update */
1842 	wcon_defaultcolors,	/* defaultcolors */
1843 	wcon_print,		/* print */
1844 	wcon_size,		/* getsize */
1845 	wcon_setsize,		/* setsize */
1846 	wcon_initacs,		/* initacs */
1847 	wcon_screen_init,	/* scinit */
1848 	wcon_wrap,		/* scexit */
1849 	wcon_twait,		/* twait */
1850 	wcon_read,		/* read */
1851 	wcon_nap,		/* nap */
1852 	wcon_kpad,		/* kpad */
1853 	wcon_keyok,		/* kyOk */
1854 	wcon_kyExist,		/* kyExist */
1855 	wcon_cursorSet		/* cursorSet */
1856 };
1857 
1858 /* --------------------------------------------------------- */
1859 
1860 static HANDLE
get_handle(int fd)1861 get_handle(int fd)
1862 {
1863     intptr_t value = _get_osfhandle(fd);
1864     return (HANDLE) value;
1865 }
1866 
1867 #if WINVER >= 0x0600
1868 /*   This function tests, whether or not the ncurses application
1869      is running as a descendant of MSYS2/cygwin mintty terminal
1870      application. mintty doesn't use Windows Console for it's screen
1871      I/O, so the native Windows _isatty doesn't recognize it as
1872      character device. But we can discover we are at the end of an
1873      Pipe and can query to server side of the pipe, looking whether
1874      or not this is mintty.
1875  */
1876 static int
_ismintty(int fd,LPHANDLE pMinTTY)1877 _ismintty(int fd, LPHANDLE pMinTTY)
1878 {
1879     HANDLE handle = get_handle(fd);
1880     DWORD dw;
1881     int code = 0;
1882 
1883     T((T_CALLED("win32con::_ismintty(%d, %p)"), fd, pMinTTY));
1884 
1885     if (handle != INVALID_HANDLE_VALUE) {
1886 	dw = GetFileType(handle);
1887 	if (dw == FILE_TYPE_PIPE) {
1888 	    if (GetNamedPipeInfo(handle, 0, 0, 0, 0)) {
1889 		ULONG pPid;
1890 		/* Requires NT6 */
1891 		if (GetNamedPipeServerProcessId(handle, &pPid)) {
1892 		    TCHAR buf[MAX_PATH];
1893 		    DWORD len = 0;
1894 		    /* These security attributes may allow us to
1895 		       create a remote thread in mintty to manipulate
1896 		       the terminal state remotely */
1897 		    HANDLE pHandle = OpenProcess(
1898 						    PROCESS_CREATE_THREAD
1899 						    | PROCESS_QUERY_INFORMATION
1900 						    | PROCESS_VM_OPERATION
1901 						    | PROCESS_VM_WRITE
1902 						    | PROCESS_VM_READ,
1903 						    FALSE,
1904 						    pPid);
1905 		    if (pMinTTY)
1906 			*pMinTTY = INVALID_HANDLE_VALUE;
1907 		    if (pHandle != INVALID_HANDLE_VALUE) {
1908 			if ((len = GetProcessImageFileName(
1909 							      pHandle,
1910 							      buf,
1911 							      (DWORD)
1912 							      array_length(buf)))) {
1913 			    TCHAR *pos = _tcsrchr(buf, _T('\\'));
1914 			    if (pos) {
1915 				pos++;
1916 				if (_tcsnicmp(pos, _TEXT("mintty.exe"), 10)
1917 				    == 0) {
1918 				    if (pMinTTY)
1919 					*pMinTTY = pHandle;
1920 				    code = 1;
1921 				}
1922 			    }
1923 			}
1924 		    }
1925 		}
1926 	    }
1927 	}
1928     }
1929     returnCode(code);
1930 }
1931 #endif
1932 
1933 /*   Borrowed from ansicon project.
1934      Check whether or not an I/O handle is associated with
1935      a Windows console.
1936 */
1937 static BOOL
IsConsoleHandle(HANDLE hdl)1938 IsConsoleHandle(HANDLE hdl)
1939 {
1940     DWORD dwFlag = 0;
1941     BOOL result;
1942 
1943     if (!GetConsoleMode(hdl, &dwFlag)) {
1944 	result = (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL);
1945     } else {
1946 	result = (int) (dwFlag & ENABLE_PROCESSED_OUTPUT);
1947     }
1948     return result;
1949 }
1950 
1951 /*   Our replacement for the systems _isatty to include also
1952      a test for mintty. This is called from the NC_ISATTY macro
1953      defined in curses.priv.h
1954  */
1955 int
_nc_mingw_isatty(int fd)1956 _nc_mingw_isatty(int fd)
1957 {
1958     int result = 0;
1959 
1960 #ifdef __MING32__
1961 #define SysISATTY(fd) _isatty(fd)
1962 #else
1963 #define SysISATTY(fd) isatty(fd)
1964 #endif
1965     if (SysISATTY(fd)) {
1966 	result = 1;
1967     } else {
1968 #if WINVER >= 0x0600
1969 	result = _ismintty(fd, NULL);
1970 #endif
1971     }
1972     return result;
1973 }
1974 
1975 /*   This is used when running in terminfo mode to discover,
1976      whether or not the "terminal" is actually a Windows
1977      Console. It's the responsibility of the console to deal
1978      with the terminal escape sequences that are sent by
1979      terminfo.
1980  */
1981 int
_nc_mingw_isconsole(int fd)1982 _nc_mingw_isconsole(int fd)
1983 {
1984     HANDLE hdl = get_handle(fd);
1985     int code = 0;
1986 
1987     T((T_CALLED("win32con::_nc_mingw_isconsole(%d)"), fd));
1988 
1989     code = (int) IsConsoleHandle(hdl);
1990 
1991     returnCode(code);
1992 }
1993 
1994 #define TC_PROLOGUE(fd) \
1995     SCREEN *sp;                                               \
1996     TERMINAL *term = 0;                                       \
1997     int code = ERR;                                           \
1998     if (_nc_screen_chain == 0)                                \
1999         return 0;                                             \
2000     for (each_screen(sp)) {                                   \
2001         if (sp->_term && (sp->_term->Filedes == fd)) {        \
2002             term = sp->_term;                                 \
2003             break;                                            \
2004         }                                                     \
2005     }                                                         \
2006     assert(term != 0)
2007 
2008 int
_nc_mingw_tcsetattr(int fd,int optional_action GCC_UNUSED,const struct termios * arg)2009 _nc_mingw_tcsetattr(
2010 		       int fd,
2011 		       int optional_action GCC_UNUSED,
2012 		       const struct termios *arg)
2013 {
2014     TC_PROLOGUE(fd);
2015 
2016     if (_nc_mingw_isconsole(fd)) {
2017 	DWORD dwFlag = 0;
2018 	HANDLE ofd = get_handle(fd);
2019 	if (ofd != INVALID_HANDLE_VALUE) {
2020 	    if (arg) {
2021 		if (arg->c_lflag & ICANON)
2022 		    dwFlag |= ENABLE_LINE_INPUT;
2023 		else
2024 		    dwFlag = dwFlag & (DWORD) (~ENABLE_LINE_INPUT);
2025 
2026 		if (arg->c_lflag & ECHO)
2027 		    dwFlag = dwFlag | ENABLE_ECHO_INPUT;
2028 		else
2029 		    dwFlag = dwFlag & (DWORD) (~ENABLE_ECHO_INPUT);
2030 
2031 		if (arg->c_iflag & BRKINT)
2032 		    dwFlag |= ENABLE_PROCESSED_INPUT;
2033 		else
2034 		    dwFlag = dwFlag & (DWORD) (~ENABLE_PROCESSED_INPUT);
2035 	    }
2036 	    dwFlag |= ENABLE_MOUSE_INPUT;
2037 	    SetConsoleMode(ofd, dwFlag);
2038 	    code = OK;
2039 	}
2040     }
2041     if (arg)
2042 	term->Nttyb = *arg;
2043 
2044     return code;
2045 }
2046 
2047 int
_nc_mingw_tcgetattr(int fd,struct termios * arg)2048 _nc_mingw_tcgetattr(int fd, struct termios *arg)
2049 {
2050     TC_PROLOGUE(fd);
2051 
2052     if (_nc_mingw_isconsole(fd)) {
2053 	if (arg)
2054 	    *arg = term->Nttyb;
2055     }
2056     return code;
2057 }
2058 
2059 int
_nc_mingw_tcflush(int fd,int queue)2060 _nc_mingw_tcflush(int fd, int queue)
2061 {
2062     TC_PROLOGUE(fd);
2063     (void) term;
2064 
2065     if (_nc_mingw_isconsole(fd)) {
2066 	if (queue == TCIFLUSH) {
2067 	    BOOL b = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
2068 	    if (!b)
2069 		return (int) GetLastError();
2070 	}
2071     }
2072     return code;
2073 }
2074 
2075 int
_nc_mingw_testmouse(SCREEN * sp,HANDLE fd,int delay EVENTLIST_2nd (_nc_eventlist * evl))2076 _nc_mingw_testmouse(
2077 		       SCREEN *sp,
2078 		       HANDLE fd,
2079 		       int delay
2080 		       EVENTLIST_2nd(_nc_eventlist * evl))
2081 {
2082     int rc = 0;
2083 
2084     assert(sp);
2085 
2086     if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
2087 	rc = TW_MOUSE;
2088     } else {
2089 	rc = console_twait(sp,
2090 			   fd,
2091 			   TWAIT_MASK,
2092 			   delay,
2093 			   (int *) 0
2094 			   EVENTLIST_2nd(evl));
2095     }
2096     return rc;
2097 }
2098 
2099 int
_nc_mingw_console_read(SCREEN * sp,HANDLE fd,int * buf)2100 _nc_mingw_console_read(
2101 			  SCREEN *sp,
2102 			  HANDLE fd,
2103 			  int *buf)
2104 {
2105     int rc = -1;
2106     INPUT_RECORD inp_rec;
2107     BOOL b;
2108     DWORD nRead;
2109     WORD vk;
2110 
2111     assert(sp);
2112     assert(buf);
2113 
2114     memset(&inp_rec, 0, sizeof(inp_rec));
2115 
2116     T((T_CALLED("_nc_mingw_console_read(%p)"), sp));
2117 
2118     while ((b = ReadConsoleInput(fd, &inp_rec, 1, &nRead))) {
2119 	if (b && nRead > 0) {
2120 	    if (rc < 0)
2121 		rc = 0;
2122 	    rc = rc + (int) nRead;
2123 	    if (inp_rec.EventType == KEY_EVENT) {
2124 		if (!inp_rec.Event.KeyEvent.bKeyDown)
2125 		    continue;
2126 		*buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
2127 		vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
2128 		/*
2129 		 * There are 24 virtual function-keys, and typically
2130 		 * 12 function-keys on a keyboard.  Use the shift-modifier
2131 		 * to provide the remaining 12 keys.
2132 		 */
2133 		if (vk >= VK_F1 && vk <= VK_F12) {
2134 		    if (inp_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) {
2135 			vk = (WORD) (vk + 12);
2136 		    }
2137 		}
2138 		if (*buf == 0) {
2139 		    int key = MapKey(vk);
2140 		    if (key < 0)
2141 			continue;
2142 		    if (sp->_keypad_on) {
2143 			*buf = key;
2144 		    } else {
2145 			ungetch('\0');
2146 			*buf = AnsiKey(vk);
2147 		    }
2148 		}
2149 		break;
2150 	    } else if (inp_rec.EventType == MOUSE_EVENT) {
2151 		if (handle_mouse(sp,
2152 				 inp_rec.Event.MouseEvent)) {
2153 		    *buf = KEY_MOUSE;
2154 		    break;
2155 		}
2156 	    }
2157 	    continue;
2158 	}
2159     }
2160     returnCode(rc);
2161 }
2162 
2163 static bool
InitConsole(void)2164 InitConsole(void)
2165 {
2166     /* initialize once, or not at all */
2167     if (!console_initialized) {
2168 	int i;
2169 	DWORD num_buttons;
2170 	WORD a;
2171 	BOOL buffered = TRUE;
2172 	BOOL b;
2173 
2174 	START_TRACE();
2175 	if (_nc_mingw_isatty(0)) {
2176 	    CON.isMinTTY = TRUE;
2177 	}
2178 
2179 	for (i = 0; i < (N_INI + FKEYS); i++) {
2180 	    if (i < N_INI) {
2181 		CON.rmap[i] = CON.map[i] =
2182 		    (DWORD) keylist[i];
2183 		CON.ansi_map[i] = (DWORD) ansi_keys[i];
2184 	    } else {
2185 		CON.rmap[i] = CON.map[i] =
2186 		    (DWORD) GenMap((VK_F1 + (i - N_INI)),
2187 				   (KEY_F(1) + (i - N_INI)));
2188 		CON.ansi_map[i] =
2189 		    (DWORD) GenMap((VK_F1 + (i - N_INI)),
2190 				   (';' + (i - N_INI)));
2191 	    }
2192 	}
2193 	qsort(CON.ansi_map,
2194 	      (size_t) (MAPSIZE),
2195 	      sizeof(keylist[0]),
2196 	      keycompare);
2197 	qsort(CON.map,
2198 	      (size_t) (MAPSIZE),
2199 	      sizeof(keylist[0]),
2200 	      keycompare);
2201 	qsort(CON.rmap,
2202 	      (size_t) (MAPSIZE),
2203 	      sizeof(keylist[0]),
2204 	      rkeycompare);
2205 
2206 	if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
2207 	    CON.numButtons = (int) num_buttons;
2208 	} else {
2209 	    CON.numButtons = 1;
2210 	}
2211 
2212 	a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
2213 	for (i = 0; i < NUMPAIRS; i++)
2214 	    CON.pairs[i] = a;
2215 
2216 	CON.inp = GetStdHandle(STD_INPUT_HANDLE);
2217 	CON.out = GetStdHandle(STD_OUTPUT_HANDLE);
2218 
2219 	b = AllocConsole();
2220 
2221 	if (!b)
2222 	    b = AttachConsole(ATTACH_PARENT_PROCESS);
2223 
2224 	if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
2225 	    T(("... will not buffer console"));
2226 	    buffered = FALSE;
2227 	    CON.hdl = CON.out;
2228 	} else {
2229 	    T(("... creating console buffer"));
2230 	    CON.hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
2231 						0,
2232 						NULL,
2233 						CONSOLE_TEXTMODE_BUFFER,
2234 						NULL);
2235 	}
2236 
2237 	if (CON.hdl != INVALID_HANDLE_VALUE) {
2238 	    CON.buffered = buffered;
2239 	    get_SBI();
2240 	    CON.save_SBI = CON.SBI;
2241 	    if (!buffered) {
2242 		save_original_screen();
2243 		set_scrollback(FALSE, &CON.SBI);
2244 	    }
2245 	    GetConsoleCursorInfo(CON.hdl, &CON.save_CI);
2246 	    T(("... initial cursor is %svisible, %d%%",
2247 	       (CON.save_CI.bVisible ? "" : "not-"),
2248 	       (int) CON.save_CI.dwSize));
2249 	}
2250 
2251 	console_initialized = TRUE;
2252     }
2253     return (CON.hdl != INVALID_HANDLE_VALUE);
2254 }
2255 
2256 static bool
okConsoleHandle(TERMINAL_CONTROL_BLOCK * TCB)2257 okConsoleHandle(TERMINAL_CONTROL_BLOCK * TCB)
2258 {
2259     return ((TCB != 0) &&
2260 	    (TCB->magic == WINMAGIC) &&
2261 	    InitConsole());
2262 }
2263 
2264 /*
2265  * While a constructor would ensure that this module is initialized, that will
2266  * interfere with applications that may combine this with GUI interfaces.
2267  */
2268 #if 0
2269 static
2270 __attribute__((constructor))
2271      void _enter_console(void)
2272 {
2273     (void) InitConsole();
2274 }
2275 #endif
2276