///////////////////////////////////////////////////////////////////////////////
//
// Rumblebutt Raw Input to UDP
//
///////////////////////////////////////////////////////////////////////////////
// Attention, including winsock2 and Windows does clash, to prevent that, define _WINSOCKAPI_ like this first:
#define _WINSOCKAPI_
#include <Windows.h>
#include <tchar.h>
#include <math.h>
#include <hidsdi.h>
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib") //Winsock Library
#define _USE_MATH_DEFINES
#define SERVER "xxx.xxx.xxx.xxx" //ip address of udp server
#define BUFLEN 512 //Max length of buffer
#define PORT 4210 //The port on which to listen for incoming data
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define WC_MAINFRAME TEXT("MainFrame")
#define CHECK(exp) { if(!(exp)) goto Error; }
#define SAFE_FREE(p) { if(p) { HeapFree(hHeap, 0, p); (p) = NULL; } }
//
// Global variables
//
LONG lAxisX;
LONG lAxisY;
LONG lAxisZ;
LONG lAxisRz;
LONG lHat;
INT g_NumberOfButtons;
//Prototyp winsock
int startWinsock(void);
void ParseRawInput(PRAWINPUT pRawInput)
{
PHIDP_PREPARSED_DATA pPreparsedData;
HIDP_CAPS Caps;
PHIDP_BUTTON_CAPS pButtonCaps;
PHIDP_VALUE_CAPS pValueCaps;
USHORT capsLength;
UINT bufferSize=0;
HANDLE hHeap;
ULONG value;
pPreparsedData = NULL;
pButtonCaps = NULL;
pValueCaps = NULL;
hHeap = GetProcessHeap();
//
// Get the preparsed data block
//
CHECK( GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, NULL, &bufferSize) == 0 );
CHECK( pPreparsedData = (PHIDP_PREPARSED_DATA)HeapAlloc(hHeap, 0, bufferSize) );
CHECK( (int)GetRawInputDeviceInfo(pRawInput->header.hDevice, RIDI_PREPARSEDDATA, pPreparsedData, &bufferSize) >= 0 );
//
// Get the joystick's capabilities
//
CHECK( HidP_GetCaps(pPreparsedData, &Caps) == HIDP_STATUS_SUCCESS )
g_NumberOfButtons = 0;
// Value caps
CHECK( pValueCaps = (PHIDP_VALUE_CAPS)HeapAlloc(hHeap, 0, sizeof(HIDP_VALUE_CAPS) * Caps.NumberInputValueCaps) );
capsLength = Caps.NumberInputValueCaps;
CHECK(HidP_GetValueCaps(HidP_Input, pValueCaps, &capsLength, pPreparsedData) == HIDP_STATUS_SUCCESS)
//
// Get the state of discrete-valued-controls
//
for(int i = 0; i < Caps.NumberInputValueCaps; i++)
{
CHECK(
HidP_GetUsageValue(
HidP_Input, pValueCaps[i].UsagePage, 0, pValueCaps[i].Range.UsageMin, &value, pPreparsedData,
(PCHAR)pRawInput->data.hid.bRawData, pRawInput->data.hid.dwSizeHid
) == HIDP_STATUS_SUCCESS );
switch(pValueCaps[i].Range.UsageMin)
{
case 0x30: // X-axis
lAxisX = (LONG)value - 128;
break;
case 0x31: // Y-axis
lAxisY = (LONG)value - 128;
break;
case 0x32: // Z-axis
lAxisZ = (LONG)value - 128;
break;
case 0x35: // Rotate-Z
lAxisRz = (LONG)value - 128;
break;
case 0x39: // Hat Switch
lHat = value;
break;
}
}
//
// Clean up
//
Error:
SAFE_FREE(pPreparsedData);
SAFE_FREE(pButtonCaps);
SAFE_FREE(pValueCaps);
}
void DrawCrosshair(HDC hDC, int x, int y, LONG xVal, LONG yVal)
{
Rectangle(hDC, x, y, x + 256, y + 256);
MoveToEx(hDC, x + xVal - 5 + 128, y + yVal + 128, NULL);
LineTo(hDC, x + xVal + 5 + 128, y + yVal + 128);
MoveToEx(hDC, x + xVal + 128, y + yVal - 5 + 128, NULL);
LineTo(hDC, x + xVal + 128, y + yVal + 5 + 128);
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
//
// Register for joystick devices
//
RAWINPUTDEVICE rid;
rid.usUsagePage = 1;
rid.usUsage = 4; // Joystick
// RIDEV_INPUTSINK is needed to let this program receive input even if not in foreground/focused
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = hWnd;
if(!RegisterRawInputDevices(&rid, 1, sizeof(RAWINPUTDEVICE)))
return -1;
}
return 0;
case WM_INPUT:
{
//
// Get the pointer to the raw device data, process it and update the window
//
PRAWINPUT pRawInput;
UINT bufferSize=0;
HANDLE hHeap;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &bufferSize, sizeof(RAWINPUTHEADER));
hHeap = GetProcessHeap();
pRawInput = (PRAWINPUT)HeapAlloc(hHeap, 0, bufferSize);
if(!pRawInput)
return 0;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, pRawInput, &bufferSize, sizeof(RAWINPUTHEADER));
ParseRawInput(pRawInput);
HeapFree(hHeap, 0, pRawInput);
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}
return 0;
case WM_PAINT:
{
//
// Draw the buttons and axis-values
//
PAINTSTRUCT ps;
HDC hDC;
hDC = BeginPaint(hWnd, &ps);
SetBkMode(hDC, TRANSPARENT);
DrawCrosshair(hDC, 50, 50, lAxisZ, 0);
TCHAR text[256];
swprintf_s(text, 256, L"Throttle: %d", lAxisZ);
TextOut(hDC, 10, 10, text, wcslen(text));
EndPaint(hWnd, &ps);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
HWND hWnd;
MSG msg;
WNDCLASSEX wcex;
struct sockaddr_in si_other;
int s, slen = sizeof(si_other);
char buf[BUFLEN];
char message[BUFLEN];
WSADATA wsa;
//Initialise winsock
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
exit(EXIT_FAILURE);
}
//create socket
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR)
{
exit(EXIT_FAILURE);
}
//setup address structure
memset((char*)&si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
si_other.sin_addr.S_un.S_addr = inet_addr(SERVER);
//
// Register window class
//
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wcex.hInstance = hInstance;
wcex.lpfnWndProc = WindowProc;
wcex.lpszClassName = WC_MAINFRAME;
wcex.lpszMenuName = NULL;
wcex.style = CS_HREDRAW | CS_VREDRAW;
if(!RegisterClassEx(&wcex))
return -1;
//
// Create window
//
hWnd = CreateWindow(WC_MAINFRAME, TEXT("Rumblebutt Raw Input to UDP"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 400, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
//
// Message loop
//
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
int sendThrottle;
// The values -127 to 127 are divided into 4 cases
// Use this if throttle has backward gear, stop, and forward
if (lAxisZ > 84) {
// Backwards
sendThrottle = 20 + (lAxisZ - 84)*2;
}
else if (lAxisZ > 74) {
// No thrust, no rumble
// Close to full stop send nothing to eliminate high pitched sound from chair
sendThrottle = 0;
}
else if (lAxisZ > 0) {
// Forward positiv
sendThrottle = 75 - lAxisZ;
}
else {
// Forward negativ
sendThrottle = 75 + (lAxisZ*-1);
}
/* Use this if throttle in game is only forward or you can shift
if (lAxisZ > 0) {
// Backwards 42 Values
sendThrottle = 128 - lAxisZ;
// Close to full stop send nothing to eliminate high pitched sound from chair
if (sendThrottle < 10) sendThrottle = 0;
}
else {
// Forward negativ
sendThrottle = 126 + (lAxisZ * -1);
}
*/
//Send via UDP
char udpsend[4];
sprintf_s(udpsend, sizeof(udpsend), "%d", sendThrottle);
sendto(s, udpsend, strlen(udpsend), 0, (struct sockaddr*)&si_other, slen);
}
closesocket(s);
WSACleanup();
return (int)msg.wParam;
}
int startWinsock(void)
{
WSADATA wsa;
return WSAStartup(MAKEWORD(2, 0), &wsa);
}
Comments
Please log in or sign up to comment.