Home | History | Annotate | Download | only in service
      1 /*
      2  * ConnectBot: simple, powerful, open-source SSH client for Android
      3  * Copyright 2007 Kenny Root, Jeffrey Sharkey
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package org.connectbot.service;
     19 
     20 import android.content.Context;
     21 import android.content.SharedPreferences;
     22 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
     23 import android.graphics.Bitmap;
     24 import android.graphics.Canvas;
     25 import android.graphics.Color;
     26 import android.graphics.Paint;
     27 import android.graphics.Typeface;
     28 import android.graphics.Bitmap.Config;
     29 import android.graphics.Paint.FontMetrics;
     30 import android.text.ClipboardManager;
     31 import android.view.ContextMenu;
     32 import android.view.Menu;
     33 import android.view.View;
     34 import android.view.ContextMenu.ContextMenuInfo;
     35 
     36 import com.googlecode.android_scripting.Log;
     37 import com.googlecode.android_scripting.R;
     38 import com.googlecode.android_scripting.facade.ui.UiFacade;
     39 import com.googlecode.android_scripting.interpreter.InterpreterProcess;
     40 import com.googlecode.android_scripting.jsonrpc.RpcReceiverManager;
     41 import com.googlecode.android_scripting.jsonrpc.RpcReceiverManagerFactory;
     42 
     43 import de.mud.terminal.VDUBuffer;
     44 import de.mud.terminal.VDUDisplay;
     45 import de.mud.terminal.vt320;
     46 
     47 import java.io.IOException;
     48 import java.nio.charset.Charset;
     49 import java.util.LinkedList;
     50 import java.util.List;
     51 
     52 import org.connectbot.TerminalView;
     53 import org.connectbot.transport.AbsTransport;
     54 import org.connectbot.util.Colors;
     55 import org.connectbot.util.PreferenceConstants;
     56 import org.connectbot.util.SelectionArea;
     57 
     58 /**
     59  * Provides a bridge between a MUD terminal buffer and a possible TerminalView. This separation
     60  * allows us to keep the TerminalBridge running in a background service. A TerminalView shares down
     61  * a bitmap that we can use for rendering when available.
     62  *
     63  *
     64  */
     65 public class TerminalBridge implements VDUDisplay, OnSharedPreferenceChangeListener {
     66 
     67   private final static int FONT_SIZE_STEP = 2;
     68 
     69   private final int[] color = new int[Colors.defaults.length];
     70 
     71   private final TerminalManager manager;
     72 
     73   private final InterpreterProcess mProcess;
     74 
     75   private int mDefaultFgColor;
     76   private int mDefaultBgColor;
     77 
     78   private int scrollback;
     79 
     80   private String delKey;
     81   private String encoding;
     82 
     83   private AbsTransport transport;
     84 
     85   private final Paint defaultPaint;
     86 
     87   private Relay relay;
     88 
     89   private Bitmap bitmap = null;
     90   private final VDUBuffer buffer;
     91 
     92   private TerminalView parent = null;
     93   private final Canvas canvas = new Canvas();
     94 
     95   private boolean forcedSize = false;
     96   private int columns;
     97   private int rows;
     98 
     99   private final TerminalKeyListener keyListener;
    100 
    101   private boolean selectingForCopy = false;
    102   private final SelectionArea selectionArea;
    103   private ClipboardManager clipboard;
    104 
    105   public int charWidth = -1;
    106   public int charHeight = -1;
    107   private int charTop = -1;
    108 
    109   private float fontSize = -1;
    110 
    111   private final List<FontSizeChangedListener> fontSizeChangedListeners;
    112 
    113   /**
    114    * Flag indicating if we should perform a full-screen redraw during our next rendering pass.
    115    */
    116   private boolean fullRedraw = false;
    117 
    118   private final PromptHelper promptHelper;
    119 
    120   /**
    121    * Create a new terminal bridge suitable for unit testing.
    122    */
    123   public TerminalBridge() {
    124     buffer = new vt320() {
    125       @Override
    126       public void write(byte[] b) {
    127       }
    128 
    129       @Override
    130       public void write(int b) {
    131       }
    132 
    133       @Override
    134       public void sendTelnetCommand(byte cmd) {
    135       }
    136 
    137       @Override
    138       public void setWindowSize(int c, int r) {
    139       }
    140 
    141       @Override
    142       public void debug(String s) {
    143       }
    144     };
    145 
    146     manager = null;
    147 
    148     defaultPaint = new Paint();
    149 
    150     selectionArea = new SelectionArea();
    151     scrollback = 1;
    152 
    153     fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>();
    154 
    155     transport = null;
    156 
    157     keyListener = new TerminalKeyListener(manager, this, buffer, null);
    158 
    159     mProcess = null;
    160 
    161     mDefaultFgColor = 0;
    162     mDefaultBgColor = 0;
    163     promptHelper = null;
    164 
    165     updateCharset();
    166   }
    167 
    168   /**
    169    * Create new terminal bridge with following parameters.
    170    */
    171   public TerminalBridge(final TerminalManager manager, InterpreterProcess process, AbsTransport t)
    172       throws IOException {
    173     this.manager = manager;
    174     transport = t;
    175     mProcess = process;
    176 
    177     String string = manager.getStringParameter(PreferenceConstants.SCROLLBACK, null);
    178     if (string != null) {
    179       scrollback = Integer.parseInt(string);
    180     } else {
    181       scrollback = PreferenceConstants.DEFAULT_SCROLLBACK;
    182     }
    183 
    184     string = manager.getStringParameter(PreferenceConstants.FONTSIZE, null);
    185     if (string != null) {
    186       fontSize = Float.parseFloat(string);
    187     } else {
    188       fontSize = PreferenceConstants.DEFAULT_FONT_SIZE;
    189     }
    190 
    191     mDefaultFgColor =
    192         manager.getIntParameter(PreferenceConstants.COLOR_FG, PreferenceConstants.DEFAULT_FG_COLOR);
    193     mDefaultBgColor =
    194         manager.getIntParameter(PreferenceConstants.COLOR_BG, PreferenceConstants.DEFAULT_BG_COLOR);
    195 
    196     delKey = manager.getStringParameter(PreferenceConstants.DELKEY, PreferenceConstants.DELKEY_DEL);
    197 
    198     // create prompt helper to relay password and hostkey requests up to gui
    199     promptHelper = new PromptHelper(this);
    200 
    201     // create our default paint
    202     defaultPaint = new Paint();
    203     defaultPaint.setAntiAlias(true);
    204     defaultPaint.setTypeface(Typeface.MONOSPACE);
    205     defaultPaint.setFakeBoldText(true); // more readable?
    206 
    207     fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>();
    208 
    209     setFontSize(fontSize);
    210 
    211     // create terminal buffer and handle outgoing data
    212     // this is probably status reply information
    213     buffer = new vt320() {
    214       @Override
    215       public void debug(String s) {
    216         Log.d(s);
    217       }
    218 
    219       @Override
    220       public void write(byte[] b) {
    221         try {
    222           if (b != null && transport != null) {
    223             transport.write(b);
    224           }
    225         } catch (IOException e) {
    226           Log.e("Problem writing outgoing data in vt320() thread", e);
    227         }
    228       }
    229 
    230       @Override
    231       public void write(int b) {
    232         try {
    233           if (transport != null) {
    234             transport.write(b);
    235           }
    236         } catch (IOException e) {
    237           Log.e("Problem writing outgoing data in vt320() thread", e);
    238         }
    239       }
    240 
    241       // We don't use telnet sequences.
    242       @Override
    243       public void sendTelnetCommand(byte cmd) {
    244       }
    245 
    246       // We don't want remote to resize our window.
    247       @Override
    248       public void setWindowSize(int c, int r) {
    249       }
    250 
    251       @Override
    252       public void beep() {
    253         if (parent.isShown()) {
    254           manager.playBeep();
    255         }
    256       }
    257     };
    258 
    259     // Don't keep any scrollback if a session is not being opened.
    260 
    261     buffer.setBufferSize(scrollback);
    262 
    263     resetColors();
    264     buffer.setDisplay(this);
    265 
    266     selectionArea = new SelectionArea();
    267 
    268     keyListener = new TerminalKeyListener(manager, this, buffer, encoding);
    269 
    270     updateCharset();
    271 
    272     manager.registerOnSharedPreferenceChangeListener(this);
    273 
    274   }
    275 
    276   /**
    277    * Spawn thread to open connection and start login process.
    278    */
    279   protected void connect() {
    280     transport.setBridge(this);
    281     transport.setManager(manager);
    282     transport.connect();
    283 
    284     ((vt320) buffer).reset();
    285 
    286     // previously tried vt100 and xterm for emulation modes
    287     // "screen" works the best for color and escape codes
    288     ((vt320) buffer).setAnswerBack("screen");
    289 
    290     if (PreferenceConstants.DELKEY_BACKSPACE.equals(delKey)) {
    291       ((vt320) buffer).setBackspace(vt320.DELETE_IS_BACKSPACE);
    292     } else {
    293       ((vt320) buffer).setBackspace(vt320.DELETE_IS_DEL);
    294     }
    295 
    296     // create thread to relay incoming connection data to buffer
    297     relay = new Relay(this, transport, (vt320) buffer, encoding);
    298     Thread relayThread = new Thread(relay);
    299     relayThread.setDaemon(true);
    300     relayThread.setName("Relay");
    301     relayThread.start();
    302 
    303     // force font-size to make sure we resizePTY as needed
    304     setFontSize(fontSize);
    305 
    306   }
    307 
    308   private void updateCharset() {
    309     encoding =
    310         manager.getStringParameter(PreferenceConstants.ENCODING, Charset.defaultCharset().name());
    311     if (relay != null) {
    312       relay.setCharset(encoding);
    313     }
    314     keyListener.setCharset(encoding);
    315   }
    316 
    317   /**
    318    * Inject a specific string into this terminal. Used for post-login strings and pasting clipboard.
    319    */
    320   public void injectString(final String string) {
    321     if (string == null || string.length() == 0) {
    322       return;
    323     }
    324 
    325     Thread injectStringThread = new Thread(new Runnable() {
    326       public void run() {
    327         try {
    328           transport.write(string.getBytes(encoding));
    329         } catch (Exception e) {
    330           Log.e("Couldn't inject string to remote host: ", e);
    331         }
    332       }
    333     });
    334     injectStringThread.setName("InjectString");
    335     injectStringThread.start();
    336   }
    337 
    338   /**
    339    * @return whether a session is open or not
    340    */
    341   public boolean isSessionOpen() {
    342     if (transport != null) {
    343       return transport.isSessionOpen();
    344     }
    345     return false;
    346   }
    347 
    348   /**
    349    * Force disconnection of this terminal bridge.
    350    */
    351   public void dispatchDisconnect(boolean immediate) {
    352 
    353     // Cancel any pending prompts.
    354     promptHelper.cancelPrompt();
    355 
    356     if (immediate) {
    357       manager.closeConnection(TerminalBridge.this, true);
    358     } else {
    359       Thread disconnectPromptThread = new Thread(new Runnable() {
    360         public void run() {
    361           String prompt = null;
    362           if (transport != null && transport.isConnected()) {
    363             prompt = manager.getResources().getString(R.string.prompt_confirm_exit);
    364           } else {
    365             prompt = manager.getResources().getString(R.string.prompt_process_exited);
    366           }
    367           Boolean result = promptHelper.requestBooleanPrompt(null, prompt);
    368 
    369           if (transport != null && transport.isConnected()) {
    370             manager.closeConnection(TerminalBridge.this, result != null && result.booleanValue());
    371           } else if (result != null && result.booleanValue()) {
    372             manager.closeConnection(TerminalBridge.this, false);
    373           }
    374         }
    375       });
    376       disconnectPromptThread.setName("DisconnectPrompt");
    377       disconnectPromptThread.setDaemon(true);
    378       disconnectPromptThread.start();
    379     }
    380   }
    381 
    382   public void setSelectingForCopy(boolean selectingForCopy) {
    383     this.selectingForCopy = selectingForCopy;
    384   }
    385 
    386   public boolean isSelectingForCopy() {
    387     return selectingForCopy;
    388   }
    389 
    390   public SelectionArea getSelectionArea() {
    391     return selectionArea;
    392   }
    393 
    394   public synchronized void tryKeyVibrate() {
    395     manager.tryKeyVibrate();
    396   }
    397 
    398   /**
    399    * Request a different font size. Will make call to parentChanged() to make sure we resize PTY if
    400    * needed.
    401    */
    402   /* package */final void setFontSize(float size) {
    403     if (size <= 0.0) {
    404       return;
    405     }
    406 
    407     defaultPaint.setTextSize(size);
    408     fontSize = size;
    409 
    410     // read new metrics to get exact pixel dimensions
    411     FontMetrics fm = defaultPaint.getFontMetrics();
    412     charTop = (int) Math.ceil(fm.top);
    413 
    414     float[] widths = new float[1];
    415     defaultPaint.getTextWidths("X", widths);
    416     charWidth = (int) Math.ceil(widths[0]);
    417     charHeight = (int) Math.ceil(fm.descent - fm.top);
    418 
    419     // refresh any bitmap with new font size
    420     if (parent != null) {
    421       parentChanged(parent);
    422     }
    423 
    424     for (FontSizeChangedListener ofscl : fontSizeChangedListeners) {
    425       ofscl.onFontSizeChanged(size);
    426     }
    427     forcedSize = false;
    428   }
    429 
    430   /**
    431    * Add an {@link FontSizeChangedListener} to the list of listeners for this bridge.
    432    *
    433    * @param listener
    434    *          listener to add
    435    */
    436   public void addFontSizeChangedListener(FontSizeChangedListener listener) {
    437     fontSizeChangedListeners.add(listener);
    438   }
    439 
    440   /**
    441    * Remove an {@link FontSizeChangedListener} from the list of listeners for this bridge.
    442    *
    443    * @param listener
    444    */
    445   public void removeFontSizeChangedListener(FontSizeChangedListener listener) {
    446     fontSizeChangedListeners.remove(listener);
    447   }
    448 
    449   /**
    450    * Something changed in our parent {@link TerminalView}, maybe it's a new parent, or maybe it's an
    451    * updated font size. We should recalculate terminal size information and request a PTY resize.
    452    */
    453   public final synchronized void parentChanged(TerminalView parent) {
    454     if (manager != null && !manager.isResizeAllowed()) {
    455       Log.d("Resize is not allowed now");
    456       return;
    457     }
    458 
    459     this.parent = parent;
    460     final int width = parent.getWidth();
    461     final int height = parent.getHeight();
    462 
    463     // Something has gone wrong with our layout; we're 0 width or height!
    464     if (width <= 0 || height <= 0) {
    465       return;
    466     }
    467 
    468     clipboard = (ClipboardManager) parent.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
    469     keyListener.setClipboardManager(clipboard);
    470 
    471     if (!forcedSize) {
    472       // recalculate buffer size
    473       int newColumns, newRows;
    474 
    475       newColumns = width / charWidth;
    476       newRows = height / charHeight;
    477 
    478       // If nothing has changed in the terminal dimensions and not an intial
    479       // draw then don't blow away scroll regions and such.
    480       if (newColumns == columns && newRows == rows) {
    481         return;
    482       }
    483 
    484       columns = newColumns;
    485       rows = newRows;
    486     }
    487 
    488     // reallocate new bitmap if needed
    489     boolean newBitmap = (bitmap == null);
    490     if (bitmap != null) {
    491       newBitmap = (bitmap.getWidth() != width || bitmap.getHeight() != height);
    492     }
    493 
    494     if (newBitmap) {
    495       discardBitmap();
    496       bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
    497       canvas.setBitmap(bitmap);
    498     }
    499 
    500     // clear out any old buffer information
    501     defaultPaint.setColor(Color.BLACK);
    502     canvas.drawPaint(defaultPaint);
    503 
    504     // Stroke the border of the terminal if the size is being forced;
    505     if (forcedSize) {
    506       int borderX = (columns * charWidth) + 1;
    507       int borderY = (rows * charHeight) + 1;
    508 
    509       defaultPaint.setColor(Color.GRAY);
    510       defaultPaint.setStrokeWidth(0.0f);
    511       if (width >= borderX) {
    512         canvas.drawLine(borderX, 0, borderX, borderY + 1, defaultPaint);
    513       }
    514       if (height >= borderY) {
    515         canvas.drawLine(0, borderY, borderX + 1, borderY, defaultPaint);
    516       }
    517     }
    518 
    519     try {
    520       // request a terminal pty resize
    521       synchronized (buffer) {
    522         buffer.setScreenSize(columns, rows, true);
    523       }
    524 
    525       if (transport != null) {
    526         transport.setDimensions(columns, rows, width, height);
    527       }
    528     } catch (Exception e) {
    529       Log.e("Problem while trying to resize screen or PTY", e);
    530     }
    531 
    532     // force full redraw with new buffer size
    533     fullRedraw = true;
    534     redraw();
    535 
    536     parent.notifyUser(String.format("%d x %d", columns, rows));
    537 
    538     Log.i(String.format("parentChanged() now width=%d, height=%d", columns, rows));
    539   }
    540 
    541   /**
    542    * Somehow our parent {@link TerminalView} was destroyed. Now we don't need to redraw anywhere,
    543    * and we can recycle our internal bitmap.
    544    */
    545   public synchronized void parentDestroyed() {
    546     parent = null;
    547     discardBitmap();
    548   }
    549 
    550   private void discardBitmap() {
    551     if (bitmap != null) {
    552       bitmap.recycle();
    553     }
    554     bitmap = null;
    555   }
    556 
    557   public void onDraw() {
    558     int fg, bg;
    559     synchronized (buffer) {
    560       boolean entireDirty = buffer.update[0] || fullRedraw;
    561       boolean isWideCharacter = false;
    562 
    563       // walk through all lines in the buffer
    564       for (int l = 0; l < buffer.height; l++) {
    565 
    566         // check if this line is dirty and needs to be repainted
    567         // also check for entire-buffer dirty flags
    568         if (!entireDirty && !buffer.update[l + 1]) {
    569           continue;
    570         }
    571 
    572         // reset dirty flag for this line
    573         buffer.update[l + 1] = false;
    574 
    575         // walk through all characters in this line
    576         for (int c = 0; c < buffer.width; c++) {
    577           int addr = 0;
    578           int currAttr = buffer.charAttributes[buffer.windowBase + l][c];
    579           // check if foreground color attribute is set
    580           if ((currAttr & VDUBuffer.COLOR_FG) != 0) {
    581             int fgcolor = ((currAttr & VDUBuffer.COLOR_FG) >> VDUBuffer.COLOR_FG_SHIFT) - 1;
    582             if (fgcolor < 8 && (currAttr & VDUBuffer.BOLD) != 0) {
    583               fg = color[fgcolor + 8];
    584             } else {
    585               fg = color[fgcolor];
    586             }
    587           } else {
    588             fg = mDefaultFgColor;
    589           }
    590 
    591           // check if background color attribute is set
    592           if ((currAttr & VDUBuffer.COLOR_BG) != 0) {
    593             bg = color[((currAttr & VDUBuffer.COLOR_BG) >> VDUBuffer.COLOR_BG_SHIFT) - 1];
    594           } else {
    595             bg = mDefaultBgColor;
    596           }
    597 
    598           // support character inversion by swapping background and foreground color
    599           if ((currAttr & VDUBuffer.INVERT) != 0) {
    600             int swapc = bg;
    601             bg = fg;
    602             fg = swapc;
    603           }
    604 
    605           // set underlined attributes if requested
    606           defaultPaint.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0);
    607 
    608           isWideCharacter = (currAttr & VDUBuffer.FULLWIDTH) != 0;
    609 
    610           if (isWideCharacter) {
    611             addr++;
    612           } else {
    613             // determine the amount of continuous characters with the same settings and print them
    614             // all at once
    615             while (c + addr < buffer.width
    616                 && buffer.charAttributes[buffer.windowBase + l][c + addr] == currAttr) {
    617               addr++;
    618             }
    619           }
    620 
    621           // Save the current clip region
    622           canvas.save(Canvas.CLIP_SAVE_FLAG);
    623 
    624           // clear this dirty area with background color
    625           defaultPaint.setColor(bg);
    626           if (isWideCharacter) {
    627             canvas.clipRect(c * charWidth, l * charHeight, (c + 2) * charWidth, (l + 1)
    628                 * charHeight);
    629           } else {
    630             canvas.clipRect(c * charWidth, l * charHeight, (c + addr) * charWidth, (l + 1)
    631                 * charHeight);
    632           }
    633           canvas.drawPaint(defaultPaint);
    634 
    635           // write the text string starting at 'c' for 'addr' number of characters
    636           defaultPaint.setColor(fg);
    637           if ((currAttr & VDUBuffer.INVISIBLE) == 0) {
    638             canvas.drawText(buffer.charArray[buffer.windowBase + l], c, addr, c * charWidth,
    639                 (l * charHeight) - charTop, defaultPaint);
    640           }
    641 
    642           // Restore the previous clip region
    643           canvas.restore();
    644 
    645           // advance to the next text block with different characteristics
    646           c += addr - 1;
    647           if (isWideCharacter) {
    648             c++;
    649           }
    650         }
    651       }
    652 
    653       // reset entire-buffer flags
    654       buffer.update[0] = false;
    655     }
    656     fullRedraw = false;
    657   }
    658 
    659   public void redraw() {
    660     if (parent != null) {
    661       parent.postInvalidate();
    662     }
    663   }
    664 
    665   // We don't have a scroll bar.
    666   public void updateScrollBar() {
    667   }
    668 
    669   /**
    670    * Resize terminal to fit [rows]x[cols] in screen of size [width]x[height]
    671    *
    672    * @param rows
    673    * @param cols
    674    * @param width
    675    * @param height
    676    */
    677   public synchronized void resizeComputed(int cols, int rows, int width, int height) {
    678     float size = 8.0f;
    679     float step = 8.0f;
    680     float limit = 0.125f;
    681 
    682     int direction;
    683 
    684     while ((direction = fontSizeCompare(size, cols, rows, width, height)) < 0) {
    685       size += step;
    686     }
    687 
    688     if (direction == 0) {
    689       Log.d(String.format("Fontsize: found match at %f", size));
    690       return;
    691     }
    692 
    693     step /= 2.0f;
    694     size -= step;
    695 
    696     while ((direction = fontSizeCompare(size, cols, rows, width, height)) != 0 && step >= limit) {
    697       step /= 2.0f;
    698       if (direction > 0) {
    699         size -= step;
    700       } else {
    701         size += step;
    702       }
    703     }
    704 
    705     if (direction > 0) {
    706       size -= step;
    707     }
    708 
    709     columns = cols;
    710     this.rows = rows;
    711     setFontSize(size);
    712     forcedSize = true;
    713   }
    714 
    715   private int fontSizeCompare(float size, int cols, int rows, int width, int height) {
    716     // read new metrics to get exact pixel dimensions
    717     defaultPaint.setTextSize(size);
    718     FontMetrics fm = defaultPaint.getFontMetrics();
    719 
    720     float[] widths = new float[1];
    721     defaultPaint.getTextWidths("X", widths);
    722     int termWidth = (int) widths[0] * cols;
    723     int termHeight = (int) Math.ceil(fm.descent - fm.top) * rows;
    724 
    725     Log.d(String.format("Fontsize: font size %f resulted in %d x %d", size, termWidth, termHeight));
    726 
    727     // Check to see if it fits in resolution specified.
    728     if (termWidth > width || termHeight > height) {
    729       return 1;
    730     }
    731 
    732     if (termWidth == width || termHeight == height) {
    733       return 0;
    734     }
    735 
    736     return -1;
    737   }
    738 
    739   /*
    740    * (non-Javadoc)
    741    *
    742    * @see de.mud.terminal.VDUDisplay#setVDUBuffer(de.mud.terminal.VDUBuffer)
    743    */
    744   @Override
    745   public void setVDUBuffer(VDUBuffer buffer) {
    746   }
    747 
    748   /*
    749    * (non-Javadoc)
    750    *
    751    * @see de.mud.terminal.VDUDisplay#setColor(byte, byte, byte, byte)
    752    */
    753   public void setColor(int index, int red, int green, int blue) {
    754     // Don't allow the system colors to be overwritten for now. May violate specs.
    755     if (index < color.length && index >= 16) {
    756       color[index] = 0xff000000 | red << 16 | green << 8 | blue;
    757     }
    758   }
    759 
    760   public final void resetColors() {
    761     System.arraycopy(Colors.defaults, 0, color, 0, Colors.defaults.length);
    762   }
    763 
    764   public TerminalKeyListener getKeyHandler() {
    765     return keyListener;
    766   }
    767 
    768   public void resetScrollPosition() {
    769     // if we're in scrollback, scroll to bottom of window on input
    770     if (buffer.windowBase != buffer.screenBase) {
    771       buffer.setWindowBase(buffer.screenBase);
    772     }
    773   }
    774 
    775   public void increaseFontSize() {
    776     setFontSize(fontSize + FONT_SIZE_STEP);
    777   }
    778 
    779   public void decreaseFontSize() {
    780     setFontSize(fontSize - FONT_SIZE_STEP);
    781   }
    782 
    783   public int getId() {
    784     return mProcess.getPort();
    785   }
    786 
    787   public String getName() {
    788     return mProcess.getName();
    789   }
    790 
    791   public InterpreterProcess getProcess() {
    792     return mProcess;
    793   }
    794 
    795   public int getForegroundColor() {
    796     return mDefaultFgColor;
    797   }
    798 
    799   public int getBackgroundColor() {
    800     return mDefaultBgColor;
    801   }
    802 
    803   public VDUBuffer getVDUBuffer() {
    804     return buffer;
    805   }
    806 
    807   public PromptHelper getPromptHelper() {
    808     return promptHelper;
    809   }
    810 
    811   public Bitmap getBitmap() {
    812     return bitmap;
    813   }
    814 
    815   public AbsTransport getTransport() {
    816     return transport;
    817   }
    818 
    819   public Paint getPaint() {
    820     return defaultPaint;
    821   }
    822 
    823   public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
    824     if (mProcess.isAlive()) {
    825       RpcReceiverManagerFactory rpcReceiverManagerFactory = mProcess.getRpcReceiverManagerFactory();
    826       for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers().values()) {
    827         UiFacade facade = manager.getReceiver(UiFacade.class);
    828         facade.onCreateContextMenu(menu, v, menuInfo);
    829       }
    830     }
    831   }
    832 
    833   public boolean onPrepareOptionsMenu(Menu menu) {
    834     boolean returnValue = false;
    835     if (mProcess.isAlive()) {
    836       RpcReceiverManagerFactory rpcReceiverManagerFactory = mProcess.getRpcReceiverManagerFactory();
    837       for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers().values()) {
    838         UiFacade facade = manager.getReceiver(UiFacade.class);
    839         returnValue = returnValue || facade.onPrepareOptionsMenu(menu);
    840       }
    841       return returnValue;
    842     }
    843     return false;
    844   }
    845 
    846   public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    847     if (PreferenceConstants.ENCODING.equals(key)) {
    848       updateCharset();
    849     } else if (PreferenceConstants.FONTSIZE.equals(key)) {
    850       String string = manager.getStringParameter(PreferenceConstants.FONTSIZE, null);
    851       if (string != null) {
    852         fontSize = Float.parseFloat(string);
    853       } else {
    854         fontSize = PreferenceConstants.DEFAULT_FONT_SIZE;
    855       }
    856       setFontSize(fontSize);
    857       fullRedraw = true;
    858     } else if (PreferenceConstants.SCROLLBACK.equals(key)) {
    859       String string = manager.getStringParameter(PreferenceConstants.SCROLLBACK, null);
    860       if (string != null) {
    861         scrollback = Integer.parseInt(string);
    862       } else {
    863         scrollback = PreferenceConstants.DEFAULT_SCROLLBACK;
    864       }
    865       buffer.setBufferSize(scrollback);
    866     } else if (PreferenceConstants.COLOR_FG.equals(key)) {
    867       mDefaultFgColor =
    868           manager.getIntParameter(PreferenceConstants.COLOR_FG,
    869               PreferenceConstants.DEFAULT_FG_COLOR);
    870       fullRedraw = true;
    871     } else if (PreferenceConstants.COLOR_BG.equals(key)) {
    872       mDefaultBgColor =
    873           manager.getIntParameter(PreferenceConstants.COLOR_BG,
    874               PreferenceConstants.DEFAULT_BG_COLOR);
    875       fullRedraw = true;
    876     }
    877     if (PreferenceConstants.DELKEY.equals(key)) {
    878       delKey =
    879           manager.getStringParameter(PreferenceConstants.DELKEY, PreferenceConstants.DELKEY_DEL);
    880       if (PreferenceConstants.DELKEY_BACKSPACE.equals(delKey)) {
    881         ((vt320) buffer).setBackspace(vt320.DELETE_IS_BACKSPACE);
    882       } else {
    883         ((vt320) buffer).setBackspace(vt320.DELETE_IS_DEL);
    884       }
    885     }
    886   }
    887 }
    888