mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 16:31:05 +02:00
218b9249a2
Expose a generic seam so a mod can drive a second screen without the engine owning any dual-screen layout policy: - render.compose hook in Renderer:endFrame hands a mod the finished world + UI canvases, their SGB zones, the frame metrics, Renderer:blitCanvas (lifted from the internal blit closure) and the SecondScreen bridge. Return true to take over the window; no wrap (or calling next) runs the normal single-window composite byte-for-byte. - SecondScreen.lua + the Android Presentation bridge (love_android_ secondary_* in common/android.cpp, GameActivity secondary display) as the optional physical-second-display transport. No battle-render changes: a mod lays out the two screens (including any battle split) itself. Ships with a unit test, no-mod parity via gate_hooks, and docs/modding.md (D14).
63 lines
2.4 KiB
Lua
63 lines
2.4 KiB
Lua
-- Unit coverage for the render.compose seam (D14: a public-API test names
|
|
-- the hook, gate_hooks supplies the no-mod parity, docs/modding.md documents
|
|
-- it). render.compose lets a mod take over window composition: a wrap that
|
|
-- returns true without calling next owns the whole window; a wrap that calls
|
|
-- next lets the engine's normal single-window composite run and decorates it.
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.modkit")
|
|
local Runtime = require("src.mods.Runtime")
|
|
local Hooks = require("src.mods.Hooks")
|
|
|
|
local savedEvents, savedHooks = Runtime.events, Runtime.hooks
|
|
local bus = Hooks.new()
|
|
Runtime.hooks = bus
|
|
|
|
-- the shape Renderer:endFrame hands the hook
|
|
local function fakeCtx()
|
|
return {
|
|
renderer = {}, worldCanvas = {}, uiCanvas = {},
|
|
worldActive = true, zones = {}, worldZones = nil,
|
|
ww = 480, wh = 432, ox = 0, oy = 12, scale = 3,
|
|
dpiX = 1, dpiY = 1, secondScreen = {},
|
|
}
|
|
end
|
|
|
|
-- takeover: a mod drawing its own layout returns true and never calls next,
|
|
-- so the engine's vanilla composite is skipped entirely
|
|
do
|
|
local vanillaRan, gotCtx = false, nil
|
|
bus:wrap("render.compose", function(next, renderer, ctx)
|
|
gotCtx = ctx
|
|
return true
|
|
end, 0, "ds-mod")
|
|
local handled = Runtime.call("render.compose",
|
|
function() vanillaRan = true; return false end,
|
|
{ tag = "renderer" }, fakeCtx())
|
|
T.eq(handled, true, "a mod returning true signals full window takeover")
|
|
T.eq(vanillaRan, false, "takeover skips the engine composite (vanilla not run)")
|
|
T.check(gotCtx ~= nil and gotCtx.ww == 480 and gotCtx.secondScreen ~= nil,
|
|
"the hook receives the frame ctx (metrics, canvases, secondScreen)")
|
|
bus.chains["render.compose"] = nil
|
|
end
|
|
|
|
-- decorate: a mod calling next lets the engine composite run, and the
|
|
-- engine's not-handled return (false) flows back through the chain
|
|
do
|
|
local vanillaRan = false
|
|
bus:wrap("render.compose", function(next, renderer, ctx)
|
|
return next()
|
|
end, 0, "ds-mod")
|
|
local handled = Runtime.call("render.compose",
|
|
function() vanillaRan = true; return false end,
|
|
{ tag = "renderer" }, fakeCtx())
|
|
T.eq(vanillaRan, true, "calling next runs the engine composite")
|
|
T.eq(handled, false, "the engine's not-handled return flows back through next")
|
|
bus.chains["render.compose"] = nil
|
|
end
|
|
|
|
Runtime.events, Runtime.hooks = savedEvents, savedHooks
|
|
|
|
T.finish("render_compose_seam")
|