mirror of
https://github.com/jonasbark/swiftcontrol.git
synced 2026-02-18 00:17:40 +01:00
allow all apps to execute keyboard key in background
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
#include "keypress_simulator_windows_plugin.h"
|
||||
|
||||
// This must be included before many other Windows headers.
|
||||
#include <windows.h>
|
||||
#include <flutter_windows.h>
|
||||
#include <psapi.h>
|
||||
#include <string.h>
|
||||
#include <flutter_windows.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <flutter/method_channel.h>
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
@@ -30,7 +30,8 @@ struct FindWindowData {
|
||||
};
|
||||
|
||||
BOOL CALLBACK EnumWindowsCallback(HWND hwnd, LPARAM lParam);
|
||||
HWND FindTargetWindow(const std::string& processName, const std::string& windowTitle);
|
||||
HWND FindTargetWindow(const std::string& processName,
|
||||
const std::string& windowTitle);
|
||||
|
||||
// static
|
||||
void KeypressSimulatorWindowsPlugin::RegisterWithRegistrar(
|
||||
@@ -71,28 +72,18 @@ void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
}
|
||||
|
||||
// List of compatible training apps to look for
|
||||
std::vector<std::string> compatibleApps = {
|
||||
"MyWhooshHD.exe",
|
||||
"indieVelo.exe",
|
||||
"biketerra.exe",
|
||||
"ROUVY.exe"
|
||||
};
|
||||
|
||||
// Apps that should receive key events directly without taking focus (allows media/apps to stay on top)
|
||||
const std::vector<std::string> backgroundInputApps = {
|
||||
"ROUVY.exe"
|
||||
};
|
||||
std::vector<std::string> compatibleApps = {"MyWhooshHD.exe", "MyWhoosh.exe",
|
||||
"indieVelo.exe", "biketerra.exe",
|
||||
"Rouvy.exe"};
|
||||
|
||||
// Try to find and focus (or directly target) a compatible app
|
||||
std::string foundProcessName;
|
||||
bool supportsBackgroundInput = false;
|
||||
bool supportsBackgroundInput = true;
|
||||
HWND targetWindow = NULL;
|
||||
for (const std::string& processName : compatibleApps) {
|
||||
targetWindow = FindTargetWindow(processName, "");
|
||||
if (targetWindow != NULL) {
|
||||
foundProcessName = processName;
|
||||
// For background-capable apps, prefer sending keys directly to their window to avoid stealing focus
|
||||
supportsBackgroundInput = std::find(backgroundInputApps.begin(), backgroundInputApps.end(), processName) != backgroundInputApps.end();
|
||||
if (!supportsBackgroundInput && GetForegroundWindow() != targetWindow) {
|
||||
SetForegroundWindow(targetWindow);
|
||||
Sleep(50); // Brief delay to ensure window is focused
|
||||
@@ -101,14 +92,18 @@ void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a target window that supports background input and it's not focused, send messages directly
|
||||
// If we found a target window that supports background input and it's not
|
||||
// focused, send messages directly
|
||||
auto postKeyMessage = [](HWND hwnd, UINT vkCode, bool down) {
|
||||
const WORD scanCode = static_cast<WORD>(MapVirtualKey(vkCode, MAPVK_VK_TO_VSC));
|
||||
// Build lParam with repeat count 1 and scan code; set transition states for key up
|
||||
const WORD scanCode =
|
||||
static_cast<WORD>(MapVirtualKey(vkCode, MAPVK_VK_TO_VSC));
|
||||
// Build lParam with repeat count 1 and scan code; set transition states for
|
||||
// key up
|
||||
LPARAM lParam = 1 | (static_cast<LPARAM>(scanCode) << 16);
|
||||
if (vkCode == VK_LEFT || vkCode == VK_RIGHT || vkCode == VK_UP || vkCode == VK_DOWN ||
|
||||
vkCode == VK_INSERT || vkCode == VK_DELETE || vkCode == VK_HOME || vkCode == VK_END ||
|
||||
vkCode == VK_PRIOR || vkCode == VK_NEXT) {
|
||||
if (vkCode == VK_LEFT || vkCode == VK_RIGHT || vkCode == VK_UP ||
|
||||
vkCode == VK_DOWN || vkCode == VK_INSERT || vkCode == VK_DELETE ||
|
||||
vkCode == VK_HOME || vkCode == VK_END || vkCode == VK_PRIOR ||
|
||||
vkCode == VK_NEXT) {
|
||||
lParam |= (1 << 24); // extended key
|
||||
}
|
||||
if (!down) {
|
||||
@@ -118,7 +113,9 @@ void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
PostMessage(hwnd, down ? WM_KEYDOWN : WM_KEYUP, vkCode, lParam);
|
||||
};
|
||||
|
||||
auto sendKeyToWindow = [&postKeyMessage](HWND hwnd, const std::vector<std::string>& mods, UINT keyCode, bool down) {
|
||||
auto sendKeyToWindow = [&postKeyMessage](HWND hwnd,
|
||||
const std::vector<std::string>& mods,
|
||||
UINT keyCode, bool down) {
|
||||
auto handleModifier = [&postKeyMessage, hwnd](UINT vk, bool press) {
|
||||
postKeyMessage(hwnd, vk, press);
|
||||
};
|
||||
@@ -153,10 +150,8 @@ void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
}
|
||||
};
|
||||
|
||||
if (targetWindow != NULL &&
|
||||
!foundProcessName.empty() &&
|
||||
supportsBackgroundInput &&
|
||||
GetForegroundWindow() != targetWindow) {
|
||||
if (targetWindow != NULL && !foundProcessName.empty() &&
|
||||
supportsBackgroundInput && GetForegroundWindow() != targetWindow) {
|
||||
sendKeyToWindow(targetWindow, modifiers, keyCode, keyDown);
|
||||
result->Success(flutter::EncodableValue(true));
|
||||
return;
|
||||
@@ -174,7 +169,8 @@ void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
};
|
||||
|
||||
// Helper function to process modifiers
|
||||
auto processModifiers = [&sendModifierKey](const std::vector<std::string>& mods, bool down) {
|
||||
auto processModifiers = [&sendModifierKey](
|
||||
const std::vector<std::string>& mods, bool down) {
|
||||
for (const std::string& modifier : mods) {
|
||||
if (modifier == "shiftModifier") {
|
||||
sendModifierKey(VK_SHIFT, down);
|
||||
@@ -201,9 +197,10 @@ void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
in.ki.wVk = 0; // when using SCANCODE, set VK=0
|
||||
in.ki.wScan = sc;
|
||||
in.ki.dwFlags = KEYEVENTF_SCANCODE | (keyDown ? 0 : KEYEVENTF_KEYUP);
|
||||
if (keyCode == VK_LEFT || keyCode == VK_RIGHT || keyCode == VK_UP || keyCode == VK_DOWN ||
|
||||
keyCode == VK_INSERT || keyCode == VK_DELETE || keyCode == VK_HOME || keyCode == VK_END ||
|
||||
keyCode == VK_PRIOR || keyCode == VK_NEXT) {
|
||||
if (keyCode == VK_LEFT || keyCode == VK_RIGHT || keyCode == VK_UP ||
|
||||
keyCode == VK_DOWN || keyCode == VK_INSERT || keyCode == VK_DELETE ||
|
||||
keyCode == VK_HOME || keyCode == VK_END || keyCode == VK_PRIOR ||
|
||||
keyCode == VK_NEXT) {
|
||||
in.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||
}
|
||||
SendInput(1, &in, sizeof(INPUT));
|
||||
@@ -222,7 +219,6 @@ void KeypressSimulatorWindowsPlugin::SimulateKeyPress(
|
||||
void KeypressSimulatorWindowsPlugin::SimulateMouseClick(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
|
||||
const EncodableMap& args = std::get<EncodableMap>(*method_call.arguments());
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
@@ -284,7 +280,8 @@ BOOL CALLBACK EnumWindowsCallback(HWND hwnd, LPARAM lParam) {
|
||||
// Get process name
|
||||
DWORD processId;
|
||||
GetWindowThreadProcessId(hwnd, &processId);
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId);
|
||||
HANDLE hProcess =
|
||||
OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId);
|
||||
char processName[MAX_PATH];
|
||||
if (hProcess) {
|
||||
DWORD size = sizeof(processName);
|
||||
@@ -317,7 +314,8 @@ BOOL CALLBACK EnumWindowsCallback(HWND hwnd, LPARAM lParam) {
|
||||
return TRUE; // Continue enumeration
|
||||
}
|
||||
|
||||
HWND FindTargetWindow(const std::string& processName, const std::string& windowTitle) {
|
||||
HWND FindTargetWindow(const std::string& processName,
|
||||
const std::string& windowTitle) {
|
||||
FindWindowData data;
|
||||
data.targetProcessName = processName;
|
||||
data.targetWindowTitle = windowTitle;
|
||||
@@ -330,19 +328,15 @@ HWND FindTargetWindow(const std::string& processName, const std::string& windowT
|
||||
void KeypressSimulatorWindowsPlugin::SimulateMediaKey(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
|
||||
const EncodableMap& args = std::get<EncodableMap>(*method_call.arguments());
|
||||
std::string keyIdentifier = std::get<std::string>(args.at(EncodableValue("key")));
|
||||
std::string keyIdentifier =
|
||||
std::get<std::string>(args.at(EncodableValue("key")));
|
||||
|
||||
// Map string identifier to Windows virtual key codes
|
||||
static const std::unordered_map<std::string, UINT> keyMap = {
|
||||
{"playPause", VK_MEDIA_PLAY_PAUSE},
|
||||
{"stop", VK_MEDIA_STOP},
|
||||
{"next", VK_MEDIA_NEXT_TRACK},
|
||||
{"previous", VK_MEDIA_PREV_TRACK},
|
||||
{"volumeUp", VK_VOLUME_UP},
|
||||
{"volumeDown", VK_VOLUME_DOWN}
|
||||
};
|
||||
{"playPause", VK_MEDIA_PLAY_PAUSE}, {"stop", VK_MEDIA_STOP},
|
||||
{"next", VK_MEDIA_NEXT_TRACK}, {"previous", VK_MEDIA_PREV_TRACK},
|
||||
{"volumeUp", VK_VOLUME_UP}, {"volumeDown", VK_VOLUME_DOWN}};
|
||||
|
||||
auto it = keyMap.find(keyIdentifier);
|
||||
if (it == keyMap.end()) {
|
||||
@@ -371,7 +365,6 @@ void KeypressSimulatorWindowsPlugin::SimulateMediaKey(
|
||||
result->Success(flutter::EncodableValue(true));
|
||||
}
|
||||
|
||||
|
||||
void KeypressSimulatorWindowsPlugin::HandleMethodCall(
|
||||
const flutter::MethodCall<flutter::EncodableValue>& method_call,
|
||||
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
|
||||
|
||||
Reference in New Issue
Block a user