InputManager: Support multiple mice via raw input
Only on Windows for now.
This commit is contained in:
@ -233,14 +233,11 @@ float ImGuiManager::GetWindowHeight()
|
||||
return s_window_height;
|
||||
}
|
||||
|
||||
void ImGuiManager::WindowResized()
|
||||
void ImGuiManager::WindowResized(float width, float height)
|
||||
{
|
||||
const u32 new_width = g_gpu_device ? g_gpu_device->GetWindowWidth() : 0;
|
||||
const u32 new_height = g_gpu_device ? g_gpu_device->GetWindowHeight() : 0;
|
||||
|
||||
s_window_width = static_cast<float>(new_width);
|
||||
s_window_height = static_cast<float>(new_height);
|
||||
ImGui::GetIO().DisplaySize = ImVec2(s_window_width, s_window_height);
|
||||
s_window_width = width;
|
||||
s_window_height = height;
|
||||
ImGui::GetIO().DisplaySize = ImVec2(width, height);
|
||||
|
||||
// Scale might have changed as a result of window resize.
|
||||
RequestScaleUpdate();
|
||||
@ -1053,7 +1050,7 @@ void ImGuiManager::DrawSoftwareCursor(const SoftwareCursor& sc, const std::pair<
|
||||
void ImGuiManager::RenderSoftwareCursors()
|
||||
{
|
||||
// This one's okay to race, worst that happens is we render the wrong number of cursors for a frame.
|
||||
const u32 pointer_count = InputManager::MAX_POINTER_DEVICES;
|
||||
const u32 pointer_count = InputManager::GetPointerCount();
|
||||
for (u32 i = 0; i < pointer_count; i++)
|
||||
DrawSoftwareCursor(s_software_cursors[i], InputManager::GetPointerAbsolutePosition(i));
|
||||
|
||||
@ -1076,8 +1073,8 @@ void ImGuiManager::SetSoftwareCursor(u32 index, std::string image_path, float im
|
||||
UpdateSoftwareCursorTexture(index);
|
||||
|
||||
// Hide the system cursor when we activate a software cursor.
|
||||
if (is_hiding_or_showing && index == 0)
|
||||
InputManager::UpdateHostMouseMode();
|
||||
if (is_hiding_or_showing && index <= InputManager::MAX_POINTER_DEVICES)
|
||||
InputManager::UpdateRelativeMouseMode();
|
||||
}
|
||||
|
||||
bool ImGuiManager::HasSoftwareCursor(u32 index)
|
||||
|
||||
@ -35,7 +35,7 @@ float GetWindowWidth();
|
||||
float GetWindowHeight();
|
||||
|
||||
/// Updates internal state when the window is size.
|
||||
void WindowResized();
|
||||
void WindowResized(float width, float height);
|
||||
|
||||
/// Updates scaling of the on-screen elements.
|
||||
void RequestScaleUpdate();
|
||||
|
||||
@ -108,6 +108,7 @@ static std::vector<std::string_view> SplitChord(std::string_view binding);
|
||||
static bool SplitBinding(std::string_view binding, std::string_view* source, std::string_view* sub_binding);
|
||||
static void PrettifyInputBindingPart(std::string_view binding, SmallString& ret, bool& changed);
|
||||
static void AddBindings(const std::vector<std::string>& bindings, const InputEventHandler& handler);
|
||||
static void UpdatePointerCount();
|
||||
|
||||
static bool IsAxisHandler(const InputEventHandler& handler);
|
||||
static float ApplySingleBindingScale(float sensitivity, float deadzone, float value);
|
||||
@ -180,11 +181,19 @@ static std::array<std::array<float, static_cast<u8>(InputPointerAxis::Count)>, I
|
||||
static std::array<std::array<PointerAxisState, static_cast<u8>(InputPointerAxis::Count)>,
|
||||
InputManager::MAX_POINTER_DEVICES>
|
||||
s_pointer_state;
|
||||
static u32 s_pointer_count = 0;
|
||||
static std::array<float, static_cast<u8>(InputPointerAxis::Count)> s_pointer_axis_scale;
|
||||
|
||||
using PointerMoveCallback = std::function<void(InputBindingKey key, float value)>;
|
||||
static std::vector<std::pair<u32, PointerMoveCallback>> s_pointer_move_callbacks;
|
||||
|
||||
// Window size, used for clamping the mouse position in raw input modes.
|
||||
static std::array<float, 2> s_window_size = {};
|
||||
static bool s_relative_mouse_mode = false;
|
||||
static bool s_relative_mouse_mode_active = false;
|
||||
static bool s_hide_host_mouse_cursor = false;
|
||||
static bool s_hide_host_mouse_cusor_active = false;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Binding Parsing
|
||||
// ------------------------------------------------------------------------
|
||||
@ -293,12 +302,12 @@ bool InputManager::ParseBindingAndGetSource(std::string_view binding, InputBindi
|
||||
|
||||
std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key)
|
||||
{
|
||||
if (binding_type == InputBindingInfo::Type::Pointer)
|
||||
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::AbsolutePointer)
|
||||
{
|
||||
// pointer and device bindings don't have a data part
|
||||
if (key.source_type == InputSourceType::Pointer)
|
||||
{
|
||||
return GetPointerDeviceName(key.data);
|
||||
return GetPointerDeviceName(key.source_index);
|
||||
}
|
||||
else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast<u32>(key.source_type)])
|
||||
{
|
||||
@ -346,7 +355,7 @@ std::string InputManager::ConvertInputBindingKeysToString(InputBindingInfo::Type
|
||||
const InputBindingKey* keys, size_t num_keys)
|
||||
{
|
||||
// can't have a chord of devices/pointers
|
||||
if (binding_type == InputBindingInfo::Type::Pointer)
|
||||
if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::AbsolutePointer)
|
||||
{
|
||||
// so only take the first
|
||||
if (num_keys > 0)
|
||||
@ -598,10 +607,10 @@ static std::array<const char*, static_cast<u32>(InputSourceType::Count)> s_input
|
||||
#ifdef _WIN32
|
||||
"DInput",
|
||||
"XInput",
|
||||
"RawInput",
|
||||
#endif
|
||||
#ifndef __ANDROID__
|
||||
"SDL",
|
||||
"RawInput",
|
||||
#else
|
||||
"Android",
|
||||
#endif
|
||||
@ -631,13 +640,13 @@ bool InputManager::GetInputSourceDefaultEnabled(InputSourceType type)
|
||||
|
||||
case InputSourceType::XInput:
|
||||
return false;
|
||||
case InputSourceType::RawInput:
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#ifndef __ANDROID__
|
||||
case InputSourceType::SDL:
|
||||
return true;
|
||||
case InputSourceType::RawInput:
|
||||
return false;
|
||||
#else
|
||||
case InputSourceType::Android:
|
||||
return true;
|
||||
@ -844,6 +853,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, const std::string& sect
|
||||
break;
|
||||
|
||||
case InputBindingInfo::Type::Pointer:
|
||||
case InputBindingInfo::Type::AbsolutePointer:
|
||||
{
|
||||
auto cb = [pad_index, base = bi.bind_index](InputBindingKey key, float value) {
|
||||
if (!System::IsValid())
|
||||
@ -867,7 +877,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, const std::string& sect
|
||||
if (!key.has_value())
|
||||
continue;
|
||||
|
||||
s_pointer_move_callbacks.emplace_back(0, cb);
|
||||
s_pointer_move_callbacks.emplace_back(key.value(), cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1171,7 +1181,7 @@ void InputManager::GenerateRelativeMouseEvents()
|
||||
{
|
||||
const bool system_running = System::IsRunning();
|
||||
|
||||
for (u32 device = 0; device < MAX_POINTER_DEVICES; device++)
|
||||
for (u32 device = 0; device < s_pointer_count; device++)
|
||||
{
|
||||
for (u32 axis = 0; axis < static_cast<u32>(static_cast<u8>(InputPointerAxis::Count)); axis++)
|
||||
{
|
||||
@ -1208,14 +1218,44 @@ void InputManager::GenerateRelativeMouseEvents()
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::UpdatePointerCount()
|
||||
{
|
||||
if (!IsUsingRawInput())
|
||||
{
|
||||
s_pointer_count = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
InputSource* ris = GetInputSourceInterface(InputSourceType::RawInput);
|
||||
DebugAssert(ris);
|
||||
|
||||
s_pointer_count = 0;
|
||||
for (const std::pair<std::string, std::string>& it : ris->EnumerateDevices())
|
||||
{
|
||||
if (it.first.starts_with("Pointer-"))
|
||||
s_pointer_count++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
u32 InputManager::GetPointerCount()
|
||||
{
|
||||
return s_pointer_count;
|
||||
}
|
||||
|
||||
std::pair<float, float> InputManager::GetPointerAbsolutePosition(u32 index)
|
||||
{
|
||||
DebugAssert(index < s_host_pointer_positions.size());
|
||||
return std::make_pair(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::X)],
|
||||
s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::Y)]);
|
||||
}
|
||||
|
||||
void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y)
|
||||
{
|
||||
if (index >= MAX_POINTER_DEVICES || s_relative_mouse_mode_active) [[unlikely]]
|
||||
return;
|
||||
|
||||
const float dx = x - std::exchange(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::X)], x);
|
||||
const float dy = y - std::exchange(s_host_pointer_positions[index][static_cast<u8>(InputPointerAxis::Y)], y);
|
||||
|
||||
@ -1236,18 +1276,26 @@ void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y)
|
||||
|
||||
void InputManager::UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input)
|
||||
{
|
||||
if (raw_input != IsUsingRawInput())
|
||||
if (index >= MAX_POINTER_DEVICES || !s_relative_mouse_mode_active)
|
||||
return;
|
||||
|
||||
s_host_pointer_positions[index][static_cast<u8>(axis)] += d;
|
||||
s_pointer_state[index][static_cast<u8>(axis)].delta.fetch_add(static_cast<s32>(d * 65536.0f),
|
||||
std::memory_order_release);
|
||||
|
||||
if (index == 0 && axis <= InputPointerAxis::Y)
|
||||
ImGuiManager::UpdateMousePosition(s_host_pointer_positions[0][0], s_host_pointer_positions[0][1]);
|
||||
// We need to clamp the position ourselves in relative mode.
|
||||
if (axis <= InputPointerAxis::Y)
|
||||
{
|
||||
s_host_pointer_positions[index][static_cast<u8>(axis)] =
|
||||
std::clamp(s_host_pointer_positions[index][static_cast<u8>(axis)], 0.0f, s_window_size[static_cast<u8>(axis)]);
|
||||
|
||||
// Imgui also needs to be updated, since the absolute position won't be set above.
|
||||
if (index == 0)
|
||||
ImGuiManager::UpdateMousePosition(s_host_pointer_positions[0][0], s_host_pointer_positions[0][1]);
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::UpdateHostMouseMode()
|
||||
void InputManager::UpdateRelativeMouseMode()
|
||||
{
|
||||
// Check for relative mode bindings, and enable if there's anything using it.
|
||||
bool has_relative_mode_bindings = !s_pointer_move_callbacks.empty();
|
||||
@ -1265,8 +1313,29 @@ void InputManager::UpdateHostMouseMode()
|
||||
}
|
||||
}
|
||||
|
||||
const bool has_software_cursor = ImGuiManager::HasSoftwareCursor(0);
|
||||
Host::SetMouseMode(has_relative_mode_bindings, has_relative_mode_bindings || has_software_cursor);
|
||||
const bool hide_mouse_cursor = has_relative_mode_bindings || ImGuiManager::HasSoftwareCursor(0);
|
||||
if (s_relative_mouse_mode == has_relative_mode_bindings && s_hide_host_mouse_cursor == hide_mouse_cursor)
|
||||
return;
|
||||
|
||||
s_relative_mouse_mode = has_relative_mode_bindings;
|
||||
s_hide_host_mouse_cursor = hide_mouse_cursor;
|
||||
UpdateRelativeMouseMode();
|
||||
}
|
||||
|
||||
void InputManager::UpdateHostMouseMode()
|
||||
{
|
||||
const bool can_change = System::IsRunning();
|
||||
const bool wanted_relative_mouse_mode = (s_relative_mouse_mode && can_change);
|
||||
const bool wanted_hide_host_mouse_cursor = (s_hide_host_mouse_cursor && can_change);
|
||||
if (wanted_relative_mouse_mode == s_relative_mouse_mode_active &&
|
||||
wanted_hide_host_mouse_cursor == s_hide_host_mouse_cusor_active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_relative_mouse_mode_active = wanted_relative_mouse_mode;
|
||||
s_hide_host_mouse_cusor_active = wanted_hide_host_mouse_cursor;
|
||||
Host::SetMouseMode(wanted_relative_mouse_mode, wanted_hide_host_mouse_cursor);
|
||||
}
|
||||
|
||||
bool InputManager::IsUsingRawInput()
|
||||
@ -1278,6 +1347,12 @@ bool InputManager::IsUsingRawInput()
|
||||
#endif
|
||||
}
|
||||
|
||||
void InputManager::SetDisplayWindowSize(float width, float height)
|
||||
{
|
||||
s_window_size[0] = width;
|
||||
s_window_size[1] = height;
|
||||
}
|
||||
|
||||
void InputManager::SetDefaultSourceConfig(SettingsInterface& si)
|
||||
{
|
||||
si.ClearSection("InputSources");
|
||||
@ -1454,11 +1529,13 @@ std::vector<std::string> InputManager::GetInputProfileNames()
|
||||
|
||||
void InputManager::OnInputDeviceConnected(std::string_view identifier, std::string_view device_name)
|
||||
{
|
||||
INFO_LOG("Device '{}' connected: '{}'", identifier, device_name);
|
||||
Host::OnInputDeviceConnected(identifier, device_name);
|
||||
}
|
||||
|
||||
void InputManager::OnInputDeviceDisconnected(InputBindingKey key, std::string_view identifier)
|
||||
{
|
||||
INFO_LOG("Device '{}' disconnected", identifier);
|
||||
Host::OnInputDeviceDisconnected(key, identifier);
|
||||
}
|
||||
|
||||
@ -1771,7 +1848,7 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
|
||||
1.0f);
|
||||
}
|
||||
|
||||
UpdateHostMouseMode();
|
||||
UpdateRelativeMouseMode();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@ -1788,6 +1865,8 @@ bool InputManager::ReloadDevices()
|
||||
changed |= s_input_sources[i]->ReloadDevices();
|
||||
}
|
||||
|
||||
UpdatePointerCount();
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
@ -1972,4 +2051,6 @@ void InputManager::ReloadSources(SettingsInterface& si, std::unique_lock<std::mu
|
||||
#else
|
||||
UpdateInputSourceState(si, settings_lock, InputSourceType::Android, &InputSource::CreateAndroidSource);
|
||||
#endif
|
||||
|
||||
UpdatePointerCount();
|
||||
}
|
||||
|
||||
@ -27,10 +27,10 @@ enum class InputSourceType : u32
|
||||
#ifdef _WIN32
|
||||
DInput,
|
||||
XInput,
|
||||
RawInput,
|
||||
#endif
|
||||
#ifndef __ANDROID__
|
||||
SDL,
|
||||
RawInput,
|
||||
#else
|
||||
Android,
|
||||
#endif
|
||||
@ -174,12 +174,12 @@ namespace InputManager {
|
||||
static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms
|
||||
|
||||
/// Maximum number of host mouse devices.
|
||||
static constexpr u32 MAX_POINTER_DEVICES = 1;
|
||||
static constexpr u32 MAX_POINTER_DEVICES = 8;
|
||||
static constexpr u32 MAX_POINTER_BUTTONS = 3;
|
||||
|
||||
/// Maximum number of software cursors. We allocate an extra two for controllers with
|
||||
/// positioning data from the controller instead of a mouse.
|
||||
static constexpr u32 MAX_SOFTWARE_CURSORS = MAX_POINTER_BUTTONS + 2;
|
||||
static constexpr u32 MAX_SOFTWARE_CURSORS = MAX_POINTER_DEVICES + 2;
|
||||
|
||||
/// Number of macro buttons per controller.
|
||||
static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 4;
|
||||
@ -311,6 +311,9 @@ void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensi
|
||||
/// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes.
|
||||
void PauseVibration();
|
||||
|
||||
/// Returns the number of currently-connected pointer devices.
|
||||
u32 GetPointerCount();
|
||||
|
||||
/// Reads absolute pointer position.
|
||||
std::pair<float, float> GetPointerAbsolutePosition(u32 index);
|
||||
|
||||
@ -322,6 +325,7 @@ void UpdatePointerAbsolutePosition(u32 index, float x, float y);
|
||||
void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false);
|
||||
|
||||
/// Updates host mouse mode (relative/cursor hiding).
|
||||
void UpdateRelativeMouseMode();
|
||||
void UpdateHostMouseMode();
|
||||
|
||||
/// Sets the state of the specified macro button.
|
||||
@ -330,6 +334,9 @@ void SetMacroButtonState(u32 pad, u32 index, bool state);
|
||||
/// Returns true if the raw input source is being used.
|
||||
bool IsUsingRawInput();
|
||||
|
||||
/// Updates InputManager's view of the window size, used for clamping raw input coordinates.
|
||||
void SetDisplayWindowSize(float width, float height);
|
||||
|
||||
/// Restores default configuration.
|
||||
void SetDefaultSourceConfig(SettingsInterface& si);
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "win32_raw_input_source.h"
|
||||
@ -8,7 +8,9 @@
|
||||
#include "core/host.h"
|
||||
#include "core/system.h"
|
||||
#include "input_manager.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <hidsdi.h>
|
||||
#include <hidusage.h>
|
||||
#include <malloc.h>
|
||||
|
||||
@ -71,7 +73,11 @@ void Win32RawInputSource::PollEvents()
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> Win32RawInputSource::EnumerateDevices()
|
||||
{
|
||||
return {};
|
||||
std::vector<std::pair<std::string, std::string>> ret;
|
||||
for (u32 pointer_index = 0; pointer_index < static_cast<u32>(m_mice.size()); pointer_index++)
|
||||
ret.emplace_back(InputManager::GetPointerDeviceName(pointer_index), GetMouseDeviceName(pointer_index));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Win32RawInputSource::UpdateMotorState(InputBindingKey key, float intensity)
|
||||
@ -117,7 +123,8 @@ bool Win32RawInputSource::RegisterDummyClass()
|
||||
wc.hInstance = GetModuleHandleW(nullptr);
|
||||
wc.lpfnWndProc = DummyWindowProc;
|
||||
wc.lpszClassName = WINDOW_CLASS_NAME;
|
||||
return (RegisterClassW(&wc) != 0);
|
||||
s_window_class_registered = (RegisterClassW(&wc) != 0);
|
||||
return s_window_class_registered;
|
||||
}
|
||||
|
||||
bool Win32RawInputSource::CreateDummyWindow()
|
||||
@ -160,12 +167,58 @@ LRESULT CALLBACK Win32RawInputSource::DummyWindowProc(HWND hwnd, UINT msg, WPARA
|
||||
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
std::string Win32RawInputSource::GetMouseDeviceName(u32 index)
|
||||
{
|
||||
#if 0
|
||||
// Doesn't work for mice :(
|
||||
const HANDLE device = m_mice[index].device;
|
||||
std::wstring wdevice_name;
|
||||
|
||||
UINT size = 0;
|
||||
if (GetRawInputDeviceInfoW(device, RIDI_DEVICENAME, nullptr, &size) == static_cast<UINT>(-1))
|
||||
goto error;
|
||||
|
||||
wdevice_name.resize(size);
|
||||
|
||||
UINT written_size = GetRawInputDeviceInfoW(device, RIDI_DEVICENAME, wdevice_name.data(), &size);
|
||||
if (written_size == static_cast<UINT>(-1))
|
||||
goto error;
|
||||
|
||||
wdevice_name.resize(written_size);
|
||||
if (wdevice_name.empty())
|
||||
goto error;
|
||||
|
||||
const HANDLE hFile = CreateFileW(wdevice_name.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, 0, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
wchar_t product_string[256];
|
||||
if (!HidD_GetProductString(hFile, product_string, sizeof(product_string)))
|
||||
{
|
||||
CloseHandle(hFile);
|
||||
goto error;
|
||||
}
|
||||
|
||||
CloseHandle(hFile);
|
||||
|
||||
return StringUtil::WideStringToUTF8String(product_string);
|
||||
|
||||
error:
|
||||
return "Unknown Device";
|
||||
#else
|
||||
return fmt::format("Raw Input Pointer {}", index);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Win32RawInputSource::OpenDevices()
|
||||
{
|
||||
UINT num_devices = 0;
|
||||
if (GetRawInputDeviceList(nullptr, &num_devices, sizeof(RAWINPUTDEVICELIST)) == static_cast<UINT>(-1) ||
|
||||
num_devices == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<RAWINPUTDEVICELIST> devices(num_devices);
|
||||
if (GetRawInputDeviceList(devices.data(), &num_devices, sizeof(RAWINPUTDEVICELIST)) == static_cast<UINT>(-1))
|
||||
@ -174,28 +227,37 @@ bool Win32RawInputSource::OpenDevices()
|
||||
|
||||
for (const RAWINPUTDEVICELIST& rid : devices)
|
||||
{
|
||||
#if 0
|
||||
if (rid.dwType == RIM_TYPEKEYBOARD)
|
||||
m_num_keyboards++;
|
||||
#endif
|
||||
if (rid.dwType == RIM_TYPEMOUSE)
|
||||
m_mice.push_back({rid.hDevice, 0u, 0, 0});
|
||||
{
|
||||
// Make sure it's a real mouse with buttons.
|
||||
// My goal with this was to stop my silly Corsair keyboard from showing up as a mouse... but it reports 32
|
||||
// buttons.
|
||||
RID_DEVICE_INFO devinfo = {
|
||||
.cbSize = sizeof(devinfo),
|
||||
.dwType = RIM_TYPEMOUSE,
|
||||
};
|
||||
UINT devinfo_size = sizeof(devinfo);
|
||||
if (GetRawInputDeviceInfoW(rid.hDevice, RIDI_DEVICEINFO, &devinfo, &devinfo_size) <= 0 ||
|
||||
devinfo.mouse.dwNumberOfButtons == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_mice.push_back({.device = rid.hDevice, .button_state = 0, .last_x = 0, .last_y = 0});
|
||||
}
|
||||
}
|
||||
|
||||
DEV_LOG("Found {} keyboards and {} mice", m_num_keyboards, m_mice.size());
|
||||
DEV_LOG("Found {} mice", m_mice.size());
|
||||
|
||||
// Grab all keyboard/mouse input.
|
||||
if (m_num_keyboards > 0)
|
||||
{
|
||||
const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_KEYBOARD, 0, m_dummy_window};
|
||||
if (!RegisterRawInputDevices(&rrid, 1, sizeof(rrid)))
|
||||
return false;
|
||||
}
|
||||
// Grab all mouse input.
|
||||
if (!m_mice.empty())
|
||||
{
|
||||
const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE, 0, m_dummy_window};
|
||||
if (!RegisterRawInputDevices(&rrid, 1, sizeof(rrid)))
|
||||
return false;
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(m_mice.size()); i++)
|
||||
InputManager::OnInputDeviceConnected(InputManager::GetPointerDeviceName(i), GetMouseDeviceName(i));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -203,17 +265,16 @@ bool Win32RawInputSource::OpenDevices()
|
||||
|
||||
void Win32RawInputSource::CloseDevices()
|
||||
{
|
||||
if (m_num_keyboards > 0)
|
||||
{
|
||||
const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE, RIDEV_REMOVE, m_dummy_window};
|
||||
RegisterRawInputDevices(&rrid, 1, sizeof(rrid));
|
||||
m_num_keyboards = 0;
|
||||
}
|
||||
|
||||
if (!m_mice.empty())
|
||||
{
|
||||
const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_KEYBOARD, RIDEV_REMOVE, m_dummy_window};
|
||||
RegisterRawInputDevices(&rrid, 1, sizeof(rrid));
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(m_mice.size()); i++)
|
||||
{
|
||||
InputManager::OnInputDeviceDisconnected(InputManager::MakePointerAxisKey(i, InputPointerAxis::X),
|
||||
InputManager::GetPointerDeviceName(i));
|
||||
}
|
||||
m_mice.clear();
|
||||
}
|
||||
}
|
||||
@ -222,9 +283,9 @@ bool Win32RawInputSource::ProcessRawInputEvent(const RAWINPUT* event)
|
||||
{
|
||||
if (event->header.dwType == RIM_TYPEMOUSE)
|
||||
{
|
||||
const u32 mouse_index = 0;
|
||||
for (MouseState& state : m_mice)
|
||||
for (u32 pointer_index = 0; pointer_index < static_cast<u32>(m_mice.size()); pointer_index++)
|
||||
{
|
||||
MouseState& state = m_mice[pointer_index];
|
||||
if (state.device != event->header.hDevice)
|
||||
continue;
|
||||
|
||||
@ -244,10 +305,6 @@ bool Win32RawInputSource::ProcessRawInputEvent(const RAWINPUT* event)
|
||||
(rm.usButtonFlags & (rm.usButtonFlags ^ std::exchange(state.button_state, rm.usButtonFlags))) &
|
||||
ALL_BUTTON_MASKS;
|
||||
|
||||
// when the VM isn't running, allow events to run as normal (so we don't break the UI)
|
||||
if (System::GetState() != System::State::Running)
|
||||
return false;
|
||||
|
||||
while (button_mask != 0)
|
||||
{
|
||||
unsigned long bit_index;
|
||||
@ -255,17 +312,17 @@ bool Win32RawInputSource::ProcessRawInputEvent(const RAWINPUT* event)
|
||||
|
||||
// these are ordered down..up for each button
|
||||
const u32 button_number = bit_index >> 1;
|
||||
const bool button_pressed = (bit_index & 1u) != 0;
|
||||
InputManager::InvokeEvents(InputManager::MakePointerButtonKey(mouse_index, button_number),
|
||||
const bool button_pressed = (bit_index & 1u) == 0;
|
||||
InputManager::InvokeEvents(InputManager::MakePointerButtonKey(pointer_index, button_number),
|
||||
static_cast<float>(button_pressed), GenericInputBinding::Unknown);
|
||||
|
||||
button_mask &= ~(1u << bit_index);
|
||||
}
|
||||
|
||||
if (dx != 0)
|
||||
InputManager::UpdatePointerRelativeDelta(mouse_index, InputPointerAxis::X, static_cast<float>(dx), true);
|
||||
InputManager::UpdatePointerRelativeDelta(pointer_index, InputPointerAxis::X, static_cast<float>(dx), true);
|
||||
if (dy != 0)
|
||||
InputManager::UpdatePointerRelativeDelta(mouse_index, InputPointerAxis::Y, static_cast<float>(dy), true);
|
||||
InputManager::UpdatePointerRelativeDelta(pointer_index, InputPointerAxis::Y, static_cast<float>(dy), true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
@ -46,6 +46,8 @@ private:
|
||||
static bool RegisterDummyClass();
|
||||
static LRESULT CALLBACK DummyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static std::string GetMouseDeviceName(u32 index);
|
||||
|
||||
bool CreateDummyWindow();
|
||||
void DestroyDummyWindow();
|
||||
bool OpenDevices();
|
||||
@ -54,7 +56,6 @@ private:
|
||||
bool ProcessRawInputEvent(const RAWINPUT* event);
|
||||
|
||||
HWND m_dummy_window = {};
|
||||
u32 m_num_keyboards = 0;
|
||||
|
||||
std::vector<MouseState> m_mice;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user