Index: safekill.c ================================================================== --- safekill.c +++ safekill.c @@ -1,34 +1,37 @@ /* [ʞ] safekill.c * ~ lexi hale - * $ cc -Ofast safekill.c -lX11 -lc -osafekill + * $ cc -Ofast safekill.c -lX11 -osafekill * © affero general public license * a utility to make it harder to accidentally nuke - * important windows on i3wm or whatever. */ + * important windows on i3wm or whatever. + + TODO add option to run as daemon that listens for + key chords, for wms that don't have built-in + support for user keybindings */ #include #include #include #include #include #include #include typedef enum { false, true } bool; -char* isvital(char* str) { - start: // tail calls for dipshits - if (*str != '{') { - if (*str == 0) return 0; - ++str; goto start; - } else { - if (str[1] == 'V' && str[2] == '}') - return str; - else { ++str; goto start; } - } -} Display *display; +Atom xvital; + +bool isvital(Window w) { + unsigned char* value; + int _i; long _l; + Atom type; + XGetWindowProperty(display,w, xvital, 0,1,False,xvital, + &type,&_i,&_l,&_l, &value); + return type != None; +} void killwin(Window w) { XEvent ev; long mask = SubstructureRedirectMask | SubstructureNotifyMask; ev.xclient.type = ClientMessage; @@ -50,27 +53,68 @@ fprintf(stderr," falling back to XDestroyWindow, but this may wreck your shit - lots of multi-win apps do *not* like it.\n"); XDestroyWindow(display,w); } } } + +bool isnum(const char* str) { + // is the string passed something strtoul can interpret as a + // number? decimal, hex, or octal? + if (*str == '0' && str[1] == 0) return true; + + enum { hex, dec, oct } mode; + if (*str == '0' && str[1] == 'x') mode = hex, str += 2; + else if (*str == '0') mode = oct, str += 1; + else mode = dec; + bool first = true; + eval: if(*str == 0) return !first; + first = false; + if(*str >= '0' && *str <= '7') { + ++ str; goto eval; + } else if ((mode == dec || mode == hex) && *str >= '0' && *str <= '9') { + ++ str; goto eval; + } else if (mode == hex && ( + (*str >= 'a' && *str <= 'f') || + (*str >= 'A' && *str <= 'F'))) { + ++ str; goto eval; + } else return false; +} + +int bad(Display* d, XErrorEvent* e) { + printf("\x1b[1merror:\x1b[m no such window\n"); + exit(1); +} + int main(const int argc, const char** argv) { Window focus; int revert; bool active; enum { vitalize, devitalize, hardkill, softkill } op; - if (argc > 3) goto usage; - if (argc == 1) + const char* winp = argv[2]; + if (argc > 3) goto usage; // nope + if (argc == 1) // no parameters, implied softkill on active win op = softkill, active = true; else { - if (argc == 2) active = true; - else active = false; + if (argc == 2) { // single argument; what kind is it? + if (isnum(argv[1])) { // single numeric argument = win id + active = false; // window is specified for us + op = softkill; // implied softkill + winp = argv[1]; // interpret argv[1] as window id + goto go_go_go; // skip the rest of this nonsense; + // we already know there's no switch + } else { + active = true; // no number passed, so it's either a + // switch with an implied active window or bad syntax + // now we need to figure out which + } + } else if (argc == 3) active = false; // win id AND opt + // determine mission parameters if (argv[1][0] == '-' && argv[1][2] == 0) { // opt is short switch switch (argv[1][1]) { // opt char - // make window mission-critical case 'v': op = vitalize; break; // make window acceptable loss case 'c': op = devitalize; break; @@ -82,16 +126,19 @@ default: goto usage; } } else goto usage; } +go_go_go: display = XOpenDisplay(NULL); + xvital = XInternAtom(display, "_k_vital", False); + XSetErrorHandler(bad); if (active) { XGetInputFocus(display, &focus, &revert); } else { - unsigned long temp = strtoul(argv[2], NULL, 0); + unsigned long temp = strtoul(winp, NULL, 0); if (errno == EINVAL || errno == ERANGE) goto usage; if (temp == 0) goto usage; focus = (Window)temp; } @@ -99,45 +146,32 @@ killwin(focus); XSync(display,false); return 0; } - XClassHint xclh; - if (XGetClassHint(display,focus,&xclh)) { - char* v; - if(v = isvital(xclh.res_class)) { - - if (op == vitalize) return 0; // nothing need be done - else if (op == devitalize) { - strcpy(v, v+3); - XSetClassHint(display,focus,&xclh); - XSync(display,false); - } else if (op == softkill) { - fprintf(stderr, "softkill forbidden\n"); - return 1; // ACCESS DENIED - } - } else { - if (op == vitalize) { - size_t sz=strlen(xclh.res_class); // TODO: remove double-walk - xclh.res_class=realloc(xclh.res_class, sz+4); - xclh.res_class[sz]='{'; - xclh.res_class[sz+1]='V'; - xclh.res_class[sz+2]='}'; - xclh.res_class[sz+3]=0; - XSetClassHint(display,focus,&xclh); - XSync(display,false); - } else if (op == devitalize) return 0; - else if (op == softkill) { - // ice that motherfucker - killwin(focus); - XSync(display,false); - return 3; - } + char* v; + if(isvital(focus)) { + if (op == vitalize) return 0; // nothing need be done + else if (op == devitalize) { + XDeleteProperty(display, focus, xvital); + XSync(display,false); + } else if (op == softkill) { + fprintf(stderr, "softkill forbidden\n"); + return 1; // ACCESS DENIED } } else { - fprintf(stderr,"\x1b[1merror:\x1b[m bad window\n"); - goto usage; + if (op == vitalize) { + char* yes="\01"; //arbitrary + XChangeProperty(display,focus, xvital,xvital,8,PropModeReplace, yes, 1); + XSync(display,false); + } else if (op == devitalize) return 0; + else if (op == softkill) { + // ice that motherfucker + killwin(focus); + XSync(display,false); + return 3; + } } return 0; usage: