Compare commits
No commits in common. "main" and "0.1.9" have entirely different histories.
8 changed files with 72 additions and 97 deletions
|
@ -15,7 +15,7 @@ Dependencies:
|
|||
- scdoc (optional)
|
||||
|
||||
```
|
||||
$ meson setup build
|
||||
$ meson build
|
||||
$ ninja -C build
|
||||
# ninja -C build install
|
||||
```
|
||||
|
|
81
menu.c
81
menu.c
|
@ -1,5 +1,4 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <poll.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -34,8 +33,6 @@ struct menu *menu_create(menu_callback callback) {
|
|||
menu->selectionbg = 0x005577ff;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -48,20 +45,24 @@ 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) {
|
||||
for (size_t i = 0; i < menu->item_count; i++) {
|
||||
struct item *item = &menu->items[i];
|
||||
free(item->text);
|
||||
struct item *next = menu->items;
|
||||
while (next) {
|
||||
struct item *item = next;
|
||||
next = item->next;
|
||||
free_item(item);
|
||||
}
|
||||
free(menu->items);
|
||||
}
|
||||
|
||||
// Destroys the menu, freeing memory associated with it.
|
||||
void menu_destroy(struct menu *menu) {
|
||||
free_pages(menu);
|
||||
free_items(menu);
|
||||
cairo_destroy(menu->test_cairo);
|
||||
cairo_surface_destroy(menu->test_surface);
|
||||
free(menu);
|
||||
}
|
||||
|
||||
|
@ -166,44 +167,34 @@ void menu_getopts(struct menu *menu, int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
// Add an item to the menu.
|
||||
void menu_add_item(struct menu *menu, char *text) {
|
||||
if ((menu->item_count & (menu->item_count - 1)) == 0) {
|
||||
size_t alloc_size = menu->item_count ? 2 * menu->item_count : 1;
|
||||
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;
|
||||
void menu_add_item(struct menu *menu, char *text, bool sort) {
|
||||
struct item *new = calloc(1, sizeof(struct item));
|
||||
if (!new) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct item *new = &menu->items[menu->item_count];
|
||||
new->text = text;
|
||||
|
||||
menu->item_count++;
|
||||
}
|
||||
|
||||
static int compare_items(const void *a, const void *b) {
|
||||
const struct item *item_a = a;
|
||||
const struct item *item_b = b;
|
||||
return strcmp(item_a->text, item_b->text);
|
||||
}
|
||||
|
||||
void menu_sort_and_deduplicate(struct menu *menu) {
|
||||
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++;
|
||||
if (sort) {
|
||||
for (struct item **item = &menu->items; *item; item = &(*item)->next) {
|
||||
int result = strcmp(new->text, (*item)->text);
|
||||
if (result == 0) {
|
||||
free_item(new);
|
||||
return;
|
||||
}
|
||||
if (result < 0) {
|
||||
new->next = *item;
|
||||
*item = new;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
|
@ -300,7 +291,6 @@ static void match_items(struct menu *menu) {
|
|||
char buf[sizeof menu->input], *tok;
|
||||
char **tokv = NULL;
|
||||
int i, tokc = 0;
|
||||
size_t k;
|
||||
size_t tok_len;
|
||||
menu->matches = NULL;
|
||||
menu->matches_end = NULL;
|
||||
|
@ -324,8 +314,8 @@ static void match_items(struct menu *menu) {
|
|||
}
|
||||
tok_len = tokc ? strlen(tokv[0]) : 0;
|
||||
|
||||
for (k = 0; k < menu->item_count; k++) {
|
||||
struct item *item = &menu->items[k];
|
||||
struct item *item;
|
||||
for (item = menu->items; item; item = item->next) {
|
||||
for (i = 0; i < tokc; i++) {
|
||||
if (!fstrstr(menu, item->text, tokv[i])) {
|
||||
/* token does not match */
|
||||
|
@ -378,6 +368,7 @@ static void match_items(struct menu *menu) {
|
|||
|
||||
// Render menu items.
|
||||
void menu_render_items(struct menu *menu) {
|
||||
render_menu(menu);
|
||||
calc_widths(menu);
|
||||
match_items(menu);
|
||||
render_menu(menu);
|
||||
|
|
13
menu.h
13
menu.h
|
@ -1,7 +1,6 @@
|
|||
#ifndef WMENU_MENU_H
|
||||
#define WMENU_MENU_H
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
@ -14,6 +13,7 @@ typedef void (*menu_callback)(struct menu *menu, char *text, bool exit);
|
|||
struct item {
|
||||
char *text;
|
||||
int width;
|
||||
struct item *next; // traverses all items
|
||||
struct item *prev_match; // previous matching item
|
||||
struct item *next_match; // next matching item
|
||||
struct page *page; // the page holding this item
|
||||
|
@ -52,10 +52,6 @@ struct menu {
|
|||
|
||||
struct wl_context *context;
|
||||
|
||||
// 1x1 surface used estimate text sizes with pango
|
||||
cairo_surface_t *test_surface;
|
||||
cairo_t *test_cairo;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int line_height;
|
||||
|
@ -68,8 +64,8 @@ struct menu {
|
|||
char input[BUFSIZ];
|
||||
size_t cursor;
|
||||
|
||||
struct item *items; // array of all items
|
||||
size_t item_count;
|
||||
struct item *items; // list of all items
|
||||
struct item *lastitem; // last item in the list
|
||||
struct item *matches; // list of matching items
|
||||
struct item *matches_end; // last matching item
|
||||
struct item *sel; // selected item
|
||||
|
@ -83,8 +79,7 @@ struct menu {
|
|||
struct menu *menu_create(menu_callback callback);
|
||||
void menu_destroy(struct menu *menu);
|
||||
void menu_getopts(struct menu *menu, int argc, char *argv[]);
|
||||
void menu_add_item(struct menu *menu, char *text);
|
||||
void menu_sort_and_deduplicate(struct menu *menu);
|
||||
void menu_add_item(struct menu *menu, char *text, bool sort);
|
||||
void menu_render_items(struct menu *menu);
|
||||
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,
|
||||
|
|
22
meson.build
22
meson.build
|
@ -33,28 +33,6 @@ rt = cc.find_library('rt')
|
|||
subdir('protocols')
|
||||
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(
|
||||
'wmenu',
|
||||
files(
|
||||
|
|
43
render.c
43
render.c
|
@ -13,13 +13,8 @@
|
|||
// Calculate text widths.
|
||||
void calc_widths(struct menu *menu) {
|
||||
struct wl_context *context = menu->context;
|
||||
int scale = context_get_scale(context);
|
||||
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;
|
||||
struct pool_buffer *current = context_get_current_buffer(context);
|
||||
cairo_t *cairo = current->cairo;
|
||||
|
||||
// Calculate prompt width
|
||||
if (menu->prompt) {
|
||||
|
@ -33,8 +28,7 @@ void calc_widths(struct menu *menu) {
|
|||
menu->right_arrow = text_width(cairo, menu->font, ">") + 2 * menu->padding;
|
||||
|
||||
// Calculate item widths and input area width
|
||||
for (size_t i = 0; i < menu->item_count; i++) {
|
||||
struct item *item = &menu->items[i];
|
||||
for (struct item *item = menu->items; item; item = item->next) {
|
||||
item->width = text_width(cairo, menu->font, item->text);
|
||||
if (item->width > menu->inputw) {
|
||||
menu->inputw = item->width;
|
||||
|
@ -189,22 +183,41 @@ static void render_to_cairo(struct menu *menu, cairo_t *cairo) {
|
|||
void render_menu(struct menu *menu) {
|
||||
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);
|
||||
struct pool_buffer *buffer = context_get_next_buffer(context, scale);
|
||||
if (!buffer) {
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cairo_t *shm = buffer->cairo;
|
||||
cairo_set_antialias(shm, CAIRO_ANTIALIAS_BEST);
|
||||
cairo_font_options_t *fo = cairo_font_options_create();
|
||||
cairo_set_font_options(shm, fo);
|
||||
cairo_font_options_destroy(fo);
|
||||
render_to_cairo(menu, shm);
|
||||
cairo_save(shm);
|
||||
cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(shm);
|
||||
cairo_restore(shm);
|
||||
cairo_set_source_surface(shm, recorder, 0, 0);
|
||||
cairo_paint(shm);
|
||||
|
||||
struct wl_surface *surface = context_get_surface(context);
|
||||
wl_surface_set_buffer_scale(surface, scale);
|
||||
wl_surface_attach(surface, buffer->buffer, 0, 0);
|
||||
wl_surface_damage(surface, 0, 0, menu->width, menu->height);
|
||||
wl_surface_commit(surface);
|
||||
|
||||
cleanup:
|
||||
cairo_destroy(cairo);
|
||||
cairo_surface_destroy(recorder);
|
||||
}
|
||||
|
|
|
@ -208,7 +208,6 @@ static void noop() {
|
|||
static void surface_enter(void *data, struct wl_surface *surface, struct wl_output *wl_output) {
|
||||
struct wl_context *context = data;
|
||||
context->output = wl_output_get_user_data(wl_output);
|
||||
menu_render_items(context->menu);
|
||||
}
|
||||
|
||||
static const struct wl_surface_listener surface_listener = {
|
||||
|
@ -440,7 +439,7 @@ int menu_run(struct menu *menu) {
|
|||
context->layer_shell,
|
||||
context->surface,
|
||||
context->output ? context->output->output : NULL,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_TOP,
|
||||
"menu"
|
||||
);
|
||||
assert(layer_surface != NULL);
|
||||
|
|
|
@ -20,11 +20,10 @@ static void read_items(struct menu *menu) {
|
|||
if (ent->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
menu_add_item(menu, strdup(ent->d_name));
|
||||
menu_add_item(menu, strdup(ent->d_name), true);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
menu_sort_and_deduplicate(menu);
|
||||
free(path);
|
||||
}
|
||||
|
||||
|
|
2
wmenu.c
2
wmenu.c
|
@ -13,7 +13,7 @@ static void read_items(struct menu *menu) {
|
|||
if (p) {
|
||||
*p = '\0';
|
||||
}
|
||||
menu_add_item(menu, strdup(buf));
|
||||
menu_add_item(menu, strdup(buf), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue