name: gum-service description: GumService — runtime entry point for MonoGame/Raylib/KNI/FNA. Triggers: GumService init, Uninitialize, DeferredQueue, hot reload, Root/PopupRoot/ModalRoot containers.
GumService Reference
What It Is
GumService is the runtime-facing API that game developers use to initialize, update, and draw Gum UI. It lives in MonoGameGum/GumService.cs (compiled for XNALIKE, RAYLIB via #if) under the Gum namespace (since issue #3119 / syntax version 3). Legacy MonoGameGum.GumService / RaylibGum.GumService names are permanent [Obsolete] subclass shims in MonoGameGum/GumServiceCompat.cs (linked into RaylibGum.csproj). WindowZoomMode, GumHotReloadManager, and related hot-reload types also live in namespace Gum.
The soft migration is intentional and finished — do not flag it as incomplete. What matters is what the user types: new code uses using Gum; and types the service as GumService (= Gum.GumService) with no using MonoGameGum; / using RaylibGum; — that is 100% future-proof. Gum.GumService.Default is deliberately typed as the derived [Obsolete] shim so legacy using MonoGameGum; / using RaylibGum; declarations keep compiling; the user never names the obsolete type, so the modern path is warning-free. Do not propose "completing" this by retyping Default to Gum.GumService or removing the shims — that would break back-compat and is not planned (stable for the foreseeable future). The fact that Default's declared type is an obsolete shim is not a problem and must not be raised as a follow-up.
Not the CLI. Gum.Cli / Gum.ProjectServices are separate tools for headless project validation and codegen.
Lifecycle
GumService.Default.Initialize(game) // one-time setup
↓ per frame:
GumService.Default.Update(gameTime)
GumService.Default.Draw()
↓ on teardown (optional):
GumService.Default.Uninitialize()
Initialize throws if called twice without an intervening Uninitialize.
Only the gumProjectFile overload loads a project. Initialize(game, gumProjectFile) loads the .gumx and runs the project-load path — which applies project-level settings (standard-element defaults, localization, and the project's TextureFilter → Renderer.TextureFilter, issue #3199). The Initialize(game, DefaultVisualsVersion) / parameterless overloads are code-only and never touch that path. So when manually verifying anything that depends on a loaded project, you must pass the .gumx path or the behavior under test never runs. The cheapest from-file smoke test is Initialize(game, "GumProject/GumProject.gumx") plus a SpriteRuntime (SourceFileName set, scaled up) added to Root.
Singleton Pattern
Default is lazily initialized via ??=. Uninitialize() sets _default = null, so after teardown GumService.Default creates a fresh instance — any stored reference to the old instance is now orphaned.
Uninitialize — Non-Obvious Details
Uninitialize() resets a large amount of shared static state across multiple singletons. Key things it does that are surprising:
- Nulls
GraphicalUiElement.SetPropertyOnRenderable,AddRenderableToManagers, etc. — delegates wired byFormsUtilities.InitializeDefaults - Calls
ElementSaveExtensions.ClearRegistrations()— clearsRegisterGueInstantiation/RegisterDefaultInstantiationTypecallbacks - Calls
LoaderManager.Self.DisposeAndClear()— disposes GPU content and empties the cache - Nulls and removes
FrameworkElement.PopupRootandFrameworkElement.ModalRootfrom managers - Resets
FileManager.RelativeDirectoryto"Content/"(only meaningful if a project was loaded) - Calls
_systemManagers.Renderer.Uninitialize()(XNALIKE only) - Sets
_default = null— next access toDefaultcreates a newGumService
FormsUtilities.Uninitialize() is internal; tests access it via InternalsVisibleTo.
Roots
| Property | Purpose |
|---|---|
Root |
Main scene container; sized to canvas on each Update |
PopupRoot |
Overlaid above Root; for non-modal popups |
ModalRoot |
Topmost layer; blocks input to everything below |
PopupRoot and ModalRoot are FrameworkElement statics, not instance fields — they are shared across all GumService instances.
Key Files
| File | Purpose |
|---|---|
MonoGameGum/GumService.cs |
Main class (namespace Gum) |
MonoGameGum/GumServiceCompat.cs |
[Obsolete] subclass shims for MonoGameGum.GumService / RaylibGum.GumService; also holds the AddChild codegen crutch and ToGraphicalUiElement forwarder |
MonoGameGum/Forms/FormsUtilities.cs |
Input/cursor/gamepad setup; Uninitialize() lives here |
GumRuntime/ElementSaveExtensions.GumRuntime.cs |
ClearRegistrations() called during Uninitialize |
RenderingLibrary/Content/LoaderManager.cs |
DisposeAndClear() called during Uninitialize |
Tests/MonoGameGum.Tests.V2/GumServiceUninitializeTests.cs |
Tests for GPU-accessible Uninitialize behavior |
Tests/Gum.ProjectServices.Tests/UninitializeTests.cs |
Tests for non-GPU Uninitialize behavior |
Testing Split
Uninitialize tests are split across two projects because FormsUtilities, LoaderManager, and ElementSaveExtensions don't all require a GPU:
Gum.ProjectServices.Tests—LoaderManager,ElementSaveExtensions,ObjectFinder(no GPU needed)MonoGameGum.Tests.V2—FormsUtilities, root containers,FrameworkElementstatics (require test setup with a mockSystemManagers)
Hot Reload
GumService.EnableHotReload(absoluteGumxSourcePath) wires up a GumHotReloadManager that watches the source project directory and rebuilds Root.Children when .gumx/.gusx/.gucx/.gutx/.fnt files change. GumService.Update ticks it each frame; Uninitialize stops and nulls it. For details on the reload pipeline, debounce, font cache eviction, and gotchas, load the gum-runtime-hot-reload skill. Public docs: docs/code/hot-reload.md.