#include "internal.h" #include "version.h" #include "3rdparty/tinydir.h" #include "3rdparty/cdl.h" #include #include #include static struct list renderers; static void bm_renderer_free(struct bm_renderer *renderer) { assert(renderer); if (renderer->handle) chckDlUnload(renderer->handle); free(renderer->name); free(renderer->file); free(renderer); } static bool load(const char *file, struct bm_renderer *renderer) { void *handle; const char *error = NULL; if (!(handle = chckDlLoad(file, &error))) goto load_fail; const char* (*regfun)(struct render_api*); if (!(regfun = chckDlLoadSymbol(handle, "register_renderer", &error))) goto load_fail; const char *name; if (!(name = regfun(&renderer->api))) goto fail; if (strcmp(renderer->api.version, BM_PLUGIN_VERSION)) goto mismatch_fail; renderer->handle = handle; renderer->name = bm_strdup(name); renderer->file = bm_strdup(file); return true; load_fail: fprintf(stderr, "%s\n", error); goto fail; mismatch_fail: fprintf(stderr, "%s: version mismatch (%s != %s)\n", name, renderer->api.version, BM_PLUGIN_VERSION); fail: if (handle) chckDlUnload(handle); return false; } static int compare(const void *a, const void *b) { const struct bm_renderer *ra = *(struct bm_renderer**)a, *rb = *(struct bm_renderer**)b; return (ra->api.prioritory > rb->api.prioritory); } static bool load_to_list(const char *file) { struct bm_renderer *renderer; if (!(renderer = calloc(1, sizeof(struct bm_renderer)))) goto fail; if (!load(file, renderer)) goto fail; chckDlUnload(renderer->handle); if (!list_add_item(&renderers, renderer)) goto fail; list_sort(&renderers, compare); return true; fail: if (renderer) bm_renderer_free(renderer); return false; } bool bm_renderer_activate(struct bm_renderer *renderer, struct bm_menu *menu) { assert(renderer); if (!load(renderer->file, renderer)) return false; menu->renderer = renderer; if (!renderer->api.constructor(menu)) goto fail; return true; fail: chckDlUnload(renderer->handle); menu->renderer = NULL; return false; } bool bm_init(void) { static const char *rpath = INSTALL_PREFIX "/lib/bemenu"; const char *path = getenv("BEMENU_RENDERER"); if (path) return load_to_list(path); path = getenv("BEMENU_RENDERERS"); if (!path || access(path, R_OK) == -1) path = rpath; tinydir_dir dir; if (tinydir_open(&dir, path) == -1) goto fail; size_t registered = 0; while (dir.has_next) { tinydir_file file; memset(&file, 0, sizeof(file)); tinydir_readfile(&dir, &file); if (!file.is_dir && !strncmp(file.name, "bemenu-renderer-", strlen("bemenu-renderer-"))) { char *fpath; if ((fpath = bm_dprintf("%s/%s", path, file.name))) { if (load_to_list(fpath)) registered++; free(fpath); } } tinydir_next(&dir); } tinydir_close(&dir); return (registered > 0 ? true : false); fail: return false; } const struct bm_renderer** bm_get_renderers(uint32_t *out_nmemb) { assert(out_nmemb); return list_get_items(&renderers, out_nmemb); } const char* bm_version(void) { return BM_VERSION; } const char* bm_renderer_get_name(const struct bm_renderer *renderer) { assert(renderer); return renderer->name; } enum bm_prioritory bm_renderer_get_prioritory(const struct bm_renderer *renderer) { assert(renderer); return renderer->api.prioritory; } /* vim: set ts=8 sw=4 tw=0 :*/