OSDN Git Service

a0beea60547c88a291d0019a3ea041cc317d6a85
[android-x86/packages-apps-AndroidTerm.git] / src / jackpal / androidterm / EmulatorView.java
1 /*
2  * Copyright (C) 2007 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 package jackpal.androidterm;
18
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22
23 import android.content.Context;
24 import android.content.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.BitmapFactory;
27 import android.graphics.Canvas;
28 import android.graphics.ColorMatrixColorFilter;
29 import android.graphics.Paint;
30 import android.graphics.PorterDuff;
31 import android.graphics.PorterDuffXfermode;
32 import android.graphics.Rect;
33 import android.graphics.Typeface;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.text.ClipboardManager;
37 import android.util.AttributeSet;
38 import android.util.DisplayMetrics;
39 import android.util.Log;
40 import android.view.GestureDetector;
41 import android.view.KeyEvent;
42 import android.view.MotionEvent;
43 import android.view.View;
44 import android.view.inputmethod.BaseInputConnection;
45 import android.view.inputmethod.CompletionInfo;
46 import android.view.inputmethod.CorrectionInfo;
47 import android.view.inputmethod.EditorInfo;
48 import android.view.inputmethod.ExtractedText;
49 import android.view.inputmethod.ExtractedTextRequest;
50 import android.view.inputmethod.InputConnection;
51
52 import jackpal.androidterm.model.TextRenderer;
53 import jackpal.androidterm.model.UpdateCallback;
54 import jackpal.androidterm.session.TerminalEmulator;
55 import jackpal.androidterm.session.TermSession;
56 import jackpal.androidterm.session.TranscriptScreen;
57 import jackpal.androidterm.util.TermSettings;
58
59 /**
60  * A view on a transcript and a terminal emulator. Displays the text of the
61  * transcript and the current cursor position of the terminal emulator.
62  */
63 public class EmulatorView extends View implements GestureDetector.OnGestureListener {
64
65     private final String TAG = "EmulatorView";
66     private final boolean LOG_KEY_EVENTS = TermDebug.DEBUG && false;
67
68     private TermSettings mSettings;
69     private TermViewFlipper mViewFlipper;
70
71     /**
72      * We defer some initialization until we have been layed out in the view
73      * hierarchy. The boolean tracks when we know what our size is.
74      */
75     private boolean mKnownSize;
76
77     private int mVisibleWidth;
78     private int mVisibleHeight;
79     private Rect mVisibleRect = new Rect();
80
81     private TermSession mTermSession;
82
83     /**
84      * Our transcript. Contains the screen and the transcript.
85      */
86     private TranscriptScreen mTranscriptScreen;
87
88     /**
89      * Total width of each character, in pixels
90      */
91     private int mCharacterWidth;
92
93     /**
94      * Total height of each character, in pixels
95      */
96     private int mCharacterHeight;
97
98     /**
99      * Used to render text
100      */
101     private TextRenderer mTextRenderer;
102
103     /**
104      * Text size. Zero means 4 x 8 font.
105      */
106     private int mTextSize;
107
108     private int mCursorStyle;
109     private int mCursorBlink;
110
111     /**
112      * Foreground color.
113      */
114     private int mForeground;
115
116     /**
117      * Background color.
118      */
119     private int mBackground;
120
121     /**
122      * Used to paint the cursor
123      */
124     private Paint mCursorPaint;
125
126     private Paint mBackgroundPaint;
127
128     private boolean mUseCookedIme;
129
130     /**
131      * Our terminal emulator. We use this to get the current cursor position.
132      */
133     private TerminalEmulator mEmulator;
134
135     /**
136      * The number of rows of text to display.
137      */
138     private int mRows;
139
140     /**
141      * The number of columns of text to display.
142      */
143     private int mColumns;
144
145     /**
146      * The number of columns that are visible on the display.
147      */
148
149     private int mVisibleColumns;
150
151     /**
152      * The top row of text to display. Ranges from -activeTranscriptRows to 0
153      */
154     private int mTopRow;
155
156     private int mLeftColumn;
157
158     /**
159      * Used to receive data from the remote process.
160      */
161     private FileOutputStream mTermOut;
162
163     private static final int SCREEN_CHECK_PERIOD = 1000;
164     private static final int CURSOR_BLINK_PERIOD = 1000;
165
166     private boolean mCursorVisible = true;
167
168     private boolean mIsSelectingText = false;
169
170
171     private float mDensity;
172
173     private float mScaledDensity;
174     private static final int SELECT_TEXT_OFFSET_Y = -40;
175     private int mSelXAnchor = -1;
176     private int mSelYAnchor = -1;
177     private int mSelX1 = -1;
178     private int mSelY1 = -1;
179     private int mSelX2 = -1;
180     private int mSelY2 = -1;
181
182     /**
183      * Used to poll if the view has changed size. Wish there was a better way to do this.
184      */
185     private Runnable mCheckSize = new Runnable() {
186
187         public void run() {
188             updateSize(false);
189             mHandler.postDelayed(this, SCREEN_CHECK_PERIOD);
190         }
191     };
192
193     private Runnable mBlinkCursor = new Runnable() {
194         public void run() {
195             if (mCursorBlink != 0) {
196                 mCursorVisible = ! mCursorVisible;
197                 mHandler.postDelayed(this, CURSOR_BLINK_PERIOD);
198             } else {
199                 mCursorVisible = true;
200             }
201             // Perhaps just invalidate the character with the cursor.
202             invalidate();
203         }
204     };
205
206     private GestureDetector mGestureDetector;
207     private float mScrollRemainder;
208     private TermKeyListener mKeyListener;
209
210     private String mImeBuffer = "";
211
212     /**
213      * Our message handler class. Implements a periodic callback.
214      */
215     private final Handler mHandler = new Handler();
216
217     /**
218      * Called by the TermSession when the contents of the view need updating
219      */
220     private UpdateCallback mUpdateNotify = new UpdateCallback() {
221         @Override
222         public void onUpdate() {
223             if ( mIsSelectingText ) {
224                 int rowShift = mEmulator.getScrollCounter();
225                 mSelY1 -= rowShift;
226                 mSelY2 -= rowShift;
227                 mSelYAnchor -= rowShift;
228             }
229             mEmulator.clearScrollCounter();
230             ensureCursorVisible();
231             invalidate();
232         }
233     };
234
235     public UpdateCallback getUpdateCallback() {
236         return mUpdateNotify;
237     }
238
239     public EmulatorView(Context context, TermSession session, TermViewFlipper viewFlipper, DisplayMetrics metrics) {
240         super(context);
241         commonConstructor(session, viewFlipper);
242         setDensity(metrics);
243     }
244
245     public void setDensity(DisplayMetrics metrics) {
246         mDensity = metrics.density;
247         mScaledDensity = metrics.scaledDensity;
248     }
249
250     public void onResume() {
251         updateSize(false);
252         mHandler.postDelayed(mCheckSize, SCREEN_CHECK_PERIOD);
253         if (mCursorBlink != 0) {
254             mHandler.postDelayed(mBlinkCursor, CURSOR_BLINK_PERIOD);
255         }
256     }
257
258     public void onPause() {
259         mHandler.removeCallbacks(mCheckSize);
260         if (mCursorBlink != 0) {
261             mHandler.removeCallbacks(mBlinkCursor);
262         }
263     }
264
265     public void updatePrefs(TermSettings settings) {
266         mSettings = settings;
267         setTextSize((int) (mSettings.getFontSize() * mDensity));
268         setCursorStyle(mSettings.getCursorStyle(), mSettings.getCursorBlink());
269         setUseCookedIME(mSettings.useCookedIME());
270         setColors();
271     }
272
273     public void setColors() {
274         int[] scheme = mSettings.getColorScheme();
275         mForeground = scheme[0];
276         mBackground = scheme[1];
277         updateText();
278     }
279
280     public void resetTerminal() {
281         mEmulator.reset();
282         invalidate();
283     }
284
285     @Override
286     public boolean onCheckIsTextEditor() {
287         return true;
288     }
289
290     @Override
291     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
292         outAttrs.inputType = mUseCookedIme ?
293                 EditorInfo.TYPE_CLASS_TEXT :
294                 EditorInfo.TYPE_NULL;
295         return new InputConnection() {
296             private boolean mInBatchEdit;
297             /**
298              * Used to handle composing text requests
299              */
300             private int mCursor;
301             private int mComposingTextStart;
302             private int mComposingTextEnd;
303             private int mSelectedTextStart;
304             private int mSelectedTextEnd;
305
306             private void sendChar(int c) {
307                 try {
308                     mapAndSend(c);
309                 } catch (IOException ex) {
310
311                 }
312             }
313
314             private void sendText(CharSequence text) {
315                 int n = text.length();
316                 try {
317                     for(int i = 0; i < n; i++) {
318                         char c = text.charAt(i);
319                         mapAndSend(c);
320                     }
321                     mTermOut.flush();
322                 } catch (IOException e) {
323                     Log.e(TAG, "error writing ", e);
324                 }
325             }
326
327             private void mapAndSend(int c) throws IOException {
328                 int result = mKeyListener.mapControlChar(c);
329                 if (result < TermKeyListener.KEYCODE_OFFSET) {
330                     mTermOut.write(result);
331                 } else {
332                     mKeyListener.handleKeyCode(result - TermKeyListener.KEYCODE_OFFSET, mTermOut, getKeypadApplicationMode());
333                 }
334             }
335
336             public boolean beginBatchEdit() {
337                 if (TermDebug.LOG_IME) {
338                     Log.w(TAG, "beginBatchEdit");
339                 }
340                 setImeBuffer("");
341                 mCursor = 0;
342                 mComposingTextStart = 0;
343                 mComposingTextEnd = 0;
344                 mInBatchEdit = true;
345                 return true;
346             }
347
348             public boolean clearMetaKeyStates(int arg0) {
349                 if (TermDebug.LOG_IME) {
350                     Log.w(TAG, "clearMetaKeyStates " + arg0);
351                 }
352                 return false;
353             }
354
355             public boolean commitCompletion(CompletionInfo arg0) {
356                 if (TermDebug.LOG_IME) {
357                     Log.w(TAG, "commitCompletion " + arg0);
358                 }
359                 return false;
360             }
361
362             public boolean endBatchEdit() {
363                 if (TermDebug.LOG_IME) {
364                     Log.w(TAG, "endBatchEdit");
365                 }
366                 mInBatchEdit = false;
367                 return true;
368             }
369
370             public boolean finishComposingText() {
371                 if (TermDebug.LOG_IME) {
372                     Log.w(TAG, "finishComposingText");
373                 }
374                 sendText(mImeBuffer);
375                 setImeBuffer("");
376                 mComposingTextStart = 0;
377                 mComposingTextEnd = 0;
378                 mCursor = 0;
379                 return true;
380             }
381
382             public int getCursorCapsMode(int arg0) {
383                 if (TermDebug.LOG_IME) {
384                     Log.w(TAG, "getCursorCapsMode(" + arg0 + ")");
385                 }
386                 return 0;
387             }
388
389             public ExtractedText getExtractedText(ExtractedTextRequest arg0,
390                     int arg1) {
391                 if (TermDebug.LOG_IME) {
392                     Log.w(TAG, "getExtractedText" + arg0 + "," + arg1);
393                 }
394                 return null;
395             }
396
397             public CharSequence getTextAfterCursor(int n, int flags) {
398                 if (TermDebug.LOG_IME) {
399                     Log.w(TAG, "getTextAfterCursor(" + n + "," + flags + ")");
400                 }
401                 int len = Math.min(n, mImeBuffer.length() - mCursor);
402                 if (len <= 0 || mCursor < 0 || mCursor >= mImeBuffer.length()) {
403                     return "";
404                 }
405                 return mImeBuffer.substring(mCursor, mCursor + len);
406             }
407
408             public CharSequence getTextBeforeCursor(int n, int flags) {
409                 if (TermDebug.LOG_IME) {
410                     Log.w(TAG, "getTextBeforeCursor(" + n + "," + flags + ")");
411                 }
412                 int len = Math.min(n, mCursor);
413                 if (len <= 0 || mCursor < 0 || mCursor >= mImeBuffer.length()) {
414                     return "";
415                 }
416                 return mImeBuffer.substring(mCursor-len, mCursor);
417             }
418
419             public boolean performContextMenuAction(int arg0) {
420                 if (TermDebug.LOG_IME) {
421                     Log.w(TAG, "performContextMenuAction" + arg0);
422                 }
423                 return true;
424             }
425
426             public boolean performPrivateCommand(String arg0, Bundle arg1) {
427                 if (TermDebug.LOG_IME) {
428                     Log.w(TAG, "performPrivateCommand" + arg0 + "," + arg1);
429                 }
430                 return true;
431             }
432
433             public boolean reportFullscreenMode(boolean arg0) {
434                 if (TermDebug.LOG_IME) {
435                     Log.w(TAG, "reportFullscreenMode" + arg0);
436                 }
437                 return true;
438             }
439
440             public boolean commitCorrection (CorrectionInfo correctionInfo) {
441                 if (TermDebug.LOG_IME) {
442                     Log.w(TAG, "commitCorrection");
443                 }
444                 return true;
445             }
446
447             public boolean commitText(CharSequence text, int newCursorPosition) {
448                 if (TermDebug.LOG_IME) {
449                     Log.w(TAG, "commitText(\"" + text + "\", " + newCursorPosition + ")");
450                 }
451                 clearComposingText();
452                 sendText(text);
453                 setImeBuffer("");
454                 mCursor = 0;
455                 return true;
456             }
457
458             private void clearComposingText() {
459                 setImeBuffer(mImeBuffer.substring(0, mComposingTextStart) +
460                     mImeBuffer.substring(mComposingTextEnd));
461                 if (mCursor < mComposingTextStart) {
462                     // do nothing
463                 } else if (mCursor < mComposingTextEnd) {
464                     mCursor = mComposingTextStart;
465                 } else {
466                     mCursor -= mComposingTextEnd - mComposingTextStart;
467                 }
468                 mComposingTextEnd = mComposingTextStart = 0;
469             }
470
471             public boolean deleteSurroundingText(int leftLength, int rightLength) {
472                 if (TermDebug.LOG_IME) {
473                     Log.w(TAG, "deleteSurroundingText(" + leftLength +
474                             "," + rightLength + ")");
475                 }
476                 if (leftLength > 0) {
477                     for (int i = 0; i < leftLength; i++) {
478                         sendKeyEvent(
479                             new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
480                     }
481                 } else if ((leftLength == 0) && (rightLength == 0)) {
482                     // Delete key held down / repeating
483                     sendKeyEvent(
484                         new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
485                 }
486                 // TODO: handle forward deletes.
487                 return true;
488             }
489
490             public boolean performEditorAction(int actionCode) {
491                 if (TermDebug.LOG_IME) {
492                     Log.w(TAG, "performEditorAction(" + actionCode + ")");
493                 }
494                 if (actionCode == EditorInfo.IME_ACTION_UNSPECIFIED) {
495                     // The "return" key has been pressed on the IME.
496                     sendText("\n");
497                 }
498                 return true;
499             }
500
501             public boolean sendKeyEvent(KeyEvent event) {
502                 if (TermDebug.LOG_IME) {
503                     Log.w(TAG, "sendKeyEvent(" + event + ")");
504                 }
505                 // Some keys are sent here rather than to commitText.
506                 // In particular, del and the digit keys are sent here.
507                 // (And I have reports that the HTC Magic also sends Return here.)
508                 // As a bit of defensive programming, handle every key.
509                 dispatchKeyEvent(event);
510                 return true;
511             }
512
513             public boolean setComposingText(CharSequence text, int newCursorPosition) {
514                 if (TermDebug.LOG_IME) {
515                     Log.w(TAG, "setComposingText(\"" + text + "\", " + newCursorPosition + ")");
516                 }
517                 setImeBuffer(mImeBuffer.substring(0, mComposingTextStart) +
518                     text + mImeBuffer.substring(mComposingTextEnd));
519                 mComposingTextEnd = mComposingTextStart + text.length();
520                 mCursor = newCursorPosition > 0 ? mComposingTextEnd + newCursorPosition - 1
521                         : mComposingTextStart - newCursorPosition;
522                 return true;
523             }
524
525             public boolean setSelection(int start, int end) {
526                 if (TermDebug.LOG_IME) {
527                     Log.w(TAG, "setSelection" + start + "," + end);
528                 }
529                 int length = mImeBuffer.length();
530                 if (start == end && start > 0 && start < length) {
531                     mSelectedTextStart = mSelectedTextEnd = 0;
532                     mCursor = start;
533                 } else if (start < end && start > 0 && end < length) {
534                     mSelectedTextStart = start;
535                     mSelectedTextEnd = end;
536                     mCursor = start;
537                 }
538                 return true;
539             }
540
541             public boolean setComposingRegion(int start, int end) {
542                 if (TermDebug.LOG_IME) {
543                     Log.w(TAG, "setComposingRegion " + start + "," + end);
544                 }
545                 if (start < end && start > 0 && end < mImeBuffer.length()) {
546                     clearComposingText();
547                     mComposingTextStart = start;
548                     mComposingTextEnd = end;
549                 }
550                 return true;
551             }
552
553             public CharSequence getSelectedText(int flags) {
554                 if (TermDebug.LOG_IME) {
555                     Log.w(TAG, "getSelectedText " + flags);
556                 }
557                 return mImeBuffer.substring(mSelectedTextStart, mSelectedTextEnd+1);
558             }
559
560         };
561     }
562
563     private void setImeBuffer(String buffer) {
564         if (!buffer.equals(mImeBuffer)) {
565             invalidate();
566         }
567         mImeBuffer = buffer;
568     }
569
570     public boolean getKeypadApplicationMode() {
571         return mEmulator.getKeypadApplicationMode();
572     }
573
574     private void commonConstructor(TermSession session, TermViewFlipper viewFlipper) {
575         mTextRenderer = null;
576         mCursorPaint = new Paint();
577         mCursorPaint.setARGB(255,128,128,128);
578         mBackgroundPaint = new Paint();
579         mTopRow = 0;
580         mLeftColumn = 0;
581         mGestureDetector = new GestureDetector(this);
582         // mGestureDetector.setIsLongpressEnabled(false);
583         setVerticalScrollBarEnabled(true);
584         setFocusable(true);
585         setFocusableInTouchMode(true);
586
587         initialize(session, viewFlipper);
588     }
589
590     @Override
591     protected int computeVerticalScrollRange() {
592         return mTranscriptScreen.getActiveRows();
593     }
594
595     @Override
596     protected int computeVerticalScrollExtent() {
597         return mRows;
598     }
599
600     @Override
601     protected int computeVerticalScrollOffset() {
602         return mTranscriptScreen.getActiveRows() + mTopRow - mRows;
603     }
604
605     /**
606      * Call this to initialize the view.
607      *
608      * @param session The terminal session this view will be displaying
609      */
610     private void initialize(TermSession session, TermViewFlipper viewFlipper) {
611         mTermSession = session;
612         mTranscriptScreen = session.getTranscriptScreen();
613         mEmulator = session.getEmulator();
614         mTermOut = session.getTermOut();
615
616         mViewFlipper = viewFlipper;
617
618         mKeyListener = new TermKeyListener();
619         mTextSize = 10;
620         mForeground = TermSettings.WHITE;
621         mBackground = TermSettings.BLACK;
622         updateText();
623
624         requestFocus();
625     }
626
627     public TermSession getTermSession() {
628         return mTermSession;
629     }
630
631     /**
632      * Page the terminal view (scroll it up or down by delta screenfulls.)
633      *
634      * @param delta the number of screens to scroll. Positive means scroll down,
635      *        negative means scroll up.
636      */
637     public void page(int delta) {
638         mTopRow =
639                 Math.min(0, Math.max(-(mTranscriptScreen
640                         .getActiveTranscriptRows()), mTopRow + mRows * delta));
641         invalidate();
642     }
643
644     /**
645      * Page the terminal view horizontally.
646      *
647      * @param deltaColumns the number of columns to scroll. Positive scrolls to
648      *        the right.
649      */
650     public void pageHorizontal(int deltaColumns) {
651         mLeftColumn =
652                 Math.max(0, Math.min(mLeftColumn + deltaColumns, mColumns
653                         - mVisibleColumns));
654         invalidate();
655     }
656
657     /**
658      * Sets the text size, which in turn sets the number of rows and columns
659      *
660      * @param fontSize the new font size, in pixels.
661      */
662     public void setTextSize(int fontSize) {
663         mTextSize = fontSize;
664         updateText();
665     }
666
667     public void setCursorStyle(int style, int blink) {
668         mCursorStyle = style;
669         if (blink != 0 && mCursorBlink == 0) {
670             mHandler.postDelayed(mBlinkCursor, CURSOR_BLINK_PERIOD);
671         } else if (blink == 0 && mCursorBlink != 0) {
672             mHandler.removeCallbacks(mBlinkCursor);
673         }
674         mCursorBlink = blink;
675     }
676
677     public void setUseCookedIME(boolean useCookedIME) {
678         mUseCookedIme = useCookedIME;
679     }
680
681     // Begin GestureDetector.OnGestureListener methods
682
683     public boolean onSingleTapUp(MotionEvent e) {
684         return true;
685     }
686
687     public void onLongPress(MotionEvent e) {
688         showContextMenu();
689     }
690
691     public boolean onScroll(MotionEvent e1, MotionEvent e2,
692             float distanceX, float distanceY) {
693         distanceY += mScrollRemainder;
694         int deltaRows = (int) (distanceY / mCharacterHeight);
695         mScrollRemainder = distanceY - deltaRows * mCharacterHeight;
696         mTopRow =
697             Math.min(0, Math.max(-(mTranscriptScreen
698                     .getActiveTranscriptRows()), mTopRow + deltaRows));
699         invalidate();
700
701         return true;
702    }
703
704     public void onSingleTapConfirmed(MotionEvent e) {
705     }
706
707     public boolean onJumpTapDown(MotionEvent e1, MotionEvent e2) {
708        // Scroll to bottom
709        mTopRow = 0;
710        invalidate();
711        return true;
712     }
713
714     public boolean onJumpTapUp(MotionEvent e1, MotionEvent e2) {
715         // Scroll to top
716         mTopRow = -mTranscriptScreen.getActiveTranscriptRows();
717         invalidate();
718         return true;
719     }
720
721     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
722             float velocityY) {
723         if (Math.abs(velocityX) > Math.abs(velocityY)) {
724             // Assume user wanted side to side movement
725             if (velocityX > 0) {
726                 // Left to right swipe -- previous window
727                 mViewFlipper.showPrevious();
728             } else {
729                 // Right to left swipe -- next window
730                 mViewFlipper.showNext();
731             }
732         } else {
733             // TODO: add animation man's (non animated) fling
734             mScrollRemainder = 0.0f;
735             onScroll(e1, e2, 2 * velocityX, -2 * velocityY);
736         }
737         return true;
738     }
739
740     public void onShowPress(MotionEvent e) {
741     }
742
743     public boolean onDown(MotionEvent e) {
744         mScrollRemainder = 0.0f;
745         return true;
746     }
747
748     // End GestureDetector.OnGestureListener methods
749
750     @Override public boolean onTouchEvent(MotionEvent ev) {
751         if (mIsSelectingText) {
752             return onTouchEventWhileSelectingText(ev);
753         } else {
754             return mGestureDetector.onTouchEvent(ev);
755         }
756     }
757
758     private boolean onTouchEventWhileSelectingText(MotionEvent ev) {
759         int action = ev.getAction();
760         int cx = (int)(ev.getX() / mCharacterWidth);
761         int cy = Math.max(0,
762                 (int)((ev.getY() + SELECT_TEXT_OFFSET_Y * mScaledDensity)
763                         / mCharacterHeight) + mTopRow);
764         switch (action) {
765         case MotionEvent.ACTION_DOWN:
766             mSelXAnchor = cx;
767             mSelYAnchor = cy;
768             mSelX1 = cx;
769             mSelY1 = cy;
770             mSelX2 = mSelX1;
771             mSelY2 = mSelY1;
772             break;
773         case MotionEvent.ACTION_MOVE:
774         case MotionEvent.ACTION_UP:
775             int minx = Math.min(mSelXAnchor, cx);
776             int maxx = Math.max(mSelXAnchor, cx);
777             int miny = Math.min(mSelYAnchor, cy);
778             int maxy = Math.max(mSelYAnchor, cy);
779             mSelX1 = minx;
780             mSelY1 = miny;
781             mSelX2 = maxx;
782             mSelY2 = maxy;
783             if (action == MotionEvent.ACTION_UP) {
784                 ClipboardManager clip = (ClipboardManager)
785                      getContext().getApplicationContext()
786                          .getSystemService(Context.CLIPBOARD_SERVICE);
787                 clip.setText(getSelectedText().trim());
788                 toggleSelectingText();
789             }
790             invalidate();
791             break;
792         default:
793             toggleSelectingText();
794             invalidate();
795             break;
796         }
797         return true;
798     }
799
800     @Override
801     public boolean onKeyDown(int keyCode, KeyEvent event) {
802         if (LOG_KEY_EVENTS) {
803             Log.w(TAG, "onKeyDown " + keyCode);
804         }
805         if (handleControlKey(keyCode, true)) {
806             return true;
807         } else if (handleFnKey(keyCode, true)) {
808             return true;
809         } else if (isSystemKey(keyCode, event)) {
810             // Don't intercept the system keys
811             return super.onKeyDown(keyCode, event);
812         }
813
814         // Translate the keyCode into an ASCII character.
815
816         try {
817             mKeyListener.keyDown(keyCode, event, mTermOut,
818                     getKeypadApplicationMode());
819         } catch (IOException e) {
820             // Ignore I/O exceptions
821         }
822         return true;
823     }
824
825     @Override
826     public boolean onKeyUp(int keyCode, KeyEvent event) {
827         if (LOG_KEY_EVENTS) {
828             Log.w(TAG, "onKeyUp " + keyCode);
829         }
830         if (handleControlKey(keyCode, false)) {
831             return true;
832         } else if (handleFnKey(keyCode, false)) {
833             return true;
834         } else if (isSystemKey(keyCode, event)) {
835             // Don't intercept the system keys
836             return super.onKeyUp(keyCode, event);
837         }
838
839         mKeyListener.keyUp(keyCode);
840         return true;
841     }
842
843
844     private boolean handleControlKey(int keyCode, boolean down) {
845         if (keyCode == mSettings.getControlKeyCode()) {
846             if (LOG_KEY_EVENTS) {
847                 Log.w(TAG, "handleControlKey " + keyCode);
848             }
849             mKeyListener.handleControlKey(down);
850             return true;
851         }
852         return false;
853     }
854
855     private boolean handleFnKey(int keyCode, boolean down) {
856         if (keyCode == mSettings.getFnKeyCode()) {
857             if (LOG_KEY_EVENTS) {
858                 Log.w(TAG, "handleFnKey " + keyCode);
859             }
860             mKeyListener.handleFnKey(down);
861             return true;
862         }
863         return false;
864     }
865
866     private boolean isSystemKey(int keyCode, KeyEvent event) {
867         return event.isSystem();
868     }
869
870     private void updateText() {
871         if (mTextSize > 0) {
872             mTextRenderer = new PaintRenderer(mTextSize, mForeground,
873                     mBackground);
874         }
875         else {
876             mTextRenderer = new Bitmap4x8FontRenderer(getResources(),
877                     mForeground, mBackground);
878         }
879         mBackgroundPaint.setColor(mBackground);
880         mCharacterWidth = mTextRenderer.getCharacterWidth();
881         mCharacterHeight = mTextRenderer.getCharacterHeight();
882
883         updateSize(true);
884     }
885
886     @Override
887     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
888         boolean oldKnownSize = mKnownSize;
889         if (!mKnownSize) {
890             mKnownSize = true;
891         }
892         updateSize(false);
893     }
894
895     private void updateSize(int w, int h) {
896         mColumns = Math.max(1, w / mCharacterWidth);
897         mRows = Math.max(1, h / mCharacterHeight);
898         mVisibleColumns = mVisibleWidth / mCharacterWidth;
899
900         mTermSession.updateSize(mColumns, mRows);
901
902         // Reset our paging:
903         mTopRow = 0;
904         mLeftColumn = 0;
905
906         invalidate();
907     }
908
909     public void updateSize(boolean force) {
910         if (mKnownSize) {
911             getWindowVisibleDisplayFrame(mVisibleRect);
912             int w = mVisibleRect.width();
913             int h = mVisibleRect.height();
914             // Log.w("Term", "(" + w + ", " + h + ")");
915             if (force || w != mVisibleWidth || h != mVisibleHeight) {
916                 mVisibleWidth = w;
917                 mVisibleHeight = h;
918                 updateSize(mVisibleWidth, mVisibleHeight);
919             }
920         }
921     }
922
923     @Override
924     protected void onDraw(Canvas canvas) {
925         updateSize(false);
926         int w = getWidth();
927         int h = getHeight();
928         canvas.drawRect(0, 0, w, h, mBackgroundPaint);
929         float x = -mLeftColumn * mCharacterWidth;
930         float y = mCharacterHeight;
931         int endLine = mTopRow + mRows;
932         int cx = mEmulator.getCursorCol();
933         int cy = mEmulator.getCursorRow();
934         for (int i = mTopRow; i < endLine; i++) {
935             int cursorX = -1;
936             if (i == cy && mCursorVisible) {
937                 cursorX = cx;
938             }
939             int selx1 = -1;
940             int selx2 = -1;
941             if ( i >= mSelY1 && i <= mSelY2 ) {
942                 if ( i == mSelY1 ) {
943                     selx1 = mSelX1;
944                 }
945                 if ( i == mSelY2 ) {
946                     selx2 = mSelX2;
947                 } else {
948                     selx2 = mColumns;
949                 }
950             }
951             mTranscriptScreen.drawText(i, canvas, x, y, mTextRenderer, cursorX, selx1, selx2, mImeBuffer);
952             y += mCharacterHeight;
953         }
954     }
955
956     private void ensureCursorVisible() {
957         mTopRow = 0;
958         if (mVisibleColumns > 0) {
959             int cx = mEmulator.getCursorCol();
960             int visibleCursorX = mEmulator.getCursorCol() - mLeftColumn;
961             if (visibleCursorX < 0) {
962                 mLeftColumn = cx;
963             } else if (visibleCursorX >= mVisibleColumns) {
964                 mLeftColumn = (cx - mVisibleColumns) + 1;
965             }
966         }
967     }
968
969     public void toggleSelectingText() {
970         mIsSelectingText = ! mIsSelectingText;
971         setVerticalScrollBarEnabled( ! mIsSelectingText );
972         if ( ! mIsSelectingText ) {
973             mSelX1 = -1;
974             mSelY1 = -1;
975             mSelX2 = -1;
976             mSelY2 = -1;
977         }
978     }
979
980     public boolean getSelectingText() {
981         return mIsSelectingText;
982     }
983
984     public String getSelectedText() {
985         return mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2);
986     }
987 }
988
989 abstract class BaseTextRenderer implements TextRenderer {
990     protected int[] mForePaint = {
991             0xff000000, // Black
992             0xffff0000, // Red
993             0xff00ff00, // green
994             0xffffff00, // yellow
995             0xff0000ff, // blue
996             0xffff00ff, // magenta
997             0xff00ffff, // cyan
998             0xffffffff  // white -- is overridden by constructor
999     };
1000     protected int[] mBackPaint = {
1001             0xff000000, // Black -- is overridden by constructor
1002             0xffcc0000, // Red
1003             0xff00cc00, // green
1004             0xffcccc00, // yellow
1005             0xff0000cc, // blue
1006             0xffff00cc, // magenta
1007             0xff00cccc, // cyan
1008             0xffffffff  // white
1009     };
1010     protected final static int mCursorPaint = 0xff808080;
1011
1012     public BaseTextRenderer(int forePaintColor, int backPaintColor) {
1013         mForePaint[7] = forePaintColor;
1014         mBackPaint[0] = backPaintColor;
1015
1016     }
1017 }
1018
1019 class Bitmap4x8FontRenderer extends BaseTextRenderer {
1020     private final static int kCharacterWidth = 4;
1021     private final static int kCharacterHeight = 8;
1022     private Bitmap mFont;
1023     private int mCurrentForeColor;
1024     private int mCurrentBackColor;
1025     private float[] mColorMatrix;
1026     private Paint mPaint;
1027     private static final float BYTE_SCALE = 1.0f / 255.0f;
1028
1029     public Bitmap4x8FontRenderer(Resources resources,
1030             int forePaintColor, int backPaintColor) {
1031         super(forePaintColor, backPaintColor);
1032         mFont = BitmapFactory.decodeResource(resources,
1033                 R.drawable.atari_small);
1034         mPaint = new Paint();
1035         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
1036     }
1037
1038     public int getCharacterWidth() {
1039         return kCharacterWidth;
1040     }
1041
1042     public int getCharacterHeight() {
1043         return kCharacterHeight;
1044     }
1045
1046     public void drawTextRun(Canvas canvas, float x, float y,
1047             int lineOffset, char[] text, int index, int count,
1048             boolean cursor, int foreColor, int backColor) {
1049         setColorMatrix(mForePaint[foreColor & 7],
1050                 cursor ? mCursorPaint : mBackPaint[backColor & 7]);
1051         int destX = (int) x + kCharacterWidth * lineOffset;
1052         int destY = (int) y;
1053         Rect srcRect = new Rect();
1054         Rect destRect = new Rect();
1055         destRect.top = (destY - kCharacterHeight);
1056         destRect.bottom = destY;
1057         for(int i = 0; i < count; i++) {
1058             char c = text[i + index];
1059             if ((cursor || (c != 32)) && (c < 128)) {
1060                 int cellX = c & 31;
1061                 int cellY = (c >> 5) & 3;
1062                 int srcX = cellX * kCharacterWidth;
1063                 int srcY = cellY * kCharacterHeight;
1064                 srcRect.set(srcX, srcY,
1065                         srcX + kCharacterWidth, srcY + kCharacterHeight);
1066                 destRect.left = destX;
1067                 destRect.right = destX + kCharacterWidth;
1068                 canvas.drawBitmap(mFont, srcRect, destRect, mPaint);
1069             }
1070             destX += kCharacterWidth;
1071         }
1072     }
1073
1074     private void setColorMatrix(int foreColor, int backColor) {
1075         if ((foreColor != mCurrentForeColor)
1076                 || (backColor != mCurrentBackColor)
1077                 || (mColorMatrix == null)) {
1078             mCurrentForeColor = foreColor;
1079             mCurrentBackColor = backColor;
1080             if (mColorMatrix == null) {
1081                 mColorMatrix = new float[20];
1082                 mColorMatrix[18] = 1.0f; // Just copy Alpha
1083             }
1084             for (int component = 0; component < 3; component++) {
1085                 int rightShift = (2 - component) << 3;
1086                 int fore = 0xff & (foreColor >> rightShift);
1087                 int back = 0xff & (backColor >> rightShift);
1088                 int delta = back - fore;
1089                 mColorMatrix[component * 6] = delta * BYTE_SCALE;
1090                 mColorMatrix[component * 5 + 4] = fore;
1091             }
1092             mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
1093         }
1094     }
1095 }
1096
1097 class PaintRenderer extends BaseTextRenderer {
1098     public PaintRenderer(int fontSize, int forePaintColor, int backPaintColor) {
1099         super(forePaintColor, backPaintColor);
1100         mTextPaint = new Paint();
1101         mTextPaint.setTypeface(Typeface.MONOSPACE);
1102         mTextPaint.setAntiAlias(true);
1103         mTextPaint.setTextSize(fontSize);
1104
1105         mCharHeight = (int) Math.ceil(mTextPaint.getFontSpacing());
1106         mCharAscent = (int) Math.ceil(mTextPaint.ascent());
1107         mCharDescent = mCharHeight + mCharAscent;
1108         mCharWidth = (int) mTextPaint.measureText(EXAMPLE_CHAR, 0, 1);
1109     }
1110
1111     public void drawTextRun(Canvas canvas, float x, float y, int lineOffset,
1112             char[] text, int index, int count,
1113             boolean cursor, int foreColor, int backColor) {
1114         if (cursor) {
1115             mTextPaint.setColor(mCursorPaint);
1116         } else {
1117             mTextPaint.setColor(mBackPaint[backColor & 0x7]);
1118         }
1119         float left = x + lineOffset * mCharWidth;
1120         canvas.drawRect(left, y + mCharAscent,
1121                 left + count * mCharWidth, y + mCharDescent,
1122                 mTextPaint);
1123         boolean bold = ( foreColor & 0x8 ) != 0;
1124         boolean underline = (backColor & 0x8) != 0;
1125         if (bold) {
1126             mTextPaint.setFakeBoldText(true);
1127         }
1128         if (underline) {
1129             mTextPaint.setUnderlineText(true);
1130         }
1131         mTextPaint.setColor(mForePaint[foreColor & 0x7]);
1132         canvas.drawText(text, index, count, left, y, mTextPaint);
1133         if (bold) {
1134             mTextPaint.setFakeBoldText(false);
1135         }
1136         if (underline) {
1137             mTextPaint.setUnderlineText(false);
1138         }
1139     }
1140
1141     public int getCharacterHeight() {
1142         return mCharHeight;
1143     }
1144
1145     public int getCharacterWidth() {
1146         return mCharWidth;
1147     }
1148
1149
1150     private Paint mTextPaint;
1151     private int mCharWidth;
1152     private int mCharHeight;
1153     private int mCharAscent;
1154     private int mCharDescent;
1155     private static final char[] EXAMPLE_CHAR = {'X'};
1156     }
1157
1158 /**
1159  * An ASCII key listener. Supports control characters and escape. Keeps track of
1160  * the current state of the alt, shift, and control keys.
1161  */
1162 class TermKeyListener {
1163     /**
1164      * Android key codes that are defined in the Android 2.3 API.
1165      * We want to recognize these codes, because they will be sent to our
1166      * app when we run on Android 2.3 systems.
1167      * But we don't want to accidentally use 2.3-specific APIs.
1168      * So we compile against the Android 1.6 APIs, and have a copy of the codes here.
1169      */
1170
1171     /** Key code constant: Unknown key code. */
1172     public static final int KEYCODE_UNKNOWN         = 0;
1173     /** Key code constant: Soft Left key.
1174      * Usually situated below the display on phones and used as a multi-function
1175      * feature key for selecting a software defined function shown on the bottom left
1176      * of the display. */
1177     public static final int KEYCODE_SOFT_LEFT       = 1;
1178     /** Key code constant: Soft Right key.
1179      * Usually situated below the display on phones and used as a multi-function
1180      * feature key for selecting a software defined function shown on the bottom right
1181      * of the display. */
1182     public static final int KEYCODE_SOFT_RIGHT      = 2;
1183     /** Key code constant: Home key.
1184      * This key is handled by the framework and is never delivered to applications. */
1185     public static final int KEYCODE_HOME            = 3;
1186     /** Key code constant: Back key. */
1187     public static final int KEYCODE_BACK            = 4;
1188     /** Key code constant: Call key. */
1189     public static final int KEYCODE_CALL            = 5;
1190     /** Key code constant: End Call key. */
1191     public static final int KEYCODE_ENDCALL         = 6;
1192     /** Key code constant: '0' key. */
1193     public static final int KEYCODE_0               = 7;
1194     /** Key code constant: '1' key. */
1195     public static final int KEYCODE_1               = 8;
1196     /** Key code constant: '2' key. */
1197     public static final int KEYCODE_2               = 9;
1198     /** Key code constant: '3' key. */
1199     public static final int KEYCODE_3               = 10;
1200     /** Key code constant: '4' key. */
1201     public static final int KEYCODE_4               = 11;
1202     /** Key code constant: '5' key. */
1203     public static final int KEYCODE_5               = 12;
1204     /** Key code constant: '6' key. */
1205     public static final int KEYCODE_6               = 13;
1206     /** Key code constant: '7' key. */
1207     public static final int KEYCODE_7               = 14;
1208     /** Key code constant: '8' key. */
1209     public static final int KEYCODE_8               = 15;
1210     /** Key code constant: '9' key. */
1211     public static final int KEYCODE_9               = 16;
1212     /** Key code constant: '*' key. */
1213     public static final int KEYCODE_STAR            = 17;
1214     /** Key code constant: '#' key. */
1215     public static final int KEYCODE_POUND           = 18;
1216     /** Key code constant: Directional Pad Up key.
1217      * May also be synthesized from trackball motions. */
1218     public static final int KEYCODE_DPAD_UP         = 19;
1219     /** Key code constant: Directional Pad Down key.
1220      * May also be synthesized from trackball motions. */
1221     public static final int KEYCODE_DPAD_DOWN       = 20;
1222     /** Key code constant: Directional Pad Left key.
1223      * May also be synthesized from trackball motions. */
1224     public static final int KEYCODE_DPAD_LEFT       = 21;
1225     /** Key code constant: Directional Pad Right key.
1226      * May also be synthesized from trackball motions. */
1227     public static final int KEYCODE_DPAD_RIGHT      = 22;
1228     /** Key code constant: Directional Pad Center key.
1229      * May also be synthesized from trackball motions. */
1230     public static final int KEYCODE_DPAD_CENTER     = 23;
1231     /** Key code constant: Volume Up key.
1232      * Adjusts the speaker volume up. */
1233     public static final int KEYCODE_VOLUME_UP       = 24;
1234     /** Key code constant: Volume Down key.
1235      * Adjusts the speaker volume down. */
1236     public static final int KEYCODE_VOLUME_DOWN     = 25;
1237     /** Key code constant: Power key. */
1238     public static final int KEYCODE_POWER           = 26;
1239     /** Key code constant: Camera key.
1240      * Used to launch a camera application or take pictures. */
1241     public static final int KEYCODE_CAMERA          = 27;
1242     /** Key code constant: Clear key. */
1243     public static final int KEYCODE_CLEAR           = 28;
1244     /** Key code constant: 'A' key. */
1245     public static final int KEYCODE_A               = 29;
1246     /** Key code constant: 'B' key. */
1247     public static final int KEYCODE_B               = 30;
1248     /** Key code constant: 'C' key. */
1249     public static final int KEYCODE_C               = 31;
1250     /** Key code constant: 'D' key. */
1251     public static final int KEYCODE_D               = 32;
1252     /** Key code constant: 'E' key. */
1253     public static final int KEYCODE_E               = 33;
1254     /** Key code constant: 'F' key. */
1255     public static final int KEYCODE_F               = 34;
1256     /** Key code constant: 'G' key. */
1257     public static final int KEYCODE_G               = 35;
1258     /** Key code constant: 'H' key. */
1259     public static final int KEYCODE_H               = 36;
1260     /** Key code constant: 'I' key. */
1261     public static final int KEYCODE_I               = 37;
1262     /** Key code constant: 'J' key. */
1263     public static final int KEYCODE_J               = 38;
1264     /** Key code constant: 'K' key. */
1265     public static final int KEYCODE_K               = 39;
1266     /** Key code constant: 'L' key. */
1267     public static final int KEYCODE_L               = 40;
1268     /** Key code constant: 'M' key. */
1269     public static final int KEYCODE_M               = 41;
1270     /** Key code constant: 'N' key. */
1271     public static final int KEYCODE_N               = 42;
1272     /** Key code constant: 'O' key. */
1273     public static final int KEYCODE_O               = 43;
1274     /** Key code constant: 'P' key. */
1275     public static final int KEYCODE_P               = 44;
1276     /** Key code constant: 'Q' key. */
1277     public static final int KEYCODE_Q               = 45;
1278     /** Key code constant: 'R' key. */
1279     public static final int KEYCODE_R               = 46;
1280     /** Key code constant: 'S' key. */
1281     public static final int KEYCODE_S               = 47;
1282     /** Key code constant: 'T' key. */
1283     public static final int KEYCODE_T               = 48;
1284     /** Key code constant: 'U' key. */
1285     public static final int KEYCODE_U               = 49;
1286     /** Key code constant: 'V' key. */
1287     public static final int KEYCODE_V               = 50;
1288     /** Key code constant: 'W' key. */
1289     public static final int KEYCODE_W               = 51;
1290     /** Key code constant: 'X' key. */
1291     public static final int KEYCODE_X               = 52;
1292     /** Key code constant: 'Y' key. */
1293     public static final int KEYCODE_Y               = 53;
1294     /** Key code constant: 'Z' key. */
1295     public static final int KEYCODE_Z               = 54;
1296     /** Key code constant: ',' key. */
1297     public static final int KEYCODE_COMMA           = 55;
1298     /** Key code constant: '.' key. */
1299     public static final int KEYCODE_PERIOD          = 56;
1300     /** Key code constant: Left Alt modifier key. */
1301     public static final int KEYCODE_ALT_LEFT        = 57;
1302     /** Key code constant: Right Alt modifier key. */
1303     public static final int KEYCODE_ALT_RIGHT       = 58;
1304     /** Key code constant: Left Shift modifier key. */
1305     public static final int KEYCODE_SHIFT_LEFT      = 59;
1306     /** Key code constant: Right Shift modifier key. */
1307     public static final int KEYCODE_SHIFT_RIGHT     = 60;
1308     /** Key code constant: Tab key. */
1309     public static final int KEYCODE_TAB             = 61;
1310     /** Key code constant: Space key. */
1311     public static final int KEYCODE_SPACE           = 62;
1312     /** Key code constant: Symbol modifier key.
1313      * Used to enter alternate symbols. */
1314     public static final int KEYCODE_SYM             = 63;
1315     /** Key code constant: Explorer special function key.
1316      * Used to launch a browser application. */
1317     public static final int KEYCODE_EXPLORER        = 64;
1318     /** Key code constant: Envelope special function key.
1319      * Used to launch a mail application. */
1320     public static final int KEYCODE_ENVELOPE        = 65;
1321     /** Key code constant: Enter key. */
1322     public static final int KEYCODE_ENTER           = 66;
1323     /** Key code constant: Backspace key.
1324      * Deletes characters before the insertion point, unlike {@link #KEYCODE_FORWARD_DEL}. */
1325     public static final int KEYCODE_DEL             = 67;
1326     /** Key code constant: '`' (backtick) key. */
1327     public static final int KEYCODE_GRAVE           = 68;
1328     /** Key code constant: '-'. */
1329     public static final int KEYCODE_MINUS           = 69;
1330     /** Key code constant: '=' key. */
1331     public static final int KEYCODE_EQUALS          = 70;
1332     /** Key code constant: '[' key. */
1333     public static final int KEYCODE_LEFT_BRACKET    = 71;
1334     /** Key code constant: ']' key. */
1335     public static final int KEYCODE_RIGHT_BRACKET   = 72;
1336     /** Key code constant: '\' key. */
1337     public static final int KEYCODE_BACKSLASH       = 73;
1338     /** Key code constant: ';' key. */
1339     public static final int KEYCODE_SEMICOLON       = 74;
1340     /** Key code constant: ''' (apostrophe) key. */
1341     public static final int KEYCODE_APOSTROPHE      = 75;
1342     /** Key code constant: '/' key. */
1343     public static final int KEYCODE_SLASH           = 76;
1344     /** Key code constant: '@' key. */
1345     public static final int KEYCODE_AT              = 77;
1346     /** Key code constant: Number modifier key.
1347      * Used to enter numeric symbols.
1348      * This key is not Num Lock; it is more like {@link #KEYCODE_ALT_LEFT} and is
1349      * interpreted as an ALT key by {@link android.text.method.MetaKeyKeyListener}. */
1350     public static final int KEYCODE_NUM             = 78;
1351     /** Key code constant: Headset Hook key.
1352      * Used to hang up calls and stop media. */
1353     public static final int KEYCODE_HEADSETHOOK     = 79;
1354     /** Key code constant: Camera Focus key.
1355      * Used to focus the camera. */
1356     public static final int KEYCODE_FOCUS           = 80;   // *Camera* focus
1357     /** Key code constant: '+' key. */
1358     public static final int KEYCODE_PLUS            = 81;
1359     /** Key code constant: Menu key. */
1360     public static final int KEYCODE_MENU            = 82;
1361     /** Key code constant: Notification key. */
1362     public static final int KEYCODE_NOTIFICATION    = 83;
1363     /** Key code constant: Search key. */
1364     public static final int KEYCODE_SEARCH          = 84;
1365     /** Key code constant: Play/Pause media key. */
1366     public static final int KEYCODE_MEDIA_PLAY_PAUSE= 85;
1367     /** Key code constant: Stop media key. */
1368     public static final int KEYCODE_MEDIA_STOP      = 86;
1369     /** Key code constant: Play Next media key. */
1370     public static final int KEYCODE_MEDIA_NEXT      = 87;
1371     /** Key code constant: Play Previous media key. */
1372     public static final int KEYCODE_MEDIA_PREVIOUS  = 88;
1373     /** Key code constant: Rewind media key. */
1374     public static final int KEYCODE_MEDIA_REWIND    = 89;
1375     /** Key code constant: Fast Forward media key. */
1376     public static final int KEYCODE_MEDIA_FAST_FORWARD = 90;
1377     /** Key code constant: Mute key.
1378      * Mutes the microphone, unlike {@link #KEYCODE_VOLUME_MUTE}. */
1379     public static final int KEYCODE_MUTE            = 91;
1380     /** Key code constant: Page Up key. */
1381     public static final int KEYCODE_PAGE_UP         = 92;
1382     /** Key code constant: Page Down key. */
1383     public static final int KEYCODE_PAGE_DOWN       = 93;
1384     /** Key code constant: Picture Symbols modifier key.
1385      * Used to switch symbol sets (Emoji, Kao-moji). */
1386     public static final int KEYCODE_PICTSYMBOLS     = 94;   // switch symbol-sets (Emoji,Kao-moji)
1387     /** Key code constant: Switch Charset modifier key.
1388      * Used to switch character sets (Kanji, Katakana). */
1389     public static final int KEYCODE_SWITCH_CHARSET  = 95;   // switch char-sets (Kanji,Katakana)
1390     /** Key code constant: A Button key.
1391      * On a game controller, the A button should be either the button labeled A
1392      * or the first button on the upper row of controller buttons. */
1393     public static final int KEYCODE_BUTTON_A        = 96;
1394     /** Key code constant: B Button key.
1395      * On a game controller, the B button should be either the button labeled B
1396      * or the second button on the upper row of controller buttons. */
1397     public static final int KEYCODE_BUTTON_B        = 97;
1398     /** Key code constant: C Button key.
1399      * On a game controller, the C button should be either the button labeled C
1400      * or the third button on the upper row of controller buttons. */
1401     public static final int KEYCODE_BUTTON_C        = 98;
1402     /** Key code constant: X Button key.
1403      * On a game controller, the X button should be either the button labeled X
1404      * or the first button on the lower row of controller buttons. */
1405     public static final int KEYCODE_BUTTON_X        = 99;
1406     /** Key code constant: Y Button key.
1407      * On a game controller, the Y button should be either the button labeled Y
1408      * or the second button on the lower row of controller buttons. */
1409     public static final int KEYCODE_BUTTON_Y        = 100;
1410     /** Key code constant: Z Button key.
1411      * On a game controller, the Z button should be either the button labeled Z
1412      * or the third button on the lower row of controller buttons. */
1413     public static final int KEYCODE_BUTTON_Z        = 101;
1414     /** Key code constant: L1 Button key.
1415      * On a game controller, the L1 button should be either the button labeled L1 (or L)
1416      * or the top left trigger button. */
1417     public static final int KEYCODE_BUTTON_L1       = 102;
1418     /** Key code constant: R1 Button key.
1419      * On a game controller, the R1 button should be either the button labeled R1 (or R)
1420      * or the top right trigger button. */
1421     public static final int KEYCODE_BUTTON_R1       = 103;
1422     /** Key code constant: L2 Button key.
1423      * On a game controller, the L2 button should be either the button labeled L2
1424      * or the bottom left trigger button. */
1425     public static final int KEYCODE_BUTTON_L2       = 104;
1426     /** Key code constant: R2 Button key.
1427      * On a game controller, the R2 button should be either the button labeled R2
1428      * or the bottom right trigger button. */
1429     public static final int KEYCODE_BUTTON_R2       = 105;
1430     /** Key code constant: Left Thumb Button key.
1431      * On a game controller, the left thumb button indicates that the left (or only)
1432      * joystick is pressed. */
1433     public static final int KEYCODE_BUTTON_THUMBL   = 106;
1434     /** Key code constant: Right Thumb Button key.
1435      * On a game controller, the right thumb button indicates that the right
1436      * joystick is pressed. */
1437     public static final int KEYCODE_BUTTON_THUMBR   = 107;
1438     /** Key code constant: Start Button key.
1439      * On a game controller, the button labeled Start. */
1440     public static final int KEYCODE_BUTTON_START    = 108;
1441     /** Key code constant: Select Button key.
1442      * On a game controller, the button labeled Select. */
1443     public static final int KEYCODE_BUTTON_SELECT   = 109;
1444     /** Key code constant: Mode Button key.
1445      * On a game controller, the button labeled Mode. */
1446     public static final int KEYCODE_BUTTON_MODE     = 110;
1447     /** Key code constant: Escape key. */
1448     public static final int KEYCODE_ESCAPE          = 111;
1449     /** Key code constant: Forward Delete key.
1450      * Deletes characters ahead of the insertion point, unlike {@link #KEYCODE_DEL}. */
1451     public static final int KEYCODE_FORWARD_DEL     = 112;
1452     /** Key code constant: Left Control modifier key. */
1453     public static final int KEYCODE_CTRL_LEFT       = 113;
1454     /** Key code constant: Right Control modifier key. */
1455     public static final int KEYCODE_CTRL_RIGHT      = 114;
1456     /** Key code constant: Caps Lock modifier key. */
1457     public static final int KEYCODE_CAPS_LOCK       = 115;
1458     /** Key code constant: Scroll Lock key. */
1459     public static final int KEYCODE_SCROLL_LOCK     = 116;
1460     /** Key code constant: Left Meta modifier key. */
1461     public static final int KEYCODE_META_LEFT       = 117;
1462     /** Key code constant: Right Meta modifier key. */
1463     public static final int KEYCODE_META_RIGHT      = 118;
1464     /** Key code constant: Function modifier key. */
1465     public static final int KEYCODE_FUNCTION        = 119;
1466     /** Key code constant: System Request / Print Screen key. */
1467     public static final int KEYCODE_SYSRQ           = 120;
1468     /** Key code constant: Break / Pause key. */
1469     public static final int KEYCODE_BREAK           = 121;
1470     /** Key code constant: Home Movement key.
1471      * Used for scrolling or moving the cursor around to the start of a line
1472      * or to the top of a list. */
1473     public static final int KEYCODE_MOVE_HOME       = 122;
1474     /** Key code constant: End Movement key.
1475      * Used for scrolling or moving the cursor around to the end of a line
1476      * or to the bottom of a list. */
1477     public static final int KEYCODE_MOVE_END        = 123;
1478     /** Key code constant: Insert key.
1479      * Toggles insert / overwrite edit mode. */
1480     public static final int KEYCODE_INSERT          = 124;
1481     /** Key code constant: Forward key.
1482      * Navigates forward in the history stack.  Complement of {@link #KEYCODE_BACK}. */
1483     public static final int KEYCODE_FORWARD         = 125;
1484     /** Key code constant: Play media key. */
1485     public static final int KEYCODE_MEDIA_PLAY      = 126;
1486     /** Key code constant: Pause media key. */
1487     public static final int KEYCODE_MEDIA_PAUSE     = 127;
1488     /** Key code constant: Close media key.
1489      * May be used to close a CD tray, for example. */
1490     public static final int KEYCODE_MEDIA_CLOSE     = 128;
1491     /** Key code constant: Eject media key.
1492      * May be used to eject a CD tray, for example. */
1493     public static final int KEYCODE_MEDIA_EJECT     = 129;
1494     /** Key code constant: Record media key. */
1495     public static final int KEYCODE_MEDIA_RECORD    = 130;
1496     /** Key code constant: F1 key. */
1497     public static final int KEYCODE_F1              = 131;
1498     /** Key code constant: F2 key. */
1499     public static final int KEYCODE_F2              = 132;
1500     /** Key code constant: F3 key. */
1501     public static final int KEYCODE_F3              = 133;
1502     /** Key code constant: F4 key. */
1503     public static final int KEYCODE_F4              = 134;
1504     /** Key code constant: F5 key. */
1505     public static final int KEYCODE_F5              = 135;
1506     /** Key code constant: F6 key. */
1507     public static final int KEYCODE_F6              = 136;
1508     /** Key code constant: F7 key. */
1509     public static final int KEYCODE_F7              = 137;
1510     /** Key code constant: F8 key. */
1511     public static final int KEYCODE_F8              = 138;
1512     /** Key code constant: F9 key. */
1513     public static final int KEYCODE_F9              = 139;
1514     /** Key code constant: F10 key. */
1515     public static final int KEYCODE_F10             = 140;
1516     /** Key code constant: F11 key. */
1517     public static final int KEYCODE_F11             = 141;
1518     /** Key code constant: F12 key. */
1519     public static final int KEYCODE_F12             = 142;
1520     /** Key code constant: Num Lock modifier key.
1521      * This is the Num Lock key; it is different from {@link #KEYCODE_NUM}.
1522      * This key generally modifies the behavior of other keys on the numeric keypad. */
1523     public static final int KEYCODE_NUM_LOCK        = 143;
1524     /** Key code constant: Numeric keypad '0' key. */
1525     public static final int KEYCODE_NUMPAD_0        = 144;
1526     /** Key code constant: Numeric keypad '1' key. */
1527     public static final int KEYCODE_NUMPAD_1        = 145;
1528     /** Key code constant: Numeric keypad '2' key. */
1529     public static final int KEYCODE_NUMPAD_2        = 146;
1530     /** Key code constant: Numeric keypad '3' key. */
1531     public static final int KEYCODE_NUMPAD_3        = 147;
1532     /** Key code constant: Numeric keypad '4' key. */
1533     public static final int KEYCODE_NUMPAD_4        = 148;
1534     /** Key code constant: Numeric keypad '5' key. */
1535     public static final int KEYCODE_NUMPAD_5        = 149;
1536     /** Key code constant: Numeric keypad '6' key. */
1537     public static final int KEYCODE_NUMPAD_6        = 150;
1538     /** Key code constant: Numeric keypad '7' key. */
1539     public static final int KEYCODE_NUMPAD_7        = 151;
1540     /** Key code constant: Numeric keypad '8' key. */
1541     public static final int KEYCODE_NUMPAD_8        = 152;
1542     /** Key code constant: Numeric keypad '9' key. */
1543     public static final int KEYCODE_NUMPAD_9        = 153;
1544     /** Key code constant: Numeric keypad '/' key (for division). */
1545     public static final int KEYCODE_NUMPAD_DIVIDE   = 154;
1546     /** Key code constant: Numeric keypad '*' key (for multiplication). */
1547     public static final int KEYCODE_NUMPAD_MULTIPLY = 155;
1548     /** Key code constant: Numeric keypad '-' key (for subtraction). */
1549     public static final int KEYCODE_NUMPAD_SUBTRACT = 156;
1550     /** Key code constant: Numeric keypad '+' key (for addition). */
1551     public static final int KEYCODE_NUMPAD_ADD      = 157;
1552     /** Key code constant: Numeric keypad '.' key (for decimals or digit grouping). */
1553     public static final int KEYCODE_NUMPAD_DOT      = 158;
1554     /** Key code constant: Numeric keypad ',' key (for decimals or digit grouping). */
1555     public static final int KEYCODE_NUMPAD_COMMA    = 159;
1556     /** Key code constant: Numeric keypad Enter key. */
1557     public static final int KEYCODE_NUMPAD_ENTER    = 160;
1558     /** Key code constant: Numeric keypad '=' key. */
1559     public static final int KEYCODE_NUMPAD_EQUALS   = 161;
1560     /** Key code constant: Numeric keypad '(' key. */
1561     public static final int KEYCODE_NUMPAD_LEFT_PAREN = 162;
1562     /** Key code constant: Numeric keypad ')' key. */
1563     public static final int KEYCODE_NUMPAD_RIGHT_PAREN = 163;
1564     /** Key code constant: Volume Mute key.
1565      * Mutes the speaker, unlike {@link #KEYCODE_MUTE}.
1566      * This key should normally be implemented as a toggle such that the first press
1567      * mutes the speaker and the second press restores the original volume. */
1568     public static final int KEYCODE_VOLUME_MUTE     = 164;
1569     /** Key code constant: Info key.
1570      * Common on TV remotes to show additional information related to what is
1571      * currently being viewed. */
1572     public static final int KEYCODE_INFO            = 165;
1573     /** Key code constant: Channel up key.
1574      * On TV remotes, increments the television channel. */
1575     public static final int KEYCODE_CHANNEL_UP      = 166;
1576     /** Key code constant: Channel down key.
1577      * On TV remotes, decrements the television channel. */
1578     public static final int KEYCODE_CHANNEL_DOWN    = 167;
1579     /** Key code constant: Zoom in key. */
1580     public static final int KEYCODE_ZOOM_IN         = 168;
1581     /** Key code constant: Zoom out key. */
1582     public static final int KEYCODE_ZOOM_OUT        = 169;
1583     /** Key code constant: TV key.
1584      * On TV remotes, switches to viewing live TV. */
1585     public static final int KEYCODE_TV              = 170;
1586     /** Key code constant: Window key.
1587      * On TV remotes, toggles picture-in-picture mode or other windowing functions. */
1588     public static final int KEYCODE_WINDOW          = 171;
1589     /** Key code constant: Guide key.
1590      * On TV remotes, shows a programming guide. */
1591     public static final int KEYCODE_GUIDE           = 172;
1592     /** Key code constant: DVR key.
1593      * On some TV remotes, switches to a DVR mode for recorded shows. */
1594     public static final int KEYCODE_DVR             = 173;
1595     /** Key code constant: Bookmark key.
1596      * On some TV remotes, bookmarks content or web pages. */
1597     public static final int KEYCODE_BOOKMARK        = 174;
1598     /** Key code constant: Toggle captions key.
1599      * Switches the mode for closed-captioning text, for example during television shows. */
1600     public static final int KEYCODE_CAPTIONS        = 175;
1601     /** Key code constant: Settings key.
1602      * Starts the system settings activity. */
1603     public static final int KEYCODE_SETTINGS        = 176;
1604     /** Key code constant: TV power key.
1605      * On TV remotes, toggles the power on a television screen. */
1606     public static final int KEYCODE_TV_POWER        = 177;
1607     /** Key code constant: TV input key.
1608      * On TV remotes, switches the input on a television screen. */
1609     public static final int KEYCODE_TV_INPUT        = 178;
1610     /** Key code constant: Set-top-box power key.
1611      * On TV remotes, toggles the power on an external Set-top-box. */
1612     public static final int KEYCODE_STB_POWER       = 179;
1613     /** Key code constant: Set-top-box input key.
1614      * On TV remotes, switches the input mode on an external Set-top-box. */
1615     public static final int KEYCODE_STB_INPUT       = 180;
1616     /** Key code constant: A/V Receiver power key.
1617      * On TV remotes, toggles the power on an external A/V Receiver. */
1618     public static final int KEYCODE_AVR_POWER       = 181;
1619     /** Key code constant: A/V Receiver input key.
1620      * On TV remotes, switches the input mode on an external A/V Receiver. */
1621     public static final int KEYCODE_AVR_INPUT       = 182;
1622     /** Key code constant: Red "programmable" key.
1623      * On TV remotes, acts as a contextual/programmable key. */
1624     public static final int KEYCODE_PROG_RED        = 183;
1625     /** Key code constant: Green "programmable" key.
1626      * On TV remotes, actsas a contextual/programmable key. */
1627     public static final int KEYCODE_PROG_GREEN      = 184;
1628     /** Key code constant: Yellow "programmable" key.
1629      * On TV remotes, acts as a contextual/programmable key. */
1630     public static final int KEYCODE_PROG_YELLOW     = 185;
1631     /** Key code constant: Blue "programmable" key.
1632      * On TV remotes, acts as a contextual/programmable key. */
1633     public static final int KEYCODE_PROG_BLUE       = 186;
1634
1635     private static final int LAST_KEYCODE           = KEYCODE_PROG_BLUE;
1636
1637     private String[] mKeyCodes = new String[256];
1638     private String[] mAppKeyCodes = new String[256];
1639
1640     private void initKeyCodes() {
1641         mKeyCodes[KEYCODE_DPAD_CENTER] = "\015";
1642         mKeyCodes[KEYCODE_DPAD_UP] = "\033[A";
1643         mKeyCodes[KEYCODE_DPAD_DOWN] = "\033[B";
1644         mKeyCodes[KEYCODE_DPAD_RIGHT] = "\033[C";
1645         mKeyCodes[KEYCODE_DPAD_LEFT] = "\033[D";
1646         mKeyCodes[KEYCODE_F1] = "\033[OP";
1647         mKeyCodes[KEYCODE_F2] = "\033[OQ";
1648         mKeyCodes[KEYCODE_F3] = "\033[OR";
1649         mKeyCodes[KEYCODE_F4] = "\033[OS";
1650         mKeyCodes[KEYCODE_F5] = "\033[15~";
1651         mKeyCodes[KEYCODE_F6] = "\033[17~";
1652         mKeyCodes[KEYCODE_F7] = "\033[18~";
1653         mKeyCodes[KEYCODE_F8] = "\033[19~";
1654         mKeyCodes[KEYCODE_F9] = "\033[20~";
1655         mKeyCodes[KEYCODE_F10] = "\033[21~";
1656         mKeyCodes[KEYCODE_F11] = "\033[23~";
1657         mKeyCodes[KEYCODE_F12] = "\033[24~";
1658         mKeyCodes[KEYCODE_SYSRQ] = "\033[32~"; // Sys Request / Print
1659         // Is this Scroll lock? mKeyCodes[Cancel] = "\033[33~";
1660         mKeyCodes[KEYCODE_BREAK] = "\033[34~"; // Pause/Break
1661
1662         mKeyCodes[KEYCODE_TAB] = "\011";
1663         mKeyCodes[KEYCODE_ENTER] = "\015";
1664         mKeyCodes[KEYCODE_ESCAPE] = "\033";
1665
1666         mKeyCodes[KEYCODE_INSERT] = "\033[2~";
1667         mKeyCodes[KEYCODE_FORWARD_DEL] = "\033[3~";
1668         mKeyCodes[KEYCODE_MOVE_HOME] = "\033[1~";
1669         mKeyCodes[KEYCODE_MOVE_END] = "\033[4~";
1670         mKeyCodes[KEYCODE_PAGE_UP] = "\033[5~";
1671         mKeyCodes[KEYCODE_PAGE_DOWN] = "\033[6~";
1672         mKeyCodes[KEYCODE_DEL]= "\177";
1673         mKeyCodes[KEYCODE_NUM_LOCK] = "\033OP";
1674         mKeyCodes[KEYCODE_NUMPAD_DIVIDE] = "/";
1675         mKeyCodes[KEYCODE_NUMPAD_MULTIPLY] = "*";
1676         mKeyCodes[KEYCODE_NUMPAD_SUBTRACT] = "-";
1677         mKeyCodes[KEYCODE_NUMPAD_ADD] = "+";
1678         mKeyCodes[KEYCODE_NUMPAD_ENTER] = "\015";
1679         mKeyCodes[KEYCODE_NUMPAD_EQUALS] = "=";
1680         mKeyCodes[KEYCODE_NUMPAD_DOT] = ".";
1681         mKeyCodes[KEYCODE_NUMPAD_COMMA] = ",";
1682         mKeyCodes[KEYCODE_NUMPAD_0] = "0";
1683         mKeyCodes[KEYCODE_NUMPAD_1] = "1";
1684         mKeyCodes[KEYCODE_NUMPAD_2] = "2";
1685         mKeyCodes[KEYCODE_NUMPAD_3] = "3";
1686         mKeyCodes[KEYCODE_NUMPAD_4] = "4";
1687         mKeyCodes[KEYCODE_NUMPAD_5] = "5";
1688         mKeyCodes[KEYCODE_NUMPAD_6] = "6";
1689         mKeyCodes[KEYCODE_NUMPAD_7] = "7";
1690         mKeyCodes[KEYCODE_NUMPAD_8] = "8";
1691         mKeyCodes[KEYCODE_NUMPAD_9] = "9";
1692
1693         mAppKeyCodes[KEYCODE_DPAD_UP] = "\033OA";
1694         mAppKeyCodes[KEYCODE_DPAD_DOWN] = "\033OB";
1695         mAppKeyCodes[KEYCODE_DPAD_RIGHT] = "\033OC";
1696         mAppKeyCodes[KEYCODE_DPAD_LEFT] = "\033OD";
1697         mAppKeyCodes[KEYCODE_NUMPAD_DIVIDE] = "\033Oo";
1698         mAppKeyCodes[KEYCODE_NUMPAD_MULTIPLY] = "\033Oj";
1699         mAppKeyCodes[KEYCODE_NUMPAD_SUBTRACT] = "\033Om";
1700         mAppKeyCodes[KEYCODE_NUMPAD_ADD] = "\033Ok";
1701         mAppKeyCodes[KEYCODE_NUMPAD_ENTER] = "\033OM";
1702         mAppKeyCodes[KEYCODE_NUMPAD_EQUALS] = "\033OX";
1703         mAppKeyCodes[KEYCODE_NUMPAD_DOT] = "\033On";
1704         mAppKeyCodes[KEYCODE_NUMPAD_COMMA] = "\033Ol";
1705         mAppKeyCodes[KEYCODE_NUMPAD_0] = "\033Op";
1706         mAppKeyCodes[KEYCODE_NUMPAD_1] = "\033Oq";
1707         mAppKeyCodes[KEYCODE_NUMPAD_2] = "\033Or";
1708         mAppKeyCodes[KEYCODE_NUMPAD_3] = "\033Os";
1709         mAppKeyCodes[KEYCODE_NUMPAD_4] = "\033Ot";
1710         mAppKeyCodes[KEYCODE_NUMPAD_5] = "\033Ou";
1711         mAppKeyCodes[KEYCODE_NUMPAD_6] = "\033Ov";
1712         mAppKeyCodes[KEYCODE_NUMPAD_7] = "\033Ow";
1713         mAppKeyCodes[KEYCODE_NUMPAD_8] = "\033Ox";
1714         mAppKeyCodes[KEYCODE_NUMPAD_9] = "\033Oy";
1715     }
1716
1717     /**
1718      * The state engine for a modifier key. Can be pressed, released, locked,
1719      * and so on.
1720      *
1721      */
1722     private class ModifierKey {
1723
1724         private int mState;
1725
1726         private static final int UNPRESSED = 0;
1727
1728         private static final int PRESSED = 1;
1729
1730         private static final int RELEASED = 2;
1731
1732         private static final int USED = 3;
1733
1734         private static final int LOCKED = 4;
1735
1736         /**
1737          * Construct a modifier key. UNPRESSED by default.
1738          *
1739          */
1740         public ModifierKey() {
1741             mState = UNPRESSED;
1742         }
1743
1744         public void onPress() {
1745             switch (mState) {
1746             case PRESSED:
1747                 // This is a repeat before use
1748                 break;
1749             case RELEASED:
1750                 mState = LOCKED;
1751                 break;
1752             case USED:
1753                 // This is a repeat after use
1754                 break;
1755             case LOCKED:
1756                 mState = UNPRESSED;
1757                 break;
1758             default:
1759                 mState = PRESSED;
1760                 break;
1761             }
1762         }
1763
1764         public void onRelease() {
1765             switch (mState) {
1766             case USED:
1767                 mState = UNPRESSED;
1768                 break;
1769             case PRESSED:
1770                 mState = RELEASED;
1771                 break;
1772             default:
1773                 // Leave state alone
1774                 break;
1775             }
1776         }
1777
1778         public void adjustAfterKeypress() {
1779             switch (mState) {
1780             case PRESSED:
1781                 mState = USED;
1782                 break;
1783             case RELEASED:
1784                 mState = UNPRESSED;
1785                 break;
1786             default:
1787                 // Leave state alone
1788                 break;
1789             }
1790         }
1791
1792         public boolean isActive() {
1793             return mState != UNPRESSED;
1794         }
1795     }
1796
1797     private ModifierKey mAltKey = new ModifierKey();
1798
1799     private ModifierKey mCapKey = new ModifierKey();
1800
1801     private ModifierKey mControlKey = new ModifierKey();
1802
1803     private ModifierKey mFnKey = new ModifierKey();
1804
1805     private boolean mCapsLock;
1806
1807     static public final int KEYCODE_OFFSET = 1000;
1808
1809     /**
1810      * Construct a term key listener.
1811      *
1812      */
1813     public TermKeyListener() {
1814         initKeyCodes();
1815     }
1816
1817     public void handleControlKey(boolean down) {
1818         if (down) {
1819             mControlKey.onPress();
1820         } else {
1821             mControlKey.onRelease();
1822         }
1823     }
1824
1825     public void handleFnKey(boolean down) {
1826         if (down) {
1827             mFnKey.onPress();
1828         } else {
1829             mFnKey.onRelease();
1830         }
1831     }
1832
1833     public int mapControlChar(int ch) {
1834         int result = ch;
1835         if (mControlKey.isActive()) {
1836             // Search is the control key.
1837             if (result >= 'a' && result <= 'z') {
1838                 result = (char) (result - 'a' + '\001');
1839             } else if (result >= 'A' && result <= 'Z') {
1840                 result = (char) (result - 'A' + '\001');
1841             } else if (result == ' ' || result == '2') {
1842                 result = 0;
1843             } else if (result == '[' || result == '3') {
1844                 result = 27; // ^[ (Esc)
1845             } else if (result == '\\' || result == '4') {
1846                 result = 28;
1847             } else if (result == ']' || result == '5') {
1848                 result = 29;
1849             } else if (result == '^' || result == '6') {
1850                 result = 30; // control-^
1851             } else if (result == '_' || result == '7') {
1852                 result = 31;
1853             } else if (result == '8') {
1854                 result = 127; // DEL
1855             } else if (result == '9') {
1856                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_F11;
1857             } else if (result == '0') {
1858                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_F12;
1859             }
1860         } else if (mFnKey.isActive()) {
1861             if (result == 'w' || result == 'W') {
1862                 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_UP;
1863             } else if (result == 'a' || result == 'A') {
1864                 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_LEFT;
1865             } else if (result == 's' || result == 'S') {
1866                 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_DOWN;
1867             } else if (result == 'd' || result == 'D') {
1868                 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_RIGHT;
1869             } else if (result == 'p' || result == 'P') {
1870                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_PAGE_UP;
1871             } else if (result == 'n' || result == 'N') {
1872                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_PAGE_DOWN;
1873             } else if (result == 't' || result == 'T') {
1874                 result = KEYCODE_OFFSET + KeyEvent.KEYCODE_TAB;
1875             } else if (result == 'l' || result == 'L') {
1876                 result = '|';
1877             } else if (result == 'u' || result == 'U') {
1878                 result = '_';
1879             } else if (result == 'e' || result == 'E') {
1880                 result = 27; // ^[ (Esc)
1881             } else if (result == '.') {
1882                 result = 28; // ^\
1883             } else if (result > '0' && result <= '9') {
1884                 // F1-F9
1885                 result = (char)(result + KEYCODE_OFFSET + TermKeyListener.KEYCODE_F1 - 1);
1886             } else if (result == '0') {
1887                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_F10;
1888             } else if (result == 'i' || result == 'I') {
1889                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_INSERT;
1890             } else if (result == 'x' || result == 'X') {
1891                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_FORWARD_DEL;
1892             } else if (result == 'h' || result == 'H') {
1893                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_MOVE_HOME;
1894             } else if (result == 'f' || result == 'F') {
1895                 result = KEYCODE_OFFSET + TermKeyListener.KEYCODE_MOVE_END;
1896             }
1897         }
1898
1899         if (result > -1) {
1900             mAltKey.adjustAfterKeypress();
1901             mCapKey.adjustAfterKeypress();
1902             mControlKey.adjustAfterKeypress();
1903             mFnKey.adjustAfterKeypress();
1904         }
1905
1906         return result;
1907     }
1908
1909     /**
1910      * Handle a keyDown event.
1911      *
1912      * @param keyCode the keycode of the keyDown event
1913      *
1914      */
1915     public void keyDown(int keyCode, KeyEvent event, OutputStream out, boolean appMode) throws IOException {
1916         if (handleKeyCode(keyCode, out, appMode)) {
1917             return;
1918         }
1919         int result = -1;
1920         switch (keyCode) {
1921         case KeyEvent.KEYCODE_ALT_RIGHT:
1922         case KeyEvent.KEYCODE_ALT_LEFT:
1923             mAltKey.onPress();
1924             break;
1925
1926         case KeyEvent.KEYCODE_SHIFT_LEFT:
1927         case KeyEvent.KEYCODE_SHIFT_RIGHT:
1928             mCapKey.onPress();
1929             break;
1930
1931         case KEYCODE_CTRL_LEFT:
1932         case KEYCODE_CTRL_RIGHT:
1933             mControlKey.onPress();
1934             break;
1935
1936         case KEYCODE_CAPS_LOCK:
1937             if (event.getRepeatCount() == 0) {
1938                 mCapsLock = !mCapsLock;
1939             }
1940             break;
1941
1942         default: {
1943             result = event.getUnicodeChar(
1944                    (mCapKey.isActive() || mCapsLock ? KeyEvent.META_SHIFT_ON : 0) |
1945                    (mAltKey.isActive() ? KeyEvent.META_ALT_ON : 0));
1946             break;
1947             }
1948         }
1949
1950         result = mapControlChar(result);
1951
1952         if (result >= KEYCODE_OFFSET) {
1953             handleKeyCode(result - KEYCODE_OFFSET, out, appMode);
1954         } else if (result >= 0) {
1955             out.write(result);
1956         }
1957     }
1958
1959     public boolean handleKeyCode(int keyCode, OutputStream out, boolean appMode) throws IOException {
1960         if (keyCode >= 0 && keyCode < mKeyCodes.length) {
1961             String code = null;
1962             if (appMode) {
1963                 code = mAppKeyCodes[keyCode];
1964             }
1965             if (code == null) {
1966                 code = mKeyCodes[keyCode];
1967             }
1968             if (code != null) {
1969                 int length = code.length();
1970                 for (int i = 0; i < length; i++) {
1971                     out.write(code.charAt(i));
1972                 }
1973                 return true;
1974             }
1975         }
1976         return false;
1977     }
1978
1979     /**
1980      * Handle a keyUp event.
1981      *
1982      * @param keyCode the keyCode of the keyUp event
1983      */
1984     public void keyUp(int keyCode) {
1985         switch (keyCode) {
1986         case KeyEvent.KEYCODE_ALT_LEFT:
1987         case KeyEvent.KEYCODE_ALT_RIGHT:
1988             mAltKey.onRelease();
1989             break;
1990         case KeyEvent.KEYCODE_SHIFT_LEFT:
1991         case KeyEvent.KEYCODE_SHIFT_RIGHT:
1992             mCapKey.onRelease();
1993             break;
1994
1995         case KEYCODE_CTRL_LEFT:
1996         case KEYCODE_CTRL_RIGHT:
1997             mControlKey.onRelease();
1998             break;
1999
2000         default:
2001             // Ignore other keyUps
2002             break;
2003         }
2004     }
2005 }