duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

win32_raw_input_source.cpp (9901B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #include "win32_raw_input_source.h"
      5 #include "common/assert.h"
      6 #include "common/log.h"
      7 #include "common/string_util.h"
      8 #include "core/host.h"
      9 #include "core/system.h"
     10 #include "input_manager.h"
     11 
     12 #include <cmath>
     13 #include <hidsdi.h>
     14 #include <hidusage.h>
     15 #include <malloc.h>
     16 
     17 Log_SetChannel(Win32RawInputSource);
     18 
     19 static const wchar_t* WINDOW_CLASS_NAME = L"Win32RawInputSource";
     20 static bool s_window_class_registered = false;
     21 
     22 static constexpr const u32 ALL_BUTTON_MASKS = RI_MOUSE_BUTTON_1_DOWN | RI_MOUSE_BUTTON_1_UP | RI_MOUSE_BUTTON_2_DOWN |
     23                                               RI_MOUSE_BUTTON_2_UP | RI_MOUSE_BUTTON_3_DOWN | RI_MOUSE_BUTTON_3_UP |
     24                                               RI_MOUSE_BUTTON_4_DOWN | RI_MOUSE_BUTTON_4_UP | RI_MOUSE_BUTTON_5_DOWN |
     25                                               RI_MOUSE_BUTTON_5_UP;
     26 
     27 Win32RawInputSource::Win32RawInputSource() = default;
     28 
     29 Win32RawInputSource::~Win32RawInputSource() = default;
     30 
     31 bool Win32RawInputSource::Initialize(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock)
     32 {
     33   if (!RegisterDummyClass())
     34   {
     35     ERROR_LOG("Failed to register dummy window class");
     36     return false;
     37   }
     38 
     39   if (!CreateDummyWindow())
     40   {
     41     ERROR_LOG("Failed to create dummy window");
     42     return false;
     43   }
     44 
     45   if (!OpenDevices())
     46   {
     47     ERROR_LOG("Failed to open devices");
     48     return false;
     49   }
     50 
     51   return true;
     52 }
     53 
     54 void Win32RawInputSource::UpdateSettings(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock)
     55 {
     56 }
     57 
     58 bool Win32RawInputSource::ReloadDevices()
     59 {
     60   return false;
     61 }
     62 
     63 void Win32RawInputSource::Shutdown()
     64 {
     65   CloseDevices();
     66   DestroyDummyWindow();
     67 }
     68 
     69 void Win32RawInputSource::PollEvents()
     70 {
     71   // noop, handled by message pump
     72 }
     73 
     74 std::vector<std::pair<std::string, std::string>> Win32RawInputSource::EnumerateDevices()
     75 {
     76   std::vector<std::pair<std::string, std::string>> ret;
     77   for (u32 pointer_index = 0; pointer_index < static_cast<u32>(m_mice.size()); pointer_index++)
     78     ret.emplace_back(InputManager::GetPointerDeviceName(pointer_index), GetMouseDeviceName(pointer_index));
     79 
     80   return ret;
     81 }
     82 
     83 void Win32RawInputSource::UpdateMotorState(InputBindingKey key, float intensity)
     84 {
     85 }
     86 
     87 void Win32RawInputSource::UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity,
     88                                            float small_intensity)
     89 {
     90 }
     91 
     92 std::optional<InputBindingKey> Win32RawInputSource::ParseKeyString(std::string_view device, std::string_view binding)
     93 {
     94   return std::nullopt;
     95 }
     96 
     97 TinyString Win32RawInputSource::ConvertKeyToString(InputBindingKey key)
     98 {
     99   return {};
    100 }
    101 
    102 TinyString Win32RawInputSource::ConvertKeyToIcon(InputBindingKey key)
    103 {
    104   return {};
    105 }
    106 
    107 std::vector<InputBindingKey> Win32RawInputSource::EnumerateMotors()
    108 {
    109   return {};
    110 }
    111 
    112 bool Win32RawInputSource::GetGenericBindingMapping(std::string_view device, GenericInputBindingMapping* mapping)
    113 {
    114   return {};
    115 }
    116 
    117 bool Win32RawInputSource::RegisterDummyClass()
    118 {
    119   if (s_window_class_registered)
    120     return true;
    121 
    122   WNDCLASSW wc = {};
    123   wc.hInstance = GetModuleHandleW(nullptr);
    124   wc.lpfnWndProc = DummyWindowProc;
    125   wc.lpszClassName = WINDOW_CLASS_NAME;
    126   s_window_class_registered = (RegisterClassW(&wc) != 0);
    127   return s_window_class_registered;
    128 }
    129 
    130 bool Win32RawInputSource::CreateDummyWindow()
    131 {
    132   m_dummy_window = CreateWindowExW(0, WINDOW_CLASS_NAME, WINDOW_CLASS_NAME, WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT,
    133                                    CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandleW(nullptr), NULL);
    134   if (!m_dummy_window)
    135     return false;
    136 
    137   SetWindowLongPtrW(m_dummy_window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
    138   return true;
    139 }
    140 
    141 void Win32RawInputSource::DestroyDummyWindow()
    142 {
    143   if (!m_dummy_window)
    144     return;
    145 
    146   DestroyWindow(m_dummy_window);
    147   m_dummy_window = {};
    148 }
    149 
    150 LRESULT CALLBACK Win32RawInputSource::DummyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    151 {
    152   if (msg != WM_INPUT)
    153     return DefWindowProcW(hwnd, msg, wParam, lParam);
    154 
    155   UINT size = 0;
    156   GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &size, sizeof(RAWINPUTHEADER));
    157 
    158   PRAWINPUT data = static_cast<PRAWINPUT>(_alloca(size));
    159   GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER));
    160 
    161   // we shouldn't get any WM_INPUT messages prior to SetWindowLongPtr(), so this'll be fine
    162   Win32RawInputSource* ris = reinterpret_cast<Win32RawInputSource*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
    163   if (ris->ProcessRawInputEvent(data))
    164     return 0;
    165 
    166   // forward through to normal message processing
    167   return DefWindowProcW(hwnd, msg, wParam, lParam);
    168 }
    169 
    170 std::string Win32RawInputSource::GetMouseDeviceName(u32 index)
    171 {
    172 #if 0
    173   // Doesn't work for mice :(
    174   const HANDLE device = m_mice[index].device;
    175   std::wstring wdevice_name;
    176 
    177   UINT size = 0;
    178   if (GetRawInputDeviceInfoW(device, RIDI_DEVICENAME, nullptr, &size) == static_cast<UINT>(-1))
    179     goto error;
    180 
    181   wdevice_name.resize(size);
    182 
    183   UINT written_size = GetRawInputDeviceInfoW(device, RIDI_DEVICENAME, wdevice_name.data(), &size);
    184   if (written_size == static_cast<UINT>(-1))
    185     goto error;
    186 
    187   wdevice_name.resize(written_size);
    188   if (wdevice_name.empty())
    189     goto error;
    190 
    191   const HANDLE hFile = CreateFileW(wdevice_name.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
    192                                    OPEN_EXISTING, 0, NULL);
    193   if (hFile == INVALID_HANDLE_VALUE)
    194     goto error;
    195 
    196   wchar_t product_string[256];
    197   if (!HidD_GetProductString(hFile, product_string, sizeof(product_string)))
    198   {
    199     CloseHandle(hFile);
    200     goto error;
    201   }
    202 
    203   CloseHandle(hFile);
    204 
    205   return StringUtil::WideStringToUTF8String(product_string);
    206 
    207 error:
    208   return "Unknown Device";
    209 #else
    210   return fmt::format("Raw Input Pointer {}", index);
    211 #endif
    212 }
    213 
    214 bool Win32RawInputSource::OpenDevices()
    215 {
    216   UINT num_devices = 0;
    217   if (GetRawInputDeviceList(nullptr, &num_devices, sizeof(RAWINPUTDEVICELIST)) == static_cast<UINT>(-1) ||
    218       num_devices == 0)
    219   {
    220     return false;
    221   }
    222 
    223   std::vector<RAWINPUTDEVICELIST> devices(num_devices);
    224   if (GetRawInputDeviceList(devices.data(), &num_devices, sizeof(RAWINPUTDEVICELIST)) == static_cast<UINT>(-1))
    225     return false;
    226   devices.resize(num_devices);
    227 
    228   for (const RAWINPUTDEVICELIST& rid : devices)
    229   {
    230     if (rid.dwType == RIM_TYPEMOUSE)
    231     {
    232       // Make sure it's a real mouse with buttons.
    233       // My goal with this was to stop my silly Corsair keyboard from showing up as a mouse... but it reports 32
    234       // buttons.
    235       RID_DEVICE_INFO devinfo = {
    236         .cbSize = sizeof(devinfo),
    237         .dwType = RIM_TYPEMOUSE,
    238       };
    239       UINT devinfo_size = sizeof(devinfo);
    240       if (GetRawInputDeviceInfoW(rid.hDevice, RIDI_DEVICEINFO, &devinfo, &devinfo_size) <= 0 ||
    241           devinfo.mouse.dwNumberOfButtons == 0)
    242       {
    243         continue;
    244       }
    245 
    246       m_mice.push_back({.device = rid.hDevice, .button_state = 0, .last_x = 0, .last_y = 0});
    247     }
    248   }
    249 
    250   DEV_LOG("Found {} mice", m_mice.size());
    251 
    252   // Grab all mouse input.
    253   if (!m_mice.empty())
    254   {
    255     const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE, 0, m_dummy_window};
    256     if (!RegisterRawInputDevices(&rrid, 1, sizeof(rrid)))
    257       return false;
    258 
    259     for (u32 i = 0; i < static_cast<u32>(m_mice.size()); i++)
    260       InputManager::OnInputDeviceConnected(InputManager::GetPointerDeviceName(i), GetMouseDeviceName(i));
    261   }
    262 
    263   return true;
    264 }
    265 
    266 void Win32RawInputSource::CloseDevices()
    267 {
    268   if (!m_mice.empty())
    269   {
    270     const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_KEYBOARD, RIDEV_REMOVE, m_dummy_window};
    271     RegisterRawInputDevices(&rrid, 1, sizeof(rrid));
    272 
    273     for (u32 i = 0; i < static_cast<u32>(m_mice.size()); i++)
    274     {
    275       InputManager::OnInputDeviceDisconnected(InputManager::MakePointerAxisKey(i, InputPointerAxis::X),
    276                                               InputManager::GetPointerDeviceName(i));
    277     }
    278     m_mice.clear();
    279   }
    280 }
    281 
    282 bool Win32RawInputSource::ProcessRawInputEvent(const RAWINPUT* event)
    283 {
    284   if (event->header.dwType == RIM_TYPEMOUSE)
    285   {
    286     for (u32 pointer_index = 0; pointer_index < static_cast<u32>(m_mice.size()); pointer_index++)
    287     {
    288       MouseState& state = m_mice[pointer_index];
    289       if (state.device != event->header.hDevice)
    290         continue;
    291 
    292       const RAWMOUSE& rm = event->data.mouse;
    293 
    294       s32 dx = rm.lLastX;
    295       s32 dy = rm.lLastY;
    296 
    297       // handle absolute positioned devices
    298       if ((rm.usFlags & MOUSE_MOVE_ABSOLUTE) == MOUSE_MOVE_ABSOLUTE)
    299       {
    300         dx -= std::exchange(dx, state.last_x);
    301         dy -= std::exchange(dy, state.last_y);
    302       }
    303 
    304       unsigned long button_mask =
    305         (rm.usButtonFlags & (rm.usButtonFlags ^ std::exchange(state.button_state, rm.usButtonFlags))) &
    306         ALL_BUTTON_MASKS;
    307 
    308       while (button_mask != 0)
    309       {
    310         unsigned long bit_index;
    311         _BitScanForward(&bit_index, button_mask);
    312 
    313         // these are ordered down..up for each button
    314         const u32 button_number = bit_index >> 1;
    315         const bool button_pressed = (bit_index & 1u) == 0;
    316         InputManager::InvokeEvents(InputManager::MakePointerButtonKey(pointer_index, button_number),
    317                                    static_cast<float>(button_pressed), GenericInputBinding::Unknown);
    318 
    319         button_mask &= ~(1u << bit_index);
    320       }
    321 
    322       if (dx != 0)
    323         InputManager::UpdatePointerRelativeDelta(pointer_index, InputPointerAxis::X, static_cast<float>(dx), true);
    324       if (dy != 0)
    325         InputManager::UpdatePointerRelativeDelta(pointer_index, InputPointerAxis::Y, static_cast<float>(dy), true);
    326 
    327       return true;
    328     }
    329   }
    330 
    331   return false;
    332 }
    333 
    334 std::unique_ptr<InputSource> InputSource::CreateWin32RawInputSource()
    335 {
    336   return std::make_unique<Win32RawInputSource>();
    337 }