#include "core/application.hpp" #include "core/logger.hpp" #include #include #include #include #include #include #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 // Render-phase marker set by GameScreen::render() — lets crash handler // identify which render call was active when backtrace is incomplete. extern volatile const char* g_crashRenderPhase; #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"; const char* phase = (const char*)g_crashRenderPhase; void* faultAddr = info ? info->si_addr : nullptr; fprintf(stderr, "\n=== CRASH: signal %s (%d) renderPhase=%s faultAddr=%p ===\n", sigName, sig, phase ? phase : "?", 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) renderPhase=%s faultAddr=%p ===\n", sigName, sig, phase ? phase : "?", 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 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; } }