/****************************************************************************
- * Copyright (c) 1998-2013,2014 Free Software Foundation, Inc. *
+ * Copyright 2018-2020,2021 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 *
/*
* 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 <curses.priv.h>
+
+#ifdef _WIN32
#include <tchar.h>
+#else
+#include <windows.h>
+#include <wchar.h>
+#endif
+
#include <io.h>
#define PSAPI_VERSION 2
#include <psapi.h>
-#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.67 2021/09/04 10:54:35 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)
#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
#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),
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 is safe
to maintain all the information about it in a single
- static scructure.
+ static structure.
*/
static struct {
BOOL initialized;
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;
return (WORD) a;
}
+#define RevAttr(attr) \
+ (WORD) (((attr) & 0xff00) | \
+ ((((attr) & 0x07) << 4) | \
+ (((attr) & 0x70) >> 4)))
+
static WORD
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)
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;
++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;
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
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;
}
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'
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] = ' ';
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++) {
* This is intentional, to avoid unnecessary breakage of applications
* using <term.h> 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) {
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;
}
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));
{
bool res = FALSE;
- AssertTCB();
if (okConsoleHandle(TCB)) {
WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
SetConsoleTextAttribute(CON.hdl, a);
{
int result = ERR;
- AssertTCB();
-
T((T_CALLED("win32con::wcon_size(%p)"), TCB));
if (okConsoleHandle(TCB) &&
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
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
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)
{
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;
}
}
AssertTCB();
if (TCB) {
- if (CON.hdl == INVALID_HANDLE_VALUE) {
+ if (!InitConsole()) {
returnVoid;
}
{
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);
+ }
}
}
{
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;
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;
+ }
}
}
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;
}
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);
diff = (int) tdiff(fstart, fend);
milliseconds = Adjust(milliseconds, diff);
- if (!isImmed && milliseconds == 0)
+ if (!isImmed && milliseconds <= 0)
break;
if (rc == WAIT_OBJECT_0) {
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;
}
goto end;
}
continue;
+ /* e.g., FOCUS_EVENT */
default:
+ CONSUME();
selectActiveHandle();
continue;
}
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;
}
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);
}
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)
{
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);
}
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);
wcon_nap, /* nap */
wcon_kpad, /* kpad */
wcon_keyok, /* kyOk */
- wcon_kyExist /* kyExist */
+ wcon_kyExist, /* kyExist */
+ wcon_cursorSet /* cursorSet */
};
/* --------------------------------------------------------- */
#if WINVER >= 0x0600
/* This function tests, whether or not the ncurses application
is running as a descendant of MSYS2/cygwin mintty terminal
- application. mintty doesn't use Windows Console for it's screen
+ application. mintty doesn't use Windows Console for its screen
I/O, so the native Windows _isatty doesn't recognize it as
character device. But we can discover we are at the end of an
Pipe and can query to server side of the pipe, looking whether
#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
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 is the responsibility of the console to deal
with the terminal escape sequences that are sent by
terminfo.
*/
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(
_nc_mingw_testmouse(
SCREEN *sp,
HANDLE fd,
- int delay)
+ int delay
+ EVENTLIST_2nd(_nc_eventlist * evl))
{
int rc = 0;
HANDLE fd,
int *buf)
{
- int n = 1;
+ int rc = -1;
INPUT_RECORD inp_rec;
BOOL b;
DWORD nRead;
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)) {
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;
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]),
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,
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