Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Refactor session and widget code into separate library project

We'd like to make the terminal emulator and view reusable by others, so
separate out this code (primarily TermSession, TerminalEmulator,
TranscriptScreen, EmulatorView, and associated classes) into a library
usable by other projects.  Users of the new library are expected to
instantiate (or subclass) TermSession, connect it to the emulation
client via an InputStream and OutputStream, and then attach it to an
EmulatorView, which will display the terminal screen and scrollback.

In the app, we subclass both TermSession (ShellTermSession) and
EmulatorView (TermView) to consolidate the setting and updating of
preferences in one place.  Since tty-related handling stays in the app
(there's no guarantee library users will be using a tty), we also
override various methods of TermSession to handle our tty functionality.
  • Loading branch information...
commit 252e24914da78b3d0e2b3b290508a7d7e261dd5a 1 parent dd31e3c
@steven676 steven676 authored
Showing with 780 additions and 361 deletions.
  1. +6 −0 libraries/emulatorview/AndroidManifest.xml
  2. +17 −0 libraries/emulatorview/ant.properties
  3. +83 −0 libraries/emulatorview/build.xml
  4. +20 −0 libraries/emulatorview/proguard-project.txt
  5. +15 −0 libraries/emulatorview/project.properties
  6. 0  { → libraries/emulatorview}/res/drawable-nodpi/atari_small_nodpi.png
  7. 0  { → libraries/emulatorview}/res/drawable/atari_small.png
  8. +1 −1  ...jackpal/androidterm/util → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/ByteQueue.java
  9. +62 −0 libraries/emulatorview/src/jackpal/androidterm/emulatorview/ColorScheme.java
  10. +52 −0 libraries/emulatorview/src/jackpal/androidterm/emulatorview/EmulatorDebug.java
  11. +53 −57 {src/jackpal/androidterm → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/EmulatorView.java
  12. +1 −1  {src/jackpal/androidterm/model → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/Screen.java
  13. +254 −0 libraries/emulatorview/src/jackpal/androidterm/emulatorview/TermSession.java
  14. +30 −38 ...droidterm/session → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/TerminalEmulator.java
  15. +1 −1  ...pal/androidterm/model → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/TextRenderer.java
  16. +7 −10 ...droidterm/session → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/TranscriptScreen.java
  17. +2 −2 ...androidterm/util → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/UnicodeTranscript.java
  18. +1 −1  ...l/androidterm/model → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/UpdateCallback.java
  19. +1 −1  ...term → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/compat/AndroidCharacterCompat.java
  20. +19 −0 libraries/emulatorview/src/jackpal/androidterm/emulatorview/compat/AndroidCompat.java
  21. +1 −1  ...dterm → libraries/emulatorview/src/jackpal/androidterm/emulatorview}/compat/KeyCharacterMapCompat.java
  22. +1 −0  project.properties
  23. +4 −2 src/jackpal/androidterm/RemoteInterface.java
  24. +63 −214 src/jackpal/androidterm/{session/TermSession.java → ShellTermSession.java}
  25. +26 −9 src/jackpal/androidterm/Term.java
  26. +0 −17 src/jackpal/androidterm/TermDebug.java
  27. +2 −1  src/jackpal/androidterm/TermService.java
  28. +50 −0 src/jackpal/androidterm/TermView.java
  29. +3 −1 src/jackpal/androidterm/TermViewFlipper.java
  30. +3 −2 src/jackpal/androidterm/WindowListAdapter.java
  31. +2 −2 src/jackpal/androidterm/util/SessionList.java
View
6 libraries/emulatorview/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="jackpal.androidterm.emulatorview"
+ android:versionCode="43"
+ android:versionName="1.0.42">
+</manifest>
View
17 libraries/emulatorview/ant.properties
@@ -0,0 +1,17 @@
+# This file is used to override default values used by the Ant build system.
+#
+# This file must be checked into Version Control Systems, as it is
+# integral to the build system of your project.
+
+# This file is only used by the Ant script.
+
+# You can use this to override default values such as
+# 'source.dir' for the location of your java source folder and
+# 'out.dir' for the location of your output folder.
+
+# You can also use it define how the release builds are signed by declaring
+# the following properties:
+# 'key.store' for the location of your keystore and
+# 'key.alias' for the name of the key to use.
+# The password will be asked during the build when you use the 'release' target.
+
View
83 libraries/emulatorview/build.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="EmulatorView" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
View
20 libraries/emulatorview/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
View
15 libraries/emulatorview/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+android.library=true
+# Project target.
+target=android-11
View
0  res/drawable-nodpi/atari_small_nodpi.png → ...atorview/res/drawable-nodpi/atari_small_nodpi.png
File renamed without changes
View
0  res/drawable/atari_small.png → libraries/emulatorview/res/drawable/atari_small.png
File renamed without changes
View
2  src/jackpal/androidterm/util/ByteQueue.java → ...c/jackpal/androidterm/emulatorview/ByteQueue.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package jackpal.androidterm.util;
+package jackpal.androidterm.emulatorview;
/**
* A multi-thread-safe produce-consumer byte array.
View
62 libraries/emulatorview/src/jackpal/androidterm/emulatorview/ColorScheme.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 Steven Luo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jackpal.androidterm.emulatorview;
+
+/**
+ * A color scheme for a 16-color VT100 terminal.
+ */
+
+public class ColorScheme {
+ int foreColorIndex;
+ int foreColor;
+ int backColorIndex;
+ int backColor;
+
+ public ColorScheme(int foreColorIndex, int foreColor, int backColorIndex, int backColor) {
+ this.foreColorIndex = foreColorIndex;
+ this.foreColor = foreColor;
+ this.backColorIndex = backColorIndex;
+ this.backColor = backColor;
+ }
+
+ public ColorScheme(int[] scheme) {
+ if (scheme.length != 4) {
+ throw new IllegalArgumentException();
+ }
+
+ this.foreColorIndex = scheme[0];
+ this.foreColor = scheme[1];
+ this.backColorIndex = scheme[2];
+ this.backColor = scheme[3];
+ }
+
+ public int getForeColor() {
+ return foreColor;
+ }
+
+ public int getBackColor() {
+ return backColor;
+ }
+
+ public int getForeColorIndex() {
+ return foreColorIndex;
+ }
+
+ public int getBackColorIndex() {
+ return backColorIndex;
+ }
+}
View
52 libraries/emulatorview/src/jackpal/androidterm/emulatorview/EmulatorDebug.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jackpal.androidterm.emulatorview;
+
+/**
+ * Debug settings.
+ */
+
+public class EmulatorDebug {
+ /**
+ * Set to true to add debugging code and logging.
+ */
+ public static final boolean DEBUG = false;
+
+ /**
+ * Set to true to log IME calls.
+ */
+ public static final boolean LOG_IME = DEBUG && false;
+
+ /**
+ * Set to true to log each character received from the remote process to the
+ * android log, which makes it easier to debug some kinds of problems with
+ * emulating escape sequences and control codes.
+ */
+ public static final boolean LOG_CHARACTERS_FLAG = DEBUG && false;
+
+ /**
+ * Set to true to log unknown escape sequences.
+ */
+ public static final boolean LOG_UNKNOWN_ESCAPE_SEQUENCES = DEBUG && false;
+
+ /**
+ * The tag we use when logging, so that our messages can be distinguished
+ * from other messages in the log. Public because it's used by several
+ * classes.
+ */
+ public static final String LOG_TAG = "EmulatorView";
+}
View
110 src/jackpal/androidterm/EmulatorView.java → ...ackpal/androidterm/emulatorview/EmulatorView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package jackpal.androidterm;
+package jackpal.androidterm.emulatorview;
import java.io.IOException;
@@ -47,25 +47,17 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import jackpal.androidterm.compat.AndroidCompat;
-import jackpal.androidterm.compat.KeyCharacterMapCompat;
-import jackpal.androidterm.model.TextRenderer;
-import jackpal.androidterm.model.UpdateCallback;
-import jackpal.androidterm.session.TerminalEmulator;
-import jackpal.androidterm.session.TermSession;
-import jackpal.androidterm.session.TranscriptScreen;
-import jackpal.androidterm.util.TermSettings;
+import jackpal.androidterm.emulatorview.compat.AndroidCompat;
+import jackpal.androidterm.emulatorview.compat.KeyCharacterMapCompat;
/**
* A view on a transcript and a terminal emulator. Displays the text of the
* transcript and the current cursor position of the terminal emulator.
*/
public class EmulatorView extends View implements GestureDetector.OnGestureListener {
-
private final String TAG = "EmulatorView";
- private final boolean LOG_KEY_EVENTS = TermDebug.DEBUG && false;
-
- private TermSettings mSettings;
+ private final boolean LOG_KEY_EVENTS = false;
+ private final boolean LOG_IME = false;
/**
* We defer some initialization until we have been layed out in the view
@@ -114,13 +106,13 @@
/**
* Foreground color.
*/
- private int mForeground = TermSettings.WHITE;
+ private int mForeground = 0xffff0000;
private int mForegroundIndex;
/**
* Background color.
*/
- private int mBackground = TermSettings.BLACK;
+ private int mBackground = 0xff000000;
private int mBackgroundIndex;
/**
@@ -166,6 +158,9 @@
private boolean mIsSelectingText = false;
+ private boolean mBackKeySendsCharacter = false;
+ private int mControlKeyCode;
+ private int mFnKeyCode;
private boolean mIsControlKeySent = false;
private boolean mIsFnKeySent = false;
@@ -251,21 +246,11 @@ public void onPause() {
mIsActive = false;
}
- public void updatePrefs(TermSettings settings) {
- mSettings = settings;
- setTextSize((int) (mSettings.getFontSize() * mDensity));
- setCursorStyle(mSettings.getCursorStyle(), mSettings.getCursorBlink());
- setUseCookedIME(mSettings.useCookedIME());
- setColors();
- mKeyListener.setBackKeyCharacter(settings.getBackKeyCharacter());
- }
-
- public void setColors() {
- int[] scheme = mSettings.getColorScheme();
- mForegroundIndex = scheme[0];
- mForeground = scheme[1];
- mBackgroundIndex = scheme[2];
- mBackground = scheme[3];
+ public void setColorScheme(ColorScheme scheme) {
+ mForegroundIndex = scheme.getForeColorIndex();
+ mForeground = scheme.getForeColor();
+ mBackgroundIndex = scheme.getBackColorIndex();
+ mBackground = scheme.getBackColor();
updateText();
}
@@ -338,7 +323,7 @@ private void mapAndSend(int c) throws IOException {
}
public boolean beginBatchEdit() {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "beginBatchEdit");
}
setImeBuffer("");
@@ -350,21 +335,21 @@ public boolean beginBatchEdit() {
}
public boolean clearMetaKeyStates(int arg0) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "clearMetaKeyStates " + arg0);
}
return false;
}
public boolean commitCompletion(CompletionInfo arg0) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "commitCompletion " + arg0);
}
return false;
}
public boolean endBatchEdit() {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "endBatchEdit");
}
mInBatchEdit = false;
@@ -372,7 +357,7 @@ public boolean endBatchEdit() {
}
public boolean finishComposingText() {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "finishComposingText");
}
sendText(mImeBuffer);
@@ -384,7 +369,7 @@ public boolean finishComposingText() {
}
public int getCursorCapsMode(int arg0) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "getCursorCapsMode(" + arg0 + ")");
}
return 0;
@@ -392,14 +377,14 @@ public int getCursorCapsMode(int arg0) {
public ExtractedText getExtractedText(ExtractedTextRequest arg0,
int arg1) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "getExtractedText" + arg0 + "," + arg1);
}
return null;
}
public CharSequence getTextAfterCursor(int n, int flags) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "getTextAfterCursor(" + n + "," + flags + ")");
}
int len = Math.min(n, mImeBuffer.length() - mCursor);
@@ -410,7 +395,7 @@ public CharSequence getTextAfterCursor(int n, int flags) {
}
public CharSequence getTextBeforeCursor(int n, int flags) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "getTextBeforeCursor(" + n + "," + flags + ")");
}
int len = Math.min(n, mCursor);
@@ -421,35 +406,35 @@ public CharSequence getTextBeforeCursor(int n, int flags) {
}
public boolean performContextMenuAction(int arg0) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "performContextMenuAction" + arg0);
}
return true;
}
public boolean performPrivateCommand(String arg0, Bundle arg1) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "performPrivateCommand" + arg0 + "," + arg1);
}
return true;
}
public boolean reportFullscreenMode(boolean arg0) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "reportFullscreenMode" + arg0);
}
return true;
}
public boolean commitCorrection (CorrectionInfo correctionInfo) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "commitCorrection");
}
return true;
}
public boolean commitText(CharSequence text, int newCursorPosition) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "commitText(\"" + text + "\", " + newCursorPosition + ")");
}
clearComposingText();
@@ -478,7 +463,7 @@ private void clearComposingText() {
}
public boolean deleteSurroundingText(int leftLength, int rightLength) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "deleteSurroundingText(" + leftLength +
"," + rightLength + ")");
}
@@ -497,7 +482,7 @@ public boolean deleteSurroundingText(int leftLength, int rightLength) {
}
public boolean performEditorAction(int actionCode) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "performEditorAction(" + actionCode + ")");
}
if (actionCode == EditorInfo.IME_ACTION_UNSPECIFIED) {
@@ -508,7 +493,7 @@ public boolean performEditorAction(int actionCode) {
}
public boolean sendKeyEvent(KeyEvent event) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "sendKeyEvent(" + event + ")");
}
// Some keys are sent here rather than to commitText.
@@ -520,7 +505,7 @@ public boolean sendKeyEvent(KeyEvent event) {
}
public boolean setComposingText(CharSequence text, int newCursorPosition) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "setComposingText(\"" + text + "\", " + newCursorPosition + ")");
}
int len = mImeBuffer.length();
@@ -536,7 +521,7 @@ public boolean setComposingText(CharSequence text, int newCursorPosition) {
}
public boolean setSelection(int start, int end) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "setSelection" + start + "," + end);
}
int length = mImeBuffer.length();
@@ -552,7 +537,7 @@ public boolean setSelection(int start, int end) {
}
public boolean setComposingRegion(int start, int end) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "setComposingRegion " + start + "," + end);
}
if (start < end && start > 0 && end < mImeBuffer.length()) {
@@ -564,7 +549,7 @@ public boolean setComposingRegion(int start, int end) {
}
public CharSequence getSelectedText(int flags) {
- if (TermDebug.LOG_IME) {
+ if (LOG_IME) {
Log.w(TAG, "getSelectedText " + flags);
}
int len = mImeBuffer.length();
@@ -602,8 +587,6 @@ private void commonConstructor(Context context, TermSession session) {
setFocusableInTouchMode(true);
mTermSession = session;
- // XXX We should really be able to fetch this from within TermSession
- session.setProcessExitMessage(context.getString(R.string.process_exit_message));
mKeyListener = new TermKeyListener(session);
}
@@ -686,7 +669,7 @@ public void pageHorizontal(int deltaColumns) {
* @param fontSize the new font size, in pixels.
*/
public void setTextSize(int fontSize) {
- mTextSize = fontSize;
+ mTextSize = (int) (fontSize * mDensity);
updateText();
}
@@ -857,7 +840,7 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
/** Do we want to intercept this system key? */
private boolean isInterceptedSystemKey(int keyCode) {
- return keyCode == KeyEvent.KEYCODE_BACK && mSettings.backKeySendsCharacter();
+ return keyCode == KeyEvent.KEYCODE_BACK && mBackKeySendsCharacter;
}
@Override
@@ -883,7 +866,7 @@ public boolean onKeyUp(int keyCode, KeyEvent event) {
private boolean handleControlKey(int keyCode, boolean down) {
- if (keyCode == mSettings.getControlKeyCode()) {
+ if (keyCode == mControlKeyCode) {
if (LOG_KEY_EVENTS) {
Log.w(TAG, "handleControlKey " + keyCode);
}
@@ -894,7 +877,7 @@ private boolean handleControlKey(int keyCode, boolean down) {
}
private boolean handleFnKey(int keyCode, boolean down) {
- if (keyCode == mSettings.getFnKeyCode()) {
+ if (keyCode == mFnKeyCode) {
if (LOG_KEY_EVENTS) {
Log.w(TAG, "handleFnKey " + keyCode);
}
@@ -1056,6 +1039,19 @@ public void sendFnKey() {
mIsFnKeySent = true;
mKeyListener.handleFnKey(true);
}
+
+ public void setBackKeyCharacter(int keyCode) {
+ mKeyListener.setBackKeyCharacter(keyCode);
+ mBackKeySendsCharacter = (keyCode != 0);
+ }
+
+ public void setControlKeyCode(int keyCode) {
+ mControlKeyCode = keyCode;
+ }
+
+ public void setFnKeyCode(int keyCode) {
+ mFnKeyCode = keyCode;
+ }
}
abstract class BaseTextRenderer implements TextRenderer {
View
2  src/jackpal/androidterm/model/Screen.java → .../src/jackpal/androidterm/emulatorview/Screen.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package jackpal.androidterm.model;
+package jackpal.androidterm.emulatorview;
/**
* An abstract screen interface. A terminal screen stores lines of text. (The
View
254 libraries/emulatorview/src/jackpal/androidterm/emulatorview/TermSession.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jackpal.androidterm.emulatorview;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * A terminal session, consisting of a TerminalEmulator, a TranscriptScreen,
+ * the PID of the process attached to the session, and the I/O streams used to
+ * talk to the process.
+ */
+public class TermSession {
+ private ColorScheme mColorScheme;
+ private UpdateCallback mNotify;
+
+ private OutputStream mTermOut;
+ private InputStream mTermIn;
+
+ private TranscriptScreen mTranscriptScreen;
+ private TerminalEmulator mEmulator;
+
+ private boolean mDefaultUTF8Mode;
+
+ private Thread mPollingThread;
+ private ByteQueue mByteQueue;
+ private byte[] mReceiveBuffer;
+
+ private CharBuffer mWriteCharBuffer;
+ private ByteBuffer mWriteByteBuffer;
+ private CharsetEncoder mUTF8Encoder;
+
+ // Number of rows in the transcript
+ private static final int TRANSCRIPT_ROWS = 10000;
+
+ private static final int NEW_INPUT = 1;
+
+ /**
+ * Callback to be invoked when a TermSession finishes.
+ */
+ public interface FinishCallback {
+ void onSessionFinish(TermSession session);
+ }
+ private FinishCallback mFinishCallback;
+
+ private boolean mIsRunning = false;
+ private Handler mMsgHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (!mIsRunning) {
+ return;
+ }
+ if (msg.what == NEW_INPUT) {
+ readFromProcess();
+ }
+ }
+ };
+
+ public TermSession() {
+ mWriteCharBuffer = CharBuffer.allocate(2);
+ mWriteByteBuffer = ByteBuffer.allocate(4);
+ mUTF8Encoder = Charset.forName("UTF-8").newEncoder();
+ mUTF8Encoder.onMalformedInput(CodingErrorAction.REPLACE);
+ mUTF8Encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+
+ mReceiveBuffer = new byte[4 * 1024];
+ mByteQueue = new ByteQueue(4 * 1024);
+
+ mPollingThread = new Thread() {
+ private byte[] mBuffer = new byte[4096];
+
+ @Override
+ public void run() {
+ try {
+ while(true) {
+ int read = mTermIn.read(mBuffer);
+ if (read == -1) {
+ // EOF -- process exited
+ return;
+ }
+ mByteQueue.write(mBuffer, 0, read);
+ mMsgHandler.sendMessage(
+ mMsgHandler.obtainMessage(NEW_INPUT));
+ }
+ } catch (IOException e) {
+ } catch (InterruptedException e) {
+ }
+ }
+ };
+ mPollingThread.setName("Input reader");
+ }
+
+ public void initializeEmulator(int columns, int rows) {
+ mTranscriptScreen = new TranscriptScreen(columns, TRANSCRIPT_ROWS, rows, mColorScheme);
+ mEmulator = new TerminalEmulator(mTranscriptScreen, columns, rows, mTermOut, mColorScheme);
+ mEmulator.setDefaultUTF8Mode(mDefaultUTF8Mode);
+
+ mIsRunning = true;
+ mPollingThread.start();
+ }
+
+ public void write(String data) {
+ try {
+ mTermOut.write(data.getBytes("UTF-8"));
+ mTermOut.flush();
+ } catch (IOException e) {
+ // Ignore exception
+ // We don't really care if the receiver isn't listening.
+ // We just make a best effort to answer the query.
+ }
+ }
+
+ public void write(int codePoint) {
+ CharBuffer charBuf = mWriteCharBuffer;
+ ByteBuffer byteBuf = mWriteByteBuffer;
+ CharsetEncoder encoder = mUTF8Encoder;
+ try {
+ charBuf.clear();
+ byteBuf.clear();
+ Character.toChars(codePoint, charBuf.array(), 0);
+ encoder.reset();
+ encoder.encode(charBuf, byteBuf, true);
+ encoder.flush(byteBuf);
+ mTermOut.write(byteBuf.array(), 0, byteBuf.position()-1);
+ mTermOut.flush();
+ } catch (IOException e) {
+ // Ignore exception
+ }
+ }
+
+ public OutputStream getTermOut() {
+ return mTermOut;
+ }
+
+ public void setTermOut(OutputStream termOut) {
+ mTermOut = termOut;
+ }
+
+ public InputStream getTermIn() {
+ return mTermIn;
+ }
+
+ public void setTermIn(InputStream termIn) {
+ mTermIn = termIn;
+ }
+
+ public boolean isRunning() {
+ return mIsRunning;
+ }
+
+ public TranscriptScreen getTranscriptScreen() {
+ return mTranscriptScreen;
+ }
+
+ public TerminalEmulator getEmulator() {
+ return mEmulator;
+ }
+
+ public void setUpdateCallback(UpdateCallback notify) {
+ mNotify = notify;
+ }
+
+ protected void notifyUpdate() {
+ if (mNotify != null) {
+ mNotify.onUpdate();
+ }
+ }
+
+ /* Override this method if you support terminal size setting, but do
+ call through to the superclass method */
+ public void updateSize(int columns, int rows) {
+ if (mEmulator == null) {
+ initializeEmulator(columns, rows);
+ } else {
+ mEmulator.updateSize(columns, rows);
+ }
+ }
+
+ public String getTranscriptText() {
+ return mTranscriptScreen.getTranscriptText();
+ }
+
+ /**
+ * Look for new input from the ptty, send it to the terminal emulator.
+ */
+ private void readFromProcess() {
+ int bytesAvailable = mByteQueue.getBytesAvailable();
+ int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
+ try {
+ int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
+ mEmulator.append(mReceiveBuffer, 0, bytesRead);
+ } catch (InterruptedException e) {
+ }
+ notifyUpdate();
+ }
+
+ public void setColorScheme(ColorScheme scheme) {
+ mColorScheme = scheme;
+ if (mEmulator == null) {
+ return;
+ }
+ mEmulator.setColorScheme(scheme);
+ mTranscriptScreen.setColorScheme(scheme);
+ }
+
+ public void setDefaultUTF8Mode(boolean utf8ByDefault) {
+ mDefaultUTF8Mode = utf8ByDefault;
+ if (mEmulator == null) {
+ return;
+ }
+ mEmulator.setDefaultUTF8Mode(utf8ByDefault);
+ }
+
+ public void reset() {
+ mEmulator.reset();
+ notifyUpdate();
+ }
+
+ public void setFinishCallback(FinishCallback callback) {
+ mFinishCallback = callback;
+ }
+
+ public void finish() {
+ mIsRunning = false;
+ mTranscriptScreen.finish();
+ if (mFinishCallback != null) {
+ mFinishCallback.onSessionFinish(this);
+ }
+ }
+}
View
68 ...jackpal/androidterm/session/TerminalEmulator.java → ...al/androidterm/emulatorview/TerminalEmulator.java
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package jackpal.androidterm.session;
+package jackpal.androidterm.emulatorview;
-import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
@@ -26,11 +26,6 @@
import android.util.Log;
-import jackpal.androidterm.TermDebug;
-import jackpal.androidterm.model.Screen;
-import jackpal.androidterm.util.TermSettings;
-import jackpal.androidterm.util.UnicodeTranscript;
-
/**
* Renders text into a screen. Contains all the terminal-specific knowlege and
* state. Emulates a subset of the X Window System xterm terminal, which in turn
@@ -39,8 +34,6 @@
* video, color) alternate screen cursor key and keypad escape sequences.
*/
public class TerminalEmulator {
- private TermSettings mTermSettings;
-
/**
* The cursor row. Numbered 0..mRows-1.
*/
@@ -65,7 +58,7 @@
* Used to send data to the remote process. Needed to implement the various
* "report" escape sequences.
*/
- private FileOutputStream mTermOut;
+ private OutputStream mTermOut;
/**
* Stores the characters that appear on the screen of the emulated terminal.
@@ -326,6 +319,7 @@
* UTF-8 support
*/
private static final int UNICODE_REPLACEMENT_CHAR = 0xfffd;
+ private boolean mDefaultUTF8Mode = false;
private boolean mUTF8Mode = false;
private boolean mUTF8EscapeUsed = false;
private int mUTF8ToFollow = 0;
@@ -341,15 +335,15 @@
* @param rows the number of rows to emulate
* @param termOut the output file descriptor that talks to the pseudo-tty.
*/
- public TerminalEmulator(TermSettings termSettings,
- Screen screen, int columns, int rows, FileOutputStream termOut) {
- mTermSettings = termSettings;
+ public TerminalEmulator(Screen screen, int columns, int rows, OutputStream termOut, ColorScheme scheme) {
mScreen = screen;
mRows = rows;
mColumns = columns;
mTabStop = new boolean[mColumns];
mTermOut = termOut;
+ setColorScheme(scheme);
+
mUTF8ByteBuffer = ByteBuffer.allocate(4);
mInputCharBuffer = CharBuffer.allocate(2);
mUTF8Decoder = Charset.forName("UTF-8").newDecoder();
@@ -502,18 +496,18 @@ public void append(byte[] buffer, int base, int length) {
for (int i = 0; i < length; i++) {
byte b = buffer[base + i];
try {
- if (TermDebug.LOG_CHARACTERS_FLAG) {
+ if (EmulatorDebug.LOG_CHARACTERS_FLAG) {
char printableB = (char) b;
if (b < 32 || b > 126) {
printableB = ' ';
}
- Log.w(TermDebug.LOG_TAG, "'" + Character.toString(printableB)
+ Log.w(EmulatorDebug.LOG_TAG, "'" + Character.toString(printableB)
+ "' (" + Integer.toString(b) + ")");
}
process(b);
mProcessedCharCount++;
} catch (Exception e) {
- Log.e(TermDebug.LOG_TAG, "Exception while processing character "
+ Log.e(EmulatorDebug.LOG_TAG, "Exception while processing character "
+ Integer.toString(mProcessedCharCount) + " code "
+ Integer.toString(b), e);
}
@@ -1179,8 +1173,8 @@ private void selectGraphicRendition() {
} else if (code == 49) { // set default background color
mBackColor = mDefaultBackColor | (mBackColor & 0x8); // preserve underscore.
} else {
- if (TermDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
- Log.w(TermDebug.LOG_TAG, String.format("SGR unknown code %d", code));
+ if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
+ Log.w(EmulatorDebug.LOG_TAG, String.format("SGR unknown code %d", code));
}
}
}
@@ -1335,21 +1329,21 @@ private int getArg(int index, int defaultValue) {
}
private void unimplementedSequence(byte b) {
- if (TermDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
+ if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
logError("unimplemented", b);
}
finishSequence();
}
private void unknownSequence(byte b) {
- if (TermDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
+ if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
logError("unknown", b);
}
finishSequence();
}
private void unknownParameter(int parameter) {
- if (TermDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
+ if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
StringBuilder buf = new StringBuilder();
buf.append("Unknown parameter");
buf.append(parameter);
@@ -1358,7 +1352,7 @@ private void unknownParameter(int parameter) {
}
private void logError(String errorType, byte b) {
- if (TermDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
+ if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
StringBuilder buf = new StringBuilder();
buf.append(errorType);
buf.append(" sequence ");
@@ -1385,8 +1379,8 @@ private void logError(String errorType, byte b) {
}
private void logError(String error) {
- if (TermDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
- Log.e(TermDebug.LOG_TAG, error);
+ if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
+ Log.e(EmulatorDebug.LOG_TAG, error);
}
finishSequence();
}
@@ -1545,11 +1539,8 @@ public void reset() {
mTopMargin = 0;
mBottomMargin = mRows;
mAboutToAutoWrap = false;
- int[] colorScheme = mTermSettings.getColorScheme();
- mDefaultForeColor = colorScheme[0];
- mDefaultBackColor = colorScheme[2];
- mForeColor = colorScheme[0];
- mBackColor = colorScheme[2];
+ mForeColor = mDefaultForeColor;
+ mBackColor = mDefaultBackColor;
mInverseColors = false;
mbKeypadApplicationMode = false;
mAlternateCharSet = false;
@@ -1557,27 +1548,28 @@ public void reset() {
setDefaultTabStops();
blockClear(0, 0, mColumns, mRows);
- mUTF8Mode = mTermSettings.defaultToUTF8Mode();
+ mUTF8Mode = mDefaultUTF8Mode;
mUTF8EscapeUsed = false;
mUTF8ToFollow = 0;
mUTF8ByteBuffer.clear();
mInputCharBuffer.clear();
}
- public void updatePrefs(TermSettings settings) {
- mTermSettings = settings;
+ public void setDefaultUTF8Mode(boolean defaultToUTF8Mode) {
+ mDefaultUTF8Mode = defaultToUTF8Mode;
if (!mUTF8EscapeUsed) {
- boolean newUTF8Mode = settings.defaultToUTF8Mode();
- if (mUTF8Mode && !newUTF8Mode) {
+ if (mUTF8Mode && !defaultToUTF8Mode) {
mUTF8ToFollow = 0;
mUTF8ByteBuffer.clear();
mInputCharBuffer.clear();
}
- mUTF8Mode = newUTF8Mode;
+ mUTF8Mode = defaultToUTF8Mode;
}
- int[] colorScheme = mTermSettings.getColorScheme();
- mDefaultForeColor = colorScheme[0];
- mDefaultBackColor = colorScheme[2];
+ }
+
+ public void setColorScheme(ColorScheme scheme) {
+ mDefaultForeColor = scheme.getForeColorIndex();
+ mDefaultBackColor = scheme.getBackColorIndex();
}
public String getSelectedText(int x1, int y1, int x2, int y2) {
View
2  src/jackpal/androidterm/model/TextRenderer.java → ...ackpal/androidterm/emulatorview/TextRenderer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package jackpal.androidterm.model;
+package jackpal.androidterm.emulatorview;
import android.graphics.Canvas;
View
17 ...jackpal/androidterm/session/TranscriptScreen.java → ...al/androidterm/emulatorview/TranscriptScreen.java
@@ -14,17 +14,13 @@
* limitations under the License.
*/
-package jackpal.androidterm.session;
+package jackpal.androidterm.emulatorview;
import java.util.Arrays;
import android.graphics.Canvas;
import android.util.Log;
-import jackpal.androidterm.model.Screen;
-import jackpal.androidterm.model.TextRenderer;
-import jackpal.androidterm.util.UnicodeTranscript;
-
/**
* A TranscriptScreen is a screen that remembers data that's been scrolled. The
* old data is stored in a ring buffer to minimize the amount of copying that
@@ -62,20 +58,21 @@
* screen.
*/
public TranscriptScreen(int columns, int totalRows, int screenRows,
- int foreColor, int backColor) {
- init(columns, totalRows, screenRows, foreColor, backColor);
+ ColorScheme scheme) {
+ init(columns, totalRows, screenRows, scheme.getForeColorIndex(), scheme.getBackColorIndex());
}
private void init(int columns, int totalRows, int screenRows, int foreColor, int backColor) {
mColumns = columns;
mTotalRows = totalRows;
mScreenRows = screenRows;
+
mData = new UnicodeTranscript(columns, totalRows, screenRows, foreColor, backColor);
mData.blockSet(0, 0, mColumns, mScreenRows, ' ', foreColor, backColor);
- }
+ }
- public void setDefaultColors(int foreColor, int backColor) {
- mData.setDefaultColors(foreColor, backColor);
+ public void setColorScheme(ColorScheme scheme) {
+ mData.setDefaultColors(scheme.getForeColorIndex(), scheme.getBackColorIndex());
}
public void finish() {
View
4 src/jackpal/androidterm/util/UnicodeTranscript.java → ...l/androidterm/emulatorview/UnicodeTranscript.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package jackpal.androidterm.util;
+package jackpal.androidterm.emulatorview;
import android.util.Log;
-import jackpal.androidterm.compat.AndroidCharacterCompat;
+import jackpal.androidterm.emulatorview.compat.AndroidCharacterCompat;
/**
* A backing store for a TranscriptScreen.
View
2  src/jackpal/androidterm/model/UpdateCallback.java → ...kpal/androidterm/emulatorview/UpdateCallback.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package jackpal.androidterm.model;
+package jackpal.androidterm.emulatorview;
/**
* Generic callback to be invoked to notify of updates.
View
2  ...al/androidterm/compat/AndroidCharacterCompat.java → ...m/emulatorview/compat/AndroidCharacterCompat.java
@@ -1,4 +1,4 @@
-package jackpal.androidterm.compat;
+package jackpal.androidterm.emulatorview.compat;
import android.text.AndroidCharacter;
View
19 libraries/emulatorview/src/jackpal/androidterm/emulatorview/compat/AndroidCompat.java
@@ -0,0 +1,19 @@
+package jackpal.androidterm.emulatorview.compat;
+
+/**
+ * The classes in this package take advantage of the fact that the VM does
+ * not attempt to load a class until it's accessed, and the verifier
+ * does not run until a class is loaded. By keeping the methods which
+ * are unavailable on older platforms in subclasses which are only ever
+ * accessed on platforms where they are available, we can preserve
+ * compatibility with older platforms without resorting to reflection.
+ *
+ * See http://developer.android.com/resources/articles/backward-compatibility.html
+ * and http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html
+ * for further discussion of this technique.
+ */
+
+public class AndroidCompat {
+ public final static int SDK =
+ Integer.valueOf(android.os.Build.VERSION.SDK);
+}
View
2  ...pal/androidterm/compat/KeyCharacterMapCompat.java → ...rm/emulatorview/compat/KeyCharacterMapCompat.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package jackpal.androidterm.compat;
+package jackpal.androidterm.emulatorview.compat;
import android.view.KeyCharacterMap;
View
1  project.properties
@@ -9,3 +9,4 @@
# Project target.
target=android-11
+android.library.reference.1=libraries/emulatorview
View
6 src/jackpal/androidterm/RemoteInterface.java
@@ -26,7 +26,8 @@
import android.preference.PreferenceManager;
import android.util.Log;
-import jackpal.androidterm.session.TermSession;
+import jackpal.androidterm.emulatorview.TermSession;
+
import jackpal.androidterm.util.TermSettings;
public class RemoteInterface extends Activity {
@@ -96,7 +97,8 @@ private void openNewWindow(String iInitialCommand) {
}
}
- TermSession session = new TermSession(mSettings, service, initialCommand);
+ TermSession session = Term.createTermSession(this, mSettings, initialCommand);
+ session.setFinishCallback(service);
service.getSessions().add(session);
Intent intent = new Intent(Intent.ACTION_MAIN);
View
277 src/jackpal/androidterm/session/TermSession.java → src/jackpal/androidterm/ShellTermSession.java
@@ -14,31 +14,24 @@
* limitations under the License.
*/
-package jackpal.androidterm.session;
+package jackpal.androidterm;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CodingErrorAction;
import java.util.ArrayList;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import jackpal.androidterm.Exec;
-import jackpal.androidterm.TermDebug;
+import jackpal.androidterm.emulatorview.ColorScheme;
+import jackpal.androidterm.emulatorview.TermSession;
+
import jackpal.androidterm.compat.FileCompat;
-import jackpal.androidterm.model.UpdateCallback;
-import jackpal.androidterm.util.ByteQueue;
import jackpal.androidterm.util.TermSettings;
/**
@@ -46,69 +39,41 @@
* the PID of the process attached to the session, and the I/O streams used to
* talk to the process.
*/
-public class TermSession {
+public class ShellTermSession extends TermSession {
private TermSettings mSettings;
- private UpdateCallback mNotify;
private int mProcId;
private FileDescriptor mTermFd;
- private FileOutputStream mTermOut;
- private FileInputStream mTermIn;
- private String mInitialCommand;
private Thread mWatcherThread;
- private TranscriptScreen mTranscriptScreen;
- private TerminalEmulator mEmulator;
-
- private Thread mPollingThread;
- private ByteQueue mByteQueue;
- private byte[] mReceiveBuffer;
+ private String mInitialCommand;
- private CharBuffer mWriteCharBuffer;
- private ByteBuffer mWriteByteBuffer;
- private CharsetEncoder mUTF8Encoder;
+ public static final int PROCESS_EXIT_FINISHES_SESSION = 0;
+ public static final int PROCESS_EXIT_DISPLAYS_MESSAGE = 1;
+ private int mProcessExitBehavior = PROCESS_EXIT_FINISHES_SESSION;
private String mProcessExitMessage;
- // Number of rows in the transcript
- private static final int TRANSCRIPT_ROWS = 10000;
-
- private static final int NEW_INPUT = 1;
- private static final int PROCESS_EXITED = 2;
-
- /**
- * Callback to be invoked when a TermSession finishes.
- */
- public interface FinishCallback {
- void onSessionFinish(TermSession session);
- }
- private FinishCallback mFinishCallback;
+ private static final int PROCESS_EXITED = 1;
- private boolean mIsRunning = false;
private Handler mMsgHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- if (!mIsRunning) {
+ if (!isRunning()) {
return;
}
- if (msg.what == NEW_INPUT) {
- readFromProcess();
- } else if (msg.what == PROCESS_EXITED) {
+ if (msg.what == PROCESS_EXITED) {
onProcessExit((Integer) msg.obj);
}
}
};
- public TermSession(TermSettings settings, FinishCallback finishCallback, String initialCommand) {
- mSettings = settings;
- mFinishCallback = finishCallback;
+ public ShellTermSession(TermSettings settings, String initialCommand) {
+ super();
- int[] processId = new int[1];
+ updatePrefs(settings);
- createSubprocess(processId);
- mProcId = processId[0];
- mTermOut = new FileOutputStream(mTermFd);
- mTermIn = new FileInputStream(mTermFd);
+ initializeSession();
mInitialCommand = initialCommand;
mWatcherThread = new Thread() {
@@ -121,95 +86,65 @@ public void run() {
}
};
mWatcherThread.setName("Process watcher");
+ }
- mWriteCharBuffer = CharBuffer.allocate(2);
- mWriteByteBuffer = ByteBuffer.allocate(4);
- mUTF8Encoder = Charset.forName("UTF-8").newEncoder();
- mUTF8Encoder.onMalformedInput(CodingErrorAction.REPLACE);
- mUTF8Encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
-
- mReceiveBuffer = new byte[4 * 1024];
- mByteQueue = new ByteQueue(4 * 1024);
-
- mPollingThread = new Thread() {
- private byte[] mBuffer = new byte[4096];
-
- @Override
- public void run() {
- try {
- while(true) {
- int read = mTermIn.read(mBuffer);
- if (read == -1) {
- // EOF -- process exited
- return;
- }
- mByteQueue.write(mBuffer, 0, read);
- mMsgHandler.sendMessage(
- mMsgHandler.obtainMessage(NEW_INPUT));
- }
- } catch (IOException e) {
- } catch (InterruptedException e) {
- }
- }
- };
- mPollingThread.setName("Input reader");
+ public void updatePrefs(TermSettings settings) {
+ mSettings = settings;
+ setColorScheme(new ColorScheme(settings.getColorScheme()));
+ setDefaultUTF8Mode(settings.defaultToUTF8Mode());
}
- private void initializeEmulator(int columns, int rows) {
+ private void initializeSession() {
TermSettings settings = mSettings;
- int[] colorScheme = settings.getColorScheme();
- mTranscriptScreen = new TranscriptScreen(columns, TRANSCRIPT_ROWS, rows, colorScheme[0], colorScheme[2]);
- mEmulator = new TerminalEmulator(settings, mTranscriptScreen, columns, rows, mTermOut);
- mIsRunning = true;
- mWatcherThread.start();
- mPollingThread.start();
+ int[] processId = new int[1];
- sendInitialCommand(mInitialCommand);
+ String path = System.getenv("PATH");
+ if (settings.verifyPath()) {
+ path = checkPath(path);
+ }
+ String[] env = new String[2];
+ env[0] = "TERM=" + settings.getTermType();
+ env[1] = "PATH=" + path;
+
+ createSubprocess(processId, settings.getShell(), env);
+ mProcId = processId[0];
+
+ setTermOut(new FileOutputStream(mTermFd));
+ setTermIn(new FileInputStream(mTermFd));
}
- private void sendInitialCommand(String initialCommand) {
- if (initialCommand.length() > 0) {
- write(initialCommand + '\r');
+ private String checkPath(String path) {
+ String[] dirs = path.split(":");
+ StringBuilder checkedPath = new StringBuilder(path.length());
+ for (String dirname : dirs) {
+ File dir = new File(dirname);
+ if (dir.isDirectory() && FileCompat.canExecute(dir)) {
+ checkedPath.append(dirname);
+ checkedPath.append(":");
+ }
}
+ return checkedPath.substring(0, checkedPath.length()-1);
}
- public void write(String data) {
- try {
- mTermOut.write(data.getBytes("UTF-8"));
- mTermOut.flush();
- } catch (IOException e) {
- // Ignore exception
- // We don't really care if the receiver isn't listening.
- // We just make a best effort to answer the query.
- }
+ @Override
+ public void initializeEmulator(int columns, int rows) {
+ super.initializeEmulator(columns, rows);
+ mWatcherThread.start();
+ sendInitialCommand(mInitialCommand);
}
- public void write(int codePoint) {
- CharBuffer charBuf = mWriteCharBuffer;
- ByteBuffer byteBuf = mWriteByteBuffer;
- CharsetEncoder encoder = mUTF8Encoder;
- try {
- charBuf.clear();
- byteBuf.clear();
- Character.toChars(codePoint, charBuf.array(), 0);
- encoder.reset();
- encoder.encode(charBuf, byteBuf, true);
- encoder.flush(byteBuf);
- mTermOut.write(byteBuf.array(), 0, byteBuf.position()-1);
- mTermOut.flush();
- } catch (IOException e) {
- // Ignore exception
+ private void sendInitialCommand(String initialCommand) {
+ if (initialCommand.length() > 0) {
+ write(initialCommand + '\r');
}
}
- private void createSubprocess(int[] processId) {
- TermSettings settings = mSettings;
- String shell = settings.getShell();
+ private void createSubprocess(int[] processId, String shell, String[] env) {
ArrayList<String> argList = parse(shell);
-
String arg0;
String[] args;
+
try {
arg0 = argList.get(0);
File file = new File(arg0);
@@ -222,20 +157,11 @@ private void createSubprocess(int[] processId) {
}
args = argList.toArray(new String[1]);
} catch (Exception e) {
- argList = parse(settings.getFailsafeShell());
+ argList = parse(mSettings.getFailsafeShell());
arg0 = argList.get(0);
args = argList.toArray(new String[1]);
}
- String termType = settings.getTermType();
- String path = System.getenv("PATH");
- if (settings.verifyPath()) {
- path = checkPath(path);
- }
- String[] env = new String[2];
- env[0] = "TERM=" + termType;
- env[1] = "PATH=" + path;
-
mTermFd = Exec.createSubprocess(arg0, args, env, processId);
}
@@ -287,85 +213,11 @@ private void createSubprocess(int[] processId) {
return result;
}
- private String checkPath(String path) {
- String[] dirs = path.split(":");
- StringBuilder checkedPath = new StringBuilder(path.length());
- for (String dirname : dirs) {
- File dir = new File(dirname);
- if (dir.isDirectory() && FileCompat.canExecute(dir)) {
- checkedPath.append(dirname);
- checkedPath.append(":");
- }
- }
- return checkedPath.substring(0, checkedPath.length()-1);
- }
-
- public FileOutputStream getTermOut() {
- return mTermOut;
- }
-
- public TranscriptScreen getTranscriptScreen() {
- return mTranscriptScreen;
- }
-
- public TerminalEmulator getEmulator() {
- return mEmulator;
- }
-
- public void setUpdateCallback(UpdateCallback notify) {
- mNotify = notify;
- }
-
+ @Override
public void updateSize(int columns, int rows) {
// Inform the attached pty of our new size:
Exec.setPtyWindowSize(mTermFd, rows, columns, 0, 0);
-
- if (mEmulator == null) {
- initializeEmulator(columns, rows);
- } else {
- mEmulator.updateSize(columns, rows);
- }
- }
-
- public String getTranscriptText() {
- return mTranscriptScreen.getTranscriptText();
- }
-
- /**
- * Look for new input from the ptty, send it to the terminal emulator.
- */
- private void readFromProcess() {
- int bytesAvailable = mByteQueue.getBytesAvailable();
- int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
- try {
- int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
- mEmulator.append(mReceiveBuffer, 0, bytesRead);
- } catch (InterruptedException e) {
- }
-
- if (mNotify != null) {
- mNotify.onUpdate();
- }
- }
-
- public void updatePrefs(TermSettings settings) {
- mSettings = settings;
- if (mEmulator == null) {
- // Not initialized yet, we'll pick up the settings then
- return;
- }
-
- mEmulator.updatePrefs(settings);
-
- int[] colorScheme = settings.getColorScheme();
- mTranscriptScreen.setDefaultColors(colorScheme[0], colorScheme[2]);
- }
-
- public void reset() {
- mEmulator.reset();
- if (mNotify != null) {
- mNotify.onUpdate();
- }
+ super.updateSize(columns, rows);
}
/* XXX We should really get this ourselves from the resource bundle, but
@@ -376,25 +228,22 @@ public void setProcessExitMessage(String message) {
private void onProcessExit(int result) {
if (mSettings.closeWindowOnProcessExit()) {
- if (mFinishCallback != null) {
- mFinishCallback.onSessionFinish(this);
- }
finish();
} else if (mProcessExitMessage != null) {
try {
byte[] msg = ("\r\n[" + mProcessExitMessage + "]").getBytes("UTF-8");
- mEmulator.append(msg, 0, msg.length);
- mNotify.onUpdate();
+ getEmulator().append(msg, 0, msg.length);
+ notifyUpdate();
} catch (UnsupportedEncodingException e) {
// Never happens
}
}
}
+ @Override
public void finish() {
Exec.hangupProcessGroup(mProcId);
Exec.close(mTermFd);
- mIsRunning = false;
- mTranscriptScreen.finish();
+ super.finish();
}
}
View
35 src/jackpal/androidterm/Term.java
@@ -54,12 +54,15 @@
import android.widget.TextView;
import android.widget.Toast;
+import jackpal.androidterm.emulatorview.ColorScheme;
+import jackpal.androidterm.emulatorview.EmulatorView;
+import jackpal.androidterm.emulatorview.TermSession;
+import jackpal.androidterm.emulatorview.UpdateCallback;
+
import jackpal.androidterm.compat.ActionBarCompat;
import jackpal.androidterm.compat.ActivityCompat;
import jackpal.androidterm.compat.AndroidCompat;
import jackpal.androidterm.compat.MenuItemCompat;
-import jackpal.androidterm.model.UpdateCallback;
-import jackpal.androidterm.session.TermSession;
import jackpal.androidterm.util.SessionList;
import jackpal.androidterm.util.TermSettings;
@@ -329,15 +332,25 @@ private void restart() {
finish();
}
+ protected static TermSession createTermSession(Context context, TermSettings settings, String initialCommand) {
+ ShellTermSession session = new ShellTermSession(settings, initialCommand);
+ // XXX We should really be able to fetch this from within TermSession
+ session.setProcessExitMessage(context.getString(R.string.process_exit_message));
+
+ return session;
+ }
+
private TermSession createTermSession() {
- String initialCommand = mSettings.getInitialCommand();
- return new TermSession(mSettings, mTermService, initialCommand);
+ TermSettings settings = mSettings;
+ TermSession session = createTermSession(this, settings, settings.getInitialCommand());
+ session.setFinishCallback(mTermService);
+ return session;
}
- private EmulatorView createEmulatorView(TermSession session) {
+ private TermView createEmulatorView(TermSession session) {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
- EmulatorView emulatorView = new EmulatorView(this, session, metrics);
+ TermView emulatorView = new TermView(this, session, metrics);
emulatorView.setExtGestureListener(new EmulatorViewGestureListener(emulatorView));
emulatorView.setOnKeyListener(mBackKeyListener);
@@ -357,17 +370,21 @@ private EmulatorView getCurrentEmulatorView() {
private void updatePrefs() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ ColorScheme colorScheme = new ColorScheme(mSettings.getColorScheme());
mViewFlipper.updatePrefs(mSettings);
+
for (View v : mViewFlipper) {
((EmulatorView) v).setDensity(metrics);
- ((EmulatorView) v).updatePrefs(mSettings);
+ ((TermView) v).updatePrefs(mSettings);
}
+
if (mTermSessions != null) {
for (TermSession session : mTermSessions) {
- session.updatePrefs(mSettings);
+ ((ShellTermSession) session).updatePrefs(mSettings);
}
}
+
{
Window win = getWindow();
WindowManager.LayoutParams params = win.getAttributes();
@@ -527,7 +544,7 @@ private void doCreateNewWindow() {
TermSession session = createTermSession();
mTermSessions.add(session);
- EmulatorView view = createEmulatorView(session);
+ TermView view = createEmulatorView(session);
view.updatePrefs(mSettings);
mViewFlipper.addView(view);
View
17 src/jackpal/androidterm/TermDebug.java
@@ -27,23 +27,6 @@
public static final boolean DEBUG = false;
/**
- * Set to true to log IME calls.
- */
- public static final boolean LOG_IME = DEBUG && false;
-
- /**
- * Set to true to log each character received from the remote process to the
- * android log, which makes it easier to debug some kinds of problems with
- * emulating escape sequences and control codes.
- */
- public static final boolean LOG_CHARACTERS_FLAG = DEBUG && false;
-
- /**
- * Set to true to log unknown escape sequences.
- */
- public static final boolean LOG_UNKNOWN_ESCAPE_SEQUENCES = DEBUG && false;
-
- /**
* The tag we use when logging, so that our messages can be distinguished
* from other messages in the log. Public because it's used by several
* classes.
View
3  src/jackpal/androidterm/TermService.java
@@ -24,8 +24,9 @@
import android.app.Notification;
import android.app.PendingIntent;
+import jackpal.androidterm.emulatorview.TermSession;
+
import jackpal.androidterm.compat.ServiceForegroundCompat;
-import jackpal.androidterm.session.TermSession;
import jackpal.androidterm.util.SessionList;
public class TermService extends Service implements TermSession.FinishCallback
View
50 src/jackpal/androidterm/TermView.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 Steven Luo
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jackpal.androidterm;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+
+import jackpal.androidterm.emulatorview.ColorScheme;
+import jackpal.androidterm.emulatorview.EmulatorView;
+import jackpal.androidterm.emulatorview.TermSession;
+
+import jackpal.androidterm.util.TermSettings;
+
+public class TermView extends EmulatorView {
+ public TermView(Context context, TermSession session, DisplayMetrics metrics) {
+ super(context, session, metrics);
+ }
+
+ public void updatePrefs(TermSettings settings, ColorScheme scheme) {
+ if (scheme == null) {
+ scheme = new ColorScheme(settings.getColorScheme());
+ }
+
+ setTextSize(settings.getFontSize());
+ setCursorStyle(settings.getCursorStyle(), settings.getCursorBlink());
+ setUseCookedIME(settings.useCookedIME());
+ setColorScheme(scheme);
+ setBackKeyCharacter(settings.getBackKeyCharacter());
+ setControlKeyCode(settings.getControlKeyCode());
+ setFnKeyCode(settings.getFnKeyCode());
+ }
+
+ public void updatePrefs(TermSettings settings) {
+ updatePrefs(settings, null);
+ }
+}
View
4 src/jackpal/androidterm/TermViewFlipper.java
@@ -29,8 +29,10 @@
import android.widget.Toast;
import android.widget.ViewFlipper;
+import jackpal.androidterm.emulatorview.EmulatorView;
+import jackpal.androidterm.emulatorview.UpdateCallback;
+
import jackpal.androidterm.compat.AndroidCompat;
-import jackpal.androidterm.model.UpdateCallback;
import jackpal.androidterm.util.TermSettings;
public class TermViewFlipper extends ViewFlipper implements Iterable<View> {
View
5 src/jackpal/androidterm/WindowListAdapter.java
@@ -22,8 +22,9 @@
import android.widget.BaseAdapter;
import android.widget.TextView;
-import jackpal.androidterm.model.UpdateCallback;
-import jackpal.androidterm.session.TermSession;
+import jackpal.androidterm.emulatorview.TermSession;
+import jackpal.androidterm.emulatorview.UpdateCallback;
+
import jackpal.androidterm.util.SessionList;
public class WindowListAdapter extends BaseAdapter implements UpdateCallback {
View
4 src/jackpal/androidterm/util/SessionList.java
@@ -20,8 +20,8 @@
import java.util.LinkedList;
import java.util.Collection;
-import jackpal.androidterm.model.UpdateCallback;
-import jackpal.androidterm.session.TermSession;
+import jackpal.androidterm.emulatorview.TermSession;
+import jackpal.androidterm.emulatorview.UpdateCallback;
/**
* An ArrayList of TermSessions which allows users to register callbacks in
Please sign in to comment.
Something went wrong with that request. Please try again.