Compare commits

...

13 commits
0.1.8 ... main

Author SHA1 Message Date
00478c32cb chore: build a shared library 2025-01-24 21:29:47 -06:00
adnano
e2542d34ed Render frame on surface enter
This ensures that the menu is rendered with the correct scale.

Fixes #14
2024-12-16 10:56:51 -05:00
M Stoeckl
3ad4b5ca3f Simplify render_menu 2024-11-08 15:28:11 -05:00
adnano
48ec172b4b README: Update meson instructions 2024-11-01 19:35:39 -04:00
M Stoeckl
0947765fc9 Only call render_menu once per frame
An actual surface is not needed to estimate font sizes; a 1x1 image
will do, as long as the cairo context has the same options.
2024-11-01 23:33:53 +00:00
M Stoeckl
260eaba88e Optimize menu sorting
Sorting and deduplicating elements after all items have been registered
improves the time complexity of constructing the item list from O(n^2)
to O(n log n). On a system with about 4000 menu items, this reduces
startup time from about 0.21 seconds to 0.13 seconds.
2024-10-31 09:30:09 -04:00
adnano
12b8f83be4 Display over fullscreen applications 2024-08-03 18:26:59 -04:00
adnano
8b23811263 Version 0.1.9 2024-06-09 20:33:37 -04:00
adnano
7d717b3696 Streamline menu callbacks 2024-06-09 20:30:58 -04:00
NAHTAIV3L
a0df7959f9 Make wmenu-run behave like dmenu_run 2024-06-09 19:02:32 -04:00
adnano
0fa9c35949 Update README.md 2024-05-25 19:10:07 -04:00
adnano
30abca4f30 Don't ignore stdin in password mode
This makes password mode work for wmenu and wmenu-run without special
cases.
2024-05-05 10:13:01 -04:00
adnano
15d7c7bcc2 Revert "Remove wmenu -P flag"
This reverts commit c05ab7520b.
2024-05-04 21:44:59 -04:00
9 changed files with 168 additions and 128 deletions

View file

