diff --git a/CMakeLists.txt b/CMakeLists.txt
index d92c93b073b7..dcd1f542d2dd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1430,6 +1430,13 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/DisassemblyManager.h
Core/Debugger/WebSocket.cpp
Core/Debugger/WebSocket.h
+ Core/Debugger/WebSocket/Common.h
+ Core/Debugger/WebSocket/GameBroadcaster.cpp
+ Core/Debugger/WebSocket/GameBroadcaster.h
+ Core/Debugger/WebSocket/LogBroadcaster.cpp
+ Core/Debugger/WebSocket/LogBroadcaster.h
+ Core/Debugger/WebSocket/SteppingBroadcaster.cpp
+ Core/Debugger/WebSocket/SteppingBroadcaster.h
Core/Dialog/PSPDialog.cpp
Core/Dialog/PSPDialog.h
Core/Dialog/PSPGamedataInstallDialog.cpp
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 672adc6c1dca..3954efcbb48b 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -185,6 +185,9 @@
+
+
+
@@ -533,6 +536,10 @@
+
+
+
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 89a505a8f4ec..524d763eda4f 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -70,6 +70,9 @@
{119ac973-e457-4025-9e1e-4fb34022ae23}
+
+ {c21d9bb5-614d-451b-8c0b-3078b29122d8}
+
@@ -692,6 +695,15 @@
Debugger
+
+ Debugger\WebSocket
+
+
+ Debugger\WebSocket
+
+
+ Debugger\WebSocket
+
@@ -1274,6 +1286,18 @@
Debugger
+
+ Debugger\WebSocket
+
+
+ Debugger\WebSocket
+
+
+ Debugger\WebSocket
+
+
+ Debugger\WebSocket
+
diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp
index 3d9cb95a6f1c..3225b8d9f961 100644
--- a/Core/Debugger/WebSocket.cpp
+++ b/Core/Debugger/WebSocket.cpp
@@ -15,113 +15,63 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
-#include
-#include
-#include "json/json_writer.h"
-#include "net/websocket_server.h"
#include "Core/Debugger/WebSocket.h"
-#include "Common/LogManager.h"
+#include "Core/Debugger/WebSocket/Common.h"
-// TODO: Move this to its own file?
-class DebuggerLogListener : public LogListener {
-public:
- void Log(const LogMessage &msg) override {
- std::lock_guard guard(lock_);
- messages_[nextMessage_] = msg;
- nextMessage_++;
- if (nextMessage_ >= BUFFER_SIZE)
- nextMessage_ -= BUFFER_SIZE;
- count_++;
- }
-
- std::vector GetMessages() {
- std::lock_guard guard(lock_);
- int splitPoint;
- int readCount;
- if (read_ + BUFFER_SIZE < count_) {
- // We'll start with our oldest then.
- splitPoint = nextMessage_;
- readCount = Count();
- } else {
- splitPoint = read_;
- readCount = count_ - read_;
- }
-
- read_ = count_;
-
- std::vector results;
- int splitEnd = std::min(splitPoint + readCount, (int)BUFFER_SIZE);
- for (int i = splitPoint; i < splitEnd; ++i) {
- results.push_back(messages_[i]);
- readCount--;
- }
- for (int i = 0; i < readCount; ++i) {
- results.push_back(messages_[i]);
- }
-
- return results;
- }
-
- int Count() const {
- return count_ < BUFFER_SIZE ? count_ : BUFFER_SIZE;
- }
-
-private:
- enum { BUFFER_SIZE = 128 };
- LogMessage messages_[BUFFER_SIZE];
- std::mutex lock_;
- int nextMessage_ = 0;
- int count_ = 0;
- int read_ = 0;
-};
+#include "Core/Debugger/WebSocket/GameBroadcaster.h"
+#include "Core/Debugger/WebSocket/LogBroadcaster.h"
+#include "Core/Debugger/WebSocket/SteppingBroadcaster.h"
-struct DebuggerLogEvent {
- std::string header;
- std::string message;
- int level;
- const char *channel;
+// TODO: Just for now, testing...
+static void WebSocketTestEvent(net::WebSocketServer *ws, const json_value *data) {
+ ws->Send(DebuggerErrorEvent("Test message", LogTypes::LNOTICE));
+}
- operator std::string() {
- JsonWriter j;
- j.begin();
- j.writeString("event", "log");
- j.writeString("header", header);
- j.writeString("message", message);
- j.writeInt("level", level);
- j.writeString("channel", channel);
- j.end();
- return j.str();
- }
-};
+typedef void (*DebuggerEventHandler)(net::WebSocketServer *ws, const json_value *data);
+static const std::unordered_map debuggerEvents({
+ {"test", &WebSocketTestEvent},
+});
void HandleDebuggerRequest(const http::Request &request) {
net::WebSocketServer *ws = net::WebSocketServer::CreateAsUpgrade(request, "debugger.ppsspp.org");
if (!ws)
return;
- DebuggerLogListener *logListener = new DebuggerLogListener();
- if (LogManager::GetInstance())
- LogManager::GetInstance()->AddListener(logListener);
+ LogBroadcaster logger;
+ GameBroadcaster game;
+ SteppingBroadcaster stepping;
- // TODO: Handle incoming messages.
ws->SetTextHandler([&](const std::string &t) {
- ws->Send(R"({"event":"error","message":"Bad message","level":2})");
+ JsonReader reader(t.c_str(), t.size());
+ if (!reader.ok()) {
+ ws->Send(DebuggerErrorEvent("Bad message: invalid JSON", LogTypes::LERROR));
+ return;
+ }
+
+ const json_value *root = reader.root();
+ const json_value *eventNode = root->get("event", JSON_STRING);
+ if (!eventNode) {
+ ws->Send(DebuggerErrorEvent("Bad message: no event property", LogTypes::LERROR));
+ return;
+ }
+
+ auto eventFunc = debuggerEvents.find(eventNode->string_value);
+ if (eventFunc != debuggerEvents.end()) {
+ eventFunc->second(ws, root);
+ } else {
+ ws->Send(DebuggerErrorEvent("Bad message: unknown event", LogTypes::LERROR));
+ }
});
ws->SetBinaryHandler([&](const std::vector &d) {
- ws->Send(R"({"event":"error","message":"Bad message","level":2})");
+ ws->Send(DebuggerErrorEvent("Bad message", LogTypes::LERROR));
});
- while (ws->Process(0.1f)) {
- auto messages = logListener->GetMessages();
- // TODO: Check for other conditions?
- for (auto msg : messages) {
- ws->Send(DebuggerLogEvent{msg.header, msg.msg, msg.level, msg.log});
- }
- continue;
+ while (ws->Process(1.0f / 60.0f)) {
+ // These send events that aren't just responses to requests.
+ logger.Broadcast(ws);
+ game.Broadcast(ws);
+ stepping.Broadcast(ws);
}
- if (LogManager::GetInstance())
- LogManager::GetInstance()->RemoveListener(logListener);
- delete logListener;
delete ws;
}
diff --git a/Core/Debugger/WebSocket/Common.h b/Core/Debugger/WebSocket/Common.h
new file mode 100644
index 000000000000..ed1213943858
--- /dev/null
+++ b/Core/Debugger/WebSocket/Common.h
@@ -0,0 +1,42 @@
+// 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 "ext/vjson/json.h"
+#include "json/json_writer.h"
+#include "net/websocket_server.h"
+#include "Common/Log.h"
+
+struct DebuggerErrorEvent {
+ DebuggerErrorEvent(const std::string m, LogTypes::LOG_LEVELS l) : message(m), level(l) {
+ }
+
+ std::string message;
+ LogTypes::LOG_LEVELS level;
+
+ operator std::string() {
+ JsonWriter j;
+ j.begin();
+ j.writeString("event", "error");
+ j.writeString("message", message);
+ j.writeInt("level", level);
+ j.end();
+ return j.str();
+ }
+};
diff --git a/Core/Debugger/WebSocket/GameBroadcaster.cpp b/Core/Debugger/WebSocket/GameBroadcaster.cpp
new file mode 100644
index 000000000000..86ee9df777e3
--- /dev/null
+++ b/Core/Debugger/WebSocket/GameBroadcaster.cpp
@@ -0,0 +1,38 @@
+// 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 "Core/Debugger/WebSocket/Common.h"
+#include "Core/Debugger/WebSocket/GameBroadcaster.h"
+#include "Core/System.h"
+
+void GameBroadcaster::Broadcast(net::WebSocketServer *ws) {
+ // TODO: This is ugly. Implement proper information instead.
+ // TODO: Should probably include info about which game, etc.
+ GlobalUIState state = GetUIState();
+ if (prevState_ != state) {
+ if (state == UISTATE_PAUSEMENU) {
+ ws->Send(R"({"event":"game_pause"})");
+ } else if (state == UISTATE_INGAME && prevState_ == UISTATE_PAUSEMENU) {
+ ws->Send(R"({"event":"game_resume"})");
+ } else if (state == UISTATE_INGAME) {
+ ws->Send(R"({"event":"game_start"})");
+ } else if (state == UISTATE_MENU) {
+ ws->Send(R"({"event":"game_quit"})");
+ }
+ prevState_ = state;
+ }
+}
diff --git a/Core/Debugger/WebSocket/GameBroadcaster.h b/Core/Debugger/WebSocket/GameBroadcaster.h
new file mode 100644
index 000000000000..63ef3eb346d9
--- /dev/null
+++ b/Core/Debugger/WebSocket/GameBroadcaster.h
@@ -0,0 +1,36 @@
+// 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 "Core/System.h"
+
+namespace net {
+class WebSocketServer;
+}
+
+struct GameBroadcaster {
+public:
+ GameBroadcaster() {
+ prevState_ = GetUIState();
+ }
+
+ void Broadcast(net::WebSocketServer *ws);
+
+private:
+ GlobalUIState prevState_;
+};
diff --git a/Core/Debugger/WebSocket/LogBroadcaster.cpp b/Core/Debugger/WebSocket/LogBroadcaster.cpp
new file mode 100644
index 000000000000..c7f034267ab0
--- /dev/null
+++ b/Core/Debugger/WebSocket/LogBroadcaster.cpp
@@ -0,0 +1,112 @@
+// 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
+#include "Common/LogManager.h"
+#include "Core/Debugger/WebSocket/Common.h"
+#include "Core/Debugger/WebSocket/LogBroadcaster.h"
+
+class DebuggerLogListener : public LogListener {
+public:
+ void Log(const LogMessage &msg) override {
+ std::lock_guard guard(lock_);
+ messages_[nextMessage_] = msg;
+ nextMessage_++;
+ if (nextMessage_ >= BUFFER_SIZE)
+ nextMessage_ -= BUFFER_SIZE;
+ count_++;
+ }
+
+ std::vector GetMessages() {
+ std::lock_guard guard(lock_);
+ int splitPoint;
+ int readCount;
+ if (read_ + BUFFER_SIZE < count_) {
+ // We'll start with our oldest then.
+ splitPoint = nextMessage_;
+ readCount = Count();
+ } else {
+ splitPoint = read_;
+ readCount = count_ - read_;
+ }
+
+ read_ = count_;
+
+ std::vector results;
+ int splitEnd = std::min(splitPoint + readCount, (int)BUFFER_SIZE);
+ for (int i = splitPoint; i < splitEnd; ++i) {
+ results.push_back(messages_[i]);
+ readCount--;
+ }
+ for (int i = 0; i < readCount; ++i) {
+ results.push_back(messages_[i]);
+ }
+
+ return results;
+ }
+
+ int Count() const {
+ return count_ < BUFFER_SIZE ? count_ : BUFFER_SIZE;
+ }
+
+private:
+ enum { BUFFER_SIZE = 1024 };
+ LogMessage messages_[BUFFER_SIZE];
+ std::mutex lock_;
+ int nextMessage_ = 0;
+ int count_ = 0;
+ int read_ = 0;
+};
+
+LogBroadcaster::LogBroadcaster() {
+ listener_ = new DebuggerLogListener();
+ if (LogManager::GetInstance())
+ LogManager::GetInstance()->AddListener(listener_);
+}
+
+LogBroadcaster::~LogBroadcaster() {
+if (LogManager::GetInstance())
+ LogManager::GetInstance()->RemoveListener(listener_);
+ delete listener_;
+}
+
+struct DebuggerLogEvent {
+ std::string header;
+ std::string message;
+ int level;
+ const char *channel;
+
+ operator std::string() {
+ JsonWriter j;
+ j.begin();
+ j.writeString("event", "log");
+ j.writeString("header", header);
+ j.writeString("message", message);
+ j.writeInt("level", level);
+ j.writeString("channel", channel);
+ j.end();
+ return j.str();
+ }
+};
+
+void LogBroadcaster::Broadcast(net::WebSocketServer *ws) {
+ auto messages = listener_->GetMessages();
+ for (auto msg : messages) {
+ ws->Send(DebuggerLogEvent{msg.header, msg.msg, msg.level, msg.log});
+ }
+}
diff --git a/Core/Debugger/WebSocket/LogBroadcaster.h b/Core/Debugger/WebSocket/LogBroadcaster.h
new file mode 100644
index 000000000000..13f26d4a066f
--- /dev/null
+++ b/Core/Debugger/WebSocket/LogBroadcaster.h
@@ -0,0 +1,35 @@
+// 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
+
+namespace net {
+class WebSocketServer;
+}
+
+class DebuggerLogListener;
+
+struct LogBroadcaster {
+public:
+ LogBroadcaster();
+ ~LogBroadcaster();
+
+ void Broadcast(net::WebSocketServer *ws);
+
+private:
+ DebuggerLogListener *listener_;
+};
diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.cpp b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp
new file mode 100644
index 000000000000..60405b4c485d
--- /dev/null
+++ b/Core/Debugger/WebSocket/SteppingBroadcaster.cpp
@@ -0,0 +1,32 @@
+// 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 "Core/Core.h"
+#include "Core/Debugger/WebSocket/Common.h"
+#include "Core/Debugger/WebSocket/SteppingBroadcaster.h"
+#include "Core/System.h"
+
+void SteppingBroadcaster::Broadcast(net::WebSocketServer *ws) {
+ // TODO: This is somewhat primitive. It'd be nice to register a callback with Core instead?
+ if (coreState != prevState_) {
+ if (Core_IsStepping() && PSP_IsInited()) {
+ // TODO: Should send more data proactively.
+ ws->Send(R"({"event":"cpu_stepping"})");
+ }
+ prevState_ = coreState;
+ }
+}
diff --git a/Core/Debugger/WebSocket/SteppingBroadcaster.h b/Core/Debugger/WebSocket/SteppingBroadcaster.h
new file mode 100644
index 000000000000..df196d9ff8ae
--- /dev/null
+++ b/Core/Debugger/WebSocket/SteppingBroadcaster.h
@@ -0,0 +1,36 @@
+// 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 "Core/Core.h"
+
+namespace net {
+class WebSocketServer;
+}
+
+struct SteppingBroadcaster {
+public:
+ SteppingBroadcaster() {
+ prevState_ = coreState;
+ }
+
+ void Broadcast(net::WebSocketServer *ws);
+
+private:
+ CoreState prevState_;
+};
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 7e2d18ba935d..7f3227f9eb14 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -299,6 +299,9 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/Debugger/Breakpoints.cpp \
$(SRC)/Core/Debugger/SymbolMap.cpp \
$(SRC)/Core/Debugger/WebSocket.cpp \
+ $(SRC)/Core/Debugger/WebSocket/GameBroadcaster.cpp \
+ $(SRC)/Core/Debugger/WebSocket/LogBroadcaster.cpp \
+ $(SRC)/Core/Debugger/WebSocket/SteppingBroadcaster.cpp \
$(SRC)/Core/Dialog/PSPDialog.cpp \
$(SRC)/Core/Dialog/PSPGamedataInstallDialog.cpp \
$(SRC)/Core/Dialog/PSPMsgDialog.cpp \