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}