31#include <GLFW/glfw3.h>
38#include <mach-o/dyld.h>
58 const char* home = std::getenv(
"HOME");
59 const std::string logPath =
60 (home !=
nullptr) ? std::string(home) +
"/Library/Logs/MetaImGUI/metaimgui.log" :
"metaimgui.log";
64 const char* localAppData = std::getenv(
"LOCALAPPDATA");
65 const std::string logPath = (localAppData !=
nullptr)
66 ? std::string(localAppData) +
"\\MetaImGUI\\logs\\metaimgui.log"
67 :
"logs\\metaimgui.log";
71 const char* home = std::getenv(
"HOME");
72 const std::string logPath =
73 (home !=
nullptr) ? std::string(home) +
"/.local/share/MetaImGUI/logs/metaimgui.log" :
"logs/metaimgui.log";
76 LOG_INFO(
"Initializing MetaImGUI v{}", Version::VERSION);
80 curl_global_init(CURL_GLOBAL_DEFAULT);
83 m_configManager = std::make_unique<ConfigManager>();
84 if (m_configManager->Load()) {
85 LOG_INFO(
"Configuration loaded successfully");
87 LOG_INFO(
"Using default configuration");
93 std::vector<std::string> translationPaths = {
94 "resources/translations/translations.json",
99 const char* appdir = std::getenv(
"METAIMGUI_APPDIR");
100 if (appdir !=
nullptr) {
101 const std::string appdir_path =
102 std::string(appdir) +
"/usr/share/MetaImGUI/resources/translations/translations.json";
103 translationPaths.insert(translationPaths.begin(), appdir_path);
109 char executablePath[1024];
110 uint32_t size =
sizeof(executablePath);
111 if (_NSGetExecutablePath(executablePath, &size) == 0) {
113 std::string exePath(executablePath);
115 size_t macosPos = exePath.rfind(
"/MacOS/");
116 if (macosPos != std::string::npos) {
117 std::string bundleResourcePath =
118 exePath.substr(0, macosPos) +
"/Resources/resources/translations/translations.json";
119 translationPaths.insert(translationPaths.begin(), bundleResourcePath);
120 LOG_DEBUG(
"macOS bundle resource path: {}", bundleResourcePath);
124 translationPaths.emplace_back(
"../Resources/resources/translations/translations.json");
125 translationPaths.emplace_back(
"MetaImGUI.app/Contents/Resources/resources/translations/translations.json");
129 translationPaths.emplace_back(
130 "../share/MetaImGUI/resources/translations/translations.json");
131 translationPaths.emplace_back(
132 "/usr/share/MetaImGUI/resources/translations/translations.json");
133 translationPaths.emplace_back(
134 "/usr/local/share/MetaImGUI/resources/translations/translations.json");
136 bool translationsLoaded =
false;
137 for (
const auto& path : translationPaths) {
139 translationsLoaded =
true;
144 if (!translationsLoaded) {
145 LOG_ERROR(
"========================================");
146 LOG_ERROR(
"CRITICAL: Failed to load translations!");
147 LOG_ERROR(
"UI will show translation keys instead of actual text");
148 LOG_ERROR(
"Tried the following locations:");
149 for (
const auto& path : translationPaths) {
152 LOG_ERROR(
"This is a PACKAGING ERROR - file is missing from bundle");
153 LOG_ERROR(
"========================================");
156 const std::string language = m_configManager->GetString(
"language").value_or(
"en");
160 auto windowSize = m_configManager->GetWindowSize();
161 const int width = windowSize ? windowSize->first : DEFAULT_WIDTH;
162 const int height = windowSize ? windowSize->second : DEFAULT_HEIGHT;
164 m_windowManager = std::make_unique<WindowManager>(WINDOW_TITLE, width, height);
165 if (!m_windowManager->Initialize()) {
166 LOG_ERROR(
"Failed to initialize window manager");
169 LOG_INFO(
"Window manager initialized");
172 m_windowManager->SetFramebufferSizeCallback(
173 [
this](
int width,
int height) { this->OnFramebufferSizeChanged(width, height); });
174 m_windowManager->SetKeyCallback(
175 [
this](
int key,
int scancode,
int action,
int mods) { this->OnKeyPressed(key, scancode, action, mods); });
176 m_windowManager->SetWindowCloseCallback([
this]() { this->OnWindowCloseRequested(); });
177 m_windowManager->SetContextLossCallback([
this]() {
return this->OnContextLoss(); });
180 m_uiRenderer = std::make_unique<UIRenderer>();
181 if (!m_uiRenderer->Initialize(m_windowManager->GetNativeWindow())) {
182 LOG_ERROR(
"Failed to initialize UI renderer");
185 LOG_INFO(
"UI renderer initialized");
188 m_dialogManager = std::make_unique<DialogManager>();
189 LOG_INFO(
"Dialog manager initialized");
192 m_updateChecker = std::make_unique<UpdateChecker>(
"andynicholson",
"MetaImGUI");
193 LOG_INFO(
"Update checker initialized");
196 m_issTracker = std::make_unique<ISSTracker>();
197 LOG_INFO(
"ISS tracker initialized");
202 m_initialized =
true;
203 LOG_INFO(
"Application initialized successfully");
215 if (!m_initialized) {
219 LOG_INFO(
"Shutting down application...");
222 if (m_configManager && m_windowManager) {
226 m_windowManager->GetWindowSize(width, height);
227 m_configManager->SetWindowSize(width, height);
228 LOG_INFO(
"Saving window size: {}x{}", width, height);
233 if (m_configManager->Save()) {
234 LOG_INFO(
"Configuration saved successfully");
240 m_issTracker->StopTracking();
242 m_issTracker.reset();
243 m_updateChecker.reset();
244 m_dialogManager.reset();
245 m_uiRenderer.reset();
246 m_windowManager.reset();
247 m_configManager.reset();
250 curl_global_cleanup();
252 m_initialized =
false;
253 LOG_INFO(
"Application shut down successfully");
260 return m_windowManager && m_windowManager->ShouldClose();
263void Application::ProcessInput() {
264 if (m_windowManager) {
265 m_windowManager->PollEvents();
269void Application::Render() {
270 if (!m_windowManager || !m_uiRenderer) {
276 const std::lock_guard<std::mutex> lock(m_updateResultMutex);
277 if (m_pendingUpdateResult) {
278 m_updateCheckInProgress =
false;
279 m_latestUpdateInfo = std::move(m_pendingUpdateResult);
280 m_showUpdateNotification =
true;
282 if (m_latestUpdateInfo->updateAvailable) {
283 m_statusMessage =
"Update available: v" + m_latestUpdateInfo->latestVersion;
284 LOG_INFO(
"Update available: v{} (current: v{})", m_latestUpdateInfo->latestVersion,
285 m_latestUpdateInfo->currentVersion);
287 m_statusMessage =
"Ready";
288 LOG_INFO(
"No updates available (current version: v{})", m_latestUpdateInfo->currentVersion);
294 const ImGuiIO& io = ImGui::GetIO();
295 m_lastFrameTime = io.Framerate;
298 m_windowManager->BeginFrame();
301 m_uiRenderer->BeginFrame();
304 ImGuiViewport* viewport = ImGui::GetMainViewport();
305 ImGui::SetNextWindowPos(viewport->Pos);
306 ImGui::SetNextWindowSize(viewport->Size);
308 const ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
309 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings |
310 ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoBringToFrontOnFocus;
312 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
313 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
314 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
316 if (ImGui::Begin(
"MetaImGUI Main",
nullptr, window_flags)) {
318 m_uiRenderer->RenderMenuBar([
this]() { this->OnExitRequested(); }, [
this]() { this->OnToggleDemoWindow(); },
319 [
this]() { this->OnCheckUpdatesRequested(); },
320 [
this]() { this->OnShowAboutRequested(); }, m_showDemoWindow,
321 [
this]() { this->OnToggleISSTracker(); }, m_showISSTracker);
324 m_uiRenderer->RenderMainWindow([
this]() { this->OnShowAboutRequested(); },
325 [
this]() { m_showDemoWindow =
true; },
326 [
this]() { this->OnShowInputDialogRequested(); });
329 m_uiRenderer->RenderStatusBar(m_statusMessage, m_lastFrameTime, Version::VERSION, m_updateCheckInProgress);
333 ImGui::PopStyleVar(3);
336 if (m_showAboutWindow) {
337 m_uiRenderer->RenderAboutWindow(m_showAboutWindow);
340 if (m_showDemoWindow) {
341 m_uiRenderer->ShowDemoWindow(m_showDemoWindow);
344 if (m_showUpdateNotification) {
345 m_uiRenderer->RenderUpdateNotification(m_showUpdateNotification, m_latestUpdateInfo.get());
348 if (m_showISSTracker) {
349 m_uiRenderer->RenderISSTrackerWindow(m_showISSTracker, m_issTracker.get());
356 if (m_showExitDialog) {
357 m_showExitDialog =
false;
360 const std::string title = loc.Tr(
"exit.title");
361 const std::string message = loc.Tr(
"exit.message");
363 m_dialogManager->ShowConfirmation(title, message, [
this](
bool confirmed) {
364 if (confirmed && m_windowManager) {
365 m_windowManager->RequestClose();
366 }
else if (m_windowManager) {
368 m_windowManager->CancelClose();
374 if (m_dialogManager) {
375 m_dialogManager->Render();
379 m_uiRenderer->EndFrame();
382 m_windowManager->EndFrame();
387void Application::OnWindowCloseRequested() {
389 if (m_windowManager) {
390 m_windowManager->CancelClose();
392 m_showExitDialog =
true;
395void Application::OnExitRequested() {
397 m_showExitDialog =
true;
400void Application::OnToggleDemoWindow() {
401 m_showDemoWindow = !m_showDemoWindow;
404void Application::OnCheckUpdatesRequested() {
408void Application::OnShowAboutRequested() {
409 m_showAboutWindow =
true;
412void Application::OnShowInputDialogRequested() {
413 if (m_dialogManager) {
415 m_dialogManager->ShowInputDialog(loc.Tr(
"input_dialog.title"), loc.Tr(
"input_dialog.prompt"),
"",
416 [
this](
const std::string& result) {
417 auto& loc = Localization::Instance();
418 if (!result.empty()) {
419 m_statusMessage = loc.Tr(
"status.input_received") +
" " + result;
420 LOG_INFO(
"User input: {}", result);
422 m_statusMessage = loc.Tr(
"status.input_cancelled");
428void Application::OnToggleISSTracker() {
429 m_showISSTracker = !m_showISSTracker;
434void Application::OnFramebufferSizeChanged(
int width,
int height) {
441void Application::OnKeyPressed(
int key,
int scancode,
int action,
int mods) {
444 if (action == GLFW_PRESS) {
446 case GLFW_KEY_ESCAPE:
450 if ((mods & GLFW_MOD_CONTROL) != 0) {
451 OnShowAboutRequested();
456 if ((mods & GLFW_MOD_SHIFT) != 0) {
457 LOG_WARNING(
"DEBUG: User triggered context loss simulation via Shift+F9");
458 if (OnContextLoss()) {
459 m_statusMessage =
"DEBUG: Context recovery successful";
461 m_statusMessage =
"DEBUG: Context recovery failed";
474void Application::CheckForUpdates() {
475 if (!m_updateChecker || m_updateCheckInProgress) {
479 m_updateCheckInProgress =
true;
480 m_statusMessage =
"Checking for updates...";
483 m_updateChecker->CheckForUpdatesAsync([
this](
const UpdateInfo& info) { this->OnUpdateCheckComplete(info); });
486void Application::OnUpdateCheckComplete(
const UpdateInfo& updateInfo) {
491 const std::lock_guard<std::mutex> lock(m_updateResultMutex);
492 m_pendingUpdateResult = std::make_unique<UpdateInfo>(updateInfo);
495bool Application::OnContextLoss() {
496 LOG_WARNING(
"Application handling context loss - attempting to recreate UI renderer");
500 m_uiRenderer->Shutdown();
504 if (!m_uiRenderer->Initialize(m_windowManager->GetNativeWindow())) {
505 LOG_ERROR(
"Failed to reinitialize UI renderer after context loss");
506 m_statusMessage =
"ERROR: Failed to recover from context loss";
510 LOG_INFO(
"UI renderer successfully reinitialized after context loss");
511 m_statusMessage =
"Recovered from display context loss";