Program Listing for File windows_input.cpp
↰ Return to documentation for file (include/input/windows/windows_input.cpp)
#include "windows_input.hpp"
#include "PDJE_Input.hpp"
#include "PDJE_LOG_SETTER.hpp"
#include "dev_path_to_name.hpp"
#include "windows_keyboard_fill.hpp"
#include <SetupAPI.h>
#include <bitset>
#include <format>
#include <memory_resource>
HWND
OS_Input::init()
{
HINSTANCE hst = GetModuleHandleW(nullptr);
WNDCLASSW wc{};
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hst;
wc.lpszClassName = Invisible_window_name.c_str();
RegisterClassW(&wc);
return CreateWindowExW(0,
wc.lpszClassName,
L"",
0,
0,
0,
0,
0,
HWND_MESSAGE,
nullptr,
hst,
nullptr);
}
inline std::string
wstring_to_utf8_nt(const std::wstring &w)
{
if (w.empty())
return {};
auto target = w;
if (target.rfind(L"\\??\\", 0) == 0) {
target.replace(0, 4, L"\\\\?\\");
}
int required = WideCharToMultiByte(CP_UTF8,
WC_ERR_INVALID_CHARS,
target.c_str(),
-1,
nullptr,
0,
nullptr,
nullptr);
if (required <= 0) {
critlog(
"pdje input module-Windows impl- WideCharToMultiByte size failed");
throw std::runtime_error("WideCharToMultiByte size failed");
}
std::string out(required, '\0');
int written = WideCharToMultiByte(CP_UTF8,
WC_ERR_INVALID_CHARS,
target.c_str(),
-1,
out.data(),
required,
nullptr,
nullptr);
if (written <= 0) {
critlog("pdje input module-Windows impl- WideCharToMultiByte convert "
"failed");
throw std::runtime_error("WideCharToMultiByte convert failed");
}
if (!out.empty() && out.back() == '\0')
out.pop_back();
return out;
}
void
OS_Input::run()
{
MSG msg;
DWORD w;
UINT size = 0;
uint64_t now;
PDJE_Dev_Type dtype;
thread_local std::pmr::unsynchronized_pool_resource mono_arena;
std::pmr::unsynchronized_pool_resource hid_arena;
std::string handlestr;
PDJE_Input_Event tempEv;
handlestr.reserve(100);
PDJE_HID_Event hidEv;
std::bitset<101> isPressed;
bool Writable = true;
while (true) {
w = MsgWaitForMultipleObjectsEx(0,
nullptr,
INFINITE,
QS_RAWINPUT | QS_POSTMESSAGE,
MWMO_INPUTAVAILABLE | MWMO_ALERTABLE);
if (w == WAIT_OBJECT_0) {
if (PeekMessageW(&msg, nullptr, WM_QUIT, WM_QUIT, PM_REMOVE)) {
break;
}
while (PeekMessageW(&msg, nullptr, WM_INPUT, WM_INPUT, PM_REMOVE)) {
Writable = true;
now = timer.Get_MicroSecond();
if (GetRawInputData(reinterpret_cast<HRAWINPUT>(msg.lParam),
RID_INPUT,
nullptr,
&size,
sizeof(RAWINPUTHEADER)) != 0 ||
size == 0) {
continue;
}
std::pmr::vector<BYTE> buf(&mono_arena);
buf.reserve(size);
if (GetRawInputData(reinterpret_cast<HRAWINPUT>(msg.lParam),
RID_INPUT,
buf.data(),
&size,
sizeof(RAWINPUTHEADER)) != size) {
continue;
}
const RAWINPUT *ri =
reinterpret_cast<const RAWINPUT *>(buf.data());
switch (ri->header.dwType) {
case RIM_TYPEMOUSE:
dtype = PDJE_Dev_Type::MOUSE;
PDJE_RAWINPUT::FillMouseInput(tempEv, ri);
break;
case RIM_TYPEKEYBOARD:
dtype = PDJE_Dev_Type::KEYBOARD;
PDJE_RAWINPUT::FillKeyboardInput(tempEv, ri);
if (isPressed.test(tempEv.keyboard.k) &&
tempEv.keyboard.pressed) {
Writable = false;
} else {
isPressed.set(tempEv.keyboard.k,
tempEv.keyboard.pressed);
}
break;
case RIM_TYPEHID:
dtype = PDJE_Dev_Type::HID;
hidEv.hid_buffer = PDJE_RAWINPUT::FillHIDInput(
hid_arena, ri, hidEv.hid_byte_size);
break;
default:
dtype = PDJE_Dev_Type::UNKNOWN;
break;
}
handlestr = std::to_string(
reinterpret_cast<uintptr_t>(ri->header.hDevice));
if (!unlisted_targets.empty()) {
if (!id_name.contains(handlestr)) {
if (GetRawInputDeviceInfoW(ri->header.hDevice,
RIDI_DEVICENAME,
nullptr,
&size) == (UINT)-1 ||
size == 0) {
} else {
std::wstring path(size, L'\0');
if (GetRawInputDeviceInfoW(ri->header.hDevice,
RIDI_DEVICENAME,
path.data(),
&size) == (UINT)-1) {
} else {
if (!path.empty() && path.back() == L'\0')
path.pop_back();
std::string device_path =
wstring_to_utf8_nt(path);
if (unlisted_targets.contains(device_path)) {
id_name[handlestr] =
unlisted_targets[device_path];
unlisted_targets.erase(device_path);
}
}
}
}
}
if (Writable) {
input_buffer.Write({ .type = dtype,
.event = tempEv,
.hid_event = hidEv,
.id = handlestr,
.microSecond = now });
}
}
while (PeekMessageW(&msg, nullptr, 0, WM_QUIT - 1, PM_REMOVE)) {
}
while (
PeekMessageW(&msg, nullptr, WM_QUIT + 1, 0xFFFF, PM_REMOVE)) {
}
}
}
}
void
OS_Input::work()
{
auto msgOnly = init();
if (!msgOnly)
return;
auto device_datas = config_data->get();
config_sync->arrive_and_wait();
if (device_datas.empty()) {
return;
}
std::vector<RAWINPUTDEVICE> devTypes;
bool hasKeyBoard = false;
bool hasMouse = false;
bool hasHID = false;
for (const auto &dev : device_datas) {
switch (dev.Type) {
case PDJE_Dev_Type::MOUSE:
hasMouse = true;
break;
case PDJE_Dev_Type::KEYBOARD:
hasKeyBoard = true;
break;
case PDJE_Dev_Type::HID:
hasHID = true;
default:
break;
}
unlisted_targets[dev.device_specific_id] = dev.Name;
}
if (hasKeyBoard) {
auto temp = RAWINPUTDEVICE{
0x01, 0x06, RIDEV_INPUTSINK | RIDEV_NOLEGACY, msgOnly
};
devTypes.push_back(temp);
}
if (hasMouse) {
auto temp = RAWINPUTDEVICE{
0x01, 0x02, RIDEV_INPUTSINK | RIDEV_NOLEGACY, msgOnly
};
devTypes.push_back(temp);
}
if (hasHID) {
auto temp = RAWINPUTDEVICE{
0x0C, 0x01, RIDEV_INPUTSINK | RIDEV_NOLEGACY, msgOnly
};
devTypes.push_back(temp);
}
auto regres = RegisterRawInputDevices(
devTypes.data(), devTypes.size(), sizeof(RAWINPUTDEVICE));
if (!regres) {
critlog("failed to register rawinput devices. maybe configed invalid "
"devices.");
return;
}
HANDLE task = nullptr;
DWORD idx = 0;
task = AvSetMmThreadCharacteristicsW(L"Games", &idx);
if (task) {
AvSetMmThreadPriority(task, AVRT_PRIORITY_HIGH);
}
// stop power throttling
#ifdef THREAD_POWER_THROTTLING_CURRENT_VERSION
THREAD_POWER_THROTTLING_STATE s{};
s.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
s.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
s.StateMask = 0; // Disable throttling
SetThreadInformation(
GetCurrentThread(), ThreadPowerThrottling, &s, sizeof(s));
#endif
ThreadID = GetCurrentThreadId();
bool ok = run_ok->get();
run_sync->arrive_and_wait();
if (!ok) {
if (task)
AvRevertMmThreadCharacteristics(task);
return;
}
run();
if (task)
AvRevertMmThreadCharacteristics(task);
return;
}
std::vector<RawDeviceData>
OS_Input::getRawDeviceDatas()
{
UINT num = 0;
if (GetRawInputDeviceList(nullptr, &num, sizeof(RAWINPUTDEVICELIST)) != 0 ||
num == 0)
return {};
std::vector<RAWINPUTDEVICELIST> list(num);
if (GetRawInputDeviceList(list.data(), &num, sizeof(RAWINPUTDEVICELIST)) ==
(UINT)-1)
return {};
std::vector<RawDeviceData> out;
out.reserve(num);
for (UINT i = 0; i < num; ++i) {
RawDeviceData dev;
auto h = list[i].hDevice;
UINT cbSize = dev.info.cbSize = sizeof(RID_DEVICE_INFO);
if (GetRawInputDeviceInfoW(h, RIDI_DEVICEINFO, &dev.info, &cbSize) ==
(UINT)-1)
continue;
UINT chars = 0;
GetRawInputDeviceInfoW(h, RIDI_DEVICENAME, nullptr, &chars);
if (chars > 0) {
std::wstring path(chars, L'\0');
if (GetRawInputDeviceInfoW(h, RIDI_DEVICENAME, &path[0], &chars) !=
(UINT)-1) {
if (!path.empty() && path.back() == L'\0')
path.pop_back();
}
dev.deviceHIDPath = path;
}
out.push_back(std::move(dev));
}
return out;
}
#include <iostream>
#include <filesystem>
std::string
OS_Input::hid_label_from_path(const std::wstring &path)
{
auto name = GetFriendlyNameFromHidPath(path);
return wstring_to_utf8_nt(name);
}
#include <iostream>
std::vector<DeviceData>
OS_Input::getDevices()
{
auto devs = getRawDeviceDatas();
std::vector<DeviceData> out;
out.reserve(devs.size());
for (auto &i : devs) {
DeviceData tempdata;
switch (i.info.dwType) {
case RIM_TYPEMOUSE:
tempdata.Type = PDJE_Dev_Type::MOUSE;
break;
case RIM_TYPEKEYBOARD:
tempdata.Type = PDJE_Dev_Type::KEYBOARD;
break;
case RIM_TYPEHID:
tempdata.Type = PDJE_Dev_Type::HID;
break;
default:
tempdata.Type = PDJE_Dev_Type::UNKNOWN;
break;
}
tempdata.Name = hid_label_from_path(i.deviceHIDPath);
tempdata.device_specific_id = wstring_to_utf8_nt(i.deviceHIDPath);
out.push_back(tempdata);
}
return out;
}
bool
OS_Input::kill()
{
return PostThreadMessageW(ThreadID, WM_QUIT, 0, 0);
}
void
OS_Input::TrigLoop()
{
worker.emplace(std::thread([this]() { this->work(); }));
}
void
OS_Input::ResetLoop()
{
worker->join();
worker.reset();
}
PDJE_INPUT_DATA_LINE
OS_Input::PullOutDataLine()
{
PDJE_INPUT_DATA_LINE dline;
dline.input_arena = &input_buffer;
dline.id_name_conv = &id_name;
return dline;
}