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