From 014c20099d0199445cedc6e3504e4309176c56ae Mon Sep 17 00:00:00 2001
From: Jari Vetoniemi <mailroxas@gmail.com>
Date: Wed, 22 Oct 2014 22:46:51 +0300
Subject: Refactor code to snake_style, turn renderers into plugins, and start
 wayland (cairo) renderer.

---
 CMakeLists.txt                      |   5 +-
 README.md                           |  12 -
 client/client.c                     |  98 +++---
 lib/3rdparty/cdl.c                  |  96 ++++++
 lib/3rdparty/cdl.h                  |  10 +
 lib/3rdparty/tinydir.h              | 548 ++++++++++++++++++++++++++++++++++
 lib/CMakeLists.txt                  |   5 +-
 lib/bemenu.h                        | 339 +++++++++++----------
 lib/draw/curses.c                   | 498 -------------------------------
 lib/filter.c                        | 141 +++++----
 lib/internal.h                      | 172 ++++++-----
 lib/item.c                          |  76 ++---
 lib/library.c                       | 152 +++++++++-
 lib/list.c                          | 121 ++++----
 lib/menu.c                          | 577 ++++++++++++------------------------
 lib/renderers/CMakeLists.txt        |  10 +
 lib/renderers/curses/CMakeLists.txt |   6 +
 lib/renderers/curses/curses.c       | 379 +++++++++++++++++++++++
 lib/util.c                          | 133 +++++----
 lib/version.h.in                    |   1 +
 test/CMakeLists.txt                 |   4 +-
 test/bmMenuNew.c                    |  20 +-
 test/bm_menu_new.c                  |  35 +++
 23 files changed, 2006 insertions(+), 1432 deletions(-)
 create mode 100644 lib/3rdparty/cdl.c
 create mode 100644 lib/3rdparty/cdl.h
 create mode 100644 lib/3rdparty/tinydir.h
 delete mode 100644 lib/draw/curses.c
 create mode 100644 lib/renderers/CMakeLists.txt
 create mode 100644 lib/renderers/curses/CMakeLists.txt
 create mode 100644 lib/renderers/curses/curses.c
 create mode 100644 test/bm_menu_new.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index ac22069..86f8b2e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,10 @@ INCLUDE(CTest)
 SET(BEMENU_NAME "bemenu")
 SET(BEMENU_DESCRIPTION "Dynamic menu library and client program inspired by dmenu")
 SET(BEMENU_VERSION "1.0.0")
-SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${chck_SOURCE_DIR}/CMake/modules)
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${bemenu_SOURCE_DIR}/CMake)
+
+# C99
+ADD_DEFINITIONS(-std=c99)
 
 # Compile library
 ADD_SUBDIRECTORY(lib)
diff --git a/README.md b/README.md
index 476c8c9..2dc6cf7 100644
--- a/README.md
+++ b/README.md
@@ -7,15 +7,3 @@ Dynamic menu library and client program inspired by dmenu
 * [GNU GPLv3 (or any later version)](LICENSE-CLIENT) for client program[s] and
   other sources except library and bindings
 * [GNU LGPLv3 (or any later version)](LICENSE-LIB) for library and bindings
