scanner/AccessibilityClickHelper.cs
2026-07-29 21:03:59 +03:00

190 lines
6.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
internal static class NativeMethods
{
internal const int WM_HOTKEY = 0x0312;
internal const int HOTKEY_ID = 1;
internal const uint KEYEVENTF_KEYUP = 0x0002;
internal const byte VK_CONTROL = 0x11;
internal const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
internal const uint MOUSEEVENTF_LEFTUP = 0x0004;
[DllImport("user32.dll")]
internal static extern bool RegisterHotKey(IntPtr hWnd, int id, uint modifiers, uint key);
[DllImport("user32.dll")]
internal static extern bool UnregisterHotKey(IntPtr hWnd, int id);
[DllImport("user32.dll")]
internal static extern void keybd_event(byte virtualKey, byte scanCode, uint flags, UIntPtr extraInfo);
[DllImport("user32.dll")]
internal static extern void mouse_event(uint flags, uint dx, uint dy, uint data, UIntPtr extraInfo);
}
internal sealed class HelperForm : Form
{
private const int DwellMilliseconds = 100;
private const int MovementThreshold = 6;
private readonly Timer timer;
private readonly Button toggleButton;
private readonly Label statusLabel;
private Point anchor;
private DateTime stableSince;
private DateTime cooldownUntil;
private bool enabled;
private bool clickLocked;
internal HelperForm()
{
Text = "PoE2 Accessibility Click Helper";
ClientSize = new Size(500, 250);
MinimumSize = new Size(500, 250);
StartPosition = FormStartPosition.CenterScreen;
TopMost = true;
BackColor = Color.FromArgb(15, 15, 24);
ForeColor = Color.White;
Font = new Font("Segoe UI", 12F);
var title = new Label {
Text = "Помощник Ctrl + Left Click",
Font = new Font("Segoe UI", 18F, FontStyle.Bold),
ForeColor = Color.FromArgb(220, 190, 100),
AutoSize = false,
TextAlign = ContentAlignment.MiddleCenter,
Bounds = new Rectangle(15, 15, 470, 45)
};
toggleButton = new Button {
Text = "ВКЛЮЧИТЬ (F8)",
Font = new Font("Segoe UI", 18F, FontStyle.Bold),
BackColor = Color.FromArgb(40, 110, 65),
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat,
Bounds = new Rectangle(60, 70, 380, 70)
};
toggleButton.Click += delegate { Toggle(); };
statusLabel = new Label {
Text = "ВЫКЛЮЧЕНО — кликов не будет",
Font = new Font("Segoe UI", 12F, FontStyle.Bold),
ForeColor = Color.FromArgb(255, 100, 100),
AutoSize = false,
TextAlign = ContentAlignment.MiddleCenter,
Bounds = new Rectangle(20, 148, 460, 35)
};
var help = new Label {
Text = "Наведи курсор и удерживай 0,1с. Один Ctrl+Click, затем сдвинь курсор.\nF8 — мгновенно включить или выключить.",
ForeColor = Color.FromArgb(190, 190, 200),
AutoSize = false,
TextAlign = ContentAlignment.MiddleCenter,
Bounds = new Rectangle(20, 185, 460, 50)
};
Controls.Add(title);
Controls.Add(toggleButton);
Controls.Add(statusLabel);
Controls.Add(help);
timer = new Timer { Interval = 20 };
timer.Tick += OnTimerTick;
timer.Start();
anchor = Cursor.Position;
stableSince = DateTime.UtcNow;
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
NativeMethods.RegisterHotKey(Handle, NativeMethods.HOTKEY_ID, 0, (uint)Keys.F8);
}
protected override void OnHandleDestroyed(EventArgs e)
{
NativeMethods.UnregisterHotKey(Handle, NativeMethods.HOTKEY_ID);
base.OnHandleDestroyed(e);
}
protected override void WndProc(ref Message message)
{
if (message.Msg == NativeMethods.WM_HOTKEY && message.WParam.ToInt32() == NativeMethods.HOTKEY_ID) {
Toggle();
return;
}
base.WndProc(ref message);
}
private void Toggle()
{
enabled = !enabled;
clickLocked = false;
cooldownUntil = DateTime.MinValue;
anchor = Cursor.Position;
stableSince = DateTime.UtcNow;
toggleButton.Text = enabled ? "ВЫКЛЮЧИТЬ (F8)" : "ВКЛЮЧИТЬ (F8)";
toggleButton.BackColor = enabled ? Color.FromArgb(135, 45, 45) : Color.FromArgb(40, 110, 65);
statusLabel.Text = enabled ? "ВКЛЮЧЕНО — удержание 0,1с" : "ВЫКЛЮЧЕНО — кликов не будет";
statusLabel.ForeColor = enabled ? Color.FromArgb(90, 230, 125) : Color.FromArgb(255, 100, 100);
}
private void OnTimerTick(object sender, EventArgs e)
{
if (!enabled) return;
DateTime now = DateTime.UtcNow;
Point current = Cursor.Position;
int dx = current.X - anchor.X;
int dy = current.Y - anchor.Y;
bool moved = (dx * dx + dy * dy) >= MovementThreshold * MovementThreshold;
if (moved) {
anchor = current;
stableSince = now;
}
if (clickLocked) {
if (now < cooldownUntil) {
double remaining = Math.Ceiling((cooldownUntil - now).TotalMilliseconds / 100.0) / 10.0;
statusLabel.Text = "Пауза " + remaining.ToString("0.0") + "с";
return;
}
clickLocked = false;
stableSince = now;
statusLabel.Text = "ВКЛЮЧЕНО — готов к следующему клику";
}
if (Bounds.Contains(current)) return;
if ((now - stableSince).TotalMilliseconds < DwellMilliseconds) return;
clickLocked = true;
cooldownUntil = now.AddSeconds(3);
PerformControlClick();
statusLabel.Text = "CTRL + CLICK выполнен — пауза 3,0с";
}
private static void PerformControlClick()
{
NativeMethods.keybd_event(NativeMethods.VK_CONTROL, 0, 0, UIntPtr.Zero);
NativeMethods.mouse_event(NativeMethods.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, UIntPtr.Zero);
NativeMethods.mouse_event(NativeMethods.MOUSEEVENTF_LEFTUP, 0, 0, 0, UIntPtr.Zero);
NativeMethods.keybd_event(NativeMethods.VK_CONTROL, 0, NativeMethods.KEYEVENTF_KEYUP, UIntPtr.Zero);
}
}
internal static class Program
{
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new HelperForm());
}
}