UiHandle¶
A reference to one native UI window or control. The handle behind every
native-UI event payload and every eq2.ui.* / eq2.quest.* / eq2.loot.* /
eq2.travel.* window lookup.
UiHandle is a real sol2 usertype, not a plain table — the one exception
to the "call members with ." rule everywhere else on this site. Call its
members with ::
local w = eq2.ui.window("Choice")
if w and w:visible() then
w:click()
end
Returned by eq2.ui.window() /
eq2.ui.find(), by eq2.quest.offer_window() /
.reward_window(), by eq2.loot.window(),
by eq2.reply_dialog.window(), and as
the h field on on_ui_action /
on_ui_showhide event payloads.
Handles are opaque and can go stale¶
A UiHandle is a slot in clockwork's own handle table, not a raw pointer —
that table has 4096 slots, shared across the whole client. Every method
below is fail-soft: a stale, wrong-kind, or exhausted handle returns nil /
false rather than erroring, so check the return value, not just whether the
call happened.
Arm event listeners only while you need them
on_ui_action mints a fresh handle per observed event. An always-on
subscription across a long session can exhaust the table, after which
every handle request fails for the rest of the session. Subscribe when
your window opens, unsubscribe when it closes. See
Native UI.
Reading¶
h:visible() / h:enabled()¶
- bool. The control's visible and enabled flags.
h:text()¶
- string or
nil. The Label-class display string, when this control has one.nilon a read failure — distinct from an empty string.
h:name()¶
- string or
nil. The control's own backing-object name (e.g."RewardPack"for the quest offer window) — not the same astext(), which is display text.
h:rows()¶
h:selected()¶
- number. 0-based selected row index for a List-class control.
-1when nothing is selected or the read failed.
h:children()¶
-
array of
{name, handle}, wherehandleis aUiHandlefor each direct child.for _, c in ipairs(w:children()) do eq2.print(c.name) end
Acting¶
h:click()¶
- bool. Fires the control's
OnPressevent. Works for most simple buttons — but seeclick_full()below for the one that doesn't.
h:click_full()¶
-
bool. The unified native click for the game's own
Buttonclass: drives the realOnActivate→OnDeactivate→OnPresssequence through the game's own emitters, rather thanOnPressalone.Prefer this when
click()does nothingSome buttons gate their action on activation state, so a bare
click()is silently inert. The Fast Travel destination pin and the map's "Next" confirm button both needclick_full().
h:button_activate(activate)¶
- bool. Fires
OnActivate(true) orOnDeactivate(false) alone, without the press. Mostly a recon primitive —click_full()is what reproduces a real click.
h:emit(event_name)¶
- bool. Replays a named native event by re-issuing the game's own
broadcaster call with whatever arguments were captured the last time
that event genuinely fired anywhere — no fabricated arguments. Returns
falseif nothing has been captured forevent_nameyet, i.e. it needs "seeding" by a real occurrence first.click_full()is self-contained and needs no seeding, which is why it's preferred when it applies.
h:set_text(s)¶
- bool. Sets a control's display text.
h:select(index)¶
- bool. Selects a row in a List-class control by index.
h:select_row(index)¶
-
bool. Sets a Table SIDL control's
SelectedRowproperty.indexis the 0-based native display-row index; pass-1to clear the selection. This is the Broker/Table row-selection primitive — there is no mouse-hit-test equivalent for a Table row.Warning
Static, not yet live-confirmed against every Table control.
h:check(b)¶
- bool. Sets a checkbox-class control's checked state.
h:scroll(delta) / h:scroll_to(y)¶
-
bool. Adjusts a
ScrollPagecontrol's scroll offset — relative or absolute — and kicks a relayout.selfmust be theScrollPagecontrol itself, not the Table/list it scrolls.Warning
Static, not yet live-confirmed.
h:find(path)¶
UiHandleornil. Recursive child search from this control, through the game's own child-lookup. Seeeq2.ui.find()for the top-level equivalent starting from the UI root.
Struct inspection¶
A guarded raw-read/write surface for confirming struct offsets live, the
UiHandle counterpart to actor. Every
read is bounds-checked and SEH-guarded — a bad offset returns nil/false
instead of crashing the client — but nothing here decodes a field for you; you
are reading raw bytes at an offset you already suspect is meaningful.
Recon primitives, not a stable API
These exist to confirm an offset from Lua instead of attaching a debugger. Don't ship a feature built on a hand-typed offset without confirming it first, and expect offsets to move across game patches.
h:peek_u8(off) / h:peek_u16(off) / h:peek_u32(off)¶
- number or
nil. Unsigned read atcontrol + off.
h:peek_i32(off)¶
- number or
nil. Signed 32-bit read.
h:peek_f32(off)¶
- number or
nil. 32-bit float read.
h:peek_u64_hex(off)¶
- string or
nil. 64-bit read, formatted as"0x...".
h:poke_u8(off, val) / h:poke_u16(off, val) / h:poke_u32(off, val) / h:poke_i32(off, val)¶
- bool. Writes
valatcontrol + off. Pure field write, no native call — nothing re-derives dependent state or triggers a relayout on its own.
h:ptr()¶
- string or
nil. This control's own address, as"0x..."— for diagnostics and log correlation, not for arithmetic in Lua.
h:vtbl(slot)¶
- string or
nil. Absolute address at vtable slotslot(byte offset), as"0x...".
h:vtbl_rva(slot)¶
- number or
nil. Same read asvtbl(), but as anEverQuest2.exe-relative RVA — paste-able straight into Ghidra, and stable across ASLR-affected launches wherevtbl()'s absolute address isn't.nilwhen the slot doesn't resolve to an address inside the game module (e.g. a thunk into a system DLL).
h:peek_rva(off)¶
- number or
nil. Reads the pointer atcontrol + offand reports it as a RVA — thevtbl_rva()companion for a vfptr that isn't at offset0(multiple inheritance can put a second vfptr partway into an object).nilwhen the value isn't a pointer inside the game module, so a data field that merely looks like a pointer readsnilrather than a bogus RVA.
h:adopt_at(off)¶
-
UiHandleornil. Reads the pointer atcontrol + offand adopts the object it points to as a new handle, so everypeek_*/poke_*above then works on it directly.Exists for containers that keep their contents in a private array instead of the generic child list
children()walks — a control can report#children() == 0while still holding real contents behind a raw pointer field.One handle per adopted object
The handle table is keyed by pointer, and adopting an object that is already held as a different handle silently repoints that other handle. Only adopt an address nothing else currently holds a handle to.
h:peek_deref_u32(off, sub_off) / h:peek_deref_f32(off, sub_off) / h:peek_deref_u64_hex(off, sub_off)¶
- Reads at
*(control + off) + sub_off— one pointer hop pastadopt_at's single dereference. The right tool for an array of pointers whose contents you want without minting a handle for each element:adopt_atneeds the target's first 8 bytes to look like a readable vtable to mint, and an array slot that's currently empty (null first bytes) fails that check even though "empty slot" is a legitimate state, not an error.
h:peek_deref_rva(off, sub_off)¶
- number or
nil. Same one-hop read as above, reported as an RVA — same module-membership guard aspeek_rva().
h:adopt_deref(off, sub_off)¶
UiHandleornil. Adopts the element pointer found via the same one-hop readpeek_deref_*uses, rather than reading a value out of it. The companion topeek_deref_*for "give me a handle to the thing this pointer-to-a-pointer refers to," and — unlikeadopt_aton the same field — correctly returnsnilon a legitimately empty array slot instead of failing to mint.
See also¶
- Native UI —
eq2.ui.windows()/.window()/.find(), and theon_ui_action/on_ui_showhideevents that hand you aUiHandle. actorstruct inspection — the same guarded raw-read pattern, applied to actors instead of controls.