#include "core/application.hpp" #include "core/logger.hpp" #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #endif #ifdef __linux__ #include #include #include #include // Keep a persistent X11 connection for emergency mouse release in signal handlers. // XOpenDisplay inside a signal handler is unreliable, so we open it once at startup. static Display* g_emergencyDisplay = nullptr; static void releaseMouseGrab() { if (g_emergencyDisplay) { XUngrabPointer(g_emergencyDisplay, CurrentTime); XUngrabKeyboard(g_emergencyDisplay, CurrentTime); XFlush(g_emergencyDisplay); } } #else static void releaseMouseGrab() {} #endif #ifdef __linux__ static void crashHandlerSigaction(int sig, siginfo_t* info, void* /*ucontext*/) { releaseMouseGrab(); void* frames[64]; int n = backtrace(frames, 64); const char* sigName = (sig == SIGSEGV) ? "SIGSEGV" : (sig == SIGABRT) ? "SIGABRT" : (sig == SIGFPE) ? "SIGFPE" : "UNKNOWN"; void* faultAddr = info ? info->si_addr : nullptr; fprintf(stderr, "\n=== CRASH: signal %s (%d) faultAddr=%p ===\n", sigName, sig, faultAddr); backtrace_symbols_fd(frames, n, STDERR_FILENO); FILE* f = fopen("/tmp/wowee_debug.log", "a"); if (f) { fprintf(f, "\n=== CRASH: signal %s (%d) faultAddr=%p ===\n", sigName, sig, faultAddr); fflush(f); backtrace_symbols_fd(frames, n, fileno(f)); fclose(f); } // Re-raise with default handler struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_DFL; sigaction(sig, &sa, nullptr); raise(sig); } #else static void crashHandler(int sig) { releaseMouseGrab(); std::signal(sig, SIG_DFL); std::raise(sig); } #endif static wowee::core::LogLevel readLogLevelFromEnv() { const char* raw = std::getenv("WOWEE_LOG_LEVEL"); if (!raw || !*raw) return wowee::core::LogLevel::WARNING; std::string level(raw); for (char& c : level) c = static_cast(std::tolower(static_cast(c))); if (level == "debug") return wowee::core::LogLevel::DEBUG; if (level == "info") return wowee::core::LogLevel::INFO; if (level == "warn" || level == "warning") return wowee::core::LogLevel::WARNING; if (level == "error") return wowee::core::kLogLevelError; if (level == "fatal") return wowee::core::LogLevel::FATAL; return wowee::core::LogLevel::WARNING; } int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) { #ifdef __linux__ g_emergencyDisplay = XOpenDisplay(nullptr); // Use sigaction for SIGSEGV/SIGABRT/SIGFPE to get si_addr (faulting address) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = crashHandlerSigaction; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, nullptr); sigaction(SIGABRT, &sa, nullptr); sigaction(SIGFPE, &sa, nullptr); } std::signal(SIGTERM, [](int) { std::_Exit(1); }); std::signal(SIGINT, [](int) { std::_Exit(1); }); #else std::signal(SIGSEGV, crashHandler); std::signal(SIGABRT, crashHandler); std::signal(SIGFPE, crashHandler); std::signal(SIGTERM, crashHandler); std::signal(SIGINT, crashHandler); #endif // Change working directory to the executable's directory so relative asset // paths (assets/shaders/, Data/, etc.) resolve correctly from any launch location. #ifdef __APPLE__ { uint32_t bufSize = 0; _NSGetExecutablePath(nullptr, &bufSize); std::string exePath(bufSize, '\0'); _NSGetExecutablePath(exePath.data(), &bufSize); chdir(dirname(exePath.data())); } #elif defined(__linux__) { char buf[4096]; ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); if (len > 0) { buf[len] = '\0'; chdir(dirname(buf)); } } #endif try { wowee::core::Logger::getInstance().setLogLevel(readLogLevelFromEnv()); LOG_INFO("=== Wowee Native Client ==="); LOG_INFO("Starting application..."); wowee::core::Application app; if (!app.initialize()) { LOG_FATAL("Failed to initialize application"); return 1; } app.run(); app.shutdown(); LOG_INFO("Application exited successfully"); #ifdef __linux__ if (g_emergencyDisplay) { XCloseDisplay(g_emergencyDisplay); g_emergencyDisplay = nullptr; } #endif return 0; } catch (const std::exception& e) { releaseMouseGrab(); LOG_FATAL("Unhandled exception: ", e.what()); return 1; } catch (...) { releaseMouseGrab(); LOG_FATAL("Unknown exception occurred"); return 1; } }