feat(gamepad): detecció i notificacions hot-plug amb GamepadManager
This commit is contained in:
1 parent
be9309d342
commit
7c35ffaa74
9 files changed
+305
-15
No files matched your search
@@ -59,6 +59,7 @@ set(APP_SOURCES
|
|||||||
source/core/input/key_config.cpp
|
source/core/input/key_config.cpp
|
||||||
source/core/input/global_inputs.cpp
|
source/core/input/global_inputs.cpp
|
||||||
source/core/input/input_state.cpp
|
source/core/input/input_state.cpp
|
||||||
|
source/core/input/gamepad_manager.cpp
|
||||||
source/core/rendering/screen.cpp
|
source/core/rendering/screen.cpp
|
||||||
source/core/rendering/canvas.cpp
|
source/core/rendering/canvas.cpp
|
||||||
source/core/rendering/notifier.cpp
|
source/core/rendering/notifier.cpp
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
notification:
|
notification:
|
||||||
test: "notificacio de prova"
|
test: "notificacio de prova"
|
||||||
|
gamepad_connected: "connectat"
|
||||||
|
gamepad_disconnected: "desconnectat"
|
||||||
|
|
||||||
input:
|
input:
|
||||||
zoom: "zoom"
|
zoom: "zoom"
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
notification:
|
notification:
|
||||||
test: "test notification"
|
test: "test notification"
|
||||||
|
gamepad_connected: "connected"
|
||||||
|
gamepad_disconnected: "disconnected"
|
||||||
|
|
||||||
input:
|
input:
|
||||||
zoom: "zoom"
|
zoom: "zoom"
|
||||||
|
|||||||
@@ -0,0 +1,164 @@
|
|||||||
|
#include "core/input/gamepad_manager.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "core/locale/locale.hpp"
|
||||||
|
#include "core/rendering/notification_presets.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
// A Emscripten, SDL 3.4+ no casa el GUID dels mandos web: Chrome/Android
|
||||||
|
// no aporten Vendor/Product ID i el parser de SDL extreu dades brutes.
|
||||||
|
// El W3C Gamepad API, però, garanteix un layout estàndard quan el mapping
|
||||||
|
// reporta "standard", així que injectem un mapping SDL explícit amb eixe
|
||||||
|
// layout per al GUID del joystick abans que SDL intenti obrir-lo com a
|
||||||
|
// gamepad. Fora d'Emscripten és un no-op.
|
||||||
|
void installWebStandardMapping(SDL_JoystickID jid) {
|
||||||
|
SDL_GUID guid = SDL_GetJoystickGUIDForID(jid);
|
||||||
|
char guid_str[33];
|
||||||
|
SDL_GUIDToString(guid, guid_str, sizeof(guid_str));
|
||||||
|
const char* name = SDL_GetJoystickNameForID(jid);
|
||||||
|
if (name == nullptr || *name == '\0') { name = "Standard Gamepad"; }
|
||||||
|
|
||||||
|
char mapping[512];
|
||||||
|
SDL_snprintf(mapping, sizeof(mapping),
|
||||||
|
"%s,%s,"
|
||||||
|
"a:b0,b:b1,x:b2,y:b3,"
|
||||||
|
"leftshoulder:b4,rightshoulder:b5,"
|
||||||
|
"lefttrigger:b6,righttrigger:b7,"
|
||||||
|
"back:b8,start:b9,"
|
||||||
|
"leftstick:b10,rightstick:b11,"
|
||||||
|
"dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,"
|
||||||
|
"guide:b16,"
|
||||||
|
"leftx:a0,lefty:a1,rightx:a2,righty:a3,"
|
||||||
|
"platform:Emscripten",
|
||||||
|
guid_str,
|
||||||
|
name);
|
||||||
|
SDL_AddGamepadMapping(mapping);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void installWebStandardMapping(SDL_JoystickID /*jid*/) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto GamepadManager::get() -> GamepadManager& {
|
||||||
|
static GamepadManager instance_;
|
||||||
|
return instance_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GamepadManager::prettyName(const char* raw) -> std::string {
|
||||||
|
std::string name = (raw != nullptr && *raw != '\0') ? std::string(raw) : std::string("Gamepad");
|
||||||
|
const auto POS = name.find_first_of("([");
|
||||||
|
if (POS != std::string::npos) { name.erase(POS); }
|
||||||
|
while (!name.empty() && name.back() == ' ') { name.pop_back(); }
|
||||||
|
if (name.size() > 25) { name.resize(25); }
|
||||||
|
if (name.empty()) { name = "Gamepad"; }
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GamepadManager::uniqueName(const std::string& base) const -> std::string {
|
||||||
|
const bool COLLISION = std::ranges::any_of(
|
||||||
|
entries_,
|
||||||
|
[&](const Entry& e) { return e.pretty_name == base; });
|
||||||
|
if (!COLLISION) { return base; }
|
||||||
|
|
||||||
|
// Format " #N" començant a #2 — la primera ocurrència manté el nom net.
|
||||||
|
for (int i = 2; i < 100; ++i) {
|
||||||
|
std::string candidate = base + " #" + std::to_string(i);
|
||||||
|
const bool MATCH = std::ranges::any_of(
|
||||||
|
entries_,
|
||||||
|
[&](const Entry& e) { return e.pretty_name == candidate; });
|
||||||
|
if (!MATCH) { return candidate; }
|
||||||
|
}
|
||||||
|
return base; // cas ultra-improbable: 99 mandos idèntics connectats
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamepadManager::addGamepad(SDL_JoystickID jid, bool notify) {
|
||||||
|
installWebStandardMapping(jid);
|
||||||
|
if (!SDL_IsGamepad(jid)) { return; }
|
||||||
|
SDL_Gamepad* pad = SDL_OpenGamepad(jid);
|
||||||
|
if (pad == nullptr) {
|
||||||
|
std::cout << "GamepadManager: SDL_OpenGamepad(" << jid
|
||||||
|
<< ") ha fallat: " << SDL_GetError() << '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string name = uniqueName(prettyName(SDL_GetGamepadName(pad)));
|
||||||
|
std::cout << "GamepadManager: connectat " << name << '\n';
|
||||||
|
entries_.push_back(Entry{.pad = pad, .instance_id = jid, .pretty_name = name});
|
||||||
|
if (notify) {
|
||||||
|
Notification::notify(name + ' ' + Locale::get().text("notification.gamepad_connected"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamepadManager::removeGamepad(SDL_JoystickID jid) {
|
||||||
|
auto it = std::ranges::find_if(
|
||||||
|
entries_,
|
||||||
|
[jid](const Entry& e) { return e.instance_id == jid; });
|
||||||
|
if (it == entries_.end()) { return; }
|
||||||
|
std::string name = std::move(it->pretty_name);
|
||||||
|
if (it->pad != nullptr) { SDL_CloseGamepad(it->pad); }
|
||||||
|
std::cout << "GamepadManager: desconnectat " << name << '\n';
|
||||||
|
entries_.erase(it);
|
||||||
|
Notification::notify(name + ' ' + Locale::get().text("notification.gamepad_disconnected"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamepadManager::init() {
|
||||||
|
if (initialized_) { return; }
|
||||||
|
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
||||||
|
std::cout << "GamepadManager: SDL_INIT_GAMEPAD ha fallat: "
|
||||||
|
<< SDL_GetError() << '\n';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// `gamecontrollerdb.txt` és opcional. Si no existeix, SDL retorna -1 i
|
||||||
|
// continuem amb les definicions internes del sistema.
|
||||||
|
const int ADDED = SDL_AddGamepadMappingsFromFile("data/input/gamecontrollerdb.txt");
|
||||||
|
if (ADDED > 0) {
|
||||||
|
std::cout << "GamepadManager: " << ADDED << " mappings de gamecontrollerdb.txt\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
SDL_JoystickID* ids = SDL_GetJoysticks(&count);
|
||||||
|
if (ids != nullptr) {
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
addGamepad(ids[i], /*notify=*/false);
|
||||||
|
}
|
||||||
|
SDL_free(ids);
|
||||||
|
}
|
||||||
|
initialized_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamepadManager::shutdown() {
|
||||||
|
for (auto& entry : entries_) {
|
||||||
|
if (entry.pad != nullptr) { SDL_CloseGamepad(entry.pad); }
|
||||||
|
}
|
||||||
|
entries_.clear();
|
||||||
|
if (initialized_) {
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
|
||||||
|
initialized_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GamepadManager::handleEvent(const SDL_Event& ev) {
|
||||||
|
if (ev.type == SDL_EVENT_GAMEPAD_ADDED) {
|
||||||
|
addGamepad(ev.gdevice.which, /*notify=*/true);
|
||||||
|
} else if (ev.type == SDL_EVENT_GAMEPAD_REMOVED) {
|
||||||
|
removeGamepad(ev.gdevice.which);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GamepadManager::getPrimary() const -> SDL_Gamepad* {
|
||||||
|
return entries_.empty() ? nullptr : entries_.front().pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GamepadManager::getCount() const -> std::size_t {
|
||||||
|
return entries_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto GamepadManager::getByPlayerIndex(std::size_t idx) const -> SDL_Gamepad* {
|
||||||
|
return idx < entries_.size() ? entries_[idx].pad : nullptr;
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Gestor global de gamepads. Descobreix els mandos presents a `init()`
|
||||||
|
// sense emetre notificacions (el jugador ja els té posats al llançar el
|
||||||
|
// joc). Durant el runtime, `handleEvent()` processa els events de hot-plug
|
||||||
|
// i emet notificacions visibles via `Notification::notify`.
|
||||||
|
//
|
||||||
|
// El polling real de botons i eixos NO es fa a través d'aquesta classe:
|
||||||
|
// els consumidors obtenen el `SDL_Gamepad*` primari (o l'enèsim per a
|
||||||
|
// multi-jugador futur) i el passen a `InputState::getGamepadButton/Axis`,
|
||||||
|
// que ho filtra pel flag de bloqueig de la UI (mateixa estratègia que
|
||||||
|
// amb el teclat).
|
||||||
|
class GamepadManager {
|
||||||
|
public:
|
||||||
|
static auto get() -> GamepadManager&;
|
||||||
|
|
||||||
|
GamepadManager(const GamepadManager&) = delete;
|
||||||
|
GamepadManager(GamepadManager&&) = delete;
|
||||||
|
auto operator=(const GamepadManager&) -> GamepadManager& = delete;
|
||||||
|
auto operator=(GamepadManager&&) -> GamepadManager& = delete;
|
||||||
|
|
||||||
|
// Inicialitza el subsistema SDL de gamepad, intenta carregar un
|
||||||
|
// `gamecontrollerdb.txt` opcional i obri silenciosament tots els
|
||||||
|
// mandos ja connectats. Cridat una sola vegada des de Director.
|
||||||
|
void init();
|
||||||
|
|
||||||
|
// Tanca tots els gamepads oberts i treu el subsistema SDL. Cridat
|
||||||
|
// des de `~Director` abans de `SDL_Quit` per evitar handles orfes.
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
// Processa SDL_EVENT_GAMEPAD_ADDED i SDL_EVENT_GAMEPAD_REMOVED
|
||||||
|
// emesos durant el runtime. Emet notificacions "<nom> connectat" o
|
||||||
|
// "<nom> desconnectat". El Director decideix quins events li arriben
|
||||||
|
// via `processGamepadEvents()`.
|
||||||
|
void handleEvent(const SDL_Event& ev);
|
||||||
|
|
||||||
|
// Primary = primer gamepad al vector. `nullptr` si no n'hi ha cap.
|
||||||
|
// Ús típic: Player 1 i navegació del ServiceMenu.
|
||||||
|
[[nodiscard]] auto getPrimary() const -> SDL_Gamepad*;
|
||||||
|
|
||||||
|
[[nodiscard]] auto getCount() const -> std::size_t;
|
||||||
|
|
||||||
|
// Retorna el mand del jugador `idx`-èsim (0-indexed) o `nullptr` si
|
||||||
|
// encara no hi ha prou mandos connectats. Reservat per a multi-jugador.
|
||||||
|
[[nodiscard]] auto getByPlayerIndex(std::size_t idx) const -> SDL_Gamepad*;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Entry {
|
||||||
|
SDL_Gamepad* pad{nullptr};
|
||||||
|
SDL_JoystickID instance_id{0};
|
||||||
|
std::string pretty_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
GamepadManager() = default;
|
||||||
|
~GamepadManager() = default;
|
||||||
|
|
||||||
|
// Retalla `raw` al primer '(' o '[', elimina espais finals i talla
|
||||||
|
// a 25 caràcters. Fallback "Gamepad" si queda buit.
|
||||||
|
[[nodiscard]] static auto prettyName(const char* raw) -> std::string;
|
||||||
|
|
||||||
|
// Si `base` ja existeix al vector, afegeix " #2", " #3", ... fins que
|
||||||
|
// trobi un nom lliure. Així dos mandos idèntics es distingeixen a les
|
||||||
|
// notificacions.
|
||||||
|
[[nodiscard]] auto uniqueName(const std::string& base) const -> std::string;
|
||||||
|
|
||||||
|
void addGamepad(SDL_JoystickID jid, bool notify);
|
||||||
|
void removeGamepad(SDL_JoystickID jid);
|
||||||
|
|
||||||
|
std::vector<Entry> entries_;
|
||||||
|
bool initialized_{false};
|
||||||
|
};
|
||||||
@@ -28,4 +28,12 @@ namespace InputState {
|
|||||||
return blocked ? zeroKeyboard() : SDL_GetKeyboardState(nullptr);
|
return blocked ? zeroKeyboard() : SDL_GetKeyboardState(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto getGamepadButton(SDL_Gamepad* gp, SDL_GamepadButton b) -> bool {
|
||||||
|
return (blocked || gp == nullptr) ? false : SDL_GetGamepadButton(gp, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getGamepadAxis(SDL_Gamepad* gp, SDL_GamepadAxis ax) -> Sint16 {
|
||||||
|
return (blocked || gp == nullptr) ? static_cast<Sint16>(0) : SDL_GetGamepadAxis(gp, ax);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace InputState
|
} // namespace InputState
|
||||||
@@ -14,17 +14,9 @@
|
|||||||
// que l'escena els vegi, així que la seva navegació segueix funcionant per
|
// que l'escena els vegi, així que la seva navegació segueix funcionant per
|
||||||
// molt que el flag estigui posat.
|
// molt que el flag estigui posat.
|
||||||
//
|
//
|
||||||
// --- Nota per a l'extensió a gamepad (pròxima iteració) ---
|
// El mateix patró aplica al gamepad: `getGamepadButton`/`getGamepadAxis`
|
||||||
// Quan s'afegeixi suport de comandament, el jugador haurà de llegir eixos
|
// retornen zero/false quan el bloqueig és actiu. Els mandos concrets els
|
||||||
// i botons via polling (p.ex. `SDL_GetGamepadAxis`/`SDL_GetGamepadButton`).
|
// obtén el caller via `GamepadManager::get().getPrimary()` o similars.
|
||||||
// Aquest mòdul s'ha d'ampliar amb:
|
|
||||||
// [[nodiscard]] auto getGamepadAxis(SDL_Gamepad* gp, SDL_GamepadAxis ax)
|
|
||||||
// -> Sint16; // 0 si bloquejat
|
|
||||||
// [[nodiscard]] auto getGamepadButton(SDL_Gamepad* gp, SDL_GamepadButton b)
|
|
||||||
// -> bool; // false si bloquejat
|
|
||||||
// Mateix patró: zero/false quan `blocked_`, pass-through altrament. Així
|
|
||||||
// Player (i el que sigui) només ha de canviar el nom de la crida; el menú
|
|
||||||
// no ha de saber res del gamepad més enllà de processar els seus events.
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
@@ -43,4 +35,12 @@ namespace InputState {
|
|||||||
// guardar-lo frame a frame.
|
// guardar-lo frame a frame.
|
||||||
[[nodiscard]] auto getKeyboardState() -> const bool*;
|
[[nodiscard]] auto getKeyboardState() -> const bool*;
|
||||||
|
|
||||||
|
// Versió filtrada de `SDL_GetGamepadButton`. Retorna false si el bloqueig
|
||||||
|
// és actiu o si `gp` és nullptr (cas habitual: no hi ha cap mand).
|
||||||
|
[[nodiscard]] auto getGamepadButton(SDL_Gamepad* gp, SDL_GamepadButton b) -> bool;
|
||||||
|
|
||||||
|
// Versió filtrada de `SDL_GetGamepadAxis`. Retorna 0 si el bloqueig
|
||||||
|
// és actiu o si `gp` és nullptr.
|
||||||
|
[[nodiscard]] auto getGamepadAxis(SDL_Gamepad* gp, SDL_GamepadAxis ax) -> Sint16;
|
||||||
|
|
||||||
} // namespace InputState
|
} // namespace InputState
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "core/audio/audio.hpp"
|
#include "core/audio/audio.hpp"
|
||||||
|
#include "core/input/gamepad_manager.hpp"
|
||||||
#include "core/input/key_config.hpp"
|
#include "core/input/key_config.hpp"
|
||||||
#include "core/locale/locale.hpp"
|
#include "core/locale/locale.hpp"
|
||||||
#include "core/rendering/notifier.hpp"
|
#include "core/rendering/notifier.hpp"
|
||||||
@@ -102,6 +103,11 @@ Director::Director() {
|
|||||||
// JA_Music_t que continga, així que no queden streams orfes.
|
// JA_Music_t que continga, així que no queden streams orfes.
|
||||||
Audio::init(Options::buildAudioConfig());
|
Audio::init(Options::buildAudioConfig());
|
||||||
|
|
||||||
|
// Gamepad: descobreix mandos presents silenciosament. A partir d'ara
|
||||||
|
// els events SDL_EVENT_GAMEPAD_ADDED/REMOVED arribaran via
|
||||||
|
// `processGamepadEvents()` i dispararan notificacions.
|
||||||
|
GamepadManager::get().init();
|
||||||
|
|
||||||
// Font compartida pels overlays: construïm una sola instància de Text
|
// Font compartida pels overlays: construïm una sola instància de Text
|
||||||
// i la injectem als consumidors (Notifier, RenderInfo) per a que cap
|
// i la injectem als consumidors (Notifier, RenderInfo) per a que cap
|
||||||
// d'ells hagi de conèixer els noms dels recursos concrets.
|
// d'ells hagi de conèixer els noms dels recursos concrets.
|
||||||
@@ -200,6 +206,7 @@ Director::~Director() {
|
|||||||
// desfà les llibreries abans que corren els destructors estàtics.
|
// desfà les llibreries abans que corren els destructors estàtics.
|
||||||
current_scene_.reset();
|
current_scene_.reset();
|
||||||
Resource::Cache::get().clearAudio();
|
Resource::Cache::get().clearAudio();
|
||||||
|
GamepadManager::get().shutdown();
|
||||||
Audio::destroy();
|
Audio::destroy();
|
||||||
Screen::get().shutdown();
|
Screen::get().shutdown();
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
@@ -235,6 +242,32 @@ void Director::processEvents() {
|
|||||||
if (QUIT_REQUESTED) { current_scene_.reset(); }
|
if (QUIT_REQUESTED) { current_scene_.reset(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Director::processGamepadEvents() {
|
||||||
|
// Dues responsabilitats: (1) passar ADDED/REMOVED al GamepadManager
|
||||||
|
// perquè obri/tanqui mandos i emeti les notificacions hot-plug; (2)
|
||||||
|
// tractar el botó BACK com a toggle del menú quan està tancat (el
|
||||||
|
// paral·lel de F9 per al teclat). Quan el menú està obert, el BACK
|
||||||
|
// passa pel pipeline del `ServiceMenu::handleEvent()` via
|
||||||
|
// `processMenuInput()` i allà es converteix en `close()`.
|
||||||
|
for (auto it = EventBuffer::events.begin(); it != EventBuffer::events.end();) {
|
||||||
|
bool consumed = false;
|
||||||
|
if (it->type == SDL_EVENT_GAMEPAD_ADDED || it->type == SDL_EVENT_GAMEPAD_REMOVED) {
|
||||||
|
GamepadManager::get().handleEvent(*it);
|
||||||
|
consumed = true;
|
||||||
|
} else if (it->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN &&
|
||||||
|
it->gbutton.button == SDL_GAMEPAD_BUTTON_BACK &&
|
||||||
|
!ServiceMenu::get().isOpen()) {
|
||||||
|
ServiceMenu::get().toggle();
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
if (consumed) {
|
||||||
|
it = EventBuffer::events.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Director::processMenuInput() {
|
void Director::processMenuInput() {
|
||||||
// Intercepta input abans que l'escena vegi els events. Si el menú està
|
// Intercepta input abans que l'escena vegi els events. Si el menú està
|
||||||
// obert, consumeix TOTS els KEY_DOWN perquè ni GlobalInputs (F1–F8,
|
// obert, consumeix TOTS els KEY_DOWN perquè ni GlobalInputs (F1–F8,
|
||||||
@@ -263,10 +296,12 @@ void Director::switchSceneIfRequested() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto Director::iterate() -> SDL_AppResult {
|
auto Director::iterate() -> SDL_AppResult {
|
||||||
// Ordre important: primer el menú (pot consumir tecles i INJECTAR un
|
// Ordre important: primer els events de gamepad (hot-plug + BACK que
|
||||||
// SDL_EVENT_QUIT des de l'acció "Eixir del joc"), després processEvents
|
// obre el menú quan està tancat), després el menú (pot consumir tecles
|
||||||
// perquè vegi el QUIT al mateix frame i tanqui l'escena — altrament el
|
// i INJECTAR un SDL_EVENT_QUIT des de l'acció "Eixir del joc"), i
|
||||||
// QUIT injectat es perdria quan es netegi EventBuffer al final d'iterate.
|
// finalment processEvents perquè vegi el QUIT al mateix frame — altrament
|
||||||
|
// el QUIT injectat es perdria quan es netegi EventBuffer al final.
|
||||||
|
processGamepadEvents();
|
||||||
processMenuInput();
|
processMenuInput();
|
||||||
processEvents();
|
processEvents();
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class Director {
|
|||||||
|
|
||||||
void createSystemFolder();
|
void createSystemFolder();
|
||||||
void processEvents();
|
void processEvents();
|
||||||
|
static void processGamepadEvents();
|
||||||
static void processMenuInput();
|
static void processMenuInput();
|
||||||
void switchSceneIfRequested();
|
void switchSceneIfRequested();
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user