first commit

This commit is contained in:
root
2026-03-30 10:05:53 +00:00
commit 59f6583862
8465 changed files with 541514 additions and 0 deletions

View File

@@ -0,0 +1,339 @@
include(../cmake/cvars.cmake)
add_library(libultraship STATIC)
set_target_properties(libultraship PROPERTIES PREFIX "")
set_property(TARGET libultraship PROPERTY CXX_STANDARD 20)
# Need to set C11 for libgfxd (_Alignas)
set_property(TARGET libultraship PROPERTY C_STANDARD 11)
set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include)
option(USE_OPENGLES "Enable GLES3" OFF)
option(GFX_DEBUG_DISASSEMBLER "Enable libgfxd" OFF)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
use_props(${PROJECT_NAME} "${CMAKE_CONFIGURATION_TYPES}" "${DEFAULT_CXX_PROPS}")
endif()
#=================== Top-Level ===================
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/install_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/install_config.h @ONLY)
file(GLOB Source_Files__TopLevel RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.h" "*.cpp")
source_group("" FILES ${Source_Files__TopLevel})
target_sources(libultraship PRIVATE ${Source_Files__TopLevel})
#=================== Audio ===================
file(GLOB Source_Files__Audio RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "audio/*.h" "audio/*.cpp")
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
list(FILTER Source_Files__Audio EXCLUDE REGEX "audio/WasapiAudioPlayer.*")
endif()
source_group("audio" FILES ${Source_Files__Audio})
target_sources(libultraship PRIVATE ${Source_Files__Audio})
#=================== Controller ===================
file(GLOB_RECURSE Source_Files__Controller RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "controller/*.h" "controller/*.cpp")
source_group("controller" FILES ${Source_Files__Controller})
target_sources(libultraship PRIVATE ${Source_Files__Controller})
#=================== Config ===================
file(GLOB Source_Files__Config RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "config/*.h" "config/*.cpp")
source_group("config" FILES ${Source_Files__Config})
target_sources(libultraship PRIVATE ${Source_Files__Config})
#=================== Public ===================
source_group("public")
file(GLOB Source_Files__Public__Libultra RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "public/libultra/*.h" "public/libultra/*.cpp")
source_group("public/libultra" FILES ${Source_Files__Public__Libultra})
file(GLOB Source_Files__Public__Bridge RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "public/bridge/*.h" "public/bridge/*.cpp")
source_group("public/bridge" FILES ${Source_Files__Public__Bridge})
target_sources(libultraship PRIVATE ${Source_Files__Public__Libultra} ${Source_Files__Public__Bridge})
#=================== Debug ===================
file(GLOB Source_Files__Debug RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "debug/*.h" "debug/*.cpp")
source_group("debug" FILES ${Source_Files__Debug})
target_sources(libultraship PRIVATE ${Source_Files__Debug})
#=================== Log ===================
file(GLOB Source_Files__Log RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "log/*.h" "log/*.cpp")
source_group("log" FILES ${Source_Files__Log})
target_sources(libultraship PRIVATE ${Source_Files__Log})
#=================== Window ===================
file(GLOB Source_Files__Window RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "window/*.h" "window/*.cpp")
source_group("window" FILES ${Source_Files__Window})
target_sources(libultraship PRIVATE ${Source_Files__Window})
#=================== Gui ===================
file(GLOB_RECURSE Source_Files__Window__Gui RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "window/gui/*.h" "window/gui/*.cpp")
source_group("window/gui" FILES ${Source_Files__Window__Gui})
target_sources(libultraship PRIVATE ${Source_Files__Window__Gui})
#=================== Utils ===================
file(GLOB_RECURSE Source_Files__Utils RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "utils/*.h" "utils/*.cpp" "utils/*.c")
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
list(APPEND Source_Files__Utils ${CMAKE_CURRENT_SOURCE_DIR}/utils/AppleFolderManager.mm)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/utils/AppleFolderManager.mm PROPERTIES COMPILE_FLAGS -fno-objc-arc)
else()
list(REMOVE_ITEM Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/utils/AppleFolderManager.h)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
list(APPEND Source_Files__Utils ${CMAKE_CURRENT_SOURCE_DIR}/utils/macUtils.mm)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/utils/macUtils.mm PROPERTIES COMPILE_FLAGS -fno-objc-arc)
else()
list(REMOVE_ITEM Header_Files__include ${CMAKE_CURRENT_SOURCE_DIR}/utils/macUtils.h)
endif()
source_group("utils" FILES ${Source_Files__Utils})
target_sources(libultraship PRIVATE ${Source_Files__Utils})
#=================== Port ===================
if (CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(Source_Files__Port
${CMAKE_CURRENT_SOURCE_DIR}/port/mobile/MobileImpl.h
${CMAKE_CURRENT_SOURCE_DIR}/port/mobile/MobileImpl.cpp
)
endif()
source_group("port" FILES ${Source_Files__Port})
target_sources(libultraship PRIVATE ${Source_Files__Port})
#=================== Resource ===================
file(GLOB Source_Files__Resource RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "resource/*.h" "resource/*.cpp")
source_group("resource" FILES ${Source_Files__Resource})
file(GLOB Source_Files__Resource__Types RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "resource/type/*.h" "resource/type/*.cpp")
source_group("resource/type" FILES ${Source_Files__Resource__Types})
file(GLOB Source_Files__Resource__Factories RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "resource/factory/*.h" "resource/factory/*.cpp")
source_group("resource/factory" FILES ${Source_Files__Resource__Factories})
file(GLOB Source_Files__Resource__Archive RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "resource/archive/*.h" "resource/archive/*.cpp")
source_group("resource/archive" FILES ${Source_Files__Resource__Archive})
target_sources(libultraship PRIVATE ${Source_Files__Resource} ${Source_Files__Resource__Types} ${Source_Files__Resource__Factories} ${Source_Files__Resource__Archive})
#=================== Graphic ===================
file(GLOB Source_Files__Graphic RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "graphic/Fast3D/*.h" "graphic/Fast3D/*.cpp" "graphic/Fast3D/*.c")
file(GLOB Source_Files__Graphic_Debug RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "graphic/Fast3D/debug/*.h" "graphic/Fast3D/debug/*.cpp" "graphic/Fast3D/debug/*.c")
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
list(FILTER Source_Files__Graphic EXCLUDE REGEX "graphic/Fast3D/gfx_dxgi*")
list(FILTER Source_Files__Graphic EXCLUDE REGEX "graphic/Fast3D/gfx_direct3d*")
list(FILTER Source_Files__Graphic EXCLUDE REGEX "graphic/Fast3D/dxsdk/*")
endif()
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
list(FILTER Source_Files__Graphic EXCLUDE REGEX "graphic/Fast3D/gfx_metal*")
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "iOS")
list(FILTER Source_Files__Graphic EXCLUDE REGEX "graphic/Fast3D/gfx_opengl*")
endif()
source_group("graphic" FILES ${Source_Files__Graphic})
source_group("graphic_debug" FILES ${Source_Files__Graphic_Debug})
target_sources(libultraship PRIVATE ${Source_Files__Graphic})
target_sources(libultraship PRIVATE ${Source_Files__Graphic_Debug})
#=================== Packages & Includes ===================
target_include_directories(libultraship
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../extern ${CMAKE_CURRENT_BINARY_DIR} $<$<BOOL:${GFX_DEBUG_DISASSEMBLER}>:${CMAKE_CURRENT_SOURCE_DIR}/../../ZAPDTR/lib/libgfxd>
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${INCLUDE_DIR} ${ADDITIONAL_LIB_INCLUDES}
)
#=================== Linking ===================
if(NOT EXCLUDE_MPQ_SUPPORT)
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows" AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS")
target_link_libraries(libultraship PUBLIC "$<LINK_LIBRARY:WHOLE_ARCHIVE,storm>")
else()
target_link_libraries(libultraship PUBLIC storm)
endif()
endif()
target_link_libraries(libultraship
PUBLIC ImGui
)
target_link_libraries(libultraship PRIVATE stb)
if (GFX_DEBUG_DISASSEMBLER)
target_link_libraries(libultraship PRIVATE libgfxd)
endif()
find_package(libzip REQUIRED)
target_link_libraries(libultraship PRIVATE libzip::zip)
find_package(nlohmann_json REQUIRED)
target_link_libraries(libultraship PUBLIC nlohmann_json::nlohmann_json)
find_package(tinyxml2 REQUIRED)
target_link_libraries(libultraship PUBLIC tinyxml2::tinyxml2)
find_package(spdlog REQUIRED)
target_link_libraries(libultraship PUBLIC spdlog::spdlog)
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(libultraship PRIVATE Threads::Threads)
find_library(COCOA Cocoa)
target_link_libraries(libultraship PRIVATE ${COCOA})
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
find_Library(OSX_FOUNDATION Foundation)
find_Library(OSX_AVFOUNDATION AVFoundation)
find_library(METAL Metal)
find_library(QUARTZCORE QuartzCore)
target_link_libraries(libultraship PRIVATE ${OSX_FOUNDATION} ${OSX_AVFOUNDATION} ${METAL} ${QUARTZCORE} ${CMAKE_DL_LIBS})
endif()
if (USE_OPENGLES)
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
find_library(OPENGLES_LIBRARY GLESv3)
target_link_libraries(libultraship PRIVATE GLESv3)
else()
find_library(OPENGLES_LIBRARY GLESv2)
target_link_libraries(libultraship PRIVATE GLESv2)
endif()
endif()
target_link_libraries(libultraship PUBLIC prism)
#=================== Compile Options & Defs ===================
target_compile_definitions(libultraship PRIVATE ${GBI_UCODE})
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_definitions(${PROJECT_NAME} PRIVATE
WIN32
_CONSOLE
_CRT_SECURE_NO_WARNINGS
ENABLE_DX11
NOMINMAX
)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES
)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "iOS")
target_compile_definitions(libultraship PRIVATE
$<$<CONFIG:Debug>:_DEBUG>
$<$<NOT:$<CONFIG:Debug>>:NDEBUG>
$<$<CONFIG:Debug>:SPDLOG_ACTIVE_LEVEL=0>
$<$<NOT:$<CONFIG:Debug>>:SPDLOG_ACTIVE_LEVEL=1>
__IOS__
)
set_xcode_property(${PROJECT_NAME} PRODUCT_BUNDLE_IDENTIFIER ${BUNDLE_ID} All)
if(NOT SIGN_LIBRARY)
set_target_properties(${PROJECT_NAME} PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
)
if(TARGET SDL2)
set_target_properties(SDL2 PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
)
endif()
set_target_properties(zip PROPERTIES
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
)
endif()
else()
target_compile_definitions(libultraship PRIVATE
ENABLE_OPENGL
$<$<CONFIG:Debug>:_DEBUG>
$<$<NOT:$<CONFIG:Debug>>:NDEBUG>
$<$<CONFIG:Debug>:SPDLOG_ACTIVE_LEVEL=0>
$<$<NOT:$<CONFIG:Debug>>:SPDLOG_ACTIVE_LEVEL=1>
$<$<BOOL:${USE_OPENGLES}>:USE_OPENGLES>
$<$<BOOL:${GFX_DEBUG_DISASSEMBLER}>:GFX_DEBUG_DISASSEMBLER>
)
endif()
if(MSVC)
target_compile_options(libultraship PRIVATE
$<$<CONFIG:Debug>:
/Od;
/Oi-
>
$<$<CONFIG:Release>:
/Oi;
/Gy
>
/permissive-;
/MP;
/sdl;
/W3;
${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT};
${DEFAULT_CXX_EXCEPTION_HANDLING};
)
target_link_options(libultraship PRIVATE
$<$<CONFIG:Release>:
/OPT:REF;
/OPT:ICF
>
/SUBSYSTEM:CONSOLE
)
endif()
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang")
target_compile_options(libultraship PRIVATE
-Wall
-Wextra
-Wno-error
-Wno-unused-variable
-Wno-unused-parameter
-Wno-unused-function
-Wno-parentheses
-Wno-narrowing
-Wno-missing-field-initializers
$<$<COMPILE_LANGUAGE:C,OBJC>:-Wno-implicit-function-declaration>
$<$<COMPILE_LANGUAGE:C,OBJC>:-Wno-int-conversion>
)
if (MSVC)
target_compile_options(libultraship PRIVATE
-Wno-implicit-function-declaration
)
endif()
endif()

View File

@@ -0,0 +1,487 @@
#include "Context.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
#include <iostream>
#include <spdlog/async.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include "install_config.h"
#include "graphic/Fast3D/debug/GfxDebugger.h"
#ifdef _WIN32
#include <tchar.h>
#endif
#ifdef __APPLE__
#include "utils/AppleFolderManager.h"
#include <unistd.h>
#include <pwd.h>
#endif
namespace Ship {
std::weak_ptr<Context> Context::mContext;
std::shared_ptr<Context> Context::GetInstance() {
return mContext.lock();
}
Context::~Context() {
SPDLOG_TRACE("destruct context");
GetWindow()->SaveWindowToConfig();
// Explicitly destructing everything so that logging is done last.
mAudio = nullptr;
mWindow = nullptr;
mConsole = nullptr;
mCrashHandler = nullptr;
mControlDeck = nullptr;
mResourceManager = nullptr;
mConsoleVariables = nullptr;
GetConfig()->Save();
mConfig = nullptr;
spdlog::shutdown();
}
std::shared_ptr<Context>
Context::CreateInstance(const std::string name, const std::string shortName, const std::string configFilePath,
const std::vector<std::string>& archivePaths, const std::unordered_set<uint32_t>& validHashes,
uint32_t reservedThreadCount, AudioSettings audioSettings, std::shared_ptr<Window> window,
std::shared_ptr<ControlDeck> controlDeck) {
if (mContext.expired()) {
auto shared = std::make_shared<Context>(name, shortName, configFilePath);
mContext = shared;
if (shared->Init(archivePaths, validHashes, reservedThreadCount, audioSettings, window, controlDeck)) {
return shared;
} else {
SPDLOG_ERROR("Failed to initialize");
return nullptr;
};
}
SPDLOG_DEBUG("Trying to create a context when it already exists. Returning existing.");
return GetInstance();
}
std::shared_ptr<Context> Context::CreateUninitializedInstance(const std::string name, const std::string shortName,
const std::string configFilePath) {
if (mContext.expired()) {
auto shared = std::make_shared<Context>(name, shortName, configFilePath);
mContext = shared;
return shared;
}
SPDLOG_DEBUG("Trying to create an uninitialized context when it already exists. Returning existing.");
return GetInstance();
}
Context::Context(std::string name, std::string shortName, std::string configFilePath)
: mConfigFilePath(std::move(configFilePath)), mName(std::move(name)), mShortName(std::move(shortName)) {
}
bool Context::Init(const std::vector<std::string>& archivePaths, const std::unordered_set<uint32_t>& validHashes,
uint32_t reservedThreadCount, AudioSettings audioSettings, std::shared_ptr<Window> window,
std::shared_ptr<ControlDeck> controlDeck) {
return InitLogging() && InitConfiguration() && InitConsoleVariables() &&
InitResourceManager(archivePaths, validHashes, reservedThreadCount) && InitControlDeck(controlDeck) &&
InitCrashHandler() && InitConsole() && InitWindow(window) && InitAudio(audioSettings) && InitGfxDebugger();
}
bool Context::InitLogging() {
if (GetLogger() != nullptr) {
return true;
}
try {
// Setup Logging
spdlog::init_thread_pool(8192, 1);
std::vector<spdlog::sink_ptr> sinks;
#if (!defined(_WIN32)) || defined(_DEBUG)
#if defined(_DEBUG) && defined(_WIN32)
// LLVM on Windows allocs a hidden console in its entrypoint function.
// We free that console here to create our own.
FreeConsole();
if (AllocConsole() == 0) {
throw std::system_error(GetLastError(), std::generic_category(), "Failed to create debug console");
}
SetConsoleOutputCP(CP_UTF8);
FILE* fDummy;
freopen_s(&fDummy, "CONOUT$", "w", stdout);
freopen_s(&fDummy, "CONOUT$", "w", stderr);
freopen_s(&fDummy, "CONIN$", "r", stdin);
std::cout.clear();
std::clog.clear();
std::cerr.clear();
std::cin.clear();
HANDLE hConOut = CreateFile(_T("CONOUT$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hConIn = CreateFile(_T("CONIN$"), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
SetStdHandle(STD_OUTPUT_HANDLE, hConOut);
SetStdHandle(STD_ERROR_HANDLE, hConOut);
SetStdHandle(STD_INPUT_HANDLE, hConIn);
std::wcout.clear();
std::wclog.clear();
std::wcerr.clear();
std::wcin.clear();
#endif
auto systemConsoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
// systemConsoleSink->set_level(spdlog::level::trace);
sinks.push_back(systemConsoleSink);
#endif
auto logPath = GetPathRelativeToAppDirectory(("logs/" + GetName() + ".log"));
auto fileSink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logPath, 1024 * 1024 * 10, 10);
#ifdef _DEBUG
fileSink->set_level(spdlog::level::trace);
#else
fileSink->set_level(spdlog::level::debug);
#endif
sinks.push_back(fileSink);
mLogger = std::make_shared<spdlog::async_logger>(GetName(), sinks.begin(), sinks.end(), spdlog::thread_pool(),
spdlog::async_overflow_policy::block);
#ifdef _DEBUG
GetLogger()->set_level(spdlog::level::trace);
#else
GetLogger()->set_level(spdlog::level::debug);
#endif
#if defined(_DEBUG)
GetLogger()->flush_on(spdlog::level::trace);
#endif
GetLogger()->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%@] [%l] %v");
spdlog::register_logger(GetLogger());
spdlog::set_default_logger(GetLogger());
return true;
} catch (const spdlog::spdlog_ex& ex) {
std::cout << "Log initialization failed: " << ex.what() << std::endl;
return false;
}
}
bool Context::InitConfiguration() {
if (GetConfig() != nullptr) {
return true;
}
mConfig = std::make_shared<Config>(GetPathRelativeToAppDirectory(mConfigFilePath));
if (GetConfig() == nullptr) {
SPDLOG_ERROR("Failed to initialize config");
return false;
}
return true;
}
bool Context::InitConsoleVariables() {
if (GetConsoleVariables() != nullptr) {
return true;
}
mConsoleVariables = std::make_shared<ConsoleVariable>();
if (GetConsoleVariables() == nullptr) {
SPDLOG_ERROR("Failed to initialize console variables");
return false;
}
return true;
}
bool Context::InitResourceManager(const std::vector<std::string>& archivePaths,
const std::unordered_set<uint32_t>& validHashes, uint32_t reservedThreadCount) {
if (GetResourceManager() != nullptr) {
return true;
}
mMainPath = GetConfig()->GetString("Game.Main Archive", GetAppDirectoryPath());
mPatchesPath = GetConfig()->GetString("Game.Patches Archive", GetAppDirectoryPath() + "/mods");
if (archivePaths.empty()) {
std::vector<std::string> paths = std::vector<std::string>();
paths.push_back(mMainPath);
paths.push_back(mPatchesPath);
mResourceManager = std::make_shared<ResourceManager>();
GetResourceManager()->Init(paths, validHashes, reservedThreadCount);
} else {
mResourceManager = std::make_shared<ResourceManager>();
GetResourceManager()->Init(archivePaths, validHashes, reservedThreadCount);
}
if (!GetResourceManager()->IsLoaded()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "OTR file not found",
"Main OTR file not found. Please generate one", nullptr);
SPDLOG_ERROR("Main OTR file not found!");
#ifdef __IOS__
// We need this exit to close the app when we dismiss the dialog
exit(0);
#endif
return false;
}
return true;
}
bool Context::InitControlDeck(std::shared_ptr<ControlDeck> controlDeck) {
if (GetControlDeck() != nullptr) {
return true;
}
mControlDeck = controlDeck;
if (GetControlDeck() == nullptr) {
SPDLOG_ERROR("Failed to initialize control deck");
return false;
}
return true;
}
bool Context::InitCrashHandler() {
if (GetCrashHandler() != nullptr) {
return true;
}
mCrashHandler = std::make_shared<CrashHandler>();
if (GetCrashHandler() == nullptr) {
SPDLOG_ERROR("Failed to initialize crash handler");
return false;
}
return true;
}
bool Context::InitAudio(AudioSettings settings) {
if (GetAudio() != nullptr) {
return true;
}
mAudio = std::make_shared<Audio>(settings);
if (GetAudio() == nullptr) {
SPDLOG_ERROR("Failed to initialize audio");
return false;
}
GetAudio()->Init();
return true;
}
bool Context::InitGfxDebugger() {
if (GetGfxDebugger() != nullptr) {
return true;
}
mGfxDebugger = std::make_shared<Fast::GfxDebugger>();
if (GetGfxDebugger() == nullptr) {
SPDLOG_ERROR("Failed to initialize gfx debugger");
return false;
}
return true;
}
bool Context::InitConsole() {
if (GetConsole() != nullptr) {
return true;
}
mConsole = std::make_shared<Console>();
if (GetConsole() == nullptr) {
SPDLOG_ERROR("Failed to initialize console");
return false;
}
GetConsole()->Init();
return true;
}
bool Context::InitWindow(std::shared_ptr<Window> window) {
if (GetWindow() != nullptr) {
return true;
}
mWindow = window;
if (GetWindow() == nullptr) {
SPDLOG_ERROR("Failed to initialize window");
return false;
}
GetWindow()->Init();
return true;
}
std::shared_ptr<ConsoleVariable> Context::GetConsoleVariables() {
return mConsoleVariables;
}
std::shared_ptr<spdlog::logger> Context::GetLogger() {
return mLogger;
}
std::shared_ptr<Config> Context::GetConfig() {
return mConfig;
}
std::shared_ptr<ResourceManager> Context::GetResourceManager() {
return mResourceManager;
}
std::shared_ptr<ControlDeck> Context::GetControlDeck() {
return mControlDeck;
}
std::shared_ptr<CrashHandler> Context::GetCrashHandler() {
return mCrashHandler;
}
std::shared_ptr<Window> Context::GetWindow() {
return mWindow;
}
std::shared_ptr<Console> Context::GetConsole() {
return mConsole;
}
std::shared_ptr<Audio> Context::GetAudio() {
return mAudio;
}
std::shared_ptr<Fast::GfxDebugger> Context::GetGfxDebugger() {
return mGfxDebugger;
}
std::string Context::GetName() {
return mName;
}
std::string Context::GetShortName() {
return mShortName;
}
std::string Context::GetAppBundlePath() {
#if defined(__ANDROID__)
const char* externaldir = "/storage/emulated/0/SOH";//SDL_AndroidGetExternalStoragePath();
if (externaldir != NULL) {
return externaldir;
}
#endif
#ifdef __IOS__
const char* home = getenv("HOME");
return std::string(home) + "/Documents";
#endif
#ifdef NON_PORTABLE
return CMAKE_INSTALL_PREFIX;
#else
#ifdef __APPLE__
FolderManager folderManager;
return folderManager.getMainBundlePath();
#endif
#ifdef __linux__
std::string progpath(PATH_MAX, '\0');
int len = readlink("/proc/self/exe", &progpath[0], progpath.size() - 1);
if (len != -1) {
progpath.resize(len);
// Find the last '/' and remove everything after it
long unsigned int lastSlash = progpath.find_last_of("/");
if (lastSlash != std::string::npos) {
progpath.erase(lastSlash);
}
return progpath;
}
#endif
return ".";
#endif
}
std::string Context::GetAppDirectoryPath(std::string appName) {
#if defined(__ANDROID__)
const char* externaldir = "/storage/emulated/0/SOH";//SDL_AndroidGetExternalStoragePath();
if (externaldir != NULL) {
return externaldir;
}
#endif
#ifdef __IOS__
const char* home = getenv("HOME");
return std::string(home) + "/Documents";
#endif
#if defined(__APPLE__)
if (char* fpath = std::getenv("SHIP_HOME")) {
if (fpath[0] == '~') {
const char* home = getenv("HOME") ? getenv("HOME") : getpwuid(getuid())->pw_dir;
return std::string(home) + std::string(fpath).substr(1);
}
return std::string(fpath);
}
#endif
#if defined(__linux__)
char* fpath = std::getenv("SHIP_HOME");
if (fpath != NULL) {
return std::string(fpath);
}
#endif
#ifdef NON_PORTABLE
if (appName.empty()) {
appName = GetInstance()->mShortName;
}
char* prefpath = SDL_GetPrefPath(NULL, appName.c_str());
if (prefpath != NULL) {
std::string ret(prefpath);
SDL_free(prefpath);
return ret;
}
#endif
return ".";
}
std::string Context::GetPathRelativeToAppBundle(const std::string path) {
return GetAppBundlePath() + "/" + path;
}
std::string Context::GetPathRelativeToAppDirectory(const std::string path, std::string appName) {
return GetAppDirectoryPath(appName) + "/" + path;
}
std::string Context::LocateFileAcrossAppDirs(const std::string path, std::string appName) {
std::string fpath;
// app configuration dir
fpath = GetPathRelativeToAppDirectory(path, appName);
if (std::filesystem::exists(fpath)) {
return fpath;
}
// app install dir
fpath = GetPathRelativeToAppBundle(path);
if (std::filesystem::exists(fpath)) {
return fpath;
}
// current dir
return "./" + std::string(path);
}
} // namespace Ship

View File

@@ -0,0 +1,96 @@
#pragma once
#include <string>
#include <memory>
#include <unordered_set>
#include <vector>
#include <unordered_map>
#include <spdlog/spdlog.h>
#include "config/Config.h"
#include "resource/ResourceManager.h"
#include "controller/controldeck/ControlDeck.h"
#include "debug/CrashHandler.h"
#include "audio/Audio.h"
#include "window/Window.h"
#include "config/ConsoleVariable.h"
#include "debug/Console.h"
#include "graphic/Fast3D/debug/GfxDebugger.h"
namespace Ship {
class Context {
public:
static std::shared_ptr<Context> GetInstance();
static std::shared_ptr<Context> CreateInstance(const std::string name, const std::string shortName,
const std::string configFilePath,
const std::vector<std::string>& archivePaths = {},
const std::unordered_set<uint32_t>& validHashes = {},
uint32_t reservedThreadCount = 1, AudioSettings audioSettings = {},
std::shared_ptr<Window> window = nullptr,
std::shared_ptr<ControlDeck> controlDeck = nullptr);
static std::shared_ptr<Context> CreateUninitializedInstance(const std::string name, const std::string shortName,
const std::string configFilePath);
static std::string GetAppBundlePath();
static std::string GetAppDirectoryPath(std::string appName = "");
static std::string GetPathRelativeToAppDirectory(const std::string path, std::string appName = "");
static std::string GetPathRelativeToAppBundle(const std::string path);
static std::string LocateFileAcrossAppDirs(const std::string path, std::string appName = "");
Context(std::string name, std::string shortName, std::string configFilePath);
~Context();
bool Init(const std::vector<std::string>& archivePaths, const std::unordered_set<uint32_t>& validHashes,
uint32_t reservedThreadCount, AudioSettings audioSettings, std::shared_ptr<Window> window = nullptr,
std::shared_ptr<ControlDeck> controlDeck = nullptr);
std::shared_ptr<spdlog::logger> GetLogger();
std::shared_ptr<Config> GetConfig();
std::shared_ptr<ConsoleVariable> GetConsoleVariables();
std::shared_ptr<ResourceManager> GetResourceManager();
std::shared_ptr<ControlDeck> GetControlDeck();
std::shared_ptr<CrashHandler> GetCrashHandler();
std::shared_ptr<Window> GetWindow();
std::shared_ptr<Console> GetConsole();
std::shared_ptr<Audio> GetAudio();
std::shared_ptr<Fast::GfxDebugger> GetGfxDebugger();
std::string GetName();
std::string GetShortName();
bool InitLogging();
bool InitConfiguration();
bool InitConsoleVariables();
bool InitResourceManager(const std::vector<std::string>& archivePaths = {},
const std::unordered_set<uint32_t>& validHashes = {}, uint32_t reservedThreadCount = 1);
bool InitControlDeck(std::shared_ptr<ControlDeck> controlDeck = nullptr);
bool InitCrashHandler();
bool InitAudio(AudioSettings settings);
bool InitGfxDebugger();
bool InitConsole();
bool InitWindow(std::shared_ptr<Window> window = nullptr);
protected:
Context() = default;
private:
static std::weak_ptr<Context> mContext;
std::shared_ptr<spdlog::logger> mLogger;
std::shared_ptr<Config> mConfig;
std::shared_ptr<ConsoleVariable> mConsoleVariables;
std::shared_ptr<ResourceManager> mResourceManager;
std::shared_ptr<ControlDeck> mControlDeck;
std::shared_ptr<CrashHandler> mCrashHandler;
std::shared_ptr<Window> mWindow;
std::shared_ptr<Console> mConsole;
std::shared_ptr<Audio> mAudio;
std::shared_ptr<Fast::GfxDebugger> mGfxDebugger;
std::string mConfigFilePath;
std::string mMainPath;
std::string mPatchesPath;
std::string mName;
std::string mShortName;
};
} // namespace Ship

View File

@@ -0,0 +1,63 @@
#include "Audio.h"
#include "Context.h"
namespace Ship {
Audio::~Audio() {
SPDLOG_TRACE("destruct audio");
}
void Audio::InitAudioPlayer() {
switch (GetCurrentAudioBackend()) {
#ifdef _WIN32
case AudioBackend::WASAPI:
mAudioPlayer = std::make_shared<WasapiAudioPlayer>(this->mAudioSettings);
break;
#endif
default:
mAudioPlayer = std::make_shared<SDLAudioPlayer>(this->mAudioSettings);
}
if (mAudioPlayer) {
if (!mAudioPlayer->Init()) {
// Failed to initialize system audio player.
// Fallback to SDL if the native system player does not work.
SetCurrentAudioBackend(AudioBackend::SDL);
mAudioPlayer = std::make_shared<SDLAudioPlayer>(this->mAudioSettings);
mAudioPlayer->Init();
}
}
}
void Audio::Init() {
mAvailableAudioBackends = std::make_shared<std::vector<AudioBackend>>();
#ifdef _WIN32
mAvailableAudioBackends->push_back(AudioBackend::WASAPI);
#endif
mAvailableAudioBackends->push_back(AudioBackend::SDL);
SetCurrentAudioBackend(Context::GetInstance()->GetConfig()->GetCurrentAudioBackend());
}
std::shared_ptr<AudioPlayer> Audio::GetAudioPlayer() {
return mAudioPlayer;
}
AudioBackend Audio::GetCurrentAudioBackend() {
return mAudioBackend;
}
void Audio::SetCurrentAudioBackend(AudioBackend backend) {
mAudioBackend = backend;
Context::GetInstance()->GetConfig()->SetCurrentAudioBackend(GetCurrentAudioBackend());
Context::GetInstance()->GetConfig()->Save();
InitAudioPlayer();
}
std::shared_ptr<std::vector<AudioBackend>> Audio::GetAvailableAudioBackends() {
return mAvailableAudioBackends;
}
} // namespace Ship

View File

@@ -0,0 +1,32 @@
#pragma once
#include <string>
#include <memory>
#include <vector>
#include "audio/AudioPlayer.h"
namespace Ship {
enum class AudioBackend { WASAPI, SDL };
class Audio {
public:
Audio(AudioSettings settings) : mAudioSettings(settings) {
}
~Audio();
void Init();
std::shared_ptr<AudioPlayer> GetAudioPlayer();
AudioBackend GetCurrentAudioBackend();
std::shared_ptr<std::vector<AudioBackend>> GetAvailableAudioBackends();
void SetCurrentAudioBackend(AudioBackend backend);
protected:
void InitAudioPlayer();
private:
std::shared_ptr<AudioPlayer> mAudioPlayer;
AudioBackend mAudioBackend;
AudioSettings mAudioSettings;
std::shared_ptr<std::vector<AudioBackend>> mAvailableAudioBackends;
};
} // namespace Ship

View File

