Native C API contract: admission, buffers, retention and route families

CNA snapshot c1c316b9  ·  Deep Dives › Reference matrices  ·  source links pinned to c1c316b9

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit c1c316b9; unless a sentence says otherwise, nothing here was built or executed. Read from modules/c-api (headers, sources, tests) and docs/c-api at c1c316b9. The C library was not built and no route or test was executed; the admission snippet was syntax-checked at an earlier snapshot (not repeated at c1c316b9) with gcc -std=c99/-std=c17 -fsyntax-only only. Destroying a caller-created device before its resources and submitting streaming audio from another thread are established by reading, not by any test.

This page is the detailed contract of CNA's experimental native C API (ABI 0.46.0) for people who write a C program or a language binding against it. It collects the rules that decide whether a program works in practice and that the overview pages state only in outline: what each form of version check actually admits, the exact caller-buffer protocol, how callback contexts and registrations live, which thread may make which call, which resources keep other resources alive, how a caller-created graphics device differs from a game's device, and how the 3,210 routes divide into families. Everything was read from the source at snapshot c1c316b9; the C library was not built and no route was executed, so this is the declared and implemented contract at source level, not a verified binary.

ℹ

The overview (build options, packaging, ABI history, coverage figures, bindings) is on the Experimental C API guide; the implementation trace (route anatomy, exception barrier, handle registry, cna_game_destroy) is on C API internals; what the gates and tests do and do not establish is on C API evidence, coverage inventory and release gate. A complete first program is Tutorial 129. This page does not repeat those.

Admitting a library: what each version check accepts

cna_get_abi_version() returns the CNA_ABI_VERSION constant the library was compiled with (CnaCApi.cpp); it needs no runtime, game or device, so it is the first call a program should make. The value is encoded by CNA_ABI_VERSION_ENCODE in abi.h as major<<16 | minor<<8 | patch (0.46.0, the version at this snapshot, is 0x00002E00). What a program does with it is a policy choice, and the common choices differ sharply while the ABI is 0.x, because a minor step below 1.0 may break compatibility: the minors from 0.30.0 to 0.35.0 were all incompatible, four later ones (0.40.0, 0.42.0, 0.43.0 and 0.44.0) changed which calls are accepted without changing a single export, and 0.46.0 declares a different constant set from 0.44.0 (it adds CNA_NATIVE_WINDOW_SYSTEM_UIKIT), so a binding written against older headers must be regenerated or re-reviewed.

CheckWhere it appearsHeaders 0.36.0, library 0.44.0Headers 0.44.0, library 0.43.0What it misses
Exact equality with CNA_ABI_VERSIONthe guide's examplerefusedrefusedNothing it admits can differ, but it also refuses a patch-level rebuild.
Same major, same minor while 0.x, not olderthe syntax-checked snippet belowrefusedrefusedEquivalent to exact equality on the minor; admits a newer patch only.
Same major, then not olderhello_cna.c (the shipped example)admittedrefusedAdmits every newer 0.x minor, including the incompatible ones.
Not older only (cna_get_abi_version() < CNA_ABI_VERSION refuses)a common shortening of the aboveadmittedrefusedAlso admits a future major version; never write it without the major comparison.
find_package(CNA 0.1 CONFIG)the consumer's CMake projectcompares the installed package version (read from that package's abi.h) with SameMajorVersion at configure timeSays nothing about the library that is loaded at run time.
ELF symbol version CNA_C_API_0.1the dynamic linkeridentical for every 0.x release; it is deliberately never bumped for a minorCannot tell any two 0.x libraries apart.

