Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ if(WIN32)
winhttp
shell32
psapi
wbemuuid
ole32
oleaut32
windowscodecs
Expand All @@ -63,7 +62,6 @@ if(WIN32)
winhttp
shell32
psapi
wbemuuid
ole32
oleaut32
windowscodecs
Expand All @@ -82,9 +80,18 @@ if(WIN32)
/ENTRY:mainCRTStartup
)
elseif(MINGW)
# MinGW: 심볼 스트리핑 (크기 최적화)
# MinGW: 크기 최적화 (-Os), LTO, 미사용 섹션 제거 (Resident Set Size 최소화)
target_compile_options(unity_wakatime PRIVATE
-Os
-flto
-ffunction-sections
-fdata-sections
)
target_link_options(unity_wakatime PRIVATE
-s # Strip symbols for smaller file size
-Os # LTO 링크 단계 최적화도 크기 우선으로
-s # Strip symbols for smaller file size
-flto
-Wl,--gc-sections
)
endif()
endif()
Expand Down
12 changes: 11 additions & 1 deletion include/file_watcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ class FileWatcher {
mutable std::mutex projectsMutex; // 스레드 안전성을 위한 뮤텍스
std::deque<FileChangeEvent> pendingEvents;
mutable std::mutex pendingEventsMutex;

std::atomic<bool> notifyScheduled{false}; // PostMessage 코얼레싱 (큐 적재 통지 1회로 합침)

// 파일 변경 이벤트 콜백 함수
std::function<void(const FileChangeEvent&)> changeCallback;
// 큐에 이벤트가 적재되었음을 메인 스레드에 통지 (PostMessage 등)
std::function<void()> notifyCallback;

/**
* 특정 프로젝트 폴더를 감시하는 워커 스레드 함수
Expand Down Expand Up @@ -96,6 +99,13 @@ class FileWatcher {
* @param callback 파일이 변경될 때 호출될 함수
*/
void SetChangeCallback(std::function<void(const FileChangeEvent&)> callback);

/**
* 큐 적재 통지 콜백 설정 (워커 스레드 → 메인 스레드 마샬링용).
* 워커 스레드가 이벤트를 큐에 넣으면 이 콜백을 호출한다(코얼레싱됨).
* @param callback 통지 시 호출될 함수 (예: PostMessage)
*/
void SetNotifyCallback(std::function<void()> callback);

/**
* Unity 프로젝트 감시 시작
Expand Down
9 changes: 9 additions & 0 deletions include/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@

namespace fs = std::filesystem;

// Release(NDEBUG)에서는 인자 평가까지 컴파일아웃된다.
#ifdef NDEBUG
#define WT_LOG(msg) ((void)0)
#define WT_ERR(msg) ((void)0)
#else
#define WT_LOG(msg) do { std::cout << msg << std::endl; } while (0)
#define WT_ERR(msg) do { std::cerr << msg << std::endl; } while (0)
#endif

class WakaTimeClient;
class FileWatcher;
class ProcessMonitor;
Expand Down
75 changes: 22 additions & 53 deletions include/process_monitor.h
Original file line number Diff line number Diff line change
@@ -1,60 +1,38 @@
#pragma once

#include "globals.h"
#include <Wbemidl.h> // WMI 인터페이스
#include <map>

#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")
#include <unordered_map>

/**
* Unity 프로세스들 감지
*/
class ProcessMonitor {
private:
std::map<DWORD, UnityInstance> activeInstances;
IWbemLocator* pLocator = nullptr;
IWbemServices* pService = nullptr;
bool wmiInitialized = false;

/**
* 문자열을 BSTR로 변환하는 헬퍼 함수
* @param str 변환할 문자열
* @return BSTR 포인터 (사용 후 SysFreeString으로 해제 필요)
*/
BSTR StringToBSTR(const std::wstring& str);

/**
* BSTR을 문자열로 변환하는 헬퍼 함수
* @param bstr 변환할 BSTR
* @return 변환된 문자열
*/
std::string BSTRToString(BSTR bstr);
std::unordered_map<DWORD, UnityInstance> activeInstances;

/**
* WMI를 사용해서 실제 프로세스 커맨드 라인 가져오기
* NtQueryInformationProcess + PEB 읽기로 프로세스 커맨드 라인을 가져온다.
* WMI/RPC를 거치지 않아 Wmiprvse를 깨우지 않고 비용이 낮다.
* (PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ 권한 필요, 64-bit 전제)
* @param pid 대상 프로세스 ID
* @return 커맨드 라인
*/
std::string GetRealCommandLine(DWORD pid);

/**
* WMI 초기화 (COM 초기화)
* @return 커맨드 라인, 실패시 빈 문자열
*/
bool InitializeWMI();
std::string GetCommandLineViaPeb(DWORD pid);

/**
* WMI 정리
* 특정 프로세스의 커맨드 라인을 가져와 Unity 프로젝트 경로로 해석
* @param pid 대상 프로세스 ID
* @return 프로젝트 경로, 실패시 빈문자열
*/
void CleanupWMI();
std::string GetProcessCommandLine(DWORD pid);

/**
* 특정 프로세스의 커맨드 라인을 가져오기
* 스냅샷 엔트리가 Unity 프로세스이면 인스턴스 정보로 해석
* @param pid 대상 프로세스 ID
* @return 커맨드 라인, 실패시 빈문자열
* @param instance 해석된 인스턴스 (출력)
* @return Unity 프로젝트로 해석되면 true
*/
std::string GetProcessCommandLine(DWORD pid);
bool ResolveUnityInstance(DWORD pid, UnityInstance& instance);

/**
* 커맨드 라인에서 Unity 프로젝트 경로 추출
Expand Down Expand Up @@ -96,33 +74,24 @@ class ProcessMonitor {
~ProcessMonitor();

/**
* 현재 실행 중인 모든 Unity 인스턴스를 스캔
* 현재 실행 중인 모든 Unity 인스턴스를 스캔 (초기 스캔용).
* 결과를 activeInstances에도 등록하여 이후 PollChanges가 중복 보고하지 않도록 한다.
* @return 발견된 Unity 인스턴스들
*/
std::vector<UnityInstance> ScanUnityProcesses();

/**
* 새로 시작된 Unity 프로세스가 있는지 확인
* @return 새로운 인스턴스들
*/
std::vector<UnityInstance> GetNewInstances();

/**
* 종료된 Unity 프로세스가 있는지 확인
* @return 종료된 인스턴스들
* 단일 스냅샷으로 새로 시작/종료된 Unity 프로세스를 한 번에 diff한다.
* 이미 알려진 PID는 재해석(PEB/디스크 조회)하지 않는다.
* @param started 새로 감지된 인스턴스 (출력)
* @param closed 종료된 인스턴스 (출력)
*/
std::vector<UnityInstance> GetClosedInstances();
void PollChanges(std::vector<UnityInstance>& started, std::vector<UnityInstance>& closed);

/**
* 특정 프로세스 ID가 실행 중인지 확인
* @param processId 프로세스 ID
* @return 실행중이면 true
*/
bool IsProcessRunning(DWORD processId);

/**
* 현재 활성화된 모든 Unity 인스턴스 반환
* @return 현재 실행중인 인스턴스들
*/
const std::map<DWORD, UnityInstance>& GetActiveInstances() const;
};
40 changes: 37 additions & 3 deletions include/tray_icon.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@

// 트레이 아이콘 관련 상수들
#define WM_TRAYICON (WM_USER + 1) // 트레이 아이콘 메시지
#define WM_APP_FILE_EVENT (WM_APP + 1) // 파일 변경 큐 적재 통지 (워커 스레드 → 메인 스레드)
#define IDM_EXIT 100 // 종료 메뉴 ID
#define IDM_SHOW_STATUS 101 // 상태 보기 메뉴 ID
#define IDM_TOGGLE_MONITORING 102 // 모니터링 토글 메뉴 ID
#define IDM_OPEN_DASHBOARD 103 // WakaTime 대시보드 열기
#define IDM_SETTINGS 104 // 설정 메뉴 ID
#define IDM_GITHUB 105 // Github 링크

// 타이머 ID 및 주기 (메시지 펌프 기반 이벤트화)
#define TIMER_PROCESS_SCAN 1 // Unity 프로세스 생성/종료 스캔
#define TIMER_PERIODIC_HEARTBEAT 2 // 포커스 유지 시 주기 heartbeat
#define PROCESS_SCAN_INTERVAL_MS 10000 // 10초
#define PERIODIC_HEARTBEAT_INTERVAL_MS 120000 // 2분

/**
* Windows 시스템 트레이에 아이콘을 표시하고 사용자 인터랙션 처리
*/
Expand All @@ -39,6 +46,11 @@ class TrayIcon {
std::function<void()> onOpenDashboard; // 대시보드 열기 콜백
std::function<void()> onShowSettings; // 설정 보기 콜백
std::function<void(const std::string&)> onApiKeyChange; // API 키 변경 콜백

// 이벤트 허브 콜백 (메시지 펌프에서 디스패치)
std::function<void()> onFileEvent; // WM_APP_FILE_EVENT → 파일 이벤트 드레인
std::function<void()> onProcessScan; // TIMER_PROCESS_SCAN → 프로세스 생성/종료 스캔
std::function<void()> onPeriodicTick; // TIMER_PERIODIC_HEARTBEAT → 주기 heartbeat 체크

/**
* 숨겨진 창 생성 (트레이 아이콘 메시지 수신용)
Expand Down Expand Up @@ -170,10 +182,17 @@ class TrayIcon {
void Shutdown();

/**
* 메시지 처리 (메인 스레드에서 호출)
* @return 처리된 메시지 수
* 메시지 펌프 실행 (메인 스레드에서 호출, WM_QUIT까지 블록).
* 진입 시 프로세스 스캔/주기 heartbeat 타이머를 설치하고 종료 시 정리한다.
* @return WM_QUIT의 exit code
*/
int ProcessMessages();
int RunMessageLoop();

/**
* 파일 변경 큐 적재를 메인 스레드에 통지 (워커 스레드에서 호출 가능).
* 메시지를 post하여 메시지 펌프가 WM_APP_FILE_EVENT로 깨어나도록 한다.
*/
void NotifyFileEvent();

/**
* 상태 정보 업데이트 (서브메뉴에 반영)
Expand Down Expand Up @@ -223,6 +242,21 @@ class TrayIcon {
*/
void SetApiKeyChangeCallback(const std::function<void(const std::string&)> &callback);

/**
* 파일 변경 이벤트 처리 콜백 설정 (WM_APP_FILE_EVENT)
*/
void SetFileEventCallback(const std::function<void()> &callback);

/**
* 프로세스 스캔 콜백 설정 (TIMER_PROCESS_SCAN)
*/
void SetProcessScanCallback(const std::function<void()> &callback);

/**
* 주기 heartbeat 틱 콜백 설정 (TIMER_PERIODIC_HEARTBEAT)
*/
void SetPeriodicTickCallback(const std::function<void()> &callback);

private:
/**
* 정적 윈도우 프로시저 - Windows API 콜백용
Expand Down
6 changes: 4 additions & 2 deletions include/unity_focus_detector.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ class UnityFocusDetector {

public:
/**
* 포커스 상태 확인
* 포그라운드 창 변경 시 호출 (SetWinEventHook 콜백에서 구동).
* 클래스명에 "Unity"가 포함되면 포커스 전이로 판정한다.
* @param hwnd 새 포그라운드 창 핸들 (nullptr 가능)
*/
void CheckFocused();
void OnForegroundChanged(HWND hwnd);

/**
* 2분마다 호출
Expand Down
8 changes: 5 additions & 3 deletions include/wakatime_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "globals.h"
#include <winhttp.h> // Windows HTTP API
#include <queue>
#include <condition_variable>

#pragma comment(lib, "winhttp.lib") // WinHTTP 라이브러리 링크

Expand All @@ -17,7 +18,6 @@ struct HeartbeatData {
std::string language; // Unity
std::string editor; // "Unity"
std::string operating_system; // "Windows"
std::string machine; // 머신 이름
int64_t time; // Unix timestamp
bool is_write; // 파일 수정 여부

Expand All @@ -40,10 +40,12 @@ class WakaTimeClient {

// HTTP 세션 관리
HINTERNET hSession; // WinHTTP 세션 핸들
HINTERNET hConnect; // WinHTTP 연결 핸들 (heartbeat 간 재사용, keep-alive)
bool initialized; // 초기화 상태

// 비동기 전송 관리
mutable std::mutex queueMutex; // 큐 접근 동기화
std::condition_variable queueCv; // 큐 대기/통지 (busy-poll 제거)
std::queue<HeartbeatData> heartbeatQueue; // 전송 대기 큐
std::chrono::steady_clock::time_point lastQueuedAt;
std::string lastQueuedEntity;
Expand Down Expand Up @@ -92,7 +94,7 @@ class WakaTimeClient {
* @return 머신 이름
*/
std::string GetMachineName();

/**
* Unix timestamp 생성 (현재 시간)
* @return Unix timestamp (초 단위)
Expand Down
Loading
Loading