Skip to content

Instantly share code, notes, and snippets.

@charlie-x
Created November 26, 2020 20:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save charlie-x/c42655120e81716e4ff580bec0b3d205 to your computer and use it in GitHub Desktop.
Save charlie-x/c42655120e81716e4ff580bec0b3d205 to your computer and use it in GitHub Desktop.
kmotion dinput starter
#include "stdafx.h"
#define DIRECTINPUT_VERSION 0x0700
#include <wbemidl.h>
#include <dinput.h>
#include <xinput.h>
#pragma comment(lib,"xinput.lib")
#define SAFE_RELEASE(x) if(x) { x->Release(); x = NULL; }
//------------------------
// Enum each PNP device using WMI and check each device ID to see if it contains
// "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device
// Unfortunately this information can not be found by just using DirectInput
//-----------------------------------------------------------------------------
static BOOL IsXInputDevice(const GUID* pGuidProductFromDirectInput)
{
IWbemLocator* pIWbemLocator = NULL;
IEnumWbemClassObject* pEnumDevices = NULL;
IWbemClassObject* pDevices[20] = { 0 };
IWbemServices* pIWbemServices = NULL;
BSTR bstrNamespace = NULL;
BSTR bstrDeviceID = NULL;
BSTR bstrClassName = NULL;
DWORD uReturned = 0;
bool bIsXinputDevice = false;
UINT iDevice = 0;
VARIANT var;
HRESULT hr;
// CoInit if needed
hr = CoInitialize(NULL);
bool bCleanupCOM = SUCCEEDED(hr);
// So we can call VariantClear() later, even if we never had a successful IWbemClassObject::Get().
VariantInit(&var);
// Create WMI
hr = CoCreateInstance(__uuidof(WbemLocator),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IWbemLocator),
(LPVOID*)&pIWbemLocator);
if (FAILED(hr) || pIWbemLocator == NULL)
goto LCleanup;
bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); if (bstrNamespace == NULL) goto LCleanup;
bstrClassName = SysAllocString(L"Win32_PNPEntity"); if (bstrClassName == NULL) goto LCleanup;
bstrDeviceID = SysAllocString(L"DeviceID"); if (bstrDeviceID == NULL) goto LCleanup;
// Connect to WMI
hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L,
0L, NULL, NULL, &pIWbemServices);
if (FAILED(hr) || pIWbemServices == NULL)
goto LCleanup;
// Switch security level to IMPERSONATE.
CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL,
RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, &pEnumDevices);
if (FAILED(hr) || pEnumDevices == NULL)
goto LCleanup;
// Loop over all devices
for (;; )
{
// Get 20 at a time
hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned);
if (FAILED(hr))
goto LCleanup;
if (uReturned == 0)
break;
for (iDevice = 0; iDevice < uReturned; iDevice++)
{
// For each device, get its device ID
hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, NULL, NULL);
if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL)
{
// Check if the device ID contains "IG_". If it does, then it's an XInput device
// This information can not be found from DirectInput
if (wcsstr(var.bstrVal, L"IG_"))
{
// If it does, then get the VID/PID from var.bstrVal
DWORD dwPid = 0, dwVid = 0;
WCHAR* strVid = wcsstr(var.bstrVal, L"VID_");
if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1)
dwVid = 0;
WCHAR* strPid = wcsstr(var.bstrVal, L"PID_");
if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1)
dwPid = 0;
// Compare the VID/PID to the DInput device
DWORD dwVidPid = MAKELONG(dwVid, dwPid);
if (dwVidPid == pGuidProductFromDirectInput->Data1)
{
bIsXinputDevice = true;
goto LCleanup;
}
}
}
VariantClear(&var);
SAFE_RELEASE(pDevices[iDevice]);
}
}
LCleanup:
VariantClear(&var);
if (bstrNamespace)
SysFreeString(bstrNamespace);
if (bstrDeviceID)
SysFreeString(bstrDeviceID);
if (bstrClassName)
SysFreeString(bstrClassName);
for (iDevice = 0; iDevice < 20; iDevice++)
SAFE_RELEASE(pDevices[iDevice]);
SAFE_RELEASE(pEnumDevices);
SAFE_RELEASE(pIWbemLocator);
SAFE_RELEASE(pIWbemServices);
if (bCleanupCOM)
CoUninitialize();
return bIsXinputDevice;
}
// EnumJoysticksCallback()
// Called once for each enumerated joystick. If we find one, create a
// device interface on it so we can play with it.
BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance,
VOID* pContext)
{
HRESULT hr;
if (IsXInputDevice(&pdidInstance->guidProduct))
return DIENUM_CONTINUE;
// Device is verified not XInput, so add it to the list of DInput devices
return DIENUM_CONTINUE;
}
// Check deadzone on left thumbstick
static bool LStickInDeadzone(XINPUT_STATE& state)
{
short x = state.Gamepad.sThumbLX;
short y = state.Gamepad.sThumbLY;
if (x > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ||
x < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
{
// X axis outside of deadzone
return false;
}
if (y > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ||
y < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE)
{
// Y axis outside of deadzone
return false;
}
// One (or both) axis inside deadzone
return true;
}
// Check deadzone on right thumbstick
static bool RStickInDeadzone(XINPUT_STATE& state)
{
short x = state.Gamepad.sThumbRX;
short y = state.Gamepad.sThumbRY;
if (x > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ||
x < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
{
// X axis outside of deadzone
return false;
}
if (y > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ||
y < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE)
{
// Y axis outside of deadzone
return false;
}
// One (or both) axis inside deadzone
return true;
}
// calculate deadzones for analog inputs (alternate with normalise)
void CalculateDeadZone(XINPUT_STATE& state, const int deadZone)
{
float LX = state.Gamepad.sThumbLX;
float LY = state.Gamepad.sThumbLY;
//determine how far the controller is pushed
float magnitude = sqrt(LX * LX + LY * LY);
//determine the direction the controller is pushed
float normalizedLX = LX / magnitude;
float normalizedLY = LY / magnitude;
float normalizedMagnitude = 0;
//check if the controller is outside a circular dead zone
if (magnitude > deadZone)
{
//clip the magnitude at its expected maximum value
if (magnitude > 32767) magnitude = 32767;
//adjust magnitude relative to the end of the dead zone
magnitude -= deadZone;
//optionally normalize the magnitude with respect to its expected range
//giving a magnitude value of 0.0 to 1.0
normalizedMagnitude = magnitude / (32767 - deadZone);
}
else //if the controller is in the deadzone zero out the magnitude
{
magnitude = 0.0;
normalizedMagnitude = 0.0;
}
}
// SetVibrate()
// Control vibration motors on GamePad
//
// @index = controller index 0 .. XUSER_MAX_COUNT
// @leftValue = 0 .. 65535 left vibration amount
// @rightValue = 0 .. 65535 right vibration amount
void SetVibrate(DWORD index, unsigned short leftValue, unsigned short rightValue)
{
XINPUT_VIBRATION vibration;
ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
vibration.wLeftMotorSpeed = leftValue;
vibration.wRightMotorSpeed = rightValue;
XInputSetState(index, &vibration);
}
MMRESULT WINAPI myjoyGetPosEx(_In_ UINT uJoyID, _Out_ LPJOYINFOEX pji)
{
// this will return all ok for an XInput device that fails to run joystick info as well.
// the enumerate should skip XInput devices
if (joyGetPosEx(uJoyID, pji) == JOYERR_NOERROR) {
//return JOYERR_NOERROR;
}
XINPUT_STATE state;
ZeroMemory(&state, sizeof(XINPUT_STATE));
DWORD dwResult = XInputGetState(uJoyID, &state);
if (dwResult == ERROR_SUCCESS) {
// map Xinput to exJoy
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) {
pji->dwButtons |= JOY_BUTTON1;
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) {
pji->dwButtons |= JOY_BUTTON2;
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) {
pji->dwButtons |= JOY_BUTTON3;
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) {
pji->dwButtons |= JOY_BUTTON4;
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) {
pji->dwButtons |= JOY_BUTTON5;
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {
pji->dwButtons |= JOY_BUTTON6;
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {
pji->dwButtons |= JOY_BUTTON7;
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {
pji->dwButtons |= JOY_BUTTON8;
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) {
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) {
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) {
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) {
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) {
}
if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) {
}
// other analog inputs
// state.Gamepad.sThumbLX
// state.Gamepad.sThumbLY
// state.Gamepad.sThumbRX
// state.Gamepad.sThumbRY
const DWORD jScale = 32768;
if (!LStickInDeadzone(state)) {
// convert from - / + to 0 - 65536
pji->dwRpos = ((state.Gamepad.sThumbLX) + 32768) / jScale;
pji->dwZpos = ((state.Gamepad.sThumbLY) + 32768) / jScale;
}
else {
pji->dwRpos = 32767;
pji->dwZpos = 32767;
}
if (!RStickInDeadzone(state)) {
pji->dwXpos = ((state.Gamepad.sThumbRX) + 32768) / jScale;
pji->dwYpos = ((state.Gamepad.sThumbRY) + 32768) / jScale;
}
else {
pji->dwXpos = 32767;
pji->dwYpos = 32767;
}
return JOYERR_NOERROR;
}
return JOYERR_NOCANDO;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment