SceneSwitcher/plugins/base/utils/windows/windows.cpp
WarmUpTill 7d0332dd0e Restructure library and plugins
The "core" macro conditions and actions have been extracted out to the
"base" plugin.

The library now mostly contains functionality which is required across
all plugins and (e.g. definitions for macro segments).

The goal is to reduce the complexity and cross-dependencies and group
the source files in a better way.

This should relsove the "library limit of 65535 objects exceeded" build
issue occuring in some Windows build environments.
2024-01-27 14:10:34 +01:00

315 lines
8.1 KiB
C++

#include "hotkey-helpers.hpp"
#include "log-helper.hpp"
#include "plugin-state-helpers.hpp"
#include <obs-frontend-api.h>
#include <QAbstractEventDispatcher>
#include <QAbstractNativeEventFilter>
#include <unordered_map>
#include <util/platform.h>
#include <windows.h>
namespace advss {
static bool canSimulateKeyPresses = true;
bool CanSimulateKeyPresses()
{
return canSimulateKeyPresses;
}
static const std::unordered_map<HotkeyType, long> keyTable = {
// Chars
{HotkeyType::Key_A, 0x41},
{HotkeyType::Key_B, 0x42},
{HotkeyType::Key_C, 0x43},
{HotkeyType::Key_D, 0x44},
{HotkeyType::Key_E, 0x45},
{HotkeyType::Key_F, 0x46},
{HotkeyType::Key_G, 0x47},
{HotkeyType::Key_H, 0x48},
{HotkeyType::Key_I, 0x49},
{HotkeyType::Key_J, 0x4A},
{HotkeyType::Key_K, 0x4B},
{HotkeyType::Key_L, 0x4C},
{HotkeyType::Key_M, 0x4D},
{HotkeyType::Key_N, 0x4E},
{HotkeyType::Key_O, 0x4F},
{HotkeyType::Key_P, 0x50},
{HotkeyType::Key_Q, 0x51},
{HotkeyType::Key_R, 0x52},
{HotkeyType::Key_S, 0x53},
{HotkeyType::Key_T, 0x54},
{HotkeyType::Key_U, 0x55},
{HotkeyType::Key_V, 0x56},
{HotkeyType::Key_W, 0x57},
{HotkeyType::Key_X, 0x58},
{HotkeyType::Key_Y, 0x59},
{HotkeyType::Key_Z, 0x5A},
// Numbers
{HotkeyType::Key_0, 0x30},
{HotkeyType::Key_1, 0x31},
{HotkeyType::Key_2, 0x32},
{HotkeyType::Key_3, 0x33},
{HotkeyType::Key_4, 0x34},
{HotkeyType::Key_5, 0x35},
{HotkeyType::Key_6, 0x36},
{HotkeyType::Key_7, 0x37},
{HotkeyType::Key_8, 0x38},
{HotkeyType::Key_9, 0x39},
{HotkeyType::Key_F1, VK_F1},
{HotkeyType::Key_F2, VK_F2},
{HotkeyType::Key_F3, VK_F3},
{HotkeyType::Key_F4, VK_F4},
{HotkeyType::Key_F5, VK_F5},
{HotkeyType::Key_F6, VK_F6},
{HotkeyType::Key_F7, VK_F7},
{HotkeyType::Key_F8, VK_F8},
{HotkeyType::Key_F9, VK_F9},
{HotkeyType::Key_F10, VK_F10},
{HotkeyType::Key_F11, VK_F11},
{HotkeyType::Key_F12, VK_F12},
{HotkeyType::Key_F13, VK_F13},
{HotkeyType::Key_F14, VK_F14},
{HotkeyType::Key_F15, VK_F15},
{HotkeyType::Key_F16, VK_F16},
{HotkeyType::Key_F17, VK_F17},
{HotkeyType::Key_F18, VK_F18},
{HotkeyType::Key_F19, VK_F19},
{HotkeyType::Key_F20, VK_F20},
{HotkeyType::Key_F21, VK_F21},
{HotkeyType::Key_F22, VK_F22},
{HotkeyType::Key_F23, VK_F23},
{HotkeyType::Key_F24, VK_F24},
{HotkeyType::Key_Escape, VK_ESCAPE},
{HotkeyType::Key_Space, VK_SPACE},
{HotkeyType::Key_Return, VK_RETURN},
{HotkeyType::Key_Backspace, VK_BACK},
{HotkeyType::Key_Tab, VK_TAB},
{HotkeyType::Key_Shift_L, VK_LSHIFT},
{HotkeyType::Key_Shift_R, VK_RSHIFT},
{HotkeyType::Key_Control_L, VK_LCONTROL},
{HotkeyType::Key_Control_R, VK_RCONTROL},
{HotkeyType::Key_Alt_L, VK_LMENU},
{HotkeyType::Key_Alt_R, VK_RMENU},
{HotkeyType::Key_Win_L, VK_LWIN},
{HotkeyType::Key_Win_R, VK_RWIN},
{HotkeyType::Key_Apps, VK_APPS},
{HotkeyType::Key_CapsLock, VK_CAPITAL},
{HotkeyType::Key_NumLock, VK_NUMLOCK},
{HotkeyType::Key_ScrollLock, VK_SCROLL},
{HotkeyType::Key_PrintScreen, VK_SNAPSHOT},
{HotkeyType::Key_Pause, VK_PAUSE},
{HotkeyType::Key_Insert, VK_INSERT},
{HotkeyType::Key_Delete, VK_DELETE},
{HotkeyType::Key_PageUP, VK_PRIOR},
{HotkeyType::Key_PageDown, VK_NEXT},
{HotkeyType::Key_Home, VK_HOME},
{HotkeyType::Key_End, VK_END},
{HotkeyType::Key_Left, VK_LEFT},
{HotkeyType::Key_Up, VK_UP},
{HotkeyType::Key_Right, VK_RIGHT},
{HotkeyType::Key_Down, VK_DOWN},
{HotkeyType::Key_Numpad0, VK_NUMPAD0},
{HotkeyType::Key_Numpad1, VK_NUMPAD1},
{HotkeyType::Key_Numpad2, VK_NUMPAD2},
{HotkeyType::Key_Numpad3, VK_NUMPAD3},
{HotkeyType::Key_Numpad4, VK_NUMPAD4},
{HotkeyType::Key_Numpad5, VK_NUMPAD5},
{HotkeyType::Key_Numpad6, VK_NUMPAD6},
{HotkeyType::Key_Numpad7, VK_NUMPAD7},
{HotkeyType::Key_Numpad8, VK_NUMPAD8},
{HotkeyType::Key_Numpad9, VK_NUMPAD9},
{HotkeyType::Key_NumpadAdd, VK_ADD},
{HotkeyType::Key_NumpadSubtract, VK_SUBTRACT},
{HotkeyType::Key_NumpadMultiply, VK_MULTIPLY},
{HotkeyType::Key_NumpadDivide, VK_DIVIDE},
{HotkeyType::Key_NumpadDecimal, VK_DECIMAL},
{HotkeyType::Key_NumpadEnter, VK_RETURN},
};
void PressKeys(const std::vector<HotkeyType> keys, int duration)
{
const int repeatInterval = 100;
INPUT ip;
ip.type = INPUT_KEYBOARD;
ip.ki.wScan = 0;
ip.ki.time = 0;
ip.ki.dwExtraInfo = 0;
for (int cur = 0; cur < duration; cur += repeatInterval) {
// Press keys
ip.ki.dwFlags = 0;
for (const auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
ip.ki.wVk = (WORD)it->second;
SendInput(1, &ip, sizeof(INPUT));
}
// When instantly releasing the key presses OBS might miss them
Sleep(repeatInterval);
}
// Release keys
ip.ki.dwFlags = KEYEVENTF_KEYUP;
for (const auto &key : keys) {
auto it = keyTable.find(key);
if (it == keyTable.end()) {
continue;
}
ip.ki.wVk = (WORD)it->second;
SendInput(1, &ip, sizeof(INPUT));
}
}
static HWND getHWNDfromTitle(const std::string &title)
{
HWND hwnd = NULL;
wchar_t wTitle[512];
os_utf8_to_wcs(title.c_str(), 0, wTitle, 512);
hwnd = FindWindowEx(NULL, NULL, NULL, wTitle);
return hwnd;
}
std::string GetWindowClassByWindowTitle(const std::string &window)
{
HWND hwnd = NULL;
hwnd = getHWNDfromTitle(window);
if (!hwnd) {
return "";
}
std::wstring wClass;
wClass.resize(1024);
if (!GetClassNameW(hwnd, &wClass[0], wClass.capacity())) {
return "";
}
size_t len = os_wcs_to_utf8(wClass.c_str(), 0, nullptr, 0);
std::string className;
className.resize(len);
os_wcs_to_utf8(wClass.c_str(), 0, &className[0], len + 1);
return className;
}
class RawMouseInputFilter;
static RawMouseInputFilter *mouseInputFilter;
static std::chrono::high_resolution_clock::time_point lastMouseLeftClickTime{};
static std::chrono::high_resolution_clock::time_point lastMouseMiddleClickTime{};
static std::chrono::high_resolution_clock::time_point lastMouseRightClickTime{};
std::chrono::high_resolution_clock::time_point GetLastMouseLeftClickTime()
{
return lastMouseLeftClickTime;
}
std::chrono::high_resolution_clock::time_point GetLastMouseMiddleClickTime()
{
return lastMouseMiddleClickTime;
}
std::chrono::high_resolution_clock::time_point GetLastMouseRightClickTime()
{
return lastMouseRightClickTime;
}
static void handleRawMouseInput(LPARAM lParam)
{
UINT dwSize;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize,
sizeof(RAWINPUTHEADER));
LPBYTE lpb = new BYTE[dwSize];
if (lpb == NULL) {
return;
}
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize,
sizeof(RAWINPUTHEADER)) != dwSize)
OutputDebugString(TEXT(
"GetRawInputData does not return correct size !\n"));
RAWINPUT *raw = (RAWINPUT *)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE) {
switch (raw->data.mouse.usButtonFlags) {
case RI_MOUSE_BUTTON_1_DOWN:
lastMouseLeftClickTime =
std::chrono::high_resolution_clock::now();
break;
case RI_MOUSE_BUTTON_3_DOWN:
lastMouseMiddleClickTime =
std::chrono::high_resolution_clock::now();
break;
case RI_MOUSE_BUTTON_2_DOWN:
lastMouseRightClickTime =
std::chrono::high_resolution_clock::now();
break;
default:
break;
}
}
delete[] lpb;
}
class RawMouseInputFilter : public QAbstractNativeEventFilter {
public:
virtual bool nativeEventFilter(const QByteArray &eventType,
void *message, qintptr *) Q_DECL_OVERRIDE
{
if (eventType != "windows_generic_MSG") {
return false;
}
MSG *msg = reinterpret_cast<MSG *>(message);
if (msg->message != WM_INPUT) {
return false;
}
handleRawMouseInput(msg->lParam);
return false;
}
};
static void setupMouseEeventFilter()
{
mouseInputFilter = new RawMouseInputFilter;
QAbstractEventDispatcher::instance()->installNativeEventFilter(
mouseInputFilter);
RAWINPUTDEVICE rid;
rid.dwFlags = RIDEV_INPUTSINK;
rid.usUsagePage = 1;
rid.usUsage = 2;
rid.hwndTarget = (HWND)obs_frontend_get_main_window_handle();
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
blog(LOG_WARNING, "Registering for raw mouse input failed!\n"
"Mouse click detection not functional!");
}
}
static bool setup()
{
AddPluginInitStep(setupMouseEeventFilter);
AddPluginCleanupStep([]() {
if (mouseInputFilter) {
delete mouseInputFilter;
mouseInputFilter = nullptr;
}
});
return true;
}
static bool init = setup();
} // namespace advss