do not edit — generated by btf.
git.druid.rocksindexmilitantprogrammerastral_canvassrc/lib/lib.c

src/lib/lib.c


#include "lib/lib.h"
#include "lib/error.h"
#include "lib/id.h"
#include "lib/queue.h"
#include "api/node.h"
 
#include <errno.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
 
typedef struct {
    pthread_t thread;
    int workerKnown;
    int threadLive;
 
    pthread_mutex_t stateMutex;
    pthread_cond_t stateCond;
    LibState state;
 
    JobQueue toWorker;
    JobQueue toMain;
 
    int running;
    int dirty;
    int width, height;
    char title[256];
    Backend backend;
    Mode mode;
    float scale;
 
    Platform platform;
    Renderer renderer;
    Style theme;
    int useVulkan;
} GlobalVars;
 
static GlobalVars G;
 
static int isWorker(void)
{
    return G.workerKnown && pthread_equal(pthread_self(), G.thread);
}
 
static int pushDirected(QueueDir dir, JobFn fn, void *data, int own)
{
    JobQueue *q;
    Job *job;
 
    if (dir == QUEUE_TO_WORKER) {
        if (isWorker()) {
            fprintf(stderr, "lib: worker cannot push toWorker (would loop)\n");
            if (own && data)
                free(data);
            return -1;
        }
        q = &G.toWorker;
    } else {
        if (!isWorker()) {
            fprintf(stderr, "lib: only the worker can push toMain\n");
            if (own && data)
                free(data);
            return -1;
        }
        q = &G.toMain;
    }
 
    job = jobCreate(fn, data, own);
    if (!job)
        return -1;
    if (queuePush(q, job) < 0) {
        jobFree(job);
        return -1;
    }
    return 0;
}
 
int libPushToWorker(JobFn fn, void *data, int own)
{
    return pushDirected(QUEUE_TO_WORKER, fn, data, own);
}
 
int libPushToMain(JobFn fn, void *data, int own)
{
    return pushDirected(QUEUE_TO_MAIN, fn, data, own);
}
 
static void runJob(Job *job)
{
    if (!job)
        return;
    if (job->fn)
        job->fn(job->data);
    jobFree(job);
}
 
int libPopMain(void)
{
    Job *job = queuePop(&G.toMain);
    if (!job)
        return -1;
    runJob(job);
    return 0;
}
 
int libMainWakeFd(void)
{
    return queueWakeFd(&G.toMain);
}
 
static void setState(LibState state)
{
    pthread_mutex_lock(&G.stateMutex);
    G.state = state;
    pthread_cond_broadcast(&G.stateCond);
    pthread_mutex_unlock(&G.stateMutex);
}
 
LibState libState(void)
{
    LibState state;
 
    pthread_mutex_lock(&G.stateMutex);
    state = G.state;
    pthread_mutex_unlock(&G.stateMutex);
    return state;
}
 
void libMarkDirty(void)
{
    G.dirty = 1;
}
 
Platform *libPlatform(void)
{
    return &G.platform;
}
 
Renderer *libRenderer(void)
{
    return &G.renderer;
}
 
static void jobQuit(void *data)
{
    (void)data;
    G.running = 0;
    G.platform.running = false;
}
 
static int pollWorker(int timeoutMs)
{
    Platform *platform = &G.platform;
    struct pollfd fds[2];
    nfds_t n = 0;
    int displayFd = platform->fileDescriptor ? platform->fileDescriptor(platform) : -1;
    int wakeFd = queueWakeFd(&G.toWorker);
    int rc;
 
    if (platform->flush)
        platform->flush(platform);
 
    if (displayFd >= 0) {
        fds[n].fd = displayFd;
        fds[n].events = POLLIN;
        n++;
    }
    if (wakeFd >= 0) {
        fds[n].fd = wakeFd;
        fds[n].events = POLLIN;
        n++;
    }
 
    rc = poll(fds, n, timeoutMs);
    if (rc < 0 && errno != EINTR)
        return -1;
 
    queueDrainWake(&G.toWorker);
 
    if (platform->pollEvents && platform->pollEvents(platform, 0) < 0)
        return -1;
    return 0;
}
 
static void processJobs(void)
{
    Job *job;
 
    while ((job = queuePop(&G.toWorker))) {
        runJob(job);
        G.dirty = 1;
    }
}
 
