From 1c511e30f540bcc7a79aa9ab96f9552097c1c6ec Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 21:34:38 +0200 Subject: Remove custom output directory paths. --- lib/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib') diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 46c532b..cf489fd 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,6 +1,3 @@ -SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY lib) -SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY lib) - # Sources SET(BEMENU_SOURCE bemenu.c -- cgit v1.2.3-70-g09d2 From 908749cfdfa037b6f5fd0f5f7b2fa66080329156 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 21:33:20 +0200 Subject: Add basic API code. => bmMenu instancing. => "Rendering" => bmMenu releasing. --- client/CMakeLists.txt | 2 +- client/client.c | 12 +++++++++--- lib/bemenu.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- lib/bemenu.h | 38 ++++++++++++++++++++++++++++++++++++++ lib/internal.h | 37 +++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 lib/bemenu.h create mode 100644 lib/internal.h (limited to 'lib') diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 91c4ea5..aceaaf5 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,6 +1,6 @@ # Sources SET(CLIENT_SOURCE client.c) -SET(CLIENT_INCLUDE) +SET(CLIENT_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/../lib") SET(CLIENT_LIBRARIES bemenu) # Warnings diff --git a/client/client.c b/client/client.c index de5468e..559651f 100644 --- a/client/client.c +++ b/client/client.c @@ -6,6 +6,7 @@ */ #include +#include /** * Main method @@ -20,9 +21,14 @@ int main(int argc, char **argv) { (void)argc, (void)argv; - /* - * code goes here - */ + bmMenu *menu = bmMenuNew(BM_DRAW_MODE_NONE); + + if (!menu) + return EXIT_FAILURE; + + bmMenuRender(menu); + + bmMenuFree(menu); return EXIT_SUCCESS; } diff --git a/lib/bemenu.c b/lib/bemenu.c index 2f92f27..a2caa13 100644 --- a/lib/bemenu.c +++ b/lib/bemenu.c @@ -2,8 +2,55 @@ * @file bemenu.c */ -/* - * code goes here +#include "internal.h" +#include +#include + +/** + * 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) +{ + bmMenu *menu = calloc(1, sizeof(bmMenu)); + + menu->drawMode = drawMode; + + if (!menu) + return NULL; + + switch (menu->drawMode) { + default:break; + } + + return menu; +} + +/** + * Release bmMenu instance. + * + * @param menu bmMenu instance to be freed from memory. + */ +void bmMenuFree(bmMenu *menu) +{ + assert(menu != NULL); + free(menu); +} + +/** + * 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. + */ +void bmMenuRender(bmMenu *menu) +{ + assert(menu != NULL); + + if (menu->renderApi.render) + menu->renderApi.render(menu->items, menu->itemsCount); +} /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/bemenu.h b/lib/bemenu.h new file mode 100644 index 0000000..f4823b4 --- /dev/null +++ b/lib/bemenu.h @@ -0,0 +1,38 @@ +/** + * @file bemenu.h + * + * Public header + */ + +/** + * Draw mode constants for setting bmMenu instance draw mode. + */ +typedef enum bmDrawMode { + BM_DRAW_MODE_NONE +} bmDrawMode; + +typedef struct _bmMenu bmMenu; + +/** + * 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); + +/** + * Release bmMenu instance. + * + * @param menu bmMenu instance to be freed from memory. + */ +void bmMenuFree(bmMenu *menu); + +/** + * Render bmMenu instance using chosen draw method. + * + * @param menu bmMenu instance to be rendered. + */ +void bmMenuRender(bmMenu *menu); + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h new file mode 100644 index 0000000..d11ebe2 --- /dev/null +++ b/lib/internal.h @@ -0,0 +1,37 @@ +/** + * @file internal.h + */ + +#include "bemenu.h" + +/** + * Internal bmItem struct that is not exposed to public. + * Represents a single item in menu. + */ +struct _bmItem { + char *text; +} _bmItem; + +/** + * Internal bmRenderApi struct. + * Renderers should be able to fill this one as they see fit. + */ +struct _bmRenderApi { + void (*render)(struct _bmItem **items, unsigned int nmemb); + void (*free)(void); +} _bmRenderApi; + +/** + * Internal bmMenu struct that is not exposed to public. + */ +struct _bmMenu { + bmDrawMode drawMode; + struct _bmRenderApi renderApi; + struct _bmItem **items; + unsigned int itemsCount; +} _bmMenu; + + +void bmRenderNullInit(struct _bmRenderApi *api); + +/* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From c609e4025e9de5c5784e0210a11e34a5a06d7626 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 21:40:46 +0200 Subject: Release renderer in bmMenuFree function. --- lib/bemenu.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/bemenu.c b/lib/bemenu.c index a2caa13..d4ae77e 100644 --- a/lib/bemenu.c +++ b/lib/bemenu.c @@ -36,6 +36,10 @@ bmMenu* bmMenuNew(bmDrawMode drawMode) void bmMenuFree(bmMenu *menu) { assert(menu != NULL); + + if (menu->renderApi.free) + menu->renderApi.free(); + free(menu); } -- cgit v1.2.3-70-g09d2 From 06c8bde93fe4a4ae366cd69f8fb2745ce95cefb4 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 21:59:34 +0200 Subject: Provide BM_DRAW_MODE_LAST for enumerating draw modes. --- lib/bemenu.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index f4823b4..3fdfb0b 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -6,9 +6,13 @@ /** * Draw mode constants for setting bmMenu instance draw mode. + * + * BM_DRAW_MODE_LAST is provided for enumerating draw modes. + * Instancing with it however provides exactly same functionality as BM_DRAW_MODE_NONE. */ typedef enum bmDrawMode { - BM_DRAW_MODE_NONE + BM_DRAW_MODE_NONE, + BM_DRAW_MODE_LAST } bmDrawMode; typedef struct _bmMenu bmMenu; -- cgit v1.2.3-70-g09d2 From 13c470fb398ded391cd4b954cb98097aea6dd07d Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 22:03:47 +0200 Subject: Remove unused function prototype. --- lib/internal.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib') diff --git a/lib/internal.h b/lib/internal.h index d11ebe2..388de08 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -31,7 +31,4 @@ struct _bmMenu { unsigned int itemsCount; } _bmMenu; - -void bmRenderNullInit(struct _bmRenderApi *api); - /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From 92aa6367b815d337a9ad7a2c7a9fb6dba224423f Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 28 Mar 2014 22:14:13 +0200 Subject: These structs are not typedefs. --- lib/internal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/internal.h b/lib/internal.h index 388de08..c79ffd1 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -10,7 +10,7 @@ */ struct _bmItem { char *text; -} _bmItem; +}; /** * Internal bmRenderApi struct. @@ -19,7 +19,7 @@ struct _bmItem { struct _bmRenderApi { void (*render)(struct _bmItem **items, unsigned int nmemb); void (*free)(void); -} _bmRenderApi; +}; /** * Internal bmMenu struct that is not exposed to public. @@ -29,6 +29,6 @@ struct _bmMenu { struct _bmRenderApi renderApi; struct _bmItem **items; unsigned int itemsCount; -} _bmMenu; +}; /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From 67be25fbe43274340de89049fec7098cdf998b47 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:09:35 +0300 Subject: Basic working bemenu with curses backend --- client/client.c | 95 +++++++++- lib/CMakeLists.txt | 7 +- lib/bemenu.c | 60 ------ lib/bemenu.h | 227 ++++++++++++++++++++++- lib/draw/curses.c | 258 +++++++++++++++++++++++++- lib/filter.c | 64 +++++++ lib/internal.h | 39 +++- lib/item.c | 75 ++++++++ lib/menu.c | 527 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/util.c | 226 +++++++++++++++++++++++ 10 files changed, 1504 insertions(+), 74 deletions(-) delete mode 100644 lib/bemenu.c create mode 100644 lib/filter.c create mode 100644 lib/item.c create mode 100644 lib/menu.c create mode 100644 lib/util.c (limited to 'lib') diff --git a/client/client.c b/client/client.c index 559651f..1c30498 100644 --- a/client/client.c +++ b/client/client.c @@ -6,8 +6,83 @@ */ #include +#include +#include +#include +#include #include +static ptrdiff_t getLine(char **outLine, size_t *outAllocated, FILE *stream) +{ + size_t len = 0, allocated; + char *s, *buffer; + + assert(outLine != NULL); + assert(outAllocated != NULL); + + if (stream == NULL || feof(stream) || ferror(stream)) + return -1; + + allocated = *outAllocated; + buffer = *outLine; + + if (buffer == NULL || allocated == 0) { + if (!(buffer = calloc(1, (allocated = 1024) + 1))) + return -1; + } + + for (s = buffer;;) { + if (fgets(s, allocated - (s - buffer), stream) == NULL) + return -1; + + len = strlen(s); + if (feof(stream)) + break; + + if (len > 0 && s[len - 1] == '\n') + break; + + if (len + 1 >= allocated - (s - buffer)) { + void *tmp = realloc(buffer, 2 * allocated); + if (!tmp) + break; + + buffer = tmp; + s = buffer + allocated - 1; + memset(s, 0, allocated - (s - buffer)); + allocated *= 2; + } else { + s += len; + } + } + + *outAllocated = allocated; + *outLine = buffer; + + if (s[len - 1] == '\n') + s[len - 1] = 0; + + return s - buffer + len; +} + +static void readItemsToMenuFromStdin(bmMenu *menu) +{ + ptrdiff_t len; + size_t size; + char *line = NULL; + + while ((len = getLine(&line, &size, stdin)) != -1) { + bmItem *item = bmItemNew((len > 0 ? line : NULL)); + if (!item) + break; + + bmMenuAddItem(menu, item); + } + + if (line) + free(line); +} + /** * Main method * @@ -21,16 +96,30 @@ int main(int argc, char **argv) { (void)argc, (void)argv; - bmMenu *menu = bmMenuNew(BM_DRAW_MODE_NONE); + bmMenu *menu = bmMenuNew(BM_DRAW_MODE_CURSES); if (!menu) return EXIT_FAILURE; - bmMenuRender(menu); + bmMenuSetTitle(menu, "bemenu"); + readItemsToMenuFromStdin(menu); + + bmKey key; + unsigned int unicode; + int status = 0; + do { + bmMenuRender(menu); + key = bmMenuGetKey(menu, &unicode); + } while ((status = bmMenuRunWithKey(menu, key, unicode)) == BM_RUN_RESULT_RUNNING); + + if (status == BM_RUN_RESULT_SELECTED) { + bmItem *item = bmMenuGetSelectedItem(menu); + printf("%s\n", bmItemGetText(item)); + } bmMenuFree(menu); - return EXIT_SUCCESS; + return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index cf489fd..e669ef5 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,6 +1,9 @@ # Sources SET(BEMENU_SOURCE - bemenu.c + menu.c + item.c + filter.c + util.c draw/curses.c ) SET(BEMENU_INCLUDE) @@ -22,6 +25,6 @@ ENDIF () # Compile INCLUDE_DIRECTORIES(${BEMENU_INCLUDE}) ADD_LIBRARY(bemenu ${BEMENU_SOURCE}) -TARGET_LINK_LIBRARIES(bemenu ${BEMENU_LIBRARIES}) +TARGET_LINK_LIBRARIES(bemenu ${BEMENU_LIBRARIES} dl) # vim: set ts=8 sw=4 tw=0 : diff --git a/lib/bemenu.c b/lib/bemenu.c deleted file mode 100644 index d4ae77e..0000000 --- a/lib/bemenu.c +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @file bemenu.c - */ - -#include "internal.h" -#include -#include - -/** - * 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) -{ - bmMenu *menu = calloc(1, sizeof(bmMenu)); - - menu->drawMode = drawMode; - - if (!menu) - return NULL; - - switch (menu->drawMode) { - default:break; - } - - return menu; -} - -/** - * Release bmMenu instance. - * - * @param menu bmMenu instance to be freed from memory. - */ -void bmMenuFree(bmMenu *menu) -{ - assert(menu != NULL); - - if (menu->renderApi.free) - menu->renderApi.free(); - - free(menu); -} - -/** - * 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. - */ -void bmMenuRender(bmMenu *menu) -{ - assert(menu != NULL); - - if (menu->renderApi.render) - menu->renderApi.render(menu->items, menu->itemsCount); -} - -/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/bemenu.h b/lib/bemenu.h index 3fdfb0b..b4b9b30 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -5,17 +5,71 @@ */ /** - * Draw mode constants for setting bmMenu instance draw mode. + * Draw mode constants for bmMenu instance draw mode. * * BM_DRAW_MODE_LAST is provided for enumerating draw modes. * Instancing with it however provides exactly same functionality as BM_DRAW_MODE_NONE. */ typedef enum bmDrawMode { BM_DRAW_MODE_NONE, + BM_DRAW_MODE_CURSES, BM_DRAW_MODE_LAST } bmDrawMode; +/** + * Filter mode constants for bmMenu instance filter mode. + * + * BM_FILTER_MODE_LAST is provided for enumerating filter modes. + * Using it as filter mode however provides exactly same functionality as BM_FILTER_MODE_DMENU. + */ +typedef enum bmFilterMode { + BM_FILTER_MODE_DMENU, + BM_FILTER_MODE_DMENU_CASE_INSENSITIVE, + BM_FILTER_MODE_LAST +} bmFilterMode; + +/** + * Result constants from bmMenuRunWithKey function. + * + * BM_RUN_RESULT_RUNNING means that menu is running and thus should be still renderer && ran. + * BM_RUN_RESULT_SELECTED means that menu was closed and items were selected. + * BM_RUN_RESULT_CANCEL means that menu was closed and selection was canceled. + */ +typedef enum bmRunResult { + BM_RUN_RESULT_RUNNING, + BM_RUN_RESULT_SELECTED, + BM_RUN_RESULT_CANCEL, +} bmRunResult; + +/** + * Key constants. + * + * BM_KEY_LAST is provided for enumerating keys. + */ +typedef enum bmKey { + BM_KEY_NONE, + BM_KEY_UP, + BM_KEY_DOWN, + BM_KEY_LEFT, + BM_KEY_RIGHT, + BM_KEY_HOME, + BM_KEY_END, + BM_KEY_PAGE_UP, + BM_KEY_PAGE_DOWN, + BM_KEY_BACKSPACE, + BM_KEY_DELETE, + BM_KEY_LINE_DELETE_LEFT, + BM_KEY_LINE_DELETE_RIGHT, + BM_KEY_WORD_DELETE, + BM_KEY_TAB, + BM_KEY_ESCAPE, + BM_KEY_RETURN, + BM_KEY_UNICODE, + BM_KEY_LAST +} bmKey; + typedef struct _bmMenu bmMenu; +typedef struct _bmItem bmItem; /** * Create new bmMenu instance. @@ -32,11 +86,180 @@ bmMenu* bmMenuNew(bmDrawMode drawMode); */ void bmMenuFree(bmMenu *menu); +/** + * Release items inside bmMenu instance. + * + * @param menu bmMenu instance which items will be freed from memory. + */ +void bmMenuFreeItems(bmMenu *menu); + +/** + * 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); + +/** + * Get active filter mode from bmMenu instance. + * + * @param menu bmMenu instance where to get filter mode. + * @return bmFilterMode constant. + */ +bmFilterMode bmMenuGetFilterMode(const bmMenu *menu); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +/** + * Remove item from bmMenu instance at specific index. + * + * @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); + +/** + * Remove item from bmMenu instance. + * 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); + +/** + * Get selected item from bmMenu instance. + * + * @param menu bmMenu instance from where to get selected item. + * @return Selected bmItem instance, NULL if none selected. + */ +bmItem* bmMenuGetSelectedItem(const bmMenu *menu); + +/** + * Get items from bmMenu instance. + * + * @param menu bmMenu instance from where to get items. + * @param nmemb 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 *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 nmemb 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 *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); + /** * Render bmMenu instance using chosen draw method. * * @param menu bmMenu instance to be rendered. */ -void bmMenuRender(bmMenu *menu); +void bmMenuRender(const bmMenu *menu); + +/** + * Poll key and unicode from underlying UI toolkit. + * + * This function will block on CURSES draw mode. + * + * @param menu bmMenu instance from which to poll. + * @param unicode Reference to unsigned int. + * @return bmKey for polled key. + */ +bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode); + +/** + * Advances menu logic with key and unicode as input. + * + * @param menu bmMenu instance to be advanced. + * @return bmRunResult for menu state. + */ +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. + */ +bmItem* bmItemNew(const char *text); + +/** + * Release bmItem instance. + * + * @param item bmItem instance to be freed from memory. + */ +void bmItemFree(bmItem *item); + +/** + * 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); + +/** + * 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); /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/draw/curses.c b/lib/draw/curses.c index c873496..48a146a 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -2,8 +2,260 @@ * @file curses.c */ -/* - * code goes here - */ +#include "../internal.h" +#include +#include +#include +#include +#include +#include +#include + +/* ncurses.h likes to define stuff for us. + * This unforunately mangles with our struct. */ +#undef erase +#undef refresh +#undef mvprintw +#undef move +#undef init_pair +#undef attroff +#undef attron +#undef getmaxx +#undef getmaxy +#undef timeout + +static struct curses { + void *handle; + WINDOW *stdscr; + WINDOW* (*initscr)(void); + int (*endwin)(void); + int (*refresh)(void); + int (*erase)(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 (*getmaxx)(WINDOW *win); + int (*getmaxy)(WINDOW *win); + int (*keypad)(WINDOW *win, bool bf); + int *ESCDELAY; +} curses; + +static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) +{ + static int ncols = 0; + static char *buffer = NULL; + int new_ncols = curses.getmaxx(curses.stdscr); + + if (new_ncols <= 0) + return; + + if (!buffer || new_ncols > ncols) { + if (buffer) + free(buffer); + + ncols = new_ncols; + + if (!(buffer = calloc(1, ncols + 1))) + return; + } + + va_list args; + va_start(args, format); + int tlen = vsnprintf(NULL, 0, format, args) + 1; + if (tlen > ncols) + tlen = ncols; + va_end(args); + + va_start(args, format); + vsnprintf(buffer, tlen, format, args); + va_end(args); + + memset(buffer + tlen - 1, ' ', ncols - tlen + 1); + + if (pair > 0) + curses.attron(COLOR_PAIR(pair)); + + curses.mvprintw(y, 0, buffer); + + if (pair > 0) + curses.attroff(COLOR_PAIR(pair)); +} + +static void _bmDrawCursesRender(const bmMenu *menu) +{ + if (!curses.stdscr) { + freopen("/dev/tty", "rw", stdin); + setlocale(LC_CTYPE, ""); + if ((curses.stdscr = curses.initscr()) == NULL) + return; + + *curses.ESCDELAY = 25; + curses.keypad(curses.stdscr, true); + + curses.start_color(); + curses.init_pair(1, COLOR_BLACK, COLOR_RED); + curses.init_pair(2, COLOR_RED, COLOR_BLACK); + } + + const unsigned int lines = curses.getmaxy(curses.stdscr); + curses.erase(); + + size_t titleLen = (menu->title ? strlen(menu->title) + 1 : 0); + + _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", menu->filter); + + if (menu->title) { + 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 selected = (items[i] == bmMenuGetSelectedItem(menu)); + _bmDrawCursesDrawLine((selected ? 2 : 0), cl++, "%s%s", (selected ? ">> " : " "), items[i]->text); + } + + curses.move(0, titleLen + menu->cursesCursor); + curses.refresh(); +} + +static void _bmDrawCursesEndWin(void) +{ + if (curses.endwin) + curses.endwin(); + + curses.stdscr = NULL; +} + +static bmKey _bmDrawCursesGetKey(unsigned int *unicode) +{ + assert(unicode != NULL); + *unicode = 0; + + if (!curses.stdscr) + return BM_KEY_NONE; + + curses.get_wch(unicode); + switch (*unicode) { + 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 KEY_HOME: return BM_KEY_HOME; + + case 5: /* C-e */ + case KEY_END: return BM_KEY_END; + + case KEY_PPAGE: return BM_KEY_PAGE_UP; + case KEY_NPAGE: return BM_KEY_PAGE_DOWN; + + case 8: /* C-h */ + case KEY_BACKSPACE: return BM_KEY_BACKSPACE; + + case 4: /* C-d */ + case KEY_DC: return BM_KEY_DELETE; + + case 21: return BM_KEY_LINE_DELETE_LEFT; /* C-u */ + case 11: return BM_KEY_LINE_DELETE_RIGHT; /* C-k */ + case 23: return BM_KEY_WORD_DELETE; /* C-w */ + + case 9: return BM_KEY_TAB; /* Tab */ + + 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); + + memset(&curses, 0, sizeof(curses)); +} + +int _bmDrawCursesInit(struct _bmRenderApi *api) +{ + memset(&curses, 0, sizeof(curses)); + + /* FIXME: hardcoded and not cross-platform */ + curses.handle = dlopen("/usr/lib/libncursesw.so.5", RTLD_LAZY); + + if (!curses.handle) + return 0; + +#define bmLoadFunction(x) (curses.x = dlsym(curses.handle, #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)) + 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(getmaxx)) + goto function_pointer_exception; + if (!bmLoadFunction(getmaxy)) + goto function_pointer_exception; + if (!bmLoadFunction(keypad)) + goto function_pointer_exception; + if (!bmLoadFunction(ESCDELAY)) + goto function_pointer_exception; + +#undef bmLoadFunction + + api->getKey = _bmDrawCursesGetKey; + api->render = _bmDrawCursesRender; + api->free = _bmDrawCursesFree; + + return 1; + +function_pointer_exception: + _bmDrawCursesFree(); + return 0; +} /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/filter.c b/lib/filter.c new file mode 100644 index 0000000..b1cf668 --- /dev/null +++ b/lib/filter.c @@ -0,0 +1,64 @@ +/** + * @file filter.c + */ + +#include "internal.h" +#include +#include +#include + +/** + * Filter that mimics the vanilla dmenu filtering. + * + * @param menu bmMenu instance to filter. + * @param count unsigned int reference to filtered items count. + * @param selected unsigned int reference to new selected item index. + * @return Pointer to array of bmItem pointers. + */ +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected) +{ + assert(menu != NULL); + assert(count != NULL); + assert(selected != NULL); + *count = *selected = 0; + + /* FIXME: not real dmenu like filtering at all */ + + bmItem **filtered = calloc(menu->itemsCount, sizeof(bmItem*)); + if (!filtered) + return NULL; + + unsigned int i, f; + for (f = i = 0; i < menu->itemsCount; ++i) { + bmItem *item = menu->items[i]; + if (item->text && strstr(item->text, menu->filter)) { + if (f == 0 || item == bmMenuGetSelectedItem(menu)) + *selected = f; + filtered[f++] = item; + } + } + + return _bmShrinkItemList(&filtered, menu->itemsCount, (*count = f)); +} + +/** + * Filter that mimics the vanilla case-insensitive dmenu filtering. + * + * @param menu bmMenu instance to filter. + * @param count unsigned int reference to filtered items count. + * @param selected unsigned int reference to new selected item index. + * @return Pointer to array of bmItem pointers. + */ +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected) +{ + assert(menu != NULL); + assert(count != NULL); + assert(selected != NULL); + *count = *selected = 0; + + /* FIXME: stub */ + + return NULL; +} + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index c79ffd1..786e082 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -4,6 +4,10 @@ #include "bemenu.h" +#ifndef size_t +# include /* for size_t */ +#endif + /** * Internal bmItem struct that is not exposed to public. * Represents a single item in menu. @@ -17,7 +21,8 @@ struct _bmItem { * Renderers should be able to fill this one as they see fit. */ struct _bmRenderApi { - void (*render)(struct _bmItem **items, unsigned int nmemb); + bmKey (*getKey)(unsigned int *unicode); + void (*render)(const bmMenu *menu); void (*free)(void); }; @@ -25,10 +30,36 @@ struct _bmRenderApi { * Internal bmMenu struct that is not exposed to public. */ struct _bmMenu { - bmDrawMode drawMode; struct _bmRenderApi renderApi; - struct _bmItem **items; - unsigned int itemsCount; + struct _bmItem **items, **filteredItems; + char *title, filter[1024]; + unsigned int cursor, cursesCursor; + unsigned int itemsCount, allocatedCount; + unsigned int filteredCount; + unsigned int index; + bmFilterMode filterMode; + bmDrawMode drawMode; }; +/* draw/curses.c */ +int _bmDrawCursesInit(struct _bmRenderApi *api); + +/* menu.c */ +int _bmMenuShouldRenderItem(const bmMenu *menu, const bmItem *item); + +/* filter.c */ +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected); + +/* util.c */ +char* _bmStrdup(const char *s); +bmItem** _bmShrinkItemList(bmItem ***list, 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 *runeWidth); +size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth); +size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth); + /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/item.c b/lib/item.c new file mode 100644 index 0000000..40ebb52 --- /dev/null +++ b/lib/item.c @@ -0,0 +1,75 @@ +/** + * @file item.c + */ + +#include "internal.h" +#include +#include +#include + +/** + * Allocate a new item. + * + * @param text Pointer to null terminated C "string", can be NULL for empty text. + * @return bmItem instance. + */ +bmItem* bmItemNew(const char *text) +{ + bmItem *item = calloc(1, sizeof(bmItem)); + + if (!item) + return NULL; + + bmItemSetText(item, text); + return item; +} + +/** + * Release bmItem instance. + * + * @param item bmItem instance to be freed from memory. + */ +void bmItemFree(bmItem *item) +{ + assert(item != NULL); + + if (item->text) + free(item->text); + + free(item); +} + +/** + * 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) +{ + assert(item != NULL); + + char *copy = NULL; + if (text && !(copy = _bmStrdup(text))) + return 0; + + if (item->text) + free(item->text); + + item->text = copy; + return 1; +} + +/** + * 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) +{ + assert(item != NULL); + return item->text; +} + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/menu.c b/lib/menu.c new file mode 100644 index 0000000..50311cf --- /dev/null +++ b/lib/menu.c @@ -0,0 +1,527 @@ +/** + * @file bemenu.c + */ + +#include "internal.h" +#include +#include +#include +#include + +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *count, unsigned int *selected) = { + _bmFilterDmenu, + _bmFilterDmenuCaseInsensitive +}; + +static void _bmMenuFilter(bmMenu *menu) +{ + assert(menu != NULL); + + if (menu->filteredItems) + free(menu->filteredItems); + + menu->filteredCount = 0; + menu->filteredItems = NULL; + + unsigned int count, selected; + bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); + + menu->filteredItems = filtered; + menu->filteredCount = count; + menu->index = selected; +} + +static int _bmMenuGrowItems(bmMenu *menu) +{ + void *tmp; + static const unsigned int step = 32; + unsigned int nsize = sizeof(bmItem*) * (menu->allocatedCount + step); + + if (!(tmp = realloc(menu->items, nsize))) { + if (!(tmp = malloc(nsize))) + return 0; + + memcpy(tmp, menu->items, sizeof(bmItem*) * menu->allocatedCount); + } + + menu->items = tmp; + menu->allocatedCount += step; + memset(&menu->items[menu->itemsCount], 0, sizeof(bmItem*) * (menu->allocatedCount - menu->itemsCount)); + return 1; +} + +/** + * 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) +{ + bmMenu *menu = calloc(1, sizeof(bmMenu)); + + menu->drawMode = drawMode; + + if (!menu) + return NULL; + + int status = 1; + + switch (menu->drawMode) { + case BM_DRAW_MODE_CURSES: + status = _bmDrawCursesInit(&menu->renderApi); + break; + + default:break; + } + + if (status == 0) { + bmMenuFree(menu); + return NULL; + } + + return menu; +} + +/** + * Release bmMenu instance. + * + * @param menu bmMenu instance to be freed from memory. + */ +void bmMenuFree(bmMenu *menu) +{ + assert(menu != NULL); + + if (menu->renderApi.free) + menu->renderApi.free(); + + if (menu->title) + free(menu->title); + + bmMenuFreeItems(menu); + free(menu); +} + +/** + * Release items inside bmMenu instance. + * + * @param menu bmMenu instance which items will be freed from memory. + */ +void bmMenuFreeItems(bmMenu *menu) +{ + assert(menu != NULL); + + unsigned int i; + for (i = 0; i < menu->itemsCount; ++i) + bmItemFree(menu->items[i]); + + free(menu->items); + menu->allocatedCount = menu->itemsCount = 0; + menu->items = NULL; +} + +/** + * 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) +{ + assert(menu != NULL); + + bmFilterMode oldMode = mode; + menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode); + + if (oldMode != mode) + _bmMenuFilter(menu); +} + +/** + * Get active filter mode from bmMenu instance. + * + * @param menu bmMenu instance where to get filter mode. + * @return bmFilterMode constant. + */ +bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) +{ + assert(menu != NULL); + return menu->filterMode; +} + +/** + * 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) +{ + assert(menu != NULL); + + char *copy = NULL; + if (title && !(copy = _bmStrdup(title))) + return 0; + + if (menu->title) + free(menu->title); + + menu->title = copy; + return 1; +} + +/** + * 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) +{ + assert(menu != NULL); + 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) +{ + assert(menu != NULL); + assert(item != NULL); + + if (menu->itemsCount >= menu->allocatedCount && !_bmMenuGrowItems(menu)) + return 0; + + if (index + 1 != menu->itemsCount) { + unsigned int i = index; + memmove(&menu->items[i + 1], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i)); + } + + menu->items[index] = item; + menu->itemsCount++; + return 1; +} + +/** + * 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) +{ + return bmMenuAddItemAt(menu, item, menu->itemsCount); +} + +/** + * Remove item from bmMenu instance at specific index. + * + * @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) +{ + assert(menu != NULL); + + unsigned int i = index; + if (i >= menu->itemsCount) + return 0; + + memmove(&menu->items[i], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i)); + return 1; +} + +/** + * Remove item from bmMenu instance. + * + * @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) +{ + assert(menu != NULL); + assert(item != NULL); + + unsigned int i; + for (i = 0; i < menu->itemsCount && menu->items[i] != item; ++i); + return bmMenuRemoveItemAt(menu, i); +} + +/** + * Get selected item from bmMenu instance. + * + * @param menu bmMenu instance from where to get selected item. + * @return Selected bmItem instance, NULL if none selected. + */ +bmItem* bmMenuGetSelectedItem(const bmMenu *menu) +{ + assert(menu != NULL); + + unsigned int count; + bmItem **items = bmMenuGetFilteredItems(menu, &count); + + if (!items || count < menu->index) + return NULL; + + return items[menu->index]; +} + +/** + * Get items from bmMenu instance. + * + * @param menu bmMenu instance from where to get items. + * @param nmemb 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 *nmemb) +{ + assert(menu != NULL); + + if (nmemb) + *nmemb = menu->itemsCount; + + return menu->items; +} + +/** + * 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 nmemb 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 *nmemb) +{ + assert(menu != NULL); + + if (nmemb) + *nmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + + return (menu->filteredItems ? menu->filteredItems : menu->items); +} + +/** + * Set items to bmMenu instance. + * Will replace all the old items on 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 bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) +{ + assert(menu != NULL); + + if (items == NULL || nmemb == 0) { + bmMenuFreeItems(menu); + return 1; + } + + bmItem **newItems; + if (!(newItems = calloc(sizeof(bmItem*), nmemb))) + return 0; + + memcpy(newItems, items, sizeof(bmItem*) * nmemb); + bmMenuFreeItems(menu); + + menu->items = newItems; + menu->allocatedCount = menu->itemsCount = nmemb; + return 1; +} + +/** + * 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. + */ +void bmMenuRender(const bmMenu *menu) +{ + assert(menu != NULL); + + if (menu->renderApi.render) + menu->renderApi.render(menu); +} + +/** + * Poll key and unicode from underlying UI toolkit. + * + * This function will block on CURSES draw mode. + * + * @param menu bmMenu instance from which to poll. + * @param unicode Reference to unsigned int. + * @return bmKey for polled key. + */ +bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) +{ + assert(menu != NULL); + assert(unicode != NULL); + + *unicode = 0; + bmKey key = BM_KEY_NONE; + + if (menu->renderApi.getKey) + key = menu->renderApi.getKey(unicode); + + return key; +} + +/** + * Advances menu logic with key and unicode as input. + * + * @param menu bmMenu instance to be advanced. + * @return bmRunResult for menu state. + */ +bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) +{ + assert(menu != NULL); + char *oldFilter = _bmStrdup(menu->filter); + unsigned int itemsCount = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + + switch (key) { + case BM_KEY_LEFT: + { + unsigned int oldCursor = menu->cursor; + menu->cursor -= _bmUtf8RunePrev(menu->filter, menu->cursor); + menu->cursesCursor -= _bmUtf8RuneWidth(menu->filter + menu->cursor, oldCursor - menu->cursor); + } + break; + + case BM_KEY_RIGHT: + { + unsigned int oldCursor = menu->cursor; + menu->cursor += _bmUtf8RuneNext(menu->filter, menu->cursor); + menu->cursesCursor += _bmUtf8RuneWidth(menu->filter + oldCursor, menu->cursor - oldCursor); + } + break; + + case BM_KEY_HOME: + menu->cursesCursor = menu->cursor = 0; + break; + + case BM_KEY_END: + menu->cursor = strlen(menu->filter); + menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + break; + + case BM_KEY_UP: + if (menu->index > 0) + menu->index--; + break; + + case BM_KEY_DOWN: + if (menu->index < itemsCount - 1) + menu->index++; + break; + + case BM_KEY_PAGE_UP: + menu->index = 0; + break; + + case BM_KEY_PAGE_DOWN: + menu->index = itemsCount - 1; + break; + + case BM_KEY_BACKSPACE: + { + size_t width; + menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); + menu->cursesCursor -= width; + } + break; + + case BM_KEY_DELETE: + _bmUtf8RuneRemove(menu->filter, menu->cursor + 1, NULL); + break; + + case BM_KEY_LINE_DELETE_LEFT: + { + while (menu->cursor > 0) { + size_t width; + menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); + menu->cursesCursor -= width; + } + } + break; + + case BM_KEY_LINE_DELETE_RIGHT: + menu->filter[menu->cursor] = 0; + break; + + case BM_KEY_WORD_DELETE: + { + 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); + } + 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); + } + while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) { + size_t width; + menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); + menu->cursesCursor -= width; + } + } + break; + + case BM_KEY_UNICODE: + { + size_t width; + menu->cursor += _bmUnicodeInsert(menu->filter, sizeof(menu->filter) - 1, menu->cursor, unicode, &width); + menu->cursesCursor += width; + } + break; + + case BM_KEY_TAB: + { + bmItem *selected = bmMenuGetSelectedItem(menu); + if (selected && bmItemGetText(selected)) { + const char *text = bmItemGetText(selected); + size_t len = strlen(text); + + if (len > sizeof(menu->filter) - 1) + len = sizeof(menu->filter) - 1; + + memset(menu->filter, 0, strlen(menu->filter)); + memcpy(menu->filter, text, len); + menu->cursor = strlen(menu->filter); + menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + } + } + break; + + case BM_KEY_RETURN: + return BM_RUN_RESULT_SELECTED; + + case BM_KEY_ESCAPE: + return BM_RUN_RESULT_CANCEL; + + default: break; + } + + if (oldFilter && strcmp(oldFilter, menu->filter)) + _bmMenuFilter(menu); + + if (oldFilter) + free(oldFilter); + return BM_RUN_RESULT_RUNNING; +} + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/util.c b/lib/util.c new file mode 100644 index 0000000..8c9bc0f --- /dev/null +++ b/lib/util.c @@ -0,0 +1,226 @@ +/** + * @file util.c + */ + +#include "internal.h" +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include + +/** + * Portable strdup. + * + * @param string C "string" to copy. + * @return Copy of the given C "string". + */ +char* _bmStrdup(const char *string) +{ + size_t len = strlen(string); + if (len == 0) + return NULL; + + void *copy = calloc(1, len + 1); + if (copy == NULL) + return NULL; + + return (char *)memcpy(copy, string, len); +} + +/** + * Shrink bmItem** list pointer. + * + * Useful helper function for filter functions. + * + * @param list Pointer to pointer to list of bmItem 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. + */ +bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) +{ + if (nsize >= osize) + return *list; + + void *tmp = malloc(sizeof(bmItem*) * nsize); + if (!tmp) + return *list; + + memcpy(tmp, *list, sizeof(bmItem*) * nsize); + free(*list); + *list = tmp; + return *list; +} + +/** + * Determite columns needed to display UTF8 string. + * + * @param string C "string" to determite. + * @return Number of columns, or -1 on failure. + */ +int _bmUtf8StringScreenWidth(const char *string) +{ + if (!string) + return 0; + + int num_char = mbstowcs(NULL, string, 0) + 1; + wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0])); + + if (mbstowcs(wstring, string, num_char) == (size_t)(-1)) { + free(wstring); + return strlen(string); + } + + int length = wcswidth(wstring, num_char); + free(wstring); + return length; +} + +/** + * Figure out how many bytes to shift to next UTF8 rune. + * + * @param string C "string" which contains the runes. + * @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) +{ + assert(string != NULL); + + size_t len = strlen(string), i = start; + if (len == 0 || len <= i || !*string) + return 0; + + while (++i < len && (string[i] & 0xc0) == 0x80); + return i - start; +} + +/** + * Figure out how many bytes to shift to previous UTF8 rune. + * + * @param string C "string" which contains the runes. + * @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) +{ + assert(string != NULL); + + size_t len = strlen(string), i = start; + if (i == 0 || len < start || !*string) + return 0; + + while (--i > 0 && (string[i] & 0xc0) == 0x80); + return start - i; +} + +/** + * Figure out how many columns are needed to display UTF8 rune. + * + * @param rune Buffer which contains the rune. + * @param u8len Byte length of the rune. + * @return Number of columns, or -1 on failure. + */ +size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) +{ + assert(rune != NULL); + char mb[5] = { 0, 0, 0, 0, 0 }; + memcpy(mb, rune, (u8len > 4 ? 4 : u8len)); + return _bmUtf8StringScreenWidth(mb); +} + +/** + * Remove previous UTF8 rune from buffer. + * + * @param string Null terminated C "string". + * @param start Start offset where to delete from. (cursor) + * @param runeWidth 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 *runeWidth) +{ + assert(string != NULL); + + if (runeWidth) + *runeWidth = 0; + + size_t len = strlen(string), oldStart = start; + if (len == 0 || len < start || !*string) + return 0; + + start -= _bmUtf8RunePrev(string, start); + + if (runeWidth) + *runeWidth = _bmUtf8RuneWidth(string + start, oldStart - start); + + memmove(string + start, string + oldStart, len - oldStart); + string[len - (oldStart - start)] = 0; + return (oldStart - start); +} + +/** + * Insert UTF8 rune to buffer. + * + * @param string Null terminated C "string". + * @param bufSize 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 runeWidth 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 *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth) +{ + assert(string != NULL); + + if (runeWidth) + *runeWidth = 0; + + size_t len = strlen(string); + if (len + u8len >= bufSize) + return 0; + + if (u8len == 1 && iscntrl(*rune)) + return 0; + + char *str = string + start; + memmove(str + u8len, str, len - start); + memcpy(str, rune, u8len); + + if (runeWidth) + *runeWidth = _bmUtf8RuneWidth(rune, u8len); + return u8len; +} + +/** + * Insert unicode character to UTF8 buffer. + * + * @param string Null terminated C "string". + * @param bufSize Size of the buffer. + * @param start Start offset where to insert to. (cursor) + * @param unicode Unicode character to insert. + * @param runeWidth 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 *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth) +{ + assert(string != NULL); + + char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); + char mb[5] = { 0, 0, 0, 0 }; + + if (u8len == 1) { + mb[0] = unicode; + } else { + size_t i, j; + for (i = j = u8len; j > 1; --j) mb[j - 1] = 0x80 | (0x3F & (unicode >> ((i - j) * 6))); + mb[0] = (~0) << (8 - i); + mb[0] |= (unicode >> (i * 6 - 6)); + } + + return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, runeWidth); +} + +/* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From 944e36b21d1bbc80680e228daa75f27b3eab4e4b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:25:37 +0300 Subject: Free filtered items. --- lib/menu.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index 50311cf..7a39746 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -98,6 +98,9 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); + if (menu->filteredItems) + free(menu->filteredItems); + bmMenuFreeItems(menu); free(menu); } -- cgit v1.2.3-70-g09d2 From dd4bddcf9877c5cf2f01cc035417f779565a0267 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:25:46 +0300 Subject: Do bounds checking correctly. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index 7a39746..b6116e9 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -272,7 +272,7 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) unsigned int count; bmItem **items = bmMenuGetFilteredItems(menu, &count); - if (!items || count < menu->index) + if (!items || count <= menu->index) return NULL; return items[menu->index]; -- cgit v1.2.3-70-g09d2 From 2615c43dc052617312ea102ab2a50cb449d9827b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:26:00 +0300 Subject: Move side effects after cleanup. --- lib/menu.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index b6116e9..5ea046a 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -510,12 +510,6 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; - case BM_KEY_RETURN: - return BM_RUN_RESULT_SELECTED; - - case BM_KEY_ESCAPE: - return BM_RUN_RESULT_CANCEL; - default: break; } @@ -524,6 +518,13 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) if (oldFilter) free(oldFilter); + + switch (key) { + case BM_KEY_RETURN: return BM_RUN_RESULT_SELECTED; + case BM_KEY_ESCAPE: return BM_RUN_RESULT_CANCEL; + default: break; + } + return BM_RUN_RESULT_RUNNING; } -- cgit v1.2.3-70-g09d2 From cff1f37f970adafce1ac3a982ea1d338e609cfed Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:31:22 +0300 Subject: Do curses refresh before endwin. --- lib/draw/curses.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 48a146a..5c6ff4a 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -128,6 +128,9 @@ static void _bmDrawCursesRender(const bmMenu *menu) static void _bmDrawCursesEndWin(void) { + if (curses.refresh) + curses.refresh(); + if (curses.endwin) curses.endwin(); -- cgit v1.2.3-70-g09d2 From 4d920ad9e4aa344c1600e7419f75f6adb647b1b0 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 01:41:32 +0300 Subject: Make asserts and ifs more consistent. --- client/client.c | 10 +++++----- lib/draw/curses.c | 2 +- lib/filter.c | 12 ++++++------ lib/item.c | 6 +++--- lib/menu.c | 42 +++++++++++++++++++++--------------------- lib/util.c | 19 +++++++++++-------- test/bmMenuNew.c | 2 +- 7 files changed, 48 insertions(+), 45 deletions(-) (limited to 'lib') diff --git a/client/client.c b/client/client.c index df56511..973e57a 100644 --- a/client/client.c +++ b/client/client.c @@ -17,22 +17,22 @@ static ptrdiff_t getLine(char **outLine, size_t *outAllocated, FILE *stream) size_t len = 0, allocated; char *s, *buffer; - assert(outLine != NULL); - assert(outAllocated != NULL); + assert(outLine); + assert(outAllocated); - if (stream == NULL || feof(stream) || ferror(stream)) + if (!stream || feof(stream) || ferror(stream)) return -1; allocated = *outAllocated; buffer = *outLine; - if (buffer == NULL || allocated == 0) { + if (!buffer || allocated == 0) { if (!(buffer = calloc(1, (allocated = 1024) + 1))) return -1; } for (s = buffer;;) { - if (fgets(s, allocated - (s - buffer), stream) == NULL) { + if (!fgets(s, allocated - (s - buffer), stream)) { *outAllocated = allocated; *outLine = buffer; return -1; diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 5c6ff4a..3582be5 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -139,7 +139,7 @@ static void _bmDrawCursesEndWin(void) static bmKey _bmDrawCursesGetKey(unsigned int *unicode) { - assert(unicode != NULL); + assert(unicode); *unicode = 0; if (!curses.stdscr) diff --git a/lib/filter.c b/lib/filter.c index b1cf668..d301415 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -17,9 +17,9 @@ */ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected) { - assert(menu != NULL); - assert(count != NULL); - assert(selected != NULL); + assert(menu); + assert(count); + assert(selected); *count = *selected = 0; /* FIXME: not real dmenu like filtering at all */ @@ -51,9 +51,9 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selecte */ bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected) { - assert(menu != NULL); - assert(count != NULL); - assert(selected != NULL); + assert(menu); + assert(count); + assert(selected); *count = *selected = 0; /* FIXME: stub */ diff --git a/lib/item.c b/lib/item.c index 40ebb52..beca6ed 100644 --- a/lib/item.c +++ b/lib/item.c @@ -31,7 +31,7 @@ bmItem* bmItemNew(const char *text) */ void bmItemFree(bmItem *item) { - assert(item != NULL); + assert(item); if (item->text) free(item->text); @@ -47,7 +47,7 @@ void bmItemFree(bmItem *item) */ int bmItemSetText(bmItem *item, const char *text) { - assert(item != NULL); + assert(item); char *copy = NULL; if (text && !(copy = _bmStrdup(text))) @@ -68,7 +68,7 @@ int bmItemSetText(bmItem *item, const char *text) */ const char* bmItemGetText(const bmItem *item) { - assert(item != NULL); + assert(item); return item->text; } diff --git a/lib/menu.c b/lib/menu.c index 5ea046a..ccdbf0d 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -15,7 +15,7 @@ static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *c static void _bmMenuFilter(bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->filteredItems) free(menu->filteredItems); @@ -90,7 +90,7 @@ bmMenu* bmMenuNew(bmDrawMode drawMode) */ void bmMenuFree(bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->renderApi.free) menu->renderApi.free(); @@ -112,7 +112,7 @@ void bmMenuFree(bmMenu *menu) */ void bmMenuFreeItems(bmMenu *menu) { - assert(menu != NULL); + assert(menu); unsigned int i; for (i = 0; i < menu->itemsCount; ++i) @@ -131,7 +131,7 @@ void bmMenuFreeItems(bmMenu *menu) */ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) { - assert(menu != NULL); + assert(menu); bmFilterMode oldMode = mode; menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode); @@ -148,7 +148,7 @@ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) */ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); return menu->filterMode; } @@ -160,7 +160,7 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) */ int bmMenuSetTitle(bmMenu *menu, const char *title) { - assert(menu != NULL); + assert(menu); char *copy = NULL; if (title && !(copy = _bmStrdup(title))) @@ -181,7 +181,7 @@ int bmMenuSetTitle(bmMenu *menu, const char *title) */ const char* bmMenuGetTitle(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); return menu->title; } @@ -195,8 +195,8 @@ const char* bmMenuGetTitle(const bmMenu *menu) */ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { - assert(menu != NULL); - assert(item != NULL); + assert(menu); + assert(item); if (menu->itemsCount >= menu->allocatedCount && !_bmMenuGrowItems(menu)) return 0; @@ -232,7 +232,7 @@ int bmMenuAddItem(bmMenu *menu, bmItem *item) */ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) { - assert(menu != NULL); + assert(menu); unsigned int i = index; if (i >= menu->itemsCount) @@ -251,8 +251,8 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) */ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) { - assert(menu != NULL); - assert(item != NULL); + assert(menu); + assert(item); unsigned int i; for (i = 0; i < menu->itemsCount && menu->items[i] != item; ++i); @@ -267,7 +267,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) */ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); unsigned int count; bmItem **items = bmMenuGetFilteredItems(menu, &count); @@ -287,7 +287,7 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) */ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) { - assert(menu != NULL); + assert(menu); if (nmemb) *nmemb = menu->itemsCount; @@ -307,7 +307,7 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) */ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) { - assert(menu != NULL); + assert(menu); if (nmemb) *nmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); @@ -326,9 +326,9 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb) */ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) { - assert(menu != NULL); + assert(menu); - if (items == NULL || nmemb == 0) { + if (!items || nmemb == 0) { bmMenuFreeItems(menu); return 1; } @@ -353,7 +353,7 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) */ void bmMenuRender(const bmMenu *menu) { - assert(menu != NULL); + assert(menu); if (menu->renderApi.render) menu->renderApi.render(menu); @@ -370,8 +370,8 @@ void bmMenuRender(const bmMenu *menu) */ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) { - assert(menu != NULL); - assert(unicode != NULL); + assert(menu); + assert(unicode); *unicode = 0; bmKey key = BM_KEY_NONE; @@ -390,7 +390,7 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) */ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) { - assert(menu != NULL); + assert(menu); char *oldFilter = _bmStrdup(menu->filter); unsigned int itemsCount = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); diff --git a/lib/util.c b/lib/util.c index 8c9bc0f..d948b05 100644 --- a/lib/util.c +++ b/lib/util.c @@ -18,6 +18,8 @@ */ char* _bmStrdup(const char *string) { + assert(string); + size_t len = strlen(string); if (len == 0) return NULL; @@ -41,6 +43,8 @@ char* _bmStrdup(const char *string) */ bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) { + assert(list); + if (nsize >= osize) return *list; @@ -62,8 +66,7 @@ bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) */ int _bmUtf8StringScreenWidth(const char *string) { - if (!string) - return 0; + assert(string); int num_char = mbstowcs(NULL, string, 0) + 1; wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0])); @@ -87,7 +90,7 @@ int _bmUtf8StringScreenWidth(const char *string) */ size_t _bmUtf8RuneNext(const char *string, size_t start) { - assert(string != NULL); + assert(string); size_t len = strlen(string), i = start; if (len == 0 || len <= i || !*string) @@ -106,7 +109,7 @@ size_t _bmUtf8RuneNext(const char *string, size_t start) */ size_t _bmUtf8RunePrev(const char *string, size_t start) { - assert(string != NULL); + assert(string); size_t len = strlen(string), i = start; if (i == 0 || len < start || !*string) @@ -125,7 +128,7 @@ size_t _bmUtf8RunePrev(const char *string, size_t start) */ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) { - assert(rune != NULL); + assert(rune); char mb[5] = { 0, 0, 0, 0, 0 }; memcpy(mb, rune, (u8len > 4 ? 4 : u8len)); return _bmUtf8StringScreenWidth(mb); @@ -141,7 +144,7 @@ size_t _bmUtf8RuneWidth(const char *rune, unsigned int u8len) */ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) { - assert(string != NULL); + assert(string); if (runeWidth) *runeWidth = 0; @@ -173,7 +176,7 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) */ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth) { - assert(string != NULL); + assert(string); if (runeWidth) *runeWidth = 0; @@ -206,7 +209,7 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char */ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth) { - assert(string != NULL); + assert(string); char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); char mb[5] = { 0, 0, 0, 0 }; diff --git a/test/bmMenuNew.c b/test/bmMenuNew.c index e1b6161..ccb572d 100644 --- a/test/bmMenuNew.c +++ b/test/bmMenuNew.c @@ -11,7 +11,7 @@ int main(int argc, char **argv) bmDrawMode i; for (i = 0; i < BM_DRAW_MODE_LAST; ++i) { bmMenu *menu = bmMenuNew(i); - assert(menu != NULL); + assert(menu); bmMenuRender(menu); bmMenuFree(menu); } -- cgit v1.2.3-70-g09d2 From 956494767c5f2fef7b5672ba60532224ea608d39 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 10:56:38 +0300 Subject: lolCase variables --- lib/draw/curses.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3582be5..3afd9bc 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -48,16 +48,16 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) { static int ncols = 0; static char *buffer = NULL; - int new_ncols = curses.getmaxx(curses.stdscr); + int newNcols = curses.getmaxx(curses.stdscr); - if (new_ncols <= 0) + if (newNcols <= 0) return; - if (!buffer || new_ncols > ncols) { + if (!buffer || newNcols > ncols) { if (buffer) free(buffer); - ncols = new_ncols; + ncols = newNcols; if (!(buffer = calloc(1, ncols + 1))) return; -- cgit v1.2.3-70-g09d2 From efc5781aec94563871d56638d3ba92475b43e58b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 10:57:37 +0300 Subject: Consistency --- lib/draw/curses.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3afd9bc..eecfcba 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -148,37 +148,54 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) curses.get_wch(unicode); switch (*unicode) { case 16: /* C-p */ - case KEY_UP: return BM_KEY_UP; + case KEY_UP: + return BM_KEY_UP; case 14: /* C-n */ - case KEY_DOWN: return BM_KEY_DOWN; + case KEY_DOWN: + return BM_KEY_DOWN; case 2: /* C-b */ - case KEY_LEFT: return BM_KEY_LEFT; + case KEY_LEFT: + return BM_KEY_LEFT; case 6: /* C-f */ - case KEY_RIGHT: return BM_KEY_RIGHT; + case KEY_RIGHT: + return BM_KEY_RIGHT; case 1: /* C-a */ - case KEY_HOME: return BM_KEY_HOME; + case KEY_HOME: + return BM_KEY_HOME; case 5: /* C-e */ - case KEY_END: return BM_KEY_END; + case KEY_END: + return BM_KEY_END; - case KEY_PPAGE: return BM_KEY_PAGE_UP; - case KEY_NPAGE: return BM_KEY_PAGE_DOWN; + case KEY_PPAGE: /* PAGE UP */ + return BM_KEY_PAGE_UP; + + case KEY_NPAGE: /* PAGE DOWN */ + return BM_KEY_PAGE_DOWN; case 8: /* C-h */ - case KEY_BACKSPACE: return BM_KEY_BACKSPACE; + case KEY_BACKSPACE: + return BM_KEY_BACKSPACE; case 4: /* C-d */ - case KEY_DC: return BM_KEY_DELETE; + case KEY_DC: + return BM_KEY_DELETE; + + case 21: /* C-u */ + return BM_KEY_LINE_DELETE_LEFT; + + case 11: /* C-k */ + return BM_KEY_LINE_DELETE_RIGHT; - case 21: return BM_KEY_LINE_DELETE_LEFT; /* C-u */ - case 11: return BM_KEY_LINE_DELETE_RIGHT; /* C-k */ - case 23: return BM_KEY_WORD_DELETE; /* C-w */ + case 23: /* C-w */ + return BM_KEY_WORD_DELETE; - case 9: return BM_KEY_TAB; /* Tab */ + case 9: /* Tab */ + return BM_KEY_TAB; case 10: /* Return */ _bmDrawCursesEndWin(); -- cgit v1.2.3-70-g09d2 From 722af2f7a0fe0e437e233cfaf7acadc3bcd0f24e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 11:07:37 +0300 Subject: Clarify which enums the functions are mapped to. --- lib/menu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index ccdbf0d..184c976 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -8,9 +8,12 @@ #include #include +/** + * Filter function map. + */ static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *count, unsigned int *selected) = { - _bmFilterDmenu, - _bmFilterDmenuCaseInsensitive + _bmFilterDmenu, /* BM_FILTER_DMENU */ + _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; static void _bmMenuFilter(bmMenu *menu) -- cgit v1.2.3-70-g09d2 From ff8ad3a804459ddc4e407b688b093b6f4543cf53 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 11:08:10 +0300 Subject: Remove @file from non-public library files. --- lib/draw/curses.c | 4 ---- lib/filter.c | 4 ---- lib/internal.h | 4 ---- lib/item.c | 4 ---- lib/menu.c | 4 ---- lib/util.c | 4 ---- 6 files changed, 24 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index eecfcba..a0f5351 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,7 +1,3 @@ -/** - * @file curses.c - */ - #include "../internal.h" #include #include diff --git a/lib/filter.c b/lib/filter.c index d301415..92e5683 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -1,7 +1,3 @@ -/** - * @file filter.c - */ - #include "internal.h" #include #include diff --git a/lib/internal.h b/lib/internal.h index 786e082..2220333 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -1,7 +1,3 @@ -/** - * @file internal.h - */ - #include "bemenu.h" #ifndef size_t diff --git a/lib/item.c b/lib/item.c index beca6ed..8320b9b 100644 --- a/lib/item.c +++ b/lib/item.c @@ -1,7 +1,3 @@ -/** - * @file item.c - */ - #include "internal.h" #include #include diff --git a/lib/menu.c b/lib/menu.c index 184c976..4ccb94a 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -1,7 +1,3 @@ -/** - * @file bemenu.c - */ - #include "internal.h" #include #include diff --git a/lib/util.c b/lib/util.c index d948b05..ae230c8 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,7 +1,3 @@ -/** - * @file util.c - */ - #include "internal.h" #define _XOPEN_SOURCE 700 #include -- cgit v1.2.3-70-g09d2 From 04dede1f96d965d3837ad9690a4ac076c99f10b3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 12:07:23 +0300 Subject: Remove unused internal function. --- lib/internal.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib') diff --git a/lib/internal.h b/lib/internal.h index 2220333..a07d1cd 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -40,9 +40,6 @@ struct _bmMenu { /* draw/curses.c */ int _bmDrawCursesInit(struct _bmRenderApi *api); -/* menu.c */ -int _bmMenuShouldRenderItem(const bmMenu *menu, const bmItem *item); - /* filter.c */ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected); bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected); -- cgit v1.2.3-70-g09d2 From e230f03e54e0ad0c521177cd7a3f4c7628608d15 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:08:26 +0300 Subject: Fix bmMenuRender documentation --- lib/menu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index 4ccb94a..29a3058 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -345,10 +345,9 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) } /** - * Create new bmMenu instance. + * Render bmMenu instance using chosen draw method. * - * @param drawMode Render method to be used for this menu instance. - * @return bmMenu for new menu instance, NULL if creation failed. + * @param menu bmMenu instance to be rendered. */ void bmMenuRender(const bmMenu *menu) { -- cgit v1.2.3-70-g09d2 From d53c91393d0c66892d25e4ef96cd23568d9fdcea Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:08:38 +0300 Subject: Fix bmMenuRunWithKey documentation --- lib/bemenu.h | 2 ++ lib/menu.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index b4b9b30..d8e604b 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -227,6 +227,8 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode); * 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); diff --git a/lib/menu.c b/lib/menu.c index 29a3058..c8c6a26 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -384,6 +384,8 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) * 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) -- cgit v1.2.3-70-g09d2 From 9a04c4a9a7ff424741f3b6f9b0e34a6f998da7b2 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:23:03 +0300 Subject: Document curses struct --- lib/draw/curses.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index a0f5351..c1bb013 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -20,6 +20,9 @@ #undef getmaxy #undef timeout +/** + * Dynamically loaded curses API. + */ static struct curses { void *handle; WINDOW *stdscr; -- cgit v1.2.3-70-g09d2 From 78713f23ac61f65a970b2870534327009213790b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 13:23:42 +0300 Subject: Document internal.h --- lib/internal.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/internal.h b/lib/internal.h index a07d1cd..41fb926 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -9,6 +9,10 @@ * Represents a single item in menu. */ struct _bmItem { + /** + * Primary text shown on item as null terminated C "string". + * Matching will be done against this text as well. + */ char *text; }; @@ -17,8 +21,20 @@ struct _bmItem { * Renderers should be able to fill this one as they see fit. */ struct _bmRenderApi { + /** + * 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); + + /** + * Tells underlying renderer to draw the menu. + */ void (*render)(const bmMenu *menu); + + /** + * Release underlying renderer. + */ void (*free)(void); }; @@ -26,14 +42,72 @@ struct _bmRenderApi { * Internal bmMenu struct that is not exposed to public. */ struct _bmMenu { + /** + * Underlying renderer access. + */ struct _bmRenderApi renderApi; - struct _bmItem **items, **filteredItems; - char *title, filter[1024]; - unsigned int cursor, cursesCursor; - unsigned int itemsCount, allocatedCount; + + /** + * All items contained in menu instance. + */ + struct _bmItem **items; + + /** + * Filtered/displayed items contained in menu instance. + */ + struct _bmItem **filteredItems; + + /** + * Menu instance title. + */ + char *title; + + /** + * Text used to filter matches. + * + * XXX: Change this to a pointer? + */ + char filter[1024]; + + /** + * Current byte offset on filter text. + */ + unsigned int cursor; + + /** + * Current column/cursor position on filter text. + */ + unsigned int cursesCursor; + + /** + * Number of items in menu instance. + */ + unsigned int itemsCount; + + /** + * Number of filtered items in menu instance. + */ unsigned int filteredCount; + + /** + * Number of allocated items in menu instance. + */ + unsigned int allocatedCount; + + /** + * Current filtered item index in menu instance. + * This index is valid for the list returned by bmMenuGetFilteredItems. + */ unsigned int index; + + /** + * Current filtering method in menu instance. + */ bmFilterMode filterMode; + + /** + * Drawing mode used in menu instance. + */ bmDrawMode drawMode; }; -- cgit v1.2.3-70-g09d2 From 8631506d9d1522f1a24e528f75682201b4f3cc7b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 17:26:42 +0300 Subject: Update documentation, include only public API in doxygen output. --- client/client.c | 16 ------- doxygen/Doxyfile.in | 6 +-- doxygen/Mainpage.dox | 2 + lib/bemenu.h | 119 ++++++++++++++++++++++++++++++++++++++++----------- lib/filter.c | 28 ++++++------ lib/internal.h | 13 +++--- lib/item.c | 8 ++-- lib/menu.c | 46 ++++++++++++-------- lib/util.c | 46 ++++++++++---------- 9 files changed, 172 insertions(+), 112 deletions(-) (limited to 'lib') diff --git a/client/client.c b/client/client.c index 973e57a..4419967 100644 --- a/client/client.c +++ b/client/client.c @@ -1,10 +1,3 @@ -/** - * @file client.c - * - * Sample client using the libbemenu. - * Also usable as dmenu replacement. - */ - #include #include #include @@ -86,15 +79,6 @@ static void readItemsToMenuFromStdin(bmMenu *menu) free(line); } -/** - * Main method - * - * This function gives and takes the life of our program. - * - * @param argc Number of arguments from command line - * @param argv Pointer to array of the arguments - * @return exit status of the program - */ int main(int argc, char **argv) { (void)argc, (void)argv; diff --git a/doxygen/Doxyfile.in b/doxygen/Doxyfile.in index fcd3604..3b80187 100644 --- a/doxygen/Doxyfile.in +++ b/doxygen/Doxyfile.in @@ -90,7 +90,7 @@ OUTPUT_LANGUAGE = English # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. -BRIEF_MEMBER_DESC = NO +BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description @@ -765,7 +765,7 @@ INPUT_ENCODING = UTF-8 # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. -FILE_PATTERNS = +FILE_PATTERNS = *.h *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -796,7 +796,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = internal.h # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/doxygen/Mainpage.dox b/doxygen/Mainpage.dox index 8da4792..869071c 100644 --- a/doxygen/Mainpage.dox +++ b/doxygen/Mainpage.dox @@ -16,4 +16,6 @@ Features: - Curses bemenu also provides 'bemenu' executable that is compatible with dmenu interface. + +Get started on the Modules page. */ diff --git a/lib/bemenu.h b/lib/bemenu.h index d8e604b..f2f0372 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -1,13 +1,34 @@ /** * @file bemenu.h * - * Public header + * Public API header. */ +typedef struct _bmMenu bmMenu; +typedef struct _bmItem bmItem; + +/** + * @defgroup Menu + * @brief Menu container. + * + * Holds all the items, runs logic and gets rendered. + */ + +/** + * @defgroup Item + * @brief Item container. + * + * Contains properties for visual representation of item. + */ + +/** + * @addtogroup Menu + * @{ */ + /** * Draw mode constants for bmMenu instance draw mode. * - * BM_DRAW_MODE_LAST is provided for enumerating draw modes. + * @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. */ typedef enum bmDrawMode { @@ -19,7 +40,7 @@ typedef enum bmDrawMode { /** * Filter mode constants for bmMenu instance filter mode. * - * BM_FILTER_MODE_LAST is provided for enumerating filter modes. + * @link ::bmFilterMode 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 { @@ -31,9 +52,9 @@ typedef enum bmFilterMode { /** * Result constants from bmMenuRunWithKey function. * - * BM_RUN_RESULT_RUNNING means that menu is running and thus should be still renderer && ran. - * BM_RUN_RESULT_SELECTED means that menu was closed and items were selected. - * BM_RUN_RESULT_CANCEL means that menu was closed and selection was canceled. + * - @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. */ typedef enum bmRunResult { BM_RUN_RESULT_RUNNING, @@ -44,7 +65,7 @@ typedef enum bmRunResult { /** * Key constants. * - * BM_KEY_LAST is provided for enumerating keys. + * @link ::bmKey BM_KEY_LAST @endlink is provided for enumerating keys. */ typedef enum bmKey { BM_KEY_NONE, @@ -68,14 +89,15 @@ typedef enum bmKey { BM_KEY_LAST } bmKey; -typedef struct _bmMenu bmMenu; -typedef struct _bmItem bmItem; +/** + * @name Menu Memory + * @{ */ /** * 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. + * @return bmMenu for new menu instance, **NULL** if creation failed. */ bmMenu* bmMenuNew(bmDrawMode drawMode); @@ -93,6 +115,12 @@ void bmMenuFree(bmMenu *menu); */ void bmMenuFreeItems(bmMenu *menu); +/** @} Menu Memory */ + +/** + * @name Menu Properties + * @{ */ + /** * Set active filter mode to bmMenu instance. * @@ -113,7 +141,7 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu); * 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. + * @param title C "string" to set as title, can be **NULL** for empty title. */ int bmMenuSetTitle(bmMenu *menu, const char *title); @@ -121,10 +149,16 @@ int bmMenuSetTitle(bmMenu *menu, const char *title); * 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. + * @return Pointer to null terminated C "string", can be **NULL** for empty title. */ const char* bmMenuGetTitle(const bmMenu *menu); +/** @} Properties */ + +/** + * @name Menu Items + * @{ */ + /** * Add item to bmMenu instance at specific index. * @@ -147,6 +181,8 @@ int bmMenuAddItem(bmMenu *menu, bmItem *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. @@ -155,7 +191,8 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index); /** * Remove item from bmMenu instance. - * The item won't be freed, use bmItemFree to do that. + * + * @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. @@ -167,18 +204,20 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item); * Get selected item from bmMenu instance. * * @param menu bmMenu instance from where to get selected item. - * @return Selected bmItem instance, NULL if none selected. + * @return Selected bmItem instance, **NULL** if none selected. */ bmItem* bmMenuGetSelectedItem(const bmMenu *menu); /** * 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 nmemb Reference to unsigned int where total count of returned items will be stored. + * @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 *nmemb); +bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); /** * Get filtered (displayed) items from bmMenu instance. @@ -187,16 +226,16 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb); * Do not store this pointer. * * @param menu bmMenu instance from where to get filtered items. - * @param nmemb Reference to unsigned int where total count of returned items will be stored. + * @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 *nmemb); +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); /** * 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. + * 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. @@ -205,6 +244,12 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *nmemb); */ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb); +/** @} Menu Items */ + +/** + * @name Menu Logic + * @{ */ + /** * Render bmMenu instance using chosen draw method. * @@ -215,13 +260,13 @@ void bmMenuRender(const bmMenu *menu); /** * Poll key and unicode from underlying UI toolkit. * - * This function will block on CURSES draw mode. + * This function will block on @link ::bmDrawMode BM_DRAW_MODE_CURSES @endlink draw mode. * * @param menu bmMenu instance from which to poll. - * @param unicode Reference to unsigned int. + * @param outUnicode Reference to unsigned int. * @return bmKey for polled key. */ -bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode); +bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode); /** * Advances menu logic with key and unicode as input. @@ -233,11 +278,23 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode); */ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode); +/** @} Menu Logic */ + +/** @} Menu */ + +/** + * @addtogroup Item + * @{ */ + +/** + * @name Item Memory + * @{ */ + /** * 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. + * @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); @@ -248,11 +305,17 @@ bmItem* bmItemNew(const char *text); */ void bmItemFree(bmItem *item); +/** @} Item Memory */ + +/** + * @name Item Properties + * @{ */ + /** * 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. + * @param text C "string" to set as text, can be **NULL** for empty text. */ int bmItemSetText(bmItem *item, const char *text); @@ -260,8 +323,12 @@ int bmItemSetText(bmItem *item, const char *text); * 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. + * @return Pointer to null terminated C "string", can be **NULL** for empty text. */ const char* bmItemGetText(const bmItem *item); +/** @} Item Properties */ + +/** @} Item */ + /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/filter.c b/lib/filter.c index 92e5683..33126c0 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -7,16 +7,16 @@ * Filter that mimics the vanilla dmenu filtering. * * @param menu bmMenu instance to filter. - * @param count unsigned int reference to filtered items count. - * @param selected unsigned int reference to new selected item index. + * @param outNmemb unsigned int reference to filtered items outNmemb. + * @param outSelected unsigned int reference to new outSelected item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected) +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) { assert(menu); - assert(count); - assert(selected); - *count = *selected = 0; + assert(outNmemb); + assert(outSelected); + *outNmemb = *outSelected = 0; /* FIXME: not real dmenu like filtering at all */ @@ -29,28 +29,28 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selecte bmItem *item = menu->items[i]; if (item->text && strstr(item->text, menu->filter)) { if (f == 0 || item == bmMenuGetSelectedItem(menu)) - *selected = f; + *outSelected = f; filtered[f++] = item; } } - return _bmShrinkItemList(&filtered, menu->itemsCount, (*count = f)); + return _bmShrinkItemList(&filtered, menu->itemsCount, (*outNmemb = f)); } /** * Filter that mimics the vanilla case-insensitive dmenu filtering. * * @param menu bmMenu instance to filter. - * @param count unsigned int reference to filtered items count. - * @param selected unsigned int reference to new selected item index. + * @param outNmemb unsigned int reference to filtered items outNmemb. + * @param outSelected unsigned int reference to new outSelected item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) { assert(menu); - assert(count); - assert(selected); - *count = *selected = 0; + assert(outNmemb); + assert(outSelected); + *outNmemb = *outSelected = 0; /* FIXME: stub */ diff --git a/lib/internal.h b/lib/internal.h index 41fb926..66eabbc 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -64,7 +64,6 @@ struct _bmMenu { /** * Text used to filter matches. - * * XXX: Change this to a pointer? */ char filter[1024]; @@ -115,18 +114,18 @@ struct _bmMenu { int _bmDrawCursesInit(struct _bmRenderApi *api); /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *count, unsigned int *selected); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *count, unsigned int *selected); +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected); /* util.c */ char* _bmStrdup(const char *s); -bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize); +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 *runeWidth); -size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth); -size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth); +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); /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/item.c b/lib/item.c index 8320b9b..e6ccc7a 100644 --- a/lib/item.c +++ b/lib/item.c @@ -6,8 +6,8 @@ /** * Allocate a new item. * - * @param text Pointer to null terminated C "string", can be NULL for empty text. - * @return bmItem instance. + * @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) { @@ -39,7 +39,7 @@ void bmItemFree(bmItem *item) * 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. + * @param text C "string" to set as text, can be **NULL** for empty text. */ int bmItemSetText(bmItem *item, const char *text) { @@ -60,7 +60,7 @@ int bmItemSetText(bmItem *item, const char *text) * 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. + * @return Pointer to null terminated C "string", can be **NULL** for empty text. */ const char* bmItemGetText(const bmItem *item) { diff --git a/lib/menu.c b/lib/menu.c index c8c6a26..3d41121 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,7 +7,7 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *count, unsigned int *selected) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; @@ -53,7 +53,7 @@ static int _bmMenuGrowItems(bmMenu *menu) * 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. + * @return bmMenu for new menu instance, **NULL** if creation failed. */ bmMenu* bmMenuNew(bmDrawMode drawMode) { @@ -155,7 +155,7 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) * 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. + * @param title C "string" to set as title, can be **NULL** for empty title. */ int bmMenuSetTitle(bmMenu *menu, const char *title) { @@ -176,7 +176,7 @@ int bmMenuSetTitle(bmMenu *menu, const char *title) * 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. + * @return Pointer to null terminated C "string", can be **NULL** for empty title. */ const char* bmMenuGetTitle(const bmMenu *menu) { @@ -225,6 +225,8 @@ int bmMenuAddItem(bmMenu *menu, bmItem *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. @@ -244,6 +246,8 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) /** * 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. @@ -262,7 +266,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) * Get selected item from bmMenu instance. * * @param menu bmMenu instance from where to get selected item. - * @return Selected bmItem instance, NULL if none selected. + * @return Selected bmItem instance, **NULL** if none selected. */ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) { @@ -280,16 +284,18 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) /** * 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 nmemb Reference to unsigned int where total count of returned items will be stored. + * @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 *nmemb) +bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (nmemb) - *nmemb = menu->itemsCount; + if (outNmemb) + *outNmemb = menu->itemsCount; return menu->items; } @@ -301,15 +307,15 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *nmemb) * Do not store this pointer. * * @param menu bmMenu instance from where to get filtered items. - * @param nmemb Reference to unsigned int where total count of returned items will be stored. + * @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 *nmemb) +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (nmemb) - *nmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + if (outNmemb) + *outNmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); return (menu->filteredItems ? menu->filteredItems : menu->items); } @@ -318,6 +324,8 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *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. @@ -360,22 +368,22 @@ void bmMenuRender(const bmMenu *menu) /** * Poll key and unicode from underlying UI toolkit. * - * This function will block on CURSES draw mode. + * This function will block on @link ::bmDrawMode BM_DRAW_MODE_CURSES @endlink draw mode. * * @param menu bmMenu instance from which to poll. - * @param unicode Reference to unsigned int. + * @param outUnicode Reference to unsigned int. * @return bmKey for polled key. */ -bmKey bmMenuGetKey(bmMenu *menu, unsigned int *unicode) +bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode) { assert(menu); - assert(unicode); + assert(outUnicode); - *unicode = 0; + *outUnicode = 0; bmKey key = BM_KEY_NONE; if (menu->renderApi.getKey) - key = menu->renderApi.getKey(unicode); + key = menu->renderApi.getKey(outUnicode); return key; } diff --git a/lib/util.c b/lib/util.c index ae230c8..5eb980d 100644 --- a/lib/util.c +++ b/lib/util.c @@ -37,21 +37,21 @@ char* _bmStrdup(const char *string) * @param nsize New size the list will be shrinked to. * @return Pointer to list of bmItem pointers. */ -bmItem** _bmShrinkItemList(bmItem ***list, size_t osize, size_t nsize) +bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize) { - assert(list); + assert(inOutList); if (nsize >= osize) - return *list; + return *inOutList; void *tmp = malloc(sizeof(bmItem*) * nsize); if (!tmp) - return *list; + return *inOutList; - memcpy(tmp, *list, sizeof(bmItem*) * nsize); - free(*list); - *list = tmp; - return *list; + memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize); + free(*inOutList); + *inOutList = tmp; + return *inOutList; } /** @@ -135,15 +135,15 @@ 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 runeWidth Reference to size_t, return number of columns for removed rune, or -1 on failure. + * @param outRuneWidth 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 *runeWidth) +size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth) { assert(string); - if (runeWidth) - *runeWidth = 0; + if (outRuneWidth) + *outRuneWidth = 0; size_t len = strlen(string), oldStart = start; if (len == 0 || len < start || !*string) @@ -151,8 +151,8 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) start -= _bmUtf8RunePrev(string, start); - if (runeWidth) - *runeWidth = _bmUtf8RuneWidth(string + start, oldStart - start); + if (outRuneWidth) + *outRuneWidth = _bmUtf8RuneWidth(string + start, oldStart - start); memmove(string + start, string + oldStart, len - oldStart); string[len - (oldStart - start)] = 0; @@ -167,15 +167,15 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *runeWidth) * @param start Start offset where to insert to. (cursor) * @param rune Buffer to insert to string. * @param u8len Byte length of the rune. - * @param runeWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. + * @param outRuneWidth 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 *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *runeWidth) +size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth) { assert(string); - if (runeWidth) - *runeWidth = 0; + if (outRuneWidth) + *outRuneWidth = 0; size_t len = strlen(string); if (len + u8len >= bufSize) @@ -188,8 +188,8 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); - if (runeWidth) - *runeWidth = _bmUtf8RuneWidth(rune, u8len); + if (outRuneWidth) + *outRuneWidth = _bmUtf8RuneWidth(rune, u8len); return u8len; } @@ -200,10 +200,10 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char * @param bufSize Size of the buffer. * @param start Start offset where to insert to. (cursor) * @param unicode Unicode character to insert. - * @param runeWidth Reference to size_t, return number of columns for inserted rune, or -1 on failure. + * @param outRuneWidth 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 *string, size_t bufSize, size_t start, unsigned int unicode, size_t *runeWidth) +size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth) { assert(string); @@ -219,7 +219,7 @@ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int mb[0] |= (unicode >> (i * 6 - 6)); } - return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, runeWidth); + return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, outRuneWidth); } /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From e874818889ed799c8f4e4db2adf77dd19013c878 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:01:34 +0300 Subject: Cleanup curses better. --- lib/draw/curses.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index c1bb013..0e19538 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,10 +1,12 @@ #include "../internal.h" +#define _XOPEN_SOURCE 700 #include #include #include #include #include #include +#include #include /* ncurses.h likes to define stuff for us. @@ -24,6 +26,7 @@ * Dynamically loaded curses API. */ static struct curses { + struct sigaction action; void *handle; WINDOW *stdscr; WINDOW* (*initscr)(void); @@ -87,7 +90,7 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) static void _bmDrawCursesRender(const bmMenu *menu) { if (!curses.stdscr) { - freopen("/dev/tty", "rw", stdin); + freopen("/dev/tty", "r", stdin); setlocale(LC_CTYPE, ""); if ((curses.stdscr = curses.initscr()) == NULL) return; @@ -134,6 +137,7 @@ static void _bmDrawCursesEndWin(void) curses.endwin(); curses.stdscr = NULL; + freopen("/dev/tty", "w", stdout); } static bmKey _bmDrawCursesGetKey(unsigned int *unicode) @@ -218,9 +222,17 @@ static void _bmDrawCursesFree(void) if (curses.handle) dlclose(curses.handle); + sigaction(SIGABRT, &curses.action, NULL); + sigaction(SIGSEGV, &curses.action, NULL); memset(&curses, 0, sizeof(curses)); } +static void _bmDrawCursesCrashHandler(int sig) +{ + (void)sig; + _bmDrawCursesFree(); +} + int _bmDrawCursesInit(struct _bmRenderApi *api) { memset(&curses, 0, sizeof(curses)); @@ -270,6 +282,11 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) api->render = _bmDrawCursesRender; api->free = _bmDrawCursesFree; + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + action.sa_handler = _bmDrawCursesCrashHandler; + sigaction(SIGABRT, &action, &curses.action); + sigaction(SIGSEGV, &action, &curses.action); return 1; function_pointer_exception: -- cgit v1.2.3-70-g09d2 From a6d0413b972580f3bbfde8750090270b0d8d463e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:04:06 +0300 Subject: Implement list structure, and feature for multiple selections. --- client/client.c | 8 +- lib/CMakeLists.txt | 1 + lib/bemenu.h | 56 ++++++++++++-- lib/draw/curses.c | 8 +- lib/filter.c | 28 +++---- lib/internal.h | 64 ++++++++++----- lib/list.c | 135 ++++++++++++++++++++++++++++++++ lib/menu.c | 223 ++++++++++++++++++++++++++++++++--------------------- 8 files changed, 386 insertions(+), 137 deletions(-) create mode 100644 lib/list.c (limited to 'lib') diff --git a/client/client.c b/client/client.c index 4bdfdc6..ecaed25 100644 --- a/client/client.c +++ b/client/client.c @@ -101,10 +101,10 @@ int main(int argc, char **argv) } while ((status = bmMenuRunWithKey(menu, key, unicode)) == BM_RUN_RESULT_RUNNING); if (status == BM_RUN_RESULT_SELECTED) { - bmItem *item = bmMenuGetSelectedItem(menu); - - if (item) - printf("%s\n", bmItemGetText(item)); + unsigned int i, count; + bmItem **items = bmMenuGetSelectedItems(menu, &count); + for (i = 0; i < count; ++i) + printf("%s\n", bmItemGetText(items[i])); } bmMenuFree(menu); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e669ef5..f53dfc6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -3,6 +3,7 @@ SET(BEMENU_SOURCE menu.c item.c filter.c + list.c util.c draw/curses.c ) diff --git a/lib/bemenu.h b/lib/bemenu.h index f2f0372..250ad53 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -85,6 +85,7 @@ typedef enum bmKey { BM_KEY_TAB, BM_KEY_ESCAPE, BM_KEY_RETURN, + BM_KEY_SHIFT_RETURN, BM_KEY_UNICODE, BM_KEY_LAST } bmKey; @@ -200,24 +201,52 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index); */ int bmMenuRemoveItem(bmMenu *menu, bmItem *item); + /** - * Get selected item from bmMenu instance. + * Get highlighted item from bmMenu instance. * - * @param menu bmMenu instance from where to get selected item. - * @return Selected bmItem instance, **NULL** if none selected. + * @param menu bmMenu instance from where to get highlighted item. + * @return Selected bmItem instance, **NULL** if none highlighted. */ -bmItem* bmMenuGetSelectedItem(const bmMenu *menu); +bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** - * Get items from bmMenu instance. + * Highlight item in menu by index. * - * @warning The pointer returned by this function may be invalid after removing or adding new items. + * @param menu bmMenu instance from where to highlight item. + * @return 1 on successful highlight, 0 on failure. + */ +int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index); + +/** + * Highlight item in menu. * - * @param menu bmMenu instance from where to get items. + * @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); + +/** + * Get selected items from bmMenu instance. + * + * @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** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); +bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb); + +/** + * Set selected items to bmMenu instance. + * + * @warning The list won't be copied. + * + * @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); /** * Get filtered (displayed) items from bmMenu instance. @@ -231,6 +260,17 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); */ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); +/** + * 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); + /** * Set items to bmMenu instance. * Will replace all the old items on bmMenu instance. diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 0e19538..336b21d 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -120,8 +120,9 @@ static void _bmDrawCursesRender(const bmMenu *menu) unsigned int itemsCount; bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount); for (i = (menu->index / (lines - 1)) * (lines - 1); i < itemsCount && cl < lines; ++i) { - int selected = (items[i] == bmMenuGetSelectedItem(menu)); - _bmDrawCursesDrawLine((selected ? 2 : 0), cl++, "%s%s", (selected ? ">> " : " "), items[i]->text); + 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); } curses.move(0, titleLen + menu->cursesCursor); @@ -200,6 +201,9 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case 9: /* Tab */ return BM_KEY_TAB; + case 18: /* C-r */ + return BM_KEY_SHIFT_RETURN; + case 10: /* Return */ _bmDrawCursesEndWin(); return BM_KEY_RETURN; diff --git a/lib/filter.c b/lib/filter.c index 33126c0..4677e73 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -8,33 +8,33 @@ * * @param menu bmMenu instance to filter. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outSelected unsigned int reference to new outSelected item index. + * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); - assert(outSelected); - *outNmemb = *outSelected = 0; + assert(outHighlighted); + *outNmemb = *outHighlighted = 0; /* FIXME: not real dmenu like filtering at all */ - bmItem **filtered = calloc(menu->itemsCount, sizeof(bmItem*)); + bmItem **filtered = calloc(menu->items.count, sizeof(bmItem*)); if (!filtered) return NULL; unsigned int i, f; - for (f = i = 0; i < menu->itemsCount; ++i) { - bmItem *item = menu->items[i]; + for (f = i = 0; i < menu->items.count; ++i) { + bmItem *item = menu->items.list[i]; if (item->text && strstr(item->text, menu->filter)) { - if (f == 0 || item == bmMenuGetSelectedItem(menu)) - *outSelected = f; + if (f == 0 || item == bmMenuGetHighlightedItem(menu)) + *outHighlighted = f; filtered[f++] = item; } } - return _bmShrinkItemList(&filtered, menu->itemsCount, (*outNmemb = f)); + return _bmShrinkItemList(&filtered, menu->items.count, (*outNmemb = f)); } /** @@ -42,15 +42,15 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outS * * @param menu bmMenu instance to filter. * @param outNmemb unsigned int reference to filtered items outNmemb. - * @param outSelected unsigned int reference to new outSelected item index. + * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); - assert(outSelected); - *outNmemb = *outSelected = 0; + assert(outHighlighted); + *outNmemb = *outHighlighted = 0; /* FIXME: stub */ diff --git a/lib/internal.h b/lib/internal.h index 66eabbc..d0a9848 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -38,6 +38,23 @@ struct _bmRenderApi { void (*free)(void); }; +struct _bmItemList { + /** + * Items in the list. + */ + struct _bmItem **list; + + /** + * Number of items. + */ + unsigned int count; + + /** + * Number of allocated items. + */ + unsigned int allocated; +}; + /** * Internal bmMenu struct that is not exposed to public. */ @@ -48,14 +65,19 @@ struct _bmMenu { struct _bmRenderApi renderApi; /** - * All items contained in menu instance. + * Items contained in menu instance. */ - struct _bmItem **items; + struct _bmItemList items; /** * Filtered/displayed items contained in menu instance. */ - struct _bmItem **filteredItems; + struct _bmItemList filtered; + + /** + * Selected items. + */ + struct _bmItemList selection; /** * Menu instance title. @@ -79,22 +101,7 @@ struct _bmMenu { unsigned int cursesCursor; /** - * Number of items in menu instance. - */ - unsigned int itemsCount; - - /** - * Number of filtered items in menu instance. - */ - unsigned int filteredCount; - - /** - * Number of allocated items in menu instance. - */ - unsigned int allocatedCount; - - /** - * Current filtered item index in menu instance. + * Current filtered/highlighted item index in menu instance. * This index is valid for the list returned by bmMenuGetFilteredItems. */ unsigned int index; @@ -113,9 +120,24 @@ struct _bmMenu { /* draw/curses.c */ int _bmDrawCursesInit(struct _bmRenderApi *api); +/* menu.c */ +int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item); + /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected); +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted); + +/* 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); /* util.c */ char* _bmStrdup(const char *s); diff --git a/lib/list.c b/lib/list.c new file mode 100644 index 0000000..2123818 --- /dev/null +++ b/lib/list.c @@ -0,0 +1,135 @@ +#include "internal.h" +#include +#include +#include + +void _bmItemListFreeList(struct _bmItemList *list) +{ + assert(list); + + if (list->list) + free(list->list); + + list->allocated = list->count = 0; + list->list = NULL; +} + +void _bmItemListFreeItems(struct _bmItemList *list) +{ + assert(list); + + unsigned int i; + for (i = 0; i < list->count; ++i) + bmItemFree(list->list[i]); + + _bmItemListFreeList(list); +} + +bmItem** _bmItemListGetItems(const struct _bmItemList *list, unsigned int *outNmemb) +{ + assert(list); + + if (outNmemb) + *outNmemb = list->count; + + return list->list; +} + +int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned int nmemb) +{ + assert(list); + + _bmItemListFreeList(list); + + if (!items || nmemb == 0) { + items = NULL; + nmemb = 0; + } + + list->list = items; + list->allocated = list->count = nmemb; + return 1; +} + +int _bmItemListSetItems(struct _bmItemList *list, const bmItem **items, unsigned int nmemb) +{ + assert(list); + + if (!items || nmemb == 0) { + _bmItemListFreeItems(list); + return 1; + } + + bmItem **newItems; + if (!(newItems = calloc(sizeof(bmItem*), nmemb))) + return 0; + + memcpy(newItems, items, sizeof(bmItem*) * nmemb); + return _bmItemListSetItemsNoCopy(list, newItems, nmemb); +} + +int _bmItemListGrow(struct _bmItemList *list, unsigned int step) +{ + assert(list); + + void *tmp; + unsigned int nsize = sizeof(bmItem*) * (list->allocated + step); + + if (!list->list || !(tmp = realloc(list->list, nsize))) { + if (!(tmp = malloc(nsize))) + return 0; + + if (list->list) + memcpy(tmp, list->list, sizeof(bmItem*) * list->allocated); + } + + list->list = tmp; + list->allocated += step; + memset(&list->list[list->count], 0, sizeof(bmItem*) * (list->allocated - list->count)); + return 1; +} + +int _bmItemListAddItemAt(struct _bmItemList *list, bmItem *item, unsigned int index) +{ + assert(list); + assert(item); + + if ((!list->list || list->allocated <= list->count) && !_bmItemListGrow(list, 32)) + return 0; + + if (index + 1 != list->count) { + unsigned int i = index; + memmove(&list->list[i + 1], &list->list[i], sizeof(bmItem*) * (list->count - i)); + } + + list->list[index] = item; + list->count++; + return 1; +} + +int _bmItemListAddItem(struct _bmItemList *list, bmItem *item) +{ + assert(list); + return _bmItemListAddItemAt(list, item, list->count); +} + +int _bmItemListRemoveItemAt(struct _bmItemList *list, unsigned int index) +{ + assert(list); + + unsigned int i = index; + if (!list->list || list->count <= i) + return 0; + + memmove(&list->list[i], &list->list[i], sizeof(bmItem*) * (list->count - i)); + return 1; +} + +int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item) +{ + unsigned int i; + for (i = 0; i < list->count && list->list[i] != item; ++i); + return _bmItemListRemoveItemAt(list, i); +} + +/* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/menu.c b/lib/menu.c index 3d41121..6274fe3 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,7 +7,7 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outSelected) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; @@ -16,37 +16,22 @@ static void _bmMenuFilter(bmMenu *menu) { assert(menu); - if (menu->filteredItems) - free(menu->filteredItems); - - menu->filteredCount = 0; - menu->filteredItems = NULL; - unsigned int count, selected; bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); - menu->filteredItems = filtered; - menu->filteredCount = count; + _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); menu->index = selected; } -static int _bmMenuGrowItems(bmMenu *menu) +int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item) { - void *tmp; - static const unsigned int step = 32; - unsigned int nsize = sizeof(bmItem*) * (menu->allocatedCount + step); - - if (!(tmp = realloc(menu->items, nsize))) { - if (!(tmp = malloc(nsize))) - return 0; - - memcpy(tmp, menu->items, sizeof(bmItem*) * menu->allocatedCount); - } + assert(menu); + assert(item); - menu->items = tmp; - menu->allocatedCount += step; - memset(&menu->items[menu->itemsCount], 0, sizeof(bmItem*) * (menu->allocatedCount - menu->itemsCount)); - return 1; + unsigned int i, count; + bmItem **items = bmMenuGetSelectedItems(menu, &count); + for (i = 0; i < count && items[i] != item; ++i); + return (i < count); } /** @@ -97,8 +82,8 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); - if (menu->filteredItems) - free(menu->filteredItems); + if (menu->filtered.list) + free(menu->filtered.list); bmMenuFreeItems(menu); free(menu); @@ -112,14 +97,9 @@ void bmMenuFree(bmMenu *menu) void bmMenuFreeItems(bmMenu *menu) { assert(menu); - - unsigned int i; - for (i = 0; i < menu->itemsCount; ++i) - bmItemFree(menu->items[i]); - - free(menu->items); - menu->allocatedCount = menu->itemsCount = 0; - menu->items = NULL; + _bmItemListFreeList(&menu->selection); + _bmItemListFreeList(&menu->filtered); + _bmItemListFreeItems(&menu->items); } /** @@ -195,19 +175,7 @@ const char* bmMenuGetTitle(const bmMenu *menu) int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { assert(menu); - assert(item); - - if (menu->itemsCount >= menu->allocatedCount && !_bmMenuGrowItems(menu)) - return 0; - - if (index + 1 != menu->itemsCount) { - unsigned int i = index; - memmove(&menu->items[i + 1], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i)); - } - - menu->items[index] = item; - menu->itemsCount++; - return 1; + return _bmItemListAddItemAt(&menu->items, item, index);; } /** @@ -219,7 +187,7 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) */ int bmMenuAddItem(bmMenu *menu, bmItem *item) { - return bmMenuAddItemAt(menu, item, menu->itemsCount); + return _bmItemListAddItem(&menu->items, item); } /** @@ -235,12 +203,18 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) { assert(menu); - unsigned int i = index; - if (i >= menu->itemsCount) + if (!menu->items.list || menu->items.count <= index) return 0; - memmove(&menu->items[i], &menu->items[i], sizeof(bmItem*) * (menu->itemsCount - i)); - return 1; + bmItem *item = menu->items.list[index]; + int ret = _bmItemListRemoveItemAt(&menu->items, index); + + if (ret) { + _bmItemListRemoveItem(&menu->selection, item); + _bmItemListRemoveItem(&menu->filtered, item); + } + + return ret; } /** @@ -255,20 +229,24 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) int bmMenuRemoveItem(bmMenu *menu, bmItem *item) { assert(menu); - assert(item); - unsigned int i; - for (i = 0; i < menu->itemsCount && menu->items[i] != item; ++i); - return bmMenuRemoveItemAt(menu, i); + int ret = _bmItemListRemoveItem(&menu->items, item); + + if (ret) { + _bmItemListRemoveItem(&menu->selection, item); + _bmItemListRemoveItem(&menu->filtered, item); + } + + return ret; } /** - * Get selected item from bmMenu instance. + * Get highlighted item from bmMenu instance. * - * @param menu bmMenu instance from where to get selected item. - * @return Selected bmItem instance, **NULL** if none selected. + * @param menu bmMenu instance from where to get highlighted item. + * @return Selected bmItem instance, **NULL** if none highlighted. */ -bmItem* bmMenuGetSelectedItem(const bmMenu *menu) +bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) { assert(menu); @@ -282,22 +260,70 @@ bmItem* bmMenuGetSelectedItem(const bmMenu *menu) } /** - * Get items from bmMenu instance. + * Highlight item in menu by index. * - * @warning The pointer returned by this function may be invalid after removing or adding new items. + * @param menu bmMenu instance from where to highlight item. + * @return 1 on successful highlight, 0 on failure. + */ +int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index) +{ + assert(menu); + unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); + + if (itemsCount <= index) + return 0; + + return (menu->index = index); +} + +/** + * Highlight item in menu. * - * @param menu bmMenu instance from where to get items. + * @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) +{ + assert(menu); + + unsigned int i, itemsCount; + bmItem **items = bmMenuGetFilteredItems(menu, &itemsCount); + for (i = 0; i < itemsCount && items[i] != item; ++i); + + if (itemsCount <= i) + return 0; + + return (menu->index = i); +} + +/** + * Get selected items from bmMenu instance. + * + * @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** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb) +bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); + return _bmItemListGetItems(&menu->selection, outNmemb); +} - if (outNmemb) - *outNmemb = menu->itemsCount; - - return menu->items; +/** + * Set selected items to bmMenu instance. + * + * @warning The list won't be copied. + * + * @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) +{ + assert(menu); + return _bmItemListSetItemsNoCopy(&menu->selection, items, nmemb); } /** @@ -314,10 +340,25 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (outNmemb) - *outNmemb = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + if (menu->filtered.list) + return _bmItemListGetItems(&menu->filtered, outNmemb); - return (menu->filteredItems ? menu->filteredItems : menu->items); + return _bmItemListGetItems(&menu->items, outNmemb); +} + +/** + * 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) +{ + assert(menu); + return _bmItemListGetItems(&menu->items, outNmemb); } /** @@ -335,21 +376,14 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) { assert(menu); - if (!items || nmemb == 0) { - bmMenuFreeItems(menu); - return 1; - } + int ret = _bmItemListSetItems(&menu->items, items, nmemb); - bmItem **newItems; - if (!(newItems = calloc(sizeof(bmItem*), nmemb))) - return 0; - - memcpy(newItems, items, sizeof(bmItem*) * nmemb); - bmMenuFreeItems(menu); + if (ret) { + _bmItemListFreeList(&menu->selection); + _bmItemListFreeList(&menu->filtered); + } - menu->items = newItems; - menu->allocatedCount = menu->itemsCount = nmemb; - return 1; + return ret; } /** @@ -400,7 +434,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) { assert(menu); char *oldFilter = _bmStrdup(menu->filter); - unsigned int itemsCount = (menu->filteredItems ? menu->filteredCount : menu->itemsCount); + unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); switch (key) { case BM_KEY_LEFT: @@ -502,9 +536,9 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_TAB: { - bmItem *selected = bmMenuGetSelectedItem(menu); - if (selected && bmItemGetText(selected)) { - const char *text = bmItemGetText(selected); + bmItem *highlighted = bmMenuGetHighlightedItem(menu); + if (highlighted && bmItemGetText(highlighted)) { + const char *text = bmItemGetText(highlighted); size_t len = strlen(text); if (len > sizeof(menu->filter) - 1) @@ -518,6 +552,19 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; + case BM_KEY_SHIFT_RETURN: + case BM_KEY_RETURN: + { + bmItem *highlighted = bmMenuGetHighlightedItem(menu); + if (highlighted && !_bmMenuItemIsSelected(menu, highlighted)) + _bmItemListAddItem(&menu->selection, highlighted); + } + break; + + case BM_KEY_ESCAPE: + _bmItemListFreeList(&menu->selection); + break; + default: break; } -- cgit v1.2.3-70-g09d2 From 702d808b285931850e66900bd6c7c7319ae6d73c Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:09:34 +0300 Subject: Make it possible to set and get userdata pointers. --- lib/bemenu.h | 34 ++++++++++++++++++++++++++++++++++ lib/internal.h | 12 ++++++++++++ lib/item.c | 25 +++++++++++++++++++++++++ lib/menu.c | 25 +++++++++++++++++++++++++ 4 files changed, 96 insertions(+) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index 250ad53..65625b3 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -122,6 +122,23 @@ void bmMenuFreeItems(bmMenu *menu); * @name Menu Properties * @{ */ +/** + * 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); + +/** + * Get userdata pointer from bmMenu instance. + * + * @param menu bmMenu instance which userdata pointer to get. + * @return Pointer for unmodified userdata. + */ +void* bmMenuGetUserdata(bmMenu *menu); + /** * Set active filter mode to bmMenu instance. * @@ -351,6 +368,23 @@ void bmItemFree(bmItem *item); * @name Item Properties * @{ */ +/** + * 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); + +/** + * Get userdata pointer from bmItem instance. + * + * @param item bmItem instance which userdata pointer to get. + * @return Pointer for unmodified userdata. + */ +void* bmItemGetUserdata(bmItem *item); + /** * Set text to bmItem instance. * diff --git a/lib/internal.h b/lib/internal.h index d0a9848..790b4ed 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -9,6 +9,12 @@ * Represents a single item in menu. */ struct _bmItem { + /** + * 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. @@ -59,6 +65,12 @@ struct _bmItemList { * Internal bmMenu struct that is not exposed to public. */ struct _bmMenu { + /** + * Userdata pointer. + * This pointer will be passed around with the menu untouched. + */ + void *userdata; + /** * Underlying renderer access. */ diff --git a/lib/item.c b/lib/item.c index e6ccc7a..f2a879a 100644 --- a/lib/item.c +++ b/lib/item.c @@ -35,6 +35,31 @@ void bmItemFree(bmItem *item) 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) +{ + 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) +{ + assert(item); + return item->userdata; +} + /** * Set text to bmItem instance. * diff --git a/lib/menu.c b/lib/menu.c index 6274fe3..a011194 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -102,6 +102,31 @@ void bmMenuFreeItems(bmMenu *menu) _bmItemListFreeItems(&menu->items); } +/** + * 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) +{ + 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) +{ + assert(menu); + return menu->userdata; +} + /** * Set active filter mode to bmMenu instance. * -- cgit v1.2.3-70-g09d2 From 9525c77f553c2e188f6f99d3b6c01d5ec671a76e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:11:41 +0300 Subject: Setter first, then getter. --- lib/bemenu.h | 55 +++++++++++++++++----------------- lib/menu.c | 98 ++++++++++++++++++++++++++++++------------------------------ 2 files changed, 76 insertions(+), 77 deletions(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index 65625b3..7d96d2c 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -218,15 +218,6 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index); */ int bmMenuRemoveItem(bmMenu *menu, bmItem *item); - -/** - * Get highlighted item from bmMenu instance. - * - * @param menu bmMenu instance from where to get highlighted item. - * @return Selected bmItem instance, **NULL** if none highlighted. - */ -bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); - /** * Highlight item in menu by index. * @@ -245,13 +236,12 @@ int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index); int bmMenuSetHighlighted(bmMenu *menu, bmItem *item); /** - * Get selected items from bmMenu instance. + * Get highlighted item from bmMenu instance. * - * @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 bmMenu instance from where to get highlighted item. + * @return Selected bmItem instance, **NULL** if none highlighted. */ -bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb); +bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** * Set selected items to bmMenu instance. @@ -266,16 +256,26 @@ bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb); int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int 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. + * Get selected items from bmMenu instance. * - * @param menu bmMenu instance from where to get filtered items. + * @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** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); +bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb); + +/** + * 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); /** * Get items from bmMenu instance. @@ -289,17 +289,16 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb); /** - * Set items to bmMenu instance. - * Will replace all the old items on bmMenu instance. + * Get filtered (displayed) items from bmMenu instance. * - * If items is **NULL**, or nmemb is zero, all items will be freed from the menu. + * @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 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. + * @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. */ -int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb); +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); /** @} Menu Items */ diff --git a/lib/menu.c b/lib/menu.c index a011194..f0a8149 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -265,25 +265,6 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) return ret; } -/** - * Get highlighted item from bmMenu instance. - * - * @param menu bmMenu instance from where to get highlighted item. - * @return Selected bmItem instance, **NULL** if none highlighted. - */ -bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) -{ - assert(menu); - - unsigned int count; - bmItem **items = bmMenuGetFilteredItems(menu, &count); - - if (!items || count <= menu->index) - return NULL; - - return items[menu->index]; -} - /** * Highlight item in menu by index. * @@ -323,16 +304,22 @@ int bmMenuSetHighlighted(bmMenu *menu, bmItem *item) } /** - * Get selected items from bmMenu instance. + * Get highlighted item from bmMenu instance. * - * @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 bmMenu instance from where to get highlighted item. + * @return Selected bmItem instance, **NULL** if none highlighted. */ -bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb) +bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) { assert(menu); - return _bmItemListGetItems(&menu->selection, outNmemb); + + unsigned int count; + bmItem **items = bmMenuGetFilteredItems(menu, &count); + + if (!items || count <= menu->index) + return NULL; + + return items[menu->index]; } /** @@ -352,23 +339,41 @@ int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int 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. + * Get selected items from bmMenu instance. * - * @param menu bmMenu instance from where to get filtered items. + * @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** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) +bmItem** bmMenuGetSelectedItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); + return _bmItemListGetItems(&menu->selection, outNmemb); +} - if (menu->filtered.list) - return _bmItemListGetItems(&menu->filtered, outNmemb); +/** + * 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) +{ + assert(menu); - return _bmItemListGetItems(&menu->items, outNmemb); + int ret = _bmItemListSetItems(&menu->items, items, nmemb); + + if (ret) { + _bmItemListFreeList(&menu->selection); + _bmItemListFreeList(&menu->filtered); + } + + return ret; } /** @@ -387,28 +392,23 @@ bmItem** bmMenuGetItems(const bmMenu *menu, unsigned int *outNmemb) } /** - * Set items to bmMenu instance. - * Will replace all the old items on bmMenu instance. + * Get filtered (displayed) items from bmMenu instance. * - * If items is **NULL**, or nmemb is zero, all items will be freed from the menu. + * @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 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. + * @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. */ -int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) +bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - int ret = _bmItemListSetItems(&menu->items, items, nmemb); - - if (ret) { - _bmItemListFreeList(&menu->selection); - _bmItemListFreeList(&menu->filtered); - } + if (menu->filtered.list) + return _bmItemListGetItems(&menu->filtered, outNmemb); - return ret; + return _bmItemListGetItems(&menu->items, outNmemb); } /** -- cgit v1.2.3-70-g09d2 From 49fc7cd08c646d44e63749dc4e0c38dcb2d00a50 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:12:42 +0300 Subject: Include index parameter in documentation. --- lib/bemenu.h | 1 + lib/menu.c | 1 + 2 files changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index 7d96d2c..35a94e5 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -222,6 +222,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item); * 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); diff --git a/lib/menu.c b/lib/menu.c index f0a8149..5f57ae8 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -269,6 +269,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) * 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) -- cgit v1.2.3-70-g09d2 From 05212f5e44571945b40f72ae70ec7012af85fe75 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 20:19:15 +0300 Subject: Improve documentation. --- lib/bemenu.h | 6 +++++- lib/menu.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index 35a94e5..4534966 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -239,6 +239,8 @@ int bmMenuSetHighlighted(bmMenu *menu, bmItem *item); /** * 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. */ @@ -247,7 +249,7 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied. + * @warning The list won't be copied, do not free it. * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. @@ -259,6 +261,8 @@ int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int 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. diff --git a/lib/menu.c b/lib/menu.c index 5f57ae8..bf26c51 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -307,6 +307,8 @@ int bmMenuSetHighlighted(bmMenu *menu, bmItem *item) /** * 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. */ @@ -326,7 +328,7 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied. + * @warning The list won't be copied, do not free it. * * @param menu bmMenu instance where items will be set. * @param items Array of bmItem pointers to set. @@ -342,6 +344,8 @@ int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int 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. -- cgit v1.2.3-70-g09d2 From 45502a2fd994fdb10b77e4ac79c5f9fd8a8399e8 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 22:02:47 +0300 Subject: Proper filtering functions. --- lib/filter.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++--------- lib/internal.h | 2 + lib/menu.c | 37 ++++++++++++---- lib/util.c | 60 ++++++++++++++++++-------- 4 files changed, 188 insertions(+), 44 deletions(-) (limited to 'lib') diff --git a/lib/filter.c b/lib/filter.c index 4677e73..7e87b11 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -4,37 +4,139 @@ #include /** - * Filter that mimics the vanilla dmenu filtering. + * Shrink bmItem** list pointer. + * + * Useful helper function for filter functions. + * + * @param list Pointer to pointer to list of bmItem 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. + */ +static bmItem** _bmFilterShrinkList(bmItem ***inOutList, size_t osize, size_t nsize) +{ + assert(inOutList); + + if (nsize == 0) { + free(*inOutList); + return (*inOutList = NULL); + } + + if (nsize >= osize) + return *inOutList; + + void *tmp = malloc(sizeof(bmItem*) * nsize); + if (!tmp) + return *inOutList; + + memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize); + free(*inOutList); + return (*inOutList = 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. + * @return Pointer to buffer that contains tokenized string, this should be freed after use. + */ +static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outTokc) +{ + assert(menu); + assert(outTokv); + assert(outTokc); + *outTokv = NULL; + *outTokc = 0; + + char **tokv = NULL, *buffer = NULL; + if (!(buffer = _bmStrdup(menu->filter))) + goto fail; + + char *s, **tmp = NULL; + unsigned int tokc = 0, tokn = 0; + for (s = strtok(buffer, " "); s; tmp[tokc - 1] = s, s = strtok(NULL, " "), tokv = tmp) + if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) + goto fail; + + *outTokv = tmp; + *outTokc = tokc; + return buffer; + +fail: + if (buffer) + free(buffer); + if (tokv) + free(tokv); + return NULL; +} + +/** + * Dmenu filterer that accepts substring function. * * @param menu bmMenu instance to filter. + * @param fstrstr Substring function used to match items. * @param outNmemb unsigned int reference to filtered items outNmemb. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); assert(outHighlighted); *outNmemb = *outHighlighted = 0; - /* FIXME: not real dmenu like filtering at all */ + unsigned int itemsCount; + bmItem **items = bmMenuGetItems(menu, &itemsCount); - bmItem **filtered = calloc(menu->items.count, sizeof(bmItem*)); + bmItem **filtered = calloc(itemsCount, sizeof(bmItem*)); if (!filtered) return NULL; + char **tokv; + unsigned int tokc; + char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); + unsigned int i, f; - for (f = i = 0; i < menu->items.count; ++i) { - bmItem *item = menu->items.list[i]; - if (item->text && strstr(item->text, menu->filter)) { - if (f == 0 || item == bmMenuGetHighlightedItem(menu)) - *outHighlighted = f; - filtered[f++] = item; + for (f = i = 0; i < itemsCount; ++i) { + bmItem *item = items[i]; + if (!item->text && tokc != 0) + continue; + + if (tokc && item->text) { + unsigned int t; + for (t = 0; t < tokc && fstrstr(item->text, tokv[t]); ++t); + if (t < tokc) + continue; } + + if (f == 0 || item == bmMenuGetHighlightedItem(menu)) + *outHighlighted = f; + filtered[f++] = item; } - return _bmShrinkItemList(&filtered, menu->items.count, (*outNmemb = f)); + if (buffer) + free(buffer); + + if (tokv) + free(tokv); + + return _bmFilterShrinkList(&filtered, menu->items.count, (*outNmemb = f)); +} + +/** + * Filter that mimics the vanilla dmenu filtering. + * + * @param menu bmMenu instance to filter. + * @param outNmemb unsigned int reference to filtered items outNmemb. + * @param outHighlighted unsigned int reference to new outHighlighted item index. + * @return Pointer to array of bmItem pointers. + */ +bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +{ + return _bmFilterDmenuFun(menu, strstr, outNmemb, outHighlighted); } /** @@ -47,14 +149,7 @@ bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outH */ bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) { - assert(menu); - assert(outNmemb); - assert(outHighlighted); - *outNmemb = *outHighlighted = 0; - - /* FIXME: stub */ - - return NULL; + return _bmFilterDmenuFun(menu, _bmStrupstr, outNmemb, outHighlighted); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index 790b4ed..043c906 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -153,6 +153,8 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); +int _bmStrupcmp(const char *hay, const char *needle); +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); diff --git a/lib/menu.c b/lib/menu.c index bf26c51..e9cf647 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -16,6 +16,11 @@ static void _bmMenuFilter(bmMenu *menu) { assert(menu); + if (!menu->items.list || menu->items.count <= 0) { + _bmItemListFreeList(&menu->filtered); + return; + } + unsigned int count, selected; bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); @@ -82,9 +87,6 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); - if (menu->filtered.list) - free(menu->filtered.list); - bmMenuFreeItems(menu); free(menu); } @@ -200,7 +202,13 @@ const char* bmMenuGetTitle(const bmMenu *menu) int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { assert(menu); - return _bmItemListAddItemAt(&menu->items, item, index);; + + int ret = _bmItemListAddItemAt(&menu->items, item, index); + + if (ret) + _bmMenuFilter(menu); + + return ret; } /** @@ -212,7 +220,12 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) */ int bmMenuAddItem(bmMenu *menu, bmItem *item) { - return _bmItemListAddItem(&menu->items, item); + int ret = _bmItemListAddItem(&menu->items, item); + + if (ret) + _bmMenuFilter(menu); + + return ret; } /** @@ -237,6 +250,7 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); + _bmMenuFilter(menu); } return ret; @@ -260,6 +274,7 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); + _bmMenuFilter(menu); } return ret; @@ -275,7 +290,9 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) int bmMenuSetHighlightedIndex(bmMenu *menu, unsigned int index) { assert(menu); - unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); + + unsigned int itemsCount; + bmMenuGetFilteredItems(menu, &itemsCount); if (itemsCount <= index) return 0; @@ -376,6 +393,7 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) if (ret) { _bmItemListFreeList(&menu->selection); _bmItemListFreeList(&menu->filtered); + _bmMenuFilter(menu); } return ret; @@ -410,7 +428,7 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (menu->filtered.list) + if (strlen(menu->filter)) return _bmItemListGetItems(&menu->filtered, outNmemb); return _bmItemListGetItems(&menu->items, outNmemb); @@ -463,8 +481,11 @@ bmKey bmMenuGetKey(bmMenu *menu, unsigned int *outUnicode) bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) { assert(menu); + + unsigned int itemsCount; + bmMenuGetFilteredItems(menu, &itemsCount); + char *oldFilter = _bmStrdup(menu->filter); - unsigned int itemsCount = (menu->filtered.list ? menu->filtered.count : menu->items.count); switch (key) { case BM_KEY_LEFT: diff --git a/lib/util.c b/lib/util.c index 5eb980d..06c89cf 100644 --- a/lib/util.c +++ b/lib/util.c @@ -28,30 +28,56 @@ char* _bmStrdup(const char *string) } /** - * Shrink bmItem** list pointer. + * Portable case-insensitive strcmp. * - * Useful helper function for filter functions. + * @param hay C "string" to match against. + * @param needle C "string" to match. + */ +int _bmStrupcmp(const char *hay, const char *needle) +{ + size_t i, len; + + if ((len = strlen(hay)) != strlen(needle)) + return 1; + + for (i = 0; i != len; ++i) + if (toupper(hay[i]) != toupper(needle[i])) + return 1; + + return 0; +} + +/** + * Portable case-insensitive strstr. * - * @param list Pointer to pointer to list of bmItem 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. + * @param hay C "string" to substring against. + * @param needle C "string" to substring. */ -bmItem** _bmShrinkItemList(bmItem ***inOutList, size_t osize, size_t nsize) +char* _bmStrupstr(const char *hay, const char *needle) { - assert(inOutList); + size_t i, r = 0, p = 0, len, len2; - if (nsize >= osize) - return *inOutList; + if (!_bmStrupcmp(hay, needle)) + return (char*)hay; - void *tmp = malloc(sizeof(bmItem*) * nsize); - if (!tmp) - return *inOutList; + if ((len = strlen(hay)) < (len2 = strlen(needle))) + return NULL; + + for (i = 0; i != len; ++i) { + if (p == len2) + return (char*)hay + r; + + if (toupper(hay[i]) == toupper(needle[p++])) { + if (!r) + r = i; + } else { + if (r) + i = r; + r = p = 0; + } + } - memcpy(tmp, *inOutList, sizeof(bmItem*) * nsize); - free(*inOutList); - *inOutList = tmp; - return *inOutList; + return (p == len2 ? (char*)hay + r : NULL); } /** -- cgit v1.2.3-70-g09d2 From 45e21171593050aedc92b1da44d0441e4a734a1b Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 22:15:01 +0300 Subject: Don't run filtering if there is no filter. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index e9cf647..acf9ffc 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -16,7 +16,7 @@ static void _bmMenuFilter(bmMenu *menu) { assert(menu); - if (!menu->items.list || menu->items.count <= 0) { + if (!strlen(menu->filter) || !menu->items.list || menu->items.count <= 0) { _bmItemListFreeList(&menu->filtered); return; } -- cgit v1.2.3-70-g09d2 From 71beb7583f6cba2eb8070d5a05dcdf05b54f52bf Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 23:05:13 +0300 Subject: Make it possible filter manually, and optimized filtering. --- lib/bemenu.h | 11 ++++++ lib/filter.c | 27 +++++++++++---- lib/internal.h | 9 +++-- lib/menu.c | 103 ++++++++++++++++++++++++++++++++------------------------- 4 files changed, 96 insertions(+), 54 deletions(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index 4534966..6348693 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -318,6 +318,17 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb); */ void bmMenuRender(const bmMenu *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); + /** * Poll key and unicode from underlying UI toolkit. * diff --git a/lib/filter.c b/lib/filter.c index 7e87b11..c8cbfff 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -76,12 +76,13 @@ fail: * Dmenu filterer that accepts substring function. * * @param menu bmMenu 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. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) { assert(menu); assert(outNmemb); @@ -89,7 +90,13 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c *outNmemb = *outHighlighted = 0; unsigned int itemsCount; - bmItem **items = bmMenuGetItems(menu, &itemsCount); + bmItem **items; + + if (addition) { + items = bmMenuGetFilteredItems(menu, &itemsCount); + } else { + items = bmMenuGetItems(menu, &itemsCount); + } bmItem **filtered = calloc(itemsCount, sizeof(bmItem*)); if (!filtered) @@ -99,6 +106,7 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c unsigned int tokc; char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); + char found = 0; unsigned int i, f; for (f = i = 0; i < itemsCount; ++i) { bmItem *item = items[i]; @@ -112,8 +120,11 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c continue; } - if (f == 0 || item == bmMenuGetHighlightedItem(menu)) + if (!found && item == bmMenuGetHighlightedItem(menu)) { *outHighlighted = f; + found = 1; + } + filtered[f++] = item; } @@ -130,26 +141,28 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c * Filter that mimics the vanilla dmenu filtering. * * @param menu bmMenu 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. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) { - return _bmFilterDmenuFun(menu, strstr, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, strstr, outNmemb, outHighlighted); } /** * Filter that mimics the vanilla case-insensitive dmenu filtering. * * @param menu bmMenu 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. * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) { - return _bmFilterDmenuFun(menu, _bmStrupstr, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, _bmStrupstr, outNmemb, outHighlighted); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index 043c906..985d366 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -102,6 +102,11 @@ struct _bmMenu { */ char filter[1024]; + /** + * Used as optimization. + */ + char *oldFilter; + /** * Current byte offset on filter text. */ @@ -136,8 +141,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api); int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item); /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); /* list.c */ void _bmItemListFreeList(struct _bmItemList *list); diff --git a/lib/menu.c b/lib/menu.c index acf9ffc..c5c98ad 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,27 +7,11 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; -static void _bmMenuFilter(bmMenu *menu) -{ - assert(menu); - - if (!strlen(menu->filter) || !menu->items.list || menu->items.count <= 0) { - _bmItemListFreeList(&menu->filtered); - return; - } - - unsigned int count, selected; - bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected); - - _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); - menu->index = selected; -} - int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item) { assert(menu); @@ -87,6 +71,9 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); + if (menu->oldFilter) + free(menu->oldFilter); + bmMenuFreeItems(menu); free(menu); } @@ -138,12 +125,7 @@ void* bmMenuGetUserdata(bmMenu *menu) void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode) { assert(menu); - - bmFilterMode oldMode = mode; menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode); - - if (oldMode != mode) - _bmMenuFilter(menu); } /** @@ -202,13 +184,7 @@ const char* bmMenuGetTitle(const bmMenu *menu) int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) { assert(menu); - - int ret = _bmItemListAddItemAt(&menu->items, item, index); - - if (ret) - _bmMenuFilter(menu); - - return ret; + return _bmItemListAddItemAt(&menu->items, item, index); } /** @@ -220,12 +196,7 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index) */ int bmMenuAddItem(bmMenu *menu, bmItem *item) { - int ret = _bmItemListAddItem(&menu->items, item); - - if (ret) - _bmMenuFilter(menu); - - return ret; + return _bmItemListAddItem(&menu->items, item); } /** @@ -250,7 +221,6 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); - _bmMenuFilter(menu); } return ret; @@ -274,7 +244,6 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item) if (ret) { _bmItemListRemoveItem(&menu->selection, item); _bmItemListRemoveItem(&menu->filtered, item); - _bmMenuFilter(menu); } return ret; @@ -393,7 +362,6 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb) if (ret) { _bmItemListFreeList(&menu->selection); _bmItemListFreeList(&menu->filtered); - _bmMenuFilter(menu); } return ret; @@ -447,6 +415,57 @@ void bmMenuRender(const bmMenu *menu) menu->renderApi.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) +{ + assert(menu); + + char addition = 0; + size_t len = strlen(menu->filter); + + if (!len || !menu->items.list || menu->items.count <= 0) { + _bmItemListFreeList(&menu->filtered); + + if (menu->oldFilter) + free(menu->oldFilter); + + menu->oldFilter = NULL; + return; + } + + if (menu->oldFilter) { + size_t oldLen = strlen(menu->oldFilter); + addition = (oldLen < len && !memcmp(menu->oldFilter, menu->filter, oldLen)); + } + + if (menu->oldFilter && addition && menu->filtered.count <= 0) + return; + + if (menu->oldFilter && !strcmp(menu->filter, menu->oldFilter)) + return; + + unsigned int count, selected; + bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count, &selected); + + _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); + menu->index = selected; + + if (count) { + if (menu->oldFilter) + free(menu->oldFilter); + + menu->oldFilter = _bmStrdup(menu->filter); + } +} + /** * Poll key and unicode from underlying UI toolkit. * @@ -485,8 +504,6 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) unsigned int itemsCount; bmMenuGetFilteredItems(menu, &itemsCount); - char *oldFilter = _bmStrdup(menu->filter); - switch (key) { case BM_KEY_LEFT: { @@ -619,11 +636,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) default: break; } - if (oldFilter && strcmp(oldFilter, menu->filter)) - _bmMenuFilter(menu); - - if (oldFilter) - free(oldFilter); + bmMenuFilter(menu); switch (key) { case BM_KEY_RETURN: return BM_RUN_RESULT_SELECTED; -- cgit v1.2.3-70-g09d2 From 8e0dd1ab39d3d041b4093e2c43fdf6715da1add3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 23:05:37 +0300 Subject: Make it possible to set filter text manually. --- lib/bemenu.h | 16 ++++++++++++++++ lib/menu.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index 6348693..d348ffa 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -400,6 +400,22 @@ void bmItemSetUserdata(bmItem *item, void *userdata); */ void* bmItemGetUserdata(bmItem *item); +/** + * 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); + +/** + * 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); + /** * Set text to bmItem instance. * diff --git a/lib/menu.c b/lib/menu.c index c5c98ad..cb0d3ee 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -116,6 +116,36 @@ void* bmMenuGetUserdata(bmMenu *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) +{ + assert(menu); + + if (!filter) { + memset(menu->filter, 0, sizeof(menu->filter)); + return; + } + + strncpy(menu->filter, filter, sizeof(menu->filter)); +} + +/** + * 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) +{ + assert(menu); + return menu->filter; +} + /** * Set active filter mode to bmMenu instance. * -- cgit v1.2.3-70-g09d2 From 590c5d9f857d594af4146fb6e019228b874b368a Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 10 Apr 2014 23:50:55 +0300 Subject: Undef get_wch --- lib/draw/curses.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 336b21d..a58dd3b 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -12,6 +12,7 @@ /* ncurses.h likes to define stuff for us. * This unforunately mangles with our struct. */ #undef erase +#undef get_wch #undef refresh #undef mvprintw #undef move -- cgit v1.2.3-70-g09d2 From dfdb810099017ae3468ee8db9d3a3da2e21be250 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 00:40:21 +0300 Subject: This is actually better without the count check. --- lib/menu.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index cb0d3ee..edbcf73 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -488,12 +488,10 @@ void bmMenuFilter(bmMenu *menu) _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); menu->index = selected; - if (count) { - if (menu->oldFilter) - free(menu->oldFilter); + if (menu->oldFilter) + free(menu->oldFilter); - menu->oldFilter = _bmStrdup(menu->filter); - } + menu->oldFilter = _bmStrdup(menu->filter); } /** -- cgit v1.2.3-70-g09d2 From 40bd036c5390118a8f1631e1d990cce29edb40f4 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 01:59:05 +0300 Subject: Free the old list. --- lib/list.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/list.c b/lib/list.c index 2123818..fb0bdb2 100644 --- a/lib/list.c +++ b/lib/list.c @@ -79,8 +79,10 @@ int _bmItemListGrow(struct _bmItemList *list, unsigned int step) if (!(tmp = malloc(nsize))) return 0; - if (list->list) + if (list->list) { memcpy(tmp, list->list, sizeof(bmItem*) * list->allocated); + free(list->list); + } } list->list = tmp; -- cgit v1.2.3-70-g09d2 From cc93ff905ff9cf8bcb71a088a16529167511b8ad Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 01:59:50 +0300 Subject: Draw lines properly. --- lib/draw/curses.c | 75 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index a58dd3b..22abfd9 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -47,37 +47,73 @@ static struct curses { int *ESCDELAY; } curses; -static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) +static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) { - static int ncols = 0; - static char *buffer = NULL; - int newNcols = curses.getmaxx(curses.stdscr); - - if (newNcols <= 0) - return; + assert(buffer); + assert(nsize); - if (!buffer || newNcols > ncols) { - if (buffer) - free(buffer); + if (nsize == 0 || nsize < osize) + return 0; - ncols = newNcols; + void *tmp; + if (!*buffer || !(tmp = realloc(*buffer, nsize))) { + if (!(tmp = malloc(nsize))) + return 0; - if (!(buffer = calloc(1, ncols + 1))) - return; + if (*buffer) { + memcpy(tmp, *buffer, osize); + free(*buffer); + } } + *buffer = tmp; + memset(*buffer + osize, ' ', (nsize - osize)); + (*buffer)[nsize - 1] = 0; + return 1; +} + +static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) +{ + static int blen = 0; + static char *buffer = NULL; + + int ncols = curses.getmaxx(curses.stdscr); + if (ncols <= 0) + return; + va_list args; va_start(args, format); - int tlen = vsnprintf(NULL, 0, format, args) + 1; - if (tlen > ncols) - tlen = ncols; + int nlen = vsnprintf(NULL, 0, format, args) + 1; + if (nlen < ncols) + nlen = ncols; va_end(args); + if ((!buffer || nlen > blen) && !_bmDrawCursesResizeBuffer(&buffer, blen, nlen)) + return; + + blen = nlen; va_start(args, format); - vsnprintf(buffer, tlen, format, args); + int slen = vsnprintf(buffer, blen - 1, format, args); + memset(buffer + slen, ' ', (blen - slen)); + buffer[blen - 1] = 0; va_end(args); - memset(buffer + tlen - 1, ' ', ncols - tlen + 1); + int dw = 0, i = 0; + while (dw < ncols && i < blen) { + if (buffer[i] == '\t') buffer[i] = ' '; + int next = _bmUtf8RuneNext(buffer, i); + dw += _bmUtf8RuneWidth(buffer + i, next); + i += (next ? next : 1); + } + + if (dw < ncols) { + if (!_bmDrawCursesResizeBuffer(&buffer, blen - 1, blen + (ncols - dw) + 1)) + return; + } else if (i < blen) { + int cc = dw - (dw - ncols); + memset(buffer + i - (dw - ncols), ' ', (ncols - cc) + 1); + buffer[i - (dw - ncols) + (ncols - cc) + 1] = 0; + } if (pair > 0) curses.attron(COLOR_PAIR(pair)); @@ -126,7 +162,8 @@ static void _bmDrawCursesRender(const bmMenu *menu) _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), items[i]->text); } - curses.move(0, titleLen + menu->cursesCursor); + unsigned int ncols = curses.getmaxx(curses.stdscr) - titleLen - 1; + curses.move(0, titleLen + (ncols < menu->cursesCursor ? ncols : menu->cursesCursor)); curses.refresh(); } -- cgit v1.2.3-70-g09d2 From 48cf42f0c9f9850659e739602b7e27b614266364 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 02:02:11 +0300 Subject: Buffer grown too much. --- lib/draw/curses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 22abfd9..034ed6f 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -107,7 +107,7 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) } if (dw < ncols) { - if (!_bmDrawCursesResizeBuffer(&buffer, blen - 1, blen + (ncols - dw) + 1)) + if (!_bmDrawCursesResizeBuffer(&buffer, blen - 1, blen + (ncols - dw))) return; } else if (i < blen) { int cc = dw - (dw - ncols); -- cgit v1.2.3-70-g09d2 From c82ebb768c39d19df084a61b6d4332f66fd341b9 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 23:09:01 +0300 Subject: Support mac, duplicate stdin/stdout to make piping possible. --- lib/draw/curses.c | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 034ed6f..83a039b 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -8,6 +8,21 @@ #include #include #include +#include + +#if _WIN32 +static const char *TTY = "CON"; +#else +static const char *TTY = "/dev/tty"; +#endif + +#if __APPLE__ + const char *DL_PATH = "libncurses.5.dylib"; +#elif _WIN32 +# error FIXME: Compile with windows... or use relative path? +#else + const char *DL_PATH = "libncursesw.so.5"; +#endif /* ncurses.h likes to define stuff for us. * This unforunately mangles with our struct. */ @@ -45,6 +60,8 @@ static struct curses { int (*getmaxy)(WINDOW *win); int (*keypad)(WINDOW *win, bool bf); int *ESCDELAY; + int oldStdin; + int oldStdout; } curses; static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) @@ -127,8 +144,14 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) static void _bmDrawCursesRender(const bmMenu *menu) { if (!curses.stdscr) { - freopen("/dev/tty", "r", stdin); + 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; @@ -169,14 +192,22 @@ static void _bmDrawCursesRender(const bmMenu *menu) static void _bmDrawCursesEndWin(void) { + freopen(TTY, "w", stdout); + if (curses.refresh) curses.refresh(); if (curses.endwin) curses.endwin(); + if (curses.stdscr) { + dup2(curses.oldStdin, STDIN_FILENO); + dup2(curses.oldStdout, STDOUT_FILENO); + close(curses.oldStdin); + close(curses.oldStdout); + } + curses.stdscr = NULL; - freopen("/dev/tty", "w", stdout); } static bmKey _bmDrawCursesGetKey(unsigned int *unicode) @@ -278,9 +309,7 @@ static void _bmDrawCursesCrashHandler(int sig) int _bmDrawCursesInit(struct _bmRenderApi *api) { memset(&curses, 0, sizeof(curses)); - - /* FIXME: hardcoded and not cross-platform */ - curses.handle = dlopen("/usr/lib/libncursesw.so.5", RTLD_LAZY); + curses.handle = dlopen(DL_PATH, RTLD_LAZY); if (!curses.handle) return 0; -- cgit v1.2.3-70-g09d2 From 28a44b9e410f16ad6f532187b99299ec8536d449 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 23:11:54 +0300 Subject: Add cast for conversion when type isn't unsigned. --- lib/draw/curses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 83a039b..b3bed81 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -218,7 +218,7 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) if (!curses.stdscr) return BM_KEY_NONE; - curses.get_wch(unicode); + curses.get_wch((wint_t*)unicode); switch (*unicode) { case 16: /* C-p */ case KEY_UP: -- cgit v1.2.3-70-g09d2 From 3c6e167578b1842d53219c143aa2e6fda22ad482 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 23:58:09 +0300 Subject: Silly me, why not just get the highlighted item once.. --- lib/filter.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/filter.c b/lib/filter.c index c8cbfff..3dd2ba4 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -106,7 +106,8 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c unsigned int tokc; char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); - char found = 0; + bmItem *highlighted = bmMenuGetHighlightedItem(menu); + unsigned int i, f; for (f = i = 0; i < itemsCount; ++i) { bmItem *item = items[i]; @@ -120,10 +121,8 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c continue; } - if (!found && item == bmMenuGetHighlightedItem(menu)) { + if (item == highlighted) *outHighlighted = f; - found = 1; - } filtered[f++] = item; } -- cgit v1.2.3-70-g09d2 From 5a0a2659edbaaede9bed08c431013230a0e11122 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Fri, 11 Apr 2014 23:58:36 +0300 Subject: Cleanup dmenu filter. --- lib/filter.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/filter.c b/lib/filter.c index 3dd2ba4..85a0037 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -98,13 +98,15 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c items = bmMenuGetItems(menu, &itemsCount); } + char *buffer = NULL; bmItem **filtered = calloc(itemsCount, sizeof(bmItem*)); if (!filtered) - return NULL; + goto fail; char **tokv; unsigned int tokc; - char *buffer = _bmFilterTokenize(menu, &tokv, &tokc); + if (!(buffer = _bmFilterTokenize(menu, &tokv, &tokc))) + goto fail; bmItem *highlighted = bmMenuGetHighlightedItem(menu); @@ -129,11 +131,17 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c if (buffer) free(buffer); - if (tokv) free(tokv); return _bmFilterShrinkList(&filtered, menu->items.count, (*outNmemb = f)); + +fail: + if (filtered) + free(filtered); + if (buffer) + free(buffer); + return NULL; } /** -- cgit v1.2.3-70-g09d2 From 46486abc64fc84a7439edc59e0f5b8b5459b3f99 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 00:00:39 +0300 Subject: Delete is emitted on mac terminal. --- lib/draw/curses.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index b3bed81..3b0e610 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -251,6 +251,7 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) return BM_KEY_PAGE_DOWN; case 8: /* C-h */ + case 127: /* Delete */ case KEY_BACKSPACE: return BM_KEY_BACKSPACE; -- cgit v1.2.3-70-g09d2 From 29c34b0861ff1e5b16831f9093863934cd429e7a Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 00:01:06 +0300 Subject: Consistency. --- lib/draw/curses.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3b0e610..30bbd8f 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -244,10 +244,10 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case KEY_END: return BM_KEY_END; - case KEY_PPAGE: /* PAGE UP */ + case KEY_PPAGE: /* Page up */ return BM_KEY_PAGE_UP; - case KEY_NPAGE: /* PAGE DOWN */ + case KEY_NPAGE: /* Page down */ return BM_KEY_PAGE_DOWN; case 8: /* C-h */ -- cgit v1.2.3-70-g09d2 From 058d8a98edb99caa538a922ac13f0f9fab5fb7fb Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 11:15:22 +0300 Subject: Try ncursesw and then ncruses as fallback. --- lib/draw/curses.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 30bbd8f..cb1d204 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -17,11 +17,19 @@ static const char *TTY = "/dev/tty"; #endif #if __APPLE__ - const char *DL_PATH = "libncurses.5.dylib"; + 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"; + const char *DL_PATH[] = { + "libncursesw.so.5", + "libncurses.so.5", + NULL + }; #endif /* ncurses.h likes to define stuff for us. @@ -310,7 +318,10 @@ static void _bmDrawCursesCrashHandler(int sig) int _bmDrawCursesInit(struct _bmRenderApi *api) { memset(&curses, 0, sizeof(curses)); - curses.handle = dlopen(DL_PATH, RTLD_LAZY); + + unsigned int i; + for (i = 0; DL_PATH[i] && !curses.handle; ++i) + curses.handle = dlopen(DL_PATH[i], RTLD_LAZY); if (!curses.handle) return 0; -- cgit v1.2.3-70-g09d2 From 471046d1b2f9ee0a7f6ac3814a261ef67e218976 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 12:55:05 +0300 Subject: Make comparator functions follow standard more. --- lib/internal.h | 1 + lib/util.c | 41 ++++++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/internal.h b/lib/internal.h index 985d366..c33c441 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -159,6 +159,7 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); 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); diff --git a/lib/util.c b/lib/util.c index 06c89cf..a77e2ff 100644 --- a/lib/util.c +++ b/lib/util.c @@ -32,19 +32,38 @@ char* _bmStrdup(const char *string) * * @param hay C "string" to match against. * @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) { - size_t i, len; + size_t len, len2; - if ((len = strlen(hay)) != strlen(needle)) - return 1; + if ((len = strlen(hay)) != (len2 = strlen(needle))) + return hay[len] - needle[len2]; - for (i = 0; i != len; ++i) - if (toupper(hay[i]) != toupper(needle[i])) - return 1; + return _bmStrnupcmp(hay, needle, len); +} + +/** + * Portable case-insensitive strncmp. + * + * @param hay C "string" to match against. + * @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) +{ + 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; - return 0; + for (i = 0; len > 0; --len, ++i) + if ((a = toupper(*p1++)) != (b = toupper(*p2++))) + return a - b; + + return a - b; } /** @@ -57,13 +76,13 @@ char* _bmStrupstr(const char *hay, const char *needle) { size_t i, r = 0, p = 0, len, len2; - if (!_bmStrupcmp(hay, needle)) - return (char*)hay; - if ((len = strlen(hay)) < (len2 = strlen(needle))) return NULL; - for (i = 0; i != len; ++i) { + if (!_bmStrnupcmp(hay, needle, len2)) + return (char*)hay; + + for (i = 0; i < len; ++i) { if (p == len2) return (char*)hay + r; -- cgit v1.2.3-70-g09d2 From 9d867bea0c0fdd620d258ecf30e30b40e5dbe3b1 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 12:56:34 +0300 Subject: Sort results like dmenu does. --- lib/filter.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/filter.c b/lib/filter.c index 85a0037..16e57a3 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -82,9 +82,11 @@ fail: * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted) +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, unsigned int *outHighlighted) { assert(menu); + assert(fstrstr); + assert(fstrncmp); assert(outNmemb); assert(outHighlighted); *outNmemb = *outHighlighted = 0; @@ -110,8 +112,9 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c bmItem *highlighted = bmMenuGetHighlightedItem(menu); - unsigned int i, f; - for (f = i = 0; i < itemsCount; ++i) { + 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]; if (!item->text && tokc != 0) continue; @@ -126,7 +129,19 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c if (item == highlighted) *outHighlighted = f; - filtered[f++] = item; + if (tokc && item->text && !fstrncmp(tokv[0], item->text, len + 1)) { /* exact matches */ + //memmove(&filtered[e + 1], &filtered[e], (f - e) * sizeof(bmItem*)); + memmove(&filtered[1], filtered, f * sizeof(bmItem*)); + 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*)); + filtered[e] = item; + e++; /* where do exact matches end */ + } else { + filtered[f] = item; + } + f++; /* where do all matches end */ } if (buffer) @@ -155,7 +170,7 @@ fail: */ bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) { - return _bmFilterDmenuFun(menu, addition, strstr, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb, outHighlighted); } /** @@ -169,7 +184,7 @@ bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, uns */ bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) { - return _bmFilterDmenuFun(menu, addition, _bmStrupstr, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb, outHighlighted); } /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From f2608c3c64cb72e1e315142076a7c356facad7de Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 14:15:37 +0300 Subject: Handle empty items. --- client/client.c | 6 ++++-- lib/draw/curses.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/client/client.c b/client/client.c index ecaed25..ff32d1b 100644 --- a/client/client.c +++ b/client/client.c @@ -103,8 +103,10 @@ int main(int argc, char **argv) if (status == BM_RUN_RESULT_SELECTED) { unsigned int i, count; bmItem **items = bmMenuGetSelectedItems(menu, &count); - for (i = 0; i < count; ++i) - printf("%s\n", bmItemGetText(items[i])); + for (i = 0; i < count; ++i) { + const char *text = bmItemGetText(items[i]); + printf("%s\n", (text ? text : "")); + } } bmMenuFree(menu); diff --git a/lib/draw/curses.c b/lib/draw/curses.c index cb1d204..653a07d 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -190,7 +190,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) 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); + _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "(null)")); } unsigned int ncols = curses.getmaxx(curses.stdscr) - titleLen - 1; -- cgit v1.2.3-70-g09d2 From 544b1973c4155ccef20aea77fef8a5e43210a426 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 14:16:25 +0300 Subject: Why is this here? Remove commented memmove. --- lib/filter.c | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/filter.c b/lib/filter.c index 16e57a3..4674b5d 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -130,7 +130,6 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c *outHighlighted = f; if (tokc && item->text && !fstrncmp(tokv[0], item->text, len + 1)) { /* exact matches */ - //memmove(&filtered[e + 1], &filtered[e], (f - e) * sizeof(bmItem*)); memmove(&filtered[1], filtered, f * sizeof(bmItem*)); filtered[0] = item; e++; /* where do exact matches end */ -- cgit v1.2.3-70-g09d2 From 9e9b671fa3863aecabdee4a9befdef91ef29f6f3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 14:36:12 +0300 Subject: Remove highlight logic from filters, it does not belong there. Plus it seems dmenu always just highlights first item on filter change. --- lib/filter.c | 21 ++++++--------------- lib/internal.h | 4 ++-- lib/menu.c | 8 ++++---- 3 files changed, 12 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/filter.c b/lib/filter.c index 4674b5d..836ad83 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -79,17 +79,15 @@ fail: * @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. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem 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, unsigned int *outHighlighted) +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) { assert(menu); assert(fstrstr); assert(fstrncmp); assert(outNmemb); - assert(outHighlighted); - *outNmemb = *outHighlighted = 0; + *outNmemb = 0; unsigned int itemsCount; bmItem **items; @@ -110,8 +108,6 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c if (!(buffer = _bmFilterTokenize(menu, &tokv, &tokc))) goto fail; - bmItem *highlighted = bmMenuGetHighlightedItem(menu); - size_t len = (tokc ? strlen(tokv[0]) : 0); unsigned int i, f, e; for (e = f = i = 0; i < itemsCount; ++i) { @@ -126,9 +122,6 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const c continue; } - if (item == highlighted) - *outHighlighted = f; - if (tokc && item->text && !fstrncmp(tokv[0], item->text, len + 1)) { /* exact matches */ memmove(&filtered[1], filtered, f * sizeof(bmItem*)); filtered[0] = item; @@ -164,12 +157,11 @@ fail: * @param menu bmMenu 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. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb) { - return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, strstr, strncmp, outNmemb); } /** @@ -178,12 +170,11 @@ bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, uns * @param menu bmMenu 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. - * @param outHighlighted unsigned int reference to new outHighlighted item index. * @return Pointer to array of bmItem pointers. */ -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb) { - return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb, outHighlighted); + return _bmFilterDmenuFun(menu, addition, _bmStrupstr, _bmStrnupcmp, outNmemb); } /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/internal.h b/lib/internal.h index c33c441..2acd6f5 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -141,8 +141,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api); int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item); /* filter.c */ -bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); -bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted); +bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb); +bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb); /* list.c */ void _bmItemListFreeList(struct _bmItemList *list); diff --git a/lib/menu.c b/lib/menu.c index edbcf73..0457da8 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -7,7 +7,7 @@ /** * Filter function map. */ -static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) = { +static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb) = { _bmFilterDmenu, /* BM_FILTER_DMENU */ _bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */ }; @@ -482,11 +482,11 @@ void bmMenuFilter(bmMenu *menu) if (menu->oldFilter && !strcmp(menu->filter, menu->oldFilter)) return; - unsigned int count, selected; - bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count, &selected); + unsigned int count; + bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count); _bmItemListSetItemsNoCopy(&menu->filtered, filtered, count); - menu->index = selected; + menu->index = 0; if (menu->oldFilter) free(menu->oldFilter); -- cgit v1.2.3-70-g09d2 From ad4e0425a6860803e5f31a08c349cd99e02e2847 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 18:42:30 +0300 Subject: Make page scrolling work like it should. (Shfit+pgup/pgdwn for old behaviour) --- lib/bemenu.h | 2 ++ lib/draw/curses.c | 13 +++++++++++++ lib/internal.h | 5 +++++ lib/menu.c | 17 ++++++++++++++++- 4 files changed, 36 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index d348ffa..0b0769d 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -77,6 +77,8 @@ typedef enum bmKey { BM_KEY_END, BM_KEY_PAGE_UP, BM_KEY_PAGE_DOWN, + BM_KEY_SHIFT_PAGE_UP, + BM_KEY_SHIFT_PAGE_DOWN, BM_KEY_BACKSPACE, BM_KEY_DELETE, BM_KEY_LINE_DELETE_LEFT, diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 653a07d..b54693f 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -198,6 +198,12 @@ static void _bmDrawCursesRender(const bmMenu *menu) curses.refresh(); } +static unsigned int _bmDrawCursesDisplayedCount(const bmMenu *menu) +{ + (void)menu; + return (curses.stdscr ? curses.getmaxy(curses.stdscr) : 0); +} + static void _bmDrawCursesEndWin(void) { freopen(TTY, "w", stdout); @@ -258,6 +264,12 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case KEY_NPAGE: /* Page down */ return BM_KEY_PAGE_DOWN; + case 398: /* S-Page up */ + return BM_KEY_SHIFT_PAGE_UP; + + case 396: /* S-Page down */ + return BM_KEY_SHIFT_PAGE_DOWN; + case 8: /* C-h */ case 127: /* Delete */ case KEY_BACKSPACE: @@ -361,6 +373,7 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) #undef bmLoadFunction + api->displayedCount = _bmDrawCursesDisplayedCount; api->getKey = _bmDrawCursesGetKey; api->render = _bmDrawCursesRender; api->free = _bmDrawCursesFree; diff --git a/lib/internal.h b/lib/internal.h index 2acd6f5..b3ca87c 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -27,6 +27,11 @@ struct _bmItem { * Renderers should be able to fill this one as they see fit. */ struct _bmRenderApi { + /** + * Get count of displayed items by the underlying renderer. + */ + unsigned int (*displayedCount)(const bmMenu *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. diff --git a/lib/menu.c b/lib/menu.c index 0457da8..e819dd5 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -532,6 +532,13 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) unsigned int itemsCount; bmMenuGetFilteredItems(menu, &itemsCount); + unsigned int displayed = 0; + if (menu->renderApi.displayedCount) + displayed = menu->renderApi.displayedCount(menu); + + if (!displayed) + displayed = itemsCount; + switch (key) { case BM_KEY_LEFT: { @@ -569,10 +576,18 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_PAGE_UP: - menu->index = 0; + menu->index = (menu->index < displayed ? 0 : menu->index - (displayed - 1)); break; case BM_KEY_PAGE_DOWN: + menu->index = (menu->index + displayed >= itemsCount ? itemsCount - 1 : menu->index + (displayed - 1)); + break; + + case BM_KEY_SHIFT_PAGE_UP: + menu->index = 0; + break; + + case BM_KEY_SHIFT_PAGE_DOWN: menu->index = itemsCount - 1; break; -- cgit v1.2.3-70-g09d2 From 3ad042c625d931b7043a857bb49cb0660258f343 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 18:44:03 +0300 Subject: S-Return (C-t, insert in curses) should return input, C-Return is mark. (C-r, C-space in curses) --- lib/bemenu.h | 1 + lib/draw/curses.c | 6 ++++++ lib/menu.c | 4 +++- 3 files changed, 10 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index 0b0769d..9ef684f 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -88,6 +88,7 @@ typedef enum bmKey { BM_KEY_ESCAPE, BM_KEY_RETURN, BM_KEY_SHIFT_RETURN, + BM_KEY_CONTROL_RETURN, BM_KEY_UNICODE, BM_KEY_LAST } bmKey; diff --git a/lib/draw/curses.c b/lib/draw/curses.c index b54693f..4b467f6 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -291,7 +291,13 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case 9: /* Tab */ return BM_KEY_TAB; + case 0: /* C-Space */ 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 */ diff --git a/lib/menu.c b/lib/menu.c index e819dd5..a53451d 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -663,7 +663,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; - case BM_KEY_SHIFT_RETURN: + case BM_KEY_CONTROL_RETURN: case BM_KEY_RETURN: { bmItem *highlighted = bmMenuGetHighlightedItem(menu); @@ -672,6 +672,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) } break; + case BM_KEY_SHIFT_RETURN: case BM_KEY_ESCAPE: _bmItemListFreeList(&menu->selection); break; @@ -682,6 +683,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) bmMenuFilter(menu); switch (key) { + case BM_KEY_SHIFT_RETURN: case BM_KEY_RETURN: return BM_RUN_RESULT_SELECTED; case BM_KEY_ESCAPE: return BM_RUN_RESULT_CANCEL; default: break; -- cgit v1.2.3-70-g09d2 From 042448798d9d2b95ce5ab9ce718b9a16ef0a5fc1 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 18:44:52 +0300 Subject: Add more keybindings to curses interface. --- lib/draw/curses.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 4b467f6..26754be 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -251,10 +251,12 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) 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; @@ -279,6 +281,7 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case KEY_DC: return BM_KEY_DELETE; + case 383: /* S-Del */ case 21: /* C-u */ return BM_KEY_LINE_DELETE_LEFT; -- cgit v1.2.3-70-g09d2 From 311e4b36768a0d4e113ccf6d2256ca95c6621508 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 19:59:21 +0300 Subject: Use strcspn instead of strtok --- client/client.c | 7 +++++-- lib/draw/curses.c | 10 +++++++--- lib/filter.c | 9 +++++++-- lib/internal.h | 1 + lib/util.c | 13 +++++++++++++ 5 files changed, 33 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/client/client.c b/client/client.c index c031c0b..8e670b1 100644 --- a/client/client.c +++ b/client/client.c @@ -26,13 +26,16 @@ static void readItemsToMenuFromStdin(bmMenu *menu) } buffer[allocated - step + read - 1] = 0; - char *s; - for (s = strtok(buffer, "\n"); s; s = strtok(NULL, "\n")) { + size_t pos; + char *s = buffer; + while ((pos = strcspn(s, "\n")) != 0) { + s[pos] = 0; bmItem *item = bmItemNew(s); if (!item) break; bmMenuAddItem(menu, item); + s += pos + 1; } free(buffer); diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 26754be..3b99cd9 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,14 +1,18 @@ #include "../internal.h" -#define _XOPEN_SOURCE 700 + +#define _XOPEN_SOURCE 500 +#include /* sigaction */ +#include /* vsnprintf */ +#undef _XOPEN_SOURCE + #include +#include #include #include #include #include #include -#include #include -#include #if _WIN32 static const char *TTY = "CON"; diff --git a/lib/filter.c b/lib/filter.c index 836ad83..811451b 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -54,12 +54,17 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT if (!(buffer = _bmStrdup(menu->filter))) goto fail; - char *s, **tmp = NULL; + size_t pos = 0; unsigned int tokc = 0, tokn = 0; - for (s = strtok(buffer, " "); s; tmp[tokc - 1] = s, s = strtok(NULL, " "), tokv = tmp) + char *s = buffer, **tmp = NULL; + while ((pos = _bmStripToken(s, " ")) != 0) { if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) goto fail; + tmp[tokc - 1] = s; + s += pos + 1; + } + *outTokv = tmp; *outTokc = tokc; return buffer; diff --git a/lib/internal.h b/lib/internal.h index b3ca87c..1ed13b4 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -163,6 +163,7 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); +size_t _bmStripToken(char *string, const char *token); 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); diff --git a/lib/util.c b/lib/util.c index a77e2ff..49f9175 100644 --- a/lib/util.c +++ b/lib/util.c @@ -27,6 +27,19 @@ char* _bmStrdup(const char *string) return (char *)memcpy(copy, string, len); } +/** + * Replaces next token in string with '\0' and returns position for the replaced token. + * + * @param string C "string" where token will be replaced. + * @return Position of the replaced token. + */ +size_t _bmStripToken(char *string, const char *token) +{ + size_t len = strcspn(string, token); + string[len] = 0; + return len; +} + /** * Portable case-insensitive strcmp. * -- cgit v1.2.3-70-g09d2 From 57e76dda1ce422fe5a00df8ee0791aef21dcfe28 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 19:59:45 +0300 Subject: Cleanup header includes. --- client/client.c | 1 - lib/util.c | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/client/client.c b/client/client.c index 8e670b1..8bc4be1 100644 --- a/client/client.c +++ b/client/client.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include diff --git a/lib/util.c b/lib/util.c index 49f9175..351bb5f 100644 --- a/lib/util.c +++ b/lib/util.c @@ -1,6 +1,9 @@ #include "internal.h" -#define _XOPEN_SOURCE 700 -#include + +#define _XOPEN_SOURCE +#include /* wcswidth */ +#undef _XOPEN_SOURCE + #include #include #include -- cgit v1.2.3-70-g09d2 From e738ae17728c697d64beb9adb2dfcc921f864120 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 19:59:58 +0300 Subject: Discard unprintable single width characters instead. --- lib/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.c b/lib/util.c index 351bb5f..c4533ca 100644 --- a/lib/util.c +++ b/lib/util.c @@ -242,7 +242,7 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char if (len + u8len >= bufSize) return 0; - if (u8len == 1 && iscntrl(*rune)) + if (u8len == 1 && !isprint(*rune)) return 0; char *str = string + start; -- cgit v1.2.3-70-g09d2 From d54381f00991669fa4ee4f3a8037f246ca1904f8 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:16:33 +0300 Subject: Fix out of bound access, and provide better tokenize api. --- client/client.c | 4 +++- lib/filter.c | 6 +++--- lib/internal.h | 2 +- lib/util.c | 7 ++++++- 4 files changed, 13 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/client/client.c b/client/client.c index 8bc4be1..2f95d52 100644 --- a/client/client.c +++ b/client/client.c @@ -28,13 +28,15 @@ static void readItemsToMenuFromStdin(bmMenu *menu) size_t pos; char *s = buffer; while ((pos = strcspn(s, "\n")) != 0) { + size_t next = pos + (s[pos] != 0); s[pos] = 0; + bmItem *item = bmItemNew(s); if (!item) break; bmMenuAddItem(menu, item); - s += pos + 1; + s += next; } free(buffer); diff --git a/lib/filter.c b/lib/filter.c index 811451b..204eac1 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -54,15 +54,15 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT if (!(buffer = _bmStrdup(menu->filter))) goto fail; - size_t pos = 0; + size_t pos = 0, next; unsigned int tokc = 0, tokn = 0; char *s = buffer, **tmp = NULL; - while ((pos = _bmStripToken(s, " ")) != 0) { + while ((pos = _bmStripToken(s, " ", &next)) > 0) { if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) goto fail; tmp[tokc - 1] = s; - s += pos + 1; + s += next; } *outTokv = tmp; diff --git a/lib/internal.h b/lib/internal.h index 1ed13b4..6d116a0 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -163,7 +163,7 @@ int _bmItemListRemoveItem(struct _bmItemList *list, const bmItem *item); /* util.c */ char* _bmStrdup(const char *s); -size_t _bmStripToken(char *string, const char *token); +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); diff --git a/lib/util.c b/lib/util.c index c4533ca..bcb27cb 100644 --- a/lib/util.c +++ b/lib/util.c @@ -34,11 +34,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. * @return Position of the replaced token. */ -size_t _bmStripToken(char *string, const char *token) +size_t _bmStripToken(char *string, const char *token, size_t *outNext) { size_t len = strcspn(string, token); + + if (outNext) + *outNext = len + (string[len] != 0); + string[len] = 0; return len; } -- cgit v1.2.3-70-g09d2 From bfc53136c8fbea59f116d8d0de92b2711f7804c0 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:21:40 +0300 Subject: Add print attribute and fix warning it catched. --- lib/draw/curses.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3b99cd9..656e47d 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -101,6 +101,9 @@ static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) return 1; } +#if __GNUC__ +__attribute__((format(printf, 3, 4))) +#endif static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) { static int blen = 0; @@ -178,8 +181,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) const unsigned int lines = curses.getmaxy(curses.stdscr); curses.erase(); - size_t titleLen = (menu->title ? strlen(menu->title) + 1 : 0); - + int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", menu->filter); if (menu->title) { -- cgit v1.2.3-70-g09d2 From 6692f73c5c3e34baa5b5d8e2fdfa524291979317 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:51:32 +0300 Subject: Meh, no (null) it's empty if it's empty. --- lib/draw/curses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 656e47d..946275e 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -196,7 +196,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) 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 : "(null)")); + _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "")); } unsigned int ncols = curses.getmaxx(curses.stdscr) - titleLen - 1; -- cgit v1.2.3-70-g09d2 From 5599e1aa8ec62788f4a75f0944eb58bbab7035df Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:52:01 +0300 Subject: Store tmp to tokv in for loop, so we can free it if something fails. --- lib/filter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/filter.c b/lib/filter.c index 204eac1..ad1b937 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -57,8 +57,8 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT size_t pos = 0, next; unsigned int tokc = 0, tokn = 0; char *s = buffer, **tmp = NULL; - while ((pos = _bmStripToken(s, " ", &next)) > 0) { - if (++tokc > tokn && !(tmp = realloc(tmp, ++tokn * sizeof(char*)))) + for (; (pos = _bmStripToken(s, " ", &next)) > 0; tokv = tmp) { + if (++tokc > tokn && !(tmp = realloc(tokv, ++tokn * sizeof(char*)))) goto fail; tmp[tokc - 1] = s; -- cgit v1.2.3-70-g09d2 From f03e03cdd9c267b9f3c171ba57799d876d80d819 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:52:29 +0300 Subject: Make filter to pointer. --- lib/draw/curses.c | 2 +- lib/internal.h | 12 ++++++++---- lib/menu.c | 40 ++++++++++++++++++---------------------- lib/util.c | 41 +++++++++++++++++++++++++++++------------ 4 files changed, 56 insertions(+), 39 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 946275e..3d7871e 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -182,7 +182,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) curses.erase(); int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); - _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", menu->filter); + _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter : "")); if (menu->title) { curses.attron(COLOR_PAIR(1)); diff --git a/lib/internal.h b/lib/internal.h index 6d116a0..4f6afbe 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -103,15 +103,19 @@ struct _bmMenu { /** * Text used to filter matches. - * XXX: Change this to a pointer? */ - char filter[1024]; + char *filter; /** * Used as optimization. */ char *oldFilter; + /** + * Size of filter buffer + */ + size_t filterSize; + /** * Current byte offset on filter text. */ @@ -173,7 +177,7 @@ 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); +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); /* vim: set ts=8 sw=4 tw=0 :*/ diff --git a/lib/menu.c b/lib/menu.c index a53451d..f414772 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -71,6 +71,9 @@ void bmMenuFree(bmMenu *menu) if (menu->title) free(menu->title); + if (menu->filter) + free(menu->filter); + if (menu->oldFilter) free(menu->oldFilter); @@ -126,12 +129,11 @@ void bmMenuSetFilter(bmMenu *menu, const char *filter) { assert(menu); - if (!filter) { - memset(menu->filter, 0, sizeof(menu->filter)); - return; - } + if (menu->filter) + free(menu->filter); - strncpy(menu->filter, filter, sizeof(menu->filter)); + menu->filter = (filter ? _bmStrdup(filter) : NULL); + menu->filterSize = (filter ? strlen(filter) : 0); } /** @@ -426,7 +428,7 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb) { assert(menu); - if (strlen(menu->filter)) + if (menu->filter && strlen(menu->filter)) return _bmItemListGetItems(&menu->filtered, outNmemb); return _bmItemListGetItems(&menu->items, outNmemb); @@ -459,7 +461,7 @@ void bmMenuFilter(bmMenu *menu) assert(menu); char addition = 0; - size_t len = strlen(menu->filter); + size_t len = (menu->filter ? strlen(menu->filter) : 0); if (!len || !menu->items.list || menu->items.count <= 0) { _bmItemListFreeList(&menu->filtered); @@ -561,7 +563,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_END: - menu->cursor = strlen(menu->filter); + menu->cursor = (menu->filter ? strlen(menu->filter) : 0); menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); break; @@ -619,17 +621,17 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_WORD_DELETE: { - while (menu->cursor < strlen(menu->filter) && !isspace(menu->filter[menu->cursor])) { + while (menu->cursor < (menu->filter ? strlen(menu->filter) : 0) && !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); } - while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && menu->filter && 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); } - while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && menu->filter && !isspace(menu->filter[menu->cursor - 1])) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -640,24 +642,18 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_UNICODE: { size_t width; - menu->cursor += _bmUnicodeInsert(menu->filter, sizeof(menu->filter) - 1, menu->cursor, unicode, &width); + menu->cursor += _bmUnicodeInsert(&menu->filter, &menu->filterSize, menu->cursor, unicode, &width); menu->cursesCursor += width; } break; case BM_KEY_TAB: { + const char *text; bmItem *highlighted = bmMenuGetHighlightedItem(menu); - if (highlighted && bmItemGetText(highlighted)) { - const char *text = bmItemGetText(highlighted); - size_t len = strlen(text); - - if (len > sizeof(menu->filter) - 1) - len = sizeof(menu->filter) - 1; - - memset(menu->filter, 0, strlen(menu->filter)); - memcpy(menu->filter, text, len); - menu->cursor = strlen(menu->filter); + if (highlighted && (text = bmItemGetText(highlighted))) { + bmMenuSetFilter(menu, text); + menu->cursor = (menu->filter ? strlen(menu->filter) : 0); menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); } } diff --git a/lib/util.c b/lib/util.c index bcb27cb..ca00514 100644 --- a/lib/util.c +++ b/lib/util.c @@ -228,29 +228,45 @@ size_t _bmUtf8RuneRemove(char *string, size_t start, size_t *outRuneWidth) /** * Insert UTF8 rune to buffer. * - * @param string Null terminated C "string". - * @param bufSize Size of the buffer. + * @param inOutString Reference to buffer. + * @param inOutBufSize 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. * @return Number of bytes inserted to buffer. */ -size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth) +size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, const char *rune, unsigned int u8len, size_t *outRuneWidth) { - assert(string); + assert(inOutString); + assert(inOutBufSize); if (outRuneWidth) *outRuneWidth = 0; - size_t len = strlen(string); - if (len + u8len >= bufSize) + size_t len = (*inOutString ? strlen(*inOutString) : 0); + if (!*inOutString && !(*inOutString = calloc(1, (*inOutBufSize = u8len + 1)))) return 0; + if (len + u8len >= *inOutBufSize) { + void *tmp; + if (!(tmp = realloc(*inOutString, (*inOutBufSize * 2)))) { + if (!(tmp = malloc((*inOutBufSize * 2)))) + return 0; + + memcpy(tmp, *inOutString, *inOutBufSize); + free(*inOutString); + } + + memset(tmp + *inOutBufSize, 0, *inOutBufSize); + *inOutString = tmp; + *inOutBufSize *= 2; + } + if (u8len == 1 && !isprint(*rune)) return 0; - char *str = string + start; + char *str = *inOutString + start; memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); @@ -262,16 +278,17 @@ size_t _bmUtf8RuneInsert(char *string, size_t bufSize, size_t start, const char /** * Insert unicode character to UTF8 buffer. * - * @param string Null terminated C "string". - * @param bufSize Size of the buffer. + * @param inOutString Reference to buffer. + * @param inOutBufSize 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. * @return Number of bytes inserted to buffer. */ -size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int unicode, size_t *outRuneWidth) +size_t _bmUnicodeInsert(char **inOutString, size_t *inOutBufSize, size_t start, unsigned int unicode, size_t *outRuneWidth) { - assert(string); + assert(inOutString); + assert(inOutBufSize); char u8len = ((unicode < 0x80) ? 1 : ((unicode < 0x800) ? 2 : ((unicode < 0x10000) ? 3 : 4))); char mb[5] = { 0, 0, 0, 0 }; @@ -285,7 +302,7 @@ size_t _bmUnicodeInsert(char *string, size_t bufSize, size_t start, unsigned int mb[0] |= (unicode >> (i * 6 - 6)); } - return _bmUtf8RuneInsert(string, bufSize, start, mb, u8len, outRuneWidth); + return _bmUtf8RuneInsert(inOutString, inOutBufSize, start, mb, u8len, outRuneWidth); } /* vim: set ts=8 sw=4 tw=0 :*/ -- cgit v1.2.3-70-g09d2 From ab54f2bc0796b963ac8181f8f31d720e9feffd98 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:55:45 +0300 Subject: Here be dragons comments. --- lib/list.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/list.c b/lib/list.c index fb0bdb2..203adf2 100644 --- a/lib/list.c +++ b/lib/list.c @@ -35,6 +35,7 @@ bmItem** _bmItemListGetItems(const struct _bmItemList *list, unsigned int *outNm return list->list; } +/** !!! Frees the old list, not items !!! */ int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned int nmemb) { assert(list); @@ -51,6 +52,7 @@ int _bmItemListSetItemsNoCopy(struct _bmItemList *list, bmItem **items, unsigned return 1; } +/** !!! Frees the old items and list !!! */ int _bmItemListSetItems(struct _bmItemList *list, const bmItem **items, unsigned int nmemb) { assert(list); -- cgit v1.2.3-70-g09d2 From 01ff5f48edde73364d8f4b11f4f5480c8cc86789 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 20:56:00 +0300 Subject: The list will be copied now. --- lib/bemenu.h | 2 -- lib/menu.c | 10 +++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index 9ef684f..37c7d1e 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -252,8 +252,6 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu); /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied, do not free it. - * * @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. diff --git a/lib/menu.c b/lib/menu.c index f414772..e31c25f 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -346,8 +346,6 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) /** * Set selected items to bmMenu instance. * - * @warning The list won't be copied, do not free it. - * * @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. @@ -356,7 +354,13 @@ bmItem* bmMenuGetHighlightedItem(const bmMenu *menu) int bmMenuSetSelectedItems(bmMenu *menu, bmItem **items, unsigned int nmemb) { assert(menu); - return _bmItemListSetItemsNoCopy(&menu->selection, items, nmemb); + + bmItem **newItems; + if (!(newItems = calloc(sizeof(bmItem*), nmemb))) + return 0; + + memcpy(newItems, items, sizeof(bmItem*) * nmemb); + return _bmItemListSetItemsNoCopy(&menu->selection, newItems, nmemb); } /** -- cgit v1.2.3-70-g09d2 From 06972a357983a4686dc37a2aa5a8b0bfd2dd4e7e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 22:12:44 +0300 Subject: CLI interface, aka option parsing. --- client/client.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++- lib/CMakeLists.txt | 7 ++- lib/bemenu.h | 21 ++++++++ lib/library.c | 8 +++ lib/version.h.in | 3 ++ 5 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 lib/library.c create mode 100644 lib/version.h.in (limited to 'lib') diff --git a/client/client.c b/client/client.c index 2f95d52..fb94ea5 100644 --- a/client/client.c +++ b/client/client.c @@ -3,6 +3,140 @@ #include #include #include +#include + +static struct { + bmFilterMode filterMode; + int wrap; + unsigned int lines; + const char *title; + int selected; + int bottom; + int grab; + int monitor; +} client = { + BM_FILTER_MODE_DMENU, /* filterMode */ + 0, /* wrap */ + 0, /* lines */ + "bemenu", /* title */ + 0, /* selected */ + 0, /* bottom */ + 0, /* grab */ + 0 /* monitor */ +}; + +static void printVersion(const char *name) +{ + char *base = strrchr(name, '/'); + printf("%s v%s\n", (base ? base + 1 : name), bmVersion()); + puts(" Date: Sat, 12 Apr 2014 22:42:37 +0300 Subject: Improve CMakeLists and make stuff installable. --- client/CMakeLists.txt | 8 ++++++-- lib/CMakeLists.txt | 28 ++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index aceaaf5..6af4e3e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,7 +1,7 @@ # Sources SET(CLIENT_SOURCE client.c) -SET(CLIENT_INCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/../lib") -SET(CLIENT_LIBRARIES bemenu) +SET(CLIENT_INCLUDE ${BEMENU_INCLUDE_DIRS}) +SET(CLIENT_LIBRARIES ${BEMENU_LIBRARIES}) # Warnings IF (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) @@ -20,5 +20,9 @@ ENDIF () INCLUDE_DIRECTORIES(${CLIENT_INCLUDE}) ADD_EXECUTABLE(client ${CLIENT_SOURCE}) TARGET_LINK_LIBRARIES(client ${CLIENT_LIBRARIES}) +SET_TARGET_PROPERTIES(client PROPERTIES OUTPUT_NAME bemenu) + +# Install +INSTALL(TARGETS client DESTINATION bin) # vim: set ts=8 sw=4 tw=0 : diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bfdb843..a52bc7b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -8,9 +8,8 @@ SET(BEMENU_SOURCE library.c draw/curses.c ) -SET(BEMENU_INCLUDE) -SET(BEMENU_LIBRARIES) +# Configure CONFIGURE_FILE(version.h.in version.h @ONLY) # Warnings @@ -26,9 +25,30 @@ IF (UNIX AND CMAKE_COMPILER_IS_GNUCC) ENDIF () ENDIF () +# Parse soversion version +STRING(REGEX MATCHALL "[0-9]+" VERSION_COMPONENTS ${BEMENU_VERSION}) +LIST(GET VERSION_COMPONENTS 0 SOVERSION) + # Compile INCLUDE_DIRECTORIES(${BEMENU_INCLUDE} ${CMAKE_CURRENT_BINARY_DIR}) -ADD_LIBRARY(bemenu ${BEMENU_SOURCE}) -TARGET_LINK_LIBRARIES(bemenu ${BEMENU_LIBRARIES} dl) +ADD_LIBRARY(bemenu SHARED ${BEMENU_SOURCE}) +SET_TARGET_PROPERTIES(bemenu PROPERTIES + VERSION ${BEMENU_VERSION} + SOVERSION ${SOVERSION}) +TARGET_LINK_LIBRARIES(bemenu dl) + +# Install +INSTALL(TARGETS bemenu DESTINATION lib) +INSTALL(FILES bemenu.h DESTINATION include) + +# Unexport +SET(BEMENU_INCLUDES) +SET(BEMENU_INCLUDE_DIRS) +SET(BEMENU_LIBRARIES) + +# Export +SET(BEMENU_INCLUDES "bemenu.h" CACHE STRING "bemenu includes exported from CMake") +SET(BEMENU_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} CACHE STRING "bemenu public header include path exported from CMake") +SET(BEMENU_LIBRARIES "bemenu" "dl" CACHE STRING "bemenu libraries exported from CMake") # vim: set ts=8 sw=4 tw=0 : -- cgit v1.2.3-70-g09d2 From 994d6074bc390492764adb51dc2235d245145afa Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:09:35 +0300 Subject: Stop assuming filter is static array anymore. --- lib/menu.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index e31c25f..5d2b37b 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -547,7 +547,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) 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); @@ -555,7 +555,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) 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); @@ -568,7 +568,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) case BM_KEY_END: menu->cursor = (menu->filter ? strlen(menu->filter) : 0); - menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + menu->cursesCursor = (menu->filter ? _bmUtf8StringScreenWidth(menu->filter) : 0); break; case BM_KEY_UP: @@ -598,7 +598,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_BACKSPACE: - { + if (menu->filter) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -606,11 +606,12 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_DELETE: - _bmUtf8RuneRemove(menu->filter, menu->cursor + 1, NULL); + if (menu->filter) + _bmUtf8RuneRemove(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); @@ -620,22 +621,23 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_LINE_DELETE_RIGHT: - menu->filter[menu->cursor] = 0; + if (menu->filter) + menu->filter[menu->cursor] = 0; break; case BM_KEY_WORD_DELETE: - { - while (menu->cursor < (menu->filter ? strlen(menu->filter) : 0) && !isspace(menu->filter[menu->cursor])) { + 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); } - while (menu->cursor > 0 && menu->filter && isspace(menu->filter[menu->cursor - 1])) { + 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); } - while (menu->cursor > 0 && menu->filter && !isspace(menu->filter[menu->cursor - 1])) { + while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) { size_t width; menu->cursor -= _bmUtf8RuneRemove(menu->filter, menu->cursor, &width); menu->cursesCursor -= width; @@ -658,7 +660,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) if (highlighted && (text = bmItemGetText(highlighted))) { bmMenuSetFilter(menu, text); menu->cursor = (menu->filter ? strlen(menu->filter) : 0); - menu->cursesCursor = _bmUtf8StringScreenWidth(menu->filter); + menu->cursesCursor = (menu->filter ? _bmUtf8StringScreenWidth(menu->filter) : 0); } } break; -- cgit v1.2.3-70-g09d2 From f722f247fcd2d53c346cc99bba15c0d20a16c652 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:20:53 +0300 Subject: Xterm does not feed S+pgup/pgdown but does so for Ctrl combination --- lib/draw/curses.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3d7871e..17089af 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -272,9 +272,11 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) 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; -- cgit v1.2.3-70-g09d2 From d22612899a74005fd442d54710c86e677742c7f4 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:21:43 +0300 Subject: Curses really does not like tabs. --- lib/util.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/util.c b/lib/util.c index ca00514..fa85431 100644 --- a/lib/util.c +++ b/lib/util.c @@ -130,16 +130,26 @@ int _bmUtf8StringScreenWidth(const char *string) { assert(string); - int num_char = mbstowcs(NULL, string, 0) + 1; + char *mstr = _bmStrdup(string); + if (!mstr) + return strlen(string); + + char *s; + for (s = mstr; *s; ++s) if (*s == '\t') *s = ' '; + + int num_char = mbstowcs(NULL, mstr, 0) + 1; wchar_t *wstring = malloc((num_char + 1) * sizeof (wstring[0])); - if (mbstowcs(wstring, string, num_char) == (size_t)(-1)) { + if (mbstowcs(wstring, mstr, num_char) == (size_t)(-1)) { free(wstring); - return strlen(string); + int len = strlen(mstr); + free(mstr); + return len; } int length = wcswidth(wstring, num_char); free(wstring); + free(mstr); return length; } -- cgit v1.2.3-70-g09d2 From 7da3ebe4239675b18560ab642abd1a100801da66 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 12 Apr 2014 23:58:01 +0300 Subject: Scrollable curses input \o/ --- lib/draw/curses.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 17089af..9e8502b 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -181,8 +181,18 @@ static void _bmDrawCursesRender(const bmMenu *menu) const unsigned int lines = curses.getmaxy(curses.stdscr); curses.erase(); - int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); - _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter : "")); + unsigned int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); + unsigned int ncols = curses.getmaxx(curses.stdscr); + unsigned int ccols = ncols - titleLen - 1; + unsigned int doffset = (menu->cursesCursor < ccols ? 0 : menu->cursesCursor - ccols); + + if (doffset > 0) { + /* find offset where we won't break the UTF8 string */ + doffset += _bmUtf8RuneNext(menu->filter, doffset); + doffset -= _bmUtf8RunePrev(menu->filter, doffset); + } + + _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter + doffset : "")); if (menu->title) { curses.attron(COLOR_PAIR(1)); @@ -199,8 +209,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) _bmDrawCursesDrawLine(color, cl++, "%s%s", (highlighted ? ">> " : " "), (items[i]->text ? items[i]->text : "")); } - unsigned int ncols = curses.getmaxx(curses.stdscr) - titleLen - 1; - curses.move(0, titleLen + (ncols < menu->cursesCursor ? ncols : menu->cursesCursor)); + curses.move(0, titleLen + (menu->cursesCursor < ccols ? menu->cursesCursor : ccols)); curses.refresh(); } -- cgit v1.2.3-70-g09d2 From 5251ef6ac03e30abf320886d5efe9644bf424ee2 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 00:12:31 +0300 Subject: Fix bad syntax, and add defgroup for library. --- lib/bemenu.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index ebafffa..f4fc954 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -7,6 +7,13 @@ typedef struct _bmMenu bmMenu; typedef struct _bmItem bmItem; +/** + * @defgroup Library + * @brief Library functions. + * + * Query library version, etc... + */ + /** * @defgroup Menu * @brief Menu container. @@ -22,7 +29,7 @@ typedef struct _bmItem bmItem; */ /** - * @addtogroub Library + * @addtogroup Library * @{ */ /** -- cgit v1.2.3-70-g09d2 From fc6539c1013d8187ed5a0dc3dfce65f3f626bf79 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 01:22:49 +0300 Subject: Buffer as format is dangerous, this functions seems to be VA one. --- lib/draw/curses.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 9e8502b..f05db01 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -150,7 +150,7 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) if (pair > 0) curses.attron(COLOR_PAIR(pair)); - curses.mvprintw(y, 0, buffer); + curses.mvprintw(y, 0, "%s", buffer); if (pair > 0) curses.attroff(COLOR_PAIR(pair)); -- cgit v1.2.3-70-g09d2 From 60c1ab88cc4ee7b5d4f87c7867059464757666bb Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 01:39:31 +0300 Subject: Store old actions to seperate actions. --- lib/draw/curses.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index f05db01..3b160a9 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -54,7 +54,8 @@ static const char *TTY = "/dev/tty"; * Dynamically loaded curses API. */ static struct curses { - struct sigaction action; + struct sigaction abrtAction; + struct sigaction segvAction; void *handle; WINDOW *stdscr; WINDOW* (*initscr)(void); @@ -342,8 +343,8 @@ static void _bmDrawCursesFree(void) if (curses.handle) dlclose(curses.handle); - sigaction(SIGABRT, &curses.action, NULL); - sigaction(SIGSEGV, &curses.action, NULL); + sigaction(SIGABRT, &curses.abrtAction, NULL); + sigaction(SIGSEGV, &curses.segvAction, NULL); memset(&curses, 0, sizeof(curses)); } @@ -407,8 +408,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) struct sigaction action; memset(&action, 0, sizeof(struct sigaction)); action.sa_handler = _bmDrawCursesCrashHandler; - sigaction(SIGABRT, &action, &curses.action); - sigaction(SIGSEGV, &action, &curses.action); + sigaction(SIGABRT, &action, &curses.abrtAction); + sigaction(SIGSEGV, &action, &curses.segvAction); return 1; function_pointer_exception: -- cgit v1.2.3-70-g09d2 From 9c0b25f6a9f7e0a0c2f8b6a4608aab68e5e6a909 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 02:47:54 +0300 Subject: Enable noecho and raw mode. --- lib/draw/curses.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 3b160a9..7d91d36 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -72,6 +72,8 @@ static struct curses { int (*getmaxx)(WINDOW *win); int (*getmaxy)(WINDOW *win); int (*keypad)(WINDOW *win, bool bf); + int (*noecho)(void); + int (*raw)(void); int *ESCDELAY; int oldStdin; int oldStdout; @@ -173,6 +175,8 @@ static void _bmDrawCursesRender(const bmMenu *menu) *curses.ESCDELAY = 25; curses.keypad(curses.stdscr, true); + curses.noecho(); + curses.raw(); curses.start_color(); curses.init_pair(1, COLOR_BLACK, COLOR_RED); @@ -395,6 +399,10 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(keypad)) 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; -- cgit v1.2.3-70-g09d2 From c2bed7689d1d97e7bfc0ab52e028e290ee0b3f0e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 03:00:34 +0300 Subject: Enable cursor (hidden when launched by vim) --- lib/draw/curses.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 7d91d36..0304dd5 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -72,6 +72,7 @@ static struct curses { int (*getmaxx)(WINDOW *win); int (*getmaxy)(WINDOW *win); int (*keypad)(WINDOW *win, bool bf); + int (*curs_set)(int visibility); int (*noecho)(void); int (*raw)(void); int *ESCDELAY; @@ -175,6 +176,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) *curses.ESCDELAY = 25; curses.keypad(curses.stdscr, true); + curses.curs_set(1); curses.noecho(); curses.raw(); @@ -399,6 +401,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(keypad)) goto function_pointer_exception; + if (!bmLoadFunction(curs_set)) + goto function_pointer_exception; if (!bmLoadFunction(noecho)) goto function_pointer_exception; if (!bmLoadFunction(raw)) -- cgit v1.2.3-70-g09d2 From 66a6f08c8a9a90fd8c176a4c6b1f5aa6094233c4 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 03:23:11 +0300 Subject: vsnprintf is not on same headers on darwin. --- lib/draw/curses.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 0304dd5..6ebfb45 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -1,5 +1,11 @@ #include "../internal.h" +#if __APPLE__ +# define _C99_SOURCE +# include /* vsnprintf */ +# undef _C99_SOURCE +#endif + #define _XOPEN_SOURCE 500 #include /* sigaction */ #include /* vsnprintf */ -- cgit v1.2.3-70-g09d2 From 850a8335d5c35cc81ba576cd4636460ea1684223 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sun, 13 Apr 2014 04:16:23 +0300 Subject: Flush input buffer after showing menu first time. --- lib/draw/curses.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 6ebfb45..9a123df 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -79,6 +79,7 @@ static struct curses { 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; @@ -181,6 +182,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) return; *curses.ESCDELAY = 25; + curses.flushinp(); curses.keypad(curses.stdscr, true); curses.curs_set(1); curses.noecho(); @@ -409,6 +411,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) 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)) -- cgit v1.2.3-70-g09d2 From 0f34b70c90afb487541448ae88ca99d7186e8150 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 18:08:13 +0300 Subject: Move this check before allocation. --- lib/util.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/util.c b/lib/util.c index fa85431..539afa2 100644 --- a/lib/util.c +++ b/lib/util.c @@ -254,6 +254,9 @@ size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, if (outRuneWidth) *outRuneWidth = 0; + if (u8len == 1 && !isprint(*rune)) + return 0; + size_t len = (*inOutString ? strlen(*inOutString) : 0); if (!*inOutString && !(*inOutString = calloc(1, (*inOutBufSize = u8len + 1)))) return 0; @@ -273,9 +276,6 @@ size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, *inOutBufSize *= 2; } - if (u8len == 1 && !isprint(*rune)) - return 0; - char *str = *inOutString + start; memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); -- cgit v1.2.3-70-g09d2 From f410a5e391deccc10838b363955fe823a336bc82 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 18:10:01 +0300 Subject: Make sure the text is null terminated. --- lib/util.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/util.c b/lib/util.c index 539afa2..2ace7f7 100644 --- a/lib/util.c +++ b/lib/util.c @@ -279,6 +279,7 @@ size_t _bmUtf8RuneInsert(char **inOutString, size_t *inOutBufSize, size_t start, char *str = *inOutString + start; memmove(str + u8len, str, len - start); memcpy(str, rune, u8len); + (*inOutString)[len + u8len] = 0; if (outRuneWidth) *outRuneWidth = _bmUtf8RuneWidth(rune, u8len); -- cgit v1.2.3-70-g09d2 From 066c5c2e61d1b91f04564115ec061968a0716533 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:00:03 +0300 Subject: Make sure text fits tightly terminal + simplified logic --- lib/draw/curses.c | 53 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 9a123df..5cd1335 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -87,12 +87,12 @@ static struct curses { int oldStdout; } curses; -static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) +static int _bmDrawCursesResizeBuffer(char **buffer, size_t *osize, size_t nsize) { assert(buffer); - assert(nsize); + assert(osize); - if (nsize == 0 || nsize < osize) + if (nsize == 0 || nsize <= *osize) return 0; void *tmp; @@ -101,14 +101,13 @@ static int _bmDrawCursesResizeBuffer(char **buffer, size_t osize, size_t nsize) return 0; if (*buffer) { - memcpy(tmp, *buffer, osize); + memcpy(tmp, *buffer, *osize); free(*buffer); } } *buffer = tmp; - memset(*buffer + osize, ' ', (nsize - osize)); - (*buffer)[nsize - 1] = 0; + *osize = nsize; return 1; } @@ -117,32 +116,27 @@ __attribute__((format(printf, 3, 4))) #endif static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) { - static int blen = 0; + static size_t blen = 0; static char *buffer = NULL; - int ncols = curses.getmaxx(curses.stdscr); + size_t ncols = curses.getmaxx(curses.stdscr); if (ncols <= 0) return; va_list args; va_start(args, format); - int nlen = vsnprintf(NULL, 0, format, args) + 1; - if (nlen < ncols) - nlen = ncols; + size_t nlen = vsnprintf(NULL, 0, format, args); va_end(args); - if ((!buffer || nlen > blen) && !_bmDrawCursesResizeBuffer(&buffer, blen, nlen)) + if ((!buffer || nlen > blen) && !_bmDrawCursesResizeBuffer(&buffer, &blen, nlen + 1)) return; - blen = nlen; va_start(args, format); - int slen = vsnprintf(buffer, blen - 1, format, args); - memset(buffer + slen, ' ', (blen - slen)); - buffer[blen - 1] = 0; + vsnprintf(buffer, blen - 1, format, args); va_end(args); - int dw = 0, i = 0; - while (dw < ncols && i < blen) { + 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); @@ -150,12 +144,27 @@ static void _bmDrawCursesDrawLine(int pair, int y, const char *format, ...) } if (dw < ncols) { - if (!_bmDrawCursesResizeBuffer(&buffer, blen - 1, blen + (ncols - dw))) - return; + /* 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) { - int cc = dw - (dw - ncols); + /* 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[i - (dw - ncols) + (ncols - cc) + 1] = 0; + buffer[offset] = 0; } if (pair > 0) -- cgit v1.2.3-70-g09d2 From 5b873de2ded661416d64dc589c1d10bde2c5dfc0 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:00:15 +0300 Subject: Better input scroll logic. --- lib/draw/curses.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 5cd1335..4ae63d7 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -208,12 +208,12 @@ static void _bmDrawCursesRender(const bmMenu *menu) unsigned int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); unsigned int ncols = curses.getmaxx(curses.stdscr); unsigned int ccols = ncols - titleLen - 1; - unsigned int doffset = (menu->cursesCursor < ccols ? 0 : menu->cursesCursor - ccols); + unsigned int dcols = 0, doffset = menu->cursor; - if (doffset > 0) { - /* find offset where we won't break the UTF8 string */ - doffset += _bmUtf8RuneNext(menu->filter, doffset); - doffset -= _bmUtf8RunePrev(menu->filter, doffset); + 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 : "")); -- cgit v1.2.3-70-g09d2 From 9df8716cb8787110aedf5d9ad9c27b4eb3bade39 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:11:26 +0300 Subject: Handle window resize. --- lib/draw/curses.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 4ae63d7..5673c90 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -62,6 +62,7 @@ static const char *TTY = "/dev/tty"; static struct curses { struct sigaction abrtAction; struct sigaction segvAction; + struct sigaction winchAction; void *handle; WINDOW *stdscr; WINDOW* (*initscr)(void); @@ -273,6 +274,11 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) curses.get_wch((wint_t*)unicode); switch (*unicode) { +#if KEY_RESIZE + case KEY_RESIZE: + return BM_KEY_NONE; +#endif + case 16: /* C-p */ case KEY_UP: return BM_KEY_UP; @@ -335,7 +341,6 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) case 9: /* Tab */ return BM_KEY_TAB; - case 0: /* C-Space */ case 18: /* C-r */ return BM_KEY_CONTROL_RETURN; @@ -368,6 +373,7 @@ static void _bmDrawCursesFree(void) sigaction(SIGABRT, &curses.abrtAction, NULL); sigaction(SIGSEGV, &curses.segvAction, NULL); + sigaction(SIGWINCH, &curses.winchAction, NULL); memset(&curses, 0, sizeof(curses)); } @@ -377,6 +383,16 @@ static void _bmDrawCursesCrashHandler(int 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)); @@ -441,6 +457,9 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) 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: -- cgit v1.2.3-70-g09d2 From bcda5404011714622ec22a37c4b7837defe4377e Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:14:30 +0300 Subject: Hide title if more than NCOLS --- lib/draw/curses.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 5673c90..2cc84af 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -206,8 +206,12 @@ static void _bmDrawCursesRender(const bmMenu *menu) const unsigned int lines = curses.getmaxy(curses.stdscr); curses.erase(); - unsigned int titleLen = (menu->title ? strlen(menu->title) + 1 : 0); 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; @@ -219,7 +223,7 @@ static void _bmDrawCursesRender(const bmMenu *menu) _bmDrawCursesDrawLine(0, 0, "%*s%s", titleLen, "", (menu->filter ? menu->filter + doffset : "")); - if (menu->title) { + if (menu->title && titleLen > 0) { curses.attron(COLOR_PAIR(1)); curses.mvprintw(0, 0, menu->title); curses.attroff(COLOR_PAIR(1)); -- cgit v1.2.3-70-g09d2 From 4f72d188ada1e6ad725dfa9927112ec90c3ddf80 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:25:04 +0300 Subject: Fix documentation. --- lib/bemenu.h | 32 ++++++++++++++++---------------- lib/library.c | 7 +++++++ 2 files changed, 23 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/bemenu.h b/lib/bemenu.h index f4fc954..172c6ed 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -170,6 +170,22 @@ void bmMenuSetUserdata(bmMenu *menu, void *userdata); */ void* bmMenuGetUserdata(bmMenu *menu); +/** + * 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); + +/** + * 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); + /** * Set active filter mode to bmMenu instance. * @@ -429,22 +445,6 @@ void bmItemSetUserdata(bmItem *item, void *userdata); */ void* bmItemGetUserdata(bmItem *item); -/** - * 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); - -/** - * 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); - /** * Set text to bmItem instance. * diff --git a/lib/library.c b/lib/library.c index 69e898b..f432eda 100644 --- a/lib/library.c +++ b/lib/library.c @@ -1,5 +1,12 @@ #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) { return BM_VERSION; -- cgit v1.2.3-70-g09d2 From 617c4ab827d9c71a122034594bb1080607f50398 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:25:16 +0300 Subject: Add selection wrapping (-w). --- client/client.c | 3 ++- lib/bemenu.h | 16 ++++++++++++++++ lib/internal.h | 5 +++++ lib/menu.c | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 55 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/client/client.c b/client/client.c index 0e6bf15..7df00bb 100644 --- a/client/client.c +++ b/client/client.c @@ -91,7 +91,7 @@ static void parseArgs(int *argc, char **argv[]) * or parse them before running getopt.. */ for (;;) { - int opt = getopt_long(*argc, *argv, "hviw:l:I:p:Ibf:m", opts, NULL); + int opt = getopt_long(*argc, *argv, "hviwl:I:p:I:bfm:", opts, NULL); if (opt < 0) break; @@ -190,6 +190,7 @@ int main(int argc, char **argv) bmMenuSetTitle(menu, client.title); bmMenuSetFilterMode(menu, client.filterMode); + bmMenuSetWrap(menu, client.wrap); readItemsToMenuFromStdin(menu); diff --git a/lib/bemenu.h b/lib/bemenu.h index 172c6ed..5457cca 100644 --- a/lib/bemenu.h +++ b/lib/bemenu.h @@ -202,6 +202,22 @@ void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode); */ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu); +/** + * 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); + +/** + * Get selection wrapping state. + * + * @param menu bmMenu instance where to get selection wrapping state. + * @return int for wrap state. + */ +int bmMenuGetWrap(const bmMenu *menu); + /** * Set title to bmMenu instance. * diff --git a/lib/internal.h b/lib/internal.h index 4f6afbe..2fc9393 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -141,6 +141,11 @@ struct _bmMenu { * Drawing mode used in menu instance. */ bmDrawMode drawMode; + + /** + * Should selection be wrapped? + */ + char wrap; }; /* draw/curses.c */ diff --git a/lib/menu.c b/lib/menu.c index 5d2b37b..d949430 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -172,6 +172,30 @@ bmFilterMode bmMenuGetFilterMode(const bmMenu *menu) return menu->filterMode; } +/** + * 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) +{ + assert(menu); + menu->wrap = (wrap ? 1 : 0); +} + +/** + * Get selection wrapping state. + * + * @param menu bmMenu instance where to get selection wrapping state. + * @return int for wrap state. + */ +int bmMenuGetWrap(const bmMenu *menu) +{ + assert(menu); + return menu->wrap; +} + /** * Set title to bmMenu instance. * @@ -572,13 +596,19 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode) break; case BM_KEY_UP: - if (menu->index > 0) + if (menu->index > 0) { menu->index--; + } else if (menu->wrap) { + menu->index = itemsCount - 1; + } break; case BM_KEY_DOWN: - if (menu->index < itemsCount - 1) + if (menu->index < itemsCount - 1) { menu->index++; + } else if (menu->wrap) { + menu->index = 0; + } break; case BM_KEY_PAGE_UP: -- cgit v1.2.3-70-g09d2 From 7533cebfe531f2264b10903a5d1b8a0d60b9fcae Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 19:33:50 +0300 Subject: Tell curses to use terminal default colors --- lib/draw/curses.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 2cc84af..f8137b8 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -76,6 +76,7 @@ static struct curses { 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); @@ -199,8 +200,9 @@ static void _bmDrawCursesRender(const bmMenu *menu) curses.raw(); curses.start_color(); + curses.use_default_colors(); curses.init_pair(1, COLOR_BLACK, COLOR_RED); - curses.init_pair(2, COLOR_RED, COLOR_BLACK); + curses.init_pair(2, COLOR_RED, -1); } const unsigned int lines = curses.getmaxy(curses.stdscr); @@ -432,6 +434,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(start_color)) goto function_pointer_exception; + if (!bmLoadFunction(use_default_colors)) + goto function_pointer_exception; if (!bmLoadFunction(getmaxx)) goto function_pointer_exception; if (!bmLoadFunction(getmaxy)) -- cgit v1.2.3-70-g09d2 From 5556779dd2b6ac48ef7e137ca0da3ac9190238bf Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 14 Apr 2014 21:18:50 +0300 Subject: Consistency. --- lib/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/menu.c b/lib/menu.c index d949430..b7ab021 100644 --- a/lib/menu.c +++ b/lib/menu.c @@ -45,7 +45,7 @@ bmMenu* bmMenuNew(bmDrawMode drawMode) status = _bmDrawCursesInit(&menu->renderApi); break; - default:break; + default: break; } if (status == 0) { -- cgit v1.2.3-70-g09d2 From cf27013c1d1790e0d5239aa6fc34999e0f173f30 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:06:51 +0300 Subject: Print the missing function and from what library in case of exception. --- lib/draw/curses.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index f8137b8..afe0a98 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -410,7 +410,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) if (!curses.handle) return 0; -#define bmLoadFunction(x) (curses.x = dlsym(curses.handle, #x)) + char *func = NULL; +#define bmLoadFunction(x) (curses.x = dlsym(curses.handle, (func = #x))) if (!bmLoadFunction(initscr)) goto function_pointer_exception; @@ -471,6 +472,7 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) return 1; function_pointer_exception: + fprintf(stderr, "-!- Could not load function '%s' from '%s'\n", func, DL_PATH[i]); _bmDrawCursesFree(); return 0; } -- cgit v1.2.3-70-g09d2 From f77ae857b181ace139cc702f2a997db6ade86b8c Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:07:47 +0300 Subject: It's better to guard whole function. --- lib/draw/curses.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index afe0a98..0f6fdd2 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -252,6 +252,9 @@ static unsigned int _bmDrawCursesDisplayedCount(const bmMenu *menu) static void _bmDrawCursesEndWin(void) { + if (!curses.stdscr) + return; + freopen(TTY, "w", stdout); if (curses.refresh) @@ -260,12 +263,10 @@ static void _bmDrawCursesEndWin(void) if (curses.endwin) curses.endwin(); - if (curses.stdscr) { - dup2(curses.oldStdin, STDIN_FILENO); - dup2(curses.oldStdout, STDOUT_FILENO); - close(curses.oldStdin); - close(curses.oldStdout); - } + dup2(curses.oldStdin, STDIN_FILENO); + dup2(curses.oldStdout, STDOUT_FILENO); + close(curses.oldStdin); + close(curses.oldStdout); curses.stdscr = NULL; } -- cgit v1.2.3-70-g09d2 From 88590ddcfefdb94f2900282ec8b7a9573b53d37f Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:14:42 +0300 Subject: Don't use the enumerator variable. --- lib/draw/curses.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 0f6fdd2..82fd194 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -403,15 +403,15 @@ static void _bmDrawCursesResizeHandler(int sig) 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(DL_PATH[i], RTLD_LAZY); + curses.handle = dlopen((lib = DL_PATH[i]), RTLD_LAZY); if (!curses.handle) return 0; - char *func = NULL; #define bmLoadFunction(x) (curses.x = dlsym(curses.handle, (func = #x))) if (!bmLoadFunction(initscr)) @@ -473,7 +473,7 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) return 1; function_pointer_exception: - fprintf(stderr, "-!- Could not load function '%s' from '%s'\n", func, DL_PATH[i]); + fprintf(stderr, "-!- Could not load function '%s' from '%s'\n", func, lib); _bmDrawCursesFree(); return 0; } -- cgit v1.2.3-70-g09d2 From 8e415197d248560b359bace7ef8f7ffe0f445099 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:31:10 +0300 Subject: This is not used --- lib/draw/curses.c | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index 82fd194..c4e323a 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -54,7 +54,6 @@ static const char *TTY = "/dev/tty"; #undef attron #undef getmaxx #undef getmaxy -#undef timeout /** * Dynamically loaded curses API. -- cgit v1.2.3-70-g09d2 From 953c61f4ad12d2a711d0c91e24e37cae871dcdc3 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Tue, 15 Apr 2014 20:34:21 +0300 Subject: Support older ncurses. --- lib/draw/curses.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/draw/curses.c b/lib/draw/curses.c index c4e323a..a1f23ef 100644 --- a/lib/draw/curses.c +++ b/lib/draw/curses.c @@ -42,9 +42,16 @@ static const char *TTY = "/dev/tty"; }; #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 @@ -68,6 +75,7 @@ static struct curses { 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); @@ -278,7 +286,11 @@ static bmKey _bmDrawCursesGetKey(unsigned int *unicode) if (!curses.stdscr) return BM_KEY_NONE; - curses.get_wch((wint_t*)unicode); + 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: @@ -419,7 +431,7 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(refresh)) goto function_pointer_exception; - if (!bmLoadFunction(get_wch)) + if (!bmLoadFunction(get_wch) && !bmLoadFunction(getch)) goto function_pointer_exception; if (!bmLoadFunction(erase)) goto function_pointer_exception; @@ -437,10 +449,6 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) goto function_pointer_exception; if (!bmLoadFunction(use_default_colors)) goto function_pointer_exception; - if (!bmLoadFunction(getmaxx)) - goto function_pointer_exception; - if (!bmLoadFunction(getmaxy)) - goto function_pointer_exception; if (!bmLoadFunction(keypad)) goto function_pointer_exception; if (!bmLoadFunction(curs_set)) @@ -454,6 +462,16 @@ int _bmDrawCursesInit(struct _bmRenderApi *api) 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; -- cgit v1.2.3-70-g09d2 From 4ce638eb3a5bdefc0301ab39331ba7b059904406 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Wed, 14 May 2014 20:50:32 +0300 Subject: Length check is fragile and breaking behaviour. --- lib/util.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/util.c b/lib/util.c index 2ace7f7..a47ff8d 100644 --- a/lib/util.c +++ b/lib/util.c @@ -57,12 +57,7 @@ size_t _bmStripToken(char *string, const char *token, size_t *outNext) */ int _bmStrupcmp(const char *hay, const char *needle) { - size_t len, len2; - - if ((len = strlen(hay)) != (len2 = strlen(needle))) - return hay[len] - needle[len2]; - - return _bmStrnupcmp(hay, needle, len); + return _bmStrnupcmp(hay, needle, strlen(hay)); } /** -- cgit v1.2.3-70-g09d2 From 536eee6d0bf8eb99b6566f7f4fd646b3cd76f532 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Thu, 21 Aug 2014 01:45:52 +0300 Subject: Skip leading whitespace. --- lib/filter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/filter.c b/lib/filter.c index ad1b937..04733c5 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -54,9 +54,12 @@ static char* _bmFilterTokenize(bmMenu *menu, char ***outTokv, unsigned int *outT if (!(buffer = _bmStrdup(menu->filter))) goto fail; + char *s; + for (s = buffer; *s && *s == ' '; ++s); + + char **tmp = NULL; size_t pos = 0, next; unsigned int tokc = 0, tokn = 0; - char *s = buffer, **tmp = NULL; for (; (pos = _bmStripToken(s, " ", &next)) > 0; tokv = tmp) { if (++tokc > tokn && !(tmp = realloc(tokv, ++tokn * sizeof(char*)))) goto fail; -- cgit v1.2.3-70-g09d2