-
-## Project Guidelines
-* **Coding style**
-	* [Linux kernel coding style](https://www.kernel.org/doc/Documentation/CodingStyle)
-	  for C sources with following exceptions:
-	  * spaces not tabs
-	  * indentation size is 4 characters (spaces)
-	  * function and variable names are camelCase except for global constants
-	* [Standard style](http://legacy.python.org/dev/peps/pep-0008/) for Python
-* **Build system** - [CMake](http://www.cmake.org/)
-* **API documentation** - [Doxygen](http://www.stack.nl/~dimitri/doxygen/index.html)
-  (JavaDoc style comments)
diff --git a/client/client.c b/client/client.c
index 54906c4..def8978 100644
--- a/client/client.c
+++ b/client/client.c
@@ -9,7 +9,7 @@
 #include <bemenu.h>
 
 static struct {
-    bmFilterMode filterMode;
+    enum bm_filter_mode filter_mode;
     int wrap;
     unsigned int lines;
     const char *title;
@@ -18,17 +18,18 @@ static struct {
     int grab;
     int monitor;
 } client = {
-    BM_FILTER_MODE_DMENU, /* filterMode */
-    0, /* wrap */
-    0, /* lines */
-    "bemenu", /* title */
-    0, /* selected */
-    0, /* bottom */
-    0, /* grab */
-    0 /* monitor */
+    .filter_mode = BM_FILTER_MODE_DMENU,
+    .wrap = 0,
+    .lines = 0,
+    .title = "bemenu",
+    .selected = 0,
+    .bottom = 0,
+    .grab = 0,
+    .monitor = 0
 };
 
-static void discoTrap(int sig)
+static void
+disco_trap(int sig)
 {
     (void)sig;
     printf("\e[?25h\n");
@@ -36,11 +37,12 @@ static void discoTrap(int sig)
     exit(EXIT_FAILURE);
 }
 
-static void disco(void)
+static void
+disco(void)
 {
     struct sigaction action;
     memset(&action, 0, sizeof(struct sigaction));
-    action.sa_handler = discoTrap;
+    action.sa_handler = disco_trap;
     sigaction(SIGABRT, &action, NULL);
     sigaction(SIGSEGV, &action, NULL);
     sigaction(SIGTRAP, &action, NULL);
@@ -62,14 +64,16 @@ static void disco(void)
     exit(EXIT_SUCCESS);
 }
 
-static void version(const char *name)
+static void
+version(const char *name)
 {
     char *base = strrchr(name, '/');
-    printf("%s v%s\n", (base ? base  + 1 : name), bmVersion());
+    printf("%s v%s\n", (base ? base  + 1 : name), bm_version());
     exit(EXIT_SUCCESS);
 }
 
-static void usage(FILE *out, const char *name)
+static void
+usage(FILE *out, const char *name)
 {
     char *base = strrchr(name, '/');
     fprintf(out, "usage: %s [options]\n", (base ? base + 1 : name));
@@ -89,15 +93,16 @@ static void usage(FILE *out, const char *name)
           " -b, --bottom          appears at the bottom of the screen. ()\n"
           " -f, --grab            grabs the keyboard before reading stdin. ()\n"
           " -m, --monitor         index of monitor where menu will appear. ()\n"
-          " -fn, --fn             defines the font to be used. ()\n"
-          " -nb, --nb             defines the normal background color. ()\n"
-          " -nf, --nf             defines the normal foreground color. ()\n"
-          " -sb, --sb             defines the selected background color. ()\n"
-          " -sf, --sf             defines the selected foreground color. ()\n", out);
+          " --fn                  defines the font to be used. ()\n"
+          " --nb                  defines the normal background color. ()\n"
+          " --nf                  defines the normal foreground color. ()\n"
+          " --sb                  defines the selected background color. ()\n"
+          " --sf                  defines the selected foreground color. ()\n", out);
     exit((out == stderr ? EXIT_FAILURE : EXIT_SUCCESS));
 }
 
-static void parseArgs(int *argc, char **argv[])
+static void
+parse_args(int *argc, char **argv[])
 {
     static const struct option opts[] = {
         { "help",        no_argument,       0, 'h' },
@@ -140,7 +145,7 @@ static void parseArgs(int *argc, char **argv[])
                 break;
 
             case 'i':
-                client.filterMode = BM_FILTER_MODE_DMENU_CASE_INSENSITIVE;
+                client.filter_mode = BM_FILTER_MODE_DMENU_CASE_INSENSITIVE;
                 break;
             case 'w':
                 client.wrap = 1;
@@ -188,7 +193,8 @@ static void parseArgs(int *argc, char **argv[])
     *argv += optind;
 }
 
-static void readItemsToMenuFromStdin(bmMenu *menu)
+static void
+read_items_to_menu_from_stdin(struct bm_menu *menu)
 {
     assert(menu);
 
@@ -215,54 +221,58 @@ static void readItemsToMenuFromStdin(bmMenu *menu)
         size_t next = pos + (s[pos] != 0);
         s[pos] = 0;
 
-        bmItem *item = bmItemNew(s);
-        if (!item)
+        struct bm_item *item;
+        if (!(item = bm_item_new(s)))
             break;
 
-        bmMenuAddItem(menu, item);
+        bm_menu_add_item(menu, item);
         s += next;
     }
 
     free(buffer);
 }
 
-int main(int argc, char **argv)
+int
+main(int argc, char **argv)
 {
-    parseArgs(&argc, &argv);
+    if (!bm_init())
+        return EXIT_FAILURE;
+
+    parse_args(&argc, &argv);
 
-    bmMenu *menu = bmMenuNew(BM_DRAW_MODE_CURSES);
-    if (!menu)
+    struct bm_menu *menu;
+    if (!(menu = bm_menu_new(NULL)))
         return EXIT_FAILURE;
 
-    bmMenuSetTitle(menu, client.title);
-    bmMenuSetFilterMode(menu, client.filterMode);
-    bmMenuSetWrap(menu, client.wrap);
+    bm_menu_set_title(menu, client.title);
+    bm_menu_set_filter_mode(menu, client.filter_mode);
+    bm_menu_set_wrap(menu, client.wrap);
 
-    readItemsToMenuFromStdin(menu);
+    read_items_to_menu_from_stdin(menu);
 
-    bmMenuSetHighlightedIndex(menu, client.selected);
+    bm_menu_set_highlighted_index(menu, client.selected);
 
-    bmKey key;
+    enum bm_key key;
     unsigned int unicode;
     int status = 0;
     do {
-        bmMenuRender(menu);
-        key = bmMenuGetKey(menu, &unicode);
-    } while ((status = bmMenuRunWithKey(menu, key, unicode)) == BM_RUN_RESULT_RUNNING);
+        bm_menu_render(menu);
+        key = bm_menu_poll_key(menu, &unicode);
+    } while ((status = bm_menu_run_with_key(menu, key, unicode)) == BM_RUN_RESULT_RUNNING);
 
     if (status == BM_RUN_RESULT_SELECTED) {
         unsigned int i, count;
-        bmItem **items = bmMenuGetSelectedItems(menu, &count);
+        struct bm_item **items = bm_menu_get_selected_items(menu, &count);
         for (i = 0; i < count; ++i) {
-            const char *text = bmItemGetText(items[i]);
+            const char *text = bm_item_get_text(items[i]);
             printf("%s\n", (text ? text : ""));
         }
 
-        if (!count && bmMenuGetFilter(menu))
-            printf("%s\n", bmMenuGetFilter(menu));
+        if (!count && bm_menu_get_filter(menu))
+            printf("%s\n", bm_menu_get_filter(menu));
     }
 
-    bmMenuFree(menu);
+    bm_menu_free(menu);
     return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE);
 }
 
diff --git a/lib/3rdparty/cdl.c b/lib/3rdparty/cdl.c
new file mode 100644
index 0000000..d7a226a
--- /dev/null
+++ b/lib/3rdparty/cdl.c
@@ -0,0 +1,96 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifndef size_t
+#  include <stddef.h>
+#endif
+
+#if defined(_WIN32) || defined(_WIN64)
+#  include <windows.h>
+#elif defined(__posix__) || defined(__unix__) || defined(__linux__) || defined(__APPLE__)
+#  include <dlfcn.h>
+#else
+#  warning "cdl: unsupported os"
+#endif
+
+void* chckDlLoad(const char *file, const char **outError)
+{
+   assert(file);
+   void *handle = NULL;
+   const char *error = NULL;
+
+#if defined(_WIN32) || defined(_WIN64)
+#if defined(__WINRT__)
+   /**
+    * WinRT only publically supports LoadPackagedLibrary() for loading .dll
+    * files. LoadLibrary() is a private API, and not available for apps
+    * (that can be published to MS' Windows Store.)
+    */
+   handle = (void*)LoadPackagedLibrary(file, 0);
+#else
+   handle = (void*)LoadLibrary(file);
+#endif
+
+   if (!handle)
+      error = "Failed to load dll file.";
+#elif defined(__posix__) || defined(__unix__) || defined(__linux__) || defined(__APPLE__)
+   if (!(handle = dlopen(file, RTLD_NOW | RTLD_LOCAL)))
+      error = dlerror();
+#else
+   error = "cdl: unsupported os";
+#endif
+
+   if (outError)
+      *outError = error;
+
+   return handle;
+}
+
+void* chckDlLoadSymbol(void *handle, const char *name, const char **outError)
+{
+   assert(handle);
+   void *symbol = NULL;
+   const char *error = NULL;
+
+#if defined(_WIN32) || defined(_WIN64)
+   if (!(symbol = (void*)GetProcAddress((HMODULE)handle, name)))
+      error = "Failed to load symbol.";
+#elif defined(__posix__) || defined(__unix__) || defined(__linux__) || defined(__APPLE__)
+   if (!(symbol = dlsym(handle, name))) {
+      size_t len = strlen(name) + 1;
+      char *nname = calloc(1, len + 1);
+
+      if (nname) {
+         /* append an underscore for platforms that need that. */
+         nname[0] = '_';
+         memcpy(nname + 1, name, len);
+         symbol = dlsym(handle, nname);
+         free(nname);
+      }
+   }
+
+   if (!symbol)
+      error = dlerror();
+#else
+   error = "cdl: unsupported os";
+#endif
+
+   if (outError)
+      *outError = error;
+
+   return symbol;
+}
+
+void chckDlUnload(void *handle)
+{
+   assert(handle);
+
+#if defined(_WIN32) || defined(_WIN64)
+   FreeLibrary((HMODULE)handle);
+#elif defined(__posix__) || defined(__unix__) || defined(__linux__) || defined(__APPLE__)
+   dlclose(handle);
+#endif
+}
+
+/* vim: set ts=8 sw=3 tw=0 :*/
diff --git a/lib/3rdparty/cdl.h b/lib/3rdparty/cdl.h
new file mode 100644
index 0000000..9255654
--- /dev/null
+++ b/lib/3rdparty/cdl.h
@@ -0,0 +1,10 @@
+#ifndef __chck_cdl__
+#define __chck_cdl__
+
+void* chckDlLoad(const char *file, const char **outError);
+void* chckDlLoadSymbol(void *handle, const char *name, const char **outError);
+void chckDlUnload(void *handle);
+
+#endif /* __chck_cdl__ */
+
+/* vim: set ts=8 sw=3 tw=0 :*/
diff --git a/lib/3rdparty/tinydir.h b/lib/3rdparty/tinydir.h
new file mode 100644
index 0000000..db11e8e
--- /dev/null
+++ b/lib/3rdparty/tinydir.h
@@ -0,0 +1,548 @@
+/*
+Copyright (c) 2013-2014, Cong Xu, Baudouin Feildel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef TINYDIR_H
+#define TINYDIR_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#pragma warning (disable : 4996)
+#else
+#include <dirent.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#endif
+
+
+/* types */
+
+#define _TINYDIR_PATH_MAX 4096
+#ifdef _MSC_VER
+/* extra chars for the "\\*" mask */
+#define _TINYDIR_PATH_EXTRA 2
+#else
+#define _TINYDIR_PATH_EXTRA 0
+#endif
+#define _TINYDIR_FILENAME_MAX 256
+
+#ifdef _MSC_VER
+#define _TINYDIR_FUNC static __inline
+#else
+#define _TINYDIR_FUNC static __inline__
+#endif
+
+typedef struct
+{
+	char path[_TINYDIR_PATH_MAX];
+	char name[_TINYDIR_FILENAME_MAX];
+	char *extension;
+	int is_dir;
+	int is_reg;
+
+#ifdef _MSC_VER
+#else
+	struct stat _s;
+#endif
+} tinydir_file;
+
+typedef struct
+{
+	char path[_TINYDIR_PATH_MAX];
+	int has_next;
+	size_t n_files;
+
+	tinydir_file *_files;
+#ifdef _MSC_VER
+	HANDLE _h;
+	WIN32_FIND_DATA _f;
+#else
+	DIR *_d;
+	struct dirent *_e;
+#endif
+} tinydir_dir;
+
+
+/* declarations */
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const char *path);
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const char *path);
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir);
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir);
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
+
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file);
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b);
+
+
+/* definitions*/
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const char *path)
+{
+	if (dir == NULL || path == NULL || strlen(path) == 0)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	/* initialise dir */
+	dir->_files = NULL;
+#ifdef _MSC_VER
+	dir->_h = INVALID_HANDLE_VALUE;
+#else
+	dir->_d = NULL;
+#endif
+	tinydir_close(dir);
+
+	strcpy(dir->path, path);
+#ifdef _MSC_VER
+	strcat(dir->path, "\\*");
+	dir->_h = FindFirstFile(dir->path, &dir->_f);
+	dir->path[strlen(dir->path) - 2] = '\0';
+	if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+	dir->_d = opendir(path);
+	if (dir->_d == NULL)
+#endif
+	{
+		errno = ENOENT;
+		goto bail;
+	}
+
+	/* read first file */
+	dir->has_next = 1;
+#ifndef _MSC_VER
+	dir->_e = readdir(dir->_d);
+	if (dir->_e == NULL)
+	{
+		dir->has_next = 0;
+	}
+#endif
+
+	return 0;
+
+bail:
+	tinydir_close(dir);
+	return -1;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const char *path)
+{
+	/* Count the number of files first, to pre-allocate the files array */
+	size_t n_files = 0;
+	if (tinydir_open(dir, path) == -1)
+	{
+		return -1;
+	}
+	while (dir->has_next)
+	{
+		n_files++;
+		if (tinydir_next(dir) == -1)
+		{
+			goto bail;
+		}
+	}
+	tinydir_close(dir);
+
+	if (tinydir_open(dir, path) == -1)
+	{
+		return -1;
+	}
+
+	dir->n_files = 0;
+	dir->_files = (tinydir_file *)malloc(sizeof *dir->_files * n_files);
+	if (dir->_files == NULL)
+	{
+		errno = ENOMEM;
+		goto bail;
+	}
+	while (dir->has_next)
+	{
+		tinydir_file *p_file;
+		dir->n_files++;
+
+		p_file = &dir->_files[dir->n_files - 1];
+		if (tinydir_readfile(dir, p_file) == -1)
+		{
+			goto bail;
+		}
+
+		if (tinydir_next(dir) == -1)
+		{
+			goto bail;
+		}
+
+		/* Just in case the number of files has changed between the first and
+		second reads, terminate without writing into unallocated memory */
+		if (dir->n_files == n_files)
+		{
+			break;
+		}
+	}
+
+	qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
+
+	return 0;
+
+bail:
+	tinydir_close(dir);
+	return -1;
+}
+
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir)
+{
+	if (dir == NULL)
+	{
+		return;
+	}
+
+	memset(dir->path, 0, sizeof(dir->path));
+	dir->has_next = 0;
+	dir->n_files = 0;
+	if (dir->_files != NULL)
+	{
+		free(dir->_files);
+	}
+	dir->_files = NULL;
+#ifdef _MSC_VER
+	if (dir->_h != INVALID_HANDLE_VALUE)
+	{
+		FindClose(dir->_h);
+	}
+	dir->_h = INVALID_HANDLE_VALUE;
+#else
+	if (dir->_d)
+	{
+		closedir(dir->_d);
+	}
+	dir->_d = NULL;
+	dir->_e = NULL;
+#endif
+}
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir)
+{
+	if (dir == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (!dir->has_next)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+#ifdef _MSC_VER
+	if (FindNextFile(dir->_h, &dir->_f) == 0)
+#else
+	dir->_e = readdir(dir->_d);
+	if (dir->_e == NULL)
+#endif
+	{
+		dir->has_next = 0;
+#ifdef _MSC_VER
+		if (GetLastError() != ERROR_SUCCESS &&
+			GetLastError() != ERROR_NO_MORE_FILES)
+		{
+			tinydir_close(dir);
+			errno = EIO;
+			return -1;
+		}
+#endif
+	}
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
+{
+	if (dir == NULL || file == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+#ifdef _MSC_VER
+	if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+	if (dir->_e == NULL)
+#endif
+	{
+		errno = ENOENT;
+		return -1;
+	}
+	if (strlen(dir->path) +
+		strlen(
+#ifdef _MSC_VER
+			dir->_f.cFileName
+#else
+			dir->_e->d_name
+#endif
+		) + 1 + _TINYDIR_PATH_EXTRA >=
+		_TINYDIR_PATH_MAX)
+	{
+		/* the path for the file will be too long */
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+	if (strlen(
+#ifdef _MSC_VER
+			dir->_f.cFileName
+#else
+			dir->_e->d_name
+#endif
+		) >= _TINYDIR_FILENAME_MAX)
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	strcpy(file->path, dir->path);
+	strcat(file->path, "/");
+	strcpy(file->name,
+#ifdef _MSC_VER
+		dir->_f.cFileName
+#else
+		dir->_e->d_name
+#endif
+	);
+	strcat(file->path, file->name);
+#ifndef _MSC_VER
+	if (stat(file->path, &file->_s) == -1)
+	{
+		return -1;
+	}
+#endif
+	_tinydir_get_ext(file);
+
+	file->is_dir =
+#ifdef _MSC_VER
+		!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#else
+		S_ISDIR(file->_s.st_mode);
+#endif
+	file->is_reg =
+#ifdef _MSC_VER
+		!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+		(
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
+#endif
+#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
+#endif
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
+			!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
+#else
+		S_ISREG(file->_s.st_mode);
+#endif
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
+{
+	if (dir == NULL || file == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (i >= dir->n_files)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+	memcpy(file, &dir->_files[i], sizeof(tinydir_file));
+	_tinydir_get_ext(file);
+
+	return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
+{
+	char path[_TINYDIR_PATH_MAX];
+	if (dir == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (i >= dir->n_files || !dir->_files[i].is_dir)
+	{
+		errno = ENOENT;
+		return -1;
+	}
+
+	strcpy(path, dir->_files[i].path);
+	tinydir_close(dir);
+	if (tinydir_open_sorted(dir, path) == -1)
+	{
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Open a single file given its path */
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const char *path)
+{
+	tinydir_dir dir;
+	int result = 0;
+	int found = 0;
+	char dir_name_buf[_TINYDIR_PATH_MAX];
+	char file_name_buf[_TINYDIR_FILENAME_MAX];
+	char *dir_name;
+	char *base_name;
+#ifdef _MSC_VER
+	char drive_buf[_TINYDIR_PATH_MAX];
+	char ext_buf[_TINYDIR_FILENAME_MAX];
+#endif
+	
+	if (file == NULL || path == NULL || strlen(path) == 0)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	if (strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	/* Get the parent path */
+#ifdef _MSC_VER
+	if (_splitpath_s(
+			path,
+			drive_buf, sizeof drive_buf,
+			dir_name_buf, sizeof dir_name_buf,
+			file_name_buf, sizeof file_name_buf,
+			ext_buf, sizeof ext_buf))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+	/* Concatenate the drive letter and dir name to form full dir name */
+	strcat(drive_buf, dir_name_buf);
+	dir_name = drive_buf;
+	/* Concatenate the file name and extension to form base name */
+	strcat(file_name_buf, ext_buf);
+	base_name = file_name_buf;
+#else
+	strcpy(dir_name_buf, path);
+	dir_name = dirname(dir_name_buf);
+	strcpy(file_name_buf, path);
+	base_name = basename(file_name_buf);
+#endif
+	
+	/* Open the parent directory */
+	if (tinydir_open(&dir, dir_name) == -1)
+	{
+		return -1;
+	}
+
+	/* Read through the parent directory and look for the file */
+	while (dir.has_next)
+	{
+		if (tinydir_readfile(&dir, file) == -1)
+		{
+			result = -1;
+			goto bail;
+		}
+		if (strcmp(file->name, base_name) == 0)
+		{
+			/* File found */
+			found = 1;
+			goto bail;
+		}
+		tinydir_next(&dir);
+	}
+	if (!found)
+	{
+		result = -1;
+		errno = ENOENT;
+	}
+	
+bail:
+	tinydir_close(&dir);
+	return result;
+}
+
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file)
+{
+	char *period = strrchr(file->name, '.');
+	if (period == NULL)
+	{
+		file->extension = &(file->name[strlen(file->name)]);
+	}
+	else
+	{
+		file->extension = period + 1;
+	}
+}
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b)
+{
+	const tinydir_file *fa = (const tinydir_file *)a;
+	const tinydir_file *fb = (const tinydir_file *)b;
+	if (fa->is_dir != fb->is_dir)
+	{
+		return -(fa->is_dir - fb->is_dir);
+	}
+	return strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
+}
+
+#endif
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index a52bc7b..416f8d1 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -6,7 +6,7 @@ SET(BEMENU_SOURCE
     util.c
     filter.c
     library.c
-    draw/curses.c
+    3rdparty/cdl.c
     )
 
 # Configure
@@ -29,6 +29,9 @@ ENDIF ()
 STRING(REGEX MATCHALL "[0-9]+" VERSION_COMPONENTS ${BEMENU_VERSION})
 LIST(GET VERSION_COMPONENTS 0 SOVERSION)
 
+# Compile renderer plugins
+ADD_SUBDIRECTORY(renderers)
+
 # Compile
 INCLUDE_DIRECTORIES(${BEMENU_INCLUDE} ${CMAKE_CURRENT_BINARY_DIR})
 ADD_LIBRARY(bemenu SHARED ${BEMENU_SOURCE})
diff --git a/lib/bemenu.h b/lib/bemenu.h
index 5457cca..a58dcb3 100644
--- a/lib/bemenu.h
+++ b/lib/bemenu.h
@@ -4,8 +4,12 @@
  * Public API header.
  */
 
-typedef struct _bmMenu bmMenu;
-typedef struct _bmItem bmItem;
+struct bm_renderer;
+struct bm_menu;
+struct bm_item;
+
+#include <stdbool.h>
+#include <stdint.h>
 
 /**
  * @defgroup Library
@@ -32,6 +36,28 @@ typedef struct _bmItem bmItem;
  * @addtogroup Library
  * @{ */
 
+/**
+ * @name Library Initialization
+ * @{ */
+
+/**
+ * Init bemenu.
+ * Loads up the renderers.
+ *
+ * @return true on success, false on failure.
+ */
+bool bm_init(void);
+
+/**
+ * Get list of available renderers.
+ *
+ * @param out_nmemb Reference to uint32_t where total count of returned renderers will be stored.
+ * @return Pointer to array of bm_renderer instances.
+ */
+const struct bm_renderer** bm_get_renderers(uint32_t *out_nmemb);
+
+/** @} Library Initialization */
+
 /**
  * @name Library Version
  * @{ */
@@ -43,59 +69,62 @@ typedef struct _bmItem bmItem;
  *
  * @return Null terminated C "string" to version string.
  */
-const char* bmVersion(void);
+const char* bm_version(void);
 
 /**  @} Library Version */
 
 /**  @} Library */
 
 /**
- * @addtogroup Menu
+ * @addtogroup Renderer
  * @{ */
 
 /**
- * Draw mode constants for bmMenu instance draw mode.
+ * Get name of the renderer.
  *
- * @link ::bmDrawMode BM_DRAW_MODE_LAST @endlink is provided for enumerating draw modes.
- * Instancing with it however provides exactly same functionality as BM_DRAW_MODE_NONE.
+ * @param renderer bm_renderer instance.
+ * @return Null terminated C "string" to renderer's name.
  */
-typedef enum bmDrawMode {
-    BM_DRAW_MODE_NONE,
-    BM_DRAW_MODE_CURSES,
-    BM_DRAW_MODE_LAST
-} bmDrawMode;
+const char* bm_renderer_get_name(const struct bm_renderer *renderer);
+
+/**
+ * @} Renderer */
+
+/**
+ * @addtogroup Menu
+ * @{ */
 
 /**
- * Filter mode constants for bmMenu instance filter mode.
+ * Filter mode constants for bm_menu instance filter mode.
  *
- * @link ::bmFilterMode BM_FILTER_MODE_LAST @endlink is provided for enumerating filter modes.
+ * @link ::bm_filter_mode BM_FILTER_MODE_LAST @endlink is provided for enumerating filter modes.
  * Using it as filter mode however provides exactly same functionality as BM_FILTER_MODE_DMENU.
  */
-typedef enum bmFilterMode {
+enum bm_filter_mode {
     BM_FILTER_MODE_DMENU,
     BM_FILTER_MODE_DMENU_CASE_INSENSITIVE,
     BM_FILTER_MODE_LAST
-} bmFilterMode;
+};
 
 /**
- * Result constants from bmMenuRunWithKey function.
+ * Result constants from bm_menu_run_with_key function.
  *
- * - @link ::bmRunResult BM_RUN_RESULT_RUNNING @endlink means that menu is running and thus should be still renderer && ran.
- * - @link ::bmRunResult BM_RUN_RESULT_SELECTED @endlink means that menu was closed and items were selected.
- * - @link ::bmRunResult BM_RUN_RESULT_CANCEL @endlink means that menu was closed and selection was canceled.
+ * - @link ::bm_run_result BM_RUN_RESULT_RUNNING @endlink means that menu is running and thus should be still renderer && ran.
+ * - @link ::bm_run_result BM_RUN_RESULT_SELECTED @endlink means that menu was closed and items were selected.
+ * - @link ::bm_run_result BM_RUN_RESULT_CANCEL @endlink means that menu was closed and selection was canceled.
  */
-typedef enum bmRunResult {
+enum bm_run_result {
     BM_RUN_RESULT_RUNNING,
     BM_RUN_RESULT_SELECTED,
     BM_RUN_RESULT_CANCEL,
-} bmRunResult;
+};
 
 /**
  * Key constants.
  *
- * @link ::bmKey BM_KEY_LAST @endlink is provided for enumerating keys.
+ * @link ::bm_key BM_KEY_LAST @endlink is provided for enumerating keys.
  */
-typedef enum bmKey {
+enum bm_key {
     BM_KEY_NONE,
     BM_KEY_UP,
     BM_KEY_DOWN,
@@ -119,33 +148,33 @@ typedef enum bmKey {
     BM_KEY_CONTROL_RETURN,
     BM_KEY_UNICODE,
     BM_KEY_LAST
-} bmKey;
+};
 
 /**
  * @name Menu Memory
  * @{ */
 
 /**
- * Create new bmMenu instance.
+ * Create new bm_menu instance.
  *
- * @param drawMode Render method to be used for this menu instance.
- * @return bmMenu for new menu instance, **NULL** if creation failed.
+ * @param renderer Name of renderer to be used for this instance, pass **NULL** for auto-detection.
+ * @return bm_menu for new menu instance, **NULL** if creation failed.
  */
-bmMenu* bmMenuNew(bmDrawMode drawMode);
+struct bm_menu* bm_menu_new(const char *renderer);
 
 /**
- * Release bmMenu instance.
+ * Release bm_menu instance.
  *
- * @param menu bmMenu instance to be freed from memory.
+ * @param menu bm_menu instance to be freed from memory.
  */
-void bmMenuFree(bmMenu *menu);
+void bm_menu_free(struct bm_menu *menu);
 
 /**
- * Release items inside bmMenu instance.
+ * Release items inside bm_menu instance.
  *
- * @param menu bmMenu instance which items will be freed from memory.
+ * @param menu bm_menu instance which items will be freed from memory.
  */
-void bmMenuFreeItems(bmMenu *menu);
+void bm_menu_free_items(struct bm_menu *menu);
 
 /**  @} Menu Memory */
 
@@ -154,85 +183,86 @@ void bmMenuFreeItems(bmMenu *menu);
  * @{ */
 
 /**
- * Set userdata pointer to bmMenu instance.
+ * Set userdata pointer to bm_menu instance.
  * Userdata will be carried unmodified by the instance.
  *
- * @param menu bmMenu instance where to set userdata pointer.
+ * @param menu bm_menu instance where to set userdata pointer.
  * @param userdata Pointer to userdata.
  */
-void bmMenuSetUserdata(bmMenu *menu, void *userdata);
+void bm_menu_set_userdata(struct bm_menu *menu, void *userdata);
 
 /**
- * Get userdata pointer from bmMenu instance.
+ * Get userdata pointer from bm_menu instance.
  *
- * @param menu bmMenu instance which userdata pointer to get.
+ * @param menu bm_menu instance which userdata pointer to get.
  * @return Pointer for unmodified userdata.
  */
-void* bmMenuGetUserdata(bmMenu *menu);
+void* bm_menu_get_userdata(struct bm_menu *menu);
 
 /**
- * Set filter text to bmMenu instance.
+ * Set filter text to bm_menu instance.
  *
- * @param menu bmMenu instance where to set filter.
+ * @param menu bm_menu instance where to set filter.
  * @param filter Null terminated C "string" to act as filter.
  */
-void bmMenuSetFilter(bmMenu *menu, const char *filter);
+void bm_menu_set_filter(struct bm_menu *menu, const char *filter);
 
 /**
- * Get filter text from  bmMenu instance.
+ * Get filter text from bm_menu instance.
  *
- * @param menu bmMenu instance where to get filter.
+ * @param menu bm_menu instance where to get filter.
  * @return Const pointer to current filter text, may be **NULL** if empty.
  */
-const char* bmMenuGetFilter(bmMenu *menu);
+const char* bm_menu_get_filter(struct bm_menu *menu);
 
 /**
- * Set active filter mode to bmMenu instance.
+ * Set active filter mode to bm_menu instance.
  *
- * @param menu bmMenu instance where to set filter mode.
- * @param mode bmFilterMode constant.
+ * @param menu bm_menu instance where to set filter mode.
+ * @param mode bm_filter_mode constant.
  */
-void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode);
+void bm_menu_set_filter_mode(struct bm_menu *menu, enum bm_filter_mode mode);
 
 /**
- * Get active filter mode from bmMenu instance.
+ * Get active filter mode from bm_menu instance.
  *
- * @param menu bmMenu instance where to get filter mode.
- * @return bmFilterMode constant.
+ * @param menu bm_menu instance where to get filter mode.
+ * @return bm_filter_mode constant.
  */
-bmFilterMode bmMenuGetFilterMode(const bmMenu *menu);
+enum bm_filter_mode bm_menu_get_filter_mode(const struct bm_menu *menu);
 
 /**
  * Set selection wrapping on/off.
  *
- * @param menu bmMenu instance where to toggle selection wrapping.
- * @param int 1 == on, 0 == off.
+ * @param menu bm_menu instance where to toggle selection wrapping.
+ * @param bool true/false.
  */
-void bmMenuSetWrap(bmMenu *menu, int wrap);
+void bm_menu_set_wrap(struct bm_menu *menu, bool wrap);
 
 /**
  * Get selection wrapping state.
  *
- * @param menu bmMenu instance where to get selection wrapping state.
+ * @param menu bm_menu instance where to get selection wrapping state.
  * @return int for wrap state.
  */
-int bmMenuGetWrap(const bmMenu *menu);
+bool bm_menu_get_wrap(const struct bm_menu *menu);
 
 /**
- * Set title to bmMenu instance.
+ * Set title to bm_menu instance.
  *
- * @param menu bmMenu instance where to set title.
+ * @param menu bm_menu instance where to set title.
  * @param title C "string" to set as title, can be **NULL** for empty title.
+ * @return true if set was succesful, false if out of memory.
  */
-int bmMenuSetTitle(bmMenu *menu, const char *title);
+bool bm_menu_set_title(struct bm_menu *menu, const char *title);
 
 /**
- * Get title from bmMenu instance.
+ * Get title from bm_menu instance.
  *
- * @param menu bmMenu instance where to get title from.
+ * @param menu bm_menu instance where to get title from.
  * @return Pointer to null terminated C "string", can be **NULL** for empty title.
  */
-const char* bmMenuGetTitle(const bmMenu *menu);
+const char* bm_menu_get_title(const struct bm_menu *menu);
 
 /**  @} Properties */
 
@@ -241,130 +271,130 @@ const char* bmMenuGetTitle(const bmMenu *menu);
  * @{ */
 
 /**
- * Add item to bmMenu instance at specific index.
+ * Add item to bm_menu instance at specific index.
  *
- * @param menu bmMenu instance where item will be added.
- * @param item bmItem instance to add.
+ * @param menu bm_menu instance where item will be added.
+ * @param item bm_item instance to add.
  * @param index Index where item will be added.
- * @return 1 on successful add, 0 on failure.
+ * @return true on successful add, false on failure.
  */
-int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index);
+bool bm_menu_add_item_at(struct bm_menu *menu, struct bm_item *item, uint32_t index);
 
 /**
- * Add item to bmMenu instance.
+ * Add item to bm_menu instance.
  *
- * @param menu bmMenu instance where item will be added.
- * @param item bmItem instance to add.
- * @return 1 on successful add, 0 on failure.
+ * @param menu bm_menu instance where item will be added.
+ * @param item bm_item instance to add.
+ * @return true on successful add, false on failure.
  */
-int bmMenuAddItem(bmMenu *menu, bmItem *item);
+bool bm_menu_add_item(struct bm_menu *menu, struct bm_item *item);
 
 /**
- * Remove item from bmMenu instance at specific index.
+ * Remove item from bm_menu instance at specific index.
  *
- * @warning The item won't be freed, use bmItemFree to do that.
+ * @warning The item won't be freed, use bm_item_free to do that.
  *
- * @param menu bmMenu instance from where item will be removed.
+ * @param menu bm_menu instance from where item will be removed.
  * @param index Index of item to remove.
- * @return 1 on successful add, 0 on failure.
+ * @return true on successful add, false on failure.
  */
-int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index);
+bool bm_menu_remove_item_at(struct bm_menu *menu, uint32_t index);
 
 /**
- * Remove item from bmMenu instance.
+ * Remove item from bm_menu instance.
  *
- * @warning The item won't be freed, use bmItemFree to do that.
+ * @warning The item won't be freed, use bm_item_free to do that.
  *
- * @param menu bmMenu instance from where item will be removed.
- * @param item bmItem instance to remove.
- * @return 1 on successful add, 0 on failure.
+ * @param menu bm_menu instance from where item will be removed.
+ * @param item bm_item instance to remove.
+ * @return true on successful add, false on failure.
  */
-int bmMenuRemoveItem(bmMenu *menu, bmItem *item);
+bool bm_menu_remove_item(struct bm_menu *menu, struct bm_item *item);
 
 /**
  * Highlight item in menu by index.
  *
- * @param menu bmMenu instance from where to highlight item.
+ * @param menu bm_menu instance from where to highlight item.
  * @param index Index of item to highlight.
- * @return 1 on successful highlight, 0 on failure.
+ * @return true on successful highlight, false on failure.
  */
-int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index);
+bool bm_menu_set_highlighted_index(struct bm_menu *menu, uint32_t index);
 
 /**
  * Highlight item in menu.
  *
- * @param menu bmMenu instance from where to highlight item.
- * @param item bmItem instance to highlight.
- * @return 1 on successful highlight, 0 on failure.
+ * @param menu bm_menu instance from where to highlight item.
+ * @param item bm_item instance to highlight.
+ * @return true on successful highlight, false on failure.
  */
-int bmMenuSetHighlighted(bmMenu *menu, bmItem *item);
+bool bm_menu_set_highlighted_item(struct bm_menu *menu, struct bm_item *item);
 
 /**
- * Get highlighted item from bmMenu instance.
+ * Get highlighted item from bm_menu instance.
  *
  * @warning The pointer returned by this function may be invalid after items change.
  *
- * @param menu bmMenu instance from where to get highlighted item.
- * @return Selected bmItem instance, **NULL** if none highlighted.
+ * @param menu bm_menu instance from where to get highlighted item.
+ * @return Selected bm_item instance, **NULL** if none highlighted.
  */
-bmItem* bmMenuGetHighlightedItem(const bmMenu *menu);
+struct bm_item* bm_menu_get_highlighted_item(const struct bm_menu *menu);
 
 /**
- * Set selected items to bmMenu instance.
+ * Set selected items to bm_menu instance.
  *
- * @param menu bmMenu instance where items will be set.
- * @param items Array of bmItem pointers to set.
+ * @param menu bm_menu instance where items will be set.
+ * @param items Array of bm_item pointers to set.
  * @param nmemb Total count of items in array.
- * @return 1 on successful set, 0 on failure.
+ * @return true on successful set, false on failure.
  */
-int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb);
+bool bm_menu_set_selected_items(struct bm_menu *menu, struct bm_item **items, uint32_t nmemb);
 
 /**
- * Get selected items from bmMenu instance.
+ * Get selected items from bm_menu instance.
  *
  * @warning The pointer returned by this function may be invalid after selection or items change.
  *
- * @param menu bmMenu instance from where to get selected items.
- * @param outNmemb Reference to unsigned int where total count of returned items will be stored.
- * @return Pointer to array of bmItem pointers.
+ * @param menu bm_menu instance from where to get selected items.
+ * @param out_nmemb Reference to uint32_t where total count of returned items will be stored.
+ * @return Pointer to array of bm_item pointers.
  */
-bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb);
+struct bm_item** bm_menu_get_selected_items(const struct bm_menu *menu, uint32_t *out_nmemb);
 
 /**
- * Set items to bmMenu instance.
- * Will replace all the old items on bmMenu instance.
+ * Set items to bm_menu instance.
+ * Will replace all the old items on bm_menu instance.
  *
  * If items is **NULL**, or nmemb is zero, all items will be freed from the menu.
  *
- * @param menu bmMenu instance where items will be set.
- * @param items Array of bmItem pointers to set.
+ * @param menu bm_menu instance where items will be set.
+ * @param items Array of bm_item pointers to set.
  * @param nmemb Total count of items in array.
- * @return 1 on successful set, 0 on failure.
+ * @return true on successful set, false on failure.
  */
-int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb);
+bool bm_menu_set_items(struct bm_menu *menu, const struct bm_item **items, uint32_t nmemb);
 
 /**
- * Get items from bmMenu instance.
+ * Get items from bm_menu instance.
  *
  * @warning The pointer returned by this function may be invalid after removing or adding new items.
  *
- * @param menu bmMenu instance from where to get items.
- * @param outNmemb Reference to unsigned int where total count of returned items will be stored.
- * @return Pointer to array of bmItem pointers.
+ * @param menu bm_menu instance from where to get items.
+ * @param out_nmemb Reference to uint32_t where total count of returned items will be stored.
+ * @return Pointer to array of bm_item pointers.
  */
-bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb);
+struct bm_item** bm_menu_get_items(const struct bm_menu *menu, uint32_t *out_nmemb);
 
 /**
- * Get filtered (displayed) items from bmMenu instance.
+ * Get filtered (displayed) items from bm_menu instance.
  *
  * @warning The pointer returned by this function _will_ be invalid when menu internally filters its list again.
  *          Do not store this pointer.
  *
- * @param menu bmMenu instance from where to get filtered items.
- * @param outNmemb Reference to unsigned int where total count of returned items will be stored.
- * @return Pointer to array of bmItem pointers.
+ * @param menu bm_menu instance from where to get filtered items.
+ * @param out_nmemb Reference to uint32_t where total count of returned items will be stored.
+ * @return Pointer to array of bm_item pointers.
  */
-bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb);
+struct bm_item** bm_menu_get_filtered_items(const struct bm_menu *menu, uint32_t *out_nmemb);
 
 /**  @} Menu Items */
 
@@ -373,11 +403,11 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb);
  * @{ */
 
 /**
- * Render bmMenu instance using chosen draw method.
+ * Render bm_menu instance using chosen renderer.
  *
- * @param menu bmMenu instance to be rendered.
+ * @param menu bm_menu instance to be rendered.
  */
-void bmMenuRender(const bmMenu *menu);
+void bm_menu_render(const struct bm_menu *menu);
 
 /**
  * Trigger filtering of menu manually.
@@ -386,30 +416,30 @@ void bmMenuRender(const bmMenu *menu);
  * Do note that filtering might be heavy, so you should only call it after batch manipulation of items.
  * Not after manipulation of each single item.
  *
- * @param menu bmMenu instance which to filter.
+ * @param menu bm_menu instance which to filter.
  */
-void bmMenuFilter(bmMenu *menu);
+void bm_menu_filter(struct bm_menu *menu);
 
 /**
  * Poll key and unicode from underlying UI toolkit.
  *
- * This function will block on @link ::bmDrawMode BM_DRAW_MODE_CURSES @endlink draw mode.
+ * This function will block on **curses** renderer.
  *
- * @param menu bmMenu instance from which to poll.
- * @param outUnicode Reference to unsigned int.
- * @return bmKey for polled key.
+ * @param menu bm_menu instance from which to poll.
+ * @param out_unicode Reference to uint32_t.
+ * @return bm_key for polled key.
  */
-bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode);
+enum bm_key bm_menu_poll_key(struct bm_menu *menu, uint32_t *out_unicode);
 
 /**
  * Advances menu logic with key and unicode as input.
  *
- * @param menu bmMenu instance to be advanced.
+ * @param menu bm_menu instance to be advanced.
  * @param key Key input that will advance menu logic.
  * @param unicode Unicode input that will advance menu logic.
- * @return bmRunResult for menu state.
+ * @return bm_run_result for menu state.
  */
-bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode);
+enum bm_run_result bm_menu_run_with_key(struct bm_menu *menu, enum bm_key key, uint32_t unicode);
 
 /**  @} Menu Logic */
 
@@ -427,16 +457,16 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode);
  * Allocate a new item.
  *
  * @param text Pointer to null terminated C "string", can be **NULL** for empty text.
