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