@@ -0,0 +1,49 @@
#include "AudioPlayer.h"
#include "spdlog/spdlog.h"
namespace Ship {
AudioPlayer::~AudioPlayer() {
SPDLOG_TRACE("destruct audio player");
}
bool AudioPlayer::Init() {
mInitialized = DoInit();
return IsInitialized();
}
bool AudioPlayer::IsInitialized() {
return mInitialized;
}
int32_t AudioPlayer::GetSampleRate() const {
return mAudioSettings.SampleRate;
}
int32_t AudioPlayer::GetSampleLength() const {
return mAudioSettings.SampleLength;
}
int32_t AudioPlayer::GetDesiredBuffered() const {
return mAudioSettings.DesiredBuffered;
}
AudioChannelsSetting AudioPlayer::GetAudioChannels() const {
return mAudioSettings.AudioSurround;
}
void AudioPlayer::SetSampleRate(int32_t rate) {
mAudioSettings.SampleRate = rate;
}
void AudioPlayer::SetSampleLength(int32_t length) {
mAudioSettings.SampleLength = length;
}
void AudioPlayer::SetDesiredBuffered(int32_t size) {
mAudioSettings.DesiredBuffered = size;
}
void AudioPlayer::SetAudioChannels(AudioChannelsSetting surround) {
mAudioSettings.AudioSurround = surround;
}
} // namespace Ship

View File

@@ -0,0 +1,58 @@
#pragma once
#include "stdint.h"
#include "stddef.h"
#include <string>
#include "public/bridge/audiobridge.h"
namespace Ship {
struct AudioSettings {
int32_t SampleRate = 44100;
int32_t SampleLength = 1024;
int32_t DesiredBuffered = 2480;
AudioChannelsSetting AudioSurround = AudioChannelsSetting::audioStereo;
};
class AudioPlayer {
public:
AudioPlayer(AudioSettings settings) : mAudioSettings(settings) {
}
~AudioPlayer();
bool Init();
virtual int32_t Buffered() = 0;
virtual void Play(const uint8_t* buf, size_t len) = 0;
bool IsInitialized();
int32_t GetSampleRate() const;
int32_t GetSampleLength() const;
int32_t GetDesiredBuffered() const;
AudioChannelsSetting GetAudioChannels() const;
void SetSampleRate(int32_t rate);
void SetSampleLength(int32_t length);
void SetDesiredBuffered(int32_t size);
void SetAudioChannels(AudioChannelsSetting surround);
protected:
virtual bool DoInit() = 0;
private:
bool mInitialized = false;
AudioSettings mAudioSettings;
};
} // namespace Ship
#ifdef _WIN32
#include "WasapiAudioPlayer.h"
#endif
#include "SDLAudioPlayer.h"

View File

@@ -0,0 +1,43 @@
#include "SDLAudioPlayer.h"
#include <spdlog/spdlog.h>
namespace Ship {
SDLAudioPlayer::~SDLAudioPlayer() {
SPDLOG_TRACE("destruct SDL audio player");
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
bool SDLAudioPlayer::DoInit() {
if (SDL_Init(SDL_INIT_AUDIO) != 0) {
SPDLOG_ERROR("SDL init error: %s\n", SDL_GetError());
return false;
}
mNumChannels = this->GetAudioChannels() == AudioChannelsSetting::audioSurround51 ? 6 : 2;
SDL_AudioSpec want, have;
SDL_zero(want);
want.freq = this->GetSampleRate();
want.format = AUDIO_S16SYS;
want.channels = mNumChannels;
want.samples = this->GetSampleLength();
want.callback = NULL;
mDevice = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
if (mDevice == 0) {
SPDLOG_ERROR("SDL_OpenAudio error: {}", SDL_GetError());
return false;
}
SDL_PauseAudioDevice(mDevice, 0);
return true;
}
int SDLAudioPlayer::Buffered() {
return SDL_GetQueuedAudioSize(mDevice) / (sizeof(int16_t) * mNumChannels);
}
void SDLAudioPlayer::Play(const uint8_t* buf, size_t len) {
if (Buffered() < 6000) {
// Don't fill the audio buffer too much in case this happens
SDL_QueueAudio(mDevice, buf, len);
}
}
} // namespace Ship

View File

@@ -0,0 +1,22 @@
#pragma once
#include "AudioPlayer.h"
#include <SDL2/SDL.h>
namespace Ship {
class SDLAudioPlayer : public AudioPlayer {
public:
SDLAudioPlayer(AudioSettings settings) : AudioPlayer(settings) {
}
~SDLAudioPlayer();
int Buffered();
void Play(const uint8_t* buf, size_t len);
protected:
bool DoInit();
private:
SDL_AudioDeviceID mDevice;
int32_t mNumChannels = 2;
};
} // namespace Ship

View File

@@ -0,0 +1,183 @@
#ifdef _WIN32
#include "WasapiAudioPlayer.h"
#include <spdlog/spdlog.h>
// These constants are currently missing from the MinGW headers.
#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
#endif
#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
#endif
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient);
namespace Ship {
void WasapiAudioPlayer::ThrowIfFailed(HRESULT res) {
if (FAILED(res)) {
throw res;
}
}
bool WasapiAudioPlayer::SetupStream() {
try {
ThrowIfFailed(mDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &mDevice));
ThrowIfFailed(mDevice->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, IID_PPV_ARGS_Helper(&mClient)));
auto audioSurround = this->GetAudioChannels();
if (audioSurround == AudioChannelsSetting::audioStereo) {
mNumChannels = 2;
WAVEFORMATEX desired;
desired.wFormatTag = WAVE_FORMAT_PCM;
desired.nChannels = mNumChannels; // Stereo audio
desired.wBitsPerSample = 16; // 16-bit audio
desired.nSamplesPerSec = this->GetSampleRate();
desired.nBlockAlign = desired.nChannels * desired.wBitsPerSample / 8;
desired.nAvgBytesPerSec = desired.nSamplesPerSec * desired.nBlockAlign; // 2 bytes per sample (16-bit audio)
desired.cbSize = 0;
ThrowIfFailed(mClient->Initialize(
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
2000000, 0, &desired, nullptr));
} else if (audioSurround == AudioChannelsSetting::audioSurround51) {
mNumChannels = 6;
WAVEFORMATEXTENSIBLE desired;
desired.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
desired.Format.nChannels = mNumChannels; // 6 channels for 5.1 audio
desired.Format.wBitsPerSample = 16; // 16-bit audio
desired.Format.nSamplesPerSec = this->GetSampleRate();
desired.Format.nBlockAlign = desired.Format.nChannels * desired.Format.wBitsPerSample / 8;
desired.Format.nAvgBytesPerSec =
desired.Format.nSamplesPerSec * desired.Format.nBlockAlign; // 2 bytes per sample (16-bit audio)
desired.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
desired.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
desired.Samples.wValidBitsPerSample = 16;
desired.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
ThrowIfFailed(mClient->Initialize(
AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
2000000, 0, (WAVEFORMATEX*)&desired, nullptr));
}
ThrowIfFailed(mClient->GetBufferSize(&mBufferFrameCount));
ThrowIfFailed(mClient->GetService(IID_PPV_ARGS(&mRenderClient)));
mStarted = false;
mInitialized = true;
} catch (HRESULT res) { return false; }
return true;
}
bool WasapiAudioPlayer::DoInit() {
try {
ThrowIfFailed(
CoCreateInstance(CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&mDeviceEnumerator)));
} catch (HRESULT res) { return false; }
ThrowIfFailed(mDeviceEnumerator->RegisterEndpointNotificationCallback(this));
return true;
}
int WasapiAudioPlayer::Buffered() {
if (!mInitialized) {
if (!SetupStream()) {
return 0;
}
}
try {
UINT32 padding;
ThrowIfFailed(mClient->GetCurrentPadding(&padding));
return padding;
} catch (HRESULT res) { return 0; }
}
void WasapiAudioPlayer::Play(const uint8_t* buf, size_t len) {
if (!mInitialized) {
if (!SetupStream()) {
return;
}
}
try {
UINT32 frames = len / (mNumChannels * sizeof(int16_t));
UINT32 padding;
ThrowIfFailed(mClient->GetCurrentPadding(&padding));
UINT32 available = mBufferFrameCount - padding;
if (available < frames) {
frames = available;
}
if (available == 0) {
return;
}
BYTE* data;
ThrowIfFailed(mRenderClient->GetBuffer(frames, &data));
memcpy(data, buf, frames * mNumChannels * sizeof(int16_t));
ThrowIfFailed(mRenderClient->ReleaseBuffer(frames, 0));
if (!mStarted && padding + frames > 1500) {
mStarted = true;
ThrowIfFailed(mClient->Start());
}
} catch (HRESULT res) {}
}
HRESULT STDMETHODCALLTYPE WasapiAudioPlayer::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) {
return S_OK;
}
HRESULT STDMETHODCALLTYPE WasapiAudioPlayer::OnDeviceAdded(LPCWSTR pwstrDeviceId) {
return S_OK;
}
HRESULT STDMETHODCALLTYPE WasapiAudioPlayer::OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
return S_OK;
}
HRESULT STDMETHODCALLTYPE WasapiAudioPlayer::OnDefaultDeviceChanged(EDataFlow flow, ERole role,
LPCWSTR pwstrDefaultDeviceId) {
if (flow == eRender && role == eConsole) {
// This callback runs on a separate thread,
// but it's not important how fast this write takes effect.
mInitialized = false;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE WasapiAudioPlayer::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) {
return S_OK;
}
ULONG STDMETHODCALLTYPE WasapiAudioPlayer::AddRef() {
return InterlockedIncrement(&mRefCount);
}
ULONG STDMETHODCALLTYPE WasapiAudioPlayer::Release() {
ULONG rc = InterlockedDecrement(&mRefCount);
if (rc == 0) {
delete this;
}
return rc;
}
HRESULT STDMETHODCALLTYPE WasapiAudioPlayer::QueryInterface(REFIID riid, VOID** ppvInterface) {
if (riid == __uuidof(IUnknown)) {
AddRef();
*ppvInterface = (IUnknown*)this;
} else if (riid == __uuidof(IMMNotificationClient)) {
AddRef();
*ppvInterface = (IMMNotificationClient*)this;
} else {
*ppvInterface = nullptr;
return E_NOINTERFACE;
}
return S_OK;
}
} // namespace Ship
#endif

View File

@@ -0,0 +1,46 @@
#pragma once
#ifdef _WIN32
#include "AudioPlayer.h"
#include <wrl/client.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
using namespace Microsoft::WRL;
namespace Ship {
class WasapiAudioPlayer : public AudioPlayer, public IMMNotificationClient {
public:
WasapiAudioPlayer(AudioSettings settings) : AudioPlayer(settings) {
}
int Buffered();
void Play(const uint8_t* buf, size_t len);
protected:
virtual HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState);
virtual HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId);
virtual HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId);
virtual HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId);
virtual HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvInterface);
void ThrowIfFailed(HRESULT res);
bool SetupStream();
bool DoInit();
private:
ComPtr<IMMDeviceEnumerator> mDeviceEnumerator;
ComPtr<IMMDevice> mDevice;
ComPtr<IAudioClient> mClient;
ComPtr<IAudioRenderClient> mRenderClient;
LONG mRefCount = 1;
UINT32 mBufferFrameCount = 0;
bool mInitialized = false;
bool mStarted = false;
int32_t mNumChannels = 2;
};
} // namespace Ship
#endif

View File

@@ -0,0 +1,317 @@
#include "Config.h"
#include <fstream>
#include <string>
#include <filesystem>
#include <unordered_map>
#include <any>
#include "utils/StringHelper.h"
#include "Context.h"
#ifdef __APPLE__
#include "graphic/Fast3D/gfx_metal.h"
#endif
namespace fs = std::filesystem;
namespace Ship {
Config::Config(std::string path) : mPath(std::move(path)), mIsNewInstance(false) {
Reload();
}
Config::~Config() {
SPDLOG_TRACE("destruct config");
}
std::string Config::FormatNestedKey(const std::string& key) {
std::vector<std::string> dots = StringHelper::Split(key, ".");
std::string tmp;
if (dots.size() > 1) {
for (const auto& dot : dots) {
tmp += "/" + dot;
}
} else {
tmp = "/" + dots[0];
}
return tmp;
}
nlohmann::json Config::Nested(const std::string& key) {
std::vector<std::string> dots = StringHelper::Split(key, ".");
if (!mFlattenedJson.is_object()) {
return mFlattenedJson;
}
nlohmann::json gjson = mFlattenedJson.unflatten();
if (dots.size() > 1) {
for (auto& dot : dots) {
if (dot == "*" || gjson.contains(dot)) {
gjson = gjson[dot];
}
}
return gjson;
}
return gjson[key];
}
std::string Config::GetString(const std::string& key, const std::string& defaultValue) {
nlohmann::json n = Nested(key);
if (n.is_string() && !n.get<std::string>().empty()) {
return n;
}
return defaultValue;
}
float Config::GetFloat(const std::string& key, float defaultValue) {
nlohmann::json n = Nested(key);
if (n.is_number_float()) {
return n;
}
return defaultValue;
}
bool Config::GetBool(const std::string& key, bool defaultValue) {
nlohmann::json n = Nested(key);
if (n.is_boolean()) {
return n;
}
return defaultValue;
}
int32_t Config::GetInt(const std::string& key, int32_t defaultValue) {
nlohmann::json n = Nested(key);
if (n.is_number_integer()) {
return n;
}
return defaultValue;
}
uint32_t Config::GetUInt(const std::string& key, uint32_t defaultValue) {
nlohmann::json n = Nested(key);
if (n.is_number_unsigned()) {
return n;
}
return defaultValue;
}
bool Config::Contains(const std::string& key) {
return !Nested(key).is_null();
}
void Config::SetString(const std::string& key, const std::string& value) {
mFlattenedJson[FormatNestedKey(key)] = value;
}
void Config::SetFloat(const std::string& key, float value) {
mFlattenedJson[FormatNestedKey(key)] = value;
}
void Config::SetBool(const std::string& key, bool value) {
mFlattenedJson[FormatNestedKey(key)] = value;
}
void Config::SetInt(const std::string& key, int32_t value) {
mFlattenedJson[FormatNestedKey(key)] = value;
}
void Config::SetUInt(const std::string& key, uint32_t value) {
mFlattenedJson[FormatNestedKey(key)] = value;
}
void Config::Erase(const std::string& key) {
mFlattenedJson.erase(FormatNestedKey(key));
}
void Config::EraseBlock(const std::string& key) {
nlohmann::json gjson = mFlattenedJson.unflatten();
if (key.find(".") != std::string::npos) {
nlohmann::json* gjson2 = &gjson;
std::vector<std::string> dots = StringHelper::Split(key, ".");
if (dots.size() > 1) {
size_t curDot = 0;
for (auto& dot : dots) {
if (gjson2->contains(dot)) {
if (curDot == dots.size() - 1) {
gjson2->at(dot).clear();
gjson2->erase(dot);
} else {
gjson2 = &gjson2->at(dot);
curDot++;
}
}
}
}
} else {
if (gjson.contains(key)) {
gjson.erase(key);
}
}
mFlattenedJson = gjson.flatten();
Save();
}
void Config::Copy(const std::string& fromKey, const std::string& toKey) {
auto nestedFromKey = FormatNestedKey(fromKey);
auto nestedToKey = FormatNestedKey(toKey);
if (mFlattenedJson.contains(nestedFromKey)) {
mFlattenedJson[nestedToKey] = mFlattenedJson[nestedFromKey];
}
}
void Config::Reload() {
if (mPath == "None" || !fs::exists(mPath) || !fs::is_regular_file(mPath)) {
mIsNewInstance = true;
mFlattenedJson = nlohmann::json::object();
return;
}
std::ifstream ifs(mPath);
try {
mNestedJson = nlohmann::json::parse(ifs);
mFlattenedJson = mNestedJson.flatten();
} catch (...) { mFlattenedJson = nlohmann::json::object(); }
}
void Config::Save() {
std::ofstream file(mPath);
file << mFlattenedJson.unflatten().dump(4);
}
template <typename T> std::vector<T> Config::GetArray(const std::string& key) {
if (nlohmann::json tmp = Nested(key); tmp.is_array()) {
return tmp.get<std::vector<T>>();
}
return std::vector<T>();
};
template <typename T> void Config::SetArray(const std::string& key, std::vector<T> array) {
mFlattenedJson[FormatNestedKey(key)] = nlohmann::json(array);
}
nlohmann::json Config::GetNestedJson() {
return mNestedJson;
}
AudioBackend Config::GetCurrentAudioBackend() {
std::string backendName = GetString("Window.AudioBackend");
if (backendName == "wasapi") {
return AudioBackend::WASAPI;
}
// Migrate pulse player in config to sdl
if (backendName == "pulse") {
SetCurrentAudioBackend(AudioBackend::SDL);
return AudioBackend::SDL;
}
if (backendName == "sdl") {
return AudioBackend::SDL;
}
SPDLOG_TRACE("Could not find AudioBackend matching value from config file ({}). Returning default AudioBackend.",
backendName);
#ifdef _WIN32
return AudioBackend::WASAPI;
#endif
return AudioBackend::SDL;
}
AudioChannelsSetting Config::GetCurrentAudioChannelsSetting() {
int32_t surround =
GetInt("CVars." CVAR_AUDIO_CHANNELS_SETTING, static_cast<int32_t>(AudioChannelsSetting::audioMax));
switch (surround) {
case AudioChannelsSetting::audioSurround51:
return AudioChannelsSetting::audioSurround51;
case AudioChannelsSetting::audioStereo:
case AudioChannelsSetting::audioMax:
default:
return AudioChannelsSetting::audioStereo;
}
}
void Config::SetCurrentAudioBackend(AudioBackend backend) {
switch (backend) {
case AudioBackend::WASAPI:
SetString("Window.AudioBackend", "wasapi");
break;
case AudioBackend::SDL:
SetString("Window.AudioBackend", "sdl");
break;
default:
SetString("Window.AudioBackend", "");
}
}
WindowBackend Config::GetWindowBackend() {
WindowBackend backend;
auto backendId = GetInt("Window.Backend.Id", -1);
if (Context::GetInstance()->GetWindow()->IsAvailableWindowBackend(backendId)) {
return static_cast<WindowBackend>(backendId);
}
SPDLOG_TRACE(
"Could not find available WindowBackend matching id from config file ({}). Returning default WindowBackend.",
backendId);
#ifdef ENABLE_DX11
return WindowBackend::FAST3D_DXGI_DX11;
#endif
#ifdef __APPLE__
if (Metal_IsSupported()) {
return WindowBackend::FAST3D_SDL_METAL;
}
#endif
return WindowBackend::FAST3D_SDL_OPENGL;
}
void Config::SetWindowBackend(WindowBackend backend) {
SetInt("Window.Backend.Id", static_cast<int>(backend));
switch (backend) {
case WindowBackend::FAST3D_DXGI_DX11:
SetString("Window.Backend.Name", "DirectX 11");
break;
case WindowBackend::FAST3D_SDL_OPENGL:
SetString("Window.Backend.Name", "OpenGL");
break;
case WindowBackend::FAST3D_SDL_METAL:
SetString("Window.Backend.Name", "Metal");
break;
default:
SetString("Window.Backend.Name", "");
}
}
bool Config::RegisterVersionUpdater(std::shared_ptr<ConfigVersionUpdater> versionUpdater) {
auto [_, emplaced] = mVersionUpdaters.emplace(versionUpdater->GetVersion(), versionUpdater);
return emplaced;
}
void Config::RunVersionUpdates() {
for (auto [_, versionUpdater] : mVersionUpdaters) {
uint32_t version = GetUInt("ConfigVersion", 0);
if (version < versionUpdater->GetVersion()) {
versionUpdater->Update(this);
SetUInt("ConfigVersion", versionUpdater->GetVersion());
}
}
Save();
}
ConfigVersionUpdater::ConfigVersionUpdater(uint32_t toVersion) : mVersion(toVersion) {
}
uint32_t ConfigVersionUpdater::GetVersion() {
return mVersion;
}
} // namespace Ship

View File

@@ -0,0 +1,102 @@
#pragma once
#include <vector>
#include <string>
#include <nlohmann/json.hpp>
#include "audio/Audio.h"
#include "window/Window.h"
namespace Ship {
/**
* @brief Abstract class representing a Config Version Updater, intended to express how to
* upgrade a Configuration file from one version of a config to another (i.e. removing
* default values, changing option names, etc.) It can be used by subclassing `ConfigVersionUpdater`,
* implementing the Update function, and implementing the Constructor passing the version that the
* Config is being updated to to this class' constructor from the child class' default constructor.
* For example: \code ConfigVersion1Updater() : ConfigVersionUpdater(1) {} \endcode
* Finally, give an instance of this subclass to a Config object via
* RegisterConfigVersionUpdater and call RunVersionUpdates.
*/
class ConfigVersionUpdater {
protected:
uint32_t mVersion;
public:
ConfigVersionUpdater(uint32_t toVersion);
/**
* @brief Performs actions on a Config object via the provided pointer to update it
* to the next version. (i.e. removing/changing default values or renaming options)
*
* @param conf
*/
virtual void Update(Config* conf) = 0;
/**
* @brief Get the value of mVersion
*
* @return uint32_t
*/
uint32_t GetVersion();
};
class Config {
public:
Config(std::string path);
~Config();
std::string GetString(const std::string& key, const std::string& defaultValue = "");
float GetFloat(const std::string& key, float defaultValue = 0.0f);
bool GetBool(const std::string& key, bool defaultValue = false);
int32_t GetInt(const std::string& key, int32_t defaultValue = 0);
uint32_t GetUInt(const std::string& key, uint32_t defaultValue = 0);
void SetString(const std::string& key, const std::string& value);
void SetFloat(const std::string& key, float value);
void SetBool(const std::string& key, bool value);
void SetInt(const std::string& key, int32_t value);
void SetUInt(const std::string& key, uint32_t value);
void Erase(const std::string& key);
void EraseBlock(const std::string& key);
void Copy(const std::string& fromKey, const std::string& toKey);
bool Contains(const std::string& key);
void Reload();
void Save();
nlohmann::json GetNestedJson();
AudioBackend GetCurrentAudioBackend();
void SetCurrentAudioBackend(AudioBackend backend);
WindowBackend GetWindowBackend();
void SetWindowBackend(WindowBackend backend);
AudioChannelsSetting GetCurrentAudioChannelsSetting();
/**
* @brief Adds a ConfigVersionUpdater instance to the list to be run later via RunVersionUpdates
*
* @param versionUpdater
* @return true if the insert was successful, or
* @return false if the insert failed, i.e. if the list already has a ConfigVersionUpdater with
* a matching version.
*/
bool RegisterVersionUpdater(std::shared_ptr<ConfigVersionUpdater> versionUpdater);
/**
* @brief Runs the Update function on each ConfigVersionUpdater instance if the version matches\
* the current ConfigVersion value of the config object.
*
*/
void RunVersionUpdates();
protected:
nlohmann::json Nested(const std::string& key);
static std::string FormatNestedKey(const std::string& key);
template <typename T> void SetArray(const std::string& key, std::vector<T> array);
template <typename T> std::vector<T> GetArray(const std::string& key);
private:
nlohmann::json mFlattenedJson;
nlohmann::json mNestedJson;
std::string mPath;
bool mIsNewInstance;
std::map<uint32_t, std::shared_ptr<ConfigVersionUpdater>> mVersionUpdaters;
};
} // namespace Ship

View File

@@ -0,0 +1,374 @@
#include "ConsoleVariable.h"
#include <functional>
#include "utils/filesystemtools/DiskFile.h"
#include <utils/Utils.h>
#include "config/Config.h"
#include "Context.h"
namespace Ship {
ConsoleVariable::ConsoleVariable() {
Load();
}
ConsoleVariable::~ConsoleVariable() {
SPDLOG_TRACE("destruct console variables");
}
std::shared_ptr<CVar> ConsoleVariable::Get(const char* name) {
auto it = mVariables.find(name);
return it != mVariables.end() ? it->second : nullptr;
}
int32_t ConsoleVariable::GetInteger(const char* name, int32_t defaultValue) {
auto variable = Get(name);
if (variable != nullptr && variable->Type == ConsoleVariableType::Integer) {
return variable->Integer;
}
return defaultValue;
}
float ConsoleVariable::GetFloat(const char* name, float defaultValue) {
auto variable = Get(name);
if (variable != nullptr && variable->Type == ConsoleVariableType::Float) {
return variable->Float;
}
return defaultValue;
}
const char* ConsoleVariable::GetString(const char* name, const char* defaultValue) {
auto variable = Get(name);
if (variable != nullptr && variable->Type == ConsoleVariableType::String) {
return variable->String.c_str();
}
return defaultValue;
}
Color_RGBA8 ConsoleVariable::GetColor(const char* name, Color_RGBA8 defaultValue) {
auto variable = Get(name);
if (variable != nullptr && variable->Type == ConsoleVariableType::Color) {
return variable->Color;
} else if (variable != nullptr && variable->Type == ConsoleVariableType::Color24) {
Color_RGBA8 temp;
temp.r = variable->Color24.r;
temp.g = variable->Color24.g;
temp.b = variable->Color24.b;
temp.a = 255;
return temp;
}
return defaultValue;
}
Color_RGB8 ConsoleVariable::GetColor24(const char* name, Color_RGB8 defaultValue) {
auto variable = Get(name);
if (variable != nullptr && variable->Type == ConsoleVariableType::Color24) {
return variable->Color24;
} else if (variable != nullptr && variable->Type == ConsoleVariableType::Color) {
Color_RGB8 temp;
temp.r = variable->Color.r;
temp.g = variable->Color.g;
temp.b = variable->Color.b;
return temp;
}
return defaultValue;
}
void ConsoleVariable::SetInteger(const char* name, int32_t value) {
auto& variable = mVariables[name];
if (variable == nullptr) {
variable = std::make_shared<CVar>();
}
variable->Type = ConsoleVariableType::Integer;
variable->Integer = value;
}
void ConsoleVariable::SetFloat(const char* name, float value) {
auto& variable = mVariables[name];
if (variable == nullptr) {
variable = std::make_shared<CVar>();
}
variable->Type = ConsoleVariableType::Float;
variable->Float = value;
}
void ConsoleVariable::SetString(const char* name, const char* value) {
auto& variable = mVariables[name];
if (variable == nullptr) {
variable = std::make_shared<CVar>();
}
variable->Type = ConsoleVariableType::String;
variable->String = std::string(value);
}
void ConsoleVariable::SetColor(const char* name, Color_RGBA8 value) {
auto& variable = mVariables[name];
if (!variable) {
variable = std::make_shared<CVar>();
}
variable->Type = ConsoleVariableType::Color;
variable->Color = value;
}
void ConsoleVariable::SetColor24(const char* name, Color_RGB8 value) {
auto& variable = mVariables[name];
if (!variable) {
variable = std::make_shared<CVar>();
}
variable->Type = ConsoleVariableType::Color24;
variable->Color24 = value;
}
void ConsoleVariable::RegisterInteger(const char* name, int32_t defaultValue) {
if (Get(name) == nullptr) {
SetInteger(name, defaultValue);
}
}
void ConsoleVariable::RegisterFloat(const char* name, float defaultValue) {
if (Get(name) == nullptr) {
SetFloat(name, defaultValue);
}
}
void ConsoleVariable::RegisterString(const char* name, const char* defaultValue) {
if (Get(name) == nullptr) {
SetString(name, defaultValue);
}
}
void ConsoleVariable::RegisterColor(const char* name, Color_RGBA8 defaultValue) {
if (Get(name) == nullptr) {
SetColor(name, defaultValue);
}
}
void ConsoleVariable::RegisterColor24(const char* name, Color_RGB8 defaultValue) {
if (Get(name) == nullptr) {
SetColor24(name, defaultValue);
}
}
void ConsoleVariable::ClearVariable(const char* name) {
std::shared_ptr<Config> conf = Context::GetInstance()->GetConfig();
auto var = Get(name);
if (var != nullptr) {
bool color = var->Type == ConsoleVariableType::Color || var->Type == ConsoleVariableType::Color24;
if (color) {
std::string a = StringHelper::Sprintf("%s.%s", name, "A");
std::string b = StringHelper::Sprintf("%s.%s", name, "B");
std::string g = StringHelper::Sprintf("%s.%s", name, "G");
std::string r = StringHelper::Sprintf("%s.%s", name, "R");
std::string t = StringHelper::Sprintf("%s.%s", name, "Type");
mVariables.erase(a);
mVariables.erase(b);
mVariables.erase(g);
mVariables.erase(r);
mVariables.erase(t);
conf->Erase(std::string("CVars.") + a);
conf->Erase(std::string("CVars.") + b);
conf->Erase(std::string("CVars.") + g);
conf->Erase(std::string("CVars.") + r);
conf->Erase(std::string("CVars.") + t);
}
}
mVariables.erase(name);
conf->Erase(StringHelper::Sprintf("CVars.%s", name));
}
void ConsoleVariable::ClearBlock(const char* name) {
std::shared_ptr<Config> conf = Context::GetInstance()->GetConfig();
conf->EraseBlock(StringHelper::Sprintf("CVars.%s", name));
Load();
}
void ConsoleVariable::CopyVariable(const char* from, const char* to) {
auto& variableFrom = mVariables[from];
if (!variableFrom) {
return;
}
auto& variableTo = mVariables[to];
if (!variableTo) {
variableTo = std::make_shared<CVar>();
}
variableTo->Type = variableFrom->Type;
switch (variableTo->Type) {
case ConsoleVariableType::Integer:
variableTo->Integer = variableFrom->Integer;
break;
case ConsoleVariableType::Float:
variableTo->Float = variableFrom->Float;
break;
case ConsoleVariableType::String:
variableTo->String = variableFrom->String;
break;
case ConsoleVariableType::Color:
variableTo->Color = variableFrom->Color;
break;
case ConsoleVariableType::Color24:
variableTo->Color24 = variableFrom->Color24;
break;
}
}
void ConsoleVariable::Save() {
std::shared_ptr<Config> conf = Context::GetInstance()->GetConfig();
for (const auto& variable : mVariables) {
const std::string key = StringHelper::Sprintf("CVars.%s", variable.first.c_str());
if (variable.second->Type == ConsoleVariableType::String && variable.second != nullptr) {
conf->SetString(key, std::string(variable.second->String));
} else if (variable.second->Type == ConsoleVariableType::Integer) {
conf->SetInt(key, variable.second->Integer);
} else if (variable.second->Type == ConsoleVariableType::Float) {
conf->SetFloat(key, variable.second->Float);
} else if (variable.second->Type == ConsoleVariableType::Color ||
variable.second->Type == ConsoleVariableType::Color24) {
auto keyStr = key.c_str();
conf->SetUInt(StringHelper::Sprintf("%s.R", keyStr), variable.second->Type == ConsoleVariableType::Color
? variable.second->Color.r
: variable.second->Color24.r);
conf->SetUInt(StringHelper::Sprintf("%s.G", keyStr), variable.second->Type == ConsoleVariableType::Color
? variable.second->Color.g
: variable.second->Color24.g);
conf->SetUInt(StringHelper::Sprintf("%s.B", keyStr), variable.second->Type == ConsoleVariableType::Color
? variable.second->Color.b
: variable.second->Color24.b);
if (variable.second->Type == ConsoleVariableType::Color) {
conf->SetUInt(StringHelper::Sprintf("%s.A", keyStr), variable.second->Color.a);
conf->SetString(StringHelper::Sprintf("%s.Type", keyStr), "RGBA");
} else {
conf->SetString(StringHelper::Sprintf("%s.Type", keyStr), "RGB");
}
}
}
conf->Save();
}
void ConsoleVariable::Load() {
std::shared_ptr<Config> conf = Context::GetInstance()->GetConfig();
conf->Reload();
if (!mVariables.empty()) {
mVariables.clear();
}
LoadFromPath("", conf->GetNestedJson()["CVars"].items());
LoadLegacy();
}
void ConsoleVariable::LoadFromPath(
std::string path, nlohmann::detail::iteration_proxy<nlohmann::detail::iter_impl<nlohmann::json>> items) {
if (!path.empty()) {
path += ".";
}
for (const auto& item : items) {
std::string itemPath = path + item.key();
auto value = item.value();
switch (value.type()) {
case nlohmann::detail::value_t::array:
break;
case nlohmann::detail::value_t::object:
if (value.contains("Type") && value["Type"].get<std::string>() == "RGBA") {
Color_RGBA8 clr;
clr.r = value["R"].get<uint8_t>();
clr.g = value["G"].get<uint8_t>();
clr.b = value["B"].get<uint8_t>();
clr.a = value["A"].get<uint8_t>();
SetColor(itemPath.c_str(), clr);
} else if (value.contains("Type") && value["Type"].get<std::string>() == "RGB") {
Color_RGB8 clr;
clr.r = value["R"].get<uint8_t>();
clr.g = value["G"].get<uint8_t>();
clr.b = value["B"].get<uint8_t>();
SetColor24(itemPath.c_str(), clr);
} else {
LoadFromPath(itemPath, value.items());
}
break;
case nlohmann::detail::value_t::string:
SetString(itemPath.c_str(), value.get<std::string>().c_str());
break;
case nlohmann::detail::value_t::boolean:
SetInteger(itemPath.c_str(), value.get<bool>());
break;
case nlohmann::detail::value_t::number_unsigned:
case nlohmann::detail::value_t::number_integer:
SetInteger(itemPath.c_str(), value.get<int>());
break;
case nlohmann::detail::value_t::number_float:
SetFloat(itemPath.c_str(), value.get<float>());
break;
default:;
}
}
}
void ConsoleVariable::LoadLegacy() {
auto conf = Context::GetPathRelativeToAppDirectory("cvars.cfg");
if (DiskFile::Exists(conf)) {
const auto lines = DiskFile::ReadAllLines(conf);
for (const std::string& line : lines) {
std::vector<std::string> cfg = StringHelper::Split(line, " = ");
if (line.empty()) {
continue;
}
if (cfg.size() < 2) {
continue;
}
if (cfg[1].find("\"") == std::string::npos && (cfg[1].find("#") != std::string::npos)) {
std::string value(cfg[1]);
value.erase(std::remove_if(value.begin(), value.end(), [](char c) { return c == '#'; }), value.end());
auto splitTest = StringHelper::Split(value, "\r")[0];
uint32_t val = std::stoul(splitTest, nullptr, 16);
Color_RGBA8 clr;
clr.r = val >> 24;
clr.g = val >> 16;
clr.b = val >> 8;
clr.a = val & 0xFF;
SetColor(cfg[0].c_str(), clr);
}
if (cfg[1].find("\"") != std::string::npos) {
std::string value(cfg[1]);
value.erase(std::remove(value.begin(), value.end(), '\"'), value.end());
#ifdef _MSC_VER
SetString(cfg[0].c_str(), _strdup(value.c_str()));
#else
SetString(cfg[0].c_str(), strdup(value.c_str()));
#endif
}
if (Math::IsNumber<float>(cfg[1])) {
SetFloat(cfg[0].c_str(), std::stof(cfg[1]));
}
if (Math::IsNumber<int>(cfg[1])) {
SetInteger(cfg[0].c_str(), std::stoi(cfg[1]));
}
}
fs::remove(conf);
}
}
} // namespace Ship

