ggpo: keyboard/mouse games support. Fix chat when keyboard disabled

ggpo: support for arcade games using keyboard (totd, luptype) and
mouse/rotary encoders (waiwai drive)
Chat and UI now usable when the keyboard is not set to a maple port.
This commit is contained in:
Flyinghead 2021-10-19 16:56:46 +02:00
parent db93a4c4a7
commit ab45b5ec8a
23 changed files with 515 additions and 353 deletions

View File

@ -738,7 +738,9 @@ if(NOT LIBRETRO)
core/input/gamepad_device.h
core/input/keyboard_device.h
core/input/mapping.cpp
core/input/mapping.h)
core/input/mapping.h
core/input/mouse.cpp
core/input/mouse.h)
endif()
if(WIN32)

View File

@ -120,6 +120,7 @@ Option<bool> GGPOEnable("GGPO", false, "network");
Option<int> GGPODelay("GGPODelay", 0, "network");
Option<bool> NetworkStats("Stats", true, "network");
Option<int> GGPOAnalogAxes("GGPOAnalogAxes", 0, "network");
Option<bool> GGPOChat("GGPOChat", true, "network");
#ifdef SUPPORT_DISPMANX
Option<bool> DispmanxMaintainAspect("maintain_aspect", true, "dispmanx");

View File

@ -492,6 +492,7 @@ extern Option<bool> GGPOEnable;
extern Option<int> GGPODelay;
extern Option<bool> NetworkStats;
extern Option<int> GGPOAnalogAxes;
extern Option<bool> GGPOChat;
#ifdef SUPPORT_DISPMANX
extern Option<bool> DispmanxMaintainAspect;

View File

@ -2,12 +2,13 @@
#include "maple_helper.h"
#include "maple_if.h"
#include "hw/naomi/naomi_cart.h"
#include "input/gamepad_device.h"
#include "cfg/option.h"
#include "stdclass.h"
MapleInputState mapleInputState[4];
void (*MapleConfigMap::UpdateVibration)(u32 port, float power, float inclination, u32 duration_ms);
static u8 GetBtFromSgn(s8 val)
{
return val+128;
@ -156,20 +157,24 @@ void MapleConfigMap::SetImage(u8 *img)
void MapleConfigMap::GetAbsCoordinates(int& x, int& y)
{
const MapleInputState& inputState = mapleInputState[playerNum()];
x = inputState.absPointerX;
y = inputState.absPointerY;
x = inputState.absPos.x;
y = inputState.absPos.y;
}
void MapleConfigMap::GetMouseInput(u8& buttons, int& x, int& y, int& wheel)
{
u32 playerNum = this->playerNum();
buttons = mo_buttons[playerNum] & 0xff;
x = (int)std::round(mo_x_delta[playerNum]);
y = (int)std::round(mo_y_delta[playerNum]);
wheel = (int)std::round(mo_wheel_delta[playerNum]);
mo_x_delta[playerNum] = 0;
mo_y_delta[playerNum] = 0;
mo_wheel_delta[playerNum] = 0;
const MapleInputState& inputState = mapleInputState[playerNum()];
buttons = inputState.mouseButtons;
x = inputState.relPos.x;
y = inputState.relPos.y * (invertMouseY ? -1 : 1);
wheel = inputState.relPos.wheel;
}
void MapleConfigMap::GetKeyboardInput(u8& shift, u8 keys[6])
{
const MapleInputState& inputState = mapleInputState[playerNum()];
shift = inputState.keyboard.shift;
memcpy(keys, inputState.keyboard.key, sizeof(inputState.keyboard.key));
}
bool maple_atomiswave_coin_chute(int slot)
@ -201,7 +206,10 @@ static void createNaomiDevices()
mcfg_DestroyDevices();
mcfg_Create(MDT_NaomiJamma, 0, 5);
if (settings.input.JammaSetup == JVS::Keyboard)
{
mcfg_Create(MDT_Keyboard, 1, 5, 0);
mcfg_Create(MDT_Keyboard, 2, 5, 1);
}
else
{
// Connect VMU B1
@ -247,6 +255,11 @@ static void createAtomiswaveDevices()
// Waiwai drive needs two track-balls
mcfg_Create(MDT_Mouse, 2, 5, 0);
mcfg_Create(MDT_Mouse, 3, 5, 1);
if (settings.content.gameId == "DRIVE")
{
MapleDevices[2][5]->config->invertMouseY = true;
MapleDevices[3][5]->config->invertMouseY = true;
}
}
}

View File

@ -62,8 +62,12 @@ public:
void GetInput(PlainJoystickState* pjs);
void GetAbsCoordinates(int& x, int& y);
void GetMouseInput(u8& buttons, int& x, int& y, int& wheel);
void GetKeyboardInput(u8& shift, u8 keys[6]);
void SetImage(u8 *img);
static void (*UpdateVibration)(u32 port, float power, float inclination, u32 duration_ms);
bool invertMouseY = false;
private:
u32 playerNum();
@ -72,13 +76,27 @@ private:
struct MapleInputState
{
MapleInputState() : halfAxes{}, fullAxes{} {}
MapleInputState() : halfAxes{}, fullAxes{} {
memset(keyboard.key, 0, sizeof(keyboard.key));
}
u32 kcode = ~0;
u8 halfAxes[PJTI_Count]; // LT, RT
int8_t fullAxes[PJAI_Count]; // Left X, Y, Right X, Y
int absPointerX = -1;
int absPointerY = -1;
u8 mouseButtons = ~0;
struct {
int x = -1;
int y = -1;
} absPos;
struct {
int16_t x = 0;
int16_t y = 0;
int16_t wheel = 0;
} relPos;
struct {
u8 shift = 0; // modifier keys bitmask
u8 key[6]; // normal keys pressed
} keyboard;
};
extern MapleInputState mapleInputState[4];

