overdraw

X11 drawable overlay
git clone https://git.sinitax.com/sinitax/overdraw
Log | Files | Refs | LICENSE | sfeed.txt

overdraw.c (6427B)


      1#include <X11/X.h>
      2#include <cairo/cairo.h>
      3#include <cairo/cairo-xlib.h>
      4#include <X11/Xlib.h>
      5#include <X11/Xutil.h>
      6#include <X11/XKBlib.h>
      7#include <X11/keysym.h>
      8#include <X11/cursorfont.h>
      9
     10#include <err.h>
     11#include <stdbool.h>
     12#include <stdio.h>
     13#include <stdlib.h>
     14#include <string.h>
     15
     16#define MIN(a, b) ((a) < (b) ? (a) : (b))
     17#define MAX(a, b) ((a) >= (b) ? (a) : (b))
     18#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
     19
     20struct rgb {
     21	double r, g, b;
     22};
     23
     24struct line {
     25	cairo_path_t *path;
     26	struct rgb color;
     27};
     28
     29static struct rgb colors[3];
     30static size_t color_index;
     31
     32XWindowAttributes scr;
     33static Display *display;
     34static Screen *screen;
     35static Window root, win;
     36static bool grabbed;
     37static bool done;
     38static bool draw;
     39
     40static cairo_surface_t *cs;
     41static cairo_t *cr;
     42
     43static struct line *lines;
     44static size_t lines_cnt, lines_cap;
     45
     46static bool transparent;
     47
     48static void
     49add_line(cairo_path_t *path, struct rgb *color)
     50{
     51	if (lines_cnt == lines_cap) {
     52		lines_cap *= 2;
     53		lines = realloc(lines, lines_cap * sizeof(struct line));
     54		if (!lines) err(1, "realloc");
     55	}
     56
     57	lines[lines_cnt].path = path;
     58	lines[lines_cnt].color = *color;
     59
     60	lines_cnt++;
     61}
     62
     63static void
     64draw_color_mark(void)
     65{
     66	struct rgb *c;
     67
     68	c = &colors[color_index];
     69	cairo_save(cr);
     70	cairo_set_source_rgb(cr, c->r, c->g, c->b);
     71	cairo_rectangle(cr, 10, 10, 20, 20);
     72	cairo_stroke_preserve(cr);
     73	cairo_fill(cr);
     74	cairo_restore(cr);
     75}
     76
     77static void
     78update_pixmap(void)
     79{
     80	Pixmap pix;
     81	GC gc;
     82
     83	pix = XCreatePixmap(display, win, (unsigned int) scr.width,
     84		(unsigned int) scr.height, (unsigned int) scr.depth);
     85	gc = XCreateGC(display, win, 0, 0);
     86	XCopyArea(display, win, pix, gc, 0, 0,
     87		(unsigned int) scr.width, (unsigned int) scr.height, 0, 0);
     88	XSetWindowBackgroundPixmap(display, win, pix);
     89}
     90
     91static void
     92draw_lines(void)
     93{
     94	int i;
     95
     96	cairo_save(cr);
     97	for (i = 0; i < lines_cnt; i++) {
     98		cairo_new_path(cr);
     99		cairo_set_source_rgb(cr, lines[i].color.r,
    100			lines[i].color.g, lines[i].color.b);
    101		cairo_append_path(cr, lines[i].path);
    102		cairo_stroke(cr);
    103	}
    104	cairo_restore(cr);
    105}
    106
    107static void
    108grab(void)
    109{
    110	Cursor cursor;
    111
    112	cursor = XCreateFontCursor(display, XC_pencil);
    113	XGrabPointer(display, root, False,
    114		ButtonMotionMask | ButtonPressMask | ButtonReleaseMask,
    115		GrabModeAsync, GrabModeAsync, root, cursor, CurrentTime);
    116	XGrabKeyboard(display, root, False, GrabModeAsync,
    117		GrabModeAsync, CurrentTime);
    118	grabbed = true;
    119}
    120
    121static void
    122ungrab(void)
    123{
    124	XUngrabKeyboard(display, CurrentTime);
    125	XUngrabPointer(display, CurrentTime);
    126	XGrabKey(display, XKeysymToKeycode(display, XK_Escape), Mod1Mask,
    127		root, False, GrabModeAsync, GrabModeAsync);
    128	grabbed = false;
    129}
    130
    131static void
    132redraw(void)
    133{
    134	if (transparent) {
    135		cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
    136		cairo_rectangle(cr, 0, 0,
    137			(double) scr.width, (double) scr.height);
    138		cairo_fill(cr);
    139		cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
    140	} else {
    141		XClearWindow(display, win);
    142	}
    143
    144	draw_color_mark();
    145	draw_lines();
    146	XFlush(display);
    147}
    148
    149static void
    150keypress(XEvent ev)
    151{
    152	KeySym keysym;
    153
    154	keysym = XkbKeycodeToKeysym(display, (KeyCode) ev.xkey.keycode, 0,
    155		ev.xkey.state & ShiftMask ? 1 : 0);
    156	switch (keysym) {
    157	case XK_Escape:
    158		if (!ev.xkey.state) {
    159			done = true;
    160		} else if ((ev.xkey.state & Mod1Mask)) {
    161			if (grabbed) {
    162				ungrab();
    163				if (!transparent) {
    164					XUnmapWindow(display, win);
    165				}
    166			} else {
    167				if (!transparent) {
    168					XMapWindow(display, win);
    169					update_pixmap();
    170					redraw();
    171				}
    172				grab();
    173			}
    174		}
    175		break;
    176	case ' ':
    177		color_index = (color_index + 1) % ARRLEN(colors);
    178		draw_color_mark();
    179		break;
    180	}
    181}
    182
    183static void
    184init(void)
    185{
    186	XSetWindowAttributes swa;
    187	XVisualInfo vinfo;
    188	Visual *visual;
    189	int depth;
    190
    191	display = XOpenDisplay(NULL);
    192	root = DefaultRootWindow(display);
    193	screen = XDefaultScreenOfDisplay(display);
    194
    195	XGetWindowAttributes(display, root, &scr);
    196
    197	if (transparent) {
    198		XMatchVisualInfo(display, DefaultScreen(display),
    199			32, TrueColor, &vinfo);
    200		visual = vinfo.visual;
    201		depth = vinfo.depth;
    202	} else {
    203		visual = XDefaultVisualOfScreen(screen);
    204		depth = CopyFromParent;
    205	}
    206
    207	swa.border_pixel = 0;
    208	swa.override_redirect = 1;
    209	swa.event_mask = ExposureMask;
    210	swa.colormap = XCreateColormap(display, root, visual, AllocNone);
    211	win = XCreateWindow(display, root, scr.x, scr.y,
    212		(unsigned int) scr.width, (unsigned int) scr.height,
    213		0, depth, InputOutput, visual, CWOverrideRedirect
    214		| CWBorderPixel | CWColormap | CWEventMask, &swa);
    215	if (!win) err(1, "XCreateWindow");
    216	XMapWindow(display, win);
    217
    218	if (!transparent)
    219		update_pixmap();
    220
    221	cs = cairo_xlib_surface_create(display,
    222		win, visual, scr.width, scr.height);
    223	cr = cairo_create(cs);
    224
    225	cairo_surface_flush(cs);
    226
    227	cairo_set_line_width(cr, 2);
    228	cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
    229
    230	colors[0] = (struct rgb) { 1.0, 1.0, 1.0 };
    231	colors[1] = (struct rgb) { 1.0, 0.0, 0.0 };
    232	colors[2] = (struct rgb) { 0.0, 0.0, 0.0 };
    233	color_index = 0;
    234
    235	lines_cap = 10;
    236	lines = calloc(lines_cap, sizeof(struct line));
    237	if (!lines) exit(1);
    238	lines_cnt = 0;
    239
    240	grab();
    241}
    242
    243static void
    244update(void)
    245{
    246	struct rgb color;
    247	cairo_path_t *path;
    248	XEvent ev;
    249
    250	done = 0;
    251	while (!done) {
    252		XNextEvent(display, &ev);
    253		switch (ev.type) {
    254		case KeyPress:
    255			keypress(ev);
    256			break;
    257		case MotionNotify:
    258			if (!draw) break;
    259			cairo_set_source_rgb(cr, color.r, color.g, color.b);
    260			cairo_line_to(cr, ev.xmotion.x, ev.xmotion.y);
    261			cairo_move_to(cr, ev.xmotion.x, ev.xmotion.y);
    262			cairo_stroke_preserve(cr);
    263			cairo_surface_flush(cs);
    264			path = cairo_copy_path(cr);
    265			if (path->num_data > 500) {
    266				add_line(path, &color);
    267				cairo_new_path(cr);
    268				cairo_line_to(cr, ev.xmotion.x, ev.xmotion.y);
    269			} else {
    270				cairo_path_destroy(path);
    271			}
    272			XFlush(display);
    273			break;
    274		case ButtonPress:
    275			cairo_new_path(cr);
    276			cairo_move_to(cr, ev.xmotion.x, ev.xmotion.y);
    277			color = colors[color_index];
    278			draw = 1;
    279			break;
    280		case ButtonRelease:
    281			draw = 0;
    282			path = cairo_copy_path(cr);
    283			add_line(path, &color);
    284
    285			redraw();
    286			break;
    287		case Expose:
    288			redraw();
    289			break;
    290		}
    291	}
    292}
    293
    294static void
    295deinit(void)
    296{
    297	cairo_surface_destroy(cs);
    298
    299	XDestroyWindow(display, win);
    300	XCloseDisplay(display);
    301
    302	free(lines);
    303}
    304
    305int
    306main(int argc, const char **argv)
    307{
    308	const char **arg;
    309
    310	transparent = false;
    311	for (arg = &argv[1]; *arg; arg++) {
    312		if (!strcmp(*arg, "-t")) {
    313			transparent = true;
    314		} else {
    315			errx(1, "Unknown arg: %s", *arg);
    316		}
    317	}
    318
    319	init();
    320
    321	update();
    322
    323	deinit();
    324}