@SuppressLint("WrongCall") // Suppress the warning since this does not refer to Android's onDraw() @Override public void onDraw(Canvas canvas) { if (bridge.bitmap != null) { // draw the bitmap bridge.onDraw(); // draw the bridge bitmap if it exists canvas.drawBitmap(bridge.bitmap, 0, 0, paint); // also draw cursor if visible if (bridge.buffer.isCursorVisible()) { int cursorColumn = bridge.buffer.getCursorColumn(); final int cursorRow = bridge.buffer.getCursorRow(); final int columns = bridge.buffer.getColumns(); if (cursorColumn == columns) cursorColumn = columns - 1; if (cursorColumn < 0 || cursorRow < 0) return; int currentAttribute = bridge.buffer.getAttributes(cursorColumn, cursorRow); boolean onWideCharacter = (currentAttribute & VDUBuffer.FULLWIDTH) != 0; int x = cursorColumn * bridge.charWidth; int y = (bridge.buffer.getCursorRow() + bridge.buffer.screenBase - bridge.buffer.windowBase) * bridge.charHeight; // Save the current clip and translation canvas.save(); canvas.translate(x, y); canvas.clipRect(0, 0, bridge.charWidth * (onWideCharacter ? 2 : 1), bridge.charHeight); canvas.drawPaint(cursorPaint); final int deadKey = bridge.getKeyHandler().getDeadKey(); if (deadKey != 0) { canvas.drawText(new char[] {(char) deadKey}, 0, 1, 0, 0, cursorStrokePaint); } // Make sure we scale our decorations to the correct size. canvas.concat(scaleMatrix); int metaState = bridge.getKeyHandler().getMetaState(); if ((metaState & TerminalKeyListener.OUR_SHIFT_ON) != 0) canvas.drawPath(shiftCursor, cursorStrokePaint); else if ((metaState & TerminalKeyListener.OUR_SHIFT_LOCK) != 0) canvas.drawPath(shiftCursor, cursorPaint); if ((metaState & TerminalKeyListener.OUR_ALT_ON) != 0) canvas.drawPath(altCursor, cursorStrokePaint); else if ((metaState & TerminalKeyListener.OUR_ALT_LOCK) != 0) canvas.drawPath(altCursor, cursorPaint); if ((metaState & TerminalKeyListener.OUR_CTRL_ON) != 0) canvas.drawPath(ctrlCursor, cursorStrokePaint); else if ((metaState & TerminalKeyListener.OUR_CTRL_LOCK) != 0) canvas.drawPath(ctrlCursor, cursorPaint); // Restore previous clip region canvas.restore(); } // draw any highlighted area if (bridge.isSelectingForCopy()) { SelectionArea area = bridge.getSelectionArea(); canvas.save(Canvas.CLIP_SAVE_FLAG); canvas.clipRect( area.getLeft() * bridge.charWidth, area.getTop() * bridge.charHeight, (area.getRight() + 1) * bridge.charWidth, (area.getBottom() + 1) * bridge.charHeight); canvas.drawPaint(cursorPaint); canvas.restore(); } } }
/** * Handle onKey() events coming down from a {@link TerminalView} above us. Modify the keys to make * more sense to a host then pass it to the transport. */ public boolean onKey(View v, int keyCode, KeyEvent event) { try { final boolean hardKeyboardHidden = manager.hardKeyboardHidden; // Ignore all key-up events except for the special keys if (event.getAction() == KeyEvent.ACTION_UP) { // There's nothing here for virtual keyboard users. if (!hardKeyboard || (hardKeyboard && hardKeyboardHidden)) return false; // skip keys if we aren't connected yet or have been disconnected if (bridge.isDisconnected() || bridge.transport == null) return false; if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) { if (keyCode == KeyEvent.KEYCODE_ALT_RIGHT && (metaState & META_SLASH) != 0) { metaState &= ~(META_SLASH | META_TRANSIENT); bridge.transport.write('/'); return true; } else if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT && (metaState & META_TAB) != 0) { metaState &= ~(META_TAB | META_TRANSIENT); bridge.transport.write(0x09); return true; } } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) { if (keyCode == KeyEvent.KEYCODE_ALT_LEFT && (metaState & META_SLASH) != 0) { metaState &= ~(META_SLASH | META_TRANSIENT); bridge.transport.write('/'); return true; } else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT && (metaState & META_TAB) != 0) { metaState &= ~(META_TAB | META_TRANSIENT); bridge.transport.write(0x09); return true; } } return false; } // check for terminal resizing keys if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) { bridge.increaseFontSize(); return true; } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { bridge.decreaseFontSize(); return true; } // skip keys if we aren't connected yet or have been disconnected if (bridge.isDisconnected() || bridge.transport == null) return false; bridge.resetScrollPosition(); if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getAction() == KeyEvent.ACTION_MULTIPLE) { byte[] input = event.getCharacters().getBytes(encoding); bridge.transport.write(input); return true; } int curMetaState = event.getMetaState(); final int orgMetaState = curMetaState; if ((metaState & META_SHIFT_MASK) != 0) { curMetaState |= KeyEvent.META_SHIFT_ON; } if ((metaState & META_ALT_MASK) != 0) { curMetaState |= KeyEvent.META_ALT_ON; } int key = event.getUnicodeChar(curMetaState); // no hard keyboard? ALT-k should pass through to below if ((orgMetaState & KeyEvent.META_ALT_ON) != 0 && (!hardKeyboard || hardKeyboardHidden)) { key = 0; } if ((key & KeyCharacterMap.COMBINING_ACCENT) != 0) { mDeadKey = key & KeyCharacterMap.COMBINING_ACCENT_MASK; return true; } if (mDeadKey != 0) { key = KeyCharacterMap.getDeadChar(mDeadKey, keyCode); mDeadKey = 0; } final boolean printing = (key != 0 && keyCode != KeyEvent.KEYCODE_ENTER); // otherwise pass through to existing session // print normal keys if (printing) { metaState &= ~(META_SLASH | META_TAB); // Remove shift and alt modifiers final int lastMetaState = metaState; metaState &= ~(META_SHIFT_ON | META_ALT_ON); if (metaState != lastMetaState) { bridge.redraw(); } if ((metaState & META_CTRL_MASK) != 0) { metaState &= ~META_CTRL_ON; bridge.redraw(); // If there is no hard keyboard or there is a hard keyboard currently hidden, // CTRL-1 through CTRL-9 will send F1 through F9 if ((!hardKeyboard || (hardKeyboard && hardKeyboardHidden)) && sendFunctionKey(keyCode)) return true; key = keyAsControl(key); } // handle pressing f-keys if ((hardKeyboard && !hardKeyboardHidden) && (curMetaState & KeyEvent.META_SHIFT_ON) != 0 && sendFunctionKey(keyCode)) return true; if (key < 0x80) bridge.transport.write(key); else // TODO write encoding routine that doesn't allocate each time bridge.transport.write(new String(Character.toChars(key)).getBytes(encoding)); return true; } // send ctrl and meta-keys as appropriate if (!hardKeyboard || hardKeyboardHidden) { int k = event.getUnicodeChar(0); int k0 = k; boolean sendCtrl = false; boolean sendMeta = false; if (k != 0) { if ((orgMetaState & HC_META_CTRL_ON) != 0) { k = keyAsControl(k); if (k != k0) sendCtrl = true; // send F1-F10 via CTRL-1 through CTRL-0 if (!sendCtrl && sendFunctionKey(keyCode)) return true; } else if ((orgMetaState & KeyEvent.META_ALT_ON) != 0) { sendMeta = true; sendEscape(); } if (sendMeta || sendCtrl) { bridge.transport.write(k); return true; } } } // try handling keymode shortcuts if (hardKeyboard && !hardKeyboardHidden && event.getRepeatCount() == 0) { if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) { switch (keyCode) { case KeyEvent.KEYCODE_ALT_RIGHT: metaState |= META_SLASH; return true; case KeyEvent.KEYCODE_SHIFT_RIGHT: metaState |= META_TAB; return true; case KeyEvent.KEYCODE_SHIFT_LEFT: metaPress(META_SHIFT_ON); return true; case KeyEvent.KEYCODE_ALT_LEFT: metaPress(META_ALT_ON); return true; } } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) { switch (keyCode) { case KeyEvent.KEYCODE_ALT_LEFT: metaState |= META_SLASH; return true; case KeyEvent.KEYCODE_SHIFT_LEFT: metaState |= META_TAB; return true; case KeyEvent.KEYCODE_SHIFT_RIGHT: metaPress(META_SHIFT_ON); return true; case KeyEvent.KEYCODE_ALT_RIGHT: metaPress(META_ALT_ON); return true; } } else { switch (keyCode) { case KeyEvent.KEYCODE_ALT_LEFT: case KeyEvent.KEYCODE_ALT_RIGHT: metaPress(META_ALT_ON); return true; case KeyEvent.KEYCODE_SHIFT_LEFT: case KeyEvent.KEYCODE_SHIFT_RIGHT: metaPress(META_SHIFT_ON); return true; } } } // look for special chars switch (keyCode) { case KEYCODE_ESCAPE: sendEscape(); return true; case KeyEvent.KEYCODE_TAB: bridge.transport.write(0x09); return true; case KeyEvent.KEYCODE_SEARCH: // check to see which shortcut the search button triggers String search = manager.prefs.getString(PreferenceConstants.SEARCH, PreferenceConstants.SEARCH_ESC); if (PreferenceConstants.SEARCH_BACKBTN.equals(search)) { // TODO: figure out what to do here! } else if (PreferenceConstants.SEARCH_ESC.equals(search)) { sendEscape(); } return true; case KeyEvent.KEYCODE_CAMERA: // check to see which shortcut the camera button triggers String camera = manager.prefs.getString( PreferenceConstants.CAMERA, PreferenceConstants.CAMERA_CTRLA_SPACE); if (PreferenceConstants.CAMERA_CTRLA_SPACE.equals(camera)) { bridge.transport.write(0x01); bridge.transport.write(' '); } else if (PreferenceConstants.CAMERA_CTRLA.equals(camera)) { bridge.transport.write(0x01); } else if (PreferenceConstants.CAMERA_ESC.equals(camera)) { ((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0); } else if (PreferenceConstants.CAMERA_ESC_A.equals(camera)) { ((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0); bridge.transport.write('a'); } break; case KeyEvent.KEYCODE_DEL: ((vt320) buffer).keyPressed(vt320.KEY_BACK_SPACE, ' ', getStateForBuffer()); metaState &= ~META_TRANSIENT; return true; case KeyEvent.KEYCODE_ENTER: ((vt320) buffer).keyTyped(vt320.KEY_ENTER, ' ', 0); metaState &= ~META_TRANSIENT; return true; case KeyEvent.KEYCODE_DPAD_LEFT: if (selectingForCopy) { selectionArea.decrementColumn(); bridge.redraw(); } else { ((vt320) buffer).keyPressed(vt320.KEY_LEFT, ' ', getStateForBuffer()); metaState &= ~META_TRANSIENT; bridge.tryKeyVibrate(); } return true; case KeyEvent.KEYCODE_DPAD_UP: if (selectingForCopy) { selectionArea.decrementRow(); bridge.redraw(); } else { ((vt320) buffer).keyPressed(vt320.KEY_UP, ' ', getStateForBuffer()); metaState &= ~META_TRANSIENT; bridge.tryKeyVibrate(); } return true; case KeyEvent.KEYCODE_DPAD_DOWN: if (selectingForCopy) { selectionArea.incrementRow(); bridge.redraw(); } else { ((vt320) buffer).keyPressed(vt320.KEY_DOWN, ' ', getStateForBuffer()); metaState &= ~META_TRANSIENT; bridge.tryKeyVibrate(); } return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (selectingForCopy) { selectionArea.incrementColumn(); bridge.redraw(); } else { ((vt320) buffer).keyPressed(vt320.KEY_RIGHT, ' ', getStateForBuffer()); metaState &= ~META_TRANSIENT; bridge.tryKeyVibrate(); } return true; case KeyEvent.KEYCODE_DPAD_CENTER: if (selectingForCopy) { if (selectionArea.isSelectingOrigin()) selectionArea.finishSelectingOrigin(); else { if (clipboard != null) { // copy selected area to clipboard String copiedText = selectionArea.copyFrom(buffer); clipboard.setText(copiedText); // XXX STOPSHIP // manager.notifyUser(manager.getString( // R.string.console_copy_done, // copiedText.length())); selectingForCopy = false; selectionArea.reset(); } } } else { if ((metaState & META_CTRL_ON) != 0) { sendEscape(); metaState &= ~META_CTRL_ON; } else metaPress(META_CTRL_ON); } bridge.redraw(); return true; case KeyEvent.KEYCODE_PAGE_UP: ((vt320) buffer).keyPressed(vt320.KEY_PAGE_UP, ' ', getStateForBuffer()); metaState &= ~META_TRANSIENT; bridge.tryKeyVibrate(); return true; case KeyEvent.KEYCODE_PAGE_DOWN: ((vt320) buffer).keyPressed(vt320.KEY_PAGE_DOWN, ' ', getStateForBuffer()); metaState &= ~META_TRANSIENT; bridge.tryKeyVibrate(); return true; case KeyEvent.KEYCODE_MOVE_HOME: // ((vt320) buffer).keyPressed(vt320.KEY_HOME, ' ',getStateForBuffer()); ((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0); bridge.transport.write("[1~".getBytes()); metaState &= ~META_TRANSIENT; bridge.tryKeyVibrate(); return true; case KeyEvent.KEYCODE_MOVE_END: // ((vt320) buffer).keyPressed(vt320.KEY_END, ' ',getStateForBuffer()); ((vt320) buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0); bridge.transport.write("[4~".getBytes()); metaState &= ~META_TRANSIENT; bridge.tryKeyVibrate(); return true; } } catch (IOException e) { Log.e(TAG, "Problem while trying to handle an onKey() event", e); try { bridge.transport.flush(); } catch (IOException ioe) { Log.d(TAG, "Our transport was closed, dispatching disconnect event"); bridge.dispatchDisconnect(false); } } catch (NullPointerException npe) { Log.d(TAG, "Input before connection established ignored."); return true; } return false; }