- * @return bmItem for new item instance, **NULL** if creation failed.
+ * @return bm_item for new item instance, **NULL** if creation failed.
  */
-bmItem* bmItemNew(const char *text);
+struct bm_item* bm_item_new(const char *text);
 
 /**
- * Release bmItem instance.
+ * Release bm_item instance.
  *
- * @param item bmItem instance to be freed from memory.
+ * @param item bm_item instance to be freed from memory.
  */
-void bmItemFree(bmItem *item);
+void bm_item_free(struct bm_item *item);
 
 /**  @} Item Memory */
 
@@ -445,37 +475,38 @@ void bmItemFree(bmItem *item);
  * @{ */
 
 /**
- * Set userdata pointer to bmItem instance.
+ * Set userdata pointer to bm_item instance.
  * Userdata will be carried unmodified by the instance.
  *
- * @param item bmItem instance where to set userdata pointer.
+ * @param item bm_item instance where to set userdata pointer.
  * @param userdata Pointer to userdata.
  */
-void bmItemSetUserdata(bmItem *item, void *userdata);
+void bm_item_set_userdata(struct bm_item *item, void *userdata);
 
 /**
- * Get userdata pointer from bmItem instance.
+ * Get userdata pointer from bm_item instance.
  *
- * @param item bmItem instance which userdata pointer to get.
+ * @param item bm_item instance which userdata pointer to get.
  * @return Pointer for unmodified userdata.
  */
-void* bmItemGetUserdata(bmItem *item);
+void* bm_item_get_userdata(struct bm_item *item);
 
 /**
- * Set text to bmItem instance.
+ * Set text to bm_item instance.
  *
- * @param item bmItem instance where to set text.
+ * @param item bm_item instance where to set text.
  * @param text C "string" to set as text, can be **NULL** for empty text.
+ * @return true if set was succesful, false if out of memory.
  */
-int bmItemSetText(bmItem *item, const char *text);
+bool bm_item_set_text(struct bm_item *item, const char *text);
 
 /**
- * Get text from bmItem instance.
+ * Get text from bm_item instance.
  *
- * @param item bmItem instance where to get text from.
+ * @param item bm_item instance where to get text from.
  * @return Pointer to null terminated C "string", can be **NULL** for empty text.
  */
-const char* bmItemGetText(const bmItem *item);
+const char* bm_item_get_text(const struct bm_item *item);
 
 /**  @} Item Properties */
 
diff --git a/lib/draw/curses.c b/lib/draw/curses.c
deleted file mode 100644
index a1f23ef..0000000
--- a/lib/draw/curses.c
+++ /dev/null
@@ -1,498 +0,0 @@
-#include "../internal.h"
-
-#if __APPLE__
-#   define _C99_SOURCE
-#   include <stdio.h> /* vsnprintf */
-#   undef _C99_SOURCE
-#endif
-
-#define _XOPEN_SOURCE 500
-#include <signal.h> /* sigaction */
-#include <stdarg.h> /* vsnprintf */
-#undef _XOPEN_SOURCE
-
-#include <wchar.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <locale.h>
-#include <ncurses.h>
-#include <dlfcn.h>
-#include <assert.h>
-
-#if _WIN32
-static const char *TTY = "CON";
-#else
-static const char *TTY = "/dev/tty";
-#endif
-
-#if __APPLE__
-    const char *DL_PATH[] = {
-        "libncursesw.5.dylib",
-        "libncurses.5.dylib",
-        NULL
-    };
-#elif _WIN32
-#   error FIXME: Compile with windows... or use relative path?
-#else
-    const char *DL_PATH[] = {
-        "libncursesw.so.5",
-        "libncurses.so.5",
-        NULL
-    };
-#endif
-
-/* these are implemented as macros in older curses */
-#ifndef NCURSES_OPAQUE
-static int wrap_getmaxx(WINDOW *win) { return getmaxx(win); }
-static int wrap_getmaxy(WINDOW *win) { return getmaxy(win); }
-#endif
-
-/* ncurses.h likes to define stuff for us.
- * This unforunately mangles with our struct. */
-#undef erase
-#undef getch
-#undef get_wch
-#undef refresh
-#undef mvprintw
-#undef move
-#undef init_pair
-#undef attroff
-#undef attron
-#undef getmaxx
-#undef getmaxy
-
-/**
- * Dynamically loaded curses API.
- */
-static struct curses {
-    struct sigaction abrtAction;
-    struct sigaction segvAction;
-    struct sigaction winchAction;
-    void *handle;
-    WINDOW *stdscr;
-    WINDOW* (*initscr)(void);
-    int (*endwin)(void);
-    int (*refresh)(void);
-    int (*erase)(void);
-    int (*getch)(void);
-    int (*get_wch)(wint_t *wch);
-    int (*mvprintw)(int x, int y, const char *fmt, ...);
-    int (*move)(int x, int y);
-    int (*init_pair)(short color, short f, short b);
-    int (*attroff)(int attrs);
-    int (*attron)(int attrs);
-    int (*start_color)(void);
-    int (*use_default_colors)(void);
-    int (*getmaxx)(WINDOW *win);
-    int (*getmaxy)(WINDOW *win);
-    int (*keypad)(WINDOW *win, bool bf);
-    int (*curs_set)(int visibility);
-    int (*flushinp)(void);
-    int (*noecho)(void);
-    int (*raw)(void);
-    int *ESCDELAY;
-    int oldStdin;
-    int oldStdout;
-} curses;
-
-static int _bmDrawCursesResizeBuffer(char **buffer, size_t *osize, size_t nsize)
-{
-    assert(buffer);
-    assert(osize);
-
-    if (nsize == 0 || nsize <= *osize)
-        return 0;
-
-    void *tmp;
-    if (!*buffer || !(tmp = realloc(*buffer, nsize))) {
-        if (!(tmp = malloc(nsize)))
-            return 0;
-
-        if (*buffer) {
-            memcpy(tmp, *buffer, *osize);
-            free(*buffer);
-        }
-    }
-
-    *buffer = tmp;
-    *osize = nsize;
-    return 1;
-}
-
-#if __GNUC__
-__attribute__((format(printf, 3, 4)))
-#endif
-static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...)
-{
-    static size_t blen = 0;
-    static char *buffer = NULL;
-
-    size_t ncols = curses.getmaxx(curses.stdscr);
-    if (ncols <= 0)
-        return;
-
-    va_list args;
-    va_start(args, format);
-    size_t nlen = vsnprintf(NULL, 0, format, args);
-    va_end(args);
-
-    if ((!buffer || nlen > blen) && !_bmDrawCursesResizeBuffer(&buffer, &blen, nlen + 1))
-        return;
-
-    va_start(args, format);
-    vsnprintf(buffer, blen - 1, format, args);
-    va_end(args);
-
-    size_t dw = 0, i = 0;
-    while (dw < ncols && i < nlen) {
-        if (buffer[i] == '\t') buffer[i] = ' ';
-        int next = _bmUtf8RuneNext(buffer, i);
-        dw += _bmUtf8RuneWidth(buffer + i, next);
-        i += (next ? next : 1);
-    }
-
-    if (dw < ncols) {
-        /* line is too short, widen it */
-        size_t offset = i + (ncols - dw);
-        if (blen <= offset && !_bmDrawCursesResizeBuffer(&buffer, &blen, offset + 1))
-             return;
-
-        memset(buffer + nlen, ' ', offset - nlen);
-        buffer[offset] = 0;
-    } else if (i < blen) {
-        /* line is too long, shorten it */
-        i -= _bmUtf8RunePrev(buffer, i - (dw - ncols)) - 1;
-        size_t cc = dw - (dw - ncols);
-
-        size_t offset = i - (dw - ncols) + (ncols - cc) + 1;
-        if (blen <= offset) {
-            int diff = offset - blen + 1;
-            if (!_bmDrawCursesResizeBuffer(&buffer, &blen, blen + diff))
-                return;
-        }
-
-        memset(buffer + i - (dw - ncols), ' ', (ncols - cc) + 1);
-        buffer[offset] = 0;
-    }
-
-    if (pair > 0)
-        curses.attron(COLOR_PAIR(pair));
-
-    curses.mvprintw(y, 0, "%s", buffer);
-
-    if (pair > 0)
-        curses.attroff(COLOR_PAIR(pair));
-}
-
-static void _bmDrawCursesRender(const bmMenu *menu)
-{
-    if (!curses.stdscr) {
-        curses.oldStdin = dup(STDIN_FILENO);
-        curses.oldStdout = dup(STDOUT_FILENO);
-
-        freopen(TTY, "w", stdout);
-        freopen(TTY, "r", stdin);
-
-        setlocale(LC_CTYPE, "");
-
-        if ((curses.stdscr = curses.initscr()) == NULL)
-            return;
-
-        *curses.ESCDELAY = 25;
-        curses.flushinp();
-        curses.keypad(curses.stdscr, true);
-        curses.curs_set(1);
-        curses.noecho();
-        curses.raw();
-
-        curses.start_color();
-        curses.use_default_colors();
-        curses.init_pair(1, COLOR_BLACK, COLOR_RED);
-        curses.init_pair(2, COLOR_RED, -1);
-    }
-
-    const unsigned int lines = curses.getmaxy(curses.stdscr);
-    curses.erase();
-
-    unsigned int ncols = curses.getmaxx(curses.stdscr);
-    unsigned int titleLen = (menu->title ? strlen(menu->title) + 1 : 0);
-
-    if (titleLen >= ncols)
-        titleLen = 0;
-
-    unsigned int ccols = ncols - titleLen - 1;
-    unsigned int dcols = 0, doffset = menu->cursor;
-
-    while (doffset > 0 && dcols < ccols) {
-        int prev = _bmUtf8RunePrev(menu->filter, doffset);
-        dcols += _bmUtf8RuneWidth(menu->filter + doffset - prev, prev);
-        doffset -= (prev ? prev : 1);
-    }
-
-    _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter + doffset : ""));
-
-    if (menu->title && titleLen > 0) {
-        curses.attron(COLOR_PAIR(1));
-        curses.mvprintw(0, 0, menu->title);
-        curses.attroff(COLOR_PAIR(1));
-    }
-
-    unsigned int i, cl = 1;
-    unsigned int itemsCount;
-    bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount);
-    for (i = (menu->index / (lines - 1)) * (lines - 1); i < itemsCount && cl < lines; ++i) {
-        int highlighted = (items[i] == bmMenuGetHighlightedItem(menu));
-        int color = (highlighted ? 2 : (_bmMenuItemIsSelected(menu, items[i]) ? 1 : 0));
-        _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : "   "), (items[i]->text ? items[i]->text : ""));
-    }
-
-    curses.move(0, titleLen + (menu->cursesCursor < ccols ? menu->cursesCursor : ccols));
-    curses.refresh();
-}
-
-static unsigned int _bmDrawCursesDisplayedCount(const bmMenu *menu)
-{
-    (void)menu;
-    return (curses.stdscr ? curses.getmaxy(curses.stdscr) : 0);
-}
-
-static void _bmDrawCursesEndWin(void)
-{
-    if (!curses.stdscr)
-        return;
-
-    freopen(TTY, "w", stdout);
-
-    if (curses.refresh)
-        curses.refresh();
-
-    if (curses.endwin)
-        curses.endwin();
-
-    dup2(curses.oldStdin, STDIN_FILENO);
-    dup2(curses.oldStdout, STDOUT_FILENO);
-    close(curses.oldStdin);
-    close(curses.oldStdout);
-
-    curses.stdscr = NULL;
-}
-
-static bmKey _bmDrawCursesGetKey(unsigned int *unicode)
-{
-    assert(unicode);
-    *unicode = 0;
-
-    if (!curses.stdscr)
-        return BM_KEY_NONE;
-
-    if (curses.get_wch)
-        curses.get_wch((wint_t*)unicode);
-    else if (curses.getch)
-        *unicode = curses.getch();
-
-    switch (*unicode) {
-#if KEY_RESIZE
-        case KEY_RESIZE:
-            return BM_KEY_NONE;
-#endif
-
-        case 16: /* C-p */
-        case KEY_UP:
-            return BM_KEY_UP;
-
-        case 14: /* C-n */
-        case KEY_DOWN:
-            return BM_KEY_DOWN;
-
-        case 2: /* C-b */
-        case KEY_LEFT:
-            return BM_KEY_LEFT;
-
-        case 6: /* C-f */
-        case KEY_RIGHT:
-            return BM_KEY_RIGHT;
-
-        case 1: /* C-a */
-        case 391: /* S-Home */
-        case KEY_HOME:
-            return BM_KEY_HOME;
-
-        case 5: /* C-e */
-        case 386: /* S-End */
-        case KEY_END:
-            return BM_KEY_END;
-
-        case KEY_PPAGE: /* Page up */
-            return BM_KEY_PAGE_UP;
-
-        case KEY_NPAGE: /* Page down */
-            return BM_KEY_PAGE_DOWN;
-
-        case 550: /* C-Page up */
-        case 398: /* S-Page up */
-            return BM_KEY_SHIFT_PAGE_UP;
-
-        case 545: /* C-Page down */
-        case 396: /* S-Page down */
-            return BM_KEY_SHIFT_PAGE_DOWN;
-
-        case 8: /* C-h */
-        case 127: /* Delete */
-        case KEY_BACKSPACE:
-            return BM_KEY_BACKSPACE;
-
-        case 4: /* C-d */
-        case KEY_DC:
-            return BM_KEY_DELETE;
-
-        case 383: /* S-Del */
-        case 21: /* C-u */
-            return BM_KEY_LINE_DELETE_LEFT;
-
-        case 11: /* C-k */
-            return BM_KEY_LINE_DELETE_RIGHT;
-
-        case 23: /* C-w */
-            return BM_KEY_WORD_DELETE;
-
-        case 9: /* Tab */
-            return BM_KEY_TAB;
-
-        case 18: /* C-r */
-            return BM_KEY_CONTROL_RETURN;
-
-        case 20: /* C-t */
-        case 331: /* Insert */
-            _bmDrawCursesEndWin();
-            return BM_KEY_SHIFT_RETURN;
-
-        case 10: /* Return */
-            _bmDrawCursesEndWin();
-            return BM_KEY_RETURN;
-
-        case 7: /* C-g */
-        case 27: /* Escape */
-            _bmDrawCursesEndWin();
-            return BM_KEY_ESCAPE;
-
-        default: break;
-    }
-
-    return BM_KEY_UNICODE;
-}
-
-static void _bmDrawCursesFree(void)
-{
-    _bmDrawCursesEndWin();
-
-    if (curses.handle)
-        dlclose(curses.handle);
-
-    sigaction(SIGABRT, &curses.abrtAction, NULL);
-    sigaction(SIGSEGV, &curses.segvAction, NULL);
-    sigaction(SIGWINCH, &curses.winchAction, NULL);
-    memset(&curses, 0, sizeof(curses));
-}
-
-static void _bmDrawCursesCrashHandler(int sig)
-{
-    (void)sig;
-    _bmDrawCursesFree();
-}
-
-static void _bmDrawCursesResizeHandler(int sig)
-{
-    (void)sig;
-    if (!curses.stdscr)
-        return;
-
-    curses.endwin();
-    curses.refresh();
-}
-
-int _bmDrawCursesInit(struct _bmRenderApi *api)
-{
-    memset(&curses, 0, sizeof(curses));
-    const char *lib = NULL, *func = NULL;
-
-    unsigned int i;
-    for (i = 0; DL_PATH[i] && !curses.handle; ++i)
-        curses.handle = dlopen((lib = DL_PATH[i]), RTLD_LAZY);
-
-    if (!curses.handle)
-        return 0;
-
-#define bmLoadFunction(x) (curses.x = dlsym(curses.handle, (func = #x)))
-
-    if (!bmLoadFunction(initscr))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(endwin))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(refresh))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(get_wch) && !bmLoadFunction(getch))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(erase))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(mvprintw))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(move))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(init_pair))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(attroff))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(attron))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(start_color))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(use_default_colors))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(keypad))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(curs_set))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(flushinp))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(noecho))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(raw))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(ESCDELAY))
-        goto function_pointer_exception;
-
-#ifndef NCURSES_OPAQUE
-    curses.getmaxx = wrap_getmaxx;
-    curses.getmaxy = wrap_getmaxy;
-#else
-    if (!bmLoadFunction(getmaxx))
-        goto function_pointer_exception;
-    if (!bmLoadFunction(getmaxy))
-        goto function_pointer_exception;
-#endif
-
-#undef bmLoadFunction
-
-    api->displayedCount = _bmDrawCursesDisplayedCount;
-    api->getKey = _bmDrawCursesGetKey;
-    api->render = _bmDrawCursesRender;
-    api->free = _bmDrawCursesFree;
-
-    struct sigaction action;
-    memset(&action, 0, sizeof(struct sigaction));
-    action.sa_handler = _bmDrawCursesCrashHandler;
-    sigaction(SIGABRT, &action, &curses.abrtAction);
-    sigaction(SIGSEGV, &action, &curses.segvAction);
-
-    action.sa_handler = _bmDrawCursesResizeHandler;
-    sigaction(SIGWINCH, &action, &curses.winchAction);
-    return 1;
-
-function_pointer_exception:
-    fprintf(stderr, "-!- Could not load function '%s' from '%s'\n", func, lib);
-    _bmDrawCursesFree();
-    return 0;
-}
-
-/* vim: set ts=8 sw=4 tw=0 :*/
diff --git a/lib/filter.c b/lib/filter.c
index 421fab1..605a740 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -4,54 +4,54 @@
 #include <string.h>
 
 /**
- * Shrink bmItem** list pointer.
+ * Shrink struct bm_item** list pointer.
  *
  * Useful helper function for filter functions.
  *
- * @param list Pointer to pointer to list of bmItem pointers.
+ * @param in_out_list Pointer to pointer to list of bm_item pointers.
  * @param osize Current size of the list.
  * @param nsize New size the list will be shrinked to.
- * @return Pointer to list of bmItem pointers.
+ * @return Pointer to list of bm_item pointers.
  */
