forked from RepoMirrors/bemenu
321 lines
11 KiB
C
321 lines
11 KiB
C
#include "common.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <assert.h>
|
|
|
|
static void
|
|
disco_trap(int sig)
|
|
{
|
|
(void)sig;
|
|
fprintf(stderr, "\e[?25h");
|
|
fflush(stderr);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static void
|
|
disco(void)
|
|
{
|
|
struct sigaction action;
|
|
memset(&action, 0, sizeof(struct sigaction));
|
|
action.sa_handler = disco_trap;
|
|
sigaction(SIGABRT, &action, NULL);
|
|
sigaction(SIGSEGV, &action, NULL);
|
|
sigaction(SIGTRAP, &action, NULL);
|
|
sigaction(SIGINT, &action, NULL);
|
|
|
|
uint32_t cc, c = 80;
|
|
fprintf(stderr, "\e[?25l");
|
|
while (1) {
|
|
for (uint32_t i = 1; i < c - 1; ++i) {
|
|
fprintf(stderr, "\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) fprintf(stderr, ((i % 2) ? "^" : "'"));
|
|
fprintf(stderr, "%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) fprintf(stderr, ((i % 2) ? "^" : "'"));
|
|
fflush(stderr);
|
|
usleep(140 * 1000);
|
|
}
|
|
}
|
|
fprintf(stderr, "\e[?25h");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
version(const char *name)
|
|
{
|
|
assert(name);
|
|
char *base = strrchr(name, '/');
|
|
printf("%s v%s\n", (base ? base + 1 : name), bm_version());
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
static void
|
|
usage(FILE *out, const char *name)
|
|
{
|
|
assert(out && 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"
|
|
" -P, --prefix text to show before highlighted item.\n"
|
|
" -I, --index select item at index automatically.\n"
|
|
" --scrollbar display scrollbar. (always, autohide)\n"
|
|
" --ifne only display menu if there are items.\n\n"
|
|
|
|
"Use BEMENU_BACKEND env variable to force backend:\n"
|
|
" curses ncurses based terminal backend\n"
|
|
" wayland wayland backend\n"
|
|
" x11 x11 backend\n\n"
|
|
|
|
"If BEMENU_BACKEND is empty, one of the GUI backends is selected automatically.\n\n"
|
|
|
|
"Backend specific options\n"
|
|
" c = ncurses, w == wayland, x == x11\n"
|
|
" (...) At end of help indicates the backend support for option.\n\n"
|
|
|
|
" -b, --bottom appears at the bottom of the screen. (wx)\n"
|
|
" -f, --grab show the menu before reading stdin. (wx)\n"
|
|
" -n, --no-overlap adjust geometry to not overlap with panels. (w)\n"
|
|
" -m, --monitor index of monitor where menu will appear. (x)\n"
|
|
" --fn defines the font to be used ('name [size]'). (wx)\n"
|
|
" --tb defines the title background color. (wx)\n"
|
|
" --tf defines the title foreground color. (wx)\n"
|
|
" --fb defines the filter background color. (wx)\n"
|
|
" --ff defines the filter foreground color. (wx)\n"
|
|
" --nb defines the normal background color. (wx)\n"
|
|
" --nf defines the normal foreground color. (wx)\n"
|
|
" --hb defines the highlighted background color. (wx)\n"
|
|
" --hf defines the highlighted foreground color. (wx)\n"
|
|
" --sb defines the selected background color. (wx)\n"
|
|
" --sf defines the selected foreground color. (wx)\n"
|
|
" --scb defines the scrollbar background color. (wx)\n"
|
|
" --scf defines the scrollbar foreground color. (wx)\n", out);
|
|
|
|
exit((out == stderr ? EXIT_FAILURE : EXIT_SUCCESS));
|
|
}
|
|
|
|
void
|
|
parse_args(struct client *client, int *argc, char **argv[])
|
|
{
|
|
assert(client && argc && 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' },
|
|
{ "prefix", required_argument, 0, 'P' },
|
|
{ "scrollbar", required_argument, 0, 0x100 },
|
|
{ "ifne", no_argument, 0, 0x115 },
|
|
|
|
{ "bottom", no_argument, 0, 'b' },
|
|
{ "grab", no_argument, 0, 'f' },
|
|
{ "no-overlap", no_argument, 0, 'n' },
|
|
{ "monitor", required_argument, 0, 'm' },
|
|
{ "fn", required_argument, 0, 0x101 },
|
|
{ "tb", required_argument, 0, 0x102 },
|
|
{ "tf", required_argument, 0, 0x103 },
|
|
{ "fb", required_argument, 0, 0x104 },
|
|
{ "ff", required_argument, 0, 0x105 },
|
|
{ "nb", required_argument, 0, 0x106 },
|
|
{ "nf", required_argument, 0, 0x107 },
|
|
{ "hb", required_argument, 0, 0x108 },
|
|
{ "hf", required_argument, 0, 0x109 },
|
|
{ "sb", required_argument, 0, 0x110 },
|
|
{ "sf", required_argument, 0, 0x111 },
|
|
{ "scb", required_argument, 0, 0x112 },
|
|
{ "scf", required_argument, 0, 0x113 },
|
|
|
|
{ "disco", no_argument, 0, 0x114 },
|
|
{ 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 (;;) {
|
|
int32_t opt = getopt_long(*argc, *argv, "hviwl:I:p:P:I:bfm:n", opts, NULL);
|
|
if (opt < 0)
|
|
break;
|
|
|
|
switch (opt) {
|
|
case 'h':
|
|
usage(stdout, *argv[0]);
|
|
break;
|
|
case 'v':
|
|
version(*argv[0]);
|
|
break;
|
|
|
|
case 'i':
|
|
client->filter_mode = BM_FILTER_MODE_DMENU_CASE_INSENSITIVE;
|
|
break;
|
|
case 'w':
|
|
client->wrap = true;
|
|
break;
|
|
case 'l':
|
|
client->lines = strtol(optarg, NULL, 10);
|
|
break;
|
|
case 'p':
|
|
client->title = optarg;
|
|
break;
|
|
case 'P':
|
|
client->prefix = optarg;
|
|
break;
|
|
case 'I':
|
|
client->selected = strtol(optarg, NULL, 10);
|
|
break;
|
|
case 0x100:
|
|
client->scrollbar = (!strcmp(optarg, "always") ? BM_SCROLLBAR_ALWAYS : (!strcmp(optarg, "autohide") ? BM_SCROLLBAR_AUTOHIDE : BM_SCROLLBAR_NONE));
|
|
break;
|
|
case 0x115:
|
|
client->ifne = true;
|
|
break;
|
|
|
|
case 'b':
|
|
client->bottom = true;
|
|
break;
|
|
case 'f':
|
|
client->grab = true;
|
|
break;
|
|
case 'm':
|
|
client->monitor = strtol(optarg, NULL, 10);
|
|
break;
|
|
case 'n':
|
|
client->no_overlap = true;
|
|
break;
|
|
|
|
case 0x101:
|
|
client->font = optarg;
|
|
break;
|
|
case 0x102:
|
|
client->colors[BM_COLOR_TITLE_BG] = optarg;
|
|
break;
|
|
case 0x103:
|
|
client->colors[BM_COLOR_TITLE_FG] = optarg;
|
|
break;
|
|
case 0x104:
|
|
client->colors[BM_COLOR_FILTER_BG] = optarg;
|
|
break;
|
|
case 0x105:
|
|
client->colors[BM_COLOR_FILTER_FG] = optarg;
|
|
break;
|
|
case 0x106:
|
|
client->colors[BM_COLOR_ITEM_BG] = optarg;
|
|
break;
|
|
case 0x107:
|
|
client->colors[BM_COLOR_ITEM_FG] = optarg;
|
|
break;
|
|
case 0x108:
|
|
client->colors[BM_COLOR_HIGHLIGHTED_BG] = optarg;
|
|
break;
|
|
case 0x109:
|
|
client->colors[BM_COLOR_HIGHLIGHTED_FG] = optarg;
|
|
break;
|
|
case 0x110:
|
|
client->colors[BM_COLOR_SELECTED_BG] = optarg;
|
|
break;
|
|
case 0x111:
|
|
client->colors[BM_COLOR_SELECTED_FG] = optarg;
|
|
break;
|
|
case 0x112:
|
|
client->colors[BM_COLOR_SCROLLBAR_BG] = optarg;
|
|
break;
|
|
case 0x113:
|
|
client->colors[BM_COLOR_SCROLLBAR_FG] = optarg;
|
|
break;
|
|
|
|
case 0x114:
|
|
disco();
|
|
break;
|
|
|
|
case ':':
|
|
case '?':
|
|
fputs("\n", stderr);
|
|
usage(stderr, *argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
*argc -= optind;
|
|
*argv += optind;
|
|
}
|
|
|
|
struct bm_menu*
|
|
menu_with_options(const struct client *client)
|
|
{
|
|
struct bm_menu *menu;
|
|
if (!(menu = bm_menu_new(NULL)))
|
|
return NULL;
|
|
|
|
bm_menu_set_font(menu, client->font);
|
|
bm_menu_set_title(menu, client->title);
|
|
bm_menu_set_prefix(menu, client->prefix);
|
|
bm_menu_set_filter_mode(menu, client->filter_mode);
|
|
bm_menu_set_lines(menu, client->lines);
|
|
bm_menu_set_wrap(menu, client->wrap);
|
|
bm_menu_set_bottom(menu, client->bottom);
|
|
bm_menu_set_monitor(menu, client->monitor);
|
|
bm_menu_set_scrollbar(menu, client->scrollbar);
|
|
|
|
for (uint32_t i = 0; i < BM_COLOR_LAST; ++i)
|
|
bm_menu_set_color(menu, i, client->colors[i]);
|
|
|
|
if (client->grab) {
|
|
bm_menu_set_filter(menu, "Loading...");
|
|
// bm_menu_grab_keyboard(menu, true);
|
|
bm_menu_render(menu);
|
|
bm_menu_set_filter(menu, NULL);
|
|
}
|
|
|
|
return menu;
|
|
}
|
|
|
|
enum bm_run_result
|
|
run_menu(const struct client *client, struct bm_menu *menu, void (*item_cb)(struct bm_item *item, const char *text))
|
|
{
|
|
bm_menu_set_highlighted_index(menu, client->selected);
|
|
bm_menu_grab_keyboard(menu, true);
|
|
bm_menu_set_panel_overlap(menu, !client->no_overlap);
|
|
|
|
if (client->ifne && !bm_menu_get_items(menu, NULL))
|
|
return BM_RUN_RESULT_CANCEL;
|
|
|
|
uint32_t unicode;
|
|
enum bm_key key;
|
|
enum bm_run_result status = BM_RUN_RESULT_RUNNING;
|
|
do {
|
|
bm_menu_render(menu);
|
|
key = bm_menu_poll_key(menu, &unicode);
|
|
} while ((status = bm_menu_run_with_key(menu, key, unicode)) == BM_RUN_RESULT_RUNNING);
|
|
|
|
if (status == BM_RUN_RESULT_SELECTED) {
|
|
uint32_t i, count;
|
|
struct bm_item **items = bm_menu_get_selected_items(menu, &count);
|
|
for (i = 0; i < count; ++i) {
|
|
const char *text = bm_item_get_text(items[i]);
|
|
item_cb(items[i], text);
|
|
}
|
|
|
|
if (!count && bm_menu_get_filter(menu))
|
|
item_cb(NULL, bm_menu_get_filter(menu));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/* vim: set ts=8 sw=4 tw=0 :*/
|