Debugger: Add a Network widget

Display socket table, SSL context and options
This commit is contained in:
Sepalani 2020-04-19 23:30:50 +04:00
parent 88ae4c7914
commit 5e33cd48da
14 changed files with 406 additions and 20 deletions

View File

@ -23,7 +23,7 @@
namespace IOS::HLE::Device
{
WII_SSL NetSSL::_SSL[NET_SSL_MAXINSTANCES];
WII_SSL NetSSL::_SSL[IOS::HLE::NET_SSL_MAXINSTANCES];
static constexpr mbedtls_x509_crt_profile mbedtls_x509_crt_profile_wii = {
/* Hashes from SHA-1 and above */
@ -247,7 +247,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
case IOCTLV_NET_SSL_SHUTDOWN:
{
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WII_SSL* ssl = &_SSL[sslID];
@ -292,7 +292,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
BufferOutSize, BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WII_SSL* ssl = &_SSL[sslID];
int ret =
@ -333,7 +333,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
BufferOutSize, BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WII_SSL* ssl = &_SSL[sslID];
const std::string cert_base_path = File::GetUserPath(D_SESSION_WIIROOT_IDX);
@ -380,7 +380,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
BufferOutSize, BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WII_SSL* ssl = &_SSL[sslID];
mbedtls_x509_crt_free(&ssl->clicert);
@ -399,7 +399,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
case IOCTLV_NET_SSL_SETBUILTINROOTCA:
{
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WII_SSL* ssl = &_SSL[sslID];
const std::string cert_base_path = File::GetUserPath(D_SESSION_WIIROOT_IDX);
@ -437,7 +437,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
case IOCTLV_NET_SSL_CONNECT:
{
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WII_SSL* ssl = &_SSL[sslID];
mbedtls_ssl_setup(&ssl->ctx, &ssl->config);
@ -464,7 +464,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
case IOCTLV_NET_SSL_DOHANDSHAKE:
{
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WiiSockMan& sm = WiiSockMan::GetInstance();
sm.DoSock(_SSL[sslID].sockfd, request, IOCTLV_NET_SSL_DOHANDSHAKE);
@ -479,7 +479,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
case IOCTLV_NET_SSL_WRITE:
{
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WiiSockMan& sm = WiiSockMan::GetInstance();
sm.DoSock(_SSL[sslID].sockfd, request, IOCTLV_NET_SSL_WRITE);
@ -503,7 +503,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
{
int ret = 0;
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WiiSockMan& sm = WiiSockMan::GetInstance();
sm.DoSock(_SSL[sslID].sockfd, request, IOCTLV_NET_SSL_READ);
@ -526,7 +526,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
case IOCTLV_NET_SSL_SETROOTCADEFAULT:
{
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WriteReturnValue(SSL_OK, BufferIn);
}
@ -554,7 +554,7 @@ IPCCommandResult NetSSL::IOCtlV(const IOCtlVRequest& request)
BufferOutSize, BufferOut2, BufferOutSize2, BufferOut3, BufferOutSize3);
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IsSSLIDValid(sslID))
{
WriteReturnValue(SSL_OK, BufferIn);
}

View File

@ -24,11 +24,7 @@
namespace IOS::HLE
{
#define NET_SSL_MAXINSTANCES 4
// TODO: remove this macro.
#define SSLID_VALID(x) \
(x >= 0 && x < NET_SSL_MAXINSTANCES && ::IOS::HLE::Device::NetSSL::_SSL[x].active)
constexpr int NET_SSL_MAXINSTANCES = 4;
enum ssl_err_t : s32
{
@ -103,5 +99,10 @@ public:
private:
bool m_cert_error_shown = false;
};
constexpr bool IsSSLIDValid(int id)
{
return (id >= 0 && id < NET_SSL_MAXINSTANCES && IOS::HLE::Device::NetSSL::_SSL[id].active);
}
} // namespace Device
} // namespace IOS::HLE

View File

@ -33,8 +33,6 @@
namespace IOS::HLE
{
constexpr int WII_SOCKET_FD_MAX = 24;
char* WiiSockMan::DecodeError(s32 ErrorCode)
{
#ifdef _WIN32
@ -325,7 +323,7 @@ void WiiSocket::Update(bool read, bool write, bool except)
if (it->is_ssl)
{
int sslID = Memory::Read_U32(BufferOut) - 1;
if (SSLID_VALID(sslID))
if (IOS::HLE::Device::IsSSLIDValid(sslID))
{
switch (it->ssl_type)
{

View File

@ -57,6 +57,8 @@ typedef struct pollfd pollfd_t;
namespace IOS::HLE
{
constexpr int WII_SOCKET_FD_MAX = 24;
enum
{
SO_MSG_OOB = 0x01,

View File

@ -176,6 +176,8 @@ add_executable(dolphin-emu
Debugger/MemoryViewWidget.h
Debugger/MemoryWidget.cpp
Debugger/MemoryWidget.h
Debugger/NetworkWidget.cpp
Debugger/NetworkWidget.h
Debugger/NewBreakpointDialog.cpp
Debugger/NewBreakpointDialog.h
Debugger/PatchInstructionDialog.cpp

View File

@ -0,0 +1,301 @@
// Copyright 2020 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DolphinQt/Debugger/NetworkWidget.h"
#include <QCheckBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QHeaderView>
#include <QTableWidget>
#include <QVBoxLayout>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <sys/types.h>
#endif
#include "Core/ConfigManager.h"
#include "Core/IOS/Network/SSL.h"
#include "Core/IOS/Network/Socket.h"
#include "DolphinQt/Host.h"
#include "DolphinQt/Settings.h"
namespace
{
QTableWidgetItem* GetSocketDomain(s32 host_fd)
{
if (host_fd < 0)
return new QTableWidgetItem();
sockaddr sa;
socklen_t sa_len = sizeof(sa);
const int ret = getsockname(host_fd, &sa, &sa_len);
if (ret != 0)
return new QTableWidgetItem(QTableWidget::tr("Unknown"));
switch (sa.sa_family)
{
case 2:
return new QTableWidgetItem(QLatin1Literal("AF_INET"));
case 23:
return new QTableWidgetItem(QLatin1Literal("AF_INET6"));
default:
return new QTableWidgetItem(QString::number(sa.sa_family));
}
}
QTableWidgetItem* GetSocketType(s32 host_fd)
{
if (host_fd < 0)
return new QTableWidgetItem();
int so_type;
socklen_t opt_len = sizeof(so_type);
const int ret =
getsockopt(host_fd, SOL_SOCKET, SO_TYPE, reinterpret_cast<char*>(&so_type), &opt_len);
if (ret != 0)
return new QTableWidgetItem(QTableWidget::tr("Unknown"));
switch (so_type)
{
case 1:
return new QTableWidgetItem(QLatin1Literal("SOCK_STREAM"));
case 2:
return new QTableWidgetItem(QLatin1Literal("SOCK_DGRAM"));
default:
return new QTableWidgetItem(QString::number(so_type));
}
}
QTableWidgetItem* GetSocketState(s32 host_fd)
{
if (host_fd < 0)
return new QTableWidgetItem();
sockaddr_in peer_addr;
socklen_t peer_addr_len = sizeof(sockaddr_in);
if (getpeername(host_fd, reinterpret_cast<sockaddr*>(&peer_addr), &peer_addr_len) == 0)
return new QTableWidgetItem(QTableWidget::tr("Connected"));
int so_accept = 0;
socklen_t opt_len = sizeof(so_accept);
const int ret =
getsockopt(host_fd, SOL_SOCKET, SO_ACCEPTCONN, reinterpret_cast<char*>(&so_accept), &opt_len);
if (ret == 0 && so_accept > 0)
return new QTableWidgetItem(QTableWidget::tr("Listening"));
return new QTableWidgetItem(QTableWidget::tr("Unbound"));
}
QTableWidgetItem* GetSocketName(s32 host_fd)
{
if (host_fd < 0)
return new QTableWidgetItem();
sockaddr_in sock_addr;
socklen_t sock_addr_len = sizeof(sockaddr_in);
if (getsockname(host_fd, reinterpret_cast<sockaddr*>(&sock_addr), &sock_addr_len) != 0)
return new QTableWidgetItem(QTableWidget::tr("Unknown"));
const QString sock_name = QStringLiteral("%1:%2")
.arg(QString::fromLatin1(inet_ntoa(sock_addr.sin_addr)))
.arg(ntohs(sock_addr.sin_port));
sockaddr_in peer_addr;
socklen_t peer_addr_len = sizeof(sockaddr_in);
if (getpeername(host_fd, reinterpret_cast<sockaddr*>(&peer_addr), &peer_addr_len) != 0)
return new QTableWidgetItem(sock_name);
const QString peer_name = QStringLiteral("%1:%2")
.arg(QString::fromLatin1(inet_ntoa(peer_addr.sin_addr)))
.arg(ntohs(peer_addr.sin_port));
return new QTableWidgetItem(QStringLiteral("%1->%2").arg(sock_name).arg(peer_name));
}
} // namespace
NetworkWidget::NetworkWidget(QWidget* parent) : QDockWidget(parent)
{
setWindowTitle(tr("Network"));
setObjectName(QStringLiteral("network"));
setHidden(!Settings::Instance().IsNetworkVisible() || !Settings::Instance().IsDebugModeEnabled());
setAllowedAreas(Qt::AllDockWidgetAreas);
CreateWidgets();
auto& settings = Settings::GetQSettings();
restoreGeometry(settings.value(QStringLiteral("networkwidget/geometry")).toByteArray());
// macOS: setHidden() needs to be evaluated before setFloating() for proper window presentation
// according to Settings
setFloating(settings.value(QStringLiteral("networkwidget/floating")).toBool());
ConnectWidgets();
connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, &NetworkWidget::Update);
connect(&Settings::Instance(), &Settings::NetworkVisibilityChanged,
[this](bool visible) { setHidden(!visible); });
connect(&Settings::Instance(), &Settings::DebugModeToggled, [this](bool enabled) {
setHidden(!enabled || !Settings::Instance().IsNetworkVisible());
});
}
NetworkWidget::~NetworkWidget()
{
auto& settings = Settings::GetQSettings();
settings.setValue(QStringLiteral("networkwidget/geometry"), saveGeometry());
settings.setValue(QStringLiteral("networkwidget/floating"), isFloating());
}
void NetworkWidget::closeEvent(QCloseEvent*)
{
Settings::Instance().SetNetworkVisible(false);
}
void NetworkWidget::showEvent(QShowEvent* event)
{
Update();
}
void NetworkWidget::CreateWidgets()
{
auto* widget = new QWidget;
auto* layout = new QVBoxLayout;
widget->setLayout(layout);
layout->addWidget(CreateSocketTableGroup());
layout->addWidget(CreateSSLContextGroup());
layout->addWidget(CreateSSLOptionsGroup());
layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding));
setWidget(widget);
Update();
}
void NetworkWidget::ConnectWidgets()
{
connect(m_dump_ssl_read_checkbox, &QCheckBox::stateChanged,
[](int state) { SConfig::GetInstance().m_SSLDumpRead = state == Qt::Checked; });
connect(m_dump_ssl_write_checkbox, &QCheckBox::stateChanged,
[](int state) { SConfig::GetInstance().m_SSLDumpWrite = state == Qt::Checked; });
connect(m_dump_root_ca_checkbox, &QCheckBox::stateChanged,
[](int state) { SConfig::GetInstance().m_SSLDumpRootCA = state == Qt::Checked; });
connect(m_dump_peer_cert_checkbox, &QCheckBox::stateChanged,
[](int state) { SConfig::GetInstance().m_SSLDumpPeerCert = state == Qt::Checked; });
connect(m_verify_certificates_checkbox, &QCheckBox::stateChanged,
[](int state) { SConfig::GetInstance().m_SSLVerifyCert = state == Qt::Checked; });
}
void NetworkWidget::Update()
{
m_socket_table->setRowCount(0);
for (u32 wii_fd = 0; wii_fd < IOS::HLE::WII_SOCKET_FD_MAX; wii_fd++)
{
m_socket_table->insertRow(wii_fd);
const s32 host_fd = IOS::HLE::WiiSockMan::GetInstance().GetHostSocket(wii_fd);
m_socket_table->setItem(wii_fd, 0, new QTableWidgetItem(QString::number(wii_fd)));
m_socket_table->setItem(wii_fd, 1, GetSocketDomain(host_fd));
m_socket_table->setItem(wii_fd, 2, GetSocketType(host_fd));
m_socket_table->setItem(wii_fd, 3, GetSocketState(host_fd));
m_socket_table->setItem(wii_fd, 4, GetSocketName(host_fd));
}
m_socket_table->resizeColumnsToContents();
m_ssl_table->setRowCount(0);
for (u32 ssl_id = 0; ssl_id < IOS::HLE::NET_SSL_MAXINSTANCES; ssl_id++)
{
m_ssl_table->insertRow(ssl_id);
s32 host_fd = -1;
if (IOS::HLE::Device::IsSSLIDValid(ssl_id) &&
IOS::HLE::Device::NetSSL::_SSL[ssl_id].ctx.p_bio != nullptr)
{
host_fd =
static_cast<mbedtls_net_context*>(IOS::HLE::Device::NetSSL::_SSL[ssl_id].ctx.p_bio)->fd;
}
m_ssl_table->setItem(ssl_id, 0, new QTableWidgetItem(QString::number(ssl_id)));
m_ssl_table->setItem(ssl_id, 1, GetSocketDomain(host_fd));
m_ssl_table->setItem(ssl_id, 2, GetSocketType(host_fd));
m_ssl_table->setItem(ssl_id, 3, GetSocketState(host_fd));
m_ssl_table->setItem(ssl_id, 4, GetSocketName(host_fd));
}
m_ssl_table->resizeColumnsToContents();
const auto& config = SConfig::GetInstance();
m_dump_ssl_read_checkbox->setChecked(config.m_SSLDumpRead);
m_dump_ssl_write_checkbox->setChecked(config.m_SSLDumpWrite);
m_dump_root_ca_checkbox->setChecked(config.m_SSLDumpRootCA);
m_dump_peer_cert_checkbox->setChecked(config.m_SSLDumpPeerCert);
m_verify_certificates_checkbox->setChecked(config.m_SSLVerifyCert);
}
QGroupBox* NetworkWidget::CreateSocketTableGroup()
{
QGroupBox* socket_table_group = new QGroupBox(tr("Socket table"));
QGridLayout* socket_table_layout = new QGridLayout;
socket_table_group->setLayout(socket_table_layout);
m_socket_table = new QTableWidget();
QStringList header{tr("FD"), tr("Domain"), tr("Type"), tr("State"), tr("Name")};
m_socket_table->setColumnCount(header.size());
m_socket_table->setHorizontalHeaderLabels(header);
m_socket_table->setTabKeyNavigation(false);
m_socket_table->verticalHeader()->setVisible(false);
m_socket_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_socket_table->setSelectionMode(QAbstractItemView::NoSelection);
m_socket_table->setWordWrap(false);
socket_table_layout->addWidget(m_socket_table, 0, 0);
socket_table_layout->setSpacing(1);
return socket_table_group;
}
QGroupBox* NetworkWidget::CreateSSLContextGroup()
{
QGroupBox* ssl_context_group = new QGroupBox(tr("SSL context"));
QGridLayout* ssl_context_layout = new QGridLayout;
ssl_context_group->setLayout(ssl_context_layout);
m_ssl_table = new QTableWidget();
QStringList header{tr("ID"), tr("Domain"), tr("Type"), tr("State"), tr("Name")};
m_ssl_table->setColumnCount(header.size());
m_ssl_table->setHorizontalHeaderLabels(header);
m_ssl_table->setTabKeyNavigation(false);
m_ssl_table->verticalHeader()->setVisible(false);
m_ssl_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_ssl_table->setSelectionMode(QAbstractItemView::NoSelection);
m_ssl_table->setWordWrap(false);
ssl_context_layout->addWidget(m_ssl_table, 0, 0);
ssl_context_layout->setSpacing(1);
return ssl_context_group;
}
QGroupBox* NetworkWidget::CreateSSLOptionsGroup()
{
QGroupBox* ssl_options_group = new QGroupBox(tr("SSL options"));
QGridLayout* ssl_options_layout = new QGridLayout;
ssl_options_group->setLayout(ssl_options_layout);
m_dump_ssl_read_checkbox = new QCheckBox(tr("Dump SSL read"));
m_dump_ssl_write_checkbox = new QCheckBox(tr("Dump SSL write"));
m_dump_root_ca_checkbox = new QCheckBox(tr("Dump root CA"));
m_dump_peer_cert_checkbox = new QCheckBox(tr("Dump peer certificates"));
m_verify_certificates_checkbox = new QCheckBox(tr("Verify certificates"));
ssl_options_layout->addWidget(m_dump_ssl_read_checkbox, 0, 0);
ssl_options_layout->addWidget(m_dump_ssl_write_checkbox, 1, 0);
ssl_options_layout->addWidget(m_verify_certificates_checkbox, 2, 0);
ssl_options_layout->addWidget(m_dump_root_ca_checkbox, 0, 1);
ssl_options_layout->addWidget(m_dump_peer_cert_checkbox, 1, 1);
ssl_options_layout->setSpacing(1);
return ssl_options_group;
}

View File

@ -0,0 +1,46 @@
// Copyright 2020 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <QDockWidget>
#include "Common/CommonTypes.h"
class QCheckBox;
class QCloseEvent;
class QGroupBox;
class QShowEvent;
class QTableWidget;
class QTableWidgetItem;
class NetworkWidget : public QDockWidget
{
Q_OBJECT
public:
explicit NetworkWidget(QWidget* parent = nullptr);
~NetworkWidget();
protected:
void closeEvent(QCloseEvent*) override;
void showEvent(QShowEvent* event) override;
private:
void CreateWidgets();
void ConnectWidgets();
void Update();
QGroupBox* CreateSocketTableGroup();
QGroupBox* CreateSSLContextGroup();
QGroupBox* CreateSSLOptionsGroup();
QTableWidget* m_socket_table;
QTableWidget* m_ssl_table;
QCheckBox* m_dump_ssl_read_checkbox;
QCheckBox* m_dump_ssl_write_checkbox;
QCheckBox* m_dump_root_ca_checkbox;
QCheckBox* m_dump_peer_cert_checkbox;
QCheckBox* m_verify_certificates_checkbox;
};

View File

@ -147,6 +147,7 @@
<QtMoc Include="Debugger\JITWidget.h" />
<QtMoc Include="Debugger\MemoryWidget.h" />
<QtMoc Include="Debugger\MemoryViewWidget.h" />
<QtMoc Include="Debugger\NetworkWidget.h" />
<QtMoc Include="Debugger\NewBreakpointDialog.h" />
<QtMoc Include="Debugger\PatchInstructionDialog.h" />
<QtMoc Include="Debugger\RegisterWidget.h" />
@ -275,6 +276,7 @@
<ClCompile Include="$(QtMocOutPrefix)MappingWindow.cpp" />
<ClCompile Include="$(QtMocOutPrefix)MemoryViewWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)MemoryWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)NetworkWidget.cpp" />
<ClCompile Include="$(QtMocOutPrefix)MenuBar.cpp" />
<ClCompile Include="$(QtMocOutPrefix)ModalMessageBox.cpp" />
<ClCompile Include="$(QtMocOutPrefix)NetPlayBrowser.cpp" />
@ -377,6 +379,7 @@
<ClCompile Include="Debugger\JITWidget.cpp" />
<ClCompile Include="Debugger\MemoryWidget.cpp" />
<ClCompile Include="Debugger\MemoryViewWidget.cpp" />
<ClCompile Include="Debugger\NetworkWidget.cpp" />
<ClCompile Include="DiscordHandler.cpp" />
<ClCompile Include="DiscordJoinRequestDialog.cpp" />
<ClCompile Include="FIFO\FIFOAnalyzer.cpp" />

View File

@ -71,6 +71,7 @@
#include "DolphinQt/Debugger/CodeWidget.h"
#include "DolphinQt/Debugger/JITWidget.h"
#include "DolphinQt/Debugger/MemoryWidget.h"
#include "DolphinQt/Debugger/NetworkWidget.h"
#include "DolphinQt/Debugger/RegisterWidget.h"
#include "DolphinQt/Debugger/WatchWidget.h"
#include "DolphinQt/DiscordHandler.h"
@ -389,6 +390,7 @@ void MainWindow::CreateComponents()
m_log_widget = new LogWidget(this);
m_log_config_widget = new LogConfigWidget(this);
m_memory_widget = new MemoryWidget(this);
m_network_widget = new NetworkWidget(this);
m_register_widget = new RegisterWidget(this);
m_watch_widget = new WatchWidget(this);
m_breakpoint_widget = new BreakpointWidget(this);
@ -643,6 +645,7 @@ void MainWindow::ConnectStack()
addDockWidget(Qt::LeftDockWidgetArea, m_watch_widget);
addDockWidget(Qt::LeftDockWidgetArea, m_breakpoint_widget);
addDockWidget(Qt::LeftDockWidgetArea, m_memory_widget);
addDockWidget(Qt::LeftDockWidgetArea, m_network_widget);
addDockWidget(Qt::LeftDockWidgetArea, m_jit_widget);
tabifyDockWidget(m_log_widget, m_log_config_widget);
@ -651,6 +654,7 @@ void MainWindow::ConnectStack()
tabifyDockWidget(m_log_widget, m_watch_widget);
tabifyDockWidget(m_log_widget, m_breakpoint_widget);
tabifyDockWidget(m_log_widget, m_memory_widget);
tabifyDockWidget(m_log_widget, m_network_widget);
tabifyDockWidget(m_log_widget, m_jit_widget);
}

View File

@ -34,6 +34,7 @@ class MemoryWidget;
class MenuBar;
class NetPlayDialog;
class NetPlaySetupDialog;
class NetworkWidget;
class RegisterWidget;
class RenderWidget;
class SearchBar;
@ -225,6 +226,7 @@ private:
LogWidget* m_log_widget;
LogConfigWidget* m_log_config_widget;
MemoryWidget* m_memory_widget;
NetworkWidget* m_network_widget;
RegisterWidget* m_register_widget;
WatchWidget* m_watch_widget;
CheatsManager* m_cheats_manager;

View File

@ -169,6 +169,7 @@ void MenuBar::OnDebugModeToggled(bool enabled)
m_show_watch->setVisible(enabled);
m_show_breakpoints->setVisible(enabled);
m_show_memory->setVisible(enabled);
m_show_network->setVisible(enabled);
m_show_jit->setVisible(enabled);
if (enabled)
@ -469,6 +470,14 @@ void MenuBar::AddViewMenu()
connect(&Settings::Instance(), &Settings::MemoryVisibilityChanged, m_show_memory,
&QAction::setChecked);
m_show_network = view_menu->addAction(tr("&Network"));
m_show_network->setCheckable(true);
m_show_network->setChecked(Settings::Instance().IsNetworkVisible());
connect(m_show_network, &QAction::toggled, &Settings::Instance(), &Settings::SetNetworkVisible);
connect(&Settings::Instance(), &Settings::NetworkVisibilityChanged, m_show_network,
&QAction::setChecked);
m_show_jit = view_menu->addAction(tr("&JIT"));
m_show_jit->setCheckable(true);
m_show_jit->setChecked(Settings::Instance().IsJITVisible());

View File

@ -235,6 +235,7 @@ private:
QAction* m_show_watch;
QAction* m_show_breakpoints;
QAction* m_show_memory;
QAction* m_show_network;
QAction* m_show_jit;
QMenu* m_cols_menu;

View File

@ -422,6 +422,20 @@ bool Settings::IsMemoryVisible() const
return QSettings().value(QStringLiteral("debugger/showmemory")).toBool();
}
void Settings::SetNetworkVisible(bool enabled)
{
if (IsNetworkVisible() == enabled)
return;
GetQSettings().setValue(QStringLiteral("debugger/shownetwork"), enabled);
emit NetworkVisibilityChanged(enabled);
}
bool Settings::IsNetworkVisible() const
{
return GetQSettings().value(QStringLiteral("debugger/shownetwork")).toBool();
}
void Settings::SetJITVisible(bool enabled)
{
if (IsJITVisible() == enabled)

View File

@ -126,6 +126,8 @@ public:
bool IsCodeVisible() const;
void SetMemoryVisible(bool enabled);
bool IsMemoryVisible() const;
void SetNetworkVisible(bool enabled);
bool IsNetworkVisible() const;
void SetJITVisible(bool enabled);
bool IsJITVisible() const;
QFont GetDebugFont() const;
@ -168,6 +170,7 @@ signals:
void BreakpointsVisibilityChanged(bool visible);
void CodeVisibilityChanged(bool visible);
void MemoryVisibilityChanged(bool visible);
void NetworkVisibilityChanged(bool visible);
void JITVisibilityChanged(bool visible);
void DebugModeToggled(bool enabled);
void DebugFontChanged(QFont font);