diff options
| author | Jari Vetoniemi <mailroxas@gmail.com> | 2014-10-22 22:46:51 +0300 | 
|---|---|---|
| committer | Jari Vetoniemi <mailroxas@gmail.com> | 2014-10-22 22:46:51 +0300 | 
| commit | 014c20099d0199445cedc6e3504e4309176c56ae (patch) | |
| tree | 2711398696489389cc9a48cf1789d7521767e054 /lib | |
| parent | 33aedfe23712ae99f3393b07567c49388fcba376 (diff) | |
| download | bemenu-014c20099d0199445cedc6e3504e4309176c56ae.tar.gz bemenu-014c20099d0199445cedc6e3504e4309176c56ae.tar.bz2 bemenu-014c20099d0199445cedc6e3504e4309176c56ae.zip | |
Refactor code to snake_style, turn renderers into plugins, and start
wayland (cairo) renderer.
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 :*/ | 
