Skip to content

C ABI Overview

The ABI is still a candidate and is not frozen. All headers mark this with MIGO_C_ABI_CANDIDATE == 1. A linkable runtime already exists (Android, Linux glibc, Windows, and OpenHarmony), but binary layouts, function signatures, and error-code semantics may still change as freeze blockers are resolved. Production hosts should pin a version and recheck the headers, struct layouts, and ownership rules before every upgrade. See Candidate status for details.

Header Contents Detailed reference
types.h MigoResult and all error codes; forward declarations for MigoEngine/MigoSession/MigoSurfaceAttachment/MigoSurfaceRelease; MIGO_API, MIGO_CALL, and platform-detection macros (such as MIGO_PLATFORM_IS_ANDROID); MIGO_C_ABI_CANDIDATE, MIGO_C_ABI_HAS_RUNTIME types.mdx
session.h MigoEngineConfig, MigoSessionConfig, MigoContentDescriptor, MigoHostCallbacks, MigoLifecycleState; migo_engine_create/destroy, migo_session_create/destroy, migo_session_set_host_callbacks, migo_session_load_content, migo_session_set_lifecycle, migo_session_notify_vsync session.mdx
surface.h MigoSurfaceDescriptor, MigoSurfaceMetrics, MigoSurfaceRelease, MigoSurfaceReleaseStatus; migo_session_attach_surface, migo_surface_update, migo_surface_begin_detach, migo_surface_release_query, migo_surface_release_destroy surface.mdx
input.h Touch (MigoTouchEvent), desktop pointer (MigoPointerEvent), wheel (MigoWheelEvent), soft keyboard (MigoKeyboardEvent), physical keys, IME composition, and gamepad input; the migo_session_send_* family; input backpressure conventions input.mdx
external_frames.h Cross-process frame submission (migo_session_submit_external_frame), frame requests (migo_session_request_external_frame), synchronization barriers, and resource channels external-frames.mdx
capabilities.h MigoCapabilities; migo_query_capabilities, which queries the ABI version range and attachable platform types supported by the linked library; callable before any handle is created capabilities.mdx
platform/*.h Strongly typed surface descriptors for each platform: android.h (ANativeWindow*), win32.h (HWND), winui.h (SwapChainPanel), x11.h (Display* + Window), wayland.h (wl_display* + wl_surface*), openharmony.h (OHNativeWindow*), macos.h, and ios.h surface.mdx
migo.h Umbrella header: includes only types, capabilities, surface, session, and input, with no additional declarations; most hosts can include this file alone engine.mdx

The following is the complete host flow from initialization through shutdown. Every struct must be zeroed first, then struct_size and abi_version must be set, and only then should fields be filled in—unknown reserved fields must remain zero.

/* Derived example: no tests/c_host coverage yet; the header is authoritative */
/* ── 0. optional: query the library's capabilities before creating any handle ── */
MigoCapabilities caps = {0};
caps.struct_size = (uint32_t)sizeof(caps);
caps.abi_version = MIGO_ABI_VERSION_CURRENT;
migo_query_capabilities(&caps);
/* caps.platform_kinds says which MIGO_PLATFORM_* can attach */
/* ── 1. create the engine ── */
MigoEngineConfig eng_cfg = {0};
eng_cfg.struct_size = (uint32_t)sizeof(eng_cfg);
eng_cfg.abi_version = MIGO_ABI_VERSION_CURRENT;
eng_cfg.flags = MIGO_ENGINE_FLAG_ALLOW_UNSIGNED_CONTENT;
eng_cfg.files_dir_utf8 = "/data/user/0/com.example/files";
eng_cfg.cache_dir_utf8 = "/data/user/0/com.example/cache";
eng_cfg.code_cache_dir_utf8 = "/data/user/0/com.example/code_cache";
MigoEngine *engine = NULL;
MigoResult r = migo_engine_create(&eng_cfg, &engine);
/* ── 2. create a session ── */
MigoSessionConfig ses_cfg = {0};
ses_cfg.struct_size = (uint32_t)sizeof(ses_cfg);
ses_cfg.abi_version = MIGO_ABI_VERSION_CURRENT;
MigoSession *session = NULL;
r = migo_session_create(engine, &ses_cfg, &session);
/* ── 3. register host callbacks (before attach and the first RUNNING) ── */
MigoHostCallbacks cbs = {0};
cbs.struct_size = (uint32_t)sizeof(cbs);
cbs.abi_version = MIGO_ABI_VERSION_CURRENT;
cbs.user_data = my_ctx;
cbs.dispatcher_data = my_dispatch_ctx;
cbs.dispatch = my_dispatch_fn; /* accepts a task; thread-safe; returns quickly */
cbs.on_ready = my_on_ready; /* content loaded */
cbs.on_error = my_on_error; /* runtime errors and backpressure notices */
cbs.on_request_frame = my_on_request_frame; /* the engine asks for the next frame; the host drives vsync */
cbs.on_surface_released = my_on_surface_released; /* optional; avoids polling */
r = migo_session_set_host_callbacks(session, &cbs);
/* ── 4. attach a surface ── */
/* fill the platform descriptor (Android here; see platform/android.h) */
MigoAndroidNativeWindowDescriptor plat = {0};
plat.struct_size = (uint32_t)sizeof(plat);
plat.abi_version = MIGO_ABI_VERSION_CURRENT;
plat.platform_kind = MIGO_PLATFORM_ANDROID_NATIVE_WINDOW;
plat.native_window = window; /* ANativeWindow* */
MigoSurfaceDescriptor sd = {0};
sd.struct_size = (uint32_t)sizeof(sd);
sd.abi_version = MIGO_ABI_VERSION_CURRENT;
sd.generation = ++surface_gen; /* strictly increasing, starting at 1 */
sd.platform_kind = MIGO_PLATFORM_ANDROID_NATIVE_WINDOW;
sd.width_pixels = width;
sd.height_pixels = height;
sd.scale_factor = dpr; /* device pixel ratio, for CSS px conversion */
sd.platform_descriptor_size = (uint32_t)sizeof(plat);
sd.platform_descriptor = &plat;
MigoSurfaceAttachment *attachment = NULL;
r = migo_session_attach_surface(session, &sd, &attachment);
/* ── 5. load content (asynchronous; the result arrives via on_ready / on_error) ── */
MigoContentDescriptor cd = {0};
cd.struct_size = (uint32_t)sizeof(cd);
cd.abi_version = MIGO_ABI_VERSION_CURRENT;
cd.content_id_utf8 = "com.example.game";
cd.entry_utf8 = "index.js";
r = migo_session_load_content(session, &cd);
/* ── 6. switch to RUNNING ── */
r = migo_session_set_lifecycle(session, MIGO_LIFECYCLE_RUNNING);
/* ── 7. event loop ── */
/* when the AChoreographer / platform vsync callback fires: */
r = migo_session_notify_vsync(session, frame_time_nanos);
/* touch events (x/y in CSS pixels, not physical pixels): */
r = migo_session_send_touch(session, &touch_event);
/* desktop mouse events: */
r = migo_session_send_pointer_event(session, &ptr_event);
r = migo_session_send_wheel_event(session, &wheel_event);
/* ── 8. exit: pause → detach → wait for the GPU release → destroy ── */
r = migo_session_set_lifecycle(session, MIGO_LIFECYCLE_PAUSED);
MigoSurfaceRelease *release = NULL;
r = migo_surface_begin_detach(attachment, &release);
/* the attachment pointer is invalid from here; the native window may not be destroyed yet */
/* poll, or query once on_surface_released has fired */
MigoSurfaceReleaseStatus status = {0};
status.struct_size = (uint32_t)sizeof(status);
status.abi_version = MIGO_ABI_VERSION_CURRENT;
while (status.state != MIGO_SURFACE_RELEASE_RELEASED) {
migo_surface_release_query(release, &status);
}
/* the GPU has let go; the native window's resources can be destroyed now */
migo_surface_release_destroy(release);
r = migo_session_destroy(session); /* refused while an attachment lives or a release is still PENDING */
r = migo_engine_destroy(engine); /* only after every session is destroyed; joins all worker threads */