View File

@ -1042,10 +1042,6 @@ struct maple_sega_purupuru : maple_base
}
};
u8 kb_shift[MAPLE_PORTS]; // shift keys pressed (bitmask)
u8 kb_led[MAPLE_PORTS]; // leds currently lit
u8 kb_key[MAPLE_PORTS][6]; // normal keys pressed
struct maple_keyboard : maple_base
{
MapleDeviceType get_device_type() override
@ -1102,16 +1098,21 @@ struct maple_keyboard : maple_base
return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll;
case MDCF_GetCondition:
w32(MFID_6_Keyboard);
//struct data
//int8 shift ; shift keys pressed (bitmask) //1
w8(kb_shift[player_num]);
//int8 led ; leds currently lit //1
w8(kb_led[player_num]);
//int8 key[6] ; normal keys pressed //6
for (int i = 0; i < 6; i++)
w8(kb_key[player_num][i]);
{
u8 shift;
u8 keys[6];
config->GetKeyboardInput(shift, keys);
w32(MFID_6_Keyboard);
//struct data
//int8 shift ; shift keys pressed (bitmask) //1
w8(shift);
//int8 led ; leds currently lit //1
w8(0);
//int8 key[6] ; normal keys pressed //6
for (int i = 0; i < 6; i++)
w8(keys[i]);
}
return MDRS_DataTransfer;
case MDC_DeviceReset:
@ -1127,28 +1128,6 @@ struct maple_keyboard : maple_base
}
};
// Mouse buttons
// bit 0: Button C
// bit 1: Right button (B)
// bit 2: Left button (A)
// bit 3: Wheel button
u8 mo_buttons[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
// Relative mouse coordinates [-512:511]
f32 mo_x_delta[4];
f32 mo_y_delta[4];
f32 mo_wheel_delta[4];
// Absolute mouse coordinates
// Range [0:639] [0:479]
// but may be outside this range if the pointer is offscreen or outside the 4:3 window.
s32 mo_x_abs[4];
s32 mo_y_abs[4];
// previous mouse coordinates for relative motion
s32 mo_x_prev[4] = { -1, -1, -1, -1 };
s32 mo_y_prev[4] = { -1, -1, -1, -1 };
// last known screen/window size
static s32 mo_width;
static s32 mo_height;
struct maple_mouse : maple_base
{
MapleDeviceType get_device_type() override
@ -1411,78 +1390,3 @@ maple_device* maple_Create(MapleDeviceType type)
return rv;
}
static void screenToNative(int& x, int& y, int width, int height)
{
float fx, fy;
if (!config::Rotate90)
{
float scale = 480.f / height;
fy = y * scale;
scale /= config::ScreenStretching / 100.f;
fx = (x - (width - 640.f / scale) / 2.f) * scale;
}
else
{
float scale = 640.f / width;
fx = x * scale;
scale /= config::ScreenStretching / 100.f;
fy = (y - (height - 480.f / scale) / 2.f) * scale;
}
x = (int)std::round(fx);
y = (int)std::round(fy);
}
void SetMousePosition(int x, int y, int width, int height, u32 mouseId)
{
if (mouseId >= MAPLE_PORTS)
return;
mo_width = width;
mo_height = height;
if (config::Rotate90)
{
int t = y;
y = x;
x = height - 1 - t;
std::swap(width, height);
}
screenToNative(x, y, width, height);
mo_x_abs[mouseId] = x;
mo_y_abs[mouseId] = y;
if (mo_x_prev[mouseId] != -1)
{
mo_x_delta[mouseId] += (f32)(x - mo_x_prev[mouseId]) * config::MouseSensitivity / 100.f;
mo_y_delta[mouseId] += (f32)(y - mo_y_prev[mouseId]) * config::MouseSensitivity / 100.f;
}
mo_x_prev[mouseId] = x;
mo_y_prev[mouseId] = y;
}
void SetRelativeMousePosition(float xrel, float yrel, u32 mouseId)
{
if (mouseId >= MAPLE_PORTS)
return;
int width = mo_width;
int height = mo_height;
if (config::Rotate90)
{
std::swap(xrel, yrel);
xrel = -xrel;
std::swap(width, height);
}
float dx = xrel * config::MouseSensitivity / 100.f;
float dy = yrel * config::MouseSensitivity / 100.f;
mo_x_delta[mouseId] += dx;
mo_y_delta[mouseId] += dy;
int minX = -width / 32;
int minY = -height / 32;
int maxX = width + width / 32;
int maxY = height + height / 32;
screenToNative(minX, minY, width, height);
screenToNative(maxX, maxY, width, height);
mo_x_abs[mouseId] += (int)std::round(dx);
mo_y_abs[mouseId] += (int)std::round(dy);
mo_x_abs[mouseId] = std::min(std::max(mo_x_abs[mouseId], minX), maxX);
mo_y_abs[mouseId] = std::min(std::max(mo_y_abs[mouseId], minY), maxY);
}

View File

@ -166,20 +166,6 @@ void limit_joystick_magnitude(s8& joyx, s8& joyy)
extern u8 EEPROM[0x100];
void load_naomi_eeprom();
// Mouse position and buttons
extern u8 mo_buttons[4];
extern s32 mo_x_abs[4];
extern s32 mo_y_abs[4];
extern f32 mo_x_delta[4];
extern f32 mo_y_delta[4];
extern f32 mo_wheel_delta[4];
extern s32 mo_x_prev[4];
extern s32 mo_y_prev[4];
void SetMousePosition(int x, int y, int width, int height, u32 mouseId = 0);
void SetRelativeMousePosition(float xrel, float yrel, u32 mouseId = 0);
#define SWAP32(a) ((((a) & 0xff) << 24) | (((a) & 0xff00) << 8) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff))
const char *GetCurrentGameButtonName(DreamcastKey key);

View File

@ -20,7 +20,6 @@
#include <memory>
#include "maple_devs.h"
#include "hw/naomi/naomi_cart.h"
#include "input/gamepad_device.h"
#include <xxhash.h>
#include "oslib/oslib.h"
#include "stdclass.h"
@ -482,7 +481,7 @@ protected:
if (init_in_progress)
return 0;
const MapleInputState& inputState = mapleInputState[std::min(player_num, (int)ARRAY_SIZE(mapleInputState) - 1)];
if (inputState.absPointerX < 0 || inputState.absPointerX > 639 || inputState.absPointerY < 0 || inputState.absPointerY > 479)
if (inputState.absPos.x < 0 || inputState.absPos.x > 639 || inputState.absPos.y < 0 || inputState.absPos.y > 479)
return 0;
else
return 0x8000;
@ -1494,8 +1493,8 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
const MapleInputState& inputState = mapleInputState[std::min(playerNum, (int)ARRAY_SIZE(mapleInputState) - 1)];
u16 x;
u16 y;
if (inputState.absPointerX < 0 || inputState.absPointerX > 639
|| inputState.absPointerY < 0 || inputState.absPointerY > 479
if (inputState.absPos.x < 0 || inputState.absPos.x > 639
|| inputState.absPos.y < 0 || inputState.absPos.y > 479
|| (buttons[playerNum] & NAOMI_RELOAD_KEY) != 0)
{
x = 0;
@ -1503,8 +1502,8 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
}
else
{
x = inputState.absPointerX * 0xFFFF / 639;
y = inputState.absPointerY * 0xFFFF / 479;
x = inputState.absPos.x * 0xFFFF / 639;
y = inputState.absPos.y * 0xFFFF / 479;
}
LOGJVS("x,y:%4x,%4x ", x, y);
JVS_OUT(x >> 8); // X, MSB
@ -1571,10 +1570,8 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
static s16 roty = 0;
// TODO Add more players.
// I can't think of any naomi multiplayer game that uses rotary encoders
rotx += mo_x_delta[first_player] * 5;
roty -= mo_y_delta[first_player] * 5;
mo_x_delta[first_player] = 0;
mo_y_delta[first_player] = 0;
rotx += mapleInputState[first_player].relPos.x * 5;
roty -= mapleInputState[first_player].relPos.y * 5;
LOGJVS("rotenc ");
for (int chan = 0; chan < buffer_in[cmdi + 1]; chan++)
{
@ -1622,8 +1619,8 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
// Ninja Assault:
u32 xr = 0x19d - 0x37;
u32 yr = 0x1fe - 0x40;
x = mapleInputState[playerNum].absPointerX * xr / 639 + 0x37;
y = mapleInputState[playerNum].absPointerY * yr / 479 + 0x40;
x = mapleInputState[playerNum].absPos.x * xr / 639 + 0x37;
y = mapleInputState[playerNum].absPos.y * yr / 479 + 0x40;
}
LOGJVS("lightgun %4x,%4x ", x, y);
JVS_OUT(x >> 8); // X, MSB

View File

@ -39,6 +39,9 @@ s8 joyrx[4];
s8 joyry[4];
u8 rt[4];
u8 lt[4];
// Keyboards
u8 kb_shift[MAPLE_PORTS]; // shift keys pressed (bitmask)
u8 kb_key[MAPLE_PORTS][6]; // normal keys pressed
std::vector<std::shared_ptr<GamepadDevice>> GamepadDevice::_gamepads;
std::mutex GamepadDevice::_gamepads_mutex;
@ -426,7 +429,7 @@ void GamepadDevice::save_mapping(int system)
InputMapping::SaveMapping(filename.c_str(), input_mapper);
}
void UpdateVibration(u32 port, float power, float inclination, u32 duration_ms)
static void updateVibration(u32 port, float power, float inclination, u32 duration_ms)
{
int i = GamepadDevice::GetGamepadCount() - 1;
for ( ; i >= 0; i--)
@ -493,6 +496,7 @@ void GamepadDevice::Register(const std::shared_ptr<GamepadDevice>& gamepad)
_gamepads_mutex.lock();
_gamepads.push_back(gamepad);
_gamepads_mutex.unlock();
MapleConfigMap::UpdateVibration = updateVibration;
}
void GamepadDevice::Unregister(const std::shared_ptr<GamepadDevice>& gamepad)
@ -517,54 +521,6 @@ void GamepadDevice::SaveMaplePorts()
}
}
void Mouse::setAbsPos(int x, int y, int width, int height) {
SetMousePosition(x, y, width, height, maple_port());
}
void Mouse::setRelPos(float deltax, float deltay) {
SetRelativeMousePosition(deltax, deltay, maple_port());
}
void Mouse::setWheel(int delta) {
if (maple_port() >= 0 && maple_port() < (int)ARRAY_SIZE(mo_wheel_delta))
mo_wheel_delta[maple_port()] += delta;
}
void Mouse::setButton(Button button, bool pressed)
{
if (maple_port() >= 0 && maple_port() < (int)ARRAY_SIZE(mo_buttons))
{
if (pressed)
mo_buttons[maple_port()] &= ~(1 << (int)button);
else
mo_buttons[maple_port()] |= 1 << (int)button;
}
if ((gui_is_open() || gui_mouse_captured()) && !is_detecting_input())
// Don't register mouse clicks as gamepad presses when gui is open
// This makes the gamepad presses to be handled first and the mouse position to be ignored
return;
gamepad_btn_input(button, pressed);
}
void SystemMouse::setAbsPos(int x, int y, int width, int height) {
gui_set_mouse_position(x, y);
Mouse::setAbsPos(x, y, width, height);
}
void SystemMouse::setButton(Button button, bool pressed) {
int uiBtn = (int)button - 1;
if (uiBtn < 2)
uiBtn ^= 1;
gui_set_mouse_button(uiBtn, pressed);
Mouse::setButton(button, pressed);
}
void SystemMouse::setWheel(int delta) {
gui_set_mouse_wheel(delta * 35);
Mouse::setWheel(delta);
}
#ifdef TEST_AUTOMATION
#include "cfg/option.h"
static bool replay_inited;

