overdraw

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

commit 3ede808ab86004aac28b768a9374331e1acea523
parent cae73b002271e0bf90f4e88c396e06c0dafab1b4
Author: Louis Burda <quent.burda@gmail.com>
Date:   Thu, 20 Oct 2022 23:57:06 +0200

Add option for transparency and un-/regrabbing

Diffstat:
Moverdraw.c | 228++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 161 insertions(+), 67 deletions(-)

diff --git a/overdraw.c b/overdraw.c @@ -1,3 +1,4 @@ +#include <X11/X.h> #include <cairo/cairo.h> #include <cairo/cairo-xlib.h> #include <X11/Xlib.h> @@ -7,6 +8,7 @@ #include <X11/cursorfont.h> #include <err.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -24,21 +26,16 @@ struct line { struct rgb color; }; -static void add_line(cairo_path_t *path, struct rgb *color); -static void clear_background(void); -static void draw_color_mark(void); -static void draw_lines(void); - static struct rgb colors[3]; static int color_index; -static XImage *image; - XWindowAttributes scr; static Display *display; static Screen *screen; static Window root, win; -static GC gc; +static bool grabbed; +static bool done; +static bool draw; static cairo_surface_t *cs; static cairo_t *cr; @@ -46,6 +43,8 @@ static cairo_t *cr; static struct line *lines; static int lines_cnt, lines_cap; +static bool transparent; + void add_line(cairo_path_t *path, struct rgb *color) { @@ -62,12 +61,6 @@ add_line(cairo_path_t *path, struct rgb *color) } void -clear_background(void) -{ - XClearWindow(display, win); -} - -void draw_color_mark(void) { struct rgb *c; @@ -82,6 +75,20 @@ draw_color_mark(void) } void +update_pixmap(void) +{ + Pixmap pix; + GC gc; + + pix = XCreatePixmap(display, win, + scr.width, scr.height, scr.depth); + gc = XCreateGC(display, win, 0, 0); + XCopyArea(display, win, pix, gc, 0, 0, + scr.width, scr.height, 0, 0); + XSetWindowBackgroundPixmap(display, win, pix); +} + +void draw_lines(void) { int i; @@ -98,42 +105,120 @@ draw_lines(void) } void -init(void) +grab(void) { Cursor cursor; + + cursor = XCreateFontCursor(display, XC_pencil); + XGrabPointer(display, root, False, + ButtonMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, root, cursor, CurrentTime); + XGrabKeyboard(display, root, False, GrabModeAsync, + GrabModeAsync, CurrentTime); + grabbed = true; +} + +void +ungrab(void) +{ + XUngrabKeyboard(display, CurrentTime); + XUngrabPointer(display, CurrentTime); + XGrabKey(display, XKeysymToKeycode(display, XK_Escape), Mod1Mask, + root, False, GrabModeAsync, GrabModeAsync); + grabbed = false; +} + +void +redraw(void) +{ + if (transparent) { + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_rectangle(cr, 0, 0, scr.width, scr.height); + cairo_fill(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + } else { + XClearWindow(display, win); + } + + draw_color_mark(); + draw_lines(); + XFlush(display); +} + +void +keypress(XEvent ev) +{ + int keysym; + + keysym = XkbKeycodeToKeysym(display, ev.xkey.keycode, 0, + ev.xkey.state & ShiftMask ? 1 : 0); + switch (keysym) { + case XK_Escape: + if (!ev.xkey.state) { + done = true; + } else if ((ev.xkey.state & Mod1Mask)) { + if (grabbed) { + ungrab(); + if (!transparent) { + XUnmapWindow(display, win); + } + } else { + if (!transparent) { + XMapWindow(display, win); + update_pixmap(); + redraw(); + } + grab(); + } + } + break; + case ' ': + color_index = (color_index + 1) % ARRLEN(colors); + draw_color_mark(); + break; + } +} + +void +init(void) +{ XSetWindowAttributes swa; XGCValues gcvals; + XVisualInfo vinfo; Visual *visual; + int depth; display = XOpenDisplay(NULL); root = DefaultRootWindow(display); + screen = XDefaultScreenOfDisplay(display); XGetWindowAttributes(display, root, &scr); + if (transparent) { + XMatchVisualInfo(display, DefaultScreen(display), + 32, TrueColor, &vinfo); + visual = vinfo.visual; + depth = vinfo.depth; + } else { + visual = XDefaultVisualOfScreen(screen); + depth = CopyFromParent; + } + + swa.border_pixel = 0; swa.override_redirect = 1; + swa.event_mask = ExposureMask; + swa.colormap = XCreateColormap(display, root, visual, AllocNone); win = XCreateWindow(display, root, scr.x, scr.y, scr.width, scr.height, - 0, CopyFromParent, InputOutput, CopyFromParent, - CWOverrideRedirect, &swa); + 0, depth, InputOutput, visual, CWOverrideRedirect + | CWBorderPixel | CWColormap | CWEventMask, &swa); if (!win) err(1, "XCreateWindow"); XMapWindow(display, win); - image = XGetImage(display, win, scr.x, scr.y, scr.width, scr.height, - AllPlanes, ZPixmap); - if (!image) err(1, "XGetImage"); - - gcvals = (XGCValues) { 0 }; - gcvals.subwindow_mode = IncludeInferiors; - gcvals.line_width = 2; - gc = XCreateGC(display, win, GCSubwindowMode | GCLineWidth, &gcvals); - - screen = XDefaultScreenOfDisplay(display); - visual = XDefaultVisualOfScreen(screen); - - Pixmap pix = XCreatePixmap(display, win, scr.width, scr.height, scr.depth); - XCopyArea(display, win, pix, gc, 0, 0, scr.width, scr.height, 0, 0); - XSetWindowBackgroundPixmap(display, win, pix); + if (!transparent) + update_pixmap(); - cs = cairo_xlib_surface_create(display, win, visual, scr.width, scr.height); + cs = cairo_xlib_surface_create(display, + win, visual, scr.width, scr.height); cr = cairo_create(cs); cairo_surface_flush(cs); @@ -146,53 +231,28 @@ init(void) colors[2] = (struct rgb) { 0.0, 0.0, 0.0 }; color_index = 0; - cursor = XCreateFontCursor(display, XC_pencil); - - XGrabPointer(display, root, False, - ButtonMotionMask | ButtonPressMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, root, cursor, CurrentTime); - - XGrabKeyboard(display, root, False, GrabModeAsync, - GrabModeAsync, CurrentTime); - lines_cap = 10; lines = calloc(lines_cap, sizeof(struct line)); if (!lines) exit(1); lines_cnt = 0; - draw_color_mark(); - XFlush(display); + grab(); } -int -main(int argc, char *argv[]) +void +update(void) { int px, py, x, y; struct rgb color; cairo_path_t *path; - int done, draw; - int keysym; XEvent ev; - init(); - done = 0; while (!done) { - if (!XPending(display)) continue; XNextEvent(display, &ev); switch (ev.type) { case KeyPress: - keysym = XkbKeycodeToKeysym(display, ev.xkey.keycode, 0, - ev.xkey.state & ShiftMask ? 1 : 0); - switch (keysym) { - case XK_Escape: - done = 1; - break; - case ' ': - color_index = (color_index + 1) % ARRLEN(colors); - draw_color_mark(); - break; - } + keypress(ev); break; case MotionNotify: if (!draw) break; @@ -201,6 +261,14 @@ main(int argc, char *argv[]) cairo_move_to(cr, ev.xmotion.x, ev.xmotion.y); cairo_stroke_preserve(cr); cairo_surface_flush(cs); + path = cairo_copy_path(cr); + if (path->num_data > 500) { + add_line(path, &color); + cairo_new_path(cr); + cairo_line_to(cr, ev.xmotion.x, ev.xmotion.y); + } else { + cairo_path_destroy(path); + } XFlush(display); break; case ButtonPress: @@ -214,17 +282,43 @@ main(int argc, char *argv[]) path = cairo_copy_path(cr); add_line(path, &color); - clear_background(); - draw_color_mark(); - draw_lines(); - - XFlush(display); + redraw(); + break; + case Expose: + redraw(); break; } } +} +void +deinit(void) +{ cairo_surface_destroy(cs); XDestroyWindow(display, win); XCloseDisplay(display); + + free(lines); +} + +int +main(int argc, const char **argv) +{ + const char **arg; + + transparent = false; + for (arg = &argv[1]; *arg; arg++) { + if (!strcmp(*arg, "-t")) { + transparent = true; + } else { + errx(1, "Unknown arg: %s", *arg); + } + } + + init(); + + update(); + + deinit(); }