-static bmItem** _bmFilterShrinkList(bmItem ***inOutList, size_t osize, size_t nsize)
+static struct bm_item**
+shrink_list(struct bm_item ***in_out_list, size_t osize, size_t nsize)
 {
-    assert(inOutList);
+    assert(in_out_list);
 
     if (nsize == 0) {
-        free(*inOutList);
-        return (*inOutList = NULL);
+        free(*in_out_list);
+        return (*in_out_list = NULL);
     }
 
     if (nsize >= osize)
-        return *inOutList;
+        return *in_out_list;
 
-    void *tmp = malloc(sizeof(bmItem*) * nsize);
+    void *tmp = malloc(sizeof(struct bm_item*) * nsize);
     if (!tmp)
-        return *inOutList;
+        return *in_out_list;
 
-    memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize);
-    free(*inOutList);
-    return (*inOutList = tmp);
+    memcpy(tmp, *in_out_list, sizeof(struct bm_item*) * nsize);
+    free(*in_out_list);
+    return (*in_out_list = tmp);
 }
 
 /**
  * Text filter tokenizer helper.
  *
- * @param menu bmMenu instance which filter to tokenize.
- * @param outTokv char pointer reference to list of tokens, this should be freed after use.
- * @param outTokc unsigned int reference to number of tokens.
+ * @param menu bm_menu instance which filter to tokenize.
+ * @param out_tokv char pointer reference to list of tokens, this should be freed after use.
+ * @param out_tokc uint32_t reference to number of tokens.
  * @return Pointer to buffer that contains tokenized string, this should be freed after use.
  */
-static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outTokc)
+static char*
+tokenize(struct bm_menu *menu, char ***out_tokv, uint32_t *out_tokc)
 {
-    assert(menu);
-    assert(outTokv);
-    assert(outTokc);
-    *outTokv = NULL;
-    *outTokc = 0;
+    assert(menu && out_tokv && out_tokc);
+    *out_tokv = NULL;
+    *out_tokc = 0;
 
     char **tokv = NULL, *buffer = NULL;
-    if (!(buffer = _bmStrdup(menu->filter)))
+    if (!(buffer = bm_strdup(menu->filter)))
         goto fail;
 
     char *s;
@@ -59,8 +59,8 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT
 
     char **tmp = NULL;
     size_t pos = 0, next;
-    unsigned int tokc = 0, tokn = 0;
-    for (; (pos = _bmStripToken(s, " ", &next)) > 0; tokv = tmp) {
+    uint32_t tokc = 0, tokn = 0;
+    for (; (pos = bm_strip_token(s, " ", &next)) > 0; tokv = tmp) {
         if (++tokc > tokn && !(tmp = realloc(tokv, ++tokn * sizeof(char*))))
             goto fail;
 
@@ -69,74 +69,70 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT
         for (; *s && *s == ' '; ++s);
     }
 
-    *outTokv = tmp;
-    *outTokc = tokc;
+    *out_tokv = tmp;
+    *out_tokc = tokc;
     return buffer;
 
 fail:
-    if (buffer)
-        free(buffer);
-    if (tokv)
-        free(tokv);
+    free(buffer);
+    free(tokv);
     return NULL;
 }
 
 /**
  * Dmenu filterer that accepts substring function.
  *
- * @param menu bmMenu instance to filter.
+ * @param menu bm_menu instance to filter.
  * @param addition This will be 1, if filter is same as previous filter with something appended.
  * @param fstrstr Substring function used to match items.
- * @param outNmemb unsigned int reference to filtered items outNmemb.
- * @return Pointer to array of bmItem pointers.
+ * @param out_nmemb uint32_t reference to filtered items count.
+ * @return Pointer to array of bm_item pointers.
  */
-bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), int (*fstrncmp)(const char *a, const char *b, size_t len), unsigned int *outNmemb)
+struct bm_item**
+filter_dmenu_fun(struct bm_menu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), int (*fstrncmp)(const char *a, const char *b, size_t len), uint32_t *out_nmemb)
 {
-    assert(menu);
-    assert(fstrstr);
-    assert(fstrncmp);
-    assert(outNmemb);
-    *outNmemb = 0;
+    assert(menu && fstrstr && fstrncmp && out_nmemb);
+    *out_nmemb = 0;
 
-    unsigned int itemsCount;
-    bmItem **items;
+    uint32_t count;
+    struct bm_item **items;
 
     if (addition) {
-        items = bmMenuGetFilteredItems(menu, &itemsCount);
+        items = bm_menu_get_filtered_items(menu, &count);
     } else {
-        items = bmMenuGetItems(menu, &itemsCount);
+        items = bm_menu_get_items(menu, &count);
     }
 
     char *buffer = NULL;
-    bmItem **filtered = calloc(itemsCount, sizeof(bmItem*));
-    if (!filtered)
+    struct bm_item **filtered;
+    if (!(filtered = calloc(count, sizeof(struct bm_item*))))
         goto fail;
 
     char **tokv;
-    unsigned int tokc;
-    if (!(buffer = _bmFilterTokenize(menu, &tokv, &tokc)))
+    uint32_t tokc;
+    if (!(buffer = tokenize(menu, &tokv, &tokc)))
         goto fail;
 
     size_t len = (tokc ? strlen(tokv[0]) : 0);
-    unsigned int i, f, e;
-    for (e = f = i = 0; i < itemsCount; ++i) {
-        bmItem *item = items[i];
+    uint32_t i, f, e;
+    for (e = f = i = 0; i < count; ++i) {
+        struct bm_item *item = items[i];
         if (!item->text && tokc != 0)
             continue;
 
         if (tokc && item->text) {
-            unsigned int t;
+            uint32_t t;
             for (t = 0; t < tokc && fstrstr(item->text, tokv[t]); ++t);
             if (t < tokc)
                 continue;
         }
 
         if (tokc && item->text && !fstrncmp(tokv[0], item->text, len + 1)) { /* exact matches */
-            memmove(&filtered[1], filtered, f * sizeof(bmItem*));
+            memmove(&filtered[1], filtered, f * sizeof(struct bm_item*));
             filtered[0] = item;
             e++; /* where do exact matches end */
         } else if (tokc && item->text && !fstrncmp(tokv[0], item->text, len)) { /* prefixes */
-            memmove(&filtered[e + 1], &filtered[e], (f - e) * sizeof(bmItem*));
+            memmove(&filtered[e + 1], &filtered[e], (f - e) * sizeof(struct bm_item*));
             filtered[e] = item;
             e++; /* where do exact matches end */
         } else {
@@ -145,45 +141,42 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c
         f++; /* where do all matches end */
     }
 
-    if (buffer)
-        free(buffer);
-    if (tokv)
-        free(tokv);
-
-    return _bmFilterShrinkList(&filtered, menu->items.count, (*outNmemb = f));
+    free(buffer);
+    free(tokv);
+    return shrink_list(&filtered, menu->items.count, (*out_nmemb = f));
 
 fail:
-    if (filtered)
-        free(filtered);
-    if (buffer)
-        free(buffer);
+    free(filtered);
+    free(buffer);
     return NULL;
 }
 
 /**
  * Filter that mimics the vanilla dmenu filtering.
  *
- * @param menu bmMenu instance to filter.
+ * @param menu bm_menu instance to filter.
  * @param addition This will be 1, if filter is same as previous filter with something appended.
- * @param outNmemb unsigned int reference to filtered items outNmemb.
- * @return Pointer to array of bmItem pointers.
+ * @param outNmemb uint32_t reference to filtered items count.
+ * @return Pointer to array of bm_item pointers.
  */
-bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb)
+struct bm_item**
+bm_filter_dmenu(struct bm_menu *menu, bool addition, uint32_t *out_nmemb)
 {
-    return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb);
+    return filter_dmenu_fun(menu, addition, strstr, strncmp, out_nmemb);
 }
 
 /**
  * Filter that mimics the vanilla case-insensitive dmenu filtering.
  *
- * @param menu bmMenu instance to filter.
+ * @param menu bm_menu instance to filter.
  * @param addition This will be 1, if filter is same as previous filter with something appended.
- * @param outNmemb unsigned int reference to filtered items outNmemb.
- * @return Pointer to array of bmItem pointers.
+ * @param outNmemb uint32_t reference to filtered items count.
+ * @return Pointer to array of bm_item pointers.
  */
-bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb)
+struct bm_item**
+bm_filter_dmenu_case_insensitive(struct bm_menu *menu, bool addition, uint32_t *out_nmemb)
 {
-    return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb);
+    return filter_dmenu_fun(menu, addition, bm_strupstr, bm_strnupcmp, out_nmemb);
 }
 
 /* vim: set ts=8 sw=4 tw=0 :*/
diff --git a/lib/internal.h b/lib/internal.h
index 2fc9393..dd232d2 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -5,71 +5,109 @@
 #endif
 
 /**
- * Internal bmItem struct that is not exposed to public.
- * Represents a single item in menu.
+ * Destructor function pointer for some list calls.
+ */
+typedef void (*list_free_fun)(void*);
+
+/**
+ * List type
  */
