URI: 
       tAdd double buffering - croptool - Image cropping tool
  HTML git clone git://lumidify.org/croptool.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/croptool.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit b4e5a51cc9a504d4386fe372378826d561babad4
   DIR parent 59805483b26574b37bb1784eaeb323dd71489f4b
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Fri,  5 Mar 2021 23:02:35 +0100
       
       Add double buffering
       
       Diffstat:
         M Makefile                            |       9 +++++++++
         M README                              |       5 ++++-
         M croptool.1                          |       7 ++-----
         M croptool.c                          |      97 ++++++++++++++++++++++++++-----
       
       4 files changed, 99 insertions(+), 19 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       t@@ -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:
   DIR diff --git a/README b/README
       t@@ -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.
   DIR diff --git a/croptool.1 b/croptool.1
       t@@ -1,4 +1,4 @@
       -.Dd January 10, 2021
       +.Dd March 5, 2021
        .Dt CROPTOOL 1
        .Os
        .Sh NAME
       t@@ -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
       t@@ -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
   DIR diff --git a/croptool.c b/croptool.c
       t@@ -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
       t@@ -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;
       t@@ -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;
       t@@ -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);
       t@@ -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);
       t@@ -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();
                }
       t@@ -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;
       t@@ -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
       t@@ -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) {