Files
Rcjy/Rcjy/Framework/Frontend/Draw/Draw.cpp
T
2026-06-08 15:49:35 +08:00

241 lines
7.8 KiB
C++

#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