android,ios: arcade vgamepad layout. android vgamepad rewrite
Refactor common virtual gamepad code. Distinct layouts for console and arcade. Add arcade buttons 5 and 6, insert card button, service mode toggle. System SP support
This commit is contained in:
parent
78fa3ee09e
commit
21f9e9fbc2
|
@ -1032,7 +1032,8 @@ if(NOT LIBRETRO)
|
|||
if(ANDROID OR IOS)
|
||||
cmrc_add_resources(flycast-resources
|
||||
WHENCE resources
|
||||
resources/picture/buttons.png)
|
||||
resources/picture/buttons.png
|
||||
resources/picture/buttons-arcade.png)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -1617,6 +1618,10 @@ if(NOT LIBRETRO)
|
|||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
shell/android-studio/flycast/src/main/jni/src/Android.cpp
|
||||
shell/android-studio/flycast/src/main/jni/src/android_gamepad.h
|
||||
shell/android-studio/flycast/src/main/jni/src/android_storage.h
|
||||
shell/android-studio/flycast/src/main/jni/src/http_client.h
|
||||
shell/android-studio/flycast/src/main/jni/src/jni_util.h
|
||||
shell/android-studio/flycast/src/main/jni/src/android_input.cpp
|
||||
shell/android-studio/flycast/src/main/jni/src/android_keyboard.h)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE android log)
|
||||
|
|
|
@ -52,6 +52,7 @@ enum DreamcastKey
|
|||
EMU_BTN_SAVESTATE,
|
||||
EMU_BTN_BYPASS_KB,
|
||||
EMU_BTN_SCREENSHOT,
|
||||
EMU_BTN_SRVMODE, // used internally by virtual gamepad
|
||||
|
||||
// Real axes
|
||||
DC_AXIS_TRIGGERS = 0x1000000,
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright 2024 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "gamepad_device.h"
|
||||
#include "ui/vgamepad.h"
|
||||
|
||||
class VirtualGamepad : public GamepadDevice
|
||||
{
|
||||
public:
|
||||
VirtualGamepad(const char *api_name, int maple_port = 0)
|
||||
: GamepadDevice(maple_port, api_name, false)
|
||||
{
|
||||
_name = "Virtual Gamepad";
|
||||
_unique_id = "virtual_gamepad_uid";
|
||||
input_mapper = std::make_shared<IdentityInputMapping>();
|
||||
// hasAnalogStick = true; // TODO has an analog stick but input mapping isn't persisted
|
||||
|
||||
leftTrigger = DC_AXIS_LT;
|
||||
rightTrigger = DC_AXIS_RT;
|
||||
}
|
||||
|
||||
bool is_virtual_gamepad() override {
|
||||
return true;
|
||||
};
|
||||
|
||||
// normalized coordinates [-1, 1]
|
||||
void joystickInput(float x, float y)
|
||||
{
|
||||
vgamepad::setAnalogStick(x, y);
|
||||
int joyx = std::round(x * 32767.f);
|
||||
int joyy = std::round(y * 32767.f);
|
||||
if (joyx >= 0)
|
||||
gamepad_axis_input(DC_AXIS_RIGHT, joyx);
|
||||
else
|
||||
gamepad_axis_input(DC_AXIS_LEFT, -joyx);
|
||||
if (joyy >= 0)
|
||||
gamepad_axis_input(DC_AXIS_DOWN, joyy);
|
||||
else
|
||||
gamepad_axis_input(DC_AXIS_UP, -joyy);
|
||||
}
|
||||
|
||||
void releaseAll()
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (buttonState & (1 << i))
|
||||
gamepad_btn_input(1 << i, false);
|
||||
buttonState = 0;
|
||||
joystickInput(0, 0);
|
||||
gamepad_axis_input(DC_AXIS_LT, 0);
|
||||
gamepad_axis_input(DC_AXIS_RT, 0);
|
||||
if (previousFastForward)
|
||||
gamepad_btn_input(EMU_BTN_FFORWARD, false);
|
||||
previousFastForward = false;
|
||||
}
|
||||
|
||||
virtual bool handleButtonInput(u32& state, u32 key, bool pressed) {
|
||||
// can be overridden in derived classes to handle specific key combos
|
||||
// (iOS up+down or left+right)
|
||||
return false;
|
||||
}
|
||||
|
||||
void buttonInput(vgamepad::ControlId controlId, bool pressed)
|
||||
{
|
||||
u32 kcode = vgamepad::controlToDcKey(controlId);
|
||||
if (kcode == 0)
|
||||
return;
|
||||
if (handleButtonInput(buttonState, kcode, pressed))
|
||||
return;
|
||||
if (kcode == DC_AXIS_LT) {
|
||||
gamepad_axis_input(DC_AXIS_LT, pressed ? 0x7fff : 0);
|
||||
}
|
||||
else if (kcode == DC_AXIS_RT) {
|
||||
gamepad_axis_input(DC_AXIS_RT, pressed ? 0x7fff : 0);
|
||||
}
|
||||
else if (kcode == EMU_BTN_SRVMODE) {
|
||||
if (pressed)
|
||||
vgamepad::toggleServiceMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pressed)
|
||||
buttonState |= kcode;
|
||||
else
|
||||
buttonState &= ~kcode;
|
||||
if ((kcode & (DC_DPAD_LEFT | DC_DPAD_RIGHT)) != 0
|
||||
&& (kcode & (DC_DPAD_UP | DC_DPAD_DOWN)) != 0)
|
||||
{
|
||||
// diagonals
|
||||
gamepad_btn_input(kcode & (DC_DPAD_LEFT | DC_DPAD_RIGHT), pressed);
|
||||
gamepad_btn_input(kcode & (DC_DPAD_UP | DC_DPAD_DOWN), pressed);
|
||||
}
|
||||
else {
|
||||
gamepad_btn_input(kcode, pressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
u32 buttonState = 0;
|
||||
bool previousFastForward = false;
|
||||
};
|
|
@ -1467,7 +1467,7 @@ static void gamepadSettingsPopup(const std::shared_ptr<GamepadDevice>& gamepad)
|
|||
|
||||
#if defined(__ANDROID__) || defined(TARGET_IPHONE)
|
||||
vgamepad::ImguiVGamepadTexture tex;
|
||||
ImGui::Image(tex.getId(), ScaledVec2(300, 112.5f), ImVec2(0, 1), ImVec2(1, 0.25f));
|
||||
ImGui::Image(tex.getId(), ScaledVec2(300.f, 150.f), ImVec2(0, 1), ImVec2(1, 0));
|
||||
#endif
|
||||
const char *gamepadPngTitle = "Select a PNG file";
|
||||
if (ImGui::Button("Choose Image...", ScaledVec2(150, 30)))
|
||||
|
|
|
@ -31,14 +31,13 @@
|
|||
#include "cfg/cfg.h"
|
||||
#include "input/gamepad.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 loadLayout();
|
||||
|
||||
struct Control
|
||||
{
|
||||
Control() = default;
|
||||
|
@ -53,9 +52,11 @@ struct Control
|
|||
};
|
||||
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()
|
||||
|
@ -87,6 +88,14 @@ void displayCommands()
|
|||
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())
|
||||
|
@ -102,7 +111,7 @@ static bool loadOSDButtons(const std::string& path)
|
|||
if (image_data == nullptr)
|
||||
return false;
|
||||
try {
|
||||
imguiDriver->updateTexture(BTN_PATH, image_data, width, height, false);
|
||||
imguiDriver->updateTexture(getButtonsResPath(), image_data, width, height, false);
|
||||
} catch (...) {
|
||||
// vulkan can throw during resizing
|
||||
}
|
||||
|
@ -115,25 +124,28 @@ static ImTextureID loadOSDButtons()
|
|||
{
|
||||
ImTextureID id{};
|
||||
// custom image
|
||||
std::string path = cfgLoadStr(CFG_SECTION, "image", "");
|
||||
std::string path = cfgLoadStr(CFG_SECTION, getButtonsCfgName(), "");
|
||||
if (loadOSDButtons(path))
|
||||
return id;
|
||||
// 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;
|
||||
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(BTN_PATH, 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(BTN_PATH, image_data, width, height, false);
|
||||
id = imguiDriver->updateTexture(getButtonsResPath(), image_data, width, height, false);
|
||||
} catch (...) {
|
||||
// vulkan can throw during resizing
|
||||
}
|
||||
|
@ -144,42 +156,100 @@ static ImTextureID loadOSDButtons()
|
|||
|
||||
ImTextureID ImguiVGamepadTexture::getId()
|
||||
{
|
||||
ImTextureID id = imguiDriver->getTexture(BTN_PATH);
|
||||
ImTextureID id = imguiDriver->getTexture(getButtonsResPath());
|
||||
if (id == ImTextureID())
|
||||
id = loadOSDButtons();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
constexpr float vjoy_sz[2][_Count] = {
|
||||
// L U R D X Y B A St LT RT Ana Stck FF LU RU LD RD
|
||||
{ 64,64,64,64, 64,64,64,64, 64, 90,90, 128, 64, 64, 64,64,64,64 },
|
||||
{ 64,64,64,64, 64,64,64,64, 64, 64,64, 128, 64, 64, 64,64,64,64 },
|
||||
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()
|
||||
{
|
||||
float u = 0;
|
||||
float v = 0;
|
||||
int i = 0;
|
||||
|
||||
for (auto& control : Controls)
|
||||
{
|
||||
control.uv0.x = (u + 1) / OSD_TEX_W;
|
||||
control.uv0.y = 1.f - (v + 1) / OSD_TEX_H;
|
||||
control.uv1.x = (u + vjoy_sz[0][i] - 1) / OSD_TEX_W;
|
||||
control.uv1.y = 1.f - (v + vjoy_sz[1][i] - 1) / OSD_TEX_H;
|
||||
|
||||
u += vjoy_sz[0][i];
|
||||
if (u >= OSD_TEX_W) {
|
||||
u -= OSD_TEX_W;
|
||||
v += vjoy_sz[1][i];
|
||||
}
|
||||
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);
|
||||
|
||||
|
@ -191,18 +261,6 @@ void hide() {
|
|||
Visible = false;
|
||||
}
|
||||
|
||||
void setPosition(ControlId id, float x, float y, float w, float h)
|
||||
{
|
||||
verify(id >= 0 && id < _VisibleCount);
|
||||
auto& control = Controls[id];
|
||||
control.pos.x = x;
|
||||
control.pos.y = y;
|
||||
if (w != 0)
|
||||
control.size.x = w;
|
||||
if (h != 0)
|
||||
control.size.y = h;
|
||||
}
|
||||
|
||||
ControlId hitTest(float x, float y)
|
||||
{
|
||||
for (const auto& control : Controls)
|
||||
|
@ -215,16 +273,17 @@ ControlId hitTest(float x, float y)
|
|||
|
||||
u32 controlToDcKey(ControlId control)
|
||||
{
|
||||
const bool arcade = settings.platform.isArcade();
|
||||
switch (control)
|
||||
{
|
||||
case Left: return DC_DPAD_LEFT;
|
||||
case Up: return DC_DPAD_UP;
|
||||
case Right: return DC_DPAD_RIGHT;
|
||||
case Down: return DC_DPAD_DOWN;
|
||||
case X: return DC_BTN_X;
|
||||
case Y: return DC_BTN_Y;
|
||||
case B: return DC_BTN_B;
|
||||
case A: return DC_BTN_A;
|
||||
case X: return serviceMode ? DC_DPAD2_DOWN : arcade ? DC_BTN_C : DC_BTN_X;
|
||||
case Y: return arcade ? DC_BTN_X : DC_BTN_Y;
|
||||
case B: return serviceMode ? DC_DPAD2_UP : DC_BTN_B;
|
||||
case A: return serviceMode ? DC_BTN_D : DC_BTN_A;
|
||||
case Start: return DC_BTN_START;
|
||||
case LeftTrigger: return DC_AXIS_LT;
|
||||
case RightTrigger: return DC_AXIS_RT;
|
||||
|
@ -233,6 +292,11 @@ u32 controlToDcKey(ControlId control)
|
|||
case RightUp: return DC_DPAD_RIGHT | DC_DPAD_UP;
|
||||
case LeftDown: return DC_DPAD_LEFT | DC_DPAD_DOWN;
|
||||
case RightDown: return DC_DPAD_RIGHT | DC_DPAD_DOWN;
|
||||
// Arcade
|
||||
case Btn4: return DC_BTN_Y;
|
||||
case Btn5: return DC_BTN_Z;
|
||||
case InsertCard: return DC_BTN_INSERT_CARD;
|
||||
case ServiceMode: return EMU_BTN_SRVMODE;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
@ -246,6 +310,19 @@ 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;
|
||||
}
|
||||
else {
|
||||
startGame();
|
||||
}
|
||||
}
|
||||
|
||||
static void drawButtonDim(ImDrawList *drawList, const Control& control, int state)
|
||||
{
|
||||
if (control.disabled)
|
||||
|
@ -255,15 +332,37 @@ static void drawButtonDim(ImDrawList *drawList, const Control& control, int stat
|
|||
ImVec2 pos = control.pos * scale_h;
|
||||
ImVec2 size = control.size * scale_h;
|
||||
pos.x += offs_x;
|
||||
if (static_cast<ControlId>(&control - &Controls[0]) == AnalogStick)
|
||||
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, control.uv0, control.uv1, color);
|
||||
tex.draw(drawList, pos, size, *uv0, *uv1, color);
|
||||
}
|
||||
|
||||
static void drawButton(ImDrawList *drawList, const Control& control, bool state) {
|
||||
|
@ -272,7 +371,6 @@ static void drawButton(ImDrawList *drawList, const Control& control, bool state)
|
|||
|
||||
void draw()
|
||||
{
|
||||
#ifndef __ANDROID__
|
||||
if (Controls[Left].pos.x == 0.f)
|
||||
{
|
||||
loadLayout();
|
||||
|
@ -280,7 +378,6 @@ void draw()
|
|||
// mark done
|
||||
Controls[Left].pos.x = 1e-12f;
|
||||
}
|
||||
#endif
|
||||
|
||||
ImDrawList *drawList = ImGui::GetBackgroundDrawList();
|
||||
drawButton(drawList, Controls[Left], kcode[0] & DC_DPAD_LEFT);
|
||||
|
@ -288,10 +385,10 @@ void draw()
|
|||
drawButton(drawList, Controls[Right], kcode[0] & DC_DPAD_RIGHT);
|
||||
drawButton(drawList, Controls[Down], kcode[0] & DC_DPAD_DOWN);
|
||||
|
||||
drawButton(drawList, Controls[X], kcode[0] & (settings.platform.isConsole() ? DC_BTN_X : DC_BTN_C));
|
||||
drawButton(drawList, Controls[X], kcode[0] & (serviceMode ? DC_DPAD2_DOWN : settings.platform.isConsole() ? DC_BTN_X : DC_BTN_C));
|
||||
drawButton(drawList, Controls[Y], kcode[0] & (settings.platform.isConsole() ? DC_BTN_Y : DC_BTN_X));
|
||||
drawButton(drawList, Controls[B], kcode[0] & DC_BTN_B);
|
||||
drawButton(drawList, Controls[A], kcode[0] & DC_BTN_A);
|
||||
drawButton(drawList, Controls[B], kcode[0] & (serviceMode ? DC_DPAD2_UP : DC_BTN_B));
|
||||
drawButton(drawList, Controls[A], kcode[0] & (serviceMode ? DC_BTN_D : DC_BTN_A));
|
||||
|
||||
drawButton(drawList, Controls[Start], kcode[0] & DC_BTN_START);
|
||||
|
||||
|
@ -303,6 +400,12 @@ void draw()
|
|||
drawButton(drawList, Controls[AnalogStick], false);
|
||||
|
||||
drawButton(drawList, Controls[FastForward], false);
|
||||
|
||||
drawButton(drawList, Controls[Btn4], kcode[0] & DC_BTN_Y);
|
||||
drawButton(drawList, Controls[Btn5], kcode[0] & DC_BTN_Z);
|
||||
drawButton(drawList, Controls[ServiceMode], !serviceMode);
|
||||
drawButton(drawList, Controls[InsertCard], kcode[0] & DC_BTN_INSERT_CARD);
|
||||
|
||||
AlphaTrans += ((float)Visible - AlphaTrans) / 2;
|
||||
}
|
||||
|
||||
|
@ -349,6 +452,7 @@ struct LayoutElement
|
|||
|
||||
void reset()
|
||||
{
|
||||
applyUiScale();
|
||||
scale = 1.f;
|
||||
const float dcw = 480.f * (float)settings.display.width / settings.display.height;
|
||||
const float uiscale = getUIScale();
|
||||
|
@ -374,6 +478,11 @@ static LayoutElement Layout[] {
|
|||
{ "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()
|
||||
|
@ -440,6 +549,26 @@ static void applyLayout()
|
|||
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() {
|
||||
|
@ -447,7 +576,7 @@ void applyUiScale() {
|
|||
element.applyUiScale();
|
||||
}
|
||||
|
||||
static void loadLayout()
|
||||
void loadLayout()
|
||||
{
|
||||
for (auto& element : Layout) {
|
||||
element.reset();
|
||||
|
@ -456,7 +585,7 @@ static void loadLayout()
|
|||
applyLayout();
|
||||
}
|
||||
|
||||
static void saveLayout()
|
||||
void saveLayout()
|
||||
{
|
||||
cfgSetAutoSave(false);
|
||||
for (auto& element : Layout)
|
||||
|
@ -500,11 +629,11 @@ void scaleElement(Element element, float factor)
|
|||
void loadImage(const std::string& path)
|
||||
{
|
||||
if (path.empty()) {
|
||||
cfgSaveStr(CFG_SECTION, "image", "");
|
||||
cfgSaveStr(CFG_SECTION, getButtonsCfgName(), "");
|
||||
loadOSDButtons();
|
||||
}
|
||||
else if (loadOSDButtons(path)) {
|
||||
cfgSaveStr(CFG_SECTION, "image", path);
|
||||
cfgSaveStr(CFG_SECTION, getButtonsCfgName(), path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -554,8 +683,13 @@ static void disableControl(ControlId ctrlId)
|
|||
void startGame()
|
||||
{
|
||||
enableAllControls();
|
||||
serviceMode = false;
|
||||
if (settings.platform.isConsole())
|
||||
{
|
||||
disableControl(Btn4);
|
||||
disableControl(Btn5);
|
||||
disableControl(ServiceMode);
|
||||
disableControl(InsertCard);
|
||||
switch (config::MapleMainDevices[0])
|
||||
{
|
||||
case MDT_LightGun:
|
||||
|
@ -590,9 +724,17 @@ void startGame()
|
|||
else
|
||||
{
|
||||
// arcade game
|
||||
// FIXME RT is used as mod key for coin, test, service (ABX)
|
||||
// FIXME RT and LT are buttons 4 & 5 in arcade mode
|
||||
// TODO insert card button for card games
|
||||
if (!card_reader::readerAvailable())
|
||||
disableControl(InsertCard);
|
||||
if (settings.platform.isAtomiswave()) {
|
||||
disableControl(Btn5);
|
||||
}
|
||||
else if (settings.platform.isSystemSP())
|
||||
{
|
||||
disableControl(Y);
|
||||
disableControl(Btn4);
|
||||
disableControl(Btn5);
|
||||
}
|
||||
if (NaomiGameInputs != nullptr)
|
||||
{
|
||||
bool fullAnalog = false;
|
||||
|
@ -618,6 +760,14 @@ void startGame()
|
|||
}
|
||||
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)
|
||||
{
|
||||
|
@ -627,21 +777,17 @@ void startGame()
|
|||
}
|
||||
if (settings.platform.isAtomiswave())
|
||||
{
|
||||
// button order: A B X Y RT
|
||||
/* these ones are always needed for now
|
||||
// button order: A B X Y B4
|
||||
if ((usedButtons & AWAVE_BTN0_KEY) == 0)
|
||||
disableControl(A);
|
||||
if ((usedButtons & AWAVE_BTN1_KEY) == 0)
|
||||
disableControl(B);
|
||||
if ((usedButtons & AWAVE_BTN2_KEY) == 0)
|
||||
disableControl(X);
|
||||
if ((usedButtons & AWAVE_BTN4_KEY) == 0 && !rt)
|
||||
disableControl(RightTrigger);
|
||||
*/
|
||||
if ((usedButtons & AWAVE_BTN3_KEY) == 0)
|
||||
disableControl(Y);
|
||||
if (!lt)
|
||||
disableControl(LeftTrigger);
|
||||
if ((usedButtons & AWAVE_BTN4_KEY) == 0)
|
||||
disableControl(Btn4);
|
||||
if ((usedButtons & AWAVE_UP_KEY) == 0)
|
||||
disableControl(Up);
|
||||
if ((usedButtons & AWAVE_DOWN_KEY) == 0)
|
||||
|
@ -650,10 +796,30 @@ void startGame()
|
|||
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_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
|
||||
{
|
||||
/* these ones are always needed for now
|
||||
if ((usedButtons & NAOMI_BTN0_KEY) == 0)
|
||||
disableControl(A);
|
||||
if ((usedButtons & NAOMI_BTN1_KEY) == 0)
|
||||
|
@ -661,16 +827,15 @@ void startGame()
|
|||
if ((usedButtons & NAOMI_BTN2_KEY) == 0)
|
||||
// C
|
||||
disableControl(X);
|
||||
if ((usedButtons & NAOMI_BTN4_KEY) == 0 && !rt)
|
||||
// Y
|
||||
disableControl(RightTrigger);
|
||||
*/
|
||||
if ((usedButtons & NAOMI_BTN3_KEY) == 0)
|
||||
// X
|
||||
disableControl(Y);
|
||||
if ((usedButtons & NAOMI_BTN5_KEY) == 0 && !lt)
|
||||
if ((usedButtons & NAOMI_BTN4_KEY) == 0)
|
||||
// Y
|
||||
disableControl(Btn4);
|
||||
if ((usedButtons & NAOMI_BTN5_KEY) == 0)
|
||||
// Z
|
||||
disableControl(LeftTrigger);
|
||||
disableControl(Btn5);
|
||||
if ((usedButtons & NAOMI_UP_KEY) == 0)
|
||||
disableControl(Up);
|
||||
if ((usedButtons & NAOMI_DOWN_KEY) == 0)
|
||||
|
@ -679,28 +844,41 @@ void startGame()
|
|||
disableControl(Left);
|
||||
if ((usedButtons & NAOMI_RIGHT_KEY) == 0)
|
||||
disableControl(Right);
|
||||
if ((usedButtons & NAOMI_START_KEY) == 0)
|
||||
disableControl(Start);
|
||||
}
|
||||
}
|
||||
else if (settings.input.lightgunGame)
|
||||
{
|
||||
disableControl(Y);
|
||||
disableControl(AnalogArea);
|
||||
disableControl(LeftTrigger);
|
||||
disableControl(Up);
|
||||
disableControl(Down);
|
||||
disableControl(Left);
|
||||
disableControl(Right);
|
||||
}
|
||||
else
|
||||
{
|
||||
// all analog games *should* have an input description
|
||||
disableControl(AnalogArea);
|
||||
if (settings.input.lightgunGame)
|
||||
{
|
||||
// TODO enable mouse?
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool enabledState[_Count];
|
||||
for (int i = 0; i < _Count; i++)
|
||||
enabledState[i] = !Controls[i].disabled;
|
||||
setEnabledControls(enabledState);
|
||||
}
|
||||
|
||||
void resetEditing() {
|
||||
resetLayout();
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
@ -721,13 +899,6 @@ void stopEditing(bool canceled)
|
|||
saveLayout();
|
||||
}
|
||||
|
||||
void resetEditing() {
|
||||
resetLayout();
|
||||
}
|
||||
|
||||
void setEnabledControls(bool enabled[_Count]) {
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace vgamepad
|
||||
|
||||
|
|
|
@ -40,13 +40,18 @@ enum ControlId
|
|||
AnalogStick,
|
||||
FastForward,
|
||||
|
||||
Btn4,
|
||||
Btn5,
|
||||
ServiceMode,
|
||||
InsertCard,
|
||||
|
||||
LeftUp,
|
||||
RightUp,
|
||||
LeftDown,
|
||||
RightDown,
|
||||
|
||||
_Count,
|
||||
_VisibleCount = FastForward + 1,
|
||||
_VisibleCount = LeftUp,
|
||||
};
|
||||
|
||||
enum Element
|
||||
|
@ -59,6 +64,10 @@ enum Element
|
|||
Elem_RT,
|
||||
Elem_Analog,
|
||||
Elem_FForward,
|
||||
Elem_Btn4,
|
||||
Elem_Btn5,
|
||||
Elem_ServiceMode,
|
||||
Elem_InsertCard,
|
||||
};
|
||||
|
||||
class ImguiVGamepadTexture : public ImguiTexture
|
||||
|
@ -69,8 +78,6 @@ public:
|
|||
|
||||
#if defined(__ANDROID__) || defined(TARGET_IPHONE)
|
||||
|
||||
void setPosition(ControlId id, float x, float y, float w = 0.f, float h = 0.f); // Legacy android
|
||||
void setEnabledControls(bool enabled[_Count]); // Legacy android
|
||||
void enableAllControls();
|
||||
void show();
|
||||
void hide();
|
||||
|
@ -87,11 +94,14 @@ ControlId hitTest(float x, float y);
|
|||
u32 controlToDcKey(ControlId control);
|
||||
void setAnalogStick(float x, float y);
|
||||
float getControlWidth(ControlId);
|
||||
void toggleServiceMode();
|
||||
|
||||
void applyUiScale();
|
||||
Element layoutHitTest(float x, float y);
|
||||
void translateElement(Element element, float dx, float dy);
|
||||
void scaleElement(Element element, float factor);
|
||||
void loadLayout();
|
||||
void saveLayout();
|
||||
|
||||
#else
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 225 KiB |
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 96 KiB |
|
@ -49,7 +49,6 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
|
|||
private static final int AUDIO_PERM_REQUEST = 1002;
|
||||
|
||||
protected SharedPreferences prefs;
|
||||
protected float[][] vjoy_d_cached; // Used for VJoy editing
|
||||
private AudioBackend audioBackend;
|
||||
protected Handler handler = new Handler();
|
||||
private boolean audioPermissionRequested = false;
|
||||
|
@ -406,7 +405,7 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat.
|
|||
}
|
||||
|
||||
//setup mic
|
||||
if (Emulator.micPluggedIn())
|
||||
if (InputDeviceManager.isMicPluggedIn())
|
||||
requestRecordAudioPermission();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ import android.util.Log;
|
|||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
import com.flycast.emulator.config.Config;
|
||||
import com.flycast.emulator.emu.JNIdc;
|
||||
import com.flycast.emulator.emu.VGamepad;
|
||||
import com.flycast.emulator.periph.InputDeviceManager;
|
||||
|
||||
public class Emulator extends Application {
|
||||
private static Context context;
|
||||
|
@ -18,32 +19,14 @@ public class Emulator extends Application {
|
|||
private WifiManager wifiManager = null;
|
||||
private WifiManager.MulticastLock multicastLock = null;
|
||||
|
||||
// see MapleDeviceType in hw/maple/maple_devs.h
|
||||
public static final int MDT_Microphone = 2;
|
||||
public static final int MDT_None = 8;
|
||||
|
||||
public static int vibrationPower = 80;
|
||||
|
||||
public static int[] maple_devices = {
|
||||
MDT_None,
|
||||
MDT_None,
|
||||
MDT_None,
|
||||
MDT_None
|
||||
};
|
||||
public static int[][] maple_expansion_devices = {
|
||||
{ MDT_None, MDT_None },
|
||||
{ MDT_None, MDT_None },
|
||||
{ MDT_None, MDT_None },
|
||||
{ MDT_None, MDT_None },
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the settings from native code
|
||||
*
|
||||
*/
|
||||
public void getConfigurationPrefs() {
|
||||
Emulator.vibrationPower = JNIdc.getVirtualGamepadVibration();
|
||||
JNIdc.getControllers(maple_devices, maple_expansion_devices);
|
||||
Emulator.vibrationPower = VGamepad.getVibrationPower();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,26 +37,17 @@ public class Emulator extends Application {
|
|||
{
|
||||
Log.i("flycast", "SaveAndroidSettings: saving preferences");
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
Emulator.vibrationPower = JNIdc.getVirtualGamepadVibration();
|
||||
JNIdc.getControllers(maple_devices, maple_expansion_devices);
|
||||
Emulator.vibrationPower = VGamepad.getVibrationPower();
|
||||
|
||||
prefs.edit()
|
||||
.putString(Config.pref_home, homeDirectory).apply();
|
||||
|
||||
if (micPluggedIn() && currentActivity instanceof BaseGLActivity) {
|
||||
if (InputDeviceManager.isMicPluggedIn() && currentActivity instanceof BaseGLActivity) {
|
||||
Log.i("flycast", "SaveAndroidSettings: MIC PLUGGED IN");
|
||||
((BaseGLActivity)currentActivity).requestRecordAudioPermission();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean micPluggedIn() {
|
||||
JNIdc.getControllers(maple_devices, maple_expansion_devices);
|
||||
for (int[] maple_expansion_device : maple_expansion_devices)
|
||||
if (maple_expansion_device[0] == MDT_Microphone
|
||||
|| maple_expansion_device[1] == MDT_Microphone)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
|
|
@ -16,10 +16,8 @@ import android.widget.RelativeLayout;
|
|||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.flycast.emulator.emu.JNIdc;
|
||||
import com.flycast.emulator.emu.NativeGLView;
|
||||
import com.flycast.emulator.periph.InputDeviceManager;
|
||||
import com.flycast.emulator.periph.VJoy;
|
||||
|
||||
public final class NativeGLActivity extends BaseGLActivity {
|
||||
|
||||
|
@ -61,38 +59,35 @@ public final class NativeGLActivity extends BaseGLActivity {
|
|||
return mView != null && mView.isSurfaceReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGameStateChange(boolean started) {
|
||||
super.onGameStateChange(started);
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (started)
|
||||
mView.showVGamepad();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Called from native code
|
||||
private void VJoyStartEditing() {
|
||||
vjoy_d_cached = VJoy.readCustomVjoyValues(getApplicationContext());
|
||||
JNIdc.showVirtualGamepad();
|
||||
mView.setEditVjoyMode(true);
|
||||
}
|
||||
// Called from native code
|
||||
private void VJoyResetEditing() {
|
||||
VJoy.resetCustomVjoyValues(getApplicationContext());
|
||||
mView.readCustomVjoyValues();
|
||||
mView.resetEditMode();
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mView.requestLayout();
|
||||
mView.setEditVjoyMode(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Called from native code
|
||||
private void VJoyStopEditing(final boolean canceled) {
|
||||
private void VJoyStopEditing() {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (canceled)
|
||||
mView.restoreCustomVjoyValues(vjoy_d_cached);
|
||||
mView.setEditVjoyMode(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
private void VJoyEnableControls(boolean[] state) {
|
||||
mView.enableVjoy(state);
|
||||
}
|
||||
|
||||
// On-screen keyboard borrowed from SDL core android code
|
||||
class ShowTextInputTask implements Runnable {
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Copyright 2024 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.flycast.emulator.emu;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class EditVirtualJoystickDelegate implements TouchEventHandler
|
||||
{
|
||||
private View view;
|
||||
private ScaleGestureDetector scaleGestureDetector;
|
||||
private int currentElement = -1;
|
||||
private float lastX, lastY;
|
||||
|
||||
public EditVirtualJoystickDelegate(View view) {
|
||||
this.view = view;
|
||||
scaleGestureDetector = new ScaleGestureDetector(view.getContext(), new EditVirtualJoystickDelegate.ScaleGestureListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
@Override
|
||||
public void show() {
|
||||
VGamepad.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event, int width, int height)
|
||||
{
|
||||
scaleGestureDetector.onTouchEvent(event);
|
||||
if (scaleGestureDetector.isInProgress())
|
||||
return true;
|
||||
|
||||
int actionMasked = event.getActionMasked();
|
||||
int actionIndex = event.getActionIndex();
|
||||
switch (actionMasked)
|
||||
{
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
currentElement = -1;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
lastX = event.getX(actionIndex) / view.getWidth();
|
||||
lastY = event.getY(actionIndex) / view.getHeight();
|
||||
currentElement = VGamepad.layoutHitTest(lastX, lastY);
|
||||
return currentElement != -1;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (currentElement != -1 && event.getPointerCount() == 1)
|
||||
{
|
||||
float x = event.getX(actionIndex) / view.getWidth();
|
||||
float y = event.getY(actionIndex) / view.getHeight();
|
||||
VGamepad.translateElement(currentElement, x - lastX, y - lastY);
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
|
||||
private int elemId = -1;
|
||||
@Override
|
||||
public boolean onScaleBegin(@NonNull ScaleGestureDetector detector)
|
||||
{
|
||||
elemId = VGamepad.layoutHitTest(detector.getFocusX() / view.getWidth(), detector.getFocusY() / view.getHeight());
|
||||
return elemId != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector)
|
||||
{
|
||||
if (elemId == -1)
|
||||
return false;
|
||||
VGamepad.scaleElement(elemId, detector.getScaleFactor());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
elemId = -1;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,12 +18,7 @@ public final class JNIdc
|
|||
|
||||
public static native void rendinitNative(Surface surface, int w, int h);
|
||||
|
||||
public static native void vjoy(int id, float x, float y, float w, float h);
|
||||
|
||||
public static native void getControllers(int[] controllers, int[][] peripherals);
|
||||
|
||||
public static native void setupMic(SipEmulator sip);
|
||||
public static native int getVirtualGamepadVibration();
|
||||
|
||||
public static native void screenCharacteristics(float screenDpi, float refreshRate);
|
||||
public static native void guiOpenSettings();
|
||||
|
@ -31,6 +26,4 @@ public final class JNIdc
|
|||
public static native boolean guiIsContentBrowser();
|
||||
public static native void guiSetInsets(int left, int right, int top, int bottom);
|
||||
|
||||
public static native void showVirtualGamepad();
|
||||
public static native void hideVirtualGamepad();
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import android.util.DisplayMetrics;
|
|||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
@ -24,11 +25,7 @@ import com.flycast.emulator.periph.InputDeviceManager;
|
|||
public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback {
|
||||
private boolean surfaceReady = false;
|
||||
private boolean paused = false;
|
||||
VirtualJoystickDelegate vjoyDelegate;
|
||||
|
||||
public void restoreCustomVjoyValues(float[][] vjoy_d_cached) {
|
||||
vjoyDelegate.restoreCustomVjoyValues(vjoy_d_cached);
|
||||
}
|
||||
private TouchEventHandler vjoyDelegate = null;
|
||||
|
||||
public NativeGLView(Context context) {
|
||||
this(context, null);
|
||||
|
@ -63,20 +60,21 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback
|
|||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
vjoyDelegate = new VirtualJoystickDelegate(this);
|
||||
if (InputDeviceManager.getInstance().hasTouchscreen())
|
||||
vjoyDelegate = new VirtualJoystickDelegate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
vjoyDelegate.stop();
|
||||
if (vjoyDelegate != null)
|
||||
vjoyDelegate.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
|
||||
{
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
vjoyDelegate.layout(getWidth(), getHeight());
|
||||
DisplayMetrics dm = getContext().getResources().getDisplayMetrics();
|
||||
|
||||
Display d;
|
||||
|
@ -101,10 +99,6 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback
|
|||
}
|
||||
}
|
||||
|
||||
public void resetEditMode() {
|
||||
vjoyDelegate.resetEditMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(final MotionEvent event)
|
||||
{
|
||||
|
@ -113,13 +107,13 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback
|
|||
InputDeviceManager.getInstance().mouseEvent(Math.round(event.getX()), Math.round(event.getY()), event.getButtonState());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
if (vjoyDelegate != null && (event.getSource() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN)
|
||||
return vjoyDelegate.onTouchEvent(event, getWidth(), getHeight());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder surfaceHolder) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -179,14 +173,18 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback
|
|||
}
|
||||
}
|
||||
|
||||
public void readCustomVjoyValues() {
|
||||
vjoyDelegate.readCustomVjoyValues();
|
||||
public void setEditVjoyMode(boolean editVjoyMode)
|
||||
{
|
||||
if (!InputDeviceManager.getInstance().hasTouchscreen())
|
||||
return;
|
||||
if (editVjoyMode && !(vjoyDelegate instanceof EditVirtualJoystickDelegate))
|
||||
vjoyDelegate = new EditVirtualJoystickDelegate(this);
|
||||
else if (!editVjoyMode && !(vjoyDelegate instanceof VirtualJoystickDelegate))
|
||||
vjoyDelegate = new VirtualJoystickDelegate(this);
|
||||
}
|
||||
|
||||
public void setEditVjoyMode(boolean editVjoyMode) {
|
||||
vjoyDelegate.setEditVjoyMode(editVjoyMode);
|
||||
}
|
||||
public void enableVjoy(boolean[] state) {
|
||||
vjoyDelegate.enableVjoy(state);
|
||||
public void showVGamepad() {
|
||||
if (vjoyDelegate != null)
|
||||
vjoyDelegate.show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright 2024 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.flycast.emulator.emu;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public interface TouchEventHandler {
|
||||
boolean onTouchEvent(MotionEvent event, int width, int height);
|
||||
void stop();
|
||||
void show();
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright 2024 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.flycast.emulator.emu;
|
||||
|
||||
public class VGamepad
|
||||
{
|
||||
static { System.loadLibrary("flycast"); }
|
||||
|
||||
public static native int getVibrationPower();
|
||||
|
||||
public static native void show();
|
||||
public static native void hide();
|
||||
public static native int hitTest(float x, float y);
|
||||
public static native float getControlWidth(int controlId);
|
||||
|
||||
public static native int layoutHitTest(float x, float y);
|
||||
public static native void scaleElement(int elemId, float scale);
|
||||
public static native void translateElement(int elemId, float x, float y);
|
||||
}
|
|
@ -1,409 +1,273 @@
|
|||
/*
|
||||
Copyright 2024 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.flycast.emulator.emu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Handler;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.View;
|
||||
|
||||
import com.flycast.emulator.periph.InputDeviceManager;
|
||||
import com.flycast.emulator.periph.VJoy;
|
||||
import com.flycast.emulator.periph.VibratorThread;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class VirtualJoystickDelegate implements TouchEventHandler
|
||||
{
|
||||
private static final int CTLID_ANARING = 11;
|
||||
private static final int CTLID_ANASTICK = 12;
|
||||
|
||||
public class VirtualJoystickDelegate {
|
||||
private VibratorThread vibratorThread;
|
||||
|
||||
private boolean editVjoyMode = false;
|
||||
private int selectedVjoyElement = VJoy.ELEM_NONE;
|
||||
private ScaleGestureDetector scaleGestureDetector;
|
||||
|
||||
private Handler handler = new Handler();
|
||||
private Runnable hideVGamepadRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
JNIdc.hideVirtualGamepad();
|
||||
VGamepad.hide();
|
||||
}
|
||||
};
|
||||
|
||||
private float[][] vjoy_d_custom;
|
||||
private boolean[] vjoy_enabled;
|
||||
|
||||
private static final float[][] vjoy = VJoy.baseVJoy();
|
||||
|
||||
private Context context;
|
||||
private View view;
|
||||
private int joyPointerId = -1;
|
||||
private float joyBiasX, joyBiasY;
|
||||
private Map<Integer, Integer> pidToControlId = new HashMap<>();
|
||||
private int mouseButtons = 0;
|
||||
private int[] mousePos = { -32768, -32768 };
|
||||
private int mousePid = -1;
|
||||
|
||||
public VirtualJoystickDelegate(View view) {
|
||||
this.view = view;
|
||||
this.context = view.getContext();
|
||||
|
||||
vibratorThread = VibratorThread.getInstance();
|
||||
|
||||
readCustomVjoyValues();
|
||||
vjoy_enabled = new boolean[VJoy.VJoyCount + 4]; // include diagonals
|
||||
Arrays.fill(vjoy_enabled, true);
|
||||
scaleGestureDetector = new ScaleGestureDetector(context, new OscOnScaleGestureListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
vibratorThread.stopThread();
|
||||
vibratorThread = null;
|
||||
}
|
||||
|
||||
public void readCustomVjoyValues() {
|
||||
vjoy_d_custom = VJoy.readCustomVjoyValues(context);
|
||||
}
|
||||
|
||||
public void restoreCustomVjoyValues(float[][] vjoy_d_cached) {
|
||||
vjoy_d_custom = vjoy_d_cached;
|
||||
VJoy.writeCustomVjoyValues(vjoy_d_cached, context);
|
||||
|
||||
resetEditMode();
|
||||
view.requestLayout();
|
||||
}
|
||||
|
||||
private void reset_analog()
|
||||
private boolean touchMouseEvent(MotionEvent event)
|
||||
{
|
||||
|
||||
int j=11;
|
||||
vjoy[j+1][0]=vjoy[j][0]+vjoy[j][2]/2-vjoy[j+1][2]/2;
|
||||
vjoy[j+1][1]=vjoy[j][1]+vjoy[j][3]/2-vjoy[j+1][3]/2;
|
||||
JNIdc.vjoy(j+1, vjoy[j+1][0], vjoy[j+1][1], vjoy[j+1][2], vjoy[j+1][3]);
|
||||
}
|
||||
|
||||
private int get_anal(int j, int axis)
|
||||
{
|
||||
return (int) (((vjoy[j+1][axis]+vjoy[j+1][axis+2]/2) - vjoy[j][axis] - vjoy[j][axis+2]/2)*254/vjoy[j][axis+2]);
|
||||
}
|
||||
|
||||
private float vbase(float p, float m, float scl)
|
||||
{
|
||||
return (int) ( m - (m -p)*scl);
|
||||
}
|
||||
|
||||
private float vbase(float p, float scl)
|
||||
{
|
||||
return (int) (p*scl );
|
||||
}
|
||||
|
||||
private boolean isTablet() {
|
||||
return (context.getResources().getConfiguration().screenLayout
|
||||
& Configuration.SCREENLAYOUT_SIZE_MASK)
|
||||
>= Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
}
|
||||
|
||||
public void layout(int width, int height)
|
||||
{
|
||||
//dcpx/cm = dcpx/px * px/cm
|
||||
float magic = isTablet() ? 0.8f : 0.7f;
|
||||
float scl = 480.0f / height * context.getResources().getDisplayMetrics().density * magic;
|
||||
float scl_dc = height / 480.0f;
|
||||
float tx = (width - 640.0f * scl_dc) / 2 / scl_dc;
|
||||
|
||||
float a_x = -tx + 24 * scl;
|
||||
float a_y = -24 * scl;
|
||||
|
||||
// Not sure how this can happen
|
||||
if (vjoy_d_custom == null)
|
||||
return;
|
||||
|
||||
float[][] vjoy_d = VJoy.getVjoy_d(vjoy_d_custom);
|
||||
|
||||
for (int i=0;i<vjoy.length;i++)
|
||||
{
|
||||
// FIXME this hack causes the slight "jump" when first moving a screen-centered button
|
||||
if (vjoy_d[i][0] == 288)
|
||||
vjoy[i][0] = vjoy_d[i][0];
|
||||
else if (vjoy_d[i][0]-vjoy_d_custom[getElementIdFromButtonId(i)][0] < 320)
|
||||
vjoy[i][0] = a_x + vbase(vjoy_d[i][0],scl);
|
||||
else
|
||||
vjoy[i][0] = -a_x + vbase(vjoy_d[i][0],640,scl);
|
||||
|
||||
vjoy[i][1] = a_y + vbase(vjoy_d[i][1],480,scl);
|
||||
|
||||
vjoy[i][2] = vbase(vjoy_d[i][2],scl);
|
||||
vjoy[i][3] = vbase(vjoy_d[i][3],scl);
|
||||
}
|
||||
|
||||
for (int i=0;i<VJoy.VJoyCount;i++)
|
||||
JNIdc.vjoy(i,vjoy[i][0],vjoy[i][1],vjoy[i][2],vjoy[i][3]);
|
||||
|
||||
reset_analog();
|
||||
VJoy.writeCustomVjoyValues(vjoy_d_custom, context);
|
||||
}
|
||||
|
||||
private int anal_id=-1, lt_id=-1, rt_id=-1;
|
||||
|
||||
public void resetEditMode() {
|
||||
editLastX = 0;
|
||||
editLastY = 0;
|
||||
}
|
||||
|
||||
private static int getElementIdFromButtonId(int buttonId) {
|
||||
if (buttonId <= 3)
|
||||
return VJoy.ELEM_DPAD; // DPAD
|
||||
else if (buttonId <= 7)
|
||||
return VJoy.ELEM_BUTTONS; // X, Y, B, A Buttons
|
||||
else if (buttonId == 8)
|
||||
return VJoy.ELEM_START; // Start
|
||||
else if (buttonId == 9)
|
||||
return VJoy.ELEM_LTRIG; // Left Trigger
|
||||
else if (buttonId == 10)
|
||||
return VJoy.ELEM_RTRIG; // Right Trigger
|
||||
else if (buttonId <= 12)
|
||||
return VJoy.ELEM_ANALOG; // Analog
|
||||
else if (buttonId == 13)
|
||||
return VJoy.ELEM_FFORWARD; // Fast-forward
|
||||
else
|
||||
return VJoy.ELEM_DPAD; // DPAD diagonals
|
||||
}
|
||||
|
||||
private static int left_trigger = 0;
|
||||
private static int right_trigger = 0;
|
||||
private static int[] mouse_pos = { -32768, -32768 };
|
||||
private static int mouse_btns = 0;
|
||||
|
||||
private float editLastX = 0, editLastY = 0;
|
||||
|
||||
public boolean onTouchEvent(MotionEvent event, int width, int height)
|
||||
{
|
||||
if ((event.getSource() & InputDevice.SOURCE_TOUCHSCREEN) != InputDevice.SOURCE_TOUCHSCREEN)
|
||||
// Ignore real mice, trackballs, etc.
|
||||
return false;
|
||||
JNIdc.showVirtualGamepad();
|
||||
this.handler.removeCallbacks(hideVGamepadRunnable);
|
||||
if (!editVjoyMode)
|
||||
this.handler.postDelayed(hideVGamepadRunnable, 10000);
|
||||
|
||||
scaleGestureDetector.onTouchEvent(event);
|
||||
|
||||
float ty = 0.0f;
|
||||
float scl = height / 480.0f;
|
||||
float tx = (width - 640.0f * scl) / 2;
|
||||
|
||||
int rv = 0xFFFFFFFF;
|
||||
boolean fastForward = false;
|
||||
|
||||
int aid = event.getActionMasked();
|
||||
int pid = event.getActionIndex();
|
||||
|
||||
if (!JNIdc.guiIsOpen() || editVjoyMode) {
|
||||
if (editVjoyMode && selectedVjoyElement != VJoy.ELEM_NONE && aid == MotionEvent.ACTION_MOVE && !scaleGestureDetector.isInProgress()) {
|
||||
float x = (event.getX() - tx) / scl;
|
||||
float y = (event.getY() - ty) / scl;
|
||||
|
||||
if (editLastX != 0 && editLastY != 0) {
|
||||
float deltaX = x - editLastX;
|
||||
float deltaY = y - editLastY;
|
||||
|
||||
vjoy_d_custom[selectedVjoyElement][0] += isTablet() ? deltaX * 2 : deltaX;
|
||||
vjoy_d_custom[selectedVjoyElement][1] += isTablet() ? deltaY * 2 : deltaY;
|
||||
|
||||
view.requestLayout();
|
||||
}
|
||||
|
||||
editLastX = x;
|
||||
editLastY = y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < event.getPointerCount(); i++) {
|
||||
float x = (event.getX(i) - tx) / scl;
|
||||
float y = (event.getY(i) - ty) / scl;
|
||||
if (anal_id != event.getPointerId(i)) {
|
||||
if (aid == MotionEvent.ACTION_POINTER_UP && pid == i)
|
||||
continue;
|
||||
for (int j = 0; j < vjoy.length; j++)
|
||||
{
|
||||
if (!editVjoyMode && !vjoy_enabled[j])
|
||||
continue;
|
||||
if (x > vjoy[j][0] && x <= (vjoy[j][0] + vjoy[j][2])
|
||||
&& y > vjoy[j][1] && y <= (vjoy[j][1] + vjoy[j][3]))
|
||||
{
|
||||
if (vjoy[j][4] >= VJoy.BTN_RTRIG) {
|
||||
// Not for analog
|
||||
if (vjoy[j][5] == 0)
|
||||
if (!editVjoyMode) {
|
||||
vibratorThread.click();
|
||||
}
|
||||
vjoy[j][5] = 2;
|
||||
}
|
||||
|
||||
|
||||
if (vjoy[j][4] == VJoy.BTN_ANARING) {
|
||||
if (editVjoyMode) {
|
||||
selectedVjoyElement = VJoy.ELEM_ANALOG;
|
||||
resetEditMode();
|
||||
} else {
|
||||
vjoy[j + 1][0] = x - vjoy[j + 1][2] / 2;
|
||||
vjoy[j + 1][1] = y - vjoy[j + 1][3] / 2;
|
||||
|
||||
JNIdc.vjoy(j + 1, vjoy[j + 1][0], vjoy[j + 1][1], vjoy[j + 1][2], vjoy[j + 1][3]);
|
||||
anal_id = event.getPointerId(i);
|
||||
}
|
||||
} else if (vjoy[j][4] != VJoy.BTN_ANAPOINT) {
|
||||
if (vjoy[j][4] == VJoy.BTN_LTRIG) {
|
||||
if (editVjoyMode) {
|
||||
selectedVjoyElement = VJoy.ELEM_LTRIG;
|
||||
resetEditMode();
|
||||
} else {
|
||||
left_trigger = 255;
|
||||
lt_id = event.getPointerId(i);
|
||||
}
|
||||
} else if (vjoy[j][4] == VJoy.BTN_RTRIG) {
|
||||
if (editVjoyMode) {
|
||||
selectedVjoyElement = VJoy.ELEM_RTRIG;
|
||||
resetEditMode();
|
||||
} else {
|
||||
right_trigger = 255;
|
||||
rt_id = event.getPointerId(i);
|
||||
}
|
||||
} else {
|
||||
if (editVjoyMode) {
|
||||
selectedVjoyElement = getElementIdFromButtonId(j);
|
||||
resetEditMode();
|
||||
} else if (vjoy[j][4] == VJoy.key_CONT_FFORWARD)
|
||||
fastForward = true;
|
||||
else
|
||||
rv &= ~(int)vjoy[j][4];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (vjoy_enabled[11]) {
|
||||
// Analog stick
|
||||
if (x < vjoy[11][0])
|
||||
x = vjoy[11][0];
|
||||
else if (x > (vjoy[11][0] + vjoy[11][2]))
|
||||
x = vjoy[11][0] + vjoy[11][2];
|
||||
|
||||
if (y < vjoy[11][1])
|
||||
y = vjoy[11][1];
|
||||
else if (y > (vjoy[11][1] + vjoy[11][3]))
|
||||
y = vjoy[11][1] + vjoy[11][3];
|
||||
|
||||
int j = 11;
|
||||
vjoy[j + 1][0] = x - vjoy[j + 1][2] / 2;
|
||||
vjoy[j + 1][1] = y - vjoy[j + 1][3] / 2;
|
||||
|
||||
JNIdc.vjoy(j + 1, vjoy[j + 1][0], vjoy[j + 1][1], vjoy[j + 1][2], vjoy[j + 1][3]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < vjoy.length; j++) {
|
||||
if (vjoy[j][5] == 2)
|
||||
vjoy[j][5] = 1;
|
||||
else if (vjoy[j][5] == 1)
|
||||
vjoy[j][5] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch(aid)
|
||||
int actionMasked = event.getActionMasked();
|
||||
int actionIndex = event.getActionIndex();
|
||||
switch (actionMasked)
|
||||
{
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
selectedVjoyElement = -1;
|
||||
reset_analog();
|
||||
anal_id = -1;
|
||||
rv = 0xFFFFFFFF;
|
||||
fastForward = false;
|
||||
right_trigger = 0;
|
||||
left_trigger = 0;
|
||||
lt_id = -1;
|
||||
rt_id = -1;
|
||||
for (int j= 0 ;j < vjoy.length; j++)
|
||||
vjoy[j][5] = 0;
|
||||
mouse_btns = 0;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
if (event.getPointerId(event.getActionIndex())==anal_id)
|
||||
{
|
||||
reset_analog();
|
||||
anal_id = -1;
|
||||
}
|
||||
else if (event.getPointerId(event.getActionIndex())==lt_id)
|
||||
{
|
||||
left_trigger = 0;
|
||||
lt_id = -1;
|
||||
}
|
||||
else if (event.getPointerId(event.getActionIndex())==rt_id)
|
||||
{
|
||||
right_trigger = 0;
|
||||
rt_id = -1;
|
||||
}
|
||||
break;
|
||||
mousePid = -1;
|
||||
mouseButtons = 0;
|
||||
InputDeviceManager.getInstance().mouseEvent(mousePos[0], mousePos[1], mouseButtons);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (event.getPointerCount() != 1)
|
||||
if (mousePid == -1 || actionMasked == MotionEvent.ACTION_DOWN)
|
||||
{
|
||||
mouse_btns = 0;
|
||||
mousePid = event.getPointerId(actionIndex);
|
||||
mousePos[0] = Math.round(event.getX(actionIndex));
|
||||
mousePos[1] = Math.round(event.getY(actionIndex));
|
||||
mouseButtons = MotionEvent.BUTTON_PRIMARY; // Mouse left button down
|
||||
InputDeviceManager.getInstance().mouseEvent(mousePos[0], mousePos[1], mouseButtons);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mouse_pos[0] = Math.round(event.getX());
|
||||
mouse_pos[1] = Math.round(event.getY());
|
||||
mouse_btns = MotionEvent.BUTTON_PRIMARY; // Mouse left button down
|
||||
}
|
||||
break;
|
||||
return false;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (event.getPointerCount() == 1)
|
||||
for (int i = 0; i < event.getPointerCount(); i++)
|
||||
{
|
||||
mouse_pos[0] = Math.round(event.getX());
|
||||
mouse_pos[1] = Math.round(event.getY());
|
||||
if (event.getPointerId(i) == mousePid) {
|
||||
mousePos[0] = Math.round(event.getX(i));
|
||||
mousePos[1] = Math.round(event.getY(i));
|
||||
InputDeviceManager.getInstance().mouseEvent(mousePos[0], mousePos[1], mouseButtons);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
if (event.getPointerId(actionIndex) == mousePid)
|
||||
{
|
||||
mousePid = -1;
|
||||
mousePos[0] = Math.round(event.getX(actionIndex));
|
||||
mousePos[1] = Math.round(event.getY(actionIndex));
|
||||
mouseButtons = 0;
|
||||
InputDeviceManager.getInstance().mouseEvent(mousePos[0], mousePos[1], mouseButtons);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
int joyx = get_anal(11, 0);
|
||||
int joyy = get_anal(11, 1);
|
||||
InputDeviceManager.getInstance().virtualGamepadEvent(rv, joyx, joyy, left_trigger, right_trigger, fastForward);
|
||||
// Only register the mouse event if no virtual gamepad button is down
|
||||
if (!editVjoyMode && ((rv == 0xFFFFFFFF && left_trigger == 0 && right_trigger == 0 && joyx == 0 && joyy == 0 && !fastForward)
|
||||
|| JNIdc.guiIsOpen()))
|
||||
InputDeviceManager.getInstance().mouseEvent(mouse_pos[0], mouse_pos[1], mouse_btns);
|
||||
return(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setEditVjoyMode(boolean editVjoyMode) {
|
||||
this.editVjoyMode = editVjoyMode;
|
||||
selectedVjoyElement = -1;
|
||||
if (editVjoyMode) {
|
||||
this.handler.removeCallbacks(hideVGamepadRunnable);
|
||||
Arrays.fill(vjoy_enabled, true);
|
||||
static class Point
|
||||
{
|
||||
Point() {
|
||||
this.x = 0.f;
|
||||
this.y = 0.f;
|
||||
}
|
||||
resetEditMode();
|
||||
Point(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
float x;
|
||||
float y;
|
||||
}
|
||||
|
||||
public void enableVjoy(boolean[] state) {
|
||||
vjoy_enabled = state;
|
||||
static Point translateCoords(Point pos, Point size)
|
||||
{
|
||||
float hscale = 480.f / size.y;
|
||||
Point p = new Point();
|
||||
p.y = pos.y * hscale;
|
||||
p.x = (pos.x - (size.x - 640.f / hscale) / 2.f) * hscale;
|
||||
return p;
|
||||
}
|
||||
|
||||
private class OscOnScaleGestureListener extends
|
||||
ScaleGestureDetector.SimpleOnScaleGestureListener {
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event, int width, int height)
|
||||
{
|
||||
if (JNIdc.guiIsOpen())
|
||||
return touchMouseEvent(event);
|
||||
show();
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
if (editVjoyMode && selectedVjoyElement != -1) {
|
||||
vjoy_d_custom[selectedVjoyElement][2] *= detector.getScaleFactor();
|
||||
view.requestLayout();
|
||||
int actionIndex = event.getActionIndex();
|
||||
switch (event.getActionMasked())
|
||||
{
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
// Release all
|
||||
pidToControlId.clear();
|
||||
joyPointerId = -1;
|
||||
InputDeviceManager.getInstance().virtualReleaseAll();
|
||||
break;
|
||||
|
||||
return true;
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// First release all
|
||||
pidToControlId.clear();
|
||||
joyPointerId = -1;
|
||||
InputDeviceManager.getInstance().virtualReleaseAll();
|
||||
// Release the mouse too
|
||||
mousePid = -1;
|
||||
mouseButtons = 0;
|
||||
InputDeviceManager.getInstance().mouseEvent(mousePos[0], mousePos[1], mouseButtons);
|
||||
// Then fall through
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
{
|
||||
Point p = new Point(event.getX(actionIndex), event.getY(actionIndex));
|
||||
p = translateCoords(p, new Point(width, height));
|
||||
int control = VGamepad.hitTest(p.x, p.y);
|
||||
if (control != -1)
|
||||
{
|
||||
int pid = event.getPointerId(actionIndex);
|
||||
if (control == CTLID_ANARING || control == CTLID_ANASTICK)
|
||||
{
|
||||
if (joyPointerId == -1)
|
||||
{
|
||||
// Analog stick down
|
||||
joyPointerId = pid;
|
||||
joyBiasX = p.x;
|
||||
joyBiasY = p.y;
|
||||
InputDeviceManager.getInstance().virtualJoystick(0, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Button down
|
||||
InputDeviceManager.getInstance().virtualButtonInput(control, true);
|
||||
pidToControlId.put(pid, control);
|
||||
vibratorThread.click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for (int i = 0; i < event.getPointerCount(); i++)
|
||||
{
|
||||
int pid = event.getPointerId(i);
|
||||
Point p = new Point(event.getX(i), event.getY(i));
|
||||
p = translateCoords(p, new Point(width, height));
|
||||
if (joyPointerId == pid)
|
||||
{
|
||||
// Analog stick
|
||||
float dx = p.x - joyBiasX;
|
||||
float dy = p.y - joyBiasY;
|
||||
float sz = VGamepad.getControlWidth(CTLID_ANASTICK);
|
||||
dx = Math.max(Math.min(1.f, dx / sz), -1.f);
|
||||
dy = Math.max(Math.min(1.f, dy / sz), -1.f);
|
||||
InputDeviceManager.getInstance().virtualJoystick(dx, dy);
|
||||
continue;
|
||||
}
|
||||
// Buttons
|
||||
int control = VGamepad.hitTest(p.x, p.y);
|
||||
int oldControl = pidToControlId.containsKey(pid) ? pidToControlId.get(pid) : -1;
|
||||
if (oldControl == control)
|
||||
// same button still pressed, or none at all
|
||||
continue;
|
||||
if (oldControl != -1) {
|
||||
// Previous button up
|
||||
InputDeviceManager.getInstance().virtualButtonInput(oldControl, false);
|
||||
pidToControlId.remove(pid);
|
||||
}
|
||||
if (control != -1 && control != CTLID_ANARING && control != CTLID_ANASTICK)
|
||||
{
|
||||
// New button down
|
||||
InputDeviceManager.getInstance().virtualButtonInput(control, true);
|
||||
pidToControlId.put(pid, control);
|
||||
vibratorThread.click();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
selectedVjoyElement = -1;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
{
|
||||
int pid = event.getPointerId(actionIndex);
|
||||
if (joyPointerId == pid)
|
||||
{
|
||||
// Analog up
|
||||
InputDeviceManager.getInstance().virtualJoystick(0, 0);
|
||||
joyPointerId = -1;
|
||||
return true;
|
||||
}
|
||||
if (pidToControlId.containsKey(pid))
|
||||
{
|
||||
// Button up
|
||||
int controlId = pidToControlId.get(pid);
|
||||
InputDeviceManager.getInstance().virtualButtonInput(controlId, false);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return touchMouseEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show()
|
||||
{
|
||||
VGamepad.show();
|
||||
this.handler.removeCallbacks(hideVGamepadRunnable);
|
||||
this.handler.postDelayed(hideVGamepadRunnable, 10000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
|
|||
private InputManager inputManager;
|
||||
private int maple_port = 0;
|
||||
|
||||
private boolean hasTouchscreen = false;
|
||||
|
||||
private static class VibrationParams {
|
||||
float power;
|
||||
float inclination;
|
||||
|
@ -39,9 +41,10 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
|
|||
public void startListening(Context applicationContext)
|
||||
{
|
||||
maple_port = 0;
|
||||
if (applicationContext.getPackageManager().hasSystemFeature("android.hardware.touchscreen"))
|
||||
joystickAdded(VIRTUAL_GAMEPAD_ID, "Virtual Gamepad", 0, "virtual_gamepad_uid",
|
||||
new int[0], new int[0], getVibrator(VIRTUAL_GAMEPAD_ID) != null);
|
||||
hasTouchscreen = applicationContext.getPackageManager().hasSystemFeature("android.hardware.touchscreen");
|
||||
if (hasTouchscreen)
|
||||
joystickAdded(VIRTUAL_GAMEPAD_ID, null, 0, null,
|
||||
null, null, getVibrator(VIRTUAL_GAMEPAD_ID) != null);
|
||||
int[] ids = InputDevice.getDeviceIds();
|
||||
for (int id : ids)
|
||||
onInputDeviceAdded(id);
|
||||
|
@ -202,12 +205,18 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
|
|||
}
|
||||
}
|
||||
|
||||
public boolean hasTouchscreen() {
|
||||
return hasTouchscreen;
|
||||
}
|
||||
|
||||
public static InputDeviceManager getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public native void init();
|
||||
public native void virtualGamepadEvent(int kcode, int joyx, int joyy, int lt, int rt, boolean fastForward);
|
||||
public native void virtualReleaseAll();
|
||||
public native void virtualJoystick(float x, float y);
|
||||
public native void virtualButtonInput(int key, boolean pressed);
|
||||
public native boolean joystickButtonEvent(int id, int button, boolean pressed);
|
||||
public native boolean joystickAxisEvent(int id, int button, int value);
|
||||
public native void mouseEvent(int xpos, int ypos, int buttons);
|
||||
|
@ -216,4 +225,5 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene
|
|||
private native void joystickRemoved(int id);
|
||||
public native boolean keyboardEvent(int key, boolean pressed);
|
||||
public native void keyboardText(int c);
|
||||
public static native boolean isMicPluggedIn();
|
||||
}
|
||||
|
|
|
@ -1,219 +0,0 @@
|
|||
package com.flycast.emulator.periph;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class VJoy {
|
||||
|
||||
public static final int key_CONT_C = 0x0001;
|
||||
public static final int key_CONT_B = 0x0002;
|
||||
public static final int key_CONT_A = 0x0004;
|
||||
public static final int key_CONT_START = 0x0008;
|
||||
public static final int key_CONT_DPAD_UP = 0x0010;
|
||||
public static final int key_CONT_DPAD_DOWN = 0x0020;
|
||||
public static final int key_CONT_DPAD_LEFT = 0x0040;
|
||||
public static final int key_CONT_DPAD_RIGHT = 0x0080;
|
||||
public static final int key_CONT_Y = 0x0200;
|
||||
public static final int key_CONT_X = 0x0400;
|
||||
public static final int key_CONT_FFORWARD = 0x3000002;
|
||||
|
||||
public static final int BTN_LTRIG = -1;
|
||||
public static final int BTN_RTRIG = -2;
|
||||
public static final int BTN_ANARING = -3;
|
||||
public static final int BTN_ANAPOINT = -4;
|
||||
|
||||
public static final int ELEM_NONE = -1;
|
||||
public static final int ELEM_DPAD = 0;
|
||||
public static final int ELEM_BUTTONS = 1;
|
||||
public static final int ELEM_START = 2;
|
||||
public static final int ELEM_LTRIG = 3;
|
||||
public static final int ELEM_RTRIG = 4;
|
||||
public static final int ELEM_ANALOG = 5;
|
||||
public static final int ELEM_FFORWARD = 6;
|
||||
|
||||
public static int VJoyCount = 14;
|
||||
|
||||
public static float[][] baseVJoy() {
|
||||
return new float[][] {
|
||||
new float[] { 24, 24+64, 64,64, key_CONT_DPAD_LEFT, 0},
|
||||
new float[] { 24+64, 24, 64,64, key_CONT_DPAD_UP, 0},
|
||||
new float[] { 24+128, 24+64, 64,64, key_CONT_DPAD_RIGHT, 0},
|
||||
new float[] { 24+64, 24+128, 64,64, key_CONT_DPAD_DOWN, 0},
|
||||
|
||||
new float[] { 440, 280+64, 64,64, key_CONT_X, 0},
|
||||
new float[] { 440+64, 280, 64,64, key_CONT_Y, 0},
|
||||
new float[] { 440+128, 280+64, 64,64, key_CONT_B, 0},
|
||||
new float[] { 440+64, 280+128,64,64, key_CONT_A, 0},
|
||||
|
||||
new float[] { 320-32, 360+32, 64,64, key_CONT_START, 0},
|
||||
|
||||
new float[] { 440, 200, 90,64, BTN_LTRIG, 0}, // LT
|
||||
new float[] { 542, 200, 90,64, BTN_RTRIG, 0}, // RT
|
||||
|
||||
new float[] { 0, 128+224,128,128,BTN_ANARING, 0}, // Analog ring
|
||||
new float[] { 32, 128+256,64,64, BTN_ANAPOINT, 0}, // Analog point
|
||||
|
||||
new float[] { 320-32, 12, 64,64, key_CONT_FFORWARD, 0}, // Fast-forward
|
||||
|
||||
new float[] { 20, 288, 64,64, key_CONT_DPAD_LEFT|key_CONT_DPAD_UP, 0}, // DPad diagonals
|
||||
new float[] { 20+128, 288, 64,64, key_CONT_DPAD_RIGHT|key_CONT_DPAD_UP, 0},
|
||||
new float[] { 20, 288+128,64,64, key_CONT_DPAD_LEFT|key_CONT_DPAD_DOWN, 0},
|
||||
new float[] { 20+128, 288+128,64,64, key_CONT_DPAD_RIGHT|key_CONT_DPAD_DOWN, 0},
|
||||
};
|
||||
}
|
||||
|
||||
public static float[][] readCustomVjoyValues(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
return new float[][] {
|
||||
// x-shift, y-shift, sizing-factor
|
||||
new float[] { prefs.getFloat("touch_x_shift_dpad", 0),
|
||||
prefs.getFloat("touch_y_shift_dpad", 0),
|
||||
prefs.getFloat("touch_scale_dpad", 1)
|
||||
}, // DPAD
|
||||
new float[] { prefs.getFloat("touch_x_shift_buttons", 0),
|
||||
prefs.getFloat("touch_y_shift_buttons", 0),
|
||||
prefs.getFloat("touch_scale_buttons", 1)
|
||||
}, // X, Y, B, A Buttons
|
||||
new float[] { prefs.getFloat("touch_x_shift_start", 0),
|
||||
prefs.getFloat("touch_y_shift_start", 0),
|
||||
prefs.getFloat("touch_scale_start", 1)
|
||||
}, // Start
|
||||
new float[] { prefs.getFloat("touch_x_shift_left_trigger", 0),
|
||||
prefs.getFloat("touch_y_shift_left_trigger", 0),
|
||||
prefs.getFloat("touch_scale_left_trigger", 1)
|
||||
}, // Left Trigger
|
||||
new float[] { prefs.getFloat("touch_x_shift_right_trigger", 0),
|
||||
prefs.getFloat("touch_y_shift_right_trigger", 0),
|
||||
prefs.getFloat("touch_scale_right_trigger", 1)
|
||||
}, // Right Trigger
|
||||
new float[] { prefs.getFloat("touch_x_shift_analog", 0),
|
||||
prefs.getFloat("touch_y_shift_analog", 0),
|
||||
prefs.getFloat("touch_scale_analog", 1)
|
||||
}, // Analog Stick
|
||||
new float[] { prefs.getFloat("touch_x_shift_fforward", 0),
|
||||
prefs.getFloat("touch_y_shift_fforward", 0),
|
||||
prefs.getFloat("touch_scale_fforward", 1)
|
||||
} // Fast-forward
|
||||
};
|
||||
}
|
||||
|
||||
public static float[][] getVjoy_d(float[][] vjoy_d_custom) {
|
||||
return new float[][] {
|
||||
// LEFT, UP, RIGHT, DOWN
|
||||
new float[] { 20+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+64*vjoy_d_custom[0][2]+vjoy_d_custom[0][1],
|
||||
64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_LEFT},
|
||||
new float[] { 20+64*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][1],
|
||||
64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_UP},
|
||||
new float[] { 20+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+64*vjoy_d_custom[0][2]+vjoy_d_custom[0][1],
|
||||
64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_RIGHT},
|
||||
new float[] { 20+64*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][1],
|
||||
64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_DOWN},
|
||||
|
||||
// X, Y, B, A
|
||||
new float[] { 448+0*vjoy_d_custom[1][2]+vjoy_d_custom[1][0], 288+64*vjoy_d_custom[1][2]+vjoy_d_custom[1][1],
|
||||
64*vjoy_d_custom[1][2],64*vjoy_d_custom[1][2], key_CONT_X},
|
||||
new float[] { 448+64*vjoy_d_custom[1][2]+vjoy_d_custom[1][0], 288+0*vjoy_d_custom[1][2]+vjoy_d_custom[1][1],
|
||||
64*vjoy_d_custom[1][2],64*vjoy_d_custom[1][2], key_CONT_Y},
|
||||
new float[] { 448+128*vjoy_d_custom[1][2]+vjoy_d_custom[1][0], 288+64*vjoy_d_custom[1][2]+vjoy_d_custom[1][1],
|
||||
64*vjoy_d_custom[1][2],64*vjoy_d_custom[1][2], key_CONT_B},
|
||||
new float[] { 448+64*vjoy_d_custom[1][2]+vjoy_d_custom[1][0], 288+128*vjoy_d_custom[1][2]+vjoy_d_custom[1][1],
|
||||
64*vjoy_d_custom[1][2],64*vjoy_d_custom[1][2], key_CONT_A},
|
||||
|
||||
// START
|
||||
new float[] { 320-32+vjoy_d_custom[2][0], 288+128+vjoy_d_custom[2][1],
|
||||
64*vjoy_d_custom[2][2],64*vjoy_d_custom[2][2], key_CONT_START},
|
||||
|
||||
// LT, RT
|
||||
new float[] { 440+vjoy_d_custom[3][0], 200+vjoy_d_custom[3][1],
|
||||
90*vjoy_d_custom[3][2],64*vjoy_d_custom[3][2], -1},
|
||||
new float[] { 542+vjoy_d_custom[4][0], 200+vjoy_d_custom[4][1],
|
||||
90*vjoy_d_custom[4][2],64*vjoy_d_custom[4][2], -2},
|
||||
|
||||
// Analog ring and point
|
||||
new float[] { 16+vjoy_d_custom[5][0], 24+32+vjoy_d_custom[5][1],
|
||||
128*vjoy_d_custom[5][2],128*vjoy_d_custom[5][2],-3},
|
||||
new float[] { 48+vjoy_d_custom[5][0], 24+64+vjoy_d_custom[5][1],
|
||||
64*vjoy_d_custom[5][2],64*vjoy_d_custom[5][2], -4},
|
||||
|
||||
// Fast-forward
|
||||
new float[] { 320-32+vjoy_d_custom[6][0], 12+vjoy_d_custom[6][1],
|
||||
64*vjoy_d_custom[6][2],64*vjoy_d_custom[6][2], -5},
|
||||
|
||||
// DPad diagonals
|
||||
new float[] { 20+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][1],
|
||||
64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_LEFT|key_CONT_DPAD_UP},
|
||||
new float[] { 20+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][1],
|
||||
64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_RIGHT|key_CONT_DPAD_UP},
|
||||
new float[] { 20+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][1],
|
||||
64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_LEFT|key_CONT_DPAD_DOWN},
|
||||
new float[] { 20+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][1],
|
||||
64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_RIGHT|key_CONT_DPAD_DOWN},
|
||||
};
|
||||
}
|
||||
|
||||
public static void writeCustomVjoyValues(float[][] vjoy_d_custom, Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
prefs.edit().putFloat("touch_x_shift_dpad", vjoy_d_custom[0][0]).apply();
|
||||
prefs.edit().putFloat("touch_y_shift_dpad", vjoy_d_custom[0][1]).apply();
|
||||
prefs.edit().putFloat("touch_scale_dpad", vjoy_d_custom[0][2]).apply();
|
||||
|
||||
prefs.edit().putFloat("touch_x_shift_buttons", vjoy_d_custom[1][0]).apply();
|
||||
prefs.edit().putFloat("touch_y_shift_buttons", vjoy_d_custom[1][1]).apply();
|
||||
prefs.edit().putFloat("touch_scale_buttons", vjoy_d_custom[1][2]).apply();
|
||||
|
||||
prefs.edit().putFloat("touch_x_shift_start", vjoy_d_custom[2][0]).apply();
|
||||
prefs.edit().putFloat("touch_y_shift_start", vjoy_d_custom[2][1]).apply();
|
||||
prefs.edit().putFloat("touch_scale_start", vjoy_d_custom[2][2]).apply();
|
||||
|
||||
prefs.edit().putFloat("touch_x_shift_left_trigger", vjoy_d_custom[3][0]).apply();
|
||||
prefs.edit().putFloat("touch_y_shift_left_trigger", vjoy_d_custom[3][1]).apply();
|
||||
prefs.edit().putFloat("touch_scale_left_trigger", vjoy_d_custom[3][2]).apply();
|
||||
|
||||
prefs.edit().putFloat("touch_x_shift_right_trigger", vjoy_d_custom[4][0]).apply();
|
||||
prefs.edit().putFloat("touch_y_shift_right_trigger", vjoy_d_custom[4][1]).apply();
|
||||
prefs.edit().putFloat("touch_scale_right_trigger", vjoy_d_custom[4][2]).apply();
|
||||
|
||||
prefs.edit().putFloat("touch_x_shift_analog", vjoy_d_custom[5][0]).apply();
|
||||
prefs.edit().putFloat("touch_y_shift_analog", vjoy_d_custom[5][1]).apply();
|
||||
prefs.edit().putFloat("touch_scale_analog", vjoy_d_custom[5][2]).apply();
|
||||
|
||||
prefs.edit().putFloat("touch_x_shift_fforward", vjoy_d_custom[6][0]).apply();
|
||||
prefs.edit().putFloat("touch_y_shift_fforward", vjoy_d_custom[6][1]).apply();
|
||||
prefs.edit().putFloat("touch_scale_fforward", vjoy_d_custom[6][2]).apply();
|
||||
}
|
||||
|
||||
public static void resetCustomVjoyValues(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
prefs.edit().remove("touch_x_shift_dpad").apply();
|
||||
prefs.edit().remove("touch_y_shift_dpad").apply();
|
||||
prefs.edit().remove("touch_scale_dpad").apply();
|
||||
|
||||
prefs.edit().remove("touch_x_shift_buttons").apply();
|
||||
prefs.edit().remove("touch_y_shift_buttons").apply();
|
||||
prefs.edit().remove("touch_scale_buttons").apply();
|
||||
|
||||
prefs.edit().remove("touch_x_shift_start").apply();
|
||||
prefs.edit().remove("touch_y_shift_start").apply();
|
||||
prefs.edit().remove("touch_scale_start").apply();
|
||||
|
||||
prefs.edit().remove("touch_x_shift_left_trigger").apply();
|
||||
prefs.edit().remove("touch_y_shift_left_trigger").apply();
|
||||
prefs.edit().remove("touch_scale_left_trigger").apply();
|
||||
|
||||
prefs.edit().remove("touch_x_shift_right_trigger").apply();
|
||||
prefs.edit().remove("touch_y_shift_right_trigger").apply();
|
||||
prefs.edit().remove("touch_scale_right_trigger").apply();
|
||||
|
||||
prefs.edit().remove("touch_x_shift_analog").apply();
|
||||
prefs.edit().remove("touch_y_shift_analog").apply();
|
||||
prefs.edit().remove("touch_scale_analog").apply();
|
||||
|
||||
prefs.edit().remove("touch_x_shift_fforward").apply();
|
||||
prefs.edit().remove("touch_y_shift_fforward").apply();
|
||||
prefs.edit().remove("touch_scale_fforward").apply();
|
||||
}
|
||||
}
|
|
@ -1,12 +1,8 @@
|
|||
#include "types.h"
|
||||
#include "hw/maple/maple_cfg.h"
|
||||
#include "hw/maple/maple_devs.h"
|
||||
#include "hw/maple/maple_if.h"
|
||||
#include "hw/naomi/naomi_cart.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "imgread/common.h"
|
||||
#include "ui/gui.h"
|
||||
#include "ui/vgamepad.h"
|
||||
#include "rend/osd.h"
|
||||
#include "cfg/cfg.h"
|
||||
#include "log/LogManager.h"
|
||||
|
@ -39,24 +35,14 @@ namespace jni
|
|||
thread_local JVMAttacher jvm_attacher;
|
||||
}
|
||||
|
||||
#include "android_gamepad.h"
|
||||
#include "android_keyboard.h"
|
||||
#include "http_client.h"
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL Java_com_flycast_emulator_emu_JNIdc_getVirtualGamepadVibration(JNIEnv *env, jobject obj)
|
||||
{
|
||||
return (jint)config::VirtualGamepadVibration;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_screenCharacteristics(JNIEnv *env, jobject obj, jfloat screenDpi, jfloat refreshRate)
|
||||
{
|
||||
settings.display.dpi = screenDpi;
|
||||
settings.display.refreshRate = refreshRate;
|
||||
}
|
||||
|
||||
std::shared_ptr<AndroidMouse> mouse;
|
||||
std::shared_ptr<AndroidKeyboard> keyboard;
|
||||
|
||||
static bool game_started;
|
||||
|
||||
//stuff for saving prefs
|
||||
|
@ -65,12 +51,10 @@ jmethodID saveAndroidSettingsMid;
|
|||
static ANativeWindow *g_window = 0;
|
||||
|
||||
// Activity
|
||||
static jobject g_activity;
|
||||
static jmethodID VJoyStartEditingMID;
|
||||
static jmethodID VJoyStopEditingMID;
|
||||
static jmethodID VJoyResetEditingMID;
|
||||
static jmethodID VJoyEnableControlsMID;
|
||||
static jmethodID showScreenKeyboardMid;
|
||||
jobject g_activity;
|
||||
extern jmethodID VJoyStartEditingMID;
|
||||
extern jmethodID VJoyStopEditingMID;
|
||||
extern jmethodID showScreenKeyboardMid;
|
||||
static jmethodID onGameStateChangeMid;
|
||||
|
||||
static void emuEventCallback(Event event, void *)
|
||||
|
@ -353,42 +337,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_rendinitNa
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_vjoy(JNIEnv * env, jobject obj,int id,float x, float y, float w, float h)
|
||||
{
|
||||
vgamepad::setPosition(static_cast<vgamepad::ControlId>(id), x, y, w, h);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_showVirtualGamepad(JNIEnv * env, jobject obj)
|
||||
{
|
||||
vgamepad::show();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_hideVirtualGamepad(JNIEnv * env, jobject obj)
|
||||
{
|
||||
vgamepad::hide();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_getControllers(JNIEnv *env, jobject obj, jintArray controllers, jobjectArray peripherals)
|
||||
{
|
||||
// might be called before JNIdc.initEnvironment()
|
||||
if (g_jvm == NULL)
|
||||
env->GetJavaVM(&g_jvm);
|
||||
|
||||
jni::IntArray jcontrollers(controllers, false);
|
||||
std::vector<int> devs;
|
||||
for (u32 i = 0; i < config::MapleMainDevices.size(); i++)
|
||||
devs.push_back((MapleDeviceType)config::MapleMainDevices[i]);
|
||||
jcontrollers.setData(devs.data());
|
||||
|
||||
jni::ObjectArray<jni::IntArray> jperipherals(peripherals, false);
|
||||
int obj_len = jperipherals.size();
|
||||
for (int i = 0; i < obj_len; ++i)
|
||||
{
|
||||
std::vector<int> devs { (MapleDeviceType)config::MapleExpansionDevices[i][0], (MapleDeviceType)config::MapleExpansionDevices[i][1] };
|
||||
jperipherals[i].setData(devs.data());
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_guiOpenSettings(JNIEnv *env, jobject obj)
|
||||
{
|
||||
gui_open_settings();
|
||||
|
@ -495,92 +443,6 @@ void SaveAndroidSettings()
|
|||
jni::env()->CallVoidMethod(g_emulator, saveAndroidSettingsMid, (jstring)homeDirectory);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_init(JNIEnv *env, jobject obj)
|
||||
{
|
||||
input_device_manager = env->NewGlobalRef(obj);
|
||||
input_device_manager_rumble = env->GetMethodID(env->GetObjectClass(obj), "rumble", "(IFFI)Z");
|
||||
// FIXME Don't connect it by default or any screen touch will register as button A press
|
||||
mouse = std::make_shared<AndroidMouse>(-1);
|
||||
GamepadDevice::Register(mouse);
|
||||
keyboard = std::make_shared<AndroidKeyboard>();
|
||||
GamepadDevice::Register(keyboard);
|
||||
gui_setOnScreenKeyboardCallback([](bool show) {
|
||||
if (g_activity != nullptr)
|
||||
jni::env()->CallVoidMethod(g_activity, showScreenKeyboardMid, show);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name,
|
||||
jint maple_port, jstring junique_id, jintArray fullAxes, jintArray halfAxes, jboolean hasRumble)
|
||||
{
|
||||
std::string joyname = jni::String(name, false);
|
||||
std::string unique_id = jni::String(junique_id, false);
|
||||
std::vector<int> full = jni::IntArray(fullAxes, false);
|
||||
std::vector<int> half = jni::IntArray(halfAxes, false);
|
||||
|
||||
std::shared_ptr<AndroidGamepadDevice> gamepad = std::make_shared<AndroidGamepadDevice>(maple_port, id, joyname.c_str(), unique_id.c_str(), full, half);
|
||||
AndroidGamepadDevice::AddAndroidGamepad(gamepad);
|
||||
gamepad->setRumbleEnabled(hasRumble);
|
||||
}
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj, jint id)
|
||||
{
|
||||
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
|
||||
if (device != NULL)
|
||||
AndroidGamepadDevice::RemoveAndroidGamepad(device);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_virtualGamepadEvent(JNIEnv *env, jobject obj, jint kcode, jint joyx, jint joyy, jint lt, jint rt, jboolean fastForward)
|
||||
{
|
||||
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(AndroidGamepadDevice::VIRTUAL_GAMEPAD_ID);
|
||||
if (device != NULL)
|
||||
device->virtual_gamepad_event(kcode, joyx, joyy, lt, rt, fastForward);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickButtonEvent(JNIEnv *env, jobject obj, jint id, jint key, jboolean pressed)
|
||||
{
|
||||
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
|
||||
if (device != NULL)
|
||||
return device->gamepad_btn_input(key, pressed);
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardEvent(JNIEnv *env, jobject obj, jint key, jboolean pressed)
|
||||
{
|
||||
keyboard->input(key, pressed);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardText(JNIEnv *env, jobject obj, jint c)
|
||||
{
|
||||
gui_keyboard_input((u16)c);
|
||||
}
|
||||
|
||||
static std::map<std::pair<jint, jint>, jint> previous_axis_values;
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickAxisEvent(JNIEnv *env, jobject obj, jint id, jint key, jint value)
|
||||
{
|
||||
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
|
||||
if (device != nullptr)
|
||||
return device->gamepad_axis_input(key, value);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_mouseEvent(JNIEnv *env, jobject obj, jint xpos, jint ypos, jint buttons)
|
||||
{
|
||||
mouse->setAbsPos(xpos, ypos, settings.display.width, settings.display.height);
|
||||
mouse->setButton(Mouse::LEFT_BUTTON, (buttons & 1) != 0);
|
||||
mouse->setButton(Mouse::RIGHT_BUTTON, (buttons & 2) != 0);
|
||||
mouse->setButton(Mouse::MIDDLE_BUTTON, (buttons & 4) != 0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_mouseScrollEvent(JNIEnv *env, jobject obj, jint scrollValue)
|
||||
{
|
||||
mouse->setWheel(scrollValue);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_BaseGLActivity_register(JNIEnv *env, jobject obj, jobject activity)
|
||||
{
|
||||
if (g_activity != nullptr) {
|
||||
|
@ -592,40 +454,12 @@ extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_BaseGLActivity_regis
|
|||
g_activity = env->NewGlobalRef(activity);
|
||||
jclass actClass = env->GetObjectClass(activity);
|
||||
VJoyStartEditingMID = env->GetMethodID(actClass, "VJoyStartEditing", "()V");
|
||||
VJoyStopEditingMID = env->GetMethodID(actClass, "VJoyStopEditing", "(Z)V");
|
||||
VJoyResetEditingMID = env->GetMethodID(actClass, "VJoyResetEditing", "()V");
|
||||
VJoyEnableControlsMID = env->GetMethodID(actClass, "VJoyEnableControls", "([Z)V");
|
||||
VJoyStopEditingMID = env->GetMethodID(actClass, "VJoyStopEditing", "()V");
|
||||
showScreenKeyboardMid = env->GetMethodID(actClass, "showScreenKeyboard", "(Z)V");
|
||||
onGameStateChangeMid = env->GetMethodID(actClass, "onGameStateChange", "(Z)V");
|
||||
}
|
||||
}
|
||||
|
||||
namespace vgamepad
|
||||
{
|
||||
|
||||
void startEditing() {
|
||||
enableAllControls();
|
||||
jni::env()->CallVoidMethod(g_activity, VJoyStartEditingMID);
|
||||
}
|
||||
void pauseEditing() {
|
||||
stopEditing(false);
|
||||
}
|
||||
void resetEditing() {
|
||||
jni::env()->CallVoidMethod(g_activity, VJoyResetEditingMID);
|
||||
}
|
||||
void stopEditing(bool canceled) {
|
||||
jni::env()->CallVoidMethod(g_activity, VJoyStopEditingMID, canceled);
|
||||
}
|
||||
|
||||
void setEnabledControls(bool enabled[_Count])
|
||||
{
|
||||
jni::BooleanArray jb{_Count};
|
||||
jb.setData(enabled, 0, _Count);
|
||||
jni::env()->CallVoidMethod(g_activity, VJoyEnableControlsMID, (jbooleanArray)jb);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void enableNetworkBroadcast(bool enable)
|
||||
{
|
||||
JNIEnv *env = jni::env();
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "input/gamepad_device.h"
|
||||
#include "input/mouse.h"
|
||||
#include "input/virtual_gamepad.h"
|
||||
#include "jni_util.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -97,26 +98,18 @@ class AndroidGamepadDevice : public GamepadDevice
|
|||
public:
|
||||
AndroidGamepadDevice(int maple_port, int id, const char *name, const char *unique_id,
|
||||
const std::vector<int>& fullAxes, const std::vector<int>& halfAxes)
|
||||
: GamepadDevice(maple_port, "Android", id != VIRTUAL_GAMEPAD_ID), android_id(id),
|
||||
: GamepadDevice(maple_port, "Android"), android_id(id),
|
||||
fullAxes(fullAxes), halfAxes(halfAxes)
|
||||
{
|
||||
_name = name;
|
||||
_unique_id = unique_id;
|
||||
INFO_LOG(INPUT, "Android: Opened joystick %d on port %d: '%s' descriptor '%s'", id, maple_port, _name.c_str(), _unique_id.c_str());
|
||||
if (id == VIRTUAL_GAMEPAD_ID)
|
||||
{
|
||||
input_mapper = std::make_shared<IdentityInputMapping>();
|
||||
// hasAnalogStick = true; // TODO has an analog stick but input mapping isn't persisted
|
||||
}
|
||||
else
|
||||
{
|
||||
loadMapping();
|
||||
save_mapping();
|
||||
hasAnalogStick = !fullAxes.empty();
|
||||
}
|
||||
|
||||
loadMapping();
|
||||
save_mapping();
|
||||
hasAnalogStick = !fullAxes.empty();
|
||||
}
|
||||
~AndroidGamepadDevice() override
|
||||
{
|
||||
~AndroidGamepadDevice() override {
|
||||
INFO_LOG(INPUT, "Android: Joystick '%s' on port %d disconnected", _name.c_str(), maple_port());
|
||||
}
|
||||
|
||||
|
@ -247,66 +240,6 @@ public:
|
|||
GamepadDevice::Unregister(gamepad);
|
||||
};
|
||||
|
||||
void virtual_gamepad_event(int kcode, int joyx, int joyy, int lt, int rt, bool fastForward)
|
||||
{
|
||||
// No virtual gamepad when the GUI is open: touch events only
|
||||
if (gui_is_open() && gui_state != GuiState::VJoyEdit)
|
||||
{
|
||||
kcode = 0xffffffff;
|
||||
joyx = joyy = rt = lt = 0;
|
||||
}
|
||||
if (settings.platform.isArcade())
|
||||
{
|
||||
if (rt > 0)
|
||||
{
|
||||
if ((kcode & DC_BTN_A) == 0)
|
||||
// RT + A -> D (coin)
|
||||
kcode &= ~DC_BTN_D;
|
||||
if ((kcode & DC_BTN_B) == 0)
|
||||
// RT + B -> Service
|
||||
kcode &= ~DC_DPAD2_UP;
|
||||
if ((kcode & DC_BTN_X) == 0)
|
||||
// RT + X -> Test
|
||||
kcode &= ~DC_DPAD2_DOWN;
|
||||
}
|
||||
// arcade mapping: X -> btn2, Y -> btn3
|
||||
if ((kcode & DC_BTN_X) == 0)
|
||||
{
|
||||
kcode &= ~DC_BTN_C;
|
||||
kcode |= DC_BTN_X;
|
||||
}
|
||||
if ((kcode & DC_BTN_Y) == 0)
|
||||
{
|
||||
kcode &= ~DC_BTN_X;
|
||||
kcode |= DC_BTN_Y;
|
||||
}
|
||||
if (rt > 0)
|
||||
// naomi btn4
|
||||
kcode &= ~DC_BTN_Y;
|
||||
if (lt > 0)
|
||||
// naomi btn5
|
||||
kcode &= ~DC_BTN_Z;
|
||||
}
|
||||
u32 changes = kcode ^ previous_kcode;
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (changes & (1 << i))
|
||||
gamepad_btn_input(1 << i, (kcode & (1 << i)) == 0);
|
||||
if (joyx >= 0)
|
||||
gamepad_axis_input(DC_AXIS_RIGHT, joyx | (joyx << 8));
|
||||
else
|
||||
gamepad_axis_input(DC_AXIS_LEFT, -joyx | (-joyx << 8));
|
||||
if (joyy >= 0)
|
||||
gamepad_axis_input(DC_AXIS_DOWN, joyy | (joyy << 8));
|
||||
else
|
||||
gamepad_axis_input(DC_AXIS_UP, -joyy | (-joyy << 8));
|
||||
gamepad_axis_input(DC_AXIS_LT, lt == 0 ? 0 : 0x7fff);
|
||||
gamepad_axis_input(DC_AXIS_RT, rt == 0 ? 0 : 0x7fff);
|
||||
previous_kcode = kcode;
|
||||
if (fastForward != previousFastForward)
|
||||
gamepad_btn_input(EMU_BTN_FFORWARD, fastForward);
|
||||
previousFastForward = fastForward;
|
||||
}
|
||||
|
||||
void rumble(float power, float inclination, u32 duration_ms) override
|
||||
{
|
||||
power *= rumblePower / 100.f;
|
||||
|
@ -317,8 +250,6 @@ public:
|
|||
this->rumbleEnabled = rumbleEnabled;
|
||||
}
|
||||
|
||||
bool is_virtual_gamepad() override { return android_id == VIRTUAL_GAMEPAD_ID; }
|
||||
|
||||
bool hasHalfAxis(int axis) const { return std::find(halfAxes.begin(), halfAxes.end(), axis) != halfAxes.end(); }
|
||||
bool hasFullAxis(int axis) const { return std::find(fullAxes.begin(), fullAxes.end(), axis) != fullAxes.end(); }
|
||||
|
||||
|
@ -336,13 +267,9 @@ public:
|
|||
input_mapper = std::make_shared<DefaultInputMapping<false, false>>(*this);
|
||||
}
|
||||
|
||||
static const int VIRTUAL_GAMEPAD_ID = 0x12345678; // must match the Java definition
|
||||
|
||||
private:
|
||||
int android_id;
|
||||
static std::map<int, std::shared_ptr<AndroidGamepadDevice>> android_gamepads;
|
||||
u32 previous_kcode = 0xffffffff;
|
||||
bool previousFastForward = false;
|
||||
std::vector<int> fullAxes;
|
||||
std::vector<int> halfAxes;
|
||||
};
|
||||
|
@ -481,3 +408,19 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
class AndroidVirtualGamepad : public VirtualGamepad
|
||||
{
|
||||
public:
|
||||
AndroidVirtualGamepad(bool rumbleEnabled) : VirtualGamepad("Android") {
|
||||
this->rumbleEnabled = rumbleEnabled;
|
||||
}
|
||||
|
||||
void rumble(float power, float inclination, u32 duration_ms) override
|
||||
{
|
||||
power *= rumblePower / 100.f;
|
||||
jboolean has_vibrator = jni::env()->CallBooleanMethod(input_device_manager, input_device_manager_rumble, GAMEPAD_ID, power, inclination, duration_ms);
|
||||
rumbleEnabled = has_vibrator;
|
||||
}
|
||||
|
||||
static constexpr int GAMEPAD_ID = 0x12345678; // must match the Java definition
|
||||
};
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
Copyright 2024 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast 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.
|
||||
|
||||
Flycast 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 Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "android_gamepad.h"
|
||||
#include "android_keyboard.h"
|
||||
#include "ui/vgamepad.h"
|
||||
#include "cfg/option.h"
|
||||
#include "hw/maple/maple_if.h"
|
||||
|
||||
std::shared_ptr<AndroidMouse> mouse;
|
||||
std::shared_ptr<AndroidKeyboard> keyboard;
|
||||
std::shared_ptr<AndroidVirtualGamepad> virtualGamepad;
|
||||
|
||||
extern jobject g_activity;
|
||||
jmethodID VJoyStartEditingMID;
|
||||
jmethodID VJoyStopEditingMID;
|
||||
jmethodID VJoyEnableControlsMID;
|
||||
jmethodID showScreenKeyboardMid;
|
||||
|
||||
//
|
||||
// VGamepad
|
||||
//
|
||||
extern "C" JNIEXPORT jint JNICALL Java_com_flycast_emulator_emu_VGamepad_getVibrationPower(JNIEnv *env, jobject obj) {
|
||||
return (jint)config::VirtualGamepadVibration;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_VGamepad_show(JNIEnv * env, jobject obj) {
|
||||
vgamepad::show();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_VGamepad_hide(JNIEnv * env, jobject obj) {
|
||||
vgamepad::hide();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL Java_com_flycast_emulator_emu_VGamepad_hitTest(JNIEnv * env, jobject obj,
|
||||
jfloat x, jfloat y) {
|
||||
return vgamepad::hitTest(x, y);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jfloat JNICALL Java_com_flycast_emulator_emu_VGamepad_getControlWidth(JNIEnv * env, jobject obj,
|
||||
jint controlId) {
|
||||
return vgamepad::getControlWidth(static_cast<vgamepad::ControlId>(controlId));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL Java_com_flycast_emulator_emu_VGamepad_layoutHitTest(JNIEnv * env, jobject obj,
|
||||
jfloat x, jfloat y) {
|
||||
return vgamepad::layoutHitTest(x, y);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_VGamepad_scaleElement(JNIEnv * env, jobject obj,
|
||||
jint elemId, jfloat scale) {
|
||||
vgamepad::scaleElement(static_cast<vgamepad::Element>(elemId), scale);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_VGamepad_translateElement(JNIEnv * env, jobject obj,
|
||||
jint elemId, jfloat x, jfloat y) {
|
||||
vgamepad::translateElement(static_cast<vgamepad::Element>(elemId), x, y);
|
||||
}
|
||||
|
||||
namespace vgamepad
|
||||
{
|
||||
|
||||
void startEditing()
|
||||
{
|
||||
// FIXME code dup with vgamepad.cpp
|
||||
enableAllControls();
|
||||
show();
|
||||
jni::env()->CallVoidMethod(g_activity, VJoyStartEditingMID);
|
||||
}
|
||||
void pauseEditing() {
|
||||
// needed? could be used by iOS to avoid relying on gui state
|
||||
jni::env()->CallVoidMethod(g_activity, VJoyStopEditingMID);
|
||||
}
|
||||
void stopEditing(bool canceled)
|
||||
{
|
||||
// FIXME code dup with vgamepad.cpp
|
||||
jni::env()->CallVoidMethod(g_activity, VJoyStopEditingMID);
|
||||
if (canceled)
|
||||
loadLayout();
|
||||
else
|
||||
saveLayout();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// InputDeviceManager
|
||||
//
|
||||
extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_isMicPluggedIn(JNIEnv *env, jobject obj)
|
||||
{
|
||||
for (const auto& devices : config::MapleExpansionDevices)
|
||||
if (static_cast<MapleDeviceType>(devices[0]) == MDT_Microphone
|
||||
|| static_cast<MapleDeviceType>(devices[1]) == MDT_Microphone)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_init(JNIEnv *env, jobject obj)
|
||||
{
|
||||
input_device_manager = env->NewGlobalRef(obj);
|
||||
input_device_manager_rumble = env->GetMethodID(env->GetObjectClass(obj), "rumble", "(IFFI)Z");
|
||||
// FIXME Don't connect it by default or any screen touch will register as button A press
|
||||
mouse = std::make_shared<AndroidMouse>(-1);
|
||||
GamepadDevice::Register(mouse);
|
||||
keyboard = std::make_shared<AndroidKeyboard>();
|
||||
GamepadDevice::Register(keyboard);
|
||||
gui_setOnScreenKeyboardCallback([](bool show) {
|
||||
if (g_activity != nullptr)
|
||||
jni::env()->CallVoidMethod(g_activity, showScreenKeyboardMid, show);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj,
|
||||
jint id, jstring name, jint maple_port, jstring junique_id, jintArray fullAxes, jintArray halfAxes, jboolean hasRumble)
|
||||
{
|
||||
if (id == AndroidVirtualGamepad::GAMEPAD_ID) {
|
||||
virtualGamepad = std::make_shared<AndroidVirtualGamepad>(hasRumble);
|
||||
GamepadDevice::Register(virtualGamepad);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string joyname = jni::String(name, false);
|
||||
std::string unique_id = jni::String(junique_id, false);
|
||||
std::vector<int> full = jni::IntArray(fullAxes, false);
|
||||
std::vector<int> half = jni::IntArray(halfAxes, false);
|
||||
|
||||
std::shared_ptr<AndroidGamepadDevice> gamepad = std::make_shared<AndroidGamepadDevice>(maple_port, id, joyname.c_str(), unique_id.c_str(), full, half);
|
||||
AndroidGamepadDevice::AddAndroidGamepad(gamepad);
|
||||
gamepad->setRumbleEnabled(hasRumble);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj,
|
||||
jint id)
|
||||
{
|
||||
if (id == AndroidVirtualGamepad::GAMEPAD_ID) {
|
||||
GamepadDevice::Unregister(virtualGamepad);
|
||||
virtualGamepad.reset();
|
||||
}
|
||||
else {
|
||||
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
|
||||
if (device)
|
||||
AndroidGamepadDevice::RemoveAndroidGamepad(device);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_virtualReleaseAll(JNIEnv *env, jobject obj) {
|
||||
if (virtualGamepad)
|
||||
virtualGamepad->releaseAll();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_virtualJoystick(JNIEnv *env, jobject obj,
|
||||
jfloat x, jfloat y) {
|
||||
if (virtualGamepad)
|
||||
virtualGamepad->joystickInput(x, y);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_virtualButtonInput(JNIEnv *env, jobject obj,
|
||||
jint controlId, jboolean pressed) {
|
||||
if (virtualGamepad)
|
||||
virtualGamepad->buttonInput(static_cast<vgamepad::ControlId>(controlId), pressed);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickButtonEvent(JNIEnv *env, jobject obj,
|
||||
jint id, jint key, jboolean pressed)
|
||||
{
|
||||
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
|
||||
if (device != NULL)
|
||||
return device->gamepad_btn_input(key, pressed);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardEvent(JNIEnv *env, jobject obj,
|
||||
jint key, jboolean pressed) {
|
||||
keyboard->input(key, pressed);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardText(JNIEnv *env, jobject obj,
|
||||
jint c) {
|
||||
gui_keyboard_input((u16)c);
|
||||
}
|
||||
|
||||
static std::map<std::pair<jint, jint>, jint> previous_axis_values;
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickAxisEvent(JNIEnv *env, jobject obj,
|
||||
jint id, jint key, jint value)
|
||||
{
|
||||
std::shared_ptr<AndroidGamepadDevice> device = AndroidGamepadDevice::GetAndroidGamepad(id);
|
||||
if (device != nullptr)
|
||||
return device->gamepad_axis_input(key, value);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_mouseEvent(JNIEnv *env, jobject obj,
|
||||
jint xpos, jint ypos, jint buttons)
|
||||
{
|
||||
mouse->setAbsPos(xpos, ypos, settings.display.width, settings.display.height);
|
||||
mouse->setButton(Mouse::LEFT_BUTTON, (buttons & 1) != 0);
|
||||
mouse->setButton(Mouse::RIGHT_BUTTON, (buttons & 2) != 0);
|
||||
mouse->setButton(Mouse::MIDDLE_BUTTON, (buttons & 4) != 0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_mouseScrollEvent(JNIEnv *env, jobject obj,
|
||||
jint scrollValue) {
|
||||
mouse->setWheel(scrollValue);
|
||||
}
|
|
@ -107,7 +107,7 @@ public:
|
|||
bool isNull() const { return object == nullptr; }
|
||||
operator jobject() const { return object; }
|
||||
|
||||
Class getClass() const;
|
||||
inline Class getClass() const;
|
||||
|
||||
template<typename T>
|
||||
T globalRef() {
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
|
||||
- (void)hideController
|
||||
{
|
||||
[self resetTouch];
|
||||
[self resetAnalog];
|
||||
[hideTimer invalidate];
|
||||
[self.view removeFromSuperview];
|
||||
}
|
||||
|
@ -89,12 +89,10 @@
|
|||
vgamepad::hide();
|
||||
}
|
||||
|
||||
- (void)resetTouch
|
||||
- (void)resetAnalog
|
||||
{
|
||||
joyTouch = nil;
|
||||
virtualGamepad->gamepad_axis_input(DC_AXIS_LEFT, 0);
|
||||
virtualGamepad->gamepad_axis_input(DC_AXIS_UP, 0);
|
||||
vgamepad::setAnalogStick(0, 0);
|
||||
virtualGamepad->joystickInput(0, 0);
|
||||
}
|
||||
|
||||
static CGPoint translateCoords(const CGPoint& pos, const CGSize& size)
|
||||
|
@ -114,20 +112,20 @@ static CGPoint translateCoords(const CGPoint& pos, const CGSize& size)
|
|||
CGPoint point = [touch locationInView:self.view];
|
||||
point = translateCoords(point, self.view.bounds.size);
|
||||
vgamepad::ControlId control = vgamepad::hitTest(point.x, point.y);
|
||||
if (joyTouch == nil && control == vgamepad::AnalogArea)
|
||||
if (joyTouch == nil && (control == vgamepad::AnalogArea || control == vgamepad::AnalogStick))
|
||||
{
|
||||
[self resetTouch];
|
||||
[self resetAnalog];
|
||||
joyTouch = touch;
|
||||
joyBias = point;
|
||||
continue;
|
||||
}
|
||||
NSValue *key = [NSValue valueWithPointer:(const void *)touch];
|
||||
if (control != vgamepad::None && control != vgamepad::AnalogArea
|
||||
&& touchToButton[key] == nil)
|
||||
&& control != vgamepad::AnalogStick && touchToButton[key] == nil)
|
||||
{
|
||||
touchToButton[key] = [NSNumber numberWithInt:control];
|
||||
// button down
|
||||
virtualGamepad->gamepad_btn_input(vgamepad::controlToDcKey(control), true);
|
||||
virtualGamepad->buttonInput(control, true);
|
||||
}
|
||||
}
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
|
@ -138,7 +136,7 @@ static CGPoint translateCoords(const CGPoint& pos, const CGSize& size)
|
|||
for (UITouch *touch in touches)
|
||||
{
|
||||
if (touch == joyTouch) {
|
||||
[self resetTouch];
|
||||
[self resetAnalog];
|
||||
continue;
|
||||
}
|
||||
NSValue *key = [NSValue valueWithPointer:(const void *)touch];
|
||||
|
@ -146,7 +144,7 @@ static CGPoint translateCoords(const CGPoint& pos, const CGSize& size)
|
|||
if (control != nil) {
|
||||
[touchToButton removeObjectForKey:key];
|
||||
// button up
|
||||
virtualGamepad->gamepad_btn_input(vgamepad::controlToDcKey((vgamepad::ControlId)control.intValue), false);
|
||||
virtualGamepad->buttonInput(static_cast<vgamepad::ControlId>(control.intValue), false);
|
||||
}
|
||||
}
|
||||
[super touchesEnded:touches withEvent:event];
|
||||
|
@ -166,17 +164,7 @@ static CGPoint translateCoords(const CGPoint& pos, const CGSize& size)
|
|||
double sz = vgamepad::getControlWidth(vgamepad::AnalogStick);
|
||||
point.x = std::max<CGFloat>(std::min<CGFloat>(1.0, point.x / sz), -1.0);
|
||||
point.y = std::max<CGFloat>(std::min<CGFloat>(1.0, point.y / sz), -1.0);
|
||||
vgamepad::setAnalogStick(point.x, point.y);
|
||||
point.x *= 32767.0;
|
||||
point.y *= 32767.0;
|
||||
if (point.x >= 0)
|
||||
virtualGamepad->gamepad_axis_input(DC_AXIS_RIGHT, (int)std::round(point.x));
|
||||
else
|
||||
virtualGamepad->gamepad_axis_input(DC_AXIS_LEFT, -(int)std::round(point.x));
|
||||
if (point.y >= 0)
|
||||
virtualGamepad->gamepad_axis_input(DC_AXIS_DOWN, (int)std::round(point.y));
|
||||
else
|
||||
virtualGamepad->gamepad_axis_input(DC_AXIS_UP, -(int)std::round(point.y));
|
||||
virtualGamepad->joystickInput(point.x, point.y);
|
||||
continue;
|
||||
}
|
||||
vgamepad::ControlId control = vgamepad::hitTest(point.x, point.y);
|
||||
|
@ -186,10 +174,10 @@ static CGPoint translateCoords(const CGPoint& pos, const CGSize& size)
|
|||
continue;
|
||||
if (prevControl != nil && prevControl.intValue != vgamepad::None && prevControl.intValue != vgamepad::AnalogArea) {
|
||||
// button up
|
||||
virtualGamepad->gamepad_btn_input(vgamepad::controlToDcKey((vgamepad::ControlId)prevControl.intValue), false);
|
||||
virtualGamepad->buttonInput(static_cast<vgamepad::ControlId>(prevControl.intValue), false);
|
||||
}
|
||||
// button down
|
||||
virtualGamepad->gamepad_btn_input(vgamepad::controlToDcKey(control), true);
|
||||
virtualGamepad->buttonInput(control, true);
|
||||
touchToButton[key] = [NSNumber numberWithInt:control];
|
||||
}
|
||||
[super touchesMoved:touches withEvent:event];
|
||||
|
@ -199,7 +187,7 @@ static CGPoint translateCoords(const CGPoint& pos, const CGSize& size)
|
|||
{
|
||||
for (UITouch *touch in touches) {
|
||||
if (touch == joyTouch) {
|
||||
[self resetTouch];
|
||||
[self resetAnalog];
|
||||
continue;
|
||||
}
|
||||
NSValue *key = [NSValue valueWithPointer:(const void *)touch];
|
||||
|
@ -207,7 +195,7 @@ static CGPoint translateCoords(const CGPoint& pos, const CGSize& size)
|
|||
if (control != nil) {
|
||||
[touchToButton removeObjectForKey:key];
|
||||
// button up
|
||||
virtualGamepad->gamepad_btn_input(vgamepad::controlToDcKey((vgamepad::ControlId)control.intValue), false);
|
||||
virtualGamepad->buttonInput(static_cast<vgamepad::ControlId>(control.intValue), false);
|
||||
}
|
||||
}
|
||||
[super touchesCancelled:touches withEvent:event];
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <cmath>
|
||||
#include "input/gamepad_device.h"
|
||||
#include "input/mouse.h"
|
||||
#include "input/virtual_gamepad.h"
|
||||
#include "ui/gui.h"
|
||||
|
||||
enum IOSButton {
|
||||
|
@ -480,110 +481,30 @@ private:
|
|||
static std::map<GCController *, std::shared_ptr<IOSGamepad>> controllers;
|
||||
};
|
||||
|
||||
class IOSVirtualGamepad : public GamepadDevice
|
||||
class IOSVirtualGamepad : public VirtualGamepad
|
||||
{
|
||||
public:
|
||||
IOSVirtualGamepad() : GamepadDevice(0, "iOS", false) {
|
||||
_name = "Virtual Gamepad";
|
||||
_unique_id = "ios-virtual-gamepad";
|
||||
input_mapper = std::make_shared<IdentityInputMapping>();
|
||||
//hasAnalogStick = true; // TODO has an analog stick but input mapping isn't persisted
|
||||
IOSVirtualGamepad() : VirtualGamepad("iOS") {
|
||||
}
|
||||
|
||||
bool is_virtual_gamepad() override { return true; }
|
||||
|
||||
std::shared_ptr<InputMapping> getDefaultMapping() override {
|
||||
return std::make_shared<DefaultIOSMapping<>>();
|
||||
}
|
||||
|
||||
bool gamepad_btn_input(u32 code, bool pressed) override
|
||||
bool handleButtonInput(u32& state, u32 key, bool pressed) override
|
||||
{
|
||||
if (pressed)
|
||||
buttonState |= code;
|
||||
else
|
||||
buttonState &= ~code;
|
||||
switch (code)
|
||||
if (!pressed
|
||||
|| (key != DC_DPAD_UP && key != DC_DPAD_DOWN && key != DC_DPAD_LEFT && key != DC_DPAD_RIGHT))
|
||||
return false;
|
||||
if (((state | key) & (DC_DPAD_UP | DC_DPAD_DOWN)) == (DC_DPAD_UP | DC_DPAD_DOWN)
|
||||
|| ((state | key) & (DC_DPAD_LEFT | DC_DPAD_RIGHT)) == (DC_DPAD_LEFT | DC_DPAD_RIGHT))
|
||||
{
|
||||
case DC_AXIS_LT:
|
||||
gamepad_axis_input(DC_AXIS_LT, pressed ? 0x7fff : 0);
|
||||
if (settings.platform.isArcade())
|
||||
GamepadDevice::gamepad_btn_input(DC_BTN_Z, pressed); // btn5
|
||||
return true;
|
||||
case DC_AXIS_RT:
|
||||
if (!pressed && maple_port() >= 0 && maple_port() <= 3)
|
||||
kcode[maple_port()] |= DC_DPAD2_UP | DC_BTN_D | DC_DPAD2_DOWN;
|
||||
gamepad_axis_input(DC_AXIS_RT, pressed ? 0x7fff : 0);
|
||||
if (settings.platform.isArcade())
|
||||
GamepadDevice::gamepad_btn_input(DC_BTN_Y, pressed); // btn4
|
||||
return true;
|
||||
default:
|
||||
if ((buttonState & (DC_DPAD_UP | DC_DPAD_DOWN)) == (DC_DPAD_UP | DC_DPAD_DOWN)
|
||||
|| (buttonState & (DC_DPAD_LEFT | DC_DPAD_RIGHT)) == (DC_DPAD_LEFT | DC_DPAD_RIGHT))
|
||||
{
|
||||
GamepadDevice::gamepad_btn_input(DC_DPAD_UP, false);
|
||||
GamepadDevice::gamepad_btn_input(DC_DPAD_DOWN, false);
|
||||
GamepadDevice::gamepad_btn_input(DC_DPAD_LEFT, false);
|
||||
GamepadDevice::gamepad_btn_input(DC_DPAD_RIGHT, false);
|
||||
buttonState = 0;
|
||||
gui_open_settings();
|
||||
return true;
|
||||
}
|
||||
if (settings.platform.isArcade() && maple_port() >= 0 && maple_port() <= 3)
|
||||
{
|
||||
u32& keycode = kcode[maple_port()];
|
||||
if ((buttonState & DC_AXIS_RT) != 0)
|
||||
{
|
||||
switch (code) {
|
||||
case DC_BTN_A:
|
||||
// RT + A -> D (coin)
|
||||
keycode = pressed ? keycode & ~DC_BTN_D : keycode | DC_BTN_D;
|
||||
break;
|
||||
case DC_BTN_B:
|
||||
// RT + B -> Service
|
||||
keycode = pressed ? keycode & ~DC_DPAD2_UP : keycode | DC_DPAD2_UP;
|
||||
break;
|
||||
case DC_BTN_X:
|
||||
// RT + X -> Test
|
||||
keycode = pressed ? keycode & ~DC_DPAD2_DOWN : keycode | DC_DPAD2_DOWN;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// arcade mapping: X -> btn2, Y -> btn3
|
||||
if (code == DC_BTN_X)
|
||||
code = DC_BTN_C; // btn2
|
||||
if (code == DC_BTN_Y)
|
||||
code = DC_BTN_X; // btn3
|
||||
}
|
||||
switch (code)
|
||||
{
|
||||
case DC_DPAD_UP | DC_DPAD_RIGHT:
|
||||
GamepadDevice::gamepad_btn_input(DC_DPAD_UP, pressed);
|
||||
code = DC_DPAD_RIGHT;
|
||||
break;
|
||||
case DC_DPAD_DOWN | DC_DPAD_RIGHT:
|
||||
GamepadDevice::gamepad_btn_input(DC_DPAD_DOWN, pressed);
|
||||
code = DC_DPAD_RIGHT;
|
||||
break;
|
||||
case DC_DPAD_DOWN | DC_DPAD_LEFT:
|
||||
GamepadDevice::gamepad_btn_input(DC_DPAD_DOWN, pressed);
|
||||
code = DC_DPAD_LEFT;
|
||||
break;
|
||||
case DC_DPAD_UP | DC_DPAD_LEFT:
|
||||
GamepadDevice::gamepad_btn_input(DC_DPAD_UP, pressed);
|
||||
code = DC_DPAD_LEFT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GamepadDevice::gamepad_btn_input(code, pressed);
|
||||
gamepad_btn_input(DC_DPAD_UP, false);
|
||||
gamepad_btn_input(DC_DPAD_DOWN, false);
|
||||
gamepad_btn_input(DC_DPAD_LEFT, false);
|
||||
gamepad_btn_input(DC_DPAD_RIGHT, false);
|
||||
state = 0;
|
||||
gui_open_settings();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
u32 buttonState = 0;
|
||||
};
|
||||
|
||||
class IOSTouchMouse : public SystemMouse
|
||||
|
|
Loading…
Reference in New Issue