TerminalView.java revision 6a142b6d4831c3841b6be1705fc97c9b75a7c9d1
1410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey/* 2410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project 3410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * 4410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * you may not use this file except in compliance with the License. 6410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * You may obtain a copy of the License at 7410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * 8410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * 10410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * See the License for the specific language governing permissions and 14410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * limitations under the License. 15410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey */ 16410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 17410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeypackage com.android.terminal; 18410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 19410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.content.Context; 20410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Canvas; 21410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Color; 22410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Paint; 23410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Paint.FontMetrics; 24410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Rect; 25479bd643981271fb0edf756ae5915e44a7352c4dJeff Sharkeyimport android.graphics.Typeface; 26410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.os.SystemClock; 27410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.util.Log; 28479bd643981271fb0edf756ae5915e44a7352c4dJeff Sharkeyimport android.view.View; 29410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 30a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkeyimport com.android.terminal.Terminal.CellRun; 31410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport com.android.terminal.Terminal.TerminalClient; 32410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 33410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey/** 34410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Rendered contents of a {@link Terminal} session. 35410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey */ 36410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeypublic class TerminalView extends View { 37410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private static final String TAG = "Terminal"; 38410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 39a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey private static final int MAX_RUN_LENGTH = 128; 40a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey 41410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private final Context mContext; 42410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private final Terminal mTerm; 43410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 44410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private final Paint mBgPaint = new Paint(); 45410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private final Paint mTextPaint = new Paint(); 46410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 47a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey /** Run of cells used when drawing */ 48a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey private final CellRun mRun; 49a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey /** Screen coordinates to draw chars into */ 50a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey private final float[] mPos; 51a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey 52410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private int mCharTop; 53410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private int mCharWidth; 54410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private int mCharHeight; 55410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 56a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey // TODO: for atomicity we might need to snapshot runs when processing 57a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey // callbacks driven by vterm thread 58a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey 59410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private TerminalClient mClient = new TerminalClient() { 60410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 61410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void damage(int startRow, int endRow, int startCol, int endCol) { 626a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey Log.d(TAG, "damage(" + startRow + ", " + endRow + ", " + startCol + ", " + endCol + ")"); 636a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey 646a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey // Invalidate region on screen 65410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int top = startRow * mCharHeight; 66410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int bottom = (endRow + 1) * mCharHeight; 67410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int left = startCol * mCharWidth; 68410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int right = (endCol + 1) * mCharWidth; 69410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey postInvalidate(left, top, right, bottom); 70410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 71410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 72410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 736a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey public void moveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol, 746a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol) { 756a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey // Treat as normal damage and perform full redraw 766a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey final int startRow = Math.min(destStartRow, srcStartRow); 776a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey final int endRow = Math.max(destEndRow, srcEndRow); 786a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey final int startCol = Math.min(destStartCol, srcStartCol); 796a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey final int endCol = Math.max(destEndCol, srcEndCol); 806a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey damage(startRow, endRow, startCol, endCol); 816a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey } 826a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey 836a142b6d4831c3841b6be1705fc97c9b75a7c9d1Jeff Sharkey @Override 84410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void bell() { 85410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey Log.i(TAG, "DING!"); 86410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 87410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey }; 88410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 89410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public TerminalView(Context context, Terminal term) { 90410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super(context); 91410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mContext = context; 92410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTerm = term; 93410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 94a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey mRun = new Terminal.CellRun(); 95a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey mRun.data = new char[MAX_RUN_LENGTH]; 96a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey 97a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey // Positions of each possible cell 98a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey // TODO: make sure this works with surrogate pairs 99a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey mPos = new float[MAX_RUN_LENGTH * 2]; 100a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey 101410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey setBackgroundColor(Color.BLACK); 102410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey setTextSize(20); 103410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 104410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey // TODO: remove this test code that triggers invalidates 105410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey setOnClickListener(new OnClickListener() { 106410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 107410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void onClick(View v) { 108410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey v.invalidate(); 109410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 110410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey }); 111410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 112410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 113410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 114410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey protected void onAttachedToWindow() { 115410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super.onAttachedToWindow(); 116410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTerm.setClient(mClient); 117410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 118410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 119410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 120410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey protected void onDetachedFromWindow() { 121410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super.onDetachedFromWindow(); 122410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTerm.setClient(null); 123410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 124410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 125410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void setTextSize(float textSize) { 1269cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey mTextPaint.setTypeface(Typeface.MONOSPACE); 127410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTextPaint.setTextSize(textSize); 128410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 129410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey // Read metrics to get exact pixel dimensions 130410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final FontMetrics fm = mTextPaint.getFontMetrics(); 131410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mCharTop = (int) Math.ceil(fm.top); 132410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 133410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final float[] widths = new float[1]; 134410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTextPaint.getTextWidths("X", widths); 135410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mCharWidth = (int) Math.ceil(widths[0]); 136410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mCharHeight = (int) Math.ceil(fm.descent - fm.top); 137410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 138a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey // Update drawing positions 139a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey for (int i = 0; i < MAX_RUN_LENGTH; i++) { 140a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey mPos[i * 2] = i * mCharWidth; 141a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey mPos[(i * 2) + 1] = -mCharTop; 142a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey } 143a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey 144410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey updateTerminalSize(); 145410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 146410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 147410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey /** 148410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Determine terminal dimensions based on current dimensions and font size, 149410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * and request that {@link Terminal} change to that size. 150410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey */ 151410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void updateTerminalSize() { 152410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey if (getWidth() > 0 && getHeight() > 0) { 153410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int rows = getHeight() / mCharHeight; 154410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int cols = getWidth() / mCharWidth; 155410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTerm.resize(rows, cols); 156a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey mTerm.flushDamage(); 157410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 158410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 159410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 160410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 161410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 162410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super.onLayout(changed, left, top, right, bottom); 163410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey if (changed) { 164410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey updateTerminalSize(); 165410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 166410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 167410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 168410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 169410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey protected void onDraw(Canvas canvas) { 170410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super.onDraw(canvas); 171410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 172410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final long start = SystemClock.elapsedRealtime(); 173410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 174410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey // Only draw dirty region of console 175410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final Rect dirty = canvas.getClipBounds(); 176410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 1779cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int rows = mTerm.getRows(); 1789cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int cols = mTerm.getCols(); 1799cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 180410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int startRow = dirty.top / mCharHeight; 1819cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int endRow = Math.min(dirty.bottom / mCharHeight, rows - 1); 182410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int startCol = dirty.left / mCharWidth; 1839cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int endCol = Math.min(dirty.right / mCharWidth, cols - 1); 1849cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 185a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey final CellRun run = mRun; 186a76e33884c55bbd5db7e512b7687210cc3f635cfJeff Sharkey final float[] pos = mPos; 187410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 188410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey for (int row = startRow; row <= endRow; row++) { 189410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey for (int col = startCol; col <= endCol;) { 1909cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey mTerm.getCellRun(row, col, run); 191410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 1929cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey mBgPaint.setColor(run.bgColor); 1939cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey mTextPaint.setColor(run.fgColor); 194410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 195410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int y = row * mCharHeight; 196410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int x = col * mCharWidth; 1979cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int xEnd = x + (run.colSize * mCharWidth); 1989cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 1999cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.save(Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG); 2009cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.translate(x, y); 2019cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.clipRect(0, 0, run.colSize * mCharWidth, mCharHeight); 2029cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 2039cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.drawPaint(mBgPaint); 2049cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.drawPosText(run.data, 0, run.dataSize, pos, mTextPaint); 205410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 2069cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.restore(); 207410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 2089cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey col += run.colSize; 209410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 210410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 211410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 212410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final long delta = SystemClock.elapsedRealtime() - start; 213410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey Log.d(TAG, "onDraw() took " + delta + "ms"); 214410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 215410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey} 216