/** * 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; XColor red; Display *display; int screen; Window root, win; //Drawable drawable; GC gc; // XftColor *scheme; 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); 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*9; win = XCreateWindow(display, root, x, y, width, height, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); printf("Window pos: {%d, %d}", x, y); fflush(stdout); cm = DefaultColormap(display, screen); if (! XAllocNamedColor(display, cm, "red", &red, &red)) { fprintf(stderr, "XAllocNamedColor - failed to allocated 'red' color.\n"); exit(1); } 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] = ":"; lnch[idx].exits = 1; if (line[0] == '#') return 0; if (line[0] == '&') { lnch[idx].exits = 0; line++; } /* Get key entry */ token = strtok(line, delim); if (strlen(token) != 1) { printf("error on token %s", token) ; return 1; } lnch[idx].key = token[0]; line+=2; // pass the key, pass the `:` /* Get description */ token = strtok(NULL, delim); if (!token) return 1; w_width = MAX((int) strlen(token), w_width); 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].desc)) strcpy(lnch[idx].desc, line); 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 main (int argc, char *argv[]) { if (argc < 2) { printf("Usage:\n%s xlnchrc\n", argv[0]); printf("xlnchrc format:\n[&]::\n"); return -1; } FILE *f; if (!strcmp(argv[1], "-")) f = stdin; else { f = fopen(argv[1], "r"); /* printf("xlnchrc required\n"); */ /* return -1; */ } while (fgets(buf, BUF_SIZE, f)) { trim(buf); parse_line(buf); w_height++; } if (f != stdin) fclose(f); 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(); // system("sleep 100"); 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(); exit(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(); exit(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(); } } } }