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 \