• 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 "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:
ScrollbackLine(dimen_t _cols)78     inline ScrollbackLine(dimen_t _cols) : cols(_cols) {
79         mCells = new VTermScreenCell[cols];
80     };
~ScrollbackLine()81     inline ~ScrollbackLine() {
82         delete mCells;
83     }
84 
copyFrom(dimen_t cols,const VTermScreenCell * cells)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 
copyTo(dimen_t cols,VTermScreenCell * cells)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 
getCell(dimen_t col,VTermScreenCell * cell)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 
term_damage(VTermRect rect,void * user)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 
term_moverect(VTermRect dest,VTermRect src,void * user)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 
term_movecursor(VTermPos pos,VTermPos oldpos,int visible,void * user)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 
term_settermprop(VTermProp prop,VTermValue * val,void * user)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 
term_setmousefunc(VTermMouseFunc func,void * data,void * user)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 
term_bell(void * user)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 
term_sb_pushline(int cols,const VTermScreenCell * cells,void * user)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 
term_sb_popline(int cols,VTermScreenCell * cells,void * user)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 
Terminal(jobject callbacks)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 
~Terminal()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 
run()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 
write(const char * bytes,size_t len)392 size_t Terminal::write(const char *bytes, size_t len) {
393     return ::write(mMasterFd, bytes, len);
394 }
395 
dispatchCharacter(int mod,int character)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 
dispatchKey(int mod,int key)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 
flushInput()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 
resize(dimen_t rows,dimen_t cols,dimen_t scrollRows)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 
onPushline(dimen_t cols,const VTermScreenCell * cells)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 
onPopline(dimen_t cols,VTermScreenCell * cells)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 
getCellLocked(VTermPos pos,VTermScreenCell * cell)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 
getRows() const533 dimen_t Terminal::getRows() const {
534     return mRows;
535 }
536 
getCols() const537 dimen_t Terminal::getCols() const {
538     return mCols;
539 }
540 
getScrollRows() const541 dimen_t Terminal::getScrollRows() const {
542     return mScrollSize;
543 }
544 
getCallbacks() const545 jobject Terminal::getCallbacks() const {
546     return mCallbacks;
547 }
548 
549 /*
550  * JNI glue
551  */
552 
com_android_terminal_Terminal_nativeInit(JNIEnv * env,jclass clazz,jobject callbacks)553 static jlong com_android_terminal_Terminal_nativeInit(JNIEnv* env, jclass clazz, jobject callbacks) {
554     return reinterpret_cast<jlong>(new Terminal(callbacks));
555 }
556 
com_android_terminal_Terminal_nativeDestroy(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeRun(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeResize(JNIEnv * env,jclass clazz,jlong ptr,jint rows,jint cols,jint scrollRows)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 
toArgb(const VTermColor & color)574 static inline int toArgb(const VTermColor& color) {
575     return (0xff << 24 | color.red << 16 | color.green << 8 | color.blue);
576 }
577 
isCellStyleEqual(const VTermScreenCell & a,const VTermScreenCell & b)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 
com_android_terminal_Terminal_nativeGetCellRun(JNIEnv * env,jclass clazz,jlong ptr,jint row,jint col,jobject run)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 
com_android_terminal_Terminal_nativeGetRows(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeGetCols(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeGetScrollRows(JNIEnv * env,jclass clazz,jlong ptr)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 
com_android_terminal_Terminal_nativeDispatchCharacter(JNIEnv * env,jclass clazz,jlong ptr,jint mod,jint c)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 
com_android_terminal_Terminal_nativeDispatchKey(JNIEnv * env,jclass clazz,jlong ptr,jint mod,jint c)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 
register_com_android_terminal_Terminal(JNIEnv * env)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