diff --git a/DDrawCompat/Config/Config.cpp b/DDrawCompat/Config/Config.cpp index a13e93a..9b3f018 100644 --- a/DDrawCompat/Config/Config.cpp +++ b/DDrawCompat/Config/Config.cpp @@ -4,6 +4,7 @@ namespace Config { Settings::AlternatePixelCenter alternatePixelCenter; Settings::Antialiasing antialiasing; + Settings::ConfigHotKey configHotKey; Settings::CpuAffinity cpuAffinity; Settings::DesktopColorDepth desktopColorDepth; Settings::DisplayFilter displayFilter; diff --git a/DDrawCompat/Config/Config.h b/DDrawCompat/Config/Config.h index 6e498a1..3848837 100644 --- a/DDrawCompat/Config/Config.h +++ b/DDrawCompat/Config/Config.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,7 @@ namespace Config extern Settings::AlternatePixelCenter alternatePixelCenter; extern Settings::Antialiasing antialiasing; + extern Settings::ConfigHotKey configHotKey; extern Settings::CpuAffinity cpuAffinity; extern Settings::DesktopColorDepth desktopColorDepth; extern Settings::DisplayFilter displayFilter; diff --git a/DDrawCompat/Config/HotKeySetting.h b/DDrawCompat/Config/HotKeySetting.h new file mode 100644 index 0000000..f4b4d8c --- /dev/null +++ b/DDrawCompat/Config/HotKeySetting.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include +#include + +namespace Config +{ + class HotKeySetting : public Setting + { + public: + const Input::HotKey& get() const { return m_value; } + virtual std::string getValueStr() const override { return toString(m_value); } + + protected: + HotKeySetting(const std::string& name, const std::string& default) : Setting(name, default) {} + + virtual void setValue(const std::string& value) override { m_value = Input::parseHotKey(value); } + + private: + Input::HotKey m_value; + }; +} diff --git a/DDrawCompat/Config/ListSetting.h b/DDrawCompat/Config/ListSetting.h index 57be3bb..e94597b 100644 --- a/DDrawCompat/Config/ListSetting.h +++ b/DDrawCompat/Config/ListSetting.h @@ -11,7 +11,7 @@ namespace Config protected: ListSetting(const std::string& name, const std::string& default); - void setValue(const std::string& value) override; + virtual void setValue(const std::string& value) override; virtual void setValues(const std::vector& values) = 0; }; } diff --git a/DDrawCompat/Config/Parser.cpp b/DDrawCompat/Config/Parser.cpp index 99562cc..48652b7 100644 --- a/DDrawCompat/Config/Parser.cpp +++ b/DDrawCompat/Config/Parser.cpp @@ -10,7 +10,6 @@ namespace { void setConfig(const std::string& name, const std::string& value, const std::string& source); - std::string tolower(const std::string& str); auto& getSettings() { @@ -76,23 +75,13 @@ namespace try { - it->second.set(tolower(value), source); + it->second.set(Config::Parser::tolower(value), source); } catch (const Config::ParsingError& error) { throw Config::ParsingError(it->second.getName() + ": " + error.what()); } } - - std::string tolower(const std::string& str) - { - std::string result(str); - for (auto& c : result) - { - c = std::tolower(c, std::locale()); - } - return result; - } } namespace Config @@ -191,6 +180,16 @@ namespace Config return value; } + std::string tolower(const std::string& str) + { + std::string result(str); + for (auto& c : result) + { + c = std::tolower(c, std::locale()); + } + return result; + } + std::string trim(const std::string& str) { auto result(str); diff --git a/DDrawCompat/Config/Parser.h b/DDrawCompat/Config/Parser.h index b1ea43d..f427816 100644 --- a/DDrawCompat/Config/Parser.h +++ b/DDrawCompat/Config/Parser.h @@ -23,6 +23,7 @@ namespace Config int parseInt(const std::string& value, int min, int max); void registerSetting(Setting& setting); std::string removeParam(const std::string& value); + std::string tolower(const std::string& str); std::string trim(const std::string& str); } } diff --git a/DDrawCompat/Config/Settings/ConfigHotKey.h b/DDrawCompat/Config/Settings/ConfigHotKey.h new file mode 100644 index 0000000..08f1beb --- /dev/null +++ b/DDrawCompat/Config/Settings/ConfigHotKey.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace Config +{ + namespace Settings + { + class ConfigHotKey : public HotKeySetting + { + public: + ConfigHotKey() : HotKeySetting("ConfigHotKey", "shift+f11") {} + }; + } +} diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 31d6abb..b0f09b4 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -211,12 +211,14 @@ + + @@ -315,6 +317,7 @@ + @@ -417,6 +420,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index 9641688..22db839 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -519,6 +519,15 @@ Header Files\Config\Settings + + Header Files\Config + + + Header Files\Input + + + Header Files\Config\Settings + @@ -821,6 +830,9 @@ Source Files\Config\Settings + + Source Files\Input + diff --git a/DDrawCompat/Input/HotKey.cpp b/DDrawCompat/Input/HotKey.cpp new file mode 100644 index 0000000..fc09d27 --- /dev/null +++ b/DDrawCompat/Input/HotKey.cpp @@ -0,0 +1,258 @@ +#include + +#include +#include + +namespace +{ + const std::map g_keyNames = []() { + std::map names; +#define VK_HANGUEL 0x15 +#define ADD_KEY_NAME(key) names[Config::Parser::tolower(std::string(#key).substr(3))] = key; + ADD_KEY_NAME(VK_CANCEL); + ADD_KEY_NAME(VK_BACK); + ADD_KEY_NAME(VK_TAB); + ADD_KEY_NAME(VK_CLEAR); + ADD_KEY_NAME(VK_RETURN); + ADD_KEY_NAME(VK_SHIFT); + ADD_KEY_NAME(VK_CONTROL); + ADD_KEY_NAME(VK_MENU); + ADD_KEY_NAME(VK_PAUSE); + ADD_KEY_NAME(VK_CAPITAL); + ADD_KEY_NAME(VK_KANA); + ADD_KEY_NAME(VK_HANGEUL); + ADD_KEY_NAME(VK_HANGUEL); + ADD_KEY_NAME(VK_HANGUL); + ADD_KEY_NAME(VK_IME_ON); + ADD_KEY_NAME(VK_JUNJA); + ADD_KEY_NAME(VK_FINAL); + ADD_KEY_NAME(VK_HANJA); + ADD_KEY_NAME(VK_KANJI); + ADD_KEY_NAME(VK_IME_OFF); + ADD_KEY_NAME(VK_ESCAPE); + ADD_KEY_NAME(VK_CONVERT); + ADD_KEY_NAME(VK_NONCONVERT); + ADD_KEY_NAME(VK_ACCEPT); + ADD_KEY_NAME(VK_MODECHANGE); + ADD_KEY_NAME(VK_SPACE); + ADD_KEY_NAME(VK_PRIOR); + ADD_KEY_NAME(VK_NEXT); + ADD_KEY_NAME(VK_END); + ADD_KEY_NAME(VK_HOME); + ADD_KEY_NAME(VK_LEFT); + ADD_KEY_NAME(VK_UP); + ADD_KEY_NAME(VK_RIGHT); + ADD_KEY_NAME(VK_DOWN); + ADD_KEY_NAME(VK_SELECT); + ADD_KEY_NAME(VK_PRINT); + ADD_KEY_NAME(VK_EXECUTE); + ADD_KEY_NAME(VK_SNAPSHOT); + ADD_KEY_NAME(VK_INSERT); + ADD_KEY_NAME(VK_DELETE); + ADD_KEY_NAME(VK_HELP); + ADD_KEY_NAME(VK_LWIN); + ADD_KEY_NAME(VK_RWIN); + ADD_KEY_NAME(VK_APPS); + ADD_KEY_NAME(VK_SLEEP); + ADD_KEY_NAME(VK_NUMPAD0); + ADD_KEY_NAME(VK_NUMPAD1); + ADD_KEY_NAME(VK_NUMPAD2); + ADD_KEY_NAME(VK_NUMPAD3); + ADD_KEY_NAME(VK_NUMPAD4); + ADD_KEY_NAME(VK_NUMPAD5); + ADD_KEY_NAME(VK_NUMPAD6); + ADD_KEY_NAME(VK_NUMPAD7); + ADD_KEY_NAME(VK_NUMPAD8); + ADD_KEY_NAME(VK_NUMPAD9); + ADD_KEY_NAME(VK_MULTIPLY); + ADD_KEY_NAME(VK_ADD); + ADD_KEY_NAME(VK_SEPARATOR); + ADD_KEY_NAME(VK_SUBTRACT); + ADD_KEY_NAME(VK_DECIMAL); + ADD_KEY_NAME(VK_DIVIDE); + ADD_KEY_NAME(VK_F1); + ADD_KEY_NAME(VK_F2); + ADD_KEY_NAME(VK_F3); + ADD_KEY_NAME(VK_F4); + ADD_KEY_NAME(VK_F5); + ADD_KEY_NAME(VK_F6); + ADD_KEY_NAME(VK_F7); + ADD_KEY_NAME(VK_F8); + ADD_KEY_NAME(VK_F9); + ADD_KEY_NAME(VK_F10); + ADD_KEY_NAME(VK_F11); + ADD_KEY_NAME(VK_F12); + ADD_KEY_NAME(VK_F13); + ADD_KEY_NAME(VK_F14); + ADD_KEY_NAME(VK_F15); + ADD_KEY_NAME(VK_F16); + ADD_KEY_NAME(VK_F17); + ADD_KEY_NAME(VK_F18); + ADD_KEY_NAME(VK_F19); + ADD_KEY_NAME(VK_F20); + ADD_KEY_NAME(VK_F21); + ADD_KEY_NAME(VK_F22); + ADD_KEY_NAME(VK_F23); + ADD_KEY_NAME(VK_F24); + ADD_KEY_NAME(VK_NUMLOCK); + ADD_KEY_NAME(VK_SCROLL); + ADD_KEY_NAME(VK_LSHIFT); + ADD_KEY_NAME(VK_RSHIFT); + ADD_KEY_NAME(VK_LCONTROL); + ADD_KEY_NAME(VK_RCONTROL); + ADD_KEY_NAME(VK_LMENU); + ADD_KEY_NAME(VK_RMENU); + ADD_KEY_NAME(VK_BROWSER_BACK); + ADD_KEY_NAME(VK_BROWSER_FORWARD); + ADD_KEY_NAME(VK_BROWSER_REFRESH); + ADD_KEY_NAME(VK_BROWSER_STOP); + ADD_KEY_NAME(VK_BROWSER_SEARCH); + ADD_KEY_NAME(VK_BROWSER_FAVORITES); + ADD_KEY_NAME(VK_BROWSER_HOME); + ADD_KEY_NAME(VK_VOLUME_MUTE); + ADD_KEY_NAME(VK_VOLUME_DOWN); + ADD_KEY_NAME(VK_VOLUME_UP); + ADD_KEY_NAME(VK_MEDIA_NEXT_TRACK); + ADD_KEY_NAME(VK_MEDIA_PREV_TRACK); + ADD_KEY_NAME(VK_MEDIA_STOP); + ADD_KEY_NAME(VK_MEDIA_PLAY_PAUSE); + ADD_KEY_NAME(VK_LAUNCH_MAIL); + ADD_KEY_NAME(VK_LAUNCH_MEDIA_SELECT); + ADD_KEY_NAME(VK_LAUNCH_APP1); + ADD_KEY_NAME(VK_LAUNCH_APP2); + ADD_KEY_NAME(VK_OEM_1); + ADD_KEY_NAME(VK_OEM_PLUS); + ADD_KEY_NAME(VK_OEM_COMMA); + ADD_KEY_NAME(VK_OEM_MINUS); + ADD_KEY_NAME(VK_OEM_PERIOD); + ADD_KEY_NAME(VK_OEM_2); + ADD_KEY_NAME(VK_OEM_3); + ADD_KEY_NAME(VK_OEM_4); + ADD_KEY_NAME(VK_OEM_5); + ADD_KEY_NAME(VK_OEM_6); + ADD_KEY_NAME(VK_OEM_7); + ADD_KEY_NAME(VK_OEM_8); + ADD_KEY_NAME(VK_OEM_102); + ADD_KEY_NAME(VK_PROCESSKEY); + ADD_KEY_NAME(VK_PACKET); + ADD_KEY_NAME(VK_ATTN); + ADD_KEY_NAME(VK_CRSEL); + ADD_KEY_NAME(VK_EXSEL); + ADD_KEY_NAME(VK_EREOF); + ADD_KEY_NAME(VK_PLAY); + ADD_KEY_NAME(VK_ZOOM); + ADD_KEY_NAME(VK_NONAME); + ADD_KEY_NAME(VK_PA1); + ADD_KEY_NAME(VK_OEM_CLEAR); +#undef ADD_KEY_NAME + return names; + }(); + + bool areExcludedModifierKeysDown(const std::set& modifiers, UINT either, UINT left, UINT right) + { + return modifiers.find(either) == modifiers.end() && + modifiers.find(left) == modifiers.end() && + modifiers.find(right) == modifiers.end() && + (GetAsyncKeyState(either) & 0x8000); + } + + UINT getKeyCode(const std::string& name) + { + if (1 == name.length()) + { + if (name[0] >= '0' && name[0] <= '9' || + name[0] >= 'a' && name[0] <= 'z') + { + return std::toupper(name[0], std::locale()); + } + } + + auto it = g_keyNames.find(name); + if (it == g_keyNames.end()) + { + throw Config::ParsingError("Invalid hotkey: '" + name + "'"); + } + return it->second; + } + + std::string getKeyName(UINT key) + { + if (key >= '0' && key <= '9' || + key >= 'A' && key <= 'Z') + { + return std::string(1, static_cast(std::tolower(key, std::locale()))); + } + + auto it = std::find_if(g_keyNames.begin(), g_keyNames.end(), [&](const auto& pair) { return pair.second == key; }); + return it != g_keyNames.end() ? it->first : "none"; + } +} + +namespace Input +{ + bool areModifierKeysDown(const std::set& modifiers) + { + for (auto modifier : modifiers) + { + if (0 == (GetAsyncKeyState(modifier) & 0x8000)) + { + return false; + } + } + + return !areExcludedModifierKeysDown(modifiers, VK_SHIFT, VK_LSHIFT, VK_RSHIFT) && + !areExcludedModifierKeysDown(modifiers, VK_CONTROL, VK_LCONTROL, VK_RCONTROL) && + !areExcludedModifierKeysDown(modifiers, VK_MENU, VK_LMENU, VK_RMENU); + } + + bool isModifierKey(UINT vk) + { + return vk >= VK_SHIFT && vk <= VK_MENU || + vk >= VK_LSHIFT && vk <= VK_RMENU; + } + + HotKey parseHotKey(std::string str) + { + HotKey hotKey; + if (str.empty() || "none" == str) + { + return hotKey; + } + + int pos = str.find('+'); + while (std::string::npos != pos) + { + std::string modifierName(Config::Parser::trim(str.substr(0, pos))); + UINT modifier = getKeyCode(modifierName); + if (!isModifierKey(modifier)) + { + throw Config::ParsingError("Not a modifier key: '" + modifierName + "'"); + } + hotKey.modifiers.insert(modifier); + str = str.substr(pos + 1); + pos = str.find('+'); + } + + hotKey.vk = getKeyCode(str); + if (isModifierKey(hotKey.vk)) + { + throw Config::ParsingError("A hotkey cannot end with a modifier key: '" + str + "'"); + } + return hotKey; + } + + std::string toString(const HotKey& hotKey) + { + if (!hotKey.vk) + { + return "none"; + } + + std::string str; + for (UINT modifier : hotKey.modifiers) + { + str += getKeyName(modifier) + '+'; + } + return str + getKeyName(hotKey.vk); + } +} diff --git a/DDrawCompat/Input/HotKey.h b/DDrawCompat/Input/HotKey.h new file mode 100644 index 0000000..8efcf9d --- /dev/null +++ b/DDrawCompat/Input/HotKey.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include + +namespace Input +{ + struct HotKey + { + UINT vk; + std::set modifiers; + + HotKey() : vk(0) {} + }; + + bool areModifierKeysDown(const std::set& modifiers); + bool isModifierKey(UINT vk); + HotKey parseHotKey(std::string str); + std::string toString(const HotKey& hotKey); +} diff --git a/DDrawCompat/Input/Input.cpp b/DDrawCompat/Input/Input.cpp index fe37b23..d6fea45 100644 --- a/DDrawCompat/Input/Input.cpp +++ b/DDrawCompat/Input/Input.cpp @@ -77,18 +77,24 @@ namespace LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { - if (HC_ACTION == nCode && (WM_KEYDOWN == wParam || WM_SYSKEYDOWN == wParam)) + if (HC_ACTION == nCode && + (WM_KEYDOWN == wParam || WM_KEYUP == wParam || WM_SYSKEYDOWN == wParam || WM_SYSKEYUP == wParam)) { DWORD pid = 0; GetWindowThreadProcessId(GetForegroundWindow(), &pid); if (GetCurrentProcessId() == pid) { auto llHook = reinterpret_cast(lParam); - auto it = std::find_if(g_hotKeys.begin(), g_hotKeys.end(), - [&](const auto& v) { return v.first.vk == llHook->vkCode; }); - if (it != g_hotKeys.end()) + for (auto& hotkey : g_hotKeys) { - it->second.action(it->second.context); + if (hotkey.first.vk == llHook->vkCode && Input::areModifierKeysDown(hotkey.first.modifiers)) + { + if (WM_KEYDOWN == wParam || WM_SYSKEYDOWN == wParam) + { + hotkey.second.action(hotkey.second.context); + } + return 1; + } } } } @@ -245,10 +251,13 @@ namespace Input void registerHotKey(const HotKey& hotKey, std::function action, void* context) { - g_hotKeys[hotKey] = { action, context }; - if (!g_keyboardHook) + if (0 != hotKey.vk) { - resetKeyboardHook(); + g_hotKeys[hotKey] = { action, context }; + if (!g_keyboardHook) + { + resetKeyboardHook(); + } } } diff --git a/DDrawCompat/Input/Input.h b/DDrawCompat/Input/Input.h index 656c8f9..601fd6c 100644 --- a/DDrawCompat/Input/Input.h +++ b/DDrawCompat/Input/Input.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -13,11 +12,7 @@ namespace Overlay namespace Input { - struct HotKey - { - UINT vk; - std::set modifiers; - }; + struct HotKey; bool operator<(const HotKey& lhs, const HotKey& rhs); diff --git a/DDrawCompat/Overlay/ConfigWindow.cpp b/DDrawCompat/Overlay/ConfigWindow.cpp index 07af079..4095ea9 100644 --- a/DDrawCompat/Overlay/ConfigWindow.cpp +++ b/DDrawCompat/Overlay/ConfigWindow.cpp @@ -8,7 +8,7 @@ namespace Overlay { ConfigWindow::ConfigWindow() - : Window(nullptr, { 0, 0, SettingControl::TOTAL_WIDTH, 200 }, { VK_F11, {} }) + : Window(nullptr, { 0, 0, SettingControl::TOTAL_WIDTH, 200 }, Config::configHotKey.get()) , m_focus(nullptr) { addControl(Config::alternatePixelCenter); diff --git a/DDrawCompat/Overlay/Window.h b/DDrawCompat/Overlay/Window.h index 3cabbc4..5c7df0e 100644 --- a/DDrawCompat/Overlay/Window.h +++ b/DDrawCompat/Overlay/Window.h @@ -5,6 +5,7 @@ #include #include +#include #include namespace Overlay