Input: Add onControllerAdded/Removed callbacks and also display the controller name in the input viewer

This commit is contained in:
Phillip Stephens 2022-02-09 00:54:53 -08:00
parent 52fd54bc3e
commit a6b2d66e1e
Signed by: Antidote
GPG Key ID: F8BEE4C83DACA60D
12 changed files with 175 additions and 59 deletions

View File

@ -36,8 +36,10 @@ struct AppDelegate {
virtual void onSpecialKeyUp(SpecialKey key) noexcept = 0; virtual void onSpecialKeyUp(SpecialKey key) noexcept = 0;
// Controller // Controller
virtual void onControllerButton(uint32_t idx, ControllerButton button, bool pressed) noexcept = 0; virtual void onControllerAdded(uint32_t which) noexcept = 0;
virtual void onControllerAxis(uint32_t idx, ControllerAxis axis, int16_t value) noexcept = 0; virtual void onControllerRemoved(uint32_t which) noexcept = 0;
virtual void onControllerButton(uint32_t which, ControllerButton button, bool pressed) noexcept = 0;
virtual void onControllerAxis(uint32_t which, ControllerAxis axis, int16_t value) noexcept = 0;
// virtual void resized([[maybe_unused]] const WindowSize& rect, [[maybe_unused]] bool sync) noexcept {} // virtual void resized([[maybe_unused]] const WindowSize& rect, [[maybe_unused]] bool sync) noexcept {}
// virtual void mouseDown([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] EMouseButton button, // virtual void mouseDown([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] EMouseButton button,

View File

@ -15,6 +15,8 @@ void App_onCharKeyUp(AppDelegate& cb, uint8_t code) noexcept;
void App_onSpecialKeyDown(AppDelegate& cb, SpecialKey key, bool isRepeat) noexcept; void App_onSpecialKeyDown(AppDelegate& cb, SpecialKey key, bool isRepeat) noexcept;
void App_onSpecialKeyUp(AppDelegate& cb, SpecialKey key) noexcept; void App_onSpecialKeyUp(AppDelegate& cb, SpecialKey key) noexcept;
// Controller // Controller
void App_onControllerButton(AppDelegate& cb, uint32_t idx, ControllerButton button, bool pressed) noexcept; void App_onControllerAdded(AppDelegate& cb, uint32_t which) noexcept;
void App_onControllerAxis(AppDelegate& cb, uint32_t idx, ControllerAxis axis, int16_t value) noexcept; void App_onControllerRemoved(AppDelegate& cb, uint32_t which) noexcept;
void App_onControllerButton(AppDelegate& cb, uint32_t which, ControllerButton button, bool pressed) noexcept;
void App_onControllerAxis(AppDelegate& cb, uint32_t which, ControllerAxis axis, int16_t value) noexcept;
} // namespace aurora } // namespace aurora

View File

@ -28,10 +28,16 @@ void App_onSpecialKeyUp(AppDelegate& cb, SpecialKey key) noexcept {
} }
// Controller // Controller
void App_onControllerButton(AppDelegate& cb, uint32_t idx, ControllerButton button, bool pressed) noexcept { void App_onControllerAdded(AppDelegate& cb, uint32_t which) noexcept {
cb.onControllerButton(idx, button, pressed); cb.onControllerAdded(which);
} }
void App_onControllerAxis(AppDelegate& cb, uint32_t idx, ControllerAxis axis, int16_t value) noexcept { void App_onControllerRemoved(AppDelegate& cb, uint32_t which) noexcept {
cb.onControllerAxis(idx, axis, value); cb.onControllerRemoved(which);
}
void App_onControllerButton(AppDelegate& cb, uint32_t which, ControllerButton button, bool pressed) noexcept {
cb.onControllerButton(which, button, pressed);
}
void App_onControllerAxis(AppDelegate& cb, uint32_t which, ControllerAxis axis, int16_t value) noexcept {
cb.onControllerAxis(which, axis, value);
} }
} // namespace aurora } // namespace aurora

View File

