Removed using namespace directive for class member function definitions in source files

This commit is contained in:
Nav
2022-02-05 15:32:08 +00:00
parent 9bbc534973
commit 53a3c815d7
116 changed files with 13113 additions and 12664 deletions

View File

@@ -11,435 +11,448 @@
#include "src/Helpers/Paths.hpp"
#include "Exceptions/InvalidConfig.hpp"
using namespace Bloom;
using namespace Bloom::Events;
using namespace Bloom::Exceptions;
namespace Bloom
{
using namespace Exceptions;
int Application::run(const std::vector<std::string>& arguments) {
try {
this->setName("Bloom");
int Application::run(const std::vector<std::string>& arguments) {
try {
this->setName("Bloom");
if (!arguments.empty()) {
auto firstArg = arguments.front();
auto commandsToCallbackMapping = this->getCommandToHandlerMapping();
if (!arguments.empty()) {
auto firstArg = arguments.front();
auto commandsToCallbackMapping = this->getCommandToHandlerMapping();
if (commandsToCallbackMapping.contains(firstArg)) {
// User has passed an argument that maps to a command callback - invoke the callback and shutdown
auto returnValue = commandsToCallbackMapping.at(firstArg)();
if (commandsToCallbackMapping.contains(firstArg)) {
// User has passed an argument that maps to a command callback - invoke the callback and shutdown
auto returnValue = commandsToCallbackMapping.at(firstArg)();
this->shutdown();
return returnValue;
}
// If the first argument didn't map to a command, we assume it's an environment name
this->selectedEnvironmentName = firstArg;
}
#ifdef BLOOM_DEBUG_BUILD
Logger::warning("This is a debug build - some functions may not work as expected.");
#endif
this->startup();
if (this->insightConfig->insightEnabled) {
this->insight = std::make_unique<Insight>(
this->eventManager,
this->projectConfig.value(),
this->environmentConfig.value(),
this->insightConfig.value(),
this->projectSettings.value().insightSettings
);
/*
* Before letting Insight occupy the main thread, process any pending events that accumulated
* during startup.
*/
this->applicationEventListener->dispatchCurrentEvents();
if (Thread::getThreadState() == ThreadState::READY) {
this->insight->run();
Logger::debug("Insight closed");
}
this->shutdown();
return returnValue;
return EXIT_SUCCESS;
}
// If the first argument didn't map to a command, we assume it's an environment name
this->selectedEnvironmentName = firstArg;
}
#ifdef BLOOM_DEBUG_BUILD
Logger::warning("This is a debug build - some functions may not work as expected.");
#endif
this->startup();
if (this->insightConfig->insightEnabled) {
this->insight = std::make_unique<Insight>(
this->eventManager,
this->projectConfig.value(),
this->environmentConfig.value(),
this->insightConfig.value(),
this->projectSettings.value().insightSettings
);
/*
* Before letting Insight occupy the main thread, process any pending events that accumulated
* during startup.
*/
this->applicationEventListener->dispatchCurrentEvents();
if (Thread::getThreadState() == ThreadState::READY) {
this->insight->run();
Logger::debug("Insight closed");
// Main event loop
while (Thread::getThreadState() == ThreadState::READY) {
this->applicationEventListener->waitAndDispatch();
}
this->shutdown();
return EXIT_SUCCESS;
}
catch (const InvalidConfig& exception) {
Logger::error(exception.getMessage());
}
catch (const Exception& exception) {
Logger::error(exception.getMessage());
}
// Main event loop
while (Thread::getThreadState() == ThreadState::READY) {
this->applicationEventListener->waitAndDispatch();
this->shutdown();
return EXIT_SUCCESS;
}
bool Application::isRunningAsRoot() {
return geteuid() == 0;
}
void Application::startup() {
auto applicationEventListener = this->applicationEventListener;
this->eventManager.registerListener(applicationEventListener);
applicationEventListener->registerCallbackForEventType<Events::ShutdownApplication>(
std::bind(&Application::onShutdownApplicationRequest, this, std::placeholders::_1)
);
this->loadProjectSettings();
this->loadProjectConfiguration();
Logger::configure(this->projectConfig.value());
Logger::debug("Bloom version: " + Application::VERSION.toString());
this->blockAllSignalsOnCurrentThread();
this->startSignalHandler();
Logger::info("Selected environment: \"" + this->selectedEnvironmentName + "\"");
Logger::debug("Number of environments extracted from config: "
+ std::to_string(this->projectConfig->environments.size()));
applicationEventListener->registerCallbackForEventType<Events::TargetControllerThreadStateChanged>(
std::bind(&Application::onTargetControllerThreadStateChanged, this, std::placeholders::_1)
);
applicationEventListener->registerCallbackForEventType<Events::DebugServerThreadStateChanged>(
std::bind(&Application::onDebugServerThreadStateChanged, this, std::placeholders::_1)
);
this->startTargetController();
this->startDebugServer();
Thread::setThreadState(ThreadState::READY);
}
void Application::shutdown() {
auto appState = Thread::getThreadState();
if (appState == ThreadState::STOPPED || appState == ThreadState::SHUTDOWN_INITIATED) {
return;
}
} catch (const InvalidConfig& exception) {
Logger::error(exception.getMessage());
Thread::setThreadState(ThreadState::SHUTDOWN_INITIATED);
Logger::info("Shutting down Bloom");
} catch (const Exception& exception) {
Logger::error(exception.getMessage());
this->stopDebugServer();
this->stopTargetController();
this->stopSignalHandler();
this->saveProjectSettings();
Thread::setThreadState(ThreadState::STOPPED);
}
this->shutdown();
return EXIT_SUCCESS;
}
void Application::loadProjectSettings() {
const auto projectSettingsPath = Paths::projectSettingsPath();
auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath));
bool Application::isRunningAsRoot() {
return geteuid() == 0;
}
if (jsonSettingsFile.exists()) {
try {
if (!jsonSettingsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
throw Exception("Failed to open settings file.");
}
void Application::startup() {
auto applicationEventListener = this->applicationEventListener;
this->eventManager.registerListener(applicationEventListener);
applicationEventListener->registerCallbackForEventType<Events::ShutdownApplication>(
std::bind(&Application::onShutdownApplicationRequest, this, std::placeholders::_1)
);
const auto jsonObject = QJsonDocument::fromJson(jsonSettingsFile.readAll()).object();
jsonSettingsFile.close();
this->loadProjectSettings();
this->loadProjectConfiguration();
Logger::configure(this->projectConfig.value());
this->projectSettings = ProjectSettings(jsonObject);
return;
Logger::debug("Bloom version: " + Application::VERSION.toString());
}
catch (const std::exception& exception) {
Logger::error(
"Failed to load project settings from " + projectSettingsPath + " - " + exception.what()
);
}
}
this->blockAllSignalsOnCurrentThread();
this->startSignalHandler();
Logger::info("Selected environment: \"" + this->selectedEnvironmentName + "\"");
Logger::debug("Number of environments extracted from config: "
+ std::to_string(this->projectConfig->environments.size()));
applicationEventListener->registerCallbackForEventType<Events::TargetControllerThreadStateChanged>(
std::bind(&Application::onTargetControllerThreadStateChanged, this, std::placeholders::_1)
);
applicationEventListener->registerCallbackForEventType<Events::DebugServerThreadStateChanged>(
std::bind(&Application::onDebugServerThreadStateChanged, this, std::placeholders::_1)
);
this->startTargetController();
this->startDebugServer();
Thread::setThreadState(ThreadState::READY);
}
void Application::shutdown() {
auto appState = Thread::getThreadState();
if (appState == ThreadState::STOPPED || appState == ThreadState::SHUTDOWN_INITIATED) {
return;
this->projectSettings = ProjectSettings();
}
Thread::setThreadState(ThreadState::SHUTDOWN_INITIATED);
Logger::info("Shutting down Bloom");
void Application::saveProjectSettings() {
if (!this->projectSettings.has_value()) {
return;
}
this->stopDebugServer();
this->stopTargetController();
this->stopSignalHandler();
const auto projectSettingsPath = Paths::projectSettingsPath();
auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath));
this->saveProjectSettings();
Thread::setThreadState(ThreadState::STOPPED);
}
Logger::debug("Saving project settings to " + projectSettingsPath);
void Application::loadProjectSettings() {
const auto projectSettingsPath = Paths::projectSettingsPath();
auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath));
QDir().mkpath(QString::fromStdString(Paths::projectSettingsDirPath()));
if (jsonSettingsFile.exists()) {
try {
if (!jsonSettingsFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
throw Exception("Failed to open settings file.");
const auto jsonDocument = QJsonDocument(this->projectSettings->toJson());
if (!jsonSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
throw Exception(
"failed to open/create settings file (" + projectSettingsPath + "). Check file permissions."
);
}
const auto jsonObject = QJsonDocument::fromJson(jsonSettingsFile.readAll()).object();
jsonSettingsFile.write(jsonDocument.toJson());
jsonSettingsFile.close();
this->projectSettings = ProjectSettings(jsonObject);
return;
} catch (const std::exception& exception) {
}
catch (const Exception& exception) {
Logger::error(
"Failed to load project settings from " + projectSettingsPath + " - " + exception.what()
"Failed to save project settings - " + exception.getMessage()
);
}
}
this->projectSettings = ProjectSettings();
}
void Application::loadProjectConfiguration() {
auto jsonConfigFile = QFile(QString::fromStdString(Paths::projectConfigPath()));
void Application::saveProjectSettings() {
if (!this->projectSettings.has_value()) {
return;
}
if (!jsonConfigFile.exists()) {
throw InvalidConfig("Bloom configuration file (bloom.json) not found. Working directory: "
+ Paths::projectDirPath());
}
const auto projectSettingsPath = Paths::projectSettingsPath();
auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath));
if (!jsonConfigFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
throw InvalidConfig("Failed to load Bloom configuration file (bloom.json) Working directory: "
+ Paths::projectDirPath());
}
Logger::debug("Saving project settings to " + projectSettingsPath);
auto jsonObject = QJsonDocument::fromJson(jsonConfigFile.readAll()).object();
jsonConfigFile.close();
QDir().mkpath(QString::fromStdString(Paths::projectSettingsDirPath()));
this->projectConfig = ProjectConfig(jsonObject);
try {
const auto jsonDocument = QJsonDocument(this->projectSettings->toJson());
if (!jsonSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) {
throw Exception(
"failed to open/create settings file (" + projectSettingsPath + "). Check file permissions."
// Validate the selected environment
if (!this->projectConfig->environments.contains(this->selectedEnvironmentName)) {
throw InvalidConfig(
"Environment (\"" + this->selectedEnvironmentName + "\") not found in configuration."
);
}
jsonSettingsFile.write(jsonDocument.toJson());
jsonSettingsFile.close();
this->environmentConfig = this->projectConfig->environments.at(this->selectedEnvironmentName);
} catch (const Exception& exception) {
Logger::error(
"Failed to save project settings - " + exception.getMessage()
);
}
}
if (this->environmentConfig->insightConfig.has_value()) {
this->insightConfig = this->environmentConfig->insightConfig.value();
void Application::loadProjectConfiguration() {
auto jsonConfigFile = QFile(QString::fromStdString(Paths::projectConfigPath()));
}
else if (this->projectConfig->insightConfig.has_value()) {
this->insightConfig = this->projectConfig->insightConfig.value();
if (!jsonConfigFile.exists()) {
throw InvalidConfig("Bloom configuration file (bloom.json) not found. Working directory: "
+ Paths::projectDirPath());
}
else {
throw InvalidConfig("Insight configuration missing.");
}
if (this->environmentConfig->debugServerConfig.has_value()) {
this->debugServerConfig = this->environmentConfig->debugServerConfig.value();
}
else if (this->projectConfig->debugServerConfig.has_value()) {
this->debugServerConfig = this->projectConfig->debugServerConfig.value();
}
else {
throw InvalidConfig("Debug server configuration missing.");
}
}
if (!jsonConfigFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
throw InvalidConfig("Failed to load Bloom configuration file (bloom.json) Working directory: "
+ Paths::projectDirPath());
}
int Application::presentHelpText() {
/*
* Silence all logging here, as we're just to display the help text and then exit the application. Any
* further logging will just be noise.
*/
Logger::silence();
auto jsonObject = QJsonDocument::fromJson(jsonConfigFile.readAll()).object();
jsonConfigFile.close();
this->projectConfig = ProjectConfig(jsonObject);
// Validate the selected environment
if (!this->projectConfig->environments.contains(this->selectedEnvironmentName)) {
throw InvalidConfig(
"Environment (\"" + this->selectedEnvironmentName + "\") not found in configuration."
);
}
this->environmentConfig = this->projectConfig->environments.at(this->selectedEnvironmentName);
if (this->environmentConfig->insightConfig.has_value()) {
this->insightConfig = this->environmentConfig->insightConfig.value();
} else if (this->projectConfig->insightConfig.has_value()) {
this->insightConfig = this->projectConfig->insightConfig.value();
} else {
throw InvalidConfig("Insight configuration missing.");
}
if (this->environmentConfig->debugServerConfig.has_value()) {
this->debugServerConfig = this->environmentConfig->debugServerConfig.value();
} else if (this->projectConfig->debugServerConfig.has_value()) {
this->debugServerConfig = this->projectConfig->debugServerConfig.value();
} else {
throw InvalidConfig("Debug server configuration missing.");
}
}
int Application::presentHelpText() {
/*
* Silence all logging here, as we're just to display the help text and then exit the application. Any
* further logging will just be noise.
*/
Logger::silence();
// The file help.txt is included in the binary image as a resource. See src/resource.qrc
auto helpFile = QFile(QString::fromStdString(
// The file help.txt is included in the binary image as a resource. See src/resource.qrc
auto helpFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/resources/help.txt"
)
);
if (!helpFile.open(QIODevice::ReadOnly)) {
// This should never happen - if it does, something has gone very wrong
throw Exception(
"Failed to open help file - please report this issue at " + Paths::homeDomainName() + "/report-issue"
+ "/resources/help.txt"
)
);
if (!helpFile.open(QIODevice::ReadOnly)) {
// This should never happen - if it does, something has gone very wrong
throw Exception(
"Failed to open help file - please report this issue at " + Paths::homeDomainName()
+ "/report-issue"
);
}
std::cout << "Bloom v" << Application::VERSION.toString() << "\n";
std::cout << QTextStream(&helpFile).readAll().toUtf8().constData() << "\n";
return EXIT_SUCCESS;
}
std::cout << "Bloom v" << Application::VERSION.toString() << "\n";
std::cout << QTextStream(&helpFile).readAll().toUtf8().constData() << "\n";
return EXIT_SUCCESS;
}
int Application::presentVersionText() {
Logger::silence();
int Application::presentVersionText() {
Logger::silence();
std::cout << "Bloom v" << Application::VERSION.toString() << "\n";
std::cout << "Bloom v" << Application::VERSION.toString() << "\n";
#ifdef BLOOM_DEBUG_BUILD
std::cout << "DEBUG BUILD - Compilation timestamp: " << __DATE__ << " " << __TIME__ << "\n";
std::cout << "DEBUG BUILD - Compilation timestamp: " << __DATE__ << " " << __TIME__ << "\n";
#endif
std::cout << Paths::homeDomainName() + "/\n";
std::cout << "Nav Mohammed\n";
return EXIT_SUCCESS;
}
int Application::initProject() {
auto configFile = QFile(QString::fromStdString(std::filesystem::current_path().string() + "/bloom.json"));
if (configFile.exists()) {
throw Exception("Bloom configuration file (bloom.json) already exists in working directory.");
std::cout << Paths::homeDomainName() + "/\n";
std::cout << "Nav Mohammed\n";
return EXIT_SUCCESS;
}
/*
* The file bloom.template.json is just a template Bloom config file that is included in the binary image as
* a resource. See src/resource.qrc
*
* We simply copy the template file into the user's working directory.
*/
auto templateConfigFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/resources/bloom.template.json"
)
);
if (!templateConfigFile.open(QIODevice::ReadOnly)) {
throw Exception(
"Failed to open template configuration file - please report this issue at "
+ Paths::homeDomainName() + "/report-issue"
int Application::initProject() {
auto configFile = QFile(
QString::fromStdString(std::filesystem::current_path().string() + "/bloom.json")
);
}
if (!configFile.open(QIODevice::ReadWrite)) {
throw Exception("Failed to create Bloom configuration file (bloom.json)");
}
configFile.write(templateConfigFile.readAll());
configFile.close();
templateConfigFile.close();
Logger::info("Bloom configuration file (bloom.json) created in working directory.");
return EXIT_SUCCESS;
}
void Application::startSignalHandler() {
this->signalHandlerThread = std::thread(&SignalHandler::run, std::ref(this->signalHandler));
}
void Application::stopSignalHandler() {
if (this->signalHandler.getThreadState() != ThreadState::STOPPED
&& this->signalHandler.getThreadState() != ThreadState::UNINITIALISED
) {
this->signalHandler.triggerShutdown();
if (configFile.exists()) {
throw Exception("Bloom configuration file (bloom.json) already exists in working directory.");
}
/*
* Send meaningless signal to the SignalHandler thread to have it shutdown. The signal will pull it out of a
* blocking state and allow it to action the shutdown. See SignalHandler::run() for more.
* The file bloom.template.json is just a template Bloom config file that is included in the binary image as
* a resource. See src/resource.qrc
*
* We simply copy the template file into the user's working directory.
*/
pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1);
}
if (this->signalHandlerThread.joinable()) {
Logger::debug("Joining SignalHandler thread");
this->signalHandlerThread.join();
Logger::debug("SignalHandler thread joined");
}
}
void Application::startTargetController() {
this->targetController = std::make_unique<TargetController>(
this->eventManager,
this->projectConfig.value(),
this->environmentConfig.value()
);
this->targetControllerThread = std::thread(
&TargetController::run,
this->targetController.get()
);
auto tcStateChangeEvent = this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>();
if (!tcStateChangeEvent.has_value() || tcStateChangeEvent->get()->getState() != ThreadState::READY) {
throw Exception("TargetController failed to startup.");
}
}
void Application::stopTargetController() {
if (this->targetController == nullptr) {
return;
}
auto targetControllerState = this->targetController->getThreadState();
if (targetControllerState == ThreadState::STARTING || targetControllerState == ThreadState::READY) {
this->eventManager.triggerEvent(std::make_shared<Events::ShutdownTargetController>());
this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>(
std::chrono::milliseconds(10000)
auto templateConfigFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()+ "/resources/bloom.template.json")
);
if (!templateConfigFile.open(QIODevice::ReadOnly)) {
throw Exception(
"Failed to open template configuration file - please report this issue at "
+ Paths::homeDomainName() + "/report-issue"
);
}
if (!configFile.open(QIODevice::ReadWrite)) {
throw Exception("Failed to create Bloom configuration file (bloom.json)");
}
configFile.write(templateConfigFile.readAll());
configFile.close();
templateConfigFile.close();
Logger::info("Bloom configuration file (bloom.json) created in working directory.");
return EXIT_SUCCESS;
}
if (this->targetControllerThread.joinable()) {
Logger::debug("Joining TargetController thread");
this->targetControllerThread.join();
Logger::debug("TargetController thread joined");
}
}
void Application::startDebugServer() {
auto supportedDebugServers = this->getSupportedDebugServers();
if (!supportedDebugServers.contains(this->debugServerConfig->name)) {
throw Exceptions::InvalidConfig("DebugServer \"" + this->debugServerConfig->name + "\" not found.");
void Application::startSignalHandler() {
this->signalHandlerThread = std::thread(&SignalHandler::run, std::ref(this->signalHandler));
}
this->debugServer = supportedDebugServers.at(this->debugServerConfig->name)();
Logger::info("Selected DebugServer: " + this->debugServer->getName());
void Application::stopSignalHandler() {
if (this->signalHandler.getThreadState() != ThreadState::STOPPED
&& this->signalHandler.getThreadState() != ThreadState::UNINITIALISED
) {
this->signalHandler.triggerShutdown();
this->debugServerThread = std::thread(
&DebugServers::DebugServer::run,
this->debugServer.get()
);
/*
* Send meaningless signal to the SignalHandler thread to have it shutdown. The signal will pull it out of
* a blocking state and allow it to action the shutdown. See SignalHandler::run() for more.
*/
pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1);
}
auto dsStateChangeEvent = this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>();
if (!dsStateChangeEvent.has_value() || dsStateChangeEvent->get()->getState() != ThreadState::READY) {
throw Exception("DebugServer failed to startup.");
}
}
void Application::stopDebugServer() {
if (this->debugServer == nullptr) {
// DebugServer hasn't been resolved yet.
return;
if (this->signalHandlerThread.joinable()) {
Logger::debug("Joining SignalHandler thread");
this->signalHandlerThread.join();
Logger::debug("SignalHandler thread joined");
}
}
auto debugServerState = this->debugServer->getThreadState();
if (debugServerState == ThreadState::STARTING || debugServerState == ThreadState::READY) {
this->eventManager.triggerEvent(std::make_shared<Events::ShutdownDebugServer>());
this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>(
std::chrono::milliseconds(5000)
void Application::startTargetController() {
this->targetController = std::make_unique<TargetController>(
this->eventManager,
this->projectConfig.value(),
this->environmentConfig.value()
);
this->targetControllerThread = std::thread(
&TargetController::run,
this->targetController.get()
);
auto tcStateChangeEvent =
this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>();
if (!tcStateChangeEvent.has_value() || tcStateChangeEvent->get()->getState() != ThreadState::READY) {
throw Exception("TargetController failed to startup.");
}
}
if (this->debugServerThread.joinable()) {
Logger::debug("Joining DebugServer thread");
this->debugServerThread.join();
Logger::debug("DebugServer thread joined");
}
}
void Application::stopTargetController() {
if (this->targetController == nullptr) {
return;
}
void Application::onTargetControllerThreadStateChanged(const Events::TargetControllerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) {
// TargetController has unexpectedly shutdown - it must have encountered a fatal error.
auto targetControllerState = this->targetController->getThreadState();
if (targetControllerState == ThreadState::STARTING || targetControllerState == ThreadState::READY) {
this->eventManager.triggerEvent(std::make_shared<Events::ShutdownTargetController>());
this->applicationEventListener->waitForEvent<Events::TargetControllerThreadStateChanged>(
std::chrono::milliseconds(10000)
);
}
if (this->targetControllerThread.joinable()) {
Logger::debug("Joining TargetController thread");
this->targetControllerThread.join();
Logger::debug("TargetController thread joined");
}
}
void Application::startDebugServer() {
auto supportedDebugServers = this->getSupportedDebugServers();
if (!supportedDebugServers.contains(this->debugServerConfig->name)) {
throw Exceptions::InvalidConfig(
"DebugServer \"" + this->debugServerConfig->name + "\" not found."
);
}
this->debugServer = supportedDebugServers.at(this->debugServerConfig->name)();
Logger::info("Selected DebugServer: " + this->debugServer->getName());
this->debugServerThread = std::thread(
&DebugServers::DebugServer::run,
this->debugServer.get()
);
auto dsStateChangeEvent = this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>();
if (!dsStateChangeEvent.has_value() || dsStateChangeEvent->get()->getState() != ThreadState::READY) {
throw Exception("DebugServer failed to startup.");
}
}
void Application::stopDebugServer() {
if (this->debugServer == nullptr) {
// DebugServer hasn't been resolved yet.
return;
}
auto debugServerState = this->debugServer->getThreadState();
if (debugServerState == ThreadState::STARTING || debugServerState == ThreadState::READY) {
this->eventManager.triggerEvent(std::make_shared<Events::ShutdownDebugServer>());
this->applicationEventListener->waitForEvent<Events::DebugServerThreadStateChanged>(
std::chrono::milliseconds(5000)
);
}
if (this->debugServerThread.joinable()) {
Logger::debug("Joining DebugServer thread");
this->debugServerThread.join();
Logger::debug("DebugServer thread joined");
}
}
void Application::onTargetControllerThreadStateChanged(const Events::TargetControllerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) {
// TargetController has unexpectedly shutdown - it must have encountered a fatal error.
this->shutdown();
}
}
void Application::onShutdownApplicationRequest(const Events::ShutdownApplication&) {
Logger::debug("ShutdownApplication event received.");
this->shutdown();
}
}
void Application::onShutdownApplicationRequest(const Events::ShutdownApplication&) {
Logger::debug("ShutdownApplication event received.");
this->shutdown();
}
void Application::onDebugServerThreadStateChanged(const Events::DebugServerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) {
// DebugServer has unexpectedly shutdown - it must have encountered a fatal error.
this->shutdown();
void Application::onDebugServerThreadStateChanged(const Events::DebugServerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED || event.getState() == ThreadState::SHUTDOWN_INITIATED) {
// DebugServer has unexpectedly shutdown - it must have encountered a fatal error.
this->shutdown();
}
}
}

View File

@@ -5,58 +5,62 @@
#include "src/Exceptions/InvalidConfig.hpp"
#include "src/Logger/Logger.hpp"
using namespace Bloom::DebugServers;
using namespace Bloom::Events;
namespace Bloom::DebugServers
{
using namespace Bloom::Events;
void DebugServer::run() {
try {
this->startup();
void DebugServer::run() {
try {
this->startup();
Logger::info("DebugServer ready");
Logger::info("DebugServer ready");
while (this->getThreadState() == ThreadState::READY) {
this->serve();
this->eventListener->dispatchCurrentEvents();
while (this->getThreadState() == ThreadState::READY) {
this->serve();
this->eventListener->dispatchCurrentEvents();
}
} catch (const std::exception& exception) {
Logger::error("DebugServer fatal error: " + std::string(exception.what()));
}
} catch (const std::exception& exception) {
Logger::error("DebugServer fatal error: " + std::string(exception.what()));
this->shutdown();
}
this->shutdown();
}
void DebugServer::startup() {
this->setName("DS");
Logger::info("Starting DebugServer");
void DebugServer::startup() {
this->setName("DS");
Logger::info("Starting DebugServer");
this->eventManager.registerListener(this->eventListener);
this->eventManager.registerListener(this->eventListener);
this->interruptEventNotifier = std::make_shared<EventNotifier>();
this->eventListener->setInterruptEventNotifier(this->interruptEventNotifier);
this->interruptEventNotifier = std::make_shared<EventNotifier>();
this->eventListener->setInterruptEventNotifier(this->interruptEventNotifier);
// Register event handlers
this->eventListener->registerCallbackForEventType<Events::ShutdownDebugServer>(
std::bind(&DebugServer::onShutdownDebugServerEvent, this, std::placeholders::_1)
);
// Register event handlers
this->eventListener->registerCallbackForEventType<Events::ShutdownDebugServer>(
std::bind(&DebugServer::onShutdownDebugServerEvent, this, std::placeholders::_1)
);
this->targetDescriptor = this->targetControllerConsole.getTargetDescriptor();
this->targetDescriptor = this->targetControllerConsole.getTargetDescriptor();
this->init();
this->setThreadStateAndEmitEvent(ThreadState::READY);
}
void DebugServer::shutdown() {
if (this->getThreadState() == ThreadState::STOPPED || this->getThreadState() == ThreadState::SHUTDOWN_INITIATED) {
return;
this->init();
this->setThreadStateAndEmitEvent(ThreadState::READY);
}
this->setThreadState(ThreadState::SHUTDOWN_INITIATED);
Logger::info("Shutting down DebugServer");
this->close();
this->setThreadStateAndEmitEvent(ThreadState::STOPPED);
this->eventManager.deregisterListener(this->eventListener->getId());
}
void DebugServer::shutdown() {
if (this->getThreadState() == ThreadState::STOPPED
|| this->getThreadState() == ThreadState::SHUTDOWN_INITIATED
) {
return;
}
void DebugServer::onShutdownDebugServerEvent(const Events::ShutdownDebugServer& event) {
this->shutdown();
this->setThreadState(ThreadState::SHUTDOWN_INITIATED);
Logger::info("Shutting down DebugServer");
this->close();
this->setThreadStateAndEmitEvent(ThreadState::STOPPED);
this->eventManager.deregisterListener(this->eventListener->getId());
}
void DebugServer::onShutdownDebugServerEvent(const Events::ShutdownDebugServer& event) {
this->shutdown();
}
}

View File

@@ -2,139 +2,147 @@
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::DebugServers::Gdb;
using namespace Bloom::Exceptions;
namespace Bloom::DebugServers::Gdb
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterType;
void AvrGdbRsp::init() {
this->loadRegisterMappings();
void AvrGdbRsp::init() {
this->loadRegisterMappings();
GdbRspDebugServer::init();
}
void AvrGdbRsp::loadRegisterMappings() {
auto& registerDescriptorsByType = this->targetDescriptor.registerDescriptorsByType;
if (!registerDescriptorsByType.contains(TargetRegisterType::STATUS_REGISTER)) {
throw Exception("Missing status register descriptor");
GdbRspDebugServer::init();
}
if (!registerDescriptorsByType.contains(TargetRegisterType::STACK_POINTER)) {
throw Exception("Missing stack pointer register descriptor");
}
void AvrGdbRsp::loadRegisterMappings() {
auto& registerDescriptorsByType = this->targetDescriptor.registerDescriptorsByType;
if (!registerDescriptorsByType.contains(TargetRegisterType::STATUS_REGISTER)) {
throw Exception("Missing status register descriptor");
}
if (!registerDescriptorsByType.contains(TargetRegisterType::PROGRAM_COUNTER)) {
throw Exception("Missing program counter register descriptor");
}
if (!registerDescriptorsByType.contains(TargetRegisterType::STACK_POINTER)) {
throw Exception("Missing stack pointer register descriptor");
}
if (!registerDescriptorsByType.contains(TargetRegisterType::GENERAL_PURPOSE_REGISTER)
|| registerDescriptorsByType.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).size() != 32
) {
throw Exception("Unexpected general purpose register count");
}
if (!registerDescriptorsByType.contains(TargetRegisterType::PROGRAM_COUNTER)) {
throw Exception("Missing program counter register descriptor");
}
/*
* Worth noting that gpRegisterDescriptors will always be sorted in the correct order, from register 0 to 31.
*
* Hmm, but the sorting is based on the start address (see TargetRegisterDescriptor::<() for more). So effectively,
* we're assuming that the registers will be laid out in the correct order, in memory. I think this assumption is
* fair.
*/
const auto& gpRegisterDescriptors = registerDescriptorsByType.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER);
if (!registerDescriptorsByType.contains(TargetRegisterType::GENERAL_PURPOSE_REGISTER)
|| registerDescriptorsByType.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).size() != 32
) {
throw Exception("Unexpected general purpose register count");
}
// General purpose CPU registers
GdbRegisterNumberType regNumber = 0;
for (const auto& descriptor : gpRegisterDescriptors) {
this->registerDescriptorsByGdbNumber.insert(std::pair(
regNumber,
RegisterDescriptor(
/*
* Worth noting that gpRegisterDescriptors will always be sorted in the correct order, from register 0 to 31.
*
* Hmm, but the sorting is based on the start address (see TargetRegisterDescriptor::<() for more). So effectively,
* we're assuming that the registers will be laid out in the correct order, in memory. I think this assumption is
* fair.
*/
const auto& gpRegisterDescriptors = registerDescriptorsByType.at(
TargetRegisterType::GENERAL_PURPOSE_REGISTER
);
// General purpose CPU registers
GdbRegisterNumberType regNumber = 0;
for (const auto& descriptor : gpRegisterDescriptors) {
this->registerDescriptorsByGdbNumber.insert(std::pair(
regNumber,
1,
"General Purpose Register " + std::to_string(regNumber)
)
));
RegisterDescriptor(
regNumber,
1,
"General Purpose Register " + std::to_string(regNumber)
)
));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
regNumber,
descriptor
));
regNumber++;
}
const auto statusDescriptor = RegisterDescriptor(
32,
1,
"Status Register"
);
this->registerDescriptorsByGdbNumber.insert(std::pair(statusDescriptor.number, statusDescriptor));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
regNumber,
descriptor
statusDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).begin())
));
regNumber++;
const auto stackPointerDescriptor = RegisterDescriptor(
33,
2,
"Stack Pointer Register"
);
this->registerDescriptorsByGdbNumber.insert(
std::pair(stackPointerDescriptor.number, stackPointerDescriptor)
);
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
stackPointerDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).begin())
));
const auto programCounterDescriptor = RegisterDescriptor(
34,
4,
"Program Counter"
);
this->registerDescriptorsByGdbNumber.insert(std::pair(
programCounterDescriptor.number,
programCounterDescriptor
));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
programCounterDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).begin())
));
if (registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).size() > statusDescriptor.size) {
throw Exception("AVR8 status target register size exceeds the GDB register size.");
}
if (registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).size() > stackPointerDescriptor.size) {
throw Exception("AVR8 stack pointer target register size exceeds the GDB register size.");
}
if (
registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).size() > programCounterDescriptor.size
) {
throw Exception("AVR8 program counter size exceeds the GDB register size.");
}
}
const auto statusDescriptor = RegisterDescriptor(
32,
1,
"Status Register"
);
this->registerDescriptorsByGdbNumber.insert(std::pair(statusDescriptor.number, statusDescriptor));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
statusDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).begin())
));
const auto stackPointerDescriptor = RegisterDescriptor(
33,
2,
"Stack Pointer Register"
);
this->registerDescriptorsByGdbNumber.insert(std::pair(stackPointerDescriptor.number, stackPointerDescriptor));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
stackPointerDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).begin())
));
const auto programCounterDescriptor = RegisterDescriptor(
34,
4,
"Program Counter"
);
this->registerDescriptorsByGdbNumber.insert(std::pair(
programCounterDescriptor.number,
programCounterDescriptor
));
this->targetRegisterDescriptorsByGdbNumber.insert(std::pair(
programCounterDescriptor.number,
*(registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).begin())
));
if (registerDescriptorsByType.at(TargetRegisterType::STATUS_REGISTER).size() > statusDescriptor.size) {
throw Exception("AVR8 status target register size exceeds the GDB register size.");
std::optional<GdbRegisterNumberType> AvrGdbRsp::getRegisterNumberFromTargetRegisterDescriptor(
const Targets::TargetRegisterDescriptor& registerDescriptor
) {
return this->targetRegisterDescriptorsByGdbNumber.valueAt(registerDescriptor);
}
if (registerDescriptorsByType.at(TargetRegisterType::STACK_POINTER).size() > stackPointerDescriptor.size) {
throw Exception("AVR8 stack pointer target register size exceeds the GDB register size.");
const RegisterDescriptor& AvrGdbRsp::getRegisterDescriptorFromNumber(GdbRegisterNumberType number) {
if (this->registerDescriptorsByGdbNumber.contains(number)) {
return this->registerDescriptorsByGdbNumber.at(number);
}
throw Exception("Unknown register from GDB - register number (" + std::to_string(number)
+ ") not mapped to any GDB register descriptor.");
}
if (registerDescriptorsByType.at(TargetRegisterType::PROGRAM_COUNTER).size() > programCounterDescriptor.size) {
throw Exception("AVR8 program counter size exceeds the GDB register size.");
const TargetRegisterDescriptor& AvrGdbRsp::getTargetRegisterDescriptorFromNumber(GdbRegisterNumberType number) {
if (this->targetRegisterDescriptorsByGdbNumber.contains(number)) {
return this->targetRegisterDescriptorsByGdbNumber.at(number);
}
throw Exception("Unknown register from GDB - register number (" + std::to_string(number)
+ ") not mapped to any target register descriptor.");
}
}
std::optional<GdbRegisterNumberType> AvrGdbRsp::getRegisterNumberFromTargetRegisterDescriptor(
const Targets::TargetRegisterDescriptor& registerDescriptor
) {
return this->targetRegisterDescriptorsByGdbNumber.valueAt(registerDescriptor);
}
const RegisterDescriptor& AvrGdbRsp::getRegisterDescriptorFromNumber(GdbRegisterNumberType number) {
if (this->registerDescriptorsByGdbNumber.contains(number)) {
return this->registerDescriptorsByGdbNumber.at(number);
}
throw Exception("Unknown register from GDB - register number (" + std::to_string(number)
+ ") not mapped to any GDB register descriptor.");
}
const TargetRegisterDescriptor& AvrGdbRsp::getTargetRegisterDescriptorFromNumber(GdbRegisterNumberType number) {
if (this->targetRegisterDescriptorsByGdbNumber.contains(number)) {
return this->targetRegisterDescriptorsByGdbNumber.at(number);
}
throw Exception("Unknown register from GDB - register number (" + std::to_string(number)
+ ") not mapped to any target register descriptor.");
}

View File

@@ -2,8 +2,9 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void CommandPacket::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
namespace Bloom::DebugServers::Gdb::CommandPackets
{
void CommandPacket::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
}

View File

@@ -4,126 +4,138 @@
#include <memory>
#include <map>
using namespace Bloom::DebugServers::Gdb;
using namespace Bloom::DebugServers::Gdb::CommandPackets;
namespace Bloom::DebugServers::Gdb
{
using CommandPackets::CommandPacket;
std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(std::vector<unsigned char> buffer) {
std::vector<std::vector<unsigned char>> output;
std::vector<std::vector<unsigned char>> CommandPacketFactory::extractRawPackets(
std::vector<unsigned char> buffer
) {
std::vector<std::vector<unsigned char>> output;
std::size_t bufferIndex;
std::size_t bufferSize = buffer.size();
unsigned char byte;
for (bufferIndex = 0; bufferIndex < bufferSize; bufferIndex++) {
byte = buffer[bufferIndex];
std::size_t bufferIndex;
std::size_t bufferSize = buffer.size();
unsigned char byte;
for (bufferIndex = 0; bufferIndex < bufferSize; bufferIndex++) {
byte = buffer[bufferIndex];
if (byte == 0x03) {
/*
* This is an interrupt packet - it doesn't carry any of the usual packet frame bytes, so we'll just
* add them here, in order to keep things consistent.
*
* Because we're effectively faking the packet frame, we can use any value for the checksum.
*/
output.push_back({'$', byte, '#', 'F', 'F'});
if (byte == 0x03) {
/*
* This is an interrupt packet - it doesn't carry any of the usual packet frame bytes, so we'll just
* add them here, in order to keep things consistent.
*
* Because we're effectively faking the packet frame, we can use any value for the checksum.
*/
output.push_back({'$', byte, '#', 'F', 'F'});
} else if (byte == '$') {
// Beginning of packet
std::vector<unsigned char> rawPacket;
rawPacket.push_back('$');
} else if (byte == '$') {
// Beginning of packet
std::vector<unsigned char> rawPacket;
rawPacket.push_back('$');
auto packetIndex = bufferIndex;
bool validPacket = false;
bool isByteEscaped = false;
auto packetIndex = bufferIndex;
bool validPacket = false;
bool isByteEscaped = false;
for (packetIndex++; packetIndex < bufferSize; packetIndex++) {
byte = buffer[packetIndex];
for (packetIndex++; packetIndex < bufferSize; packetIndex++) {
byte = buffer[packetIndex];
if (byte == '}' && !isByteEscaped) {
isByteEscaped = true;
continue;
}
if (byte == '}' && !isByteEscaped) {
isByteEscaped = true;
continue;
}
if (byte == '$' && !isByteEscaped) {
// Unexpected end of packet
validPacket = false;
break;
}
if (byte == '#' && !isByteEscaped) {
// End of packet data
if ((bufferSize - 1) < (packetIndex + 2)) {
// There should be at least two more bytes in the buffer, for the checksum.
if (byte == '$' && !isByteEscaped) {
// Unexpected end of packet
validPacket = false;
break;
}
if (byte == '#' && !isByteEscaped) {
// End of packet data
if ((bufferSize - 1) < (packetIndex + 2)) {
// There should be at least two more bytes in the buffer, for the checksum.
break;
}
rawPacket.push_back(byte);
// Add the checksum bytes and break the loop
rawPacket.push_back(buffer[++packetIndex]);
rawPacket.push_back(buffer[++packetIndex]);
validPacket = true;
break;
}
if (isByteEscaped) {
// Escaped bytes are XOR'd with a 0x20 mask.
byte ^= 0x20;
isByteEscaped = false;
}
rawPacket.push_back(byte);
// Add the checksum bytes and break the loop
rawPacket.push_back(buffer[++packetIndex]);
rawPacket.push_back(buffer[++packetIndex]);
validPacket = true;
break;
}
if (isByteEscaped) {
// Escaped bytes are XOR'd with a 0x20 mask.
byte ^= 0x20;
isByteEscaped = false;
if (validPacket) {
output.push_back(rawPacket);
bufferIndex = packetIndex;
}
rawPacket.push_back(byte);
}
if (validPacket) {
output.push_back(rawPacket);
bufferIndex = packetIndex;
}
}
return output;
}
return output;
}
std::unique_ptr<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
// This is an interrupt request - create a fake packet for it
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
}
auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end());
if (rawPacketString.size() >= 2) {
/*
* First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when
* looking for a command identifier string.
*/
if (rawPacketString.find("qSupported") == 1) {
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
} else if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
} else if (rawPacketString[1] == 'P') {
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
} else if (rawPacketString[1] == 'c') {
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
} else if (rawPacketString[1] == 's') {
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
} else if (rawPacketString[1] == 'm') {
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
} else if (rawPacketString[1] == 'M') {
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
} else if (rawPacketString[1] == 'Z') {
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
} else if (rawPacketString[1] == 'z') {
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
std::unique_ptr<CommandPacket> CommandPacketFactory::create(std::vector<unsigned char> rawPacket) {
if (rawPacket.size() == 5 && rawPacket[1] == 0x03) {
// This is an interrupt request - create a fake packet for it
return std::make_unique<CommandPackets::InterruptExecution>(rawPacket);
}
}
return std::make_unique<CommandPacket>(rawPacket);
auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end());
if (rawPacketString.size() >= 2) {
/*
* First byte of the raw packet will be 0x24 ('$'), so find() should return 1, not 0, when
* looking for a command identifier string.
*/
if (rawPacketString.find("qSupported") == 1) {
return std::make_unique<CommandPackets::SupportedFeaturesQuery>(rawPacket);
}
if (rawPacketString[1] == 'g' || rawPacketString[1] == 'p') {
return std::make_unique<CommandPackets::ReadRegisters>(rawPacket);
}
if (rawPacketString[1] == 'P') {
return std::make_unique<CommandPackets::WriteRegister>(rawPacket);
}
if (rawPacketString[1] == 'c') {
return std::make_unique<CommandPackets::ContinueExecution>(rawPacket);
}
if (rawPacketString[1] == 's') {
return std::make_unique<CommandPackets::StepExecution>(rawPacket);
}
if (rawPacketString[1] == 'm') {
return std::make_unique<CommandPackets::ReadMemory>(rawPacket);
}
if (rawPacketString[1] == 'M') {
return std::make_unique<CommandPackets::WriteMemory>(rawPacket);
}
if (rawPacketString[1] == 'Z') {
return std::make_unique<CommandPackets::SetBreakpoint>(rawPacket);
}
if (rawPacketString[1] == 'z') {
return std::make_unique<CommandPackets::RemoveBreakpoint>(rawPacket);
}
}
return std::make_unique<CommandPacket>(rawPacket);
}
}

View File

@@ -4,16 +4,17 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void ContinueExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void ContinueExecution::init() {
if (this->data.size() > 1) {
this->fromProgramCounter = static_cast<std::uint32_t>(
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
);
void ContinueExecution::init() {
if (this->data.size() > 1) {
this->fromProgramCounter = static_cast<std::uint32_t>(
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
);
}
}
}

View File

@@ -2,8 +2,9 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
void InterruptExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
namespace Bloom::DebugServers::Gdb::CommandPackets
{
void InterruptExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
}

View File

@@ -2,43 +2,47 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::Exceptions;
void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void ReadMemory::init() {
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
void ReadMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 1),
static_cast<int>(this->data.size() - 1)
);
void ReadMemory::init() {
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
}
/*
* The read memory ('m') packet consists of two segments, an address and a number of bytes to read.
* These are separated by a comma character.
*/
auto packetSegments = packetString.split(",");
auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 1),
static_cast<int>(this->data.size() - 1)
);
if (packetSegments.size() != 2) {
throw Exception("Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()));
}
/*
* The read memory ('m') packet consists of two segments, an address and a number of bytes to read.
* These are separated by a comma character.
*/
auto packetSegments = packetString.split(",");
bool conversionStatus = false;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
if (packetSegments.size() != 2) {
throw Exception(
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
);
}
if (!conversionStatus) {
throw Exception("Failed to parse start address from read memory packet data");
}
bool conversionStatus = false;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse start address from read memory packet data");
}
if (!conversionStatus) {
throw Exception("Failed to parse read length from read memory packet data");
this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse read length from read memory packet data");
}
}
}

View File

@@ -2,15 +2,18 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void ReadRegisters::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void ReadRegisters::init() {
if (this->data.size() >= 2 && this->data.front() == 'p') {
// This command packet is requesting a specific register
this->registerNumber = static_cast<size_t>(std::stoi(std::string(this->data.begin() + 1, this->data.end())));
void ReadRegisters::init() {
if (this->data.size() >= 2 && this->data.front() == 'p') {
// This command packet is requesting a specific register
this->registerNumber = static_cast<size_t>(
std::stoi(std::string(this->data.begin() + 1, this->data.end()))
);
}
}
}

View File

@@ -4,36 +4,38 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::Exceptions;
void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void RemoveBreakpoint::init() {
if (data.size() < 6) {
throw Exception("Unexpected RemoveBreakpoint packet size");
void RemoveBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
// z0 = SW breakpoint, z1 = HW breakpoint
this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
void RemoveBreakpoint::init() {
if (data.size() < 6) {
throw Exception("Unexpected RemoveBreakpoint packet size");
}
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
// z0 = SW breakpoint, z1 = HW breakpoint
this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet");
}
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet");
}
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from RemoveBreakpoint packet.");
}
}
}

View File

@@ -1,40 +1,41 @@
#include "SetBreakpoint.hpp"
#include <QtCore/QString>
#include <QtCore/QStringList>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::Exceptions;
void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void SetBreakpoint::init() {
if (data.size() < 6) {
throw Exception("Unexpected SetBreakpoint packet size");
void SetBreakpoint::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
// Z0 = SW breakpoint, Z1 = HW breakpoint
this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
void SetBreakpoint::init() {
if (data.size() < 6) {
throw Exception("Unexpected SetBreakpoint packet size");
}
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
// Z0 = SW breakpoint, Z1 = HW breakpoint
this->type = (data[1] == 0) ? BreakpointType::SOFTWARE_BREAKPOINT : (data[1] == 1) ?
BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN;
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in SetBreakpoint packet");
}
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 2),
static_cast<int>(this->data.size() - 2)
);
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
auto packetSegments = packetData.split(",");
if (packetSegments.size() < 3) {
throw Exception("Unexpected number of packet segments in SetBreakpoint packet");
}
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
bool conversionStatus = true;
this->address = packetSegments.at(1).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to convert address hex value from SetBreakpoint packet.");
}
}
}

View File

@@ -1,18 +1,20 @@
#include "StepExecution.hpp"
#include <cstdint>
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
#include "StepExecution.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void StepExecution::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void StepExecution::init() {
if (this->data.size() > 1) {
this->fromProgramCounter = static_cast<std::uint32_t>(
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
);
void StepExecution::init() {
if (this->data.size() > 1) {
this->fromProgramCounter = static_cast<std::uint32_t>(
std::stoi(std::string(this->data.begin(), this->data.end()), nullptr, 16)
);
}
}
}

View File

@@ -16,7 +16,7 @@ namespace Bloom::DebugServers::Gdb::CommandPackets
/**
* The address from which to begin the step.
*/
std::optional<size_t> fromProgramCounter;
std::optional<std::size_t> fromProgramCounter;
explicit StepExecution(const std::vector<unsigned char>& rawPacket): CommandPacket(rawPacket) {
init();

View File

@@ -4,38 +4,39 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void SupportedFeaturesQuery::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void SupportedFeaturesQuery::init() {
/*
* For qSupported packets, supported and unsupported GDB features are reported in the packet
* data, where each GDB feature is separated by a semicolon.
*/
void SupportedFeaturesQuery::init() {
/*
* For qSupported packets, supported and unsupported GDB features are reported in the packet
* data, where each GDB feature is separated by a semicolon.
*/
// The "qSupported:" prefix occupies 11 bytes
if (data.size() > 11) {
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 11),
static_cast<int>(this->data.size() - 11)
);
// The "qSupported:" prefix occupies 11 bytes
if (data.size() > 11) {
auto packetData = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 11),
static_cast<int>(this->data.size() - 11)
);
auto featureList = packetData.split(";");
auto gdbFeatureMapping = getGdbFeatureToNameMapping();
auto featureList = packetData.split(";");
auto gdbFeatureMapping = getGdbFeatureToNameMapping();
for (int i = 0; i < featureList.size(); i++) {
auto featureString = featureList.at(i);
for (int i = 0; i < featureList.size(); i++) {
auto featureString = featureList.at(i);
// We only care about supported features. Supported features will precede a '+' character.
if (featureString[featureString.size() - 1] == '+') {
featureString.remove('+');
// We only care about supported features. Supported features will precede a '+' character.
if (featureString[featureString.size() - 1] == '+') {
featureString.remove('+');
auto feature = gdbFeatureMapping.valueAt(featureString.toStdString());
if (feature.has_value()) {
this->supportedFeatures.insert(static_cast<decltype(feature)::value_type>(feature.value()));
auto feature = gdbFeatureMapping.valueAt(featureString.toStdString());
if (feature.has_value()) {
this->supportedFeatures.insert(static_cast<decltype(feature)::value_type>(feature.value()));
}
}
}
}

View File

@@ -2,52 +2,59 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::Exceptions;
void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void WriteMemory::init() {
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
void WriteMemory::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 1),
static_cast<int>(this->data.size() - 1)
);
void WriteMemory::init() {
if (this->data.size() < 4) {
throw Exception("Invalid packet length");
}
/*
* The write memory ('M') packet consists of three segments, an address, a length and a buffer.
* The address and length are separated by a comma character, and the buffer proceeds a colon character.
*/
auto packetSegments = packetString.split(",");
if (packetSegments.size() != 2) {
throw Exception("Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()));
}
auto packetString = QString::fromLocal8Bit(
reinterpret_cast<const char*>(this->data.data() + 1),
static_cast<int>(this->data.size() - 1)
);
bool conversionStatus = false;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
/*
* The write memory ('M') packet consists of three segments, an address, a length and a buffer.
* The address and length are separated by a comma character, and the buffer proceeds a colon character.
*/
auto packetSegments = packetString.split(",");
if (packetSegments.size() != 2) {
throw Exception(
"Unexpected number of segments in packet data: " + std::to_string(packetSegments.size())
);
}
if (!conversionStatus) {
throw Exception("Failed to parse start address from write memory packet data");
}
bool conversionStatus = false;
this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16);
auto lengthAndBufferSegments = packetSegments.at(1).split(":");
if (lengthAndBufferSegments.size() != 2) {
throw Exception("Unexpected number of segments in packet data: " + std::to_string(lengthAndBufferSegments.size()));
}
if (!conversionStatus) {
throw Exception("Failed to parse start address from write memory packet data");
}
auto bufferSize = lengthAndBufferSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse write length from write memory packet data");
}
auto lengthAndBufferSegments = packetSegments.at(1).split(":");
if (lengthAndBufferSegments.size() != 2) {
throw Exception(
"Unexpected number of segments in packet data: "
+ std::to_string(lengthAndBufferSegments.size())
);
}
this->buffer = Packet::hexToData(lengthAndBufferSegments.at(1).toStdString());
auto bufferSize = lengthAndBufferSegments.at(0).toUInt(&conversionStatus, 16);
if (!conversionStatus) {
throw Exception("Failed to parse write length from write memory packet data");
}
if (this->buffer.size() != bufferSize) {
throw Exception("Buffer size does not match length value given in write memory packet");
this->buffer = Packet::hexToData(lengthAndBufferSegments.at(1).toStdString());
if (this->buffer.size() != bufferSize) {
throw Exception("Buffer size does not match length value given in write memory packet");
}
}
}

View File

@@ -3,27 +3,29 @@
#include "src/DebugServers/GdbRsp/GdbRspDebugServer.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::Exceptions;
namespace Bloom::DebugServers::Gdb::CommandPackets
{
using namespace Bloom::Exceptions;
void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
void WriteRegister::init() {
// The P packet updates a single register
auto packet = std::string(this->data.begin(), this->data.end());
if (packet.size() < 4) {
throw Exception("Invalid P command packet - insufficient data in packet.");
void WriteRegister::dispatchToHandler(Gdb::GdbRspDebugServer& gdbRspDebugServer) {
gdbRspDebugServer.handleGdbPacket(*this);
}
if (packet.find('=') == std::string::npos) {
throw Exception("Invalid P command packet - unexpected format");
}
void WriteRegister::init() {
// The P packet updates a single register
auto packet = std::string(this->data.begin(), this->data.end());
auto packetSegments = QString::fromStdString(packet).split("=");
this->registerNumber = static_cast<int>(packetSegments.front().mid(1).toUInt(nullptr, 16));
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
std::reverse(this->registerValue.begin(), this->registerValue.end());
if (packet.size() < 4) {
throw Exception("Invalid P command packet - insufficient data in packet.");
}
if (packet.find('=') == std::string::npos) {
throw Exception("Invalid P command packet - unexpected format");
}
auto packetSegments = QString::fromStdString(packet).split("=");
this->registerNumber = static_cast<int>(packetSegments.front().mid(1).toUInt(nullptr, 16));
this->registerValue = Packet::hexToData(packetSegments.back().toStdString());
std::reverse(this->registerValue.begin(), this->registerValue.end());
}
}

View File

@@ -14,199 +14,206 @@
#include "src/Logger/Logger.hpp"
using namespace Bloom::DebugServers::Gdb;
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::DebugServers::Gdb::ResponsePackets;
using namespace Bloom::DebugServers::Gdb::Exceptions;
using namespace Bloom::Exceptions;
namespace Bloom::DebugServers::Gdb
{
using namespace CommandPackets;
using namespace ResponsePackets;
using namespace Exceptions;
using namespace Bloom::Exceptions;
void Connection::accept(int serverSocketFileDescriptor) {
int socketAddressLength = sizeof(this->socketAddress);
void Connection::accept(int serverSocketFileDescriptor) {
int socketAddressLength = sizeof(this->socketAddress);
this->socketFileDescriptor = ::accept(
serverSocketFileDescriptor,
(sockaddr*)& (this->socketAddress),
(socklen_t*)& socketAddressLength
);
this->socketFileDescriptor = ::accept(
serverSocketFileDescriptor,
(sockaddr*) &(this->socketAddress),
(socklen_t*) &socketAddressLength
);
if (this->socketFileDescriptor == -1) {
throw Exception("Failed to accept GDB Remote Serial Protocol connection");
if (this->socketFileDescriptor == -1) {
throw Exception("Failed to accept GDB Remote Serial Protocol connection");
}
::fcntl(
this->socketFileDescriptor,
F_SETFL,
fcntl(this->socketFileDescriptor, F_GETFL, 0) | O_NONBLOCK
);
// Create event FD
this->eventFileDescriptor = ::epoll_create(2);
struct epoll_event event = {};
event.events = EPOLLIN;
event.data.fd = this->socketFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->socketFileDescriptor, &event) != 0) {
throw Exception(
"Failed to create event FD for GDB client connection - could not add client connection "
"socket FD to epoll FD"
);
}
this->enableReadInterrupts();
}
::fcntl(
this->socketFileDescriptor,
F_SETFL,
fcntl(this->socketFileDescriptor, F_GETFL, 0) | O_NONBLOCK
);
// Create event FD
this->eventFileDescriptor = ::epoll_create(2);
struct epoll_event event = {};
event.events = EPOLLIN;
event.data.fd = this->socketFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->socketFileDescriptor, &event) != 0) {
throw Exception("Failed to create event FD for GDB client connection - could not add client connection "
"socket FD to epoll FD");
}
this->enableReadInterrupts();
}
void Connection::close() noexcept {
if (this->socketFileDescriptor > 0) {
::close(this->socketFileDescriptor);
this->socketFileDescriptor = 0;
}
}
std::vector<std::unique_ptr<CommandPacket>> Connection::readPackets() {
auto buffer = this->read();
Logger::debug("GDB client data received (" + std::to_string(buffer.size()) + " bytes): " + std::string(buffer.begin(), buffer.end()));
auto rawPackets = CommandPacketFactory::extractRawPackets(buffer);
std::vector<std::unique_ptr<CommandPacket>> output;
for (const auto& rawPacket : rawPackets) {
try {
output.push_back(CommandPacketFactory::create(rawPacket));
this->write({'+'});
} catch (const ClientDisconnected& exception) {
throw exception;
} catch (const Exception& exception) {
Logger::error("Failed to parse GDB packet - " + exception.getMessage());
this->write({'-'});
void Connection::close() noexcept {
if (this->socketFileDescriptor > 0) {
::close(this->socketFileDescriptor);
this->socketFileDescriptor = 0;
}
}
return output;
}
std::vector<std::unique_ptr<CommandPacket>> Connection::readPackets() {
auto buffer = this->read();
Logger::debug("GDB client data received (" + std::to_string(buffer.size()) + " bytes): "
+ std::string(buffer.begin(), buffer.end()));
void Connection::writePacket(const ResponsePacket& packet) {
// Write the packet repeatedly until the GDB client acknowledges it.
int attempts = 0;
auto rawPacket = packet.toRawPacket();
auto rawPackets = CommandPacketFactory::extractRawPackets(buffer);
std::vector<std::unique_ptr<CommandPacket>> output;
do {
if (attempts > 10) {
throw ClientCommunicationError("Failed to write GDB response packet - client failed to "
"acknowledge receipt - retry limit reached");
for (const auto& rawPacket : rawPackets) {
try {
output.push_back(CommandPacketFactory::create(rawPacket));
this->write({'+'});
} catch (const ClientDisconnected& exception) {
throw exception;
} catch (const Exception& exception) {
Logger::error("Failed to parse GDB packet - " + exception.getMessage());
this->write({'-'});
}
}
this->write(rawPacket);
attempts++;
} while(this->readSingleByte(false).value_or(0) != '+');
}
std::vector<unsigned char> Connection::read(size_t bytes, bool interruptible, std::optional<int> msTimeout) {
auto output = std::vector<unsigned char>();
constexpr size_t bufferSize = 1024;
std::array<unsigned char, bufferSize> buffer = {};
ssize_t bytesRead;
if (interruptible) {
if (this->readInterruptEnabled != interruptible) {
this->enableReadInterrupts();
} else {
// Clear any previous interrupts that are still hanging around
this->interruptEventNotifier->clear();
}
return output;
}
if (this->readInterruptEnabled != interruptible && !interruptible) {
this->disableReadInterrupts();
void Connection::writePacket(const ResponsePacket& packet) {
// Write the packet repeatedly until the GDB client acknowledges it.
int attempts = 0;
auto rawPacket = packet.toRawPacket();
do {
if (attempts > 10) {
throw ClientCommunicationError("Failed to write GDB response packet - client failed to "
"acknowledge receipt - retry limit reached");
}
this->write(rawPacket);
attempts++;
} while (this->readSingleByte(false).value_or(0) != '+');
}
std::array<struct epoll_event, 1> events = {};
std::vector<unsigned char> Connection::read(size_t bytes, bool interruptible, std::optional<int> msTimeout) {
auto output = std::vector<unsigned char>();
constexpr size_t bufferSize = 1024;
std::array<unsigned char, bufferSize> buffer = {};
ssize_t bytesRead;
int eventCount = ::epoll_wait(
this->eventFileDescriptor,
events.data(),
1,
msTimeout.value_or(-1)
);
if (interruptible) {
if (this->readInterruptEnabled != interruptible) {
this->enableReadInterrupts();
if (eventCount > 0) {
for (size_t i = 0; i < eventCount; i++) {
auto fileDescriptor = events[i].data.fd;
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
// Interrupted
} else {
// Clear any previous interrupts that are still hanging around
this->interruptEventNotifier->clear();
throw DebugServerInterrupted();
}
}
size_t bytesToRead = (bytes > bufferSize || bytes == 0) ? bufferSize : bytes;
while (bytesToRead > 0 && (bytesRead = ::read(this->socketFileDescriptor, buffer.data(), bytesToRead)) > 0) {
output.insert(output.end(), buffer.begin(), buffer.begin() + bytesRead);
if (this->readInterruptEnabled != interruptible && !interruptible) {
this->disableReadInterrupts();
}
if (bytesRead < bytesToRead) {
// No more data available
break;
std::array<struct epoll_event, 1> events = {};
int eventCount = ::epoll_wait(
this->eventFileDescriptor,
events.data(),
1,
msTimeout.value_or(-1)
);
if (eventCount > 0) {
for (size_t i = 0; i < eventCount; i++) {
auto fileDescriptor = events[i].data.fd;
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
// Interrupted
this->interruptEventNotifier->clear();
throw DebugServerInterrupted();
}
}
bytesToRead = ((bytes - output.size()) > bufferSize || bytes == 0) ? bufferSize : (bytes - output.size());
size_t bytesToRead = (bytes > bufferSize || bytes == 0) ? bufferSize : bytes;
while (bytesToRead > 0
&& (bytesRead = ::read(this->socketFileDescriptor, buffer.data(), bytesToRead)) > 0) {
output.insert(output.end(), buffer.begin(), buffer.begin() + bytesRead);
if (bytesRead < bytesToRead) {
// No more data available
break;
}
bytesToRead =
((bytes - output.size()) > bufferSize || bytes == 0) ? bufferSize : (bytes - output.size());
}
if (output.empty()) {
// EOF means the client has disconnected
throw ClientDisconnected();
}
}
if (output.empty()) {
// EOF means the client has disconnected
throw ClientDisconnected();
return output;
}
std::optional<unsigned char> Connection::readSingleByte(bool interruptible) {
auto bytes = this->read(1, interruptible, 300);
if (!bytes.empty()) {
return bytes.front();
}
return std::nullopt;
}
return output;
}
void Connection::write(const std::vector<unsigned char>& buffer) {
Logger::debug("Writing packet: " + std::string(buffer.begin(), buffer.end()));
if (::write(this->socketFileDescriptor, buffer.data(), buffer.size()) == -1) {
if (errno == EPIPE || errno == ECONNRESET) {
// Connection was closed
throw ClientDisconnected();
}
std::optional<unsigned char> Connection::readSingleByte(bool interruptible) {
auto bytes = this->read(1, interruptible, 300);
if (!bytes.empty()) {
return bytes.front();
}
return std::nullopt;
}
void Connection::write(const std::vector<unsigned char>& buffer) {
Logger::debug("Writing packet: " + std::string(buffer.begin(), buffer.end()));
if (::write(this->socketFileDescriptor, buffer.data(), buffer.size()) == -1) {
if (errno == EPIPE || errno == ECONNRESET) {
// Connection was closed
throw ClientDisconnected();
} else {
throw ClientCommunicationError("Failed to write " + std::to_string(buffer.size())
+ " bytes to GDP client socket - error no: " + std::to_string(errno));
+ " bytes to GDP client socket - error no: "
+ std::to_string(errno));
}
}
}
void Connection::disableReadInterrupts() {
if (::epoll_ctl(
this->eventFileDescriptor,
EPOLL_CTL_DEL,
this->interruptEventNotifier->getFileDescriptor(),
NULL) != 0
) {
throw Exception("Failed to disable GDB client connection read interrupts - epoll_ctl failed");
void Connection::disableReadInterrupts() {
if (::epoll_ctl(
this->eventFileDescriptor,
EPOLL_CTL_DEL,
this->interruptEventNotifier->getFileDescriptor(),
NULL) != 0
) {
throw Exception("Failed to disable GDB client connection read interrupts - epoll_ctl failed");
}
this->readInterruptEnabled = false;
}
this->readInterruptEnabled = false;
}
void Connection::enableReadInterrupts() {
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
struct epoll_event event = {};
event.events = EPOLLIN;
event.data.fd = interruptFileDescriptor;
void Connection::enableReadInterrupts() {
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
struct epoll_event event = {};
event.events = EPOLLIN;
event.data.fd = interruptFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
throw Exception("Failed to enable GDB client connection read interrupts - epoll_ctl failed");
}
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
throw Exception("Failed to enable GDB client connection read interrupts - epoll_ctl failed");
this->readInterruptEnabled = true;
}
this->readInterruptEnabled = true;
}

View File

@@ -12,457 +12,465 @@
#include "src/Exceptions/Exception.hpp"
#include "src/Exceptions/InvalidConfig.hpp"
using namespace Bloom::DebugServers::Gdb;
using namespace Bloom::DebugServers::Gdb::CommandPackets;
using namespace Bloom::DebugServers::Gdb::ResponsePackets;
using namespace Bloom::DebugServers::Gdb::Exceptions;
using namespace Bloom::Events;
using namespace Bloom::Exceptions;
namespace Bloom::DebugServers::Gdb
{
using namespace CommandPackets;
using namespace ResponsePackets;
using namespace Exceptions;
using namespace Bloom::Events;
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetRegister;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetBreakpoint;
using Bloom::Targets::TargetRegister;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetBreakpoint;
void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) {
auto packetData = packet.getData();
auto packetString = std::string(packetData.begin(), packetData.end());
void GdbRspDebugServer::handleGdbPacket(CommandPacket& packet) {
auto packetData = packet.getData();
auto packetString = std::string(packetData.begin(), packetData.end());
if (packetString[0] == '?') {
// Status report
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
if (packetString[0] == '?') {
// Status report
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
} else if (packetString[0] == 'D') {
// Detach packet - there's not really anything we need to do here, so just respond with an OK
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} else if (packetString[0] == 'D') {
// Detach packet - there's not really anything we need to do here, so just respond with an OK
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} else if (packetString.find("qAttached") == 0) {
Logger::debug("Handling qAttached");
this->clientConnection->writePacket(ResponsePacket({1}));
} else {
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
// Respond with an empty packet
this->clientConnection->writePacket(ResponsePacket({0}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) {
Logger::debug("Handling QuerySupport packet");
if (!packet.isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
&& !packet.isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
) {
// All GDB clients are expected to support breakpoints!
throw ClientNotSupported("GDB client does not support HW or SW breakpoints");
}
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
auto response = ResponsePackets::SupportedFeaturesResponse({
{Feature::SOFTWARE_BREAKPOINTS, std::nullopt},
{Feature::PACKET_SIZE, std::to_string(this->clientConnection->getMaxPacketSize())},
});
this->clientConnection->writePacket(response);
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadRegisters& packet) {
Logger::debug("Handling ReadRegisters packet");
try {
auto descriptors = TargetRegisterDescriptors();
if (packet.registerNumber.has_value()) {
Logger::debug("Reading register number: " + std::to_string(packet.registerNumber.value()));
descriptors.insert(this->getTargetRegisterDescriptorFromNumber(packet.registerNumber.value()));
} else if (packetString.find("qAttached") == 0) {
Logger::debug("Handling qAttached");
this->clientConnection->writePacket(ResponsePacket({1}));
} else {
// Read all target registers mapped to a GDB register
for (const auto& descriptor : this->getRegisterNumberToDescriptorMapping().getMap()) {
descriptors.insert(this->getTargetRegisterDescriptorFromNumber(descriptor.second.number));
}
Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response");
// Respond with an empty packet
this->clientConnection->writePacket(ResponsePacket({0}));
}
auto registerSet = this->targetControllerConsole.readRegisters(descriptors);
/*
* Sort each register by their respective GDB register number - this will leave us with a collection of
* registers in the order expected by the GDB client.
*/
std::sort(
registerSet.begin(),
registerSet.end(),
[this] (const TargetRegister& registerA, const TargetRegister& registerB) {
return this->getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) <
this->getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor);
}
);
/*
* Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure
* that each register value size matches the size in the associated GDB register descriptor, implode the
* values, convert to hexadecimal form and send to the GDB client.
*/
auto registers = std::vector<unsigned char>();
for (auto& reg : registerSet) {
std::reverse(reg.value.begin(), reg.value.end());
const auto gdbRegisterNumber = this->getRegisterNumberFromTargetRegisterDescriptor(reg.descriptor).value();
const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(gdbRegisterNumber);
if (reg.value.size() < gdbRegisterDescriptor.size) {
reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00);
}
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
}
auto responseRegisters = Packet::dataToHex(registers);
this->clientConnection->writePacket(
ResponsePacket(std::vector<unsigned char>(responseRegisters.begin(), responseRegisters.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read general registers - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteRegister& packet) {
Logger::debug("Handling WriteRegister packet");
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SupportedFeaturesQuery& packet) {
Logger::debug("Handling QuerySupport packet");
try {
auto targetRegisterDescriptor = this->getTargetRegisterDescriptorFromNumber(packet.registerNumber);
const auto valueSize = packet.registerValue.size();
if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) {
// Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size.
for (auto i = packet.registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) {
if (packet.registerValue.at(i) != 0x00) {
// If we reach a non-zero byte, we cannot trim anymore without changing the data
break;
}
packet.registerValue.erase(packet.registerValue.begin() + i);
}
if (packet.registerValue.size() > targetRegisterDescriptor.size) {
const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(packet.registerNumber);
throw Exception("Cannot set value for " + gdbRegisterDescriptor.name
+ " - value size exceeds register size."
);
}
if (!packet.isFeatureSupported(Feature::HARDWARE_BREAKPOINTS)
&& !packet.isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS)
) {
// All GDB clients are expected to support breakpoints!
throw ClientNotSupported("GDB client does not support HW or SW breakpoints");
}
this->targetControllerConsole.writeRegisters({
TargetRegister(targetRegisterDescriptor, packet.registerValue)
// Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom
auto response = ResponsePackets::SupportedFeaturesResponse({
{Feature::SOFTWARE_BREAKPOINTS, std::nullopt},
{Feature::PACKET_SIZE, std::to_string(this->clientConnection->getMaxPacketSize())},
});
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to write registers - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ContinueExecution& packet) {
Logger::debug("Handling ContinueExecution packet");
try {
this->targetControllerConsole.continueTargetExecution(packet.fromProgramCounter);
this->clientConnection->waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to continue execution on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::StepExecution& packet) {
Logger::debug("Handling StepExecution packet");
try {
this->targetControllerConsole.stepTargetExecution(packet.fromProgramCounter);
this->clientConnection->waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to step execution on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadMemory& packet) {
Logger::debug("Handling ReadMemory packet");
try {
auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress);
auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress);
auto memoryBuffer = this->targetControllerConsole.readMemory(memoryType, startAddress, packet.bytes);
auto hexMemoryBuffer = Packet::dataToHex(memoryBuffer);
this->clientConnection->writePacket(
ResponsePacket(std::vector<unsigned char>(hexMemoryBuffer.begin(), hexMemoryBuffer.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read memory from target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteMemory& packet) {
Logger::debug("Handling WriteMemory packet");
try {
auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress);
auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress);
this->targetControllerConsole.writeMemory(memoryType, startAddress, packet.buffer);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to write memory two target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SetBreakpoint& packet) {
Logger::debug("Handling SetBreakpoint packet");
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = packet.address;
this->targetControllerConsole.setBreakpoint(breakpoint);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::RemoveBreakpoint& packet) {
Logger::debug("Removing breakpoint at address " + std::to_string(packet.address));
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = packet.address;
this->targetControllerConsole.removeBreakpoint(breakpoint);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::InterruptExecution& packet) {
Logger::debug("Handling InterruptExecution packet");
try {
this->targetControllerConsole.stopTargetExecution();
this->clientConnection->writePacket(TargetStopped(Signal::INTERRUPTED));
this->clientConnection->waitingForBreak = false;
} catch (const Exception& exception) {
Logger::error("Failed to interrupt execution - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::init() {
auto ipAddress = this->debugServerConfig.jsonObject.find("ipAddress")->toString().toStdString();
auto configPortJsonValue = this->debugServerConfig.jsonObject.find("port");
auto configPortValue = configPortJsonValue->isString()
? static_cast<std::uint16_t>(configPortJsonValue->toString().toInt(nullptr, 10))
: static_cast<std::uint16_t>(configPortJsonValue->toInt());
if (!ipAddress.empty()) {
this->listeningAddress = ipAddress;
this->clientConnection->writePacket(response);
}
if (configPortValue > 0) {
this->listeningPortNumber = configPortValue;
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadRegisters& packet) {
Logger::debug("Handling ReadRegisters packet");
this->socketAddress.sin_family = AF_INET;
this->socketAddress.sin_port = htons(this->listeningPortNumber);
try {
auto descriptors = TargetRegisterDescriptors();
if (::inet_pton(AF_INET, this->listeningAddress.c_str(), &(this->socketAddress.sin_addr)) == 0) {
// Invalid IP address
throw InvalidConfig("Invalid IP address provided in config file: (\"" + this->listeningAddress + "\")");
}
if (packet.registerNumber.has_value()) {
Logger::debug("Reading register number: " + std::to_string(packet.registerNumber.value()));
descriptors.insert(this->getTargetRegisterDescriptorFromNumber(packet.registerNumber.value()));
int socketFileDescriptor;
} else {
// Read all target registers mapped to a GDB register
for (const auto& descriptor : this->getRegisterNumberToDescriptorMapping().getMap()) {
descriptors.insert(this->getTargetRegisterDescriptorFromNumber(descriptor.second.number));
}
}
if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) {
throw Exception("Failed to create socket file descriptor.");
}
if (::setsockopt(
socketFileDescriptor,
SOL_SOCKET,
SO_REUSEADDR,
&(this->enableReuseAddressSocketOption),
sizeof(this->enableReuseAddressSocketOption)
) < 0
) {
Logger::error("Failed to set socket SO_REUSEADDR option.");
}
if (::bind(
socketFileDescriptor,
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
sizeof(this->socketAddress)
) < 0
) {
throw Exception("Failed to bind address. The selected port number ("
+ std::to_string(this->listeningPortNumber) + ") may be in use.");
}
this->serverSocketFileDescriptor = socketFileDescriptor;
this->eventFileDescriptor = ::epoll_create(2);
struct epoll_event event = {};
event.events = EPOLLIN;
event.data.fd = this->serverSocketFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) {
throw Exception("Failed epoll_ctl server socket");
}
if (this->interruptEventNotifier != nullptr) {
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
event.events = EPOLLIN;
event.data.fd = interruptFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
throw Exception("Failed epoll_ctl interrupt event fd");
}
}
Logger::info("GDB RSP address: " + this->listeningAddress);
Logger::info("GDB RSP port: " + std::to_string(this->listeningPortNumber));
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1)
);
}
void GdbRspDebugServer::close() {
this->closeClientConnection();
if (this->serverSocketFileDescriptor > 0) {
::close(this->serverSocketFileDescriptor);
}
}
void GdbRspDebugServer::serve() {
try {
if (!this->clientConnection.has_value()) {
Logger::info("Waiting for GDB RSP connection");
do {
this->waitForConnection();
} while (!this->clientConnection.has_value());
this->clientConnection->accept(this->serverSocketFileDescriptor);
Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress());
this->eventManager.triggerEvent(std::make_shared<Events::DebugSessionStarted>());
auto registerSet = this->targetControllerConsole.readRegisters(descriptors);
/*
* Before proceeding with a new debug session, we must ensure that the TargetController is able to
* service it.
* Sort each register by their respective GDB register number - this will leave us with a collection of
* registers in the order expected by the GDB client.
*/
if (!this->targetControllerConsole.isTargetControllerInService()) {
this->closeClientConnection();
throw DebugSessionAborted("TargetController not in service");
std::sort(
registerSet.begin(),
registerSet.end(),
[this](const TargetRegister& registerA, const TargetRegister& registerB) {
return this->getRegisterNumberFromTargetRegisterDescriptor(registerA.descriptor) <
this->getRegisterNumberFromTargetRegisterDescriptor(registerB.descriptor);
}
);
/*
* Finally, reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure
* that each register value size matches the size in the associated GDB register descriptor, implode the
* values, convert to hexadecimal form and send to the GDB client.
*/
auto registers = std::vector<unsigned char>();
for (auto& reg : registerSet) {
std::reverse(reg.value.begin(), reg.value.end());
const auto gdbRegisterNumber = this->getRegisterNumberFromTargetRegisterDescriptor(
reg.descriptor
).value();
const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(gdbRegisterNumber);
if (reg.value.size() < gdbRegisterDescriptor.size) {
reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00);
}
registers.insert(registers.end(), reg.value.begin(), reg.value.end());
}
auto responseRegisters = Packet::dataToHex(registers);
this->clientConnection->writePacket(
ResponsePacket(std::vector<unsigned char>(responseRegisters.begin(), responseRegisters.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read general registers - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteRegister& packet) {
Logger::debug("Handling WriteRegister packet");
try {
auto targetRegisterDescriptor = this->getTargetRegisterDescriptorFromNumber(packet.registerNumber);
const auto valueSize = packet.registerValue.size();
if (valueSize > 0 && valueSize > targetRegisterDescriptor.size) {
// Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size.
for (auto i = packet.registerValue.size() - 1; i >= targetRegisterDescriptor.size; i--) {
if (packet.registerValue.at(i) != 0x00) {
// If we reach a non-zero byte, we cannot trim anymore without changing the data
break;
}
packet.registerValue.erase(packet.registerValue.begin() + i);
}
if (packet.registerValue.size() > targetRegisterDescriptor.size) {
const auto& gdbRegisterDescriptor = this->getRegisterDescriptorFromNumber(packet.registerNumber);
throw Exception("Cannot set value for " + gdbRegisterDescriptor.name
+ " - value size exceeds register size."
);
}
}
this->targetControllerConsole.writeRegisters({
TargetRegister(targetRegisterDescriptor, packet.registerValue)
});
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to write registers - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ContinueExecution& packet) {
Logger::debug("Handling ContinueExecution packet");
try {
this->targetControllerConsole.continueTargetExecution(packet.fromProgramCounter);
this->clientConnection->waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to continue execution on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::StepExecution& packet) {
Logger::debug("Handling StepExecution packet");
try {
this->targetControllerConsole.stepTargetExecution(packet.fromProgramCounter);
this->clientConnection->waitingForBreak = true;
} catch (const Exception& exception) {
Logger::error("Failed to step execution on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::ReadMemory& packet) {
Logger::debug("Handling ReadMemory packet");
try {
auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress);
auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress);
auto memoryBuffer = this->targetControllerConsole.readMemory(memoryType, startAddress, packet.bytes);
auto hexMemoryBuffer = Packet::dataToHex(memoryBuffer);
this->clientConnection->writePacket(
ResponsePacket(std::vector<unsigned char>(hexMemoryBuffer.begin(), hexMemoryBuffer.end()))
);
} catch (const Exception& exception) {
Logger::error("Failed to read memory from target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::WriteMemory& packet) {
Logger::debug("Handling WriteMemory packet");
try {
auto memoryType = this->getMemoryTypeFromGdbAddress(packet.startAddress);
auto startAddress = this->removeMemoryTypeIndicatorFromGdbAddress(packet.startAddress);
this->targetControllerConsole.writeMemory(memoryType, startAddress, packet.buffer);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to write memory two target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::SetBreakpoint& packet) {
Logger::debug("Handling SetBreakpoint packet");
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = packet.address;
this->targetControllerConsole.setBreakpoint(breakpoint);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to set breakpoint on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::RemoveBreakpoint& packet) {
Logger::debug("Removing breakpoint at address " + std::to_string(packet.address));
try {
auto breakpoint = TargetBreakpoint();
breakpoint.address = packet.address;
this->targetControllerConsole.removeBreakpoint(breakpoint);
this->clientConnection->writePacket(ResponsePacket({'O', 'K'}));
} catch (const Exception& exception) {
Logger::error("Failed to remove breakpoint on target - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::handleGdbPacket(CommandPackets::InterruptExecution& packet) {
Logger::debug("Handling InterruptExecution packet");
try {
this->targetControllerConsole.stopTargetExecution();
this->clientConnection->writePacket(TargetStopped(Signal::INTERRUPTED));
this->clientConnection->waitingForBreak = false;
} catch (const Exception& exception) {
Logger::error("Failed to interrupt execution - " + exception.getMessage());
this->clientConnection->writePacket(ResponsePacket({'E', '0', '1'}));
}
}
void GdbRspDebugServer::init() {
auto ipAddress = this->debugServerConfig.jsonObject.find("ipAddress")->toString().toStdString();
auto configPortJsonValue = this->debugServerConfig.jsonObject.find("port");
auto configPortValue = configPortJsonValue->isString()
? static_cast<std::uint16_t>(configPortJsonValue->toString().toInt(nullptr, 10))
: static_cast<std::uint16_t>(configPortJsonValue->toInt());
if (!ipAddress.empty()) {
this->listeningAddress = ipAddress;
}
if (configPortValue > 0) {
this->listeningPortNumber = configPortValue;
}
this->socketAddress.sin_family = AF_INET;
this->socketAddress.sin_port = htons(this->listeningPortNumber);
if (::inet_pton(AF_INET, this->listeningAddress.c_str(), &(this->socketAddress.sin_addr)) == 0) {
// Invalid IP address
throw InvalidConfig(
"Invalid IP address provided in config file: (\"" + this->listeningAddress + "\")"
);
}
int socketFileDescriptor;
if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) {
throw Exception("Failed to create socket file descriptor.");
}
if (::setsockopt(
socketFileDescriptor,
SOL_SOCKET,
SO_REUSEADDR,
&(this->enableReuseAddressSocketOption),
sizeof(this->enableReuseAddressSocketOption)
) < 0
) {
Logger::error("Failed to set socket SO_REUSEADDR option.");
}
if (::bind(
socketFileDescriptor,
reinterpret_cast<const sockaddr*>(&(this->socketAddress)),
sizeof(this->socketAddress)
) < 0
) {
throw Exception("Failed to bind address. The selected port number ("
+ std::to_string(this->listeningPortNumber) + ") may be in use.");
}
this->serverSocketFileDescriptor = socketFileDescriptor;
this->eventFileDescriptor = ::epoll_create(2);
struct epoll_event event = {};
event.events = EPOLLIN;
event.data.fd = this->serverSocketFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, this->serverSocketFileDescriptor, &event) != 0) {
throw Exception("Failed epoll_ctl server socket");
}
if (this->interruptEventNotifier != nullptr) {
auto interruptFileDescriptor = this->interruptEventNotifier->getFileDescriptor();
event.events = EPOLLIN;
event.data.fd = interruptFileDescriptor;
if (::epoll_ctl(this->eventFileDescriptor, EPOLL_CTL_ADD, interruptFileDescriptor, &event) != 0) {
throw Exception("Failed epoll_ctl interrupt event fd");
}
}
auto packets = this->clientConnection->readPackets();
Logger::info("GDB RSP address: " + this->listeningAddress);
Logger::info("GDB RSP port: " + std::to_string(this->listeningPortNumber));
// Only process the last packet - any others will likely be duplicates from an impatient client
if (!packets.empty()) {
// Double-dispatch to appropriate handler
packets.back()->dispatchToHandler(*this);
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
std::bind(&GdbRspDebugServer::onTargetControllerStateReported, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1)
);
}
void GdbRspDebugServer::close() {
this->closeClientConnection();
if (this->serverSocketFileDescriptor > 0) {
::close(this->serverSocketFileDescriptor);
}
} catch (const ClientDisconnected&) {
Logger::info("GDB RSP client disconnected");
this->closeClientConnection();
return;
} catch (const ClientCommunicationError& exception) {
Logger::error("GDB RSP client communication error - " + exception.getMessage() + " - closing connection");
this->closeClientConnection();
return;
} catch (const ClientNotSupported& exception) {
Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection");
this->closeClientConnection();
return;
} catch (const DebugSessionAborted& exception) {
Logger::warning("GDB debug session aborted - " + exception.getMessage());
this->closeClientConnection();
return;
} catch (const DebugServerInterrupted&) {
// Server was interrupted
Logger::debug("GDB RSP interrupted");
return;
}
}
void GdbRspDebugServer::waitForConnection() {
if (::listen(this->serverSocketFileDescriptor, 3) != 0) {
throw Exception("Failed to listen on server socket");
}
constexpr int maxEvents = 5;
std::array<struct epoll_event, maxEvents> events = {};
int eventCount = ::epoll_wait(
this->eventFileDescriptor,
events.data(),
maxEvents,
-1
);
void GdbRspDebugServer::serve() {
try {
if (!this->clientConnection.has_value()) {
Logger::info("Waiting for GDB RSP connection");
if (eventCount > 0) {
for (size_t i = 0; i < eventCount; i++) {
auto fileDescriptor = events.at(i).data.fd;
do {
this->waitForConnection();
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
// Interrupted
this->interruptEventNotifier->clear();
throw DebugServerInterrupted();
} while (!this->clientConnection.has_value());
this->clientConnection->accept(this->serverSocketFileDescriptor);
Logger::info("Accepted GDP RSP connection from " + this->clientConnection->getIpAddress());
this->eventManager.triggerEvent(std::make_shared<Events::DebugSessionStarted>());
/*
* Before proceeding with a new debug session, we must ensure that the TargetController is able to
* service it.
*/
if (!this->targetControllerConsole.isTargetControllerInService()) {
this->closeClientConnection();
throw DebugSessionAborted("TargetController not in service");
}
}
auto packets = this->clientConnection->readPackets();
// Only process the last packet - any others will likely be duplicates from an impatient client
if (!packets.empty()) {
// Double-dispatch to appropriate handler
packets.back()->dispatchToHandler(*this);
}
} catch (const ClientDisconnected&) {
Logger::info("GDB RSP client disconnected");
this->closeClientConnection();
return;
} catch (const ClientCommunicationError& exception) {
Logger::error(
"GDB RSP client communication error - " + exception.getMessage() + " - closing connection"
);
this->closeClientConnection();
return;
} catch (const ClientNotSupported& exception) {
Logger::error("Invalid GDB RSP client - " + exception.getMessage() + " - closing connection");
this->closeClientConnection();
return;
} catch (const DebugSessionAborted& exception) {
Logger::warning("GDB debug session aborted - " + exception.getMessage());
this->closeClientConnection();
return;
} catch (const DebugServerInterrupted&) {
// Server was interrupted
Logger::debug("GDB RSP interrupted");
return;
}
}
void GdbRspDebugServer::waitForConnection() {
if (::listen(this->serverSocketFileDescriptor, 3) != 0) {
throw Exception("Failed to listen on server socket");
}
this->clientConnection = Connection(this->interruptEventNotifier);
}
}
constexpr int maxEvents = 5;
std::array<struct epoll_event, maxEvents> events = {};
int eventCount = ::epoll_wait(
this->eventFileDescriptor,
events.data(),
maxEvents,
-1
);
void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) {
if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) {
Logger::warning("Terminating debug session - TargetController suspended unexpectedly");
this->closeClientConnection();
}
}
if (eventCount > 0) {
for (size_t i = 0; i < eventCount; i++) {
auto fileDescriptor = events.at(i).data.fd;
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) {
if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) {
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
this->clientConnection->waitingForBreak = false;
if (fileDescriptor == this->interruptEventNotifier->getFileDescriptor()) {
// Interrupted
this->interruptEventNotifier->clear();
throw DebugServerInterrupted();
}
}
this->clientConnection = Connection(this->interruptEventNotifier);
}
}
void GdbRspDebugServer::onTargetControllerStateReported(const Events::TargetControllerStateReported& event) {
if (event.state == TargetControllerState::SUSPENDED && this->clientConnection.has_value()) {
Logger::warning("Terminating debug session - TargetController suspended unexpectedly");
this->closeClientConnection();
}
}
void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped&) {
if (this->clientConnection.has_value() && this->clientConnection->waitingForBreak) {
this->clientConnection->writePacket(TargetStopped(Signal::TRAP));
this->clientConnection->waitingForBreak = false;
}
}
}

View File

@@ -1,23 +1,25 @@
#include "SupportedFeaturesResponse.hpp"
using namespace Bloom::DebugServers::Gdb::ResponsePackets;
namespace Bloom::DebugServers::Gdb::ResponsePackets
{
std::vector<unsigned char> SupportedFeaturesResponse::getData() const {
std::string output = "qSupported:";
auto gdbFeatureMapping = getGdbFeatureToNameMapping();
std::vector<unsigned char> SupportedFeaturesResponse::getData() const {
std::string output = "qSupported:";
auto gdbFeatureMapping = getGdbFeatureToNameMapping();
for (const auto& supportedFeature : this->supportedFeatures) {
auto featureString = gdbFeatureMapping.valueAt(supportedFeature.first);
for (const auto& supportedFeature : this->supportedFeatures) {
auto featureString = gdbFeatureMapping.valueAt(supportedFeature.first);
if (featureString.has_value()) {
if (supportedFeature.second.has_value()) {
output.append(featureString.value() + "=" + supportedFeature.second.value() + ";");
} else {
output.append(featureString.value() + "+;");
}
if (featureString.has_value()) {
if (supportedFeature.second.has_value()) {
output.append(featureString.value() + "=" + supportedFeature.second.value() + ";");
} else {
output.append(featureString.value() + "+;");
}
}
}
return std::vector<unsigned char>(output.begin(), output.end());
return std::vector<unsigned char>(output.begin(), output.end());
}
}

View File

@@ -3,90 +3,98 @@
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers
{
using namespace Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
void AtmelIce::init() {
UsbDevice::init();
void AtmelIce::init() {
UsbDevice::init();
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
this->setConfiguration(0);
usbHidInterface.init();
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
this->setConfiguration(0);
usbHidInterface.init();
}
/*
* The Atmel-ICE EDBG/CMSIS-DAP interface doesn't operate properly when sending commands too quickly.
*
* Because of this, we have to enforce a minimum time gap between commands. See comment
* in CmsisDapInterface class declaration for more info.
*/
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
this->startSession();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
/*
* The Atmel-ICE EDBG/CMSIS-DAP interface doesn't operate properly when sending commands too quickly.
*
* Because of this, we have to enforce a minimum time gap between commands. See comment
* in CmsisDapInterface class declaration for more info.
*/
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
void AtmelIce::close() {
if (this->sessionStarted) {
this->endSession();
}
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
this->startSession();
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
std::string AtmelIce::getSerialNumber() {
using namespace CommandFrames::Discovery;
void AtmelIce::close() {
if (this->sessionStarted) {
this->endSession();
}
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
std::string AtmelIce::getSerialNumber() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
);
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
void AtmelIce::startSession() {
using namespace CommandFrames::HouseKeeping;
void AtmelIce::startSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::StartSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Atmel-ICE!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Atmel-ICE!");
}
this->sessionStarted = true;
}
this->sessionStarted = true;
}
void AtmelIce::endSession() {
using namespace CommandFrames::HouseKeeping;
void AtmelIce::endSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::EndSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Atmel-ICE!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Atmel-ICE!");
}
this->sessionStarted = false;
}
this->sessionStarted = false;
}

View File

@@ -3,82 +3,90 @@
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers
{
using namespace Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
void CuriosityNano::init() {
UsbDevice::init();
void CuriosityNano::init() {
UsbDevice::init();
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
usbHidInterface.init();
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
usbHidInterface.init();
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
if (!this->sessionStarted) {
this->startSession();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
void CuriosityNano::close() {
if (this->sessionStarted) {
this->endSession();
}
if (!this->sessionStarted) {
this->startSession();
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
std::string CuriosityNano::getSerialNumber() {
using namespace CommandFrames::Discovery;
void CuriosityNano::close() {
if (this->sessionStarted) {
this->endSession();
}
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
std::string CuriosityNano::getSerialNumber() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
);
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
void CuriosityNano::startSession() {
using namespace CommandFrames::HouseKeeping;
void CuriosityNano::startSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::StartSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Curiosity Nano!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Curiosity Nano!");
}
this->sessionStarted = true;
}
this->sessionStarted = true;
}
void CuriosityNano::endSession() {
using namespace CommandFrames::HouseKeeping;
void CuriosityNano::endSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::EndSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Curiosity Nano!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Curiosity Nano!");
}
this->sessionStarted = false;
}
this->sessionStarted = false;
}

View File

@@ -3,83 +3,91 @@
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers
{
using namespace Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
void MplabPickit4::init() {
UsbDevice::init();
void MplabPickit4::init() {
UsbDevice::init();
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
if (!usbHidInterface.isInitialised()) {
usbHidInterface.init();
if (!usbHidInterface.isInitialised()) {
usbHidInterface.init();
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
this->startSession();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
void MplabPickit4::close() {
if (this->sessionStarted) {
this->endSession();
}
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
this->startSession();
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
std::string MplabPickit4::getSerialNumber() {
using namespace CommandFrames::Discovery;
this->setInitialised(true);
}
void MplabPickit4::close() {
if (this->sessionStarted) {
this->endSession();
}
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
std::string MplabPickit4::getSerialNumber() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
);
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
void MplabPickit4::startSession() {
using namespace CommandFrames::HouseKeeping;
void MplabPickit4::startSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::StartSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with MPLAB PICkit 4!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with MPLAB PICkit 4!");
}
this->sessionStarted = true;
}
this->sessionStarted = true;
}
void MplabPickit4::endSession() {
using namespace CommandFrames::HouseKeeping;
void MplabPickit4::endSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::EndSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with MPLAB PICkit 4!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with MPLAB PICkit 4!");
}
this->sessionStarted = false;
}
this->sessionStarted = false;
}

View File

@@ -3,82 +3,90 @@
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers
{
using namespace Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
void MplabSnap::init() {
UsbDevice::init();
void MplabSnap::init() {
UsbDevice::init();
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
if (!usbHidInterface.isInitialised()) {
usbHidInterface.init();
if (!usbHidInterface.isInitialised()) {
usbHidInterface.init();
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
this->startSession();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
void MplabSnap::close() {
if (this->sessionStarted) {
this->endSession();
}
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
this->startSession();
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
std::string MplabSnap::getSerialNumber() {
using namespace CommandFrames::Discovery;
void MplabSnap::close() {
if (this->sessionStarted) {
this->endSession();
}
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
std::string MplabSnap::getSerialNumber() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
);
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
void MplabSnap::startSession() {
using namespace CommandFrames::HouseKeeping;
void MplabSnap::startSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::StartSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with MPLAB Snap!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with MPLAB Snap!");
}
this->sessionStarted = true;
}
this->sessionStarted = true;
}
void MplabSnap::endSession() {
using namespace CommandFrames::HouseKeeping;
void MplabSnap::endSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::EndSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with MPLAB Snap!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with MPLAB Snap!");
}
this->sessionStarted = false;
}
this->sessionStarted = false;
}

View File

@@ -3,92 +3,100 @@
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers
{
using namespace Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
using Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::EdbgAvr8Interface;
void PowerDebugger::init() {
UsbDevice::init();
void PowerDebugger::init() {
UsbDevice::init();
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
if (!usbHidInterface.isInitialised()) {
usbHidInterface.init();
}
if (!usbHidInterface.isInitialised()) {
usbHidInterface.init();
/*
* The Power Debugger EDBG/CMSIS-DAP interface doesn't operate properly when sending commands too quickly.
*
* Because of this, we have to enforce a minimum time gap between commands. See comment in
* CmsisDapInterface class declaration for more info.
*/
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
this->startSession();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
/*
* The Power Debugger EDBG/CMSIS-DAP interface doesn't operate properly when sending commands too quickly.
*
* Because of this, we have to enforce a minimum time gap between commands. See comment in
* CmsisDapInterface class declaration for more info.
*/
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
void PowerDebugger::close() {
if (this->sessionStarted) {
this->endSession();
}
// We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so.
if (!this->sessionStarted) {
this->startSession();
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
std::string PowerDebugger::getSerialNumber() {
using namespace CommandFrames::Discovery;
void PowerDebugger::close() {
if (this->sessionStarted) {
this->endSession();
}
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
std::string PowerDebugger::getSerialNumber() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
);
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
void PowerDebugger::startSession() {
using namespace CommandFrames::HouseKeeping;
void PowerDebugger::startSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::StartSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure(
"Failed to start session with the Power Debugger - device returned failed response ID"
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure(
"Failed to start session with the Power Debugger - device returned failed response ID"
);
}
this->sessionStarted = true;
}
this->sessionStarted = true;
}
void PowerDebugger::endSession() {
using namespace CommandFrames::HouseKeeping;
void PowerDebugger::endSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::EndSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with the Power Debugger - device returned failed response ID");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure(
"Failed to end session with the Power Debugger - device returned failed response ID"
);
}
this->sessionStarted = false;
}
this->sessionStarted = false;
}

View File

@@ -3,82 +3,90 @@
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers
{
using namespace Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
void XplainedMini::init() {
UsbDevice::init();
void XplainedMini::init() {
UsbDevice::init();
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
usbHidInterface.init();
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
usbHidInterface.init();
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
if (!this->sessionStarted) {
this->startSession();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
void XplainedMini::close() {
if (this->sessionStarted) {
this->endSession();
}
if (!this->sessionStarted) {
this->startSession();
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
std::string XplainedMini::getSerialNumber() {
using namespace CommandFrames::Discovery;
void XplainedMini::close() {
if (this->sessionStarted) {
this->endSession();
}
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
std::string XplainedMini::getSerialNumber() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
);
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
void XplainedMini::startSession() {
using namespace CommandFrames::HouseKeeping;
void XplainedMini::startSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::StartSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Xplained Mini!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Xplained Mini!");
}
this->sessionStarted = true;
}
this->sessionStarted = true;
}
void XplainedMini::endSession() {
using namespace CommandFrames::HouseKeeping;
void XplainedMini::endSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::EndSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Xplained Mini!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Xplained Mini!");
}
this->sessionStarted = false;
}
this->sessionStarted = false;
}

View File

@@ -3,82 +3,90 @@
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers
{
using namespace Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
void XplainedNano::init() {
UsbDevice::init();
void XplainedNano::init() {
UsbDevice::init();
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
usbHidInterface.init();
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
usbHidInterface.init();
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
if (!this->sessionStarted) {
this->startSession();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
void XplainedNano::close() {
if (this->sessionStarted) {
this->endSession();
}
if (!this->sessionStarted) {
this->startSession();
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
this->setInitialised(true);
}
std::string XplainedNano::getSerialNumber() {
using namespace CommandFrames::Discovery;
void XplainedNano::close() {
if (this->sessionStarted) {
this->endSession();
}
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
std::string XplainedNano::getSerialNumber() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
);
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
void XplainedNano::startSession() {
using namespace CommandFrames::HouseKeeping;
void XplainedNano::startSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::StartSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Xplained Nano!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Xplained Nano!");
}
this->sessionStarted = true;
}
this->sessionStarted = true;
}
void XplainedNano::endSession() {
using namespace CommandFrames::HouseKeeping;
void XplainedNano::endSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::EndSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Xplained Nano!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Xplained Nano!");
}
this->sessionStarted = false;
}
this->sessionStarted = false;
}

View File

@@ -3,91 +3,99 @@
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers
{
using namespace Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
void XplainedPro::init() {
UsbDevice::init();
void XplainedPro::init() {
UsbDevice::init();
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
// TODO: Move away from hard-coding the CMSIS-DAP/EDBG interface number
auto& usbHidInterface = this->getEdbgInterface().getUsbHidInterface();
usbHidInterface.setNumber(0);
usbHidInterface.setLibUsbDevice(this->libUsbDevice);
usbHidInterface.setLibUsbDeviceHandle(this->libUsbDeviceHandle);
usbHidInterface.setVendorId(this->vendorId);
usbHidInterface.setProductId(this->productId);
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
usbHidInterface.init();
if (!usbHidInterface.isInitialised()) {
usbHidInterface.detachKernelDriver();
usbHidInterface.init();
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
if (!this->sessionStarted) {
this->startSession();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
/*
* The Xplained Pro debug tool returns incorrect data for any read memory command that exceeds 256 bytes in the
* size of the read request, despite the fact that the HID report size is 512 bytes. The debug tool doesn't
* report any error, it just returns incorrect data.
*
* This means we must enforce a hard limit on the number of bytes we attempt to access, per request.
*/
this->edbgAvr8Interface->setMaximumMemoryAccessSizePerRequest(256);
this->setInitialised(true);
}
this->getEdbgInterface().setMinimumCommandTimeGap(std::chrono::milliseconds(35));
void XplainedPro::close() {
if (this->sessionStarted) {
this->endSession();
}
if (!this->sessionStarted) {
this->startSession();
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
this->edbgAvr8Interface = std::make_unique<EdbgAvr8Interface>(this->edbgInterface);
std::string XplainedPro::getSerialNumber() {
using namespace CommandFrames::Discovery;
/*
* The Xplained Pro debug tool returns incorrect data for any read memory command that exceeds 256 bytes in the
* size of the read request, despite the fact that the HID report size is 512 bytes. The debug tool doesn't
* report any error, it just returns incorrect data.
*
* This means we must enforce a hard limit on the number of bytes we attempt to access, per request.
*/
this->edbgAvr8Interface->setMaximumMemoryAccessSizePerRequest(256);
this->setInitialised(true);
}
void XplainedPro::close() {
if (this->sessionStarted) {
this->endSession();
}
this->getEdbgInterface().getUsbHidInterface().close();
UsbDevice::close();
}
std::string XplainedPro::getSerialNumber() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::Discovery::Query(CommandFrames::Discovery::QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != CommandFrames::Discovery::ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
Query(QueryContext::SERIAL_NUMBER)
);
if (response.getResponseId() != ResponseId::OK) {
throw DeviceInitializationFailure(
"Failed to fetch serial number from device - invalid Discovery Protocol response ID."
);
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
auto data = response.getPayloadData();
return std::string(data.begin(), data.end());
}
void XplainedPro::startSession() {
using namespace CommandFrames::HouseKeeping;
void XplainedPro::startSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::StartSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
StartSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Xplained Pro!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceInitializationFailure("Failed to start session with Xplained Pro!");
}
this->sessionStarted = true;
}
this->sessionStarted = true;
}
void XplainedPro::endSession() {
using namespace CommandFrames::HouseKeeping;
void XplainedPro::endSession() {
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
CommandFrames::HouseKeeping::EndSession()
);
auto response = this->getEdbgInterface().sendAvrCommandFrameAndWaitForResponseFrame(
EndSession()
);
if (response.getResponseId() == CommandFrames::HouseKeeping::ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Xplained Pro!");
if (response.getResponseId() == ResponseId::FAILED) {
// Failed response returned!
throw DeviceFailure("Failed to end session with Xplained Pro!");
}
this->sessionStarted = false;
}
this->sessionStarted = false;
}

View File

@@ -6,45 +6,47 @@
#include "src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.hpp"
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
{
using namespace Bloom::Exceptions;
void CmsisDapInterface::sendCommand(const Command& cmsisDapCommand) {
if (this->msSendCommandDelay.count() > 0) {
using namespace std::chrono;
std::int64_t now = duration_cast<milliseconds>(high_resolution_clock::now().time_since_epoch()).count();
std::int64_t difference = (now - this->lastCommandSentTimeStamp);
void CmsisDapInterface::sendCommand(const Command& cmsisDapCommand) {
if (this->msSendCommandDelay.count() > 0) {
using namespace std::chrono;
std::int64_t now = duration_cast<milliseconds>(high_resolution_clock::now().time_since_epoch()).count();
std::int64_t difference = (now - this->lastCommandSentTimeStamp);
if (difference < this->msSendCommandDelay.count()) {
std::this_thread::sleep_for(milliseconds(this->msSendCommandDelay.count() - difference));
if (difference < this->msSendCommandDelay.count()) {
std::this_thread::sleep_for(milliseconds(this->msSendCommandDelay.count() - difference));
}
this->lastCommandSentTimeStamp = now;
}
this->lastCommandSentTimeStamp = now;
this->getUsbHidInterface().write(static_cast<std::vector<unsigned char>>(cmsisDapCommand));
}
this->getUsbHidInterface().write(static_cast<std::vector<unsigned char>>(cmsisDapCommand));
}
std::unique_ptr<Response> CmsisDapInterface::getResponse() {
auto rawResponse = this->getUsbHidInterface().read(10000);
std::unique_ptr<Response> CmsisDapInterface::getResponse() {
auto rawResponse = this->getUsbHidInterface().read(10000);
if (rawResponse.empty()) {
throw DeviceCommunicationFailure("Empty CMSIS-DAP response received");
}
if (rawResponse.empty()) {
throw DeviceCommunicationFailure("Empty CMSIS-DAP response received");
auto response = std::make_unique<Response>(Response());
response->init(rawResponse);
return response;
}
auto response = std::make_unique<Response>(Response());
response->init(rawResponse);
return response;
}
std::unique_ptr<Response> CmsisDapInterface::sendCommandAndWaitForResponse(const Command& cmsisDapCommand) {
this->sendCommand(cmsisDapCommand);
auto response = this->getResponse();
std::unique_ptr<Response> CmsisDapInterface::sendCommandAndWaitForResponse(const Command& cmsisDapCommand) {
this->sendCommand(cmsisDapCommand);
auto response = this->getResponse();
if (response->getResponseId() != cmsisDapCommand.getCommandId()) {
// This response is not what we were expecting
throw DeviceCommunicationFailure("Unexpected response to CMSIS-DAP command.");
}
if (response->getResponseId() != cmsisDapCommand.getCommandId()) {
// This response is not what we were expecting
throw DeviceCommunicationFailure("Unexpected response to CMSIS-DAP command.");
return response;
}
return response;
}

View File

@@ -1,11 +1,12 @@
#include "Command.hpp"
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
{
Command::operator std::vector<unsigned char>() const {
auto rawCommand = std::vector<unsigned char>(1, this->getCommandId());
auto commandData = this->getData();
rawCommand.insert(rawCommand.end(), commandData.begin(), commandData.end());
Command::operator std::vector<unsigned char> () const {
auto rawCommand = std::vector<unsigned char>(1, this->getCommandId());
auto commandData = this->getData();
rawCommand.insert(rawCommand.end(), commandData.begin(), commandData.end());
return rawCommand;
return rawCommand;
}
}

View File

@@ -2,13 +2,14 @@
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap
{
void Response::init(const std::vector<unsigned char>& rawResponse) {
if (rawResponse.empty()) {
throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response");
}
void Response::init(const std::vector<unsigned char>& rawResponse) {
if (rawResponse.empty()) {
throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response");
this->setResponseId(rawResponse[0]);
this->setData(std::vector<unsigned char>(rawResponse.begin() + 1, rawResponse.end()));
}
this->setResponseId(rawResponse[0]);
this->setData(std::vector<unsigned char>(rawResponse.begin() + 1, rawResponse.end()));
}

View File

@@ -1,22 +1,23 @@
#include "AvrCommand.hpp"
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
{
std::vector<unsigned char> AvrCommand::getData() const {
std::vector<unsigned char> data;
auto commandPacket = this->getCommandPacket();
std::size_t commandPacketSize = commandPacket.size();
data.resize(3 + commandPacketSize);
// FragmentInfo byte
data[0] = static_cast<unsigned char>((this->getFragmentNumber() << 4) | this->getFragmentCount());
std::vector<unsigned char> AvrCommand::getData() const {
std::vector<unsigned char> data;
auto commandPacket = this->getCommandPacket();
std::size_t commandPacketSize = commandPacket.size();
data.resize(3 + commandPacketSize);
// FragmentInfo byte
data[0] = static_cast<unsigned char>((this->getFragmentNumber() << 4) | this->getFragmentCount());
// Size byte
data[1] = static_cast<unsigned char>(commandPacketSize >> 8);
data[2] = static_cast<unsigned char>(commandPacketSize & 0xFF);
// Size byte
data[1] = (unsigned char) (commandPacketSize >> 8);
data[2] = (unsigned char) (commandPacketSize & 0xFF);
if (commandPacketSize > 0) {
data.insert(data.begin() + 3, commandPacket.begin(), commandPacket.end());
}
if (commandPacketSize > 0) {
data.insert(data.begin() + 3, commandPacket.begin(), commandPacket.end());
return data;
}
return data;
}

View File

@@ -2,48 +2,50 @@
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
{
using namespace Bloom::Exceptions;
void AvrEvent::init(const std::vector<unsigned char>& rawResponse) {
Response::init(rawResponse);
void AvrEvent::init(const std::vector<unsigned char>& rawResponse) {
Response::init(rawResponse);
if (this->getResponseId() != 0x82) {
throw Exception("Failed to construct AvrEvent object - invalid response ID.");
}
if (this->getResponseId() != 0x82) {
throw Exception("Failed to construct AvrEvent object - invalid response ID.");
}
auto& responseData = this->getData();
const auto& responseData = this->getData();
if (responseData.size() < 2) {
// All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID)
throw Exception("Failed to construct AvrEvent object - AVR_EVT response "
"returned no additional data.");
}
if (responseData.size() < 2) {
// All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID)
throw Exception("Failed to construct AvrEvent object - AVR_EVT response "
"returned no additional data.");
}
// Response size is two bytes, MSB
auto responsePacketSize = static_cast<std::size_t>((responseData[0] << 8) | responseData[1]);
// Response size is two bytes, MSB
auto responsePacketSize = static_cast<std::size_t>((responseData[0] << 8) | responseData[1]);
if (responseData.size() < 2) {
// All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID)
throw Exception("Failed to construct AvrEvent object - AVR_EVT response "
"contained invalid event data size.");
}
if (responseData.size() < 2) {
// All AVR_EVT responses should consist of at least two bytes (excluding the AVR_EVT ID)
throw Exception("Failed to construct AvrEvent object - AVR_EVT response "
"contained invalid event data size.");
}
auto eventData = std::vector<unsigned char>();
auto eventData = std::vector<unsigned char>();
/*
* Ignore the SOF, protocol version &handler id and sequence ID (with all make up 5 bytes in total, 7 when
* you include the two size bytes)
*/
eventData.insert(
eventData.end(),
responseData.begin() + 7,
responseData.begin() + 7 + static_cast<long>(responsePacketSize)
);
/*
* Ignore the SOF, protocol version &handler id and sequence ID (with all make up 5 bytes in total, 7 when
* you include the two size bytes)
*/
eventData.insert(
eventData.end(),
responseData.begin() + 7,
responseData.begin() + 7 + static_cast<long>(responsePacketSize)
);
this->setEventData(eventData);
this->setEventData(eventData);
if (!eventData.empty()) {
this->eventId = eventData[0];
if (!eventData.empty()) {
this->eventId = eventData[0];
}
}
}

View File

@@ -2,40 +2,42 @@
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
{
using namespace Bloom::Exceptions;
void AvrResponse::init(const std::vector<unsigned char>& rawResponse) {
Response::init(rawResponse);
void AvrResponse::init(const std::vector<unsigned char>& rawResponse) {
Response::init(rawResponse);
if (this->getResponseId() != 0x81) {
throw Exception("Failed to construct AvrResponse object - invalid response ID.");
if (this->getResponseId() != 0x81) {
throw Exception("Failed to construct AvrResponse object - invalid response ID.");
}
const auto& responseData = this->getData();
if (responseData.empty()) {
// All AVR responses should contain at least one byte (the fragment info byte)
throw Exception("Failed to construct AvrResponse object - AVR_RSP response "
"returned no additional data");
}
if (responseData[0] == 0x00) {
// This AVR Response contains no data (the device had no data to send), so we can stop here.
return;
}
this->setFragmentCount(static_cast<std::uint8_t>(responseData[0] & 0x0FU));
this->setFragmentNumber(static_cast<std::uint8_t>(responseData[0] >> 4));
// Response size is two bytes, MSB
const auto responsePacketSize = static_cast<std::size_t>((responseData[1] << 8U) + responseData[2]);
std::vector<unsigned char> responsePacket;
responsePacket.resize(responsePacketSize);
for (std::size_t i = 0; i < responsePacketSize; i++) {
responsePacket[i] = responseData[i + 3];
}
this->setResponsePacket(responsePacket);
}
auto& responseData = this->getData();
if (responseData.empty()) {
// All AVR responses should contain at least one byte (the fragment info byte)
throw Exception("Failed to construct AvrResponse object - AVR_RSP response "
"returned no additional data");
}
if (responseData[0] == 0x00) {
// This AVR Response contains no data (the device had no data to send), so we can stop here.
return;
}
this->setFragmentCount(static_cast<std::uint8_t>(responseData[0] & 0x0Fu));
this->setFragmentNumber(static_cast<std::uint8_t>(responseData[0] >> 4));
// Response size is two bytes, MSB
std::size_t responsePacketSize = static_cast<std::size_t>((responseData[1] << 8u) + responseData[2]);
std::vector<unsigned char> responsePacket;
responsePacket.resize(responsePacketSize);
for (std::size_t i = 0; i < responsePacketSize; i++) {
responsePacket[i] = responseData[i + 3];
}
this->setResponsePacket(responsePacket);
}

View File

@@ -3,54 +3,55 @@
#include <bitset>
#include <cmath>
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr::CommandFrames::Avr8Generic
{
std::vector<unsigned char> ReadMemory::getPayload() const {
/*
* The read memory command consists of 11/11 + (this->bytes / 8) bytes:
* 1. Command ID (0x21 for the general read memory command, 0x22 for reading with a mask)
* 2. Version (0x00)
* 3. Memory type (Avr8MemoryType)
* 4. Start address (4 bytes)
* 5. Number of bytes to read (4 bytes)
* 6. Mask to apply (this->bytes / 8) - only required if we're using the masked read command (command ID 0x22).
*/
auto output = std::vector<unsigned char>(11, 0x00);
output[0] = this->excludedAddresses.empty() ? 0x21 : 0x22;
output[1] = 0x00;
output[2] = static_cast<unsigned char>(this->type);
output[3] = static_cast<unsigned char>(this->address);
output[4] = static_cast<unsigned char>(this->address >> 8);
output[5] = static_cast<unsigned char>(this->address >> 16);
output[6] = static_cast<unsigned char>(this->address >> 24);
output[7] = static_cast<unsigned char>(this->bytes);
output[8] = static_cast<unsigned char>(this->bytes >> 8);
output[9] = static_cast<unsigned char>(this->bytes >> 16);
output[10] = static_cast<unsigned char>(this->bytes >> 24);
std::vector<unsigned char> ReadMemory::getPayload() const {
/*
* The read memory command consists of 11/11 + (this->bytes / 8) bytes:
* 1. Command ID (0x21 for the general read memory command, 0x22 for reading with a mask)
* 2. Version (0x00)
* 3. Memory type (Avr8MemoryType)
* 4. Start address (4 bytes)
* 5. Number of bytes to read (4 bytes)
* 6. Mask to apply (this->bytes / 8) - only required if we're using the masked read command (command ID 0x22).
*/
auto output = std::vector<unsigned char>(11, 0x00);
output[0] = this->excludedAddresses.empty() ? 0x21 : 0x22;
output[1] = 0x00;
output[2] = static_cast<unsigned char>(this->type);
output[3] = static_cast<unsigned char>(this->address);
output[4] = static_cast<unsigned char>(this->address >> 8);
output[5] = static_cast<unsigned char>(this->address >> 16);
output[6] = static_cast<unsigned char>(this->address >> 24);
output[7] = static_cast<unsigned char>(this->bytes);
output[8] = static_cast<unsigned char>(this->bytes >> 8);
output[9] = static_cast<unsigned char>(this->bytes >> 16);
output[10] = static_cast<unsigned char>(this->bytes >> 24);
if (!this->excludedAddresses.empty()) {
const auto endAddress = this->address + (this->bytes - 1);
if (!this->excludedAddresses.empty()) {
const auto endAddress = this->address + (this->bytes - 1);
constexpr auto byteBitSize = std::numeric_limits<unsigned char>::digits;
auto byteBitset = std::bitset<byteBitSize>();
byteBitset.reset();
constexpr auto byteBitSize = std::numeric_limits<unsigned char>::digits;
auto byteBitset = std::bitset<byteBitSize>();
byteBitset.reset();
for (std::uint32_t address = this->address; address <= endAddress; address++) {
auto addressIndex = address - this->address;
auto bitIndex = static_cast<std::size_t>(
addressIndex - (std::floor(addressIndex / byteBitSize) * byteBitSize)
);
for (std::uint32_t address = this->address; address <= endAddress; address++) {
auto addressIndex = address - this->address;
auto bitIndex = static_cast<std::size_t>(
addressIndex - (std::floor(addressIndex / byteBitSize) * byteBitSize)
);
if (!this->excludedAddresses.contains(address)) {
byteBitset[bitIndex] = 1;
}
if (!this->excludedAddresses.contains(address)) {
byteBitset[bitIndex] = 1;
}
if (address > 0 && (bitIndex == 7 || address == endAddress)) {
output.emplace_back(byteBitset.to_ulong());
byteBitset.reset();
if (address > 0 && (bitIndex == 7 || address == endAddress)) {
output.emplace_back(byteBitset.to_ulong());
byteBitset.reset();
}
}
}
}
return output;
return output;
}
}

View File

@@ -2,62 +2,63 @@
#include <math.h>
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
std::vector<AvrCommand> AvrCommandFrame::generateAvrCommands(std::size_t maximumCommandPacketSize) const {
auto rawCommandFrame = static_cast<std::vector<unsigned char>>(*this);
std::size_t commandFrameSize = rawCommandFrame.size();
auto commandsRequired = static_cast<std::size_t>(
ceil(static_cast<float>(commandFrameSize) / static_cast<float>(maximumCommandPacketSize))
);
std::vector<AvrCommand> avrCommands;
std::size_t copiedPacketSize = 0;
for (std::size_t i = 0; i < commandsRequired; i++) {
AvrCommand avrCommand;
avrCommand.setFragmentCount(commandsRequired);
avrCommand.setFragmentNumber(i + 1);
auto commandPacket = avrCommand.getCommandPacket();
// If we're on the last packet, the packet size will be what ever is left of the AvrCommandFrame
std::size_t commandPacketSize = ((i + 1) != commandsRequired) ? maximumCommandPacketSize
: (commandFrameSize - (maximumCommandPacketSize * i));
commandPacket.insert(
commandPacket.end(),
rawCommandFrame.begin() + static_cast<long>(copiedPacketSize),
rawCommandFrame.begin() + static_cast<long>(copiedPacketSize + commandPacketSize)
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
{
std::vector<AvrCommand> AvrCommandFrame::generateAvrCommands(std::size_t maximumCommandPacketSize) const {
auto rawCommandFrame = static_cast<std::vector<unsigned char>>(*this);
std::size_t commandFrameSize = rawCommandFrame.size();
auto commandsRequired = static_cast<std::size_t>(
ceil(static_cast<float>(commandFrameSize) / static_cast<float>(maximumCommandPacketSize))
);
avrCommand.setCommandPacket(commandPacket);
avrCommands.push_back(avrCommand);
copiedPacketSize += commandPacketSize;
std::vector<AvrCommand> avrCommands;
std::size_t copiedPacketSize = 0;
for (std::size_t i = 0; i < commandsRequired; i++) {
AvrCommand avrCommand;
avrCommand.setFragmentCount(commandsRequired);
avrCommand.setFragmentNumber(i + 1);
auto commandPacket = avrCommand.getCommandPacket();
// If we're on the last packet, the packet size will be what ever is left of the AvrCommandFrame
std::size_t commandPacketSize = ((i + 1) != commandsRequired) ? maximumCommandPacketSize
: (commandFrameSize - (maximumCommandPacketSize * i));
commandPacket.insert(
commandPacket.end(),
rawCommandFrame.begin() + static_cast<long>(copiedPacketSize),
rawCommandFrame.begin() + static_cast<long>(copiedPacketSize + commandPacketSize)
);
avrCommand.setCommandPacket(commandPacket);
avrCommands.push_back(avrCommand);
copiedPacketSize += commandPacketSize;
}
return avrCommands;
}
return avrCommands;
}
AvrCommandFrame::operator std::vector<unsigned char>() const {
auto data = this->getPayload();
auto dataSize = data.size();
AvrCommandFrame::operator std::vector<unsigned char> () const {
auto data = this->getPayload();
auto dataSize = data.size();
auto rawCommand = std::vector<unsigned char>(5);
auto rawCommand = std::vector<unsigned char>(5);
rawCommand[0] = this->SOF;
rawCommand[1] = this->getProtocolVersion();
rawCommand[0] = this->SOF;
rawCommand[1] = this->getProtocolVersion();
rawCommand[2] = static_cast<unsigned char>(this->getSequenceId());
rawCommand[3] = static_cast<unsigned char>(this->getSequenceId() >> 8);
rawCommand[2] = static_cast<unsigned char>(this->getSequenceId());
rawCommand[3] = static_cast<unsigned char>(this->getSequenceId() >> 8);
rawCommand[4] = static_cast<unsigned char>(this->getProtocolHandlerId());
rawCommand[4] = static_cast<unsigned char>(this->getProtocolHandlerId());
if (dataSize > 0) {
rawCommand.insert(
rawCommand.end(),
data.begin(),
data.end()
);
}
if (dataSize > 0) {
rawCommand.insert(
rawCommand.end(),
data.begin(),
data.end()
);
return rawCommand;
}
return rawCommand;
}

View File

@@ -2,28 +2,33 @@
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Targets;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
{
using namespace Bloom::Exceptions;
void BreakEvent::init(const AvrEvent& event) {
AvrEvent::init(event);
auto& data = this->getEventData();
using Bloom::Targets::TargetBreakCause;
if (data.size() < 8) {
/*
* All BreakEvent packets must consist of at least 9 bytes:
* 1 byte for event ID
* 4 bytes for program counter
* 1 byte for break cause
* 2 bytes for extended info
*/
throw Exception("Failed to process BreakEvent from AvrEvent - unexpected packet size.");
void BreakEvent::init(const AvrEvent& event) {
AvrEvent::init(event);
const auto& data = this->getEventData();
if (data.size() < 8) {
/*
* All BreakEvent packets must consist of at least 9 bytes:
* 1 byte for event ID
* 4 bytes for program counter
* 1 byte for break cause
* 2 bytes for extended info
*/
throw Exception("Failed to process BreakEvent from AvrEvent - unexpected packet size.");
}
// Program counter consists of 4 bytes
this->programCounter = static_cast<std::uint32_t>(
(data[4] << 24) | (data[3] << 16) | (data[2] << 8) | data[1]
) * 2;
// Break cause is 1 byte, where 0x01 is 'program breakpoint' and 0x00 'unspecified'
this->breakCause = data[7] == 0x01 ? TargetBreakCause::BREAKPOINT : TargetBreakCause::UNKNOWN;
}
// Program counter consists of 4 bytes
this->programCounter = static_cast<std::uint32_t>((data[4] << 24) | (data[3] << 16) | (data[2] << 8) | data[1]) * 2;
// Break cause is 1 byte, where 0x01 is 'program breakpoint' and 0x00 'unspecified'
this->breakCause = data[7] == 0x01 ? TargetBreakCause::BREAKPOINT : TargetBreakCause::UNKNOWN;
}

View File

@@ -2,36 +2,38 @@
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr
{
using namespace Bloom::Exceptions;
void AvrResponseFrame::initFromAvrResponses(const std::vector<AvrResponse>& avrResponses) {
// Build a raw frame buffer from the AVRResponse objects and just call initFromRawFrame()
std::vector<unsigned char> rawFrame;
void AvrResponseFrame::initFromAvrResponses(const std::vector<AvrResponse>& avrResponses) {
// Build a raw frame buffer from the AVRResponse objects and just call initFromRawFrame()
std::vector<unsigned char> rawFrame;
for (auto& avrResponse : avrResponses) {
auto responsePacket = avrResponse.getResponsePacket();
rawFrame.insert(rawFrame.end(), responsePacket.begin(), responsePacket.end());
for (const auto& avrResponse : avrResponses) {
auto responsePacket = avrResponse.getResponsePacket();
rawFrame.insert(rawFrame.end(), responsePacket.begin(), responsePacket.end());
}
return this->initFromRawFrame(rawFrame);
}
return this->initFromRawFrame(rawFrame);
}
void AvrResponseFrame::initFromRawFrame(const std::vector<unsigned char>& rawFrame) {
if (rawFrame.size() < 4) {
// All AVR response frames must consist of at least four bytes (SOF, sequence ID (two bytes) and
// a protocol handler ID)
throw Exception("Failed to construct AvrResponseFrame - unexpected end to raw frame");
}
if (rawFrame[0] != 0x0E) {
// The SOF field must always be 0x0E
throw Exception("Failed to construct AvrResponseFrame - unexpected SOF field value in raw frame");
}
this->setSequenceId(static_cast<std::uint16_t>((rawFrame[2] << 8) + rawFrame[1]));
this->setProtocolHandlerId(rawFrame[3]);
auto& payload = this->getPayload();
payload.insert(payload.begin(), rawFrame.begin() + 4, rawFrame.end());
void AvrResponseFrame::initFromRawFrame(const std::vector<unsigned char>& rawFrame) {
if (rawFrame.size() < 4) {
// All AVR response frames must consist of at least four bytes (SOF, sequence ID (two bytes) and
// a protocol handler ID)
throw Exception("Failed to construct AvrResponseFrame - unexpected end to raw frame");
}
if (rawFrame[0] != 0x0E) {
// The SOF field must always be 0x0E
throw Exception("Failed to construct AvrResponseFrame - unexpected SOF field value in raw frame");
}
this->setSequenceId(static_cast<std::uint16_t>((rawFrame[2] << 8) + rawFrame[1]));
this->setProtocolHandlerId(rawFrame[3]);
auto& payload = this->getPayload();
payload.insert(payload.begin(), rawFrame.begin() + 4, rawFrame.end());
}
}

View File

@@ -4,95 +4,99 @@
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
using namespace Bloom::DebugToolDrivers;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg;
using namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg::Avr;
using namespace Bloom::Exceptions;
using namespace Bloom::Exceptions;
namespace Bloom::DebugToolDrivers::Protocols::CmsisDap::Edbg
{
using namespace Bloom::Exceptions;
Protocols::CmsisDap::Response EdbgInterface::sendAvrCommandFrameAndWaitForResponse(
const Protocols::CmsisDap::Edbg::Avr::AvrCommandFrame& avrCommandFrame
) {
// An AVR command frame can be split into multiple CMSIS-DAP commands. Each command
// containing a fragment of the AvrCommandFrame.
Protocols::CmsisDap::Response EdbgInterface::sendAvrCommandFrameAndWaitForResponse(
const Protocols::CmsisDap::Edbg::Avr::AvrCommandFrame& avrCommandFrame
) {
// An AVR command frame can be split into multiple CMSIS-DAP commands. Each command
// containing a fragment of the AvrCommandFrame.
// Minus 3 to accommodate AVR command meta data
std::size_t maximumCommandPacketSize = (this->getUsbHidInputReportSize() - 3);
// Minus 3 to accommodate AVR command meta data
std::size_t maximumCommandPacketSize = (this->getUsbHidInputReportSize() - 3);
auto avrCommands = avrCommandFrame.generateAvrCommands(maximumCommandPacketSize);
auto avrCommands = avrCommandFrame.generateAvrCommands(maximumCommandPacketSize);
for (auto& avrCommand : avrCommands) {
// Send command to device
auto response = this->sendCommandAndWaitForResponse(avrCommand);
for (auto& avrCommand : avrCommands) {
// Send command to device
auto response = this->sendCommandAndWaitForResponse(avrCommand);
if (&avrCommand ==& avrCommands.back()) {
return* response;
if (&avrCommand == &avrCommands.back()) {
return *response;
}
}
// This should never happen
throw DeviceCommunicationFailure(
"Cannot send AVR command frame - failed to generate CMSIS-DAP Vendor (AVR) commands"
);
}
// This should never happen
throw DeviceCommunicationFailure(
"Cannot send AVR command frame - failed to generate CMSIS-DAP Vendor (AVR) commands"
);
}
Protocols::CmsisDap::Edbg::Avr::AvrResponse EdbgInterface::getAvrResponse() {
auto cmsisResponse = this->getResponse();
Protocols::CmsisDap::Edbg::Avr::AvrResponse EdbgInterface::getAvrResponse() {
auto cmsisResponse = this->getResponse();
if (cmsisResponse->getResponseId() == 0x81) {
// This is an AVR_RSP response
auto avrResponse = Protocols::CmsisDap::Edbg::Avr::AvrResponse();
avrResponse.init(*cmsisResponse);
return avrResponse;
}
if (cmsisResponse->getResponseId() == 0x81) {
// This is an AVR_RSP response
auto avrResponse = Protocols::CmsisDap::Edbg::Avr::AvrResponse();
avrResponse.init(*cmsisResponse);
return avrResponse;
} else {
throw DeviceCommunicationFailure("Unexpected response to AvrResponseCommand from device");
}
}
std::optional<Protocols::CmsisDap::Edbg::Avr::AvrEvent> EdbgInterface::requestAvrEvent() {
this->sendCommand(AvrEventCommand());
auto cmsisResponse = this->getResponse();
std::optional<Protocols::CmsisDap::Edbg::Avr::AvrEvent> EdbgInterface::requestAvrEvent() {
this->sendCommand(AvrEventCommand());
auto cmsisResponse = this->getResponse();
if (cmsisResponse->getResponseId() == 0x82) {
// This is an AVR_EVT response
auto avrEvent = Protocols::CmsisDap::Edbg::Avr::AvrEvent();
avrEvent.init(*cmsisResponse);
return avrEvent.getEventDataSize() > 0 ? std::optional(avrEvent) : std::nullopt;
}
if (cmsisResponse->getResponseId() == 0x82) {
// This is an AVR_EVT response
auto avrEvent = Protocols::CmsisDap::Edbg::Avr::AvrEvent();
avrEvent.init(*cmsisResponse);
return avrEvent.getEventDataSize() > 0 ? std::optional(avrEvent): std::nullopt;
} else {
throw DeviceCommunicationFailure("Unexpected response to AvrEventCommand from device");
}
}
std::vector<Protocols::CmsisDap::Edbg::Avr::AvrResponse> EdbgInterface::requestAvrResponses() {
std::vector<Protocols::CmsisDap::Edbg::Avr::AvrResponse> responses;
AvrResponseCommand responseCommand;
std::vector<Protocols::CmsisDap::Edbg::Avr::AvrResponse> EdbgInterface::requestAvrResponses() {
using Protocols::CmsisDap::Edbg::Avr::AvrResponseCommand;
this->sendCommand(responseCommand);
auto response = this->getAvrResponse();
responses.push_back(response);
int fragmentCount = response.getFragmentCount();
std::vector<Protocols::CmsisDap::Edbg::Avr::AvrResponse> responses;
AvrResponseCommand responseCommand;
while (responses.size() < fragmentCount) {
// There are more response packets
this->sendCommand(responseCommand);
response = this->getAvrResponse();
if (response.getFragmentCount() != fragmentCount) {
throw DeviceCommunicationFailure(
"Failed to fetch AVRResponse objects - invalid fragment count returned."
);
}
if (response.getFragmentCount() == 0 && response.getFragmentNumber() == 0) {
throw DeviceCommunicationFailure("Failed to fetch AVRResponse objects - unexpected empty response");
} else if (response.getFragmentNumber() == 0) {
// End of response data ( &this packet can be ignored)
break;
}
auto response = this->getAvrResponse();
responses.push_back(response);
}
int fragmentCount = response.getFragmentCount();
return responses;
while (responses.size() < fragmentCount) {
// There are more response packets
this->sendCommand(responseCommand);
response = this->getAvrResponse();
if (response.getFragmentCount() != fragmentCount) {
throw DeviceCommunicationFailure(
"Failed to fetch AVRResponse objects - invalid fragment count returned."
);
}
if (response.getFragmentCount() == 0 && response.getFragmentNumber() == 0) {
throw DeviceCommunicationFailure(
"Failed to fetch AVRResponse objects - unexpected empty response"
);
}
if (response.getFragmentNumber() == 0) {
// End of response data ( &this packet can be ignored)
break;
}
responses.push_back(response);
}
return responses;
}
}

View File

@@ -5,127 +5,133 @@
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
using namespace Bloom::Usb;
using namespace Bloom::Exceptions;
namespace Bloom::Usb
{
using namespace Bloom::Exceptions;
void HidInterface::init() {
if (this->libUsbDevice == nullptr) {
throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer.");
}
hid_init();
hid_device* hidDevice = nullptr;
std::string hidInterfacePath = this->getDevicePathByInterfaceNumber(this->getNumber());
Logger::debug("HID device path: " + hidInterfacePath);
if ((hidDevice = hid_open_path(hidInterfacePath.c_str())) == nullptr) {
throw DeviceInitializationFailure("Failed to open HID device via hidapi.");
}
if (hidDevice->input_ep_max_packet_size < 1) {
throw DeviceInitializationFailure(
"Invalid max packet size for USB endpoint, on interface " + std::to_string(this->getNumber())
);
}
this->setHidDevice(hidDevice);
this->setInputReportSize(static_cast<std::size_t>(hidDevice->input_ep_max_packet_size));
this->libUsbDeviceHandle = hidDevice->device_handle;
this->initialised = true;
}
void HidInterface::close() {
auto* hidDevice = this->getHidDevice();
if (hidDevice != nullptr) {
this->libUsbDeviceHandle = nullptr;
hid_close(hidDevice);
// hid_close() releases the interface
this->claimed = false;
}
hid_exit();
Interface::close();
}
std::vector<unsigned char> HidInterface::read(unsigned int timeout) {
std::vector<unsigned char> output;
auto readSize = this->getInputReportSize();
// Attempt to read the first HID report packet, and whatever is left after that.
output.resize(readSize);
auto transferredByteCount = this->read(output.data(), readSize, timeout);
auto totalByteCount = transferredByteCount;
while (transferredByteCount >= readSize) {
output.resize(totalByteCount + readSize);
transferredByteCount = this->read(output.data() + totalByteCount, readSize, 1);
totalByteCount += transferredByteCount;
}
output.resize(totalByteCount);
return output;
}
void HidInterface::write(std::vector<unsigned char> buffer) {
if (buffer.size() > this->getInputReportSize()) {
throw DeviceCommunicationFailure(
"Cannot send data via HID interface - data exceeds maximum packet size."
);
}
if (buffer.size() < this->getInputReportSize()) {
/*
* Every report we send via the USB HID interface should be of a fixed size.
* In the event of a report being too small, we just fill the buffer vector with 0.
*/
buffer.resize(this->getInputReportSize(), 0);
}
int transferred = 0;
auto length = buffer.size();
if ((transferred = hid_write(this->getHidDevice(), buffer.data(), length)) != length) {
Logger::debug("Attempted to write " + std::to_string(length)
+ " bytes to HID interface. Bytes written: " + std::to_string(transferred));
throw DeviceCommunicationFailure("Failed to write data to HID interface.");
}
}
std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) {
int transferred;
if ((transferred = hid_read_timeout(
this->hidDevice,
buffer,
maxLength,
timeout == 0 ? -1 : static_cast<int>(timeout))
) == -1
) {
throw DeviceCommunicationFailure("Failed to read from HID device.");
}
return static_cast<std::size_t>(transferred);
}
std::string HidInterface::getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber) {
hid_device_info* hidDeviceInfoList = hid_enumerate(this->getVendorId(), this->getProductId());
while (hidDeviceInfoList != nullptr) {
if (hidDeviceInfoList->interface_number == interfaceNumber) {
break;
void HidInterface::init() {
if (this->libUsbDevice == nullptr) {
throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer.");
}
hidDeviceInfoList = hidDeviceInfoList->next;
hid_init();
hid_device* hidDevice = nullptr;
std::string hidInterfacePath = this->getDevicePathByInterfaceNumber(this->getNumber());
Logger::debug("HID device path: " + hidInterfacePath);
if ((hidDevice = hid_open_path(hidInterfacePath.c_str())) == nullptr) {
throw DeviceInitializationFailure("Failed to open HID device via hidapi.");
}
if (hidDevice->input_ep_max_packet_size < 1) {
throw DeviceInitializationFailure(
"Invalid max packet size for USB endpoint, on interface "
+ std::to_string(this->getNumber())
);
}
this->setHidDevice(hidDevice);
this->setInputReportSize(static_cast<std::size_t>(hidDevice->input_ep_max_packet_size));
this->libUsbDeviceHandle = hidDevice->device_handle;
this->initialised = true;
}
if (hidDeviceInfoList == nullptr) {
throw DeviceInitializationFailure("Failed to match interface number with HID interface.");
void HidInterface::close() {
auto* hidDevice = this->getHidDevice();
if (hidDevice != nullptr) {
this->libUsbDeviceHandle = nullptr;
hid_close(hidDevice);
// hid_close() releases the interface
this->claimed = false;
}
hid_exit();
Interface::close();
}
auto path = std::string(hidDeviceInfoList->path);
hid_free_enumeration(hidDeviceInfoList);
return path;
std::vector<unsigned char> HidInterface::read(unsigned int timeout) {
std::vector<unsigned char> output;
auto readSize = this->getInputReportSize();
// Attempt to read the first HID report packet, and whatever is left after that.
output.resize(readSize);
auto transferredByteCount = this->read(output.data(), readSize, timeout);
auto totalByteCount = transferredByteCount;
while (transferredByteCount >= readSize) {
output.resize(totalByteCount + readSize);
transferredByteCount = this->read(output.data() + totalByteCount, readSize, 1);
totalByteCount += transferredByteCount;
}
output.resize(totalByteCount);
return output;
}
void HidInterface::write(std::vector<unsigned char> buffer) {
if (buffer.size() > this->getInputReportSize()) {
throw DeviceCommunicationFailure(
"Cannot send data via HID interface - data exceeds maximum packet size."
);
}
if (buffer.size() < this->getInputReportSize()) {
/*
* Every report we send via the USB HID interface should be of a fixed size.
* In the event of a report being too small, we just fill the buffer vector with 0.
*/
buffer.resize(this->getInputReportSize(), 0);
}
int transferred = 0;
auto length = buffer.size();
if ((transferred = hid_write(this->getHidDevice(), buffer.data(), length)) != length) {
Logger::debug("Attempted to write " + std::to_string(length)
+ " bytes to HID interface. Bytes written: " + std::to_string(transferred));
throw DeviceCommunicationFailure("Failed to write data to HID interface.");
}
}
std::size_t HidInterface::read(unsigned char* buffer, std::size_t maxLength, unsigned int timeout) {
int transferred;
if ((transferred = hid_read_timeout(
this->hidDevice,
buffer,
maxLength,
timeout == 0 ? -1 : static_cast<int>(timeout))
) == -1
) {
throw DeviceCommunicationFailure("Failed to read from HID device.");
}
return static_cast<std::size_t>(transferred);
}
std::string HidInterface::getDevicePathByInterfaceNumber(const std::uint16_t& interfaceNumber) {
hid_device_info* hidDeviceInfoList = hid_enumerate(
this->getVendorId(),
this->getProductId()
);
while (hidDeviceInfoList != nullptr) {
if (hidDeviceInfoList->interface_number == interfaceNumber) {
break;
}
hidDeviceInfoList = hidDeviceInfoList->next;
}
if (hidDeviceInfoList == nullptr) {
throw DeviceInitializationFailure("Failed to match interface number with HID interface.");
}
auto path = std::string(hidDeviceInfoList->path);
hid_free_enumeration(hidDeviceInfoList);
return path;
}
}

View File

@@ -1,121 +1,123 @@
#include "Interface.hpp"
#include <libusb-1.0/libusb.h>
#include <chrono>
#include "src/TargetController/Exceptions/DeviceFailure.hpp"
#include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using namespace Bloom::Usb;
using namespace Bloom::Exceptions;
namespace Bloom::Usb
{
using namespace Bloom::Exceptions;
void Interface::init() {
if (this->libUsbDevice == nullptr) {
throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer.");
}
if (this->libUsbDeviceHandle == nullptr) {
throw DeviceInitializationFailure("Cannot initialise interface without libusb device handle.");
}
this->initialised = true;
}
void Interface::close() {
if (this->libUsbDeviceHandle != nullptr) {
this->release();
}
this->initialised = false;
}
void Interface::claim() {
int interfaceNumber = this->getNumber();
this->detachKernelDriver();
if (libusb_claim_interface(this->libUsbDeviceHandle, interfaceNumber) != 0) {
throw DeviceInitializationFailure(
"Failed to claim interface {" + std::to_string(interfaceNumber) + "} on USB device\n"
);
}
this->claimed = true;
}
void Interface::detachKernelDriver() {
int interfaceNumber = this->getNumber();
int libUsbStatusCode;
if ((libUsbStatusCode = libusb_kernel_driver_active(this->libUsbDeviceHandle, interfaceNumber)) != 0) {
if (libUsbStatusCode == 1) {
// A kernel driver is active on this interface. Attempt to detach it
if (libusb_detach_kernel_driver(this->libUsbDeviceHandle, interfaceNumber) != 0) {
throw DeviceInitializationFailure("Failed to detach kernel driver from interface " +
std::to_string(interfaceNumber) + "\n");
}
} else {
throw DeviceInitializationFailure("Failed to check for active kernel driver on USB interface.");
void Interface::init() {
if (this->libUsbDevice == nullptr) {
throw DeviceInitializationFailure("Cannot initialise interface without libusb device pointer.");
}
}
}
void Interface::release() {
if (this->isClaimed()) {
if (libusb_release_interface(this->libUsbDeviceHandle, this->getNumber()) != 0) {
throw DeviceFailure(
"Failed to release interface {" + std::to_string(this->getNumber()) + "} on USB device\n"
if (this->libUsbDeviceHandle == nullptr) {
throw DeviceInitializationFailure("Cannot initialise interface without libusb device handle.");
}
this->initialised = true;
}
void Interface::close() {
if (this->libUsbDeviceHandle != nullptr) {
this->release();
}
this->initialised = false;
}
void Interface::claim() {
int interfaceNumber = this->getNumber();
this->detachKernelDriver();
if (libusb_claim_interface(this->libUsbDeviceHandle, interfaceNumber) != 0) {
throw DeviceInitializationFailure(
"Failed to claim interface {" + std::to_string(interfaceNumber) + "} on USB device\n"
);
}
this->claimed = false;
this->claimed = true;
}
}
int Interface::read(unsigned char* buffer, unsigned char endPoint, size_t length, size_t timeout) {
int totalTransferred = 0;
int transferred = 0;
int libUsbStatusCode = 0;
void Interface::detachKernelDriver() {
int interfaceNumber = this->getNumber();
int libUsbStatusCode;
if ((libUsbStatusCode = libusb_kernel_driver_active(this->libUsbDeviceHandle, interfaceNumber)) != 0) {
if (libUsbStatusCode == 1) {
// A kernel driver is active on this interface. Attempt to detach it
if (libusb_detach_kernel_driver(this->libUsbDeviceHandle, interfaceNumber) != 0) {
throw DeviceInitializationFailure("Failed to detach kernel driver from interface " +
std::to_string(interfaceNumber) + "\n");
}
} else {
throw DeviceInitializationFailure("Failed to check for active kernel driver on USB interface.");
}
}
}
void Interface::release() {
if (this->isClaimed()) {
if (libusb_release_interface(this->libUsbDeviceHandle, this->getNumber()) != 0) {
throw DeviceFailure(
"Failed to release interface {" + std::to_string(this->getNumber())
+ "} on USB device\n"
);
}
this->claimed = false;
}
}
int Interface::read(unsigned char* buffer, unsigned char endPoint, size_t length, size_t timeout) {
int totalTransferred = 0;
int transferred = 0;
int libUsbStatusCode = 0;
while (length > totalTransferred) {
libUsbStatusCode = libusb_interrupt_transfer(
this->libUsbDeviceHandle,
endPoint,
buffer,
static_cast<int>(length),
&transferred,
static_cast<unsigned int>(timeout)
);
if (libUsbStatusCode != 0 && libUsbStatusCode != -7) {
throw DeviceCommunicationFailure(
"Failed to read from USB device. Error code returned: " + std::to_string(libUsbStatusCode)
);
}
totalTransferred += transferred;
}
return transferred;
}
void Interface::write(unsigned char* buffer, unsigned char endPoint, int length) {
int transferred = 0;
int libUsbStatusCode = 0;
while (length > totalTransferred) {
libUsbStatusCode = libusb_interrupt_transfer(
this->libUsbDeviceHandle,
endPoint,
buffer,
static_cast<int>(length),
length,
&transferred,
static_cast<unsigned int>(timeout)
0
);
if (libUsbStatusCode != 0 && libUsbStatusCode != -7) {
if (libUsbStatusCode != 0) {
throw DeviceCommunicationFailure(
"Failed to read from USB device. Error code returned: " + std::to_string(libUsbStatusCode)
);
}
totalTransferred += transferred;
}
return transferred;
}
void Interface::write(unsigned char* buffer, unsigned char endPoint, int length) {
int transferred = 0;
int libUsbStatusCode = 0;
libUsbStatusCode = libusb_interrupt_transfer(
this->libUsbDeviceHandle,
endPoint,
buffer,
length,
&transferred,
0
);
if (libUsbStatusCode != 0) {
throw DeviceCommunicationFailure(
"Failed to read from USB device. Error code returned: " + std::to_string(libUsbStatusCode)
);
}
}

View File

@@ -3,102 +3,107 @@
#include "src/Logger/Logger.hpp"
#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp"
using Bloom::Usb::UsbDevice;
using namespace Bloom::Exceptions;
namespace Bloom::Usb
{
using namespace Bloom::Exceptions;
void UsbDevice::init() {
libusb_init(&this->libUsbContext);
// libusb_set_option(this->libUsbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE);
auto devices = this->findMatchingDevices();
void UsbDevice::init() {
libusb_init(&this->libUsbContext);
// libusb_set_option(this->libUsbContext, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE);
auto devices = this->findMatchingDevices();
if (devices.empty()) {
throw DeviceInitializationFailure("Failed to find USB device with matching vendor & product ID.");
} else if (devices.size() > 1) {
// TODO: implement support for multiple devices (maybe via serial number?)
throw DeviceInitializationFailure(
"Numerous devices of matching vendor & product ID found.\n"
"Please ensure that only one debug tool is connected and then try again."
);
}
// For now, just use the first device found.
auto device = devices.front();
this->setLibUsbDevice(device);
int libUsbStatusCode;
// Obtain a device handle from libusb
if ((libUsbStatusCode = libusb_open(libUsbDevice, &this->libUsbDeviceHandle)) < 0) {
throw DeviceInitializationFailure(
"Failed to open USB device - error code " + std::to_string(libUsbStatusCode) + " returned."
);
}
}
void UsbDevice::setConfiguration(int configIndex) {
libusb_config_descriptor* configDescriptor = {};
int libUsbStatusCode;
if ((libUsbStatusCode = libusb_get_config_descriptor(this->libUsbDevice, 0, &configDescriptor))) {
throw DeviceInitializationFailure(
"Failed to obtain USB configuration descriptor - error code " + std::to_string(libUsbStatusCode)
+ " returned."
);
}
if ((libUsbStatusCode = libusb_set_configuration(this->libUsbDeviceHandle, configDescriptor->bConfigurationValue))) {
throw DeviceInitializationFailure(
"Failed to set USB configuration - error code " + std::to_string(libUsbStatusCode) + " returned."
);
}
libusb_free_config_descriptor(configDescriptor);
}
std::vector<libusb_device*> UsbDevice::findMatchingDevices(
std::optional<std::uint16_t> vendorId, std::optional<std::uint16_t> productId
) {
auto libUsbContext = this->libUsbContext;
libusb_device** devices = nullptr;
libusb_device* device;
std::vector<libusb_device*> matchedDevices;
ssize_t i = 0, libUsbStatusCode;
auto vendorIdToMatch = vendorId.value_or(this->vendorId);
auto productIdToMatch = productId.value_or(this->productId);
if ((libUsbStatusCode = libusb_get_device_list(libUsbContext, &devices)) < 0) {
throw DeviceInitializationFailure(
"Failed to retrieve USB devices - return code: '" + std::to_string(libUsbStatusCode) + "'"
);
}
while ((device = devices[i++]) != nullptr) {
struct libusb_device_descriptor desc = {};
if ((libUsbStatusCode = libusb_get_device_descriptor(device, &desc)) < 0) {
Logger::warning("Failed to retrieve USB device descriptor - return code: '"
+ std::to_string(libUsbStatusCode) + "'");
continue;
if (devices.empty()) {
throw DeviceInitializationFailure("Failed to find USB device with matching vendor & product ID.");
}
if (desc.idVendor == vendorIdToMatch && desc.idProduct == productIdToMatch) {
matchedDevices.push_back(device);
if (devices.size() > 1) {
// TODO: implement support for multiple devices (maybe via serial number?)
throw DeviceInitializationFailure(
"Numerous devices of matching vendor & product ID found.\n"
"Please ensure that only one debug tool is connected and then try again."
);
}
// For now, just use the first device found.
auto* device = devices.front();
this->setLibUsbDevice(device);
const int libUsbStatusCode = libusb_open(libUsbDevice, &this->libUsbDeviceHandle);
// Obtain a device handle from libusb
if (libUsbStatusCode < 0) {
throw DeviceInitializationFailure(
"Failed to open USB device - error code " + std::to_string(libUsbStatusCode) + " returned."
);
}
}
libusb_free_device_list(devices, 1);
return matchedDevices;
}
void UsbDevice::setConfiguration(int configIndex) {
libusb_config_descriptor* configDescriptor = {};
int libUsbStatusCode = libusb_get_config_descriptor(this->libUsbDevice, 0, &configDescriptor);
void UsbDevice::close() {
if (this->libUsbDeviceHandle != nullptr) {
libusb_close(this->libUsbDeviceHandle);
this->libUsbDeviceHandle = nullptr;
if (libUsbStatusCode < 0) {
throw DeviceInitializationFailure(
"Failed to obtain USB configuration descriptor - error code " + std::to_string(libUsbStatusCode)
+ " returned."
);
}
libUsbStatusCode = libusb_set_configuration(this->libUsbDeviceHandle, configDescriptor->bConfigurationValue);
if (libUsbStatusCode < 0) {
throw DeviceInitializationFailure(
"Failed to set USB configuration - error code " + std::to_string(libUsbStatusCode) + " returned."
);
}
libusb_free_config_descriptor(configDescriptor);
}
if (this->libUsbContext != nullptr) {
libusb_exit(this->libUsbContext);
std::vector<libusb_device*> UsbDevice::findMatchingDevices(
std::optional<std::uint16_t> vendorId, std::optional<std::uint16_t> productId
) {
auto* libUsbContext = this->libUsbContext;
libusb_device** devices = nullptr;
libusb_device* device;
std::vector<libusb_device*> matchedDevices;
auto vendorIdToMatch = vendorId.value_or(this->vendorId);
auto productIdToMatch = productId.value_or(this->productId);
ssize_t libUsbStatusCode = libusb_get_device_list(libUsbContext, &devices);
if (libUsbStatusCode < 0) {
throw DeviceInitializationFailure(
"Failed to retrieve USB devices - return code: '" + std::to_string(libUsbStatusCode) + "'"
);
}
ssize_t i = 0;
while ((device = devices[i++]) != nullptr) {
struct libusb_device_descriptor desc = {};
if ((libUsbStatusCode = libusb_get_device_descriptor(device, &desc)) < 0) {
Logger::warning("Failed to retrieve USB device descriptor - return code: '"
+ std::to_string(libUsbStatusCode) + "'");
continue;
}
if (desc.idVendor == vendorIdToMatch && desc.idProduct == productIdToMatch) {
matchedDevices.push_back(device);
}
}
libusb_free_device_list(devices, 1);
return matchedDevices;
}
void UsbDevice::close() {
if (this->libUsbDeviceHandle != nullptr) {
libusb_close(this->libUsbDeviceHandle);
this->libUsbDeviceHandle = nullptr;
}
if (this->libUsbContext != nullptr) {
libusb_exit(this->libUsbContext);
}
}
}

View File

@@ -2,98 +2,105 @@
#include "src/Logger/Logger.hpp"
using namespace Bloom;
using namespace Bloom::Events;
namespace Bloom
{
using namespace Bloom::Events;
std::set<Events::EventType> EventListener::getRegisteredEventTypes() {
return this->registeredEventTypes.getValue();
}
void EventListener::registerEvent(SharedGenericEventPointer event) {
Logger::debug("Event \"" + event->getName() + "\" (" + std::to_string(event->id)
+ ") registered for listener " + this->name);
auto queueLock = this->eventQueueByEventType.acquireLock();
auto& eventQueueByType = this->eventQueueByEventType.getReference();
eventQueueByType[event->getType()].push(std::move(event));
this->eventQueueByEventTypeCV.notify_all();
if (this->interruptEventNotifier != nullptr && this->interruptEventNotifier->isInitialised()) {
this->interruptEventNotifier->notify();
std::set<Events::EventType> EventListener::getRegisteredEventTypes() {
return this->registeredEventTypes.getValue();
}
}
void EventListener::waitAndDispatch(int msTimeout) {
auto queueLock = this->eventQueueByEventType.acquireLock();
auto& eventQueueByType = this->eventQueueByEventType.getReference();
auto registeredEventTypes = this->getRegisteredEventTypes();
std::optional<SharedGenericEventPointer> event;
void EventListener::registerEvent(SharedGenericEventPointer event) {
Logger::debug("Event \"" + event->getName() + "\" (" + std::to_string(event->id)
+ ") registered for listener " + this->name);
auto queueLock = this->eventQueueByEventType.acquireLock();
auto& eventQueueByType = this->eventQueueByEventType.getReference();
eventQueueByType[event->getType()].push(std::move(event));
this->eventQueueByEventTypeCV.notify_all();
if (this->interruptEventNotifier != nullptr && this->interruptEventNotifier->isInitialised()) {
this->interruptEventNotifier->notify();
}
}
void EventListener::waitAndDispatch(int msTimeout) {
auto queueLock = this->eventQueueByEventType.acquireLock();
auto& eventQueueByType = this->eventQueueByEventType.getReference();
auto registeredEventTypes = this->getRegisteredEventTypes();
std::optional<SharedGenericEventPointer> event;
auto eventsFound = [&registeredEventTypes, &event, &eventQueueByType]() -> bool {
for (auto& eventQueue: eventQueueByType) {
if (registeredEventTypes.contains(eventQueue.first) && !eventQueue.second.empty()) {
return true;
}
}
return false;
};
if (msTimeout > 0) {
this->eventQueueByEventTypeCV.wait_for(queueLock, std::chrono::milliseconds(msTimeout), eventsFound);
} else {
this->eventQueueByEventTypeCV.wait(queueLock, eventsFound);
}
/*
* We don't want the dispatch to block other threads from registering more events. We don't need the
* lock anymore so it's fine to release it here.
*/
queueLock.unlock();
this->dispatchCurrentEvents();
}
void EventListener::dispatchEvent(const SharedGenericEventPointer& event) {
Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ").");
// Dispatch the event to all registered handlers
auto mappingLock = this->eventTypeToCallbacksMapping.acquireLock();
auto& callbacks = this->eventTypeToCallbacksMapping.getReference().find(event->getType())->second;
mappingLock.unlock();
for (auto& callback : callbacks) {
callback(*(event.get()));
}
}
void EventListener::dispatchCurrentEvents() {
auto events = this->getEvents();
for (const auto& event: events) {
dispatchEvent(event);
}
}
std::vector<SharedGenericEventPointer> EventListener::getEvents() {
auto queueLock = this->eventQueueByEventType.acquireLock();
auto& eventQueueByType = this->eventQueueByEventType.getReference();
std::vector<SharedGenericEventPointer> output;
auto eventsFound = [&registeredEventTypes, &event, &eventQueueByType] () -> bool {
for (auto& eventQueue: eventQueueByType) {
if (registeredEventTypes.contains(eventQueue.first) && !eventQueue.second.empty()) {
return true;
if (!eventQueue.second.empty()) {
output.push_back(std::move(eventQueue.second.front()));
eventQueue.second.pop();
}
}
return false;
};
if (msTimeout > 0) {
this->eventQueueByEventTypeCV.wait_for(queueLock, std::chrono::milliseconds(msTimeout), eventsFound);
std::sort(
output.begin(),
output.end(),
[](const SharedGenericEventPointer& a, const SharedGenericEventPointer& b) {
return a->id < b->id;
}
);
} else {
this->eventQueueByEventTypeCV.wait(queueLock, eventsFound);
return output;
}
/*
* We don't want the dispatch to block other threads from registering more events. We don't need the
* lock anymore so it's fine to release it here.
*/
queueLock.unlock();
this->dispatchCurrentEvents();
}
void EventListener::dispatchEvent(const SharedGenericEventPointer& event) {
Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ").");
// Dispatch the event to all registered handlers
auto mappingLock = this->eventTypeToCallbacksMapping.acquireLock();
auto& callbacks = this->eventTypeToCallbacksMapping.getReference().find(event->getType())->second;
mappingLock.unlock();
for (auto& callback : callbacks) {
callback(*(event.get()));
void EventListener::clearAllCallbacks() {
auto lock = this->eventTypeToCallbacksMapping.acquireLock();
this->eventTypeToCallbacksMapping.getReference().clear();
}
}
void EventListener::dispatchCurrentEvents() {
auto events = this->getEvents();
for (const auto& event: events) {
dispatchEvent(event);
}
}
std::vector<SharedGenericEventPointer> EventListener::getEvents() {
auto queueLock = this->eventQueueByEventType.acquireLock();
auto& eventQueueByType = this->eventQueueByEventType.getReference();
std::vector<SharedGenericEventPointer> output;
for (auto& eventQueue: eventQueueByType) {
if (!eventQueue.second.empty()) {
output.push_back(std::move(eventQueue.second.front()));
eventQueue.second.pop();
}
}
std::sort(output.begin(), output.end(), [] (const SharedGenericEventPointer& a, const SharedGenericEventPointer& b) {
return a->id < b->id;
});
return output;
}
void EventListener::clearAllCallbacks() {
auto lock = this->eventTypeToCallbacksMapping.acquireLock();
this->eventTypeToCallbacksMapping.getReference().clear();
}

View File

@@ -1,35 +1,36 @@
#include "EventManager.hpp"
using namespace Bloom;
void EventManager::registerListener(std::shared_ptr<EventListener> listener) {
auto registerListenersLock = std::unique_lock(this->registerListenerMutex);
this->registeredListeners.insert(std::pair(listener->getId(), std::move(listener)));
}
void EventManager::deregisterListener(size_t listenerId) {
auto registerListenersLock = std::unique_lock(this->registerListenerMutex);
this->registeredListeners.erase(listenerId);
}
void EventManager::triggerEvent(const std::shared_ptr<const Events::Event>& event) {
auto registerListenersLock = std::unique_lock(this->registerListenerMutex);
for(const auto& [listenerId, listener] : this->registeredListeners) {
if (listener->isEventTypeRegistered(event->getType())) {
listener->registerEvent(event);
}
namespace Bloom
{
void EventManager::registerListener(std::shared_ptr<EventListener> listener) {
auto registerListenersLock = std::unique_lock(this->registerListenerMutex);
this->registeredListeners.insert(std::pair(listener->getId(), std::move(listener)));
}
}
bool EventManager::isEventTypeListenedFor(Events::EventType eventType) {
auto registerListenersLock = std::unique_lock(this->registerListenerMutex);
void EventManager::deregisterListener(size_t listenerId) {
auto registerListenersLock = std::unique_lock(this->registerListenerMutex);
this->registeredListeners.erase(listenerId);
}
for(const auto& [listenerId, listener] : this->registeredListeners) {
if (listener->isEventTypeRegistered(eventType)) {
return true;
void EventManager::triggerEvent(const std::shared_ptr<const Events::Event>& event) {
auto registerListenersLock = std::unique_lock(this->registerListenerMutex);
for (const auto&[listenerId, listener] : this->registeredListeners) {
if (listener->isEventTypeRegistered(event->getType())) {
listener->registerEvent(event);
}
}
}
return false;
bool EventManager::isEventTypeListenedFor(Events::EventType eventType) {
auto registerListenersLock = std::unique_lock(this->registerListenerMutex);
for (const auto&[listenerId, listener] : this->registeredListeners) {
if (listener->isEventTypeRegistered(eventType)) {
return true;
}
}
return false;
}
}

View File

@@ -7,14 +7,15 @@
#include "src/Exceptions/Exception.hpp"
using namespace Bloom;
namespace Bloom
{
std::string Paths::applicationDirPath() {
auto pathCharArray = std::array<char, PATH_MAX>();
std::string Paths::applicationDirPath() {
auto pathCharArray = std::array<char, PATH_MAX>();
if (readlink("/proc/self/exe", pathCharArray.data(), PATH_MAX) < 0) {
throw Exceptions::Exception("Failed to obtain application directory path.");
}
if (readlink("/proc/self/exe", pathCharArray.data(), PATH_MAX) < 0) {
throw Exceptions::Exception("Failed to obtain application directory path.");
return std::filesystem::path(std::string(pathCharArray.begin(), pathCharArray.end())).parent_path();
}
return std::filesystem::path(std::string(pathCharArray.begin(), pathCharArray.end())).parent_path();
}

View File

@@ -10,203 +10,205 @@
#include "src/Application.hpp"
#include "InsightWorker/Tasks/QueryLatestVersionNumber.hpp"
using namespace Bloom;
using namespace Bloom::Exceptions;
namespace Bloom
{
using namespace Bloom::Exceptions;
void Insight::run() {
try {
this->startup();
void Insight::run() {
try {
this->startup();
this->workerThread->start();
this->setThreadState(ThreadState::READY);
Logger::info("Insight ready");
this->application.exec();
this->workerThread->start();
this->setThreadState(ThreadState::READY);
Logger::info("Insight ready");
this->application.exec();
} catch (const Exception& exception) {
Logger::error("Insight encountered a fatal error. See below for errors:");
Logger::error(exception.getMessage());
} catch (const Exception& exception) {
Logger::error("Insight encountered a fatal error. See below for errors:");
Logger::error(exception.getMessage());
} catch (const std::exception& exception) {
Logger::error("Insight encountered a fatal error. See below for errors:");
Logger::error(std::string(exception.what()));
}
this->shutdown();
}
void Insight::startup() {
Logger::info("Starting Insight");
this->setThreadState(ThreadState::STARTING);
this->eventManager.registerListener(this->eventListener);
this->eventListener->registerCallbackForEventType<Events::ShutdownApplication>(
std::bind(&Insight::onShutdownApplicationEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::TargetControllerThreadStateChanged>(
std::bind(&Insight::onTargetControllerThreadStateChangedEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::DebugServerThreadStateChanged>(
std::bind(&Insight::onDebugServerThreadStateChangedEvent, this, std::placeholders::_1)
);
auto targetDescriptor = this->targetControllerConsole.getTargetDescriptor();
QApplication::setQuitOnLastWindowClosed(true);
QApplication::setStyle(new BloomProxyStyle());
auto globalStylesheet = QFile(
QString::fromStdString(
Paths::compiledResourcesPath() + "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/Global.qss"
)
);
if (!globalStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open global stylesheet file");
}
this->application.setStyleSheet(globalStylesheet.readAll());
qRegisterMetaType<Bloom::Targets::TargetDescriptor>();
qRegisterMetaType<Bloom::Targets::TargetPinDescriptor>();
qRegisterMetaType<Bloom::Targets::TargetPinState>();
qRegisterMetaType<Bloom::Targets::TargetState>();
qRegisterMetaType<std::map<int, Bloom::Targets::TargetPinState>>();
// Load Ubuntu fonts
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-B.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-BI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-C.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-L.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-LI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-M.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-MI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-B.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-BI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-R.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-RI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-R.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-RI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-Th.ttf")
);
/*
* We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to
* process our events. To address this, we use a QTimer to dispatch our events on an interval.
*
* This allows us to use Qt's event loop whilst still being able to process our own events.
*/
auto* eventDispatchTimer = new QTimer(&(this->application));
QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Insight::dispatchEvents);
eventDispatchTimer->start(100);
this->mainWindow->setInsightConfig(this->insightConfig);
this->mainWindow->setEnvironmentConfig(this->environmentConfig);
this->mainWindow->init(targetDescriptor);
// Prepare worker thread
this->workerThread = new QThread();
this->workerThread->setObjectName("IW");
this->insightWorker->moveToThread(this->workerThread);
QObject::connect(this->workerThread, &QThread::started, this->insightWorker, &InsightWorker::startup);
QObject::connect(this->workerThread, &QThread::finished, this->insightWorker, &QObject::deleteLater);
QObject::connect(this->workerThread, &QThread::finished, this->workerThread, &QThread::deleteLater);
QObject::connect(this->insightWorker, &InsightWorker::ready, this, [this] {
this->checkBloomVersion();
});
this->mainWindow->show();
}
void Insight::shutdown() {
if (this->getThreadState() == ThreadState::STOPPED) {
return;
}
Logger::info("Shutting down Insight");
this->mainWindow->close();
if (this->workerThread != nullptr && this->workerThread->isRunning()) {
this->workerThread->quit();
}
this->application.exit(0);
this->setThreadState(ThreadState::STOPPED);
}
void Insight::checkBloomVersion() {
auto currentVersionNumber = Application::VERSION;
auto* versionQueryTask = new QueryLatestVersionNumber(
currentVersionNumber
);
QObject::connect(
versionQueryTask,
&QueryLatestVersionNumber::latestVersionNumberRetrieved,
this,
[this, currentVersionNumber] (const VersionNumber& latestVersionNumber) {
if (latestVersionNumber > currentVersionNumber) {
Logger::warning(
"Bloom v" + latestVersionNumber.toString()
+ " is available to download - upgrade via " + Paths::homeDomainName()
);
}
} catch (const std::exception& exception) {
Logger::error("Insight encountered a fatal error. See below for errors:");
Logger::error(std::string(exception.what()));
}
);
this->insightWorker->queueTask(versionQueryTask);
}
void Insight::onShutdownApplicationEvent(const Events::ShutdownApplication&) {
/*
* Once Insight shuts down, control of the main thread will be returned to Application::run(), which
* will pickup the ShutdownApplication event and proceed with the shutdown.
*/
this->shutdown();
}
void Insight::onTargetControllerThreadStateChangedEvent(const Events::TargetControllerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED) {
// Something horrible has happened with the TargetController - Insight is useless without the TargetController
this->shutdown();
}
}
void Insight::onDebugServerThreadStateChangedEvent(const Events::DebugServerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED) {
// Something horrible has happened with the DebugServer
void Insight::startup() {
Logger::info("Starting Insight");
this->setThreadState(ThreadState::STARTING);
this->eventManager.registerListener(this->eventListener);
this->eventListener->registerCallbackForEventType<Events::ShutdownApplication>(
std::bind(&Insight::onShutdownApplicationEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::TargetControllerThreadStateChanged>(
std::bind(&Insight::onTargetControllerThreadStateChangedEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::DebugServerThreadStateChanged>(
std::bind(&Insight::onDebugServerThreadStateChangedEvent, this, std::placeholders::_1)
);
auto targetDescriptor = this->targetControllerConsole.getTargetDescriptor();
QApplication::setQuitOnLastWindowClosed(true);
QApplication::setStyle(new BloomProxyStyle());
auto globalStylesheet = QFile(
QString::fromStdString(
Paths::compiledResourcesPath() + "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/Global.qss"
)
);
if (!globalStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open global stylesheet file");
}
this->application.setStyleSheet(globalStylesheet.readAll());
qRegisterMetaType<Bloom::Targets::TargetDescriptor>();
qRegisterMetaType<Bloom::Targets::TargetPinDescriptor>();
qRegisterMetaType<Bloom::Targets::TargetPinState>();
qRegisterMetaType<Bloom::Targets::TargetState>();
qRegisterMetaType<std::map<int, Bloom::Targets::TargetPinState>>();
// Load Ubuntu fonts
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-B.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-BI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-C.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-L.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-LI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-M.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-MI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-B.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-BI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-R.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/UbuntuMono-RI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-R.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-RI.ttf")
);
QFontDatabase::addApplicationFont(
QString::fromStdString(Paths::resourcesDirPath() + "/Fonts/Ubuntu/Ubuntu-Th.ttf")
);
/*
* We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to
* process our events. To address this, we use a QTimer to dispatch our events on an interval.
*
* This allows us to use Qt's event loop whilst still being able to process our own events.
*/
auto* eventDispatchTimer = new QTimer(&(this->application));
QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Insight::dispatchEvents);
eventDispatchTimer->start(100);
this->mainWindow->setInsightConfig(this->insightConfig);
this->mainWindow->setEnvironmentConfig(this->environmentConfig);
this->mainWindow->init(targetDescriptor);
// Prepare worker thread
this->workerThread = new QThread();
this->workerThread->setObjectName("IW");
this->insightWorker->moveToThread(this->workerThread);
QObject::connect(this->workerThread, &QThread::started, this->insightWorker, &InsightWorker::startup);
QObject::connect(this->workerThread, &QThread::finished, this->insightWorker, &QObject::deleteLater);
QObject::connect(this->workerThread, &QThread::finished, this->workerThread, &QThread::deleteLater);
QObject::connect(this->insightWorker, &InsightWorker::ready, this, [this] {
this->checkBloomVersion();
});
this->mainWindow->show();
}
void Insight::shutdown() {
if (this->getThreadState() == ThreadState::STOPPED) {
return;
}
Logger::info("Shutting down Insight");
this->mainWindow->close();
if (this->workerThread != nullptr && this->workerThread->isRunning()) {
this->workerThread->quit();
}
this->application.exit(0);
this->setThreadState(ThreadState::STOPPED);
}
void Insight::checkBloomVersion() {
auto currentVersionNumber = Application::VERSION;
auto* versionQueryTask = new QueryLatestVersionNumber(
currentVersionNumber
);
QObject::connect(
versionQueryTask,
&QueryLatestVersionNumber::latestVersionNumberRetrieved,
this,
[this, currentVersionNumber] (const VersionNumber& latestVersionNumber) {
if (latestVersionNumber > currentVersionNumber) {
Logger::warning(
"Bloom v" + latestVersionNumber.toString()
+ " is available to download - upgrade via " + Paths::homeDomainName()
);
}
}
);
this->insightWorker->queueTask(versionQueryTask);
}
void Insight::onShutdownApplicationEvent(const Events::ShutdownApplication&) {
/*
* Once Insight shuts down, control of the main thread will be returned to Application::run(), which
* will pickup the ShutdownApplication event and proceed with the shutdown.
*/
this->shutdown();
}
void Insight::onTargetControllerThreadStateChangedEvent(const Events::TargetControllerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED) {
// Something horrible has happened with the TargetController - Insight is useless without the TargetController
this->shutdown();
}
}
void Insight::onDebugServerThreadStateChangedEvent(const Events::DebugServerThreadStateChanged& event) {
if (event.getState() == ThreadState::STOPPED) {
// Something horrible has happened with the DebugServer
this->shutdown();
}
}
}

View File

@@ -6,135 +6,138 @@
#include "src/Helpers/Thread.hpp"
#include "src/Logger/Logger.hpp"
using namespace Bloom;
using namespace Bloom::Exceptions;
namespace Bloom
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetState;
using Bloom::Targets::TargetState;
InsightWorker::InsightWorker(EventManager& eventManager): eventManager(eventManager) {}
InsightWorker::InsightWorker(EventManager& eventManager): eventManager(eventManager) {}
void InsightWorker::queueTask(InsightWorkerTask* task) {
auto taskQueueLock = this->queuedTasks.acquireLock();
task->moveToThread(this->thread());
task->setParent(this);
this->queuedTasks.getReference().push(task);
emit this->taskQueued();
}
void InsightWorker::startup() {
Logger::debug("Starting InsightWorker thread");
this->eventManager.registerListener(this->eventListener);
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
std::bind(&InsightWorker::onTargetControllerStateReportedEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
std::bind(&InsightWorker::onTargetStoppedEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::TargetExecutionResumed>(
std::bind(&InsightWorker::onTargetResumedEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::RegistersWrittenToTarget>(
std::bind(&InsightWorker::onTargetRegistersWrittenEvent, this, std::placeholders::_1)
);
this->eventDispatchTimer = new QTimer(this);
QObject::connect(this->eventDispatchTimer, &QTimer::timeout, this, &InsightWorker::dispatchEvents);
this->eventDispatchTimer->start(5);
QObject::connect(this, &InsightWorker::taskQueued, this, &InsightWorker::executeTasks);
this->eventManager.triggerEvent(
std::make_shared<Events::InsightThreadStateChanged>(ThreadState::READY)
);
emit this->ready();
}
void InsightWorker::requestPinStates(int variantId) {
this->targetControllerConsole.requestPinStates(variantId);
}
std::optional<InsightWorkerTask*> InsightWorker::getQueuedTask() {
auto task = std::optional<InsightWorkerTask*>();
auto& queuedTasks = this->queuedTasks.getReference();
auto taskQueueLock = this->queuedTasks.acquireLock();
if (!queuedTasks.empty()) {
task = queuedTasks.front();
queuedTasks.pop();
void InsightWorker::queueTask(InsightWorkerTask* task) {
auto taskQueueLock = this->queuedTasks.acquireLock();
task->moveToThread(this->thread());
task->setParent(this);
this->queuedTasks.getReference().push(task);
emit this->taskQueued();
}
return task;
}
void InsightWorker::startup() {
Logger::debug("Starting InsightWorker thread");
this->eventManager.registerListener(this->eventListener);
void InsightWorker::onTargetStoppedEvent(const Events::TargetExecutionStopped& event) {
/*
* When we report a target halt to Insight, Insight will immediately seek more data from the target (such as GPIO
* pin states). This can be problematic for cases where the target had halted due to a conditional breakpoint.
*
* For conditional breakpoints, a software breakpoint is employed to halt target execution and give the debug
* client an opportunity to check the condition. In cases where the condition is not met, the client will
* immediately request for execution to be resumed. It's important that Insight does not get in the way of this
* process, as it could end up slowing things down significantly.
*
* For the above reason, we don't want to report any target halts to Insight, unless we can be sure that the client
* isn't going to immediately resume execution upon checking the condition.
*
* We do this by providing a time window for the TargetExecutionResumed event. If the event is triggered within
* that time window, we won't report the target halt to Insight, thus preventing Insight from needlessly seeking
* data from the target and slowing things down.
*
* This isn't the best approach, TBH, as it introduces a delay to Insight's response to the target halting. The
* problem is, we cannot differentiate a conditional breakpoint with a software breakpoint, so this seems to be the
* only way. It would be nice if the debug client gave us some form of indication of whether the breakpoint is a
* conditional one.
*/
auto resumedEvent = this->eventListener->waitForEvent<Events::TargetExecutionResumed>(
std::chrono::milliseconds(650)
);
this->eventListener->registerCallbackForEventType<Events::TargetControllerStateReported>(
std::bind(&InsightWorker::onTargetControllerStateReportedEvent, this, std::placeholders::_1)
);
if (!resumedEvent.has_value()) {
emit this->targetStateUpdated(TargetState::STOPPED);
emit this->targetProgramCounterUpdated(event.programCounter);
this->eventListener->registerCallbackForEventType<Events::TargetExecutionStopped>(
std::bind(&InsightWorker::onTargetStoppedEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::TargetExecutionResumed>(
std::bind(&InsightWorker::onTargetResumedEvent, this, std::placeholders::_1)
);
this->eventListener->registerCallbackForEventType<Events::RegistersWrittenToTarget>(
std::bind(&InsightWorker::onTargetRegistersWrittenEvent, this, std::placeholders::_1)
);
this->eventDispatchTimer = new QTimer(this);
QObject::connect(this->eventDispatchTimer, &QTimer::timeout, this, &InsightWorker::dispatchEvents);
this->eventDispatchTimer->start(5);
QObject::connect(this, &InsightWorker::taskQueued, this, &InsightWorker::executeTasks);
this->eventManager.triggerEvent(
std::make_shared<Events::InsightThreadStateChanged>(ThreadState::READY)
);
emit this->ready();
}
}
void InsightWorker::onTargetResumedEvent(const Events::TargetExecutionResumed& event) {
emit this->targetStateUpdated(TargetState::RUNNING);
}
void InsightWorker::requestPinStates(int variantId) {
this->targetControllerConsole.requestPinStates(variantId);
}
void InsightWorker::onTargetRegistersWrittenEvent(const Events::RegistersWrittenToTarget& event) {
emit this->targetRegistersWritten(event.registers, event.createdTimestamp);
}
std::optional<InsightWorkerTask*> InsightWorker::getQueuedTask() {
auto task = std::optional<InsightWorkerTask*>();
void InsightWorker::onTargetControllerStateReportedEvent(const Events::TargetControllerStateReported& event) {
if (this->lastTargetControllerState == TargetControllerState::ACTIVE
&& event.state == TargetControllerState::SUSPENDED
) {
emit this->targetControllerSuspended();
auto& queuedTasks = this->queuedTasks.getReference();
auto taskQueueLock = this->queuedTasks.acquireLock();
} else if (this->lastTargetControllerState == TargetControllerState::SUSPENDED
&& event.state == TargetControllerState::ACTIVE
) {
try {
emit this->targetControllerResumed(this->targetControllerConsole.getTargetDescriptor());
if (!queuedTasks.empty()) {
task = queuedTasks.front();
queuedTasks.pop();
}
} catch (const Exception& exception) {
Logger::error("Insight resume failed - " + exception.getMessage());
return task;
}
void InsightWorker::onTargetStoppedEvent(const Events::TargetExecutionStopped& event) {
/*
* When we report a target halt to Insight, Insight will immediately seek more data from the target (such as
* GPIO pin states). This can be problematic for cases where the target had halted due to a conditional
* breakpoint.
*
* For conditional breakpoints, a software breakpoint is employed to halt target execution and give the debug
* client an opportunity to check the condition. In cases where the condition is not met, the client will
* immediately request for execution to be resumed. It's important that Insight does not get in the way of this
* process, as it could end up slowing things down significantly.
*
* For the above reason, we don't want to report any target halts to Insight, unless we can be sure that the
* client isn't going to immediately resume execution upon checking the condition.
*
* We do this by providing a time window for the TargetExecutionResumed event. If the event is triggered within
* that time window, we won't report the target halt to Insight, thus preventing Insight from needlessly
* seeking data from the target and slowing things down.
*
* This isn't the best approach, TBH, as it introduces a delay to Insight's response to the target halting. The
* problem is, we cannot differentiate a conditional breakpoint with a software breakpoint, so this seems to be
* the only way. It would be nice if the debug client gave us some form of indication of whether the breakpoint
* is a conditional one.
*/
auto resumedEvent = this->eventListener->waitForEvent<Events::TargetExecutionResumed>(
std::chrono::milliseconds(650)
);
if (!resumedEvent.has_value()) {
emit this->targetStateUpdated(TargetState::STOPPED);
emit this->targetProgramCounterUpdated(event.programCounter);
}
}
this->lastTargetControllerState = event.state;
}
void InsightWorker::executeTasks() {
auto task = std::optional<InsightWorkerTask*>();
void InsightWorker::onTargetResumedEvent(const Events::TargetExecutionResumed& event) {
emit this->targetStateUpdated(TargetState::RUNNING);
}
while ((task = this->getQueuedTask()).has_value()) {
task.value()->execute(this->targetControllerConsole);
void InsightWorker::onTargetRegistersWrittenEvent(const Events::RegistersWrittenToTarget& event) {
emit this->targetRegistersWritten(event.registers, event.createdTimestamp);
}
void InsightWorker::onTargetControllerStateReportedEvent(const Events::TargetControllerStateReported& event) {
if (this->lastTargetControllerState == TargetControllerState::ACTIVE
&& event.state == TargetControllerState::SUSPENDED
) {
emit this->targetControllerSuspended();
} else if (this->lastTargetControllerState == TargetControllerState::SUSPENDED
&& event.state == TargetControllerState::ACTIVE
) {
try {
emit this->targetControllerResumed(this->targetControllerConsole.getTargetDescriptor());
} catch (const Exception& exception) {
Logger::error("Insight resume failed - " + exception.getMessage());
}
}
this->lastTargetControllerState = event.state;
}
void InsightWorker::executeTasks() {
auto task = std::optional<InsightWorkerTask*>();
while ((task = this->getQueuedTask()).has_value()) {
task.value()->execute(this->targetControllerConsole);
}
}
}

View File

@@ -2,19 +2,20 @@
#include "src/Logger/Logger.hpp"
using namespace Bloom;
namespace Bloom
{
void InsightWorkerTask::execute(TargetControllerConsole& targetControllerConsole) {
try {
this->state = InsightWorkerTaskState::STARTED;
emit this->started();
this->run(targetControllerConsole);
this->state = InsightWorkerTaskState::COMPLETED;
emit this->completed();
void InsightWorkerTask::execute(TargetControllerConsole& targetControllerConsole) {
try {
this->state = InsightWorkerTaskState::STARTED;
emit this->started();
this->run(targetControllerConsole);
this->state = InsightWorkerTaskState::COMPLETED;
emit this->completed();
} catch (std::exception& exception) {
this->state = InsightWorkerTaskState::FAILED;
Logger::error("InsightWorker task failed - " + std::string(exception.what()));
emit this->failed(QString::fromStdString(exception.what()));
} catch (std::exception& exception) {
this->state = InsightWorkerTaskState::FAILED;
Logger::error("InsightWorker task failed - " + std::string(exception.what()));
emit this->failed(QString::fromStdString(exception.what()));
}
}
}

View File

@@ -9,26 +9,27 @@
#include "src/Helpers/Paths.hpp"
using namespace Bloom;
namespace Bloom
{
void QueryLatestVersionNumber::run(TargetControllerConsole& targetControllerConsole) {
auto* networkAccessManager = new QNetworkAccessManager(this);
auto queryVersionEndpointUrl = QUrl(QString::fromStdString(Paths::homeDomainName() + "/latest-version"));
queryVersionEndpointUrl.setScheme("http");
queryVersionEndpointUrl.setQuery(QUrlQuery({
{"currentVersionNumber", QString::fromStdString(this->currentVersionNumber.toString())}
}));
void QueryLatestVersionNumber::run(TargetControllerConsole& targetControllerConsole) {
auto* networkAccessManager = new QNetworkAccessManager(this);
auto queryVersionEndpointUrl = QUrl(QString::fromStdString(Paths::homeDomainName() + "/latest-version"));
queryVersionEndpointUrl.setScheme("http");
queryVersionEndpointUrl.setQuery(QUrlQuery({
{"currentVersionNumber", QString::fromStdString(this->currentVersionNumber.toString())}
}));
auto* response = networkAccessManager->get(QNetworkRequest(queryVersionEndpointUrl));
QObject::connect(response, &QNetworkReply::finished, this, [this, response] {
const auto jsonResponseObject = QJsonDocument::fromJson(response->readAll()).object();
auto* response = networkAccessManager->get(QNetworkRequest(queryVersionEndpointUrl));
QObject::connect(response, &QNetworkReply::finished, this, [this, response] {
const auto jsonResponseObject = QJsonDocument::fromJson(response->readAll()).object();
if (jsonResponseObject.contains("latestVersionNumber")) {
emit this->latestVersionNumberRetrieved(
VersionNumber(
jsonResponseObject.value("latestVersionNumber").toString().toStdString()
)
);
}
});
if (jsonResponseObject.contains("latestVersionNumber")) {
emit this->latestVersionNumberRetrieved(
VersionNumber(
jsonResponseObject.value("latestVersionNumber").toString().toStdString()
)
);
}
});
}
}

View File

@@ -1,7 +1,8 @@
#include "ReadStackPointer.hpp"
using namespace Bloom;
void ReadStackPointer::run(TargetControllerConsole& targetControllerConsole) {
emit this->stackPointerRead(targetControllerConsole.getStackPointer());
namespace Bloom
{
void ReadStackPointer::run(TargetControllerConsole& targetControllerConsole) {
emit this->stackPointerRead(targetControllerConsole.getStackPointer());
}
}

View File

@@ -1,14 +1,15 @@
#include "ReadTargetMemory.hpp"
using namespace Bloom;
void ReadTargetMemory::run(TargetControllerConsole& targetControllerConsole) {
emit this->targetMemoryRead(
targetControllerConsole.readMemory(
this->memoryType,
this->startAddress,
this->size,
this->excludedAddressRanges
)
);
namespace Bloom
{
void ReadTargetMemory::run(TargetControllerConsole& targetControllerConsole) {
emit this->targetMemoryRead(
targetControllerConsole.readMemory(
this->memoryType,
this->startAddress,
this->size,
this->excludedAddressRanges
)
);
}
}

View File

@@ -1,7 +1,8 @@
#include "ReadTargetRegisters.hpp"
using namespace Bloom;
void ReadTargetRegisters::run(TargetControllerConsole& targetControllerConsole) {
emit this->targetRegistersRead(targetControllerConsole.readRegisters(this->descriptors));
namespace Bloom
{
void ReadTargetRegisters::run(TargetControllerConsole& targetControllerConsole) {
emit this->targetRegistersRead(targetControllerConsole.readRegisters(this->descriptors));
}
}

View File

@@ -1,7 +1,8 @@
#include "RefreshTargetPinStates.hpp"
using namespace Bloom;
void RefreshTargetPinStates::run(TargetControllerConsole& targetControllerConsole) {
emit this->targetPinStatesRetrieved(targetControllerConsole.getPinStates(this->variantId));
namespace Bloom
{
void RefreshTargetPinStates::run(TargetControllerConsole& targetControllerConsole) {
emit this->targetPinStatesRetrieved(targetControllerConsole.getPinStates(this->variantId));
}
}

View File

@@ -1,7 +1,8 @@
#include "SetTargetPinState.hpp"
using namespace Bloom;
void SetTargetPinState::run(TargetControllerConsole& targetControllerConsole) {
targetControllerConsole.setPinState(this->pinDescriptor, this->pinState);
namespace Bloom
{
void SetTargetPinState::run(TargetControllerConsole& targetControllerConsole) {
targetControllerConsole.setPinState(this->pinDescriptor, this->pinState);
}
}

View File

@@ -1,7 +1,8 @@
#include "WriteTargetRegister.hpp"
using namespace Bloom;
void WriteTargetRegister::run(TargetControllerConsole& targetControllerConsole) {
targetControllerConsole.writeRegisters({this->targetRegister});
namespace Bloom
{
void WriteTargetRegister::run(TargetControllerConsole& targetControllerConsole) {
targetControllerConsole.writeRegisters({this->targetRegister});
}
}

View File

@@ -6,37 +6,39 @@
#include "src/Helpers/Paths.hpp"
#include "src/Application.hpp"
using namespace Bloom;
using namespace Exceptions;
namespace Bloom
{
using namespace Exceptions;
AboutWindow::AboutWindow(QWidget* parent): QObject(parent) {
auto aboutWindowUiFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/UiFiles/AboutWindow.ui"
)
);
auto aboutWindowStylesheet = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/AboutWindow.qss"
)
);
AboutWindow::AboutWindow(QWidget* parent): QObject(parent) {
auto aboutWindowUiFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/UiFiles/AboutWindow.ui"
)
);
auto aboutWindowStylesheet = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Stylesheets/AboutWindow.qss"
)
);
if (!aboutWindowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open AboutWindow UI file");
}
if (!aboutWindowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open AboutWindow UI file");
}
if (!aboutWindowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open AboutWindow QSS file");
}
if (!aboutWindowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open AboutWindow QSS file");
}
auto uiLoader = QUiLoader();
this->windowWidget = uiLoader.load(&aboutWindowUiFile, parent);
this->windowWidget->setStyleSheet(aboutWindowStylesheet.readAll());
this->windowWidget->setFixedSize(400, 300);
auto uiLoader = QUiLoader();
this->windowWidget = uiLoader.load(&aboutWindowUiFile, parent);
this->windowWidget->setStyleSheet(aboutWindowStylesheet.readAll());
this->windowWidget->setFixedSize(400, 300);
auto versionLabel = this->windowWidget->findChild<QLabel*>("version-label");
auto versionLabel = this->windowWidget->findChild<QLabel*>("version-label");
if (versionLabel != nullptr) {
versionLabel->setText("Bloom v" + QString::fromStdString(Application::VERSION.toString()));
if (versionLabel != nullptr) {
versionLabel->setText("Bloom v" + QString::fromStdString(Application::VERSION.toString()));
}
}
}

View File

@@ -1,16 +1,17 @@
#include "BloomProxyStyle.hpp"
using namespace Bloom;
namespace Bloom
{
int BloomProxyStyle::styleHint(
StyleHint hint,
const QStyleOption* option,
const QWidget* widget,
QStyleHintReturn* returnData
) const {
if (hint == QStyle::SH_ComboBox_Popup) {
return 0;
}
int BloomProxyStyle::styleHint(
StyleHint hint,
const QStyleOption* option,
const QWidget* widget,
QStyleHintReturn* returnData
) const {
if (hint == QStyle::SH_ComboBox_Popup) {
return 0;
return QProxyStyle::styleHint(hint, option, widget, returnData);
}
return QProxyStyle::styleHint(hint, option, widget, returnData);
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,91 +12,93 @@
#include "Widgets/ExpandingHeightScrollAreaWidget.hpp"
#include "Widgets/TargetWidgets/TargetPackageWidgetContainer.hpp"
using namespace Bloom;
using namespace Bloom::Widgets;
namespace Bloom
{
using namespace Bloom::Widgets;
UiLoader::UiLoader(QObject* parent): QUiLoader(parent) {
this->customWidgetConstructorsByWidgetName = decltype(this->customWidgetConstructorsByWidgetName) {
{
"PanelWidget",
[this] (QWidget* parent, const QString& name) {
auto* widget = new PanelWidget(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"RotatableLabel",
[this] (QWidget* parent, const QString& name) {
auto* widget = new RotatableLabel("", parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"LabeledSeparator",
[this] (QWidget* parent, const QString& name) {
auto* widget = new LabeledSeparator(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"TextInput",
[this] (QWidget* parent, const QString& name) {
auto* widget = new TextInput(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"ExpandingHeightScrollAreaWidget",
[this] (QWidget* parent, const QString& name) {
auto* widget = new ExpandingHeightScrollAreaWidget(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"SvgWidget",
[this] (QWidget* parent, const QString& name) {
auto* widget = new SvgWidget(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"SvgToolButton",
[this] (QWidget* parent, const QString& name) {
auto* widget = new SvgToolButton(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"TargetPackageWidgetContainer",
[this] (QWidget* parent, const QString& name) {
auto* widget = new InsightTargetWidgets::TargetPackageWidgetContainer(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
};
}
QWidget* UiLoader::createWidget(const QString& className, QWidget* parent, const QString& name) {
if (this->customWidgetConstructorsByWidgetName.contains(className)) {
// This is a custom widget - call the mapped constructor
return this->customWidgetConstructorsByWidgetName.at(className)(parent, name);
UiLoader::UiLoader(QObject* parent): QUiLoader(parent) {
this->customWidgetConstructorsByWidgetName = decltype(this->customWidgetConstructorsByWidgetName) {
{
"PanelWidget",
[this] (QWidget* parent, const QString& name) {
auto* widget = new PanelWidget(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"RotatableLabel",
[this] (QWidget* parent, const QString& name) {
auto* widget = new RotatableLabel("", parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"LabeledSeparator",
[this] (QWidget* parent, const QString& name) {
auto* widget = new LabeledSeparator(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"TextInput",
[this] (QWidget* parent, const QString& name) {
auto* widget = new TextInput(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"ExpandingHeightScrollAreaWidget",
[this] (QWidget* parent, const QString& name) {
auto* widget = new ExpandingHeightScrollAreaWidget(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"SvgWidget",
[this] (QWidget* parent, const QString& name) {
auto* widget = new SvgWidget(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"SvgToolButton",
[this] (QWidget* parent, const QString& name) {
auto* widget = new SvgToolButton(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
{
"TargetPackageWidgetContainer",
[this] (QWidget* parent, const QString& name) {
auto* widget = new InsightTargetWidgets::TargetPackageWidgetContainer(parent);
widget->setObjectName(name);
widget->setStyleSheet(parent->styleSheet());
return widget;
}
},
};
}
return QUiLoader::createWidget(className, parent, name);
QWidget* UiLoader::createWidget(const QString& className, QWidget* parent, const QString& name) {
if (this->customWidgetConstructorsByWidgetName.contains(className)) {
// This is a custom widget - call the mapped constructor
return this->customWidgetConstructorsByWidgetName.at(className)(parent, name);
}
return QUiLoader::createWidget(className, parent, name);
}
}

View File

@@ -1,22 +1,23 @@
#include "ClickableWidget.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
void ClickableWidget::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::MouseButton::LeftButton) {
emit this->clicked();
void ClickableWidget::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::MouseButton::LeftButton) {
emit this->clicked();
} else if (event->button() == Qt::MouseButton::RightButton) {
emit this->rightClicked();
}
} else if (event->button() == Qt::MouseButton::RightButton) {
emit this->rightClicked();
QWidget::mouseReleaseEvent(event);
}
QWidget::mouseReleaseEvent(event);
}
void ClickableWidget::mouseDoubleClickEvent(QMouseEvent* event) {
if (event->button() == Qt::MouseButton::LeftButton) {
emit this->doubleClicked();
}
void ClickableWidget::mouseDoubleClickEvent(QMouseEvent* event) {
if (event->button() == Qt::MouseButton::LeftButton) {
emit this->doubleClicked();
QWidget::mouseDoubleClickEvent(event);
}
QWidget::mouseDoubleClickEvent(event);
}

View File

@@ -7,58 +7,64 @@
#include "src/Helpers/Paths.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
using Bloom::Exceptions::Exception;
using Bloom::Exceptions::Exception;
ErrorDialogue::ErrorDialogue(
const QString& windowTitle,
const QString& errorMessage,
QWidget* parent
): QDialog(parent) {
this->setObjectName("error-dialogue");
this->setAttribute(Qt::WA_DeleteOnClose, true);
this->setWindowTitle(windowTitle);
ErrorDialogue::ErrorDialogue(
const QString& windowTitle,
const QString& errorMessage,
QWidget* parent
): QDialog(parent) {
this->setObjectName("error-dialogue");
this->setAttribute(Qt::WA_DeleteOnClose, true);
this->setWindowTitle(windowTitle);
auto dialogueUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/UiFiles/ErrorDialogue.ui"
)
);
auto dialogueUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/UiFiles/ErrorDialogue.ui"
)
);
auto dialogueStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/Stylesheets/ErrorDialogue.qss"
)
);
auto dialogueStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/ErrorDialogue/Stylesheets/ErrorDialogue.qss"
)
);
if (!dialogueUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open ErrorDialogue UI file");
}
if (!dialogueUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open ErrorDialogue UI file");
if (!dialogueStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open ErrorDialogue stylesheet file");
}
this->setStyleSheet(dialogueStylesheet.readAll());
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&dialogueUiFile, this);
this->errorMessageDescriptionLabel = this->container->findChild<QLabel*>(
"error-message-description-label"
);
this->okButton = this->container->findChild<QPushButton*>("ok-btn");
this->container->setContentsMargins(15, 10, 15, 15);
this->errorMessageDescriptionLabel->setText(errorMessage);
QObject::connect(this->okButton, &QPushButton::clicked, this, &QDialog::close);
}
if (!dialogueStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open ErrorDialogue stylesheet file");
void ErrorDialogue::showEvent(QShowEvent* event) {
const auto containerSize = this->container->sizeHint();
const auto windowSize = QSize(
std::max(containerSize.width(), 500),
std::max(containerSize.height(), 100)
);
this->setFixedSize(windowSize);
this->container->setFixedSize(windowSize);
}
this->setStyleSheet(dialogueStylesheet.readAll());
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&dialogueUiFile, this);
this->errorMessageDescriptionLabel = this->container->findChild<QLabel*>("error-message-description-label");
this->okButton = this->container->findChild<QPushButton*>("ok-btn");
this->container->setContentsMargins(15, 10, 15, 15);
this->errorMessageDescriptionLabel->setText(errorMessage);
QObject::connect(this->okButton, &QPushButton::clicked, this, &QDialog::close);
}
void ErrorDialogue::showEvent(QShowEvent* event) {
const auto containerSize = this->container->sizeHint();
const auto windowSize = QSize(std::max(containerSize.width(), 500), std::max(containerSize.height(), 100));
this->setFixedSize(windowSize);
this->container->setFixedSize(windowSize);
}

View File

@@ -1,35 +1,36 @@
#include "LabeledSeparator.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
LabeledSeparator::LabeledSeparator(QString title, QWidget* parent): title(std::move(title)), QWidget(parent) {
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
this->setFixedHeight(LabeledSeparator::DEFAULT_HEIGHT);
}
LabeledSeparator::LabeledSeparator(QString title, QWidget* parent): title(std::move(title)), QWidget(parent) {
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
this->setFixedHeight(LabeledSeparator::DEFAULT_HEIGHT);
}
void LabeledSeparator::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void LabeledSeparator::drawWidget(QPainter& painter) {
const auto fontMetrics = painter.fontMetrics();
const auto titleSize = fontMetrics.size(Qt::TextFlag::TextSingleLine, this->title);
const auto titleRect = QRect(
QPoint(this->marginLeft, (this->height() - titleSize.height()) / 2),
titleSize
);
const auto lineYPosition = titleRect.y() + (titleRect.height() / 2);
const auto line = QLine(
titleRect.right() + 8,
lineYPosition,
this->width() - this->marginRight,
lineYPosition
);
painter.drawText(titleRect, Qt::AlignCenter, this->title);
painter.setPen(this->lineColor);
painter.drawLine(line);
void LabeledSeparator::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void LabeledSeparator::drawWidget(QPainter& painter) {
const auto fontMetrics = painter.fontMetrics();
const auto titleSize = fontMetrics.size(Qt::TextFlag::TextSingleLine, this->title);
const auto titleRect = QRect(
QPoint(this->marginLeft, (this->height() - titleSize.height()) / 2),
titleSize
);
const auto lineYPosition = titleRect.y() + (titleRect.height() / 2);
const auto line = QLine(
titleRect.right() + 8,
lineYPosition,
this->width() - this->marginRight,
lineYPosition
);
painter.drawText(titleRect, Qt::AlignCenter, this->title);
painter.setPen(this->lineColor);
painter.drawLine(line);
}
}

View File

@@ -2,138 +2,139 @@
#include <QLayout>
using namespace Bloom::Widgets;
PanelWidget::PanelWidget(QWidget* parent): QFrame(parent) {
this->setMouseTracking(false);
this->setAttribute(Qt::WA_Hover, true);
}
void PanelWidget::setMinimumResize(int minimumResize) {
this->minimumResize = minimumResize;
const auto currentSize = this->size();
if (this->panelType == PanelWidgetType::LEFT && currentSize.width() < this->minimumResize) {
this->setFixedWidth(this->minimumResize);
} else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() < this->minimumResize) {
this->setFixedHeight(this->minimumResize);
namespace Bloom::Widgets
{
PanelWidget::PanelWidget(QWidget* parent): QFrame(parent) {
this->setMouseTracking(false);
this->setAttribute(Qt::WA_Hover, true);
}
}
void PanelWidget::setMaximumResize(int maximumResize) {
this->maximumResize = maximumResize;
void PanelWidget::setMinimumResize(int minimumResize) {
this->minimumResize = minimumResize;
const auto currentSize = this->size();
const auto currentSize = this->size();
if (this->panelType == PanelWidgetType::LEFT && currentSize.width() > this->maximumResize) {
this->setFixedWidth(this->maximumResize);
if (this->panelType == PanelWidgetType::LEFT && currentSize.width() < this->minimumResize) {
this->setFixedWidth(this->minimumResize);
} else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() > this->maximumResize) {
this->setFixedHeight(this->maximumResize);
} else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() < this->minimumResize) {
this->setFixedHeight(this->minimumResize);
}
}
}
void PanelWidget::setPanelType(PanelWidgetType panelType) {
this->panelType = panelType;
void PanelWidget::setMaximumResize(int maximumResize) {
this->maximumResize = maximumResize;
if (this->panelType == PanelWidgetType::LEFT) {
this->resizeCursor = Qt::SplitHCursor;
this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
const auto currentSize = this->size();
} else {
this->resizeCursor = Qt::SplitVCursor;
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
if (this->panelType == PanelWidgetType::LEFT && currentSize.width() > this->maximumResize) {
this->setFixedWidth(this->maximumResize);
} else if (this->panelType == PanelWidgetType::BOTTOM && currentSize.height() > this->maximumResize) {
this->setFixedHeight(this->maximumResize);
}
}
}
bool PanelWidget::event(QEvent* event) {
if (event->type() == QEvent::Type::HoverMove) {
auto hoverEvent = static_cast<QHoverEvent*>(event);
if (this->resizingActive || this->isPositionWithinHandleArea(hoverEvent->position().toPoint())) {
this->setCursor(this->resizeCursor);
void PanelWidget::setPanelType(PanelWidgetType panelType) {
this->panelType = panelType;
if (this->panelType == PanelWidgetType::LEFT) {
this->resizeCursor = Qt::SplitHCursor;
this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding);
} else {
this->resizeCursor = Qt::SplitVCursor;
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
}
}
bool PanelWidget::event(QEvent* event) {
if (event->type() == QEvent::Type::HoverMove) {
auto hoverEvent = static_cast<QHoverEvent*>(event);
if (this->resizingActive || this->isPositionWithinHandleArea(hoverEvent->position().toPoint())) {
this->setCursor(this->resizeCursor);
} else {
this->setCursor(Qt::ArrowCursor);
}
}
return QFrame::event(event);
}
void PanelWidget::mousePressEvent(QMouseEvent* event) {
const auto position = event->pos();
if (event->buttons() & Qt::LeftButton && this->isPositionWithinHandleArea(position)) {
this->resizingActive = true;
switch (this->panelType) {
case PanelWidgetType::LEFT: {
this->resizingOffset = this->width() - position.x();
break;
}
case PanelWidgetType::BOTTOM: {
this->resizingOffset = position.y();
break;
}
}
}
}
void PanelWidget::mouseReleaseEvent(QMouseEvent* event) {
if (this->resizingActive) {
this->resizingActive = false;
this->resizingOffset = 0;
this->setCursor(Qt::ArrowCursor);
}
}
return QFrame::event(event);
}
void PanelWidget::mouseMoveEvent(QMouseEvent* event) {
if (this->resizingActive) {
const auto position = event->pos();
void PanelWidget::mousePressEvent(QMouseEvent* event) {
const auto position = event->pos();
if (this->panelType == PanelWidgetType::LEFT) {
this->setFixedWidth(
std::max(
this->minimumResize,
std::min(this->maximumResize, position.x() + this->resizingOffset)
)
);
if (event->buttons() & Qt::LeftButton && this->isPositionWithinHandleArea(position)) {
this->resizingActive = true;
switch (this->panelType) {
case PanelWidgetType::LEFT: {
this->resizingOffset = this->width() - position.x();
break;
}
case PanelWidgetType::BOTTOM: {
this->resizingOffset = position.y();
break;
} else if (this->panelType == PanelWidgetType::BOTTOM) {
this->setFixedHeight(
std::max(
this->minimumResize,
std::min(this->maximumResize, this->height() + (-position.y()) + this->resizingOffset)
)
);
}
}
}
}
void PanelWidget::mouseReleaseEvent(QMouseEvent* event) {
if (this->resizingActive) {
this->resizingActive = false;
this->resizingOffset = 0;
this->setCursor(Qt::ArrowCursor);
}
}
void PanelWidget::mouseMoveEvent(QMouseEvent* event) {
if (this->resizingActive) {
const auto position = event->pos();
std::pair<QPoint, QPoint> PanelWidget::getHandleArea() const {
const auto currentSize = this->size();
if (this->panelType == PanelWidgetType::LEFT) {
this->setFixedWidth(
std::max(
this->minimumResize,
std::min(this->maximumResize, position.x() + this->resizingOffset)
)
return std::pair(
QPoint(currentSize.width() - this->handleSize, 0),
QPoint(currentSize.width(), currentSize.height())
);
} else if (this->panelType == PanelWidgetType::BOTTOM) {
this->setFixedHeight(
std::max(
this->minimumResize,
std::min(this->maximumResize, this->height() + (-position.y()) + this->resizingOffset)
)
} else {
return std::pair(
QPoint(0, 0),
QPoint(currentSize.width(), this->handleSize)
);
}
}
}
std::pair<QPoint, QPoint> PanelWidget::getHandleArea() const {
const auto currentSize = this->size();
bool PanelWidget::isPositionWithinHandleArea(const QPoint& position) const {
const auto handleArea = this->getHandleArea();
if (this->panelType == PanelWidgetType::LEFT) {
return std::pair(
QPoint(currentSize.width() - this->handleSize, 0),
QPoint(currentSize.width(), currentSize.height())
);
} else {
return std::pair(
QPoint(0, 0),
QPoint(currentSize.width(), this->handleSize)
return (
position.x() >= handleArea.first.x() && position.x() <= handleArea.second.x()
&& position.y() >= handleArea.first.y() && position.y() <= handleArea.second.y()
);
}
}
bool PanelWidget::isPositionWithinHandleArea(const QPoint& position) const {
const auto handleArea = this->getHandleArea();
return (
position.x() >= handleArea.first.x() && position.x() <= handleArea.second.x()
&& position.y() >= handleArea.first.y() && position.y() <= handleArea.second.y()
);
}

View File

@@ -2,60 +2,61 @@
#include <QPainter>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
void RotatableLabel::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
static auto containerSize = this->getContainerSize();
static auto textSize = QLabel::minimumSizeHint();
static auto margins = this->contentsMargins();
painter.setClipRect(0, 0, containerSize.width(), containerSize.height());
painter.save();
painter.setPen(Qt::PenStyle::SolidLine);
painter.setPen(QColor(this->isEnabled() ? "#afb1b3" : "#808484"));
painter.translate(std::ceil(containerSize.width() / 2), std::ceil(containerSize.height() / 2));
painter.rotate(this->angle);
painter.drawText(
-(textSize.width() / 2) + margins.left(),
(textSize.height() / 2) + margins.top(),
this->text()
);
void RotatableLabel::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
static auto containerSize = this->getContainerSize();
static auto textSize = QLabel::minimumSizeHint();
static auto margins = this->contentsMargins();
painter.setClipRect(0, 0, containerSize.width(), containerSize.height());
painter.save();
painter.setPen(Qt::PenStyle::SolidLine);
painter.setPen(QColor(this->isEnabled() ? "#afb1b3" : "#808484"));
painter.translate(std::ceil(containerSize.width() / 2), std::ceil(containerSize.height() / 2));
painter.rotate(this->angle);
painter.drawText(
-(textSize.width() / 2) + margins.left(),
(textSize.height() / 2) + margins.top(),
this->text()
);
painter.restore();
}
QSize RotatableLabel::getContainerSize() const {
auto size = QSize();
auto textSize = QLabel::sizeHint();
if (this->angle % 360 == 0 || this->angle % 180 == 0) {
size = textSize;
} else if (this->angle % 90 == 0) {
size.setHeight(textSize.width());
size.setWidth(textSize.height());
} else {
auto angle = this->angle;
if (angle > 90) {
float angleMultiplier = static_cast<float>(angle) / 90;
angleMultiplier = static_cast<float>(angleMultiplier) - std::floor(angleMultiplier);
angle = static_cast<int>(90 * angleMultiplier);
}
auto angleRadians = angle * (M_PI / 180);
size.setWidth(static_cast<int>(
std::cos(angleRadians) * textSize.width()
+ std::ceil(std::sin(angleRadians) * textSize.height())
))
;
size.setHeight(static_cast<int>(
std::sin(angleRadians) * textSize.width()
+ std::ceil(std::cos(angleRadians) * textSize.height())
));
painter.restore();
}
return size;
QSize RotatableLabel::getContainerSize() const {
auto size = QSize();
auto textSize = QLabel::sizeHint();
if (this->angle % 360 == 0 || this->angle % 180 == 0) {
size = textSize;
} else if (this->angle % 90 == 0) {
size.setHeight(textSize.width());
size.setWidth(textSize.height());
} else {
auto angle = this->angle;
if (angle > 90) {
float angleMultiplier = static_cast<float>(angle) / 90;
angleMultiplier = static_cast<float>(angleMultiplier) - std::floor(angleMultiplier);
angle = static_cast<int>(90 * angleMultiplier);
}
auto angleRadians = angle * (M_PI / 180);
size.setWidth(static_cast<int>(
std::cos(angleRadians) * textSize.width()
+ std::ceil(std::sin(angleRadians) * textSize.height())
))
;
size.setHeight(static_cast<int>(
std::sin(angleRadians) * textSize.width()
+ std::ceil(std::cos(angleRadians) * textSize.height())
));
}
return size;
}
}

View File

@@ -2,22 +2,23 @@
#include <QMenu>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
SvgToolButton::SvgToolButton(QWidget* parent): QToolButton(parent) {
this->setButtonWidth(10);
this->setButtonHeight(10);
}
SvgToolButton::SvgToolButton(QWidget* parent): QToolButton(parent) {
this->setButtonWidth(10);
this->setButtonHeight(10);
}
void SvgToolButton::childEvent(QChildEvent* childEvent) {
if ((childEvent->added() || childEvent->polished()) && childEvent->child()->isWidgetType()) {
/*
* If a menu widget has been added as a child to this SvgToolButton, associate the menu with the button
* via QToolButton::setMenu().
*/
auto* menuWidget = qobject_cast<QMenu*>(childEvent->child());
if (menuWidget != nullptr && menuWidget != this->menu()) {
this->setMenu(menuWidget);
void SvgToolButton::childEvent(QChildEvent* childEvent) {
if ((childEvent->added() || childEvent->polished()) && childEvent->child()->isWidgetType()) {
/*
* If a menu widget has been added as a child to this SvgToolButton, associate the menu with the button
* via QToolButton::setMenu().
*/
auto* menuWidget = qobject_cast<QMenu*>(childEvent->child());
if (menuWidget != nullptr && menuWidget != this->menu()) {
this->setMenu(menuWidget);
}
}
}
}

View File

@@ -3,71 +3,76 @@
#include <QPainter>
#include <cmath>
using namespace Bloom::Widgets;
SvgWidget::SvgWidget(QWidget* parent): QFrame(parent) {
this->renderer.setAspectRatioMode(Qt::AspectRatioMode::KeepAspectRatioByExpanding);
}
void SvgWidget::startSpin() {
if (this->spinningAnimation == nullptr) {
this->spinningAnimation = new QPropertyAnimation(this, "angle", this);
this->spinningAnimation->setDuration(2000);
this->spinningAnimation->setStartValue(0);
this->spinningAnimation->setEndValue(360);
QObject::connect(this->spinningAnimation, &QPropertyAnimation::finished, this, [this] {
this->spinningAnimation->start();
});
namespace Bloom::Widgets
{
SvgWidget::SvgWidget(QWidget* parent): QFrame(parent) {
this->renderer.setAspectRatioMode(Qt::AspectRatioMode::KeepAspectRatioByExpanding);
}
this->spinningAnimation->start();
}
void SvgWidget::startSpin() {
if (this->spinningAnimation == nullptr) {
this->spinningAnimation = new QPropertyAnimation(this, "angle", this);
this->spinningAnimation->setDuration(2000);
this->spinningAnimation->setStartValue(0);
this->spinningAnimation->setEndValue(360);
void SvgWidget::stopSpin() {
if (this->spinningAnimation != nullptr) {
this->spinningAnimation->stop();
this->setAngle(0);
}
}
void SvgWidget::paintEvent(QPaintEvent* paintEvent) {
auto painter = QPainter(this);
auto svgSize = this->renderer.defaultSize();
auto margins = this->contentsMargins();
const auto containerSize = this->frameSize();
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
if (this->angle % 360 != 0) {
painter.translate(
std::ceil(static_cast<float>(containerSize.width() / 2)),
std::ceil(static_cast<float>(containerSize.height() / 2))
);
painter.rotate(this->angle);
painter.translate(
-std::ceil(static_cast<float>(containerSize.width() / 2)),
-std::ceil(static_cast<float>(containerSize.height() / 2))
);
}
this->renderer.render(&painter, QRectF(
std::ceil(static_cast<float>(containerSize.width() - svgSize.width()) / 2 + static_cast<float>(margins.left())),
std::ceil(static_cast<float>(containerSize.height() - svgSize.height()) / 2 + static_cast<float>(margins.top())),
svgSize.width(),
svgSize.height()
));
}
void SvgWidget::changeEvent(QEvent* event) {
if (event->type() == QEvent::EnabledChange && !this->disabledSvgFilePath.isEmpty()) {
if (!this->isEnabled()) {
this->renderer.load(this->disabledSvgFilePath);
} else {
this->renderer.load(this->svgFilePath);
QObject::connect(this->spinningAnimation, &QPropertyAnimation::finished, this, [this] {
this->spinningAnimation->start();
});
}
this->repaint();
this->spinningAnimation->start();
}
void SvgWidget::stopSpin() {
if (this->spinningAnimation != nullptr) {
this->spinningAnimation->stop();
this->setAngle(0);
}
}
void SvgWidget::paintEvent(QPaintEvent* paintEvent) {
auto painter = QPainter(this);
auto svgSize = this->renderer.defaultSize();
auto margins = this->contentsMargins();
const auto containerSize = this->frameSize();
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
if (this->angle % 360 != 0) {
painter.translate(
std::ceil(static_cast<float>(containerSize.width() / 2)),
std::ceil(static_cast<float>(containerSize.height() / 2))
);
painter.rotate(this->angle);
painter.translate(
-std::ceil(static_cast<float>(containerSize.width() / 2)),
-std::ceil(static_cast<float>(containerSize.height() / 2))
);
}
this->renderer.render(&painter, QRectF(
std::ceil(
static_cast<float>(containerSize.width() - svgSize.width()) / 2 + static_cast<float>(margins.left())
),
std::ceil(
static_cast<float>(containerSize.height() - svgSize.height()) / 2 + static_cast<float>(margins.top())
),
svgSize.width(),
svgSize.height()
));
}
void SvgWidget::changeEvent(QEvent* event) {
if (event->type() == QEvent::EnabledChange && !this->disabledSvgFilePath.isEmpty()) {
if (!this->isEnabled()) {
this->renderer.load(this->disabledSvgFilePath);
} else {
this->renderer.load(this->svgFilePath);
}
this->repaint();
}
}
}

View File

@@ -4,109 +4,115 @@
#include "ByteItem.hpp"
using namespace Bloom::Widgets;
AnnotationItem::AnnotationItem(
std::uint32_t startAddress,
std::size_t size,
QString labelText,
AnnotationItemPosition position
):
QGraphicsItem(nullptr),
startAddress(startAddress),
size(size),
endAddress(static_cast<std::uint32_t>(startAddress + size - 1)),
labelText(std::move(labelText)),
position(position),
width(static_cast<int>((ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) * size - ByteItem::RIGHT_MARGIN)),
height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : AnnotationItem::BOTTOM_HEIGHT
) {
this->setAcceptHoverEvents(true);
this->setToolTip(this->labelText);
}
AnnotationItem::AnnotationItem(
const Targets::TargetMemoryAddressRange& addressRange,
const QString& labelText,
AnnotationItemPosition position
): AnnotationItem(
addressRange.startAddress,
addressRange.endAddress - addressRange.startAddress + 1,
labelText,
position
) {}
AnnotationItem::AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position)
: AnnotationItem(
focusedMemoryRegion.addressRange,
focusedMemoryRegion.name,
position
) {}
void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto lineColor = this->getLineColor();
auto labelFontColor = this->getLabelFontColor();
auto font = painter->font();
font.setPixelSize(this->getLabelFontSize());
painter->setFont(font);
const auto isEnabled = this->isEnabled();
lineColor.setAlpha(isEnabled ? 255 : 100);
labelFontColor.setAlpha(isEnabled ? 255 : 100);
const auto fontMetrics = painter->fontMetrics();
auto labelSize = fontMetrics.size(Qt::TextSingleLine, this->labelText);
if (labelSize.width() > this->width) {
labelSize.setWidth(this->width);
this->labelText = fontMetrics.elidedText(this->labelText, Qt::TextElideMode::ElideRight, this->width);
namespace Bloom::Widgets
{
AnnotationItem::AnnotationItem(
std::uint32_t startAddress,
std::size_t size,
QString labelText,
AnnotationItemPosition position
):
QGraphicsItem(nullptr),
startAddress(startAddress),
size(size),
endAddress(static_cast<std::uint32_t>(startAddress + size - 1)),
labelText(std::move(labelText)),
position(position),
width(static_cast<int>((ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) * size - ByteItem::RIGHT_MARGIN)),
height(position == AnnotationItemPosition::TOP ? AnnotationItem::TOP_HEIGHT : AnnotationItem::BOTTOM_HEIGHT
) {
this->setAcceptHoverEvents(true);
this->setToolTip(this->labelText);
}
constexpr auto verticalLineLength = 5;
const auto verticalLineYStart = this->position == AnnotationItemPosition::BOTTOM ? 0 : AnnotationItem::TOP_HEIGHT;
const auto verticalLineYEnd = this->position == AnnotationItemPosition::BOTTOM ?
verticalLineLength : AnnotationItem::TOP_HEIGHT - verticalLineLength;
AnnotationItem::AnnotationItem(
const Targets::TargetMemoryAddressRange& addressRange,
const QString& labelText,
AnnotationItemPosition position
): AnnotationItem(
addressRange.startAddress,
addressRange.endAddress - addressRange.startAddress + 1,
labelText,
position
) {}
const auto labelRect = QRect(
(this->width - labelSize.width()) / 2,
verticalLineYEnd - (this->position == AnnotationItemPosition::BOTTOM ? -6: labelSize.height() + 6),
labelSize.width(),
labelSize.height()
);
AnnotationItem::AnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion, AnnotationItemPosition position)
: AnnotationItem(
focusedMemoryRegion.addressRange,
focusedMemoryRegion.name,
position
) {}
painter->setPen(lineColor);
void AnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto lineColor = this->getLineColor();
auto labelFontColor = this->getLabelFontColor();
if (this->size > 1) {
painter->drawLine(QLine(
ByteItem::WIDTH / 2,
verticalLineYStart,
ByteItem::WIDTH / 2,
verticalLineYEnd
));
auto font = painter->font();
font.setPixelSize(this->getLabelFontSize());
painter->setFont(font);
const auto isEnabled = this->isEnabled();
lineColor.setAlpha(isEnabled ? 255 : 100);
labelFontColor.setAlpha(isEnabled ? 255 : 100);
const auto fontMetrics = painter->fontMetrics();
auto labelSize = fontMetrics.size(Qt::TextSingleLine, this->labelText);
if (labelSize.width() > this->width) {
labelSize.setWidth(this->width);
this->labelText = fontMetrics.elidedText(
this->labelText,
Qt::TextElideMode::ElideRight,
this->width
);
}
constexpr auto verticalLineLength = 5;
const auto verticalLineYStart = this->position == AnnotationItemPosition::BOTTOM ? 0
: AnnotationItem::TOP_HEIGHT;
const auto verticalLineYEnd = this->position == AnnotationItemPosition::BOTTOM ?
verticalLineLength : AnnotationItem::TOP_HEIGHT - verticalLineLength;
const auto labelRect = QRect(
(this->width - labelSize.width()) / 2,
verticalLineYEnd - (this->position == AnnotationItemPosition::BOTTOM ? -6: labelSize.height() + 6),
labelSize.width(),
labelSize.height()
);
painter->setPen(lineColor);
if (this->size > 1) {
painter->drawLine(QLine(
ByteItem::WIDTH / 2,
verticalLineYStart,
ByteItem::WIDTH / 2,
verticalLineYEnd
));
painter->drawLine(QLine(
this->width - (ByteItem::WIDTH / 2),
verticalLineYStart,
this->width - (ByteItem::WIDTH / 2),
verticalLineYEnd
));
painter->drawLine(QLine(
ByteItem::WIDTH / 2,
verticalLineYEnd,
(ByteItem::WIDTH / 2) + (this->width - ByteItem::WIDTH),
verticalLineYEnd
));
}
painter->drawLine(QLine(
this->width - (ByteItem::WIDTH / 2),
verticalLineYStart,
this->width - (ByteItem::WIDTH / 2),
verticalLineYEnd
));
painter->drawLine(QLine(
ByteItem::WIDTH / 2,
this->width / 2,
verticalLineYEnd,
(ByteItem::WIDTH / 2) + (this->width - ByteItem::WIDTH),
verticalLineYEnd
this->width / 2,
(this->position == AnnotationItemPosition::BOTTOM ? verticalLineYEnd + 4 : verticalLineYEnd - 4)
));
painter->setPen(labelFontColor);
painter->drawText(labelRect, Qt::AlignCenter, this->labelText);
}
painter->drawLine(QLine(
this->width / 2,
verticalLineYEnd,
this->width / 2,
(this->position == AnnotationItemPosition::BOTTOM ? verticalLineYEnd + 4 : verticalLineYEnd - 4)
));
painter->setPen(labelFontColor);
painter->drawText(labelRect, Qt::AlignCenter, this->labelText);
}

View File

@@ -1,71 +1,72 @@
#include "ByteAddressContainer.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
void ByteAddressContainer::adjustAddressLabels(
const std::map<std::size_t, std::vector<ByteItem*>>& byteItemsByRowIndex
) {
static const auto margins = QMargins(0, 10, 0, 0);
void ByteAddressContainer::adjustAddressLabels(
const std::map<std::size_t, std::vector<ByteItem*>>& byteItemsByRowIndex
) {
static const auto margins = QMargins(0, 10, 0, 0);
const auto newRowCount = byteItemsByRowIndex.size();
const auto layoutItemMaxIndex = static_cast<int>(this->addressItemsByRowIndex.size() - 1);
const auto newRowCount = byteItemsByRowIndex.size();
const auto layoutItemMaxIndex = static_cast<int>(this->addressItemsByRowIndex.size() - 1);
for (const auto& mappingPair : byteItemsByRowIndex) {
const auto rowIndex = static_cast<std::size_t>(mappingPair.first);
const auto& byteItems = mappingPair.second;
for (const auto& mappingPair : byteItemsByRowIndex) {
const auto rowIndex = static_cast<std::size_t>(mappingPair.first);
const auto& byteItems = mappingPair.second;
if (byteItems.empty()) {
continue;
}
if (byteItems.empty()) {
continue;
ByteAddressItem* addressLabel = nullptr;
if (static_cast<int>(rowIndex) > layoutItemMaxIndex) {
addressLabel = new ByteAddressItem(this);
this->addressItemsByRowIndex.insert(std::pair(rowIndex, addressLabel));
} else {
addressLabel = this->addressItemsByRowIndex.at(rowIndex);
}
const auto& firstByteItem = byteItems.front();
addressLabel->setAddressHex(firstByteItem->relativeAddressHex);
addressLabel->setPos(
0,
firstByteItem->pos().y()
);
}
ByteAddressItem* addressLabel = nullptr;
if (static_cast<int>(rowIndex) > layoutItemMaxIndex) {
addressLabel = new ByteAddressItem(this);
this->addressItemsByRowIndex.insert(std::pair(rowIndex, addressLabel));
// Delete any address items we no longer need
const auto addressItemCount = this->addressItemsByRowIndex.size();
} else {
addressLabel = this->addressItemsByRowIndex.at(rowIndex);
if (newRowCount > 0 && newRowCount < addressItemCount) {
for (auto i = (addressItemCount - 1); i >= newRowCount; i--) {
delete this->addressItemsByRowIndex.at(i);
this->addressItemsByRowIndex.erase(i);
}
}
const auto& firstByteItem = byteItems.front();
addressLabel->setAddressHex(firstByteItem->relativeAddressHex);
addressLabel->setPos(
this->update();
}
void ByteAddressContainer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
static const auto backgroundColor = QColor(0x35, 0x36, 0x33);
static const auto borderColor = QColor(0x41, 0x42, 0x3F);
painter->setPen(Qt::PenStyle::NoPen);
painter->setBrush(backgroundColor);
painter->drawRect(
0,
firstByteItem->pos().y()
0,
ByteAddressContainer::WIDTH,
static_cast<int>(this->boundingRect().height())
);
painter->setPen(borderColor);
painter->drawLine(
ByteAddressContainer::WIDTH - 1,
0,
ByteAddressContainer::WIDTH - 1,
static_cast<int>(this->boundingRect().height())
);
}
// Delete any address items we no longer need
const auto addressItemCount = this->addressItemsByRowIndex.size();
if (newRowCount > 0 && newRowCount < addressItemCount) {
for (auto i = (addressItemCount - 1); i >= newRowCount; i--) {
delete this->addressItemsByRowIndex.at(i);
this->addressItemsByRowIndex.erase(i);
}
}
this->update();
}
void ByteAddressContainer::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
static const auto backgroundColor = QColor(0x35, 0x36, 0x33);
static const auto borderColor = QColor(0x41, 0x42, 0x3F);
painter->setPen(Qt::PenStyle::NoPen);
painter->setBrush(backgroundColor);
painter->drawRect(
0,
0,
ByteAddressContainer::WIDTH,
static_cast<int>(this->boundingRect().height())
);
painter->setPen(borderColor);
painter->drawLine(
ByteAddressContainer::WIDTH - 1,
0,
ByteAddressContainer::WIDTH - 1,
static_cast<int>(this->boundingRect().height())
);
}

View File

@@ -1,28 +1,31 @@
#include "ByteAddressItem.hpp"
#include <QPainter>
#include <QStyle>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
void ByteAddressItem::setAddressHex(const QString& addressHex) {
this->setCacheMode(
QGraphicsItem::CacheMode::ItemCoordinateCache,
QSize(ByteAddressItem::WIDTH, ByteAddressItem::HEIGHT)
);
this->addressHex = addressHex;
}
void ByteAddressItem::setAddressHex(const QString& addressHex) {
this->setCacheMode(
QGraphicsItem::CacheMode::ItemCoordinateCache,
QSize(ByteAddressItem::WIDTH, ByteAddressItem::HEIGHT)
);
this->addressHex = addressHex;
}
void ByteAddressItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
static const auto widgetRect = this->boundingRect();
static auto fontColor = QColor(0x8F, 0x91, 0x92);
static auto font = QFont("'Ubuntu', sans-serif");
font.setPixelSize(12);
fontColor.setAlpha(!this->isEnabled() ? 100 : 255);
painter->setFont(font);
painter->setPen(fontColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->addressHex);
void ByteAddressItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
static const auto widgetRect = this->boundingRect();
static auto fontColor = QColor(0x8F, 0x91, 0x92);
static auto font = QFont("'Ubuntu', sans-serif");
font.setPixelSize(12);
fontColor.setAlpha(!this->isEnabled() ? 100 : 255);
painter->setFont(font);
painter->setPen(fontColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->addressHex);
}
}

View File

@@ -1,166 +1,175 @@
#include "ByteItem.hpp"
#include <QPainter>
#include <QStyle>
using namespace Bloom::Widgets;
ByteItem::ByteItem(
std::size_t byteIndex,
std::uint32_t address,
std::optional<std::uint32_t>& currentStackPointer,
std::optional<ByteItem*>& hoveredByteItem,
std::optional<AnnotationItem*>& hoveredAnnotationItem,
std::set<std::uint32_t>& highlightedAddresses,
const HexViewerWidgetSettings& settings
):
QGraphicsItem(nullptr),
byteIndex(byteIndex),
address(address),
currentStackPointer(currentStackPointer),
hoveredByteItem(hoveredByteItem),
hoveredAnnotationItem(hoveredAnnotationItem),
highlightedAddresses(highlightedAddresses),
settings(settings)
namespace Bloom::Widgets
{
this->setCacheMode(
QGraphicsItem::CacheMode::ItemCoordinateCache,
QSize(ByteItem::WIDTH, ByteItem::HEIGHT)
);
this->setAcceptHoverEvents(true);
ByteItem::ByteItem(
std::size_t byteIndex,
std::uint32_t address,
std::optional<std::uint32_t>& currentStackPointer,
std::optional<ByteItem*>& hoveredByteItem,
std::optional<AnnotationItem*>& hoveredAnnotationItem,
std::set<std::uint32_t>& highlightedAddresses,
const HexViewerWidgetSettings& settings
)
: QGraphicsItem(nullptr)
, byteIndex(byteIndex)
, address(address)
, currentStackPointer(currentStackPointer)
, hoveredByteItem(hoveredByteItem)
, hoveredAnnotationItem(hoveredAnnotationItem)
, highlightedAddresses(highlightedAddresses)
, settings(settings)
{
this->setCacheMode(
QGraphicsItem::CacheMode::ItemCoordinateCache,
QSize(ByteItem::WIDTH, ByteItem::HEIGHT)
);
this->setAcceptHoverEvents(true);
this->addressHex = "0x" + QString::number(this->address, 16).rightJustified(8, '0').toUpper();
this->relativeAddressHex = "0x" + QString::number(this->byteIndex, 16).rightJustified(8, '0').toUpper();
this->addressHex = "0x" + QString::number(this->address, 16).rightJustified(
8,
'0'
).toUpper();
this->relativeAddressHex = "0x" + QString::number(this->byteIndex, 16).rightJustified(
8,
'0'
).toUpper();
this->setSelected(false);
}
void ByteItem::setValue(unsigned char value) {
this->valueChanged = this->valueInitialised && this->value != value;
this->value = value;
this->hexValue = QString::number(this->value, 16).rightJustified(2, '0').toUpper();
this->asciiValue = (this->value >= 32 && this->value <= 126)
? std::optional("'" + QString(QChar(this->value)) + "'") : std::nullopt;
this->valueInitialised = this->excludedMemoryRegion == nullptr;
this->update();
}
void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
painter->setPen(Qt::PenStyle::NoPen);
// TODO: This code could do with some tidying. It's getting quite messy.
static const auto widgetRect = this->boundingRect();
static const auto standardTextColor = QColor(0xAF, 0xB1, 0xB3);
static const auto valueChangedTextColor = QColor(0x54, 0x7F, 0xBA);
static auto font = QFont("'Ubuntu', sans-serif");
static const auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255);
static const auto focusedRegionBackgroundColor = QColor(0x44, 0x44, 0x41, 255);
static const auto stackMemoryBackgroundColor = QColor(0x67, 0x57, 0x20, 210);
static const auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70);
static const auto hoveredNeighbourBackgroundColor = QColor(0x8E, 0x8B, 0x83, 30);
static const auto hoveredAnnotationBackgroundColor = QColor(0x8E, 0x8B, 0x83, 50);
const auto isEnabled = this->isEnabled();
const auto highlightingEnabled = !this->highlightedAddresses.empty();
const auto highlightedByte = highlightingEnabled && this->highlightedAddresses.contains(this->address);
auto textColor = standardTextColor;
auto asciiTextColor = QColor(0xA7, 0x77, 0x26);
auto backgroundColor = std::optional<QColor>();
font.setPixelSize(11);
painter->setFont(font);
if (highlightedByte) {
backgroundColor = highlightedBackgroundColor;
asciiTextColor = standardTextColor;
} else if (this->settings.highlightStackMemory && this->currentStackPointer.has_value()
&& this->address > this->currentStackPointer
) {
// This byte is within the stack memory
backgroundColor = stackMemoryBackgroundColor;
asciiTextColor = standardTextColor;
} else if (this->settings.highlightFocusedMemory && this->focusedMemoryRegion != nullptr) {
// This byte is within a focused region
backgroundColor = focusedRegionBackgroundColor;
this->setSelected(false);
}
const auto* hoveredByteItem = this->hoveredByteItem.value_or(nullptr);
const auto* hoveredAnnotationItem = this->hoveredAnnotationItem.value_or(nullptr);
if (hoveredByteItem != nullptr) {
if (hoveredByteItem == this) {
if (backgroundColor.has_value()) {
backgroundColor->setAlpha(255);
void ByteItem::setValue(unsigned char value) {
this->valueChanged = this->valueInitialised && this->value != value;
} else {
backgroundColor = hoveredBackgroundColor;
}
this->value = value;
this->hexValue = QString::number(this->value, 16).rightJustified(2, '0').toUpper();
this->asciiValue = (this->value >= 32 && this->value <= 126)
? std::optional("'" + QString(QChar(this->value)) + "'") : std::nullopt;
} else if (this->settings.highlightHoveredRowAndCol
&& (
hoveredByteItem->currentColumnIndex == this->currentColumnIndex
|| hoveredByteItem->currentRowIndex == this->currentRowIndex
)
this->valueInitialised = this->excludedMemoryRegion == nullptr;
this->update();
}
void ByteItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
painter->setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
painter->setPen(Qt::PenStyle::NoPen);
// TODO: This code could do with some tidying. It's getting quite messy.
static const auto widgetRect = this->boundingRect();
static const auto standardTextColor = QColor(0xAF, 0xB1, 0xB3);
static const auto valueChangedTextColor = QColor(0x54, 0x7F, 0xBA);
static auto font = QFont("'Ubuntu', sans-serif");
static const auto highlightedBackgroundColor = QColor(0x3C, 0x59, 0x5C, 255);
static const auto focusedRegionBackgroundColor = QColor(0x44, 0x44, 0x41, 255);
static const auto stackMemoryBackgroundColor = QColor(0x67, 0x57, 0x20, 210);
static const auto hoveredBackgroundColor = QColor(0x8E, 0x8B, 0x83, 70);
static const auto hoveredNeighbourBackgroundColor = QColor(0x8E, 0x8B, 0x83, 30);
static const auto hoveredAnnotationBackgroundColor = QColor(0x8E, 0x8B, 0x83, 50);
const auto isEnabled = this->isEnabled();
const auto highlightingEnabled = !this->highlightedAddresses.empty();
const auto highlightedByte = highlightingEnabled && this->highlightedAddresses.contains(this->address);
auto textColor = standardTextColor;
auto asciiTextColor = QColor(0xA7, 0x77, 0x26);
auto backgroundColor = std::optional<QColor>();
font.setPixelSize(11);
painter->setFont(font);
if (highlightedByte) {
backgroundColor = highlightedBackgroundColor;
asciiTextColor = standardTextColor;
} else if (this->settings.highlightStackMemory && this->currentStackPointer.has_value()
&& this->address > this->currentStackPointer
) {
if (backgroundColor.has_value()) {
backgroundColor->setAlpha(220);
// This byte is within the stack memory
backgroundColor = stackMemoryBackgroundColor;
asciiTextColor = standardTextColor;
} else if (this->settings.highlightFocusedMemory && this->focusedMemoryRegion != nullptr) {
// This byte is within a focused region
backgroundColor = focusedRegionBackgroundColor;
}
const auto* hoveredByteItem = this->hoveredByteItem.value_or(nullptr);
const auto* hoveredAnnotationItem = this->hoveredAnnotationItem.value_or(nullptr);
if (hoveredByteItem != nullptr) {
if (hoveredByteItem == this) {
if (backgroundColor.has_value()) {
backgroundColor->setAlpha(255);
} else {
backgroundColor = hoveredBackgroundColor;
}
} else if (this->settings.highlightHoveredRowAndCol
&& (
hoveredByteItem->currentColumnIndex == this->currentColumnIndex
|| hoveredByteItem->currentRowIndex == this->currentRowIndex
)
) {
if (backgroundColor.has_value()) {
backgroundColor->setAlpha(220);
} else {
backgroundColor = hoveredNeighbourBackgroundColor;
}
}
} else if (
!this->settings.highlightFocusedMemory
&& hoveredAnnotationItem != nullptr
&& this->address >= hoveredAnnotationItem->startAddress
&& this->address <= hoveredAnnotationItem->endAddress
) {
backgroundColor = hoveredAnnotationBackgroundColor;
}
if (backgroundColor.has_value()) {
if (!isEnabled || (highlightingEnabled && !highlightedByte)) {
backgroundColor->setAlpha(100);
}
painter->setBrush(backgroundColor.value());
painter->drawRect(widgetRect);
}
if (this->valueInitialised && this->excludedMemoryRegion == nullptr) {
if (this->settings.displayAsciiValues && this->asciiValue.has_value()) {
if (!isEnabled || (highlightingEnabled && !highlightedByte)) {
asciiTextColor.setAlpha(100);
}
painter->setPen(asciiTextColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->asciiValue.value());
} else {
backgroundColor = hoveredNeighbourBackgroundColor;
if (!isEnabled || (highlightingEnabled && !highlightedByte) || this->settings.displayAsciiValues) {
textColor.setAlpha(100);
}
painter->setPen(textColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->hexValue);
return;
}
}
} else if (
!this->settings.highlightFocusedMemory
&& hoveredAnnotationItem != nullptr
&& this->address >= hoveredAnnotationItem->startAddress
&& this->address <= hoveredAnnotationItem->endAddress
) {
backgroundColor = hoveredAnnotationBackgroundColor;
}
if (backgroundColor.has_value()) {
if (!isEnabled || (highlightingEnabled && !highlightedByte)) {
backgroundColor->setAlpha(100);
}
painter->setBrush(backgroundColor.value());
painter->drawRect(widgetRect);
}
if (this->valueInitialised && this->excludedMemoryRegion == nullptr) {
if (this->settings.displayAsciiValues && this->asciiValue.has_value()) {
if (!isEnabled || (highlightingEnabled && !highlightedByte)) {
asciiTextColor.setAlpha(100);
}
painter->setPen(asciiTextColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->asciiValue.value());
} else {
if (!isEnabled || (highlightingEnabled && !highlightedByte) || this->settings.displayAsciiValues) {
textColor.setAlpha(100);
}
textColor.setAlpha(100);
painter->setPen(textColor);
painter->drawText(widgetRect, Qt::AlignCenter, this->hexValue);
return;
static const auto placeholderString = QString("??");
painter->drawText(widgetRect, Qt::AlignCenter, placeholderString);
}
} else {
textColor.setAlpha(100);
painter->setPen(textColor);
static const auto placeholderString = QString("??");
painter->drawText(widgetRect, Qt::AlignCenter, placeholderString);
}
}

View File

@@ -1,53 +1,54 @@
#include "ByteItemContainerGraphicsView.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
using Bloom::Targets::TargetMemoryDescriptor;
using Bloom::Targets::TargetMemoryDescriptor;
ByteItemContainerGraphicsView::ByteItemContainerGraphicsView(
const TargetMemoryDescriptor& targetMemoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
const HexViewerWidgetSettings& settings,
QLabel* hoveredAddressLabel,
QWidget* parent
): QGraphicsView(parent) {
this->setObjectName("graphics-view");
this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
this->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
this->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
this->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
ByteItemContainerGraphicsView::ByteItemContainerGraphicsView(
const TargetMemoryDescriptor& targetMemoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
const HexViewerWidgetSettings& settings,
QLabel* hoveredAddressLabel,
QWidget* parent
): QGraphicsView(parent) {
this->setObjectName("graphics-view");
this->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
this->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
this->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
this->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
this->scene = new ByteItemGraphicsScene(
targetMemoryDescriptor,
focusedMemoryRegions,
excludedMemoryRegions,
insightWorker,
settings,
hoveredAddressLabel,
this
);
this->scene = new ByteItemGraphicsScene(
targetMemoryDescriptor,
focusedMemoryRegions,
excludedMemoryRegions,
insightWorker,
settings,
hoveredAddressLabel,
this
);
this->setScene(this->scene);
this->setScene(this->scene);
}
void ByteItemContainerGraphicsView::scrollToByteItemAtAddress(std::uint32_t address) {
this->centerOn(this->scene->getByteItemPositionByAddress(address));
}
bool ByteItemContainerGraphicsView::event(QEvent* event) {
const auto eventType = event->type();
if (eventType == QEvent::Type::EnabledChange) {
this->scene->setEnabled(this->isEnabled());
}
return QGraphicsView::event(event);
}
void ByteItemContainerGraphicsView::scrollToByteItemAtAddress(std::uint32_t address) {
this->centerOn(this->scene->getByteItemPositionByAddress(address));
}
void ByteItemContainerGraphicsView::resizeEvent(QResizeEvent* event) {
QGraphicsView::resizeEvent(event);
this->scene->adjustSize();
bool ByteItemContainerGraphicsView::event(QEvent* event) {
const auto eventType = event->type();
if (eventType == QEvent::Type::EnabledChange) {
this->scene->setEnabled(this->isEnabled());
}
return QGraphicsView::event(event);
}
void ByteItemContainerGraphicsView::resizeEvent(QResizeEvent* event) {
QGraphicsView::resizeEvent(event);
this->scene->adjustSize();
}
}

View File

@@ -2,494 +2,497 @@
#include <cmath>
using namespace Bloom::Widgets;
using Bloom::Targets::TargetMemoryDescriptor;
ByteItemGraphicsScene::ByteItemGraphicsScene(
const TargetMemoryDescriptor& targetMemoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
const HexViewerWidgetSettings& settings,
QLabel* hoveredAddressLabel,
QGraphicsView* parent
):
QGraphicsScene(parent),
targetMemoryDescriptor(targetMemoryDescriptor),
focusedMemoryRegions(focusedMemoryRegions),
excludedMemoryRegions(excludedMemoryRegions),
insightWorker(insightWorker),
settings(settings),
hoveredAddressLabel(hoveredAddressLabel),
parent(parent)
namespace Bloom::Widgets
{
this->setObjectName("byte-widget-container");
using Bloom::Targets::TargetMemoryDescriptor;
this->byteAddressContainer = new ByteAddressContainer();
this->addItem(this->byteAddressContainer);
ByteItemGraphicsScene::ByteItemGraphicsScene(
const TargetMemoryDescriptor& targetMemoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
const HexViewerWidgetSettings& settings,
QLabel* hoveredAddressLabel,
QGraphicsView* parent
)
: QGraphicsScene(parent)
, targetMemoryDescriptor(targetMemoryDescriptor)
, focusedMemoryRegions(focusedMemoryRegions)
, excludedMemoryRegions(excludedMemoryRegions)
, insightWorker(insightWorker)
, settings(settings)
, hoveredAddressLabel(hoveredAddressLabel)
, parent(parent)
{
this->setObjectName("byte-widget-container");
// Construct ByteWidget objects
const auto memorySize = this->targetMemoryDescriptor.size();
const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress;
for (std::uint32_t i = 0; i < memorySize; i++) {
const auto address = startAddress + i;
this->byteAddressContainer = new ByteAddressContainer();
this->addItem(this->byteAddressContainer);
auto* byteWidget = new ByteItem(
i,
address,
this->currentStackPointer,
this->hoveredByteWidget,
this->hoveredAnnotationItem,
this->highlightedAddresses,
settings
// Construct ByteWidget objects
const auto memorySize = this->targetMemoryDescriptor.size();
const auto startAddress = this->targetMemoryDescriptor.addressRange.startAddress;
for (std::uint32_t i = 0; i < memorySize; i++) {
const auto address = startAddress + i;
auto* byteWidget = new ByteItem(
i,
address,
this->currentStackPointer,
this->hoveredByteWidget,
this->hoveredAnnotationItem,
this->highlightedAddresses,
settings
);
this->byteItemsByAddress.insert(std::pair(
address,
byteWidget
));
this->addItem(byteWidget);
}
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&ByteItemGraphicsScene::onTargetStateChanged
);
this->byteItemsByAddress.insert(std::pair(
address,
byteWidget
));
this->addItem(byteWidget);
this->refreshRegions();
this->adjustSize();
}
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&ByteItemGraphicsScene::onTargetStateChanged
);
void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buffer) {
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
byteWidget->setValue(buffer.at(byteWidget->byteIndex));
}
this->refreshRegions();
this->adjustSize();
}
void ByteItemGraphicsScene::updateValues(const Targets::TargetMemoryBuffer& buffer) {
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
byteWidget->setValue(buffer.at(byteWidget->byteIndex));
this->updateAnnotationValues(buffer);
this->lastValueBuffer = buffer;
}
this->updateAnnotationValues(buffer);
this->lastValueBuffer = buffer;
}
void ByteItemGraphicsScene::updateStackPointer(std::uint32_t stackPointer) {
this->currentStackPointer = stackPointer;
this->invalidateChildItemCaches();
}
void ByteItemGraphicsScene::updateStackPointer(std::uint32_t stackPointer) {
this->currentStackPointer = stackPointer;
this->invalidateChildItemCaches();
}
void ByteItemGraphicsScene::setHighlightedAddresses(const std::set<std::uint32_t>& highlightedAddresses) {
this->highlightedAddresses = highlightedAddresses;
this->invalidateChildItemCaches();
}
void ByteItemGraphicsScene::setHighlightedAddresses(const std::set<std::uint32_t>& highlightedAddresses) {
this->highlightedAddresses = highlightedAddresses;
this->invalidateChildItemCaches();
}
void ByteItemGraphicsScene::refreshRegions() {
for (auto& [byteAddress, byteWidget] : this->byteItemsByAddress) {
byteWidget->focusedMemoryRegion = nullptr;
byteWidget->excludedMemoryRegion = nullptr;
void ByteItemGraphicsScene::refreshRegions() {
for (auto& [byteAddress, byteWidget] : this->byteItemsByAddress) {
byteWidget->focusedMemoryRegion = nullptr;
byteWidget->excludedMemoryRegion = nullptr;
for (const auto& focusedRegion : this->focusedMemoryRegions) {
if (byteAddress >= focusedRegion.addressRange.startAddress
&& byteAddress <= focusedRegion.addressRange.endAddress
) {
byteWidget->focusedMemoryRegion = &focusedRegion;
break;
}
}
for (const auto& excludedRegion : this->excludedMemoryRegions) {
if (byteAddress >= excludedRegion.addressRange.startAddress
&& byteAddress <= excludedRegion.addressRange.endAddress
) {
byteWidget->excludedMemoryRegion = &excludedRegion;
break;
}
}
byteWidget->update();
}
// Refresh annotation items
this->hoveredAnnotationItem = std::nullopt;
for (auto* annotationItem : this->annotationItems) {
this->removeItem(annotationItem);
delete annotationItem;
}
this->annotationItems.clear();
this->valueAnnotationItems.clear();
for (const auto& focusedRegion : this->focusedMemoryRegions) {
if (byteAddress >= focusedRegion.addressRange.startAddress
&& byteAddress <= focusedRegion.addressRange.endAddress
) {
byteWidget->focusedMemoryRegion = &focusedRegion;
break;
auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM);
annotationItem->setEnabled(this->enabled);
this->addItem(annotationItem);
this->annotationItems.emplace_back(annotationItem);
if (focusedRegion.dataType != MemoryRegionDataType::UNKNOWN) {
auto* valueAnnotationItem = new ValueAnnotationItem(focusedRegion);
valueAnnotationItem->setEnabled(this->enabled);
this->addItem(valueAnnotationItem);
this->annotationItems.emplace_back(valueAnnotationItem);
this->valueAnnotationItems.emplace_back(valueAnnotationItem);
}
}
for (const auto& excludedRegion : this->excludedMemoryRegions) {
if (byteAddress >= excludedRegion.addressRange.startAddress
&& byteAddress <= excludedRegion.addressRange.endAddress
) {
byteWidget->excludedMemoryRegion = &excludedRegion;
break;
if (this->targetState == Targets::TargetState::STOPPED && this->enabled && !this->lastValueBuffer.empty()) {
this->updateAnnotationValues(this->lastValueBuffer);
}
this->adjustSize(true);
}
void ByteItemGraphicsScene::adjustSize(bool forced) {
const auto width = this->getSceneWidth();
const auto columnCount = static_cast<std::size_t>(
std::floor(
(width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH
+ ByteItem::RIGHT_MARGIN) / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
)
);
const auto rowCount = static_cast<int>(
std::ceil(static_cast<double>(this->byteItemsByAddress.size()) / static_cast<double>(columnCount))
);
// Don't bother recalculating the byte & annotation positions if the number of rows & columns haven't changed.
if (this->byteItemsByAddress.empty()
|| (
!forced
&& rowCount == this->byteItemsByRowIndex.size()
&& columnCount == this->byteItemsByColumnIndex.size()
)
) {
this->setSceneRect(
0,
0,
width,
std::max(static_cast<int>(this->sceneRect().height()), this->parent->viewport()->height())
);
return;
}
if (!this->byteItemsByAddress.empty()) {
this->adjustByteItemPositions();
if (this->settings.displayAnnotations) {
this->adjustAnnotationItemPositions();
}
const auto* lastByteItem = (--this->byteItemsByAddress.end())->second;
const auto sceneHeight = static_cast<int>(
lastByteItem->pos().y() + ByteItem::HEIGHT + AnnotationItem::BOTTOM_HEIGHT + this->margins.bottom()
);
this->setSceneRect(
0,
0,
width,
std::max(sceneHeight, this->parent->height())
);
}
byteWidget->update();
this->update();
}
// Refresh annotation items
this->hoveredAnnotationItem = std::nullopt;
for (auto* annotationItem : this->annotationItems) {
this->removeItem(annotationItem);
delete annotationItem;
}
void ByteItemGraphicsScene::setEnabled(bool enabled) {
if (this->enabled != enabled) {
this->enabled = enabled;
this->annotationItems.clear();
this->valueAnnotationItems.clear();
for (auto& [byteAddress, byteItem] : this->byteItemsByAddress) {
byteItem->setEnabled(this->enabled);
}
for (const auto& focusedRegion : this->focusedMemoryRegions) {
auto* annotationItem = new AnnotationItem(focusedRegion, AnnotationItemPosition::BOTTOM);
annotationItem->setEnabled(this->enabled);
this->addItem(annotationItem);
this->annotationItems.emplace_back(annotationItem);
for (auto* annotationItem : this->annotationItems) {
annotationItem->setEnabled(this->enabled);
}
if (focusedRegion.dataType != MemoryRegionDataType::UNKNOWN) {
auto* valueAnnotationItem = new ValueAnnotationItem(focusedRegion);
valueAnnotationItem->setEnabled(this->enabled);
this->addItem(valueAnnotationItem);
this->annotationItems.emplace_back(valueAnnotationItem);
this->valueAnnotationItems.emplace_back(valueAnnotationItem);
this->byteAddressContainer->setEnabled(enabled);
this->byteAddressContainer->update();
this->update();
}
}
if (this->targetState == Targets::TargetState::STOPPED && this->enabled && !this->lastValueBuffer.empty()) {
this->updateAnnotationValues(this->lastValueBuffer);
}
this->adjustSize(true);
}
void ByteItemGraphicsScene::adjustSize(bool forced) {
const auto width = this->getSceneWidth();
const auto columnCount = static_cast<std::size_t>(
std::floor(
(width - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN)
/ (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
)
);
const auto rowCount = static_cast<int>(
std::ceil(static_cast<double>(this->byteItemsByAddress.size()) / static_cast<double>(columnCount))
);
// Don't bother recalculating the byte item & annotation positions if the number of rows & columns have not changed.
if (this->byteItemsByAddress.empty()
|| (
!forced
&& rowCount == this->byteItemsByRowIndex.size()
&& columnCount == this->byteItemsByColumnIndex.size()
)
) {
this->setSceneRect(
0,
0,
width,
std::max(static_cast<int>(this->sceneRect().height()), this->parent->viewport()->height())
);
return;
}
if (!this->byteItemsByAddress.empty()) {
this->adjustByteItemPositions();
if (this->settings.displayAnnotations) {
this->adjustAnnotationItemPositions();
}
const auto* lastByteItem = (--this->byteItemsByAddress.end())->second;
const auto sceneHeight = static_cast<int>(
lastByteItem->pos().y() + ByteItem::HEIGHT + AnnotationItem::BOTTOM_HEIGHT + this->margins.bottom()
);
this->setSceneRect(
0,
0,
width,
std::max(sceneHeight, this->parent->height())
);
}
this->update();
}
void ByteItemGraphicsScene::setEnabled(bool enabled) {
if (this->enabled != enabled) {
this->enabled = enabled;
for (auto& [byteAddress, byteItem] : this->byteItemsByAddress) {
byteItem->setEnabled(this->enabled);
void ByteItemGraphicsScene::invalidateChildItemCaches() {
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
byteWidget->update();
}
for (auto* annotationItem : this->annotationItems) {
annotationItem->setEnabled(this->enabled);
annotationItem->update();
}
}
QPointF ByteItemGraphicsScene::getByteItemPositionByAddress(std::uint32_t address) {
if (this->byteItemsByAddress.contains(address)) {
return this->byteItemsByAddress.at(address)->pos();
}
this->byteAddressContainer->setEnabled(enabled);
this->byteAddressContainer->update();
this->update();
}
}
void ByteItemGraphicsScene::invalidateChildItemCaches() {
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
byteWidget->update();
return QPointF();
}
for (auto* annotationItem : this->annotationItems) {
annotationItem->update();
}
}
QPointF ByteItemGraphicsScene::getByteItemPositionByAddress(std::uint32_t address) {
if (this->byteItemsByAddress.contains(address)) {
return this->byteItemsByAddress.at(address)->pos();
}
return QPointF();
}
bool ByteItemGraphicsScene::event(QEvent* event) {
if (event->type() == QEvent::Type::GraphicsSceneLeave && this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
return QGraphicsScene::event(event);
}
void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
auto hoveredItems = this->items(mouseEvent->scenePos());
ByteItem* hoveredByteItem = nullptr;
AnnotationItem* hoveredAnnotationItem = nullptr;
if (!hoveredItems.empty()) {
hoveredByteItem = dynamic_cast<ByteItem*>(hoveredItems.at(0));
hoveredAnnotationItem = dynamic_cast<AnnotationItem*>(hoveredItems.at(0));
}
if (hoveredByteItem != nullptr) {
this->onByteWidgetEnter(hoveredByteItem);
return;
}
if (this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
if (hoveredAnnotationItem != nullptr) {
this->onAnnotationItemEnter(hoveredAnnotationItem);
return;
}
if (this->hoveredAnnotationItem.has_value()) {
this->onAnnotationItemLeave();
}
}
void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) {
const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress;
for (auto* valueAnnotationItem : this->valueAnnotationItems) {
if (valueAnnotationItem->size > buffer.size()) {
continue;
bool ByteItemGraphicsScene::event(QEvent* event) {
if (event->type() == QEvent::Type::GraphicsSceneLeave && this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
const auto relativeStartAddress = valueAnnotationItem->startAddress - memoryStartAddress;
const auto relativeEndAddress = valueAnnotationItem->endAddress - memoryStartAddress;
valueAnnotationItem->setValue(Targets::TargetMemoryBuffer(
buffer.begin() + relativeStartAddress,
buffer.begin() + relativeEndAddress + 1
));
return QGraphicsScene::event(event);
}
}
void ByteItemGraphicsScene::adjustByteItemPositions() {
const auto columnCount = static_cast<std::size_t>(
std::floor(
(this->getSceneWidth() - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH + ByteItem::RIGHT_MARGIN)
/ (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
)
);
void ByteItemGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent* mouseEvent) {
auto hoveredItems = this->items(mouseEvent->scenePos());
ByteItem* hoveredByteItem = nullptr;
AnnotationItem* hoveredAnnotationItem = nullptr;
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByRowIndex;
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByColumnIndex;
if (!hoveredItems.empty()) {
hoveredByteItem = dynamic_cast<ByteItem*>(hoveredItems.at(0));
hoveredAnnotationItem = dynamic_cast<AnnotationItem*>(hoveredItems.at(0));
}
auto rowIndicesWithTopAnnotations = std::set<std::size_t>();
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
if (hoveredByteItem != nullptr) {
this->onByteWidgetEnter(hoveredByteItem);
return;
}
for (auto* annotationItem : this->annotationItems) {
const auto firstByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((annotationItem->startAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(columnCount)) - 1
if (this->hoveredByteWidget.has_value()) {
this->onByteWidgetLeave();
}
if (hoveredAnnotationItem != nullptr) {
this->onAnnotationItemEnter(hoveredAnnotationItem);
return;
}
if (this->hoveredAnnotationItem.has_value()) {
this->onAnnotationItemLeave();
}
}
void ByteItemGraphicsScene::updateAnnotationValues(const Targets::TargetMemoryBuffer& buffer) {
const auto memoryStartAddress = this->targetMemoryDescriptor.addressRange.startAddress;
for (auto* valueAnnotationItem : this->valueAnnotationItems) {
if (valueAnnotationItem->size > buffer.size()) {
continue;
}
const auto relativeStartAddress = valueAnnotationItem->startAddress - memoryStartAddress;
const auto relativeEndAddress = valueAnnotationItem->endAddress - memoryStartAddress;
valueAnnotationItem->setValue(Targets::TargetMemoryBuffer(
buffer.begin() + relativeStartAddress,
buffer.begin() + relativeEndAddress + 1
));
}
}
void ByteItemGraphicsScene::adjustByteItemPositions() {
const auto columnCount = static_cast<std::size_t>(
std::floor(
(this->getSceneWidth() - this->margins.left() - this->margins.right() - ByteAddressContainer::WIDTH
+ ByteItem::RIGHT_MARGIN) / (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN)
)
);
const auto lastByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((annotationItem->endAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(columnCount)) - 1
);
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByRowIndex;
std::map<std::size_t, std::vector<ByteItem*>> byteWidgetsByColumnIndex;
// We only display annotations that span a single row.
if (this->settings.displayAnnotations && firstByteRowIndex == lastByteRowIndex) {
annotationItem->show();
auto rowIndicesWithTopAnnotations = std::set<std::size_t>();
auto rowIndicesWithBottomAnnotations = std::set<std::size_t>();
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
for (auto* annotationItem : this->annotationItems) {
const auto firstByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((annotationItem->startAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(columnCount)) - 1
);
const auto lastByteRowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>((annotationItem->endAddress - memoryAddressRange.startAddress) + 1)
/ static_cast<double>(columnCount)) - 1
);
// We only display annotations that span a single row.
if (this->settings.displayAnnotations && firstByteRowIndex == lastByteRowIndex) {
annotationItem->show();
if (annotationItem->position == AnnotationItemPosition::TOP) {
rowIndicesWithTopAnnotations.insert(firstByteRowIndex);
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
rowIndicesWithBottomAnnotations.insert(firstByteRowIndex);
}
} else {
annotationItem->hide();
}
}
constexpr auto annotationTopHeight = AnnotationItem::TOP_HEIGHT;
constexpr auto annotationBottomHeight = AnnotationItem::BOTTOM_HEIGHT;
std::size_t lastRowIndex = 0;
int rowYPosition = margins.top();
auto currentRowAnnotatedTop = false;
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
const auto rowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>(byteWidget->byteIndex + 1) / static_cast<double>(columnCount)) - 1
);
const auto columnIndex = static_cast<std::size_t>(
static_cast<double>(byteWidget->byteIndex)
- (std::floor(byteWidget->byteIndex / columnCount) * static_cast<double>(columnCount))
);
if (rowIndex != lastRowIndex) {
rowYPosition += ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN;
currentRowAnnotatedTop = false;
if (rowIndicesWithBottomAnnotations.contains(lastRowIndex)) {
rowYPosition += annotationBottomHeight;
}
}
if (!currentRowAnnotatedTop && rowIndicesWithTopAnnotations.contains(rowIndex)) {
rowYPosition += annotationTopHeight;
currentRowAnnotatedTop = true;
}
byteWidget->setPos(
static_cast<int>(
columnIndex * (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + this->margins.left()
+ ByteAddressContainer::WIDTH
),
rowYPosition
);
byteWidget->currentRowIndex = rowIndex;
byteWidget->currentColumnIndex = columnIndex;
byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget);
byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget);
lastRowIndex = rowIndex;
}
this->byteItemsByRowIndex = std::move(byteWidgetsByRowIndex);
this->byteItemsByColumnIndex = std::move(byteWidgetsByColumnIndex);
this->byteAddressContainer->adjustAddressLabels(this->byteItemsByRowIndex);
}
void ByteItemGraphicsScene::adjustAnnotationItemPositions() {
if (this->byteItemsByAddress.empty() || !this->settings.displayAnnotations) {
return;
}
for (auto* annotationItem : this->annotationItems) {
if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) {
annotationItem->hide();
continue;
}
const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos();
if (annotationItem->position == AnnotationItemPosition::TOP) {
rowIndicesWithTopAnnotations.insert(firstByteRowIndex);
annotationItem->setPos(
firstByteItemPosition.x(),
firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1
);
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
rowIndicesWithBottomAnnotations.insert(firstByteRowIndex);
annotationItem->setPos(
firstByteItemPosition.x(),
firstByteItemPosition.y() + ByteItem::HEIGHT
);
}
}
}
void ByteItemGraphicsScene::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) {
if (this->hoveredByteWidget.has_value()) {
if (this->hoveredByteWidget.value() == widget) {
// This byte item is already marked as hovered
return;
}
this->onByteWidgetLeave();
}
this->hoveredByteWidget = widget;
this->hoveredAddressLabel->setText(
"Relative Address (Absolute Address): " + widget->relativeAddressHex + " (" + widget->addressHex + ")"
);
if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) {
for (auto& byteWidget : this->byteItemsByColumnIndex.at(widget->currentColumnIndex)) {
byteWidget->update();
}
for (auto& byteWidget : this->byteItemsByRowIndex.at(widget->currentRowIndex)) {
byteWidget->update();
}
} else {
annotationItem->hide();
widget->update();
}
}
constexpr auto annotationTopHeight = AnnotationItem::TOP_HEIGHT;
constexpr auto annotationBottomHeight = AnnotationItem::BOTTOM_HEIGHT;
void ByteItemGraphicsScene::onByteWidgetLeave() {
auto* byteItem = this->hoveredByteWidget.value();
this->hoveredByteWidget = std::nullopt;
std::size_t lastRowIndex = 0;
int rowYPosition = margins.top();
auto currentRowAnnotatedTop = false;
this->hoveredAddressLabel->setText("Relative Address (Absolute Address):");
for (auto& [address, byteWidget] : this->byteItemsByAddress) {
const auto rowIndex = static_cast<std::size_t>(
std::ceil(static_cast<double>(byteWidget->byteIndex + 1) / static_cast<double>(columnCount)) - 1
);
const auto columnIndex = static_cast<std::size_t>(
static_cast<double>(byteWidget->byteIndex)
- (std::floor(byteWidget->byteIndex / columnCount) * static_cast<double>(columnCount))
);
if (rowIndex != lastRowIndex) {
rowYPosition += ByteItem::HEIGHT + ByteItem::BOTTOM_MARGIN;
currentRowAnnotatedTop = false;
if (rowIndicesWithBottomAnnotations.contains(lastRowIndex)) {
rowYPosition += annotationBottomHeight;
if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) {
for (auto& byteWidget : this->byteItemsByColumnIndex.at(byteItem->currentColumnIndex)) {
byteWidget->update();
}
for (auto& byteWidget : this->byteItemsByRowIndex.at(byteItem->currentRowIndex)) {
byteWidget->update();
}
} else {
byteItem->update();
}
if (!currentRowAnnotatedTop && rowIndicesWithTopAnnotations.contains(rowIndex)) {
rowYPosition += annotationTopHeight;
currentRowAnnotatedTop = true;
}
byteWidget->setPos(
static_cast<int>(
columnIndex * (ByteItem::WIDTH + ByteItem::RIGHT_MARGIN) + this->margins.left() + ByteAddressContainer::WIDTH),
rowYPosition
);
byteWidget->currentRowIndex = rowIndex;
byteWidget->currentColumnIndex = columnIndex;
byteWidgetsByRowIndex[byteWidget->currentRowIndex].emplace_back(byteWidget);
byteWidgetsByColumnIndex[byteWidget->currentColumnIndex].emplace_back(byteWidget);
lastRowIndex = rowIndex;
}
this->byteItemsByRowIndex = std::move(byteWidgetsByRowIndex);
this->byteItemsByColumnIndex = std::move(byteWidgetsByColumnIndex);
void ByteItemGraphicsScene::onAnnotationItemEnter(AnnotationItem* annotationItem) {
if (this->hoveredAnnotationItem.has_value()) {
if (this->hoveredAnnotationItem.value() == annotationItem) {
return;
}
this->byteAddressContainer->adjustAddressLabels(this->byteItemsByRowIndex);
}
void ByteItemGraphicsScene::adjustAnnotationItemPositions() {
if (this->byteItemsByAddress.empty() || !this->settings.displayAnnotations) {
return;
}
for (auto* annotationItem : this->annotationItems) {
if (!this->byteItemsByAddress.contains(annotationItem->startAddress)) {
annotationItem->hide();
continue;
this->onAnnotationItemLeave();
}
const auto firstByteItemPosition = this->byteItemsByAddress.at(annotationItem->startAddress)->pos();
this->hoveredAnnotationItem = annotationItem;
if (annotationItem->position == AnnotationItemPosition::TOP) {
annotationItem->setPos(
firstByteItemPosition.x(),
firstByteItemPosition.y() - AnnotationItem::TOP_HEIGHT - 1
);
for (
auto byteItemAddress = annotationItem->startAddress;
byteItemAddress <= annotationItem->endAddress;
byteItemAddress++
) {
this->byteItemsByAddress.at(byteItemAddress)->update();
}
}
} else if (annotationItem->position == AnnotationItemPosition::BOTTOM) {
annotationItem->setPos(
firstByteItemPosition.x(),
firstByteItemPosition.y() + ByteItem::HEIGHT
);
void ByteItemGraphicsScene::onAnnotationItemLeave() {
auto* annotationItem = this->hoveredAnnotationItem.value();
this->hoveredAnnotationItem = std::nullopt;
for (
auto byteItemAddress = annotationItem->startAddress;
byteItemAddress <= annotationItem->endAddress;
byteItemAddress++
) {
this->byteItemsByAddress.at(byteItemAddress)->update();
}
}
}
void ByteItemGraphicsScene::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void ByteItemGraphicsScene::onByteWidgetEnter(ByteItem* widget) {
if (this->hoveredByteWidget.has_value()) {
if (this->hoveredByteWidget.value() == widget) {
// This byte item is already marked as hovered
return;
}
this->onByteWidgetLeave();
}
this->hoveredByteWidget = widget;
this->hoveredAddressLabel->setText(
"Relative Address (Absolute Address): " + widget->relativeAddressHex + " (" + widget->addressHex + ")"
);
if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) {
for (auto& byteWidget : this->byteItemsByColumnIndex.at(widget->currentColumnIndex)) {
byteWidget->update();
}
for (auto& byteWidget : this->byteItemsByRowIndex.at(widget->currentRowIndex)) {
byteWidget->update();
}
} else {
widget->update();
}
}
void ByteItemGraphicsScene::onByteWidgetLeave() {
auto* byteItem = this->hoveredByteWidget.value();
this->hoveredByteWidget = std::nullopt;
this->hoveredAddressLabel->setText("Relative Address (Absolute Address):");
if (this->settings.highlightHoveredRowAndCol && !this->byteItemsByRowIndex.empty()) {
for (auto& byteWidget : this->byteItemsByColumnIndex.at(byteItem->currentColumnIndex)) {
byteWidget->update();
}
for (auto& byteWidget : this->byteItemsByRowIndex.at(byteItem->currentRowIndex)) {
byteWidget->update();
}
} else {
byteItem->update();
}
}
void ByteItemGraphicsScene::onAnnotationItemEnter(AnnotationItem* annotationItem) {
if (this->hoveredAnnotationItem.has_value()) {
if (this->hoveredAnnotationItem.value() == annotationItem) {
return;
}
this->onAnnotationItemLeave();
}
this->hoveredAnnotationItem = annotationItem;
for (
auto byteItemAddress = annotationItem->startAddress;
byteItemAddress <= annotationItem->endAddress;
byteItemAddress++
) {
this->byteItemsByAddress.at(byteItemAddress)->update();
}
}
void ByteItemGraphicsScene::onAnnotationItemLeave() {
auto* annotationItem = this->hoveredAnnotationItem.value();
this->hoveredAnnotationItem = std::nullopt;
for (
auto byteItemAddress = annotationItem->startAddress;
byteItemAddress <= annotationItem->endAddress;
byteItemAddress++
) {
this->byteItemsByAddress.at(byteItemAddress)->update();
}
}

View File

@@ -5,240 +5,244 @@
#include "src/Helpers/Paths.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetMemoryDescriptor;
HexViewerWidget::HexViewerWidget(
const TargetMemoryDescriptor& targetMemoryDescriptor,
HexViewerWidgetSettings& settings,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
QWidget* parent
):
QWidget(parent),
targetMemoryDescriptor(targetMemoryDescriptor),
settings(settings),
focusedMemoryRegions(focusedMemoryRegions),
excludedMemoryRegions(excludedMemoryRegions),
insightWorker(insightWorker)
namespace Bloom::Widgets
{
this->setObjectName("hex-viewer-widget");
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
using namespace Bloom::Exceptions;
auto widgetUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget"
+ "/UiFiles/HexViewerWidget.ui"
)
);
using Bloom::Targets::TargetMemoryDescriptor;
if (!widgetUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open HexViewerWidget UI file");
}
HexViewerWidget::HexViewerWidget(
const TargetMemoryDescriptor& targetMemoryDescriptor,
HexViewerWidgetSettings& settings,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
InsightWorker& insightWorker,
QWidget* parent
)
: QWidget(parent)
, targetMemoryDescriptor(targetMemoryDescriptor)
, settings(settings)
, focusedMemoryRegions(focusedMemoryRegions)
, excludedMemoryRegions(excludedMemoryRegions)
, insightWorker(insightWorker)
{
this->setObjectName("hex-viewer-widget");
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&widgetUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(0, 0, 0, 0);
auto widgetUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane/HexViewerWidget"
+ "/UiFiles/HexViewerWidget.ui"
)
);
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
if (!widgetUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open HexViewerWidget UI file");
}
this->refreshButton = this->toolBar->findChild<SvgToolButton*>("refresh-memory-btn");
this->highlightStackMemoryButton = this->toolBar->findChild<SvgToolButton*>("highlight-stack-memory-btn");
this->highlightHoveredRowAndColumnButton = this->toolBar->findChild<SvgToolButton*>(
"highlight-hovered-rows-columns-btn"
);
this->highlightFocusedMemoryButton = this->container->findChild<SvgToolButton*>(
"highlight-focused-memory-btn"
);
this->displayAnnotationsButton = this->container->findChild<SvgToolButton*>("display-annotations-btn");
this->displayAsciiButton = this->container->findChild<SvgToolButton*>("display-ascii-btn");
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&widgetUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(0, 0, 0, 0);
this->goToAddressInput = this->container->findChild<TextInput*>("go-to-address-input");
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
this->bottomBar = this->container->findChild<QWidget*>("bottom-bar");
this->toolBar->setContentsMargins(0, 0, 0, 0);
this->toolBar->layout()->setContentsMargins(5, 0, 5, 1);
this->refreshButton = this->toolBar->findChild<SvgToolButton*>("refresh-memory-btn");
this->highlightStackMemoryButton = this->toolBar->findChild<SvgToolButton*>(
"highlight-stack-memory-btn"
);
this->highlightHoveredRowAndColumnButton = this->toolBar->findChild<SvgToolButton*>(
"highlight-hovered-rows-columns-btn"
);
this->highlightFocusedMemoryButton = this->container->findChild<SvgToolButton*>(
"highlight-focused-memory-btn"
);
this->displayAnnotationsButton = this->container->findChild<SvgToolButton*>("display-annotations-btn");
this->displayAsciiButton = this->container->findChild<SvgToolButton*>("display-ascii-btn");
this->bottomBar->setContentsMargins(0, 0, 0, 0);
this->bottomBar->layout()->setContentsMargins(5, 0, 5, 0);
this->goToAddressInput = this->container->findChild<TextInput*>("go-to-address-input");
this->hoveredAddressLabel = this->bottomBar->findChild<QLabel*>("byte-address-label");
this->toolBar->setContentsMargins(0, 0, 0, 0);
this->toolBar->layout()->setContentsMargins(5, 0, 5, 1);
this->byteItemGraphicsViewContainer = this->container->findChild<QWidget*>("graphics-view-container");
this->byteItemGraphicsView = new ByteItemContainerGraphicsView(
this->targetMemoryDescriptor,
this->focusedMemoryRegions,
this->excludedMemoryRegions,
this->insightWorker,
this->settings,
this->hoveredAddressLabel,
this->byteItemGraphicsViewContainer
);
this->byteItemGraphicsScene = this->byteItemGraphicsView->getScene();
this->bottomBar->setContentsMargins(0, 0, 0, 0);
this->bottomBar->layout()->setContentsMargins(5, 0, 5, 0);
this->setHoveredRowAndColumnHighlightingEnabled(this->settings.highlightHoveredRowAndCol);
this->setFocusedMemoryHighlightingEnabled(this->settings.highlightFocusedMemory);
this->setAnnotationsEnabled(this->settings.displayAnnotations);
this->setDisplayAsciiEnabled(this->settings.displayAsciiValues);
this->hoveredAddressLabel = this->bottomBar->findChild<QLabel*>("byte-address-label");
if (this->targetMemoryDescriptor.type == Targets::TargetMemoryType::RAM) {
this->highlightStackMemoryButton->show();
this->setStackMemoryHighlightingEnabled(this->settings.highlightStackMemory);
this->byteItemGraphicsViewContainer = this->container->findChild<QWidget*>("graphics-view-container");
this->byteItemGraphicsView = new ByteItemContainerGraphicsView(
this->targetMemoryDescriptor,
this->focusedMemoryRegions,
this->excludedMemoryRegions,
this->insightWorker,
this->settings,
this->hoveredAddressLabel,
this->byteItemGraphicsViewContainer
);
this->byteItemGraphicsScene = this->byteItemGraphicsView->getScene();
this->setHoveredRowAndColumnHighlightingEnabled(this->settings.highlightHoveredRowAndCol);
this->setFocusedMemoryHighlightingEnabled(this->settings.highlightFocusedMemory);
this->setAnnotationsEnabled(this->settings.displayAnnotations);
this->setDisplayAsciiEnabled(this->settings.displayAsciiValues);
if (this->targetMemoryDescriptor.type == Targets::TargetMemoryType::RAM) {
this->highlightStackMemoryButton->show();
this->setStackMemoryHighlightingEnabled(this->settings.highlightStackMemory);
QObject::connect(
this->highlightStackMemoryButton,
&QToolButton::clicked,
this,
[this] {
this->setStackMemoryHighlightingEnabled(!this->settings.highlightStackMemory);
}
);
} else {
this->highlightStackMemoryButton->hide();
this->setStackMemoryHighlightingEnabled(false);
}
QObject::connect(
this->highlightStackMemoryButton,
this->highlightHoveredRowAndColumnButton,
&QToolButton::clicked,
this,
[this] {
this->setStackMemoryHighlightingEnabled(!this->settings.highlightStackMemory);
this->setHoveredRowAndColumnHighlightingEnabled(!this->settings.highlightHoveredRowAndCol);
}
);
} else {
this->highlightStackMemoryButton->hide();
this->setStackMemoryHighlightingEnabled(false);
QObject::connect(
this->highlightFocusedMemoryButton,
&QToolButton::clicked,
this,
[this] {
this->setFocusedMemoryHighlightingEnabled(!this->settings.highlightFocusedMemory);
}
);
QObject::connect(
this->displayAnnotationsButton,
&QToolButton::clicked,
this,
[this] {
this->setAnnotationsEnabled(!this->settings.displayAnnotations);
}
);
QObject::connect(
this->displayAsciiButton,
&QToolButton::clicked,
this,
[this] {
this->setDisplayAsciiEnabled(!this->settings.displayAsciiValues);
}
);
QObject::connect(
this->goToAddressInput,
&QLineEdit::textEdited,
this,
&HexViewerWidget::onGoToAddressInputChanged
);
QObject::connect(
this->goToAddressInput,
&TextInput::focusChanged,
this,
&HexViewerWidget::onGoToAddressInputChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&HexViewerWidget::onTargetStateChanged
);
this->show();
}
QObject::connect(
this->highlightHoveredRowAndColumnButton,
&QToolButton::clicked,
this,
[this] {
this->setHoveredRowAndColumnHighlightingEnabled(!this->settings.highlightHoveredRowAndCol);
}
);
QObject::connect(
this->highlightFocusedMemoryButton,
&QToolButton::clicked,
this,
[this] {
this->setFocusedMemoryHighlightingEnabled(!this->settings.highlightFocusedMemory);
}
);
QObject::connect(
this->displayAnnotationsButton,
&QToolButton::clicked,
this,
[this] {
this->setAnnotationsEnabled(!this->settings.displayAnnotations);
}
);
QObject::connect(
this->displayAsciiButton,
&QToolButton::clicked,
this,
[this] {
this->setDisplayAsciiEnabled(!this->settings.displayAsciiValues);
}
);
QObject::connect(
this->goToAddressInput,
&QLineEdit::textEdited,
this,
&HexViewerWidget::onGoToAddressInputChanged
);
QObject::connect(
this->goToAddressInput,
&TextInput::focusChanged,
this,
&HexViewerWidget::onGoToAddressInputChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&HexViewerWidget::onTargetStateChanged
);
this->show();
}
void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) {
this->byteItemGraphicsScene->updateValues(buffer);
}
void HexViewerWidget::refreshRegions() {
this->byteItemGraphicsScene->refreshRegions();
}
void HexViewerWidget::setStackPointer(std::uint32_t stackPointer) {
this->byteItemGraphicsScene->updateStackPointer(stackPointer);
}
void HexViewerWidget::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(
this->width(),
this->height()
);
this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size());
}
void HexViewerWidget::showEvent(QShowEvent* event) {
this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size());
}
void HexViewerWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void HexViewerWidget::setStackMemoryHighlightingEnabled(bool enabled) {
this->highlightStackMemoryButton->setChecked(enabled);
this->settings.highlightStackMemory = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setHoveredRowAndColumnHighlightingEnabled(bool enabled) {
this->highlightHoveredRowAndColumnButton->setChecked(enabled);
this->settings.highlightHoveredRowAndCol = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setFocusedMemoryHighlightingEnabled(bool enabled) {
this->highlightFocusedMemoryButton->setChecked(enabled);
this->settings.highlightFocusedMemory = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setAnnotationsEnabled(bool enabled) {
this->displayAnnotationsButton->setChecked(enabled);
this->settings.displayAnnotations = enabled;
this->byteItemGraphicsScene->adjustSize(true);
}
void HexViewerWidget::setDisplayAsciiEnabled(bool enabled) {
this->displayAsciiButton->setChecked(enabled);
this->settings.displayAsciiValues = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::onGoToAddressInputChanged() {
auto addressConversionOk = false;
const auto address = this->goToAddressInput->text().toUInt(&addressConversionOk, 16);
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
if (addressConversionOk && memoryAddressRange.contains(address) && this->goToAddressInput->hasFocus()) {
this->byteItemGraphicsScene->setHighlightedAddresses({address});
this->byteItemGraphicsView->scrollToByteItemAtAddress(address);
return;
void HexViewerWidget::updateValues(const Targets::TargetMemoryBuffer& buffer) {
this->byteItemGraphicsScene->updateValues(buffer);
}
this->byteItemGraphicsScene->setHighlightedAddresses({});
void HexViewerWidget::refreshRegions() {
this->byteItemGraphicsScene->refreshRegions();
}
void HexViewerWidget::setStackPointer(std::uint32_t stackPointer) {
this->byteItemGraphicsScene->updateStackPointer(stackPointer);
}
void HexViewerWidget::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(
this->width(),
this->height()
);
this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size());
}
void HexViewerWidget::showEvent(QShowEvent* event) {
this->byteItemGraphicsView->setFixedSize(this->byteItemGraphicsViewContainer->size());
}
void HexViewerWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void HexViewerWidget::setStackMemoryHighlightingEnabled(bool enabled) {
this->highlightStackMemoryButton->setChecked(enabled);
this->settings.highlightStackMemory = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setHoveredRowAndColumnHighlightingEnabled(bool enabled) {
this->highlightHoveredRowAndColumnButton->setChecked(enabled);
this->settings.highlightHoveredRowAndCol = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setFocusedMemoryHighlightingEnabled(bool enabled) {
this->highlightFocusedMemoryButton->setChecked(enabled);
this->settings.highlightFocusedMemory = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::setAnnotationsEnabled(bool enabled) {
this->displayAnnotationsButton->setChecked(enabled);
this->settings.displayAnnotations = enabled;
this->byteItemGraphicsScene->adjustSize(true);
}
void HexViewerWidget::setDisplayAsciiEnabled(bool enabled) {
this->displayAsciiButton->setChecked(enabled);
this->settings.displayAsciiValues = enabled;
this->byteItemGraphicsScene->invalidateChildItemCaches();
}
void HexViewerWidget::onGoToAddressInputChanged() {
auto addressConversionOk = false;
const auto address = this->goToAddressInput->text().toUInt(&addressConversionOk, 16);
const auto& memoryAddressRange = this->targetMemoryDescriptor.addressRange;
if (addressConversionOk && memoryAddressRange.contains(address) && this->goToAddressInput->hasFocus()) {
this->byteItemGraphicsScene->setHighlightedAddresses({address});
this->byteItemGraphicsView->scrollToByteItemAtAddress(address);
return;
}
this->byteItemGraphicsScene->setHighlightedAddresses({});
}
}

View File

@@ -2,68 +2,46 @@
#include <algorithm>
using namespace Bloom::Widgets;
ValueAnnotationItem::ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion)
: AnnotationItem(focusedMemoryRegion, AnnotationItemPosition::TOP)
, focusedMemoryRegion(focusedMemoryRegion)
, endianness(focusedMemoryRegion.endianness)
namespace Bloom::Widgets
{
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
}
void ValueAnnotationItem::setValue(const Targets::TargetMemoryBuffer& value) {
this->value = value;
if (this->endianness == Targets::TargetMemoryEndianness::LITTLE) {
std::reverse(this->value.begin(), this->value.end());
}
this->refreshLabelText();
this->setToolTip(this->labelText);
}
void ValueAnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto font = painter->font();
font.setItalic(true);
painter->setFont(font);
AnnotationItem::paint(painter, option, widget);
}
void ValueAnnotationItem::refreshLabelText() {
this->update();
if (this->value.empty()) {
ValueAnnotationItem::ValueAnnotationItem(const FocusedMemoryRegion& focusedMemoryRegion)
: AnnotationItem(focusedMemoryRegion, AnnotationItemPosition::TOP)
, focusedMemoryRegion(focusedMemoryRegion)
, endianness(focusedMemoryRegion.endianness)
{
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
return;
}
switch (this->focusedMemoryRegion.dataType) {
case MemoryRegionDataType::UNSIGNED_INTEGER: {
std::uint64_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
}
void ValueAnnotationItem::setValue(const Targets::TargetMemoryBuffer& value) {
this->value = value;
this->labelText = QString::number(integerValue);
break;
if (this->endianness == Targets::TargetMemoryEndianness::LITTLE) {
std::reverse(this->value.begin(), this->value.end());
}
case MemoryRegionDataType::SIGNED_INTEGER: {
const auto valueSize = this->value.size();
if (valueSize == 1) {
this->labelText = QString::number(static_cast<int8_t>(this->value[0]));
break;
}
this->refreshLabelText();
this->setToolTip(this->labelText);
}
if (valueSize == 2) {
this->labelText = QString::number(static_cast<int16_t>((this->value[0] << 8) | this->value[1]));
break;
}
void ValueAnnotationItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
auto font = painter->font();
font.setItalic(true);
painter->setFont(font);
if (valueSize <= 4) {
std::int32_t integerValue = 0;
AnnotationItem::paint(painter, option, widget);
}
void ValueAnnotationItem::refreshLabelText() {
this->update();
if (this->value.empty()) {
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
return;
}
switch (this->focusedMemoryRegion.dataType) {
case MemoryRegionDataType::UNSIGNED_INTEGER: {
std::uint64_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
}
@@ -71,42 +49,65 @@ void ValueAnnotationItem::refreshLabelText() {
this->labelText = QString::number(integerValue);
break;
}
case MemoryRegionDataType::SIGNED_INTEGER: {
const auto valueSize = this->value.size();
std::int64_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
if (valueSize == 1) {
this->labelText = QString::number(static_cast<int8_t>(this->value[0]));
break;
}
if (valueSize == 2) {
this->labelText = QString::number(static_cast<int16_t>((this->value[0] << 8) | this->value[1]));
break;
}
if (valueSize <= 4) {
std::int32_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
}
this->labelText = QString::number(integerValue);
break;
}
std::int64_t integerValue = 0;
for (const auto& byte : this->value) {
integerValue = (integerValue << 8) | byte;
}
this->labelText = QString::number(integerValue);
break;
}
case MemoryRegionDataType::ASCII_STRING: {
// Replace non-ASCII chars with '?'
auto asciiData = this->value;
this->labelText = QString::number(integerValue);
break;
}
case MemoryRegionDataType::ASCII_STRING: {
// Replace non-ASCII chars with '?'
auto asciiData = this->value;
std::replace_if(
asciiData.begin(),
asciiData.end(),
[] (unsigned char value) {
/*
* We only care about non-control characters (with the exception of the white space character) in
* the standard ASCII range.
*/
constexpr auto asciiRangeStart = 32;
constexpr auto asciiRangeEnd = 126;
return value < asciiRangeStart || value > asciiRangeEnd;
},
'?'
);
std::replace_if(
asciiData.begin(),
asciiData.end(),
[] (unsigned char value) {
/*
* We only care about non-control characters (with the exception of the white space character) in
* the standard ASCII range.
*/
constexpr auto asciiRangeStart = 32;
constexpr auto asciiRangeEnd = 126;
return value < asciiRangeStart || value > asciiRangeEnd;
},
'?'
);
this->labelText = "'" + QString::fromLatin1(
reinterpret_cast<const char*>(asciiData.data()),
static_cast<qsizetype>(asciiData.size())
) + "'";
break;
}
default: {
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
this->labelText = "'" + QString::fromLatin1(
reinterpret_cast<const char*>(asciiData.data()),
static_cast<qsizetype>(asciiData.size())
) + "'";
break;
}
default: {
this->labelText = QString(ValueAnnotationItem::DEFAULT_LABEL_TEXT);
}
}
}
}

View File

@@ -6,41 +6,42 @@
#include "src/Exceptions/Exception.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
using namespace Bloom;
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
ExcludedRegionItem::ExcludedRegionItem(
const ExcludedMemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) {
auto formUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui"
)
);
using Targets::TargetMemoryAddressRange;
if (!formUiFile.open(QFile::ReadOnly)) {
throw Bloom::Exceptions::Exception("Failed to open excluded region item form UI file");
}
ExcludedRegionItem::ExcludedRegionItem(
const ExcludedMemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) {
auto formUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/ExcludedMemoryRegionForm.ui"
)
);
auto uiLoader = UiLoader(this);
this->formWidget = uiLoader.load(&formUiFile, this);
if (!formUiFile.open(QFile::ReadOnly)) {
throw Bloom::Exceptions::Exception("Failed to open excluded region item form UI file");
this->initFormInputs();
}
auto uiLoader = UiLoader(this);
this->formWidget = uiLoader.load(&formUiFile, this);
void ExcludedRegionItem::applyChanges() {
using Targets::TargetMemoryAddressRange;
this->initFormInputs();
}
void ExcludedRegionItem::applyChanges() {
this->memoryRegion.name = this->nameInput->text();
const auto inputAddressRange = TargetMemoryAddressRange(
this->startAddressInput->text().toUInt(nullptr, 16),
this->endAddressInput->text().toUInt(nullptr, 16)
);
this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType();
this->memoryRegion.addressRange = this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange;
this->memoryRegion.name = this->nameInput->text();
const auto inputAddressRange = TargetMemoryAddressRange(
this->startAddressInput->text().toUInt(nullptr, 16),
this->endAddressInput->text().toUInt(nullptr, 16)
);
this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType();
this->memoryRegion.addressRange =
this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange;
}
}

View File

@@ -6,99 +6,105 @@
#include "src/Exceptions/Exception.hpp"
#include "src/Insight/UserInterfaces/InsightWindow/UiLoader.hpp"
using namespace Bloom;
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
using Targets::TargetMemoryAddressRange;
using Targets::TargetMemoryAddressRange;
FocusedRegionItem::FocusedRegionItem(
const FocusedMemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) {
auto formUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui"
)
);
FocusedRegionItem::FocusedRegionItem(
const FocusedMemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryRegion(region), RegionItem(region, memoryDescriptor, parent) {
auto formUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/FocusedMemoryRegionForm.ui"
)
);
if (!formUiFile.open(QFile::ReadOnly)) {
throw Bloom::Exceptions::Exception("Failed to open focused region item form UI file");
}
auto uiLoader = UiLoader(this);
this->formWidget = uiLoader.load(&formUiFile, this);
this->initFormInputs();
}
void FocusedRegionItem::applyChanges() {
this->memoryRegion.name = this->nameInput->text();
const auto inputAddressRange = TargetMemoryAddressRange(
this->startAddressInput->text().toUInt(nullptr, 16),
this->endAddressInput->text().toUInt(nullptr, 16)
);
this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType();
this->memoryRegion.addressRange =
this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange;
auto selectedDataTypeOptionName = this->dataTypeInput->currentData().toString();
if (FocusedRegionItem::dataTypeOptionsByName.contains(selectedDataTypeOptionName)) {
this->memoryRegion.dataType = FocusedRegionItem::dataTypeOptionsByName.at(selectedDataTypeOptionName).dataType;
}
auto selectedEndiannessOptionName = this->endiannessInput->currentData().toString();
if (FocusedRegionItem::endiannessOptionsByName.contains(selectedEndiannessOptionName)) {
this->memoryRegion.endianness = FocusedRegionItem::endiannessOptionsByName.at(
selectedEndiannessOptionName
).endianness;
}
}
void FocusedRegionItem::initFormInputs() {
RegionItem::initFormInputs();
const auto& region = this->memoryRegion;
this->dataTypeInput = this->formWidget->findChild<QComboBox*>("data-type-input");
this->endiannessInput = this->formWidget->findChild<QComboBox*>("endianness-input");
for (const auto& [optionName, option] : FocusedRegionItem::dataTypeOptionsByName) {
this->dataTypeInput->addItem(option.text, optionName);
}
for (const auto& [optionName, option] : FocusedRegionItem::endiannessOptionsByName) {
this->endiannessInput->addItem(option.text, optionName);
}
switch (region.dataType) {
case MemoryRegionDataType::UNSIGNED_INTEGER: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("unsigned_integer").text);
break;
if (!formUiFile.open(QFile::ReadOnly)) {
throw Bloom::Exceptions::Exception("Failed to open focused region item form UI file");
}
case MemoryRegionDataType::SIGNED_INTEGER: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("signed_integer").text);
break;
auto uiLoader = UiLoader(this);
this->formWidget = uiLoader.load(&formUiFile, this);
this->initFormInputs();
}
void FocusedRegionItem::applyChanges() {
this->memoryRegion.name = this->nameInput->text();
const auto inputAddressRange = TargetMemoryAddressRange(
this->startAddressInput->text().toUInt(nullptr, 16),
this->endAddressInput->text().toUInt(nullptr, 16)
);
this->memoryRegion.addressRangeInputType = this->getSelectedAddressInputType();
this->memoryRegion.addressRange =
this->memoryRegion.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(inputAddressRange) : inputAddressRange;
auto selectedDataTypeOptionName = this->dataTypeInput->currentData().toString();
if (FocusedRegionItem::dataTypeOptionsByName.contains(selectedDataTypeOptionName)) {
this->memoryRegion.dataType = FocusedRegionItem::dataTypeOptionsByName.at(
selectedDataTypeOptionName
).dataType;
}
case MemoryRegionDataType::ASCII_STRING: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("ascii").text);
break;
}
default: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("other").text);
auto selectedEndiannessOptionName = this->endiannessInput->currentData().toString();
if (FocusedRegionItem::endiannessOptionsByName.contains(selectedEndiannessOptionName)) {
this->memoryRegion.endianness = FocusedRegionItem::endiannessOptionsByName.at(
selectedEndiannessOptionName
).endianness;
}
}
switch (region.endianness) {
case Targets::TargetMemoryEndianness::LITTLE: {
this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("little").text);
break;
void FocusedRegionItem::initFormInputs() {
RegionItem::initFormInputs();
const auto& region = this->memoryRegion;
this->dataTypeInput = this->formWidget->findChild<QComboBox*>("data-type-input");
this->endiannessInput = this->formWidget->findChild<QComboBox*>("endianness-input");
for (const auto& [optionName, option] : FocusedRegionItem::dataTypeOptionsByName) {
this->dataTypeInput->addItem(option.text, optionName);
}
case Targets::TargetMemoryEndianness::BIG: {
this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("big").text);
break;
for (const auto& [optionName, option] : FocusedRegionItem::endiannessOptionsByName) {
this->endiannessInput->addItem(option.text, optionName);
}
switch (region.dataType) {
case MemoryRegionDataType::UNSIGNED_INTEGER: {
this->dataTypeInput->setCurrentText(
FocusedRegionItem::dataTypeOptionsByName.at("unsigned_integer").text
);
break;
}
case MemoryRegionDataType::SIGNED_INTEGER: {
this->dataTypeInput->setCurrentText(
FocusedRegionItem::dataTypeOptionsByName.at("signed_integer").text
);
break;
}
case MemoryRegionDataType::ASCII_STRING: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("ascii").text);
break;
}
default: {
this->dataTypeInput->setCurrentText(FocusedRegionItem::dataTypeOptionsByName.at("other").text);
}
}
switch (region.endianness) {
case Targets::TargetMemoryEndianness::LITTLE: {
this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("little").text);
break;
}
case Targets::TargetMemoryEndianness::BIG: {
this->endiannessInput->setCurrentText(FocusedRegionItem::endiannessOptionsByName.at("big").text);
break;
}
}
}
}

View File

@@ -10,383 +10,401 @@
#include "src/Helpers/Paths.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom;
using namespace Bloom::Widgets;
using Bloom::Exceptions::Exception;
MemoryRegionManagerWindow::MemoryRegionManagerWindow(
const Targets::TargetMemoryDescriptor& memoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
QWidget* parent
):
QWidget(parent),
memoryDescriptor(memoryDescriptor),
focusedMemoryRegions(focusedMemoryRegions),
excludedMemoryRegions(excludedMemoryRegions)
namespace Bloom::Widgets
{
this->setWindowFlag(Qt::Window);
this->setObjectName("memory-region-manager-window");
this->setWindowTitle(
"Memory Regions - "
+ QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM")
);
using Bloom::Exceptions::Exception;
auto windowUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui"
)
);
auto windowStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open MemoryRegionManagerWindow UI file");
}
if (!windowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open MemoryRegionManagerWindow stylesheet file");
}
this->setStyleSheet(windowStylesheet.readAll());
this->setFixedSize(QSize(970, 540));
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(0, 0, 0, 0);
this->regionSelector = this->container->findChild<QWidget*>("region-selector");
auto* regionSelectorToolBar = this->regionSelector->findChild<QWidget*>("region-selector-tool-bar");
this->addRegionButton = this->regionSelector->findChild<SvgToolButton*>("add-region-btn");
this->removeRegionButton = this->regionSelector->findChild<SvgToolButton*>("remove-region-btn");
this->addFocusedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-focused-region");
this->addExcludedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-excluded-region");
this->regionItemScrollArea = this->regionSelector->findChild<QScrollArea*>("region-item-scroll-area");
this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild<QWidget*>("item-container");
this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild<QVBoxLayout*>(
"item-container-layout"
);
this->stackedFormLayout = this->container->findChild<QStackedLayout*>("stacked-form-layout");
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
this->closeButton = this->container->findChild<QPushButton*>("close-btn");
regionSelectorToolBar->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollArea->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewport->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewportLayout->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewportLayout->setDirection(QBoxLayout::Direction::TopToBottom);
QObject::connect(
this->addFocusedRegionMenuAction,
&QAction::triggered,
this,
&MemoryRegionManagerWindow::onNewFocusedRegionTrigger
);
QObject::connect(
this->addExcludedRegionMenuAction,
&QAction::triggered,
this,
&MemoryRegionManagerWindow::onNewExcludedRegionTrigger
);
QObject::connect(
this->removeRegionButton,
&QToolButton::clicked,
this,
&MemoryRegionManagerWindow::onDeleteRegionTrigger
);
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
QObject::connect(this->applyButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::applyChanges);
QObject::connect(this->helpButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::openHelpPage);
this->refreshRegions();
// Position the inspection window at the center of the main Insight window
this->move(parent->window()->geometry().center() - this->rect().center());
this->show();
}
void MemoryRegionManagerWindow::refreshRegions() {
this->clearRegions();
for (const auto& focusedRegion: this->focusedMemoryRegions) {
this->addFocusedRegion(focusedRegion);
}
for (const auto& excludedRegion: this->excludedMemoryRegions) {
this->addExcludedRegion(excludedRegion);
}
this->sortRegionItems();
}
void MemoryRegionManagerWindow::showEvent(QShowEvent* event) {
if (this->selectedRegion == nullptr && this->regionItemScrollAreaViewportLayout->count() > 0) {
auto* firstRegionItem = qobject_cast<RegionItem*>(
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
MemoryRegionManagerWindow::MemoryRegionManagerWindow(
const Targets::TargetMemoryDescriptor& memoryDescriptor,
std::vector<FocusedMemoryRegion>& focusedMemoryRegions,
std::vector<ExcludedMemoryRegion>& excludedMemoryRegions,
QWidget* parent
)
: QWidget(parent)
, memoryDescriptor(memoryDescriptor)
, focusedMemoryRegions(focusedMemoryRegions)
, excludedMemoryRegions(excludedMemoryRegions)
{
this->setWindowFlag(Qt::Window);
this->setObjectName("memory-region-manager-window");
this->setWindowTitle(
"Memory Regions - "
+ QString(this->memoryDescriptor.type == Targets::TargetMemoryType::EEPROM ? "EEPROM" : "RAM")
);
if (firstRegionItem != nullptr) {
firstRegionItem->setSelected(true);
}
}
}
void MemoryRegionManagerWindow::clearRegions() {
this->selectedRegion = nullptr;
for (auto* focusedRegionItem: this->focusedRegionItems) {
this->regionItemScrollAreaViewportLayout->removeWidget(focusedRegionItem);
focusedRegionItem->getFormWidget()->deleteLater();
focusedRegionItem->deleteLater();
}
for (auto* excludedRegionItem: this->excludedRegionItems) {
this->regionItemScrollAreaViewportLayout->removeWidget(excludedRegionItem);
excludedRegionItem->getFormWidget()->deleteLater();
excludedRegionItem->deleteLater();
}
this->focusedRegionItems.clear();
this->excludedRegionItems.clear();
}
void MemoryRegionManagerWindow::sortRegionItems() {
/*
* This isn't very pretty.
*
* Because the insertion order is persisted in QBoxLayouts, sorting the items requires removing them from the
* layout, and then re-inserting them in the correct order.
*/
auto regionItemCompare = [] (RegionItem* itemA, RegionItem* itemB) {
return itemA->getMemoryRegion().createdDate < itemB->getMemoryRegion().createdDate;
};
auto sortedRegionItems = std::set<RegionItem*, decltype(regionItemCompare)>(regionItemCompare);
QLayoutItem* layoutItem = nullptr;
while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) {
auto* regionItem = qobject_cast<RegionItem*>(layoutItem->widget());
if (regionItem != nullptr) {
sortedRegionItems.insert(regionItem);
}
delete layoutItem;
}
for (auto* regionItem: sortedRegionItems) {
this->regionItemScrollAreaViewportLayout->addWidget(regionItem);
}
}
FocusedRegionItem* MemoryRegionManagerWindow::addFocusedRegion(const FocusedMemoryRegion& region) {
auto* focusedRegionItem = new FocusedRegionItem(region, this->memoryDescriptor, this->regionItemScrollAreaViewport);
this->focusedRegionItems.insert(focusedRegionItem);
this->regionItemScrollAreaViewportLayout->addWidget(focusedRegionItem);
this->stackedFormLayout->addWidget(focusedRegionItem->getFormWidget());
QObject::connect(focusedRegionItem, &RegionItem::selected, this, &MemoryRegionManagerWindow::onRegionSelected);
return focusedRegionItem;
}
ExcludedRegionItem* MemoryRegionManagerWindow::addExcludedRegion(const ExcludedMemoryRegion& region) {
auto* excludedRegionItem = new ExcludedRegionItem(region, this->memoryDescriptor, this->regionItemScrollAreaViewport);
this->excludedRegionItems.insert(excludedRegionItem);
this->regionItemScrollAreaViewportLayout->addWidget(excludedRegionItem);
this->stackedFormLayout->addWidget(excludedRegionItem->getFormWidget());
QObject::connect(excludedRegionItem, &RegionItem::selected, this, &MemoryRegionManagerWindow::onRegionSelected);
return excludedRegionItem;
}
void MemoryRegionManagerWindow::onRegionSelected(RegionItem* selectedRegion) {
if (this->selectedRegion != nullptr && this->selectedRegion != selectedRegion) {
this->selectedRegion->setSelected(false);
}
this->selectedRegion = selectedRegion;
this->stackedFormLayout->setCurrentWidget(this->selectedRegion->getFormWidget());
}
void MemoryRegionManagerWindow::onNewFocusedRegionTrigger() {
using Targets::TargetMemoryAddressRange;
auto* region = this->addFocusedRegion(FocusedMemoryRegion(
"Untitled Region",
TargetMemoryAddressRange(
this->memoryDescriptor.addressRange.startAddress,
this->memoryDescriptor.addressRange.startAddress + 10
)
));
region->setSelected(true);
}
void MemoryRegionManagerWindow::onNewExcludedRegionTrigger() {
using Targets::TargetMemoryAddressRange;
auto* region = this->addExcludedRegion(ExcludedMemoryRegion(
"Untitled Region",
TargetMemoryAddressRange(
this->memoryDescriptor.addressRange.startAddress,
this->memoryDescriptor.addressRange.startAddress + 10
)
));
region->setSelected(true);
}
void MemoryRegionManagerWindow::onDeleteRegionTrigger() {
if (this->selectedRegion == nullptr) {
return;
}
auto* regionItem = this->selectedRegion;
const auto& region = regionItem->getMemoryRegion();
if (region.type == MemoryRegionType::FOCUSED) {
auto* focusedRegionItem = qobject_cast<FocusedRegionItem*>(regionItem);
if (focusedRegionItem != nullptr) {
this->focusedRegionItems.erase(focusedRegionItem);
}
} else {
auto* excludedRegionItem = qobject_cast<ExcludedRegionItem*>(regionItem);
if (excludedRegionItem != nullptr) {
this->excludedRegionItems.erase(excludedRegionItem);
}
}
regionItem->getFormWidget()->deleteLater();
this->regionItemScrollAreaViewportLayout->removeWidget(regionItem);
regionItem->deleteLater();
this->selectedRegion = nullptr;
if (this->regionItemScrollAreaViewportLayout->count() > 0) {
auto* regionItem = qobject_cast<RegionItem*>(
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
auto windowUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/UiFiles/MemoryRegionManagerWindow.ui"
)
);
if (regionItem != nullptr) {
regionItem->setSelected(true);
auto windowStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetMemoryInspectionPane"
+ "/MemoryRegionManager/Stylesheets/MemoryRegionManagerWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open MemoryRegionManagerWindow UI file");
}
if (!windowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open MemoryRegionManagerWindow stylesheet file");
}
this->setStyleSheet(windowStylesheet.readAll());
this->setFixedSize(QSize(970, 540));
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(0, 0, 0, 0);
this->regionSelector = this->container->findChild<QWidget*>("region-selector");
auto* regionSelectorToolBar = this->regionSelector->findChild<QWidget*>("region-selector-tool-bar");
this->addRegionButton = this->regionSelector->findChild<SvgToolButton*>("add-region-btn");
this->removeRegionButton = this->regionSelector->findChild<SvgToolButton*>("remove-region-btn");
this->addFocusedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-focused-region");
this->addExcludedRegionMenuAction = this->addRegionButton->findChild<QAction*>("add-excluded-region");
this->regionItemScrollArea = this->regionSelector->findChild<QScrollArea*>("region-item-scroll-area");
this->regionItemScrollAreaViewport = this->regionItemScrollArea->findChild<QWidget*>("item-container");
this->regionItemScrollAreaViewportLayout = this->regionItemScrollAreaViewport->findChild<QVBoxLayout*>(
"item-container-layout"
);
this->stackedFormLayout = this->container->findChild<QStackedLayout*>("stacked-form-layout");
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
this->closeButton = this->container->findChild<QPushButton*>("close-btn");
regionSelectorToolBar->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollArea->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewport->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewportLayout->setContentsMargins(0, 0, 0, 0);
this->regionItemScrollAreaViewportLayout->setDirection(QBoxLayout::Direction::TopToBottom);
QObject::connect(
this->addFocusedRegionMenuAction,
&QAction::triggered,
this,
&MemoryRegionManagerWindow::onNewFocusedRegionTrigger
);
QObject::connect(
this->addExcludedRegionMenuAction,
&QAction::triggered,
this,
&MemoryRegionManagerWindow::onNewExcludedRegionTrigger
);
QObject::connect(
this->removeRegionButton,
&QToolButton::clicked,
this,
&MemoryRegionManagerWindow::onDeleteRegionTrigger
);
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
QObject::connect(this->applyButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::applyChanges);
QObject::connect(this->helpButton, &QPushButton::clicked, this, &MemoryRegionManagerWindow::openHelpPage);
this->refreshRegions();
// Position the inspection window at the center of the main Insight window
this->move(parent->window()->geometry().center() - this->rect().center());
this->show();
}
}
void MemoryRegionManagerWindow::applyChanges() {
auto processedFocusedMemoryRegions = std::vector<FocusedMemoryRegion>();
auto processedExcludedMemoryRegions = std::vector<ExcludedMemoryRegion>();
void MemoryRegionManagerWindow::refreshRegions() {
this->clearRegions();
for (auto* focusedRegionItem : this->focusedRegionItems) {
const auto validationFailures = focusedRegionItem->getValidationFailures();
for (const auto& focusedRegion: this->focusedMemoryRegions) {
this->addFocusedRegion(focusedRegion);
}
if (!validationFailures.empty()) {
auto* errorDialogue = new ErrorDialogue(
"Invalid Memory Region",
"Invalid memory region \"" + focusedRegionItem->getRegionNameInputValue() + "\""
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
this
for (const auto& excludedRegion: this->excludedMemoryRegions) {
this->addExcludedRegion(excludedRegion);
}
this->sortRegionItems();
}
void MemoryRegionManagerWindow::showEvent(QShowEvent* event) {
if (this->selectedRegion == nullptr && this->regionItemScrollAreaViewportLayout->count() > 0) {
auto* firstRegionItem = qobject_cast<RegionItem*>(
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
);
errorDialogue->show();
if (firstRegionItem != nullptr) {
firstRegionItem->setSelected(true);
}
}
}
void MemoryRegionManagerWindow::clearRegions() {
this->selectedRegion = nullptr;
for (auto* focusedRegionItem: this->focusedRegionItems) {
this->regionItemScrollAreaViewportLayout->removeWidget(focusedRegionItem);
focusedRegionItem->getFormWidget()->deleteLater();
focusedRegionItem->deleteLater();
}
for (auto* excludedRegionItem: this->excludedRegionItems) {
this->regionItemScrollAreaViewportLayout->removeWidget(excludedRegionItem);
excludedRegionItem->getFormWidget()->deleteLater();
excludedRegionItem->deleteLater();
}
this->focusedRegionItems.clear();
this->excludedRegionItems.clear();
}
void MemoryRegionManagerWindow::sortRegionItems() {
/*
* This isn't very pretty.
*
* Because the insertion order is persisted in QBoxLayouts, sorting the items requires removing them from the
* layout, and then re-inserting them in the correct order.
*/
auto regionItemCompare = [] (RegionItem* itemA, RegionItem* itemB) {
return itemA->getMemoryRegion().createdDate < itemB->getMemoryRegion().createdDate;
};
auto sortedRegionItems = std::set<RegionItem*, decltype(regionItemCompare)>(regionItemCompare);
QLayoutItem* layoutItem = nullptr;
while ((layoutItem = this->regionItemScrollAreaViewportLayout->takeAt(0)) != nullptr) {
auto* regionItem = qobject_cast<RegionItem*>(layoutItem->widget());
if (regionItem != nullptr) {
sortedRegionItems.insert(regionItem);
}
delete layoutItem;
}
for (auto* regionItem: sortedRegionItems) {
this->regionItemScrollAreaViewportLayout->addWidget(regionItem);
}
}
FocusedRegionItem* MemoryRegionManagerWindow::addFocusedRegion(const FocusedMemoryRegion& region) {
auto* focusedRegionItem = new FocusedRegionItem(
region,
this->memoryDescriptor,
this->regionItemScrollAreaViewport
);
this->focusedRegionItems.insert(focusedRegionItem);
this->regionItemScrollAreaViewportLayout->addWidget(focusedRegionItem);
this->stackedFormLayout->addWidget(focusedRegionItem->getFormWidget());
QObject::connect(
focusedRegionItem,
&RegionItem::selected,
this,
&MemoryRegionManagerWindow::onRegionSelected
);
return focusedRegionItem;
}
ExcludedRegionItem* MemoryRegionManagerWindow::addExcludedRegion(const ExcludedMemoryRegion& region) {
auto* excludedRegionItem = new ExcludedRegionItem(
region,
this->memoryDescriptor,
this->regionItemScrollAreaViewport
);
this->excludedRegionItems.insert(excludedRegionItem);
this->regionItemScrollAreaViewportLayout->addWidget(excludedRegionItem);
this->stackedFormLayout->addWidget(excludedRegionItem->getFormWidget());
QObject::connect(
excludedRegionItem,
&RegionItem::selected,
this,
&MemoryRegionManagerWindow::onRegionSelected
);
return excludedRegionItem;
}
void MemoryRegionManagerWindow::onRegionSelected(RegionItem* selectedRegion) {
if (this->selectedRegion != nullptr && this->selectedRegion != selectedRegion) {
this->selectedRegion->setSelected(false);
}
this->selectedRegion = selectedRegion;
this->stackedFormLayout->setCurrentWidget(this->selectedRegion->getFormWidget());
}
void MemoryRegionManagerWindow::onNewFocusedRegionTrigger() {
using Targets::TargetMemoryAddressRange;
auto* region = this->addFocusedRegion(FocusedMemoryRegion(
"Untitled Region",
TargetMemoryAddressRange(
this->memoryDescriptor.addressRange.startAddress,
this->memoryDescriptor.addressRange.startAddress + 10
)
));
region->setSelected(true);
}
void MemoryRegionManagerWindow::onNewExcludedRegionTrigger() {
using Targets::TargetMemoryAddressRange;
auto* region = this->addExcludedRegion(ExcludedMemoryRegion(
"Untitled Region",
TargetMemoryAddressRange(
this->memoryDescriptor.addressRange.startAddress,
this->memoryDescriptor.addressRange.startAddress + 10
)
));
region->setSelected(true);
}
void MemoryRegionManagerWindow::onDeleteRegionTrigger() {
if (this->selectedRegion == nullptr) {
return;
}
focusedRegionItem->applyChanges();
const auto& focusedRegion = focusedRegionItem->getMemoryRegion();
for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) {
if (processedFocusedRegion.intersectsWith(focusedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + focusedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedFocusedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
auto* regionItem = this->selectedRegion;
const auto& region = regionItem->getMemoryRegion();
if (region.type == MemoryRegionType::FOCUSED) {
auto* focusedRegionItem = qobject_cast<FocusedRegionItem*>(regionItem);
if (focusedRegionItem != nullptr) {
this->focusedRegionItems.erase(focusedRegionItem);
}
} else {
auto* excludedRegionItem = qobject_cast<ExcludedRegionItem*>(regionItem);
if (excludedRegionItem != nullptr) {
this->excludedRegionItems.erase(excludedRegionItem);
}
}
processedFocusedMemoryRegions.emplace_back(focusedRegion);
}
regionItem->getFormWidget()->deleteLater();
this->regionItemScrollAreaViewportLayout->removeWidget(regionItem);
regionItem->deleteLater();
for (auto* excludedRegionItem : this->excludedRegionItems) {
const auto validationFailures = excludedRegionItem->getValidationFailures();
this->selectedRegion = nullptr;
if (!validationFailures.empty()) {
auto* errorDialogue = new ErrorDialogue(
"Invalid Memory Region",
"Invalid memory region \"" + excludedRegionItem->getRegionNameInputValue() + "\""
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
this
if (this->regionItemScrollAreaViewportLayout->count() > 0) {
auto* regionItem = qobject_cast<RegionItem*>(
this->regionItemScrollAreaViewportLayout->itemAt(0)->widget()
);
errorDialogue->show();
return;
}
excludedRegionItem->applyChanges();
auto excludedRegion = excludedRegionItem->getMemoryRegion();
for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) {
if (processedFocusedRegion.intersectsWith(excludedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + excludedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedFocusedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
if (regionItem != nullptr) {
regionItem->setSelected(true);
}
}
for (const auto& processedExcludedRegion : processedExcludedMemoryRegions) {
if (processedExcludedRegion.intersectsWith(excludedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + excludedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedExcludedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
}
}
processedExcludedMemoryRegions.emplace_back(excludedRegion);
}
this->focusedMemoryRegions = std::move(processedFocusedMemoryRegions);
this->excludedMemoryRegions = std::move(processedExcludedMemoryRegions);
this->close();
emit this->changesApplied();
}
void MemoryRegionManagerWindow::applyChanges() {
auto processedFocusedMemoryRegions = std::vector<FocusedMemoryRegion>();
auto processedExcludedMemoryRegions = std::vector<ExcludedMemoryRegion>();
void MemoryRegionManagerWindow::openHelpPage() {
QDesktopServices::openUrl(
QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/manage-memory-regions"))
);
for (auto* focusedRegionItem : this->focusedRegionItems) {
const auto validationFailures = focusedRegionItem->getValidationFailures();
if (!validationFailures.empty()) {
auto* errorDialogue = new ErrorDialogue(
"Invalid Memory Region",
"Invalid memory region \"" + focusedRegionItem->getRegionNameInputValue() + "\""
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
this
);
errorDialogue->show();
return;
}
focusedRegionItem->applyChanges();
const auto& focusedRegion = focusedRegionItem->getMemoryRegion();
for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) {
if (processedFocusedRegion.intersectsWith(focusedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + focusedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedFocusedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
}
}
processedFocusedMemoryRegions.emplace_back(focusedRegion);
}
for (auto* excludedRegionItem : this->excludedRegionItems) {
const auto validationFailures = excludedRegionItem->getValidationFailures();
if (!validationFailures.empty()) {
auto* errorDialogue = new ErrorDialogue(
"Invalid Memory Region",
"Invalid memory region \"" + excludedRegionItem->getRegionNameInputValue() + "\""
+ "<br/><br/>- " + validationFailures.join("<br/>- "),
this
);
errorDialogue->show();
return;
}
excludedRegionItem->applyChanges();
auto excludedRegion = excludedRegionItem->getMemoryRegion();
for (const auto& processedFocusedRegion : processedFocusedMemoryRegions) {
if (processedFocusedRegion.intersectsWith(excludedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + excludedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedFocusedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
}
}
for (const auto& processedExcludedRegion : processedExcludedMemoryRegions) {
if (processedExcludedRegion.intersectsWith(excludedRegion)) {
auto* errorDialogue = new ErrorDialogue(
"Intersecting Region Found",
"Region \"" + excludedRegionItem->getRegionNameInputValue()
+ "\" intersects with region \"" + processedExcludedRegion.name + "\". "
+ "Regions cannot intersect. Please review the relevant address ranges.",
this
);
errorDialogue->show();
return;
}
}
processedExcludedMemoryRegions.emplace_back(excludedRegion);
}
this->focusedMemoryRegions = std::move(processedFocusedMemoryRegions);
this->excludedMemoryRegions = std::move(processedExcludedMemoryRegions);
this->close();
emit this->changesApplied();
}
void MemoryRegionManagerWindow::openHelpPage() {
QDesktopServices::openUrl(
QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/manage-memory-regions"))
);
}
}

View File

@@ -3,217 +3,228 @@
#include <QHBoxLayout>
#include <QString>
using namespace Bloom;
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
using Targets::TargetMemoryAddressRange;
using Targets::TargetMemoryAddressRange;
RegionItem::RegionItem(
const MemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryDescriptor(memoryDescriptor), ClickableWidget(parent) {
this->setObjectName("region-item");
this->setFixedHeight(50);
this->layout->setContentsMargins(5, 5, 5, 0);
RegionItem::RegionItem(
const MemoryRegion& region,
const Targets::TargetMemoryDescriptor& memoryDescriptor,
QWidget* parent
): memoryDescriptor(memoryDescriptor), ClickableWidget(parent) {
this->setObjectName("region-item");
this->setFixedHeight(50);
this->layout->setContentsMargins(5, 5, 5, 0);
this->timeLabel->setText(region.createdDate.toString("hh:mm"));
this->timeLabel->setObjectName("time-label");
this->timeLabel->setText(region.createdDate.toString("hh:mm"));
this->timeLabel->setObjectName("time-label");
auto regionName = region.name;
regionName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH);
this->nameLabel->setText(regionName);
this->nameLabel->setObjectName("name-label");
auto regionName = region.name;
regionName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH);
this->nameLabel->setText(regionName);
this->nameLabel->setObjectName("name-label");
this->typeLabel->setText(region.type == MemoryRegionType::FOCUSED ? "Focused" : "Excluded");
this->typeLabel->setObjectName("type-label");
this->typeLabel->setText(region.type == MemoryRegionType::FOCUSED ? "Focused" : "Excluded");
this->typeLabel->setObjectName("type-label");
this->addressRangeLabel->setText(
"0x" + QString::number(region.addressRange.startAddress, 16).toUpper() + QString(" -> ")
+ "0x" + QString::number(region.addressRange.endAddress, 16).toUpper()
);
this->addressRangeLabel->setObjectName("address-label");
this->addressRangeLabel->setText(
"0x" + QString::number(region.addressRange.startAddress, 16).toUpper() + QString(" -> ")
+ "0x" + QString::number(region.addressRange.endAddress, 16).toUpper()
);
this->addressRangeLabel->setObjectName("address-label");
auto* topLabelLayout = new QHBoxLayout();
topLabelLayout->setSpacing(0);
topLabelLayout->setContentsMargins(0, 0, 0, 0);
topLabelLayout->addWidget(this->nameLabel, 0, Qt::AlignmentFlag::AlignLeft);
topLabelLayout->addStretch(1);
topLabelLayout->addWidget(this->typeLabel, 0, Qt::AlignmentFlag::AlignRight);
auto* topLabelLayout = new QHBoxLayout();
topLabelLayout->setSpacing(0);
topLabelLayout->setContentsMargins(0, 0, 0, 0);
topLabelLayout->addWidget(this->nameLabel, 0, Qt::AlignmentFlag::AlignLeft);
topLabelLayout->addStretch(1);
topLabelLayout->addWidget(this->typeLabel, 0, Qt::AlignmentFlag::AlignRight);
auto* bottomLabelLayout = new QHBoxLayout();
bottomLabelLayout->setSpacing(0);
bottomLabelLayout->setContentsMargins(0, 0, 0, 0);
bottomLabelLayout->addWidget(this->addressRangeLabel, 0, Qt::AlignmentFlag::AlignLeft);
bottomLabelLayout->addStretch(1);
bottomLabelLayout->addWidget(this->timeLabel, 0, Qt::AlignmentFlag::AlignRight);
auto* bottomLabelLayout = new QHBoxLayout();
bottomLabelLayout->setSpacing(0);
bottomLabelLayout->setContentsMargins(0, 0, 0, 0);
bottomLabelLayout->addWidget(this->addressRangeLabel, 0, Qt::AlignmentFlag::AlignLeft);
bottomLabelLayout->addStretch(1);
bottomLabelLayout->addWidget(this->timeLabel, 0, Qt::AlignmentFlag::AlignRight);
this->layout->setSpacing(5);
this->layout->addLayout(topLabelLayout);
this->layout->addLayout(bottomLabelLayout);
this->layout->addStretch(1);
this->layout->setSpacing(5);
this->layout->addLayout(topLabelLayout);
this->layout->addLayout(bottomLabelLayout);
this->layout->addStretch(1);
auto onClick = [this] {
this->setSelected(true);
};
auto onClick = [this] {
this->setSelected(true);
};
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
this->setSelected(false);
}
void RegionItem::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
if (selected) {
emit this->selected(this);
}
}
QStringList RegionItem::getValidationFailures() const {
auto validationFailures = QStringList();
if (this->nameInput->text().isEmpty()) {
validationFailures.emplace_back("Missing region name.");
this->setSelected(false);
}
bool conversionOk = false;
void RegionItem::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&conversionOk, 16);
if (!conversionOk) {
validationFailures.emplace_back("Invalid start address.");
if (selected) {
emit this->selected(this);
}
}
std::uint32_t endAddress = this->endAddressInput->text().toUInt(&conversionOk, 16);
if (!conversionOk) {
validationFailures.emplace_back("Invalid end address.");
QStringList RegionItem::getValidationFailures() const {
auto validationFailures = QStringList();
if (this->nameInput->text().isEmpty()) {
validationFailures.emplace_back("Missing region name.");
}
bool conversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&conversionOk, 16);
if (!conversionOk) {
validationFailures.emplace_back("Invalid start address.");
}
std::uint32_t endAddress = this->endAddressInput->text().toUInt(&conversionOk, 16);
if (!conversionOk) {
validationFailures.emplace_back("Invalid end address.");
}
if (startAddress > endAddress) {
validationFailures.emplace_back("The start address exceeds the end address.");
}
auto addressType = this->getSelectedAddressInputType();
const auto memoryAddressRange = this->memoryDescriptor.addressRange;
const auto memoryAddressRangeStr = QString(
"0x" + QString::number(memoryAddressRange.startAddress, 16).toUpper() + QString(" -> ")
+ "0x" + QString::number(memoryAddressRange.endAddress, 16).toUpper()
);
const auto absoluteAddressRange = addressType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(
TargetMemoryAddressRange(startAddress, endAddress)
) : TargetMemoryAddressRange(startAddress, endAddress);
if (absoluteAddressRange.startAddress < memoryAddressRange.startAddress
|| absoluteAddressRange.startAddress > memoryAddressRange.endAddress
) {
validationFailures.emplace_back(
"The start address is not within the absolute memory address range (" + memoryAddressRangeStr + ")."
);
}
if (absoluteAddressRange.endAddress < memoryAddressRange.startAddress
|| absoluteAddressRange.endAddress > memoryAddressRange.endAddress
) {
validationFailures.emplace_back(
"The end address not within the absolute memory address range (" + memoryAddressRangeStr + ")."
);
}
return validationFailures;
}
if (startAddress > endAddress) {
validationFailures.emplace_back("The start address exceeds the end address.");
void RegionItem::initFormInputs() {
const auto& region = this->getMemoryRegion();
this->nameInput = this->formWidget->findChild<TextInput*>("name-input");
this->addressTypeInput = this->formWidget->findChild<QComboBox*>("address-type-input");
this->startAddressInput = this->formWidget->findChild<TextInput*>("start-address-input");
this->endAddressInput = this->formWidget->findChild<TextInput*>("end-address-input");
this->sizeInput = this->formWidget->findChild<TextInput*>("size-input");
this->nameInput->setText(region.name);
this->sizeInput->setText(
QString::number((region.addressRange.endAddress - region.addressRange.startAddress) + 1)
);
for (const auto& [optionName, option] : RegionItem::addressRangeTypeOptionsByName) {
this->addressTypeInput->addItem(option.text, optionName);
}
if (region.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE) {
auto relativeAddressRange = this->convertAbsoluteToRelativeAddressRange(region.addressRange);
this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("relative").text);
this->startAddressInput->setText(
"0x" + QString::number(relativeAddressRange.startAddress, 16).toUpper()
);
this->endAddressInput->setText(
"0x" + QString::number(relativeAddressRange.endAddress, 16).toUpper()
);
} else {
this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("absolute").text);
this->startAddressInput->setText(
"0x" + QString::number(region.addressRange.startAddress, 16).toUpper()
);
this->endAddressInput->setText(
"0x" + QString::number(region.addressRange.endAddress, 16).toUpper()
);
}
QObject::connect(this->startAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange);
QObject::connect(this->endAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange);
QObject::connect(this->sizeInput, &QLineEdit::textEdited, this, &RegionItem::onSizeInputChange);
QObject::connect(this->nameInput, &QLineEdit::textEdited, this, &RegionItem::onNameInputChange);
}
auto addressType = this->getSelectedAddressInputType();
const auto memoryAddressRange = this->memoryDescriptor.addressRange;
MemoryRegionAddressInputType RegionItem::getSelectedAddressInputType() const {
auto selectedAddressTypeOptionName = this->addressTypeInput->currentData().toString();
if (RegionItem::addressRangeTypeOptionsByName.contains(selectedAddressTypeOptionName)) {
return RegionItem::addressRangeTypeOptionsByName.at(selectedAddressTypeOptionName).addressType;
}
const auto memoryAddressRangeStr = QString(
"0x" + QString::number(memoryAddressRange.startAddress, 16).toUpper() + QString(" -> ")
+ "0x" + QString::number(memoryAddressRange.endAddress, 16).toUpper()
);
return MemoryRegionAddressInputType::ABSOLUTE;
}
const auto absoluteAddressRange = addressType == MemoryRegionAddressInputType::RELATIVE ?
this->convertRelativeToAbsoluteAddressRange(TargetMemoryAddressRange(startAddress, endAddress))
: TargetMemoryAddressRange(startAddress, endAddress);
if (absoluteAddressRange.startAddress < memoryAddressRange.startAddress
|| absoluteAddressRange.startAddress > memoryAddressRange.endAddress
) {
validationFailures.emplace_back(
"The start address is not within the absolute memory address range (" + memoryAddressRangeStr + ")."
TargetMemoryAddressRange RegionItem::convertAbsoluteToRelativeAddressRange(
const TargetMemoryAddressRange& absoluteAddressRange
) const {
return TargetMemoryAddressRange(
absoluteAddressRange.startAddress - this->memoryDescriptor.addressRange.startAddress,
absoluteAddressRange.endAddress - this->memoryDescriptor.addressRange.startAddress
);
}
if (absoluteAddressRange.endAddress < memoryAddressRange.startAddress
|| absoluteAddressRange.endAddress > memoryAddressRange.endAddress
) {
validationFailures.emplace_back(
"The end address not within the absolute memory address range (" + memoryAddressRangeStr + ")."
TargetMemoryAddressRange RegionItem::convertRelativeToAbsoluteAddressRange(
const TargetMemoryAddressRange& relativeAddressRange
) const {
return TargetMemoryAddressRange(
relativeAddressRange.startAddress + this->memoryDescriptor.addressRange.startAddress,
relativeAddressRange.endAddress + this->memoryDescriptor.addressRange.startAddress
);
}
return validationFailures;
}
void RegionItem::onAddressRangeInputChange() {
bool startAddressConversionOk = false;
bool endAddressConversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16);
std::uint32_t endAddress = this->endAddressInput->text().toUInt(&endAddressConversionOk, 16);
void RegionItem::initFormInputs() {
const auto& region = this->getMemoryRegion();
this->nameInput = this->formWidget->findChild<TextInput*>("name-input");
this->addressTypeInput = this->formWidget->findChild<QComboBox*>("address-type-input");
this->startAddressInput = this->formWidget->findChild<TextInput*>("start-address-input");
this->endAddressInput = this->formWidget->findChild<TextInput*>("end-address-input");
this->sizeInput = this->formWidget->findChild<TextInput*>("size-input");
this->nameInput->setText(region.name);
this->sizeInput->setText(QString::number((region.addressRange.endAddress - region.addressRange.startAddress) + 1));
for (const auto& [optionName, option] : RegionItem::addressRangeTypeOptionsByName) {
this->addressTypeInput->addItem(option.text, optionName);
if (startAddressConversionOk && endAddressConversionOk && startAddress <= endAddress) {
this->sizeInput->setText(QString::number((endAddress - startAddress) + 1));
}
}
if (region.addressRangeInputType == MemoryRegionAddressInputType::RELATIVE) {
auto relativeAddressRange = this->convertAbsoluteToRelativeAddressRange(region.addressRange);
this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("relative").text);
void RegionItem::onSizeInputChange() {
bool startAddressConversionOk = false;
bool sizeConversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16);
std::uint32_t size = this->sizeInput->text().toUInt(&sizeConversionOk, 10);
this->startAddressInput->setText("0x" + QString::number(relativeAddressRange.startAddress, 16).toUpper());
this->endAddressInput->setText("0x" + QString::number(relativeAddressRange.endAddress, 16).toUpper());
} else {
this->addressTypeInput->setCurrentText(RegionItem::addressRangeTypeOptionsByName.at("absolute").text);
this->startAddressInput->setText("0x" + QString::number(region.addressRange.startAddress, 16).toUpper());
this->endAddressInput->setText("0x" + QString::number(region.addressRange.endAddress, 16).toUpper());
if (startAddressConversionOk && sizeConversionOk && size > 0) {
this->endAddressInput->setText("0x" + QString::number((startAddress + size) - 1, 16).toUpper());
}
}
QObject::connect(this->startAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange);
QObject::connect(this->endAddressInput, &QLineEdit::textEdited, this, &RegionItem::onAddressRangeInputChange);
QObject::connect(this->sizeInput, &QLineEdit::textEdited, this, &RegionItem::onSizeInputChange);
QObject::connect(this->nameInput, &QLineEdit::textEdited, this, &RegionItem::onNameInputChange);
}
MemoryRegionAddressInputType RegionItem::getSelectedAddressInputType() const {
auto selectedAddressTypeOptionName = this->addressTypeInput->currentData().toString();
if (RegionItem::addressRangeTypeOptionsByName.contains(selectedAddressTypeOptionName)) {
return RegionItem::addressRangeTypeOptionsByName.at(selectedAddressTypeOptionName).addressType;
}
return MemoryRegionAddressInputType::ABSOLUTE;
}
TargetMemoryAddressRange RegionItem::convertAbsoluteToRelativeAddressRange(
const TargetMemoryAddressRange& absoluteAddressRange
) const {
return TargetMemoryAddressRange(
absoluteAddressRange.startAddress - this->memoryDescriptor.addressRange.startAddress,
absoluteAddressRange.endAddress - this->memoryDescriptor.addressRange.startAddress
);
}
TargetMemoryAddressRange RegionItem::convertRelativeToAbsoluteAddressRange(
const TargetMemoryAddressRange& relativeAddressRange
) const {
return TargetMemoryAddressRange(
relativeAddressRange.startAddress + this->memoryDescriptor.addressRange.startAddress,
relativeAddressRange.endAddress + this->memoryDescriptor.addressRange.startAddress
);
}
void RegionItem::onAddressRangeInputChange() {
bool startAddressConversionOk = false;
bool endAddressConversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16);
std::uint32_t endAddress = this->endAddressInput->text().toUInt(&endAddressConversionOk, 16);
if (startAddressConversionOk && endAddressConversionOk && startAddress <= endAddress) {
this->sizeInput->setText(QString::number((endAddress - startAddress) + 1));
void RegionItem::onNameInputChange() {
auto newName = this->nameInput->text();
newName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH);
this->nameLabel->setText(newName);
}
}
void RegionItem::onSizeInputChange() {
bool startAddressConversionOk = false;
bool sizeConversionOk = false;
std::uint32_t startAddress = this->startAddressInput->text().toUInt(&startAddressConversionOk, 16);
std::uint32_t size = this->sizeInput->text().toUInt(&sizeConversionOk, 10);
if (startAddressConversionOk && sizeConversionOk && size > 0) {
this->endAddressInput->setText("0x" + QString::number((startAddress + size) - 1, 16).toUpper());
}
}
void RegionItem::onNameInputChange() {
auto newName = this->nameInput->text();
newName.truncate(RegionItem::NAME_LABEL_MAX_LENGTH);
this->nameLabel->setText(newName);
}

View File

@@ -4,75 +4,79 @@
#include <QScrollArea>
#include <QMargins>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
BitBodyWidget::BitBodyWidget(
int bitIndex,
std::bitset<std::numeric_limits<unsigned char>::digits>::reference bit,
bool readOnly,
QWidget* parent
): ClickableWidget(parent), bitIndex(bitIndex), bit(bit), readOnly(readOnly) {
this->setFixedSize(BitBodyWidget::WIDTH, BitBodyWidget::HEIGHT);
this->setContentsMargins(0, 0, 0, 0);
}
BitBodyWidget::BitBodyWidget(
int bitIndex,
std::bitset<std::numeric_limits<unsigned char>::digits>::reference bit,
bool readOnly,
QWidget* parent
): ClickableWidget(parent), bitIndex(bitIndex), bit(bit), readOnly(readOnly) {
this->setFixedSize(BitBodyWidget::WIDTH, BitBodyWidget::HEIGHT);
this->setContentsMargins(0, 0, 0, 0);
}
bool BitBodyWidget::event(QEvent* event) {
if (this->isEnabled() && !this->readOnly) {
switch (event->type()) {
case QEvent::Enter: {
this->hoverActive = true;
this->update();
break;
}
case QEvent::Leave: {
this->hoverActive = false;
this->update();
break;
}
default: {
break;
}
}
}
bool BitBodyWidget::event(QEvent* event) {
if (this->isEnabled() && !this->readOnly) {
switch (event->type()) {
case QEvent::Enter: {
this->hoverActive = true;
return QWidget::event(event);
}
void BitBodyWidget::mouseReleaseEvent(QMouseEvent* event) {
if (this->isEnabled()) {
if (!this->readOnly && event->button() == Qt::MouseButton::LeftButton) {
this->bit = !this->bit;
this->update();
break;
}
case QEvent::Leave: {
this->hoverActive = false;
this->update();
break;
}
default: {
break;
}
ClickableWidget::mouseReleaseEvent(event);
}
}
return QWidget::event(event);
}
void BitBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BitBodyWidget::mouseReleaseEvent(QMouseEvent* event) {
if (this->isEnabled()) {
if (!this->readOnly && event->button() == Qt::MouseButton::LeftButton) {
this->bit = !this->bit;
this->update();
void BitBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
auto bodyColor = QColor(this->bit == true ? "#7B5E38" : "#908D85");
if (!this->isEnabled()) {
bodyColor.setAlpha(100);
} else if (!this->hoverActive) {
bodyColor.setAlpha(235);
}
ClickableWidget::mouseReleaseEvent(event);
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(bodyColor);
painter.drawRect(
0,
0,
BitBodyWidget::WIDTH,
BitBodyWidget::HEIGHT
);
}
}
void BitBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BitBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
auto bodyColor = QColor(this->bit == true ? "#7B5E38" : "#908D85");
if (!this->isEnabled()) {
bodyColor.setAlpha(100);
} else if (!this->hoverActive) {
bodyColor.setAlpha(235);
}
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(bodyColor);
painter.drawRect(
0,
0,
BitBodyWidget::WIDTH,
BitBodyWidget::HEIGHT
);
}

View File

@@ -6,50 +6,51 @@
#include "BitBodyWidget.hpp"
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
BitWidget::BitWidget(
int bitIndex,
int bitNumber,
std::bitset<std::numeric_limits<unsigned char>::digits>& bitset,
bool readOnly,
QWidget* parent
): QWidget(parent), bitIndex(bitIndex), bitNumber(bitNumber), bitset(bitset), readOnly(readOnly) {
this->setFixedSize(BitWidget::WIDTH, BitWidget::HEIGHT);
BitWidget::BitWidget(
int bitIndex,
int bitNumber,
std::bitset<std::numeric_limits<unsigned char>::digits>& bitset,
bool readOnly,
QWidget* parent
): QWidget(parent), bitIndex(bitIndex), bitNumber(bitNumber), bitset(bitset), readOnly(readOnly) {
this->setFixedSize(BitWidget::WIDTH, BitWidget::HEIGHT);
auto* layout = new QVBoxLayout(this);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
layout->setAlignment(Qt::AlignmentFlag::AlignTop);
auto* layout = new QVBoxLayout(this);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
layout->setAlignment(Qt::AlignmentFlag::AlignTop);
this->body = new BitBodyWidget(
this->bitIndex,
this->bitset[static_cast<size_t>(this->bitIndex)],
this->readOnly,
this
);
this->body = new BitBodyWidget(
this->bitIndex,
this->bitset[static_cast<size_t>(this->bitIndex)],
this->readOnly,
this
);
this->bitLabel = new QLabel("Bit", this);
this->bitNumberLabel = new QLabel(QString::number(this->bitNumber), this);
this->bitLabel = new QLabel("Bit", this);
this->bitNumberLabel = new QLabel(QString::number(this->bitNumber), this);
this->bitLabel->setObjectName("register-bit-label");
this->bitNumberLabel->setObjectName("register-bit-number-label");
this->bitLabel->setObjectName("register-bit-label");
this->bitNumberLabel->setObjectName("register-bit-number-label");
this->bitLabel->setFixedHeight(BitWidget::LABEL_HEIGHT);
this->bitNumberLabel->setFixedHeight(BitWidget::LABEL_HEIGHT);
this->bitLabel->setFixedHeight(BitWidget::LABEL_HEIGHT);
this->bitNumberLabel->setFixedHeight(BitWidget::LABEL_HEIGHT);
this->bitLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
this->bitNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
this->bitLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
this->bitNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
layout->addWidget(this->bitLabel);
layout->addWidget(this->bitNumberLabel);
layout->addSpacing(BitWidget::VERTICAL_SPACING);
layout->addWidget(this->body);
layout->addStretch(1);
layout->addWidget(this->bitLabel);
layout->addWidget(this->bitNumberLabel);
layout->addSpacing(BitWidget::VERTICAL_SPACING);
layout->addWidget(this->body);
layout->addStretch(1);
if (!this->readOnly) {
QObject::connect(this->body, &BitBodyWidget::clicked, this, [this] {
emit this->bitChanged();
});
if (!this->readOnly) {
QObject::connect(this->body, &BitBodyWidget::clicked, this, [this] {
emit this->bitChanged();
});
}
}
}

View File

@@ -8,101 +8,102 @@
#include "../TargetRegisterInspectorWindow.hpp"
using namespace Bloom::Widgets;
BitsetWidget::BitsetWidget(int byteNumber, unsigned char& byte, bool readOnly, QWidget* parent):
QWidget(parent), byteNumber(byteNumber), byte(byte), readOnly(readOnly) {
this->setObjectName("bitset-widget");
auto* bitLayout = new QHBoxLayout(this);
bitLayout->setSpacing(BitWidget::SPACING);
bitLayout->setContentsMargins(0, 0, 0, 0);
this->setContentsMargins(0, 0, 0, 0);
this->setFixedSize(
static_cast<int>((BitWidget::WIDTH + BitWidget::SPACING) * this->bitset.size() - BitWidget::SPACING),
BitsetWidget::HEIGHT
);
for (int bitIndex = (std::numeric_limits<unsigned char>::digits - 1); bitIndex >= 0; bitIndex--) {
auto* bitWidget = new BitWidget(
bitIndex,
(this->byteNumber * 8) + bitIndex,
this->bitset,
this->readOnly,
this
namespace Bloom::Widgets
{
BitsetWidget::BitsetWidget(int byteNumber, unsigned char& byte, bool readOnly, QWidget* parent)
: QWidget(parent), byteNumber(byteNumber), byte(byte), readOnly(readOnly) {
this->setObjectName("bitset-widget");
auto* bitLayout = new QHBoxLayout(this);
bitLayout->setSpacing(BitWidget::SPACING);
bitLayout->setContentsMargins(0, 0, 0, 0);
this->setContentsMargins(0, 0, 0, 0);
this->setFixedSize(
static_cast<int>((BitWidget::WIDTH + BitWidget::SPACING) * this->bitset.size() - BitWidget::SPACING),
BitsetWidget::HEIGHT
);
bitLayout->addWidget(bitWidget, 0, Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop);
QObject::connect(
bitWidget,
&BitWidget::bitChanged,
this,
[this] {
this->byte = static_cast<unsigned char>(this->bitset.to_ulong());
this->repaint();
emit this->byteChanged();
}
for (int bitIndex = (std::numeric_limits<unsigned char>::digits - 1); bitIndex >= 0; bitIndex--) {
auto* bitWidget = new BitWidget(
bitIndex,
(this->byteNumber * 8) + bitIndex,
this->bitset,
this->readOnly,
this
);
bitLayout->addWidget(bitWidget, 0, Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop);
QObject::connect(
bitWidget,
&BitWidget::bitChanged,
this,
[this] {
this->byte = static_cast<unsigned char>(this->bitset.to_ulong());
this->repaint();
emit this->byteChanged();
}
);
}
}
void BitsetWidget::updateValue() {
this->bitset = {this->byte};
this->update();
}
void BitsetWidget::paintEvent(QPaintEvent* event) {
QWidget::paintEvent(event);
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BitsetWidget::drawWidget(QPainter& painter) {
auto byteHex = "0x" + QString::number(this->byte, 16).toUpper();
painter.setPen(QColor("#474747"));
constexpr int labelHeight = 11;
int containerWidth = this->width();
constexpr int charWidth = 6;
auto labelWidth = static_cast<int>(charWidth * byteHex.size()) + 13;
auto width = (containerWidth - (BitWidget::WIDTH) - labelWidth) / 2;
painter.drawLine(QLine(
BitWidget::WIDTH / 2,
BitWidget::HEIGHT,
BitWidget::WIDTH / 2,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT,
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
BitWidget::WIDTH / 2,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT,
static_cast<int>((BitWidget::WIDTH / 2) + width),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
static_cast<int>((BitWidget::WIDTH / 2) + width + labelWidth),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT,
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.setPen(QColor("#8a8a8d"));
painter.drawText(
QRect(
static_cast<int>((BitWidget::WIDTH / 2) + width),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - (labelHeight / 2),
labelWidth,
labelHeight
),
Qt::AlignCenter,
byteHex
);
}
}
void BitsetWidget::updateValue() {
this->bitset = {this->byte};
this->update();
}
void BitsetWidget::paintEvent(QPaintEvent* event) {
QWidget::paintEvent(event);
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BitsetWidget::drawWidget(QPainter& painter) {
auto byteHex = "0x" + QString::number(this->byte, 16).toUpper();
painter.setPen(QColor("#474747"));
constexpr int labelHeight = 11;
int containerWidth = this->width();
constexpr int charWidth = 6;
auto labelWidth = static_cast<int>(charWidth * byteHex.size()) + 13;
auto width = (containerWidth - (BitWidget::WIDTH) - labelWidth) / 2;
painter.drawLine(QLine(
BitWidget::WIDTH / 2,
BitWidget::HEIGHT,
BitWidget::WIDTH / 2,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT,
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
BitWidget::WIDTH / 2,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT,
static_cast<int>((BitWidget::WIDTH / 2) + width),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.drawLine(QLine(
static_cast<int>((BitWidget::WIDTH / 2) + width + labelWidth),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT,
containerWidth - (BitWidget::WIDTH / 2) - 1,
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT
));
painter.setPen(QColor("#8a8a8d"));
painter.drawText(
QRect(
static_cast<int>((BitWidget::WIDTH / 2) + width),
BitWidget::HEIGHT + BitsetWidget::VALUE_GRAPHIC_HEIGHT - (labelHeight / 2),
labelWidth,
labelHeight
),
Qt::AlignCenter,
byteHex
);
}

View File

@@ -3,15 +3,16 @@
#include <QStyle>
#include <QVBoxLayout>
using namespace Bloom::Widgets;
CurrentItem::CurrentItem(
const Targets::TargetMemoryBuffer& registerValue,
QWidget* parent
): Item(registerValue, parent) {
this->setObjectName("current-item");
this->setFixedHeight(30);
this->titleLabel->setText("Current value");
this->layout->addWidget(titleLabel, 0, Qt::AlignmentFlag::AlignLeft);
this->layout->setContentsMargins(5, 0, 5, 0);
namespace Bloom::Widgets
{
CurrentItem::CurrentItem(
const Targets::TargetMemoryBuffer& registerValue,
QWidget* parent
): Item(registerValue, parent) {
this->setObjectName("current-item");
this->setFixedHeight(30);
this->titleLabel->setText("Current value");
this->layout->addWidget(titleLabel, 0, Qt::AlignmentFlag::AlignLeft);
this->layout->setContentsMargins(5, 0, 5, 0);
}
}

View File

@@ -2,26 +2,27 @@
#include <QStyle>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
Item::Item(const Targets::TargetMemoryBuffer& registerValue, QWidget* parent)
: ClickableWidget(parent), registerValue(registerValue) {
auto onClick = [this] {
this->setSelected(true);
};
Item::Item(const Targets::TargetMemoryBuffer& registerValue, QWidget* parent):
ClickableWidget(parent), registerValue(registerValue) {
auto onClick = [this] {
this->setSelected(true);
};
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
this->setSelected(false);
}
this->setSelected(false);
}
void Item::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
void Item::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
if (selected) {
emit this->selected(this);
if (selected) {
emit this->selected(this);
}
}
}

View File

@@ -4,38 +4,39 @@
#include <QHBoxLayout>
#include <QByteArray>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
RegisterHistoryItem::RegisterHistoryItem(
const Targets::TargetMemoryBuffer& registerValue,
const QDateTime& changeDate,
QWidget* parent
): Item(registerValue, parent) {
this->setObjectName("register-history-item");
this->setFixedHeight(50);
this->layout->setContentsMargins(5, 8, 5, 0);
RegisterHistoryItem::RegisterHistoryItem(
const Targets::TargetMemoryBuffer& registerValue,
const QDateTime& changeDate,
QWidget* parent
): Item(registerValue, parent) {
this->setObjectName("register-history-item");
this->setFixedHeight(50);
this->layout->setContentsMargins(5, 8, 5, 0);
this->dateLabel->setText(changeDate.toString("dd/MM/yyyy hh:mm:ss"));
this->dateLabel->setObjectName("date-label");
this->dateLabel->setText(changeDate.toString("dd/MM/yyyy hh:mm:ss"));
this->dateLabel->setObjectName("date-label");
this->valueLabel->setText("0x" + QString(QByteArray(
reinterpret_cast<const char*>(registerValue.data()),
static_cast<qsizetype>(registerValue.size())
).toHex()).toUpper());
this->valueLabel->setObjectName("value-label");
this->valueLabel->setText("0x" + QString(QByteArray(
reinterpret_cast<const char*>(registerValue.data()),
static_cast<qsizetype>(registerValue.size())
).toHex()).toUpper());
this->valueLabel->setObjectName("value-label");
this->descriptionLabel->setText("Register Written");
this->descriptionLabel->setObjectName("description-label");
this->descriptionLabel->setText("Register Written");
this->descriptionLabel->setObjectName("description-label");
auto* subLabelLayout = new QHBoxLayout();
subLabelLayout->setSpacing(0);
subLabelLayout->setContentsMargins(0, 0, 0, 0);
subLabelLayout->addWidget(this->valueLabel, 0, Qt::AlignmentFlag::AlignLeft);
subLabelLayout->addStretch(1);
subLabelLayout->addWidget(this->descriptionLabel, 0, Qt::AlignmentFlag::AlignRight);
auto* subLabelLayout = new QHBoxLayout();
subLabelLayout->setSpacing(0);
subLabelLayout->setContentsMargins(0, 0, 0, 0);
subLabelLayout->addWidget(this->valueLabel, 0, Qt::AlignmentFlag::AlignLeft);
subLabelLayout->addStretch(1);
subLabelLayout->addWidget(this->descriptionLabel, 0, Qt::AlignmentFlag::AlignRight);
this->layout->setSpacing(5);
this->layout->addWidget(this->dateLabel, 0, Qt::AlignmentFlag::AlignTop);
this->layout->addLayout(subLabelLayout);
this->layout->addStretch(1);
this->layout->setSpacing(5);
this->layout->addWidget(this->dateLabel, 0, Qt::AlignmentFlag::AlignTop);
this->layout->addLayout(subLabelLayout);
this->layout->addStretch(1);
}
}

View File

@@ -12,125 +12,130 @@
#include "src/Helpers/DateTime.hpp"
#include "src/Exceptions/Exception.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
namespace Bloom::Widgets
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
RegisterHistoryWidget::RegisterHistoryWidget(
const Targets::TargetRegisterDescriptor& registerDescriptor,
const Targets::TargetMemoryBuffer& currentValue,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent), registerDescriptor(registerDescriptor), insightWorker(insightWorker) {
this->setObjectName("target-register-history-widget");
this->setFixedWidth(300);
RegisterHistoryWidget::RegisterHistoryWidget(
const Targets::TargetRegisterDescriptor& registerDescriptor,
const Targets::TargetMemoryBuffer& currentValue,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent), registerDescriptor(registerDescriptor), insightWorker(insightWorker) {
this->setObjectName("target-register-history-widget");
this->setFixedWidth(300);
auto widgetUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget"
+ "/UiFiles/RegisterHistoryWidget.ui"
)
);
auto widgetUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/RegisterHistoryWidget"
+ "/UiFiles/RegisterHistoryWidget.ui"
)
);
if (!widgetUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open RegisterHistoryWidget UI file");
}
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&widgetUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(1, 1, 1, 1);
this->itemContainer = this->container->findChild<QWidget*>("item-container");
this->itemContainerLayout = this->itemContainer->findChild<QVBoxLayout*>("item-container-layout");
auto titleBar = this->container->findChild<QWidget*>("title-bar");
auto title = titleBar->findChild<QLabel*>("title");
titleBar->setContentsMargins(0, 0, 0, 0);
title->setFixedHeight(titleBar->height());
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&RegisterHistoryWidget::onTargetStateChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetRegistersWritten,
this,
&RegisterHistoryWidget::onRegistersWritten
);
this->currentItem = new CurrentItem(currentValue, this);
QObject::connect(this->currentItem, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange);
this->itemContainerLayout->addWidget(this->currentItem);
this->currentItem->setSelected(true);
auto* separatorWidget = new QWidget(this);
auto* separatorLayout = new QHBoxLayout(separatorWidget);
auto* separatorLabel = new QLabel("Select an item to restore", separatorWidget);
separatorWidget->setFixedHeight(40);
separatorWidget->setObjectName("separator-widget");
separatorLayout->setContentsMargins(0, 10, 0, 10);
separatorLabel->setObjectName("separator-label");
separatorLayout->addWidget(separatorLabel, 0, Qt::AlignmentFlag::AlignHCenter);
this->itemContainerLayout->addWidget(separatorWidget);
this->show();
}
void RegisterHistoryWidget::updateCurrentItemValue(const Targets::TargetMemoryBuffer& registerValue) {
this->currentItem->registerValue = registerValue;
if (this->selectedItemWidget != nullptr && this->currentItem == this->selectedItemWidget) {
this->selectCurrentItem();
}
}
void RegisterHistoryWidget::selectCurrentItem() {
this->currentItem->setSelected(true);
}
void RegisterHistoryWidget::addItem(const Targets::TargetMemoryBuffer& registerValue, const QDateTime& changeDate) {
auto* item = new RegisterHistoryItem(registerValue, changeDate, this->itemContainer);
QObject::connect(item, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange);
this->itemContainerLayout->insertWidget(2, item);
}
void RegisterHistoryWidget::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(
this->width(),
this->height()
);
}
void RegisterHistoryWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void RegisterHistoryWidget::onItemSelectionChange(Item* newlySelectedWidget) {
if (this->selectedItemWidget != newlySelectedWidget) {
if (this->selectedItemWidget != nullptr) {
this->selectedItemWidget->setSelected(false);
if (!widgetUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open RegisterHistoryWidget UI file");
}
this->selectedItemWidget = newlySelectedWidget;
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&widgetUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(1, 1, 1, 1);
this->itemContainer = this->container->findChild<QWidget*>("item-container");
this->itemContainerLayout = this->itemContainer->findChild<QVBoxLayout*>("item-container-layout");
auto titleBar = this->container->findChild<QWidget*>("title-bar");
auto title = titleBar->findChild<QLabel*>("title");
titleBar->setContentsMargins(0, 0, 0, 0);
title->setFixedHeight(titleBar->height());
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&RegisterHistoryWidget::onTargetStateChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetRegistersWritten,
this,
&RegisterHistoryWidget::onRegistersWritten
);
this->currentItem = new CurrentItem(currentValue, this);
QObject::connect(this->currentItem, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange);
this->itemContainerLayout->addWidget(this->currentItem);
this->currentItem->setSelected(true);
auto* separatorWidget = new QWidget(this);
auto* separatorLayout = new QHBoxLayout(separatorWidget);
auto* separatorLabel = new QLabel("Select an item to restore", separatorWidget);
separatorWidget->setFixedHeight(40);
separatorWidget->setObjectName("separator-widget");
separatorLayout->setContentsMargins(0, 10, 0, 10);
separatorLabel->setObjectName("separator-label");
separatorLayout->addWidget(separatorLabel, 0, Qt::AlignmentFlag::AlignHCenter);
this->itemContainerLayout->addWidget(separatorWidget);
this->show();
}
emit this->historyItemSelected(newlySelectedWidget->registerValue);
}
void RegisterHistoryWidget::updateCurrentItemValue(const Targets::TargetMemoryBuffer& registerValue) {
this->currentItem->registerValue = registerValue;
void RegisterHistoryWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters, const QDateTime& changeDate) {
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor == this->registerDescriptor) {
this->addItem(targetRegister.value, changeDate);
this->updateCurrentItemValue(targetRegister.value);
if (this->selectedItemWidget != nullptr && this->currentItem == this->selectedItemWidget) {
this->selectCurrentItem();
}
}
void RegisterHistoryWidget::selectCurrentItem() {
this->currentItem->setSelected(true);
}
void RegisterHistoryWidget::addItem(const Targets::TargetMemoryBuffer& registerValue, const QDateTime& changeDate) {
auto* item = new RegisterHistoryItem(registerValue, changeDate, this->itemContainer);
QObject::connect(item, &Item::selected, this, &RegisterHistoryWidget::onItemSelectionChange);
this->itemContainerLayout->insertWidget(2, item);
}
void RegisterHistoryWidget::resizeEvent(QResizeEvent* event) {
this->container->setFixedSize(
this->width(),
this->height()
);
}
void RegisterHistoryWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
}
void RegisterHistoryWidget::onItemSelectionChange(Item* newlySelectedWidget) {
if (this->selectedItemWidget != newlySelectedWidget) {
if (this->selectedItemWidget != nullptr) {
this->selectedItemWidget->setSelected(false);
}
this->selectedItemWidget = newlySelectedWidget;
}
emit this->historyItemSelected(newlySelectedWidget->registerValue);
}
void RegisterHistoryWidget::onRegistersWritten(
Targets::TargetRegisters targetRegisters,
const QDateTime& changeDate
) {
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor == this->registerDescriptor) {
this->addItem(targetRegister.value, changeDate);
this->updateCurrentItemValue(targetRegister.value);
}
}
}
}

View File

@@ -14,360 +14,380 @@
#include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp"
#include "src/Insight/InsightWorker/Tasks/WriteTargetRegister.hpp"
using namespace Bloom::Widgets;
using Bloom::Exceptions::Exception;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetState;
TargetRegisterInspectorWindow::TargetRegisterInspectorWindow(
const Targets::TargetRegisterDescriptor& registerDescriptor,
InsightWorker& insightWorker,
TargetState currentTargetState,
std::optional<Targets::TargetMemoryBuffer> registerValue,
QWidget* parent
):
QWidget(parent),
registerDescriptor(registerDescriptor),
insightWorker(insightWorker),
registerValue(registerValue.value_or(Targets::TargetMemoryBuffer(registerDescriptor.size, 0)))
namespace Bloom::Widgets
{
this->setWindowFlag(Qt::Window);
auto registerName = QString::fromStdString(this->registerDescriptor.name.value()).toUpper();
this->setObjectName("target-register-inspector-window");
this->setWindowTitle("Inspect Register");
using Bloom::Exceptions::Exception;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetState;
auto windowUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/UiFiles/TargetRegisterInspectorWindow.ui"
)
);
TargetRegisterInspectorWindow::TargetRegisterInspectorWindow(
const Targets::TargetRegisterDescriptor& registerDescriptor,
InsightWorker& insightWorker,
TargetState currentTargetState,
const std::optional<Targets::TargetMemoryBuffer>& registerValue,
QWidget* parent
)
: QWidget(parent)
, registerDescriptor(registerDescriptor)
, insightWorker(insightWorker)
, registerValue(registerValue.value_or(Targets::TargetMemoryBuffer(registerDescriptor.size, 0)))
{
this->setWindowFlag(Qt::Window);
auto registerName = QString::fromStdString(this->registerDescriptor.name.value()).toUpper();
this->setObjectName("target-register-inspector-window");
this->setWindowTitle("Inspect Register");
auto windowStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/Stylesheets/TargetRegisterInspectorWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegisterInspectorWindow UI file");
}
if (!windowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegisterInspectorWindow stylesheet file");
}
auto windowSize = QSize(
840,
static_cast<int>(
440
+ ((BitsetWidget::HEIGHT + 20) * std::ceil(static_cast<float>(this->registerValue.size()) / 2))
+ (!this->registerDescriptor.writable ? 50 : 0)
)
);
auto containerMargins = QMargins(15, 30, 15, 10);
this->setStyleSheet(windowStylesheet.readAll());
this->setFixedSize(windowSize);
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(containerMargins);
this->registerNameLabel = this->container->findChild<QLabel*>("register-name");
this->registerDescriptionLabel = this->container->findChild<QLabel*>("register-description");
this->contentContainer = this->container->findChild<QWidget*>("content-container");
this->registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
this->registerValueTextInput = this->container->findChild<QLineEdit*>("register-value-text-input");
this->registerValueBitsetWidgetContainer = this->container->findChild<QWidget*>("register-value-bitset-widget-container");
this->refreshValueButton = this->container->findChild<QPushButton*>("refresh-value-btn");
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
this->closeButton = this->container->findChild<QPushButton*>("close-btn");
this->registerNameLabel->setText(registerName);
if (this->registerDescriptor.description.has_value()) {
this->registerDescriptionLabel->setText(QString::fromStdString(this->registerDescriptor.description.value()));
this->registerDescriptionLabel->setVisible(true);
}
this->registerHistoryWidget = new RegisterHistoryWidget(
this->registerDescriptor,
this->registerValue,
insightWorker,
this->container
);
auto* contentLayout = this->container->findChild<QHBoxLayout*>("content-layout");
contentLayout->insertWidget(0, this->registerHistoryWidget, 0, Qt::AlignmentFlag::AlignTop);
auto* registerDetailsContainer = this->container->findChild<QWidget*>("register-details-container");
auto* registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
registerValueContainer->setContentsMargins(15, 15, 15, 15);
registerDetailsContainer->setContentsMargins(15, 15, 15, 15);
auto* registerDetailsNameInput = registerDetailsContainer->findChild<QLineEdit*>("register-details-name-input");
auto* registerDetailsSizeInput = registerDetailsContainer->findChild<QLineEdit*>("register-details-size-input");
auto* registerDetailsStartAddressInput = registerDetailsContainer->findChild<QLineEdit*>(
"register-details-start-address-input"
);
registerDetailsNameInput->setText(registerName);
registerDetailsSizeInput->setText(QString::number(this->registerDescriptor.size));
registerDetailsStartAddressInput->setText(
"0x" + QString::number(this->registerDescriptor.startAddress.value(), 16).toUpper()
);
this->registerValueTextInput->setFixedWidth(BitsetWidget::WIDTH * 2);
if (!this->registerDescriptor.writable) {
this->registerValueTextInput->setDisabled(true);
this->applyButton->setVisible(false);
auto* readOnlyIndicatorLabel = this->registerValueContainer->findChild<QLabel*>("read-only-indicator-label");
readOnlyIndicatorLabel->show();
}
auto* registerBitsetWidgetLayout = this->registerValueBitsetWidgetContainer->findChild<QVBoxLayout*>(
"register-value-bitset-widget-layout"
);
/*
* Each row of the BitsetWidget container should hold two BitsetWidgets. So we have a horizontal layout nested
* within a vertical layout.
*/
auto* bitsetSingleHorizontalLayout = new QHBoxLayout();
bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING);
bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0);
// The register value will be in MSB, which is OK for us as we present the bit widgets in MSB.
auto byteNumber = static_cast<int>(this->registerValue.size() - 1);
for (std::uint32_t registerByteIndex = 0; registerByteIndex < this->registerValue.size(); registerByteIndex++) {
auto* bitsetWidget = new BitsetWidget(
byteNumber,
this->registerValue.at(registerByteIndex),
!this->registerDescriptor.writable,
this
auto windowUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/UiFiles/"
"TargetRegisterInspectorWindow.ui"
)
);
bitsetSingleHorizontalLayout->addWidget(bitsetWidget, 0, Qt::AlignmentFlag::AlignLeft);
auto windowStylesheet = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegisterInspector/Stylesheets/"
"TargetRegisterInspectorWindow.qss"
)
);
if (!windowUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegisterInspectorWindow UI file");
}
if (!windowStylesheet.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegisterInspectorWindow stylesheet file");
}
auto windowSize = QSize(
840,
static_cast<int>(
440
+ ((BitsetWidget::HEIGHT + 20) * std::ceil(static_cast<float>(this->registerValue.size()) / 2))
+ (!this->registerDescriptor.writable ? 50 : 0)
)
);
auto containerMargins = QMargins(15, 30, 15, 10);
this->setStyleSheet(windowStylesheet.readAll());
this->setFixedSize(windowSize);
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&windowUiFile, this);
this->container->setFixedSize(this->size());
this->container->setContentsMargins(containerMargins);
this->registerNameLabel = this->container->findChild<QLabel*>("register-name");
this->registerDescriptionLabel = this->container->findChild<QLabel*>("register-description");
this->contentContainer = this->container->findChild<QWidget*>("content-container");
this->registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
this->registerValueTextInput = this->container->findChild<QLineEdit*>("register-value-text-input");
this->registerValueBitsetWidgetContainer = this->container->findChild<QWidget*>(
"register-value-bitset-widget-container"
);
this->refreshValueButton = this->container->findChild<QPushButton*>("refresh-value-btn");
this->applyButton = this->container->findChild<QPushButton*>("apply-btn");
this->helpButton = this->container->findChild<QPushButton*>("help-btn");
this->closeButton = this->container->findChild<QPushButton*>("close-btn");
this->registerNameLabel->setText(registerName);
if (this->registerDescriptor.description.has_value()) {
this->registerDescriptionLabel->setText(
QString::fromStdString(this->registerDescriptor.description.value())
);
this->registerDescriptionLabel->setVisible(true);
}
this->registerHistoryWidget = new RegisterHistoryWidget(
this->registerDescriptor,
this->registerValue,
insightWorker,
this->container
);
auto* contentLayout = this->container->findChild<QHBoxLayout*>("content-layout");
contentLayout->insertWidget(0, this->registerHistoryWidget, 0, Qt::AlignmentFlag::AlignTop);
auto* registerDetailsContainer = this->container->findChild<QWidget*>("register-details-container");
auto* registerValueContainer = this->container->findChild<QWidget*>("register-value-container");
registerValueContainer->setContentsMargins(15, 15, 15, 15);
registerDetailsContainer->setContentsMargins(15, 15, 15, 15);
auto* registerDetailsNameInput = registerDetailsContainer->findChild<QLineEdit*>(
"register-details-name-input"
);
auto* registerDetailsSizeInput = registerDetailsContainer->findChild<QLineEdit*>(
"register-details-size-input"
);
auto* registerDetailsStartAddressInput = registerDetailsContainer->findChild<QLineEdit*>(
"register-details-start-address-input"
);
registerDetailsNameInput->setText(registerName);
registerDetailsSizeInput->setText(QString::number(this->registerDescriptor.size));
registerDetailsStartAddressInput->setText(
"0x" + QString::number(this->registerDescriptor.startAddress.value(), 16).toUpper()
);
this->registerValueTextInput->setFixedWidth(BitsetWidget::WIDTH * 2);
if (!this->registerDescriptor.writable) {
this->registerValueTextInput->setDisabled(true);
this->applyButton->setVisible(false);
auto* readOnlyIndicatorLabel = this->registerValueContainer->findChild<QLabel*>(
"read-only-indicator-label"
);
readOnlyIndicatorLabel->show();
}
auto* registerBitsetWidgetLayout = this->registerValueBitsetWidgetContainer->findChild<QVBoxLayout*>(
"register-value-bitset-widget-layout"
);
/*
* Each row of the BitsetWidget container should hold two BitsetWidgets. So we have a horizontal layout nested
* within a vertical layout.
*/
auto* bitsetSingleHorizontalLayout = new QHBoxLayout();
bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING);
bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0);
// The register value will be in MSB, which is OK for us as we present the bit widgets in MSB.
auto byteNumber = static_cast<int>(this->registerValue.size() - 1);
for (std::uint32_t registerByteIndex = 0; registerByteIndex < this->registerValue.size(); registerByteIndex++) {
auto* bitsetWidget = new BitsetWidget(
byteNumber,
this->registerValue.at(registerByteIndex),
!this->registerDescriptor.writable,
this
);
bitsetSingleHorizontalLayout->addWidget(bitsetWidget, 0, Qt::AlignmentFlag::AlignLeft);
QObject::connect(
bitsetWidget,
&BitsetWidget::byteChanged,
this,
&TargetRegisterInspectorWindow::updateRegisterValueInputField
);
this->bitsetWidgets.push_back(bitsetWidget);
if (((registerByteIndex + 1) % 2) == 0) {
registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout);
bitsetSingleHorizontalLayout = new QHBoxLayout();
bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING);
bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0);
}
byteNumber--;
}
registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout);
registerBitsetWidgetLayout->addStretch(1);
this->registerHistoryWidget->setFixedHeight(this->contentContainer->sizeHint().height());
QObject::connect(this->helpButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::openHelpPage);
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
QObject::connect(
bitsetWidget,
&BitsetWidget::byteChanged,
this->refreshValueButton,
&QPushButton::clicked,
this,
&TargetRegisterInspectorWindow::updateRegisterValueInputField
&TargetRegisterInspectorWindow::refreshRegisterValue
);
this->bitsetWidgets.push_back(bitsetWidget);
QObject::connect(this->applyButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::applyChanges);
if (((registerByteIndex + 1) % 2) == 0) {
registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout);
bitsetSingleHorizontalLayout = new QHBoxLayout();
bitsetSingleHorizontalLayout->setSpacing(BitWidget::SPACING);
bitsetSingleHorizontalLayout->setContentsMargins(0, 0, 0, 0);
QObject::connect(
this->registerHistoryWidget,
&RegisterHistoryWidget::historyItemSelected,
this,
&TargetRegisterInspectorWindow::onHistoryItemSelected
);
QObject::connect(
this->registerValueTextInput,
&QLineEdit::textEdited,
this,
&TargetRegisterInspectorWindow::onValueTextInputChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&TargetRegisterInspectorWindow::onTargetStateChanged
);
this->updateRegisterValueInputField();
this->onTargetStateChanged(currentTargetState);
// Position the inspection window at the center of the main Insight window
this->move(parent->window()->geometry().center() - this->rect().center());
this->show();
}
bool TargetRegisterInspectorWindow::registerSupported(const Targets::TargetRegisterDescriptor& descriptor) {
return (descriptor.size > 0 && descriptor.size <= 8);
}
void TargetRegisterInspectorWindow::setValue(const Targets::TargetMemoryBuffer& registerValue) {
this->registerValue = registerValue;
this->registerHistoryWidget->updateCurrentItemValue(this->registerValue);
this->registerHistoryWidget->selectCurrentItem();
}
void TargetRegisterInspectorWindow::onValueTextInputChanged(QString text) {
if (text.isEmpty()) {
text = "0";
}
byteNumber--;
}
bool validHexValue = false;
text.toLongLong(&validHexValue, 16);
if (!validHexValue) {
return;
}
registerBitsetWidgetLayout->addLayout(bitsetSingleHorizontalLayout);
registerBitsetWidgetLayout->addStretch(1);
auto registerSize = this->registerDescriptor.size;
auto newValue = QByteArray::fromHex(
text.remove("0x", Qt::CaseInsensitive).toLatin1()
).rightJustified(registerSize, 0).right(registerSize);
this->registerHistoryWidget->setFixedHeight(this->contentContainer->sizeHint().height());
assert(newValue.size() >= registerSize);
assert(registerValue.size() == registerSize);
for (std::uint32_t byteIndex = 0; byteIndex < registerSize; byteIndex++) {
this->registerValue.at(byteIndex) = static_cast<unsigned char>(newValue.at(byteIndex));
}
QObject::connect(this->helpButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::openHelpPage);
QObject::connect(this->closeButton, &QPushButton::clicked, this, &QWidget::close);
QObject::connect(
this->refreshValueButton,
&QPushButton::clicked,
this,
&TargetRegisterInspectorWindow::refreshRegisterValue
);
QObject::connect(this->applyButton, &QPushButton::clicked, this, &TargetRegisterInspectorWindow::applyChanges);
QObject::connect(
this->registerHistoryWidget,
&RegisterHistoryWidget::historyItemSelected,
this,
&TargetRegisterInspectorWindow::onHistoryItemSelected
);
QObject::connect(
this->registerValueTextInput,
&QLineEdit::textEdited,
this,
&TargetRegisterInspectorWindow::onValueTextInputChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&TargetRegisterInspectorWindow::onTargetStateChanged
);
this->updateRegisterValueInputField();
this->onTargetStateChanged(currentTargetState);
// Position the inspection window at the center of the main Insight window
this->move(parent->window()->geometry().center() - this->rect().center());
this->show();
}
bool TargetRegisterInspectorWindow::registerSupported(const Targets::TargetRegisterDescriptor& descriptor) {
return (descriptor.size > 0 && descriptor.size <= 8);
}
void TargetRegisterInspectorWindow::setValue(const Targets::TargetMemoryBuffer& registerValue) {
this->registerValue = registerValue;
this->registerHistoryWidget->updateCurrentItemValue(this->registerValue);
this->registerHistoryWidget->selectCurrentItem();
}
void TargetRegisterInspectorWindow::onValueTextInputChanged(QString text) {
if (text.isEmpty()) {
text = "0";
}
bool validHexValue = false;
text.toLongLong(&validHexValue, 16);
if (!validHexValue) {
return;
}
auto registerSize = this->registerDescriptor.size;
auto newValue = QByteArray::fromHex(
text.remove("0x", Qt::CaseInsensitive).toLatin1()
).rightJustified(registerSize, 0).right(registerSize);
assert(newValue.size() >= registerSize);
assert(registerValue.size() == registerSize);
for (std::uint32_t byteIndex = 0; byteIndex < registerSize; byteIndex++) {
this->registerValue.at(byteIndex) = static_cast<unsigned char>(newValue.at(byteIndex));
}
for (auto& bitsetWidget : this->bitsetWidgets) {
bitsetWidget->updateValue();
}
}
void TargetRegisterInspectorWindow::onTargetStateChanged(TargetState newState) {
if (newState != TargetState::STOPPED) {
this->registerValueTextInput->setDisabled(true);
this->registerValueBitsetWidgetContainer->setDisabled(true);
this->applyButton->setDisabled(true);
this->refreshValueButton->setDisabled(true);
} else if (this->targetState != TargetState::STOPPED && this->registerValueContainer->isEnabled()) {
this->registerValueBitsetWidgetContainer->setDisabled(false);
this->refreshValueButton->setDisabled(false);
if (this->registerDescriptor.writable) {
this->registerValueTextInput->setDisabled(false);
this->applyButton->setDisabled(false);
for (auto& bitsetWidget : this->bitsetWidgets) {
bitsetWidget->updateValue();
}
}
this->targetState = newState;
}
void TargetRegisterInspectorWindow::onTargetStateChanged(TargetState newState) {
if (newState != TargetState::STOPPED) {
this->registerValueTextInput->setDisabled(true);
this->registerValueBitsetWidgetContainer->setDisabled(true);
this->applyButton->setDisabled(true);
this->refreshValueButton->setDisabled(true);
void TargetRegisterInspectorWindow::onHistoryItemSelected(const Targets::TargetMemoryBuffer& selectedRegisterValue) {
this->registerValue = selectedRegisterValue;
this->updateValue();
} else if (this->targetState != TargetState::STOPPED && this->registerValueContainer->isEnabled()) {
this->registerValueBitsetWidgetContainer->setDisabled(false);
this->refreshValueButton->setDisabled(false);
if (this->registerHistoryWidget->isCurrentItemSelected()) {
this->refreshValueButton->setVisible(true);
} else {
this->refreshValueButton->setVisible(false);
}
}
void TargetRegisterInspectorWindow::updateRegisterValueInputField() {
auto value = QByteArray(
reinterpret_cast<const char*>(this->registerValue.data()),
static_cast<qsizetype>(this->registerValue.size())
);
this->registerValueTextInput->setText("0x" + QString(value.toHex()).toUpper());
}
void TargetRegisterInspectorWindow::updateRegisterValueBitsetWidgets() {
for (auto& bitsetWidget : this->bitsetWidgets) {
bitsetWidget->updateValue();
}
}
void TargetRegisterInspectorWindow::updateValue() {
this->updateRegisterValueInputField();
this->updateRegisterValueBitsetWidgets();
}
void TargetRegisterInspectorWindow::refreshRegisterValue() {
this->registerValueContainer->setDisabled(true);
auto* readTargetRegisterTask = new ReadTargetRegisters({this->registerDescriptor});
QObject::connect(
readTargetRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
[this] (Targets::TargetRegisters targetRegisters) {
this->registerValueContainer->setDisabled(false);
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor == this->registerDescriptor) {
this->setValue(targetRegister.value);
}
if (this->registerDescriptor.writable) {
this->registerValueTextInput->setDisabled(false);
this->applyButton->setDisabled(false);
}
}
);
QObject::connect(
readTargetRegisterTask,
&InsightWorkerTask::failed,
this,
[this] {
this->registerValueContainer->setDisabled(false);
this->targetState = newState;
}
void TargetRegisterInspectorWindow::onHistoryItemSelected(
const Targets::TargetMemoryBuffer& selectedRegisterValue
) {
this->registerValue = selectedRegisterValue;
this->updateValue();
if (this->registerHistoryWidget->isCurrentItemSelected()) {
this->refreshValueButton->setVisible(true);
} else {
this->refreshValueButton->setVisible(false);
}
);
}
this->insightWorker.queueTask(readTargetRegisterTask);
}
void TargetRegisterInspectorWindow::applyChanges() {
this->registerValueContainer->setDisabled(true);
const auto targetRegister = Targets::TargetRegister(this->registerDescriptor, this->registerValue);
auto* writeRegisterTask = new WriteTargetRegister(targetRegister);
QObject::connect(writeRegisterTask, &InsightWorkerTask::completed, this, [this, targetRegister] {
this->registerValueContainer->setDisabled(false);
emit this->insightWorker.targetRegistersWritten(
{targetRegister},
DateTime::currentDateTime()
void TargetRegisterInspectorWindow::updateRegisterValueInputField() {
auto value = QByteArray(
reinterpret_cast<const char*>(this->registerValue.data()),
static_cast<qsizetype>(this->registerValue.size())
);
this->registerHistoryWidget->updateCurrentItemValue(targetRegister.value);
this->registerHistoryWidget->selectCurrentItem();
});
QObject::connect(writeRegisterTask, &InsightWorkerTask::failed, this, [this] (QString errorMessage) {
this->registerValueContainer->setDisabled(false);
auto* errorDialogue = new ErrorDialogue(
"Error",
"Failed to update " + QString::fromStdString(
this->registerDescriptor.name.value_or("")
).toUpper() + " register value - " + errorMessage,
this
this->registerValueTextInput->setText("0x" + QString(value.toHex()).toUpper());
}
void TargetRegisterInspectorWindow::updateRegisterValueBitsetWidgets() {
for (auto& bitsetWidget : this->bitsetWidgets) {
bitsetWidget->updateValue();
}
}
void TargetRegisterInspectorWindow::updateValue() {
this->updateRegisterValueInputField();
this->updateRegisterValueBitsetWidgets();
}
void TargetRegisterInspectorWindow::refreshRegisterValue() {
this->registerValueContainer->setDisabled(true);
auto* readTargetRegisterTask = new ReadTargetRegisters({this->registerDescriptor});
QObject::connect(
readTargetRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
[this] (Targets::TargetRegisters targetRegisters) {
this->registerValueContainer->setDisabled(false);
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor == this->registerDescriptor) {
this->setValue(targetRegister.value);
}
}
}
);
errorDialogue->show();
});
this->insightWorker.queueTask(writeRegisterTask);
}
QObject::connect(
readTargetRegisterTask,
&InsightWorkerTask::failed,
this,
[this] {
this->registerValueContainer->setDisabled(false);
}
);
void TargetRegisterInspectorWindow::openHelpPage() {
QDesktopServices::openUrl(QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/register-inspection")));
this->insightWorker.queueTask(readTargetRegisterTask);
}
void TargetRegisterInspectorWindow::applyChanges() {
this->registerValueContainer->setDisabled(true);
const auto targetRegister = Targets::TargetRegister(
this->registerDescriptor,
this->registerValue
);
auto* writeRegisterTask = new WriteTargetRegister(targetRegister);
QObject::connect(writeRegisterTask, &InsightWorkerTask::completed, this, [this, targetRegister] {
this->registerValueContainer->setDisabled(false);
emit this->insightWorker.targetRegistersWritten(
{targetRegister},
DateTime::currentDateTime()
);
this->registerHistoryWidget->updateCurrentItemValue(targetRegister.value);
this->registerHistoryWidget->selectCurrentItem();
});
QObject::connect(writeRegisterTask, &InsightWorkerTask::failed, this, [this] (QString errorMessage) {
this->registerValueContainer->setDisabled(false);
auto* errorDialogue = new ErrorDialogue(
"Error",
"Failed to update " + QString::fromStdString(
this->registerDescriptor.name.value_or("")
).toUpper() + " register value - " + errorMessage,
this
);
errorDialogue->show();
});
this->insightWorker.queueTask(writeRegisterTask);
}
void TargetRegisterInspectorWindow::openHelpPage() {
QDesktopServices::openUrl(
QUrl(QString::fromStdString(Paths::homeDomainName() + "/docs/register-inspection"))
);
}
}

View File

@@ -29,7 +29,7 @@ namespace Bloom::Widgets
const Targets::TargetRegisterDescriptor& registerDescriptor,
InsightWorker& insightWorker,
Targets::TargetState currentTargetState,
std::optional<Targets::TargetMemoryBuffer> registerValue = std::nullopt,
const std::optional<Targets::TargetMemoryBuffer>& registerValue = std::nullopt,
QWidget* parent = nullptr
);

View File

@@ -2,27 +2,28 @@
#include <QStyle>
using namespace Bloom::Widgets;
namespace Bloom::Widgets
{
ItemWidget::ItemWidget(QWidget* parent): ClickableWidget(parent) {
auto onClick = [this] {
this->setSelected(true);
};
ItemWidget::ItemWidget(QWidget* parent): ClickableWidget(parent) {
auto onClick = [this] {
this->setSelected(true);
};
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
QObject::connect(this, &ClickableWidget::clicked, this, onClick);
QObject::connect(this, &ClickableWidget::rightClicked, this, onClick);
this->setSelected(false);
}
void ItemWidget::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
if (selected) {
emit this->selected(this);
this->setSelected(false);
}
this->postSetSelected(selected);
void ItemWidget::setSelected(bool selected) {
this->setProperty("selected", selected);
this->style()->unpolish(this);
this->style()->polish(this);
if (selected) {
emit this->selected(this);
}
this->postSetSelected(selected);
}
}

View File

@@ -8,143 +8,145 @@
#include "src/Helpers/Paths.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
namespace Bloom::Widgets
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptor;
RegisterGroupWidget::RegisterGroupWidget(
QString name,
const std::set<TargetRegisterDescriptor>& registerDescriptors,
InsightWorker& insightWorker,
TargetRegistersPaneWidget* parent
): ItemWidget(parent), name(std::move(name)) {
this->setObjectName(this->name);
RegisterGroupWidget::RegisterGroupWidget(
QString name,
const std::set<TargetRegisterDescriptor>& registerDescriptors,
InsightWorker& insightWorker,
TargetRegistersPaneWidget* parent
): ItemWidget(parent), name(std::move(name)) {
this->setObjectName(this->name);
this->headerWidget->setObjectName("register-group-header");
this->headerWidget->setFixedHeight(25);
auto* headerLayout = new QHBoxLayout(this->headerWidget);
headerLayout->setContentsMargins(5, 0, 0, 0);
this->headerWidget->setObjectName("register-group-header");
this->headerWidget->setFixedHeight(25);
auto* headerLayout = new QHBoxLayout(this->headerWidget);
headerLayout->setContentsMargins(5, 0, 0, 0);
this->label->setText(this->name);
this->label->setText(this->name);
this->arrowIcon->setObjectName("arrow-icon");
auto static arrowIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg"
);
this->arrowIcon->setSvgFilePath(arrowIconPath);
this->arrowIcon->setContainerHeight(15);
this->arrowIcon->setContainerWidth(14);
this->arrowIcon->setObjectName("arrow-icon");
auto static arrowIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/arrow.svg"
);
this->arrowIcon->setSvgFilePath(arrowIconPath);
this->arrowIcon->setContainerHeight(15);
this->arrowIcon->setContainerWidth(14);
this->registerGroupIcon->setObjectName("register-group-icon");
auto static registerIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg"
);
this->registerGroupIcon->setSvgFilePath(registerIconPath);
this->registerGroupIcon->setContainerHeight(15);
this->registerGroupIcon->setContainerWidth(15);
this->registerGroupIcon->setObjectName("register-group-icon");
auto static registerIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register-group.svg"
);
this->registerGroupIcon->setSvgFilePath(registerIconPath);
this->registerGroupIcon->setContainerHeight(15);
this->registerGroupIcon->setContainerWidth(15);
headerLayout->addWidget(this->arrowIcon);
headerLayout->addWidget(this->registerGroupIcon);
headerLayout->addWidget(this->label);
headerLayout->addWidget(this->arrowIcon);
headerLayout->addWidget(this->registerGroupIcon);
headerLayout->addWidget(this->label);
auto* bodyLayout = new QVBoxLayout(this->bodyWidget);
bodyLayout->setContentsMargins(0, 0,0,0);
bodyLayout->setSpacing(0);
auto* bodyLayout = new QVBoxLayout(this->bodyWidget);
bodyLayout->setContentsMargins(0, 0,0,0);
bodyLayout->setSpacing(0);
for (const auto& descriptor : registerDescriptors) {
if (!descriptor.name.has_value()) {
continue;
for (const auto& descriptor : registerDescriptors) {
if (!descriptor.name.has_value()) {
continue;
}
if (!descriptor.readable) {
continue;
}
auto* registerWidget = new RegisterWidget(descriptor, insightWorker, this->bodyWidget);
bodyLayout->addWidget(registerWidget, 0, Qt::AlignmentFlag::AlignTop);
QObject::connect(
registerWidget,
&ItemWidget::selected,
parent,
&TargetRegistersPaneWidget::onItemSelectionChange
);
this->registerWidgets.insert(registerWidget);
this->registerWidgetsMappedByDescriptor.insert(std::pair(descriptor, registerWidget));
}
if (!descriptor.readable) {
continue;
}
bodyLayout->addStretch(1);
auto* registerWidget = new RegisterWidget(descriptor, insightWorker, this->bodyWidget);
bodyLayout->addWidget(registerWidget, 0, Qt::AlignmentFlag::AlignTop);
this->layout->setContentsMargins(0,0,0,0);
this->layout->setSpacing(0);
this->layout->addWidget(this->headerWidget, 0, Qt::AlignmentFlag::AlignTop);
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignTop);
this->layout->addStretch(1);
this->collapse();
QObject::connect(this->headerWidget, &ClickableWidget::doubleClicked, [this] {
if (this->collapsed) {
this->expand();
} else {
this->collapse();
}
});
QObject::connect(
registerWidget,
this->headerWidget,
&ItemWidget::selected,
parent,
&TargetRegistersPaneWidget::onItemSelectionChange
);
this->registerWidgets.insert(registerWidget);
this->registerWidgetsMappedByDescriptor.insert(std::pair(descriptor, registerWidget));
}
bodyLayout->addStretch(1);
void RegisterGroupWidget::collapse() {
this->arrowIcon->setAngle(0);
this->bodyWidget->setVisible(false);
this->collapsed = true;
}
this->layout->setContentsMargins(0,0,0,0);
this->layout->setSpacing(0);
this->layout->addWidget(this->headerWidget, 0, Qt::AlignmentFlag::AlignTop);
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignTop);
this->layout->addStretch(1);
void RegisterGroupWidget::expand() {
this->arrowIcon->setAngle(90);
this->bodyWidget->setVisible(true);
this->collapsed = false;
}
this->collapse();
void RegisterGroupWidget::setAllRegistersVisible(bool visible) {
for (const auto& registerWidget : this->registerWidgets) {
registerWidget->setVisible(visible);
}
}
QObject::connect(this->headerWidget, &ClickableWidget::doubleClicked, [this] {
if (this->collapsed) {
this->expand();
void RegisterGroupWidget::filterRegisters(const QString& keyword) {
int matchingWidgetCount = 0;
for (const auto& registerWidget : this->registerWidgets) {
if (keyword.isEmpty() || (registerWidget->searchKeywords.contains(keyword, Qt::CaseInsensitive))) {
matchingWidgetCount++;
registerWidget->setVisible(true);
} else {
} else {
registerWidget->setVisible(false);
}
}
if (matchingWidgetCount == 0) {
this->collapse();
}
});
QObject::connect(
this->headerWidget,
&ItemWidget::selected,
parent,
&TargetRegistersPaneWidget::onItemSelectionChange
);
}
void RegisterGroupWidget::collapse() {
this->arrowIcon->setAngle(0);
this->bodyWidget->setVisible(false);
this->collapsed = true;
}
void RegisterGroupWidget::expand() {
this->arrowIcon->setAngle(90);
this->bodyWidget->setVisible(true);
this->collapsed = false;
}
void RegisterGroupWidget::setAllRegistersVisible(bool visible) {
for (const auto& registerWidget : this->registerWidgets) {
registerWidget->setVisible(visible);
}
}
void RegisterGroupWidget::filterRegisters(const QString& keyword) {
int matchingWidgetCount = 0;
for (const auto& registerWidget : this->registerWidgets) {
if (keyword.isEmpty() || (registerWidget->searchKeywords.contains(keyword, Qt::CaseInsensitive))) {
matchingWidgetCount++;
registerWidget->setVisible(true);
this->setVisible(false);
} else {
registerWidget->setVisible(false);
}
}
this->setVisible(true);
if (!keyword.isEmpty()) {
this->expand();
if (matchingWidgetCount == 0) {
this->collapse();
this->setVisible(false);
} else {
this->setVisible(true);
if (!keyword.isEmpty()) {
this->expand();
} else {
this->collapse();
} else {
this->collapse();
}
}
}
}

View File

@@ -9,226 +9,229 @@
#include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetRegisterDescriptor;
RegisterWidget::RegisterWidget(
TargetRegisterDescriptor descriptor,
InsightWorker& insightWorker,
QWidget *parent
)
: ItemWidget(parent)
, descriptor(std::move(descriptor))
, searchKeywords(QString::fromStdString(
this->descriptor.name.value_or("") + this->descriptor.description.value_or("")
).toLower())
, insightWorker(insightWorker)
namespace Bloom::Widgets
{
this->setObjectName("register-item");
this->setFixedHeight(25);
using namespace Bloom::Exceptions;
this->nameLabel->setText(QString::fromStdString(this->descriptor.name.value()).toUpper());
this->valueLabel->setObjectName("value");
using Bloom::Targets::TargetRegisterDescriptor;
this->registerIcon->setObjectName("register-icon");
auto static registerIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg"
);
this->registerIcon->setSvgFilePath(registerIconPath);
this->registerIcon->setContainerHeight(15);
this->registerIcon->setContainerWidth(15);
RegisterWidget::RegisterWidget(
TargetRegisterDescriptor descriptor,
InsightWorker& insightWorker,
QWidget *parent
)
: ItemWidget(parent)
, descriptor(std::move(descriptor))
, searchKeywords(QString::fromStdString(
this->descriptor.name.value_or("") + this->descriptor.description.value_or("")
).toLower())
, insightWorker(insightWorker)
{
this->setObjectName("register-item");
this->setFixedHeight(25);
this->layout->setContentsMargins(47, 0, 0, 0);
this->layout->setSpacing(0);
this->layout->addWidget(this->registerIcon);
this->layout->addSpacing(7);
this->layout->addWidget(this->nameLabel);
this->layout->addSpacing(5);
this->layout->addWidget(this->valueLabel);
this->layout->addStretch(1);
this->nameLabel->setText(QString::fromStdString(this->descriptor.name.value()).toUpper());
this->valueLabel->setObjectName("value");
QObject::connect(this, &ClickableWidget::doubleClicked, this, &RegisterWidget::openInspectionWindow);
QObject::connect(this->openInspectionWindowAction, &QAction::triggered, this, &RegisterWidget::openInspectionWindow);
QObject::connect(this->refreshValueAction, &QAction::triggered, this, &RegisterWidget::refreshValue);
QObject::connect(this->copyValueNameAction, &QAction::triggered, this, &RegisterWidget::copyName);
QObject::connect(this->copyValueHexAction, &QAction::triggered, this, &RegisterWidget::copyValueHex);
QObject::connect(this->copyValueDecimalAction, &QAction::triggered, this, &RegisterWidget::copyValueDecimal);
QObject::connect(this->copyValueBinaryAction, &QAction::triggered, this, &RegisterWidget::copyValueBinary);
this->registerIcon->setObjectName("register-icon");
auto static registerIconPath = QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/Images/register.svg"
);
this->registerIcon->setSvgFilePath(registerIconPath);
this->registerIcon->setContainerHeight(15);
this->registerIcon->setContainerWidth(15);
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetStateUpdated,
this,
&RegisterWidget::onTargetStateChange
);
}
this->layout->setContentsMargins(47, 0, 0, 0);
this->layout->setSpacing(0);
this->layout->addWidget(this->registerIcon);
this->layout->addSpacing(7);
this->layout->addWidget(this->nameLabel);
this->layout->addSpacing(5);
this->layout->addWidget(this->valueLabel);
this->layout->addStretch(1);
void RegisterWidget::setRegisterValue(const Targets::TargetRegister& targetRegister) {
const auto valueChanged = this->currentRegister.has_value()
&& this->currentRegister.value().value != targetRegister.value;
this->currentRegister = targetRegister;
QObject::connect(this, &ClickableWidget::doubleClicked, this, &RegisterWidget::openInspectionWindow);
QObject::connect(this->openInspectionWindowAction, &QAction::triggered, this, &RegisterWidget::openInspectionWindow);
QObject::connect(this->refreshValueAction, &QAction::triggered, this, &RegisterWidget::refreshValue);
QObject::connect(this->copyValueNameAction, &QAction::triggered, this, &RegisterWidget::copyName);
QObject::connect(this->copyValueHexAction, &QAction::triggered, this, &RegisterWidget::copyValueHex);
QObject::connect(this->copyValueDecimalAction, &QAction::triggered, this, &RegisterWidget::copyValueDecimal);
QObject::connect(this->copyValueBinaryAction, &QAction::triggered, this, &RegisterWidget::copyValueBinary);
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(targetRegister.value.data()),
static_cast<qsizetype>(targetRegister.value.size())
);
auto hexValueByteArray = valueByteArray.toHex();
auto registerValue = ": 0x" + QString(hexValueByteArray).toUpper()
+ " | " + QString::number(hexValueByteArray.toUInt(nullptr, 16));
if (targetRegister.value.size() == 1 && targetRegister.value[0] >= 32 && targetRegister.value[0] <= 126) {
registerValue += " | '" + QString(valueByteArray) + "'";
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetStateUpdated,
this,
&RegisterWidget::onTargetStateChange
);
}
this->valueLabel->setProperty("changed", valueChanged);
this->valueLabel->style()->unpolish(this->valueLabel);
this->valueLabel->style()->polish(this->valueLabel);
void RegisterWidget::setRegisterValue(const Targets::TargetRegister& targetRegister) {
const auto valueChanged = this->currentRegister.has_value()
&& this->currentRegister.value().value != targetRegister.value;
this->currentRegister = targetRegister;
this->valueLabel->setText(registerValue);
if (this->inspectWindow != nullptr) {
this->inspectWindow->setValue(targetRegister.value);
}
}
void RegisterWidget::clearInlineValue() {
this->valueLabel->clear();
}
void RegisterWidget::contextMenuEvent(QContextMenuEvent* event) {
this->setSelected(true);
auto* menu = new QMenu(this);
menu->addAction(this->openInspectionWindowAction);
menu->addAction(this->refreshValueAction);
menu->addSeparator();
auto* copyMenu = new QMenu("Copy", this);
copyMenu->addAction(this->copyValueNameAction);
copyMenu->addSeparator();
copyMenu->addAction(this->copyValueDecimalAction);
copyMenu->addAction(this->copyValueHexAction);
copyMenu->addAction(this->copyValueBinaryAction);
menu->addMenu(copyMenu);
this->openInspectionWindowAction->setEnabled(TargetRegisterInspectorWindow::registerSupported(this->descriptor));
const auto targetStopped = this->targetState == Targets::TargetState::STOPPED;
const auto targetStoppedAndValuePresent = targetStopped && this->currentRegister.has_value();
this->refreshValueAction->setEnabled(targetStopped);
this->copyValueDecimalAction->setEnabled(targetStoppedAndValuePresent);
this->copyValueHexAction->setEnabled(targetStoppedAndValuePresent);
this->copyValueBinaryAction->setEnabled(targetStoppedAndValuePresent);
menu->exec(event->globalPos());
}
void RegisterWidget::openInspectionWindow() {
if (!TargetRegisterInspectorWindow::registerSupported(this->descriptor)) {
return;
}
if (this->inspectWindow == nullptr) {
this->inspectWindow = new TargetRegisterInspectorWindow(
this->descriptor,
this->insightWorker,
this->targetState,
this->currentRegister.has_value() ? std::optional(this->currentRegister->value) : std::nullopt,
this
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(targetRegister.value.data()),
static_cast<qsizetype>(targetRegister.value.size())
);
} else {
if (this->currentRegister.has_value()) {
this->inspectWindow->setValue(this->currentRegister->value);
auto hexValueByteArray = valueByteArray.toHex();
auto registerValue = ": 0x" + QString(hexValueByteArray).toUpper()
+ " | " + QString::number(hexValueByteArray.toUInt(nullptr, 16));
if (targetRegister.value.size() == 1 && targetRegister.value[0] >= 32 && targetRegister.value[0] <= 126) {
registerValue += " | '" + QString(valueByteArray) + "'";
}
if (!this->inspectWindow->isVisible()) {
this->inspectWindow->show();
this->valueLabel->setProperty("changed", valueChanged);
this->valueLabel->style()->unpolish(this->valueLabel);
this->valueLabel->style()->polish(this->valueLabel);
this->valueLabel->setText(registerValue);
if (this->inspectWindow != nullptr) {
this->inspectWindow->setValue(targetRegister.value);
}
}
void RegisterWidget::clearInlineValue() {
this->valueLabel->clear();
}
void RegisterWidget::contextMenuEvent(QContextMenuEvent* event) {
this->setSelected(true);
auto* menu = new QMenu(this);
menu->addAction(this->openInspectionWindowAction);
menu->addAction(this->refreshValueAction);
menu->addSeparator();
auto* copyMenu = new QMenu("Copy", this);
copyMenu->addAction(this->copyValueNameAction);
copyMenu->addSeparator();
copyMenu->addAction(this->copyValueDecimalAction);
copyMenu->addAction(this->copyValueHexAction);
copyMenu->addAction(this->copyValueBinaryAction);
menu->addMenu(copyMenu);
this->openInspectionWindowAction->setEnabled(TargetRegisterInspectorWindow::registerSupported(this->descriptor));
const auto targetStopped = this->targetState == Targets::TargetState::STOPPED;
const auto targetStoppedAndValuePresent = targetStopped && this->currentRegister.has_value();
this->refreshValueAction->setEnabled(targetStopped);
this->copyValueDecimalAction->setEnabled(targetStoppedAndValuePresent);
this->copyValueHexAction->setEnabled(targetStoppedAndValuePresent);
this->copyValueBinaryAction->setEnabled(targetStoppedAndValuePresent);
menu->exec(event->globalPos());
}
void RegisterWidget::openInspectionWindow() {
if (!TargetRegisterInspectorWindow::registerSupported(this->descriptor)) {
return;
}
if (this->inspectWindow == nullptr) {
this->inspectWindow = new TargetRegisterInspectorWindow(
this->descriptor,
this->insightWorker,
this->targetState,
this->currentRegister.has_value() ? std::optional(this->currentRegister->value)
: std::nullopt,
this
);
} else {
this->inspectWindow->activateWindow();
}
}
}
if (this->currentRegister.has_value()) {
this->inspectWindow->setValue(this->currentRegister->value);
}
void RegisterWidget::refreshValue() {
auto* readRegisterTask = new ReadTargetRegisters({this->descriptor});
if (!this->inspectWindow->isVisible()) {
this->inspectWindow->show();
QObject::connect(
readRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
[this] (Targets::TargetRegisters registers) {
for (const auto& targetRegister : registers) {
if (targetRegister.descriptor == this->descriptor) {
this->setRegisterValue(targetRegister);
}
} else {
this->inspectWindow->activateWindow();
}
}
);
this->insightWorker.queueTask(readRegisterTask);
}
void RegisterWidget::copyName() {
if (this->nameLabel != nullptr) {
QApplication::clipboard()->setText(this->nameLabel->text());
}
}
void RegisterWidget::copyValueHex() {
if (this->currentRegister.has_value()) {
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
static_cast<qsizetype>(this->currentRegister.value().value.size())
).toHex();
QApplication::clipboard()->setText(QString(valueByteArray).toUpper());
void RegisterWidget::refreshValue() {
auto* readRegisterTask = new ReadTargetRegisters({this->descriptor});
QObject::connect(
readRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
[this] (Targets::TargetRegisters registers) {
for (const auto& targetRegister : registers) {
if (targetRegister.descriptor == this->descriptor) {
this->setRegisterValue(targetRegister);
}
}
}
);
this->insightWorker.queueTask(readRegisterTask);
}
}
void RegisterWidget::copyValueDecimal() {
if (this->currentRegister.has_value()) {
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
static_cast<qsizetype>(this->currentRegister.value().value.size())
).toHex();
QApplication::clipboard()->setText(QString::number(valueByteArray.toUInt(nullptr, 16)));
}
}
void RegisterWidget::copyValueBinary() {
if (this->currentRegister.has_value()) {
const auto registerValueSize = static_cast<qsizetype>(this->currentRegister.value().size());
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
registerValueSize
).toHex();
auto bitString = QString::number(valueByteArray.toUInt(nullptr, 16), 2);
if (bitString.size() < (registerValueSize * 8)) {
bitString = bitString.rightJustified((registerValueSize * 8), '0');
void RegisterWidget::copyName() {
if (this->nameLabel != nullptr) {
QApplication::clipboard()->setText(this->nameLabel->text());
}
}
QApplication::clipboard()->setText(bitString);
}
}
void RegisterWidget::postSetSelected(bool selected) {
auto valueLabelStyle = this->valueLabel->style();
valueLabelStyle->unpolish(this->valueLabel);
valueLabelStyle->polish(this->valueLabel);
}
void RegisterWidget::onTargetStateChange(Targets::TargetState newState) {
this->targetState = newState;
if (this->targetState == Targets::TargetState::RUNNING) {
this->clearInlineValue();
void RegisterWidget::copyValueHex() {
if (this->currentRegister.has_value()) {
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
static_cast<qsizetype>(this->currentRegister.value().value.size())
).toHex();
QApplication::clipboard()->setText(QString(valueByteArray).toUpper());
}
}
void RegisterWidget::copyValueDecimal() {
if (this->currentRegister.has_value()) {
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
static_cast<qsizetype>(this->currentRegister.value().value.size())
).toHex();
QApplication::clipboard()->setText(QString::number(valueByteArray.toUInt(nullptr, 16)));
}
}
void RegisterWidget::copyValueBinary() {
if (this->currentRegister.has_value()) {
const auto registerValueSize = static_cast<qsizetype>(this->currentRegister.value().size());
auto valueByteArray = QByteArray(
reinterpret_cast<const char*>(this->currentRegister.value().value.data()),
registerValueSize
).toHex();
auto bitString = QString::number(valueByteArray.toUInt(nullptr, 16), 2);
if (bitString.size() < (registerValueSize * 8)) {
bitString = bitString.rightJustified((registerValueSize * 8), '0');
}
QApplication::clipboard()->setText(bitString);
}
}
void RegisterWidget::postSetSelected(bool selected) {
auto valueLabelStyle = this->valueLabel->style();
valueLabelStyle->unpolish(this->valueLabel);
valueLabelStyle->polish(this->valueLabel);
}
void RegisterWidget::onTargetStateChange(Targets::TargetState newState) {
this->targetState = newState;
if (this->targetState == Targets::TargetState::RUNNING) {
this->clearInlineValue();
}
}
}

View File

@@ -12,239 +12,244 @@
#include "src/Insight/InsightWorker/Tasks/ReadTargetRegisters.hpp"
using namespace Bloom::Widgets;
using namespace Bloom::Exceptions;
namespace Bloom::Widgets
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetDescriptor;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
using Bloom::Targets::TargetDescriptor;
using Bloom::Targets::TargetRegisterDescriptor;
using Bloom::Targets::TargetRegisterDescriptors;
using Bloom::Targets::TargetRegisterType;
TargetRegistersPaneWidget::TargetRegistersPaneWidget(
const TargetDescriptor& targetDescriptor,
InsightWorker& insightWorker,
PanelWidget* parent
): QWidget(parent), parent(parent), targetDescriptor(targetDescriptor), insightWorker(insightWorker) {
this->setObjectName("target-registers-side-pane");
TargetRegistersPaneWidget::TargetRegistersPaneWidget(
const TargetDescriptor& targetDescriptor,
InsightWorker& insightWorker,
PanelWidget* parent
): QWidget(parent), parent(parent), targetDescriptor(targetDescriptor), insightWorker(insightWorker) {
this->setObjectName("target-registers-side-pane");
auto targetRegistersPaneUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/TargetRegistersSidePane.ui"
)
);
auto targetRegistersPaneUiFile = QFile(
QString::fromStdString(Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetRegistersPane/UiFiles/"
"TargetRegistersSidePane.ui"
)
);
if (!targetRegistersPaneUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegistersSidePane UI file");
}
if (!targetRegistersPaneUiFile.open(QFile::ReadOnly)) {
throw Exception("Failed to open TargetRegistersSidePane UI file");
}
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&targetRegistersPaneUiFile, this);
this->container->setFixedSize(parent->width(), parent->maximumHeight());
auto uiLoader = UiLoader(this);
this->container = uiLoader.load(&targetRegistersPaneUiFile, this);
this->container->setFixedSize(parent->width(), parent->maximumHeight());
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(this->container);
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(this->container);
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
this->collapseAllButton = this->toolBar->findChild<SvgToolButton*>("collapse-all-btn");
this->expandAllButton = this->toolBar->findChild<SvgToolButton*>("expand-all-btn");
this->toolBar->layout()->setContentsMargins(5, 0, 5, 0);
this->searchInput = this->container->findChild<QLineEdit*>("search-input");
this->toolBar = this->container->findChild<QWidget*>("tool-bar");
this->collapseAllButton = this->toolBar->findChild<SvgToolButton*>("collapse-all-btn");
this->expandAllButton = this->toolBar->findChild<SvgToolButton*>("expand-all-btn");
this->toolBar->layout()->setContentsMargins(5, 0, 5, 0);
this->searchInput = this->container->findChild<QLineEdit*>("search-input");
QObject::connect(this->expandAllButton, &QToolButton::clicked, [this] {
this->expandAllRegisterGroups();
});
QObject::connect(this->expandAllButton, &QToolButton::clicked, [this] {
this->expandAllRegisterGroups();
});
QObject::connect(this->collapseAllButton, &QToolButton::clicked, [this] {
this->collapseAllRegisterGroups();
});
QObject::connect(this->collapseAllButton, &QToolButton::clicked, [this] {
this->collapseAllRegisterGroups();
});
QObject::connect(this->searchInput, &QLineEdit::textChanged, [this] {
this->filterRegisters(this->searchInput->text());
});
QObject::connect(this->searchInput, &QLineEdit::textChanged, [this] {
this->filterRegisters(this->searchInput->text());
});
this->itemScrollArea = this->container->findChild<QScrollArea*>("item-scroll-area");
this->itemScrollArea = this->container->findChild<QScrollArea*>("item-scroll-area");
this->itemContainer = this->container->findChild<QWidget*>("item-container");
auto itemLayout = this->itemContainer->findChild<QVBoxLayout*>();
this->itemContainer = this->container->findChild<QWidget*>("item-container");
auto itemLayout = this->itemContainer->findChild<QVBoxLayout*>();
auto& registerDescriptors = targetDescriptor.registerDescriptorsByType;
this->renderedDescriptors = std::set<TargetRegisterDescriptor>(
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).begin(),
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).end()
);
auto& registerDescriptors = targetDescriptor.registerDescriptorsByType;
this->renderedDescriptors = std::set<TargetRegisterDescriptor>(
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).begin(),
registerDescriptors.at(TargetRegisterType::GENERAL_PURPOSE_REGISTER).end()
);
auto* generalPurposeRegisterGroupWidget = new RegisterGroupWidget(
"CPU General Purpose",
this->renderedDescriptors,
insightWorker,
this
);
itemLayout->addWidget(generalPurposeRegisterGroupWidget, 0, Qt::AlignTop);
this->registerGroupWidgets.insert(generalPurposeRegisterGroupWidget);
auto registerDescriptorsByGroupName = std::map<std::string, std::set<TargetRegisterDescriptor>>();
for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::OTHER)) {
registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor);
}
for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::PORT_REGISTER)) {
registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor);
}
for (const auto& [groupName, registerDescriptors] : registerDescriptorsByGroupName) {
auto* registerGroupWidget = new RegisterGroupWidget(
QString::fromStdString(groupName).toUpper(),
registerDescriptors,
auto* generalPurposeRegisterGroupWidget = new RegisterGroupWidget(
"CPU General Purpose",
this->renderedDescriptors,
insightWorker,
this
);
itemLayout->addWidget(registerGroupWidget, 0, Qt::AlignTop);
this->registerGroupWidgets.insert(registerGroupWidget);
this->renderedDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end());
}
itemLayout->addWidget(generalPurposeRegisterGroupWidget, 0, Qt::AlignTop);
this->registerGroupWidgets.insert(generalPurposeRegisterGroupWidget);
itemLayout->addStretch(1);
QObject::connect(
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
&TargetRegistersPaneWidget::onTargetStateChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetRegistersWritten,
this,
&TargetRegistersPaneWidget::onRegistersRead
);
}
void TargetRegistersPaneWidget::filterRegisters(const QString& keyword) {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
// If the group name matches the keyword, then don't bother iterating through all the register widgets
if (keyword.isEmpty() || registerGroupWidget->name.contains(keyword, Qt::CaseInsensitive)) {
registerGroupWidget->setVisible(true);
registerGroupWidget->setAllRegistersVisible(true);
if (!keyword.isEmpty()) {
registerGroupWidget->expand();
} else {
registerGroupWidget->collapse();
}
} else {
registerGroupWidget->filterRegisters(keyword);
auto registerDescriptorsByGroupName = std::map<std::string, std::set<TargetRegisterDescriptor>>();
for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::OTHER)) {
registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor);
}
}
}
void TargetRegistersPaneWidget::collapseAllRegisterGroups() {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
registerGroupWidget->collapse();
}
}
for (const auto& registerDescriptor : registerDescriptors.at(TargetRegisterType::PORT_REGISTER)) {
registerDescriptorsByGroupName[registerDescriptor.groupName.value_or("other")].insert(registerDescriptor);
}
void TargetRegistersPaneWidget::expandAllRegisterGroups() {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
registerGroupWidget->expand();
}
}
for (const auto& [groupName, registerDescriptors] : registerDescriptorsByGroupName) {
auto* registerGroupWidget = new RegisterGroupWidget(
QString::fromStdString(groupName).toUpper(),
registerDescriptors,
insightWorker,
this
);
void TargetRegistersPaneWidget::refreshRegisterValues(std::optional<std::function<void(void)>> callback) {
auto& descriptors = this->renderedDescriptors;
itemLayout->addWidget(registerGroupWidget, 0, Qt::AlignTop);
this->registerGroupWidgets.insert(registerGroupWidget);
this->renderedDescriptors.insert(registerDescriptors.begin(), registerDescriptors.end());
}
if (descriptors.empty()) {
return;
}
itemLayout->addStretch(1);
auto* readRegisterTask = new ReadTargetRegisters(descriptors);
QObject::connect(
readRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
&TargetRegistersPaneWidget::onRegistersRead
);
if (callback.has_value()) {
QObject::connect(
readRegisterTask,
&InsightWorkerTask::completed,
&insightWorker,
&InsightWorker::targetStateUpdated,
this,
callback.value()
&TargetRegistersPaneWidget::onTargetStateChanged
);
QObject::connect(
&insightWorker,
&InsightWorker::targetRegistersWritten,
this,
&TargetRegistersPaneWidget::onRegistersRead
);
}
this->insightWorker.queueTask(readRegisterTask);
}
void TargetRegistersPaneWidget::filterRegisters(const QString& keyword) {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
// If the group name matches the keyword, then don't bother iterating through all the register widgets
if (keyword.isEmpty() || registerGroupWidget->name.contains(keyword, Qt::CaseInsensitive)) {
registerGroupWidget->setVisible(true);
registerGroupWidget->setAllRegistersVisible(true);
void TargetRegistersPaneWidget::activate() {
this->show();
this->activated = true;
this->postActivate();
}
if (!keyword.isEmpty()) {
registerGroupWidget->expand();
void TargetRegistersPaneWidget::deactivate() {
this->hide();
this->activated = false;
this->postDeactivate();
}
} else {
registerGroupWidget->collapse();
}
void TargetRegistersPaneWidget::onItemSelectionChange(ItemWidget* newlySelectedWidget) {
// Only one item in the target registers pane can be selected at any given time.
if (this->selectedItemWidget != newlySelectedWidget) {
if (this->selectedItemWidget != nullptr) {
this->selectedItemWidget->setSelected(false);
} else {
registerGroupWidget->filterRegisters(keyword);
}
}
}
void TargetRegistersPaneWidget::collapseAllRegisterGroups() {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
registerGroupWidget->collapse();
}
}
void TargetRegistersPaneWidget::expandAllRegisterGroups() {
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
registerGroupWidget->expand();
}
}
void TargetRegistersPaneWidget::refreshRegisterValues(std::optional<std::function<void(void)>> callback) {
auto& descriptors = this->renderedDescriptors;
if (descriptors.empty()) {
return;
}
this->selectedItemWidget = newlySelectedWidget;
auto* readRegisterTask = new ReadTargetRegisters(descriptors);
QObject::connect(
readRegisterTask,
&ReadTargetRegisters::targetRegistersRead,
this,
&TargetRegistersPaneWidget::onRegistersRead
);
if (callback.has_value()) {
QObject::connect(
readRegisterTask,
&InsightWorkerTask::completed,
this,
callback.value()
);
}
this->insightWorker.queueTask(readRegisterTask);
}
}
void TargetRegistersPaneWidget::resizeEvent(QResizeEvent* event) {
const auto parentSize = this->parent->size();
const auto width = parentSize.width() - 1;
this->container->setFixedWidth(width);
this->searchInput->setFixedWidth(width - 20);
/*
* In order to avoid the panel resize handle overlapping the scroll bar handle, we reduce the size of
* the scroll area.
*/
this->itemScrollArea->setFixedWidth(width - this->parent->getHandleSize());
}
void TargetRegistersPaneWidget::postActivate() {
if (this->targetState == Targets::TargetState::STOPPED) {
this->refreshRegisterValues();
void TargetRegistersPaneWidget::activate() {
this->show();
this->activated = true;
this->postActivate();
}
}
void TargetRegistersPaneWidget::postDeactivate() {
}
void TargetRegistersPaneWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
if (newState == TargetState::STOPPED && this->activated) {
this->refreshRegisterValues();
void TargetRegistersPaneWidget::deactivate() {
this->hide();
this->activated = false;
this->postDeactivate();
}
}
void TargetRegistersPaneWidget::onRegistersRead(const Targets::TargetRegisters& registers) {
for (const auto& targetRegister : registers) {
auto& descriptor = targetRegister.descriptor;
void TargetRegistersPaneWidget::onItemSelectionChange(ItemWidget* newlySelectedWidget) {
// Only one item in the target registers pane can be selected at any given time.
if (this->selectedItemWidget != newlySelectedWidget) {
if (this->selectedItemWidget != nullptr) {
this->selectedItemWidget->setSelected(false);
}
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
if (registerGroupWidget->registerWidgetsMappedByDescriptor.contains(descriptor)) {
registerGroupWidget->registerWidgetsMappedByDescriptor.at(descriptor)->setRegisterValue(targetRegister);
break;
this->selectedItemWidget = newlySelectedWidget;
}
}
void TargetRegistersPaneWidget::resizeEvent(QResizeEvent* event) {
const auto parentSize = this->parent->size();
const auto width = parentSize.width() - 1;
this->container->setFixedWidth(width);
this->searchInput->setFixedWidth(width - 20);
/*
* In order to avoid the panel resize handle overlapping the scroll bar handle, we reduce the size of
* the scroll area.
*/
this->itemScrollArea->setFixedWidth(width - this->parent->getHandleSize());
}
void TargetRegistersPaneWidget::postActivate() {
if (this->targetState == Targets::TargetState::STOPPED) {
this->refreshRegisterValues();
}
}
void TargetRegistersPaneWidget::postDeactivate() {
}
void TargetRegistersPaneWidget::onTargetStateChanged(Targets::TargetState newState) {
using Targets::TargetState;
this->targetState = newState;
if (newState == TargetState::STOPPED && this->activated) {
this->refreshRegisterValues();
}
}
void TargetRegistersPaneWidget::onRegistersRead(const Targets::TargetRegisters& registers) {
for (const auto& targetRegister : registers) {
auto& descriptor = targetRegister.descriptor;
for (const auto& registerGroupWidget : this->registerGroupWidgets) {
if (registerGroupWidget->registerWidgetsMappedByDescriptor.contains(descriptor)) {
registerGroupWidget->registerWidgetsMappedByDescriptor.at(
descriptor
)->setRegisterValue(targetRegister);
break;
}
}
}
}

View File

@@ -1,81 +1,85 @@
#include "BodyWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Dip;
namespace Bloom::Widgets::InsightTargetWidgets::Dip
{
BodyWidget::BodyWidget(QWidget* parent, std::size_t pinCount): QWidget(parent) {
this->setObjectName("target-body");
BodyWidget::BodyWidget(QWidget* parent, std::size_t pinCount): QWidget(parent) {
this->setObjectName("target-body");
/*
* The DIP package widget looks awkward when the body height is fixed. For this reason, the body height and
* indicator sizes are proportional to the pin count.
*
* The proportionality constants used below were chosen because they look the nicest. No other reason.
*/
this->setFixedHeight(
std::min(
BodyWidget::MAXIMUM_BODY_HEIGHT,
std::max(
BodyWidget::MINIMUM_BODY_HEIGHT,
static_cast<int>(std::ceil(3.6 * static_cast<double>(pinCount)))
/*
* The DIP package widget looks awkward when the body height is fixed. For this reason, the body height and
* indicator sizes are proportional to the pin count.
*
* The proportionality constants used below were chosen because they look the nicest. No other reason.
*/
this->setFixedHeight(
std::min(
BodyWidget::MAXIMUM_BODY_HEIGHT,
std::max(
BodyWidget::MINIMUM_BODY_HEIGHT,
static_cast<int>(std::ceil(3.6 * static_cast<double>(pinCount)))
)
)
)
);
);
this->firstPinIndicatorDiameter = std::min(
BodyWidget::MAXIMUM_FIRST_PIN_INDICATOR_HEIGHT,
std::max(BodyWidget::MINIMUM_FIRST_PIN_INDICATOR_HEIGHT, static_cast<int>(std::floor(pinCount / 2)))
);
this->firstPinIndicatorDiameter = std::min(
BodyWidget::MAXIMUM_FIRST_PIN_INDICATOR_HEIGHT,
std::max(BodyWidget::MINIMUM_FIRST_PIN_INDICATOR_HEIGHT, static_cast<int>(std::floor(pinCount / 2)))
);
this->orientationIndicatorDiameter = this->firstPinIndicatorDiameter % 2 == 0 ?
this->firstPinIndicatorDiameter + 3
: this->firstPinIndicatorDiameter + 2;
}
void BodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
const auto bodyHeight = this->height();
const auto bodyRect = QRectF(
0,
0,
this->width(),
bodyHeight
);
// The first pin indicator is just a circle positioned close to the first pin
const auto firstPinIndicatorRect = QRectF(
6,
(bodyHeight - this->firstPinIndicatorDiameter - 6),
this->firstPinIndicatorDiameter,
this->firstPinIndicatorDiameter
);
/*
* The orientation indicator is just a half-circle cut-out, positioned on the side of the DIP package closest to
* the first pin.
*/
const auto orientationIndicatorRect = QRectF(
-(this->orientationIndicatorDiameter / 2),
(bodyHeight / 2) - (this->orientationIndicatorDiameter / 2),
this->orientationIndicatorDiameter,
this->orientationIndicatorDiameter
);
static const auto backgroundColor = QColor(0x37, 0x38, 0x35);
auto targetBodyColor = this->getBodyColor();
if (!this->isEnabled()) {
targetBodyColor.setAlpha(this->getDisableAlphaLevel());
this->orientationIndicatorDiameter = this->firstPinIndicatorDiameter % 2 == 0 ?
this->firstPinIndicatorDiameter + 3
: this->firstPinIndicatorDiameter + 2;
}
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(targetBodyColor);
painter.drawRect(bodyRect);
painter.setBrush(backgroundColor);
painter.drawEllipse(firstPinIndicatorRect);
painter.drawEllipse(orientationIndicatorRect);
void BodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
const auto bodyHeight = this->height();
const auto bodyRect = QRectF(
0,
0,
this->width(),
bodyHeight
);
// The first pin indicator is just a circle positioned close to the first pin
const auto firstPinIndicatorRect = QRectF(
6,
(bodyHeight - this->firstPinIndicatorDiameter - 6),
this->firstPinIndicatorDiameter,
this->firstPinIndicatorDiameter
);
/*
* The orientation indicator is just a half-circle cut-out, positioned on the side of the DIP package
* closest to the first pin.
*/
const auto orientationIndicatorRect = QRectF(
-(this->orientationIndicatorDiameter / 2),
(bodyHeight / 2) - (this->orientationIndicatorDiameter / 2),
this->orientationIndicatorDiameter,
this->orientationIndicatorDiameter
);
static const auto backgroundColor = QColor(0x37, 0x38, 0x35);
auto targetBodyColor = this->getBodyColor();
if (!this->isEnabled()) {
targetBodyColor.setAlpha(this->getDisableAlphaLevel());
}
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(targetBodyColor);
painter.drawRect(bodyRect);
painter.setBrush(backgroundColor);
painter.drawEllipse(firstPinIndicatorRect);
painter.drawEllipse(orientationIndicatorRect);
}
}

View File

@@ -8,242 +8,245 @@
#include "src/Helpers/Paths.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Dip;
using namespace Bloom::Exceptions;
namespace Bloom::Widgets::InsightTargetWidgets::Dip
{
using namespace Bloom::Exceptions;
using Bloom::Targets::TargetVariant;
using Bloom::Targets::TargetVariant;
DualInlinePackageWidget::DualInlinePackageWidget(
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPackageWidget(targetVariant, insightWorker, parent) {
auto stylesheetFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/Stylesheets/DualInlinePackage.qss"
)
);
stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll());
this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size());
this->layout = new QVBoxLayout();
this->layout->setSpacing(PinWidget::WIDTH_SPACING);
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->topPinLayout = new QHBoxLayout();
this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft);
this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->bottomPinLayout = new QHBoxLayout();
this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) {
auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this);
this->pinWidgets.push_back(pinWidget);
TargetPackageWidget::pinWidgets.push_back(pinWidget);
if (targetPinNumber <= (targetVariant.pinDescriptorsByNumber.size() / 2)) {
this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter);
} else {
this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter);
}
}
this->bodyWidget = new BodyWidget(this, targetVariant.pinDescriptorsByNumber.size());
this->layout->addLayout(this->topPinLayout);
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignVCenter);
this->layout->addLayout(this->bottomPinLayout);
this->setLayout(this->layout);
const auto bodyWidgetHeight = this->bodyWidget->height();
const auto bodyWidgetWidth = ((PinWidget::MINIMUM_WIDTH + PinWidget::WIDTH_SPACING)
* static_cast<int>(this->pinWidgets.size() / 2)) - PinWidget::WIDTH_SPACING + 46;
this->bodyWidget->setGeometry(
0,
PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING,
bodyWidgetWidth,
bodyWidgetHeight
);
const auto width = bodyWidgetWidth;
const auto height = (
(
PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING + PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + (
(PinWidget::LABEL_HEIGHT + PinWidget::PIN_LABEL_SPACING) * 2
DualInlinePackageWidget::DualInlinePackageWidget(
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPackageWidget(targetVariant, insightWorker, parent) {
auto stylesheetFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/Stylesheets/"
"DualInlinePackage.qss"
)
) * 2) + bodyWidgetHeight;
);
stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll());
this->topPinLayout->setGeometry(QRect(0, 0, width, PinWidget::MAXIMUM_HEIGHT));
this->bottomPinLayout->setGeometry(
QRect(
0,
(PinWidget::MAXIMUM_HEIGHT + bodyWidgetHeight + (PinWidget::WIDTH_SPACING * 2)),
width,
PinWidget::MAXIMUM_HEIGHT
)
);
this->topPinLayout->setContentsMargins(23, 0, 23, 0);
this->bottomPinLayout->setContentsMargins( 23, 0, 23, 0);
this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size());
this->layout = new QVBoxLayout();
this->layout->setSpacing(PinWidget::WIDTH_SPACING);
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->setFixedSize(width, height);
this->topPinLayout = new QHBoxLayout();
this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft);
this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->bottomPinLayout = new QHBoxLayout();
this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->setGeometry(
(parent->width() / 2) - (width / 2),
(parent->height() / 2) - (height / 2),
width,
height
);
}
for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) {
auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this);
this->pinWidgets.push_back(pinWidget);
TargetPackageWidget::pinWidgets.push_back(pinWidget);
void DualInlinePackageWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void DualInlinePackageWidget::drawWidget(QPainter& painter) {
using Targets::TargetPinState;
static auto pinNameFont = QFont("'Ubuntu', sans-serif");
static auto pinDirectionFont = pinNameFont;
pinNameFont.setPixelSize(11);
pinDirectionFont.setPixelSize(9);
static const auto lineColor = QColor(0x4F, 0x4F, 0x4F);
static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA);
static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D);
static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA);
static const auto inDirectionText = QString("IN");
static const auto outDirectionText = QString("OUT");
for (const auto* pinWidget : this->pinWidgets) {
const auto pinGeoPosition = pinWidget->pos();
const auto& pinState = pinWidget->getPinState();
const auto pinStateChanged = pinWidget->hasPinStateChanged();
painter.setFont(pinNameFont);
if (pinWidget->position == Position::TOP) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y()
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
- pinDirectionLabelLineLength,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength
- (PinWidget::MAXIMUM_LABEL_HEIGHT * 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
if (targetPinNumber <= (targetVariant.pinDescriptorsByNumber.size() / 2)) {
this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter);
} else {
this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignHCenter);
}
}
} else if (pinWidget->position == Position::BOTTOM) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
this->bodyWidget = new BodyWidget(this, targetVariant.pinDescriptorsByNumber.size());
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
));
this->layout->addLayout(this->topPinLayout);
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignVCenter);
this->layout->addLayout(this->bottomPinLayout);
this->setLayout(this->layout);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
const auto bodyWidgetHeight = this->bodyWidget->height();
const auto bodyWidgetWidth = ((PinWidget::MINIMUM_WIDTH + PinWidget::WIDTH_SPACING)
* static_cast<int>(this->pinWidgets.size() / 2)) - PinWidget::WIDTH_SPACING + 46;
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
this->bodyWidget->setGeometry(
0,
PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING,
bodyWidgetWidth,
bodyWidgetHeight
);
const auto width = bodyWidgetWidth;
const auto height = (
(
PinWidget::MAXIMUM_HEIGHT + PinWidget::WIDTH_SPACING + PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::PIN_LABEL_SHORT_LINE_LENGTH + (
(PinWidget::LABEL_HEIGHT + PinWidget::PIN_LABEL_SPACING) * 2
)
) * 2) + bodyWidgetHeight;
this->topPinLayout->setGeometry(QRect(0, 0, width, PinWidget::MAXIMUM_HEIGHT));
this->bottomPinLayout->setGeometry(
QRect(
0,
(PinWidget::MAXIMUM_HEIGHT + bodyWidgetHeight + (PinWidget::WIDTH_SPACING * 2)),
width,
PinWidget::MAXIMUM_HEIGHT
)
);
this->topPinLayout->setContentsMargins(23, 0, 23, 0);
this->bottomPinLayout->setContentsMargins( 23, 0, 23, 0);
this->setFixedSize(width, height);
this->setGeometry(
(parent->width() / 2) - (width / 2),
(parent->height() / 2) - (height / 2),
width,
height
);
}
void DualInlinePackageWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void DualInlinePackageWidget::drawWidget(QPainter& painter) {
using Targets::TargetPinState;
static auto pinNameFont = QFont("'Ubuntu', sans-serif");
static auto pinDirectionFont = pinNameFont;
pinNameFont.setPixelSize(11);
pinDirectionFont.setPixelSize(9);
static const auto lineColor = QColor(0x4F, 0x4F, 0x4F);
static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA);
static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D);
static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA);
static const auto inDirectionText = QString("IN");
static const auto outDirectionText = QString("OUT");
for (const auto* pinWidget : this->pinWidgets) {
const auto pinGeoPosition = pinWidget->pos();
const auto& pinState = pinWidget->getPinState();
const auto pinStateChanged = pinWidget->hasPinStateChanged();
painter.setFont(pinNameFont);
if (pinWidget->position == Position::TOP) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT,
pinGeoPosition.y() - pinNameLabelLineLength,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength
pinGeoPosition.y()
));
painter.setPen(pinDirectionFontColor);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength,
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
- pinDirectionLabelLineLength,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength
- (PinWidget::MAXIMUM_LABEL_HEIGHT * 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::BOTTOM) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT,
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MINIMUM_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
}
}
}

View File

@@ -2,34 +2,39 @@
#include <QPainter>
using namespace Bloom::Widgets::InsightTargetWidgets::Dip;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Dip
{
using namespace Bloom::Targets;
PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor)
: TargetPinBodyWidget(parent, std::move(pinDescriptor)) {
this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT);
}
void PinBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void PinBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
auto pinWidth = PinBodyWidget::WIDTH;
auto pinHeight = PinBodyWidget::HEIGHT;
this->setFixedSize(pinWidth, pinHeight);
auto pinColor = this->getBodyColor();
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(pinColor);
painter.drawRect(
0,
0,
pinWidth,
pinHeight
);
PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor)
: TargetPinBodyWidget(parent, std::move(pinDescriptor)) {
this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT);
}
void PinBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void PinBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
auto pinWidth = PinBodyWidget::WIDTH;
auto pinHeight = PinBodyWidget::HEIGHT;
this->setFixedSize(pinWidth, pinHeight);
auto pinColor = this->getBodyColor();
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(pinColor);
painter.drawRect(
0,
0,
pinWidth,
pinHeight
);
}
}

View File

@@ -1,47 +1,49 @@
#include "PinWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Dip;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Dip
{
using namespace Bloom::Targets;
PinWidget::PinWidget(
const TargetPinDescriptor& pinDescriptor,
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) {
this->setFixedSize(PinWidget::MINIMUM_WIDTH, PinWidget::MAXIMUM_HEIGHT);
PinWidget::PinWidget(
const TargetPinDescriptor& pinDescriptor,
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) {
this->setFixedSize(PinWidget::MINIMUM_WIDTH, PinWidget::MAXIMUM_HEIGHT);
this->layout = new QVBoxLayout();
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setSpacing(0);
this->layout = new QVBoxLayout();
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setSpacing(0);
this->bodyWidget = new PinBodyWidget(this, this->pinDescriptor);
this->position = (pinDescriptor.number > (targetVariant.pinDescriptorsByNumber.size() / 2))
? Position::TOP : Position::BOTTOM;
this->bodyWidget = new PinBodyWidget(this, this->pinDescriptor);
this->position = (pinDescriptor.number > (targetVariant.pinDescriptorsByNumber.size() / 2))
? Position::TOP : Position::BOTTOM;
const bool isTopWidget = this->position == Position::TOP;
const bool isTopWidget = this->position == Position::TOP;
this->layout->setAlignment(isTopWidget ? (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom)
: (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop));
this->layout->setAlignment(isTopWidget ? (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom)
: (Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop));
this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper();
this->pinNameLabelText.truncate(5);
this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper();
this->pinNameLabelText.truncate(5);
this->pinNumberLabel = new QLabel(this);
this->pinNumberLabel->setObjectName("target-pin-number");
this->pinNumberLabel->setText(QString::number(pinDescriptor.number));
this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
this->pinNumberLabel = new QLabel(this);
this->pinNumberLabel->setObjectName("target-pin-number");
this->pinNumberLabel->setText(QString::number(pinDescriptor.number));
this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
if (isTopWidget) {
this->layout->setDirection(QBoxLayout::Direction::BottomToTop);
if (isTopWidget) {
this->layout->setDirection(QBoxLayout::Direction::BottomToTop);
}
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignHCenter);
this->layout->insertSpacing(1, PinWidget::PIN_LABEL_SPACING);
this->layout->addWidget(this->pinNumberLabel, 0, Qt::AlignmentFlag::AlignHCenter);
this->pinNumberLabel->setFixedHeight(PinWidget::LABEL_HEIGHT);
this->setLayout(this->layout);
connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked);
}
this->layout->addWidget(this->bodyWidget, 0, Qt::AlignmentFlag::AlignHCenter);
this->layout->insertSpacing(1, PinWidget::PIN_LABEL_SPACING);
this->layout->addWidget(this->pinNumberLabel, 0, Qt::AlignmentFlag::AlignHCenter);
this->pinNumberLabel->setFixedHeight(PinWidget::LABEL_HEIGHT);
this->setLayout(this->layout);
connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked);
}

View File

@@ -2,47 +2,51 @@
#include <QPainter>
using namespace Bloom::Widgets::InsightTargetWidgets::Qfp;
void BodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void BodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
auto targetBodyColor = this->getBodyColor();
auto backgroundColor = QColor("#373835");
if (!this->isEnabled()) {
targetBodyColor.setAlpha(this->getDisableAlphaLevel());
namespace Bloom::Widgets::InsightTargetWidgets::Qfp
{
void BodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(targetBodyColor);
void BodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
const auto containerSize = this->size();
const auto targetBodyWidth = containerSize.width() - 8;
const auto targetBodyHeight = containerSize.height() - 8;
auto targetBodyColor = this->getBodyColor();
auto backgroundColor = QColor("#373835");
auto targetBodyPoint = QPoint(
(containerSize.width() / 2) - (targetBodyWidth / 2),
(containerSize.height() / 2) - (targetBodyHeight / 2)
);
if (!this->isEnabled()) {
targetBodyColor.setAlpha(this->getDisableAlphaLevel());
}
painter.drawRect(
targetBodyPoint.x(),
targetBodyPoint.y(),
targetBodyWidth,
targetBodyHeight
);
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(targetBodyColor);
painter.setBrush(backgroundColor);
painter.drawEllipse(QRectF(
targetBodyPoint.x() + 13,
targetBodyPoint.y() + 13,
18,
18
));
const auto containerSize = this->size();
const auto targetBodyWidth = containerSize.width() - 8;
const auto targetBodyHeight = containerSize.height() - 8;
auto targetBodyPoint = QPoint(
(containerSize.width() / 2) - (targetBodyWidth / 2),
(containerSize.height() / 2) - (targetBodyHeight / 2)
);
painter.drawRect(
targetBodyPoint.x(),
targetBodyPoint.y(),
targetBodyWidth,
targetBodyHeight
);
painter.setBrush(backgroundColor);
painter.drawEllipse(QRectF(
targetBodyPoint.x() + 13,
targetBodyPoint.y() + 13,
18,
18
));
}
}

View File

@@ -3,39 +3,44 @@
#include <QPainter>
#include <QEvent>
using namespace Bloom::Widgets::InsightTargetWidgets::Qfp;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Qfp
{
using namespace Bloom::Targets;
PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor, bool isVertical)
: TargetPinBodyWidget(parent, std::move(pinDescriptor)), isVertical(isVertical) {
if (isVertical) {
this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT);
PinBodyWidget::PinBodyWidget(QWidget* parent, Targets::TargetPinDescriptor pinDescriptor, bool isVertical)
: TargetPinBodyWidget(parent, std::move(pinDescriptor)), isVertical(isVertical) {
if (isVertical) {
this->setFixedSize(PinBodyWidget::WIDTH, PinBodyWidget::HEIGHT);
} else {
this->setFixedSize(PinBodyWidget::HEIGHT, PinBodyWidget::WIDTH);
} else {
this->setFixedSize(PinBodyWidget::HEIGHT, PinBodyWidget::WIDTH);
}
}
void PinBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void PinBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(
QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform,
true
);
auto pinWidth = this->isVertical ? PinBodyWidget::WIDTH : PinBodyWidget::HEIGHT;
auto pinHeight = this->isVertical ? PinBodyWidget::HEIGHT : PinBodyWidget::WIDTH;
this->setFixedSize(pinWidth, pinHeight);
auto pinColor = this->getBodyColor();
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(pinColor);
painter.drawRect(
0,
0,
pinWidth,
pinHeight
);
}
}
void PinBodyWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void PinBodyWidget::drawWidget(QPainter& painter) {
painter.setRenderHints(QPainter::RenderHint::Antialiasing | QPainter::RenderHint::SmoothPixmapTransform, true);
auto pinWidth = this->isVertical ? PinBodyWidget::WIDTH : PinBodyWidget::HEIGHT;
auto pinHeight = this->isVertical ? PinBodyWidget::HEIGHT : PinBodyWidget::WIDTH;
this->setFixedSize(pinWidth, pinHeight);
auto pinColor = this->getBodyColor();
painter.setPen(Qt::PenStyle::NoPen);
painter.setBrush(pinColor);
painter.drawRect(
0,
0,
pinWidth,
pinHeight
);
}

View File

@@ -7,95 +7,97 @@
#include "PinWidget.hpp"
#include "PinBodyWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Qfp;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Qfp
{
using namespace Bloom::Targets;
PinWidget::PinWidget(
const TargetPinDescriptor& pinDescriptor,
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) {
this->layout = new QBoxLayout(QBoxLayout::TopToBottom);
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setSpacing(0);
PinWidget::PinWidget(
const TargetPinDescriptor& pinDescriptor,
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPinWidget(pinDescriptor, targetVariant, insightWorker, parent) {
this->layout = new QBoxLayout(QBoxLayout::TopToBottom);
this->layout->setContentsMargins(0, 0, 0, 0);
this->layout->setSpacing(0);
auto pinCountPerLayout = (targetVariant.pinDescriptorsByNumber.size() / 4);
auto pinCountPerLayout = (targetVariant.pinDescriptorsByNumber.size() / 4);
if (pinDescriptor.number <= pinCountPerLayout) {
this->position = Position::LEFT;
if (pinDescriptor.number <= pinCountPerLayout) {
this->position = Position::LEFT;
} else if (pinDescriptor.number > pinCountPerLayout && pinDescriptor.number <= (pinCountPerLayout * 2)) {
this->position = Position::BOTTOM;
} else if (pinDescriptor.number > pinCountPerLayout && pinDescriptor.number <= (pinCountPerLayout * 2)) {
this->position = Position::BOTTOM;
} else if (pinDescriptor.number > (pinCountPerLayout * 2) && pinDescriptor.number <= (pinCountPerLayout * 3)) {
this->position = Position::RIGHT;
} else if (pinDescriptor.number > (pinCountPerLayout * 2) && pinDescriptor.number <= (pinCountPerLayout * 3)) {
this->position = Position::RIGHT;
} else if (pinDescriptor.number > (pinCountPerLayout * 3) && pinDescriptor.number <= (pinCountPerLayout * 4)) {
this->position = Position::TOP;
}
} else if (pinDescriptor.number > (pinCountPerLayout * 3) && pinDescriptor.number <= (pinCountPerLayout * 4)) {
this->position = Position::TOP;
}
this->bodyWidget = new PinBodyWidget(
this,
this->pinDescriptor,
(this->position == Position::TOP || this->position == Position::BOTTOM)
);
this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper();
this->pinNameLabelText.truncate(5);
this->pinNumberLabel = new QLabel(this);
this->pinNumberLabel->setObjectName("target-pin-number");
auto pinNumberText = QString::number(pinDescriptor.number);
pinNumberText.truncate(5);
this->pinNumberLabel->setText(QString::number(pinDescriptor.number));
this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
if (this->position == Position::LEFT) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignRight));
this->layout->setDirection(QBoxLayout::Direction::RightToLeft);
this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT);
} else if (this->position == Position::BOTTOM) {
this->layout->setAlignment(Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop);
this->layout->setDirection(QBoxLayout::Direction::TopToBottom);
this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT);
} else if (this->position == Position::RIGHT) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft));
this->layout->setDirection(QBoxLayout::Direction::LeftToRight);
this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT);
} else if (this->position == Position::TOP) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom));
this->layout->setDirection(QBoxLayout::Direction::BottomToTop);
this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT);
}
this->layout->addWidget(this->bodyWidget);
this->layout->addSpacing(3);
this->layout->addWidget(this->pinNumberLabel);
if (this->position == Position::LEFT || this->position == Position::RIGHT) {
this->pinNumberLabel->setFixedSize(
PinWidget::MAXIMUM_PIN_NUMBER_LABEL_WIDTH,
PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2
this->bodyWidget = new PinBodyWidget(
this,
this->pinDescriptor,
(this->position == Position::TOP || this->position == Position::BOTTOM)
);
} else if (this->position == Position::TOP || this->position == Position::BOTTOM) {
this->pinNumberLabel->setFixedSize(PinBodyWidget::WIDTH, PinWidget::LABEL_HEIGHT - 2);
this->pinNameLabelText = QString::fromStdString(pinDescriptor.name).toUpper();
this->pinNameLabelText.truncate(5);
this->pinNumberLabel = new QLabel(this);
this->pinNumberLabel->setObjectName("target-pin-number");
auto pinNumberText = QString::number(pinDescriptor.number);
pinNumberText.truncate(5);
this->pinNumberLabel->setText(QString::number(pinDescriptor.number));
this->pinNumberLabel->setAlignment(Qt::AlignmentFlag::AlignCenter);
if (this->position == Position::LEFT) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignRight));
this->layout->setDirection(QBoxLayout::Direction::RightToLeft);
this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT);
} else if (this->position == Position::BOTTOM) {
this->layout->setAlignment(Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignTop);
this->layout->setDirection(QBoxLayout::Direction::TopToBottom);
this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT);
} else if (this->position == Position::RIGHT) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignVCenter | Qt::AlignmentFlag::AlignLeft));
this->layout->setDirection(QBoxLayout::Direction::LeftToRight);
this->setFixedSize(PinWidget::MAXIMUM_HORIZONTAL_WIDTH, PinWidget::MAXIMUM_HORIZONTAL_HEIGHT);
} else if (this->position == Position::TOP) {
this->layout->setAlignment((Qt::AlignmentFlag::AlignHCenter | Qt::AlignmentFlag::AlignBottom));
this->layout->setDirection(QBoxLayout::Direction::BottomToTop);
this->setFixedSize(PinWidget::MAXIMUM_VERTICAL_WIDTH, PinWidget::MAXIMUM_VERTICAL_HEIGHT);
}
this->layout->addWidget(this->bodyWidget);
this->layout->addSpacing(3);
this->layout->addWidget(this->pinNumberLabel);
if (this->position == Position::LEFT || this->position == Position::RIGHT) {
this->pinNumberLabel->setFixedSize(
PinWidget::MAXIMUM_PIN_NUMBER_LABEL_WIDTH,
PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2
);
} else if (this->position == Position::TOP || this->position == Position::BOTTOM) {
this->pinNumberLabel->setFixedSize(PinBodyWidget::WIDTH, PinWidget::LABEL_HEIGHT - 2);
}
this->layout->addStretch(1);
this->setLayout(this->layout);
connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked);
}
this->layout->addStretch(1);
this->setLayout(this->layout);
void PinWidget::updatePinState(const Targets::TargetPinState& pinState) {
TargetPinWidget::updatePinState(pinState);
connect(this->bodyWidget, &PinBodyWidget::clicked, this, &TargetPinWidget::onWidgetBodyClicked);
}
void PinWidget::updatePinState(const Targets::TargetPinState& pinState) {
TargetPinWidget::updatePinState(pinState);
if (this->bodyWidget != nullptr) {
this->bodyWidget->setPinState(pinState);
if (this->bodyWidget != nullptr) {
this->bodyWidget->setPinState(pinState);
}
}
}

View File

@@ -11,403 +11,407 @@
#include "PinWidget.hpp"
#include "BodyWidget.hpp"
using namespace Bloom::Widgets::InsightTargetWidgets::Qfp;
using namespace Bloom::Targets;
namespace Bloom::Widgets::InsightTargetWidgets::Qfp
{
using namespace Bloom::Targets;
QuadFlatPackageWidget::QuadFlatPackageWidget(
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPackageWidget(targetVariant, insightWorker, parent) {
assert((targetVariant.pinDescriptorsByNumber.size() % 4) == 0);
QuadFlatPackageWidget::QuadFlatPackageWidget(
const TargetVariant& targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): TargetPackageWidget(targetVariant, insightWorker, parent) {
assert((targetVariant.pinDescriptorsByNumber.size() % 4) == 0);
auto stylesheetFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/Stylesheets/QuadFlatPackage.qss"
)
);
stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll());
auto stylesheetFile = QFile(QString::fromStdString(
Paths::compiledResourcesPath()
+ "/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/Stylesheets/QuadFlatPackage.qss"
)
);
stylesheetFile.open(QFile::ReadOnly);
this->setStyleSheet(stylesheetFile.readAll());
this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size());
this->layout = new QVBoxLayout();
this->layout->setSpacing(0);
this->layout->setContentsMargins(0, 0, 0, 0);
this->pinWidgets.reserve(targetVariant.pinDescriptorsByNumber.size());
this->layout = new QVBoxLayout();
this->layout->setSpacing(0);
this->layout->setContentsMargins(0, 0, 0, 0);
this->horizontalLayout = new QHBoxLayout();
this->horizontalLayout->setSpacing(0);
this->horizontalLayout->setDirection(QBoxLayout::Direction::LeftToRight);
this->horizontalLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->horizontalLayout = new QHBoxLayout();
this->horizontalLayout->setSpacing(0);
this->horizontalLayout->setDirection(QBoxLayout::Direction::LeftToRight);
this->horizontalLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->topPinLayout = new QHBoxLayout();
this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft);
this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->topPinLayout = new QHBoxLayout();
this->topPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->topPinLayout->setDirection(QBoxLayout::Direction::RightToLeft);
this->topPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->rightPinLayout = new QVBoxLayout();
this->rightPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->rightPinLayout->setDirection(QBoxLayout::Direction::BottomToTop);
this->rightPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->rightPinLayout = new QVBoxLayout();
this->rightPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->rightPinLayout->setDirection(QBoxLayout::Direction::BottomToTop);
this->rightPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->bottomPinLayout = new QHBoxLayout();
this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->bottomPinLayout->setDirection(QBoxLayout::Direction::LeftToRight);
this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->bottomPinLayout = new QHBoxLayout();
this->bottomPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->bottomPinLayout->setDirection(QBoxLayout::Direction::LeftToRight);
this->bottomPinLayout->setAlignment(Qt::AlignmentFlag::AlignHCenter);
this->leftPinLayout = new QVBoxLayout();
this->leftPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->leftPinLayout->setDirection(QBoxLayout::Direction::TopToBottom);
this->leftPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
this->leftPinLayout = new QVBoxLayout();
this->leftPinLayout->setSpacing(PinWidget::WIDTH_SPACING);
this->leftPinLayout->setDirection(QBoxLayout::Direction::TopToBottom);
this->leftPinLayout->setAlignment(Qt::AlignmentFlag::AlignVCenter);
const auto pinCountPerLayout = static_cast<int>(targetVariant.pinDescriptorsByNumber.size() / 4);
for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) {
auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this);
this->pinWidgets.push_back(pinWidget);
TargetPackageWidget::pinWidgets.push_back(pinWidget);
const auto pinCountPerLayout = static_cast<int>(targetVariant.pinDescriptorsByNumber.size() / 4);
for (const auto& [targetPinNumber, targetPinDescriptor]: targetVariant.pinDescriptorsByNumber) {
auto* pinWidget = new PinWidget(targetPinDescriptor, targetVariant, insightWorker, this);
this->pinWidgets.push_back(pinWidget);
TargetPackageWidget::pinWidgets.push_back(pinWidget);
if (pinWidget->position == Position::LEFT) {
this->leftPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignRight);
if (pinWidget->position == Position::LEFT) {
this->leftPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignRight);
} else if (pinWidget->position == Position::BOTTOM) {
this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignTop);
} else if (pinWidget->position == Position::BOTTOM) {
this->bottomPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignTop);
} else if (pinWidget->position == Position::RIGHT) {
this->rightPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignLeft);
} else if (pinWidget->position == Position::RIGHT) {
this->rightPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignLeft);
} else if (pinWidget->position == Position::TOP) {
this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignBottom);
} else if (pinWidget->position == Position::TOP) {
this->topPinLayout->addWidget(pinWidget, 0, Qt::AlignmentFlag::AlignBottom);
}
}
this->bodyWidget = new BodyWidget(this);
this->layout->addLayout(this->topPinLayout);
this->horizontalLayout->addLayout(this->leftPinLayout);
this->horizontalLayout->addWidget(this->bodyWidget);
this->horizontalLayout->addLayout(this->rightPinLayout);
this->layout->addLayout(this->horizontalLayout);
this->layout->addLayout(this->bottomPinLayout);
this->setLayout(this->layout);
// Layout sizing, positioning and padding
const auto verticalPinWidgetHeight = PinWidget::MAXIMUM_VERTICAL_HEIGHT;
const auto verticalPinWidgetWidth = PinWidget::MAXIMUM_VERTICAL_WIDTH;
const auto horizontalPinWidgetHeight = PinWidget::MAXIMUM_HORIZONTAL_HEIGHT;
const auto horizontalPinWidgetWidth = PinWidget::MAXIMUM_HORIZONTAL_WIDTH;
/*
* Horizontal layouts are the right and left pin layouts - the ones that hold horizontal pin widgets.
* The bottom and top layouts are vertical layouts, as they hold the vertical pin widgets.
*/
const auto horizontalLayoutHeight = ((horizontalPinWidgetHeight + PinWidget::WIDTH_SPACING) * pinCountPerLayout
+ PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING);
const auto verticalLayoutWidth = ((verticalPinWidgetWidth + PinWidget::WIDTH_SPACING) * pinCountPerLayout
+ PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING);
const auto height = horizontalLayoutHeight + (verticalPinWidgetHeight * 2) + (
(
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
+ (PinWidget::MAXIMUM_LABEL_HEIGHT * 2)
+ (PinWidget::PIN_LABEL_SPACING * 3)
) * 2
);
const auto width = verticalLayoutWidth + (horizontalPinWidgetWidth * 2) + (
(
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::MAXIMUM_LABEL_WIDTH
+ PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH
+ (PinWidget::PIN_LABEL_SPACING * 2)
) * 2
);
this->topPinLayout->insertSpacing(0, horizontalPinWidgetWidth);
this->topPinLayout->addSpacing(horizontalPinWidgetWidth);
this->bottomPinLayout->insertSpacing(0, horizontalPinWidgetWidth);
this->bottomPinLayout->addSpacing(horizontalPinWidgetWidth);
this->leftPinLayout->setGeometry(QRect(
0,
verticalPinWidgetHeight,
horizontalPinWidgetWidth,
horizontalLayoutHeight
));
this->bodyWidget->setFixedSize(verticalLayoutWidth, horizontalLayoutHeight);
this->rightPinLayout->setGeometry(QRect(
horizontalLayoutHeight + horizontalPinWidgetWidth,
verticalPinWidgetHeight,
horizontalPinWidgetWidth,
horizontalLayoutHeight
));
const auto pinWidgetLayoutMargin = PinWidget::PIN_WIDGET_LAYOUT_PADDING / 2;
this->topPinLayout->setContentsMargins(
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin,
0
);
this->bottomPinLayout->setContentsMargins(
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin,
0
);
this->leftPinLayout->setContentsMargins(
0,
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin
);
this->rightPinLayout->setContentsMargins(
0,
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin
);
this->setFixedSize(width, height);
// Set the fixed size and center the widget
this->setGeometry(
(parent->width() / 2) - (width / 2),
(parent->height() / 2) - (height / 2),
width,
height
);
}
this->bodyWidget = new BodyWidget(this);
this->layout->addLayout(this->topPinLayout);
this->horizontalLayout->addLayout(this->leftPinLayout);
this->horizontalLayout->addWidget(this->bodyWidget);
this->horizontalLayout->addLayout(this->rightPinLayout);
this->layout->addLayout(this->horizontalLayout);
this->layout->addLayout(this->bottomPinLayout);
this->setLayout(this->layout);
void QuadFlatPackageWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
// Layout sizing, positioning and padding
const auto verticalPinWidgetHeight = PinWidget::MAXIMUM_VERTICAL_HEIGHT;
const auto verticalPinWidgetWidth = PinWidget::MAXIMUM_VERTICAL_WIDTH;
const auto horizontalPinWidgetHeight = PinWidget::MAXIMUM_HORIZONTAL_HEIGHT;
const auto horizontalPinWidgetWidth = PinWidget::MAXIMUM_HORIZONTAL_WIDTH;
void QuadFlatPackageWidget::drawWidget(QPainter& painter) {
static auto pinNameFont = QFont("'Ubuntu', sans-serif");
static auto pinDirectionFont = pinNameFont;
pinNameFont.setPixelSize(11);
pinDirectionFont.setPixelSize(9);
/*
* Horizontal layouts are the right and left pin layouts - the ones that hold horizontal pin widgets.
* The bottom and top layouts are vertical layouts, as they hold the vertical pin widgets.
*/
const auto horizontalLayoutHeight = ((horizontalPinWidgetHeight + PinWidget::WIDTH_SPACING) * pinCountPerLayout
+ PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING);
static const auto lineColor = QColor(0x4F, 0x4F, 0x4F);
static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA);
static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D);
static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA);
const auto verticalLayoutWidth = ((verticalPinWidgetWidth + PinWidget::WIDTH_SPACING) * pinCountPerLayout
+ PinWidget::PIN_WIDGET_LAYOUT_PADDING - PinWidget::WIDTH_SPACING);
static const auto inDirectionText = QString("IN");
static const auto outDirectionText = QString("OUT");
const auto height = horizontalLayoutHeight + (verticalPinWidgetHeight * 2) + (
(
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
+ (PinWidget::MAXIMUM_LABEL_HEIGHT * 2)
+ (PinWidget::PIN_LABEL_SPACING * 3)
) * 2
);
const auto width = verticalLayoutWidth + (horizontalPinWidgetWidth * 2) + (
(
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
+ PinWidget::MAXIMUM_LABEL_WIDTH
+ PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH
+ (PinWidget::PIN_LABEL_SPACING * 2)
) * 2
);
for (const auto* pinWidget : this->pinWidgets) {
const auto pinGeoPosition = pinWidget->pos();
const auto& pinState = pinWidget->getPinState();
const auto pinStateChanged = pinWidget->hasPinStateChanged();
this->topPinLayout->insertSpacing(0, horizontalPinWidgetWidth);
this->topPinLayout->addSpacing(horizontalPinWidgetWidth);
painter.setFont(pinNameFont);
this->bottomPinLayout->insertSpacing(0, horizontalPinWidgetWidth);
this->bottomPinLayout->addSpacing(horizontalPinWidgetWidth);
if (pinWidget->position == Position::LEFT) {
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2),
pinGeoPosition.x(),
pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2)
));
this->leftPinLayout->setGeometry(QRect(
0,
verticalPinWidgetHeight,
horizontalPinWidgetWidth,
horizontalLayoutHeight
));
this->bodyWidget->setFixedSize(verticalLayoutWidth, horizontalLayoutHeight);
this->rightPinLayout->setGeometry(QRect(
horizontalLayoutHeight + horizontalPinWidgetWidth,
verticalPinWidgetHeight,
horizontalPinWidgetWidth,
horizontalLayoutHeight
));
const auto pinWidgetLayoutMargin = PinWidget::PIN_WIDGET_LAYOUT_PADDING / 2;
this->topPinLayout->setContentsMargins(
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin,
0
);
this->bottomPinLayout->setContentsMargins(
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin,
0
);
this->leftPinLayout->setContentsMargins(
0,
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin
);
this->rightPinLayout->setContentsMargins(
0,
pinWidgetLayoutMargin,
0,
pinWidgetLayoutMargin
);
this->setFixedSize(width, height);
// Set the fixed size and center the widget
this->setGeometry(
(parent->width() / 2) - (width / 2),
(parent->height() / 2) - (height / 2),
width,
height
);
}
void QuadFlatPackageWidget::paintEvent(QPaintEvent* event) {
auto painter = QPainter(this);
this->drawWidget(painter);
}
void QuadFlatPackageWidget::drawWidget(QPainter& painter) {
static auto pinNameFont = QFont("'Ubuntu', sans-serif");
static auto pinDirectionFont = pinNameFont;
pinNameFont.setPixelSize(11);
pinDirectionFont.setPixelSize(9);
static const auto lineColor = QColor(0x4F, 0x4F, 0x4F);
static const auto pinNameFontColor = QColor(0xA6, 0xA7, 0xAA);
static const auto pinDirectionFontColor = QColor(0x8A, 0x8A, 0x8D);
static const auto pinChangedFontColor = QColor(0x4D, 0x7B, 0xBA);
static const auto inDirectionText = QString("IN");
static const auto outDirectionText = QString("OUT");
for (const auto* pinWidget : this->pinWidgets) {
const auto pinGeoPosition = pinWidget->pos();
const auto& pinState = pinWidget->getPinState();
const auto pinStateChanged = pinWidget->hasPinStateChanged();
painter.setFont(pinNameFont);
if (pinWidget->position == Position::LEFT) {
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2),
pinGeoPosition.x(),
pinGeoPosition.y() + (PinWidget::MAXIMUM_HORIZONTAL_HEIGHT / 2)
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - PinWidget::MAXIMUM_LABEL_WIDTH
- (PinWidget::PIN_LABEL_SPACING * 2),
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(pinDirectionFontColor);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH - PinWidget::MAXIMUM_LABEL_WIDTH
- (PinWidget::PIN_LABEL_SPACING * 3) - PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH
- PinWidget::MAXIMUM_LABEL_WIDTH - (PinWidget::PIN_LABEL_SPACING * 2),
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
pinWidget->pinNameLabelText
);
}
} else if (pinWidget->position == Position::RIGHT) {
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH + PinWidget::PIN_LABEL_LONG_LINE_LENGTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
));
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() - PinWidget::PIN_LABEL_LONG_LINE_LENGTH
- PinWidget::MAXIMUM_LABEL_WIDTH - (PinWidget::PIN_LABEL_SPACING * 3)
- PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::RIGHT) {
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH + 8,
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
));
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(pinDirectionFontColor);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH + PinWidget::MAXIMUM_LABEL_WIDTH
+ (PinWidget::PIN_LABEL_SPACING * 3),
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH + 8,
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::TOP) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y()
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
- pinDirectionLabelLineLength,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength
- (PinWidget::MAXIMUM_LABEL_HEIGHT * 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
pinWidget->pinNameLabelText
);
}
} else if (pinWidget->position == Position::BOTTOM) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + PinWidget::MAXIMUM_HORIZONTAL_WIDTH
+ PinWidget::PIN_LABEL_LONG_LINE_LENGTH + PinWidget::MAXIMUM_LABEL_WIDTH
+ (PinWidget::PIN_LABEL_SPACING * 3),
pinGeoPosition.y() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_HEIGHT / 2),
PinWidget::MAXIMUM_PIN_DIRECTION_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::TOP) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT,
pinGeoPosition.y() - pinNameLabelLineLength,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength
pinGeoPosition.y()
));
painter.setPen(pinDirectionFontColor);
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
- pinDirectionLabelLineLength,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - PinWidget::MAXIMUM_LABEL_HEIGHT
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() - pinNameLabelLineLength - pinDirectionLabelLineLength
- (PinWidget::MAXIMUM_LABEL_HEIGHT * 2),
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
} else if (pinWidget->position == Position::BOTTOM) {
painter.setPen(lineColor);
const auto pinNameLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_LONG_LINE_LENGTH
: PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
);
const auto pinDirectionLabelLineLength = (pinWidget->getPinNumber() % 2 == 0 ?
PinWidget::PIN_LABEL_SHORT_LINE_LENGTH
: PinWidget::PIN_LABEL_LONG_LINE_LENGTH
);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
));
painter.setPen(pinStateChanged ? pinChangedFontColor : pinNameFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinWidget->pinNameLabelText
);
if (pinState.has_value() && pinState->ioDirection.has_value()) {
painter.setFont(pinDirectionFont);
painter.setPen(lineColor);
painter.drawLine(QLine(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
+ PinWidget::MAXIMUM_LABEL_HEIGHT,
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength
));
painter.setPen(pinDirectionFontColor);
painter.drawText(
QRect(
pinGeoPosition.x() + (PinWidget::MAXIMUM_VERTICAL_WIDTH / 2)
- (PinWidget::MAXIMUM_LABEL_WIDTH / 2),
pinGeoPosition.y() + PinWidget::MAXIMUM_VERTICAL_HEIGHT + pinNameLabelLineLength
+ PinWidget::MAXIMUM_LABEL_HEIGHT + pinDirectionLabelLineLength,
PinWidget::MAXIMUM_LABEL_WIDTH,
PinWidget::MAXIMUM_LABEL_HEIGHT
),
Qt::AlignCenter,
pinState->ioDirection == TargetPinState::IoDirection::INPUT ? inDirectionText : outDirectionText
);
}
}
}
}

View File

@@ -4,90 +4,90 @@
#include "src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.hpp"
using namespace Bloom;
using namespace Bloom::Widgets::InsightTargetWidgets;
namespace Bloom::Widgets::InsightTargetWidgets
{
using Bloom::Targets::TargetState;
using Bloom::Targets::TargetState;
TargetPackageWidget::TargetPackageWidget(
Targets::TargetVariant targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent), targetVariant(std::move(targetVariant)), insightWorker(insightWorker) {
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetStateUpdated,
this,
&TargetPackageWidget::onTargetStateChanged
);
TargetPackageWidget::TargetPackageWidget(
Targets::TargetVariant targetVariant,
InsightWorker& insightWorker,
QWidget* parent
): QWidget(parent), targetVariant(std::move(targetVariant)), insightWorker(insightWorker) {
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetStateUpdated,
this,
&TargetPackageWidget::onTargetStateChanged
);
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetRegistersWritten,
this,
&TargetPackageWidget::onRegistersWritten
);
QObject::connect(
&(this->insightWorker),
&InsightWorker::targetRegistersWritten,
this,
&TargetPackageWidget::onRegistersWritten
);
this->setDisabled(true);
}
this->setDisabled(true);
}
void TargetPackageWidget::refreshPinStates(std::optional<std::function<void(void)>> callback) {
auto* refreshTask = new RefreshTargetPinStates(this->targetVariant.id);
QObject::connect(
refreshTask,
&RefreshTargetPinStates::targetPinStatesRetrieved,
this,
&TargetPackageWidget::updatePinStates
);
if (callback.has_value()) {
void TargetPackageWidget::refreshPinStates(std::optional<std::function<void(void)>> callback) {
auto* refreshTask = new RefreshTargetPinStates(this->targetVariant.id);
QObject::connect(
refreshTask,
&InsightWorkerTask::completed,
&RefreshTargetPinStates::targetPinStatesRetrieved,
this,
callback.value()
&TargetPackageWidget::updatePinStates
);
if (callback.has_value()) {
QObject::connect(
refreshTask,
&InsightWorkerTask::completed,
this,
callback.value()
);
}
this->insightWorker.queueTask(refreshTask);
}
this->insightWorker.queueTask(refreshTask);
}
void TargetPackageWidget::updatePinStates(const Targets::TargetPinStateMappingType& pinStatesByNumber) {
for (auto& pinWidget : this->pinWidgets) {
auto pinNumber = pinWidget->getPinNumber();
if (pinStatesByNumber.contains(pinNumber)) {
pinWidget->updatePinState(pinStatesByNumber.at(pinNumber));
}
}
void TargetPackageWidget::updatePinStates(const Targets::TargetPinStateMappingType& pinStatesByNumber) {
for (auto& pinWidget : this->pinWidgets) {
auto pinNumber = pinWidget->getPinNumber();
if (pinStatesByNumber.contains(pinNumber)) {
pinWidget->updatePinState(pinStatesByNumber.at(pinNumber));
this->update();
}
void TargetPackageWidget::onTargetStateChanged(TargetState newState) {
this->targetState = newState;
if (newState == TargetState::RUNNING) {
this->setDisabled(true);
} else if (newState == TargetState::STOPPED) {
this->refreshPinStates([this] {
if (this->targetState == TargetState::STOPPED) {
this->setDisabled(false);
}
});
}
}
this->update();
}
void TargetPackageWidget::onTargetStateChanged(TargetState newState) {
this->targetState = newState;
if (newState == TargetState::RUNNING) {
this->setDisabled(true);
} else if (newState == TargetState::STOPPED) {
this->refreshPinStates([this] {
if (this->targetState == TargetState::STOPPED) {
this->setDisabled(false);
}
});
}
}
void TargetPackageWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters) {
if (this->targetState != TargetState::STOPPED) {
return;
}
// If a PORT register was just updated, refresh pin states.
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor.type == Targets::TargetRegisterType::PORT_REGISTER) {
this->refreshPinStates();
void TargetPackageWidget::onRegistersWritten(Targets::TargetRegisters targetRegisters) {
if (this->targetState != TargetState::STOPPED) {
return;
}
// If a PORT register was just updated, refresh pin states.
for (const auto& targetRegister : targetRegisters) {
if (targetRegister.descriptor.type == Targets::TargetRegisterType::PORT_REGISTER) {
this->refreshPinStates();
return;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More