flycast/core/ui/vgamepad.cpp

955 lines
24 KiB
C++

/*
Copyright 2019 flyinghead
This file is part of reicast.
reicast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
reicast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
#if defined(__ANDROID__) || defined(TARGET_IPHONE)
#include "vgamepad.h"
#include "gui.h"
#include "stdclass.h"
#include "imgui.h"
#include "rend/osd.h"
#include "imgui_driver.h"
#include "input/gamepad.h"
#include "input/gamepad_device.h"
#include "oslib/storage.h"
#include "oslib/resources.h"
#include "cfg/cfg.h"
#include "input/gamepad.h"
#include "input/mouse.h"
#include "hw/naomi/naomi_cart.h"
#include "hw/naomi/card_reader.h"
#include "hw/maple/maple_devs.h"
#include <stb_image.h>
namespace vgamepad
{
static void stopEditing(bool canceled);
static void loadLayout();
struct Control
{
Control() = default;
Control(float x, float y, float w = 64.f, float h = 64.f)
: pos(x, y), size(w, h), uv0(0, 0), uv1(1, 1) {}
ImVec2 pos;
ImVec2 size;
ImVec2 uv0;
ImVec2 uv1;
bool disabled = false;
};
static Control Controls[_Count];
static bool Visible = true;
static bool serviceMode;
static float AlphaTrans = 1.f;
static ImVec2 StickPos; // analog stick position [-1, 1]
constexpr char const *BTN_PATH = "picture/buttons.png";
constexpr char const *BTN_PATH_ARCADE = "picture/buttons-arcade.png";
constexpr char const *CFG_SECTION = "vgamepad";
void displayCommands()
{
draw();
centerNextWindow();
ImGui::Begin("##vgamepad", NULL, ImGuiWindowFlags_NoDecoration);
if (ImGui::Button("Save", ScaledVec2(150, 50)))
{
stopEditing(false);
gui_setState(GuiState::Settings);
}
ImGui::SameLine();
if (ImGui::Button("Reset", ScaledVec2(150, 50)))
{
resetEditing();
startEditing();
gui_setState(GuiState::VJoyEdit);
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ScaledVec2(150, 50)))
{
stopEditing(true);
gui_setState(GuiState::Settings);
}
ImGui::End();
}
static const char *getButtonsResPath() {
return settings.platform.isConsole() ? BTN_PATH : BTN_PATH_ARCADE;
}
static const char *getButtonsCfgName() {
return settings.platform.isConsole() ? "image" : "image_arcade";
}
static bool loadOSDButtons(const std::string& path)
{
if (path.empty())
return false;
FILE *file = hostfs::storage().openFile(path, "rb");
if (file == nullptr)
return false;
stbi_set_flip_vertically_on_load(1);
int width, height, n;
u8 *image_data = stbi_load_from_file(file, &width, &height, &n, STBI_rgb_alpha);
std::fclose(file);
if (image_data == nullptr)
return false;
try {
imguiDriver->updateTexture(getButtonsResPath(), image_data, width, height, false);
} catch (...) {
// vulkan can throw during resizing
}
free(image_data);
return true;
}
static ImTextureID loadOSDButtons()
{
ImTextureID id{};
// custom image
std::string path = cfgLoadStr(CFG_SECTION, getButtonsCfgName(), "");
if (loadOSDButtons(path))
return id;
if (settings.platform.isConsole())
{
// legacy buttons.png in data folder
if (loadOSDButtons(get_readonly_data_path("buttons.png")))
return id;
// also try the home folder (android)
if (loadOSDButtons(get_readonly_config_path("buttons.png")))
return id;
}
// default in resource
size_t size;
std::unique_ptr<u8[]> data = resource::load(getButtonsResPath(), size);
stbi_set_flip_vertically_on_load(1);
int width, height, n;
u8 *image_data = stbi_load_from_memory(data.get(), (int)size, &width, &height, &n, STBI_rgb_alpha);
if (image_data != nullptr)
{
try {
id = imguiDriver->updateTexture(getButtonsResPath(), image_data, width, height, false);
} catch (...) {
// vulkan can throw during resizing
}
free(image_data);
}
return id;
}
ImTextureID ImguiVGamepadTexture::getId()
{
ImTextureID id = imguiDriver->getTexture(getButtonsResPath());
if (id == ImTextureID())
id = loadOSDButtons();
return id;
}
constexpr float vjoy_tex[_Count][4] = {
// L
{ 0, 0, 64, 64 },
// U
{ 64, 0, 64, 64 },
// R
{ 128, 0, 64, 64 },
// D
{ 192, 0, 64, 64 },
// Y, btn3
{ 256, 0, 64, 64 },
// X, btn2
{ 320, 0, 64, 64 },
// B, btn1
{ 384, 0, 64, 64 },
// A, btn0
{ 448, 0, 64, 64 },
// Start
{ 0, 64, 64, 64 },
// LT
{ 64, 64, 90, 64 },
// RT
{ 154, 64, 90, 64 },
// Analog
{ 244, 64, 128, 128 },
// Stick
{ 372, 64, 64, 64 },
// Fast forward
{ 436, 64, 64, 64 },
// C, btn4
{ 0, 128, 64, 64 },
// Z, btn5
{ 64, 128, 64, 64 },
// service mode
{ 0, 192, 64, 64 },
// insert card
{ 64, 192, 64, 64 },
// Special controls
// service
{ 128, 128, 64, 64 },
// coin
{ 384, 128, 64, 64 },
// test
{ 448, 128, 64, 64 },
};
static ImVec2 coinUV0, coinUV1;
static ImVec2 serviceUV0, serviceUV1;
static ImVec2 testUV0, testUV1;
constexpr float OSD_TEX_W = 512.f;
constexpr float OSD_TEX_H = 256.f;
static void setUV()
{
int i = 0;
for (auto& control : Controls)
{
control.uv0.x = (vjoy_tex[i][0] + 1) / OSD_TEX_W;
control.uv0.y = 1.f - (vjoy_tex[i][1] + 1) / OSD_TEX_H;
control.uv1.x = (vjoy_tex[i][0] + vjoy_tex[i][2] - 1) / OSD_TEX_W;
control.uv1.y = 1.f - (vjoy_tex[i][1] + vjoy_tex[i][3] - 1) / OSD_TEX_H;
i++;
if (i >= _VisibleCount)
break;
}
serviceUV0.x = (vjoy_tex[i][0] + 1) / OSD_TEX_W;
serviceUV0.y = 1.f - (vjoy_tex[i][1] + 1) / OSD_TEX_H;
serviceUV1.x = (vjoy_tex[i][0] + vjoy_tex[i][2] - 1) / OSD_TEX_W;
serviceUV1.y = 1.f - (vjoy_tex[i][1] + vjoy_tex[i][3] - 1) / OSD_TEX_H;
i++;
coinUV0.x = (vjoy_tex[i][0] + 1) / OSD_TEX_W;
coinUV0.y = 1.f - (vjoy_tex[i][1] + 1) / OSD_TEX_H;
coinUV1.x = (vjoy_tex[i][0] + vjoy_tex[i][2] - 1) / OSD_TEX_W;
coinUV1.y = 1.f - (vjoy_tex[i][1] + vjoy_tex[i][3] - 1) / OSD_TEX_H;
i++;
testUV0.x = (vjoy_tex[i][0] + 1) / OSD_TEX_W;
testUV0.y = 1.f - (vjoy_tex[i][1] + 1) / OSD_TEX_H;
testUV1.x = (vjoy_tex[i][0] + vjoy_tex[i][2] - 1) / OSD_TEX_W;
testUV1.y = 1.f - (vjoy_tex[i][1] + vjoy_tex[i][3] - 1) / OSD_TEX_H;
i++;
}
static OnLoad _(&setUV);
void show() {
Visible = true;
}
void hide() {
Visible = false;
}
ControlId hitTest(float x, float y)
{
for (const auto& control : Controls)
if (!control.disabled
&& x >= control.pos.x && x < control.pos.x + control.size.x
&& y >= control.pos.y && y < control.pos.y + control.size.y)
return static_cast<ControlId>(&control - &Controls[0]);
return None;
}
static u32 buttonMap[_Count] {
DC_DPAD_LEFT,
DC_DPAD_UP,
DC_DPAD_RIGHT,
DC_DPAD_DOWN,
DC_BTN_X,
DC_BTN_Y,
DC_BTN_B,
DC_BTN_A,
DC_BTN_START,
DC_AXIS_LT,
DC_AXIS_RT,
0, // not used: analog area
0, // not used: analog stick
EMU_BTN_FFORWARD,
DC_BTN_Y, // button 4
DC_BTN_Z, // button 5
EMU_BTN_SRVMODE,
DC_BTN_INSERT_CARD,
DC_DPAD_LEFT | DC_DPAD_UP,
DC_DPAD_RIGHT | DC_DPAD_UP,
DC_DPAD_LEFT | DC_DPAD_DOWN,
DC_DPAD_RIGHT | DC_DPAD_DOWN,
};
void setButtonMap()
{
const bool arcade = settings.platform.isArcade();
if (serviceMode)
{
buttonMap[A] = DC_BTN_D;
buttonMap[B] = DC_DPAD2_UP;
buttonMap[X] = DC_DPAD2_DOWN;
}
else
{
buttonMap[A] = DC_BTN_A;
buttonMap[B] = DC_BTN_B;
buttonMap[X] = arcade ? DC_BTN_C : DC_BTN_X;
}
buttonMap[Y] = arcade ? DC_BTN_X : DC_BTN_Y;
}
u32 controlToDcKey(ControlId control)
{
if (control >= Left && control < _Count)
return buttonMap[control];
else
return 0;
}
void setAnalogStick(float x, float y) {
StickPos.x = x;
StickPos.y = y;
}
float getControlWidth(ControlId control) {
return Controls[control].size.x;
}
void toggleServiceMode()
{
serviceMode = !serviceMode;
if (serviceMode)
{
Controls[A].disabled = false;
Controls[B].disabled = false;
Controls[X].disabled = false;
setButtonMap();
}
else {
startGame();
}
}
static void drawButtonDim(ImDrawList *drawList, const Control& control, int state)
{
if (control.disabled)
return;
float scale_h = settings.display.height / 480.f;
float offs_x = (settings.display.width - scale_h * 640.f) / 2.f;
ImVec2 pos = control.pos * scale_h;
ImVec2 size = control.size * scale_h;
pos.x += offs_x;
ControlId controlId = static_cast<ControlId>(&control - &Controls[0]);
if (controlId == AnalogStick)
pos += StickPos * size;
float col = (0.5f - 0.25f * state / 255) * AlphaTrans;
float alpha = (100.f - config::VirtualGamepadTransparency) / 100.f * AlphaTrans;
ImVec4 color(col, col, col, alpha);
const ImVec2* uv0 = &control.uv0;
const ImVec2* uv1 = &control.uv1;
if (serviceMode)
switch (controlId)
{
case A:
uv0 = &coinUV0;
uv1 = &coinUV1;
break;
case B:
uv0 = &serviceUV0;
uv1 = &serviceUV1;
break;
case X:
uv0 = &testUV0;
uv1 = &testUV1;
break;
default:
break;
}
ImguiVGamepadTexture tex;
tex.draw(drawList, pos, size, *uv0, *uv1, color);
}
static void drawButton(ImDrawList *drawList, const Control& control, bool state) {
drawButtonDim(drawList, control, state ? 0 : 255);
}
void draw()
{
if (Controls[Left].pos.x == 0.f)
{
loadLayout();
if (Controls[Left].pos.x == 0.f)
// mark done
Controls[Left].pos.x = 1e-12f;
}
ImDrawList *drawList = ImGui::GetBackgroundDrawList();
drawButton(drawList, Controls[Left], kcode[0] & buttonMap[Left]);
drawButton(drawList, Controls[Up], kcode[0] & buttonMap[Up]);
drawButton(drawList, Controls[Right], kcode[0] & buttonMap[Right]);
drawButton(drawList, Controls[Down], kcode[0] & buttonMap[Down]);
drawButton(drawList, Controls[X], kcode[0] & buttonMap[X]);
drawButton(drawList, Controls[Y], kcode[0] & buttonMap[Y]);
drawButton(drawList, Controls[B], kcode[0] & buttonMap[B]);
drawButton(drawList, Controls[A], kcode[0] & buttonMap[A]);
drawButton(drawList, Controls[Start], kcode[0] & buttonMap[Start]);
drawButtonDim(drawList, Controls[LeftTrigger], lt[0] >> 8);
drawButtonDim(drawList, Controls[RightTrigger], rt[0] >> 8);
drawButton(drawList, Controls[AnalogArea], true);
drawButton(drawList, Controls[AnalogStick], false);
drawButton(drawList, Controls[FastForward], false);
drawButton(drawList, Controls[Btn4], kcode[0] & buttonMap[Btn4]);
drawButton(drawList, Controls[Btn5], kcode[0] & buttonMap[Btn5]);
drawButton(drawList, Controls[ServiceMode], !serviceMode);
drawButton(drawList, Controls[InsertCard], kcode[0] & buttonMap[InsertCard]);
AlphaTrans += ((float)Visible - AlphaTrans) / 2;
}
static float getUIScale() {
// scale is 1.1 for a 320 dpi screen of height 750
return 1.1f * 750.f / settings.display.height * settings.display.dpi / 320.f;
}
struct LayoutElement
{
const std::string name;
const float dx, dy; // default pos in dc coords, relative to sides (or middle if 0)
const float dw, dh; // default size in dc coords
float x, y; // normalized coordinates [0, 1]
float w, h; // normalized coordinates [0, 1], scaled with uiScale
float scale; // user scale
void load()
{
x = cfgLoadFloat(CFG_SECTION, name + "_x", x);
y = cfgLoadFloat(CFG_SECTION, name + "_y", y);
scale = cfgLoadFloat(CFG_SECTION, name + "_scale", scale);
}
void save() const
{
cfgSaveFloat(CFG_SECTION, name + "_x", x);
cfgSaveFloat(CFG_SECTION, name + "_y", y);
cfgSaveFloat(CFG_SECTION, name + "_scale", scale);
}
bool hitTest(float nx, float ny) const {
return nx >= x && nx < x + w * scale
&& ny >= y && ny < y + h * scale;
}
void applyUiScale()
{
const float dcw = 480.f * (float)settings.display.width / settings.display.height;
const float uiscale = getUIScale();
w = dw / dcw * uiscale;
h = dh / 480.f * uiscale;
}
void reset()
{
applyUiScale();
scale = 1.f;
const float dcw = 480.f * (float)settings.display.width / settings.display.height;
const float uiscale = getUIScale();
if (dx == 0)
x = 0.5f - w / 2;
else if (dx > 0)
x = dx / dcw * uiscale;
else
x = 1.f - w + dx / dcw * uiscale;
if (dy == 0)
y = 0.5f - h / 2;
else if (dy > 0)
y = dy / 480.f * uiscale;
else
y = 1.f - h + dy / 480.f * uiscale;
}
};
static LayoutElement Layout[] {
{ "dpad", 32.f, -24.f, 192.f, 192.f },
{ "buttons", -24.f, -24.f, 192.f, 192.f },
{ "start", 0.f, -24.f, 64.f, 64.f },
{ "LT", -134.f,-240.f, 90.f, 64.f },
{ "RT", -32.f,-240.f, 90.f, 64.f },
{ "analog", 40.f,-320.f, 128.f, 128.f },
{ "fforward", -24.f, 24.f, 64.f, 64.f },
{ "btn4", -24.f,-216.f, 64.f, 64.f },
{ "btn5", -152.f,-216.f, 64.f, 64.f },
{ "service", -24.f, 96.f, 64.f, 64.f },
{ "inscard", 40.f,-250.f, 64.f, 64.f },
};
static void applyLayout()
{
const float dcw = 480.f * (float)settings.display.width / settings.display.height;
const float dx = (dcw - 640.f) / 2;
const float uiscale = getUIScale();
float x, y, scale;
// DPad
x = Layout[Elem_DPad].x * dcw - dx;
y = Layout[Elem_DPad].y * 480.f;
scale = Layout[Elem_DPad].scale * uiscale;
Controls[Left].pos = { x + 0.f * scale, y + 64.f * scale };
Controls[Up].pos = { x + 64.f * scale, y + 0.f * scale };
Controls[Right].pos = { x + 128.f * scale, y + 64.f * scale };
Controls[Down].pos = { x + 64.f * scale, y + 128.f * scale };
for (int control = Left; control <= Down; control++)
Controls[control].size = { 64.f * scale, 64.f * scale };
Controls[LeftUp].pos = { x + 0.f * scale, y + 0.f * scale };
Controls[LeftDown].pos = { x + 0.f * scale, y + 128.f * scale };
Controls[RightUp].pos = { x + 128.f * scale, y + 0.f * scale };
Controls[RightDown].pos = { x + 128.f * scale, y + 128.f * scale };
for (int control = LeftUp; control <= RightDown; control++)
Controls[control].size = { 64.f * scale, 64.f * scale };
// Buttons
x = Layout[Elem_Buttons].x * dcw - dx;
y = Layout[Elem_Buttons].y * 480.f;
scale = Layout[Elem_Buttons].scale * uiscale;
Controls[X].pos = { x + 0.f * scale, y + 64.f * scale };
Controls[Y].pos = { x + 64.f * scale, y + 0.f * scale };
Controls[B].pos = { x + 128.f * scale, y + 64.f * scale };
Controls[A].pos = { x + 64.f * scale, y + 128.f * scale };
for (int control = X; control <= A; control++)
Controls[control].size = { 64.f * scale, 64.f * scale };
// Start
scale = Layout[Elem_Start].scale * uiscale;
Controls[Start].pos = { Layout[Elem_Start].x * dcw - dx, Layout[Elem_Start].y * 480.f };
Controls[Start].size = { Layout[Elem_Start].dw * scale, Layout[Elem_Start].dh * scale };
// Left trigger
scale = Layout[Elem_LT].scale * uiscale;
Controls[LeftTrigger].pos = { Layout[Elem_LT].x * dcw - dx, Layout[Elem_LT].y * 480.f };
Controls[LeftTrigger].size = { Layout[Elem_LT].dw * scale, Layout[Elem_LT].dh * scale };
// Right trigger
scale = Layout[Elem_RT].scale * uiscale;
Controls[RightTrigger].pos = { Layout[Elem_RT].x * dcw - dx, Layout[Elem_RT].y * 480.f };
Controls[RightTrigger].size = { Layout[Elem_RT].dw * scale, Layout[Elem_RT].dh * scale };
// Analog
x = Layout[Elem_Analog].x * dcw - dx;
y = Layout[Elem_Analog].y * 480.f;
scale = Layout[Elem_Analog].scale * uiscale;
Controls[AnalogArea].pos = { x, y };
Controls[AnalogArea].size = { Layout[Elem_Analog].dw * scale, Layout[Elem_Analog].dh * scale };
Controls[AnalogStick].pos = { x + 32.f * scale, y + 32.f * scale };
Controls[AnalogStick].size = { 64.f * scale, 64.f * scale };
// Fast forward
scale = Layout[Elem_FForward].scale * uiscale;
Controls[FastForward].pos = { Layout[Elem_FForward].x * dcw - dx, Layout[Elem_FForward].y * 480.f };
Controls[FastForward].size = { Layout[Elem_FForward].dw * scale, Layout[Elem_FForward].dh * scale };
// ARCADE
// Button 4
scale = Layout[Elem_Btn4].scale * uiscale;
Controls[Btn4].pos = { Layout[Elem_Btn4].x * dcw - dx, Layout[Elem_Btn4].y * 480.f };
Controls[Btn4].size = { Layout[Elem_Btn4].dw * scale, Layout[Elem_Btn4].dh * scale };
// Button 5
scale = Layout[Elem_Btn5].scale * uiscale;
Controls[Btn5].pos = { Layout[Elem_Btn5].x * dcw - dx, Layout[Elem_Btn5].y * 480.f };
Controls[Btn5].size = { Layout[Elem_Btn5].dw * scale, Layout[Elem_Btn5].dh * scale };
// Service Mode
scale = Layout[Elem_ServiceMode].scale * uiscale;
Controls[ServiceMode].pos = { Layout[Elem_ServiceMode].x * dcw - dx, Layout[Elem_ServiceMode].y * 480.f };
Controls[ServiceMode].size = { Layout[Elem_ServiceMode].dw * scale, Layout[Elem_ServiceMode].dh * scale };
// Insert Card
scale = Layout[Elem_InsertCard].scale * uiscale;
Controls[InsertCard].pos = { Layout[Elem_InsertCard].x * dcw - dx, Layout[Elem_InsertCard].y * 480.f };
Controls[InsertCard].size = { Layout[Elem_InsertCard].dw * scale, Layout[Elem_InsertCard].dh * scale };
}
void applyUiScale() {
for (auto& element : Layout)
element.applyUiScale();
}
static void loadLayout()
{
for (auto& element : Layout) {
element.reset();
element.load();
}
applyLayout();
}
static void saveLayout()
{
cfgSetAutoSave(false);
for (auto& element : Layout)
element.save();
cfgSetAutoSave(false);
}
static void resetLayout()
{
for (auto& element : Layout)
element.reset();
applyLayout();
}
Element layoutHitTest(float x, float y)
{
for (const auto& element : Layout)
if (element.hitTest(x, y))
return static_cast<Element>(&element - &Layout[0]);
return Elem_None;
}
void translateElement(Element element, float dx, float dy)
{
LayoutElement& e = Layout[element];
e.x += dx;
e.y += dy;
applyLayout();
}
void scaleElement(Element element, float factor)
{
LayoutElement& e = Layout[element];
float dx = e.w * e.scale * (factor - 1.f) / 2.f;
float dy = e.h * e.scale * (factor - 1.f) / 2.f;
e.scale *= factor;
// keep centered
translateElement(element, -dx, -dy);
}
void loadImage(const std::string& path)
{
if (path.empty()) {
cfgSaveStr(CFG_SECTION, getButtonsCfgName(), "");
loadOSDButtons();
}
else if (loadOSDButtons(path)) {
cfgSaveStr(CFG_SECTION, getButtonsCfgName(), path);
}
}
static void enableAllControls()
{
for (auto& control : Controls)
control.disabled = false;
}
static void disableControl(ControlId ctrlId)
{
#ifdef TARGET_IPHONE
if (ctrlId == Up || ctrlId == Down)
// Needed to pause the emulator
return;
#endif
Controls[ctrlId].disabled = true;
switch (ctrlId)
{
case Left:
Controls[LeftUp].disabled = true;
Controls[LeftDown].disabled = true;
break;
case Right:
Controls[RightUp].disabled = true;
Controls[RightDown].disabled = true;
break;
case Up:
Controls[LeftUp].disabled = true;
Controls[RightUp].disabled = true;
break;
case Down:
Controls[LeftDown].disabled = true;
Controls[RightDown].disabled = true;
break;
case AnalogArea:
case AnalogStick:
Controls[AnalogArea].disabled = true;
Controls[AnalogStick].disabled = true;
break;
default:
break;
}
}
void startGame()
{
enableAllControls();
serviceMode = false;
setButtonMap();
bool enableTouchMouse = false;
if (settings.platform.isConsole())
{
disableControl(Btn4);
disableControl(Btn5);
disableControl(ServiceMode);
disableControl(InsertCard);
switch (config::MapleMainDevices[0])
{
case MDT_LightGun:
enableTouchMouse = true;
disableControl(AnalogArea);
disableControl(LeftTrigger);
disableControl(RightTrigger);
disableControl(A);
disableControl(X);
disableControl(Y);
break;
case MDT_AsciiStick:
// TODO add CZ
disableControl(AnalogArea);
disableControl(LeftTrigger);
disableControl(RightTrigger);
break;
case MDT_PopnMusicController:
// TODO add C btn
disableControl(AnalogArea);
disableControl(LeftTrigger);
disableControl(RightTrigger);
break;
case MDT_RacingController:
disableControl(X);
disableControl(Y);
break;
default:
break;
}
}
else
{
// arcade game
if (!card_reader::readerAvailable())
disableControl(InsertCard);
if (settings.platform.isAtomiswave()) {
disableControl(Btn5);
}
else if (settings.platform.isSystemSP())
{
disableControl(Btn4);
disableControl(Btn5);
}
if (NaomiGameInputs != nullptr)
{
bool fullAnalog = false;
bool rt = false;
bool lt = false;
for (const auto& axis : NaomiGameInputs->axes)
{
if (axis.name == nullptr)
break;
switch (axis.axis)
{
case 0:
case 1:
fullAnalog = true;
break;
case 4:
rt = true;
break;
case 5:
lt = true;
break;
}
}
if (!fullAnalog)
disableControl(AnalogArea);
if (!lt)
disableControl(LeftTrigger);
else
disableControl(Btn5);
if (!rt)
disableControl(RightTrigger);
else
disableControl(Btn4);
u32 usedButtons = 0;
for (const auto& button : NaomiGameInputs->buttons)
{
if (button.name == nullptr)
break;
usedButtons |= button.source;
}
if (settings.platform.isAtomiswave())
{
// button order: A B X Y B4
if ((usedButtons & AWAVE_BTN0_KEY) == 0 || settings.input.lightgunGame)
disableControl(A);
if ((usedButtons & AWAVE_BTN1_KEY) == 0)
disableControl(B);
if ((usedButtons & AWAVE_BTN2_KEY) == 0)
disableControl(X);
if ((usedButtons & AWAVE_BTN3_KEY) == 0)
disableControl(Y);
if ((usedButtons & AWAVE_BTN4_KEY) == 0)
disableControl(Btn4);
if ((usedButtons & AWAVE_UP_KEY) == 0)
disableControl(Up);
if ((usedButtons & AWAVE_DOWN_KEY) == 0)
disableControl(Down);
if ((usedButtons & AWAVE_LEFT_KEY) == 0)
disableControl(Left);
if ((usedButtons & AWAVE_RIGHT_KEY) == 0)
disableControl(Right);
if ((usedButtons & AWAVE_START_KEY) == 0)
disableControl(Start);
}
else if (settings.platform.isSystemSP())
{
if ((usedButtons & DC_BTN_A) == 0)
disableControl(A);
if ((usedButtons & DC_BTN_B) == 0)
disableControl(B);
if ((usedButtons & DC_BTN_C) == 0)
disableControl(X);
if ((usedButtons & DC_BTN_X) == 0)
disableControl(Y);
if ((usedButtons & DC_DPAD_UP) == 0)
disableControl(Up);
if ((usedButtons & DC_DPAD_DOWN) == 0)
disableControl(Down);
if ((usedButtons & DC_DPAD_LEFT) == 0)
disableControl(Left);
if ((usedButtons & DC_DPAD_RIGHT) == 0)
disableControl(Right);
if ((usedButtons & DC_BTN_START) == 0)
disableControl(Start);
}
else
{
if ((usedButtons & NAOMI_BTN0_KEY) == 0 || settings.input.lightgunGame)
disableControl(A);
if ((usedButtons & (NAOMI_BTN1_KEY | NAOMI_RELOAD_KEY)) == 0)
disableControl(B);
else if (settings.input.lightgunGame
&& (usedButtons & NAOMI_RELOAD_KEY) != 0
&& (usedButtons & NAOMI_BTN1_KEY) == 0)
// Remap button 1 to reload for lightgun games that need it
buttonMap[B] = DC_BTN_RELOAD;
if ((usedButtons & NAOMI_BTN2_KEY) == 0)
// C
disableControl(X);
if ((usedButtons & NAOMI_BTN3_KEY) == 0)
// X
disableControl(Y);
if ((usedButtons & NAOMI_BTN4_KEY) == 0)
// Y
disableControl(Btn4);
if ((usedButtons & NAOMI_BTN5_KEY) == 0)
// Z
disableControl(Btn5);
if ((usedButtons & NAOMI_UP_KEY) == 0)
disableControl(Up);
if ((usedButtons & NAOMI_DOWN_KEY) == 0)
disableControl(Down);
if ((usedButtons & NAOMI_LEFT_KEY) == 0)
disableControl(Left);
if ((usedButtons & NAOMI_RIGHT_KEY) == 0)
disableControl(Right);
if ((usedButtons & NAOMI_START_KEY) == 0)
disableControl(Start);
}
if (settings.input.lightgunGame)
enableTouchMouse = true;
}
else
{
if (settings.input.lightgunGame)
{
enableTouchMouse = true;
disableControl(A);
disableControl(X);
disableControl(Y);
disableControl(Btn4);
disableControl(Btn5);
disableControl(AnalogArea);
disableControl(LeftTrigger);
disableControl(RightTrigger);
disableControl(Up);
disableControl(Down);
disableControl(Left);
disableControl(Right);
}
else
{
// all analog games *should* have an input description
disableControl(AnalogArea);
disableControl(LeftTrigger);
disableControl(RightTrigger);
}
}
}
std::shared_ptr<TouchMouse> touchMouse = GamepadDevice::GetGamepad<TouchMouse>();
if (touchMouse != nullptr)
{
if (enableTouchMouse) {
if (touchMouse->maple_port() == -1)
touchMouse->set_maple_port(0);
}
else {
if (touchMouse->maple_port() == 0)
touchMouse->set_maple_port(-1);
}
}
}
void resetEditing() {
resetLayout();
}
void startEditing()
{
enableAllControls();
show();
setEditMode(true);
}
void pauseEditing() {
setEditMode(false);
}
static void stopEditing(bool canceled)
{
setEditMode(false);
if (canceled)
loadLayout();
else
saveLayout();
}
} // namespace vgamepad
#endif // __ANDROID__ || TARGET_IPHONE