diff --git a/docs/wmenu.1.scd b/docs/wmenu.1.scd index 4519e8b..94eadbb 100644 --- a/docs/wmenu.1.scd +++ b/docs/wmenu.1.scd @@ -9,6 +9,8 @@ wmenu - dynamic menu for Wayland *wmenu* [-biPv] \ [-f _font_] \ [-l _lines_] \ + [-g _height_] \ + [-w _width_] \ [-o _output_] \ [-p _prompt_] \ [-N _color_] [-n _color_] \ @@ -24,8 +26,8 @@ items from stdin. When the user selects an item and presses Return, their choice is printed to stdout and wmenu terminates. Entering text will narrow the items to those matching the tokens in the input. -*wmenu-run* is a special invocation of wmenu which lists programs in the user's -$PATH and runs the result. +*wmenu-run* is a special script which lists programs in the user's +$PATH and runs the result with the main *wmenu* executable. # OPTIONS @@ -49,6 +51,12 @@ $PATH and runs the result. *-l* _lines_ wmenu lists items vertically, with the given number of lines. +*-g* _height_ + this value will be added to the height of each line. + +*-w* _width_ + the width of the window will be constrained to this value. + *-o* _output_ wmenu is displayed on the output with the given name. diff --git a/install-wmenu-run.sh b/install-wmenu-run.sh new file mode 100755 index 0000000..f5e61c1 --- /dev/null +++ b/install-wmenu-run.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +cp ../wmenu-run /usr/local/bin/ diff --git a/menu.c b/menu.c index 207d71c..3709fbf 100644 --- a/menu.c +++ b/menu.c @@ -27,6 +27,7 @@ struct menu *menu_create(menu_callback callback) { struct menu *menu = calloc(1, sizeof(struct menu)); menu->strncmp = strncmp; menu->font = "monospace 10"; + menu->line_height = get_font_height(menu->font) + 2; menu->normalbg = 0x222222ff; menu->normalfg = 0xbbbbbbff; menu->promptbg = 0x005577ff; @@ -85,11 +86,14 @@ static bool parse_color(const char *color, uint32_t *result) { // Parse menu options from command line arguments. void menu_getopts(struct menu *menu, int argc, char *argv[]) { const char *usage = - "Usage: wmenu [-biPv] [-f font] [-l lines] [-o output] [-p prompt]\n" - "\t[-N color] [-n color] [-M color] [-m color] [-S color] [-s color]\n"; + "Usage: wmenu [-biPv] [-f font] [-l lines] [-g height] [-w width] [-o output]\n" + "\t[-p prompt] [-N color] [-n color] [-M color] [-m color] [-S color] [-s color]\n"; + menu->customwidth = 0; + menu->customheight = 0; + int opt; - while ((opt = getopt(argc, argv, "bhiPvf:l:o:p:N:n:M:m:S:s:")) != -1) { + while ((opt = getopt(argc, argv, "bhiPvf:l:g:w:o:p:N:n:M:m:S:s:")) != -1) { switch (opt) { case 'b': menu->bottom = true; @@ -109,6 +113,13 @@ void menu_getopts(struct menu *menu, int argc, char *argv[]) { case 'l': menu->lines = atoi(optarg); break; + case 'g': + menu->customheight = atoi(optarg); + menu->line_height = get_font_height(menu->font) + menu->customheight; + break; + case 'w': + menu->customwidth = atoi(optarg); + break; case 'o': menu->output_name = optarg; break; @@ -156,13 +167,11 @@ void menu_getopts(struct menu *menu, int argc, char *argv[]) { exit(EXIT_FAILURE); } - int height = get_font_height(menu->font); - menu->line_height = height + 2; menu->height = menu->line_height; if (menu->lines > 0) { menu->height += menu->height * menu->lines; } - menu->padding = height / 2; + menu->padding = get_font_height(menu->font) / 2; } // Add an item to the menu. diff --git a/menu.h b/menu.h index fdd9ad2..382961e 100644 --- a/menu.h +++ b/menu.h @@ -59,6 +59,8 @@ struct menu { int width; int height; int line_height; + int customwidth; + int customheight; int padding; int inputw; int promptw; diff --git a/meson.build b/meson.build index a3b507d..54f6d3e 100644 --- a/meson.build +++ b/meson.build @@ -21,12 +21,12 @@ add_project_arguments(cc.get_supported_arguments([ '-Wvla', ]), language : 'c') -cairo = dependency('cairo') -pango = dependency('pango') -pangocairo = dependency('pangocairo') -wayland_client = dependency('wayland-client') -wayland_protos = dependency('wayland-protocols') -xkbcommon = dependency('xkbcommon') +cairo = dependency('cairo') +pango = dependency('pango') +pangocairo = dependency('pangocairo') +wayland_client = dependency('wayland-client') +wayland_protos = dependency('wayland-protocols') +xkbcommon = dependency('xkbcommon') rt = cc.find_library('rt') @@ -56,25 +56,4 @@ executable( install: true, ) -executable( - 'wmenu-run', - files( - 'menu.c', - 'pango.c', - 'pool-buffer.c', - 'render.c', - 'wayland.c', - 'wmenu-run.c', - ), - dependencies: [ - cairo, - client_protos, - pango, - pangocairo, - rt, - wayland_client, - wayland_protos, - xkbcommon, - ], - install: true, -) +meson.add_install_script('install-wmenu-run.sh') diff --git a/render.c b/render.c index e86d93b..a9055a1 100644 --- a/render.c +++ b/render.c @@ -106,7 +106,7 @@ static void render_input(struct menu *menu, cairo_t *cairo) { // Renders a cursor for the input field. static void render_cursor(struct menu *menu, cairo_t *cairo) { const int cursor_width = 2; - const int cursor_margin = 2; + const int cursor_margin = menu->line_height / 5; int cursor_pos = menu->promptw + menu->padding + text_width(cairo, menu->font, menu->input) - text_width(cairo, menu->font, &menu->input[menu->cursor]) @@ -144,11 +144,11 @@ static void render_horizontal_page(struct menu *menu, cairo_t *cairo, struct pag // Draw left and right scroll indicators if necessary if (page->prev) { - cairo_move_to(cairo, menu->promptw + menu->inputw + menu->padding, 0); + cairo_move_to(cairo, menu->promptw + menu->inputw + menu->padding, menu->customheight / 2); pango_printf(cairo, menu->font, 1, "<"); } if (page->next) { - cairo_move_to(cairo, menu->width - menu->right_arrow + menu->padding, 0); + cairo_move_to(cairo, menu->width - menu->right_arrow + menu->padding, menu->customheight / 2); pango_printf(cairo, menu->font, 1, ">"); } } diff --git a/wayland.c b/wayland.c index 823ced1..617f392 100644 --- a/wayland.c +++ b/wayland.c @@ -220,7 +220,11 @@ static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height) { struct wl_context *context = data; - context->menu->width = width; + if (context->menu->customwidth > 0) { + context->menu->width = context->menu->customwidth; + } else { + context->menu->width = width; + } context->menu->height = height; zwlr_layer_surface_v1_ack_configure(surface, serial); } diff --git a/wmenu-run b/wmenu-run new file mode 100755 index 0000000..6a9dcbd --- /dev/null +++ b/wmenu-run @@ -0,0 +1,2 @@ +#!/bin/sh +compgen -c | sort | wmenu "$@" | sh diff --git a/wmenu-run.c b/wmenu-run.c deleted file mode 100644 index 1b7b8c1..0000000 --- a/wmenu-run.c +++ /dev/null @@ -1,78 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include - -#include "menu.h" -#include "wayland.h" -#include "xdg-activation-v1-client-protocol.h" - -static void read_items(struct menu *menu) { - char *path = strdup(getenv("PATH")); - for (char *p = strtok(path, ":"); p != NULL; p = strtok(NULL, ":")) { - DIR *dir = opendir(p); - if (dir == NULL) { - continue; - } - for (struct dirent *ent = readdir(dir); ent != NULL; ent = readdir(dir)) { - if (ent->d_name[0] == '.') { - continue; - } - menu_add_item(menu, strdup(ent->d_name)); - } - closedir(dir); - } - menu_sort_and_deduplicate(menu); - free(path); -} - -struct command { - struct menu *menu; - char *text; - bool exit; -}; - -static void activation_token_done(void *data, struct xdg_activation_token_v1 *activation_token, - const char *token) { - struct command *cmd = data; - xdg_activation_token_v1_destroy(activation_token); - - int pid = fork(); - if (pid == 0) { - setenv("XDG_ACTIVATION_TOKEN", token, true); - char *argv[] = {"/bin/sh", "-c", cmd->text, NULL}; - execvp(argv[0], (char**)argv); - } else { - if (cmd->exit) { - cmd->menu->exit = true; - } - } -} - -static const struct xdg_activation_token_v1_listener activation_token_listener = { - .done = activation_token_done, -}; - -static void exec_item(struct menu *menu, char *text, bool exit) { - struct command *cmd = calloc(1, sizeof(struct command)); - cmd->menu = menu; - cmd->text = strdup(text); - cmd->exit = exit; - - struct xdg_activation_v1 *activation = context_get_xdg_activation(menu->context); - struct xdg_activation_token_v1 *activation_token = xdg_activation_v1_get_activation_token(activation); - xdg_activation_token_v1_set_surface(activation_token, context_get_surface(menu->context)); - xdg_activation_token_v1_add_listener(activation_token, &activation_token_listener, cmd); - xdg_activation_token_v1_commit(activation_token); -} - -int main(int argc, char *argv[]) { - struct menu *menu = menu_create(exec_item); - menu_getopts(menu, argc, argv); - read_items(menu); - int status = menu_run(menu); - menu_destroy(menu); - return status; -}