@@ -58,6 +58,9 @@ void OnTrayExit()
5858{
5959 WT_LOG (" [Main] Exit requested from tray" );
6060 Globals::RequestExit ();
61+ // 메시지 펌프(GetMessage)를 깨워 정상 종료시킨다. 트레이 메뉴 핸들러는
62+ // 메인 스레드(WndProc)에서 동기 호출되므로 여기서 PostQuitMessage가 안전하다.
63+ PostQuitMessage (0 );
6164}
6265
6366void OnTrayShowStatus ()
@@ -241,6 +244,15 @@ void InitialUnityProjectScan()
241244 WT_LOG (" [Main] Initial scan complete. Watching " << instances.size () << " Unity projects" );
242245}
243246
247+ // 포그라운드 창 변경 이벤트 콜백 (SetWinEventHook).
248+ // WINEVENT_OUTOFCONTEXT라 메인 스레드의 메시지 펌프 중 디스패치되므로 마샬링 불필요.
249+ void CALLBACK FocusWinEventProc (HWINEVENTHOOK , const DWORD event, const HWND hwnd,
250+ const LONG idObject, LONG , DWORD , DWORD )
251+ {
252+ if (event != EVENT_SYSTEM_FOREGROUND || idObject != OBJID_WINDOW ) return ;
253+ if (g_unityFocusDetector) g_unityFocusDetector->OnForegroundChanged (hwnd);
254+ }
255+
244256int main ()
245257{
246258 WT_LOG (" [Main] Unity WakaTime Monitor Starting..." );
@@ -282,6 +294,11 @@ int main()
282294 g_fileWatcher = &fileWatcher;
283295
284296 fileWatcher.SetChangeCallback (OnFileChanged);
297+ // 워커 스레드의 파일 변경 → 메인 스레드로 PostMessage 마샬링 (InitialScan 이전에 설치)
298+ fileWatcher.SetNotifyCallback ([&trayIcon]()
299+ {
300+ trayIcon.NotifyFileEvent ();
301+ });
285302
286303 UnityFocusDetector unityFocusDetector;
287304 g_unityFocusDetector = &unityFocusDetector;
@@ -296,43 +313,40 @@ int main()
296313
297314 WT_LOG (" \n [Main] Unity WakaTime is now running in background!" );
298315
299- auto lastScan = std::chrono::steady_clock::now ();
300- const auto scanInterval = std::chrono::seconds (10 );
301-
302- while (!Globals::ShouldExit ())
316+ // 이벤트 허브 콜백 배선 (TrayIcon의 메시지 펌프에서 디스패치)
317+ trayIcon.SetFileEventCallback ([]()
303318 {
304- if (g_fileWatcher)
305- {
306- g_fileWatcher->DrainPendingEvents ();
307- }
319+ if (g_fileWatcher) g_fileWatcher->DrainPendingEvents ();
320+ });
308321
309- if (g_unityFocusDetector) {
310- g_unityFocusDetector->CheckFocused ();
311- g_unityFocusDetector->SendPeriodicHeartbeat ();
312- }
322+ trayIcon.SetProcessScanCallback ([&processMonitor]()
323+ {
324+ std::vector<UnityInstance> started;
325+ std::vector<UnityInstance> closed;
326+ processMonitor.PollChanges (started, closed);
327+ if (!started.empty ()) HandleNewUnityInstances (started);
328+ if (!closed.empty ()) HandleClosedUnityInstances (closed);
329+ });
330+
331+ trayIcon.SetPeriodicTickCallback ([]()
332+ {
333+ if (g_unityFocusDetector) g_unityFocusDetector->SendPeriodicHeartbeat ();
334+ });
313335
314- int msgCount = trayIcon.ProcessMessages ();
315- if (msgCount > 5 ) std::this_thread::sleep_for (std::chrono::milliseconds (50 )); // 많은 메시지 → 빠른 처리
316- else if (msgCount > 0 ) std::this_thread::sleep_for (std::chrono::milliseconds (100 )); // 일부 메시지 → 보통 처리
317- else std::this_thread::sleep_for (std::chrono::milliseconds (1000 )); // 메시지 없음 → 여유 있게
336+ // 포커스 추적: SetWinEventHook(OUTOFCONTEXT) → 콜백이 메인 펌프에서 디스패치됨 (매초 폴링 제거)
337+ const HWINEVENTHOOK focusHook = SetWinEventHook (
338+ EVENT_SYSTEM_FOREGROUND , EVENT_SYSTEM_FOREGROUND ,
339+ nullptr , FocusWinEventProc, 0 , 0 ,
340+ WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS );
318341
319- if (auto now = std::chrono::steady_clock::now (); now - lastScan >= scanInterval)
320- {
321- // 새로운 Unity 인스턴스 감지
322- if (auto newInstances = processMonitor.GetNewInstances (); !newInstances.empty ())
323- {
324- HandleNewUnityInstances (newInstances);
325- }
342+ // 훅 설치 시점에 이미 Unity가 포그라운드일 수 있으므로 초기 상태 1회 캡처
343+ unityFocusDetector.OnForegroundChanged (GetForegroundWindow ());
326344
327- // 종료된 Unity 인스턴스 감지
328- if (auto closedInstances = processMonitor.GetClosedInstances (); !closedInstances.empty ())
329- {
330- HandleClosedUnityInstances (closedInstances);
331- }
345+ // 메시지 펌프: idle 시 GetMessage가 커널에서 블록되어 CPU ≈ 0,
346+ // 파일/포커스/타이머/트레이 이벤트에만 깨어난다. WM_QUIT까지 블록.
347+ trayIcon.RunMessageLoop ();
332348
333- lastScan = now;
334- }
335- }
349+ if (focusHook) UnhookWinEvent (focusHook);
336350
337351 WT_LOG (" \n [Main] Shutting down Unity WakaTime..." );
338352 trayIcon.ShowInfoNotification (" Unity WakaTime shutting down..." );
0 commit comments