diff --git a/i18n/MouseClick_en_US.ts b/i18n/MouseClick_en_US.ts index 6d6df54..fe190c6 100644 --- a/i18n/MouseClick_en_US.ts +++ b/i18n/MouseClick_en_US.ts @@ -79,6 +79,21 @@ Set Max Random Interval Set Max Random Interval + + + Random Click Offset + Random Click Offset + + + + Set Max Random Offset + Set Max Random Offset + + + + px + px + Memory Configuration diff --git a/i18n/MouseClick_zh_CN.ts b/i18n/MouseClick_zh_CN.ts index 9988634..d95f61f 100644 --- a/i18n/MouseClick_zh_CN.ts +++ b/i18n/MouseClick_zh_CN.ts @@ -79,6 +79,21 @@ Set Max Random Interval 最大随机时间 + + + Random Click Offset + 随机点击偏移 + + + + Set Max Random Offset + 最大随机偏移 + + + + px + 像素 + Memory Configuration diff --git a/i18n/MouseClick_zh_TW.ts b/i18n/MouseClick_zh_TW.ts index d18aac1..9451b8f 100644 --- a/i18n/MouseClick_zh_TW.ts +++ b/i18n/MouseClick_zh_TW.ts @@ -79,6 +79,21 @@ Set Max Random Interval 最大隨機時間 + + + Random Click Offset + 隨機點擊偏移 + + + + Set Max Random Offset + 最大隨機偏移 + + + + px + 像素 + Memory Configuration diff --git a/src/modules/nav_pages/mouseclickpage.cpp b/src/modules/nav_pages/mouseclickpage.cpp index 809b197..93b7095 100644 --- a/src/modules/nav_pages/mouseclickpage.cpp +++ b/src/modules/nav_pages/mouseclickpage.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "../../utils/clicker.h" @@ -153,7 +154,11 @@ MouseClickPage::MouseClickPage(const QString& title, SettingsPage& settings_page random_interval_time->setMinimum(0.02); random_interval_time->setSingleStep(0.01); random_interval_time->setValue(app_settings.RandomIntervalTime()); - random_interval_time->setEnabled(false); + random_interval_time->setEnabled(random_interval_toggle_btn->isChecked()); + + if (random_interval_toggle_btn->isChecked()) { + interval_time->setEnabled(false); + } random_interval_time_content_layout->addWidget(random_interval_time_desc); random_interval_time_content_layout->addWidget(random_interval_time); @@ -161,6 +166,61 @@ MouseClickPage::MouseClickPage(const QString& title, SettingsPage& settings_page /********************/ + QWidget* random_offset_toggle_content = new QWidget(page_content); + random_offset_toggle_content->setFixedHeight(pageContentUniformHeight); + + QHBoxLayout* random_offset_toggle_content_layout = new QHBoxLayout(random_offset_toggle_content); + random_offset_toggle_content_layout->setSpacing(0); + random_offset_toggle_content_layout->setContentsMargins(QMargins()); + + QLabel* random_offset_toggle_desc = new QLabel(random_offset_toggle_content); + random_offset_toggle_desc->setObjectName(QStringLiteral("random-offset-toggle-desc")); + random_offset_toggle_desc->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + random_offset_toggle_desc->setFocusPolicy(Qt::NoFocus); + random_offset_toggle_desc->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + random_offset_toggle_desc->setText(tr("Random Click Offset")); + + QRadioButton* random_offset_toggle_btn = new QRadioButton(random_offset_toggle_content); + random_offset_toggle_btn->setObjectName(QStringLiteral("random-offset-toggle-btn")); + random_offset_toggle_btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + random_offset_toggle_btn->setChecked(app_settings.EnableRandomOffset()); + + random_offset_toggle_content_layout->addWidget(random_offset_toggle_desc); + random_offset_toggle_content_layout->addWidget(random_offset_toggle_btn); + random_offset_toggle_content->setLayout(random_offset_toggle_content_layout); + + /********************/ + + QWidget* random_offset_distance_content = new QWidget(page_content); + random_offset_distance_content->setFixedHeight(pageContentUniformHeight); + + QHBoxLayout* random_offset_distance_content_layout = new QHBoxLayout(random_offset_distance_content); + random_offset_distance_content_layout->setSpacing(0); + random_offset_distance_content_layout->setContentsMargins(QMargins()); + + QLabel* random_offset_distance_desc = new QLabel(random_offset_distance_content); + random_offset_distance_desc->setObjectName(QStringLiteral("random-offset-distance-desc")); + random_offset_distance_desc->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + random_offset_distance_desc->setFocusPolicy(Qt::NoFocus); + random_offset_distance_desc->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + random_offset_distance_desc->setText(tr("Set Max Random Offset")); + + QSpinBox* random_offset_distance = new QSpinBox(random_offset_distance_content); + random_offset_distance->setObjectName(QStringLiteral("random-offset-distance")); + random_offset_distance->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + random_offset_distance->setSuffix(tr(" px")); + random_offset_distance->setMaximum(1000); + random_offset_distance->setMinimum(0); + random_offset_distance->setSingleStep(1); + random_offset_distance->setValue(app_settings.RandomOffsetDistance()); + random_offset_distance->setEnabled(random_offset_toggle_btn->isChecked()); + + random_offset_distance_content_layout->addWidget(random_offset_distance_desc); + random_offset_distance_content_layout->addWidget(random_offset_distance); + random_offset_distance_content->setLayout(random_offset_distance_content_layout); + + /********************/ + QWidget* memory_configuration_toggle_content = new QWidget(page_content); memory_configuration_toggle_content->setFixedHeight(pageContentUniformHeight); @@ -190,6 +250,8 @@ MouseClickPage::MouseClickPage(const QString& title, SettingsPage& settings_page page_content_layout->addWidget(interval_time_content); page_content_layout->addWidget(random_interval_toggle_content); page_content_layout->addWidget(random_interval_time_content); + page_content_layout->addWidget(random_offset_toggle_content); + page_content_layout->addWidget(random_offset_distance_content); page_content_layout->addWidget(memory_configuration_toggle_content); page_content_layout->addStretch(); @@ -210,6 +272,12 @@ MouseClickPage::MouseClickPage(const QString& title, SettingsPage& settings_page }); connect(random_interval_time, &QDoubleSpinBox::valueChanged, &SettingsAgent::instance(), &SettingsAgent::setRandomIntervalTime); + connect(random_offset_toggle_btn, &QRadioButton::toggled, random_offset_distance, [random_offset_distance](bool checked) { + random_offset_distance->setEnabled(checked); + SettingsAgent::instance().setEnableRandomOffset(checked); + }); + + connect(random_offset_distance, &QSpinBox::valueChanged, &SettingsAgent::instance(), &SettingsAgent::setRandomOffsetDistance); connect(memory_configuration_toggle_btn, &QRadioButton::toggled, &SettingsAgent::instance(), &SettingsAgent::setEnableMemoryConfiguration); // hotkey event @@ -223,6 +291,8 @@ MouseClickPage::MouseClickPage(const QString& title, SettingsPage& settings_page interval_time->setEnabled(!random_interval_toggle_btn->isChecked()); random_interval_toggle_btn->setEnabled(true); random_interval_time->setEnabled(random_interval_toggle_btn->isChecked()); + random_offset_toggle_btn->setEnabled(true); + random_offset_distance->setEnabled(random_offset_toggle_btn->isChecked()); memory_configuration_toggle_btn->setEnabled(true); } else { Qt::MouseButton btn_type; @@ -239,14 +309,19 @@ MouseClickPage::MouseClickPage(const QString& title, SettingsPage& settings_page int interval = static_cast(interval_time->value() * 1000); // 转为毫秒值 bool random_interval_flag = random_interval_toggle_btn->isChecked(); int max_random_interval = static_cast(random_interval_time->value() * 1000); // 转为毫秒值 + bool random_offset_flag = random_offset_toggle_btn->isChecked(); + int max_random_offset = random_offset_distance->value(); // 像素值 - NavPage::_clicker->initParameters(btn_type, interval, random_interval_flag, max_random_interval); + NavPage::_clicker->initParameters(btn_type, interval, random_interval_flag, max_random_interval, + random_offset_flag, max_random_offset); NavPage::_clicker_thread->start(); // Note: This should be initiated through a sub-thread. click_type_list->setEnabled(false); interval_time->setEnabled(false); random_interval_toggle_btn->setEnabled(false); random_interval_time->setEnabled(false); + random_offset_toggle_btn->setEnabled(false); + random_offset_distance->setEnabled(false); memory_configuration_toggle_btn->setEnabled(false); } }); diff --git a/src/modules/settingsagent.cpp b/src/modules/settingsagent.cpp index 557f0a1..a73d764 100644 --- a/src/modules/settingsagent.cpp +++ b/src/modules/settingsagent.cpp @@ -102,6 +102,26 @@ void SettingsAgent::setRandomIntervalTime(const double random_interval_time) _config["RandomIntervalTime"] = random_interval_time; } +bool SettingsAgent::EnableRandomOffset() const +{ + return _config["EnableRandomOffset"].toBool(); +} + +void SettingsAgent::setEnableRandomOffset(bool enable_random_offset) +{ + _config["EnableRandomOffset"] = enable_random_offset; +} + +int SettingsAgent::RandomOffsetDistance() const +{ + return _config["RandomOffsetDistance"].toInt(); +} + +void SettingsAgent::setRandomOffsetDistance(const int random_offset_distance) +{ + _config["RandomOffsetDistance"] = random_offset_distance; +} + bool SettingsAgent::EnableMemoryConfiguration() const { return _config["EnableMemoryConfiguration"].toBool(); @@ -159,6 +179,8 @@ SettingsAgent::SettingsAgent(QObject *parent) _config["IntervalTime"] = settings.value("IntervalTime", _DEFAULT_INTERVALTIME); _config["EnableRandomInterval"] = settings.value("EnableRandomInterval", _DEFAULT_RANDOMINTERVAL); _config["RandomIntervalTime"] = settings.value("RandomIntervalTime", _DEFAULT_RANDOMINTERVALTIME); + _config["EnableRandomOffset"] = settings.value("EnableRandomOffset", _DEFAULT_RANDOMOFFSET); + _config["RandomOffsetDistance"] = settings.value("RandomOffsetDistance", _DEFAULT_RANDOMOFFSETDISTANCE); _config["EnableMemoryConfiguration"] = settings.value("EnableMemoryConfiguration", _DEFAULT_MEMORYCONFIGURATION); settings.endGroup(); @@ -187,6 +209,12 @@ SettingsAgent::SettingsAgent(QObject *parent) if (!_config["RandomIntervalTime"].isValid() || _config["RandomIntervalTime"].metaType().id() == QMetaType::UnknownType) { _config["RandomIntervalTime"] = _DEFAULT_RANDOMINTERVALTIME; } + if (!_config["EnableRandomOffset"].isValid() || _config["EnableRandomOffset"].metaType().id() == QMetaType::UnknownType) { + _config["EnableRandomOffset"] = _DEFAULT_RANDOMOFFSET; + } + if (!_config["RandomOffsetDistance"].isValid() || _config["RandomOffsetDistance"].metaType().id() == QMetaType::UnknownType) { + _config["RandomOffsetDistance"] = _DEFAULT_RANDOMOFFSETDISTANCE; + } if (!_config["EnableMemoryConfiguration"].isValid() || _config["EnableMemoryConfiguration"].metaType().id() == QMetaType::UnknownType) { _config["EnableMemoryConfiguration"] = _DEFAULT_MEMORYCONFIGURATION; } @@ -214,6 +242,8 @@ SettingsAgent::SettingsAgent(QObject *parent) _config["IntervalTime"] = _DEFAULT_INTERVALTIME; _config["EnableRandomInterval"] = _DEFAULT_RANDOMINTERVAL; _config["RandomIntervalTime"] = _DEFAULT_RANDOMINTERVALTIME; + _config["EnableRandomOffset"] = _DEFAULT_RANDOMOFFSET; + _config["RandomOffsetDistance"] = _DEFAULT_RANDOMOFFSETDISTANCE; } @@ -245,6 +275,8 @@ SettingsAgent::~SettingsAgent() settings.setValue("IntervalTime", _config["IntervalTime"]); settings.setValue("EnableRandomInterval", _config["EnableRandomInterval"]); settings.setValue("RandomIntervalTime", _config["RandomIntervalTime"]); + settings.setValue("EnableRandomOffset", _config["EnableRandomOffset"]); + settings.setValue("RandomOffsetDistance", _config["RandomOffsetDistance"]); settings.setValue("EnableMemoryConfiguration", _config["EnableMemoryConfiguration"]); settings.endGroup(); } diff --git a/src/modules/settingsagent.h b/src/modules/settingsagent.h index 07902f1..9efa00f 100644 --- a/src/modules/settingsagent.h +++ b/src/modules/settingsagent.h @@ -41,6 +41,12 @@ class SettingsAgent : public QObject double RandomIntervalTime() const; void setRandomIntervalTime(const double random_interval_time); + bool EnableRandomOffset() const; + void setEnableRandomOffset(bool enable_random_offset); + + int RandomOffsetDistance() const; + void setRandomOffsetDistance(const int random_offset_distance); + bool EnableMemoryConfiguration() const; void setEnableMemoryConfiguration(bool memory_configuration); /***********************************/ @@ -76,6 +82,8 @@ class SettingsAgent : public QObject const double _DEFAULT_INTERVALTIME = 0.01; const bool _DEFAULT_RANDOMINTERVAL = false; const double _DEFAULT_RANDOMINTERVALTIME = 0.02; + const bool _DEFAULT_RANDOMOFFSET = false; + const int _DEFAULT_RANDOMOFFSETDISTANCE = 0; const bool _DEFAULT_MEMORYCONFIGURATION = false; }; diff --git a/src/utils/clicker.cpp b/src/utils/clicker.cpp index 51828d1..2fcc458 100644 --- a/src/utils/clicker.cpp +++ b/src/utils/clicker.cpp @@ -13,7 +13,9 @@ Clicker::Clicker(QObject* parent) _btn_type(Qt::LeftButton), _interval(10), _random_interval_flag(false), - _max_random_interval(20) + _max_random_interval(20), + _random_offset_flag(false), + _max_random_offset(0) {} Clicker::~Clicker() @@ -23,12 +25,16 @@ Clicker::~Clicker() Clicker& Clicker::initParameters(Qt::MouseButton btnType, int interval, bool randomIntervalFlag, - int maxRandomInterval) + int maxRandomInterval, + bool randomOffsetFlag, + int maxRandomOffset) { _btn_type = btnType; _interval = interval; _random_interval_flag = randomIntervalFlag; _max_random_interval = maxRandomInterval; + _random_offset_flag = randomOffsetFlag; + _max_random_offset = maxRandomOffset; return (*this); } @@ -86,13 +92,50 @@ void Clicker::msleep(int ms) } #if defined(Q_OS_WIN) +POINT Clicker::randomClickPosition(const POINT& base_pos) const +{ + if (!_random_offset_flag || _max_random_offset <= 0) { + return base_pos; + } + + int dx = 0; + int dy = 0; + const int max_offset_squared = _max_random_offset * _max_random_offset; + do { + dx = QRandomGenerator::global()->bounded(-_max_random_offset, _max_random_offset + 1); + dy = QRandomGenerator::global()->bounded(-_max_random_offset, _max_random_offset + 1); + } while (dx * dx + dy * dy > max_offset_squared); + + POINT click_pos {base_pos.x + dx, base_pos.y + dy}; + + const int min_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + const int min_y = GetSystemMetrics(SM_YVIRTUALSCREEN); + const int max_x = min_x + GetSystemMetrics(SM_CXVIRTUALSCREEN) - 1; + const int max_y = min_y + GetSystemMetrics(SM_CYVIRTUALSCREEN) - 1; + + click_pos.x = qBound(min_x, click_pos.x, max_x); + click_pos.y = qBound(min_y, click_pos.y, max_y); + + return click_pos; +} + +void Clicker::clickAtPosition(DWORD down_event, DWORD up_event, const POINT& base_pos, const POINT& click_pos) const +{ + SetCursorPos(click_pos.x, click_pos.y); + mouse_event(down_event | up_event, click_pos.x, click_pos.y, 0, 0); + + if (click_pos.x != base_pos.x || click_pos.y != base_pos.y) { + SetCursorPos(base_pos.x, base_pos.y); + } +} + void Clicker::leftClick() { POINT mouse_pos; while (_run) { GetCursorPos(&mouse_pos); - mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, mouse_pos.x, mouse_pos.y, 0, 0); + clickAtPosition(MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, mouse_pos, randomClickPosition(mouse_pos)); msleep(_interval); } } @@ -103,7 +146,7 @@ void Clicker::rightClick() while (_run) { GetCursorPos(&mouse_pos); - mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, mouse_pos.x, mouse_pos.y, 0, 0); + clickAtPosition(MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, mouse_pos, randomClickPosition(mouse_pos)); msleep(_interval); } } @@ -114,7 +157,7 @@ void Clicker::middleClick() while (_run) { GetCursorPos(&mouse_pos); - mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, mouse_pos.x, mouse_pos.y, 0, 0); + clickAtPosition(MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, mouse_pos, randomClickPosition(mouse_pos)); msleep(_interval); } } @@ -125,7 +168,7 @@ void Clicker::leftRandomClick() while (_run) { GetCursorPos(&mouse_pos); - mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, mouse_pos.x, mouse_pos.y, 0, 0); + clickAtPosition(MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, mouse_pos, randomClickPosition(mouse_pos)); msleep(QRandomGenerator::global()->bounded(0, _max_random_interval)); } } @@ -136,7 +179,7 @@ void Clicker::rightRandomClick() while (_run) { GetCursorPos(&mouse_pos); - mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, mouse_pos.x, mouse_pos.y, 0, 0); + clickAtPosition(MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, mouse_pos, randomClickPosition(mouse_pos)); msleep(QRandomGenerator::global()->bounded(0, _max_random_interval)); } } @@ -147,7 +190,7 @@ void Clicker::middleRandomClick() while (_run) { GetCursorPos(&mouse_pos); - mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, mouse_pos.x, mouse_pos.y, 0, 0); + clickAtPosition(MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, mouse_pos, randomClickPosition(mouse_pos)); msleep(QRandomGenerator::global()->bounded(0, _max_random_interval)); } } diff --git a/src/utils/clicker.h b/src/utils/clicker.h index fb270db..cbfa715 100644 --- a/src/utils/clicker.h +++ b/src/utils/clicker.h @@ -5,6 +5,10 @@ #include #include +#if defined(Q_OS_WIN) +#include +#endif + class Clicker : public QObject { Q_OBJECT @@ -14,7 +18,9 @@ class Clicker : public QObject Clicker& initParameters(Qt::MouseButton btnType, int interval, bool randomIntervalFlag, - int maxRandomInterval); + int maxRandomInterval, + bool randomOffsetFlag, + int maxRandomOffset); public Q_SLOTS: void start(); @@ -26,6 +32,8 @@ public Q_SLOTS: int _interval; bool _random_interval_flag; int _max_random_interval; + bool _random_offset_flag; + int _max_random_offset; QMutex _interrupt_sleep_immediately; QWaitCondition _sleep_timer; @@ -38,6 +46,11 @@ public Q_SLOTS: void leftRandomClick(); void rightRandomClick(); void middleRandomClick(); + +#if defined(Q_OS_WIN) + POINT randomClickPosition(const POINT& base_pos) const; + void clickAtPosition(DWORD down_event, DWORD up_event, const POINT& base_pos, const POINT& click_pos) const; +#endif }; #endif // CLICKER_H