View File

@ -156,75 +156,3 @@ extern u32 kcode[4];
extern u8 rt[4], lt[4];
extern s8 joyx[4], joyy[4];
extern s8 joyrx[4], joyry[4];
void UpdateVibration(u32 port, float power, float inclination, u32 duration_ms);
class MouseInputMapping : public InputMapping
{
public:
MouseInputMapping()
{
name = "Mouse";
set_button(DC_BTN_A, 2); // Left
set_button(DC_BTN_B, 1); // Right
set_button(DC_BTN_START, 3); // Middle
dirty = false;
}
};
class Mouse : public GamepadDevice
{
protected:
Mouse(const char *apiName, int maplePort = 0) : GamepadDevice(maplePort, apiName) {
this->_name = "Mouse";
}
virtual std::shared_ptr<InputMapping> getDefaultMapping() override {
return std::make_shared<MouseInputMapping>();
}
public:
enum Button {
LEFT_BUTTON = 2,
RIGHT_BUTTON = 1,
MIDDLE_BUTTON = 3,
BUTTON_4 = 4,
BUTTON_5 = 5
};
virtual const char *get_button_name(u32 code) override
{
switch((Button)code)
{
case LEFT_BUTTON:
return "Left Button";
case RIGHT_BUTTON:
return "Right Button";
case MIDDLE_BUTTON:
return "Middle Button";
case BUTTON_4:
return "Button 4";
case BUTTON_5:
return "Button 5";
default:
return nullptr;
}
}
void setAbsPos(int x, int y, int width, int height);
void setRelPos(float deltax, float deltay);
void setButton(Button button, bool pressed);
void setWheel(int delta);
};
class SystemMouse : public Mouse
{
protected:
SystemMouse(const char *apiName, int maplePort = 0) : Mouse(apiName, maplePort) {}
public:
void setAbsPos(int x, int y, int width, int height);
void setButton(Button button, bool pressed);
void setWheel(int delta);
};