-struct _bmItem {
+struct list {
     /**
-     * Userdata pointer.
-     * This pointer will be passed around with the item untouched.
+     * Items in the list.
      */
-    void *userdata;
+    void **items;
 
     /**
-     * Primary text shown on item as null terminated C "string".
-     * Matching will be done against this text as well.
+     * Number of items.
      */
-    char *text;
+    uint32_t count;
+
+    /**
+     * Number of allocated items.
+     */
+    uint32_t allocated;
 };
 
 /**
- * Internal bmRenderApi struct.
+ * Internal render api struct.
  * Renderers should be able to fill this one as they see fit.
  */
-struct _bmRenderApi {
+struct render_api {
+    /**
+     * Create underlying renderer.
+     */
+    void (*constructor)(void);
+
+    /**
+     * Release underlying renderer.
+     */
+    void (*destructor)(void);
+
     /**
      * Get count of displayed items by the underlying renderer.
      */
-    unsigned int (*displayedCount)(const bmMenu *menu);
+    uint32_t (*get_displayed_count)(const struct bm_menu *menu);
 
     /**
      * If the underlying renderer is a UI toolkit. (curses, etc...)
      * There might be possibility to get user input, and this should be thus implemented.
      */
-    bmKey (*getKey)(unsigned int *unicode);
+    enum bm_key (*poll_key)(uint32_t *unicode);
 
     /**
      * Tells underlying renderer to draw the menu.
      */
-    void (*render)(const bmMenu *menu);
+    void (*render)(const struct bm_menu *menu);
+};
+
+/**
+ * Internal bm_renderer struct.
+ */
+struct bm_renderer {
+    /**
+     * Name of the renderer.
+     */
+    char *name;
 
     /**
-     * Release underlying renderer.
+     * File path of the renderer.
      */
-    void (*free)(void);
-};
+    char *file;
 
-struct _bmItemList {
     /**
-     * Items in the list.
+     * Open handle to the plugin file of this renderer.
      */
-    struct _bmItem **list;
+    void *handle;
 
     /**
-     * Number of items.
+     * API
      */
-    unsigned int count;
+    struct render_api api;
+};
 
+/**
+ * Internal bm_item struct that is not exposed to public.
+ * Represents a single item in menu.
+ */
+struct bm_item {
     /**
-     * Number of allocated items.
+     * Userdata pointer.
+     * This pointer will be passed around with the item untouched.
+     */
+    void *userdata;
+
+    /**
+     * Primary text shown on item as null terminated C "string".
+     * Matching will be done against this text as well.
      */
-    unsigned int allocated;
+    char *text;
 };
 
 /**
- * Internal bmMenu struct that is not exposed to public.
+ * Internal bm_menu struct that is not exposed to public.
  */
-struct _bmMenu {
+struct bm_menu {
     /**
      * Userdata pointer.
      * This pointer will be passed around with the menu untouched.
@@ -79,22 +117,22 @@ struct _bmMenu {
     /**
      * Underlying renderer access.
      */
-    struct _bmRenderApi renderApi;
+    const struct bm_renderer *renderer;
 
     /**
      * Items contained in menu instance.
      */
-    struct _bmItemList items;
+    struct list items;
 
     /**
      * Filtered/displayed items contained in menu instance.
      */
-    struct _bmItemList filtered;
+    struct list filtered;
 
     /**
      * Selected items.
      */
-    struct _bmItemList selection;
+    struct list selection;
 
     /**
      * Menu instance title.
@@ -109,80 +147,74 @@ struct _bmMenu {
     /**
      * Used as optimization.
      */
-    char *oldFilter;
+    char *old_filter;
 
     /**
      * Size of filter buffer
      */
-    size_t filterSize;
+    size_t filter_size;
 
     /**
      * Current byte offset on filter text.
      */
-    unsigned int cursor;
+    uint32_t cursor;
 
     /**
      * Current column/cursor position on filter text.
      */
-    unsigned int cursesCursor;
+    uint32_t curses_cursor;
 
     /**
      * Current filtered/highlighted item index in menu instance.
      * This index is valid for the list returned by bmMenuGetFilteredItems.
      */
-    unsigned int index;
+    uint32_t index;
 
     /**
      * Current filtering method in menu instance.
      */
-    bmFilterMode filterMode;
-
-    /**
-     * Drawing mode used in menu instance.
-     */
-    bmDrawMode drawMode;
+    enum bm_filter_mode filter_mode;
 
     /**
      * Should selection be wrapped?
      */
-    char wrap;
+    bool wrap;
 };
 
-/* draw/curses.c */
-int _bmDrawCursesInit(struct _bmRenderApi *api);
+/* library.c */
+bool bm_renderer_activate(struct bm_renderer *renderer);
 
 /* menu.c */
-int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item);
+bool bm_menu_item_is_selected(const struct bm_menu *menu, const struct bm_item *item);
 
 /* filter.c */
-bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb);
-bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb);
+struct bm_item** bm_filter_dmenu(struct bm_menu *menu, bool addition, uint32_t *out_nmemb);
+struct bm_item** bm_filter_dmenu_case_insensitive(struct bm_menu *menu, bool addition, uint32_t *out_nmemb);
 
 /* list.c */
-void _bmItemListFreeList(struct _bmItemList *list);
-void _bmItemListFreeItems(struct _bmItemList *list);
-bmItem** _bmItemListGetItems(const struct _bmItemList *list, unsigned int *outNmemb);
-int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned int nmemb);
-int _bmItemListSetItems(struct _bmItemList *list, const bmItem **items, unsigned int nmemb);
-int _bmItemListGrow(struct _bmItemList *list, unsigned int step);
-int _bmItemListAddItemAt(struct _bmItemList *list, bmItem *item, unsigned int index);
-int _bmItemListAddItem(struct _bmItemList *list, bmItem *item);
-int _bmItemListRemoveItemAt(struct _bmItemList *list, unsigned int index);
-int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item);
+void list_free_list(struct list *list);
+void list_free_items(struct list *list, list_free_fun destructor);
+void* list_get_items(const struct list *list, uint32_t *out_nmemb);
+bool list_set_items_no_copy(struct list *list, void *items, uint32_t nmemb);
+bool list_set_items(struct list *list, const void *items, uint32_t nmemb, list_free_fun destructor);
+bool list_grow(struct list *list, uint32_t step);
+bool list_add_item_at(struct list *list, void *item, uint32_t index);
+bool list_add_item(struct list *list, void *item);
+bool list_remove_item_at(struct list *list, uint32_t index);
+bool list_remove_item(struct list *list, const void *item);
 
 /* util.c */
-char* _bmStrdup(const char *s);
-size_t _bmStripToken(char *string, const char *token, size_t *outNext);
-int _bmStrupcmp(const char *hay, const char *needle);
-int _bmStrnupcmp(const char *hay, const char *needle, size_t len);
-char* _bmStrupstr(const char *hay, const char *needle);
-bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize);
-int _bmUtf8StringScreenWidth(const char *string);
-size_t _bmUtf8RuneNext(const char *string, size_t start);
-size_t _bmUtf8RunePrev(const char *string, size_t start);
-size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len);
-size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth);
-size_t _bmUtf8RuneInsert(char **string, size_t *bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth);
-size_t _bmUnicodeInsert(char **string, size_t *bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth);
+char* bm_strdup(const char *s);
+size_t bm_strip_token(char *string, const char *token, size_t *out_next);
+int bm_strupcmp(const char *hay, const char *needle);
+int bm_strnupcmp(const char *hay, const char *needle, size_t len);
+char* bm_strupstr(const char *hay, const char *needle);
+int32_t bm_utf8_string_screen_width(const char *string);
+size_t bm_utf8_rune_next(const char *string, size_t start);
+size_t bm_utf8_rune_prev(const char *string, size_t start);
+size_t bm_utf8_rune_width(const char *rune, uint32_t u8len);
+size_t bm_utf8_rune_remove(char *string, size_t start, size_t *out_rune_width);
+size_t bm_utf8_rune_insert(char **string, size_t *bufSize, size_t start, const char *rune, uint32_t u8len, size_t *out_rune_width);
+size_t bm_unicode_insert(char **string, size_t *bufSize, size_t start, uint32_t unicode, size_t *out_rune_width);
 
 /* vim: set ts=8 sw=4 tw=0 :*/
diff --git a/lib/item.c b/lib/item.c
index f2a879a..1d12357 100644
--- a/lib/item.c
+++ b/lib/item.c
@@ -3,91 +3,55 @@
 #include <assert.h>
 #include <string.h>
 
-/**
- * Allocate a new item.
- *
- * @param text Pointer to null terminated C "string", can be **NULL** for empty text.
- * @return bmItem for new item instance, **NULL** if creation failed.
- */
-bmItem* bmItemNew(const char *text)
+struct bm_item*
+bm_item_new(const char *text)
 {
-    bmItem *item = calloc(1, sizeof(bmItem));
-
-    if (!item)
+    struct bm_item *item;
+    if (!(item = calloc(1, sizeof(struct bm_item))))
         return NULL;
 
-    bmItemSetText(item, text);
+    bm_item_set_text(item, text);
     return item;
 }
 
-/**
- * Release bmItem instance.
- *
- * @param item bmItem instance to be freed from memory.
- */
-void bmItemFree(bmItem *item)
+void
+bm_item_free(struct bm_item *item)
 {
     assert(item);
-
-    if (item->text)
-        free(item->text);
-
+    free(item->text);
     free(item);
 }
 
-/**
- * Set userdata pointer to bmItem instance.
- * Userdata will be carried unmodified by the instance.
- *
- * @param item bmItem instance where to set userdata pointer.
- * @param userdata Pointer to userdata.
- */
-void bmItemSetUserdata(bmItem *item, void *userdata)
+void
+bm_item_set_userdata(struct bm_item *item, void *userdata)
 {
     assert(item);
     item->userdata = userdata;
 }
 
-/**
- * Get userdata pointer from bmItem instance.
- *
- * @param item bmItem instance which userdata pointer to get.
- * @return Pointer for unmodified userdata.
- */
-void* bmItemGetUserdata(bmItem *item)
+void*
+bm_item_get_userdata(struct bm_item *item)
 {
     assert(item);
     return item->userdata;
 }
 
-/**
- * Set text to bmItem instance.
- *
- * @param item bmItem instance where to set text.
- * @param text C "string" to set as text, can be **NULL** for empty text.
- */
-int bmItemSetText(bmItem *item, const char *text)
+bool
+bm_item_set_text(struct bm_item *item, const char *text)
 {
     assert(item);
 
     char *copy = NULL;
-    if (text && !(copy = _bmStrdup(text)))
-        return 0;
-
-    if (item->text)
-        free(item->text);
+    if (text && !(copy = bm_strdup(text)))
+        return false;
 
+    free(item->text);
     item->text = copy;
-    return 1;
+    return true;
 }
 
-/**
- * Get text from bmItem instance.
- *
- * @param item bmItem instance where to get text from.
- * @return Pointer to null terminated C "string", can be **NULL** for empty text.
- */
-const char* bmItemGetText(const bmItem *item)
+const char*
+bm_item_get_text(const struct bm_item *item)
 {
     assert(item);
     return item->text;
diff --git a/lib/library.c b/lib/library.c
index f432eda..6544086 100644
--- a/lib/library.c
+++ b/lib/library.c
@@ -1,15 +1,151 @@
+#include "internal.h"
 #include "version.h"
 
-/**
- * Get version of the library in 'major.minor.patch' format.
- *
- * @see @link http://semver.org/ Semantic Versioning @endlink
- *
- * @return Null terminated C "string" to version string.
- */
-const char *bmVersion(void)
+#include "3rdparty/tinydir.h"
+#include "3rdparty/cdl.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+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 fail;
+
+    const char* (*regfun)(struct render_api*);
+    if (!(regfun = chckDlLoadSymbol(handle, "register_renderer", &error)))
+        goto fail;
+
+    renderer->file = bm_strdup(file);
+    renderer->handle = handle;
+    const char *name = regfun(&renderer->api);
+    renderer->name = (name ? bm_strdup(name) : NULL);
+    return true;
+
+fail:
+    if (handle)
+        chckDlUnload(handle);
+    fprintf(stderr, "%s\n", error);
+    return false;
+}
+
+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);
+    return list_add_item(&renderers, renderer);
+
+fail:
+    if (renderer)
+        bm_renderer_free(renderer);
+    return false;
+}
+
+bool
+bm_renderer_activate(struct bm_renderer *renderer)
+{
+    assert(renderer);
+
+    if (!load(renderer->file, renderer))
+        return false;
+
+    return true;
+}
+
+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-"))) {
+            size_t len = snprintf(NULL, 0, "%s/%s", file.name, path);
+
+            char *fpath;
+            if ((fpath = calloc(1, len + 1))) {
+                sprintf(fpath, "%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;
+}
+
 /* vim: set ts=8 sw=4 tw=0 :*/
diff --git a/lib/list.c b/lib/list.c
index 203adf2..918e830 100644
--- a/lib/list.c
+++ b/lib/list.c
@@ -3,137 +3,142 @@
 #include <string.h>
 #include <assert.h>
 
-void _bmItemListFreeList(struct _bmItemList *list)
+void
+list_free_list(struct list *list)
 {
     assert(list);
-
-    if (list->list)
-        free(list->list);
-
+    free(list->items);
     list->allocated = list->count = 0;
-    list->list = NULL;
+    list->items = NULL;
 }
 
-void _bmItemListFreeItems(struct _bmItemList *list)
+void
+list_free_items(struct list *list, list_free_fun destructor)
 {
     assert(list);
 
-    unsigned int i;
-    for (i = 0; i < list->count; ++i)
-        bmItemFree(list->list[i]);
+    for (uint32_t i = 0; i < list->count; ++i)
+        destructor(list->items[i]);
 
-    _bmItemListFreeList(list);
+    list_free_list(list);
 }
 
-bmItem** _bmItemListGetItems(const struct _bmItemList *list, unsigned int *outNmemb)
+void*
+list_get_items(const struct list *list, uint32_t *out_nmemb)
 {
     assert(list);
 
-    if (outNmemb)
-        *outNmemb = list->count;
+    if (out_nmemb)
+        *out_nmemb = list->count;
 
-    return list->list;
+    return list->items;
 }
 
 /** !!! Frees the old list, not items !!! */
-int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned int nmemb)
+bool
+list_set_items_no_copy(struct list *list, void *items, uint32_t nmemb)
 {
     assert(list);
 
-    _bmItemListFreeList(list);
+    list_free_list(list);
 
     if (!items || nmemb == 0) {
         items = NULL;
         nmemb = 0;
     }
 
-    list->list = items;
+    list->items = items;
     list->allocated = list->count = nmemb;
-    return 1;
+    return true;
 }
 
 /** !!! Frees the old items and list !!! */
-int _bmItemListSetItems(struct _bmItemList *list, const bmItem **items, unsigned int nmemb)
+bool
+list_set_items(struct list *list, const void *items, uint32_t nmemb, list_free_fun destructor)
 {
     assert(list);
 
     if (!items || nmemb == 0) {
-        _bmItemListFreeItems(list);
-        return 1;
+        list_free_items(list, destructor);
+        return true;
     }
 
-    bmItem **newItems;
-    if (!(newItems = calloc(sizeof(bmItem*), nmemb)))
-        return 0;
+    void *new_items;
+    if (!(new_items = calloc(sizeof(void*), nmemb)))
+        return false;
 
-    memcpy(newItems, items, sizeof(bmItem*) * nmemb);
-    return _bmItemListSetItemsNoCopy(list, newItems, nmemb);
+    memcpy(new_items, items, sizeof(void*) * nmemb);
+    return list_set_items_no_copy(list, new_items, nmemb);
 }
 
-int _bmItemListGrow(struct _bmItemList *list, unsigned int step)
+bool
+list_grow(struct list *list, uint32_t step)
 {
     assert(list);
 
     void *tmp;
-    unsigned int nsize = sizeof(bmItem*) * (list->allocated + step);
+    uint32_t nsize = sizeof(struct bm_item*) * (list->allocated + step);
 
-    if (!list->list || !(tmp = realloc(list->list, nsize))) {
+    if (!list->items || !(tmp = realloc(list->items, nsize))) {
         if (!(tmp = malloc(nsize)))
-            return 0;
+            return false;
 
-        if (list->list) {
-            memcpy(tmp, list->list, sizeof(bmItem*) * list->allocated);
-            free(list->list);
+        if (list->items) {
+            memcpy(tmp, list->items, sizeof(struct bm_item*) * list->allocated);
+            free(list->items);
         }
     }
 
-    list->list = tmp;
+    list->items = tmp;
     list->allocated += step;
-    memset(&list->list[list->count], 0, sizeof(bmItem*) * (list->allocated - list->count));
-    return 1;
+    memset(&list->items[list->count], 0, sizeof(struct bm_item*) * (list->allocated - list->count));
+    return true;
 }
 
-int _bmItemListAddItemAt(struct _bmItemList *list, bmItem *item, unsigned int index)
+bool
+list_add_item_at(struct list *list, void *item, uint32_t index)
 {
-    assert(list);
-    assert(item);
+    assert(list && item);
 
-    if ((!list->list || list->allocated <= list->count) && !_bmItemListGrow(list, 32))
-        return 0;
+    if ((!list->items || list->allocated <= list->count) && !list_grow(list, 32))
+        return false;
 
     if (index + 1 != list->count) {
-        unsigned int i = index;
-        memmove(&list->list[i + 1], &list->list[i], sizeof(bmItem*) * (list->count - i));
+        uint32_t i = index;
+        memmove(&list->items[i + 1], &list->items[i], sizeof(struct bm_item*) * (list->count - i));
     }
 
-    list->list[index] = item;
+    list->items[index] = item;
     list->count++;
-    return 1;
+    return true;
 }
 
-int _bmItemListAddItem(struct _bmItemList *list, bmItem *item)
+bool
+list_add_item(struct list *list, void *item)
 {
     assert(list);
-    return _bmItemListAddItemAt(list, item, list->count);
+    return list_add_item_at(list, item, list->count);
 }
 
-int _bmItemListRemoveItemAt(struct _bmItemList *list, unsigned int index)
+bool
+list_remove_item_at(struct list *list, uint32_t index)
 {
     assert(list);
 
-    unsigned int i = index;
-    if (!list->list || list->count <= i)
-        return 0;
+    uint32_t i = index;
+    if (!list->items || list->count <= i)
+        return false;
 
-    memmove(&list->list[i], &list->list[i], sizeof(bmItem*) * (list->count - i));
-    return 1;
+    memmove(&list->items[i], &list->items[i], sizeof(void*) * (list->count - i));
+    return true;
 }
 
-int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item)
+bool
+list_remove_item(struct list *list, const void *item)
 {
-    unsigned int i;
-    for (i = 0; i < list->count && list->list[i] != item; ++i);
-    return _bmItemListRemoveItemAt(list, i);
+    uint32_t i;
+    for (i = 0; i < list->count && list->items[i] != item; ++i);
+    return list_remove_item_at(list, i);
 }
 
 /* vim: set ts=8 sw=4 tw=0 :*/
diff --git a/lib/menu.c b/lib/menu.c
index b7ab021..a8390fa 100644
--- a/lib/menu.c
+++ b/lib/menu.c
@@ -7,359 +7,241 @@
 /**
  * Filter function map.
  */
-static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb) = {
-    _bmFilterDmenu, /* BM_FILTER_DMENU */
-    _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */
+static struct bm_item** (*filter_func[BM_FILTER_MODE_LAST])(struct bm_menu *menu, bool addition, uint32_t *out_nmemb) = {
+    bm_filter_dmenu, /* BM_FILTER_DMENU */
+    bm_filter_dmenu_case_insensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */
 };
 
-int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item)
+bool
+bm_menu_item_is_selected(const struct bm_menu *menu, const struct bm_item *item)
 {
     assert(menu);
     assert(item);
 
-    unsigned int i, count;
-    bmItem **items = bmMenuGetSelectedItems(menu, &count);
+    uint32_t i, count;
+    struct bm_item **items = bm_menu_get_selected_items(menu, &count);
     for (i = 0; i < count && items[i] != item; ++i);
     return (i < count);
 }
 
