Home | History | Annotate | Download | only in terminal
      1 /*
      2  * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
      3  *
      4  * (c) Matthias L. Jugel, Marcus Meiner 1996-2005. All Rights Reserved.
      5  *
      6  * Please visit http://javatelnet.org/ for updates and contact.
      7  *
      8  * --LICENSE NOTICE--
      9  * This program is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU General Public License
     11  * as published by the Free Software Foundation; either version 2
     12  * of the License, or (at your option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License
     20  * along with this program; if not, write to the Free Software
     21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     22  * --LICENSE NOTICE--
     23  *
     24  */
     25 
     26 package de.mud.terminal;
     27 
     28 import java.util.Arrays;
     29 
     30 /**
     31  * Implementation of a Video Display Unit (VDU) buffer. This class contains all methods to
     32  * manipulate the buffer that stores characters and their attributes as well as the regions
     33  * displayed.
     34  *
     35  * @version $Id: VDUBuffer.java 503 2005-10-24 07:34:13Z marcus $
     36  */
     37 public class VDUBuffer {
     38 
     39   /** The current version id tag */
     40   public final static String ID = "$Id: VDUBuffer.java 503 2005-10-24 07:34:13Z marcus $";
     41 
     42   /** Enable debug messages. */
     43   public final static int debug = 0;
     44 
     45   public int height, width; /* rows and columns */
     46   public boolean[] update; /* contains the lines that need update */
     47   public char[][] charArray; /* contains the characters */
     48   public int[][] charAttributes; /* contains character attrs */
     49   public int bufSize;
     50   public int maxBufSize; /* buffer sizes */
     51   public int screenBase; /* the actual screen start */
     52   public int windowBase; /* where the start displaying */
     53   public int scrollMarker; /* marks the last line inserted */
     54 
     55   private int topMargin; /* top scroll margin */
     56   private int bottomMargin; /* bottom scroll margin */
     57 
     58   // cursor variables
     59   protected boolean showcursor = true;
     60   protected int cursorX, cursorY;
     61 
     62   /** Scroll up when inserting a line. */
     63   public final static boolean SCROLL_UP = false;
     64   /** Scroll down when inserting a line. */
     65   public final static boolean SCROLL_DOWN = true;
     66 
     67   /*
     68    * Attributes bit-field usage:
     69    *
     70    * 8421 8421 8421 8421 8421 8421 8421 8421 |||| |||| |||| |||| |||| |||| |||| |||`- Bold |||| ||||
     71    * |||| |||| |||| |||| |||| ||`-- Underline |||| |||| |||| |||| |||| |||| |||| |`--- Invert ||||
     72    * |||| |||| |||| |||| |||| |||| `---- Low |||| |||| |||| |||| |||| |||| |||`------ Invisible ||||
     73    * |||| |||| |||| ||`+-++++-+++------- Foreground Color |||| |||| |`++-++++-++------------------
     74    * Background Color |||| |||| `----------------------------- Fullwidth character
     75    * `+++-++++------------------------------- Reserved for future use
     76    */
     77 
     78   /** Make character normal. */
     79   public final static int NORMAL = 0x00;
     80   /** Make character bold. */
     81   public final static int BOLD = 0x01;
     82   /** Underline character. */
     83   public final static int UNDERLINE = 0x02;
     84   /** Invert character. */
     85   public final static int INVERT = 0x04;
     86   /** Lower intensity character. */
     87   public final static int LOW = 0x08;
     88   /** Invisible character. */
     89   public final static int INVISIBLE = 0x10;
     90   /** Unicode full-width character (CJK, et al.) */
     91   public final static int FULLWIDTH = 0x8000000;
     92 
     93   /** how much to left shift the foreground color */
     94   public final static int COLOR_FG_SHIFT = 5;
     95   /** how much to left shift the background color */
     96   public final static int COLOR_BG_SHIFT = 14;
     97   /** color mask */
     98   public final static int COLOR = 0x7fffe0; /* 0000 0000 0111 1111 1111 1111 1110 0000 */
     99   /** foreground color mask */
    100   public final static int COLOR_FG = 0x3fe0; /* 0000 0000 0000 0000 0011 1111 1110 0000 */
    101   /** background color mask */
    102   public final static int COLOR_BG = 0x7fc000; /* 0000 0000 0111 1111 1100 0000 0000 0000 */
    103 
    104   /**
    105    * Create a new video display buffer with the passed width and height in characters.
    106    *
    107    * @param width
    108    *          the length of the character lines
    109    * @param height
    110    *          the amount of lines on the screen
    111    */
    112   public VDUBuffer(int width, int height) {
    113     // set the display screen size
    114     setScreenSize(width, height, false);
    115   }
    116 
    117   /**
    118    * Create a standard video display buffer with 80 columns and 24 lines.
    119    */
    120   public VDUBuffer() {
    121     this(80, 24);
    122   }
    123 
    124   /**
    125    * Put a character on the screen with normal font and outline. The character previously on that
    126    * position will be overwritten. You need to call redraw() to update the screen.
    127    *
    128    * @param c
    129    *          x-coordinate (column)
    130    * @param l
    131    *          y-coordinate (line)
    132    * @param ch
    133    *          the character to show on the screen
    134    * @see #insertChar
    135    * @see #deleteChar
    136    * @see #redraw
    137    */
    138   public void putChar(int c, int l, char ch) {
    139     putChar(c, l, ch, NORMAL);
    140   }
    141 
    142   /**
    143    * Put a character on the screen with specific font and outline. The character previously on that
    144    * position will be overwritten. You need to call redraw() to update the screen.
    145    *
    146    * @param c
    147    *          x-coordinate (column)
    148    * @param l
    149    *          y-coordinate (line)
    150    * @param ch
    151    *          the character to show on the screen
    152    * @param attributes
    153    *          the character attributes
    154    * @see #BOLD
    155    * @see #UNDERLINE
    156    * @see #INVERT
    157    * @see #INVISIBLE
    158    * @see #NORMAL
    159    * @see #LOW
    160    * @see #insertChar
    161    * @see #deleteChar
    162    * @see #redraw
    163    */
    164 
    165   public void putChar(int c, int l, char ch, int attributes) {
    166     charArray[screenBase + l][c] = ch;
    167     charAttributes[screenBase + l][c] = attributes;
    168     if (l < height) {
    169       update[l + 1] = true;
    170     }
    171   }
    172 
    173   /**
    174    * Get the character at the specified position.
    175    *
    176    * @param c
    177    *          x-coordinate (column)
    178    * @param l
    179    *          y-coordinate (line)
    180    * @see #putChar
    181    */
    182   public char getChar(int c, int l) {
    183     return charArray[screenBase + l][c];
    184   }
    185 
    186   /**
    187    * Get the attributes for the specified position.
    188    *
    189    * @param c
    190    *          x-coordinate (column)
    191    * @param l
    192    *          y-coordinate (line)
    193    * @see #putChar
    194    */
    195   public int getAttributes(int c, int l) {
    196     return charAttributes[screenBase + l][c];
    197   }
    198 
    199   /**
    200    * Insert a character at a specific position on the screen. All character right to from this
    201    * position will be moved one to the right. You need to call redraw() to update the screen.
    202    *
    203    * @param c
    204    *          x-coordinate (column)
    205    * @param l
    206    *          y-coordinate (line)
    207    * @param ch
    208    *          the character to insert
    209    * @param attributes
    210    *          the character attributes
    211    * @see #BOLD
    212    * @see #UNDERLINE
    213    * @see #INVERT
    214    * @see #INVISIBLE
    215    * @see #NORMAL
    216    * @see #LOW
    217    * @see #putChar
    218    * @see #deleteChar
    219    * @see #redraw
    220    */
    221   public void insertChar(int c, int l, char ch, int attributes) {
    222     System.arraycopy(charArray[screenBase + l], c, charArray[screenBase + l], c + 1, width - c - 1);
    223     System.arraycopy(charAttributes[screenBase + l], c, charAttributes[screenBase + l], c + 1,
    224         width - c - 1);
    225     putChar(c, l, ch, attributes);
    226   }
    227 
    228   /**
    229    * Delete a character at a given position on the screen. All characters right to the position will
    230    * be moved one to the left. You need to call redraw() to update the screen.
    231    *
    232    * @param c
    233    *          x-coordinate (column)
    234    * @param l
    235    *          y-coordinate (line)
    236    * @see #putChar
    237    * @see #insertChar
    238    * @see #redraw
    239    */
    240   public void deleteChar(int c, int l) {
    241     if (c < width - 1) {
    242       System.arraycopy(charArray[screenBase + l], c + 1, charArray[screenBase + l], c, width - c
    243           - 1);
    244       System.arraycopy(charAttributes[screenBase + l], c + 1, charAttributes[screenBase + l], c,
    245           width - c - 1);
    246     }
    247     putChar(width - 1, l, (char) 0);
    248   }
    249 
    250   /**
    251    * Put a String at a specific position. Any characters previously on that position will be
    252    * overwritten. You need to call redraw() for screen update.
    253    *
    254    * @param c
    255    *          x-coordinate (column)
    256    * @param l
    257    *          y-coordinate (line)
    258    * @param s
    259    *          the string to be shown on the screen
    260    * @see #BOLD
    261    * @see #UNDERLINE
    262    * @see #INVERT
    263    * @see #INVISIBLE
    264    * @see #NORMAL
    265    * @see #LOW
    266    * @see #putChar
    267    * @see #insertLine
    268    * @see #deleteLine
    269    * @see #redraw
    270    */
    271   public void putString(int c, int l, String s) {
    272     putString(c, l, s, NORMAL);
    273   }
    274 
    275   /**
    276    * Put a String at a specific position giving all characters the same attributes. Any characters
    277    * previously on that position will be overwritten. You need to call redraw() to update the
    278    * screen.
    279    *
    280    * @param c
    281    *          x-coordinate (column)
    282    * @param l
    283    *          y-coordinate (line)
    284    * @param s
    285    *          the string to be shown on the screen
    286    * @param attributes
    287    *          character attributes
    288    * @see #BOLD
    289    * @see #UNDERLINE
    290    * @see #INVERT
    291    * @see #INVISIBLE
    292    * @see #NORMAL
    293    * @see #LOW
    294    * @see #putChar
    295    * @see #insertLine
    296    * @see #deleteLine
    297    * @see #redraw
    298    */
    299   public void putString(int c, int l, String s, int attributes) {
    300     for (int i = 0; i < s.length() && c + i < width; i++) {
    301       putChar(c + i, l, s.charAt(i), attributes);
    302     }
    303   }
    304 
    305   /**
    306    * Insert a blank line at a specific position. The current line and all previous lines are
    307    * scrolled one line up. The top line is lost. You need to call redraw() to update the screen.
    308    *
    309    * @param l
    310    *          the y-coordinate to insert the line
    311    * @see #deleteLine
    312    * @see #redraw
    313    */
    314   public void insertLine(int l) {
    315     insertLine(l, 1, SCROLL_UP);
    316   }
    317 
    318   /**
    319    * Insert blank lines at a specific position. You need to call redraw() to update the screen
    320    *
    321    * @param l
    322    *          the y-coordinate to insert the line
    323    * @param n
    324    *          amount of lines to be inserted
    325    * @see #deleteLine
    326    * @see #redraw
    327    */
    328   public void insertLine(int l, int n) {
    329     insertLine(l, n, SCROLL_UP);
    330   }
    331 
    332   /**
    333    * Insert a blank line at a specific position. Scroll text according to the argument. You need to
    334    * call redraw() to update the screen
    335    *
    336    * @param l
    337    *          the y-coordinate to insert the line
    338    * @param scrollDown
    339    *          scroll down
    340    * @see #deleteLine
    341    * @see #SCROLL_UP
    342    * @see #SCROLL_DOWN
    343    * @see #redraw
    344    */
    345   public void insertLine(int l, boolean scrollDown) {
    346     insertLine(l, 1, scrollDown);
    347   }
    348 
    349   /**
    350    * Insert blank lines at a specific position. The current line and all previous lines are scrolled
    351    * one line up. The top line is lost. You need to call redraw() to update the screen.
    352    *
    353    * @param l
    354    *          the y-coordinate to insert the line
    355    * @param n
    356    *          number of lines to be inserted
    357    * @param scrollDown
    358    *          scroll down
    359    * @see #deleteLine
    360    * @see #SCROLL_UP
    361    * @see #SCROLL_DOWN
    362    * @see #redraw
    363    */
    364   public synchronized void insertLine(int l, int n, boolean scrollDown) {
    365     char cbuf[][] = null;
    366     int abuf[][] = null;
    367     int offset = 0;
    368     int oldBase = screenBase;
    369 
    370     int newScreenBase = screenBase;
    371     int newWindowBase = windowBase;
    372     int newBufSize = bufSize;
    373 
    374     if (l > bottomMargin) {
    375       return;
    376     }
    377     int top =
    378         (l < topMargin ? 0 : (l > bottomMargin ? (bottomMargin + 1 < height ? bottomMargin + 1
    379             : height - 1) : topMargin));
    380     int bottom =
    381         (l > bottomMargin ? height - 1 : (l < topMargin ? (topMargin > 0 ? topMargin - 1 : 0)
    382             : bottomMargin));
    383 
    384     // System.out.println("l is "+l+", top is "+top+", bottom is "+bottom+", bottomargin is "+bottomMargin+", topMargin is "+topMargin);
    385 
    386     if (scrollDown) {
    387       if (n > (bottom - top)) {
    388         n = (bottom - top);
    389       }
    390       int size = bottom - l - (n - 1);
    391       if (size < 0) {
    392         size = 0;
    393       }
    394       cbuf = new char[size][];
    395       abuf = new int[size][];
    396 
    397       System.arraycopy(charArray, oldBase + l, cbuf, 0, bottom - l - (n - 1));
    398       System.arraycopy(charAttributes, oldBase + l, abuf, 0, bottom - l - (n - 1));
    399       System.arraycopy(cbuf, 0, charArray, oldBase + l + n, bottom - l - (n - 1));
    400       System.arraycopy(abuf, 0, charAttributes, oldBase + l + n, bottom - l - (n - 1));
    401       cbuf = charArray;
    402       abuf = charAttributes;
    403     } else {
    404       try {
    405         if (n > (bottom - top) + 1) {
    406           n = (bottom - top) + 1;
    407         }
    408         if (bufSize < maxBufSize) {
    409           if (bufSize + n > maxBufSize) {
    410             offset = n - (maxBufSize - bufSize);
    411             scrollMarker += offset;
    412             newBufSize = maxBufSize;
    413             newScreenBase = maxBufSize - height - 1;
    414             newWindowBase = screenBase;
    415           } else {
    416             scrollMarker += n;
    417             newScreenBase += n;
    418             newWindowBase += n;
    419             newBufSize += n;
    420           }
    421 
    422           cbuf = new char[newBufSize][];
    423           abuf = new int[newBufSize][];
    424         } else {
    425           offset = n;
    426           cbuf = charArray;
    427           abuf = charAttributes;
    428         }
    429         // copy anything from the top of the buffer (+offset) to the new top
    430         // up to the screenBase.
    431         if (oldBase > 0) {
    432           System.arraycopy(charArray, offset, cbuf, 0, oldBase - offset);
    433           System.arraycopy(charAttributes, offset, abuf, 0, oldBase - offset);
    434         }
    435         // copy anything from the top of the screen (screenBase) up to the
    436         // topMargin to the new screen
    437         if (top > 0) {
    438           System.arraycopy(charArray, oldBase, cbuf, newScreenBase, top);
    439           System.arraycopy(charAttributes, oldBase, abuf, newScreenBase, top);
    440         }
    441         // copy anything from the topMargin up to the amount of lines inserted
    442         // to the gap left over between scrollback buffer and screenBase
    443         if (oldBase >= 0) {
    444           System.arraycopy(charArray, oldBase + top, cbuf, oldBase - offset, n);
    445           System.arraycopy(charAttributes, oldBase + top, abuf, oldBase - offset, n);
    446         }
    447         // copy anything from topMargin + n up to the line linserted to the
    448         // topMargin
    449         System
    450             .arraycopy(charArray, oldBase + top + n, cbuf, newScreenBase + top, l - top - (n - 1));
    451         System.arraycopy(charAttributes, oldBase + top + n, abuf, newScreenBase + top, l - top
    452             - (n - 1));
    453         //
    454         // copy the all lines next to the inserted to the new buffer
    455         if (l < height - 1) {
    456           System.arraycopy(charArray, oldBase + l + 1, cbuf, newScreenBase + l + 1, (height - 1)
    457               - l);
    458           System.arraycopy(charAttributes, oldBase + l + 1, abuf, newScreenBase + l + 1,
    459               (height - 1) - l);
    460         }
    461       } catch (ArrayIndexOutOfBoundsException e) {
    462         // this should not happen anymore, but I will leave the code
    463         // here in case something happens anyway. That code above is
    464         // so complex I always have a hard time understanding what
    465         // I did, even though there are comments
    466         System.err.println("*** Error while scrolling up:");
    467         System.err.println("--- BEGIN STACK TRACE ---");
    468         e.printStackTrace();
    469         System.err.println("--- END STACK TRACE ---");
    470         System.err.println("bufSize=" + bufSize + ", maxBufSize=" + maxBufSize);
    471         System.err.println("top=" + top + ", bottom=" + bottom);
    472         System.err.println("n=" + n + ", l=" + l);
    473         System.err.println("screenBase=" + screenBase + ", windowBase=" + windowBase);
    474         System.err.println("newScreenBase=" + newScreenBase + ", newWindowBase=" + newWindowBase);
    475         System.err.println("oldBase=" + oldBase);
    476         System.err.println("size.width=" + width + ", size.height=" + height);
    477         System.err.println("abuf.length=" + abuf.length + ", cbuf.length=" + cbuf.length);
    478         System.err.println("*** done dumping debug information");
    479       }
    480     }
    481 
    482     // this is a little helper to mark the scrolling
    483     scrollMarker -= n;
    484 
    485     for (int i = 0; i < n; i++) {
    486       cbuf[(newScreenBase + l) + (scrollDown ? i : -i)] = new char[width];
    487       Arrays.fill(cbuf[(newScreenBase + l) + (scrollDown ? i : -i)], ' ');
    488       abuf[(newScreenBase + l) + (scrollDown ? i : -i)] = new int[width];
    489     }
    490 
    491     charArray = cbuf;
    492     charAttributes = abuf;
    493     screenBase = newScreenBase;
    494     windowBase = newWindowBase;
    495     bufSize = newBufSize;
    496 
    497     if (scrollDown) {
    498       markLine(l, bottom - l + 1);
    499     } else {
    500       markLine(top, l - top + 1);
    501     }
    502 
    503     display.updateScrollBar();
    504   }
    505 
    506   /**
    507    * Delete a line at a specific position. Subsequent lines will be scrolled up to fill the space
    508    * and a blank line is inserted at the end of the screen.
    509    *
    510    * @param l
    511    *          the y-coordinate to insert the line
    512    * @see #deleteLine
    513    */
    514   public void deleteLine(int l) {
    515     int bottom = (l > bottomMargin ? height - 1 : (l < topMargin ? topMargin : bottomMargin + 1));
    516     int numRows = bottom - l - 1;
    517 
    518     char[] discardedChars = charArray[screenBase + l];
    519     int[] discardedAttributes = charAttributes[screenBase + l];
    520 
    521     if (numRows > 0) {
    522       System.arraycopy(charArray, screenBase + l + 1, charArray, screenBase + l, numRows);
    523       System.arraycopy(charAttributes, screenBase + l + 1, charAttributes, screenBase + l, numRows);
    524     }
    525 
    526     int newBottomRow = screenBase + bottom - 1;
    527     charArray[newBottomRow] = discardedChars;
    528     charAttributes[newBottomRow] = discardedAttributes;
    529     Arrays.fill(charArray[newBottomRow], ' ');
    530     Arrays.fill(charAttributes[newBottomRow], 0);
    531 
    532     markLine(l, bottom - l);
    533   }
    534 
    535   /**
    536    * Delete a rectangular portion of the screen. You need to call redraw() to update the screen.
    537    *
    538    * @param c
    539    *          x-coordinate (column)
    540    * @param l
    541    *          y-coordinate (row)
    542    * @param w
    543    *          with of the area in characters
    544    * @param h
    545    *          height of the area in characters
    546    * @param curAttr
    547    *          attribute to fill
    548    * @see #deleteChar
    549    * @see #deleteLine
    550    * @see #redraw
    551    */
    552   public void deleteArea(int c, int l, int w, int h, int curAttr) {
    553     int endColumn = c + w;
    554     int targetRow = screenBase + l;
    555     for (int i = 0; i < h && l + i < height; i++) {
    556       Arrays.fill(charAttributes[targetRow], c, endColumn, curAttr);
    557       Arrays.fill(charArray[targetRow], c, endColumn, ' ');
    558       targetRow++;
    559     }
    560     markLine(l, h);
    561   }
    562 
    563   /**
    564    * Delete a rectangular portion of the screen. You need to call redraw() to update the screen.
    565    *
    566    * @param c
    567    *          x-coordinate (column)
    568    * @param l
    569    *          y-coordinate (row)
    570    * @param w
    571    *          with of the area in characters
    572    * @param h
    573    *          height of the area in characters
    574    * @see #deleteChar
    575    * @see #deleteLine
    576    * @see #redraw
    577    */
    578   public void deleteArea(int c, int l, int w, int h) {
    579     deleteArea(c, l, w, h, 0);
    580   }
    581 
    582   /**
    583    * Sets whether the cursor is visible or not.
    584    *
    585    * @param doshow
    586    */
    587   public void showCursor(boolean doshow) {
    588     showcursor = doshow;
    589   }
    590 
    591   /**
    592    * Check whether the cursor is currently visible.
    593    *
    594    * @return visibility
    595    */
    596   public boolean isCursorVisible() {
    597     return showcursor;
    598   }
    599 
    600   /**
    601    * Puts the cursor at the specified position.
    602    *
    603    * @param c
    604    *          column
    605    * @param l
    606    *          line
    607    */
    608   public void setCursorPosition(int c, int l) {
    609     cursorX = c;
    610     cursorY = l;
    611   }
    612 
    613   /**
    614    * Get the current column of the cursor position.
    615    */
    616   public int getCursorColumn() {
    617     return cursorX;
    618   }
    619 
    620   /**
    621    * Get the current line of the cursor position.
    622    */
    623   public int getCursorRow() {
    624     return cursorY;
    625   }
    626 
    627   /**
    628    * Set the current window base. This allows to view the scrollback buffer.
    629    *
    630    * @param line
    631    *          the line where the screen window starts
    632    * @see #setBufferSize
    633    * @see #getBufferSize
    634    */
    635   public void setWindowBase(int line) {
    636     if (line > screenBase) {
    637       line = screenBase;
    638     } else if (line < 0) {
    639       line = 0;
    640     }
    641     windowBase = line;
    642     update[0] = true;
    643     redraw();
    644   }
    645 
    646   /**
    647    * Get the current window base.
    648    *
    649    * @see #setWindowBase
    650    */
    651   public int getWindowBase() {
    652     return windowBase;
    653   }
    654 
    655   /**
    656    * Set the scroll margins simultaneously. If they're out of bounds, trim them.
    657    *
    658    * @param l1
    659    *          line that is the top
    660    * @param l2
    661    *          line that is the bottom
    662    */
    663   public void setMargins(int l1, int l2) {
    664     if (l1 > l2) {
    665       return;
    666     }
    667 
    668     if (l1 < 0) {
    669       l1 = 0;
    670     }
    671     if (l2 >= height) {
    672       l2 = height - 1;
    673     }
    674 
    675     topMargin = l1;
    676     bottomMargin = l2;
    677   }
    678 
    679   /**
    680    * Set the top scroll margin for the screen. If the current bottom margin is smaller it will
    681    * become the top margin and the line will become the bottom margin.
    682    *
    683    * @param l
    684    *          line that is the margin
    685    */
    686   public void setTopMargin(int l) {
    687     if (l > bottomMargin) {
    688       topMargin = bottomMargin;
    689       bottomMargin = l;
    690     } else {
    691       topMargin = l;
    692     }
    693     if (topMargin < 0) {
    694       topMargin = 0;
    695     }
    696     if (bottomMargin >= height) {
    697       bottomMargin = height - 1;
    698     }
    699   }
    700 
    701   /**
    702    * Get the top scroll margin.
    703    */
    704   public int getTopMargin() {
    705     return topMargin;
    706   }
    707 
    708   /**
    709    * Set the bottom scroll margin for the screen. If the current top margin is bigger it will become
    710    * the bottom margin and the line will become the top margin.
    711    *
    712    * @param l
    713    *          line that is the margin
    714    */
    715   public void setBottomMargin(int l) {
    716     if (l < topMargin) {
    717       bottomMargin = topMargin;
    718       topMargin = l;
    719     } else {
    720       bottomMargin = l;
    721     }
    722     if (topMargin < 0) {
    723       topMargin = 0;
    724     }
    725     if (bottomMargin >= height) {
    726       bottomMargin = height - 1;
    727     }
    728   }
    729 
    730   /**
    731    * Get the bottom scroll margin.
    732    */
    733   public int getBottomMargin() {
    734     return bottomMargin;
    735   }
    736 
    737   /**
    738    * Set scrollback buffer size.
    739    *
    740    * @param amount
    741    *          new size of the buffer
    742    */
    743   public void setBufferSize(int amount) {
    744     if (amount < height) {
    745       amount = height;
    746     }
    747     if (amount < maxBufSize) {
    748       char cbuf[][] = new char[amount][width];
    749       int abuf[][] = new int[amount][width];
    750       int copyStart = bufSize - amount < 0 ? 0 : bufSize - amount;
    751       int copyCount = bufSize - amount < 0 ? bufSize : amount;
    752       if (charArray != null) {
    753         System.arraycopy(charArray, copyStart, cbuf, 0, copyCount);
    754       }
    755       if (charAttributes != null) {
    756         System.arraycopy(charAttributes, copyStart, abuf, 0, copyCount);
    757       }
    758       charArray = cbuf;
    759       charAttributes = abuf;
    760       bufSize = copyCount;
    761       screenBase = bufSize - height;
    762       windowBase = screenBase;
    763     }
    764     maxBufSize = amount;
    765 
    766     update[0] = true;
    767     redraw();
    768   }
    769 
    770   /**
    771    * Retrieve current scrollback buffer size.
    772    *
    773    * @see #setBufferSize
    774    */
    775   public int getBufferSize() {
    776     return bufSize;
    777   }
    778 
    779   /**
    780    * Retrieve maximum buffer Size.
    781    *
    782    * @see #getBufferSize
    783    */
    784   public int getMaxBufferSize() {
    785     return maxBufSize;
    786   }
    787 
    788   /**
    789    * Change the size of the screen. This will include adjustment of the scrollback buffer.
    790    *
    791    * @param w
    792    *          of the screen
    793    * @param h
    794    *          of the screen
    795    */
    796   @SuppressWarnings("unused")
    797   public void setScreenSize(int w, int h, boolean broadcast) {
    798     char cbuf[][];
    799     int abuf[][];
    800     int maxSize = bufSize;
    801 
    802     if (w < 1 || h < 1) {
    803       return;
    804     }
    805 
    806     if (debug > 0) {
    807       System.err.println("VDU: screen size [" + w + "," + h + "]");
    808     }
    809 
    810     if (h > maxBufSize) {
    811       maxBufSize = h;
    812     }
    813 
    814     if (h > bufSize) {
    815       bufSize = h;
    816       screenBase = 0;
    817       windowBase = 0;
    818     }
    819 
    820     if (windowBase + h >= bufSize) {
    821       windowBase = bufSize - h;
    822     }
    823 
    824     if (screenBase + h >= bufSize) {
    825       screenBase = bufSize - h;
    826     }
    827 
    828     cbuf = new char[bufSize][w];
    829     abuf = new int[bufSize][w];
    830 
    831     for (int i = 0; i < bufSize; i++) {
    832       Arrays.fill(cbuf[i], ' ');
    833     }
    834 
    835     if (bufSize < maxSize) {
    836       maxSize = bufSize;
    837     }
    838 
    839     int rowLength;
    840     if (charArray != null && charAttributes != null) {
    841       for (int i = 0; i < maxSize && charArray[i] != null; i++) {
    842         rowLength = charArray[i].length;
    843         System.arraycopy(charArray[i], 0, cbuf[i], 0, w < rowLength ? w : rowLength);
    844         System.arraycopy(charAttributes[i], 0, abuf[i], 0, w < rowLength ? w : rowLength);
    845       }
    846     }
    847 
    848     int C = getCursorColumn();
    849     if (C < 0) {
    850       C = 0;
    851     } else if (C >= width) {
    852       C = width - 1;
    853     }
    854 
    855     int R = getCursorRow();
    856     if (R < 0) {
    857       R = 0;
    858     } else if (R >= height) {
    859       R = height - 1;
    860     }
    861 
    862     setCursorPosition(C, R);
    863 
    864     charArray = cbuf;
    865     charAttributes = abuf;
    866     width = w;
    867     height = h;
    868     topMargin = 0;
    869     bottomMargin = h - 1;
    870     update = new boolean[h + 1];
    871     update[0] = true;
    872     /*
    873      * FIXME: ??? if(resizeStrategy == RESIZE_FONT) setBounds(getBounds());
    874      */
    875   }
    876 
    877   /**
    878    * Get amount of rows on the screen.
    879    */
    880   public int getRows() {
    881     return height;
    882   }
    883 
    884   /**
    885    * Get amount of columns on the screen.
    886    */
    887   public int getColumns() {
    888     return width;
    889   }
    890 
    891   /**
    892    * Mark lines to be updated with redraw().
    893    *
    894    * @param l
    895    *          starting line
    896    * @param n
    897    *          amount of lines to be updated
    898    * @see #redraw
    899    */
    900   public void markLine(int l, int n) {
    901     for (int i = 0; (i < n) && (l + i < height); i++) {
    902       update[l + i + 1] = true;
    903     }
    904   }
    905 
    906   // private static int checkBounds(int value, int lower, int upper) {
    907   // if (value < lower)
    908   // return lower;
    909   // else if (value > upper)
    910   // return upper;
    911   // else
    912   // return value;
    913   // }
    914 
    915   /** a generic display that should redraw on demand */
    916   protected VDUDisplay display;
    917 
    918   public void setDisplay(VDUDisplay display) {
    919     this.display = display;
    920   }
    921 
    922   /**
    923    * Trigger a redraw on the display.
    924    */
    925   protected void redraw() {
    926     if (display != null) {
    927       display.redraw();
    928     }
    929   }
    930 }
    931