diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/CMakeLists.txt | 10 | ||||
-rw-r--r-- | client/client.c | 281 |
2 files changed, 266 insertions, 25 deletions
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 70ecd4b..6af4e3e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,9 +1,7 @@ -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY bin) - # Sources SET(CLIENT_SOURCE client.c) -SET(CLIENT_INCLUDE) -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) @@ -22,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/client/client.c b/client/client.c index de5468e..54906c4 100644 --- a/client/client.c +++ b/client/client.c @@ -1,30 +1,269 @@ -/** - * @file client.c - * - * Sample client using the libbemenu. - * Also usable as dmenu replacement. - */ - +#define _XOPEN_SOURCE 500 #include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <unistd.h> +#include <signal.h> +#include <getopt.h> +#include <bemenu.h> + +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 discoTrap(int sig) +{ + (void)sig; + printf("\e[?25h\n"); + fflush(stdout); + exit(EXIT_FAILURE); +} + +static void disco(void) +{ + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + action.sa_handler = discoTrap; + sigaction(SIGABRT, &action, NULL); + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGTRAP, &action, NULL); + sigaction(SIGINT, &action, NULL); + + unsigned int i, cc, c = 80; + printf("\e[?25l"); + while (1) { + for (i = 1; i < c - 1; ++i) { + printf("\r %*s%s %s %s ", (i > c / 2 ? c - i : i), " ", ((i % 2) ? "<o/" : "\\o>"), ((i % 4) ? "DISCO" : " "), ((i %2) ? "\\o>" : "<o/")); + for (cc = 0; cc < (i < c / 2 ? c / 2 - i : i - c / 2); ++cc) printf(((i % 2) ? "^" : "'")); + printf("%s %s \r %s %s", ((i % 2) ? "*" : "•"), ((i % 3) ? "\\o" : "<o"), ((i % 3) ? "o/" : "o>"), ((i % 2) ? "*" : "•")); + for (cc = 2; cc < (i > c / 2 ? c - i : i); ++cc) printf(((i % 2) ? "^" : "'")); + fflush(stdout); + usleep(140 * 1000); + } + } + printf("\e[?25h"); + exit(EXIT_SUCCESS); +} + +static void version(const char *name) +{ + char *base = strrchr(name, '/'); + printf("%s v%s\n", (base ? base + 1 : name), bmVersion()); + exit(EXIT_SUCCESS); +} + +static void usage(FILE *out, const char *name) +{ + char *base = strrchr(name, '/'); + fprintf(out, "usage: %s [options]\n", (base ? base + 1 : name)); + fputs("Options\n" + " -h, --help display this help and exit.\n" + " -v, --version display version.\n" + " -i, --ignorecase match items case insensitively.\n" + " -w, --wrap wraps cursor selection.\n" + " -l, --list list items vertically with the given number of lines.\n" + " -p, --prompt defines the prompt text to be displayed.\n" + " -I, --index select item at index automatically.\n\n" + + "Backend specific options\n" + " c = ncurses\n" // x == x11 + " (...) At end of help indicates the backend support for option.\n\n" + + " -b, --bottom appears at the bottom of the screen. ()\n" + " -f, --grab grabs the keyboard before reading stdin. ()\n" + " -m, --monitor index of monitor where menu will appear. ()\n" + " -fn, --fn defines the font to be used. ()\n" + " -nb, --nb defines the normal background color. ()\n" + " -nf, --nf defines the normal foreground color. ()\n" + " -sb, --sb defines the selected background color. ()\n" + " -sf, --sf defines the selected foreground color. ()\n", out); + exit((out == stderr ? EXIT_FAILURE : EXIT_SUCCESS)); +} + +static void parseArgs(int *argc, char **argv[]) +{ + static const struct option opts[] = { + { "help", no_argument, 0, 'h' }, + { "version", no_argument, 0, 'v' }, + + { "ignorecase", no_argument, 0, 'i' }, + { "wrap", no_argument, 0, 'w' }, + { "list", required_argument, 0, 'l' }, + { "prompt", required_argument, 0, 'p' }, + { "index", required_argument, 0, 'I' }, + + { "bottom", no_argument, 0, 'b' }, + { "grab", no_argument, 0, 'f' }, + { "monitor", required_argument, 0, 'm' }, + { "fn", required_argument, 0, 0x100 }, + { "nb", required_argument, 0, 0x101 }, + { "nf", required_argument, 0, 0x102 }, + { "sb", required_argument, 0, 0x103 }, + { "sf", required_argument, 0, 0x104 }, + + { "disco", no_argument, 0, 0x105 }, + { 0, 0, 0, 0 } + }; + + /* TODO: getopt does not support -sf, -sb etc.. + * Either break the interface and make them --sf, --sb (like they are now), + * or parse them before running getopt.. */ + + for (;;) { + int opt = getopt_long(*argc, *argv, "hviwl:I:p:I:bfm:", opts, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'h': + usage(stdout, *argv[0]); + break; + case 'v': + version(*argv[0]); + break; + + case 'i': + client.filterMode = BM_FILTER_MODE_DMENU_CASE_INSENSITIVE; + break; + case 'w': + client.wrap = 1; + break; + case 'l': + client.lines = strtol(optarg, NULL, 10); + break; + case 'p': + client.title = optarg; + break; + case 'I': + client.selected = strtol(optarg, NULL, 10); + break; + + case 'b': + client.bottom = 1; + break; + case 'f': + client.grab = 1; + break; + case 'm': + client.monitor = strtol(optarg, NULL, 10); + break; + + case 0x100: + case 0x101: + case 0x102: + case 0x103: + case 0x104: + break; + + case 0x105: + disco(); + break; + + case ':': + case '?': + fputs("\n", stderr); + usage(stderr, *argv[0]); + break; + } + } + + *argc -= optind; + *argv += optind; +} + +static void readItemsToMenuFromStdin(bmMenu *menu) +{ + assert(menu); + + size_t step = 1024, allocated; + char *buffer; + + if (!(buffer = malloc((allocated = step)))) + return; + + size_t read; + while ((read = fread(buffer + (allocated - step), 1, step, stdin)) == step) { + void *tmp; + if (!(tmp = realloc(buffer, (allocated += step)))) { + free(buffer); + return; + } + buffer = tmp; + } + buffer[allocated - step + read - 1] = 0; + + 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 += next; + } + + free(buffer); +} -/** - * 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; + parseArgs(&argc, &argv); + + bmMenu *menu = bmMenuNew(BM_DRAW_MODE_CURSES); + if (!menu) + return EXIT_FAILURE; + + bmMenuSetTitle(menu, client.title); + bmMenuSetFilterMode(menu, client.filterMode); + bmMenuSetWrap(menu, client.wrap); + + readItemsToMenuFromStdin(menu); + + bmMenuSetHighlightedIndex(menu, client.selected); + + 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) { + unsigned int i, count; + bmItem **items = bmMenuGetSelectedItems(menu, &count); + for (i = 0; i < count; ++i) { + const char *text = bmItemGetText(items[i]); + printf("%s\n", (text ? text : "")); + } - /* - * code goes here - */ + if (!count && bmMenuGetFilter(menu)) + printf("%s\n", bmMenuGetFilter(menu)); + } - return EXIT_SUCCESS; + bmMenuFree(menu); + return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE); } /* vim: set ts=8 sw=4 tw=0 :*/ |