1 /****************************************************************************
2 * Copyright (c) 1998-2013,2014 Free Software Foundation, Inc. *
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: *
12 * The above copyright notice and this permission notice shall be included *
13 * in all copies or substantial portions of the Software. *
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. *
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 *
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 - implement flash
38 * TODO - improve screen-repainting, using implied wraparound
39 * TODO - if non-buffered, change buffer size (temporarily) to window-size - SetConsoleScreenBufferSize
42 #include <curses.priv.h>
46 #define PSAPI_VERSION 2
49 #define CUR my_term.type.
51 MODULE_ID("$Id: win_driver.c,v 1.43 2014/08/09 20:31:40 tom Exp $")
54 # error We need GCC to compile for MinGW
57 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
59 #define EXP_OPTIMIZE 0
61 #define array_length(a) (sizeof(a)/sizeof(a[0]))
63 #define okConsoleHandle(TCB) (TCB != 0 && CON.hdl != INVALID_HANDLE_VALUE)
65 #define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC))
66 #define SetSP() assert(TCB->csp != 0); sp = TCB->csp; (void) sp
68 #define GenMap(vKey,key) MAKELONG(key, vKey)
70 #define AdjustY() (CON.buffered ? 0 : (int) CON.SBI.srWindow.Top)
72 static const LONG keylist[] =
74 GenMap(VK_PRIOR, KEY_PPAGE),
75 GenMap(VK_NEXT, KEY_NPAGE),
76 GenMap(VK_END, KEY_END),
77 GenMap(VK_HOME, KEY_HOME),
78 GenMap(VK_LEFT, KEY_LEFT),
79 GenMap(VK_UP, KEY_UP),
80 GenMap(VK_RIGHT, KEY_RIGHT),
81 GenMap(VK_DOWN, KEY_DOWN),
82 GenMap(VK_DELETE, KEY_DC),
83 GenMap(VK_INSERT, KEY_IC)
85 #define N_INI ((int)array_length(keylist))
87 #define MAPSIZE (FKEYS + N_INI)
90 /* A process can only have a single console, so it's save
91 to maintain all the information about it in a single
100 BOOL isTermInfoConsole;
108 WORD pairs[NUMPAIRS];
110 CHAR_INFO *save_screen;
112 SMALL_RECT save_region;
113 CONSOLE_SCREEN_BUFFER_INFO SBI;
116 static BOOL console_initialized = FALSE;
119 MapColor(bool fore, int color)
121 static const int _cmap[] =
122 {0, 4, 2, 6, 1, 5, 3, 7};
124 if (color < 0 || color > 7)
134 MapAttr(WORD res, attr_t ch)
140 if (p > 0 && p < NUMPAIRS) {
143 res = (WORD) ((res & 0xff00) | a);
147 if (ch & A_REVERSE) {
148 res = (WORD) ((res & 0xff00) |
149 (((res & 0x07) << 4) |
150 ((res & 0x70) >> 4)));
153 if (ch & A_STANDOUT) {
154 res = (WORD) ((res & 0xff00) |
155 (((res & 0x07) << 4) |
156 ((res & 0x70) >> 4)) |
157 BACKGROUND_INTENSITY);
161 res |= FOREGROUND_INTENSITY;
164 res |= BACKGROUND_INTENSITY;
169 #if 0 /* def TRACE */
171 dump_screen(const char *fn, int ln)
173 int max_cells = (CON.SBI.dwSize.Y * (1 + CON.SBI.dwSize.X)) + 1;
174 char output[max_cells];
175 CHAR_INFO save_screen[max_cells];
177 SMALL_RECT save_region;
180 T(("dump_screen %s@%d", fn, ln));
182 save_region.Top = CON.SBI.srWindow.Top;
183 save_region.Left = CON.SBI.srWindow.Left;
184 save_region.Bottom = CON.SBI.srWindow.Bottom;
185 save_region.Right = CON.SBI.srWindow.Right;
187 save_size.X = (SHORT) (save_region.Right - save_region.Left + 1);
188 save_size.Y = (SHORT) (save_region.Bottom - save_region.Top + 1);
190 bufferCoord.X = bufferCoord.Y = 0;
192 if (ReadConsoleOutput(CON.hdl,
201 for (i = save_region.Top; i <= save_region.Bottom; ++i) {
202 for (j = save_region.Left; j <= save_region.Right; ++j) {
203 output[k++] = save_screen[ij++].Char.AsciiChar;
209 T(("DUMP: %d,%d - %d,%d",
219 #define dump_screen(fn,ln) /* nothing */
222 #if USE_WIDEC_SUPPORT
224 * TODO: support surrogate pairs
225 * TODO: support combining characters
227 * TODO: _nc_wacs should be part of sp.
230 con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
243 for (i = actual = 0; i < limit; i++) {
247 ci[actual].Char.UnicodeChar = CharOf(ch);
248 ci[actual].Attributes = MapAttr(CON.SBI.wAttributes,
250 if (AttrOf(ch) & A_ALTCHARSET) {
252 int which = CharOf(ch);
255 && CharOf(_nc_wacs[which]) != 0) {
256 ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
258 ci[actual].Char.UnicodeChar = ' ';
267 siz.X = (SHORT) actual;
270 rec.Left = (SHORT) x;
271 rec.Top = (SHORT) (y + AdjustY());
272 rec.Right = (SHORT) (x + limit - 1);
273 rec.Bottom = rec.Top;
275 return WriteConsoleOutputW(CON.hdl, ci, siz, loc, &rec);
277 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
280 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
292 for (i = 0; i < n; i++) {
294 ci[i].Char.AsciiChar = ChCharOf(ch);
295 ci[i].Attributes = MapAttr(CON.SBI.wAttributes,
297 if (ChAttrOf(ch) & A_ALTCHARSET) {
299 ci[i].Char.AsciiChar =
300 ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
309 rec.Left = (short) x;
311 rec.Right = (short) (x + n - 1);
312 rec.Bottom = rec.Top;
314 return WriteConsoleOutput(CON.hdl, ci, siz, loc, &rec);
316 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
321 * Comparing new/current screens, determine the last column-index for a change
322 * beginning on the given row,col position. Unlike a serial terminal, there is
323 * no cost for "moving" the "cursor" on the line as we update it.
326 find_end_of_change(SCREEN *sp, int row, int col)
329 struct ldat *curdat = CurScreen(sp)->_line + row;
330 struct ldat *newdat = NewScreen(sp)->_line + row;
332 while (col <= newdat->lastchar) {
333 #if USE_WIDEC_SUPPORT
334 if (isWidecExt(curdat->text[col]) || isWidecExt(newdat->text[col])) {
336 } else if (memcmp(&curdat->text[col],
338 sizeof(curdat->text[0]))) {
344 if (curdat->text[col] != newdat->text[col]) {
356 * Given a row,col position at the end of a change-chunk, look for the
357 * beginning of the next change-chunk.
360 find_next_change(SCREEN *sp, int row, int col)
362 struct ldat *curdat = CurScreen(sp)->_line + row;
363 struct ldat *newdat = NewScreen(sp)->_line + row;
364 int result = newdat->lastchar + 1;
366 while (++col <= newdat->lastchar) {
367 #if USE_WIDEC_SUPPORT
368 if (isWidecExt(curdat->text[col]) != isWidecExt(newdat->text[col])) {
371 } else if (memcmp(&curdat->text[col],
373 sizeof(curdat->text[0]))) {
378 if (curdat->text[col] != newdat->text[col]) {
387 #define EndChange(first) \
388 find_end_of_change(sp, y, first)
389 #define NextChange(last) \
390 find_next_change(sp, y, last)
392 #endif /* EXP_OPTIMIZE */
394 #define MARK_NOCHANGE(win,row) \
395 win->_line[row].firstchar = _NOCHANGE; \
396 win->_line[row].lastchar = _NOCHANGE
399 selectActiveHandle(void)
401 if (CON.lastOut != CON.hdl) {
402 CON.lastOut = CON.hdl;
403 SetConsoleActiveScreenBuffer(CON.lastOut);
408 restore_original_screen(void)
412 SMALL_RECT save_region = CON.save_region;
414 T(("... restoring %s", CON.window_only ? "window" : "entire buffer"));
416 bufferCoord.X = bufferCoord.Y = 0;
418 if (WriteConsoleOutput(CON.hdl,
424 mvcur(-1, -1, LINES - 2, 0);
425 T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)",
433 T(("... restore original screen contents err"));
439 wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
442 return "win32console";
446 wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
449 int y, nonempty, n, x0, x1, Width, Height;
455 T((T_CALLED("win32con::wcon_doupdate(%p)"), TCB));
456 if (okConsoleHandle(TCB)) {
458 Width = screen_columns(sp);
459 Height = screen_lines(sp);
460 nonempty = min(Height, NewScreen(sp)->_maxy + 1);
462 T(("... %dx%d clear cur:%d new:%d",
464 CurScreen(sp)->_clear,
465 NewScreen(sp)->_clear));
467 if (SP_PARM->_endwin) {
469 T(("coming back from shell mode"));
470 NCURSES_SP_NAME(reset_prog_mode) (NCURSES_SP_ARG);
472 NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG);
473 NCURSES_SP_NAME(_nc_screen_resume) (NCURSES_SP_ARG);
474 SP_PARM->_mouse_resume(SP_PARM);
476 SP_PARM->_endwin = FALSE;
479 if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
481 #if USE_WIDEC_SUPPORT
482 cchar_t empty[Width];
488 for (x = 0; x < Width; x++)
489 setcchar(&empty[x], blank, 0, 0, 0);
493 for (x = 0; x < Width; x++)
497 for (y = 0; y < nonempty; y++) {
498 con_write(TCB, y, 0, empty, Width);
500 CurScreen(sp)->_line[y].text,
501 (size_t) Width * sizeof(empty[0]));
503 CurScreen(sp)->_clear = FALSE;
504 NewScreen(sp)->_clear = FALSE;
505 touchwin(NewScreen(sp));
506 T(("... cleared %dx%d lines @%d of screen", nonempty, Width,
510 for (y = 0; y < nonempty; y++) {
511 x0 = NewScreen(sp)->_line[y].firstchar;
512 if (x0 != _NOCHANGE) {
515 int limit = NewScreen(sp)->_line[y].lastchar;
516 while ((x1 = EndChange(x0)) <= limit) {
517 while ((x2 = NextChange(x1)) <= limit && x2 <= (x1 + 2)) {
521 memcpy(&CurScreen(sp)->_line[y].text[x0],
522 &NewScreen(sp)->_line[y].text[x0],
523 n * sizeof(CurScreen(sp)->_line[y].text[x0]));
527 &CurScreen(sp)->_line[y].text[x0], n);
531 /* mark line changed successfully */
532 if (y <= NewScreen(sp)->_maxy) {
533 MARK_NOCHANGE(NewScreen(sp), y);
535 if (y <= CurScreen(sp)->_maxy) {
536 MARK_NOCHANGE(CurScreen(sp), y);
539 x1 = NewScreen(sp)->_line[y].lastchar;
542 memcpy(&CurScreen(sp)->_line[y].text[x0],
543 &NewScreen(sp)->_line[y].text[x0],
544 (size_t) n * sizeof(CurScreen(sp)->_line[y].text[x0]));
548 &CurScreen(sp)->_line[y].text[x0], n);
550 /* mark line changed successfully */
551 if (y <= NewScreen(sp)->_maxy) {
552 MARK_NOCHANGE(NewScreen(sp), y);
554 if (y <= CurScreen(sp)->_maxy) {
555 MARK_NOCHANGE(CurScreen(sp), y);
562 /* put everything back in sync */
563 for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
564 MARK_NOCHANGE(NewScreen(sp), y);
566 for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
567 MARK_NOCHANGE(CurScreen(sp), y);
570 if (!NewScreen(sp)->_leaveok) {
571 CurScreen(sp)->_curx = NewScreen(sp)->_curx;
572 CurScreen(sp)->_cury = NewScreen(sp)->_cury;
574 TCB->drv->td_hwcur(TCB,
576 CurScreen(sp)->_cury, CurScreen(sp)->_curx);
578 selectActiveHandle();
585 wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
587 int *errret GCC_UNUSED)
591 T((T_CALLED("win32con::wcon_CanHandle(%p)"), TCB));
593 assert((TCB != 0) && (tname != 0));
595 TCB->magic = WINMAGIC;
597 if (tname == 0 || *tname == 0)
599 else if (tname != 0 && *tname == '#') {
601 * Use "#" (a character which cannot begin a terminal's name) to
602 * select specific driver from the table.
604 * In principle, we could have more than one non-terminfo driver,
607 size_t n = strlen(tname + 1);
609 && ((strncmp(tname + 1, "win32console", n) == 0)
610 || (strncmp(tname + 1, "win32con", n) == 0))) {
613 } else if (tname != 0 && stricmp(tname, "unknown") == 0) {
618 * This is intentional, to avoid unnecessary breakage of applications
619 * using <term.h> symbols.
621 if (code && (TCB->term.type.Booleans == 0)) {
622 _nc_init_termtype(&(TCB->term.type));
626 if (_nc_mingw_isconsole(0))
627 CON.isTermInfoConsole = TRUE;
633 wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
634 int beepFlag GCC_UNUSED)
642 MessageBeep(MB_ICONWARNING); /* MB_OK might be better */
647 wcon_print(TERMINAL_CONTROL_BLOCK * TCB,
648 char *data GCC_UNUSED,
660 wcon_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
677 if (GetConsoleScreenBufferInfo(CON.hdl, &(CON.SBI))) {
678 T(("GetConsoleScreenBufferInfo"));
679 T(("... buffer(X:%d Y:%d)",
682 T(("... window(X:%d Y:%d)",
683 CON.SBI.dwMaximumWindowSize.X,
684 CON.SBI.dwMaximumWindowSize.Y));
685 T(("... cursor(X:%d Y:%d)",
686 CON.SBI.dwCursorPosition.X,
687 CON.SBI.dwCursorPosition.Y));
688 T(("... display(Top:%d Bottom:%d Left:%d Right:%d)",
689 CON.SBI.srWindow.Top,
690 CON.SBI.srWindow.Bottom,
691 CON.SBI.srWindow.Left,
692 CON.SBI.srWindow.Right));
697 CON.origin.X = CON.SBI.srWindow.Left;
698 CON.origin.Y = CON.SBI.srWindow.Top;
702 T(("GetConsoleScreenBufferInfo ERR"));
708 wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
711 int (*outc) (SCREEN *, int) GCC_UNUSED)
715 if (okConsoleHandle(TCB)) {
716 WORD a = MapColor(fore, color);
717 a |= (WORD) ((CON.SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f));
718 SetConsoleTextAttribute(CON.hdl, a);
724 wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB)
729 if (okConsoleHandle(TCB)) {
730 WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
731 SetConsoleTextAttribute(CON.hdl, a);
739 wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
751 wcon_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
757 T((T_CALLED("win32con::wcon_size(%p)"), TCB));
759 if (okConsoleHandle(TCB) &&
763 *Lines = (int) (CON.SBI.dwSize.Y);
764 *Cols = (int) (CON.SBI.dwSize.X);
766 *Lines = (int) (CON.SBI.srWindow.Bottom + 1 -
767 CON.SBI.srWindow.Top);
768 *Cols = (int) (CON.SBI.srWindow.Right + 1 -
769 CON.SBI.srWindow.Left);
777 wcon_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
786 wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
794 if (TCB == 0 || buf == NULL)
798 iflag = buf->c_iflag;
799 lflag = buf->c_lflag;
801 GetConsoleMode(CON.inp, &dwFlag);
804 dwFlag |= ENABLE_LINE_INPUT;
806 dwFlag &= (DWORD) (~ENABLE_LINE_INPUT);
809 dwFlag |= ENABLE_ECHO_INPUT;
811 dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT);
814 dwFlag |= ENABLE_PROCESSED_INPUT;
816 dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT);
818 dwFlag |= ENABLE_MOUSE_INPUT;
820 buf->c_iflag = iflag;
821 buf->c_lflag = lflag;
822 SetConsoleMode(CON.inp, dwFlag);
823 TCB->term.Nttyb = *buf;
825 iflag = TCB->term.Nttyb.c_iflag;
826 lflag = TCB->term.Nttyb.c_lflag;
827 GetConsoleMode(CON.inp, &dwFlag);
829 if (dwFlag & ENABLE_LINE_INPUT)
832 lflag &= (tcflag_t) (~ICANON);
834 if (dwFlag & ENABLE_ECHO_INPUT)
837 lflag &= (tcflag_t) (~ECHO);
839 if (dwFlag & ENABLE_PROCESSED_INPUT)
842 iflag &= (tcflag_t) (~BRKINT);
844 TCB->term.Nttyb.c_iflag = iflag;
845 TCB->term.Nttyb.c_lflag = lflag;
847 *buf = TCB->term.Nttyb;
853 wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
856 TERMINAL *_term = (TERMINAL *) TCB;
862 T((T_CALLED("win32con::wcon_mode(%p, prog=%d, def=%d)"), TCB, progFlag, defFlag));
863 CON.progMode = progFlag;
864 CON.lastOut = progFlag ? CON.hdl : CON.out;
865 SetConsoleActiveScreenBuffer(CON.lastOut);
867 if (progFlag) /* prog mode */ {
869 if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
870 _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS);
874 /* reset_prog_mode */
875 if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
878 _nc_keypad(sp, TRUE);
883 T(("... buffered:%d, clear:%d", CON.buffered, CurScreen(sp)->_clear));
884 } else { /* shell mode */
887 if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
891 /* reset_shell_mode */
893 _nc_keypad(sp, FALSE);
894 NCURSES_SP_NAME(_nc_flush) (sp);
896 code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb));
898 if (!restore_original_screen())
908 wcon_screen_init(SCREEN *sp GCC_UNUSED)
913 wcon_wrap(SCREEN *sp GCC_UNUSED)
918 rkeycompare(const void *el1, const void *el2)
920 WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
921 WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
923 return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
927 keycompare(const void *el1, const void *el2)
929 WORD key1 = HIWORD((*((const LONG *) el1)));
930 WORD key2 = HIWORD((*((const LONG *) el2)));
932 return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
940 LONG key = GenMap(vKey, 0);
945 (size_t) (N_INI + FKEYS),
949 key = *((LONG *) res);
951 code = (int) (nKey & 0x7fff);
959 wcon_release(TERMINAL_CONTROL_BLOCK * TCB)
961 T((T_CALLED("win32con::wcon_release(%p)"), TCB));
971 read_screen_data(void)
977 CON.save_size.X = (SHORT) (CON.save_region.Right
978 - CON.save_region.Left + 1);
979 CON.save_size.Y = (SHORT) (CON.save_region.Bottom
980 - CON.save_region.Top + 1);
982 want = (size_t) (CON.save_size.X * CON.save_size.Y);
984 if ((CON.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
985 bufferCoord.X = bufferCoord.Y = 0;
987 T(("... reading console %s %dx%d into %d,%d - %d,%d at %d,%d",
988 CON.window_only ? "window" : "buffer",
989 CON.save_size.Y, CON.save_size.X,
991 CON.save_region.Left,
992 CON.save_region.Bottom,
993 CON.save_region.Right,
997 if (ReadConsoleOutput(CON.hdl,
1001 &CON.save_region)) {
1004 T((" error %#lx", (unsigned long) GetLastError()));
1005 FreeAndNull(CON.save_screen);
1013 * Attempt to save the screen contents. PDCurses does this if
1014 * PDC_RESTORE_SCREEN is set, giving the same visual appearance on
1015 * restoration as if the library had allocated a console buffer. MSDN
1016 * says that the data which can be read is limited to 64Kb (and may be
1020 save_original_screen(void)
1022 bool result = FALSE;
1024 CON.save_region.Top = 0;
1025 CON.save_region.Left = 0;
1026 CON.save_region.Bottom = (SHORT) (CON.SBI.dwSize.Y - 1);
1027 CON.save_region.Right = (SHORT) (CON.SBI.dwSize.X - 1);
1029 if (read_screen_data()) {
1033 CON.save_region.Top = CON.SBI.srWindow.Top;
1034 CON.save_region.Left = CON.SBI.srWindow.Left;
1035 CON.save_region.Bottom = CON.SBI.srWindow.Bottom;
1036 CON.save_region.Right = CON.SBI.srWindow.Right;
1038 CON.window_only = TRUE;
1040 if (read_screen_data()) {
1045 T(("... save original screen contents %s", result ? "ok" : "err"));
1050 wcon_init(TERMINAL_CONTROL_BLOCK * TCB)
1052 T((T_CALLED("win32con::wcon_init(%p)"), TCB));
1057 if (CON.hdl == INVALID_HANDLE_VALUE) {
1061 TCB->info.initcolor = TRUE;
1062 TCB->info.canchange = FALSE;
1063 TCB->info.hascolor = TRUE;
1064 TCB->info.caninit = TRUE;
1066 TCB->info.maxpairs = NUMPAIRS;
1067 TCB->info.maxcolors = 8;
1068 TCB->info.numlabels = 0;
1069 TCB->info.labelwidth = 0;
1070 TCB->info.labelheight = 0;
1071 TCB->info.nocolorvideo = 1;
1072 TCB->info.tabsize = 8;
1074 TCB->info.numbuttons = CON.numButtons;
1075 TCB->info.defaultPalette = _nc_cga_palette;
1082 wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB,
1092 if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
1093 && (b >= 0) && (b < 8)) {
1094 CON.pairs[pair] = MapColor(true, f) | MapColor(false, b);
1099 wcon_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
1100 int color GCC_UNUSED,
1112 wcon_do_color(TERMINAL_CONTROL_BLOCK * TCB,
1113 int old_pair GCC_UNUSED,
1114 int pair GCC_UNUSED,
1115 int reverse GCC_UNUSED,
1116 int (*outc) (SCREEN *, int) GCC_UNUSED
1126 wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
1133 sp->_mouse_type = M_TERM_DRIVER;
1137 wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB, int delay)
1145 if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1148 rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
1152 EVENTLIST_2nd(evl));
1159 wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
1160 int yold GCC_UNUSED, int xold GCC_UNUSED,
1164 if (okConsoleHandle(TCB)) {
1167 loc.Y = (short) (y + AdjustY());
1168 SetConsoleCursorPosition(CON.hdl, loc);
1175 wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
1176 int labnum GCC_UNUSED,
1177 char *text GCC_UNUSED)
1186 wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1187 int OnFlag GCC_UNUSED)
1196 wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1198 chtype res = A_NORMAL;
1199 res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1204 wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1213 wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1214 chtype *real_map GCC_UNUSED,
1215 chtype *fake_map GCC_UNUSED)
1217 #define DATA(a,b) { a, b }
1222 DATA('a', 0xb1), /* ACS_CKBOARD */
1223 DATA('f', 0xf8), /* ACS_DEGREE */
1224 DATA('g', 0xf1), /* ACS_PLMINUS */
1225 DATA('j', 0xd9), /* ACS_LRCORNER */
1226 DATA('l', 0xda), /* ACS_ULCORNER */
1227 DATA('k', 0xbf), /* ACS_URCORNER */
1228 DATA('m', 0xc0), /* ACS_LLCORNER */
1229 DATA('n', 0xc5), /* ACS_PLUS */
1230 DATA('q', 0xc4), /* ACS_HLINE */
1231 DATA('t', 0xc3), /* ACS_LTEE */
1232 DATA('u', 0xb4), /* ACS_RTEE */
1233 DATA('v', 0xc1), /* ACS_BTEE */
1234 DATA('w', 0xc2), /* ACS_TTEE */
1235 DATA('x', 0xb3), /* ACS_VLINE */
1236 DATA('y', 0xf3), /* ACS_LEQUAL */
1237 DATA('z', 0xf2), /* ACS_GEQUAL */
1238 DATA('0', 0xdb), /* ACS_BLOCK */
1239 DATA('{', 0xe3), /* ACS_PI */
1240 DATA('}', 0x9c), /* ACS_STERLING */
1241 DATA(',', 0xae), /* ACS_LARROW */
1242 DATA('+', 0xaf), /* ACS_RARROW */
1243 DATA('~', 0xf9), /* ACS_BULLET */
1252 for (n = 0; n < SIZEOF(table); ++n) {
1253 real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET;
1255 sp->_screen_acs_map[table[n].acs_code] = TRUE;
1260 tdiff(FILETIME fstart, FILETIME fend)
1262 ULARGE_INTEGER ustart;
1263 ULARGE_INTEGER uend;
1266 ustart.LowPart = fstart.dwLowDateTime;
1267 ustart.HighPart = fstart.dwHighDateTime;
1268 uend.LowPart = fend.dwLowDateTime;
1269 uend.HighPart = fend.dwHighDateTime;
1271 diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1276 Adjust(int milliseconds, int diff)
1278 if (milliseconds != INFINITY) {
1279 milliseconds -= diff;
1280 if (milliseconds < 0)
1283 return milliseconds;
1286 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1287 FROM_LEFT_2ND_BUTTON_PRESSED | \
1288 FROM_LEFT_3RD_BUTTON_PRESSED | \
1289 FROM_LEFT_4TH_BUTTON_PRESSED | \
1290 RIGHTMOST_BUTTON_PRESSED)
1293 decode_mouse(SCREEN *sp, int mask)
1298 assert(sp && console_initialized);
1300 if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1301 result |= BUTTON1_PRESSED;
1302 if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1303 result |= BUTTON2_PRESSED;
1304 if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1305 result |= BUTTON3_PRESSED;
1306 if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1307 result |= BUTTON4_PRESSED;
1309 if (mask & RIGHTMOST_BUTTON_PRESSED) {
1310 switch (CON.numButtons) {
1312 result |= BUTTON1_PRESSED;
1315 result |= BUTTON2_PRESSED;
1318 result |= BUTTON3_PRESSED;
1321 result |= BUTTON4_PRESSED;
1336 EVENTLIST_2nd(_nc_eventlist * evl))
1338 INPUT_RECORD inp_rec;
1340 DWORD nRead = 0, rc = (DWORD) (-1);
1345 bool isImmed = (milliseconds == 0);
1347 #define CONSUME() ReadConsoleInput(fd,&inp_rec,1,&nRead)
1351 TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1352 milliseconds, mode));
1354 if (milliseconds < 0)
1355 milliseconds = INFINITY;
1357 memset(&inp_rec, 0, sizeof(inp_rec));
1360 GetSystemTimeAsFileTime(&fstart);
1361 rc = WaitForSingleObject(fd, (DWORD) milliseconds);
1362 GetSystemTimeAsFileTime(&fend);
1363 diff = (int) tdiff(fstart, fend);
1364 milliseconds = Adjust(milliseconds, diff);
1366 if (!isImmed && milliseconds == 0)
1369 if (rc == WAIT_OBJECT_0) {
1371 b = GetNumberOfConsoleInputEvents(fd, &nRead);
1372 if (b && nRead > 0) {
1373 b = PeekConsoleInput(fd, &inp_rec, 1, &nRead);
1374 if (b && nRead > 0) {
1375 switch (inp_rec.EventType) {
1377 if (mode & TW_INPUT) {
1378 WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1379 char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1381 if (inp_rec.Event.KeyEvent.bKeyDown) {
1383 int nKey = MapKey(vk);
1384 if ((nKey < 0) || FALSE == sp->_keypad_on) {
1397 if (decode_mouse(sp,
1398 (inp_rec.Event.MouseEvent.dwButtonState
1399 & BUTTON_MASK)) == 0) {
1401 } else if (mode & TW_MOUSE) {
1407 selectActiveHandle();
1415 if (rc != WAIT_TIMEOUT) {
1426 TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1427 code, errno, milliseconds));
1430 *timeleft = milliseconds;
1436 wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,
1440 EVENTLIST_2nd(_nc_eventlist * evl))
1448 code = console_twait(sp,
1452 timeleft EVENTLIST_2nd(_nc_eventlist * evl));
1457 handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer)
1460 bool result = FALSE;
1464 sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1465 sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1468 * We're only interested if the button is pressed or released.
1469 * FIXME: implement continuous event-tracking.
1471 if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1473 memset(&work, 0, sizeof(work));
1475 if (sp->_drv_mouse_new_buttons) {
1477 work.bstate |= (mmask_t) decode_mouse(sp, sp->_drv_mouse_new_buttons);
1481 /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1482 work.bstate |= (mmask_t) (decode_mouse(sp,
1483 sp->_drv_mouse_old_buttons)
1489 work.x = mer.dwMousePosition.X;
1490 work.y = mer.dwMousePosition.Y - AdjustY();
1492 sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1493 sp->_drv_mouse_tail += 1;
1500 wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1509 T((T_CALLED("win32con::wcon_read(%p)"), TCB));
1510 n = _nc_mingw_console_read(sp, CON.inp, buf);
1515 wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1517 T((T_CALLED("win32con::wcon_nap(%p, %d)"), TCB, ms));
1523 wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int keycode)
1528 LONG key = GenMap(0, (WORD) keycode);
1530 T((T_CALLED("win32con::wcon_kyExist(%d)"), keycode));
1533 (size_t) (N_INI + FKEYS),
1537 key = *((LONG *) res);
1539 if (!(nKey & 0x8000))
1546 wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1554 T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag));
1562 wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB,
1571 LONG key = GenMap(0, (WORD) keycode);
1573 T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag));
1581 (size_t) (N_INI + FKEYS),
1585 key = *((LONG *) res);
1587 nKey = (LOWORD(key)) & 0x7fff;
1590 *(LONG *) res = GenMap(vKey, nKey);
1596 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1598 wcon_name, /* Name */
1599 wcon_CanHandle, /* CanHandle */
1600 wcon_init, /* init */
1601 wcon_release, /* release */
1602 wcon_size, /* size */
1603 wcon_sgmode, /* sgmode */
1604 wcon_conattr, /* conattr */
1605 wcon_mvcur, /* hwcur */
1606 wcon_mode, /* mode */
1607 wcon_rescol, /* rescol */
1608 wcon_rescolors, /* rescolors */
1609 wcon_setcolor, /* color */
1610 wcon_dobeepflash, /* DoBeepFlash */
1611 wcon_initpair, /* initpair */
1612 wcon_initcolor, /* initcolor */
1613 wcon_do_color, /* docolor */
1614 wcon_initmouse, /* initmouse */
1615 wcon_testmouse, /* testmouse */
1616 wcon_setfilter, /* setfilter */
1617 wcon_hwlabel, /* hwlabel */
1618 wcon_hwlabelOnOff, /* hwlabelOnOff */
1619 wcon_doupdate, /* update */
1620 wcon_defaultcolors, /* defaultcolors */
1621 wcon_print, /* print */
1622 wcon_size, /* getsize */
1623 wcon_setsize, /* setsize */
1624 wcon_initacs, /* initacs */
1625 wcon_screen_init, /* scinit */
1626 wcon_wrap, /* scexit */
1627 wcon_twait, /* twait */
1628 wcon_read, /* read */
1630 wcon_kpad, /* kpad */
1631 wcon_keyok, /* kyOk */
1632 wcon_kyExist /* kyExist */
1635 /* --------------------------------------------------------- */
1640 intptr_t value = _get_osfhandle(fd);
1641 return (HANDLE) value;
1644 #if WINVER >= 0x0600
1645 /* This function tests, whether or not the ncurses application
1646 is running as a descendant of MSYS2/cygwin mintty terminal
1647 application. mintty doesn't use Windows Console for it's screen
1648 I/O, so the native Windows _isatty doesn't recognize it as
1649 character device. But we can discover we are at the end of an
1650 Pipe and can query to server side of the pipe, looking whether
1651 or not this is mintty.
1654 _ismintty(int fd, LPHANDLE pMinTTY)
1656 HANDLE handle = get_handle(fd);
1660 T((T_CALLED("win32con::_ismintty(%d, %p)"), fd, pMinTTY));
1662 if (handle != INVALID_HANDLE_VALUE) {
1663 dw = GetFileType(handle);
1664 if (dw == FILE_TYPE_PIPE) {
1665 if (GetNamedPipeInfo(handle, 0, 0, 0, 0)) {
1668 if (GetNamedPipeServerProcessId(handle, &pPid)) {
1669 TCHAR buf[MAX_PATH];
1671 /* These security attributes may allow us to
1672 create a remote thread in mintty to manipulate
1673 the terminal state remotely */
1674 HANDLE pHandle = OpenProcess(
1675 PROCESS_CREATE_THREAD
1676 | PROCESS_QUERY_INFORMATION
1677 | PROCESS_VM_OPERATION
1683 *pMinTTY = INVALID_HANDLE_VALUE;
1684 if (pHandle != INVALID_HANDLE_VALUE) {
1685 if ((len = GetProcessImageFileName(
1689 array_length(buf)))) {
1690 TCHAR *pos = _tcsrchr(buf, _T('\\'));
1693 if (_tcsnicmp(pos, _TEXT("mintty.exe"), 10)
1710 /* Borrowed from ansicon project.
1711 Check whether or not an I/O handle is associated with
1715 IsConsoleHandle(HANDLE hdl)
1720 if (!GetConsoleMode(hdl, &dwFlag)) {
1721 result = (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL);
1723 result = (int) (dwFlag & ENABLE_PROCESSED_OUTPUT);
1728 /* Our replacement for the systems _isatty to include also
1729 a test for mintty. This is called from the NC_ISATTY macro
1730 defined in curses.priv.h
1733 _nc_mingw_isatty(int fd)
1740 #if WINVER >= 0x0600
1741 result = _ismintty(fd, NULL);
1747 /* This is used when running in terminfo mode to discover,
1748 whether or not the "terminal" is actually a Windows
1749 Console. It's the responsibilty of the console to deal
1750 with the terminal escape sequences that are sent by
1754 _nc_mingw_isconsole(int fd)
1756 HANDLE hdl = get_handle(fd);
1759 T((T_CALLED("win32con::_nc_mingw_isconsole(%d)"), fd));
1761 code = (int) IsConsoleHandle(hdl);
1766 #define TC_PROLOGUE(fd) \
1768 TERMINAL *term = 0; \
1770 if (_nc_screen_chain == 0) \
1772 for (each_screen(sp)) { \
1773 if (sp->_term && (sp->_term->Filedes == fd)) { \
1781 _nc_mingw_tcsetattr(
1783 int optional_action GCC_UNUSED,
1784 const struct termios *arg)
1788 if (_nc_mingw_isconsole(fd)) {
1790 HANDLE ofd = get_handle(fd);
1791 if (ofd != INVALID_HANDLE_VALUE) {
1793 if (arg->c_lflag & ICANON)
1794 dwFlag |= ENABLE_LINE_INPUT;
1796 dwFlag = dwFlag & (DWORD) (~ENABLE_LINE_INPUT);
1798 if (arg->c_lflag & ECHO)
1799 dwFlag = dwFlag | ENABLE_ECHO_INPUT;
1801 dwFlag = dwFlag & (DWORD) (~ENABLE_ECHO_INPUT);
1803 if (arg->c_iflag & BRKINT)
1804 dwFlag |= ENABLE_PROCESSED_INPUT;
1806 dwFlag = dwFlag & (DWORD) (~ENABLE_PROCESSED_INPUT);
1808 dwFlag |= ENABLE_MOUSE_INPUT;
1809 SetConsoleMode(ofd, dwFlag);
1820 _nc_mingw_tcgetattr(int fd, struct termios *arg)
1824 if (_nc_mingw_isconsole(fd)) {
1832 _nc_mingw_tcflush(int fd, int queue)
1837 if (_nc_mingw_isconsole(fd)) {
1838 if (queue == TCIFLUSH) {
1839 BOOL b = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
1841 return (int) GetLastError();
1848 _nc_mingw_testmouse(
1857 if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1860 rc = console_twait(sp,
1865 EVENTLIST_2nd(evl));
1871 _nc_mingw_console_read(
1877 INPUT_RECORD inp_rec;
1885 memset(&inp_rec, 0, sizeof(inp_rec));
1887 T((T_CALLED("_nc_mingw_console_read(%p)"), sp));
1889 while ((b = ReadConsoleInput(fd, &inp_rec, 1, &nRead))) {
1890 if (b && nRead > 0) {
1891 if (inp_rec.EventType == KEY_EVENT) {
1892 if (!inp_rec.Event.KeyEvent.bKeyDown)
1894 *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
1895 vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1897 if (sp->_keypad_on) {
1905 } else { /* *buf != 0 */
1908 } else if (inp_rec.EventType == MOUSE_EVENT) {
1909 if (handle_mouse(sp,
1910 inp_rec.Event.MouseEvent)) {
1922 __attribute__((constructor))
1923 void _enter_console(void)
1925 if (!console_initialized) {
1929 BOOL buffered = TRUE;
1933 if (_nc_mingw_isatty(0)) {
1934 CON.isMinTTY = TRUE;
1937 for (i = 0; i < (N_INI + FKEYS); i++) {
1939 CON.rmap[i] = CON.map[i] =
1942 CON.rmap[i] = CON.map[i] =
1943 (DWORD) GenMap((VK_F1 + (i - N_INI)),
1944 (KEY_F(1) + (i - N_INI)));
1955 if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
1956 CON.numButtons = (int) num_buttons;
1961 a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
1962 for (i = 0; i < NUMPAIRS; i++)
1965 CON.inp = GetStdHandle(STD_INPUT_HANDLE);
1966 CON.out = GetStdHandle(STD_OUTPUT_HANDLE);
1971 b = AttachConsole(ATTACH_PARENT_PROCESS);
1973 if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
1974 T(("... will not buffer console"));
1978 T(("... creating console buffer"));
1979 CON.hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
1982 CONSOLE_TEXTMODE_BUFFER,
1986 if (CON.hdl != INVALID_HANDLE_VALUE) {
1987 CON.buffered = buffered;
1990 save_original_screen();
1994 console_initialized = TRUE;