Compare commits
13 commits
Author | SHA1 | Date | |
---|---|---|---|
00478c32cb | |||
![]() |
e2542d34ed | ||
![]() |
3ad4b5ca3f | ||
![]() |
48ec172b4b | ||
![]() |
0947765fc9 | ||
![]() |
260eaba88e | ||
![]() |
12b8f83be4 | ||
![]() |
8b23811263 | ||
![]() |
7d717b3696 | ||
![]() |
a0df7959f9 | ||
![]() |
0fa9c35949 | ||
![]() |
30abca4f30 | ||
![]() |
15d7c7bcc2 |
9 changed files with 168 additions and 128 deletions
13
README.md
13
README.md
|
@ -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.
|
|
||||||
|
|
|
@ -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
103
menu.c
|
@ -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
22
menu.h
|
@ -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,
|
||||||
|
|
24
meson.build
24
meson.build
|
@ -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(
|
||||||
|
|
61
render.c
61
render.c
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
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 <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
13
wmenu.c
|
@ -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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue