Link search Menu Expand Document

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