xsnip.c (3996B)
1#include <Imlib2.h> 2#include <X11/Xlib.h> 3#include <X11/XKBlib.h> 4#include <X11/keysym.h> 5#include <X11/cursorfont.h> 6 7#include <unistd.h> 8#include <err.h> 9#include <fcntl.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <stdarg.h> 13#include <string.h> 14 15#define MIN(a, b) ((a) < (b) ? (a) : (b)) 16#define MAX(a, b) ((a) >= (b) ? (a) : (b)) 17 18static const char usage[] = "xsnip [-h] [-d DELAY] OUTFILE"; 19 20static const char *filepath; 21 22static XWindowAttributes scr; 23static Display *display; 24static Window root, win; 25static int rx, ry, rw, rh; 26static GC gc; 27 28void 29update(int x1, int y1, int x2, int y2) 30{ 31 rx = MIN(x1, x2); 32 ry = MIN(y1, y2); 33 rw = MAX(1, MAX(x1, x2) - MIN(x1, x2)); 34 rh = MAX(1, MAX(y1, y2) - MIN(y1, y2)); 35} 36 37void 38init(void) 39{ 40 XSetWindowAttributes swa; 41 XGCValues gcvals; 42 Screen *screen; 43 44 display = XOpenDisplay(NULL); 45 screen = XDefaultScreenOfDisplay(display); 46 47 root = DefaultRootWindow(display); 48 XGetWindowAttributes(display, root, &scr); 49 50 swa.override_redirect = 1; 51 win = XCreateWindow(display, root, scr.x, scr.y, scr.width, scr.height, 52 0, CopyFromParent, InputOutput, CopyFromParent, 53 CWOverrideRedirect, &swa); 54 XMapWindow(display, win); 55 56 gcvals = (XGCValues) { 0 }; 57 gcvals.foreground = XWhitePixelOfScreen(screen); 58 gcvals.function = GXxor; 59 gc = XCreateGC(display, win, GCFunction | GCForeground, &gcvals); 60} 61 62void 63deinit(void) 64{ 65 XDestroyWindow(display, win); 66 XCloseDisplay(display); 67} 68 69void 70saveimg(void) 71{ 72 Imlib_Image img; 73 const char *file; 74 const char *hint; 75 int fd; 76 77 imlib_context_set_display(display); 78 imlib_context_set_visual(DefaultVisual(display, 0)); 79 imlib_context_set_drawable(win); 80 81 img = imlib_create_image_from_drawable(0, rx, ry, rw, rh, 1); 82 83 imlib_context_set_image(img); 84 85 fd = open(filepath, O_WRONLY | O_CREAT); 86 if (!fd) perror("open"); 87 88 file = strrchr(filepath, '/'); 89 file = file ? file + 1 : filepath; 90 hint = strchr(file, '.') ? file : "dsnip.png"; 91 imlib_save_image_fd(fd, hint); 92 if (imlib_get_error()) { 93 fprintf(stderr, "xsnip: imlib_save_image: %s\n", imlib_strerror(imlib_get_error())); 94 exit(1); 95 } 96 97 close(fd); 98 99 imlib_free_image(); 100} 101 102void 103capture(void) 104{ 105 int draw, dirty, done; 106 int px, py, x, y, keysym; 107 Cursor cursor; 108 XEvent ev; 109 110 cursor = XCreateFontCursor(display, XC_left_ptr); 111 112 XGrabPointer(display, root, False, 113 ButtonMotionMask | ButtonPressMask | ButtonReleaseMask, 114 GrabModeAsync, GrabModeAsync, root, cursor, CurrentTime); 115 116 XGrabKeyboard(display, root, False, GrabModeAsync, 117 GrabModeAsync, CurrentTime); 118 119 done = dirty = 0; 120 while (!done) { 121 if (!XPending(display)) continue; 122 XNextEvent(display, &ev); 123 switch (ev.type) { 124 case KeyPress: 125 keysym = XkbKeycodeToKeysym(display, ev.xkey.keycode, 0, 126 ev.xkey.state & ShiftMask ? 1 : 0); 127 if (keysym == XK_Escape) { 128 deinit(); 129 exit(1); 130 } 131 break; 132 case MotionNotify: 133 if (draw && dirty) { 134 XDrawRectangle(display, win, gc, 135 rx, ry, rw, rh); 136 } 137 138 x = ev.xmotion.x; 139 y = ev.xmotion.y; 140 update(px, py, x, y); 141 142 if (draw) { 143 XDrawRectangle(display, win, gc, 144 rx, ry, rw, rh); 145 dirty = 1; 146 XFlush(display); 147 } 148 break; 149 case ButtonPress: 150 px = ev.xbutton.x; 151 py = ev.xbutton.y; 152 draw = 1; 153 break; 154 case ButtonRelease: 155 done = 1; 156 break; 157 } 158 } 159 160 XDrawRectangle(display, win, gc, 161 rx, ry, rw, rh); 162} 163 164int 165main(int argc, char **argv) 166{ 167 char **arg, *end; 168 int delay; 169 170 delay = 0; 171 filepath = NULL; 172 for (arg = &argv[1]; *arg; arg++) { 173 if (!strcmp(*arg, "-d")) { 174 delay = strtol(*(++arg), &end, 10); 175 if (end && *end || delay <= 0) 176 errx(1, "Invalid delay"); 177 } else if (!strcmp(*arg, "-h")) { 178 printf("Usage: %s\n", usage); 179 return 0; 180 } else if (!filepath) { 181 filepath = *arg; 182 if (!strcmp(filepath, "-")) 183 filepath = "/dev/stdout"; 184 } else { 185 fprintf(stderr, "Usage: %s\n", usage); 186 return 1; 187 } 188 } 189 190 if (!filepath) { 191 fprintf(stderr, "USAGE: %s\n", usage); 192 return 1; 193 } 194 195 if (delay) sleep(delay); 196 197 init(); 198 199 capture(); 200 201 saveimg(); 202 203 deinit(); 204}