Debugger stuff. Lots of wasted work :/
This commit is contained in:
@@ -58,6 +58,15 @@ std::unique_ptr<Application> Application::Create() {
|
||||
}
|
||||
|
||||
bool Application::Initialize() {
|
||||
// Bind the object model to the client so it'll maintain state.
|
||||
system_ = std::make_unique<model::System>(loop(), &client_);
|
||||
client_.set_listener(system_.get());
|
||||
|
||||
// TODO(benvanik): flags and such.
|
||||
if (!client_.Connect("localhost", 9002)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
main_window_ = std::make_unique<MainWindow>(this);
|
||||
if (!main_window_->Initialize()) {
|
||||
XELOGE("Unable to initialize main window");
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/debug/client/xdp/xdp_client.h"
|
||||
#include "xenia/debug/ui/model/system.h"
|
||||
#include "xenia/ui/loop.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -29,6 +31,8 @@ class Application {
|
||||
|
||||
xe::ui::Loop* loop() { return loop_.get(); }
|
||||
MainWindow* main_window() const { return main_window_.get(); }
|
||||
client::xdp::XdpClient* client() { return &client_; }
|
||||
model::System* system() const { return system_.get(); }
|
||||
|
||||
void Quit();
|
||||
|
||||
@@ -39,6 +43,9 @@ class Application {
|
||||
|
||||
std::unique_ptr<xe::ui::Loop> loop_;
|
||||
std::unique_ptr<MainWindow> main_window_;
|
||||
client::xdp::XdpClient client_;
|
||||
|
||||
std::unique_ptr<model::System> system_;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
|
||||
using namespace xe::debug::client::xdp;
|
||||
|
||||
using xe::ui::MenuItem;
|
||||
|
||||
const std::wstring kBaseTitle = L"xenia debugger";
|
||||
@@ -30,6 +32,8 @@ MainWindow::MainWindow(Application* app) : app_(app) {}
|
||||
MainWindow::~MainWindow() = default;
|
||||
|
||||
bool MainWindow::Initialize() {
|
||||
client_ = app_->client();
|
||||
|
||||
platform_window_ = xe::ui::Window::Create(app()->loop(), kBaseTitle);
|
||||
if (!platform_window_) {
|
||||
return false;
|
||||
@@ -106,11 +110,14 @@ void MainWindow::BuildUI() {
|
||||
.gravity(Gravity::kAll)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.axis(Axis::kY)
|
||||
.child(LayoutBoxNode()
|
||||
.id("toolbar_box")
|
||||
.gravity(Gravity::kTop | Gravity::kLeftRight)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.child(LabelNode("toolbar")))
|
||||
.child(
|
||||
LayoutBoxNode()
|
||||
.id("toolbar_box")
|
||||
.gravity(Gravity::kTop | Gravity::kLeftRight)
|
||||
.distribution(LayoutDistribution::kGravity)
|
||||
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||
.child(ButtonNode("Pause").id("pause_button"))
|
||||
.child(ButtonNode("Continue").id("resume_button")))
|
||||
.child(
|
||||
SplitContainerNode()
|
||||
.id("split_container")
|
||||
@@ -132,15 +139,50 @@ void MainWindow::BuildUI() {
|
||||
{TBIDC("tab_container"), &ui_.tab_container},
|
||||
});
|
||||
|
||||
handler_ = std::make_unique<el::EventHandler>(window_.get());
|
||||
handler_->Listen(el::EventType::kClick, TBIDC("pause_button"),
|
||||
[this](const el::Event& ev) {
|
||||
client_->Interrupt();
|
||||
return true;
|
||||
});
|
||||
handler_->Listen(el::EventType::kClick, TBIDC("resume_button"),
|
||||
[this](const el::Event& ev) {
|
||||
client_->Continue();
|
||||
return true;
|
||||
});
|
||||
|
||||
system()->on_execution_state_changed.AddListener(
|
||||
[this]() { UpdateElementState(); });
|
||||
|
||||
ui_.tab_container->tab_bar()->LoadNodeTree(ButtonNode(cpu_view_.name()));
|
||||
ui_.tab_container->content_root()->AddChild(cpu_view_.BuildUI());
|
||||
cpu_view_.Setup(Application::current()->client());
|
||||
|
||||
ui_.tab_container->tab_bar()->LoadNodeTree(ButtonNode(gpu_view_.name()));
|
||||
ui_.tab_container->content_root()->AddChild(gpu_view_.BuildUI());
|
||||
gpu_view_.Setup(Application::current()->client());
|
||||
|
||||
UpdateElementState();
|
||||
|
||||
el::util::ShowDebugInfoSettingsWindow(root_element);
|
||||
}
|
||||
|
||||
void MainWindow::UpdateElementState() {
|
||||
bool is_running = client_->execution_state() == ExecutionState::kRunning;
|
||||
|
||||
el::TabContainer* tab_container;
|
||||
el::Button* pause_button;
|
||||
el::Button* resume_button;
|
||||
window_->GetElementsById({
|
||||
{TBIDC("tab_container"), &tab_container},
|
||||
{TBIDC("pause_button"), &pause_button},
|
||||
{TBIDC("resume_button"), &resume_button},
|
||||
});
|
||||
tab_container->set_enabled(!is_running);
|
||||
pause_button->set_enabled(is_running);
|
||||
resume_button->set_enabled(!is_running);
|
||||
}
|
||||
|
||||
void MainWindow::OnClose() { app_->Quit(); }
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -28,6 +28,8 @@ class MainWindow {
|
||||
~MainWindow();
|
||||
|
||||
Application* app() const { return app_; }
|
||||
xe::ui::Loop* loop() const { return app_->loop(); }
|
||||
model::System* system() const { return app_->system(); }
|
||||
|
||||
bool Initialize();
|
||||
|
||||
@@ -35,10 +37,12 @@ class MainWindow {
|
||||
|
||||
private:
|
||||
void BuildUI();
|
||||
void UpdateElementState();
|
||||
|
||||
void OnClose();
|
||||
|
||||
Application* app_ = nullptr;
|
||||
xe::debug::client::xdp::XdpClient* client_ = nullptr;
|
||||
|
||||
std::unique_ptr<xe::ui::Window> platform_window_;
|
||||
std::unique_ptr<el::Window> window_;
|
||||
@@ -47,6 +51,7 @@ class MainWindow {
|
||||
el::LayoutBox* toolbar_box;
|
||||
el::TabContainer* tab_container;
|
||||
} ui_ = {0};
|
||||
std::unique_ptr<el::EventHandler> handler_;
|
||||
views::cpu::CpuView cpu_view_;
|
||||
views::gpu::GpuView gpu_view_;
|
||||
};
|
||||
|
||||
22
src/xenia/debug/ui/model/function.cc
Normal file
22
src/xenia/debug/ui/model/function.cc
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/debug/ui/model/function.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
//
|
||||
|
||||
} // namespace model
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
29
src/xenia/debug/ui/model/function.h
Normal file
29
src/xenia/debug/ui/model/function.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_UI_MODEL_FUNCTION_H_
|
||||
#define XENIA_DEBUG_UI_MODEL_FUNCTION_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
class Function {
|
||||
public:
|
||||
};
|
||||
|
||||
} // namespace model
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_UI_MODEL_FUNCTION_H_
|
||||
32
src/xenia/debug/ui/model/module.cc
Normal file
32
src/xenia/debug/ui/model/module.cc
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/debug/ui/model/module.h"
|
||||
|
||||
#include "xenia/debug/ui/model/system.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
void Module::Update(const proto::ModuleListEntry* entry) {
|
||||
if (!entry_.module_handle) {
|
||||
std::memcpy(&entry_, entry, sizeof(entry_));
|
||||
} else {
|
||||
std::memcpy(&temp_entry_, entry, sizeof(temp_entry_));
|
||||
system_->loop()->Post(
|
||||
[this]() { std::memcpy(&entry_, &temp_entry_, sizeof(temp_entry_)); });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace model
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
51
src/xenia/debug/ui/model/module.h
Normal file
51
src/xenia/debug/ui/model/module.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_UI_MODEL_MODULE_H_
|
||||
#define XENIA_DEBUG_UI_MODEL_MODULE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/debug/proto/xdp_protocol.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
class System;
|
||||
|
||||
class Module {
|
||||
public:
|
||||
Module(System* system) : system_(system) {}
|
||||
|
||||
bool is_dead() const { return is_dead_; }
|
||||
void set_dead(bool is_dead) { is_dead_ = is_dead; }
|
||||
|
||||
uint32_t module_handle() const { return entry_.module_handle; }
|
||||
bool is_kernel_module() const { return entry_.is_kernel_module; }
|
||||
std::string name() const { return entry_.name; }
|
||||
const proto::ModuleListEntry* entry() const { return &entry_; }
|
||||
|
||||
void Update(const proto::ModuleListEntry* entry);
|
||||
|
||||
private:
|
||||
System* system_ = nullptr;
|
||||
bool is_dead_ = false;
|
||||
proto::ModuleListEntry entry_ = {0};
|
||||
proto::ModuleListEntry temp_entry_ = {0};
|
||||
};
|
||||
|
||||
} // namespace model
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_UI_MODEL_MODULE_H_
|
||||
127
src/xenia/debug/ui/model/system.cc
Normal file
127
src/xenia/debug/ui/model/system.cc
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/debug/ui/model/system.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
using namespace xe::debug::proto;
|
||||
|
||||
System::System(xe::ui::Loop* loop, client::xdp::XdpClient* client)
|
||||
: loop_(loop), client_(client) {}
|
||||
|
||||
ExecutionState System::execution_state() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
return client_->execution_state();
|
||||
}
|
||||
|
||||
std::vector<Module*> System::modules() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
std::vector<Module*> result;
|
||||
for (auto& module : modules_) {
|
||||
result.push_back(module.get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Thread*> System::threads() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
std::vector<Thread*> result;
|
||||
for (auto& thread : threads_) {
|
||||
result.push_back(thread.get());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Module* System::GetModuleByHandle(uint32_t module_handle) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
auto it = modules_by_handle_.find(module_handle);
|
||||
return it != modules_by_handle_.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
Thread* System::GetThreadByHandle(uint32_t thread_handle) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
auto it = threads_by_handle_.find(thread_handle);
|
||||
return it != threads_by_handle_.end() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
void System::OnExecutionStateChanged(ExecutionState execution_state) {
|
||||
loop_->Post([this]() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
on_execution_state_changed();
|
||||
});
|
||||
}
|
||||
|
||||
void System::OnModulesUpdated(std::vector<const ModuleListEntry*> entries) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
std::unordered_set<uint32_t> extra_modules;
|
||||
for (size_t i = 0; i < modules_.size(); ++i) {
|
||||
extra_modules.emplace(modules_[i]->module_handle());
|
||||
}
|
||||
for (auto entry : entries) {
|
||||
auto existing_module = modules_by_handle_.find(entry->module_handle);
|
||||
if (existing_module == modules_by_handle_.end()) {
|
||||
auto module = std::make_unique<Module>(this);
|
||||
module->Update(entry);
|
||||
modules_by_handle_.emplace(entry->module_handle, module.get());
|
||||
modules_.emplace_back(std::move(module));
|
||||
} else {
|
||||
existing_module->second->Update(entry);
|
||||
extra_modules.erase(existing_module->first);
|
||||
}
|
||||
}
|
||||
for (auto module_handle : extra_modules) {
|
||||
modules_by_handle_[module_handle]->set_dead(true);
|
||||
}
|
||||
}
|
||||
loop_->Post([this]() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
on_modules_updated();
|
||||
});
|
||||
}
|
||||
|
||||
void System::OnThreadsUpdated(std::vector<const ThreadListEntry*> entries) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
std::unordered_set<uint32_t> extra_threads;
|
||||
for (size_t i = 0; i < threads_.size(); ++i) {
|
||||
extra_threads.emplace(threads_[i]->thread_handle());
|
||||
}
|
||||
for (auto entry : entries) {
|
||||
auto existing_thread = threads_by_handle_.find(entry->thread_handle);
|
||||
if (existing_thread == threads_by_handle_.end()) {
|
||||
auto thread = std::make_unique<Thread>(this);
|
||||
thread->Update(entry);
|
||||
threads_by_handle_.emplace(entry->thread_handle, thread.get());
|
||||
threads_.emplace_back(std::move(thread));
|
||||
} else {
|
||||
existing_thread->second->Update(entry);
|
||||
extra_threads.erase(existing_thread->first);
|
||||
}
|
||||
}
|
||||
for (auto thread_handle : extra_threads) {
|
||||
modules_by_handle_[thread_handle]->set_dead(true);
|
||||
}
|
||||
}
|
||||
loop_->Post([this]() {
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
on_threads_updated();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace model
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
74
src/xenia/debug/ui/model/system.h
Normal file
74
src/xenia/debug/ui/model/system.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_UI_MODEL_SYSTEM_H_
|
||||
#define XENIA_DEBUG_UI_MODEL_SYSTEM_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/delegate.h"
|
||||
#include "xenia/debug/client/xdp/xdp_client.h"
|
||||
#include "xenia/debug/ui/model/function.h"
|
||||
#include "xenia/debug/ui/model/module.h"
|
||||
#include "xenia/debug/ui/model/thread.h"
|
||||
#include "xenia/ui/loop.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
using xe::debug::client::xdp::ExecutionState;
|
||||
|
||||
class System : public client::xdp::ClientListener {
|
||||
public:
|
||||
System(xe::ui::Loop* loop, client::xdp::XdpClient* client);
|
||||
|
||||
xe::ui::Loop* loop() const { return loop_; }
|
||||
client::xdp::XdpClient* client() const { return client_; }
|
||||
|
||||
ExecutionState execution_state();
|
||||
|
||||
std::vector<Module*> modules();
|
||||
std::vector<Thread*> threads();
|
||||
|
||||
Module* GetModuleByHandle(uint32_t module_handle);
|
||||
Thread* GetThreadByHandle(uint32_t thread_handle);
|
||||
|
||||
Delegate<void> on_execution_state_changed;
|
||||
Delegate<void> on_modules_updated;
|
||||
Delegate<void> on_threads_updated;
|
||||
|
||||
private:
|
||||
void OnExecutionStateChanged(ExecutionState execution_state) override;
|
||||
void OnModulesUpdated(
|
||||
std::vector<const proto::ModuleListEntry*> entries) override;
|
||||
void OnThreadsUpdated(
|
||||
std::vector<const proto::ThreadListEntry*> entries) override;
|
||||
|
||||
xe::ui::Loop* loop_ = nullptr;
|
||||
client::xdp::XdpClient* client_ = nullptr;
|
||||
|
||||
std::recursive_mutex mutex_;
|
||||
std::vector<std::unique_ptr<Module>> modules_;
|
||||
std::unordered_map<uint32_t, Module*> modules_by_handle_;
|
||||
std::vector<std::unique_ptr<Thread>> threads_;
|
||||
std::unordered_map<uint32_t, Thread*> threads_by_handle_;
|
||||
};
|
||||
|
||||
} // namespace model
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_UI_MODEL_SYSTEM_H_
|
||||
40
src/xenia/debug/ui/model/thread.cc
Normal file
40
src/xenia/debug/ui/model/thread.cc
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/debug/ui/model/thread.h"
|
||||
|
||||
#include "xenia/debug/ui/model/system.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
std::string Thread::to_string() {
|
||||
std::string value = entry_.name;
|
||||
if (is_host_thread()) {
|
||||
value += " (host)";
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void Thread::Update(const proto::ThreadListEntry* entry) {
|
||||
if (!entry_.thread_handle) {
|
||||
std::memcpy(&entry_, entry, sizeof(entry_));
|
||||
} else {
|
||||
std::memcpy(&temp_entry_, entry, sizeof(temp_entry_));
|
||||
system_->loop()->Post(
|
||||
[this]() { std::memcpy(&entry_, &temp_entry_, sizeof(temp_entry_)); });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace model
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
54
src/xenia/debug/ui/model/thread.h
Normal file
54
src/xenia/debug/ui/model/thread.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_UI_MODEL_THREAD_H_
|
||||
#define XENIA_DEBUG_UI_MODEL_THREAD_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/debug/proto/xdp_protocol.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace model {
|
||||
|
||||
class System;
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
Thread(System* system) : system_(system) {}
|
||||
|
||||
bool is_dead() const { return is_dead_; }
|
||||
void set_dead(bool is_dead) { is_dead_ = is_dead; }
|
||||
|
||||
uint32_t thread_handle() const { return entry_.thread_handle; }
|
||||
uint32_t thread_id() const { return entry_.thread_id; }
|
||||
bool is_host_thread() const { return entry_.is_host_thread; }
|
||||
std::string name() const { return entry_.name; }
|
||||
const proto::ThreadListEntry* entry() const { return &entry_; }
|
||||
|
||||
std::string to_string();
|
||||
|
||||
void Update(const proto::ThreadListEntry* entry);
|
||||
|
||||
private:
|
||||
System* system_ = nullptr;
|
||||
bool is_dead_ = false;
|
||||
proto::ThreadListEntry entry_ = {0};
|
||||
proto::ThreadListEntry temp_entry_ = {0};
|
||||
};
|
||||
|
||||
} // namespace model
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_UI_MODEL_THREAD_H_
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <string>
|
||||
|
||||
#include "el/elements.h"
|
||||
#include "el/event_handler.h"
|
||||
#include "xenia/debug/ui/application.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
@@ -25,14 +27,21 @@ class View {
|
||||
|
||||
std::string name() const { return name_; }
|
||||
el::LayoutBox* root_element() { return &root_element_; }
|
||||
xe::ui::Loop* loop() const { return Application::current()->loop(); }
|
||||
client::xdp::XdpClient* client() const { return client_; }
|
||||
model::System* system() const { return Application::current()->system(); }
|
||||
|
||||
virtual el::Element* BuildUI() = 0;
|
||||
|
||||
virtual void Setup(xe::debug::client::xdp::XdpClient* client) = 0;
|
||||
|
||||
protected:
|
||||
View(std::string name) : name_(name) {}
|
||||
|
||||
std::string name_;
|
||||
el::LayoutBox root_element_;
|
||||
std::unique_ptr<el::EventHandler> handler_;
|
||||
xe::debug::client::xdp::XdpClient* client_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace ui {
|
||||
namespace views {
|
||||
namespace cpu {
|
||||
|
||||
using namespace xe::debug::client::xdp;
|
||||
|
||||
CpuView::CpuView() : View("CPU") {}
|
||||
|
||||
CpuView::~CpuView() = default;
|
||||
@@ -29,22 +31,13 @@ el::Element* CpuView::BuildUI() {
|
||||
.gravity(Gravity::kAll)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.axis(Axis::kY)
|
||||
.child(
|
||||
LayoutBoxNode()
|
||||
.gravity(Gravity::kTop | Gravity::kLeftRight)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.axis(Axis::kX)
|
||||
.skin("button_group")
|
||||
.child(ButtonNode("?"))
|
||||
.child(
|
||||
DropDownButtonNode().item("Module").item("Module").item(
|
||||
"Module")))
|
||||
.child(ListBoxNode()
|
||||
.gravity(Gravity::kAll)
|
||||
.item("fn")
|
||||
.item("fn")
|
||||
.item("fn")
|
||||
.item("fn"))
|
||||
.child(LayoutBoxNode()
|
||||
.gravity(Gravity::kTop | Gravity::kLeftRight)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.axis(Axis::kX)
|
||||
.skin("button_group")
|
||||
.child(DropDownButtonNode().id("module_dropdown")))
|
||||
.child(ListBoxNode().id("function_listbox").gravity(Gravity::kAll))
|
||||
.child(LayoutBoxNode()
|
||||
.gravity(Gravity::kBottom | Gravity::kLeftRight)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
@@ -102,10 +95,9 @@ el::Element* CpuView::BuildUI() {
|
||||
.distribution(LayoutDistribution::kGravity)
|
||||
.distribution_position(LayoutDistributionPosition::kLeftTop)
|
||||
.axis(Axis::kX)
|
||||
.child(ButtonNode("button"))
|
||||
.child(ButtonNode("button"))
|
||||
.child(ButtonNode("button")))
|
||||
.child(DropDownButtonNode().id("thread_dropdown")))
|
||||
.child(LayoutBoxNode()
|
||||
.id("source_content")
|
||||
.gravity(Gravity::kAll)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.child(SplitContainerNode()
|
||||
@@ -144,9 +136,91 @@ el::Element* CpuView::BuildUI() {
|
||||
root_element_.GetElementsById({
|
||||
//
|
||||
});
|
||||
handler_ = std::make_unique<el::EventHandler>(&root_element_);
|
||||
|
||||
handler_->Listen(el::EventType::kChanged, TBIDC("module_dropdown"),
|
||||
[this](const el::Event& ev) {
|
||||
UpdateFunctionList();
|
||||
return true;
|
||||
});
|
||||
|
||||
return &root_element_;
|
||||
}
|
||||
|
||||
void CpuView::Setup(XdpClient* client) {
|
||||
client_ = client;
|
||||
|
||||
system()->on_execution_state_changed.AddListener(
|
||||
[this]() { UpdateElementState(); });
|
||||
system()->on_modules_updated.AddListener([this]() { UpdateModuleList(); });
|
||||
system()->on_threads_updated.AddListener([this]() { UpdateThreadList(); });
|
||||
}
|
||||
|
||||
void CpuView::UpdateElementState() {
|
||||
root_element_.GetElementsById({
|
||||
//
|
||||
});
|
||||
}
|
||||
|
||||
void CpuView::UpdateModuleList() {
|
||||
el::DropDownButton* module_dropdown;
|
||||
root_element_.GetElementsById({
|
||||
{TBIDC("module_dropdown"), &module_dropdown},
|
||||
});
|
||||
auto module_items = module_dropdown->default_source();
|
||||
auto modules = system()->modules();
|
||||
bool is_first = module_items->size() == 0;
|
||||
for (size_t i = module_items->size(); i < modules.size(); ++i) {
|
||||
auto module = modules[i];
|
||||
auto item = std::make_unique<el::GenericStringItem>(module->name());
|
||||
item->id = module->module_handle();
|
||||
module_items->push_back(std::move(item));
|
||||
}
|
||||
if (is_first) {
|
||||
module_dropdown->set_value(int(module_items->size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
void CpuView::UpdateFunctionList() {
|
||||
el::DropDownButton* module_dropdown;
|
||||
el::ListBox* function_listbox;
|
||||
root_element_.GetElementsById({
|
||||
{TBIDC("module_dropdown"), &module_dropdown},
|
||||
{TBIDC("function_listbox"), &function_listbox},
|
||||
});
|
||||
auto module_handle = module_dropdown->selected_item_id();
|
||||
auto module = system()->GetModuleByHandle(module_handle);
|
||||
if (!module) {
|
||||
function_listbox->default_source()->clear();
|
||||
return;
|
||||
}
|
||||
// TODO(benvanik): fetch list.
|
||||
}
|
||||
|
||||
void CpuView::UpdateThreadList() {
|
||||
el::DropDownButton* thread_dropdown;
|
||||
root_element_.GetElementsById({
|
||||
{TBIDC("thread_dropdown"), &thread_dropdown},
|
||||
});
|
||||
auto thread_items = thread_dropdown->default_source();
|
||||
auto threads = system()->threads();
|
||||
bool is_first = thread_items->size() == 0;
|
||||
for (size_t i = 0; i < thread_items->size(); ++i) {
|
||||
auto thread = threads[i];
|
||||
auto item = thread_items->at(i);
|
||||
item->str = thread->to_string();
|
||||
}
|
||||
for (size_t i = thread_items->size(); i < threads.size(); ++i) {
|
||||
auto thread = threads[i];
|
||||
auto item = std::make_unique<el::GenericStringItem>(thread->to_string());
|
||||
item->id = thread->thread_handle();
|
||||
thread_items->push_back(std::move(item));
|
||||
}
|
||||
if (is_first) {
|
||||
thread_dropdown->set_value(int(thread_items->size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace views
|
||||
} // namespace ui
|
||||
|
||||
@@ -28,7 +28,13 @@ class CpuView : public View {
|
||||
|
||||
el::Element* BuildUI() override;
|
||||
|
||||
void Setup(xe::debug::client::xdp::XdpClient* client) override;
|
||||
|
||||
protected:
|
||||
void UpdateElementState();
|
||||
void UpdateModuleList();
|
||||
void UpdateFunctionList();
|
||||
void UpdateThreadList();
|
||||
};
|
||||
|
||||
} // namespace cpu
|
||||
|
||||
@@ -32,9 +32,14 @@ el::Element* GpuView::BuildUI() {
|
||||
root_element_.GetElementsById({
|
||||
//
|
||||
});
|
||||
handler_ = std::make_unique<el::EventHandler>(&root_element_);
|
||||
return &root_element_;
|
||||
}
|
||||
|
||||
void GpuView::Setup(xe::debug::client::xdp::XdpClient* client) {
|
||||
//
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace views
|
||||
} // namespace ui
|
||||
|
||||
@@ -28,6 +28,8 @@ class GpuView : public View {
|
||||
|
||||
el::Element* BuildUI() override;
|
||||
|
||||
void Setup(xe::debug::client::xdp::XdpClient* client) override;
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user