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 <nativehelper/JNIHelp.h> 25 #include <nativehelper/ScopedLocalRef.h> 26 #include <nativehelper/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: 76 inline ScrollbackLine(dimen_t _cols) : cols(_cols) { 77 mCells = new VTermScreenCell[cols]; 78 }; 79 inline ~ScrollbackLine() { 80 delete mCells; 81 } 82 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 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 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 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 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 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 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 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 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 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 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 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 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 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 390 size_t Terminal::write(const char *bytes, size_t len) { 391 return ::write(mMasterFd, bytes, len); 392 } 393 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 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 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 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 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 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 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 531 dimen_t Terminal::getRows() const { 532 return mRows; 533 } 534 535 dimen_t Terminal::getCols() const { 536 return mCols; 537 } 538 539 dimen_t Terminal::getScrollRows() const { 540 return mScrollSize; 541 } 542 543 jobject Terminal::getCallbacks() const { 544 return mCallbacks; 545 } 546 547 /* 548 * JNI glue 549 */ 550 551 static jlong com_android_terminal_Terminal_nativeInit(JNIEnv* env, jclass clazz, jobject callbacks) { 552 return reinterpret_cast<jlong>(new Terminal(callbacks)); 553 } 554 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 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 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 572 static inline int toArgb(const VTermColor& color) { 573 return (0xff << 24 | color.red << 16 | color.green << 8 | color.blue); 574 } 575 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 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 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 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 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 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 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 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