@ -3,7 +3,12 @@ use sdl2::controller::{Axis, Button};
use crate::{ use crate::{
app_run, get_args, get_backend, get_backend_string, get_dxt_compression_supported, app_run, get_args, get_backend, get_backend_string, get_dxt_compression_supported,
get_window_size, get_window_size,
sdl::{get_controller_player_index, set_controller_player_index}, sdl::{
get_controller_player_index,
set_controller_player_index,
is_controller_gamecube,
get_controller_name
},
set_fullscreen, set_window_title, App, WindowContext, set_fullscreen, set_window_title, App, WindowContext,
}; };
@ -33,6 +38,8 @@ pub(crate) mod ffi {
); );
pub(crate) fn App_onSpecialKeyUp(cb: Pin<&mut AppDelegate>, key: SpecialKey); pub(crate) fn App_onSpecialKeyUp(cb: Pin<&mut AppDelegate>, key: SpecialKey);
// Controller // Controller
pub(crate) fn App_onControllerAdded(cb: Pin<&mut AppDelegate>, which: u32);
pub(crate) fn App_onControllerRemoved(cb: Pin<&mut AppDelegate>, which: u32);
pub(crate) fn App_onControllerButton( pub(crate) fn App_onControllerButton(
cb: Pin<&mut AppDelegate>, cb: Pin<&mut AppDelegate>,
idx: u32, idx: u32,
@ -162,6 +169,8 @@ pub(crate) mod ffi {
fn set_fullscreen(v: bool); fn set_fullscreen(v: bool);
fn get_controller_player_index(which: u32) -> i32; fn get_controller_player_index(which: u32) -> i32;
fn set_controller_player_index(which: u32, index: i32); fn set_controller_player_index(which: u32, index: i32);
fn is_controller_gamecube(which: u32) -> bool;
fn get_controller_name(which: u32) -> String;
} }
} }
impl From<Button> for ffi::ControllerButton { impl From<Button> for ffi::ControllerButton {

View File

@ -38,17 +38,18 @@ pub(crate) fn poll_sdl_events(
.add_mapping(new_mapping.as_str()) .add_mapping(new_mapping.as_str())
.expect("Failed to overwrite mapping"); .expect("Failed to overwrite mapping");
} }
state.open_controllers.insert(controller.instance_id(), controller); let instance_id: u32 = controller.instance_id();
state.open_controllers.insert(instance_id, controller);
unsafe { ffi::App_onControllerAdded(delegate.as_mut().unwrap_unchecked(), instance_id); }
} }
Err(err) => { Err(err) => {
log::warn!("Failed to open SDL controller {} ({:?})", which, err); log::warn!("Failed to open SDL controller {} ({:?})", which, err);
} }
} }
// TODO app connected event
} }
Event::ControllerDeviceRemoved { which, .. } => { Event::ControllerDeviceRemoved { which, .. } => {
unsafe { ffi::App_onControllerRemoved(delegate.as_mut().unwrap_unchecked(), which); }
state.open_controllers.remove(&which); state.open_controllers.remove(&which);
// TODO app disconnected event
} }
Event::ControllerButtonDown { which, button, .. } => unsafe { Event::ControllerButtonDown { which, button, .. } => unsafe {
ffi::App_onControllerButton( ffi::App_onControllerButton(
@ -135,3 +136,15 @@ pub(crate) fn set_controller_player_index(which: u32, index: i32) {
c.set_player_index(index); c.set_player_index(index);
} }
} }
pub(crate) fn is_controller_gamecube(which: u32) -> bool {
get_app().sdl.open_controllers.get(&which)
.map_or(false, |c| c.name()
.to_lowercase()
.eq("nintendo gamecube controller"))
}
pub(crate) fn get_controller_name(which: u32) -> String {
get_app().sdl.open_controllers.get(&which).map_or(String::from(""), |c| c.name())
}

View File

@ -271,6 +271,9 @@ private:
using delta_clock = std::chrono::high_resolution_clock; using delta_clock = std::chrono::high_resolution_clock;
std::chrono::time_point<delta_clock> m_prevFrameTime; std::chrono::time_point<delta_clock> m_prevFrameTime;
std::vector<u32> m_defferredControllers; // used to capture controllers added before CInputGenerator
// is built, i.e during initialization
public: public:
Application(hecl::Runtime::FileStoreManager& fileMgr, hecl::CVarManager& cvarMgr, hecl::CVarCommons& cvarCmns) Application(hecl::Runtime::FileStoreManager& fileMgr, hecl::CVarManager& cvarMgr, hecl::CVarCommons& cvarCmns)
: m_fileMgr(fileMgr), m_cvarManager(cvarMgr), m_cvarCommons(cvarCmns), m_imGuiConsole(cvarMgr, cvarCmns) {} : m_fileMgr(fileMgr), m_cvarManager(cvarMgr), m_cvarCommons(cvarCmns), m_imGuiConsole(cvarMgr, cvarCmns) {}
@ -314,6 +317,15 @@ public:
} }
bool onAppIdle(float realDt) noexcept override { bool onAppIdle(float realDt) noexcept override {
if (auto* input = g_InputGenerator) {
if (!m_defferredControllers.empty()) {
for (const auto which : m_defferredControllers) {
input->controllerAdded(which);
}
m_defferredControllers.clear();
}
}
if (!m_projectInitialized && !m_deferredProject.empty()) { if (!m_projectInitialized && !m_deferredProject.empty()) {
if (CDvdFile::Initialize(m_deferredProject)) { if (CDvdFile::Initialize(m_deferredProject)) {
m_projectInitialized = true; m_projectInitialized = true;
@ -429,6 +441,20 @@ public:
} }
} }
void onControllerAdded(uint32_t which) noexcept override {
if (auto* input = g_InputGenerator) {
input->controllerAdded(which);
} else {
m_defferredControllers.emplace_back(which);
}
}
void onControllerRemoved(uint32_t which) noexcept override {
if (auto* input = g_InputGenerator) {
input->controllerRemoved(which);
}
}
void onAppExiting() noexcept override { void onAppExiting() noexcept override {
m_imGuiConsole.Shutdown(); m_imGuiConsole.Shutdown();
if (g_mainMP1) { if (g_mainMP1) {

View File

@ -10,6 +10,8 @@
#include "ImGuiEngine.hpp" #include "ImGuiEngine.hpp"
#include "magic_enum.hpp" #include "magic_enum.hpp"
#include <cstdarg>
#include <zeus/CEulerAngles.hpp> #include <zeus/CEulerAngles.hpp>
namespace ImGui { namespace ImGui {
@ -866,6 +868,15 @@ void ImGuiConsole::ShowDebugOverlay() {
} }
ImGui::End(); ImGui::End();
} }
void TextCenter(const std::string& text) {
float font_size = ImGui::GetFontSize() * text.size() / 2;
ImGui::SameLine(
ImGui::GetWindowSize().x / 2 -
font_size + (font_size / 2)
);
ImGui::TextUnformatted(text.c_str());
}
void ImGuiConsole::ShowInputViewer() { void ImGuiConsole::ShowInputViewer() {
if (!m_showInput || g_InputGenerator == nullptr) { if (!m_showInput || g_InputGenerator == nullptr) {
@ -875,6 +886,10 @@ void ImGuiConsole::ShowInputViewer() {
if (input.x4_controllerIdx != 0) { if (input.x4_controllerIdx != 0) {
return; return;
} }
if (m_whichController != input.m_which) {
m_controllerName = static_cast<std::string>(aurora::get_controller_name(input.m_which));
m_whichController = input.m_which;
}
// Code -stolen- borrowed from Practice Mod // Code -stolen- borrowed from Practice Mod
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
@ -886,10 +901,14 @@ void ImGuiConsole::ShowInputViewer() {
} }
ImGui::SetNextWindowBgAlpha(0.65f); ImGui::SetNextWindowBgAlpha(0.65f);
if (ImGui::Begin("Input Overlay", nullptr, windowFlags)) { if (ImGui::Begin("Input Overlay", nullptr, windowFlags)) {
float scale = ImGui::GetIO().DisplayFramebufferScale.x;
if (!m_controllerName.empty()) {
TextCenter(m_controllerName);
ImGui::Separator();
}
ImDrawList* dl = ImGui::GetWindowDrawList(); ImDrawList* dl = ImGui::GetWindowDrawList();
zeus::CVector2f p = ImGui::GetCursorScreenPos(); zeus::CVector2f p = ImGui::GetCursorScreenPos();
float scale = ImGui::GetIO().DisplayFramebufferScale.x;
float leftStickRadius = 30 * scale; float leftStickRadius = 30 * scale;
p = p + zeus::CVector2f{20, 20} * scale; // Pad p so we don't clip outside our rect p = p + zeus::CVector2f{20, 20} * scale; // Pad p so we don't clip outside our rect
zeus::CVector2f leftStickCenter = p + zeus::CVector2f(30, 45) * scale; zeus::CVector2f leftStickCenter = p + zeus::CVector2f(30, 45) * scale;

View File

@ -96,6 +96,8 @@ private:
double m_lastRoomTime = 0.f; double m_lastRoomTime = 0.f;
double m_currentRoomStart = 0.f; double m_currentRoomStart = 0.f;
float m_menuHintTime = 5.f; float m_menuHintTime = 5.f;
std::string m_controllerName;
u32 m_whichController = -1;
void ShowAppMainMenuBar(bool canInspect); void ShowAppMainMenuBar(bool canInspect);
void ShowMenuGame(); void ShowMenuGame();

View File

@ -94,19 +94,22 @@ CFinalInput::CFinalInput(int cIdx, float dt, const SAuroraControllerState& data,
, x2e_b28_PDPRight(DDPRight() && !prevInput.DDPRight()) , x2e_b28_PDPRight(DDPRight() && !prevInput.DDPRight())
, x2e_b29_PDPDown(DDPDown() && !prevInput.DDPDown()) , x2e_b29_PDPDown(DDPDown() && !prevInput.DDPDown())
, x2e_b30_PDPLeft(DDPLeft() && !prevInput.DDPLeft()) , x2e_b30_PDPLeft(DDPLeft() && !prevInput.DDPLeft())
, x2e_b31_PStart(DStart() && !prevInput.DStart()) { , x2e_b31_PStart(DStart() && !prevInput.DStart())
if (x2c_b29_L) { , m_which(data.m_which){
x18_anaLeftTrigger = 150.f * 0.007f; if (!data.m_isGamecube) {
} if (x2c_b29_L && x18_anaLeftTrigger <= 0.f) {
if (x2c_b30_R) { x18_anaLeftTrigger = 150.f * 0.007f;
x1c_anaRightTrigger = 150.f * 0.007f; }
} if (x2c_b30_R && x1c_anaRightTrigger <= 0.f) {
x1c_anaRightTrigger = 150.f * 0.007f;
}
if (x18_anaLeftTrigger > (150.f * 0.007f) && !x2c_b29_L) { if (x18_anaLeftTrigger >= (150.f * 0.007f) && !x2c_b29_L) {
x2c_b29_L = true; x2c_b29_L = true;
} }
if (x1c_anaRightTrigger > (150.f * 0.007f) && !x2c_b30_R) { if (x1c_anaRightTrigger >= (150.f * 0.007f) && !x2c_b30_R) {
x2c_b30_R = true; x2c_b30_R = true;
}
} }
} }
@ -208,6 +211,7 @@ CFinalInput& CFinalInput::operator|=(const CFinalInput& other) {
m_PSpecialKeys = other.m_PSpecialKeys; m_PSpecialKeys = other.m_PSpecialKeys;
m_PMouseButtons = other.m_PMouseButtons; m_PMouseButtons = other.m_PMouseButtons;
} }
m_which = other.m_which;
return *this; return *this;
} }

View File

@ -12,14 +12,19 @@
namespace metaforce { namespace metaforce {
struct SAuroraControllerState { struct SAuroraControllerState {
u32 m_which = -1;
bool m_isGamecube = false;
std::array<int16_t, size_t(aurora::ControllerAxis::MAX)> m_axes{}; std::array<int16_t, size_t(aurora::ControllerAxis::MAX)> m_axes{};
std::bitset<size_t(aurora::ControllerButton::MAX)> m_btns{}; std::bitset<size_t(aurora::ControllerButton::MAX)> m_btns{};
SAuroraControllerState() = default;
SAuroraControllerState(uint32_t which, bool isGamecube) : m_which(which), m_isGamecube(isGamecube) {}
void clamp(); void clamp();
}; };
struct CFinalInput { struct CFinalInput {
float x0_dt = 0.0f; float x0_dt = 0.0f;
u32 x4_controllerIdx = 0; u32 x4_controllerIdx = -1;
float x8_anaLeftX = 0.0f; float x8_anaLeftX = 0.0f;
float xc_anaLeftY = 0.0f; float xc_anaLeftY = 0.0f;
float x10_anaRightX = 0.0f; float x10_anaRightX = 0.0f;

View File

@ -14,33 +14,61 @@ void CInputGenerator::Update(float dt, CArchitectureQueue& queue) {
const CFinalInput& kbInput = getFinalInput(0, dt); const CFinalInput& kbInput = getFinalInput(0, dt);
queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, kbInput)); queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, kbInput));
/* Dolphin controllers next */ /* Dolphin controllers next */
// for (int i = 0; i < 4; ++i) { // for (int i = 0; i < 4; ++i) {
// bool connected; // bool connected;
// EStatusChange change = m_dolphinCb.getStatusChange(i, connected); // EStatusChange change = m_dolphinCb.getStatusChange(i, connected);
// if (change != EStatusChange::NoChange) // if (change != EStatusChange::NoChange)
// queue.Push(MakeMsg::CreateControllerStatus(EArchMsgTarget::Game, i, connected)); // queue.Push(MakeMsg::CreateControllerStatus(EArchMsgTarget::Game, i, connected));
// if (connected) { // if (connected) {
// CFinalInput input = m_dolphinCb.getFinalInput(i, dt, m_leftDiv, m_rightDiv); // CFinalInput input = m_dolphinCb.getFinalInput(i, dt, m_leftDiv, m_rightDiv);
// if (i == 0) /* Merge KB input with first controller */ // if (i == 0) /* Merge KB input with first controller */
// { // {
// input |= kbInput; // input |= kbInput;
// kbUsed = true; // kbUsed = true;
// } // }
// m_lastUpdate = input; // m_lastUpdate = input;
// queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, input)); // queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, input));
// } // }
// } // }
// /* Send straight keyboard input if no first controller present */ // /* Send straight keyboard input if no first controller present */
// if (!kbUsed) { // if (!kbUsed) {
// m_lastUpdate = kbInput; // m_lastUpdate = kbInput;
// } // }
}
void CInputGenerator::controllerAdded(uint32_t which) noexcept {
s32 player = aurora::get_controller_player_index(which);
if (player < 0) {
player = 0;
aurora::set_controller_player_index(which, 0);
}
m_state[player] = SAuroraControllerState(which, aurora::is_controller_gamecube(which));
}
void CInputGenerator::controllerRemoved(uint32_t which) noexcept {
auto* it =
std::find_if(m_state.begin(), m_state.end(), [&which](const auto& s) { return s.m_which == which; });
if (it == m_state.end()) {
return;
}
(*it) = SAuroraControllerState();
}
void CInputGenerator::controllerButton(uint32_t which, aurora::ControllerButton button, bool pressed) noexcept {
s32 player = aurora::get_controller_player_index(which);
if (player < 0) {
return;
}
m_state[player].m_btns.set(size_t(button), pressed);
} }
void CInputGenerator::controllerAxis(uint32_t which, aurora::ControllerAxis axis, int16_t value) noexcept { void CInputGenerator::controllerAxis(uint32_t which, aurora::ControllerAxis axis, int16_t value) noexcept {
s32 idx = aurora::get_controller_player_index(which); s32 player = aurora::get_controller_player_index(which);
if (idx < 0) { if (player < 0) {
return; return;
} }
@ -62,7 +90,7 @@ void CInputGenerator::controllerAxis(uint32_t which, aurora::ControllerAxis axis
break; break;
} }
m_state[idx].m_axes[size_t(axis)] = value; m_state[player].m_axes[size_t(axis)] = value;
} }
const CFinalInput& CInputGenerator::getFinalInput(unsigned int idx, float dt) { const CFinalInput& CInputGenerator::getFinalInput(unsigned int idx, float dt) {

View File

@ -25,7 +25,7 @@ class CInputGenerator /*: public boo::DeviceFinder*/ {
float m_leftDiv; float m_leftDiv;
float m_rightDiv; float m_rightDiv;
CKeyboardMouseControllerData m_data; CKeyboardMouseControllerData m_data;
SAuroraControllerState m_state[4]; std::array<SAuroraControllerState, 4> m_state;
CFinalInput m_lastUpdate; CFinalInput m_lastUpdate;
const CFinalInput& getFinalInput(unsigned idx, float dt); const CFinalInput& getFinalInput(unsigned idx, float dt);
@ -43,14 +43,14 @@ public:
// } // }
// } // }
void controllerButton(uint32_t idx, aurora::ControllerButton button, bool pressed) noexcept { void controllerAdded(uint32_t which) noexcept;
s32 player = aurora::get_controller_player_index(idx);
if (player < 0) { void controllerRemoved(uint32_t which) noexcept;
return;
} void controllerButton(uint32_t which, aurora::ControllerButton button, bool pressed) noexcept;
m_state[player].m_btns.set(size_t(button), pressed);
} void controllerAxis(uint32_t which, aurora::ControllerAxis axis, int16_t value) noexcept;
void controllerAxis(uint32_t idx, aurora::ControllerAxis axis, int16_t value) noexcept;
/* Keyboard and mouse events are delivered on the main game /* Keyboard and mouse events are delivered on the main game
* thread as part of the app's main event loop. The OS is responsible * thread as part of the app's main event loop. The OS is responsible