util  Check-in [795f87aa54]

Overview
Comment:rewrite safekill to use X properties instead of hacking a flag into the window class
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 795f87aa54562a21d8fc2831a6efeac93cf0efa0345bbf8c78af9f89ee7d4bfc
User & Date: lexi on 2019-04-14 03:07:41
Other Links: manifest | tags
Context
2019-04-17
16:52
add shit, updates check-in: 196f94b613 user: lexi tags: trunk
2019-04-14
03:07
rewrite safekill to use X properties instead of hacking a flag into the window class check-in: 795f87aa54 user: lexi tags: trunk
2019-04-13
13:44
fix typo check-in: ee4f58caef user: lexi tags: trunk
Changes

Modified safekill.c from [95fcf40cb0] to [59e57157c0].

     1      1   /* [ʞ] safekill.c <c.hale.su/lexi/util>
     2      2    *  ~ lexi hale <lexi@hale.su>
     3         - *  $ cc -Ofast safekill.c -lX11 -lc -osafekill
            3  + *  $ cc -Ofast safekill.c -lX11 -osafekill
     4      4    *  © affero general public license
     5      5    
     6      6    * a utility to make it harder to accidentally nuke
     7         - * important windows on i3wm or whatever. */
            7  + * important windows on i3wm or whatever.
            8  +
            9  + TODO add option to run as daemon that listens for
           10  +      key chords, for wms that don't have built-in
           11  +      support for user keybindings */
     8     12   
     9     13   #include <X11/Xlib.h>
    10     14   #include <X11/Xutil.h>
    11     15   #include <X11/Xatom.h>
    12     16   #include <stdio.h>
    13     17   #include <stdlib.h>
    14     18   #include <string.h>
    15     19   #include <errno.h>
    16     20   typedef enum { false, true } bool;
    17         -char* isvital(char* str) {
    18         -	start: // tail calls for dipshits
    19         -		if (*str != '{') {
    20         -			if (*str == 0) return 0;
    21         -			++str; goto start;
    22         -		} else {
    23         -			if (str[1] == 'V' && str[2] == '}')
    24         -				return str;
    25         -			else { ++str; goto start; }
    26         -		}
    27         -}
    28     21   
    29     22   Display *display;
           23  +Atom xvital;
           24  +
           25  +bool isvital(Window w) {
           26  +	unsigned char* value;
           27  +	int _i; long _l;
           28  +	Atom type;
           29  +	XGetWindowProperty(display,w, xvital, 0,1,False,xvital,
           30  +				&type,&_i,&_l,&_l, &value);
           31  +	return type != None;
           32  +}
    30     33   
    31     34   void killwin(Window w) {
    32     35   	XEvent ev;
    33     36   	long mask = SubstructureRedirectMask | SubstructureNotifyMask;
    34     37   	ev.xclient.type       = ClientMessage;
    35     38   	ev.xclient.serial     = 0;
    36     39   	ev.xclient.send_event = True;
................................................................................
    48     51   			fprintf(stderr,"\n");
    49     52   		} else {
    50     53   			fprintf(stderr," falling back to XDestroyWindow, but this may wreck your shit - lots of multi-win apps do *not* like it.\n");
    51     54   			XDestroyWindow(display,w);
    52     55   		}
    53     56   	}
    54     57   }
           58  +
           59  +bool isnum(const char* str) {
           60  +	// is the string passed something strtoul can interpret as a
           61  +	// number? decimal, hex, or octal?
           62  +	if (*str == '0' && str[1] == 0) return true;
           63  +
           64  +	enum { hex, dec, oct } mode;
           65  +	if (*str == '0' && str[1] == 'x') mode = hex, str += 2;
           66  +	            else if (*str == '0') mode = oct, str += 1;
           67  +			                     else mode = dec;
           68  +	bool first = true;
           69  +	eval: if(*str == 0) return !first;
           70  +		  first = false;
           71  +		  if(*str >= '0' && *str <= '7') {
           72  +			  ++ str; goto eval;
           73  +		  } else if ((mode == dec || mode == hex) && *str >= '0' && *str <= '9') {
           74  +			  ++ str; goto eval;
           75  +		  } else if (mode == hex && (
           76  +				  (*str >= 'a' && *str <= 'f') ||
           77  +				  (*str >= 'A' && *str <= 'F'))) {
           78  +			  ++ str; goto eval;
           79  +		  } else return false;
           80  +}
           81  +
           82  +int bad(Display* d, XErrorEvent* e) {
           83  +	printf("\x1b[1merror:\x1b[m no such window\n");
           84  +	exit(1);
           85  +}
           86  +
    55     87   int main(const int argc, const char** argv) {
    56     88   	Window focus;
    57     89   	int revert;
    58     90   
    59     91   	bool active;
    60     92   	enum { vitalize, devitalize, hardkill, softkill } op;
    61     93   
    62         -	if (argc > 3) goto usage;
    63         -	if (argc == 1)
           94  +	const char* winp = argv[2];
           95  +	if (argc >  3) goto usage; // nope
           96  +	if (argc == 1) // no parameters, implied softkill on active win
    64     97   		op = softkill, active = true;
    65     98   	else {
    66         -		if (argc == 2) active = true;
    67         -		else           active = false;
           99  +		if (argc == 2) { // single argument; what kind is it?
          100  +			if (isnum(argv[1])) { // single numeric argument = win id 
          101  +				active = false; // window is specified for us
          102  +				op     = softkill; // implied softkill
          103  +				winp   = argv[1]; // interpret argv[1] as window id
          104  +				goto   go_go_go; // skip the rest of this nonsense;
          105  +					// we already know there's no switch
          106  +			} else {
          107  +				active = true; // no number passed, so it's either a
          108  +				// switch with an implied active window or bad syntax
          109  +				// now we need to figure out which
          110  +			}
          111  +		} else if (argc == 3) active = false; // win id AND opt
    68    112   
          113  +		// determine mission parameters
    69    114   		if (argv[1][0] == '-' && argv[1][2] == 0) { // opt is short switch
    70    115   			switch (argv[1][1]) { // opt char
    71         -
    72    116   				// make window mission-critical
    73    117   				case 'v': op = vitalize; break;
    74    118   
    75    119   				// make window acceptable loss
    76    120   				case 'c': op = devitalize; break;
    77    121   
    78    122   				// liquidate by any means necessary
................................................................................
    80    124   
    81    125   				// with all due respect sir what the fuck
    82    126   				default: goto usage;
    83    127   			}
    84    128   		} else goto usage;
    85    129   	}
    86    130   
          131  +go_go_go:
    87    132   	display = XOpenDisplay(NULL);
          133  +	xvital = XInternAtom(display, "_k_vital", False);
          134  +	XSetErrorHandler(bad);
    88    135   
    89    136   	if (active) {
    90    137   		XGetInputFocus(display, &focus, &revert);
    91    138   	} else {
    92         -		unsigned long temp = strtoul(argv[2], NULL, 0);
          139  +		unsigned long temp = strtoul(winp, NULL, 0);
    93    140   		if (errno == EINVAL || errno == ERANGE) goto usage;
    94    141   		if (temp == 0) goto usage;
    95    142   		focus = (Window)temp;
    96    143   	}
    97    144   
    98    145   	if(op == hardkill) {
    99    146   		killwin(focus);
   100    147   		XSync(display,false);
   101    148   		return 0;
   102    149   	}
   103    150   
   104         -	XClassHint xclh;
   105         -	if (XGetClassHint(display,focus,&xclh)) {
   106         -		char* v;
   107         -		if(v = isvital(xclh.res_class)) {
   108         -
   109         -			if (op == vitalize) return 0; // nothing need be done
   110         -			else if (op == devitalize) {
   111         -				strcpy(v, v+3);
   112         -				XSetClassHint(display,focus,&xclh);
   113         -				XSync(display,false);
   114         -			} else if (op == softkill) {
   115         -				fprintf(stderr, "softkill forbidden\n");
   116         -				return 1; // ACCESS DENIED
   117         -			}
   118         -		} else {
   119         -			if (op == vitalize) {
   120         -				size_t sz=strlen(xclh.res_class); // TODO: remove double-walk
   121         -				xclh.res_class=realloc(xclh.res_class, sz+4);
   122         -				xclh.res_class[sz]='{';
   123         -				xclh.res_class[sz+1]='V';
   124         -				xclh.res_class[sz+2]='}';
   125         -				xclh.res_class[sz+3]=0;
   126         -				XSetClassHint(display,focus,&xclh);
   127         -				XSync(display,false);
   128         -			} else if (op == devitalize) return 0;
   129         -			else if (op == softkill) {
   130         -				// ice that motherfucker
   131         -				killwin(focus);
   132         -				XSync(display,false);
   133         -				return 3;
   134         -			}
          151  +	char* v;
          152  +	if(isvital(focus)) {
          153  +		if        (op == vitalize) return 0; // nothing need be done
          154  +		else if   (op == devitalize) {
          155  +			XDeleteProperty(display, focus, xvital);
          156  +			XSync(display,false);
          157  +		} else if (op == softkill) {
          158  +			fprintf(stderr, "softkill forbidden\n");
          159  +			return 1; // ACCESS DENIED
   135    160   		}
   136    161   	} else {
   137         -		fprintf(stderr,"\x1b[1merror:\x1b[m bad window\n");
   138         -		goto usage;
          162  +		if        (op == vitalize) {
          163  +			char* yes="\01"; //arbitrary
          164  +			XChangeProperty(display,focus, xvital,xvital,8,PropModeReplace, yes, 1);
          165  +			XSync(display,false);
          166  +		} else if (op == devitalize) return 0;
          167  +		else if   (op == softkill) {
          168  +			// ice that motherfucker
          169  +			killwin(focus);
          170  +			XSync(display,false);
          171  +			return 3;
          172  +		}
   139    173   	}
   140    174   
   141    175   	return 0;
   142    176   
   143    177   usage:
   144    178   	#define info  "\x1b[34m-- "
   145    179   	#define param "\x1b[32m"