; ========== CapsLockX ========== ; 名称:CapsLockX 程序入口 ; 描述:用于生成模块加载组件、集成模块帮助文件、智能重载 CapsLockX 核心等功能。 ; 作者:snomiao ; 联系:snomiao@gmail.com ; 支持:https://github.com/snomiao/CapsLockX ; 编码:UTF-8 with BOM ; 版权:Copyright © 2017-2024 Snowstar Laboratory. All Rights Reserved. ; 感谢:张工 QQ: 45289331 参与调试 ; LICENCE: GNU GPLv3 ; ========== CapsLockX ========== if (A_IsAdmin) { #SingleInstance Force ; 管理员权限下跳过对话框并自动替换旧实例 } else { #SingleInstance, Off ; 普通用户无法替换管理员权限实例,故 Off,使用多开的方式修改配置文件触发管理员权限实例重载 } SetWorkingDir, %A_ScriptDir% #Include %A_ScriptDir%/Core/CapsLockX-Config.ahk #Include %A_ScriptDir%/Core/CapsLockX-RunSilent.ahk #Include %A_ScriptDir%/Core/CapsLockX-i18n.ahk global CLX_ModuleDir := "./Modules" ; global CLX_用户模块路径 := CLX_ConfigDir "/Modules" global CLX_CoreDir := "./Core" ; 版本 global CLX_Version FileRead, CLX_Version, ./Core/version.txt CLX_Version := CLX_Version ? CLX_Version : "未知版本" global CLX_VersionName := "v" CLX_Version ; 加载过程提示 global 显示加载提示 := 1 global loadingTips := "" ; 对 核心模块 进行 编码清洗 CONVERT_FILE_TO_UTF8_WITH_BOM_ENCODING(CLX_CoreDir "/CapsLockX-Config.ahk") CONVERT_FILE_TO_UTF8_WITH_BOM_ENCODING(CLX_CoreDir "/CapsLockX-Core.ahk") CONVERT_FILE_TO_UTF8_WITH_BOM_ENCODING(CLX_CoreDir "/CapsLockX-RunSilent.ahk") CONVERT_FILE_TO_UTF8_WITH_BOM_ENCODING(CLX_CoreDir "/CapsLockX-Update.ahk") CONVERT_FILE_TO_UTF8_WITH_BOM_ENCODING(CLX_CoreDir "/CapsLockX-i18n.ahk") ; 复制用户模块 ; TODO FIX:如果CLX已经开了的话,这一步会触发重启,这可能会导致一些文件冲突的BUG…… FileDelete, %CLX_ModuleDir%/*.user.ahk FileDelete, %CLX_ModuleDir%/*.user.md FileCopy %CLX_ConfigDir%/*.user.ahk, %CLX_ModuleDir%/, 1 FileCopy %CLX_ConfigDir%/*.user.md, %CLX_ModuleDir%/, 1 ; 模块加载器路径 global CLX_ModulesRunner := "Core\CapsLockX-ModulesRunner.ahk" global CLX_ModulesFunctions := "Core\CapsLockX-ModulesFunctions.ahk" ; LoadModules(CLX_ModulesRunner, CLX_ModulesFunctions) ; 判断安装方式 global NPM全局安装也 := InStr(A_ScriptFullPath, APPDATA) == 1 && InStr("node_modules", A_ScriptFullPath) global GIT仓库安装也 := "true" == Trim(CLX_RunSilent("cmd /c git rev-parse --is-inside-work-tree"), "`r`n`t` ") ; 模块帮助向README编译() ; 隐藏 ToolTip #NoTrayIcon ; 隐藏托盘图标 ToolTip ; 当 CI_TEST 启用时,仅测试编译效果,不启动核心 EnvGet ENVIROMENT, ENVIROMENT ; msgbox % DllCall("GetCommandLine", "str") ; msgbox % !!RegExMatch(DllCall("GetCommandLine", "str"), "/CI_TEST") if ("CI_TEST" == ENVIROMENT || !!RegExMatch(DllCall("GetCommandLine", "str"), "/CI_TEST")) { tooltip % "[INFO] MODULE LOAD OK, SKIP CORE" OutputDebug, % "[INFO] MODULE LOAD OK, SKIP CORE" ExitApp } else { CapsLockX启动() } ; #Persistent ; SetTimer, CapsLockX启动, -1 Return 模块帮助向README编译(){ ; 编译README.md INPUT_README_FILE := "./README.md" FileEncoding, UTF-8-Raw FileRead, source, %INPUT_README_FILE% ; 编译一次 global 显示加载提示 显示加载提示 := 0 target := 模块编译和帮助README更新(source) if (target == source) { traytip MODULE_HELP_NOT_CHANGED return "NOT_CHANGED" } ; 如果不一样,就再编译一次,并且显示加载提示 显示加载提示 := 1 加载提示追加(t("模块帮助有变更")) ; 然后进行稳定性检查 source := 模块编译和帮助README更新(target) if (target != source) { MsgBox % t("警告:模块帮助未能稳定更新,如果你看到了这个消息,请联系雪星(QQ:997596439, 邮箱: snomiao@gmail.com)") } ; 输出到 ./README.md (用于 github-pages ) ; docs_target := 模块编译和帮助README更新(source, 1) FileDelete % INPUT_README_FILE FileAppend %target%, % INPUT_README_FILE, UTF-8-Raw ; 输出根目录 README.md (用于 github 首页) ; ; FileDelete ./README.md ; PREFIX := "`n`n" ; ; replace docs/media ; StringReplace, target, target, % "(./", % "(./docs/", All ; StringReplace, target, target, % "( ./", % "( ./docs/", All ; ; StringReplace, target, target, ./media/, ./docs/media/, All ; ; StringReplace, target, target, ./docs/, ./docs/, All ; FileAppend %PREFIX%%target%, ./README.md, UTF-8-Raw ; Reload ; ExitApp } 加载提示追加(msg, clear = 0){ global 显示加载提示 if(!显示加载提示) return if (clear || loadingTips == "") { loadingTips := "CapsLockX " CLX_Version "`n" } loadingTips .= msg "`n" } 加载提示显示(){ ToolTip % loadingTips sleep 1000 } 模块编译和帮助README更新(sourceREADME){ lang := "en" FileEncoding UTF-8-Raw ; 列出模块文件 ModuleFiles := "" ; read as module dir loop, Files, %CLX_ModuleDir%\*, D { ; Do not Recurse into subfolders. 子文件夹由模块自己去include去加载 ModuleFile := A_LoopFileName "/" A_LoopFileName ".ahk" ; MsgBox, % CLX_ModuleDir "/" ModuleFile if (FileExist(CLX_ModuleDir "/" ModuleFile)) { ModuleFiles .= ModuleFile "`n" ; MsgBox, ModuleFile %ModuleFile% } } ; Msgbox % "ModuleFiles" ModuleFiles loop, Files, %CLX_ModuleDir%\*.ahk { ; Do not Recurse into subfolders. 子文件夹由模块自己去include去加载 ModuleFiles .= A_LoopFileName "`n" } ModuleFiles := Trim(ModuleFiles, "`n") Sort ModuleFiles ; 生成帮助 全部模块帮助 := "" i := 0 loop, Parse, ModuleFiles, `n { i++ ; 匹配模块名 模块文件 := A_LoopField 匹配结果 := false 匹配结果 := 匹配结果 || RegExMatch(A_LoopField, "O)(?:.*/)?((?:.*[-])*)(.*?)(?:\.user)?\.ahk", Match) 匹配结果 := 匹配结果 || RegExMatch(A_LoopField, "O)(?:.*/)?((?:.*[-])*)(.*?)(?:\.用户)?\.ahk", Match) if (!匹配结果) { ; Msgbox % "模块文件" 模块文件 Continue } 模块文件名称 := Match[1] Match[2] ; Msgbox % "模块文件名称" 模块文件名称 模块名称 := Match[2] 模块帮助内容 := "" 模块帮助文件 := "" if (!模块帮助内容) { 模块帮助文件 := CLX_ModuleDir "/" 模块名称 ".md" if (FileExist(模块帮助文件)) { FileRead, 模块帮助内容, %模块帮助文件% } } if (!模块帮助内容) { 模块帮助文件 := CLX_ModuleDir "/" 模块文件名称 ".md" if (FileExist(模块帮助文件)) { FileRead, 模块帮助内容, %模块帮助文件% } } ; 加载模块描述 FileRead, 模块文件内容, % CLX_ModuleDir "/" 模块文件 T%模块名称%_Disabled := CLX_Config("ModuleDisable", "T" 模块名称 "_Disabled", 0, "是否禁用模块:" 模块名称 (模块描述1 ? " - " 模块描述1 : "") ) if (模块帮助内容) { 模块帮助内容 := Trim(模块帮助内容, " `t`n") 加载提示追加(t("加载模块帮助:") . i . "-" . 模块名称) 全部模块帮助 .= "" "`n`n" ; 替换资源链接的相对目录(图片gif等) FileCopy, % CLX_ModuleDir . "\*.gif", .\docs\media\, 1 FileCopy, % CLX_ModuleDir . "\*.png", .\docs\media\, 1 ; FileCopy, %CLX_ModuleDir%\*.gif, .\docs\media\, 1 ; FileCopy, %CLX_ModuleDir%\*.png, .\docs\media\, 1 ; 模块帮助内容 := RegExReplace(模块帮助内容, "m)\[(.*)\]\(\s*?\.\/(.*?)\)", "[$1](./media/$2)") 模块帮助内容 := RegExReplace(模块帮助内容, "m)\[(.*)\]\(\s*?\.\/(.*?)\)", "[$1](./docs/media/$2)") ; 没有标题的,给自动加标题 if (!RegExMatch(模块帮助内容, "^#")) { ; if (T%模块名称%_Disabled) { ; 模块帮助内容 .= "### " 模块名称 "模块(禁用)" "`n" ; } else { ; 模块帮助内容 .= "### " 模块名称 "模块" "`n" ; } 模块帮助内容 .= "### " 模块名称 "模块" "`n" } ; translate to en ; 模块帮助内容 := t(模块帮助内容, lang) 模块帮助内容 := t(模块帮助内容) ; 替换标题层级 模块帮助内容 := RegExReplace(模块帮助内容, "(?<=^|\n)#", "###") 全部模块帮助 .= 模块帮助内容 . "`n`n" } if (T%模块名称%_Disabled) { 加载提示追加(t("跳过模块:") . i . " " . 模块名称) } else { ; 这里引入模块代码 CONVERT_FILE_TO_UTF8_WITH_BOM_ENCODING(CLX_ModuleDir "/" 模块文件) ; 导入模块 模块初始化代码 .= "GoSub CLX_ModuleSetup_" i "`n" 模块导入代码 .= "`n" "#If" "`n" "`n" 模块导入代码 .= "CLX_ModuleSetup_" i ":" "`n" if (模块帮助内容 && 模块帮助文件) { 模块导入代码 .= " " " " " " " " "CLX_THIS_MODULE_HELP_FILE_PATH = " 模块帮助文件 "`n" } else { 模块导入代码 .= " " " " " " " " "CLX_THIS_MODULE_HELP_FILE_PATH := """"" "`n" } 模块导入代码 .= " " " " " " " " "#Include " CLX_ModuleDir "/" 模块文件 "`n" 模块导入代码 .= "Return" "`n" 加载提示追加(t("运行模块:") . i . " " . 模块名称) } } 加载提示显示() ; 拼接模块加载器代码 常量语句 .= "; " . t("请勿直接编辑本文件,以下内容由核心加载器自动生成。雪星/(20210318)") . "`n" 常量语句 .= "global CLX_ModuleDir := " """" CLX_ModuleDir """" "`n" 常量语句 .= "global CLX_CoreDir := " """" CLX_CoreDir """" "`n" 常量语句 .= "global CLX_Version := " """" CLX_Version """" "`n" 常量语句 .= "global CLX_VersionName := " """" CLX_VersionName """" "`n" 模块运行器 .= 常量语句 "`n" 模块初始化代码 模块加载器 .= "Return" "`n" 模块导入代码 FileEncoding UTF-8 FileDelete %CLX_ModulesRunner% FileAppend %模块运行器%, %CLX_ModulesRunner% if (!FileExist(CLX_ModulesRunner)) { msg = msg .= t("Unable to write ModulesRunner.ahk, if you are install with chocolatey, run me as admin at the first time please.") msg .= "`n" msg .= t("注意:未能写入模块运行器,如果使用 chocolatey 首次安装后,请以管理员权限运行。") msgbox %msg% } FileDelete %CLX_ModulesFunctions% FileAppend %模块加载器%, %CLX_ModulesFunctions% 加载提示显示() 全部模块帮助 := Trim(全部模块帮助, " `t`n") ; 生成 README 替换代码 NeedleRegEx := "m)(\s*)()([\s\S]*)\r?\n(\s*)(\r?\n)" Replacement := "$1$2`n" 全部模块帮助 "`n$4$5" targetREADME := RegExReplace(sourceREADME, NeedleRegEx, Replacement, Replaces) ; msgbox % 全部模块帮助 ; 检查 README 替换情况 if (!Replaces) { Run https://capslockx.snomiao.com/ MsgBox % t("加载模块帮助遇到错误。") . "`n" . t("已为你打开 CapsLockX 主页,请手动更新 CapsLockX") MsgBox % targetREADME Return sourceREADME } Return targetREADME } CapsLockX启动(){ CoreAHK := CLX_CoreDir "\CapsLockX-Core.ahk" UpdatorAHK := CLX_CoreDir "\CapsLockX-Update.ahk" ; 为了避免运行时对更新模块的影响,先把 EXE 文件扔到 Temp 目录,然后再使用 Temp 里的 AHK 来运行本核心。 AHK_EXE_CORE_PATH := "./Core/capslockx-ahkv1.exe" AHK_EXE_ROOT_PATH := "CapsLockX.exe" AHK_EXE_TEMP_PATH := A_Temp "/CapsLockX-AHK.exe" FileCopy, %AHK_EXE_CORE_PATH%, %AHK_EXE_TEMP_PATH%, 1 if (!FileExist(AHK_EXE_TEMP_PATH)) { FileCopy, %AHK_EXE_ROOT_PATH%, %AHK_EXE_TEMP_PATH%, 1 } if (!FileExist(AHK_EXE_TEMP_PATH)) { AHK_EXE_TEMP_PATH := AHK_EXE_CORE_PATH } ; 运行更新组件 Run %AHK_EXE_TEMP_PATH% %UpdatorAHK%, %A_ScriptDir% ; 运行核心 ; 启动 global T_AskRunAsAdmin := CLX_ConfigGet("Core", "T_AskRunAsAdmin", 0) adminCommand := RegExMatch(DllCall("GetCommandLine", "str"), "/admin") ; Forward --no-core flag from Rust launcher to Core noCoreFlag := RegExMatch(DllCall("GetCommandLine", "str"), "--no-core") ? " --no-core" : "" if (!A_IsAdmin && T_AskRunAsAdmin || adminCommand) { RunWait *RunAs %AHK_EXE_TEMP_PATH% %CoreAHK%%noCoreFlag%, %A_ScriptDir% } else { RunWait %AHK_EXE_TEMP_PATH% %CoreAHK%%noCoreFlag%, %A_ScriptDir% } if (ErrorLevel) { MsgBox, 4, % t("CapsLockX 错误"), % t("CapsLockX 异常退出,是否重载?") IfMsgBox No return Reload } else { TrayTip, % t("CapsLockX 退出"), % t("CapsLockX 已退出。") Sleep, 1000 } ExitApp }