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 
17 package com.android.terminal;
18 
19 import android.graphics.Color;
20 
21 /**
22  * Single terminal session backed by a pseudo terminal on the local device.
23  */
24 public class Terminal {
25     public static final String TAG = "Terminal";
26 
27     public final int key;
28 
29     private static int sNumber = 0;
30 
31     static {
32         System.loadLibrary("jni_terminal");
33     }
34 
35     /**
36      * Represents a run of one or more {@code VTermScreenCell} which all have
37      * the same formatting.
38      */
39     public static class CellRun {
40         char[] data;
41         int dataSize;
42         int colSize;
43 
44         boolean bold;
45         int underline;
46         boolean blink;
47         boolean reverse;
48         boolean strike;
49         int font;
50 
51         int fg = Color.CYAN;
52         int bg = Color.DKGRAY;
53     }
54 
55     // NOTE: clients must not call back into terminal while handling a callback,
56     // since native mutex isn't reentrant.
57     public interface TerminalClient {
onDamage(int startRow, int endRow, int startCol, int endCol)58         public void onDamage(int startRow, int endRow, int startCol, int endCol);
onMoveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol, int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol)59         public void onMoveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol,
60                 int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol);
onMoveCursor(int posRow, int posCol, int oldPosRow, int oldPosCol, int visible)61         public void onMoveCursor(int posRow, int posCol, int oldPosRow, int oldPosCol, int visible);
onBell()62         public void onBell();
63     }
64 
65     private final long mNativePtr;
66     private final Thread mThread;
67 
68     private String mTitle;
69 
70     private TerminalClient mClient;
71 
72     private boolean mCursorVisible;
73     private int mCursorRow;
74     private int mCursorCol;
75 
76     private final TerminalCallbacks mCallbacks = new TerminalCallbacks() {
77         @Override
78         public int damage(int startRow, int endRow, int startCol, int endCol) {
79             if (mClient != null) {
80                 mClient.onDamage(startRow, endRow, startCol, endCol);
81             }
82             return 1;
83         }
84 
85         @Override
86         public int moveRect(int destStartRow, int destEndRow, int destStartCol, int destEndCol,
87                 int srcStartRow, int srcEndRow, int srcStartCol, int srcEndCol) {
88             if (mClient != null) {
89                 mClient.onMoveRect(destStartRow, destEndRow, destStartCol, destEndCol, srcStartRow,
90                         srcEndRow, srcStartCol, srcEndCol);
91             }
92             return 1;
93         }
94 
95         @Override
96         public int moveCursor(int posRow, int posCol, int oldPosRow, int oldPosCol, int visible) {
97             mCursorVisible = (visible != 0);
98             mCursorRow = posRow;
99             mCursorCol = posCol;
100             if (mClient != null) {
101                 mClient.onMoveCursor(posRow, posCol, oldPosRow, oldPosCol, visible);
102             }
103             return 1;
104         }
105 
106         @Override
107         public int bell() {
108             if (mClient != null) {
109                 mClient.onBell();
110             }
111             return 1;
112         }
113     };
114 
Terminal()115     public Terminal() {
116         mNativePtr = nativeInit(mCallbacks);
117         key = sNumber++;
118         mTitle = TAG + " " + key;
119         mThread = new Thread(mTitle) {
120             @Override
121             public void run() {
122                 nativeRun(mNativePtr);
123             }
124         };
125     }
126 
127     /**
128      * Start thread which internally forks and manages the pseudo terminal.
129      */
start()130     public void start() {
131         mThread.start();
132     }
133 
destroy()134     public void destroy() {
135         if (nativeDestroy(mNativePtr) != 0) {
136             throw new IllegalStateException("destroy failed");
137         }
138     }
139 
setClient(TerminalClient client)140     public void setClient(TerminalClient client) {
141         mClient = client;
142     }
143 
resize(int rows, int cols, int scrollRows)144     public void resize(int rows, int cols, int scrollRows) {
145         if (nativeResize(mNativePtr, rows, cols, scrollRows) != 0) {
146             throw new IllegalStateException("resize failed");
147         }
148     }
149 
getRows()150     public int getRows() {
151         return nativeGetRows(mNativePtr);
152     }
153 
getCols()154     public int getCols() {
155         return nativeGetCols(mNativePtr);
156     }
157 
getScrollRows()158     public int getScrollRows() {
159         return nativeGetScrollRows(mNativePtr);
160     }
161 
getCellRun(int row, int col, CellRun run)162     public void getCellRun(int row, int col, CellRun run) {
163         if (nativeGetCellRun(mNativePtr, row, col, run) != 0) {
164             throw new IllegalStateException("getCell failed");
165         }
166     }
167 
getCursorVisible()168     public boolean getCursorVisible() {
169         return mCursorVisible;
170     }
171 
getCursorRow()172     public int getCursorRow() {
173         return mCursorRow;
174     }
175 
getCursorCol()176     public int getCursorCol() {
177         return mCursorCol;
178     }
179 
getTitle()180     public String getTitle() {
181         // TODO: hook up to title passed through termprop
182         return mTitle;
183     }
184 
dispatchKey(int modifiers, int key)185     public boolean dispatchKey(int modifiers, int key) {
186         return nativeDispatchKey(mNativePtr, modifiers, key);
187     }
188 
dispatchCharacter(int modifiers, int character)189     public boolean dispatchCharacter(int modifiers, int character) {
190         return nativeDispatchCharacter(mNativePtr, modifiers, character);
191     }
192 
nativeInit(TerminalCallbacks callbacks)193     private static native long nativeInit(TerminalCallbacks callbacks);
nativeDestroy(long ptr)194     private static native int nativeDestroy(long ptr);
195 
nativeRun(long ptr)196     private static native int nativeRun(long ptr);
nativeResize(long ptr, int rows, int cols, int scrollRows)197     private static native int nativeResize(long ptr, int rows, int cols, int scrollRows);
nativeGetCellRun(long ptr, int row, int col, CellRun run)198     private static native int nativeGetCellRun(long ptr, int row, int col, CellRun run);
nativeGetRows(long ptr)199     private static native int nativeGetRows(long ptr);
nativeGetCols(long ptr)200     private static native int nativeGetCols(long ptr);
nativeGetScrollRows(long ptr)201     private static native int nativeGetScrollRows(long ptr);
202 
nativeDispatchKey(long ptr, int modifiers, int key)203     private static native boolean nativeDispatchKey(long ptr, int modifiers, int key);
nativeDispatchCharacter(long ptr, int modifiers, int character)204     private static native boolean nativeDispatchCharacter(long ptr, int modifiers, int character);
205 }
206