Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor interop to use CsWin32/LibraryImport #49

Merged
merged 13 commits into from
Jan 9, 2025
5 changes: 2 additions & 3 deletions FluentAutoClicker/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace FluentAutoClicker;
/// </summary>
public partial class App : Application
{
public static Window Window { get; private set; } = null!;
public static readonly MainWindow MainWindow = new();

/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
Expand All @@ -44,7 +44,6 @@ public App()
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
Window = new MainWindow();
Window.Activate();
MainWindow.Activate();
}
}
5 changes: 5 additions & 0 deletions FluentAutoClicker/FluentAutoClicker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseWinUI>true</UseWinUI>
<EnableMsixTooling>true</EnableMsixTooling>
<DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
Expand All @@ -37,6 +38,10 @@
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.106">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.241114003" />
<PackageReference Include="WinUIEx" Version="2.5.0" />
Expand Down
130 changes: 51 additions & 79 deletions FluentAutoClicker/Helpers/AutoClicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// along with Fluent Auto Clicker. If not, see <https://www.gnu.org/licenses/>.

using System.Runtime.InteropServices;
using Windows.Win32;
using Windows.Win32.UI.Input.KeyboardAndMouse;

namespace FluentAutoClicker.Helpers;

Expand All @@ -24,9 +26,6 @@ namespace FluentAutoClicker.Helpers;
/// </summary>
public static class AutoClicker
{
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, Input[] pInputs, int cbSize);

private static Thread? _autoClickerThread;
private static bool _isAutoClickerRunning;

Expand All @@ -37,114 +36,87 @@ public static class AutoClicker
/// <param name="clickAmount">The number of clicks before stopping the auto clicker thread.</param>
/// <param name="mouseButtonType">The mouse button used to click.</param>
/// <param name="clickDelayOffset">The amount of time in milliseconds to add randomly to the millisecond delay between clicks.</param>
public static void StartAutoClicker(int millisecondsDelay, int clickAmount, int mouseButtonType, int clickDelayOffset)
public static void Start(int millisecondsDelay = 100, int clickAmount = 0,
int mouseButtonType = 0, int clickDelayOffset = 0)
{
// TODO: Evaluate whether a thread is necessary for this.
// TODO: Move the parameters to another function to be able to change parameters while the thread is running.
_isAutoClickerRunning = true;
_autoClickerThread = new Thread(() => AutoClickerThread(millisecondsDelay, clickAmount, mouseButtonType, clickDelayOffset));
_autoClickerThread = new Thread(() =>
AutoClickerThread(millisecondsDelay, clickAmount, mouseButtonType, clickDelayOffset));
_autoClickerThread.Start();
}

/// <summary>
/// Stops the auto clicker thread.
/// </summary>
public static void StopAutoClicker()
public static void Stop()
{
_isAutoClickerRunning = false;
// HACK: Incorrectly stops the thread, but it works for now.
_autoClickerThread?.Join();
}

private static async void AutoClickerThread(int clickInterval, int repeatAmount, int mouseButton, int clickOffset)
private static async void AutoClickerThread(int clickInterval, int repeatAmount, int mouseButton,
int clickOffset)
{
int clickCount = 0;
Random random = new();

while (_isAutoClickerRunning)
{
// Stop if we click more than repeat amount
if (clickCount >= repeatAmount && repeatAmount != 0)
{
StopAutoClicker();
Stop();
break;
}

// TODO: Move this to a enum instead of a number
switch (mouseButton)
{
case 0:
MouseEvent(0, 0, (uint)MouseEventF.LeftDown, 0, 0, IntPtr.Zero);
MouseEvent(0, 0, (uint)MouseEventF.LeftUp, 0, 0, IntPtr.Zero);
break;
case 1:
MouseEvent(0, 0, (uint)MouseEventF.MiddleDown, 0, 0, IntPtr.Zero);
MouseEvent(0, 0, (uint)MouseEventF.MiddleUp, 0, 0, IntPtr.Zero);
break;
case 2:
MouseEvent(0, 0, (uint)MouseEventF.RightDown, 0, 0, IntPtr.Zero);
MouseEvent(0, 0, (uint)MouseEventF.RightUp, 0, 0, IntPtr.Zero);
break;
}

if (repeatAmount > 0)
{
clickCount++;
}
// Click mouse and increment click count
ClickMouse(mouseButton);
clickCount++;

int randomClickOffset = random.Next(0, clickOffset);
// Delay before next click
int randomClickOffset = new Random().Next(0, clickOffset);
await Task.Delay(clickInterval + randomClickOffset);
}
}