View File

@@ -0,0 +1,63 @@
#pragma once
#include "libultraship/color.h"
#include <nlohmann/json.hpp>
#include <stdint.h>
#include <memory>
#include <unordered_map>
#include <string>
namespace Ship {
typedef enum class ConsoleVariableType { Integer, Float, String, Color, Color24 } ConsoleVariableType;
typedef struct CVar {
const char* Name;
ConsoleVariableType Type;
int32_t Integer;
float Float;
std::string String;
Color_RGBA8 Color;
Color_RGB8 Color24;
} CVar;
class ConsoleVariable {
public:
ConsoleVariable();
~ConsoleVariable();
std::shared_ptr<CVar> Get(const char* name);
int32_t GetInteger(const char* name, int32_t defaultValue);
float GetFloat(const char* name, float defaultValue);
const char* GetString(const char* name, const char* defaultValue);
Color_RGBA8 GetColor(const char* name, Color_RGBA8 defaultValue);
Color_RGB8 GetColor24(const char* name, Color_RGB8 defaultValue);
void SetInteger(const char* name, int32_t value);
void SetFloat(const char* name, float value);
void SetString(const char* name, const char* value);
void SetColor(const char* name, Color_RGBA8 value);
void SetColor24(const char* name, Color_RGB8 value);
void RegisterInteger(const char* name, int32_t defaultValue);
void RegisterFloat(const char* name, float defaultValue);
void RegisterString(const char* name, const char* defaultValue);
void RegisterColor(const char* name, Color_RGBA8 defaultValue);
void RegisterColor24(const char* name, Color_RGB8 defaultValue);
void ClearVariable(const char* name);
void ClearBlock(const char* name);
void CopyVariable(const char* from, const char* to);
void Save();
void Load();
protected:
void LoadFromPath(std::string path,
nlohmann::detail::iteration_proxy<nlohmann::detail::iter_impl<nlohmann::json>> items);
void LoadLegacy();
private:
std::unordered_map<std::string, std::shared_ptr<CVar>> mVariables;
};
} // namespace Ship

View File

@@ -0,0 +1,164 @@
#include "ControlDeck.h"
#include "Context.h"
#include "controller/controldevice/controller/Controller.h"
#include "utils/StringHelper.h"
#include "public/bridge/consolevariablebridge.h"
#include <imgui.h>
#include "controller/controldevice/controller/mapping/mouse/WheelHandler.h"
namespace Ship {
ControlDeck::ControlDeck(std::vector<CONTROLLERBUTTONS_T> additionalBitmasks,
std::shared_ptr<ControllerDefaultMappings> controllerDefaultMappings) {
mConnectedPhysicalDeviceManager = std::make_shared<ConnectedPhysicalDeviceManager>();
mGlobalSDLDeviceSettings = std::make_shared<GlobalSDLDeviceSettings>();
mControllerDefaultMappings = controllerDefaultMappings == nullptr ? std::make_shared<ControllerDefaultMappings>()
: controllerDefaultMappings;
}
ControlDeck::~ControlDeck() {
SPDLOG_TRACE("destruct control deck");
}
void ControlDeck::Init(uint8_t* controllerBits) {
mControllerBits = controllerBits;
*mControllerBits |= 1 << 0;
for (auto port : mPorts) {
if (port->GetConnectedController()->HasConfig()) {
port->GetConnectedController()->ReloadAllMappingsFromConfig();
}
}
// if we don't have a config for controller 1, set default bindings
if (!mPorts[0]->GetConnectedController()->HasConfig()) {
mPorts[0]->GetConnectedController()->AddDefaultMappings(PhysicalDeviceType::Keyboard);
mPorts[0]->GetConnectedController()->AddDefaultMappings(PhysicalDeviceType::Mouse);
mPorts[0]->GetConnectedController()->AddDefaultMappings(PhysicalDeviceType::SDLGamepad);
}
}
bool ControlDeck::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode) {
bool result = false;
for (auto port : mPorts) {
auto controller = port->GetConnectedController();
if (controller != nullptr) {
result = controller->ProcessKeyboardEvent(eventType, scancode) || result;
}
}
return result;
}
bool ControlDeck::ProcessMouseButtonEvent(bool isPressed, MouseBtn button) {
bool result = false;
for (auto port : mPorts) {
auto controller = port->GetConnectedController();
if (controller != nullptr) {
result = controller->ProcessMouseButtonEvent(isPressed, button) || result;
}
}
return result;
}
bool ControlDeck::AllGameInputBlocked() {
return !mGameInputBlockers.empty();
}
bool ControlDeck::GamepadGameInputBlocked() {
// block controller input when using the controller to navigate imgui menus
return AllGameInputBlocked() || Context::GetInstance()->GetWindow()->GetGui()->GetMenuOrMenubarVisible() &&
CVarGetInteger(CVAR_IMGUI_CONTROLLER_NAV, 0);
}
bool ControlDeck::KeyboardGameInputBlocked() {
// block keyboard input when typing in imgui
ImGuiWindow* activeIDWindow = ImGui::GetCurrentContext()->ActiveIdWindow;
return AllGameInputBlocked() ||
(activeIDWindow != NULL &&
activeIDWindow->ID != Context::GetInstance()->GetWindow()->GetGui()->GetMainGameWindowID()) ||
ImGui::GetTopMostPopupModal() != NULL; // ImGui::GetIO().WantCaptureKeyboard, but ActiveId check altered
}
bool ControlDeck::MouseGameInputBlocked() {
// block mouse input when user interacting with gui
ImGuiWindow* window = ImGui::GetCurrentContext()->HoveredWindow;
if (window == NULL) {
return true;
}
return AllGameInputBlocked() ||
(window->ID != Context::GetInstance()->GetWindow()->GetGui()->GetMainGameWindowID());
}
std::shared_ptr<Controller> ControlDeck::GetControllerByPort(uint8_t port) {
return mPorts[port]->GetConnectedController();
}
void ControlDeck::BlockGameInput(int32_t blockId) {
mGameInputBlockers[blockId] = true;
}
void ControlDeck::UnblockGameInput(int32_t blockId) {
mGameInputBlockers.erase(blockId);
}
std::shared_ptr<ConnectedPhysicalDeviceManager> ControlDeck::GetConnectedPhysicalDeviceManager() {
return mConnectedPhysicalDeviceManager;
}
std::shared_ptr<GlobalSDLDeviceSettings> ControlDeck::GetGlobalSDLDeviceSettings() {
return mGlobalSDLDeviceSettings;
}
std::shared_ptr<ControllerDefaultMappings> ControlDeck::GetControllerDefaultMappings() {
return mControllerDefaultMappings;
}
} // namespace Ship
namespace LUS {
ControlDeck::ControlDeck(std::vector<CONTROLLERBUTTONS_T> additionalBitmasks,
std::shared_ptr<Ship::ControllerDefaultMappings> controllerDefaultMappings)
: Ship::ControlDeck(additionalBitmasks, controllerDefaultMappings), mPads(nullptr) {
for (int32_t i = 0; i < MAXCONTROLLERS; i++) {
mPorts.push_back(std::make_shared<Ship::ControlPort>(i, std::make_shared<Controller>(i, additionalBitmasks)));
}
}
ControlDeck::ControlDeck(std::vector<CONTROLLERBUTTONS_T> additionalBitmasks)
: ControlDeck(additionalBitmasks, nullptr) {
}
ControlDeck::ControlDeck() : ControlDeck(std::vector<CONTROLLERBUTTONS_T>()) {
}
OSContPad* ControlDeck::GetPads() {
return mPads;
}
void ControlDeck::WriteToPad(void* pad) {
WriteToOSContPad((OSContPad*)pad);
}
void ControlDeck::WriteToOSContPad(OSContPad* pad) {
SDL_PumpEvents();
Ship::WheelHandler::GetInstance()->Update();
if (AllGameInputBlocked()) {
return;
}
mPads = pad;
for (size_t i = 0; i < mPorts.size(); i++) {
const std::shared_ptr<Ship::Controller> controller = mPorts[i]->GetConnectedController();
if (controller != nullptr) {
controller->ReadToPad(&pad[i]);
}
}
}
} // namespace LUS

View File

@@ -0,0 +1,64 @@
#pragma once
#include "ControlPort.h"
#include <vector>
#include <config/Config.h>
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
#include "controller/physicaldevice/ConnectedPhysicalDeviceManager.h"
#include "controller/physicaldevice/GlobalSDLDeviceSettings.h"
#include "controller/controldevice/controller/mapping/ControllerDefaultMappings.h"
namespace Ship {
class ControlDeck {
public:
ControlDeck(std::vector<CONTROLLERBUTTONS_T> additionalBitmasks,
std::shared_ptr<ControllerDefaultMappings> controllerDefaultMappings);
~ControlDeck();
void Init(uint8_t* controllerBits);
virtual void WriteToPad(void* pads) = 0;
uint8_t* GetControllerBits();
std::shared_ptr<Controller> GetControllerByPort(uint8_t port);
void BlockGameInput(int32_t blockId);
void UnblockGameInput(int32_t blockId);
bool GamepadGameInputBlocked();
bool KeyboardGameInputBlocked();
bool MouseGameInputBlocked();
bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
bool ProcessMouseButtonEvent(bool isPressed, MouseBtn button);
std::shared_ptr<ConnectedPhysicalDeviceManager> GetConnectedPhysicalDeviceManager();
std::shared_ptr<GlobalSDLDeviceSettings> GetGlobalSDLDeviceSettings();
std::shared_ptr<ControllerDefaultMappings> GetControllerDefaultMappings();
protected:
bool AllGameInputBlocked();
std::vector<std::shared_ptr<ControlPort>> mPorts = {};
private:
uint8_t* mControllerBits = nullptr;
std::unordered_map<int32_t, bool> mGameInputBlockers;
std::shared_ptr<ConnectedPhysicalDeviceManager> mConnectedPhysicalDeviceManager;
std::shared_ptr<GlobalSDLDeviceSettings> mGlobalSDLDeviceSettings;
std::shared_ptr<ControllerDefaultMappings> mControllerDefaultMappings;
};
} // namespace Ship
namespace LUS {
class ControlDeck : public Ship::ControlDeck {
public:
ControlDeck();
ControlDeck(std::vector<CONTROLLERBUTTONS_T> additionalBitmasks);
ControlDeck(std::vector<CONTROLLERBUTTONS_T> additionalBitmasks,
std::shared_ptr<Ship::ControllerDefaultMappings> controllerDefaultMappings);
OSContPad* GetPads();
void WriteToPad(void* pad) override;
private:
void WriteToOSContPad(OSContPad* pad);
OSContPad* mPads;
};
} // namespace LUS

View File

@@ -0,0 +1,30 @@
#include "ControlPort.h"
namespace Ship {
ControlPort::ControlPort(uint8_t portIndex) : mPortIndex(portIndex), mDevice(nullptr) {
}
ControlPort::ControlPort(uint8_t portIndex, std::shared_ptr<ControlDevice> device)
: mPortIndex(portIndex), mDevice(device) {
}
ControlPort::~ControlPort() {
}
void ControlPort::Connect(std::shared_ptr<ControlDevice> device) {
mDevice = device;
}
void ControlPort::Disconnect() {
mDevice = nullptr;
}
std::shared_ptr<ControlDevice> ControlPort::GetConnectedDevice() {
return mDevice;
}
std::shared_ptr<Controller> ControlPort::GetConnectedController() {
return std::dynamic_pointer_cast<Controller>(mDevice);
}
} // namespace Ship

View File

@@ -0,0 +1,24 @@
#pragma once
#include "controller/controldevice/ControlDevice.h"
#include "controller/controldevice/controller/Controller.h"
#include <memory>
namespace Ship {
class ControlPort {
public:
ControlPort(uint8_t portIndex);
ControlPort(uint8_t portIndex, std::shared_ptr<ControlDevice> device);
~ControlPort();
void Connect(std::shared_ptr<ControlDevice> device);
void Disconnect();
std::shared_ptr<ControlDevice> GetConnectedDevice();
std::shared_ptr<Controller> GetConnectedController();
private:
uint8_t mPortIndex;
std::shared_ptr<ControlDevice> mDevice;
};
} // namespace Ship

View File

@@ -0,0 +1,10 @@
#include "ControlDevice.h"
namespace Ship {
ControlDevice::ControlDevice(uint8_t portIndex) : mPortIndex(portIndex) {
}
ControlDevice::~ControlDevice() {
}
} // namespace Ship

View File

@@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
namespace Ship {
class ControlDevice {
public:
ControlDevice(uint8_t portIndex);
virtual ~ControlDevice();
protected:
uint8_t mPortIndex;
};
} // namespace Ship

View File

@@ -0,0 +1,269 @@
#include "controller/controldevice/controller/Controller.h"
#include <memory>
#include <algorithm>
#include "public/bridge/consolevariablebridge.h"
#if __APPLE__
#include <SDL_events.h>
#else
#include <SDL2/SDL_events.h>
#endif
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#define M_TAU 6.2831853071795864769252867665590057 // 2 * pi
#define MINIMUM_RADIUS_TO_MAP_NOTCH 0.9
namespace Ship {
Controller::Controller(uint8_t portIndex, std::vector<CONTROLLERBUTTONS_T> additionalBitmasks)
: ControlDevice(portIndex) {
for (auto bitmask : { BUTTON_BITMASKS }) {
mButtons[bitmask] = std::make_shared<ControllerButton>(portIndex, bitmask);
}
for (auto bitmask : additionalBitmasks) {
mButtons[bitmask] = std::make_shared<ControllerButton>(portIndex, bitmask);
}
mLeftStick = std::make_shared<ControllerStick>(portIndex, LEFT_STICK);
mRightStick = std::make_shared<ControllerStick>(portIndex, RIGHT_STICK);
mGyro = std::make_shared<ControllerGyro>(portIndex);
mRumble = std::make_shared<ControllerRumble>(portIndex);
mLED = std::make_shared<ControllerLED>(portIndex);
}
Controller::Controller(uint8_t portIndex) : Controller(portIndex, {}) {
}
Controller::~Controller() {
SPDLOG_TRACE("destruct controller");
}
std::unordered_map<CONTROLLERBUTTONS_T, std::shared_ptr<ControllerButton>> Controller::GetAllButtons() {
return mButtons;
}
std::shared_ptr<ControllerButton> Controller::GetButton(CONTROLLERBUTTONS_T bitmask) {
return mButtons[bitmask];
}
std::shared_ptr<ControllerStick> Controller::GetLeftStick() {
return mLeftStick;
}
std::shared_ptr<ControllerStick> Controller::GetRightStick() {
return mRightStick;
}
std::shared_ptr<ControllerGyro> Controller::GetGyro() {
return mGyro;
}
std::shared_ptr<ControllerRumble> Controller::GetRumble() {
return mRumble;
}
std::shared_ptr<ControllerLED> Controller::GetLED() {
return mLED;
}
uint8_t Controller::GetPortIndex() {
return mPortIndex;
}
bool Controller::HasConfig() {
const std::string hasConfigCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.HasConfig", mPortIndex + 1);
return CVarGetInteger(hasConfigCvarKey.c_str(), false);
}
void Controller::ClearAllMappings() {
for (auto [bitmask, button] : GetAllButtons()) {
button->ClearAllButtonMappings();
}
GetLeftStick()->ClearAllMappings();
GetRightStick()->ClearAllMappings();
GetGyro()->ClearGyroMapping();
GetRumble()->ClearAllMappings();
GetLED()->ClearAllMappings();
}
void Controller::ClearAllMappingsForDeviceType(PhysicalDeviceType physicalDeviceType) {
for (auto [bitmask, button] : GetAllButtons()) {
button->ClearAllButtonMappingsForDeviceType(physicalDeviceType);
}
GetLeftStick()->ClearAllMappingsForDeviceType(physicalDeviceType);
GetRightStick()->ClearAllMappingsForDeviceType(physicalDeviceType);
auto gyroMapping = GetGyro()->GetGyroMapping();
if (gyroMapping != nullptr && gyroMapping->GetPhysicalDeviceType() == physicalDeviceType) {
GetGyro()->ClearGyroMapping();
}
GetRumble()->ClearAllMappingsForDeviceType(physicalDeviceType);
GetLED()->ClearAllMappingsForDeviceType(physicalDeviceType);
}
void Controller::AddDefaultMappings(PhysicalDeviceType physicalDeviceType) {
for (auto [bitmask, button] : GetAllButtons()) {
button->AddDefaultMappings(physicalDeviceType);
}
GetLeftStick()->AddDefaultMappings(physicalDeviceType);
GetRightStick()->AddDefaultMappings(physicalDeviceType);
GetRumble()->AddDefaultMappings(physicalDeviceType);
const std::string hasConfigCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.HasConfig", mPortIndex + 1);
CVarSetInteger(hasConfigCvarKey.c_str(), true);
CVarSave();
}
void Controller::ReloadAllMappingsFromConfig() {
for (auto [bitmask, button] : GetAllButtons()) {
button->ReloadAllMappingsFromConfig();
}
GetLeftStick()->ReloadAllMappingsFromConfig();
GetRightStick()->ReloadAllMappingsFromConfig();
GetGyro()->ReloadGyroMappingFromConfig();
GetRumble()->ReloadAllMappingsFromConfig();
GetLED()->ReloadAllMappingsFromConfig();
}
bool Controller::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode) {
bool result = false;
for (auto [bitmask, button] : GetAllButtons()) {
result = button->ProcessKeyboardEvent(eventType, scancode) || result;
}
result = GetLeftStick()->ProcessKeyboardEvent(eventType, scancode) || result;
result = GetRightStick()->ProcessKeyboardEvent(eventType, scancode) || result;
return result;
}
bool Controller::ProcessMouseButtonEvent(bool isPressed, MouseBtn mouseButton) {
bool result = false;
for (auto [bitmask, button] : GetAllButtons()) {
result = button->ProcessMouseButtonEvent(isPressed, mouseButton) || result;
}
result = GetLeftStick()->ProcessMouseButtonEvent(isPressed, mouseButton) || result;
result = GetRightStick()->ProcessMouseButtonEvent(isPressed, mouseButton) || result;
return result;
}
bool Controller::HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType) {
for (auto [bitmask, button] : GetAllButtons()) {
if (button->HasMappingsForPhysicalDeviceType(physicalDeviceType)) {
return true;
}
}
if (GetLeftStick()->HasMappingsForPhysicalDeviceType(physicalDeviceType)) {
return true;
}
if (GetRightStick()->HasMappingsForPhysicalDeviceType(physicalDeviceType)) {
return true;
}
if (GetGyro()->HasMappingForPhysicalDeviceType(physicalDeviceType)) {
return true;
}
if (GetRumble()->HasMappingsForPhysicalDeviceType(physicalDeviceType)) {
return true;
}
if (GetLED()->HasMappingsForPhysicalDeviceType(physicalDeviceType)) {
return true;
}
return false;
}
std::shared_ptr<ControllerButton> Controller::GetButtonByBitmask(CONTROLLERBUTTONS_T bitmask) {
return mButtons[bitmask];
}
std::vector<std::shared_ptr<ControllerMapping>> Controller::GetAllMappings() {
std::vector<std::shared_ptr<ControllerMapping>> allMappings;
for (auto [bitmask, button] : GetAllButtons()) {
for (auto [id, mapping] : button->GetAllButtonMappings()) {
allMappings.push_back(mapping);
}
}
for (auto stick : { GetLeftStick(), GetRightStick() }) {
for (auto [direction, mappings] : stick->GetAllAxisDirectionMappings()) {
for (auto [id, mapping] : mappings) {
allMappings.push_back(mapping);
}
}
}
allMappings.push_back(GetGyro()->GetGyroMapping());
for (auto [id, mapping] : GetRumble()->GetAllRumbleMappings()) {
allMappings.push_back(mapping);
}
for (auto [id, mapping] : GetLED()->GetAllLEDMappings()) {
allMappings.push_back(mapping);
}
return allMappings;
}
} // namespace Ship
namespace LUS {
Controller::Controller(uint8_t portIndex, std::vector<CONTROLLERBUTTONS_T> additionalBitmasks)
: Ship::Controller(portIndex, additionalBitmasks) {
}
Controller::Controller(uint8_t portIndex) : Ship::Controller(portIndex, {}) {
}
void Controller::ReadToPad(void* pad) {
ReadToOSContPad((OSContPad*)pad);
}
void Controller::ReadToOSContPad(OSContPad* pad) {
OSContPad padToBuffer = { 0 };
// Button Inputs
for (auto [bitmask, button] : mButtons) {
button->UpdatePad(padToBuffer.button);
}
// Stick Inputs
GetLeftStick()->UpdatePad(padToBuffer.stick_x, padToBuffer.stick_y);
GetRightStick()->UpdatePad(padToBuffer.right_stick_x, padToBuffer.right_stick_y);
// Gyro
GetGyro()->UpdatePad(padToBuffer.gyro_x, padToBuffer.gyro_y);
mPadBuffer.push_front(padToBuffer);
if (pad != nullptr) {
auto& padFromBuffer =
mPadBuffer[std::min(mPadBuffer.size() - 1, (size_t)CVarGetInteger(CVAR_SIMULATED_INPUT_LAG, 0))];
pad->button |= padFromBuffer.button;
if (pad->stick_x == 0) {
pad->stick_x = padFromBuffer.stick_x;
}
if (pad->stick_y == 0) {
pad->stick_y = padFromBuffer.stick_y;
}
if (pad->right_stick_x == 0) {
pad->right_stick_x = padFromBuffer.right_stick_x;
}
if (pad->right_stick_y == 0) {
pad->right_stick_y = padFromBuffer.right_stick_y;
}
if (pad->gyro_x == 0) {
pad->gyro_x = padFromBuffer.gyro_x;
}
if (pad->gyro_y == 0) {
pad->gyro_y = padFromBuffer.gyro_y;
}
}
while (mPadBuffer.size() > 6) {
mPadBuffer.pop_back();
}
}
} // namespace LUS

View File

@@ -0,0 +1,83 @@
#pragma once
#include <map>
#include <memory>
#include <string>
#include <cstdint>
#include <queue>
#include <vector>
#include <map>
#include "libultraship/libultra/controller.h"
#include "libultraship/color.h"
#include <unordered_map>
#include "ControllerButton.h"
#include "ControllerStick.h"
#include "ControllerGyro.h"
#include "ControllerRumble.h"
#include "ControllerLED.h"
#include "controller/controldevice/ControlDevice.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
class Controller : public ControlDevice {
public:
Controller(uint8_t portIndex);
Controller(uint8_t portIndex, std::vector<CONTROLLERBUTTONS_T> additionalBitmasks);
~Controller();
void ReloadAllMappingsFromConfig();
bool IsConnected() const;
void Connect();
void Disconnect();
void ClearAllMappings();
void ClearAllMappingsForDeviceType(PhysicalDeviceType physicalDeviceType);
void AddDefaultMappings(PhysicalDeviceType physicalDeviceType);
std::unordered_map<CONTROLLERBUTTONS_T, std::shared_ptr<ControllerButton>> GetAllButtons();
std::shared_ptr<ControllerButton> GetButtonByBitmask(CONTROLLERBUTTONS_T bitmask);
std::shared_ptr<ControllerButton> GetButton(CONTROLLERBUTTONS_T bitmask);
std::shared_ptr<ControllerStick> GetLeftStick();
std::shared_ptr<ControllerStick> GetRightStick();
std::shared_ptr<ControllerGyro> GetGyro();
std::shared_ptr<ControllerRumble> GetRumble();
std::shared_ptr<ControllerLED> GetLED();
virtual void ReadToPad(void* pad) = 0;
bool HasConfig();
uint8_t GetPortIndex();
std::vector<std::shared_ptr<ControllerMapping>> GetAllMappings();
bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
bool ProcessMouseButtonEvent(bool isPressed, MouseBtn button);
bool HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType);
protected:
std::unordered_map<CONTROLLERBUTTONS_T, std::shared_ptr<ControllerButton>> mButtons;
private:
void LoadButtonMappingFromConfig(std::string id);
void SaveButtonMappingIdsToConfig();
std::shared_ptr<ControllerStick> mLeftStick, mRightStick;
std::shared_ptr<ControllerGyro> mGyro;
std::shared_ptr<ControllerRumble> mRumble;
std::shared_ptr<ControllerLED> mLED;
};
} // namespace Ship
namespace LUS {
class Controller : public Ship::Controller {
public:
Controller(uint8_t portIndex);
Controller(uint8_t portIndex, std::vector<CONTROLLERBUTTONS_T> additionalBitmasks);
void ReadToPad(void* pad) override;
private:
void ReadToOSContPad(OSContPad* pad);
std::deque<OSContPad> mPadBuffer;
};
} // namespace LUS

View File

@@ -0,0 +1,286 @@
#include "ControllerButton.h"
#include "controller/controldevice/controller/mapping/factories/ButtonMappingFactory.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardKeyToButtonMapping.h"
#include "controller/controldevice/controller/mapping/mouse/MouseButtonToButtonMapping.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include <sstream>
#include <algorithm>
#include "Context.h"
namespace Ship {
ControllerButton::ControllerButton(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask)
: mPortIndex(portIndex), mBitmask(bitmask), mUseEventInputToCreateNewMapping(false),
mKeyboardScancodeForNewMapping(LUS_KB_UNKNOWN), mMouseButtonForNewMapping(LUS_MOUSE_BTN_UNKNOWN) {
}
ControllerButton::~ControllerButton() {
}
std::string ControllerButton::GetConfigNameFromBitmask(CONTROLLERBUTTONS_T bitmask) {
switch (bitmask) {
case BTN_A:
return "A";
case BTN_B:
return "B";
case BTN_L:
return "L";
case BTN_R:
return "R";
case BTN_Z:
return "Z";
case BTN_START:
return "Start";
case BTN_CLEFT:
return "CLeft";
case BTN_CRIGHT:
return "CRight";
case BTN_CUP:
return "CUp";
case BTN_CDOWN:
return "CDown";
case BTN_DLEFT:
return "DLeft";
case BTN_DRIGHT:
return "DRight";
case BTN_DUP:
return "DUp";
case BTN_DDOWN:
return "DDown";
default:
// if we don't have a name for this bitmask,
// which happens with additionalBitmasks provided by ports,
// return the stringified bitmask
return std::to_string(bitmask);
}
}
std::unordered_map<std::string, std::shared_ptr<ControllerButtonMapping>> ControllerButton::GetAllButtonMappings() {
return mButtonMappings;
}
std::shared_ptr<ControllerButtonMapping> ControllerButton::GetButtonMappingById(std::string id) {
if (!mButtonMappings.contains(id)) {
return nullptr;
}
return mButtonMappings[id];
}
void ControllerButton::AddButtonMapping(std::shared_ptr<ControllerButtonMapping> mapping) {
mButtonMappings[mapping->GetButtonMappingId()] = mapping;
}
void ControllerButton::ClearButtonMappingId(std::string id) {
mButtonMappings.erase(id);
SaveButtonMappingIdsToConfig();
}
void ControllerButton::ClearButtonMapping(std::string id) {
mButtonMappings[id]->EraseFromConfig();
mButtonMappings.erase(id);
SaveButtonMappingIdsToConfig();
}
void ControllerButton::ClearButtonMapping(std::shared_ptr<ControllerButtonMapping> mapping) {
ClearButtonMapping(mapping->GetButtonMappingId());
}
void ControllerButton::LoadButtonMappingFromConfig(std::string id) {
auto mapping = ButtonMappingFactory::CreateButtonMappingFromConfig(mPortIndex, id);
if (mapping == nullptr) {
return;
}
AddButtonMapping(mapping);
}
void ControllerButton::SaveButtonMappingIdsToConfig() {
// todo: this efficently (when we build out cvar array support?)
std::string buttonMappingIdListString = "";
for (auto [id, mapping] : mButtonMappings) {
buttonMappingIdListString += id;
buttonMappingIdListString += ",";
}
const std::string buttonMappingIdsCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.Buttons.%sButtonMappingIds", mPortIndex + 1,
GetConfigNameFromBitmask(mBitmask).c_str());
if (buttonMappingIdListString == "") {
CVarClear(buttonMappingIdsCvarKey.c_str());
} else {
CVarSetString(buttonMappingIdsCvarKey.c_str(), buttonMappingIdListString.c_str());
}
CVarSave();
}
void ControllerButton::ReloadAllMappingsFromConfig() {
mButtonMappings.clear();
// todo: this efficently (when we build out cvar array support?)
// i don't expect it to really be a problem with the small number of mappings we have
// for each controller (especially compared to include/exclude locations in rando), and
// the audio editor pattern doesn't work for this because that looks for ids that are either
// hardcoded or provided by an otr file
const std::string buttonMappingIdsCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.Buttons.%sButtonMappingIds", mPortIndex + 1,
GetConfigNameFromBitmask(mBitmask).c_str());
std::stringstream buttonMappingIdsStringStream(CVarGetString(buttonMappingIdsCvarKey.c_str(), ""));
std::string buttonMappingIdString;
while (getline(buttonMappingIdsStringStream, buttonMappingIdString, ',')) {
LoadButtonMappingFromConfig(buttonMappingIdString);
}
}
void ControllerButton::ClearAllButtonMappings() {
for (auto [id, mapping] : mButtonMappings) {
mapping->EraseFromConfig();
}
mButtonMappings.clear();
SaveButtonMappingIdsToConfig();
}
void ControllerButton::ClearAllButtonMappingsForDeviceType(PhysicalDeviceType physicalDeviceType) {
std::vector<std::string> mappingIdsToRemove;
for (auto [id, mapping] : mButtonMappings) {
if (mapping->GetPhysicalDeviceType() == physicalDeviceType) {
mapping->EraseFromConfig();
mappingIdsToRemove.push_back(id);
}
}
for (auto id : mappingIdsToRemove) {
auto it = mButtonMappings.find(id);
if (it != mButtonMappings.end()) {
mButtonMappings.erase(it);
}
}
SaveButtonMappingIdsToConfig();
}
void ControllerButton::UpdatePad(CONTROLLERBUTTONS_T& padButtons) {
for (const auto& [id, mapping] : mButtonMappings) {
mapping->UpdatePad(padButtons);
}
}
bool ControllerButton::HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType) {
return std::any_of(mButtonMappings.begin(), mButtonMappings.end(), [physicalDeviceType](const auto& mapping) {
return mapping.second->GetPhysicalDeviceType() == physicalDeviceType;
});
}
bool ControllerButton::AddOrEditButtonMappingFromRawPress(CONTROLLERBUTTONS_T bitmask, std::string id) {
std::shared_ptr<ControllerButtonMapping> mapping = nullptr;
mUseEventInputToCreateNewMapping = true;
if (mKeyboardScancodeForNewMapping != LUS_KB_UNKNOWN) {
mapping = std::make_shared<KeyboardKeyToButtonMapping>(mPortIndex, bitmask, mKeyboardScancodeForNewMapping);
}
else if (!Context::GetInstance()->GetWindow()->GetGui()->IsMouseOverAnyGuiItem() &&
Context::GetInstance()->GetWindow()->GetGui()->IsMouseOverActivePopup()) {
if (mMouseButtonForNewMapping != LUS_MOUSE_BTN_UNKNOWN) {
mapping = std::make_shared<MouseButtonToButtonMapping>(mPortIndex, bitmask, mMouseButtonForNewMapping);
} else {
mapping = ButtonMappingFactory::CreateButtonMappingFromMouseWheelInput(mPortIndex, bitmask);
}
}
if (mapping == nullptr) {
mapping = ButtonMappingFactory::CreateButtonMappingFromSDLInput(mPortIndex, bitmask);
}
if (mapping == nullptr) {
return false;
}
mKeyboardScancodeForNewMapping = LUS_KB_UNKNOWN;
mMouseButtonForNewMapping = LUS_MOUSE_BTN_UNKNOWN;
mUseEventInputToCreateNewMapping = false;
if (id != "") {
ClearButtonMapping(id);
}
AddButtonMapping(mapping);
mapping->SaveToConfig();
SaveButtonMappingIdsToConfig();
const std::string hasConfigCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.HasConfig", mPortIndex + 1);
CVarSetInteger(hasConfigCvarKey.c_str(), true);
CVarSave();
return true;
}
bool ControllerButton::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode) {
if (mUseEventInputToCreateNewMapping) {
if (eventType == LUS_KB_EVENT_KEY_DOWN) {
mKeyboardScancodeForNewMapping = scancode;
return true;
} else {
mKeyboardScancodeForNewMapping = LUS_KB_UNKNOWN;
}
}
bool result = false;
for (auto [id, mapping] : GetAllButtonMappings()) {
if (mapping->GetMappingType() == MAPPING_TYPE_KEYBOARD) {
std::shared_ptr<KeyboardKeyToButtonMapping> ktobMapping =
std::dynamic_pointer_cast<KeyboardKeyToButtonMapping>(mapping);
if (ktobMapping != nullptr) {
result = result || ktobMapping->ProcessKeyboardEvent(eventType, scancode);
}
}
}
return result;
}
bool ControllerButton::ProcessMouseButtonEvent(bool isPressed, MouseBtn button) {
if (mUseEventInputToCreateNewMapping) {
if (isPressed) {
mMouseButtonForNewMapping = button;
return true;
} else {
mMouseButtonForNewMapping = LUS_MOUSE_BTN_UNKNOWN;
}
}
bool result = false;
for (auto [id, mapping] : GetAllButtonMappings()) {
if (mapping->GetMappingType() == MAPPING_TYPE_MOUSE) {
std::shared_ptr<MouseButtonToButtonMapping> mtobMapping =
std::dynamic_pointer_cast<MouseButtonToButtonMapping>(mapping);
if (mtobMapping != nullptr) {
result = result || mtobMapping->ProcessMouseButtonEvent(isPressed, button);
}
}
}
return result;
}
void ControllerButton::AddDefaultMappings(PhysicalDeviceType physicalDeviceType) {
if (physicalDeviceType == PhysicalDeviceType::SDLGamepad) {
for (auto mapping : ButtonMappingFactory::CreateDefaultSDLButtonMappings(mPortIndex, mBitmask)) {
AddButtonMapping(mapping);
}
}
if (physicalDeviceType == PhysicalDeviceType::Keyboard) {
for (auto mapping : ButtonMappingFactory::CreateDefaultKeyboardButtonMappings(mPortIndex, mBitmask)) {
AddButtonMapping(mapping);
}
}
for (auto [id, mapping] : mButtonMappings) {
mapping->SaveToConfig();
}
SaveButtonMappingIdsToConfig();
}
} // namespace Ship

