The DasherCore C API (src/dasher.h / src/CAPI.cpp) wraps the C++ predictive text engine so that any language — Swift, Kotlin, C#, JavaScript (via WASM), Rust — can integrate Dasher without touching C++ directly.
The API is designed around a single opaque session handle (dasher_ctx) with a frame-based rendering loop: you feed pointer input, call dasher_frame() to advance the engine, and receive an array of draw commands to render with your platform's canvas API.
Header: src/dasher.h
Implementation: src/CAPI.cpp
Tests: tests/test_capi.cpp
Frontend (Swift / Kotlin / C# / ...)
│
dasher.h (C API)
│
CAPI.cpp
│
┌────────────────────────────────────────────┐
│ dasher_ctx::Interface │
│ extends CDashIntfScreenMsgs │
│ extends CDashIntfSettings │
│ extends CDasherInterfaceBase │
└────────────────────────────────────────────┘
│ │
CommandScreen PointerInput
(captures draw (wraps pointer
commands into position into
int32 arrays) Dasher input system)
- Single opaque handle — one
dasher_ctx*per session - Not thread-safe — one thread per context
- Zero-copy rendering — draw commands returned as raw
int32arrays (6 ints per command), valid only until the nextdasher_frame()call - Ephemeral string pointers — all
const char*returns are valid only until the next API call on the same context - Null-safe — every function handles
NULL ctxgracefully (returns empty/zero, never crashes)
The C API is built with CMake:
mkdir build && cd build
cmake .. -DBUILD_CAPI=ON -DBUILD_TESTS=ON -DTEST_DATA_DIR=/path/to/Data
cmake --build .| Option | Default | Description |
|---|---|---|
BUILD_CAPI |
ON | Build the C API shared library (libdasher) |
BUILD_TESTS |
ON | Build unit tests (requires BUILD_CAPI=ON) |
TEST_DATA_DIR |
(none) | Compile-time path to Data/ directory for tests |
| Target | Type | Output |
|---|---|---|
DasherCore |
Static library | Core C++ engine |
dasher |
Shared library | C API (src/CAPI.cpp) |
dasher_*_tests |
Executables | Unit tests (one executable per test file, e.g. dasher_capi_tests) |
settings_manifest.json is processed by Scripts/generate_parameters.py at configure time to auto-generate src/DasherCore/Parameters.cpp. Python 3 is required.
#include "dasher.h"
// 1. Create session
char* error = NULL;
dasher_ctx* ctx = dasher_create("/path/to/Data", "/path/to/user_dir", &error);
if (!ctx) {
fprintf(stderr, "Failed: %s\n", error);
return 1;
}
// 2. Set screen size (triggers engine initialization)
dasher_set_screen_size(ctx, 800, 600);
// 3. Configure (optional)
dasher_set_speed_percent(ctx, 120);
dasher_set_alphabet_id(ctx, "English");
dasher_set_locale(ctx, "de");
// 4. Main loop
while (running) {
dasher_mouse_move(ctx, pointer_x, pointer_y);
int* cmds; int cmd_count;
char** strs; int str_count;
dasher_frame(ctx, time_ms, &cmds, &cmd_count, &strs, &str_count);
// Render draw commands
for (int i = 0; i < cmd_count; i += 6) {
render_command(cmds[i], cmds[i+1], cmds[i+2],
cmds[i+3], cmds[i+4], cmds[i+5], strs);
}
}
// 5. Cleanup
dasher_save_settings(ctx);
dasher_destroy(ctx);dasher_ctx* dasher_create(const char* data_dir, const char* user_dir, char** out_error);Creates a new Dasher session.
data_dir— Path to DasherCore'sData/directory (alphabets, colours, training files). Must be readable.user_dir— Writable directory for settings. IfNULL,data_diris used. Settings are stored in<user_dir>/dasher_settings.xml.out_error— If notNULL, set to a human-readable error string on failure. Do NOT free. Valid until next API call.- Returns — Session handle, or
NULLon failure.
void dasher_destroy(dasher_ctx* ctx);Destroys a session and frees all resources. Safe to call with NULL.
void dasher_set_screen_size(dasher_ctx* ctx, int width, int height);Sets the canvas dimensions. Must be called before dasher_frame() — it triggers engine initialization (Realize()). Call again on window resize.
void dasher_mouse_move(dasher_ctx* ctx, float x, float y);
void dasher_mouse_down(dasher_ctx* ctx);
void dasher_mouse_up(dasher_ctx* ctx);mouse_move— Feed pointer coordinates (mouse, touch, eyetracker). Origin top-left, pixels. Values are clamped to screen bounds.mouse_down— Signal pointer press (starts Dasher zooming).mouse_up— Signal pointer release (pauses Dasher zooming).
void dasher_key_event(dasher_ctx* ctx, int key, int pressed);For switch access, keyboard, or button input. Key values (DASHER_KEY_* constants in dasher.h):
| Key | Constant | Value |
|---|---|---|
| Start/Stop | DASHER_KEY_START_STOP |
0 |
| Buttons 1–4 | DASHER_KEY_BUTTON_1..4 |
1–4 |
| Primary | 100 | |
| Secondary | 101 | |
| Tertiary | 102 |
pressed: 1 for key down, 0 for key up.
void dasher_frame(dasher_ctx* ctx, int64_t time_ms,
int** out_commands, int* out_command_count,
char*** out_strings, int* out_string_count);Advances one frame and returns draw commands.
time_ms— Current time in milliseconds. If <= 0, treated as 0.out_commands— Set to internal command buffer (array ofint). Valid until nextdasher_frame(). Do NOT free.out_command_count— Set to total number of ints (not number of commands — divide by 6 for command count).out_strings— Array ofchar*string pointers. Valid until nextdasher_frame().out_string_count— Number of strings.
Each command is 6 int32_t values: [opcode, a, b, c, d, argb]. Opcode constants (DASHER_CMD_*) are defined in dasher.h — prefer them over raw numbers.
| Opcode | Constant | Fields | Description |
|---|---|---|---|
| 0 | DASHER_CMD_CLEAR |
argb = background colour | Fill entire canvas |
| 1 | DASHER_CMD_CIRCLE |
a=x, b=y, c=radius, d=1 filled / 0 outline, argb | Circle shape |
| 2 | DASHER_CMD_LINE |
a=x1, b=y1, c=x2, d=y2, argb | Line segment |
| 3 | DASHER_CMD_RECT_OUTLINE |
a=x1, b=y1, c=x2, d=y2, argb | Rectangle outline |
| 4 | DASHER_CMD_RECT_FILL |
a=x1, b=y1, c=x2, d=y2, argb | Filled rectangle |
| 5 | DASHER_CMD_TEXT |
a=x, b=y, c=fontSize, d=stringIndex, argb | Render string from strings array |
ARGB format: (alpha << 24) | (red << 16) | (green << 8) | blue
for (int i = 0; i < cmd_count; i += 6) {
int opcode = cmds[i+0];
int a = cmds[i+1], b = cmds[i+2], c = cmds[i+3], d = cmds[i+4];
int argb = cmds[i+5];
switch (opcode) {
case DASHER_CMD_CLEAR: fill_background(argb); break;
case DASHER_CMD_CIRCLE: draw_circle(a, b, c, d == 1, argb); break;
case DASHER_CMD_LINE: draw_line(a, b, c, d, argb); break;
case DASHER_CMD_RECT_OUTLINE: draw_rect_outline(a, b, c, d, argb); break;
case DASHER_CMD_RECT_FILL: draw_rect_filled(a, b, c, d, argb); break;
case DASHER_CMD_TEXT: draw_text(a, b, c, strs[d], argb); break;
}
}int dasher_color_argb(int alpha, int red, int green, int blue); // Create ARGB
int dasher_color_rgb(int red, int green, int blue); // Create opaque (alpha=255)
int dasher_color_get_alpha(int argb); // Extract alpha
int dasher_color_get_red(int argb); // Extract red
int dasher_color_get_green(int argb); // Extract green
int dasher_color_get_blue(int argb); // Extract blueThe int[] command buffer from dasher_frame() (Strand 1) is 2D painter's algorithm — no depth buffer, no compositing. It can't represent 3D extruded cubes, VR/spatial layouts, or fully custom visualisations. Strand 2 is the alternative: query the visible node tree each frame and render it yourself with your own graphics API.
The two strands coexist. A frontend can use Strand 1 for most rendering and drop into Strand 2 for specific features (e.g. render cube mode as real 3D from the node tree), or render everything from scratch.
Node capture is off by default — Strand 1-only frontends pay zero overhead. Enable it once at setup:
dasher_set_visible_nodes_enabled(ctx, 1);After this, each dasher_frame() records the nodes it draws; dasher_get_visible_nodes() returns them.
typedef struct dasher_node_info {
int struct_size; // caller sets to sizeof(dasher_node_info) before the call
long long dasher_y1; // node's Dasher-Y range
long long dasher_y2;
int symbol; // alphabet symbol index (-1 for group/control nodes)
int has_children; // 1 if this node has children
int depth; // tree depth from the rendered root (0 = root)
int is_game_node; // 1 if on the game-mode path
int screen_x1, screen_y1; // node's clipped screen bounds
int screen_x2, screen_y2;
int fill_argb; // node fill colour (from active palette)
int outline_argb; // node outline colour
int label_index; // index into out_strings (-1 if no label)
} dasher_node_info;
// Returns the number of visible nodes (may exceed max_nodes — grow the buffer
// and re-query if so). Nodes are depth-first (parent before children).
// out_strings / out_string_count hold label text; label_index indexes into it.
// out_nodes and out_strings are engine-owned, valid until the next
// dasher_frame() or dasher_get_visible_nodes() call.
// Returns -1 if capture is disabled, the engine isn't realised, or the caller's
// struct_size is smaller than this engine's struct.
int dasher_get_visible_nodes(dasher_ctx* ctx, dasher_node_info* out_nodes, int max_nodes,
char*** out_strings, int* out_string_count);The captured node set matches exactly what the Strand 1 command buffer draws for the same frame (Strand 1/Strand 2 parity), so a frontend can mix strands without drift. screen_x1..y2 use the same clip formula as the DASHER_CMD_RECT_FILL draw. depth is the source for cube extrusion (deeper nodes recede); symbol/label_index identify what each node represents.
typedef struct dasher_viewport {
int struct_size; // caller sets to sizeof(dasher_viewport) before the call
long long crosshair_x; // crosshair Dasher X (fixed at the origin)
long long crosshair_y; // crosshair Dasher Y
long long visible_min_y; // visible Dasher-Y range
long long visible_max_y;
int screen_width; // canvas size from dasher_set_screen_size
int screen_height;
} dasher_viewport;
int dasher_get_viewport(dasher_ctx* ctx, dasher_viewport* out); // 0 on success, -1 on errordasher_set_visible_nodes_enabled(ctx, 1);
// each frame:
dasher_frame(ctx, time_ms, &cmds, &cc, &strs, &sc); // still call it — it advances the engine
dasher_node_info nodes[256];
for (int i = 0; i < 256; ++i) nodes[i].struct_size = sizeof(dasher_node_info);
char** labels = NULL;
int label_count = 0;
int n = dasher_get_visible_nodes(ctx, nodes, 256, &labels, &label_count);
if (n > 256) { /* grew past the buffer — reallocate and re-query */ }
for (int i = 0; i < n && i < 256; ++i) {
dasher_node_info nd = nodes[i];
// Render however you like: flat rect, 3D cube extruded by nd.depth, sphere…
if (nd.label_index >= 0) draw_label(labels[nd.label_index], nd.screen_x1, nd.screen_y1);
draw_node(nd.screen_x1, nd.screen_y1, nd.screen_x2, nd.screen_y2,
nd.fill_argb, nd.outline_argb);
}For a 3D cube frontend, render the same nodes in two passes (flat layout first, then cubes composited on top using depth for the extrusion), or render everything in 3D from the start. LP_SHAPE_TYPE (read via dasher_get_long_parameter) is a hint the frontend can honour or ignore — shape and depth become frontend concerns, not engine concerns.
const char* dasher_get_output_text(dasher_ctx* ctx); // Current buffer
void dasher_reset_output_text(dasher_ctx* ctx); // Clear text, keep model position
void dasher_reset(dasher_ctx* ctx); // Clear text AND reset model to starttypedef void (*dasher_output_callback)(int event_type, const char* text, void* user_data);
void dasher_set_output_callback(dasher_ctx* ctx, dasher_output_callback callback, void* user_data);Receives real-time text events without polling. Event types (DASHER_EVENT_* constants in dasher.h):
| Type | Meaning |
|---|---|
DASHER_EVENT_OUTPUT (0) |
Text output (insertion) |
DASHER_EVENT_DELETE (1) |
Text delete (backspace) |
DASHER_EVENT_BUFFER_CLEAR (2) |
Buffer cleared wholesale — dasher_reset, dasher_reset_output_text, or an alphabet change (which clears the buffer). text is empty; shadow buffers must be cleared, not diffed |
The callback fires on the thread calling dasher_frame(). DASHER_EVENT_BUFFER_CLEAR may
also fire on the thread calling the reset function itself.
The engine positions node labels (including the anti-overlap "shunting" that pushes a child label past its parent) using reported text widths. Real glyph advances differ substantially from any estimate, and estimation errors compound down the label chain — at deep zoom this surfaced to users as jumbled, overlapping letters (issue #56). Frontends rendering the command buffer should register a measurement callback that uses the same font they draw text commands with:
typedef int (*dasher_text_size_callback)(const char* text, int font_size,
int* out_width, int* out_height,
void* user_data);
void dasher_set_text_size_callback(dasher_ctx* ctx, dasher_text_size_callback callback, void* user_data);
void dasher_text_metrics_changed(dasher_ctx* ctx);- Fill
*out_width/*out_heightin pixels and return0; return non-zero to fall back to the engine's estimate (the failure is not cached — the next frame retries). - The callback fires on the thread calling
dasher_frame(); results are cached per label and font size, so steady-state frames don't re-measure. - Registering before
dasher_set_screen_sizeis fine — the callback is forwarded when the screen is created. - After the canvas font changes (e.g.
SP_DASHER_FONT), calldasher_text_metrics_changed()to invalidate the cache. - Wrapped labels (the paused/lock message) are not measured through the callback; they always use the estimate.
- Without a callback the engine estimates width as
utf8_codepoints × fontSize / 2(code points, not bytes — accented text previously over-measured). Good enough to run; not good enough for deep zoom.
typedef void (*dasher_message_callback)(int message_type, const char* text, void* user_data);
void dasher_set_message_callback(dasher_ctx* ctx, dasher_message_callback callback, void* user_data);Receives engine messages (warnings, errors, info). When registered, default canvas-message rendering is suppressed — the frontend handles display. Message types:
| Type | Meaning |
|---|---|
| 0 | Informational (non-modal) |
| 1 | Warning (modal, pauses entry) |
int dasher_get_alphabet_count(dasher_ctx* ctx);
const char* dasher_get_alphabet_name(dasher_ctx* ctx, int index);
const char* dasher_get_alphabet_id(dasher_ctx* ctx);
void dasher_set_alphabet_id(dasher_ctx* ctx, const char* alphabet_id);- Alphabet IDs are human-readable names like
"English with limited punctuation". - Setting a new alphabet clears the edit buffer.
- If called before
dasher_set_screen_size, the alphabet is stored and applied after initialization.
int dasher_get_language_model_count(void); // Number of registered LMs
int dasher_get_language_model_id_at(int index); // ID by index (0..count-1)
const char* dasher_get_language_model_name(int id); // Display name
const char* dasher_get_language_model_description(int id); // Description
int dasher_get_language_model_id(dasher_ctx* ctx); // Current active LM
void dasher_set_language_model_id(dasher_ctx* ctx, int model_id); // Switch LM
int dasher_get_language_model_param_count(int id); // LM-specific params
int dasher_get_language_model_param_key(int id, int index);| ID | Name | Description |
|---|---|---|
| 0 | PPM | Prediction by Partial Match (default) |
| 2 | Word | Word-level language model |
| 3 | Mixture | PPM + Dictionary blend |
| 4 | CTW | Context Tree Weighting |
Note: ID 1 is unused (historical). IDs 5+ are available for external LMs.
Each LM exposes relevant tuning parameters via dasher_get_language_model_param_count/key:
| LM | Parameters |
|---|---|
| PPM | LP_LM_ALPHA, LP_LM_BETA, LP_LM_MAX_ORDER, LP_LM_EXCLUSION, LP_LM_UPDATE_EXCLUSION |
| Word | LP_LM_WORD_ALPHA, LP_LM_MAX_ORDER |
| Mixture | All PPM params + LP_LM_MIXTURE, LP_LM_WORD_ALPHA |
| CTW | LP_LM_MAX_ORDER |
See LM_REGISTRY.md for details on registering custom LMs.
int dasher_get_speed_percent(dasher_ctx* ctx);
void dasher_set_speed_percent(dasher_ctx* ctx, int percent);- Range: the engine's declared
LP_MAX_BITRATEbounds (raw 1–1000 → ~1–625 %, in whole-percent steps of the raw unit). The previous fixed 20–400 clamp truncated Dasher v5's top speeds (v5 allowed raw 10–800 = up to 500 %); values are now clamped to the manifest range instead. Default: 100. - Internally maps to
LP_MAX_BITRATE:bitrate = percent / 100.0 * 160
DasherCore has a self-describing parameter schema with 99 parameters across three types:
| Type | Prefix | Count | Accessors |
|---|---|---|---|
| Boolean | BP_* |
30 | dasher_get/set_bool_parameter |
| Long | LP_* |
56 | dasher_get/set_long_parameter |
| String | SP_* |
13 | dasher_get/set_string_parameter |
int dasher_get_bool_parameter(dasher_ctx* ctx, int key);
void dasher_set_bool_parameter(dasher_ctx* ctx, int key, int value);
long dasher_get_long_parameter(dasher_ctx* ctx, int key);
void dasher_set_long_parameter(dasher_ctx* ctx, int key, long value);
const char* dasher_get_string_parameter(dasher_ctx* ctx, int key);
void dasher_set_string_parameter(dasher_ctx* ctx, int key, const char* value);
int dasher_find_parameter_key(const char* enum_key_name);Parameter keys are defined in src/DasherCore/Parameters.h. Use dasher_find_parameter_key to look up keys by name (e.g. "LP_LANGUAGE_MODEL_ID").
Frontends can build settings UIs dynamically:
typedef struct dasher_parameter_info {
int key; // BP_*/LP_*/SP_* enum value
const char* name; // human-readable name
const char* desc; // human-readable description
int type; // 0=bool, 1=long, 2=string
int ui_type; // 0=none, 1=switch, 2=slider, 3=step, 4=enum, 5=textField
long min_val; // minimum value (numeric)
long max_val; // maximum value (numeric)
long step; // step size
int advanced; // 1 if advanced/expert setting
const char* group; // "Input", "Language", "Appearance", "Customization", "Output", "Game Mode"
const char* subgroup; // filter class (e.g. "CSmoothingFilter")
} dasher_parameter_info;
int dasher_get_parameter_count(void);
int dasher_get_parameter_info(int index, dasher_parameter_info* out);For parameters with ui_type == 4 (enum):
int dasher_get_parameter_enum_count(int key);
const char* dasher_get_parameter_enum_name(int key, int index);
int dasher_get_parameter_enum_value(int key, int index);For string parameters (e.g. alphabet list, palette list):
int dasher_get_parameter_string_values(dasher_ctx* ctx, int key, const char** out_names, int max_out);Returns count; copies up to max_out pointers. Pointers valid until next API call.
Parameters are organized into groups for UI presentation:
| Group | Contents |
|---|---|
| Input | Input filters, control modes, smoothing, buttons |
| Language | Alphabet, language model, LM tuning, adaptive learning |
| Appearance | Font, node shape, geometry, transparency |
| Customization | Mouse line, colour palette, zoom steps, margins |
| Output | Speed, framerate, clipboard, speech |
| Game Mode | Help path drawing, distance/time settings |
Parameters are defined in settings_manifest.json and auto-generated into Parameters.cpp. Each entry includes:
- Key, storage name, type, default value
- Label and description (localizable)
- UI type hint, min/max/step
- Group and subgroup
- Tier (common / advanced / expert)
- Optional:
dependsOn,platformDefaults,enumValues,persistence
int dasher_get_palette_count(dasher_ctx* ctx);
const char* dasher_get_palette_name(dasher_ctx* ctx, int index);
const char* dasher_get_current_palette(dasher_ctx* ctx);
int dasher_get_palette_preview_colors(dasher_ctx* ctx, int index, int* out_colors);
void dasher_set_palette(dasher_ctx* ctx, const char* palette_name);dasher_get_palette_preview_colors writes 4 ARGB preview colours into out_colors (must have room for 4 ints). Returns 0 on success.
DasherCore owns a light/dark appearance model so frontends don't each reinvent the System/Light/Dark toggle, the companion lookup, or the palette-preference storage. State persists to <user_dir>/appearance_settings.xml. The active palette (returned by dasher_get_current_palette) is derived from mode + system input + preferences, so an auto-switch can never overwrite the user's explicit choice across restarts.
int dasher_get_palette_appearance(dasher_ctx* ctx, int index); // DASHER_PALETTE_APPEARANCE_*; -1=oor
const char* dasher_find_companion_palette(dasher_ctx* ctx, const char* palette_name); // "" if none (v2; was NULL)
int dasher_get_appearance_mode(dasher_ctx* ctx); // DASHER_APPEARANCE_MODE_*
void dasher_set_appearance_mode(dasher_ctx* ctx, int mode);
int dasher_get_system_appearance(dasher_ctx* ctx); // DASHER_PALETTE_APPEARANCE_LIGHT/DARK (transient)
void dasher_set_system_appearance(dasher_ctx* ctx, int appearance);
const char* dasher_get_light_palette(dasher_ctx* ctx); // persisted preferences
const char* dasher_get_dark_palette(dasher_ctx* ctx);
void dasher_set_light_palette(dasher_ctx* ctx, const char* name);
void dasher_set_dark_palette(dasher_ctx* ctx, const char* name);
void dasher_set_user_palette(dasher_ctx* ctx, const char* name); // sets current side + defaults other- Mode
SYSTEMfollowsdasher_set_system_appearance;LIGHT/DARKforce that side. - Two preferences (
light_palette,dark_palette) are stored independently, so a user can mix — e.g. Rainbow for light, TurboLUT Dark for dark.dasher_set_user_palettesets the current effective side and defaults the other to the chosen palette's companion. dasher_set_paletteroutes throughdasher_set_user_palette, so existing pickers stay correct within the model.
Typical frontend usage:
// On launch and whenever the OS appearance changes:
dasher_set_system_appearance(ctx, os_is_dark ? DASHER_PALETTE_APPEARANCE_DARK : DASHER_PALETTE_APPEARANCE_LIGHT);
// Settings UI: System / Light / Dark control
dasher_set_appearance_mode(ctx, mode); // DASHER_APPEARANCE_MODE_*int dasher_enter_game_mode(dasher_ctx* ctx); // Returns 0 on success, -1 if no text
void dasher_leave_game_mode(dasher_ctx* ctx);
int dasher_game_mode_active(dasher_ctx* ctx); // 1=on, 0=off
void dasher_game_set_canvas_text(dasher_ctx* ctx, int enabled); // Suppress canvas text
const char* dasher_game_get_target_text(dasher_ctx* ctx); // Target sentence
int dasher_game_get_correct_count(dasher_ctx* ctx); // Correct symbols (or -1)
int dasher_game_get_target_length(dasher_ctx* ctx); // Total symbols (or -1)
const char* dasher_game_get_wrong_text(dasher_ctx* ctx); // Wrong text since last correctGame mode provides a typing tutor where users type a target sentence. The frontend can either:
- Let the engine render game UI on canvas (default)
- Suppress canvas text with
dasher_game_set_canvas_text(ctx, 0)and render its own UI using the game query functions
int dasher_set_locale(dasher_ctx* ctx, const char* locale);
const char* dasher_get_locale(dasher_ctx* ctx);
void dasher_set_string_override(dasher_ctx* ctx, const char* key, const char* value);
const char* dasher_get_localized_string(dasher_ctx* ctx, const char* key);dasher_set_localeloadsData/Strings/strings_{locale}.json. Returns 0 on success, -1 if not found.NULLor"en"resets to English defaults.dasher_set_string_overrideoverrides a specific translatable string by key (e.g."BP_DRAW_MOUSE_LINE.label"). PassNULLvalue to clear. Overrides take precedence over locale files.dasher_get_localized_stringreturns the string for a key (checks overrides first, then locale file). ReturnsNULLif not found.
Keys follow the pattern: {PARAM_KEY}.label, {PARAM_KEY}.description, {PARAM_KEY}.enum.{Enum Label}
Examples:
"BP_DRAW_MOUSE_LINE.label"→ "Draw Mouse Line""LP_GEOMETRY.enum.Old Style"→ translated enum label
void dasher_save_settings(dasher_ctx* ctx);Saves current settings to the XML file specified at creation (<user_dir>/dasher_settings.xml).
void dasher_reset_settings(dasher_ctx* ctx);Resets every parameter to its built-in default value (from Parameters.h). It routes through the typed SetBoolParameter / SetLongParameter / SetStringParameter methods, so the normal parameter-change notifications fire and a live engine reconfigures itself (alphabet, colour palette, and language model are reloaded, etc.).
This only affects the in-memory session — it does not delete the persisted files. Frontends that want persisted defaults should delete dasher_settings.xml (and appearance_settings.xml for the RFC 0007 appearance sidecar) from the user directory before calling, so defaults also load on the next launch. Null-safe: a no-op for NULL ctx.
const char* dasher_get_training_path(dasher_ctx* ctx); // absolute path, engine-owned
int dasher_import_training_text(dasher_ctx* ctx, const char* text);
int dasher_capi_version(void); // 1 = user-dir training scandasher_get_training_path— the single file adaptive learning appends to for the current alphabet (<user_dir>/training_<alphabet>.txtat the user-dir root). Frontend training UIs (export / size / reset) must read this path, never a derived one. Empty when no model is realized. Pointer valid until the next API call on the context.- Since CAPI version 1, the startup training load scans the per-context user data directory (in addition to the bundled data dir) — learning accumulated in previous sessions is loaded automatically for split-dir frontends (Android, GTK, Apple). Single-dir setups (
data_dir == user_dir, Windows today) are scanned once, never double-trained. Frontends that previously re-imported the training file afterdasher_createas a stopgap must gate that ondasher_capi_version() >= 1being false, or the same text will be counted twice.
var error: UnsafeMutablePointer<CChar>?
let ctx = dasher_create(bundlePath, docPath, &error)
dasher_set_screen_size(ctx, 800, 600)
dasher_set_output_callback(ctx, { eventType, text, userData in
// inject text into UITextView
}, nil)
// In display link callback:
dasher_mouse_move(ctx, Float(touch.x), Float(touch.y))
var cmds: UnsafeMutablePointer<Int32>?; var cmdCount: Int32 = 0
var strs: UnsafeMutablePointer<UnsafePointer<CChar>?>?; var strCount: Int32 = 0
dasher_frame(ctx, Int64(Date().timeIntervalSince1970 * 1000), &cmds, &cmdCount, &strs, &strCount)
// render with UIKit/CoreGraphicsval ctx = dasher_create(dataDir, userDir, null)
dasher_set_screen_size(ctx, width, height)
// In render loop:
dasher_mouse_move(ctx, x.toFloat(), y.toFloat())
val cmds = IntArray(10000)
val cmdCount = intArrayOf(0)
dasher_frame(ctx, System.currentTimeMillis(), cmds, cmdCount, null, null)
// render with Canvas APIdasher_set_screen_sizemust be called beforedasher_frame— it triggers engine initialization.out_command_countis total int count, not command count — divide by 6 for command count.- String pointers are ephemeral — copy immediately if you need the value beyond the current API call.
- Localization state is global — changing locale in one context affects all contexts (shared static state).
- Speed percent mapping — 100% =
LP_MAX_BITRATEof 160, clamped to the engine's declaredLP_MAX_BITRATErange (not a fixed percent cap). - Language model ID gap — IDs are 0, 2, 3, 4 (no ID 1). Historical.
- No font rendering — but text measurement is frontend-supplied — the engine never rasterises text; frontends draw
DASHER_CMD_TEXTcommands with a real font. Registerdasher_set_text_size_callbackso label layout uses that font's actual metrics; without it, width is estimated asutf8_codepoints × fontSize / 2(see Text Measurement Callback, issue #56). - Polygons are decomposed into line segments — no filled polygon opcode.
- Fully transparent elements are skipped — commands with alpha=0 are not emitted.