Implemented a fix to the lag problems that are occuring with the Win32
console port. The fix implements a console back buffer which significantly reduces the number of calls made to WriteConsoleOutputXXX and eliminates the lag users have been experiencing.
This commit is contained in:
@@ -151,6 +151,180 @@ SOURCEWHERE pSourceWhere;
|
||||
SOURCEAUTHOR pSourceAuthor;
|
||||
KEYHANDLERNAME pKeyHandlerName;
|
||||
|
||||
/* CP437 to Unicode mapping according to the Unicode Consortium */
|
||||
static const WCHAR cp437[] = {
|
||||
0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
|
||||
0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
|
||||
0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
|
||||
0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
|
||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
|
||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
||||
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
||||
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
|
||||
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
|
||||
};
|
||||
|
||||
/*
|
||||
* cpConsole provides the mapping of characters in the console code page to UNICODE.
|
||||
* It maps a character to at most two WCHARs storing the number of WCHARs in count.
|
||||
*
|
||||
* NOTE: cpConsole is only valid if has_unicode is TRUE.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
WCHAR characters[2];
|
||||
int count;
|
||||
} CodePageMapping;
|
||||
|
||||
static CodePageMapping cpConsole[256];
|
||||
|
||||
static void initialize_cp_console()
|
||||
{
|
||||
if (has_unicode) {
|
||||
UINT codePage = GetConsoleOutputCP();
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
char c = (char)i;
|
||||
cpConsole[i].count = MultiByteToWideChar(codePage, 0, &c, 1, &cpConsole[i].characters[0], 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Console Buffer Flipping Support
|
||||
*
|
||||
* To minimize the number of calls into the WriteConsoleOutputXXX methods, we implement a notion
|
||||
* of a console back buffer which keeps the next frame of console output as it is being composed.
|
||||
* When ready to show the new frame, we compare this next frame to what is currently being output
|
||||
* and only call WriteConsoleOutputXXX for those console values that need to change.
|
||||
*/
|
||||
|
||||
#define CONSOLE_CLEAR_ATTRIBUTE (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
|
||||
#define CONSOLE_CLEAR_CHARACTER (' ')
|
||||
|
||||
#define CONSOLE_BUFFER_WIDTH 80
|
||||
#define CONSOLE_BUFFER_HEIGHT 25
|
||||
#define CONSOLE_BUFFER_SIZE (CONSOLE_BUFFER_WIDTH * CONSOLE_BUFFER_HEIGHT)
|
||||
|
||||
typedef struct {
|
||||
WCHAR characters[2];
|
||||
int count;
|
||||
WORD attribute;
|
||||
} cell_t;
|
||||
|
||||
typedef struct {
|
||||
cell_t cells[CONSOLE_BUFFER_HEIGHT][CONSOLE_BUFFER_WIDTH];
|
||||
} console_buffer_t;
|
||||
|
||||
console_buffer_t back_buffer;
|
||||
console_buffer_t front_buffer;
|
||||
cell_t clear_cell;
|
||||
cell_t undefined_cell;
|
||||
|
||||
static boolean buffer_flipping_initialized = FALSE;
|
||||
|
||||
static void initialize_buffer_flipping();
|
||||
|
||||
static void back_buffer_flip();
|
||||
|
||||
static void buffer_fill_to_end(console_buffer_t * buffer, cell_t * cell, int x, int y);
|
||||
static void back_buffer_clear_to_end_of_line(int x, int y);
|
||||
static void back_buffer_write(cell_t * cell, int x, int y);
|
||||
|
||||
|
||||
static void initialize_buffer_flipping()
|
||||
{
|
||||
clear_cell.attribute = CONSOLE_CLEAR_ATTRIBUTE;
|
||||
clear_cell.characters[0] = CONSOLE_CLEAR_CHARACTER;
|
||||
clear_cell.characters[1] = 0;
|
||||
clear_cell.count = 1;
|
||||
|
||||
undefined_cell = clear_cell;
|
||||
undefined_cell.count = 0;
|
||||
|
||||
buffer_fill_to_end(&front_buffer, &undefined_cell, 0, 0);
|
||||
buffer_fill_to_end(&back_buffer, &clear_cell, 0, 0);
|
||||
|
||||
buffer_flipping_initialized = TRUE;
|
||||
}
|
||||
|
||||
static void back_buffer_flip()
|
||||
{
|
||||
cell_t * back_cell = &back_buffer.cells[0][0];
|
||||
cell_t * front_cell = &front_buffer.cells[0][0];
|
||||
COORD pos;
|
||||
|
||||
if (!buffer_flipping_initialized)
|
||||
return;
|
||||
|
||||
for (pos.Y = 0; pos.Y < CONSOLE_BUFFER_HEIGHT; pos.Y++) {
|
||||
for (pos.X = 0; pos.X < CONSOLE_BUFFER_WIDTH; pos.X++, back_cell++, front_cell++) {
|
||||
if (back_cell->attribute != front_cell->attribute) {
|
||||
WriteConsoleOutputAttribute(hConOut, &back_cell->attribute, 1, pos, &acount);
|
||||
front_cell->attribute = back_cell->attribute;
|
||||
}
|
||||
if (back_cell->count != front_cell->count ||
|
||||
back_cell->characters[0] != front_cell->characters[0] ||
|
||||
back_cell->characters[1] != front_cell->characters[1]) {
|
||||
if (has_unicode) {
|
||||
WriteConsoleOutputCharacterW(hConOut, back_cell->characters, back_cell->count, pos, &ccount);
|
||||
} else {
|
||||
char ch = (char)back_cell->characters[0];
|
||||
WriteConsoleOutputCharacterA(hConOut, &ch, 1, pos, &ccount);
|
||||
}
|
||||
*front_cell = *back_cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void buffer_fill_to_end(console_buffer_t * buffer, cell_t * src, int x, int y)
|
||||
{
|
||||
cell_t * dst = &buffer->cells[y][x];
|
||||
cell_t * sentinel = &buffer->cells[0][0] + CONSOLE_BUFFER_SIZE;
|
||||
while (dst != sentinel)
|
||||
*dst++ = clear_cell;
|
||||
}
|
||||
|
||||
static void back_buffer_write(cell_t * cell, int x, int y)
|
||||
{
|
||||
back_buffer.cells[y][x] = *cell;
|
||||
}
|
||||
|
||||
static void back_buffer_clear_to_end_of_line(int x, int y)
|
||||
{
|
||||
cell_t * cell;
|
||||
cell_t *sentinel;
|
||||
|
||||
cell = &back_buffer.cells[y][x];
|
||||
sentinel = &back_buffer.cells[y+1][0];
|
||||
while (cell != sentinel)
|
||||
*cell++ = clear_cell;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called after returning from ! or ^Z
|
||||
*/
|
||||
@@ -197,7 +371,7 @@ setftty()
|
||||
adjust_palette();
|
||||
#endif
|
||||
start_screen();
|
||||
has_unicode = ((GetVersion() & 0x80000000) == 0);
|
||||
initialize_buffer_flipping();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -232,6 +406,10 @@ tty_end_screen()
|
||||
clear_screen();
|
||||
really_move_cursor();
|
||||
if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
|
||||
|
||||
buffer_fill_to_end(&back_buffer, &clear_cell, 0, 0);
|
||||
buffer_fill_to_end(&front_buffer, &clear_cell, 0, 0);
|
||||
|
||||
DWORD ccnt;
|
||||
COORD newcoord;
|
||||
|
||||
@@ -279,6 +457,10 @@ int mode;
|
||||
DWORD cmode;
|
||||
long mask;
|
||||
|
||||
has_unicode = ((GetVersion() & 0x80000000) == 0);
|
||||
|
||||
initialize_cp_console();
|
||||
|
||||
GUILaunched = 0;
|
||||
|
||||
try :
|
||||
@@ -440,6 +622,8 @@ really_move_cursor()
|
||||
console.cursor.Y = ttyDisplay->cury;
|
||||
}
|
||||
SetConsoleCursorPosition(hConOut, console.cursor);
|
||||
|
||||
back_buffer_flip();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -498,6 +682,7 @@ xputc_core(ch)
|
||||
char ch;
|
||||
{
|
||||
boolean inverse = FALSE;
|
||||
cell_t cell;
|
||||
switch (ch) {
|
||||
case '\n':
|
||||
console.cursor.Y++;
|
||||
@@ -515,17 +700,19 @@ char ch;
|
||||
ttycolors[console.current_nhcolor];
|
||||
if (console.current_nhattr[ATR_BOLD])
|
||||
console.attr |= (inverse) ?
|
||||
BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
|
||||
WriteConsoleOutputAttribute(hConOut, &console.attr, 1, console.cursor, &acount);
|
||||
BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
|
||||
|
||||
cell.attribute = console.attr;
|
||||
if (has_unicode) {
|
||||
/* Avoid bug in ANSI API on WinNT */
|
||||
WCHAR c2[2];
|
||||
int rc;
|
||||
rc = MultiByteToWideChar(GetConsoleOutputCP(), 0, &ch, 1, c2, 2);
|
||||
WriteConsoleOutputCharacterW(hConOut, c2, rc, console.cursor, &ccount);
|
||||
cell.characters[0] = cpConsole[ch].characters[0];
|
||||
cell.characters[1] = cpConsole[ch].characters[1];
|
||||
cell.count = cpConsole[ch].count;
|
||||
} else {
|
||||
WriteConsoleOutputCharacterA(hConOut, &ch, 1, console.cursor, &ccount);
|
||||
cell.characters[0] = ch;
|
||||
cell.characters[1] = 0;
|
||||
cell.count = 1;
|
||||
}
|
||||
back_buffer_write(&cell, console.cursor.X, console.cursor.Y);
|
||||
console.cursor.X++;
|
||||
}
|
||||
}
|
||||
@@ -535,46 +722,11 @@ char ch;
|
||||
* for win32. It is used for glyphs only, not text.
|
||||
*/
|
||||
|
||||
/* CP437 to Unicode mapping according to the Unicode Consortium */
|
||||
static const WCHAR cp437[] = {
|
||||
0x0020, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
|
||||
0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
|
||||
0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
|
||||
0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC,
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
|
||||
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
|
||||
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
|
||||
0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
|
||||
0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
|
||||
0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
|
||||
0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
|
||||
0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
|
||||
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
|
||||
0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
|
||||
0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
|
||||
0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
|
||||
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
|
||||
0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
|
||||
0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
|
||||
0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
|
||||
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
|
||||
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
|
||||
};
|
||||
|
||||
void
|
||||
g_putch(in_ch)
|
||||
int in_ch;
|
||||
{
|
||||
cell_t cell;
|
||||
boolean inverse = FALSE;
|
||||
unsigned char ch = (unsigned char) in_ch;
|
||||
|
||||
@@ -587,23 +739,24 @@ int in_ch;
|
||||
ttycolors[console.current_nhcolor];
|
||||
if (console.current_nhattr[ATR_BOLD])
|
||||
console.attr |= (inverse) ? BACKGROUND_INTENSITY : FOREGROUND_INTENSITY;
|
||||
WriteConsoleOutputAttribute(hConOut, &console.attr, 1, console.cursor, &acount);
|
||||
|
||||
cell.attribute = console.attr;
|
||||
cell.characters[1] = 0;
|
||||
cell.count = 1;
|
||||
if (has_unicode)
|
||||
WriteConsoleOutputCharacterW(hConOut, &cp437[ch], 1, console.cursor, &ccount);
|
||||
cell.characters[0] = cp437[ch];
|
||||
else
|
||||
WriteConsoleOutputCharacterA(hConOut, &ch, 1, console.cursor, &ccount);
|
||||
cell.characters[0] = ch;
|
||||
back_buffer_write(&cell, console.cursor.X, console.cursor.Y);
|
||||
}
|
||||
|
||||
void
|
||||
cl_end()
|
||||
{
|
||||
int cx;
|
||||
console.cursor.X = ttyDisplay->curx;
|
||||
console.cursor.Y = ttyDisplay->cury;
|
||||
cx = CO - console.cursor.X;
|
||||
FillConsoleOutputAttribute(hConOut, DEFTEXTCOLOR, cx, console.cursor, &acount);
|
||||
FillConsoleOutputCharacter(hConOut, ' ', cx, console.cursor, &ccount);
|
||||
|
||||
back_buffer_clear_to_end_of_line(console.cursor.X, console.cursor.Y);
|
||||
|
||||
tty_curs(BASE_WINDOW, (int) ttyDisplay->curx + 1, (int) ttyDisplay->cury);
|
||||
}
|
||||
|
||||
@@ -611,6 +764,10 @@ void
|
||||
raw_clear_screen()
|
||||
{
|
||||
if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
|
||||
|
||||
buffer_fill_to_end(&front_buffer, &clear_cell, 0, 0);
|
||||
buffer_fill_to_end(&back_buffer, &clear_cell, 0, 0);
|
||||
|
||||
DWORD ccnt;
|
||||
COORD newcoord;
|
||||
|
||||
@@ -621,6 +778,7 @@ raw_clear_screen()
|
||||
csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
|
||||
FillConsoleOutputCharacter(
|
||||
hConOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, newcoord, &ccnt);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,11 +809,16 @@ cl_eos()
|
||||
{
|
||||
int cy = ttyDisplay->cury + 1;
|
||||
if (GetConsoleScreenBufferInfo(hConOut, &csbi)) {
|
||||
|
||||
buffer_fill_to_end(&front_buffer, &clear_cell, ttyDisplay->curx, ttyDisplay->cury);
|
||||
buffer_fill_to_end(&back_buffer, &clear_cell, ttyDisplay->curx, ttyDisplay->cury);
|
||||
|
||||
DWORD ccnt;
|
||||
COORD newcoord;
|
||||
|
||||
newcoord.X = ttyDisplay->curx;
|
||||
newcoord.Y = ttyDisplay->cury;
|
||||
|
||||
FillConsoleOutputAttribute(
|
||||
hConOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
|
||||
csbi.dwSize.X * csbi.dwSize.Y - cy, newcoord, &ccnt);
|
||||
|
||||
Reference in New Issue
Block a user