suggestion picker: a persistent layer to complement virtual keyboards like wvkbd
-rw-r--r--.gitignore1
-rw-r--r--Makefile11
-rw-r--r--README.md6
-rw-r--r--config.mk2
-rw-r--r--drw.c18
-rw-r--r--drw.h1
-rw-r--r--keyboard.c8
-rw-r--r--main.c169
-rw-r--r--suggpicker.1.scd21
9 files changed, 153 insertions, 84 deletions
diff --git a/.gitignore b/.gitignore
index f2595a2..ef21f4e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@
.gdb_history
*.log
suggpicker
+suggpicker.1
diff --git a/Makefile b/Makefile
index ac9250b..4c9f28b 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index ea2dca5..4d938e4 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/config.mk b/config.mk
index f66ced1..fd9f467 100644
--- a/config.mk
+++ b/config.mk
@@ -1,2 +1,2 @@
-VERSION = 0.1.0
+VERSION = 0.1.4
CFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=700
diff --git a/drw.c b/drw.c
index a7cfd20..6ea904e 100644
--- a/drw.c
+++ b/drw.c
@@ -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) {
diff --git a/drw.h b/drw.h
index fb3df1c..c1139f4 100644
--- a/drw.h
+++ b/drw.h
@@ -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;
diff --git a/keyboard.c b/keyboard.c
index bfdfdc9..293a2f6 100644
--- a/keyboard.c
+++ b/keyboard.c
@@ -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);
}
}
diff --git a/main.c b/main.c
index 311c4e9..848041b 100644
--- a/main.c
+++ b/main.c
@@ -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)