summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/CMakeLists.txt10
-rw-r--r--client/client.c281
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 :*/