How config.c and boxpreferences.c Manage Persistent User Configurations Across Platforms in ArmorPaint
ArmorPaint persists user settings via JSON-based configuration files stored in platform-specific directories, using config.c for global application preferences and boxpreferences.c for UI layout states, both leveraging the Kinc abstraction layer for cross-platform file I/O.
ArmorPaint implements a robust, platform-agnostic configuration system that preserves user data across Windows, macOS, and Linux sessions. The architecture relies on two dedicated C modules located in paint/sources/config.c and paint/sources/boxpreferences.c to handle persistent storage. These modules utilize the Kinc framework's path and file utilities to ensure settings remain consistent regardless of the underlying operating system.
Platform-Aware Storage Locations
Both configuration modules determine the appropriate filesystem location at runtime using kinc_path_user() and kinc_path_join() from the Kinc library. This abstraction eliminates hardcoded paths and ensures compliance with each platform's user data conventions.
Windows Configuration Paths
On Windows, ArmorPaint stores configuration data in the %APPDATA%\ArmorPaint\ directory. The config_init() function constructs this path by joining the result of kinc_path_user() with "ArmorPaint" and appending the filename.
- Global config:
%APPDATA%\ArmorPaint\config.json - UI preferences:
%APPDATA%\ArmorPaint\preferences.json(managed by boxpreferences.c)
macOS and Linux Configuration Paths
Unix-like platforms follow the XDG Base Directory specification and macOS application support conventions:
| Platform | Configuration Directory | File |
|---|---|---|
| macOS | ~/Library/Application Support/ArmorPaint/ |
config.json |
| Linux | ~/.config/armorpaint/ |
config.json |
The same kinc_path_join() logic applies, with the directory component adjusted per platform during compilation.
JSON-Based Serialization Architecture
Both modules use a lightweight JSON parser (json.h/json.c) to serialize and deserialize configuration data. The schema includes a version field ("version": 2) to enable safe migration of older configuration files when the application updates.
Saving Configuration Data
The config_save() and boxpreferences_save() functions populate JSON objects with current state values and write them using kinc_file_save_bytes():
void config_save(void) {
// Build JSON object with current settings
json_object *root = json_object_new_object();
json_object_object_add(root, "version", json_object_new_int(2));
json_object_object_add(root, "brush_size", json_object_new_double(brush_size));
const char *json_str = json_object_to_json_string(root);
kinc_file_save_bytes(config_path, (unsigned char *)json_str, strlen(json_str));
json_object_put(root);
}
Loading Configuration Data
On startup, config_load() and boxpreferences_load() read existing files via kinc_file_load_bytes(), parse the JSON, and populate internal structures:
void config_load(void) {
unsigned char *data = kinc_file_load_bytes(config_path, &size);
if (data) {
json_object *root = json_tokener_parse((char *)data);
json_object *version;
if (json_object_object_get_ex(root, "version", &version)) {
// Migrate or load based on version number
}
json_object_put(root);
free(data);
}
}
Separation of Concerns in Configuration Modules
The codebase deliberately splits responsibilities between the two modules to maintain clean architecture:
-
paint/sources/config.c: Manages global application state including default brush parameters, recent projects list, last opened directory, and GPU backend selection. This module initializes early inmain()viaconfig_init(). -
paint/sources/boxpreferences.c: Handles dockable UI panel states specifically. It stores each panel's position, dimensions, and collapsed/expanded status. The module saves immediately when users drag or resize panels throughboxpreferences_save().
Both modules expose parallel APIs:
// Global configuration API
void config_init(void);
void config_load(void);
void config_save(void);
// UI preferences API
void boxpreferences_init(void);
void boxpreferences_load(void);
void boxpreferences_save(void);
Cross-Platform Abstraction via Kinc
All filesystem operations route through Kinc functions to ensure portability:
kinc_path_user(): Returns the base user data directory appropriate for the current platformkinc_path_join(): Safely concatenates path components with correct directory separatorskinc_file_load_bytes()/kinc_file_save_bytes(): Handle raw binary I/O without platform-specific file handles
This abstraction allows identical source code to compile and execute on Windows, macOS, and Linux without conditional compilation blocks in the configuration logic itself.
Thread Safety and Error Handling
To prevent race conditions when the UI thread writes preferences while the main thread reads them, both modules protect file I/O with a global mutex:
static kinc_mutex_t config_mutex;
void config_save(void) {
kinc_mutex_lock(&config_mutex);
// ... file operations ...
kinc_mutex_unlock(&config_mutex);
}
Error handling follows a defensive pattern: if kinc_file_load_bytes() returns NULL (missing file) or JSON parsing fails, the modules log the error and initialize default values. The application then writes a fresh configuration file on the next save operation, ensuring corrupted or deleted configs never crash the program.
Summary
- config.c and boxpreferences.c in
paint/sources/implement ArmorPaint's persistent storage using JSON serialization. - Storage paths are determined at runtime using
kinc_path_user()andkinc_path_join(), yielding%APPDATA%\ArmorPaint\on Windows and~/.config/armorpaint/on Linux. - The system uses a versioned JSON schema (currently version 2) to support configuration migration between application updates.
- File I/O operations are abstracted through Kinc functions (
kinc_file_load_bytes,kinc_file_save_bytes) for cross-platform compatibility. - A global mutex (
config_mutex) ensures thread-safe access to configuration files during concurrent UI and main thread operations.
Frequently Asked Questions
Where does ArmorPaint store its configuration files on Windows?
ArmorPaint stores configuration files in %APPDATA%\ArmorPaint\config.json, resolved at runtime using kinc_path_user() joined with the application name. This path appears in paint/sources/config.c where the global config_path variable is initialized during config_init().
How does ArmorPaint handle corrupted or missing configuration files?
When config_load() or boxpreferences_load() encounters a missing file or invalid JSON, the functions fall back to hardcoded default values and log the error. The application continues normally and regenerates a valid configuration file on the next save operation, ensuring user workflow interruption is minimal.
What is the difference between config.c and boxpreferences.c?
config.c manages global application settings such as brush defaults, recent projects, and hardware preferences, while boxpreferences.c specifically tracks UI panel geometry and visibility states. The former saves primarily on application shutdown, whereas the latter triggers saves immediately when users manipulate dockable panels.
Is the ArmorPaint configuration format versioned for upgrades?
Yes, both configuration modules write a "version": 2 field into their JSON output. The loading functions check this version number to determine if migration logic is required, allowing newer application versions to safely upgrade older configuration files without losing user data.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →