soport per a gamepad
This commit is contained in:
@@ -29,6 +29,7 @@ set(APP_SOURCES
|
|||||||
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp
|
source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp
|
||||||
|
|
||||||
# Core - Input (nova capa)
|
# Core - Input (nova capa)
|
||||||
|
source/core/input/gamepad.cpp
|
||||||
source/core/input/global_inputs.cpp
|
source/core/input/global_inputs.cpp
|
||||||
source/core/input/mouse.cpp
|
source/core/input/mouse.cpp
|
||||||
|
|
||||||
|
|||||||
167
source/core/input/gamepad.cpp
Normal file
167
source/core/input/gamepad.cpp
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#include "core/input/gamepad.hpp"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "core/jail/jinput.hpp"
|
||||||
|
#include "core/rendering/menu.hpp"
|
||||||
|
#include "game/options.hpp"
|
||||||
|
|
||||||
|
namespace Gamepad {
|
||||||
|
|
||||||
|
static SDL_Gamepad* pad_ = nullptr;
|
||||||
|
static SDL_JoystickID pad_id_ = 0;
|
||||||
|
|
||||||
|
// Dead-zone del stick esquerre (rang Sint16: -32768..32767)
|
||||||
|
static constexpr Sint16 STICK_DEADZONE = 12000;
|
||||||
|
|
||||||
|
// Estat previ per a detecció de flanc (edge-triggered)
|
||||||
|
static bool prev_up_ = false;
|
||||||
|
static bool prev_down_ = false;
|
||||||
|
static bool prev_left_ = false;
|
||||||
|
static bool prev_right_ = false;
|
||||||
|
static bool prev_a_ = false;
|
||||||
|
static bool prev_b_ = false;
|
||||||
|
static bool prev_start_ = false;
|
||||||
|
static bool prev_back_ = false;
|
||||||
|
|
||||||
|
static void openFirstGamepad() {
|
||||||
|
int count = 0;
|
||||||
|
SDL_JoystickID* ids = SDL_GetGamepads(&count);
|
||||||
|
if (ids && count > 0) {
|
||||||
|
pad_ = SDL_OpenGamepad(ids[0]);
|
||||||
|
if (pad_) {
|
||||||
|
pad_id_ = ids[0];
|
||||||
|
SDL_Log("Gamepad connectat: %s", SDL_GetGamepadName(pad_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ids) SDL_free(ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) {
|
||||||
|
SDL_Log("No s'ha pogut inicialitzar SDL_INIT_GAMEPAD: %s", SDL_GetError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openFirstGamepad();
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy() {
|
||||||
|
if (pad_) {
|
||||||
|
SDL_CloseGamepad(pad_);
|
||||||
|
pad_ = nullptr;
|
||||||
|
pad_id_ = 0;
|
||||||
|
}
|
||||||
|
SDL_QuitSubSystem(SDL_INIT_GAMEPAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto isConnected() -> bool {
|
||||||
|
return pad_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEvent(const SDL_Event& event) {
|
||||||
|
if (event.type == SDL_EVENT_GAMEPAD_ADDED) {
|
||||||
|
if (!pad_) {
|
||||||
|
pad_ = SDL_OpenGamepad(event.gdevice.which);
|
||||||
|
if (pad_) {
|
||||||
|
pad_id_ = event.gdevice.which;
|
||||||
|
SDL_Log("Gamepad connectat: %s", SDL_GetGamepadName(pad_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event.type == SDL_EVENT_GAMEPAD_REMOVED) {
|
||||||
|
if (pad_ && event.gdevice.which == pad_id_) {
|
||||||
|
SDL_Log("Gamepad desconnectat");
|
||||||
|
SDL_CloseGamepad(pad_);
|
||||||
|
pad_ = nullptr;
|
||||||
|
pad_id_ = 0;
|
||||||
|
// Neteja qualsevol tecla virtual que poguera estar premuda
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_UP, false);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_DOWN, false);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_LEFT, false);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_RIGHT, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emet un parell de KEY_DOWN + KEY_UP sintètics que passaran pel handleEvents
|
||||||
|
// del Director (menú, ESC, F12, etc.)
|
||||||
|
static void pushKey(SDL_Scancode sc) {
|
||||||
|
SDL_Event e;
|
||||||
|
SDL_zero(e);
|
||||||
|
e.type = SDL_EVENT_KEY_DOWN;
|
||||||
|
e.key.scancode = sc;
|
||||||
|
e.key.repeat = false;
|
||||||
|
e.key.down = true;
|
||||||
|
SDL_PushEvent(&e);
|
||||||
|
e.type = SDL_EVENT_KEY_UP;
|
||||||
|
e.key.down = false;
|
||||||
|
SDL_PushEvent(&e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
if (!pad_) return;
|
||||||
|
|
||||||
|
// D-pad
|
||||||
|
bool dup = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_DPAD_UP);
|
||||||
|
bool ddn = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
|
||||||
|
bool dlt = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
|
||||||
|
bool drt = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
|
||||||
|
|
||||||
|
// Stick esquerre amb dead-zone
|
||||||
|
Sint16 lx = SDL_GetGamepadAxis(pad_, SDL_GAMEPAD_AXIS_LEFTX);
|
||||||
|
Sint16 ly = SDL_GetGamepadAxis(pad_, SDL_GAMEPAD_AXIS_LEFTY);
|
||||||
|
bool sup = ly < -STICK_DEADZONE;
|
||||||
|
bool sdn = ly > STICK_DEADZONE;
|
||||||
|
bool slt = lx < -STICK_DEADZONE;
|
||||||
|
bool srt = lx > STICK_DEADZONE;
|
||||||
|
|
||||||
|
bool up = dup || sup;
|
||||||
|
bool dn = ddn || sdn;
|
||||||
|
bool lt = dlt || slt;
|
||||||
|
bool rt = drt || srt;
|
||||||
|
|
||||||
|
// Botons
|
||||||
|
bool a = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_SOUTH); // A/Cross
|
||||||
|
bool b = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_EAST); // B/Circle
|
||||||
|
bool start = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_START);
|
||||||
|
bool back = SDL_GetGamepadButton(pad_, SDL_GAMEPAD_BUTTON_BACK);
|
||||||
|
|
||||||
|
// Start → obre/tanca menú (flanc)
|
||||||
|
if (start && !prev_start_) pushKey(Options::keys_gui.menu_toggle);
|
||||||
|
// Back → ESC (flanc)
|
||||||
|
if (back && !prev_back_) pushKey(SDL_SCANCODE_ESCAPE);
|
||||||
|
|
||||||
|
if (Menu::isOpen()) {
|
||||||
|
// Navegació del menú per flanc
|
||||||
|
if (up && !prev_up_) pushKey(SDL_SCANCODE_UP);
|
||||||
|
if (dn && !prev_down_) pushKey(SDL_SCANCODE_DOWN);
|
||||||
|
if (lt && !prev_left_) pushKey(SDL_SCANCODE_LEFT);
|
||||||
|
if (rt && !prev_right_) pushKey(SDL_SCANCODE_RIGHT);
|
||||||
|
if (a && !prev_a_) pushKey(SDL_SCANCODE_RETURN);
|
||||||
|
if (b && !prev_b_) pushKey(SDL_SCANCODE_BACKSPACE);
|
||||||
|
|
||||||
|
// Assegura que el joc no rep tecles de moviment mentre el menú està obert
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_UP, false);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_DOWN, false);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_LEFT, false);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_RIGHT, false);
|
||||||
|
} else {
|
||||||
|
// Moviment al joc — level-triggered (polling)
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_UP, up);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_DOWN, dn);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_LEFT, lt);
|
||||||
|
JI_SetVirtualKey(SDL_SCANCODE_RIGHT, rt);
|
||||||
|
// Botó A al joc: emet Enter per avançar seqüències (JI_AnyKey)
|
||||||
|
if (a && !prev_a_) pushKey(SDL_SCANCODE_RETURN);
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_up_ = up;
|
||||||
|
prev_down_ = dn;
|
||||||
|
prev_left_ = lt;
|
||||||
|
prev_right_ = rt;
|
||||||
|
prev_a_ = a;
|
||||||
|
prev_b_ = b;
|
||||||
|
prev_start_ = start;
|
||||||
|
prev_back_ = back;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Gamepad
|
||||||
18
source/core/input/gamepad.hpp
Normal file
18
source/core/input/gamepad.hpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
// Suport de gamepad: mapeja D-pad/stick a les tecles de fletxa virtuals del joc
|
||||||
|
// i emet events SDL sintètics per a navegació del menú (F12, ESC, Enter, etc.)
|
||||||
|
namespace Gamepad {
|
||||||
|
void init();
|
||||||
|
void destroy();
|
||||||
|
|
||||||
|
// Hot-plug: processa events SDL_EVENT_GAMEPAD_ADDED/REMOVED
|
||||||
|
void handleEvent(const SDL_Event& event);
|
||||||
|
|
||||||
|
// Poll d'estat cada frame — cridat des del Director
|
||||||
|
void update();
|
||||||
|
|
||||||
|
[[nodiscard]] auto isConnected() -> bool;
|
||||||
|
} // namespace Gamepad
|
||||||
@@ -20,6 +20,14 @@ void JI_SetInputBlocked(bool blocked) {
|
|||||||
input_blocked = blocked;
|
input_blocked = blocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Uint8 virtual_keystates[SDL_SCANCODE_COUNT] = {0};
|
||||||
|
|
||||||
|
void JI_SetVirtualKey(int scancode, bool pressed) {
|
||||||
|
if (scancode >= 0 && scancode < SDL_SCANCODE_COUNT) {
|
||||||
|
virtual_keystates[scancode] = pressed ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void JI_moveCheats(Uint8 new_key) {
|
void JI_moveCheats(Uint8 new_key) {
|
||||||
cheat[0] = cheat[1];
|
cheat[0] = cheat[1];
|
||||||
cheat[1] = cheat[2];
|
cheat[1] = cheat[2];
|
||||||
@@ -47,7 +55,8 @@ bool JI_KeyPressed(int key) {
|
|||||||
if (input_blocked) return false;
|
if (input_blocked) return false;
|
||||||
// ESC bloquejada pel Director (primera pulsació mostra notificació)
|
// ESC bloquejada pel Director (primera pulsació mostra notificació)
|
||||||
if (key == SDL_SCANCODE_ESCAPE && Director::get()->isEscBlocked()) return false;
|
if (key == SDL_SCANCODE_ESCAPE && Director::get()->isEscBlocked()) return false;
|
||||||
return keystates[key] != 0;
|
if (key < 0 || key >= SDL_SCANCODE_COUNT) return false;
|
||||||
|
return keystates[key] != 0 || virtual_keystates[key] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JI_CheatActivated(const char* cheat_code) {
|
bool JI_CheatActivated(const char* cheat_code) {
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ void JI_DisableKeyboard(Uint32 time);
|
|||||||
// Bloqueja tot l'input cap al joc (JI_KeyPressed retorna false per a tot)
|
// Bloqueja tot l'input cap al joc (JI_KeyPressed retorna false per a tot)
|
||||||
void JI_SetInputBlocked(bool blocked);
|
void JI_SetInputBlocked(bool blocked);
|
||||||
|
|
||||||
|
// Estableix l'estat d'una tecla virtual (p.ex. des del gamepad).
|
||||||
|
// JI_KeyPressed retorna true si el teclat real O la virtual estan premudes.
|
||||||
|
void JI_SetVirtualKey(int scancode, bool pressed);
|
||||||
|
|
||||||
void JI_Update();
|
void JI_Update();
|
||||||
|
|
||||||
bool JI_KeyPressed(int key);
|
bool JI_KeyPressed(int key);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "core/input/gamepad.hpp"
|
||||||
#include "core/input/global_inputs.hpp"
|
#include "core/input/global_inputs.hpp"
|
||||||
#include "core/input/mouse.hpp"
|
#include "core/input/mouse.hpp"
|
||||||
#include "core/jail/jgame.hpp"
|
#include "core/jail/jgame.hpp"
|
||||||
@@ -22,9 +23,11 @@ Director* Director::instance_ = nullptr;
|
|||||||
|
|
||||||
void Director::init() {
|
void Director::init() {
|
||||||
instance_ = new Director();
|
instance_ = new Director();
|
||||||
|
Gamepad::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Director::destroy() {
|
void Director::destroy() {
|
||||||
|
Gamepad::destroy();
|
||||||
delete instance_;
|
delete instance_;
|
||||||
instance_ = nullptr;
|
instance_ = nullptr;
|
||||||
}
|
}
|
||||||
@@ -50,6 +53,7 @@ void Director::run() {
|
|||||||
Uint32 frame_start = SDL_GetTicks();
|
Uint32 frame_start = SDL_GetTicks();
|
||||||
|
|
||||||
handleEvents();
|
handleEvents();
|
||||||
|
Gamepad::update();
|
||||||
GlobalInputs::handle();
|
GlobalInputs::handle();
|
||||||
Mouse::updateCursorVisibility();
|
Mouse::updateCursorVisibility();
|
||||||
|
|
||||||
@@ -108,6 +112,11 @@ void Director::handleEvents() {
|
|||||||
JG_QuitSignal();
|
JG_QuitSignal();
|
||||||
requestQuit();
|
requestQuit();
|
||||||
}
|
}
|
||||||
|
// Hot-plug de gamepad
|
||||||
|
if (event.type == SDL_EVENT_GAMEPAD_ADDED || event.type == SDL_EVENT_GAMEPAD_REMOVED) {
|
||||||
|
Gamepad::handleEvent(event);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Menú: F12 (o tecla configurada) obre/tanca el menú flotant
|
// Menú: F12 (o tecla configurada) obre/tanca el menú flotant
|
||||||
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
|
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
|
||||||
event.key.scancode == Options::keys_gui.menu_toggle) {
|
event.key.scancode == Options::keys_gui.menu_toggle) {
|
||||||
|
|||||||
Reference in New Issue
Block a user