Four handle types cross this ABI; their ownership and destruction rules differ. Confusing them is the most common cause of memory-safety problems.

Handle Uniqueness and ownership Concurrency Destruction
MigoEngine* Unique; held by the host All entry points are thread-safe; the host only needs to serialize its own calls migo_engine_destroy, after all child Sessions are destroyed; a successful return is a completion barrier for all Migo worker threads
MigoSession* Unique; held by the host The host serializes calls on one Session; different Sessions may be driven concurrently migo_session_destroy; rejected while an attachment is live, a surface migration is in progress, or any release remains PENDING
MigoSurfaceAttachment* Unique; independent aliases prohibited Serialized with its Session; at most one is active at a time Consumed only by migo_surface_begin_detach; Session destruction does not consume it and will fail
MigoSurfaceRelease* Unique; held by the host May be queried from any host-serialized thread; it holds no Surface resource lease migo_surface_release_destroy, only after MIGO_SURFACE_RELEASE_RELEASED; a RELEASED observer may outlive its Session

Important rules:

  • migo_surface_begin_detach returning MIGO_OK means retirement has started, not that the GPU has finished. The host must keep native window resources and the event loop alive until migo_surface_release_query reports MIGO_SURFACE_RELEASE_RELEASED. Destroying them early is a driver-internal use-after-free that the engine cannot detect or prevent.
  • migo_engine_destroy is the final thread completion barrier. Until it returns, the host must not destroy native display/window resources or unload the Migo library, even if every surface release has reached RELEASED.
  • Session callbacks execute only inside dispatched tasks, hold no engine/Session/attachment locks, and may re-enter detach or destroy.

All functions return MigoResult (int32_t). The following list is ordered by numeric value:

