1

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:

  1. Vector2I is essentially struct with 2 ints: x and y, with some function to add,subtract,etc..
  2. weapon_selector->recoilTable is initialized, its not emtpy! Look at populate_recoil_table() function
  3. 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));
}
xXTurner
  • 61
  • 9
  • Are you sure this part is correct?: `|| !GetAsyncKeyState(VK_RBUTTON)` – ssbssa Apr 08 '21 at 17:27
  • @ssbssa Yes it works just like intended, i dont think this is the problem, i tried to change it like the left button (detect for holding) but the overall outcome is the same – xXTurner Apr 08 '21 at 17:36
  • So there is no change of behavior if you remove that part? Because I think it makes a big difference. – ssbssa Apr 08 '21 at 17:37
  • You should always mask out all bits of the return value of `GetAsyncKeyState` except for the bits you are interested in. According to the official Microsoft documentation, the least significant bit is only provided for backward compatibility and should not be relied upon. Therefore, you are probably only interested in the most significant bit. In that case, you should mask the return value with `& 0x8000`. – Andreas Wenzel Apr 08 '21 at 17:42
  • Even if i mask the value properly this does not answer my main problem. – xXTurner Apr 08 '21 at 18:47
  • You are not setting the `dx` and `dy` members of the [`MOUSEINPUT`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-mouseinput) structure. Also, you may want to check the return value of `SendInput`. – Andreas Wenzel Apr 08 '21 at 19:13
  • Nowhere in the documentation says that dx and dy are required for the mouse click, after all i just want to click not move and click or click on specific coordinates – xXTurner Apr 08 '21 at 21:30
  • @xXTurner: The documentation states that `dx` and `dy` represent absolute or relative values, depending on the value of the `dwFlags` member. Since you did not set the `MOUSEEVENTF_ABSOLUTE` flag, they represent relative values. Therefore, you must set both members to zero if you want the click to not move. – Andreas Wenzel Apr 08 '21 at 22:04
  • Well its not moving in the first place even if i dont set it, also that is not the question/problem i have, but i will initialize it to 0 anyways. – xXTurner Apr 08 '21 at 22:10
  • @xXTurner: If you supply `SendInput` with invalid garbage data, then I wouldn't be surprised if this causes `SendInput` to fail completely. In that case, the mouse position also wouldn't move. If that is not the problem, then I am unable to help you, unless you provide a [mre]. – Andreas Wenzel Apr 08 '21 at 22:34
  • Could you please show [a minimal, reproducible sample](https://stackoverflow.com/help/minimal-reproducible-example) without private information? This can help us identify your problem. – Zeus Apr 09 '21 at 03:00
  • Yes, I added a minimal, reproducible sample. Hope it helps :) – xXTurner Apr 09 '21 at 10:27
  • Fixed my issue, updated the thread, thanks for everyone's help! – xXTurner Apr 09 '21 at 22:12

0 Answers0