/*
* This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
*
* (c) Matthias L. Jugel, Marcus Mei�ner 1996-2005. All Rights Reserved.
*
* Please visit http://javatelnet.org/ for updates and contact.
*
* --LICENSE NOTICE--
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* --LICENSE NOTICE--
*
*/
package de.mud.terminal;
import java.util.Arrays;
import android.util.Log;
/**
* Implementation of a Video Display Unit (VDU) buffer. This class contains all
* methods to manipulate the buffer that stores characters and their attributes
* as well as the regions displayed.
*
* @author Matthias L. Jugel, Marcus Mei�ner
* @version $Id: VDUBuffer.java 503 2005-10-24 07:34:13Z marcus $
*/
public class VDUBuffer {
/** The current version id tag */
public final static String ID = "$Id: VDUBuffer.java 503 2005-10-24 07:34:13Z marcus $";
/** Enable debug messages. */
public final static int debug = 0;
public int height, width; /* rows and columns */
public boolean[] update; /* contains the lines that need update */
public char[][] charArray; /* contains the characters */
public int[][] charAttributes; /* contains character attrs */
public int bufSize;
public int maxBufSize; /* buffer sizes */
public int screenBase; /* the actual screen start */
public int windowBase; /* where the start displaying */
public int scrollMarker; /* marks the last line inserted */
private int topMargin; /* top scroll margin */
private int bottomMargin; /* bottom scroll margin */
// cursor variables
protected boolean showcursor = true;
protected int cursorX, cursorY;
/** Scroll up when inserting a line. */
public final static boolean SCROLL_UP = false;
/** Scroll down when inserting a line. */
public final static boolean SCROLL_DOWN = true;
/** Make character normal. */
public final static int NORMAL = 0x00;
/** Make character bold. */
public final static int BOLD = 0x01;
/** Underline character. */
public final static int UNDERLINE = 0x02;
/** Invert character. */
public final static int INVERT = 0x04;
/** Lower intensity character. */
public final static int LOW = 0x08;
/** Invisible character. */
public final static int INVISIBLE = 0x10;
/** how much to left shift the foreground color */
public final static int COLOR_FG_SHIFT = 5;
/** how much to left shift the background color */
public final static int COLOR_BG_SHIFT = 14;
/** color mask */
public final static int COLOR = 0x7fffe0; /*
* 0000 0000 0111 1111 1111 1111
* 1110 0000
*/
/** foreground color mask */
public final static int COLOR_FG = 0x3fe0; /*
* 0000 0000 0000 0000 0011 1111
* 1110 0000
*/
/** background color mask */
public final static int COLOR_BG = 0x7fc000; /*
* 0000 0000 0111 1111 1100 0000
* 0000 0000
*/
/**
* Create a new video display buffer with the passed width and height in
* characters.
*
* @param width
* the length of the character lines
* @param height
* the amount of lines on the screen
*/
public VDUBuffer(int width, int height) {
// set the display screen size
setScreenSize(width, height, false);
}
/**
* Create a standard video display buffer with 80 columns and 24 lines.
*/
public VDUBuffer() {
this(80, 24);
}
/**
* Put a character on the screen with normal font and outline. The character
* previously on that position will be overwritten. You need to call
* redraw() to update the screen.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (line)
* @param ch
* the character to show on the screen
* @see #insertChar
* @see #deleteChar
* @see #redraw
*/
public void putChar(int c, int l, char ch) {
putChar(c, l, ch, NORMAL);
}
/**
* Put a character on the screen with specific font and outline. The
* character previously on that position will be overwritten. You need to
* call redraw() to update the screen.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (line)
* @param ch
* the character to show on the screen
* @param attributes
* the character attributes
* @see #BOLD
* @see #UNDERLINE
* @see #INVERT
* @see #INVISIBLE
* @see #NORMAL
* @see #LOW
* @see #insertChar
* @see #deleteChar
* @see #redraw
*/
public void putChar(int c, int l, char ch, int attributes) {
c = checkBounds(c, 0, width - 1);
l = checkBounds(l, 0, height - 1);
charArray[screenBase + l][c] = ch;
charAttributes[screenBase + l][c] = attributes;
markLine(l, 1);
}
/**
* Get the character at the specified position.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (line)
* @see #putChar
*/
public char getChar(int c, int l) {
c = checkBounds(c, 0, width - 1);
l = checkBounds(l, 0, height - 1);
return charArray[screenBase + l][c];
}
/**
* Get the attributes for the specified position.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (line)
* @see #putChar
*/
public int getAttributes(int c, int l) {
c = checkBounds(c, 0, width - 1);
l = checkBounds(l, 0, height - 1);
return charAttributes[screenBase + l][c];
}
/**
* Insert a character at a specific position on the screen. All character
* right to from this position will be moved one to the right. You need to
* call redraw() to update the screen.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (line)
* @param ch
* the character to insert
* @param attributes
* the character attributes
* @see #BOLD
* @see #UNDERLINE
* @see #INVERT
* @see #INVISIBLE
* @see #NORMAL
* @see #LOW
* @see #putChar
* @see #deleteChar
* @see #redraw
*/
public void insertChar(int c, int l, char ch, int attributes) {
c = checkBounds(c, 0, width - 1);
l = checkBounds(l, 0, height - 1);
System.arraycopy(charArray[screenBase + l], c,
charArray[screenBase + l], c + 1, width - c - 1);
System.arraycopy(charAttributes[screenBase + l], c,
charAttributes[screenBase + l], c + 1, width - c - 1);
putChar(c, l, ch, attributes);
}
/**
* Delete a character at a given position on the screen. All characters
* right to the position will be moved one to the left. You need to call
* redraw() to update the screen.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (line)
* @see #putChar
* @see #insertChar
* @see #redraw
*/
public void deleteChar(int c, int l) {
c = checkBounds(c, 0, width - 1);
l = checkBounds(l, 0, height - 1);
if (c < width - 1) {
System.arraycopy(charArray[screenBase + l], c + 1,
charArray[screenBase + l], c, width - c - 1);
System.arraycopy(charAttributes[screenBase + l], c + 1,
charAttributes[screenBase + l], c, width - c - 1);
}
putChar(width - 1, l, (char) 0);
}
/**
* Put a String at a specific position. Any characters previously on that
* position will be overwritten. You need to call redraw() for screen
* update.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (line)
* @param s
* the string to be shown on the screen
* @see #BOLD
* @see #UNDERLINE
* @see #INVERT
* @see #INVISIBLE
* @see #NORMAL
* @see #LOW
* @see #putChar
* @see #insertLine
* @see #deleteLine
* @see #redraw
*/
public void putString(int c, int l, String s) {
putString(c, l, s, NORMAL);
}
/**
* Put a String at a specific position giving all characters the same
* attributes. Any characters previously on that position will be
* overwritten. You need to call redraw() to update the screen.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (line)
* @param s
* the string to be shown on the screen
* @param attributes
* character attributes
* @see #BOLD
* @see #UNDERLINE
* @see #INVERT
* @see #INVISIBLE
* @see #NORMAL
* @see #LOW
* @see #putChar
* @see #insertLine
* @see #deleteLine
* @see #redraw
*/
public void putString(int c, int l, String s, int attributes) {
for (int i = 0; i < s.length() && c + i < width; i++)
putChar(c + i, l, s.charAt(i), attributes);
}
/**
* Insert a blank line at a specific position. The current line and all
* previous lines are scrolled one line up. The top line is lost. You need
* to call redraw() to update the screen.
*
* @param l
* the y-coordinate to insert the line
* @see #deleteLine
* @see #redraw
*/
public void insertLine(int l) {
insertLine(l, 1, SCROLL_UP);
}
/**
* Insert blank lines at a specific position. You need to call redraw() to
* update the screen
*
* @param l
* the y-coordinate to insert the line
* @param n
* amount of lines to be inserted
* @see #deleteLine
* @see #redraw
*/
public void insertLine(int l, int n) {
insertLine(l, n, SCROLL_UP);
}
/**
* Insert a blank line at a specific position. Scroll text according to the
* argument. You need to call redraw() to update the screen
*
* @param l
* the y-coordinate to insert the line
* @param scrollDown
* scroll down
* @see #deleteLine
* @see #SCROLL_UP
* @see #SCROLL_DOWN
* @see #redraw
*/
public void insertLine(int l, boolean scrollDown) {
insertLine(l, 1, scrollDown);
}
/**
* Insert blank lines at a specific position. The current line and all
* previous lines are scrolled one line up. The top line is lost. You need
* to call redraw() to update the screen.
*
* @param l
* the y-coordinate to insert the line
* @param n
* number of lines to be inserted
* @param scrollDown
* scroll down
* @see #deleteLine
* @see #SCROLL_UP
* @see #SCROLL_DOWN
* @see #redraw
*/
public synchronized void insertLine(int l, int n, boolean scrollDown) {
l = checkBounds(l, 0, height - 1);
char cbuf[][] = null;
int abuf[][] = null;
int offset = 0;
int oldBase = screenBase;
if (l > bottomMargin) /*
* We do not scroll below bottom margin (below the
* scrolling region).
*/
return;
int top = (l < topMargin ? 0
: (l > bottomMargin ? (bottomMargin + 1 < height ? bottomMargin + 1
: height - 1)
: topMargin));
int bottom = (l > bottomMargin ? height - 1
: (l < topMargin ? (topMargin > 0 ? topMargin - 1 : 0)
: bottomMargin));
// System.out.println("l is "+l+", top is "+top+", bottom is "+bottom+", bottomargin is "+bottomMargin+", topMargin is "+topMargin);
if (scrollDown) {
if (n > (bottom - top))
n = (bottom - top);
int size = bottom - l - (n - 1);
if (size < 0)
size = 0;
cbuf = new char[size][width];
abuf = new int[size][width];
System.arraycopy(charArray, oldBase + l, cbuf, 0, bottom - l
- (n - 1));
System.arraycopy(charAttributes, oldBase + l, abuf, 0, bottom - l
- (n - 1));
System.arraycopy(cbuf, 0, charArray, oldBase + l + n, bottom - l
- (n - 1));
System.arraycopy(abuf, 0, charAttributes, oldBase + l + n, bottom
- l - (n - 1));
cbuf = charArray;
abuf = charAttributes;
} else {
try {
if (n > (bottom - top) + 1)
n = (bottom - top) + 1;
if (bufSize < maxBufSize) {
if (bufSize + n > maxBufSize) {
offset = n - (maxBufSize - bufSize);
scrollMarker += offset;
bufSize = maxBufSize;
screenBase = maxBufSize - height - 1;
windowBase = screenBase;
} else {
scrollMarker += n;
screenBase += n;
windowBase += n;
bufSize += n;
}
cbuf = new char[bufSize][width];
abuf = new int[bufSize][width];
} else {
offset = n;
cbuf = charArray;
abuf = charAttributes;
}
// copy anything from the top of the buffer (+offset) to the new
// top
// up to the screenBase.
if (oldBase > 0) {
System.arraycopy(charArray, offset, cbuf, 0, oldBase
- offset);
System.arraycopy(charAttributes, offset, abuf, 0, oldBase
- offset);
}
// copy anything from the top of the screen (screenBase) up to
// the
// topMargin to the new screen
if (top > 0) {
System.arraycopy(charArray, oldBase, cbuf, screenBase, top);
System.arraycopy(charAttributes, oldBase, abuf, screenBase,
top);
}
// copy anything from the topMargin up to the amount of lines
// inserted
// to the gap left over between scrollback buffer and screenBase
if (oldBase > 0) {
System.arraycopy(charArray, oldBase + top, cbuf, oldBase
- offset, n);
System.arraycopy(charAttributes, oldBase + top, abuf,
oldBase - offset, n);
}
// copy anything from topMargin + n up to the line linserted to
// the
// topMargin
System.arraycopy(charArray, oldBase + top + n, cbuf, screenBase
+ top, l - top - (n - 1));
System.arraycopy(charAttributes, oldBase + top + n, abuf,
screenBase + top, l - top - (n - 1));
//
// copy the all lines next to the inserted to the new buffer
if (l < height - 1) {
System.arraycopy(charArray, oldBase + l + 1, cbuf,
screenBase + l + 1, (height - 1) - l);
System.arraycopy(charAttributes, oldBase + l + 1, abuf,
screenBase + l + 1, (height - 1) - l);
}
} catch (ArrayIndexOutOfBoundsException e) {
// this should not happen anymore, but I will leave the code
// here in case something happens anyway. That code above is
// so complex I always have a hard time understanding what
// I did, even though there are comments
System.err.println("*** Error while scrolling up:");
System.err.println("--- BEGIN STACK TRACE ---");
e.printStackTrace();
System.err.println("--- END STACK TRACE ---");
System.err.println("bufSize=" + bufSize + ", maxBufSize="
+ maxBufSize);
System.err.println("top=" + top + ", bottom=" + bottom);
System.err.println("n=" + n + ", l=" + l);
System.err.println("screenBase=" + screenBase + ", windowBase="
+ windowBase);
System.err.println("oldBase=" + oldBase);
System.err.println("size.width=" + width + ", size.height="
+ height);
System.err.println("abuf.length=" + abuf.length
+ ", cbuf.length=" + cbuf.length);
System.err.println("*** done dumping debug information");
}
}
// this is a little helper to mark the scrolling
scrollMarker -= n;
for (int i = 0; i < n; i++) {
cbuf[(screenBase + l) + (scrollDown ? i : -i)] = new char[width];
abuf[(screenBase + l) + (scrollDown ? i : -i)] = new int[width];
}
charArray = cbuf;
charAttributes = abuf;
if (scrollDown)
markLine(l, bottom - l + 1);
else
markLine(top, l - top + 1);
display.updateScrollBar();
}
/**
* Delete a line at a specific position. Subsequent lines will be scrolled
* up to fill the space and a blank line is inserted at the end of the
* screen.
*
* @param l
* the y-coordinate to insert the line
* @see #deleteLine
*/
public void deleteLine(int l) {
l = checkBounds(l, 0, height - 1);
int bottom = (l > bottomMargin ? height - 1
: (l < topMargin ? topMargin : bottomMargin + 1));
int numRows = bottom - l - 1;
if (numRows > 0) {
System.arraycopy(charArray, screenBase + l + 1, charArray,
screenBase + l, numRows);
System.arraycopy(charAttributes, screenBase + l + 1,
charAttributes, screenBase + l, numRows);
}
charArray[screenBase + bottom - 1] = new char[width];
charAttributes[screenBase + bottom - 1] = new int[width];
markLine(l, bottom - l);
}
/**
* Delete a rectangular portion of the screen. You need to call redraw() to
* update the screen.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (row)
* @param w
* with of the area in characters
* @param h
* height of the area in characters
* @param curAttr
* attribute to fill
* @see #deleteChar
* @see #deleteLine
* @see #redraw
*/
public void deleteArea(int c, int l, int w, int h, int curAttr) {
//Log.i("VDU", "deleteArea:" + c + "," + l + "," + w + "," + h);
c = checkBounds(c, 0, width - 1);
l = checkBounds(l, 0, height - 1);
char cbuf[] = new char[w];
int abuf[] = new int[w];
Arrays.fill(cbuf, ' ');
for (int i = 0; i < w; i++)
abuf[i] = curAttr;
for (int i = 0; i < h && l + i < height; i++) {
System.arraycopy(cbuf, 0, charArray[screenBase + l + i], c, w);
System.arraycopy(abuf, 0, charAttributes[screenBase + l + i], c, w);
}
markLine(l, h);
}
/**
* Delete a rectangular portion of the screen. You need to call redraw() to
* update the screen.
*
* @param c
* x-coordinate (column)
* @param l
* y-coordinate (row)
* @param w
* with of the area in characters
* @param h
* height of the area in characters
* @see #deleteChar
* @see #deleteLine
* @see #redraw
*/
public void deleteArea(int c, int l, int w, int h) {
c = checkBounds(c, 0, width - 1);
l = checkBounds(l, 0, height - 1);
char cbuf[] = new char[w];
int abuf[] = new int[w];
Arrays.fill(cbuf, ' ');
for (int i = 0; i < h && l + i < height; i++) {
System.arraycopy(cbuf, 0, charArray[screenBase + l + i], c, w);
System.arraycopy(abuf, 0, charAttributes[screenBase + l + i], c, w);
}
markLine(l, h);
}
/**
* Sets whether the cursor is visible or not.
*
* @param doshow
*/
public void showCursor(boolean doshow) {
if (doshow != showcursor)
markLine(cursorY, 1);
showcursor = doshow;
}
/**
* Check whether the cursor is currently visible.
*
* @return visibility
*/
public boolean isCursorVisible() {
return showcursor;
}
/**
* Puts the cursor at the specified position.
*
* @param c
* column
* @param l
* line
*/
public void setCursorPosition(int c, int l) {
cursorX = checkBounds(c, 0, width - 1);
cursorY = checkBounds(l, 0, height - 1);
markLine(cursorY, 1);
}
/**
* Get the current column of the cursor position.
*/
public int getCursorColumn() {
return cursorX;
}
/**
* Get the current line of the cursor position.
*/
public int getCursorRow() {
return cursorY;
}
/**
* Set the current window base. This allows to view the scrollback buffer.
*
* @param line
* the line where the screen window starts
* @see #setBufferSize
* @see #getBufferSize
*/
public void setWindowBase(int line) {
if (line > screenBase)
line = screenBase;
else if (line < 0)
line = 0;
windowBase = line;
update[0] = true;
redraw();
}
/**
* Get the current window base.
*
* @see #setWindowBase
*/
public int getWindowBase() {
return windowBase;
}
/**
* Set the scroll margins simultaneously. If they're out of bounds, trim
* them.
*
* @param l1
* line that is the top
* @param l2
* line that is the bottom
*/
public void setMargins(int l1, int l2) {
if (l1 > l2)
return;
if (l1 < 0)
l1 = 0;
if (l2 > height - 1)
l2 = height - 1;
topMargin = l1;
bottomMargin = l2;
}
/**
* Set the top scroll margin for the screen. If the current bottom margin is
* smaller it will become the top margin and the line will become the bottom
* margin.
*
* @param l
* line that is the margin
*/
public void setTopMargin(int l) {
if (l > bottomMargin) {
topMargin = bottomMargin;
bottomMargin = l;
} else
topMargin = l;
if (topMargin < 0)
topMargin = 0;
if (bottomMargin > height - 1)
bottomMargin = height - 1;
}
/**
* Get the top scroll margin.
*/
public int getTopMargin() {
return topMargin;
}
/**
* Set the bottom scroll margin for the screen. If the current top margin is
* bigger it will become the bottom margin and the line will become the top
* margin.
*
* @param l
* line that is the margin
*/
public void setBottomMargin(int l) {
if (l < topMargin) {
bottomMargin = topMargin;
topMargin = l;
} else
bottomMargin = l;
if (topMargin < 0)
topMargin = 0;
if (bottomMargin > height - 1)
bottomMargin = height - 1;
}
/**
* Get the bottom scroll margin.
*/
public int getBottomMargin() {
return bottomMargin;
}
/**
* Set scrollback buffer size.
*
* @param amount
* new size of the buffer
*/
public void setBufferSize(int amount) {
if (amount < height)
amount = height;
if (amount < maxBufSize) {
char cbuf[][] = new char[amount][width];
int abuf[][] = new int[amount][width];
int copyStart = bufSize - amount < 0 ? 0 : bufSize - amount;
int copyCount = bufSize - amount < 0 ? bufSize : amount;
if (charArray != null)
System.arraycopy(charArray, copyStart, cbuf, 0, copyCount);
if (charAttributes != null)
System.arraycopy(charAttributes, copyStart, abuf, 0, copyCount);
charArray = cbuf;
charAttributes = abuf;
bufSize = copyCount;
screenBase = bufSize - height;
windowBase = screenBase;
}
maxBufSize = amount;
update[0] = true;
redraw();
}
/**
* Retrieve current scrollback buffer size.
*
* @see #setBufferSize
*/
public int getBufferSize() {
return bufSize;
}
/**
* Retrieve maximum buffer Size.
*
* @see #getBufferSize
*/
public int getMaxBufferSize() {
return maxBufSize;
}
/**
* Change the size of the screen. This will include adjustment of the
* scrollback buffer.
*
* @param w
* of the screen
* @param h
* of the screen
*/
public void setScreenSize(int w, int h, boolean broadcast) {
char cbuf[][];
int abuf[][];
int bsize = bufSize;
if (w < 1 || h < 1)
return;
if (debug > 0)
System.err.println("VDU: screen size [" + w + "," + h + "]");
if (h > maxBufSize)
maxBufSize = h;
if (h > bufSize) {
bufSize = h;
screenBase = 0;
windowBase = 0;
}
if (windowBase + h >= bufSize)
windowBase = bufSize - h;
if (screenBase + h >= bufSize)
screenBase = bufSize - h;
cbuf = new char[bufSize][w];
abuf = new int[bufSize][w];
for (int i = 0; i < bufSize; i++) {
Arrays.fill(cbuf[i], ' ');
}
if (charArray != null && charAttributes != null) {
for (int i = 0; i < bsize && i < bufSize; i++) {
System.arraycopy(charArray[i], 0, cbuf[i], 0, w < width ? w
: width);
System.arraycopy(charAttributes[i], 0, abuf[i], 0,
w < width ? w : width);
}
}
charArray = cbuf;
charAttributes = abuf;
width = w;
height = h;
topMargin = 0;
bottomMargin = h - 1;
update = new boolean[h + 1];
update[0] = true;
/*
* FIXME: ??? if(resizeStrategy == RESIZE_FONT) setBounds(getBounds());
*/
}
/**
* Get amount of rows on the screen.
*/
public int getRows() {
return height;
}
/**
* Get amount of columns on the screen.
*/
public int getColumns() {
return width;
}
/**
* Mark lines to be updated with redraw().
*
* @param l
* starting line
* @param n
* amount of lines to be updated
* @see #redraw
*/
public void markLine(int l, int n) {
l = checkBounds(l, 0, height - 1);
for (int i = 0; (i < n) && (l + i < height); i++)
update[l + i + 1] = true;
}
private int checkBounds(int value, int lower, int upper) {
if (value < lower)
return lower;
if (value > upper)
return upper;
return value;
}
/** a generic display that should redraw on demand */
protected VDUDisplay display;
public void setDisplay(VDUDisplay display) {
this.display = display;
}
/**
* Trigger a redraw on the display.
*/
protected void redraw() {
if (display != null)
display.redraw();
}
}