public boolean handleKeyCode(int keyCode, KeyEvent event, boolean appMode) throws IOException {
    String code = null;
    if (event != null) {
      int keyMod = 0;
      // META_CTRL_ON was added only in API 11, so don't use it,
      // use our own tracking of Ctrl key instead.
      // (event.getMetaState() & META_CTRL_ON) != 0
      if (mHardwareControlKey || mControlKey.isActive()) {
        keyMod |= KEYMOD_CTRL;
      }
      if ((event.getMetaState() & META_ALT_ON) != 0) {
        keyMod |= KEYMOD_ALT;
      }
      if ((event.getMetaState() & META_SHIFT_ON) != 0) {
        keyMod |= KEYMOD_SHIFT;
      }
      // First try to map scancode
      code = mKeyMap.get(event.getScanCode() | KEYMOD_SCAN | keyMod);
      if (code == null) {
        code = mKeyMap.get(keyCode | keyMod);
      }
    }

    if (code == null && keyCode >= 0 && keyCode < mKeyCodes.length) {
      if (appMode) {
        code = mAppKeyCodes[keyCode];
      }
      if (code == null) {
        code = mKeyCodes[keyCode];
      }
    }

    if (code != null) {
      if (EmulatorDebug.LOG_CHARACTERS_FLAG) {
        byte[] bytes = code.getBytes();
        Log.d(
            EmulatorDebug.LOG_TAG,
            "Out: '" + EmulatorDebug.bytesToString(bytes, 0, bytes.length) + "'");
      }
      mTermSession.write(code);
      return true;
    }
    return false;
  }
 public boolean isCtrlActive() {
   return mControlKey.isActive();
 }
 public boolean isAltActive() {
   return mAltKey.isActive();
 }
  /**
   * Handle a keyDown event.
   *
   * @param keyCode the keycode of the keyDown event
   */
  public void keyDown(int keyCode, KeyEvent event, boolean appMode, boolean allowToggle)
      throws IOException {
    if (LOG_KEYS) {
      Log.i(TAG, "keyDown(" + keyCode + "," + event + "," + appMode + "," + allowToggle + ")");
    }
    if (handleKeyCode(keyCode, event, appMode)) {
      return;
    }
    int result = -1;
    boolean chordedCtrl = false;
    boolean setHighBit = false;
    switch (keyCode) {
      case KeyEvent.KEYCODE_ALT_RIGHT:
      case KeyEvent.KEYCODE_ALT_LEFT:
        if (allowToggle) {
          mAltKey.onPress();
          updateCursorMode();
        }
        break;

      case KeyEvent.KEYCODE_SHIFT_LEFT:
      case KeyEvent.KEYCODE_SHIFT_RIGHT:
        if (allowToggle) {
          mCapKey.onPress();
          updateCursorMode();
        }
        break;

      case KEYCODE_CTRL_LEFT:
      case KEYCODE_CTRL_RIGHT:
        // Ignore the control key.
        return;

      case KEYCODE_CAPS_LOCK:
        // Ignore the capslock key.
        return;

      case KEYCODE_FUNCTION:
        // Ignore the function key.
        return;

      case KeyEvent.KEYCODE_BACK:
        result = mBackKeyCode;
        break;

      default:
        {
          int metaState = event.getMetaState();
          chordedCtrl = ((META_CTRL_ON & metaState) != 0);
          boolean effectiveCaps = allowToggle && (mCapKey.isActive());
          boolean effectiveAlt = allowToggle && mAltKey.isActive();
          int effectiveMetaState = metaState & (~META_CTRL_MASK);
          if (effectiveCaps) {
            effectiveMetaState |= KeyEvent.META_SHIFT_ON;
          }
          if (!allowToggle && (effectiveMetaState & META_ALT_ON) != 0) {
            effectiveAlt = true;
          }
          if (effectiveAlt) {
            if (mAltSendsEsc) {
              mTermSession.write(new byte[] {0x1b}, 0, 1);
              effectiveMetaState &= ~KeyEvent.META_ALT_MASK;
            } else if (SUPPORT_8_BIT_META) {
              setHighBit = true;
              effectiveMetaState &= ~KeyEvent.META_ALT_MASK;
            } else {
              // Legacy behavior: Pass Alt through to allow composing characters.
              effectiveMetaState |= KeyEvent.META_ALT_ON;
            }
          }

          // Note: The Hacker keyboard IME key labeled Alt actually sends Meta.

          if ((metaState & KeyEvent.META_META_ON) != 0) {
            if (mAltSendsEsc) {
              mTermSession.write(new byte[] {0x1b}, 0, 1);
              effectiveMetaState &= ~KeyEvent.META_META_MASK;
            } else {
              if (SUPPORT_8_BIT_META) {
                setHighBit = true;
                effectiveMetaState &= ~KeyEvent.META_META_MASK;
              }
            }
          }
          result = event.getUnicodeChar(effectiveMetaState);

          if ((result & KeyCharacterMap.COMBINING_ACCENT) != 0) {
            if (LOG_COMBINING_ACCENT) {
              Log.i(TAG, "Got combining accent " + result);
            }
            mCombiningAccent = result & KeyCharacterMap.COMBINING_ACCENT_MASK;
            return;
          }
          if (mCombiningAccent != 0) {
            int unaccentedChar = result;
            result = KeyCharacterMap.getDeadChar(mCombiningAccent, unaccentedChar);
            if (LOG_COMBINING_ACCENT) {
              Log.i(
                  TAG,
                  "getDeadChar(" + mCombiningAccent + ", " + unaccentedChar + ") -> " + result);
            }
            mCombiningAccent = 0;
          }

          break;
        }
    }

    boolean effectiveControl =
        chordedCtrl || mHardwareControlKey || (allowToggle && mControlKey.isActive());
    boolean effectiveFn = allowToggle && mFnKey.isActive();

    result = mapControlChar(effectiveControl, effectiveFn, result);

    if (result >= KEYCODE_OFFSET) {
      handleKeyCode(result - KEYCODE_OFFSET, null, appMode);
    } else if (result >= 0) {
      if (setHighBit) {
        result |= 0x80;
      }
      mTermSession.write(result);
    }
  }
 public int mapControlChar(int ch) {
   return mapControlChar(mHardwareControlKey || mControlKey.isActive(), mFnKey.isActive(), ch);
 }