Three separate things decide whether an old program runs against a new library, and a version check covers only the first. The check decides whether the program chooses to run. Linking decides whether every route it calls still exists: 0.30.0 deleted the 858 routes of engine_layer.h, and 0.35.0, which merged two development lines, removed eleven avatar extension routes and the Guide setters cna_guide_set_is_visible and cna_guide_set_is_trial_mode, so a program that references one of them fails when the dynamic linker binds that symbol, whatever its version check said. Behaviour decides the rest: a documented contract change (0.3.0 made every CNA_Bool route refuse bytes other than 0 and 1; 0.9.0 changed seven documented contracts; 0.32.0 made unconfigured gamer-services sign-in and social calls refuse) passes both the admission check of a program that accepts newer minors and the link, and shows up only as different results. The rule-only minors cut the other way: a program built against 0.44.0 headers that borrows the device in an Activated handler links against a 0.43.0 library without complaint, and that call then answers CNA_RESULT_INVALID_STATE. That is why the strict check is the safe default under 0.x, and why the looser one in hello_cna.c, whose comment reasons that "a lower minor lacks routes this program may have been compiled against", is a bet on additive evolution that the version history does not support.

A strict admission check

#include <CNA/C/abi.h>
#include <stdint.h>
#include <stdio.h>

/* Returns 1 when the loaded library may be used by code compiled against these headers. */
static int cna_abi_admits(void)
{
    const uint32_t runtime_abi = cna_get_abi_version();
    const uint32_t runtime_major = runtime_abi >> 16U;
    const uint32_t runtime_minor = (runtime_abi >> 8U) & 0xFFU;

    if (runtime_major != CNA_ABI_VERSION_MAJOR) {
        return 0;                       /* a different major is never compatible */
    }
    if (CNA_ABI_VERSION_MAJOR == 0U && runtime_minor != CNA_ABI_VERSION_MINOR) {
        return 0;                       /* 0.x: any other minor may be incompatible */
    }
    if (runtime_abi < CNA_ABI_VERSION) {
        return 0;                       /* older patch than the headers */
    }
    return 1;
}

int main(void)
{
    if (!cna_abi_admits()) {
        fprintf(stderr, "CNA C ABI 0x%08x does not match the headers (0x%08x)\n",
                (unsigned)cna_get_abi_version(), (unsigned)CNA_ABI_VERSION);
        return 1;
    }
    return 0;
}

The snippet was syntax-checked at an earlier snapshot (not repeated at c1c316b9) with gcc -std=c99 and -std=c17, -Wall -Wextra -Wpedantic -Werror -fsyntax-only, against the public headers of the previous snapshot b0e97bb1; it was not linked or run. abi.h at c1c316b9 differs from the b0e97bb1 header only in its minor number (46 instead of 44). The snippet needs only abi.h, which includes nothing but the C standard headers. When CNA reaches ABI 1.0 (a decision its release gate explicitly does not make) the second condition drops out by itself and the check becomes "same major, not older", which is the rule the 1.x policy is designed for.

The same reasoning gives the short list that a binding or long-lived C program should follow: require one ABI version and say which; branch on capability and renderer-feature queries rather than renderer names or numbers; treat the ownership, thread and child-order rules below as part of the ABI, rule-only minors included; and keep a consumer test on every platform you ship, because CNA's continuous integration builds no C library and exercises none of those rules (see the evidence ladder).

Caller buffers, strings and arrays

Every variable-length output uses a count-then-copy pair and the caller's memory; the ABI never allocates an output on the caller's behalf. The rules are written in STRINGS_AND_BUFFERS.md and the simplest implementation of them is cna_error_copy_last_message in CnaCApi.cpp, which the table follows case by case.

DestinationCapacityResultWritten
anyany, with a null count outputCNA_RESULT_INVALID_ARGUMENTnothing
nullnonzeroCNA_RESULT_INVALID_ARGUMENTnothing
nullzeroCNA_RESULT_BUFFER_TOO_SMALL, or success when the required count is zerothe required count only
validsmaller than requiredCNA_RESULT_BUFFER_TOO_SMALLthe required count only; not one byte of data
validat least the required countCNA_RESULT_SUCCESSthe count and exactly that many bytes or elements

