Skip to content

Plugin drawing ​

Group draw · 35 exports · from forks/gamexueren/scripthook.h (framework 1.0-beta7-wildm1, SH_API_VERSION 2)

A plugin's own window inside the game's overlay, drawn by the plugin's own code.

A plugin registers one callback by name (ShDrawAdd) and is called once per frame, on the render thread, inside an ImGui window titled with that name. Inside the callback it draws with the primitives below - text, panels, buttons, toggles, numbers, sliders, a text box - using the same fonts, colours and resolution-scaled metrics as the framework's own menu, so a plugin's UI looks like it belongs. Nothing registered means nothing drawn and nothing paid: the layer is a single loop over an empty table.

The primitives are implemented by the overlay (ImGui lives there), but they are reached through a plain C ABI and a table of function pointers - ImGui is NOT part of the plugin API, so a plugin is ordinary C, links the import library (or GetProcAddress) and needs no C++ at all. When the framework is built without the overlay - the MinGW chain has no D3D11 or ImGui - every primitive is a safe no-op and ShDrawReady() returns 0, so the same plugin source works in both builds.

The input box deserves its own paragraph, because the split of work is deliberate. The framework owns the CHARACTERS and the IME session: the game's window is the only place a system IME can deliver composed text (it arrives as WM_CHAR / WM_IME_CHAR while the box is open), so the framework collects those, gives them to the owner through ShDrawInputTake(), and owns the composition string, the candidate list, the candidate-window anchoring and the "text is being composed, keep your hands off Enter / Esc / Backspace" test (ShDrawInputComposing). The OWNER owns the buffer: it appends what ShDrawInputTake() hands it, deletes, pastes and decides what Enter means. The framework never edits text and never interprets a command key, so there is exactly one writer per byte, and hotkeys and pulsing keys stay where the game's input model needs them - polled by the owner, not read out of the window's message queue.

The contract, in full:

  • One callback per frame, on the render thread, only while the overlay is up. It runs on the game's Present path, so it must not block: no sleeps, no waiting on another thread, no I/O. Drawing only.
  • Registration and ShDrawInput* may be called from any thread; the callback is the only part that is render-thread bound.
  • A drawer at the end of the frame is not drawn; a drawer registered during one starts on the next.
  • Sixteen drawers maximum, names up to 47 characters. A seventeenth is refused with a log line rather than falling back silently.
  • Window positions and sizes are remembered for the session (the framework deliberately keeps no imgui.ini on disk). Removing the drawer with ShDrawDel() removes the window with it; closing it with its own close box only hides it, and ShDrawShow() brings it back.
  • The layer will not protect the game from a plugin that crashes in its callback - it runs on the render thread. Keep it trivial.
ExportSummary
ShDrawScaleCurrent UI scale, 1.0 = the 1080p baseline.
ShDrawReadyIs the overlay attached and drawing?
ShDrawAddRegister a drawer: name is the window title and the key for ShDrawDel; fn is called once per frame on the render thread.
ShDrawAddExSame, with window options (see ShDrawOpts).
ShDrawDelDrop a drawer and its window.
ShDrawCountHow many drawers are registered (visible or hidden).
ShDrawShowHide or show a drawer's window without unregistering it.
ShDrawShownIs that drawer's window visible?
ShDrawPushFontPush / pop the font.
ShDrawPopFontSee ShDrawPushFont.
ShDrawTextOne line of text at the cursor.
ShDrawTextColoredOne line of text in a chosen colour (0xRRGGBB, alpha 0-255).
ShDrawTextWrappedText wrapped to the window's width.
ShDrawHintA small dim line, the way the menu shows its control hints.
ShDrawSpacingVertical gap, next widget on the same line, horizontal rule.
ShDrawSameLineSee ShDrawSpacing.
ShDrawSeparatorSee ShDrawSpacing.
ShDrawPanelA filled rounded panel / a plain filled rectangle of that size, advancing the cursor past it.
ShDrawRectSee ShDrawPanel.
ShDrawButtonA button.
ShDrawToggleA checkbox bound to *v (any non-zero is on).
ShDrawNumberAn integer field with +/- steps, clamped to [mn, mx].
ShDrawSliderA float slider clamped to [mn, mx].
ShDrawListA drop-down of n NUL-terminated labels; *idx is the selection.
ShDrawInputOpenStart an input session for the box called id: from here the framework collects the characters the game window receives and the IME session belongs to this box.
ShDrawInputCloseEnd the input session (the box is gone, or the game is about to inject the text into its own field).
ShDrawInputIsOpenIs a session live?
ShDrawInputTakeCollect the characters that arrived since the last call, as UTF-8, NUL-terminated, at most cap bytes (leave room for the terminator - 64 is plenty per frame at 60 fps).
ShDrawInputSetModeCandidate window mode: 0 = the overlay draws the candidate list itself (default), 1 = the input method's own window is used and steered under the box.
ShDrawInputGetModeSee ShDrawInputSetMode.
ShDrawInputComposingTrue while an IME composition (pinyin) or its candidate list is live.
ShDrawInputSendingTrue while the owner is injecting the text into the game's own chat field.
ShDrawInputSetSendingTell the framework the owner started / finished injecting.
ShDrawInputWndMsgInternal, used by the overlay's window hook: feed one window message from the subclassed game window.
ShDrawInputBoxDraw the input box: panel, text, IME composition, caret, candidate list and the hint line above it, at the current cursor position, sized to its content.