-/**
- * Create new bmMenu instance.
- *
- * @param drawMode Render method to be used for this menu instance.
- * @return bmMenu for new menu instance, **NULL** if creation failed.
- */
-bmMenu* bmMenuNew(bmDrawMode drawMode)
+struct bm_menu*
+bm_menu_new(const char *renderer)
 {
-    bmMenu *menu = calloc(1, sizeof(bmMenu));
-
-    menu->drawMode = drawMode;
-
-    if (!menu)
+    struct bm_menu *menu;
+    if (!(menu = calloc(1, sizeof(struct bm_menu))))
         return NULL;
 
-    int status = 1;
+    uint32_t count;
+    const struct bm_renderer **renderers = bm_get_renderers(&count);
 
-    switch (menu->drawMode) {
-        case BM_DRAW_MODE_CURSES:
-            status = _bmDrawCursesInit(&menu->renderApi);
-            break;
+    bool status = false;
+    for (uint32_t i = 0; i < count; ++i) {
+        if (renderer && strcmp(renderer, renderers[i]->name))
+            continue;
 
-        default: break;
+        if (bm_renderer_activate((struct bm_renderer*)renderers[i])) {
+            status = true;
+            menu->renderer = renderers[i];
+            break;
+        }
     }
 
-    if (status == 0) {
-        bmMenuFree(menu);
+    if (!status) {
+        bm_menu_free(menu);
         return NULL;
     }
 
     return menu;
 }
 
-/**
- * Release bmMenu instance.
- *
- * @param menu bmMenu instance to be freed from memory.
- */
-void bmMenuFree(bmMenu *menu)
+void
+bm_menu_free(struct bm_menu *menu)
 {
     assert(menu);
 
-    if (menu->renderApi.free)
-        menu->renderApi.free();
-
-    if (menu->title)
-        free(menu->title);
+    if (menu->renderer && menu->renderer->api.destructor)
+        menu->renderer->api.destructor();
 
-    if (menu->filter)
-        free(menu->filter);
+    free(menu->title);
+    free(menu->filter);
+    free(menu->old_filter);
 
-    if (menu->oldFilter)
-        free(menu->oldFilter);
-
-    bmMenuFreeItems(menu);
+    bm_menu_free_items(menu);
     free(menu);
 }
 
-/**
- * Release items inside bmMenu instance.
- *
- * @param menu bmMenu instance which items will be freed from memory.
- */
-void bmMenuFreeItems(bmMenu *menu)
+void
+bm_menu_free_items(struct bm_menu *menu)
 {
     assert(menu);
-    _bmItemListFreeList(&menu->selection);
-    _bmItemListFreeList(&menu->filtered);
-    _bmItemListFreeItems(&menu->items);
+    list_free_list(&menu->selection);
+    list_free_list(&menu->filtered);
+    list_free_items(&menu->items, (list_free_fun)bm_item_free);
 }
 
-/**
- * Set userdata pointer to bmMenu instance.
- * Userdata will be carried unmodified by the instance.
- *
- * @param menu bmMenu instance where to set userdata pointer.
- * @param userdata Pointer to userdata.
- */
-void bmMenuSetUserdata(bmMenu *menu, void *userdata)
+void
+bm_menu_set_userdata(struct bm_menu *menu, void *userdata)
 {
     assert(menu);
     menu->userdata = userdata;
 }
 
-/**
- * Get userdata pointer from bmMenu instance.
- *
- * @param menu bmMenu instance which userdata pointer to get.
- * @return Pointer for unmodified userdata.
- */
-void* bmMenuGetUserdata(bmMenu *menu)
+void*
+bm_menu_get_userdata(struct bm_menu *menu)
 {
     assert(menu);
     return menu->userdata;
 }
 
-/**
- * Set filter text to bmMenu instance.
- *
- * @param menu bmMenu instance where to set filter.
- * @param filter Null terminated C "string" to act as filter.
- */
-void bmMenuSetFilter(bmMenu *menu, const char *filter)
+void
+bm_menu_set_filter(struct bm_menu *menu, const char *filter)
 {
     assert(menu);
 
-    if (menu->filter)
-        free(menu->filter);
-
-    menu->filter = (filter ? _bmStrdup(filter) : NULL);
-    menu->filterSize = (filter ? strlen(filter) : 0);
+    free(menu->filter);
+    menu->filter = (filter ? bm_strdup(filter) : NULL);
+    menu->filter_size = (filter ? strlen(filter) : 0);
 }
 
-/**
- * Get filter text from  bmMenu instance.
- *
- * @param menu bmMenu instance where to get filter.
- * @return Const pointer to current filter text, may be **NULL** if empty.
- */
-const char* bmMenuGetFilter(bmMenu *menu)
+const char*
+bm_menu_get_filter(struct bm_menu *menu)
 {
     assert(menu);
     return menu->filter;
 }
 
-/**
- * Set active filter mode to bmMenu instance.
- *
- * @param menu bmMenu instance where to set filter mode.
- * @param mode bmFilterMode constant.
- */
-void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode)
+void
+bm_menu_set_filter_mode(struct bm_menu *menu, enum bm_filter_mode mode)
 {
     assert(menu);
-    menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode);
+    menu->filter_mode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode);
 }
 
-/**
- * Get active filter mode from bmMenu instance.
- *
- * @param menu bmMenu instance where to get filter mode.
- * @return bmFilterMode constant.
- */
-bmFilterMode bmMenuGetFilterMode(const bmMenu *menu)
+enum bm_filter_mode
+bm_menu_get_filter_mode(const struct bm_menu *menu)
 {
     assert(menu);
-    return menu->filterMode;
+    return menu->filter_mode;
 }
 
-/**
- * Set selection wrapping on/off.
- *
- * @param menu bmMenu instance where to toggle selection wrapping.
- * @param int 1 == on, 0 == off.
- */
-void bmMenuSetWrap(bmMenu *menu, int wrap)
+void
+bm_menu_set_wrap(struct bm_menu *menu, bool wrap)
 {
     assert(menu);
-    menu->wrap = (wrap ? 1 : 0);
+    menu->wrap = wrap;
 }
 
-/**
- * Get selection wrapping state.
- *
- * @param menu bmMenu instance where to get selection wrapping state.
- * @return int for wrap state.
- */
-int bmMenuGetWrap(const bmMenu *menu)
+bool
+bm_menu_get_wrap(const struct bm_menu *menu)
 {
     assert(menu);
     return menu->wrap;
 }
 
-/**
- * Set title to bmMenu instance.
- *
- * @param menu bmMenu instance where to set title.
- * @param title C "string" to set as title, can be **NULL** for empty title.
- */
-int bmMenuSetTitle(bmMenu *menu, const char *title)
+bool
+bm_menu_set_title(struct bm_menu *menu, const char *title)
 {
     assert(menu);
 
     char *copy = NULL;
-    if (title && !(copy = _bmStrdup(title)))
-        return 0;
-
-    if (menu->title)
-        free(menu->title);
+    if (title && !(copy = bm_strdup(title)))
+        return false;
 
+    free(menu->title);
     menu->title = copy;
-    return 1;
+    return true;
 }
 
-/**
- * Get title from bmMenu instance.
- *
- * @param menu bmMenu instance where to get title from.
- * @return Pointer to null terminated C "string", can be **NULL** for empty title.
- */
-const char* bmMenuGetTitle(const bmMenu *menu)
+const char*
+bm_menu_get_title(const struct bm_menu *menu)
 {
     assert(menu);
     return menu->title;
 }
 
-/**
- * Add item to bmMenu instance at specific index.
- *
- * @param menu bmMenu instance where item will be added.
- * @param item bmItem instance to add.
- * @param index Index where item will be added.
- * @return 1 on successful add, 0 on failure.
- */
-int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index)
+bool
+bm_menu_add_items_at(struct bm_menu *menu, struct bm_item *item, uint32_t index)
 {
     assert(menu);
-    return _bmItemListAddItemAt(&menu->items, item, index);
+    return list_add_item_at(&menu->items, item, index);
 }
 
-/**
- * Add item to bmMenu instance.
- *
- * @param menu bmMenu instance where item will be added.
- * @param item bmItem instance to add.
- * @return 1 on successful add, 0 on failure.
- */
-int bmMenuAddItem(bmMenu *menu, bmItem *item)
+bool
+bm_menu_add_item(struct bm_menu *menu, struct bm_item *item)
 {
-    return _bmItemListAddItem(&menu->items, item);
+    return list_add_item(&menu->items, item);
 }
 
-/**
- * Remove item from bmMenu instance at specific index.
- *
- * @warning The item won't be freed, use bmItemFree to do that.
- *
- * @param menu bmMenu instance from where item will be removed.
- * @param index Index of item to remove.
- * @return 1 on successful add, 0 on failure.
- */
-int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index)
+bool
+bm_menu_remove_item_at(struct bm_menu *menu, uint32_t index)
 {
     assert(menu);
 
-    if (!menu->items.list || menu->items.count <= index)
+    if (!menu->items.items || menu->items.count <= index)
         return 0;
 
-    bmItem *item = menu->items.list[index];
-    int ret = _bmItemListRemoveItemAt(&menu->items, index);
+    bool ret = list_remove_item_at(&menu->items, index);
 
     if (ret) {
-        _bmItemListRemoveItem(&menu->selection, item);
-        _bmItemListRemoveItem(&menu->filtered, item);
+        struct bm_item *item = ((struct bm_item**)menu->items.items)[index];
+        list_remove_item(&menu->selection, item);
+        list_remove_item(&menu->filtered, item);
     }
 
     return ret;
 }
 
-/**
- * Remove item from bmMenu instance.
- *
- * @warning The item won't be freed, use bmItemFree to do that.
- *
- * @param menu bmMenu instance from where item will be removed.
- * @param item bmItem instance to remove.
- * @return 1 on successful add, 0 on failure.
- */
-int bmMenuRemoveItem(bmMenu *menu, bmItem *item)
+bool
+bm_menu_remove_item(struct bm_menu *menu, struct bm_item *item)
 {
     assert(menu);
 
-    int ret = _bmItemListRemoveItem(&menu->items, item);
+    bool ret = list_remove_item(&menu->items, item);
 
     if (ret) {
-        _bmItemListRemoveItem(&menu->selection, item);
-        _bmItemListRemoveItem(&menu->filtered, item);
+        list_remove_item(&menu->selection, item);
+        list_remove_item(&menu->filtered, item);
     }
 
     return ret;
 }
 
-/**
- * Highlight item in menu by index.
- *
- * @param menu bmMenu instance from where to highlight item.
- * @param index Index of item to highlight.
- * @return 1 on successful highlight, 0 on failure.
- */
-int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index)
+bool
+bm_menu_set_highlighted_index(struct bm_menu *menu, uint32_t index)
 {
     assert(menu);
 
-    unsigned int itemsCount;
-    bmMenuGetFilteredItems(menu, &itemsCount);
+    uint32_t count;
+    bm_menu_get_filtered_items(menu, &count);
 
-    if (itemsCount <= index)
+    if (count <= index)
         return 0;
 
     return (menu->index = index);
 }
 
-/**
- * Highlight item in menu.
- *
- * @param menu bmMenu instance from where to highlight item.
- * @param item bmItem instance to highlight.
- * @return 1 on successful highlight, 0 on failure.
- */
-int bmMenuSetHighlighted(bmMenu *menu, bmItem *item)
+bool
+bm_menu_set_highlighted_item(struct bm_menu *menu, struct bm_item *item)
 {
     assert(menu);
 
-    unsigned int i, itemsCount;
-    bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount);
-    for (i = 0; i < itemsCount && items[i] != item; ++i);
+    uint32_t i, count;
+    struct bm_item **items = bm_menu_get_filtered_items(menu, &count);
+    for (i = 0; i < count && items[i] != item; ++i);
 
-    if (itemsCount <= i)
+    if (count <= i)
         return 0;
 
     return (menu->index = i);
 }
 
-/**
- * Get highlighted item from bmMenu instance.
- *
- * @warning The pointer returned by this function may be invalid after items change.
- *
- * @param menu bmMenu instance from where to get highlighted item.
- * @return Selected bmItem instance, **NULL** if none highlighted.
- */
-bmItem* bmMenuGetHighlightedItem(const bmMenu *menu)
+struct bm_item*
+bm_menu_get_highlighted_item(const struct bm_menu *menu)
 {
     assert(menu);
 
-    unsigned int count;
-    bmItem **items = bmMenuGetFilteredItems(menu, &count);
+    uint32_t count;
+    struct bm_item **items = bm_menu_get_filtered_items(menu, &count);
 
     if (!items || count <= menu->index)
         return NULL;
@@ -367,244 +249,169 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu)
     return items[menu->index];
 }
 
-/**
- * Set selected items to bmMenu instance.
- *
- * @param menu bmMenu instance where items will be set.
- * @param items Array of bmItem pointers to set.
- * @param nmemb Total count of items in array.
- * @return 1 on successful set, 0 on failure.
- */
-int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb)
+bool
+bm_menu_set_selected_items(struct bm_menu *menu, struct bm_item **items, uint32_t nmemb)
 {
     assert(menu);
 
-    bmItem **newItems;
-    if (!(newItems = calloc(sizeof(bmItem*), nmemb)))
+    struct bm_item **new_items;
+    if (!(new_items = calloc(sizeof(struct bm_item*), nmemb)))
         return 0;
 
-    memcpy(newItems, items, sizeof(bmItem*) * nmemb);
-    return _bmItemListSetItemsNoCopy(&menu->selection, newItems, nmemb);
+    memcpy(new_items, items, sizeof(struct bm_item*) * nmemb);
+    return list_set_items_no_copy(&menu->selection, new_items, nmemb);
 }
 
-/**
- * Get selected items from bmMenu instance.
- *
- * @warning The pointer returned by this function may be invalid after selection or items change.
- *
- * @param menu bmMenu instance from where to get selected items.
- * @param outNmemb Reference to unsigned int where total count of returned items will be stored.
- * @return Pointer to array of bmItem pointers.
- */
-bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb)
+struct bm_item**
+bm_menu_get_selected_items(const struct bm_menu *menu, uint32_t *out_nmemb)
 {
     assert(menu);
-    return _bmItemListGetItems(&menu->selection, outNmemb);
+    return list_get_items(&menu->selection, out_nmemb);
 }
 
-/**
- * Set items to bmMenu instance.
- * Will replace all the old items on bmMenu instance.
- *
- * If items is **NULL**, or nmemb is zero, all items will be freed from the menu.
- *
- * @param menu bmMenu instance where items will be set.
- * @param items Array of bmItem pointers to set.
- * @param nmemb Total count of items in array.
- * @return 1 on successful set, 0 on failure.
- */
-int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb)
+bool
+bm_menu_set_items(struct bm_menu *menu, const struct bm_item **items, uint32_t nmemb)
 {
     assert(menu);
 
-    int ret = _bmItemListSetItems(&menu->items, items, nmemb);
+    bool ret = list_set_items(&menu->items, items, nmemb, (list_free_fun)bm_item_free);
 
     if (ret) {
-        _bmItemListFreeList(&menu->selection);
-        _bmItemListFreeList(&menu->filtered);
+        list_free_list(&menu->selection);
+        list_free_list(&menu->filtered);
     }
 
     return ret;
 }
 
-/**
- * Get items from bmMenu instance.
- *
- * @warning The pointer returned by this function may be invalid after removing or adding new items.
- *
- * @param menu bmMenu instance from where to get items.
- * @param outNmemb Reference to unsigned int where total count of returned items will be stored.
- * @return Pointer to array of bmItem pointers.
- */
-bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb)
+struct bm_item**
+bm_menu_get_items(const struct bm_menu *menu, uint32_t *out_nmemb)
 {
     assert(menu);
-    return _bmItemListGetItems(&menu->items, outNmemb);
+    return list_get_items(&menu->items, out_nmemb);
 }
 
-/**
- * Get filtered (displayed) items from bmMenu instance.
- *
- * @warning The pointer returned by this function _will_ be invalid when menu internally filters its list again.
- *          Do not store this pointer.
- *
- * @param menu bmMenu instance from where to get filtered items.
- * @param outNmemb Reference to unsigned int where total count of returned items will be stored.
- * @return Pointer to array of bmItem pointers.
- */
-bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb)
+struct bm_item**
+bm_menu_get_filtered_items(const struct bm_menu *menu, uint32_t *out_nmemb)
 {
     assert(menu);
 
     if (menu->filter && strlen(menu->filter))
-        return _bmItemListGetItems(&menu->filtered, outNmemb);
+        return list_get_items(&menu->filtered, out_nmemb);
 
-    return _bmItemListGetItems(&menu->items, outNmemb);
+    return list_get_items(&menu->items, out_nmemb);
 }
 
-/**
- * Render bmMenu instance using chosen draw method.
- *
- * @param menu bmMenu instance to be rendered.
- */
-void bmMenuRender(const bmMenu *menu)
+void
+bm_menu_render(const struct bm_menu *menu)
 {
     assert(menu);
 
-    if (menu->renderApi.render)
-        menu->renderApi.render(menu);
+    if (menu->renderer->api.render)
+        menu->renderer->api.render(menu);
 }
 
-/**
- * Trigger filtering of menu manually.
- * This is useful when adding new items and want to dynamically see them filtered.
- *
- * Do note that filtering might be heavy, so you should only call it after batch manipulation of items.
- * Not after manipulation of each single item.
- *
- * @param menu bmMenu instance which to filter.
- */
-void bmMenuFilter(bmMenu *menu)
+void
+bm_menu_filter(struct bm_menu *menu)
 {
     assert(menu);
 
     char addition = 0;
     size_t len = (menu->filter ? strlen(menu->filter) : 0);
 
-    if (!len || !menu->items.list || menu->items.count <= 0) {
-        _bmItemListFreeList(&menu->filtered);
-
-        if (menu->oldFilter)
-            free(menu->oldFilter);
-
-        menu->oldFilter = NULL;
+    if (!len || !menu->items.items || menu->items.count <= 0) {
+        list_free_list(&menu->filtered);
+        free(menu->old_filter);
+        menu->old_filter = NULL;
         return;
     }
 
-    if (menu->oldFilter) {
-        size_t oldLen = strlen(menu->oldFilter);
-        addition = (oldLen < len && !memcmp(menu->oldFilter, menu->filter, oldLen));
+    if (menu->old_filter) {
+        size_t oldLen = strlen(menu->old_filter);
+        addition = (oldLen < len && !memcmp(menu->old_filter, menu->filter, oldLen));
     }
 
-    if (menu->oldFilter && addition && menu->filtered.count <= 0)
+    if (menu->old_filter && addition && menu->filtered.count <= 0)
         return;
 
-    if (menu->oldFilter && !strcmp(menu->filter, menu->oldFilter))
+    if (menu->old_filter && !strcmp(menu->filter, menu->old_filter))
         return;
 
-    unsigned int count;
-    bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count);
+    uint32_t count;
+    struct bm_item **filtered = filter_func[menu->filter_mode](menu, addition, &count);
 
-    _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count);
+    list_set_items_no_copy(&menu->filtered, filtered, count);
     menu->index = 0;
 
-    if (menu->oldFilter)
-        free(menu->oldFilter);
-
-    menu->oldFilter = _bmStrdup(menu->filter);
+    free(menu->old_filter);
+    menu->old_filter = bm_strdup(menu->filter);
 }
 
