TerminalView.java revision 6a142b6d4831c3841b6be1705fc97c9b75a7c9d1
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.terminal; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.graphics.Color; 22import android.graphics.Paint; 23import android.graphics.Paint.FontMetrics; 24import android.graphics.Rect; 25import android.graphics.Typeface; 26import android.os.SystemClock; 27import android.util.Log; 28import android.view.View; 29 30import com.android.terminal.Terminal.CellRun; 31import com.android.terminal.Terminal.TerminalClient; 32 33/** 34 * Rendered contents of a {@link Terminal} session. 35 */ 36public class TerminalView extends View { 37 private static final String TAG = "Terminal"; 38 39 private static final int MAX_RUN_LENGTH = 128; 40 41 private final Context mContext; 42 private final Terminal mTerm; 43 44 private final Paint mBgPaint = new Paint(); 45 private final Paint mTextPaint = new Paint(); 46 47 /** Run of cells used when drawing */ 48 private final CellRun mRun; 49 /** Screen coordinates to draw chars into */ 50 private final float[] mPos; 51 52 private int mCharTop; 53 private int mCharWidth; 54 private int mCharHeight; 55 56 // TODO: for atomicity we might need to snapshot runs when processing 57 // callbacks driven by vterm thread 58 59 private TerminalClient mClient = new TerminalClient() { 60 @Override 61 public void damage(int startRow, int endRow, int startCol, int endCol) { 62 Log.d(TAG, "damage(" + startRow + ", " + endRow + ", " + startCol + ", " + endCol + ")"); 63 64 // Invalidate region on screen 65 final int top = startRow * mCharHeight; 66 final int bottom = (endRow + 1) * mCharHeight; 67 final int left = startCol * mCharWidth; 68 final int right = (endCol + 1) * mCharWidth; 69 postInvalidate(left, top, right, bottom); 70 } 71 72 @Override 73 public void moveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol, 74 int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol) { 75 // Treat as normal damage and perform full redraw 76 final int startRow = Math.min(destStartRow, srcStartRow); 77 final int endRow = Math.max(destEndRow, srcEndRow); 78 final int startCol = Math.min(destStartCol, srcStartCol); 79 final int endCol = Math.max(destEndCol, srcEndCol); 80 damage(startRow, endRow, startCol, endCol); 81 } 82 83 @Override 84 public void bell() { 85 Log.i(TAG, "DING!"); 86 } 87 }; 88 89 public TerminalView(Context context, Terminal term) { 90 super(context); 91 mContext = context; 92 mTerm = term; 93 94 mRun = new Terminal.CellRun(); 95 mRun.data = new char[MAX_RUN_LENGTH]; 96 97 // Positions of each possible cell 98 // TODO: make sure this works with surrogate pairs 99 mPos = new float[MAX_RUN_LENGTH * 2]; 100 101 setBackgroundColor(Color.BLACK); 102 setTextSize(20); 103 104 // TODO: remove this test code that triggers invalidates 105 setOnClickListener(new OnClickListener() { 106 @Override 107 public void onClick(View v) { 108 v.invalidate(); 109 } 110 }); 111 } 112 113 @Override 114 protected void onAttachedToWindow() { 115 super.onAttachedToWindow(); 116 mTerm.setClient(mClient); 117 } 118 119 @Override 120 protected void onDetachedFromWindow() { 121 super.onDetachedFromWindow(); 122 mTerm.setClient(null); 123 } 124 125 public void setTextSize(float textSize) { 126 mTextPaint.setTypeface(Typeface.MONOSPACE); 127 mTextPaint.setTextSize(textSize); 128 129 // Read metrics to get exact pixel dimensions 130 final FontMetrics fm = mTextPaint.getFontMetrics(); 131 mCharTop = (int) Math.ceil(fm.top); 132 133 final float[] widths = new float[1]; 134 mTextPaint.getTextWidths("X", widths); 135 mCharWidth = (int) Math.ceil(widths[0]); 136 mCharHeight = (int) Math.ceil(fm.descent - fm.top); 137 138 // Update drawing positions 139 for (int i = 0; i < MAX_RUN_LENGTH; i++) { 140 mPos[i * 2] = i * mCharWidth; 141 mPos[(i * 2) + 1] = -mCharTop; 142 } 143 144 updateTerminalSize(); 145 } 146 147 /** 148 * Determine terminal dimensions based on current dimensions and font size, 149 * and request that {@link Terminal} change to that size. 150 */ 151 public void updateTerminalSize() { 152 if (getWidth() > 0 && getHeight() > 0) { 153 final int rows = getHeight() / mCharHeight; 154 final int cols = getWidth() / mCharWidth; 155 mTerm.resize(rows, cols); 156 mTerm.flushDamage(); 157 } 158 } 159 160 @Override 161 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 162 super.onLayout(changed, left, top, right, bottom); 163 if (changed) { 164 updateTerminalSize(); 165 } 166 } 167 168 @Override 169 protected void onDraw(Canvas canvas) { 170 super.onDraw(canvas); 171 172 final long start = SystemClock.elapsedRealtime(); 173 174 // Only draw dirty region of console 175 final Rect dirty = canvas.getClipBounds(); 176 177 final int rows = mTerm.getRows(); 178 final int cols = mTerm.getCols(); 179 180 final int startRow = dirty.top / mCharHeight; 181 final int endRow = Math.min(dirty.bottom / mCharHeight, rows - 1); 182 final int startCol = dirty.left / mCharWidth; 183 final int endCol = Math.min(dirty.right / mCharWidth, cols - 1); 184 185 final CellRun run = mRun; 186 final float[] pos = mPos; 187 188 for (int row = startRow; row <= endRow; row++) { 189 for (int col = startCol; col <= endCol;) { 190 mTerm.getCellRun(row, col, run); 191 192 mBgPaint.setColor(run.bgColor); 193 mTextPaint.setColor(run.fgColor); 194 195 final int y = row * mCharHeight; 196 final int x = col * mCharWidth; 197 final int xEnd = x + (run.colSize * mCharWidth); 198 199 canvas.save(Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG); 200 canvas.translate(x, y); 201 canvas.clipRect(0, 0, run.colSize * mCharWidth, mCharHeight); 202 203 canvas.drawPaint(mBgPaint); 204 canvas.drawPosText(run.data, 0, run.dataSize, pos, mTextPaint); 205 206 canvas.restore(); 207 208 col += run.colSize; 209 } 210 } 211 212 final long delta = SystemClock.elapsedRealtime() - start; 213 Log.d(TAG, "onDraw() took " + delta + "ms"); 214 } 215} 216