diff --git a/src/tray_windows.c b/src/tray_windows.c index 79c589f..9f0d14e 100644 --- a/src/tray_windows.c +++ b/src/tray_windows.c @@ -256,14 +256,26 @@ int tray_init(struct tray *tray) { int tray_loop(int blocking) { MSG msg; + int has_message = 0; + + // Use the thread queue and only dispatch when a message was actually retrieved. + // This ensures WM_QUIT is observed and avoids dispatching an uninitialized MSG. if (blocking) { - GetMessage(&msg, hwnd, 0, 0); + has_message = GetMessage(&msg, NULL, 0, 0); + if (has_message <= 0) { + return -1; + } } else { - PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE); + has_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); + if (has_message == 0) { + return 0; + } } + if (msg.message == WM_QUIT) { return -1; } + TranslateMessage(&msg); DispatchMessage(&msg); return 0; diff --git a/tests/unit/test_tray.cpp b/tests/unit/test_tray.cpp index e6b082f..5bd0792 100644 --- a/tests/unit/test_tray.cpp +++ b/tests/unit/test_tray.cpp @@ -235,6 +235,28 @@ TEST_F(TrayTest, TestTrayLoop) { EXPECT_EQ(result, 0); } +#if defined(TRAY_WINAPI) +TEST_F(TrayTest, TestTrayLoopHandlesThreadQuitMessage) { + int initResult = tray_init(&testTray); + trayRunning = (initResult == 0); + ASSERT_EQ(initResult, 0); + + // WM_QUIT is posted to the thread queue, not to a specific window. + PostQuitMessage(0); + + bool sawQuit = false; + for (int i = 0; i < 200; ++i) { + if (tray_loop(0) == -1) { + sawQuit = true; + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + EXPECT_TRUE(sawQuit); +} +#endif + TEST_F(TrayTest, TestTrayUpdate) { int initResult = tray_init(&testTray); trayRunning = (initResult == 0);