31#include <GLFW/glfw3.h>
37#include <mach-o/dyld.h>
57 const char* home = std::getenv(
"HOME");
58 const std::string logPath =
59 (home !=
nullptr) ? std::string(home) +
"/Library/Logs/MetaImGUI/metaimgui.log" :
"metaimgui.log";
63 const char* localAppData = std::getenv(
"LOCALAPPDATA");
64 const std::string logPath = (localAppData !=
nullptr)
65 ? std::string(localAppData) +
"\\MetaImGUI\\logs\\metaimgui.log"
66 :
"logs\\metaimgui.log";
70 const char* home = std::getenv(
"HOME");
71 const std::string logPath =
72 (home !=
nullptr) ? std::string(home) +
"/.local/share/MetaImGUI/logs/metaimgui.log" :
"logs/metaimgui.log";
75 LOG_INFO(
"Initializing MetaImGUI v{}", Version::VERSION);
78 m_configManager = std::make_unique<ConfigManager>();
79 if (m_configManager->Load()) {
80 LOG_INFO(
"Configuration loaded successfully");
82 LOG_INFO(
"Using default configuration");
88 std::vector<std::string> translationPaths = {
89 "resources/translations/translations.json",
94 const char* appdir = std::getenv(
"METAIMGUI_APPDIR");
95 if (appdir !=
nullptr) {
96 const std::string appdir_path =
97 std::string(appdir) +
"/usr/share/MetaImGUI/resources/translations/translations.json";
98 translationPaths.insert(translationPaths.begin(), appdir_path);
104 char executablePath[1024];
105 uint32_t size =
sizeof(executablePath);
106 if (_NSGetExecutablePath(executablePath, &size) == 0) {
108 std::string exePath(executablePath);
110 size_t macosPos = exePath.rfind(
"/MacOS/");
111 if (macosPos != std::string::npos) {
112 std::string bundleResourcePath =
113 exePath.substr(0, macosPos) +
"/Resources/resources/translations/translations.json";
114 translationPaths.insert(translationPaths.begin(), bundleResourcePath);
115 LOG_DEBUG(
"macOS bundle resource path: {}", bundleResourcePath);
119 translationPaths.emplace_back(
"../Resources/resources/translations/translations.json");
120 translationPaths.emplace_back(
"MetaImGUI.app/Contents/Resources/resources/translations/translations.json");
124 translationPaths.emplace_back(
125 "../share/MetaImGUI/resources/translations/translations.json");
126 translationPaths.emplace_back(
127 "/usr/share/MetaImGUI/resources/translations/translations.json");
128 translationPaths.emplace_back(
129 "/usr/local/share/MetaImGUI/resources/translations/translations.json");
131 bool translationsLoaded =
false;
132 for (
const auto& path : translationPaths) {
134 translationsLoaded =
true;
139 if (!translationsLoaded) {
140 LOG_ERROR(
"========================================");
141 LOG_ERROR(
"CRITICAL: Failed to load translations!");
142 LOG_ERROR(
"UI will show translation keys instead of actual text");
143 LOG_ERROR(
"Tried the following locations:");
144 for (
const auto& path : translationPaths) {
147 LOG_ERROR(
"This is a PACKAGING ERROR - file is missing from bundle");
148 LOG_ERROR(
"========================================");
151 const std::string language = m_configManager->GetString(
"language").value_or(
"en");
155 auto windowSize = m_configManager->GetWindowSize();
156 const int width = windowSize ? windowSize->first : DEFAULT_WIDTH;
157 const int height = windowSize ? windowSize->second : DEFAULT_HEIGHT;
159 m_windowManager = std::make_unique<WindowManager>(WINDOW_TITLE, width, height);
160 if (!m_windowManager->Initialize()) {
161 LOG_ERROR(
"Failed to initialize window manager");
164 LOG_INFO(
"Window manager initialized");
167 m_windowManager->SetFramebufferSizeCallback(
168 [
this](
int width,
int height) { this->OnFramebufferSizeChanged(width, height); });
169 m_windowManager->SetKeyCallback(
170 [
this](
int key,
int scancode,
int action,
int mods) { this->OnKeyPressed(key, scancode, action, mods); });
171 m_windowManager->SetWindowCloseCallback([
this]() { this->OnWindowCloseRequested(); });
172 m_windowManager->SetContextLossCallback([
this]() {
return this->OnContextLoss(); });
175 m_uiRenderer = std::make_unique<UIRenderer>();
176 if (!m_uiRenderer->Initialize(m_windowManager->GetNativeWindow())) {
177 LOG_ERROR(
"Failed to initialize UI renderer");
180 LOG_INFO(
"UI renderer initialized");
183 m_dialogManager = std::make_unique<DialogManager>();
184 LOG_INFO(
"Dialog manager initialized");
187 m_updateChecker = std::make_unique<UpdateChecker>(
"andynicholson",
"MetaImGUI");
188 LOG_INFO(
"Update checker initialized");
191 m_issTracker = std::make_unique<ISSTracker>();
192 LOG_INFO(
"ISS tracker initialized");
197 m_initialized =
true;
198 LOG_INFO(
"Application initialized successfully");
210 if (!m_initialized) {
214 LOG_INFO(
"Shutting down application...");
217 if (m_configManager && m_windowManager) {
221 m_windowManager->GetWindowSize(width, height);
222 m_configManager->SetWindowSize(width, height);
223 LOG_INFO(
"Saving window size: {}x{}", width, height);
228 if (m_configManager->Save()) {
229 LOG_INFO(
"Configuration saved successfully");
235 m_issTracker->StopTracking();
237 m_issTracker.reset();
238 m_updateChecker.reset();
239 m_dialogManager.reset();
240 m_uiRenderer.reset();
241 m_windowManager.reset();
242 m_configManager.reset();
244 m_initialized =
false;
245 LOG_INFO(
"Application shut down successfully");
252 return m_windowManager && m_windowManager->ShouldClose();
255void Application::ProcessInput() {
256 if (m_windowManager) {
257 m_windowManager->PollEvents();
261void Application::Render() {
262 if (!m_windowManager || !m_uiRenderer) {
267 const ImGuiIO& io = ImGui::GetIO();
268 m_lastFrameTime = io.Framerate;
271 m_windowManager->BeginFrame();
274 m_uiRenderer->BeginFrame();
277 ImGuiViewport* viewport = ImGui::GetMainViewport();
278 ImGui::SetNextWindowPos(viewport->Pos);
279 ImGui::SetNextWindowSize(viewport->Size);
281 const ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
282 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings |
283 ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoBringToFrontOnFocus;
285 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
286 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
287 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
289 if (ImGui::Begin(
"MetaImGUI Main",
nullptr, window_flags)) {
291 m_uiRenderer->RenderMenuBar([
this]() { this->OnExitRequested(); }, [
this]() { this->OnToggleDemoWindow(); },
292 [
this]() { this->OnCheckUpdatesRequested(); },
293 [
this]() { this->OnShowAboutRequested(); }, m_showDemoWindow,
294 [
this]() { this->OnToggleISSTracker(); }, m_showISSTracker);
297 m_uiRenderer->RenderMainWindow([
this]() { this->OnShowAboutRequested(); },
298 [
this]() { m_showDemoWindow =
true; },
299 [
this]() { this->OnShowInputDialogRequested(); });
302 m_uiRenderer->RenderStatusBar(m_statusMessage, m_lastFrameTime, Version::VERSION, m_updateCheckInProgress);
306 ImGui::PopStyleVar(3);
309 if (m_showAboutWindow) {
310 m_uiRenderer->RenderAboutWindow(m_showAboutWindow);
313 if (m_showDemoWindow) {
314 m_uiRenderer->ShowDemoWindow(m_showDemoWindow);
317 if (m_showUpdateNotification) {
318 m_uiRenderer->RenderUpdateNotification(m_showUpdateNotification, m_latestUpdateInfo.get());
321 if (m_showISSTracker) {
322 m_uiRenderer->RenderISSTrackerWindow(m_showISSTracker, m_issTracker.get());
326 if (m_showExitDialog) {
328 const std::string title = loc.Tr(
"exit.title");
329 const std::string message = loc.Tr(
"exit.message");
331 m_dialogManager->ShowConfirmation(title, message, [
this](
bool confirmed) {
332 if (confirmed && m_windowManager) {
333 m_windowManager->RequestClose();
334 }
else if (m_windowManager) {
336 m_windowManager->CancelClose();
338 m_showExitDialog =
false;
343 if (m_dialogManager) {
344 m_dialogManager->Render();
348 m_uiRenderer->EndFrame();
351 m_windowManager->EndFrame();
356void Application::OnWindowCloseRequested() {
358 if (m_windowManager) {
359 m_windowManager->CancelClose();
361 m_showExitDialog =
true;
364void Application::OnExitRequested() {
366 m_showExitDialog =
true;
369void Application::OnToggleDemoWindow() {
370 m_showDemoWindow = !m_showDemoWindow;
373void Application::OnCheckUpdatesRequested() {
377void Application::OnShowAboutRequested() {
378 m_showAboutWindow =
true;
381void Application::OnShowInputDialogRequested() {
382 if (m_dialogManager) {
384 m_dialogManager->ShowInputDialog(loc.Tr(
"input_dialog.title"), loc.Tr(
"input_dialog.prompt"),
"",
385 [
this](
const std::string& result) {
386 auto& loc = Localization::Instance();
387 if (!result.empty()) {
388 m_statusMessage = loc.Tr(
"status.input_received") +
" " + result;
389 LOG_INFO(
"User input: {}", result);
391 m_statusMessage = loc.Tr(
"status.input_cancelled");
397void Application::OnToggleISSTracker() {
398 m_showISSTracker = !m_showISSTracker;
403void Application::OnFramebufferSizeChanged(
int width,
int height) {
410void Application::OnKeyPressed(
int key,
int scancode,
int action,
int mods) {
413 if (action == GLFW_PRESS) {
415 case GLFW_KEY_ESCAPE:
419 if ((mods & GLFW_MOD_CONTROL) != 0) {
420 OnShowAboutRequested();
425 if ((mods & GLFW_MOD_SHIFT) != 0) {
426 LOG_WARNING(
"DEBUG: User triggered context loss simulation via Shift+F9");
427 if (OnContextLoss()) {
428 m_statusMessage =
"DEBUG: Context recovery successful";
430 m_statusMessage =
"DEBUG: Context recovery failed";
443void Application::CheckForUpdates() {
444 if (!m_updateChecker || m_updateCheckInProgress) {
448 m_updateCheckInProgress =
true;
449 m_statusMessage =
"Checking for updates...";
452 m_updateChecker->CheckForUpdatesAsync([
this](
const UpdateInfo& info) { this->OnUpdateCheckComplete(info); });
455void Application::OnUpdateCheckComplete(
const UpdateInfo& updateInfo) {
456 m_updateCheckInProgress =
false;
459 m_latestUpdateInfo = std::make_unique<UpdateInfo>(updateInfo);
460 m_showUpdateNotification =
true;
462 if (updateInfo.updateAvailable) {
463 m_statusMessage =
"Update available: v" + updateInfo.latestVersion;
464 LOG_INFO(
"Update available: v{} (current: v{})", updateInfo.latestVersion, updateInfo.currentVersion);
466 m_statusMessage =
"Ready";
467 LOG_INFO(
"No updates available (current version: v{})", updateInfo.currentVersion);
471bool Application::OnContextLoss() {
472 LOG_WARNING(
"Application handling context loss - attempting to recreate UI renderer");
476 m_uiRenderer->Shutdown();
480 if (!m_uiRenderer->Initialize(m_windowManager->GetNativeWindow())) {
481 LOG_ERROR(
"Failed to reinitialize UI renderer after context loss");
482 m_statusMessage =
"ERROR: Failed to recover from context loss";
486 LOG_INFO(
"UI renderer successfully reinitialized after context loss");
487 m_statusMessage =
"Recovered from display context loss";