blob: 2425b3a44ce6f21eb2261d2a73a2cc4bf64b60ee [file] [log] [blame]
Steve Kondikae271bc2015-11-15 02:50:53 +01001/****************************************************************************
micky3879b9f5e72025-07-08 18:04:53 -04002 * Copyright 2018-2021,2023 Thomas E. Dickey *
3 * Copyright 2008-2016,2017 Free Software Foundation, Inc. *
Steve Kondikae271bc2015-11-15 02:50:53 +01004 * *
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
micky3879b9f5e72025-07-08 18:04:53 -040044#ifdef _WIN32
Steve Kondikae271bc2015-11-15 02:50:53 +010045#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
micky3879b9f5e72025-07-08 18:04:53 -040056#define CUR TerminalType(my_term).
Steve Kondikae271bc2015-11-15 02:50:53 +010057
micky3879b9f5e72025-07-08 18:04:53 -040058#define CONTROL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
Steve Kondikae271bc2015-11-15 02:50:53 +010059
micky3879b9f5e72025-07-08 18:04:53 -040060MODULE_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))
Steve Kondikae271bc2015-11-15 02:50:53 +010063
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
70static bool InitConsole(void);
71static 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
micky3879b9f5e72025-07-08 18:04:53 -040087/* *INDENT-OFF* */
Steve Kondikae271bc2015-11-15 02:50:53 +010088static const LONG keylist[] =
89{
micky3879b9f5e72025-07-08 18:04:53 -040090 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),
Steve Kondikae271bc2015-11-15 02:50:53 +010098 GenMap(VK_DELETE, KEY_DC),
99 GenMap(VK_INSERT, KEY_IC)
100};
101static const LONG ansi_keys[] =
102{
micky3879b9f5e72025-07-08 18:04:53 -0400103 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'),
Steve Kondikae271bc2015-11-15 02:50:53 +0100111 GenMap(VK_DELETE, 'S'),
112 GenMap(VK_INSERT, 'R')
113};
micky3879b9f5e72025-07-08 18:04:53 -0400114/* *INDENT-ON* */
Steve Kondikae271bc2015-11-15 02:50:53 +0100115#define N_INI ((int)array_length(keylist))
116#define FKEYS 24
117#define MAPSIZE (FKEYS + N_INI)
118#define NUMPAIRS 64
119
micky3879b9f5e72025-07-08 18:04:53 -0400120/* A process can only have a single console, so it is safe
Steve Kondikae271bc2015-11-15 02:50:53 +0100121 to maintain all the information about it in a single
122 static structure.
123 */
124static struct {
125 BOOL initialized;
126 BOOL buffered;
127 BOOL window_only;
128 BOOL progMode;
Steve Kondikae271bc2015-11-15 02:50:53 +0100129 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
148static BOOL console_initialized = FALSE;
149
150static WORD
151MapColor(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
170static WORD
171MapAttr(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 */
202static void
203dump_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 */
261static BOOL
262con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
263{
264 int actual = 0;
micky3879b9f5e72025-07-08 18:04:53 -0400265 CHAR_INFO *ci = TypeAlloca(CHAR_INFO, limit);
Steve Kondikae271bc2015-11-15 02:50:53 +0100266 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
311static BOOL
312con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
313{
micky3879b9f5e72025-07-08 18:04:53 -0400314 CHAR_INFO *ci = TypeAlloca(CHAR_INFO, n);
Steve Kondikae271bc2015-11-15 02:50:53 +0100315 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 */
357static int
358find_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 */
391static int
392find_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
430static void
431selectActiveHandle(void)
432{
433 if (CON.lastOut != CON.hdl) {
434 CON.lastOut = CON.hdl;
435 SetConsoleActiveScreenBuffer(CON.lastOut);
436 }
437}
438
439static bool
440restore_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
471static const char *
472wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
473{
474 (void) TCB;
475 return "win32console";
476}
477
478static int
479wcon_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);
micky3879b9f5e72025-07-08 18:04:53 -0400491 nonempty = Min(Height, NewScreen(sp)->_maxy + 1);
Steve Kondikae271bc2015-11-15 02:50:53 +0100492
493 T(("... %dx%d clear cur:%d new:%d",
494 Height, Width,
495 CurScreen(sp)->_clear,
496 NewScreen(sp)->_clear));
497
micky3879b9f5e72025-07-08 18:04:53 -0400498 if (SP_PARM->_endwin == ewSuspend) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100499
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
micky3879b9f5e72025-07-08 18:04:53 -0400507 SP_PARM->_endwin = ewRunning;
Steve Kondikae271bc2015-11-15 02:50:53 +0100508 }
509
510 if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
511 int x;
512#if USE_WIDEC_SUPPORT
micky3879b9f5e72025-07-08 18:04:53 -0400513 cchar_t *empty = TypeAlloca(cchar_t, Width);
Steve Kondikae271bc2015-11-15 02:50:53 +0100514 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
micky3879b9f5e72025-07-08 18:04:53 -0400522 chtype *empty = TypeAlloca(chtype, Width);
Steve Kondikae271bc2015-11-15 02:50:53 +0100523
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
micky3879b9f5e72025-07-08 18:04:53 -0400615#ifdef __MING32__
616#define SysISATTY(fd) _isatty(fd)
617#else
618#define SysISATTY(fd) isatty(fd)
619#endif
620
Steve Kondikae271bc2015-11-15 02:50:53 +0100621static bool
622wcon_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;
micky3879b9f5e72025-07-08 18:04:53 -0400652 } else if (SysISATTY(TCB->term.Filedes)) {
653 code = TRUE;
Steve Kondikae271bc2015-11-15 02:50:53 +0100654 }
655
656 /*
657 * This is intentional, to avoid unnecessary breakage of applications
658 * using <term.h> symbols.
659 */
micky3879b9f5e72025-07-08 18:04:53 -0400660 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
Steve Kondikae271bc2015-11-15 02:50:53 +0100665 }
666
667 if (!code) {
668 if (_nc_mingw_isconsole(0))
669 CON.isTermInfoConsole = TRUE;
670 }
671 returnBool(code);
672}
673
674static int
675wcon_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
micky3879b9f5e72025-07-08 18:04:53 -0400686 CHAR_INFO *this_screen = TypeAlloca(CHAR_INFO, max_cells);
687 CHAR_INFO *that_screen = TypeAlloca(CHAR_INFO, max_cells);
Steve Kondikae271bc2015-11-15 02:50:53 +0100688 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
micky3879b9f5e72025-07-08 18:04:53 -0400712 memcpy(that_screen,
713 this_screen,
714 sizeof(CHAR_INFO) * (size_t) max_cells);
Steve Kondikae271bc2015-11-15 02:50:53 +0100715
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
732static int
733wcon_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
745static int
746wcon_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
759static bool
760get_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
793static void
794wcon_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
807static bool
808wcon_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
821static bool
822wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
823{
824 int result = FALSE;
825 SCREEN *sp;
826
827 AssertTCB();
828 SetSP();
829
830 return result;
831}
832
833static int
834wcon_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
857static int
858wcon_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
866static int
867wcon_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 */
939static void
940set_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
1011static int
1012wcon_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
1074static void
1075wcon_screen_init(SCREEN *sp GCC_UNUSED)
1076{
1077}
1078
1079static void
1080wcon_wrap(SCREEN *sp GCC_UNUSED)
1081{
1082}
1083
1084static int
1085rkeycompare(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
1093static int
1094keycompare(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
1102static int
1103MapKey(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
1125static int
1126AnsiKey(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
1148static void
1149wcon_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
1160static bool
1161read_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 */
1210static bool
1211save_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
1240static void
1241wcon_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
1272static void
1273wcon_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
1290static void
1291wcon_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
1303static void
1304wcon_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
1317static void
1318wcon_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
1329static int
micky3879b9f5e72025-07-08 18:04:53 -04001330wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB,
1331 int delay
1332 EVENTLIST_2nd(_nc_eventlist * evl))
Steve Kondikae271bc2015-11-15 02:50:53 +01001333{
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
1354static int
1355wcon_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
1370static void
1371wcon_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
1381static void
1382wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1383 int OnFlag GCC_UNUSED)
1384{
1385 SCREEN *sp;
1386
1387 AssertTCB();
1388 SetSP();
1389}
1390
1391static chtype
1392wcon_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
1399static void
1400wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1401{
1402 SCREEN *sp;
1403
1404 AssertTCB();
1405 SetSP();
1406}
1407
1408static void
1409wcon_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
1456static ULONGLONG
1457tdiff(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
1472static int
1473Adjust(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
micky3879b9f5e72025-07-08 18:04:53 -04001489static mmask_t
Steve Kondikae271bc2015-11-15 02:50:53 +01001490decode_mouse(SCREEN *sp, int mask)
1491{
micky3879b9f5e72025-07-08 18:04:53 -04001492 mmask_t result = 0;
Steve Kondikae271bc2015-11-15 02:50:53 +01001493
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
1526static int
1527console_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
micky3879b9f5e72025-07-08 18:04:53 -04001544#ifdef NCURSES_WGETCH_EVENTS
1545 (void) evl; /* TODO: implement wgetch-events */
1546#endif
1547
Steve Kondikae271bc2015-11-15 02:50:53 +01001548#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
1638static int
1639wcon_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,
micky3879b9f5e72025-07-08 18:04:53 -04001655 timeleft EVENTLIST_2nd(evl));
Steve Kondikae271bc2015-11-15 02:50:53 +01001656 }
1657 return code;
1658}
1659
1660static bool
1661handle_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
micky3879b9f5e72025-07-08 18:04:53 -04001681 work.bstate |= decode_mouse(sp, sp->_drv_mouse_new_buttons);
Steve Kondikae271bc2015-11-15 02:50:53 +01001682
1683 } else {
1684
1685 /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
micky3879b9f5e72025-07-08 18:04:53 -04001686 work.bstate |= (decode_mouse(sp,
1687 sp->_drv_mouse_old_buttons)
1688 >> 1);
Steve Kondikae271bc2015-11-15 02:50:53 +01001689
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
1703static int
1704wcon_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
1720static int
1721wcon_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
1728static int
1729wcon_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
1751static bool
1752wcon_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
1774static int
1775wcon_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
1792static int
1793wcon_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
1828NCURSES_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
1870static HANDLE
1871get_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
micky3879b9f5e72025-07-08 18:04:53 -04001880 application. mintty doesn't use Windows Console for its screen
Steve Kondikae271bc2015-11-15 02:50:53 +01001881 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 */
1886static int
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*/
1947static BOOL
1948IsConsoleHandle(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 */
1965int
1966_nc_mingw_isatty(int fd)
1967{
1968 int result = 0;
1969
Steve Kondikae271bc2015-11-15 02:50:53 +01001970 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
micky3879b9f5e72025-07-08 18:04:53 -04001982 Console. It is the responsibility of the console to deal
Steve Kondikae271bc2015-11-15 02:50:53 +01001983 with the terminal escape sequences that are sent by
1984 terminfo.
1985 */
1986int
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
2013int
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
2052int
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
2064int
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
2080int
2081_nc_mingw_testmouse(
2082 SCREEN *sp,
2083 HANDLE fd,
micky3879b9f5e72025-07-08 18:04:53 -04002084 int delay
2085 EVENTLIST_2nd(_nc_eventlist * evl))
Steve Kondikae271bc2015-11-15 02:50:53 +01002086{
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
2104int
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;
micky3879b9f5e72025-07-08 18:04:53 -04002127 rc = rc + (int) nRead;
Steve Kondikae271bc2015-11-15 02:50:53 +01002128 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 /*
micky3879b9f5e72025-07-08 18:04:53 -04002134 * 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.
Steve Kondikae271bc2015-11-15 02:50:53 +01002137 */
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 }
micky3879b9f5e72025-07-08 18:04:53 -04002153 } else if (vk == VK_BACK) {
2154 if (!(inp_rec.Event.KeyEvent.dwControlKeyState
2155 & (SHIFT_PRESSED | CONTROL_PRESSED))) {
2156 *buf = KEY_BACKSPACE;
2157 }
Steve Kondikae271bc2015-11-15 02:50:53 +01002158 }
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
2173static bool
2174InitConsole(void)
2175{
micky3879b9f5e72025-07-08 18:04:53 -04002176 /* initialize once, or not at all */
Steve Kondikae271bc2015-11-15 02:50:53 +01002177 if (!console_initialized) {
2178 int i;
2179 DWORD num_buttons;
2180 WORD a;
2181 BOOL buffered = TRUE;
2182 BOOL b;
2183
2184 START_TRACE();
Steve Kondikae271bc2015-11-15 02:50:53 +01002185
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
Steve Kondikae271bc2015-11-15 02:50:53 +01002223 b = AllocConsole();
2224
2225 if (!b)
2226 b = AttachConsole(ATTACH_PARENT_PROCESS);
2227
micky3879b9f5e72025-07-08 18:04:53 -04002228 CON.inp = GetDirectHandle("CONIN$", FILE_SHARE_READ);
2229 CON.out = GetDirectHandle("CONOUT$", FILE_SHARE_WRITE);
2230
Steve Kondikae271bc2015-11-15 02:50:53 +01002231 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,
micky3879b9f5e72025-07-08 18:04:53 -04002238 FILE_SHARE_READ | FILE_SHARE_WRITE,
Steve Kondikae271bc2015-11-15 02:50:53 +01002239 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
2263static bool
2264okConsoleHandle(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
2276static
2277__attribute__((constructor))
2278 void _enter_console(void)
2279{
2280 (void) InitConsole();
2281}
2282#endif