ShDrawScale ​

c
SH_API float ShDrawScale(void);

Current UI scale, 1.0 = the 1080p baseline. The primitives already scale themselves; this is for a plugin that wants to lay out custom pixel values (a canvas, an image, manual spacing) against the same factor the menu uses. Falls back to 1.0 while no renderer is up.

scripthook.h, line 1556

ShDrawReady ​

c
SH_API int   ShDrawReady(void);

Is the overlay attached and drawing? 0 on a build without the overlay, and for the first frames before ImGui comes up. A plugin that only draws needs no test - the primitives are no-ops - but one that wants to skip work can ask.

scripthook.h, line 1561

ShDrawAdd ​

c
SH_API int ShDrawAdd(const char *name, ShDrawFn fn, void *user);

Register a drawer: name is the window title and the key for ShDrawDel; fn is called once per frame on the render thread. A second call with a name already registered replaces its callback rather than adding a slot. Returns 0 if the name is empty, longer than 47 characters, the callback is NULL, or all sixteen slots are taken - each of those is logged.

scripthook.h, line 1616

ShDrawAddEx ​

c
SH_API int ShDrawAddEx(const char *name, ShDrawFn fn, void *user,
                       const ShDrawOpts *opts);

Same, with window options (see ShDrawOpts).

scripthook.h, line 1618

ShDrawDel ​

c
SH_API int ShDrawDel(const char *name);

Drop a drawer and its window. Returns 0 if there was no such name. Safe to call from inside a drawer's own callback, and safe to call for a name that was never registered.

scripthook.h, line 1623

ShDrawCount ​

c
SH_API int ShDrawCount(void);

How many drawers are registered (visible or hidden).

scripthook.h, line 1625

ShDrawShow ​

c
SH_API int ShDrawShow(const char *name, int on);

Hide or show a drawer's window without unregistering it. A hidden drawer is not called at all. Returns 0 if there was no such name.

scripthook.h, line 1628

ShDrawShown ​

c
SH_API int ShDrawShown(const char *name);

Is that drawer's window visible? 0 for a name that is not registered. A drawer whose own close box the user clicked reads 0 here until ShDrawShow() brings it back.

scripthook.h, line 1632

ShDrawPushFont ​

c
SH_API void ShDrawPushFont(int which);

Push / pop the font. Everything drawn between the two calls uses it; pops must be balanced (the renderer restores the stack at the end of every callback regardless).

scripthook.h, line 1646

ShDrawPopFont ​

c
SH_API void ShDrawPopFont(void);

Documented together with ShDrawPushFont just above.

scripthook.h, line 1647

ShDrawText ​

c
SH_API void ShDrawText(const char *utf8);

One line of text at the cursor.

scripthook.h, line 1650

ShDrawTextColored ​

c
SH_API void ShDrawTextColored(const char *utf8, unsigned rgb, int a);

One line of text in a chosen colour (0xRRGGBB, alpha 0-255).

scripthook.h, line 1652

ShDrawTextWrapped ​

c
SH_API void ShDrawTextWrapped(const char *utf8);

Text wrapped to the window's width.

scripthook.h, line 1654

ShDrawHint ​

c
SH_API void ShDrawHint(const char *utf8);

A small dim line, the way the menu shows its control hints.

scripthook.h, line 1656

ShDrawSpacing ​

c
SH_API void ShDrawSpacing(void);

Vertical gap, next widget on the same line, horizontal rule.

scripthook.h, line 1659

ShDrawSameLine ​

c
SH_API void ShDrawSameLine(void);

Documented together with ShDrawSpacing just above.

scripthook.h, line 1660

ShDrawSeparator ​

c
SH_API void ShDrawSeparator(void);

Documented together with ShDrawSpacing just above.

scripthook.h, line 1661

ShDrawPanel ​

c
SH_API void ShDrawPanel(float w, float h, unsigned rgb, int a);

A filled rounded panel / a plain filled rectangle of that size, advancing the cursor past it. The colour is an 0xRRGGBB + alpha pair like ShDrawTextColored; the panel uses the overlay's own corner rounding.

scripthook.h, line 1667

ShDrawRect ​

c
SH_API void ShDrawRect(float w, float h, unsigned rgb, int a);

Documented together with ShDrawPanel just above.

scripthook.h, line 1668

ShDrawButton ​

c
SH_API int ShDrawButton(const char *label);