A too-small buffer therefore never receives a prefix: the protocol is all-or-nothing, which is stronger than merely not splitting a UTF-8 character, and the full required count is always reported so a caller can allocate once and repeat. The count is in bytes for text and in elements for typed arrays (for example cna_graphics_renderer_copy_available_ext counts CNA_GraphicsRendererType values), and it never includes a terminator: copied text is raw UTF-8 without a trailing NUL, and a NUL-terminating variant exists only where a declaration documents one. Error-message routes are also exempt from overwriting the thread's last diagnostic, so a program can query and copy it without losing it. The same holds for the canonical-exception queries added in 0.37.0 (core.h): cna_error_copy_last_exception_type_ext gives the .NET name of the exception behind the last failure (System.ArgumentOutOfRangeException, Microsoft.Xna.Framework.GamerServices.GamerPrivilegeException) and cna_error_copy_last_exception_param_name_ext an argument exception's parameter name, so a managed binding can re-raise the exact exception that a result code alone cannot name; a failure the C layer raises itself names none, and both sizes are then zero. cna_packet_reader_copy_data_ext (0.36.0) is a plain instance of the protocol: it copies a packet reader's whole buffer, writes nothing when the destination is too small, and leaves the reader's position where it was.

Inputs are borrowed. A CNA_StringView is {data, byte_length}, valid only for the call, never read past its length and not NUL-terminated by contract; data == NULL is legal only with length zero. Text must be valid UTF-8, and where the value is a path, asset name, window title or identifier an embedded U+0000 is refused with CNA_RESULT_ENCODING, so a consumer cannot rely on C-string truncation. Arrays are a pointer plus a uint64_t count whose unit the route names; null with a zero count is valid, null with a nonzero count is CNA_RESULT_INVALID_ARGUMENT, and the element-size multiplication is overflow-checked before any caller memory is touched. An array is read during the call and not retained unless the declaration says it copies: the PCM creation routes and cna_dynamic_sound_effect_instance_submit_buffer copy their bytes (the latter's header says the caller's buffer "may be reused or freed the moment this returns"), and cna_graphics_renderer_set_fallback_chain_ext copies its identity array into the process-wide selection before returning.

Callback tables, contexts and registrations

A callback is a typed C function pointer plus a caller-owned void* context; CNA never takes ownership of the context memory. The rules in CALLBACKS_AND_THREADING.md and OWNERSHIP.md, which the runtime and family sources follow:

  • Game callbacks. cna_game_create copies the CNA_GameCallbacks table, but the functions it points to and its context must stay valid until cna_game_destroy returns. CNA_GameTime is non-null only in the update and draw callbacks; it is null for load, unload and exit. cna_game_run blocks until the game exits; a host that owns the loop, such as a browser page, calls cna_game_run_frame_ext (0.38.0, the C form of the C++ Game::RunFrameEXT) once per frame instead: the first call initializes the game and begins the run, and the call that finds the game exited ends it with exiting and end_run, which cna_game_run_one_frame, a bare frame step, never delivers.
  • Event registrations are owned handles. Subscribing returns a registration handle; destroying it removes the callback, and the ownership document says the removal synchronizes with any documented in-flight invocation before the call returns. Destroying the event's source removes every registration and invalidates their handles. The caller's context must remain valid until the registration is destroyed or the source has finished destruction. Registrations are the deliberate exception to the game-child counters: they do not block cna_game_destroy.
  • Callback-scoped handles are borrowed. The game handle passed to a lifecycle callback and the device from cna_game_get_graphics_device are valid only for that callback; CNA generation-invalidates the device handle before returning to the loop, so a stored copy later answers CNA_RESULT_INVALID_HANDLE. Two windows widen this without changing it: before the game's run begins, the creating thread may borrow the device with the owned game handle (the device XNA's ApplyChanges creates in a game's constructor), and that copy lasts until the run's first callback returns (0.42.0); and the Activated and Deactivated handlers registered with cna_game_subscribe, like a game component's C handlers, run inside a callback scope and may borrow it too (0.44.0). Between frames, once the run has begun, the route still answers CNA_RESULT_INVALID_STATE. Objects created from that borrowed device (a texture, a SpriteBatch, a content manager) are owned game children and survive the callback.

