Start moving the config view functionality into the control panel. The

current config is now displayed there.
This commit is contained in:
David Given
2025-09-12 00:20:46 +02:00
parent e3d7fa69d8
commit b306c7063b
7 changed files with 382 additions and 109 deletions

View File

@@ -168,7 +168,7 @@ void ConfigView::drawContent()
if (ImGui::Button("fluxengine.view.config.rescan"_lang))
probeConfig();
std::set<int> applicableOptions{SOURCESINK};
std::set<int> applicableOptions{ANY_SOURCESINK};
if ((std::string)driveSetting == DEVICE_MANUAL)
{
auto manualDevicePathSetting = DynamicSetting<std::fs::path>(

View File

@@ -37,6 +37,3 @@ private:
std::shared_ptr<DeviceMap> _devices;
};
static constexpr std::string DEVICE_MANUAL = "manual";
static constexpr std::string DEVICE_FLUXFILE = "fluxfile";

View File

@@ -13,6 +13,7 @@
#include "lib/decoders/decoders.h"
#include "lib/imagewriter/imagewriter.h"
#include "lib/algorithms/readerwriter.h"
#include "lib/usb/usbfinder.h"
#include "arch/arch.h"
#include "globals.h"
#include "datastore.h"
@@ -22,6 +23,8 @@
#include <deque>
#include <semaphore>
using hex::operator""_lang;
static std::shared_ptr<const DiskFlux> diskFlux;
static std::deque<std::function<void()>> pendingTasks;
@@ -30,6 +33,7 @@ static std::thread workerThread;
static std::atomic<bool> busy;
static bool configurationValid;
static std::map<std::string, Datastore::Device> devices;
static std::map<CylinderHead, std::shared_ptr<const TrackInfo>>
physicalCylinderLayouts;
static std::map<CylinderHeadSector, std::shared_ptr<const Sector>>
@@ -112,6 +116,14 @@ static void workerThread_cb()
}
}
void Datastore::runOnWorkerThread(std::function<void()> callback)
{
const std::lock_guard<std::mutex> lock(pendingTasksMutex);
pendingTasks.push_back(callback);
if (workerThread.get_id() == std::thread::id())
workerThread = std::move(std::thread(workerThread_cb));
}
void Datastore::init()
{
runOnWorkerThread(
@@ -128,6 +140,8 @@ void Datastore::init()
});
});
probeDevices();
Events::SeekToSectorViaPhysicalLocation::subscribe(
[](CylinderHeadSector physicalLocation)
{
@@ -180,6 +194,11 @@ bool Datastore::isConfigurationValid()
return isConfigurationValid;
}
const std::map<std::string, Datastore::Device>& Datastore::getDevices()
{
return devices;
}
const std::map<CylinderHead, std::shared_ptr<const TrackInfo>>&
Datastore::getphysicalCylinderLayouts()
{
@@ -214,14 +233,6 @@ std::optional<unsigned> Datastore::findBlockByLogicalLocation(
return it->second;
}
void Datastore::runOnWorkerThread(std::function<void()> callback)
{
const std::lock_guard<std::mutex> lock(pendingTasksMutex);
pendingTasks.push_back(callback);
if (workerThread.get_id() == std::thread::id())
workerThread = std::move(std::thread(workerThread_cb));
}
template <typename T>
static T readSetting(const std::string& leaf, const T defaultValue)
{
@@ -256,6 +267,30 @@ static void rebuildDiskFluxIndices()
}
}
void Datastore::probeDevices()
{
Datastore::runOnWorkerThread(
[]
{
hex::log::debug("probing USB");
auto usbDevices = findUsbDevices();
hex::TaskManager::doLater(
[usbDevices]
{
devices.clear();
devices[DEVICE_FLUXFILE] = {
nullptr, "fluxengine.view.config.fluxfile"_lang};
devices[DEVICE_MANUAL] = {
nullptr, "fluxengine.view.config.manual"_lang};
for (auto it : usbDevices)
devices["#" + it->serial] = {it,
fmt::format(
"{}: {}", getDeviceName(it->type), it->serial)};
});
});
}
void Datastore::rebuildConfiguration()
{
hex::TaskManager::doLaterOnce(

View File

@@ -5,14 +5,28 @@
#include "lib/data/layout.h"
class DiskFlux;
class CandidateDevice;
static constexpr std::string DEVICE_MANUAL = "manual";
static constexpr std::string DEVICE_FLUXFILE = "fluxfile";
class Datastore
{
public:
struct Device
{
std::shared_ptr<CandidateDevice> coreDevice;
std::string label;
};
public:
static void init();
static bool isBusy();
static bool isConfigurationValid();
static void probeDevices();
static const std::map<std::string, Device>& getDevices();
static const std::map<CylinderHead, std::shared_ptr<const TrackInfo>>&
getphysicalCylinderLayouts();
static std::shared_ptr<const Sector> findSectorByPhysicalLocation(

View File

@@ -23,3 +23,12 @@ namespace Events
EVENT_DEF(SeekToSectorViaPhysicalLocation, CylinderHeadSector);
EVENT_DEF(SeekToTrackViaPhysicalLocation, CylinderHead);
}
template <typename K, typename V>
inline const V& findOrDefault(const std::map<K, V>& map, const K& key, const V& defaultValue = V())
{
auto it = map.find(key);
if (it == map.end())
return defaultValue;
return it->second;
}

View File

@@ -16,6 +16,12 @@
"fluxengine.summary.controls.read": "Read from device",
"fluxengine.summary.controls.write": "Write to device",
"fluxengine.summary.controls.stop": "Stop",
"fluxengine.view.summary.edit": "Edit",
"fluxengine.view.summary.highDensity": "High density",
"fluxengine.view.summary.yes": "yes",
"fluxengine.view.summary.no": "no",
"fluxengine.view.summary.format": "Format",
"fluxengine.view.summary.variations": "Variant",
"fluxengine.view.image.name": "FluxEngine sector map"
}

View File

@@ -8,6 +8,7 @@
#include "lib/config/config.h"
#include "lib/data/flux.h"
#include "lib/data/sector.h"
#include "lib/config/proto.h"
#include "globals.h"
#include "summaryview.h"
#include "datastore.h"
@@ -57,6 +58,179 @@ static void saveSectorImage()
fs::openFileBrowser(fs::DialogMode::Save, {}, Datastore::writeImage);
}
static void showOptions() {}
static void emitOptions(const OptionsMap& options,
const ConfigProto* config,
const std::set<int>& applicableOptions)
{
for (auto& it : config->option())
{
if (!applicableOptions.empty() &&
std::ranges::none_of(it.applicability(),
[&](int item)
{
return applicableOptions.contains(item);
}))
continue;
ImGui::Text(fmt::format("{}: {}",
wolv::util::capitalizeString(it.comment()),
options.contains(it.name()) ? "fluxengine.view.summary.yes"_lang
: "fluxengine.view.summary.no"_lang));
}
for (auto& it : config->option_group())
{
if (!applicableOptions.empty() &&
std::ranges::none_of(it.applicability(),
[&](int item)
{
return applicableOptions.contains(item);
}))
continue;
std::string comment = it.comment();
if (comment == "$formats")
comment = (std::string) "fluxengine.view.summary.variations"_lang;
const OptionProto* selectedOption = nullptr;
if (it.name().empty())
{
for (auto& ot : it.option())
if (options.contains(ot.name()))
selectedOption = &ot;
}
else
{
auto value = findOrDefault(options, it.name());
for (auto& ot : it.option())
if (ot.name() == value)
selectedOption = &ot;
}
if (!selectedOption)
for (auto& ot : it.option())
if (ot.set_by_default())
selectedOption = &ot;
ImGui::Text(fmt::format("{}:", comment));
ImGui::SameLine();
if (selectedOption)
ImGui::TextWrapped(
wolv::util::capitalizeString(selectedOption->comment())
.c_str());
else
ImGui::TextWrapped("***bad***");
}
}
static void drawDeviceBox()
{
if (ImGuiExt::BeginSubWindow("Device",
nullptr,
ImGui::GetContentRegionAvail(),
ImGuiChildFlags_None))
{
ON_SCOPE_EXIT
{
ImGuiExt::EndSubWindow();
};
/* Device name */
std::set<int> applicableOptions = {ANY_SOURCESINK};
auto deviceName = hex::ContentRegistry::Settings::read<std::string>(
FLUXENGINE_CONFIG, "fluxengine.settings.device", DEVICE_FLUXFILE);
auto device = findOrDefault(Datastore::getDevices(),
deviceName,
{.label = "No device configured"});
if (deviceName == DEVICE_FLUXFILE)
{
ImGui::Text(fmt::format("{}: {}",
device.label,
hex::ContentRegistry::Settings::read<std::fs::path>(
FLUXENGINE_CONFIG,
"fluxengine.settings.fluxfile",
"no file configured")
.filename()
.string()));
applicableOptions.insert(FLUXFILE_SOURCESINK);
}
else if (deviceName == DEVICE_MANUAL)
{
ImGui::Text(fmt::format("Greaseweazle: {}",
hex::ContentRegistry::Settings::read<std::string>(
FLUXENGINE_CONFIG,
"fluxengine.settings.manualDevicePath",
"no path configured")));
applicableOptions.insert(MANUAL_SOURCESINK);
applicableOptions.insert(HARDWARE_SOURCESINK);
}
else
{
ImGui::Text(device.label.c_str());
applicableOptions.insert(HARDWARE_SOURCESINK);
}
/* Other options */
auto globalOptions =
stringToOptions(hex::ContentRegistry::Settings::read<std::string>(
FLUXENGINE_CONFIG, "fluxengine.settings.globalSettings", ""));
emitOptions(
globalOptions, formats.at("_global_options"), applicableOptions);
ImGui::Button(
fmt::format("{}##device", "fluxengine.view.summary.edit"_lang)
.c_str());
}
}
void drawFormatBox()
{
if (ImGuiExt::BeginSubWindow("Format",
nullptr,
ImGui::GetContentRegionAvail(),
ImGuiChildFlags_None))
{
ON_SCOPE_EXIT
{
ImGuiExt::EndSubWindow();
};
/* Format name */
auto formatName =
hex::ContentRegistry::Settings::read<std::string>(FLUXENGINE_CONFIG,
"fluxengine.settings.format.selected",
DEVICE_FLUXFILE);
auto format = findOrDefault(formats, formatName);
if (!format)
{
ImGui::Text("***bad***");
return;
}
ImGui::Text(fmt::format("{}:", "fluxengine.view.summary.format"_lang));
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Text(format->shortname());
ImGui::Text(format->comment());
ImGui::EndGroup();
auto formatOptions = stringToOptions(
hex::ContentRegistry::Settings::read<std::string>(FLUXENGINE_CONFIG,
fmt::format("fluxengine.settings.{}", formatName),
""));
emitOptions(formatOptions, format, {});
ImGui::Button(
fmt::format("{}##format", "fluxengine.view.summary.edit"_lang)
.c_str());
}
}
void SummaryView::drawContent()
{
auto diskFlux = Datastore::getDiskFlux();
@@ -199,9 +373,9 @@ void SummaryView::drawContent()
}
ImGui::SetCursorPosY(
ImGui::GetContentRegionAvail().y - ImGui::GetFontSize() * 3);
if (ImGui::BeginTable("controlPanel",
7,
ImGui::GetContentRegionAvail().y - ImGui::GetFontSize() * 6);
if (ImGui::BeginTable("controlPanelOuter",
3,
ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoClip))
{
ON_SCOPE_EXIT
@@ -209,107 +383,145 @@ void SummaryView::drawContent()
ImGui::EndTable();
};
bool busy = Datastore::isBusy() || !Datastore::isConfigurationValid();
bool hasImage = diskFlux && diskFlux->image;
auto majorButtonWidth = ImGui::GetFontSize() * 10;
auto sideWidth = ImGui::GetFontSize() * 20;
ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, sideWidth);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, majorButtonWidth);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, majorButtonWidth * 1.5);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, majorButtonWidth);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
"", ImGuiTableColumnFlags_WidthFixed, sideWidth);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
if (MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_DEVICE_FLOPPY, "Read disk"),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy))
Datastore::beginRead();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_VS_FOLDER_OPENED, "Load sector image"),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_VS_SAVE_AS, "Write disk").c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !hasImage);
drawDeviceBox();
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_REPEAT, "Reread bad tracks").c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy);
ImGui::TableNextColumn();
ImGui::Text(ICON_VS_ARROW_RIGHT);
ImGui::TableNextColumn();
if (MaybeDisabledButton(
fmt::format("{} {}", ICON_VS_SAVE_ALL, "Save sector image")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !hasImage))
saveSectorImage();
ImGui::TableNextColumn();
ImGui::Text(ICON_VS_ARROW_RIGHT);
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_DOWNLOAD, "Save flux file").c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !diskFlux);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
if (MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_UPLOAD, "Load flux file").c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy))
loadFluxFile();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_VS_NEW_FILE, "Create blank image")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy);
}
{
auto size = ImVec2(ImGui::GetWindowSize().x * 0.6, 0);
ImGui::SetCursorPos({ImGui::GetWindowSize().x / 2 - size.x / 2,
ImGui::GetWindowSize().y - ImGui::GetFontSize() * 1.5f});
auto red = ImGuiExt::GetCustomColorU32(ImGuiCustomCol_LoggerError);
auto text = ImGui::GetColorU32(ImGuiCol_Text);
auto redHover = ImMixU32(red, text, 32);
auto redPressed = ImMixU32(red, text, 64);
ImGui::PushStyleColor(ImGuiCol_Button, red);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, redHover);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, redPressed);
ON_SCOPE_EXIT
if (ImGuiExt::BeginSubWindow("Controls",
nullptr,
ImGui::GetContentRegionAvail(),
ImGuiChildFlags_None))
{
ImGui::PopStyleColor(3);
};
ON_SCOPE_EXIT
{
ImGuiExt::EndSubWindow();
};
if (ImGui::BeginTable("controlPanel",
5,
ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_NoClip))
{
ON_SCOPE_EXIT
{
ImGui::EndTable();
};
if (MaybeDisabledButton(fmt::format("{} {}",
ICON_TA_CANCEL,
"fluxengine.summary.controls.stop"_lang),
size,
!Datastore::isBusy()))
Datastore::stop();
bool busy =
Datastore::isBusy() || !Datastore::isConfigurationValid();
bool hasImage = diskFlux && diskFlux->image;
auto majorButtonWidth = ImGui::GetFontSize() * 10;
ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, majorButtonWidth);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn(
"", ImGuiTableColumnFlags_WidthFixed, majorButtonWidth);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (MaybeDisabledButton(
fmt::format(
"{} {}", ICON_TA_DEVICE_FLOPPY, "Read disk"),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy))
Datastore::beginRead();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format(
"{} {}", ICON_VS_FOLDER_OPENED, "Load sector image"),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_VS_SAVE_AS, "Write disk").c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !hasImage);
ImGui::TableNextRow();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_REPEAT, "Reread bad tracks")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy);
ImGui::TableNextColumn();
ImGui::Text(ICON_VS_ARROW_RIGHT);
ImGui::TableNextColumn();
if (MaybeDisabledButton(
fmt::format(
"{} {}", ICON_VS_SAVE_ALL, "Save sector image")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !hasImage))
saveSectorImage();
ImGui::TableNextColumn();
ImGui::Text(ICON_VS_ARROW_RIGHT);
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_DOWNLOAD, "Save flux file")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy || !diskFlux);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (MaybeDisabledButton(
fmt::format("{} {}", ICON_TA_UPLOAD, "Load flux file")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy))
loadFluxFile();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
MaybeDisabledButton(
fmt::format("{} {}", ICON_VS_NEW_FILE, "Create blank image")
.c_str(),
ImVec2(ImGui::GetContentRegionAvail().x, 0),
busy);
}
{
auto size = ImVec2(ImGui::GetWindowSize().x * 0.6, 0);
ImGui::SetCursorPos({ImGui::GetWindowSize().x / 2 - size.x / 2,
ImGui::GetWindowSize().y - ImGui::GetFontSize() * 1.5f});
auto red =
ImGuiExt::GetCustomColorU32(ImGuiCustomCol_LoggerError);
auto text = ImGui::GetColorU32(ImGuiCol_Text);
auto redHover = ImMixU32(red, text, 32);
auto redPressed = ImMixU32(red, text, 64);
ImGui::PushStyleColor(ImGuiCol_Button, red);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, redHover);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, redPressed);
ON_SCOPE_EXIT
{
ImGui::PopStyleColor(3);
};
if (MaybeDisabledButton(
fmt::format("{} {}",
ICON_TA_CANCEL,
"fluxengine.summary.controls.stop"_lang),
size,
!Datastore::isBusy()))
Datastore::stop();
}
}
ImGui::TableNextColumn();
drawFormatBox();
}
}
}