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