#include "InputEditor.h" #include "Controller.h" #include "Window.h" #include "Lib/ImGui/imgui.h" #include "ImGuiImpl.h" #include "Utils/StringHelper.h" #include "Lib/ImGui/imgui_internal.h" #include "Cvar.h" namespace Ship { #define SEPARATION() ImGui::Dummy(ImVec2(0, 5)) void InputEditor::Init() { BtnReading = -1; } std::shared_ptr GetControllerPerSlot(int slot) { auto controlDeck = Ship::Window::GetInstance()->GetControlDeck(); return controlDeck->GetPhysicalDeviceFromVirtualSlot(slot); } void InputEditor::DrawButton(const char* label, int32_t n64Btn) { const std::shared_ptr backend = GetControllerPerSlot(CurrentPort); float size = 40; bool readingMode = BtnReading == n64Btn; bool disabled = (BtnReading != -1 && !readingMode) || !backend->Connected() || backend->GetGuid() == "Auto"; ImVec2 len = ImGui::CalcTextSize(label); ImVec2 pos = ImGui::GetCursorPos(); ImGui::SetCursorPosY(pos.y + len.y / 4); ImGui::SetCursorPosX(pos.x + abs(len.x - size)); ImGui::Text("%s", label); ImGui::SameLine(); ImGui::SetCursorPosY(pos.y); if(disabled) { ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f); } if(readingMode) { const int32_t btn = backend->ReadRawPress(); if(btn != -1) { backend->SetButtonMapping(CurrentPort, n64Btn, btn); BtnReading = -1; // avoid immediately triggering another button during gamepad nav ImGui::SetKeyboardFocusHere(0); } } const std::string BtnName = backend->GetButtonName(CurrentPort, n64Btn); if (ImGui::Button(StringHelper::Sprintf("%s##HBTNID_%d", readingMode ? "Press a Key..." : BtnName.c_str(), n64Btn).c_str())) { BtnReading = n64Btn; backend->ClearRawPress(); } if(disabled) { ImGui::PopItemFlag(); ImGui::PopStyleVar(); } } void InputEditor::DrawVirtualStick(const char* label, ImVec2 stick) { ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 5, ImGui::GetCursorPos().y)); ImGui::BeginChild(label, ImVec2(68, 75), false); ImDrawList* draw_list = ImGui::GetWindowDrawList(); const ImVec2 p = ImGui::GetCursorScreenPos(); float sz = 45.0f; float rad = sz * 0.5f; ImVec2 pos = ImVec2(p.x + sz * 0.5f + 12, p.y + sz * 0.5f + 11); float stickX = (stick.x / 83.0f) * (rad * 0.5f); float stickY = -(stick.y / 83.0f) * (rad * 0.5f); ImVec4 rect = ImVec4(p.x + 2, p.y + 2, 65, 65); draw_list->AddRect(ImVec2(rect.x, rect.y), ImVec2(rect.x + rect.z, rect.y + rect.w), ImColor(100, 100, 100, 255), 0.0f, 0, 1.5f); draw_list->AddCircleFilled(pos, rad, ImColor(130, 130, 130, 255), 8); draw_list->AddCircleFilled(ImVec2(pos.x + stickX, pos.y + stickY), 5, ImColor(15, 15, 15, 255), 7); ImGui::EndChild(); } void InputEditor::DrawControllerSchema() { auto controlDeck = Ship::Window::GetInstance()->GetControlDeck(); auto Backend = controlDeck->GetPhysicalDeviceFromVirtualSlot(CurrentPort); auto profile = Backend->getProfile(CurrentPort); bool IsKeyboard = Backend->GetGuid() == "Keyboard" || Backend->GetGuid() == "Auto" || !Backend->Connected(); std::string ControllerName = Backend->GetControllerName(); if (ImGui::BeginCombo("##ControllerEntries", ControllerName.c_str())) { for (uint8_t i = 0; i < controlDeck->GetNumPhysicalDevices(); i++) { std::string DeviceName = controlDeck->GetPhysicalDevice(i)->GetControllerName(); if (DeviceName != "Keyboard" && DeviceName != "Auto") { DeviceName += "##"+std::to_string(i); } if (ImGui::Selectable(DeviceName.c_str(), i == controlDeck->GetVirtualDevice(CurrentPort))) { controlDeck->SetPhysicalDevice(CurrentPort, i); } } ImGui::EndCombo(); } ImGui::SameLine(); if(ImGui::Button("Refresh")) { controlDeck->ScanPhysicalDevices(); } SohImGui::BeginGroupPanel("Buttons", ImVec2(150, 20)); DrawButton("A", BTN_A); DrawButton("B", BTN_B); DrawButton("L", BTN_L); DrawButton("R", BTN_R); DrawButton("Z", BTN_Z); DrawButton("START", BTN_START); SEPARATION(); #ifdef __SWITCH__ SohImGui::EndGroupPanel(IsKeyboard ? 7.0f : 56.0f); #else SohImGui::EndGroupPanel(IsKeyboard ? 7.0f : 48.0f); #endif ImGui::SameLine(); SohImGui::BeginGroupPanel("Digital Pad", ImVec2(150, 20)); DrawButton("Up", BTN_DUP); DrawButton("Down", BTN_DDOWN); DrawButton("Left", BTN_DLEFT); DrawButton("Right", BTN_DRIGHT); SEPARATION(); #ifdef __SWITCH__ SohImGui::EndGroupPanel(IsKeyboard ? 53.0f : 122.0f); #else SohImGui::EndGroupPanel(IsKeyboard ? 53.0f : 94.0f); #endif ImGui::SameLine(); SohImGui::BeginGroupPanel("Analog Stick", ImVec2(150, 20)); DrawButton("Up", BTN_STICKUP); DrawButton("Down", BTN_STICKDOWN); DrawButton("Left", BTN_STICKLEFT); DrawButton("Right", BTN_STICKRIGHT); if (!IsKeyboard) { ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8); DrawVirtualStick("##MainVirtualStick", ImVec2(Backend->getLeftStickX(CurrentPort), Backend->getLeftStickY(CurrentPort))); ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); #ifdef __WIIU__ ImGui::BeginChild("##MSInput", ImVec2(90 * 2, 50 * 2), false); #else ImGui::BeginChild("##MSInput", ImVec2(90, 50), false); #endif ImGui::Text("Deadzone"); #ifdef __WIIU__ ImGui::PushItemWidth(80 * 2); #else ImGui::PushItemWidth(80); #endif // The window has deadzone per stick, so we need to // set the deadzone for both left stick axes here // SDL_CONTROLLER_AXIS_LEFTX: 0 // SDL_CONTROLLER_AXIS_LEFTY: 1 ImGui::InputFloat("##MDZone", &profile->AxisDeadzones[0], 1.0f, 0.0f, "%.0f"); profile->AxisDeadzones[1] = profile->AxisDeadzones[0]; ImGui::PopItemWidth(); ImGui::EndChild(); } else { ImGui::Dummy(ImVec2(0, 6)); } #ifdef __SWITCH__ SohImGui::EndGroupPanel(IsKeyboard ? 52.0f : 52.0f); #else SohImGui::EndGroupPanel(IsKeyboard ? 52.0f : 24.0f); #endif ImGui::SameLine(); if (!IsKeyboard) { ImGui::SameLine(); SohImGui::BeginGroupPanel("Right Stick", ImVec2(150, 20)); DrawButton("Up", BTN_VSTICKUP); DrawButton("Down", BTN_VSTICKDOWN); DrawButton("Left", BTN_VSTICKLEFT); DrawButton("Right", BTN_VSTICKRIGHT); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8); // 2 is the SDL value for right stick X axis // 3 is the SDL value for right stick Y axis. DrawVirtualStick("##RightVirtualStick", ImVec2(Backend->getRightStickX(CurrentPort), Backend->getRightStickY(CurrentPort))); ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); #ifdef __WIIU__ ImGui::BeginChild("##CSInput", ImVec2(90 * 2, 85 * 2), false); #else ImGui::BeginChild("##CSInput", ImVec2(90, 85), false); #endif ImGui::Text("Deadzone"); #ifdef __WIIU__ ImGui::PushItemWidth(80 * 2); #else ImGui::PushItemWidth(80); #endif // The window has deadzone per stick, so we need to // set the deadzone for both right stick axes here // SDL_CONTROLLER_AXIS_RIGHTX: 2 // SDL_CONTROLLER_AXIS_RIGHTY: 3 ImGui::InputFloat("##MDZone", &profile->AxisDeadzones[2], 1.0f, 0.0f, "%.0f"); profile->AxisDeadzones[3] = profile->AxisDeadzones[2]; ImGui::PopItemWidth(); ImGui::EndChild(); #ifdef __SWITCH__ SohImGui::EndGroupPanel(43.0f); #else SohImGui::EndGroupPanel(14.0f); #endif } if(Backend->CanGyro()) { #ifndef __WIIU__ ImGui::SameLine(); #endif SohImGui::BeginGroupPanel("Gyro Options", ImVec2(175, 20)); float cursorX = ImGui::GetCursorPosX() + 5; ImGui::SetCursorPosX(cursorX); ImGui::Checkbox("Enable Gyro", &profile->UseGyro); ImGui::SetCursorPosX(cursorX); ImGui::Text("Gyro Sensitivity: %d%%", static_cast(100.0f * profile->GyroData[GYRO_SENSITIVITY])); #ifdef __WIIU__ ImGui::PushItemWidth(135.0f * 2); #else ImGui::PushItemWidth(135.0f); #endif ImGui::SetCursorPosX(cursorX); ImGui::SliderFloat("##GSensitivity", &profile->GyroData[GYRO_SENSITIVITY], 0.0f, 1.0f, ""); ImGui::PopItemWidth(); ImGui::Dummy(ImVec2(0, 1)); ImGui::SetCursorPosX(cursorX); if (ImGui::Button("Recalibrate Gyro##RGyro")) { profile->GyroData[DRIFT_X] = 0.0f; profile->GyroData[DRIFT_Y] = 0.0f; } ImGui::SetCursorPosX(cursorX); DrawVirtualStick("##GyroPreview", ImVec2(-10.0f * Backend->getGyroY(CurrentPort), 10.0f * Backend->getGyroX(CurrentPort))); ImGui::SameLine(); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5); #ifdef __WIIU__ ImGui::BeginChild("##GyInput", ImVec2(90 * 2, 85 * 2), false); #else ImGui::BeginChild("##GyInput", ImVec2(90, 85), false); #endif ImGui::Text("Drift X"); #ifdef __WIIU__ ImGui::PushItemWidth(80 * 2); #else ImGui::PushItemWidth(80); #endif ImGui::InputFloat("##GDriftX", &profile->GyroData[DRIFT_X], 1.0f, 0.0f, "%.1f"); ImGui::PopItemWidth(); ImGui::Text("Drift Y"); #ifdef __WIIU__ ImGui::PushItemWidth(80 * 2); #else ImGui::PushItemWidth(80); #endif ImGui::InputFloat("##GDriftY", &profile->GyroData[DRIFT_Y], 1.0f, 0.0f, "%.1f"); ImGui::PopItemWidth(); ImGui::EndChild(); #ifdef __SWITCH__ SohImGui::EndGroupPanel(46.0f); #else SohImGui::EndGroupPanel(14.0f); #endif } ImGui::SameLine(); const ImVec2 cursor = ImGui::GetCursorPos(); SohImGui::BeginGroupPanel("C-Buttons", ImVec2(158, 20)); DrawButton("Up", BTN_CUP); DrawButton("Down", BTN_CDOWN); DrawButton("Left", BTN_CLEFT); DrawButton("Right", BTN_CRIGHT); ImGui::Dummy(ImVec2(0, 5)); SohImGui::EndGroupPanel(); ImGui::SetCursorPosX(cursor.x); #ifdef __SWITCH__ ImGui::SetCursorPosY(cursor.y + 167); #elif defined(__WIIU__) ImGui::SetCursorPosY(cursor.y + 120 * 2); #else ImGui::SetCursorPosY(cursor.y + 120); #endif SohImGui::BeginGroupPanel("Options", ImVec2(158, 20)); float cursorX = ImGui::GetCursorPosX() + 5; ImGui::SetCursorPosX(cursorX); ImGui::Checkbox("Rumble Enabled", &profile->UseRumble); if (Backend->CanRumble()) { ImGui::SetCursorPosX(cursorX); ImGui::Text("Rumble Force: %d%%", static_cast(100.0f * profile->RumbleStrength)); ImGui::SetCursorPosX(cursorX); #ifdef __WIIU__ ImGui::PushItemWidth(135.0f * 2); #else ImGui::PushItemWidth(135.0f); #endif ImGui::SliderFloat("##RStrength", &profile->RumbleStrength, 0.0f, 1.0f, ""); ImGui::PopItemWidth(); } ImGui::Dummy(ImVec2(0, 5)); SohImGui::EndGroupPanel(IsKeyboard ? 0.0f : 2.0f); } void InputEditor::DrawHud() { if (!this->Opened) { BtnReading = -1; CVar_SetS32("gControllerConfigurationEnabled", 0); return; } #ifdef __SWITCH__ ImVec2 minSize = ImVec2(641, 250); ImVec2 maxSize = ImVec2(2200, 505); #elif defined(__WIIU__) ImVec2 minSize = ImVec2(641 * 2, 250 * 2); ImVec2 maxSize = ImVec2(1200 * 2, 290 * 2); #else ImVec2 minSize = ImVec2(641, 250); ImVec2 maxSize = ImVec2(1200, 290); #endif ImGui::SetNextWindowSizeConstraints(minSize, maxSize); //OTRTODO: Disable this stupid workaround ( ReadRawPress() only works when the window is on the main viewport ) ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); ImGui::Begin("Controller Configuration", &this->Opened, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize); ImGui::BeginTabBar("##Controllers"); for (int i = 0; i < 4; i++) { if (ImGui::BeginTabItem(StringHelper::Sprintf("Port %d", i + 1).c_str())) { CurrentPort = i; ImGui::EndTabItem(); } } ImGui::EndTabBar(); // Draw current cfg DrawControllerSchema(); ImGui::End(); } bool InputEditor::IsOpened() { return Opened; } void InputEditor::Open() { Opened = true; } void InputEditor::Close() { Opened = false; } }