mods: 社区修改
mods 是用于创建 norns 系统软件核心功能自定义修改的小代码块。mods 是修改 norns 基本功能的特性,对所有脚本都有效,但这些特性不需要包含在基础的 norns 代码库中。
安装 mod
mods 可以通过 maiden 项目管理器 安装,与其他社区脚本一起。安装 mod 后,请确保使用 SYSTEM > RESTART 重启 norns,然后转到下一节的说明。
启用/禁用 mod
就其本质而言,mods 可能对系统稳定性产生负面影响,或进行可能不被普遍欢迎的系统级更改。即使已安装,mod 在默认情况下也会以未加载状态启动。必须明确启用 mod 并重启 norns,其功能才会可用。
要启用 mod,请访问 SYSTEM > MODS 查看已安装 mod 的列表。已加载的 mod 在其名称左侧会有一个小点。使用 E2 选择列表中的项目,然后使用 E3 根据需要启用或禁用。未加载的 mod 在其名称右侧会显示 +,表示重启后将启用(并因此加载)。已加载的 mod 在其名称右侧会显示 -,表示重启后将禁用。
有菜单的已加载 mod 在其名称末尾会有 >。按 K3 将进入 mod 的菜单。
编写 mod
mod 像标准的 norns 脚本一样位于 dust 文件夹中。与 norns 脚本不同,mod 中的代码在 matron 启动时被加载。mods 可以全局修改或扩展 Lua 环境,使更改对所有脚本可见。
mods 通过注册函数来工作,这些函数由 matron 钩子调用。matron 定义了五个钩子:
system_post_startup- 在 matron 完全启动且系统状态已恢复但在运行任何脚本之前调用system_pre_shutdown- 从菜单中选择SYSTEM > SLEEP时调用script_pre_init- 在脚本加载后但在其引擎启动、pmap 设置恢复和init()函数调用之前调用script_post_init- 在脚本的init()函数被调用后调用script_post_cleanup- 在脚本的cleanup()函数被调用后调用,通常在脚本之间切换时发生
请注意,钩子用于发生在任何 mod 之外的事件 - 围绕系统的启动和关闭,以及围绕任何脚本的加载和卸载。因此,通过向钩子注册函数,mods 可以与这些事物交互。
多个 mods 可以为给定钩子各自注册自己的回调函数。matron 将在执行钩子之前按字母顺序排序。这允许添加参数的 mods 以一致的顺序添加它们 - 例如,需要在执行前获得所有参数的 mods 应该以 ~ 开头,以确保它们在最后。
在注册函数内生成的任何错误都会被捕获并在 maiden REPL 中被忽略。
mods 还可以通过 SYSTEM > MODS 菜单提供自己的菜单页面。
mod 示例
mod 是任何包含名为 lib/mod.lua 文件的 norns 脚本/项目。这种布局允许 mod 是独立项目或简单地与现有 norns 脚本捆绑在一起的功能。mod 可以根据需要自由 require 其他模块。
此示例演示了 mod 开发方法:
--
-- 需要 `mods` 模块以获得对钩子、菜单和其他实用函数的访问权限。
--
local mod = require 'core/mods'
--
-- [可选] mod 就像任何普通的 lua 模块。局部变量可用于保存需要在钩子、菜单和 mod 本身提供的任何 api 之间访问的任何状态。
--
-- 这里使用单个表来保存一些 x/y 值
--
local state = {
x = 0,
y = 0,
}
--
-- [可选] 钩子本质上是回调,可以被多个 mods 同时使用。向钩子注册的每个函数还必须包含一个名称。使用现有函数的名称注册新函数将替换现有函数。使用描述性名称(包括 mod 本身的名称)可以帮助调试,因为回调函数的名称将在调用回调函数之前由 matron 打印出来(在 maiden 中可见)。
--
-- 这里我们有虚拟功能来帮助确认事物被调用并通过 mod 提供的函数测试对 mod 级别状态的访问。
--
mod.hook.register("system_post_startup", "my startup hacks", function()
state.system_post_startup = true
end)
mod.hook.register("script_pre_init", "my init hacks", function()
-- 在脚本 `init()` 函数被调用之前在这里调整全局环境
end)
--
-- [可选] 菜单:扩展菜单系统是通过创建一个包含所有必需菜单函数的表来完成的。
--
local m = {}
m.key = function(n, z)
if n == 2 and z == 1 then
-- 返回到 mod 选择菜单
mod.menu.exit()
end
end
m.enc = function(n, d)
if n == 2 then state.x = state.x + d
elseif n == 3 then state.y = state.y + d end
-- 告诉菜单系统重绘,这反过来调用 mod 的菜单重绘函数
mod.menu.redraw()
end
m.redraw = function()
screen.clear()
screen.move(64,40)
screen.text_center(state.x .. "/" .. state.y)
screen.update()
end
m.init = function() end -- 在菜单进入时,即,如果你想启动计时器
m.deinit = function() end -- 在菜单退出时
-- 注册 mod 菜单
--
-- 注意:`mod.this_name` 是一个便利变量,将被设置为正在加载的 mod 的名称。为了使菜单工作,它必须用与 dust 文件夹中 mod 名称匹配的名称注册。
--
mod.menu.register(mod.this_name, m)
--
-- [可选] 从模块返回值允许 mod 通过正常的 lua `require` 函数向脚本提供库功能。
--
-- 注意:脚本使用 `require` 加载 mod 功能而不是 norns 特定的 `include` 函数很重要。使用 `require` 确保只加载一个 mod 副本。如果脚本使用 `include`,每次运行脚本时都会加载菜单、钩子函数和状态的新副本,替换之前注册的函数/菜单。
--
-- 这里我们提供一个单一函数,允许脚本获取 mod 的状态表。在脚本中使用这个函数看起来像:
--
-- local mod = require 'name_of_mod/lib/mod'
-- local the_state = mod.get_state()
--
local api = {}
api.get_state = function()
return state
end
return api