Differences From
Artifact [95fcf40cb0]:
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"