diff --git a/src/parser/AnsiParser.js b/src/parser/AnsiParser.js index 7444771..9a2a3b8 100644 --- a/src/parser/AnsiParser.js +++ b/src/parser/AnsiParser.js @@ -45,7 +45,6 @@ TERM.AnsiParser = function (viewer){ }; this.readByte = function (b) { - if(b == ESCAPE) { escapeCommand = []; escapeCommand.push(b); @@ -64,22 +63,18 @@ TERM.AnsiParser = function (viewer){ } else { switch(b) { case BACKSPACE: - viewer.moveBackward(1, true); - break; - + viewer.moveBackward(1, false); + break; case LINE_FEED: - viewer.carriageReturn(); viewer.moveDown(1); - break; - + break; case CARRIAGE_RETURN: viewer.carriageReturn(); - break; - + break; case FORM_FEED: viewer.eraseScreen(); viewer.reposition(0, 0); - break; + break; } } } diff --git a/src/parser/CharacterCodes.js b/src/parser/CharacterCodes.js index e1684af..8f65b06 100644 --- a/src/parser/CharacterCodes.js +++ b/src/parser/CharacterCodes.js @@ -2,7 +2,7 @@ * @author Peter Nitsch */ -const NULL = 0 +const NULL = 0; const START_OF_HEADING = 1; const START_OF_TEXT = 2; const END_OF_TEXT = 3; diff --git a/src/parser/EscapeSequencer.js b/src/parser/EscapeSequencer.js index 50e32f7..2bf32e9 100644 --- a/src/parser/EscapeSequencer.js +++ b/src/parser/EscapeSequencer.js @@ -14,7 +14,8 @@ TERM.EscapeSequencer = function (viewer){ this.init = function() { this.actionCharacterLib[ LATIN_CAPITAL_LETTER_H ] = this.cursorPosition; - this.actionCharacterLib[ LATIN_SMALL_LETTER_F ] = this.cursorPosition; + this.actionCharacterLib[ LATIN_SMALL_LETTER_F ] = this.cursorPosition; + this.actionCharacterLib[ LATIN_CAPITAL_LETTER_G ] = this.cursorPosition; this.actionCharacterLib[ LATIN_CAPITAL_LETTER_A ] = this.cursorUp; this.actionCharacterLib[ LATIN_CAPITAL_LETTER_B ] = this.cursorDown; this.actionCharacterLib[ LATIN_CAPITAL_LETTER_C] = this.cursorForward; @@ -28,10 +29,15 @@ TERM.EscapeSequencer = function (viewer){ this.actionCharacterLib[ LATIN_SMALL_LETTER_H ] = this.setMode; this.actionCharacterLib[ LATIN_SMALL_LETTER_L ] = this.resetMode; this.actionCharacterLib[ LATIN_SMALL_LETTER_P ] = this.setKeyboardStrings; - this.actionCharacterLib[ LATIN_CAPITAL_LETTER_M ] = this.scrollUp; + this.actionCharacterLib[ LATIN_CAPITAL_LETTER_M ] = this.reverseIndex; this.actionCharacterLib[ LATIN_SMALL_LETTER_R ] = this.scrollScreen; + this.actionCharacterLib[ LATIN_CAPITAL_LETTER_X ] = this.eraseChars; + this.actionCharacterLib[ LATIN_SMALL_LETTER_N ] = this.reportCursorPos; // TO DO + this.actionCharacterLib[ LESS_THAN_SIGN ] = this.unused; + this.actionCharacterLib[ GREATER_THAN_SIGN ] = this.unused; + this.actionCharacterLib[ EQUALS_SIGN ] = this.unused; this.actionCharacterLib[ LATIN_SMALL_LETTER_A ] = this.unused; this.actionCharacterLib[ LATIN_SMALL_LETTER_D ] = this.unused; this.actionCharacterLib[ LATIN_SMALL_LETTER_E ] = this.unused; @@ -39,7 +45,6 @@ TERM.EscapeSequencer = function (viewer){ this.actionCharacterLib[ LATIN_CAPITAL_LETTER_P ] = this.unused; this.actionCharacterLib[ LATIN_CAPITAL_LETTER_E ] = this.unused; this.actionCharacterLib[ LATIN_CAPITAL_LETTER_F ] = this.unused; - this.actionCharacterLib[ LATIN_CAPITAL_LETTER_X ] = this.unused; }; @@ -116,22 +121,47 @@ TERM.EscapeSequencer = function (viewer){ } column = parseInt(columnStr); + column = (column>0) ? column-1 : 0; + line = (line>0) ? line-1 : 0; + + viewer.reposition(column, line); + } else if(params.slice(2, params.indexOf(lastCharacter)).length > 0){ - lineArray = params.slice(2, params.length-1); - for( i=0; i0) ? column-1 : 0; + line = (line>0) ? line-1 : 0; + + viewer.reposition(column, line); + + } else if (lastCharacter == LATIN_CAPITAL_LETTER_G) { + columnArray = params.slice(2, params.length-1); + for( i=0; i0) ? column-1 : 0; + + viewer.repositionColumn(column); } - line = parseInt(lineStr); } - column = (column>0) ? column-1 : 0; - line = (line>0) ? line-1 : 0; - viewer.reposition(column, line); } }; + this.cursorUp = function(params) { + if (params[1] == LEFT_PARENTHESIS|| params[1] == RIGHT_PARENTHESIS ) + { + //Set Alternate char set A + return; + } var valueArray = params.slice(2, params.length-1); var valueStr = ""; for( i=0; i 0) ? parseInt(valueStr) : 1; - viewer.moveDown(value); }; - this.cursorForward = function(params) { + this.cursorForward = function(params) { + if (params[1] == LEFT_PARENTHESIS|| params[1] == RIGHT_PARENTHESIS ) + { + //Set Alternate char set C + return; + } var valueArray = params.slice(2, params.length-1); var valueStr = ""; for( i=0; i 0) ? parseInt(valueStr) : 1; + viewer.eraseChars(value); + }; + + this.eraseDisplay = function(params) { if( params[2]==DIGIT_ONE ){ viewer.eraseUp(); @@ -361,19 +422,54 @@ TERM.EscapeSequencer = function (viewer){ } }; - // Terminal functions - this.setMode = function(){ - // TO DO + this.reportCursorPos = function(params) { + if (params[2] == DIGIT_SIX){ + var x = (viewer.cursor.x / viewer.cursor.columnWidth).toString(); + var y = (viewer.cursor.y / viewer.cursor.lineHeight).toString(); + var cmd=[]; + cmd.push(ESCAPE); + cmd.push(LEFT_SQUARE_BRACKET); + for(var i=0;i= 32 && key <= 127) { + if (event.ctrlKey) { + switch (key) { + // For historic reasons, some control characters are treated specially + case /* 3 */ 51: key = 27; break; + case /* 4 */ 52: key = 28; break; + case /* 5 */ 53: key = 29; break; + case /* 6 */ 54: key = 30; break; + case /* 7 */ 55: key = 31; break; + case /* 8 */ 56: key = 127; break; + case /* ? */ 63: key = 127; break; + default: key &= 31; break; + } + TERM.socket.writeByte(key); + handled = true; + } + } + } + if (handled) { + if (event.preventDefault !== undefined ) + event.preventDefault(); + if (event.stopPropagation !== undefined ) + event.stopPropagation(); } - }, false); - }; + } + ; + var fontmap = new Image(); fontmap.onload = function (){ diff --git a/src/viewer/AnsiViewer.js b/src/viewer/AnsiViewer.js index 47510bc..ed1e97f 100644 --- a/src/viewer/AnsiViewer.js +++ b/src/viewer/AnsiViewer.js @@ -4,7 +4,6 @@ TERM.AnsiViewer = function (fontmap){ - this.cursor = new TERM.Cursor(); this.parser = new TERM.AnsiParser(this); var fontmap = fontmap; @@ -12,14 +11,27 @@ TERM.AnsiViewer = function (fontmap){ var canvas = document.getElementById("canvas"); var width = canvas.width; var height = canvas.height; + this.cursor = new TERM.Cursor(width,height); var topMargin = 1; var botMargin = 25; var ctx = canvas.getContext("2d"); var scroll = true; var _savedPosition = new TERM.Point(); + var cursorKeyAppMode = false; + var autoWrapMode = true; + var currentCursorPosX,currentCursorPosy; + var origCursorImg,blinkCursorImg; + var cursorBlink = false; + var intervalCursorFunction; this.readBytes = function (bytes) { + clearInterval(this.intervalCursorFunction); + this.undrawCursor(); this.parser.parse(bytes); + var inst = this; + this.intervalCursorFunction = setInterval(function() { + inst.drawCursor(); + }, 500); }; this.clearCanvas = function(){ @@ -53,7 +65,7 @@ TERM.AnsiViewer = function (fontmap){ this.draw(character); this.cursor.moveForward(1); - if(!this.cursor.infinitewidth && this.cursor.x + this.cursor.columnwidth > this.cursor.maxColumnwidth * this.cursor.columnwidth){ + if(autoWrapMode && !this.cursor.infiniteWidth && this.cursor.x + this.cursor.columnWidth > this.cursor.maxColumnWidth * this.cursor.columnWidth){ this.moveDown(1); this.cursor.carriageReturn(); } @@ -70,6 +82,69 @@ TERM.AnsiViewer = function (fontmap){ this.cursor.x, this.cursor.y, font.width, font.height); }; + this.drawCursor = function() { + if (this.currentCursorPosX != this.cursor.x || this.currentCursorPosY != this.cursor.y ) { + + origCursorImg = ctx.getImageData(this.cursor.x, this.cursor.y, 8, 16 ); + var pix = origCursorImg.data; + blinkCursorImg = ctx.createImageData(8, 16); + var newPix = blinkCursorImg.data; + this.currentCursorPosX = this.cursor.x; + this.currentCursorPosY = this.cursor.y; + + var bgColorR = pix[0]; + var bgColorG = pix[1]; + var bgColorB = pix[2]; + + var fgColorR,fgColorG,fgColorB; + for (var i = 256; i < 288; i+=4) { + if (pix[i] != bgColorR || pix[i+1] != bgColorG ||pix[i+2] != bgColorB) { + fgColorR=pix[i]; + fgColorG=pix[i+1]; + fgColorB=pix[i+2]; + break; + } + } + + if (i>=288) { + // we didnt find fgcolor + fgColorR=168; + fgColorG=168; + fgColorB=168; + } + + for ( var i = 0, n = pix.length; i < n; i +=4) { + if (pix[i] == bgColorR && pix[i+1] == bgColorG && pix[i+2] == bgColorB) { + newPix[i] = fgColorR; + newPix[i+1] = fgColorG; + newPix[i+2] = fgColorB; + newPix[i+3] = 255; + } else { + newPix[i] = bgColorR; + newPix[i+1] = bgColorG; + newPix[i+2] = bgColorB; + newPix[i+3] = 255; + } + + } + } + + + if (cursorBlink) { + ctx.putImageData(origCursorImg,this.cursor.x, this.cursor.y); + } else { + ctx.putImageData(blinkCursorImg,this.cursor.x, this.cursor.y); + } + cursorBlink = !cursorBlink; + }; + + this.undrawCursor = function() { + if (cursorBlink) { + ctx.putImageData(origCursorImg,this.currentCursorPosX, this.currentCursorPosY); + cursorBlink = false; + } + }; + this.carriageReturn = function() { this.cursor.carriageReturn(); }; @@ -90,8 +165,9 @@ TERM.AnsiViewer = function (fontmap){ }; this.moveDown = function(val) { - if(this.cursor.y >= this.cursor.lineheight*(botMargin-1) && scroll){ - this.scrollUp(1); + var scrollMax = Math.min(botMargin-1,this.cursor.maxLines); + if(this.cursor.y > this.cursor.lineHeight*(scrollMax-val) && scroll){ + this.scrollUp(val); } else { this.cursor.moveDown(val); } @@ -106,9 +182,22 @@ TERM.AnsiViewer = function (fontmap){ }; this.reposition = function(x, y) { - this.cursor.x = x * this.cursor.columnwidth; - this.cursor.y = y * this.cursor.lineheight; + if (x < this.cursor.maxColumns) { + this.cursor.x = x * this.cursor.columnWidth; + } else { + this.cursor.x = (this.cursor.maxColumns - 1) * this.cursor.columnWidth; + } + if (y < this.cursor.maxLines) { + this.cursor.y = y * this.cursor.lineHeight; + } else { + this.cursor.y = (this.cursor.maxLines - 1) * this.cursor.lineHeight; + } }; + + this.repositionColumn = function(x) { + this.cursor.x = x * this.cursor.columnWidth; + }; + this.restorePosition = function() { this.cursor.x = _savedPosition.x; @@ -121,41 +210,48 @@ TERM.AnsiViewer = function (fontmap){ }; this.displayCleared = function() { - ctx.fillStyle = BLACK_NORMAL; - ctx.fillRect(0, 0, this.cursor.maxColumnwidth * this.cursor.columnwidth, this.cursor.maxLineheight * this.cursor.lineheight); + ctx.fillStyle = this.cursor.backgroundColor; + ctx.fillRect(0, (topMargin - 1) * this.cursor.lineHeight, this.cursor.maxColumnWidth * this.cursor.columnWidth, ((botMargin +1) * this.cursor.lineHeight) - (topMargin * this.cursor.lineHeight) ); // calculate the height correctly, botMArgin - topMargin }; + this.eraseUp = function() { - ctx.fillStyle = BLACK_NORMAL; - ctx.fillRect(0, 0, this.cursor.maxColumnwidth * this.cursor.columnwidth, this.cursor.y); + ctx.fillStyle = this.cursor.backgroundColor; + ctx.fillRect(0, 0, this.cursor.maxColumnWidth * this.cursor.columnWidth, this.cursor.y); }; this.eraseScreen = function() { ctx.fillStyle = this.cursor.backgroundColor; - ctx.fillRect(0, 0, this.cursor.maxColumnwidth * this.cursor.columnwidth, this.cursor.maxLineheight * this.cursor.lineheight); + ctx.fillRect(0, 0, this.cursor.maxColumnWidth * this.cursor.columnWidth, this.cursor.maxLines * this.cursor.lineHeight); }; this.eraseDown = function() { - ctx.fillStyle = BLACK_NORMAL; - ctx.fillRect(0, this.cursor.y, this.cursor.maxColumnwidth * this.cursor.columnwidth, (this.cursor.maxLineheight * this.cursor.lineheight) - this.cursor.y); + ctx.fillStyle = this.cursor.backgroundColor; + ctx.fillRect(0, this.cursor.y, this.cursor.maxColumnWidth * this.cursor.columnWidth, (this.cursor.maxLines * this.cursor.lineHeight) - this.cursor.y); }; this.eraseEndOfLine = function() { - ctx.fillStyle = BLACK_NORMAL; - var w = (this.cursor.maxColumnwidth * this.cursor.columnwidth) - (this.cursor.x - this.cursor.columnwidth); - ctx.fillRect(this.cursor.x, this.cursor.y, w, this.cursor.lineheight); + ctx.fillStyle = this.cursor.backgroundColor; + var w = (this.cursor.maxColumnWidth * this.cursor.columnWidth) - (this.cursor.x - this.cursor.columnWidth); + ctx.fillRect(this.cursor.x, this.cursor.y, w, this.cursor.lineHeight); }; this.eraseStartOfLine = function() { - ctx.fillStyle = BLACK_NORMAL; - ctx.fillRect(0, this.cursor.y, this.cursor.x, this.cursor.lineheight); + ctx.fillStyle = this.cursor.backgroundColor; + ctx.fillRect(0, this.cursor.y, this.cursor.x, this.cursor.lineHeight); }; this.eraseLine = function() { - ctx.fillStyle = BLACK_NORMAL; - ctx.fillRect(0, this.cursor.y, this.cursor.maxColumnwidth * this.cursor.columnwidth, this.cursor.lineheight); + ctx.fillStyle = this.cursor.backgroundColor; + ctx.fillRect(0, this.cursor.y, this.cursor.maxColumnWidth * this.cursor.columnWidth, this.cursor.lineHeight); }; + this.eraseChars = function(val) { + ctx.fillStyle = this.cursor.backgroundColor; + var w = val * this.cursor.columnWidth; + ctx.fillRect(this.cursor.x, this.cursor.y, w, this.cursor.lineHeight); + }; + this.backgroundColorChanged = function(color) { this.cursor.backgroundColor = color; }; @@ -166,22 +262,57 @@ TERM.AnsiViewer = function (fontmap){ this.home = function() { this.cursor.x = 0; - this.cursor.y = (topMargin-1) * this.cursor.maxLineHeight; + this.cursor.y = 0; }; this.scrollScreen = function(start, end) { - topMargin = start; - botMargin = end; - - handleHome(); + if (start!=undefined && end!=undefined) { + topMargin = start; + botMargin = end; + } + this.home(); + }; + + this.reverseIndex = function() { + if (this.cursor.y <= ((topMargin - 1) * this.cursor.lineHeight)) { + this.scrollDown(1); + } else { + this.cursor.y = this.cursor.y - this.cursor.lineHeight; + } + }; + + this.scrollDown = function(val) { + var x=0; + var y= (topMargin-1)* this.cursor.lineHeight; + var width = canvas.width ; + var height = this.cursor.lineHeight * (botMargin - topMargin); + var canvasData = ctx.getImageData(x,y ,width , height ); + this.displayCleared(); + ctx.putImageData(canvasData, 0, this.cursor.lineHeight*(topMargin-1) + (val * this.cursor.lineHeight) ); }; this.scrollUp = function(val) { - var canvasData = ctx.getImageData(0, topMargin * this.cursor.lineheight, this.cursor.maxColumnwidth*this.cursor.columnwidth, this.cursor.lineheight * (botMargin-topMargin)); + var x=0; + var y= topMargin * this.cursor.lineHeight; + var width = canvas.width ; + var height = this.cursor.lineHeight * (botMargin - topMargin); + var canvasData = ctx.getImageData(x,y ,width , height ); this.displayCleared(); - ctx.putImageData(canvasData, 0, this.cursor.lineheight*(topMargin-1)); + ctx.putImageData(canvasData, 0, this.cursor.lineHeight*(topMargin-1) ); + }; + + this.cursorKeyToAppMode = function(mode) { + cursorKeyAppMode = mode; + }; + + this.setAutoWrap = function(mode) { + autoWrapMode = mode; }; this.clearCanvas(); + var inst = this; + this.intervalCursorFunction = setInterval(function() { + inst.drawCursor(); + }, 500); -}; \ No newline at end of file +}; diff --git a/src/viewer/Cursor.js b/src/viewer/Cursor.js index ad5d9af..5ab1a4c 100644 --- a/src/viewer/Cursor.js +++ b/src/viewer/Cursor.js @@ -2,16 +2,16 @@ * @author Peter Nitsch */ -TERM.Cursor = function (){ +TERM.Cursor = function (width,height){ this.foregroundColor = WHITE_NORMAL; this.backgroundColor = BLACK_NORMAL; this.position = new TERM.Point(); - this.maxColumnWidth = 80; - this.maxLineHeight = 25; this.columnWidth = 8; this.lineHeight = 16; - this.maxColumns = 80; + this.maxColumns = Math.floor(width / this.columnWidth); + this.maxColumnWidth = this.maxColumns; + this.maxLineHeight = Math.floor(height / this.lineHeight); this.infiniteWidth = false; this.infiniteHeight = false;