1 /****************************************************************************
2 * Copyright 2018-2020,2021 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.67 2021/09/04 10:54:35 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 is 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 its 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 is 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