blob: adea7a39e158463de95ca91f1f1237c862e57887 [file] [log] [blame]
// Copyright 2010-2014, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "win32/base/keyevent_handler.h"
#include <ime.h>
#include <string.h>
#include <windows.h>
#include <set>
#include "base/logging.h"
#include "base/util.h"
#include "client/client_interface.h"
#include "config/config_handler.h"
#include "session/commands.pb.h"
#include "session/key_info_util.h"
#include "win32/base/conversion_mode_util.h"
#include "win32/base/input_state.h"
#include "win32/base/keyboard.h"
namespace mozc {
namespace win32 {
using commands::KeyEvent;
using commands::Output;
namespace {
// The Mozc protocol has expected the client to send a key event with
// |KeyEvent::HANKAKU| special key as if there was single Hankaku/Zenkaku
// key. This is why we map both VK_DBE_SBCSCHAR and VK_DBE_DBCSCHAR into
// KeyEvent::HANKAKU.
const KeyEvent::SpecialKey kSpecialKeyMap[] = {
KeyEvent::NO_SPECIALKEY, // 0x00:
KeyEvent::NO_SPECIALKEY, // 0x01: VK_LBUTTON
KeyEvent::NO_SPECIALKEY, // 0x02: VK_RBUTTON
KeyEvent::NO_SPECIALKEY, // 0x03: VK_CANCEL
KeyEvent::NO_SPECIALKEY, // 0x04: VK_MBUTTON
KeyEvent::NO_SPECIALKEY, // 0x05: VK_XBUTTON1
KeyEvent::NO_SPECIALKEY, // 0x06: VK_XBUTTON2
KeyEvent::NO_SPECIALKEY, // 0x07:
KeyEvent::BACKSPACE, // 0x08: VK_BACK
KeyEvent::TAB, // 0x09: VK_TAB
KeyEvent::NO_SPECIALKEY, // 0x0A:
KeyEvent::NO_SPECIALKEY, // 0x0B:
KeyEvent::CLEAR, // 0x0C: VK_CLEAR
KeyEvent::ENTER, // 0x0D: VK_RETURN
KeyEvent::NO_SPECIALKEY, // 0x0E:
KeyEvent::NO_SPECIALKEY, // 0x0F:
KeyEvent::NO_SPECIALKEY, // 0x10: VK_SHIFT
KeyEvent::NO_SPECIALKEY, // 0x11: VK_CONTROL
KeyEvent::NO_SPECIALKEY, // 0x12: VK_MENU
KeyEvent::NO_SPECIALKEY, // 0x13: VK_PAUSE
KeyEvent::CAPS_LOCK, // 0x14: VK_CAPITAL
KeyEvent::NO_SPECIALKEY, // 0x15: VK_HANGUL, VK_KANA
KeyEvent::NO_SPECIALKEY, // 0x16:
KeyEvent::NO_SPECIALKEY, // 0x17: VK_JUNJA
KeyEvent::NO_SPECIALKEY, // 0x18: VK_FINAL
// VK_KANJI is very special in IMM32 mode. It activates IME in OS-side
// regardless of the actual key binding in an IME. On the other hand,
// this automatic activation does not happen in TSF mode. To work around
// this anomaly, we map VK_KANJI to KeyEvent::NO_SPECIALKEY instead of
// KeyEvent::KANJI in IMM32 mode and map VK_KANJI to KeyEvent::HANKAKU
// via VK_DBE_DBCSCHAR in TSF mode. See b/7970379 for the background.
KeyEvent::NO_SPECIALKEY, // 0x19: VK_HANJA, VK_KANJI
KeyEvent::NO_SPECIALKEY, // 0x1A:
KeyEvent::ESCAPE, // 0x1B: VK_ESCAPE
KeyEvent::HENKAN, // 0x1C: VK_CONVERT
KeyEvent::MUHENKAN, // 0x1D: VK_NONCONVERT
KeyEvent::NO_SPECIALKEY, // 0x1E: VK_ACCEPT
KeyEvent::NO_SPECIALKEY, // 0x1F: VK_MODECHANGE
KeyEvent::SPACE, // 0x20: VK_SPACE
KeyEvent::PAGE_UP, // 0x21: VK_PRIOR
KeyEvent::PAGE_DOWN, // 0x22: VK_NEXT
KeyEvent::END, // 0x23: VK_END
KeyEvent::HOME, // 0x24: VK_HOME
KeyEvent::LEFT, // 0x25: VK_LEFT
KeyEvent::UP, // 0x26: VK_UP
KeyEvent::RIGHT, // 0x27: VK_RIGHT
KeyEvent::DOWN, // 0x28: VK_DOWN
KeyEvent::NO_SPECIALKEY, // 0x29: VK_SELECT
KeyEvent::NO_SPECIALKEY, // 0x2A: VK_PRINT
KeyEvent::NO_SPECIALKEY, // 0x2B: VK_EXECUTE
KeyEvent::NO_SPECIALKEY, // 0x2C: VK_SNAPSHOT
KeyEvent::INSERT, // 0x2D: VK_INSERT
KeyEvent::DEL, // 0x2E: VK_DELETE
KeyEvent::NO_SPECIALKEY, // 0x2F: VK_HELP
KeyEvent::NO_SPECIALKEY, // 0x30: VK_0
KeyEvent::NO_SPECIALKEY, // 0x31: VK_1
KeyEvent::NO_SPECIALKEY, // 0x32: VK_2
KeyEvent::NO_SPECIALKEY, // 0x33: VK_3
KeyEvent::NO_SPECIALKEY, // 0x34: VK_4
KeyEvent::NO_SPECIALKEY, // 0x35: VK_5
KeyEvent::NO_SPECIALKEY, // 0x36: VK_6
KeyEvent::NO_SPECIALKEY, // 0x37: VK_7
KeyEvent::NO_SPECIALKEY, // 0x38: VK_8
KeyEvent::NO_SPECIALKEY, // 0x39: VK_9
KeyEvent::NO_SPECIALKEY, // 0x3A:
KeyEvent::NO_SPECIALKEY, // 0x3B:
KeyEvent::NO_SPECIALKEY, // 0x3C:
KeyEvent::NO_SPECIALKEY, // 0x3D:
KeyEvent::NO_SPECIALKEY, // 0x3E:
KeyEvent::NO_SPECIALKEY, // 0x3F:
KeyEvent::NO_SPECIALKEY, // 0x40:
KeyEvent::NO_SPECIALKEY, // 0x41: VK_A
KeyEvent::NO_SPECIALKEY, // 0x42: VK_B
KeyEvent::NO_SPECIALKEY, // 0x43: VK_C
KeyEvent::NO_SPECIALKEY, // 0x44: VK_D
KeyEvent::NO_SPECIALKEY, // 0x45: VK_E
KeyEvent::NO_SPECIALKEY, // 0x46: VK_F
KeyEvent::NO_SPECIALKEY, // 0x47: VK_G
KeyEvent::NO_SPECIALKEY, // 0x48: VK_H
KeyEvent::NO_SPECIALKEY, // 0x49: VK_I
KeyEvent::NO_SPECIALKEY, // 0x4A: VK_J
KeyEvent::NO_SPECIALKEY, // 0x4B: VK_K
KeyEvent::NO_SPECIALKEY, // 0x4C: VK_L
KeyEvent::NO_SPECIALKEY, // 0x4D: VK_M
KeyEvent::NO_SPECIALKEY, // 0x4E: VK_N
KeyEvent::NO_SPECIALKEY, // 0x4F: VK_O
KeyEvent::NO_SPECIALKEY, // 0x50: VK_P
KeyEvent::NO_SPECIALKEY, // 0x51: VK_Q
KeyEvent::NO_SPECIALKEY, // 0x52: VK_R
KeyEvent::NO_SPECIALKEY, // 0x53: VK_S
KeyEvent::NO_SPECIALKEY, // 0x54: VK_T
KeyEvent::NO_SPECIALKEY, // 0x55: VK_U
KeyEvent::NO_SPECIALKEY, // 0x56: VK_V
KeyEvent::NO_SPECIALKEY, // 0x57: VK_W
KeyEvent::NO_SPECIALKEY, // 0x58: VK_X
KeyEvent::NO_SPECIALKEY, // 0x59: VK_Y
KeyEvent::NO_SPECIALKEY, // 0x5A: VK_Z
KeyEvent::NO_SPECIALKEY, // 0x5B: VK_LWIN
KeyEvent::NO_SPECIALKEY, // 0x5C: VK_RWIN
KeyEvent::NO_SPECIALKEY, // 0x5D: VK_APPS
KeyEvent::NO_SPECIALKEY, // 0x5E:
KeyEvent::NO_SPECIALKEY, // 0x5F: VK_SLEEP
KeyEvent::NUMPAD0, // 0x60: VK_NUMPAD0
KeyEvent::NUMPAD1, // 0x61: VK_NUMPAD1
KeyEvent::NUMPAD2, // 0x62: VK_NUMPAD2
KeyEvent::NUMPAD3, // 0x63: VK_NUMPAD3
KeyEvent::NUMPAD4, // 0x64: VK_NUMPAD4
KeyEvent::NUMPAD5, // 0x65: VK_NUMPAD5
KeyEvent::NUMPAD6, // 0x66: VK_NUMPAD6
KeyEvent::NUMPAD7, // 0x67: VK_NUMPAD7
KeyEvent::NUMPAD8, // 0x68: VK_NUMPAD8
KeyEvent::NUMPAD9, // 0x69: VK_NUMPAD9
KeyEvent::MULTIPLY, // 0x6A: VK_MULTIPLY
KeyEvent::ADD, // 0x6B: VK_ADD
KeyEvent::SEPARATOR, // 0x6C: VK_SEPARATOR
KeyEvent::SUBTRACT, // 0x6D: VK_SUBTRACT
KeyEvent::DECIMAL, // 0x6E: VK_DECIMAL
KeyEvent::DIVIDE, // 0x6F: VK_DIVIDE
KeyEvent::F1, // 0x70: VK_F1
KeyEvent::F2, // 0x71: VK_F2
KeyEvent::F3, // 0x72: VK_F3
KeyEvent::F4, // 0x73: VK_F4
KeyEvent::F5, // 0x74: VK_F5
KeyEvent::F6, // 0x75: VK_F6
KeyEvent::F7, // 0x76: VK_F7
KeyEvent::F8, // 0x77: VK_F8
KeyEvent::F9, // 0x78: VK_F9
KeyEvent::F10, // 0x79: VK_F10
KeyEvent::F11, // 0x7A: VK_F11
KeyEvent::F12, // 0x7B: VK_F12
KeyEvent::F13, // 0x7C: VK_F13
KeyEvent::F14, // 0x7D: VK_F14
KeyEvent::F15, // 0x7E: VK_F15
KeyEvent::F16, // 0x7F: VK_F16
KeyEvent::F17, // 0x80: VK_F17
KeyEvent::F18, // 0x81: VK_F18
KeyEvent::F19, // 0x82: VK_F19
KeyEvent::F20, // 0x83: VK_F20
KeyEvent::F21, // 0x84: VK_F21
KeyEvent::F22, // 0x85: VK_F22
KeyEvent::F23, // 0x86: VK_F23
KeyEvent::F24, // 0x87: VK_F24
KeyEvent::NO_SPECIALKEY, // 0x88:
KeyEvent::NO_SPECIALKEY, // 0x89:
KeyEvent::NO_SPECIALKEY, // 0x8A:
KeyEvent::NO_SPECIALKEY, // 0x8B:
KeyEvent::NO_SPECIALKEY, // 0x8C:
KeyEvent::NO_SPECIALKEY, // 0x8D:
KeyEvent::NO_SPECIALKEY, // 0x8E:
KeyEvent::NO_SPECIALKEY, // 0x8F:
KeyEvent::NO_SPECIALKEY, // 0x90: VK_NUMLOCK
KeyEvent::NO_SPECIALKEY, // 0x91: VK_SCROLL
KeyEvent::NO_SPECIALKEY, // 0x92: VK_OEM_FJ_JISHO, VK_OEM_NEC_EQUAL
KeyEvent::NO_SPECIALKEY, // 0x93: VK_OEM_FJ_MASSHOU
KeyEvent::NO_SPECIALKEY, // 0x94: VK_OEM_FJ_TOUROKU
KeyEvent::NO_SPECIALKEY, // 0x95: VK_OEM_FJ_LOYA
KeyEvent::NO_SPECIALKEY, // 0x96: VK_OEM_FJ_ROYA
KeyEvent::NO_SPECIALKEY, // 0x97:
KeyEvent::NO_SPECIALKEY, // 0x98:
KeyEvent::NO_SPECIALKEY, // 0x99:
KeyEvent::NO_SPECIALKEY, // 0x9A:
KeyEvent::NO_SPECIALKEY, // 0x9B:
KeyEvent::NO_SPECIALKEY, // 0x9C:
KeyEvent::NO_SPECIALKEY, // 0x9D:
KeyEvent::NO_SPECIALKEY, // 0x9E:
KeyEvent::NO_SPECIALKEY, // 0x9F:
KeyEvent::NO_SPECIALKEY, // 0xA0: VK_LSHIFT
KeyEvent::NO_SPECIALKEY, // 0xA1: VK_RSHIFT
KeyEvent::NO_SPECIALKEY, // 0xA2: VK_LCONTROL
KeyEvent::NO_SPECIALKEY, // 0xA3: VK_RCONTROL
KeyEvent::NO_SPECIALKEY, // 0xA4: VK_LMENU
KeyEvent::NO_SPECIALKEY, // 0xA5: VK_RMENU
KeyEvent::NO_SPECIALKEY, // 0xA6: VK_BROWSER_BACK
KeyEvent::NO_SPECIALKEY, // 0xA7: VK_BROWSER_FORWARD
KeyEvent::NO_SPECIALKEY, // 0xA8: VK_BROWSER_REFRESH
KeyEvent::NO_SPECIALKEY, // 0xA9: VK_BROWSER_STOP
KeyEvent::NO_SPECIALKEY, // 0xAA: VK_BROWSER_SEARCH
KeyEvent::NO_SPECIALKEY, // 0xAB: VK_BROWSER_FAVORITES
KeyEvent::NO_SPECIALKEY, // 0xAC: VK_BROWSER_HOME
KeyEvent::NO_SPECIALKEY, // 0xAD: VK_VOLUME_MUTE
KeyEvent::NO_SPECIALKEY, // 0xAE: VK_VOLUME_DOWN
KeyEvent::NO_SPECIALKEY, // 0xAF: VK_VOLUME_UP
KeyEvent::NO_SPECIALKEY, // 0xB0: VK_MEDIA_NEXT_TRACK
KeyEvent::NO_SPECIALKEY, // 0xB1: VK_MEDIA_PREV_TRACK
KeyEvent::NO_SPECIALKEY, // 0xB2: VK_MEDIA_STOP
KeyEvent::NO_SPECIALKEY, // 0xB3: VK_MEDIA_PLAY_PAUSE
KeyEvent::NO_SPECIALKEY, // 0xB4: VK_LAUNCH_MAIL
KeyEvent::NO_SPECIALKEY, // 0xB5: VK_LAUNCH_MEDIA_SELECT
KeyEvent::NO_SPECIALKEY, // 0xB6: VK_LAUNCH_APP1
KeyEvent::NO_SPECIALKEY, // 0xB7: VK_LAUNCH_APP2
KeyEvent::NO_SPECIALKEY, // 0xB8:
KeyEvent::NO_SPECIALKEY, // 0xB9:
KeyEvent::NO_SPECIALKEY, // 0xBA: VK_OEM_1
KeyEvent::NO_SPECIALKEY, // 0xBB: VK_OEM_PLUS
KeyEvent::NO_SPECIALKEY, // 0xBC: VK_OEM_COMMA
KeyEvent::NO_SPECIALKEY, // 0xBD: VK_OEM_MINUS
KeyEvent::NO_SPECIALKEY, // 0xBE: VK_OEM_PERIOD
KeyEvent::NO_SPECIALKEY, // 0xBF: VK_OEM_2
KeyEvent::NO_SPECIALKEY, // 0xC0: VK_OEM_3
KeyEvent::NO_SPECIALKEY, // 0xC1: VK_ABNT_C1
// The numpad comma on the Apple Japanese 109 keyboard is somehow mapped into
// VK_ABNT_C2, which is only defined in kbd.h.
// See also http://blogs.msdn.com/b/michkap/archive/2006/10/07/799605.aspx
// See also b/6639635.
KeyEvent::COMMA, // 0xC2: VK_ABNT_C2
KeyEvent::NO_SPECIALKEY, // 0xC3:
KeyEvent::NO_SPECIALKEY, // 0xC4:
KeyEvent::NO_SPECIALKEY, // 0xC5:
KeyEvent::NO_SPECIALKEY, // 0xC6:
KeyEvent::NO_SPECIALKEY, // 0xC7:
KeyEvent::NO_SPECIALKEY, // 0xC8:
KeyEvent::NO_SPECIALKEY, // 0xC9:
KeyEvent::NO_SPECIALKEY, // 0xCA:
KeyEvent::NO_SPECIALKEY, // 0xCB:
KeyEvent::NO_SPECIALKEY, // 0xCC:
KeyEvent::NO_SPECIALKEY, // 0xCD:
KeyEvent::NO_SPECIALKEY, // 0xCE:
KeyEvent::NO_SPECIALKEY, // 0xCF:
KeyEvent::NO_SPECIALKEY, // 0xD0:
KeyEvent::NO_SPECIALKEY, // 0xD1:
KeyEvent::NO_SPECIALKEY, // 0xD2:
KeyEvent::NO_SPECIALKEY, // 0xD3:
KeyEvent::NO_SPECIALKEY, // 0xD4:
KeyEvent::NO_SPECIALKEY, // 0xD5:
KeyEvent::NO_SPECIALKEY, // 0xD6:
KeyEvent::NO_SPECIALKEY, // 0xD7:
KeyEvent::NO_SPECIALKEY, // 0xD8:
KeyEvent::NO_SPECIALKEY, // 0xD9:
KeyEvent::NO_SPECIALKEY, // 0xDA:
KeyEvent::NO_SPECIALKEY, // 0xDB: VK_OEM_4
KeyEvent::NO_SPECIALKEY, // 0xDC: VK_OEM_5
KeyEvent::NO_SPECIALKEY, // 0xDD: VK_OEM_6
KeyEvent::NO_SPECIALKEY, // 0xDE: VK_OEM_7
KeyEvent::NO_SPECIALKEY, // 0xDF: VK_OEM_8
KeyEvent::NO_SPECIALKEY, // 0xE0:
KeyEvent::NO_SPECIALKEY, // 0xE1: VK_OEM_AX
KeyEvent::NO_SPECIALKEY, // 0xE2: VK_OEM_102
KeyEvent::NO_SPECIALKEY, // 0xE3: VK_ICO_HELP
KeyEvent::NO_SPECIALKEY, // 0xE4: VK_ICO_00
KeyEvent::NO_SPECIALKEY, // 0xE5: VK_PROCESSKEY
KeyEvent::NO_SPECIALKEY, // 0xE6: VK_ICO_CLEAR
KeyEvent::NO_SPECIALKEY, // 0xE7: VK_PACKET
KeyEvent::NO_SPECIALKEY, // 0xE8:
KeyEvent::NO_SPECIALKEY, // 0xE9:
KeyEvent::NO_SPECIALKEY, // 0xEA:
KeyEvent::NO_SPECIALKEY, // 0xEB:
KeyEvent::NO_SPECIALKEY, // 0xEC:
KeyEvent::NO_SPECIALKEY, // 0xED:
KeyEvent::NO_SPECIALKEY, // 0xEE:
KeyEvent::NO_SPECIALKEY, // 0xEF:
KeyEvent::EISU, // 0xF0: VK_DBE_ALPHANUMERIC
KeyEvent::KATAKANA, // 0xF1: VK_DBE_KATAKANA
KeyEvent::KANA, // 0xF2: VK_DBE_HIRAGANA
KeyEvent::HANKAKU, // 0xF3: VK_DBE_SBCSCHAR
KeyEvent::HANKAKU, // 0xF4: VK_DBE_DBCSCHAR
KeyEvent::NO_SPECIALKEY, // 0xF5: VK_DBE_ROMAN
KeyEvent::NO_SPECIALKEY, // 0xF6: VK_DBE_NOROMAN
KeyEvent::NO_SPECIALKEY, // 0xF7: VK_DBE_ENTERWORDREGISTERMODE
KeyEvent::NO_SPECIALKEY, // 0xF8: VK_DBE_ENTERIMECONFIGMODE
KeyEvent::NO_SPECIALKEY, // 0xF9: VK_DBE_FLUSHSTRING
KeyEvent::NO_SPECIALKEY, // 0xFA: VK_DBE_CODEINPUT
KeyEvent::NO_SPECIALKEY, // 0xFB: VK_DBE_NOCODEINPUT
KeyEvent::NO_SPECIALKEY, // 0xFC: VK_DBE_DETERMINESTRING
KeyEvent::NO_SPECIALKEY, // 0xFD: VK_DBE_ENTERDLGCONVERSIONMODE
KeyEvent::NO_SPECIALKEY, // 0xFE:
KeyEvent::NO_SPECIALKEY, // 0xFF:
};
void ClearModifyerKeyIfNeeded(
const KeyEvent *key, set<KeyEvent::ModifierKey> *modifiers) {
if (key == nullptr) {
return;
}
if (modifiers == nullptr) {
return;
}
if (key->has_special_key()) {
switch (key->special_key()) {
// Clear modifier keys when the key is filtered in
// KeyBindingFilter::Encode in gui/config_dialog/keybinding_editor.cc
case KeyEvent::EISU: // VK_DBE_ALPHANUMERIC
case KeyEvent::HANKAKU: // VK_DBE_SBCSCHAR or VK_DBE_DBCSCHAR
case KeyEvent::KANA: // VK_DBE_HIRAGANA
case KeyEvent::KATAKANA: // VK_DBE_KATAKANA
modifiers->clear();
break;
default:
// Do nothing.
break;
}
}
}
// See b/2576120 for details.
bool IsNotimplementedKey(const VirtualKey &virtual_key) {
switch (virtual_key.virtual_key()) {
case VK_DBE_ROMAN: // Changes the mode to Roman characters.
case VK_DBE_NOROMAN: // Changes the mode to non-Roman characters.
// Cuurently these keys are handled by the client, NOT the server.
// See b/3118905
// TODO(yukawa, komatsu): Handle these keys in the server.
return true;
case VK_DBE_ENTERWORDREGISTERMODE:
// Activates the word registration dialog box.
// Ctrl+Alt+Muhenkan on 106 Japanese Keyboard.
return true;
case VK_DBE_ENTERIMECONFIGMODE:
// Activates a dialog box for setting up an IME environment.
// Ctrl+Alt+Hankaku/Zenkaku on 106 Japanese Keyboard.
return true;
case VK_DBE_FLUSHSTRING:
// Deletes the undetermined string without determining it.
return true;
case VK_DBE_CODEINPUT:
// Changes the mode to code input.
return true;
case VK_DBE_NOCODEINPUT:
// Changes the mode to no-code input.
return true;
case VK_DBE_DETERMINESTRING:
return true;
case VK_DBE_ENTERDLGCONVERSIONMODE:
return true;
default:
return false;
}
}
bool ConvertToKeyEventMain(const VirtualKey &virtual_key,
BYTE scan_code,
bool is_key_down,
bool is_menu_active,
const InputBehavior &behavior,
const InputState &ime_state,
const KeyboardStatus &keyboard_status,
Win32KeyboardInterface *keyboard,
commands::KeyEvent *key,
set<KeyEvent::ModifierKey> *modifer_keys) {
if (key == nullptr) {
return false;
}
key->Clear();
if (modifer_keys == nullptr) {
return false;
}
modifer_keys->clear();
// Support VK_PACKET.
if (virtual_key.virtual_key() == VK_PACKET) {
const char32 character = virtual_key.unicode_char();
string utf8_characters;
Util::UCS4ToUTF8(character, &utf8_characters);
if (utf8_characters.empty()) {
return false;
}
// Setting |key_string| only to pass an arbitrary character to the
// converter.
key->set_key_string(utf8_characters);
return true;
}
// TODO(yukawa): Distinguish left key from right key to fix b/2674446.
if (keyboard_status.IsPressed(VK_SHIFT)) {
modifer_keys->insert(KeyEvent::SHIFT);
}
if (keyboard_status.IsPressed(VK_CONTROL)) {
modifer_keys->insert(KeyEvent::CTRL);
}
if (keyboard_status.IsPressed(VK_MENU)) {
modifer_keys->insert(KeyEvent::ALT);
}
if (keyboard_status.IsPressed(VK_CAPITAL)) {
modifer_keys->insert(KeyEvent::CAPS);
}
const KeyEvent::SpecialKey special_key =
kSpecialKeyMap[virtual_key.virtual_key()];
if (special_key != KeyEvent::NO_SPECIALKEY) {
key->set_special_key(special_key);
// Currently the Mozc server expects that the modifier keys are always
// empty for some special keys. So we have to clear the modifier keys if
// needed.
ClearModifyerKeyIfNeeded(key, modifer_keys);
return true;
}
// Modifier keys should be handled.
// Due to the anomaly of Mozc protocol, |modifer_keys| should be set even
// when this is the key-up message of the modifier key.
switch (virtual_key.virtual_key()) {
case VK_SHIFT:
modifer_keys->insert(KeyEvent::SHIFT);
return true;
case VK_CONTROL:
modifer_keys->insert(KeyEvent::CTRL);
return true;
case VK_MENU:
modifer_keys->insert(KeyEvent::ALT);
return true;
case VK_CAPITAL:
modifer_keys->insert(KeyEvent::CAPS);
return true;
}
// The high-order bit of this value is set if the key is up.
// http://msdn.microsoft.com/en-us/library/ms646322.aspx
const UINT to_unicode_scancode =
static_cast<UINT>(scan_code) | (is_key_down ? 0 : 0x8000);
const DWORD to_unicode_flag = (is_menu_active ? 1 : 0);
KeyboardStatus keyboard_status_wo_kana_lock = keyboard_status;
keyboard_status_wo_kana_lock.SetState(VK_KANA, 0);
bool has_valid_key_string = false;
// Instead of using the actual toggle state of Kana-lock key, an expected
// toggle state of the Kana-lock is emulated based on the IME open/close
// state and conversion mode. See b/3046717 for details.
// Note that we never set |key_string| when Ctrl key is pressed because
// no valid Kana character will be generated with Ctrl key. See b/9684668.
const bool use_kana_input =
behavior.prefer_kana_input && ime_state.open &&
!keyboard_status_wo_kana_lock.IsPressed(VK_CONTROL) &&
((ime_state.logical_conversion_mode & IME_CMODE_NATIVE) ==
IME_CMODE_NATIVE);
if (use_kana_input) {
// Make a snapshot of keyboard state, then update it so that the
// Kana-lock state is 'locked'.
KeyboardStatus keyboard_status_w_kana_lock = keyboard_status;
const BYTE kKeyPressed = 0x80;
const BYTE kKeyToggled = 0x1;
keyboard_status_w_kana_lock.SetState(VK_KANA, kKeyPressed | kKeyToggled);
WCHAR kana_codes[16] = {};
const int kana_locked_length = keyboard->ToUnicode(
virtual_key.virtual_key(), to_unicode_scancode,
keyboard_status_w_kana_lock.status(), &kana_codes[0],
arraysize(kana_codes),
to_unicode_flag);
if (kana_locked_length != 1) {
return false;
}
const wchar_t kana_code = kana_codes[0];
// TODO(yukawa): Move the following character conversion logic into
// mozc::Util as HalfWidthKatakanaToHiragana.
const wchar_t whalf_katakana[] = {static_cast<wchar_t>(kana_code), L'\0' };
string half_katakana, full_katakana, full_hiragana;
mozc::Util::WideToUTF8(whalf_katakana, &half_katakana);
mozc::Util::HalfWidthKatakanaToFullWidthKatakana(
half_katakana.c_str(),
&full_katakana);
mozc::Util::KatakanaToHiragana(full_katakana.c_str(),
&full_hiragana);
key->set_key_string(full_hiragana);
has_valid_key_string = true;
}
// To conform to Mozc protocol, we need special hack for VK_A, VK_B, ...,
// VK_Z. For these keys, currently we cannot support the current keyboard
// layout. For example, imagine a keyboard layout which generates
// characters as follows.
// VK_A -> 'a'
// VK_A + SHIFT -> '('
// Unfortunately, the current Mozc protocol cannot handle these cases because
// there is serious ambiguity betwen 'Key' and 'Character' in Mozc key
// bindings.
const bool is_vk_alpha =
('A' <= virtual_key.virtual_key() && virtual_key.virtual_key() <= 'Z');
if (is_vk_alpha) {
const char keycode = static_cast<char>(virtual_key.virtual_key());
const size_t index = (keycode - 'A');
if (keyboard_status_wo_kana_lock.IsToggled(VK_CAPITAL)) {
// CapsLock is enabled.
modifer_keys->insert(KeyEvent::CAPS);
if (keyboard_status_wo_kana_lock.IsPressed(VK_SHIFT)) {
if (!keyboard_status_wo_kana_lock.IsPressed(VK_CONTROL)) {
modifer_keys->erase(KeyEvent::SHIFT);
}
key->set_key_code('a' + index);
} else {
key->set_key_code('A' + index);
}
if (keyboard_status_wo_kana_lock.IsPressed(VK_CONTROL)) {
modifer_keys->insert(KeyEvent::CTRL);
return true;
}
return true;
}
// CapsLock is not enabled.
DCHECK(!keyboard_status_wo_kana_lock.IsPressed(VK_CAPITAL));
if (keyboard_status_wo_kana_lock.IsPressed(VK_CONTROL)) {
modifer_keys->insert(KeyEvent::CTRL);
key->set_key_code('a' + index);
return true;
}
if (keyboard_status_wo_kana_lock.IsPressed(VK_SHIFT)) {
// In this cases, SHIFT modifier should be removed.
modifer_keys->erase(KeyEvent::SHIFT);
key->set_key_code('A' + index);
return true;
}
key->set_key_code('a' + index);
return true;
}
// Mozc key binding tool does not allow to use a key combination
// |KeyEvent::CTRL| + |KeyEvent::Shift| + X except that X is VK_A, ..., or,
// VK_Z, or other special keys defined in Mozc protocol such as backspace
// or space.
if (keyboard_status_wo_kana_lock.IsPressed(VK_SHIFT) &&
keyboard_status_wo_kana_lock.IsPressed(VK_CONTROL)) {
// Assume the server does not support this key combination.
DCHECK(!is_vk_alpha);
return false;
}
// To conform to Mozc protocol, we have to clear control modifier before
// obtaining the unicode text. For example, the mozc server expects Ctrl+^
// to be sent as '^' + |KeyEvent::CTRL|.
if (keyboard_status_wo_kana_lock.IsPressed(VK_CONTROL)) {
// We can assume the shiftkey is not pressed here.
DCHECK(!keyboard_status_wo_kana_lock.IsPressed(VK_SHIFT));
keyboard_status_wo_kana_lock.SetState(VK_CONTROL, 0);
keyboard_status_wo_kana_lock.SetState(VK_LCONTROL, 0);
keyboard_status_wo_kana_lock.SetState(VK_RCONTROL, 0);
}
WCHAR codes[16] = {};
int kana_unlocked_length = keyboard->ToUnicode(
virtual_key.virtual_key(), to_unicode_scancode,
keyboard_status_wo_kana_lock.status(), &codes[0],
arraysize(codes), to_unicode_flag);
// A workaround for b/3029665.
// Keyboard drivers of the JIS keyboard do not produce a key code for
// some key combinations such as SHIFT + '0' but the mozc server requires
// the client to set a key code if it is in the kana mode.
// So change the keycode for SHIFT + X to the one without SHIFT.
// TODO(komatsu): Clarify the expected algorithm for the client.
if ((kana_unlocked_length == 0) &&
keyboard_status_wo_kana_lock.IsPressed(VK_SHIFT) &&
has_valid_key_string) {
// Remove shift key.
keyboard_status_wo_kana_lock.SetState(VK_SHIFT, 0);
keyboard_status_wo_kana_lock.SetState(VK_LSHIFT, 0);
keyboard_status_wo_kana_lock.SetState(VK_RSHIFT, 0);
kana_unlocked_length = keyboard->ToUnicode(
virtual_key.virtual_key(), to_unicode_scancode,
keyboard_status_wo_kana_lock.status(), &codes[0],
arraysize(codes), to_unicode_flag);
}
if (kana_unlocked_length != 1) {
return false;
}
// Remove the SHIFT modifier if CapsLock is not locked.
if (modifer_keys->find(KeyEvent::CAPS) == modifer_keys->end()) {
modifer_keys->erase(KeyEvent::SHIFT);
}
key->set_key_code(codes[0]);
return true;
}
} // namespace
KeyEventHandlerResult::KeyEventHandlerResult()
: should_be_eaten(false),
should_be_sent_to_server(false),
succeeded(false) {
}
KeyEventHandlerResult KeyEventHandler::HandleKey(
const VirtualKey &virtual_key,
BYTE scan_code,
bool is_key_down,
const KeyboardStatus &keyboard_status,
const InputBehavior &behavior,
const InputState &ime_state,
Win32KeyboardInterface *keyboard,
mozc::commands::KeyEvent *key) {
KeyEventHandlerResult result;
if (key == nullptr) {
result.succeeded = false;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
if (behavior.disabled) {
result.succeeded = true;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
// There exist some keys which are ideally handled but the server has not
// supported them yet. In order not to pass these key events to the
// application, we will trap them but do not send them to the server.
if (IsNotimplementedKey(virtual_key)) {
result.succeeded = true;
result.should_be_eaten = true;
result.should_be_sent_to_server = false;
return result;
}
if (!ConvertToKeyEvent(virtual_key, scan_code, is_key_down, false,
behavior, ime_state, keyboard_status, keyboard, key)) {
result.succeeded = true;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
// We do not handle key message unless the key is one of force activation
// keys.
if (!ime_state.open) {
// TODO(yukawa): Treat VK_PACKET as a direct mode key.
const bool is_direct_mode_command =
is_key_down &&
KeyInfoUtil::ContainsKey(behavior.direct_mode_keys, *key);
if (!is_direct_mode_command) {
result.succeeded = true;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
// For historical reasons, pass the visible conversion mode to the
// converter.
const DWORD repotring_mode = ime_state.visible_conversion_mode;
commands::CompositionMode mozc_mode = commands::DIRECT;
if (!ConversionModeUtil::GetMozcModeFromNativeMode(
repotring_mode, &mozc_mode)) {
result.succeeded = false;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
key->set_activated(ime_state.open);
key->set_mode(mozc_mode);
result.succeeded = true;
result.should_be_eaten = true;
result.should_be_sent_to_server = true;
return result;
}
DCHECK(ime_state.open);
// For historical reasons, pass the visible conversion mode to the converter.
const DWORD repotring_mode = ime_state.visible_conversion_mode;
commands::CompositionMode mozc_mode = commands::HIRAGANA;
if (!ConversionModeUtil::GetMozcModeFromNativeMode(
repotring_mode, &mozc_mode)) {
result.succeeded = false;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
key->set_activated(ime_state.open);
key->set_mode(mozc_mode);
switch (virtual_key.virtual_key()) {
case VK_SHIFT:
case VK_CONTROL:
case VK_MENU:
if (is_key_down) {
// Will not eat this message.
result.succeeded = true;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
if (ime_state.last_down_key.virtual_key() !=
virtual_key.virtual_key()) {
// Will not eat this message.
result.succeeded = true;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
// We will send this message to the server.
result.succeeded = true;
result.should_be_eaten = true;
result.should_be_sent_to_server = true;
return result;
break;
}
// As commented above, we do not send key-up messages in general. Few
// exceptional cases have already been examined.
if (!is_key_down) {
result.succeeded = true;
result.should_be_eaten = false;
result.should_be_sent_to_server = false;
return result;
}
// We will send this message to the server.
result.succeeded = true;
result.should_be_eaten = true;
result.should_be_sent_to_server = true;
return result;
}
KeyEventHandlerResult KeyEventHandler::ImeProcessKey(
const VirtualKey &virtual_key,
BYTE scan_code,
bool is_key_down,
const KeyboardStatus &keyboard_status,
const InputBehavior &behavior,
const InputState &initial_state,
const commands::Context &context,
client::ClientInterface *client,
Win32KeyboardInterface *keyboard,
InputState *next_state,
commands::Output *output) {
// We will update kana-lock state even if the IME is disabled, including
// safe-mode.
KeyboardStatus new_keyboard_status;
UnlockKanaLock(keyboard_status, keyboard, &new_keyboard_status);
InputState dummy_state;
if (next_state == nullptr) {
next_state = &dummy_state;
}
*next_state = initial_state;
Output dummy_output;
if (output == nullptr) {
output = &dummy_output;
}
output->Clear();
// Although Mozc has not explicitly supported any key-up message, there exist
// some situations where the client has to send key message when it receives
// a key-up message. Currently we have following exceptions.
// - Shift/Control/Alt keys
// The Mozc protocol had originally allowed the client to ignore key-up
// events of these modifier keys but later has changed to expect the
// client to send a key message which contains only modifiers field and
// mode field to support b/2269058 and b/1995170.
if (is_key_down) {
// This is an ugly workaround to determine which key-up message for a
// modifier key should be sent to the server. Currently, the Mozc server
// expects the client to send such a key-up message only when a modifier
// key is released just after the same key is pressed, that is, any other
// key is not pressed between the key-down and key-up of a modifier key.
// Here are some examples, where [D] and [U] mean 'key down' and 'key up'.
// (1) [D]Shift -> [D]A -> [U]Shift -> [U]A
// In this case, only 'A' will be sent to the server
// (2) [D]Shift -> [U]Shift -> [D]A -> [U]A
// In this case, 'Shift' and 'A' will be sent to the server.
// (3) [D]Shift -> [D]Control -> [U]Shift -> [U]Control
// In this case, no key message will be sent to the server.
// (4) [D]Shift -> [D]Control -> [U]Control -> [U]Shift
// In this case, 'Control+Shift' will be sent to the server. Note
// that |KeyEvent::modifier_keys| will contain all the modifier keys
// when the client receives '[U]Control'.
// Unfortunately, it is currently client's responsibility to remember the
// key sequence to generate appropriate key messages as expected by the
// server. Strictly speaking, the Mozc client is actually stateful in
// this sense.
next_state->last_down_key = virtual_key;
}
KeyEvent key;
KeyEventHandlerResult result = HandleKey(
virtual_key, scan_code, is_key_down, keyboard_status,
behavior, initial_state, keyboard, &key);
if (!result.succeeded) {
return result;
}
if (!result.should_be_sent_to_server) {
return result;
}
if (!client->TestSendKeyWithContext(key, context, output)) {
result.succeeded = false;
return result;
}
if (!output->has_consumed()) {
result.succeeded = false;
return result;
}
if (output->has_status()) {
if (!mozc::win32::ConversionModeUtil::ConvertStatusFromMozcToNative(
output->status(), behavior.prefer_kana_input,
&next_state->open,
&next_state->logical_conversion_mode,
&next_state->visible_conversion_mode)) {
result.succeeded = false;
return result;
}
}
result.should_be_eaten = output->consumed();
return result;
}
KeyEventHandlerResult KeyEventHandler::ImeToAsciiEx(
const VirtualKey &virtual_key,
BYTE scan_code,
bool is_key_down,
const KeyboardStatus &keyboard_status,
const InputBehavior &behavior,
const InputState &initial_state,
const commands::Context &context,
client::ClientInterface *client,
Win32KeyboardInterface *keyboard,
InputState *next_state,
commands::Output *output) {
KeyboardStatus new_keyboard_status;
UnlockKanaLock(keyboard_status, keyboard, &new_keyboard_status);
InputState dummy_state;
if (next_state == nullptr) {
next_state = &dummy_state;
}
*next_state = initial_state;
Output dummy_output;
if (output == nullptr) {
output = &dummy_output;
}
output->Clear();
KeyEvent key;
KeyEventHandlerResult result = HandleKey(
virtual_key, scan_code, is_key_down, keyboard_status, behavior,
initial_state, keyboard, &key);
if (!result.succeeded) {
return result;
}
if (!result.should_be_sent_to_server) {
return result;
}
if (!client->SendKeyWithContext(key, context, output)) {
result.succeeded = false;
return result;
}
// Launch tool if required.
MaybeSpawnTool(client, output);
if (!output->has_consumed()) {
result.succeeded = false;
return result;
}
if (output->has_status()) {
if (!mozc::win32::ConversionModeUtil::ConvertStatusFromMozcToNative(
output->status(), behavior.prefer_kana_input,
&next_state->open,
&next_state->logical_conversion_mode,
&next_state->visible_conversion_mode)) {
result.succeeded = false;
return result;
}
}
result.should_be_eaten = output->consumed();
return result;
}
bool KeyEventHandler::ConvertToKeyEvent(
const VirtualKey &virtual_key,
BYTE scan_code,
bool is_key_down,
bool is_menu_active,
const InputBehavior &behavior,
const InputState &ime_state,
const KeyboardStatus &keyboard_status,
Win32KeyboardInterface *keyboard,
mozc::commands::KeyEvent *key) {
// Since Mozc protocol requires tricky conditions for modifiers, using set
// container makes the the main part of key event conversion simple rather
// than using vector-like container.
set<KeyEvent::ModifierKey> modifiers;
const bool result = ConvertToKeyEventMain(
virtual_key,
scan_code,
is_key_down,
is_menu_active,
behavior,
ime_state,
keyboard_status,
keyboard,
key,
&modifiers);
if (!result) {
return false;
}
// Updates |modifier_keys| field based on the returned set of modifier keys.
for (set<KeyEvent::ModifierKey>::const_iterator i = modifiers.begin();
i != modifiers.end(); ++i) {
key->add_modifier_keys(*i);
}
return true;
}
void KeyEventHandler::UnlockKanaLock(
const KeyboardStatus &keyboard_status,
Win32KeyboardInterface *keyboard,
KeyboardStatus *new_keyboard_status) {
KeyboardStatus dummy_status;
if (new_keyboard_status == nullptr) {
new_keyboard_status = &dummy_status;
}
*new_keyboard_status = keyboard_status;
if (keyboard->IsKanaLocked(keyboard_status)) {
new_keyboard_status->SetState(VK_KANA, 0);
keyboard->SetKeyboardState(*new_keyboard_status);
}
}
void KeyEventHandler::MaybeSpawnTool(mozc::client::ClientInterface *client,
commands::Output *output) {
// URL handling:
// When Output::url is set, MozcTool is supposed to be launched by client.
// At this moment, we disable this feature as it may cause security hole.
// if (output->has_url()) {
// client->OpenBrowser(output->url());
// output->clear_url();
// }
// launch_tool_mode handling:
// When Output::launch_tool_mode is set, MozcTool is supposed to be launched
// by client with specified mode.
// TODO(team): move it to better place.
if (output->has_launch_tool_mode()) {
string mode;
switch (output->launch_tool_mode()) {
case commands::Output::CONFIG_DIALOG:
mode = "config_dialog";
break;
case commands::Output::WORD_REGISTER_DIALOG:
mode = "word_register_dialog";
break;
case commands::Output::DICTIONARY_TOOL:
mode = "dictionary_tool";
break;
case commands::Output::NO_TOOL:
default:
// Do nothing.
break;
}
output->clear_launch_tool_mode();
if (!mode.empty()) {
client->LaunchTool(mode, "");
}
}
}
void KeyEventHandler::UpdateBehaviorInImeProcessKey(
const VirtualKey &virtual_key,
bool is_key_down,
const InputState &initial_state,
InputBehavior *behavior) {
if (behavior == nullptr) {
return;
}
if (!initial_state.open) {
return;
}
if (!behavior->use_romaji_key_to_toggle_input_style) {
return;
}
switch (virtual_key.virtual_key()) {
// Do not discriminate between VK_DBE_ROMAN and VK_DBE_NOROMAN because
// these key states are not per-thread nor per-process but system-wide
// or session-wide, which means that any key stroke in another
// thread/process may change the global toggle state at any time.
case VK_DBE_ROMAN:
case VK_DBE_NOROMAN:
if (is_key_down) {
// Flip the input style if this is key down event.
behavior->prefer_kana_input = !behavior->prefer_kana_input;
}
break;
default:
// Otherwise, do nothing.
break;
}
}
} // namespace win32
} // namespace mozc