main
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
#include "Draw.h"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "../../Backend/Config/Config.h"
|
||||
#include "../../Backend/Misc/Fonts/FontManager.h"
|
||||
#include "../Renderer/Renderer.h"
|
||||
#include <psapi.h>
|
||||
|
||||
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<unsigned char>(color[0]),
|
||||
static_cast<unsigned char>(color[1]),
|
||||
static_cast<unsigned char>(color[2]),
|
||||
static_cast<unsigned char>(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<unsigned char>(colorborder[0]),
|
||||
static_cast<unsigned char>(colorborder[1]),
|
||||
static_cast<unsigned char>(colorborder[2]),
|
||||
static_cast<unsigned char>(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
|
||||
Reference in New Issue
Block a user