static void *libMain(void *arg)
{
    (void)arg;
    G.thread = pthread_self();
    G.workerKnown = 1;
 
    memset(&G.platform, 0, sizeof(G.platform));
    G.platform.backend = G.backend;
    G.platform.mode = G.mode;
    if (platformOpen(&G.platform, G.title, G.width, G.height) < 0) {
        canvasErrorSet("platform open failed within lib");
        setState(LIB_STATE_FAILED);
        return NULL;
    }
    if (G.scale != 0.0f && G.platform.scale <= 0)
        G.platform.scale = 1;
 
    if (rendererInit(&G.renderer, &G.platform) != 0) {
        if (!canvasErrorGet()[0])
            canvasErrorSet("Vulkan instance failed within lib");
        platformClose(&G.platform);
        setState(LIB_STATE_FAILED);
        return NULL;
    }
    G.useVulkan = 1;
 
    if (nodeGraphInit() != 0) {
        canvasErrorSet("widget tree init failed within lib");
        rendererDestroy(&G.renderer);
        platformClose(&G.platform);
        setState(LIB_STATE_FAILED);
        return NULL;
    }
    nodeApplyStyle(nodeRoot(), &G.theme);
 
    G.running = 1;
    G.dirty = 1;
    setState(LIB_STATE_READY);
 
    while (G.running && G.platform.running) {
        if (pollWorker(G.dirty ? 0 : -1) < 0)
            break;
 
        processJobs();
 
        {
            Event event;
            while (platformPop(&G.platform, &event)) {
                G.dirty = 1;
                if (event.kind == EVENT_QUIT)
                    G.running = 0;
            }
        }
 
        if (G.dirty && G.running && G.platform.running) {
            Hud hud;
            rendererFillClear(&G.platform, &G.theme, G.mode, &hud);
            if (rendererFrame(&G.renderer, &G.platform, &hud) < 0)
                break;
            G.dirty = 0;
        }
    }
 
    nodeGraphDestroy();
    if (G.useVulkan)
        rendererDestroy(&G.renderer);
    platformClose(&G.platform);
    setState(LIB_STATE_STOPPED);
    return NULL;
}
 
static void libCleanup(void)
{
    queueDestroy(&G.toWorker);
    queueDestroy(&G.toMain);
    idPoolDestroy();
    pthread_mutex_destroy(&G.stateMutex);
    pthread_cond_destroy(&G.stateCond);
    memset(&G, 0, sizeof(G));
}
 
static Backend mapBackend(A_Backend backend)
{
    switch (backend) {
    case A_BACKEND_X11:
        return BACKEND_X11;
    case A_BACKEND_WAYLAND:
        return BACKEND_WAYLAND;
    default:
        return BACKEND_AUTO;
    }
}
 
static Mode mapMode(A_Mode mode)
{
    switch (mode) {
    case A_MODE_SIMPLE:
        return MODE_SIMPLE;
    case A_MODE_TEXT:
        return MODE_TEXT;
    default:
        return MODE_COMPLEX;
    }
}
 
int libStart(int width, int height, const char *title, const CanvasOptions *opt)
{
    LibState state;
    int rc;
    CanvasOptions defaults = {
        .backend = A_BACKEND_AUTO,
        .mode = A_MODE_COMPLEX,
        .scale = 0.0f,
        .theme = {
            .backgroundColor = {16, 18, 24, 255},
            .foregroundColor = {40, 44, 52, 255},
            .textColor = {220, 224, 232, 255},
            .fontSize = 14,
        },
    };
 
    canvasErrorClear();
    if (G.threadLive) {
        canvasErrorSet("A_Init failed within lib: already running");
        return -1;
    }
 
    if (!opt)
        opt = &defaults;
 
    memset(&G, 0, sizeof(G));
    G.toWorker.wake_fd = -1;
    G.toMain.wake_fd = -1;
    G.width = width > 0 ? width : 800;
    G.height = height > 0 ? height : 480;
    G.backend = mapBackend(opt->backend);
    G.mode = mapMode(opt->mode);
    G.scale = opt->scale;
    G.theme = opt->theme;
    if (G.theme.backgroundColor.a == 0 && G.theme.backgroundColor.r == 0 &&
        G.theme.backgroundColor.g == 0 && G.theme.backgroundColor.b == 0)
        G.theme = defaults.theme;
    snprintf(G.title, sizeof(G.title), "%s", title ? title : "window");
 
    if (pthread_mutex_init(&G.stateMutex, NULL) != 0)
        return -1;
    if (pthread_cond_init(&G.stateCond, NULL) != 0) {
        pthread_mutex_destroy(&G.stateMutex);
        return -1;
    }
    if (idPoolInit() < 0) {
        pthread_mutex_destroy(&G.stateMutex);
        pthread_cond_destroy(&G.stateCond);
        return -1;
    }
    if (queueInit(&G.toWorker) < 0 || queueInit(&G.toMain) < 0) {
        libCleanup();
        return -1;
    }
 
    G.state = LIB_STATE_STARTING;
    rc = pthread_create(&G.thread, NULL, libMain, NULL);
    if (rc != 0) {
        libCleanup();
        return rc;
    }
    G.threadLive = 1;
 
    pthread_mutex_lock(&G.stateMutex);
    while (G.state == LIB_STATE_STARTING)
        pthread_cond_wait(&G.stateCond, &G.stateMutex);
    state = G.state;
    pthread_mutex_unlock(&G.stateMutex);
 
    if (state != LIB_STATE_READY) {
        pthread_join(G.thread, NULL);
        G.threadLive = 0;
        libCleanup();
        return -1;
    }
    return 0;
}
 
void libWait(void)
{
    if (!G.threadLive)
        return;
    pthread_join(G.thread, NULL);
    G.threadLive = 0;
    libCleanup();
}
 
int libIsOpen(void)
{
    LibState state = libState();
    return G.threadLive && (state == LIB_STATE_READY || state == LIB_STATE_BUSY);
}
 
void libStop(void)
{
    if (!G.threadLive)
        return;
    libPushToWorker(jobQuit, NULL, 0);
    pthread_join(G.thread, NULL);
    G.threadLive = 0;
    libCleanup();
}
powered by btf.