Complete reference for all public functions, types, and macros in apostrophe.h and apostrophe_widgets.h.
This reference documents Apostrophe v1.1.0 (2026-03-30).
| Macro | Value | Description |
|---|---|---|
AP_OK |
0 |
Success return code |
AP_ERROR |
-1 |
Error return code |
AP_CANCELLED |
-2 |
User cancelled (pressed back) |
AP_REFERENCE_WIDTH |
1024 |
Reference width for scaling |
AP_SCALE_DAMPING |
0.75f |
Damping for screens wider than reference |
AP_DS(base) |
— | Scale a pixel value by integer device_scale (2 or 3) |
AP_S(base) |
— | Scale a pixel value from reference to actual screen |
AP_PLATFORM_NAME |
"tg5040" etc. |
Compile-time platform identifier |
AP_PLATFORM_IS_DEVICE |
0 or 1 |
Whether building for a real device |
AP_INPUT_DEBOUNCE |
20 |
Input debounce delay (ms) |
AP_INPUT_REPEAT_DELAY |
300 |
Initial hold delay (ms) |
AP_INPUT_REPEAT_RATE |
100 |
Repeat rate (ms) |
AP_AXIS_DEADZONE |
16000 / 20000 on my355 |
Joystick axis dead zone |
AP_TEXT_SCROLL_SPEED |
1 |
Text scroll speed (pixels per tick) |
AP_TEXT_SCROLL_PAUSE_MS |
1000 |
Pause at scroll endpoints (ms) |
AP_TEXTURE_CACHE_SIZE |
8 |
LRU texture cache capacity |
AP_MAX_COMBOS |
16 |
Max registered button combos |
AP_MAX_LOG_LEN |
2048 |
Max log message length |
Virtual button identifiers, unified from all input sources.
AP_BTN_NONE, AP_BTN_UP, AP_BTN_DOWN, AP_BTN_LEFT, AP_BTN_RIGHT,
AP_BTN_A, AP_BTN_B, AP_BTN_X, AP_BTN_Y,
AP_BTN_L1, AP_BTN_L2, AP_BTN_R1, AP_BTN_R2,
AP_BTN_START, AP_BTN_SELECT, AP_BTN_MENU, AP_BTN_POWERAP_FONT_EXTRA_LARGE // 24 × device_scale (title/header)
AP_FONT_LARGE // 16 × device_scale (list items — NextUI FONT_LARGE)
AP_FONT_MEDIUM // 14 × device_scale (single-char button label — NextUI FONT_MEDIUM)
AP_FONT_SMALL // 12 × device_scale (hint text — NextUI FONT_SMALL)
AP_FONT_TINY // 10 × device_scale (multi-char button label — NextUI FONT_TINY)
AP_FONT_MICRO // 7 × device_scale (overlay text — NextUI FONT_MICRO)Font sizes use integer device_scale (2 for MY355/TG5050/TG5040 handheld, 3 for TG5040 brick), matching NextUI's SCALE1(FONT_*) exactly. On screens with more logical pixels than the 320×240 baseline (MY355), an automatic font bump of 0–5 is added to each base size before scaling. See ap_get_font_bump().
AP_ALIGN_LEFT, AP_ALIGN_CENTER, AP_ALIGN_RIGHTAP_ACTION_SELECTED // User pressed confirm
AP_ACTION_BACK // User pressed back
AP_ACTION_TRIGGERED
AP_ACTION_SECONDARY_TRIGGERED
AP_ACTION_CONFIRMED
AP_ACTION_TERTIARY_TRIGGERED
AP_ACTION_OPTION_CHANGED // Options list standard value changed
AP_ACTION_CUSTOM // Alias of AP_ACTION_TRIGGERED (backward compatibility)Alias for SDL_Color (r, g, b, a).
typedef struct {
ap_color highlight; // Selected item pill background
ap_color accent; // Footer outer pill, status bar bg
ap_color button_label; // Text inside footer button pills
ap_color text; // Default text color
ap_color highlighted_text; // Text on selected items
ap_color hint; // Dim/help text
ap_color background; // Screen background
char font_path[512];
char bg_image_path[512];
} ap_theme;typedef struct {
const char *window_title; // Window title (macOS only)
const char *font_path; // Path to .ttf, NULL = auto
const char *bg_image_path; // Background image, NULL = none
const char *log_path; // Log file, NULL = stderr only
const char *primary_color_hex; // Override accent "#RRGGBB"
bool disable_background; // Set true to skip bg.png
bool is_nextui; // Load theme from nextval.elf
ap_cpu_speed cpu_speed; // Set CPU at init; 0 = AP_CPU_SPEED_DEFAULT (no-op)
bool disable_font_bump; // Set true to disable automatic font bumping
} ap_config;typedef struct {
ap_button button;
bool pressed; // true = down, false = up
bool repeated; // true = generated by hold-repeat, false = fresh press
} ap_input_event;typedef struct {
ap_button button;
const char *label;
bool is_confirm; // true = right-aligned group
const char *button_text; // optional display-only override for the button pill text
} ap_footer_item;button_text only affects what is drawn in the footer. It does not change input handling, which still comes from your widget logic and button bindings.
typedef struct {
bool enabled; // true = collapse overflowing footer hints into +N
ap_button chord_a; // First button in the hidden-actions chord
ap_button chord_b; // Second button in the hidden-actions chord
} ap_footer_overflow_opts;// Clock display mode constants
#define AP_CLOCK_AUTO 0 // Follow NextUI showclock setting (default)
#define AP_CLOCK_SHOW 1 // Always show, regardless of device settings
#define AP_CLOCK_HIDE 2 // Always hide, regardless of device settings
typedef struct {
int show_clock; // AP_CLOCK_AUTO (default), AP_CLOCK_SHOW, or AP_CLOCK_HIDE
bool use_24h; // Only used when show_clock == AP_CLOCK_SHOW
bool show_battery; // Show battery icon from device or desktop preview state
bool show_wifi; // Show wifi icon when connected / previewed
} ap_status_bar_opts;Clock behaviour: By default (show_clock left at 0 / AP_CLOCK_AUTO), the clock
visibility and format are read from the NextUI device settings (showclock and clock24h
in minuisettings.txt). On desktop builds, auto mode follows the preview settings file
from AP_MINUI_SETTINGS_PATH when provided; otherwise auto mode hides the clock. Use
AP_CLOCK_SHOW to force the clock visible regardless of settings, or AP_CLOCK_HIDE to
always suppress it. When using AP_CLOCK_SHOW, the use_24h field controls the time
format; in auto mode, clock24h from the settings file is used instead.
Wifi behaviour: When show_wifi is true, the wifi icon is only shown when the current
device or desktop preview signal strength is greater than 0. When disconnected, the icon is
hidden and the pill shrinks accordingly. This matches NextUI's behaviour of hiding the wifi
icon when not connected.
Battery and wifi can be enabled independently. When status sprites are active, the clock is hidden, and exactly one of those icons is visible, Apostrophe renders a centered square pill for that single icon so width calculation and drawing stay aligned.
On device builds, battery and wifi icons are rendered from the NextUI asset spritesheet
($SDCARD_PATH/.system/res/assets@Nx.png, defaulting to /mnt/SDCARD/.system/res/assets@Nx.png).
On desktop builds, the same sprite path is used when AP_STATUS_ASSETS_DIR points at a folder
containing assets@1x.png through assets@4x.png. Pills (status bar, footer) then use the
same pre-rendered sprites for smooth anti-aliased edges on both device and desktop. Without
status assets, desktop falls back to procedural pill drawing and text placeholders. Apostrophe
does not bundle the upstream NextUI sprite sheets; these files must come from your own local
preview cache or device/source checkout.
Desktop preview environment variables:
AP_STATUS_ASSETS_DIR: directory containingassets@Nx.pngAP_NEXTVAL_PATH: JSON file used for NextUI theme colors on desktopAP_MINUI_SETTINGS_PATH: mockminuisettings.txtused byAP_CLOCK_AUTOandbatterypercAP_PREVIEW_WIFI_STRENGTH: preview wifi strength (0..3)AP_PREVIEW_BATTERY_PERCENT: preview battery level (0..100)AP_PREVIEW_CHARGING: preview charging state (0or1)
Initialise Apostrophe. Creates SDL window/renderer, loads fonts, detects screen size, loads theme (if is_nextui), sets up input, starts power button handler (on device).
On my355 device builds, raw power handling listens for KEY_POWER from Linux input devices. A short press triggers suspend, and a long press (>= 1000ms) triggers shutdown orchestration (/tmp/poweroff). Suspend first attempts echo mem > /sys/power/state and falls back to echo freeze > /sys/power/state if needed. After resume, power-key events are ignored for 1000ms to avoid immediate re-suspend from wake events (matching NextUI behavior).
Returns AP_OK on success, AP_ERROR on failure.
Shut down completely. Frees all resources, destroys SDL context, stops background threads.
Get the current screen dimensions in pixels.
Get the current scaling factor (screen_width / reference_width, with damping).
Scale a pixel value from 1024-reference space to actual screen space. Equivalent to the AP_S() macro as a function call.
Scale a base font size by device_scale (integer multiplier: 2 for MY355/TG5050/TG5040 handheld, 3 for TG5040 brick). Returns base_size * device_scale.
Macro. Scales an integer pixel value from reference (1024px) to actual screen:
int margin = AP_S(20); // 20px * scale factorGet a pointer to the current theme. Modifiable.
Load theme colors from the NextUI configuration, including the fallback background color. Accepts both the current color7 background key and the legacy bgcolor key for backward compatibility. Returns AP_OK on success, AP_ERROR on failure. Called automatically during ap_init() when ap_config.is_nextui is true.
Parse a #RRGGBB hex string and return the corresponding ap_color (with alpha 255). Returns black {0,0,0,255} on invalid input.
Parse a #RRGGBB string and apply it as the theme accent color: ap_set_theme_color("#FF6600");
Reload the background image at runtime. Destroys the current background texture and loads a new one from bg_path. If bg_path is NULL or empty, falls back to /mnt/SDCARD/bg.png on device or the AP_BACKGROUND_PATH environment variable on desktop. Returns AP_OK on success (or if no fallback path is available), AP_ERROR if the image cannot be loaded.
Get a pre-loaded, pre-scaled font for the given tier. Returns NULL if not loaded.
Get the automatic font bump computed at init (0–5). The bump is added to each base font size before device_scale multiplication. Computed from the logical resolution (screen / device_scale) relative to the 320×240 baseline (MY355). Returns 0 on MY355 and TG5040 brick, typically 2 on TG5050. Set ap_config.disable_font_bump = true to force 0.
Poll for the next input event. Returns true if an event was available.
Handles SDL event processing internally: keyboard events, raw joystick buttons/axes/hats (TrimUI), SDL GameController buttons/axes (macOS + recognised gamepads), platform-specific scancodes (my355), and quit events.
Swap A/B and X/Y button mappings. When flip is true, hardware A reports as AP_BTN_B and vice versa (likewise X/Y). Useful for platforms where the firmware already swaps face buttons.
Get the display name string for a virtual button (e.g. "A", "UP", "START"). Returns "Unknown" for out-of-range values.
Set input debounce delay in milliseconds.
Configure directional hold repeat timing for D-pad/arrow/button-mapped directions.
Clear the screen to the theme background color (or render bg image if configured).
Present the rendered frame. Call after all drawing for the frame. When the renderer does not have vsync (e.g. software fallback), this function automatically throttles to ~60 fps via SDL_Delay to prevent CPU hot-spinning.
Request another frame immediately. Use this for active animations or callbacks that need the next frame to render without waiting for input.
Schedule a redraw in the future while the UI is otherwise idle. This is useful for widgets that need periodic refresh without adding their own polling loop. For example, an ap_list() footer callback can call ap_request_frame_in(100) to keep a live footer label in sync with external state.
Draw the background image/color (called automatically by ap_clear_screen).
Render text. Returns the rendered width in pixels.
Render text clipped to a maximum width. Performs a hard pixel clip with no truncation indicator.
int ap_draw_text_ellipsized(TTF_Font *font, const char *text, int x, int y, ap_color color, int max_w)
Render text truncated with "..." if it exceeds max_w. If the text fits, it is rendered normally. Uses a binary search to find the longest prefix that fits alongside the ellipsis, respecting UTF-8 character boundaries. Returns the rendered width in pixels.
void ap_draw_text_wrapped(TTF_Font *font, const char *text, int x, int y, int max_w, ap_color color, ap_text_align align)
Render multi-line word-wrapped text.
Measure text width without rendering.
Measure the width the text would occupy if ellipsized to fit max_w, without rendering. Returns the full text width when the text already fits. Returns 0 when max_w <= 0. If the ellipsis itself is as wide as or wider than max_w, returns max_w to match the clipped fallback behavior used by ap_draw_text_ellipsized().
Measure the total height in pixels that word-wrapped text would occupy at the given max_w constraint, without rendering. Useful for pre-calculating layout sizes.
Draw a filled rectangle.
Draw a pill shape (fully rounded rectangle where corner radius = h/2). Uses the pre-rendered NextUI pill sprite when status assets are loaded; otherwise falls back to procedural drawing.
Draw a filled rounded rectangle with arbitrary corner radius using scanline quarter-circle fill with sub-pixel anti-aliasing (no SDL2_gfx dependency).
Draw a filled circle at center (cx, cy) with radius r.
Draw a loaded SDL texture at the given position/size.
Load an image from disk (PNG, JPG) and return an SDL_Texture. Returns NULL on failure.
Draw a vertical scrollbar track and thumb. The thumb size and position are computed from visible/total/offset. Does nothing if total <= visible.
Draw a rounded progress bar. progress is clamped to 0.0–1.0.
Calculate the usable content area of the screen, accounting for title bar, footer, and status bar. Returns an SDL_Rect with x, y, w, h in pixels. Use this to position widget content within the available space.
Draw a title at the top-left of the screen. If status_bar is non-NULL, the status bar is also drawn at the top-right and the title is clipped to avoid overlapping it.
Draw a title centered horizontally in the available space. Behaves like ap_draw_screen_title but centers the text instead of left-aligning it. Uses the same progressive font-tier fallback (extra-large → large → medium) and clips to avoid overlapping the status bar.
Fade-in/fade-out overlay for scene transitions.
typedef struct {
uint32_t start_ms; // SDL_GetTicks() value when fade began
int duration_ms; // Total duration of the fade
bool fade_in; // true = black->transparent, false = transparent->black
bool active; // false = not animating
} ap_fade;Start a fade-in: the screen transitions from fully black to transparent over duration_ms milliseconds.
Start a fade-out: the screen transitions from transparent to fully black over duration_ms milliseconds.
Draw the fade overlay. Call after drawing your scene and before ap_present(). Returns true while the fade is still animating, false when complete.
Draw the footer bar at the bottom of the screen with button hints.
Non-confirm items render in one continuous outer pill on the left; confirm items render in one continuous outer pill on the right. Inside each outer pill, every item shows an inner button pill (letter/symbol) followed by a text label. Sizing matches NextUI: PILL_SIZE (30) outer height, BUTTON_SIZE (20) inner circles, all scaled by device_scale. Font tiers: AP_FONT_MEDIUM (14 base) for single-char button labels, AP_FONT_TINY (10 base) for multi-char labels, AP_FONT_SMALL (12 base) for hint text.
When footer overflow handling is enabled and the hints do not fit on one row, Apostrophe keeps the footer on a single line, preserves the right-aligned confirm group, shows a compact +N marker for hidden hints, and exposes a hidden-actions overlay that can be opened by calling ap_show_footer_overflow() (commonly bound to the Menu button) or by configuring a chord. Hidden footer items remain normal actions; only their on-screen hints are collapsed.
Get the footer height in pixels (scaled).
Set the global footer overflow behaviour. Pass NULL to restore the default configuration (enabled = true, chord_a = AP_BTN_NONE, chord_b = AP_BTN_NONE). To enable a button chord shortcut, set chord_a and chord_b to the desired buttons (e.g. AP_BTN_L1 and AP_BTN_R1).
Read the current global footer overflow configuration.
Programmatically open the hidden-actions overlay when hidden footer items exist. This is useful in screens with custom input loops (i.e. not using ap_list) that need to support the Menu button for footer overflow. The call is a no-op when there are no hidden items. Note: hidden item state is computed by ap_draw_footer(), so this function must be called after at least one footer draw pass in the current frame.
Draw a status bar pill at the top-right of the screen. Shows clock, battery, and wifi status. Battery and wifi icons come from the NextUI asset spritesheet whenever status assets are loaded. Position matches NextUI's PADDING offset (10px unscaled). When sprites are active and only one battery/wifi icon is visible with the clock hidden, the pill collapses to a centered square icon pill.
Calculate the pixel width of the status bar pill, including padding. Use this to clip long title text to avoid overlap. The result matches ap_draw_status_bar() for all clock/battery/wifi combinations, including square single-icon sprite pills.
Get the height of the status bar in pixels (scaled).
Initialise a text scroll state. Resets offset, direction, and pause timer.
Reset a text scroll state to the beginning.
Advance the ping-pong scroll animation. Call once per frame, passing the frame delta time in milliseconds.
Look up a texture in the LRU cache by key string. Returns NULL on miss.
Store a texture in the LRU cache. Evicts least-recently-used entries when full.
Flush the entire texture cache and free all textures.
The combo system detects two kinds of multi-button input:
- Chords — multiple buttons pressed simultaneously (e.g. L1+R1 together)
- Sequences — buttons pressed in a specific order (e.g. Up, Up, Down, Down)
Combos do not suppress individual button events — ap_poll_input() still returns every press and release as normal.
typedef enum { AP_COMBO_CHORD, AP_COMBO_SEQUENCE } ap_combo_type;Distinguishes the kind of combo in a ap_combo_event. Set to AP_COMBO_CHORD for chords and AP_COMBO_SEQUENCE for sequences.
typedef void (*ap_combo_callback)(const char *id, ap_combo_type type, void *userdata);Callback signature used by the _ex registration variants. Called synchronously at the moment the combo triggers or a chord releases.
typedef struct {
const char *id; /* identifier passed to ap_register_chord/sequence */
bool triggered; /* true = fired, false = chord released */
ap_combo_type type; /* AP_COMBO_CHORD or AP_COMBO_SEQUENCE */
} ap_combo_event;Register a simultaneous button chord. All count buttons must be held at the same time, with
the time between the earliest and latest press no greater than window_ms (default 100ms if 0).
Returns AP_ERROR when id is NULL/empty, buttons is NULL, or count is outside 1..8.
When the chord triggers, a combo event is queued with triggered = true. When any button in the
chord is released, a second event is queued with triggered = false. A chord will not re-trigger
until all buttons have been released and pressed again.
int ap_register_chord_ex(const char *id, ap_button *buttons, int count, uint32_t window_ms, ap_combo_callback on_trigger, ap_combo_callback on_release, void *userdata)
Like ap_register_chord, but also registers optional callbacks. on_trigger fires when the chord fires; on_release fires when any button is released. Either may be NULL. Callbacks fire synchronously before the event is enqueued, so polling still works alongside them — both are additive.
int ap_register_sequence(const char *id, ap_button *buttons, int count, uint32_t timeout_ms, bool strict)
Register an ordered button sequence. Each button must be pressed within timeout_ms of the
previous one (default 500ms if 0). When strict is true, any extraneous button press during
the sequence window causes the match to fail.
Returns AP_ERROR when id is NULL/empty, buttons is NULL, or count is outside 1..8.
Sequences only fire triggered = true events (no release event).
int ap_register_sequence_ex(const char *id, ap_button *buttons, int count, uint32_t timeout_ms, bool strict, ap_combo_callback on_trigger, void *userdata)
Like ap_register_sequence, but also registers an optional on_trigger callback. There is no on_release parameter for sequences — they are one-shot events with no release phase.
Deactivate a previously registered combo by its id. The combo slot is marked inactive but not removed.
Remove all registered combos and reset internal detection state.
Poll the combo event queue. Returns true if a combo event is available, filling event with:
id— the string identifier passed toap_register_chord()orap_register_sequence()triggered—truewhen the combo fires,falsewhen a chord is releasedtype—AP_COMBO_CHORDorAP_COMBO_SEQUENCE
| Limit | Value |
|---|---|
| Max registered combos | 16 (AP_MAX_COMBOS) |
| Max buttons per combo | 8 |
| Combo event queue size | 16 |
| Sequence detection buffer | 20 recent presses |
/* Register combos */
ap_button shoulders[] = { AP_BTN_L1, AP_BTN_R1 };
ap_register_chord("shoulders", shoulders, 2, 150);
ap_button uudd[] = { AP_BTN_UP, AP_BTN_UP, AP_BTN_DOWN, AP_BTN_DOWN };
ap_register_sequence("uudd", uudd, 4, 500, false);
/* In your main loop: */
ap_combo_event combo;
while (ap_poll_combo(&combo)) {
const char *kind = (combo.type == AP_COMBO_CHORD) ? "chord" : "seq";
if (combo.triggered)
printf("Triggered [%s]: %s\n", kind, combo.id);
else
printf("Released [%s]: %s\n", kind, combo.id);
}void on_trigger(const char *id, ap_combo_type type, void *userdata) {
printf("Triggered: %s\n", id);
}
void on_release(const char *id, ap_combo_type type, void *userdata) {
printf("Released: %s\n", id);
}
/* Chord with trigger + release callbacks */
ap_button shoulders[] = { AP_BTN_L1, AP_BTN_R1 };
ap_register_chord_ex("shoulders", shoulders, 2, 150, on_trigger, on_release, NULL);
/* Sequence with trigger callback only (no release phase) */
ap_button uudd[] = { AP_BTN_UP, AP_BTN_UP, AP_BTN_DOWN, AP_BTN_DOWN };
ap_register_sequence_ex("uudd", uudd, 4, 500, false, on_trigger, NULL);
/* ap_poll_combo() still works — callbacks and polling are both active */See examples/combo/main.c for a complete working example with both modes.
Printf-style logging. Writes to stderr and optionally to the configured log file.
Set the active log file path. Passing NULL disables file logging and keeps stderr logging only.
Resolve a standard NextUI-style log path for an app binary name:
LOGS_PATH/<app_name>.txtSHARED_USERDATA_PATH/logs/<app_name>.txtHOME/.userdata/logs/<app_name>.txt
Returns NULL if no suitable base path is available.
Get the underlying SDL renderer.
Get the underlying SDL window.
Show the SDL window. Primarily useful on desktop builds where the window may be hidden at startup.
Hide the SDL window. No-op if the window has not been created.
Enable or disable the background power button handler. On device, this listens for KEY_POWER from Linux input devices — a short press triggers suspend, a long press (>= 1s) triggers shutdown. Enabled automatically by ap_init() on device builds.
Control CPU frequency and fan speed on NextUI handheld hardware. All functions are no-ops (or return -1) on desktop builds. Fan functions have no effect on MY355 and TG5040, which have no fan hardware.
typedef enum {
AP_CPU_SPEED_DEFAULT = 0, /* do not change (zero-init default) */
AP_CPU_SPEED_MENU, /* light UI work */
AP_CPU_SPEED_POWERSAVE, /* battery saving */
AP_CPU_SPEED_NORMAL, /* standard pak speed — recommended default */
AP_CPU_SPEED_PERFORMANCE, /* maximum speed */
} ap_cpu_speed;Approximate frequencies per preset and platform:
| Preset | MY355 | TG5040 | TG5050 (big core) |
|---|---|---|---|
AP_CPU_SPEED_MENU |
600 MHz | 600 MHz | 672 MHz |
AP_CPU_SPEED_POWERSAVE |
1200 MHz | 1200 MHz | 1200 MHz |
AP_CPU_SPEED_NORMAL |
1608 MHz | 1608 MHz | 1680 MHz |
AP_CPU_SPEED_PERFORMANCE |
2000 MHz | 2000 MHz | 2160 MHz |
AP_CPU_SPEED_NORMAL is the recommended default for paks — it matches what NextUI's launch.sh
sets before handing control to a pak binary.
ap_config.cpu_speed — set this field in ap_config to apply a speed preset during
ap_init(). Zero (default struct value) leaves the CPU unchanged.
ap_config cfg = {
.window_title = "My Pak",
.is_nextui = AP_PLATFORM_IS_DEVICE,
.cpu_speed = AP_CPU_SPEED_NORMAL, /* set at init */
};
ap_init(&cfg);Set the CPU to a named preset. Internally writes userspace to the cpufreq governor and then
writes the platform-specific frequency to scaling_setspeed. Returns AP_OK on success,
AP_ERROR if the sysfs write fails. No-op (returns AP_OK) on desktop builds.
Read the current CPU frequency in MHz. Returns -1 on error or desktop builds.
Read the CPU temperature in °C from /sys/devices/virtual/thermal/thermal_zone0/temp.
Returns -1 on error or desktop builds.
my355 is a special case in NextUI: the shipped MinUI.pak/launch.sh boots at 1992 MHz, but
NextUI's workspace/my355/platform/platform.c uses 2000 MHz for the named performance preset.
Apostrophe follows platform.c for the preset API.
typedef enum {
AP_FAN_MODE_UNSUPPORTED = -1,
AP_FAN_MODE_MANUAL = 0,
AP_FAN_MODE_AUTO_QUIET,
AP_FAN_MODE_AUTO_NORMAL,
AP_FAN_MODE_AUTO_PERFORMANCE,
} ap_fan_mode;AP_FAN_MODE_AUTO_* mirrors NextUI's TG5050 fancontrol helper modes. On platforms without fan
hardware, ap_get_fan_mode() returns AP_FAN_MODE_UNSUPPORTED.
Set the TG5050 fan mode. AP_FAN_MODE_MANUAL stops any active fancontrol daemon without
changing the current raw fan state. The auto modes launch NextUI's
/mnt/SDCARD/.system/tg5050/bin/fancontrol helper with quiet, normal, or performance.
Returns AP_ERROR if an auto mode is requested and the helper is unavailable. No-op (returns
AP_OK) on non-TG5050 platforms and desktop builds.
Read the current TG5050 fan mode. If a fancontrol daemon is running, this reports the matching
auto mode. Otherwise, it reports AP_FAN_MODE_MANUAL when the fan sysfs node is readable, or
AP_FAN_MODE_UNSUPPORTED if the mode cannot be determined.
Set the fan to a fixed 0–100 percentage. On TG5050, Apostrophe first stops any active
fancontrol daemon, then prefers invoking NextUI's helper with that percentage; if the helper is
missing it falls back to a direct write to cooling_device0/cur_state. The direct sysfs fallback
uses NextUI-compatible rounding: (31 * percent + 50) / 100. Pass -1 to leave the current
speed unchanged. Only has effect on TG5050; no-op (returns AP_OK) on all other platforms and
desktop builds.
Read the current fan speed as a 0–100 percentage. Returns 0 on non-TG5050 platforms,
-1 if the sysfs read fails.
| Platform | CPU sysfs | Fan |
|---|---|---|
| MY355 (Miyoo Flip) | cpufreq/policy0/scaling_setspeed |
None |
| TG5040 (Trimui Brick) | cpu0/cpufreq/scaling_setspeed |
None |
| TG5050 (Trimui Smart Pro S) | cpu4/cpufreq/scaling_setspeed (big core) |
cooling_device0/cur_state (0–31), plus fancontrol helper auto curves |
| Desktop | No-op | No-op |
/* Set CPU at init */
ap_config cfg = { .is_nextui = AP_PLATFORM_IS_DEVICE,
.cpu_speed = AP_CPU_SPEED_NORMAL };
ap_init(&cfg);
/* Change speed at runtime */
ap_set_cpu_speed(AP_CPU_SPEED_PERFORMANCE);
int mhz = ap_get_cpu_speed_mhz(); /* e.g. 1680 on TG5050 */
int temp = ap_get_cpu_temp_celsius(); /* e.g. 42 */
/* Fan (TG5050 only) */
ap_set_fan_mode(AP_FAN_MODE_AUTO_NORMAL);
ap_set_fan_speed(50); /* 50% */
ap_fan_mode mode = ap_get_fan_mode();
int fan = ap_get_fan_speed(); /* 0–100 */See examples/perf/main.c for a live-readout demo with preset picker.
Get the last error message set by Apostrophe. Returns an empty string if no error.
Convenience check: returns true if result == AP_CANCELLED.
All widget functions return AP_OK on successful interaction, AP_CANCELLED when the user presses back, or AP_ERROR on failure.
ap_list_opts ap_list_default_opts(const char *title, ap_list_item *items, int count);
int ap_list(ap_list_opts *opts, ap_list_result *result);Scrollable list with:
- Single selection (A button)
- Multi-select mode (checkboxes)
- Reorder mode (toggle reorder button + D-Pad)
- Image thumbnails
- Optional hidden scrollbar (
hide_scrollbar) - Optional live footer updates (
footer_update) - Text overflow scrolling
- Help overlay (Menu)
- Explicit action bindings (
action_button,secondary_action_button,confirm_button,tertiary_action_button)
Footer hints are visual only. Behavior is driven by the action button fields in ap_list_opts.
ap_list_item:
typedef struct {
const char *label;
const char *metadata; // Hidden item data (e.g. path), not rendered
SDL_Texture *image; // Optional thumbnail (shown when show_images = true)
bool selected; // For multi-select
SDL_Texture *background_image; // Optional fullscreen preview for the focused item
const char *trailing_text; // Optional right-aligned visible hint text
} ap_list_item;Use metadata for hidden payloads such as paths or IDs. Use trailing_text for right-aligned UI text shown in the row. The AP_LIST_ITEM / AP_LIST_ITEM_BG helper macros still initialize only label and metadata; use designated initializers when you want a visible trailing hint.
ap_list_opts (action-related fields):
typedef struct ap_list_opts ap_list_opts;
typedef void (*ap_list_footer_update_fn)(ap_list_opts *opts, int cursor, void *userdata);
struct ap_list_opts {
...
ap_button reorder_button;
ap_button action_button;
ap_button secondary_action_button;
ap_button confirm_button;
ap_button tertiary_action_button;
bool hide_scrollbar; // Hide scrollbar while keeping scrolling behavior unchanged
int initial_index;
int visible_start_index;
TTF_Font *item_font; // Override list item text (default: AP_FONT_LARGE)
ap_list_footer_update_fn footer_update; // Optional live footer updater
void *footer_update_userdata;
};item_font overrides the font used to render list item labels and trailing hints. When NULL (zero-init default), the widget uses ap_get_font(AP_FONT_LARGE). Pass a font obtained from ap_get_font() or a custom-loaded TTF_Font to override.
hide_scrollbar suppresses the scrollbar gutter and thumb without changing list navigation, cursor behavior, or visible-item paging.
footer_update runs once per list loop after cursor and scroll position are finalized, but before the footer is drawn. It may inspect opts->items[cursor] and update existing footer text such as label or button_text. Keep the callback cheap and avoid mutating layout-driving state such as item_count, footer_count, or replacing the footer/items arrays while the list is open. If the footer needs to refresh without input, call ap_request_frame_in(ms) or ap_request_frame() from inside the callback.
When trailing_text is set, ap_list() renders it right-aligned using theme->hint. The hint is skipped for that row if reserving space for it would leave less than AP_S(96) for the main label.
D-Pad Left/Right skip forward/backward by one page (max_visible items) in ap_list. L1/R1 jump between alphabetical letter groups (items should be pre-sorted for best results). Both require no configuration but are disabled while reorder mode is active.
The help overlay is triggered by the Menu button. When help_text is set, Menu shows the help overlay first; if hidden footer items also exist, the footer overflow overlay follows. When no help_text is set, Menu opens the footer overflow directly.
ap_list_result:
typedef struct {
int selected_index;
ap_list_action action;
ap_list_item *items;
int item_count;
int visible_start_index;
} ap_list_result;int ap_options_list(ap_options_list_opts *opts, ap_options_list_result *result);Settings-style list where each row has a label and a configurable value area:
| Type | Behavior |
|---|---|
AP_OPT_STANDARD |
Left/Right cycles through predefined values; if confirm_button == AP_BTN_A, A confirms instead of cycling |
AP_OPT_KEYBOARD |
A opens keyboard for text input; if confirm_button == AP_BTN_A, confirming text also confirms the list |
AP_OPT_CLICKABLE |
A triggers a navigation/action callback |
AP_OPT_COLOR_PICKER |
A opens the color picker; if confirm_button == AP_BTN_A, picking a color also confirms the list |
Action buttons are explicit in ap_options_list_opts (action_button, secondary_action_button, confirm_button), and footer hints remain visual-only.
When return_on_option_change is enabled, a successful Left/Right cycle on a standard item exits immediately with AP_ACTION_OPTION_CHANGED after updating selected_option. If confirm_button != AP_BTN_A, pressing A on a standard item also cycles forward and returns AP_ACTION_OPTION_CHANGED. This keeps option-change exits distinct from action_button, which still reports AP_ACTION_TRIGGERED.
When confirm_button is set to AP_BTN_A, A takes on a "confirm and exit" role across all item types:
- Standard items: A confirms immediately (use Left/Right to change values).
- Keyboard/Color picker items: A opens the sub-editor; confirming inside it also exits the options list with
AP_ACTION_CONFIRMED. Cancelling the sub-editor returns to the list. - Clickable items: Unchanged — A exits with
AP_ACTION_SELECTED. When option storage is malformed (options == NULLor out-of-rangeselected_option), Apostrophe safely clamps/ignores the invalid value instead of dereferencing invalid memory. Long labels and option values are ellipsized as needed to keep the left label and right value area from overlapping on narrow screens.AP_OPT_CLICKABLErows render their trailing>in both focused and unfocused states.
ap_options_list_opts (action/scroll fields):
typedef struct {
...
int initial_selected_index;
int visible_start_index;
ap_button action_button;
ap_button secondary_action_button;
ap_button confirm_button;
bool return_on_option_change;
TTF_Font *label_font; // Override option label text (default: AP_FONT_LARGE)
TTF_Font *value_font; // Override option value text (default: AP_FONT_TINY)
} ap_options_list_opts;return_on_option_change makes standard-option changes return immediately with AP_ACTION_OPTION_CHANGED after the value updates. Leave it as false (the zero-init default) to keep the existing in-place behavior. label_font overrides the font used for option labels; value_font overrides the font used for option values. When NULL (zero-init default), the widget uses ap_get_font(AP_FONT_LARGE) and ap_get_font(AP_FONT_TINY) respectively. Pass a font obtained from ap_get_font() or a custom-loaded TTF_Font to override.
ap_options_list_result:
typedef struct {
int focused_index;
ap_list_action action;
...
int visible_start_index;
} ap_options_list_result;action may be AP_ACTION_OPTION_CHANGED when return_on_option_change is enabled, AP_ACTION_TRIGGERED for action_button, AP_ACTION_SECONDARY_TRIGGERED for secondary_action_button, AP_ACTION_SELECTED for clickable rows, AP_ACTION_CONFIRMED for confirm exits, or AP_ACTION_BACK.
int ap_keyboard(const char *initial_text, const char *help_text,
ap_keyboard_layout layout, ap_keyboard_result *result);
int ap_url_keyboard(const char *initial_text, const char *help_text,
ap_url_keyboard_config *cfg, ap_keyboard_result *result);5-row on-screen keyboard matching Gabagool's layout:
- Row 0: Numbers 1-0 + backspace (⌫, 2× width)
- Row 1: QWERTY row (10 keys, centered)
- Row 2: ASDF row (9 keys) + enter (↵, 1.5× width)
- Row 3: Shift (⇧, 2× width) + ZXCV row (7 keys) + symbol toggle (#+=, 2× width)
- Row 4: Space bar (8× width, centered)
Parameters:
initial_text: Pre-filled text shown in the input field on open (may beNULLfor empty).help_text: Text shown verbatim in the Menu help overlay. PassNULLto use the built-in keyboard instructions. Do not pass a prompt string here — it is not displayed as an on-screen label.
Button mapping (Gabagool-compatible):
- B: Backspace
- X: Space (general) / Toggle symbol alternates (URL)
- Y: Exit without saving
- Select: Toggle shift
- Start: Confirm
- L1/R1: Move text cursor left/right
- Menu: Help overlay (shows
help_textor built-in instructions whenhelp_textisNULL)
URL Keyboard adds configurable shortcut rows above the QWERTY keys:
- Default shortcuts:
https://,www.,.com,.org,.net,.io,.dev,.app,.edu,.gov - X toggles to symbol alternates:
http://,ftp://,.co,.tv,.me,.gg,.uk,.de,.ca,.au - URL special chars row:
/ : @ - _ . ~ ? # & - Bottom-row toggle switches between
123andabc, replacing the URL rows with digits and symbol sets when enabled - No space bar in URL mode
Layouts: AP_KB_GENERAL, AP_KB_URL, AP_KB_NUMERIC
Result: ap_keyboard_result.text (char[1024])
int ap_confirmation(ap_message_opts *opts, ap_confirm_result *result);Modal dialog with a message (optionally with an image above it). Waits for user to press A (confirm) or B (cancel).
Result: ap_confirm_result.confirmed (bool)
int ap_selection(const char *message, ap_selection_option *options, int count,
ap_footer_item *footer, int footer_count,
ap_selection_result *result);Horizontal pill-style chooser. User presses Left/Right to cycle options, A to confirm.
Result: ap_selection_result.selected_index
int ap_process_message(ap_process_opts *opts, ap_process_fn fn, void *userdata);Runs a worker function in a background thread while displaying a message and optional progress bar.
typedef int (*ap_process_fn)(void *userdata);ap_process_opts:
typedef struct {
const char *message;
bool show_progress;
float *progress; // Worker updates this [0.0–1.0]
int *interrupt_signal; // UI sets to 1 on cancel
ap_button interrupt_button; // Cancel button (AP_BTN_NONE = none)
char **dynamic_message; // Worker can update displayed text
int message_lines;
} ap_process_opts;int ap_detail_screen(ap_detail_opts *opts, ap_detail_result *result);Scrollable multi-section view for displaying information. Supports:
| Section Type | Content |
|---|---|
AP_SECTION_INFO |
Key-value pairs |
AP_SECTION_DESCRIPTION |
Wrapped text block |
AP_SECTION_IMAGE |
Single image |
AP_SECTION_TABLE |
Tabular data |
AP_SECTION_IMAGE textures are loaded once when the detail screen opens and reused for each frame until the screen exits.
ap_detail_action enum:
typedef enum {
AP_DETAIL_BACK = 0, // User pressed back
AP_DETAIL_ACTION, // User pressed the primary action button (A)
AP_DETAIL_SECONDARY_ACTION // User pressed the secondary action button (Y)
} ap_detail_action;ap_detail_screen() exits with AP_DETAIL_BACK on B, AP_DETAIL_ACTION on A, and AP_DETAIL_SECONDARY_ACTION on Y. If you want the Y action to be visible to users, add a matching Y footer hint.
ap_detail_opts (styling fields):
typedef struct {
...
bool center_title;
bool show_section_separator;
const ap_color *key_color;
TTF_Font *body_font; // Override body/value text (default: AP_FONT_TINY)
TTF_Font *section_title_font; // Override section headers (default: AP_FONT_SMALL)
TTF_Font *key_font; // Override info-pair key text (default: AP_FONT_TINY)
} ap_detail_opts;body_font overrides the font used for description text and info-pair values. section_title_font overrides section header text. key_font overrides info-pair key (left-hand) text. When NULL (zero-init default), the widget uses ap_get_font(AP_FONT_TINY) for body and key text, and ap_get_font(AP_FONT_SMALL) for section titles. Pass a font obtained from ap_get_font() or a custom-loaded TTF_Font to override.
ap_detail_result:
typedef struct {
ap_detail_action action;
} ap_detail_result;int ap_queue_viewer(const ap_queue_opts *opts);Live-updating, filterable display for background job queues. The widget polls a caller-supplied snapshot callback each frame — all threading stays in the caller.
Features:
- Animated pill selection (same as list widget)
- Horizontal text scroll on long titles when selected
- Per-item inline progress bars on the subtitle row
- Filter cycling: ALL / IN PROGRESS / DONE / FAILED by default, overrideable via
filter_labels[4](Y button) - Summary bar:
"X/Y COMPLETE, Z FAILED"above footer - A: Detail callback for terminal items (DONE/FAILED/SKIPPED)
- X: Cancel callback while active, clear-done callback when idle
- Menu / desktop
H: open hidden footer actions when the footer shows+N - Idle-aware: calls
ap_request_frame()only while jobs are active
Navigation matches ap_list(): D-Pad Left/Right skip by one visible page, while L1/R1 jump to the previous/next first-letter group within the active filter. For best results, keep queue titles pre-sorted.
ap_queue_status:
typedef enum {
AP_QUEUE_PENDING = 0, // Waiting to start (hint color)
AP_QUEUE_RUNNING = 1, // Actively being processed (accent color)
AP_QUEUE_DONE = 2, // Completed successfully (soft green, RGBA 100, 200, 100, 255)
AP_QUEUE_FAILED = 3, // Ended in error (red)
AP_QUEUE_SKIPPED = 4, // Intentionally skipped or cancelled (hint color)
} ap_queue_status;ap_queue_item (filled by snapshot callback each frame):
typedef struct {
char title[256]; // Large primary label
char subtitle[128]; // Small secondary label
char status_text[64]; // Right-aligned status string
ap_queue_status status; // Color-coding and filter
float progress; // 0.0–1.0 = inline progress bar; < 0 = no bar
void *userdata; // Caller-defined context
} ap_queue_item;ap_queue_opts:
typedef struct {
const char *title; // Screen title, e.g. "DOWNLOADS"
ap_queue_snapshot_fn snapshot; // Required: fills items each frame
int max_items; // Buffer capacity; 0 → 256
void *userdata; // Passed to all callbacks
ap_queue_detail_fn on_detail; // Optional: A button on terminal items
ap_queue_cancel_fn on_cancel; // Optional: X button while queue active
ap_queue_clear_fn on_clear; // Optional: X button when queue idle
ap_status_bar_opts *status_bar; // Optional: top-right status pill
bool hide_filter; // Set true to hide Y=FILTER cycling
const char *filter_labels[4]; // Optional filter labels: [0]=ALL, [1]=IN PROGRESS (PENDING+RUNNING), [2]=DONE, [3]=FAILED; NULL or "" entries use defaults
} ap_queue_opts;Callbacks:
// Fill buf[0..max] with current state. Must be thread-safe. Return count.
typedef int (*ap_queue_snapshot_fn)(ap_queue_item *buf, int max, void *userdata);
// Called when A is pressed on a terminal item. May push another widget.
typedef void (*ap_queue_detail_fn)(const ap_queue_item *item, void *userdata);
// Called when X is pressed while active items remain.
// Update your queue state so subsequent snapshots reflect cancellation.
typedef void (*ap_queue_cancel_fn)(void *userdata);
// Called when X is pressed to clear completed items.
typedef void (*ap_queue_clear_fn)(void *userdata);Example (minimal):
static int my_snapshot(ap_queue_item *buf, int max, void *ud) {
pthread_mutex_lock(&queue_mutex);
int n = copy_queue_to_buf(buf, max); // your thread-safe copy
pthread_mutex_unlock(&queue_mutex);
return n;
}
static void my_cancel(void *ud) {
/* confirm cancellation, then mark unfinished items as CANCELLED */
}
ap_queue_opts opts = {
.title = "DOWNLOADS",
.snapshot = my_snapshot,
.on_cancel = my_cancel,
.filter_labels = { "ALL", "ACTIVE", "COMPLETE", "ERRORS" },
};
ap_queue_viewer(&opts);To represent cancelled downloads, keep queue state in your own data model and have future snapshots return AP_QUEUE_SKIPPED with status_text = "CANCELLED" for unfinished items after on_cancel runs.
int ap_download_manager(ap_download *downloads, int count,
ap_download_opts *opts, ap_download_result *result);Multi-threaded file downloader with per-file progress bars. Requires libcurl (compile with -DAP_ENABLE_CURL and link with -lcurl).
For Apostrophe device example builds, bundled curl is enabled by default for EXAMPLE=download via USE_BUNDLED_CURL=1.
This builds dependencies into build/third_party/<platform>/..., stages runtime libs in build/<platform>/download/lib,
and expects pak launchers to include that directory in LD_LIBRARY_PATH.
Features:
- Thread pool with configurable concurrency (default 3)
- Per-file progress bars (3/4 screen width, max 900px)
- Live download speed display (toggleable with X)
- Auto-scroll to active downloads
- Cancel all with Y
- Custom HTTP headers and SSL options
ap_download (per-job):
typedef struct {
const char *url; // Source URL
const char *dest_path; // Destination file path
const char *label; // Display label
ap_download_status status; // AP_DL_PENDING/DOWNLOADING/COMPLETE/FAILED
float progress; // 0.0–1.0
double speed_bps; // Bytes per second
int http_code; // HTTP response code
char error[256]; // Error message
} ap_download;ap_download_opts:
typedef struct {
int max_concurrent; // Max simultaneous downloads (default 3)
bool skip_ssl_verify; // Disable SSL cert verification
const char **headers; // "Header: Value" strings
int header_count;
} ap_download_opts;ap_download_result:
typedef struct {
int total;
int completed;
int failed;
bool cancelled;
} ap_download_result;int ap_color_picker(ap_color initial, ap_color *result);5×5 grid of predefined colors. Navigate with D-Pad, confirm with A.
void ap_show_help_overlay(const char *text);Full-screen scrollable text overlay. Typically triggered by Menu in widgets that have help_text configured.
typedef enum {
AP_FILE_PICKER_FILES = 0, /* Only files are selectable */
AP_FILE_PICKER_DIRS = 1, /* Only directories are selectable */
AP_FILE_PICKER_BOTH = 2, /* Files and directories are selectable */
} ap_file_picker_mode;typedef struct {
const char *title; /* Screen title (NULL = show relative path from root) */
ap_file_picker_mode mode; /* What can be selected */
const char *initial_path; /* Starting directory (NULL = root_path) */
const char *root_path; /* Navigation boundary (NULL = auto-detect per platform) */
const char **extensions; /* File extension filter array, e.g. {"zip","7z"} */
int extension_count; /* Number of entries in extensions array */
bool allow_create; /* Show NEW DIR action (X button) in DIRS/BOTH modes */
bool show_hidden; /* Show dotfiles/dotdirs (e.g. .env, .gitignore, .config) */
ap_status_bar_opts *status_bar; /* Optional status bar */
} ap_file_picker_opts;typedef struct {
char path[1024]; /* Full absolute path of the selected item */
bool is_directory; /* True when the selected item is a directory */
} ap_file_picker_result;| Function | Description |
|---|---|
ap_file_picker_default_opts(title) |
Create default options (mode = FILES, no filter, no create) |
ap_file_picker(opts, result) |
Show blocking file picker. Returns AP_OK on selection, AP_CANCELLED on back from root |
| Button | Action |
|---|---|
| D-Pad Up/Down | Navigate items |
| D-Pad Left/Right | Page up/down |
| L1/R1 | Jump between letter groups |
| A | Enter folder / select file |
| B | Go up one directory (cancel at root) |
| X | Create new folder (DIRS / BOTH modes with allow_create) |
| START | Select current directory (DIRS / BOTH modes) |
| Platform | Default root |
|---|---|
Device (AP_PLATFORM_IS_DEVICE) |
SDCARD_PATH env var, or /mnt/SDCARD |
| Windows | USERPROFILE env var, or . |
| macOS / Linux | HOME env var, or . |
Notes:
- On device,
SDCARD_PATHis always the hard ceiling even ifroot_pathis provided. - On desktop,
root_pathoverrides the default home root when provided. initial_pathis only used when it resolves to a directory inside the effective root.allow_createis ignored inAP_FILE_PICKER_FILES.- In
AP_FILE_PICKER_BOTH, theAfooter hint updates live:ENTERon directories,OPENon files. - Set
show_hidden = trueto list and select dotfiles like.envand dot-directories like.config. - New-folder creation only accepts a single path component; separators and
./..are rejected. - Dotfiles still use the normal extension-filter logic when
extensionsare configured.
ap_file_picker_opts opts = ap_file_picker_default_opts("Select ROM");
opts.mode = AP_FILE_PICKER_FILES;
opts.extensions = (const char *[]){"zip", "7z"};
opts.extension_count = 2;
ap_file_picker_result result;
int rc = ap_file_picker(&opts, &result);
if (rc == AP_OK) {
printf("Selected: %s (dir=%d)\n", result.path, result.is_directory);
}