283 lines
9.8 KiB
C++
283 lines
9.8 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "pch.h"
|
|
#include "KeyChordSerialization.h"
|
|
|
|
using namespace winrt::Microsoft::Terminal::Settings;
|
|
|
|
static constexpr std::wstring_view CTRL_KEY{ L"ctrl" };
|
|
static constexpr std::wstring_view SHIFT_KEY{ L"shift" };
|
|
static constexpr std::wstring_view ALT_KEY{ L"alt" };
|
|
|
|
static constexpr int MAX_CHORD_PARTS = 4;
|
|
|
|
// clang-format off
|
|
static const std::unordered_map<std::wstring_view, int32_t> vkeyNamePairs {
|
|
{ L"backspace" , VK_BACK },
|
|
{ L"tab" , VK_TAB },
|
|
{ L"enter" , VK_RETURN },
|
|
{ L"esc" , VK_ESCAPE },
|
|
{ L"escape" , VK_ESCAPE },
|
|
{ L"space" , VK_SPACE },
|
|
{ L"pgup" , VK_PRIOR },
|
|
{ L"pageup" , VK_PRIOR },
|
|
{ L"pgdn" , VK_NEXT },
|
|
{ L"pagedown" , VK_NEXT },
|
|
{ L"end" , VK_END },
|
|
{ L"home" , VK_HOME },
|
|
{ L"left" , VK_LEFT },
|
|
{ L"up" , VK_UP },
|
|
{ L"right" , VK_RIGHT },
|
|
{ L"down" , VK_DOWN },
|
|
{ L"insert" , VK_INSERT },
|
|
{ L"delete" , VK_DELETE },
|
|
{ L"numpad_0" , VK_NUMPAD0 },
|
|
{ L"numpad0" , VK_NUMPAD0 },
|
|
{ L"numpad_1" , VK_NUMPAD1 },
|
|
{ L"numpad1" , VK_NUMPAD1 },
|
|
{ L"numpad_2" , VK_NUMPAD2 },
|
|
{ L"numpad2" , VK_NUMPAD2 },
|
|
{ L"numpad_3" , VK_NUMPAD3 },
|
|
{ L"numpad3" , VK_NUMPAD3 },
|
|
{ L"numpad_4" , VK_NUMPAD4 },
|
|
{ L"numpad4" , VK_NUMPAD4 },
|
|
{ L"numpad_5" , VK_NUMPAD5 },
|
|
{ L"numpad5" , VK_NUMPAD5 },
|
|
{ L"numpad_6" , VK_NUMPAD6 },
|
|
{ L"numpad6" , VK_NUMPAD6 },
|
|
{ L"numpad_7" , VK_NUMPAD7 },
|
|
{ L"numpad7" , VK_NUMPAD7 },
|
|
{ L"numpad_8" , VK_NUMPAD8 },
|
|
{ L"numpad8" , VK_NUMPAD8 },
|
|
{ L"numpad_9" , VK_NUMPAD9 },
|
|
{ L"numpad9" , VK_NUMPAD9 },
|
|
{ L"numpad_multiply" , VK_MULTIPLY },
|
|
{ L"numpad_plus" , VK_ADD },
|
|
{ L"numpad_add" , VK_ADD },
|
|
{ L"numpad_minus" , VK_SUBTRACT },
|
|
{ L"numpad_subtract" , VK_SUBTRACT },
|
|
{ L"numpad_period" , VK_DECIMAL },
|
|
{ L"numpad_decimal" , VK_DECIMAL },
|
|
{ L"numpad_divide" , VK_DIVIDE },
|
|
{ L"f1" , VK_F1 },
|
|
{ L"f2" , VK_F2 },
|
|
{ L"f3" , VK_F3 },
|
|
{ L"f4" , VK_F4 },
|
|
{ L"f5" , VK_F5 },
|
|
{ L"f6" , VK_F6 },
|
|
{ L"f7" , VK_F7 },
|
|
{ L"f8" , VK_F8 },
|
|
{ L"f9" , VK_F9 },
|
|
{ L"f10" , VK_F10 },
|
|
{ L"f11" , VK_F11 },
|
|
{ L"f12" , VK_F12 },
|
|
{ L"f13" , VK_F13 },
|
|
{ L"f14" , VK_F14 },
|
|
{ L"f15" , VK_F15 },
|
|
{ L"f16" , VK_F16 },
|
|
{ L"f17" , VK_F17 },
|
|
{ L"f18" , VK_F18 },
|
|
{ L"f19" , VK_F19 },
|
|
{ L"f20" , VK_F20 },
|
|
{ L"f21" , VK_F21 },
|
|
{ L"f22" , VK_F22 },
|
|
{ L"f23" , VK_F23 },
|
|
{ L"f24" , VK_F24 },
|
|
{ L"plus" , VK_OEM_PLUS }
|
|
};
|
|
// clang-format on
|
|
|
|
// Function Description:
|
|
// - Deserializes the given string into a new KeyChord instance. If this
|
|
// fails to translate the string into a keychord, it will throw a
|
|
// hresult_invalid_argument exception.
|
|
// - The string should fit the format "[ctrl+][alt+][shift+]<keyName>",
|
|
// where each modifier is optional, and keyName is either one of the
|
|
// names listed in the vkeyNamePairs vector above, or is one of 0-9a-zA-Z.
|
|
// Arguments:
|
|
// - hstr: the string to parse into a keychord.
|
|
// Return Value:
|
|
// - a newly constructed KeyChord
|
|
winrt::Microsoft::Terminal::Settings::KeyChord KeyChordSerialization::FromString(const winrt::hstring& hstr)
|
|
{
|
|
std::wstring wstr{ hstr };
|
|
|
|
// Split the string on '+'
|
|
std::wstring temp;
|
|
std::vector<std::wstring> parts;
|
|
std::wstringstream wss(wstr);
|
|
|
|
while (std::getline(wss, temp, L'+'))
|
|
{
|
|
parts.push_back(temp);
|
|
|
|
// If we have > 4, something's wrong.
|
|
if (parts.size() > MAX_CHORD_PARTS)
|
|
{
|
|
throw winrt::hresult_invalid_argument();
|
|
}
|
|
}
|
|
|
|
KeyModifiers modifiers = KeyModifiers::None;
|
|
int32_t vkey = 0;
|
|
|
|
// Look for ctrl, shift, alt. Anything else might be a key
|
|
for (const auto& part : parts)
|
|
{
|
|
std::wstring lowercase = part;
|
|
std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), std::towlower);
|
|
if (lowercase == CTRL_KEY)
|
|
{
|
|
modifiers |= KeyModifiers::Ctrl;
|
|
}
|
|
else if (lowercase == ALT_KEY)
|
|
{
|
|
modifiers |= KeyModifiers::Alt;
|
|
}
|
|
else if (lowercase == SHIFT_KEY)
|
|
{
|
|
modifiers |= KeyModifiers::Shift;
|
|
}
|
|
else
|
|
{
|
|
bool foundKey = false;
|
|
// For potential keys, look through the pairs of strings and vkeys
|
|
if (part.size() == 1)
|
|
{
|
|
const wchar_t wch = part.at(0);
|
|
// Quick lookup: ranges of vkeys that correlate directly to a key.
|
|
if (wch >= L'0' && wch <= L'9')
|
|
{
|
|
vkey = static_cast<int32_t>(wch);
|
|
foundKey = true;
|
|
}
|
|
else if (wch >= L'a' && wch <= L'z')
|
|
{
|
|
// subtract 0x20 to shift to uppercase
|
|
vkey = static_cast<int32_t>(wch - 0x20);
|
|
foundKey = true;
|
|
}
|
|
else if (wch >= L'A' && wch <= L'Z')
|
|
{
|
|
vkey = static_cast<int32_t>(wch);
|
|
foundKey = true;
|
|
}
|
|
}
|
|
|
|
// If we didn't find the key with a quick lookup, search the
|
|
// table to see if we have a matching name.
|
|
if (!foundKey && vkeyNamePairs.find(part) != vkeyNamePairs.end())
|
|
{
|
|
vkey = vkeyNamePairs.at(part);
|
|
foundKey = true;
|
|
break;
|
|
}
|
|
|
|
// If we haven't found a key, attempt a keyboard mapping
|
|
if (!foundKey && part.size() == 1)
|
|
{
|
|
auto oemVk = VkKeyScanW(part[0]);
|
|
if (oemVk != -1)
|
|
{
|
|
vkey = oemVk & 0xFF;
|
|
auto oemModifiers = (oemVk & 0xFF00) >> 8;
|
|
// We're using WI_SetFlagIf instead of WI_UpdateFlag because we want to be strictly additive
|
|
// to the user's specified modifiers. ctrl+| should be the same as ctrl+shift+\,
|
|
// but if we used WI_UpdateFlag, ctrl+shift+\ would turn _off_ Shift because \ doesn't
|
|
// require it.
|
|
WI_SetFlagIf(modifiers, KeyModifiers::Shift, WI_IsFlagSet(oemModifiers, 1U));
|
|
WI_SetFlagIf(modifiers, KeyModifiers::Ctrl, WI_IsFlagSet(oemModifiers, 2U));
|
|
WI_SetFlagIf(modifiers, KeyModifiers::Alt, WI_IsFlagSet(oemModifiers, 4U));
|
|
foundKey = true;
|
|
}
|
|
}
|
|
|
|
// If we weren't able to find a match, throw an exception.
|
|
if (!foundKey)
|
|
{
|
|
throw winrt::hresult_invalid_argument();
|
|
}
|
|
}
|
|
}
|
|
|
|
return winrt::Microsoft::Terminal::Settings::KeyChord{ modifiers, vkey };
|
|
}
|
|
|
|
// Function Description:
|
|
// - Serialize this keychord into a string representation.
|
|
// - The string will fit the format "[ctrl+][alt+][shift+]<keyName>",
|
|
// where each modifier is optional, and keyName is either one of the
|
|
// names listed in the vkeyNamePairs vector above, or is one of 0-9a-z.
|
|
// Return Value:
|
|
// - a string which is an equivalent serialization of this object.
|
|
winrt::hstring KeyChordSerialization::ToString(const KeyChord& chord)
|
|
{
|
|
bool serializedSuccessfully = false;
|
|
const auto modifiers = chord.Modifiers();
|
|
const auto vkey = chord.Vkey();
|
|
|
|
std::wstring buffer{ L"" };
|
|
|
|
// Add modifiers
|
|
if (WI_IsFlagSet(modifiers, KeyModifiers::Ctrl))
|
|
{
|
|
buffer += CTRL_KEY;
|
|
buffer += L"+";
|
|
}
|
|
if (WI_IsFlagSet(modifiers, KeyModifiers::Alt))
|
|
{
|
|
buffer += ALT_KEY;
|
|
buffer += L"+";
|
|
}
|
|
if (WI_IsFlagSet(modifiers, KeyModifiers::Shift))
|
|
{
|
|
buffer += SHIFT_KEY;
|
|
buffer += L"+";
|
|
}
|
|
|
|
// Quick lookup: ranges of vkeys that correlate directly to a key.
|
|
if (vkey >= L'0' && vkey <= L'9')
|
|
{
|
|
buffer += std::wstring(1, static_cast<wchar_t>(vkey));
|
|
serializedSuccessfully = true;
|
|
}
|
|
else if (vkey >= L'A' && vkey <= L'Z')
|
|
{
|
|
// add 0x20 to shift to lowercase
|
|
buffer += std::wstring(1, static_cast<wchar_t>(vkey + 0x20));
|
|
serializedSuccessfully = true;
|
|
}
|
|
else
|
|
{
|
|
bool foundKey = false;
|
|
for (const auto& pair : vkeyNamePairs)
|
|
{
|
|
if (pair.second == vkey)
|
|
{
|
|
buffer += pair.first;
|
|
serializedSuccessfully = true;
|
|
foundKey = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!foundKey)
|
|
{
|
|
auto mappedChar = MapVirtualKeyW(vkey, MAPVK_VK_TO_CHAR);
|
|
if (mappedChar != 0)
|
|
{
|
|
wchar_t mappedWch = gsl::narrow_cast<wchar_t>(mappedChar);
|
|
buffer += std::wstring_view{ &mappedWch, 1 };
|
|
serializedSuccessfully = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!serializedSuccessfully)
|
|
{
|
|
buffer = L"";
|
|
}
|
|
|
|
return winrt::hstring{ buffer };
|
|
}
|