mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-03 20:22:45 +00:00
Debugger: Add API to trigger buttons.
This commit is contained in:
parent
a88f6b45d2
commit
de3277c679
@ -1492,6 +1492,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/Debugger/WebSocket/GPURecordSubscriber.h
|
||||
Core/Debugger/WebSocket/HLESubscriber.cpp
|
||||
Core/Debugger/WebSocket/HLESubscriber.h
|
||||
Core/Debugger/WebSocket/InputSubscriber.cpp
|
||||
Core/Debugger/WebSocket/InputSubscriber.h
|
||||
Core/Debugger/WebSocket/LogBroadcaster.cpp
|
||||
Core/Debugger/WebSocket/LogBroadcaster.h
|
||||
Core/Debugger/WebSocket/MemorySubscriber.cpp
|
||||
|
@ -437,6 +437,7 @@
|
||||
<ClCompile Include="Debugger\WebSocket\GPUBufferSubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\GPURecordSubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\HLESubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\InputSubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\LogBroadcaster.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\DisasmSubscriber.cpp" />
|
||||
<ClCompile Include="Debugger\WebSocket\MemorySubscriber.cpp" />
|
||||
@ -981,6 +982,7 @@
|
||||
<ClInclude Include="Debugger\WebSocket\GPUBufferSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\GPURecordSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\HLESubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\InputSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\SteppingSubscriber.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\WebSocketUtils.h" />
|
||||
<ClInclude Include="Debugger\WebSocket\CPUCoreSubscriber.h" />
|
||||
|
@ -965,6 +965,9 @@
|
||||
<ClCompile Include="ThreadPools.cpp">
|
||||
<Filter>Core</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger\WebSocket\InputSubscriber.cpp">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
@ -1649,6 +1652,9 @@
|
||||
<ClInclude Include="ThreadPools.h">
|
||||
<Filter>Core</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Debugger\WebSocket\InputSubscriber.h">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "Core/Debugger/WebSocket/GPUBufferSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/GPURecordSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/HLESubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/InputSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/MemorySubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/SteppingSubscriber.h"
|
||||
|
||||
@ -67,6 +68,7 @@ static const std::vector<SubscriberInit> subscribers({
|
||||
&WebSocketGPUBufferInit,
|
||||
&WebSocketGPURecordInit,
|
||||
&WebSocketHLEInit,
|
||||
&WebSocketInputInit,
|
||||
&WebSocketMemoryInit,
|
||||
&WebSocketSteppingInit,
|
||||
});
|
||||
|
256
Core/Debugger/WebSocket/InputSubscriber.cpp
Normal file
256
Core/Debugger/WebSocket/InputSubscriber.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
// Copyright (c) 2021- PPSSPP Project.
|
||||
|
||||
// This program 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, version 2.0 or later versions.
|
||||
|
||||
// This program 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 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/Debugger/WebSocket/InputSubscriber.h"
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
#include "Core/HLE/sceCtrl.h"
|
||||
#include "Core/HLE/sceDisplay.h"
|
||||
|
||||
static const std::unordered_map<std::string, uint32_t> buttonLookup = {
|
||||
{ "cross", CTRL_CROSS },
|
||||
{ "circle", CTRL_CIRCLE },
|
||||
{ "triangle", CTRL_TRIANGLE },
|
||||
{ "square", CTRL_SQUARE },
|
||||
{ "up", CTRL_UP },
|
||||
{ "down", CTRL_DOWN },
|
||||
{ "left", CTRL_LEFT },
|
||||
{ "right", CTRL_RIGHT },
|
||||
{ "start", CTRL_START },
|
||||
{ "select", CTRL_SELECT },
|
||||
{ "home", CTRL_HOME },
|
||||
{ "screen", CTRL_SCREEN },
|
||||
{ "note", CTRL_NOTE },
|
||||
{ "ltrigger", CTRL_LTRIGGER },
|
||||
{ "rtrigger", CTRL_RTRIGGER },
|
||||
{ "hold", CTRL_HOLD },
|
||||
{ "wlan", CTRL_WLAN },
|
||||
{ "remote_hold", CTRL_REMOTE_HOLD },
|
||||
{ "vol_up", CTRL_VOL_UP },
|
||||
{ "vol_down", CTRL_VOL_DOWN },
|
||||
{ "disc", CTRL_DISC },
|
||||
{ "memstick", CTRL_MEMSTICK },
|
||||
{ "forward", CTRL_FORWARD },
|
||||
{ "back", CTRL_BACK },
|
||||
{ "playpause", CTRL_PLAYPAUSE },
|
||||
};
|
||||
|
||||
struct WebSocketInputState : public DebuggerSubscriber {
|
||||
void Buttons(DebuggerRequest &req);
|
||||
void Press(DebuggerRequest &req);
|
||||
void Analog(DebuggerRequest &req);
|
||||
|
||||
void Broadcast(net::WebSocketServer *ws) override;
|
||||
|
||||
protected:
|
||||
struct PressInfo {
|
||||
std::string ticket;
|
||||
uint32_t button;
|
||||
uint32_t duration;
|
||||
|
||||
std::string Event();
|
||||
};
|
||||
|
||||
std::vector<PressInfo> pressTickets_;
|
||||
int lastCounter_ = -1;
|
||||
};
|
||||
|
||||
std::string WebSocketInputState::PressInfo::Event() {
|
||||
JsonWriter j;
|
||||
j.begin();
|
||||
j.writeString("event", "input.press");
|
||||
if (!ticket.empty()) {
|
||||
j.writeRaw("ticket", ticket);
|
||||
}
|
||||
j.end();
|
||||
return j.str();
|
||||
}
|
||||
|
||||
DebuggerSubscriber *WebSocketInputInit(DebuggerEventHandlerMap &map) {
|
||||
auto p = new WebSocketInputState();
|
||||
map["input.buttons"] = std::bind(&WebSocketInputState::Buttons, p, std::placeholders::_1);
|
||||
map["input.press"] = std::bind(&WebSocketInputState::Press, p, std::placeholders::_1);
|
||||
map["input.analog"] = std::bind(&WebSocketInputState::Analog, p, std::placeholders::_1);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Alter PSP button press flags (input.buttons)
|
||||
//
|
||||
// Parameters:
|
||||
// - buttons: object containing button names as string keys, boolean press state as value.
|
||||
//
|
||||
// Button names (some are not respected by PPSSPP):
|
||||
// - cross: button on bottom side of right pad.
|
||||
// - circle: button on right side of right pad.
|
||||
// - triangle: button on top side of right pad.
|
||||
// - square: button on left side of right pad.
|
||||
// - up: d-pad up button.
|
||||
// - down: d-pad down button.
|
||||
// - left: d-pad left button.
|
||||
// - right: d-pad right button.
|
||||
// - start: rightmost button at bottom of device.
|
||||
// - select: second to the right at bottom of device.
|
||||
// - home: leftmost button at bottom of device.
|
||||
// - screen: brightness control button at bottom of device.
|
||||
// - note: mute control button at bottom of device.
|
||||
// - ltrigger: left shoulder trigger button.
|
||||
// - rtrigger: right shoulder trigger button.
|
||||
// - hold: hold setting of power switch.
|
||||
// - wlan: wireless networking switch.
|
||||
// - remote_hold: hold switch on headset.
|
||||
// - vol_up: volume up button next to home at bottom of device.
|
||||
// - vol_down: volume down button next to home at bottom of device.
|
||||
// - disc: UMD disc sensor.
|
||||
// - memstick: memory stick sensor.
|
||||
// - forward: forward button on headset.
|
||||
// - back: back button on headset.
|
||||
// - playpause: play/pause button on headset.
|
||||
//
|
||||
// Empty response.
|
||||
void WebSocketInputState::Buttons(DebuggerRequest &req) {
|
||||
const JsonNode *jsonButtons = req.data.get("buttons");
|
||||
if (!jsonButtons) {
|
||||
return req.Fail("Missing 'buttons' parameter");
|
||||
}
|
||||
if (jsonButtons->value.getTag() != JSON_OBJECT) {
|
||||
return req.Fail("Invalid 'buttons' parameter type");
|
||||
}
|
||||
|
||||
uint32_t downFlags = 0;
|
||||
uint32_t upFlags = 0;
|
||||
|
||||
for (const JsonNode *button : jsonButtons->value) {
|
||||
auto info = buttonLookup.find(button->key);
|
||||
if (info == buttonLookup.end()) {
|
||||
return req.Fail(StringFromFormat("Unsupported 'buttons' object key '%s'", button->key));
|
||||
}
|
||||
if (button->value.getTag() == JSON_TRUE) {
|
||||
downFlags |= info->second;
|
||||
} else if (button->value.getTag() == JSON_FALSE) {
|
||||
upFlags |= info->second;
|
||||
} else if (button->value.getTag() != JSON_NULL) {
|
||||
return req.Fail(StringFromFormat("Unsupported 'buttons' object type for key '%s'", button->key));
|
||||
}
|
||||
}
|
||||
|
||||
if (downFlags) {
|
||||
__CtrlButtonDown(downFlags);
|
||||
}
|
||||
if (upFlags) {
|
||||
__CtrlButtonUp(upFlags);
|
||||
}
|
||||
|
||||
req.Respond();
|
||||
}
|
||||
|
||||
// Press and release a button (input.press)
|
||||
//
|
||||
// Parameters:
|
||||
// - button: required string indicating button name (see input.buttons.)
|
||||
// - duration: optional integer indicating frames to press for, defaults to 1.
|
||||
//
|
||||
// Empty response once released.
|
||||
void WebSocketInputState::Press(DebuggerRequest &req) {
|
||||
std::string button;
|
||||
if (!req.ParamString("button", &button))
|
||||
return;
|
||||
|
||||
PressInfo press;
|
||||
press.duration = 1;
|
||||
if (!req.ParamU32("duration", &press.duration, false, DebuggerParamType::OPTIONAL))
|
||||
return;
|
||||
if (press.duration < 0)
|
||||
return req.Fail("Parameter 'duration' must not be negative");
|
||||
const JsonNode *value = req.data.get("ticket");
|
||||
press.ticket = value ? json_stringify(value) : "";
|
||||
|
||||
auto info = buttonLookup.find(button);
|
||||
if (info == buttonLookup.end()) {
|
||||
return req.Fail(StringFromFormat("Unsupported button value '%s'", button.c_str()));
|
||||
}
|
||||
press.button = info->second;
|
||||
|
||||
__CtrlButtonDown(press.button);
|
||||
pressTickets_.push_back(press);
|
||||
}
|
||||
|
||||
void WebSocketInputState::Broadcast(net::WebSocketServer *ws) {
|
||||
int counter = __DisplayGetNumVblanks();
|
||||
if (pressTickets_.empty() || lastCounter_ == counter)
|
||||
return;
|
||||
lastCounter_ = counter;
|
||||
|
||||
for (PressInfo &press : pressTickets_) {
|
||||
press.duration--;
|
||||
if (press.duration == -1) {
|
||||
__CtrlButtonUp(press.button);
|
||||
ws->Send(press.Event());
|
||||
}
|
||||
}
|
||||
auto negative = [](const PressInfo &press) -> bool {
|
||||
return press.duration < 0;
|
||||
};
|
||||
pressTickets_.erase(std::remove_if(pressTickets_.begin(), pressTickets_.end(), negative), pressTickets_.end());
|
||||
}
|
||||
|
||||
static bool AnalogValue(DebuggerRequest &req, float *value, const char *name) {
|
||||
const JsonNode *node = req.data.get(name);
|
||||
if (!node) {
|
||||
req.Fail(StringFromFormat("Missing '%s' parameter", name));
|
||||
return false;
|
||||
}
|
||||
if (node->value.getTag() != JSON_NUMBER) {
|
||||
req.Fail(StringFromFormat("Invalid '%s' parameter type", name));
|
||||
return false;
|
||||
}
|
||||
|
||||
double val = node->value.toNumber();
|
||||
if (val < 1.0 || val > 1.0) {
|
||||
req.Fail(StringFromFormat("Parameter '%s' must be between -1.0 and 1.0", name));
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = (float)val;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set coordinates of analog stick (input.analog)
|
||||
//
|
||||
// Parameters:
|
||||
// - x: required number from -1.0 to 1.0.
|
||||
// - y: required number from -1.0 to 1.0.
|
||||
// - stick: optional string, either "left" (default) or "right".
|
||||
//
|
||||
// Empty response.
|
||||
void WebSocketInputState::Analog(DebuggerRequest &req) {
|
||||
std::string stick = "left";
|
||||
if (!req.ParamString("stick", &stick, DebuggerParamType::OPTIONAL))
|
||||
return;
|
||||
if (stick != "left" && stick != "right")
|
||||
return req.Fail(StringFromFormat("Parameter 'stick' must be 'left' or 'right', not '%s'", stick.c_str()));
|
||||
float x, y;
|
||||
if (!AnalogValue(req, &x, "x") || !AnalogValue(req, &y, "y"))
|
||||
return;
|
||||
|
||||
__CtrlSetAnalogX(x, stick == "left" ? CTRL_STICK_LEFT : CTRL_STICK_RIGHT);
|
||||
__CtrlSetAnalogY(y, stick == "left" ? CTRL_STICK_LEFT : CTRL_STICK_RIGHT);
|
||||
|
||||
req.Respond();
|
||||
}
|
22
Core/Debugger/WebSocket/InputSubscriber.h
Normal file
22
Core/Debugger/WebSocket/InputSubscriber.h
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2021- PPSSPP Project.
|
||||
|
||||
// This program 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, version 2.0 or later versions.
|
||||
|
||||
// This program 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 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/Debugger/WebSocket/WebSocketUtils.h"
|
||||
|
||||
DebuggerSubscriber *WebSocketInputInit(DebuggerEventHandlerMap &map);
|
@ -399,6 +399,7 @@
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\GPUBufferSubscriber.h" />
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\GPURecordSubscriber.h" />
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\HLESubscriber.h" />
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\InputSubscriber.h" />
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\LogBroadcaster.h" />
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\MemorySubscriber.h" />
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\SteppingBroadcaster.h" />
|
||||
@ -629,6 +630,7 @@
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\GPUBufferSubscriber.cpp" />
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\GPURecordSubscriber.cpp" />
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\HLESubscriber.cpp" />
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\InputSubscriber.cpp" />
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\LogBroadcaster.cpp" />
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\MemorySubscriber.cpp" />
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\SteppingBroadcaster.cpp" />
|
||||
|
@ -682,6 +682,9 @@
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\HLESubscriber.cpp">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\InputSubscriber.cpp">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\Core\Debugger\WebSocket\LogBroadcaster.cpp">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClCompile>
|
||||
@ -1488,6 +1491,9 @@
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\HLESubscriber.h">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\InputSubscriber.h">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Core\Debugger\WebSocket\LogBroadcaster.h">
|
||||
<Filter>Debugger\WebSocket</Filter>
|
||||
</ClInclude>
|
||||
|
@ -413,6 +413,7 @@ EXEC_AND_LIB_FILES := \
|
||||
$(SRC)/Core/Debugger/WebSocket/GPUBufferSubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/GPURecordSubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/HLESubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/InputSubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/MemorySubscriber.cpp \
|
||||
$(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \
|
||||
|
Loading…
x
Reference in New Issue
Block a user