Как точно измерить движение мыши в дюймах или сантиметрах для мыши с известным 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

Другие вопросы по тегам