/****************************************************************************
- * Copyright (c) 1998-2013,2014 Free Software Foundation, Inc. *
+ * Copyright (c) 1998-2014,2015 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 - implement flash
- * TODO - improve screen-repainting, using implied wraparound
- * TODO - if non-buffered, change buffer size (temporarily) to window-size - SetConsoleScreenBufferSize
+ * 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 __MINGW32__
#include <tchar.h>
+#else
+#include <windows.h>
+#include <wchar.h>
+#endif
+
#include <io.h>
#define PSAPI_VERSION 2
#define CUR my_term.type.
-MODULE_ID("$Id: win_driver.c,v 1.43 2014/08/09 20:31:40 tom Exp $")
+MODULE_ID("$Id: win_driver.c,v 1.54 2015/01/10 23:13:24 tom Exp $")
#ifndef __GNUC__
# error We need GCC to compile for MinGW
#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's 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 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)
bufferCoord.X = bufferCoord.Y = 0;
- if (ReadConsoleOutput(CON.hdl,
- save_screen,
- save_size,
- bufferCoord,
- &save_region)) {
+ if (read_screen(CON.hdl,
+ save_screen,
+ save_size,
+ bufferCoord,
+ &save_region)) {
int i, j;
int ij = 0;
int k = 0;
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
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
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.save_size,
- bufferCoord,
- &save_region)) {
+ 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)",
static int
wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
- int beepFlag GCC_UNUSED)
+ int beepFlag)
{
SCREEN *sp;
int res = OK;
+ 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[max_cells];
+ CHAR_INFO that_screen[max_cells];
+ COORD this_size;
+ SMALL_RECT this_region;
+ COORD bufferCoord;
+
AssertTCB();
+
SetSP();
- MessageBeep(MB_ICONWARNING); /* MB_OK might be better */
+ 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(that_screen));
+
+ 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 */
+ }
return res;
}
return OK;
}
+#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 {
+ int high = info->srWindow.Bottom - info->srWindow.Top + 1;
+ int wide = info->srWindow.Right - info->srWindow.Left + 1;
+
+ 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;
+ }
+
+ rect.Left =
+ rect.Top = 0;
+ rect.Right = (SHORT) (wide - 1);
+ rect.Bottom = (SHORT) (high - 1);
+
+ coord.X = (SHORT) wide;
+ coord.Y = (SHORT) high;
+
+ if (info->dwSize.Y != high ||
+ info->dwSize.X != wide ||
+ info->srWindow.Top != 0 ||
+ info->srWindow.Left != 0) {
+ changed = TRUE;
+ }
+
+ }
+
+ 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
wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
{
if (sp->_keypad_on)
_nc_keypad(sp, TRUE);
}
+ if (!CON.buffered) {
+ set_scrollback(FALSE, &CON.SBI);
+ }
code = OK;
}
}
}
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;
}
+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)
{
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;
+ 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",
bufferCoord.Y,
bufferCoord.X));
- if (ReadConsoleOutput(CON.hdl,
- CON.save_screen,
- CON.save_size,
- bufferCoord,
- &CON.save_region)) {
+ if (read_screen(CON.hdl,
+ CON.save_screen,
+ CON.save_size,
+ bufferCoord,
+ &CON.save_region)) {
result = TRUE;
} else {
T((" error %#lx", (unsigned long) GetLastError()));
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;
}
returnCode(OK);
}
+static int
+wcon_cursorSet(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int mode)
+{
+ int res = -1;
+ CONSOLE_CURSOR_INFO this_CI = CON.save_CI;
+ T((T_CALLED("win32con:wcon_cursorSet(%d)"), mode));
+ 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)
{
wcon_nap, /* nap */
wcon_kpad, /* kpad */
wcon_keyok, /* kyOk */
- wcon_kyExist /* kyExist */
+ wcon_kyExist, /* kyExist */
+ wcon_cursorSet /* cursorSet */
};
/* --------------------------------------------------------- */
{
int result = 0;
- if (_isatty(fd)) {
+#ifdef __MING32__
+#define SysISATTY(fd) _isatty(fd)
+#else
+#define SysISATTY(fd) isatty(fd)
+#endif
+ if (SysISATTY(fd)) {
result = 1;
} else {
#if WINVER >= 0x0600
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)) {
}
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]),
if (CON.hdl != INVALID_HANDLE_VALUE) {
CON.buffered = buffered;
get_SBI();
+ 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;