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 #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: 78 inline ScrollbackLine(dimen_t _cols) : cols(_cols) { 79 mCells = new VTermScreenCell[cols]; 80 }; 81 inline ~ScrollbackLine() { 82 delete mCells; 83 } 84 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 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 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); 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 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 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 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 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, prop, 205 val->boolean ? JNI_TRUE : JNI_FALSE); 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 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 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 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 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 269 Terminal::Terminal(jobject callbacks) : 270 mCallbacks(callbacks), mRows(25), mCols(80), 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(mRows, mCols); 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 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 302 status_t Terminal::run() { 303 struct termios termios; 304 memset(&termios, 0, sizeof(termios)); 305 termios.c_iflag = ICRNL|IXON|IUTF8; 306 termios.c_oflag = OPOST|ONLCR|NL0|CR0|TAB0|BS0|VT0|FF0; 307 termios.c_cflag = CS8|CREAD; 308 termios.c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK; 309 310 cfsetispeed(&termios, B38400); 311 cfsetospeed(&termios, B38400); 312 313 termios.c_cc[VINTR] = 0x1f & 'C'; 314 termios.c_cc[VQUIT] = 0x1f & '\\'; 315 termios.c_cc[VERASE] = 0x7f; 316 termios.c_cc[VKILL] = 0x1f & 'U'; 317 termios.c_cc[VEOF] = 0x1f & 'D'; 318 termios.c_cc[VSTART] = 0x1f & 'Q'; 319 termios.c_cc[VSTOP] = 0x1f & 'S'; 320 termios.c_cc[VSUSP] = 0x1f & 'Z'; 321 termios.c_cc[VREPRINT] = 0x1f & 'R'; 322 termios.c_cc[VWERASE] = 0x1f & 'W'; 323 termios.c_cc[VLNEXT] = 0x1f & 'V'; 324 termios.c_cc[VMIN] = 1; 325 termios.c_cc[VTIME] = 0; 326 327 struct winsize size = { mRows, mCols, 0, 0 }; 328 329 int stderr_save_fd = dup(2); 330 if (stderr_save_fd < 0) { 331 ALOGE("failed to dup stderr - %s", strerror(errno)); 332 } 333 334 mChildPid = forkpty(&mMasterFd, NULL, &termios, &size); 335 if (mChildPid == 0) { 336 /* Restore the ISIG signals back to defaults */ 337 signal(SIGINT, SIG_DFL); 338 signal(SIGQUIT, SIG_DFL); 339 signal(SIGSTOP, SIG_DFL); 340 signal(SIGCONT, SIG_DFL); 341 342 FILE *stderr_save = fdopen(stderr_save_fd, "a"); 343 344 if (!stderr_save) { 345 ALOGE("failed to open stderr - %s", strerror(errno)); 346 } 347 348 // We know execvp(2) won't actually try to modify this. 349 char *shell = const_cast<char*>("/system/bin/sh"); 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 392 size_t Terminal::write(const char *bytes, size_t len) { 393 return ::write(mMasterFd, bytes, len); 394 } 395 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 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 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 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 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 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 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 533 dimen_t Terminal::getRows() const { 534 return mRows; 535 } 536 537 dimen_t Terminal::getCols() const { 538 return mCols; 539 } 540 541 dimen_t Terminal::getScrollRows() const { 542 return mScrollSize; 543 } 544 545 jobject Terminal::getCallbacks() const { 546 return mCallbacks; 547 } 548 549 /* 550 * JNI glue 551 */ 552 553 static jlong com_android_terminal_Terminal_nativeInit(JNIEnv* env, jclass clazz, jobject callbacks) { 554 return reinterpret_cast<jlong>(new Terminal(callbacks)); 555 } 556 557 static jint com_android_terminal_Terminal_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { 558 Terminal* term = reinterpret_cast<Terminal*>(ptr); 559 delete term; 560 return 0; 561 } 562 563 static jint com_android_terminal_Terminal_nativeRun(JNIEnv* env, jclass clazz, jlong ptr) { 564 Terminal* term = reinterpret_cast<Terminal*>(ptr); 565 return term->run(); 566 } 567 568 static jint com_android_terminal_Terminal_nativeResize(JNIEnv* env, 569 jclass clazz, jlong ptr, jint rows, jint cols, jint scrollRows) { 570 Terminal* term = reinterpret_cast<Terminal*>(ptr); 571 return term->resize(rows, cols, scrollRows); 572 } 573 574 static inline int toArgb(const VTermColor& color) { 575 return (0xff << 24 | color.red << 16 | color.green << 8 | color.blue); 576 } 577 578 static inline bool isCellStyleEqual(const VTermScreenCell& a, const VTermScreenCell& b) { 579 if (toArgb(a.fg) != toArgb(b.fg)) return false; 580 if (toArgb(a.bg) != toArgb(b.bg)) return false; 581 582 if (a.attrs.bold != b.attrs.bold) return false; 583 if (a.attrs.underline != b.attrs.underline) return false; 584 if (a.attrs.italic != b.attrs.italic) return false; 585 if (a.attrs.blink != b.attrs.blink) return false; 586 if (a.attrs.reverse != b.attrs.reverse) return false; 587 if (a.attrs.strike != b.attrs.strike) return false; 588 if (a.attrs.font != b.attrs.font) return false; 589 590 return true; 591 } 592 593 static jint com_android_terminal_Terminal_nativeGetCellRun(JNIEnv* env, 594 jclass clazz, jlong ptr, jint row, jint col, jobject run) { 595 Terminal* term = reinterpret_cast<Terminal*>(ptr); 596 Mutex::Autolock lock(term->mLock); 597 598 jcharArray dataArray = (jcharArray) env->GetObjectField(run, cellRunDataField); 599 ScopedCharArrayRW data(env, dataArray); 600 if (data.get() == NULL) { 601 return -1; 602 } 603 604 VTermScreenCell firstCell, cell; 605 606 VTermPos pos = { 607 .row = row, 608 .col = col, 609 }; 610 611 size_t dataSize = 0; 612 size_t colSize = 0; 613 while ((size_t) pos.col < term->getCols()) { 614 memset(&cell, 0, sizeof(VTermScreenCell)); 615 term->getCellLocked(pos, &cell); 616 617 if (colSize == 0) { 618 env->SetIntField(run, cellRunFgField, toArgb(cell.fg)); 619 env->SetIntField(run, cellRunBgField, toArgb(cell.bg)); 620 memcpy(&firstCell, &cell, sizeof(VTermScreenCell)); 621 } else { 622 if (!isCellStyleEqual(cell, firstCell)) { 623 break; 624 } 625 } 626 627 // Only include cell chars if they fit into run 628 uint32_t rawCell = cell.chars[0]; 629 size_t size = (rawCell < 0x10000) ? 1 : 2; 630 if (dataSize + size <= data.size()) { 631 if (rawCell < 0x10000) { 632 data[dataSize++] = rawCell; 633 } else { 634 data[dataSize++] = (((rawCell - 0x10000) >> 10) & 0x3ff) + 0xd800; 635 data[dataSize++] = ((rawCell - 0x10000) & 0x3ff) + 0xdc00; 636 } 637 638 for (int i = 1; i < cell.width; i++) { 639 data[dataSize++] = ' '; 640 } 641 642 colSize += cell.width; 643 pos.col += cell.width; 644 } else { 645 break; 646 } 647 } 648 649 env->SetIntField(run, cellRunDataSizeField, dataSize); 650 env->SetIntField(run, cellRunColSizeField, colSize); 651 652 return 0; 653 } 654 655 static jint com_android_terminal_Terminal_nativeGetRows(JNIEnv* env, jclass clazz, jlong ptr) { 656 Terminal* term = reinterpret_cast<Terminal*>(ptr); 657 return term->getRows(); 658 } 659 660 static jint com_android_terminal_Terminal_nativeGetCols(JNIEnv* env, jclass clazz, jlong ptr) { 661 Terminal* term = reinterpret_cast<Terminal*>(ptr); 662 return term->getCols(); 663 } 664 665 static jint com_android_terminal_Terminal_nativeGetScrollRows(JNIEnv* env, jclass clazz, jlong ptr) { 666 Terminal* term = reinterpret_cast<Terminal*>(ptr); 667 return term->getScrollRows(); 668 } 669 670 static jboolean com_android_terminal_Terminal_nativeDispatchCharacter(JNIEnv *env, jclass clazz, 671 jlong ptr, jint mod, jint c) { 672 Terminal* term = reinterpret_cast<Terminal*>(ptr); 673 return term->dispatchCharacter(mod, c); 674 } 675 676 static jboolean com_android_terminal_Terminal_nativeDispatchKey(JNIEnv *env, jclass clazz, 677 jlong ptr, jint mod, jint c) { 678 Terminal* term = reinterpret_cast<Terminal*>(ptr); 679 return term->dispatchKey(mod, c); 680 } 681 682 static JNINativeMethod gMethods[] = { 683 { "nativeInit", "(Lcom/android/terminal/TerminalCallbacks;)J", (void*)com_android_terminal_Terminal_nativeInit }, 684 { "nativeDestroy", "(J)I", (void*)com_android_terminal_Terminal_nativeDestroy }, 685 { "nativeRun", "(J)I", (void*)com_android_terminal_Terminal_nativeRun }, 686 { "nativeResize", "(JIII)I", (void*)com_android_terminal_Terminal_nativeResize }, 687 { "nativeGetCellRun", "(JIILcom/android/terminal/Terminal$CellRun;)I", (void*)com_android_terminal_Terminal_nativeGetCellRun }, 688 { "nativeGetRows", "(J)I", (void*)com_android_terminal_Terminal_nativeGetRows }, 689 { "nativeGetCols", "(J)I", (void*)com_android_terminal_Terminal_nativeGetCols }, 690 { "nativeGetScrollRows", "(J)I", (void*)com_android_terminal_Terminal_nativeGetScrollRows }, 691 { "nativeDispatchCharacter", "(JII)Z", (void*)com_android_terminal_Terminal_nativeDispatchCharacter}, 692 { "nativeDispatchKey", "(JII)Z", (void*)com_android_terminal_Terminal_nativeDispatchKey }, 693 }; 694 695 int register_com_android_terminal_Terminal(JNIEnv* env) { 696 ScopedLocalRef<jclass> localClass(env, 697 env->FindClass("com/android/terminal/TerminalCallbacks")); 698 699 android::terminalCallbacksClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); 700 701 android::damageMethod = env->GetMethodID(terminalCallbacksClass, "damage", "(IIII)I"); 702 android::moveRectMethod = env->GetMethodID(terminalCallbacksClass, "moveRect", "(IIIIIIII)I"); 703 android::moveCursorMethod = env->GetMethodID(terminalCallbacksClass, "moveCursor", 704 "(IIIII)I"); 705 android::setTermPropBooleanMethod = env->GetMethodID(terminalCallbacksClass, 706 "setTermPropBoolean", "(IZ)I"); 707 android::setTermPropIntMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropInt", 708 "(II)I"); 709 android::setTermPropStringMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropString", 710 "(ILjava/lang/String;)I"); 711 android::setTermPropColorMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropColor", 712 "(IIII)I"); 713 android::bellMethod = env->GetMethodID(terminalCallbacksClass, "bell", "()I"); 714 715 ScopedLocalRef<jclass> cellRunLocal(env, 716 env->FindClass("com/android/terminal/Terminal$CellRun")); 717 cellRunClass = reinterpret_cast<jclass>(env->NewGlobalRef(cellRunLocal.get())); 718 cellRunDataField = env->GetFieldID(cellRunClass, "data", "[C"); 719 cellRunDataSizeField = env->GetFieldID(cellRunClass, "dataSize", "I"); 720 cellRunColSizeField = env->GetFieldID(cellRunClass, "colSize", "I"); 721 cellRunFgField = env->GetFieldID(cellRunClass, "fg", "I"); 722 cellRunBgField = env->GetFieldID(cellRunClass, "bg", "I"); 723 724 return jniRegisterNativeMethods(env, "com/android/terminal/Terminal", 725 gMethods, NELEM(gMethods)); 726 } 727 728 } /* namespace android */ 729