View File

@@ -0,0 +1,54 @@
#pragma once
#include "mapping/ControllerButtonMapping.h"
#include <memory>
#include <unordered_map>
#include <string>
#include "libultraship/libultra/controller.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
#define BUTTON_BITMASKS \
BTN_A, BTN_B, BTN_L, BTN_R, BTN_Z, BTN_START, BTN_CLEFT, BTN_CRIGHT, BTN_CUP, BTN_CDOWN, BTN_DLEFT, BTN_DRIGHT, \
BTN_DUP, BTN_DDOWN
class ControllerButton {
public:
ControllerButton(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask);
~ControllerButton();
std::shared_ptr<ControllerButtonMapping> GetButtonMappingById(std::string id);
std::unordered_map<std::string, std::shared_ptr<ControllerButtonMapping>> GetAllButtonMappings();
void AddButtonMapping(std::shared_ptr<ControllerButtonMapping> mapping);
void ClearButtonMappingId(std::string id);
void ClearButtonMapping(std::string id);
void ClearButtonMapping(std::shared_ptr<ControllerButtonMapping> mapping);
void AddDefaultMappings(PhysicalDeviceType physicalDeviceType);
void LoadButtonMappingFromConfig(std::string id);
void SaveButtonMappingIdsToConfig();
void ReloadAllMappingsFromConfig();
void ClearAllButtonMappings();
void ClearAllButtonMappingsForDeviceType(PhysicalDeviceType physicalDeviceType);
bool AddOrEditButtonMappingFromRawPress(CONTROLLERBUTTONS_T bitmask, std::string id);
void UpdatePad(CONTROLLERBUTTONS_T& padButtons);
bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
bool ProcessMouseButtonEvent(bool isPressed, Ship::MouseBtn button);
bool HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType);
private:
uint8_t mPortIndex;
CONTROLLERBUTTONS_T mBitmask;
std::unordered_map<std::string, std::shared_ptr<ControllerButtonMapping>> mButtonMappings;
std::string GetConfigNameFromBitmask(CONTROLLERBUTTONS_T bitmask);
bool mUseEventInputToCreateNewMapping;
KbScancode mKeyboardScancodeForNewMapping;
MouseBtn mMouseButtonForNewMapping;
};
} // namespace Ship

View File

@@ -0,0 +1,94 @@
#include "ControllerGyro.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include "controller/controldevice/controller/mapping/factories/GyroMappingFactory.h"
namespace Ship {
ControllerGyro::ControllerGyro(uint8_t portIndex) : mPortIndex(portIndex) {
}
ControllerGyro::~ControllerGyro() {
}
std::shared_ptr<ControllerGyroMapping> ControllerGyro::GetGyroMapping() {
return mGyroMapping;
}
void ControllerGyro::SetGyroMapping(std::shared_ptr<ControllerGyroMapping> mapping) {
mGyroMapping = mapping;
}
bool ControllerGyro::SetGyroMappingFromRawPress() {
std::shared_ptr<ControllerGyroMapping> mapping = nullptr;
mapping = GyroMappingFactory::CreateGyroMappingFromSDLInput(mPortIndex);
if (mapping == nullptr) {
return false;
}
SetGyroMapping(mapping);
mapping->SaveToConfig();
SaveGyroMappingIdToConfig();
const std::string hasConfigCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.HasConfig", mPortIndex + 1);
CVarSetInteger(hasConfigCvarKey.c_str(), true);
CVarSave();
return true;
}
void ControllerGyro::UpdatePad(float& x, float& y) {
if (mGyroMapping == nullptr) {
return;
}
mGyroMapping->UpdatePad(x, y);
}
void ControllerGyro::SaveGyroMappingIdToConfig() {
const std::string gyroMappingIdCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.Gyro.GyroMappingId", mPortIndex + 1);
if (mGyroMapping == nullptr) {
CVarClear(gyroMappingIdCvarKey.c_str());
} else {
CVarSetString(gyroMappingIdCvarKey.c_str(), mGyroMapping->GetGyroMappingId().c_str());
}
CVarSave();
}
void ControllerGyro::ClearGyroMapping() {
if (mGyroMapping == nullptr) {
return;
}
mGyroMapping->EraseFromConfig();
mGyroMapping = nullptr;
SaveGyroMappingIdToConfig();
}
void ControllerGyro::ReloadGyroMappingFromConfig() {
const std::string gyroMappingIdCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.Gyro.GyroMappingId", mPortIndex + 1);
std::string id = CVarGetString(gyroMappingIdCvarKey.c_str(), "");
if (id == "") {
mGyroMapping = nullptr;
return;
}
mGyroMapping = GyroMappingFactory::CreateGyroMappingFromConfig(mPortIndex, id);
mGyroMapping->SaveToConfig();
SaveGyroMappingIdToConfig();
}
bool ControllerGyro::HasMappingForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType) {
if (mGyroMapping == nullptr) {
return false;
}
return mGyroMapping->GetPhysicalDeviceType() == physicalDeviceType;
}
} // namespace Ship

View File

@@ -0,0 +1,30 @@
#pragma once
#include <cstdint>
#include <memory>
#include "controller/controldevice/controller/mapping/ControllerGyroMapping.h"
namespace Ship {
class ControllerGyro {
public:
ControllerGyro(uint8_t portIndex);
~ControllerGyro();
void ReloadGyroMappingFromConfig();
void ClearGyroMapping();
void SaveGyroMappingIdToConfig();
std::shared_ptr<ControllerGyroMapping> GetGyroMapping();
void SetGyroMapping(std::shared_ptr<ControllerGyroMapping> mapping);
bool SetGyroMappingFromRawPress();
void UpdatePad(float& x, float& y);
bool HasMappingForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType);
private:
uint8_t mPortIndex;
std::shared_ptr<ControllerGyroMapping> mGyroMapping;
};
} // namespace Ship

View File

@@ -0,0 +1,138 @@
#include "ControllerLED.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include <sstream>
#include <algorithm>
#include "controller/controldevice/controller/mapping/factories/LEDMappingFactory.h"
namespace Ship {
ControllerLED::ControllerLED(uint8_t portIndex) : mPortIndex(portIndex) {
}
ControllerLED::~ControllerLED() {
}
void ControllerLED::SetLEDColor(Color_RGB8 color) {
for (auto [id, mapping] : mLEDMappings) {
mapping->SetLEDColor(color);
}
}
void ControllerLED::SaveLEDMappingIdsToConfig() {
// todo: this efficently (when we build out cvar array support?)
std::string ledMappingIdListString = "";
for (auto [id, mapping] : mLEDMappings) {
ledMappingIdListString += id;
ledMappingIdListString += ",";
}
const std::string ledMappingIdsCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.LEDMappingIds", mPortIndex + 1);
if (ledMappingIdsCvarKey == "") {
CVarClear(ledMappingIdsCvarKey.c_str());
} else {
CVarSetString(ledMappingIdsCvarKey.c_str(), ledMappingIdListString.c_str());
}
CVarSave();
}
void ControllerLED::AddLEDMapping(std::shared_ptr<ControllerLEDMapping> mapping) {
mLEDMappings[mapping->GetLEDMappingId()] = mapping;
}
void ControllerLED::ClearLEDMappingId(std::string id) {
mLEDMappings.erase(id);
SaveLEDMappingIdsToConfig();
}
void ControllerLED::ClearLEDMapping(std::string id) {
mLEDMappings[id]->EraseFromConfig();
mLEDMappings.erase(id);
SaveLEDMappingIdsToConfig();
}
void ControllerLED::ClearAllMappings() {
for (auto [id, mapping] : mLEDMappings) {
mapping->EraseFromConfig();
}
mLEDMappings.clear();
SaveLEDMappingIdsToConfig();
}
void ControllerLED::ClearAllMappingsForDeviceType(PhysicalDeviceType physicalDeviceType) {
std::vector<std::string> mappingIdsToRemove;
for (auto [id, mapping] : mLEDMappings) {
if (mapping->GetPhysicalDeviceType() == physicalDeviceType) {
mapping->EraseFromConfig();
mappingIdsToRemove.push_back(id);
}
}
for (auto id : mappingIdsToRemove) {
auto it = mLEDMappings.find(id);
if (it != mLEDMappings.end()) {
mLEDMappings.erase(it);
}
}
SaveLEDMappingIdsToConfig();
}
void ControllerLED::LoadLEDMappingFromConfig(std::string id) {
auto mapping = LEDMappingFactory::CreateLEDMappingFromConfig(mPortIndex, id);
if (mapping == nullptr) {
return;
}
AddLEDMapping(mapping);
}
void ControllerLED::ReloadAllMappingsFromConfig() {
mLEDMappings.clear();
// todo: this efficently (when we build out cvar array support?)
// i don't expect it to really be a problem with the small number of mappings we have
// for each controller (especially compared to include/exclude locations in rando), and
// the audio editor pattern doesn't work for this because that looks for ids that are either
// hardcoded or provided by an otr file
const std::string ledMappingIdsCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.LEDMappingIds", mPortIndex + 1);
std::stringstream ledMappingIdsStringStream(CVarGetString(ledMappingIdsCvarKey.c_str(), ""));
std::string ledMappingIdString;
while (getline(ledMappingIdsStringStream, ledMappingIdString, ',')) {
LoadLEDMappingFromConfig(ledMappingIdString);
}
}
std::unordered_map<std::string, std::shared_ptr<ControllerLEDMapping>> ControllerLED::GetAllLEDMappings() {
return mLEDMappings;
}
bool ControllerLED::AddLEDMappingFromRawPress() {
std::shared_ptr<ControllerLEDMapping> mapping = nullptr;
mapping = LEDMappingFactory::CreateLEDMappingFromSDLInput(mPortIndex);
if (mapping == nullptr) {
return false;
}
AddLEDMapping(mapping);
mapping->SaveToConfig();
SaveLEDMappingIdsToConfig();
const std::string hasConfigCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.HasConfig", mPortIndex + 1);
CVarSetInteger(hasConfigCvarKey.c_str(), true);
CVarSave();
return true;
}
bool ControllerLED::HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType) {
return std::any_of(mLEDMappings.begin(), mLEDMappings.end(), [physicalDeviceType](const auto& mapping) {
return mapping.second->GetPhysicalDeviceType() == physicalDeviceType;
});
}
} // namespace Ship

View File

@@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <string>
#include "controller/controldevice/controller/mapping/ControllerLEDMapping.h"
namespace Ship {
class ControllerLED {
public:
ControllerLED(uint8_t portIndex);
~ControllerLED();
std::unordered_map<std::string, std::shared_ptr<ControllerLEDMapping>> GetAllLEDMappings();
void AddLEDMapping(std::shared_ptr<ControllerLEDMapping> mapping);
void ClearLEDMappingId(std::string id);
void ClearLEDMapping(std::string id);
void SaveLEDMappingIdsToConfig();
void ClearAllMappings();
void ClearAllMappingsForDeviceType(PhysicalDeviceType physicalDeviceType);
void LoadLEDMappingFromConfig(std::string id);
void ReloadAllMappingsFromConfig();
bool AddLEDMappingFromRawPress();
void SetLEDColor(Color_RGB8 color);
bool HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType);
private:
uint8_t mPortIndex;
std::unordered_map<std::string, std::shared_ptr<ControllerLEDMapping>> mLEDMappings;
};
} // namespace Ship

View File

@@ -0,0 +1,154 @@
#include "ControllerRumble.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include <sstream>
#include "controller/controldevice/controller/mapping/factories/RumbleMappingFactory.h"
namespace Ship {
ControllerRumble::ControllerRumble(uint8_t portIndex) : mPortIndex(portIndex) {
}
ControllerRumble::~ControllerRumble() {
}
void ControllerRumble::StartRumble() {
for (auto [id, mapping] : mRumbleMappings) {
mapping->StartRumble();
}
}
void ControllerRumble::StopRumble() {
for (auto [id, mapping] : mRumbleMappings) {
mapping->StopRumble();
}
}
void ControllerRumble::SaveRumbleMappingIdsToConfig() {
// todo: this efficently (when we build out cvar array support?)
std::string rumbleMappingIdListString = "";
for (auto [id, mapping] : mRumbleMappings) {
rumbleMappingIdListString += id;
rumbleMappingIdListString += ",";
}
const std::string rumbleMappingIdsCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.RumbleMappingIds", mPortIndex + 1);
if (rumbleMappingIdListString == "") {
CVarClear(rumbleMappingIdsCvarKey.c_str());
} else {
CVarSetString(rumbleMappingIdsCvarKey.c_str(), rumbleMappingIdListString.c_str());
}
CVarSave();
}
void ControllerRumble::AddRumbleMapping(std::shared_ptr<ControllerRumbleMapping> mapping) {
mRumbleMappings[mapping->GetRumbleMappingId()] = mapping;
}
void ControllerRumble::ClearRumbleMappingId(std::string id) {
mRumbleMappings.erase(id);
SaveRumbleMappingIdsToConfig();
}
void ControllerRumble::ClearRumbleMapping(std::string id) {
mRumbleMappings[id]->EraseFromConfig();
mRumbleMappings.erase(id);
SaveRumbleMappingIdsToConfig();
}
void ControllerRumble::ClearAllMappings() {
for (auto [id, mapping] : mRumbleMappings) {
mapping->EraseFromConfig();
}
mRumbleMappings.clear();
SaveRumbleMappingIdsToConfig();
}
void ControllerRumble::ClearAllMappingsForDeviceType(PhysicalDeviceType physicalDeviceType) {
std::vector<std::string> mappingIdsToRemove;
for (auto [id, mapping] : mRumbleMappings) {
if (mapping->GetPhysicalDeviceType() == physicalDeviceType) {
mapping->EraseFromConfig();
mappingIdsToRemove.push_back(id);
}
}
for (auto id : mappingIdsToRemove) {
auto it = mRumbleMappings.find(id);
if (it != mRumbleMappings.end()) {
mRumbleMappings.erase(it);
}
}
SaveRumbleMappingIdsToConfig();
}
void ControllerRumble::AddDefaultMappings(PhysicalDeviceType physicalDeviceType) {
for (auto mapping : RumbleMappingFactory::CreateDefaultSDLRumbleMappings(physicalDeviceType, mPortIndex)) {
AddRumbleMapping(mapping);
}
for (auto [id, mapping] : mRumbleMappings) {
mapping->SaveToConfig();
}
SaveRumbleMappingIdsToConfig();
}
void ControllerRumble::LoadRumbleMappingFromConfig(std::string id) {
auto mapping = RumbleMappingFactory::CreateRumbleMappingFromConfig(mPortIndex, id);
if (mapping == nullptr) {
return;
}
AddRumbleMapping(mapping);
}
void ControllerRumble::ReloadAllMappingsFromConfig() {
mRumbleMappings.clear();
// todo: this efficently (when we build out cvar array support?)
// i don't expect it to really be a problem with the small number of mappings we have
// for each controller (especially compared to include/exclude locations in rando), and
// the audio editor pattern doesn't work for this because that looks for ids that are either
// hardcoded or provided by an otr file
const std::string rumbleMappingIdsCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.RumbleMappingIds", mPortIndex + 1);
std::stringstream rumbleMappingIdsStringStream(CVarGetString(rumbleMappingIdsCvarKey.c_str(), ""));
std::string rumbleMappingIdString;
while (getline(rumbleMappingIdsStringStream, rumbleMappingIdString, ',')) {
LoadRumbleMappingFromConfig(rumbleMappingIdString);
}
}
std::unordered_map<std::string, std::shared_ptr<ControllerRumbleMapping>> ControllerRumble::GetAllRumbleMappings() {
return mRumbleMappings;
}
bool ControllerRumble::AddRumbleMappingFromRawPress() {
std::shared_ptr<ControllerRumbleMapping> mapping = nullptr;
mapping = RumbleMappingFactory::CreateRumbleMappingFromSDLInput(mPortIndex);
if (mapping == nullptr) {
return false;
}
AddRumbleMapping(mapping);
mapping->SaveToConfig();
SaveRumbleMappingIdsToConfig();
const std::string hasConfigCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.HasConfig", mPortIndex + 1);
CVarSetInteger(hasConfigCvarKey.c_str(), true);
CVarSave();
return true;
}
bool ControllerRumble::HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType) {
return std::any_of(mRumbleMappings.begin(), mRumbleMappings.end(), [physicalDeviceType](const auto& mapping) {
return mapping.second->GetPhysicalDeviceType() == physicalDeviceType;
});
}
} // namespace Ship

View File

@@ -0,0 +1,38 @@
#pragma once
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <string>
#include "controller/controldevice/controller/mapping/ControllerRumbleMapping.h"
namespace Ship {
class ControllerRumble {
public:
ControllerRumble(uint8_t portIndex);
~ControllerRumble();
std::unordered_map<std::string, std::shared_ptr<ControllerRumbleMapping>> GetAllRumbleMappings();
void AddRumbleMapping(std::shared_ptr<ControllerRumbleMapping> mapping);
void ClearRumbleMappingId(std::string id);
void ClearRumbleMapping(std::string id);
void SaveRumbleMappingIdsToConfig();
void ClearAllMappings();
void ClearAllMappingsForDeviceType(PhysicalDeviceType physicalDeviceType);
void AddDefaultMappings(PhysicalDeviceType physicalDeviceType);
void LoadRumbleMappingFromConfig(std::string id);
void ReloadAllMappingsFromConfig();
bool AddRumbleMappingFromRawPress();
void StartRumble();
void StopRumble();
bool HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType);
private:
uint8_t mPortIndex;
std::unordered_map<std::string, std::shared_ptr<ControllerRumbleMapping>> mRumbleMappings;
};
} // namespace Ship

View File

@@ -0,0 +1,469 @@
#include "ControllerStick.h"
#include <spdlog/spdlog.h>
#include "controller/controldevice/controller/mapping/keyboard/KeyboardKeyToAxisDirectionMapping.h"
#include "controller/controldevice/controller/mapping/mouse/MouseButtonToAxisDirectionMapping.h"
#include "controller/controldevice/controller/mapping/factories/AxisDirectionMappingFactory.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include <sstream>
#include <algorithm>
#include "Context.h"
// for some reason windows isn't seeing M_PI
// this is copied from my system's math.h
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define M_TAU 2 * M_PI
#define MINIMUM_RADIUS_TO_MAP_NOTCH 0.9
namespace Ship {
ControllerStick::ControllerStick(uint8_t portIndex, StickIndex stickIndex)
: mPortIndex(portIndex), mStickIndex(stickIndex), mUseEventInputToCreateNewMapping(false),
mKeyboardScancodeForNewMapping(KbScancode::LUS_KB_UNKNOWN), mMouseButtonForNewMapping(LUS_MOUSE_BTN_UNKNOWN) {
mSensitivityPercentage = DEFAULT_STICK_SENSITIVITY_PERCENTAGE;
mSensitivity = 1.0f;
mDeadzonePercentage = DEFAULT_STICK_DEADZONE_PERCENTAGE;
mDeadzone = 17.0f;
mNotchSnapAngle = 0;
}
ControllerStick::~ControllerStick() {
}
void ControllerStick::ClearAllMappings() {
for (auto [direction, directionMappings] : mAxisDirectionMappings) {
for (auto [id, mapping] : directionMappings) {
mapping->EraseFromConfig();
}
}
mAxisDirectionMappings.clear();
SaveAxisDirectionMappingIdsToConfig();
SetDeadzone(20);
SetNotchSnapAngle(0);
}
void ControllerStick::ClearAllMappingsForDeviceType(PhysicalDeviceType physicalDeviceType) {
std::vector<std::string> mappingIdsToRemove;
for (auto [direction, directionMappings] : mAxisDirectionMappings) {
for (auto [id, mapping] : directionMappings) {
if (mapping->GetPhysicalDeviceType() == physicalDeviceType) {
mapping->EraseFromConfig();
mappingIdsToRemove.push_back(id);
}
}
}
for (auto id : mappingIdsToRemove) {
for (auto [direction, directionMappings] : mAxisDirectionMappings) {
auto it = directionMappings.find(id);
if (it != directionMappings.end()) {
directionMappings.erase(it);
}
}
}
SaveAxisDirectionMappingIdsToConfig();
}
// todo: where should this live?
std::unordered_map<StickIndex, std::string> stickIndexToConfigStickIndexName = { { LEFT_STICK, "LeftStick" },
{ RIGHT_STICK, "RightStick" } };
// todo: where should this live?
std::unordered_map<Direction, std::string> directionToConfigDirectionName = {
{ LEFT, "Left" }, { RIGHT, "Right" }, { UP, "Up" }, { DOWN, "Down" }
};
void ControllerStick::SaveAxisDirectionMappingIdsToConfig() {
for (auto [direction, directionMappings] : mAxisDirectionMappings) {
// todo: this efficently (when we build out cvar array support?)
std::string axisDirectionMappingIdListString = "";
for (auto [id, mapping] : directionMappings) {
axisDirectionMappingIdListString += id;
axisDirectionMappingIdListString += ",";
}
const std::string axisDirectionMappingIdsCvarKey = StringHelper::Sprintf(
CVAR_PREFIX_CONTROLLERS ".Port%d.%s.%sAxisDirectionMappingIds", mPortIndex + 1,
stickIndexToConfigStickIndexName[mStickIndex].c_str(), directionToConfigDirectionName[direction].c_str());
if (axisDirectionMappingIdListString == "") {
CVarClear(axisDirectionMappingIdsCvarKey.c_str());
} else {
CVarSetString(axisDirectionMappingIdsCvarKey.c_str(), axisDirectionMappingIdListString.c_str());
}
}
CVarSave();
}
void ControllerStick::ClearAxisDirectionMappingId(Direction direction, std::string id) {
mAxisDirectionMappings[direction].erase(id);
SaveAxisDirectionMappingIdsToConfig();
}
void ControllerStick::ClearAxisDirectionMapping(Direction direction, std::string id) {
mAxisDirectionMappings[direction][id]->EraseFromConfig();
mAxisDirectionMappings[direction].erase(id);
SaveAxisDirectionMappingIdsToConfig();
}
void ControllerStick::ClearAxisDirectionMapping(Direction direction,
std::shared_ptr<ControllerAxisDirectionMapping> mapping) {
ClearAxisDirectionMapping(direction, mapping->GetAxisDirectionMappingId());
}
void ControllerStick::AddAxisDirectionMapping(Direction direction,
std::shared_ptr<ControllerAxisDirectionMapping> mapping) {
mAxisDirectionMappings[direction][mapping->GetAxisDirectionMappingId()] = mapping;
}
void ControllerStick::AddDefaultMappings(PhysicalDeviceType physicalDeviceType) {
if (physicalDeviceType == PhysicalDeviceType::SDLGamepad) {
for (auto mapping :
AxisDirectionMappingFactory::CreateDefaultSDLAxisDirectionMappings(mPortIndex, mStickIndex)) {
AddAxisDirectionMapping(mapping->GetDirection(), mapping);
}
}
if (physicalDeviceType == PhysicalDeviceType::Keyboard) {
for (auto mapping :
AxisDirectionMappingFactory::CreateDefaultKeyboardAxisDirectionMappings(mPortIndex, mStickIndex)) {
AddAxisDirectionMapping(mapping->GetDirection(), mapping);
}
}
for (auto [direction, directionMappings] : mAxisDirectionMappings) {
for (auto [id, mapping] : directionMappings) {
mapping->SaveToConfig();
}
}
SaveAxisDirectionMappingIdsToConfig();
}
void ControllerStick::LoadAxisDirectionMappingFromConfig(std::string id) {
auto mapping = AxisDirectionMappingFactory::CreateAxisDirectionMappingFromConfig(mPortIndex, mStickIndex, id);
if (mapping == nullptr) {
return;
}
AddAxisDirectionMapping(mapping->GetDirection(), mapping);
}
void ControllerStick::ReloadAllMappingsFromConfig() {
mAxisDirectionMappings.clear();
// todo: this efficently (when we build out cvar array support?)
// i don't expect it to really be a problem with the small number of mappings we have
// for each controller (especially compared to include/exclude locations in rando), and
// the audio editor pattern doesn't work for this because that looks for ids that are either
// hardcoded or provided by an otr file
for (auto direction : { LEFT, RIGHT, UP, DOWN }) {
const std::string axisDirectionMappingIdsCvarKey = StringHelper::Sprintf(
CVAR_PREFIX_CONTROLLERS ".Port%d.%s.%sAxisDirectionMappingIds", mPortIndex + 1,
stickIndexToConfigStickIndexName[mStickIndex].c_str(), directionToConfigDirectionName[direction].c_str());
std::stringstream axisDirectionMappingIdsStringStream(
CVarGetString(axisDirectionMappingIdsCvarKey.c_str(), ""));
std::string axisDirectionMappingIdString;
while (getline(axisDirectionMappingIdsStringStream, axisDirectionMappingIdString, ',')) {
LoadAxisDirectionMappingFromConfig(axisDirectionMappingIdString);
}
}
SetSensitivity(
CVarGetInteger(StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.%s.SensitivityPercentage", mPortIndex + 1,
stickIndexToConfigStickIndexName[mStickIndex].c_str())
.c_str(),
DEFAULT_STICK_SENSITIVITY_PERCENTAGE));
SetDeadzone(
CVarGetInteger(StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.%s.DeadzonePercentage", mPortIndex + 1,
stickIndexToConfigStickIndexName[mStickIndex].c_str())
.c_str(),
DEFAULT_STICK_DEADZONE_PERCENTAGE));
SetNotchSnapAngle(
CVarGetInteger(StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.%s.NotchSnapAngle", mPortIndex + 1,
stickIndexToConfigStickIndexName[mStickIndex].c_str())
.c_str(),
0));
}
double ControllerStick::GetClosestNotch(double angle, double approximationThreshold) {
constexpr auto octagonAngle = M_TAU / 8;
const auto closestNotch = std::round(angle / octagonAngle) * octagonAngle;
const auto distanceToNotch = std::abs(fmod(closestNotch - angle + M_PI, M_TAU) - M_PI);
return distanceToNotch < approximationThreshold / 2 ? closestNotch : angle;
}
float ControllerStick::GetAxisDirectionValue(Direction direction) {
if (mAxisDirectionMappings[direction].size() == 0) {
return 0;
}
if (mAxisDirectionMappings[direction].size() == 1) {
return mAxisDirectionMappings[direction].begin()->second->GetNormalizedAxisDirectionValue();
}
auto maxMapping = std::max_element(
mAxisDirectionMappings[direction].begin(), mAxisDirectionMappings[direction].end(),
[](const std::pair<std::string, std::shared_ptr<ControllerAxisDirectionMapping>> a,
const std::pair<std::string, std::shared_ptr<ControllerAxisDirectionMapping>> b) {
return a.second->GetNormalizedAxisDirectionValue() < b.second->GetNormalizedAxisDirectionValue();
});
return maxMapping->second->GetNormalizedAxisDirectionValue();
}
void ControllerStick::Process(int8_t& x, int8_t& y) {
double sx = GetAxisDirectionValue(RIGHT) - GetAxisDirectionValue(LEFT);
double sy = GetAxisDirectionValue(UP) - GetAxisDirectionValue(DOWN);
double ux = fabs(sx) * mSensitivity;
double uy = fabs(sy) * mSensitivity;
// create scaled circular dead-zone
auto len = sqrt(ux * ux + uy * uy);
if (len < mDeadzone) {
len = 0;
} else if (len > MAX_AXIS_RANGE) {
len = MAX_AXIS_RANGE / len;
} else {
len = (len - mDeadzone) * MAX_AXIS_RANGE / (MAX_AXIS_RANGE - mDeadzone) / len;
}
ux *= len;
uy *= len;
// bound diagonals to an octagonal range {-69 ... +69}
if (ux != 0.0 && uy != 0.0) {
auto slope = uy / ux;
auto edgex = copysign(MAX_AXIS_RANGE / (fabs(slope) + 16.0 / 69.0), ux);
auto edgey = copysign(std::min(fabs(edgex * slope), MAX_AXIS_RANGE / (1.0 / fabs(slope) + 16.0 / 69.0)), y);
edgex = edgey / slope;
auto scale = sqrt(edgex * edgex + edgey * edgey) / MAX_AXIS_RANGE;
ux *= scale;
uy *= scale;
}
// map to virtual notches
const double notchProximityValRadians = mNotchSnapAngle * M_TAU / 360;
const double distance = std::sqrt((ux * ux) + (uy * uy)) / MAX_AXIS_RANGE;
if (distance >= MINIMUM_RADIUS_TO_MAP_NOTCH) {
auto angle = std::atan2(uy, ux) + M_TAU;
auto newAngle = GetClosestNotch(angle, notchProximityValRadians);
ux = std::cos(newAngle) * distance * MAX_AXIS_RANGE;
uy = std::sin(newAngle) * distance * MAX_AXIS_RANGE;
}
// assign back to original sign
x = copysign(ux, sx);
y = copysign(uy, sy);
}
bool ControllerStick::AddOrEditAxisDirectionMappingFromRawPress(Direction direction, std::string id) {
std::shared_ptr<ControllerAxisDirectionMapping> mapping = nullptr;
mUseEventInputToCreateNewMapping = true;
if (mKeyboardScancodeForNewMapping != LUS_KB_UNKNOWN) {
mapping = std::make_shared<KeyboardKeyToAxisDirectionMapping>(mPortIndex, mStickIndex, direction,
mKeyboardScancodeForNewMapping);
} else if (!Context::GetInstance()->GetWindow()->GetGui()->IsMouseOverAnyGuiItem() &&
Context::GetInstance()->GetWindow()->GetGui()->IsMouseOverActivePopup()) {
if (mMouseButtonForNewMapping != LUS_MOUSE_BTN_UNKNOWN) {
mapping = std::make_shared<MouseButtonToAxisDirectionMapping>(mPortIndex, mStickIndex, direction,
mMouseButtonForNewMapping);
} else {
mapping = AxisDirectionMappingFactory::CreateAxisDirectionMappingFromMouseWheelInput(
mPortIndex, mStickIndex, direction);
}
}
if (mapping == nullptr) {
mapping =
AxisDirectionMappingFactory::CreateAxisDirectionMappingFromSDLInput(mPortIndex, mStickIndex, direction);
}
if (mapping == nullptr) {
return false;
}
mKeyboardScancodeForNewMapping = LUS_KB_UNKNOWN;
mMouseButtonForNewMapping = LUS_MOUSE_BTN_UNKNOWN;
mUseEventInputToCreateNewMapping = false;
if (id != "") {
ClearAxisDirectionMapping(direction, id);
}
AddAxisDirectionMapping(direction, mapping);
mapping->SaveToConfig();
SaveAxisDirectionMappingIdsToConfig();
const std::string hasConfigCvarKey =
StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.HasConfig", mPortIndex + 1);
CVarSetInteger(hasConfigCvarKey.c_str(), true);
CVarSave();
return true;
}
std::shared_ptr<ControllerAxisDirectionMapping> ControllerStick::GetAxisDirectionMappingById(Direction direction,
std::string id) {
if (!mAxisDirectionMappings[direction].contains(id)) {
return nullptr;
}
return mAxisDirectionMappings[direction][id];
}
std::unordered_map<std::string, std::shared_ptr<ControllerAxisDirectionMapping>>
ControllerStick::GetAllAxisDirectionMappingByDirection(Direction direction) {
return mAxisDirectionMappings[direction];
}
void ControllerStick::UpdatePad(int8_t& x, int8_t& y) {
Process(x, y);
}
bool ControllerStick::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode) {
if (mUseEventInputToCreateNewMapping) {
if (eventType == LUS_KB_EVENT_KEY_DOWN) {
mKeyboardScancodeForNewMapping = scancode;
return true;
} else {
mKeyboardScancodeForNewMapping = LUS_KB_UNKNOWN;
}
}
bool result = false;
for (auto [direction, mappings] : mAxisDirectionMappings) {
for (auto [id, mapping] : mappings) {
if (mapping->GetMappingType() == MAPPING_TYPE_KEYBOARD) {
std::shared_ptr<KeyboardKeyToAxisDirectionMapping> ktoadMapping =
std::dynamic_pointer_cast<KeyboardKeyToAxisDirectionMapping>(mapping);
if (ktoadMapping != nullptr) {
result = result || ktoadMapping->ProcessKeyboardEvent(eventType, scancode);
}
}
}
}
return result;
}
bool ControllerStick::ProcessMouseButtonEvent(bool isPressed, MouseBtn button) {
if (mUseEventInputToCreateNewMapping) {
if (isPressed) {
mMouseButtonForNewMapping = button;
return true;
} else {
mMouseButtonForNewMapping = LUS_MOUSE_BTN_UNKNOWN;
}
}
bool result = false;
for (auto [direction, mappings] : mAxisDirectionMappings) {
for (auto [id, mapping] : mappings) {
if (mapping->GetMappingType() == MAPPING_TYPE_MOUSE) {
std::shared_ptr<MouseButtonToAxisDirectionMapping> mtoadMapping =
std::dynamic_pointer_cast<MouseButtonToAxisDirectionMapping>(mapping);
if (mtoadMapping != nullptr) {
result = result || mtoadMapping->ProcessMouseButtonEvent(isPressed, button);
}
}
}
}
return result;
}
void ControllerStick::SetSensitivity(uint8_t sensitivityPercentage) {
mSensitivityPercentage = sensitivityPercentage;
mSensitivity = sensitivityPercentage / 100.0f;
CVarSetInteger(StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.%s.SensitivityPercentage", mPortIndex + 1,
stickIndexToConfigStickIndexName[mStickIndex].c_str())
.c_str(),
mSensitivityPercentage);
CVarSave();
}
void ControllerStick::ResetSensitivityToDefault() {
SetSensitivity(DEFAULT_STICK_SENSITIVITY_PERCENTAGE);
}
uint8_t ControllerStick::GetSensitivityPercentage() {
return mSensitivityPercentage;
}
bool ControllerStick::SensitivityIsDefault() {
return mSensitivityPercentage == DEFAULT_STICK_SENSITIVITY_PERCENTAGE;
}
void ControllerStick::SetDeadzone(uint8_t deadzonePercentage) {
mDeadzonePercentage = deadzonePercentage;
mDeadzone = MAX_AXIS_RANGE * (deadzonePercentage / 100.0f);
CVarSetInteger(StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.%s.DeadzonePercentage", mPortIndex + 1,
stickIndexToConfigStickIndexName[mStickIndex].c_str())
.c_str(),
mDeadzonePercentage);
CVarSave();
}
void ControllerStick::ResetDeadzoneToDefault() {
SetDeadzone(DEFAULT_STICK_DEADZONE_PERCENTAGE);
}
uint8_t ControllerStick::GetDeadzonePercentage() {
return mDeadzonePercentage;
}
bool ControllerStick::DeadzoneIsDefault() {
return mDeadzonePercentage == DEFAULT_STICK_DEADZONE_PERCENTAGE;
}
void ControllerStick::SetNotchSnapAngle(uint8_t notchSnapAngle) {
mNotchSnapAngle = notchSnapAngle;
CVarSetInteger(StringHelper::Sprintf(CVAR_PREFIX_CONTROLLERS ".Port%d.%s.NotchSnapAngle", mPortIndex + 1,
stickIndexToConfigStickIndexName[mStickIndex].c_str())
.c_str(),
mNotchSnapAngle);
CVarSave();
}
void ControllerStick::ResetNotchSnapAngleToDefault() {
SetNotchSnapAngle(DEFAULT_NOTCH_SNAP_ANGLE);
}
uint8_t ControllerStick::GetNotchSnapAngle() {
return mNotchSnapAngle;
}
bool ControllerStick::NotchSnapAngleIsDefault() {
return mNotchSnapAngle == DEFAULT_NOTCH_SNAP_ANGLE;
}
bool ControllerStick::HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType) {
return std::any_of(mAxisDirectionMappings.begin(), mAxisDirectionMappings.end(),
[physicalDeviceType](const auto& directionMappings) {
return std::any_of(directionMappings.second.begin(), directionMappings.second.end(),
[physicalDeviceType](const auto& mappingPair) {
return mappingPair.second->GetPhysicalDeviceType() ==
physicalDeviceType;
});
});
}
std::unordered_map<Direction, std::unordered_map<std::string, std::shared_ptr<ControllerAxisDirectionMapping>>>
ControllerStick::GetAllAxisDirectionMappings() {
return mAxisDirectionMappings;
}
StickIndex ControllerStick::GetStickIndex() {
return mStickIndex;
}
} // namespace Ship

