commit 4145c7ed8951400913687c1c3e5acb594f95f9ba
parent cc71e45e451288c483857818ca6fb89bf7160f03
Author: Louis Burda <quent.burda@gmail.com>
Date: Sun, 2 Jan 2022 20:59:30 +0100
Antialiased line drawing in different colors
Using cairo to allow for more advanced graphics, such as
antialiasing. Paths also allow for the implementation of
undo / redo functionality in the future.
Note, this commit is still missing certain garbage collection
and creates visual artifacts on ButtonRelease, because the
calls to clear_background and draw_lines blit to the screen
automatically instead of waiting until XFlush(display). FIX THIS.
Diffstat:
7 files changed, 256 insertions(+), 55 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,3 +1,4 @@
.cache
compile_commands.json
build
+overdraw
diff --git a/.gitmodules b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "libs/xosd"]
- path = libs/xosd
- url = git@sinitax.com:sinitax/xosd-custom
diff --git a/Makefile b/Makefile
@@ -1,17 +1,12 @@
-CFLAGS = -I libs/xosd/include
-LDLIBS = -lX11 -lXext -lpthread
+CFLAGS =
+LDLIBS = -lX11 -lcairo
-all: build/overdraw
+.PHONY: all clean
-build:
- mkdir build
+all: overdraw
-libs/xosd:
- git submodule update --init --recursive
+clean:
+ rm overdraw
-build/xosd.o: | build libs/xosd
- make -C libs/xosd build/libxosd.o
- cp libs/xosd/build/libxosd.o build/xosd.o
-
-build/overdraw: src/overdraw.c build/xosd.o | build
+overdraw: overdraw.c
$(CC) -o $@ $^ $(CFLAGS) $(LDLIBS)
diff --git a/README b/README
@@ -0,0 +1,6 @@
+implement initial drawing same way as dsnip, save operations in list and apply to copied pixmap on each frame
+after that, draw on root with IncludeInferior that same operation ever expose
+
+might be a better way with a 32bit depth visual overlayed window, see:
+https://stackoverflow.com/questions/21780789/x11-draw-on-overlay-window/57780772#57780772
+https://stackoverflow.com/questions/16400937/click-through-transparent-xlib-windows (for click through)
diff --git a/libs/xosd b/libs/xosd
@@ -1 +0,0 @@
-Subproject commit 15756aed2e4af2f51133844b9f73221eacafa4c1
diff --git a/overdraw.c b/overdraw.c
@@ -0,0 +1,242 @@
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+#include <X11/keysym.h>
+#include <X11/cursorfont.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
+
+struct rgb {
+ double r, g, b;
+};
+
+struct line {
+ cairo_path_t *path;
+ struct rgb color;
+};
+
+static void die(const char *fmtstr, ...);
+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 cairo_surface_t *cs;
+static cairo_t *cr;
+
+static struct line *lines;
+static int lines_cnt, lines_cap;
+
+void
+die(const char *fmtstr, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmtstr);
+ vfprintf(stderr, fmtstr, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+void
+add_line(cairo_path_t *path, struct rgb *color)
+{
+ if (lines_cnt == lines_cap) {
+ lines_cap *= 2;
+ lines = realloc(lines, lines_cap * sizeof(struct line));
+ if (!lines) exit(1);
+ }
+
+ lines[lines_cnt].path = path;
+ lines[lines_cnt].color = *color;
+
+ lines_cnt++;
+}
+
+void
+clear_background(void)
+{
+ XClearWindow(display, win);
+}
+
+void
+draw_color_mark(void)
+{
+ struct rgb *c;
+
+ c = &colors[color_index];
+ cairo_save(cr);
+ cairo_set_source_rgba(cr, c->r, c->g, c->b, 1);
+ cairo_rectangle(cr, 10, 10, 20, 20);
+ cairo_stroke_preserve(cr);
+ cairo_fill(cr);
+ cairo_restore(cr);
+}
+
+void
+draw_lines(void)
+{
+ int i;
+
+ cairo_save(cr);
+ for (i = 0; i < lines_cnt; i++) {
+ cairo_new_path(cr);
+ cairo_set_source_rgba(cr, lines[i].color.r,
+ lines[i].color.g, lines[i].color.b, 1);
+ cairo_append_path(cr, lines[i].path);
+ cairo_stroke(cr);
+ }
+ cairo_restore(cr);
+}
+
+void
+init(void)
+{
+ Cursor cursor;
+ XSetWindowAttributes swa;
+ XGCValues gcvals;
+ Visual *visual;
+
+ display = XOpenDisplay(NULL);
+ root = DefaultRootWindow(display);
+
+ XGetWindowAttributes(display, root, &scr);
+
+ swa.override_redirect = 1;
+ win = XCreateWindow(display, root, scr.x, scr.y, scr.width, scr.height,
+ 0, CopyFromParent, InputOutput, CopyFromParent,
+ CWOverrideRedirect, &swa);
+ if (!win) die("Failed to create window\n");
+ XMapWindow(display, win);
+
+ image = XGetImage(display, win, scr.x, scr.y, scr.width, scr.height,
+ AllPlanes, ZPixmap);
+ if (!image) die("Failed to capture screen\n");
+
+ 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);
+
+ cs = cairo_xlib_surface_create(display, win, visual, scr.width, scr.height);
+ cr = cairo_create(cs);
+
+ cairo_surface_flush(cs);
+
+ cairo_set_line_width(cr, 2);
+ cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
+
+ colors[0] = (struct rgb) { 1.0, 1.0, 1.0 };
+ colors[1] = (struct rgb) { 1.0, 0.0, 0.0 };
+ 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);
+}
+
+int
+main(int argc, char *argv[])
+{
+ 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;
+ }
+ break;
+ case MotionNotify:
+ if (!draw) break;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, 1);
+ cairo_line_to(cr, ev.xmotion.x, ev.xmotion.y);
+ cairo_move_to(cr, ev.xmotion.x, ev.xmotion.y);
+ cairo_stroke_preserve(cr);
+ cairo_surface_flush(cs);
+ XFlush(display);
+ break;
+ case ButtonPress:
+ cairo_new_path(cr);
+ cairo_move_to(cr, ev.xmotion.x, ev.xmotion.y);
+ color = colors[color_index];
+ draw = 1;
+ break;
+ case ButtonRelease:
+ draw = 0;
+ path = cairo_copy_path(cr);
+ add_line(path, &color);
+
+ clear_background();
+ draw_color_mark();
+ draw_lines();
+ XFlush(display);
+ break;
+ }
+ }
+
+ cairo_surface_destroy(cs);
+
+ XDestroyWindow(display, win);
+ XCloseDisplay(display);
+}
diff --git a/src/overdraw.c b/src/overdraw.c
@@ -1,39 +0,0 @@
-#include <X11/X.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <locale.h>
-#include <xosd.h>
-
-#include <X11/Xlib.h>
-#include <time.h>
-
-int
-main (int argc, char *argv[])
-{
- Window root, fromroot, tmpwin;
- Display *display;
- XGCValues gcvals;
- GC gc;
- int x, y, prev_x, prev_y, tmp, screen;
- uint tmp2;
-
- display = XOpenDisplay(NULL);
- root = DefaultRootWindow(display);
- screen = XDefaultScreen(display);
-
- gcvals.foreground = WhitePixel(display, screen);
- gcvals.plane_mask = AllPlanes;
- gc = XCreateGC(display, root, GCPlaneMask | GCForeground, &gcvals);
-
- XQueryPointer(display, root, &fromroot, &tmpwin, &prev_x, &prev_y, &tmp, &tmp, &tmp2);
- while(1) {
- XQueryPointer(display, root, &fromroot, &tmpwin, &x, &y, &tmp, &tmp, &tmp2);
- if (x != prev_x || y != prev_y) {
- XDrawLine(display, root, gc, prev_x, prev_y, x, y);
- prev_x = x;
- prev_y = y;
- }
- usleep(50000);
- }
-}