/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/layers/KeyboardMap.h" #include "mozilla/TextEvents.h" // for IgnoreModifierState, ShortcutKeyCandidate namespace mozilla { namespace layers { KeyboardShortcut::KeyboardShortcut() : mKeyCode(0), mCharCode(0), mModifiers(0), mModifiersMask(0), mEventType(KeyboardInput::KeyboardEventType::KEY_OTHER), mDispatchToContent(false) {} KeyboardShortcut::KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType, uint32_t aKeyCode, uint32_t aCharCode, Modifiers aModifiers, Modifiers aModifiersMask, const KeyboardScrollAction& aAction) : mAction(aAction), mKeyCode(aKeyCode), mCharCode(aCharCode), mModifiers(aModifiers), mModifiersMask(aModifiersMask), mEventType(aEventType), mDispatchToContent(false) {} KeyboardShortcut::KeyboardShortcut(KeyboardInput::KeyboardEventType aEventType, uint32_t aKeyCode, uint32_t aCharCode, Modifiers aModifiers, Modifiers aModifiersMask) : mKeyCode(aKeyCode), mCharCode(aCharCode), mModifiers(aModifiers), mModifiersMask(aModifiersMask), mEventType(aEventType), mDispatchToContent(true) {} /* static */ void KeyboardShortcut::AppendHardcodedShortcuts( nsTArray& aShortcuts) { // Tab KeyboardShortcut tab1; tab1.mDispatchToContent = true; tab1.mKeyCode = NS_VK_TAB; tab1.mCharCode = 0; tab1.mModifiers = 0; tab1.mModifiersMask = 0; tab1.mEventType = KeyboardInput::KEY_PRESS; aShortcuts.AppendElement(tab1); // F6 KeyboardShortcut tab2; tab2.mDispatchToContent = true; tab2.mKeyCode = NS_VK_F6; tab2.mCharCode = 0; tab2.mModifiers = 0; tab2.mModifiersMask = 0; tab2.mEventType = KeyboardInput::KEY_PRESS; aShortcuts.AppendElement(tab2); } bool KeyboardShortcut::Matches(const KeyboardInput& aInput, const IgnoreModifierState& aIgnore, uint32_t aOverrideCharCode) const { return mEventType == aInput.mType && MatchesKey(aInput, aOverrideCharCode) && MatchesModifiers(aInput, aIgnore); } bool KeyboardShortcut::MatchesKey(const KeyboardInput& aInput, uint32_t aOverrideCharCode) const { // Compare by the key code if we have one if (!mCharCode) { return mKeyCode == aInput.mKeyCode; } // We are comparing by char code uint32_t charCode; // If we are comparing against a shortcut candidate then we might // have an override char code if (aOverrideCharCode) { charCode = aOverrideCharCode; } else { charCode = aInput.mCharCode; } // Both char codes must be in lowercase to compare correctly if (IS_IN_BMP(charCode)) { charCode = ToLowerCase(static_cast(charCode)); } return mCharCode == charCode; } bool KeyboardShortcut::MatchesModifiers( const KeyboardInput& aInput, const IgnoreModifierState& aIgnore) const { Modifiers modifiersMask = mModifiersMask; // If we are ignoring Shift or OS, then unset that part of the mask if (aIgnore.mOS) { modifiersMask &= ~MODIFIER_OS; } if (aIgnore.mShift) { modifiersMask &= ~MODIFIER_SHIFT; } // Mask off the modifiers we are ignoring from the keyboard input return (aInput.modifiers & modifiersMask) == mModifiers; } KeyboardMap::KeyboardMap(nsTArray&& aShortcuts) : mShortcuts(aShortcuts) {} KeyboardMap::KeyboardMap() = default; Maybe KeyboardMap::FindMatch( const KeyboardInput& aEvent) const { // If there are no shortcut candidates, then just search with with the // keyboard input if (aEvent.mShortcutCandidates.IsEmpty()) { return FindMatchInternal(aEvent, IgnoreModifierState()); } // Otherwise do a search with each shortcut candidate in order for (auto& key : aEvent.mShortcutCandidates) { IgnoreModifierState ignoreModifierState; ignoreModifierState.mShift = key.mIgnoreShift; auto match = FindMatchInternal(aEvent, ignoreModifierState, key.mCharCode); if (match) { return match; } } return Nothing(); } Maybe KeyboardMap::FindMatchInternal( const KeyboardInput& aEvent, const IgnoreModifierState& aIgnore, uint32_t aOverrideCharCode) const { for (auto& shortcut : mShortcuts) { if (shortcut.Matches(aEvent, aIgnore, aOverrideCharCode)) { return Some(shortcut); } } #ifdef XP_WIN // Windows native applications ignore Windows-Logo key state when checking // shortcut keys even if the key is pressed. Therefore, if there is no // shortcut key which exactly matches current modifier state, we should // retry to look for a shortcut key without the Windows-Logo key press. if (!aIgnore.mOS && (aEvent.modifiers & MODIFIER_OS)) { IgnoreModifierState ignoreModifierState(aIgnore); ignoreModifierState.mOS = true; return FindMatchInternal(aEvent, ignoreModifierState, aOverrideCharCode); } #endif return Nothing(); } } // namespace layers } // namespace mozilla