View File

@@ -0,0 +1,83 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h"
#include <cstdint>
#include <memory>
#include <unordered_map>
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
#define DEFAULT_STICK_SENSITIVITY_PERCENTAGE 100
#define DEFAULT_STICK_DEADZONE_PERCENTAGE 20
#define DEFAULT_NOTCH_SNAP_ANGLE 0
class ControllerStick {
public:
ControllerStick(uint8_t portIndex, StickIndex stickIndex);
~ControllerStick();
void ReloadAllMappingsFromConfig();
void AddDefaultMappings(PhysicalDeviceType physicalDeviceType);
void ClearAllMappings();
void ClearAllMappingsForDeviceType(PhysicalDeviceType physicalDeviceType);
void UpdatePad(int8_t& x, int8_t& y);
std::shared_ptr<ControllerAxisDirectionMapping> GetAxisDirectionMappingById(Direction direction, std::string id);
std::unordered_map<Direction, std::unordered_map<std::string, std::shared_ptr<ControllerAxisDirectionMapping>>>
GetAllAxisDirectionMappings();
std::unordered_map<std::string, std::shared_ptr<ControllerAxisDirectionMapping>>
GetAllAxisDirectionMappingByDirection(Direction direction);
void AddAxisDirectionMapping(Direction direction, std::shared_ptr<ControllerAxisDirectionMapping> mapping);
void ClearAxisDirectionMappingId(Direction direction, std::string id);
void ClearAxisDirectionMapping(Direction direction, std::string id);
void ClearAxisDirectionMapping(Direction direction, std::shared_ptr<ControllerAxisDirectionMapping> mapping);
void SaveAxisDirectionMappingIdsToConfig();
bool AddOrEditAxisDirectionMappingFromRawPress(Direction direction, std::string id);
void Process(int8_t& x, int8_t& y);
void ResetSensitivityToDefault();
void SetSensitivity(uint8_t sensitivityPercentage);
uint8_t GetSensitivityPercentage();
bool SensitivityIsDefault();
void ResetDeadzoneToDefault();
void SetDeadzone(uint8_t deadzonePercentage);
uint8_t GetDeadzonePercentage();
bool DeadzoneIsDefault();
void ResetNotchSnapAngleToDefault();
void SetNotchSnapAngle(uint8_t notchSnapAngle);
uint8_t GetNotchSnapAngle();
bool NotchSnapAngleIsDefault();
bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
bool ProcessMouseButtonEvent(bool isPressed, Ship::MouseBtn button);
bool HasMappingsForPhysicalDeviceType(PhysicalDeviceType physicalDeviceType);
StickIndex GetStickIndex();
private:
double GetClosestNotch(double angle, double approximationThreshold);
void LoadAxisDirectionMappingFromConfig(std::string id);
float GetAxisDirectionValue(Direction direction);
uint8_t mPortIndex;
StickIndex mStickIndex;
uint8_t mSensitivityPercentage;
float mSensitivity;
// TODO: handle deadzones separately for X and Y?
uint8_t mDeadzonePercentage;
float mDeadzone;
uint8_t mNotchSnapAngle;
std::unordered_map<Direction, std::unordered_map<std::string, std::shared_ptr<ControllerAxisDirectionMapping>>>
mAxisDirectionMappings;
bool mUseEventInputToCreateNewMapping;
KbScancode mKeyboardScancodeForNewMapping;
MouseBtn mMouseButtonForNewMapping;
};
} // namespace Ship

View File

@@ -0,0 +1,27 @@
#include "ControllerAxisDirectionMapping.h"
#include <random>
#include <sstream>
namespace Ship {
ControllerAxisDirectionMapping::ControllerAxisDirectionMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex,
StickIndex stickIndex, Direction direction)
: ControllerInputMapping(physicalDeviceType), mPortIndex(portIndex), mStickIndex(stickIndex),
mDirection(direction) {
}
ControllerAxisDirectionMapping::~ControllerAxisDirectionMapping() {
}
int8_t ControllerAxisDirectionMapping::GetMappingType() {
return MAPPING_TYPE_UNKNOWN;
}
Direction ControllerAxisDirectionMapping::GetDirection() {
return mDirection;
}
void ControllerAxisDirectionMapping::SetPortIndex(uint8_t portIndex) {
mPortIndex = portIndex;
}
} // namespace Ship

View File

@@ -0,0 +1,34 @@
#pragma once
#include <cstdint>
#include <string>
#include "ControllerInputMapping.h"
#define MAX_AXIS_RANGE 85.0f
namespace Ship {
enum StickIndex { LEFT_STICK, RIGHT_STICK };
enum Direction { LEFT, RIGHT, UP, DOWN };
class ControllerAxisDirectionMapping : virtual public ControllerInputMapping {
public:
ControllerAxisDirectionMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex, StickIndex stickIndex,
Direction direction);
~ControllerAxisDirectionMapping();
virtual float GetNormalizedAxisDirectionValue() = 0;
virtual int8_t GetMappingType();
virtual std::string GetAxisDirectionMappingId() = 0;
virtual void SaveToConfig() = 0;
virtual void EraseFromConfig() = 0;
Direction GetDirection();
void SetPortIndex(uint8_t portIndex);
protected:
uint8_t mPortIndex;
StickIndex mStickIndex;
Direction mDirection;
};
} // namespace Ship

View File

@@ -0,0 +1,28 @@
#include "ControllerButtonMapping.h"
#include <random>
#include <sstream>
#include "public/bridge/consolevariablebridge.h"
namespace Ship {
ControllerButtonMapping::ControllerButtonMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex,
CONTROLLERBUTTONS_T bitmask)
: ControllerInputMapping(physicalDeviceType), mPortIndex(portIndex), mBitmask(bitmask) {
}
ControllerButtonMapping::~ControllerButtonMapping() {
}
CONTROLLERBUTTONS_T ControllerButtonMapping::GetBitmask() {
return mBitmask;
}
int8_t ControllerButtonMapping::GetMappingType() {
return MAPPING_TYPE_UNKNOWN;
}
void ControllerButtonMapping::SetPortIndex(uint8_t portIndex) {
mPortIndex = portIndex;
}
} // namespace Ship

View File

@@ -0,0 +1,33 @@
#pragma once
#include <cstdint>
#include <string>
#include "ControllerInputMapping.h"
namespace Ship {
#ifndef CONTROLLERBUTTONS_T
#define CONTROLLERBUTTONS_T uint16_t
#endif
class ControllerButtonMapping : virtual public ControllerInputMapping {
public:
ControllerButtonMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex, CONTROLLERBUTTONS_T bitmask);
~ControllerButtonMapping();
virtual std::string GetButtonMappingId() = 0;
CONTROLLERBUTTONS_T GetBitmask();
virtual void UpdatePad(CONTROLLERBUTTONS_T& padButtons) = 0;
virtual int8_t GetMappingType();
void SetPortIndex(uint8_t portIndex);
virtual void SaveToConfig() = 0;
virtual void EraseFromConfig() = 0;
protected:
uint8_t mPortIndex;
CONTROLLERBUTTONS_T mBitmask;
};
} // namespace Ship

View File

@@ -0,0 +1,174 @@
#include "ControllerDefaultMappings.h"
#include "libultraship/libultra/controller.h"
namespace Ship {
ControllerDefaultMappings::ControllerDefaultMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<KbScancode>> defaultKeyboardKeyToButtonMappings,
std::unordered_map<StickIndex, std::vector<std::pair<Direction, KbScancode>>>
defaultKeyboardKeyToAxisDirectionMappings,
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<SDL_GameControllerButton>>
defaultSDLButtonToButtonMappings,
std::unordered_map<StickIndex, std::vector<std::pair<Direction, SDL_GameControllerButton>>>
defaultSDLButtonToAxisDirectionMappings,
std::unordered_map<CONTROLLERBUTTONS_T, std::vector<std::pair<SDL_GameControllerAxis, int32_t>>>
defaultSDLAxisDirectionToButtonMappings,
std::unordered_map<StickIndex, std::vector<std::pair<Direction, std::pair<SDL_GameControllerAxis, int32_t>>>>
defaultSDLAxisDirectionToAxisDirectionMappings) {
SetDefaultKeyboardKeyToButtonMappings(defaultKeyboardKeyToButtonMappings);
SetDefaultKeyboardKeyToAxisDirectionMappings(defaultKeyboardKeyToAxisDirectionMappings);
SetDefaultSDLButtonToButtonMappings(defaultSDLButtonToButtonMappings);
SetDefaultSDLButtonToAxisDirectionMappings(defaultSDLButtonToAxisDirectionMappings);
SetDefaultSDLAxisDirectionToButtonMappings(defaultSDLAxisDirectionToButtonMappings);
SetDefaultSDLAxisDirectionToAxisDirectionMappings(defaultSDLAxisDirectionToAxisDirectionMappings);
}
ControllerDefaultMappings::ControllerDefaultMappings()
: ControllerDefaultMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<KbScancode>>(),
std::unordered_map<StickIndex, std::vector<std::pair<Direction, KbScancode>>>(),
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<SDL_GameControllerButton>>(),
std::unordered_map<StickIndex, std::vector<std::pair<Direction, SDL_GameControllerButton>>>(),
std::unordered_map<CONTROLLERBUTTONS_T, std::vector<std::pair<SDL_GameControllerAxis, int32_t>>>(),
std::unordered_map<StickIndex,
std::vector<std::pair<Direction, std::pair<SDL_GameControllerAxis, int32_t>>>>()) {
}
ControllerDefaultMappings::~ControllerDefaultMappings() {
}
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<KbScancode>>
ControllerDefaultMappings::GetDefaultKeyboardKeyToButtonMappings() {
return mDefaultKeyboardKeyToButtonMappings;
}
void ControllerDefaultMappings::SetDefaultKeyboardKeyToButtonMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<KbScancode>> defaultKeyboardKeyToButtonMappings) {
if (!defaultKeyboardKeyToButtonMappings.empty()) {
mDefaultKeyboardKeyToButtonMappings = defaultKeyboardKeyToButtonMappings;
return;
}
mDefaultKeyboardKeyToButtonMappings[BTN_A] = { KbScancode::LUS_KB_X };
mDefaultKeyboardKeyToButtonMappings[BTN_B] = { KbScancode::LUS_KB_C };
mDefaultKeyboardKeyToButtonMappings[BTN_L] = { KbScancode::LUS_KB_E };
mDefaultKeyboardKeyToButtonMappings[BTN_R] = { KbScancode::LUS_KB_R };
mDefaultKeyboardKeyToButtonMappings[BTN_Z] = { KbScancode::LUS_KB_Z };
mDefaultKeyboardKeyToButtonMappings[BTN_START] = { KbScancode::LUS_KB_SPACE };
mDefaultKeyboardKeyToButtonMappings[BTN_CUP] = { KbScancode::LUS_KB_ARROWKEY_UP };
mDefaultKeyboardKeyToButtonMappings[BTN_CDOWN] = { KbScancode::LUS_KB_ARROWKEY_DOWN };
mDefaultKeyboardKeyToButtonMappings[BTN_CLEFT] = { KbScancode::LUS_KB_ARROWKEY_LEFT };
mDefaultKeyboardKeyToButtonMappings[BTN_CRIGHT] = { KbScancode::LUS_KB_ARROWKEY_RIGHT };
mDefaultKeyboardKeyToButtonMappings[BTN_DUP] = { KbScancode::LUS_KB_T };
mDefaultKeyboardKeyToButtonMappings[BTN_DDOWN] = { KbScancode::LUS_KB_G };
mDefaultKeyboardKeyToButtonMappings[BTN_DLEFT] = { KbScancode::LUS_KB_F };
mDefaultKeyboardKeyToButtonMappings[BTN_DRIGHT] = { KbScancode::LUS_KB_H };
}
std::unordered_map<StickIndex, std::vector<std::pair<Direction, KbScancode>>>
ControllerDefaultMappings::GetDefaultKeyboardKeyToAxisDirectionMappings() {
return mDefaultKeyboardKeyToAxisDirectionMappings;
}
void ControllerDefaultMappings::SetDefaultKeyboardKeyToAxisDirectionMappings(
std::unordered_map<StickIndex, std::vector<std::pair<Direction, KbScancode>>>
defaultKeyboardKeyToAxisDirectionMappings) {
if (!defaultKeyboardKeyToAxisDirectionMappings.empty()) {
mDefaultKeyboardKeyToAxisDirectionMappings = defaultKeyboardKeyToAxisDirectionMappings;
return;
}
mDefaultKeyboardKeyToAxisDirectionMappings[LEFT_STICK] = { { LEFT, KbScancode::LUS_KB_A },
{ RIGHT, KbScancode::LUS_KB_D },
{ UP, KbScancode::LUS_KB_W },
{ DOWN, KbScancode::LUS_KB_S } };
}
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<SDL_GameControllerButton>>
ControllerDefaultMappings::GetDefaultSDLButtonToButtonMappings() {
return mDefaultSDLButtonToButtonMappings;
}
void ControllerDefaultMappings::SetDefaultSDLButtonToButtonMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<SDL_GameControllerButton>>
defaultSDLButtonToButtonMappings) {
if (!defaultSDLButtonToButtonMappings.empty()) {
mDefaultSDLButtonToButtonMappings = defaultSDLButtonToButtonMappings;
return;
}
mDefaultSDLButtonToButtonMappings[BTN_A] = { SDL_CONTROLLER_BUTTON_A };
mDefaultSDLButtonToButtonMappings[BTN_B] = { SDL_CONTROLLER_BUTTON_B };
mDefaultSDLButtonToButtonMappings[BTN_L] = { SDL_CONTROLLER_BUTTON_LEFTSHOULDER };
mDefaultSDLButtonToButtonMappings[BTN_START] = { SDL_CONTROLLER_BUTTON_START };
mDefaultSDLButtonToButtonMappings[BTN_DUP] = { SDL_CONTROLLER_BUTTON_DPAD_UP };
mDefaultSDLButtonToButtonMappings[BTN_DDOWN] = { SDL_CONTROLLER_BUTTON_DPAD_DOWN };
mDefaultSDLButtonToButtonMappings[BTN_DLEFT] = { SDL_CONTROLLER_BUTTON_DPAD_LEFT };
mDefaultSDLButtonToButtonMappings[BTN_DRIGHT] = { SDL_CONTROLLER_BUTTON_DPAD_RIGHT };
}
std::unordered_map<StickIndex, std::vector<std::pair<Direction, SDL_GameControllerButton>>>
ControllerDefaultMappings::GetDefaultSDLButtonToAxisDirectionMappings() {
return mDefaultSDLButtonToAxisDirectionMappings;
}
void ControllerDefaultMappings::SetDefaultSDLButtonToAxisDirectionMappings(
std::unordered_map<StickIndex, std::vector<std::pair<Direction, SDL_GameControllerButton>>>
defaultSDLButtonToAxisDirectionMappings) {
if (!defaultSDLButtonToAxisDirectionMappings.empty()) {
mDefaultSDLButtonToAxisDirectionMappings = defaultSDLButtonToAxisDirectionMappings;
return;
}
}
std::unordered_map<CONTROLLERBUTTONS_T, std::vector<std::pair<SDL_GameControllerAxis, int32_t>>>
ControllerDefaultMappings::GetDefaultSDLAxisDirectionToButtonMappings() {
return mDefaultSDLAxisDirectionToButtonMappings;
}
void ControllerDefaultMappings::SetDefaultSDLAxisDirectionToButtonMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::vector<std::pair<SDL_GameControllerAxis, int32_t>>>
defaultSDLAxisDirectionToButtonMappings) {
if (!defaultSDLAxisDirectionToButtonMappings.empty()) {
mDefaultSDLAxisDirectionToButtonMappings = defaultSDLAxisDirectionToButtonMappings;
return;
}
mDefaultSDLAxisDirectionToButtonMappings[BTN_R] = { { SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 1 } };
mDefaultSDLAxisDirectionToButtonMappings[BTN_Z] = { { SDL_CONTROLLER_AXIS_TRIGGERLEFT, 1 } };
mDefaultSDLAxisDirectionToButtonMappings[BTN_CUP] = { { SDL_CONTROLLER_AXIS_RIGHTY, -1 } };
mDefaultSDLAxisDirectionToButtonMappings[BTN_CDOWN] = { { SDL_CONTROLLER_AXIS_RIGHTY, 1 } };
mDefaultSDLAxisDirectionToButtonMappings[BTN_CLEFT] = { { SDL_CONTROLLER_AXIS_RIGHTX, -1 } };
mDefaultSDLAxisDirectionToButtonMappings[BTN_CRIGHT] = { { SDL_CONTROLLER_AXIS_RIGHTX, 1 } };
}
std::unordered_map<StickIndex, std::vector<std::pair<Direction, std::pair<SDL_GameControllerAxis, int32_t>>>>
ControllerDefaultMappings::GetDefaultSDLAxisDirectionToAxisDirectionMappings() {
return mDefaultSDLAxisDirectionToAxisDirectionMappings;
}
void ControllerDefaultMappings::SetDefaultSDLAxisDirectionToAxisDirectionMappings(
std::unordered_map<StickIndex, std::vector<std::pair<Direction, std::pair<SDL_GameControllerAxis, int32_t>>>>
defaultSDLAxisDirectionToAxisDirectionMappings) {
if (!defaultSDLAxisDirectionToAxisDirectionMappings.empty()) {
mDefaultSDLAxisDirectionToAxisDirectionMappings = defaultSDLAxisDirectionToAxisDirectionMappings;
return;
}
mDefaultSDLAxisDirectionToAxisDirectionMappings[LEFT_STICK] = {
{ LEFT, { SDL_CONTROLLER_AXIS_LEFTX, -1 } },
{ RIGHT, { SDL_CONTROLLER_AXIS_LEFTX, 1 } },
{ UP, { SDL_CONTROLLER_AXIS_LEFTY, -1 } },
{ DOWN, { SDL_CONTROLLER_AXIS_LEFTY, 1 } },
};
mDefaultSDLAxisDirectionToAxisDirectionMappings[RIGHT_STICK] = {
{ LEFT, { SDL_CONTROLLER_AXIS_RIGHTX, -1 } },
{ RIGHT, { SDL_CONTROLLER_AXIS_RIGHTX, 1 } },
{ UP, { SDL_CONTROLLER_AXIS_RIGHTY, -1 } },
{ DOWN, { SDL_CONTROLLER_AXIS_RIGHTY, 1 } },
};
}
} // namespace Ship

View File

@@ -0,0 +1,84 @@
#pragma once
#include <cstdint>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <SDL2/SDL.h>
#include "ControllerAxisDirectionMapping.h"
#ifndef CONTROLLERBUTTONS_T
#define CONTROLLERBUTTONS_T uint16_t
#endif
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
class ControllerDefaultMappings {
public:
ControllerDefaultMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<KbScancode>> defaultKeyboardKeyToButtonMappings,
std::unordered_map<StickIndex, std::vector<std::pair<Direction, KbScancode>>>
defaultKeyboardKeyToAxisDirectionMappings,
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<SDL_GameControllerButton>>
defaultSDLButtonToButtonMappings,
std::unordered_map<StickIndex, std::vector<std::pair<Direction, SDL_GameControllerButton>>>
defaultSDLButtonToAxisDirectionMappings,
std::unordered_map<CONTROLLERBUTTONS_T, std::vector<std::pair<SDL_GameControllerAxis, int32_t>>>
defaultSDLAxisDirectionToButtonMappings,
std::unordered_map<StickIndex, std::vector<std::pair<Direction, std::pair<SDL_GameControllerAxis, int32_t>>>>
defaultSDLAxisDirectionToAxisDirectionMappings);
ControllerDefaultMappings();
~ControllerDefaultMappings();
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<KbScancode>> GetDefaultKeyboardKeyToButtonMappings();
std::unordered_map<StickIndex, std::vector<std::pair<Direction, KbScancode>>>
GetDefaultKeyboardKeyToAxisDirectionMappings();
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<SDL_GameControllerButton>>
GetDefaultSDLButtonToButtonMappings();
std::unordered_map<StickIndex, std::vector<std::pair<Direction, SDL_GameControllerButton>>>
GetDefaultSDLButtonToAxisDirectionMappings();
std::unordered_map<CONTROLLERBUTTONS_T, std::vector<std::pair<SDL_GameControllerAxis, int32_t>>>
GetDefaultSDLAxisDirectionToButtonMappings();
std::unordered_map<StickIndex, std::vector<std::pair<Direction, std::pair<SDL_GameControllerAxis, int32_t>>>>
GetDefaultSDLAxisDirectionToAxisDirectionMappings();
private:
void SetDefaultKeyboardKeyToButtonMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<KbScancode>> defaultKeyboardKeyToButtonMappings);
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<KbScancode>> mDefaultKeyboardKeyToButtonMappings;
void SetDefaultKeyboardKeyToAxisDirectionMappings(
std::unordered_map<StickIndex, std::vector<std::pair<Direction, KbScancode>>>
defaultKeyboardKeyToAxisDirectionMappings);
std::unordered_map<StickIndex, std::vector<std::pair<Direction, KbScancode>>>
mDefaultKeyboardKeyToAxisDirectionMappings;
void SetDefaultSDLButtonToButtonMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<SDL_GameControllerButton>>
defaultSDLButtonToButtonMappings);
std::unordered_map<CONTROLLERBUTTONS_T, std::unordered_set<SDL_GameControllerButton>>
mDefaultSDLButtonToButtonMappings;
void SetDefaultSDLButtonToAxisDirectionMappings(
std::unordered_map<StickIndex, std::vector<std::pair<Direction, SDL_GameControllerButton>>>
defaultSDLButtonToAxisDirectionMappings);
std::unordered_map<StickIndex, std::vector<std::pair<Direction, SDL_GameControllerButton>>>
mDefaultSDLButtonToAxisDirectionMappings;
void SetDefaultSDLAxisDirectionToButtonMappings(
std::unordered_map<CONTROLLERBUTTONS_T, std::vector<std::pair<SDL_GameControllerAxis, int32_t>>>
defaultSDLAxisDirectionToButtonMappings);
std::unordered_map<CONTROLLERBUTTONS_T, std::vector<std::pair<SDL_GameControllerAxis, int32_t>>>
mDefaultSDLAxisDirectionToButtonMappings;
void SetDefaultSDLAxisDirectionToAxisDirectionMappings(
std::unordered_map<StickIndex, std::vector<std::pair<Direction, std::pair<SDL_GameControllerAxis, int32_t>>>>
defaultSDLAxisDirectionToAxisDirectionMappings);
std::unordered_map<StickIndex, std::vector<std::pair<Direction, std::pair<SDL_GameControllerAxis, int32_t>>>>
mDefaultSDLAxisDirectionToAxisDirectionMappings;
};
} // namespace Ship

View File

@@ -0,0 +1,38 @@
#include "ControllerGyroMapping.h"
namespace Ship {
ControllerGyroMapping::ControllerGyroMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex,
float sensitivity)
: ControllerInputMapping(physicalDeviceType), mPortIndex(portIndex), mSensitivity(sensitivity) {
mSensitivityPercent = mSensitivity * 100;
}
ControllerGyroMapping::~ControllerGyroMapping() {
}
void ControllerGyroMapping::SetSensitivity(uint8_t sensitivityPercent) {
mSensitivityPercent = sensitivityPercent;
mSensitivity = sensitivityPercent / 100.0f;
SaveToConfig();
}
uint8_t ControllerGyroMapping::GetSensitivityPercent() {
return mSensitivityPercent;
}
float ControllerGyroMapping::GetSensitivity() {
return mSensitivity;
}
void ControllerGyroMapping::ResetSensitivityToDefault() {
SetSensitivity(GYRO_SENSITIVITY_DEFAULT);
}
bool ControllerGyroMapping::SensitivityIsDefault() {
return mSensitivityPercent == GYRO_SENSITIVITY_DEFAULT;
}
void ControllerGyroMapping::SetPortIndex(uint8_t portIndex) {
mPortIndex = portIndex;
}
} // namespace Ship

View File

