diff --git a/include/services/hid.hpp b/include/services/hid.hpp index a0eefb1c..642cd6f3 100644 --- a/include/services/hid.hpp +++ b/include/services/hid.hpp @@ -27,6 +27,12 @@ namespace HID::Keys { GPIO0Inv = 1 << 12, // Inverted value of GPIO bit 0 GPIO14Inv = 1 << 13, // Inverted value of GPIO bit 14 + // CirclePad Pro buttons. We store them in the HID service for ease, even though they're only used by the IR service + // Whenever the HID service writes to shared memory, we remember to mask them out + ZL = 1 << 14, + ZR = 1 << 15, + CirclePadProButtons = ZL | ZR, + CirclePadRight = 1 << 28, // X >= 41 CirclePadLeft = 1 << 29, // X <= -41 CirclePadUp = 1 << 30, // Y >= 41 @@ -58,6 +64,9 @@ class HIDService { s16 roll, pitch, yaw; // Gyroscope state s16 accelX, accelY, accelZ; // Accelerometer state + // New 3DS/CirclePad Pro C-stick state + s16 cStickX, cStickY; + bool accelerometerEnabled; bool eventsInitialized; bool gyroEnabled; @@ -113,7 +122,7 @@ class HIDService { // Turn bits 28 and 29 off in the new button state, which indicate whether the circlepad is steering left or right // Then, set them according to the new value of x - newButtons &= ~0x3000'0000; + newButtons &= ~(HID::Keys::CirclePadLeft | HID::Keys::CirclePadRight); if (x >= 41) // Pressing right newButtons |= 1 << 28; else if (x <= -41) // Pressing left @@ -125,13 +134,19 @@ class HIDService { // Turn bits 30 and 31 off in the new button state, which indicate whether the circlepad is steering up or down // Then, set them according to the new value of y - newButtons &= ~0xC000'0000; + newButtons &= ~(HID::Keys::CirclePadUp | HID::Keys::CirclePadDown); if (y >= 41) // Pressing up newButtons |= 1 << 30; else if (y <= -41) // Pressing down newButtons |= 1 << 31; } + void setCStickX(s16 x) { cStickX = x; } + void setCStickY(s16 y) { cStickY = y; } + + s16 getCStickX() { return cStickX; } + s16 getCStickY() { return cStickY; } + void setRoll(s16 value) { roll = value; } void setPitch(s16 value) { pitch = value; } void setYaw(s16 value) { yaw = value; } @@ -157,9 +172,6 @@ class HIDService { touchScreenPressed = true; } - void releaseTouchScreen() { - touchScreenPressed = false; - } - + void releaseTouchScreen() { touchScreenPressed = false; } bool isTouchScreenPressed() { return touchScreenPressed; } }; diff --git a/include/services/ir/circlepad_pro.hpp b/include/services/ir/circlepad_pro.hpp index 2202c1cf..6895ab6f 100644 --- a/include/services/ir/circlepad_pro.hpp +++ b/include/services/ir/circlepad_pro.hpp @@ -8,7 +8,6 @@ namespace IR { public: struct ButtonState { static constexpr int C_STICK_CENTER = 0x800; - static constexpr int C_STICK_RADIUS = 0x7FF; union { BitField<0, 8, u32> header; diff --git a/include/services/ir/ir_user.hpp b/include/services/ir/ir_user.hpp index 221367f9..556ff1d9 100644 --- a/include/services/ir/ir_user.hpp +++ b/include/services/ir/ir_user.hpp @@ -8,6 +8,7 @@ #include "logger.hpp" #include "memory.hpp" #include "result/result.hpp" +#include "services/hid.hpp" #include "services/ir/circlepad_pro.hpp" // Circular dependencies in this project? Never @@ -23,6 +24,8 @@ class IRUserService { Handle handle = KernelHandles::IR_USER; Memory& mem; Kernel& kernel; + // The IR service has a reference to the HID service as that's where the frontends store CirclePad Pro button state in + HIDService& hid; const EmulatorConfig& config; MAKE_LOG_FUNCTION(log, irUserLogger) @@ -76,11 +79,9 @@ class IRUserService { void sendPayload(std::span payload); public: - IRUserService(Memory& mem, const EmulatorConfig& config, Kernel& kernel) - : mem(mem), config(config), kernel(kernel), cpp([&](IR::Device::Payload payload) { sendPayload(payload); }) {} + IRUserService(Memory& mem, HIDService& hid, const EmulatorConfig& config, Kernel& kernel) + : mem(mem), hid(hid), config(config), kernel(kernel), cpp([&](IR::Device::Payload payload) { sendPayload(payload); }) {} - void setZRPressed(bool pressed) { cpp.state.buttons.zrNotPressed = pressed ? 0 : 1; } - void setZLPressed(bool pressed) { cpp.state.buttons.zrNotPressed = pressed ? 0 : 1; } void setCStickX(s16 value) { cpp.state.cStick.x = value; } void setCStickY(s16 value) { cpp.state.cStick.y = value; } diff --git a/src/core/services/hid.cpp b/src/core/services/hid.cpp index a7b9b13b..997884f1 100644 --- a/src/core/services/hid.cpp +++ b/src/core/services/hid.cpp @@ -1,7 +1,9 @@ #include "services/hid.hpp" + +#include + #include "ipc.hpp" #include "kernel.hpp" -#include namespace HIDCommands { enum : u32 { @@ -36,6 +38,8 @@ void HIDService::reset() { touchScreenX = touchScreenY = 0; roll = pitch = yaw = 0; accelX = accelY = accelZ = 0; + + cStickX = cStickY = 0; } void HIDService::handleSyncRequest(u32 messagePointer) { @@ -87,17 +91,17 @@ void HIDService::disableGyroscopeLow(u32 messagePointer) { void HIDService::getGyroscopeLowCalibrateParam(u32 messagePointer) { log("HID::GetGyroscopeLowCalibrateParam\n"); - constexpr s16 unit = 6700; // Approximately from Citra which took it from hardware + constexpr s16 unit = 6700; // Approximately from Citra which took it from hardware mem.write32(messagePointer, IPC::responseHeader(0x16, 6, 0)); mem.write32(messagePointer + 4, Result::Success); // Fill calibration data (for x/y/z depending on i) for (int i = 0; i < 3; i++) { - const u32 pointer = messagePointer + 8 + i * 3 * sizeof(u16); // Pointer to write the calibration info for the current coordinate + const u32 pointer = messagePointer + 8 + i * 3 * sizeof(u16); // Pointer to write the calibration info for the current coordinate - mem.write16(pointer, 0); // Zero point - mem.write16(pointer + 1 * sizeof(u16), unit); // Positive unit point - mem.write16(pointer + 2 * sizeof(u16), -unit); // Negative unit point + mem.write16(pointer, 0); // Zero point + mem.write16(pointer + 1 * sizeof(u16), unit); // Positive unit point + mem.write16(pointer + 2 * sizeof(u16), -unit); // Negative unit point } } @@ -134,9 +138,9 @@ void HIDService::getIPCHandles(u32 messagePointer) { } mem.write32(messagePointer, IPC::responseHeader(0xA, 1, 7)); - mem.write32(messagePointer + 4, Result::Success); // Result code - mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor - mem.write32(messagePointer + 12, KernelHandles::HIDSharedMemHandle); // Shared memory handle + mem.write32(messagePointer + 4, Result::Success); // Result code + mem.write32(messagePointer + 8, 0x14000000); // Translation descriptor + mem.write32(messagePointer + 12, KernelHandles::HIDSharedMemHandle); // Shared memory handle // Write HID event handles for (int i = 0; i < events.size(); i++) { @@ -149,23 +153,27 @@ void HIDService::updateInputs(u64 currentTick) { if (sharedMem) { // First, update the pad state if (nextPadIndex == 0) { - writeSharedMem(0x8, readSharedMem(0x0)); // Copy previous tick count - writeSharedMem(0x0, currentTick); // Write new tick count + writeSharedMem(0x8, readSharedMem(0x0)); // Copy previous tick count + writeSharedMem(0x0, currentTick); // Write new tick count } - writeSharedMem(0x10, nextPadIndex); // Index last updated by the HID module - writeSharedMem(0x1C, newButtons); // Current PAD state - writeSharedMem(0x20, circlePadX); // Current circle pad state - writeSharedMem(0x22, circlePadY); - - const size_t padEntryOffset = 0x28 + (nextPadIndex * 0x10); // Offset in the array of 8 pad entries - nextPadIndex = (nextPadIndex + 1) % 8; // Move to next entry - - const u32 pressed = (newButtons ^ oldButtons) & newButtons; // Pressed buttons - const u32 released = (newButtons ^ oldButtons) & oldButtons; // Released buttons + // Mask out the CirclePadPro buttons when writing to HID shared memory, since the actual OS doesn't store anything in those bits + const u32 currentButtons = newButtons & ~HID::Keys::CirclePadProButtons; + const u32 previousButtons = oldButtons & ~HID::Keys::CirclePadProButtons; oldButtons = newButtons; - writeSharedMem(padEntryOffset, newButtons); + writeSharedMem(0x10, nextPadIndex); // Index last updated by the HID module + writeSharedMem(0x1C, currentButtons); // Current PAD state + writeSharedMem(0x20, circlePadX); // Current circle pad state + writeSharedMem(0x22, circlePadY); + + const size_t padEntryOffset = 0x28 + (nextPadIndex * 0x10); // Offset in the array of 8 pad entries + nextPadIndex = (nextPadIndex + 1) % 8; // Move to next entry + + const u32 pressed = (currentButtons ^ previousButtons) & currentButtons; // Pressed buttons + const u32 released = (currentButtons ^ previousButtons) & previousButtons; // Released buttons + + writeSharedMem(padEntryOffset, currentButtons); writeSharedMem(padEntryOffset + 4, pressed); writeSharedMem(padEntryOffset + 8, released); writeSharedMem(padEntryOffset + 12, circlePadX); @@ -173,12 +181,12 @@ void HIDService::updateInputs(u64 currentTick) { // Next, update touchscreen state if (nextTouchscreenIndex == 0) { - writeSharedMem(0xB0, readSharedMem(0xA8)); // Copy previous tick count - writeSharedMem(0xA8, currentTick); // Write new tick count + writeSharedMem(0xB0, readSharedMem(0xA8)); // Copy previous tick count + writeSharedMem(0xA8, currentTick); // Write new tick count } - writeSharedMem(0xB8, nextTouchscreenIndex); // Index last updated by the HID module - const size_t touchEntryOffset = 0xC8 + (nextTouchscreenIndex * 8); // Offset in the array of 8 touchscreen entries - nextTouchscreenIndex = (nextTouchscreenIndex + 1) % 8; // Move to next entry + writeSharedMem(0xB8, nextTouchscreenIndex); // Index last updated by the HID module + const size_t touchEntryOffset = 0xC8 + (nextTouchscreenIndex * 8); // Offset in the array of 8 touchscreen entries + nextTouchscreenIndex = (nextTouchscreenIndex + 1) % 8; // Move to next entry writeSharedMem(touchEntryOffset, touchScreenX); writeSharedMem(touchEntryOffset + 2, touchScreenY); @@ -186,10 +194,10 @@ void HIDService::updateInputs(u64 currentTick) { // Next, update accelerometer state if (nextAccelerometerIndex == 0) { - writeSharedMem(0x110, readSharedMem(0x108)); // Copy previous tick count - writeSharedMem(0x108, currentTick); // Write new tick count + writeSharedMem(0x110, readSharedMem(0x108)); // Copy previous tick count + writeSharedMem(0x108, currentTick); // Write new tick count } - writeSharedMem(0x118, nextAccelerometerIndex); // Index last updated by the HID module + writeSharedMem(0x118, nextAccelerometerIndex); // Index last updated by the HID module const size_t accelEntryOffset = 0x128 + (nextAccelerometerIndex * 6); // Offset in the array of 8 accelerometer entries // Raw data of current accelerometer entry @@ -204,12 +212,12 @@ void HIDService::updateInputs(u64 currentTick) { accelerometerData[0] = accelX; accelerometerData[1] = accelY; accelerometerData[2] = accelZ; - nextAccelerometerIndex = (nextAccelerometerIndex + 1) % 8; // Move to next entry + nextAccelerometerIndex = (nextAccelerometerIndex + 1) % 8; // Move to next entry // Next, update gyro state if (nextGyroIndex == 0) { - writeSharedMem(0x160, readSharedMem(0x158)); // Copy previous tick count - writeSharedMem(0x158, currentTick); // Write new tick count + writeSharedMem(0x160, readSharedMem(0x158)); // Copy previous tick count + writeSharedMem(0x158, currentTick); // Write new tick count } const size_t gyroEntryOffset = 0x178 + (nextGyroIndex * 6); // Offset in the array of 8 touchscreen entries s16* gyroData = getSharedMemPointer(gyroEntryOffset); @@ -220,8 +228,8 @@ void HIDService::updateInputs(u64 currentTick) { // Since gyroscope euler angles are relative, we zero them out here and the frontend will update them again when we receive a new rotation roll = pitch = yaw = 0; - writeSharedMem(0x168, nextGyroIndex); // Index last updated by the HID module - nextGyroIndex = (nextGyroIndex + 1) % 32; // Move to next entry + writeSharedMem(0x168, nextGyroIndex); // Index last updated by the HID module + nextGyroIndex = (nextGyroIndex + 1) % 32; // Move to next entry } // For some reason, the original developers decided to signal the HID events each time the OS rescanned inputs diff --git a/src/core/services/ir/ir_user.cpp b/src/core/services/ir/ir_user.cpp index 05bc513a..6af21182 100644 --- a/src/core/services/ir/ir_user.cpp +++ b/src/core/services/ir/ir_user.cpp @@ -333,7 +333,17 @@ void IRUserService::updateCirclePadPro() { return; } - std::vector response(sizeof(cpp.state)); - std::memcpy(response.data(), &cpp.state, sizeof(cpp.state)); + // The button state for the CirclePad Pro is stored in the HID service to make the frontend logic simpler + // We take the button state, format it nicely into the CirclePad Pro struct, and return it + auto& cppState = cpp.state; + u32 buttons = hid.getOldButtons(); + + cppState.buttons.zlNotPressed = (buttons & HID::Keys::ZL) ? 0 : 1; + cppState.buttons.zrNotPressed = (buttons & HID::Keys::ZR) ? 0 : 1; + cppState.cStick.x = hid.getCStickX(); + cppState.cStick.y = hid.getCStickY(); + + std::vector response(sizeof(cppState)); + std::memcpy(response.data(), &cppState, sizeof(cppState)); sendPayload(response); } \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 46282428..8205cd64 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -7,7 +7,7 @@ ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), - cfg(mem, config), csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel, config), hid(mem, kernel), http(mem), ir_user(mem, config, kernel), + cfg(mem, config), csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel, config), hid(mem, kernel), http(mem), ir_user(mem, hid, config, kernel), frd(mem), fs(mem, kernel, config), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), ns(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} diff --git a/src/http_server.cpp b/src/http_server.cpp index 41c7ae82..c0114bc1 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -93,8 +93,10 @@ HttpServer::HttpServer(Emulator* emulator) {"Left", {HID::Keys::Left}}, {"Up", {HID::Keys::Up}}, {"Down", {HID::Keys::Down}}, - {"R", {HID::Keys::R}}, {"L", {HID::Keys::L}}, + {"R", {HID::Keys::R}}, + {"ZL", {HID::Keys::ZL}}, + {"ZR", {HID::Keys::ZR}}, {"X", {HID::Keys::X}}, {"Y", {HID::Keys::Y}}, }) { diff --git a/src/hydra_core.cpp b/src/hydra_core.cpp index 0bcd21a8..2f80b8b0 100644 --- a/src/hydra_core.cpp +++ b/src/hydra_core.cpp @@ -82,6 +82,7 @@ void HydraCore::runFrame() { hid.setKey(HID::Keys::Down, checkButtonCallback(0, hydra::ButtonType::Keypad1Down)); hid.setKey(HID::Keys::Left, checkButtonCallback(0, hydra::ButtonType::Keypad1Left)); hid.setKey(HID::Keys::Right, checkButtonCallback(0, hydra::ButtonType::Keypad1Right)); + // TODO: N3DS buttons int x = !!checkButtonCallback(0, hydra::ButtonType::Analog1Right) - !!checkButtonCallback(0, hydra::ButtonType::Analog1Left); int y = !!checkButtonCallback(0, hydra::ButtonType::Analog1Up) - !!checkButtonCallback(0, hydra::ButtonType::Analog1Down); diff --git a/src/libretro_core.cpp b/src/libretro_core.cpp index 24651516..4786b317 100644 --- a/src/libretro_core.cpp +++ b/src/libretro_core.cpp @@ -351,6 +351,7 @@ void retro_run() { hid.setKey(HID::Keys::Down, getButtonState(RETRO_DEVICE_ID_JOYPAD_DOWN)); hid.setKey(HID::Keys::Left, getButtonState(RETRO_DEVICE_ID_JOYPAD_LEFT)); hid.setKey(HID::Keys::Right, getButtonState(RETRO_DEVICE_ID_JOYPAD_RIGHT)); + // TODO: N3DS buttons // Get analog values for the left analog stick (Right analog stick is N3DS-only and unimplemented) float xLeft = getAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X); diff --git a/src/lua.cpp b/src/lua.cpp index 5b78cec2..a5d497fd 100644 --- a/src/lua.cpp +++ b/src/lua.cpp @@ -367,6 +367,8 @@ void LuaManager::initializeThunks() { addIntConstant(HID::Keys::Right, "__ButtonRight"); addIntConstant(HID::Keys::L, "__ButtonL"); addIntConstant(HID::Keys::R, "__ButtonR"); + addIntConstant(HID::Keys::ZL, "__ButtonZL"); + addIntConstant(HID::Keys::ZR, "__ButtonZR"); // Call our Lua runtime initialization before any Lua script runs luaL_loadstring(L, runtimeInit); diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index 9b4db334..88891ddf 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -592,47 +592,53 @@ void MainWindow::initControllers() { } void MainWindow::pollControllers() { - // Update circlepad if a controller is plugged in + // Update circlepad/c-stick/ZL/ZR if a controller is plugged in if (gameController != nullptr) { HIDService& hid = emu->getServiceManager().getHID(); const s16 stickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTX); const s16 stickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTY); constexpr s16 deadzone = 3276; - constexpr s16 maxValue = 0x9C; - constexpr s16 div = 0x8000 / maxValue; + constexpr s16 triggerThreshold = SDL_JOYSTICK_AXIS_MAX / 2; - // Avoid overriding the keyboard's circlepad input - if (std::abs(stickX) < deadzone && !keyboardAnalogX) { - hid.setCirclepadX(0); - } else { - hid.setCirclepadX(stickX / div); + { + // Update circlepad + constexpr s16 circlepadMax = 0x9C; + constexpr s16 div = 0x8000 / circlepadMax; + + // Avoid overriding the keyboard's circlepad input + if (std::abs(stickX) < deadzone && !keyboardAnalogX) { + hid.setCirclepadX(0); + } else { + hid.setCirclepadX(stickX / div); + } + + if (std::abs(stickY) < deadzone && !keyboardAnalogY) { + hid.setCirclepadY(0); + } else { + hid.setCirclepadY(-(stickY / div)); + } } - if (std::abs(stickY) < deadzone && !keyboardAnalogY) { - hid.setCirclepadY(0); - } else { - hid.setCirclepadY(-(stickY / div)); - } - - auto& ir = emu->getServiceManager().getIRUser(); const s16 l2 = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_TRIGGERLEFT); const s16 r2 = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); const s16 cstickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_RIGHTX); const s16 cstickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_RIGHTY); - ir.setZLPressed(l2 > 16000); - ir.setZRPressed(r2 > 16000); + hid.setKey(HID::Keys::ZL, l2 > triggerThreshold); + hid.setKey(HID::Keys::ZR, r2 > triggerThreshold); + // Update C-Stick + // To convert from SDL coordinates, ie [-32768, 32767] to [-2048, 2047] we just divide by 8 if (std::abs(cstickX) < deadzone) { - ir.setCStickX(IR::CirclePadPro::ButtonState::C_STICK_CENTER); + hid.setCStickX(IR::CirclePadPro::ButtonState::C_STICK_CENTER); } else { - ir.setCStickX(cstickX / 8); + hid.setCStickX(cstickX / 8); } if (std::abs(cstickY) < deadzone) { - ir.setCStickY(IR::CirclePadPro::ButtonState::C_STICK_CENTER); + hid.setCStickY(IR::CirclePadPro::ButtonState::C_STICK_CENTER); } else { - ir.setCStickY(-(cstickY / 8)); + hid.setCStickY(-(cstickY / 8)); } } diff --git a/src/panda_qt/mappings.cpp b/src/panda_qt/mappings.cpp index d41b0a31..99b98107 100644 --- a/src/panda_qt/mappings.cpp +++ b/src/panda_qt/mappings.cpp @@ -10,6 +10,8 @@ InputMappings InputMappings::defaultKeyboardMappings() { mappings.setMapping(Qt::Key_I, HID::Keys::Y); mappings.setMapping(Qt::Key_Q, HID::Keys::L); mappings.setMapping(Qt::Key_P, HID::Keys::R); + mappings.setMapping(Qt::Key_1, HID::Keys::ZL); + mappings.setMapping(Qt::Key_0, HID::Keys::ZR); mappings.setMapping(Qt::Key_Up, HID::Keys::Up); mappings.setMapping(Qt::Key_Down, HID::Keys::Down); mappings.setMapping(Qt::Key_Right, HID::Keys::Right); diff --git a/src/panda_sdl/frontend_sdl.cpp b/src/panda_sdl/frontend_sdl.cpp index 2d60d2fa..50e03e3a 100644 --- a/src/panda_sdl/frontend_sdl.cpp +++ b/src/panda_sdl/frontend_sdl.cpp @@ -401,24 +401,52 @@ void FrontendSDL::run() { // Update controller analog sticks and HID service if (emu.romType != ROMType::None) { + // Update circlepad/c-stick/ZL/ZR if a controller is plugged in if (gameController != nullptr) { const s16 stickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTX); const s16 stickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_LEFTY); constexpr s16 deadzone = 3276; - constexpr s16 maxValue = 0x9C; - constexpr s16 div = 0x8000 / maxValue; + constexpr s16 triggerThreshold = SDL_JOYSTICK_AXIS_MAX / 2; - // Avoid overriding the keyboard's circlepad input - if (abs(stickX) < deadzone && !keyboardAnalogX) { - hid.setCirclepadX(0); - } else { - hid.setCirclepadX(stickX / div); + { + // Update circlepad + constexpr s16 circlepadMax = 0x9C; + constexpr s16 div = 0x8000 / circlepadMax; + + // Avoid overriding the keyboard's circlepad input + if (std::abs(stickX) < deadzone && !keyboardAnalogX) { + hid.setCirclepadX(0); + } else { + hid.setCirclepadX(stickX / div); + } + + if (std::abs(stickY) < deadzone && !keyboardAnalogY) { + hid.setCirclepadY(0); + } else { + hid.setCirclepadY(-(stickY / div)); + } } - if (abs(stickY) < deadzone && !keyboardAnalogY) { - hid.setCirclepadY(0); + const s16 l2 = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_TRIGGERLEFT); + const s16 r2 = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); + const s16 cstickX = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_RIGHTX); + const s16 cstickY = SDL_GameControllerGetAxis(gameController, SDL_CONTROLLER_AXIS_RIGHTY); + + hid.setKey(HID::Keys::ZL, l2 > triggerThreshold); + hid.setKey(HID::Keys::ZR, r2 > triggerThreshold); + + // Update C-Stick + // To convert from SDL coordinates, ie [-32768, 32767] to [-2048, 2047] we just divide by 8 + if (std::abs(cstickX) < deadzone) { + hid.setCStickX(IR::CirclePadPro::ButtonState::C_STICK_CENTER); } else { - hid.setCirclepadY(-(stickY / div)); + hid.setCStickX(cstickX / 8); + } + + if (std::abs(cstickY) < deadzone) { + hid.setCStickY(IR::CirclePadPro::ButtonState::C_STICK_CENTER); + } else { + hid.setCStickY(-(cstickY / 8)); } } diff --git a/src/panda_sdl/mappings.cpp b/src/panda_sdl/mappings.cpp index 0c09b852..58d139c4 100644 --- a/src/panda_sdl/mappings.cpp +++ b/src/panda_sdl/mappings.cpp @@ -10,6 +10,8 @@ InputMappings InputMappings::defaultKeyboardMappings() { mappings.setMapping(SDLK_i, HID::Keys::Y); mappings.setMapping(SDLK_q, HID::Keys::L); mappings.setMapping(SDLK_p, HID::Keys::R); + mappings.setMapping(SDLK_1, HID::Keys::ZL); + mappings.setMapping(SDLK_0, HID::Keys::ZR); mappings.setMapping(SDLK_UP, HID::Keys::Up); mappings.setMapping(SDLK_DOWN, HID::Keys::Down); mappings.setMapping(SDLK_RIGHT, HID::Keys::Right);