A button. Returns 1 on the frame it is clicked.

scripthook.h, line 1671

ShDrawToggle ​

c
SH_API int ShDrawToggle(const char *label, int *v);

A checkbox bound to *v (any non-zero is on). Returns 1 when the user flips it; *v is updated.

scripthook.h, line 1674

ShDrawNumber ​

c
SH_API int ShDrawNumber(const char *label, int *v, int step, int mn, int mx);

An integer field with +/- steps, clamped to [mn, mx]. Returns 1 when it changed.

scripthook.h, line 1677

ShDrawSlider ​

c
SH_API int ShDrawSlider(const char *label, float *v, float mn, float mx);

A float slider clamped to [mn, mx]. Returns 1 when it changed.

scripthook.h, line 1679

ShDrawList ​

c
SH_API int ShDrawList(const char *label, int *idx, const char *const *items,
                      int n);

A drop-down of n NUL-terminated labels; *idx is the selection. Returns 1 when it changed.

scripthook.h, line 1682

ShDrawInputOpen ​

c
SH_API int  ShDrawInputOpen(const char *id);

Start an input session for the box called id: from here the framework collects the characters the game window receives and the IME session belongs to this box. Only one session exists at a time - opening one while another is open moves it. Returns 0 on a bad id.

scripthook.h, line 1689

ShDrawInputClose ​

c
SH_API void ShDrawInputClose(void);

End the input session (the box is gone, or the game is about to inject the text into its own field). Any undrained characters are dropped. Safe to call with no session open.

scripthook.h, line 1693

ShDrawInputIsOpen ​

c
SH_API int  ShDrawInputIsOpen(void);

Is a session live? This is what the window hook and the IME probe key off, so an owner that polls its own hotkeys uses it to notice that something else (the menu, the console) took the keyboard away.

scripthook.h, line 1697

ShDrawInputTake ​

c
SH_API int  ShDrawInputTake(char *out, int cap);

Collect the characters that arrived since the last call, as UTF-8, NUL-terminated, at most cap bytes (leave room for the terminator - 64 is plenty per frame at 60 fps). Returns the byte count. A character that does not fit stays queued: nothing is ever lost, the drain simply takes another frame. Call it from wherever the owner edits its buffer; out may be a stack buffer.

scripthook.h, line 1704

ShDrawInputSetMode ​

c
SH_API void ShDrawInputSetMode(int mode);

Candidate window mode: 0 = the overlay draws the candidate list itself (default), 1 = the input method's own window is used and steered under the box. Mirrors the menu's "Candidate window" row.

scripthook.h, line 1708

ShDrawInputGetMode ​

c
SH_API int  ShDrawInputGetMode(void);

Documented together with ShDrawInputSetMode just above.

scripthook.h, line 1709

ShDrawInputComposing ​

c
SH_API int  ShDrawInputComposing(void);

True while an IME composition (pinyin) or its candidate list is live. The owner must leave Enter / Esc / Backspace to the IME in that state - they edit the composition, not the buffer. Callable from any thread.

scripthook.h, line 1714

ShDrawInputSending ​

c
SH_API int  ShDrawInputSending(void);

True while the owner is injecting the text into the game's own chat field. The window hook keeps swallowing the user's physical keys during that window: a leaked Enter keyup would make the game submit before the injection finished.

scripthook.h, line 1719

ShDrawInputSetSending ​

c
SH_API void ShDrawInputSetSending(int on);

Tell the framework the owner started / finished injecting. While it is set the hook swallows physical keys but lets the injected WM_CHAR characters through - that is the channel into the game.

scripthook.h, line 1723

ShDrawInputWndMsg ​

c
SH_API int  ShDrawInputWndMsg(uint64_t hwnd, uint32_t msg,
                              uint64_t wp, uint64_t lp);

Internal, used by the overlay's window hook: feed one window message from the subclassed game window. Returns 1 when the message was consumed and the game must not see it. Text arrives as WM_CHAR / WM_IME_CHAR and is queued for ShDrawInputTake(); every other keyboard message is swallowed while a box is open, except Tab.

scripthook.h, line 1730

ShDrawInputBox ​

c
SH_API int  ShDrawInputBox(const char *id, const char *text,
                           const char *hint, ShDrawInput *out);

Draw the input box: panel, text, IME composition, caret, candidate list and the hint line above it, at the current cursor position, sized to its content. This is the same widget the framework's own Chinese chat box is made of, so a plugin's box is identical to it.

id must be the name the session was opened with - that is what decides out->focused, and only a focused box takes the IME anchor. text is whatever the owner wants shown (UTF-8, NUL-terminated); hint is a small line drawn above the box and may be NULL or empty. Returns 1 if the box was drawn.

scripthook.h, line 1752

WildM is an independent community project. It is not affiliated with or endorsed by Ubisoft. Ghost Recon Wildlands is a trademark of Ubisoft Entertainment.