mirror of https://github.com/AxioDL/metaforce.git
Input: Add onControllerAdded/Removed callbacks and also display the controller name in the input viewer
This commit is contained in:
parent
52fd54bc3e
commit
a6b2d66e1e
|
@ -36,8 +36,10 @@ struct AppDelegate {
|
|||
virtual void onSpecialKeyUp(SpecialKey key) noexcept = 0;
|
||||
|
||||
// Controller
|
||||
virtual void onControllerButton(uint32_t idx, ControllerButton button, bool pressed) noexcept = 0;
|
||||
virtual void onControllerAxis(uint32_t idx, ControllerAxis axis, int16_t value) noexcept = 0;
|
||||
virtual void onControllerAdded(uint32_t which) 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 mouseDown([[maybe_unused]] const SWindowCoord& coord, [[maybe_unused]] EMouseButton button,
|
||||
|
|
|
@ -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_onSpecialKeyUp(AppDelegate& cb, SpecialKey key) noexcept;
|
||||
// Controller
|
||||
void App_onControllerButton(AppDelegate& cb, uint32_t idx, ControllerButton button, bool pressed) noexcept;
|
||||
void App_onControllerAxis(AppDelegate& cb, uint32_t idx, ControllerAxis axis, int16_t value) noexcept;
|
||||
void App_onControllerAdded(AppDelegate& cb, uint32_t which) 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
|
||||
|
|
|
@ -28,10 +28,16 @@ void App_onSpecialKeyUp(AppDelegate& cb, SpecialKey key) noexcept {
|
|||
}
|
||||
|
||||
// Controller
|
||||
void App_onControllerButton(AppDelegate& cb, uint32_t idx, ControllerButton button, bool pressed) noexcept {
|
||||
cb.onControllerButton(idx, button, pressed);
|
||||
void App_onControllerAdded(AppDelegate& cb, uint32_t which) noexcept {
|
||||
cb.onControllerAdded(which);
|
||||
}
|
||||
void App_onControllerAxis(AppDelegate& cb, uint32_t idx, ControllerAxis axis, int16_t value) noexcept {
|
||||
cb.onControllerAxis(idx, axis, value);
|
||||
void App_onControllerRemoved(AppDelegate& cb, uint32_t which) noexcept {
|
||||
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
|
||||
|
|
|
@ -3,7 +3,12 @@ use sdl2::controller::{Axis, Button};
|
|||
use crate::{
|
||||
app_run, get_args, get_backend, get_backend_string, get_dxt_compression_supported,
|
||||
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,
|
||||
};
|
||||
|
||||
|
@ -33,6 +38,8 @@ pub(crate) mod ffi {
|
|||
);
|
||||
pub(crate) fn App_onSpecialKeyUp(cb: Pin<&mut AppDelegate>, key: SpecialKey);
|
||||
// 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(
|
||||
cb: Pin<&mut AppDelegate>,
|
||||
idx: u32,
|
||||
|
@ -162,6 +169,8 @@ pub(crate) mod ffi {
|
|||
fn set_fullscreen(v: bool);
|
||||
fn get_controller_player_index(which: u32) -> 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 {
|
||||
|
|
|
@ -38,17 +38,18 @@ pub(crate) fn poll_sdl_events(
|
|||
.add_mapping(new_mapping.as_str())
|
||||
.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) => {
|
||||
log::warn!("Failed to open SDL controller {} ({:?})", which, err);
|
||||
}
|
||||
}
|
||||
// TODO app connected event
|
||||
}
|
||||
Event::ControllerDeviceRemoved { which, .. } => {
|
||||
unsafe { ffi::App_onControllerRemoved(delegate.as_mut().unwrap_unchecked(), which); }
|
||||
state.open_controllers.remove(&which);
|
||||
// TODO app disconnected event
|
||||
}
|
||||
Event::ControllerButtonDown { which, button, .. } => unsafe {
|
||||
ffi::App_onControllerButton(
|
||||
|
@ -135,3 +136,15 @@ pub(crate) fn set_controller_player_index(which: u32, index: i32) {
|
|||
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())
|
||||
}
|
||||
|
|
|
@ -271,6 +271,9 @@ private:
|
|||
using delta_clock = std::chrono::high_resolution_clock;
|
||||
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:
|
||||
Application(hecl::Runtime::FileStoreManager& fileMgr, hecl::CVarManager& cvarMgr, hecl::CVarCommons& 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 {
|
||||
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 (CDvdFile::Initialize(m_deferredProject)) {
|
||||
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 {
|
||||
m_imGuiConsole.Shutdown();
|
||||
if (g_mainMP1) {
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "ImGuiEngine.hpp"
|
||||
#include "magic_enum.hpp"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#include <zeus/CEulerAngles.hpp>
|
||||
|
||||
namespace ImGui {
|
||||
|
@ -866,6 +868,15 @@ void ImGuiConsole::ShowDebugOverlay() {
|
|||
}
|
||||
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() {
|
||||
if (!m_showInput || g_InputGenerator == nullptr) {
|
||||
|
@ -875,6 +886,10 @@ void ImGuiConsole::ShowInputViewer() {
|
|||
if (input.x4_controllerIdx != 0) {
|
||||
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
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
|
||||
|
@ -886,10 +901,14 @@ void ImGuiConsole::ShowInputViewer() {
|
|||
}
|
||||
ImGui::SetNextWindowBgAlpha(0.65f);
|
||||
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();
|
||||
zeus::CVector2f p = ImGui::GetCursorScreenPos();
|
||||
|
||||
float scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
float leftStickRadius = 30 * scale;
|
||||
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;
|
||||
|
|
|
@ -96,6 +96,8 @@ private:
|
|||
double m_lastRoomTime = 0.f;
|
||||
double m_currentRoomStart = 0.f;
|
||||
float m_menuHintTime = 5.f;
|
||||
std::string m_controllerName;
|
||||
u32 m_whichController = -1;
|
||||
|
||||
void ShowAppMainMenuBar(bool canInspect);
|
||||
void ShowMenuGame();
|
||||
|
|
|
@ -94,19 +94,22 @@ CFinalInput::CFinalInput(int cIdx, float dt, const SAuroraControllerState& data,
|
|||
, x2e_b28_PDPRight(DDPRight() && !prevInput.DDPRight())
|
||||
, x2e_b29_PDPDown(DDPDown() && !prevInput.DDPDown())
|
||||
, x2e_b30_PDPLeft(DDPLeft() && !prevInput.DDPLeft())
|
||||
, x2e_b31_PStart(DStart() && !prevInput.DStart()) {
|
||||
if (x2c_b29_L) {
|
||||
x18_anaLeftTrigger = 150.f * 0.007f;
|
||||
}
|
||||
if (x2c_b30_R) {
|
||||
x1c_anaRightTrigger = 150.f * 0.007f;
|
||||
}
|
||||
, x2e_b31_PStart(DStart() && !prevInput.DStart())
|
||||
, m_which(data.m_which){
|
||||
if (!data.m_isGamecube) {
|
||||
if (x2c_b29_L && x18_anaLeftTrigger <= 0.f) {
|
||||
x18_anaLeftTrigger = 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) {
|
||||
x2c_b29_L = true;
|
||||
}
|
||||
if (x1c_anaRightTrigger > (150.f * 0.007f) && !x2c_b30_R) {
|
||||
x2c_b30_R = true;
|
||||
if (x18_anaLeftTrigger >= (150.f * 0.007f) && !x2c_b29_L) {
|
||||
x2c_b29_L = true;
|
||||
}
|
||||
if (x1c_anaRightTrigger >= (150.f * 0.007f) && !x2c_b30_R) {
|
||||
x2c_b30_R = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,6 +211,7 @@ CFinalInput& CFinalInput::operator|=(const CFinalInput& other) {
|
|||
m_PSpecialKeys = other.m_PSpecialKeys;
|
||||
m_PMouseButtons = other.m_PMouseButtons;
|
||||
}
|
||||
m_which = other.m_which;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,14 +12,19 @@
|
|||
namespace metaforce {
|
||||
|
||||
struct SAuroraControllerState {
|
||||
u32 m_which = -1;
|
||||
bool m_isGamecube = false;
|
||||
std::array<int16_t, size_t(aurora::ControllerAxis::MAX)> m_axes{};
|
||||
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();
|
||||
};
|
||||
|
||||
struct CFinalInput {
|
||||
float x0_dt = 0.0f;
|
||||
u32 x4_controllerIdx = 0;
|
||||
u32 x4_controllerIdx = -1;
|
||||
float x8_anaLeftX = 0.0f;
|
||||
float xc_anaLeftY = 0.0f;
|
||||
float x10_anaRightX = 0.0f;
|
||||
|
|
|
@ -14,33 +14,61 @@ void CInputGenerator::Update(float dt, CArchitectureQueue& queue) {
|
|||
const CFinalInput& kbInput = getFinalInput(0, dt);
|
||||
queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, kbInput));
|
||||
|
||||
/* Dolphin controllers next */
|
||||
// for (int i = 0; i < 4; ++i) {
|
||||
// bool connected;
|
||||
// EStatusChange change = m_dolphinCb.getStatusChange(i, connected);
|
||||
// if (change != EStatusChange::NoChange)
|
||||
// queue.Push(MakeMsg::CreateControllerStatus(EArchMsgTarget::Game, i, connected));
|
||||
// if (connected) {
|
||||
// CFinalInput input = m_dolphinCb.getFinalInput(i, dt, m_leftDiv, m_rightDiv);
|
||||
// if (i == 0) /* Merge KB input with first controller */
|
||||
// {
|
||||
// input |= kbInput;
|
||||
// kbUsed = true;
|
||||
// }
|
||||
// m_lastUpdate = input;
|
||||
// queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, input));
|
||||
// }
|
||||
// }
|
||||
/* Dolphin controllers next */
|
||||
// for (int i = 0; i < 4; ++i) {
|
||||
// bool connected;
|
||||
// EStatusChange change = m_dolphinCb.getStatusChange(i, connected);
|
||||
// if (change != EStatusChange::NoChange)
|
||||
// queue.Push(MakeMsg::CreateControllerStatus(EArchMsgTarget::Game, i, connected));
|
||||
// if (connected) {
|
||||
// CFinalInput input = m_dolphinCb.getFinalInput(i, dt, m_leftDiv, m_rightDiv);
|
||||
// if (i == 0) /* Merge KB input with first controller */
|
||||
// {
|
||||
// input |= kbInput;
|
||||
// kbUsed = true;
|
||||
// }
|
||||
// m_lastUpdate = input;
|
||||
// queue.Push(MakeMsg::CreateUserInput(EArchMsgTarget::Game, input));
|
||||
// }
|
||||
// }
|
||||
|
||||
// /* Send straight keyboard input if no first controller present */
|
||||
// if (!kbUsed) {
|
||||
// m_lastUpdate = kbInput;
|
||||
// }
|
||||
// /* Send straight keyboard input if no first controller present */
|
||||
// if (!kbUsed) {
|
||||
// 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 {
|
||||
s32 idx = aurora::get_controller_player_index(which);
|
||||
if (idx < 0) {
|
||||
s32 player = aurora::get_controller_player_index(which);
|
||||
if (player < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -62,7 +90,7 @@ void CInputGenerator::controllerAxis(uint32_t which, aurora::ControllerAxis axis
|
|||
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) {
|
||||
|
|
|
@ -25,7 +25,7 @@ class CInputGenerator /*: public boo::DeviceFinder*/ {
|
|||
float m_leftDiv;
|
||||
float m_rightDiv;
|
||||
CKeyboardMouseControllerData m_data;
|
||||
SAuroraControllerState m_state[4];
|
||||
std::array<SAuroraControllerState, 4> m_state;
|
||||
|
||||
CFinalInput m_lastUpdate;
|
||||
const CFinalInput& getFinalInput(unsigned idx, float dt);
|
||||
|
@ -43,14 +43,14 @@ public:
|
|||
// }
|
||||
// }
|
||||
|
||||
void controllerButton(uint32_t idx, aurora::ControllerButton button, bool pressed) noexcept {
|
||||
s32 player = aurora::get_controller_player_index(idx);
|
||||
if (player < 0) {
|
||||
return;
|
||||
}
|
||||
m_state[player].m_btns.set(size_t(button), pressed);
|
||||
}
|
||||
void controllerAxis(uint32_t idx, aurora::ControllerAxis axis, int16_t value) noexcept;
|
||||
void controllerAdded(uint32_t which) noexcept;
|
||||
|
||||
void controllerRemoved(uint32_t which) noexcept;
|
||||
|
||||
void controllerButton(uint32_t which, aurora::ControllerButton button, bool pressed) noexcept;
|
||||
|
||||
void controllerAxis(uint32_t which, aurora::ControllerAxis axis, int16_t value) noexcept;
|
||||
|
||||
|
||||
/* Keyboard and mouse events are delivered on the main game
|
||||
* thread as part of the app's main event loop. The OS is responsible
|
||||
|
|
Loading…
Reference in New Issue