#include "Draw.h" #include #include #include #include #include "../../Backend/Config/Config.h" #include "../../Backend/Misc/Fonts/FontManager.h" #include "../Renderer/Renderer.h" #include CConfig* cfg = CConfig::get(); static std::time_t GetSystemTimeRaw() { auto now = std::chrono::system_clock::now(); return std::chrono::system_clock::to_time_t(now); } static std::tm GetSystemTimeLocal() { std::time_t now = GetSystemTimeRaw(); std::tm localTime; localtime_s(&localTime, &now); return localTime; } static std::string GetSystemTimeString(const char* format) { std::tm localTime = GetSystemTimeLocal(); std::stringstream ss; ss << std::put_time(&localTime, format); return ss.str(); } static void Time() { if (!cfg->b["time"]) return; // 时间格式: 0-3 标准(前导零), 4-7 短格式(所有两位数字去前导零) static const char* baseFormats[] = { "%H:%M:%S", "%H:%M", "%I:%M:%S %p", "%I:%M %p" }; int fmtIdx = cfg->i.count("time_format") ? std::clamp(cfg->i["time_format"], 0, 7) : 0; bool shortFormat = (fmtIdx >= 4); std::string time = GetSystemTimeString(baseFormats[shortFormat ? (fmtIdx - 4) : fmtIdx]); // 短格式:去除所有两位数字的前导零 "01:02:03" → "1:2:3" if (shortFormat) { std::string out; for (size_t i = 0; i < time.size(); i++) { if (time[i] == '0' && i + 1 < time.size() && time[i + 1] >= '0' && time[i + 1] <= '9' && (i == 0 || time[i - 1] < '0' || time[i - 1] > '9')) { continue; // skip leading zero } out += time[i]; } time = out; } // 分离主时间部分与 AM/PM 后缀 std::string timeMain = time; std::string timeSuffix; const char* ampmMarkers[] = { " AM", " PM" }; for (int mi = 0; mi < 2; mi++) { size_t pos = time.rfind(ampmMarkers[mi]); if (pos != std::string::npos) { timeMain = time.substr(0, pos); timeSuffix = ampmMarkers[mi]; break; } } auto& fm = FontManager::GetInstance(); // 注册主字体 int fontSize = cfg->i["time_text_size"]; if (fontSize <= 0) fontSize = 45; static int lastFontSize = 0; if (lastFontSize != fontSize) { if (lastFontSize == 0) fm.RegisterFont("TimeFont", "C:\\Windows\\Fonts\\Verdana.ttf", fontSize); else fm.SetFontBaseSize("TimeFont", fontSize); lastFontSize = fontSize; } fm.EnsureText("TimeFont", timeMain.c_str()); RLFont font = fm.GetFont("TimeFont"); float charSpacing = (float)cfg->i["time_text_character_spacing"]; // 注册 AM/PM 小字体(主字体 ~55% 大小) RLFont smallFont = { 0 }; int suffixFontSize = 0; float suffixOffsetX = 0.f; if (!timeSuffix.empty()) { suffixFontSize = std::max(8, (int)(fontSize * 0.55f)); static int lastSuffixSize = 0; if (lastSuffixSize != suffixFontSize) { if (lastSuffixSize == 0) fm.RegisterFont("TimeFontSmall", "C:\\Windows\\Fonts\\Verdana.ttf", suffixFontSize); else fm.SetFontBaseSize("TimeFontSmall", suffixFontSize); lastSuffixSize = suffixFontSize; } fm.EnsureText("TimeFontSmall", timeSuffix.c_str()); smallFont = fm.GetFont("TimeFontSmall"); // 后缀 X 偏移 = 主时间文本宽度 + 字符间距 RLVector2 mainSize = fm.MeasureText("TimeFont", timeMain.c_str(), fontSize); suffixOffsetX = mainSize.x + charSpacing; } const int* color = cfg->c["time_text_Color"]; RLColor textColor = { static_cast(color[0]), static_cast(color[1]), static_cast(color[2]), static_cast(color[3]) }; float xPos = cfg->f["time_x_pos"]; float yPos = cfg->f["time_y_pos"]; // AM/PM 垂直偏下偏移靠左 float suffixOffsetY = timeSuffix.empty() ? 0.f : ((float)fontSize - (float)suffixFontSize) * 0.65f; const int* colorborder = cfg->c["text_border_Color"]; RLColor borderCol = { static_cast(colorborder[0]), static_cast(colorborder[1]), static_cast(colorborder[2]), static_cast(colorborder[3]) }; if (cfg->b["text_border"]) { for (int dy = -1; dy <= 1; ++dy) { for (int dx = -1; dx <= 1; ++dx) { if (dx == 0 && dy == 0) continue; RLDrawTextEx(font, timeMain.c_str(), RLVector2{ xPos + (float)dx, yPos + (float)dy }, (float)fontSize, charSpacing, borderCol); } } } RLDrawTextEx(font, timeMain.c_str(), RLVector2{ xPos, yPos }, (float)fontSize, charSpacing, textColor); } static std::string GetForegroundProcessName() { HWND fg = GetForegroundWindow(); if (!fg) return ""; DWORD pid = 0; GetWindowThreadProcessId(fg, &pid); if (pid == 0) return ""; HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); if (!hProc) return ""; char exeName[MAX_PATH] = ""; DWORD size = MAX_PATH; if (!QueryFullProcessImageNameA(hProc, 0, exeName, &size)) { CloseHandle(hProc); return ""; } CloseHandle(hProc); const char* base = strrchr(exeName, '\\'); return base ? (base + 1) : exeName; } static bool IsWindowWhitelisted() { std::string whitelist(cfg->s["keyboard_whitelist"]); if (whitelist.empty()) return false; std::string fgName = GetForegroundProcessName(); if (fgName.empty()) return false; // Case-insensitive exact match against whitelist entries size_t pos = 0; while (pos < whitelist.size()) { size_t end = whitelist.find(';', pos); if (end == std::string::npos) end = whitelist.size(); std::string entry = whitelist.substr(pos, end - pos); if (!entry.empty() && _stricmp(entry.c_str(), fgName.c_str()) == 0) return true; pos = end + 1; } return false; } static void KeyboardControl() { if (!cfg->b["keyboard_control"]) return; // Skip control if active window is in whitelist if (IsWindowWhitelisted()) return; // Throttle: only attempt toggle every 250ms to prevent frame-rate flicker // when the game also fights for lock-key state static ULONGLONG lastAttempt = 0; ULONGLONG now = GetTickCount64(); if (now - lastAttempt < 250) return; lastAttempt = now; // Caps Lock control { int mode = cfg->i["Caps_control"]; if (mode != 0) { bool capsOn = (GetKeyState(VK_CAPITAL) & 0x0001) != 0; bool wantOn = (mode == 1); // 1=常开(Always On), 2=常关(Always Off) if (capsOn != wantOn) { // keybd_event is NOT subject to UIPI (unlike SendInput), so it works with elevated fullscreen games keybd_event(VK_CAPITAL, 0x3A, 0, 0); keybd_event(VK_CAPITAL, 0x3A, KEYEVENTF_KEYUP, 0); } } } // Num Lock control { int mode = cfg->i["Num_control"]; if (mode != 0) { bool numOn = (GetKeyState(VK_NUMLOCK) & 0x0001) != 0; bool wantOn = (mode == 1); // 1=常开(Always On), 2=常关(Always Off) if (numOn != wantOn) { // keybd_event is NOT subject to UIPI (unlike SendInput), so it works with elevated fullscreen games keybd_event(VK_NUMLOCK, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0); keybd_event(VK_NUMLOCK, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); } } } } namespace Render { void DrawOverlay() { KeyboardControl(); Time(); } } // namespace Render