-
-
Save j4james/f8f3fb5ecd066dfc722dd98fec7a5742 to your computer and use it in GitHub Desktop.
''' | |
Rectangular Area Operations | |
This is a collection of test cases for the DEC rectangular area operations. | |
For each test, the top half of the screen shows the expected output, while the | |
bottom half shows the actual output. | |
NB: The expected output is based on the documentation in the DEC STD-070 manual | |
but has not yet been confirmed on any of the actual DEC terminals. | |
''' | |
import sys | |
selected_test = sys.argv[1] if len(sys.argv) > 1 else None | |
test_attrs = '1;4;7;33;41' | |
def write(s): | |
if hasattr(sys.stdout, 'buffer'): | |
sys.stdout.buffer.write(s.encode('latin1')) | |
else: | |
sys.stdout.write(s) | |
def fill(x,y,w,h,c,attrs = ''): | |
if attrs != None: write('\033[%sm' % attrs) | |
for i in range(h): | |
write('\033[%d;%dH' % (y+i,x)) | |
write(c * w) | |
def decaln(): | |
write('\033[m\033[H\033#8') | |
class TestGroup: | |
group_number = 0 | |
def __init__(self, name, test_setup = None): | |
TestGroup.group_number += 1 | |
self.group_number = TestGroup.group_number | |
self.group_name = name | |
self.test_setup = test_setup | |
self.test_number = 0 | |
self.active_test = None | |
self.double_width_lines = None | |
def __del__(self): | |
self.end_last_test() | |
write('\033[m\033[H\033[J') | |
def new_test(self, name): | |
self.test_number += 1 | |
self.test_id = '%d.%d' % (self.group_number, self.test_number) | |
if not self.is_selected(): return False | |
self.end_last_test() | |
self.test_setup() | |
self.active_test = '%s %s: %s' % (self.test_id, self.group_name, name) | |
return True | |
def reset_double_width(self, *lines): | |
self.double_width_lines = lines | |
def is_selected(self): | |
if selected_test == None: return True | |
if selected_test == self.test_id: return True | |
return self.test_id.startswith(selected_test+'.') | |
def end_last_test(self): | |
if self.active_test: | |
write('\033[m\033[H') | |
write('%s\033[K' % self.active_test) | |
write('\033[999C\033[7DEXPECTED') | |
write('\033[13H') | |
write('%s\033[K' % self.active_test) | |
write('\033[999C\033[5DACTUAL') | |
write('\r') | |
sys.stdout.flush() | |
sys.stdin.readline() | |
if self.double_width_lines: | |
for row in self.double_width_lines: | |
write('\033[%dH\033#5' % row) | |
self.double_width_lines = None | |
self.active_test = None | |
def test_decfra(): | |
def test_setup(): | |
decaln() | |
write('\033[%sm' % test_attrs) | |
g = TestGroup('DECFRA', test_setup) | |
if g.new_test('Basic functionality'): | |
''' | |
STD070: When this control is received, the terminal fills rectangular area | |
of character positions defined by Pt, Pl, Pb, and Pr, replacing both the | |
character and the rendition present in those character positions with the | |
character defined by the decimal value of Pch and the current renditions | |
set through the SGR command. | |
''' | |
write('\033[42;16;27;22;54$x') | |
# Expected output | |
fill(27, 4, 28, 7, '*', test_attrs) | |
if g.new_test('Default parameters'): | |
''' | |
STD070: Default values are Pch = 32, Pt = 1, Pb = last-line-of-page, PI = 1, | |
and Pr = last-column-of-page. | |
''' | |
write('\033[;16;1;19;80$x') | |
write('\033[32;20$x') | |
# Expected output | |
fill(1, 4, 80, 9, ' ', test_attrs) | |
if g.new_test('Invalid parameters'): | |
''' | |
STD070: The Pch parameter may be a decimal value from 32 to 126, or from 160 | |
160 to 255. If the value of Pch is not in the above range, the command is | |
ignored. Pt must be less or equal to Pb, and Pl must be less than or equal | |
to Pr, otherwise the entire control function is ignored. | |
''' | |
write('\033[42;16;80;22;71$x') # Ignored, Pl > Pr | |
write('\033[42;22;1;16;10$x') # Ignored, Pt > Pb | |
test_chars = [31,32,126,0,160,255,145] | |
for i,ch in enumerate(test_chars): | |
write('\033[%d;%d;27;%d;54$x' % (ch,i+16,i+16)) | |
# Expected output | |
for i,ch in enumerate(test_chars): | |
if ch not in [31,145]: # 31 and 145 ignored | |
if ch == 0: ch = 32 # 0 is the same as default (32) | |
fill(27, 4+i, 28, 1, chr(ch), test_attrs) | |
if g.new_test('Origin mode'): | |
''' | |
STD070: The coordinates of the rectangular area are affected by the setting | |
of Origin Mode. This control is not otherwise affected by the margins. | |
''' | |
write('\033[17;20r') # Margins 17 to 20 | |
write('\033[?6h') # Origin Mode enabled | |
write('\033[65;6;33;8;48$x') # Clamped | |
write('\033[66;2;36;6;45$x') # Clipped | |
write('\033[?6l') # Origin Mode reset | |
write('\033[67;16;39;22;42$x') # Not Clipped | |
write('\033[r') # Margins reset | |
# Expected output | |
fill(39, 4, 4, 2, 'C', test_attrs) | |
fill(36, 6, 1, 2, 'BBBCCCCBBB', test_attrs) | |
fill(33, 8, 1, 1, 'AAABBBCCCCBBBAAA', test_attrs) | |
fill(39, 9, 4, 2, 'C', test_attrs) | |
if g.new_test('Double-width/height lines'): | |
''' | |
STD070: If the rectangular area contains both single and double width lines, | |
the area affected may not appear rectangular on the display. | |
''' | |
write('\033[16H\033#3') | |
write('\033[17H\033#4') | |
write('\033[18H\033#6') | |
write('\033[42;16;27;22;54$x') | |
# Expected output | |
fill(27, 4, 28, 7, '*', test_attrs) | |
write('\033[4H\033#3') | |
write('\033[5H\033#4') | |
write('\033[6H\033#6') | |
g.reset_double_width(4,5,6,16,17,18) | |
if g.new_test('Overflowing page boundary'): | |
''' | |
STD070: If a value exceeds the width or height of the active page, it is | |
treated as the width or height of the active page. | |
''' | |
write('\033[42;19;41;9999;9999$x') | |
# Expected output | |
fill(41, 7, 40, 6, '*', test_attrs) | |
if g.new_test('Character set translation'): | |
''' | |
STD070: The decimal value refers to the character in the current GL and GR | |
"in-use" character table, which is used to fill the specified rectangular | |
area. | |
''' | |
write('\033(0') | |
write('\033[110;16;27;22;54$x') | |
# Expected output | |
fill(27, 4, 28, 7, 'n', test_attrs) | |
write('\033(B') | |
pass | |
def test_decera(): | |
erase_attrs = '0;45' | |
def test_setup(): | |
fill(1, 1, 80, 24, 'E', test_attrs) | |
write('\033[0;4;7;36;45m') | |
g = TestGroup('DECERA', test_setup) | |
if g.new_test('Basic functionality'): | |
''' | |
STD070: When this control is received, the terminal erases the rectangular | |
area of character positions defined by Pt, Pl, Pb, and Pr, clearing all | |
character attributes. When an area is erased, all characters are replaced | |
with the Space character (2/0). | |
''' | |
write('\033[16;27;22;54$z') | |
# Expected output | |
fill(27, 4, 28, 7, ' ', erase_attrs) | |
if g.new_test('Default parameters'): | |
''' | |
STD070: Default values are Pt = 1, Pb = last-line-of-page, Pl = 1, and Pr = | |
last-column-of-page. | |
''' | |
write('\033[16$z') | |
# Expected output | |
fill(1, 4, 80, 9, ' ', erase_attrs) | |
if g.new_test('Invalid parameters'): | |
''' | |
STD070: Pt must be less or equal to Pb, and Pl must be less than or equal | |
to Pr, otherwise the entire control function is ignored. | |
''' | |
write('\033[16;80;22;71$z') # Ignored, Pl > Pr | |
write('\033[22;1;16;10$z') # Ignored, Pt > Pb | |
write('\033[18;38;20;43$z') | |
# Expected output | |
fill(38, 6, 6, 3, ' ', erase_attrs) | |
if g.new_test('Origin mode'): | |
''' | |
STD070: The coordinates of the rectangular area are affected by the setting | |
of Origin Mode. This control is not otherwise affected by the margins. | |
''' | |
write('\033[17;20r') # Margins 17 to 20 | |
write('\033[?6h') # Origin Mode enabled | |
write('\033[6;33;8;48$z') # Clamped | |
write('\033[2;36;6;45$z') # Clipped | |
write('\033[?6l') # Origin Mode reset | |
write('\033[16;39;22;42$z') # Not Clipped | |
write('\033[r') # Margins reset | |
# Expected output | |
fill(39, 4, 4, 2, ' ', erase_attrs) | |
fill(36, 6, 10, 2, ' ', None) | |
fill(33, 8, 16, 1, ' ', None) | |
fill(39, 9, 4, 2, ' ', None) | |
if g.new_test('Double-width/height lines'): | |
''' | |
STD070: If the rectangular area contains both single and double width lines, | |
the area affected may not appear rectangular on the display. | |
''' | |
write('\033[16H\033#3') | |
write('\033[17H\033#4') | |
write('\033[18H\033#6') | |
write('\033[16;27;22;54$z') | |
# Expected output | |
fill(27, 4, 28, 7, ' ', erase_attrs) | |
write('\033[4H\033#3') | |
write('\033[5H\033#4') | |
write('\033[6H\033#6') | |
g.reset_double_width(4,5,6,16,17,18) | |
if g.new_test('Overflowing page boundary'): | |
''' | |
STD070: If a value exceeds the width or height of the active page, it is | |
treated as the width or height of the active page. | |
''' | |
write('\033[19;41;9999;9999$z') | |
# Expected output | |
fill(41, 7, 40, 6, ' ', erase_attrs) | |
def test_decsera(): | |
def diagonal(y): | |
for i in range(9): | |
write('\033[%d;%dHEE' % (y+i,34+i*2)) | |
def test_setup(): | |
fill(1, 1, 80, 24, 'E', test_attrs) | |
write('\033[1"q') | |
diagonal(16) | |
write('\033[0"q') | |
write('\033[m') | |
g = TestGroup('DECSERA', test_setup) | |
if g.new_test('Basic functionality'): | |
''' | |
STD070: When a character is erased, it is replaced with the Space character | |
(2/0). This sequence does not change or clear any video character attributes | |
(SGR or DECSCA) | |
''' | |
write('\033[16;27;22;54${') | |
# Expected output | |
fill(27, 4, 28, 7, ' ', test_attrs) | |
diagonal(4) | |
if g.new_test('Default parameters'): | |
''' | |
STD070: Default values are Pt = 1, Pb = last-line-of-page, Pl = 1, and Pr = | |
last-column-of-page. | |
''' | |
write('\033[16${') | |
# Expected output | |
fill(1, 4, 80, 9, ' ', test_attrs) | |
diagonal(4) | |
if g.new_test('Invalid parameters'): | |
''' | |
STD070: Pt must be less or equal to Pb, and Pl must be less than or equal | |
to Pr, otherwise the entire control function is ignored. | |
''' | |
write('\033[16;80;22;71${') # Ignored, Pl > Pr | |
write('\033[22;1;16;10${') # Ignored, Pt > Pb | |
write('\033[18;38;20;43${') | |
# Expected output | |
fill(38, 6, 6, 3, ' ', test_attrs) | |
diagonal(4) | |
if g.new_test('Origin mode'): | |
''' | |
STD070: The coordinates of the rectangular area are affected by the setting | |
of Origin Mode. This control is not otherwise affected by the margins. | |
''' | |
write('\033[17;20r') # Margins 17 to 20 | |
write('\033[?6h') # Origin Mode enabled | |
write('\033[6;33;8;48${') # Clamped | |
write('\033[2;36;6;45${') # Clipped | |
write('\033[?6l') # Origin Mode reset | |
write('\033[16;39;22;42${') # Not Clipped | |
write('\033[r') # Margins reset | |
# Expected output | |
fill(39, 4, 4, 2, ' ', test_attrs) | |
fill(36, 6, 10, 2, ' ', None) | |
fill(33, 8, 16, 1, ' ', None) | |
fill(39, 9, 4, 2, ' ', None) | |
diagonal(4) | |
if g.new_test('Double-width/height lines'): | |
''' | |
STD070: If the rectangular area contains both single and double width lines, | |
the area affected may not appear rectangular on the display. | |
''' | |
write('\033[16H\033#3') | |
write('\033[17H\033#4') | |
write('\033[18H\033#6') | |
write('\033[16;27;22;54${') | |
# Expected output | |
fill(27, 4, 28, 7, ' ', test_attrs) | |
diagonal(4) | |
write('\033[4H\033#3') | |
write('\033[5H\033#4') | |
write('\033[6H\033#6') | |
g.reset_double_width(4,5,6,16,17,18) | |
if g.new_test('Overflowing page boundary'): | |
''' | |
STD070: If a value exceeds the width or height of the active page, it is | |
treated as the width or height of the active page. | |
''' | |
write('\033[19;41;9999;9999${') | |
# Expected output | |
fill(41, 7, 40, 6, ' ', test_attrs) | |
diagonal(4) | |
def test_deccra(): | |
def pattern(x, y, h = 3, clipped = False): | |
for i in range(h): | |
write('\033[%d;%dH***' % (y+i,x)) | |
if not clipped or i == 1: | |
write('\033[22mooo\033[1m') | |
def test_setup(): | |
decaln() | |
write('\033[%sm' % test_attrs) | |
pattern(38, 18) | |
g = TestGroup('DECCRA', test_setup) | |
if g.new_test('Basic functionality'): | |
''' | |
STD070: the terminal copies the character values and attributes in the | |
specified rectangular area of character positions defined by Pts, Pls, Pbs, | |
and Prs on the specified page Pps, to the area specified by the coordinate | |
Ptd, Pld on the specified page Ppd. | |
''' | |
write('\033[18;38;20;43;1;20;42;1$v') | |
write('\033[20;42;22;47;1;16;34;1$v') | |
# Expected output | |
pattern(38, 6) | |
pattern(42, 8) | |
pattern(34, 4) | |
if g.new_test('Default parameters'): | |
''' | |
STD070: Default values are Pts = 1, Pls = 1, Pbs = last-line-of-page, Prs = | |
last-column-of-page, Pps = 1, Ptd = 1, Pld = 1, and Ppd = 1. | |
''' | |
write('\033[;;;;;2;3$v') | |
write('\033[3;5;21;45$v') | |
# Expected output | |
pattern(40, 7) | |
pattern(36, 5) | |
if g.new_test('Invalid parameters'): | |
''' | |
STD070: Pts must be less or equal to Pbs, and Pls must be less than or equal | |
to Prs, otherwise the entire control function is ignored. | |
''' | |
write('\033[18;38;20;43;1;20;42;1$v') | |
write('\033[20;42;22;47;1;16;34;1$v') | |
write('\033[16;47;23;34;1;16;1;1$v') # Ignored, Pls > Prs | |
write('\033[23;34;16;47;1;16;67;1$v') # Ignored, Pbs > Pts | |
# Expected output | |
pattern(38, 6) | |
pattern(42, 8) | |
pattern(34, 4) | |
if g.new_test('Origin mode'): | |
''' | |
STD070: The coordinates of the rectangular area are affected by the setting | |
of Origin Mode. This control is not otherwise affected by the margins. | |
''' | |
write('\033[17;19r') # Margins 17 to 19 | |
write('\033[?6h') # Origin Mode enabled | |
write('\033[6;38;8;43;1;1;28;1$v') # Clamped Src | |
write('\033[3;38;5;43;1;2;29;1$v') # Clipped | |
write('\033[2;38;4;43;1;5;30;1$v') # Clamped Dst | |
write('\033[?6l') # Origin Mode reset | |
write('\033[18;38;20;43;1;19;47;1$v') # Not Clipped | |
write('\033[r') # Margins reset | |
# Expected output | |
pattern(38, 6) | |
pattern(47, 7) | |
pattern(28, 5, h=1) | |
pattern(29, 6, h=1) | |
pattern(30, 7, h=1) | |
if g.new_test('Double-width/height lines'): | |
''' | |
STD070: If the rectangular area contains both single and double width lines, | |
the area copied FROM may not appear rectangular on the display. Text copied | |
to the destination area take on the line attributes of that area. | |
''' | |
write('\033[18H\033#6') | |
write('\033[20H\033#6') | |
write('\033[18;38;20;43;1;20;44;1$v') | |
write('\033[18;38;20;43;1;16;32;1$v') | |
# Expected output | |
pattern(38, 6) | |
pattern(44, 8, clipped=True) | |
pattern(32, 4, clipped=True) | |
write('\033[6H\033#6') | |
write('\033[8H\033#6') | |
g.reset_double_width(6,8,18,20) | |
if g.new_test('Overflowing page boundary'): | |
''' | |
STD070: If a value exceeds the width or height of the active page, it is | |
treated as the width or height of the active page. If the destination | |
rectangle specified is partially off of the Page, clipping of information | |
will occur. | |
''' | |
write('\033[18;38;999;999;1;17;36;1$v') | |
write('\033[17;36;999;999;1;19;40;1$v') | |
# Expected output | |
pattern(36, 5) | |
pattern(40, 7) | |
if g.new_test('Copying between pages'): | |
''' | |
STD070: the terminal copies the character values and attributes in the | |
specified rectangular area ... on the specified page Pps, to the area ... | |
on the specified page Ppd. | |
''' | |
write('\033[2 P\033[14H\033[m\033[J\033[1 P') | |
write('\033[18;38;20;43;1;18;38;2$v') | |
write('\033[16;34;22;47;2;17;39;1$v') | |
write('\033[16;34;22;47;2;15;29;1$v') | |
# Expected output | |
write('\033[%sm' % test_attrs) | |
fill(29, 3, 14, 7, ' ') | |
fill(39, 5, 14, 7, ' ') | |
write('\033[%sm' % test_attrs) | |
pattern(33, 5) | |
pattern(43, 7) | |
def test_deccara(): | |
attrs = '1;4;7' | |
def test_setup(): | |
decaln() | |
write('\033[2*x') | |
g = TestGroup('DECCARA', test_setup) | |
if g.new_test('Basic functionality'): | |
''' | |
STD070: This sequence changes the video attributes for all of the character | |
positions in a rectangular area without altering any characters in those | |
positions. | |
''' | |
write('\033[16;27;22;54;%s$r' % attrs) | |
# Expected output | |
fill(27, 4, 28, 7, 'E', attrs) | |
if g.new_test('Default parameters'): | |
''' | |
STD070: Default values are Pt = 1, Pb = last-line-of-page, Pl = 1, and Pr = | |
last-column-of-page. The default for the video attribute parameters, if no | |
valid subsequent parameter is received, is 0, which will clear all video | |
attributes in the specified area. | |
''' | |
write('\033[16;;;;%s$r' % attrs) | |
write('\033[18;27;22;54$r') | |
# Expected output | |
fill(1, 4, 80, 9, 'E', attrs) | |
fill(27, 6, 28, 5, 'E') | |
if g.new_test('Invalid parameters'): | |
''' | |
STD070: Pt must be less or equal to Pb, and Pl must be less than or equal | |
to Pr, otherwise the entire control function is ignored. | |
''' | |
write('\033[16;80;22;71;%s$r' % attrs) # Ignored, Pl > Pr | |
write('\033[22;1;16;10;%s$r' % attrs) # Ignored, Pt > Pb | |
write('\033[18;38;20;43;%s$r' % attrs) | |
# Expected output | |
fill(38, 6, 6, 3, 'E', attrs) | |
if g.new_test('Origin mode'): | |
''' | |
STD070: The coordinates of the rectangular area are affected by the current | |
setting of Origin Mode. This control is not otherwise affected by the | |
margins. | |
''' | |
write('\033[17;20r') # Margins 17 to 20 | |
write('\033[?6h') # Origin Mode enabled | |
write('\033[6;33;8;48;%s$r' % attrs) # Clamped | |
write('\033[2;36;6;45;%s$r' % attrs) # Clipped | |
write('\033[?6l') # Origin Mode reset | |
write('\033[16;39;22;42;%s$r' % attrs) # Not Clipped | |
write('\033[r') # Margins reset | |
# Expected output | |
fill(39, 4, 4, 2, 'E', attrs) | |
fill(36, 6, 10, 2, 'E', None) | |
fill(33, 8, 16, 1, 'E', None) | |
fill(39, 9, 4, 2, 'E', None) | |
if g.new_test('Double-width/height lines'): | |
''' | |
STD070: If the rectangular area contains both single and double width lines, | |
the area affected may not appear rectangular on the display. | |
''' | |
write('\033[16H\033#3') | |
write('\033[17H\033#4') | |
write('\033[18H\033#6') | |
write('\033[16;27;22;54;%s$r' % attrs) | |
# Expected output | |
fill(27, 4, 28, 7, 'E', attrs) | |
write('\033[4H\033#3') | |
write('\033[5H\033#4') | |
write('\033[6H\033#6') | |
g.reset_double_width(4,5,6,16,17,18) | |
if g.new_test('Overflowing page boundary'): | |
''' | |
STD070: If a value exceeds the width or height of the active page, it is | |
treated as the width or height of the active page. | |
''' | |
write('\033[19;41;9999;9999;%s$r' % attrs) | |
# Expected output | |
fill(41, 7, 40, 6, 'E', attrs) | |
def test_decrara(): | |
def test_setup(): | |
decaln() | |
write('\033[2*x') | |
g = TestGroup('DECRARA', test_setup) | |
if g.new_test('Basic functionality'): | |
''' | |
STD070: This sequence reverses one or more video attributes for all of the | |
character positions in a rectangular area without altering any characters in | |
those positions. | |
''' | |
write('\033[16;27;22;54;1;4;7$t') | |
# Expected output | |
fill(27, 4, 28, 7, 'E', '1;4;7') | |
if g.new_test('Default parameters'): | |
''' | |
STD070: Default values are Pt = 1, Pb = last-line-of-page, Pl = 1, and Pr = | |
last-column-of-page. | |
''' | |
write('\033[16;;;;1;4;7$t') | |
# Expected output | |
fill(1, 4, 80, 9, 'E', '1;4;7') | |
if g.new_test('Invalid parameters'): | |
''' | |
STD070: Pt must be less or equal to Pb, and Pl must be less than or equal | |
to Pr, otherwise the entire control function is ignored. There is no default | |
for the video attribute parameters, if no valid subsequent parameter is | |
received, the command is ignored. | |
''' | |
write('\033[16;80;22;71;1;4;7$t') # Ignored, Pl > Pr | |
write('\033[22;1;16;10;1;4;7$t') # Ignored, Pt > Pb | |
write('\033[16;27;17;54$t') # Ignored, no attribute parameters | |
write('\033[18;38;20;43;1;4;7$t') | |
# Expected output | |
fill(38, 6, 6, 3, 'E', '1;4;7') | |
if g.new_test('Origin mode'): | |
''' | |
STD070: The coordinates of the rectangular area are affected by the current | |
setting of Origin Mode. This control is not otherwise affected by the | |
margins. | |
''' | |
write('\033[17;20r') # Margins 17 to 20 | |
write('\033[?6h') # Origin Mode enabled | |
write('\033[6;33;8;48;1;4;7$t') # Clamped | |
write('\033[2;36;6;45;1;4;7$t') # Clipped | |
write('\033[?6l') # Origin Mode reset | |
write('\033[16;39;22;42;1;4;7$t') # Not Clipped | |
write('\033[r') # Margins reset | |
# Expected output | |
fill(39, 4, 4, 2, 'E', '1;4;7') | |
fill(36, 6, 3, 2, 'E', None) | |
fill(43, 6, 3, 2, 'E', None) | |
fill(33, 8, 3, 1, 'E', None) | |
fill(46, 8, 3, 1, 'E', None) | |
fill(39, 8, 4, 3, 'E', None) | |
if g.new_test('Double-width/height lines'): | |
''' | |
STD070: If the rectangular area contains both single and double width lines, | |
the area affected may not appear rectangular on the display. | |
''' | |
write('\033[16H\033#3') | |
write('\033[17H\033#4') | |
write('\033[18H\033#6') | |
write('\033[16;27;22;54;1;4;7$t') | |
# Expected output | |
fill(27, 4, 28, 7, 'E', '1;4;7') | |
write('\033[4H\033#3') | |
write('\033[5H\033#4') | |
write('\033[6H\033#6') | |
g.reset_double_width(4,5,6,16,17,18) | |
if g.new_test('Overflowing page boundary'): | |
''' | |
STD070: If a value exceeds the width or height of the active page, it is | |
treated as the width or height of the active page. | |
''' | |
write('\033[19;41;9999;9999;1;4;7$t') | |
# Expected output | |
fill(41, 7, 40, 6, 'E', '1;4;7') | |
def test_attributes(): | |
color = ';33;41' | |
all = '0;1;4;7'+color | |
def test_setup(): | |
decaln() | |
write('\033[2*x') # Rectangle mode | |
fill(31,18,6,3,'*',all) | |
fill(45,18,6,3,'*',None) | |
g = TestGroup('Attributes', test_setup) | |
''' | |
DECCARA: The default, if no valid subsequent parameter is received, is 0. | |
DECRARA: There is no default. If no valid subsequent parameter is received, | |
the command is ignored. | |
Both: When multiple parameters are used, they are cumulative. | |
''' | |
test_cases = [ | |
('', 'Default'), | |
('0', 'All Off / Reverse All (0)'), | |
('1', 'Increased Intensity (1)'), | |
('4', 'Underscore (4)'), | |
('7', 'Negative Image (7)'), | |
('22', 'Normal Intensity (22)'), | |
('24', 'No Underline (24)'), | |
('27', 'Positive Image (27)'), | |
('4;7', 'Underscore + Negative (4;7)'), | |
('0;7', 'All Off + Negative (0;7)'), | |
(';7', 'Default + Negative (0;7)'), | |
('7;27','Negative + Positive (7;27)'), | |
('44', 'Blue Background (44)'), | |
('32', 'Green Foreground (32)'), | |
] | |
def apply(src, add): | |
src = set([int(n) for n in src.split(';')]) | |
if add == '': add = '0' | |
for n in add.split(';'): | |
n = int(n) if n else 0 | |
if n == 0: | |
src.clear() | |
elif n == 22: | |
src.discard(1) | |
elif n in [24,27]: | |
src.discard(n-20) | |
else: | |
if n//10 == 3: src = set(i for i in src if i//10 != 3) | |
if n//10 == 4: src = set(i for i in src if i//10 != 4) | |
src.add(n) | |
return ';'.join(['0'] + [str(n) for n in src if n]) | |
def reverse(src, rev): | |
src = set([int(n) for n in src.split(';') if n != '0']) | |
if rev: | |
all_rendition = [1,2,3,4,5,6,7,8,9,21,53] | |
for n in rev.split(';'): | |
if n in ['','0']: | |
expanded = all_rendition | |
else: | |
expanded = [int(n)] | |
for n in expanded: | |
if n in src: | |
src.discard(n) | |
elif n in all_rendition: | |
src.add(n) | |
return ';'.join(['0'] + [str(n) for n in src if n]) | |
for attrs,description in test_cases: | |
if g.new_test(description): | |
semiattrs = ';'+attrs if attrs else attrs | |
write('\033[16;27;22;40%s$r' % semiattrs) | |
write('\033[16;41;22;54%s$t' % semiattrs) | |
# Expected output | |
a1 = apply('0', attrs) | |
a2 = apply(all, attrs) | |
fill(27,4,14,7,'E',a1) | |
fill(31,6,6,3,'*',a2) | |
a1 = reverse('0', attrs) | |
a2 = reverse(all, attrs) | |
fill(41,4,14,7,'E',a1) | |
fill(45,6,6,3,'*',a2) | |
def test_decsace(): | |
attrs = '1;4;7' | |
def test_setup(): | |
decaln() | |
g = TestGroup('DECSACE', test_setup) | |
if g.new_test('Basic functionality'): | |
write('\033[1*x') # Stream | |
write('\033[16;34;17;47;%s$r' % attrs); | |
write('\033[2*x') # Rectangle | |
write('\033[18;34;20;47;%s$r' % attrs); | |
write('\033[0*x') # Stream | |
write('\033[21;34;22;47;%s$r' % attrs); | |
# Expected output | |
fill(34,4,47,1,'E','0;'+attrs) | |
fill(1,5,47,1,'E',None) | |
fill(34,6,14,3,'E',None) | |
fill(34,9,47,1,'E',None) | |
fill(1,10,47,1,'E',None) | |
if g.new_test('Default parameter'): | |
write('\033[2*x') # Start with 2 (rectangle) and see if | |
write('\033[*x') # the default (stream) overrides that. | |
write('\033[16;34;22;47;%s$r' % attrs); | |
# Expected output | |
fill(34,4,47,1,'E','0;'+attrs) | |
fill(1,5,80,5,'E',None) | |
fill(1,10,47,1,'E',None) | |
if g.new_test('Invalid parameters'): | |
write('\033[1*x') | |
write('\033[15;34;17;33;%s$r' % attrs); # Ignored, Pl > Pr | |
write('\033[1*x') | |
write('\033[3*x') # Ignored, Ps > 2 | |
write('\033[17;34;19;47;%s$r' % attrs); | |
write('\033[2*x') | |
write('\033[3*x') # Ignored, Ps > 2 | |
write('\033[20;34;21;47;%s$r' % attrs); | |
# Expected output | |
fill(34,5,47,1,'E','0;'+attrs) | |
fill(1,6,80,1,'E',None) | |
fill(1,7,47,1,'E',None) | |
fill(34,8,14,2,'E',None) | |
if g.new_test('Unoccupied positions (stream)'): | |
write('\033[18;39H\033[4@') # ECH unoccupied | |
write('\033[19;39H\033[2K ') # EL unoccupied + occupied spaces | |
write('\033[20;39H\033[4@') # ECH unoccupied | |
write('\033[1*x') | |
write('\033[16;34;19;40;%s$r' % attrs); | |
write('\033[19;41;22;47;%s$t' % attrs); | |
# Expected output | |
fill(1,7,80,1,' ') | |
fill(34,4,47,1,'E','0;'+attrs) | |
fill(1,5,80,2,'E',None) | |
fill(1,8,80,2,'E',None) | |
fill(1,10,47,1,'E',None) | |
fill(39,7,4,1,' ',None) | |
fill(39,6,4,1,' ','0') | |
fill(39,8,4,1,' ',None) | |
if g.new_test('Unoccupied positions (rectangle)'): | |
write('\033[18;39H\033[4@') # ECH unoccupied | |
write('\033[19;39H\033[2K ') # EL unoccupied + occupied spaces | |
write('\033[20;39H\033[4@') # ECH unoccupied | |
write('\033[2*x') | |
write('\033[16;34;22;40;%s$r' % attrs); | |
write('\033[16;41;22;47;%s$t' % attrs); | |
# Expected output | |
fill(1,7,80,1,' ') | |
fill(34,4,14,3,'E','0;'+attrs) | |
fill(34,7,14,3,' ',None) | |
fill(34,8,14,3,'E',None) | |
fill(39,6,4,1,' ',None) | |
fill(39,8,4,1,' ',None) | |
# Try and make sure we're using ISO 2022 encoding. | |
write('\033%@') | |
sys.stdout.flush() | |
# Make sure Erase Color Mode is reset. | |
write('\033[?117l') | |
test_decfra() | |
test_decera() | |
test_decsera() | |
test_deccra() | |
test_deccara() | |
test_decrara() | |
test_attributes() | |
test_decsace() |
One can still search for the patent numbers 4791566 and 5165020 at Patent Public Search | USPTO.
IIUC, https://ppubs.uspto.gov/pubwebapp/external.html?q=(%225165020%22).pn.%20OR%20(%224791566%22).pn. should be a link to such a search, but I couldn't get that to work.
I think I've found them on Google Patents:
https://patents.google.com/patent/US4791566/en
https://patents.google.com/patent/US5165020/en
These patents do not describe how the commands and their parameters are encoded. Perhaps it can be figured out by trial and error. (A log of the communication with the official SSU software would be easier to analyze, but my AlphaStation 500 is not in good condition and lacks OpenVMS. Community licenses are available but Community License Agreement §2.e. forbids reverse engineering.)
A log of the communication with the official SSU software would be easier to analyze
Yeah, the more I think about it having a log of both sides of the communication is probably essential. There's really not that much to go on in those patents.
If you want to attempt that, there is some discussion of the the protocol here:
https://paperlined.org/apps/terminals/control_characters/TDSMP.html
They say the protocol was patented, and in theory you should be able to work it out from those patents, but the links they gave appear to be dead now. I'm sure there must be somewhere you can look them up though.