Add tab handling

Support setting (HTS & DECST8C) and clearing (TBC) tabs, overriding the
console's own tab processing.  I've extended TBC with `\e[8g` to restore
console processing and added an extra parameter to DECST8C to set a
particular tab size.
This commit is contained in:
Jason Hood 2017-12-10 22:52:38 +10:00
parent 645f57e59c
commit d20ab7471f
4 changed files with 181 additions and 82 deletions

90
ANSI.c
View File

@ -169,7 +169,8 @@
added IND, NEL & RI (using buffer, in keeping with LF); added IND, NEL & RI (using buffer, in keeping with LF);
added DA, DECCOLM, DECNCSM, DECSC & DECRC; added DA, DECCOLM, DECNCSM, DECSC & DECRC;
an explicit zero parameter should still default to one; an explicit zero parameter should still default to one;
restrict parameters to a maximum value of 32767. restrict parameters to a maximum value of 32767;
added tab handling.
*/ */
#include "ansicon.h" #include "ansicon.h"
@ -199,7 +200,8 @@ struct Cache
} cache[CACHE]; } cache[CACHE];
#define ESC '\x1B' // ESCape character #define ESC '\x1B' // ESCape character
#define BEL '\x07' #define BEL '\x07' // BELl
#define HT '\x09' // Horizontal Tabulation
#define SO '\x0E' // Shift Out #define SO '\x0E' // Shift Out
#define SI '\x0F' // Shift In #define SI '\x0F' // Shift In
@ -334,6 +336,8 @@ typedef BOOL (WINAPI *PHCSBIX)(
PHCSBIX GetConsoleScreenBufferInfoX, SetConsoleScreenBufferInfoX; PHCSBIX GetConsoleScreenBufferInfoX, SetConsoleScreenBufferInfoX;
#define MAX_TABS 2048
typedef struct typedef struct
{ {
BYTE foreground; // ANSI base color (0 to 7; add 30) BYTE foreground; // ANSI base color (0 to 7; add 30)
@ -349,6 +353,8 @@ typedef struct
SHORT buf_width; // buffer width prior to setting 132 columns SHORT buf_width; // buffer width prior to setting 132 columns
SHORT win_width; // window width prior to setting 132 columns SHORT win_width; // window width prior to setting 132 columns
BYTE noclear; // don't clear the screen on column mode change BYTE noclear; // don't clear the screen on column mode change
BYTE tabs; // handle tabs directly
BYTE tab_stop[MAX_TABS];
} STATE, *PSTATE; } STATE, *PSTATE;
PSTATE pState; PSTATE pState;
@ -704,6 +710,19 @@ void send_palette_sequence( COLORREF c )
SendSequence( buf ); SendSequence( buf );
} }
// Clear existing tabs and set tab stops at every size columns.
void init_tabs( int size )
{
int i;
memset( pState->tab_stop, FALSE, MAX_TABS );
for (i = 0; i < MAX_TABS; i += size)
pState->tab_stop[i] = TRUE;
pState->tabs = TRUE;
}
// ========== Print functions // ========== Print functions
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -751,8 +770,11 @@ void InterpretEscSeq( void )
if (prefix == '[') if (prefix == '[')
{ {
if (prefix2 == '?' && (suffix == 'h' || suffix == 'l') && es_argc == 1) if (prefix2 == '?')
{ {
if (suffix == 'h' || suffix == 'l')
{
if (es_argc != 1) return;
switch (es_argv[0]) switch (es_argv[0])
{ {
case 25: case 25:
@ -826,6 +848,14 @@ void InterpretEscSeq( void )
} }
} }
} }
else if (suffix == 'W')
{
if (es_argv[0] != 5 || es_argc > 2) return;
if (es_argc == 1) es_argv[1] = 8;
init_tabs( es_argv[1] );
return;
}
}
// Ignore any other private sequences. // Ignore any other private sequences.
if (prefix2 != 0) if (prefix2 != 0)
return; return;
@ -1185,9 +1215,37 @@ void InterpretEscSeq( void )
SetConsoleCursorPosition( hConOut, Pos ); SetConsoleCursorPosition( hConOut, Pos );
return; return;
case 'g':
if (es_argc > 1) return; // ESC[g == ESC[0g
switch (es_argv[0])
{
case 0: // ESC[0g Clear tab at cursor
if (!pState->tabs) init_tabs( 8 );
if (CUR.X < MAX_TABS) pState->tab_stop[CUR.X] = FALSE;
return;
case 3: // ESC[3g Clear all tabs
memset( pState->tab_stop, FALSE, MAX_TABS );
pState->tabs = TRUE;
return;
case 8: // ESC[8g Let console handle tabs
pState->tabs = FALSE;
return;
default:
return;
}
case 'I': // ESC[#I Moves cursor forward # tabs case 'I': // ESC[#I Moves cursor forward # tabs
if (es_argc > 1) return; // ESC[I == ESC[1I if (es_argc > 1) return; // ESC[I == ESC[1I
Pos.Y = CUR.Y; Pos.Y = CUR.Y;
if (pState->tabs)
{
Pos.X = CUR.X;
while (++Pos.X < MAX_TABS && (!pState->tab_stop[Pos.X] || --p1 > 0)) ;
}
else
Pos.X = (CUR.X & -8) + p1 * 8; Pos.X = (CUR.X & -8) + p1 * 8;
if (Pos.X > RIGHT) Pos.X = RIGHT; if (Pos.X > RIGHT) Pos.X = RIGHT;
SetConsoleCursorPosition( hConOut, Pos ); SetConsoleCursorPosition( hConOut, Pos );
@ -1196,10 +1254,18 @@ void InterpretEscSeq( void )
case 'Z': // ESC[#Z Moves cursor back # tabs case 'Z': // ESC[#Z Moves cursor back # tabs
if (es_argc > 1) return; // ESC[Z == ESC[1Z if (es_argc > 1) return; // ESC[Z == ESC[1Z
Pos.Y = CUR.Y; Pos.Y = CUR.Y;
if (pState->tabs)
{
Pos.X = (CUR.X < MAX_TABS) ? CUR.X : MAX_TABS;
while (--Pos.X > 0 && (!pState->tab_stop[Pos.X] || --p1 > 0)) ;
}
else
{
if ((CUR.X & 7) == 0) if ((CUR.X & 7) == 0)
Pos.X = CUR.X - p1 * 8; Pos.X = CUR.X - p1 * 8;
else else
Pos.X = (CUR.X & -8) - (p1 - 1) * 8; Pos.X = (CUR.X & -8) - (p1 - 1) * 8;
}
if (Pos.X < LEFT) Pos.X = LEFT; if (Pos.X < LEFT) Pos.X = LEFT;
SetConsoleCursorPosition( hConOut, Pos ); SetConsoleCursorPosition( hConOut, Pos );
return; return;
@ -1579,6 +1645,15 @@ ParseAndPrintString( HANDLE hDev,
} }
else if (c == SO) shifted = TRUE; else if (c == SO) shifted = TRUE;
else if (c == SI) shifted = G0_special; else if (c == SI) shifted = G0_special;
else if (c == HT && pState != NULL && pState->tabs)
{
CONSOLE_SCREEN_BUFFER_INFO Info;
FlushBuffer();
GetConsoleScreenBufferInfo( hConOut, &Info );
while (++CUR.X < MAX_TABS && !pState->tab_stop[CUR.X]) ;
if (CUR.X > RIGHT) CUR.X = RIGHT;
SetConsoleCursorPosition( hConOut, CUR );
}
else PushBuffer( (WCHAR)c ); else PushBuffer( (WCHAR)c );
} }
else if (state == 2) else if (state == 2)
@ -1622,6 +1697,15 @@ ParseAndPrintString( HANDLE hDev,
ScrollUp(); ScrollUp();
state = 1; state = 1;
} }
else if (c == 'H') // HTS Character Tabulation Set
{
CONSOLE_SCREEN_BUFFER_INFO Info;
if (!pState->tabs) init_tabs( 8 );
FlushBuffer();
GetConsoleScreenBufferInfo( hConOut, &Info );
if (CUR.X < MAX_TABS) pState->tab_stop[CUR.X] = TRUE;
state = 1;
}
else if (c == '7' || // DECSC Save Cursor else if (c == '7' || // DECSC Save Cursor
c == '8') // DECRC Restore Cursor c == '8') // DECRC Restore Cursor
{ {

View File

@ -91,7 +91,7 @@
use -pu to unload from the parent. use -pu to unload from the parent.
*/ */
#define PDATE L"9 December, 2017" #define PDATE L"10 December, 2017"
#include "ansicon.h" #include "ansicon.h"
#include "version.h" #include "version.h"

View File

@ -150,7 +150,8 @@ Usage
Sequences Recognised Sequences Recognised
==================== ====================
The following escape sequences are recognised. The following escape sequences are recognised (see "sequences.txt" for a
more complete description).
\e]0;titleBEL xterm: Set window's title (and icon, ignored) \e]0;titleBEL xterm: Set window's title (and icon, ignored)
\e]2;titleBEL xterm: Set window's title \e]2;titleBEL xterm: Set window's title
@ -183,6 +184,8 @@ Sequences Recognised
\e[#;#;#...,~ DECPS DEC Play Sound \e[#;#;#...,~ DECPS DEC Play Sound
\e8 DECRC DEC Restore Cursor \e8 DECRC DEC Restore Cursor
\e7 DECSC DEC Save Cursor \e7 DECSC DEC Save Cursor
\e[?5W DECST8C DEC Set Tab at Every 8 Columns
\e[?5;#W DECST8C DEC Set Tab at Every # Columns (ANSICON extension)
\e[?25h DECTCEM DEC Text Cursor Enable Mode (show cursor) \e[?25h DECTCEM DEC Text Cursor Enable Mode (show cursor)
\e[?25l DECTCEM DEC Text Cursor Enable Mode (hide cursor) \e[?25l DECTCEM DEC Text Cursor Enable Mode (hide cursor)
\e[#M DL Delete Line \e[#M DL Delete Line
@ -193,6 +196,8 @@ Sequences Recognised
\e[#` HPA Character Position Absolute \e[#` HPA Character Position Absolute
\e[#j HPB Character Position Backward \e[#j HPB Character Position Backward
\e[#a HPR Character Position Forward \e[#a HPR Character Position Forward
HT HT Character Tabulation
\eH HTS Character Tabulation Set
\e[#;#f HVP Character And Line Position \e[#;#f HVP Character And Line Position
\e[#@ ICH Insert Character \e[#@ ICH Insert Character
\e[#L IL Insert Line \e[#L IL Insert Line
@ -205,13 +210,14 @@ Sequences Recognised
\e(0 SCS Select Character Set (DEC special graphics) \e(0 SCS Select Character Set (DEC special graphics)
\e(B SCS Select Character Set (ASCII) \e(B SCS Select Character Set (ASCII)
\e[#;#;#m SGR Select Graphic Rendition \e[#;#;#m SGR Select Graphic Rendition
\e[#g TBC Tabulation Clear
\e[#d VPA Line Position Absolute \e[#d VPA Line Position Absolute
\e[#k VPB Line Position Backward \e[#k VPB Line Position Backward
\e[#e VPR Line Position Forward \e[#e VPR Line Position Forward
'\e' represents the escape character (ASCII 27); '#' represents a decimal '\e' represents the escape character (ASCII 27); '#' represents a decimal
number (optional, in most cases defaulting to 1); BEL, SO and SI are ASCII number (optional, in most cases defaulting to 1); BEL, HT, SO and SI are
7, 14 and 15. See "sequences.txt" for a more complete description. ASCII 7, 9, 14 and 15.
Escape followed by a control character will display that character, not Escape followed by a control character will display that character, not
perform its function; an unrecognised character will preserve the escape. perform its function; an unrecognised character will preserve the escape.
@ -286,7 +292,7 @@ DEC Special Graphics Character Set
Limitations Limitations
=========== ===========
Tabs are fixed at eight columns. Tabs can only be set up to column 2048.
The saved position will not be restored correctly if the buffer scrolls. The saved position will not be restored correctly if the buffer scrolls.
Palette sequences only work from Vista. Palette sequences only work from Vista.
@ -304,7 +310,7 @@ Version History
Legend: + added, - bug-fixed, * changed. Legend: + added, - bug-fixed, * changed.
1.80 - 9 December, 2017: 1.80 - 10 December, 2017:
- fix unloading; - fix unloading;
- fix -e et al when redirecting to CON; - fix -e et al when redirecting to CON;
- hook CreateFile and CreateConsoleScreenBuffer to force read/write access - hook CreateFile and CreateConsoleScreenBuffer to force read/write access
@ -323,7 +329,8 @@ Version History
+ added palette sequences; + added palette sequences;
+ added -pu to unload from the parent; + added -pu to unload from the parent;
+ added IND, NEL, RI, DA, DECCOLM, DECNCSM, DECSC & DECRC; + added IND, NEL, RI, DA, DECCOLM, DECNCSM, DECSC & DECRC;
+ added SCS, but only for special/ASCII (same as Win10). + added SCS, but only for special/ASCII (same as Win10);
+ added tab handling (HT, HTS, TBC & DECST8C).
1.72 - 24 December, 2015: 1.72 - 24 December, 2015:
- handle STD_OUTPUT_HANDLE & STD_ERROR_HANDLE in WriteFile; - handle STD_OUTPUT_HANDLE & STD_ERROR_HANDLE in WriteFile;
@ -551,5 +558,5 @@ Distribution
in LICENSE.txt. in LICENSE.txt.
============================ ==============================
Jason Hood, 9 December, 2017. Jason Hood, 10 December, 2017.

View File

@ -83,6 +83,14 @@ M move cursor up one line (scroll if necessary; always uses buffer)
[#C move cursor right # characters [#C move cursor right # characters
[D move cursor left one character [D move cursor left one character
[#D move cursor left # characters [#D move cursor left # characters
H set tab stop
[g remove tab stop at cursor
[0g as above
[3g remove all tab stops
[8g restore console tab handling (ANSICON extension)
[?5W set tab stops every 8 columns
[?5;#W set tab stops every # columns (ANSICON extension)
[I move cursor forward one tab [I move cursor forward one tab
[#I move cursor forward # tabs [#I move cursor forward # tabs
[Z move cursor back one tab [Z move cursor back one tab