/** * 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 #include #include #include #include #include #include #include #include #include #include "util.h" #define BUF_SIZE 2048 #define MAX_ENTRIES 32 char buf[BUF_SIZE] = ""; struct lnch_t { int exits; char key; char desc[512]; char cmd[1024]; }; struct lnch_t 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); const char * fontname = "-*-terminus-medium-*-*-*-16-*-*-*-*-*-*-*"; 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] = ":"; if (strlen(line) == 0 || line[0] == '#') return 0; 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 = 10; 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)); } } void redraw() { XClearWindow(display, win); draw_text(); } int run() { XEvent event; /* the XEvent declaration !!! */ KeySym key; /* a dealie-bob to handle KeyPress Events */ char text[255]; /* a char buffer for KeyPress Events */ int rc; init(); draw_text(); grabkeyboard(); grabfocus(); /* look for events forever... */ while(1) { /* get the next event and stuff it into our event variable. Note: only events we set the mask for are detected! */ XNextEvent(display, &event); if (event.type==Expose && event.xexpose.count==0) { /* the window was exposed redraw it! */ redraw(); } /* Keep window in view */ if (event.type==VisibilityNotify && event.xvisibility.state != VisibilityUnobscured) XRaiseWindow(display, win); if (event.type==KeyPress && XLookupString(&event.xkey,text,255,&key,0)==1) { /* use the XLookupString routine to convert the invent KeyPress data into regular text. Weird but necessary... */ if (text[0]=='q') { close_x(); return 0; } printf("You pressed the %c key!\n",text[0]); for (unsigned int i = 0; i < idx; i++) { if (lnch[i].key == text[0]) { rc = run_cmd(lnch[i].cmd); if (lnch[i].exits) { close_x(); return rc; } } } } if (event.type==ButtonPress) { /* tell where the mouse Button was Pressed */ int x=event.xbutton.x, y=event.xbutton.y; strcpy(text,">>xlnch<<"); XSetForeground(display,gc,rand()%255); XDrawString(display,win,gc,x,y, text, strlen(text)); } if (event.type==FocusIn) { if (event.xfocus.window != win) { grabfocus(); } } } } int main (int argc, char *argv[]) { 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[&]:[]:\n"); return 0; } FILE *f; if (argc == 1 || !strcmp(argv[1], "-")) f = stdin; else { f = fopen(argv[1], "r"); if (!f) die("Cannot open file %s", argv[1]); } int n = 1; char tmp[BUF_SIZE] = ""; while (fgets(buf, BUF_SIZE, f)) { trim(buf); strcpy(tmp, buf); if (parse_line(buf)) { fprintf(stderr, "%s:%d: cannot parse ā€˜%sā€™\n", argc == 1 || !strcmp(argv[1], "-") ? "stdin": argv[1], n, tmp); } else { w_height++; } n++; } if (f != stdin) fclose(f); return run(); }