forked from RepoMirrors/bemenu
270 lines
7.8 KiB
C
270 lines
7.8 KiB
C
#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);
|
|
}
|
|
|
|
int main(int argc, char **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 : ""));
|
|
}
|
|
|
|
if (!count && bmMenuGetFilter(menu))
|
|
printf("%s\n", bmMenuGetFilter(menu));
|
|
}
|
|
|
|
bmMenuFree(menu);
|
|
return (status == BM_RUN_RESULT_SELECTED ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
}
|
|
|
|
/* vim: set ts=8 sw=4 tw=0 :*/
|