X-Git-Url: http://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=ncurses%2Fwin32con%2Fwin_driver.c;h=72a9cf53525f8a94d692384347f3b56a552df929;hp=443476460eb1fd23c20193000a248c76b8f4562b;hb=18f51bcf99101e5bf4c737239c44064227f34db8;hpb=c9c32236e27689a61c794b714c6b86c460866e35 diff --git a/ncurses/win32con/win_driver.c b/ncurses/win32con/win_driver.c index 44347646..72a9cf53 100644 --- a/ncurses/win32con/win_driver.c +++ b/ncurses/win32con/win_driver.c @@ -1,5 +1,6 @@ /**************************************************************************** - * Copyright (c) 1998-2013,2014 Free Software Foundation, Inc. * + * Copyright 2018,2020 Thomas E. Dickey * + * Copyright 2008-2016,2017 Free Software Foundation, Inc. * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * @@ -34,22 +35,29 @@ /* * TODO - GetMousePos(POINT * result) from ntconio.c * TODO - implement nodelay + * TODO - improve screen-repainting performance, using implied wraparound to reduce write's + * TODO - make it optional whether screen is restored or not when non-buffered */ #include + +#ifdef _WIN32 #include +#else +#include +#include +#endif + #include #define PSAPI_VERSION 2 #include -#define CUR my_term.type. +#define CUR TerminalType(my_term). -MODULE_ID("$Id: win_driver.c,v 1.38 2014/05/10 21:50:00 tom Exp $") +MODULE_ID("$Id: win_driver.c,v 1.66 2020/03/01 00:18:49 tom Exp $") -#ifndef __GNUC__ -# error We need GCC to compile for MinGW -#endif +#define TypeAlloca(type,count) (type*) _alloca(sizeof(type) * (size_t) (count)) #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE) @@ -57,7 +65,8 @@ MODULE_ID("$Id: win_driver.c,v 1.38 2014/05/10 21:50:00 tom Exp $") #define array_length(a) (sizeof(a)/sizeof(a[0])) -#define okConsoleHandle(TCB) (TCB != 0 && CON.hdl != INVALID_HANDLE_VALUE) +static bool InitConsole(void); +static bool okConsoleHandle(TERMINAL_CONTROL_BLOCK *); #define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC)) #define SetSP() assert(TCB->csp != 0); sp = TCB->csp; (void) sp @@ -66,6 +75,14 @@ MODULE_ID("$Id: win_driver.c,v 1.38 2014/05/10 21:50:00 tom Exp $") #define AdjustY() (CON.buffered ? 0 : (int) CON.SBI.srWindow.Top) +#if USE_WIDEC_SUPPORT +#define write_screen WriteConsoleOutputW +#define read_screen ReadConsoleOutputW +#else +#define write_screen WriteConsoleOutput +#define read_screen ReadConsoleOutput +#endif + static const LONG keylist[] = { GenMap(VK_PRIOR, KEY_PPAGE), @@ -79,14 +96,27 @@ static const LONG keylist[] = GenMap(VK_DELETE, KEY_DC), GenMap(VK_INSERT, KEY_IC) }; +static const LONG ansi_keys[] = +{ + GenMap(VK_PRIOR, 'I'), + GenMap(VK_NEXT, 'Q'), + GenMap(VK_END, 'O'), + GenMap(VK_HOME, 'H'), + GenMap(VK_LEFT, 'K'), + GenMap(VK_UP, 'H'), + GenMap(VK_RIGHT, 'M'), + GenMap(VK_DOWN, 'P'), + GenMap(VK_DELETE, 'S'), + GenMap(VK_INSERT, 'R') +}; #define N_INI ((int)array_length(keylist)) #define FKEYS 24 #define MAPSIZE (FKEYS + N_INI) #define NUMPAIRS 64 -/* A process can only have a single console, so it's save +/* A process can only have a single console, so it's safe to maintain all the information about it in a single - static scructure. + static structure. */ static struct { BOOL initialized; @@ -100,12 +130,17 @@ static struct { HANDLE hdl; HANDLE lastOut; int numButtons; + DWORD ansi_map[MAPSIZE]; DWORD map[MAPSIZE]; DWORD rmap[MAPSIZE]; WORD pairs[NUMPAIRS]; COORD origin; CHAR_INFO *save_screen; + COORD save_size; + SMALL_RECT save_region; CONSOLE_SCREEN_BUFFER_INFO SBI; + CONSOLE_SCREEN_BUFFER_INFO save_SBI; + CONSOLE_CURSOR_INFO save_CI; } CON; static BOOL console_initialized = FALSE; @@ -125,6 +160,11 @@ MapColor(bool fore, int color) return (WORD) a; } +#define RevAttr(attr) \ + (WORD) (((attr) & 0xff00) | \ + ((((attr) & 0x07) << 4) | \ + (((attr) & 0x70) >> 4))) + static WORD MapAttr(WORD res, attr_t ch) { @@ -140,16 +180,11 @@ MapAttr(WORD res, attr_t ch) } if (ch & A_REVERSE) { - res = (WORD) ((res & 0xff00) | - (((res & 0x07) << 4) | - ((res & 0x70) >> 4))); + res = RevAttr(res); } if (ch & A_STANDOUT) { - res = (WORD) ((res & 0xff00) | - (((res & 0x07) << 4) | - ((res & 0x70) >> 4)) | - BACKGROUND_INTENSITY); + res = RevAttr(res) | BACKGROUND_INTENSITY; } if (ch & A_BOLD) @@ -161,19 +196,71 @@ MapAttr(WORD res, attr_t ch) return res; } +#if 0 /* def TRACE */ +static void +dump_screen(const char *fn, int ln) +{ + int max_cells = (CON.SBI.dwSize.Y * (1 + CON.SBI.dwSize.X)) + 1; + char output[max_cells]; + CHAR_INFO save_screen[max_cells]; + COORD save_size; + SMALL_RECT save_region; + COORD bufferCoord; + + T(("dump_screen %s@%d", fn, ln)); + + save_region.Top = CON.SBI.srWindow.Top; + save_region.Left = CON.SBI.srWindow.Left; + save_region.Bottom = CON.SBI.srWindow.Bottom; + save_region.Right = CON.SBI.srWindow.Right; + + save_size.X = (SHORT) (save_region.Right - save_region.Left + 1); + save_size.Y = (SHORT) (save_region.Bottom - save_region.Top + 1); + + bufferCoord.X = bufferCoord.Y = 0; + + if (read_screen(CON.hdl, + save_screen, + save_size, + bufferCoord, + &save_region)) { + int i, j; + int ij = 0; + int k = 0; + + for (i = save_region.Top; i <= save_region.Bottom; ++i) { + for (j = save_region.Left; j <= save_region.Right; ++j) { + output[k++] = save_screen[ij++].Char.AsciiChar; + } + output[k++] = '\n'; + } + output[k] = 0; + + T(("DUMP: %d,%d - %d,%d", + save_region.Top, + save_region.Left, + save_region.Bottom, + save_region.Right)); + T(("%s", output)); + } +} + +#else +#define dump_screen(fn,ln) /* nothing */ +#endif + #if USE_WIDEC_SUPPORT /* * TODO: support surrogate pairs * TODO: support combining characters * TODO: support acsc - * TODO: check wcwidth of base character, fill if needed for double-width * TODO: _nc_wacs should be part of sp. */ static BOOL con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit) { int actual = 0; - CHAR_INFO ci[limit]; + CHAR_INFO *ci = TypeAlloca(CHAR_INFO, limit); COORD loc, siz; SMALL_RECT rec; int i; @@ -205,24 +292,24 @@ con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit) ++actual; } - loc.X = (short) 0; - loc.Y = (short) 0; - siz.X = (short) actual; + loc.X = (SHORT) 0; + loc.Y = (SHORT) 0; + siz.X = (SHORT) actual; siz.Y = 1; - rec.Left = (short) x; + rec.Left = (SHORT) x; rec.Top = (SHORT) (y + AdjustY()); - rec.Right = (short) (x + limit - 1); + rec.Right = (SHORT) (x + limit - 1); rec.Bottom = rec.Top; - return WriteConsoleOutputW(CON.hdl, ci, siz, loc, &rec); + return write_screen(CON.hdl, ci, siz, loc, &rec); } #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n) #else static BOOL con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n) { - CHAR_INFO ci[n]; + CHAR_INFO *ci = TypeAlloca(CHAR_INFO, n); COORD loc, siz; SMALL_RECT rec; int i; @@ -254,7 +341,7 @@ con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n) rec.Right = (short) (x + n - 1); rec.Bottom = rec.Top; - return WriteConsoleOutput(CON.hdl, ci, siz, loc, &rec); + return write_screen(CON.hdl, ci, siz, loc, &rec); } #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n) #endif @@ -351,34 +438,31 @@ static bool restore_original_screen(void) { COORD bufferCoord; - SMALL_RECT writeRegion; bool result = FALSE; + SMALL_RECT save_region = CON.save_region; - if (CON.window_only) { - writeRegion.Top = CON.SBI.srWindow.Top; - writeRegion.Left = CON.SBI.srWindow.Left; - writeRegion.Bottom = CON.SBI.srWindow.Bottom; - writeRegion.Right = CON.SBI.srWindow.Right; - T(("... restoring window")); - } else { - writeRegion.Top = 0; - writeRegion.Left = 0; - writeRegion.Bottom = (SHORT) (CON.SBI.dwSize.Y - 1); - writeRegion.Right = (SHORT) (CON.SBI.dwSize.X - 1); - T(("... restoring entire buffer")); - } + T(("... restoring %s", CON.window_only ? "window" : "entire buffer")); - bufferCoord.X = bufferCoord.Y = 0; + bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0); + bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0); - if (WriteConsoleOutput(CON.hdl, - CON.save_screen, - CON.SBI.dwSize, - bufferCoord, - &writeRegion)) { + if (write_screen(CON.hdl, + CON.save_screen, + CON.save_size, + bufferCoord, + &save_region)) { result = TRUE; mvcur(-1, -1, LINES - 2, 0); + T(("... restore original screen contents ok %dx%d (%d,%d - %d,%d)", + CON.save_size.Y, + CON.save_size.X, + save_region.Top, + save_region.Left, + save_region.Bottom, + save_region.Right)); + } else { + T(("... restore original screen contents err")); } - T(("... restore original screen contents %s", result ? "ok" : "err")); return result; } @@ -396,20 +480,35 @@ wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB) int y, nonempty, n, x0, x1, Width, Height; SCREEN *sp; - AssertTCB(); - SetSP(); - T((T_CALLED("win32con::wcon_doupdate(%p)"), TCB)); if (okConsoleHandle(TCB)) { + SetSP(); Width = screen_columns(sp); Height = screen_lines(sp); nonempty = min(Height, NewScreen(sp)->_maxy + 1); + T(("... %dx%d clear cur:%d new:%d", + Height, Width, + CurScreen(sp)->_clear, + NewScreen(sp)->_clear)); + + if (SP_PARM->_endwin == ewSuspend) { + + T(("coming back from shell mode")); + NCURSES_SP_NAME(reset_prog_mode) (NCURSES_SP_ARG); + + NCURSES_SP_NAME(_nc_mvcur_resume) (NCURSES_SP_ARG); + NCURSES_SP_NAME(_nc_screen_resume) (NCURSES_SP_ARG); + SP_PARM->_mouse_resume(SP_PARM); + + SP_PARM->_endwin = ewRunning; + } + if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) { int x; #if USE_WIDEC_SUPPORT - cchar_t empty[Width]; + cchar_t *empty = TypeAlloca(cchar_t, Width); wchar_t blank[2] = { L' ', L'\0' @@ -418,7 +517,7 @@ wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB) for (x = 0; x < Width; x++) setcchar(&empty[x], blank, 0, 0, 0); #else - chtype empty[Width]; + chtype *empty = TypeAlloca(chtype, Width); for (x = 0; x < Width; x++) empty[x] = ' '; @@ -433,6 +532,8 @@ wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB) CurScreen(sp)->_clear = FALSE; NewScreen(sp)->_clear = FALSE; touchwin(NewScreen(sp)); + T(("... cleared %dx%d lines @%d of screen", nonempty, Width, + AdjustY())); } for (y = 0; y < nonempty; y++) { @@ -546,8 +647,11 @@ wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB, * This is intentional, to avoid unnecessary breakage of applications * using symbols. */ - if (code && (TCB->term.type.Booleans == 0)) { - _nc_init_termtype(&(TCB->term.type)); + if (code && (TerminalType(&TCB->term).Booleans == 0)) { + _nc_init_termtype(&TerminalType(&TCB->term)); +#if NCURSES_EXT_NUMBERS + _nc_export_termtype2(&TCB->term.type, &TerminalType(&TCB->term)); +#endif } if (!code) { @@ -559,14 +663,59 @@ wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB, static int wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB, - int beepFlag GCC_UNUSED) + int beepFlag) { SCREEN *sp; int res = ERR; - AssertTCB(); - SetSP(); + int high = (CON.SBI.srWindow.Bottom - CON.SBI.srWindow.Top + 1); + int wide = (CON.SBI.srWindow.Right - CON.SBI.srWindow.Left + 1); + int max_cells = (high * wide); + int i; + + CHAR_INFO *this_screen = TypeAlloca(CHAR_INFO, max_cells); + CHAR_INFO *that_screen = TypeAlloca(CHAR_INFO, max_cells); + COORD this_size; + SMALL_RECT this_region; + COORD bufferCoord; + + if (okConsoleHandle(TCB)) { + SetSP(); + this_region.Top = CON.SBI.srWindow.Top; + this_region.Left = CON.SBI.srWindow.Left; + this_region.Bottom = CON.SBI.srWindow.Bottom; + this_region.Right = CON.SBI.srWindow.Right; + + this_size.X = (SHORT) wide; + this_size.Y = (SHORT) high; + + bufferCoord.X = this_region.Left; + bufferCoord.Y = this_region.Top; + + if (!beepFlag && + read_screen(CON.hdl, + this_screen, + this_size, + bufferCoord, + &this_region)) { + + memcpy(that_screen, + this_screen, + sizeof(CHAR_INFO) * (size_t) max_cells); + + for (i = 0; i < max_cells; i++) { + that_screen[i].Attributes = RevAttr(that_screen[i].Attributes); + } + + write_screen(CON.hdl, that_screen, this_size, bufferCoord, &this_region); + Sleep(200); + write_screen(CON.hdl, this_screen, this_size, bufferCoord, &this_region); + } else { + MessageBeep(MB_ICONWARNING); /* MB_OK might be better */ + } + res = OK; + } return res; } @@ -637,8 +786,6 @@ wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB, int color, int (*outc) (SCREEN *, int) GCC_UNUSED) { - AssertTCB(); - if (okConsoleHandle(TCB)) { WORD a = MapColor(fore, color); a |= (WORD) ((CON.SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f)); @@ -652,7 +799,6 @@ wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB) { bool res = FALSE; - AssertTCB(); if (okConsoleHandle(TCB)) { WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN; SetConsoleTextAttribute(CON.hdl, a); @@ -679,8 +825,6 @@ wcon_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols) { int result = ERR; - AssertTCB(); - T((T_CALLED("win32con::wcon_size(%p)"), TCB)); if (okConsoleHandle(TCB) && @@ -715,65 +859,143 @@ wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf) DWORD dwFlag = 0; tcflag_t iflag; tcflag_t lflag; + int result = ERR; - AssertTCB(); + if (buf != NULL && okConsoleHandle(TCB)) { - if (TCB == 0 || buf == NULL) - return ERR; + if (setFlag) { + iflag = buf->c_iflag; + lflag = buf->c_lflag; - if (setFlag) { - iflag = buf->c_iflag; - lflag = buf->c_lflag; + GetConsoleMode(CON.inp, &dwFlag); - GetConsoleMode(CON.inp, &dwFlag); + if (lflag & ICANON) + dwFlag |= ENABLE_LINE_INPUT; + else + dwFlag &= (DWORD) (~ENABLE_LINE_INPUT); - if (lflag & ICANON) - dwFlag |= ENABLE_LINE_INPUT; - else - dwFlag &= (DWORD) (~ENABLE_LINE_INPUT); + if (lflag & ECHO) + dwFlag |= ENABLE_ECHO_INPUT; + else + dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT); - if (lflag & ECHO) - dwFlag |= ENABLE_ECHO_INPUT; - else - dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT); + if (iflag & BRKINT) + dwFlag |= ENABLE_PROCESSED_INPUT; + else + dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT); - if (iflag & BRKINT) - dwFlag |= ENABLE_PROCESSED_INPUT; - else - dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT); + dwFlag |= ENABLE_MOUSE_INPUT; - dwFlag |= ENABLE_MOUSE_INPUT; + buf->c_iflag = iflag; + buf->c_lflag = lflag; + SetConsoleMode(CON.inp, dwFlag); + TCB->term.Nttyb = *buf; + } else { + iflag = TCB->term.Nttyb.c_iflag; + lflag = TCB->term.Nttyb.c_lflag; + GetConsoleMode(CON.inp, &dwFlag); - buf->c_iflag = iflag; - buf->c_lflag = lflag; - SetConsoleMode(CON.inp, dwFlag); - TCB->term.Nttyb = *buf; + if (dwFlag & ENABLE_LINE_INPUT) + lflag |= ICANON; + else + lflag &= (tcflag_t) (~ICANON); + + if (dwFlag & ENABLE_ECHO_INPUT) + lflag |= ECHO; + else + lflag &= (tcflag_t) (~ECHO); + + if (dwFlag & ENABLE_PROCESSED_INPUT) + iflag |= BRKINT; + else + iflag &= (tcflag_t) (~BRKINT); + + TCB->term.Nttyb.c_iflag = iflag; + TCB->term.Nttyb.c_lflag = lflag; + + *buf = TCB->term.Nttyb; + } + result = OK; + } + return result; +} + +#define MIN_WIDE 80 +#define MIN_HIGH 24 + +/* + * In "normal" mode, reset the buffer- and window-sizes back to their original values. + */ +static void +set_scrollback(bool normal, CONSOLE_SCREEN_BUFFER_INFO * info) +{ + SMALL_RECT rect; + COORD coord; + bool changed = FALSE; + + T((T_CALLED("win32con::set_scrollback(%s)"), + (normal + ? "normal" + : "application"))); + + T(("... SBI.srWindow %d,%d .. %d,%d", + info->srWindow.Top, + info->srWindow.Left, + info->srWindow.Bottom, + info->srWindow.Right)); + T(("... SBI.dwSize %dx%d", + info->dwSize.Y, + info->dwSize.X)); + + if (normal) { + rect = info->srWindow; + coord = info->dwSize; + if (memcmp(info, &CON.SBI, sizeof(*info)) != 0) { + changed = TRUE; + CON.SBI = *info; + } } else { - iflag = TCB->term.Nttyb.c_iflag; - lflag = TCB->term.Nttyb.c_lflag; - GetConsoleMode(CON.inp, &dwFlag); + int high = info->srWindow.Bottom - info->srWindow.Top + 1; + int wide = info->srWindow.Right - info->srWindow.Left + 1; - if (dwFlag & ENABLE_LINE_INPUT) - lflag |= ICANON; - else - lflag &= (tcflag_t) (~ICANON); + if (high < MIN_HIGH) { + T(("... height %d < %d", high, MIN_HIGH)); + high = MIN_HIGH; + changed = TRUE; + } + if (wide < MIN_WIDE) { + T(("... width %d < %d", wide, MIN_WIDE)); + wide = MIN_WIDE; + changed = TRUE; + } - if (dwFlag & ENABLE_ECHO_INPUT) - lflag |= ECHO; - else - lflag &= (tcflag_t) (~ECHO); + rect.Left = + rect.Top = 0; + rect.Right = (SHORT) (wide - 1); + rect.Bottom = (SHORT) (high - 1); - if (dwFlag & ENABLE_PROCESSED_INPUT) - iflag |= BRKINT; - else - iflag &= (tcflag_t) (~BRKINT); + coord.X = (SHORT) wide; + coord.Y = (SHORT) high; - TCB->term.Nttyb.c_iflag = iflag; - TCB->term.Nttyb.c_lflag = lflag; + if (info->dwSize.Y != high || + info->dwSize.X != wide || + info->srWindow.Top != 0 || + info->srWindow.Left != 0) { + changed = TRUE; + } - *buf = TCB->term.Nttyb; } - return OK; + + if (changed) { + T(("... coord %d,%d", coord.Y, coord.X)); + T(("... rect %d,%d - %d,%d", + rect.Top, rect.Left, + rect.Bottom, rect.Right)); + SetConsoleScreenBufferSize(CON.hdl, coord); /* dwSize */ + SetConsoleWindowInfo(CON.hdl, TRUE, &rect); /* srWindow */ + get_SBI(); + } + returnVoid; } static int @@ -783,51 +1005,60 @@ wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag) TERMINAL *_term = (TERMINAL *) TCB; int code = ERR; - AssertTCB(); - sp = TCB->csp; - - T((T_CALLED("win32con::wcon_mode(%p, prog=%d, def=%d)"), TCB, progFlag, defFlag)); - CON.progMode = progFlag; - CON.lastOut = progFlag ? CON.hdl : CON.out; - SetConsoleActiveScreenBuffer(CON.lastOut); - - if (progFlag) /* prog mode */ { - if (defFlag) { - if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) { - _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS); - code = OK; + if (okConsoleHandle(TCB)) { + sp = TCB->csp; + + T((T_CALLED("win32con::wcon_mode(%p, prog=%d, def=%d)"), + TCB, progFlag, defFlag)); + + CON.progMode = progFlag; + CON.lastOut = progFlag ? CON.hdl : CON.out; + SetConsoleActiveScreenBuffer(CON.lastOut); + + if (progFlag) /* prog mode */ { + if (defFlag) { + if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) { + _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS); + code = OK; + } + } else { + /* reset_prog_mode */ + if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) { + if (sp) { + if (sp->_keypad_on) + _nc_keypad(sp, TRUE); + } + if (!CON.buffered) { + set_scrollback(FALSE, &CON.SBI); + } + code = OK; + } } - } else { - /* reset_prog_mode */ - if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) { + T(("... buffered:%d, clear:%d", CON.buffered, CurScreen(sp)->_clear)); + } else { /* shell mode */ + if (defFlag) { + /* def_shell_mode */ + if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) { + code = OK; + } + } else { + /* reset_shell_mode */ if (sp) { - if (sp->_keypad_on) - _nc_keypad(sp, TRUE); + _nc_keypad(sp, FALSE); + NCURSES_SP_NAME(_nc_flush) (sp); } - code = OK; - } - } - } else { /* shell mode */ - if (defFlag) { - /* def_shell_mode */ - if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) { - code = OK; - } - } else { - /* reset_shell_mode */ - if (sp) { - _nc_keypad(sp, FALSE); - NCURSES_SP_NAME(_nc_flush) (sp); - } - code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb)); - if (!CON.buffered) { - if (!restore_original_screen()) - code = ERR; + code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb)); + if (!CON.buffered) { + set_scrollback(TRUE, &CON.save_SBI); + if (!restore_original_screen()) + code = ERR; + } + SetConsoleCursorInfo(CON.hdl, &CON.save_CI); } } - } - return (code); + } + returnCode(code); } static void @@ -881,6 +1112,29 @@ MapKey(WORD vKey) return code; } +static int +AnsiKey(WORD vKey) +{ + WORD nKey = 0; + void *res; + LONG key = GenMap(vKey, 0); + int code = -1; + + res = bsearch(&key, + CON.ansi_map, + (size_t) (N_INI + FKEYS), + sizeof(keylist[0]), + keycompare); + if (res) { + key = *((LONG *) res); + nKey = LOWORD(key); + code = (int) (nKey & 0x7fff); + if (nKey & 0x8000) + code = -code; + } + return code; +} + static void wcon_release(TERMINAL_CONTROL_BLOCK * TCB) { @@ -893,85 +1147,79 @@ wcon_release(TERMINAL_CONTROL_BLOCK * TCB) returnVoid; } -/* - * Attempt to save the screen contents. PDCurses does this if - * PDC_RESTORE_SCREEN is set, giving the same visual appearance on restoration - * as if the library had allocated a console buffer. - */ static bool -save_original_screen(void) +read_screen_data(void) { bool result = FALSE; - COORD bufferSize; COORD bufferCoord; - SMALL_RECT readRegion; size_t want; - bufferSize.X = CON.SBI.dwSize.X; - bufferSize.Y = CON.SBI.dwSize.Y; - want = (size_t) (bufferSize.X * bufferSize.Y); + CON.save_size.X = (SHORT) (CON.save_region.Right + - CON.save_region.Left + 1); + CON.save_size.Y = (SHORT) (CON.save_region.Bottom + - CON.save_region.Top + 1); + + want = (size_t) (CON.save_size.X * CON.save_size.Y); if ((CON.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) { - bufferCoord.X = bufferCoord.Y = 0; - - readRegion.Top = 0; - readRegion.Left = 0; - readRegion.Bottom = (SHORT) (bufferSize.Y - 1); - readRegion.Right = (SHORT) (bufferSize.X - 1); - - T(("... reading console buffer %dx%d into %d,%d - %d,%d at %d,%d", - bufferSize.Y, bufferSize.X, - readRegion.Top, - readRegion.Left, - readRegion.Bottom, - readRegion.Right, + bufferCoord.X = (SHORT) (CON.window_only ? CON.SBI.srWindow.Left : 0); + bufferCoord.Y = (SHORT) (CON.window_only ? CON.SBI.srWindow.Top : 0); + + T(("... reading console %s %dx%d into %d,%d - %d,%d at %d,%d", + CON.window_only ? "window" : "buffer", + CON.save_size.Y, CON.save_size.X, + CON.save_region.Top, + CON.save_region.Left, + CON.save_region.Bottom, + CON.save_region.Right, bufferCoord.Y, bufferCoord.X)); - if (ReadConsoleOutput(CON.hdl, - CON.save_screen, - bufferSize, - bufferCoord, - &readRegion)) { + if (read_screen(CON.hdl, + CON.save_screen, + CON.save_size, + bufferCoord, + &CON.save_region)) { result = TRUE; } else { T((" error %#lx", (unsigned long) GetLastError())); FreeAndNull(CON.save_screen); + } + } - bufferSize.X = (SHORT) (CON.SBI.srWindow.Right - - CON.SBI.srWindow.Left + 1); - bufferSize.Y = (SHORT) (CON.SBI.srWindow.Bottom - - CON.SBI.srWindow.Top + 1); - want = (size_t) (bufferSize.X * bufferSize.Y); - - if ((CON.save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) { - bufferCoord.X = bufferCoord.Y = 0; - - readRegion.Top = CON.SBI.srWindow.Top; - readRegion.Left = CON.SBI.srWindow.Left; - readRegion.Bottom = CON.SBI.srWindow.Bottom; - readRegion.Right = CON.SBI.srWindow.Right; - - T(("... reading console window %dx%d into %d,%d - %d,%d at %d,%d", - bufferSize.Y, bufferSize.X, - readRegion.Top, - readRegion.Left, - readRegion.Bottom, - readRegion.Right, - bufferCoord.Y, - bufferCoord.X)); - - if (ReadConsoleOutput(CON.hdl, - CON.save_screen, - bufferSize, - bufferCoord, - &readRegion)) { - result = TRUE; - CON.window_only = TRUE; - } else { - T((" error %#lx", (unsigned long) GetLastError())); - } - } + return result; +} + +/* + * Attempt to save the screen contents. PDCurses does this if + * PDC_RESTORE_SCREEN is set, giving the same visual appearance on + * restoration as if the library had allocated a console buffer. MSDN + * says that the data which can be read is limited to 64Kb (and may be + * less). + */ +static bool +save_original_screen(void) +{ + bool result = FALSE; + + CON.save_region.Top = 0; + CON.save_region.Left = 0; + CON.save_region.Bottom = (SHORT) (CON.SBI.dwSize.Y - 1); + CON.save_region.Right = (SHORT) (CON.SBI.dwSize.X - 1); + + if (read_screen_data()) { + result = TRUE; + } else { + + CON.save_region.Top = CON.SBI.srWindow.Top; + CON.save_region.Left = CON.SBI.srWindow.Left; + CON.save_region.Bottom = CON.SBI.srWindow.Bottom; + CON.save_region.Right = CON.SBI.srWindow.Right; + + CON.window_only = TRUE; + + if (read_screen_data()) { + result = TRUE; } } @@ -987,7 +1235,7 @@ wcon_init(TERMINAL_CONTROL_BLOCK * TCB) AssertTCB(); if (TCB) { - if (CON.hdl == INVALID_HANDLE_VALUE) { + if (!InitConsole()) { returnVoid; } @@ -1019,12 +1267,13 @@ wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB, { SCREEN *sp; - AssertTCB(); - SetSP(); + if (okConsoleHandle(TCB)) { + SetSP(); - if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8) - && (b >= 0) && (b < 8)) { - CON.pairs[pair] = MapColor(true, f) | MapColor(false, b); + if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8) + && (b >= 0) && (b < 8)) { + CON.pairs[pair] = MapColor(true, f) | MapColor(false, b); + } } } @@ -1060,29 +1309,33 @@ wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB) { SCREEN *sp; - AssertTCB(); - SetSP(); + if (okConsoleHandle(TCB)) { + SetSP(); - sp->_mouse_type = M_TERM_DRIVER; + sp->_mouse_type = M_TERM_DRIVER; + } } static int -wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB, int delay) +wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB, + int delay + EVENTLIST_2nd(_nc_eventlist * evl)) { int rc = 0; SCREEN *sp; - AssertTCB(); - SetSP(); + if (okConsoleHandle(TCB)) { + SetSP(); - if (sp->_drv_mouse_head < sp->_drv_mouse_tail) { - rc = TW_MOUSE; - } else { - rc = TCBOf(sp)->drv->td_twait(TCBOf(sp), - TWAIT_MASK, - delay, - (int *) 0 - EVENTLIST_2nd(evl)); + if (sp->_drv_mouse_head < sp->_drv_mouse_tail) { + rc = TW_MOUSE; + } else { + rc = TCBOf(sp)->drv->td_twait(TCBOf(sp), + TWAIT_MASK, + delay, + (int *) 0 + EVENTLIST_2nd(evl)); + } } return rc; @@ -1179,13 +1432,14 @@ wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB, unsigned n; SCREEN *sp; - AssertTCB(); - SetSP(); + if (okConsoleHandle(TCB)) { + SetSP(); - for (n = 0; n < SIZEOF(table); ++n) { - real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET; - if (sp != 0) - sp->_screen_acs_map[table[n].acs_code] = TRUE; + for (n = 0; n < SIZEOF(table); ++n) { + real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET; + if (sp != 0) + sp->_screen_acs_map[table[n].acs_code] = TRUE; + } } } @@ -1208,11 +1462,11 @@ tdiff(FILETIME fstart, FILETIME fend) static int Adjust(int milliseconds, int diff) { - if (milliseconds == INFINITY) - return milliseconds; - milliseconds -= diff; - if (milliseconds < 0) - milliseconds = 0; + if (milliseconds != INFINITY) { + milliseconds -= diff; + if (milliseconds < 0) + milliseconds = 0; + } return milliseconds; } @@ -1277,6 +1531,10 @@ console_twait( int diff; bool isImmed = (milliseconds == 0); +#ifdef NCURSES_WGETCH_EVENTS + (void) evl; /* TODO: implement wgetch-events */ +#endif + #define CONSUME() ReadConsoleInput(fd,&inp_rec,1,&nRead) assert(sp); @@ -1296,7 +1554,7 @@ console_twait( diff = (int) tdiff(fstart, fend); milliseconds = Adjust(milliseconds, diff); - if (!isImmed && milliseconds == 0) + if (!isImmed && milliseconds <= 0) break; if (rc == WAIT_OBJECT_0) { @@ -1314,7 +1572,7 @@ console_twait( if (inp_rec.Event.KeyEvent.bKeyDown) { if (0 == ch) { int nKey = MapKey(vk); - if ((nKey < 0) || FALSE == sp->_keypad_on) { + if (nKey < 0) { CONSUME(); continue; } @@ -1336,7 +1594,9 @@ console_twait( goto end; } continue; + /* e.g., FOCUS_EVENT */ default: + CONSUME(); selectActiveHandle(); continue; } @@ -1373,16 +1633,17 @@ wcon_twait(TERMINAL_CONTROL_BLOCK * TCB, EVENTLIST_2nd(_nc_eventlist * evl)) { SCREEN *sp; - int code; + int code = 0; - AssertTCB(); - SetSP(); + if (okConsoleHandle(TCB)) { + SetSP(); - code = console_twait(sp, - CON.inp, - mode, - milliseconds, - timeleft EVENTLIST_2nd(_nc_eventlist * evl)); + code = console_twait(sp, + CON.inp, + mode, + milliseconds, + timeleft EVENTLIST_2nd(evl)); + } return code; } @@ -1433,14 +1694,16 @@ static int wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf) { SCREEN *sp; - int n; + int n = -1; + + T((T_CALLED("win32con::wcon_read(%p)"), TCB)); - AssertTCB(); assert(buf); - SetSP(); + if (okConsoleHandle(TCB)) { + SetSP(); - T((T_CALLED("win32con::wcon_read(%p)"), TCB)); - n = _nc_mingw_console_read(sp, CON.inp, buf); + n = _nc_mingw_console_read(sp, CON.inp, buf); + } returnCode(n); } @@ -1452,6 +1715,29 @@ wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms) returnCode(OK); } +static int +wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int mode) +{ + int res = -1; + + T((T_CALLED("win32con:wcon_cursorSet(%d)"), mode)); + if (okConsoleHandle(TCB)) { + CONSOLE_CURSOR_INFO this_CI = CON.save_CI; + switch (mode) { + case 0: + this_CI.bVisible = FALSE; + break; + case 1: + break; + case 2: + this_CI.dwSize = 100; + break; + } + SetConsoleCursorInfo(CON.hdl, &this_CI); + } + returnCode(res); +} + static bool wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int keycode) { @@ -1481,12 +1767,14 @@ wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED) SCREEN *sp; int code = ERR; - AssertTCB(); - SetSP(); - T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag)); - if (sp) { - code = OK; + + if (okConsoleHandle(TCB)) { + SetSP(); + + if (sp) { + code = OK; + } } returnCode(code); } @@ -1505,22 +1793,23 @@ wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB, T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag)); - AssertTCB(); - SetSP(); - - if (sp) { - res = bsearch(&key, - CON.rmap, - (size_t) (N_INI + FKEYS), - sizeof(keylist[0]), - rkeycompare); - if (res) { - key = *((LONG *) res); - vKey = HIWORD(key); - nKey = (LOWORD(key)) & 0x7fff; - if (!flag) - nKey |= 0x8000; - *(LONG *) res = GenMap(vKey, nKey); + if (okConsoleHandle(TCB)) { + SetSP(); + + if (sp) { + res = bsearch(&key, + CON.rmap, + (size_t) (N_INI + FKEYS), + sizeof(keylist[0]), + rkeycompare); + if (res) { + key = *((LONG *) res); + vKey = HIWORD(key); + nKey = (LOWORD(key)) & 0x7fff; + if (!flag) + nKey |= 0x8000; + *(LONG *) res = GenMap(vKey, nKey); + } } } returnCode(code); @@ -1562,7 +1851,8 @@ NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = { wcon_nap, /* nap */ wcon_kpad, /* kpad */ wcon_keyok, /* kyOk */ - wcon_kyExist /* kyExist */ + wcon_kyExist, /* kyExist */ + wcon_cursorSet /* cursorSet */ }; /* --------------------------------------------------------- */ @@ -1641,17 +1931,21 @@ _ismintty(int fd, LPHANDLE pMinTTY) #endif /* Borrowed from ansicon project. - Check wether or not a I/O handle is associated with + Check whether or not an I/O handle is associated with a Windows console. */ static BOOL IsConsoleHandle(HANDLE hdl) { DWORD dwFlag = 0; + BOOL result; + if (!GetConsoleMode(hdl, &dwFlag)) { - return (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL); + result = (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL); + } else { + result = (int) (dwFlag & ENABLE_PROCESSED_OUTPUT); } - return (int) (dwFlag & ENABLE_PROCESSED_OUTPUT); + return result; } /* Our replacement for the systems _isatty to include also @@ -1661,18 +1955,26 @@ IsConsoleHandle(HANDLE hdl) int _nc_mingw_isatty(int fd) { - if (_isatty(fd)) - return 1; -#if WINVER < 0x0600 - return 0; + int result = 0; + +#ifdef __MING32__ +#define SysISATTY(fd) _isatty(fd) #else - return _ismintty(fd, NULL); +#define SysISATTY(fd) isatty(fd) +#endif + if (SysISATTY(fd)) { + result = 1; + } else { +#if WINVER >= 0x0600 + result = _ismintty(fd, NULL); #endif + } + return result; } /* This is used when running in terminfo mode to discover, whether or not the "terminal" is actually a Windows - Console. It's the responsibilty of the console to deal + Console. It's the responsibility of the console to deal with the terminal escape sequences that are sent by terminfo. */ @@ -1693,15 +1995,15 @@ _nc_mingw_isconsole(int fd) SCREEN *sp; \ TERMINAL *term = 0; \ int code = ERR; \ - if (_nc_screen_chain==0) \ - return 0; \ + if (_nc_screen_chain == 0) \ + return 0; \ for (each_screen(sp)) { \ - if (sp->_term && sp->_term->Filedes==fd) { \ - term = sp->_term; \ - break; \ + if (sp->_term && (sp->_term->Filedes == fd)) { \ + term = sp->_term; \ + break; \ } \ } \ - assert(term!=0) + assert(term != 0) int _nc_mingw_tcsetattr( @@ -1774,7 +2076,8 @@ int _nc_mingw_testmouse( SCREEN *sp, HANDLE fd, - int delay) + int delay + EVENTLIST_2nd(_nc_eventlist * evl)) { int rc = 0; @@ -1799,7 +2102,7 @@ _nc_mingw_console_read( HANDLE fd, int *buf) { - int n = 1; + int rc = -1; INPUT_RECORD inp_rec; BOOL b; DWORD nRead; @@ -1814,23 +2117,36 @@ _nc_mingw_console_read( while ((b = ReadConsoleInput(fd, &inp_rec, 1, &nRead))) { if (b && nRead > 0) { + if (rc < 0) + rc = 0; + rc = rc + (int) nRead; if (inp_rec.EventType == KEY_EVENT) { if (!inp_rec.Event.KeyEvent.bKeyDown) continue; *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar; vk = inp_rec.Event.KeyEvent.wVirtualKeyCode; + /* + * There are 24 virtual function-keys, and typically + * 12 function-keys on a keyboard. Use the shift-modifier + * to provide the remaining 12 keys. + */ + if (vk >= VK_F1 && vk <= VK_F12) { + if (inp_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED) { + vk = (WORD) (vk + 12); + } + } if (*buf == 0) { - if (sp->_keypad_on) { - *buf = MapKey(vk); - if (0 > (*buf)) - continue; - else - break; - } else + int key = MapKey(vk); + if (key < 0) continue; - } else { /* *buf != 0 */ - break; + if (sp->_keypad_on) { + *buf = key; + } else { + ungetch('\0'); + *buf = AnsiKey(vk); + } } + break; } else if (inp_rec.EventType == MOUSE_EVENT) { if (handle_mouse(sp, inp_rec.Event.MouseEvent)) { @@ -1841,13 +2157,13 @@ _nc_mingw_console_read( continue; } } - returnCode(n); + returnCode(rc); } -static -__attribute__((constructor)) - void _enter_console(void) +static bool +InitConsole(void) { + /* initialize once, or not at all */ if (!console_initialized) { int i; DWORD num_buttons; @@ -1855,19 +2171,29 @@ __attribute__((constructor)) BOOL buffered = TRUE; BOOL b; + START_TRACE(); if (_nc_mingw_isatty(0)) { CON.isMinTTY = TRUE; } for (i = 0; i < (N_INI + FKEYS); i++) { - if (i < N_INI) + if (i < N_INI) { CON.rmap[i] = CON.map[i] = (DWORD) keylist[i]; - else + CON.ansi_map[i] = (DWORD) ansi_keys[i]; + } else { CON.rmap[i] = CON.map[i] = (DWORD) GenMap((VK_F1 + (i - N_INI)), (KEY_F(1) + (i - N_INI))); + CON.ansi_map[i] = + (DWORD) GenMap((VK_F1 + (i - N_INI)), + (';' + (i - N_INI))); + } } + qsort(CON.ansi_map, + (size_t) (MAPSIZE), + sizeof(keylist[0]), + keycompare); qsort(CON.map, (size_t) (MAPSIZE), sizeof(keylist[0]), @@ -1896,9 +2222,11 @@ __attribute__((constructor)) b = AttachConsole(ATTACH_PARENT_PROCESS); if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) { - CON.hdl = CON.out; + T(("... will not buffer console")); buffered = FALSE; + CON.hdl = CON.out; } else { + T(("... creating console buffer")); CON.hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, @@ -1909,11 +2237,39 @@ __attribute__((constructor)) if (CON.hdl != INVALID_HANDLE_VALUE) { CON.buffered = buffered; get_SBI(); - if (buffered) { + CON.save_SBI = CON.SBI; + if (!buffered) { save_original_screen(); + set_scrollback(FALSE, &CON.SBI); } + GetConsoleCursorInfo(CON.hdl, &CON.save_CI); + T(("... initial cursor is %svisible, %d%%", + (CON.save_CI.bVisible ? "" : "not-"), + (int) CON.save_CI.dwSize)); } console_initialized = TRUE; } + return (CON.hdl != INVALID_HANDLE_VALUE); } + +static bool +okConsoleHandle(TERMINAL_CONTROL_BLOCK * TCB) +{ + return ((TCB != 0) && + (TCB->magic == WINMAGIC) && + InitConsole()); +} + +/* + * While a constructor would ensure that this module is initialized, that will + * interfere with applications that may combine this with GUI interfaces. + */ +#if 0 +static +__attribute__((constructor)) + void _enter_console(void) +{ + (void) InitConsole(); +} +#endif