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 #if DEBUG_CALLBACKS 220 ALOGW("term_setmousefunc"); 221 #endif 222 return 1; 223 } 224 225 static int term_bell(void *user) { 226 Terminal* term = reinterpret_cast<Terminal*>(user); 227 #if DEBUG_CALLBACKS 228 ALOGW("term_bell"); 229 #endif 230 231 JNIEnv* env = AndroidRuntime::getJNIEnv(); 232 return env->CallIntMethod(term->getCallbacks(), bellMethod); 233 } 234 235 static int term_sb_pushline(int cols, const VTermScreenCell *cells, void *user) { 236 Terminal* term = reinterpret_cast<Terminal*>(user); 237 #if DEBUG_CALLBACKS 238 ALOGW("term_sb_pushline"); 239 #endif 240 241 return term->onPushline(cols, cells); 242 } 243 244 static int term_sb_popline(int cols, VTermScreenCell *cells, void *user) { 245 Terminal* term = reinterpret_cast<Terminal*>(user); 246 #if DEBUG_CALLBACKS 247 ALOGW("term_sb_popline"); 248 #endif 249 250 return term->onPopline(cols, cells); 251 } 252 253 static VTermScreenCallbacks cb = { 254 .damage = term_damage, 255 .moverect = term_moverect, 256 .movecursor = term_movecursor, 257 .settermprop = term_settermprop, 258 .setmousefunc = term_setmousefunc, 259 .bell = term_bell, 260 // Resize requests are applied immediately, so callback is ignored 261 .resize = NULL, 262 .sb_pushline = term_sb_pushline, 263 .sb_popline = term_sb_popline, 264 }; 265 266 Terminal::Terminal(jobject callbacks) : 267 mCallbacks(callbacks), mRows(25), mCols(80), mKilled(false), 268 mScrollCur(0), mScrollSize(100) { 269 JNIEnv* env = AndroidRuntime::getJNIEnv(); 270 mCallbacks = env->NewGlobalRef(callbacks); 271 272 mScroll = new ScrollbackLine*[mScrollSize]; 273 memset(mScroll, 0, sizeof(ScrollbackLine*) * mScrollSize); 274 275 /* Create VTerm */ 276 mVt = vterm_new(mRows, mCols); 277 vterm_parser_set_utf8(mVt, 1); 278 279 /* Set up screen */ 280 mVts = vterm_obtain_screen(mVt); 281 vterm_screen_enable_altscreen(mVts, 1); 282 vterm_screen_set_callbacks(mVts, &cb, this); 283 vterm_screen_set_damage_merge(mVts, VTERM_DAMAGE_SCROLL); 284 vterm_screen_reset(mVts, 1); 285 } 286 287 Terminal::~Terminal() { 288 close(mMasterFd); 289 ::kill(mChildPid, SIGHUP); 290 291 vterm_free(mVt); 292 293 delete mScroll; 294 295 JNIEnv *env = AndroidRuntime::getJNIEnv(); 296 env->DeleteGlobalRef(mCallbacks); 297 } 298 299 status_t Terminal::run() { 300 struct termios termios; 301 memset(&termios, 0, sizeof(termios)); 302 termios.c_iflag = ICRNL|IXON|IUTF8; 303 termios.c_oflag = OPOST|ONLCR|NL0|CR0|TAB0|BS0|VT0|FF0; 304 termios.c_cflag = CS8|CREAD; 305 termios.c_lflag = ISIG|ICANON|IEXTEN|ECHO|ECHOE|ECHOK; 306 307 cfsetispeed(&termios, B38400); 308 cfsetospeed(&termios, B38400); 309 310 termios.c_cc[VINTR] = 0x1f & 'C'; 311 termios.c_cc[VQUIT] = 0x1f & '\\'; 312 termios.c_cc[VERASE] = 0x7f; 313 termios.c_cc[VKILL] = 0x1f & 'U'; 314 termios.c_cc[VEOF] = 0x1f & 'D'; 315 termios.c_cc[VSTART] = 0x1f & 'Q'; 316 termios.c_cc[VSTOP] = 0x1f & 'S'; 317 termios.c_cc[VSUSP] = 0x1f & 'Z'; 318 termios.c_cc[VREPRINT] = 0x1f & 'R'; 319 termios.c_cc[VWERASE] = 0x1f & 'W'; 320 termios.c_cc[VLNEXT] = 0x1f & 'V'; 321 termios.c_cc[VMIN] = 1; 322 termios.c_cc[VTIME] = 0; 323 324 struct winsize size = { mRows, mCols, 0, 0 }; 325 326 int stderr_save_fd = dup(2); 327 if (stderr_save_fd < 0) { 328 ALOGE("failed to dup stderr - %s", strerror(errno)); 329 } 330 331 mChildPid = forkpty(&mMasterFd, NULL, &termios, &size); 332 if (mChildPid == 0) { 333 /* Restore the ISIG signals back to defaults */ 334 signal(SIGINT, SIG_DFL); 335 signal(SIGQUIT, SIG_DFL); 336 signal(SIGSTOP, SIG_DFL); 337 signal(SIGCONT, SIG_DFL); 338 339 FILE *stderr_save = fdopen(stderr_save_fd, "a"); 340 341 if (!stderr_save) { 342 ALOGE("failed to open stderr - %s", strerror(errno)); 343 } 344 345 // We know execvp(2) won't actually try to modify this. 346 char *shell = const_cast<char*>("/system/bin/sh"); 347 #if USE_TEST_SHELL 348 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}; 349 #else 350 char *args[2] = {shell, NULL}; 351 #endif 352 353 execvp(shell, args); 354 fprintf(stderr_save, "Cannot exec(%s) - %s\n", shell, strerror(errno)); 355 _exit(1); 356 } 357 358 ALOGD("entering read() loop"); 359 while (1) { 360 char buffer[4096]; 361 ssize_t bytes = ::read(mMasterFd, buffer, sizeof buffer); 362 #if DEBUG_IO 363 ALOGD("read() returned %d bytes", bytes); 364 #endif 365 366 if (mKilled) { 367 ALOGD("kill() requested"); 368 break; 369 } 370 if (bytes == 0) { 371 ALOGD("read() found EOF"); 372 break; 373 } 374 if (bytes == -1) { 375 ALOGE("read() failed: %s", strerror(errno)); 376 return 1; 377 } 378 379 { 380 Mutex::Autolock lock(mLock); 381 vterm_push_bytes(mVt, buffer, bytes); 382 vterm_screen_flush_damage(mVts); 383 } 384 } 385 386 return 0; 387 } 388 389 size_t Terminal::write(const char *bytes, size_t len) { 390 return ::write(mMasterFd, bytes, len); 391 } 392 393 bool Terminal::dispatchCharacter(int mod, int character) { 394 Mutex::Autolock lock(mLock); 395 vterm_input_push_char(mVt, static_cast<VTermModifier>(mod), character); 396 return flushInput(); 397 } 398 399 bool Terminal::dispatchKey(int mod, int key) { 400 Mutex::Autolock lock(mLock); 401 vterm_input_push_key(mVt, static_cast<VTermModifier>(mod), static_cast<VTermKey>(key)); 402 return flushInput(); 403 } 404 405 bool Terminal::flushInput() { 406 size_t len = vterm_output_get_buffer_current(mVt); 407 if (len) { 408 char buf[len]; 409 len = vterm_output_bufferread(mVt, buf, len); 410 return len == write(buf, len); 411 } 412 return true; 413 } 414 415 status_t Terminal::resize(dimen_t rows, dimen_t cols, dimen_t scrollRows) { 416 Mutex::Autolock lock(mLock); 417 418 ALOGD("resize(%d, %d, %d)", rows, cols, scrollRows); 419 420 mRows = rows; 421 mCols = cols; 422 // TODO: resize scrollback 423 424 struct winsize size = { rows, cols, 0, 0 }; 425 ioctl(mMasterFd, TIOCSWINSZ, &size); 426 427 vterm_set_size(mVt, rows, cols); 428 vterm_screen_flush_damage(mVts); 429 430 return 0; 431 } 432 433 status_t Terminal::onPushline(dimen_t cols, const VTermScreenCell* cells) { 434 ScrollbackLine* line = NULL; 435 if (mScrollCur == mScrollSize) { 436 /* Recycle old row if it's the right size */ 437 if (mScroll[mScrollCur - 1]->cols == cols) { 438 line = mScroll[mScrollCur - 1]; 439 } else { 440 delete mScroll[mScrollCur - 1]; 441 } 442 443 memmove(mScroll + 1, mScroll, sizeof(ScrollbackLine*) * (mScrollCur - 1)); 444 } else if (mScrollCur > 0) { 445 memmove(mScroll + 1, mScroll, sizeof(ScrollbackLine*) * mScrollCur); 446 } 447 448 if (line == NULL) { 449 line = new ScrollbackLine(cols); 450 } 451 452 mScroll[0] = line; 453 454 if (mScrollCur < mScrollSize) { 455 mScrollCur++; 456 } 457 458 line->copyFrom(cols, cells); 459 return 1; 460 } 461 462 status_t Terminal::onPopline(dimen_t cols, VTermScreenCell* cells) { 463 if (mScrollCur == 0) { 464 return 0; 465 } 466 467 ScrollbackLine* line = mScroll[0]; 468 mScrollCur--; 469 memmove(mScroll, mScroll + 1, sizeof(ScrollbackLine*) * mScrollCur); 470 471 dimen_t n = line->copyTo(cols, cells); 472 for (dimen_t col = n; col < cols; col++) { 473 cells[col].chars[0] = 0; 474 cells[col].width = 1; 475 } 476 477 delete line; 478 return 1; 479 } 480 481 void Terminal::getCellLocked(VTermPos pos, VTermScreenCell* cell) { 482 // The UI may be asking for cell data while the model is changing 483 // underneath it, so we always fill with meaningful data. 484 485 if (pos.row < 0) { 486 size_t scrollRow = -pos.row; 487 if (scrollRow > mScrollCur) { 488 // Invalid region above current scrollback 489 cell->width = 1; 490 #if DEBUG_SCROLLBACK 491 cell->bg.red = 255; 492 #endif 493 return; 494 } 495 496 ScrollbackLine* line = mScroll[scrollRow - 1]; 497 if ((size_t) pos.col < line->cols) { 498 // Valid scrollback cell 499 line->getCell(pos.col, cell); 500 cell->width = 1; 501 #if DEBUG_SCROLLBACK 502 cell->bg.blue = 255; 503 #endif 504 return; 505 } else { 506 // Extend last scrollback cell into invalid region 507 line->getCell(line->cols - 1, cell); 508 cell->width = 1; 509 cell->chars[0] = ' '; 510 #if DEBUG_SCROLLBACK 511 cell->bg.green = 255; 512 #endif 513 return; 514 } 515 } 516 517 if ((size_t) pos.row >= mRows) { 518 // Invalid region below screen 519 cell->width = 1; 520 #if DEBUG_SCROLLBACK 521 cell->bg.red = 128; 522 #endif 523 return; 524 } 525 526 // Valid screen cell 527 vterm_screen_get_cell(mVts, pos, cell); 528 } 529 530 dimen_t Terminal::getRows() const { 531 return mRows; 532 } 533 534 dimen_t Terminal::getCols() const { 535 return mCols; 536 } 537 538 dimen_t Terminal::getScrollRows() const { 539 return mScrollSize; 540 } 541 542 jobject Terminal::getCallbacks() const { 543 return mCallbacks; 544 } 545 546 /* 547 * JNI glue 548 */ 549 550 static jlong com_android_terminal_Terminal_nativeInit(JNIEnv* env, jclass clazz, jobject callbacks) { 551 return reinterpret_cast<jlong>(new Terminal(callbacks)); 552 } 553 554 static jint com_android_terminal_Terminal_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { 555 Terminal* term = reinterpret_cast<Terminal*>(ptr); 556 delete term; 557 return 0; 558 } 559 560 static jint com_android_terminal_Terminal_nativeRun(JNIEnv* env, jclass clazz, jlong ptr) { 561 Terminal* term = reinterpret_cast<Terminal*>(ptr); 562 return term->run(); 563 } 564 565 static jint com_android_terminal_Terminal_nativeResize(JNIEnv* env, 566 jclass clazz, jlong ptr, jint rows, jint cols, jint scrollRows) { 567 Terminal* term = reinterpret_cast<Terminal*>(ptr); 568 return term->resize(rows, cols, scrollRows); 569 } 570 571 static inline int toArgb(const VTermColor& color) { 572 return (0xff << 24 | color.red << 16 | color.green << 8 | color.blue); 573 } 574 575 static inline bool isCellStyleEqual(const VTermScreenCell& a, const VTermScreenCell& b) { 576 if (toArgb(a.fg) != toArgb(b.fg)) return false; 577 if (toArgb(a.bg) != toArgb(b.bg)) return false; 578 579 if (a.attrs.bold != b.attrs.bold) return false; 580 if (a.attrs.underline != b.attrs.underline) return false; 581 if (a.attrs.italic != b.attrs.italic) return false; 582 if (a.attrs.blink != b.attrs.blink) return false; 583 if (a.attrs.reverse != b.attrs.reverse) return false; 584 if (a.attrs.strike != b.attrs.strike) return false; 585 if (a.attrs.font != b.attrs.font) return false; 586 587 return true; 588 } 589 590 static jint com_android_terminal_Terminal_nativeGetCellRun(JNIEnv* env, 591 jclass clazz, jlong ptr, jint row, jint col, jobject run) { 592 Terminal* term = reinterpret_cast<Terminal*>(ptr); 593 Mutex::Autolock lock(term->mLock); 594 595 jcharArray dataArray = (jcharArray) env->GetObjectField(run, cellRunDataField); 596 ScopedCharArrayRW data(env, dataArray); 597 if (data.get() == NULL) { 598 return -1; 599 } 600 601 VTermScreenCell firstCell, cell; 602 603 VTermPos pos = { 604 .row = row, 605 .col = col, 606 }; 607 608 size_t dataSize = 0; 609 size_t colSize = 0; 610 while ((size_t) pos.col < term->getCols()) { 611 memset(&cell, 0, sizeof(VTermScreenCell)); 612 term->getCellLocked(pos, &cell); 613 614 if (colSize == 0) { 615 env->SetIntField(run, cellRunFgField, toArgb(cell.fg)); 616 env->SetIntField(run, cellRunBgField, toArgb(cell.bg)); 617 memcpy(&firstCell, &cell, sizeof(VTermScreenCell)); 618 } else { 619 if (!isCellStyleEqual(cell, firstCell)) { 620 break; 621 } 622 } 623 624 // Only include cell chars if they fit into run 625 uint32_t rawCell = cell.chars[0]; 626 size_t size = (rawCell < 0x10000) ? 1 : 2; 627 if (dataSize + size <= data.size()) { 628 if (rawCell < 0x10000) { 629 data[dataSize++] = rawCell; 630 } else { 631 data[dataSize++] = (((rawCell - 0x10000) >> 10) & 0x3ff) + 0xd800; 632 data[dataSize++] = ((rawCell - 0x10000) & 0x3ff) + 0xdc00; 633 } 634 635 for (int i = 1; i < cell.width; i++) { 636 data[dataSize++] = ' '; 637 } 638 639 colSize += cell.width; 640 pos.col += cell.width; 641 } else { 642 break; 643 } 644 } 645 646 env->SetIntField(run, cellRunDataSizeField, dataSize); 647 env->SetIntField(run, cellRunColSizeField, colSize); 648 649 return 0; 650 } 651 652 static jint com_android_terminal_Terminal_nativeGetRows(JNIEnv* env, jclass clazz, jlong ptr) { 653 Terminal* term = reinterpret_cast<Terminal*>(ptr); 654 return term->getRows(); 655 } 656 657 static jint com_android_terminal_Terminal_nativeGetCols(JNIEnv* env, jclass clazz, jlong ptr) { 658 Terminal* term = reinterpret_cast<Terminal*>(ptr); 659 return term->getCols(); 660 } 661 662 static jint com_android_terminal_Terminal_nativeGetScrollRows(JNIEnv* env, jclass clazz, jlong ptr) { 663 Terminal* term = reinterpret_cast<Terminal*>(ptr); 664 return term->getScrollRows(); 665 } 666 667 static jboolean com_android_terminal_Terminal_nativeDispatchCharacter(JNIEnv *env, jclass clazz, 668 jlong ptr, jint mod, jint c) { 669 Terminal* term = reinterpret_cast<Terminal*>(ptr); 670 return term->dispatchCharacter(mod, c); 671 } 672 673 static jboolean com_android_terminal_Terminal_nativeDispatchKey(JNIEnv *env, jclass clazz, 674 jlong ptr, jint mod, jint c) { 675 Terminal* term = reinterpret_cast<Terminal*>(ptr); 676 return term->dispatchKey(mod, c); 677 } 678 679 static JNINativeMethod gMethods[] = { 680 { "nativeInit", "(Lcom/android/terminal/TerminalCallbacks;)J", (void*)com_android_terminal_Terminal_nativeInit }, 681 { "nativeDestroy", "(J)I", (void*)com_android_terminal_Terminal_nativeDestroy }, 682 { "nativeRun", "(J)I", (void*)com_android_terminal_Terminal_nativeRun }, 683 { "nativeResize", "(JIII)I", (void*)com_android_terminal_Terminal_nativeResize }, 684 { "nativeGetCellRun", "(JIILcom/android/terminal/Terminal$CellRun;)I", (void*)com_android_terminal_Terminal_nativeGetCellRun }, 685 { "nativeGetRows", "(J)I", (void*)com_android_terminal_Terminal_nativeGetRows }, 686 { "nativeGetCols", "(J)I", (void*)com_android_terminal_Terminal_nativeGetCols }, 687 { "nativeGetScrollRows", "(J)I", (void*)com_android_terminal_Terminal_nativeGetScrollRows }, 688 { "nativeDispatchCharacter", "(JII)Z", (void*)com_android_terminal_Terminal_nativeDispatchCharacter}, 689 { "nativeDispatchKey", "(JII)Z", (void*)com_android_terminal_Terminal_nativeDispatchKey }, 690 }; 691 692 int register_com_android_terminal_Terminal(JNIEnv* env) { 693 ScopedLocalRef<jclass> localClass(env, 694 env->FindClass("com/android/terminal/TerminalCallbacks")); 695 696 android::terminalCallbacksClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); 697 698 android::damageMethod = env->GetMethodID(terminalCallbacksClass, "damage", "(IIII)I"); 699 android::moveRectMethod = env->GetMethodID(terminalCallbacksClass, "moveRect", "(IIIIIIII)I"); 700 android::moveCursorMethod = env->GetMethodID(terminalCallbacksClass, "moveCursor", 701 "(IIIII)I"); 702 android::setTermPropBooleanMethod = env->GetMethodID(terminalCallbacksClass, 703 "setTermPropBoolean", "(IZ)I"); 704 android::setTermPropIntMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropInt", 705 "(II)I"); 706 android::setTermPropStringMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropString", 707 "(ILjava/lang/String;)I"); 708 android::setTermPropColorMethod = env->GetMethodID(terminalCallbacksClass, "setTermPropColor", 709 "(IIII)I"); 710 android::bellMethod = env->GetMethodID(terminalCallbacksClass, "bell", "()I"); 711 712 ScopedLocalRef<jclass> cellRunLocal(env, 713 env->FindClass("com/android/terminal/Terminal$CellRun")); 714 cellRunClass = reinterpret_cast<jclass>(env->NewGlobalRef(cellRunLocal.get())); 715 cellRunDataField = env->GetFieldID(cellRunClass, "data", "[C"); 716 cellRunDataSizeField = env->GetFieldID(cellRunClass, "dataSize", "I"); 717 cellRunColSizeField = env->GetFieldID(cellRunClass, "colSize", "I"); 718 cellRunFgField = env->GetFieldID(cellRunClass, "fg", "I"); 719 cellRunBgField = env->GetFieldID(cellRunClass, "bg", "I"); 720 721 return jniRegisterNativeMethods(env, "com/android/terminal/Terminal", 722 gMethods, NELEM(gMethods)); 723 } 724 725 } /* namespace android */ 726