19f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li/*
29f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * ConnectBot: simple, powerful, open-source SSH client for Android
39f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Copyright 2007 Kenny Root, Jeffrey Sharkey
49f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
59f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Licensed under the Apache License, Version 2.0 (the "License");
69f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * you may not use this file except in compliance with the License.
79f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * You may obtain a copy of the License at
89f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
99f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *     http://www.apache.org/licenses/LICENSE-2.0
109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Unless required by applicable law or agreed to in writing, software
129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * distributed under the License is distributed on an "AS IS" BASIS,
139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * See the License for the specific language governing permissions and
159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * limitations under the License.
169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */
179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Lipackage org.connectbot.service;
199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.Context;
219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.SharedPreferences;
229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.content.SharedPreferences.OnSharedPreferenceChangeListener;
239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.graphics.Bitmap;
249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.graphics.Canvas;
259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.graphics.Color;
269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.graphics.Paint;
279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.graphics.Typeface;
289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.graphics.Bitmap.Config;
299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.graphics.Paint.FontMetrics;
309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.text.ClipboardManager;
319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.view.ContextMenu;
329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.view.Menu;
339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.view.View;
349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport android.view.ContextMenu.ContextMenuInfo;
359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.Log;
379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.R;
389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.facade.ui.UiFacade;
399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.interpreter.InterpreterProcess;
409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.jsonrpc.RpcReceiverManager;
419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport com.googlecode.android_scripting.jsonrpc.RpcReceiverManagerFactory;
429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport de.mud.terminal.VDUBuffer;
449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport de.mud.terminal.VDUDisplay;
459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport de.mud.terminal.vt320;
469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.io.IOException;
489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.nio.charset.Charset;
499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.LinkedList;
509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport java.util.List;
519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.connectbot.TerminalView;
539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.connectbot.transport.AbsTransport;
549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.connectbot.util.Colors;
559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.connectbot.util.PreferenceConstants;
569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Liimport org.connectbot.util.SelectionArea;
579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li/**
599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * Provides a bridge between a MUD terminal buffer and a possible TerminalView. This separation
609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * allows us to keep the TerminalBridge running in a background service. A TerminalView shares down
619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li * a bitmap that we can use for rendering when available.
629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li *
649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li */
659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Lipublic class TerminalBridge implements VDUDisplay, OnSharedPreferenceChangeListener {
669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final static int FONT_SIZE_STEP = 2;
689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final int[] color = new int[Colors.defaults.length];
709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final TerminalManager manager;
729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final InterpreterProcess mProcess;
749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private int mDefaultFgColor;
769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private int mDefaultBgColor;
779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private int scrollback;
799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private String delKey;
819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private String encoding;
829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private AbsTransport transport;
849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final Paint defaultPaint;
869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private Relay relay;
889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private Bitmap bitmap = null;
909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final VDUBuffer buffer;
919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private TerminalView parent = null;
939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final Canvas canvas = new Canvas();
949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private boolean forcedSize = false;
969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private int columns;
979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private int rows;
989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final TerminalKeyListener keyListener;
1009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private boolean selectingForCopy = false;
1029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final SelectionArea selectionArea;
1039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private ClipboardManager clipboard;
1049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public int charWidth = -1;
1069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public int charHeight = -1;
1079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private int charTop = -1;
1089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private float fontSize = -1;
1109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final List<FontSizeChangedListener> fontSizeChangedListeners;
1129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
1149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Flag indicating if we should perform a full-screen redraw during our next rendering pass.
1159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
1169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private boolean fullRedraw = false;
1179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private final PromptHelper promptHelper;
1199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
1219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Create a new terminal bridge suitable for unit testing.
1229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
1239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public TerminalBridge() {
1249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    buffer = new vt320() {
1259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
1269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void write(byte[] b) {
1279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
1309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void write(int b) {
1319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
1349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void sendTelnetCommand(byte cmd) {
1359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
1389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void setWindowSize(int c, int r) {
1399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
1429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void debug(String s) {
1439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
1449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    };
1459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    manager = null;
1479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint = new Paint();
1499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    selectionArea = new SelectionArea();
1519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    scrollback = 1;
1529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>();
1549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    transport = null;
1569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    keyListener = new TerminalKeyListener(manager, this, buffer, null);
1589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mProcess = null;
1609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mDefaultFgColor = 0;
1629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mDefaultBgColor = 0;
1639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    promptHelper = null;
1649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    updateCharset();
1669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
1679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
1699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Create new terminal bridge with following parameters.
1709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
1719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public TerminalBridge(final TerminalManager manager, InterpreterProcess process, AbsTransport t)
1729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      throws IOException {
1739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    this.manager = manager;
1749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    transport = t;
1759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mProcess = process;
1769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    String string = manager.getStringParameter(PreferenceConstants.SCROLLBACK, null);
1789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (string != null) {
1799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      scrollback = Integer.parseInt(string);
1809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } else {
1819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      scrollback = PreferenceConstants.DEFAULT_SCROLLBACK;
1829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
1839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    string = manager.getStringParameter(PreferenceConstants.FONTSIZE, null);
1859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (string != null) {
1869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      fontSize = Float.parseFloat(string);
1879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } else {
1889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      fontSize = PreferenceConstants.DEFAULT_FONT_SIZE;
1899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
1909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mDefaultFgColor =
1929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        manager.getIntParameter(PreferenceConstants.COLOR_FG, PreferenceConstants.DEFAULT_FG_COLOR);
1939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    mDefaultBgColor =
1949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        manager.getIntParameter(PreferenceConstants.COLOR_BG, PreferenceConstants.DEFAULT_BG_COLOR);
1959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    delKey = manager.getStringParameter(PreferenceConstants.DELKEY, PreferenceConstants.DELKEY_DEL);
1979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
1989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // create prompt helper to relay password and hostkey requests up to gui
1999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    promptHelper = new PromptHelper(this);
2009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // create our default paint
2029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint = new Paint();
2039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint.setAntiAlias(true);
2049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint.setTypeface(Typeface.MONOSPACE);
2059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint.setFakeBoldText(true); // more readable?
2069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>();
2089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    setFontSize(fontSize);
2109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // create terminal buffer and handle outgoing data
2129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // this is probably status reply information
2139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    buffer = new vt320() {
2149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
2159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void debug(String s) {
2169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        Log.d(s);
2179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
2209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void write(byte[] b) {
2219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        try {
2229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if (b != null && transport != null) {
2239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            transport.write(b);
2249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
2259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        } catch (IOException e) {
2269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          Log.e("Problem writing outgoing data in vt320() thread", e);
2279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
2289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
2319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void write(int b) {
2329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        try {
2339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if (transport != null) {
2349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            transport.write(b);
2359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
2369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        } catch (IOException e) {
2379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          Log.e("Problem writing outgoing data in vt320() thread", e);
2389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
2399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // We don't use telnet sequences.
2429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
2439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void sendTelnetCommand(byte cmd) {
2449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // We don't want remote to resize our window.
2479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
2489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void setWindowSize(int c, int r) {
2499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      @Override
2529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void beep() {
2539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        if (parent.isShown()) {
2549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          manager.playBeep();
2559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
2569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
2579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    };
2589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // Don't keep any scrollback if a session is not being opened.
2609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    buffer.setBufferSize(scrollback);
2629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    resetColors();
2649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    buffer.setDisplay(this);
2659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    selectionArea = new SelectionArea();
2679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    keyListener = new TerminalKeyListener(manager, this, buffer, encoding);
2699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    updateCharset();
2719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    manager.registerOnSharedPreferenceChangeListener(this);
2739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
2759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
2779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Spawn thread to open connection and start login process.
2789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
2799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  protected void connect() {
2809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    transport.setBridge(this);
2819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    transport.setManager(manager);
2829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    transport.connect();
2839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    ((vt320) buffer).reset();
2859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // previously tried vt100 and xterm for emulation modes
2879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // "screen" works the best for color and escape codes
2889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    ((vt320) buffer).setAnswerBack("screen");
2899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (PreferenceConstants.DELKEY_BACKSPACE.equals(delKey)) {
2919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      ((vt320) buffer).setBackspace(vt320.DELETE_IS_BACKSPACE);
2929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } else {
2939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      ((vt320) buffer).setBackspace(vt320.DELETE_IS_DEL);
2949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
2959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
2969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // create thread to relay incoming connection data to buffer
2979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    relay = new Relay(this, transport, (vt320) buffer, encoding);
2989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    Thread relayThread = new Thread(relay);
2999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    relayThread.setDaemon(true);
3009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    relayThread.setName("Relay");
3019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    relayThread.start();
3029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // force font-size to make sure we resizePTY as needed
3049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    setFontSize(fontSize);
3059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private void updateCharset() {
3099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    encoding =
3109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        manager.getStringParameter(PreferenceConstants.ENCODING, Charset.defaultCharset().name());
3119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (relay != null) {
3129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      relay.setCharset(encoding);
3139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
3149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    keyListener.setCharset(encoding);
3159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
3189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Inject a specific string into this terminal. Used for post-login strings and pasting clipboard.
3199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
3209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void injectString(final String string) {
3219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (string == null || string.length() == 0) {
3229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return;
3239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
3249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    Thread injectStringThread = new Thread(new Runnable() {
3269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      public void run() {
3279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        try {
3289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          transport.write(string.getBytes(encoding));
3299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        } catch (Exception e) {
3309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          Log.e("Couldn't inject string to remote host: ", e);
3319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
3329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
3339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    });
3349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    injectStringThread.setName("InjectString");
3359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    injectStringThread.start();
3369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
3399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @return whether a session is open or not
3409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
3419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public boolean isSessionOpen() {
3429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (transport != null) {
3439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return transport.isSessionOpen();
3449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
3459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return false;
3469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
3499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Force disconnection of this terminal bridge.
3509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
3519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void dispatchDisconnect(boolean immediate) {
3529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // Cancel any pending prompts.
3549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    promptHelper.cancelPrompt();
3559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (immediate) {
3579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      manager.closeConnection(TerminalBridge.this, true);
3589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } else {
3599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Thread disconnectPromptThread = new Thread(new Runnable() {
3609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        public void run() {
3619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          String prompt = null;
3629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if (transport != null && transport.isConnected()) {
3639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            prompt = manager.getResources().getString(R.string.prompt_confirm_exit);
3649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          } else {
3659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            prompt = manager.getResources().getString(R.string.prompt_process_exited);
3669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
3679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          Boolean result = promptHelper.requestBooleanPrompt(null, prompt);
3689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if (transport != null && transport.isConnected()) {
3709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            manager.closeConnection(TerminalBridge.this, result != null && result.booleanValue());
3719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          } else if (result != null && result.booleanValue()) {
3729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            manager.closeConnection(TerminalBridge.this, false);
3739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
3749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
3759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      });
3769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      disconnectPromptThread.setName("DisconnectPrompt");
3779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      disconnectPromptThread.setDaemon(true);
3789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      disconnectPromptThread.start();
3799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
3809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void setSelectingForCopy(boolean selectingForCopy) {
3839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    this.selectingForCopy = selectingForCopy;
3849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public boolean isSelectingForCopy() {
3879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return selectingForCopy;
3889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public SelectionArea getSelectionArea() {
3919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return selectionArea;
3929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public synchronized void tryKeyVibrate() {
3959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    manager.tryKeyVibrate();
3969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
3979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
3989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
3999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Request a different font size. Will make call to parentChanged() to make sure we resize PTY if
4009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * needed.
4019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
4029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /* package */final void setFontSize(float size) {
4039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (size <= 0.0) {
4049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return;
4059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
4069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint.setTextSize(size);
4089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    fontSize = size;
4099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // read new metrics to get exact pixel dimensions
4119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    FontMetrics fm = defaultPaint.getFontMetrics();
4129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    charTop = (int) Math.ceil(fm.top);
4139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    float[] widths = new float[1];
4159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint.getTextWidths("X", widths);
4169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    charWidth = (int) Math.ceil(widths[0]);
4179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    charHeight = (int) Math.ceil(fm.descent - fm.top);
4189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // refresh any bitmap with new font size
4209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (parent != null) {
4219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      parentChanged(parent);
4229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
4239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    for (FontSizeChangedListener ofscl : fontSizeChangedListeners) {
4259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      ofscl.onFontSizeChanged(size);
4269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
4279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    forcedSize = false;
4289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
4299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
4319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Add an {@link FontSizeChangedListener} to the list of listeners for this bridge.
4329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   *
4339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @param listener
4349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   *          listener to add
4359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
4369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void addFontSizeChangedListener(FontSizeChangedListener listener) {
4379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    fontSizeChangedListeners.add(listener);
4389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
4399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
4419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Remove an {@link FontSizeChangedListener} from the list of listeners for this bridge.
4429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   *
4439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @param listener
4449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
4459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void removeFontSizeChangedListener(FontSizeChangedListener listener) {
4469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    fontSizeChangedListeners.remove(listener);
4479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
4489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
4509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Something changed in our parent {@link TerminalView}, maybe it's a new parent, or maybe it's an
4519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * updated font size. We should recalculate terminal size information and request a PTY resize.
4529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
4539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public final synchronized void parentChanged(TerminalView parent) {
4549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (manager != null && !manager.isResizeAllowed()) {
4559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Log.d("Resize is not allowed now");
4569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return;
4579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
4589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    this.parent = parent;
4609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    final int width = parent.getWidth();
4619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    final int height = parent.getHeight();
4629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // Something has gone wrong with our layout; we're 0 width or height!
4649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (width <= 0 || height <= 0) {
4659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return;
4669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
4679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    clipboard = (ClipboardManager) parent.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
4699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    keyListener.setClipboardManager(clipboard);
4709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (!forcedSize) {
4729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // recalculate buffer size
4739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      int newColumns, newRows;
4749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      newColumns = width / charWidth;
4769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      newRows = height / charHeight;
4779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // If nothing has changed in the terminal dimensions and not an intial
4799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // draw then don't blow away scroll regions and such.
4809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (newColumns == columns && newRows == rows) {
4819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        return;
4829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
4839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      columns = newColumns;
4859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      rows = newRows;
4869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
4879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // reallocate new bitmap if needed
4899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    boolean newBitmap = (bitmap == null);
4909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (bitmap != null) {
4919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      newBitmap = (bitmap.getWidth() != width || bitmap.getHeight() != height);
4929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
4939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
4949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (newBitmap) {
4959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      discardBitmap();
4969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
4979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      canvas.setBitmap(bitmap);
4989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
4999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // clear out any old buffer information
5019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint.setColor(Color.BLACK);
5029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    canvas.drawPaint(defaultPaint);
5039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // Stroke the border of the terminal if the size is being forced;
5059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (forcedSize) {
5069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      int borderX = (columns * charWidth) + 1;
5079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      int borderY = (rows * charHeight) + 1;
5089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      defaultPaint.setColor(Color.GRAY);
5109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      defaultPaint.setStrokeWidth(0.0f);
5119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (width >= borderX) {
5129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        canvas.drawLine(borderX, 0, borderX, borderY + 1, defaultPaint);
5139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
5149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (height >= borderY) {
5159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        canvas.drawLine(0, borderY, borderX + 1, borderY, defaultPaint);
5169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
5179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
5189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    try {
5209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // request a terminal pty resize
5219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      synchronized (buffer) {
5229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        buffer.setScreenSize(columns, rows, true);
5239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
5249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (transport != null) {
5269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        transport.setDimensions(columns, rows, width, height);
5279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
5289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } catch (Exception e) {
5299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Log.e("Problem while trying to resize screen or PTY", e);
5309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
5319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // force full redraw with new buffer size
5339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    fullRedraw = true;
5349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    redraw();
5359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    parent.notifyUser(String.format("%d x %d", columns, rows));
5379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    Log.i(String.format("parentChanged() now width=%d, height=%d", columns, rows));
5399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
5409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
5429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Somehow our parent {@link TerminalView} was destroyed. Now we don't need to redraw anywhere,
5439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * and we can recycle our internal bitmap.
5449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
5459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public synchronized void parentDestroyed() {
5469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    parent = null;
5479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    discardBitmap();
5489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
5499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private void discardBitmap() {
5519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (bitmap != null) {
5529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      bitmap.recycle();
5539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
5549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    bitmap = null;
5559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
5569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void onDraw() {
5589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    int fg, bg;
5599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    synchronized (buffer) {
5609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      boolean entireDirty = buffer.update[0] || fullRedraw;
5619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      boolean isWideCharacter = false;
5629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // walk through all lines in the buffer
5649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      for (int l = 0; l < buffer.height; l++) {
5659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        // check if this line is dirty and needs to be repainted
5679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        // also check for entire-buffer dirty flags
5689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        if (!entireDirty && !buffer.update[l + 1]) {
5699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          continue;
5709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
5719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        // reset dirty flag for this line
5739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        buffer.update[l + 1] = false;
5749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        // walk through all characters in this line
5769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        for (int c = 0; c < buffer.width; c++) {
5779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          int addr = 0;
5789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          int currAttr = buffer.charAttributes[buffer.windowBase + l][c];
5799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // check if foreground color attribute is set
5809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if ((currAttr & VDUBuffer.COLOR_FG) != 0) {
5819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            int fgcolor = ((currAttr & VDUBuffer.COLOR_FG) >> VDUBuffer.COLOR_FG_SHIFT) - 1;
5829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            if (fgcolor < 8 && (currAttr & VDUBuffer.BOLD) != 0) {
5839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li              fg = color[fgcolor + 8];
5849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            } else {
5859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li              fg = color[fgcolor];
5869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            }
5879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          } else {
5889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            fg = mDefaultFgColor;
5899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
5909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // check if background color attribute is set
5929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if ((currAttr & VDUBuffer.COLOR_BG) != 0) {
5939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            bg = color[((currAttr & VDUBuffer.COLOR_BG) >> VDUBuffer.COLOR_BG_SHIFT) - 1];
5949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          } else {
5959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            bg = mDefaultBgColor;
5969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
5979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
5989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // support character inversion by swapping background and foreground color
5999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if ((currAttr & VDUBuffer.INVERT) != 0) {
6009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            int swapc = bg;
6019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            bg = fg;
6029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            fg = swapc;
6039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
6049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // set underlined attributes if requested
6069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          defaultPaint.setUnderlineText((currAttr & VDUBuffer.UNDERLINE) != 0);
6079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          isWideCharacter = (currAttr & VDUBuffer.FULLWIDTH) != 0;
6099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if (isWideCharacter) {
6119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            addr++;
6129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          } else {
6139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            // determine the amount of continuous characters with the same settings and print them
6149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            // all at once
6159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            while (c + addr < buffer.width
6169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li                && buffer.charAttributes[buffer.windowBase + l][c + addr] == currAttr) {
6179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li              addr++;
6189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            }
6199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
6209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // Save the current clip region
6229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          canvas.save(Canvas.CLIP_SAVE_FLAG);
6239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // clear this dirty area with background color
6259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          defaultPaint.setColor(bg);
6269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if (isWideCharacter) {
6279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            canvas.clipRect(c * charWidth, l * charHeight, (c + 2) * charWidth, (l + 1)
6289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li                * charHeight);
6299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          } else {
6309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            canvas.clipRect(c * charWidth, l * charHeight, (c + addr) * charWidth, (l + 1)
6319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li                * charHeight);
6329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
6339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          canvas.drawPaint(defaultPaint);
6349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // write the text string starting at 'c' for 'addr' number of characters
6369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          defaultPaint.setColor(fg);
6379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if ((currAttr & VDUBuffer.INVISIBLE) == 0) {
6389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            canvas.drawText(buffer.charArray[buffer.windowBase + l], c, addr, c * charWidth,
6399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li                (l * charHeight) - charTop, defaultPaint);
6409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
6419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // Restore the previous clip region
6439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          canvas.restore();
6449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          // advance to the next text block with different characteristics
6469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          c += addr - 1;
6479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          if (isWideCharacter) {
6489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li            c++;
6499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          }
6509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        }
6519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
6529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      // reset entire-buffer flags
6549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      buffer.update[0] = false;
6559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
6569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    fullRedraw = false;
6579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
6589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void redraw() {
6609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (parent != null) {
6619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      parent.postInvalidate();
6629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
6639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
6649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  // We don't have a scroll bar.
6669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void updateScrollBar() {
6679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
6689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /**
6709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * Resize terminal to fit [rows]x[cols] in screen of size [width]x[height]
6719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   *
6729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @param rows
6739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @param cols
6749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @param width
6759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @param height
6769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
6779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public synchronized void resizeComputed(int cols, int rows, int width, int height) {
6789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    float size = 8.0f;
6799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    float step = 8.0f;
6809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    float limit = 0.125f;
6819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    int direction;
6839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    while ((direction = fontSizeCompare(size, cols, rows, width, height)) < 0) {
6859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      size += step;
6869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
6879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (direction == 0) {
6899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      Log.d(String.format("Fontsize: found match at %f", size));
6909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return;
6919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
6929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    step /= 2.0f;
6949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    size -= step;
6959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
6969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    while ((direction = fontSizeCompare(size, cols, rows, width, height)) != 0 && step >= limit) {
6979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      step /= 2.0f;
6989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (direction > 0) {
6999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        size -= step;
7009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      } else {
7019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        size += step;
7029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
7039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
7049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (direction > 0) {
7069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      size -= step;
7079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
7089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    columns = cols;
7109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    this.rows = rows;
7119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    setFontSize(size);
7129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    forcedSize = true;
7139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  private int fontSizeCompare(float size, int cols, int rows, int width, int height) {
7169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // read new metrics to get exact pixel dimensions
7179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint.setTextSize(size);
7189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    FontMetrics fm = defaultPaint.getFontMetrics();
7199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    float[] widths = new float[1];
7219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    defaultPaint.getTextWidths("X", widths);
7229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    int termWidth = (int) widths[0] * cols;
7239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    int termHeight = (int) Math.ceil(fm.descent - fm.top) * rows;
7249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    Log.d(String.format("Fontsize: font size %f resulted in %d x %d", size, termWidth, termHeight));
7269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // Check to see if it fits in resolution specified.
7289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (termWidth > width || termHeight > height) {
7299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return 1;
7309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
7319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (termWidth == width || termHeight == height) {
7339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return 0;
7349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
7359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return -1;
7379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /*
7409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * (non-Javadoc)
7419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   *
7429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @see de.mud.terminal.VDUDisplay#setVDUBuffer(de.mud.terminal.VDUBuffer)
7439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
7449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  @Override
7459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void setVDUBuffer(VDUBuffer buffer) {
7469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  /*
7499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * (non-Javadoc)
7509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   *
7519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   * @see de.mud.terminal.VDUDisplay#setColor(byte, byte, byte, byte)
7529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li   */
7539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void setColor(int index, int red, int green, int blue) {
7549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // Don't allow the system colors to be overwritten for now. May violate specs.
7559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (index < color.length && index >= 16) {
7569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      color[index] = 0xff000000 | red << 16 | green << 8 | blue;
7579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
7589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public final void resetColors() {
7619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    System.arraycopy(Colors.defaults, 0, color, 0, Colors.defaults.length);
7629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public TerminalKeyListener getKeyHandler() {
7659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return keyListener;
7669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void resetScrollPosition() {
7699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    // if we're in scrollback, scroll to bottom of window on input
7709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (buffer.windowBase != buffer.screenBase) {
7719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      buffer.setWindowBase(buffer.screenBase);
7729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
7739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void increaseFontSize() {
7769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    setFontSize(fontSize + FONT_SIZE_STEP);
7779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void decreaseFontSize() {
7809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    setFontSize(fontSize - FONT_SIZE_STEP);
7819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public int getId() {
7849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return mProcess.getPort();
7859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public String getName() {
7889f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return mProcess.getName();
7899f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7909f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7919f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public InterpreterProcess getProcess() {
7929f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return mProcess;
7939f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7949f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7959f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public int getForegroundColor() {
7969f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return mDefaultFgColor;
7979f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
7989f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
7999f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public int getBackgroundColor() {
8009f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return mDefaultBgColor;
8019f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8029f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
8039f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public VDUBuffer getVDUBuffer() {
8049f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return buffer;
8059f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8069f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
8079f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public PromptHelper getPromptHelper() {
8089f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return promptHelper;
8099f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8109f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
8119f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public Bitmap getBitmap() {
8129f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return bitmap;
8139f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8149f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
8159f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public AbsTransport getTransport() {
8169f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return transport;
8179f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8189f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
8199f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public Paint getPaint() {
8209f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return defaultPaint;
8219f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8229f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
8239f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
8249f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (mProcess.isAlive()) {
8259f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      RpcReceiverManagerFactory rpcReceiverManagerFactory = mProcess.getRpcReceiverManagerFactory();
8269f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers().values()) {
8279f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        UiFacade facade = manager.getReceiver(UiFacade.class);
8289f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        facade.onCreateContextMenu(menu, v, menuInfo);
8299f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
8309f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
8319f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8329f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
8339f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public boolean onPrepareOptionsMenu(Menu menu) {
8349f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    boolean returnValue = false;
8359f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (mProcess.isAlive()) {
8369f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      RpcReceiverManagerFactory rpcReceiverManagerFactory = mProcess.getRpcReceiverManagerFactory();
8379f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      for (RpcReceiverManager manager : rpcReceiverManagerFactory.getRpcReceiverManagers().values()) {
8389f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        UiFacade facade = manager.getReceiver(UiFacade.class);
8399f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        returnValue = returnValue || facade.onPrepareOptionsMenu(menu);
8409f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
8419f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      return returnValue;
8429f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
8439f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    return false;
8449f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8459f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li
8469f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
8479f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (PreferenceConstants.ENCODING.equals(key)) {
8489f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      updateCharset();
8499f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } else if (PreferenceConstants.FONTSIZE.equals(key)) {
8509f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      String string = manager.getStringParameter(PreferenceConstants.FONTSIZE, null);
8519f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (string != null) {
8529f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        fontSize = Float.parseFloat(string);
8539f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      } else {
8549f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        fontSize = PreferenceConstants.DEFAULT_FONT_SIZE;
8559f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
8569f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      setFontSize(fontSize);
8579f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      fullRedraw = true;
8589f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } else if (PreferenceConstants.SCROLLBACK.equals(key)) {
8599f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      String string = manager.getStringParameter(PreferenceConstants.SCROLLBACK, null);
8609f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (string != null) {
8619f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        scrollback = Integer.parseInt(string);
8629f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      } else {
8639f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        scrollback = PreferenceConstants.DEFAULT_SCROLLBACK;
8649f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
8659f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      buffer.setBufferSize(scrollback);
8669f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } else if (PreferenceConstants.COLOR_FG.equals(key)) {
8679f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mDefaultFgColor =
8689f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          manager.getIntParameter(PreferenceConstants.COLOR_FG,
8699f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li              PreferenceConstants.DEFAULT_FG_COLOR);
8709f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      fullRedraw = true;
8719f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    } else if (PreferenceConstants.COLOR_BG.equals(key)) {
8729f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      mDefaultBgColor =
8739f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          manager.getIntParameter(PreferenceConstants.COLOR_BG,
8749f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li              PreferenceConstants.DEFAULT_BG_COLOR);
8759f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      fullRedraw = true;
8769f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
8779f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    if (PreferenceConstants.DELKEY.equals(key)) {
8789f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      delKey =
8799f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li          manager.getStringParameter(PreferenceConstants.DELKEY, PreferenceConstants.DELKEY_DEL);
8809f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      if (PreferenceConstants.DELKEY_BACKSPACE.equals(delKey)) {
8819f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        ((vt320) buffer).setBackspace(vt320.DELETE_IS_BACKSPACE);
8829f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      } else {
8839f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li        ((vt320) buffer).setBackspace(vt320.DELETE_IS_DEL);
8849f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li      }
8859f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li    }
8869f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li  }
8879f32db87b486c93a0ea71eb1781ee45676b8bf8bXin Li}
888