summaryrefslogtreecommitdiffstats
path: root/xlnch.c
diff options
context:
space:
mode:
authorgramanas <anastasis.gramm2@gmail.com>2020-07-20 19:30:46 +0300
committerAnastasios Grammenos <anastasios.grammenos@noris.gr>2020-12-02 14:04:55 +0200
commitfa2a1b1e6d31f3b21e50ea5730a8fb5967d087e8 (patch)
tree51bdd30a10afc582f6692646a4a9dcbb11e238da /xlnch.c
downloadxlnch-fa2a1b1e6d31f3b21e50ea5730a8fb5967d087e8.tar.gz
xlnch-fa2a1b1e6d31f3b21e50ea5730a8fb5967d087e8.tar.bz2
xlnch-fa2a1b1e6d31f3b21e50ea5730a8fb5967d087e8.zip
Initial
Diffstat (limited to 'xlnch.c')
-rw-r--r--xlnch.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/xlnch.c b/xlnch.c
new file mode 100644
index 0000000..a5ffd62
--- /dev/null
+++ b/xlnch.c
@@ -0,0 +1,371 @@
+/**
+ * xlnch -- D e s c r i p t i o n
+ * Copyright (C) 2019 Anastasis Grammenos
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* Basic X code from:
+ * Brian Hammond 2/9/96. http://mech.math.msu.su/~nap/2/GWindow/xintro.html
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <wordexp.h>
+#include <X11/Xlib.h>
+#include <X11/Xos.h>
+#include <X11/Xutil.h>
+
+#include "util.h"
+
+#define BUF_SIZE 2048
+#define MAX_ENTRIES 32
+
+#define FONTNAME "-*-terminus-medium-*-*-*-16-*-*-*-*-*-*-*"
+
+char buf[BUF_SIZE] = "", cur[BUF_SIZE] = "";
+
+struct lnch_t {
+ int exits;
+ char key;
+ char desc[512];
+ char cmd[1024];
+} lnch[MAX_ENTRIES];
+
+unsigned int idx = 0;
+
+Colormap cm;
+Display *display;
+GC gc;
+int screen;
+Window root, win;
+XColor red;
+XFontStruct *font;
+
+int w_width = 0;
+int w_height = 0;
+
+void get_curson_pos(int *x, int *y)
+{
+ int win_x_return, win_y_return;
+ unsigned int mask_return;
+ Window root_return, child_return;
+
+ XQueryPointer(display, root, &root_return, &child_return,
+ x, y, &win_x_return, &win_y_return, &mask_return);
+}
+
+void init()
+{
+ XWindowAttributes wa;
+ XSetWindowAttributes swa;
+
+ display = XOpenDisplay(NULL);
+ screen = DefaultScreen(display);
+ root = RootWindow(display, screen);
+ if (!XGetWindowAttributes(display, root, &wa))
+ die("could not get embedding window attributes: 0x%lx", root);
+
+ gc = XCreateGC(display, root, 0, NULL);
+ XSetLineAttributes(display, gc, 1, LineSolid, CapButt, JoinMiter);
+
+ cm = DefaultColormap(display, screen);
+ if (!XAllocNamedColor(display, cm, "red", &red, &red)) {
+ fprintf(stderr,
+ "XAllocNamedColor - failed to allocated 'red' color.\n");
+ exit(1);
+ }
+
+ swa.override_redirect = True;
+ swa.background_pixel = BlackPixel(display, screen);
+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+
+ int x, y;
+ get_curson_pos(&x, &y);
+
+ int height = 16 + (9 + 3) * w_height - 3 + 16,
+ width = 16 + w_width * 11;
+ win = XCreateWindow(display, root, x, y, width, height, 0,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWOverrideRedirect | CWBackPixel | CWEventMask,
+ &swa);
+
+ font = XLoadQueryFont(display, FONTNAME);
+ /* If the font could not be loaded, revert to the "fixed" font. */
+ if (!font) {
+ fprintf(stderr, "unable to load font %s: using fixed\n", FONTNAME);
+ font = XLoadQueryFont(display, "fixed");
+ }
+ XSetFont(display, gc, font->fid);
+
+ XMapRaised(display, win);
+ XRaiseWindow(display, win);
+}
+
+void close_x()
+{
+ XFreeGC(display, gc);
+ XDestroyWindow(display, win);
+ XCloseDisplay(display);
+}
+
+int parse_line(char *line)
+{
+ char *token;
+ char delim[2] = ":";
+
+ /* Copy buf to cur for error reporting */
+ strcpy(cur, buf);
+
+ trim(buf);
+
+ if (strlen(line) == 0 || line[0] == '#')
+ return 2;
+
+ lnch[idx].exits = 1;
+
+ if (line[0] == '&') {
+ lnch[idx].exits = 0;
+ line++;
+ }
+
+ /* Get key entry */
+ token = strtok(line, delim);
+
+ if (strlen(token) != 1)
+ return 1;
+
+ lnch[idx].key = token[0];
+ line += 2; // pass the key, pass the `:`
+
+ /* Get description */
+ token = strtok(NULL, delim);
+
+ if (!token)
+ return 1;
+
+ strcpy(lnch[idx].desc, token);
+ line += (strlen(token) + 1); // pass the decription, pass the `:`
+
+ /* Get command */
+ strcpy(lnch[idx].cmd, line);
+
+ /* If description is epmty command becomes the descritpion */
+ if (!strlen(lnch[idx].cmd))
+ strcpy(lnch[idx].cmd, lnch[idx].desc);
+
+ w_width = MAX((int) strlen(lnch[idx].desc), w_width);
+ idx++;
+ return 0;
+}
+
+int run_cmd(char *cmd)
+{
+ wordexp_t p;
+ int rc = wordexp(cmd, &p, 0);
+ switch (rc) {
+ case WRDE_BADCHAR:
+ printf
+ ("Illegal occurrence of newline or one of |, &, ;, <, >, (, ), {, }.\n");
+ break;
+ case WRDE_BADVAL:
+ printf
+ ("An undefined shell variable was referenced, and the WRDE_UNDEF flag told us to consider this an error.\n");
+ break;
+ case WRDE_CMDSUB:
+ printf
+ ("Command substitution requested, but the WRDE_NOCMD flag told us to consider this an error.\n");
+ break;
+ case WRDE_NOSPACE:
+ printf("Out of memory.\n");
+ break;
+ case WRDE_SYNTAX:
+ printf
+ ("Shell syntax error, such as unbalanced parentheses or unmatched quotes.\n");
+ break;
+ default:
+ printf("Running:");
+ for (size_t i = 0; i < p.we_wordc; i++) {
+ printf(" %s", p.we_wordv[i]);
+ }
+ printf("\n");
+
+ /* double fork from i3wm's exec implementation */
+ if (fork() == 0) {
+ setsid();
+ if (fork() == 0) {
+ execvp(p.we_wordv[0], p.we_wordv);
+ /* never reached */
+ }
+ _exit(EXIT_SUCCESS);
+ }
+ wait(0);
+ }
+ wordfree(&p);
+ return rc;
+}
+
+void grabfocus()
+{
+ struct timespec ts = {.tv_sec = 0,.tv_nsec = 10000000 };
+ Window focuswin;
+ int i, revertwin;
+
+ for (i = 0; i < 100; ++i) {
+ XGetInputFocus(display, &focuswin, &revertwin);
+ if (focuswin == win)
+ return;
+ XSetInputFocus(display, win, RevertToParent, CurrentTime);
+ nanosleep(&ts, NULL);
+ }
+ printf("cannot grab focus\n");
+}
+
+void grabkeyboard()
+{
+ struct timespec ts = {.tv_sec = 0,.tv_nsec = 1000000 };
+ int i;
+
+ /* try to grab keyboard, we may have to wait for another process to ungrab */
+ for (i = 0; i < 1000; i++) {
+ if (XGrabKeyboard(display, root, True, GrabModeAsync,
+ GrabModeAsync, CurrentTime) == GrabSuccess)
+ return;
+ nanosleep(&ts, NULL);
+ }
+ printf("Cannot grab keyboard\n");
+}
+
+void draw_text()
+{
+ char text[2048];
+ int x = 10, y = 2;
+ for (unsigned int i = 0; i < idx; i++) {
+ if (lnch[i].exits) {
+ XSetForeground(display, gc, WhitePixel(display, screen));
+ } else {
+ XSetForeground(display, gc, red.pixel);
+ }
+ sprintf(text, "%c: %s", lnch[i].key, lnch[i].desc);
+ XDrawString(display, win, gc, x, y += 13, text, strlen(text));
+ }
+}
+
+int run()
+{
+ XEvent event;
+ KeySym keysym;
+ char text[255];
+ int flag, rc;
+
+ init();
+ draw_text();
+
+ grabkeyboard();
+ grabfocus();
+
+ while (1) {
+ XNextEvent(display, &event);
+
+ if (event.type == Expose && event.xexpose.count == 0) {
+ /* the window was exposed redraw it! */
+ XClearWindow(display, win);
+ draw_text();
+ }
+
+ /* Keep window in view */
+ else if (event.type == VisibilityNotify &&
+ event.xvisibility.state != VisibilityUnobscured)
+ XRaiseWindow(display, win);
+
+ else if (event.type == KeyPress) {
+ /* C-g and ESC quit */
+ if ((event.xkey.state == 4 && event.xkey.keycode == 42) /* => C-g */
+ ||event.xkey.keycode == 9) { /* => ESC */
+ close_x();
+ return 0;
+ }
+
+ XLookupString(&event.xkey, text, 255, &keysym, 0);
+ flag = 0;
+
+ for (unsigned int i = 0; i < idx; i++) {
+ if (lnch[i].key == text[0]) {
+ rc = run_cmd(lnch[i].cmd);
+ flag = 1;
+ if (lnch[i].exits) {
+ close_x();
+ return rc;
+ }
+ }
+ }
+
+ /* Q and q quit as well, as long as they don't match anything */
+ if (!flag && event.xkey.keycode == 24) { /* Q or q */
+ close_x();
+ return 0;
+ }
+ }
+
+ else if (event.type == FocusIn) {
+ if (event.xfocus.window != win) {
+ grabfocus();
+ }
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int rc, n = 1;
+ FILE *f;
+
+ if ((argc > 1) &&
+ (!strcmp(argv[1], "--help") ||
+ !strcmp(argv[1], "-h") || !strcmp(argv[1], "-?"))) {
+ printf("Usage:\n%s [xlnchrc]\n", argv[0]);
+ printf("\nxlnchrc format:\n[&]<key>:[<description>]:<command>\n");
+ return 0;
+ }
+
+ if (argc == 1 || !strcmp(argv[1], "-"))
+ f = stdin;
+ else {
+ f = fopen(argv[1], "r");
+ if (!f)
+ die("Cannot open file %s", argv[1]);
+ }
+
+ while (fgets(buf, BUF_SIZE, f)) {
+ rc = parse_line(buf);
+ if (rc == 1) {
+ fprintf(stderr, "%s:%d: cannot parse ā€˜%sā€™\n",
+ argc == 1
+ || !strcmp(argv[1], "-") ? "stdin" : argv[1], n, cur);
+ } else if (rc == 0) {
+ w_height++;
+ }
+ n++;
+ }
+
+ if (f != stdin)
+ fclose(f);
+
+ return run();
+}