winipc-ua/Utils/InputHandler.cs

332 lines
11 KiB
C#
Raw Normal View History

2025-08-02 20:18:00 +03:00
/* This software is licensed by the MIT License, see LICENSE file */
/* Copyright © 2024-2025 Gregory Lirent */
#if WINIPC_UA_SERVER
using System.Runtime.InteropServices;
using WinIPC.Models;
namespace WinIPC.Utils
{
public class InputHandler
{
private Dictionary<IntPtr, Dictionary<Button, bool>> _state = new();
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, IntPtr pInputs, int cbSize);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern short GetKeyState(int nVirtKey);
[DllImport("user32.dll")]
private static extern int MapVirtualKey(int uCode, uint uMapType);
private bool IsKeyPressed(int keyCode)
{
return (GetKeyState(keyCode) & 0x8000) != 0;
}
private ushort GetMouseKeyStates()
{
ushort keyStates = 0;
if (IsKeyPressed(0x01)) keyStates |= 0x0001; // lbutton
if (IsKeyPressed(0x02)) keyStates |= 0x0002; // rbutton
if (IsKeyPressed(0x04)) keyStates |= 0x0010; // mbutton
if (IsKeyPressed(0x05)) keyStates |= 0x0020; // xbutton1
if (IsKeyPressed(0x06)) keyStates |= 0x0040; // xbutton2
if (IsKeyPressed(0x10)) keyStates |= 0x0004; // Shift
if (IsKeyPressed(0x11)) keyStates |= 0x0008; // Ctrl
return keyStates;
}
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
private struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(8)]
public MOUSEINPUT mi;
[FieldOffset(8)]
public KEYBDINPUT ki;
}
private static IntPtr MakeParam(int low, int high = 0)
{
return (high << 16) | (low & 0xffff);
}
private static IntPtr GetCursorPos(IntPtr hwnd)
{
WindowScanner.GetCursorPos(out var pos);
if (hwnd != IntPtr.Zero)
{
WindowScanner.ScreenToClient(hwnd, ref pos);
}
return MakeParam(pos.X, pos.Y);
}
private static readonly int INPUT_SIZE = Marshal.SizeOf(typeof(INPUT));
private Dictionary<Button, bool> GetStateDict(IntPtr hwnd)
{
Dictionary<Button, bool> state;
if (_state.TryGetValue(hwnd, out var s))
{
state = s;
}
else { _state.Add(hwnd, state = new()); }
return state;
}
private async Task<bool> ProcessMouseInput(IntPtr hwnd, Button button, bool press)
{
bool success = false;
var state = GetStateDict(hwnd);
if (state.TryGetValue(button, out var s) && s == press) return true;
return await Task.Run(async () =>
{
if (hwnd == IntPtr.Zero)
{
var buffer = Marshal.AllocHGlobal(INPUT_SIZE);
var input = new INPUT
{
type = 0x00,
mi = new()
};
if (button.HasFlag(Button.MouseExtraFlag))
{
input.mi.dwFlags = (uint)Button.MouseExtraFlag << (press ? 0 : 1);
input.mi.mouseData = (int)(button & ~Button.MouseExtraFlag);
}
else
{
input.mi.dwFlags = (uint)button << (press ? 0 : 1);
}
Marshal.StructureToPtr(input, buffer, true);
success = await Task.Run(() => { return 1 != SendInput(1, buffer, INPUT_SIZE); });
Marshal.FreeHGlobal(buffer);
}
else
{
int msg = 0;
int wFlags = 0;
switch(button)
{
case Button.MouseLeft:
msg = press ? 0x0201 : 0x0202;
break;
case Button.MouseRight:
msg = press ? 0x0204 : 0x0205;
break;
case Button.MouseMiddle:
msg = press ? 0x0207 : 0x0208;
break;
case Button.MouseExtra1:
case Button.MouseExtra2:
msg = press ? 0x020b : 0x020c;
wFlags = (int)button & 0x7f;
break;
default:
// TODO Log
break;
}
SendMessage(hwnd, msg, MakeParam(GetMouseKeyStates(), wFlags), GetCursorPos(hwnd));
success = true;
}
if (success) state[button] = press;
return success;
});
}
private async Task<bool> ProcessKeyboardInput(IntPtr hwnd, Button button, bool press)
{
bool success = false;
var state = GetStateDict(hwnd);
if (state.TryGetValue(button, out var s) && s == press) return true;
return await Task.Run<bool>(async () =>
{
if (hwnd == IntPtr.Zero)
{
var buffer = Marshal.AllocHGlobal(INPUT_SIZE);
var flags = (uint)(press ? 0x00 : 0x02);
var input = new INPUT
{
type = 0x01,
ki = new()
};
if (button.HasFlag(Button.ExtendedKeyFlag))
{
flags |= 0x01;
button &= ~Button.ExtendedKeyFlag;
}
input.ki.wVk = (ushort)button;
input.ki.dwFlags = flags;
Marshal.StructureToPtr(input, buffer, true);
success = await Task.Run(() => { return 1 != SendInput(1, buffer, INPUT_SIZE); });
Marshal.FreeHGlobal(buffer);
}
else
{
var btn = (int)(button & ~Button.ExtendedKeyFlag);
var msg = press ? 0x0100 : 0x0101;
if (button == Button.LAlt || button == Button.RAlt) msg += 4;
int flags = MapVirtualKey(btn, 0) | (button.HasFlag(Button.ExtendedKeyFlag) ? 0x0100 : 0) |
(IsKeyPressed(0x12) ? 0x2000 : 0) | (s ? 0x4000 : 0) | (press ? 0 : 0x8000);
SendMessage(hwnd, msg, MakeParam(btn), MakeParam(1, flags));
success = true;
}
if (success) state[button] = press;
return success;
});
}
public async Task<bool> KeyUp(IntPtr hwnd, Button button, int delay)
{
Task<bool> pTask;
bool success;
if (button.HasFlag(Button.MouseKeyFlag))
{
pTask = ProcessMouseInput(hwnd, button & ~Button.MouseKeyFlag, false);
}
else
{
pTask = ProcessKeyboardInput(hwnd, button, false);
}
if (success = await pTask)
{
await Task.Delay(delay);
}
return success;
}
public async Task<bool> KeyDown(IntPtr hwnd, Button button, int delay)
{
Task<bool> pTask;
bool success;
if (button.HasFlag(Button.MouseKeyFlag))
{
pTask = ProcessMouseInput(hwnd, button & ~Button.MouseKeyFlag, true);
}
else
{
pTask = ProcessKeyboardInput(hwnd, button, true);
}
if (success = await pTask)
{
await Task.Delay(delay);
}
return success;
}
public async Task<bool> MouseScroll(IntPtr hwnd, int offset, int delay)
{
bool success = false;
if (hwnd == IntPtr.Zero)
{
var buffer = Marshal.AllocHGlobal(INPUT_SIZE);
var input = new INPUT
{
type = 0x00,
mi = new MOUSEINPUT
{
dwFlags = 0x0800,
mouseData = offset
}
};
Marshal.StructureToPtr(input, buffer, true);
success = await Task.Run(() => { return 1 != SendInput(1, buffer, INPUT_SIZE); });
Marshal.FreeHGlobal(buffer);
}
else
{
SendMessage(hwnd, 0x020a, MakeParam(GetMouseKeyStates(), offset), GetCursorPos(IntPtr.Zero));
success = true;
}
await Task.Delay(delay);
return success;
}
public async Task<bool> MouseMove(IntPtr hwnd, int x, int y, int delay)
{
bool success = false;
if (hwnd == IntPtr.Zero)
{
var buffer = Marshal.AllocHGlobal(INPUT_SIZE);
var input = new INPUT
{
type = 0x00,
mi = new MOUSEINPUT
{
dwFlags = 0x8001,
dx = x,
dy = y
}
};
Marshal.StructureToPtr(input, buffer, true);
success = await Task.Run(() => { return 1 != SendInput(1, buffer, INPUT_SIZE); });
Marshal.FreeHGlobal(buffer);
}
else
{
SendMessage(hwnd, 0x0200, MakeParam(GetMouseKeyStates()), MakeParam(x, y));
success = true;
}
await Task.Delay(delay);
return success;
}
}
}
#endif