@@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <string>
#include "ControllerInputMapping.h"
namespace Ship {
#define GYRO_SENSITIVITY_DEFAULT 100
class ControllerGyroMapping : virtual public ControllerInputMapping {
public:
ControllerGyroMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex, float sensitivity);
~ControllerGyroMapping();
virtual void UpdatePad(float& x, float& y) = 0;
virtual void SaveToConfig() = 0;
virtual void EraseFromConfig() = 0;
virtual void Recalibrate() = 0;
virtual std::string GetGyroMappingId() = 0;
float GetSensitivity();
uint8_t GetSensitivityPercent();
void SetSensitivity(uint8_t sensitivityPercent);
void ResetSensitivityToDefault();
bool SensitivityIsDefault();
void SetPortIndex(uint8_t portIndex);
protected:
uint8_t mPortIndex;
uint8_t mSensitivityPercent;
float mSensitivity;
};
} // namespace Ship

View File

@@ -0,0 +1,14 @@
#include "ControllerInputMapping.h"
namespace Ship {
ControllerInputMapping::ControllerInputMapping(PhysicalDeviceType physicalDeviceType)
: ControllerMapping(physicalDeviceType) {
}
ControllerInputMapping::~ControllerInputMapping() {
}
std::string ControllerInputMapping::GetPhysicalInputName() {
return "Unknown";
}
} // namespace Ship

View File

@@ -0,0 +1,14 @@
#pragma once
#include <string>
#include "ControllerMapping.h"
namespace Ship {
class ControllerInputMapping : public ControllerMapping {
public:
ControllerInputMapping(PhysicalDeviceType physicalDeviceType);
~ControllerInputMapping();
virtual std::string GetPhysicalInputName();
};
} // namespace Ship

View File

@@ -0,0 +1,42 @@
#include "ControllerLEDMapping.h"
namespace Ship {
ControllerLEDMapping::ControllerLEDMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex,
uint8_t colorSource, Color_RGB8 savedColor)
: ControllerMapping(physicalDeviceType), mPortIndex(portIndex), mColorSource(colorSource), mSavedColor(savedColor) {
}
ControllerLEDMapping::~ControllerLEDMapping() {
}
void ControllerLEDMapping::SetColorSource(uint8_t colorSource) {
mColorSource = colorSource;
if (mColorSource == LED_COLOR_SOURCE_OFF) {
SetLEDColor(Color_RGB8({ 0, 0, 0 }));
}
if (mColorSource == LED_COLOR_SOURCE_SET) {
SetLEDColor(mSavedColor);
}
SaveToConfig();
}
uint8_t ControllerLEDMapping::GetColorSource() {
return mColorSource;
}
void ControllerLEDMapping::SetSavedColor(Color_RGB8 colorToSave) {
mSavedColor = colorToSave;
if (mColorSource == LED_COLOR_SOURCE_SET) {
SetLEDColor(mSavedColor);
}
SaveToConfig();
}
Color_RGB8 ControllerLEDMapping::GetSavedColor() {
return mSavedColor;
}
void ControllerLEDMapping::SetPortIndex(uint8_t portIndex) {
mPortIndex = portIndex;
}
} // namespace Ship

View File

@@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#include <string>
#include "ControllerMapping.h"
#include "libultraship/color.h"
namespace Ship {
#define LED_COLOR_SOURCE_OFF 0
#define LED_COLOR_SOURCE_SET 1
#define LED_COLOR_SOURCE_GAME 2
class ControllerLEDMapping : public ControllerMapping {
public:
ControllerLEDMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex, uint8_t colorSource,
Color_RGB8 savedColor);
~ControllerLEDMapping();
void SetColorSource(uint8_t colorSource);
uint8_t GetColorSource();
virtual void SetLEDColor(Color_RGB8 color) = 0;
void SetSavedColor(Color_RGB8 colorToSave);
Color_RGB8 GetSavedColor();
void SetPortIndex(uint8_t portIndex);
virtual std::string GetLEDMappingId() = 0;
virtual void SaveToConfig() = 0;
virtual void EraseFromConfig() = 0;
protected:
uint8_t mPortIndex;
uint8_t mColorSource;
Color_RGB8 mSavedColor;
};
} // namespace Ship

View File

@@ -0,0 +1,17 @@
#include "ControllerMapping.h"
namespace Ship {
ControllerMapping::ControllerMapping(PhysicalDeviceType physicalDeviceType) : mPhysicalDeviceType(physicalDeviceType) {
}
ControllerMapping::~ControllerMapping() {
}
std::string ControllerMapping::GetPhysicalDeviceName() {
return "Unknown";
}
PhysicalDeviceType ControllerMapping::GetPhysicalDeviceType() {
return mPhysicalDeviceType;
}
} // namespace Ship

View File

@@ -0,0 +1,23 @@
#pragma once
#include <string>
#include "controller/physicaldevice/PhysicalDeviceType.h"
namespace Ship {
#define MAPPING_TYPE_UNKNOWN -1
#define MAPPING_TYPE_GAMEPAD 0
#define MAPPING_TYPE_KEYBOARD 1
#define MAPPING_TYPE_MOUSE 2
class ControllerMapping {
public:
ControllerMapping(PhysicalDeviceType physicalDeviceType);
~ControllerMapping();
virtual std::string GetPhysicalDeviceName();
PhysicalDeviceType GetPhysicalDeviceType();
protected:
PhysicalDeviceType mPhysicalDeviceType;
};
} // namespace Ship

View File

@@ -0,0 +1,50 @@
#include "ControllerRumbleMapping.h"
namespace Ship {
ControllerRumbleMapping::ControllerRumbleMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex,
uint8_t lowFrequencyIntensityPercentage,
uint8_t highFrequencyIntensityPercentage)
: ControllerMapping(physicalDeviceType), mPortIndex(portIndex),
mLowFrequencyIntensityPercentage(lowFrequencyIntensityPercentage),
mHighFrequencyIntensityPercentage(highFrequencyIntensityPercentage) {
}
ControllerRumbleMapping::~ControllerRumbleMapping() {
}
void ControllerRumbleMapping::SetLowFrequencyIntensity(uint8_t intensityPercentage) {
mLowFrequencyIntensityPercentage = intensityPercentage;
}
void ControllerRumbleMapping::SetHighFrequencyIntensity(uint8_t intensityPercentage) {
mHighFrequencyIntensityPercentage = intensityPercentage;
}
void ControllerRumbleMapping::ResetLowFrequencyIntensityToDefault() {
SetLowFrequencyIntensity(DEFAULT_LOW_FREQUENCY_RUMBLE_PERCENTAGE);
}
void ControllerRumbleMapping::ResetHighFrequencyIntensityToDefault() {
SetHighFrequencyIntensity(DEFAULT_HIGH_FREQUENCY_RUMBLE_PERCENTAGE);
}
uint8_t ControllerRumbleMapping::GetLowFrequencyIntensityPercentage() {
return mLowFrequencyIntensityPercentage;
}
uint8_t ControllerRumbleMapping::GetHighFrequencyIntensityPercentage() {
return mHighFrequencyIntensityPercentage;
}
bool ControllerRumbleMapping::HighFrequencyIntensityIsDefault() {
return mHighFrequencyIntensityPercentage == DEFAULT_HIGH_FREQUENCY_RUMBLE_PERCENTAGE;
}
bool ControllerRumbleMapping::LowFrequencyIntensityIsDefault() {
return mLowFrequencyIntensityPercentage == DEFAULT_LOW_FREQUENCY_RUMBLE_PERCENTAGE;
}
void ControllerRumbleMapping::SetPortIndex(uint8_t portIndex) {
mPortIndex = portIndex;
}
} // namespace Ship

View File

@@ -0,0 +1,40 @@
#pragma once
#include <cstdint>
#include <string>
#include "ControllerMapping.h"
namespace Ship {
#define DEFAULT_HIGH_FREQUENCY_RUMBLE_PERCENTAGE 50
#define DEFAULT_LOW_FREQUENCY_RUMBLE_PERCENTAGE 50
class ControllerRumbleMapping : public ControllerMapping {
public:
ControllerRumbleMapping(PhysicalDeviceType physicalDeviceType, uint8_t portIndex,
uint8_t lowFrequencyIntensityPercentage, uint8_t highFrequencyIntensityPercentage);
~ControllerRumbleMapping();
virtual void StartRumble() = 0;
virtual void StopRumble() = 0;
virtual void SetLowFrequencyIntensity(uint8_t intensityPercentage);
virtual void SetHighFrequencyIntensity(uint8_t intensityPercentage);
uint8_t GetLowFrequencyIntensityPercentage();
uint8_t GetHighFrequencyIntensityPercentage();
bool HighFrequencyIntensityIsDefault();
bool LowFrequencyIntensityIsDefault();
void ResetHighFrequencyIntensityToDefault();
void ResetLowFrequencyIntensityToDefault();
void SetPortIndex(uint8_t portIndex);
virtual std::string GetRumbleMappingId() = 0;
virtual void SaveToConfig() = 0;
virtual void EraseFromConfig() = 0;
protected:
uint8_t mPortIndex;
uint8_t mLowFrequencyIntensityPercentage;
uint8_t mHighFrequencyIntensityPercentage;
};
} // namespace Ship

View File

@@ -0,0 +1,212 @@
#include "AxisDirectionMappingFactory.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardKeyToAxisDirectionMapping.h"
#include "controller/controldevice/controller/mapping/mouse/MouseButtonToAxisDirectionMapping.h"
#include "controller/controldevice/controller/mapping/mouse/MouseWheelToAxisDirectionMapping.h"
#include "controller/controldevice/controller/mapping/sdl/SDLButtonToAxisDirectionMapping.h"
#include "controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToAxisDirectionMapping.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include "Context.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
#include "controller/controldevice/controller/mapping/mouse/WheelHandler.h"
namespace Ship {
std::shared_ptr<ControllerAxisDirectionMapping>
AxisDirectionMappingFactory::CreateAxisDirectionMappingFromConfig(uint8_t portIndex, StickIndex stickIndex,
std::string id) {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + id;
const std::string mappingClass =
CVarGetString(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str(), "");
if (mappingClass == "SDLAxisDirectionToAxisDirectionMapping") {
int32_t direction = CVarGetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), -1);
int32_t sdlControllerAxis =
CVarGetInteger(StringHelper::Sprintf("%s.SDLControllerAxis", mappingCvarKey.c_str()).c_str(), -1);
int32_t axisDirection =
CVarGetInteger(StringHelper::Sprintf("%s.AxisDirection", mappingCvarKey.c_str()).c_str(), 0);
if ((direction != LEFT && direction != RIGHT && direction != UP && direction != DOWN) ||
sdlControllerAxis == -1 || (axisDirection != NEGATIVE && axisDirection != POSITIVE)) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
return std::make_shared<SDLAxisDirectionToAxisDirectionMapping>(
portIndex, stickIndex, static_cast<Direction>(direction), sdlControllerAxis, axisDirection);
}
if (mappingClass == "SDLButtonToAxisDirectionMapping") {
int32_t direction = CVarGetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), -1);
int32_t sdlControllerButton =
CVarGetInteger(StringHelper::Sprintf("%s.SDLControllerButton", mappingCvarKey.c_str()).c_str(), -1);
if ((direction != LEFT && direction != RIGHT && direction != UP && direction != DOWN) ||
sdlControllerButton == -1) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
return std::make_shared<SDLButtonToAxisDirectionMapping>(
portIndex, stickIndex, static_cast<Direction>(direction), sdlControllerButton);
}
if (mappingClass == "KeyboardKeyToAxisDirectionMapping") {
int32_t direction = CVarGetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), -1);
int32_t scancode =
CVarGetInteger(StringHelper::Sprintf("%s.KeyboardScancode", mappingCvarKey.c_str()).c_str(), 0);
if (direction != LEFT && direction != RIGHT && direction != UP && direction != DOWN) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
return std::make_shared<KeyboardKeyToAxisDirectionMapping>(
portIndex, stickIndex, static_cast<Direction>(direction), static_cast<KbScancode>(scancode));
}
if (mappingClass == "MouseButtonToAxisDirectionMapping") {
int32_t direction = CVarGetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), -1);
int mouseButton = CVarGetInteger(StringHelper::Sprintf("%s.MouseButton", mappingCvarKey.c_str()).c_str(), 0);
if (direction != LEFT && direction != RIGHT && direction != UP && direction != DOWN) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
return std::make_shared<MouseButtonToAxisDirectionMapping>(
portIndex, stickIndex, static_cast<Direction>(direction), static_cast<MouseBtn>(mouseButton));
}
if (mappingClass == "MouseWheelToAxisDirectionMapping") {
int32_t direction = CVarGetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), -1);
int wheelDirection =
CVarGetInteger(StringHelper::Sprintf("%s.WheelDirection", mappingCvarKey.c_str()).c_str(), 0);
if (direction != LEFT && direction != RIGHT && direction != UP && direction != DOWN) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
return std::make_shared<MouseWheelToAxisDirectionMapping>(
portIndex, stickIndex, static_cast<Direction>(direction), static_cast<WheelDirection>(wheelDirection));
}
return nullptr;
}
std::vector<std::shared_ptr<ControllerAxisDirectionMapping>>
AxisDirectionMappingFactory::CreateDefaultKeyboardAxisDirectionMappings(uint8_t portIndex, StickIndex stickIndex) {
std::vector<std::shared_ptr<ControllerAxisDirectionMapping>> mappings;
auto defaultsForStick = Context::GetInstance()
->GetControlDeck()
->GetControllerDefaultMappings()
->GetDefaultKeyboardKeyToAxisDirectionMappings()[stickIndex];
for (const auto& [direction, scancode] : defaultsForStick) {
mappings.push_back(
std::make_shared<KeyboardKeyToAxisDirectionMapping>(portIndex, stickIndex, direction, scancode));
}
return mappings;
}
std::vector<std::shared_ptr<ControllerAxisDirectionMapping>>
AxisDirectionMappingFactory::CreateDefaultSDLAxisDirectionMappings(uint8_t portIndex, StickIndex stickIndex) {
std::vector<std::shared_ptr<ControllerAxisDirectionMapping>> mappings;
auto defaultButtonsForStick = Context::GetInstance()
->GetControlDeck()
->GetControllerDefaultMappings()
->GetDefaultSDLButtonToAxisDirectionMappings()[stickIndex];
for (const auto& [direction, sdlGamepadButton] : defaultButtonsForStick) {
mappings.push_back(
std::make_shared<SDLButtonToAxisDirectionMapping>(portIndex, stickIndex, direction, sdlGamepadButton));
}
auto defaultAxisDirectionsForStick = Context::GetInstance()
->GetControlDeck()
->GetControllerDefaultMappings()
->GetDefaultSDLAxisDirectionToAxisDirectionMappings()[stickIndex];
for (const auto& [direction, sdlGamepadAxisDirection] : defaultAxisDirectionsForStick) {
auto [sdlGamepadAxis, sdlGamepadDirection] = sdlGamepadAxisDirection;
mappings.push_back(std::make_shared<SDLAxisDirectionToAxisDirectionMapping>(
portIndex, stickIndex, direction, sdlGamepadAxis, sdlGamepadDirection));
}
return mappings;
}
std::shared_ptr<ControllerAxisDirectionMapping>
AxisDirectionMappingFactory::CreateAxisDirectionMappingFromSDLInput(uint8_t portIndex, StickIndex stickIndex,
Direction direction) {
std::shared_ptr<ControllerAxisDirectionMapping> mapping = nullptr;
for (auto [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
portIndex)) {
for (int32_t button = SDL_CONTROLLER_BUTTON_A; button < SDL_CONTROLLER_BUTTON_MAX; button++) {
if (SDL_GameControllerGetButton(gamepad, static_cast<SDL_GameControllerButton>(button))) {
mapping = std::make_shared<SDLButtonToAxisDirectionMapping>(portIndex, stickIndex, direction, button);
break;
}
}
if (mapping != nullptr) {
break;
}
for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) {
const auto axis = static_cast<SDL_GameControllerAxis>(i);
const auto axisValue = SDL_GameControllerGetAxis(gamepad, axis) / 32767.0f;
int32_t axisDirection = 0;
if (axisValue < -0.7f) {
axisDirection = NEGATIVE;
} else if (axisValue > 0.7f) {
axisDirection = POSITIVE;
}
if (axisDirection == 0) {
continue;
}
mapping = std::make_shared<SDLAxisDirectionToAxisDirectionMapping>(portIndex, stickIndex, direction, axis,
axisDirection);
break;
}
}
return mapping;
}
std::shared_ptr<ControllerAxisDirectionMapping>
AxisDirectionMappingFactory::CreateAxisDirectionMappingFromMouseWheelInput(uint8_t portIndex, StickIndex stickIndex,
Direction direction) {
WheelDirections wheelDirections = WheelHandler::GetInstance()->GetDirections();
WheelDirection wheelDirection;
if (wheelDirections.x != LUS_WHEEL_NONE) {
wheelDirection = wheelDirections.x;
} else if (wheelDirections.y != LUS_WHEEL_NONE) {
wheelDirection = wheelDirections.y;
} else {
return nullptr;
}
return std::make_shared<MouseWheelToAxisDirectionMapping>(portIndex, stickIndex, direction, wheelDirection);
}
} // namespace Ship

View File

@@ -0,0 +1,26 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h"
#include <memory>
#include <string>
#include <vector>
namespace Ship {
class AxisDirectionMappingFactory {
public:
static std::shared_ptr<ControllerAxisDirectionMapping>
CreateAxisDirectionMappingFromConfig(uint8_t portIndex, StickIndex stickIndex, std::string id);
static std::vector<std::shared_ptr<ControllerAxisDirectionMapping>>
CreateDefaultKeyboardAxisDirectionMappings(uint8_t portIndex, StickIndex stickIndex);
static std::vector<std::shared_ptr<ControllerAxisDirectionMapping>>
CreateDefaultSDLAxisDirectionMappings(uint8_t portIndex, StickIndex stickIndex);
static std::shared_ptr<ControllerAxisDirectionMapping>
CreateAxisDirectionMappingFromSDLInput(uint8_t portIndex, StickIndex stickIndex, Direction direction);
static std::shared_ptr<ControllerAxisDirectionMapping>
CreateAxisDirectionMappingFromMouseWheelInput(uint8_t portIndex, StickIndex stickIndex, Direction direction);
};
} // namespace Ship

View File

@@ -0,0 +1,179 @@
#include "ButtonMappingFactory.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include "libultraship/libultra/controller.h"
#include "Context.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardKeyToButtonMapping.h"
#include "controller/controldevice/controller/mapping/mouse/MouseButtonToButtonMapping.h"
#include "controller/controldevice/controller/mapping/mouse/MouseWheelToButtonMapping.h"
#include "controller/controldevice/controller/mapping/sdl/SDLButtonToButtonMapping.h"
#include "controller/controldevice/controller/mapping/sdl/SDLAxisDirectionToButtonMapping.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
#include "controller/controldevice/controller/mapping/mouse/WheelHandler.h"
namespace Ship {
std::shared_ptr<ControllerButtonMapping> ButtonMappingFactory::CreateButtonMappingFromConfig(uint8_t portIndex,
std::string id) {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + id;
const std::string mappingClass =
CVarGetString(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str(), "");
CONTROLLERBUTTONS_T bitmask =
CVarGetInteger(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str(), 0);
if (!bitmask) {
// all button mappings need bitmasks
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
if (mappingClass == "SDLButtonToButtonMapping") {
int32_t sdlControllerButton =
CVarGetInteger(StringHelper::Sprintf("%s.SDLControllerButton", mappingCvarKey.c_str()).c_str(), -1);
if (sdlControllerButton == -1) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
return std::make_shared<SDLButtonToButtonMapping>(portIndex, bitmask, sdlControllerButton);
}
if (mappingClass == "SDLAxisDirectionToButtonMapping") {
int32_t sdlControllerAxis =
CVarGetInteger(StringHelper::Sprintf("%s.SDLControllerAxis", mappingCvarKey.c_str()).c_str(), -1);
int32_t axisDirection =
CVarGetInteger(StringHelper::Sprintf("%s.AxisDirection", mappingCvarKey.c_str()).c_str(), 0);
if (sdlControllerAxis == -1 || (axisDirection != -1 && axisDirection != 1)) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
return std::make_shared<SDLAxisDirectionToButtonMapping>(portIndex, bitmask, sdlControllerAxis, axisDirection);
}
if (mappingClass == "KeyboardKeyToButtonMapping") {
int32_t scancode =
CVarGetInteger(StringHelper::Sprintf("%s.KeyboardScancode", mappingCvarKey.c_str()).c_str(), 0);
return std::make_shared<KeyboardKeyToButtonMapping>(portIndex, bitmask, static_cast<KbScancode>(scancode));
}
if (mappingClass == "MouseButtonToButtonMapping") {
int mouseButton = CVarGetInteger(StringHelper::Sprintf("%s.MouseButton", mappingCvarKey.c_str()).c_str(), 0);
return std::make_shared<MouseButtonToButtonMapping>(portIndex, bitmask, static_cast<MouseBtn>(mouseButton));
}
if (mappingClass == "MouseWheelToButtonMapping") {
int wheelDirection =
CVarGetInteger(StringHelper::Sprintf("%s.WheelDirection", mappingCvarKey.c_str()).c_str(), 0);
return std::make_shared<MouseWheelToButtonMapping>(portIndex, bitmask,
static_cast<WheelDirection>(wheelDirection));
}
return nullptr;
}
std::vector<std::shared_ptr<ControllerButtonMapping>>
ButtonMappingFactory::CreateDefaultKeyboardButtonMappings(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask) {
std::vector<std::shared_ptr<ControllerButtonMapping>> mappings;
auto defaultsForBitmask = Context::GetInstance()
->GetControlDeck()
->GetControllerDefaultMappings()
->GetDefaultKeyboardKeyToButtonMappings()[bitmask];
for (const auto& scancode : defaultsForBitmask) {
mappings.push_back(std::make_shared<KeyboardKeyToButtonMapping>(portIndex, bitmask, scancode));
}
return mappings;
}
std::vector<std::shared_ptr<ControllerButtonMapping>>
ButtonMappingFactory::CreateDefaultSDLButtonMappings(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask) {
std::vector<std::shared_ptr<ControllerButtonMapping>> mappings;
auto defaultButtonsForBitmask = Context::GetInstance()
->GetControlDeck()
->GetControllerDefaultMappings()
->GetDefaultSDLButtonToButtonMappings()[bitmask];
for (const auto& sdlGamepadButton : defaultButtonsForBitmask) {
mappings.push_back(std::make_shared<SDLButtonToButtonMapping>(portIndex, bitmask, sdlGamepadButton));
}
auto defaultAxisDirectionsForBitmask = Context::GetInstance()
->GetControlDeck()
->GetControllerDefaultMappings()
->GetDefaultSDLAxisDirectionToButtonMappings()[bitmask];
for (const auto& [sdlGamepadAxis, axisDirection] : defaultAxisDirectionsForBitmask) {
mappings.push_back(
std::make_shared<SDLAxisDirectionToButtonMapping>(portIndex, bitmask, sdlGamepadAxis, axisDirection));
}
return mappings;
}
std::shared_ptr<ControllerButtonMapping>
ButtonMappingFactory::CreateButtonMappingFromSDLInput(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask) {
std::shared_ptr<ControllerButtonMapping> mapping = nullptr;
for (auto [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
portIndex)) {
for (int32_t button = SDL_CONTROLLER_BUTTON_A; button < SDL_CONTROLLER_BUTTON_MAX; button++) {
if (SDL_GameControllerGetButton(gamepad, static_cast<SDL_GameControllerButton>(button))) {
mapping = std::make_shared<SDLButtonToButtonMapping>(portIndex, bitmask, button);
break;
}
}
if (mapping != nullptr) {
break;
}
for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) {
const auto axis = static_cast<SDL_GameControllerAxis>(i);
const auto axisValue = SDL_GameControllerGetAxis(gamepad, axis) / 32767.0f;
int32_t axisDirection = 0;
if (axisValue < -0.7f) {
axisDirection = NEGATIVE;
} else if (axisValue > 0.7f) {
axisDirection = POSITIVE;
}
if (axisDirection == 0) {
continue;
}
mapping = std::make_shared<SDLAxisDirectionToButtonMapping>(portIndex, bitmask, axis, axisDirection);
break;
}
}
return mapping;
}
std::shared_ptr<ControllerButtonMapping>
ButtonMappingFactory::CreateButtonMappingFromMouseWheelInput(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask) {
WheelDirections wheelDirections = WheelHandler::GetInstance()->GetDirections();
WheelDirection wheelDirection;
if (wheelDirections.x != LUS_WHEEL_NONE) {
wheelDirection = wheelDirections.x;
} else if (wheelDirections.y != LUS_WHEEL_NONE) {
wheelDirection = wheelDirections.y;
} else {
return nullptr;
}
return std::make_shared<MouseWheelToButtonMapping>(portIndex, bitmask, wheelDirection);
}
} // namespace Ship

View File

@@ -0,0 +1,25 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerButtonMapping.h"
#include <memory>
#include <string>
#include <vector>
namespace Ship {
class ButtonMappingFactory {
public:
static std::shared_ptr<ControllerButtonMapping> CreateButtonMappingFromConfig(uint8_t portIndex, std::string id);
static std::vector<std::shared_ptr<ControllerButtonMapping>>
CreateDefaultKeyboardButtonMappings(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask);
static std::vector<std::shared_ptr<ControllerButtonMapping>>
CreateDefaultSDLButtonMappings(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask);
static std::shared_ptr<ControllerButtonMapping> CreateButtonMappingFromSDLInput(uint8_t portIndex,
CONTROLLERBUTTONS_T bitmask);
static std::shared_ptr<ControllerButtonMapping> CreateButtonMappingFromMouseWheelInput(uint8_t portIndex,
CONTROLLERBUTTONS_T bitmask);
};
} // namespace Ship

View File

@@ -0,0 +1,81 @@
#include "GyroMappingFactory.h"
#include "controller/controldevice/controller/mapping/sdl/SDLGyroMapping.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include "libultraship/libultra/controller.h"
#include "Context.h"
namespace Ship {
std::shared_ptr<ControllerGyroMapping> GyroMappingFactory::CreateGyroMappingFromConfig(uint8_t portIndex,
std::string id) {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".GyroMappings." + id;
const std::string mappingClass =
CVarGetString(StringHelper::Sprintf("%s.GyroMappingClass", mappingCvarKey.c_str()).c_str(), "");
float sensitivity = CVarGetFloat(StringHelper::Sprintf("%s.Sensitivity", mappingCvarKey.c_str()).c_str(), 2.0f);
if (sensitivity < 0.0f || sensitivity > 1.0f) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
if (mappingClass == "SDLGyroMapping") {
float neutralPitch =
CVarGetFloat(StringHelper::Sprintf("%s.NeutralPitch", mappingCvarKey.c_str()).c_str(), 0.0f);
float neutralYaw = CVarGetFloat(StringHelper::Sprintf("%s.NeutralYaw", mappingCvarKey.c_str()).c_str(), 0.0f);
float neutralRoll = CVarGetFloat(StringHelper::Sprintf("%s.NeutralRoll", mappingCvarKey.c_str()).c_str(), 0.0f);
return std::make_shared<SDLGyroMapping>(portIndex, sensitivity, neutralPitch, neutralYaw, neutralRoll);
}
return nullptr;
}
std::shared_ptr<ControllerGyroMapping> GyroMappingFactory::CreateGyroMappingFromSDLInput(uint8_t portIndex) {
std::shared_ptr<ControllerGyroMapping> mapping = nullptr;
for (auto [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
portIndex)) {
if (!SDL_GameControllerHasSensor(gamepad, SDL_SENSOR_GYRO)) {
#ifndef __ANDROID__
continue;
#endif
}
for (int32_t button = SDL_CONTROLLER_BUTTON_A; button < SDL_CONTROLLER_BUTTON_MAX; button++) {
if (SDL_GameControllerGetButton(gamepad, static_cast<SDL_GameControllerButton>(button))) {
mapping = std::make_shared<SDLGyroMapping>(portIndex, 1.0f, 0.0f, 0.0f, 0.0f);
mapping->Recalibrate();
break;
}
}
if (mapping != nullptr) {
break;
}
for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) {
const auto axis = static_cast<SDL_GameControllerAxis>(i);
const auto axisValue = SDL_GameControllerGetAxis(gamepad, axis) / 32767.0f;
int32_t axisDirection = 0;
if (axisValue < -0.7f) {
axisDirection = NEGATIVE;
} else if (axisValue > 0.7f) {
axisDirection = POSITIVE;
}
if (axisDirection == 0) {
continue;
}
mapping = std::make_shared<SDLGyroMapping>(portIndex, 1.0f, 0.0f, 0.0f, 0.0f);
mapping->Recalibrate();
break;
}
}
return mapping;
}
} // namespace Ship

View File

@@ -0,0 +1,13 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerGyroMapping.h"
#include <memory>
#include <string>
namespace Ship {
class GyroMappingFactory {
public:
static std::shared_ptr<ControllerGyroMapping> CreateGyroMappingFromConfig(uint8_t portIndex, std::string id);
static std::shared_ptr<ControllerGyroMapping> CreateGyroMappingFromSDLInput(uint8_t portIndex);
};
} // namespace Ship

View File

@@ -0,0 +1,75 @@
#include "LEDMappingFactory.h"
#include "controller/controldevice/controller/mapping/sdl/SDLLEDMapping.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include "libultraship/libultra/controller.h"
#include "Context.h"
namespace Ship {
std::shared_ptr<ControllerLEDMapping> LEDMappingFactory::CreateLEDMappingFromConfig(uint8_t portIndex, std::string id) {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".LEDMappings." + id;
const std::string mappingClass =
CVarGetString(StringHelper::Sprintf("%s.LEDMappingClass", mappingCvarKey.c_str()).c_str(), "");
int32_t colorSource = CVarGetInteger(StringHelper::Sprintf("%s.ColorSource", mappingCvarKey.c_str()).c_str(), -1);
Color_RGB8 savedColor =
CVarGetColor24(StringHelper::Sprintf("%s.SavedColor", mappingCvarKey.c_str()).c_str(), { 0, 0, 0 });
if (colorSource != LED_COLOR_SOURCE_OFF && colorSource != LED_COLOR_SOURCE_SET &&
colorSource != LED_COLOR_SOURCE_GAME) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
if (mappingClass == "SDLLEDMapping") {
return std::make_shared<SDLLEDMapping>(portIndex, colorSource, savedColor);
}
return nullptr;
}
std::shared_ptr<ControllerLEDMapping> LEDMappingFactory::CreateLEDMappingFromSDLInput(uint8_t portIndex) {
std::shared_ptr<ControllerLEDMapping> mapping = nullptr;
for (auto [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
portIndex)) {
if (!SDL_GameControllerHasLED(gamepad)) {
continue;
}
for (int32_t button = SDL_CONTROLLER_BUTTON_A; button < SDL_CONTROLLER_BUTTON_MAX; button++) {
if (SDL_GameControllerGetButton(gamepad, static_cast<SDL_GameControllerButton>(button))) {
mapping = std::make_shared<SDLLEDMapping>(portIndex, 0, Color_RGB8({ 0, 0, 0 }));
break;
}
}
if (mapping != nullptr) {
break;
}
for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) {
const auto axis = static_cast<SDL_GameControllerAxis>(i);
const auto axisValue = SDL_GameControllerGetAxis(gamepad, axis) / 32767.0f;
int32_t axisDirection = 0;
if (axisValue < -0.7f) {
axisDirection = NEGATIVE;
} else if (axisValue > 0.7f) {
axisDirection = POSITIVE;
}
if (axisDirection == 0) {
continue;
}
mapping = std::make_shared<SDLLEDMapping>(portIndex, 0, Color_RGB8({ 0, 0, 0 }));
break;
}
}
return mapping;
}
} // namespace Ship

View File

@@ -0,0 +1,14 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerLEDMapping.h"
#include <memory>
#include <string>
#include <vector>
namespace Ship {
class LEDMappingFactory {
public:
static std::shared_ptr<ControllerLEDMapping> CreateLEDMappingFromConfig(uint8_t portIndex, std::string id);
static std::shared_ptr<ControllerLEDMapping> CreateLEDMappingFromSDLInput(uint8_t portIndex);
};
} // namespace Ship

View File

