TerminalView.java revision 9cae0a9616b1b71eac7e762d198fe1da47fea901
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; 239cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkeyimport android.graphics.Typeface; 24410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Paint.FontMetrics; 25410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.graphics.Rect; 26410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.view.View; 27410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 28410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.os.SystemClock; 29410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkeyimport android.util.Log; 30410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 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 39410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private final Context mContext; 40410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private final Terminal mTerm; 41410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 42410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private final Paint mBgPaint = new Paint(); 43410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private final Paint mTextPaint = new Paint(); 44410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 45410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private int mCharTop; 46410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private int mCharWidth; 47410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private int mCharHeight; 48410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 49410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey private TerminalClient mClient = new TerminalClient() { 50410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 51410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void damage(int startRow, int endRow, int startCol, int endCol) { 52410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int top = startRow * mCharHeight; 53410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int bottom = (endRow + 1) * mCharHeight; 54410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int left = startCol * mCharWidth; 55410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int right = (endCol + 1) * mCharWidth; 56410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 57410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey // Invalidate region on screen 58410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey postInvalidate(left, top, right, bottom); 59410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 60410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 61410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 62410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void bell() { 63410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey Log.i(TAG, "DING!"); 64410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 65410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey }; 66410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 67410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public TerminalView(Context context, Terminal term) { 68410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super(context); 69410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mContext = context; 70410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTerm = term; 71410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 72410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey setBackgroundColor(Color.BLACK); 73410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey setTextSize(20); 74410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 75410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey // TODO: remove this test code that triggers invalidates 76410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey setOnClickListener(new OnClickListener() { 77410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 78410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void onClick(View v) { 79410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey v.invalidate(); 80410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 81410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey }); 82410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 83410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 84410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 85410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey protected void onAttachedToWindow() { 86410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super.onAttachedToWindow(); 87410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTerm.setClient(mClient); 88410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 89410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 90410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 91410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey protected void onDetachedFromWindow() { 92410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super.onDetachedFromWindow(); 93410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTerm.setClient(null); 94410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 95410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 96410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void setTextSize(float textSize) { 979cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey mTextPaint.setTypeface(Typeface.MONOSPACE); 98410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTextPaint.setTextSize(textSize); 99410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 100410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey // Read metrics to get exact pixel dimensions 101410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final FontMetrics fm = mTextPaint.getFontMetrics(); 102410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mCharTop = (int) Math.ceil(fm.top); 103410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 104410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final float[] widths = new float[1]; 105410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTextPaint.getTextWidths("X", widths); 106410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mCharWidth = (int) Math.ceil(widths[0]); 107410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mCharHeight = (int) Math.ceil(fm.descent - fm.top); 108410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 109410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey updateTerminalSize(); 110410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 111410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 112410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey /** 113410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * Determine terminal dimensions based on current dimensions and font size, 114410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey * and request that {@link Terminal} change to that size. 115410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey */ 116410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey public void updateTerminalSize() { 117410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey if (getWidth() > 0 && getHeight() > 0) { 118410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int rows = getHeight() / mCharHeight; 119410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int cols = getWidth() / mCharWidth; 120410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey mTerm.resize(rows, cols); 121410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 122410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 123410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 124410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 125410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 126410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super.onLayout(changed, left, top, right, bottom); 127410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey if (changed) { 128410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey updateTerminalSize(); 129410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 130410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 1319cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 1329cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey private static final int MAX_RUN_LENGTH = 128; 133410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 134410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey @Override 135410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey protected void onDraw(Canvas canvas) { 136410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey super.onDraw(canvas); 137410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 138410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final long start = SystemClock.elapsedRealtime(); 139410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 140410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey // Only draw dirty region of console 141410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final Rect dirty = canvas.getClipBounds(); 142410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 1439cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int rows = mTerm.getRows(); 1449cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int cols = mTerm.getCols(); 1459cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 146410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int startRow = dirty.top / mCharHeight; 1479cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int endRow = Math.min(dirty.bottom / mCharHeight, rows - 1); 148410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int startCol = dirty.left / mCharWidth; 1499cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int endCol = Math.min(dirty.right / mCharWidth, cols - 1); 1509cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 1519cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final Terminal.CellRun run = new Terminal.CellRun(); 1529cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey run.data = new char[MAX_RUN_LENGTH]; 153410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 1549cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey // Positions of each possible cell 1559cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey // TODO: make sure this works with surrogate pairs 1569cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final float[] pos = new float[MAX_RUN_LENGTH * 2]; 1579cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey for (int i = 0; i < MAX_RUN_LENGTH; i++) { 1589cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey pos[i * 2] = i * mCharWidth; 1599cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey pos[(i * 2) + 1] = -mCharTop; 1609cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey } 161410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 162410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey for (int row = startRow; row <= endRow; row++) { 163410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey for (int col = startCol; col <= endCol;) { 1649cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey mTerm.getCellRun(row, col, run); 165410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 1669cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey mBgPaint.setColor(run.bgColor); 1679cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey mTextPaint.setColor(run.fgColor); 168410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 169410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int y = row * mCharHeight; 170410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final int x = col * mCharWidth; 1719cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey final int xEnd = x + (run.colSize * mCharWidth); 1729cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 1739cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.save(Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG); 1749cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.translate(x, y); 1759cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.clipRect(0, 0, run.colSize * mCharWidth, mCharHeight); 1769cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey 1779cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.drawPaint(mBgPaint); 1789cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.drawPosText(run.data, 0, run.dataSize, pos, mTextPaint); 179410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 1809cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey canvas.restore(); 181410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 1829cae0a9616b1b71eac7e762d198fe1da47fea901Jeff Sharkey col += run.colSize; 183410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 184410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 185410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey 186410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey final long delta = SystemClock.elapsedRealtime() - start; 187410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey Log.d(TAG, "onDraw() took " + delta + "ms"); 188410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey } 189410e0da343fd581f3112037deb475db9fb0da850Jeff Sharkey} 190