; ============================================================ ; nameit —— 屏幕角落设备名水印(AutoHotkey v2) ; ; 功能:在屏幕指定角落显示设备名/任意文字 ; · 字高按像素精确控制 ; · 白色字身 + 黑色描边,深浅背景都清晰 ; · 始终置顶,鼠标完全穿透 ; 依赖:仅需 AutoHotkey v2(无需其他组件) ; 配置:同目录 nameit.ini,托盘菜单可打开/重载 ; 用法:见 README.md 或运行 nameit.ahk --help ; ============================================================ #Requires AutoHotkey v2.0 #SingleInstance Force Persistent APP_NAME := "nameit" INI := A_ScriptDir "\nameit.ini" LNK := A_Startup "\" APP_NAME ".lnk" ; ---------------------- 命令行 ---------------------- if (A_Args.Length > 0) { cmd := A_Args[1] if (cmd = "--install" || cmd = "-i") { ok := EnsureStartup() MsgBox(ok ? "已设置开机自启:`n" LNK : "设置开机自启失败,请检查权限。", APP_NAME, ok ? "Iconi" : "Icon!") } else if (cmd = "--uninstall" || cmd = "-u") { RemoveStartup() MsgBox("已取消开机自启。", APP_NAME, "Iconi") ExitApp } else if (cmd = "--help" || cmd = "-h" || cmd = "/?") { MsgBox(UsageText(), APP_NAME, "Iconi") ExitApp } } ; ---------------------- 读取配置 ---------------------- if !FileExist(INI) try FileAppend(IniTemplate(), INI, "UTF-8") CFG_TEXT := Cfg("general", "Text", A_ComputerName) CASE_MODE := Cfg("general", "Case", "as-is") FONT_FACE := Cfg("font", "Face", "") FONT_PX := Integer(Cfg("font", "SizePx", "50")) FONT_WT := Integer(Cfg("font", "Weight", "700")) COL_TEXT := Cfg("color", "Text", "FFFFFF") COL_STROKE := Cfg("color", "Stroke", "000000") STROKE_W := Integer(Cfg("color", "StrokeWidth", "4")) CORNER := Cfg("layout", "Corner", "bottom-left") MONITOR := Integer(Cfg("layout", "Monitor", "1")) MARGIN_X := Integer(Cfg("layout", "MarginX", "24")) MARGIN_Y := Integer(Cfg("layout", "MarginY", "18")) if (CASE_MODE = "upper") CFG_TEXT := StrUpper(CFG_TEXT) else if (CASE_MODE = "lower") CFG_TEXT := StrLower(CFG_TEXT) if (FONT_FACE = "") FONT_FACE := RegExMatch(CFG_TEXT, "[\x{4E00}-\x{9FFF}]") ? "Microsoft YaHei UI" : "Segoe UI" if (FONT_PX < 1) FONT_PX := 50 if (STROKE_W < 0) STROKE_W := 0 ; 透明色键:抠掉这个颜色即"看不见背景";确保不与文字/描边同色 KEY_COLOR := "0A0A0A" if (COL_TEXT = KEY_COLOR || COL_STROKE = KEY_COLOR) KEY_COLOR := "010203" ; ---------------------- 创建窗口 ---------------------- hFont := CreateFontPx(FONT_PX, FONT_WT, FONT_FACE) sz := MeasureText(hFont, CFG_TEXT) ; WS_EX_TRANSPARENT(0x20) 鼠标穿透 | WS_EX_LAYERED(0x80000) 色键透明 | WS_EX_NOACTIVATE(0x08000000) 不抢焦点 g := Gui("+AlwaysOnTop -Caption +ToolWindow +E0x20 +E0x80000 +E0x08000000", APP_NAME "Overlay") g.BackColor := KEY_COLOR g.MarginX := 0 g.MarginY := 0 padL := STROKE_W + 2 ctlW := sz.w + 4 ctlH := sz.h + 4 ; 1) 黑色描边层:绕字形外圈放多个同色文字控件(BackgroundTrans 使它们互不遮挡) if (STROKE_W > 0) { g.SetFont("c" COL_STROKE, FONT_FACE) for off in RingOffsets(STROKE_W) { lbl := g.Add("Text", "x" padL + off.x " y" padL + off.y " w" ctlW " h" ctlH " BackgroundTrans", CFG_TEXT) DllCall("SendMessageW", "Ptr", lbl.Hwnd, "UInt", 0x0030, "Ptr", hFont, "Ptr", 1) ; WM_SETFONT } } ; 2) 字身层(最后添加 = 位于最上层) g.SetFont("c" COL_TEXT, FONT_FACE) main := g.Add("Text", "x" padL " y" padL " w" ctlW " h" ctlH " BackgroundTrans", CFG_TEXT) DllCall("SendMessageW", "Ptr", main.Hwnd, "UInt", 0x0030, "Ptr", hFont, "Ptr", 1) ; WM_SETFONT W := ctlW + 2 * padL H := ctlH + 2 * padL g.Show("NoActivate w" W " h" H) WinSetAlwaysOnTop(1, "ahk_id " g.Hwnd) DllCall("SetLayeredWindowAttributes", "Ptr", g.Hwnd, "UInt", BGR(KEY_COLOR), "UInt", 255, "UInt", 1) PlaceOverlay() SetTimer(PlaceOverlay, 2000) ; 分辨率/任务栏/显示器变化时自动重新定位 ; ---------------------- 托盘菜单 ---------------------- A_IconTip := "nameit:" CFG_TEXT BuildMenu() ; ====================================================== ; 函数 ; ====================================================== Cfg(section, key, def) { global INI v := "" try v := IniRead(INI, section, key, "") return (v = "" ? def : v) } CreateFontPx(pixelHeight, weight, face) { return DllCall("CreateFontW" , "Int", -pixelHeight ; 负值 = 字身高度(像素) , "Int", 0, "Int", 0, "Int", 0 , "Int", weight , "UInt", 0, "UInt", 0, "UInt", 0 ; 斜体 / 下划线 / 删除线 , "UInt", 1 ; DEFAULT_CHARSET , "UInt", 0, "UInt", 0 , "UInt", 4 ; ANTIALIASED_QUALITY , "UInt", 0 , "WStr", face, "Ptr") } MeasureText(hFont, text) { hdc := DllCall("GetDC", "Ptr", 0, "Ptr") old := DllCall("SelectObject", "Ptr", hdc, "Ptr", hFont, "Ptr") size := Buffer(8, 0) DllCall("GetTextExtentPoint32W", "Ptr", hdc, "WStr", text, "Int", StrLen(text), "Ptr", size) DllCall("SelectObject", "Ptr", hdc, "Ptr", old) DllCall("ReleaseDC", "Ptr", 0, "Ptr", hdc) return { w: NumGet(size, 0, "Int"), h: NumGet(size, 4, "Int") } } ; 半径 radius 的一圈整数像素偏移(去重) RingOffsets(radius) { offsets := [] seen := Map() steps := 64 Loop steps { a := (A_Index - 1) * (360 / steps) * 3.14159265358979 / 180 dx := Round(Cos(a) * radius) dy := Round(Sin(a) * radius) if (!dx && !dy) continue k := dx "," dy if seen.Has(k) continue seen[k] := 1 offsets.Push({ x: dx, y: dy }) } return offsets } BGR(c) => ((Integer("0x" c) & 0xFF) << 16) | (Integer("0x" c) & 0xFF00) | ((Integer("0x" c) >> 16) & 0xFF) PlaceOverlay() { global g, CORNER, MONITOR, MARGIN_X, MARGIN_Y if !g return n := MONITOR if (n < 1 || n > MonitorGetCount()) n := 1 MonitorGetWorkArea(n, &mL, &mT, &mR, &mB) g.GetPos(, , &w, &h) vert := "bottom", horiz := "left" parts := StrSplit(CORNER, "-") if (parts.Length >= 2) { vert := parts[1] horiz := parts[2] } if (horiz = "left") x := mL + MARGIN_X else if (horiz = "right") x := mR - w - MARGIN_X else x := mL + (mR - mL - w) // 2 if (vert = "top") y := mT + MARGIN_Y else if (vert = "middle") y := mT + (mB - mT - h) // 2 else y := mB - h - MARGIN_Y g.Move(x, y) } ToggleOverlay() { global g if DllCall("IsWindowVisible", "Ptr", g.Hwnd) g.Hide() else g.Show("NoActivate") } BuildMenu() { global INI, LNK A_TrayMenu.Delete() A_TrayMenu.Add("显示 / 隐藏", (*) => ToggleOverlay()) A_TrayMenu.Add("重新载入配置", (*) => Reload()) A_TrayMenu.Add("打开配置文件", (*) => Run('"' INI '"')) A_TrayMenu.Add() A_TrayMenu.Add("开机自启", MenuToggleStartup) A_TrayMenu.Add() A_TrayMenu.Add("退出", (*) => ExitApp()) if FileExist(LNK) A_TrayMenu.Check("开机自启") } MenuToggleStartup(ItemName, ItemPos, MyMenu) { global LNK if FileExist(LNK) { RemoveStartup() MyMenu.Uncheck(ItemName) } else { EnsureStartup() if FileExist(LNK) MyMenu.Check(ItemName) } } EnsureStartup() { global APP_NAME, LNK if FileExist(LNK) return true try { sh := ComObject("WScript.Shell") sc := sh.CreateShortcut(LNK) sc.TargetPath := A_AhkPath sc.Arguments := '"' A_ScriptFullPath '"' sc.WorkingDirectory := A_ScriptDir sc.IconLocation := A_AhkPath ",0" sc.Description := "nameit - 屏幕角落设备名水印" sc.Save() } catch { return false } return FileExist(LNK) ? true : false } RemoveStartup() { global LNK if FileExist(LNK) try FileDelete LNK } UsageText() { return "nameit - 屏幕角落设备名水印`n`n" . "用法:`n" . " 双击 nameit.ahk 启动水印`n" . " nameit.ahk --install 设置开机自启(并启动)`n" . " nameit.ahk --uninstall 取消开机自启`n" . " nameit.ahk --help 显示本帮助`n`n" . "配置:编辑同目录 nameit.ini,托盘右键菜单可打开/重新载入。" } IniTemplate() { return "; ============================================================" "`n" . "; nameit 配置文件(UTF-8)" "`n" . "; 修改后:托盘图标右键 -> 重新载入配置(或重启脚本)" "`n" . "; ============================================================" "`n" . "" "`n" . "[general]" "`n" . "; 显示的文字;留空 = 自动使用本机电脑名" "`n" . "Text=" "`n" . "; 大小写:as-is 原样 / upper 全大写 / lower 全小写" "`n" . "Case=as-is" "`n" . "" "`n" . "[font]" "`n" . "; 字体名;留空 = 自动(纯英文用 Segoe UI,含中文用 Microsoft YaHei UI)" "`n" . "Face=" "`n" . "; 字高(像素)" "`n" . "SizePx=50" "`n" . "; 字重:700 粗体 / 400 常规" "`n" . "Weight=700" "`n" . "" "`n" . "[color]" "`n" . "; 字身颜色(RRGGBB)" "`n" . "Text=FFFFFF" "`n" . "; 描边颜色(RRGGBB)" "`n" . "Stroke=000000" "`n" . "; 描边粗细(像素),0 = 不描边" "`n" . "StrokeWidth=4" "`n" . "" "`n" . "[layout]" "`n" . "; 位置: top / middle / bottom 配 left / center / right" "`n" . "; 例如 bottom-left、top-right、middle-center" "`n" . "Corner=bottom-left" "`n" . "; 显示器编号:1 = 主显示器,2 = 第二块…" "`n" . "Monitor=1" "`n" . "; 到屏幕边缘的间距(像素)" "`n" . "MarginX=24" "`n" . "MarginY=18" "`n" }