V implementaci API je teď app loop s FPS limitem, callbacky a helpery na kreslení textu, znaků, obdélníků a rámečku.\n\nJe tam i ochrana proti blikání VGA přes bezpečnější práci s barvami pozadí.
512 lines
12 KiB
C
512 lines
12 KiB
C
/*
|
|
* AsterOS Kernel
|
|
* Autor: Pavel Kalas
|
|
* Rok: 2026
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Tento soubor implementuje bridge mezi API pro aplikace a jadrem.
|
|
* Dnes funguje jako tenka vrstva nad kernel sluzbami a syscall API,
|
|
* aby bylo mozne na stejnem rozhrani pozdeji stavet user-space runtime.
|
|
*/
|
|
|
|
#include "aster_api.h"
|
|
#include "drivers.h"
|
|
#include "display.h"
|
|
#include "memory.h"
|
|
#include "process.h"
|
|
#include "scheduler.h"
|
|
#include "storage.h"
|
|
#include "string.h"
|
|
#include "syscall.h"
|
|
#include "timer.h"
|
|
|
|
static aster_api_dirent_cb g_list_cb = 0;
|
|
static void *g_list_user = 0;
|
|
static int g_app_should_close = 0;
|
|
|
|
static int clamp_i32(int v, int min_v, int max_v) {
|
|
if (v < min_v) {
|
|
return min_v;
|
|
}
|
|
if (v > max_v) {
|
|
return max_v;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
static void aster_api_list_bridge(const char *name, u8 is_dir, u16 size) {
|
|
aster_api_dirent_t entry;
|
|
usize len = 0;
|
|
|
|
if (!g_list_cb || !name) {
|
|
return;
|
|
}
|
|
|
|
aster_memset(&entry, 0, sizeof(entry));
|
|
while (name[len] && len + 1 < ASTERFS_NAME_LEN) {
|
|
entry.name[len] = name[len];
|
|
++len;
|
|
}
|
|
entry.name[len] = '\0';
|
|
entry.is_dir = is_dir;
|
|
entry.size = size;
|
|
g_list_cb(&entry, g_list_user);
|
|
}
|
|
|
|
int aster_api_context_init(aster_app_context_t *ctx) {
|
|
if (!ctx) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
ctx->api_version = ASTER_APP_API_VERSION;
|
|
ctx->capabilities = ASTER_API_CAP_CONSOLE |
|
|
ASTER_API_CAP_TIME |
|
|
ASTER_API_CAP_MEMORY |
|
|
ASTER_API_CAP_INPUT |
|
|
ASTER_API_CAP_STORAGE |
|
|
ASTER_API_CAP_PROCESS;
|
|
ctx->boot_ticks = (u64)timer_ticks();
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_print(const char *text) {
|
|
return (int)aster_write(text);
|
|
}
|
|
|
|
int aster_api_print_line(const char *text) {
|
|
int rc;
|
|
|
|
rc = aster_api_print(text ? text : "");
|
|
if (rc != 0) {
|
|
return rc;
|
|
}
|
|
|
|
return aster_api_print("\n");
|
|
}
|
|
|
|
int aster_api_clear(void) {
|
|
return (int)aster_clear();
|
|
}
|
|
|
|
int aster_api_set_color(u8 fg, u8 bg) {
|
|
display_set_color(fg, bg);
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_get_cursor(usize *out_row, usize *out_col) {
|
|
if (!out_row && !out_col) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
display_get_cursor(out_row, out_col);
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_set_cursor(usize row, usize col) {
|
|
display_set_cursor(row, col);
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_ticks(u64 *out_ticks) {
|
|
if (!out_ticks) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
*out_ticks = aster_api_ticks_now();
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
u64 aster_api_ticks_now(void) {
|
|
return aster_ticks();
|
|
}
|
|
|
|
int aster_api_sleep_ms(u32 ms) {
|
|
return (int)aster_sleep_ms((u64)ms);
|
|
}
|
|
|
|
void *aster_api_alloc(usize size) {
|
|
return aster_alloc(size);
|
|
}
|
|
|
|
void aster_api_memset(void *dst, u8 value, usize size) {
|
|
if (!dst || size == 0) {
|
|
return;
|
|
}
|
|
|
|
aster_memset(dst, (int)value, size);
|
|
}
|
|
|
|
void aster_api_memcpy(void *dst, const void *src, usize size) {
|
|
if (!dst || !src || size == 0) {
|
|
return;
|
|
}
|
|
|
|
aster_memcpy(dst, src, size);
|
|
}
|
|
|
|
int aster_api_memory_info(aster_api_memory_info_t *out_info) {
|
|
if (!out_info) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
out_info->total_pages = memory_total_pages();
|
|
out_info->free_pages = memory_free_pages();
|
|
out_info->used_pages = out_info->total_pages >= out_info->free_pages
|
|
? out_info->total_pages - out_info->free_pages
|
|
: 0;
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_read_key(void) {
|
|
return (int)aster_read_key();
|
|
}
|
|
|
|
int aster_api_try_read_key(void) {
|
|
return (int)aster_try_read_key();
|
|
}
|
|
|
|
int aster_api_read_line(char *buffer, int max_len) {
|
|
return (int)aster_read_line(buffer, (i64)max_len);
|
|
}
|
|
|
|
int aster_api_render(int x, int y, int width, int height, u8 color) {
|
|
char row_buf[ASTER_API_SCREEN_W + 1];
|
|
int i;
|
|
int yy;
|
|
int w;
|
|
int x0;
|
|
int y0;
|
|
int y1;
|
|
|
|
if (width <= 0 || height <= 0) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
x0 = clamp_i32(x, 0, ASTER_API_SCREEN_W - 1);
|
|
y0 = clamp_i32(y, 0, ASTER_API_SCREEN_H - 1);
|
|
y1 = clamp_i32(y + height - 1, 0, ASTER_API_SCREEN_H - 1);
|
|
|
|
if (x >= ASTER_API_SCREEN_W || y >= ASTER_API_SCREEN_H) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
w = width;
|
|
if (x0 + w > ASTER_API_SCREEN_W) {
|
|
w = ASTER_API_SCREEN_W - x0;
|
|
}
|
|
if (w <= 0) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
for (i = 0; i < w; ++i) {
|
|
row_buf[i] = ' ';
|
|
}
|
|
row_buf[w] = '\0';
|
|
|
|
for (yy = y0; yy <= y1; ++yy) {
|
|
display_write_at((usize)yy, (usize)x0, row_buf, 0x0F, color & 0x07);
|
|
}
|
|
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_render_char(int x, int y, char ch, u8 fg, u8 bg) {
|
|
char text[2];
|
|
|
|
if (x < 0 || y < 0 || x >= ASTER_API_SCREEN_W || y >= ASTER_API_SCREEN_H) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
text[0] = ch;
|
|
text[1] = '\0';
|
|
display_write_at((usize)y, (usize)x, text, fg & 0x0F, bg & 0x07);
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_render_text(int x, int y, const char *text, u8 font_color, u8 back_color) {
|
|
if (!text) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
if (x < 0 || y < 0 || x >= ASTER_API_SCREEN_W || y >= ASTER_API_SCREEN_H) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
display_write_at((usize)y, (usize)x, text, font_color & 0x0F, back_color & 0x07);
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_render_border(int x, int y, int width, int height, u8 fg, u8 bg) {
|
|
int xx;
|
|
int yy;
|
|
int right;
|
|
int bottom;
|
|
|
|
if (width < 2 || height < 2) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
right = x + width - 1;
|
|
bottom = y + height - 1;
|
|
|
|
for (xx = x; xx <= right; ++xx) {
|
|
(void)aster_api_render_char(xx, y, '-', fg, bg);
|
|
(void)aster_api_render_char(xx, bottom, '-', fg, bg);
|
|
}
|
|
|
|
for (yy = y; yy <= bottom; ++yy) {
|
|
(void)aster_api_render_char(x, yy, '|', fg, bg);
|
|
(void)aster_api_render_char(right, yy, '|', fg, bg);
|
|
}
|
|
|
|
(void)aster_api_render_char(x, y, '+', fg, bg);
|
|
(void)aster_api_render_char(right, y, '+', fg, bg);
|
|
(void)aster_api_render_char(x, bottom, '+', fg, bg);
|
|
(void)aster_api_render_char(right, bottom, '+', fg, bg);
|
|
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_file_create(const char *path) {
|
|
return asterfs_create_file(path);
|
|
}
|
|
|
|
int aster_api_dir_create(const char *path) {
|
|
return asterfs_create_dir(path);
|
|
}
|
|
|
|
int aster_api_file_remove(const char *path) {
|
|
return asterfs_remove_file(path);
|
|
}
|
|
|
|
int aster_api_dir_remove(const char *path) {
|
|
return asterfs_remove_dir(path);
|
|
}
|
|
|
|
int aster_api_path_exists(const char *path) {
|
|
return asterfs_get_type(path) >= 0 ? 1 : 0;
|
|
}
|
|
|
|
int aster_api_file_exists(const char *path) {
|
|
return asterfs_get_type(path) == 0 ? 1 : 0;
|
|
}
|
|
|
|
int aster_api_dir_exists(const char *path) {
|
|
return asterfs_get_type(path) == 1 ? 1 : 0;
|
|
}
|
|
|
|
int aster_api_file_read(const char *path, u8 *out, u16 max_len) {
|
|
return asterfs_read_file(path, out, max_len);
|
|
}
|
|
|
|
int aster_api_file_write(const char *path, const u8 *data, u16 len) {
|
|
return asterfs_write_file(path, data, len);
|
|
}
|
|
|
|
int aster_api_file_read_text(const char *path, char *out, u16 max_len) {
|
|
int n;
|
|
|
|
if (!out || max_len == 0) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
n = aster_api_file_read(path, (u8 *)out, (u16)(max_len - 1));
|
|
if (n < 0) {
|
|
out[0] = '\0';
|
|
return n;
|
|
}
|
|
|
|
out[n] = '\0';
|
|
return n;
|
|
}
|
|
|
|
int aster_api_file_write_text(const char *path, const char *text) {
|
|
usize len;
|
|
|
|
if (!text) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
len = aster_strlen(text);
|
|
if (len > 0xFFFFUL) {
|
|
len = 0xFFFFUL;
|
|
}
|
|
|
|
return aster_api_file_write(path, (const u8 *)text, (u16)len);
|
|
}
|
|
|
|
int aster_api_list_dir(const char *path, aster_api_dirent_cb cb, void *user) {
|
|
if (!path || !cb) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
g_list_cb = cb;
|
|
g_list_user = user;
|
|
asterfs_list_dir(path, aster_api_list_bridge);
|
|
g_list_cb = 0;
|
|
g_list_user = 0;
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_process_spawn(void (*entry)(void), const char *name, u8 priority) {
|
|
return (int)aster_process_create(entry, name, priority);
|
|
}
|
|
|
|
int aster_api_process_count(usize *out_count) {
|
|
if (!out_count) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
*out_count = process_count();
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_process_current(aster_api_process_info_t *out_info) {
|
|
process_t *p;
|
|
|
|
if (!out_info) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
p = process_current();
|
|
if (!p) {
|
|
return ASTER_API_ERR_NOT_FOUND;
|
|
}
|
|
|
|
out_info->pid = p->pid;
|
|
out_info->state = (u8)p->state;
|
|
out_info->priority = p->priority;
|
|
out_info->name = p->name;
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_process_get(usize index, aster_api_process_info_t *out_info) {
|
|
process_t *table;
|
|
usize count;
|
|
|
|
if (!out_info) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
count = process_count();
|
|
if (index >= count) {
|
|
return ASTER_API_ERR_NOT_FOUND;
|
|
}
|
|
|
|
table = process_table();
|
|
out_info->pid = table[index].pid;
|
|
out_info->state = (u8)table[index].state;
|
|
out_info->priority = table[index].priority;
|
|
out_info->name = table[index].name;
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
int aster_api_yield(void) {
|
|
scheduler_yield();
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
usize aster_api_str_len(const char *text) {
|
|
return aster_strlen(text);
|
|
}
|
|
|
|
int aster_api_str_equal(const char *a, const char *b) {
|
|
if (!a || !b) {
|
|
return 0;
|
|
}
|
|
|
|
return aster_strcmp(a, b) == 0 ? 1 : 0;
|
|
}
|
|
|
|
int aster_api_str_starts_with(const char *text, const char *prefix) {
|
|
usize n;
|
|
|
|
if (!text || !prefix) {
|
|
return 0;
|
|
}
|
|
|
|
n = aster_strlen(prefix);
|
|
return aster_strncmp(text, prefix, n) == 0 ? 1 : 0;
|
|
}
|
|
|
|
int aster_api_app_run(const aster_api_app_callbacks_t *callbacks, void *user, u32 target_fps) {
|
|
aster_api_app_state_t state;
|
|
u64 prev;
|
|
|
|
if (!callbacks) {
|
|
return ASTER_API_ERR_INVALID;
|
|
}
|
|
|
|
if (target_fps == 0) {
|
|
target_fps = 20;
|
|
}
|
|
|
|
g_app_should_close = 0;
|
|
|
|
aster_memset(&state, 0, sizeof(state));
|
|
state.running = 1;
|
|
state.target_fps = target_fps;
|
|
state.ticks_now = aster_api_ticks_now();
|
|
prev = state.ticks_now;
|
|
|
|
if (callbacks->initial) {
|
|
int rc = callbacks->initial(&state, user);
|
|
if (rc == ASTER_API_APP_CLOSE) {
|
|
state.running = 0;
|
|
} else if (rc == ASTER_API_APP_ERROR) {
|
|
return ASTER_API_APP_ERROR;
|
|
}
|
|
}
|
|
|
|
while (state.running && !g_app_should_close) {
|
|
int urc = ASTER_API_APP_CONTINUE;
|
|
int drc = ASTER_API_APP_CONTINUE;
|
|
u32 frame_ms;
|
|
|
|
state.ticks_now = aster_api_ticks_now();
|
|
state.dt_ticks = state.ticks_now - prev;
|
|
prev = state.ticks_now;
|
|
|
|
if (callbacks->update) {
|
|
urc = callbacks->update(&state, user);
|
|
if (urc == ASTER_API_APP_ERROR) {
|
|
state.running = 0;
|
|
break;
|
|
}
|
|
if (urc == ASTER_API_APP_CLOSE) {
|
|
state.running = 0;
|
|
}
|
|
}
|
|
|
|
if (state.running && callbacks->draw) {
|
|
drc = callbacks->draw(&state, user);
|
|
if (drc == ASTER_API_APP_ERROR) {
|
|
state.running = 0;
|
|
break;
|
|
}
|
|
if (drc == ASTER_API_APP_CLOSE) {
|
|
state.running = 0;
|
|
}
|
|
}
|
|
|
|
++state.frame;
|
|
frame_ms = (u32)(1000U / (state.target_fps ? state.target_fps : 1U));
|
|
if (frame_ms == 0) {
|
|
frame_ms = 1;
|
|
}
|
|
(void)aster_api_sleep_ms(frame_ms);
|
|
}
|
|
|
|
if (callbacks->closing) {
|
|
(void)callbacks->closing(&state, user);
|
|
}
|
|
|
|
return ASTER_API_OK;
|
|
}
|
|
|
|
void aster_api_app_request_close(void) {
|
|
g_app_should_close = 1;
|
|
}
|