// SPDX-License-Identifier: MIT // // ES-DE // GuiDetectDevice.cpp // // Detect input devices (keyboards, joysticks and gamepads). // #include "guis/GuiDetectDevice.h" #include "InputManager.h" #include "Window.h" #include "components/TextComponent.h" #include "guis/GuiInputConfig.h" #include "utils/FileSystemUtil.h" #include "utils/StringUtil.h" #define HOLD_TIME 1000.0f GuiDetectDevice::GuiDetectDevice(bool firstRun, bool forcedConfig, const std::function& doneCallback) : mFirstRun {firstRun} , mForcedConfig {forcedConfig} , mRenderer {Renderer::getInstance()} , mBackground {":/graphics/frame.svg"} , mGrid {glm::ivec2 {1, 5}} { mHoldingConfig = nullptr; mHoldTime = 0; mDoneCallback = doneCallback; addChild(&mBackground); addChild(&mGrid); // Title. mTitle = std::make_shared(firstRun ? "WELCOME" : "CONFIGURE INPUT DEVICE", Font::get(FONT_SIZE_LARGE), mMenuColorTitle, ALIGN_CENTER); mGrid.setEntry(mTitle, glm::ivec2 {0, 0}, false, true, glm::ivec2 {1, 1}, GridFlags::BORDER_BOTTOM); // Device info. std::stringstream deviceInfo; int numDevices {InputManager::getInstance().getNumJoysticks()}; if (numDevices > 0) deviceInfo << numDevices << " GAMEPAD" << (numDevices > 1 ? "S" : "") << " DETECTED"; else deviceInfo << "NO GAMEPADS DETECTED"; if (numDevices > 1 && Settings::getInstance()->getBool("InputOnlyFirstController")) deviceInfo << " (ONLY ACCEPTING INPUT FROM FIRST CONTROLLER)"; mDeviceInfo = std::make_shared(deviceInfo.str(), Font::get(FONT_SIZE_SMALL), mMenuColorSecondary, ALIGN_CENTER); mGrid.setEntry(mDeviceInfo, glm::ivec2 {0, 1}, false, true); // Message. if (numDevices > 0) { mMsg1 = std::make_shared( "HOLD A BUTTON ON YOUR GAMEPAD OR KEYBOARD TO CONFIGURE IT", Font::get(FONT_SIZE_SMALL), mMenuColorPrimary, ALIGN_CENTER); } else { mMsg1 = std::make_shared("HOLD A BUTTON ON YOUR KEYBOARD TO CONFIGURE IT", Font::get(FONT_SIZE_SMALL), mMenuColorPrimary, ALIGN_CENTER); } mGrid.setEntry(mMsg1, glm::ivec2 {0, 2}, false, true); const std::string msg2str {firstRun ? "PRESS ESC TO SKIP (OR F4 TO QUIT AT ANY TIME)" : "PRESS ESC TO CANCEL"}; mMsg2 = std::make_shared(msg2str, Font::get(FONT_SIZE_SMALL), mMenuColorPrimary, ALIGN_CENTER); mGrid.setEntry(mMsg2, glm::ivec2 {0, 3}, false, true); // Currently held device. mDeviceHeld = std::make_shared("", Font::get(FONT_SIZE_MEDIUM), 0xFFFFFFFF, ALIGN_CENTER); mGrid.setEntry(mDeviceHeld, glm::ivec2 {0, 4}, false, true); // Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent // regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference. const float aspectValue {1.778f / mRenderer->getScreenAspectRatio()}; const float width {glm::clamp(0.60f * aspectValue, 0.50f, (mRenderer->getIsVerticalOrientation() ? 0.85f : 0.80f)) * mRenderer->getScreenWidth()}; setSize(width, mRenderer->getScreenHeight() * 0.5f); setPosition((mRenderer->getScreenWidth() - mSize.x) / 2.0f, (mRenderer->getScreenHeight() - mSize.y) / 2.0f); } void GuiDetectDevice::onSizeChanged() { mBackground.fitTo(mSize); mGrid.setSize(mSize); mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y); mGrid.setRowHeightPerc(2, mMsg1->getFont()->getHeight() / mSize.y); mGrid.setRowHeightPerc(3, mMsg2->getFont()->getHeight() / mSize.y); } bool GuiDetectDevice::input(InputConfig* config, Input input) { if (!mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY && input.value && input.id == SDLK_ESCAPE) { if (mDoneCallback) mDoneCallback(); // Cancel the configuration. delete this; // Delete GUI element. return true; } // First run, but the user chooses to skip the configuration. This will default to the // built-in keyboard mappings. else if (mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY && input.value && input.id == SDLK_ESCAPE) { if (mDoneCallback) mDoneCallback(); delete this; // Delete GUI element. return true; } if (input.type == TYPE_BUTTON || input.type == TYPE_AXIS || input.type == TYPE_KEY || input.type == TYPE_CEC_BUTTON) { if (input.value && mHoldingConfig == nullptr) { // Started holding. mHoldingConfig = config; mHoldTime = static_cast(HOLD_TIME); mDeviceHeld->setText(Utils::String::toUpper(config->getDeviceName())); } else if (!input.value && mHoldingConfig == config) { // Cancel. mHoldingConfig = nullptr; mDeviceHeld->setText(""); } } return true; } void GuiDetectDevice::update(int deltaTime) { if (mHoldingConfig) { // If ES-DE starts and if a known device is connected after startup, then skip // controller configuration unless the flag to force the configuration was passed // on the command line. if (!mForcedConfig && mFirstRun && Utils::FileSystem::exists(InputManager::getConfigPath()) && InputManager::getInstance().getNumConfiguredDevices() > 0) { if (mDoneCallback) mDoneCallback(); delete this; // Delete GUI element. } else { mHoldTime -= deltaTime; // Fade in device name. const float t {std::fabs((static_cast(mHoldTime) / HOLD_TIME) - 1.0f)}; mDeviceHeld->setColor(mMenuColorDetectDeviceHeld | static_cast(t * 255.0f)); if (mHoldTime <= 0) { // A device was selected. mWindow->pushGui(new GuiInputConfig(mHoldingConfig, true, mDoneCallback)); delete this; } } } }