1
0
mirror of https://github.com/Cloudef/bemenu synced 2024-09-24 21:10:46 +02:00
bemenu/client/client.c

227 lines
6.5 KiB
C
Raw Normal View History

2014-03-18 18:35:10 +01:00
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <bemenu.h>
2014-04-12 21:12:44 +02:00
#include <getopt.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 */
};
2014-04-14 19:42:10 +02:00
static void version(const char *name)
2014-04-12 21:12:44 +02:00
{
char *base = strrchr(name, '/');
printf("%s v%s\n", (base ? base + 1 : name), bmVersion());
2014-04-14 19:42:10 +02:00
exit(EXIT_SUCCESS);
2014-04-12 21:12:44 +02:00
}
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"
2014-04-14 18:39:20 +02:00
" -sf, --sf defines the selected foreground color. ()\n", out);
2014-04-12 21:12:44 +02:00
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 },
{ 0, 0, 0, 0 }
};
2014-04-12 21:15:46 +02:00
/* 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.. */
2014-04-12 21:12:44 +02:00
for (;;) {
2014-04-14 18:25:16 +02:00
int opt = getopt_long(*argc, *argv, "hviwl:I:p:I:bfm:", opts, NULL);
2014-04-12 21:12:44 +02:00
if (opt < 0)
break;
switch (opt) {
case 'h':
usage(stdout, *argv[0]);
break;
case 'v':
2014-04-14 19:42:10 +02:00
version(*argv[0]);
2014-04-14 18:39:29 +02:00
break;
2014-04-12 21:12:44 +02:00
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;
2014-04-14 18:39:41 +02:00
case ':':
case '?':
fputs("\n", stderr);
usage(stderr, *argv[0]);
break;
2014-04-12 21:12:44 +02:00
}
}
*argc -= optind;
*argv += optind;
}
2014-03-18 18:35:10 +01:00
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;
2014-04-12 18:59:21 +02:00
size_t pos;
char *s = buffer;
while ((pos = strcspn(s, "\n")) != 0) {
size_t next = pos + (s[pos] != 0);
2014-04-12 18:59:21 +02:00
s[pos] = 0;
bmItem *item = bmItemNew(s);
if (!item)
break;
bmMenuAddItem(menu, item);
s += next;
}
free(buffer);
}
2014-03-18 18:35:10 +01:00
int main(int argc, char **argv)
{
2014-04-12 21:12:44 +02:00
parseArgs(&argc, &argv);
2014-03-18 18:35:10 +01:00
bmMenu *menu = bmMenuNew(BM_DRAW_MODE_CURSES);
if (!menu)
return EXIT_FAILURE;
2014-04-12 21:12:44 +02:00
bmMenuSetTitle(menu, client.title);
bmMenuSetFilterMode(menu, client.filterMode);
2014-04-14 18:25:16 +02:00
bmMenuSetWrap(menu, client.wrap);
2014-04-12 21:12:44 +02:00
readItemsToMenuFromStdin(menu);
2014-04-12 21:12:44 +02:00
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);
2014-04-12 13:15:37 +02:00
for (i = 0; i < count; ++i) {
const char *text = bmItemGetText(items[i]);
printf("%s\n", (text ? text : ""));
}
if (!count && bmMenuGetFilter(menu))
printf("%s\n", bmMenuGetFilter(menu));
}
bmMenuFree(menu);
return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE);
2014-03-18 18:35:10 +01:00
}
/* vim: set ts=8 sw=4 tw=0 :*/