commit b4e5a51cc9a504d4386fe372378826d561babad4
parent 59805483b26574b37bb1784eaeb323dd71489f4b
Author: lumidify <nobody@lumidify.org>
Date: Fri, 5 Mar 2021 23:02:35 +0100
Add double buffering
Diffstat:
4 files changed, 99 insertions(+), 19 deletions(-)
diff --git a/Makefile b/Makefile
@@ -13,6 +13,15 @@ MAN1 = ${BIN:=.1}
CFLAGS += -D_POSIX_C_SOURCE=200809L `pkg-config --cflags x11` `imlib2-config --cflags`
LDFLAGS += `pkg-config --libs x11` `imlib2-config --libs` -lm
+# Configuration options:
+
+# comment to disable double buffering
+CFLAGS += `pkg-config --cflags xext`
+LDFLAGS += `pkg-config --libs xext`
+
+# uncomment to disable double buffering
+#CFLAGS += -DNODB
+
all: ${BIN}
.c:
diff --git a/README b/README
@@ -1,8 +1,11 @@
croptool - mass image cropping tool
REQUIREMENTS: xlib, imlib2
+OPTIONAL: xext (for double-buffering extension)
croptool is a simple tool to select cropping rectangles
on images and print out a command to crop each image.
-See croptool.1 for more information.
+See Makefile for compile-time options.
+
+See croptool.1 for usage information.
diff --git a/croptool.1 b/croptool.1
@@ -1,4 +1,4 @@
-.Dd January 10, 2021
+.Dd March 5, 2021
.Dt CROPTOOL 1
.Os
.Sh NAME
@@ -95,7 +95,7 @@ Redraw the window. This is useful when automatic redrawing is disabled with
.Bl -tag -width Ds
.It LEFT-CLICK
When inside an existing cropping rectangle, drag it around. When on one of the
-edges, resize the rectangle, locking it to the that dimension. When on one of
+edges, resize the rectangle, locking it to that dimension. When on one of
the corners, resize the rectangle regardless of dimension. When outside an
existing cropping rectangle, start a new cropping rectangle.
.El
@@ -110,9 +110,6 @@ existing cropping rectangle, start a new cropping rectangle.
The filenames are printed out without any escaping, so filenames with quotes
may cause issues depending on the output format.
.Pp
-Nothing in particular has been done to prevent screen flicker, so there is
-flickering when resizing the window or cropping rectangle.
-.Pp
Transparent portions of images should probably be shown differently, but I'm
too lazy to fix that and don't really care at the moment.
.Pp
diff --git a/croptool.c b/croptool.c
@@ -26,6 +26,9 @@
#include <X11/keysym.h>
#include <X11/XF86keysym.h>
#include <X11/cursorfont.h>
+#ifndef NODB
+#include <X11/extensions/Xdbe.h>
+#endif
#include <Imlib2.h>
/* The number of pixels to check on each side when checking
@@ -84,6 +87,11 @@ static struct {
GC gc;
Window win;
Visual *vis;
+ Drawable drawable;
+ #ifndef NODB
+ XdbeBackBuffer back_buf;
+ int db_enabled;
+ #endif
Colormap cm;
int screen;
int depth;
@@ -284,12 +292,63 @@ setup(int argc, char *argv[]) {
state.depth = DefaultDepth(state.dpy, state.screen);
state.cm = DefaultColormap(state.dpy, state.screen);
+ #ifndef NODB
+ state.db_enabled = 0;
+ /* based on http://wili.cc/blog/xdbe.html */
+ int major, minor;
+ if (XdbeQueryExtension(state.dpy, &major, &minor)) {
+ int num_screens = 1;
+ Drawable screens[] = { DefaultRootWindow(state.dpy) };
+ XdbeScreenVisualInfo *info = XdbeGetVisualInfo(state.dpy, screens, &num_screens);
+ if (!info || num_screens < 1 || info->count < 1) {
+ fprintf(stderr, "Warning: No visuals support Xdbe, "
+ "double buffering disabled.\n");
+ } else {
+ XVisualInfo xvisinfo_templ;
+ xvisinfo_templ.visualid = info->visinfo[0].visual;
+ xvisinfo_templ.screen = 0;
+ xvisinfo_templ.depth = info->visinfo[0].depth;
+ int matches;
+ XVisualInfo *xvisinfo_match = XGetVisualInfo(
+ state.dpy,
+ VisualIDMask | VisualScreenMask | VisualDepthMask,
+ &xvisinfo_templ, &matches
+ );
+ if (!xvisinfo_match || matches < 1) {
+ fprintf(stderr, "Warning: Couldn't match a Visual "
+ "with double buffering, double "
+ "buffering disabled.\n");
+ } else {
+ state.vis = xvisinfo_match->visual;
+ state.depth = xvisinfo_match->depth;
+ state.db_enabled = 1;
+ }
+ XFree(xvisinfo_match);
+ }
+ XdbeFreeVisualInfo(info);
+ } else {
+ fprintf(stderr, "Warning: No Xdbe support, double buffering disabled.\n");
+ }
+ #endif
+
memset(&attrs, 0, sizeof(attrs));
- attrs.background_pixmap = None;
+ attrs.background_pixel = BlackPixel(state.dpy, state.screen);
attrs.colormap = state.cm;
+ attrs.bit_gravity = NorthWestGravity;
state.win = XCreateWindow(state.dpy, DefaultRootWindow(state.dpy), 0, 0,
state.window_w, state.window_h, 0, state.depth,
- InputOutput, state.vis, CWBackPixmap | CWColormap, &attrs);
+ InputOutput, state.vis, CWBackPixel | CWColormap | CWBitGravity, &attrs);
+
+ #ifndef NODB
+ if (state.db_enabled) {
+ state.back_buf = XdbeAllocateBackBufferName(state.dpy, state.win, XdbeCopied);
+ state.drawable = state.back_buf;
+ } else {
+ state.drawable = state.win;
+ }
+ #else
+ state.drawable = state.win;
+ #endif
memset(&gcv, 0, sizeof(gcv));
gcv.line_width = LINE_WIDTH;
@@ -327,7 +386,7 @@ setup(int argc, char *argv[]) {
imlib_context_set_display(state.dpy);
imlib_context_set_visual(state.vis);
imlib_context_set_colormap(state.cm);
- imlib_context_set_drawable(state.win);
+ imlib_context_set_drawable(state.drawable);
state.updates = imlib_updates_init();
next_picture(0);
@@ -442,8 +501,8 @@ redraw(void) {
Imlib_Updates current_update;
if (!state.cur_image || state.cur_selection < 0) {
XSetForeground(state.dpy, state.gc, BlackPixel(state.dpy, state.screen));
- XFillRectangle(state.dpy, state.win, state.gc, 0, 0, state.window_w, state.window_h);
- return;
+ XFillRectangle(state.dpy, state.drawable, state.gc, 0, 0, state.window_w, state.window_h);
+ goto swap_buffers;
}
struct Selection *sel = &state.selections[state.cur_selection];
state.updates = imlib_updates_merge_for_rendering(state.updates, sel->scaled_w, sel->scaled_h);
@@ -456,11 +515,9 @@ redraw(void) {
imlib_context_set_image(buffer);
imlib_blend_image_onto_image(
state.cur_image, 0, 0, 0,
- state.selections[state.cur_selection].orig_w,
- state.selections[state.cur_selection].orig_h,
+ sel->orig_w, sel->orig_h,
-up_x, -up_y,
- state.selections[state.cur_selection].scaled_w,
- state.selections[state.cur_selection].scaled_h);
+ sel->scaled_w, sel->scaled_h);
imlib_render_image_on_drawable(up_x, up_y);
imlib_free_image();
}
@@ -470,8 +527,8 @@ redraw(void) {
/* wipe the black area around the image */
XSetForeground(state.dpy, state.gc, BlackPixel(state.dpy, state.screen));
- XFillRectangle(state.dpy, state.win, state.gc, 0, sel->scaled_h, sel->scaled_w, state.window_h - sel->scaled_h);
- XFillRectangle(state.dpy, state.win, state.gc, sel->scaled_w, 0, state.window_w - sel->scaled_w, state.window_h);
+ XFillRectangle(state.dpy, state.drawable, state.gc, 0, sel->scaled_h, sel->scaled_w, state.window_h - sel->scaled_h);
+ XFillRectangle(state.dpy, state.drawable, state.gc, sel->scaled_w, 0, state.window_w - sel->scaled_w, state.window_h);
/* draw the rectangle */
struct Rect rect = sel->rect;
@@ -479,8 +536,22 @@ redraw(void) {
XColor col = state.cur_col == 1 ? state.col1 : state.col2;
XSetForeground(state.dpy, state.gc, col.pixel);
sort_coordinates(&rect.x0, &rect.y0, &rect.x1, &rect.y1);
- XDrawRectangle(state.dpy, state.win, state.gc, rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0);
+ XDrawRectangle(state.dpy, state.drawable, state.gc, rect.x0, rect.y0, rect.x1 - rect.x0, rect.y1 - rect.y0);
+ }
+
+swap_buffers:
+ #ifndef NODB
+ if (state.db_enabled) {
+ XdbeSwapInfo swap_info;
+ swap_info.swap_window = state.win;
+ swap_info.swap_action = XdbeCopied;
+
+ if (!XdbeSwapBuffers(state.dpy, &swap_info, 1))
+ fprintf(stderr, "Warning: Unable to swap buffers.\n");
}
+ #else
+ ;
+ #endif
}
static void
@@ -864,7 +935,7 @@ switch_color(void) {
static void
key_press(XEvent event) {
XWindowAttributes attrs;
- char buf[64];
+ char buf[32];
KeySym sym;
XLookupString(&event.xkey, buf, sizeof(buf), &sym, NULL);
switch (sym) {