first commit
This commit is contained in:
339
libultraship/src/CMakeLists.txt
Normal file
339
libultraship/src/CMakeLists.txt
Normal 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()
|
||||
487
libultraship/src/Context.cpp
Normal file
487
libultraship/src/Context.cpp
Normal 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
|
||||
96
libultraship/src/Context.h
Normal file
96
libultraship/src/Context.h
Normal 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
|
||||
63
libultraship/src/audio/Audio.cpp
Normal file
63
libultraship/src/audio/Audio.cpp
Normal 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
|
||||
32
libultraship/src/audio/Audio.h
Normal file
32
libultraship/src/audio/Audio.h
Normal 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
|
||||
49
libultraship/src/audio/AudioPlayer.cpp
Normal file
49
libultraship/src/audio/AudioPlayer.cpp
Normal 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
|
||||
58
libultraship/src/audio/AudioPlayer.h
Normal file
58
libultraship/src/audio/AudioPlayer.h
Normal 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"
|
||||
43
libultraship/src/audio/SDLAudioPlayer.cpp
Normal file
43
libultraship/src/audio/SDLAudioPlayer.cpp
Normal 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
|
||||
22
libultraship/src/audio/SDLAudioPlayer.h
Normal file
22
libultraship/src/audio/SDLAudioPlayer.h
Normal 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
|
||||
183
libultraship/src/audio/WasapiAudioPlayer.cpp
Normal file
183
libultraship/src/audio/WasapiAudioPlayer.cpp
Normal 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
|
||||
46
libultraship/src/audio/WasapiAudioPlayer.h
Normal file
46
libultraship/src/audio/WasapiAudioPlayer.h
Normal 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
|
||||
317
libultraship/src/config/Config.cpp
Normal file
317
libultraship/src/config/Config.cpp
Normal 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
|
||||
102
libultraship/src/config/Config.h
Normal file
102
libultraship/src/config/Config.h
Normal 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
|
||||
374
libultraship/src/config/ConsoleVariable.cpp
Normal file
374
libultraship/src/config/ConsoleVariable.cpp
Normal 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
|
||||
63
libultraship/src/config/ConsoleVariable.h
Normal file
63
libultraship/src/config/ConsoleVariable.h
Normal 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
|
||||
164
libultraship/src/controller/controldeck/ControlDeck.cpp
Normal file
164
libultraship/src/controller/controldeck/ControlDeck.cpp
Normal 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
|
||||
64
libultraship/src/controller/controldeck/ControlDeck.h
Normal file
64
libultraship/src/controller/controldeck/ControlDeck.h
Normal 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
|
||||
30
libultraship/src/controller/controldeck/ControlPort.cpp
Normal file
30
libultraship/src/controller/controldeck/ControlPort.cpp
Normal 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
|
||||
24
libultraship/src/controller/controldeck/ControlPort.h
Normal file
24
libultraship/src/controller/controldeck/ControlPort.h
Normal 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
|
||||
10
libultraship/src/controller/controldevice/ControlDevice.cpp
Normal file
10
libultraship/src/controller/controldevice/ControlDevice.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "ControlDevice.h"
|
||||
|
||||
namespace Ship {
|
||||
ControlDevice::ControlDevice(uint8_t portIndex) : mPortIndex(portIndex) {
|
||||
}
|
||||
|
||||
ControlDevice::~ControlDevice() {
|
||||
}
|
||||
|
||||
} // namespace Ship
|
||||
14
libultraship/src/controller/controldevice/ControlDevice.h
Normal file
14
libultraship/src/controller/controldevice/ControlDevice.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user