Skip to content
Snippets Groups Projects
Commit 280fbf5e authored by deuce's avatar deuce
Browse files

Add load/remote screen copies which parse the ANSI sent to the remote.

Use default of 80x24 for both.
Use the screen object to parse ANSI and keep the systems idea of the
"Current Attribute" correct.

Once this is all fixed up, I'll have everything I need for getstr()!
parent 6c1b5319
Branches
Tags
No related merge requests found
/*
* Screen object...
* This object is the estimate of what a screen looks like modeled
* as a Graphic object (Screen.graphic).
*
* The Graphic object should not be written directly, but instead,
* all data should be provided using the print() method.
*/
if (js.global.Graphic === undefined)
load("graphic.js");
if (js.global.Attribute === undefined)
load("attribute.js");
function Screen(w, h, attr, fill)
{
this.graphic = new Graphic(w, h, attr, fill);
this.escbuf = '';
this.pos = {x:0, y:0};
this.stored_pos = {x:0, y:0};
this.attr = new Attribute(7);
}
Screen.prototype.print=function(str) {
var m;
var ext;
var pb;
var ib;
var fb;
var p;
var chars;
var remain;
var i;
var tg;
var seq;
function writech(scr, ch) {
var i;
var gr;
function check_scrollup(scr) {
while (scr.pos.y >= scr.graphic.height) {
// Scroll up...
gr = scr.graphic.get(0,1,scr.graphic.width-1, scr.graphic.height-2);
scr.graphic.put(gr,0,0);
for (i=0; i<scr.graphic.width; i++) {
scr.graphic.data[i][scr.graphic.height-1].ch = scr.graphic.ch;
scr.graphic.data[i][scr.graphic.height-1].attr = scr.attr.value;
}
scr.pos.y--;
}
}
// Handle special chars.
switch(ch) {
case '\x00': // NUL is not displayed.
break;
case '\x07': // Beep is not displayed.
break;
case '\x08': // Backspace.
scr.pos.x--;
if (scr.pos.x < 0)
scr.pos.x = 0;
break;
case '\x09': // Tab
do {
scr.pos.x++;
} while(scr.pos.x % 8);
if (scr.pos.x >= scr.graphic.width)
scr.pos.x = scr.graphic.width-1;
break;
case '\x0a': // Linefeed
scr.pos.y++;
check_scrollup(scr);
break;
case '\x0c': // For feed (clear screen and home)
scr.graphic.clear();
scr.pos.x=0;
scr.pos.y=0;
break;
case '\x0d': // Carriage return
scr.pos.x = 0;
break;
default:
scr.graphic.data[scr.pos.x][scr.pos.y].ch = ch;
scr.graphic.data[scr.pos.x][scr.pos.y].attr = new Attribute(scr.attr);
scr.pos.x++;
if (scr.pos.x >= scr.graphic.width) {
scr.pos.x = 0;
scr.pos.y++;
check_scrollup(scr);
}
break;
}
}
function param_defaults(params, defaults) {
var i;
for (i=0; i<defaults.length; i++) {
if (params[i] == undefined || params[i].length == 0)
params[i]=defaults[i];
}
for (i=0; i<params.length; i++) {
if (params[i]===undefined || params[i]==='')
params[i] = 0;
else
params[i] = parseInt(params[i], 10);
}
}
// Prepend the ESC buffer to avoid failing on split ANSI
str = this.escbuf + str;
this.escbuf = '';
while((m=str.match(/^(.*?)\x1b\[([<-\?]{0,1})([0-;]*)([ -\/]*)([@-~])([\x00-\xff]*)$/)) !== null) {
chars = m[1];
ext = m[2];
pb = m[3];
ib = m[4];
fb = m[5];
remain = m[6];
seq = ext + ib + fb;
var x;
var y;
str = remain;
// Send regular chars before the sequence...
for (i=0; i<chars.length; i++)
writech(this, chars[i]);
// We don't support any : paramters... strip and ignore.
p = pb.replace(/:[^;:]*/g, '');
p = p.split(';');
switch(fb) {
case '@': // Insert character
param_defaults(p, [1]);
if (p[1] > this.graphic.width - this.pos.x)
p[1] = this.graphic.width - this.pos.x;
if (this.pos.x < this.graphic.width-1) {
tg = this.graphic.get(this.pos.x, this.pos.y, this.graphic.width-1 - p[1], this.pos.y);
tg = this.graphic.put(this.pos.x + p[1], this.pos.y, this.graphic.width - 1, this.pos.y);
}
for (x = 0; x<p[1]; x++) {
this.graphic.data[this.pos.x + x][this.pos.y].ch = this.graphic.ch;
this.graphic.data[this.pos.x + x][this.pos.y].attr = this.attr.value;
}
break;
case 'A': // Cursor Up
param_defaults(p, [1]);
this.pos.y -= p[0];
if (this.pos.y < 0)
this.pos.y = 0;
break;
case 'B': // Cursor Down
param_defaults(p, [1]);
this.pos.y += p[0];
if (this.pos.y >= this.graphic.height)
this.pos.y = this.graphic.height-1;
break;
case 'C': // Cursor Right
param_defaults(p, [1]);
this.pos.x += p[0];
if (this.pos.x >= this.graphic.width)
this.pos.x = this.graphic.width-1;
break;
case 'D': // Cursor Left
param_defaults(p, [1]);
this.pos.x -= p[0];
if (this.pos.x < 0)
this.pos.x = 0;
break;
case 'H': // Cursor position
case 'f':
param_defaults(p, [1,1]);
if (p[0] >= 0 && p[0] < this.graphic.height && p[1] >= 0 && p[1] <= this.graphic.width) {
this.pos.x = p[1];
this.pos.y = p[0];
}
break;
case 'J': // Erase in screen
param_defaults(p, [0]);
switch(p[0]) {
case 0: // Erase to end of screen...
for (x = this.pos.x; x<this.pos.width; x++) {
this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
this.graphic.data[x][this.pos.y].attr = this.attr.value;
}
for (y = this.pos.y+1; y<this.pos.height; y++) {
for (x = 0; x<this.graphic.width; x++) {
this.graphic.data[x][y].ch = this.graphic.ch;
this.graphic.data[x][y].attr = this.attr.value;
}
}
break;
case 1: // Erase to beginning of screen...
for (y = 0; y < this.pos.y; y++) {
for (x = 0; x<this.graphic.width; x++) {
this.graphic.data[x][y].ch = this.graphic.ch;
this.graphic.data[x][y].attr = this.attr.value;
}
}
for (x = 0; x<=this.pos.x; x++) {
this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
this.graphic.data[x][this.pos.y].attr = this.attr.value;
}
break;
case 2: // Erase entire screen (Most BBS terminals also move to 1/1)
this.graphic.clear();
break;
}
break;
case 'K': // Erase in line
param_defaults(p, [0]);
switch(p[0]) {
case 0: // Erase to eol
for (x = this.pos.x; x<this.pos.width; x++) {
this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
this.graphic.data[x][this.pos.y].attr = this.attr.value;
}
break;
case 1: // Erase to start of line
for (x = 0; x<=this.pos.x; x++) {
this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
this.graphic.data[x][this.pos.y].attr = this.attr.value;
}
break;
case 2: // Erase entire line
for (x = 0; x<this.graphic.width; x++) {
this.graphic.data[x][this.pos.y].ch = this.graphic.ch;
this.graphic.data[x][this.pos.y].attr = this.attr.value;
}
break;
default:
break;
}
break;
case 'P': // Delete character
param_defaults(p, [1]);
if (p[1] > this.graphic.width - this.pos.x)
p[1] = this.graphic.width - this.pos.x;
if (this.pos.x < this.graphic.width-1) {
tg = this.graphic.get(this.pos.x + p[1], this.pos.y, this.graphic.width - 1, this.pos.y);
tg = this.graphic.put(this.pos.x, this.pos.y, (this.graphic.width - 1) - p[1], this.pos.y);
}
for (x = 0; x<p[1]; x++) {
this.graphic.data[(this.width - 1) - x][this.pos.y].ch = this.graphic.ch;
this.graphic.data[(this.width - 1) - x][this.pos.y].attr = this.attr.value;
}
break;
case 'X': // Erase character
param_defaults(p, [1]);
if (p[1] > this.graphic.width - this.pos.x)
p[1] = this.graphic.width - this.pos.x;
for (x = 0; x<p[1]; x++) {
this.graphic.data[this.pos.x + x][this.pos.y].ch = this.graphic.ch;
this.graphic.data[this.pos.x + x][this.pos.y].attr = this.attr.value;
}
break;
case 'm':
param_defaults(p, [0]);
for (i=0; i<p.length; i++) {
switch(p[i]) {
case 0:
this.attr.value = this.graphic.attribute.value;
break;
case 1:
this.attr.bright = true;
break;
case 2:
case 22:
this.attr.bright = false;
break;
case 5:
case 6:
this.attr.blink = true;
break;
case 7:
tg = this.attr.bg;
this.attr.bg = this.attr.fg;
this.attr.fg = tg;
break;
case 8:
this.attr.fg = this.attr.bg;
break;
case 25:
this.attr.blink = false;
break;
case 30:
this.attr.fg = Attribute.BLACK;
break;
case 31:
this.attr.fg = Attribute.RED;
break;
case 32:
this.attr.fg = Attribute.GREEN;
break;
case 33:
this.attr.fg = Attribute.YELLOW;
break;
case 34:
this.attr.fg = Attribute.BLUE;
break;
case 35:
this.attr.fg = Attribute.MAGENTA;
break;
case 36:
this.attr.fg = Attribute.CYAN;
break;
case 37:
this.attr.fg = Attribute.WHITE;
break;
case 40:
this.attr.bg = Attribute.BLACK;
break;
case 41:
this.attr.bg = Attribute.RED;
break;
case 42:
this.attr.bg = Attribute.GREEN;
break;
case 43:
this.attr.bg = Attribute.YELLOW;
break;
case 44:
this.attr.bg = Attribute.BLUE;
break;
case 45:
this.attr.bg = Attribute.MAGENTA;
break;
case 46:
this.attr.bg = Attribute.CYAN;
break;
case 47:
this.attr.bg = Attribute.WHITE;
break;
}
}
break;
case 's':
this.saved_pos.x = this.pos.x;
this.saved_pos.y = this.pos.y;
break;
case 'u':
this.pos.x = this.saved_pos.x;
this.pos.y = this.saved_pos.y;
break;
// Still TODO...
case 'n': // Device status report... no action from this object.
case 'Z': // Back tabulate
case 'S': // Scroll up
case 'T': // Scroll down
case 'L': // Insert line
case 'M': // Delete line (also ANSI music!)
default:
log("Sent unsupported ANSI sequence '"+ext+pb+ib+fb+"' please let shurd@sasktel.net net know about this so it can be fixed.");
}
}
if ((m=str.match(/^(.*?)(\x1b(\[([<-\?]{0,1})([0-;]*)([ -\/]*)([@-~])?)?)$/)) !== null) {
str = m[1];
this.escbuf = m[2];
}
// Send regular chars before the sequence...
for (i=0; i<str.length; i++)
writech(this, str[i]);
};
js.load_path_list.unshift(js.exec_dir+"/dorkit/");
if (js.global.system !== undefined)
js.load_path_list.unshift(system.exec_dir+"/dorkit/");
load("attribute.js");
load("graphic.js");
load("screen.js");
var dk = {
console:{
......@@ -73,14 +72,25 @@ var dk = {
x:1, // Current column (1-based)
y:1, // Current row (1-based)
attr:new Attribute(7), // Current attribute
_attr:new Attribute(7),
get attr() {
return this._attr;
},
set attr(val) {
var n = new Attribute(val);
this.print(n.ansi(this._attr));
this._attr = n;
},
ansi:true, // ANSI support is enabled
charset:'cp437', // Supported character set
local:true, // True if writes should go to the local screen
remote:true, // True if writes should go to the remote terminal
rows:undefined, // Rows in users terminal
cols:undefined, // Columns in users terminal
rows:24, // Rows in users terminal
cols:80, // Columns in users terminal
keybuf:'',
local_screen:new Screen(80, 24, 7, ' '),
remote_screen:new Screen(80, 24, 7, ' '),
/*
* Returns a string with ^A codes converted to ANSI or stripped
......@@ -198,6 +208,7 @@ var dk = {
* sets the current attribute to 7
*/
clear:function() {
this.attr=7;
if (this.local)
this.local_io.clear();
if (this.remote)
......@@ -232,19 +243,29 @@ var dk = {
* or undefined on error (ie: invalid block specified).
*/
getblock:function(sx,sy,ex,ey) {
return this.remote_screen.graphic.get(sx,sy,ex,ey);
},
/*
* Writes a string unmodified.
* TODO: This needs to parse ANSI and set attr...
*/
print:function(string) {
var m;
if (this.local)
if (this.local) {
if (this.local_screen !== undefined) {
this.local_screen.print(string);
this._attr.value = this.local_screen.attr.value;
}
this.local_io.print(string);
if (this.remote)
}
if (this.remote) {
if (this.remote_screen !== undefined) {
this.remote_screen.print(string);
this.attr.value = this.remote_screen.attr.value;
}
this.remote_io.print(string);
}
},
/*
......@@ -258,14 +279,14 @@ var dk = {
* Writes a string after parsing ^A codes.
*/
aprint:function(string) {
this.println(this.parse_ctrla(line));
this.println(this.parse_ctrla(line, this.attr));
},
/*
* Writes a string after parsing ^A codes and appends a "\r\n".
*/
aprintln:function(line) {
this.println(this.parse_ctrla(line));
this.println(this.parse_ctrla(line, this.attr));
},
/*
......@@ -455,6 +476,10 @@ var dk = {
if(this.console.waitkey(500)) {
if (this.console.getkey() == this.console.key.POSITION_REPORT) {
// TODO: Should we trust the drop file on number of rows?
if (this.console.cols != this.console.last_pos.x || this.console.rows != this.console.last_pos.y) {
this.console.remote_screen = new Screen(this.console.last_pos.x, this.console.last_pos.y, 7, ' ');
this.console.local_screen = new Screen(this.console.last_pos.x, this.console.last_pos.y, 7, ' ');
}
this.console.cols = this.console.last_pos.x;
this.console.rows = this.console.last_pos.y;
this.console.ansi = true;
......@@ -468,6 +493,7 @@ var dk = {
parse_dropfile:function(path) {
var f = new File(path);
var df;
var rows;
if (!f.open("r"))
return false;
......@@ -511,7 +537,12 @@ var dk = {
this.codepage = '7-bit';
break;
}
this.rows = parseInt(df[20], 10);
rows = parseInt(df[20], 10);
if (rows != this.console.rows) {
this.console.remote_screen = new Screen(this.console.cols, rows, 7, ' ');
this.console.local_screen = new Screen(this.console.cols, rows, 7, ' ');
}
this.rows = rows;
this.user.expert_mode = (df[21].toUpperCase === 'Y') ? true : false;
this.user.conference = df[22].split(/\s*,\s*/);
this.user.curr_conference = parseInt(df[23]);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment