first
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# IDEs
|
||||
/.idea
|
||||
/example/.idea
|
||||
.vscode
|
||||
|
||||
# Build files
|
||||
/cmake-build-debug
|
||||
/cmake-build-release
|
||||
/example/cmake-build-debug
|
||||
|
||||
|
||||
/build
|
||||
Vendored
+68
@@ -0,0 +1,68 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(autogui-cpp VERSION 1.0.0 LANGUAGES CXX)
|
||||
|
||||
# 基础配置
|
||||
set(CMAKE_CXX_STANDARD 17) # Cpp17
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# 创建库目标
|
||||
add_library(autogui-cpp STATIC
|
||||
src/Keyboard.cpp
|
||||
src/Mouse.cpp
|
||||
src/Utils.cpp
|
||||
src/Autogui.cpp
|
||||
)
|
||||
|
||||
# 设置头文件搜索路径
|
||||
# 使用生成器表达式支持add_subdirectory和install两种使用方式
|
||||
target_include_directories(autogui-cpp PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:include/autogui-cpp>
|
||||
)
|
||||
|
||||
# 平台特定配置
|
||||
# macOS平台
|
||||
if(APPLE)
|
||||
# 查找并链接ApplicationServices框架
|
||||
# 该框架包含CoreGraphics和Carbon相关API
|
||||
find_library(APPLICATIONSERVICES_LIBRARY ApplicationServices REQUIRED)
|
||||
target_link_libraries(autogui-cpp PUBLIC ${APPLICATIONSERVICES_LIBRARY})
|
||||
|
||||
# Windows平台
|
||||
elseif(WIN32)
|
||||
# 链接User32.lib (提供SendInput, GetCursorPos等API)
|
||||
target_link_libraries(autogui-cpp PUBLIC User32)
|
||||
|
||||
# Linux/Unix平台
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
# Linux需要X11后端(Wayland尚未支持)
|
||||
find_package(X11 REQUIRED)
|
||||
# 查找Xtst库(XTest扩展)
|
||||
find_library(X11_Xtst_LIB Xtst)
|
||||
if(NOT X11_Xtst_LIB)
|
||||
message(FATAL_ERROR "Xtst library not found. Install libxtst-dev.")
|
||||
endif()
|
||||
# 查找Xext库(X扩展)
|
||||
find_library(X11_Xext_LIB Xext)
|
||||
if(NOT X11_Xext_LIB)
|
||||
message(FATAL_ERROR "Xext library not found.")
|
||||
endif()
|
||||
# 查找XRandR库(Xrandr扩展)
|
||||
find_library(X11_Xrandr_LIB Xrandr)
|
||||
if(NOT X11_Xrandr_LIB)
|
||||
message(FATAL_ERROR "XRandR library not found. Install libxrandr-dev.")
|
||||
endif()
|
||||
# 链接所有必需的X11库
|
||||
target_link_libraries(autogui-cpp PUBLIC
|
||||
${X11_LIBRARIES}
|
||||
${X11_Xtst_LIB}
|
||||
${X11_Xext_LIB}
|
||||
${X11_Xrandr_LIB}
|
||||
)
|
||||
target_include_directories(autogui-cpp PUBLIC ${X11_INCLUDE_DIR})
|
||||
endif()
|
||||
# 集成方式: add_subdirectory
|
||||
# 在你的项目CMakeLists.txt中:
|
||||
# add_subdirectory(autogui-cpp)
|
||||
# target_link_libraries(your_target PRIVATE autogui-cpp)
|
||||
Vendored
+118
@@ -0,0 +1,118 @@
|
||||
# autogui-cpp
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
本项目继承自[Robot CPP](https://github.com/developer239/robot-cpp),在此感谢该项目的贡献。
|
||||
|
||||
|
||||
原先的项目仅支持`Windows`与`Mac OS`,本项目增加了对`Linux X11`的支持,`Linux Wanland`的支持正在开发中
|
||||
|
||||
为什么要创建本项目,这是因为我在苦苦寻找`pyautogui`的cpp实现,但很遗憾,我并没有找到这个轮子。
|
||||
|
||||
本项目在原项目的基础上,增加了以下文件:
|
||||
1. `Autogui.cpp/.h`
|
||||
2. `SimpleAutoGUI.h`
|
||||
|
||||
并修复了原项目的bug:
|
||||
1. 模拟键盘输入的时候,无法处理大小写与符号的转换
|
||||
|
||||
最终实现了与`pyautogui`类似的功能。
|
||||
|
||||
但是,本项目不支持以下功能:
|
||||
1. 模拟键盘输入只支持`ASCII`字符,如果你需要输入中文,或者其他语言的字符,这可能是一个很大的工程量。
|
||||
更好的解决方案是将内容copy到剪贴板,然后粘贴到目标位置。
|
||||
|
||||
同时,本项目裁剪掉了原项目的部分功能:
|
||||
1. `Hooks`
|
||||
2. `Screen`
|
||||
3. `Record`
|
||||
|
||||
主要是因为有着更加完美的上位替代品,例如`Screen`可用`Qt`的`QScreen`替代,并且支持的更加完美。
|
||||
|
||||
注意:原项目并没有说明代码的开源协议,因此关于`Robot-cpp`部分的代码,解释权归属于原作者`
|
||||
developer239`。
|
||||
|
||||
本项目被我直接使用在[Yosuga](https://github.com/Misakityan/Yosuga)这个我自己的项目当中。
|
||||
|
||||
项目已在以下平台完成测试:
|
||||
1. Windows 10
|
||||
2. kUbuntu 24.04
|
||||
|
||||
由于我没有Mac PC, 因此并未在mac上进行测试,不过mac部分都是直接使用`Robot-cpp`部分的代码,原作者测试没问题,
|
||||
大概也是可以使用的。如果遇到bug,尽情提issue即可。
|
||||
|
||||
## How to use?
|
||||
想使用本项目十分容易,在你的项目CMakeLists.txt中:
|
||||
```cmake
|
||||
add_subdirectory(autogui-cpp)
|
||||
target_link_libraries(your_target PRIVATE autogui-cpp) # your_target为你项目的构建目标
|
||||
# 注意:有时候你还需要根据导入本项目的位置,在autogui-cpp的前面增加一些路径信息
|
||||
```
|
||||
之后,在你需要的地方`#include "SimpleAutoGUI.h"`,访问AutoGUI当中已经封装好的函数即可使用。
|
||||
|
||||
|
||||
## some examples
|
||||
```c++
|
||||
#include <iostream>
|
||||
#include "SimpleAutoGUI.h"
|
||||
int main() {
|
||||
Robot::Point screenSize = AutoGUI::size();
|
||||
std::cout << "屏幕尺寸: " << screenSize.x << "x" << screenSize.y << std::endl;
|
||||
|
||||
// 测试1: 移动鼠标
|
||||
std::cout << "移动鼠标到屏幕中心..." << std::endl;
|
||||
int centerX = screenSize.x / 2;
|
||||
int centerY = screenSize.y / 2;
|
||||
AutoGUI::moveTo(centerX, centerY, 2);
|
||||
|
||||
// 测试2: 单击
|
||||
std::cout << "左键单击..." << std::endl;
|
||||
AutoGUI::click();
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
// 测试3: 右键单击
|
||||
std::cout << "右键单击..." << std::endl;
|
||||
AutoGUI::rightSingle();
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
// 测试4: 双击
|
||||
std::cout << "左键双击..." << std::endl;
|
||||
AutoGUI::leftDouble();
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
// 测试5: 拖拽
|
||||
std::cout << "拖拽测试..." << std::endl;
|
||||
AutoGUI::dragRel(100, 100, 0.5);
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
// 测试6: 输入文本
|
||||
std::cout << "输入文本测试..." << std::endl;
|
||||
AutoGUI::type("Hello, AutoGUI!", 0.05); // 每个字符间隔50ms
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
// 测试7: 快捷键
|
||||
std::cout << "快捷键测试 (Ctrl+A)..." << std::endl;
|
||||
AutoGUI::hotkey({AutoGUI::Keys::CTRL, "a"});
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
std::cout << "快捷键测试 (Ctrl+C)..." << std::endl;
|
||||
AutoGUI::hotkey({AutoGUI::Keys::CTRL, "c"});
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
// 测试8: 滚动
|
||||
std::cout << "向上滚动..." << std::endl;
|
||||
AutoGUI::scroll(5);
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
std::cout << "向下滚动..." << std::endl;
|
||||
AutoGUI::scroll(-5);
|
||||
AutoGUI::sleep(0.5);
|
||||
|
||||
// 测试9: 获取鼠标位置
|
||||
Robot::Point pos = AutoGUI::position();
|
||||
std::cout << "当前鼠标位置: (" << pos.x << ", " << pos.y << ")" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
Vendored
+745
@@ -0,0 +1,745 @@
|
||||
//
|
||||
// Created by misaki on 2026/1/25.
|
||||
//
|
||||
|
||||
#include "Autogui.h"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <cstring>
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
namespace AutoGUI {
|
||||
|
||||
// 内部辅助函数
|
||||
namespace {
|
||||
|
||||
// 等待指定毫秒数
|
||||
void delayMs(const int ms) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
}
|
||||
|
||||
// 将秒转换为毫秒
|
||||
int secondsToMs(double seconds) { return static_cast<int>(seconds * 1000); }
|
||||
|
||||
// 获取当前鼠标位置(内部使用)
|
||||
Robot::Point getCurrentPosition() { return Robot::Mouse::GetPosition(); }
|
||||
|
||||
} // namespace
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <algorithm>
|
||||
|
||||
// 获取所有屏幕信息
|
||||
std::vector<ScreenInfo> getAllScreens() {
|
||||
std::vector<ScreenInfo> screens;
|
||||
Display* display = XOpenDisplay(nullptr);
|
||||
if (!display) return screens;
|
||||
|
||||
XRRScreenResources* resources = XRRGetScreenResources(display, DefaultRootWindow(display));
|
||||
if (resources) {
|
||||
for (int i = 0; i < resources->noutput; i++) {
|
||||
XRROutputInfo* output = XRRGetOutputInfo(display, resources, resources->outputs[i]);
|
||||
if (output && output->connection == RR_Connected && output->crtc) {
|
||||
XRRCrtcInfo* crtc = XRRGetCrtcInfo(display, resources, output->crtc);
|
||||
if (crtc) {
|
||||
ScreenInfo info;
|
||||
info.id = i;
|
||||
info.x = crtc->x;
|
||||
info.y = crtc->y;
|
||||
info.width = crtc->width;
|
||||
info.height = crtc->height;
|
||||
info.isPrimary = (output->name && strcmp(output->name, "primary") == 0);
|
||||
// 或者使用 XRRGetOutputPrimary 来判断
|
||||
screens.push_back(info);
|
||||
XRRFreeCrtcInfo(crtc);
|
||||
}
|
||||
XRRFreeOutputInfo(output);
|
||||
}
|
||||
}
|
||||
XRRFreeScreenResources(resources);
|
||||
}
|
||||
XCloseDisplay(display);
|
||||
return screens;
|
||||
}
|
||||
|
||||
// 获取当前鼠标所在的屏幕
|
||||
ScreenInfo getCurrentScreen() {
|
||||
auto screens = getAllScreens();
|
||||
if (screens.empty()) return {0, 0, 0, 1920, 1080, true};
|
||||
|
||||
// 获取当前鼠标位置(虚拟桌面绝对坐标)
|
||||
Robot::Point mouse = position();
|
||||
|
||||
// 找到包含该点的屏幕
|
||||
for (const auto& screen : screens) {
|
||||
if (mouse.x >= screen.x && mouse.x < screen.x + screen.width &&
|
||||
mouse.y >= screen.y && mouse.y < screen.y + screen.height) {
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
|
||||
// 默认返回主屏或第一个屏幕
|
||||
auto it = std::find_if(screens.begin(), screens.end(),
|
||||
[](const ScreenInfo& s) { return s.isPrimary; });
|
||||
return (it != screens.end()) ? *it : screens[0];
|
||||
}
|
||||
|
||||
// 相对于当前屏幕移动
|
||||
void moveToOnCurrentScreen(int x, int y, double duration) {
|
||||
const ScreenInfo current = getCurrentScreen();
|
||||
// 转换为虚拟桌面绝对坐标
|
||||
int absX = current.x + x;
|
||||
int absY = current.y + y;
|
||||
moveTo(absX, absY, duration);
|
||||
}
|
||||
|
||||
// 获取当前屏幕尺寸
|
||||
Robot::Point getScreenSize(int screenId) {
|
||||
if (screenId == -1) {
|
||||
ScreenInfo current = getCurrentScreen();
|
||||
return {current.width, current.height};
|
||||
}
|
||||
auto screens = getAllScreens();
|
||||
for (const auto& s : screens) {
|
||||
if (s.id == screenId) return {s.width, s.height};
|
||||
}
|
||||
return {1920, 1080};
|
||||
}
|
||||
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
#include <Windows.h>
|
||||
|
||||
std::vector<ScreenInfo> getAllScreens() {
|
||||
std::vector<ScreenInfo> screens;
|
||||
EnumDisplayMonitors(nullptr, nullptr,
|
||||
[](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL {
|
||||
auto* screens = reinterpret_cast<std::vector<ScreenInfo>*>(dwData);
|
||||
MONITORINFOEX info;
|
||||
info.cbSize = sizeof(info);
|
||||
if (GetMonitorInfo(hMonitor, &info)) {
|
||||
ScreenInfo si;
|
||||
si.id = screens->size();
|
||||
si.x = info.rcMonitor.left;
|
||||
si.y = info.rcMonitor.top;
|
||||
si.width = info.rcMonitor.right - info.rcMonitor.left;
|
||||
si.height = info.rcMonitor.bottom - info.rcMonitor.top;
|
||||
si.isPrimary = (info.dwFlags & MONITORINFOF_PRIMARY) != 0;
|
||||
screens->push_back(si);
|
||||
}
|
||||
return TRUE;
|
||||
}, reinterpret_cast<LPARAM>(&screens));
|
||||
return screens;
|
||||
}
|
||||
|
||||
ScreenInfo getCurrentScreen() {
|
||||
// 获取当前鼠标位置
|
||||
POINT pt;
|
||||
GetCursorPos(&pt);
|
||||
// 查找包含该点的显示器
|
||||
HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFOEX info;
|
||||
info.cbSize = sizeof(info);
|
||||
GetMonitorInfo(hMonitor, &info);
|
||||
|
||||
ScreenInfo si;
|
||||
si.x = info.rcMonitor.left;
|
||||
si.y = info.rcMonitor.top;
|
||||
si.width = info.rcMonitor.right - info.rcMonitor.left;
|
||||
si.height = info.rcMonitor.bottom - info.rcMonitor.top;
|
||||
return si;
|
||||
}
|
||||
|
||||
void moveToOnCurrentScreen(int x, int y, double duration) {
|
||||
ScreenInfo current = getCurrentScreen();
|
||||
moveTo(current.x + x, current.y + y, duration);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// 实现主要API函数
|
||||
void moveTo(int x, int y, double duration) {
|
||||
Robot::Point target{x, y};
|
||||
|
||||
if (duration > 0.0) {
|
||||
Robot::Mouse::MoveSmooth(target);
|
||||
} else {
|
||||
Robot::Mouse::Move(target);
|
||||
}
|
||||
}
|
||||
|
||||
void moveRel(int xOffset, int yOffset, double duration) {
|
||||
Robot::Point current = getCurrentPosition();
|
||||
Robot::Point target{current.x + xOffset, current.y + yOffset};
|
||||
|
||||
moveTo(target.x, target.y, duration);
|
||||
}
|
||||
|
||||
void click(int x, int y, Button button, int clicks, double interval) {
|
||||
// 如果提供了坐标,先移动到该位置
|
||||
if (x >= 0 && y >= 0) {
|
||||
moveTo(x, y);
|
||||
delayMs(10);
|
||||
}
|
||||
|
||||
Robot::MouseButton robotButton = toRobotButton(button);
|
||||
|
||||
for (int i = 0; i < clicks; i++) {
|
||||
if (clicks == 2 && i == 0) {
|
||||
// 双击
|
||||
Robot::Mouse::DoubleClick(robotButton);
|
||||
} else {
|
||||
Robot::Mouse::Click(robotButton);
|
||||
}
|
||||
|
||||
// 如果不是最后一次点击,并且设置了间隔,则等待
|
||||
if (i < clicks - 1 && interval > 0) {
|
||||
sleep(interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void leftDouble(int x, int y) { click(x, y, Button::LEFT, 2); }
|
||||
|
||||
void rightSingle(int x, int y) { click(x, y, Button::RIGHT, 1); }
|
||||
|
||||
void middleClick(int x, int y) { click(x, y, Button::MIDDLE, 1); }
|
||||
|
||||
void mouseDown(Button button, int x, int y) {
|
||||
// 如果提供了坐标,先移动到该位置
|
||||
if (x >= 0 && y >= 0) {
|
||||
moveTo(x, y);
|
||||
delayMs(10);
|
||||
}
|
||||
|
||||
Robot::Mouse::ToggleButton(true, toRobotButton(button));
|
||||
}
|
||||
|
||||
void mouseUp(Button button, int x, int y) {
|
||||
// 如果提供了坐标,先移动到该位置
|
||||
if (x >= 0 && y >= 0) {
|
||||
moveTo(x, y);
|
||||
delayMs(10);
|
||||
}
|
||||
|
||||
Robot::Mouse::ToggleButton(false, toRobotButton(button));
|
||||
}
|
||||
|
||||
void drag(const int x1, const int y1, const int x2, const int y2,
|
||||
const double duration, const Button button) {
|
||||
// 参数验证
|
||||
if (!isValidCoord(x1, y1)) {
|
||||
throw AutoGUIException("Invalid start coordinates: (" + std::to_string(x1) +
|
||||
", " + std::to_string(y1) + ")");
|
||||
}
|
||||
if (!isValidCoord(x2, y2)) {
|
||||
throw AutoGUIException("Invalid end coordinates: (" + std::to_string(x2) +
|
||||
", " + std::to_string(y2) + ")");
|
||||
}
|
||||
if (duration < 0) {
|
||||
throw AutoGUIException("Duration cannot be negative");
|
||||
}
|
||||
Robot::MouseButton robotButton = toRobotButton(button);
|
||||
// 移动到起始位置
|
||||
Robot::Point start{x1, y1};
|
||||
Robot::Mouse::Move(start);
|
||||
delayMs(10); // 短暂延迟确保移动完成
|
||||
// 按下鼠标按钮
|
||||
Robot::Mouse::ToggleButton(true, robotButton);
|
||||
delayMs(10); // 短暂延迟确保按钮按下
|
||||
// 拖动到目标位置
|
||||
Robot::Point end{x2, y2};
|
||||
if (duration > 0.0) {
|
||||
// 使用平滑拖动
|
||||
Robot::Mouse::DragSmooth(end);
|
||||
} else {
|
||||
// 立即拖动
|
||||
Robot::Mouse::Drag(end);
|
||||
}
|
||||
// 释放鼠标按钮
|
||||
Robot::Mouse::ToggleButton(false, robotButton);
|
||||
}
|
||||
|
||||
void dragTo(const int x, const int y, const double duration,
|
||||
const Button button) {
|
||||
// 获取当前位置
|
||||
Robot::Point current = getCurrentPosition();
|
||||
// 如果当前位置就是目标位置,则只做点击操作
|
||||
if (current.x == x && current.y == y) {
|
||||
click(x, y, button);
|
||||
return;
|
||||
}
|
||||
// 调用 drag 函数
|
||||
drag(current.x, current.y, x, y, duration, button);
|
||||
}
|
||||
|
||||
void dragRel(const int xOffset, const int yOffset, const double duration,
|
||||
const Button button) {
|
||||
// 获取当前位置
|
||||
const Robot::Point current = getCurrentPosition();
|
||||
// 计算目标位置
|
||||
const int targetX = current.x + xOffset;
|
||||
const int targetY = current.y + yOffset;
|
||||
// 如果偏移量为0,则只做点击操作
|
||||
if (xOffset == 0 && yOffset == 0) {
|
||||
click(current.x, current.y, button);
|
||||
return;
|
||||
}
|
||||
// 调用 drag 函数
|
||||
drag(current.x, current.y, targetX, targetY, duration, button);
|
||||
}
|
||||
|
||||
Robot::Point position() { return getCurrentPosition(); }
|
||||
|
||||
void scroll(int clicks, int x) {
|
||||
// 注意:正数向上滚动,负数向下滚动
|
||||
// 与AutoGUI一致
|
||||
Robot::Mouse::ScrollBy(clicks, x);
|
||||
}
|
||||
|
||||
void type(const std::string &text, double interval) {
|
||||
if (interval > 0.0) {
|
||||
// 有间隔的输入
|
||||
for (char c : text) {
|
||||
Robot::Keyboard::Click(c);
|
||||
if (interval > 0) {
|
||||
sleep(interval);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 无间隔快速输入
|
||||
Robot::Keyboard::Type(text);
|
||||
}
|
||||
}
|
||||
|
||||
void press(const std::string &key) {
|
||||
std::string lowerKey = toLower(key);
|
||||
|
||||
if (lowerKey.length() == 1) {
|
||||
// 单个字符键
|
||||
Robot::Keyboard::Click(lowerKey[0]);
|
||||
} else if (isSpecialKey(lowerKey)) {
|
||||
// 特殊键
|
||||
Robot::Keyboard::SpecialKey specialKey = stringToSpecialKey(lowerKey);
|
||||
Robot::Keyboard::Click(specialKey);
|
||||
} else {
|
||||
throw AutoGUIException("Unknown key: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
void keyDown(const std::string &key) {
|
||||
std::string lowerKey = toLower(key);
|
||||
|
||||
if (lowerKey.length() == 1) {
|
||||
// 单个字符键
|
||||
Robot::Keyboard::Press(lowerKey[0]);
|
||||
} else if (isSpecialKey(lowerKey)) {
|
||||
// 特殊键
|
||||
Robot::Keyboard::SpecialKey specialKey = stringToSpecialKey(lowerKey);
|
||||
Robot::Keyboard::Press(specialKey);
|
||||
} else {
|
||||
throw AutoGUIException("Unknown key: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
void keyUp(const std::string &key) {
|
||||
std::string lowerKey = toLower(key);
|
||||
|
||||
if (lowerKey.length() == 1) {
|
||||
// 单个字符键
|
||||
Robot::Keyboard::Release(lowerKey[0]);
|
||||
} else if (isSpecialKey(lowerKey)) {
|
||||
// 特殊键
|
||||
Robot::Keyboard::SpecialKey specialKey = stringToSpecialKey(lowerKey);
|
||||
Robot::Keyboard::Release(specialKey);
|
||||
} else {
|
||||
throw AutoGUIException("Unknown key: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
void hotkey(const std::initializer_list<std::string> &keys) {
|
||||
// 按下所有键
|
||||
for (const auto &key : keys) {
|
||||
keyDown(key);
|
||||
}
|
||||
|
||||
delayMs(50);
|
||||
|
||||
// 释放所有键(按相反顺序)
|
||||
const std::string *end = keys.end();
|
||||
const std::string *begin = keys.begin();
|
||||
for (const std::string *it = end - 1; it >= begin; --it) {
|
||||
keyUp(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void hotkey(const std::vector<std::string> &keys) {
|
||||
// 按下所有键
|
||||
for (const auto &key : keys) {
|
||||
keyDown(key);
|
||||
}
|
||||
|
||||
delayMs(50);
|
||||
|
||||
// 释放所有键(按相反顺序)
|
||||
for (auto it = keys.rbegin(); it != keys.rend(); ++it) {
|
||||
keyUp(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void sleep(double seconds) {
|
||||
if (seconds > 0) {
|
||||
delayMs(secondsToMs(seconds));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <Windows.h>
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#elif defined(__linux__)
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#endif
|
||||
|
||||
// 平台特定函数实现
|
||||
Robot::Point size() {
|
||||
// 平台特定实现
|
||||
#ifdef _WIN32
|
||||
// Windows实现
|
||||
int width = GetSystemMetrics(SM_CXSCREEN);
|
||||
int height = GetSystemMetrics(SM_CYSCREEN);
|
||||
return {width, height};
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
// macOS实现
|
||||
CGRect mainDisplayBounds = CGDisplayBounds(CGMainDisplayID());
|
||||
int width = static_cast<int>(CGRectGetWidth(mainDisplayBounds));
|
||||
int height = static_cast<int>(CGRectGetHeight(mainDisplayBounds));
|
||||
return {width, height};
|
||||
|
||||
#elif defined(__linux__)
|
||||
Display *display = XOpenDisplay(nullptr);
|
||||
if (!display) return {1920, 1080};
|
||||
|
||||
int width = 0, height = 0;
|
||||
|
||||
// 使用 XRRGetScreenResources (兼容 XRandR 1.2+,比 Current 版本兼容性更好)
|
||||
XRRScreenResources *resources = XRRGetScreenResources(display, DefaultRootWindow(display));
|
||||
if (resources) {
|
||||
RROutput target_output = None;
|
||||
|
||||
// 条件编译:只在 XRandR 1.3+ 时使用 Primary Output 功能
|
||||
#if defined(RANDR_MAJOR) && defined(RANDR_MINOR)
|
||||
#if RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 3)
|
||||
target_output = XRRGetOutputPrimary(display, DefaultRootWindow(display));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// 如果没有获取到主显示器(或版本太低),使用第一个已连接的显示器
|
||||
if (target_output == None) {
|
||||
for (int i = 0; i < resources->noutput; i++) {
|
||||
XRROutputInfo *info = XRRGetOutputInfo(display, resources, resources->outputs[i]);
|
||||
if (info) {
|
||||
if (info->connection == 0) {
|
||||
target_output = resources->outputs[i];
|
||||
XRRFreeOutputInfo(info);
|
||||
break;
|
||||
}
|
||||
XRRFreeOutputInfo(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取选中显示器的分辨率
|
||||
if (target_output != None) {
|
||||
XRROutputInfo *output_info = XRRGetOutputInfo(display, resources, target_output);
|
||||
if (output_info && output_info->crtc) {
|
||||
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(display, resources, output_info->crtc);
|
||||
if (crtc_info) {
|
||||
width = static_cast<int>(crtc_info->width);
|
||||
height = static_cast<int>(crtc_info->height);
|
||||
XRRFreeCrtcInfo(crtc_info);
|
||||
}
|
||||
XRRFreeOutputInfo(output_info);
|
||||
}
|
||||
}
|
||||
XRRFreeScreenResources(resources);
|
||||
}
|
||||
|
||||
XCloseDisplay(display);
|
||||
return (width > 0 && height > 0) ? Robot::Point{width, height} : Robot::Point{1920, 1080};
|
||||
|
||||
#else
|
||||
// 其他平台
|
||||
return {1920, 1080}; // 默认值
|
||||
#endif
|
||||
}
|
||||
|
||||
// 辅助函数实现
|
||||
|
||||
bool isValidCoord(int x, int y) {
|
||||
const Robot::Point screenSize = size();
|
||||
return x >= 0 && x < screenSize.x && y >= 0 && y < screenSize.y;
|
||||
}
|
||||
|
||||
std::string toLower(const std::string &str) {
|
||||
std::string result = str;
|
||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace ExtendedFunc {
|
||||
void dragRect(const int x1, const int y1, const int width, const int height,
|
||||
double const duration, const Button button) {
|
||||
// 参数验证
|
||||
if (width <= 0 || height <= 0) {
|
||||
throw AutoGUIException("Width and height must be positive");
|
||||
}
|
||||
// 计算矩形右下角坐标
|
||||
const int x2 = x1 + width;
|
||||
const int y2 = y1 + height;
|
||||
|
||||
// 拖动矩形:左上角 -> 右上角 -> 右下角 -> 左下角 -> 左上角
|
||||
drag(x1, y1, x2, y1, duration/4, button); // 上边
|
||||
drag(x2, y1, x2, y2, duration/4, button); // 右边
|
||||
drag(x2, y2, x1, y2, duration/4, button); // 下边
|
||||
drag(x1, y2, x1, y1, duration/4, button); // 左边
|
||||
}
|
||||
|
||||
void dragCircle(const int centerX, const int centerY, const int radius,
|
||||
double const duration, const Button button) {
|
||||
// 参数验证
|
||||
if (radius <= 0) {
|
||||
throw AutoGUIException("Radius must be positive");
|
||||
}
|
||||
// 计算圆上的点数量(越多越平滑)
|
||||
const int segments = 36; // 每10度一个点
|
||||
const double angleStep = 2.0 * M_PI / segments;
|
||||
// 从3点钟方向开始
|
||||
double startX = centerX + radius;
|
||||
double startY = centerY;
|
||||
|
||||
// 拖动圆形
|
||||
for (int i = 1; i <= segments; i++) {
|
||||
double angle = angleStep * i;
|
||||
double endX = centerX + radius * cos(angle);
|
||||
double endY = centerY + radius * sin(angle);
|
||||
// 拖动到下一个点
|
||||
drag(static_cast<int>(startX), static_cast<int>(startY),
|
||||
static_cast<int>(endX), static_cast<int>(endY),
|
||||
duration / segments, button);
|
||||
// 更新起点为终点
|
||||
startX = endX;
|
||||
startY = endY;
|
||||
}
|
||||
}
|
||||
|
||||
void dragRandomInArea(const int x1, const int y1, const int x2, const int y2,
|
||||
const double minDuration, const double maxDuration,
|
||||
const Button button) {
|
||||
// 参数验证
|
||||
if (x1 >= x2 || y1 >= y2) {
|
||||
throw AutoGUIException("Invalid area coordinates");
|
||||
}
|
||||
|
||||
if (minDuration < 0 || maxDuration < 0 || minDuration > maxDuration) {
|
||||
throw AutoGUIException("Invalid duration range");
|
||||
}
|
||||
|
||||
// 设置随机数生成器
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> distX(x1, x2);
|
||||
std::uniform_int_distribution<> distY(y1, y2);
|
||||
std::uniform_real_distribution<> distDuration(minDuration, maxDuration);
|
||||
|
||||
// 生成随机起始点和结束点
|
||||
int startX = distX(gen);
|
||||
int startY = distY(gen);
|
||||
int endX = distX(gen);
|
||||
int endY = distY(gen);
|
||||
double duration = distDuration(gen);
|
||||
|
||||
// 执行随机拖动
|
||||
drag(startX, startY, endX, endY, duration, button);
|
||||
}
|
||||
|
||||
// 模拟人类打字
|
||||
void typeHumanLike(const std::string& text, double minDelay, double maxDelay, double errorRate) {
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> delayDist(minDelay, maxDelay);
|
||||
std::uniform_real_distribution<> errorDist(0.0, 1.0);
|
||||
|
||||
for (char c : text) {
|
||||
// 有一定概率打错字
|
||||
if (errorDist(gen) < errorRate) {
|
||||
// 输入一个随机错误字符
|
||||
char wrongChar = 'a' + static_cast<char>(errorDist(gen) * 26);
|
||||
AutoGUI::press(std::string(1, wrongChar));
|
||||
|
||||
// 退格删除错误
|
||||
AutoGUI::press("backspace");
|
||||
AutoGUI::sleep(0.1);
|
||||
}
|
||||
|
||||
// 输入正确字符
|
||||
AutoGUI::press(std::string(1, c));
|
||||
|
||||
// 随机延迟
|
||||
double delay = delayDist(gen);
|
||||
AutoGUI::sleep(delay);
|
||||
}
|
||||
}
|
||||
|
||||
void typewriteEnter(const std::string& text, double interval) {
|
||||
AutoGUI::type(text, interval);
|
||||
AutoGUI::press("enter");
|
||||
}
|
||||
|
||||
void typewriteLines(const std::vector<std::string>& lines, double interval, double lineDelay) {
|
||||
for (size_t i = 0; i < lines.size(); i++) {
|
||||
AutoGUI::type(lines[i], interval);
|
||||
if (i < lines.size() - 1) {
|
||||
AutoGUI::press("enter");
|
||||
AutoGUI::sleep(lineDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void typeDateTime(const std::string& format) {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto time = std::chrono::system_clock::to_time_t(now);
|
||||
const std::tm tm = *std::localtime(&time);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::put_time(&tm, format.c_str());
|
||||
AutoGUI::type(ss.str());
|
||||
}
|
||||
|
||||
/// 键盘快捷键扩展实现
|
||||
void saveFile() {
|
||||
AutoGUI::hotkey({"ctrl", "s"});
|
||||
}
|
||||
|
||||
void openFile() {
|
||||
AutoGUI::hotkey({"ctrl", "o"});
|
||||
}
|
||||
|
||||
void newFile() {
|
||||
AutoGUI::hotkey({"ctrl", "n"});
|
||||
}
|
||||
|
||||
void copy() {
|
||||
AutoGUI::hotkey({"ctrl", "c"});
|
||||
}
|
||||
|
||||
void paste() {
|
||||
AutoGUI::hotkey({"ctrl", "v"});
|
||||
}
|
||||
|
||||
void cut() {
|
||||
AutoGUI::hotkey({"ctrl", "x"});
|
||||
}
|
||||
|
||||
void selectAll() {
|
||||
AutoGUI::hotkey({"ctrl", "a"});
|
||||
}
|
||||
|
||||
void undo() {
|
||||
AutoGUI::hotkey({"ctrl", "z"});
|
||||
}
|
||||
|
||||
void redo() {
|
||||
#ifdef _WIN32
|
||||
AutoGUI::hotkey({"ctrl", "y"});
|
||||
#else
|
||||
AutoGUI::hotkey({"ctrl", "shift", "z"});
|
||||
#endif
|
||||
}
|
||||
|
||||
void find() {
|
||||
AutoGUI::hotkey({"ctrl", "f"});
|
||||
}
|
||||
|
||||
void replace() {
|
||||
AutoGUI::hotkey({"ctrl", "h"});
|
||||
}
|
||||
|
||||
void print() {
|
||||
AutoGUI::hotkey({"ctrl", "p"});
|
||||
}
|
||||
|
||||
void closeWindow(bool useAltF4) {
|
||||
if (useAltF4) {
|
||||
AutoGUI::hotkey({"alt", "f4"});
|
||||
} else {
|
||||
AutoGUI::hotkey({"ctrl", "w"});
|
||||
}
|
||||
}
|
||||
|
||||
void switchWindow(int times) {
|
||||
for (int i = 0; i < times; i++) {
|
||||
AutoGUI::hotkey({"alt", "tab"});
|
||||
AutoGUI::sleep(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
AutoGUI::press("f5");
|
||||
}
|
||||
|
||||
/// 游戏/自动化操作实现
|
||||
void autoClicker(int x, int y, int count, double interval, AutoGUI::Button button) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
AutoGUI::click(x, y, button);
|
||||
if (i < count - 1) {
|
||||
AutoGUI::sleep(interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rapidClicker(int x, int y, double duration, double clickRate, AutoGUI::Button button) {
|
||||
int totalClicks = static_cast<int>(duration * clickRate);
|
||||
double interval = 1.0 / clickRate;
|
||||
|
||||
for (int i = 0; i < totalClicks; i++) {
|
||||
AutoGUI::click(x, y, button);
|
||||
AutoGUI::sleep(interval);
|
||||
}
|
||||
}
|
||||
|
||||
void rapidKeyPress(const std::string& key, double duration, double pressRate) {
|
||||
int totalPresses = static_cast<int>(duration * pressRate);
|
||||
double interval = 1.0 / pressRate;
|
||||
|
||||
for (int i = 0; i < totalPresses; i++) {
|
||||
AutoGUI::press(key);
|
||||
AutoGUI::sleep(interval);
|
||||
}
|
||||
}
|
||||
|
||||
void rapidHotkey(const std::vector<std::string>& keys, double duration, double pressRate) {
|
||||
int totalPresses = static_cast<int>(duration * pressRate);
|
||||
double interval = 1.0 / pressRate;
|
||||
|
||||
for (int i = 0; i < totalPresses; i++) {
|
||||
AutoGUI::hotkey(keys);
|
||||
AutoGUI::sleep(interval);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ExtendedFunc
|
||||
|
||||
} // namespace AutoGUI
|
||||
Vendored
+521
@@ -0,0 +1,521 @@
|
||||
//
|
||||
// Created by misaki on 2026/1/25.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <initializer_list>
|
||||
|
||||
#include "Keyboard.h"
|
||||
#include "Mouse.h"
|
||||
#include "types.h"
|
||||
|
||||
namespace AutoGUI {
|
||||
|
||||
// 错误类型
|
||||
class AutoGUIException : public std::exception {
|
||||
private:
|
||||
std::string message;
|
||||
|
||||
public:
|
||||
explicit AutoGUIException(std::string msg) : message(std::move(msg)) {}
|
||||
|
||||
[[nodiscard]] const char* what() const noexcept override {
|
||||
return message.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
// 鼠标按钮枚举
|
||||
enum class Button {
|
||||
LEFT = 0,
|
||||
RIGHT = 1,
|
||||
MIDDLE = 2
|
||||
};
|
||||
|
||||
// 将 Button 转换为 Robot::MouseButton
|
||||
inline Robot::MouseButton toRobotButton(Button button) {
|
||||
switch (button) {
|
||||
case Button::LEFT: return Robot::MouseButton::LEFT_BUTTON;
|
||||
case Button::RIGHT: return Robot::MouseButton::RIGHT_BUTTON;
|
||||
case Button::MIDDLE: return Robot::MouseButton::CENTER_BUTTON;
|
||||
default: return Robot::MouseButton::LEFT_BUTTON;
|
||||
}
|
||||
}
|
||||
|
||||
// 将 Button 枚举转换为字符串
|
||||
inline std::string buttonToString(Button button) {
|
||||
switch (button) {
|
||||
case Button::LEFT: return "left";
|
||||
case Button::RIGHT: return "right";
|
||||
case Button::MIDDLE: return "middle";
|
||||
default: return "left";
|
||||
}
|
||||
}
|
||||
|
||||
// 键名字符串到 Keyboard::SpecialKey 的映射
|
||||
inline Robot::Keyboard::SpecialKey stringToSpecialKey(const std::string& key) {
|
||||
static std::map<std::string, Robot::Keyboard::SpecialKey> keyMap = {
|
||||
{"backspace", Robot::Keyboard::BACKSPACE},
|
||||
{"enter", Robot::Keyboard::ENTER},
|
||||
{"return", Robot::Keyboard::ENTER},
|
||||
{"tab", Robot::Keyboard::TAB},
|
||||
{"escape", Robot::Keyboard::ESCAPE},
|
||||
{"esc", Robot::Keyboard::ESCAPE},
|
||||
{"up", Robot::Keyboard::UP},
|
||||
{"down", Robot::Keyboard::DOWN},
|
||||
{"right", Robot::Keyboard::RIGHT},
|
||||
{"left", Robot::Keyboard::LEFT},
|
||||
{"win", Robot::Keyboard::META},
|
||||
{"command", Robot::Keyboard::META},
|
||||
{"cmd", Robot::Keyboard::META},
|
||||
{"alt", Robot::Keyboard::ALT},
|
||||
{"ctrl", Robot::Keyboard::CONTROL},
|
||||
{"control", Robot::Keyboard::CONTROL},
|
||||
{"shift", Robot::Keyboard::SHIFT},
|
||||
{"capslock", Robot::Keyboard::CAPSLOCK},
|
||||
{"f1", Robot::Keyboard::F1},
|
||||
{"f2", Robot::Keyboard::F2},
|
||||
{"f3", Robot::Keyboard::F3},
|
||||
{"f4", Robot::Keyboard::F4},
|
||||
{"f5", Robot::Keyboard::F5},
|
||||
{"f6", Robot::Keyboard::F6},
|
||||
{"f7", Robot::Keyboard::F7},
|
||||
{"f8", Robot::Keyboard::F8},
|
||||
{"f9", Robot::Keyboard::F9},
|
||||
{"f10", Robot::Keyboard::F10},
|
||||
{"f11", Robot::Keyboard::F11},
|
||||
{"f12", Robot::Keyboard::F12},
|
||||
{"space", Robot::Keyboard::BACKSPACE} // 特殊处理空格
|
||||
};
|
||||
|
||||
std::string lower = key;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
|
||||
auto it = keyMap.find(lower);
|
||||
if (it != keyMap.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// 如果是单个字符,直接返回(会在其他地方处理)
|
||||
if (lower.length() == 1) {
|
||||
// 这是单个字符键,不是特殊键
|
||||
throw AutoGUIException("Unknown key: " + key);
|
||||
}
|
||||
|
||||
throw AutoGUIException("Unknown key: " + key);
|
||||
}
|
||||
|
||||
// 检查字符串是否是特殊键
|
||||
inline bool isSpecialKey(const std::string& key) {
|
||||
static std::vector<std::string> specialKeys = {
|
||||
"backspace", "enter", "return", "tab", "escape", "esc",
|
||||
"up", "down", "right", "left", "win", "command", "cmd",
|
||||
"alt", "ctrl", "control", "shift", "capslock",
|
||||
"f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12",
|
||||
"space"
|
||||
};
|
||||
|
||||
std::string lower = key;
|
||||
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
|
||||
|
||||
return std::find(specialKeys.begin(), specialKeys.end(), lower) != specialKeys.end();
|
||||
}
|
||||
|
||||
// 解决多屏幕移动鼠标问题
|
||||
struct ScreenInfo {
|
||||
int id; // 屏幕ID
|
||||
int x, y; // 相对于虚拟桌面原点的偏移
|
||||
int width, height;// 屏幕尺寸
|
||||
bool isPrimary; // 是否主屏
|
||||
};
|
||||
|
||||
// 获取所有屏幕信息
|
||||
std::vector<ScreenInfo> getAllScreens();
|
||||
|
||||
// 获取当前鼠标所在的屏幕
|
||||
ScreenInfo getCurrentScreen();
|
||||
|
||||
// 相对于当前屏幕移动鼠标(以当前屏幕左上角为原点)
|
||||
void moveToOnCurrentScreen(int x, int y, double duration = 0.0);
|
||||
|
||||
// 获取指定屏幕的尺寸(替代原来的size(),支持多屏)
|
||||
Robot::Point getScreenSize(int screenId = -1); // -1表示当前屏幕
|
||||
|
||||
|
||||
// 主要 API 函数
|
||||
/**
|
||||
* @brief 移动鼠标到指定位置
|
||||
* @param x 目标位置的X坐标
|
||||
* @param y 目标位置的Y坐标
|
||||
* @param duration 移动持续时间(秒),0表示立即移动
|
||||
*/
|
||||
void moveTo(int x, int y, double duration = 0.0);
|
||||
|
||||
/**
|
||||
* @brief 相对移动鼠标
|
||||
* @param xOffset X方向偏移量
|
||||
* @param yOffset Y方向偏移量
|
||||
* @param duration 移动持续时间(秒),0表示立即移动
|
||||
*/
|
||||
void moveRel(int xOffset, int yOffset, double duration = 0.0);
|
||||
|
||||
/**
|
||||
* @brief 单击鼠标
|
||||
* @param x 点击位置的X坐标,-1表示当前位置
|
||||
* @param y 点击位置的Y坐标,-1表示当前位置
|
||||
* @param button 鼠标按钮:left, right, middle
|
||||
* @param clicks 点击次数
|
||||
* @param interval 多次点击之间的间隔(秒)
|
||||
*/
|
||||
void click(int x = -1, int y = -1,
|
||||
Button button = Button::LEFT,
|
||||
int clicks = 1,
|
||||
double interval = 0.0);
|
||||
|
||||
/**
|
||||
* @brief 左键双击
|
||||
* @param x 双击位置的X坐标,-1表示当前位置
|
||||
* @param y 双击位置的Y坐标,-1表示当前位置
|
||||
*/
|
||||
void leftDouble(int x = -1, int y = -1);
|
||||
|
||||
/**
|
||||
* @brief 右键单击
|
||||
* @param x 点击位置的X坐标,-1表示当前位置
|
||||
* @param y 点击位置的Y坐标,-1表示当前位置
|
||||
*/
|
||||
void rightSingle(int x = -1, int y = -1);
|
||||
|
||||
/**
|
||||
* @brief 中键单击
|
||||
* @param x 点击位置的X坐标,-1表示当前位置
|
||||
* @param y 点击位置的Y坐标,-1表示当前位置
|
||||
*/
|
||||
void middleClick(int x = -1, int y = -1);
|
||||
|
||||
/**
|
||||
* @brief 按下鼠标按钮
|
||||
* @param button 要按下的鼠标按钮
|
||||
* @param x 按下位置的X坐标,-1表示当前位置
|
||||
* @param y 按下位置的Y坐标,-1表示当前位置
|
||||
*/
|
||||
void mouseDown(Button button = Button::LEFT, int x = -1, int y = -1);
|
||||
|
||||
/**
|
||||
* @brief 释放鼠标按钮
|
||||
* @param button 要释放的鼠标按钮
|
||||
* @param x 释放位置的X坐标,-1表示当前位置
|
||||
* @param y 释放位置的Y坐标,-1表示当前位置
|
||||
*/
|
||||
void mouseUp(Button button = Button::LEFT, int x = -1, int y = -1);
|
||||
|
||||
/**
|
||||
* @brief 从指定位置拖动到另一个位置
|
||||
* @param x1 起始位置的X坐标
|
||||
* @param y1 起始位置的Y坐标
|
||||
* @param x2 目标位置的X坐标
|
||||
* @param y2 目标位置的Y坐标
|
||||
* @param duration 拖动持续时间(秒),0表示立即拖动
|
||||
* @param button 拖动时按住的鼠标按钮
|
||||
*/
|
||||
void drag(int x1, int y1, int x2, int y2,
|
||||
double duration = 0.0,
|
||||
Button button = Button::LEFT);
|
||||
|
||||
/**
|
||||
* @brief 拖拽鼠标到指定位置
|
||||
* @param x 目标位置的X坐标
|
||||
* @param y 目标位置的Y坐标
|
||||
* @param duration 拖拽持续时间(秒),0表示立即拖拽
|
||||
* @param button 拖拽时按住的鼠标按钮
|
||||
*/
|
||||
void dragTo(int x, int y, double duration = 0.0, Button button = Button::LEFT);
|
||||
|
||||
/**
|
||||
* @brief 相对拖拽鼠标
|
||||
* @param xOffset X方向偏移量
|
||||
* @param yOffset Y方向偏移量
|
||||
* @param duration 拖拽持续时间(秒),0表示立即拖拽
|
||||
* @param button 拖拽时按住的鼠标按钮
|
||||
*/
|
||||
void dragRel(int xOffset, int yOffset, double duration = 0.0, Button button = Button::LEFT);
|
||||
|
||||
/**
|
||||
* @brief 获取当前鼠标位置
|
||||
* @return 包含x,y坐标的Point结构体
|
||||
*/
|
||||
Robot::Point position();
|
||||
|
||||
/**
|
||||
* @brief 滚动鼠标滚轮
|
||||
* @param clicks 滚动量,正数向上滚动,负数向下滚动
|
||||
* @param x 水平滚动量(Linux/macOS支持)
|
||||
*/
|
||||
void scroll(int clicks, int x = 0);
|
||||
|
||||
/**
|
||||
* @brief 输入文本
|
||||
* @param text 要输入的文本
|
||||
* @param interval 字符之间的间隔时间(秒),0表示无间隔
|
||||
* 注意:只支持ASCII字符
|
||||
*/
|
||||
void type(const std::string& text, double interval = 0.0);
|
||||
|
||||
/**
|
||||
* @brief 按下并释放一个键
|
||||
* @param key 键名(如:"a", "enter", "ctrl"等)
|
||||
*/
|
||||
void press(const std::string& key);
|
||||
|
||||
/**
|
||||
* @brief 按下键(不释放)
|
||||
* @param key 键名
|
||||
*/
|
||||
void keyDown(const std::string& key);
|
||||
|
||||
/**
|
||||
* @brief 释放键
|
||||
* @param key 键名
|
||||
*/
|
||||
void keyUp(const std::string& key);
|
||||
|
||||
/**
|
||||
* @brief 按下组合键
|
||||
* @param keys 键名列表,如 {"ctrl", "c"}
|
||||
*/
|
||||
void hotkey(const std::initializer_list<std::string>& keys);
|
||||
|
||||
/**
|
||||
* @brief 按下组合键(向量版本)
|
||||
* @param keys 键名向量
|
||||
*/
|
||||
void hotkey(const std::vector<std::string>& keys);
|
||||
|
||||
/**
|
||||
* @brief 睡眠/等待
|
||||
* @param seconds 等待的秒数
|
||||
*/
|
||||
void sleep(double seconds);
|
||||
|
||||
/**
|
||||
* @brief 获取屏幕尺寸
|
||||
* @return 包含屏幕宽度和高度的Point结构体
|
||||
* @note 需要平台特定实现
|
||||
* @note 注意:不提供屏幕管理功能,如果你有多个屏幕,那么返回的size可能是错误的,对于单个屏幕是没有影响的,对于获取屏幕尺寸,更加推荐使用Qt当中的接口
|
||||
*/
|
||||
Robot::Point size();
|
||||
|
||||
// 辅助函数
|
||||
/**
|
||||
* @brief 检查坐标是否有效
|
||||
* @param x X坐标
|
||||
* @param y Y坐标
|
||||
* @return 如果坐标有效返回true
|
||||
*/
|
||||
bool isValidCoord(int x, int y);
|
||||
|
||||
/**
|
||||
* @brief 将字符串转换为小写
|
||||
* @param str 输入字符串
|
||||
* @return 小写字符串
|
||||
*/
|
||||
std::string toLower(const std::string& str);
|
||||
|
||||
namespace ExtendedFunc {
|
||||
/// drag拓展功能
|
||||
/**
|
||||
* @brief 从指定位置开始拖动一个矩形区域
|
||||
* @param x1 矩形左上角X坐标
|
||||
* @param y1 矩形左上角Y坐标
|
||||
* @param width 矩形宽度
|
||||
* @param height 矩形高度
|
||||
* @param duration 拖动持续时间(秒)
|
||||
* @param button 拖动时按住的鼠标按钮
|
||||
*/
|
||||
void dragRect(int x1, int y1, int width, int height,
|
||||
double duration = 0.5, Button button = Button::LEFT);
|
||||
|
||||
/**
|
||||
* @brief 拖动一个圆形区域(从中心开始到边缘)
|
||||
* @param centerX 圆心X坐标
|
||||
* @param centerY 圆心Y坐标
|
||||
* @param radius 圆半径
|
||||
* @param duration 拖动持续时间(秒)
|
||||
* @param button 拖动时按住的鼠标按钮
|
||||
*/
|
||||
void dragCircle(int centerX, int centerY, int radius,
|
||||
double duration = 0.5, Button button = Button::LEFT);
|
||||
|
||||
/**
|
||||
* @brief 在区域内随机拖动(用于测试或自动化)
|
||||
* @param x1 区域左上角X坐标
|
||||
* @param y1 区域左上角Y坐标
|
||||
* @param x2 区域右下角X坐标
|
||||
* @param y2 区域右下角Y坐标
|
||||
* @param minDuration 最小拖动持续时间(秒)
|
||||
* @param maxDuration 最大拖动持续时间(秒)
|
||||
* @param button 拖动时按住的鼠标按钮
|
||||
*/
|
||||
void dragRandomInArea(int x1, int y1, int x2, int y2,
|
||||
double minDuration = 0.1, double maxDuration = 0.5,
|
||||
Button button = Button::LEFT);
|
||||
|
||||
/// 文字输入扩展功能
|
||||
/**
|
||||
* @brief 模拟人类打字(带随机延迟和可能的错误)
|
||||
* @param text 要输入的文本
|
||||
* @param minDelay 最小延迟(秒)
|
||||
* @param maxDelay 最大延迟(秒)
|
||||
* @param errorRate 错误率(0.0-1.0)
|
||||
*/
|
||||
void typeHumanLike(const std::string& text, double minDelay = 0.05,
|
||||
double maxDelay = 0.2, double errorRate = 0.0);
|
||||
|
||||
/**
|
||||
* @brief 输入文本并回车
|
||||
* @param text 要输入的文本
|
||||
* @param interval 字符间隔
|
||||
*/
|
||||
void typewriteEnter(const std::string& text, double interval = 0.0);
|
||||
|
||||
/**
|
||||
* @brief 输入多行文本
|
||||
* @param lines 文本行数组
|
||||
* @param interval 字符间隔
|
||||
* @param lineDelay 行间延迟
|
||||
*/
|
||||
void typewriteLines(const std::vector<std::string>& lines,
|
||||
double interval = 0.0, double lineDelay = 0.1);
|
||||
|
||||
/**
|
||||
* @brief 输入当前日期时间
|
||||
* @param format 时间格式(如:"%Y-%m-%d %H:%M:%S")
|
||||
*/
|
||||
void typeDateTime(const std::string& format = "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
/// 键盘快捷键扩展功能
|
||||
/**
|
||||
* @brief 保存文件 (Ctrl+S)
|
||||
*/
|
||||
void saveFile();
|
||||
|
||||
/**
|
||||
* @brief 打开文件 (Ctrl+O)
|
||||
*/
|
||||
void openFile();
|
||||
|
||||
/**
|
||||
* @brief 新建文件 (Ctrl+N)
|
||||
*/
|
||||
void newFile();
|
||||
|
||||
/**
|
||||
* @brief 复制 (Ctrl+C)
|
||||
*/
|
||||
void copy();
|
||||
|
||||
/**
|
||||
* @brief 粘贴 (Ctrl+V)
|
||||
*/
|
||||
void paste();
|
||||
|
||||
/**
|
||||
* @brief 剪切 (Ctrl+X)
|
||||
*/
|
||||
void cut();
|
||||
|
||||
/**
|
||||
* @brief 全选 (Ctrl+A)
|
||||
*/
|
||||
void selectAll();
|
||||
|
||||
/**
|
||||
* @brief 撤销 (Ctrl+Z)
|
||||
*/
|
||||
void undo();
|
||||
|
||||
/**
|
||||
* @brief 重做 (Ctrl+Y 或 Ctrl+Shift+Z)
|
||||
*/
|
||||
void redo();
|
||||
|
||||
/**
|
||||
* @brief 查找 (Ctrl+F)
|
||||
*/
|
||||
void find();
|
||||
|
||||
/**
|
||||
* @brief 替换 (Ctrl+H)
|
||||
*/
|
||||
void replace();
|
||||
|
||||
/**
|
||||
* @brief 打印 (Ctrl+P)
|
||||
*/
|
||||
void print();
|
||||
|
||||
/**
|
||||
* @brief 关闭窗口 (Alt+F4 或 Ctrl+W)
|
||||
*/
|
||||
void closeWindow(bool useAltF4 = true);
|
||||
|
||||
/**
|
||||
* @brief 切换窗口 (Alt+Tab)
|
||||
* @param times 切换次数
|
||||
*/
|
||||
void switchWindow(int times = 1);
|
||||
|
||||
/**
|
||||
* @brief 刷新 (F5)
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
/// 游戏/自动化操作功能拓展
|
||||
/**
|
||||
* @brief 自动点击器(连续点击)
|
||||
* @param x 点击位置X坐标
|
||||
* @param y 点击位置Y坐标
|
||||
* @param count 点击次数
|
||||
* @param interval 点击间隔(秒)
|
||||
* @param button 鼠标按钮
|
||||
*/
|
||||
void autoClicker(int x, int y, int count = 10, double interval = 0.1,
|
||||
Button button = Button::LEFT);
|
||||
|
||||
/**
|
||||
* @brief 连点器(按住连点)
|
||||
* @param x 点击位置X坐标
|
||||
* @param y 点击位置Y坐标
|
||||
* @param duration 持续时间(秒)
|
||||
* @param clickRate 点击频率(次/秒)
|
||||
* @param button 鼠标按钮
|
||||
*/
|
||||
void rapidClicker(int x, int y, double duration = 1.0,
|
||||
double clickRate = 10.0,
|
||||
Button button = Button::LEFT);
|
||||
|
||||
/**
|
||||
* @brief 按键连发(按住按键)
|
||||
* @param key 按键名称
|
||||
* @param duration 持续时间(秒)
|
||||
* @param pressRate 按键频率(次/秒)
|
||||
*/
|
||||
void rapidKeyPress(const std::string& key, double duration = 1.0,
|
||||
double pressRate = 10.0);
|
||||
|
||||
/**
|
||||
* @brief 组合键连发
|
||||
* @param keys 按键组合
|
||||
* @param duration 持续时间
|
||||
* @param pressRate 按键频率
|
||||
*/
|
||||
void rapidHotkey(const std::vector<std::string>& keys, double duration = 1.0,
|
||||
double pressRate = 5.0);
|
||||
|
||||
} // namespace ExtendedFunc
|
||||
|
||||
} // namespace AutoGUI
|
||||
+898
@@ -0,0 +1,898 @@
|
||||
#ifdef __APPLE__
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <map>
|
||||
#include <cstring>
|
||||
|
||||
#include "./Keyboard.h"
|
||||
#include "./Utils.h"
|
||||
|
||||
namespace Robot {
|
||||
|
||||
int Keyboard::delay = 1;
|
||||
|
||||
const char Keyboard::INVALID_ASCII = static_cast<char>(0xFF);
|
||||
|
||||
std::thread Keyboard::keyPressThread;
|
||||
std::atomic<bool> Keyboard::continueHolding(false);
|
||||
std::set<char> Keyboard::heldAsciiChars;
|
||||
std::set<Keyboard::SpecialKey> Keyboard::heldSpecialKeys;
|
||||
|
||||
#ifdef __linux__
|
||||
Display* Keyboard::display = nullptr;
|
||||
Window Keyboard::rootWindow = 0;
|
||||
|
||||
void Keyboard::InitializeX11() {
|
||||
if (display == nullptr) {
|
||||
display = XOpenDisplay(nullptr);
|
||||
if (display == nullptr) {
|
||||
throw std::runtime_error("Cannot open X11 display");
|
||||
}
|
||||
rootWindow = DefaultRootWindow(display);
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::CleanupX11() {
|
||||
if (display != nullptr) {
|
||||
XCloseDisplay(display);
|
||||
display = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
static bool NeedShiftForKeySym(KeySym keysym) {
|
||||
// 检查这个键是否需要 Shift 键配合
|
||||
switch (keysym) {
|
||||
case XK_exclam: // !
|
||||
case XK_at: // @
|
||||
case XK_numbersign: // #
|
||||
case XK_dollar: // $
|
||||
case XK_percent: // %
|
||||
case XK_asciicircum: // ^
|
||||
case XK_ampersand: // &
|
||||
case XK_asterisk: // *
|
||||
case XK_parenleft: // (
|
||||
case XK_parenright: // )
|
||||
case XK_underscore: // _
|
||||
case XK_plus: // +
|
||||
case XK_braceleft: // {
|
||||
case XK_braceright: // }
|
||||
case XK_bar: // |
|
||||
case XK_colon: // :
|
||||
case XK_quotedbl: // "
|
||||
case XK_less: // <
|
||||
case XK_greater: // >
|
||||
case XK_question: // ?
|
||||
case XK_asciitilde: // ~
|
||||
return true;
|
||||
|
||||
// 大写字母需要 Shift 键
|
||||
case XK_A:
|
||||
case XK_B:
|
||||
case XK_C:
|
||||
case XK_D:
|
||||
case XK_E:
|
||||
case XK_F:
|
||||
case XK_G:
|
||||
case XK_H:
|
||||
case XK_I:
|
||||
case XK_J:
|
||||
case XK_K:
|
||||
case XK_L:
|
||||
case XK_M:
|
||||
case XK_N:
|
||||
case XK_O:
|
||||
case XK_P:
|
||||
case XK_Q:
|
||||
case XK_R:
|
||||
case XK_S:
|
||||
case XK_T:
|
||||
case XK_U:
|
||||
case XK_V:
|
||||
case XK_W:
|
||||
case XK_X:
|
||||
case XK_Y:
|
||||
case XK_Z:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool NeedShiftForChar(const char c) {
|
||||
// 简单判断:大写字母和特殊符号需要 Shift
|
||||
if (std::isupper(c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 特殊符号需要 Shift
|
||||
const char* shiftChars = "!@#$%^&*()_+{}|:\"<>?~";
|
||||
return (strchr(shiftChars, c) != nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void Keyboard::HoldStart(char asciiChar) {
|
||||
if (heldAsciiChars.empty() && heldSpecialKeys.empty()) {
|
||||
continueHolding = true;
|
||||
keyPressThread = std::thread(KeyHoldThread);
|
||||
}
|
||||
heldAsciiChars.insert(asciiChar);
|
||||
}
|
||||
|
||||
void Keyboard::HoldStart(SpecialKey specialKey) {
|
||||
if (heldAsciiChars.empty() && heldSpecialKeys.empty()) {
|
||||
continueHolding = true;
|
||||
keyPressThread = std::thread(KeyHoldThread);
|
||||
}
|
||||
heldSpecialKeys.insert(specialKey);
|
||||
}
|
||||
|
||||
void Keyboard::HoldStop(char asciiChar) {
|
||||
heldAsciiChars.erase(asciiChar);
|
||||
if (heldAsciiChars.empty() && heldSpecialKeys.empty()) {
|
||||
continueHolding = false;
|
||||
if (keyPressThread.joinable()) {
|
||||
keyPressThread.join();
|
||||
}
|
||||
}
|
||||
Release(asciiChar);
|
||||
}
|
||||
|
||||
void Keyboard::HoldStop(SpecialKey specialKey) {
|
||||
heldSpecialKeys.erase(specialKey);
|
||||
if (heldAsciiChars.empty() && heldSpecialKeys.empty()) {
|
||||
continueHolding = false;
|
||||
if (keyPressThread.joinable()) {
|
||||
keyPressThread.join();
|
||||
}
|
||||
}
|
||||
Release(specialKey);
|
||||
}
|
||||
|
||||
void Keyboard::KeyHoldThread() {
|
||||
while (continueHolding) {
|
||||
for (char asciiChar : heldAsciiChars) {
|
||||
Press(asciiChar);
|
||||
}
|
||||
for (SpecialKey specialKey : heldSpecialKeys) {
|
||||
Press(specialKey);
|
||||
}
|
||||
Robot::delay(50);
|
||||
}
|
||||
|
||||
for (char asciiChar : heldAsciiChars) {
|
||||
Release(asciiChar);
|
||||
}
|
||||
for (SpecialKey specialKey : heldSpecialKeys) {
|
||||
Release(specialKey);
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::Type(const std::string &query) {
|
||||
for (const char c : query) {
|
||||
if (!KeyUtils::IsValidAscii(c)) {
|
||||
std::cerr << "Warning: Skipping invalid ASCII character: " << static_cast<int>(c) << std::endl;
|
||||
continue;
|
||||
}
|
||||
Click(c);
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::TypeHumanLike(const std::string &query) {
|
||||
std::normal_distribution<double> distribution(75, 25);
|
||||
std::random_device rd;
|
||||
std::mt19937 engine(rd());
|
||||
|
||||
for (char c : query) {
|
||||
if (!KeyUtils::IsValidAscii(c)) {
|
||||
std::cerr << "Warning: Skipping invalid ASCII character: " << static_cast<int>(c) << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
Click(c);
|
||||
Robot::delay((int)distribution(engine));
|
||||
}
|
||||
}
|
||||
|
||||
void Keyboard::Click(char asciiChar) {
|
||||
#ifdef __linux__
|
||||
InitializeX11();
|
||||
// Linux 特殊处理:对于需要 Shift 的字符,先按下 Shift
|
||||
bool needShift = NeedShiftForChar(asciiChar);
|
||||
|
||||
if (needShift) {
|
||||
// 获取 Shift 键的键码
|
||||
KeyCode shiftKeyCode = XKeysymToKeycode(display, XK_Shift_L);
|
||||
if (shiftKeyCode != 0) {
|
||||
XTestFakeKeyEvent(display, shiftKeyCode, True, CurrentTime);
|
||||
XFlush(display);
|
||||
Robot::delay(delay);
|
||||
}
|
||||
}
|
||||
Press(asciiChar);
|
||||
Release(asciiChar);
|
||||
if (needShift) {
|
||||
// 释放 Shift 键
|
||||
KeyCode shiftKeyCode = XKeysymToKeycode(display, XK_Shift_L);
|
||||
if (shiftKeyCode != 0) {
|
||||
XTestFakeKeyEvent(display, shiftKeyCode, False, CurrentTime);
|
||||
XFlush(display);
|
||||
Robot::delay(delay);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
// macOS实现
|
||||
bool needShift = KeyUtils::NeedsShift(asciiChar);~
|
||||
char baseKey = KeyUtils::GetBaseKey(asciiChar);
|
||||
auto it = asciiToVirtualKeyMap.find(baseKey);
|
||||
if (it == asciiToVirtualKeyMap.end()) {
|
||||
std::cerr << "Warning: Character not found in key map: " << asciiChar << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
CGKeyCode keycode = static_cast<CGKeyCode>(it->second);
|
||||
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
|
||||
if (needShift) {
|
||||
// 按下Shift键
|
||||
CGEventRef shiftDown = CGEventCreateKeyboardEvent(source, kVK_Shift, true);
|
||||
CGEventPost(kCGHIDEventTap, shiftDown);
|
||||
CFRelease(shiftDown);
|
||||
Robot::delay(delay);
|
||||
}
|
||||
|
||||
// 按下并释放目标键
|
||||
CGEventRef keyDown = CGEventCreateKeyboardEvent(source, keycode, true);
|
||||
CGEventRef keyUp = CGEventCreateKeyboardEvent(source, keycode, false);
|
||||
|
||||
CGEventPost(kCGHIDEventTap, keyDown);
|
||||
Robot::delay(delay);
|
||||
CGEventPost(kCGHIDEventTap, keyUp);
|
||||
Robot::delay(delay);
|
||||
|
||||
if (needShift) {
|
||||
// 释放Shift键
|
||||
CGEventRef shiftUp = CGEventCreateKeyboardEvent(source, kVK_Shift, false);
|
||||
CGEventPost(kCGHIDEventTap, shiftUp);
|
||||
CFRelease(shiftUp);
|
||||
Robot::delay(delay);
|
||||
}
|
||||
|
||||
CFRelease(keyDown);
|
||||
CFRelease(keyUp);
|
||||
CFRelease(source);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows实现
|
||||
bool needShift = KeyUtils::NeedsShift(asciiChar);
|
||||
char baseKey = KeyUtils::GetBaseKey(asciiChar);
|
||||
|
||||
// 获取基础键的虚拟键码
|
||||
SHORT vkAndShift = VkKeyScan(baseKey);
|
||||
if (vkAndShift == -1) {
|
||||
std::cerr << "Warning: Cannot get virtual key for character: " << asciiChar << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
WORD keycode = static_cast<WORD>(vkAndShift & 0xFF);
|
||||
|
||||
std::vector<INPUT> inputs;
|
||||
|
||||
if (needShift) {
|
||||
// Shift键按下
|
||||
INPUT shiftDown = {0};
|
||||
shiftDown.type = INPUT_KEYBOARD;
|
||||
shiftDown.ki.wVk = VK_SHIFT;
|
||||
inputs.push_back(shiftDown);
|
||||
}
|
||||
|
||||
// 目标键按下
|
||||
INPUT keyDown = {0};
|
||||
keyDown.type = INPUT_KEYBOARD;
|
||||
keyDown.ki.wVk = keycode;
|
||||
inputs.push_back(keyDown);
|
||||
|
||||
// 目标键释放
|
||||
INPUT keyUp = {0};
|
||||
keyUp.type = INPUT_KEYBOARD;
|
||||
keyUp.ki.wVk = keycode;
|
||||
keyUp.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
inputs.push_back(keyUp);
|
||||
|
||||
if (needShift) {
|
||||
// Shift键释放
|
||||
INPUT shiftUp = {0};
|
||||
shiftUp.type = INPUT_KEYBOARD;
|
||||
shiftUp.ki.wVk = VK_SHIFT;
|
||||
shiftUp.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
inputs.push_back(shiftUp);
|
||||
}
|
||||
|
||||
// 发送所有输入
|
||||
SendInput(static_cast<UINT>(inputs.size()), inputs.data(), sizeof(INPUT));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Keyboard::Click(SpecialKey specialKey) {
|
||||
Press(specialKey);
|
||||
Release(specialKey);
|
||||
}
|
||||
|
||||
void Keyboard::Press(char asciiChar) {
|
||||
KeyCode keycode = AsciiToVirtualKey(asciiChar);
|
||||
#ifdef __APPLE__
|
||||
CGEventSourceRef source =
|
||||
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventRef event = CGEventCreateKeyboardEvent(source, keycode, true);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
|
||||
CFRelease(event);
|
||||
CFRelease(source);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = keycode;
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
InitializeX11();
|
||||
KeyCode xkeycode = XKeysymToKeycode(display, keycode);
|
||||
if (xkeycode != 0) {
|
||||
XTestFakeKeyEvent(display, xkeycode, True, CurrentTime);
|
||||
XFlush(display);
|
||||
}
|
||||
#endif
|
||||
Robot::delay(delay);
|
||||
}
|
||||
|
||||
void Keyboard::Press(SpecialKey specialKey) {
|
||||
KeyCode keycode = SpecialKeyToVirtualKey(specialKey);
|
||||
#ifdef __APPLE__
|
||||
CGEventRef event = CGEventCreateKeyboardEvent(nullptr, keycode, true);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = keycode;
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
InitializeX11();
|
||||
KeyCode xkeycode = XKeysymToKeycode(display, keycode);
|
||||
if (xkeycode != 0) {
|
||||
XTestFakeKeyEvent(display, xkeycode, True, CurrentTime);
|
||||
XFlush(display);
|
||||
}
|
||||
#endif
|
||||
Robot::delay(delay);
|
||||
}
|
||||
|
||||
void Keyboard::Release(char asciiChar) {
|
||||
KeyCode keycode = AsciiToVirtualKey(asciiChar);
|
||||
#ifdef __APPLE__
|
||||
CGEventRef event =
|
||||
CGEventCreateKeyboardEvent(nullptr, (CGKeyCode)keycode, false);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = keycode;
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
InitializeX11();
|
||||
KeyCode xkeycode = XKeysymToKeycode(display, keycode);
|
||||
if (xkeycode != 0) {
|
||||
XTestFakeKeyEvent(display, xkeycode, False, CurrentTime);
|
||||
XFlush(display);
|
||||
}
|
||||
#endif
|
||||
Robot::delay(delay);
|
||||
}
|
||||
|
||||
void Keyboard::Release(SpecialKey specialKey) {
|
||||
KeyCode keycode = SpecialKeyToVirtualKey(specialKey);
|
||||
#ifdef __APPLE__
|
||||
CGEventRef event =
|
||||
CGEventCreateKeyboardEvent(nullptr, (CGKeyCode)keycode, false);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_KEYBOARD;
|
||||
input.ki.wVk = keycode;
|
||||
input.ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
InitializeX11();
|
||||
KeyCode xkeycode = XKeysymToKeycode(display, keycode);
|
||||
if (xkeycode != 0) {
|
||||
XTestFakeKeyEvent(display, xkeycode, False, CurrentTime);
|
||||
XFlush(display);
|
||||
}
|
||||
#endif
|
||||
Robot::delay(delay);
|
||||
}
|
||||
|
||||
KeyCode Keyboard::SpecialKeyToVirtualKey(SpecialKey specialKey) {
|
||||
return specialKeyToVirtualKeyMap.at(specialKey);
|
||||
}
|
||||
|
||||
KeyCode Keyboard::AsciiToVirtualKey(char asciiChar) {
|
||||
#ifdef __APPLE__
|
||||
auto it = asciiToVirtualKeyMap.find(asciiChar);
|
||||
if (it == asciiToVirtualKeyMap.end()) {
|
||||
std::cerr
|
||||
<< "Warning: Character not found in the virtual key map. Ignoring..."
|
||||
<< std::endl;
|
||||
return 0xFFFF; // Return an invalid keycode
|
||||
}
|
||||
return static_cast<KeyCode>(it->second);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows: VkKeyScan 函数返回一个short,高字节是shift状态,低字节是虚拟键码
|
||||
SHORT vkAndShift = VkKeyScan(asciiChar);
|
||||
if (vkAndShift == -1) {
|
||||
std::cerr
|
||||
<< "Warning: Character not found in the virtual key map. Ignoring..."
|
||||
<< std::endl;
|
||||
return 0xFFFF; // Return an invalid keycode
|
||||
}
|
||||
// 返回虚拟键码(低字节)
|
||||
return static_cast<KeyCode>(vkAndShift & 0xFF);
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
// Convert ASCII to X11 KeySym
|
||||
// For Linux, we need to handle case sensitivity properly
|
||||
if (std::isalpha(asciiChar)) {
|
||||
// For letters, use lowercase KeySym
|
||||
switch (char lowerChar = std::tolower(asciiChar)) {
|
||||
case 'a': return XK_a;
|
||||
case 'b': return XK_b;
|
||||
case 'c': return XK_c;
|
||||
case 'd': return XK_d;
|
||||
case 'e': return XK_e;
|
||||
case 'f': return XK_f;
|
||||
case 'g': return XK_g;
|
||||
case 'h': return XK_h;
|
||||
case 'i': return XK_i;
|
||||
case 'j': return XK_j;
|
||||
case 'k': return XK_k;
|
||||
case 'l': return XK_l;
|
||||
case 'm': return XK_m;
|
||||
case 'n': return XK_n;
|
||||
case 'o': return XK_o;
|
||||
case 'p': return XK_p;
|
||||
case 'q': return XK_q;
|
||||
case 'r': return XK_r;
|
||||
case 's': return XK_s;
|
||||
case 't': return XK_t;
|
||||
case 'u': return XK_u;
|
||||
case 'v': return XK_v;
|
||||
case 'w': return XK_w;
|
||||
case 'x': return XK_x;
|
||||
case 'y': return XK_y;
|
||||
case 'z': return XK_z;
|
||||
}
|
||||
}
|
||||
|
||||
// For numbers and special characters
|
||||
switch (asciiChar) {
|
||||
// Numbers
|
||||
case '0': return XK_0;
|
||||
case '1': return XK_1;
|
||||
case '2': return XK_2;
|
||||
case '3': return XK_3;
|
||||
case '4': return XK_4;
|
||||
case '5': return XK_5;
|
||||
case '6': return XK_6;
|
||||
case '7': return XK_7;
|
||||
case '8': return XK_8;
|
||||
case '9': return XK_9;
|
||||
|
||||
// Special characters that need shift
|
||||
case '!': return XK_exclam; // Shift+1
|
||||
case '@': return XK_at; // Shift+2
|
||||
case '#': return XK_numbersign; // Shift+3
|
||||
case '$': return XK_dollar; // Shift+4
|
||||
case '%': return XK_percent; // Shift+5
|
||||
case '^': return XK_asciicircum; // Shift+6
|
||||
case '&': return XK_ampersand; // Shift+7
|
||||
case '*': return XK_asterisk; // Shift+8
|
||||
case '(': return XK_parenleft; // Shift+9
|
||||
case ')': return XK_parenright; // Shift+0
|
||||
case '_': return XK_underscore; // Shift+-
|
||||
case '+': return XK_plus; // Shift+=
|
||||
case '{': return XK_braceleft; // Shift+[
|
||||
case '}': return XK_braceright; // Shift+]
|
||||
case '|': return XK_bar; // Shift+\
|
||||
case ':': return XK_colon; // Shift+;
|
||||
case '"': return XK_quotedbl; // Shift+'
|
||||
case '<': return XK_less; // Shift+,
|
||||
case '>': return XK_greater; // Shift+.
|
||||
case '?': return XK_question; // Shift+/
|
||||
case '~': return XK_asciitilde; // Shift+`
|
||||
|
||||
// Special characters without shift
|
||||
case ' ': return XK_space;
|
||||
case '\t': return XK_Tab;
|
||||
case '\n': return XK_Return;
|
||||
case '\b': return XK_BackSpace;
|
||||
case 27: return XK_Escape; // ESC
|
||||
|
||||
case '-': return XK_minus;
|
||||
case '=': return XK_equal;
|
||||
case '[': return XK_bracketleft;
|
||||
case ']': return XK_bracketright;
|
||||
case '\\': return XK_backslash;
|
||||
case ';': return XK_semicolon;
|
||||
case '\'': return XK_apostrophe;
|
||||
case ',': return XK_comma;
|
||||
case '.': return XK_period;
|
||||
case '/': return XK_slash;
|
||||
case '`': return XK_grave;
|
||||
|
||||
case ':': return XK_colon;
|
||||
|
||||
default:
|
||||
std::cerr << "Warning: Character " << static_cast<int>(asciiChar)
|
||||
<< " not mapped to X11 KeySym. Using XK_space instead."
|
||||
<< std::endl;
|
||||
return XK_space;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::map<Keyboard::SpecialKey, KeyCode> Keyboard::specialKeyToVirtualKeyMap = {
|
||||
{Keyboard::BACKSPACE, kVK_Delete},
|
||||
{Keyboard::ENTER, kVK_Return},
|
||||
{Keyboard::TAB, kVK_Tab},
|
||||
{Keyboard::ESCAPE, kVK_Escape},
|
||||
{Keyboard::UP, kVK_UpArrow},
|
||||
{Keyboard::DOWN, kVK_DownArrow},
|
||||
{Keyboard::RIGHT, kVK_RightArrow},
|
||||
{Keyboard::LEFT, kVK_LeftArrow},
|
||||
{Keyboard::META, kVK_Command},
|
||||
{Keyboard::ALT, kVK_Option},
|
||||
{Keyboard::CONTROL, kVK_Control},
|
||||
{Keyboard::SHIFT, kVK_Shift},
|
||||
{Keyboard::CAPSLOCK, kVK_CapsLock},
|
||||
{Keyboard::F1, kVK_F1},
|
||||
{Keyboard::F2, kVK_F2},
|
||||
{Keyboard::F3, kVK_F3},
|
||||
{Keyboard::F4, kVK_F4},
|
||||
{Keyboard::F5, kVK_F5},
|
||||
{Keyboard::F6, kVK_F6},
|
||||
{Keyboard::F7, kVK_F7},
|
||||
{Keyboard::F8, kVK_F8},
|
||||
{Keyboard::F9, kVK_F9},
|
||||
{Keyboard::F10, kVK_F10},
|
||||
{Keyboard::F11, kVK_F11},
|
||||
{Keyboard::F12, kVK_F12}};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef __linux__
|
||||
std::map<Keyboard::SpecialKey, KeyCode> Keyboard::specialKeyToVirtualKeyMap = {
|
||||
{Keyboard::BACKSPACE, XK_BackSpace},
|
||||
{Keyboard::ENTER, XK_Return},
|
||||
{Keyboard::TAB, XK_Tab},
|
||||
{Keyboard::ESCAPE, XK_Escape},
|
||||
{Keyboard::UP, XK_Up},
|
||||
{Keyboard::DOWN, XK_Down},
|
||||
{Keyboard::RIGHT, XK_Right},
|
||||
{Keyboard::LEFT, XK_Left},
|
||||
{Keyboard::META, XK_Super_L}, // Super/Windows key
|
||||
{Keyboard::ALT, XK_Alt_L},
|
||||
{Keyboard::CONTROL, XK_Control_L},
|
||||
{Keyboard::SHIFT, XK_Shift_L},
|
||||
{Keyboard::CAPSLOCK, XK_Caps_Lock},
|
||||
{Keyboard::F1, XK_F1},
|
||||
{Keyboard::F2, XK_F2},
|
||||
{Keyboard::F3, XK_F3},
|
||||
{Keyboard::F4, XK_F4},
|
||||
{Keyboard::F5, XK_F5},
|
||||
{Keyboard::F6, XK_F6},
|
||||
{Keyboard::F7, XK_F7},
|
||||
{Keyboard::F8, XK_F8},
|
||||
{Keyboard::F9, XK_F9},
|
||||
{Keyboard::F10, XK_F10},
|
||||
{Keyboard::F11, XK_F11},
|
||||
{Keyboard::F12, XK_F12}};
|
||||
#endif
|
||||
|
||||
char Keyboard::VirtualKeyToAscii(KeyCode virtualKey) {
|
||||
#ifdef __APPLE__
|
||||
auto map = asciiToVirtualKeyMap;
|
||||
auto it = std::find_if(
|
||||
map.begin(),
|
||||
map.end(),
|
||||
[virtualKey](const std::pair<char, int> &p) {
|
||||
return p.second == virtualKey;
|
||||
}
|
||||
);
|
||||
|
||||
if (it == map.end()) {
|
||||
return INVALID_ASCII;
|
||||
}
|
||||
|
||||
return it->first;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Convert the virtual key code to a scan code
|
||||
UINT scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);
|
||||
|
||||
// Convert the scan code to the corresponding character
|
||||
char character = 0;
|
||||
BYTE keyboardState[256] = {0};
|
||||
GetKeyboardState(keyboardState);
|
||||
wchar_t buffer[2];
|
||||
if (ToUnicode(virtualKey, scanCode, keyboardState, buffer, 2, 0) == 1) {
|
||||
character = static_cast<char>(buffer[0]);
|
||||
}
|
||||
return character;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
InitializeX11();
|
||||
|
||||
// Get the keycode from keysym
|
||||
KeyCode keycode = XKeysymToKeycode(display, virtualKey);
|
||||
if (keycode == 0) {
|
||||
return INVALID_ASCII;
|
||||
}
|
||||
|
||||
// Get the current keyboard state
|
||||
char keys[32];
|
||||
XQueryKeymap(display, keys);
|
||||
|
||||
// Get the current modifier state
|
||||
XKeyEvent event;
|
||||
event.display = display;
|
||||
event.window = rootWindow;
|
||||
event.root = rootWindow;
|
||||
event.subwindow = None;
|
||||
event.time = CurrentTime;
|
||||
event.x = event.y = event.x_root = event.y_root = 0;
|
||||
event.same_screen = True;
|
||||
event.keycode = keycode;
|
||||
event.state = 0;
|
||||
|
||||
// Try to get the character with different modifier states
|
||||
KeySym keysym;
|
||||
XLookupString(&event, nullptr, 0, &keysym, nullptr);
|
||||
|
||||
// Convert keysym to ASCII if possible
|
||||
if (keysym >= 0x20 && keysym <= 0x7E) { // Printable ASCII range
|
||||
return static_cast<char>(keysym);
|
||||
}
|
||||
|
||||
return INVALID_ASCII;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Keyboard::SpecialKey Keyboard::VirtualKeyToSpecialKey(KeyCode virtualKey) {
|
||||
#ifdef __APPLE__
|
||||
switch (virtualKey) {
|
||||
case 123:
|
||||
return Keyboard::LEFT;
|
||||
case 124:
|
||||
return Keyboard::RIGHT;
|
||||
case 125:
|
||||
return Keyboard::DOWN;
|
||||
case 126:
|
||||
return Keyboard::UP;
|
||||
case 36:
|
||||
return Keyboard::ENTER;
|
||||
case 48:
|
||||
return Keyboard::TAB;
|
||||
case 51:
|
||||
return Keyboard::BACKSPACE;
|
||||
case 53:
|
||||
return Keyboard::ESCAPE;
|
||||
case 55:
|
||||
return Keyboard::META;
|
||||
case 56:
|
||||
return Keyboard::SHIFT;
|
||||
case 57:
|
||||
return Keyboard::CAPSLOCK;
|
||||
case 58:
|
||||
return Keyboard::ALT;
|
||||
case 59:
|
||||
return Keyboard::CONTROL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
switch (virtualKey) {
|
||||
case VK_LEFT:
|
||||
return Keyboard::LEFT;
|
||||
case VK_RIGHT:
|
||||
return Keyboard::RIGHT;
|
||||
case VK_DOWN:
|
||||
return Keyboard::DOWN;
|
||||
case VK_UP:
|
||||
return Keyboard::UP;
|
||||
case VK_RETURN:
|
||||
return Keyboard::ENTER;
|
||||
case VK_TAB:
|
||||
return Keyboard::TAB;
|
||||
case VK_BACK:
|
||||
return Keyboard::BACKSPACE;
|
||||
case VK_ESCAPE:
|
||||
return Keyboard::ESCAPE;
|
||||
case VK_LWIN:
|
||||
case VK_RWIN:
|
||||
return Keyboard::META;
|
||||
case VK_SHIFT:
|
||||
return Keyboard::SHIFT;
|
||||
case VK_CAPITAL:
|
||||
return Keyboard::CAPSLOCK;
|
||||
case VK_MENU:
|
||||
return Keyboard::ALT;
|
||||
case VK_CONTROL:
|
||||
return Keyboard::CONTROL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
switch (virtualKey) {
|
||||
case XK_Left:
|
||||
return Keyboard::LEFT;
|
||||
case XK_Right:
|
||||
return Keyboard::RIGHT;
|
||||
case XK_Down:
|
||||
return Keyboard::DOWN;
|
||||
case XK_Up:
|
||||
return Keyboard::UP;
|
||||
case XK_Return:
|
||||
return Keyboard::ENTER;
|
||||
case XK_Tab:
|
||||
return Keyboard::TAB;
|
||||
case XK_BackSpace:
|
||||
return Keyboard::BACKSPACE;
|
||||
case XK_Escape:
|
||||
return Keyboard::ESCAPE;
|
||||
case XK_Super_L:
|
||||
case XK_Super_R:
|
||||
return Keyboard::META;
|
||||
case XK_Shift_L:
|
||||
case XK_Shift_R:
|
||||
return Keyboard::SHIFT;
|
||||
case XK_Caps_Lock:
|
||||
return Keyboard::CAPSLOCK;
|
||||
case XK_Alt_L:
|
||||
case XK_Alt_R:
|
||||
return Keyboard::ALT;
|
||||
case XK_Control_L:
|
||||
case XK_Control_R:
|
||||
return Keyboard::CONTROL;
|
||||
}
|
||||
#endif
|
||||
// Default case for all platforms
|
||||
return static_cast<Keyboard::SpecialKey>(0);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::map<Keyboard::SpecialKey, KeyCode> Keyboard::specialKeyToVirtualKeyMap = {
|
||||
{Keyboard::BACKSPACE, VK_BACK},
|
||||
{Keyboard::ENTER, VK_RETURN},
|
||||
{Keyboard::TAB, VK_TAB},
|
||||
{Keyboard::ESCAPE, VK_ESCAPE},
|
||||
{Keyboard::UP, VK_UP},
|
||||
{Keyboard::DOWN, VK_DOWN},
|
||||
{Keyboard::RIGHT, VK_RIGHT},
|
||||
{Keyboard::LEFT, VK_LEFT},
|
||||
{Keyboard::META, VK_LWIN},
|
||||
{Keyboard::ALT, VK_MENU},
|
||||
{Keyboard::CONTROL, VK_CONTROL},
|
||||
{Keyboard::SHIFT, VK_SHIFT},
|
||||
{Keyboard::CAPSLOCK, VK_CAPITAL},
|
||||
{Keyboard::F1, VK_F1},
|
||||
{Keyboard::F2, VK_F2},
|
||||
{Keyboard::F3, VK_F3},
|
||||
{Keyboard::F4, VK_F4},
|
||||
{Keyboard::F5, VK_F5},
|
||||
{Keyboard::F6, VK_F6},
|
||||
{Keyboard::F7, VK_F7},
|
||||
{Keyboard::F8, VK_F8},
|
||||
{Keyboard::F9, VK_F9},
|
||||
{Keyboard::F10, VK_F10},
|
||||
{Keyboard::F11, VK_F11},
|
||||
{Keyboard::F12, VK_F12}};
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::map<char, int> Keyboard::asciiToVirtualKeyMap = {
|
||||
// 数字
|
||||
{'0', kVK_ANSI_0}, {'1', kVK_ANSI_1}, {'2', kVK_ANSI_2},
|
||||
{'3', kVK_ANSI_3}, {'4', kVK_ANSI_4}, {'5', kVK_ANSI_5},
|
||||
{'6', kVK_ANSI_6}, {'7', kVK_ANSI_7}, {'8', kVK_ANSI_8},
|
||||
{'9', kVK_ANSI_9},
|
||||
|
||||
// 字母(只使用小写)
|
||||
{'a', kVK_ANSI_A}, {'b', kVK_ANSI_B}, {'c', kVK_ANSI_C},
|
||||
{'d', kVK_ANSI_D}, {'e', kVK_ANSI_E}, {'f', kVK_ANSI_F},
|
||||
{'g', kVK_ANSI_G}, {'h', kVK_ANSI_H}, {'i', kVK_ANSI_I},
|
||||
{'j', kVK_ANSI_J}, {'k', kVK_ANSI_K}, {'l', kVK_ANSI_L},
|
||||
{'m', kVK_ANSI_M}, {'n', kVK_ANSI_N}, {'o', kVK_ANSI_O},
|
||||
{'p', kVK_ANSI_P}, {'q', kVK_ANSI_Q}, {'r', kVK_ANSI_R},
|
||||
{'s', kVK_ANSI_S}, {'t', kVK_ANSI_T}, {'u', kVK_ANSI_U},
|
||||
{'v', kVK_ANSI_V}, {'w', kVK_ANSI_W}, {'x', kVK_ANSI_X},
|
||||
{'y', kVK_ANSI_Y}, {'z', kVK_ANSI_Z},
|
||||
|
||||
// 特殊字符(不需要Shift的)
|
||||
{' ', kVK_Space},
|
||||
{'-', kVK_ANSI_Minus},
|
||||
{'=', kVK_ANSI_Equal},
|
||||
{'[', kVK_ANSI_LeftBracket},
|
||||
{']', kVK_ANSI_RightBracket},
|
||||
{'\\', kVK_ANSI_Backslash},
|
||||
{';', kVK_ANSI_Semicolon},
|
||||
{'\'', kVK_ANSI_Quote},
|
||||
{',', kVK_ANSI_Comma},
|
||||
{'.', kVK_ANSI_Period},
|
||||
{'/', kVK_ANSI_Slash},
|
||||
{'`', kVK_ANSI_Grave},
|
||||
|
||||
// 需要Shift的字符(映射到对应数字或符号键)
|
||||
{'!', kVK_ANSI_1}, // Shift+1
|
||||
{'@', kVK_ANSI_2}, // Shift+2
|
||||
{'#', kVK_ANSI_3}, // Shift+3
|
||||
{'$', kVK_ANSI_4}, // Shift+4
|
||||
{'%', kVK_ANSI_5}, // Shift+5
|
||||
{'^', kVK_ANSI_6}, // Shift+6
|
||||
{'&', kVK_ANSI_7}, // Shift+7
|
||||
{'*', kVK_ANSI_8}, // Shift+8
|
||||
{'(', kVK_ANSI_9}, // Shift+9
|
||||
{')', kVK_ANSI_0}, // Shift+0
|
||||
{'_', kVK_ANSI_Minus}, // Shift+-
|
||||
{'+', kVK_ANSI_Equal}, // Shift+=
|
||||
{'{', kVK_ANSI_LeftBracket}, // Shift+[
|
||||
{'}', kVK_ANSI_RightBracket}, // Shift+]
|
||||
{'|', kVK_ANSI_Backslash}, // Shift+\
|
||||
{':', kVK_ANSI_Semicolon}, // Shift+;
|
||||
{'"', kVK_ANSI_Quote}, // Shift+'
|
||||
{'<', kVK_ANSI_Comma}, // Shift+,
|
||||
{'>', kVK_ANSI_Period}, // Shift+.
|
||||
{'?', kVK_ANSI_Slash}, // Shift+/
|
||||
{'~', kVK_ANSI_Grave} // Shift+`
|
||||
};
|
||||
#endif
|
||||
} // namespace Robot
|
||||
Vendored
+124
@@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#import <Carbon/Carbon.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/keysym.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
|
||||
namespace Robot {
|
||||
|
||||
#ifdef __APPLE__
|
||||
typedef CGKeyCode KeyCode;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef WORD KeyCode;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
typedef KeySym KeyCode;
|
||||
#endif
|
||||
|
||||
class Keyboard {
|
||||
public:
|
||||
enum SpecialKey {
|
||||
BACKSPACE,
|
||||
ENTER,
|
||||
TAB,
|
||||
ESCAPE,
|
||||
UP,
|
||||
DOWN,
|
||||
RIGHT,
|
||||
LEFT,
|
||||
META,
|
||||
ALT,
|
||||
CONTROL,
|
||||
SHIFT,
|
||||
CAPSLOCK,
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12
|
||||
};
|
||||
|
||||
static const char INVALID_ASCII;
|
||||
|
||||
Keyboard() = delete;
|
||||
virtual ~Keyboard() = default;
|
||||
|
||||
static void Type(const std::string& query);
|
||||
|
||||
static void TypeHumanLike(const std::string& query);
|
||||
|
||||
static void Click(char asciiChar);
|
||||
static void Click(SpecialKey specialKey);
|
||||
|
||||
static void HoldStart(char asciiChar);
|
||||
static void HoldStart(SpecialKey specialKey);
|
||||
static void HoldStop(char asciiChar);
|
||||
static void HoldStop(SpecialKey specialKey);
|
||||
|
||||
static void Press(char asciiChar);
|
||||
static void Press(SpecialKey specialKey);
|
||||
|
||||
static void Release(char asciiChar);
|
||||
static void Release(SpecialKey specialKey);
|
||||
|
||||
static char VirtualKeyToAscii(KeyCode virtualKey);
|
||||
static SpecialKey VirtualKeyToSpecialKey(KeyCode virtualKey);
|
||||
|
||||
private:
|
||||
static std::thread keyPressThread;
|
||||
static std::atomic<bool> continueHolding;
|
||||
static std::set<char> heldAsciiChars;
|
||||
static std::set<SpecialKey> heldSpecialKeys;
|
||||
|
||||
static void KeyHoldThread();
|
||||
|
||||
static int delay;
|
||||
|
||||
static KeyCode AsciiToVirtualKey(char asciiChar);
|
||||
|
||||
static KeyCode SpecialKeyToVirtualKey(SpecialKey specialKey);
|
||||
|
||||
static std::map<SpecialKey, KeyCode> specialKeyToVirtualKeyMap;
|
||||
// Platform-specific implementations
|
||||
#ifdef __linux__
|
||||
static Display* display;
|
||||
static Window rootWindow;
|
||||
static void InitializeX11();
|
||||
static void CleanupX11();
|
||||
#endif
|
||||
// note: windows alternative doesn't use map
|
||||
#ifdef __APPLE__
|
||||
static std::map<char, int> asciiToVirtualKeyMap;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Robot
|
||||
Vendored
+375
@@ -0,0 +1,375 @@
|
||||
#include "./Mouse.h"
|
||||
#include "./Utils.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#elif __APPLE__
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif __linux__
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
namespace Robot {
|
||||
|
||||
unsigned int Mouse::delay = 16;
|
||||
bool Mouse::isPressed = false;
|
||||
MouseButton Mouse::pressedButton = MouseButton::LEFT_BUTTON;
|
||||
|
||||
#ifdef __linux__
|
||||
Display* Mouse::display = nullptr;
|
||||
Window Mouse::rootWindow = 0;
|
||||
|
||||
void Mouse::InitializeX11() {
|
||||
if (display == nullptr) {
|
||||
display = XOpenDisplay(nullptr);
|
||||
if (display == nullptr) {
|
||||
throw std::runtime_error("Cannot open X11 display");
|
||||
}
|
||||
rootWindow = DefaultRootWindow(display);
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::CleanupX11() {
|
||||
if (display != nullptr) {
|
||||
XCloseDisplay(display);
|
||||
display = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
POINT Mouse::getCurrentPosition() {
|
||||
POINT winPoint;
|
||||
GetCursorPos(&winPoint);
|
||||
return winPoint;
|
||||
}
|
||||
#elif __APPLE__
|
||||
CGPoint Mouse::getCurrentPosition() {
|
||||
CGEventRef event = CGEventCreate(nullptr);
|
||||
CGPoint cursor = CGEventGetLocation(event);
|
||||
CFRelease(event);
|
||||
return cursor;
|
||||
}
|
||||
#elif __linux__
|
||||
Robot::Point Mouse::getCurrentPosition() {
|
||||
InitializeX11();
|
||||
|
||||
Robot::Point point;
|
||||
Window root_return, child_return;
|
||||
int root_x, root_y;
|
||||
int win_x, win_y;
|
||||
unsigned int mask_return;
|
||||
|
||||
XQueryPointer(display, rootWindow, &root_return, &child_return,
|
||||
&root_x, &root_y, &win_x, &win_y, &mask_return);
|
||||
|
||||
point.x = root_x;
|
||||
point.y = root_y;
|
||||
|
||||
return point;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Mouse::Move(Robot::Point point) {
|
||||
#ifdef _WIN32
|
||||
SetCursorPos(point.x, point.y);
|
||||
#elif __APPLE__
|
||||
CGPoint target = CGPointMake(point.x, point.y);
|
||||
CGEventRef event = CGEventCreateMouseEvent(
|
||||
nullptr,
|
||||
kCGEventMouseMoved,
|
||||
target,
|
||||
kCGMouseButtonLeft
|
||||
);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
CFRelease(event);
|
||||
|
||||
if (Mouse::isPressed) {
|
||||
Mouse::MoveWithButtonPressed(point, Mouse::pressedButton);
|
||||
}
|
||||
#elif __linux__
|
||||
InitializeX11();
|
||||
|
||||
// Move the mouse using XTest
|
||||
XTestFakeMotionEvent(display, -1, point.x, point.y, CurrentTime);
|
||||
XFlush(display);
|
||||
|
||||
if (Mouse::isPressed) {
|
||||
Mouse::MoveWithButtonPressed(point, Mouse::pressedButton);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Robot::Point Mouse::GetPosition() {
|
||||
// TODO: how long exactly should we wait?
|
||||
Robot::delay(16);
|
||||
|
||||
Robot::Point point;
|
||||
#ifdef _WIN32
|
||||
POINT cursor = getCurrentPosition();
|
||||
#elif __APPLE__
|
||||
CGPoint cursor = getCurrentPosition();
|
||||
#elif __linux__
|
||||
Robot::Point cursor = getCurrentPosition();
|
||||
point.x = cursor.x;
|
||||
point.y = cursor.y;
|
||||
#endif
|
||||
point.x = cursor.x;
|
||||
point.y = cursor.y;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
void Mouse::ToggleButton(bool down, MouseButton button, bool doubleClick) {
|
||||
#ifdef _WIN32
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_MOUSE;
|
||||
input.mi.dwFlags =
|
||||
(button == MouseButton::LEFT_BUTTON
|
||||
? (down ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP)
|
||||
: button == MouseButton::RIGHT_BUTTON
|
||||
? (down ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP)
|
||||
: (down ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP));
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
#elif __APPLE__
|
||||
CGPoint currentPosition = getCurrentPosition();
|
||||
|
||||
CGEventType buttonType;
|
||||
switch (button) {
|
||||
case MouseButton::LEFT_BUTTON:
|
||||
buttonType = down ? kCGEventLeftMouseDown : kCGEventLeftMouseUp;
|
||||
break;
|
||||
case MouseButton::RIGHT_BUTTON:
|
||||
buttonType = down ? kCGEventRightMouseDown : kCGEventRightMouseUp;
|
||||
break;
|
||||
case MouseButton::CENTER_BUTTON:
|
||||
buttonType = down ? kCGEventOtherMouseDown : kCGEventOtherMouseUp;
|
||||
break;
|
||||
}
|
||||
|
||||
CGEventRef buttonEvent = CGEventCreateMouseEvent(
|
||||
nullptr,
|
||||
buttonType,
|
||||
currentPosition,
|
||||
(button == MouseButton::CENTER_BUTTON) ? kCGMouseButtonCenter
|
||||
: kCGMouseButtonLeft
|
||||
);
|
||||
|
||||
if (doubleClick) {
|
||||
CGEventSetIntegerValueField(buttonEvent, kCGMouseEventClickState, 2);
|
||||
}
|
||||
|
||||
CGEventPost(kCGHIDEventTap, buttonEvent);
|
||||
CFRelease(buttonEvent);
|
||||
#elif __linux__
|
||||
InitializeX11();
|
||||
|
||||
unsigned int buttonCode;
|
||||
switch (button) {
|
||||
case MouseButton::LEFT_BUTTON:
|
||||
buttonCode = 1;
|
||||
break;
|
||||
case MouseButton::RIGHT_BUTTON:
|
||||
buttonCode = 3;
|
||||
break;
|
||||
case MouseButton::CENTER_BUTTON:
|
||||
buttonCode = 2;
|
||||
break;
|
||||
default:
|
||||
buttonCode = 1;
|
||||
}
|
||||
|
||||
if (down) {
|
||||
XTestFakeButtonEvent(display, buttonCode, True, CurrentTime);
|
||||
} else {
|
||||
XTestFakeButtonEvent(display, buttonCode, False, CurrentTime);
|
||||
}
|
||||
|
||||
XFlush(display);
|
||||
|
||||
// Handle double click
|
||||
if (doubleClick && !down) {
|
||||
Robot::delay(10);
|
||||
XTestFakeButtonEvent(display, buttonCode, True, CurrentTime);
|
||||
XTestFakeButtonEvent(display, buttonCode, False, CurrentTime);
|
||||
XFlush(display);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (down) {
|
||||
Mouse::isPressed = true;
|
||||
Mouse::pressedButton = button;
|
||||
} else {
|
||||
Mouse::isPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::MoveWithButtonPressed(Robot::Point point, MouseButton button) {
|
||||
#ifdef _WIN32
|
||||
// On Windows, just calling Move is enough as it will keep the button state.
|
||||
Mouse::Move(point);
|
||||
#elif __APPLE__
|
||||
CGPoint target = CGPointMake(point.x, point.y);
|
||||
|
||||
CGEventType dragEventType;
|
||||
CGMouseButton cgButton;
|
||||
switch (button) {
|
||||
case MouseButton::LEFT_BUTTON:
|
||||
dragEventType = kCGEventLeftMouseDragged;
|
||||
cgButton = kCGMouseButtonLeft;
|
||||
break;
|
||||
case MouseButton::RIGHT_BUTTON:
|
||||
dragEventType = kCGEventRightMouseDragged;
|
||||
cgButton = kCGMouseButtonRight;
|
||||
break;
|
||||
case MouseButton::CENTER_BUTTON:
|
||||
dragEventType = kCGEventOtherMouseDragged;
|
||||
cgButton = kCGMouseButtonCenter;
|
||||
break;
|
||||
}
|
||||
|
||||
CGEventRef mouseDragEvent =
|
||||
CGEventCreateMouseEvent(nullptr, dragEventType, target, cgButton);
|
||||
CGEventPost(kCGHIDEventTap, mouseDragEvent);
|
||||
CFRelease(mouseDragEvent);
|
||||
#elif __linux__
|
||||
InitializeX11();
|
||||
|
||||
// For dragging, we simulate mouse motion with button pressed
|
||||
unsigned int buttonState = 0;
|
||||
switch (button) {
|
||||
case MouseButton::LEFT_BUTTON:
|
||||
buttonState = Button1Mask;
|
||||
break;
|
||||
case MouseButton::RIGHT_BUTTON:
|
||||
buttonState = Button3Mask;
|
||||
break;
|
||||
case MouseButton::CENTER_BUTTON:
|
||||
buttonState = Button2Mask;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create a motion event with the appropriate button state
|
||||
XTestFakeMotionEvent(display, -1, point.x, point.y, CurrentTime);
|
||||
|
||||
// Ensure the button is held down
|
||||
unsigned int buttonCode;
|
||||
switch (button) {
|
||||
case MouseButton::LEFT_BUTTON:
|
||||
buttonCode = 1;
|
||||
break;
|
||||
case MouseButton::RIGHT_BUTTON:
|
||||
buttonCode = 3;
|
||||
break;
|
||||
case MouseButton::CENTER_BUTTON:
|
||||
buttonCode = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
XFlush(display);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mouse::Click(MouseButton button) {
|
||||
ToggleButton(true, button);
|
||||
Robot::delay(10); // add a little delay
|
||||
ToggleButton(false, button);
|
||||
}
|
||||
|
||||
void Mouse::DoubleClick(MouseButton button) {
|
||||
Click(button);
|
||||
Robot::delay(80);
|
||||
Click(button);
|
||||
}
|
||||
|
||||
void Mouse::ScrollBy(int y, int x) {
|
||||
#ifdef _WIN32
|
||||
INPUT input = {0};
|
||||
input.type = INPUT_MOUSE;
|
||||
|
||||
input.mi.dwFlags = MOUSEEVENTF_WHEEL;
|
||||
input.mi.mouseData = static_cast<DWORD>(WHEEL_DELTA * y);
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
|
||||
input.mi.dwFlags = MOUSEEVENTF_HWHEEL;
|
||||
input.mi.mouseData = static_cast<DWORD>(WHEEL_DELTA * x);
|
||||
SendInput(1, &input, sizeof(INPUT));
|
||||
#elif __APPLE__
|
||||
CGEventRef scrollEvent =
|
||||
CGEventCreateScrollWheelEvent(nullptr, kCGScrollEventUnitPixel, 2, y, x);
|
||||
CGEventPost(kCGHIDEventTap, scrollEvent);
|
||||
CFRelease(scrollEvent);
|
||||
#elif __linux__
|
||||
InitializeX11();
|
||||
|
||||
// Vertical scrolling
|
||||
if (y != 0) {
|
||||
unsigned int buttonCode = (y > 0) ? 4 : 5; // 4=up, 5=down
|
||||
for (int i = 0; i < std::abs(y); i++) {
|
||||
XTestFakeButtonEvent(display, buttonCode, True, CurrentTime);
|
||||
XTestFakeButtonEvent(display, buttonCode, False, CurrentTime);
|
||||
XFlush(display);
|
||||
Robot::delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontal scrolling
|
||||
if (x != 0) {
|
||||
unsigned int buttonCode = (x > 0) ? 7 : 6; // 6=left, 7=right
|
||||
for (int i = 0; i < std::abs(x); i++) {
|
||||
XTestFakeButtonEvent(display, buttonCode, True, CurrentTime);
|
||||
XTestFakeButtonEvent(display, buttonCode, False, CurrentTime);
|
||||
XFlush(display);
|
||||
Robot::delay(10);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mouse::Drag(Robot::Point toPoint) {
|
||||
Robot::Mouse::ToggleButton(true, Robot::MouseButton::LEFT_BUTTON);
|
||||
Robot::delay(10);
|
||||
Mouse::Move(toPoint);
|
||||
Robot::delay(10);
|
||||
Mouse::ToggleButton(false, MouseButton::LEFT_BUTTON);
|
||||
}
|
||||
|
||||
void Mouse::MoveSmooth(Robot::Point point) {
|
||||
Robot::Point currentPosition = GetPosition();
|
||||
|
||||
int dx = point.x - currentPosition.x;
|
||||
int dy = point.y - currentPosition.y;
|
||||
|
||||
int steps = (std::abs(dx) > std::abs(dy)) ? std::abs(dx) : std::abs(dy);
|
||||
|
||||
float deltaX = static_cast<float>(dx) / steps;
|
||||
float deltaY = static_cast<float>(dy) / steps;
|
||||
|
||||
for (int i = 1; i <= steps; i++) {
|
||||
Robot::Point stepPosition;
|
||||
stepPosition.x = currentPosition.x + static_cast<int>(deltaX * i);
|
||||
stepPosition.y = currentPosition.y + static_cast<int>(deltaY * i);
|
||||
|
||||
if (Mouse::isPressed) {
|
||||
MoveWithButtonPressed(stepPosition, Mouse::pressedButton);
|
||||
} else {
|
||||
Move(stepPosition);
|
||||
}
|
||||
|
||||
Robot::delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::DragSmooth(Robot::Point toPoint) {
|
||||
Robot::Mouse::ToggleButton(true, Robot::MouseButton::LEFT_BUTTON);
|
||||
Robot::delay(10);
|
||||
Mouse::MoveSmooth(toPoint);
|
||||
Robot::delay(10);
|
||||
Mouse::ToggleButton(false, MouseButton::LEFT_BUTTON);
|
||||
}
|
||||
|
||||
} // namespace Robot
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#elif __APPLE__
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif __linux__
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#endif
|
||||
namespace Robot {
|
||||
|
||||
enum class MouseButton : uint8_t {
|
||||
LEFT_BUTTON = 0,
|
||||
RIGHT_BUTTON = 1,
|
||||
CENTER_BUTTON = 2
|
||||
};
|
||||
|
||||
class Mouse {
|
||||
public:
|
||||
static unsigned int delay;
|
||||
|
||||
static bool isPressed;
|
||||
static MouseButton pressedButton;
|
||||
|
||||
Mouse() = delete;
|
||||
|
||||
static void Move(Robot::Point point);
|
||||
|
||||
static void MoveSmooth(Robot::Point point);
|
||||
|
||||
static void Drag(Robot::Point toPoint);
|
||||
|
||||
static void DragSmooth(Robot::Point toPoint);
|
||||
|
||||
static Robot::Point GetPosition();
|
||||
|
||||
static void ToggleButton(
|
||||
bool down, MouseButton button, bool doubleClick = false
|
||||
);
|
||||
|
||||
static void Click(MouseButton button);
|
||||
|
||||
static void DoubleClick(MouseButton button);
|
||||
|
||||
static void ScrollBy(int y, int x = 0);
|
||||
|
||||
private:
|
||||
static void MoveWithButtonPressed(Robot::Point point, MouseButton button);
|
||||
// Platform-specific implementations
|
||||
#ifdef __linux__
|
||||
static Display* display;
|
||||
static Window rootWindow;
|
||||
static void InitializeX11();
|
||||
static void CleanupX11();
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
static POINT getCurrentPosition();
|
||||
#elif __APPLE__
|
||||
static CGPoint getCurrentPosition();
|
||||
#elif __linux__
|
||||
static Robot::Point getCurrentPosition();
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
} // namespace Robot
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Created by misaki on 2026/1/25.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
// 包含所有必要头文件
|
||||
#include "Autogui.h"
|
||||
|
||||
// 常用常量
|
||||
namespace AutoGUI {
|
||||
// 鼠标按钮常量
|
||||
const Button LEFT = Button::LEFT;
|
||||
const Button RIGHT = Button::RIGHT;
|
||||
const Button MIDDLE = Button::MIDDLE;
|
||||
|
||||
// 常用键常量(字符串形式,用于press、keyDown等函数)
|
||||
namespace Keys {
|
||||
const std::string BACKSPACE = "backspace";
|
||||
const std::string ENTER = "enter";
|
||||
const std::string TAB = "tab";
|
||||
const std::string ESCAPE = "escape";
|
||||
const std::string ESC = "esc";
|
||||
const std::string UP = "up";
|
||||
const std::string DOWN = "down";
|
||||
const std::string RIGHT = "right";
|
||||
const std::string LEFT = "left";
|
||||
const std::string WIN = "win";
|
||||
const std::string COMMAND = "command";
|
||||
const std::string ALT = "alt";
|
||||
const std::string CTRL = "ctrl";
|
||||
const std::string SHIFT = "shift";
|
||||
const std::string CAPSLOCK = "capslock";
|
||||
const std::string F1 = "f1";
|
||||
const std::string F2 = "f2";
|
||||
const std::string F3 = "f3";
|
||||
const std::string F4 = "f4";
|
||||
const std::string F5 = "f5";
|
||||
const std::string F6 = "f6";
|
||||
const std::string F7 = "f7";
|
||||
const std::string F8 = "f8";
|
||||
const std::string F9 = "f9";
|
||||
const std::string F10 = "f10";
|
||||
const std::string F11 = "f11";
|
||||
const std::string F12 = "f12";
|
||||
const std::string SPACE = "space";
|
||||
}
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
#include "./Utils.h"
|
||||
|
||||
namespace Robot {
|
||||
|
||||
void delay(unsigned int ms) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
}
|
||||
|
||||
} // namespace Robot
|
||||
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
namespace Robot {
|
||||
|
||||
void delay(unsigned int ms);
|
||||
|
||||
namespace KeyUtils {
|
||||
// 检查字符是否需要Shift键
|
||||
inline bool NeedsShift(char c) {
|
||||
// 大写字母需要Shift
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 特殊符号需要Shift
|
||||
const char* shiftChars = "!@#$%^&*()_+{}|:\"<>?~";
|
||||
return (strchr(shiftChars, c) != nullptr);
|
||||
}
|
||||
|
||||
// 获取字符的基础键(物理键位)
|
||||
inline char GetBaseKey(char c) {
|
||||
// 对于字母,返回小写形式
|
||||
if (c >= 'A' && c <= 'Z') {
|
||||
return std::tolower(c);
|
||||
}
|
||||
|
||||
// 对于数字,保持不变
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c;
|
||||
}
|
||||
|
||||
// 对于需要Shift的符号,返回对应的基础键
|
||||
switch (c) {
|
||||
case '!': return '1';
|
||||
case '@': return '2';
|
||||
case '#': return '3';
|
||||
case '$': return '4';
|
||||
case '%': return '5';
|
||||
case '^': return '6';
|
||||
case '&': return '7';
|
||||
case '*': return '8';
|
||||
case '(': return '9';
|
||||
case ')': return '0';
|
||||
case '_': return '-';
|
||||
case '+': return '=';
|
||||
case '{': return '[';
|
||||
case '}': return ']';
|
||||
case '|': return '\\';
|
||||
case ':': return ';';
|
||||
case '"': return '\'';
|
||||
case '<': return ',';
|
||||
case '>': return '.';
|
||||
case '?': return '/';
|
||||
case '~': return '`';
|
||||
default: return c;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查字符是否是有效的ASCII字符
|
||||
inline bool IsValidAscii(char c) {
|
||||
return c >= 32 && c <= 126; // 可打印ASCII字符
|
||||
}
|
||||
|
||||
} // namespace KeyUtils
|
||||
|
||||
} // namespace Robot
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Robot {
|
||||
|
||||
struct Point {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
[[nodiscard]] double Distance(Point target) const {
|
||||
return sqrt(pow(target.x - x, 2) + pow(target.y - y, 2));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Robot
|
||||
Reference in New Issue
Block a user