[C++] 纯文本查看 复制代码
/**
* 虚拟键盘指示灯 - 平滑透明 GDI+ 版本(托盘自定义图标)
* 适用于 Dev-C++ (MinGW-w64) 默认 ANSI 设置
* 需添加链接库 gdiplus:
* 项目选项 -> 参数 -> 链接器 -> 加入: -lgdiplus
* 需添加资源文件 resource.rc,内容为: IDI_MYICON ICON "app.ico"
*/
#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <gdiplus.h>
#include <shellapi.h> // NOTIFYICONDATA
#include <string>
using namespace Gdiplus;
// 自定义图标资源 ID,必须与 resource.rc 中的 ID 一致
#define IDI_MYICON 100
// 窗口尺寸
const int WINDOW_W = 120;
const int WINDOW_H = 30;
const int TIMER_ID = 1;
// 指示灯参数
const int RADIUS = 11; // 圆形半径
const int Y_CENTER = WINDOW_H / 2; // 圆心 Y 坐标
const int FIRST_X = 25; // 第一个圆的 X 坐标
const int SPACING = 35; // 圆之间的水平间距
const BYTE ALPHA = 135;
// 跟随鼠标偏移
const int OFFSET_X = 20;
const int OFFSET_Y = 20;
// 三个键:Caps Lock / Scroll Lock / Num Lock
const int KEYS[3] = { VK_CAPITAL, VK_SCROLL, VK_NUMLOCK };
const wchar_t* LABELS[3] = { L"A", L"S", L"1" };
// 自定义消息:托盘图标回调
#define WM_TRAYICON (WM_APP + 1)
// 菜单ID
#define ID_EXIT 40001
// 函数声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL IsKeyToggled(int vk);
void MoveToMouse(HWND hwnd);
void DrawLeds(HWND hwnd);
void UpdateLed(HWND hwnd);
void AddTrayIcon(HWND hwnd, HINSTANCE hInst);
void RemoveTrayIcon(HWND hwnd);
// GDI+ 令牌
ULONG_PTR gdiplusToken;
// 托盘数据(全局,便于删除)
NOTIFYICONDATA nid = { 0 };
// 入口
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
// 初始化 GDI+
GdiplusStartupInput gdipInput;
GdiplusStartup(&gdiplusToken, &gdipInput, NULL);
const char CLASS_NAME[] = "SmoothLedWindow";
WNDCLASS wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_LAYERED,
CLASS_NAME,
"", // 窗口标题
WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT,
WINDOW_W, WINDOW_H,
NULL, NULL, hInstance, NULL
);
// 添加托盘图标(使用自定义图标)
AddTrayIcon(hwnd, hInstance);
SetTimer(hwnd, TIMER_ID, 20, NULL);
ShowWindow(hwnd, SW_SHOW);
UpdateLed(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
GdiplusShutdown(gdiplusToken);
return 0;
}
// 添加系统托盘图标(使用自定义图标资源)
void AddTrayIcon(HWND hwnd, HINSTANCE hInst)
{
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.uID = 1; // 图标标识
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = WM_TRAYICON;
// 加载自定义图标,IDI_MYICON 在 resource.rc 中定义
nid.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_MYICON));
// 托盘提示文字
lstrcpy(nid.szTip, TEXT("键盘指示灯"));
Shell_NotifyIcon(NIM_ADD, &nid);
}
// 删除系统托盘图标
void RemoveTrayIcon(HWND hwnd)
{
Shell_NotifyIcon(NIM_DELETE, &nid);
}
BOOL IsKeyToggled(int vk)
{
return (GetKeyState(vk) & 0x0001) != 0;
}
void MoveToMouse(HWND hwnd)
{
POINT pt;
GetCursorPos(&pt);
SetWindowPos(hwnd, NULL,
pt.x + OFFSET_X, pt.y + OFFSET_Y,
0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
// 核心绘制函数
void DrawLeds(HWND hwnd)
{
RECT rc;
GetWindowRect(hwnd, &rc);
int w = rc.right - rc.left;
int h = rc.bottom - rc.top;
HDC hdcScreen = GetDC(NULL);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h; // top-down
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
BYTE* pBits = NULL;
HBITMAP hBitmap = CreateDIBSection(hdcScreen, &bmi, DIB_RGB_COLORS,
(void**)&pBits, NULL, 0);
HBITMAP oldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap);
Graphics graphics(hdcMem);
graphics.SetSmoothingMode(SmoothingModeAntiAlias);
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
// 完全透明背景
graphics.Clear(Color(0, 0, 0, 0));
// 绘制三个指示灯
for (int i = 0; i < 3; ++i)
{
bool on = IsKeyToggled(KEYS[i]);
int cx = FIRST_X + i * SPACING;
int cy = Y_CENTER;
// 颜色:开启=绿,关闭=深灰
Color color = on ? Color(ALPHA, 0, 200, 0) : Color(ALPHA, 64, 64, 64);
SolidBrush brush(color);
graphics.FillEllipse(&brush, cx - RADIUS, cy - RADIUS,
2 * RADIUS, 2 * RADIUS);
// 文字白色居中
Font font(L"Segoe UI", 9, FontStyleRegular, UnitPoint);
SolidBrush textBrush(Color(ALPHA, 255, 255, 255));
StringFormat format;
format.SetAlignment(StringAlignmentCenter);
format.SetLineAlignment(StringAlignmentCenter);
RectF textRect(cx - RADIUS, cy - RADIUS + 1, 2 * RADIUS, 2 * RADIUS);
graphics.DrawString(LABELS[i], -1, &font, textRect, &format, &textBrush);
}
// 分层显示
BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
POINT ptSrc = { 0, 0 };
SIZE sizeWnd = { w, h };
POINT ptDst = { rc.left, rc.top };
UpdateLayeredWindow(hwnd, hdcScreen, &ptDst, &sizeWnd,
hdcMem, &ptSrc, 0, &blend, ULW_ALPHA);
SelectObject(hdcMem, oldBitmap);
DeleteObject(hBitmap);
DeleteDC(hdcMem);
ReleaseDC(NULL, hdcScreen);
}
void UpdateLed(HWND hwnd)
{
MoveToMouse(hwnd);
DrawLeds(hwnd);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_TIMER:
if (wParam == TIMER_ID)
UpdateLed(hwnd);
return 0;
case WM_TRAYICON:
if (lParam == WM_RBUTTONUP)
{
// 弹出右键菜单
POINT pt;
GetCursorPos(&pt);
SetForegroundWindow(hwnd); // 使菜单可正常关闭
HMENU hMenu = CreatePopupMenu();
AppendMenu(hMenu, MF_STRING, ID_EXIT, TEXT("退出"));
TrackPopupMenu(hMenu, TPM_RIGHTBUTTON,
pt.x, pt.y, 0, hwnd, NULL);
DestroyMenu(hMenu);
PostMessage(hwnd, WM_NULL, 0, 0); // 消除菜单激活状态
}
return 0;
case WM_COMMAND:
if (LOWORD(wParam) == ID_EXIT)
{
DestroyWindow(hwnd);
}
return 0;
case WM_RBUTTONUP:
DestroyWindow(hwnd);
return 0;
case WM_DESTROY:
KillTimer(hwnd, TIMER_ID);
RemoveTrayIcon(hwnd); // 移除托盘图标
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}