From f97df5d1537535f0af84298cc8f1ab82f351d0be Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 18 Jan 2021 17:51:35 -0800 Subject: [PATCH] Debugger: Broadcast ctrl input events. This can be useful to trigger debugging functionality on button press. --- CMakeLists.txt | 2 + Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 ++ Core/Debugger/WebSocket.cpp | 5 +- Core/Debugger/WebSocket/InputBroadcaster.cpp | 96 ++++++++++++++++++++ Core/Debugger/WebSocket/InputBroadcaster.h | 48 ++++++++++ Core/Debugger/WebSocket/InputSubscriber.cpp | 35 ++++--- Core/Debugger/WebSocket/InputSubscriber.h | 3 + UWP/CoreUWP/CoreUWP.vcxproj | 2 + UWP/CoreUWP/CoreUWP.vcxproj.filters | 6 ++ android/jni/Android.mk | 1 + 11 files changed, 190 insertions(+), 16 deletions(-) create mode 100644 Core/Debugger/WebSocket/InputBroadcaster.cpp create mode 100644 Core/Debugger/WebSocket/InputBroadcaster.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b943386e..9be8dfd629 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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/InputBroadcaster.cpp + Core/Debugger/WebSocket/InputBroadcaster.h Core/Debugger/WebSocket/InputSubscriber.cpp Core/Debugger/WebSocket/InputSubscriber.h Core/Debugger/WebSocket/LogBroadcaster.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index d899148602..060eeb4ddb 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -437,6 +437,7 @@ + @@ -982,6 +983,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index bb8f2bcfc5..93b6b137ef 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -968,6 +968,9 @@ Debugger\WebSocket + + Debugger\WebSocket + @@ -1655,6 +1658,9 @@ Debugger\WebSocket + + Debugger\WebSocket + diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 4dad585e71..c609e08698 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -45,6 +45,7 @@ // For other events, look inside Core/Debugger/WebSocket/ for details on each event. #include "Core/Debugger/WebSocket/GameBroadcaster.h" +#include "Core/Debugger/WebSocket/InputBroadcaster.h" #include "Core/Debugger/WebSocket/LogBroadcaster.h" #include "Core/Debugger/WebSocket/SteppingBroadcaster.h" @@ -127,8 +128,9 @@ void HandleDebuggerRequest(const http::Request &request) { UpdateConnected(1); SetupDebuggerLock(); - LogBroadcaster logger; GameBroadcaster game; + LogBroadcaster logger; + InputBroadcaster input; SteppingBroadcaster stepping; std::unordered_map eventHandlers; @@ -177,6 +179,7 @@ void HandleDebuggerRequest(const http::Request &request) { logger.Broadcast(ws); game.Broadcast(ws); stepping.Broadcast(ws); + input.Broadcast(ws); for (size_t i = 0; i < subscribers.size(); ++i) { if (subscriberData[i]) { diff --git a/Core/Debugger/WebSocket/InputBroadcaster.cpp b/Core/Debugger/WebSocket/InputBroadcaster.cpp new file mode 100644 index 0000000000..1502c6702f --- /dev/null +++ b/Core/Debugger/WebSocket/InputBroadcaster.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2018- 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 +#include "Core/Debugger/WebSocket/InputBroadcaster.h" +#include "Core/Debugger/WebSocket/InputSubscriber.h" +#include "Core/Debugger/WebSocket/WebSocketUtils.h" +#include "Core/HLE/sceCtrl.h" +#include "Core/HLE/sceDisplay.h" + +// Button press state change (input.buttons) +// +// Sent unexpectedly with these properties: +// - buttons: an object with button names as keys and bool press state as values. +// +// See input.buttons.send in InputSubscriber for button names. Only changed buttons are sent. + +// Analog position change (input.analog) +// +// Sent unexpectedly with these properties: +// - stick: "left" or "right". +// - x: number between -1.0 and 1.0, representing horizontal position in a square. +// - y: number between -1.0 and 1.0, representing vertical position in a square. + +std::string InputBroadcaster::Analog::Event(const char *stick) { + JsonWriter j; + j.begin(); + j.writeString("event", "input.analog"); + j.writeString("stick", stick); + j.writeFloat("x", x); + j.writeFloat("y", y); + j.end(); + return j.str(); +} + +static std::string ButtonsEvent(uint32_t lastButtons, uint32_t newButtons) { + uint32_t pressed = newButtons & ~lastButtons; + uint32_t released = ~newButtons & lastButtons; + + JsonWriter j; + j.begin(); + j.writeString("event", "input.buttons"); + j.pushDict("buttons"); + for (auto it : WebSocketInputButtonLookup()) { + if (pressed & it.second) { + j.writeBool(it.first, true); + } else if (released & it.second) { + j.writeBool(it.first, false); + } + } + j.pop(); + j.end(); + return j.str(); +} + +void InputBroadcaster::Broadcast(net::WebSocketServer *ws) { + int counter = __DisplayGetNumVblanks(); + if (lastCounter_ == counter) + return; + lastCounter_ = counter; + + uint32_t newButtons = __CtrlPeekButtons(); + if (newButtons != lastButtons_) { + ws->Send(ButtonsEvent(lastButtons_, newButtons)); + lastButtons_ = newButtons; + } + + Analog newAnalog; + __CtrlPeekAnalog(CTRL_STICK_LEFT, &newAnalog.x, &newAnalog.y); + if (!lastAnalog_[0].Equals(newAnalog)) { + ws->Send(newAnalog.Event("left")); + lastAnalog_[0].x = newAnalog.x; + lastAnalog_[0].y = newAnalog.y; + } + + __CtrlPeekAnalog(CTRL_STICK_RIGHT, &newAnalog.x, &newAnalog.y); + if (!lastAnalog_[1].Equals(newAnalog)) { + ws->Send(newAnalog.Event("right")); + lastAnalog_[1].x = newAnalog.x; + lastAnalog_[1].y = newAnalog.y; + } +} diff --git a/Core/Debugger/WebSocket/InputBroadcaster.h b/Core/Debugger/WebSocket/InputBroadcaster.h new file mode 100644 index 0000000000..dd8031d902 --- /dev/null +++ b/Core/Debugger/WebSocket/InputBroadcaster.h @@ -0,0 +1,48 @@ +// Copyright (c) 2018- 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 +#include + +namespace net { +class WebSocketServer; +} + +struct InputBroadcaster { +public: + InputBroadcaster() { + } + + void Broadcast(net::WebSocketServer *ws); + +private: + struct Analog { + float x = 0.0f; + float y = 0.0f; + + bool Equals(const Analog &other) const { + return x == other.x && y == other.y; + } + std::string Event(const char *stick); + }; + + int lastCounter_ = -1; + uint32_t lastButtons_ = 0; + Analog lastAnalog_[2]; +}; diff --git a/Core/Debugger/WebSocket/InputSubscriber.cpp b/Core/Debugger/WebSocket/InputSubscriber.cpp index 9e6276c68c..b35c8413e7 100644 --- a/Core/Debugger/WebSocket/InputSubscriber.cpp +++ b/Core/Debugger/WebSocket/InputSubscriber.cpp @@ -23,7 +23,8 @@ #include "Core/HLE/sceCtrl.h" #include "Core/HLE/sceDisplay.h" -static const std::unordered_map buttonLookup = { +// This is also used in InputBroadcaster. +const std::unordered_map buttonLookup = { { "cross", CTRL_CROSS }, { "circle", CTRL_CIRCLE }, { "triangle", CTRL_TRIANGLE }, @@ -52,9 +53,9 @@ static const std::unordered_map buttonLookup = { }; struct WebSocketInputState : public DebuggerSubscriber { - void Buttons(DebuggerRequest &req); - void Press(DebuggerRequest &req); - void Analog(DebuggerRequest &req); + void ButtonsSend(DebuggerRequest &req); + void ButtonsPress(DebuggerRequest &req); + void AnalogSend(DebuggerRequest &req); void Broadcast(net::WebSocketServer *ws) override; @@ -74,7 +75,7 @@ protected: std::string WebSocketInputState::PressInfo::Event() { JsonWriter j; j.begin(); - j.writeString("event", "input.press"); + j.writeString("event", "input.buttons.press"); if (!ticket.empty()) { j.writeRaw("ticket", ticket); } @@ -82,16 +83,20 @@ std::string WebSocketInputState::PressInfo::Event() { return j.str(); } +const std::unordered_map &WebSocketInputButtonLookup() { + return buttonLookup; +} + 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); + map["input.buttons.send"] = std::bind(&WebSocketInputState::ButtonsSend, p, std::placeholders::_1); + map["input.buttons.press"] = std::bind(&WebSocketInputState::ButtonsPress, p, std::placeholders::_1); + map["input.analog.send"] = std::bind(&WebSocketInputState::AnalogSend, p, std::placeholders::_1); return p; } -// Alter PSP button press flags (input.buttons) +// Alter PSP button press flags (input.buttons.send) // // Parameters: // - buttons: object containing button names as string keys, boolean press state as value. @@ -124,7 +129,7 @@ DebuggerSubscriber *WebSocketInputInit(DebuggerEventHandlerMap &map) { // - playpause: play/pause button on headset. // // Empty response. -void WebSocketInputState::Buttons(DebuggerRequest &req) { +void WebSocketInputState::ButtonsSend(DebuggerRequest &req) { const JsonNode *jsonButtons = req.data.get("buttons"); if (!jsonButtons) { return req.Fail("Missing 'buttons' parameter"); @@ -160,14 +165,14 @@ void WebSocketInputState::Buttons(DebuggerRequest &req) { req.Respond(); } -// Press and release a button (input.press) +// Press and release a button (input.buttons.press) // // Parameters: -// - button: required string indicating button name (see input.buttons.) +// - button: required string indicating button name (see input.buttons.send.) // - duration: optional integer indicating frames to press for, defaults to 1. // // Empty response once released. -void WebSocketInputState::Press(DebuggerRequest &req) { +void WebSocketInputState::ButtonsPress(DebuggerRequest &req) { std::string button; if (!req.ParamString("button", &button)) return; @@ -231,7 +236,7 @@ static bool AnalogValue(DebuggerRequest &req, float *value, const char *name) { return true; } -// Set coordinates of analog stick (input.analog) +// Set coordinates of analog stick (input.analog.send) // // Parameters: // - x: required number from -1.0 to 1.0. @@ -239,7 +244,7 @@ static bool AnalogValue(DebuggerRequest &req, float *value, const char *name) { // - stick: optional string, either "left" (default) or "right". // // Empty response. -void WebSocketInputState::Analog(DebuggerRequest &req) { +void WebSocketInputState::AnalogSend(DebuggerRequest &req) { std::string stick = "left"; if (!req.ParamString("stick", &stick, DebuggerParamType::OPTIONAL)) return; diff --git a/Core/Debugger/WebSocket/InputSubscriber.h b/Core/Debugger/WebSocket/InputSubscriber.h index ed67ae659d..65acbe9efe 100644 --- a/Core/Debugger/WebSocket/InputSubscriber.h +++ b/Core/Debugger/WebSocket/InputSubscriber.h @@ -17,6 +17,9 @@ #pragma once +#include +#include #include "Core/Debugger/WebSocket/WebSocketUtils.h" DebuggerSubscriber *WebSocketInputInit(DebuggerEventHandlerMap &map); +const std::unordered_map &WebSocketInputButtonLookup(); diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 77fff29b39..8b40a36f2e 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -399,6 +399,7 @@ + @@ -630,6 +631,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 06db4d5876..c688cd49f5 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -682,6 +682,9 @@ Debugger\WebSocket + + Debugger\WebSocket + Debugger\WebSocket @@ -1491,6 +1494,9 @@ Debugger\WebSocket + + Debugger\WebSocket + Debugger\WebSocket diff --git a/android/jni/Android.mk b/android/jni/Android.mk index ef935455d6..e3e9ba19ff 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -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/InputBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/InputSubscriber.cpp \ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \ $(SRC)/Core/Debugger/WebSocket/MemorySubscriber.cpp \