@@ -0,0 +1,88 @@
#include "RumbleMappingFactory.h"
#include "controller/controldevice/controller/mapping/sdl/SDLRumbleMapping.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include "libultraship/libultra/controller.h"
#include "Context.h"
namespace Ship {
std::shared_ptr<ControllerRumbleMapping> RumbleMappingFactory::CreateRumbleMappingFromConfig(uint8_t portIndex,
std::string id) {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".RumbleMappings." + id;
const std::string mappingClass =
CVarGetString(StringHelper::Sprintf("%s.RumbleMappingClass", mappingCvarKey.c_str()).c_str(), "");
int32_t lowFrequencyIntensityPercentage =
CVarGetInteger(StringHelper::Sprintf("%s.LowFrequencyIntensity", mappingCvarKey.c_str()).c_str(), -1);
int32_t highFrequencyIntensityPercentage =
CVarGetInteger(StringHelper::Sprintf("%s.HighFrequencyIntensity", mappingCvarKey.c_str()).c_str(), -1);
if (lowFrequencyIntensityPercentage < 0 || lowFrequencyIntensityPercentage > 100 ||
highFrequencyIntensityPercentage < 0 || highFrequencyIntensityPercentage > 100) {
// something about this mapping is invalid
CVarClear(mappingCvarKey.c_str());
CVarSave();
return nullptr;
}
if (mappingClass == "SDLRumbleMapping") {
return std::make_shared<SDLRumbleMapping>(portIndex, lowFrequencyIntensityPercentage,
highFrequencyIntensityPercentage);
}
return nullptr;
}
std::vector<std::shared_ptr<ControllerRumbleMapping>>
RumbleMappingFactory::CreateDefaultSDLRumbleMappings(PhysicalDeviceType physicalDeviceType, uint8_t portIndex) {
std::vector<std::shared_ptr<ControllerRumbleMapping>> mappings = { std::make_shared<SDLRumbleMapping>(
portIndex, DEFAULT_LOW_FREQUENCY_RUMBLE_PERCENTAGE, DEFAULT_HIGH_FREQUENCY_RUMBLE_PERCENTAGE) };
return mappings;
}
std::shared_ptr<ControllerRumbleMapping> RumbleMappingFactory::CreateRumbleMappingFromSDLInput(uint8_t portIndex) {
std::shared_ptr<ControllerRumbleMapping> mapping = nullptr;
for (auto [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
portIndex)) {
if (!SDL_GameControllerHasRumble(gamepad)) {
continue;
}
for (int32_t button = SDL_CONTROLLER_BUTTON_A; button < SDL_CONTROLLER_BUTTON_MAX; button++) {
if (SDL_GameControllerGetButton(gamepad, static_cast<SDL_GameControllerButton>(button))) {
mapping = std::make_shared<SDLRumbleMapping>(portIndex, DEFAULT_LOW_FREQUENCY_RUMBLE_PERCENTAGE,
DEFAULT_HIGH_FREQUENCY_RUMBLE_PERCENTAGE);
break;
}
}
if (mapping != nullptr) {
break;
}
for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) {
const auto axis = static_cast<SDL_GameControllerAxis>(i);
const auto axisValue = SDL_GameControllerGetAxis(gamepad, axis) / 32767.0f;
int32_t axisDirection = 0;
if (axisValue < -0.7f) {
axisDirection = NEGATIVE;
} else if (axisValue > 0.7f) {
axisDirection = POSITIVE;
}
if (axisDirection == 0) {
continue;
}
mapping = std::make_shared<SDLRumbleMapping>(portIndex, DEFAULT_LOW_FREQUENCY_RUMBLE_PERCENTAGE,
DEFAULT_HIGH_FREQUENCY_RUMBLE_PERCENTAGE);
break;
}
}
return mapping;
}
} // namespace Ship

View File

@@ -0,0 +1,18 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerRumbleMapping.h"
#include <memory>
#include <string>
#include <vector>
namespace Ship {
class RumbleMappingFactory {
public:
static std::shared_ptr<ControllerRumbleMapping> CreateRumbleMappingFromConfig(uint8_t portIndex, std::string id);
static std::vector<std::shared_ptr<ControllerRumbleMapping>>
CreateDefaultSDLRumbleMappings(PhysicalDeviceType physicalDeviceType, uint8_t portIndex);
static std::shared_ptr<ControllerRumbleMapping> CreateRumbleMappingFromSDLInput(uint8_t portIndex);
};
} // namespace Ship

View File

@@ -0,0 +1,45 @@
#include "KeyboardKeyToAnyMapping.h"
#include "Context.h"
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
namespace Ship {
KeyboardKeyToAnyMapping::KeyboardKeyToAnyMapping(KbScancode scancode)
: ControllerInputMapping(PhysicalDeviceType::Keyboard), mKeyboardScancode(scancode), mKeyPressed(false) {
}
KeyboardKeyToAnyMapping::~KeyboardKeyToAnyMapping() {
}
std::string KeyboardKeyToAnyMapping::GetPhysicalInputName() {
return Context::GetInstance()->GetWindow()->GetKeyName(mKeyboardScancode);
}
bool KeyboardKeyToAnyMapping::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode) {
if (eventType == KbEventType::LUS_KB_EVENT_ALL_KEYS_UP) {
mKeyPressed = false;
return true;
}
if (mKeyboardScancode != scancode) {
return false;
}
if (eventType == KbEventType::LUS_KB_EVENT_KEY_DOWN) {
mKeyPressed = true;
return true;
}
if (eventType == KbEventType::LUS_KB_EVENT_KEY_UP) {
mKeyPressed = false;
return true;
}
return false;
}
std::string KeyboardKeyToAnyMapping::GetPhysicalDeviceName() {
return "Keyboard";
}
} // namespace Ship

View File

@@ -0,0 +1,19 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerInputMapping.h"
#include "KeyboardScancodes.h"
namespace Ship {
class KeyboardKeyToAnyMapping : virtual public ControllerInputMapping {
public:
KeyboardKeyToAnyMapping(KbScancode scancode);
~KeyboardKeyToAnyMapping();
std::string GetPhysicalInputName() override;
bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
std::string GetPhysicalDeviceName() override;
protected:
KbScancode mKeyboardScancode;
bool mKeyPressed;
};
} // namespace Ship

View File

@@ -0,0 +1,49 @@
#include "KeyboardKeyToAxisDirectionMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
namespace Ship {
KeyboardKeyToAxisDirectionMapping::KeyboardKeyToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex,
Direction direction, KbScancode scancode)
: ControllerInputMapping(PhysicalDeviceType::Keyboard), KeyboardKeyToAnyMapping(scancode),
ControllerAxisDirectionMapping(PhysicalDeviceType::Keyboard, portIndex, stickIndex, direction) {
}
float KeyboardKeyToAxisDirectionMapping::GetNormalizedAxisDirectionValue() {
if (Context::GetInstance()->GetControlDeck()->KeyboardGameInputBlocked()) {
return 0.0f;
}
return mKeyPressed ? MAX_AXIS_RANGE : 0.0f;
}
std::string KeyboardKeyToAxisDirectionMapping::GetAxisDirectionMappingId() {
return StringHelper::Sprintf("P%d-S%d-D%d-KB%d", mPortIndex, mStickIndex, mDirection, mKeyboardScancode);
}
void KeyboardKeyToAxisDirectionMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarSetString(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str(),
"KeyboardKeyToAxisDirectionMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str(), mStickIndex);
CVarSetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), mDirection);
CVarSetInteger(StringHelper::Sprintf("%s.KeyboardScancode", mappingCvarKey.c_str()).c_str(), mKeyboardScancode);
CVarSave();
}
void KeyboardKeyToAxisDirectionMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarClear(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.KeyboardScancode", mappingCvarKey.c_str()).c_str());
CVarSave();
}
int8_t KeyboardKeyToAxisDirectionMapping::GetMappingType() {
return MAPPING_TYPE_KEYBOARD;
}
} // namespace Ship

View File

@@ -0,0 +1,15 @@
#include "controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h"
#include "KeyboardKeyToAnyMapping.h"
namespace Ship {
class KeyboardKeyToAxisDirectionMapping final : public KeyboardKeyToAnyMapping, public ControllerAxisDirectionMapping {
public:
KeyboardKeyToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex, Direction direction,
KbScancode scancode);
float GetNormalizedAxisDirectionValue() override;
std::string GetAxisDirectionMappingId() override;
int8_t GetMappingType() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,52 @@
#include "KeyboardKeyToButtonMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
namespace Ship {
KeyboardKeyToButtonMapping::KeyboardKeyToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask,
KbScancode scancode)
: ControllerInputMapping(PhysicalDeviceType::Keyboard), KeyboardKeyToAnyMapping(scancode),
ControllerButtonMapping(PhysicalDeviceType::Keyboard, portIndex, bitmask) {
}
void KeyboardKeyToButtonMapping::UpdatePad(CONTROLLERBUTTONS_T& padButtons) {
if (Context::GetInstance()->GetControlDeck()->KeyboardGameInputBlocked()) {
return;
}
if (!mKeyPressed) {
return;
}
padButtons |= mBitmask;
}
int8_t KeyboardKeyToButtonMapping::GetMappingType() {
return MAPPING_TYPE_KEYBOARD;
}
std::string KeyboardKeyToButtonMapping::GetButtonMappingId() {
return StringHelper::Sprintf("P%d-B%d-KB%d", mPortIndex, mBitmask, mKeyboardScancode);
}
void KeyboardKeyToButtonMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarSetString(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str(),
"KeyboardKeyToButtonMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str(), mBitmask);
CVarSetInteger(StringHelper::Sprintf("%s.KeyboardScancode", mappingCvarKey.c_str()).c_str(), mKeyboardScancode);
CVarSave();
}
void KeyboardKeyToButtonMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarClear(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.KeyboardScancode", mappingCvarKey.c_str()).c_str());
CVarSave();
}
} // namespace Ship

View File

@@ -0,0 +1,14 @@
#include "controller/controldevice/controller/mapping/ControllerButtonMapping.h"
#include "KeyboardKeyToAnyMapping.h"
namespace Ship {
class KeyboardKeyToButtonMapping final : public KeyboardKeyToAnyMapping, public ControllerButtonMapping {
public:
KeyboardKeyToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask, KbScancode scancode);
void UpdatePad(CONTROLLERBUTTONS_T& padButtons) override;
int8_t GetMappingType() override;
std::string GetButtonMappingId() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,148 @@
#pragma once
#ifdef __cplusplus
#include <string>
namespace Ship {
#endif
typedef enum KbEventType {
LUS_KB_EVENT_KEY_DOWN = 0,
LUS_KB_EVENT_KEY_UP = 1,
LUS_KB_EVENT_ALL_KEYS_UP = 2,
LUS_KB_EVENT_MAX
} KbEventType;
typedef enum KbScancode {
LUS_KB_UNKNOWN = 0,
LUS_KB_ESCAPE = 1,
LUS_KB_1 = 2,
LUS_KB_2 = 3,
LUS_KB_3 = 4,
LUS_KB_4 = 5,
LUS_KB_5 = 6,
LUS_KB_6 = 7,
LUS_KB_7 = 8,
LUS_KB_8 = 9,
LUS_KB_9 = 10,
LUS_KB_0 = 11,
LUS_KB_OEM_MINUS = 12,
LUS_KB_OEM_PLUS = 13,
LUS_KB_BACKSPACE = 14,
LUS_KB_TAB = 15,
LUS_KB_Q = 16,
LUS_KB_W = 17,
LUS_KB_E = 18,
LUS_KB_R = 19,
LUS_KB_T = 20,
LUS_KB_Y = 21,
LUS_KB_U = 22,
LUS_KB_I = 23,
LUS_KB_O = 24,
LUS_KB_P = 25,
LUS_KB_OEM_4 = 26, // open bracket([)/brace({) on US keyboard
LUS_KB_OEM_6 = 27, // backslash(\)/pipe(|) on US keyboard
LUS_KB_ENTER = 28,
LUS_KB_CONTROL = 29,
LUS_KB_A = 30,
LUS_KB_S = 31,
LUS_KB_D = 32,
LUS_KB_F = 33,
LUS_KB_G = 34,
LUS_KB_H = 35,
LUS_KB_J = 36,
LUS_KB_K = 37,
LUS_KB_L = 38,
LUS_KB_OEM_1 = 39, // semicolon (;)/colon (:) on US keyboard
LUS_KB_OEM_7 = 40, // single quote(')/double quote(") on US keyboard
LUS_KB_OEM_3 = 41, // grave (`)/tilde(~) on US keyboard
LUS_KB_SHIFT = 42,
LUS_KB_OEM_5 = 43, // backslash(\)/pipe(|) on US keyboard
LUS_KB_Z = 44,
LUS_KB_X = 45,
LUS_KB_C = 46,
LUS_KB_V = 47,
LUS_KB_B = 48,
LUS_KB_N = 49,
LUS_KB_M = 50,
LUS_KB_OEM_COMMA = 51,
LUS_KB_OEM_PERIOD = 52,
LUS_KB_OEM_2 = 53, // slash (/)/question mark (?) on US keyboard
LUS_KB_RSHIFT = 54,
LUS_KB_MULTIPLY = 55, // '*' on numpad
LUS_KB_ALT = 56,
LUS_KB_SPACE = 57,
LUS_KB_CAPSLOCK = 58,
LUS_KB_F1 = 59,
LUS_KB_F2 = 60,
LUS_KB_F3 = 61,
LUS_KB_F4 = 62,
LUS_KB_F5 = 63,
LUS_KB_F6 = 64,
LUS_KB_F7 = 65,
LUS_KB_F8 = 66,
LUS_KB_F9 = 67,
LUS_KB_F10 = 68, // Generally inadvised to use this as it's a windows system key.
LUS_KB_PAUSE = 69,
LUS_KB_SCROLL = 70,
LUS_KB_NUMPAD7 = 71,
LUS_KB_NUMPAD8 = 72,
LUS_KB_NUMPAD9 = 73,
LUS_KB_SUBTRACT = 74, // - on numpad
LUS_KB_NUMPAD4 = 75,
LUS_KB_NUMPAD5 = 76,
LUS_KB_NUMPAD6 = 77,
LUS_KB_ADD = 78, // + on numpad
LUS_KB_NUMPAD1 = 79,
LUS_KB_NUMPAD2 = 80,
LUS_KB_NUMPAD3 = 81,
LUS_KB_NUMPAD0 = 82,
LUS_KB_NUMPAD_DEL = 83,
LUS_KB_PRINTSCREEN = 84,
LUS_KB_F11 = 87,
LUS_KB_F12 = 88,
LUS_KB_F13 = 124,
LUS_KB_F14 = 125,
LUS_KB_F15 = 126,
LUS_KB_F16 = 127,
LUS_KB_F17 = 128,
LUS_KB_F18 = 129,
LUS_KB_F19 = 130,
LUS_KB_F20 = 131,
LUS_KB_F21 = 132,
LUS_KB_F22 = 133,
LUS_KB_F23 = 134,
LUS_KB_F24 = 135,
LUS_KB_ARROWKEY_UP = 328,
LUS_KB_ARROWKEY_LEFT = 331,
LUS_KB_ARROWKEY_RIGHT = 333,
LUS_KB_ARROWKEY_DOWN = 336,
LUS_KB_MAX
} KbScancode;
typedef enum MouseBtn {
LUS_MOUSE_BTN_LEFT,
LUS_MOUSE_BTN_MIDDLE,
LUS_MOUSE_BTN_RIGHT,
LUS_MOUSE_BTN_BACKWARD,
LUS_MOUSE_BTN_FORWARD,
LUS_MOUSE_BTN_COUNT,
LUS_MOUSE_BTN_UNKNOWN
} MouseBtn;
typedef enum WheelDirection {
LUS_WHEEL_NONE,
LUS_WHEEL_LEFT,
LUS_WHEEL_RIGHT,
LUS_WHEEL_UP,
LUS_WHEEL_DOWN,
LUS_WHEEL_UNKNOWN
} WheelDirection;
#ifdef __cplusplus
static std::string mouseBtnNames[7] = { "MouseLeft", "MouseMiddle", "MouseRight", "MouseBackward",
"MouseForward", "MOUSE_BTN_COUNT", "MOUSE_BTN_UNKNOWN" };
static std::string wheelDirectionNames[6] = { "LUS_WHEEL_NONE", "WheelLeft", "WheelRight",
"WheelUp", "WheelDown", "LUS_WHEEL_UNKNOWN" };
} // namespace Ship
#endif

View File

@@ -0,0 +1,31 @@
#include "MouseButtonToAnyMapping.h"
#include "Context.h"
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
namespace Ship {
MouseButtonToAnyMapping::MouseButtonToAnyMapping(MouseBtn button)
: ControllerInputMapping(PhysicalDeviceType::Mouse), mButton(button), mKeyPressed(false) {
}
MouseButtonToAnyMapping::~MouseButtonToAnyMapping() {
}
std::string MouseButtonToAnyMapping::GetPhysicalInputName() {
return mouseBtnNames[static_cast<int>(mButton)];
}
bool MouseButtonToAnyMapping::ProcessMouseButtonEvent(bool isPressed, MouseBtn button) {
if (mButton != button) {
return false;
}
mKeyPressed = isPressed;
return true;
}
std::string MouseButtonToAnyMapping::GetPhysicalDeviceName() {
return "Mouse";
}
} // namespace Ship

View File

@@ -0,0 +1,19 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerInputMapping.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
class MouseButtonToAnyMapping : virtual public ControllerInputMapping {
public:
MouseButtonToAnyMapping(MouseBtn button);
~MouseButtonToAnyMapping();
std::string GetPhysicalInputName() override;
bool ProcessMouseButtonEvent(bool isPressed, MouseBtn button);
std::string GetPhysicalDeviceName() override;
protected:
MouseBtn mButton;
bool mKeyPressed;
};
} // namespace Ship

View File

@@ -0,0 +1,49 @@
#include "MouseButtonToAxisDirectionMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
namespace Ship {
MouseButtonToAxisDirectionMapping::MouseButtonToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex,
Direction direction, MouseBtn button)
: ControllerInputMapping(PhysicalDeviceType::Mouse), MouseButtonToAnyMapping(button),
ControllerAxisDirectionMapping(PhysicalDeviceType::Mouse, portIndex, stickIndex, direction) {
}
float MouseButtonToAxisDirectionMapping::GetNormalizedAxisDirectionValue() {
if (Context::GetInstance()->GetControlDeck()->MouseGameInputBlocked()) {
return 0.0f;
}
return mKeyPressed ? MAX_AXIS_RANGE : 0.0f;
}
std::string MouseButtonToAxisDirectionMapping::GetAxisDirectionMappingId() {
return StringHelper::Sprintf("P%d-S%d-D%d-MOUSE%d", mPortIndex, mStickIndex, mDirection, mButton);
}
void MouseButtonToAxisDirectionMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarSetString(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str(),
"MouseButtonToAxisDirectionMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str(), mStickIndex);
CVarSetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), mDirection);
CVarSetInteger(StringHelper::Sprintf("%s.MouseButton", mappingCvarKey.c_str()).c_str(), static_cast<int>(mButton));
CVarSave();
}
void MouseButtonToAxisDirectionMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarClear(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.MouseButton", mappingCvarKey.c_str()).c_str());
CVarSave();
}
int8_t MouseButtonToAxisDirectionMapping::GetMappingType() {
return MAPPING_TYPE_MOUSE;
}
} // namespace Ship

View File

@@ -0,0 +1,17 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h"
#include "MouseButtonToAnyMapping.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
class MouseButtonToAxisDirectionMapping final : public MouseButtonToAnyMapping, public ControllerAxisDirectionMapping {
public:
MouseButtonToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex, Direction direction, MouseBtn button);
float GetNormalizedAxisDirectionValue() override;
std::string GetAxisDirectionMappingId() override;
int8_t GetMappingType() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,51 @@
#include "MouseButtonToButtonMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
namespace Ship {
MouseButtonToButtonMapping::MouseButtonToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask, MouseBtn button)
: ControllerInputMapping(PhysicalDeviceType::Mouse), MouseButtonToAnyMapping(button),
ControllerButtonMapping(PhysicalDeviceType::Mouse, portIndex, bitmask) {
}
void MouseButtonToButtonMapping::UpdatePad(CONTROLLERBUTTONS_T& padButtons) {
if (Context::GetInstance()->GetControlDeck()->MouseGameInputBlocked()) {
return;
}
if (!mKeyPressed) {
return;
}
padButtons |= mBitmask;
}
int8_t MouseButtonToButtonMapping::GetMappingType() {
return MAPPING_TYPE_MOUSE;
}
std::string MouseButtonToButtonMapping::GetButtonMappingId() {
return StringHelper::Sprintf("P%d-B%d-MOUSE%d", mPortIndex, mBitmask, mButton);
}
void MouseButtonToButtonMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarSetString(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str(),
"MouseButtonToButtonMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str(), mBitmask);
CVarSetInteger(StringHelper::Sprintf("%s.MouseButton", mappingCvarKey.c_str()).c_str(), static_cast<int>(mButton));
CVarSave();
}
void MouseButtonToButtonMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarClear(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.MouseButton", mappingCvarKey.c_str()).c_str());
CVarSave();
}
} // namespace Ship

View File

@@ -0,0 +1,17 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerButtonMapping.h"
#include "MouseButtonToAnyMapping.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
class MouseButtonToButtonMapping final : public MouseButtonToAnyMapping, public ControllerButtonMapping {
public:
MouseButtonToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask, MouseBtn button);
void UpdatePad(CONTROLLERBUTTONS_T& padButtons) override;
int8_t GetMappingType() override;
std::string GetButtonMappingId() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,22 @@
#include "MouseWheelToAnyMapping.h"
#include "Context.h"
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
namespace Ship {
MouseWheelToAnyMapping::MouseWheelToAnyMapping(WheelDirection wheelDirection)
: ControllerInputMapping(PhysicalDeviceType::Mouse), mWheelDirection(wheelDirection) {
}
MouseWheelToAnyMapping::~MouseWheelToAnyMapping() {
}
std::string MouseWheelToAnyMapping::GetPhysicalInputName() {
return wheelDirectionNames[static_cast<int>(mWheelDirection)];
}
std::string MouseWheelToAnyMapping::GetPhysicalDeviceName() {
return "Mouse";
}
} // namespace Ship

View File

@@ -0,0 +1,17 @@
#pragma once
#include "controller/controldevice/controller/mapping/ControllerInputMapping.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
class MouseWheelToAnyMapping : virtual public ControllerInputMapping {
public:
MouseWheelToAnyMapping(WheelDirection wheelDirection);
~MouseWheelToAnyMapping();
std::string GetPhysicalInputName() override;
std::string GetPhysicalDeviceName() override;
protected:
WheelDirection mWheelDirection;
};
} // namespace Ship

View File

@@ -0,0 +1,56 @@
#include "MouseWheelToAxisDirectionMapping.h"
#include <spdlog/spdlog.h>
#include <cmath>
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
#include "WheelHandler.h"
namespace Ship {
MouseWheelToAxisDirectionMapping::MouseWheelToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex,
Direction direction, WheelDirection wheelDirection)
: ControllerInputMapping(PhysicalDeviceType::Mouse), MouseWheelToAnyMapping(wheelDirection),
ControllerAxisDirectionMapping(PhysicalDeviceType::Mouse, portIndex, stickIndex, direction) {
}
float MouseWheelToAxisDirectionMapping::GetNormalizedAxisDirectionValue() {
if (Context::GetInstance()->GetControlDeck()->MouseGameInputBlocked()) {
return 0.0f;
}
if (WheelHandler::GetInstance()->GetBufferedDirectionValue(mWheelDirection) > 0) {
return MAX_AXIS_RANGE;
} else {
return 0.0f;
}
}
std::string MouseWheelToAxisDirectionMapping::GetAxisDirectionMappingId() {
return StringHelper::Sprintf("P%d-S%d-D%d-WHEEL%d", mPortIndex, mStickIndex, mDirection, mWheelDirection);
}
void MouseWheelToAxisDirectionMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarSetString(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str(),
"MouseWheelToAxisDirectionMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str(), mStickIndex);
CVarSetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), mDirection);
CVarSetInteger(StringHelper::Sprintf("%s.WheelDirection", mappingCvarKey.c_str()).c_str(),
static_cast<int>(mWheelDirection));
CVarSave();
}
void MouseWheelToAxisDirectionMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarClear(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.WheelDirection", mappingCvarKey.c_str()).c_str());
CVarSave();
}
int8_t MouseWheelToAxisDirectionMapping::GetMappingType() {
return MAPPING_TYPE_MOUSE;
}
} // namespace Ship

View File

@@ -0,0 +1,18 @@
#pragma once
#include "MouseWheelToAnyMapping.h"
#include "controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
class MouseWheelToAxisDirectionMapping final : public MouseWheelToAnyMapping, public ControllerAxisDirectionMapping {
public:
MouseWheelToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex, Direction direction,
WheelDirection wheelDirection);
float GetNormalizedAxisDirectionValue() override;
std::string GetAxisDirectionMappingId() override;
int8_t GetMappingType() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,53 @@
#include "MouseWheelToButtonMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "public/bridge/consolevariablebridge.h"
#include "WheelHandler.h"
#include "Context.h"
namespace Ship {
MouseWheelToButtonMapping::MouseWheelToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask,
WheelDirection wheelDirection)
: ControllerInputMapping(PhysicalDeviceType::Mouse), MouseWheelToAnyMapping(wheelDirection),
ControllerButtonMapping(PhysicalDeviceType::Mouse, portIndex, bitmask) {
}
void MouseWheelToButtonMapping::UpdatePad(CONTROLLERBUTTONS_T& padButtons) {
if (Context::GetInstance()->GetControlDeck()->MouseGameInputBlocked()) {
return;
}
WheelDirections directions = WheelHandler::GetInstance()->GetDirections();
if (mWheelDirection == directions.x || mWheelDirection == directions.y) {
padButtons |= mBitmask;
}
}
int8_t MouseWheelToButtonMapping::GetMappingType() {
return MAPPING_TYPE_MOUSE;
}
std::string MouseWheelToButtonMapping::GetButtonMappingId() {
return StringHelper::Sprintf("P%d-B%d-WHEEL%d", mPortIndex, mBitmask, mWheelDirection);
}
void MouseWheelToButtonMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarSetString(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str(),
"MouseWheelToButtonMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str(), mBitmask);
CVarSetInteger(StringHelper::Sprintf("%s.WheelDirection", mappingCvarKey.c_str()).c_str(),
static_cast<int>(mWheelDirection));
CVarSave();
}
void MouseWheelToButtonMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarClear(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.WheelDirection", mappingCvarKey.c_str()).c_str());
CVarSave();
}
} // namespace Ship

View File

@@ -0,0 +1,17 @@
#pragma once
#include "MouseWheelToAnyMapping.h"
#include "controller/controldevice/controller/mapping/ControllerButtonMapping.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
class MouseWheelToButtonMapping final : public MouseWheelToAnyMapping, public ControllerButtonMapping {
public:
MouseWheelToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask, WheelDirection wheelDirection);
void UpdatePad(CONTROLLERBUTTONS_T& padButtons) override;
int8_t GetMappingType() override;
std::string GetButtonMappingId() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,102 @@
#include "WheelHandler.h"
#include "Context.h"
#include <cmath>
namespace Ship {
WheelHandler::WheelHandler() {
mDirections = { LUS_WHEEL_NONE, LUS_WHEEL_NONE };
}
WheelHandler::~WheelHandler() {
}
std::shared_ptr<WheelHandler> WheelHandler::mInstance;
std::shared_ptr<WheelHandler> WheelHandler::GetInstance() {
if (mInstance == nullptr) {
mInstance = std::make_shared<WheelHandler>();
}
return mInstance;
}
void WheelHandler::UpdateAxisBuffer(float* buf, float input) {
static const float LIMIT = 3.0f;
static const float REDUCE_STEP = 1.0f;
if (input != 0.0f) {
// add current input to buffer
*buf += input;
// limit buffer
if (fabs(*buf) > LIMIT) {
*buf = copysignf(LIMIT, *buf);
}
} else if (*buf != 0.0f) {
// reduce buffered value
if (fabs(*buf) <= REDUCE_STEP) {
*buf = 0.0f;
} else {
*buf -= copysignf(REDUCE_STEP, *buf);
}
}
}
void WheelHandler::Update() {
mCoords = Context::GetInstance()->GetWindow()->GetMouseWheel();
UpdateAxisBuffer(&mBufferedCoords.x, mCoords.x);
UpdateAxisBuffer(&mBufferedCoords.y, mCoords.y);
mDirections.x = mDirections.y = LUS_WHEEL_NONE;
if (mCoords.x < 0) {
mDirections.x = LUS_WHEEL_LEFT;
} else if (mCoords.x > 0) {
mDirections.x = LUS_WHEEL_RIGHT;
}
if (mCoords.y < 0) {
mDirections.y = LUS_WHEEL_DOWN;
} else if (mCoords.y > 0) {
mDirections.y = LUS_WHEEL_UP;
}
}
CoordsF WheelHandler::GetCoords() {
return mCoords;
}
WheelDirections WheelHandler::GetDirections() {
return mDirections;
}
float WheelHandler::CalcDirectionValue(CoordsF& coords, WheelDirection direction) {
switch (direction) {
case LUS_WHEEL_LEFT:
if (coords.x < 0) {
return -coords.x;
}
break;
case LUS_WHEEL_RIGHT:
if (coords.x > 0) {
return coords.x;
}
break;
case LUS_WHEEL_DOWN:
if (coords.y < 0) {
return -coords.y;
}
break;
case LUS_WHEEL_UP:
if (coords.y > 0) {
return coords.y;
}
}
return 0.0f;
}
float WheelHandler::GetDirectionValue(WheelDirection direction) {
return CalcDirectionValue(mCoords, direction);
}
float WheelHandler::GetBufferedDirectionValue(WheelDirection direction) {
return CalcDirectionValue(mBufferedCoords, direction);
}
} // namespace Ship

View File

@@ -0,0 +1,36 @@
#pragma once
#include <memory>
#include "window/Window.h"
#include "controller/controldevice/controller/mapping/keyboard/KeyboardScancodes.h"
namespace Ship {
struct WheelDirections {
WheelDirection x;
WheelDirection y;
};
class WheelHandler {
public:
WheelHandler();
~WheelHandler();
static std::shared_ptr<WheelHandler> GetInstance();
void Update();
CoordsF GetCoords();
WheelDirections GetDirections();
float GetDirectionValue(WheelDirection direction);
float GetBufferedDirectionValue(WheelDirection direction);
private:
float CalcDirectionValue(CoordsF& coords, WheelDirection direction);
void UpdateAxisBuffer(float* buf, float input);
static std::shared_ptr<WheelHandler> mInstance;
WheelDirections mDirections;
CoordsF mCoords;
CoordsF mBufferedCoords;
};
} // namespace Ship

View File

@@ -0,0 +1,56 @@
#include "SDLAxisDirectionToAnyMapping.h"
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
namespace Ship {
SDLAxisDirectionToAnyMapping::SDLAxisDirectionToAnyMapping(int32_t sdlControllerAxis, int32_t axisDirection)
: ControllerInputMapping(PhysicalDeviceType::SDLGamepad) {
mControllerAxis = static_cast<SDL_GameControllerAxis>(sdlControllerAxis);
mAxisDirection = static_cast<AxisDirection>(axisDirection);
}
SDLAxisDirectionToAnyMapping::~SDLAxisDirectionToAnyMapping() {
}
std::string SDLAxisDirectionToAnyMapping::GetPhysicalInputName() {
switch (mControllerAxis) {
case SDL_CONTROLLER_AXIS_LEFTX:
return StringHelper::Sprintf("Left Stick %s",
mAxisDirection == NEGATIVE ? ICON_FA_ARROW_LEFT : ICON_FA_ARROW_RIGHT);
case SDL_CONTROLLER_AXIS_LEFTY:
return StringHelper::Sprintf("Left Stick %s",
mAxisDirection == NEGATIVE ? ICON_FA_ARROW_UP : ICON_FA_ARROW_DOWN);
case SDL_CONTROLLER_AXIS_RIGHTX:
return StringHelper::Sprintf("Right Stick %s",
mAxisDirection == NEGATIVE ? ICON_FA_ARROW_LEFT : ICON_FA_ARROW_RIGHT);
case SDL_CONTROLLER_AXIS_RIGHTY:
return StringHelper::Sprintf("Right Stick %s",
mAxisDirection == NEGATIVE ? ICON_FA_ARROW_UP : ICON_FA_ARROW_DOWN);
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
return "LT";
break;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
return "RT";
break;
default:
break;
}
return StringHelper::Sprintf("Axis %d %s", mControllerAxis,
mAxisDirection == NEGATIVE ? ICON_FA_MINUS : ICON_FA_PLUS);
}
std::string SDLAxisDirectionToAnyMapping::GetPhysicalDeviceName() {
return "SDL Gamepad";
}
bool SDLAxisDirectionToAnyMapping::AxisIsTrigger() {
return mControllerAxis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || mControllerAxis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT;
}
bool SDLAxisDirectionToAnyMapping::AxisIsStick() {
return mControllerAxis == SDL_CONTROLLER_AXIS_LEFTX || mControllerAxis == SDL_CONTROLLER_AXIS_LEFTY ||
mControllerAxis == SDL_CONTROLLER_AXIS_RIGHTX || mControllerAxis == SDL_CONTROLLER_AXIS_RIGHTY;
}
} // namespace Ship

