/**
   * Handle a keyUp event.
   *
   * @param keyCode the keyCode of the keyUp event
   */
  public void keyUp(int keyCode, KeyEvent event) {
    boolean allowToggle = isEventFromToggleDevice(event);
    switch (keyCode) {
      case KeyEvent.KEYCODE_ALT_LEFT:
      case KeyEvent.KEYCODE_ALT_RIGHT:
        if (allowToggle) {
          mAltKey.onRelease();
          updateCursorMode();
        }
        break;
      case KeyEvent.KEYCODE_SHIFT_LEFT:
      case KeyEvent.KEYCODE_SHIFT_RIGHT:
        if (allowToggle) {
          mCapKey.onRelease();
          updateCursorMode();
        }
        break;

      case KEYCODE_CTRL_LEFT:
      case KEYCODE_CTRL_RIGHT:
        // ignore control keys.
        break;

      default:
        // Ignore other keyUps
        break;
    }
  }
 public void handleFnKey(boolean down) {
   if (down) {
     mFnKey.onPress();
   } else {
     mFnKey.onRelease();
   }
   updateCursorMode();
 }
  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();
 }
 private static int getCursorModeHelper(ModifierKey key, int shift) {
   return key.getUIMode() << shift;
 }
  /**
   * 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(boolean control, boolean fn, int ch) {
    int result = ch;
    if (control) {
      // Search is the control key.
      if (result >= 'a' && result <= 'z') {
        result = (char) (result - 'a' + '\001');
      } else if (result >= 'A' && result <= 'Z') {
        result = (char) (result - 'A' + '\001');
      } else if (result == ' ' || result == '2') {
        result = 0;
      } else if (result == '[' || result == '3') {
        result = 27; // ^[ (Esc)
      } else if (result == '\\' || result == '4') {
        result = 28;
      } else if (result == ']' || result == '5') {
        result = 29;
      } else if (result == '^' || result == '6') {
        result = 30; // control-^
      } else if (result == '_' || result == '7') {
        result = 31;
      } else if (result == '8') {
        result = 127; // DEL
      } else if (result == '9') {
        result = KEYCODE_OFFSET + KEYCODE_F11;
      } else if (result == '0') {
        result = KEYCODE_OFFSET + KEYCODE_F12;
      }
    } else if (fn) {
      if (result == 'w' || result == 'W') {
        result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_UP;
      } else if (result == 'a' || result == 'A') {
        result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_LEFT;
      } else if (result == 's' || result == 'S') {
        result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_DOWN;
      } else if (result == 'd' || result == 'D') {
        result = KEYCODE_OFFSET + KeyEvent.KEYCODE_DPAD_RIGHT;
      } else if (result == 'p' || result == 'P') {
        result = KEYCODE_OFFSET + KEYCODE_PAGE_UP;
      } else if (result == 'n' || result == 'N') {
        result = KEYCODE_OFFSET + KEYCODE_PAGE_DOWN;
      } else if (result == 't' || result == 'T') {
        result = KEYCODE_OFFSET + KeyEvent.KEYCODE_TAB;
      } else if (result == 'l' || result == 'L') {
        result = '|';
      } else if (result == 'u' || result == 'U') {
        result = '_';
      } else if (result == 'e' || result == 'E') {
        result = 27; // ^[ (Esc)
      } else if (result == '.') {
        result = 28; // ^\
      } else if (result > '0' && result <= '9') {
        // F1-F9
        result = (char) (result + KEYCODE_OFFSET + KEYCODE_F1 - 1);
      } else if (result == '0') {
        result = KEYCODE_OFFSET + KEYCODE_F10;
      } else if (result == 'i' || result == 'I') {
        result = KEYCODE_OFFSET + KEYCODE_INSERT;
      } else if (result == 'x' || result == 'X') {
        result = KEYCODE_OFFSET + KEYCODE_FORWARD_DEL;
      } else if (result == 'h' || result == 'H') {
        result = KEYCODE_OFFSET + KEYCODE_MOVE_HOME;
      } else if (result == 'f' || result == 'F') {
        result = KEYCODE_OFFSET + KEYCODE_MOVE_END;
      }
    }

    if (result > -1) {
      mAltKey.adjustAfterKeypress();
      mCapKey.adjustAfterKeypress();
      mControlKey.adjustAfterKeypress();
      mFnKey.adjustAfterKeypress();
      updateCursorMode();
    }

    return result;
  }
 public int mapControlChar(int ch) {
   return mapControlChar(mHardwareControlKey || mControlKey.isActive(), mFnKey.isActive(), ch);
 }