diff --git a/src/ngscopeclient/MainWindow.cpp b/src/ngscopeclient/MainWindow.cpp index d5348a38..01df36ce 100644 --- a/src/ngscopeclient/MainWindow.cpp +++ b/src/ngscopeclient/MainWindow.cpp @@ -115,8 +115,8 @@ static void MainWindow_OnChangedViewport([[maybe_unused]] ImGuiViewport *vp) #define DBG_SUFFIX "" #endif -MainWindow::MainWindow(shared_ptr queue) - : VulkanWindow("ngscopeclient " NGSCOPECLIENT_VERSION " " DBG_SUFFIX SAN_SUFFIX, queue) +MainWindow::MainWindow(shared_ptr queue, bool maximized, bool restored) + : VulkanWindow("ngscopeclient " NGSCOPECLIENT_VERSION " " DBG_SUFFIX SAN_SUFFIX, queue, maximized, restored) , m_showDemo(false) , m_nextWaveformGroup(1) , m_toolbarIconSize(0) @@ -610,7 +610,7 @@ void MainWindow::ResetStyle() style.FontSizeBase = oldStyle.FontSizeBase; style.FontScaleMain = oldStyle.FontScaleMain; style.FontScaleDpi = oldStyle.FontScaleDpi; - style.ScaleAllSizes(style.FontScaleDpi); + style.ScaleAllSizes(VulkanWindow::m_forceDPIScaling ? VulkanWindow::m_forcedUIScale : style.FontScaleDpi); switch(m_session.GetPreferences().GetEnumRaw("Appearance.General.theme")) { @@ -2604,6 +2604,12 @@ bool MainWindow::LoadUIConfiguration(int version, const YAML::Node& node) m_pendingHeight = window["height"].as(); m_softwareResizeRequested = true; } + // Load trace alpha + if(window["traceAlpha"]) + m_traceAlpha = window["traceAlpha"].as(); + // Load persistance s + if(window["persistenceDecay"]) + m_persistenceDecay = window["persistenceDecay"].as(); } //Waveform groups @@ -3294,6 +3300,8 @@ YAML::Node MainWindow::SerializeUIConfiguration() window["height"] = m_height; window["width"] = m_width; window["fullscreen"] = m_fullscreen; + window["traceAlpha"] = m_traceAlpha; + window["persistenceDecay"] = m_persistenceDecay; node["window"] = window; //Waveform areas are hierarchical internally, but written as separate area and group headings diff --git a/src/ngscopeclient/MainWindow.h b/src/ngscopeclient/MainWindow.h index 5773afc0..abad8054 100644 --- a/src/ngscopeclient/MainWindow.h +++ b/src/ngscopeclient/MainWindow.h @@ -164,7 +164,7 @@ class DockDialogRequest class MainWindow : public VulkanWindow { public: - MainWindow(std::shared_ptr queue); + MainWindow(std::shared_ptr queue, bool maximized, bool restored); virtual ~MainWindow(); static bool OnMemoryPressureStatic(MemoryPressureLevel level, MemoryPressureType type, size_t requestedSize); diff --git a/src/ngscopeclient/PreferenceManager.cpp b/src/ngscopeclient/PreferenceManager.cpp index 4ce02a0d..8ee52fc6 100644 --- a/src/ngscopeclient/PreferenceManager.cpp +++ b/src/ngscopeclient/PreferenceManager.cpp @@ -67,6 +67,11 @@ const Preference& PreferenceManager::GetPreference(const string& path) const return this->m_treeRoot.GetLeaf(path); } +Preference& PreferenceManager::GetPreference(const string& path) +{ + return this->m_treeRoot.GetLeaf(path); +} + void PreferenceManager::DeterminePath() { #ifdef _WIN32 diff --git a/src/ngscopeclient/PreferenceManager.h b/src/ngscopeclient/PreferenceManager.h index ffe220c1..35ac5b8e 100644 --- a/src/ngscopeclient/PreferenceManager.h +++ b/src/ngscopeclient/PreferenceManager.h @@ -65,9 +65,13 @@ class PreferenceManager PreferenceManager& operator=(const PreferenceManager&) = delete; PreferenceManager& operator=(PreferenceManager&&) = default; + // Singleton access + static PreferenceManager& GetPreferences() { return m_instance; }; + public: void SavePreferences(); PreferenceCategory& AllPreferences(); + Preference& GetPreference(const std::string& path); std::string GetConfigDirectory() { return m_configDir; } @@ -103,6 +107,8 @@ class PreferenceManager PreferenceCategory m_treeRoot; std::string m_filePath; std::string m_configDir; + // Singleton instance + static PreferenceManager m_instance; }; #endif // PreferenceManager_h diff --git a/src/ngscopeclient/PreferenceSchema.cpp b/src/ngscopeclient/PreferenceSchema.cpp index 3de6a10f..8e9699bd 100644 --- a/src/ngscopeclient/PreferenceSchema.cpp +++ b/src/ngscopeclient/PreferenceSchema.cpp @@ -31,6 +31,8 @@ #include "PreferenceTypes.h" #include "ngscopeclient.h" +PreferenceManager PreferenceManager::m_instance; + void PreferenceManager::InitializeDefaults() { auto& appearance = this->m_treeRoot.AddCategory("Appearance"); @@ -483,6 +485,31 @@ void PreferenceManager::InitializeDefaults() .EnumValue("Multi window", VIEWPORT_ENABLE) .EnumValue("Single window", VIEWPORT_DISABLE) ); + windows.AddPreference( + Preference::Enum("startup_mode", STARTUP_MODE_WINDOWED) + .Label("Window startup mode") + .Description( + "Specifies the way Ngscopeclient window should be opened at startup.\n" + "\n" + "The default is windowed: the application is started in a fixed 1280x720 window.\n" + "Other options are:\n" + " - Maximized: the window is maximized on the main screen,\n" + " - Last State: the window is restored at the last position and size.\n" + ) + .EnumValue("Windowed", STARTUP_MODE_WINDOWED) + .EnumValue("Maximized", STARTUP_MODE_MAXIMIZED) + .EnumValue("Last State", STARTUP_MODE_LAST_STATE) + ); + auto& windowStartup = appearance.AddCategory("Startup"); + windowStartup.AddPreference(Preference::Bool("startup_fullscreen", false).Invisible()); + windowStartup.AddPreference(Preference::Bool("startup_maximized", false).Invisible()); + windowStartup.AddPreference(Preference::Int ("startup_pos_x", 0).Invisible()); + windowStartup.AddPreference(Preference::Int ("startup_pos_y", 0).Invisible()); + windowStartup.AddPreference(Preference::Int ("startup_size_width", 0).Invisible()); + windowStartup.AddPreference(Preference::Int ("startup_size_heigth", 0).Invisible()); + windowStartup.AddPreference(Preference::String ("monitor_name", "").Invisible()); + windowStartup.AddPreference(Preference::Int ("monitor_width", 0).Invisible()); + windowStartup.AddPreference(Preference::Int ("monitor_heigth", 0).Invisible()); auto& drivers = this->m_treeRoot.AddCategory("Drivers"); auto& dgeneral = drivers.AddCategory("General"); diff --git a/src/ngscopeclient/PreferenceTypes.h b/src/ngscopeclient/PreferenceTypes.h index 60a6cbfd..35eb2749 100644 --- a/src/ngscopeclient/PreferenceTypes.h +++ b/src/ngscopeclient/PreferenceTypes.h @@ -55,6 +55,13 @@ enum ViewportMode VIEWPORT_DISABLE }; +enum StartupMode +{ + STARTUP_MODE_WINDOWED, + STARTUP_MODE_MAXIMIZED, + STARTUP_MODE_LAST_STATE +}; + enum DataWidth { WIDTH_AUTO, diff --git a/src/ngscopeclient/Session.cpp b/src/ngscopeclient/Session.cpp index 3963ecc6..de71c2ed 100644 --- a/src/ngscopeclient/Session.cpp +++ b/src/ngscopeclient/Session.cpp @@ -2857,7 +2857,7 @@ void Session::ApplyPreferences(shared_ptr scope) auto lecroy = dynamic_pointer_cast(scope); if(lecroy) { - if(m_preferences.GetBool("Drivers.Teledyne LeCroy.force_16bit")) + if(GetPreferences().GetBool("Drivers.Teledyne LeCroy.force_16bit")) lecroy->ForceHDMode(true); //else auto resolution depending on instrument type @@ -2865,7 +2865,7 @@ void Session::ApplyPreferences(shared_ptr scope) auto siglent = dynamic_pointer_cast(scope); if(siglent) { - auto dataWidth = m_preferences.GetEnumRaw("Drivers.Siglent SDS HD.data_width"); + auto dataWidth = GetPreferences().GetEnumRaw("Drivers.Siglent SDS HD.data_width"); if(dataWidth == WIDTH_8_BITS) { siglent->ForceHDMode(false); @@ -2878,7 +2878,7 @@ void Session::ApplyPreferences(shared_ptr scope) auto rsrtb = dynamic_pointer_cast(scope); if(rsrtb) { - auto dataWidth = m_preferences.GetEnumRaw("Drivers.RohdeSchwarz RTB.data_width"); + auto dataWidth = GetPreferences().GetEnumRaw("Drivers.RohdeSchwarz RTB.data_width"); if(dataWidth == WIDTH_8_BITS) { rsrtb->ForceHDMode(false); @@ -2891,7 +2891,7 @@ void Session::ApplyPreferences(shared_ptr scope) auto rigol = dynamic_pointer_cast(scope); if(rigol) { - auto dataWidth = m_preferences.GetEnumRaw("Drivers.Rigol DHO.data_width"); + auto dataWidth = GetPreferences().GetEnumRaw("Drivers.Rigol DHO.data_width"); if(dataWidth == WIDTH_8_BITS) { rigol->ForceHDMode(false); diff --git a/src/ngscopeclient/Session.h b/src/ngscopeclient/Session.h index 94a95dea..3cd92f1c 100644 --- a/src/ngscopeclient/Session.h +++ b/src/ngscopeclient/Session.h @@ -593,15 +593,12 @@ class Session protected: std::optional m_hoverTime; +public: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // End user preferences (persistent across sessions) - //Preferences state - PreferenceManager m_preferences; - -public: PreferenceManager& GetPreferences() - { return m_preferences; } + { return PreferenceManager::GetPreferences(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Reference filters (used to query legal inputs to filters etc) diff --git a/src/ngscopeclient/VulkanWindow.cpp b/src/ngscopeclient/VulkanWindow.cpp index b53ec83e..2b6b6ab2 100644 --- a/src/ngscopeclient/VulkanWindow.cpp +++ b/src/ngscopeclient/VulkanWindow.cpp @@ -37,6 +37,8 @@ #include "TextureManager.h" #include "VulkanWindow.h" #include "VulkanFFTPlan.h" +#include "PreferenceManager.h" +#include "PreferenceTypes.h" using namespace std; @@ -57,7 +59,7 @@ void (*ImGui_ImplVulkan_SetWindowSize)(ImGuiViewport* viewport, ImVec2 size); /** @brief Creates a new top level window with the specified title */ -VulkanWindow::VulkanWindow(const string& title, shared_ptr queue) +VulkanWindow::VulkanWindow(const string& title, shared_ptr queue, bool maximize, bool restore) : m_renderQueue(queue) , m_resizeEventPending(false) , m_softwareResizeRequested(false) @@ -69,6 +71,7 @@ VulkanWindow::VulkanWindow(const string& title, shared_ptr queue) , m_width(0) , m_height(0) , m_fullscreen(false) + , m_restore(restore) , m_windowedX(0) , m_windowedY(0) , m_windowedWidth(0) @@ -83,8 +86,27 @@ VulkanWindow::VulkanWindow(const string& title, shared_ptr queue) io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.ConfigDpiScaleFonts = true; - io.ConfigDpiScaleViewports = true; + // Check for environment variable DPI scaling override + const char *font_scale_override = getenv("NGSCOPECLIENT_FONT_SCALE"); + const char *ui_scale_override = getenv("NGSCOPECLIENT_UI_SCALE"); + if(font_scale_override || ui_scale_override) + { + m_forceDPIScaling = true; + if(font_scale_override) + { + m_forcedFontScale = atof(font_scale_override); + } + if(ui_scale_override) + { + m_forcedUIScale = atof(ui_scale_override); + } + LogTrace("Forcing DPI scaling with font scale %f and UI scale %f\n",m_forcedFontScale,m_forcedUIScale); + } + else + { + io.ConfigDpiScaleFonts = true; + io.ConfigDpiScaleViewports = true; + } //Don't serialize UI config for now //TODO: serialize to scopesession or something? https://github.com/ocornut/imgui/issues/4294 @@ -104,13 +126,117 @@ VulkanWindow::VulkanWindow(const string& title, shared_ptr queue) //Scale the initial window size by the monitor DPI glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); - //Create the window - m_window = glfwCreateWindow(1280, 720, title.c_str(), nullptr, nullptr); + // Determine window creation mode according to preferences and command line arguments + bool maximized = false; + bool restored = false; + bool windowed = false; + PreferenceManager& preferences = PreferenceManager::GetPreferences(); + // Priority to command line arguments + if(maximize) + { + maximized = true; + } + else if(restore) + { + restored = true; + } + else + { // No command line argument, check for preferences + auto mode = preferences.GetEnumRaw("Appearance.Windowing.startup_mode"); + switch(mode) + { + case STARTUP_MODE_MAXIMIZED: + maximized = true; + break; + case STARTUP_MODE_LAST_STATE: + restored = true; + break; + case STARTUP_MODE_WINDOWED: + default: + windowed = true; + break; + } + } + + //Prepare window creation + int workAreaXPosition, workAreaYPosition, workAreaWidth, workAreaHeigth; + int windowXPosition = 0, windowYPosition = 0, windowWidth = 0, windowHeigth = 0; + bool restoreWindowPosition = false; + bool fullscreen = false; + if(windowed) + { // Window creation with fixed size + m_window = glfwCreateWindow(1280, 720, title.c_str(), nullptr, nullptr); + } + else + { // Get primary monitor's content area for default sizing + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + glfwGetMonitorWorkarea(monitor, &workAreaXPosition, &workAreaYPosition, &workAreaWidth, &workAreaHeigth); + LogTrace("Workarea position and size: %d %d %d %d\n", workAreaXPosition, workAreaYPosition, workAreaWidth, workAreaHeigth); + windowWidth = workAreaWidth; + windowHeigth = workAreaHeigth; + if(restored) + { // Restore window size and position from preferences + int windowWidthPref = preferences.GetInt("Appearance.Startup.startup_size_width"); + int windowHeigthPref = preferences.GetInt("Appearance.Startup.startup_size_heigth"); + int windowXPositionPref = preferences.GetInt("Appearance.Startup.startup_pos_x"); + int windowYPositionPref = preferences.GetInt("Appearance.Startup.startup_pos_y"); + string monitorName = preferences.GetString("Appearance.Startup.monitor_name"); + int monitorWidth = preferences.GetInt("Appearance.Startup.monitor_width"); + int monitorHeigth = preferences.GetInt("Appearance.Startup.monitor_heigth"); + fullscreen = preferences.GetBool("Appearance.Startup.startup_fullscreen"); + maximized = preferences.GetBool("Appearance.Startup.startup_maximized"); + if(windowWidthPref != 0 && windowHeigthPref != 0 && IsPositionValid(monitorName, monitorWidth, monitorHeigth, windowXPositionPref, windowYPositionPref)) + { // We have stored position and size: use them + windowWidth = windowWidthPref; + windowHeigth = windowHeigthPref; + windowXPosition = windowXPositionPref; + windowYPosition = windowYPositionPref; + LogTrace("Preferences startup position and size: %d %d %d %d\n", windowXPosition, windowYPosition, windowWidthPref, windowHeigthPref); + restoreWindowPosition = true; + } + else + { // No previous position: default to maximized + maximized = true; + } + if(fullscreen) + { // Save prefs for when we get out of fullscreen mode + m_width = windowWidthPref; + m_height = windowHeigthPref; + m_windowedX = windowXPositionPref; + m_windowedY = windowYPositionPref; + } + } + // Create the window with calculated size on default monitor, we will reposition it after if needed + LogTrace("Creating window with size: %d %d and maximized = %d\n", windowWidth, windowHeigth, maximized); + m_window = glfwCreateWindow(windowWidth, windowHeigth, title.c_str(), nullptr, nullptr); + } if(!m_window) { LogError("Window creation failed\n"); abort(); } + if(!windowed) + { // Now that the window has been created, we can set its position and size correctly + int left, top, right, bottom; + if(!restoreWindowPosition) + { // Get frame size to adjust window default position and size accordingly + glfwGetWindowFrameSize(m_window, &left, &top, &right, &bottom); + LogTrace("Window frame size: %d %d %d %d\n", top, left, right, bottom); + windowXPosition = 0; + windowYPosition = top; + windowHeigth -= top; + } + LogTrace("Resizing window with postion and size: %d %d %d %d\n", windowXPosition, windowYPosition, windowWidth, windowHeigth); + // Actually set the window position and size with calculated values + glfwSetWindowMonitor(m_window, nullptr, windowXPosition, windowYPosition, windowWidth, windowHeigth, GLFW_DONT_CARE); + if(maximized) glfwMaximizeWindow(m_window); + m_width = windowWidth; + m_height = windowHeigth; + } + if(fullscreen) + { + SetFullscreen(true); + } //Create a Vulkan surface for drawing onto VkSurfaceKHR surface; @@ -179,6 +305,7 @@ VulkanWindow::VulkanWindow(const string& title, shared_ptr queue) info.ImageCount = m_backBuffers.size(); info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; info.PipelineInfoMain.RenderPass = **m_renderPass; + if(m_forceDPIScaling) ImGui::GetStyle().FontScaleMain = m_forcedFontScale; //HERE BE DRAGONS: // We're handing imgui a VkQueue here without holding the lock. @@ -632,6 +759,56 @@ void VulkanWindow::DoRender(vk::raii::CommandBuffer& /*cmdBuf*/) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Window management +GLFWmonitor* VulkanWindow::GetCurrentMonitor() +{ + int monitorNumber; + GLFWmonitor** monitors = glfwGetMonitors(&monitorNumber); + int wx, wy; + glfwGetWindowPos(m_window, &wx, &wy); + for (int i = 0; i < monitorNumber; ++i) + { + int mx, my, mw, mh; + glfwGetMonitorWorkarea(monitors[i], &mx, &my, &mw, &mh); + if (wx >= mx && wx < mx + mw && wy >= my && wy < my + mh) + { // Window's top left corner is in this monitor's working area + return monitors[i]; + } + } + // Not found + return nullptr; +} + +#define MINIMUM_WINDOW_VISIBLE_AREA_SIZE 100 + +bool VulkanWindow::IsPositionValid(const std::string monitorName, int monitorWidth, int monitorHeigth, int windowXPos, int windowYPos) +{ + int monitorNumber; + GLFWmonitor** monitors = glfwGetMonitors(&monitorNumber); + for (int i = 0; i < monitorNumber; ++i) + { + int mx, my, mw, mh; + glfwGetMonitorWorkarea(monitors[i], &mx, &my, &mw, &mh); + LogTrace("Checking monitor with position and size: %d %d %d %d for window pos %d %d and orginal monotir size %d %d\n", mx, my, mx, mh, windowXPos, windowYPos, monitorWidth, monitorHeigth); + if (windowXPos >= mx && windowXPos + MINIMUM_WINDOW_VISIBLE_AREA_SIZE < mx + mw + && windowYPos >= my && windowYPos + MINIMUM_WINDOW_VISIBLE_AREA_SIZE < my + mh) + { // Check position name since several monitors can share the same name + string name = string(glfwGetMonitorName(monitors[i])); + LogTrace("Found match for name %s (original %s)\n", name.c_str(), monitorName.c_str()); + if(name == monitorName && mw == monitorWidth && mh == monitorHeigth) + { // Monitor name and size match + return true; + } + else + { // Monitor configuration has changed + return false; + } + } + } + // Not found + return false; +} + + void VulkanWindow::SetFullscreen(bool fullscreen) { m_fullscreen = fullscreen; @@ -685,6 +862,31 @@ void VulkanWindow::SetFullscreen(bool fullscreen) } } +void VulkanWindow::SaveWindowPositionAndSize() +{ + int x, y; + glfwGetWindowPos(m_window, &x, &y); + PreferenceManager& preferences = PreferenceManager::GetPreferences(); + preferences.GetPreference("Appearance.Startup.startup_size_width").SetInt(m_width); + preferences.GetPreference("Appearance.Startup.startup_size_heigth").SetInt(m_height); + preferences.GetPreference("Appearance.Startup.startup_pos_x").SetInt(x); + preferences.GetPreference("Appearance.Startup.startup_pos_y").SetInt(y); + preferences.GetPreference("Appearance.Startup.startup_fullscreen").SetBool(m_fullscreen); + bool maximized = (glfwGetWindowAttrib(m_window, GLFW_MAXIMIZED) == GLFW_TRUE); + preferences.GetPreference("Appearance.Startup.startup_maximized").SetBool(maximized); + int monitorWidth = 0, monitorHeigth = 0; + string monitorName = ""; + GLFWmonitor* currentMonitor = GetCurrentMonitor(); + if(currentMonitor) + { + monitorName = glfwGetMonitorName(currentMonitor); + glfwGetMonitorWorkarea(currentMonitor,&x,&y,&monitorWidth,&monitorHeigth); + } + preferences.GetPreference("Appearance.Startup.monitor_width").SetInt(monitorWidth); + preferences.GetPreference("Appearance.Startup.monitor_heigth").SetInt(monitorHeigth); + preferences.GetPreference("Appearance.Startup.monitor_name").SetString(monitorName); +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ImGui hooks diff --git a/src/ngscopeclient/VulkanWindow.h b/src/ngscopeclient/VulkanWindow.h index 413d4bab..57b5239b 100644 --- a/src/ngscopeclient/VulkanWindow.h +++ b/src/ngscopeclient/VulkanWindow.h @@ -43,7 +43,7 @@ class Texture; class VulkanWindow { public: - VulkanWindow(const std::string& title, std::shared_ptr queue); + VulkanWindow(const std::string& title, std::shared_ptr queue, bool noMaximize, bool noRestore); virtual ~VulkanWindow(); GLFWwindow* GetWindow() @@ -64,9 +64,13 @@ class VulkanWindow bool IsFullscreen() { return m_fullscreen; } + void SaveWindowPositionAndSize(); + protected: bool UpdateFramebuffer(); void SetFullscreen(bool fullscreen); + GLFWmonitor* GetCurrentMonitor(); + bool IsPositionValid(const std::string monitorName, int monitorWidth, int monitorHeigth, int windowXPos, int windowYPos); virtual void DoRender(vk::raii::CommandBuffer& cmdBuf); virtual void RenderUI(); @@ -158,6 +162,18 @@ class VulkanWindow ///@brief Fullscreen flag bool m_fullscreen; + ///@brief True if user asked (via command line argument) not to restore previous window state + bool m_restore; + + ///@brief True if user asked (via NGSCOPECLIENT_UI_SCALE or NGSCOPECLIENT_FONT_SCALE environment variable) to force DPI scaling + bool m_forceDPIScaling; + + ///@brief Forced font DPI scale value + float m_forcedFontScale = 1.0f; + + ///@brief Forced UI DPI scale value + float m_forcedUIScale = 1.0f; + ///@brief Saved position before we went fullscreen int m_windowedX; diff --git a/src/ngscopeclient/main.cpp b/src/ngscopeclient/main.cpp index b4b632a6..09f367c9 100644 --- a/src/ngscopeclient/main.cpp +++ b/src/ngscopeclient/main.cpp @@ -57,8 +57,10 @@ static void print_help(FILE* stream) "ngscopeclient is a test and measurement remote control and analysis suite\n" "\n" "General options:\n" - " --version print the application version and exit\n" - " --help, -h print this help and exit\n" + " --version print the application version and exit\n" + " --help, -h print this help and exit\n" + " --maximize, -m maximize ngscopeclient window on startup\n" + " --restore, -r restore previous ngscopeclient window size and position\n" "\n" "Logging options:\n" " -q, --quiet make logging one level quieter (can be repeated)\n" @@ -92,7 +94,37 @@ int main(int argc, char* argv[]) //Global settings Severity console_verbosity = Severity::NOTICE; + //Windows needs special console handling! + #ifdef _WIN32 + bool attachConsoleFailed = false; + bool getConsoleWindowFailed = false; + //If we have a parent process console, we were probably run from a powershell/cmd.exe session. + //If we had one, we need to attach to it (since as a Win32 subsystem application we aren't connected by default) + //Failing here indicates we were run from explorer, and thus should not be spawning a console window + //(we just log to the GuiLogSink instead) + if(!AttachConsole(ATTACH_PARENT_PROCESS)) + { + attachConsoleFailed = true; + } + + //Once we've attached to the console (if we had one), make sure we had a window for it + else if(GetConsoleWindow() == NULL) + getConsoleWindowFailed = true; + + //If we get here, we were run from a Windows shell session and should log to that console + else + { + //We're using the existing parent process console. + //Reopen stdio streams so they point to it + freopen("CON", "w", stdout); + freopen("CON", "w", stderr); + freopen("CON", "r", stdin); + } + #endif + string sessionToOpen; + bool maximize = false; + bool restore = false; vector instrumentConnectionStrings; for(int i=1; i queue(g_vkQueueManager->GetRenderQueue("g_mainWindow.render")); - g_mainWindow = make_unique(queue); + g_mainWindow = make_unique(queue,maximize,restore); + auto& session = g_mainWindow->GetSession(); @@ -279,6 +319,8 @@ int main(int argc, char* argv[]) //Draw the main window g_mainWindow->Render(); } + // Store window position and size for next startup + g_mainWindow->SaveWindowPositionAndSize(); session.ClearBackgroundThreads(); }