feat: add keyboard layout support

This commit is contained in:
2026-08-13 17:57:44 +02:00
parent 86e3ebfe33
commit 821543238d
36 changed files with 1132 additions and 125 deletions
+81 -71
View File
@@ -1,7 +1,8 @@
/*
* config.h
* Config file manager for MontaukOS programs
* Loads, modifies, and saves TOML config files from 0:/config/
* Loads, modifies, and saves TOML config files from 0:/config/ and
* 0:/users/<name>/config/, and reads OS data tables from 0:/os/data/
* Copyright (c) 2026 Daniel Hammer
*/
@@ -187,29 +188,13 @@ namespace config {
// ---- File operations ----
// Ensure the config directory exists
inline void ensure_dir() {
montauk::fmkdir(CONFIG_DIR);
}
// Build full path: "0:/config/<name>.toml"
inline void build_path(char* out, int outSz, const char* name) {
int p = 0;
const char* dir = CONFIG_DIR;
while (*dir && p < outSz - 2) out[p++] = *dir++;
out[p++] = '/';
while (*name && p < outSz - 6) out[p++] = *name++;
// Append ".toml"
const char* ext = ".toml";
while (*ext && p < outSz - 1) out[p++] = *ext++;
out[p] = '\0';
}
// Load a config file by name (without extension).
// Returns an initialized Doc (empty if file doesn't exist).
inline toml::Doc load(const char* name) {
char path[128];
build_path(path, sizeof(path), name);
// ---- Shared file I/O (absolute path in, Doc out) ----
// Only the path builders below know where each class of file lives. These
// two do the actual work for system config, per-user config and OS data.
// Read and parse a TOML file at an absolute path.
// Returns an initialized Doc (empty if the file is missing or empty).
inline toml::Doc load_path(const char* path) {
int handle = montauk::open(path);
if (handle < 0) {
toml::Doc doc;
@@ -235,15 +220,9 @@ namespace config {
return doc;
}
// Save a Doc to disk as a TOML file.
// Creates the file if it doesn't exist.
// Returns 0 on success, negative on error.
inline int save(const char* name, toml::Doc* doc) {
ensure_dir();
char path[128];
build_path(path, sizeof(path), name);
// Serialize a Doc and write it to an absolute path. The caller creates the
// parent directory. Returns 0 on success, negative on error.
inline int save_path(const char* path, toml::Doc* doc) {
char* text = serialize(doc);
int textLen = montauk::slen(text);
@@ -262,6 +241,42 @@ namespace config {
return ret < 0 ? ret : 0;
}
inline void ensure_dir() {
montauk::fmkdir(CONFIG_DIR);
}
// Build full path: "0:/config/<name>.toml"
inline void build_path(char* out, int outSz, const char* name) {
int p = 0;
const char* dir = CONFIG_DIR;
while (*dir && p < outSz - 2) out[p++] = *dir++;
out[p++] = '/';
while (*name && p < outSz - 6) out[p++] = *name++;
// Append ".toml"
const char* ext = ".toml";
while (*ext && p < outSz - 1) out[p++] = *ext++;
out[p] = '\0';
}
// Load a config file by name (without extension).
// Returns an initialized Doc (empty if file doesn't exist).
inline toml::Doc load(const char* name) {
char path[128];
build_path(path, sizeof(path), name);
return load_path(path);
}
// Save a Doc to disk as a TOML file.
// Creates the file if it doesn't exist.
// Returns 0 on success, negative on error.
inline int save(const char* name, toml::Doc* doc) {
ensure_dir();
char path[128];
build_path(path, sizeof(path), name);
return save_path(path, doc);
}
// ---- Per-user config ----
// Build path: "0:/users/<username>/config/<name>.toml"
@@ -298,30 +313,7 @@ namespace config {
inline toml::Doc load_user(const char* username, const char* name) {
char path[192];
build_user_path(path, sizeof(path), username, name);
int handle = montauk::open(path);
if (handle < 0) {
toml::Doc doc;
doc.init();
return doc;
}
uint64_t size = montauk::getsize(handle);
if (size == 0) {
montauk::close(handle);
toml::Doc doc;
doc.init();
return doc;
}
char* text = (char*)montauk::malloc(size + 1);
montauk::read(handle, (uint8_t*)text, 0, size);
montauk::close(handle);
text[size] = '\0';
toml::Doc doc = toml::parse(text);
montauk::mfree(text);
return doc;
return load_path(path);
}
// Save a per-user config file
@@ -330,21 +322,7 @@ namespace config {
char path[192];
build_user_path(path, sizeof(path), username, name);
char* text = serialize(doc);
int textLen = montauk::slen(text);
montauk::fdelete(path);
int handle = montauk::fcreate(path);
if (handle < 0) {
montauk::mfree(text);
return -1;
}
int ret = montauk::fwrite(handle, (const uint8_t*)text, 0, textLen);
montauk::close(handle);
montauk::mfree(text);
return ret < 0 ? ret : 0;
return save_path(path, doc);
}
// Delete a config file. Returns 0 on success.
@@ -426,4 +404,36 @@ namespace config {
}
} // namespace config
// ---- Read-only OS data tables ----
// Reference data that ships with the OS and is never written back: keyboard
// layouts, time zone tables and the like. This lives under 0:/os because it is
// OS payload, not configuration -- 0:/config is for state a user or admin
// edits. There is deliberately no save() here, so the read-only nature of the
// directory is enforced by the API rather than by convention.
namespace data {
static constexpr const char* DATA_DIR = "0:/os/data";
// Build full path: "0:/os/data/<name>.toml"
inline void build_path(char* out, int outSz, const char* name) {
int p = 0;
const char* dir = DATA_DIR;
while (*dir && p < outSz - 2) out[p++] = *dir++;
out[p++] = '/';
while (*name && p < outSz - 6) out[p++] = *name++;
const char* ext = ".toml";
while (*ext && p < outSz - 1) out[p++] = *ext++;
out[p] = '\0';
}
// Load an OS data table by name (without extension).
// Returns an initialized Doc (empty if the file is missing).
inline toml::Doc load(const char* name) {
char path[128];
build_path(path, sizeof(path), name);
return config::load_path(path);
}
} // namespace data
} // namespace montauk