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