Error code Value Typical trigger
MIGO_OK 0 Call succeeded.
MIGO_ERROR_INVALID_ARGUMENT -1 A NULL pointer; struct_size smaller than the minimum record; an out-of-range field (zero generation, zero dimensions, non-positive or non-finite scale_factor); or a nonzero reserved field.
MIGO_ERROR_UNSUPPORTED_ABI -2 abi_version does not match the current engine build, or the record declared by struct_size is larger than this build knows (the host is newer than the library).
MIGO_ERROR_UNSUPPORTED_PLATFORM -3 platform_kind is defined by the ABI but unsupported by this build (platform_kinds is not set in migo_query_capabilities).
MIGO_ERROR_UNSUPPORTED_CAPABILITY -4 A requested capability bit (such as wide color gamut, transparency, or mailbox presentation) is not implemented in this build; the engine rejects it rather than silently degrading.
MIGO_ERROR_INVALID_STATE -5 A handle was destroyed; the lifecycle state disallows the operation (such as destroying a Session while an attachment is live); or callbacks are being installed a second time.
MIGO_ERROR_WRONG_THREAD -6 Reserved; unused in the current version.
MIGO_ERROR_STALE_SURFACE -7 generation is not greater than the latest value accepted by the Session (at attach), or the handle is not the current active attachment (at update/detach).
MIGO_ERROR_CANCELLED -8 Reserved; unused in the current version.
MIGO_ERROR_DISPATCH_REJECTED -9 The host dispatcher rejected the task; the engine reclaims and drops the task without running or leaking it. The host should return this code when it rejects a task.
MIGO_ERROR_OUT_OF_MEMORY -10 Reserved; unused in the current version.
MIGO_ERROR_INTERNAL -11 An internal engine invariant failed (lock poisoning or an early panic); always logged and not something the caller can infer from or retry.
MIGO_ERROR_WOULD_BLOCK -12 An input event was not accepted because the host command queue was full (non-blocking); the host may retry while the event is still current. The first saturation is reported through MigoOnErrorFn; a later successful input resets the notification.

MIGO_API and MIGO_CALL are defined in types.h and control the visibility and calling convention of all migo_* symbols:

Scenario Define macro MIGO_API expansion MIGO_CALL expansion
Windows: building the migo DLL itself MIGO_BUILD_SHARED __declspec(dllexport) __cdecl
Windows: linking the migo DLL dynamically MIGO_USE_SHARED __declspec(dllimport) __cdecl
Windows: linking a static library Neither defined (empty) __cdecl
GCC / Clang (Linux, Android, and so on) No definition needed __attribute__((visibility("default"))) (empty)
  • Android / OpenHarmony: The SDK provides libmigo_capi.a (a static library), which the host links into its own .so. The platform NDK model is not suited to a third-party shared .so.
  • Linux glibc: The SDK provides both libmigo.so and libmigo.a, with pkg-config and CMake integration.
  • Windows: The SDK provides migo.lib (an import library), with a CMake package.

For every versioned struct, first zero it with = {0}, then explicitly set struct_size and abi_version, and then fill in fields. reserved fields must remain zero.

MigoSurfaceDescriptor sd = {0};
sd.struct_size = (uint32_t)sizeof(sd);
sd.abi_version = MIGO_ABI_VERSION_CURRENT;
/* fill the fields you need */

The library receives a copy; pointers are borrowed during the call and may be released after it returns. Structs written by the library (MigoCapabilities, MigoSurfaceReleaseStatus) follow the mirror rule: no more bytes are written than the caller’s struct_size allows, so an older host calling a newer library cannot be overrun.

#define MIGO_C_ABI_CANDIDATE 1 /* every target: still a candidate */
#define MIGO_C_ABI_HAS_RUNTIME 1 /* Android / Linux glibc / Windows / OpenHarmony: a linkable runtime exists */
#define MIGO_C_ABI_HAS_RUNTIME 0 /* other targets: compiles only, no runtime */

MIGO_C_ABI_HAS_RUNTIME reports the target platform at header compile time, not the actual capability of the linked library. Confirm runtime availability by calling migo_query_capabilities.

Runtime present does not mean the ABI is frozen. Known freeze blockers as of the document generation date include:

  • Complete validation of struct layouts and calling conventions across compilers and architectures (LP64 / LLP64 / ILP32)
  • Android multi-pointer device testing (instrumentation APK still pending)
  • Android compatibility and performance gate tests (Linux complete, Android open)
  • Android third-party consumer NDK package artifacts (mechanism implemented, artifacts awaiting regeneration)

See include/migo/README.md for the complete blocker list.

Recommendations for downstream integration:

  1. Pin the Migo header version in the build system (a git submodule or package-manager lockfile).
  2. Before each upgrade, use the layout assertions in tests/c_abi/ to verify that the new headers are compatible with the host target.
  3. Do not rely on the specific semantics of currently reserved codes such as MIGO_ERROR_WRONG_THREAD, MIGO_ERROR_CANCELLED, and MIGO_ERROR_OUT_OF_MEMORY; no v1 entry point returns them.
Document Covers
Types and macros types.h
Engine and Session migo.h (umbrella)
Session API session.h
Surface lifecycle surface.h, platform/*.h
Input events input.h
External frames external_frames.h
Runtime capability query capabilities.h