demo: port all Widgets sections, ShowStyleEditor/ShowStyleSelector, menus; fix crashes found by click fuzzer (color/scalar default args, InputTextWithHint string hint, AddText overload, splitter reset, CollapsingHeader close button, callback data methods, SetKeyboardFocusHere & misc getters)

This commit is contained in:
4DBug
2026-10-08 08:06:00 -05:00
parent 701d1d3449
commit d7a2b8a4df
10 changed files with 4386 additions and 23 deletions
+1 -1
View File
@@ -1 +1 @@
[[12, "imgui.lua"], [13676, "imgui_demo.lua"], [14613, "imgui_demo_2.lua"], [17688, "imgui_docking.lua"], [20816, "imgui_dragdrop.lua"], [21108, "imgui_draw.lua"], [26103, "imgui_h.lua"], [28257, "imgui_impl_roblox.lua"], [29022, "imgui_impl_roblox_fonts.lua"], [29800, "imgui_internal.lua"], [32956, "imgui_tables.lua"], [37303, "imgui_tabs.lua"], [38470, "imgui_widgets.lua"], [46945, "imstb_rectpack.lua"], [47391, "imstb_textedit.lua"], [48632, "imstb_truetype.lua"], [51100, "imstd_minstdio.lua"]] [[12, "imgui.lua"], [13756, "imgui_demo.lua"], [14689, "imgui_demo_2.lua"], [17764, "imgui_demo_widgets.lua"], [19688, "imgui_docking.lua"], [22816, "imgui_dragdrop.lua"], [23108, "imgui_draw.lua"], [28114, "imgui_h.lua"], [30324, "imgui_impl_roblox.lua"], [31089, "imgui_impl_roblox_fonts.lua"], [31867, "imgui_internal.lua"], [35023, "imgui_tables.lua"], [39370, "imgui_tabs.lua"], [40537, "imgui_widgets.lua"], [49061, "imstb_rectpack.lua"], [49507, "imstb_textedit.lua"], [50748, "imstb_truetype.lua"], [53216, "imstd_minstdio.lua"]]
+2160 -11
View File
File diff suppressed because it is too large Load Diff
+80
View File
@@ -12930,6 +12930,86 @@ function ImGui.BeginTooltipHidden()
return ret return ret
end end
-- [SECTION] MISC GETTERS/SETTERS, FOCUS
function ImGui.NavMoveRequestResolveWithLastItem(result)
local g = GImGui
g.NavMoveScoringItems = false -- Ensure request doesn't need more processing
ImGui.NavApplyItemToResult(result)
ImGui.NavUpdateAnyRequestFlag()
end
function ImGui.SetKeyboardFocusHere(offset)
if offset == nil then offset = 0 end
local g = GImGui
local window = g.CurrentWindow
IM_ASSERT(offset >= -1) -- -1 is allowed but not below
if g.DragDropActive or g.MovingWindow ~= nil then
return
end
ImGui.SetNavWindow(window)
local move_flags = bit32.bor(ImGuiNavMoveFlags.IsTabbing, ImGuiNavMoveFlags.Activate, ImGuiNavMoveFlags.FocusApi, ImGuiNavMoveFlags.NoSetNavCursorVisible)
local scroll_flags = window.Appearing and bit32.bor(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.AlwaysCenterY) or bit32.bor(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.KeepVisibleEdgeY)
ImGui.NavMoveRequestSubmit(ImGuiDir.None, (offset < 0) and ImGuiDir.Up or ImGuiDir.Down, move_flags, scroll_flags)
if offset == -1 then
ImGui.NavMoveRequestResolveWithLastItem(g.NavMoveResultLocal)
else
g.NavTabbingDir = 1
g.NavTabbingCounter = offset + 1
end
end
function ImGui.FocusItem()
local g = GImGui
local window = g.CurrentWindow
if g.DragDropActive or g.MovingWindow ~= nil then
return
end
local move_flags = bit32.bor(ImGuiNavMoveFlags.IsTabbing, ImGuiNavMoveFlags.FocusApi, ImGuiNavMoveFlags.NoSetNavCursorVisible, ImGuiNavMoveFlags.NoSelect)
local scroll_flags = window.Appearing and bit32.bor(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.AlwaysCenterY) or bit32.bor(ImGuiScrollFlags.KeepVisibleEdgeX, ImGuiScrollFlags.KeepVisibleEdgeY)
ImGui.SetNavWindow(window)
ImGui.NavMoveRequestSubmit(ImGuiDir.None, ImGuiDir.Up, move_flags, scroll_flags)
ImGui.NavMoveRequestResolveWithLastItem(g.NavMoveResultLocal)
end
function ImGui.ActivateItemByID(id)
local g = GImGui
g.NavNextActivateId = id
g.NavNextActivateFlags = ImGuiActivateFlags.None
end
function ImGui.SetNavCursorVisible(visible)
local g = GImGui
if g.NavWindow and bit32.band(g.NavWindow.Flags, ImGuiWindowFlags.NoNavInputs) ~= 0 then
visible = false
elseif g.IO.ConfigNavCursorVisibleAlways then
visible = true
end
g.NavCursorVisible = visible
end
function ImGui.GetFrameCount() return GImGui.FrameCount end
function ImGui.GetStateStorage() return GImGui.CurrentWindow.DC.StateStorage end
function ImGui.SetStateStorage(tree)
local window = GImGui.CurrentWindow
window.DC.StateStorage = tree or window.StateStorage
end
function ImGui.SetNextFrameWantCaptureKeyboard(want) GImGui.WantCaptureKeyboardNextFrame = want and 1 or 0 end
function ImGui.SetNextFrameWantCaptureMouse(want) GImGui.WantCaptureMouseNextFrame = want and 1 or 0 end
function ImGui.GetVersion() return IMGUI_VERSION end
local StyleColorNames = nil
function ImGui.GetStyleColorName(idx)
if StyleColorNames == nil then
StyleColorNames = {}
for k, v in pairs(ImGuiCol) do if k ~= "COUNT" then StyleColorNames[v] = k end end
end
return StyleColorNames[idx] or "Unknown"
end
-- [SECTION] NAV END FRAME -- [SECTION] NAV END FRAME
local NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15 local NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15
+1 -5
View File
@@ -119,11 +119,7 @@ end
--- Closable header: CollapsingHeader(label, &p_open). Returns visible, open. --- Closable header: CollapsingHeader(label, &p_open). Returns visible, open.
function DemoCollapsingHeaderClosable(label, open) function DemoCollapsingHeaderClosable(label, open)
if not open then return false, open end return ImGui.CollapsingHeader(label, open)
local visible = ImGui.CollapsingHeader(label, open)
-- ponytail: until CollapsingHeader(label, p_open) lands, a boolean 2nd arg is treated as flags=None by the library
if type(visible) == "table" then visible = visible[1] end
return visible, open
end end
--- ImColor::HSV() as ImVec4 --- ImColor::HSV() as ImVec4
+1 -1
View File
@@ -838,7 +838,7 @@ local function DemoWindowLayout_TextClipping()
else else
local clip_rect = ImVec4(p0.x, p0.y, p1.x, p1.y) local clip_rect = ImVec4(p0.x, p0.y, p1.x, p1.y)
draw_list:AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255)) draw_list:AddRectFilled(p0, p1, IM_COL32(90, 90, 120, 255))
draw_list:AddText(ImGui.GetFont(), ImGui.GetFontSize(), text_pos, IM_COL32_WHITE, text_str, nil, 0.0, clip_rect) draw_list:AddText(ImGui.GetFont(), ImGui.GetFontSize(), text_pos, IM_COL32_WHITE, text_str, 1, #text_str + 1, 0.0, clip_rect)
end end
until true end until true end
end end
File diff suppressed because it is too large Load Diff
+11
View File
@@ -3129,6 +3129,12 @@ end
-- TODO: -- TODO:
function MT.ImDrawList:_ResetForNewFrame() function MT.ImDrawList:_ResetForNewFrame()
if self._Splitter._Count > 1 then
self._Splitter:Merge(self)
end
self._Splitter._Current = 0 -- [Lua] recover from an error thrown mid-split
self._Splitter._Count = 1
self.CmdBuffer:resize(0) self.CmdBuffer:resize(0)
self.IdxBuffer:resize(0) self.IdxBuffer:resize(0)
self.VtxBuffer:resize(0) self.VtxBuffer:resize(0)
@@ -4205,6 +4211,11 @@ end
--- @param wrap_width float --- @param wrap_width float
--- @param cpu_fine_clip_rect? ImVec4 --- @param cpu_fine_clip_rect? ImVec4
function MT.ImDrawList:AddText(font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect) function MT.ImDrawList:AddText(font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect)
-- Overload: AddText(pos, col, text, text_end?)
if type(font) == "table" and type(font[1]) == "number" then
font, font_size, pos, col, text, text_begin, text_end, wrap_width, cpu_fine_clip_rect = nil, 0.0, font, font_size, pos, 1, col, 0.0, nil
if text_end == nil then text_end = #text + 1 end
end
if text_begin == nil then text_begin = 1 end if text_begin == nil then text_begin = 1 end
if bit32.band(col, IM_COL32_A_MASK) == 0 then return end if bit32.band(col, IM_COL32_A_MASK) == 0 then return end
+57 -1
View File
@@ -67,6 +67,9 @@ function ImS32(val) return bitAnd(val, 0xFFFFFFFF) end
--- @alias ImDrawIdx unsigned_int --- @alias ImDrawIdx unsigned_int
IMGUI_VERSION = "1.93.0 WIP"
IMGUI_VERSION_NUM = 19298
IM_UNICODE_CODEPOINT_INVALID = 0xFFFD IM_UNICODE_CODEPOINT_INVALID = 0xFFFD
IM_UNICODE_CODEPOINT_MAX = 0xFFFF IM_UNICODE_CODEPOINT_MAX = 0xFFFF
@@ -1980,10 +1983,63 @@ ImGuiInputTextFlags = {
--- @class ImGuiInputTextCallbackData --- @class ImGuiInputTextCallbackData
--- @return ImGuiInputTextCallbackData --- @return ImGuiInputTextCallbackData
-- Buf is a 1-based zero-terminated byte table; positions (CursorPos, pos...) are 0-based byte offsets like upstream.
MT.ImGuiInputTextCallbackData = {}
MT.ImGuiInputTextCallbackData.__index = MT.ImGuiInputTextCallbackData
function ImGuiInputTextCallbackData() function ImGuiInputTextCallbackData()
return {} return setmetatable({}, MT.ImGuiInputTextCallbackData)
end end
function MT.ImGuiInputTextCallbackData:DeleteChars(pos, bytes_count)
IM_ASSERT(pos + bytes_count <= self.BufTextLen)
local buf = self.Buf
local n = self.BufTextLen - bytes_count - pos + 1 -- includes zero terminator
for i = 1, n do buf[pos + i] = buf[pos + bytes_count + i] end
if self.CursorPos >= pos + bytes_count then
self.CursorPos = self.CursorPos - bytes_count
elseif self.CursorPos >= pos then
self.CursorPos = pos
end
self.SelectionStart = self.CursorPos; self.SelectionEnd = self.CursorPos
self.BufDirty = true
self.BufTextLen = self.BufTextLen - bytes_count
end
--- @param new_text string
function MT.ImGuiInputTextCallbackData:InsertChars(pos, new_text)
local new_text_len = #new_text
if new_text_len == 0 then return end
local is_resizable = bit32.band(self.Flags, ImGuiInputTextFlags.CallbackResize) ~= 0
-- We support partial insertion
local avail = self.BufSize - 1 - self.BufTextLen
if not is_resizable and new_text_len > avail then
new_text_len = avail -- ponytail: truncates by bytes, not to the closest UTF-8 codepoint
end
if new_text_len <= 0 then return end
-- ponytail: CallbackResize buffer growth not supported, text is truncated at BufSize instead
if self.BufTextLen + new_text_len + 1 > self.BufSize then
new_text_len = self.BufSize - 1 - self.BufTextLen
if new_text_len <= 0 then return end
end
local buf = self.Buf
for i = self.BufTextLen, pos + 1, -1 do buf[i + new_text_len] = buf[i] end
for i = 1, new_text_len do buf[pos + i] = string.byte(new_text, i) end
buf[self.BufTextLen + new_text_len + 1] = 0
self.BufDirty = true
self.BufTextLen = self.BufTextLen + new_text_len
if self.CursorPos >= pos then
self.CursorPos = self.CursorPos + new_text_len
end
self.SelectionStart = self.CursorPos; self.SelectionEnd = self.CursorPos
end
function MT.ImGuiInputTextCallbackData:SelectAll() self.SelectionStart = 0; self.SelectionEnd = self.BufTextLen end
function MT.ImGuiInputTextCallbackData:ClearSelection() self.SelectionStart = self.BufTextLen; self.SelectionEnd = self.BufTextLen end
function MT.ImGuiInputTextCallbackData:HasSelection() return self.SelectionStart ~= self.SelectionEnd end
--- @alias ImGuiInputTextCallback fun(data: ImGuiInputTextCallbackData) --- @alias ImGuiInputTextCallback fun(data: ImGuiInputTextCallbackData)
--- @alias ImGuiMemAllocFunc fun(T: function, start_idx: int, end_idx: int, userdata: any): table --- @alias ImGuiMemAllocFunc fun(T: function, start_idx: int, end_idx: int, userdata: any): table
+53 -4
View File
@@ -2704,6 +2704,7 @@ end
--- @param format? string --- @param format? string
--- @param flags? ImGuiInputTextFlags --- @param flags? ImGuiInputTextFlags
function ImGui.InputScalarN(label, data_type, data, components, step, step_fast, format, flags) function ImGui.InputScalarN(label, data_type, data, components, step, step_fast, format, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow() local window = ImGui.GetCurrentWindow()
if window.SkipItems then if window.SkipItems then
return false return false
@@ -3052,6 +3053,8 @@ end
--- @param format string --- @param format string
--- @param flags ImGuiSliderFlags --- @param flags ImGuiSliderFlags
function ImGui.DragScalar(label, data_type, data, v_speed, min, max, format, flags) function ImGui.DragScalar(label, data_type, data, v_speed, min, max, format, flags)
if v_speed == nil then v_speed = 1.0 end
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow() local window = ImGui.GetCurrentWindow()
if window.SkipItems then if window.SkipItems then
return data, false return data, false
@@ -3922,6 +3925,7 @@ end
--- @param format? string --- @param format? string
--- @param flags? ImGuiSliderFlags --- @param flags? ImGuiSliderFlags
function ImGui.VSliderScalar(label, size, data_type, data, min, max, format, flags) function ImGui.VSliderScalar(label, size, data_type, data, min, max, format, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow() local window = ImGui.GetCurrentWindow()
if window.SkipItems then if window.SkipItems then
return data, false return data, false
@@ -4776,7 +4780,20 @@ end
--- @param flags ImGuiInputTextFlags --- @param flags ImGuiInputTextFlags
--- @param callback? ImGuiInputTextCallback --- @param callback? ImGuiInputTextCallback
--- @param callback_user_data any --- @param callback_user_data any
local HintBufCache = setmetatable({}, { __mode = "k" })
local function HintToBuf(hint)
local t = HintBufCache[hint]
if t == nil then
t = {}
for i = 1, #hint do t[i] = string.byte(hint, i) end
t[#hint + 1] = 0
HintBufCache[hint] = t
end
return t
end
function ImGui.InputTextEx(label, hint, buf, buf_size, size_arg, flags, callback, callback_user_data) function ImGui.InputTextEx(label, hint, buf, buf_size, size_arg, flags, callback, callback_user_data)
if type(hint) == "string" then hint = HintToBuf(hint) end
local window = ImGui.GetCurrentWindow() local window = ImGui.GetCurrentWindow()
if window.SkipItems then if window.SkipItems then
return false return false
@@ -5823,6 +5840,7 @@ local fmt_table_float = {
--- @param col float[] --- @param col float[]
--- @param flags ImGuiColorEditFlags --- @param flags ImGuiColorEditFlags
function ImGui.ColorEdit4(label, col, flags) function ImGui.ColorEdit4(label, col, flags)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow() local window = ImGui.GetCurrentWindow()
if window.SkipItems then if window.SkipItems then
return false return false
@@ -6074,12 +6092,14 @@ end
--- @param flags? ImGuiColorEditFlags --- @param flags? ImGuiColorEditFlags
function ImGui.ColorEdit3(label, col, flags) function ImGui.ColorEdit3(label, col, flags)
if flags == nil then flags = 0 end if flags == nil then flags = 0 end
if flags == nil then flags = 0 end
return ImGui.ColorEdit4(label, col, bit32.bor(flags, ImGuiColorEditFlags.NoAlpha)) return ImGui.ColorEdit4(label, col, bit32.bor(flags, ImGuiColorEditFlags.NoAlpha))
end end
-- `col` mutated in place, returns value_changed -- `col` mutated in place, returns value_changed
function ImGui.ColorPicker3(label, col, flags) function ImGui.ColorPicker3(label, col, flags)
if flags == nil then flags = 0 end
local col4 = { col[1], col[2], col[3], 1.0 } local col4 = { col[1], col[2], col[3], 1.0 }
if not ImGui.ColorPicker4(label, col4, bit32.bor(flags or 0, ImGuiColorEditFlags.NoAlpha)) then if not ImGui.ColorPicker4(label, col4, bit32.bor(flags or 0, ImGuiColorEditFlags.NoAlpha)) then
return false return false
@@ -6122,6 +6142,7 @@ local backup_initial_col = {0, 0, 0, 0}
--- @param flags ImGuiColorEditFlags --- @param flags ImGuiColorEditFlags
--- @param ref_col? float[] --- @param ref_col? float[]
function ImGui.ColorPicker4(label, col, flags, ref_col) function ImGui.ColorPicker4(label, col, flags, ref_col)
if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow() local window = ImGui.GetCurrentWindow()
if window.SkipItems then if window.SkipItems then
return false return false
@@ -7356,17 +7377,45 @@ function ImGui.SetNextItemStorageID(storage_id)
g.NextItemData.StorageId = storage_id g.NextItemData.StorageId = storage_id
end end
-- Overloads: CollapsingHeader(label, flags?) -> is_open
-- CollapsingHeader(label, p_visible: bool, flags?) -> is_open, p_visible
--- @param label string --- @param label string
--- @param flags? ImGuiTreeNodeFlags function ImGui.CollapsingHeader(label, a, b)
function ImGui.CollapsingHeader(label, flags) local p_visible, flags
if type(a) == "boolean" then p_visible, flags = a, b else flags = a end
if flags == nil then flags = 0 end if flags == nil then flags = 0 end
local window = ImGui.GetCurrentWindow() local window = ImGui.GetCurrentWindow()
if window.SkipItems then if window.SkipItems then
return false return false, p_visible
end end
if p_visible == false then
return false, p_visible
end
local id = window:GetID(label) local id = window:GetID(label)
return ImGui.TreeNodeBehavior(id, bit32.bor(flags, ImGuiTreeNodeFlags.CollapsingHeader), label) flags = bit32.bor(flags, ImGuiTreeNodeFlags.CollapsingHeader)
if p_visible ~= nil then
flags = bit32.bor(flags, ImGuiTreeNodeFlags.AllowOverlap, ImGuiTreeNodeFlags.ClipLabelForTrailingButton)
end
local is_open = ImGui.TreeNodeBehavior(id, flags, label)
if p_visible ~= nil then
-- Create a small overlapping close button
local g = GImGui
local last_item_backup = ImGuiLastItemData()
ImGuiLastItemData_Copy(last_item_backup, g.LastItemData)
local button_size = g.FontSize
local button_x = ImMax(g.LastItemData.Rect.Min.x, g.LastItemData.Rect.Max.x - g.Style.FramePadding.x - button_size)
local button_y = g.LastItemData.Rect.Min.y + g.Style.FramePadding.y
local close_button_id = ImGui.GetIDWithSeed("#CLOSE", nil, id)
if ImGui.CloseButton(close_button_id, ImVec2(button_x, button_y)) then
p_visible = false
end
ImGuiLastItemData_Copy(g.LastItemData, last_item_backup)
end
return is_open, p_visible
end end
---------------------------------------------------------------- ----------------------------------------------------------------
+101
View File
@@ -0,0 +1,101 @@
-- Click fuzzer for the demo: records every visible item rect (by wrapping ImGui.ItemAdd) and clicks/drags/scrolls/types on random ones.
-- Usage: python3 tools/test/run.py --main tools/test/scripts/t_click_fuzz.luau --frames 3000 --quiet
-- Change SEED to explore other paths.
local SEED = tonumber(_G.FUZZ_SEED) or 1
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local G = require(ReplicatedStorage:WaitForChild("ImGui"))
local ImGui, ImVec2 = G.ImGui, G.ImVec2
ImGui.CreateContext()
local io = ImGui.GetIO()
io.ConfigFlags = bit32.bor(io.ConfigFlags, G.ImGuiConfigFlags.NavEnableKeyboard, G.ImGuiConfigFlags.DockingEnable)
G.ImGui_ImplRoblox.Init()
math.randomseed(SEED)
local items, prev_items = {}, {}
local orig_ItemAdd = ImGui.ItemAdd
ImGui.ItemAdd = function(bb, id, nav_bb, extra)
local r = orig_ItemAdd(bb, id, nav_bb, extra)
if r and id ~= 0 then
local g = ImGui.GetCurrentContext()
local w = g.CurrentWindow
local c = w.ClipRect
local x1, y1 = math.max(bb.Min.x, c.Min.x), math.max(bb.Min.y, c.Min.y)
local x2, y2 = math.min(bb.Max.x, c.Max.x), math.min(bb.Max.y, c.Max.y)
if x2 - x1 >= 2 and y2 - y1 >= 2 then
items[#items + 1] = { x1, y1, x2, y2, w.Name }
end
end
return r
end
local log = {}
local function note(s) log[#log + 1] = s; if #log > 12 then table.remove(log, 1) end end
-- action queue: list of {x, y, down, key, char, wheel}
local queue = {}
local mx, my, down = 500, 500, false
local function plan()
local list = prev_items
if #list == 0 then queue = { { 500, 500, false } }; return end
local it = list[math.random(#list)]
local x = it[1] + math.random() * (it[3] - it[1])
local y = it[2] + math.random() * (it[4] - it[2])
local r = math.random()
if r < 0.65 then
note(string.format("click %.0f,%.0f in %s", x, y, it[5]))
queue = { { x, y, false }, { x, y, true }, { x, y, false }, { x, y, false } }
elseif r < 0.8 then
local dx, dy = math.random(-150, 150), math.random(-80, 80)
note(string.format("drag %.0f,%.0f by %d,%d in %s", x, y, dx, dy, it[5]))
queue = { { x, y, false }, { x, y, true } }
for i = 1, 6 do queue[#queue + 1] = { x + dx * i / 6, y + dy * i / 6, true } end
queue[#queue + 1] = { x + dx, y + dy, false }
elseif r < 0.9 then
note(string.format("wheel %.0f,%.0f in %s", x, y, it[5]))
queue = { { x, y, false, wheel = (math.random() < 0.5) and -3 or 3 } }
elseif r < 0.95 then
note(string.format("dblclick %.0f,%.0f in %s", x, y, it[5]))
queue = { { x, y, false }, { x, y, true }, { x, y, false }, { x, y, true }, { x, y, false }, { x, y, false } }
else
local keys = { G.ImGuiKey.Enter, G.ImGuiKey.Escape, G.ImGuiKey.Tab, G.ImGuiKey.UpArrow, G.ImGuiKey.DownArrow, G.ImGuiKey.Backspace }
local k = keys[math.random(#keys)]
note("key " .. tostring(k) .. " + text")
queue = { { mx, my, down, key = k }, { mx, my, down, char = "a1" } }
end
end
local frame = 0
RunService.Stepped:Connect(function()
frame += 1
local ok, err = xpcall(function()
G.ImGui_ImplRoblox.NewFrame()
if #queue == 0 then plan() end
local a = table.remove(queue, 1)
mx, my = a[1], a[2]
io:AddMousePosEvent(mx, my)
if a[3] ~= down then down = a[3]; io:AddMouseButtonEvent(0, down) end
if a.wheel then io:AddMouseWheelEvent(0, a.wheel) end
if a.key then io:AddKeyEvent(a.key, true); io:AddKeyEvent(a.key, false) end
if a.char then for i = 1, #a.char do io:AddInputCharacter(string.byte(a.char, i)) end end
prev_items = items
items = {}
ImGui.NewFrame()
ImGui.DockSpaceOverViewport(0, nil, G.ImGuiDockNodeFlags.PassthruCentralNode)
if frame % 400 == 1 then
ImGui.SetNextWindowCollapsed(false)
ImGui.SetNextWindowPos(ImVec2(650, 20))
ImGui.SetNextWindowSize(ImVec2(700, 1000))
end
ImGui.ShowDemoWindow()
ImGui.Render()
G.ImGui_ImplRoblox.RenderDrawData(ImGui.GetDrawData())
end, debug.traceback)
if not ok then
print("FUZZ FAIL at frame " .. frame .. " seed " .. SEED)
for _, l in ipairs(log) do print(" " .. l) end
error(err, 0)
end
if frame % 500 == 0 then print("frame", frame, "items", #prev_items) end
end)