diff options
Diffstat (limited to 'xlnch.c')
-rw-r--r-- | xlnch.c | 362 |
1 files changed, 362 insertions, 0 deletions
@@ -0,0 +1,362 @@ +/** + * 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 <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xos.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include <sys/wait.h> + +#include <wordexp.h> + + +Display *dis; +int screen; +Window win; +GC gc; + +Colormap cm; +XColor red; + +void init_x() +{ + /* get the colors black and white (see section for details) */ + unsigned long black,white; + + /* use the information from the environment variable DISPLAY + to create the X connection: + */ + dis=XOpenDisplay((char *)0); + screen=DefaultScreen(dis); + black=BlackPixel(dis,screen); + white=WhitePixel(dis, screen); + + /* once the display is initialized, create the window. + This window will be have be 200 pixels across and 300 down. + It will have the foreground white and background black + */ + win=XCreateSimpleWindow(dis,DefaultRootWindow(dis),0,0, + 200, 300, 1, white, black); + + /* here is where some properties of the window can be set. + The third and fourth items indicate the name which appears + at the top of the window and the name of the minimized window + respectively. + */ + XSetStandardProperties(dis,win,"xlnch","xlnch",None,NULL,0,NULL); + + XClassHint *hint = XAllocClassHint(); + if (hint) { + hint->res_name = "xlnch"; + hint->res_class = "xlnch"; + } + XSetClassHint(dis,win,hint); + XFree(hint); + + /* this routine determines which types of input are allowed in + the input. see the appropriate section for details... + */ + XSelectInput(dis, win, ExposureMask|ButtonPressMask|KeyPressMask); + + /* create the Graphics Context */ + gc=XCreateGC(dis, win, 0,0); + + /* here is another routine to set the foreground and background + colors _currently_ in use in the window. + */ + XSetBackground(dis,gc,black); + XSetForeground(dis,gc,white); + + cm = DefaultColormap(dis, screen); + if (! XAllocNamedColor(dis, cm, "red", &red, &red)) { + fprintf(stderr, "XAllocNamedColor - failed to allocated 'red' color.\n"); + exit(1); + } + + /* Font font = XLoadFont(dis,"*mono*"); */ + /* XSetFont(dis,gc,font); */ + + /* clear the window and bring it on top of the other windows */ + XClearWindow(dis, win); + XMapRaised(dis, win); +} + +void close_x() +{ + XFreeGC(dis, gc); + XDestroyWindow(dis,win); + XCloseDisplay(dis); +} + + +#define BUF_SIZE 2048 +char buf[BUF_SIZE] = ""; + +struct lnch_t { + int exits; + char key; + char desc[512]; + char cmd[1024]; +}; + +struct lnch_t lnch[32]; +unsigned int idx = 0; + +void trim(char * str) +{ + int i; + int begin = 0; + int end = strlen(str) - 1; + + while (isspace((unsigned char) str[begin])) + begin++; + + while (isspace((unsigned char) str[end]) && (end >= begin)) + end--; + + /* Shift all characters back to the start of the string array. */ + for (i = begin; i <= end; i++) + str[i - begin] = str[i]; + + str[i - begin] = '\0'; +} + +int parse_line(char * line) +{ + char *token; + char delim[2] = ":"; + + lnch[idx].exits = 1; + + 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); + 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(dis, &focuswin, &revertwin); + if (focuswin == win) + return; + XSetInputFocus(dis, 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(dis, DefaultRootWindow(dis), 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(dis,gc,WhitePixel(dis,DefaultScreen(dis))); + else + XSetForeground(dis,gc,red.pixel); + sprintf(text, "%c: %s", lnch[i].key, lnch[i].desc); + XDrawString(dis,win,gc,x,y+=13, text, strlen(text)); + } +} + +void redraw() +{ + XClearWindow(dis, win); + draw_text(); +} + +int main (int argc, char *argv[]) +{ + if (argc < 2) { + printf("Usage:\n%s xlnchrc\n", argv[0]); + printf("xlnchrc format:\n[&]<key>:<description>:<command>\n"); + return -1; + } + FILE *f; + f = fopen(argv[1], "r"); + + if (!f) { + printf("xlnchrc required\n"); + return -1; + } + + while (fgets(buf, BUF_SIZE, f)) { + trim(buf); + parse_line(buf); + } + + 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_x(); + 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(dis, &event); + + if (event.type==Expose && event.xexpose.count==0) { + /* the window was exposed redraw it! */ + redraw(); + } + + 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(0); + } + } + } + } + if (event.type==ButtonPress) { + /* tell where the mouse Button was Pressed */ + int x=event.xbutton.x, + y=event.xbutton.y; + + strcpy(text,">>xlnch<<"); + XSetForeground(dis,gc,rand()%255); + XDrawString(dis,win,gc,x,y, text, strlen(text)); + } + + if (event.type==FocusIn) { + if (event.xfocus.window != win) { + grabfocus(); + } + } + } +} |