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 #define LOG_TAG "Terminal"
18
19 #include <utils/Log.h>
20 #include <utils/Mutex.h>
21 #include "android_runtime/AndroidRuntime.h"
22
23 #include "forkpty.h"
24 #include "jni.h"
25 #include "JNIHelp.h"
26 #include "ScopedLocalRef.h"
27 #include "ScopedPrimitiveArray.h"
28
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <termios.h>
32 #include <unistd.h>
33 #include <util.h>
34 #include <utmp.h>
35
36 #include <vterm.h>
37
38 #include <string.h>
39
40 #define USE_TEST_SHELL 0
41 #define DEBUG_CALLBACKS 0
42 #define DEBUG_IO 0
43 #define DEBUG_SCROLLBACK 0
44
45 namespace android {
46
47 /*
48 * Callback class reference
49 */
50 static jclass terminalCallbacksClass;
51
52 /*
53 * Callback methods
54 */
55 static jmethodID damageMethod;
56 static jmethodID moveRectMethod;
57 static jmethodID moveCursorMethod;
58 static jmethodID setTermPropBooleanMethod;
59 static jmethodID setTermPropIntMethod;
60 static jmethodID setTermPropStringMethod;
61 static jmethodID setTermPropColorMethod;
62 static jmethodID bellMethod;
63
64 /*
65 * CellRun class
66 */
67 static jclass cellRunClass;
68 static jfieldID cellRunDataField;
69 static jfieldID cellRunDataSizeField;
70 static jfieldID cellRunColSizeField;
71 static jfieldID cellRunFgField;
72 static jfieldID cellRunBgField;
73
74 typedef short unsigned int dimen_t;
75
76 class ScrollbackLine {
77 public:
ScrollbackLine(dimen_t _cols)78 inline ScrollbackLine(dimen_t _cols) : cols(_cols) {
79 mCells = new VTermScreenCell[cols];
80 };
~ScrollbackLine()81 inline ~ScrollbackLine() {
82 delete mCells;
83 }
84
copyFrom(dimen_t cols,const VTermScreenCell * cells)85 inline dimen_t copyFrom(dimen_t cols, const VTermScreenCell* cells) {
86 dimen_t n = this->cols > cols ? cols : this->cols;
87 memcpy(mCells, cells, sizeof(VTermScreenCell) * n);
88 return n;
89 }
90
copyTo(dimen_t cols,VTermScreenCell * cells)91 inline dimen_t copyTo(dimen_t cols, VTermScreenCell* cells) {
92 dimen_t n = cols > this->cols ? this->cols : cols;
93 memcpy(cells, mCells, sizeof(VTermScreenCell) * n);
94 return n;
95 }
96
getCell(dimen_t col,VTermScreenCell * cell)97 inline void getCell(dimen_t col, VTermScreenCell* cell) {
98 *cell = mCells[col];
99 }
100
101 const dimen_t cols;
102
103 private:
104 VTermScreenCell* mCells;
105 };
106
107 /*
108 * Terminal session
109 */
110 class Terminal {
111 public:
112 Terminal(jobject callbacks, dimen_t rows, dimen_t cols);
113 ~Terminal();
114
115 status_t run();
116
117 size_t write(const char *bytes, size_t len);
118
119 bool dispatchCharacter(int mod, int character);
120 bool dispatchKey(int mod, int key);
121 bool flushInput();
122
123 status_t resize(dimen_t rows, dimen_t cols, dimen_t scrollRows);
124
125 status_t onPushline(dimen_t cols, const VTermScreenCell* cells);
126 status_t onPopline(dimen_t cols, VTermScreenCell* cells);
127
128 void getCellLocked(VTermPos pos, VTermScreenCell* cell);
129
130 dimen_t getRows() const;
131 dimen_t getCols() const;
132 dimen_t getScrollRows() const;
133
134 jobject getCallbacks() const;
135
136 // Lock protecting mutations of internal libvterm state
137 Mutex mLock;
138
139 private:
140 int mMasterFd;
141 pid_t mChildPid;
142 VTerm *mVt;
143 VTermScreen *mVts;
144
145 jobject mCallbacks;
146
147 dimen_t mRows;
148 dimen_t mCols;
149 bool mKilled;
150
151 ScrollbackLine **mScroll;
152 dimen_t mScrollCur;
153 dimen_t mScrollSize;
154
155 };
156
157 /*
158 * VTerm event handlers
159 */
160
term_damage(VTermRect rect,void * user)161 static int term_damage(VTermRect rect, void *user) {
162 Terminal* term = reinterpret_cast<Terminal*>(user);
163 #if DEBUG_CALLBACKS
164 ALOGW("term_damage");
165 #endif
166
167 JNIEnv* env = AndroidRuntime::getJNIEnv();
168 return env->CallIntMethod(term->getCallbacks(), damageMethod, rect.start_row, rect.end_row,
169 rect.start_col, rect.end_col);
170 }
171
term_moverect(VTermRect dest,VTermRect src,void * user)172 static int term_moverect(VTermRect dest, VTermRect src, void *user) {
173 Terminal* term = reinterpret_cast<Terminal*>(user);
174 #if DEBUG_CALLBACKS
175 ALOGW("term_moverect");
176 #endif
177
178 JNIEnv* env = AndroidRuntime::getJNIEnv();
179 return env->CallIntMethod(term->getCallbacks(), moveRectMethod,
180 dest.start_row, dest.end_row, dest.start_col, dest.end_col,
181 src.start_row, src.end_row, src.start_col, src.end_col);
182 }
183
term_movecursor(VTermPos pos,VTermPos oldpos,int visible,void * user)184 static int term_movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user) {
185 Terminal* term = reinterpret_cast<Terminal*>(user);
186 #if DEBUG_CALLBACKS
187 ALOGW("term_movecursor");
188 #endif
189
190 JNIEnv* env = AndroidRuntime::getJNIEnv();
191 return env->CallIntMethod(term->getCallbacks(), moveCursorMethod, pos.row,
192 pos.col, oldpos.row, oldpos.col, visible);
193 }
194
term_settermprop(VTermProp prop,VTermValue * val,void * user)195 static int term_settermprop(VTermProp prop, VTermValue *val, void *user) {
196 Terminal* term = reinterpret_cast<Terminal*>(user);
197 #if DEBUG_CALLBACKS
198 ALOGW("term_settermprop");
199 #endif
200
201 JNIEnv* env = AndroidRuntime::getJNIEnv();
202 switch (vterm_get_prop_type(prop)) {
203 case VTERM_VALUETYPE_BOOL:
204 return env->CallIntMethod(term->getCallbacks(), setTermPropBooleanMethod,
205 static_cast<jboolean>(val->boolean));
206 case VTERM_VALUETYPE_INT:
207 return env->CallIntMethod(term->getCallbacks(), setTermPropIntMethod, prop, val->number);
208 case VTERM_VALUETYPE_STRING:
209 return env->CallIntMethod(term->getCallbacks(), setTermPropStringMethod, prop,
210 env->NewStringUTF(val->string));
211 case VTERM_VALUETYPE_COLOR:
212 return env->CallIntMethod(term->getCallbacks(), setTermPropIntMethod, prop, val->color.red,
213 val->color.green, val->color.blue);
214 default:
215 ALOGE("unknown callback type");
216 return 0;
217 }
218 }
219
term_setmousefunc(VTermMouseFunc func,void * data,void * user)220 static int term_setmousefunc(VTermMouseFunc func, void *data, void *user) {
221 Terminal* term = reinterpret_cast<Terminal*>(user);
222 #if DEBUG_CALLBACKS
223 ALOGW("term_setmousefunc");
224 #endif
225 return 1;
226 }
227
term_bell(void * user)228 static int term_bell(void *user) {
229 Terminal* term = reinterpret_cast<Terminal*>(user);
230 #if DEBUG_CALLBACKS
231 ALOGW("term_bell");
232 #endif
233
234 JNIEnv* env = AndroidRuntime::getJNIEnv();
235 return env->CallIntMethod(term->getCallbacks(), bellMethod);
236 }
237
term_sb_pushline(int cols,const VTermScreenCell * cells,void * user)238 static int term_sb_pushline(int cols, const VTermScreenCell *cells, void *user) {
239 Terminal* term = reinterpret_cast<Terminal*>(user);
240 #if DEBUG_CALLBACKS
241 ALOGW("term_sb_pushline");
242 #endif
243
244 return term->onPushline(cols, cells);
245 }
246
term_sb_popline(int cols,VTermScreenCell * cells,void * user)247 static int term_sb_popline(int cols, VTermScreenCell *cells, void *user) {
248 Terminal* term = reinterpret_cast<Terminal*>(user);
249 #if DEBUG_CALLBACKS
250 ALOGW("term_sb_popline");
251 #endif
252
253 return term->onPopline(cols, cells);
254 }
255
256 static VTermScreenCallbacks cb = {
257 .damage = term_damage,
258 .moverect = term_moverect,
259 .movecursor = term_movecursor,
260 .settermprop = term_settermprop,
261 .setmousefunc = term_setmousefunc,
262 .bell = term_bell,
263 // Resize requests are applied immediately, so callback is ignored
264 .resize = NULL,
265 .sb_pushline = term_sb_pushline,
266 .sb_popline = term_sb_popline,
267 };
268
Terminal(jobject callbacks,dimen_t rows,dimen_t cols)269 Terminal::Terminal(jobject callbacks, dimen_t rows, dimen_t cols) :
270 mCallbacks(callbacks), mRows(rows), mCols(cols), mKilled(false),
271 mScrollCur(0), mScrollSize(100) {
272 JNIEnv* env = AndroidRuntime::getJNIEnv();
273 mCallbacks = env->NewGlobalRef(callbacks);
274
275 mScroll = new ScrollbackLine*[mScrollSize];
276 memset(mScroll, 0, sizeof(ScrollbackLine*) * mScrollSize);
277
278 /* Create VTerm */
279 mVt = vterm_new(rows, cols);
280 vterm_parser_set_utf8(mVt, 1);
281
282 /* Set up screen */
283 mVts = vterm_obtain_screen(mVt);
284 vterm_screen_enable_altscreen(mVts, 1);
285 vterm_screen_set_callbacks(mVts, &cb, this);
286 vterm_screen_set_damage_merge(mVts, VTERM_DAMAGE_SCROLL);
287 vterm_screen_reset(mVts, 1);
288 }
289
~Terminal()290 Terminal::~Terminal() {
291 close(mMasterFd);
292 ::kill(mChildPid, SIGHUP);
293
294 vterm_free(mVt);
295
296 delete mScroll;
297
298 JNIEnv *env = AndroidRuntime::getJNIEnv();
299 env->DeleteGlobalRef(mCallbacks);
300 }
301
run()302 status_t Terminal::run() {
303 struct termios termios = {
304 .c_iflag = ICRNL|IXON|IUTF8,
305 .c_oflag = OPOST|ONLCR|NL0|CR0|TAB0|BS0|VT0|FF0,
306 .c_cflag = CS8|CREAD,
307 .c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK,
308 /* c_cc later */
309 };
310
311 cfsetispeed(&termios, B38400);
312 cfsetospeed(&termios, B38400);
313
314 termios.c_cc[VINTR] = 0x1f & 'C';
315 termios.c_cc[VQUIT] = 0x1f & '\\';
316 termios.c_cc[VERASE] = 0x7f;
317 termios.c_cc[VKILL] = 0x1f & 'U';
318 termios.c_cc[VEOF] = 0x1f & 'D';
319 termios.c_cc[VSTART] = 0x1f & 'Q';
320 termios.c_cc[VSTOP] = 0x1f & 'S';
321 termios.c_cc[VSUSP] = 0x1f & 'Z';
322 termios.c_cc[VREPRINT] = 0x1f & 'R';
323 termios.c_cc[VWERASE] = 0x1f & 'W';
324 termios.c_cc[VLNEXT] = 0x1f & 'V';
325 termios.c_cc[VMIN] = 1;
326 termios.c_cc[VTIME] = 0;
327
328 struct winsize size = { mRows, mCols, 0, 0 };
329
330 int stderr_save_fd = dup(2);
331 if (stderr_save_fd < 0) {
332 ALOGE("failed to dup stderr - %s", strerror(errno));
333 }
334
335 mChildPid = forkpty(&mMasterFd, NULL, &termios, &size);
336 if (mChildPid == 0) {
337 /* Restore the ISIG signals back to defaults */
338 signal(SIGINT, SIG_DFL);
339 signal(SIGQUIT, SIG_DFL);
340 signal(SIGSTOP, SIG_DFL);
341 signal(SIGCONT, SIG_DFL);
342
343 FILE *stderr_save = fdopen(stderr_save_fd, "a");
344
345 if (!stderr_save) {
346 ALOGE("failed to open stderr - %s", strerror(errno));
347 }
348
349 char *shell = "/system/bin/sh"; //getenv("SHELL");
350 #if USE_TEST_SHELL
351 char *args[4] = {shell, "-c", "x=1; c=0; while true; do echo -e \"stop \e[00;3${c}mechoing\e[00m yourself! ($x)\"; x=$(( $x + 1 )); c=$((($c+1)%7)); if [ $x -gt 110 ]; then sleep 0.5; fi; done", NULL};
352 #else
353 char *args[2] = {shell, NULL};
354 #endif
355
356 execvp(shell, args);
357 fprintf(stderr_save, "Cannot exec(%s) - %s\n", shell, strerror(errno));
358 _exit(1);
359 }
360
361 ALOGD("entering read() loop");
362 while (1) {
363 char buffer[4096];
364 ssize_t bytes = ::read(mMasterFd, buffer, sizeof buffer);
365 #if DEBUG_IO
366 ALOGD("read() returned %d bytes", bytes);
367 #endif
368
369 if (mKilled) {
370 ALOGD("kill() requested");
371 break;
372 }
373 if (bytes == 0) {
374 ALOGD("read() found EOF");
375 break;
376 }
377 if (bytes == -1) {
378 ALOGE("read() failed: %s", strerror(errno));
379 return 1;
380 }
381
382 {
383 Mutex::Autolock lock(mLock);
384 vterm_push_bytes(mVt, buffer, bytes);
385 vterm_screen_flush_damage(mVts);
386 }
387 }
388
389 return 0;
390 }
391
write(const char * bytes,size_t len)392 size_t Terminal::write(const char *bytes, size_t len) {
393 return ::write(mMasterFd, bytes, len);
394 }
395
dispatchCharacter(int mod,int character)396 bool Terminal::dispatchCharacter(int mod, int character) {
397 Mutex::Autolock lock(mLock);
398 vterm_input_push_char(mVt, static_cast<VTermModifier>(mod), character);
399 return flushInput();
400 }
401
dispatchKey(int mod,int key)402 bool Terminal::dispatchKey(int mod, int key) {
403 Mutex::Autolock lock(mLock);
404 vterm_input_push_key(mVt, static_cast<VTermModifier>(mod), static_cast<VTermKey>(key));
405 return flushInput();
406 }
407
flushInput()408 bool Terminal::flushInput() {
409 size_t len = vterm_output_get_buffer_current(mVt);
410 if (len) {
411 char buf[len];
412 len = vterm_output_bufferread(mVt, buf, len);
413 return len == write(buf, len);
414 }
415 return true;
416 }
417
resize(dimen_t rows,dimen_t cols,dimen_t scrollRows)418 status_t Terminal::resize(dimen_t rows, dimen_t cols, dimen_t scrollRows) {
419 Mutex::Autolock lock(mLock);
420
421 ALOGD("resize(%d, %d, %d)", rows, cols, scrollRows);
422
423 mRows = rows;
424 mCols = cols;
425 // TODO: resize scrollback
426
427 struct winsize size = { rows, cols, 0, 0 };
428 ioctl(mMasterFd, TIOCSWINSZ, &size);
429
430 vterm_set_size(mVt, rows, cols);
431 vterm_screen_flush_damage(mVts);
432
433 return 0;
434 }
435
onPushline(dimen_t cols,const VTermScreenCell * cells)436 status_t Terminal::onPushline(dimen_t cols, const VTermScreenCell* cells) {
437 ScrollbackLine* line = NULL;
438 if (mScrollCur == mScrollSize) {
439 /* Recycle old row if it's the right size */
440 if (mScroll[mScrollCur - 1]->cols == cols) {
441 line = mScroll[mScrollCur - 1];
442 } else {
443 delete mScroll[mScrollCur - 1];
444 }
445
446 memmove(mScroll + 1, mScroll, sizeof(ScrollbackLine*) * (mScrollCur - 1));
447 } else if (mScrollCur > 0) {
448 memmove(mScroll + 1, mScroll, sizeof(ScrollbackLine*) * mScrollCur);
449 }
450
451 if (line == NULL) {
452 line = new ScrollbackLine(cols);
453 }
454
455 mScroll[0] = line;
456
457 if (mScrollCur < mScrollSize) {
458 mScrollCur++;
459 }
460
461 line->copyFrom(cols, cells);
462 return 1;
463 }
464
onPopline(dimen_t cols,VTermScreenCell * cells)465 status_t Terminal::onPopline(dimen_t cols, VTermScreenCell* cells) {
466 if (mScrollCur == 0) {
467 return 0;
468 }
469
470 ScrollbackLine* line = mScroll[0];
471 mScrollCur--;
472 memmove(mScroll, mScroll + 1, sizeof(ScrollbackLine*) * mScrollCur);
473
474 dimen_t n = line->copyTo(cols, cells);
475 for (dimen_t col = n; col < cols; col++) {
476 cells[col].chars[0] = 0;
477 cells[col].width = 1;
478 }
479
480 delete line;
481 return 1;
482 }
483
getCellLocked(VTermPos pos,VTermScreenCell * cell)484 void Terminal::getCellLocked(VTermPos pos, VTermScreenCell* cell) {
485 // The UI may be asking for cell data while the model is changing
486 // underneath it, so we always fill with meaningful data.
487
488 if (pos.row < 0) {
489 size_t scrollRow = -pos.row;
490 if (scrollRow > mScrollCur) {
491 // Invalid region above current scrollback
492 cell->width = 1;
493 #if DEBUG_SCROLLBACK
494 cell->bg.red = 255;
495 #endif
496 return;
497 }
498
499 ScrollbackLine* line = mScroll[scrollRow - 1];
500 if ((size_t) pos.col < line->cols) {
501 // Valid scrollback cell
502 line->getCell(pos.col, cell);
503 cell->width = 1;
504 #if DEBUG_SCROLLBACK
505 cell->bg.blue = 255;
506 #endif
507 return;
508 } else {
509 // Extend last scrollback cell into invalid region
510 line->getCell(line->cols - 1, cell);
511 cell->width = 1;
512 cell->chars[0] = ' ';
513 #if DEBUG_SCROLLBACK
514 cell->bg.green = 255;
515 #endif
516 return;
517 }
518 }
519
520 if ((size_t) pos.row >= mRows) {
521 // Invalid region below screen
522 cell->width = 1;
523 #if DEBUG_SCROLLBACK
524 cell->bg.red = 128;
525 #endif
526 return;
527 }
528
529 // Valid screen cell
530 vterm_screen_get_cell(mVts, pos, cell);
531 }
532
getRows() const533 dimen_t Terminal::getRows() const {
534 return mRows;
535 }
536
getCols() const537 dimen_t Terminal::getCols() const {
538 return mCols;
539 }
540
getScrollRows() const541 dimen_t Terminal::getScrollRows() const {
542 return mScrollSize;
543 }
544
getCallbacks() const545 jobject Terminal::getCallbacks() const {
546 return mCallbacks;
547 }
548
549 /*
550 * JNI glue
551 */
552
com_android_terminal_Terminal_nativeInit(JNIEnv * env,jclass clazz,jobject callbacks,jint rows,jint cols)553 static jlong com_android_terminal_Terminal_nativeInit(JNIEnv* env, jclass clazz, jobject callbacks,
554 jint rows, jint cols) {
555 return reinterpret_cast<jlong>(new Terminal(callbacks, rows, cols));
556 }
557
com_android_terminal_Terminal_nativeDestroy(JNIEnv * env,jclass clazz,jlong ptr)558 static jint com_android_terminal_Terminal_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
559 Terminal* term = reinterpret_cast<Terminal*>(ptr);
560 delete term;
561 return 0;
562 }
563
com_android_terminal_Terminal_nativeRun(JNIEnv * env,jclass clazz,jlong ptr)564 static jint com_android_terminal_Terminal_nativeRun(JNIEnv* env, jclass clazz, jlong ptr) {
565 Terminal* term = reinterpret_cast<Terminal*>(ptr);
566 return term->run();
567 }
568
com_android_terminal_Terminal_nativeResize(JNIEnv * env,jclass clazz,jlong ptr,jint rows,jint cols,jint scrollRows)569 static jint com_android_terminal_Terminal_nativeResize(JNIEnv* env,
570 jclass clazz, jlong ptr, jint rows, jint cols, jint scrollRows) {
571 Terminal* term = reinterpret_cast<Terminal*>(ptr);
572 return term->resize(rows, cols, scrollRows);
573 }
574
toArgb(const VTermColor & color)575 static inline int toArgb(const VTermColor& color) {
576 return (0xff << 24 | color.red << 16 | color.green << 8 | color.blue);
577 }
578
isCellStyleEqual(const VTermScreenCell & a,const VTermScreenCell & b)579 static inline bool isCellStyleEqual(const VTermScreenCell& a, const VTermScreenCell& b) {
580 if (toArgb(a.fg) != toArgb(b.fg)) return false;
581 if (toArgb(a.bg) != toArgb(b.bg)) return false;
582
583 if (a.attrs.bold != b.attrs.bold) return false;
584 if (a.attrs.underline != b.attrs.underline) return false;
585 if (a.attrs.italic != b.attrs.italic) return false;
586 if (a.attrs.blink != b.attrs.blink) return false;
587 if (a.attrs.reverse != b.attrs.reverse) return false;
588 if (a.attrs.strike != b.attrs.strike) return false;
589 if (a.attrs.font != b.attrs.font) return false;
590
591 return true;
592 }
593
com_android_terminal_Terminal_nativeGetCellRun(JNIEnv * env,jclass clazz,jlong ptr,jint row,jint col,jobject run)594 static jint com_android_terminal_Terminal_nativeGetCellRun(JNIEnv* env,
595 jclass clazz, jlong ptr, jint row, jint col, jobject run) {
596 Terminal* term = reinterpret_cast<Terminal*>(ptr);
597 Mutex::Autolock lock(term->mLock);
598
599 jcharArray dataArray = (jcharArray) env->GetObjectField(run, cellRunDataField);
600 ScopedCharArrayRW data(env, dataArray);
601 if (data.get() == NULL) {
602 return -1;
603 }
604
605 VTermScreenCell firstCell, cell;
606
607 VTermPos pos = {
608 .row = row,
609 .col = col,
610 };
611
612 size_t dataSize = 0;
613 size_t colSize = 0;
614 while ((size_t) pos.col < term->getCols()) {
615 memset(&cell, 0, sizeof(VTermScreenCell));
616 term->getCellLocked(pos, &cell);
617
618 if (colSize == 0) {
619 env->SetIntField(run, cellRunFgField, toArgb(cell.fg));
620 env->SetIntField(run, cellRunBgField, toArgb(cell.bg));
621 memcpy(&firstCell, &cell, sizeof(VTermScreenCell));
622 } else {
623 if (!isCellStyleEqual(cell, firstCell)) {
624 break;
625 }
626 }
627
628 // Only include cell chars if they fit into run
629 uint32_t rawCell = cell.chars[0];
630 size_t size = (rawCell < 0x10000) ? 1 : 2;
631 if (dataSize + size <= data.size()) {
632 if (rawCell < 0x10000) {
633 data[dataSize++] = rawCell;
634 } else {
635 data[dataSize++] = (((rawCell - 0x10000) >> 10) & 0x3ff) + 0xd800;
636 data[dataSize++] = ((rawCell - 0x10000) & 0x3ff) + 0xdc00;
637 }
638
639 for (int i = 1; i < cell.width; i++) {
640 data[dataSize++] = ' ';
641 }
642
643 colSize += cell.width;
644 pos.col += cell.width;
645 } else {
646 break;
647 }
648 }
649
650 env->SetIntField(run, cellRunDataSizeField, dataSize);
651 env->SetIntField(run, cellRunColSizeField, colSize);
652
653 return 0;
654 }
655
com_android_terminal_Terminal_nativeGetRows(JNIEnv * env,jclass clazz,jlong ptr)656 static jint com_android_terminal_Terminal_nativeGetRows(JNIEnv* env, jclass clazz, jlong ptr) {
657 Terminal* term = reinterpret_cast<Terminal*>(ptr);
658 return term->getRows();
659 }
660
com_android_terminal_Terminal_nativeGetCols(JNIEnv * env,jclass clazz,jlong ptr)661 static jint com_android_terminal_Terminal_nativeGetCols(JNIEnv* env, jclass clazz, jlong ptr) {
662 Terminal* term = reinterpret_cast<Terminal*>(ptr);
663 return term->getCols();
664 }
665
com_android_terminal_Terminal_nativeGetScrollRows(JNIEnv * env,jclass clazz,jlong ptr)666 static jint com_android_terminal_Terminal_nativeGetScrollRows(JNIEnv* env, jclass clazz, jlong ptr) {
667 Terminal* term = reinterpret_cast<Terminal*>(ptr);
668 return term->getScrollRows();
669 }
670
com_android_terminal_Terminal_nativeDispatchCharacter(JNIEnv * env,jclass clazz,jlong ptr,jint mod,jint c)671 static jboolean com_android_terminal_Terminal_nativeDispatchCharacter(JNIEnv *env, jclass clazz,
672 jlong ptr, jint mod, jint c) {
673 Terminal* term = reinterpret_cast<Terminal*>(ptr);
674 return term->dispatchCharacter(mod, c);
675 }
676
com_android_terminal_Terminal_nativeDispatchKey(JNIEnv * env,jclass clazz,jlong ptr,jint mod,jint c)677 static jboolean com_android_terminal_Terminal_nativeDispatchKey(JNIEnv *env, jclass clazz,
678 jlong ptr, jint mod, jint c) {
679 Terminal* term = reinterpret_cast<Terminal*>(ptr);
680 return term->dispatchKey(mod, c);
681 }
682
683 static JNINativeMethod gMethods[] = {
684 { "nativeInit", "(Lcom/android/terminal/TerminalCallbacks;II)L", (void*)com_android_terminal_Terminal_nativeInit },
685 { "nativeDestroy", "(J)I", (void*)com_android_terminal_Terminal_nativeDestroy },
686 { "nativeRun", "(J)I", (void*)com_android_terminal_Terminal_nativeRun },
687 { "nativeResize", "(JIII)I", (void*)com_android_terminal_Terminal_nativeResize },
688 { "nativeGetCellRun", "(JIILcom/android/terminal/Terminal$CellRun;)I", (void*)com_android_terminal_Terminal_nativeGetCellRun },
689 { "nativeGetRows", "(J)I", (void*)com_android_terminal_Terminal_nativeGetRows },
690 { "nativeGetCols", "(J)I", (void*)com_android_terminal_Terminal_nativeGetCols },
691 { "nativeGetScrollRows", "(J)I", (void*)com_android_terminal_Terminal_nativeGetScrollRows },
692 { "nativeDispatchCharacter", "(JII)Z", (void*)com_android_terminal_Terminal_nativeDispatchCharacter},
693 { "nativeDispatchKey", "(JII)Z", (void*)com_android_terminal_Terminal_nativeDispatchKey },
694 };
695
register_com_android_terminal_Terminal(JNIEnv * env)696 int register_com_android_terminal_Terminal(JNIEnv* env) {
697 ScopedLocalRef<jclass> localClass(env,
698 env->FindClass("com/android/terminal/TerminalCallbacks"));
699
700 android::terminalCallbacksClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
701
702 android::damageMethod = env->GetMethodID(terminalCallbacksClass, "damage", "(IIII)I");
703 android::moveRectMethod = env->GetMethodID(terminalCallbacksClass, "moveRect", "(IIIIIIII)I");
704 android::moveCursorMethod = env->GetMethodID(terminalCallbacksClass, "moveCursor",
705 "(IIIII)I");
706 android::setTermPropBooleanMethod = env->GetMethodID(terminalCallbacksClass,
707 "setTermPropBoolean", "(IZ)I");
708 android::setTermPropIntMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropInt",
709 "(II)I");
710 android::setTermPropStringMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropString",
711 "(ILjava/lang/String;)I");
712 android::setTermPropColorMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropColor",
713 "(IIII)I");
714 android::bellMethod = env->GetMethodID(terminalCallbacksClass, "bell", "()I");
715
716 ScopedLocalRef<jclass> cellRunLocal(env,
717 env->FindClass("com/android/terminal/Terminal$CellRun"));
718 cellRunClass = reinterpret_cast<jclass>(env->NewGlobalRef(cellRunLocal.get()));
719 cellRunDataField = env->GetFieldID(cellRunClass, "data", "[C");
720 cellRunDataSizeField = env->GetFieldID(cellRunClass, "dataSize", "I");
721 cellRunColSizeField = env->GetFieldID(cellRunClass, "colSize", "I");
722 cellRunFgField = env->GetFieldID(cellRunClass, "fg", "I");
723 cellRunBgField = env->GetFieldID(cellRunClass, "bg", "I");
724
725 return jniRegisterNativeMethods(env, "com/android/terminal/Terminal",
726 gMethods, NELEM(gMethods));
727 }
728
729 } /* namespace android */
730