吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2182|回复: 56
收起左侧

[Windows] 跟随鼠标的键盘指示灯

  [复制链接]
暗夜硝烟 发表于 2026-7-21 10:46
本帖最后由 暗夜硝烟 于 2026-7-21 11:40 编辑

键盘指示灯用着用着不亮了,使用很不方便。
硬件坏了软件来解决,这个是一个跟随鼠标的键盘指示灯。


image.png

https://wwbhx.lanzout.com/iUevP3xrm9oj
密码:51x8

演示 gif 图
https://www.helloimg.com/i/2026/07/21/6a5ee9632e18a.gif

[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);
}

免费评分

参与人数 8吾爱币 +7 热心值 +7 收起 理由
Kayay + 1 感谢您的宝贵建议,我们会努力争取做得更好!
luoluolu + 1 谢谢@Thanks!
yanglinman + 1 谢谢@Thanks!
modoko + 1 + 1 谢谢@Thanks!
侯永平 + 1 + 1 我很赞同!
AxiaoWyaoA + 2 + 1 用心讨论,共获提升!
freckle + 1 + 1 谢谢@Thanks!
xiyi0519 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

taoshengyijiu07 发表于 2026-7-21 14:24
我也分享一个,屏显大小写切换,提取自联想电脑,解压缩后,双击运行里面的LaunchUtility即可,没有屏幕显示,也没有图标显示。
只是在切换大小写,数字键开关时,屏幕上会短暂显示一下。
有需要的可以试试。

链接: https://pan.baidu.com/s/1UIBN6AC6V2tZtmys3-1NWg?pwd=pqbt 提取码: pqbt
xiyi0519 发表于 2026-7-21 11:03
fhl315 发表于 2026-7-21 13:46
这个是真的好用啊,我前些日子买了一款无线键盘,就是没有键盘指示灯的,结果每次不知道有没有开大小写,然后情急之下,到处找软件,最后用了虚拟键盘灯 V1.3软件,好像也是论坛里下载的,是真解决了没有灯的大问题,你这个更绝,直接就在鼠标边上,这更实用,要是能有设置的地方更好了,设置把颜色调成其他更显眼一些的
tritan 发表于 2026-7-21 10:51
键盘跑马灯?
ieee1395 发表于 2026-7-21 10:52
这个挺稀奇啊
bachelor66 发表于 2026-7-21 10:55

应该是键盘上的大小写等三个小灯的提示吧
aigaboy 发表于 2026-7-21 11:03
不晓得是点亮哪些灯?下载来试试看
shibo106 发表于 2026-7-21 11:09
显示位置能固定吗
richly 发表于 2026-7-21 11:11
试用了下,有点意思....   大小写 ,滚动锁定,数字小键盘   保存了.
西北玄天一片云 发表于 2026-7-21 11:14
你说的很好,我试一试
samson12138 发表于 2026-7-21 11:14
这个键盘灯一般很难比键盘早坏吧哈哈哈哈哈哈
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-7-22 23:53

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表