diff --git a/core/src/command_args.cpp b/core/src/command_args.cpp new file mode 100644 index 00000000..c108f810 --- /dev/null +++ b/core/src/command_args.cpp @@ -0,0 +1,103 @@ +#include "command_args.h" + +int CommandArgsParser::parse(int argc, char* argv[]) { + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + // Check for long and short name arguments + if (!arg.rfind("--", 0)) { + arg = arg.substr(2); + } + else if (!arg.rfind("-", 0)) { + if (aliases.find(arg[1]) == aliases.end()) { + printf("Unknown argument\n"); + showHelp(); + return -1; + } + arg = aliases[arg[1]]; + } + else { + printf("Invalid argument\n"); + showHelp(); + return -1; + } + + // Make sure the argument exists + if (args.find(arg) == args.end()) { + printf("Unknown argument\n"); + showHelp(); + return -1; + } + + // Parse depending on type + CLIArg& carg = args[arg]; + + // If not void, make sure an argument is available and retrieve it + if (carg.type != CLI_ARG_TYPE_VOID && i + 1 >= argc) { + printf("Missing argument\n"); + showHelp(); + return -1; + } + + // Parse void since arg won't be needed + if (carg.type == CLI_ARG_TYPE_VOID) { + carg.bval = true; + continue; + } + + // Parse types that require parsing + arg = argv[++i]; + if (carg.type == CLI_ARG_TYPE_BOOL) { + // Enforce lower case + for (int i = 0; i < arg.size(); i++) { arg[i] = std::tolower(arg[i]); } + + if (arg == "true" || arg == "on" || arg == "1") { + carg.bval = true; + } + else if (arg == "false" || arg == "off" || arg == "0") { + carg.bval = true; + } + else { + printf("Invald argument, expected bool (true, false, on, off, 1, 0)\n"); + showHelp(); + return -1; + } + } + else if (carg.type == CLI_ARG_TYPE_INT) { + try { + carg.ival = std::stoi(arg); + } + catch (std::exception e) { + printf("Invald argument, failed to parse integer\n"); + showHelp(); + return -1; + } + } + else if (carg.type == CLI_ARG_TYPE_FLOAT) { + try { + carg.fval = std::stod(arg); + } + catch (std::exception e) { + printf("Invald argument, failed to parse float\n"); + showHelp(); + return -1; + } + } + else if (carg.type == CLI_ARG_TYPE_STRING) { + carg.sval = arg; + } + } + + return 0; +} + +void CommandArgsParser::showHelp() { + for (auto const& [ln, arg] : args) { + if (arg.alias) { + printf("-%c\t--%s\t\t%s\n", arg.alias, ln.c_str(), arg.description.c_str()); + } + else { + printf(" \t--%s\t\t%s\n", ln.c_str(), arg.description.c_str()); + } + } +} \ No newline at end of file diff --git a/core/src/command_args.h b/core/src/command_args.h new file mode 100644 index 00000000..272990df --- /dev/null +++ b/core/src/command_args.h @@ -0,0 +1,127 @@ +#pragma once +#include +#include +#include + +enum CLIArgType { + CLI_ARG_TYPE_INVALID, + CLI_ARG_TYPE_VOID, + CLI_ARG_TYPE_BOOL, + CLI_ARG_TYPE_INT, + CLI_ARG_TYPE_FLOAT, + CLI_ARG_TYPE_STRING +}; + +class CommandArgsParser; + +class CLIArg { +public: + CLIArg() { + type = CLI_ARG_TYPE_INVALID; + } + + CLIArg(char al, std::string desc) { + alias = al; + description = desc; + type = CLI_ARG_TYPE_VOID; + } + + CLIArg(char al, std::string desc, bool b) { + alias = al; + description = desc; + type = CLI_ARG_TYPE_BOOL; + bval = b; + } + + CLIArg(char al, std::string desc, int i) { + alias = al; + description = desc; + type = CLI_ARG_TYPE_INT; + ival = i; + } + + CLIArg(char al, std::string desc, double f) { + alias = al; + description = desc; + type = CLI_ARG_TYPE_FLOAT; + fval = f; + } + + CLIArg(char al, std::string desc, std::string s) { + printf("String const called\n"); + alias = al; + description = desc; + type = CLI_ARG_TYPE_STRING; + sval = s; + } + + CLIArg(char al, std::string desc, const char* s) { + printf("String const called\n"); + alias = al; + description = desc; + type = CLI_ARG_TYPE_STRING; + sval = s; + } + + operator bool() const { + if (type != CLI_ARG_TYPE_BOOL && type != CLI_ARG_TYPE_VOID) { throw std::runtime_error("Not a bool"); } + return bval; + } + + operator int() const { + if (type != CLI_ARG_TYPE_INT) { throw std::runtime_error("Not an int"); } + return ival; + } + + operator float() const { + if (type != CLI_ARG_TYPE_FLOAT) { throw std::runtime_error("Not a float"); } + return (float)fval; + } + + operator double() const { + if (type != CLI_ARG_TYPE_FLOAT) { throw std::runtime_error("Not a float"); } + return fval; + } + + operator std::string() const { + if (type != CLI_ARG_TYPE_STRING) { throw std::runtime_error("Not a string"); } + return sval; + } + + friend CommandArgsParser; + + CLIArgType type; + char alias; + std::string description; + +private: + bool bval; + int ival; + std::string sval; + double fval; +}; + +class CommandArgsParser { +public: + void define(char shortName, std::string name, std::string desc) { + args[name] = CLIArg(shortName, desc); + aliases[shortName] = name; + } + + template + void define(char shortName, std::string name, std::string desc, T defValue) { + args[name] = CLIArg(shortName, desc, defValue); + aliases[shortName] = name; + } + + int parse(int argc, char* argv[]); + void showHelp(); + + CLIArg operator[](std::string name) { + return args[name]; + } + +private: + std::map args; + std::map aliases; +}; \ No newline at end of file diff --git a/core/src/core.cpp b/core/src/core.cpp index cc0db1f6..2dd131a8 100644 --- a/core/src/core.cpp +++ b/core/src/core.cpp @@ -184,6 +184,7 @@ int sdrpp_main(int argc, char* argv[]) { // Themes defConfig["theme"] = "Dark"; + defConfig["uiScale"] = 1.0f; defConfig["modules"] = json::array(); @@ -279,6 +280,9 @@ int sdrpp_main(int argc, char* argv[]) { core::configManager.conf["moduleInstances"][_name] = newMod; } + // Load UI scaling + style::uiScale = core::configManager.conf["uiScale"]; + core::configManager.release(true); if (options::opts.serverMode) { return server::main(); } diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 5b910aa1..7625e739 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -450,7 +450,7 @@ void MainWindow::draw() { // Handle menu resize ImVec2 winSize = ImGui::GetWindowSize(); ImVec2 mousePos = ImGui::GetMousePos(); - if (!lockWaterfallControls) { + if (!lockWaterfallControls && showMenu) { float curY = ImGui::GetCursorPosY(); bool click = ImGui::IsMouseClicked(ImGuiMouseButton_Left); bool down = ImGui::IsMouseDown(ImGuiMouseButton_Left); @@ -639,6 +639,7 @@ void MainWindow::draw() { ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - (ImGui::CalcTextSize("Min").x / 2.0)); ImGui::TextUnformatted("Min"); ImGui::SetCursorPosX((ImGui::GetWindowSize().x / 2.0) - 10 * style::uiScale); + ImGui::SetItemUsingMouseWheel(); if (ImGui::VSliderFloat("##_9_", wfSliderSize, &fftMin, 0.0, -160.0f, "")) { fftMin = std::min(fftMax - 10, fftMin); core::configManager.acquire(); diff --git a/core/src/gui/menus/display.cpp b/core/src/gui/menus/display.cpp index 91853bca..bb5fbfc0 100644 --- a/core/src/gui/menus/display.cpp +++ b/core/src/gui/menus/display.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace displaymenu { bool showWaterfall; @@ -18,6 +19,10 @@ namespace displaymenu { std::string colorMapAuthor = ""; int selectedWindow = 0; int fftRate = 20; + int uiScaleId = 0; + bool restartRequired = false; + + OptionList uiScales; const int FFTSizes[] = { 524288, @@ -85,6 +90,17 @@ namespace displaymenu { selectedWindow = std::clamp((int)core::configManager.conf["fftWindow"], 0, _FFT_WINDOW_COUNT - 1); gui::mainWindow.setFFTWindow(selectedWindow); + + // Define and load UI scales + uiScales.define(0.5f, "50%", 0.5f); + uiScales.define(1.0f, "100%", 1.0f); + uiScales.define(1.5f, "150%", 1.5f); + uiScales.define(2.0f, "200%", 2.0f); + uiScales.define(2.5f, "250%", 2.5f); + uiScales.define(3.0f, "300%", 3.0f); + uiScales.define(3.5f, "350%", 3.5f); + uiScales.define(4.0f, "400%", 4.0f); + uiScaleId = uiScales.valueId(style::uiScale); } void draw(void* ctx) { @@ -112,6 +128,15 @@ namespace displaymenu { core::configManager.release(true); } + ImGui::LeftLabel("High-DPI Scaling"); + ImGui::FillWidth(); + if (ImGui::Combo("##sdrpp_ui_scale", &uiScaleId, uiScales.txt)) { + core::configManager.acquire(); + core::configManager.conf["uiScale"] = uiScales[uiScaleId]; + core::configManager.release(true); + restartRequired = true; + } + ImGui::LeftLabel("FFT Framerate"); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) { @@ -153,5 +178,9 @@ namespace displaymenu { } ImGui::Text("Color map Author: %s", colorMapAuthor.c_str()); } + + if (restartRequired) { + ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Restart required."); + } } } \ No newline at end of file