summaryrefslogtreecommitdiff
path: root/src/com/android/terminal/TerminalView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/terminal/TerminalView.java')
-rw-r--r--src/com/android/terminal/TerminalView.java354
1 files changed, 192 insertions, 162 deletions
diff --git a/src/com/android/terminal/TerminalView.java b/src/com/android/terminal/TerminalView.java
index 4764183..72d1191 100644
--- a/src/com/android/terminal/TerminalView.java
+++ b/src/com/android/terminal/TerminalView.java
@@ -16,20 +16,23 @@
package com.android.terminal;
+import static com.android.terminal.Terminal.TAG;
+
import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
-import android.graphics.Rect;
import android.graphics.Typeface;
-import android.os.SystemClock;
+import android.os.Parcelable;
+import android.util.AttributeSet;
import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
-import android.view.KeyEvent;
-import android.view.View;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
import com.android.terminal.Terminal.CellRun;
import com.android.terminal.Terminal.TerminalClient;
@@ -37,219 +40,246 @@ import com.android.terminal.Terminal.TerminalClient;
/**
* Rendered contents of a {@link Terminal} session.
*/
-public class TerminalView extends View {
- private static final String TAG = "Terminal";
+public class TerminalView extends ListView {
private static final boolean LOGD = true;
- private static final int MAX_RUN_LENGTH = 128;
+ private static final boolean SCROLL_ON_DAMAGE = false;
+ private static final boolean SCROLL_ON_INPUT = true;
- private final Context mContext;
+ private Terminal mTerm;
- private final Paint mBgPaint = new Paint();
- private final Paint mTextPaint = new Paint();
+ private boolean mScrolled;
- /** Run of cells used when drawing */
- private final CellRun mRun;
- /** Screen coordinates to draw chars into */
- private final float[] mPos;
+ private int mRows;
+ private int mCols;
+ private int mScrollRows;
- private Terminal mTerm;
+ private final TerminalMetrics mMetrics = new TerminalMetrics();
+ private final TerminalKeys mTermKeys = new TerminalKeys();
- private TerminalKeys mTermKeys;
+ /**
+ * Metrics shared between all {@link TerminalLineView} children. Locking
+ * provided by main thread.
+ */
+ static class TerminalMetrics {
+ private static final int MAX_RUN_LENGTH = 128;
- private int mCharTop;
- private int mCharWidth;
- private int mCharHeight;
+ final Paint bgPaint = new Paint();
+ final Paint textPaint = new Paint();
- // TODO: for atomicity we might need to snapshot runs when processing
- // callbacks driven by vterm thread
+ /** Run of cells used when drawing */
+ final CellRun run;
+ /** Screen coordinates to draw chars into */
+ final float[] pos;
- private TerminalClient mClient = new TerminalClient() {
- @Override
- public void damage(int startRow, int endRow, int startCol, int endCol) {
- if (LOGD) Log.d(TAG, "damage(" + startRow + ", " + endRow + ", " + startCol + ", " + endCol + ")");
-
- // Invalidate region on screen
- final int top = startRow * mCharHeight;
- final int bottom = (endRow + 1) * mCharHeight;
- final int left = startCol * mCharWidth;
- final int right = (endCol + 1) * mCharWidth;
- postInvalidate(left, top, right, bottom);
+ int charTop;
+ int charWidth;
+ int charHeight;
+
+ public TerminalMetrics() {
+ run = new Terminal.CellRun();
+ run.data = new char[MAX_RUN_LENGTH];
+
+ // Positions of each possible cell
+ // TODO: make sure this works with surrogate pairs
+ pos = new float[MAX_RUN_LENGTH * 2];
+ setTextSize(20);
}
- @Override
- public void moveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol,
- int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol) {
- // Treat as normal damage and perform full redraw
- final int startRow = Math.min(destStartRow, srcStartRow);
- final int endRow = Math.max(destEndRow, srcEndRow);
- final int startCol = Math.min(destStartCol, srcStartCol);
- final int endCol = Math.max(destEndCol, srcEndCol);
- damage(startRow, endRow, startCol, endCol);
+ public void setTextSize(float textSize) {
+ textPaint.setTypeface(Typeface.MONOSPACE);
+ textPaint.setAntiAlias(true);
+ textPaint.setTextSize(textSize);
+
+ // Read metrics to get exact pixel dimensions
+ final FontMetrics fm = textPaint.getFontMetrics();
+ charTop = (int) Math.ceil(fm.top);
+
+ final float[] widths = new float[1];
+ textPaint.getTextWidths("X", widths);
+ charWidth = (int) Math.ceil(widths[0]);
+ charHeight = (int) Math.ceil(fm.descent - fm.top);
+
+ // Update drawing positions
+ for (int i = 0; i < MAX_RUN_LENGTH; i++) {
+ pos[i * 2] = i * charWidth;
+ pos[(i * 2) + 1] = -charTop;
+ }
}
+ }
+ private final Runnable mDamageRunnable = new Runnable() {
@Override
- public void bell() {
- Log.i(TAG, "DING!");
+ public void run() {
+ invalidateViews();
+ if (SCROLL_ON_DAMAGE) {
+ scrollToBottom(true);
+ }
}
};
public TerminalView(Context context) {
- super(context);
- mContext = context;
+ this(context, null);
+ }
- mRun = new Terminal.CellRun();
- mRun.data = new char[MAX_RUN_LENGTH];
+ public TerminalView(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.listViewStyle);
+ }
- // Positions of each possible cell
- // TODO: make sure this works with surrogate pairs
- mPos = new float[MAX_RUN_LENGTH * 2];
+ public TerminalView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
- setBackgroundColor(Color.BLACK);
- setTextSize(20);
+ setBackground(null);
+ setDivider(null);
- // TODO: remove this test code that triggers invalidates
- setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- v.invalidate();
- v.requestFocus();
- }
- });
-
- // Set view properties
setFocusable(true);
setFocusableInTouchMode(true);
- setScrollContainer(true);
- mTermKeys = new TerminalKeys();
- setOnKeyListener(mTermKeys);
+ setAdapter(mAdapter);
+ setOnKeyListener(mKeyListener);
}
- public void setTerminal(Terminal term) {
- final Terminal orig = mTerm;
- if (orig != null) {
- orig.setClient(null);
- }
- mTerm = term;
- if (term != null) {
- term.setClient(mClient);
- mTermKeys.setTerminal(term);
+ private final BaseAdapter mAdapter = new BaseAdapter() {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final TerminalLineView view;
+ if (convertView != null) {
+ view = (TerminalLineView) convertView;
+ } else {
+ view = new TerminalLineView(parent.getContext(), mTerm, mMetrics);
+ }
+
+ view.pos = position;
+ view.row = posToRow(position);
+ view.cols = mCols;
+ return view;
}
- updateTerminalSize();
- }
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mTerm != null) {
- mTerm.setClient(mClient);
+ @Override
+ public long getItemId(int position) {
+ return position;
}
- }
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mTerm != null) {
- mTerm.setClient(null);
+ @Override
+ public Object getItem(int position) {
+ return null;
}
- }
- public void setTextSize(float textSize) {
- mTextPaint.setTypeface(Typeface.MONOSPACE);
- mTextPaint.setAntiAlias(true);
- mTextPaint.setTextSize(textSize);
-
- // Read metrics to get exact pixel dimensions
- final FontMetrics fm = mTextPaint.getFontMetrics();
- mCharTop = (int) Math.ceil(fm.top);
-
- final float[] widths = new float[1];
- mTextPaint.getTextWidths("X", widths);
- mCharWidth = (int) Math.ceil(widths[0]);
- mCharHeight = (int) Math.ceil(fm.descent - fm.top);
-
- // Update drawing positions
- for (int i = 0; i < MAX_RUN_LENGTH; i++) {
- mPos[i * 2] = i * mCharWidth;
- mPos[(i * 2) + 1] = -mCharTop;
+ @Override
+ public int getCount() {
+ if (mTerm != null) {
+ return mRows + mScrollRows;
+ } else {
+ return 0;
+ }
}
+ };
- updateTerminalSize();
- }
+ private TerminalClient mClient = new TerminalClient() {
+ @Override
+ public void onDamage(final int startRow, final int endRow, int startCol, int endCol) {
+ post(mDamageRunnable);
+ }
- /**
- * Determine terminal dimensions based on current dimensions and font size,
- * and request that {@link Terminal} change to that size.
- */
- public void updateTerminalSize() {
- if (getWidth() > 0 && getHeight() > 0 && mTerm != null) {
- final int rows = getHeight() / mCharHeight;
- final int cols = getWidth() / mCharWidth;
- mTerm.resize(rows, cols);
+ @Override
+ public void onMoveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol,
+ int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol) {
+ post(mDamageRunnable);
}
- }
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (changed) {
- updateTerminalSize();
+ @Override
+ public void onBell() {
+ Log.i(TAG, "DING!");
}
+ };
+
+ private int rowToPos(int row) {
+ return row + mScrollRows;
}
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
+ private int posToRow(int pos) {
+ return pos - mScrollRows;
+ }
- if (mTerm == null) {
- Log.w(TAG, "onDraw() without a terminal");
- canvas.drawColor(Color.MAGENTA);
- return;
+ private View.OnKeyListener mKeyListener = new OnKeyListener() {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ final boolean res = mTermKeys.onKey(v, keyCode, event);
+ if (res && SCROLL_ON_INPUT) {
+ scrollToBottom(true);
+ }
+ return res;
}
+ };
- final long start = SystemClock.elapsedRealtime();
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ super.onRestoreInstanceState(state);
+ mScrolled = true;
+ }
- // Only draw dirty region of console
- final Rect dirty = canvas.getClipBounds();
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (!mScrolled) {
+ scrollToBottom(false);
+ }
+ }
- final int rows = mTerm.getRows();
- final int cols = mTerm.getCols();
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
- final int startRow = dirty.top / mCharHeight;
- final int endRow = Math.min(dirty.bottom / mCharHeight, rows - 1);
- final int startCol = dirty.left / mCharWidth;
- final int endCol = Math.min(dirty.right / mCharWidth, cols - 1);
+ final int rows = h / mMetrics.charHeight;
+ final int cols = w / mMetrics.charWidth;
+ final int scrollRows = mScrollRows;
- final CellRun run = mRun;
- final float[] pos = mPos;
+ final boolean sizeChanged = (rows != mRows || cols != mCols || scrollRows != mScrollRows);
+ if (mTerm != null && sizeChanged) {
+ mTerm.resize(rows, cols, scrollRows);
- for (int row = startRow; row <= endRow; row++) {
- for (int col = startCol; col <= endCol;) {
- mTerm.getCellRun(row, col, run);
+ mRows = rows;
+ mCols = cols;
+ mScrollRows = scrollRows;
- mBgPaint.setColor(run.bg);
- mTextPaint.setColor(run.fg);
+ mAdapter.notifyDataSetChanged();
+ }
+ }
- final int y = row * mCharHeight;
- final int x = col * mCharWidth;
- final int xEnd = x + (run.colSize * mCharWidth);
+ public void scrollToBottom(boolean animate) {
+ final int dur = animate ? 250 : 0;
+ smoothScrollToPositionFromTop(getCount(), 0, dur);
+ mScrolled = true;
+ }
- canvas.save(Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
- canvas.translate(x, y);
- canvas.clipRect(0, 0, run.colSize * mCharWidth, mCharHeight);
+ public void setTerminal(Terminal term) {
+ final Terminal orig = mTerm;
+ if (orig != null) {
+ orig.setClient(null);
+ }
+ mTerm = term;
+ mScrolled = false;
+ if (term != null) {
+ term.setClient(mClient);
+ mTermKeys.setTerminal(term);
- canvas.drawPaint(mBgPaint);
- canvas.drawPosText(run.data, 0, run.dataSize, pos, mTextPaint);
+ // Populate any current settings
+ mRows = mTerm.getRows();
+ mCols = mTerm.getCols();
+ mScrollRows = mTerm.getScrollRows();
+ mAdapter.notifyDataSetChanged();
+ }
+ }
- canvas.restore();
+ public Terminal getTerminal() {
+ return mTerm;
+ }
- col += run.colSize;
- }
- }
+ public void setTextSize(float textSize) {
+ mMetrics.setTextSize(textSize);
- final long delta = SystemClock.elapsedRealtime() - start;
- if (LOGD) Log.d(TAG, "onDraw() took " + delta + "ms");
+ // Layout will kick off terminal resize when needed
+ requestLayout();
}
@Override