diff options
Diffstat (limited to 'src/com/android/terminal')
-rw-r--r-- | src/com/android/terminal/Terminal.java | 56 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalActivity.java | 48 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalCallbacks.java | 8 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalKeys.java | 2 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalLineView.java | 85 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalService.java | 30 | ||||
-rw-r--r-- | src/com/android/terminal/TerminalView.java | 354 |
7 files changed, 361 insertions, 222 deletions
diff --git a/src/com/android/terminal/Terminal.java b/src/com/android/terminal/Terminal.java index 35c2cee..cdf3f60 100644 --- a/src/com/android/terminal/Terminal.java +++ b/src/com/android/terminal/Terminal.java @@ -22,7 +22,9 @@ import android.graphics.Color; * Single terminal session backed by a pseudo terminal on the local device. */ public class Terminal { - private static final String TAG = "Terminal"; + public static final String TAG = "Terminal"; + + public final int key; private static int sNumber = 0; @@ -46,15 +48,17 @@ public class Terminal { boolean strike; int font; - int fg = Color.RED; - int bg = Color.BLUE; + int fg = Color.CYAN; + int bg = Color.DKGRAY; } + // NOTE: clients must not call back into terminal while handling a callback, + // since native mutex isn't reentrant. public interface TerminalClient { - public void damage(int startRow, int endRow, int startCol, int endCol); - public void moveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol, + public void onDamage(int startRow, int endRow, int startCol, int endCol); + public void onMoveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol, int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol); - public void bell(); + public void onBell(); } private final int mNativePtr; @@ -68,7 +72,7 @@ public class Terminal { @Override public int damage(int startRow, int endRow, int startCol, int endCol) { if (mClient != null) { - mClient.damage(startRow, endRow, startCol, endCol); + mClient.onDamage(startRow, endRow, startCol, endCol); } return 1; } @@ -77,7 +81,7 @@ public class Terminal { public int moveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol, int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol) { if (mClient != null) { - mClient.moveRect(destStartRow, destEndRow, destStartCol, destEndCol, srcStartRow, + mClient.onMoveRect(destStartRow, destEndRow, destStartCol, destEndCol, srcStartRow, srcEndRow, srcStartCol, srcEndCol); } return 1; @@ -86,7 +90,7 @@ public class Terminal { @Override public int bell() { if (mClient != null) { - mClient.bell(); + mClient.onBell(); } return 1; } @@ -94,8 +98,9 @@ public class Terminal { public Terminal() { mNativePtr = nativeInit(mCallbacks, 25, 80); - mTitle = TAG + " " + sNumber++; - mThread = new Thread(TAG) { + key = sNumber++; + mTitle = TAG + " " + key; + mThread = new Thread(mTitle) { @Override public void run() { nativeRun(mNativePtr); @@ -110,9 +115,9 @@ public class Terminal { mThread.start(); } - public void stop() { - if (nativeStop(mNativePtr) != 0) { - throw new IllegalStateException("stop failed"); + public void destroy() { + if (nativeDestroy(mNativePtr) != 0) { + throw new IllegalStateException("destroy failed"); } } @@ -120,14 +125,8 @@ public class Terminal { mClient = client; } - public void flushDamage() { - if (nativeFlushDamage(mNativePtr) != 0) { - throw new IllegalStateException("flushDamage failed"); - } - } - - public void resize(int rows, int cols) { - if (nativeResize(mNativePtr, rows, cols) != 0) { + public void resize(int rows, int cols, int scrollRows) { + if (nativeResize(mNativePtr, rows, cols, scrollRows) != 0) { throw new IllegalStateException("resize failed"); } } @@ -140,6 +139,10 @@ public class Terminal { return nativeGetCols(mNativePtr); } + public int getScrollRows() { + return nativeGetScrollRows(mNativePtr); + } + public void getCellRun(int row, int col, CellRun run) { if (nativeGetCellRun(mNativePtr, row, col, run) != 0) { throw new IllegalStateException("getCell failed"); @@ -159,16 +162,15 @@ public class Terminal { return nativeDispatchCharacter(mNativePtr, modifiers, character); } - private static native int nativeInit(TerminalCallbacks callbacks, int rows, int cols); - private static native int nativeRun(int ptr); - private static native int nativeStop(int ptr); + private static native int nativeDestroy(int ptr); - private static native int nativeFlushDamage(int ptr); - private static native int nativeResize(int ptr, int rows, int cols); + private static native int nativeRun(int ptr); + private static native int nativeResize(int ptr, int rows, int cols, int scrollRows); private static native int nativeGetCellRun(int ptr, int row, int col, CellRun run); private static native int nativeGetRows(int ptr); private static native int nativeGetCols(int ptr); + private static native int nativeGetScrollRows(int ptr); private static native boolean nativeDispatchKey(int ptr, int modifiers, int key); private static native boolean nativeDispatchCharacter(int ptr, int modifiers, int character); diff --git a/src/com/android/terminal/TerminalActivity.java b/src/com/android/terminal/TerminalActivity.java index 99d2be6..dd94d74 100644 --- a/src/com/android/terminal/TerminalActivity.java +++ b/src/com/android/terminal/TerminalActivity.java @@ -16,6 +16,8 @@ package com.android.terminal; +import static com.android.terminal.Terminal.TAG; + import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -23,10 +25,12 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; +import android.os.Parcelable; import android.support.v4.view.PagerAdapter; import android.support.v4.view.PagerTitleStrip; import android.support.v4.view.ViewPager; import android.util.Log; +import android.util.SparseArray; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -37,7 +41,6 @@ import android.view.ViewGroup; * {@link TerminalService}. */ public class TerminalActivity extends Activity { - private static final String TAG = "Terminal"; private TerminalService mService; @@ -59,6 +62,7 @@ public class TerminalActivity extends Activity { // Bind UI to known terminals mTermAdapter.notifyDataSetChanged(); + invalidateOptionsMenu(); } @Override @@ -69,6 +73,9 @@ public class TerminalActivity extends Activity { }; private final PagerAdapter mTermAdapter = new PagerAdapter() { + private SparseArray<SparseArray<Parcelable>> + mSavedState = new SparseArray<SparseArray<Parcelable>>(); + @Override public int getCount() { if (mService != null) { @@ -80,9 +87,17 @@ public class TerminalActivity extends Activity { @Override public Object instantiateItem(ViewGroup container, int position) { - final Terminal term = mService.getTerminals().get(position); final TerminalView view = new TerminalView(container.getContext()); + view.setId(android.R.id.list); + + final Terminal term = mService.getTerminals().valueAt(position); view.setTerminal(term); + + final SparseArray<Parcelable> state = mSavedState.get(term.key); + if (state != null) { + view.restoreHierarchyState(state); + } + container.addView(view); return view; } @@ -90,13 +105,24 @@ public class TerminalActivity extends Activity { @Override public void destroyItem(ViewGroup container, int position, Object object) { final TerminalView view = (TerminalView) object; + + final int key = view.getTerminal().key; + SparseArray<Parcelable> state = mSavedState.get(key); + if (state == null) { + state = new SparseArray<Parcelable>(); + mSavedState.put(key, state); + } + view.saveHierarchyState(state); + view.setTerminal(null); container.removeView(view); } @Override public int getItemPosition(Object object) { - final int index = mService.getTerminals().indexOf(object); + final TerminalView view = (TerminalView) object; + final int key = view.getTerminal().key; + final int index = mService.getTerminals().indexOfKey(key); if (index == -1) { return POSITION_NONE; } else { @@ -111,7 +137,7 @@ public class TerminalActivity extends Activity { @Override public CharSequence getPageTitle(int position) { - return mService.getTerminals().get(position).getTitle(); + return mService.getTerminals().valueAt(position).getTitle(); } }; @@ -147,21 +173,29 @@ public class TerminalActivity extends Activity { } @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.menu_close_tab).setEnabled(mTermAdapter.getCount() > 0); + return true; + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_new_tab: { mService.createTerminal(); mTermAdapter.notifyDataSetChanged(); + invalidateOptionsMenu(); final int index = mService.getTerminals().size() - 1; mPager.setCurrentItem(index, true); return true; } case R.id.menu_close_tab: { final int index = mPager.getCurrentItem(); - final Terminal term = mService.getTerminals().get(index); - mService.destroyTerminal(term); - // TODO: ask adamp about buggy zero item behavior + final int key = mService.getTerminals().keyAt(index); + mService.destroyTerminal(key); mTermAdapter.notifyDataSetChanged(); + invalidateOptionsMenu(); return true; } } diff --git a/src/com/android/terminal/TerminalCallbacks.java b/src/com/android/terminal/TerminalCallbacks.java index b449075..fb5a1ca 100644 --- a/src/com/android/terminal/TerminalCallbacks.java +++ b/src/com/android/terminal/TerminalCallbacks.java @@ -21,10 +21,6 @@ public abstract class TerminalCallbacks { return 1; } - public int prescroll(int startRow, int endRow, int startCol, int endCol) { - return 1; - } - public int moveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol, int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol) { return 1; @@ -53,8 +49,4 @@ public abstract class TerminalCallbacks { public int bell() { return 1; } - - public int resize(int rows, int cols) { - return 1; - } } diff --git a/src/com/android/terminal/TerminalKeys.java b/src/com/android/terminal/TerminalKeys.java index 17cab5e..67dc231 100644 --- a/src/com/android/terminal/TerminalKeys.java +++ b/src/com/android/terminal/TerminalKeys.java @@ -21,7 +21,7 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; -public class TerminalKeys implements View.OnKeyListener { +public class TerminalKeys { private static final String TAG = "TerminalKeys"; private static final boolean DEBUG = true; // Taken from vterm_input.h diff --git a/src/com/android/terminal/TerminalLineView.java b/src/com/android/terminal/TerminalLineView.java new file mode 100644 index 0000000..fff1301 --- /dev/null +++ b/src/com/android/terminal/TerminalLineView.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 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 com.android.terminal; + +import static com.android.terminal.Terminal.TAG; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.util.Log; +import android.view.View; + +import com.android.terminal.TerminalView.TerminalMetrics; + +/** + * Rendered contents of a single line of a {@link Terminal} session. + */ +public class TerminalLineView extends View { + public int pos; + public int row; + public int cols; + + private final Terminal mTerm; + private final TerminalMetrics mMetrics; + + public TerminalLineView(Context context, Terminal term, TerminalMetrics metrics) { + super(context); + mTerm = term; + mMetrics = metrics; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), + getDefaultSize(mMetrics.charHeight, heightMeasureSpec)); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mTerm == null) { + Log.w(TAG, "onDraw() without a terminal"); + canvas.drawColor(Color.MAGENTA); + return; + } + + final TerminalMetrics m = mMetrics; + + for (int col = 0; col < cols;) { + mTerm.getCellRun(row, col, m.run); + + m.bgPaint.setColor(m.run.bg); + m.textPaint.setColor(m.run.fg); + + final int x = col * m.charWidth; + final int xEnd = x + (m.run.colSize * m.charWidth); + + canvas.save(); + canvas.translate(x, 0); + canvas.clipRect(0, 0, m.run.colSize * m.charWidth, m.charHeight); + + canvas.drawPaint(m.bgPaint); + canvas.drawPosText(m.run.data, 0, m.run.dataSize, m.pos, m.textPaint); + + canvas.restore(); + + col += m.run.colSize; + } + } +} diff --git a/src/com/android/terminal/TerminalService.java b/src/com/android/terminal/TerminalService.java index ebbdcce..4399390 100644 --- a/src/com/android/terminal/TerminalService.java +++ b/src/com/android/terminal/TerminalService.java @@ -20,19 +20,14 @@ import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import android.util.SparseArray; /** * Background service that keeps {@link Terminal} instances running and warm * when UI isn't present. */ public class TerminalService extends Service { - private static final String TAG = "Terminal"; - - private final ArrayList<Terminal> mTerminals = new ArrayList<Terminal>(); + private final SparseArray<Terminal> mTerminals = new SparseArray<Terminal>(); public class ServiceBinder extends Binder { public TerminalService getService() { @@ -45,28 +40,29 @@ public class TerminalService extends Service { return new ServiceBinder(); } - public List<Terminal> getTerminals() { - return Collections.unmodifiableList(mTerminals); + public SparseArray<Terminal> getTerminals() { + return mTerminals; } - public Terminal createTerminal() { + public int createTerminal() { // If our first terminal, start ourselves as long-lived service - if (mTerminals.isEmpty()) { + if (mTerminals.size() == 0) { startService(new Intent(this, TerminalService.class)); } final Terminal term = new Terminal(); term.start(); - mTerminals.add(term); - return term; + mTerminals.put(term.key, term); + return term.key; } - public void destroyTerminal(Terminal term) { - term.stop(); - mTerminals.remove(term); + public void destroyTerminal(int key) { + final Terminal term = mTerminals.get(key); + term.destroy(); + mTerminals.delete(key); // If our last terminal, tear down long-lived service - if (mTerminals.isEmpty()) { + if (mTerminals.size() == 0) { stopService(new Intent(this, TerminalService.class)); } } 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 |