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