-/**
- * Poll key and unicode from underlying UI toolkit.
- *
- * This function will block on @link ::bmDrawMode BM_DRAW_MODE_CURSES @endlink draw mode.
- *
- * @param menu bmMenu instance from which to poll.
- * @param outUnicode Reference to unsigned int.
- * @return bmKey for polled key.
- */
-bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode)
+enum bm_key
+bm_menu_poll_key(struct bm_menu *menu, uint32_t *out_unicode)
 {
-    assert(menu);
-    assert(outUnicode);
+    assert(menu && out_unicode);
 
-    *outUnicode = 0;
-    bmKey key = BM_KEY_NONE;
+    *out_unicode = 0;
+    enum bm_key key = BM_KEY_NONE;
 
-    if (menu->renderApi.getKey)
-        key = menu->renderApi.getKey(outUnicode);
+    if (menu->renderer->api.poll_key)
+        key = menu->renderer->api.poll_key(out_unicode);
 
     return key;
 }
 
-/**
- * Advances menu logic with key and unicode as input.
- *
- * @param menu bmMenu instance to be advanced.
- * @param key Key input that will advance menu logic.
- * @param unicode Unicode input that will advance menu logic.
- * @return bmRunResult for menu state.
- */
-bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
+enum bm_run_result
+bm_menu_run_with_key(struct bm_menu *menu, enum bm_key key, uint32_t unicode)
 {
     assert(menu);
 
-    unsigned int itemsCount;
-    bmMenuGetFilteredItems(menu, &itemsCount);
+    uint32_t count;
+    bm_menu_get_filtered_items(menu, &count);
 
-    unsigned int displayed = 0;
-    if (menu->renderApi.displayedCount)
-        displayed = menu->renderApi.displayedCount(menu);
+    uint32_t displayed = 0;
+    if (menu->renderer->api.get_displayed_count)
+        displayed = menu->renderer->api.get_displayed_count(menu);
 
     if (!displayed)
-        displayed = itemsCount;
+        displayed = count;
 
     switch (key) {
         case BM_KEY_LEFT:
             if (menu->filter) {
-                unsigned int oldCursor = menu->cursor;
-                menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor);
-                menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor);
+                uint32_t oldCursor = menu->cursor;
+                menu->cursor -= bm_utf8_rune_prev(menu->filter, menu->cursor);
+                menu->curses_cursor -= bm_utf8_rune_width(menu->filter + menu->cursor, oldCursor - menu->cursor);
             }
             break;
 
         case BM_KEY_RIGHT:
             if (menu->filter) {
-                unsigned int oldCursor = menu->cursor;
-                menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor);
-                menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor);
+                uint32_t oldCursor = menu->cursor;
+                menu->cursor += bm_utf8_rune_next(menu->filter, menu->cursor);
+                menu->curses_cursor += bm_utf8_rune_width(menu->filter + oldCursor, menu->cursor - oldCursor);
             }
             break;
 
         case BM_KEY_HOME:
-            menu->cursesCursor = menu->cursor = 0;
+            menu->curses_cursor = menu->cursor = 0;
             break;
 
         case BM_KEY_END:
             menu->cursor = (menu->filter ? strlen(menu->filter) : 0);
-            menu->cursesCursor = (menu->filter ? _bmUtf8StringScreenWidth(menu->filter) : 0);
+            menu->curses_cursor = (menu->filter ? bm_utf8_string_screen_width(menu->filter) : 0);
             break;
 
         case BM_KEY_UP:
             if (menu->index > 0) {
                 menu->index--;
             } else if (menu->wrap) {
-                menu->index = itemsCount - 1;
+                menu->index = count - 1;
             }
             break;
 
         case BM_KEY_DOWN:
-            if (menu->index < itemsCount - 1) {
+            if (menu->index < count - 1) {
                 menu->index++;
             } else if (menu->wrap) {
                 menu->index = 0;
@@ -616,7 +423,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
             break;
 
         case BM_KEY_PAGE_DOWN:
-            menu->index = (menu->index + displayed >= itemsCount ? itemsCount - 1 : menu->index + (displayed - 1));
+            menu->index = (menu->index + displayed >= count ? count - 1 : menu->index + (displayed - 1));
             break;
 
         case BM_KEY_SHIFT_PAGE_UP:
@@ -624,28 +431,28 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
             break;
 
         case BM_KEY_SHIFT_PAGE_DOWN:
-            menu->index = itemsCount - 1;
+            menu->index = count - 1;
             break;
 
         case BM_KEY_BACKSPACE:
             if (menu->filter) {
                 size_t width;
-                menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width);
-                menu->cursesCursor -= width;
+                menu->cursor -= bm_utf8_rune_remove(menu->filter, menu->cursor, &width);
+                menu->curses_cursor -= width;
             }
             break;
 
         case BM_KEY_DELETE:
             if (menu->filter)
-                _bmUtf8RuneRemove(menu->filter, menu->cursor + 1, NULL);
+                bm_utf8_rune_remove(menu->filter, menu->cursor + 1, NULL);
             break;
 
         case BM_KEY_LINE_DELETE_LEFT:
             if (menu->filter) {
                 while (menu->cursor > 0) {
                     size_t width;
-                    menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width);
-                    menu->cursesCursor -= width;
+                    menu->cursor -= bm_utf8_rune_remove(menu->filter, menu->cursor, &width);
+                    menu->curses_cursor -= width;
                 }
             }
             break;
@@ -658,19 +465,19 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
         case BM_KEY_WORD_DELETE:
            if (menu->filter) {
                 while (menu->cursor < strlen(menu->filter) && !isspace(menu->filter[menu->cursor])) {
-                    unsigned int oldCursor = menu->cursor;
-                    menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor);
-                    menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor);
+                    uint32_t oldCursor = menu->cursor;
+                    menu->cursor += bm_utf8_rune_next(menu->filter, menu->cursor);
+                    menu->curses_cursor += bm_utf8_rune_width(menu->filter + oldCursor, menu->cursor - oldCursor);
                 }
                 while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])) {
-                    unsigned int oldCursor = menu->cursor;
-                    menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor);
-                    menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor);
+                    uint32_t oldCursor = menu->cursor;
+                    menu->cursor -= bm_utf8_rune_prev(menu->filter, menu->cursor);
+                    menu->curses_cursor -= bm_utf8_rune_width(menu->filter + menu->cursor, oldCursor - menu->cursor);
                 }
                 while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) {
                     size_t width;
-                    menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width);
-                    menu->cursesCursor -= width;
+                    menu->cursor -= bm_utf8_rune_remove(menu->filter, menu->cursor, &width);
+                    menu->curses_cursor -= width;
                 }
             }
             break;
@@ -678,19 +485,19 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
         case BM_KEY_UNICODE:
             {
                 size_t width;
-                menu->cursor += _bmUnicodeInsert(&menu->filter, &menu->filterSize, menu->cursor, unicode, &width);
-                menu->cursesCursor += width;
+                menu->cursor += bm_unicode_insert(&menu->filter, &menu->filter_size, menu->cursor, unicode, &width);
+                menu->curses_cursor += width;
             }
             break;
 
         case BM_KEY_TAB:
             {
                 const char *text;
-                bmItem *highlighted = bmMenuGetHighlightedItem(menu);
-                if (highlighted && (text = bmItemGetText(highlighted))) {
-                    bmMenuSetFilter(menu, text);
+                struct bm_item *highlighted = bm_menu_get_highlighted_item(menu);
+                if (highlighted && (text = bm_item_get_text(highlighted))) {
+                    bm_menu_set_filter(menu, text);
                     menu->cursor = (menu->filter ? strlen(menu->filter) : 0);
-                    menu->cursesCursor = (menu->filter ? _bmUtf8StringScreenWidth(menu->filter) : 0);
+                    menu->curses_cursor = (menu->filter ? bm_utf8_string_screen_width(menu->filter) : 0);
                 }
             }
             break;
@@ -698,21 +505,21 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
         case BM_KEY_CONTROL_RETURN:
         case BM_KEY_RETURN:
             {
-                bmItem *highlighted = bmMenuGetHighlightedItem(menu);
-                if (highlighted && !_bmMenuItemIsSelected(menu, highlighted))
-                    _bmItemListAddItem(&menu->selection, highlighted);
+                struct bm_item *highlighted = bm_menu_get_highlighted_item(menu);
+                if (highlighted && !bm_menu_item_is_selected(menu, highlighted))
+                    list_add_item(&menu->selection, highlighted);
             }
             break;
 
         case BM_KEY_SHIFT_RETURN:
         case BM_KEY_ESCAPE:
-            _bmItemListFreeList(&menu->selection);
+            list_free_list(&menu->selection);
             break;
 
         default: break;
     }
 
-    bmMenuFilter(menu);
+    bm_menu_filter(menu);
 
     switch (key) {
         case BM_KEY_SHIFT_RETURN:
diff --git a/lib/renderers/CMakeLists.txt b/lib/renderers/CMakeLists.txt
new file mode 100644
index 0000000..0bc4733
--- /dev/null
+++ b/lib/renderers/CMakeLists.txt
@@ -0,0 +1,10 @@
+SET(RENDERERS
+    "curses"
+)
+
+SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/renderers)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..)
+FOREACH (renderer ${RENDERERS})
+    ADD_SUBDIRECTORY(${renderer})
+ENDFOREACH()
diff --git a/lib/renderers/curses/CMakeLists.txt b/lib/renderers/curses/CMakeLists.txt
new file mode 100644
index 0000000..1d2abec
--- /dev/null
+++ b/lib/renderers/curses/CMakeLists.txt
@@ -0,0 +1,6 @@
+SET(CURSES_NEED_NCURSES TRUE)
+FIND_PACKAGE(Curses)
+ADD_LIBRARY(bemenu-renderer-curses SHARED curses.c)
+SET_TARGET_PROPERTIES(bemenu-renderer-curses PROPERTIES PREFIX "")
+TARGET_LINK_LIBRARIES(bemenu-renderer-curses ${CURSES_LIBRARY})
+INSTALL(TARGETS bemenu-renderer-curses DESTINATION lib/bemenu)
diff --git a/lib/renderers/curses/curses.c b/lib/renderers/curses/curses.c
new file mode 100644
index 0000000..0ba763e
--- /dev/null
+++ b/lib/renderers/curses/curses.c
@@ -0,0 +1,379 @@
+#if __APPLE__
+#   define _C99_SOURCE
+#   include <stdio.h> /* vsnprintf */
+#   undef _C99_SOURCE
+#endif
+
+#define _XOPEN_SOURCE 500
+#include <signal.h> /* sigaction */
+#include <stdarg.h> /* vsnprintf */
+#undef _XOPEN_SOURCE
+
+#include "internal.h"
+
+#include <wchar.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <ncurses.h>
+#include <dlfcn.h>
+#include <assert.h>
+
+#if _WIN32
+static const char *TTY = "CON";
+#else
+static const char *TTY = "/dev/tty";
+#endif
+
+/**
+ * Dynamically loaded curses API.
+ */
+static struct curses {
+    WINDOW *stdscr;
+    struct sigaction abrt_action;
+    struct sigaction segv_action;
+    struct sigaction winch_action;
+    int old_stdin;
+    int old_stdout;
+} curses;
+
+static void
+terminate(void)
+{
+    if (!curses.stdscr)
+        return;
+
+    freopen(TTY, "w", stdout);
+
+    refresh();
+    endwin();
+
+    dup2(curses.old_stdin, STDIN_FILENO);
+    dup2(curses.old_stdout, STDOUT_FILENO);
+    close(curses.old_stdin);
+    close(curses.old_stdout);
+
+    curses.stdscr = NULL;
+}
+
+static void
+crash_handler(int sig)
+{
+    (void)sig;
+    terminate();
+}
+
+static void resize_handler(int sig)
+{
+    (void)sig;
+    if (!curses.stdscr)
+        return;
+
+    endwin();
+    refresh();
+}
+
+static bool
+resize_buffer(char **buffer, size_t *osize, size_t nsize)
+{
+    assert(buffer);
+    assert(osize);
+
+    if (nsize == 0 || nsize <= *osize)
+        return false;
+
+    void *tmp;
+    if (!*buffer || !(tmp = realloc(*buffer, nsize))) {
+        if (!(tmp = malloc(nsize)))
+            return 0;
+
+        if (*buffer) {
+            memcpy(tmp, *buffer, *osize);
+            free(*buffer);
+        }
+    }
+
+    *buffer = tmp;
+    *osize = nsize;
+    return true;
+}
+
+#if __GNUC__
+__attribute__((format(printf, 3, 4)))
+#endif
+static void
+draw_line(int32_t pair, int32_t y, const char *format, ...)
+{
+    static size_t blen = 0;
+    static char *buffer = NULL;
+
+    size_t ncols;
+    if ((ncols = getmaxx(curses.stdscr)) <= 0)
+        return;
+
+    va_list args;
+    va_start(args, format);
+    size_t nlen = vsnprintf(NULL, 0, format, args);
+    va_end(args);
+
+    if ((!buffer || nlen > blen) && !resize_buffer(&buffer, &blen, nlen + 1))
+        return;
+
+    va_start(args, format);
+    vsnprintf(buffer, blen - 1, format, args);
+    va_end(args);
+
+    size_t dw = 0, i = 0;
+    while (dw < ncols && i < nlen) {
+        if (buffer[i] == '\t') buffer[i] = ' ';
+        int32_t next = bm_utf8_rune_next(buffer, i);
+        dw += bm_utf8_rune_width(buffer + i, next);
+        i += (next ? next : 1);
+    }
+
+    if (dw < ncols) {
+        /* line is too short, widen it */
+        size_t offset = i + (ncols - dw);
+        if (blen <= offset && !resize_buffer(&buffer, &blen, offset + 1))
+             return;
+
+        memset(buffer + nlen, ' ', offset - nlen);
+        buffer[offset] = 0;
+    } else if (i < blen) {
+        /* line is too long, shorten it */
+        i -= bm_utf8_rune_prev(buffer, i - (dw - ncols)) - 1;
+        size_t cc = dw - (dw - ncols);
+
+        size_t offset = i - (dw - ncols) + (ncols - cc) + 1;
+        if (blen <= offset) {
+            int32_t diff = offset - blen + 1;
+            if (!resize_buffer(&buffer, &blen, blen + diff))
+                return;
+        }
+
+        memset(buffer + i - (dw - ncols), ' ', (ncols - cc) + 1);
+        buffer[offset] = 0;
+    }
+
+    if (pair > 0)
+        attron(COLOR_PAIR(pair));
+
+    mvprintw(y, 0, "%s", buffer);
+
+    if (pair > 0)
+        attroff(COLOR_PAIR(pair));
+}
+
+static void
+render(const struct bm_menu *menu)
+{
+    if (!curses.stdscr) {
+        curses.old_stdin = dup(STDIN_FILENO);
+        curses.old_stdout = dup(STDOUT_FILENO);
+
+        freopen(TTY, "w", stdout);
+        freopen(TTY, "r", stdin);
+
+        setlocale(LC_CTYPE, "");
+
+        if ((curses.stdscr = initscr()) == NULL)
+            return;
+
+        ESCDELAY = 25;
+        flushinp();
+        keypad(curses.stdscr, true);
+        curs_set(1);
+        noecho();
+        raw();
+
+        start_color();
+        use_default_colors();
+        init_pair(1, COLOR_BLACK, COLOR_RED);
+        init_pair(2, COLOR_RED, -1);
+    }
+
+    const uint32_t lines = getmaxy(curses.stdscr);
+    erase();
+
+    uint32_t ncols = getmaxx(curses.stdscr);
+    uint32_t title_len = (menu->title ? strlen(menu->title) + 1 : 0);
+
+    if (title_len >= ncols)
+        title_len = 0;
+
+    uint32_t ccols = ncols - title_len - 1;
+    uint32_t dcols = 0, doffset = menu->cursor;
+
+    while (doffset > 0 && dcols < ccols) {
+        int prev = bm_utf8_rune_prev(menu->filter, doffset);
+        dcols += bm_utf8_rune_width(menu->filter + doffset - prev, prev);
+        doffset -= (prev ? prev : 1);
+    }
+
+    draw_line(0, 0, "%*s%s", title_len, "", (menu->filter ? menu->filter + doffset : ""));
+
+    if (menu->title && title_len > 0) {
+        attron(COLOR_PAIR(1));
+        mvprintw(0, 0, menu->title);
+        attroff(COLOR_PAIR(1));
+    }
+
+    uint32_t count, cl = 1;
+    struct bm_item **items = bm_menu_get_filtered_items(menu, &count);
+    for (uint32_t i = (menu->index / (lines - 1)) * (lines - 1); i < count && cl < lines; ++i) {
+        bool highlighted = (items[i] == bm_menu_get_highlighted_item(menu));
+        int32_t color = (highlighted ? 2 : (bm_menu_item_is_selected(menu, items[i]) ? 1 : 0));
+        draw_line(color, cl++, "%s%s", (highlighted ? ">> " : "   "), (items[i]->text ? items[i]->text : ""));
+    }
+
+    move(0, title_len + (menu->curses_cursor < ccols ? menu->curses_cursor : ccols));
+    refresh();
+}
+
+static uint32_t
+get_displayed_count(const struct bm_menu *menu)
+{
+    (void)menu;
+    return (curses.stdscr ? getmaxy(curses.stdscr) : 0);
+}
+
+static enum bm_key
+poll_key(uint32_t *unicode)
+{
+    assert(unicode);
+    *unicode = 0;
+
+    if (!curses.stdscr)
+        return BM_KEY_NONE;
+
+    *unicode = getch();
+
+    switch (*unicode) {
+#if KEY_RESIZE
+        case KEY_RESIZE:
+            return BM_KEY_NONE;
+#endif
+
+        case 16: /* C-p */
+        case KEY_UP:
+            return BM_KEY_UP;
+
+        case 14: /* C-n */
+        case KEY_DOWN:
+            return BM_KEY_DOWN;
+
+        case 2: /* C-b */
+        case KEY_LEFT:
+            return BM_KEY_LEFT;
+
+        case 6: /* C-f */
+        case KEY_RIGHT:
+            return BM_KEY_RIGHT;
+
+        case 1: /* C-a */
+        case 391: /* S-Home */
+        case KEY_HOME:
+            return BM_KEY_HOME;
+
+        case 5: /* C-e */
+        case 386: /* S-End */
+        case KEY_END:
+            return BM_KEY_END;
+
+        case KEY_PPAGE: /* Page up */
+            return BM_KEY_PAGE_UP;
+
+        case KEY_NPAGE: /* Page down */
+            return BM_KEY_PAGE_DOWN;
+
+        case 550: /* C-Page up */
+        case 398: /* S-Page up */
+            return BM_KEY_SHIFT_PAGE_UP;
+
+        case 545: /* C-Page down */
+        case 396: /* S-Page down */
+            return BM_KEY_SHIFT_PAGE_DOWN;
+
+        case 8: /* C-h */
+        case 127: /* Delete */
+        case KEY_BACKSPACE:
+            return BM_KEY_BACKSPACE;
+
+        case 4: /* C-d */
+        case KEY_DC:
+            return BM_KEY_DELETE;
+
+        case 383: /* S-Del */
+        case 21: /* C-u */
+            return BM_KEY_LINE_DELETE_LEFT;
+
+        case 11: /* C-k */
+            return BM_KEY_LINE_DELETE_RIGHT;
+
+        case 23: /* C-w */
+            return BM_KEY_WORD_DELETE;
+
+        case 9: /* Tab */
+            return BM_KEY_TAB;
+
+        case 18: /* C-r */
+            return BM_KEY_CONTROL_RETURN;
+
+        case 20: /* C-t */
+        case 331: /* Insert */
+            terminate();
+            return BM_KEY_SHIFT_RETURN;
+
+        case 10: /* Return */
+            terminate();
+            return BM_KEY_RETURN;
+
+        case 7: /* C-g */
+        case 27: /* Escape */
+            terminate();
+            return BM_KEY_ESCAPE;
+
+        default: break;
+    }
+
+    return BM_KEY_UNICODE;
+}
+
+static void
+destructor(void)
+{
+    terminate();
+    sigaction(SIGABRT, &curses.abrt_action, NULL);
+    sigaction(SIGSEGV, &curses.segv_action, NULL);
+    sigaction(SIGWINCH, &curses.winch_action, NULL);
+    memset(&curses, 0, sizeof(curses));
+}
+
+static void
+constructor(void)
+{
+    memset(&curses, 0, sizeof(curses));
+
+    struct sigaction action;
+    memset(&action, 0, sizeof(struct sigaction));
+    action.sa_handler = crash_handler;
+    sigaction(SIGABRT, &action, &curses.abrt_action);
+    sigaction(SIGSEGV, &action, &curses.segv_action);
+
+    action.sa_handler = resize_handler;
+    sigaction(SIGWINCH, &action, &curses.winch_action);
+}
+
+extern const char*
+register_renderer(struct render_api *api)
+{
+    api->constructor = constructor;
+    api->destructor = destructor;
+    api->get_displayed_count = get_displayed_count;
+    api->poll_key = poll_key;
+    api->render = render;
+    return "curses";
+}
+
+/* vim: set ts=8 sw=4 tw=0 :*/
diff --git a/lib/util.c b/lib/util.c
index a47ff8d..246e102 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -1,9 +1,9 @@
-#include "internal.h"
-
 #define _XOPEN_SOURCE
 #include <wchar.h> /* wcswidth */
 #undef _XOPEN_SOURCE
 
