(Android) Added initial touch overlay support

This commit is contained in:
Leon Styhre 2024-01-13 16:14:46 +01:00
parent d50a062f5e
commit e91512a519
14 changed files with 253 additions and 4 deletions

1
.gitignore vendored
View file

@ -36,6 +36,7 @@ es-de.worker.js
# Android # Android
/android /android
/logback.log /logback.log
es-core/src/InputOverlay.*
es-core/src/utils/PlatformUtilAndroid.* es-core/src/utils/PlatformUtilAndroid.*
# AppImage # AppImage

View file

@ -1144,6 +1144,20 @@ void GuiMenu::openInputDeviceOptions()
} }
}); });
#if defined(__ANDROID__)
// Whether to enable the touch overlay.
auto inputTouchOverlay = std::make_shared<SwitchComponent>();
inputTouchOverlay->setState(Settings::getInstance()->getBool("InputTouchOverlay"));
s->addWithLabel("ENABLE TOUCH OVERLAY", inputTouchOverlay);
s->addSaveFunc([inputTouchOverlay, s] {
if (Settings::getInstance()->getBool("InputTouchOverlay") !=
inputTouchOverlay->getState()) {
Settings::getInstance()->setBool("InputTouchOverlay", inputTouchOverlay->getState());
s->setNeedsSaving();
}
});
#endif
// Whether to only accept input from the first controller. // Whether to only accept input from the first controller.
auto inputOnlyFirstController = std::make_shared<SwitchComponent>(); auto inputOnlyFirstController = std::make_shared<SwitchComponent>();
inputOnlyFirstController->setState( inputOnlyFirstController->setState(

View file

@ -38,6 +38,7 @@
#include <SDL2/SDL_timer.h> #include <SDL2/SDL_timer.h>
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include "InputOverlay.h"
#include "utils/PlatformUtilAndroid.h" #include "utils/PlatformUtilAndroid.h"
#endif #endif
@ -890,6 +891,8 @@ int main(int argc, char* argv[])
} }
#if defined(__ANDROID__) #if defined(__ANDROID__)
InputOverlay::getInstance();
LOG(LogDebug) << "Android API level: " << SDL_GetAndroidSDKVersion(); LOG(LogDebug) << "Android API level: " << SDL_GetAndroidSDKVersion();
Utils::Platform::Android::printDeviceInfo(); Utils::Platform::Android::printDeviceInfo();
int storageState {SDL_AndroidGetExternalStorageState()}; int storageState {SDL_AndroidGetExternalStorageState()};

View file