@ -15,7 +15,7 @@ Dependencies:
- scdoc (optional) - scdoc (optional)
``` ```
$ meson build $ meson setup build
$ ninja -C build $ ninja -C build
# ninja -C build install # ninja -C build install
``` ```
@ -30,14 +30,3 @@ To use wmenu with Sway, you can add the following to your configuration file:
set $menu wmenu-run set $menu wmenu-run
bindsym $mod+d exec $menu bindsym $mod+d exec $menu
``` ```
## Contributing
Send patches and questions to [~adnano/wmenu-devel](https://lists.sr.ht/~adnano/wmenu-devel).
Subscribe to release announcements on [~adnano/wmenu-announce](https://lists.sr.ht/~adnano/wmenu-announce).
## Credits
This project started as a fork of [dmenu-wl](https://github.com/nyyManni/dmenu-wayland).
However, most of the code was rewritten from scratch.

View file

@ -6,7 +6,7 @@ wmenu - dynamic menu for Wayland
# SYNOPSIS # SYNOPSIS
*wmenu* [-biv] \ *wmenu* [-biPv] \
[-f _font_] \ [-f _font_] \
[-l _lines_] \ [-l _lines_] \
[-o _output_] \ [-o _output_] \
@ -35,6 +35,10 @@ $PATH and runs the result.
*-i* *-i*
wmenu matches menu items case insensitively. wmenu matches menu items case insensitively.
*-P*
wmenu will not directly display the keyboard input, but instead replace it
with asterisks.
*-v* *-v*
prints version information to stdout, then exits. prints version information to stdout, then exits.

103
menu.c
View file

@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <poll.h> #include <poll.h>
#include <stdbool.h> #include <stdbool.h>
@ -22,7 +23,7 @@
#include "wayland.h" #include "wayland.h"
// Creates and returns a new menu. // Creates and returns a new menu.
struct menu *menu_create() { struct menu *menu_create(menu_callback callback) {
struct menu *menu = calloc(1, sizeof(struct menu)); struct menu *menu = calloc(1, sizeof(struct menu));
menu->strncmp = strncmp; menu->strncmp = strncmp;
menu->font = "monospace 10"; menu->font = "monospace 10";
@ -32,6 +33,9 @@ struct menu *menu_create() {
menu->promptfg = 0xeeeeeeff; menu->promptfg = 0xeeeeeeff;
menu->selectionbg = 0x005577ff; menu->selectionbg = 0x005577ff;
menu->selectionfg = 0xeeeeeeff; menu->selectionfg = 0xeeeeeeff;
menu->callback = callback;
menu->test_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
menu->test_cairo = cairo_create(menu->test_surface);
return menu; return menu;
} }
@ -44,24 +48,20 @@ static void free_pages(struct menu *menu) {
} }
} }
static void free_item(struct item *item) {
free(item->text);
free(item);
}
static void free_items(struct menu *menu) { static void free_items(struct menu *menu) {
struct item *next = menu->items; for (size_t i = 0; i < menu->item_count; i++) {
while (next) { struct item *item = &menu->items[i];
struct item *item = next; free(item->text);
next = item->next;
free_item(item);
} }
free(menu->items);
} }
// Destroys the menu, freeing memory associated with it. // Destroys the menu, freeing memory associated with it.
void menu_destroy(struct menu *menu) { void menu_destroy(struct menu *menu) {
free_pages(menu); free_pages(menu);
free_items(menu); free_items(menu);
cairo_destroy(menu->test_cairo);
cairo_surface_destroy(menu->test_surface);
free(menu); free(menu);
} }
@ -85,11 +85,11 @@ static bool parse_color(const char *color, uint32_t *result) {
// Parse menu options from command line arguments. // Parse menu options from command line arguments.
void menu_getopts(struct menu *menu, int argc, char *argv[]) { void menu_getopts(struct menu *menu, int argc, char *argv[]) {
const char *usage = const char *usage =
"Usage: wmenu [-biPvx] [-f font] [-l lines] [-o output] [-p prompt]\n" "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"; "\t[-N color] [-n color] [-M color] [-m color] [-S color] [-s color]\n";
int opt; int opt;
while ((opt = getopt(argc, argv, "bhivf:l:o:p:N:n:M:m:S:s:")) != -1) { while ((opt = getopt(argc, argv, "bhiPvf:l:o:p:N:n:M:m:S:s:")) != -1) {
switch (opt) { switch (opt) {
case 'b': case 'b':
menu->bottom = true; menu->bottom = true;
@ -97,6 +97,9 @@ void menu_getopts(struct menu *menu, int argc, char *argv[]) {
case 'i': case 'i':
menu->strncmp = strncasecmp; menu->strncmp = strncasecmp;
break; break;
case 'P':
menu->passwd = true;
break;
case 'v': case 'v':
puts("wmenu " VERSION); puts("wmenu " VERSION);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
@ -163,34 +166,44 @@ void menu_getopts(struct menu *menu, int argc, char *argv[]) {
} }
// Add an item to the menu. // Add an item to the menu.
void menu_add_item(struct menu *menu, char *text, bool sort) { void menu_add_item(struct menu *menu, char *text) {
struct item *new = calloc(1, sizeof(struct item)); if ((menu->item_count & (menu->item_count - 1)) == 0) {
if (!new) { size_t alloc_size = menu->item_count ? 2 * menu->item_count : 1;
return; void *new_array = realloc(menu->items, sizeof(struct item) * alloc_size);
if (!new_array) {
fprintf(stderr, "could not realloc %zu bytes", sizeof(struct item) * alloc_size);
exit(EXIT_FAILURE);
}
menu->items = new_array;
} }
struct item *new = &menu->items[menu->item_count];
new->text = text; new->text = text;
if (sort) { menu->item_count++;
for (struct item **item = &menu->items; *item; item = &(*item)->next) { }
int result = strcmp(new->text, (*item)->text);
if (result == 0) { static int compare_items(const void *a, const void *b) {
free_item(new); const struct item *item_a = a;
return; const struct item *item_b = b;
} return strcmp(item_a->text, item_b->text);
if (result < 0) { }
new->next = *item;
*item = new; void menu_sort_and_deduplicate(struct menu *menu) {
return; size_t j = 1;
} size_t i;
qsort(menu->items, menu->item_count, sizeof(*menu->items), compare_items);
for (i = 1; i < menu->item_count; i++) {
if (strcmp(menu->items[i].text, menu->items[j - 1].text) == 0) {
free(menu->items[i].text);
} else {
menu->items[j] = menu->items[i];
j++;
} }
} }
menu->item_count = j;
if (menu->lastitem) {
menu->lastitem->next = new;
} else {
menu->items = new;
}
menu->lastitem = new;
} }
static void append_page(struct page *page, struct page **first, struct page **last) { static void append_page(struct page *page, struct page **first, struct page **last) {
@ -287,6 +300,7 @@ static void match_items(struct menu *menu) {
char buf[sizeof menu->input], *tok; char buf[sizeof menu->input], *tok;
char **tokv = NULL; char **tokv = NULL;
int i, tokc = 0; int i, tokc = 0;
size_t k;
size_t tok_len; size_t tok_len;
menu->matches = NULL; menu->matches = NULL;
menu->matches_end = NULL; menu->matches_end = NULL;
@ -310,8 +324,8 @@ static void match_items(struct menu *menu) {
} }
tok_len = tokc ? strlen(tokv[0]) : 0; tok_len = tokc ? strlen(tokv[0]) : 0;
struct item *item; for (k = 0; k < menu->item_count; k++) {
for (item = menu->items; item; item = item->next) { struct item *item = &menu->items[k];
for (i = 0; i < tokc; i++) { for (i = 0; i < tokc; i++) {
if (!fstrstr(menu, item->text, tokv[i])) { if (!fstrstr(menu, item->text, tokv[i])) {
/* token does not match */ /* token does not match */
@ -364,7 +378,6 @@ static void match_items(struct menu *menu) {
// Render menu items. // Render menu items.
void menu_render_items(struct menu *menu) { void menu_render_items(struct menu *menu) {
render_menu(menu);
calc_widths(menu); calc_widths(menu);
match_items(menu); match_items(menu);
render_menu(menu); render_menu(menu);
@ -568,18 +581,10 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
case XKB_KEY_Return: case XKB_KEY_Return:
case XKB_KEY_KP_Enter: case XKB_KEY_KP_Enter:
if (shift) { if (shift) {
puts(menu->input); menu->callback(menu, menu->input, true);
fflush(stdout);
menu->exit = true;
} else if (menu->callback) {
menu->callback(menu);
} else { } else {
char *text = menu->sel ? menu->sel->text : menu->input; char *text = menu->sel ? menu->sel->text : menu->input;
puts(text); menu->callback(menu, text, !ctrl);
fflush(stdout);
if (!ctrl) {
menu->exit = true;
}
} }
break; break;
case XKB_KEY_Left: case XKB_KEY_Left:

22
menu.h
View file

@ -1,16 +1,19 @@
#ifndef WMENU_MENU_H #ifndef WMENU_MENU_H
#define WMENU_MENU_H #define WMENU_MENU_H
#include <cairo/cairo.h>
#include <stdbool.h> #include <stdbool.h>
#include <sys/types.h> #include <sys/types.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <wayland-client.h> #include <wayland-client.h>
struct menu;
typedef void (*menu_callback)(struct menu *menu, char *text, bool exit);
// A menu item. // A menu item.
struct item { struct item {
char *text; char *text;
int width; int width;
struct item *next; // traverses all items
struct item *prev_match; // previous matching item struct item *prev_match; // previous matching item
struct item *next_match; // next matching item struct item *next_match; // next matching item
struct page *page; // the page holding this item struct page *page; // the page holding this item
@ -30,6 +33,8 @@ struct menu {
bool bottom; bool bottom;
// The function used to match menu items // The function used to match menu items
int (*strncmp)(const char *, const char *, size_t); int (*strncmp)(const char *, const char *, size_t);
// Whether the input is a password
bool passwd;
// The font used to display the menu // The font used to display the menu
char *font; char *font;
// The number of lines to list items vertically // The number of lines to list items vertically
@ -47,6 +52,10 @@ struct menu {
struct wl_context *context; struct wl_context *context;
// 1x1 surface used estimate text sizes with pango
cairo_surface_t *test_surface;
cairo_t *test_cairo;
int width; int width;
int height; int height;
int line_height; int line_height;
@ -59,22 +68,23 @@ struct menu {
char input[BUFSIZ]; char input[BUFSIZ];
size_t cursor; size_t cursor;
struct item *items; // list of all items struct item *items; // array of all items
struct item *lastitem; // last item in the list size_t item_count;
struct item *matches; // list of matching items struct item *matches; // list of matching items
struct item *matches_end; // last matching item struct item *matches_end; // last matching item
struct item *sel; // selected item struct item *sel; // selected item
struct page *pages; // list of pages struct page *pages; // list of pages
void (*callback)(struct menu *menu); menu_callback callback;
bool exit; bool exit;
bool failure; bool failure;
}; };
struct menu *menu_create(); struct menu *menu_create(menu_callback callback);
void menu_destroy(struct menu *menu); void menu_destroy(struct menu *menu);
void menu_getopts(struct menu *menu, int argc, char *argv[]); void menu_getopts(struct menu *menu, int argc, char *argv[]);
void menu_add_item(struct menu *menu, char *text, bool sort); void menu_add_item(struct menu *menu, char *text);
void menu_sort_and_deduplicate(struct menu *menu);
void menu_render_items(struct menu *menu); void menu_render_items(struct menu *menu);
void menu_paste(struct menu *menu, const char *text, ssize_t len); void menu_paste(struct menu *menu, const char *text, ssize_t len);
void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state, void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,

View file

@ -1,7 +1,7 @@
project( project(
'wmenu', 'wmenu',
'c', 'c',
version: '0.1.8', version: '0.1.9',
license: 'MIT', license: 'MIT',
default_options: [ default_options: [
'c_std=c11', 'c_std=c11',
@ -33,6 +33,28 @@ rt = cc.find_library('rt')
subdir('protocols') subdir('protocols')
subdir('docs') subdir('docs')
shared_library(
'wmenu',
files(
'menu.c',
'pango.c',
'pool-buffer.c',
'render.c',
'wayland.c',
'wmenu.c',
),
dependencies: [
cairo,
client_protos,
pango,
pangocairo,
rt,
wayland_client,
wayland_protos,
xkbcommon,
],
)
executable( executable(
'wmenu', 'wmenu',
files( files(

View file

@ -13,8 +13,13 @@
// Calculate text widths. // Calculate text widths.
void calc_widths(struct menu *menu) { void calc_widths(struct menu *menu) {
struct wl_context *context = menu->context; struct wl_context *context = menu->context;
struct pool_buffer *current = context_get_current_buffer(context); int scale = context_get_scale(context);
cairo_t *cairo = current->cairo; cairo_surface_set_device_scale(menu->test_surface, scale, scale);
cairo_set_antialias(menu->test_cairo, CAIRO_ANTIALIAS_BEST);
cairo_font_options_t *fo = cairo_font_options_create();
cairo_set_font_options(menu->test_cairo, fo);
cairo_font_options_destroy(fo);
cairo_t *cairo = menu->test_cairo;
// Calculate prompt width // Calculate prompt width
if (menu->prompt) { if (menu->prompt) {
@ -28,7 +33,8 @@ void calc_widths(struct menu *menu) {
menu->right_arrow = text_width(cairo, menu->font, ">") + 2 * menu->padding; menu->right_arrow = text_width(cairo, menu->font, ">") + 2 * menu->padding;
// Calculate item widths and input area width // Calculate item widths and input area width
for (struct item *item = menu->items; item; item = item->next) { for (size_t i = 0; i < menu->item_count; i++) {
struct item *item = &menu->items[i];
item->width = text_width(cairo, menu->font, item->text); item->width = text_width(cairo, menu->font, item->text);
if (item->width > menu->inputw) { if (item->width > menu->inputw) {
menu->inputw = item->width; menu->inputw = item->width;
@ -79,8 +85,22 @@ static void render_prompt(struct menu *menu, cairo_t *cairo) {
// Renders the input text. // Renders the input text.
static void render_input(struct menu *menu, cairo_t *cairo) { static void render_input(struct menu *menu, cairo_t *cairo) {
render_text(menu, cairo, menu->input, menu->promptw, 0, 0, char *censort = NULL;
0, menu->normalfg, menu->padding, menu->padding);
if (menu->passwd) {
censort = calloc(1, sizeof(menu->input));
if (!censort) {
return;
}
memset(censort, '*', strlen(menu->input));
}
render_text(menu, cairo, menu->passwd ? censort : menu->input,
menu->promptw, 0, 0, 0, menu->normalfg, menu->padding, menu->padding);
if (censort) {
free(censort);
}
} }
// Renders a cursor for the input field. // Renders a cursor for the input field.
@ -169,41 +189,22 @@ static void render_to_cairo(struct menu *menu, cairo_t *cairo) {
void render_menu(struct menu *menu) { void render_menu(struct menu *menu) {
struct wl_context *context = menu->context; struct wl_context *context = menu->context;
cairo_surface_t *recorder = cairo_recording_surface_create(
CAIRO_CONTENT_COLOR_ALPHA, NULL);
cairo_t *cairo = cairo_create(recorder);
cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST);
cairo_font_options_t *fo = cairo_font_options_create();
cairo_set_font_options(cairo, fo);
cairo_font_options_destroy(fo);
cairo_save(cairo);
cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR);
cairo_paint(cairo);
cairo_restore(cairo);
render_to_cairo(menu, cairo);
int scale = context_get_scale(context); int scale = context_get_scale(context);
struct pool_buffer *buffer = context_get_next_buffer(context, scale); struct pool_buffer *buffer = context_get_next_buffer(context, scale);
if (!buffer) { if (!buffer) {
goto cleanup; return;
} }
cairo_t *shm = buffer->cairo; cairo_t *shm = buffer->cairo;
cairo_save(shm); cairo_set_antialias(shm, CAIRO_ANTIALIAS_BEST);
cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); cairo_font_options_t *fo = cairo_font_options_create();
cairo_paint(shm); cairo_set_font_options(shm, fo);
cairo_restore(shm); cairo_font_options_destroy(fo);
cairo_set_source_surface(shm, recorder, 0, 0); render_to_cairo(menu, shm);
cairo_paint(shm);
struct wl_surface *surface = context_get_surface(context); struct wl_surface *surface = context_get_surface(context);
wl_surface_set_buffer_scale(surface, scale); wl_surface_set_buffer_scale(surface, scale);
wl_surface_attach(surface, buffer->buffer, 0, 0); wl_surface_attach(surface, buffer->buffer, 0, 0);
wl_surface_damage(surface, 0, 0, menu->width, menu->height); wl_surface_damage(surface, 0, 0, menu->width, menu->height);
wl_surface_commit(surface); wl_surface_commit(surface);
cleanup:
cairo_destroy(cairo);
cairo_surface_destroy(recorder);
} }

View file

@ -208,6 +208,7 @@ static void noop() {
static void surface_enter(void *data, struct wl_surface *surface, struct wl_output *wl_output) { static void surface_enter(void *data, struct wl_surface *surface, struct wl_output *wl_output) {
struct wl_context *context = data; struct wl_context *context = data;
context->output = wl_output_get_user_data(wl_output); context->output = wl_output_get_user_data(wl_output);
menu_render_items(context->menu);
} }
static const struct wl_surface_listener surface_listener = { static const struct wl_surface_listener surface_listener = {
@ -439,7 +440,7 @@ int menu_run(struct menu *menu) {
context->layer_shell, context->layer_shell,
context->surface, context->surface,
context->output ? context->output->output : NULL, context->output ? context->output->output : NULL,
ZWLR_LAYER_SHELL_V1_LAYER_TOP, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
"menu" "menu"
); );
assert(layer_surface != NULL); assert(layer_surface != NULL);
@ -494,11 +495,10 @@ int menu_run(struct menu *menu) {
} }
} }
bool failure = menu->failure;
context_destroy(context); context_destroy(context);
menu->context = NULL; menu->context = NULL;
if (failure) { if (menu->failure) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
return EXIT_SUCCESS; return EXIT_SUCCESS;

View file

@ -1,6 +1,6 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@ -20,56 +20,56 @@ static void read_items(struct menu *menu) {
if (ent->d_name[0] == '.') { if (ent->d_name[0] == '.') {
continue; continue;
} }
menu_add_item(menu, strdup(ent->d_name), true); menu_add_item(menu, strdup(ent->d_name));
} }
closedir(dir); closedir(dir);
} }
menu_sort_and_deduplicate(menu);
free(path); free(path);
} }
struct executable { struct command {
struct menu *menu; struct menu *menu;
char *name; char *text;
bool exit;
}; };
static void activation_token_done(void *data, struct xdg_activation_token_v1 *activation_token, static void activation_token_done(void *data, struct xdg_activation_token_v1 *activation_token,
const char *token) { const char *token) {
struct executable *exe = data; struct command *cmd = data;
xdg_activation_token_v1_destroy(activation_token); xdg_activation_token_v1_destroy(activation_token);
menu_destroy(exe->menu);
setenv("XDG_ACTIVATION_TOKEN", token, true); int pid = fork();
execlp(exe->name, exe->name, NULL); if (pid == 0) {
setenv("XDG_ACTIVATION_TOKEN", token, true);
fprintf(stderr, "Failed to execute selection: %s\n", strerror(errno)); char *argv[] = {"/bin/sh", "-c", cmd->text, NULL};
free(exe->name); execvp(argv[0], (char**)argv);
free(exe); } else {
exit(EXIT_FAILURE); if (cmd->exit) {
cmd->menu->exit = true;
}
}
} }
static const struct xdg_activation_token_v1_listener activation_token_listener = { static const struct xdg_activation_token_v1_listener activation_token_listener = {
.done = activation_token_done, .done = activation_token_done,
}; };
static void exec(struct menu *menu) { static void exec_item(struct menu *menu, char *text, bool exit) {
if (!menu->sel) { struct command *cmd = calloc(1, sizeof(struct command));
return; cmd->menu = menu;
} cmd->text = strdup(text);
cmd->exit = exit;
struct executable *exe = calloc(1, sizeof(struct executable));
exe->menu = menu;
exe->name = strdup(menu->sel->text);
struct xdg_activation_v1 *activation = context_get_xdg_activation(menu->context); 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); 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_set_surface(activation_token, context_get_surface(menu->context));
xdg_activation_token_v1_add_listener(activation_token, &activation_token_listener, exe); xdg_activation_token_v1_add_listener(activation_token, &activation_token_listener, cmd);
xdg_activation_token_v1_commit(activation_token); xdg_activation_token_v1_commit(activation_token);
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
struct menu *menu = menu_create(); struct menu *menu = menu_create(exec_item);
menu->callback = exec;
menu_getopts(menu, argc, argv); menu_getopts(menu, argc, argv);
read_items(menu); read_items(menu);
int status = menu_run(menu); int status = menu_run(menu);

13
wmenu.c
View file

@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <string.h> #include <string.h>
#include "menu.h" #include "menu.h"
@ -12,12 +13,20 @@ static void read_items(struct menu *menu) {
if (p) { if (p) {
*p = '\0'; *p = '\0';
} }
menu_add_item(menu, strdup(buf), false); menu_add_item(menu, strdup(buf));
}
}
static void print_item(struct menu *menu, char *text, bool exit) {
puts(text);
fflush(stdout);
if (exit) {
menu->exit = true;
} }
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
struct menu *menu = menu_create(); struct menu *menu = menu_create(print_item);
menu_getopts(menu, argc, argv); menu_getopts(menu, argc, argv);
read_items(menu); read_items(menu);
int status = menu_run(menu); int status = menu_run(menu);