Как точно измерить движение мыши в дюймах или сантиметрах для мыши с известным DPI
У меня игровая мышь Logitech G500 с полным разрешением 5700 точек на дюйм.
Я пытаюсь написать программу на C++, которая точно измеряет горизонтальное перемещение мыши в физических единицах, т.е. сантиметры или дюймы.
Я использую Windows API и Windows Raw ввод через сообщение WM_INPUT, чтобы получить необработанные изменения движения от мыши.
Затем я предполагаю, что 1 единица движения, сообщаемая через WM_INPUT, составляет 1/5700th от дюйма, и, отслеживая чистое движение мыши, я подумал, что мог бы выполнить простой расчет, чтобы получить чистое физическое движение:
расстояние (дюймы) = total_movement_from_wminput / dpi; // dpi = 5700 в этом случае
К сожалению, расчет не кажется точным. По физическим измерениям, проведенным только на моем коврике для мыши, я могу сказать, что в течение примерно 6 дюймов движения мыши вычисление дает значение около 5 с половиной дюймов (потеря примерно 1/2 дюйма).
Куда я иду не так? Я установил мышь на 5700 точек на дюйм на его панели управления, может ли фактический DPI быть меньше этого? Является ли мое предположение о том, что 1 единица изменения через WM_INPUT составляет 1/dpi дюймов физического движения, неверно?
У кого-нибудь есть идеи о том, как я могу сделать это, чтобы быть точным? Спасибо!
1 ответ
Марк,
Кажется, что проблема может быть, когда вы перемещаете мышь быстрее, чем событие Windows WM_INPUT
обрабатывает это. Например, предположим, что мышь переместилась на 2 пикселя в одном кадре. У вас будет потеря 1/5700 дюйма (в вашем случае), потому что для одного WM_INPUT
событие обработано, вы бы переместили два пикселя.
Чтобы это исправить, вы должны проверить, сколько пикселей перемещает мышь при каждой отправке сообщения WM_INPUT в программу. Что вам нужно сделать, это сделать RAWINPUTDEVICE
переменной и установите структуру так, чтобы она имела информацию о мышке.
Следующий код регистрирует RAWINPUTDEVICE
так что это может быть использовано в WM_INPUT
,
RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
Rid[0].dwFlags = RIDEV_INPUTSINK;
Rid[0].hwndTarget = hWnd;
RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]);
Следующий код активно использует Rid
Переменная два определяет, сколько пикселей мышь переместила с момента последнего WM_INPUT
был инициирован.
case WM_INPUT:
{
UINT dwSize = 40;
static BYTE lpb[40];
GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
lpb, &dwSize, sizeof(RAWINPUTHEADER));
RAWINPUT* raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE)
{
int xPosRelative = raw->data.mouse.lLastX; // Could be 1, or could be more than 1
int yPosRelative = raw->data.mouse.lLastY; // Could be 1, or could be more than 1!
}
break;
}
Обратите внимание, что этот код является тем же самым кодом, представленным на msdn в этой самой теме (ссылка ниже).
Теперь у вас может быть глобальная переменная некоторого типа, которая имеет x-position и y-position (в пикселях) мыши. Затем вы просто делите эти переменные на DPI и получаете смещение в дюймах от значения, установленного для глобальных переменных равным 0.
Совершенно более простой способ - обработать WM_MOUSEMOVE
событие вместо Это позволяет легко определить точное положение мыши (конечно, в пикселях). Используя это, вы можете вычесть это из значений пикселей начальной позиции.
Пример:
DPI = 5700.
Начальная позиция = (100px, 300px).
Положение через 3 секунды = (500px, 400px).
Количество перемещаемых дюймов за эти 3 секунды = ( (500px - 100px)/5700 дюймов, (400px - 300px)/5700 дюймов)
Общее правило: количество дюймов, перемещаемых после S секунд = (inital_pixels_x - final_pixels_x)/DPI дюймов
по горизонтали, (initial_pixels_y - final_pixels_y)/ дюйм по вертикали
Здесь final_pixels_x - это x-позиция мыши через s секунд, а final_pixels y - это y-позиция после s секунд.
Подводя итог тому, что вы сделали неправильно, вы ошибочно предположили, что каждый WM_INPUT
событие означает, что 1 пиксель был пройден мышью.
Если по какой-то причине я неправильно понял вопрос, и вы знаете, что вы уже перемещаете правильное количество пикселей, оставьте комментарий, и я сделаю все возможное, чтобы попытаться исправить мой ответ. Тем не менее, я все равно рекомендую использовать WM_MOUSEMOVE
вместо WM_INPUT
как это специально для мыши, и он применяет "ускорение указателя", о котором вы можете прочитать по ссылке в самом низу.
Спасибо за вопрос, tcs08
MSND-код и объяснение ввода с помощью WM_INPUT
MSND-код и объяснение для ввода с помощью мыши с WM_MOUSEMOVE