SceneSwitcher/plugins/base/utils/windows/windows.cpp

388 lines
9.4 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 bool windowIsValid(HWND window)
{
if (!IsWindowVisible(window)) {
return false;
}
// Only accept for top level windows and not controls within them
if (window != GetAncestor(window, GA_ROOT)) {
return false;
}
return true;
}
static std::vector<HWND> getHWNDfromTitle(const std::string &title)
{
HWND hwnd = nullptr;
HWND hPrevWnd = nullptr;
std::vector<HWND> hwnds;
wchar_t wTitle[512];
os_utf8_to_wcs(title.c_str(), 0, wTitle, 512);
while ((hwnd = FindWindowEx(NULL, hPrevWnd, NULL, wTitle)) != nullptr) {
if (windowIsValid(hwnd)) {
hwnds.emplace_back(hwnd);
}
hPrevWnd = hwnd;
}
return hwnds;
}
std::string GetWindowClassByWindowTitle(const std::string &window)
{
auto hwnds = getHWNDfromTitle(window);
if (hwnds.empty()) {
return "";
}
auto hwnd = hwnds.at(0);
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;
}
void SetFocusWindow(const std::string &title)
{
auto hwnds = getHWNDfromTitle(title);
if (hwnds.empty()) {
return;
}
for (const auto &handle : hwnds) {
const bool isMinimized = IsIconic(handle);
ShowWindow(handle, isMinimized ? SW_RESTORE : SW_SHOW);
SetForegroundWindow(handle);
SetFocus(handle);
SetActiveWindow(handle);
}
}
void CloseWindow(const std::string &title)
{
auto hwnds = getHWNDfromTitle(title);
if (hwnds.empty()) {
return;
}
for (const auto &handle : hwnds) {
SendMessage(handle, WM_CLOSE, 0, 0);
}
}
void MaximizeWindow(const std::string &title)
{
auto hwnds = getHWNDfromTitle(title);
if (hwnds.empty()) {
return;
}
for (const auto &handle : hwnds) {
ShowWindow(handle, SW_MAXIMIZE);
}
}
void MinimizeWindow(const std::string &title)
{
auto hwnds = getHWNDfromTitle(title);
if (hwnds.empty()) {
return;
}
for (const auto &handle : hwnds) {
ShowWindow(handle, SW_MINIMIZE);
}
}
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