From 1e5ee55ff70ca7abcc8c223ae34ff5b94e0a647d Mon Sep 17 00:00:00 2001 From: Justinas Grigas Date: Sat, 15 Jun 2024 21:13:12 +0100 Subject: [PATCH] qalc: calculator mode Updates over previous patch: - add flag to manpage synopsis. - uses poll instead of select (prevents hang when qalc is not installed). - follow suckless style. - qalc function names start with qalc. --- dmenu.1 | 5 +- dmenu.c | 160 +++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 133 insertions(+), 32 deletions(-) diff --git a/dmenu.1 b/dmenu.1 index 323f93c..ee5ca31 100644 --- a/dmenu.1 +++ b/dmenu.1 @@ -3,7 +3,7 @@ dmenu \- dynamic menu .SH SYNOPSIS .B dmenu -.RB [ \-bfiv ] +.RB [ \-bCfiv ] .RB [ \-l .IR lines ] .RB [ \-m @@ -40,6 +40,9 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. .B \-b dmenu appears at the bottom of the screen. .TP +.B \-C +dmenu becomes a calculator. +.TP .B \-f dmenu grabs the keyboard before reading stdin if not reading from a tty. This is faster, but will lock up X until stdin reaches end\-of\-file. diff --git a/dmenu.c b/dmenu.c index 40f93e0..67b7f02 100644 --- a/dmenu.c +++ b/dmenu.c @@ -7,6 +7,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include @@ -33,6 +38,12 @@ struct item { int out; }; +static struct { + pid_t pid; + int enable, in[2], out[2]; + char buf[256]; +} qalc; + static char text[BUFSIZ] = ""; static char *embed; static int bh, mw, mh; @@ -226,9 +237,78 @@ grabkeyboard(void) die("cannot grab keyboard"); } +static void +qalc_init(void) +{ + pipe(qalc.in); + pipe2(qalc.out, O_NONBLOCK); + qalc.pid = fork(); + if (qalc.pid == -1) + die("failed to fork for qalc"); + if (qalc.pid == 0) { + dup2(qalc.in[0], STDIN_FILENO); + dup2(qalc.out[1], STDOUT_FILENO); + close(qalc.in[1]); + close(qalc.out[0]); + prctl(PR_SET_PDEATHSIG, SIGTERM); + execl("/usr/bin/qalc", "qalc", "-c0", "-t", NULL); + die("execl qalc failed"); + } else { /* parent */ + close(qalc.in[0]); + close(qalc.out[1]); + items = malloc(sizeof(struct item) * 2); + items[0].text = malloc(LENGTH(qalc.buf)); + strcpy(items[0].text, "no result"); + items[1].out = 0; + items[1].text = NULL; + } +} + +static void +qalc_recv(void) +{ + ssize_t r = read(qalc.out[0], qalc.buf, LENGTH(qalc.buf)); + if (r < 0) + die("error reading qalc.out"); + if (qalc.buf[0] == '\n') { + int i; + for (i = 3; i < LENGTH(qalc.buf) && qalc.buf[i] != '\n'; ++i) + items[0].text[i - 3] = qalc.buf[i]; + items[0].text[i - 3] = 0; + if (r != LENGTH(qalc.buf)) + return; + } + while (read(qalc.out[0], qalc.buf, LENGTH(qalc.buf)) != -1) + ; /* empty the pipe */ + if (errno != EAGAIN && errno != EWOULDBLOCK) + die("error emptying qalc.out"); +} + +static void +qalc_send(void) +{ + int s = strlen(text); + text[s] = '\n'; + write(qalc.in[1], text, s + 1); + text[s] = 0; +} + +static void +qalc_match(void) +{ + matches = matchend = NULL; + appenditem(items, &matches, &matchend); + curr = sel = matches; + calcoffsets(); +} + static void match(void) { + if (qalc.enable) { + qalc_match(); + return; + } static char **tokv = NULL; static int tokn = 0; @@ -523,6 +603,9 @@ insert: break; } + if (qalc.enable) + qalc_send(); + draw: drawmenu(); } @@ -576,36 +659,46 @@ static void run(void) { XEvent ev; - - while (!XNextEvent(dpy, &ev)) { - if (XFilterEvent(&ev, win)) - continue; - switch(ev.type) { - case DestroyNotify: - if (ev.xdestroywindow.window != win) + int xfd = ConnectionNumber(dpy); + struct pollfd fds[] = { + {xfd, POLLIN, 0}, + {qalc.out[0], POLLIN, 0}, + }; + while (poll(fds, 2, -1) > 0) { + if (qalc.enable && fds[1].revents & POLLIN) { + qalc_recv(); + drawmenu(); + } + while (XPending(dpy) && !XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, win)) + continue; + switch (ev.type) { + case DestroyNotify: + if (ev.xdestroywindow.window != win) + break; + cleanup(); + exit(1); + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); break; - cleanup(); - exit(1); - case Expose: - if (ev.xexpose.count == 0) - drw_map(drw, win, 0, 0, mw, mh); - break; - case FocusIn: - /* regrab focus from parent window */ - if (ev.xfocus.window != win) - grabfocus(); - break; - case KeyPress: - keypress(&ev.xkey); - break; - case SelectionNotify: - if (ev.xselection.property == utf8) - paste(); - break; - case VisibilityNotify: - if (ev.xvisibility.state != VisibilityUnobscured) - XRaiseWindow(dpy, win); - break; + case FocusIn: + /* regrab focus from parent window */ + if (ev.xfocus.window != win) + grabfocus(); + break; + case KeyPress: + keypress(&ev.xkey); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + paste(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, win); + break; + } } } } @@ -715,7 +808,7 @@ setup(void) static void usage(void) { - die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + die("usage: dmenu [-bCfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]"); } @@ -732,6 +825,8 @@ main(int argc, char *argv[]) exit(0); } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ topbar = 0; + else if (!strcmp(argv[i], "-C")) /* enable calculator */ + qalc.enable = 1; else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ fast = 1; else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ @@ -782,7 +877,10 @@ main(int argc, char *argv[]) die("pledge"); #endif - if (fast && !isatty(0)) { + if (qalc.enable) { + qalc_init(); + grabkeyboard(); + } else if (fast && !isatty(0)) { grabkeyboard(); readstdin(); } else { -- 2.45.2