private static void MouseEvent(int dx, int dy, uint dwFlags, uint dwData, uint time, nint dwExtraInfo)
/// <summary>
/// Clicks the mouse button.
/// </summary>
/// <param name="button">The mouse button to click.</param>
private static void ClickMouse(int button)
{
Input[] inputs = new Input[2];
inputs[0] = MouseInput(dx, dy, dwData, dwFlags, time, dwExtraInfo);
inputs[1] = MouseInput(dx, dy, dwData, dwFlags, time, dwExtraInfo);
_ = SendInput((uint)inputs.Length, inputs, Marshal.SizeOf<Input>());
if (button == 0) // Left mouse button
{
SendMouseInput(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN);
SendMouseInput(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP);
}
else if (button == 1) // Middle mouse button
{
SendMouseInput(MOUSE_EVENT_FLAGS.MOUSEEVENTF_MIDDLEDOWN);
SendMouseInput(MOUSE_EVENT_FLAGS.MOUSEEVENTF_MIDDLEUP);
}
else if (button == 2) // Right mouse button
{
SendMouseInput(MOUSE_EVENT_FLAGS.MOUSEEVENTF_RIGHTDOWN);
SendMouseInput(MOUSE_EVENT_FLAGS.MOUSEEVENTF_RIGHTUP);
}
}

private static Input MouseInput(int dx, int dy, uint mouseData, uint dwFlags, uint time, nint dwExtraInfo)
/// <summary>
/// Sends a mouse input event.
/// </summary>
/// <param name="dwFlags">The mouse event flags that specify the type of mouse event.</param>
private static void SendMouseInput(MOUSE_EVENT_FLAGS dwFlags)
{
return new Input
{
type = 0,
mi = new InputMouse
INPUT[] inputs =
[
new()
{
dx = dx,
dy = dy,
mouseData = mouseData,
dwFlags = dwFlags,
time = time,
dwExtraInfo = dwExtraInfo
type = INPUT_TYPE.INPUT_MOUSE,
Anonymous = new INPUT._Anonymous_e__Union { mi = new MOUSEINPUT { dwFlags = dwFlags } }
}
};
}
];

[StructLayout(LayoutKind.Sequential)]
private struct Input
{
public int type;
public InputMouse mi;
}

[StructLayout(LayoutKind.Sequential)]
private struct InputMouse
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}

[Flags]
private enum MouseEventF : uint
{
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040
_ = PInvoke.SendInput(inputs, Marshal.SizeOf<INPUT>());
}
}
}
148 changes: 0 additions & 148 deletions FluentAutoClicker/Helpers/WindowMessageHook.cs

This file was deleted.

6 changes: 2 additions & 4 deletions FluentAutoClicker/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@
<CheckBox
x:Name="ClickRepeatCheckBox"
x:Uid="ClickRepeatCheckBox"
Checked="ClickRepeatCheckBox_Checked"
Unchecked="ClickRepeatCheckBox_Unchecked" />
Click="CheckBox_Click" />
<NumberBox
x:Name="ClickRepeatAmount"
HorizontalAlignment="Left"
Expand All @@ -121,8 +120,7 @@
<CheckBox
x:Name="ClickOffsetCheckBox"
x:Uid="ClickOffsetCheckBox"
Checked="ClickOffsetCheckBox_Checked"
Unchecked="ClickOffsetCheckBox_Unchecked" />
Click="CheckBox_Click" />
<NumberBox
x:Name="ClickOffsetAmount"
HorizontalAlignment="Left"
Expand Down
Loading
Loading