bemenu/client/common/common.c
Alyssa Ross 93cde4831b
bemenu: add --line-height / -H option
Text is displayed vertically centered in a line. If unspecified, or 0,
the previous behaviour of making the height the size of the text, plus
two pixels on either side, is used, so there will be no change in
behaviour if this option is not used.

Fixes https://github.com/Cloudef/bemenu/issues/44.
2019-05-31 23:20:54 +00:00

327 lines
12 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"
" -H, --line-height defines the height to make each menu line (0 = default height). (wx)\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' },
{ "line-height", required_argument, 0, 'H' },
{ "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:H: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 'H':
client->line_height = strtol(optarg, NULL, 10);
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_line_height(menu, client->line_height);
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 :*/