xsnip

X11 screenshotter
git clone https://git.sinitax.com/sinitax/xsnip
Log | Files | Refs | LICENSE | sfeed.txt

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}