Skip to content

Instantly share code, notes, and snippets.

@JibbSmart
Last active October 10, 2023 01:24
Show Gist options
  • Save JibbSmart/8cbaba568c1c2e1193771459aa5385df to your computer and use it in GitHub Desktop.
Save JibbSmart/8cbaba568c1c2e1193771459aa5385df to your computer and use it in GitHub Desktop.
Quick ImGui application I made to figure out all the quality of life stuff folks might want for JoyShockLibrary 3.0
// Looks like this: https://x.com/JibbSmart/status/1642565648403550208?s=20
// Settings
int _gyroSpace = 2;
float _sensitivity = 1.f;
float _tightening = 2.f;
int _focusedDevice = 0;
bool _gyroActive = false;
int _defaultCalibrationMode = 1;
// Callback for applying our settings to new connections
void ConnectCallback(int deviceId)
{
JslSetGyroSpace(deviceId, _gyroSpace);
if (_defaultCalibrationMode > 0)
{
JslSetAutomaticCalibration(deviceId, true);
}
}
void MoveMouse(float x, float y)
{
static float accumulatedX = 0.f;
static float accumulatedY = 0.f;
accumulatedX += x;
accumulatedY += y;
int applicableX = (int)accumulatedX;
int applicableY = (int)accumulatedY;
accumulatedX -= applicableX;
accumulatedY -= applicableY;
INPUT input;
input.type = INPUT_MOUSE;
input.mi.mouseData = 0;
input.mi.time = 0;
input.mi.dx = applicableX;
input.mi.dy = applicableY;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
SendInput(1, &input, sizeof(input));
}
void DoGyro(float x, float y, float z, float deltaSeconds)
{
const float inputSize = sqrtf(x * x + y * y + z * z);
float tightenedSensitivity = _sensitivity * 50.f;
if (inputSize < _tightening && _tightening > 0)
{
tightenedSensitivity *= inputSize / _tightening;
}
MoveMouse(-y * tightenedSensitivity * deltaSeconds, -x * tightenedSensitivity * deltaSeconds);
}
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true;
switch (msg)
{
case WM_DEVICECHANGE:
JslConnectDevices();
break;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
{
_gyroActive = false;
}
break;
...
}
// Main code
int main(int, char**)
{
// Adapted from the Dear ImGui examples
// Initialize Direct3D
...
// Setup Dear ImGui context
...
JslSetConnectCallback(ConnectCallback);
JslConnectDevices();
// Main loop
bool done = false;
while (!done)
{
// Poll and handle messages (inputs, window resize, etc.)
// See the WndProc() function below for our to dispatch events to the Win32 backend.
MSG msg;
while (::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
if (msg.message == WM_QUIT)
done = true;
}
if (done)
break;
// Start the Dear ImGui frame
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// Where most of the JSL stuff happens...
{
static int numConnectedDevices = 0;
ImGui::Begin("JoyShockLibrary Sample", NULL, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text("For demonstrating many of the features of JoyShockLibrary");
int deviceHandles[16];
numConnectedDevices = JslGetConnectedDeviceHandles(deviceHandles, 16);
ImGui::PushID("##SideBySide");
ImGui::BeginGroup();
{
ImGui::Text("Connected Devices (%d)", numConnectedDevices);
if (_focusedDevice >= numConnectedDevices)
{
_focusedDevice = numConnectedDevices - 1;
}
if (_focusedDevice < 0)
{
_focusedDevice = 0;
}
for (int deviceIndex = 0; deviceIndex < numConnectedDevices; ++deviceIndex)
{
ImGui::PushID(deviceIndex);
const int deviceHandle = deviceHandles[deviceIndex];
switch (JslGetControllerType(deviceHandle))
{
case JS_TYPE_JOYCON_LEFT:
ImGui::RadioButton("Joy-Con (Left)", &_focusedDevice, deviceIndex);
break;
case JS_TYPE_JOYCON_RIGHT:
ImGui::RadioButton("Joy-Con (Right)", &_focusedDevice, deviceIndex);
break;
case JS_TYPE_PRO_CONTROLLER:
ImGui::RadioButton("Pro Controller", &_focusedDevice, deviceIndex);
break;
case JS_TYPE_DS4:
ImGui::RadioButton("DualShock 4", &_focusedDevice, deviceIndex);
break;
case JS_TYPE_DS:
ImGui::RadioButton("DualSense", &_focusedDevice, deviceIndex);
break;
default:
ImGui::RadioButton("Unknown Controller", &_focusedDevice, deviceIndex);
break;
}
ImGui::PopID();
}
if (numConnectedDevices > 0)
{
const int deviceHandle = deviceHandles[_focusedDevice];
float velocityX, velocityY, velocityZ;
JslGetAndFlushAccumulatedGyro(deviceHandle, velocityX, velocityY, velocityZ);
JSL_SETTINGS infoAndSettings = JslGetControllerInfoAndSettings(deviceHandle);
ImVec4 deviceColour;
deviceColour.w = 1.f;
deviceColour.x = ((infoAndSettings.colour >> 16) & 0xFF) / 255.f;
deviceColour.y = ((infoAndSettings.colour >> 8) & 0xFF) / 255.f;
deviceColour.z = (infoAndSettings.colour & 0xFF) / 255.f;
// if this controller has a light, use the colour picker here
if (infoAndSettings.controllerType <= JS_TYPE_PRO_CONTROLLER)
{
ImGui::PushStyleColor(ImGuiCol_Button, deviceColour);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, deviceColour);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, deviceColour);
ImGui::Button("##Colour", ImVec2(300, 20));
ImGui::PopStyleColor(3);
}
else
{
// playstation controller has LED we can set
float ledColour[3] = { deviceColour.x, deviceColour.y, deviceColour.z };
if (ImGui::ColorEdit3("LED", ledColour))
{
ImColor selectedColour = ImColor(ledColour[0], ledColour[1], ledColour[2]);
ImU32 colourAsInt = (ImU32)selectedColour;
// but, imgui's colour is ABGR, and JSL is just 0RGB, so...
int jslColour = (0xFF0000 & (colourAsInt << 16)) | (0xFF00 & colourAsInt) | (0xFF & (colourAsInt >> 16));
JslSetLightColour(deviceHandle, (ImU32)jslColour);
}
}
float offsetX, offsetY, offsetZ;
JslGetCalibrationOffset(deviceHandle, offsetX, offsetY, offsetZ);
ImGui::Text("Calibration Offset =\n\t(%04.3f, %04.3f, %04.3f)", offsetX, offsetY, offsetZ);
if (infoAndSettings.autoCalibrationEnabled)
{
ImGui::Text("Calibration Mode: Automatic");
}
else
{
ImGui::Text("Calibration Mode: Manual");
}
JSL_AUTO_CALIBRATION autoCalibrationStatus = JslGetAutoCalibrationStatus(deviceHandle);
if (autoCalibrationStatus.autoCalibrationEnabled)
{
if (autoCalibrationStatus.isSteady)
{
ImGui::Text("Steady, Calibration Confidence: %1.1f", autoCalibrationStatus.confidence);
}
else
{
ImGui::Text("Moving! Calibration Confidence: %1.1f", autoCalibrationStatus.confidence);
}
}
if (ImGui::Button("Reset"))
{
JslResetContinuousCalibration(deviceHandle);
}
ImGui::SameLine();
if (infoAndSettings.isCalibrating)
{
if (ImGui::Button("Finish Manual"))
{
JslPauseContinuousCalibration(deviceHandle);
}
}
else if (ImGui::Button("Start Manual"))
{
JslSetAutomaticCalibration(deviceHandle, false);
JslStartContinuousCalibration(deviceHandle);
}
if (!infoAndSettings.isCalibrating && !infoAndSettings.autoCalibrationEnabled)
{
ImGui::SameLine();
if (ImGui::Button("Start Automatic"))
{
JslPauseContinuousCalibration(deviceHandle);
JslSetAutomaticCalibration(deviceHandle, true);
}
}
if (_gyroActive)
{
if (ImGui::Button("[ESC] Disable Gyro Mouse", ImVec2(300, 80)))
{
_gyroActive = false;
}
else
{
float frameTime = 1.f / io.Framerate;
DoGyro(velocityX, velocityY, velocityZ, frameTime);
}
}
else if (ImGui::Button("Enable Gyro Mouse", ImVec2(300, 80)))
{
_gyroActive = true;
}
MOTION_STATE motionState = JslGetMotionState(deviceHandle);
ImGui::Text("Gravity =\n\t(%04.3f, %04.3f, %04.3f)", motionState.gravX, motionState.gravY, motionState.gravZ);
ImGui::Text("Acceleration =\n\t(%04.3f, %04.3f, %04.3f)", motionState.accelX, motionState.accelY, motionState.accelZ);
ImGui::Text("Quaternion =\n\t(%04.3f, %04.3f, %04.3f, %04.3f)", motionState.quatW, motionState.quatX, motionState.quatY, motionState.quatZ);
}
else
{
ImGui::Button("No Gyro Devices Connected", ImVec2(300, 80));
}
}
ImGui::EndGroup(); ImGui::SameLine();
ImGui::BeginGroup();
{
ImGui::PushItemWidth(300);
ImGui::SliderFloat("##Sensitivity", &_sensitivity, 0.0f, 10.0f, "Sensitivity = %.1f");
ImGui::SliderFloat("##Tightening", &_tightening, 0.0f, 20.0f, "Tightening = %.1f");
ImGui::Text("Gyro Space");
bool updateGyroSpace = ImGui::RadioButton("Local", &_gyroSpace, 0); ImGui::SameLine();
updateGyroSpace |= ImGui::RadioButton("World", &_gyroSpace, 1); ImGui::SameLine();
updateGyroSpace |= ImGui::RadioButton("Player", &_gyroSpace, 2);
if (updateGyroSpace)
{
for (int deviceIndex = 0; deviceIndex < numConnectedDevices; ++deviceIndex)
{
const int deviceHandle = deviceHandles[deviceIndex];
JslSetGyroSpace(deviceHandle, _gyroSpace);
}
}
ImGui::PopItemWidth();
}
ImGui::EndGroup();
ImGui::PopID();
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::End();
}
// Render
...
}
// Cleanup
...
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy