diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-11 21:53:42 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-11 21:53:42 +0300 |
| commit | 927ae621c9a3d46dbc401b8e9ea7bbed1bf2e97b (patch) | |
| tree | aeb00c16770dcf5a36d28c1167cc39c6759059fb /src/common | |
initial: Yet Another Surface Library (YASL) 0.1.0
A minimal, modern desktop window surface library in C.
Supports Wayland on Linux and Cocoa/Metal on macOS with
presentation timing, fractional scaling, IME, and CSD.
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/loop.c | 124 | ||||
| -rw-r--r-- | src/common/timing.c | 69 | ||||
| -rw-r--r-- | src/common/utf8.c | 82 |
3 files changed, 275 insertions, 0 deletions
diff --git a/src/common/loop.c b/src/common/loop.c new file mode 100644 index 0000000..4c2da0d --- /dev/null +++ b/src/common/loop.c @@ -0,0 +1,124 @@ +#define _POSIX_C_SOURCE 200809L +#include "yasl/yasl.h" + +#include <errno.h> +#include <poll.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#define YASL_MAX_FDS 64 + +typedef int (*yasl_loop_fd_fn)(int fd, short revents, void *ud); + +struct yasl_fd_source { + yasl_loop_fd_fn fn; + void *ud; + int id; + int fd; + short events; +}; + +struct yasl_loop { + struct yasl_fd_source fds[YASL_MAX_FDS]; + struct pollfd pollfds[YASL_MAX_FDS]; + int count; + int next_id; + bool running; +}; + +uint64_t yasl_loop_now_ns(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec; +} + +struct yasl_loop *yasl_loop_create(void) +{ + struct yasl_loop *l = calloc(1, sizeof(*l)); + if (!l) return NULL; + l->next_id = 1; + return l; +} + +void yasl_loop_destroy(struct yasl_loop *l) +{ + free(l); +} + +int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events, + int (*fn)(int fd, short revents, void *ud), void *ud) +{ + if (!l || l->count >= YASL_MAX_FDS || fd < 0) + return -1; + int id = l->next_id++; + l->fds[l->count] = (struct yasl_fd_source){ + .fn = fn, .ud = ud, .id = id, .fd = fd, .events = events, + }; + l->pollfds[l->count] = (struct pollfd){ + .fd = fd, .events = events, .revents = 0, + }; + l->count++; + return id; +} + +int yasl_loop_del_fd(struct yasl_loop *l, int id) +{ + if (!l || id <= 0) return -1; + for (int i = 0; i < l->count; i++) { + if (l->fds[i].id == id) { + l->count--; + l->fds[i] = l->fds[l->count]; + l->pollfds[i] = l->pollfds[l->count]; + return 0; + } + } + return -1; +} + +int yasl_loop_set_fd_events(struct yasl_loop *l, int id, short events) +{ + if (!l || id <= 0) return -1; + for (int i = 0; i < l->count; i++) { + if (l->fds[i].id == id) { + l->fds[i].events = events; + l->pollfds[i].events = events; + return 0; + } + } + return -1; +} + +void yasl_loop_kick(struct yasl_loop *l) +{ + (void)l; +} + +int yasl_loop_run(struct yasl_loop *l) +{ + if (!l) return -1; + l->running = true; + while (l->running) { + int n = poll(l->pollfds, (nfds_t)l->count, -1); + if (n < 0) { + if (errno == EINTR) continue; + return -1; + } + for (int i = 0; i < l->count && l->running; i++) { + if (l->pollfds[i].revents) { + struct yasl_fd_source src = l->fds[i]; + short revents = l->pollfds[i].revents; + l->pollfds[i].revents = 0; + if (src.fn) + src.fn(src.fd, revents, src.ud); + } + } + } + return 0; +} + +void yasl_loop_quit(struct yasl_loop *l) +{ + if (l) l->running = false; +} diff --git a/src/common/timing.c b/src/common/timing.c new file mode 100644 index 0000000..b5e342f --- /dev/null +++ b/src/common/timing.c @@ -0,0 +1,69 @@ +#include "yasl/timing.h" + +uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns) +{ + uint64_t now_ms = now_ns / 1000000; + int64_t ahead = (int32_t)(ms - (uint32_t)now_ms); + if (ahead < 0 && (uint64_t)-ahead > now_ms) + return (uint64_t)ms * 1000000; + return (now_ms + (uint64_t)ahead) * 1000000; +} + +void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample) +{ + if (c->last_present_ns) { + double dt = (double)(now - c->last_present_ns); + if (sample && dt < 250e6) + c->refresh_ns += 0.125 * (dt - c->refresh_ns); + } + c->last_present_ns = now; +} + +void yasl_clock_note_resize(struct yasl_frame_clock *c, uint64_t dt_ns) +{ + c->resize_count++; + c->resize_sum_ns += dt_ns; + if (dt_ns > c->resize_max_ns) + c->resize_max_ns = dt_ns; +} + +void yasl_clock_resize_stats(const struct yasl_frame_clock *c, + struct yasl_resize_stats *out) +{ + out->count = c->resize_count; + out->avg_us = c->resize_count + ? (double)c->resize_sum_ns / 1e3 / (double)c->resize_count + : 0.0; + out->max_us = (double)c->resize_max_ns / 1e3; + out->minflt = c->resize_minflt; +} + +double yasl_clock_refresh_ms(const struct yasl_frame_clock *c) +{ + return c->refresh_ns / 1e6; +} + +uint64_t yasl_clock_next_present_ns(const struct yasl_frame_clock *c) +{ + return c->last_present_ns + ? c->last_present_ns + (uint64_t)c->refresh_ns + : 0; +} + +uint64_t yasl_clock_frame_timestamp_ns(struct yasl_frame_clock *c, uint64_t now) +{ + if (!c->last_present_ns || c->refresh_ns <= 0.0) { + c->frame_time_ns = now; + return now; + } + double k = ((double)now - (double)c->last_present_ns) / c->refresh_ns + 0.5; + uint64_t grid = c->last_present_ns + + (uint64_t)((uint64_t)(k > 0.0 ? k : 0.0) * c->refresh_ns); + if (!c->frame_time_ns) { + c->frame_time_ns = grid; + return grid; + } + uint64_t t = grid > c->frame_time_ns ? grid : c->frame_time_ns; + c->frame_time_ns = t; + return t; +} diff --git a/src/common/utf8.c b/src/common/utf8.c new file mode 100644 index 0000000..089fd89 --- /dev/null +++ b/src/common/utf8.c @@ -0,0 +1,82 @@ +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <stdint.h> + +bool yasl_utf8_validate(const char *str, size_t len) +{ + const unsigned char *p = (const unsigned char *)str; + const unsigned char *end = p + len; + + while (p < end) { + if (*p < 0x80) { + p++; + } else if ((*p & 0xe0) == 0xc0) { + if (p + 1 >= end || (p[1] & 0xc0) != 0x80 || (*p & 0x1e) == 0) + return false; + p += 2; + } else if ((*p & 0xf0) == 0xe0) { + if (p + 2 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80) + return false; + uint32_t cp = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); + if (cp < 0x800 || (cp >= 0xd800 && cp <= 0xdfff)) + return false; + p += 3; + } else if ((*p & 0xf8) == 0xf0) { + if (p + 3 >= end || (p[1] & 0xc0) != 0x80 || (p[2] & 0xc0) != 0x80 || (p[3] & 0xc0) != 0x80) + return false; + uint32_t cp = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f); + if (cp < 0x10000 || cp > 0x10ffff) + return false; + p += 4; + } else { + return false; + } + } + return true; +} + +char *yasl_utf8_sanitize_dup(const char *str) +{ + if (!str) return NULL; + size_t len = strlen(str); + char *out = malloc(len + 1); + if (!out) return NULL; + + const unsigned char *p = (const unsigned char *)str; + size_t o = 0; + while (*p) { + if (*p < 0x80) { + out[o++] = (char)*p++; + } else if ((*p & 0xe0) == 0xc0 && (p[1] & 0xc0) == 0x80 && (*p & 0x1e)) { + out[o++] = (char)*p++; + out[o++] = (char)*p++; + } else if ((*p & 0xf0) == 0xe0 && (p[1] & 0xc0) == 0x80 && (p[2] & 0xc0) == 0x80) { + uint32_t cp = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f); + if (cp >= 0x800 && !(cp >= 0xd800 && cp <= 0xdfff)) { + out[o++] = (char)*p++; + out[o++] = (char)*p++; + out[o++] = (char)*p++; + } else { + out[o++] = '?'; + p++; + } + } else if ((*p & 0xf8) == 0xf0 && (p[1] & 0xc0) == 0x80 && (p[2] & 0xc0) == 0x80 && (p[3] & 0xc0) == 0x80) { + uint32_t cp = ((p[0] & 0x07) << 18) | ((p[1] & 0x3f) << 12) | ((p[2] & 0x3f) << 6) | (p[3] & 0x3f); + if (cp >= 0x10000 && cp <= 0x10ffff) { + out[o++] = (char)*p++; + out[o++] = (char)*p++; + out[o++] = (char)*p++; + out[o++] = (char)*p++; + } else { + out[o++] = '?'; + p++; + } + } else { + out[o++] = '?'; + p++; + } + } + out[o] = '\0'; + return out; +} |
