aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-02 14:43:09 +0200
committeruser <user@clank>2026-09-02 14:43:09 +0200
commit18d1aef0f9bf4620135fd9f77f26683e6f2f8c00 (patch)
treebf3ea334988ce03f61705ba9628941c175c1ee38 /src
parentweb: the real site is the only site, and web/publish ships it (diff)
downloadslosh-18d1aef0f9bf4620135fd9f77f26683e6f2f8c00.tar.gz
slosh-18d1aef0f9bf4620135fd9f77f26683e6f2f8c00.tar.xz
slosh-18d1aef0f9bf4620135fd9f77f26683e6f2f8c00.zip
shaders: remove the .so plugin loader
The static musl release builds cannot dlopen -- musl compiles it out -- so loadable shader plugins were a feature most shipped binaries could not use. Nothing we ship used it either: every shader in contrib/shaders is an expression. Gone: the dlopen/glob loader in shader.c, shader_dir in the config, the plugin ABI table and entry macro, the Windows dl*/glob shims that existed only for it, the contrib/shader-plugin skeleton, its tests, and every doc mention. shader_abi.h stays as the shader model (cell_t, shade_ctx_t, shader_t) with a note about what it used to be.
Diffstat (limited to 'src')
-rw-r--r--src/compat_win.c88
-rw-r--r--src/config.c37
-rw-r--r--src/shader.c148
3 files changed, 2 insertions, 271 deletions
diff --git a/src/compat_win.c b/src/compat_win.c
index fcce416..7b5b914 100644
--- a/src/compat_win.c
+++ b/src/compat_win.c
@@ -603,94 +603,6 @@ int sl_spawn_detached(const char *const argv[], const char *logpath) {
return (int)pi.dwProcessId;
}
-/* ---- dynamic loading ----------------------------------------------------- */
-
-static char g_dlerr[256];
-
-void *sl_dlopen(const char *path, int flags) {
- (void)flags;
- HMODULE h = LoadLibraryA(path);
- if (!h)
- snprintf(g_dlerr, sizeof g_dlerr, "LoadLibrary failed (%lu)",
- (unsigned long)GetLastError());
- return (void *)h;
-}
-void *sl_dlsym(void *lib, const char *sym) {
- FARPROC p = GetProcAddress((HMODULE)lib, sym);
- if (!p) snprintf(g_dlerr, sizeof g_dlerr, "no symbol %s", sym);
- return (void *)(uintptr_t)p;
-}
-int sl_dlclose(void *lib) { return FreeLibrary((HMODULE)lib) ? 0 : -1; }
-const char *sl_dlerror(void) { return g_dlerr[0] ? g_dlerr : NULL; }
-
-/* ---- glob ---------------------------------------------------------------- */
-
-/* Only what shader.c asks for: one directory, one wildcard in the last
- * component. FindFirstFile does the matching. */
-int sl_glob(const char *pat, int flags, int (*errfn)(const char *, int),
- glob_t *g) {
- (void)flags;
- (void)errfn;
- if (!g) return GLOB_NOMATCH;
- memset(g, 0, sizeof *g);
-
- char dir[PATH_MAX];
- snprintf(dir, sizeof dir, "%s", pat);
- char *slash = strrchr(dir, '/');
- char *bslash = strrchr(dir, '\\');
- if (bslash > slash) slash = bslash;
- if (slash)
- *slash = 0;
- else
- dir[0] = 0;
-
- WIN32_FIND_DATAA fd;
- HANDLE h = FindFirstFileA(pat, &fd);
- if (h == INVALID_HANDLE_VALUE) return GLOB_NOMATCH;
-
- size_t cap = 16, n = 0;
- char **v = (char **)malloc(cap * sizeof *v);
- if (!v) {
- FindClose(h);
- return GLOB_NOMATCH;
- }
- do {
- if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0)
- continue;
- if (n + 1 >= cap) {
- cap *= 2;
- char **nv = (char **)realloc(v, cap * sizeof *v);
- if (!nv) break;
- v = nv;
- }
- char full[PATH_MAX];
- if (dir[0])
- snprintf(full, sizeof full, "%s/%s", dir, fd.cFileName);
- else
- snprintf(full, sizeof full, "%s", fd.cFileName);
- v[n] = _strdup(full);
- if (v[n]) n++;
- } while (FindNextFileA(h, &fd));
- FindClose(h);
-
- if (n == 0) {
- free(v);
- return GLOB_NOMATCH;
- }
- v[n] = NULL;
- g->gl_pathc = n;
- g->gl_pathv = v;
- return 0;
-}
-
-void sl_globfree(glob_t *g) {
- if (!g || !g->gl_pathv) return;
- for (size_t i = 0; i < g->gl_pathc; i++) free(g->gl_pathv[i]);
- free(g->gl_pathv);
- g->gl_pathv = NULL;
- g->gl_pathc = 0;
-}
-
/* ---- console ------------------------------------------------------------- */
static DWORD g_in_mode = 0, g_out_mode = 0;
diff --git a/src/config.c b/src/config.c
index f85ccb1..0c7af63 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1169,7 +1169,6 @@ void config_free(config_t *c) {
free(c->binds);
free(c->shell);
free(c->editor);
- free(c->shader_dir);
free(c->project_layout);
for (size_t i = 0; i < c->nproject_roots; i++) free(c->project_roots[i]);
for (size_t i = 0; i < c->nfiles; i++) free(c->files[i]);
@@ -1655,10 +1654,6 @@ char *config_render(const config_t *c) {
cb_qstr(&b, "editor", c->editor, NULL);
else
cb_add(&b, "// editor \"nvim\" // unset: $EDITOR, then vi\n");
- if (c->shader_dir)
- cb_qstr(&b, "shader_dir", c->shader_dir, NULL);
- else
- cb_add(&b, "// shader_dir \"~/.config/slosh/shaders\"\n");
if (c->nproject_roots) {
cb_add(&b, "project_roots");
for (size_t i = 0; i < c->nproject_roots; i++) {
@@ -1878,7 +1873,6 @@ static const char *const KNOWN_TOP[] = {
"scrollback",
"scrollback_bytes",
"select_scroll_ms",
- "shader_dir",
"shaders",
"shell",
"splash_ms",
@@ -2214,37 +2208,6 @@ static bool load_into(config_t *c, const char *path, int depth, char *err,
c->editor = strdup(ed);
}
- /* Shader plugins, before any shader is named below: a `shaders` block may
- * use what one of these adds, and a name is looked up as it is parsed. The
- * default is a directory beside this file, so dropping a `.so` next to the
- * config is the whole installation procedure. */
- const char *sdir = kdl_arg(kdl_child(root, "shader_dir"), 0, NULL);
- if (sdir) {
- free(c->shader_dir);
- c->shader_dir = strdup(sdir);
- }
- {
- char dir[1024];
- if (c->shader_dir) {
- char buf[1024];
- snprintf(dir, sizeof dir, "%s",
- path_expand(c->shader_dir, buf, sizeof buf));
- } else {
- snprintf(dir, sizeof dir, "%s", path);
- char *slash = strrchr(dir, '/');
- if (slash)
- slash[1] = 0;
- else
- dir[0] = 0;
- snprintf(dir + strlen(dir), sizeof dir - strlen(dir), "shaders");
- }
- char lerr[256] = {0};
- shader_load_dir(dir, lerr, sizeof lerr);
- /* A plugin that will not load is worth a line, and worth nothing more:
- * the config it came with still works, minus that effect (D9). */
- if (lerr[0]) complain(c, err, errcap, 0, "%s", lerr);
- }
-
/* Where projects live. Several roots because people have `~/dev` and `~/work`,
* and a `depth` property rather than a second node because it is one small
* fixed record -- the same shape `min_pane cols=24 rows=6` uses.
diff --git a/src/shader.c b/src/shader.c
index 2cd5375..e6414cc 100644
--- a/src/shader.c
+++ b/src/shader.c
@@ -1,10 +1,7 @@
-/* The shader pass, the built-in shaders, and loading more of them from disk.
- * See shader.h for the model and shader_abi.h for what a plugin sees. */
+/* The shader pass and the built-in shaders. See shader.h for the model. */
#define _GNU_SOURCE
#include "shader.h"
-#include <dlfcn.h>
-#include <glob.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -163,28 +160,9 @@ static const shader_def_t REGISTRY[] = {
#define NBUILTIN (sizeof REGISTRY / sizeof *REGISTRY)
-/* ---- loaded shaders -----------------------------------------------------
- *
- * Kept beside the built-ins rather than merged into them: a built-in is a
- * fixed table the compiler can put in .rodata, and this is a list that grows
- * at runtime. Lookup asks the built-ins first, so a plugin cannot shadow
- * `dim` and change what every existing config means.
- *
- * Both the name and the function belong to the loaded library, which is why
- * nothing here is ever freed or unloaded: a shader_t copied into a config or
- * onto a pane holds both pointers. */
-static struct {
- shader_def_t *defs;
- size_t n, cap;
- char **paths; /* what has been loaded, so a reload does not load it twice */
- size_t npaths, pathcap;
-} LOADED;
-
static const shader_def_t *find_kind(const char *kind) {
for (size_t i = 0; i < NBUILTIN; i++)
if (strcmp(REGISTRY[i].name, kind) == 0) return &REGISTRY[i];
- for (size_t i = 0; i < LOADED.n; i++)
- if (strcmp(LOADED.defs[i].name, kind) == 0) return &LOADED.defs[i];
return NULL;
}
@@ -209,129 +187,7 @@ bool shader_make_p(shader_t *out, const char *kind, color_t color,
}
const char *shader_kind(size_t i) {
- if (i < NBUILTIN) return REGISTRY[i].name;
- i -= NBUILTIN;
- return i < LOADED.n ? LOADED.defs[i].name : NULL;
-}
-
-/* ---- loading ------------------------------------------------------------ */
-
-static bool already_loaded(const char *path) {
- for (size_t i = 0; i < LOADED.npaths; i++)
- if (strcmp(LOADED.paths[i], path) == 0) return true;
- return false;
-}
-
-static void remember_path(const char *path) {
- if (LOADED.npaths == LOADED.pathcap) {
- LOADED.pathcap = LOADED.pathcap ? LOADED.pathcap * 2 : 8;
- LOADED.paths = realloc(LOADED.paths, LOADED.pathcap * sizeof *LOADED.paths);
- }
- LOADED.paths[LOADED.npaths++] = strdup(path);
-}
-
-/* One library. Everything that can be wrong with it is checked before a
- * single one of its shaders is registered, so a plugin is all-or-nothing:
- * half a bundle is harder to explain than none of it. */
-static size_t load_one(const char *path, char *err, size_t errcap) {
- void *lib = dlopen(path, RTLD_NOW | RTLD_LOCAL);
- if (!lib) {
- if (err && !err[0])
- snprintf(err, errcap, "%s: %s", path, dlerror() ? dlerror() : "dlopen");
- return 0;
- }
-
- /* An object pointer is not a function pointer in ISO C, and dlsym returns
- * the former. POSIX requires the conversion to work; the cast through a
- * union is how it is written without the compiler being right to warn. */
- union {
- void *obj;
- const shader_plugin_t *(*fn)(void);
- } entry = {.obj = dlsym(lib, SLOSH_SHADER_PLUGIN_SYM)};
-
- if (!entry.obj) {
- if (err && !err[0])
- snprintf(err, errcap, "%s: no " SLOSH_SHADER_PLUGIN_SYM, path);
- dlclose(lib);
- return 0;
- }
-
- const shader_plugin_t *t = entry.fn();
- if (!t || t->abi != SLOSH_SHADER_ABI) {
- if (err && !err[0])
- snprintf(err, errcap, "%s: shader ABI %u, we speak %u", path,
- t ? t->abi : 0, SLOSH_SHADER_ABI);
- dlclose(lib);
- return 0;
- }
- /* The version says which contract; the sizes say whether the plugin really
- * has that contract's structs. They differ when someone edits their copy of
- * shader_abi.h, which a version number alone reports as fine and a running
- * session reports as garbled colours or a crash. */
- if (t->cell_size != sizeof(cell_t) || t->ctx_size != sizeof(shade_ctx_t) ||
- t->shader_size != sizeof(shader_t)) {
- if (err && !err[0])
- snprintf(err, errcap, "%s: built against different structs", path);
- dlclose(lib);
- return 0;
- }
- if (t->count && !t->shaders) {
- if (err && !err[0]) snprintf(err, errcap, "%s: empty shader table", path);
- dlclose(lib);
- return 0;
- }
-
- for (size_t i = 0; i < t->count; i++) {
- const shader_def_t *d = &t->shaders[i];
- if (!d->name || !*d->name || !d->fn) {
- if (err && !err[0]) snprintf(err, errcap, "%s: nameless shader", path);
- continue;
- }
- if (find_kind(d->name)) {
- /* Refused rather than replacing: a config naming `dim` means the `dim`
- * it was written against, and a plugin that could redefine it would
- * change effects nobody asked it to touch. */
- if (err && !err[0])
- snprintf(err, errcap, "%s: shader %s already exists", path, d->name);
- continue;
- }
- if (LOADED.n == LOADED.cap) {
- LOADED.cap = LOADED.cap ? LOADED.cap * 2 : 8;
- LOADED.defs = realloc(LOADED.defs, LOADED.cap * sizeof *LOADED.defs);
- }
- LOADED.defs[LOADED.n++] = *d;
- }
-
- /* The library stays mapped for the life of the session, on purpose: see
- * shader_load_dir()'s comment in the header. */
- return t->count;
-}
-
-size_t shader_load_dir(const char *dir, char *err, size_t errcap) {
- if (err && errcap) err[0] = 0;
- if (!dir || !*dir) return 0;
-
- char pattern[1024];
- snprintf(pattern, sizeof pattern, "%s/*.so", dir);
-
- glob_t g = {0};
- int rc = glob(pattern, 0, NULL, &g);
- if (rc != 0) {
- globfree(&g);
- return 0; /* no directory, or nothing in it: the normal case */
- }
-
- size_t added = 0;
- for (size_t i = 0; i < g.gl_pathc; i++) {
- if (already_loaded(g.gl_pathv[i])) continue;
- /* Remembered whether or not it loads. A library that fails today fails
- * every reload, and saying so once is a warning while saying so on every
- * save is a broken editor. */
- remember_path(g.gl_pathv[i]);
- added += load_one(g.gl_pathv[i], err, errcap);
- }
- globfree(&g);
- return added;
+ return i < NBUILTIN ? REGISTRY[i].name : NULL;
}
/* ---- the pass ----------------------------------------------------------- */