View File

@@ -0,0 +1,20 @@
#pragma once
#include "SDLMapping.h"
#include "controller/controldevice/controller/mapping/ControllerInputMapping.h"
namespace Ship {
class SDLAxisDirectionToAnyMapping : virtual public ControllerInputMapping {
public:
SDLAxisDirectionToAnyMapping(int32_t sdlControllerAxis, int32_t axisDirection);
~SDLAxisDirectionToAnyMapping();
std::string GetPhysicalInputName() override;
std::string GetPhysicalDeviceName() override;
bool AxisIsTrigger();
bool AxisIsStick();
protected:
SDL_GameControllerAxis mControllerAxis;
AxisDirection mAxisDirection;
};
} // namespace Ship

View File

@@ -0,0 +1,78 @@
#include "SDLAxisDirectionToAxisDirectionMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
#define MAX_SDL_RANGE (float)INT16_MAX
namespace Ship {
SDLAxisDirectionToAxisDirectionMapping::SDLAxisDirectionToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex,
Direction direction,
int32_t sdlControllerAxis,
int32_t axisDirection)
: ControllerInputMapping(PhysicalDeviceType::SDLGamepad),
ControllerAxisDirectionMapping(PhysicalDeviceType::SDLGamepad, portIndex, stickIndex, direction),
SDLAxisDirectionToAnyMapping(sdlControllerAxis, axisDirection) {
}
float SDLAxisDirectionToAxisDirectionMapping::GetNormalizedAxisDirectionValue() {
if (Context::GetInstance()->GetControlDeck()->GamepadGameInputBlocked()) {
return 0.0f;
}
// todo: i don't like making a vector here, not sure what a better solution is
std::vector<float> normalizedValues = {};
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
const auto axisValue = SDL_GameControllerGetAxis(gamepad, mControllerAxis);
if ((mAxisDirection == POSITIVE && axisValue < 0) || (mAxisDirection == NEGATIVE && axisValue > 0)) {
normalizedValues.push_back(0.0f);
continue;
}
// scale {-32768 ... +32767} to {-MAX_AXIS_RANGE ... +MAX_AXIS_RANGE}
// and use the absolute value of it
normalizedValues.push_back(fabs(axisValue * MAX_AXIS_RANGE / MAX_SDL_RANGE));
}
if (normalizedValues.size() == 0) {
return 0.0f;
}
return *std::max_element(normalizedValues.begin(), normalizedValues.end());
}
std::string SDLAxisDirectionToAxisDirectionMapping::GetAxisDirectionMappingId() {
return StringHelper::Sprintf("P%d-S%d-D%d-SDLA%d-AD%s", mPortIndex, mStickIndex, mDirection, mControllerAxis,
mAxisDirection == 1 ? "P" : "N");
}
void SDLAxisDirectionToAxisDirectionMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarSetString(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str(),
"SDLAxisDirectionToAxisDirectionMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str(), mStickIndex);
CVarSetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), mDirection);
CVarSetInteger(StringHelper::Sprintf("%s.SDLControllerAxis", mappingCvarKey.c_str()).c_str(), mControllerAxis);
CVarSetInteger(StringHelper::Sprintf("%s.AxisDirection", mappingCvarKey.c_str()).c_str(), mAxisDirection);
CVarSave();
}
void SDLAxisDirectionToAxisDirectionMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarClear(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.SDLControllerAxis", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.AxisDirection", mappingCvarKey.c_str()).c_str());
CVarSave();
}
int8_t SDLAxisDirectionToAxisDirectionMapping::GetMappingType() {
return MAPPING_TYPE_GAMEPAD;
}
} // namespace Ship

View File

@@ -0,0 +1,16 @@
#include "controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h"
#include "SDLAxisDirectionToAnyMapping.h"
namespace Ship {
class SDLAxisDirectionToAxisDirectionMapping final : public ControllerAxisDirectionMapping,
public SDLAxisDirectionToAnyMapping {
public:
SDLAxisDirectionToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex, Direction direction,
int32_t sdlControllerAxis, int32_t axisDirection);
float GetNormalizedAxisDirectionValue() override;
std::string GetAxisDirectionMappingId() override;
int8_t GetMappingType() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,74 @@
#include "SDLAxisDirectionToButtonMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
namespace Ship {
SDLAxisDirectionToButtonMapping::SDLAxisDirectionToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask,
int32_t sdlControllerAxis, int32_t axisDirection)
: ControllerInputMapping(PhysicalDeviceType::SDLGamepad),
ControllerButtonMapping(PhysicalDeviceType::SDLGamepad, portIndex, bitmask),
SDLAxisDirectionToAnyMapping(sdlControllerAxis, axisDirection) {
}
void SDLAxisDirectionToButtonMapping::UpdatePad(CONTROLLERBUTTONS_T& padButtons) {
if (Context::GetInstance()->GetControlDeck()->GamepadGameInputBlocked()) {
return;
}
int32_t axisThresholdPercentage = 25;
if (AxisIsStick()) {
axisThresholdPercentage = Ship::Context::GetInstance()
->GetControlDeck()
->GetGlobalSDLDeviceSettings()
->GetStickAxisThresholdPercentage();
} else if (AxisIsTrigger()) {
axisThresholdPercentage = Ship::Context::GetInstance()
->GetControlDeck()
->GetGlobalSDLDeviceSettings()
->GetTriggerAxisThresholdPercentage();
}
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
const auto axisValue = SDL_GameControllerGetAxis(gamepad, mControllerAxis);
auto axisMinValue = SDL_JOYSTICK_AXIS_MAX * (axisThresholdPercentage / 100.0f);
if ((mAxisDirection == POSITIVE && axisValue > axisMinValue) ||
(mAxisDirection == NEGATIVE && axisValue < -axisMinValue)) {
padButtons |= mBitmask;
}
}
}
int8_t SDLAxisDirectionToButtonMapping::GetMappingType() {
return MAPPING_TYPE_GAMEPAD;
}
std::string SDLAxisDirectionToButtonMapping::GetButtonMappingId() {
return StringHelper::Sprintf("P%d-B%d-SDLA%d-AD%s", mPortIndex, mBitmask, mControllerAxis,
mAxisDirection == 1 ? "P" : "N");
}
void SDLAxisDirectionToButtonMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarSetString(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str(),
"SDLAxisDirectionToButtonMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str(), mBitmask);
CVarSetInteger(StringHelper::Sprintf("%s.SDLControllerAxis", mappingCvarKey.c_str()).c_str(), mControllerAxis);
CVarSetInteger(StringHelper::Sprintf("%s.AxisDirection", mappingCvarKey.c_str()).c_str(), mAxisDirection);
CVarSave();
}
void SDLAxisDirectionToButtonMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarClear(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.SDLControllerAxis", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.AxisDirection", mappingCvarKey.c_str()).c_str());
CVarSave();
}
} // namespace Ship

View File

@@ -0,0 +1,15 @@
#include "controller/controldevice/controller/mapping/ControllerButtonMapping.h"
#include "SDLAxisDirectionToAnyMapping.h"
namespace Ship {
class SDLAxisDirectionToButtonMapping final : public ControllerButtonMapping, public SDLAxisDirectionToAnyMapping {
public:
SDLAxisDirectionToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask, int32_t sdlControllerAxis,
int32_t axisDirection);
void UpdatePad(CONTROLLERBUTTONS_T& padButtons) override;
int8_t GetMappingType() override;
std::string GetButtonMappingId() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,72 @@
#include "SDLButtonToAnyMapping.h"
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
namespace Ship {
SDLButtonToAnyMapping::SDLButtonToAnyMapping(int32_t sdlControllerButton)
: ControllerInputMapping(PhysicalDeviceType::SDLGamepad) {
mControllerButton = static_cast<SDL_GameControllerButton>(sdlControllerButton);
}
SDLButtonToAnyMapping::~SDLButtonToAnyMapping() {
}
std::string SDLButtonToAnyMapping::GetPhysicalInputName() {
switch (mControllerButton) {
case SDL_CONTROLLER_BUTTON_A:
return "A";
case SDL_CONTROLLER_BUTTON_B:
return "B";
case SDL_CONTROLLER_BUTTON_X:
return "X";
case SDL_CONTROLLER_BUTTON_Y:
return "Y";
case SDL_CONTROLLER_BUTTON_BACK:
return "View";
case SDL_CONTROLLER_BUTTON_GUIDE:
return "Xbox";
case SDL_CONTROLLER_BUTTON_START:
return StringHelper::Sprintf("%s", ICON_FA_BARS);
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
return "LS";
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
return "RS";
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
return "LB";
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
return "RB";
case SDL_CONTROLLER_BUTTON_DPAD_UP:
return StringHelper::Sprintf("D-Pad %s", ICON_FA_ARROW_UP);
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
return StringHelper::Sprintf("D-Pad %s", ICON_FA_ARROW_DOWN);
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
return StringHelper::Sprintf("D-Pad %s", ICON_FA_ARROW_LEFT);
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
return StringHelper::Sprintf("D-Pad %s", ICON_FA_ARROW_RIGHT);
case SDL_CONTROLLER_BUTTON_MISC1:
return "Share"; /* Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button,
Amazon Luna microphone button */
case SDL_CONTROLLER_BUTTON_PADDLE1:
return "P1";
case SDL_CONTROLLER_BUTTON_PADDLE2:
return "P2";
case SDL_CONTROLLER_BUTTON_PADDLE3:
return "P3";
case SDL_CONTROLLER_BUTTON_PADDLE4:
return "P4";
default:
break;
}
return GetGenericButtonName();
}
std::string SDLButtonToAnyMapping::GetGenericButtonName() {
return StringHelper::Sprintf("B%d", mControllerButton);
}
std::string SDLButtonToAnyMapping::GetPhysicalDeviceName() {
return "SDL Gamepad";
}
} // namespace Ship

View File

@@ -0,0 +1,20 @@
#pragma once
#include "SDLMapping.h"
#include "controller/controldevice/controller/mapping/ControllerInputMapping.h"
namespace Ship {
class SDLButtonToAnyMapping : virtual public ControllerInputMapping {
public:
SDLButtonToAnyMapping(int32_t sdlControllerButton);
~SDLButtonToAnyMapping();
std::string GetPhysicalInputName() override;
std::string GetPhysicalDeviceName() override;
protected:
SDL_GameControllerButton mControllerButton;
private:
std::string GetGenericButtonName();
};
} // namespace Ship

View File

@@ -0,0 +1,60 @@
#include "SDLButtonToAxisDirectionMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
#define MAX_SDL_RANGE (float)INT16_MAX
namespace Ship {
SDLButtonToAxisDirectionMapping::SDLButtonToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex,
Direction direction, int32_t sdlControllerButton)
: ControllerInputMapping(PhysicalDeviceType::SDLGamepad),
ControllerAxisDirectionMapping(PhysicalDeviceType::SDLGamepad, portIndex, stickIndex, direction),
SDLButtonToAnyMapping(sdlControllerButton) {
}
float SDLButtonToAxisDirectionMapping::GetNormalizedAxisDirectionValue() {
if (Context::GetInstance()->GetControlDeck()->GamepadGameInputBlocked()) {
return 0.0f;
}
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
if (SDL_GameControllerGetButton(gamepad, mControllerButton)) {
return MAX_AXIS_RANGE;
}
}
return 0.0f;
}
std::string SDLButtonToAxisDirectionMapping::GetAxisDirectionMappingId() {
return StringHelper::Sprintf("P%d-S%d-D%d-SDLB%d", mPortIndex, mStickIndex, mDirection, mControllerButton);
}
void SDLButtonToAxisDirectionMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarSetString(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str(),
"SDLButtonToAxisDirectionMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str(), mStickIndex);
CVarSetInteger(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str(), mDirection);
CVarSetInteger(StringHelper::Sprintf("%s.SDLControllerButton", mappingCvarKey.c_str()).c_str(), mControllerButton);
CVarSave();
}
void SDLButtonToAxisDirectionMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".AxisDirectionMappings." + GetAxisDirectionMappingId();
CVarClear(StringHelper::Sprintf("%s.Stick", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Direction", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.AxisDirectionMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.SDLControllerButton", mappingCvarKey.c_str()).c_str());
CVarSave();
}
int8_t SDLButtonToAxisDirectionMapping::GetMappingType() {
return MAPPING_TYPE_GAMEPAD;
}
} // namespace Ship

View File

@@ -0,0 +1,15 @@
#include "controller/controldevice/controller/mapping/ControllerAxisDirectionMapping.h"
#include "SDLButtonToAnyMapping.h"
namespace Ship {
class SDLButtonToAxisDirectionMapping final : public ControllerAxisDirectionMapping, public SDLButtonToAnyMapping {
public:
SDLButtonToAxisDirectionMapping(uint8_t portIndex, StickIndex stickIndex, Direction direction,
int32_t sdlControllerButton);
float GetNormalizedAxisDirectionValue() override;
std::string GetAxisDirectionMappingId() override;
int8_t GetMappingType() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,55 @@
#include "SDLButtonToButtonMapping.h"
#include <spdlog/spdlog.h>
#include "utils/StringHelper.h"
#include "window/gui/IconsFontAwesome4.h"
#include "public/bridge/consolevariablebridge.h"
#include "Context.h"
namespace Ship {
SDLButtonToButtonMapping::SDLButtonToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask,
int32_t sdlControllerButton)
: ControllerInputMapping(PhysicalDeviceType::SDLGamepad), SDLButtonToAnyMapping(sdlControllerButton),
ControllerButtonMapping(PhysicalDeviceType::SDLGamepad, portIndex, bitmask) {
}
void SDLButtonToButtonMapping::UpdatePad(CONTROLLERBUTTONS_T& padButtons) {
if (Context::GetInstance()->GetControlDeck()->GamepadGameInputBlocked()) {
return;
}
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
if (SDL_GameControllerGetButton(gamepad, mControllerButton)) {
padButtons |= mBitmask;
return;
}
}
}
int8_t SDLButtonToButtonMapping::GetMappingType() {
return MAPPING_TYPE_GAMEPAD;
}
std::string SDLButtonToButtonMapping::GetButtonMappingId() {
return StringHelper::Sprintf("P%d-B%d-SDLB%d", mPortIndex, mBitmask, mControllerButton);
}
void SDLButtonToButtonMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarSetString(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str(),
"SDLButtonToButtonMapping");
CVarSetInteger(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str(), mBitmask);
CVarSetInteger(StringHelper::Sprintf("%s.SDLControllerButton", mappingCvarKey.c_str()).c_str(), mControllerButton);
CVarSave();
}
void SDLButtonToButtonMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".ButtonMappings." + GetButtonMappingId();
CVarClear(StringHelper::Sprintf("%s.ButtonMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Bitmask", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.SDLControllerButton", mappingCvarKey.c_str()).c_str());
CVarSave();
}
} // namespace Ship

View File

@@ -0,0 +1,14 @@
#include "controller/controldevice/controller/mapping/ControllerButtonMapping.h"
#include "SDLButtonToAnyMapping.h"
namespace Ship {
class SDLButtonToButtonMapping final : public SDLButtonToAnyMapping, public ControllerButtonMapping {
public:
SDLButtonToButtonMapping(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask, int32_t sdlControllerButton);
void UpdatePad(CONTROLLERBUTTONS_T& padButtons) override;
int8_t GetMappingType() override;
std::string GetButtonMappingId() override;
void SaveToConfig() override;
void EraseFromConfig() override;
};
} // namespace Ship

View File

@@ -0,0 +1,162 @@
#include "SDLGyroMapping.h"
#include "controller/controldevice/controller/mapping/ControllerGyroMapping.h"
#include <spdlog/spdlog.h>
#include "Context.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
namespace Ship {
SDLGyroMapping::SDLGyroMapping(uint8_t portIndex, float sensitivity, float neutralPitch, float neutralYaw,
float neutralRoll)
: ControllerInputMapping(PhysicalDeviceType::SDLGamepad),
ControllerGyroMapping(PhysicalDeviceType::SDLGamepad, portIndex, sensitivity), mNeutralPitch(neutralPitch),
mNeutralYaw(neutralYaw), mNeutralRoll(neutralRoll) {
}
void SDLGyroMapping::Recalibrate() {
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
if (!SDL_GameControllerHasSensor(gamepad, SDL_SENSOR_GYRO)) {
#ifndef __ANDROID__
continue;
#endif
}
// just use gyro on the first gyro supported device we find
float gyroData[3];
#ifdef __ANDROID__
GetAndroidGyroData(gyroData);
#else
SDL_GameControllerSetSensorEnabled(gamepad, SDL_SENSOR_GYRO, SDL_TRUE);
SDL_GameControllerGetSensorData(gamepad, SDL_SENSOR_GYRO, gyroData, 3);
#endif
mNeutralPitch = gyroData[0];
mNeutralYaw = gyroData[1];
mNeutralRoll = gyroData[2];
return;
}
mNeutralPitch = 0;
mNeutralYaw = 0;
mNeutralRoll = 0;
}
void SDLGyroMapping::UpdatePad(float& x, float& y) {
if (Context::GetInstance()->GetControlDeck()->GamepadGameInputBlocked()) {
x = 0;
y = 0;
return;
}
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
if (!SDL_GameControllerHasSensor(gamepad, SDL_SENSOR_GYRO)) {
#ifndef __ANDROID__
continue
#endif
}
// just use gyro on the first gyro supported device we find
float gyroData[3];
#ifdef __ANDROID__
GetAndroidGyroData(gyroData);
#else
SDL_GameControllerSetSensorEnabled(gamepad, SDL_SENSOR_GYRO, SDL_TRUE);
SDL_GameControllerGetSensorData(gamepad, SDL_SENSOR_GYRO, gyroData, 3);
#endif
x = (gyroData[0] - mNeutralPitch) * mSensitivity;
y = (gyroData[1] - mNeutralYaw) * mSensitivity;
return;
}
// if we didn't find a gyro device zero everything out
x = 0;
y = 0;
}
std::string SDLGyroMapping::GetGyroMappingId() {
return StringHelper::Sprintf("P%d", mPortIndex);
}
void SDLGyroMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".GyroMappings." + GetGyroMappingId();
CVarSetString(StringHelper::Sprintf("%s.GyroMappingClass", mappingCvarKey.c_str()).c_str(), "SDLGyroMapping");
CVarSetFloat(StringHelper::Sprintf("%s.Sensitivity", mappingCvarKey.c_str()).c_str(), mSensitivity);
CVarSetFloat(StringHelper::Sprintf("%s.NeutralPitch", mappingCvarKey.c_str()).c_str(), mNeutralPitch);
CVarSetFloat(StringHelper::Sprintf("%s.NeutralYaw", mappingCvarKey.c_str()).c_str(), mNeutralYaw);
CVarSetFloat(StringHelper::Sprintf("%s.NeutralRoll", mappingCvarKey.c_str()).c_str(), mNeutralRoll);
CVarSave();
}
void SDLGyroMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".GyroMappings." + GetGyroMappingId();
CVarClear(StringHelper::Sprintf("%s.GyroMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.Sensitivity", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.NeutralPitch", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.NeutralYaw", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.NeutralRoll", mappingCvarKey.c_str()).c_str());
CVarSave();
}
#ifdef __ANDROID__
void SDLGyroMapping::GetAndroidGyroData(float gyroData[3]){
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
if (!SDL_GameControllerHasSensor(gamepad, SDL_SENSOR_GYRO)) {
continue;
}
SDL_GameControllerSetSensorEnabled(gamepad, SDL_SENSOR_GYRO, SDL_TRUE);
SDL_GameControllerGetSensorData(gamepad, SDL_SENSOR_GYRO, gyroData, 3);
return;
}
if(gyroSensor == nullptr){ // populate gyroSensor variable if it hasn't been created yet
for (int i = 0; i<SDL_NumSensors();i++) {
if (SDL_SensorGetDeviceType(i) == SDL_SENSOR_GYRO) {
gyroSensor = SDL_SensorOpen(i);
break;
}
}
}
SDL_SensorUpdate();
SDL_SensorGetData(gyroSensor, gyroData, 3);
float gyroX = gyroData[0];
float gyroY = gyroData[1];
switch(SDL_GetDisplayOrientation(0)){
case(SDL_ORIENTATION_PORTRAIT):
// nothing to do
break;
case(SDL_ORIENTATION_PORTRAIT_FLIPPED):
gyroData[0] = -gyroX;
gyroData[1] = -gyroY;
break;
case(SDL_ORIENTATION_LANDSCAPE):
gyroData[0] = -gyroY;
gyroData[1] = gyroX;
break;
case(SDL_ORIENTATION_LANDSCAPE_FLIPPED):
gyroData[0] = gyroY;
gyroData[1] = -gyroX;
break;
case(SDL_ORIENTATION_UNKNOWN):
// nothing to do
break;
}
}
#endif
std::string SDLGyroMapping::GetPhysicalDeviceName() {
return "SDL Gamepad";
}
} // namespace Ship

View File

@@ -0,0 +1,25 @@
#include "controller/controldevice/controller/mapping/ControllerGyroMapping.h"
#include "SDLMapping.h"
namespace Ship {
class SDLGyroMapping final : public ControllerGyroMapping {
public:
SDLGyroMapping(uint8_t portIndex, float sensitivity, float neutralPitch, float neutralYaw, float neutralRoll);
void UpdatePad(float& x, float& y) override;
void SaveToConfig() override;
void EraseFromConfig() override;
void Recalibrate() override;
std::string GetGyroMappingId() override;
std::string GetPhysicalDeviceName() override;
private:
#ifdef __ANDROID__
SDL_Sensor* gyroSensor = nullptr;
void GetAndroidGyroData(float gyroData[3]);
#endif
float mNeutralPitch;
float mNeutralYaw;
float mNeutralRoll;
};
} // namespace Ship

View File

@@ -0,0 +1,57 @@
#include "SDLLEDMapping.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include "Context.h"
namespace Ship {
SDLLEDMapping::SDLLEDMapping(uint8_t portIndex, uint8_t colorSource, Color_RGB8 savedColor)
: ControllerLEDMapping(PhysicalDeviceType::SDLGamepad, portIndex, colorSource, savedColor) {
}
void SDLLEDMapping::SetLEDColor(Color_RGB8 color) {
if (mColorSource == LED_COLOR_SOURCE_OFF) {
color = { 0, 0, 0 };
}
if (mColorSource == LED_COLOR_SOURCE_SET) {
color = mSavedColor;
}
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
if (!SDL_GameControllerHasLED(gamepad)) {
continue;
}
SDL_JoystickSetLED(SDL_GameControllerGetJoystick(gamepad), color.r, color.g, color.b);
}
}
std::string SDLLEDMapping::GetLEDMappingId() {
return StringHelper::Sprintf("P%d", mPortIndex);
}
void SDLLEDMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".LEDMappings." + GetLEDMappingId();
CVarSetString(StringHelper::Sprintf("%s.LEDMappingClass", mappingCvarKey.c_str()).c_str(), "SDLLEDMapping");
CVarSetInteger(StringHelper::Sprintf("%s.ColorSource", mappingCvarKey.c_str()).c_str(), mColorSource);
CVarSetColor24(StringHelper::Sprintf("%s.SavedColor", mappingCvarKey.c_str()).c_str(), mSavedColor);
CVarSave();
}
void SDLLEDMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".LEDMappings." + GetLEDMappingId();
CVarClear(StringHelper::Sprintf("%s.LEDMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.ColorSource", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.SavedColor", mappingCvarKey.c_str()).c_str());
CVarSave();
}
std::string SDLLEDMapping::GetPhysicalDeviceName() {
return "SDL Gamepad";
}
} // namespace Ship

View File

@@ -0,0 +1,17 @@
#include "controller/controldevice/controller/mapping/ControllerLEDMapping.h"
#include "SDLMapping.h"
namespace Ship {
class SDLLEDMapping final : public ControllerLEDMapping {
public:
SDLLEDMapping(uint8_t portIndex, uint8_t colorSource, Color_RGB8 savedColor);
void SetLEDColor(Color_RGB8 color) override;
std::string GetLEDMappingId() override;
void SaveToConfig() override;
void EraseFromConfig() override;
std::string GetPhysicalDeviceName() override;
};
} // namespace Ship

View File

@@ -0,0 +1,10 @@
#pragma once
#include <SDL2/SDL.h>
namespace Ship {
enum Axis { X = 0, Y = 1 };
enum AxisDirection { NEGATIVE = -1, POSITIVE = 1 };
} // namespace Ship

View File

@@ -0,0 +1,69 @@
#include "SDLRumbleMapping.h"
#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include "Context.h"
namespace Ship {
SDLRumbleMapping::SDLRumbleMapping(uint8_t portIndex, uint8_t lowFrequencyIntensityPercentage,
uint8_t highFrequencyIntensityPercentage)
: ControllerRumbleMapping(PhysicalDeviceType::SDLGamepad, portIndex, lowFrequencyIntensityPercentage,
highFrequencyIntensityPercentage) {
SetLowFrequencyIntensity(lowFrequencyIntensityPercentage);
SetHighFrequencyIntensity(highFrequencyIntensityPercentage);
}
void SDLRumbleMapping::StartRumble() {
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
SDL_GameControllerRumble(gamepad, mLowFrequencyIntensity, mHighFrequencyIntensity, 0);
}
}
void SDLRumbleMapping::StopRumble() {
for (const auto& [instanceId, gamepad] :
Context::GetInstance()->GetControlDeck()->GetConnectedPhysicalDeviceManager()->GetConnectedSDLGamepadsForPort(
mPortIndex)) {
SDL_GameControllerRumble(gamepad, 0, 0, 0);
}
}
void SDLRumbleMapping::SetLowFrequencyIntensity(uint8_t intensityPercentage) {
mLowFrequencyIntensityPercentage = intensityPercentage;
mLowFrequencyIntensity = UINT16_MAX * (intensityPercentage / 100.0f);
}
void SDLRumbleMapping::SetHighFrequencyIntensity(uint8_t intensityPercentage) {
mHighFrequencyIntensityPercentage = intensityPercentage;
mHighFrequencyIntensity = UINT16_MAX * (intensityPercentage / 100.0f);
}
std::string SDLRumbleMapping::GetRumbleMappingId() {
return StringHelper::Sprintf("P%d", mPortIndex);
}
void SDLRumbleMapping::SaveToConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".RumbleMappings." + GetRumbleMappingId();
CVarSetString(StringHelper::Sprintf("%s.RumbleMappingClass", mappingCvarKey.c_str()).c_str(), "SDLRumbleMapping");
CVarSetInteger(StringHelper::Sprintf("%s.LowFrequencyIntensity", mappingCvarKey.c_str()).c_str(),
mLowFrequencyIntensityPercentage);
CVarSetInteger(StringHelper::Sprintf("%s.HighFrequencyIntensity", mappingCvarKey.c_str()).c_str(),
mHighFrequencyIntensityPercentage);
CVarSave();
}
void SDLRumbleMapping::EraseFromConfig() {
const std::string mappingCvarKey = CVAR_PREFIX_CONTROLLERS ".RumbleMappings." + GetRumbleMappingId();
CVarClear(StringHelper::Sprintf("%s.RumbleMappingClass", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.LowFrequencyIntensity", mappingCvarKey.c_str()).c_str());
CVarClear(StringHelper::Sprintf("%s.HighFrequencyIntensity", mappingCvarKey.c_str()).c_str());
CVarSave();
}
std::string SDLRumbleMapping::GetPhysicalDeviceName() {
return "SDL Gamepad";
}
} // namespace Ship

View File

@@ -0,0 +1,25 @@
#include "controller/controldevice/controller/mapping/ControllerRumbleMapping.h"
#include "SDLMapping.h"
namespace Ship {
class SDLRumbleMapping final : public ControllerRumbleMapping {
public:
SDLRumbleMapping(uint8_t portIndex, uint8_t lowFrequencyIntensityPercentage,
uint8_t highFrequencyIntensityPercentage);
void StartRumble() override;
void StopRumble() override;
void SetLowFrequencyIntensity(uint8_t intensityPercentage) override;
void SetHighFrequencyIntensity(uint8_t intensityPercentage) override;
std::string GetRumbleMappingId() override;
void SaveToConfig() override;
void EraseFromConfig() override;
std::string GetPhysicalDeviceName() override;
private:
uint16_t mLowFrequencyIntensity;
uint16_t mHighFrequencyIntensity;
};
} // namespace Ship

View File

@@ -0,0 +1,85 @@
#include "ConnectedPhysicalDeviceManager.h"
#if defined(__ANDROID__)
#include "port/mobile/MobileImpl.h"
#endif
namespace Ship {
ConnectedPhysicalDeviceManager::ConnectedPhysicalDeviceManager() {
}
ConnectedPhysicalDeviceManager::~ConnectedPhysicalDeviceManager() {
}
std::unordered_map<int32_t, SDL_GameController*>
ConnectedPhysicalDeviceManager::GetConnectedSDLGamepadsForPort(uint8_t portIndex) {
std::unordered_map<int32_t, SDL_GameController*> result;
for (const auto& [instanceId, gamepad] : mConnectedSDLGamepads) {
if (!PortIsIgnoringInstanceId(portIndex, instanceId)) {
result[instanceId] = gamepad;
}
}
return result;
}
std::unordered_map<int32_t, std::string> ConnectedPhysicalDeviceManager::GetConnectedSDLGamepadNames() {
return mConnectedSDLGamepadNames;
}
std::unordered_set<int32_t> ConnectedPhysicalDeviceManager::GetIgnoredInstanceIdsForPort(uint8_t portIndex) {
return mIgnoredInstanceIds[portIndex];
}
bool ConnectedPhysicalDeviceManager::PortIsIgnoringInstanceId(uint8_t portIndex, int32_t instanceId) {
return GetIgnoredInstanceIdsForPort(portIndex).contains(instanceId);
}
void ConnectedPhysicalDeviceManager::IgnoreInstanceIdForPort(uint8_t portIndex, int32_t instanceId) {
mIgnoredInstanceIds[portIndex].insert(instanceId);
}
void ConnectedPhysicalDeviceManager::UnignoreInstanceIdForPort(uint8_t portIndex, int32_t instanceId) {
mIgnoredInstanceIds[portIndex].erase(instanceId);
}
void ConnectedPhysicalDeviceManager::HandlePhysicalDeviceConnect(int32_t sdlDeviceIndex) {
RefreshConnectedSDLGamepads();
}
void ConnectedPhysicalDeviceManager::HandlePhysicalDeviceDisconnect(int32_t sdlJoystickInstanceId) {
RefreshConnectedSDLGamepads();
}
void ConnectedPhysicalDeviceManager::RefreshConnectedSDLGamepads() {
mConnectedSDLGamepads.clear();
mConnectedSDLGamepadNames.clear();
for (int32_t i = 0; i < SDL_NumJoysticks(); i++) {
#if defined(__ANDROID__)
// skip if invalid SDL Gamepad
const char* gamepadName = SDL_GameControllerNameForIndex(i);
if (Ship::Mobile::IsInvalidGamepad(gamepadName))
{ continue; }
#endif
// skip if this SDL joystick isn't a Gamepad
if (!SDL_IsGameController(i)) {
continue;
}
auto gamepad = SDL_GameControllerOpen(i);
auto instanceId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepad));
auto name = SDL_GameControllerName(gamepad);
mConnectedSDLGamepads[instanceId] = gamepad;
mConnectedSDLGamepadNames[instanceId] = name;
for (uint8_t port = 1; port < 4; port++) {
mIgnoredInstanceIds[port].insert(instanceId);
}
}
}
} // namespace Ship

Some files were not shown because too many files have changed in this diff Show More