Threads: the creating-thread rule and its opt-in queue

Every handle is resolved through the registry, which by default refuses any thread other than the one that created the handle with CNA_RESULT_THREAD. A program or binding can opt in to having such calls served instead (0.39.0, runtime.h): cna_game_set_foreign_thread_calls_ext(game, CNA_TRUE), called on the game's thread, turns a call that was refused only because another thread created the handle into a queued call. The caller blocks; the game thread runs the call at the start of its next update or draw, inside the lifecycle callback scope and for up to 8 ms per step while calls keep arriving; and the caller receives the result and, for a failure, the error record the call left. Calls that involve no other thread's handle still run where they are made, routes that drive or destroy the game still refuse, and calls still waiting when the feature is turned off or the game is destroyed fail with CNA_RESULT_INVALID_STATE. A game thread that waits inside a callback for such a thread (XNA's loading screens join a drawing thread from Update) would wait for a call only it can run; it calls cna_game_run_foreign_thread_calls_ext (0.41.0) while it waits, and without that it deadlocks.

Three narrower relaxations need no opt-in. cna_sound_effect_play and cna_sound_effect_play_with_settings answer on any thread, as XNA's SoundEffect.Play did, while every other sound-effect route keeps the creating-thread rule (0.43.0). The twelve cna_graphics_adapter_* routes accept the active game's own handle on its creating thread, so a game can read the default adapter in its constructor before any callback has lent a device (0.40.0; cna_graphics_adapters_refresh still refuses). And the device itself may be borrowed before the run and in the activation handlers, as described above (0.42.0, 0.44.0). Separately, several threads may create and destroy caller-created devices at the same time; the window, renderer and context set-up is serialised inside CNA (graphics_device.h).

On Apple platforms the creating thread must also be the process’s main thread whenever the renderer needs a window, because SDL’s Cocoa and UIKit video drivers initialise only there. A game created on another thread with such a renderer is refused with a message that names the main-thread requirement, while SOFTWARE runs on SDL’s offscreen driver without a window; exiting the process from another thread while the main-thread game is alive remains supported (CALLBACKS_AND_THREADING.md; measured on macOS in CNA’s Apple campaign).

Streaming audio keeps the default rule. The header and AUDIO.md say that copying the submitted buffer makes submission "safe from a producer thread while playback runs", but cna_dynamic_sound_effect_instance_submit_buffer looks its instance up through the same thread-checked registry call as every other route (CnaCApiAudio.cpp, BorrowDynamicInstance), so a call from a producer thread answers CNA_RESULT_THREAD, or, with the foreign-thread queue on, waits for the game thread to run it (by reading; no test submits from a second thread; see Known Issues). A binding or program with a producer or finalizer thread therefore either turns the queue on or hands the work to the creation thread itself.

Resources that keep other resources alive

Several families hold references between C handles, and a destroy route refuses with CNA_RESULT_INVALID_STATE while such a reference is live. These are separate from the game-child counters described on C API internals.

SpriteBatch submissions retain textures until a successful end

The C SpriteBatch draws only through bulk submissions (cna_sprite_batch_submit_many, cna_sprite_batch_submit_scaled_many) and cna_sprite_batch_draw_string. A bulk submission in CnaCApiGraphics.cpp first resolves every command's texture and refuses the whole submission with CNA_RESULT_INVALID_HANDLE if one belongs to a different game than the batch; only then does it retain each texture (incrementing its activeBatchReferenceCount and keeping a shared reference in the batch) and forward the draws; drawing a string retains the font's texture the same way. cna_texture2d_destroy refuses while that count, or the texture's font, effect, model or render-target-scope count, is nonzero. cna_sprite_batch_end calls the native End first and releases the references only after it returns; if End throws, the barrier converts the failure, the batch stays inside its begin/end interval and the textures stay retained. The explicit recovery is cna_sprite_batch_destroy, which, during an active interval, drops the deferred commands, releases every texture reference, disposes the batch and releases its handle. Immediate-mode commands that already reached the renderer cannot be undone. The consequence for a binding: destroying a texture that was drawn in the current interval is a state error, not a use-after-free, and an exception during End leaves a state that only destroying the batch clears.

Loaded content handles outlive the manager's cache

Each typed load (for example cna_content_manager_load_texture2d in CnaCApiContent.cpp) calls ContentManager::Load<T>, copies the returned value into a new object and publishes it as a separate owned handle whose parent is the game. cna_content_manager_unload only calls the native Unload, which in ContentManager.cpp clears the manager's cache of loaded assets; neither it nor cna_content_manager_destroy touches handles already issued to C. A loaded texture therefore remains a valid handle after its manager is unloaded or destroyed and keeps its own obligation to be destroyed before the game; a second load of the same name after an unload goes back to the storage ladder and yields a new handle. The manager a game owns (cna_game_get_content_manager_ext) is borrowed: destroying it is refused with CNA_RESULT_INVALID_STATE, and it is released with the game. Handle validity after Unload follows from the code; no test uses a loaded resource after unloading its manager. The content routes as a family, and the CNB routes beside them, are described on CNB format: the C ABI surface.

Model parts retain their render bundles

An owned skinned-model handle can retain complete same-device bundles of vertex buffer, index buffer, mesh part and optional texture. The caller still owns each original handle, but its typed destroy and the generic graphics-resource disposal refuse while a live model part retains it; replacing a part by name, removing it, replacing the model or destroying the model releases the retained references. Indexed mesh-part queries return a new owned alias, while the optional texture query returns the existing handle, whose lifetime the model protects (OWNERSHIP.md; the texture-side gate is the model reference count checked by cna_texture2d_destroy).

Routes that consume a handle

OWNERSHIP.md gives every handle and pointer parameter one of six categories: owned (release exactly once before runtime shutdown), borrowed (usable only for the call or callback), retained (CNA keeps an extra reference or a copy), transferred (CNA consumes ownership on a successful return), callback context (the caller keeps the void* valid until unregistration completes) and caller buffer (caller storage for the whole call; CNA stores it only where the route says it copies or retains). Transfer is the rare one, and it is the only category that makes a handle you still hold stop existing.

No route at this snapshot consumes a handle the caller passes in: the category is declared, and a binding should still be ready for a route that documents it, but every route that takes another object's handle either borrows it for the call, copies it or retains it, and the caller's handle stays valid and still has to be destroyed. cna_media_queue_add appends a copy of the song, because a C caller cannot give a handle's object away without being left holding a stale handle; cna_effect_material_retain_parameter_texture_ext lets a material keep a texture alive while the caller may still release its own handle. The move routes (cna_skinned_model_ext_create_move, cna_skinned_model_ext_move_assign) leave their source valid but empty, and cna_cnb_model_from_cnj_take_model moves a compiled model out of a compile result into a new owned handle, after which the result may be released at once and a second take answers CNA_RESULT_INVALID_ARGUMENT (cnb.h).

Game devices and caller-created devices

The ABI hands out graphics devices in two ways, declared in graphics_device.h, and they have different lifetime rules.

PropertyA game's deviceA caller-created device
Obtained bycna_game_get_graphics_device, inside a lifecycle callback, or before the run begins on the thread that created the gamecna_graphics_device_create (adapter index, graphics profile, versioned CNA_PresentationParameters); no game needed, and several may exist at once
Handle lifetimeBorrowed; invalidated when the callback returns (one borrowed before the run, when the run's first callback returns)Owned; ends at cna_graphics_device_destroy, which refuses any other kind of device handle
Owner of its resourcesThe game: resources count against cna_game_destroyThe device itself; its resources do not gate cna_game_destroy
Cross-device useRefused in both directions: a resource remembers the device that made it, whether the other device is a game's or caller-created
Destroying the device while resources liveNot possible: the game refuses to be destroyed firstPermitted by the header ("released with it"); see below

cna_graphics_device_destroy (CnaCApiDisplay.cpp) resolves the owned device, calls GraphicsDevice::Dispose() and releases the handle; it checks no child count. By reading, the C++ layer makes that order safe: disposing the device disposes every resource it tracks, and a resource disposed afterwards skips the device once its lifetime token has expired. The resource handles, however, stay registered until the caller destroys each one, and the header does not say whether those destroy calls are still required after the device has gone. No test covers this order: the pure-C smoke tests destroy their resources before the device. Destroying children first is therefore the conservative practice for a caller-created device, not a rule the header imposes; the sequence the header permits is unexercised (see Known Issues). A related asymmetry: cna_graphics_resource_get_graphics_device in CnaCApiGraphicsResource.cpp answers the question through the game-device borrow path, so for a resource made on a caller-created device it does not return that device's handle but fails (see Known Issues).

Choosing and identifying the renderer from C

The selection routes in core_ext.h mirror the C++ selection surface and are listed on Runtime renderer selection. The rules that matter to a C caller:

  • Before the first device. The preferred renderer, the fallback chain and automatic fallback are process-wide and can be set only until a renderer has been created. After that cna_graphics_renderer_get_is_latched_ext reports CNA_TRUE and every setter answers CNA_RESULT_INVALID_STATE. cna_graphics_renderer_set_preferred_ext also refuses an identity that is not compiled into the build when no fallback chain is configured.
  • Selected is not active. cna_graphics_renderer_get_selected_ext reports what will be attempted first (or CNA_RESULT_INVALID_STATE while the CNA_GRAPHICS_RENDERER environment variable names a renderer this build refuses and none was set explicitly); cna_graphics_renderer_get_active_ext reports what was actually created and answers CNA_RESULT_INVALID_STATE until something has been, rather than guessing.
  • Why a renderer was passed over. Each fallback record carries a reason: CNA_GRAPHICS_RENDERER_FALLBACK_NOT_COMPILED_IN (0), CNA_GRAPHICS_RENDERER_FALLBACK_PROBE_UNAVAILABLE (1), CNA_GRAPHICS_RENDERER_FALLBACK_INITIALIZATION_FAILED (2) and CNA_GRAPHICS_RENDERER_FALLBACK_WINDOW_KIND_CONFLICT (3); the diagnostic text is read through its own count/copy pair because the ABI never puts an unbounded string in a fixed structure.
  • Compiled-in versus available. cna_graphics_renderer_get_current_type is a compile-time constant that needs no device. cna_graphics_renderer_copy_available_ext lists the identities compiled into this build (all-or-nothing, as above); an enumerated identity is still not a capability claim.

Identity values are sparse and stable (CNA_GRAPHICS_RENDERER_MAXIMUM is 43 for 14 public identities, and reserved values are never reused), which is explained with the value table on the C API guide.

The 3,210 routes by family

The overview lists the eight largest headers. The table groups all 60 headers of CNA/C by subject; the counts are lines beginning with CNA_C_API at c1c316b9 and sum to the 3,210 exported routes. Four headers declare no route of their own: the umbrella cna.h, named_colors.h (constants), math_values.h and graphics3d.h (types).

FamilyHeaders (routes)TotalWhat it carries
Core, ABI and errorsabi.h (1), core.h (7), core_ext.h (57)65The version query, the thread's last-error and canonical-exception queries, and CNA extensions: logging, platform and operating-system queries, renderer identities, classification and selection.
Game runtimeruntime.h (45), runtime_components.h (39), runtime_graphics_manager.h (38), runtime_window.h (20)142Game creation, callbacks, the blocking and host-driven runs, frame hooks, foreign-thread calls, game components and their events, the service container, GraphicsDeviceManager, the window.
Math and value typesvectors.h (137), geometry.h (94), matrix.h (57), math.h (52), curve.h (45), quaternion.h (28), color.h (24), packed_vectors.h (7)444Value types passed as POD structures with initializer and operation routes: vectors, planes and bounding volumes, matrices, curves, colours, packed vectors.
Graphics device, state and resourcesgraphics_device.h (87), graphics.h (27), vertex_resources.h (24), display.h (22), texture.h (21), texture_volume.h (16), graphics_state.h (14), vertex_values.h (13), graphics_resource.h (12), render_target.h (12), index_resources.h (10), sprite_font.h (9)267Devices, adapters and presentation parameters, textures (2D, 3D, cube), buffers and declarations, render targets, state objects, SpriteBatch and fonts, the generic resource queries.
Effects and modelseffects.h (290), models.h (216)506Stock, skinned, environment-map and PBR effects, custom shader effects, parameters and techniques; models, bones, skinning, animation player, morph targets.
CNAEXT graphics extensionsgraphics_ext.h (44)44The ASCII, CRT and colour-depth post-process effects and DebugDraw, plus the core image-based-light and indirect-draw value initializers; present in every build, with cna_graphics_ext_is_available reporting whether CNA_CNAEXT was enabled and the effect routes answering CNA_RESULT_NOT_SUPPORTED when it is off.
Contentcnb.h (272), content_readers.h (62), content.h (32)366The .cnb container, schemas, cna_cnb_compile_cnj and the loader registry; content managers and typed loads; XNB content readers, reflective readers and the object dictionary.
Audioaudio.h (74), xact.h (62)136PCM sound effects and instances, 3D audio, streaming instances (cna_dynamic_sound_effect_instance_create, a sound-effect-instance handle that also accepts every instance route), index-addressed microphones (cna_microphone_get_count and friends); XACT audio engine, wave and sound banks, categories and cues.
Mediamedia_library.h (148), video.h (42), media_player.h (41), media.h (39)270The media library catalogue and pictures, songs and the media player, video playback (refusing with CNA_RESULT_NOT_SUPPORTED when no video backend is built).
Inputinput_gamepad.h (63), input_touch.h (47), input_devices.h (37), input_haptics.h (36), input_joystick.h (30), input_mouse.h (20), input_keyboard.h (17), input_text.h (17), input.h (15), input_cursor.h (6)288Snapshots and queries for every input device, touch and gestures, text input, haptics, raw joysticks, cursors, host device enumeration.
Devices and sensorssensors.h (144), devices.h (62)206Accelerometer, gyroscope, compass and motion; the device environment, camera, vibration, dialogs, locale, clipboard and power queries.
Servicesgamer_services.h (239), net_sessions.h (104), net.h (51), storage.h (49), net_gamers.h (33)476GamerServices (gamers, achievements, leaderboards, Guide, avatars), network sessions and gamers, storage containers.

All families follow the same handle, result, thread and buffer rules; a specialised route is sometimes narrower than its C++ counterpart. Fifteen declarations are recorded as approved partial mappings: GameServiceContainer::GetService and RemoveService, for instance, are served by cna_game_services_contains_ext and cna_game_services_remove_ext, which name the two services the runtime registers, because a C caller cannot name a C++ type to key the container with. What the ABI deliberately leaves out (the build-time Content Pipeline, the Windows Phone shell module, the platform substrate, renderer implementations, and Diagnostics and the Inspector, which have no routes) and how the inventory classifies it is on the evidence page.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

Maintainer workflow
I need to update the C API
Tests and validation
Test architecture