Handle window close cleanly and show exit popup selection
This commit is contained in:
@ -217,6 +217,7 @@ void Audio::startBackgroundLoading() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
loadingAbort = false;
|
||||||
loadingComplete = false;
|
loadingComplete = false;
|
||||||
loadedCount = 0;
|
loadedCount = 0;
|
||||||
loadingThread = std::thread(&Audio::backgroundLoadingThread, this);
|
loadingThread = std::thread(&Audio::backgroundLoadingThread, this);
|
||||||
@ -235,12 +236,18 @@ void Audio::backgroundLoadingThread() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
if (loadingAbort.load()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
std::string path;
|
std::string path;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(pendingTracksMutex);
|
std::lock_guard<std::mutex> lock(pendingTracksMutex);
|
||||||
if (pendingTracks.empty()) break;
|
if (pendingTracks.empty()) break;
|
||||||
path = std::move(pendingTracks.front());
|
path = std::move(pendingTracks.front());
|
||||||
pendingTracks.erase(pendingTracks.begin());
|
pendingTracks.erase(pendingTracks.begin());
|
||||||
|
if (loadingAbort.load()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
AudioTrack t;
|
AudioTrack t;
|
||||||
t.path = path;
|
t.path = path;
|
||||||
@ -255,6 +262,10 @@ void Audio::backgroundLoadingThread() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Thread-safe addition to tracks
|
// Thread-safe addition to tracks
|
||||||
|
if (loadingAbort.load()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(tracksMutex);
|
std::lock_guard<std::mutex> lock(tracksMutex);
|
||||||
tracks.push_back(std::move(t));
|
tracks.push_back(std::move(t));
|
||||||
@ -262,8 +273,12 @@ void Audio::backgroundLoadingThread() {
|
|||||||
|
|
||||||
loadedCount++;
|
loadedCount++;
|
||||||
|
|
||||||
// Small delay to prevent overwhelming the system
|
// Small delay to prevent overwhelming the system (unless abort requested)
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
if (!loadingAbort.load()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -328,9 +343,15 @@ void Audio::playGameMusic() {
|
|||||||
|
|
||||||
void Audio::shutdown(){
|
void Audio::shutdown(){
|
||||||
// Stop background loading thread first
|
// Stop background loading thread first
|
||||||
|
loadingAbort = true;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(pendingTracksMutex);
|
||||||
|
pendingTracks.clear();
|
||||||
|
}
|
||||||
if (loadingThread.joinable()) {
|
if (loadingThread.joinable()) {
|
||||||
loadingThread.join();
|
loadingThread.join();
|
||||||
}
|
}
|
||||||
|
loadingComplete = true;
|
||||||
|
|
||||||
if(audioStream){ SDL_DestroyAudioStream(audioStream); audioStream=nullptr; }
|
if(audioStream){ SDL_DestroyAudioStream(audioStream); audioStream=nullptr; }
|
||||||
tracks.clear();
|
tracks.clear();
|
||||||
|
|||||||
@ -72,6 +72,7 @@ private:
|
|||||||
std::mutex tracksMutex;
|
std::mutex tracksMutex;
|
||||||
std::mutex pendingTracksMutex;
|
std::mutex pendingTracksMutex;
|
||||||
std::atomic<bool> loadingComplete{false};
|
std::atomic<bool> loadingComplete{false};
|
||||||
|
std::atomic<bool> loadingAbort{false};
|
||||||
std::atomic<int> loadedCount{0};
|
std::atomic<int> loadedCount{0};
|
||||||
|
|
||||||
// SFX mixing support
|
// SFX mixing support
|
||||||
|
|||||||
@ -398,13 +398,14 @@ bool ApplicationManager::initializeManagers() {
|
|||||||
// Forward all window events to StateManager
|
// Forward all window events to StateManager
|
||||||
if (!m_stateManager) return;
|
if (!m_stateManager) return;
|
||||||
SDL_Event ev{};
|
SDL_Event ev{};
|
||||||
ev.type = SDL_EVENT_WINDOW_RESIZED; // generic mapping; handlers can inspect inner fields
|
ev.type = we.type;
|
||||||
ev.window = we;
|
ev.window = we;
|
||||||
m_stateManager->handleEvent(ev);
|
m_stateManager->handleEvent(ev);
|
||||||
});
|
});
|
||||||
|
|
||||||
m_inputManager->registerQuitHandler([this](){
|
m_inputManager->registerQuitHandler([this](){
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[QUIT] InputManager quit handler invoked - setting running=false");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[QUIT] InputManager quit handler invoked - setting running=false");
|
||||||
|
traceFile("ApplicationManager: quit handler -> m_running=false");
|
||||||
m_running = false;
|
m_running = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,16 +22,8 @@ void InputManager::processEvents() {
|
|||||||
}
|
}
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
m_shouldQuit = true;
|
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||||
for (auto& handler : m_quitHandlers) {
|
handleQuitEvent();
|
||||||
try {
|
|
||||||
// Trace quit event handling
|
|
||||||
FILE* f = fopen("tetris_trace.log", "a"); if (f) { fprintf(f, "InputManager: SDL_EVENT_QUIT polled\n"); fclose(f); }
|
|
||||||
handler();
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
SDL_LogError(SDL_LOG_CATEGORY_INPUT, "Exception in quit handler: %s", e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_EVENT_KEY_DOWN:
|
case SDL_EVENT_KEY_DOWN:
|
||||||
@ -254,6 +246,10 @@ void InputManager::handleMouseMotionEvent(const SDL_MouseMotionEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::handleWindowEvent(const SDL_WindowEvent& event) {
|
void InputManager::handleWindowEvent(const SDL_WindowEvent& event) {
|
||||||
|
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
|
||||||
|
handleQuitEvent();
|
||||||
|
}
|
||||||
|
|
||||||
// Notify handlers
|
// Notify handlers
|
||||||
for (auto& handler : m_windowEventHandlers) {
|
for (auto& handler : m_windowEventHandlers) {
|
||||||
try {
|
try {
|
||||||
@ -353,6 +349,11 @@ void InputManager::reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::handleQuitEvent() {
|
void InputManager::handleQuitEvent() {
|
||||||
|
FILE* f = fopen("tetris_trace.log", "a");
|
||||||
|
if (f) {
|
||||||
|
fprintf(f, "InputManager::handleQuitEvent invoked\n");
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
m_shouldQuit = true;
|
m_shouldQuit = true;
|
||||||
for (auto& handler : m_quitHandlers) {
|
for (auto& handler : m_quitHandlers) {
|
||||||
handler();
|
handler();
|
||||||
|
|||||||
@ -700,7 +700,7 @@ int main(int, char **)
|
|||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
while (SDL_PollEvent(&e))
|
while (SDL_PollEvent(&e))
|
||||||
{
|
{
|
||||||
if (e.type == SDL_EVENT_QUIT)
|
if (e.type == SDL_EVENT_QUIT || e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED)
|
||||||
running = false;
|
running = false;
|
||||||
else {
|
else {
|
||||||
// Route event to state manager handlers for per-state logic
|
// Route event to state manager handlers for per-state logic
|
||||||
|
|||||||
@ -712,7 +712,7 @@ int main(int, char **)
|
|||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
while (SDL_PollEvent(&e))
|
while (SDL_PollEvent(&e))
|
||||||
{
|
{
|
||||||
if (e.type == SDL_EVENT_QUIT)
|
if (e.type == SDL_EVENT_QUIT || e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED)
|
||||||
running = false;
|
running = false;
|
||||||
else {
|
else {
|
||||||
// Route event to state manager handlers for per-state logic
|
// Route event to state manager handlers for per-state logic
|
||||||
|
|||||||
Reference in New Issue
Block a user