Я работаю над программой, которая облегчит играть в мобильные игры на ПК, сделав элементы управления более интуитивно понятными. В основном, программа отображения ввода.
. Для этого я использую Raw Input API с низкоуровневыми зацепками мыши и SendInput.
Код работает так:
1) На мышке нажата кнопка. Raw Input получает информацию о событиях мыши. Низкоуровневая мышь Hook блокирует этот ввод для вступления в силу.
2) Затем Raw Input обрабатывает ввод мыши. Он изменяет положение курсора с помощью SetCursorPos и отправляет синтезированный щелчок левой кнопкой мыши (вверх или вниз) через SendInput.
3) Низкоуровневая мышь Hook получает синтезированный щелчок левой кнопкой мыши и позволяет ему пройти (не блокирует его),т.е. любое событие, которое не синтезируется, блокируется.
Это работает, но есть проблема. SendInput работает мгновенно, если во время вызова вводится информация от мыши. Но если он вызывается при отсутствии ввода от мыши, он показывает заметную задержку.
Вот видео демонстрация. Обратите внимание на поле перетаскивания после отпускания мыши. SendInput работает мгновенно, если я перемещаю мышь после отпускания, поле перетаскивания исчезает мгновенно. Но если после отпускания удерживать мышь, поле перетаскивания остается на некоторое время, а затем исчезает. Когда я прекращаю перетаскивать, я отпускаю мышь сразу же, но окно перетаскивания не исчезает. В окне вывода я распечатываю '0' и '1' до и после SendInput соответственно, чтобы сделать задержку более четкой. '01' является мгновенным при перемещении моей мыши, но есть задержка между 0 и 1, когда мышь все еще после отпускания.
https://youtu.be/9Wn4Nv9_U7w
Редактировать:
Вот код, который будет воспроизводить ошибку, просто создайте приложение для рабочего стола Windows, замените код по умолчанию этим и соберите. Затем следуйте инструкциям в комментариях, чтобы воспроизвести ошибку.
//error reproduced
//to see error in action, run program and click the middle mouse button to activate hook
//then drag LMB to a point and stop, try multiple times and observe drag box not disappearing
//middle click down again deactivates hook
//NOTE: in case you lose control of mouse, ctrl+alt+delete -> task manager -> arrow key down and delete the project application
#include "stdafx.h"
#include <sstream>
HHOOK mouseHook;
HWND createSkillCursor(HINSTANCE hInstance);
POINT initialPoint();
void registerRawMouse(HWND hwnd);
void processMouseRawInput(HWND hwnd, LPARAM lParam);
//left click SendInput function
void click(bool down)
{
//for debugging purposes
std::stringstream ss;
//initialize synthetic input
INPUT i = { 0 };
i.type = INPUT_MOUSE;
i.mi.dwExtraInfo = 13470; //synthetic click has this extra info
//left click down or up
if (down)
{
if (GetAsyncKeyState(VK_LBUTTON) == 0)
{
i.mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1, &i, sizeof(INPUT));
}
i.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &i, sizeof(INPUT));
}
else
{
i.mi.dwFlags = MOUSEEVENTF_LEFTUP;
ss << "0";
OutputDebugStringA(ss.str().c_str());
SendInput(1, &i, sizeof(INPUT));
ss.str("");
ss << "1 ";
OutputDebugStringA(ss.str().c_str());
}
//The error occurs for both up and down clicks but you move the mouse after down click so it don't occur most times on down click
}
//hook callback
LRESULT CALLBACK MouseHookCallback(int nCode, WPARAM wParam, LPARAM lParam)
{
//to recieve info on synthetic click
MSLLHOOKSTRUCT *mi;
mi = (MSLLHOOKSTRUCT*)lParam;
if (nCode >= 0)
{
if (mi->dwExtraInfo != 13470)
{
return 1;
}
//hook only processes left clicks
switch (wParam)
{
case WM_LBUTTONDOWN:
break;
case WM_LBUTTONUP:
break;
default:
return 1;
}
}
return CallNextHookEx(mouseHook, nCode, wParam, lParam);
}
void SetHook()
{
mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookCallback, NULL, 0);
}
void ReleaseHook()
{
UnhookWindowsHookEx(mouseHook);
}
//raw input processing
void processMouseRawInput(HWND hwnd, LPARAM lParam)
{
std::stringstream ss;
static POINT mp = initialPoint(); // initialize mouse initial point
static long rx, ry; //relative movement of mouse
static bool hook = false; //for activating and deactivating hook
//variables for raw input
BYTE *buffer = new BYTE[40];
UINT bsize = 40;
RAWINPUT *raw;
USHORT ulButtons;
ULONG extraInfo;
//receive raw data, btw this also recieves synthetic input so don't be fooled
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, buffer, &bsize, sizeof(RAWINPUTHEADER));
raw = (RAWINPUT*)buffer;
rx = raw->data.mouse.lLastX;
ry = raw->data.mouse.lLastY;
ulButtons = raw->data.mouse.ulButtons;
extraInfo = raw->data.mouse.ulExtraInformation;
//activate hook toggle is middle mouse button down
if (ulButtons & RI_MOUSE_MIDDLE_BUTTON_DOWN)
{
hook = !hook;
if (hook)
{
ss << "\nActive!\n";
SetHook();
}
else
{
ss << "\nInactive!\n";
ReleaseHook();
}
OutputDebugStringA(ss.str().c_str());
}
//for changing mouse position
mp.x += rx;
mp.y += ry;
//for 1920x1080 screen
if (mp.x > 1920)
{
mp.x = 1920;
}
else if (mp.x < 0)
{
mp.x = 0;
}
if (mp.y > 1080)
{
mp.y = 1080;
}
else if (mp.y < 0)
{
mp.y = 0;
}
//update mouse position
SetCursorPos(mp.x, mp.y);
//input is synthetic if extraInfo == 13470 so don't process it, i.e. only process actual mouse input
if (hook && extraInfo != 13470)
{
if (ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN)
{
click(true);
}
else if (ulButtons & RI_MOUSE_LEFT_BUTTON_UP)
{
click(false);
}
}
}
//window proc for the white box
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_INPUT:
processMouseRawInput(hwnd, lParam);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
//application entry
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
//cursor window
HWND hwnd = createSkillCursor(hInstance);
ShowWindow(hwnd, nCmdShow);
//mouse raw data
registerRawMouse(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//initialization for the white box
HWND createSkillCursor(HINSTANCE hInstance)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_POPUPWINDOW, // Window style
// Size and position
970, 620, 34, 34,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL)
{
return 0;
}
return hwnd;
}
//for registering mouse as raw input device
void registerRawMouse(HWND hwnd)
{
RAWINPUTDEVICE Rid;
Rid.usUsagePage = 1;
Rid.usUsage = 2;
Rid.dwFlags = RIDEV_INPUTSINK;
Rid.hwndTarget = hwnd;
RegisterRawInputDevices(&Rid, 1, sizeof(RAWINPUTDEVICE));
}
//initial mouse position on starting program
POINT initialPoint()
{
POINT p = { 0, 0 };
p.x = 1480;
p.y = 592;
return p;
}
Любая помощь будет признательна, эта проблема сводит меня с ума.