Files
nameit/nameit.ahk
T

308 lines
10 KiB
AutoHotkey
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
; ============================================================
; 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"
}