From 9a40d784e1b2b07b827f19ee91c662e0b0c129e4 Mon Sep 17 00:00:00 2001
From: David Goodwin
Date: Fri, 21 Nov 2025 17:29:10 +1300
Subject: [PATCH 1/4] Some initial work towards making DECRQCRA less wrong
Right now it should correctly compute the checksum for any region of the display that the host has written to completely. If any cells in the DECRQCRA region are erased, then the checksum will be incorrect as erased cells should not be included.
Additionally, colour information is not currently included in the checksum - that still needs to be done too.
And lastly, the checksum is only valid for the latin1 charset - for other VT420/520 charsets further work will need to be done.
---
kermit/k95/ckoco3.c | 72 +++++++++++++++++++++------------------------
1 file changed, 33 insertions(+), 39 deletions(-)
diff --git a/kermit/k95/ckoco3.c b/kermit/k95/ckoco3.c
index 01cbbf0c..4f721c1c 100644
--- a/kermit/k95/ckoco3.c
+++ b/kermit/k95/ckoco3.c
@@ -6764,13 +6764,16 @@ decdwl_escape(bool dwlflag) {
int
calculate_decrqcra_checksum(int top, int left, int bot, int right, int page, BOOL obey_margins) {
- int checksum=0;
+ unsigned short checksum=0;
int x, y, height, width, max_page;
height = VscrnGetHeight(VTERM) - (tt_status[VTERM] ? 1 : 0);
width = VscrnGetWidth(VTERM);
max_page = term_max_page(VTERM);
+ /* TODO: If page == 0, calculate checksum of
+ * *all* pages (unless on alternate buffer) */
+
if (top < 1) top = 1;
if (left < 1) left = 1;
if (bot < 1) bot = height;
@@ -6779,6 +6782,11 @@ calculate_decrqcra_checksum(int top, int left, int bot, int right, int page, BOO
else page = page - 1;
if (obey_margins) {
+ debug(F111, "DECRQCRA", "margintop", vscrn_page_margin_top(VTERM,page));
+ debug(F111, "DECRQCRA", "marginleft", vscrn_page_margin_left(VTERM,page));
+ debug(F111, "DECRQCRA", "marginbot", vscrn_page_margin_bot(VTERM,page));
+ debug(F111, "DECRQCRA", "marginright", vscrn_page_margin_right(VTERM,page));
+
if (top < vscrn_page_margin_top(VTERM,page)) top = vscrn_page_margin_top(VTERM,page);
if (top > vscrn_page_margin_bot(VTERM,page) + 1) top = vscrn_page_margin_bot(VTERM,page) + 1;
if (left < vscrn_page_margin_left(VTERM,page)) left = vscrn_page_margin_left(VTERM,page);
@@ -6806,7 +6814,7 @@ calculate_decrqcra_checksum(int top, int left, int bot, int right, int page, BOO
videoline * line = VscrnGetPageLineFromTop(VTERM, y, page);
for ( x=left-1; xcells[x].c;
a = line->vt_char_attrs[x];
@@ -6815,23 +6823,11 @@ calculate_decrqcra_checksum(int top, int left, int bot, int right, int page, BOO
fgcoloridx = cell_video_attr_foreground(line->cells[x].video_attr);
bgcoloridx = cell_video_attr_background(line->cells[x].video_attr);
- /* Xterm implements the following behaviour to
- * supposedly match what the VT525 does (I don't
- * have access to a VT525 to confirm the
- * behaviour myself): If the current background
- * color is the default and the current foreground
- * is *not* the default, then ignore the bold attribute
- * if its set.
- */
- if (a & VT_CHAR_ATTR_BOLD) {
- unsigned char df_fg, df_bg;
- df_fg = cell_video_attr_foreground(defaultattribute);
- df_bg = cell_video_attr_background(defaultattribute);
- if (df_bg == bgcoloridx && df_fg != fgcoloridx) {
- checksum -= 0x80;
- }
- }
+#ifdef CK_COLORS_24BIT
+ /* TODO: Map all colors into SGR palette */
+#endif /* CK_COLORS_24BIT */
+ /* TODO: this is wrong I think*/
if (fgcoloridx < 16) {
fgcoloridx = sgrindex[fgcoloridx%8];
} else {
@@ -6851,21 +6847,24 @@ calculate_decrqcra_checksum(int top, int left, int bot, int right, int page, BOO
debug(F111, "DECRQCRA iteration", "x", x);
debug(F111, "DECRQCRA iteration", "y", y);
debug(F111, "DECRQCRA iteration", "c", c);
+ debug(F111, "DECRQCRA iteration", "a", a);
debug(F111, "DECRQCRA iteration", "checksum", checksum);
- checksum += c;
+ checksum -= c;
- debug(F111, "DECRQCRA iteration", "checksum+c", checksum);
+ debug(F111, "DECRQCRA iteration", "checksum-c", checksum);
- if (a & VT_CHAR_ATTR_PROTECTED) checksum += 0x04;
- if (a & VT_CHAR_ATTR_INVISIBLE) checksum += 0x08;
- if (a & VT_CHAR_ATTR_UNDERLINE) checksum += 0x10;
- if (a & VT_CHAR_ATTR_REVERSE) checksum += 0x20;
- if (a & VT_CHAR_ATTR_BLINK) checksum += 0x40;
- if (a & VT_CHAR_ATTR_BOLD) checksum += 0x80;
- /*checksum += bgcoloridx;
- checksum += fgcoloridx * 0x10;*/
- debug(F111, "DECRQCRA iteration", "checksum+attrs", checksum);
+ if (a & VT_CHAR_ATTR_PROTECTED) checksum -= 0x04;
+ if (a & VT_CHAR_ATTR_INVISIBLE) checksum -= 0x08;
+ if (a & VT_CHAR_ATTR_UNDERLINE) checksum -= 0x10;
+ if (a & VT_CHAR_ATTR_REVERSE) checksum -= 0x20;
+ if (a & VT_CHAR_ATTR_BLINK) checksum -= 0x40;
+ if (a & VT_CHAR_ATTR_BOLD) checksum -= 0x80;
+ if (ISVT525(tt_type_mode) && 0) {
+ checksum -= bgcoloridx;
+ checksum -= fgcoloridx * 0x10;
+ }
+ debug(F111, "DECRQCRA iteration", "checksum-attrs", checksum);
}
}
debug(F111, "DECRQCRA", "checksum", checksum);
@@ -18757,30 +18756,25 @@ vtcsi(void)
page = ALTERNATE_BUFFER_PAGE(VTERM);
}
- /*checksum &= 0xffff;*/
- top = pn[3] + (vscrn_page_margin_top(VTERM,page) > 1 ? vscrn_page_margin_top(VTERM,page) : 0);
- left = pn[4] + (vscrn_page_margin_left(VTERM,page) > 1 ? vscrn_page_margin_left(VTERM,page) : 0);
+ top = pn[3];
+ left = pn[4];
bot = pn[5];
right = pn[6];
debug(F111, "DECRQCRA", "pid", pid);
+ debug(F111, "DECRQCRA", "page", pn[2]);
debug(F111, "DECRQCRA", "init-top", pn[3]);
debug(F111, "DECRQCRA", "init-left", pn[4]);
debug(F111, "DECRQCRA", "init-bot", pn[5]);
debug(F111, "DECRQCRA", "init-right", pn[6]);
- debug(F111, "DECRQCRA", "margintop", vscrn_page_margin_top(VTERM,page));
- debug(F111, "DECRQCRA", "marginleft", vscrn_page_margin_left(VTERM,page));
- debug(F111, "DECRQCRA", "marginbot", vscrn_page_margin_bot(VTERM,page));
- debug(F111, "DECRQCRA", "marginright", vscrn_page_margin_right(VTERM,page));
-
checksum = calculate_decrqcra_checksum(
top, left, bot, right, page, TRUE);
if (send_c1) {
- sprintf(buf, "\033P%d!~%04X%c", pid, checksum,_ST8);
+ sprintf(buf, "P%d!~%04X%c", pid, checksum, _ST8);
} else {
- sprintf(buf, "\033P%d!~%04X\033\\", pid, checksum);
+ sprintf(buf, "P%d!~%04X\033\\", pid, checksum);
}
sendescseq(buf);
}
From 755ad42e52e4ef9341c95819dcf0a6d4fc60132d Mon Sep 17 00:00:00 2001
From: David Goodwin
Date: Sat, 29 Nov 2025 20:07:24 +1300
Subject: [PATCH 2/4] Track erased character cells with a new attribute
This is required for DECCARA and DECRARA which do not affect erased cells when in stream mode, and also by DECRQCRA which excludes erased cells from the checksum.
As it's only the three VT420 control sequences that pay attention to whether a cell is in the erased state or not, this change hasn't been fully rolled out to other emulations which all ignore this attribute and may or may not always clear it appropriately when a cell is written. Mostly its the protected field stuff that may still need further work.
---
kermit/k95/ckoco2.c | 21 ++++++---
kermit/k95/ckoco3.c | 106 ++++++++++++++++++++++++++++++--------------
kermit/k95/ckocon.c | 5 ++-
kermit/k95/ckocon.h | 23 ++++++++--
kermit/k95/ckowys.c | 2 +
5 files changed, 111 insertions(+), 46 deletions(-)
diff --git a/kermit/k95/ckoco2.c b/kermit/k95/ckoco2.c
index 18339eee..4c4cba96 100644
--- a/kermit/k95/ckoco2.c
+++ b/kermit/k95/ckoco2.c
@@ -1856,7 +1856,7 @@ VscrnScrollLf( BYTE vmode, USHORT TopRow, USHORT LeftCol, USHORT BotRow,
}
for ( x = RightCol - Columns + 1 ; x <= RightCol ; x++ ){
line->cells[x] = Cell ;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -1903,7 +1903,7 @@ VscrnScrollRt( BYTE vmode, USHORT TopRow, USHORT LeftCol, USHORT BotRow,
}
for ( x = LeftCol + Columns - 1 ; x >= LeftCol ; x-- ){
line->cells[x] = Cell ;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -2043,7 +2043,7 @@ VscrnWrtCell( BYTE vmode, viocell Cell, vtattrib att, USHORT Row, USHORT Col )
for ( i=0 ; icells[i].c = ' ' ;
line->cells[i].video_attr = cellcolor ;
- line->vt_char_attrs[i] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[i] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -2060,6 +2060,7 @@ VscrnWrtCell( BYTE vmode, viocell Cell, vtattrib att, USHORT Row, USHORT Col )
(att.graphic ? VT_CHAR_ATTR_GRAPHIC : 0) |
(att.hyperlink ? VT_CHAR_ATTR_HYPERLINK : 0) |
(att.crossedout ? VT_CHAR_ATTR_CROSSEDOUT: 0) |
+ (att.erased ? VT_CHAR_ATTR_ERASED : 0) |
(att.wyseattr ? WY_CHAR_ATTR : 0) ;
line->hyperlinks[Col] = att.hyperlink ? att.linkid : 0;
return NO_ERROR ;
@@ -2620,6 +2621,7 @@ VscrnGetVtCharAttr( BYTE vmode, SHORT x, SHORT y )
vta.graphic = attr & VT_CHAR_ATTR_GRAPHIC ? 1 : 0 ;
vta.wyseattr = attr & WY_CHAR_ATTR ? 1 : 0 ;
vta.crossedout = attr & VT_CHAR_ATTR_CROSSEDOUT ? 1 : 0 ;
+ vta.erased = attr & VT_CHAR_ATTR_ERASED ? 1 : 0 ;
vta.hyperlink = attr & VT_CHAR_ATTR_HYPERLINK ? 1 : 0;
vta.linkid = attr & VT_CHAR_ATTR_HYPERLINK ? line->hyperlinks[x] : 0;
@@ -2652,6 +2654,7 @@ VscrnSetVtCharAttr( BYTE vmode, SHORT x, SHORT y, vtattrib vta )
(vta.unerasable ? VT_CHAR_ATTR_PROTECTED : 0) |
(vta.graphic ? VT_CHAR_ATTR_GRAPHIC : 0) |
(vta.crossedout ? VT_CHAR_ATTR_CROSSEDOUT: 0) |
+ (vta.erased ? VT_CHAR_ATTR_ERASED : 0) |
(vta.hyperlink ? VT_CHAR_ATTR_HYPERLINK : 0) |
(vta.wyseattr ? WY_CHAR_ATTR : 0) ;
line = VscrnGetLineFromTop(vmode,y,FALSE);
@@ -3636,7 +3639,7 @@ VscrnScrollPage(BYTE vmode, int updown, int topmargin, int bottommargin,
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
for ( x = 0 ; x < MAXTERMCOL ; x++ ) {
line->cells[x] = blankcell ;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -3675,7 +3678,7 @@ VscrnScrollPage(BYTE vmode, int updown, int topmargin, int bottommargin,
for ( x = 0 ; x < MAXTERMCOL ; x++ )
{
line->cells[x] = blankcell ;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -3720,7 +3723,7 @@ VscrnScrollPage(BYTE vmode, int updown, int topmargin, int bottommargin,
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
for ( x = 0 ; x < MAXTERMCOL ; x++ ) {
line->cells[x] = blankcell ;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
break;
@@ -5656,7 +5659,7 @@ VscrnInit( BYTE vmode )
for ( x = 0 ; x < MAXTERMCOL ; x++ ) {
line->cells[x].c = ' ' ;
line->cells[x].video_attr = vmode == VTERM ? attribute : colorcmd;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -5711,6 +5714,10 @@ VscrnIsClear( BYTE vmode, int page )
debug(F100,"VscrnIsClear video_attr != cellcolor","",0);
return 0;
}
+ /* TODO: Ideally we'd check if VT_CHAR_ATTR_ERASED is set too, but
+ * in order to do that we'd need to be *certain* that all
+ * emulations are correctly setting and clearing the attribute
+ * which is not currently the case. */
}
}
debug(F100,"VscrnIsClear all clear","",0);
diff --git a/kermit/k95/ckoco3.c b/kermit/k95/ckoco3.c
index 4f721c1c..7eeb5b47 100644
--- a/kermit/k95/ckoco3.c
+++ b/kermit/k95/ckoco3.c
@@ -426,12 +426,12 @@ cell_video_attr_t /* Video attribute bytes */
cell_video_attr_t decatc_colors[16];
-vtattrib attrib={0,0,0,0,0,0,0,0,0,0},
+vtattrib attrib={0,0,0,0,0,0,0,0,0,0,0,0,0,0},
savedattrib[SAVED_CURSORS]={
- {0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},
- {0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0},
- {0,0,0,0,0,0,0,0,0,0}},
- cmdattrib={0,0,0,0,0,0,0,0,0,0};
+ {0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ {0,0,0,0,0,0,0,0,0,0,0,0,0,0}},
+ cmdattrib={0,0,0,0,0,0,0,0,0,0,0,0,0,0};
extern int wherex[]; /* Screen column, 1-based */
extern int wherey[]; /* Screen row, 1-based */
@@ -5976,7 +5976,7 @@ clrtoeoln( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor ;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -6019,7 +6019,7 @@ clreoscr_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
@@ -6035,7 +6035,7 @@ clreoscr_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
}
@@ -6081,7 +6081,7 @@ clrboscr_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
}
@@ -6092,7 +6092,7 @@ clrboscr_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
}
@@ -6164,7 +6164,7 @@ clreoreg_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
@@ -6177,7 +6177,7 @@ clreoreg_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
}
@@ -6221,7 +6221,7 @@ clrboreg_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
}
@@ -6232,7 +6232,7 @@ clrboreg_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
}
@@ -6270,7 +6270,7 @@ clrbol_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -6307,7 +6307,7 @@ clreol_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -6344,7 +6344,7 @@ clrline_escape( BYTE vmode, CHAR fillchar ) {
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -6353,11 +6353,15 @@ clrcol_escape( BYTE vmode, CHAR fillchar ) {
int ys ;
int x = wherex[VTERM]-1 ;
int y ;
- vtattrib vta ={0,0,0,0,0,0,0,0,0,0};
+ vtattrib vta ={0,0,0,0,0,0,0,0,0,0,0,0,0,0};
viocell cell;
cell.c = fillchar;
cell.video_attr = geterasecolor(vmode);
+ /* TODO: vta.erased = TRUE; ?
+ * Used by WYSE emulations to clear a column to a particular character, so
+ * perhaps not?
+ **/
if ( fillchar == NUL )
cell.c = SP ;
@@ -6426,7 +6430,7 @@ clrrect_escape( BYTE vmode, int top, int left, int bot, int right, CHAR fillchar
{
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -6487,7 +6491,7 @@ selclrtoeoln( BYTE vmode, CHAR fillchar ) { /* | Page: Cursor */
if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -6515,7 +6519,7 @@ selclreoscr_escape( BYTE vmode, CHAR fillchar ) { /* | Page: Cursor */
if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
@@ -6528,7 +6532,7 @@ selclreoscr_escape( BYTE vmode, CHAR fillchar ) { /* | Page: Cursor */
if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -6554,7 +6558,7 @@ selclrboscr_escape( BYTE vmode, CHAR fillchar ) { /* | Page: Cursor */
if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -6566,7 +6570,7 @@ selclrboscr_escape( BYTE vmode, CHAR fillchar ) { /* | Page: Cursor */
if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -6589,7 +6593,7 @@ selclrbol_escape( BYTE vmode, CHAR fillchar ) { /* | Page: Cursor */
if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -6612,7 +6616,7 @@ selclrline_escape( BYTE vmode, CHAR fillchar ) { /* | Page: Cursor */
if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -6623,11 +6627,14 @@ selclrcol_escape( BYTE vmode, CHAR fillchar ) {
int ys = VscrnGetHeight(VTERM)-(tt_status[VTERM]?1:0);
int x = wherex[VTERM]-1 ;
int y ;
- vtattrib vta ={0,0,0,0,0,0,0,0,0,0};
+ vtattrib vta ={0,0,0,0,0,0,0,0,0,0,0,0,0,0};
viocell cell;
cell.c = fillchar;
cell.video_attr = geterasecolor(vmode);
+ /* TODO: vta.erased = TRUE; ?
+ * Used by WYSE emulations to selectively clear a column to a particular
+ * character, so perhaps not? */
if ( fillchar == NUL )
cell.c = SP ;
@@ -6689,7 +6696,7 @@ selclrrect_escape( BYTE vmode, int top, int left, int bot, int right,
if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
}
@@ -6701,7 +6708,7 @@ boxrect_escape( BYTE vmode, int row, int col )
{
int brow, bcol, erow, ecol, x, y ;
viocell cell ;
- vtattrib vta = {0,0,0,0,0,0,0,0,0,0,0};
+ vtattrib vta = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};
if ( vmode == VTERM ) {
cell.video_attr = attribute ;
@@ -6819,6 +6826,11 @@ calculate_decrqcra_checksum(int top, int left, int bot, int right, int page, BOO
c = line->cells[x].c;
a = line->vt_char_attrs[x];
+ if (a == VT_CHAR_ATTR_ERASED) {
+ /* Unoccupied character cells are excluded from the checksum */
+ continue;
+ }
+
/* These return 0 for RGB colors */
fgcoloridx = cell_video_attr_foreground(line->cells[x].video_attr);
bgcoloridx = cell_video_attr_background(line->cells[x].video_attr);
@@ -15185,7 +15197,7 @@ cwrite(unsigned short ch) { /* Used by ckcnet.c for */
/* now to process it */
USHORT Row, Col;
viocell vio={0,0};
- vtattrib vta={0,0,0,0,0,0,0,0,0,0};
+ vtattrib vta={0,0,0,0,0,0,0,0,0,0,0,0,0,0};
pScrnBufInf = (PCONSOLE_SCREEN_BUFFER_INFO) vtnt_buf;
pCursor = (PCOORD) ((PCHAR) pScrnBufInf
@@ -15832,7 +15844,7 @@ scrninit() {
void
wrtch(unsigned short ch) {
viocell cell;
- vtattrib vta = {0,0,0,0,0,0,0,0,0,0};
+ vtattrib vta = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int vmode = decsasd == SASD_TERMINAL ? VTERM : VSTATUS ;
extern int k95stdio,k95stdin,k95stdout;
@@ -15878,6 +15890,7 @@ wrtch(unsigned short ch) {
vta.wyseattr = FALSE ;
cell.c = ch;
cell.video_attr = attribute;
+ vta.erased = FALSE;
if ( (ISWYSE(tt_type_mode) ||
ISTVI(tt_type_mode) ||
@@ -15908,6 +15921,7 @@ wrtch(unsigned short ch) {
/* Retrieve the attributes of the new position */
vta = VscrnGetVtCharAttr( vmode,x-1,y-1 ) ;
+ vta.erased = FALSE;
if ( !tt_hidattr )
vta.wyseattr = FALSE ;
}
@@ -17243,7 +17257,7 @@ ComputeColorFromAttr( int mode, cell_video_attr_t colorattr, USHORT vtattr )
_vtattr = vtattr;
_decstglt = decstglt;
- if (vtattr == VT_CHAR_ATTR_NORMAL)
+ if (vtattr == VT_CHAR_ATTR_NORMAL || vtattr == VT_CHAR_ATTR_ERASED)
goto done;
if (!(vtattr & WY_CHAR_ATTR) || tt_hidattr)
@@ -17499,7 +17513,7 @@ static bool private=FALSE;
static bool ansiext=FALSE;
static bool zdsext=FALSE;
static bool kermext=FALSE;
-static vtattrib blankattrib={0,0,0,0,0,0,0,0,0,0};
+static vtattrib blankattrib={0,0,0,0,0,0,0,0,0,0,0,1 /* erased */,0,0};
void
vtcsi(void)
@@ -17663,6 +17677,7 @@ vtcsi(void)
attrib.graphic = FALSE ;
attrib.wyseattr = FALSE ;
attrib.crossedout = FALSE ;
+ attrib.erased = FALSE;
attrib.hyperlink = FALSE;
attrib.linkid = 0;
@@ -18275,6 +18290,12 @@ vtcsi(void)
for ( x=0; xvt_char_attrs[pn[2]+x-1];
+ if (a == VT_CHAR_ATTR_ERASED) {
+ /* In rectangle mode, unoccuped (erased)
+ * character positions are changed to
+ * blanks (become unerased) */
+ a = VT_CHAR_ATTR_NORMAL;
+ }
switch ( pn[z] ) {
case 0:
a = VT_CHAR_ATTR_NORMAL;
@@ -18314,6 +18335,11 @@ vtcsi(void)
for ( x = (y==0 ? pn[2] - 1 : 0);
x < ((y==h-1) ? pn[4] : VscrnGetWidth(VTERM));
x++ ) {
+ if (line->vt_char_attrs[x] == VT_CHAR_ATTR_ERASED) {
+ /* In stream mode, DECCARA doesn't affect
+ * unoccupied (erased) character positions */
+ continue;
+ }
for ( z=5; z<=k; z++ ) {
USHORT a = line->vt_char_attrs[x];
switch ( pn[z] ) {
@@ -18397,6 +18423,12 @@ vtcsi(void)
for ( x=0; xvt_char_attrs[pn[2]+x-1];
+ if (a == VT_CHAR_ATTR_ERASED) {
+ /* In rectangle mode, unoccuped (erased)
+ * character positions are changed to
+ * blanks (become unerased) */
+ a = VT_CHAR_ATTR_NORMAL;
+ }
if (pn[z] == 0 || pn[z] == 1) {
if ( a & VT_CHAR_ATTR_BOLD )
a &= ~VT_CHAR_ATTR_BOLD;
@@ -18431,6 +18463,11 @@ vtcsi(void)
for ( x = (y==0 ? pn[2] - 1 : 0);
x < ((y==h-1) ? pn[4] : VscrnGetWidth(VTERM));
x++ ) {
+ if (line->vt_char_attrs[x] == VT_CHAR_ATTR_ERASED) {
+ /* In stream mode, DECRARA doesn't affect
+ * unoccupied (erased) character positions */
+ continue;
+ }
for ( z=5; z<=k; z++ ) {
USHORT a = line->vt_char_attrs[x];
if (pn[z] == 0 || pn[z] == 1) {
@@ -21262,6 +21299,7 @@ vtcsi(void)
attrib.graphic = FALSE ;
attrib.dim = FALSE ;
attrib.crossedout = FALSE ;
+ attrib.erased = FALSE;
attrib.wyseattr = FALSE ;
attrib.hyperlink = FALSE;
attrib.linkid = 0;
@@ -21514,6 +21552,7 @@ vtcsi(void)
attrib.wyseattr = FALSE ;
attrib.hyperlink = FALSE;
attrib.crossedout = FALSE;
+ attrib.erased = FALSE;
attrib.linkid = 0;
sco8bit = FALSE ;
@@ -23281,6 +23320,7 @@ vtcsi(void)
int start, end, width;
blankvcell.c = ' ' ;
blankvcell.video_attr = geterasecolor(VTERM) ;
+ /* TODO: attrib.erased = TRUE; ? */
start = end = wherex[VTERM];
width = VscrnGetWidth(VTERM);
if (start > 1) {
diff --git a/kermit/k95/ckocon.c b/kermit/k95/ckocon.c
index a8fc4b8f..15cb1b6b 100644
--- a/kermit/k95/ckocon.c
+++ b/kermit/k95/ckocon.c
@@ -764,6 +764,7 @@ void
cleartermpage( BYTE vmode, int page ) {
int x,y ;
videoline * line ;
+ cell_video_attr_t erasecolor = geterasecolor(vmode) ;
for ( y = 0 ; y < VscrnGetHeight(vmode) ; y++ ) {
line = VscrnGetPageLineFromTop(vmode,y,page) ;
@@ -771,8 +772,8 @@ cleartermpage( BYTE vmode, int page ) {
line->vt_line_attr = VT_LINE_ATTR_NORMAL ;
for ( x = 0 ; x < MAXTERMCOL ; x++ ) {
line->cells[x].c = ' ' ;
- line->cells[x].video_attr = vmode == VTERM ? attribute : colorcmd ;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ line->cells[x].video_attr = erasecolor;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
}
}
lgotoxy(vmode,1, 1);
diff --git a/kermit/k95/ckocon.h b/kermit/k95/ckocon.h
index d36bbf7e..e614fc6e 100644
--- a/kermit/k95/ckocon.h
+++ b/kermit/k95/ckocon.h
@@ -1250,6 +1250,7 @@ typedef struct _vtattrib { /* Character (SGR) attributes, 1 bit each */
unsigned wyseattr:1; /* Wyse Attribute */
unsigned italic:1; /* Italic */
unsigned crossedout:1; /* Crossed out */
+ unsigned erased:1; /* Erased cell */
unsigned hyperlink:1; /* Hyperlink */
unsigned short linkid; /* Hyperlink Index */
} vtattrib ;
@@ -1269,14 +1270,28 @@ typedef struct _vtattrib { /* Character (SGR) attributes, 1 bit each */
#define VT_CHAR_ATTR_GRAPHIC ((USHORT) 0x0040)
#define VT_CHAR_ATTR_DIM ((USHORT) 0x0080)
#define WY_CHAR_ATTR ((USHORT) 0x0100)
-#define KUI_CHAR_ATTR_UPPER_HALF ((USHORT) 0x0200)
-#define KUI_CHAR_ATTR_LOWER_HALF ((USHORT) 0x0400)
+#define VT_CHAR_ATTR_ERASED ((USHORT) 0x0200)
+#define VT_CHAR_RESERVED_3 ((USHORT) 0x0400) /* Unused */
#define VT_CHAR_ATTR_ITALIC ((USHORT) 0x0800)
#define VT_CHAR_ATTR_HYPERLINK ((USHORT) 0x1000)
#define VT_CHAR_ATTR_CROSSEDOUT ((USHORT) 0x2000)
/* These three are available for use */
-#define VT_CHAR_RESERVED_2 ((USHORT) 0x4000) /* Doubly-underlined */
-#define VT_CHAR_RESERVED_1 ((USHORT) 0x8000)
+#define VT_CHAR_RESERVED_2 ((USHORT) 0x4000) /* Unused */
+#define VT_CHAR_RESERVED_1 ((USHORT) 0x8000) /* Unused */
+
+/* In the future:
+ * - The three reserved fields will probably together become the three-bit
+ * underline style for which there are six options (none, normal, double,
+ * dashed, dotted and wavy) and two unused values.
+ * - VT_CHAR_ATTR_UNDERLINE may become doubly-underlined as XTerm tracks that
+ * separately from singly underlined (a character can be both singly and
+ * doubly underlined at the same time, with doubly underlined taking
+ * priority). In this case, doubly-underlined may be removed from the above
+ * 3-bit field resulting in three unused values there.
+ * - If an extra attribute field is required for something, VT_CHAR_ERASED
+ * could be moved to one of the unused underline styles as ERASED is
+ * mutually exclusive with underline (and all other attributes).
+ */
#define VT_LINE_ATTR_NORMAL ((USHORT) 0x00)
#define VT_LINE_ATTR_DOUBLE_WIDE ((USHORT) 0x01)
diff --git a/kermit/k95/ckowys.c b/kermit/k95/ckowys.c
index c34af2cc..54bafe85 100644
--- a/kermit/k95/ckowys.c
+++ b/kermit/k95/ckowys.c
@@ -2049,6 +2049,7 @@ wyseascii( int ch )
x = wherex[VTERM]-1 ;
vta.unerasable = TRUE ;
+ vta.erased = FALSE;
for ( y=wherey[VTERM]-1 ; y
Date: Tue, 2 Dec 2025 21:05:13 +1300
Subject: [PATCH 3/4] Fix build error
---
kermit/k95/ckoco2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kermit/k95/ckoco2.c b/kermit/k95/ckoco2.c
index 83b8c119..6a67ff9b 100644
--- a/kermit/k95/ckoco2.c
+++ b/kermit/k95/ckoco2.c
@@ -2045,7 +2045,7 @@ USHORT vtattrib_to_int(vtattrib vta) {
(vta.unerasable ? VT_CHAR_ATTR_PROTECTED : 0) |
(vta.graphic ? VT_CHAR_ATTR_GRAPHIC : 0) |
(vta.crossedout ? VT_CHAR_ATTR_CROSSEDOUT: 0) |
- (att.erased ? VT_CHAR_ATTR_ERASED : 0) |
+ (vta.erased ? VT_CHAR_ATTR_ERASED : 0) |
(vta.hyperlink ? VT_CHAR_ATTR_HYPERLINK : 0) |
(vta.wyseattr ? WY_CHAR_ATTR : 0) ;
From 77d77e0d903ef5c485f88e52df8a64eee387b699 Mon Sep 17 00:00:00 2001
From: David Goodwin
Date: Tue, 2 Dec 2025 21:34:50 +1300
Subject: [PATCH 4/4] Fix a pair of problems with DECFRA
It wasn't checking that the character specified was within the valid range, and it wasn't translating from the currently selected remote character set to the local one. When in UTF-8, no translation is applied and the entire base multilingual plane is allowed.
Also adjusted DECERA so that it only erases non-erased cells.
---
doc/changes.md | 2 ++
doc/ctlseqs.xml | 15 +++++++++++----
kermit/k95/ckoco3.c | 42 ++++++++++++++++++++++++++++++------------
kermit/k95/ckocon.h | 2 +-
4 files changed, 44 insertions(+), 17 deletions(-)
diff --git a/doc/changes.md b/doc/changes.md
index e89c6a65..d3f97b95 100644
--- a/doc/changes.md
+++ b/doc/changes.md
@@ -407,6 +407,8 @@ as part of K95 at this time, the default terminal remains VT220 for now.
width if the parameters value is 0. Any value less than 80 will now produce an
80 column terminal.
- Fixed crash writing to unopened file
+ - Fixed DECFRA accepting invalid fill character specifications
+ - Fixed DECFRA not using the selected remote character set for the fill character
## Kermit 95 v3.0 beta 7 - 27 January 2025
diff --git a/doc/ctlseqs.xml b/doc/ctlseqs.xml
index 6cab8362..6add4057 100644
--- a/doc/ctlseqs.xml
+++ b/doc/ctlseqs.xml
@@ -16252,10 +16252,17 @@ DCS $ q 3 , } ST
this will only be available to VT420 and higher emulations.
- is the character
- to fill with, while ;
- ; ;
- is the rectangle to fill
+ ;
+ ; ;
+ is the rectangle to fill.
+
+
+ is an integer referring to a
+ character in the current GL or GR character set to fill the
+ specified rectangular area. When not in UTF8 mode, it must fall
+ in the range 32-126 (GL) or 160-255 (GR). If the remote
+ character set is UTF8, then the second range is extended to
+ 160-65535 to cover the Unicode base multilingual plane.
cells[x].c = fillchar ;
- line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
+ /* If we're erasing, then ignore already erased cells. If we're just
+ * filling, then fill everything. */
+ if (line->vt_char_attrs[x] != VT_CHAR_ATTR_ERASED || !erase) {
+ line->cells[x].c = fillchar ;
+ line->cells[x].video_attr = cellcolor;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
+ }
}
}
@@ -7004,10 +7011,11 @@ selclrrect_escape( BYTE vmode, int top, int left, int bot, int right,
line = VscrnGetLineFromTop( vmode, l, FALSE ) ;
for ( x=startx ; x <= endx ; x++ )
{
- if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED ) ) {
+ if ( !(line->vt_char_attrs[x] & VT_CHAR_ATTR_PROTECTED) &&
+ line->vt_char_attrs[x] != VT_CHAR_ATTR_ERASED ) {
line->cells[x].c = fillchar ;
line->cells[x].video_attr = cellcolor;
- line->vt_char_attrs[x] = VT_CHAR_ATTR_ERASED ;
+ line->vt_char_attrs[x] = VT_CHAR_ATTR_NORMAL ;
}
}
}
@@ -19223,10 +19231,20 @@ vtcsi(void)
pn[2] = 1 ;
if ( k < 1 )
pn[1] = SP ;
- clrrect_escape( VTERM, pn[2], pn[3],
- pn[4], pn[5], pn[1] ) ;
- if (cursor_on_visible_page(VTERM)) {
- VscrnIsDirty(VTERM);
+
+ /* GL--------------------------- GR & BMP -------- */
+ if (pn[1] >= 32 && (pn[1] <= 126 || pn[1] >= 160)
+ && (pn[1] <= 255 || (tt_utf8 && pn[1] <= 65535))) {
+
+ int c = pn[1];
+ if ( !tt_utf8 )
+ c = rtolxlat(pn[1]);
+
+ clrrect_escape( VTERM, pn[2], pn[3],
+ pn[4], pn[5], c ) ;
+ if (cursor_on_visible_page(VTERM)) {
+ VscrnIsDirty(VTERM);
+ }
}
}
break;
@@ -19248,7 +19266,7 @@ vtcsi(void)
if ( k < 1 || pn[1] < 1 )
pn[1] = 1 ;
clrrect_escape( VTERM, pn[1], pn[2],
- pn[3], pn[4], SP ) ;
+ pn[3], pn[4], NUL ) ;
if (cursor_on_visible_page(VTERM)) {
VscrnIsDirty(VTERM);
}
diff --git a/kermit/k95/ckocon.h b/kermit/k95/ckocon.h
index 04d5f16a..9af22a4d 100644
--- a/kermit/k95/ckocon.h
+++ b/kermit/k95/ckocon.h
@@ -1627,7 +1627,7 @@ _PROTOTYP(void clreoscr_escape, (BYTE,CHAR));
_PROTOTYP(void clreol_escape, (BYTE,CHAR));
_PROTOTYP(void clrline_escape, (BYTE,CHAR));
_PROTOTYP(void clrcol_escape, (BYTE,CHAR));
-_PROTOTYP(void clrrect_escape, (BYTE, int, int, int, int, CHAR)) ;
+_PROTOTYP(void clrrect_escape, (BYTE, int, int, int, int, int)) ;
_PROTOTYP(void selclrtoeoln, (BYTE,CHAR));
_PROTOTYP(void selclrbol_escape, (BYTE,CHAR));
_PROTOTYP(void selclrbos_escape, (BYTE,CHAR));