libncurses: updated to 6.0
[tomato.git] / release / src / router / libncurses / ncurses / win32con / win_driver.c
blob78ccfeda1b8289f472ccbf72084121a6919d3e8e
1 /****************************************************************************
2 * Copyright (c) 1998-2014,2015 Free Software Foundation, Inc. *
3 * *
4 * Permission is hereby granted, free of charge, to any person obtaining a *
5 * copy of this software and associated documentation files (the *
6 * "Software"), to deal in the Software without restriction, including *
7 * without limitation the rights to use, copy, modify, merge, publish, *
8 * distribute, distribute with modifications, sublicense, and/or sell *
9 * copies of the Software, and to permit persons to whom the Software is *
10 * furnished to do so, subject to the following conditions: *
11 * *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
14 * *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
22 * *
23 * Except as contained in this notice, the name(s) of the above copyright *
24 * holders shall not be used in advertising or otherwise to promote the *
25 * sale, use or other dealings in this Software without prior written *
26 * authorization. *
27 ****************************************************************************/
29 /****************************************************************************
30 * Author: Juergen Pfeifer *
31 * and: Thomas E. Dickey *
32 ****************************************************************************/
35 * TODO - GetMousePos(POINT * result) from ntconio.c
36 * TODO - implement nodelay
37 * TODO - improve screen-repainting performance, using implied wraparound to reduce write's
38 * TODO - make it optional whether screen is restored or not when non-buffered
41 #include <curses.priv.h>
43 #ifdef __MINGW32__
44 #include <tchar.h>
45 #else
46 #include <windows.h>
47 #include <wchar.h>
48 #endif
50 #include <io.h>
52 #define PSAPI_VERSION 2
53 #include <psapi.h>
55 #define CUR my_term.type.
57 MODULE_ID("$Id: win_driver.c,v 1.55 2015/02/28 21:30:23 tom Exp $")
59 #ifndef __GNUC__
60 # error We need GCC to compile for MinGW
61 #endif
63 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
65 #define EXP_OPTIMIZE 0
67 #define array_length(a) (sizeof(a)/sizeof(a[0]))
69 static bool InitConsole(void);
70 static bool okConsoleHandle(TERMINAL_CONTROL_BLOCK *);
72 #define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC))
73 #define SetSP() assert(TCB->csp != 0); sp = TCB->csp; (void) sp
75 #define GenMap(vKey,key) MAKELONG(key, vKey)
77 #define AdjustY() (CON.buffered ? 0 : (int) CON.SBI.srWindow.Top)
79 #if USE_WIDEC_SUPPORT
80 #define write_screen WriteConsoleOutputW
81 #define read_screen ReadConsoleOutputW
82 #else
83 #define write_screen WriteConsoleOutput
84 #define read_screen ReadConsoleOutput
85 #endif
87 static const LONG keylist[] =
89 GenMap(VK_PRIOR, KEY_PPAGE),
90 GenMap(VK_NEXT, KEY_NPAGE),
91 GenMap(VK_END, KEY_END),
92 GenMap(VK_HOME, KEY_HOME),
93 GenMap(VK_LEFT, KEY_LEFT),
94 GenMap(VK_UP, KEY_UP),
95 GenMap(VK_RIGHT, KEY_RIGHT),
96 GenMap(VK_DOWN, KEY_DOWN),
97 GenMap(VK_DELETE, KEY_DC),
98 GenMap(VK_INSERT, KEY_IC)
100 static const LONG ansi_keys[] =
102 GenMap(VK_PRIOR, 'I'),
103 GenMap(VK_NEXT, 'Q'),
104 GenMap(VK_END, 'O'),
105 GenMap(VK_HOME, 'H'),
106 GenMap(VK_LEFT, 'K'),
107 GenMap(VK_UP, 'H'),
108 GenMap(VK_RIGHT, 'M'),
109 GenMap(VK_DOWN, 'P'),
110 GenMap(VK_DELETE, 'S'),
111 GenMap(VK_INSERT, 'R')
113 #define N_INI ((int)array_length(keylist))
114 #define FKEYS 24
115 #define MAPSIZE (FKEYS + N_INI)
116 #define NUMPAIRS 64
118 /* A process can only have a single console, so it's safe
119 to maintain all the information about it in a single
120 static structure.
122 static struct {
123 BOOL initialized;
124 BOOL buffered;
125 BOOL window_only;
126 BOOL progMode;
127 BOOL isMinTTY;
128 BOOL isTermInfoConsole;
129 HANDLE out;
130 HANDLE inp;
131 HANDLE hdl;
132 HANDLE lastOut;
133 int numButtons;
134 DWORD ansi_map[MAPSIZE];
135 DWORD map[MAPSIZE];
136 DWORD rmap[MAPSIZE];
137 WORD pairs[NUMPAIRS];
138 COORD origin;
139 CHAR_INFO *save_screen;
140 COORD save_size;
141 SMALL_RECT save_region;
142 CONSOLE_SCREEN_BUFFER_INFO SBI;
143 CONSOLE_SCREEN_BUFFER_INFO save_SBI;
144 CONSOLE_CURSOR_INFO save_CI;
145 } CON;
147 static BOOL console_initialized = FALSE;
149 static WORD
150 MapColor(bool fore, int color)
152 static const int _cmap[] =
153 {0, 4, 2, 6, 1, 5, 3, 7};
154 int a;
155 if (color < 0 || color > 7)
156 a = fore ? 7 : 0;
157 else
158 a = _cmap[color];
159 if (!fore)
160 a = a << 4;
161 return (WORD) a;
164 #define RevAttr(attr) \
165 (WORD) (((attr) & 0xff00) | \
166 ((((attr) & 0x07) << 4) | \
167 (((attr) & 0x70) >> 4)))
169 static WORD
170 MapAttr(WORD res, attr_t ch)
172 if (ch & A_COLOR) {
173 int p;
175 p = PairNumber(ch);
176 if (p > 0 && p < NUMPAIRS) {
177 WORD a;
178 a = CON.pairs[p];
179 res = (WORD) ((res & 0xff00) | a);
183 if (ch & A_REVERSE) {
184 res = RevAttr(res);
187 if (ch & A_STANDOUT) {
188 res = RevAttr(res) | BACKGROUND_INTENSITY;
191 if (ch & A_BOLD)
192 res |= FOREGROUND_INTENSITY;
194 if (ch & A_DIM)
195 res |= BACKGROUND_INTENSITY;
197 return res;
200 #if 0 /* def TRACE */
201 static void
202 dump_screen(const char *fn, int ln)
204 int max_cells = (CON.SBI.dwSize.Y * (1 + CON.SBI.dwSize.X)) + 1;
205 char output[max_cells];
206 CHAR_INFO save_screen[max_cells];
207 COORD save_size;
208 SMALL_RECT save_region;
209 COORD bufferCoord;
211 T(("dump_screen %s@%d", fn, ln));
213 save_region.Top = CON.SBI.srWindow.Top;
214 save_region.Left = CON.SBI.srWindow.Left;
215 save_region.Bottom = CON.SBI.srWindow.Bottom;
216 save_region.Right = CON.SBI.srWindow.Right;
218 save_size.X = (SHORT) (save_region.Right - save_region.Left + 1);
219 save_size.Y = (SHORT) (save_region.Bottom - save_region.Top + 1);
221 bufferCoord.X = bufferCoord.Y = 0;
223 if (read_screen(CON.hdl,
224 save_screen,
225 save_size,
226 bufferCoord,
227 &save_region)) {
228 int i, j;
229 int ij = 0;
230 int k = 0;
232 for (i = save_region.Top; i <= save_region.Bottom; ++i) {
233 for (j = save_region.Left; j <= save_region.Right; ++j) {
234 output[k++] = save_screen[ij++].Char.AsciiChar;
236 output[k++] = '\n';
238 output[k] = 0;
240 T(("DUMP: %d,%d - %d,%d",
241 save_region.Top,
242 save_region.Left,
243 save_region.Bottom,
244 save_region.Right));
245 T(("%s", output));
249 #else
250 #define dump_screen(fn,ln) /* nothing */
251 #endif
253 #if USE_WIDEC_SUPPORT
255 * TODO: support surrogate pairs
256 * TODO: support combining characters
257 * TODO: support acsc
258 * TODO: _nc_wacs should be part of sp.
260 static BOOL
261 con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
263 int actual = 0;
264 CHAR_INFO ci[limit];
265 COORD loc, siz;
266 SMALL_RECT rec;
267 int i;
268 cchar_t ch;
269 SCREEN *sp;
271 AssertTCB();
272 SetSP();
274 for (i = actual = 0; i < limit; i++) {
275 ch = str[i];
276 if (isWidecExt(ch))
277 continue;
278 ci[actual].Char.UnicodeChar = CharOf(ch);
279 ci[actual].Attributes = MapAttr(CON.SBI.wAttributes,
280 AttrOf(ch));
281 if (AttrOf(ch) & A_ALTCHARSET) {
282 if (_nc_wacs) {
283 int which = CharOf(ch);
284 if (which > 0
285 && which < ACS_LEN
286 && CharOf(_nc_wacs[which]) != 0) {
287 ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
288 } else {
289 ci[actual].Char.UnicodeChar = ' ';
293 ++actual;
296 loc.X = (SHORT) 0;
297 loc.Y = (SHORT) 0;
298 siz.X = (SHORT) actual;
299 siz.Y = 1;
301 rec.Left = (SHORT) x;
302 rec.Top = (SHORT) (y + AdjustY());
303 rec.Right = (SHORT) (x + limit - 1);
304 rec.Bottom = rec.Top;
306 return write_screen(CON.hdl, ci, siz, loc, &rec);
308 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
309 #else
310 static BOOL
311 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
313 CHAR_INFO ci[n];
314 COORD loc, siz;
315 SMALL_RECT rec;
316 int i;
317 chtype ch;
318 SCREEN *sp;
320 AssertTCB();
321 SetSP();
323 for (i = 0; i < n; i++) {
324 ch = str[i];
325 ci[i].Char.AsciiChar = ChCharOf(ch);
326 ci[i].Attributes = MapAttr(CON.SBI.wAttributes,
327 ChAttrOf(ch));
328 if (ChAttrOf(ch) & A_ALTCHARSET) {
329 if (sp->_acs_map)
330 ci[i].Char.AsciiChar =
331 ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
335 loc.X = (short) 0;
336 loc.Y = (short) 0;
337 siz.X = (short) n;
338 siz.Y = 1;
340 rec.Left = (short) x;
341 rec.Top = (short) y;
342 rec.Right = (short) (x + n - 1);
343 rec.Bottom = rec.Top;
345 return write_screen(CON.hdl, ci, siz, loc, &rec);
347 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
348 #endif
350 #if EXP_OPTIMIZE
352 * Comparing new/current screens, determine the last column-index for a change
353 * beginning on the given row,col position. Unlike a serial terminal, there is
354 * no cost for "moving" the "cursor" on the line as we update it.
356 static int
357 find_end_of_change(SCREEN *sp, int row, int col)
359 int result = col;
360 struct ldat *curdat = CurScreen(sp)->_line + row;
361 struct ldat *newdat = NewScreen(sp)->_line + row;
363 while (col <= newdat->lastchar) {
364 #if USE_WIDEC_SUPPORT
365 if (isWidecExt(curdat->text[col]) || isWidecExt(newdat->text[col])) {
366 result = col;
367 } else if (memcmp(&curdat->text[col],
368 &newdat->text[col],
369 sizeof(curdat->text[0]))) {
370 result = col;
371 } else {
372 break;
374 #else
375 if (curdat->text[col] != newdat->text[col]) {
376 result = col;
377 } else {
378 break;
380 #endif
381 ++col;
383 return result;
387 * Given a row,col position at the end of a change-chunk, look for the
388 * beginning of the next change-chunk.
390 static int
391 find_next_change(SCREEN *sp, int row, int col)
393 struct ldat *curdat = CurScreen(sp)->_line + row;
394 struct ldat *newdat = NewScreen(sp)->_line + row;
395 int result = newdat->lastchar + 1;
397 while (++col <= newdat->lastchar) {
398 #if USE_WIDEC_SUPPORT
399 if (isWidecExt(curdat->text[col]) != isWidecExt(newdat->text[col])) {
400 result = col;
401 break;
402 } else if (memcmp(&curdat->text[col],
403 &newdat->text[col],
404 sizeof(curdat->text[0]))) {
405 result = col;
406 break;
408 #else
409 if (curdat->text[col] != newdat->text[col]) {
410 result = col;
411 break;
413 #endif
415 return result;
418 #define EndChange(first) \
419 find_end_of_change(sp, y, first)
420 #define NextChange(last) \
421 find_next_change(sp, y, last)
423 #endif /* EXP_OPTIMIZE */
425 #define MARK_NOCHANGE(win,row) \
426 win->_line[row].firstchar = _NOCHANGE; \
427 win->_line[row].lastchar = _NOCHANGE
429 static void
430 selectActiveHandle(void)
432 if (CON.lastOut != CON.hdl) {
433 CON.lastOut = CON.hdl;
434 SetConsoleActiveScreenBuffer(CON.lastOut);
438 static bool
439 restore_original_screen(void)
441 COORD bufferCoord;
442 bool result = FALSE;
443 SMALL_RECT save_region = CON.save_region;
445 T(("... restoring %s", CON.window_only ? "window" : "entire buffer"));
447 bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0);
448 bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0);
450 if (write_screen(CON.hdl,
451 CON.save_screen,
452 CON.save_size,
453 bufferCoord,
454 &save_region)) {
455 result = TRUE;
456 mvcur(-1, -1, LINES - 2, 0);
457 T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)",
458 CON.save_size.Y,
459 CON.save_size.X,
460 save_region.Top,
461 save_region.Left,
462 save_region.Bottom,
463 save_region.Right));
464 } else {
465 T(("... restore original screen contents err"));
467 return result;
470 static const char *
471 wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
473 (void) TCB;
474 return "win32console";
477 static int
478 wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
480 int result = ERR;
481 int y, nonempty, n, x0, x1, Width, Height;
482 SCREEN *sp;
484 T((T_CALLED("win32con::wcon_doupdate(%p)"), TCB));
485 if (okConsoleHandle(TCB)) {
486 SetSP();
488 Width = screen_columns(sp);
489 Height = screen_lines(sp);
490 nonempty = min(Height, NewScreen(sp)->_maxy + 1);
492 T(("... %dx%d clear cur:%d new:%d",
493 Height, Width,
494 CurScreen(sp)->_clear,
495 NewScreen(sp)->_clear));
497 if (SP_PARM->_endwin) {
499 T(("coming back from shell mode"));
500 NCURSES_SP_NAME(reset_prog_mode) (NCURSES_SP_ARG);
502 NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG);
503 NCURSES_SP_NAME(_nc_screen_resume) (NCURSES_SP_ARG);
504 SP_PARM->_mouse_resume(SP_PARM);
506 SP_PARM->_endwin = FALSE;
509 if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
510 int x;
511 #if USE_WIDEC_SUPPORT
512 cchar_t empty[Width];
513 wchar_t blank[2] =
515 L' ', L'\0'
518 for (x = 0; x < Width; x++)
519 setcchar(&empty[x], blank, 0, 0, 0);
520 #else
521 chtype empty[Width];
523 for (x = 0; x < Width; x++)
524 empty[x] = ' ';
525 #endif
527 for (y = 0; y < nonempty; y++) {
528 con_write(TCB, y, 0, empty, Width);
529 memcpy(empty,
530 CurScreen(sp)->_line[y].text,
531 (size_t) Width * sizeof(empty[0]));
533 CurScreen(sp)->_clear = FALSE;
534 NewScreen(sp)->_clear = FALSE;
535 touchwin(NewScreen(sp));
536 T(("... cleared %dx%d lines @%d of screen", nonempty, Width,
537 AdjustY()));
540 for (y = 0; y < nonempty; y++) {
541 x0 = NewScreen(sp)->_line[y].firstchar;
542 if (x0 != _NOCHANGE) {
543 #if EXP_OPTIMIZE
544 int x2;
545 int limit = NewScreen(sp)->_line[y].lastchar;
546 while ((x1 = EndChange(x0)) <= limit) {
547 while ((x2 = NextChange(x1)) <= limit && x2 <= (x1 + 2)) {
548 x1 = x2;
550 n = x1 - x0 + 1;
551 memcpy(&CurScreen(sp)->_line[y].text[x0],
552 &NewScreen(sp)->_line[y].text[x0],
553 n * sizeof(CurScreen(sp)->_line[y].text[x0]));
554 con_write(TCB,
557 &CurScreen(sp)->_line[y].text[x0], n);
558 x0 = NextChange(x1);
561 /* mark line changed successfully */
562 if (y <= NewScreen(sp)->_maxy) {
563 MARK_NOCHANGE(NewScreen(sp), y);
565 if (y <= CurScreen(sp)->_maxy) {
566 MARK_NOCHANGE(CurScreen(sp), y);
568 #else
569 x1 = NewScreen(sp)->_line[y].lastchar;
570 n = x1 - x0 + 1;
571 if (n > 0) {
572 memcpy(&CurScreen(sp)->_line[y].text[x0],
573 &NewScreen(sp)->_line[y].text[x0],
574 (size_t) n * sizeof(CurScreen(sp)->_line[y].text[x0]));
575 con_write(TCB,
578 &CurScreen(sp)->_line[y].text[x0], n);
580 /* mark line changed successfully */
581 if (y <= NewScreen(sp)->_maxy) {
582 MARK_NOCHANGE(NewScreen(sp), y);
584 if (y <= CurScreen(sp)->_maxy) {
585 MARK_NOCHANGE(CurScreen(sp), y);
588 #endif
592 /* put everything back in sync */
593 for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
594 MARK_NOCHANGE(NewScreen(sp), y);
596 for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
597 MARK_NOCHANGE(CurScreen(sp), y);
600 if (!NewScreen(sp)->_leaveok) {
601 CurScreen(sp)->_curx = NewScreen(sp)->_curx;
602 CurScreen(sp)->_cury = NewScreen(sp)->_cury;
604 TCB->drv->td_hwcur(TCB,
605 0, 0,
606 CurScreen(sp)->_cury, CurScreen(sp)->_curx);
608 selectActiveHandle();
609 result = OK;
611 returnCode(result);
614 static bool
615 wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
616 const char *tname,
617 int *errret GCC_UNUSED)
619 bool code = FALSE;
621 T((T_CALLED("win32con::wcon_CanHandle(%p)"), TCB));
623 assert((TCB != 0) && (tname != 0));
625 TCB->magic = WINMAGIC;
627 if (tname == 0 || *tname == 0)
628 code = TRUE;
629 else if (tname != 0 && *tname == '#') {
631 * Use "#" (a character which cannot begin a terminal's name) to
632 * select specific driver from the table.
634 * In principle, we could have more than one non-terminfo driver,
635 * e.g., "win32gui".
637 size_t n = strlen(tname + 1);
638 if (n != 0
639 && ((strncmp(tname + 1, "win32console", n) == 0)
640 || (strncmp(tname + 1, "win32con", n) == 0))) {
641 code = TRUE;
643 } else if (tname != 0 && stricmp(tname, "unknown") == 0) {
644 code = TRUE;
648 * This is intentional, to avoid unnecessary breakage of applications
649 * using <term.h> symbols.
651 if (code && (TCB->term.type.Booleans == 0)) {
652 _nc_init_termtype(&(TCB->term.type));
655 if (!code) {
656 if (_nc_mingw_isconsole(0))
657 CON.isTermInfoConsole = TRUE;
659 returnBool(code);
662 static int
663 wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
664 int beepFlag)
666 SCREEN *sp;
667 int res = ERR;
669 int high = (CON.SBI.srWindow.Bottom - CON.SBI.srWindow.Top + 1);
670 int wide = (CON.SBI.srWindow.Right - CON.SBI.srWindow.Left + 1);
671 int max_cells = (high * wide);
672 int i;
674 CHAR_INFO this_screen[max_cells];
675 CHAR_INFO that_screen[max_cells];
676 COORD this_size;
677 SMALL_RECT this_region;
678 COORD bufferCoord;
680 if (okConsoleHandle(TCB)) {
681 SetSP();
682 this_region.Top = CON.SBI.srWindow.Top;
683 this_region.Left = CON.SBI.srWindow.Left;
684 this_region.Bottom = CON.SBI.srWindow.Bottom;
685 this_region.Right = CON.SBI.srWindow.Right;
687 this_size.X = (SHORT) wide;
688 this_size.Y = (SHORT) high;
690 bufferCoord.X = this_region.Left;
691 bufferCoord.Y = this_region.Top;
693 if (!beepFlag &&
694 read_screen(CON.hdl,
695 this_screen,
696 this_size,
697 bufferCoord,
698 &this_region)) {
700 memcpy(that_screen, this_screen, sizeof(that_screen));
702 for (i = 0; i < max_cells; i++) {
703 that_screen[i].Attributes = RevAttr(that_screen[i].Attributes);
706 write_screen(CON.hdl, that_screen, this_size, bufferCoord, &this_region);
707 Sleep(200);
708 write_screen(CON.hdl, this_screen, this_size, bufferCoord, &this_region);
710 } else {
711 MessageBeep(MB_ICONWARNING); /* MB_OK might be better */
713 res = OK;
715 return res;
718 static int
719 wcon_print(TERMINAL_CONTROL_BLOCK * TCB,
720 char *data GCC_UNUSED,
721 int len GCC_UNUSED)
723 SCREEN *sp;
725 AssertTCB();
726 SetSP();
728 return ERR;
731 static int
732 wcon_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
733 int fg GCC_UNUSED,
734 int bg GCC_UNUSED)
736 SCREEN *sp;
737 int code = ERR;
739 AssertTCB();
740 SetSP();
742 return (code);
745 static bool
746 get_SBI(void)
748 bool rc = FALSE;
749 if (GetConsoleScreenBufferInfo(CON.hdl, &(CON.SBI))) {
750 T(("GetConsoleScreenBufferInfo"));
751 T(("... buffer(X:%d Y:%d)",
752 CON.SBI.dwSize.X,
753 CON.SBI.dwSize.Y));
754 T(("... window(X:%d Y:%d)",
755 CON.SBI.dwMaximumWindowSize.X,
756 CON.SBI.dwMaximumWindowSize.Y));
757 T(("... cursor(X:%d Y:%d)",
758 CON.SBI.dwCursorPosition.X,
759 CON.SBI.dwCursorPosition.Y));
760 T(("... display(Top:%d Bottom:%d Left:%d Right:%d)",
761 CON.SBI.srWindow.Top,
762 CON.SBI.srWindow.Bottom,
763 CON.SBI.srWindow.Left,
764 CON.SBI.srWindow.Right));
765 if (CON.buffered) {
766 CON.origin.X = 0;
767 CON.origin.Y = 0;
768 } else {
769 CON.origin.X = CON.SBI.srWindow.Left;
770 CON.origin.Y = CON.SBI.srWindow.Top;
772 rc = TRUE;
773 } else {
774 T(("GetConsoleScreenBufferInfo ERR"));
776 return rc;
779 static void
780 wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
781 int fore,
782 int color,
783 int (*outc) (SCREEN *, int) GCC_UNUSED)
785 if (okConsoleHandle(TCB)) {
786 WORD a = MapColor(fore, color);
787 a |= (WORD) ((CON.SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f));
788 SetConsoleTextAttribute(CON.hdl, a);
789 get_SBI();
793 static bool
794 wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB)
796 bool res = FALSE;
798 if (okConsoleHandle(TCB)) {
799 WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
800 SetConsoleTextAttribute(CON.hdl, a);
801 get_SBI();
802 res = TRUE;
804 return res;
807 static bool
808 wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
810 int result = FALSE;
811 SCREEN *sp;
813 AssertTCB();
814 SetSP();
816 return result;
819 static int
820 wcon_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
822 int result = ERR;
824 T((T_CALLED("win32con::wcon_size(%p)"), TCB));
826 if (okConsoleHandle(TCB) &&
827 Lines != NULL &&
828 Cols != NULL) {
829 if (CON.buffered) {
830 *Lines = (int) (CON.SBI.dwSize.Y);
831 *Cols = (int) (CON.SBI.dwSize.X);
832 } else {
833 *Lines = (int) (CON.SBI.srWindow.Bottom + 1 -
834 CON.SBI.srWindow.Top);
835 *Cols = (int) (CON.SBI.srWindow.Right + 1 -
836 CON.SBI.srWindow.Left);
838 result = OK;
840 returnCode(result);
843 static int
844 wcon_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
845 int l GCC_UNUSED,
846 int c GCC_UNUSED)
848 AssertTCB();
849 return ERR;
852 static int
853 wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
855 DWORD dwFlag = 0;
856 tcflag_t iflag;
857 tcflag_t lflag;
858 int result = ERR;
860 if (buf != NULL && okConsoleHandle(TCB)) {
862 if (setFlag) {
863 iflag = buf->c_iflag;
864 lflag = buf->c_lflag;
866 GetConsoleMode(CON.inp, &dwFlag);
868 if (lflag & ICANON)
869 dwFlag |= ENABLE_LINE_INPUT;
870 else
871 dwFlag &= (DWORD) (~ENABLE_LINE_INPUT);
873 if (lflag & ECHO)
874 dwFlag |= ENABLE_ECHO_INPUT;
875 else
876 dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT);
878 if (iflag & BRKINT)
879 dwFlag |= ENABLE_PROCESSED_INPUT;
880 else
881 dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT);
883 dwFlag |= ENABLE_MOUSE_INPUT;
885 buf->c_iflag = iflag;
886 buf->c_lflag = lflag;
887 SetConsoleMode(CON.inp, dwFlag);
888 TCB->term.Nttyb = *buf;
889 } else {
890 iflag = TCB->term.Nttyb.c_iflag;
891 lflag = TCB->term.Nttyb.c_lflag;
892 GetConsoleMode(CON.inp, &dwFlag);
894 if (dwFlag & ENABLE_LINE_INPUT)
895 lflag |= ICANON;
896 else
897 lflag &= (tcflag_t) (~ICANON);
899 if (dwFlag & ENABLE_ECHO_INPUT)
900 lflag |= ECHO;
901 else
902 lflag &= (tcflag_t) (~ECHO);
904 if (dwFlag & ENABLE_PROCESSED_INPUT)
905 iflag |= BRKINT;
906 else
907 iflag &= (tcflag_t) (~BRKINT);
909 TCB->term.Nttyb.c_iflag = iflag;
910 TCB->term.Nttyb.c_lflag = lflag;
912 *buf = TCB->term.Nttyb;
914 result = OK;
916 return result;
919 #define MIN_WIDE 80
920 #define MIN_HIGH 24
923 * In "normal" mode, reset the buffer- and window-sizes back to their original values.
925 static void
926 set_scrollback(bool normal, CONSOLE_SCREEN_BUFFER_INFO * info)
928 SMALL_RECT rect;
929 COORD coord;
930 bool changed = FALSE;
932 T((T_CALLED("win32con::set_scrollback(%s)"),
933 (normal
934 ? "normal"
935 : "application")));
937 T(("... SBI.srWindow %d,%d .. %d,%d",
938 info->srWindow.Top,
939 info->srWindow.Left,
940 info->srWindow.Bottom,
941 info->srWindow.Right));
942 T(("... SBI.dwSize %dx%d",
943 info->dwSize.Y,
944 info->dwSize.X));
946 if (normal) {
947 rect = info->srWindow;
948 coord = info->dwSize;
949 if (memcmp(info, &CON.SBI, sizeof(*info)) != 0) {
950 changed = TRUE;
951 CON.SBI = *info;
953 } else {
954 int high = info->srWindow.Bottom - info->srWindow.Top + 1;
955 int wide = info->srWindow.Right - info->srWindow.Left + 1;
957 if (high < MIN_HIGH) {
958 T(("... height %d < %d", high, MIN_HIGH));
959 high = MIN_HIGH;
960 changed = TRUE;
962 if (wide < MIN_WIDE) {
963 T(("... width %d < %d", wide, MIN_WIDE));
964 wide = MIN_WIDE;
965 changed = TRUE;
968 rect.Left =
969 rect.Top = 0;
970 rect.Right = (SHORT) (wide - 1);
971 rect.Bottom = (SHORT) (high - 1);
973 coord.X = (SHORT) wide;
974 coord.Y = (SHORT) high;
976 if (info->dwSize.Y != high ||
977 info->dwSize.X != wide ||
978 info->srWindow.Top != 0 ||
979 info->srWindow.Left != 0) {
980 changed = TRUE;
985 if (changed) {
986 T(("... coord %d,%d", coord.Y, coord.X));
987 T(("... rect %d,%d - %d,%d",
988 rect.Top, rect.Left,
989 rect.Bottom, rect.Right));
990 SetConsoleScreenBufferSize(CON.hdl, coord); /* dwSize */
991 SetConsoleWindowInfo(CON.hdl, TRUE, &rect); /* srWindow */
992 get_SBI();
994 returnVoid;
997 static int
998 wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
1000 SCREEN *sp;
1001 TERMINAL *_term = (TERMINAL *) TCB;
1002 int code = ERR;
1004 if (okConsoleHandle(TCB)) {
1005 sp = TCB->csp;
1007 T((T_CALLED("win32con::wcon_mode(%p, prog=%d, def=%d)"),
1008 TCB, progFlag, defFlag));
1010 CON.progMode = progFlag;
1011 CON.lastOut = progFlag ? CON.hdl : CON.out;
1012 SetConsoleActiveScreenBuffer(CON.lastOut);
1014 if (progFlag) /* prog mode */ {
1015 if (defFlag) {
1016 if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
1017 _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS);
1018 code = OK;
1020 } else {
1021 /* reset_prog_mode */
1022 if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
1023 if (sp) {
1024 if (sp->_keypad_on)
1025 _nc_keypad(sp, TRUE);
1027 if (!CON.buffered) {
1028 set_scrollback(FALSE, &CON.SBI);
1030 code = OK;
1033 T(("... buffered:%d, clear:%d", CON.buffered, CurScreen(sp)->_clear));
1034 } else { /* shell mode */
1035 if (defFlag) {
1036 /* def_shell_mode */
1037 if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
1038 code = OK;
1040 } else {
1041 /* reset_shell_mode */
1042 if (sp) {
1043 _nc_keypad(sp, FALSE);
1044 NCURSES_SP_NAME(_nc_flush) (sp);
1046 code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb));
1047 if (!CON.buffered) {
1048 set_scrollback(TRUE, &CON.save_SBI);
1049 if (!restore_original_screen())
1050 code = ERR;
1052 SetConsoleCursorInfo(CON.hdl, &CON.save_CI);
1057 returnCode(code);
1060 static void
1061 wcon_screen_init(SCREEN *sp GCC_UNUSED)
1065 static void
1066 wcon_wrap(SCREEN *sp GCC_UNUSED)
1070 static int
1071 rkeycompare(const void *el1, const void *el2)
1073 WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
1074 WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
1076 return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
1079 static int
1080 keycompare(const void *el1, const void *el2)
1082 WORD key1 = HIWORD((*((const LONG *) el1)));
1083 WORD key2 = HIWORD((*((const LONG *) el2)));
1085 return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
1088 static int
1089 MapKey(WORD vKey)
1091 WORD nKey = 0;
1092 void *res;
1093 LONG key = GenMap(vKey, 0);
1094 int code = -1;
1096 res = bsearch(&key,
1097 CON.map,
1098 (size_t) (N_INI + FKEYS),
1099 sizeof(keylist[0]),
1100 keycompare);
1101 if (res) {
1102 key = *((LONG *) res);
1103 nKey = LOWORD(key);
1104 code = (int) (nKey & 0x7fff);
1105 if (nKey & 0x8000)
1106 code = -code;
1108 return code;
1111 static int
1112 AnsiKey(WORD vKey)
1114 WORD nKey = 0;
1115 void *res;
1116 LONG key = GenMap(vKey, 0);
1117 int code = -1;
1119 res = bsearch(&key,
1120 CON.ansi_map,
1121 (size_t) (N_INI + FKEYS),
1122 sizeof(keylist[0]),
1123 keycompare);
1124 if (res) {
1125 key = *((LONG *) res);
1126 nKey = LOWORD(key);
1127 code = (int) (nKey & 0x7fff);
1128 if (nKey & 0x8000)
1129 code = -code;
1131 return code;
1134 static void
1135 wcon_release(TERMINAL_CONTROL_BLOCK * TCB)
1137 T((T_CALLED("win32con::wcon_release(%p)"), TCB));
1139 AssertTCB();
1140 if (TCB->prop)
1141 free(TCB->prop);
1143 returnVoid;
1146 static bool
1147 read_screen_data(void)
1149 bool result = FALSE;
1150 COORD bufferCoord;
1151 size_t want;
1153 CON.save_size.X = (SHORT) (CON.save_region.Right
1154 - CON.save_region.Left + 1);
1155 CON.save_size.Y = (SHORT) (CON.save_region.Bottom
1156 - CON.save_region.Top + 1);
1158 want = (size_t) (CON.save_size.X * CON.save_size.Y);
1160 if ((CON.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
1161 bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0);
1162 bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0);
1164 T(("... reading console %s %dx%d into %d,%d - %d,%d at %d,%d",
1165 CON.window_only ? "window" : "buffer",
1166 CON.save_size.Y, CON.save_size.X,
1167 CON.save_region.Top,
1168 CON.save_region.Left,
1169 CON.save_region.Bottom,
1170 CON.save_region.Right,
1171 bufferCoord.Y,
1172 bufferCoord.X));
1174 if (read_screen(CON.hdl,
1175 CON.save_screen,
1176 CON.save_size,
1177 bufferCoord,
1178 &CON.save_region)) {
1179 result = TRUE;
1180 } else {
1181 T((" error %#lx", (unsigned long) GetLastError()));
1182 FreeAndNull(CON.save_screen);
1186 return result;
1190 * Attempt to save the screen contents. PDCurses does this if
1191 * PDC_RESTORE_SCREEN is set, giving the same visual appearance on
1192 * restoration as if the library had allocated a console buffer. MSDN
1193 * says that the data which can be read is limited to 64Kb (and may be
1194 * less).
1196 static bool
1197 save_original_screen(void)
1199 bool result = FALSE;
1201 CON.save_region.Top = 0;
1202 CON.save_region.Left = 0;
1203 CON.save_region.Bottom = (SHORT) (CON.SBI.dwSize.Y - 1);
1204 CON.save_region.Right = (SHORT) (CON.SBI.dwSize.X - 1);
1206 if (read_screen_data()) {
1207 result = TRUE;
1208 } else {
1210 CON.save_region.Top = CON.SBI.srWindow.Top;
1211 CON.save_region.Left = CON.SBI.srWindow.Left;
1212 CON.save_region.Bottom = CON.SBI.srWindow.Bottom;
1213 CON.save_region.Right = CON.SBI.srWindow.Right;
1215 CON.window_only = TRUE;
1217 if (read_screen_data()) {
1218 result = TRUE;
1222 T(("... save original screen contents %s", result ? "ok" : "err"));
1223 return result;
1226 static void
1227 wcon_init(TERMINAL_CONTROL_BLOCK * TCB)
1229 T((T_CALLED("win32con::wcon_init(%p)"), TCB));
1231 AssertTCB();
1233 if (TCB) {
1234 if (!InitConsole()) {
1235 returnVoid;
1238 TCB->info.initcolor = TRUE;
1239 TCB->info.canchange = FALSE;
1240 TCB->info.hascolor = TRUE;
1241 TCB->info.caninit = TRUE;
1243 TCB->info.maxpairs = NUMPAIRS;
1244 TCB->info.maxcolors = 8;
1245 TCB->info.numlabels = 0;
1246 TCB->info.labelwidth = 0;
1247 TCB->info.labelheight = 0;
1248 TCB->info.nocolorvideo = 1;
1249 TCB->info.tabsize = 8;
1251 TCB->info.numbuttons = CON.numButtons;
1252 TCB->info.defaultPalette = _nc_cga_palette;
1255 returnVoid;
1258 static void
1259 wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB,
1260 int pair,
1261 int f,
1262 int b)
1264 SCREEN *sp;
1266 if (okConsoleHandle(TCB)) {
1267 SetSP();
1269 if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
1270 && (b >= 0) && (b < 8)) {
1271 CON.pairs[pair] = MapColor(true, f) | MapColor(false, b);
1276 static void
1277 wcon_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
1278 int color GCC_UNUSED,
1279 int r GCC_UNUSED,
1280 int g GCC_UNUSED,
1281 int b GCC_UNUSED)
1283 SCREEN *sp;
1285 AssertTCB();
1286 SetSP();
1289 static void
1290 wcon_do_color(TERMINAL_CONTROL_BLOCK * TCB,
1291 int old_pair GCC_UNUSED,
1292 int pair GCC_UNUSED,
1293 int reverse GCC_UNUSED,
1294 int (*outc) (SCREEN *, int) GCC_UNUSED
1297 SCREEN *sp;
1299 AssertTCB();
1300 SetSP();
1303 static void
1304 wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
1306 SCREEN *sp;
1308 if (okConsoleHandle(TCB)) {
1309 SetSP();
1311 sp->_mouse_type = M_TERM_DRIVER;
1315 static int
1316 wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB, int delay)
1318 int rc = 0;
1319 SCREEN *sp;
1321 if (okConsoleHandle(TCB)) {
1322 SetSP();
1324 if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1325 rc = TW_MOUSE;
1326 } else {
1327 rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
1328 TWAIT_MASK,
1329 delay,
1330 (int *) 0
1331 EVENTLIST_2nd(evl));
1335 return rc;
1338 static int
1339 wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
1340 int yold GCC_UNUSED, int xold GCC_UNUSED,
1341 int y, int x)
1343 int ret = ERR;
1344 if (okConsoleHandle(TCB)) {
1345 COORD loc;
1346 loc.X = (short) x;
1347 loc.Y = (short) (y + AdjustY());
1348 SetConsoleCursorPosition(CON.hdl, loc);
1349 ret = OK;
1351 return ret;
1354 static void
1355 wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
1356 int labnum GCC_UNUSED,
1357 char *text GCC_UNUSED)
1359 SCREEN *sp;
1361 AssertTCB();
1362 SetSP();
1365 static void
1366 wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1367 int OnFlag GCC_UNUSED)
1369 SCREEN *sp;
1371 AssertTCB();
1372 SetSP();
1375 static chtype
1376 wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1378 chtype res = A_NORMAL;
1379 res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1380 return res;
1383 static void
1384 wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1386 SCREEN *sp;
1388 AssertTCB();
1389 SetSP();
1392 static void
1393 wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1394 chtype *real_map GCC_UNUSED,
1395 chtype *fake_map GCC_UNUSED)
1397 #define DATA(a,b) { a, b }
1398 static struct {
1399 int acs_code;
1400 int use_code;
1401 } table[] = {
1402 DATA('a', 0xb1), /* ACS_CKBOARD */
1403 DATA('f', 0xf8), /* ACS_DEGREE */
1404 DATA('g', 0xf1), /* ACS_PLMINUS */
1405 DATA('j', 0xd9), /* ACS_LRCORNER */
1406 DATA('l', 0xda), /* ACS_ULCORNER */
1407 DATA('k', 0xbf), /* ACS_URCORNER */
1408 DATA('m', 0xc0), /* ACS_LLCORNER */
1409 DATA('n', 0xc5), /* ACS_PLUS */
1410 DATA('q', 0xc4), /* ACS_HLINE */
1411 DATA('t', 0xc3), /* ACS_LTEE */
1412 DATA('u', 0xb4), /* ACS_RTEE */
1413 DATA('v', 0xc1), /* ACS_BTEE */
1414 DATA('w', 0xc2), /* ACS_TTEE */
1415 DATA('x', 0xb3), /* ACS_VLINE */
1416 DATA('y', 0xf3), /* ACS_LEQUAL */
1417 DATA('z', 0xf2), /* ACS_GEQUAL */
1418 DATA('0', 0xdb), /* ACS_BLOCK */
1419 DATA('{', 0xe3), /* ACS_PI */
1420 DATA('}', 0x9c), /* ACS_STERLING */
1421 DATA(',', 0xae), /* ACS_LARROW */
1422 DATA('+', 0xaf), /* ACS_RARROW */
1423 DATA('~', 0xf9), /* ACS_BULLET */
1425 #undef DATA
1426 unsigned n;
1428 SCREEN *sp;
1429 if (okConsoleHandle(TCB)) {
1430 SetSP();
1432 for (n = 0; n < SIZEOF(table); ++n) {
1433 real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET;
1434 if (sp != 0)
1435 sp->_screen_acs_map[table[n].acs_code] = TRUE;
1440 static ULONGLONG
1441 tdiff(FILETIME fstart, FILETIME fend)
1443 ULARGE_INTEGER ustart;
1444 ULARGE_INTEGER uend;
1445 ULONGLONG diff;
1447 ustart.LowPart = fstart.dwLowDateTime;
1448 ustart.HighPart = fstart.dwHighDateTime;
1449 uend.LowPart = fend.dwLowDateTime;
1450 uend.HighPart = fend.dwHighDateTime;
1452 diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1453 return diff;
1456 static int
1457 Adjust(int milliseconds, int diff)
1459 if (milliseconds != INFINITY) {
1460 milliseconds -= diff;
1461 if (milliseconds < 0)
1462 milliseconds = 0;
1464 return milliseconds;
1467 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1468 FROM_LEFT_2ND_BUTTON_PRESSED | \
1469 FROM_LEFT_3RD_BUTTON_PRESSED | \
1470 FROM_LEFT_4TH_BUTTON_PRESSED | \
1471 RIGHTMOST_BUTTON_PRESSED)
1473 static int
1474 decode_mouse(SCREEN *sp, int mask)
1476 int result = 0;
1478 (void) sp;
1479 assert(sp && console_initialized);
1481 if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1482 result |= BUTTON1_PRESSED;
1483 if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1484 result |= BUTTON2_PRESSED;
1485 if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1486 result |= BUTTON3_PRESSED;
1487 if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1488 result |= BUTTON4_PRESSED;
1490 if (mask & RIGHTMOST_BUTTON_PRESSED) {
1491 switch (CON.numButtons) {
1492 case 1:
1493 result |= BUTTON1_PRESSED;
1494 break;
1495 case 2:
1496 result |= BUTTON2_PRESSED;
1497 break;
1498 case 3:
1499 result |= BUTTON3_PRESSED;
1500 break;
1501 case 4:
1502 result |= BUTTON4_PRESSED;
1503 break;
1507 return result;
1510 static int
1511 console_twait(
1512 SCREEN *sp,
1513 HANDLE fd,
1514 int mode,
1515 int milliseconds,
1516 int *timeleft
1517 EVENTLIST_2nd(_nc_eventlist * evl))
1519 INPUT_RECORD inp_rec;
1520 BOOL b;
1521 DWORD nRead = 0, rc = (DWORD) (-1);
1522 int code = 0;
1523 FILETIME fstart;
1524 FILETIME fend;
1525 int diff;
1526 bool isImmed = (milliseconds == 0);
1528 #define CONSUME() ReadConsoleInput(fd,&inp_rec,1,&nRead)
1530 assert(sp);
1532 TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1533 milliseconds, mode));
1535 if (milliseconds < 0)
1536 milliseconds = INFINITY;
1538 memset(&inp_rec, 0, sizeof(inp_rec));
1540 while (true) {
1541 GetSystemTimeAsFileTime(&fstart);
1542 rc = WaitForSingleObject(fd, (DWORD) milliseconds);
1543 GetSystemTimeAsFileTime(&fend);
1544 diff = (int) tdiff(fstart, fend);
1545 milliseconds = Adjust(milliseconds, diff);
1547 if (!isImmed && milliseconds <= 0)
1548 break;
1550 if (rc == WAIT_OBJECT_0) {
1551 if (mode) {
1552 b = GetNumberOfConsoleInputEvents(fd, &nRead);
1553 if (b && nRead > 0) {
1554 b = PeekConsoleInput(fd, &inp_rec, 1, &nRead);
1555 if (b && nRead > 0) {
1556 switch (inp_rec.EventType) {
1557 case KEY_EVENT:
1558 if (mode & TW_INPUT) {
1559 WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1560 char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1562 if (inp_rec.Event.KeyEvent.bKeyDown) {
1563 if (0 == ch) {
1564 int nKey = MapKey(vk);
1565 if (nKey < 0) {
1566 CONSUME();
1567 continue;
1570 code = TW_INPUT;
1571 goto end;
1572 } else {
1573 CONSUME();
1576 continue;
1577 case MOUSE_EVENT:
1578 if (decode_mouse(sp,
1579 (inp_rec.Event.MouseEvent.dwButtonState
1580 & BUTTON_MASK)) == 0) {
1581 CONSUME();
1582 } else if (mode & TW_MOUSE) {
1583 code = TW_MOUSE;
1584 goto end;
1586 continue;
1587 /* e.g., FOCUS_EVENT */
1588 default:
1589 CONSUME();
1590 selectActiveHandle();
1591 continue;
1596 continue;
1597 } else {
1598 if (rc != WAIT_TIMEOUT) {
1599 code = -1;
1600 break;
1601 } else {
1602 code = 0;
1603 break;
1607 end:
1609 TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1610 code, errno, milliseconds));
1612 if (timeleft)
1613 *timeleft = milliseconds;
1615 return code;
1618 static int
1619 wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,
1620 int mode,
1621 int milliseconds,
1622 int *timeleft
1623 EVENTLIST_2nd(_nc_eventlist * evl))
1625 SCREEN *sp;
1626 int code = 0;
1628 if (okConsoleHandle(TCB)) {
1629 SetSP();
1631 code = console_twait(sp,
1632 CON.inp,
1633 mode,
1634 milliseconds,
1635 timeleft EVENTLIST_2nd(_nc_eventlist * evl));
1637 return code;
1640 static bool
1641 handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer)
1643 MEVENT work;
1644 bool result = FALSE;
1646 assert(sp);
1648 sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1649 sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1652 * We're only interested if the button is pressed or released.
1653 * FIXME: implement continuous event-tracking.
1655 if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1657 memset(&work, 0, sizeof(work));
1659 if (sp->_drv_mouse_new_buttons) {
1661 work.bstate |= (mmask_t) decode_mouse(sp, sp->_drv_mouse_new_buttons);
1663 } else {
1665 /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1666 work.bstate |= (mmask_t) (decode_mouse(sp,
1667 sp->_drv_mouse_old_buttons)
1668 >> 1);
1670 result = TRUE;
1673 work.x = mer.dwMousePosition.X;
1674 work.y = mer.dwMousePosition.Y - AdjustY();
1676 sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1677 sp->_drv_mouse_tail += 1;
1680 return result;
1683 static int
1684 wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1686 SCREEN *sp;
1687 int n = -1;
1689 T((T_CALLED("win32con::wcon_read(%p)"), TCB));
1691 assert(buf);
1692 if (okConsoleHandle(TCB)) {
1693 SetSP();
1695 n = _nc_mingw_console_read(sp, CON.inp, buf);
1697 returnCode(n);
1700 static int
1701 wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1703 T((T_CALLED("win32con::wcon_nap(%p, %d)"), TCB, ms));
1704 Sleep((DWORD) ms);
1705 returnCode(OK);
1708 static int
1709 wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int mode)
1711 int res = -1;
1713 T((T_CALLED("win32con:wcon_cursorSet(%d)"), mode));
1714 if (okConsoleHandle(TCB)) {
1715 CONSOLE_CURSOR_INFO this_CI = CON.save_CI;
1716 switch (mode) {
1717 case 0:
1718 this_CI.bVisible = FALSE;
1719 break;
1720 case 1:
1721 break;
1722 case 2:
1723 this_CI.dwSize = 100;
1724 break;
1726 SetConsoleCursorInfo(CON.hdl, &this_CI);
1728 returnCode(res);
1731 static bool
1732 wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int keycode)
1734 WORD nKey;
1735 void *res;
1736 bool found = FALSE;
1737 LONG key = GenMap(0, (WORD) keycode);
1739 T((T_CALLED("win32con::wcon_kyExist(%d)"), keycode));
1740 res = bsearch(&key,
1741 CON.rmap,
1742 (size_t) (N_INI + FKEYS),
1743 sizeof(keylist[0]),
1744 rkeycompare);
1745 if (res) {
1746 key = *((LONG *) res);
1747 nKey = LOWORD(key);
1748 if (!(nKey & 0x8000))
1749 found = TRUE;
1751 returnCode(found);
1754 static int
1755 wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1757 SCREEN *sp;
1758 int code = ERR;
1760 T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag));
1762 if (okConsoleHandle(TCB)) {
1763 SetSP();
1765 if (sp) {
1766 code = OK;
1769 returnCode(code);
1772 static int
1773 wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB,
1774 int keycode,
1775 int flag)
1777 int code = ERR;
1778 SCREEN *sp;
1779 WORD nKey;
1780 WORD vKey;
1781 void *res;
1782 LONG key = GenMap(0, (WORD) keycode);
1784 T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag));
1786 if (okConsoleHandle(TCB)) {
1787 SetSP();
1789 if (sp) {
1790 res = bsearch(&key,
1791 CON.rmap,
1792 (size_t) (N_INI + FKEYS),
1793 sizeof(keylist[0]),
1794 rkeycompare);
1795 if (res) {
1796 key = *((LONG *) res);
1797 vKey = HIWORD(key);
1798 nKey = (LOWORD(key)) & 0x7fff;
1799 if (!flag)
1800 nKey |= 0x8000;
1801 *(LONG *) res = GenMap(vKey, nKey);
1805 returnCode(code);
1808 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1809 FALSE,
1810 wcon_name, /* Name */
1811 wcon_CanHandle, /* CanHandle */
1812 wcon_init, /* init */
1813 wcon_release, /* release */
1814 wcon_size, /* size */
1815 wcon_sgmode, /* sgmode */
1816 wcon_conattr, /* conattr */
1817 wcon_mvcur, /* hwcur */
1818 wcon_mode, /* mode */
1819 wcon_rescol, /* rescol */
1820 wcon_rescolors, /* rescolors */
1821 wcon_setcolor, /* color */
1822 wcon_dobeepflash, /* DoBeepFlash */
1823 wcon_initpair, /* initpair */
1824 wcon_initcolor, /* initcolor */
1825 wcon_do_color, /* docolor */
1826 wcon_initmouse, /* initmouse */
1827 wcon_testmouse, /* testmouse */
1828 wcon_setfilter, /* setfilter */
1829 wcon_hwlabel, /* hwlabel */
1830 wcon_hwlabelOnOff, /* hwlabelOnOff */
1831 wcon_doupdate, /* update */
1832 wcon_defaultcolors, /* defaultcolors */
1833 wcon_print, /* print */
1834 wcon_size, /* getsize */
1835 wcon_setsize, /* setsize */
1836 wcon_initacs, /* initacs */
1837 wcon_screen_init, /* scinit */
1838 wcon_wrap, /* scexit */
1839 wcon_twait, /* twait */
1840 wcon_read, /* read */
1841 wcon_nap, /* nap */
1842 wcon_kpad, /* kpad */
1843 wcon_keyok, /* kyOk */
1844 wcon_kyExist, /* kyExist */
1845 wcon_cursorSet /* cursorSet */
1848 /* --------------------------------------------------------- */
1850 static HANDLE
1851 get_handle(int fd)
1853 intptr_t value = _get_osfhandle(fd);
1854 return (HANDLE) value;
1857 #if WINVER >= 0x0600
1858 /* This function tests, whether or not the ncurses application
1859 is running as a descendant of MSYS2/cygwin mintty terminal
1860 application. mintty doesn't use Windows Console for it's screen
1861 I/O, so the native Windows _isatty doesn't recognize it as
1862 character device. But we can discover we are at the end of an
1863 Pipe and can query to server side of the pipe, looking whether
1864 or not this is mintty.
1866 static int
1867 _ismintty(int fd, LPHANDLE pMinTTY)
1869 HANDLE handle = get_handle(fd);
1870 DWORD dw;
1871 int code = 0;
1873 T((T_CALLED("win32con::_ismintty(%d, %p)"), fd, pMinTTY));
1875 if (handle != INVALID_HANDLE_VALUE) {
1876 dw = GetFileType(handle);
1877 if (dw == FILE_TYPE_PIPE) {
1878 if (GetNamedPipeInfo(handle, 0, 0, 0, 0)) {
1879 ULONG pPid;
1880 /* Requires NT6 */
1881 if (GetNamedPipeServerProcessId(handle, &pPid)) {
1882 TCHAR buf[MAX_PATH];
1883 DWORD len = 0;
1884 /* These security attributes may allow us to
1885 create a remote thread in mintty to manipulate
1886 the terminal state remotely */
1887 HANDLE pHandle = OpenProcess(
1888 PROCESS_CREATE_THREAD
1889 | PROCESS_QUERY_INFORMATION
1890 | PROCESS_VM_OPERATION
1891 | PROCESS_VM_WRITE
1892 | PROCESS_VM_READ,
1893 FALSE,
1894 pPid);
1895 if (pMinTTY)
1896 *pMinTTY = INVALID_HANDLE_VALUE;
1897 if (pHandle != INVALID_HANDLE_VALUE) {
1898 if ((len = GetProcessImageFileName(
1899 pHandle,
1900 buf,
1901 (DWORD)
1902 array_length(buf)))) {
1903 TCHAR *pos = _tcsrchr(buf, _T('\\'));
1904 if (pos) {
1905 pos++;
1906 if (_tcsnicmp(pos, _TEXT("mintty.exe"), 10)
1907 == 0) {
1908 if (pMinTTY)
1909 *pMinTTY = pHandle;
1910 code = 1;
1919 returnCode(code);
1921 #endif
1923 /* Borrowed from ansicon project.
1924 Check whether or not an I/O handle is associated with
1925 a Windows console.
1927 static BOOL
1928 IsConsoleHandle(HANDLE hdl)
1930 DWORD dwFlag = 0;
1931 BOOL result;
1933 if (!GetConsoleMode(hdl, &dwFlag)) {
1934 result = (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL);
1935 } else {
1936 result = (int) (dwFlag & ENABLE_PROCESSED_OUTPUT);
1938 return result;
1941 /* Our replacement for the systems _isatty to include also
1942 a test for mintty. This is called from the NC_ISATTY macro
1943 defined in curses.priv.h
1946 _nc_mingw_isatty(int fd)
1948 int result = 0;
1950 #ifdef __MING32__
1951 #define SysISATTY(fd) _isatty(fd)
1952 #else
1953 #define SysISATTY(fd) isatty(fd)
1954 #endif
1955 if (SysISATTY(fd)) {
1956 result = 1;
1957 } else {
1958 #if WINVER >= 0x0600
1959 result = _ismintty(fd, NULL);
1960 #endif
1962 return result;
1965 /* This is used when running in terminfo mode to discover,
1966 whether or not the "terminal" is actually a Windows
1967 Console. It's the responsibilty of the console to deal
1968 with the terminal escape sequences that are sent by
1969 terminfo.
1972 _nc_mingw_isconsole(int fd)
1974 HANDLE hdl = get_handle(fd);
1975 int code = 0;
1977 T((T_CALLED("win32con::_nc_mingw_isconsole(%d)"), fd));
1979 code = (int) IsConsoleHandle(hdl);
1981 returnCode(code);
1984 #define TC_PROLOGUE(fd) \
1985 SCREEN *sp; \
1986 TERMINAL *term = 0; \
1987 int code = ERR; \
1988 if (_nc_screen_chain == 0) \
1989 return 0; \
1990 for (each_screen(sp)) { \
1991 if (sp->_term && (sp->_term->Filedes == fd)) { \
1992 term = sp->_term; \
1993 break; \
1996 assert(term != 0)
1999 _nc_mingw_tcsetattr(
2000 int fd,
2001 int optional_action GCC_UNUSED,
2002 const struct termios *arg)
2004 TC_PROLOGUE(fd);
2006 if (_nc_mingw_isconsole(fd)) {
2007 DWORD dwFlag = 0;
2008 HANDLE ofd = get_handle(fd);
2009 if (ofd != INVALID_HANDLE_VALUE) {
2010 if (arg) {
2011 if (arg->c_lflag & ICANON)
2012 dwFlag |= ENABLE_LINE_INPUT;
2013 else
2014 dwFlag = dwFlag & (DWORD) (~ENABLE_LINE_INPUT);
2016 if (arg->c_lflag & ECHO)
2017 dwFlag = dwFlag | ENABLE_ECHO_INPUT;
2018 else
2019 dwFlag = dwFlag & (DWORD) (~ENABLE_ECHO_INPUT);
2021 if (arg->c_iflag & BRKINT)
2022 dwFlag |= ENABLE_PROCESSED_INPUT;
2023 else
2024 dwFlag = dwFlag & (DWORD) (~ENABLE_PROCESSED_INPUT);
2026 dwFlag |= ENABLE_MOUSE_INPUT;
2027 SetConsoleMode(ofd, dwFlag);
2028 code = OK;
2031 if (arg)
2032 term->Nttyb = *arg;
2034 return code;
2038 _nc_mingw_tcgetattr(int fd, struct termios *arg)
2040 TC_PROLOGUE(fd);
2042 if (_nc_mingw_isconsole(fd)) {
2043 if (arg)
2044 *arg = term->Nttyb;
2046 return code;
2050 _nc_mingw_tcflush(int fd, int queue)
2052 TC_PROLOGUE(fd);
2053 (void) term;
2055 if (_nc_mingw_isconsole(fd)) {
2056 if (queue == TCIFLUSH) {
2057 BOOL b = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
2058 if (!b)
2059 return (int) GetLastError();
2062 return code;
2066 _nc_mingw_testmouse(
2067 SCREEN *sp,
2068 HANDLE fd,
2069 int delay)
2071 int rc = 0;
2073 assert(sp);
2075 if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
2076 rc = TW_MOUSE;
2077 } else {
2078 rc = console_twait(sp,
2080 TWAIT_MASK,
2081 delay,
2082 (int *) 0
2083 EVENTLIST_2nd(evl));
2085 return rc;
2089 _nc_mingw_console_read(
2090 SCREEN *sp,
2091 HANDLE fd,
2092 int *buf)
2094 int rc = -1;
2095 INPUT_RECORD inp_rec;
2096 BOOL b;
2097 DWORD nRead;
2098 WORD vk;
2100 assert(sp);
2101 assert(buf);
2103 memset(&inp_rec, 0, sizeof(inp_rec));
2105 T((T_CALLED("_nc_mingw_console_read(%p)"), sp));
2107 while ((b = ReadConsoleInput(fd, &inp_rec, 1, &nRead))) {
2108 if (b && nRead > 0) {
2109 if (rc < 0)
2110 rc = 0;
2111 rc += nRead;
2112 if (inp_rec.EventType == KEY_EVENT) {
2113 if (!inp_rec.Event.KeyEvent.bKeyDown)
2114 continue;
2115 *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
2116 vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
2118 * There are 24 virtual function-keys, and typically
2119 * 12 function-keys on a keyboard. Use the shift-modifier
2120 * to provide the remaining 12 keys.
2122 if (vk >= VK_F1 && vk <= VK_F12) {
2123 if (inp_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) {
2124 vk = (WORD) (vk + 12);
2127 if (*buf == 0) {
2128 int key = MapKey(vk);
2129 if (key < 0)
2130 continue;
2131 if (sp->_keypad_on) {
2132 *buf = key;
2133 } else {
2134 ungetch('\0');
2135 *buf = AnsiKey(vk);
2138 break;
2139 } else if (inp_rec.EventType == MOUSE_EVENT) {
2140 if (handle_mouse(sp,
2141 inp_rec.Event.MouseEvent)) {
2142 *buf = KEY_MOUSE;
2143 break;
2146 continue;
2149 returnCode(rc);
2152 static bool
2153 InitConsole(void)
2155 /* initalize once, or not at all */
2156 if (!console_initialized) {
2157 int i;
2158 DWORD num_buttons;
2159 WORD a;
2160 BOOL buffered = TRUE;
2161 BOOL b;
2163 START_TRACE();
2164 if (_nc_mingw_isatty(0)) {
2165 CON.isMinTTY = TRUE;
2168 for (i = 0; i < (N_INI + FKEYS); i++) {
2169 if (i < N_INI) {
2170 CON.rmap[i] = CON.map[i] =
2171 (DWORD) keylist[i];
2172 CON.ansi_map[i] = (DWORD) ansi_keys[i];
2173 } else {
2174 CON.rmap[i] = CON.map[i] =
2175 (DWORD) GenMap((VK_F1 + (i - N_INI)),
2176 (KEY_F(1) + (i - N_INI)));
2177 CON.ansi_map[i] =
2178 (DWORD) GenMap((VK_F1 + (i - N_INI)),
2179 (';' + (i - N_INI)));
2182 qsort(CON.ansi_map,
2183 (size_t) (MAPSIZE),
2184 sizeof(keylist[0]),
2185 keycompare);
2186 qsort(CON.map,
2187 (size_t) (MAPSIZE),
2188 sizeof(keylist[0]),
2189 keycompare);
2190 qsort(CON.rmap,
2191 (size_t) (MAPSIZE),
2192 sizeof(keylist[0]),
2193 rkeycompare);
2195 if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
2196 CON.numButtons = (int) num_buttons;
2197 } else {
2198 CON.numButtons = 1;
2201 a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
2202 for (i = 0; i < NUMPAIRS; i++)
2203 CON.pairs[i] = a;
2205 CON.inp = GetStdHandle(STD_INPUT_HANDLE);
2206 CON.out = GetStdHandle(STD_OUTPUT_HANDLE);
2208 b = AllocConsole();
2210 if (!b)
2211 b = AttachConsole(ATTACH_PARENT_PROCESS);
2213 if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
2214 T(("... will not buffer console"));
2215 buffered = FALSE;
2216 CON.hdl = CON.out;
2217 } else {
2218 T(("... creating console buffer"));
2219 CON.hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
2221 NULL,
2222 CONSOLE_TEXTMODE_BUFFER,
2223 NULL);
2226 if (CON.hdl != INVALID_HANDLE_VALUE) {
2227 CON.buffered = buffered;
2228 get_SBI();
2229 CON.save_SBI = CON.SBI;
2230 if (!buffered) {
2231 save_original_screen();
2232 set_scrollback(FALSE, &CON.SBI);
2234 GetConsoleCursorInfo(CON.hdl, &CON.save_CI);
2235 T(("... initial cursor is %svisible, %d%%",
2236 (CON.save_CI.bVisible ? "" : "not-"),
2237 (int) CON.save_CI.dwSize));
2240 console_initialized = TRUE;
2242 return (CON.hdl != INVALID_HANDLE_VALUE);
2245 static bool
2246 okConsoleHandle(TERMINAL_CONTROL_BLOCK * TCB)
2248 return ((TCB != 0) &&
2249 (TCB->magic == WINMAGIC) &&
2250 InitConsole());
2254 * While a constructor would ensure that this module is initialized, that will
2255 * interfere with applications that may combine this with GUI interfaces.
2257 #if 0
2258 static
2259 __attribute__((constructor))
2260 void _enter_console(void)
2262 (void) InitConsole();
2264 #endif