So I want to make when you hold down your left click, the mouse starts to click at regular intervals.
In theory all should work right, but in practice, not so well.
Anyways here is the code I am using for the detection and clicking of the mouse:
while (1)
{
if (GetAsyncKeyState(VK_LBUTTON) && GetAsyncKeyState(VK_RBUTTON))
{
if (weapon_selector == nullptr) continue;
if (weapon_selector->isSemiAutomatic)
{
for (Vector2I x : weapon_selector->recoilTable)
{
// Detect for hardware click instead
if (!(GetAsyncKeyState(VK_LBUTTON) & 0x8000) || !GetAsyncKeyState(VK_RBUTTON)) // This right here should detect if you are not holding either of the left or right mouse buttons, and thus abort the movement/clicks of the mouse
{
Utilities::SleepThread(200);
break;
}
Mouse::Click(); // Look in the code below how this is defined
Mouse::MoveRelative_Lerp(x.x, x.y, (int)weapon_selector->repeatDelay); // This is essentially doing this_thread::sleep_for() for around 150 ms every time after the click has happened, which would give plenty of time for the GetAsyncKeyState() function to "update" or whatever if it needed it. This function only moves the mouse, it does not click anywhere!
}
Utilities::SleepThread(200);
}
else
{
...
}
}
Utilities::SleepThread(1);
}
Here is my implementation for the Mouse::Click() function:
void Mouse::Click()
{
INPUT input;
input.type = INPUT_MOUSE;
input.mi.mouseData = NULL;
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
input.mi.time = 0;
input.mi.dwExtraInfo = NULL;
SendInput(1, &input, sizeof(input));
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1, &input, sizeof(input));
}
I tried pretty much everything I could think of to fix this with no success, like Mouse low level hook, also checking the injected flag in low level mouse hook, Detecting hardware mouse clicking with RegisterRawInputDevices() (with no success either), setting virtualClick variables and all kinds of different checks, no luck.. im tired trying to do this for the past 2 days
I would appreciate it a lot if someone has dealt with this before and can help me.
Minimal reproducible example:
main.cpp
#include <Windows.h>
#include <hidusage.h>
#include <iostream>
#include <cmath>
#include <string>
#include <thread>
#include <atomic>
#include <vector>
#include <fstream>
#include "Mouse.hpp"
#include "Utils.hpp"
#include "Vector2_int.hpp"
struct WeaponData
{
const char* szName;
std::vector<Vector2I> recoilTable;
int repeatDelay;
int magCapacity;
bool isSemiAutomatic;
};
#pragma region Recoil Tables
WeaponData RecoilTables[] = {
{"Revolver", {}, 175, 8, true},
{"SAR", {}, 175, 16, true},
{"SAP (P2000)", {}, 150, 10, true},
{"Python", {}, 150, 6, true},
{"Thompson", {}, 130, 20, false},
{"Custom SMG", {}, 100, 24, false},
{"AK", {}, 133, 30, false},
{"MP5", {}, 100, 30, false},
{"LR300", {}, 120, 30, false},
{"M92", {}, 150, 15, true},
{"M249", {}, 120, 100, false},
{"M39", {}, 200, 20, true},
};
#pragma endregion
void populate_recoil_table(WeaponData* data)
{
if (data->recoilTable.size() > 0) return;
for (int i = 0; i < data->magCapacity; i++)
{
data->recoilTable.push_back({0, 0});
}
}
int main()
{
WeaponData* weapon_selector = &RecoilTables[0];
populate_recoil_table(weapon_selector);
// just to see the movement of the mouse
weapon_selector->recoilTable.at(0).x = 20;
weapon_selector->recoilTable.at(0).y = 20;
while (1)
{
if (GetAsyncKeyState(VK_LBUTTON) && GetAsyncKeyState(VK_RBUTTON))
{
if (weapon_selector == nullptr) continue;
if (weapon_selector->isSemiAutomatic)
{
for (Vector2I x : weapon_selector->recoilTable)
{
if (!(GetAsyncKeyState(VK_LBUTTON) & 0x8000) || !(GetAsyncKeyState(VK_RBUTTON) & 0x8000))
{
puts("stopped bcs no buttons hold");
Utilities::SleepThread(200);
break;
}
puts("clicking");
Mouse::Click();
Utilities::SleepThread(weapon_selector->repeatDelay);
}
Utilities::SleepThread(200);
}
else
{
for (Vector2I x : weapon_selector->recoilTable)
{
if (!GetAsyncKeyState(VK_LBUTTON) || !GetAsyncKeyState(VK_RBUTTON)) { Utilities::SleepThread(200); break; }
Mouse::MoveRelative_Lerp(x.x, x.y, (int)weapon_selector->repeatDelay);
}
Utilities::SleepThread(200);
}
}
Utilities::SleepThread(1);
}
}
Mouse::Click() function
void Mouse::Click()
{
INPUT input;
input.type = INPUT_MOUSE;
input.mi.mouseData = NULL;
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
input.mi.dx = 0;
input.mi.dy = 0;
input.mi.time = 0;
input.mi.dwExtraInfo = NULL;
SendInput(1, &input, sizeof(input)); // First send UP event to "unclick" our mouse button
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN; // Then send DOWN event to click and hold the button
SendInput(1, &input, sizeof(input));
input.mi.dwFlags = MOUSEEVENTF_LEFTUP; // Then send UP again to release it to simulate a click
SendInput(1, &input, sizeof(input));
}
Utilities::SleepThread() function
void SleepThread(uint32_t milliseconds)
{
std::chrono::high_resolution_clock::time_point target = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(milliseconds);
while (std::chrono::high_resolution_clock::now() < target)
{
std::this_thread::yield();
}
}
Things to note for the example:
- Vector2I is essentially struct with 2 ints: x and y, with some function to add,subtract,etc..
- weapon_selector->recoilTable is initialized, its not emtpy! Look at populate_recoil_table() function
- The code is compiled with C++17 (MSVC compiler/Visual Studio 2019)
EDIT: I fixed my issue
I had to first "unpress" my button then send DOWN message again, idk why i didnt think of it earlier, the only downside is that the last message before the function ends is DOWN, so the mouse is "stuck" for a little bit (cannot be noticed) but it updates straight away if your physical button is not pressed.
Fixed click function:
void Mouse::Click()
{
INPUT input;
input.type = INPUT_MOUSE;
input.mi.mouseData = NULL;
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
input.mi.dx = 0;
input.mi.dy = 0;
input.mi.time = 0;
input.mi.dwExtraInfo = NULL;
SendInput(1, &input, sizeof(input));
Utilities::SleepThread(10); // The application im targeting doesnt get the update if its instant mouse press, thus waiting a little
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &input, sizeof(input));
}