diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/3rdparty/cdl.c | 96 | ||||
-rw-r--r-- | lib/3rdparty/cdl.h | 10 | ||||
-rw-r--r-- | lib/3rdparty/tinydir.h | 548 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 5 | ||||
-rw-r--r-- | lib/bemenu.h | 339 | ||||
-rw-r--r-- | lib/draw/curses.c | 498 | ||||
-rw-r--r-- | lib/filter.c | 141 | ||||
-rw-r--r-- | lib/internal.h | 172 | ||||
-rw-r--r-- | lib/item.c | 76 | ||||
-rw-r--r-- | lib/library.c | 152 | ||||
-rw-r--r-- | lib/list.c | 121 | ||||
-rw-r--r-- | lib/menu.c | 577 | ||||
-rw-r--r-- | lib/renderers/CMakeLists.txt | 10 | ||||
-rw-r--r-- | lib/renderers/curses/CMakeLists.txt | 6 | ||||
-rw-r--r-- | lib/renderers/curses/curses.c | 379 | ||||
-rw-r--r-- | lib/util.c | 133 | ||||
-rw-r--r-- | lib/version.h.in | 1 |
17 files changed, 1898 insertions, 1366 deletions
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 @@ -33,6 +37,28 @@ typedef struct _bmItem bmItem; * @{ */ /** + * @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 :*/ @@ -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 :*/ @@ -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 :*/ @@ -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 :*/ @@ -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 :*/ |