View File

@ -404,41 +404,40 @@ static inline void setFlag(int& v, u32 bitmask, bool set)
template <typename Keycode>
void KeyboardDeviceTemplate<Keycode>::keyboard_input(Keycode keycode, bool pressed, int modifier_keys)
{
const int port = maple_port();
if (port < 0 || port > (int)ARRAY_SIZE(kb_key))
return;
u8 dc_keycode = convert_keycode(keycode);
if (port < (int)ARRAY_SIZE(kb_key))
// Some OSes (Mac OS) don't distinguish left and right modifier keys to we set them both.
// But not for Alt since Right Alt is used as a special modifier keys on some international
// keyboards.
switch (dc_keycode)
{
// Some OSes (Mac OS) don't distinguish left and right modifier keys to we set them both.
// But not for Alt since Right Alt is used as a special modifier keys on some international
// keyboards.
switch (dc_keycode)
{
case 0xE1: // Left Shift
case 0xE5: // Right Shift
setFlag(_modifier_keys, DC_KBMOD_LEFTSHIFT | DC_KBMOD_RIGHTSHIFT, pressed);
break;
case 0xE0: // Left Ctrl
case 0xE4: // Right Ctrl
setFlag(_modifier_keys, DC_KBMOD_LEFTCTRL | DC_KBMOD_RIGHTCTRL, pressed);
break;
case 0xE2: // Left Alt
setFlag(_modifier_keys, DC_KBMOD_LEFTALT, pressed);
break;
case 0xE6: // Right Alt
setFlag(_modifier_keys, DC_KBMOD_RIGHTALT, pressed);
break;
case 0xE7: // S2 special key
setFlag(_modifier_keys, DC_KBMOD_S2, pressed);
break;
default:
break;
}
case 0xE1: // Left Shift
case 0xE5: // Right Shift
setFlag(_modifier_keys, DC_KBMOD_LEFTSHIFT | DC_KBMOD_RIGHTSHIFT, pressed);
break;
case 0xE0: // Left Ctrl
case 0xE4: // Right Ctrl
setFlag(_modifier_keys, DC_KBMOD_LEFTCTRL | DC_KBMOD_RIGHTCTRL, pressed);
break;
case 0xE2: // Left Alt
setFlag(_modifier_keys, DC_KBMOD_LEFTALT, pressed);
break;
case 0xE6: // Right Alt
setFlag(_modifier_keys, DC_KBMOD_RIGHTALT, pressed);
break;
case 0xE7: // S2 special key
setFlag(_modifier_keys, DC_KBMOD_S2, pressed);
break;
default:
break;
}
const int port = maple_port();
if (port >= 0 && port < (int)ARRAY_SIZE(kb_shift))
kb_shift[port] = _modifier_keys;
if (dc_keycode != 0 && dc_keycode < 0xE0)
if (dc_keycode != 0 && dc_keycode < 0xE0)
{
gui_keyboard_key(dc_keycode, pressed, _modifier_keys);
if (port >= 0 && port < (int)ARRAY_SIZE(kb_key))
{
if (pressed)
{
@ -474,7 +473,6 @@ void KeyboardDeviceTemplate<Keycode>::keyboard_input(Keycode keycode, bool press
if (gui_keyboard_captured())
{
// chat: disable the keyboard controller. Only accept emu keys (menu, escape...)
const int port = maple_port();
set_maple_port(-1);
gamepad_btn_input(dc_keycode, pressed);
set_maple_port(port);

168
core/input/mouse.cpp Normal file
View File

@ -0,0 +1,168 @@
/*
Copyright 2021 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 "mouse.h"
#include "rend/gui.h"
// Mouse buttons
// bit 0: Button C
// bit 1: Right button (B)
// bit 2: Left button (A)
// bit 3: Wheel button
u8 mo_buttons[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
// Relative mouse coordinates [-512:511]
f32 mo_x_delta[4];
f32 mo_y_delta[4];
f32 mo_wheel_delta[4];
// Absolute mouse coordinates
// Range [0:639] [0:479]
// but may be outside this range if the pointer is offscreen or outside the 4:3 window.
s32 mo_x_abs[4];
s32 mo_y_abs[4];
// previous mouse coordinates for relative motion
s32 mo_x_prev[4] = { -1, -1, -1, -1 };
s32 mo_y_prev[4] = { -1, -1, -1, -1 };
// last known screen/window size
static s32 mo_width;
static s32 mo_height;
void Mouse::setAbsPos(int x, int y, int width, int height) {
SetMousePosition(x, y, width, height, maple_port());
}
void Mouse::setRelPos(float deltax, float deltay) {
SetRelativeMousePosition(deltax, deltay, maple_port());
}
void Mouse::setWheel(int delta) {
if (maple_port() >= 0 && maple_port() < (int)ARRAY_SIZE(mo_wheel_delta))
mo_wheel_delta[maple_port()] += delta;
}
void Mouse::setButton(Button button, bool pressed)
{
if (maple_port() >= 0 && maple_port() < (int)ARRAY_SIZE(mo_buttons))
{
if (pressed)
mo_buttons[maple_port()] &= ~(1 << (int)button);
else
mo_buttons[maple_port()] |= 1 << (int)button;
}
if ((gui_is_open() || gui_mouse_captured()) && !is_detecting_input())
// Don't register mouse clicks as gamepad presses when gui is open
// This makes the gamepad presses to be handled first and the mouse position to be ignored
return;
gamepad_btn_input(button, pressed);
}
void SystemMouse::setAbsPos(int x, int y, int width, int height) {
gui_set_mouse_position(x, y);
Mouse::setAbsPos(x, y, width, height);
}
void SystemMouse::setButton(Button button, bool pressed) {
int uiBtn = (int)button - 1;
if (uiBtn < 2)
uiBtn ^= 1;
gui_set_mouse_button(uiBtn, pressed);
Mouse::setButton(button, pressed);
}
void SystemMouse::setWheel(int delta) {
gui_set_mouse_wheel(delta * 35);
Mouse::setWheel(delta);
}
static void screenToNative(int& x, int& y, int width, int height)
{
float fx, fy;
if (!config::Rotate90)
{
float scale = 480.f / height;
fy = y * scale;
scale /= config::ScreenStretching / 100.f;
fx = (x - (width - 640.f / scale) / 2.f) * scale;
}
else
{
float scale = 640.f / width;
fx = x * scale;
scale /= config::ScreenStretching / 100.f;
fy = (y - (height - 480.f / scale) / 2.f) * scale;
}
x = (int)std::round(fx);
y = (int)std::round(fy);
}
void SetMousePosition(int x, int y, int width, int height, u32 mouseId)
{
if (mouseId >= ARRAY_SIZE(mo_x_abs))
return;
mo_width = width;
mo_height = height;
if (config::Rotate90)
{
int t = y;
y = x;
x = height - 1 - t;
std::swap(width, height);
}
screenToNative(x, y, width, height);
mo_x_abs[mouseId] = x;
mo_y_abs[mouseId] = y;
if (mo_x_prev[mouseId] != -1)
{
mo_x_delta[mouseId] += (f32)(x - mo_x_prev[mouseId]) * config::MouseSensitivity / 100.f;
mo_y_delta[mouseId] += (f32)(y - mo_y_prev[mouseId]) * config::MouseSensitivity / 100.f;
}
mo_x_prev[mouseId] = x;
mo_y_prev[mouseId] = y;
}
void SetRelativeMousePosition(float xrel, float yrel, u32 mouseId)
{
if (mouseId >= ARRAY_SIZE(mo_x_delta))
return;
int width = mo_width;
int height = mo_height;
if (config::Rotate90)
{
std::swap(xrel, yrel);
xrel = -xrel;
std::swap(width, height);
}
float dx = xrel * config::MouseSensitivity / 100.f;
float dy = yrel * config::MouseSensitivity / 100.f;
mo_x_delta[mouseId] += dx;
mo_y_delta[mouseId] += dy;
int minX = -width / 32;
int minY = -height / 32;
int maxX = width + width / 32;
int maxY = height + height / 32;
screenToNative(minX, minY, width, height);
screenToNative(maxX, maxY, width, height);
mo_x_abs[mouseId] += (int)std::round(dx);
mo_y_abs[mouseId] += (int)std::round(dy);
mo_x_abs[mouseId] = std::min(std::max(mo_x_abs[mouseId], minX), maxX);
mo_y_abs[mouseId] = std::min(std::max(mo_y_abs[mouseId], minY), maxY);
}

104
core/input/mouse.h Normal file
View File

@ -0,0 +1,104 @@
/*
Copyright 2021 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"
// Mouse position and buttons
extern u8 mo_buttons[4];
extern s32 mo_x_abs[4];
extern s32 mo_y_abs[4];
extern f32 mo_x_delta[4];
extern f32 mo_y_delta[4];
extern f32 mo_wheel_delta[4];
extern s32 mo_x_prev[4];
extern s32 mo_y_prev[4];
void SetMousePosition(int x, int y, int width, int height, u32 mouseId = 0);
void SetRelativeMousePosition(float xrel, float yrel, u32 mouseId = 0);
class MouseInputMapping : public InputMapping
{
public:
MouseInputMapping()
{
name = "Mouse";
set_button(DC_BTN_A, 2); // Left
set_button(DC_BTN_B, 1); // Right
set_button(DC_BTN_START, 3); // Middle
dirty = false;
}
};
class Mouse : public GamepadDevice
{
protected:
Mouse(const char *apiName, int maplePort = 0) : GamepadDevice(maplePort, apiName) {
this->_name = "Mouse";
}
virtual std::shared_ptr<InputMapping> getDefaultMapping() override {
return std::make_shared<MouseInputMapping>();
}
public:
enum Button {
LEFT_BUTTON = 2,
RIGHT_BUTTON = 1,
MIDDLE_BUTTON = 3,
BUTTON_4 = 4,
BUTTON_5 = 5
};
virtual const char *get_button_name(u32 code) override
{
switch((Button)code)
{
case LEFT_BUTTON:
return "Left Button";
case RIGHT_BUTTON:
return "Right Button";
case MIDDLE_BUTTON:
return "Middle Button";
case BUTTON_4:
return "Button 4";
case BUTTON_5:
return "Button 5";
default:
return nullptr;
}
}
void setAbsPos(int x, int y, int width, int height);
void setRelPos(float deltax, float deltay);
void setButton(Button button, bool pressed);
void setWheel(int delta);
};
class SystemMouse : public Mouse
{
protected:
SystemMouse(const char *apiName, int maplePort = 0) : Mouse(apiName, maplePort) {}
public:
void setAbsPos(int x, int y, int width, int height);
void setButton(Button button, bool pressed);
void setWheel(int delta);
};

View File

@ -20,6 +20,8 @@
#include "hw/maple/maple_cfg.h"
#include "hw/maple/maple_devs.h"
#include "input/gamepad_device.h"
#include "input/keyboard_device.h"
#include "input/mouse.h"
#include "cfg/option.h"
#include <algorithm>
@ -42,8 +44,17 @@ static void getLocalInput(MapleInputState inputState[4])
state.fullAxes[PJAI_Y1] = joyy[player];
state.fullAxes[PJAI_X2] = joyrx[player];
state.fullAxes[PJAI_Y2] = joyry[player];
state.absPointerX = mo_x_abs[player];
state.absPointerY = mo_y_abs[player];
state.mouseButtons = mo_buttons[player];
state.absPos.x = mo_x_abs[player];
state.absPos.y = mo_y_abs[player];
state.keyboard.shift = kb_shift[player];
memcpy(state.keyboard.key, kb_key[player], sizeof(kb_key[player]));
state.relPos.x = std::round(mo_x_delta[player]);
state.relPos.y = std::round(mo_y_delta[player]);
state.relPos.wheel = std::round(mo_wheel_delta[player]);
mo_x_delta[player] -= state.relPos.x;
mo_y_delta[player] -= state.relPos.y;
mo_wheel_delta[player] -= state.relPos.wheel;
}
}
@ -101,6 +112,9 @@ static bool _endOfFrame;
static MiniUPnP miniupnp;
static int analogAxes;
static bool absPointerPos;
static bool keyboardGame;
static bool mouseGame;
static int inputSize;
static bool inRollback;
static void (*chatCallback)(int playerNum, const std::string& msg);
@ -122,6 +136,30 @@ static int lastSavedFrame = -1;
static int timesyncOccurred;
#pragma pack(push, 1)
struct Inputs
{
u32 kcode:20;
u32 mouseButtons:4;
u8 kbModifiers;
union {
struct {
u8 x;
u8 y;
} analog;
struct {
s16 x;
s16 y;
} absPos;
struct {
s16 x;
s16 y;
s16 wheel;
} relPos;
u8 keys[6];
} u;
};
struct GameEvent
{
enum : char {
@ -440,8 +478,14 @@ void startSession(int localPort, int localPlayerNum)
{
analogAxes = 0;
absPointerPos = false;
keyboardGame = false;
mouseGame = false;
if (settings.input.JammaSetup == JVS::LightGun || settings.input.JammaSetup == JVS::LightGunAsAnalog)
absPointerPos = true;
else if (settings.input.JammaSetup == JVS::Keyboard)
keyboardGame = true;
else if (settings.input.JammaSetup == JVS::RotaryEncoders)
mouseGame = true;
else if (NaomiGameInputs != nullptr)
{
for (const auto& axis : NaomiGameInputs->axes)
@ -454,7 +498,8 @@ void startSession(int localPort, int localPlayerNum)
}
NOTICE_LOG(NETWORK, "GGPO: Using %d full analog axes", analogAxes);
}
const u32 inputSize = sizeof(kcode[0]) + analogAxes + (int)absPointerPos * 4;
inputSize = sizeof(kcode[0]) + analogAxes + (int)absPointerPos * sizeof(Inputs::u.absPos)
+ (int)keyboardGame * sizeof(Inputs::u.keys) + (int)mouseGame * sizeof(Inputs::u.relPos);
VerificationData verif;
MD5Sum().add(settings.network.md5.bios)
@ -551,10 +596,9 @@ void getInput(MapleInputState inputState[4])
for (int player = 0; player < 4; player++)
inputState[player] = {};
u32 inputSize = sizeof(u32) + analogAxes + (int)absPointerPos * 4;
std::vector<u8> inputs(inputSize * MAX_PLAYERS);
std::vector<u8> inputData(inputSize * MAX_PLAYERS);
// should not call any callback
GGPOErrorCode error = ggpo_synchronize_input(ggpoSession, (void *)&inputs[0], inputs.size(), nullptr);
GGPOErrorCode error = ggpo_synchronize_input(ggpoSession, (void *)&inputData[0], inputData.size(), nullptr);
if (error != GGPO_OK)
{
stopSession();
@ -564,17 +608,30 @@ void getInput(MapleInputState inputState[4])
for (int player = 0; player < MAX_PLAYERS; player++)
{
MapleInputState& state = inputState[player];
state.kcode = ~(*(u32 *)&inputs[player * inputSize]);
const Inputs *inputs = (Inputs *)&inputData[player * inputSize];
state.kcode = ~inputs->kcode;
if (analogAxes > 0)
{
state.fullAxes[PJAI_X1] = inputs[player * inputSize + 4];
state.fullAxes[PJAI_X1] = inputs->u.analog.x;
if (analogAxes >= 2)
state.fullAxes[PJAI_Y1] = inputs[player * inputSize + 5];
state.fullAxes[PJAI_Y1] = inputs->u.analog.y;
}
else if (absPointerPos)
{
state.absPointerX = *(s16 *)&inputs[player * inputSize + 4];
state.absPointerY = *(s16 *)&inputs[player * inputSize + 6];
state.absPos.x = inputs->u.absPos.x;
state.absPos.y = inputs->u.absPos.y;
}
else if (keyboardGame)
{
memcpy(state.keyboard.key, inputs->u.keys, sizeof(state.keyboard.key));
state.keyboard.shift = inputs->kbModifiers;
}
else if (mouseGame)
{
state.relPos.x = inputs->u.relPos.x;
state.relPos.y = inputs->u.relPos.y;
state.relPos.wheel = inputs->u.relPos.wheel;
state.mouseButtons = ~inputs->mouseButtons;
}
state.halfAxes[PJTI_R] = (state.kcode & BTN_TRIGGER_RIGHT) == 0 ? 255 : 0;
state.halfAxes[PJTI_L] = (state.kcode & BTN_TRIGGER_LEFT) == 0 ? 255 : 0;
@ -620,30 +677,43 @@ bool nextFrame()
do {
if (!config::ThreadedRendering)
UpdateInputState();
u32 input = ~kcode[localPlayerNum];
Inputs inputs;
inputs.kcode = ~kcode[localPlayerNum];
if (rt[localPlayerNum] >= 64)
input |= BTN_TRIGGER_RIGHT;
inputs.kcode |= BTN_TRIGGER_RIGHT;
else
input &= ~BTN_TRIGGER_RIGHT;
inputs.kcode &= ~BTN_TRIGGER_RIGHT;
if (lt[localPlayerNum] >= 64)
input |= BTN_TRIGGER_LEFT;
inputs.kcode |= BTN_TRIGGER_LEFT;
else
input &= ~BTN_TRIGGER_LEFT;
u32 inputSize = sizeof(input) + analogAxes + (int)absPointerPos * 4;
std::vector<u8> allInput(inputSize);
*(u32 *)&allInput[0] = input;
inputs.kcode &= ~BTN_TRIGGER_LEFT;
if (analogAxes > 0)
{
allInput[4] = joyx[localPlayerNum];
inputs.u.analog.x = joyx[localPlayerNum];
if (analogAxes >= 2)
allInput[5] = joyy[localPlayerNum];
inputs.u.analog.y = joyy[localPlayerNum];
}
else if (absPointerPos)
{
*(s16 *)&allInput[4] = mo_x_abs[localPlayerNum];
*(s16 *)&allInput[6] = mo_y_abs[localPlayerNum];
inputs.u.absPos.x = mo_x_abs[localPlayerNum];
inputs.u.absPos.y = mo_y_abs[localPlayerNum];
}
GGPOErrorCode result = ggpo_add_local_input(ggpoSession, localPlayer, &allInput[0], inputSize);
else if (keyboardGame)
{
inputs.kbModifiers = kb_shift[localPlayerNum];
memcpy(inputs.u.keys, kb_key[localPlayerNum], sizeof(kb_key[localPlayerNum]));
}
else if (mouseGame)
{
inputs.mouseButtons = ~mo_buttons[localPlayerNum];
inputs.u.relPos.x = std::round(mo_x_delta[localPlayerNum]);
inputs.u.relPos.y = std::round(mo_y_delta[localPlayerNum]);
inputs.u.relPos.wheel = std::round(mo_wheel_delta[localPlayerNum]);
mo_x_delta[localPlayerNum] -= inputs.u.relPos.x;
mo_y_delta[localPlayerNum] -= inputs.u.relPos.y;
mo_wheel_delta[localPlayerNum] -= inputs.u.relPos.wheel;
}
GGPOErrorCode result = ggpo_add_local_input(ggpoSession, localPlayer, &inputs, inputSize);
if (result == GGPO_OK)
break;
if (result != GGPO_ERRORCODE_PREDICTION_THRESHOLD)

View File

@ -31,6 +31,7 @@
#include "network/ggpo.h"
#include "wsi/context.h"
#include "input/gamepad_device.h"
#include "input/mouse.h"
#include "gui_util.h"
#include "gui_android.h"
#include "game_scanner.h"
@ -46,9 +47,6 @@
static bool game_started;
extern u8 kb_shift[MAPLE_PORTS]; // shift keys pressed (bitmask)
extern u8 kb_key[MAPLE_PORTS][6]; // normal keys pressed
int screen_dpi = 96;
int insetLeft, insetRight, insetTop, insetBottom;
@ -279,6 +277,16 @@ void gui_keyboard_inputUTF8(const std::string& s)
io.AddInputCharactersUTF8(s.c_str());
}
void gui_keyboard_key(u8 keyCode, bool pressed, u8 modifiers)
{
if (!inited)
return;
ImGuiIO& io = ImGui::GetIO();
io.KeyCtrl = (modifiers & (0x01 | 0x10)) != 0;
io.KeyShift = (modifiers & (0x02 | 0x20)) != 0;
io.KeysDown[keyCode] = pressed;
}
bool gui_keyboard_captured()
{
ImGuiIO& io = ImGui::GetIO();
@ -323,21 +331,6 @@ static void ImGui_Impl_NewFrame()
ImGuiIO& io = ImGui::GetIO();
// Read keyboard modifiers inputs
io.KeyCtrl = 0;
io.KeyShift = 0;
io.KeyAlt = false;
io.KeySuper = false;
memset(&io.KeysDown[0], 0, sizeof(io.KeysDown));
for (int port = 0; port < 4; port++)
{
io.KeyCtrl |= (kb_shift[port] & (0x01 | 0x10)) != 0;
io.KeyShift |= (kb_shift[port] & (0x02 | 0x20)) != 0;
for (int i = 0; i < IM_ARRAYSIZE(kb_key[0]); i++)
if (kb_key[port][i] != 0)
io.KeysDown[kb_key[port][i]] = true;
}
if (mouseX < 0 || mouseX >= settings.display.width || mouseY < 0 || mouseY >= settings.display.height)
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
else
@ -1895,6 +1888,7 @@ static void gui_display_settings()
ImGui::SameLine();
OptionRadioButton<int>("Full", config::GGPOAnalogAxes, 2, "Use the left thumbstick horizontal and vertical axes");
OptionCheckbox("Enable Chat", config::GGPOChat, "Open the chat window when a chat message is received");
OptionCheckbox("Network Statistics", config::NetworkStats,
"Display network statistics on screen");
}

View File

@ -32,6 +32,7 @@ void gui_refresh_files();
void gui_cheats();
void gui_keyboard_input(u16 wc);
void gui_keyboard_inputUTF8(const std::string& s);
void gui_keyboard_key(u8 keyCode, bool pressed, u8 modifiers);
bool gui_keyboard_captured();
bool gui_mouse_captured();
void gui_set_mouse_position(int x, int y);

View File

@ -100,7 +100,8 @@ public:
void receive(int playerNum, const std::string& msg)
{
visible = true;
if (config::GGPOChat)
visible = true;
std::string line = "<" + playerName(true) + "> " + msg;
lines.push_back(std::make_pair(YELLOW, line));
newMessage = true;

View File

@ -1,4 +1,5 @@
#include "../input/gamepad_device.h"
#include "input/gamepad_device.h"
#include "input/mouse.h"
#include "oslib/oslib.h"
#include "sdl.h"
#include "rend/gui.h"

View File

@ -19,6 +19,7 @@
#pragma once
#include "input/gamepad_device.h"
#include "input/mouse.h"
#include <algorithm>
static jobject input_device_manager;

View File

@ -22,6 +22,7 @@
#include <cmath>
#include "input/gamepad_device.h"
#include "input/mouse.h"
#include "rend/gui.h"
enum IOSButton {

View File

@ -7,7 +7,7 @@
#pragma once
#import <GameController/GameController.h>
#include "input/gamepad_device.h"
#include "input/mouse.h"
class API_AVAILABLE(ios(14.0)) IOSMouse : public SystemMouse
{

View File

@ -5,7 +5,7 @@
// Created by flyinghead on 26/02/2019.
// Copyright © 2019 reicast. All rights reserved.
//
#include "input/gamepad_device.h"
#include "input/mouse.h"
class OSXMouse : public SystemMouse
{

View File

@ -116,6 +116,21 @@ u8 lt[4];
u32 vks[4];
s8 joyx[4], joyy[4];
s8 joyrx[4], joyry[4];
// Mouse buttons
// bit 0: Button C
// bit 1: Right button (B)
// bit 2: Left button (A)
// bit 3: Wheel button
u8 mo_buttons[4] = { 0xFF, 0xFF, 0xFF, 0xFF };
// Relative mouse coordinates [-512:511]
f32 mo_x_delta[4];
f32 mo_y_delta[4];
f32 mo_wheel_delta[4];
// Absolute mouse coordinates
// Range [0:639] [0:479]
// but may be outside this range if the pointer is offscreen or outside the 4:3 window.
s32 mo_x_abs[4];
s32 mo_y_abs[4];
static bool enable_purupuru = true;
static u32 vib_stop_time[4];
@ -151,6 +166,7 @@ static void init_disk_control_interface();
static bool read_m3u(const char *file);
void UpdateInputState();
void gui_display_notification(const char *msg, int duration);
static void updateVibration(u32 port, float power, float inclination, u32 durationMs);
static std::string game_data;
static char g_base_name[128];
@ -276,6 +292,7 @@ void retro_init()
ERROR_LOG(VMEM, "Cannot reserve memory space");
os_InstallFaultHandler();
MapleConfigMap::UpdateVibration = updateVibration;
}
void retro_deinit()
@ -2571,7 +2588,7 @@ void UpdateInputState()
UpdateInputState(3);
}
void UpdateVibration(u32 port, float power, float inclination, u32 durationMs)
static void updateVibration(u32 port, float power, float inclination, u32 durationMs)
{
if (!rumble.set_rumble_state)
return;
@ -2583,8 +2600,8 @@ void UpdateVibration(u32 port, float power, float inclination, u32 durationMs)
vib_delta[port] = inclination;
}
extern u8 kb_key[4][6]; // normal keys pressed
extern u8 kb_shift[4]; // modifier keys pressed (bitmask)
u8 kb_key[4][6]; // normal keys pressed
u8 kb_shift[4]; // modifier keys pressed (bitmask)
static int kb_used;
static void release_key(unsigned dc_keycode)