@ -170,7 +170,9 @@ set(CORE_SOURCES
) )
if(ANDROID) if(ANDROID)
set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/InputOverlay.h)
set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtilAndroid.h) set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtilAndroid.h)
set(CORE_HEADERS ${CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/src/InputOverlay.cpp)
set(CORE_SOURCES ${CORE_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtilAndroid.cpp) set(CORE_SOURCES ${CORE_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/src/utils/PlatformUtilAndroid.cpp)
endif() endif()

View file

@ -17,12 +17,14 @@
#include <vector> #include <vector>
#define DEVICE_KEYBOARD -1 #define DEVICE_KEYBOARD -1
#define DEVICE_CEC -2 #define DEVICE_TOUCH -2
#define DEVICE_CEC -3
enum InputType { enum InputType {
TYPE_AXIS, TYPE_AXIS,
TYPE_BUTTON, TYPE_BUTTON,
TYPE_KEY, TYPE_KEY,
TYPE_TOUCH,
TYPE_CEC_BUTTON, TYPE_CEC_BUTTON,
TYPE_COUNT TYPE_COUNT
}; };

View file

@ -22,7 +22,8 @@
#include <pugixml.hpp> #include <pugixml.hpp>
#define KEYBOARD_GUID_STRING "-1" #define KEYBOARD_GUID_STRING "-1"
#define CEC_GUID_STRING "-2" #define TOUCH_GUID_STRING "-2"
#define CEC_GUID_STRING "-3"
namespace namespace
{ {
@ -32,7 +33,12 @@ namespace
InputManager::InputManager() noexcept InputManager::InputManager() noexcept
: mWindow {Window::getInstance()} : mWindow {Window::getInstance()}
#if defined(__ANDROID__)
, mInputOverlay {InputOverlay::getInstance()}
#endif
, mKeyboardInputConfig {nullptr} , mKeyboardInputConfig {nullptr}
, mTouchInputConfig {nullptr}
, mCECInputConfig {nullptr}
{ {
} }
@ -81,6 +87,11 @@ void InputManager::init()
LOG(LogInfo) << "Added keyboard with default configuration"; LOG(LogInfo) << "Added keyboard with default configuration";
} }
#if defined(__ANDROID__)
mTouchInputConfig = std::make_unique<InputConfig>(DEVICE_TOUCH, "Touch", TOUCH_GUID_STRING);
loadTouchConfig();
#endif
// Load optional controller mappings. Normally the supported controllers should be compiled // Load optional controller mappings. Normally the supported controllers should be compiled
// into SDL as a header file, but if a user has a very rare controller that is not supported, // into SDL as a header file, but if a user has a very rare controller that is not supported,
// the bundled mapping is incorrect, or the SDL version is a bit older, it makes sense to be // the bundled mapping is incorrect, or the SDL version is a bit older, it makes sense to be
@ -136,6 +147,7 @@ void InputManager::deinit()
mInputConfigs.clear(); mInputConfigs.clear();
mKeyboardInputConfig.reset(); mKeyboardInputConfig.reset();
mTouchInputConfig.reset();
mCECInputConfig.reset(); mCECInputConfig.reset();
SDL_GameControllerEventState(SDL_DISABLE); SDL_GameControllerEventState(SDL_DISABLE);
@ -284,6 +296,11 @@ int InputManager::getNumConfiguredDevices()
if (mKeyboardInputConfig->isConfigured()) if (mKeyboardInputConfig->isConfigured())
++num; ++num;
#if defined(__ANDROID__)
if (mTouchInputConfig->isConfigured())
++num;
#endif
if (mCECInputConfig->isConfigured()) if (mCECInputConfig->isConfigured())
++num; ++num;
@ -313,8 +330,11 @@ std::string InputManager::getDeviceGUIDString(int deviceId)
{ {
if (deviceId == DEVICE_KEYBOARD) if (deviceId == DEVICE_KEYBOARD)
return KEYBOARD_GUID_STRING; return KEYBOARD_GUID_STRING;
#if defined(__ANDROID__)
if (deviceId == DEVICE_CEC) else if (deviceId == DEVICE_TOUCH)
return TOUCH_GUID_STRING;
#endif
else if (deviceId == DEVICE_CEC)
return CEC_GUID_STRING; return CEC_GUID_STRING;
auto it = mJoysticks.find(deviceId); auto it = mJoysticks.find(deviceId);
@ -333,6 +353,10 @@ InputConfig* InputManager::getInputConfigByDevice(int device)
{ {
if (device == DEVICE_KEYBOARD) if (device == DEVICE_KEYBOARD)
return mKeyboardInputConfig.get(); return mKeyboardInputConfig.get();
#if defined(__ANDROID__)
else if (device == DEVICE_TOUCH)
return mTouchInputConfig.get();
#endif
else if (device == DEVICE_CEC) else if (device == DEVICE_CEC)
return mCECInputConfig.get(); return mCECInputConfig.get();
else else
@ -510,6 +534,62 @@ bool InputManager::parseEvent(const SDL_Event& event)
Input(DEVICE_KEYBOARD, TYPE_KEY, event.key.keysym.sym, 0, false)); Input(DEVICE_KEYBOARD, TYPE_KEY, event.key.keysym.sym, 0, false));
return true; return true;
} }
#if defined(__ANDROID__)
case SDL_FINGERDOWN: {
if (!Settings::getInstance()->getBool("InputTouchOverlay"))
return false;
const int buttonID {mInputOverlay.getButtonId(
SDL_FINGERDOWN, event.tfinger.fingerId + 1, event.tfinger.x, event.tfinger.y)};
if (buttonID != -2) {
mWindow->input(getInputConfigByDevice(DEVICE_TOUCH),
Input(DEVICE_TOUCH, TYPE_TOUCH, buttonID, 1, false));
return true;
}
else {
return false;
}
}
case SDL_FINGERUP: {
if (!Settings::getInstance()->getBool("InputTouchOverlay"))
return false;
const int buttonID {mInputOverlay.getButtonId(SDL_FINGERUP, event.tfinger.fingerId + 1,
event.tfinger.x, event.tfinger.y)};
if (buttonID != -2) {
mWindow->input(getInputConfigByDevice(DEVICE_TOUCH),
Input(DEVICE_TOUCH, TYPE_TOUCH, buttonID, 0, false));
return true;
}
else {
return false;
}
}
case SDL_FINGERMOTION: {
if (!Settings::getInstance()->getBool("InputTouchOverlay"))
return false;
bool releasedButton {false};
const int buttonID {
mInputOverlay.getButtonId(SDL_FINGERMOTION, event.tfinger.fingerId + 1,
event.tfinger.x, event.tfinger.y, &releasedButton)};
if (buttonID == -2)
return false;
if (releasedButton) {
mWindow->input(getInputConfigByDevice(DEVICE_TOUCH),
Input(DEVICE_TOUCH, TYPE_TOUCH, buttonID, 0, false));
return true;
}
else {
mWindow->input(getInputConfigByDevice(DEVICE_TOUCH),
Input(DEVICE_TOUCH, TYPE_TOUCH, buttonID, 1, false));
return true;
}
}
#endif
case SDL_TEXTINPUT: { case SDL_TEXTINPUT: {
mWindow->textInput(event.text.text); mWindow->textInput(event.text.text);
break; break;
@ -646,6 +726,31 @@ void InputManager::loadDefaultControllerConfig(SDL_JoystickID deviceIndex)
// clang-format on // clang-format on
} }
void InputManager::loadTouchConfig()
{
InputConfig* cfg {mTouchInputConfig.get()};
if (cfg->isConfigured())
return;
// clang-format off
cfg->mapInput("Up", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_DPAD_UP, 1, true));
cfg->mapInput("Down", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_DPAD_DOWN, 1, true));
cfg->mapInput("Left", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 1, true));
cfg->mapInput("Right", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, 1, true));
cfg->mapInput("Start", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_START, 1, true));
cfg->mapInput("Back", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_BACK, 1, true));
cfg->mapInput("A", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_A, 1, true));
cfg->mapInput("B", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_B, 1, true));
cfg->mapInput("X", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_X, 1, true));
cfg->mapInput("Y", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_Y, 1, true));
cfg->mapInput("LeftShoulder", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, 1, true));
cfg->mapInput("RightShoulder", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, 1, true));
cfg->mapInput("LeftTrigger", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_AXIS_TRIGGERLEFT, 1, true));
cfg->mapInput("RightTrigger", Input(DEVICE_TOUCH, TYPE_TOUCH, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 1, true));
// clang-format on
}
void InputManager::addControllerByDeviceIndex(Window* window, int deviceIndex) void InputManager::addControllerByDeviceIndex(Window* window, int deviceIndex)
{ {
// Open joystick and add it to our list. // Open joystick and add it to our list.

View file

@ -12,6 +12,7 @@
#define ES_CORE_INPUT_MANAGER_H #define ES_CORE_INPUT_MANAGER_H
#include "CECInput.h" #include "CECInput.h"
#include "InputOverlay.h"
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_joystick.h> #include <SDL2/SDL_joystick.h>
@ -57,12 +58,16 @@ private:
bool loadInputConfig(InputConfig* config); bool loadInputConfig(InputConfig* config);
void loadDefaultKBConfig(); void loadDefaultKBConfig();
void loadDefaultControllerConfig(SDL_JoystickID deviceIndex); void loadDefaultControllerConfig(SDL_JoystickID deviceIndex);
void loadTouchConfig();
void addControllerByDeviceIndex(Window* window, int deviceIndex); void addControllerByDeviceIndex(Window* window, int deviceIndex);
void removeControllerByJoystickID(Window* window, SDL_JoystickID joyID); void removeControllerByJoystickID(Window* window, SDL_JoystickID joyID);
Window* mWindow; Window* mWindow;
CECInput mCECInput; CECInput mCECInput;
#if defined(__ANDROID__)
InputOverlay& mInputOverlay;
#endif
static const int DEADZONE_TRIGGERS = 18000; static const int DEADZONE_TRIGGERS = 18000;
static const int DEADZONE_THUMBSTICKS = 23000; static const int DEADZONE_THUMBSTICKS = 23000;
@ -73,6 +78,7 @@ private:
std::map<SDL_JoystickID, std::unique_ptr<InputConfig>> mInputConfigs; std::map<SDL_JoystickID, std::unique_ptr<InputConfig>> mInputConfigs;
std::unique_ptr<InputConfig> mKeyboardInputConfig; std::unique_ptr<InputConfig> mKeyboardInputConfig;
std::unique_ptr<InputConfig> mTouchInputConfig;
std::unique_ptr<InputConfig> mCECInputConfig; std::unique_ptr<InputConfig> mCECInputConfig;
std::map<std::pair<SDL_JoystickID, int>, int> mPrevAxisValues; std::map<std::pair<SDL_JoystickID, int>, int> mPrevAxisValues;

View file

@ -239,6 +239,9 @@ void Settings::setDefaults()
// Input device settings. // Input device settings.
mStringMap["InputControllerType"] = {"xbox", "xbox"}; mStringMap["InputControllerType"] = {"xbox", "xbox"};
#if defined(__ANDROID__)
mBoolMap["InputTouchOverlay"] = {true, true};
#endif
mBoolMap["InputOnlyFirstController"] = {false, false}; mBoolMap["InputOnlyFirstController"] = {false, false};
mBoolMap["InputIgnoreKeyboard"] = {false, false}; mBoolMap["InputIgnoreKeyboard"] = {false, false};

View file

@ -18,6 +18,10 @@
#include "guis/GuiInfoPopup.h" #include "guis/GuiInfoPopup.h"
#include "resources/Font.h" #include "resources/Font.h"
#if defined(__ANDROID__)
#include "InputOverlay.h"
#endif
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
@ -662,6 +666,11 @@ void Window::render()
if (mRenderScreensaver) if (mRenderScreensaver)
mScreensaver->renderScreensaver(); mScreensaver->renderScreensaver();
#if defined(__ANDROID__)
if (Settings::getInstance()->getBool("InputTouchOverlay"))
InputOverlay::getInstance().render(mRenderer->getIdentity());
#endif
if (Settings::getInstance()->getBool("DisplayGPUStatistics") && mFrameDataText) { if (Settings::getInstance()->getBool("DisplayGPUStatistics") && mFrameDataText) {
mRenderer->setMatrix(mRenderer->getIdentity()); mRenderer->setMatrix(mRenderer->getIdentity());
mDefaultFonts.at(1)->renderTextCache(mFrameDataText.get()); mDefaultFonts.at(1)->renderTextCache(mFrameDataText.get());

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="52"
height="52"
version="1.1"
viewBox="0 0 52 52"
id="svg4"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<circle
style="fill:#ffffff;fill-opacity:0.25;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.735;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill;stop-color:#000000"
id="path949"
cx="26"
cy="26"
r="25.13261" />
<path
id="rect852"
style="fill:#ffffff;fill-opacity:0.25;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.735;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;stop-color:#000000"
d="M 21.761353,11.664063 14.67151,40.335937 h 6.855469 L 23.10901,33.34375 h 5.761719 l 1.582031,6.992187 h 6.874997 L 30.218385,11.664063 Z m 4.238282,8.749999 1.699218,7.519531 h -3.359374 c 0.898437,-4.010417 1.451823,-6.516925 1.660156,-7.519531 z" />
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="52"
height="52"
version="1.1"
viewBox="0 0 52 52"
id="svg4"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<circle
style="fill:#ffffff;fill-opacity:0.25;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.735;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill;stop-color:#000000"
id="path949"
cx="26"
cy="26"
r="25.13261" />
<path
id="button_b"
d="m 17.230469,11.722656 v 28.554688 h 9.160156 c 2.552083,-0.02604 4.583333,-0.781249 6.09375,-2.265625 1.523437,-1.497395 2.285156,-3.561197 2.285156,-6.191406 0,-1.692708 -0.32552,-3.072916 -0.976562,-4.140625 -0.63802,-1.080729 -1.653646,-1.888021 -3.046875,-2.421875 1.002604,-0.377604 1.770833,-0.989584 2.304687,-1.835938 0.78125,-1.184896 1.171875,-2.604166 1.171875,-4.257812 0,-2.591146 -0.722656,-4.479167 -2.167968,-5.664063 -1.445313,-1.184896 -3.756511,-1.777344 -6.933594,-1.777344 z m 6.503906,5.292969 h 1.347656 c 0.807292,0 1.393229,0.221355 1.757813,0.664063 0.377604,0.442709 0.566406,1.171875 0.566406,2.1875 0,1.002604 -0.16276,1.757813 -0.488281,2.265625 -0.32552,0.494792 -0.872396,0.742187 -1.640625,0.742187 h -1.542969 z m 0,10.996094 h 1.699219 c 1.614583,0 2.421875,1.10026 2.421875,3.300781 0,2.408854 -0.78125,3.613281 -2.34375,3.613281 h -1.777344 z"
style="fill:#ffffff;fill-opacity:0.25;stroke:#ffffff;stroke-width:1.735;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.998283" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="52"
height="40"
version="1.1"
viewBox="0 0 52 40"
id="svg4"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<path
id="rect6831"
style="opacity:1;fill:#ffffff;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.74532;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill;stop-color:#000000;stop-opacity:1;fill-opacity:0.25"
d="M 0.8722719,0.87265904 V 39.127341 H 31.564741 L 50.76394,19.999999 31.564741,0.87265904 Z" />
</svg>

After

Width:  |  Height:  |  Size: 664 B

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="52"
height="52"
version="1.1"
viewBox="0 0 52 52"
id="svg4"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<circle
style="fill:#ffffff;fill-opacity:0.25;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.735;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill;stop-color:#000000"
id="path949"
cx="26"
cy="26"
r="25.13261" />
<path
id="rect1405"
style="fill:#ffffff;fill-opacity:0.25;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.735;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill;stop-color:#000000"
d="m 16.259727,11.722656 6.054687,13.730469 -6.523437,14.824219 h 7.070312 l 3.007813,-8.144532 3.28125,8.144532 h 7.050781 L 29.677695,25.667968 35.712852,11.722656 h -7.109375 l -2.539063,7.265625 -2.929687,-7.265625 z" />
</svg>

After

Width:  |  Height:  |  Size: 1,011 B

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="52"
height="52"
version="1.1"
viewBox="0 0 52 52"
id="svg4"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<circle
style="fill:#ffffff;fill-opacity:0.25;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.735;stroke-linecap:square;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill;stop-color:#000000"
id="path949"
cx="26"
cy="26"
r="25.13261" />
<path
id="button_y"
d="M 15.88925,11.722656 22.608,28.949219 v 11.328125 h 6.757812 V 28.753906 l 6.738281,-17.03125 h -7.109375 c -1.015625,3.072917 -2.005208,6.022136 -2.96875,8.847656 l -2.988281,-8.847656 z"
style="fill:#ffffff;fill-opacity:0.25;stroke:#ffffff;stroke-width:1.735;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 907 B