mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 06:05:44 +00:00
1326 lines
41 KiB
C++
1326 lines
41 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/Util.h"
|
|
|
|
#include "KeyboardLayout.h"
|
|
#include "nsWindow.h"
|
|
#include "nsIMM32Handler.h"
|
|
|
|
#include "nsMemory.h"
|
|
#include "nsToolkit.h"
|
|
#include "nsQuickSort.h"
|
|
#include "nsAlgorithm.h"
|
|
#include "nsGUIEvent.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "WidgetUtils.h"
|
|
#include "WinUtils.h"
|
|
|
|
#include "nsIDOMKeyEvent.h"
|
|
|
|
#include <windows.h>
|
|
#include <winuser.h>
|
|
#include <algorithm>
|
|
|
|
#ifndef WINABLEAPI
|
|
#include <winable.h>
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
namespace widget {
|
|
|
|
struct DeadKeyEntry
|
|
{
|
|
PRUnichar BaseChar;
|
|
PRUnichar CompositeChar;
|
|
};
|
|
|
|
|
|
class DeadKeyTable
|
|
{
|
|
friend class KeyboardLayout;
|
|
|
|
uint16_t mEntries;
|
|
// KeyboardLayout::AddDeadKeyTable() will allocate as many entries as
|
|
// required. It is the only way to create new DeadKeyTable instances.
|
|
DeadKeyEntry mTable[1];
|
|
|
|
void Init(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries)
|
|
{
|
|
mEntries = aEntries;
|
|
memcpy(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry));
|
|
}
|
|
|
|
static uint32_t SizeInBytes(uint32_t aEntries)
|
|
{
|
|
return offsetof(DeadKeyTable, mTable) + aEntries * sizeof(DeadKeyEntry);
|
|
}
|
|
|
|
public:
|
|
uint32_t Entries() const
|
|
{
|
|
return mEntries;
|
|
}
|
|
|
|
bool IsEqual(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) const
|
|
{
|
|
return (mEntries == aEntries &&
|
|
!memcmp(mTable, aDeadKeyArray,
|
|
aEntries * sizeof(DeadKeyEntry)));
|
|
}
|
|
|
|
PRUnichar GetCompositeChar(PRUnichar aBaseChar) const;
|
|
};
|
|
|
|
|
|
/*****************************************************************************
|
|
* mozilla::widget::ModifierKeyState
|
|
*****************************************************************************/
|
|
|
|
void
|
|
ModifierKeyState::Update()
|
|
{
|
|
mModifiers = 0;
|
|
if (IS_VK_DOWN(VK_SHIFT)) {
|
|
mModifiers |= MODIFIER_SHIFT;
|
|
}
|
|
if (IS_VK_DOWN(VK_CONTROL)) {
|
|
mModifiers |= MODIFIER_CONTROL;
|
|
}
|
|
if (IS_VK_DOWN(VK_MENU)) {
|
|
mModifiers |= MODIFIER_ALT;
|
|
}
|
|
if (IS_VK_DOWN(VK_LWIN) || IS_VK_DOWN(VK_RWIN)) {
|
|
mModifiers |= MODIFIER_OS;
|
|
}
|
|
if (::GetKeyState(VK_CAPITAL) & 1) {
|
|
mModifiers |= MODIFIER_CAPSLOCK;
|
|
}
|
|
if (::GetKeyState(VK_NUMLOCK) & 1) {
|
|
mModifiers |= MODIFIER_NUMLOCK;
|
|
}
|
|
if (::GetKeyState(VK_SCROLL) & 1) {
|
|
mModifiers |= MODIFIER_SCROLLLOCK;
|
|
}
|
|
|
|
EnsureAltGr();
|
|
}
|
|
|
|
void
|
|
ModifierKeyState::InitInputEvent(nsInputEvent& aInputEvent) const
|
|
{
|
|
aInputEvent.modifiers = mModifiers;
|
|
|
|
switch(aInputEvent.eventStructType) {
|
|
case NS_MOUSE_EVENT:
|
|
case NS_MOUSE_SCROLL_EVENT:
|
|
case NS_WHEEL_EVENT:
|
|
case NS_DRAG_EVENT:
|
|
case NS_SIMPLE_GESTURE_EVENT:
|
|
InitMouseEvent(aInputEvent);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ModifierKeyState::InitMouseEvent(nsInputEvent& aMouseEvent) const
|
|
{
|
|
NS_ASSERTION(aMouseEvent.eventStructType == NS_MOUSE_EVENT ||
|
|
aMouseEvent.eventStructType == NS_WHEEL_EVENT ||
|
|
aMouseEvent.eventStructType == NS_DRAG_EVENT ||
|
|
aMouseEvent.eventStructType == NS_SIMPLE_GESTURE_EVENT,
|
|
"called with non-mouse event");
|
|
|
|
nsMouseEvent_base& mouseEvent = static_cast<nsMouseEvent_base&>(aMouseEvent);
|
|
mouseEvent.buttons = 0;
|
|
if (::GetKeyState(VK_LBUTTON) < 0) {
|
|
mouseEvent.buttons |= nsMouseEvent::eLeftButtonFlag;
|
|
}
|
|
if (::GetKeyState(VK_RBUTTON) < 0) {
|
|
mouseEvent.buttons |= nsMouseEvent::eRightButtonFlag;
|
|
}
|
|
if (::GetKeyState(VK_MBUTTON) < 0) {
|
|
mouseEvent.buttons |= nsMouseEvent::eMiddleButtonFlag;
|
|
}
|
|
if (::GetKeyState(VK_XBUTTON1) < 0) {
|
|
mouseEvent.buttons |= nsMouseEvent::e4thButtonFlag;
|
|
}
|
|
if (::GetKeyState(VK_XBUTTON2) < 0) {
|
|
mouseEvent.buttons |= nsMouseEvent::e5thButtonFlag;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* mozilla::widget::UniCharsAndModifiers
|
|
*****************************************************************************/
|
|
|
|
void
|
|
UniCharsAndModifiers::Append(PRUnichar aUniChar, Modifiers aModifiers)
|
|
{
|
|
MOZ_ASSERT(mLength < 5);
|
|
mChars[mLength] = aUniChar;
|
|
mModifiers[mLength] = aModifiers;
|
|
mLength++;
|
|
}
|
|
|
|
void
|
|
UniCharsAndModifiers::FillModifiers(Modifiers aModifiers)
|
|
{
|
|
for (uint32_t i = 0; i < mLength; i++) {
|
|
mModifiers[i] = aModifiers;
|
|
}
|
|
}
|
|
|
|
bool
|
|
UniCharsAndModifiers::UniCharsEqual(const UniCharsAndModifiers& aOther) const
|
|
{
|
|
if (mLength != aOther.mLength) {
|
|
return false;
|
|
}
|
|
return !memcmp(mChars, aOther.mChars, mLength * sizeof(PRUnichar));
|
|
}
|
|
|
|
bool
|
|
UniCharsAndModifiers::UniCharsCaseInsensitiveEqual(
|
|
const UniCharsAndModifiers& aOther) const
|
|
{
|
|
if (mLength != aOther.mLength) {
|
|
return false;
|
|
}
|
|
|
|
nsCaseInsensitiveStringComparator comp;
|
|
return !comp(mChars, aOther.mChars, mLength, aOther.mLength);
|
|
}
|
|
|
|
UniCharsAndModifiers&
|
|
UniCharsAndModifiers::operator+=(const UniCharsAndModifiers& aOther)
|
|
{
|
|
uint32_t copyCount = std::min(aOther.mLength, 5 - mLength);
|
|
NS_ENSURE_TRUE(copyCount > 0, *this);
|
|
memcpy(&mChars[mLength], aOther.mChars, copyCount * sizeof(PRUnichar));
|
|
memcpy(&mModifiers[mLength], aOther.mModifiers,
|
|
copyCount * sizeof(Modifiers));
|
|
mLength += copyCount;
|
|
return *this;
|
|
}
|
|
|
|
UniCharsAndModifiers
|
|
UniCharsAndModifiers::operator+(const UniCharsAndModifiers& aOther) const
|
|
{
|
|
UniCharsAndModifiers result(*this);
|
|
result += aOther;
|
|
return result;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* mozilla::widget::VirtualKey
|
|
*****************************************************************************/
|
|
|
|
inline PRUnichar
|
|
VirtualKey::GetCompositeChar(ShiftState aShiftState, PRUnichar aBaseChar) const
|
|
{
|
|
return mShiftStates[aShiftState].DeadKey.Table->GetCompositeChar(aBaseChar);
|
|
}
|
|
|
|
const DeadKeyTable*
|
|
VirtualKey::MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
|
|
uint32_t aEntries) const
|
|
{
|
|
if (!mIsDeadKey) {
|
|
return nullptr;
|
|
}
|
|
|
|
for (ShiftState shiftState = 0; shiftState < 16; shiftState++) {
|
|
if (!IsDeadKey(shiftState)) {
|
|
continue;
|
|
}
|
|
const DeadKeyTable* dkt = mShiftStates[shiftState].DeadKey.Table;
|
|
if (dkt && dkt->IsEqual(aDeadKeyArray, aEntries)) {
|
|
return dkt;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
VirtualKey::SetNormalChars(ShiftState aShiftState,
|
|
const PRUnichar* aChars,
|
|
uint32_t aNumOfChars)
|
|
{
|
|
NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index");
|
|
|
|
SetDeadKey(aShiftState, false);
|
|
|
|
for (uint32_t index = 0; index < aNumOfChars; index++) {
|
|
// Ignore legacy non-printable control characters
|
|
mShiftStates[aShiftState].Normal.Chars[index] =
|
|
(aChars[index] >= 0x20) ? aChars[index] : 0;
|
|
}
|
|
|
|
uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars);
|
|
for (uint32_t index = aNumOfChars; index < len; index++) {
|
|
mShiftStates[aShiftState].Normal.Chars[index] = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
VirtualKey::SetDeadChar(ShiftState aShiftState, PRUnichar aDeadChar)
|
|
{
|
|
NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index");
|
|
|
|
SetDeadKey(aShiftState, true);
|
|
|
|
mShiftStates[aShiftState].DeadKey.DeadChar = aDeadChar;
|
|
mShiftStates[aShiftState].DeadKey.Table = nullptr;
|
|
}
|
|
|
|
UniCharsAndModifiers
|
|
VirtualKey::GetUniChars(ShiftState aShiftState) const
|
|
{
|
|
UniCharsAndModifiers result = GetNativeUniChars(aShiftState);
|
|
|
|
const ShiftState STATE_ALT_CONTROL = (STATE_ALT | STATE_CONTROL);
|
|
if (!(aShiftState & STATE_ALT_CONTROL)) {
|
|
return result;
|
|
}
|
|
|
|
if (!result.mLength) {
|
|
result = GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
|
|
result.FillModifiers(ShiftStateToModifiers(aShiftState));
|
|
return result;
|
|
}
|
|
|
|
if ((aShiftState & STATE_ALT_CONTROL) == STATE_ALT_CONTROL) {
|
|
// Even if the shifted chars and the unshifted chars are same, we
|
|
// should consume the Alt key state and the Ctrl key state when
|
|
// AltGr key is pressed. Because if we don't consume them, the input
|
|
// events are ignored on nsEditor. (I.e., Users cannot input the
|
|
// characters with this key combination.)
|
|
Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
|
|
finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
|
|
result.FillModifiers(finalModifiers);
|
|
return result;
|
|
}
|
|
|
|
UniCharsAndModifiers unmodifiedReslt =
|
|
GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL);
|
|
if (!result.UniCharsEqual(unmodifiedReslt)) {
|
|
// Otherwise, we should consume the Alt key state and the Ctrl key state
|
|
// only when the shifted chars and unshifted chars are different.
|
|
Modifiers finalModifiers = ShiftStateToModifiers(aShiftState);
|
|
finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL);
|
|
result.FillModifiers(finalModifiers);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
UniCharsAndModifiers
|
|
VirtualKey::GetNativeUniChars(ShiftState aShiftState) const
|
|
{
|
|
UniCharsAndModifiers result;
|
|
Modifiers modifiers = ShiftStateToModifiers(aShiftState);
|
|
if (IsDeadKey(aShiftState)) {
|
|
result.Append(mShiftStates[aShiftState].DeadKey.DeadChar, modifiers);
|
|
return result;
|
|
}
|
|
|
|
uint32_t index;
|
|
uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars);
|
|
for (index = 0;
|
|
index < len && mShiftStates[aShiftState].Normal.Chars[index]; index++) {
|
|
result.Append(mShiftStates[aShiftState].Normal.Chars[index], modifiers);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// static
|
|
void
|
|
VirtualKey::FillKbdState(PBYTE aKbdState,
|
|
const ShiftState aShiftState)
|
|
{
|
|
NS_ASSERTION(aShiftState < 16, "aShiftState out of range");
|
|
|
|
if (aShiftState & STATE_SHIFT) {
|
|
aKbdState[VK_SHIFT] |= 0x80;
|
|
} else {
|
|
aKbdState[VK_SHIFT] &= ~0x80;
|
|
aKbdState[VK_LSHIFT] &= ~0x80;
|
|
aKbdState[VK_RSHIFT] &= ~0x80;
|
|
}
|
|
|
|
if (aShiftState & STATE_CONTROL) {
|
|
aKbdState[VK_CONTROL] |= 0x80;
|
|
} else {
|
|
aKbdState[VK_CONTROL] &= ~0x80;
|
|
aKbdState[VK_LCONTROL] &= ~0x80;
|
|
aKbdState[VK_RCONTROL] &= ~0x80;
|
|
}
|
|
|
|
if (aShiftState & STATE_ALT) {
|
|
aKbdState[VK_MENU] |= 0x80;
|
|
} else {
|
|
aKbdState[VK_MENU] &= ~0x80;
|
|
aKbdState[VK_LMENU] &= ~0x80;
|
|
aKbdState[VK_RMENU] &= ~0x80;
|
|
}
|
|
|
|
if (aShiftState & STATE_CAPSLOCK) {
|
|
aKbdState[VK_CAPITAL] |= 0x01;
|
|
} else {
|
|
aKbdState[VK_CAPITAL] &= ~0x01;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* mozilla::widget::NativeKey
|
|
*****************************************************************************/
|
|
|
|
NativeKey::NativeKey(const KeyboardLayout& aKeyboardLayout,
|
|
nsWindow* aWindow,
|
|
const MSG& aKeyOrCharMessage) :
|
|
mDOMKeyCode(0), mMessage(aKeyOrCharMessage.message),
|
|
mVirtualKeyCode(0), mOriginalVirtualKeyCode(0)
|
|
{
|
|
mScanCode = WinUtils::GetScanCode(aKeyOrCharMessage.lParam);
|
|
mIsExtended = WinUtils::IsExtendedScanCode(aKeyOrCharMessage.lParam);
|
|
// On WinXP and WinServer2003, we cannot compute the virtual keycode for
|
|
// extended keys due to the API limitation.
|
|
bool canComputeVirtualKeyCodeFromScanCode =
|
|
(!mIsExtended || WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION);
|
|
switch (mMessage) {
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
case WM_SYSKEYDOWN:
|
|
case WM_SYSKEYUP: {
|
|
// First, resolve the IME converted virtual keycode to its original
|
|
// keycode.
|
|
if (aKeyOrCharMessage.wParam == VK_PROCESSKEY) {
|
|
mOriginalVirtualKeyCode = static_cast<uint8_t>(
|
|
::ImmGetVirtualKey(aWindow->GetWindowHandle()));
|
|
} else {
|
|
mOriginalVirtualKeyCode =
|
|
static_cast<uint8_t>(aKeyOrCharMessage.wParam);
|
|
}
|
|
|
|
// Most keys are not distinguished as left or right keys.
|
|
bool isLeftRightDistinguishedKey = false;
|
|
|
|
// mOriginalVirtualKeyCode must not distinguish left or right of
|
|
// Shift, Control or Alt.
|
|
switch (mOriginalVirtualKeyCode) {
|
|
case VK_SHIFT:
|
|
case VK_CONTROL:
|
|
case VK_MENU:
|
|
isLeftRightDistinguishedKey = true;
|
|
break;
|
|
case VK_LSHIFT:
|
|
case VK_RSHIFT:
|
|
mVirtualKeyCode = mOriginalVirtualKeyCode;
|
|
mOriginalVirtualKeyCode = VK_SHIFT;
|
|
isLeftRightDistinguishedKey = true;
|
|
break;
|
|
case VK_LCONTROL:
|
|
case VK_RCONTROL:
|
|
mVirtualKeyCode = mOriginalVirtualKeyCode;
|
|
mOriginalVirtualKeyCode = VK_CONTROL;
|
|
isLeftRightDistinguishedKey = true;
|
|
break;
|
|
case VK_LMENU:
|
|
case VK_RMENU:
|
|
mVirtualKeyCode = mOriginalVirtualKeyCode;
|
|
mOriginalVirtualKeyCode = VK_MENU;
|
|
isLeftRightDistinguishedKey = true;
|
|
break;
|
|
}
|
|
|
|
// If virtual keycode (left-right distinguished keycode) is already
|
|
// computed, we don't need to do anymore.
|
|
if (mVirtualKeyCode) {
|
|
break;
|
|
}
|
|
|
|
// If the keycode doesn't have LR distinguished keycode, we just set
|
|
// mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute
|
|
// it from MapVirtualKeyEx() because the scan code might be wrong if
|
|
// the message is sent/posted by other application. Then, we will compute
|
|
// unexpected keycode from the scan code.
|
|
if (!isLeftRightDistinguishedKey) {
|
|
break;
|
|
}
|
|
|
|
if (!canComputeVirtualKeyCodeFromScanCode) {
|
|
// The right control key and the right alt key are extended keys.
|
|
// Therefore, we never get VK_RCONTRL and VK_RMENU for the result of
|
|
// MapVirtualKeyEx() on WinXP or WinServer2003.
|
|
//
|
|
// If VK_CONTROL or VK_MENU key message is caused by an extended key,
|
|
// we should assume that the right key of them is pressed.
|
|
switch (mOriginalVirtualKeyCode) {
|
|
case VK_CONTROL:
|
|
mVirtualKeyCode = VK_RCONTROL;
|
|
break;
|
|
case VK_MENU:
|
|
mVirtualKeyCode = VK_RMENU;
|
|
break;
|
|
case VK_SHIFT:
|
|
// Neither left shift nor right shift is not an extended key,
|
|
// let's use VK_LSHIFT for invalid scan code.
|
|
mVirtualKeyCode = VK_LSHIFT;
|
|
break;
|
|
default:
|
|
MOZ_NOT_REACHED("Unsupported mOriginalVirtualKeyCode");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
NS_ASSERTION(!mVirtualKeyCode,
|
|
"mVirtualKeyCode has been computed already");
|
|
|
|
// Otherwise, compute the virtual keycode with MapVirtualKeyEx().
|
|
mVirtualKeyCode = static_cast<uint8_t>(
|
|
::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(),
|
|
MAPVK_VSC_TO_VK_EX, aKeyboardLayout.GetLayout()));
|
|
|
|
// The result might be unexpected value due to the scan code is
|
|
// wrong. For example, any key messages can be generated by
|
|
// SendMessage() or PostMessage() from applications. So, it's possible
|
|
// failure. Then, let's respect the extended flag even if it might be
|
|
// set intentionally.
|
|
switch (mOriginalVirtualKeyCode) {
|
|
case VK_CONTROL:
|
|
if (mVirtualKeyCode != VK_LCONTROL &&
|
|
mVirtualKeyCode != VK_RCONTROL) {
|
|
mVirtualKeyCode = mIsExtended ? VK_RCONTROL : VK_LCONTROL;
|
|
}
|
|
break;
|
|
case VK_MENU:
|
|
if (mVirtualKeyCode != VK_LMENU && mVirtualKeyCode != VK_RMENU) {
|
|
mVirtualKeyCode = mIsExtended ? VK_RMENU : VK_LMENU;
|
|
}
|
|
break;
|
|
case VK_SHIFT:
|
|
if (mVirtualKeyCode != VK_LSHIFT && mVirtualKeyCode != VK_RSHIFT) {
|
|
// Neither left shift nor right shift is not an extended key,
|
|
// let's use VK_LSHIFT for invalid scan code.
|
|
mVirtualKeyCode = VK_LSHIFT;
|
|
}
|
|
break;
|
|
default:
|
|
MOZ_NOT_REACHED("Unsupported mOriginalVirtualKeyCode");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case WM_CHAR:
|
|
case WM_UNICHAR:
|
|
case WM_SYSCHAR:
|
|
// We cannot compute the virtual key code from WM_CHAR message on WinXP
|
|
// if it's caused by an extended key.
|
|
if (!canComputeVirtualKeyCodeFromScanCode) {
|
|
break;
|
|
}
|
|
mVirtualKeyCode = mOriginalVirtualKeyCode = static_cast<uint8_t>(
|
|
::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(),
|
|
MAPVK_VSC_TO_VK_EX, aKeyboardLayout.GetLayout()));
|
|
break;
|
|
default:
|
|
MOZ_NOT_REACHED("Unsupported message");
|
|
break;
|
|
}
|
|
|
|
if (!mVirtualKeyCode) {
|
|
mVirtualKeyCode = mOriginalVirtualKeyCode;
|
|
}
|
|
|
|
mDOMKeyCode =
|
|
aKeyboardLayout.ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode);
|
|
mKeyNameIndex =
|
|
aKeyboardLayout.ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode);
|
|
}
|
|
|
|
UINT
|
|
NativeKey::GetScanCodeWithExtendedFlag() const
|
|
{
|
|
// MapVirtualKeyEx() has been improved for supporting extended keys since
|
|
// Vista. When we call it for mapping a scancode of an extended key and
|
|
// a virtual keycode, we need to add 0xE000 to the scancode.
|
|
// On Win XP and Win Server 2003, this doesn't support. On them, we have
|
|
// no way to get virtual keycodes from scancode of extended keys.
|
|
if (!mIsExtended ||
|
|
WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION) {
|
|
return mScanCode;
|
|
}
|
|
return (0xE000 | mScanCode);
|
|
}
|
|
|
|
uint32_t
|
|
NativeKey::GetKeyLocation() const
|
|
{
|
|
switch (mVirtualKeyCode) {
|
|
case VK_LSHIFT:
|
|
case VK_LCONTROL:
|
|
case VK_LMENU:
|
|
case VK_LWIN:
|
|
return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
|
|
|
|
case VK_RSHIFT:
|
|
case VK_RCONTROL:
|
|
case VK_RMENU:
|
|
case VK_RWIN:
|
|
return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
|
|
|
|
case VK_RETURN:
|
|
// XXX This code assumes that all keyboard drivers use same mapping.
|
|
return !mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD :
|
|
nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
|
|
|
|
case VK_INSERT:
|
|
case VK_DELETE:
|
|
case VK_END:
|
|
case VK_DOWN:
|
|
case VK_NEXT:
|
|
case VK_LEFT:
|
|
case VK_CLEAR:
|
|
case VK_RIGHT:
|
|
case VK_HOME:
|
|
case VK_UP:
|
|
case VK_PRIOR:
|
|
// XXX This code assumes that all keyboard drivers use same mapping.
|
|
return mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD :
|
|
nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
|
|
|
|
// NumLock key isn't included due to IE9's behavior.
|
|
case VK_NUMPAD0:
|
|
case VK_NUMPAD1:
|
|
case VK_NUMPAD2:
|
|
case VK_NUMPAD3:
|
|
case VK_NUMPAD4:
|
|
case VK_NUMPAD5:
|
|
case VK_NUMPAD6:
|
|
case VK_NUMPAD7:
|
|
case VK_NUMPAD8:
|
|
case VK_NUMPAD9:
|
|
case VK_DECIMAL:
|
|
case VK_DIVIDE:
|
|
case VK_MULTIPLY:
|
|
case VK_SUBTRACT:
|
|
case VK_ADD:
|
|
return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
|
|
|
|
case VK_SHIFT:
|
|
case VK_CONTROL:
|
|
case VK_MENU:
|
|
NS_WARNING("Failed to decide the key location?");
|
|
|
|
default:
|
|
return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* mozilla::widget::KeyboardLayout
|
|
*****************************************************************************/
|
|
|
|
KeyboardLayout::KeyboardLayout() :
|
|
mKeyboardLayout(0), mPendingKeyboardLayout(0)
|
|
{
|
|
mDeadKeyTableListHead = nullptr;
|
|
|
|
// Note: Don't call LoadLayout from here. Because an instance of this class
|
|
// can be static. In that case, we cannot use any services in LoadLayout,
|
|
// e.g., pref service.
|
|
}
|
|
|
|
KeyboardLayout::~KeyboardLayout()
|
|
{
|
|
ReleaseDeadKeyTables();
|
|
}
|
|
|
|
bool
|
|
KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey)
|
|
{
|
|
return GetKeyIndex(aVirtualKey) >= 0;
|
|
}
|
|
|
|
bool
|
|
KeyboardLayout::IsDeadKey(uint8_t aVirtualKey,
|
|
const ModifierKeyState& aModKeyState) const
|
|
{
|
|
int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey);
|
|
if (virtualKeyIndex < 0) {
|
|
return false;
|
|
}
|
|
|
|
return mVirtualKeys[virtualKeyIndex].IsDeadKey(
|
|
VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
|
|
}
|
|
|
|
void
|
|
KeyboardLayout::InitNativeKey(NativeKey& aNativeKey,
|
|
const ModifierKeyState& aModKeyState)
|
|
{
|
|
if (mPendingKeyboardLayout) {
|
|
LoadLayout(mPendingKeyboardLayout);
|
|
}
|
|
|
|
uint8_t virtualKey = aNativeKey.GetOriginalVirtualKeyCode();
|
|
int32_t virtualKeyIndex = GetKeyIndex(virtualKey);
|
|
|
|
if (virtualKeyIndex < 0) {
|
|
// Does not produce any printable characters, but still preserves the
|
|
// dead-key state.
|
|
return;
|
|
}
|
|
|
|
bool isKeyDown = aNativeKey.IsKeyDownMessage();
|
|
uint8_t shiftState =
|
|
VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers());
|
|
|
|
if (mVirtualKeys[virtualKeyIndex].IsDeadKey(shiftState)) {
|
|
if ((isKeyDown && mActiveDeadKey < 0) ||
|
|
(!isKeyDown && mActiveDeadKey == virtualKey)) {
|
|
// First dead key event doesn't generate characters.
|
|
if (isKeyDown) {
|
|
// Dead-key state activated at keydown.
|
|
mActiveDeadKey = virtualKey;
|
|
mDeadKeyShiftState = shiftState;
|
|
}
|
|
UniCharsAndModifiers deadChars =
|
|
mVirtualKeys[virtualKeyIndex].GetNativeUniChars(shiftState);
|
|
NS_ASSERTION(deadChars.mLength == 1,
|
|
"dead key must generate only one character");
|
|
aNativeKey.mKeyNameIndex =
|
|
WidgetUtils::GetDeadKeyNameIndex(deadChars.mChars[0]);
|
|
return;
|
|
}
|
|
|
|
// Dead-key followed by another dead-key. Reset dead-key state and
|
|
// return both dead-key characters.
|
|
int32_t activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey);
|
|
UniCharsAndModifiers prevDeadChars =
|
|
mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState);
|
|
UniCharsAndModifiers newChars =
|
|
mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState);
|
|
// But keypress events should be fired for each committed character.
|
|
aNativeKey.mCommittedCharsAndModifiers = prevDeadChars + newChars;
|
|
if (isKeyDown) {
|
|
DeactivateDeadKeyState();
|
|
}
|
|
return;
|
|
}
|
|
|
|
UniCharsAndModifiers baseChars =
|
|
mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState);
|
|
if (mActiveDeadKey < 0) {
|
|
// No dead-keys are active. Just return the produced characters.
|
|
aNativeKey.mCommittedCharsAndModifiers = baseChars;
|
|
return;
|
|
}
|
|
|
|
// Dead-key was active. See if pressed base character does produce
|
|
// valid composite character.
|
|
int32_t activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey);
|
|
PRUnichar compositeChar = (baseChars.mLength == 1 && baseChars.mChars[0]) ?
|
|
mVirtualKeys[activeDeadKeyIndex].GetCompositeChar(mDeadKeyShiftState,
|
|
baseChars.mChars[0]) : 0;
|
|
if (compositeChar) {
|
|
// Active dead-key and base character does produce exactly one
|
|
// composite character.
|
|
aNativeKey.mCommittedCharsAndModifiers.Append(compositeChar,
|
|
baseChars.mModifiers[0]);
|
|
if (isKeyDown) {
|
|
DeactivateDeadKeyState();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// There is no valid dead-key and base character combination.
|
|
// Return dead-key character followed by base character.
|
|
UniCharsAndModifiers deadChars =
|
|
mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState);
|
|
// But keypress events should be fired for each committed character.
|
|
aNativeKey.mCommittedCharsAndModifiers = deadChars + baseChars;
|
|
if (isKeyDown) {
|
|
DeactivateDeadKeyState();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
UniCharsAndModifiers
|
|
KeyboardLayout::GetUniCharsAndModifiers(
|
|
uint8_t aVirtualKey,
|
|
const ModifierKeyState& aModKeyState) const
|
|
{
|
|
UniCharsAndModifiers result;
|
|
int32_t key = GetKeyIndex(aVirtualKey);
|
|
if (key < 0) {
|
|
return result;
|
|
}
|
|
return mVirtualKeys[key].
|
|
GetUniChars(VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()));
|
|
}
|
|
|
|
void
|
|
KeyboardLayout::LoadLayout(HKL aLayout, bool aLoadLater)
|
|
{
|
|
if (aLoadLater) {
|
|
mPendingKeyboardLayout = aLayout;
|
|
return;
|
|
}
|
|
|
|
mPendingKeyboardLayout = 0;
|
|
|
|
if (mKeyboardLayout == aLayout) {
|
|
return;
|
|
}
|
|
|
|
mKeyboardLayout = aLayout;
|
|
|
|
BYTE kbdState[256];
|
|
memset(kbdState, 0, sizeof(kbdState));
|
|
|
|
BYTE originalKbdState[256];
|
|
// Bitfield with all shift states that have at least one dead-key.
|
|
uint16_t shiftStatesWithDeadKeys = 0;
|
|
// Bitfield with all shift states that produce any possible dead-key base
|
|
// characters.
|
|
uint16_t shiftStatesWithBaseChars = 0;
|
|
|
|
mActiveDeadKey = -1;
|
|
|
|
ReleaseDeadKeyTables();
|
|
|
|
::GetKeyboardState(originalKbdState);
|
|
|
|
// For each shift state gather all printable characters that are produced
|
|
// for normal case when no any dead-key is active.
|
|
|
|
for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
|
|
VirtualKey::FillKbdState(kbdState, shiftState);
|
|
for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
|
|
int32_t vki = GetKeyIndex(virtualKey);
|
|
if (vki < 0) {
|
|
continue;
|
|
}
|
|
NS_ASSERTION(uint32_t(vki) < ArrayLength(mVirtualKeys), "invalid index");
|
|
PRUnichar uniChars[5];
|
|
int32_t ret =
|
|
::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars,
|
|
ArrayLength(uniChars), 0, mKeyboardLayout);
|
|
// dead-key
|
|
if (ret < 0) {
|
|
shiftStatesWithDeadKeys |= (1 << shiftState);
|
|
// Repeat dead-key to deactivate it and get its character
|
|
// representation.
|
|
PRUnichar deadChar[2];
|
|
ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)deadChar,
|
|
ArrayLength(deadChar), 0, mKeyboardLayout);
|
|
NS_ASSERTION(ret == 2, "Expecting twice repeated dead-key character");
|
|
mVirtualKeys[vki].SetDeadChar(shiftState, deadChar[0]);
|
|
} else {
|
|
if (ret == 1) {
|
|
// dead-key can pair only with exactly one base character.
|
|
shiftStatesWithBaseChars |= (1 << shiftState);
|
|
}
|
|
mVirtualKeys[vki].SetNormalChars(shiftState, uniChars, ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now process each dead-key to find all its base characters and resulting
|
|
// composite characters.
|
|
for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) {
|
|
if (!(shiftStatesWithDeadKeys & (1 << shiftState))) {
|
|
continue;
|
|
}
|
|
|
|
VirtualKey::FillKbdState(kbdState, shiftState);
|
|
|
|
for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
|
|
int32_t vki = GetKeyIndex(virtualKey);
|
|
if (vki >= 0 && mVirtualKeys[vki].IsDeadKey(shiftState)) {
|
|
DeadKeyEntry deadKeyArray[256];
|
|
int32_t n = GetDeadKeyCombinations(virtualKey, kbdState,
|
|
shiftStatesWithBaseChars,
|
|
deadKeyArray,
|
|
ArrayLength(deadKeyArray));
|
|
const DeadKeyTable* dkt =
|
|
mVirtualKeys[vki].MatchingDeadKeyTable(deadKeyArray, n);
|
|
if (!dkt) {
|
|
dkt = AddDeadKeyTable(deadKeyArray, n);
|
|
}
|
|
mVirtualKeys[vki].AttachDeadKeyTable(shiftState, dkt);
|
|
}
|
|
}
|
|
}
|
|
|
|
::SetKeyboardState(originalKbdState);
|
|
}
|
|
|
|
inline int32_t
|
|
KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey)
|
|
{
|
|
// Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed
|
|
// to produce visible representation:
|
|
// 0x20 - VK_SPACE ' '
|
|
// 0x30..0x39 '0'..'9'
|
|
// 0x41..0x5A 'A'..'Z'
|
|
// 0x60..0x69 '0'..'9' on numpad
|
|
// 0x6A - VK_MULTIPLY '*' on numpad
|
|
// 0x6B - VK_ADD '+' on numpad
|
|
// 0x6D - VK_SUBTRACT '-' on numpad
|
|
// 0x6E - VK_DECIMAL '.' on numpad
|
|
// 0x6F - VK_DIVIDE '/' on numpad
|
|
// 0x6E - VK_DECIMAL '.'
|
|
// 0xBA - VK_OEM_1 ';:' for US
|
|
// 0xBB - VK_OEM_PLUS '+' any country
|
|
// 0xBC - VK_OEM_COMMA ',' any country
|
|
// 0xBD - VK_OEM_MINUS '-' any country
|
|
// 0xBE - VK_OEM_PERIOD '.' any country
|
|
// 0xBF - VK_OEM_2 '/?' for US
|
|
// 0xC0 - VK_OEM_3 '`~' for US
|
|
// 0xDB - VK_OEM_4 '[{' for US
|
|
// 0xDC - VK_OEM_5 '\|' for US
|
|
// 0xDD - VK_OEM_6 ']}' for US
|
|
// 0xDE - VK_OEM_7 ''"' for US
|
|
// 0xDF - VK_OEM_8
|
|
// 0xE1 - no name
|
|
// 0xE2 - VK_OEM_102 '\_' for JIS
|
|
// 0xE3 - no name
|
|
// 0xE4 - no name
|
|
|
|
static const int8_t xlat[256] =
|
|
{
|
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
//-----------------------------------------------------------------------
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10
|
|
0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30
|
|
-1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40
|
|
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50
|
|
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0
|
|
58, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, 60, 61, 62, 63, // D0
|
|
-1, 64, 65, 66, 67, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0
|
|
};
|
|
|
|
return xlat[aVirtualKey];
|
|
}
|
|
|
|
int
|
|
KeyboardLayout::CompareDeadKeyEntries(const void* aArg1,
|
|
const void* aArg2,
|
|
void*)
|
|
{
|
|
const DeadKeyEntry* arg1 = static_cast<const DeadKeyEntry*>(aArg1);
|
|
const DeadKeyEntry* arg2 = static_cast<const DeadKeyEntry*>(aArg2);
|
|
|
|
return arg1->BaseChar - arg2->BaseChar;
|
|
}
|
|
|
|
const DeadKeyTable*
|
|
KeyboardLayout::AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
|
|
uint32_t aEntries)
|
|
{
|
|
DeadKeyTableListEntry* next = mDeadKeyTableListHead;
|
|
|
|
const size_t bytes = offsetof(DeadKeyTableListEntry, data) +
|
|
DeadKeyTable::SizeInBytes(aEntries);
|
|
uint8_t* p = new uint8_t[bytes];
|
|
|
|
mDeadKeyTableListHead = reinterpret_cast<DeadKeyTableListEntry*>(p);
|
|
mDeadKeyTableListHead->next = next;
|
|
|
|
DeadKeyTable* dkt =
|
|
reinterpret_cast<DeadKeyTable*>(mDeadKeyTableListHead->data);
|
|
|
|
dkt->Init(aDeadKeyArray, aEntries);
|
|
|
|
return dkt;
|
|
}
|
|
|
|
void
|
|
KeyboardLayout::ReleaseDeadKeyTables()
|
|
{
|
|
while (mDeadKeyTableListHead) {
|
|
uint8_t* p = reinterpret_cast<uint8_t*>(mDeadKeyTableListHead);
|
|
mDeadKeyTableListHead = mDeadKeyTableListHead->next;
|
|
|
|
delete [] p;
|
|
}
|
|
}
|
|
|
|
bool
|
|
KeyboardLayout::EnsureDeadKeyActive(bool aIsActive,
|
|
uint8_t aDeadKey,
|
|
const PBYTE aDeadKeyKbdState)
|
|
{
|
|
int32_t ret;
|
|
do {
|
|
PRUnichar dummyChars[5];
|
|
ret = ::ToUnicodeEx(aDeadKey, 0, (PBYTE)aDeadKeyKbdState,
|
|
(LPWSTR)dummyChars, ArrayLength(dummyChars), 0,
|
|
mKeyboardLayout);
|
|
// returned values:
|
|
// <0 - Dead key state is active. The keyboard driver will wait for next
|
|
// character.
|
|
// 1 - Previous pressed key was a valid base character that produced
|
|
// exactly one composite character.
|
|
// >1 - Previous pressed key does not produce any composite characters.
|
|
// Return dead-key character followed by base character(s).
|
|
} while ((ret < 0) != aIsActive);
|
|
|
|
return (ret < 0);
|
|
}
|
|
|
|
void
|
|
KeyboardLayout::DeactivateDeadKeyState()
|
|
{
|
|
if (mActiveDeadKey < 0) {
|
|
return;
|
|
}
|
|
|
|
BYTE kbdState[256];
|
|
memset(kbdState, 0, sizeof(kbdState));
|
|
|
|
VirtualKey::FillKbdState(kbdState, mDeadKeyShiftState);
|
|
|
|
EnsureDeadKeyActive(false, mActiveDeadKey, kbdState);
|
|
mActiveDeadKey = -1;
|
|
}
|
|
|
|
bool
|
|
KeyboardLayout::AddDeadKeyEntry(PRUnichar aBaseChar,
|
|
PRUnichar aCompositeChar,
|
|
DeadKeyEntry* aDeadKeyArray,
|
|
uint32_t aEntries)
|
|
{
|
|
for (uint32_t index = 0; index < aEntries; index++) {
|
|
if (aDeadKeyArray[index].BaseChar == aBaseChar) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
aDeadKeyArray[aEntries].BaseChar = aBaseChar;
|
|
aDeadKeyArray[aEntries].CompositeChar = aCompositeChar;
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t
|
|
KeyboardLayout::GetDeadKeyCombinations(uint8_t aDeadKey,
|
|
const PBYTE aDeadKeyKbdState,
|
|
uint16_t aShiftStatesWithBaseChars,
|
|
DeadKeyEntry* aDeadKeyArray,
|
|
uint32_t aMaxEntries)
|
|
{
|
|
bool deadKeyActive = false;
|
|
uint32_t entries = 0;
|
|
BYTE kbdState[256];
|
|
memset(kbdState, 0, sizeof(kbdState));
|
|
|
|
for (uint32_t shiftState = 0; shiftState < 16; shiftState++) {
|
|
if (!(aShiftStatesWithBaseChars & (1 << shiftState))) {
|
|
continue;
|
|
}
|
|
|
|
VirtualKey::FillKbdState(kbdState, shiftState);
|
|
|
|
for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) {
|
|
int32_t vki = GetKeyIndex(virtualKey);
|
|
// Dead-key can pair only with such key that produces exactly one base
|
|
// character.
|
|
if (vki >= 0 &&
|
|
mVirtualKeys[vki].GetNativeUniChars(shiftState).mLength == 1) {
|
|
// Ensure dead-key is in active state, when it swallows entered
|
|
// character and waits for the next pressed key.
|
|
if (!deadKeyActive) {
|
|
deadKeyActive = EnsureDeadKeyActive(true, aDeadKey,
|
|
aDeadKeyKbdState);
|
|
}
|
|
|
|
// Depending on the character the followed the dead-key, the keyboard
|
|
// driver can produce one composite character, or a dead-key character
|
|
// followed by a second character.
|
|
PRUnichar compositeChars[5];
|
|
int32_t ret =
|
|
::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)compositeChars,
|
|
ArrayLength(compositeChars), 0, mKeyboardLayout);
|
|
switch (ret) {
|
|
case 0:
|
|
// This key combination does not produce any characters. The
|
|
// dead-key is still in active state.
|
|
break;
|
|
case 1: {
|
|
// Exactly one composite character produced. Now, when dead-key
|
|
// is not active, repeat the last character one more time to
|
|
// determine the base character.
|
|
PRUnichar baseChars[5];
|
|
ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)baseChars,
|
|
ArrayLength(baseChars), 0, mKeyboardLayout);
|
|
NS_ASSERTION(ret == 1, "One base character expected");
|
|
if (ret == 1 && entries < aMaxEntries &&
|
|
AddDeadKeyEntry(baseChars[0], compositeChars[0],
|
|
aDeadKeyArray, entries)) {
|
|
entries++;
|
|
}
|
|
deadKeyActive = false;
|
|
break;
|
|
}
|
|
default:
|
|
// 1. Unexpected dead-key. Dead-key chaining is not supported.
|
|
// 2. More than one character generated. This is not a valid
|
|
// dead-key and base character combination.
|
|
deadKeyActive = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (deadKeyActive) {
|
|
deadKeyActive = EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState);
|
|
}
|
|
|
|
NS_QuickSort(aDeadKeyArray, entries, sizeof(DeadKeyEntry),
|
|
CompareDeadKeyEntries, nullptr);
|
|
return entries;
|
|
}
|
|
|
|
uint32_t
|
|
KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const
|
|
{
|
|
// Alphabet or Numeric or Numpad or Function keys
|
|
if ((aNativeKeyCode >= 0x30 && aNativeKeyCode <= 0x39) ||
|
|
(aNativeKeyCode >= 0x41 && aNativeKeyCode <= 0x5A) ||
|
|
(aNativeKeyCode >= 0x60 && aNativeKeyCode <= 0x87)) {
|
|
return static_cast<uint32_t>(aNativeKeyCode);
|
|
}
|
|
switch (aNativeKeyCode) {
|
|
// Following keycodes are same as our DOM keycodes
|
|
case VK_CANCEL:
|
|
case VK_BACK:
|
|
case VK_TAB:
|
|
case VK_CLEAR:
|
|
case VK_RETURN:
|
|
case VK_SHIFT:
|
|
case VK_CONTROL:
|
|
case VK_MENU: // Alt
|
|
case VK_PAUSE:
|
|
case VK_CAPITAL: // CAPS LOCK
|
|
case VK_KANA: // same as VK_HANGUL
|
|
case VK_JUNJA:
|
|
case VK_FINAL:
|
|
case VK_HANJA: // same as VK_KANJI
|
|
case VK_ESCAPE:
|
|
case VK_CONVERT:
|
|
case VK_NONCONVERT:
|
|
case VK_ACCEPT:
|
|
case VK_MODECHANGE:
|
|
case VK_SPACE:
|
|
case VK_PRIOR: // PAGE UP
|
|
case VK_NEXT: // PAGE DOWN
|
|
case VK_END:
|
|
case VK_HOME:
|
|
case VK_LEFT:
|
|
case VK_UP:
|
|
case VK_RIGHT:
|
|
case VK_DOWN:
|
|
case VK_SELECT:
|
|
case VK_PRINT:
|
|
case VK_EXECUTE:
|
|
case VK_SNAPSHOT:
|
|
case VK_INSERT:
|
|
case VK_DELETE:
|
|
case VK_APPS: // Context Menu
|
|
case VK_SLEEP:
|
|
case VK_NUMLOCK:
|
|
case VK_SCROLL: // SCROLL LOCK
|
|
case VK_ATTN: // Attension key of IBM midrange computers, e.g., AS/400
|
|
case VK_CRSEL: // Cursor Selection
|
|
case VK_EXSEL: // Extend Selection
|
|
case VK_EREOF: // Erase EOF key of IBM 3270 keyboard layout
|
|
case VK_PLAY:
|
|
case VK_ZOOM:
|
|
case VK_PA1: // PA1 key of IBM 3270 keyboard layout
|
|
return uint32_t(aNativeKeyCode);
|
|
|
|
case VK_HELP:
|
|
return NS_VK_HELP;
|
|
|
|
// Windows key should be mapped to a Win keycode
|
|
// They should be able to be distinguished by DOM3 KeyboardEvent.location
|
|
case VK_LWIN:
|
|
case VK_RWIN:
|
|
return NS_VK_WIN;
|
|
|
|
case VK_VOLUME_MUTE:
|
|
return NS_VK_VOLUME_MUTE;
|
|
case VK_VOLUME_DOWN:
|
|
return NS_VK_VOLUME_DOWN;
|
|
case VK_VOLUME_UP:
|
|
return NS_VK_VOLUME_UP;
|
|
|
|
// Following keycodes are not defined in our DOM keycodes.
|
|
case VK_BROWSER_BACK:
|
|
case VK_BROWSER_FORWARD:
|
|
case VK_BROWSER_REFRESH:
|
|
case VK_BROWSER_STOP:
|
|
case VK_BROWSER_SEARCH:
|
|
case VK_BROWSER_FAVORITES:
|
|
case VK_BROWSER_HOME:
|
|
case VK_MEDIA_NEXT_TRACK:
|
|
case VK_MEDIA_STOP:
|
|
case VK_MEDIA_PLAY_PAUSE:
|
|
case VK_LAUNCH_MAIL:
|
|
case VK_LAUNCH_MEDIA_SELECT:
|
|
case VK_LAUNCH_APP1:
|
|
case VK_LAUNCH_APP2:
|
|
return 0;
|
|
|
|
// Following OEM specific virtual keycodes should pass through DOM keyCode
|
|
// for compatibility with the other browsers on Windows.
|
|
|
|
// Following OEM specific virtual keycodes are defined for Fujitsu/OASYS.
|
|
case VK_OEM_FJ_JISHO:
|
|
case VK_OEM_FJ_MASSHOU:
|
|
case VK_OEM_FJ_TOUROKU:
|
|
case VK_OEM_FJ_LOYA:
|
|
case VK_OEM_FJ_ROYA:
|
|
// Not sure what means "ICO".
|
|
case VK_ICO_HELP:
|
|
case VK_ICO_00:
|
|
case VK_ICO_CLEAR:
|
|
// Following OEM specific virtual keycodes are defined for Nokia/Ericsson.
|
|
case VK_OEM_RESET:
|
|
case VK_OEM_JUMP:
|
|
case VK_OEM_PA1:
|
|
case VK_OEM_PA2:
|
|
case VK_OEM_PA3:
|
|
case VK_OEM_WSCTRL:
|
|
case VK_OEM_CUSEL:
|
|
case VK_OEM_ATTN:
|
|
case VK_OEM_FINISH:
|
|
case VK_OEM_COPY:
|
|
case VK_OEM_AUTO:
|
|
case VK_OEM_ENLW:
|
|
case VK_OEM_BACKTAB:
|
|
// VK_OEM_CLEAR is defined as not OEM specific, but let's pass though
|
|
// DOM keyCode like other OEM specific virtual keycodes.
|
|
case VK_OEM_CLEAR:
|
|
return uint32_t(aNativeKeyCode);
|
|
|
|
// 0xE1 is an OEM specific virtual keycode. However, the value is already
|
|
// used in our DOM keyCode for AltGr on Linux. So, this virtual keycode
|
|
// cannot pass through DOM keyCode.
|
|
case 0xE1:
|
|
return 0;
|
|
|
|
// Following keycodes are OEM keys which are keycodes for non-alphabet and
|
|
// non-numeric keys, we should compute each keycode of them from unshifted
|
|
// character which is inputted by each key. But if the unshifted character
|
|
// is not an ASCII character but shifted character is an ASCII character,
|
|
// we should refer it.
|
|
case VK_OEM_1:
|
|
case VK_OEM_PLUS:
|
|
case VK_OEM_COMMA:
|
|
case VK_OEM_MINUS:
|
|
case VK_OEM_PERIOD:
|
|
case VK_OEM_2:
|
|
case VK_OEM_3:
|
|
case VK_OEM_4:
|
|
case VK_OEM_5:
|
|
case VK_OEM_6:
|
|
case VK_OEM_7:
|
|
case VK_OEM_8:
|
|
case VK_OEM_102:
|
|
{
|
|
NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode),
|
|
"The key must be printable");
|
|
ModifierKeyState modKeyState(0);
|
|
UniCharsAndModifiers uniChars =
|
|
GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
|
|
if (uniChars.mLength != 1 ||
|
|
uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) {
|
|
modKeyState.Set(MODIFIER_SHIFT);
|
|
uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState);
|
|
if (uniChars.mLength != 1 ||
|
|
uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) {
|
|
return 0;
|
|
}
|
|
}
|
|
return WidgetUtils::ComputeKeyCodeFromChar(uniChars.mChars[0]);
|
|
}
|
|
|
|
// VK_PROCESSKEY means IME already consumed the key event.
|
|
case VK_PROCESSKEY:
|
|
return 0;
|
|
// VK_PACKET is generated by SendInput() API, we don't need to
|
|
// care this message as key event.
|
|
case VK_PACKET:
|
|
return 0;
|
|
}
|
|
NS_WARNING("Unknown key code comes, please check latest MSDN document,"
|
|
" there may be some new keycodes we have not known.");
|
|
return 0;
|
|
}
|
|
|
|
KeyNameIndex
|
|
KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const
|
|
{
|
|
switch (aVirtualKey) {
|
|
|
|
#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
|
|
case aNativeKey: return aKeyNameIndex;
|
|
|
|
#include "NativeKeyToDOMKeyName.h"
|
|
|
|
#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
|
|
|
|
default:
|
|
return IsPrintableCharKey(aVirtualKey) ? KEY_NAME_INDEX_PrintableKey :
|
|
KEY_NAME_INDEX_Unidentified;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* mozilla::widget::DeadKeyTable
|
|
*****************************************************************************/
|
|
|
|
PRUnichar
|
|
DeadKeyTable::GetCompositeChar(PRUnichar aBaseChar) const
|
|
{
|
|
// Dead-key table is sorted by BaseChar in ascending order.
|
|
// Usually they are too small to use binary search.
|
|
|
|
for (uint32_t index = 0; index < mEntries; index++) {
|
|
if (mTable[index].BaseChar == aBaseChar) {
|
|
return mTable[index].CompositeChar;
|
|
}
|
|
if (mTable[index].BaseChar > aBaseChar) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // namespace widget
|
|
} // namespace mozilla
|
|
|