• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
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 #if DEBUG_CALLBACKS
220     ALOGW("term_setmousefunc");
221 #endif
222     return 1;
223 }
224 
term_bell(void * user)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 
term_sb_pushline(int cols,const VTermScreenCell * cells,void * user)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 
term_sb_popline(int cols,VTermScreenCell * cells,void * user)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 
Terminal(jobject callbacks)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 
~Terminal()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 
run()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 
write(const char * bytes,size_t len)389 size_t Terminal::write(const char *bytes, size_t len) {
390     return ::write(mMasterFd, bytes, len);
391 }
392 
dispatchCharacter(int mod,int character)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 
dispatchKey(int mod,int key)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 
flushInput()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 
resize(dimen_t rows,dimen_t cols,dimen_t scrollRows)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 
onPushline(dimen_t cols,const VTermScreenCell * cells)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 
onPopline(dimen_t cols,VTermScreenCell * cells)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 
getCellLocked(VTermPos pos,VTermScreenCell * cell)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 
getRows() const530 dimen_t Terminal::getRows() const {
531     return mRows;
532 }
533 
getCols() const534 dimen_t Terminal::getCols() const {
535     return mCols;
536 }
537 
getScrollRows() const538 dimen_t Terminal::getScrollRows() const {
539     return mScrollSize;
540 }
541 
getCallbacks() const542 jobject Terminal::getCallbacks() const {
543     return mCallbacks;
544 }
545 
546 /*
547  * JNI glue
548  */
549 
com_android_terminal_Terminal_nativeInit(JNIEnv * env,jclass clazz,jobject callbacks)550 static jlong com_android_terminal_Terminal_nativeInit(JNIEnv* env, jclass clazz, jobject callbacks) {
551     return reinterpret_cast<jlong>(new Terminal(callbacks));
552 }
553 
com_android_terminal_Terminal_nativeDestroy(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeRun(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeResize(JNIEnv * env,jclass clazz,jlong ptr,jint rows,jint cols,jint scrollRows)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 
toArgb(const VTermColor & color)571 static inline int toArgb(const VTermColor& color) {
572     return (0xff << 24 | color.red << 16 | color.green << 8 | color.blue);
573 }
574 
isCellStyleEqual(const VTermScreenCell & a,const VTermScreenCell & b)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 
com_android_terminal_Terminal_nativeGetCellRun(JNIEnv * env,jclass clazz,jlong ptr,jint row,jint col,jobject run)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 
com_android_terminal_Terminal_nativeGetRows(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeGetCols(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeGetScrollRows(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeDispatchCharacter(JNIEnv * env,jclass clazz,jlong ptr,jint mod,jint c)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 
com_android_terminal_Terminal_nativeDispatchKey(JNIEnv * env,jclass clazz,jlong ptr,jint mod,jint c)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 
register_com_android_terminal_Terminal(JNIEnv * env)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