suggestion picker: a persistent layer to complement virtual keyboards like wvkbd
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 11 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | config.mk | 2 | ||||
| -rw-r--r-- | drw.c | 18 | ||||
| -rw-r--r-- | drw.h | 1 | ||||
| -rw-r--r-- | keyboard.c | 8 | ||||
| -rw-r--r-- | main.c | 169 | ||||
| -rw-r--r-- | suggpicker.1.scd | 21 |
9 files changed, 153 insertions, 84 deletions
@@ -5,3 +5,4 @@ .gdb_history *.log suggpicker +suggpicker.1 @@ -2,10 +2,12 @@ include config.mk NAME=suggpicker BIN=${NAME} +MAN=${NAME}.1 SRC=. -PKGS = wayland-client xkbcommon pangocairo +PKGS = wayland-client pangocairo +PREFIX?=/usr/local MY_SOURCES += $(wildcard $(SRC)/*.c) MY_HEADERS += $(wildcard $(SRC)/*.h) @@ -21,7 +23,7 @@ SOURCES = $(MY_SOURCES) $(WAYLAND_SRC) OBJECTS = $(SOURCES:.c=.o) -all: ${BIN} +all: ${BIN} ${MAN} proto/%-client-protocol.c: proto/%.xml wayland-scanner code < $? > $@ @@ -34,6 +36,9 @@ $(OBJECTS): $(HDRS) $(MY_HEADERS) ${BIN}: config.h $(OBJECTS) $(CC) -o ${BIN} $(OBJECTS) $(LDFLAGS) +${MAN}: ${MAN}.scd + scdoc < $? > $@ + clean: rm -f $(OBJECTS) $(HDRS) $(WAYLAND_SRC) ${BIN} @@ -44,3 +49,5 @@ install: all mkdir -p ${DESTDIR}${PREFIX}/bin cp -f ${BIN} ${DESTDIR}${PREFIX}/bin chmod 755 ${DESTDIR}${PREFIX}/bin/${BIN} + mkdir -p ${DESTDIR}${PREFIX}/share/man/man1 + cp -f ${MAN} ${DESTDIR}${PREFIX}/share/man/man1 @@ -13,15 +13,15 @@ Each line of input is treated as TSV. You'll need the following developer packages - - pangocairo + - cairo + - pango - wayland-client - - xkbcommon Make any customizations you would like in `config.h` and run `make` ## Usage -The keyboard can be hidden by sending it a `SIGUSR1` signal and shown again by sending it `SIGUSR2`. This saves some +The keyboard can be hidden by sending it a `SIGUSR1` signal, shown again by sending it `SIGUSR2`, or toggled by sending it `SIGRTMIN`. This saves some start up time and may be appropriate in some low-resource environments. ## Contribute @@ -1,2 +1,2 @@ -VERSION = 0.1.0 +VERSION = 0.1.4 CFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=700 @@ -20,31 +20,13 @@ drwsurf_resize(struct drwsurf *ds, uint32_t w, uint32_t h, uint32_t s) { setup_buffer(ds); } -static void surface_frame_callback(void *data, struct wl_callback *cb, - uint32_t time); - -static struct wl_callback_listener frame_listener = {.done = - surface_frame_callback}; - void drwsurf_flip(struct drwsurf *ds) { - ds->cb = wl_surface_frame(ds->surf); - wl_callback_add_listener(ds->cb, &frame_listener, (void *)ds); - wl_surface_attach(ds->surf, ds->buf, 0, 0); wl_surface_commit(ds->surf); } void -surface_frame_callback(void *data, struct wl_callback *cb, uint32_t time) { - struct drwsurf *ds = (struct drwsurf *)data; - wl_callback_destroy(cb); - ds->cb = NULL; - - drwsurf_flip(ds); -} - -void drw_draw_text(struct drwsurf *d, Color color, uint32_t x, uint32_t y, uint32_t w, uint32_t h, const char *label) { @@ -15,7 +15,6 @@ struct drwsurf { struct wl_surface *surf; struct wl_buffer *buf; struct wl_shm *shm; - struct wl_callback *cb; unsigned char *pool_data; cairo_t *cairo; @@ -29,11 +29,16 @@ kbd_init_suggs(struct key *suggs, uint32_t width, uint32_t height) { struct key *k = suggs; double rowlength = kbd_get_row_length(k); + uint8_t i = 0; while (k->label != NULL) { k->x = x; k->y = y; k->w = width / rowlength; x += k->w; + i++; + if (x < (width * i) / rowlength) { + k->w++; x++; + } k->h = keyheight; k++; } @@ -68,6 +73,7 @@ kbd_unpress_key(struct kbd *kb) { if (kb->last_press) { kbd_draw_key(kb, kb->last_press, Unpress); kb->last_press = NULL; + drwsurf_flip(kb->surf); } } @@ -78,7 +84,6 @@ kbd_release_key(struct kbd *kb) { // Important so autocompleted words get typed in time fflush(stdout); kbd_unpress_key(kb); - kbd_draw_layout(kb); } } @@ -99,6 +104,7 @@ kbd_press_key(struct kbd *kb, struct key *k) { if (k->label) { kb->last_press = k; kbd_draw_key(kb, k, Press); + drwsurf_flip(kb->surf); } } @@ -4,6 +4,7 @@ #include <stdlib.h> #include <string.h> #include <sys/mman.h> +#include <sys/signalfd.h> #include <unistd.h> #include <wayland-client.h> #include <wchar.h> @@ -17,7 +18,7 @@ exit(1) /* client state */ -static const char *namespace = "wlroots"; +static const char *namespace = "suggpicker"; static struct wl_display *display; static struct wl_compositor *compositor; static struct wl_seat *seat; @@ -43,6 +44,7 @@ static int cur_x = -1, cur_y = -1; static bool cur_press = false; static struct kbd keyboard; static uint32_t height, normal_height, landscape_height; +static bool hidden = false; /* event handler prototypes */ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, @@ -238,19 +240,27 @@ static void display_handle_geometry(void *data, struct wl_output *wl_output, int x, int y, int physical_width, int physical_height, int subpixel, const char *make, const char *model, int transform) { - if (transform % 2 == 0 && keyboard.landscape) { - keyboard.landscape = false; - height = normal_height; - } else if (transform % 2 != 0 && !keyboard.landscape) { - keyboard.landscape = true; + // Swap width and height on rotated displays + if (transform % 2 != 0) { + int tmp = physical_width; + physical_width = physical_height; + physical_height = tmp; + } + bool landscape = physical_width > physical_height; + if (landscape == keyboard.landscape) + return; + keyboard.landscape = landscape; + if (keyboard.landscape) { height = landscape_height; } else { - return; // no changes + height = normal_height; } - zwlr_layer_surface_v1_set_size(layer_surface, 0, height); - zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, height); - wl_surface_commit(draw_surf.surf); + if (layer_surface) { + zwlr_layer_surface_v1_set_size(layer_surface, 0, height); + zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, height); + wl_surface_commit(draw_surf.surf); + } } static void @@ -290,7 +300,6 @@ handle_global(void *data, struct wl_registry *registry, uint32_t name, } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); - } } @@ -300,13 +309,16 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) {} void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) { - if (w != keyboard.w || h != keyboard.h) { + if (w != keyboard.w || h != keyboard.h || hidden) { keyboard.w = w; keyboard.h = h; kbd_resize(&keyboard); + hidden = false; } zwlr_layer_surface_v1_ack_configure(surface, serial); + + drwsurf_flip(&draw_surf); } void @@ -331,24 +343,26 @@ usage(char *argv0) { void hide(int sigint) { - signal(SIGUSR1, hide); + if (keyboard.debug) + fprintf(stderr, "hiding keyboard\n"); if (!layer_surface) { + if (keyboard.debug) + fprintf(stderr, "can't hide nothing\n"); return; } - zwlr_layer_surface_v1_destroy(layer_surface); wl_surface_destroy(draw_surf.surf); layer_surface = NULL; - if (draw_surf.cb) { - wl_callback_destroy(draw_surf.cb); - draw_surf.cb = NULL; - } + hidden = true; } void show(int sigint) { - signal(SIGUSR2, show); + if (keyboard.debug) + fprintf(stderr, "showing keyboard\n"); if (layer_surface) { + if (keyboard.debug) + fprintf(stderr, "already shown\n"); return; } @@ -366,12 +380,19 @@ show(int sigint) { zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, NULL); wl_surface_commit(draw_surf.surf); - - wl_display_roundtrip(display); - drwsurf_flip(&draw_surf); } void +toggle_visibility(int sigint) { + if (keyboard.debug) + fprintf(stderr, "toggling visibility\n"); + if (hidden) { + show(sigint); + } else { + hide(sigint); + } +} +void handle_input(FILE *fd, struct key *sugg, struct kbd *kb) { char *line; line = malloc(1024); @@ -381,29 +402,35 @@ handle_input(FILE *fd, struct key *sugg, struct kbd *kb) { int i; struct key *key = sugg; char *l = line; - for (i = 0; l[i+1]; i++) { + for (i = 0; l[i + 1]; i++) { if (l[i] == '\t') { free(key->label); - key->label = strndup(l,i); + key->label = strndup(l, i); l += i + 1; i = 0; key++; } else if (l[i] == '\n') { break; } - } + } free(key->label); - key->label = strndup(l,i); + key->label = strndup(l, i); key++; free(key->label); key->label = NULL; kbd_init_suggs(sugg, kb->w, kb->h); kbd_draw_layout(kb); + drwsurf_flip(kb->surf); } free(line); } +void +pipewarn(int sigint) { + fprintf(stderr, "suggpicker: cannot pipe data out.\n"); +} + int main(int argc, char **argv) { /* parse command line arguments */ @@ -414,8 +441,6 @@ main(int argc, char **argv) { /* keyboard settings */ keyboard.scheme = scheme; - bool starthidden = false; - int i; for (i = 1; argv[i]; i++) { if ((!strcmp(argv[i], "-v")) || (!strcmp(argv[i], "--version"))) { @@ -424,11 +449,6 @@ main(int argc, char **argv) { } else if ((!strcmp(argv[i], "-h")) || (!strcmp(argv[i], "--help"))) { usage(argv[0]); exit(0); - } else if (!strcmp(argv[i], "-l")) { - if (i >= argc - 1) { - usage(argv[0]); - exit(1); - } } else if (!strcmp(argv[i], "-H")) { if (i >= argc - 1) { usage(argv[0]); @@ -447,7 +467,7 @@ main(int argc, char **argv) { fc_font_pattern = estrdup(argv[++i]); } else if ((!strcmp(argv[i], "-hidden")) || (!strcmp(argv[i], "--hidden"))) { - starthidden = true; + hidden = true; } else { fprintf(stderr, "Invalid argument: %s\n", argv[i]); usage(argv[0]); @@ -486,36 +506,69 @@ main(int argc, char **argv) { draw_ctx.font_description = pango_font_description_from_string(fc_font_pattern); - if (!starthidden) { + if (!hidden) { show(0); - } else { - signal(SIGUSR2, show); } - signal(SIGUSR1, hide); - // We need a more complicated event loop than wayland's default. - struct pollfd fds[2]; - fds[0].fd = STDIN_FILENO; - fds[0].events = POLLIN; - fds[1].fd = wl_display_get_fd(display); - fds[1].events = POLLIN; - // Initial dispatch so that bar appears (and can take events). - wl_display_dispatch(display); + struct pollfd fds[3]; + int WAYLAND_FD = 0; + int SIGNAL_FD = 1; + int STDIN_FD = 2; + fds[WAYLAND_FD].events = POLLIN; + fds[SIGNAL_FD].events = POLLIN; + fds[STDIN_FD].events = POLLIN; + + fds[WAYLAND_FD].fd = wl_display_get_fd(display); + if (fds[WAYLAND_FD].fd == -1) { + die("Failed to get wayland_fd: %d\n", errno); + } + + sigset_t signal_mask; + sigemptyset(&signal_mask); + sigaddset(&signal_mask, SIGUSR1); + sigaddset(&signal_mask, SIGUSR2); + sigaddset(&signal_mask, SIGRTMIN); + sigaddset(&signal_mask, SIGPIPE); + if (sigprocmask(SIG_BLOCK, &signal_mask, NULL) == -1) { + die("Failed to disable handled signals: %d\n", errno); + } + fds[SIGNAL_FD].fd = signalfd(-1, &signal_mask, 0); + if (fds[SIGNAL_FD].fd == -1) { + die("Failed to get signalfd: %d\n", errno); + } + + fds[STDIN_FD].fd = STDIN_FILENO; + while (run_display) { - while(layer_surface && poll(fds, 2, -1) != -1) { - if (fds[0].revents & POLLIN) { - handle_input(stdin, keyboard.suggs, &keyboard); - } - if (wl_display_dispatch(display) == -1) { - break; - } - wl_display_flush(display); + wl_display_flush(display); + poll(fds, 3, -1); + if (fds[STDIN_FD].revents & POLLIN) { + handle_input(stdin, keyboard.suggs, &keyboard); + } + if (fds[WAYLAND_FD].revents & POLLIN) { + wl_display_dispatch(display); + } + if (fds[WAYLAND_FD].revents & POLLERR) { + die("Exceptional condition on wayland socket.\n"); } - wl_display_roundtrip(display); - while (run_display && !layer_surface) { - // Hidden - sleep(1); + if (fds[WAYLAND_FD].revents & POLLHUP) { + die("Wayland socket has been disconnected.\n"); + } + + if (fds[SIGNAL_FD].revents & POLLIN) { + struct signalfd_siginfo si; + + if (read(fds[SIGNAL_FD].fd, &si, sizeof(si)) != sizeof(si)) + fprintf(stderr, "Signal read error: %d", errno); + else if (si.ssi_signo == SIGUSR1) + hide(0); + else if (si.ssi_signo == SIGUSR2) + show(0); + else if (si.ssi_signo == SIGRTMIN) + toggle_visibility(0); + else if (si.ssi_signo == SIGPIPE) + pipewarn(0); } } diff --git a/suggpicker.1.scd b/suggpicker.1.scd new file mode 100644 index 0000000..9800255 --- /dev/null +++ b/suggpicker.1.scd @@ -0,0 +1,21 @@ +suggpicker(1) + +# NAME +suggpicker - floating bar for making decisions + +# SYNOPSIS + +suggpicker < choices.sock > selection.sock + +wvkbd -O | swipeGuess words.txt 5 | *suggpicker* | completelyTypeWord.sh + +# DESCRIPTION + +*suggpicker* is a persistent dynamic menu for Wayland. + +Each line of tab-separated choices it reads changes the choices on the bar. Choices which are clicked or tapped will be outputted into stdout, so it can be processed by another program. + +It's made to be used in conjunction with a virtual keyboard, which it will float above or below. + +# SEE ALSO +*bemenu*(1) *swipeGuess*(1) *wvkbd*(1) *SwipeBehavior*(7) |