mirror of
				https://codeberg.org/adnano/wmenu.git
				synced 2025-11-03 09:01:46 -05:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "main" and "0.1.8" have entirely different histories.
		
	
	
		
	
		
					 9 changed files with 150 additions and 182 deletions
				
			
		
							
								
								
									
										13
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
					@ -15,7 +15,7 @@ Dependencies:
 | 
				
			||||||
- scdoc (optional)
 | 
					- scdoc (optional)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
$ meson setup build
 | 
					$ meson build
 | 
				
			||||||
$ ninja -C build
 | 
					$ ninja -C build
 | 
				
			||||||
# ninja -C build install
 | 
					# ninja -C build install
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
| 
						 | 
					@ -30,3 +30,14 @@ 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.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ wmenu - dynamic menu for Wayland
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# SYNOPSIS
 | 
					# SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*wmenu* [-biPv] \
 | 
					*wmenu* [-biv] \
 | 
				
			||||||
  [-f _font_] \
 | 
					  [-f _font_] \
 | 
				
			||||||
  [-l _lines_] \
 | 
					  [-l _lines_] \
 | 
				
			||||||
  [-o _output_] \
 | 
					  [-o _output_] \
 | 
				
			||||||
| 
						 | 
					@ -35,10 +35,6 @@ $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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										152
									
								
								menu.c
									
										
									
									
									
								
							
							
						
						
									
										152
									
								
								menu.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
#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>
 | 
				
			||||||
| 
						 | 
					@ -23,7 +22,7 @@
 | 
				
			||||||
#include "wayland.h"
 | 
					#include "wayland.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Creates and returns a new menu.
 | 
					// Creates and returns a new menu.
 | 
				
			||||||
struct menu *menu_create(menu_callback callback) {
 | 
					struct menu *menu_create() {
 | 
				
			||||||
	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";
 | 
				
			||||||
| 
						 | 
					@ -33,9 +32,6 @@ struct menu *menu_create(menu_callback callback) {
 | 
				
			||||||
	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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,20 +44,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) {
 | 
					static void free_items(struct menu *menu) {
 | 
				
			||||||
	for (size_t i = 0; i < menu->item_count; i++) {
 | 
						struct item *next = menu->items;
 | 
				
			||||||
		struct item *item = &menu->items[i];
 | 
						while (next) {
 | 
				
			||||||
		free(item->text);
 | 
							struct item *item = next;
 | 
				
			||||||
 | 
							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 [-biPv] [-f font] [-l lines] [-o output] [-p prompt]\n"
 | 
							"Usage: wmenu [-biPvx] [-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, "bhiPvf:l:o:p:N:n:M:m:S:s:")) != -1) {
 | 
						while ((opt = getopt(argc, argv, "bhivf: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,9 +97,6 @@ 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);
 | 
				
			||||||
| 
						 | 
					@ -166,44 +163,34 @@ 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) {
 | 
					void menu_add_item(struct menu *menu, char *text, bool sort) {
 | 
				
			||||||
	if ((menu->item_count & (menu->item_count - 1)) == 0) {
 | 
						struct item *new = calloc(1, sizeof(struct item));
 | 
				
			||||||
		size_t alloc_size = menu->item_count ? 2 * menu->item_count : 1;
 | 
						if (!new) {
 | 
				
			||||||
		void *new_array = realloc(menu->items, sizeof(struct item) * alloc_size);
 | 
							return;
 | 
				
			||||||
		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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	menu->item_count++;
 | 
						if (sort) {
 | 
				
			||||||
}
 | 
							for (struct item **item = &menu->items; *item; item = &(*item)->next) {
 | 
				
			||||||
 | 
								int result = strcmp(new->text, (*item)->text);
 | 
				
			||||||
static int compare_items(const void *a, const void *b) {
 | 
								if (result == 0) {
 | 
				
			||||||
	const struct item *item_a = a;
 | 
									free_item(new);
 | 
				
			||||||
	const struct item *item_b = b;
 | 
									return;
 | 
				
			||||||
	return strcmp(item_a->text, item_b->text);
 | 
								}
 | 
				
			||||||
}
 | 
								if (result < 0) {
 | 
				
			||||||
 | 
									new->next = *item;
 | 
				
			||||||
void menu_sort_and_deduplicate(struct menu *menu) {
 | 
									*item = new;
 | 
				
			||||||
	size_t j = 1;
 | 
									return;
 | 
				
			||||||
	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) {
 | 
				
			||||||
| 
						 | 
					@ -300,7 +287,6 @@ 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;
 | 
				
			||||||
| 
						 | 
					@ -324,8 +310,8 @@ static void match_items(struct menu *menu) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	tok_len = tokc ? strlen(tokv[0]) : 0;
 | 
						tok_len = tokc ? strlen(tokv[0]) : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (k = 0; k < menu->item_count; k++) {
 | 
						struct item *item;
 | 
				
			||||||
		struct item *item = &menu->items[k];
 | 
						for (item = menu->items; item; item = item->next) {
 | 
				
			||||||
		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 */
 | 
				
			||||||
| 
						 | 
					@ -376,13 +362,9 @@ static void match_items(struct menu *menu) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Marks the menu as needing to be rendered again.
 | 
					 | 
				
			||||||
void menu_invalidate(struct menu *menu) {
 | 
					 | 
				
			||||||
	menu->rendered = false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 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);
 | 
				
			||||||
| 
						 | 
					@ -503,13 +485,13 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
			// Delete right
 | 
								// Delete right
 | 
				
			||||||
			menu->input[menu->cursor] = '\0';
 | 
								menu->input[menu->cursor] = '\0';
 | 
				
			||||||
			match_items(menu);
 | 
								match_items(menu);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		case XKB_KEY_u:
 | 
							case XKB_KEY_u:
 | 
				
			||||||
			// Delete left
 | 
								// Delete left
 | 
				
			||||||
			insert(menu, NULL, 0 - menu->cursor);
 | 
								insert(menu, NULL, 0 - menu->cursor);
 | 
				
			||||||
			match_items(menu);
 | 
								match_items(menu);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		case XKB_KEY_w:
 | 
							case XKB_KEY_w:
 | 
				
			||||||
			// Delete word
 | 
								// Delete word
 | 
				
			||||||
| 
						 | 
					@ -520,7 +502,7 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
				insert(menu, NULL, nextrune(menu, -1) - menu->cursor);
 | 
									insert(menu, NULL, nextrune(menu, -1) - menu->cursor);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			match_items(menu);
 | 
								match_items(menu);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		case XKB_KEY_Y:
 | 
							case XKB_KEY_Y:
 | 
				
			||||||
			// Paste clipboard
 | 
								// Paste clipboard
 | 
				
			||||||
| 
						 | 
					@ -528,17 +510,17 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			match_items(menu);
 | 
								match_items(menu);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		case XKB_KEY_Left:
 | 
							case XKB_KEY_Left:
 | 
				
			||||||
		case XKB_KEY_KP_Left:
 | 
							case XKB_KEY_KP_Left:
 | 
				
			||||||
			movewordedge(menu, -1);
 | 
								movewordedge(menu, -1);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		case XKB_KEY_Right:
 | 
							case XKB_KEY_Right:
 | 
				
			||||||
		case XKB_KEY_KP_Right:
 | 
							case XKB_KEY_KP_Right:
 | 
				
			||||||
			movewordedge(menu, +1);
 | 
								movewordedge(menu, +1);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case XKB_KEY_Return:
 | 
							case XKB_KEY_Return:
 | 
				
			||||||
| 
						 | 
					@ -552,11 +534,11 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
		switch (sym) {
 | 
							switch (sym) {
 | 
				
			||||||
		case XKB_KEY_b:
 | 
							case XKB_KEY_b:
 | 
				
			||||||
			movewordedge(menu, -1);
 | 
								movewordedge(menu, -1);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		case XKB_KEY_f:
 | 
							case XKB_KEY_f:
 | 
				
			||||||
			movewordedge(menu, +1);
 | 
								movewordedge(menu, +1);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		case XKB_KEY_g:
 | 
							case XKB_KEY_g:
 | 
				
			||||||
			sym = XKB_KEY_Home;
 | 
								sym = XKB_KEY_Home;
 | 
				
			||||||
| 
						 | 
					@ -586,10 +568,18 @@ 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) {
 | 
				
			||||||
			menu->callback(menu, menu->input, true);
 | 
								puts(menu->input);
 | 
				
			||||||
 | 
								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;
 | 
				
			||||||
			menu->callback(menu, text, !ctrl);
 | 
								puts(text);
 | 
				
			||||||
 | 
								fflush(stdout);
 | 
				
			||||||
 | 
								if (!ctrl) {
 | 
				
			||||||
 | 
									menu->exit = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_Left:
 | 
						case XKB_KEY_Left:
 | 
				
			||||||
| 
						 | 
					@ -598,10 +588,10 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
	case XKB_KEY_KP_Up:
 | 
						case XKB_KEY_KP_Up:
 | 
				
			||||||
		if (menu->sel && menu->sel->prev_match) {
 | 
							if (menu->sel && menu->sel->prev_match) {
 | 
				
			||||||
			menu->sel = menu->sel->prev_match;
 | 
								menu->sel = menu->sel->prev_match;
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		} else if (menu->cursor > 0) {
 | 
							} else if (menu->cursor > 0) {
 | 
				
			||||||
			menu->cursor = nextrune(menu, -1);
 | 
								menu->cursor = nextrune(menu, -1);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_Right:
 | 
						case XKB_KEY_Right:
 | 
				
			||||||
| 
						 | 
					@ -610,51 +600,51 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
	case XKB_KEY_KP_Down:
 | 
						case XKB_KEY_KP_Down:
 | 
				
			||||||
		if (menu->cursor < len) {
 | 
							if (menu->cursor < len) {
 | 
				
			||||||
			menu->cursor = nextrune(menu, +1);
 | 
								menu->cursor = nextrune(menu, +1);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		} else if (menu->sel && menu->sel->next_match) {
 | 
							} else if (menu->sel && menu->sel->next_match) {
 | 
				
			||||||
			menu->sel = menu->sel->next_match;
 | 
								menu->sel = menu->sel->next_match;
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_Prior:
 | 
						case XKB_KEY_Prior:
 | 
				
			||||||
	case XKB_KEY_KP_Prior:
 | 
						case XKB_KEY_KP_Prior:
 | 
				
			||||||
		if (menu->sel && menu->sel->page->prev) {
 | 
							if (menu->sel && menu->sel->page->prev) {
 | 
				
			||||||
			menu->sel = menu->sel->page->prev->first;
 | 
								menu->sel = menu->sel->page->prev->first;
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_Next:
 | 
						case XKB_KEY_Next:
 | 
				
			||||||
	case XKB_KEY_KP_Next:
 | 
						case XKB_KEY_KP_Next:
 | 
				
			||||||
		if (menu->sel && menu->sel->page->next) {
 | 
							if (menu->sel && menu->sel->page->next) {
 | 
				
			||||||
			menu->sel = menu->sel->page->next->first;
 | 
								menu->sel = menu->sel->page->next->first;
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_Home:
 | 
						case XKB_KEY_Home:
 | 
				
			||||||
	case XKB_KEY_KP_Home:
 | 
						case XKB_KEY_KP_Home:
 | 
				
			||||||
		if (menu->sel == menu->matches) {
 | 
							if (menu->sel == menu->matches) {
 | 
				
			||||||
			menu->cursor = 0;
 | 
								menu->cursor = 0;
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			menu->sel = menu->matches;
 | 
								menu->sel = menu->matches;
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_End:
 | 
						case XKB_KEY_End:
 | 
				
			||||||
	case XKB_KEY_KP_End:
 | 
						case XKB_KEY_KP_End:
 | 
				
			||||||
		if (menu->cursor < len) {
 | 
							if (menu->cursor < len) {
 | 
				
			||||||
			menu->cursor = len;
 | 
								menu->cursor = len;
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			menu->sel = menu->matches_end;
 | 
								menu->sel = menu->matches_end;
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_BackSpace:
 | 
						case XKB_KEY_BackSpace:
 | 
				
			||||||
		if (menu->cursor > 0) {
 | 
							if (menu->cursor > 0) {
 | 
				
			||||||
			insert(menu, NULL, nextrune(menu, -1) - menu->cursor);
 | 
								insert(menu, NULL, nextrune(menu, -1) - menu->cursor);
 | 
				
			||||||
			match_items(menu);
 | 
								match_items(menu);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_Delete:
 | 
						case XKB_KEY_Delete:
 | 
				
			||||||
| 
						 | 
					@ -665,7 +655,7 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
		menu->cursor = nextrune(menu, +1);
 | 
							menu->cursor = nextrune(menu, +1);
 | 
				
			||||||
		insert(menu, NULL, nextrune(menu, -1) - menu->cursor);
 | 
							insert(menu, NULL, nextrune(menu, -1) - menu->cursor);
 | 
				
			||||||
		match_items(menu);
 | 
							match_items(menu);
 | 
				
			||||||
		menu_invalidate(menu);
 | 
							render_menu(menu);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_Tab:
 | 
						case XKB_KEY_Tab:
 | 
				
			||||||
		if (!menu->sel) {
 | 
							if (!menu->sel) {
 | 
				
			||||||
| 
						 | 
					@ -675,7 +665,7 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
		memcpy(menu->input, menu->sel->text, menu->cursor);
 | 
							memcpy(menu->input, menu->sel->text, menu->cursor);
 | 
				
			||||||
		menu->input[menu->cursor] = '\0';
 | 
							menu->input[menu->cursor] = '\0';
 | 
				
			||||||
		match_items(menu);
 | 
							match_items(menu);
 | 
				
			||||||
		menu_invalidate(menu);
 | 
							render_menu(menu);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case XKB_KEY_Escape:
 | 
						case XKB_KEY_Escape:
 | 
				
			||||||
		menu->exit = true;
 | 
							menu->exit = true;
 | 
				
			||||||
| 
						 | 
					@ -685,7 +675,7 @@ void menu_keypress(struct menu *menu, enum wl_keyboard_key_state key_state,
 | 
				
			||||||
		if (xkb_keysym_to_utf8(sym, buf, 8)) {
 | 
							if (xkb_keysym_to_utf8(sym, buf, 8)) {
 | 
				
			||||||
			insert(menu, buf, strnlen(buf, 8));
 | 
								insert(menu, buf, strnlen(buf, 8));
 | 
				
			||||||
			match_items(menu);
 | 
								match_items(menu);
 | 
				
			||||||
			menu_invalidate(menu);
 | 
								render_menu(menu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										24
									
								
								menu.h
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								menu.h
									
										
									
									
									
								
							| 
						 | 
					@ -1,19 +1,16 @@
 | 
				
			||||||
#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
 | 
				
			||||||
| 
						 | 
					@ -33,8 +30,6 @@ 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
 | 
				
			||||||
| 
						 | 
					@ -52,10 +47,6 @@ 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;
 | 
				
			||||||
| 
						 | 
					@ -64,29 +55,26 @@ struct menu {
 | 
				
			||||||
	int promptw;
 | 
						int promptw;
 | 
				
			||||||
	int left_arrow;
 | 
						int left_arrow;
 | 
				
			||||||
	int right_arrow;
 | 
						int right_arrow;
 | 
				
			||||||
	bool rendered;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char input[BUFSIZ];
 | 
						char input[BUFSIZ];
 | 
				
			||||||
	size_t cursor;
 | 
						size_t cursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct item *items;       // array of all items
 | 
						struct item *items;       // list of all items
 | 
				
			||||||
	size_t item_count;
 | 
						struct item *lastitem;    // last item in the list
 | 
				
			||||||
	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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	menu_callback callback;
 | 
						void (*callback)(struct menu *menu);
 | 
				
			||||||
	bool exit;
 | 
						bool exit;
 | 
				
			||||||
	bool failure;
 | 
						bool failure;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct menu *menu_create(menu_callback callback);
 | 
					struct menu *menu_create();
 | 
				
			||||||
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);
 | 
					void menu_add_item(struct menu *menu, char *text, bool sort);
 | 
				
			||||||
void menu_sort_and_deduplicate(struct menu *menu);
 | 
					 | 
				
			||||||
void menu_invalidate(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,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
project(
 | 
					project(
 | 
				
			||||||
	'wmenu',
 | 
						'wmenu',
 | 
				
			||||||
	'c',
 | 
						'c',
 | 
				
			||||||
	version: '0.2.0',
 | 
						version: '0.1.8',
 | 
				
			||||||
	license: 'MIT',
 | 
						license: 'MIT',
 | 
				
			||||||
	default_options: [
 | 
						default_options: [
 | 
				
			||||||
		'c_std=c11',
 | 
							'c_std=c11',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										61
									
								
								render.c
									
										
									
									
									
								
							
							
						
						
									
										61
									
								
								render.c
									
										
									
									
									
								
							| 
						 | 
					@ -13,13 +13,8 @@
 | 
				
			||||||
// 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;
 | 
				
			||||||
	int scale = context_get_scale(context);
 | 
						struct pool_buffer *current = context_get_current_buffer(context);
 | 
				
			||||||
	cairo_surface_set_device_scale(menu->test_surface, scale, scale);
 | 
						cairo_t *cairo = current->cairo;
 | 
				
			||||||
	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) {
 | 
				
			||||||
| 
						 | 
					@ -33,8 +28,7 @@ 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 (size_t i = 0; i < menu->item_count; i++) {
 | 
						for (struct item *item = menu->items; item; item = item->next) {
 | 
				
			||||||
		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;
 | 
				
			||||||
| 
						 | 
					@ -85,22 +79,8 @@ 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) {
 | 
				
			||||||
	char *censort = NULL;
 | 
						render_text(menu, cairo, menu->input, menu->promptw, 0, 0,
 | 
				
			||||||
 | 
							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.
 | 
				
			||||||
| 
						 | 
					@ -189,18 +169,33 @@ 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) {
 | 
				
			||||||
		return;
 | 
							goto cleanup;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cairo_t *shm = buffer->cairo;
 | 
						cairo_t *shm = buffer->cairo;
 | 
				
			||||||
	cairo_set_antialias(shm, CAIRO_ANTIALIAS_BEST);
 | 
						cairo_save(shm);
 | 
				
			||||||
	cairo_font_options_t *fo = cairo_font_options_create();
 | 
						cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR);
 | 
				
			||||||
	cairo_set_font_options(shm, fo);
 | 
						cairo_paint(shm);
 | 
				
			||||||
	cairo_font_options_destroy(fo);
 | 
						cairo_restore(shm);
 | 
				
			||||||
	render_to_cairo(menu, shm);
 | 
						cairo_set_source_surface(shm, recorder, 0, 0);
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
| 
						 | 
					@ -208,5 +203,7 @@ void render_menu(struct menu *menu) {
 | 
				
			||||||
	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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	menu->rendered = true;
 | 
					cleanup:
 | 
				
			||||||
 | 
						cairo_destroy(cairo);
 | 
				
			||||||
 | 
						cairo_surface_destroy(recorder);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										13
									
								
								wayland.c
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								wayland.c
									
										
									
									
									
								
							| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
#include <errno.h>
 | 
					#include <errno.h>
 | 
				
			||||||
#include <poll.h>
 | 
					#include <poll.h>
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <signal.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
| 
						 | 
					@ -17,7 +18,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "menu.h"
 | 
					#include "menu.h"
 | 
				
			||||||
#include "pool-buffer.h"
 | 
					#include "pool-buffer.h"
 | 
				
			||||||
#include "render.h"
 | 
					 | 
				
			||||||
#include "wayland.h"
 | 
					#include "wayland.h"
 | 
				
			||||||
#include "xdg-activation-v1-client-protocol.h"
 | 
					#include "xdg-activation-v1-client-protocol.h"
 | 
				
			||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
 | 
					#include "wlr-layer-shell-unstable-v1-client-protocol.h"
 | 
				
			||||||
| 
						 | 
					@ -208,7 +208,6 @@ 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_invalidate(context->menu);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct wl_surface_listener surface_listener = {
 | 
					static const struct wl_surface_listener surface_listener = {
 | 
				
			||||||
| 
						 | 
					@ -440,7 +439,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_OVERLAY,
 | 
							ZWLR_LAYER_SHELL_V1_LAYER_TOP,
 | 
				
			||||||
		"menu"
 | 
							"menu"
 | 
				
			||||||
	);
 | 
						);
 | 
				
			||||||
	assert(layer_surface != NULL);
 | 
						assert(layer_surface != NULL);
 | 
				
			||||||
| 
						 | 
					@ -493,17 +492,13 @@ int menu_run(struct menu *menu) {
 | 
				
			||||||
		if (fds[1].revents & POLLIN) {
 | 
							if (fds[1].revents & POLLIN) {
 | 
				
			||||||
			keyboard_repeat(context->keyboard);
 | 
								keyboard_repeat(context->keyboard);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Render the menu if necessary
 | 
					 | 
				
			||||||
		if (!menu->rendered) {
 | 
					 | 
				
			||||||
			render_menu(menu);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool failure = menu->failure;
 | 
				
			||||||
	context_destroy(context);
 | 
						context_destroy(context);
 | 
				
			||||||
	menu->context = NULL;
 | 
						menu->context = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (menu->failure) {
 | 
						if (failure) {
 | 
				
			||||||
		return EXIT_FAILURE;
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return EXIT_SUCCESS;
 | 
						return EXIT_SUCCESS;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										48
									
								
								wmenu-run.c
									
										
									
									
									
								
							
							
						
						
									
										48
									
								
								wmenu-run.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
#define _POSIX_C_SOURCE 200809L
 | 
					#define _POSIX_C_SOURCE 200809L
 | 
				
			||||||
#include <dirent.h>
 | 
					#include <dirent.h>
 | 
				
			||||||
#include <stdio.h>
 | 
					#include <errno.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));
 | 
								menu_add_item(menu, strdup(ent->d_name), true);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		closedir(dir);
 | 
							closedir(dir);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	menu_sort_and_deduplicate(menu);
 | 
					 | 
				
			||||||
	free(path);
 | 
						free(path);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct command {
 | 
					struct executable {
 | 
				
			||||||
	struct menu *menu;
 | 
						struct menu *menu;
 | 
				
			||||||
	char *text;
 | 
						char *name;
 | 
				
			||||||
	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 command *cmd = data;
 | 
						struct executable *exe = data;
 | 
				
			||||||
	xdg_activation_token_v1_destroy(activation_token);
 | 
						xdg_activation_token_v1_destroy(activation_token);
 | 
				
			||||||
 | 
						menu_destroy(exe->menu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int pid = fork();
 | 
						setenv("XDG_ACTIVATION_TOKEN", token, true);
 | 
				
			||||||
	if (pid == 0) {
 | 
						execlp(exe->name, exe->name, NULL);
 | 
				
			||||||
		setenv("XDG_ACTIVATION_TOKEN", token, true);
 | 
					
 | 
				
			||||||
		char *argv[] = {"/bin/sh", "-c", cmd->text, NULL};
 | 
						fprintf(stderr, "Failed to execute selection: %s\n", strerror(errno));
 | 
				
			||||||
		execvp(argv[0], (char**)argv);
 | 
						free(exe->name);
 | 
				
			||||||
	} else {
 | 
						free(exe);
 | 
				
			||||||
		if (cmd->exit) {
 | 
						exit(EXIT_FAILURE);
 | 
				
			||||||
			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_item(struct menu *menu, char *text, bool exit) {
 | 
					static void exec(struct menu *menu) {
 | 
				
			||||||
	struct command *cmd = calloc(1, sizeof(struct command));
 | 
						if (!menu->sel) {
 | 
				
			||||||
	cmd->menu = menu;
 | 
							return;
 | 
				
			||||||
	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, cmd);
 | 
						xdg_activation_token_v1_add_listener(activation_token, &activation_token_listener, exe);
 | 
				
			||||||
	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(exec_item);
 | 
						struct menu *menu = menu_create();
 | 
				
			||||||
 | 
						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
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								wmenu.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
#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"
 | 
				
			||||||
| 
						 | 
					@ -13,20 +12,12 @@ static void read_items(struct menu *menu) {
 | 
				
			||||||
		if (p) {
 | 
							if (p) {
 | 
				
			||||||
			*p = '\0';
 | 
								*p = '\0';
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		menu_add_item(menu, strdup(buf));
 | 
							menu_add_item(menu, strdup(buf), false);
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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(print_item);
 | 
						struct menu *menu = menu_create();
 | 
				
			||||||
	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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue