• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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    */
TerminalBridge()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    */
TerminalBridge(final TerminalManager manager, InterpreterProcess process, AbsTransport t)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    */
connect()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 
updateCharset()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    */
injectString(final String string)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    */
isSessionOpen()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    */
dispatchDisconnect(boolean immediate)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 
setSelectingForCopy(boolean selectingForCopy)382   public void setSelectingForCopy(boolean selectingForCopy) {
383     this.selectingForCopy = selectingForCopy;
384   }
385 
isSelectingForCopy()386   public boolean isSelectingForCopy() {
387     return selectingForCopy;
388   }
389 
getSelectionArea()390   public SelectionArea getSelectionArea() {
391     return selectionArea;
392   }
393 
tryKeyVibrate()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    */
setFontSize(float size)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    */
addFontSizeChangedListener(FontSizeChangedListener listener)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    */
removeFontSizeChangedListener(FontSizeChangedListener listener)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    */
parentChanged(TerminalView parent)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    */
parentDestroyed()545   public synchronized void parentDestroyed() {
546     parent = null;
547     discardBitmap();
548   }
549 
discardBitmap()550   private void discardBitmap() {
551     if (bitmap != null) {
552       bitmap.recycle();
553     }
554     bitmap = null;
555   }
556 
onDraw()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 
redraw()659   public void redraw() {
660     if (parent != null) {
661       parent.postInvalidate();
662     }
663   }
664 
665   // We don't have a scroll bar.
updateScrollBar()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    */
resizeComputed(int cols, int rows, int width, int height)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 
fontSizeCompare(float size, int cols, int rows, int width, int height)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
setVDUBuffer(VDUBuffer buffer)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    */
setColor(int index, int red, int green, int blue)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 
resetColors()760   public final void resetColors() {
761     System.arraycopy(Colors.defaults, 0, color, 0, Colors.defaults.length);
762   }
763 
getKeyHandler()764   public TerminalKeyListener getKeyHandler() {
765     return keyListener;
766   }
767 
resetScrollPosition()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 
increaseFontSize()775   public void increaseFontSize() {
776     setFontSize(fontSize + FONT_SIZE_STEP);
777   }
778 
decreaseFontSize()779   public void decreaseFontSize() {
780     setFontSize(fontSize - FONT_SIZE_STEP);
781   }
782 
getId()783   public int getId() {
784     return mProcess.getPort();
785   }
786 
getName()787   public String getName() {
788     return mProcess.getName();
789   }
790 
getProcess()791   public InterpreterProcess getProcess() {
792     return mProcess;
793   }
794 
getForegroundColor()795   public int getForegroundColor() {
796     return mDefaultFgColor;
797   }
798 
getBackgroundColor()799   public int getBackgroundColor() {
800     return mDefaultBgColor;
801   }
802 
getVDUBuffer()803   public VDUBuffer getVDUBuffer() {
804     return buffer;
805   }
806 
getPromptHelper()807   public PromptHelper getPromptHelper() {
808     return promptHelper;
809   }
810 
getBitmap()811   public Bitmap getBitmap() {
812     return bitmap;
813   }
814 
getTransport()815   public AbsTransport getTransport() {
816     return transport;
817   }
818 
getPaint()819   public Paint getPaint() {
820     return defaultPaint;
821   }
822 
onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)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 
onPrepareOptionsMenu(Menu menu)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 
onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)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