@@ 1,10 1,12 @@
#include <errno.h>
#include <poll.h>
+#include <signal.h>
#include <sys/signalfd.h>
+#include <sys/wait.h>
#include <unistd.h>
-#include <signal.h>
#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
#include <X11/Xatom.h>
#include <X11/Xft/Xft.h>
@@ 26,6 28,7 @@ enum anchor {
struct options {
const char* font;
const char* name;
+ const char* action;
enum anchor anchor;
char sep;
size_t initial_buffer_size;
@@ 130,7 133,9 @@ static void x11_init(struct x11_state* x11, const struct options* opts)
&cls);
XFree(name.value);
- XSelectInput(dpy, xw, StructureNotifyMask | ExposureMask);
+ XSelectInput(dpy, xw,
+ StructureNotifyMask | ExposureMask
+ | KeyPressMask | ButtonPressMask | ButtonReleaseMask);
XSync(dpy, False);
@@ 377,8 382,33 @@ static void update_window_position(struct x11_state* x11,
XMoveResizeWindow(x11->dpy, x11->xw, x, y, ct->extent_x, ct->extent_y);
}
+static void trigger_action(const struct options* opts, const char* trigger)
+{
+ trace("triggering action");
+ if(!opts->action) {
+ return;
+ }
+
+ pid_t p = fork(); CHECK(p, "fork");
+ if(p == 0) {
+ p = fork(); CHECK(p, "fork");
+ if(p == 0) {
+ debug("running action (%d): %s", getpid(), opts->action);
+ int r = execl("/bin/sh", "sh", "-c", opts->action, trigger, NULL);
+ CHECK(r, "execl");
+ } else {
+ exit(0);
+ }
+ } else {
+ trace("waiting for intermediate child: %d", p);
+ int r = waitpid(p, NULL, 0);
+ CHECK(r, "waitpid");
+ }
+}
+
static void x11_handle_event(struct x11_state* x11, struct xft_state* xft,
- struct state* st, const struct content* ct)
+ struct state* st, const struct content* ct,
+ const struct options* opts)
{
while(XPending(x11->dpy)) {
XEvent ev;
@@ 415,6 445,24 @@ static void x11_handle_event(struct x11_state* x11, struct xft_state* xft,
draw_window(x11, xft);
} else if(ev.type == GraphicsExpose) {
} else if(ev.type == NoExpose) {
+ } else if(ev.type == KeyPress) {
+ KeySym ks = XkbKeycodeToKeysym(
+ x11->dpy, ev.xkey.keycode, 0,
+ ev.xkey.state & ShiftMask ? 1 : 0);
+ if(ks == XK_Escape || ks == XK_q
+ || (ks == XK_w && ev.xkey.state & ControlMask)) {
+ debug("keyboard triggered graceful shutdown");
+ st->running = 0;
+ } else if(ks == XK_Return) {
+ trigger_action(opts, "return");
+ } else if(ks == XK_space) {
+ trigger_action(opts, "space");
+ }
+ } else if (ev.type == ButtonPress) {
+ char buf[10];
+ snprintf(LIT(buf), "mouse%u", ev.xbutton.button);
+ trigger_action(opts, buf);
+ } else if (ev.type == ButtonRelease) {
} else {
warning("ignored event: type=%d", ev.type);
}
@@ 529,12 577,13 @@ static void print_usage(int fd, const char* prog)
dprintf(fd, "usage: %s [OPTION]...\n", prog);
dprintf(fd, "\n");
dprintf(fd, "options:\n");
- dprintf(fd, " -0 use a null character to separate messages (default is newline)\n");
- dprintf(fd, " -a DIR anchor window to border at DIR (N S W E NW SW NE SE)\n");
- dprintf(fd, " -t NAME set window title to NAME\n");
- dprintf(fd, " -f FONT use FONT\n");
- dprintf(fd, " -H don't close the window when stdin closes\n");
- dprintf(fd, " -h print this message\n");
+ dprintf(fd, " -0 use a null character to separate messages (default is newline)\n");
+ dprintf(fd, " -a DIR anchor window to border at DIR (N S W E NW SW NE SE)\n");
+ dprintf(fd, " -t NAME set window title to NAME\n");
+ dprintf(fd, " -f FONT use FONT\n");
+ dprintf(fd, " -H don't close the window when stdin closes\n");
+ dprintf(fd, " -A ACTION run ACTION (like system(3) does) when enter, space or mouse buttons are pressed ($0 is bound to the relevant button)\n");
+ dprintf(fd, " -h print this message\n");
}
static void parse_options(struct options* o, int argc, char* argv[])
@@ 545,9 594,10 @@ static void parse_options(struct options* o, int argc, char* argv[])
o->initial_buffer_size = 128;
o->font = "sans:pixelsize=34";
o->hang = 0;
+ o->action = NULL;
int res;
- while((res = getopt(argc, argv, "a:0t:f:Hh")) != -1) {
+ while((res = getopt(argc, argv, "a:0t:f:HA:h")) != -1) {
switch(res) {
case 'a':
if(strcmp(optarg, "N") == 0) {
@@ 585,6 635,10 @@ static void parse_options(struct options* o, int argc, char* argv[])
case 'H':
o->hang = 1;
break;
+ case 'A':
+ o->action = strdup(optarg);
+ CHECK_MALLOC(o->action);
+ break;
case 'h':
default:
print_usage(res == 'h' ? 1 : 2, argv[0]);
@@ 633,13 687,14 @@ int main(int argc, char* argv[])
while(st.running) {
int r = poll(fds, 2 + receiving_input, -1);
CHECK(r, "poll");
+ trace("poll");
if(fds[0].revents & POLLIN) {
signalfd_handle_event(sfd, &st);
}
if(fds[1].revents & POLLIN) {
- x11_handle_event(&x11, &xft, &st, &ct);
+ x11_handle_event(&x11, &xft, &st, &ct, &opts);
}
if(receiving_input) {
@@ 653,8 708,10 @@ int main(int argc, char* argv[])
}
if(fds[2].revents & POLLHUP) {
+ trace("HUP");
receiving_input = 0;
if(!opts.hang) {
+ debug("HUP and not hanging: initiate graceful shutdown");
st.running = 0;
}
}