benilla's layer speaks the 1.12 API alone: handlers take this and no arguments, GetBinding answers the 1.12 list, the Key Bindings page captures through its window, /errors chains the error handler, and the host hooks leave the VM (#569)
benilla's layer now uses only the 1.12 API and the 1.12 Lua forms, and the host hooks it used are gone from the VM. - **The VM calls handlers as 1.12.1 does.** A handler body is compiled as the chunk itself, so `self` in it is an ordinary global (`0x704c70`). A handler is called with no arguments (`0x705199`). The loader reads no `function=` attribute. - **`GetBinding`/`GetNumBindings` answer the 1.12 list** (`0x4b7f60`): `HEADER_*` section rows, plus every key bound to a command, with no category column. The stock `KeyBindingFrame` now shows its section headers and both key columns. - **The Key Bindings page captures keys through its own window**, as `Blizzard_BindingUI` does: the frame's `OnKeyDown` for keys, clicks for mouse buttons, and the wheel. It takes the `Benilla` prefix, so loading the stock binding addon no longer breaks it. The UI pointer feed now hands frames mouse buttons 4 and 5. - **`/errors` keeps its log by chaining the Lua error handler**, and the stock ScriptErrors dialog is unchanged. Load failures that never raise now print one chat line each at world entry. - **Slash commands:** `/reload` and `/errors` are `SlashCmdList` rows in the layer. `/convertraid` is gone; 1.12.1 has no such command. The dev commands are host rows in dev builds only, so a player build answers them with the unknown-command line once. - **Naming:** every global the layer defines is `Benilla`-prefixed. `BenillaBindCapture`, `KeyBindings_OnHostKey`, the three script-error globals and `SubmitChatInput` are removed. Verified: - New tests for each rule and each removed surface; 16 mutations each fail a test. - Region dumps of the Options window (general page, Key Bindings, armed) are identical before and after. - `scripts/gates.sh` and `scripts/smoke.sh` are green. - A live run bound a key, a Shift chord and the middle button through the page, and opened `/errors`. Built on macOS; no platform seam touched. Fixes #539 Fixes #544 Fixes #407 Part of #171 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0f36374445
commit
05e204de10
@@ -1,10 +1,10 @@
|
||||
<Ui>
|
||||
<!--
|
||||
The script error log, which 1.12 does not have, so every name carries the `Benilla` prefix
|
||||
(`tests::reference_surface`); the stock `ScriptErrors` dialog shows one message, this keeps
|
||||
the log. Shaped like the reference's CraftFrame. It polls the engine:
|
||||
`BenillaGetNumScriptErrors()` is (retained, total-ever), `BenillaGetScriptErrorInfo(i)` is
|
||||
`seq, kind, message, count` (1-based, oldest first, nil past the end).
|
||||
The script error log, which 1.12 does not have, so every name carries the `Benilla` prefix;
|
||||
the stock `ScriptErrors` dialog shows one message, this keeps the log. Shaped like the
|
||||
reference's CraftFrame. It collects as a 1.12 error-log addon does: it wraps the handler
|
||||
`geterrorhandler` holds, the stock `_ERRORMESSAGE` (BasicControls.xml:16), which still runs
|
||||
after it. An addon that later calls `seterrorhandler` replaces the chain, as in 1.12.
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
-- A pushable above every stock panel's, so a game window opening pushes the log aside.
|
||||
@@ -18,6 +18,39 @@
|
||||
-- A `seq`, not an index: indices shift when the cap evicts.
|
||||
BenillaScriptLogSelected = nil
|
||||
|
||||
-- Distinct messages kept before the oldest is evicted.
|
||||
BENILLA_SCRIPTLOG_CAP = 256
|
||||
-- Rows oldest first, each { seq, message, count }; `total` is the last `seq` handed out,
|
||||
-- kept apart from the row count so an eviction or a clear never renumbers.
|
||||
BenillaScriptLog = { rows = {}, total = 0 }
|
||||
|
||||
-- A repeat bumps its row's count and keeps its place.
|
||||
function BenillaScriptLog_Record(message)
|
||||
if type(message) ~= "string" then message = tostring(message) end
|
||||
local log = BenillaScriptLog
|
||||
local found = nil
|
||||
for i = 1, table.getn(log.rows) do
|
||||
if log.rows[i].message == message then found = log.rows[i] end
|
||||
end
|
||||
if found then
|
||||
found.count = found.count + 1
|
||||
else
|
||||
log.total = log.total + 1
|
||||
if table.getn(log.rows) >= BENILLA_SCRIPTLOG_CAP then table.remove(log.rows, 1) end
|
||||
table.insert(log.rows, { seq = log.total, message = message, count = 1 })
|
||||
end
|
||||
if BenillaScriptLogFrame and BenillaScriptLogFrame:IsVisible() then
|
||||
BenillaScriptLog_Update()
|
||||
end
|
||||
end
|
||||
|
||||
-- The chain: the log first, guarded so a fault in it cannot stop the stock handler.
|
||||
local benillaStockErrorHandler = geterrorhandler()
|
||||
seterrorhandler(function(message)
|
||||
pcall(BenillaScriptLog_Record, message)
|
||||
return benillaStockErrorHandler(message)
|
||||
end)
|
||||
|
||||
-- `strsub` counts bytes, so the cut backs up over UTF-8 continuation bytes (0x80..0xBF).
|
||||
function BenillaScriptLog_RowText(message)
|
||||
local cut = strfind(message, "\n", 1, 1)
|
||||
@@ -35,18 +68,10 @@
|
||||
return head
|
||||
end
|
||||
|
||||
-- `load` red (the addon is not running), `error` amber (code raised), `warn` grey (a call
|
||||
-- was accepted and quietly did nothing).
|
||||
function BenillaScriptLog_RowColor(kind)
|
||||
if kind == "load" then return 1.0, 0.3, 0.3 end
|
||||
if kind == "warn" then return 0.65, 0.65, 0.65 end
|
||||
return 1.0, 0.82, 0.0
|
||||
end
|
||||
|
||||
function BenillaScriptLog_Update()
|
||||
local shown, total = BenillaGetNumScriptErrors()
|
||||
local shown = table.getn(BenillaScriptLog.rows)
|
||||
local total = BenillaScriptLog.total
|
||||
|
||||
-- "Problems", because two of the three kinds are not errors.
|
||||
local summary
|
||||
if total == 0 then
|
||||
summary = "No problems this session."
|
||||
@@ -68,18 +93,17 @@
|
||||
for i = 1, BENILLA_SCRIPTLOG_ROWS do
|
||||
local button = getglobal("BenillaScriptLogRow" .. i)
|
||||
local index = offset + i
|
||||
local seq, kind, message, count = BenillaGetScriptErrorInfo(index)
|
||||
if seq then
|
||||
button.seq = seq
|
||||
button.message = message
|
||||
local row = BenillaScriptLog.rows[index]
|
||||
if row then
|
||||
button.seq = row.seq
|
||||
button.message = row.message
|
||||
-- `#12 x1113`: one deduplicated failure that happened 1,113 times.
|
||||
local label = "#" .. seq
|
||||
if count > 1 then label = label .. " x" .. count end
|
||||
local label = "#" .. row.seq
|
||||
if row.count > 1 then label = label .. " x" .. row.count end
|
||||
button.index:SetText(label)
|
||||
button.label:SetText(BenillaScriptLog_RowText(message))
|
||||
button.label:SetTextColor(BenillaScriptLog_RowColor(kind))
|
||||
button.label:SetText(BenillaScriptLog_RowText(row.message))
|
||||
button:Show()
|
||||
if seq == BenillaScriptLogSelected then selectedRow = button end
|
||||
if row.seq == BenillaScriptLogSelected then selectedRow = button end
|
||||
else
|
||||
button.seq = nil
|
||||
button.message = nil
|
||||
@@ -103,11 +127,9 @@
|
||||
function BenillaScriptLog_UpdateDetail()
|
||||
local text = ""
|
||||
if BenillaScriptLogSelected then
|
||||
local shown = BenillaGetNumScriptErrors()
|
||||
for i = 1, shown do
|
||||
local seq, _, message = BenillaGetScriptErrorInfo(i)
|
||||
if seq == BenillaScriptLogSelected then
|
||||
text = message
|
||||
for _, row in ipairs(BenillaScriptLog.rows) do
|
||||
if row.seq == BenillaScriptLogSelected then
|
||||
text = row.message
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -133,7 +155,7 @@
|
||||
BenillaScriptLog_Update()
|
||||
end
|
||||
|
||||
function BenillaScriptLogFrame_OnLoad(self)
|
||||
function BenillaScriptLogFrame_OnLoad()
|
||||
-- Masks the square icon to the portrait ring.
|
||||
SetPortraitToTexture("BenillaScriptLogFramePortrait",
|
||||
"Interface\\Icons\\INV_Misc_Note_01")
|
||||
@@ -157,7 +179,7 @@
|
||||
|
||||
-- `/errors clear`: keeps the numbering, so a number never names two failures.
|
||||
function BenillaScriptLog_Clear()
|
||||
BenillaClearScriptErrors()
|
||||
BenillaScriptLog.rows = {}
|
||||
BenillaScriptLogSelected = nil
|
||||
if getglobal("BenillaScriptLogFrame"):IsVisible() then
|
||||
BenillaScriptLog_Update()
|
||||
@@ -184,10 +206,10 @@
|
||||
<HighlightTexture file="Interface\Buttons\UI-Listbox-Highlight2" alphaMode="ADD"/>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
self.index = getglobal(self:GetName() .. "Index")
|
||||
self.label = getglobal(self:GetName() .. "Label")
|
||||
this.index = getglobal(this:GetName() .. "Index")
|
||||
this.label = getglobal(this:GetName() .. "Label")
|
||||
</OnLoad>
|
||||
<OnClick>BenillaScriptLogRow_OnClick(self)</OnClick>
|
||||
<OnClick>BenillaScriptLogRow_OnClick(this)</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
@@ -263,10 +285,10 @@
|
||||
<Size><AbsDimension x="293" y="128"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-314"/></Offset></Anchor></Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>self:SetScrollChild(getglobal(self:GetName() .. "Child"))</OnLoad>
|
||||
<OnMouseWheel>BenillaScroll_OnMouseWheel(self, arg1)</OnMouseWheel>
|
||||
<OnVerticalScroll>BenillaScroll_SyncBar(self)</OnVerticalScroll>
|
||||
<OnScrollRangeChanged>BenillaScroll_SyncBar(self)</OnScrollRangeChanged>
|
||||
<OnLoad>this:SetScrollChild(getglobal(this:GetName() .. "Child"))</OnLoad>
|
||||
<OnMouseWheel>BenillaScroll_OnMouseWheel(this, arg1)</OnMouseWheel>
|
||||
<OnVerticalScroll>BenillaScroll_SyncBar(this)</OnVerticalScroll>
|
||||
<OnScrollRangeChanged>BenillaScroll_SyncBar(this)</OnScrollRangeChanged>
|
||||
</Scripts>
|
||||
<Frames>
|
||||
<Slider name="$parentBar" inherits="BenillaScrollBarTemplate" hidden="true">
|
||||
@@ -274,7 +296,7 @@
|
||||
<Anchor point="TOPLEFT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="6" y="-16"/></Offset></Anchor>
|
||||
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="6" y="16"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts><OnValueChanged>BenillaScrollBar_OnValueChanged(self, arg1)</OnValueChanged></Scripts>
|
||||
<Scripts><OnValueChanged>BenillaScrollBar_OnValueChanged(this, arg1)</OnValueChanged></Scripts>
|
||||
</Slider>
|
||||
<Frame name="$parentChild">
|
||||
<Size><AbsDimension x="293" y="128"/></Size>
|
||||
@@ -304,34 +326,32 @@
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>BenillaScriptLogFrame_OnLoad(self)</OnLoad>
|
||||
<OnLoad>BenillaScriptLogFrame_OnLoad()</OnLoad>
|
||||
<!-- A spin bubbles up the parent chain; the detail pane's own handler wins there. -->
|
||||
<OnMouseWheel>BenillaScriptLogFrame_OnMouseWheel(arg1)</OnMouseWheel>
|
||||
<OnShow>BenillaScriptLog_Update()</OnShow>
|
||||
<OnUpdate>BenillaScriptLogFrame_OnUpdate()</OnUpdate>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
<!-- Benilla's slash commands, handed to SubmitChatInput (ui_chat::input); loads after the
|
||||
stock ChatFrame.xml, which declares SlashCmdList. -->
|
||||
<!-- The player's commands, as an addon registers them; after the stock ChatFrame.lua, which
|
||||
declares SlashCmdList. -->
|
||||
<Script><![CDATA[
|
||||
local BENILLA_COMMANDS = {
|
||||
{ "CHATTEST", "/chattest" },
|
||||
{ "SHOT", "/shot" },
|
||||
{ "LIQUID", "/liquid" },
|
||||
{ "CASTVIS", "/castvis" },
|
||||
{ "PARTYTEST", "/partytest" },
|
||||
{ "REACTION", "/reaction", "/react" },
|
||||
{ "RELOAD", "/reload" },
|
||||
{ "ERRORS", "/errors", "/err" },
|
||||
{ "CONVERTRAID", "/convertraid" },
|
||||
}
|
||||
for _, row in ipairs(BENILLA_COMMANDS) do
|
||||
local key = "BENILLA_" .. row[1]
|
||||
for i = 2, table.getn(row) do
|
||||
setglobal("SLASH_" .. key .. (i - 1), row[i])
|
||||
end
|
||||
SlashCmdList[key] = function(msg)
|
||||
SubmitChatInput(row[2] .. " " .. (msg or ""))
|
||||
-- Deviation: 1.12 has no `/reload` (later clients added it; 1.12 reloads through
|
||||
-- `/console reloadui`); kept so a player can reload the interface after toggling addons.
|
||||
SLASH_BENILLA_RELOAD1 = "/reload"
|
||||
SlashCmdList["BENILLA_RELOAD"] = function()
|
||||
ReloadUI()
|
||||
end
|
||||
|
||||
-- `/errors` toggles the log and `/errors clear` empties it; `/err` is the short form.
|
||||
SLASH_BENILLA_ERRORS1 = "/errors"
|
||||
SLASH_BENILLA_ERRORS2 = "/err"
|
||||
SlashCmdList["BENILLA_ERRORS"] = function(msg)
|
||||
local arg = string.lower((string.gsub(msg or "", "^%s*(.-)%s*$", "%1")))
|
||||
if arg == "clear" then
|
||||
BenillaScriptLog_Clear()
|
||||
else
|
||||
BenillaScriptLog_Toggle()
|
||||
end
|
||||
end
|
||||
]]></Script>
|
||||
|
||||
Reference in New Issue
Block a user