+#include "internal.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
@@ -15,7 +15,8 @@
  * @param string C "string" to copy.
  * @return Copy of the given C "string".
  */
-char* _bmStrdup(const char *string)
+char*
+bm_strdup(const char *string)
 {
     assert(string);
 
@@ -34,15 +35,16 @@ char* _bmStrdup(const char *string)
  * Replaces next token in string with '\0' and returns position for the replaced token.
  *
  * @param string C "string" where token will be replaced.
- * @param outNext Reference to position of next delimiter, or 0 if none.
+ * @param out_next Reference to position of next delimiter, or 0 if none.
  * @return Position of the replaced token.
  */
-size_t _bmStripToken(char *string, const char *token, size_t *outNext)
+size_t
+bm_strip_token(char *string, const char *token, size_t *out_next)
 {
     size_t len = strcspn(string, token);
 
-    if (outNext)
-        *outNext = len + (string[len] != 0);
+    if (out_next)
+        *out_next = len + (string[len] != 0);
 
     string[len] = 0;
     return len;
@@ -55,9 +57,10 @@ size_t _bmStripToken(char *string, const char *token, size_t *outNext)
  * @param needle C "string" to match.
  * @return Less than, equal to or greater than zero if hay is lexicographically less than, equal to or greater than needle.
  */
-int _bmStrupcmp(const char *hay, const char *needle)
+int
+bm_strupcmp(const char *hay, const char *needle)
 {
-    return _bmStrnupcmp(hay, needle, strlen(hay));
+    return bm_strnupcmp(hay, needle, strlen(hay));
 }
 
 /**
@@ -67,15 +70,14 @@ int _bmStrupcmp(const char *hay, const char *needle)
  * @param needle C "string" to match.
  * @return Less than, equal to or greater than zero if hay is lexicographically less than, equal to or greater than needle.
  */
-int _bmStrnupcmp(const char *hay, const char *needle, size_t len)
+int
+bm_strnupcmp(const char *hay, const char *needle, size_t len)
 {
-    size_t i = 0;
-    unsigned char a = 0, b = 0;
-
     const unsigned char *p1 = (const unsigned char*)hay;
     const unsigned char *p2 = (const unsigned char*)needle;
 
-    for (i = 0; len > 0; --len, ++i)
+    unsigned char a = 0, b = 0;
+    for (size_t i = 0; len > 0; --len, ++i)
         if ((a = toupper(*p1++)) != (b = toupper(*p2++)))
             return a - b;
 
@@ -88,17 +90,18 @@ int _bmStrnupcmp(const char *hay, const char *needle, size_t len)
  * @param hay C "string" to substring against.
  * @param needle C "string" to substring.
  */
-char* _bmStrupstr(const char *hay, const char *needle)
+char*
+bm_strupstr(const char *hay, const char *needle)
 {
-    size_t i, r = 0, p = 0, len, len2;
+    size_t r = 0, p = 0, len, len2;
 
     if ((len = strlen(hay)) < (len2 = strlen(needle)))
         return NULL;
 
-    if (!_bmStrnupcmp(hay, needle, len2))
+    if (!bm_strnupcmp(hay, needle, len2))
         return (char*)hay;
 
-    for (i = 0; i < len; ++i) {
+    for (size_t i = 0; i < len; ++i) {
         if (p == len2)
             return (char*)hay + r;
 
@@ -121,12 +124,13 @@ char* _bmStrupstr(const char *hay, const char *needle)
  * @param string C "string" to determite.
  * @return Number of columns, or -1 on failure.
  */
-int _bmUtf8StringScreenWidth(const char *string)
+int32_t
+bm_utf8_string_screen_width(const char *string)
 {
     assert(string);
 
-    char *mstr = _bmStrdup(string);
-    if (!mstr)
+    char *mstr;
+    if (!(mstr = bm_strdup(string)))
         return strlen(string);
 
     char *s;
@@ -142,7 +146,7 @@ int _bmUtf8StringScreenWidth(const char *string)
         return len;
     }
 
-    int length = wcswidth(wstring, num_char);
+    int32_t length = wcswidth(wstring, num_char);
     free(wstring);
     free(mstr);
     return length;
@@ -155,7 +159,8 @@ int _bmUtf8StringScreenWidth(const char *string)
  * @param start Offset where to figure out next rune. (cursor)
  * @return Number of bytes to next UTF8 rune.
  */
-size_t _bmUtf8RuneNext(const char *string, size_t start)
+size_t
+bm_utf8_rune_next(const char *string, size_t start)
 {
     assert(string);
 
@@ -174,7 +179,8 @@ size_t _bmUtf8RuneNext(const char *string, size_t start)
  * @param start Offset where to figure out previous rune. (cursor)
  * @return Number of bytes to previous UTF8 rune.
  */
-size_t _bmUtf8RunePrev(const char *string, size_t start)
+size_t
+bm_utf8_rune_prev(const char *string, size_t start)
 {
     assert(string);
 
@@ -193,12 +199,13 @@ size_t _bmUtf8RunePrev(const char *string, size_t start)
  * @param u8len Byte length of the rune.
  * @return Number of columns, or -1 on failure.
  */
-size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len)
+size_t
+bm_utf8_rune_width(const char *rune, uint32_t u8len)
 {
     assert(rune);
     char mb[5] = { 0, 0, 0, 0, 0 };
     memcpy(mb, rune, (u8len > 4 ? 4 : u8len));
-    return _bmUtf8StringScreenWidth(mb);
+    return bm_utf8_string_screen_width(mb);
 }
 
 /**
@@ -206,24 +213,25 @@ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len)
  *
  * @param string Null terminated C "string".
  * @param start Start offset where to delete from. (cursor)
- * @param outRuneWidth Reference to size_t, return number of columns for removed rune, or -1 on failure.
+ * @param out_rune_width Reference to size_t, return number of columns for removed rune, or -1 on failure.
  * @return Number of bytes removed from buffer.
  */
-size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth)
+size_t
+bm_utf8_rune_remove(char *string, size_t start, size_t *out_rune_width)
 {
     assert(string);
 
-    if (outRuneWidth)
-        *outRuneWidth = 0;
+    if (out_rune_width)
+        *out_rune_width = 0;
 
     size_t len = strlen(string), oldStart = start;
     if (len == 0 || len < start || !*string)
         return 0;
 
-    start -= _bmUtf8RunePrev(string, start);
+    start -= bm_utf8_rune_prev(string, start);
 
-    if (outRuneWidth)
-        *outRuneWidth = _bmUtf8RuneWidth(string + start, oldStart - start);
+    if (out_rune_width)
+        *out_rune_width = bm_utf8_rune_width(string + start, oldStart - start);
 
     memmove(string + start, string + oldStart, len - oldStart);
     string[len - (oldStart - start)] = 0;
@@ -233,68 +241,69 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth)
 /**
  * Insert UTF8 rune to buffer.
  *
- * @param inOutString Reference to buffer.
- * @param inOutBufSize Reference to size of the buffer.
+ * @param in_out_string Reference to buffer.
+ * @param in_out_buf_size Reference to size of the buffer.
  * @param start Start offset where to insert to. (cursor)
  * @param rune Buffer to insert to string.
  * @param u8len Byte length of the rune.
- * @param outRuneWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure.
+ * @param out_rune_width Reference to size_t, return number of columns for inserted rune, or -1 on failure.
  * @return Number of bytes inserted to buffer.
  */
-size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth)
+size_t
+bm_utf8_rune_insert(char **in_out_string, size_t *in_out_buf_size, size_t start, const char *rune, uint32_t u8len, size_t *out_rune_width)
 {
-    assert(inOutString);
-    assert(inOutBufSize);
+    assert(in_out_string);
+    assert(in_out_buf_size);
 
-    if (outRuneWidth)
-        *outRuneWidth = 0;
+    if (out_rune_width)
+        *out_rune_width = 0;
 
     if (u8len == 1 && !isprint(*rune))
         return 0;
 
-    size_t len = (*inOutString ? strlen(*inOutString) : 0);
-    if (!*inOutString && !(*inOutString = calloc(1, (*inOutBufSize = u8len + 1))))
+    size_t len = (*in_out_string ? strlen(*in_out_string) : 0);
+    if (!*in_out_string && !(*in_out_string = calloc(1, (*in_out_buf_size = u8len + 1))))
         return 0;
 
-    if (len + u8len >= *inOutBufSize) {
+    if (len + u8len >= *in_out_buf_size) {
         void *tmp;
-        if (!(tmp = realloc(*inOutString, (*inOutBufSize * 2)))) {
-            if (!(tmp = malloc((*inOutBufSize * 2))))
+        if (!(tmp = realloc(*in_out_string, (*in_out_buf_size * 2)))) {
+            if (!(tmp = malloc((*in_out_buf_size * 2))))
                 return 0;
 
-            memcpy(tmp, *inOutString, *inOutBufSize);
-            free(*inOutString);
+            memcpy(tmp, *in_out_string, *in_out_buf_size);
+            free(*in_out_string);
         }
 
-        memset(tmp + *inOutBufSize, 0, *inOutBufSize);
-        *inOutString = tmp;
-        *inOutBufSize *= 2;
+        memset(tmp + *in_out_buf_size, 0, *in_out_buf_size);
+        *in_out_string = tmp;
+        *in_out_buf_size *= 2;
     }
 
-    char *str = *inOutString + start;
+    char *str = *in_out_string + start;
     memmove(str + u8len, str, len - start);
     memcpy(str, rune, u8len);
-    (*inOutString)[len + u8len] = 0;
+    (*in_out_string)[len + u8len] = 0;
 
-    if (outRuneWidth)
-        *outRuneWidth = _bmUtf8RuneWidth(rune, u8len);
+    if (out_rune_width)
+        *out_rune_width = bm_utf8_rune_width(rune, u8len);
     return u8len;
 }
 
 /**
  * Insert unicode character to UTF8 buffer.
  *
- * @param inOutString Reference to buffer.
- * @param inOutBufSize Reference to size of the buffer.
+ * @param in_out_string Reference to buffer.
+ * @param in_out_buf_size Reference to size of the buffer.
  * @param start Start offset where to insert to. (cursor)
  * @param unicode Unicode character to insert.
- * @param outRuneWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure.
+ * @param out_rune_width Reference to size_t, return number of columns for inserted rune, or -1 on failure.
  * @return Number of bytes inserted to buffer.
  */
-size_t _bmUnicodeInsert(char **inOutString, size_t *inOutBufSize, size_t start, unsigned int unicode, size_t *outRuneWidth)
+size_t
+bm_unicode_insert(char **in_out_string, size_t *in_out_buf_size, size_t start, uint32_t unicode, size_t *out_rune_width)
 {
-    assert(inOutString);
-    assert(inOutBufSize);
+    assert(in_out_string && in_out_buf_size);
 
     char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4)));
     char mb[5] = { 0, 0, 0, 0 };
@@ -308,7 +317,7 @@ size_t _bmUnicodeInsert(char **inOutString, size_t *inOutBufSize, size_t start,
         mb[0] |= (unicode >> (i * 6 - 6));
     }
 
-    return _bmUtf8RuneInsert(inOutString, inOutBufSize, start, mb, u8len, outRuneWidth);
+    return bm_utf8_rune_insert(in_out_string, in_out_buf_size, start, mb, u8len, out_rune_width);
 }
 
 /* vim: set ts=8 sw=4 tw=0 :*/
diff --git a/lib/version.h.in b/lib/version.h.in
index 6be8b5f..9d9b851 100644
--- a/lib/version.h.in
+++ b/lib/version.h.in
@@ -1,3 +1,4 @@
+#define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
 static const char *BM_VERSION = "@BEMENU_VERSION@";
 
 /* vim: set ts=8 sw=4 tw=0 :*/
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index f9d13a5..556df6a 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,11 +1,11 @@
 SET(TESTS
-    "bmMenuNew"
+    "bm_menu_new"
     )
 
 INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../lib")
 FOREACH (test ${TESTS})
     ADD_EXECUTABLE(${test}_test ${test}.c)
-    TARGET_LINK_LIBRARIES(${test}_test bemenu)
+    TARGET_LINK_LIBRARIES(${test}_test bemenu ${BEMENU_LIBRARIES})
 
     IF (WIN32)
         ADD_TEST(${test}_test ${test}_test.exe)
diff --git a/test/bmMenuNew.c b/test/bmMenuNew.c
index ebab314..bccfbc8 100644
--- a/test/bmMenuNew.c
+++ b/test/bmMenuNew.c
@@ -1,25 +1,31 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <string.h>
 #include <assert.h>
 #include <bemenu.h>
 
-int main(int argc, char **argv)
+int
+main(int argc, char **argv)
 {
     (void)argc, (void)argv;
 
+    if (!bm_init())
+        return EXIT_FAILURE;
+
     // TEST: Instance bmMenu with all possible draw modes.
     {
-        bmDrawMode i;
-        for (i = 0; i < BM_DRAW_MODE_LAST; ++i) {
-            if (i == BM_DRAW_MODE_CURSES && !isatty(STDIN_FILENO)) {
+        uint32_t count;
+        const struct bm_renderer **renderers = bm_get_renderers(&count);
+        for (int32_t i = 0; i < count; ++i) {
+            if (!strcmp(bm_renderer_get_name(renderers[i]), "curses") && !isatty(STDIN_FILENO)) {
                 printf("Skipping test for mode BM_DRAW_MODE_CURSES, as not running on terminal.\n");
                 continue;
             }
-            bmMenu *menu = bmMenuNew(i);
+            struct bm_menu *menu = bm_menu_new(bm_renderer_get_name(renderers[i]));
             assert(menu);
-            bmMenuRender(menu);
-            bmMenuFree(menu);
+            bm_menu_render(menu);
+            bm_menu_free(menu);
         }
     }
 
diff --git a/test/bm_menu_new.c b/test/bm_menu_new.c
new file mode 100644
index 0000000..ba9e20b
--- /dev/null
+++ b/test/bm_menu_new.c
@@ -0,0 +1,35 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <bemenu.h>
+
+int
+main(int argc, char **argv)
+{
+    (void)argc, (void)argv;
+
+    if (!bm_init())
+        return EXIT_FAILURE;
+
+    // TEST: Instance bmMenu with all possible draw modes.
+    {
+        uint32_t count;
+        const struct bm_renderer **renderers = bm_get_renderers(&count);
+        for (int32_t i = 0; i < count; ++i) {
+            if (!strcmp(bm_renderer_get_name(renderers[i]), "curses") && !isatty(STDIN_FILENO)) {
+                printf("Skipping test for mode BM_DRAW_MODE_CURSES, as not running on terminal.\n");
+                continue;
+            }
+            struct bm_menu *menu = bm_menu_new(bm_renderer_get_name(renderers[i]));
+            assert(menu);
+            bm_menu_render(menu);
+            bm_menu_render(menu);
+        }
+    }
+
+    return EXIT_SUCCESS;
+}
+
+/* vim: set ts=8 sw=4 tw=0 :*/
-- 
cgit v1.2.3-70-g09d2