diff --git a/helperJson.cpp b/helperJson.cpp index 5c12a87..546c004 100644 --- a/helperJson.cpp +++ b/helperJson.cpp @@ -4,12 +4,17 @@ #include "helperJson.h" #include "External/nlohmann/json.hpp" -#include +#include +#include +#include +#include +#include using json = nlohmann::json; -const char* configFileName{ "UserData.json" }; +const wchar_t* configFileName{ L"UserData.json" }; const char* savesPath{ "Saves/" }; +const wchar_t* appDataFolderName{ L"\\LatencyMeterRefreshed\\" }; int LatencyIndexSort(const void* a, const void* b) { @@ -46,6 +51,15 @@ void to_json(json& j, const PerformanceData& performanceData) }; } +void to_json(json& j, const MiscData& miscData) +{ + j = json + { + + {"localUserData", miscData.localUserData}, + }; +} + void to_json(json& j, const UserData& userData) { //json styleJson = userData.style; @@ -54,6 +68,7 @@ void to_json(json& j, const UserData& userData) { { "style", userData.style }, { "performance", userData.performance }, + { "misc", userData.misc }, }; } @@ -87,6 +102,11 @@ void from_json(const json& j, PerformanceData& performanceData) j.at("VSync").get_to(performanceData.VSync); } +void from_json(const json& j, MiscData& miscData) +{ + j.at("localUserData").get_to(miscData.localUserData); +} + void from_json(const json& j, StyleData& styleData) { j.at("mainColor").get_to(styleData.mainColor); @@ -104,6 +124,7 @@ void from_json(const json& j, UserData& userData) { j.at("style").get_to(userData.style); j.at("performance").get_to(userData.performance); + j.at("misc").get_to(userData.misc); //auto &style = j.at("style"); //style.at("mainColor").get_to(userData.style.mainColor); //style.at("fontColor").get_to(userData.style.fontColor); @@ -128,13 +149,83 @@ void from_json(const json& j, TabInfo& reading) strcpy_s(reading.name, j.value("name", "").c_str()); } +std::wstring HelperJson::GetAppDataUserConfingPath() +{ + PWSTR appDataPath = NULL; + if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath) == S_OK) + { + std::wstring filePath = std::wstring(appDataPath) + std::wstring(appDataFolderName) + std::wstring(configFileName); + filePath = filePath.substr(0, filePath.find_last_of(L"\\/") + 1) + std::wstring(configFileName); + + std::ifstream configFile(filePath); + + configFile.close(); + return filePath; + } + + return L""; +} + +std::wstring HelperJson::GetLocalUserConfingPath() +{ + wchar_t localPath[_MAX_PATH]; + GetModuleFileNameW(NULL, localPath, _MAX_PATH); + + std::wstring filePath = std::wstring(localPath) + std::wstring(configFileName); + filePath = filePath.substr(0, filePath.find_last_of(L"\\/") + 1) + std::wstring(configFileName); + + std::ifstream configFile(filePath); + + configFile.close(); + return filePath; +} + +std::wstring GetUserDataPath(bool islocal) +{ + std::ofstream configFile; + std::wstring filePath; + if (islocal) + { + wchar_t localPath[_MAX_PATH]; + GetModuleFileNameW(NULL, localPath, _MAX_PATH); + + filePath = std::wstring(localPath) + std::wstring(configFileName); + filePath = filePath.substr(0, filePath.find_last_of(L"\\/") + 1) + std::wstring(configFileName); + + configFile = std::ofstream(filePath); + } + + else + { + PWSTR appDataPath = NULL; + if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath) == S_OK) + { + filePath = std::wstring(appDataPath) + std::wstring(appDataFolderName) + std::wstring(configFileName); + filePath = filePath.substr(0, filePath.find_last_of(L"\\/") + 1) + std::wstring(configFileName); + + std::filesystem::create_directories(std::wstring(appDataPath) + std::wstring(appDataFolderName)); + + configFile = std::ofstream(filePath); + } + } + + if (!configFile.good()) + { + configFile.close(); + return L""; + } + + configFile.close(); + + return filePath; +} + void HelperJson::SaveUserData(UserData& userData) { - char localPath[_MAX_PATH + 1]; - GetModuleFileName(NULL, localPath, _MAX_PATH); + std::wstring filePath = GetUserDataPath(userData.misc.localUserData); - std::string filePath = std::string(localPath); - filePath = filePath.substr(0, filePath.find_last_of("\\/") + 1) + std::string(configFileName); + if (filePath.empty()) + return; std::ofstream configFile(filePath); @@ -157,18 +248,37 @@ void HelperJson::SaveUserStyle(StyleData& styleData) bool HelperJson::GetUserData(UserData& userData) { - char localPath[_MAX_PATH + 1]; - GetModuleFileName(NULL, localPath, _MAX_PATH); + std::wstring filePath; + + wchar_t localPath[_MAX_PATH]; + GetModuleFileNameW(NULL, localPath, _MAX_PATH); - std::string filePath = std::string(localPath) + std::string(configFileName); - filePath = filePath.substr(0, filePath.find_last_of("\\/") + 1) + std::string(configFileName); + filePath = std::wstring(localPath) + std::wstring(configFileName); + filePath = filePath.substr(0, filePath.find_last_of(L"\\/") + 1) + std::wstring(configFileName); std::ifstream configFile(filePath); if (!configFile.good()) { configFile.close(); - return false; + + PWSTR appDataPath = NULL; + if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath) == S_OK) + { + + filePath = std::wstring(appDataPath) + std::wstring(appDataFolderName) + std::wstring(configFileName); + filePath = filePath.substr(0, filePath.find_last_of(L"\\/") + 1) + std::wstring(configFileName); + + configFile = std::ifstream(filePath); + + if (!configFile.good()) + { + configFile.close(); + return false; + } + } + else + return false; } json data = json::parse(configFile); @@ -209,4 +319,41 @@ void HelperJson::GetLatencyTests(TabInfo& tests, char path[_MAX_PATH]) from_json(j, tests); saveFile.close(); +} + +void HelperJson::UserConfigLocationChanged(bool isNewLocal) +{ + std::wstring appDataPath = GetAppDataUserConfingPath(); + std::wstring localPath = GetLocalUserConfingPath(); + + bool appDataPathExists = false; + bool localPathExists = false; + + if (!appDataPath.empty()) + { + std::ifstream testFile(appDataPath); + appDataPathExists = testFile.good(); + testFile.close(); + } + + if (!localPath.empty()) + { + std::ifstream testFile(localPath); + localPathExists = testFile.good(); + testFile.close(); + } + + if ((isNewLocal && !appDataPathExists) || (!isNewLocal && !localPathExists)) + return; + + if (isNewLocal) + { + std::filesystem::copy(appDataPath, localPath, std::filesystem::copy_options::overwrite_existing); + std::filesystem::remove(appDataPath); + } + else + { + std::filesystem::copy(localPath, appDataPath, std::filesystem::copy_options::overwrite_existing); + std::filesystem::remove(localPath); + } } \ No newline at end of file diff --git a/helperJson.h b/helperJson.h index e4182e3..65113c3 100644 --- a/helperJson.h +++ b/helperJson.h @@ -4,6 +4,7 @@ #include "structs.h" #include "constants.h" + namespace HelperJson { void SaveUserStyle(StyleData &styleData); @@ -15,4 +16,9 @@ namespace HelperJson void SaveLatencyTests(TabInfo tests, char path[_MAX_PATH]); void GetLatencyTests(TabInfo& tests, char path[_MAX_PATH]); + + void UserConfigLocationChanged(bool isNewLocal); + + std::wstring GetAppDataUserConfingPath(); + std::wstring GetLocalUserConfingPath(); } \ No newline at end of file diff --git a/main.cpp b/main.cpp index a4241ad..4e728bd 100644 --- a/main.cpp +++ b/main.cpp @@ -13,6 +13,7 @@ #include "constants.h" #include "gui.h" +// idk why I didn't just expose it in GUI.h... HWND hwnd; ImVec2 detectionRectSize{ 0, 200 }; @@ -22,8 +23,8 @@ uint64_t lastFrame = 0; // Frametime in microseconds float averageFrametime = 0; -const unsigned int AVERAGE_FRAME_AMOUNT = 10000; -uint64_t frames[AVERAGE_FRAME_AMOUNT]{ 0 }; +const unsigned int AVERAGE_FRAME_COUNT = 10000; +uint64_t frames[AVERAGE_FRAME_COUNT]{ 0 }; unsigned long long totalFrames = 0; // ---- Styling ---- (Don't ask me why I didn't create structures for these things earlier) @@ -128,6 +129,8 @@ int selectedTab = 0; bool wasItemAddedGUI = false; +bool shouldRunConfiguration = false; + // Forward declaration void GotSerialChar(char c); bool SaveAsMeasurements(); @@ -161,8 +164,9 @@ void ClearData(); + Fix too many tabs obscuring tabs beneath them + Check if ANY tab is unsaved when closing - Pack Save (Save all the tabs into a single file) (?) -- Configuration Screen ++ Configuration Screen + Pack fonts into a byte array and load them that way. +- Add option to display frametime (?) */ @@ -470,6 +474,9 @@ void SaveCurrentUserConfig() //showPlotsBak = showPlots; + if (backupUserData.misc.localUserData != currentUserData.misc.localUserData) + HelperJson::UserConfigLocationChanged(currentUserData.misc.localUserData); + backupUserData = currentUserData; HelperJson::SaveUserData(currentUserData); @@ -973,6 +980,12 @@ int OnGui() { ImGuiStyle& style = ImGui::GetStyle(); + if (shouldRunConfiguration) + { + ImGui::OpenPopup("Set Up"); + shouldRunConfiguration = false; + } + if (ImGui::BeginMenuBar()) { //static bool saveMeasurementsPopup = false; @@ -1144,7 +1157,6 @@ int OnGui() //UINT modesNum = 256; //DXGI_MODE_DESC monitorModes[256]; //GUI::vOutputs[1]->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, 0, &modesNum, monitorModes); - //auto x = 0; //DXGI_MODE_DESC mode = monitorModes[modesNum - 1]; //GUI::g_pSwapChain->ResizeTarget(&mode); //GUI::g_pSwapChain->ResizeBuffers(0, 1080, 1920, DXGI_FORMAT_R8G8B8A8_UNORM, 0); @@ -1273,7 +1285,7 @@ int OnGui() static int selectedSettings = 0; const auto avail = ImGui::GetContentRegionAvail(); - const char* items[2]{ "Style", "Performance" }; + const char* items[3]{ "Style", "Performance", "Misc" }; // Makes list take ({listBoxSize}*100)% width of the parent float listBoxSize = 0.3f; @@ -1443,15 +1455,30 @@ int OnGui() ImGui::Checkbox("Show Plots", ¤tUserData.performance.showPlots); TOOLTIP("Plots can have a small impact on the performance"); + ImGui::Spacing(); + //ImGui::Checkbox("VSync", ¤tUserData.performance.VSync); ImGui::SliderInt("VSync", &(int&)currentUserData.performance.VSync, 0, 4, "%d", ImGuiSliderFlags_AlwaysClamp); TOOLTIP("This setting synchronizes your monitor with the program to avoid screen tearing.\n(Adds a significant amount of latency)"); ImGui::EndChild(); + } + + break; + + // Misc + case 2: + if ((ImGui::BeginChild("Misc", { ((1 - listBoxSize) * avail.x) - style.FramePadding.x * 2, avail.y - ImGui::GetFrameHeightWithSpacing() }, true))) + { + ImGui::Checkbox("Save config locally", ¤tUserData.misc.localUserData); + TOOLTIP("Chose whether you want to save config file in the same directory as the program, or in the AppData folder"); - break; + ImGui::EndChild(); } + + break; } + ImGui::BeginGroup(); auto buttonsAvail = ImGui::GetContentRegionAvail(); @@ -1500,8 +1527,6 @@ int OnGui() //selectedTab = tabsInfo.size() - 1; auto flags = selectedTab == tabN ? ImGuiTabItemFlags_SetSelected : 0; flags |= tabsInfo[tabN].isSaved ? 0 : ImGuiTabItemFlags_UnsavedDocument; - if (selectedTab == tabN && tabN == 2) - auto x = 0; if (ImGui::BeginTabItem(tabNameLabel, NULL, flags)) { ImGui::EndTabItem(); @@ -1687,15 +1712,6 @@ int OnGui() ImGui::EndPopup(); } - - - //if (ImGui::TabItemButton((std::string("Tab ") + std::to_string(tabN)).c_str())) - //{ - // if (tabN != 0) - // auto x = 0; - // selectedTab = tabN; - // //ImGui::EndTabItem(); - //}; } @@ -1753,7 +1769,7 @@ int OnGui() ImGui::Text("Application average %.3f \xC2\xB5s/frame (%.1f FPS) (CPU ONLY)", averageFrametime, 1000000.f / averageFrametime); // The buffer contains just too many frames to display them on the screen (and displaying every 10th or so frame makes no sense). Also smoothing out the data set would hurt the performance too much - //ImGui::PlotLines("FPS Chart", [](void* data, int idx) { return sinf(float(frames[idx%10]) / 1000); }, frames, AVERAGE_FRAME_AMOUNT); + //ImGui::PlotLines("FPS Chart", [](void* data, int idx) { return sinf(float(frames[idx%10]) / 1000); }, frames, AVERAGE_FRAME_COUNT); ImGui::SeparatorSpace(ImGuiSeparatorFlags_Horizontal, { avail.x, ImGui::GetFrameHeight() }); @@ -2099,6 +2115,26 @@ int OnGui() false ); + + if (ImGui::BeginPopupModal("Set Up", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) + { + ImGui::Text("Setup configuration"); + + ImGui::SeparatorSpace(0, { 0, 5 }); + + ImGui::Checkbox("Save config locally", ¤tUserData.misc.localUserData); + TOOLTIP("Chose whether you want to save config file in the same directory as the program, or in the AppData folder"); + + ImGui::Spacing(); + + if (ImGui::Button("Save", {-1, 0})) + { + SaveCurrentUserConfig(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + static bool isExitingWindowOpen = false; if (isExiting) @@ -2637,7 +2673,8 @@ int main(int argc, char** argv) ImGuiIO& io = ImGui::GetIO(); auto defaultTab = TabInfo(); - defaultTab.name[4] = '\0'; + defaultTab.name[4] = '0'; + defaultTab.name[5] = '\0'; tabsInfo.push_back(defaultTab); UINT modesNum = 256; @@ -2731,10 +2768,19 @@ int main(int argc, char** argv) currentUserData.style.fontColorBrightness = backupUserData.style.fontColorBrightness = .1f; currentUserData.style.fontSize = backupUserData.style.fontSize = 1.f; + shouldRunConfiguration = true; + // Note: this style might be a little bit different prior to applying it. (different darker colors) } - LoadCurrentUserConfig(); + // Just imagine what would happen if user for example had a 220 character long username and path to %AppData + "imgui.ini" would exceed 260. lovely + auto imguiFileIniPath = (currentUserData.misc.localUserData ? HelperJson::GetLocalUserConfingPath() : HelperJson::GetAppDataUserConfingPath()); + imguiFileIniPath = imguiFileIniPath.substr(0, imguiFileIniPath.find_last_of(L"\\/") + 1) + L"imgui.ini"; + char outputPathBuffer[MAX_PATH]; + + wcstombs_s(nullptr, outputPathBuffer, MAX_PATH, imguiFileIniPath.c_str(), imguiFileIniPath.size()); + + io.IniFilename = outputPathBuffer; Serial::FindAvailableSerialPorts(); @@ -2794,15 +2840,13 @@ int main(int argc, char** argv) // Average frametime calculation //printf("Frame: %i\n", frameIndex); - frameIndex = min(AVERAGE_FRAME_AMOUNT - 1, frameIndex); - if (frames[frameIndex] > 10000) - auto x = 0; + frameIndex = min(AVERAGE_FRAME_COUNT - 1, frameIndex); frameSum -= frames[frameIndex]; frameSum += newFrame; frames[frameIndex] = newFrame; - frameIndex = (frameIndex + 1) % AVERAGE_FRAME_AMOUNT; + frameIndex = (frameIndex + 1) % AVERAGE_FRAME_COUNT; - averageFrametime = ((float)frameSum) / AVERAGE_FRAME_AMOUNT; + averageFrametime = ((float)frameSum) / AVERAGE_FRAME_COUNT; totalFrames++; lastFrame = curTime; @@ -2819,4 +2863,4 @@ int main(int argc, char** argv) Serial::Close(); GUI::Destroy(); -} +} \ No newline at end of file diff --git a/structs.h b/structs.h index e537df2..a099134 100644 --- a/structs.h +++ b/structs.h @@ -74,8 +74,14 @@ struct PerformanceData unsigned int VSync; }; +struct MiscData +{ + bool localUserData = false; +}; + struct UserData { StyleData style; PerformanceData performance; + MiscData misc; }; \ No newline at end of file