unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* RFC: flicker-free double-buffered Emacs under X11
@ 2016-10-21  1:32 Daniel Colascione
  2016-10-21  2:27 ` Lars Ingebrigtsen
                   ` (3 more replies)
  0 siblings, 4 replies; 31+ messages in thread
From: Daniel Colascione @ 2016-10-21  1:32 UTC (permalink / raw)
  To: emacs-devel

This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to 
avoid showing the user incomplete drawing results. Without this patch, I 
can make Emacs flicker like crazy by running isearch for a piece of text 
unique in a buffer and holding down C-s. With this patch, Emacs does not 
flicker no matter what I do to it.

The patch also stops flickering that occurs when using the "solid 
resizing" feature of some window managers --- i.e., when the WM redraws 
windows as the user drags their edges, as opposed to displaying some 
kind of bounding-box in lieu of the actual window contents.

I've tried to avoid changing the logic in the single-buffered case 
(which we still support), and I've also tried to avoid touching Cairo 
rendering. This patch should work with any toolkit --- I've tested GTK+3 
and no toolkit at all.

A few notes:

* We do a buffer flip at the end of redisplay instead of in 
x_update_end() so the user never sees the completely-cleared state that 
we enter immediately after clear_garbaged_frames(). x_update_end() does 
do a buffer flip if it's called outside redisplay. I've added a new 
terminal hook to support this hack.

* The DBE documentation claims that XClearWindow and XClearArea clear 
both the front and back buffers. It's a lie. In my experiments, these 
functions clear only the front buffer.

* XFT stops drawing after we give XftCreateDraw a DBE back-buffer and 
the size of that back buffer changes. To work around this problem, we 
discard any caches XftDraw object we might have to a frame after that 
frame changes size. I haven't noticed any performance problems.

commit 15fdd8f63533201f05627ede634a8f5ae4757d7e
Author: Daniel Colascione <dancol@dancol.org>
Date:   Thu Oct 20 16:50:54 2016 -0700

     Add double-buffered output support to Emacs

diff --git a/configure.ac b/configure.ac
index cd11b10..4716b43 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3712,6 +3712,24 @@ AC_DEFUN
  AC_SUBST(XFIXES_CFLAGS)
  AC_SUBST(XFIXES_LIBS)

+### Use Xdbe (-lXdbe) if available
+HAVE_XDBE=no
+if test "${HAVE_X11}" = "yes"; then
+  AC_CHECK_HEADER(X11/extensions/Xdbe.h,
+    [AC_CHECK_LIB(Xext, XdbeAllocateBackBufferName, HAVE_XDBE=yes)],
+    [],
+    [#include <X11/Xlib.h>
+    ])
+  if test $HAVE_XDBE = yes; then
+    XDBE_LIBS=-lXext
+  fi
+  if test $HAVE_XDBE = yes; then
+    AC_DEFINE(HAVE_XDBE, 1, [Define to 1 if you have the Xdbe extension.])
+  fi
+fi
+AC_SUBST(XDBE_CFLAGS)
+AC_SUBST(XDBE_LIBS)
+
  ### Use libxml (-lxml2) if available
  ### mingw32 doesn't use -lxml2, since it loads the library dynamically.
  HAVE_LIBXML2=no
diff --git a/src/Makefile.in b/src/Makefile.in
index 89f7a92..dc0bfff 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -254,6 +254,9 @@ XINERAMA_CFLAGS =
  XFIXES_LIBS = @XFIXES_LIBS@
  XFIXES_CFLAGS = @XFIXES_CFLAGS@

+XDBE_LIBS = @XDBE_LIBS@
+XDBE_CFLAGS = @XDBE_CFLAGS@
+
  ## widget.o if USE_X_TOOLKIT, otherwise empty.
  WIDGET_OBJ=@WIDGET_OBJ@

@@ -372,7 +375,7 @@ ALL_CFLAGS=
    $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \
    $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
    $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \
-  $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \
+  $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \
    $(WEBKIT_CFLAGS) \
    $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
    $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
@@ -489,6 +492,7 @@ LIBES =
     $(WEBKIT_LIBS) \
     $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
     $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \
+   $(XDBE_LIBS) \
     $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \
     $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) 
$(LIBSELINUX_LIBS) \
     $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
diff --git a/src/dispnew.c b/src/dispnew.c
index 70d4de0..8f81cee 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f)
  {
    /* Error if F has no glyphs.  */
    eassert (f->glyphs_initialized_p);
+  font_flush_frame_caches (f);
    update_begin (f);
    if (FRAME_MSDOS_P (f))
      FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f));
diff --git a/src/font.c b/src/font.c
index f8e6794..033995e 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, 
Lisp_Object arg, Lisp_Object result)
  }

  void
+font_flush_frame_caches (struct frame *f)
+{
+  struct font_driver_list *list;
+
+  for (list = f->font_driver_list; list; list = list->next)
+    if (list->on && list->driver->flush_frame_caches)
+      list->driver->flush_frame_caches (f);
+}
+
+void
  syms_of_font (void)
  {
    sort_shift_bits[FONT_TYPE_INDEX] = 0;
diff --git a/src/font.h b/src/font.h
index cf47729..961e9c4 100644
--- a/src/font.h
+++ b/src/font.h
@@ -763,6 +763,12 @@ struct font_driver
       Return non-nil if the driver support rendering of combining
       characters for FONT according to Unicode combining class.  */
    Lisp_Object (*combining_capability) (struct font *font);
+
+  /* Optional
+
+     Called when frame F is redrawn from scratch.  Font engines may
+     invalidate certain caches in this case.  */
+  void (*flush_frame_caches) (struct frame *f);
  };


@@ -862,7 +868,9 @@ extern void *font_get_frame_data (struct frame *f, 
Lisp_Object);
  extern void font_filter_properties (Lisp_Object font,
  				    Lisp_Object alist,
  				    const char *const boolean_properties[],
-				    const char *const non_boolean_properties[]);
+                                    const char *const 
non_boolean_properties[]);
+
+extern void font_flush_frame_caches (struct frame *f);

  #ifdef HAVE_FREETYPE
  extern struct font_driver ftfont_driver;
diff --git a/src/ftxfont.c b/src/ftxfont.c
index f49d44f..bfdeb40 100644
--- a/src/ftxfont.c
+++ b/src/ftxfont.c
@@ -95,7 +95,7 @@ ftxfont_get_gcs (struct frame *f, unsigned long 
foreground, unsigned long backgr
        if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &color))
  	break;
        xgcv.foreground = color.pixel;
-      new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), 
FRAME_X_DRAWABLE (f),
  				   GCForeground, &xgcv);
      }
    unblock_input ();
@@ -139,14 +139,14 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, 
GC *gcs, struct font *font,
  		p[n[0]].y = y - bitmap.top + i;
  		if (++n[0] == size)
  		  {
-		    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  				 gc_fore, p, size, CoordModeOrigin);
  		    n[0] = 0;
  		  }
  	      }
  	}
        if (flush && n[0] > 0)
-	XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+        XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  		     gc_fore, p, n[0], CoordModeOrigin);
      }
    else
@@ -168,7 +168,7 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC 
*gcs, struct font *font,
  		  pp[n[idx]].y = y - bitmap.top + i;
  		  if (++(n[idx]) == size)
  		    {
-		      XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                      XDrawPoints (FRAME_X_DISPLAY (f), 
FRAME_X_DRAWABLE (f),
  				   idx == 6 ? gc_fore : gcs[idx], pp, size,
  				   CoordModeOrigin);
  		      n[idx] = 0;
@@ -180,10 +180,10 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, 
GC *gcs, struct font *font,
  	{
  	  for (i = 0; i < 6; i++)
  	    if (n[i] > 0)
-	      XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+              XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  			   gcs[i], p + 0x100 * i, n[i], CoordModeOrigin);
  	  if (n[6] > 0)
-	    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+            XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  			 gc_fore, p + 0x600, n[6], CoordModeOrigin);
  	}
      }
@@ -203,7 +203,7 @@ ftxfont_draw_background (struct frame *f, struct 
font *font, GC gc, int x, int y
    XGetGCValues (FRAME_X_DISPLAY (f), gc,
  		GCForeground | GCBackground, &xgcv);
    XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background);
-  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc,
+  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc,
  		  x, y - FONT_BASE (font), width, FONT_HEIGHT (font));
    XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground);
  }
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 88e6d30..f81940b 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -48,6 +48,10 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
  #include "emacsgtkfixed.h"
  #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
  #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
  #define gtk_widget_set_has_window(w, b) \
    (gtk_fixed_set_has_window (GTK_FIXED (w), b))
@@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f)
       by callers of this function.  */
    gtk_widget_realize (wfixed);
    FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
+  set_up_x_back_buffer (f);

    /* Since GTK clears its window by filling with the background color,
       we must keep X and GTK background in sync.  */
@@ -1296,6 +1301,15 @@ xg_free_frame_widgets (struct frame *f)
        if (tbinfo)
          xfree (tbinfo);

+#ifdef HAVE_XDBE
+      if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+        {
+          XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f),
+                                        FRAME_X_DRAWABLE (f));
+          FRAME_X_DRAWABLE (f) = 0;
+        }
+#endif
+
        gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
        FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
        FRAME_GTK_OUTER_WIDGET (f) = 0;
diff --git a/src/image.c b/src/image.c
index 9bd2455..1303a93 100644
--- a/src/image.c
+++ b/src/image.c
@@ -220,7 +220,7 @@ x_create_bitmap_from_data (struct frame *f, char 
*bits, unsigned int width, unsi

  #ifdef HAVE_X_WINDOWS
    Pixmap bitmap;
-  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE 
(f),
  				  bits, width, height);
    if (! bitmap)
      return -1;
@@ -327,7 +327,7 @@ x_create_bitmap_from_file (struct frame *f, 
Lisp_Object file)

    filename = SSDATA (found);

-  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  			    filename, &width, &height, &bitmap, &xhot, &yhot);
    if (result != BitmapSuccess)
      return -1;
@@ -1952,7 +1952,7 @@ x_create_x_image_and_pixmap (struct frame *f, int 
width, int height, int depth,
  {
  #ifdef HAVE_X_WINDOWS
    Display *display = FRAME_X_DISPLAY (f);
-  Window window = FRAME_X_WINDOW (f);
+  Drawable drawable = FRAME_X_DRAWABLE (f);
    Screen *screen = FRAME_X_SCREEN (f);

    eassert (input_blocked_p ());
@@ -1981,7 +1981,7 @@ x_create_x_image_and_pixmap (struct frame *f, int 
width, int height, int depth,
    (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);

    /* Allocate a pixmap of the same size.  */
-  *pixmap = XCreatePixmap (display, window, width, height, depth);
+  *pixmap = XCreatePixmap (display, drawable, width, height, depth);
    if (*pixmap == NO_PIXMAP)
      {
        x_destroy_x_image (*ximg);
@@ -2742,7 +2742,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, 
struct image *img, char *data,
    img->pixmap =
     (x_check_image_size (0, img->width, img->height)
      ? XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f),
-				   FRAME_X_WINDOW (f),
+                                   FRAME_X_DRAWABLE (f),
  				   data,
  				   img->width, img->height,
  				   fg, bg,
@@ -3520,7 +3520,7 @@ x_create_bitmap_from_xpm_data (struct frame *f, 
const char **bits)
    xpm_init_color_cache (f, &attrs);
  #endif

-  rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  				(char **) bits, &bitmap, &mask, &attrs);
    if (rc != XpmSuccess)
      {
@@ -3758,7 +3758,7 @@ xpm_load (struct frame *f, struct image *img)
  #ifdef HAVE_X_WINDOWS
    if (rc == XpmSuccess)
      {
-      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), 
FRAME_X_DRAWABLE (f),
  				   img->ximg->width, img->ximg->height,
  				   img->ximg->depth);
        if (img->pixmap == NO_PIXMAP)
@@ -3768,7 +3768,7 @@ xpm_load (struct frame *f, struct image *img)
  	}
        else if (img->mask_img)
  	{
-	  img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+          img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), 
FRAME_X_DRAWABLE (f),
  				     img->mask_img->width,
  				     img->mask_img->height,
  				     img->mask_img->depth);
@@ -9541,7 +9541,7 @@ gs_load (struct frame *f, struct image *img)
      {
        /* Only W32 version did BLOCK_INPUT here.  ++kfs */
        block_input ();
-      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), 
FRAME_X_DRAWABLE (f),
  				   img->width, img->height,
  				   DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
        unblock_input ();
@@ -9557,7 +9557,7 @@ gs_load (struct frame *f, struct image *img)
       if successful.  We do not record_unwind_protect here because
       other places in redisplay like calling window scroll functions
       don't either.  Let the Lisp loader use `unwind-protect' instead.  */
-  printnum1 = FRAME_X_WINDOW (f);
+  printnum1 = FRAME_X_DRAWABLE (f);
    printnum2 = img->pixmap;
    window_and_pixmap_id
      = make_formatted_string (buffer, "%"pMu" %"pMu, printnum1, printnum2);
diff --git a/src/termhooks.h b/src/termhooks.h
index ff74d99..c8d7fae 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -477,6 +477,7 @@ struct terminal

    void (*update_begin_hook) (struct frame *);
    void (*update_end_hook) (struct frame *);
+  void (*redisplay_end_hook) (struct frame *);
    void (*set_terminal_window_hook) (struct frame *, int);

    /* Multi-frame and mouse support hooks.  */
diff --git a/src/xdisp.c b/src/xdisp.c
index 3af5ea4..04f0ca1 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2501,7 +2501,7 @@ remember_mouse_glyph (struct frame *f, int gx, int 
gy, NativeRectangle *rect)

    /* Visible feedback for debugging.  */
  #if false && defined HAVE_X_WINDOWS
-  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  		  f->output_data.x->normal_gc,
  		  gx, gy, width, height);
  #endif
@@ -14211,6 +14211,13 @@ redisplay_internal (void)
        windows_or_buffers_changed = 0;
      }

+  FOR_EACH_FRAME (tail, frame)
+    {
+      struct frame *f = XFRAME (frame);
+      if (FRAME_TERMINAL (f)->redisplay_end_hook)
+        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
+    }
+
    /* Start SIGIO interrupts coming again.  Having them off during the
       code above makes it less likely one will discard output, but not
       impossible, since there might be stuff in the system buffer here.
@@ -24608,7 +24615,7 @@ init_glyph_string (struct glyph_string *s,
    s->hdc = hdc;
  #endif
    s->display = FRAME_X_DISPLAY (s->f);
-  s->window = FRAME_X_WINDOW (s->f);
+  s->window = FRAME_X_DRAWABLE (s->f);
    s->char2b = char2b;
    s->hl = hl;
    s->row = row;
diff --git a/src/xfaces.c b/src/xfaces.c
index 5837f35..accb98b 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -495,7 +495,7 @@ x_create_gc (struct frame *f, unsigned long mask, 
XGCValues *xgcv)
  {
    GC gc;
    block_input ();
-  gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), mask, xgcv);
+  gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), mask, xgcv);
    unblock_input ();
    IF_DEBUG (++ngcs);
    return gc;
diff --git a/src/xfns.c b/src/xfns.c
index 8571d0e..2098036 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -53,6 +53,10 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
  #include "gtkutil.h"
  #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
  #ifdef USE_X_TOOLKIT
  #include <X11/Shell.h>

@@ -2483,6 +2487,29 @@ xic_set_xfontset (struct frame *f, const char 
*base_fontname)


  \f
+
+void
+set_up_x_back_buffer (struct frame* f)
+{
+  FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
+#ifdef HAVE_XDBE
+  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
+    {
+      /* If allocating a back buffer fails, just use single-buffered
+         rendering.  */
+      x_sync (f);
+      x_catch_errors (FRAME_X_DISPLAY (f));
+      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
+        FRAME_X_DISPLAY (f),
+        FRAME_X_WINDOW (f),
+        XdbeCopied);
+      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
+        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
+      x_uncatch_errors_after_check ();
+    }
+#endif
+}
+
  #ifdef USE_X_TOOLKIT

  /* Create and set up the X widget for frame F.  */
@@ -2638,7 +2665,7 @@ x_window (struct frame *f, long window_prompting)
  		     f->output_data.x->parent_desc, 0, 0);

    FRAME_X_WINDOW (f) = XtWindow (frame_widget);
-
+  set_up_x_back_buffer (f);
    validate_x_resource_name ();

    class_hints.res_name = SSDATA (Vx_resource_name);
@@ -2784,7 +2811,8 @@ x_window (struct frame *f)
  		     CopyFromParent, /* depth */
  		     InputOutput, /* class */
  		     FRAME_X_VISUAL (f),
-		     attribute_mask, &attributes);
+                     attribute_mask, &attributes);
+  set_up_x_back_buffer (f);

  #ifdef HAVE_X_I18N
    if (use_xim)
@@ -2938,7 +2966,7 @@ x_make_gc (struct frame *f)
    gc_values.line_width = 0;	/* Means 1 using fast algorithm.  */
    f->output_data.x->normal_gc
      = XCreateGC (FRAME_X_DISPLAY (f),
-		 FRAME_X_WINDOW (f),
+                 FRAME_X_DRAWABLE (f),
  		 GCLineWidth | GCForeground | GCBackground,
  		 &gc_values);

@@ -2947,7 +2975,7 @@ x_make_gc (struct frame *f)
    gc_values.background = FRAME_FOREGROUND_PIXEL (f);
    f->output_data.x->reverse_gc
      = XCreateGC (FRAME_X_DISPLAY (f),
-		 FRAME_X_WINDOW (f),
+                 FRAME_X_DRAWABLE (f),
  		 GCForeground | GCBackground | GCLineWidth,
  		 &gc_values);

@@ -2956,7 +2984,7 @@ x_make_gc (struct frame *f)
    gc_values.background = f->output_data.x->cursor_pixel;
    gc_values.fill_style = FillOpaqueStippled;
    f->output_data.x->cursor_gc
-    = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+    = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  		 (GCForeground | GCBackground
  		  | GCFillStyle | GCLineWidth),
  		 &gc_values);
@@ -5636,7 +5664,8 @@ x_create_tip_frame (struct x_display_info 
*dpyinfo, Lisp_Object parms)
  		       /* Border.  */
  		       f->border_width,
  		       CopyFromParent, InputOutput, CopyFromParent,
-		       mask, &attrs);
+                       mask, &attrs);
+    set_up_x_back_buffer (f);
      XChangeProperty (FRAME_X_DISPLAY (f), tip_window,
                       FRAME_DISPLAY_INFO (f)->Xatom_net_window_type,
                       XA_ATOM, 32, PropModeReplace,
diff --git a/src/xfont.c b/src/xfont.c
index 45b0e0a..c2b7317 100644
--- a/src/xfont.c
+++ b/src/xfont.c
@@ -1057,20 +1057,20 @@ xfont_draw (struct glyph_string *s, int from, 
int to, int x, int y,
  	{
  	  if (s->padding_p)
  	    for (i = 0; i < len; i++)
-	      XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+              XDrawImageString (FRAME_X_DISPLAY (s->f), 
FRAME_X_DRAWABLE (s->f),
  				gc, x + i, y, str + i, 1);
  	  else
-	    XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+            XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE 
(s->f),
  			      gc, x, y, str, len);
  	}
        else
  	{
  	  if (s->padding_p)
  	    for (i = 0; i < len; i++)
-	      XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+              XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
  			   gc, x + i, y, str + i, 1);
  	  else
-	    XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+            XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
  			 gc, x, y, str, len);
  	}
        unblock_input ();
@@ -1083,20 +1083,20 @@ xfont_draw (struct glyph_string *s, int from, 
int to, int x, int y,
      {
        if (s->padding_p)
  	for (i = 0; i < len; i++)
-	  XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+          XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE 
(s->f),
  			      gc, x + i, y, s->char2b + from + i, 1);
        else
-	XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+        XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE 
(s->f),
  			    gc, x, y, s->char2b + from, len);
      }
    else
      {
        if (s->padding_p)
  	for (i = 0; i < len; i++)
-	  XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+          XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
  			 gc, x + i, y, s->char2b + from + i, 1);
        else
-	XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+        XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
  		       gc, x, y, s->char2b + from, len);
      }
    unblock_input ();
diff --git a/src/xftfont.c b/src/xftfont.c
index 34c6f7d..447adf6 100644
--- a/src/xftfont.c
+++ b/src/xftfont.c
@@ -586,7 +586,7 @@ xftfont_get_xft_draw (struct frame *f)
      {
        block_input ();
        xft_draw= XftDrawCreate (FRAME_X_DISPLAY (f),
-			       FRAME_X_WINDOW (f),
+                               FRAME_X_DRAWABLE (f),
  			       FRAME_X_VISUAL (f),
  			       FRAME_X_COLORMAP (f));
        unblock_input ();
@@ -695,6 +695,13 @@ xftfont_end_for_frame (struct frame *f)
    return 0;
  }

+static void
+xftfont_flush_frame_caches (struct frame *f)
+{
+  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+    xftfont_end_for_frame (f);
+}
+
  static bool
  xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object,
                          Lisp_Object entity)
@@ -777,6 +784,9 @@ This is needed with some fonts to correct vertical 
overlap of glyphs.  */);
  #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF)
    xftfont_driver.shape = xftfont_shape;
  #endif
+  // When using X double buffering, the XftDraw structure we
+  // build seems to be useless once a frame is resized, so
+  xftfont_driver.flush_frame_caches = xftfont_flush_frame_caches;

    register_font_driver (&xftfont_driver, NULL);
  }
diff --git a/src/xterm.c b/src/xterm.c
index 7476694..7116784 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -45,6 +45,10 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
  #include <X11/extensions/Xrender.h>
  #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
  /* Load sys/types.h if not already loaded.
     In some systems loading it twice is suicidal.  */
  #ifndef makedev
@@ -360,7 +364,7 @@ x_begin_cr_clip (struct frame *f, GC gc)
          {
            cairo_surface_t *surface;
            surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
-                                               FRAME_X_WINDOW (f),
+                                               FRAME_X_DRAWABLE (f),
                                                 FRAME_DISPLAY_INFO 
(f)->visual,
                                                 FRAME_PIXEL_WIDTH (f),
                                                 FRAME_PIXEL_HEIGHT (f));
@@ -722,7 +726,7 @@ x_fill_rectangle (struct frame *f, GC gc, int x, int 
y, int width, int height)
    cairo_fill (cr);
    x_end_cr_clip (f);
  #else
-  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  		  gc, x, y, width, height);
  #endif
  }
@@ -740,7 +744,7 @@ x_draw_rectangle (struct frame *f, GC gc, int x, int 
y, int width, int height)
    cairo_stroke (cr);
    x_end_cr_clip (f);
  #else
-  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  		  gc, x, y, width, height);
  #endif
  }
@@ -756,7 +760,10 @@ x_clear_window (struct frame *f)
    cairo_paint (cr);
    x_end_cr_clip (f);
  #else
-  XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
+  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+    x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
+  else
+    XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
  #endif
  }

@@ -1067,7 +1074,7 @@ x_draw_vertical_window_border (struct window *w, 
int x, int y0, int y1)
  #ifdef USE_CAIRO
    x_fill_rectangle (f, f->output_data.x->normal_gc, x, y0, 1, y1 - y0);
  #else
-  XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
  	     f->output_data.x->normal_gc, x, y0, x, y1);
  #endif
  }
@@ -1179,6 +1186,21 @@ x_update_window_end (struct window *w, bool 
cursor_on_p,
  /* End update of frame F.  This function is installed as a hook in
     update_end.  */

+#if defined (HAVE_XDBE)
+static void
+show_back_buffer (struct frame *f)
+{
+  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+    {
+      XdbeSwapInfo swap_info;
+      memset (&swap_info, 0, sizeof (swap_info));
+      swap_info.swap_window = FRAME_X_WINDOW (f);
+      swap_info.swap_action = XdbeCopied;
+      XdbeSwapBuffers (FRAME_X_DISPLAY (f), &swap_info, 1);
+    }
+}
+#endif
+
  static void
  x_update_end (struct frame *f)
  {
@@ -1207,7 +1229,7 @@ x_update_end (struct frame *f)
            if (! FRAME_EXTERNAL_MENU_BAR (f))
              height += FRAME_MENU_BAR_HEIGHT (f);
            surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
-                                               FRAME_X_WINDOW (f),
+                                               FRAME_X_DRAWABLE (f),
                                                 FRAME_DISPLAY_INFO 
(f)->visual,
                                                 width,
                                                 height);
@@ -1220,7 +1242,14 @@ x_update_end (struct frame *f)
        cairo_destroy (cr);
        unblock_input ();
      }
-#endif /* USE_CAIRO */
+#endif
+
+#ifdef HAVE_XDBE
+  if (redisplaying_p)
+    FRAME_X_NEED_BUFFER_FLIP (f) = true;
+  else
+    show_back_buffer (f);
+#endif

  #ifndef XFlush
    block_input ();
@@ -1229,6 +1258,17 @@ x_update_end (struct frame *f)
  #endif
  }

+static void
+x_redisplay_end (struct frame *f)
+{
+#ifdef HAVE_XDBE
+  if (FRAME_X_NEED_BUFFER_FLIP (f))
+    {
+      show_back_buffer (f);
+      FRAME_X_NEED_BUFFER_FLIP (f) = false;
+    }
+#endif
+}

  /* This function is called from various places in xdisp.c
     whenever a complete update has been performed.  */
@@ -1354,7 +1394,7 @@ x_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row, struct draw_fring
  #else  /* not USE_CAIRO */
    if (p->which)
      {
-      Window window = FRAME_X_WINDOW (f);
+      Drawable drawable = FRAME_X_DRAWABLE (f);
        char *bits;
        Pixmap pixmap, clipmask = (Pixmap) 0;
        int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f));
@@ -1367,7 +1407,7 @@ x_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row, struct draw_fring

        /* Draw the bitmap.  I believe these small pixmaps can be cached
  	 by the server.  */
-      pixmap = XCreatePixmapFromBitmapData (display, window, bits, 
p->wd, p->h,
+      pixmap = XCreatePixmapFromBitmapData (display, drawable, bits, 
p->wd, p->h,
  					    (p->cursor_p
  					     ? (p->overlay_p ? face->background
  						: f->output_data.x->cursor_pixel)
@@ -1386,7 +1426,7 @@ x_draw_fringe_bitmap (struct window *w, struct 
glyph_row *row, struct draw_fring
  	  XChangeGC (display, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin, 
&gcv);
  	}

-      XCopyArea (display, pixmap, window, gc, 0, 0,
+      XCopyArea (display, pixmap, drawable, gc, 0, 0,
  		 p->wd, p->h, p->x, p->y);
        XFreePixmap (display, pixmap);

@@ -2565,7 +2605,7 @@ x_setup_relief_color (struct frame *f, struct 
relief *relief, double factor,
      {
        xgcv.stipple = dpyinfo->gray;
        mask |= GCStipple;
-      relief->gc = XCreateGC (dpy, FRAME_X_WINDOW (f), mask, &xgcv);
+      relief->gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), mask, &xgcv);
      }
    else
      XChangeGC (dpy, relief->gc, mask, &xgcv);
@@ -2696,7 +2736,7 @@ x_draw_relief_rect (struct frame *f,
    x_reset_clip_rectangles (f, bottom_right_gc);
  #else
    Display *dpy = FRAME_X_DISPLAY (f);
-  Window window = FRAME_X_WINDOW (f);
+  Drawable drawable = FRAME_X_DRAWABLE (f);
    int i;
    GC gc;

@@ -2715,12 +2755,12 @@ x_draw_relief_rect (struct frame *f,
    if (top_p)
      {
        if (width == 1)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
  		   left_x + left_p, top_y,
  		   right_x + !right_p, top_y);

        for (i = 1; i < width; ++i)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
  		   left_x  + i * left_p, top_y + i,
  		   right_x + 1 - i * right_p, top_y + i);
      }
@@ -2729,13 +2769,13 @@ x_draw_relief_rect (struct frame *f,
    if (left_p)
      {
        if (width == 1)
-	XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y);
+        XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y);

-      XClearArea (dpy, window, left_x, top_y, 1, 1, False);
-      XClearArea (dpy, window, left_x, bottom_y, 1, 1, False);
+      x_clear_area(f, left_x, top_y, 1, 1);
+      x_clear_area(f, left_x, bottom_y, 1, 1);

        for (i = (width > 1 ? 1 : 0); i < width; ++i)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
  		   left_x + i, top_y + (i + 1) * top_p,
  		   left_x + i, bottom_y + 1 - (i + 1) * bot_p);
      }
@@ -2751,23 +2791,23 @@ x_draw_relief_rect (struct frame *f,
      {
        /* Outermost top line.  */
        if (top_p)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
  		   left_x  + left_p, top_y,
  		   right_x + !right_p, top_y);

        /* Outermost left line.  */
        if (left_p)
-	XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y);
+        XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y);
      }

    /* Bottom.  */
    if (bot_p)
      {
-      XDrawLine (dpy, window, gc,
+      XDrawLine (dpy, drawable, gc,
  		 left_x + left_p, bottom_y,
  		 right_x + !right_p, bottom_y);
        for (i = 1; i < width; ++i)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
  		   left_x  + i * left_p, bottom_y - i,
  		   right_x + 1 - i * right_p, bottom_y - i);
      }
@@ -2775,10 +2815,10 @@ x_draw_relief_rect (struct frame *f,
    /* Right.  */
    if (right_p)
      {
-      XClearArea (dpy, window, right_x, top_y, 1, 1, False);
-      XClearArea (dpy, window, right_x, bottom_y, 1, 1, False);
+      x_clear_area(f, right_x, top_y, 1, 1);
+      x_clear_area(f, right_x, bottom_y, 1, 1);
        for (i = 0; i < width; ++i)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
  		   right_x - i, top_y + (i + 1) * top_p,
  		   right_x - i, bottom_y + 1 - (i + 1) * bot_p);
      }
@@ -3741,7 +3781,7 @@ x_shift_glyphs_for_insert (struct frame *f, int x, 
int y, int width, int height,
  /* Never called on a GUI frame, see
     http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html
  */
-  XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), FRAME_X_WINDOW (f),
+  XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), 
FRAME_X_DRAWABLE (f),
  	     f->output_data.x->normal_gc,
  	     x, y, width, height,
  	     x + shift_by, y);
@@ -3782,8 +3822,14 @@ x_clear_area (struct frame *f, int x, int y, int 
width, int height)
    cairo_fill (cr);
    x_end_cr_clip (f);
  #else
-  x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
-		 x, y, width, height, False);
+    if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+      XFillRectangle (FRAME_X_DISPLAY (f),
+                      FRAME_X_DRAWABLE (f),
+                      f->output_data.x->reverse_gc,
+                      x, y, width, height);
+  else
+    x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                   x, y, width, height, False);
  #endif
  }

@@ -4109,7 +4155,7 @@ x_scroll_run (struct window *w, struct run *run)
    SET_FRAME_GARBAGED (f);
  #else
    XCopyArea (FRAME_X_DISPLAY (f),
-	     FRAME_X_WINDOW (f), FRAME_X_WINDOW (f),
+             FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f),
  	     f->output_data.x->normal_gc,
  	     x, from_y,
  	     width, height,
@@ -7769,6 +7815,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                SET_FRAME_VISIBLE (f, 1);
                SET_FRAME_ICONIFIED (f, false);
                f->output_data.x->has_been_visible = true;
+              font_flush_frame_caches (f);
                SET_FRAME_GARBAGED (f);
              }
            else
@@ -8437,7 +8484,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              {
                if (FRAME_PIXEL_HEIGHT (f) != 
configureEvent.xconfigure.height
                    || FRAME_PIXEL_WIDTH (f) != 
configureEvent.xconfigure.width)
-                SET_FRAME_GARBAGED (f);
+                {
+                  font_flush_frame_caches (f);
+                  SET_FRAME_GARBAGED (f);
+                }
                FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height;
                FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width;
              }
@@ -8463,7 +8513,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                || configureEvent.xconfigure.height != 
FRAME_PIXEL_HEIGHT (f))
              {
                change_frame_size (f, width, height, false, true, false, 
true);
-	      x_clear_under_internal_border (f);
+              x_clear_under_internal_border (f);
+              font_flush_frame_caches (f);
  	      SET_FRAME_GARBAGED (f);
                cancel_mouse_face (f);
              }
@@ -8880,7 +8931,7 @@ x_draw_hollow_cursor (struct window *w, struct 
glyph_row *row)
    if (dpyinfo->scratch_cursor_gc)
      XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv);
    else
-    dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_WINDOW (f),
+    dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f),
  					    GCForeground, &xgcv);
    gc = dpyinfo->scratch_cursor_gc;

@@ -8937,7 +8988,7 @@ x_draw_bar_cursor (struct window *w, struct 
glyph_row *row, int width, enum text
    else
      {
        Display *dpy = FRAME_X_DISPLAY (f);
-      Window window = FRAME_X_WINDOW (f);
+      Drawable drawable = FRAME_X_DRAWABLE (f);
        GC gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc;
        unsigned long mask = GCForeground | GCBackground | 
GCGraphicsExposures;
        struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
@@ -8958,7 +9009,7 @@ x_draw_bar_cursor (struct window *w, struct 
glyph_row *row, int width, enum text
  	XChangeGC (dpy, gc, mask, &xgcv);
        else
  	{
-	  gc = XCreateGC (dpy, window, mask, &xgcv);
+          gc = XCreateGC (dpy, drawable, mask, &xgcv);
  	  FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
  	}

@@ -11271,7 +11322,14 @@ x_free_frame_resources (struct frame *f)
  #endif /* USE_GTK */

        if (FRAME_X_WINDOW (f))
-	XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
+        {
+#ifdef HAVE_XDBE
+          if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+            XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f),
+                                          FRAME_X_DRAWABLE (f));
+#endif
+          XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
+        }
  #endif /* !USE_X_TOOLKIT */

        unload_color (f, FRAME_FOREGROUND_PIXEL (f));
@@ -12111,7 +12169,17 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
      }
    else
      dpyinfo->cmap = XCreateColormap (dpyinfo->display, 
dpyinfo->root_window,
-				     dpyinfo->visual, AllocNone);
+                                     dpyinfo->visual, AllocNone);
+
+#ifdef HAVE_XDBE
+  dpyinfo->supports_xdbe = false;
+    {
+      int xdbe_major;
+      int xdbe_minor;
+      if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor))
+        dpyinfo->supports_xdbe = true;
+    }
+#endif

  #ifdef HAVE_XFT
    {
@@ -12590,6 +12658,7 @@ x_create_terminal (struct x_display_info *dpyinfo)
    terminal->toggle_invisible_pointer_hook = XTtoggle_invisible_pointer;
    terminal->update_begin_hook = x_update_begin;
    terminal->update_end_hook = x_update_end;
+  terminal->redisplay_end_hook = x_redisplay_end;
    terminal->read_socket_hook = XTread_socket;
    terminal->frame_up_to_date_hook = XTframe_up_to_date;
    terminal->mouse_position_hook = XTmouse_position;
diff --git a/src/xterm.h b/src/xterm.h
index 675a484..cb1aa1d 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -475,6 +475,10 @@ struct x_display_info
  #ifdef USE_XCB
    xcb_connection_t *xcb_connection;
  #endif
+
+#ifdef HAVE_XDBE
+  bool supports_xdbe;
+#endif
  };

  #ifdef HAVE_X_I18N
@@ -527,6 +531,17 @@ struct x_output
       and the X window has not yet been created.  */
    Window window_desc;

+#ifdef HAVE_XDBE
+  /* The drawable to which we're rendering.  In the single-buffered
+     base, the window itself.  In the double-buffered case, the
+     window's back buffer.  */
+  Drawable draw_desc;
+
+  /* Set to true when we need a buffer flip.  We do a buffer flip only
+     at the end of redisplay in order to minimize flicker.  */
+  bool need_buffer_flip;
+#endif
+
    /* The X window used for the bitmap icon;
       or 0 if we don't have a bitmap icon.  */
    Window icon_desc;
@@ -737,6 +752,18 @@ enum
  /* Return the X window used for displaying data in frame F.  */
  #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc)

+/* Return the drawable used for rendering to frame F.  */
+#ifdef HAVE_XDBE
+#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc)
+#else
+#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f)))
+#endif
+
+/* Return the need-buffer-flip flag for frame F.  */
+#ifdef HAVE_XDBE
+#define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip)
+#endif
+
  /* Return the outermost X window associated with the frame F.  */
  #ifdef USE_X_TOOLKIT
  #define FRAME_OUTER_WINDOW(f) ((f)->output_data.x->widget ?             \
@@ -1140,6 +1167,8 @@ extern bool x_wm_supports (struct frame *, Atom);
  extern void x_wait_for_event (struct frame *, int);
  extern void x_clear_under_internal_border (struct frame *f);

+extern void set_up_x_back_buffer (struct frame* f);
+
  /* Defined in xselect.c.  */

  extern void x_handle_property_notify (const XPropertyEvent *);




^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione
@ 2016-10-21  2:27 ` Lars Ingebrigtsen
  2016-10-21  2:31   ` Daniel Colascione
  2016-10-21  3:24 ` Óscar Fuentes
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 31+ messages in thread
From: Lars Ingebrigtsen @ 2016-10-21  2:27 UTC (permalink / raw)
  To: Daniel Colascione; +Cc: emacs-devel

Daniel Colascione <dancol@dancol.org> writes:

> This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to
> avoid showing the user incomplete drawing results. Without this patch,
> I can make Emacs flicker like crazy by running isearch for a piece of
> text unique in a buffer and holding down C-s. With this patch, Emacs
> does not flicker no matter what I do to it.

That sounds just wonderful.  Will it also remove flickers when watching
animated GIFs?

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  2:27 ` Lars Ingebrigtsen
@ 2016-10-21  2:31   ` Daniel Colascione
  0 siblings, 0 replies; 31+ messages in thread
From: Daniel Colascione @ 2016-10-21  2:31 UTC (permalink / raw)
  To: Lars Ingebrigtsen; +Cc: emacs-devel



On October 20, 2016 7:27:04 PM PDT, Lars Ingebrigtsen <larsi@gnus.org> wrote:
>Daniel Colascione <dancol@dancol.org> writes:
>
>> This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension
>to
>> avoid showing the user incomplete drawing results. Without this
>patch,
>> I can make Emacs flicker like crazy by running isearch for a piece of
>> text unique in a buffer and holding down C-s. With this patch, Emacs
>> does not flicker no matter what I do to it.
>
>That sounds just wonderful.  Will it also remove flickers when watching
>animated GIFs?

Probably? TIAS





^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione
  2016-10-21  2:27 ` Lars Ingebrigtsen
@ 2016-10-21  3:24 ` Óscar Fuentes
  2016-10-21  3:31   ` Clément Pit--Claudel
  2016-10-21  3:56 ` Clément Pit--Claudel
  2016-10-21  8:49 ` Eli Zaretskii
  3 siblings, 1 reply; 31+ messages in thread
From: Óscar Fuentes @ 2016-10-21  3:24 UTC (permalink / raw)
  To: emacs-devel

Daniel Colascione <dancol@dancol.org> writes:

> This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to
> avoid showing the user incomplete drawing results. Without this patch,
> I can make Emacs flicker like crazy by running isearch for a piece of
> text unique in a buffer and holding down C-s. With this patch, Emacs
> does not flicker no matter what I do to it.
[snip]

For me Emacs flickers by just writing or scrolling around. It is quite
annoying to work with unless I activate the tearing prevention feature
on KDE's settings, which I'll like to avoid because it is buggy.

I'm eager to try your code, but `patch' complains about

oscar@qcore:~/dev/emacs/stable/source$ patch -p 1 < ~/p.patch 
patching file configure.ac
patching file src/Makefile.in
patch: **** malformed patch at line 60: $(LIBSELINUX_LIBS) \


And indeed, the patch shows:

    $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS)
$(LIBSELINUX_LIBS) \
    $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \


It looks like the typical text wrap problem. There are more cases like
this.

Could you send the patch as an attachment, please?





^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  3:24 ` Óscar Fuentes
@ 2016-10-21  3:31   ` Clément Pit--Claudel
  2016-10-21  3:41     ` Óscar Fuentes
  2016-10-21  8:23     ` Andreas Schwab
  0 siblings, 2 replies; 31+ messages in thread
From: Clément Pit--Claudel @ 2016-10-21  3:31 UTC (permalink / raw)
  To: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 475 bytes --]

On 2016-10-20 23:24, Óscar Fuentes wrote:
> I'm eager to try your code, but `patch' complains about
> 
> oscar@qcore:~/dev/emacs/stable/source$ patch -p 1 < ~/p.patch 
> patching file configure.ac
> patching file src/Makefile.in
> patch: **** malformed patch at line 60: $(LIBSELINUX_LIBS) \

The patch applies fine for me with  git apply --reject --ignore-space-change --ignore-whitespace double-buffer.patch.  Maybe that works for you too?

Cheers,
Clément. 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  3:31   ` Clément Pit--Claudel
@ 2016-10-21  3:41     ` Óscar Fuentes
  2016-10-21  3:50       ` Clément Pit--Claudel
  2016-10-21  8:23     ` Andreas Schwab
  1 sibling, 1 reply; 31+ messages in thread
From: Óscar Fuentes @ 2016-10-21  3:41 UTC (permalink / raw)
  To: emacs-devel

Clément Pit--Claudel <clement.pit@gmail.com> writes:

> The patch applies fine for me with git apply --reject
> --ignore-space-change --ignore-whitespace double-buffer.patch. Maybe
> that works for you too?

Hmmm... no:

oscar@qcore:~/dev/emacs/stable/source$ git apply --reject --ignore-space-change --ignore-whitespace ~/p.patch 
fatal: corrupt patch at line 66

oscar@qcore:~/dev/emacs/stable/source$ git --version
git version 2.9.3




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  3:41     ` Óscar Fuentes
@ 2016-10-21  3:50       ` Clément Pit--Claudel
  0 siblings, 0 replies; 31+ messages in thread
From: Clément Pit--Claudel @ 2016-10-21  3:50 UTC (permalink / raw)
  To: emacs-devel


[-- Attachment #1.1.1: Type: text/plain, Size: 593 bytes --]

On 2016-10-20 23:41, Óscar Fuentes wrote:
> Clément Pit--Claudel <clement.pit@gmail.com> writes:
> 
>> The patch applies fine for me with git apply --reject
>> --ignore-space-change --ignore-whitespace double-buffer.patch. Maybe
>> that works for you too?
> 
> Hmmm... no:
> 
> oscar@qcore:~/dev/emacs/stable/source$ git apply --reject --ignore-space-change --ignore-whitespace ~/p.patch 
> fatal: corrupt patch at line 66
> 
> oscar@qcore:~/dev/emacs/stable/source$ git --version
> git version 2.9.3

I've attached the patch that applied correctly for me (I use git 2.7.4).

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.1.2: double-buffer.patch --]
[-- Type: text/x-diff; name="double-buffer.patch", Size: 38590 bytes --]

commit 15fdd8f63533201f05627ede634a8f5ae4757d7e1010
Authorf: Daniel Colascione <dancol@dancol.org>
Date:   Thu Oct 20 16:50:54 2016 -0700

    Add double-buffered output support to Emacs

diff --git a/configure.ac b/configure.ac
index cd11b10..4716b43 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3712,6 +3712,24 @@ AC_DEFUN
 AC_SUBST(XFIXES_CFLAGS)
 AC_SUBST(XFIXES_LIBS)

+### Use Xdbe (-lXdbe) if available
+HAVE_XDBE=no
+if test "${HAVE_X11}" = "yes"; then
+  AC_CHECK_HEADER(X11/extensions/Xdbe.h,
+    [AC_CHECK_LIB(Xext, XdbeAllocateBackBufferName, HAVE_XDBE=yes)],
+    [],
+    [#include <X11/Xlib.h>
+    ])
+  if test $HAVE_XDBE = yes; then
+    XDBE_LIBS=-lXext
+  fi
+  if test $HAVE_XDBE = yes; then
+    AC_DEFINE(HAVE_XDBE, 1, [Define to 1 if you have the Xdbe extension.])
+  fi
+fi
+AC_SUBST(XDBE_CFLAGS)
+AC_SUBST(XDBE_LIBS)
+
 ### Use libxml (-lxml2) if available
 ### mingw32 doesn't use -lxml2, since it loads the library dynamically.
 HAVE_LIBXML2=no
diff --git a/src/Makefile.in b/src/Makefile.in
index 89f7a92..dc0bfff 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -254,6 +254,9 @@ XINERAMA_CFLAGS =
 XFIXES_LIBS = @XFIXES_LIBS@
 XFIXES_CFLAGS = @XFIXES_CFLAGS@

+XDBE_LIBS = @XDBE_LIBS@
+XDBE_CFLAGS = @XDBE_CFLAGS@
+
 ## widget.o if USE_X_TOOLKIT, otherwise empty.
 WIDGET_OBJ=@WIDGET_OBJ@

@@ -372,7 +375,7 @@ ALL_CFLAGS=
   $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \
   $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
   $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \
-  $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \
+  $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \
   $(WEBKIT_CFLAGS) \
   $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
   $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
@@ -489,6 +492,7 @@ LIBES =
    $(WEBKIT_LIBS) \
    $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \
+   $(XDBE_LIBS) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
    $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
diff --git a/src/dispnew.c b/src/dispnew.c
index 70d4de0..8f81cee 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f)
 {
   /* Error if F has no glyphs.  */
   eassert (f->glyphs_initialized_p);
+  font_flush_frame_caches (f);
   update_begin (f);
   if (FRAME_MSDOS_P (f))
     FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f));
diff --git a/src/font.c b/src/font.c
index f8e6794..033995e 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, Lisp_Object arg, Lisp_Object result)
 }

 void
+font_flush_frame_caches (struct frame *f)
+{
+  struct font_driver_list *list;
+
+  for (list = f->font_driver_list; list; list = list->next)
+    if (list->on && list->driver->flush_frame_caches)
+      list->driver->flush_frame_caches (f);
+}
+
+void
 syms_of_font (void)
 {
   sort_shift_bits[FONT_TYPE_INDEX] = 0;
diff --git a/src/font.h b/src/font.h
index cf47729..961e9c4 100644
--- a/src/font.h
+++ b/src/font.h
@@ -763,6 +763,12 @@ struct font_driver
      Return non-nil if the driver support rendering of combining
      characters for FONT according to Unicode combining class.  */
   Lisp_Object (*combining_capability) (struct font *font);
+
+  /* Optional
+
+     Called when frame F is redrawn from scratch.  Font engines may
+     invalidate certain caches in this case.  */
+  void (*flush_frame_caches) (struct frame *f);
 };


@@ -862,7 +868,9 @@ extern void *font_get_frame_data (struct frame *f, Lisp_Object);
 extern void font_filter_properties (Lisp_Object font,
                     Lisp_Object alist,
                     const char *const boolean_properties[],
-                    const char *const non_boolean_properties[]);
+                                    const char *const non_boolean_properties[]);
+
+extern void font_flush_frame_caches (struct frame *f);

 #ifdef HAVE_FREETYPE
 extern struct font_driver ftfont_driver;
diff --git a/src/ftxfont.c b/src/ftxfont.c
index f49d44f..bfdeb40 100644
--- a/src/ftxfont.c
+++ b/src/ftxfont.c
@@ -95,7 +95,7 @@ ftxfont_get_gcs (struct frame *f, unsigned long foreground, unsigned long backgr
       if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &color))
     break;
       xgcv.foreground = color.pixel;
-      new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                    GCForeground, &xgcv);
     }
   unblock_input ();
@@ -139,14 +139,14 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font,
         p[n[0]].y = y - bitmap.top + i;
         if (++n[0] == size)
           {
-            XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                  gc_fore, p, size, CoordModeOrigin);
             n[0] = 0;
           }
           }
     }
       if (flush && n[0] > 0)
-    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+        XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
              gc_fore, p, n[0], CoordModeOrigin);
     }
   else
@@ -168,7 +168,7 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font,
           pp[n[idx]].y = y - bitmap.top + i;
           if (++(n[idx]) == size)
             {
-              XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                      XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                    idx == 6 ? gc_fore : gcs[idx], pp, size,
                    CoordModeOrigin);
               n[idx] = 0;
@@ -180,10 +180,10 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font,
     {
       for (i = 0; i < 6; i++)
         if (n[i] > 0)
-          XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+              XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                gcs[i], p + 0x100 * i, n[i], CoordModeOrigin);
       if (n[6] > 0)
-        XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+            XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
              gc_fore, p + 0x600, n[6], CoordModeOrigin);
     }
     }
@@ -203,7 +203,7 @@ ftxfont_draw_background (struct frame *f, struct font *font, GC gc, int x, int y
   XGetGCValues (FRAME_X_DISPLAY (f), gc,
         GCForeground | GCBackground, &xgcv);
   XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background);
-  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc,
+  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc,
           x, y - FONT_BASE (font), width, FONT_HEIGHT (font));
   XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground);
 }
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 88e6d30..f81940b 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -48,6 +48,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "emacsgtkfixed.h"
 #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
 #define gtk_widget_set_has_window(w, b) \
   (gtk_fixed_set_has_window (GTK_FIXED (w), b))
@@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f)
      by callers of this function.  */
   gtk_widget_realize (wfixed);
   FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
+  set_up_x_back_buffer (f);

   /* Since GTK clears its window by filling with the background color,
      we must keep X and GTK background in sync.  */
@@ -1296,6 +1301,15 @@ xg_free_frame_widgets (struct frame *f)
       if (tbinfo)
         xfree (tbinfo);

+#ifdef HAVE_XDBE
+      if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+        {
+          XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f),
+                                        FRAME_X_DRAWABLE (f));
+          FRAME_X_DRAWABLE (f) = 0;
+        }
+#endif
+
       gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
       FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
       FRAME_GTK_OUTER_WIDGET (f) = 0;
diff --git a/src/image.c b/src/image.c
index 9bd2455..1303a93 100644
--- a/src/image.c
+++ b/src/image.c
@@ -220,7 +220,7 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi

 #ifdef HAVE_X_WINDOWS
   Pixmap bitmap;
-  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                   bits, width, height);
   if (! bitmap)
     return -1;
@@ -327,7 +327,7 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file)

   filename = SSDATA (found);

-  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                 filename, &width, &height, &bitmap, &xhot, &yhot);
   if (result != BitmapSuccess)
     return -1;
@@ -1952,7 +1952,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
 {
 #ifdef HAVE_X_WINDOWS
   Display *display = FRAME_X_DISPLAY (f);
-  Window window = FRAME_X_WINDOW (f);
+  Drawable drawable = FRAME_X_DRAWABLE (f);
   Screen *screen = FRAME_X_SCREEN (f);

   eassert (input_blocked_p ());
@@ -1981,7 +1981,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
   (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);

   /* Allocate a pixmap of the same size.  */
-  *pixmap = XCreatePixmap (display, window, width, height, depth);
+  *pixmap = XCreatePixmap (display, drawable, width, height, depth);
   if (*pixmap == NO_PIXMAP)
     {
       x_destroy_x_image (*ximg);
@@ -2742,7 +2742,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data,
   img->pixmap =
    (x_check_image_size (0, img->width, img->height)
     ? XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f),
-                   FRAME_X_WINDOW (f),
+                                   FRAME_X_DRAWABLE (f),
                    data,
                    img->width, img->height,
                    fg, bg,
@@ -3520,7 +3520,7 @@ x_create_bitmap_from_xpm_data (struct frame *f, const char **bits)
   xpm_init_color_cache (f, &attrs);
 #endif

-  rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                 (char **) bits, &bitmap, &mask, &attrs);
   if (rc != XpmSuccess)
     {
@@ -3758,7 +3758,7 @@ xpm_load (struct frame *f, struct image *img)
 #ifdef HAVE_X_WINDOWS
   if (rc == XpmSuccess)
     {
-      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                    img->ximg->width, img->ximg->height,
                    img->ximg->depth);
       if (img->pixmap == NO_PIXMAP)
@@ -3768,7 +3768,7 @@ xpm_load (struct frame *f, struct image *img)
     }
       else if (img->mask_img)
     {
-      img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+          img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                      img->mask_img->width,
                      img->mask_img->height,
                      img->mask_img->depth);
@@ -9541,7 +9541,7 @@ gs_load (struct frame *f, struct image *img)
     {
       /* Only W32 version did BLOCK_INPUT here.  ++kfs */
       block_input ();
-      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
                    img->width, img->height,
                    DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
       unblock_input ();
@@ -9557,7 +9557,7 @@ gs_load (struct frame *f, struct image *img)
      if successful.  We do not record_unwind_protect here because
      other places in redisplay like calling window scroll functions
      don't either.  Let the Lisp loader use `unwind-protect' instead.  */
-  printnum1 = FRAME_X_WINDOW (f);
+  printnum1 = FRAME_X_DRAWABLE (f);
   printnum2 = img->pixmap;
   window_and_pixmap_id
     = make_formatted_string (buffer, "%"pMu" %"pMu, printnum1, printnum2);
diff --git a/src/termhooks.h b/src/termhooks.h
index ff74d99..c8d7fae 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -477,6 +477,7 @@ struct terminal

   void (*update_begin_hook) (struct frame *);
   void (*update_end_hook) (struct frame *);
+  void (*redisplay_end_hook) (struct frame *);
   void (*set_terminal_window_hook) (struct frame *, int);

   /* Multi-frame and mouse support hooks.  */
diff --git a/src/xdisp.c b/src/xdisp.c
index 3af5ea4..04f0ca1 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2501,7 +2501,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)

   /* Visible feedback for debugging.  */
 #if false && defined HAVE_X_WINDOWS
-  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
           f->output_data.x->normal_gc,
           gx, gy, width, height);
 #endif
@@ -14211,6 +14211,13 @@ redisplay_internal (void)
       windows_or_buffers_changed = 0;
     }

+  FOR_EACH_FRAME (tail, frame)
+    {
+      struct frame *f = XFRAME (frame);
+      if (FRAME_TERMINAL (f)->redisplay_end_hook)
+        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
+    }
+
   /* Start SIGIO interrupts coming again.  Having them off during the
      code above makes it less likely one will discard output, but not
      impossible, since there might be stuff in the system buffer here.
@@ -24608,7 +24615,7 @@ init_glyph_string (struct glyph_string *s,
   s->hdc = hdc;
 #endif
   s->display = FRAME_X_DISPLAY (s->f);
-  s->window = FRAME_X_WINDOW (s->f);
+  s->window = FRAME_X_DRAWABLE (s->f);
   s->char2b = char2b;
   s->hl = hl;
   s->row = row;
diff --git a/src/xfaces.c b/src/xfaces.c
index 5837f35..accb98b 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -495,7 +495,7 @@ x_create_gc (struct frame *f, unsigned long mask, XGCValues *xgcv)
 {
   GC gc;
   block_input ();
-  gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), mask, xgcv);
+  gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), mask, xgcv);
   unblock_input ();
   IF_DEBUG (++ngcs);
   return gc;
diff --git a/src/xfns.c b/src/xfns.c
index 8571d0e..2098036 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -53,6 +53,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "gtkutil.h"
 #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
 #ifdef USE_X_TOOLKIT
 #include <X11/Shell.h>

@@ -2483,6 +2487,29 @@ xic_set_xfontset (struct frame *f, const char *base_fontname)


 \f
+
+void
+set_up_x_back_buffer (struct frame* f)
+{
+  FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
+#ifdef HAVE_XDBE
+  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
+    {
+      /* If allocating a back buffer fails, just use single-buffered
+         rendering.  */
+      x_sync (f);
+      x_catch_errors (FRAME_X_DISPLAY (f));
+      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
+        FRAME_X_DISPLAY (f),
+        FRAME_X_WINDOW (f),
+        XdbeCopied);
+      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
+        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
+      x_uncatch_errors_after_check ();
+    }
+#endif
+}
+
 #ifdef USE_X_TOOLKIT

 /* Create and set up the X widget for frame F.  */
@@ -2638,7 +2665,7 @@ x_window (struct frame *f, long window_prompting)
              f->output_data.x->parent_desc, 0, 0);

   FRAME_X_WINDOW (f) = XtWindow (frame_widget);
-
+  set_up_x_back_buffer (f);
   validate_x_resource_name ();

   class_hints.res_name = SSDATA (Vx_resource_name);
@@ -2784,7 +2811,8 @@ x_window (struct frame *f)
              CopyFromParent, /* depth */
              InputOutput, /* class */
              FRAME_X_VISUAL (f),
-             attribute_mask, &attributes);
+                     attribute_mask, &attributes);
+  set_up_x_back_buffer (f);

 #ifdef HAVE_X_I18N
   if (use_xim)
@@ -2938,7 +2966,7 @@ x_make_gc (struct frame *f)
   gc_values.line_width = 0;    /* Means 1 using fast algorithm.  */
   f->output_data.x->normal_gc
     = XCreateGC (FRAME_X_DISPLAY (f),
-         FRAME_X_WINDOW (f),
+                 FRAME_X_DRAWABLE (f),
          GCLineWidth | GCForeground | GCBackground,
          &gc_values);

@@ -2947,7 +2975,7 @@ x_make_gc (struct frame *f)
   gc_values.background = FRAME_FOREGROUND_PIXEL (f);
   f->output_data.x->reverse_gc
     = XCreateGC (FRAME_X_DISPLAY (f),
-         FRAME_X_WINDOW (f),
+                 FRAME_X_DRAWABLE (f),
          GCForeground | GCBackground | GCLineWidth,
          &gc_values);

@@ -2956,7 +2984,7 @@ x_make_gc (struct frame *f)
   gc_values.background = f->output_data.x->cursor_pixel;
   gc_values.fill_style = FillOpaqueStippled;
   f->output_data.x->cursor_gc
-    = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+    = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
          (GCForeground | GCBackground
           | GCFillStyle | GCLineWidth),
          &gc_values);
@@ -5636,7 +5664,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms)
                /* Border.  */
                f->border_width,
                CopyFromParent, InputOutput, CopyFromParent,
-               mask, &attrs);
+                       mask, &attrs);
+    set_up_x_back_buffer (f);
     XChangeProperty (FRAME_X_DISPLAY (f), tip_window,
                      FRAME_DISPLAY_INFO (f)->Xatom_net_window_type,
                      XA_ATOM, 32, PropModeReplace,
diff --git a/src/xfont.c b/src/xfont.c
index 45b0e0a..c2b7317 100644
--- a/src/xfont.c
+++ b/src/xfont.c
@@ -1057,20 +1057,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y,
     {
       if (s->padding_p)
         for (i = 0; i < len; i++)
-          XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+              XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
                 gc, x + i, y, str + i, 1);
       else
-        XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+            XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
                   gc, x, y, str, len);
     }
       else
     {
       if (s->padding_p)
         for (i = 0; i < len; i++)
-          XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+              XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
                gc, x + i, y, str + i, 1);
       else
-        XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+            XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
              gc, x, y, str, len);
     }
       unblock_input ();
@@ -1083,20 +1083,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y,
     {
       if (s->padding_p)
     for (i = 0; i < len; i++)
-      XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+          XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
                   gc, x + i, y, s->char2b + from + i, 1);
       else
-    XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+        XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
                 gc, x, y, s->char2b + from, len);
     }
   else
     {
       if (s->padding_p)
     for (i = 0; i < len; i++)
-      XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+          XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
              gc, x + i, y, s->char2b + from + i, 1);
       else
-    XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+        XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
                gc, x, y, s->char2b + from, len);
     }
   unblock_input ();
diff --git a/src/xftfont.c b/src/xftfont.c
index 34c6f7d..447adf6 100644
--- a/src/xftfont.c
+++ b/src/xftfont.c
@@ -586,7 +586,7 @@ xftfont_get_xft_draw (struct frame *f)
     {
       block_input ();
       xft_draw= XftDrawCreate (FRAME_X_DISPLAY (f),
-                   FRAME_X_WINDOW (f),
+                               FRAME_X_DRAWABLE (f),
                    FRAME_X_VISUAL (f),
                    FRAME_X_COLORMAP (f));
       unblock_input ();
@@ -695,6 +695,13 @@ xftfont_end_for_frame (struct frame *f)
   return 0;
 }

+static void
+xftfont_flush_frame_caches (struct frame *f)
+{
+  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+    xftfont_end_for_frame (f);
+}
+
 static bool
 xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object,
                         Lisp_Object entity)
@@ -777,6 +784,9 @@ This is needed with some fonts to correct vertical overlap of glyphs.  */);
 #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF)
   xftfont_driver.shape = xftfont_shape;
 #endif
+  // When using X double buffering, the XftDraw structure we
+  // build seems to be useless once a frame is resized, so
+  xftfont_driver.flush_frame_caches = xftfont_flush_frame_caches;

   register_font_driver (&xftfont_driver, NULL);
 }
diff --git a/src/xterm.c b/src/xterm.c
index 7476694..7116784 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -45,6 +45,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <X11/extensions/Xrender.h>
 #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
 /* Load sys/types.h if not already loaded.
    In some systems loading it twice is suicidal.  */
 #ifndef makedev
@@ -360,7 +364,7 @@ x_begin_cr_clip (struct frame *f, GC gc)
         {
           cairo_surface_t *surface;
           surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
-                                               FRAME_X_WINDOW (f),
+                                               FRAME_X_DRAWABLE (f),
                                                FRAME_DISPLAY_INFO (f)->visual,
                                                FRAME_PIXEL_WIDTH (f),
                                                FRAME_PIXEL_HEIGHT (f));
@@ -722,7 +726,7 @@ x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height)
   cairo_fill (cr);
   x_end_cr_clip (f);
 #else
-  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
           gc, x, y, width, height);
 #endif
 }
@@ -740,7 +744,7 @@ x_draw_rectangle (struct frame *f, GC gc, int x, int y, int width, int height)
   cairo_stroke (cr);
   x_end_cr_clip (f);
 #else
-  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
           gc, x, y, width, height);
 #endif
 }
@@ -756,7 +760,10 @@ x_clear_window (struct frame *f)
   cairo_paint (cr);
   x_end_cr_clip (f);
 #else
-  XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
+  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+    x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
+  else
+    XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
 #endif
 }

@@ -1067,7 +1074,7 @@ x_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
 #ifdef USE_CAIRO
   x_fill_rectangle (f, f->output_data.x->normal_gc, x, y0, 1, y1 - y0);
 #else
-  XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
          f->output_data.x->normal_gc, x, y0, x, y1);
 #endif
 }
@@ -1179,6 +1186,21 @@ x_update_window_end (struct window *w, bool cursor_on_p,
 /* End update of frame F.  This function is installed as a hook in
    update_end.  */

+#if defined (HAVE_XDBE)
+static void
+show_back_buffer (struct frame *f)
+{
+  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+    {
+      XdbeSwapInfo swap_info;
+      memset (&swap_info, 0, sizeof (swap_info));
+      swap_info.swap_window = FRAME_X_WINDOW (f);
+      swap_info.swap_action = XdbeCopied;
+      XdbeSwapBuffers (FRAME_X_DISPLAY (f), &swap_info, 1);
+    }
+}
+#endif
+
 static void
 x_update_end (struct frame *f)
 {
@@ -1207,7 +1229,7 @@ x_update_end (struct frame *f)
           if (! FRAME_EXTERNAL_MENU_BAR (f))
             height += FRAME_MENU_BAR_HEIGHT (f);
           surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
-                                               FRAME_X_WINDOW (f),
+                                               FRAME_X_DRAWABLE (f),
                                                FRAME_DISPLAY_INFO (f)->visual,
                                                width,
                                                height);
@@ -1220,7 +1242,14 @@ x_update_end (struct frame *f)
       cairo_destroy (cr);
       unblock_input ();
     }
-#endif /* USE_CAIRO */
+#endif
+
+#ifdef HAVE_XDBE
+  if (redisplaying_p)
+    FRAME_X_NEED_BUFFER_FLIP (f) = true;
+  else
+    show_back_buffer (f);
+#endif

 #ifndef XFlush
   block_input ();
@@ -1229,6 +1258,17 @@ x_update_end (struct frame *f)
 #endif
 }

+static void
+x_redisplay_end (struct frame *f)
+{
+#ifdef HAVE_XDBE
+  if (FRAME_X_NEED_BUFFER_FLIP (f))
+    {
+      show_back_buffer (f);
+      FRAME_X_NEED_BUFFER_FLIP (f) = false;
+    }
+#endif
+}

 /* This function is called from various places in xdisp.c
    whenever a complete update has been performed.  */
@@ -1354,7 +1394,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring
 #else  /* not USE_CAIRO */
   if (p->which)
     {
-      Window window = FRAME_X_WINDOW (f);
+      Drawable drawable = FRAME_X_DRAWABLE (f);
       char *bits;
       Pixmap pixmap, clipmask = (Pixmap) 0;
       int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f));
@@ -1367,7 +1407,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring

       /* Draw the bitmap.  I believe these small pixmaps can be cached
      by the server.  */
-      pixmap = XCreatePixmapFromBitmapData (display, window, bits, p->wd, p->h,
+      pixmap = XCreatePixmapFromBitmapData (display, drawable, bits, p->wd, p->h,
                         (p->cursor_p
                          ? (p->overlay_p ? face->background
                         : f->output_data.x->cursor_pixel)
@@ -1386,7 +1426,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring
       XChangeGC (display, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
     }

-      XCopyArea (display, pixmap, window, gc, 0, 0,
+      XCopyArea (display, pixmap, drawable, gc, 0, 0,
          p->wd, p->h, p->x, p->y);
       XFreePixmap (display, pixmap);

@@ -2565,7 +2605,7 @@ x_setup_relief_color (struct frame *f, struct relief *relief, double factor,
     {
       xgcv.stipple = dpyinfo->gray;
       mask |= GCStipple;
-      relief->gc = XCreateGC (dpy, FRAME_X_WINDOW (f), mask, &xgcv);
+      relief->gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), mask, &xgcv);
     }
   else
     XChangeGC (dpy, relief->gc, mask, &xgcv);
@@ -2696,7 +2736,7 @@ x_draw_relief_rect (struct frame *f,
   x_reset_clip_rectangles (f, bottom_right_gc);
 #else
   Display *dpy = FRAME_X_DISPLAY (f);
-  Window window = FRAME_X_WINDOW (f);
+  Drawable drawable = FRAME_X_DRAWABLE (f);
   int i;
   GC gc;

@@ -2715,12 +2755,12 @@ x_draw_relief_rect (struct frame *f,
   if (top_p)
     {
       if (width == 1)
-    XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
            left_x + left_p, top_y,
            right_x + !right_p, top_y);

       for (i = 1; i < width; ++i)
-    XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
            left_x  + i * left_p, top_y + i,
            right_x + 1 - i * right_p, top_y + i);
     }
@@ -2729,13 +2769,13 @@ x_draw_relief_rect (struct frame *f,
   if (left_p)
     {
       if (width == 1)
-    XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y);
+        XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y);

-      XClearArea (dpy, window, left_x, top_y, 1, 1, False);
-      XClearArea (dpy, window, left_x, bottom_y, 1, 1, False);
+      x_clear_area(f, left_x, top_y, 1, 1);
+      x_clear_area(f, left_x, bottom_y, 1, 1);

       for (i = (width > 1 ? 1 : 0); i < width; ++i)
-    XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
            left_x + i, top_y + (i + 1) * top_p,
            left_x + i, bottom_y + 1 - (i + 1) * bot_p);
     }
@@ -2751,23 +2791,23 @@ x_draw_relief_rect (struct frame *f,
     {
       /* Outermost top line.  */
       if (top_p)
-    XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
            left_x  + left_p, top_y,
            right_x + !right_p, top_y);

       /* Outermost left line.  */
       if (left_p)
-    XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y);
+        XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y);
     }

   /* Bottom.  */
   if (bot_p)
     {
-      XDrawLine (dpy, window, gc,
+      XDrawLine (dpy, drawable, gc,
          left_x + left_p, bottom_y,
          right_x + !right_p, bottom_y);
       for (i = 1; i < width; ++i)
-    XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
            left_x  + i * left_p, bottom_y - i,
            right_x + 1 - i * right_p, bottom_y - i);
     }
@@ -2775,10 +2815,10 @@ x_draw_relief_rect (struct frame *f,
   /* Right.  */
   if (right_p)
     {
-      XClearArea (dpy, window, right_x, top_y, 1, 1, False);
-      XClearArea (dpy, window, right_x, bottom_y, 1, 1, False);
+      x_clear_area(f, right_x, top_y, 1, 1);
+      x_clear_area(f, right_x, bottom_y, 1, 1);
       for (i = 0; i < width; ++i)
-    XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
            right_x - i, top_y + (i + 1) * top_p,
            right_x - i, bottom_y + 1 - (i + 1) * bot_p);
     }
@@ -3741,7 +3781,7 @@ x_shift_glyphs_for_insert (struct frame *f, int x, int y, int width, int height,
 /* Never called on a GUI frame, see
    http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html
 */
-  XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), FRAME_X_WINDOW (f),
+  XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f),
          f->output_data.x->normal_gc,
          x, y, width, height,
          x + shift_by, y);
@@ -3782,8 +3822,14 @@ x_clear_area (struct frame *f, int x, int y, int width, int height)
   cairo_fill (cr);
   x_end_cr_clip (f);
 #else
-  x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
-         x, y, width, height, False);
+    if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+      XFillRectangle (FRAME_X_DISPLAY (f),
+                      FRAME_X_DRAWABLE (f),
+                      f->output_data.x->reverse_gc,
+                      x, y, width, height);
+  else
+    x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                   x, y, width, height, False);
 #endif
 }

@@ -4109,7 +4155,7 @@ x_scroll_run (struct window *w, struct run *run)
   SET_FRAME_GARBAGED (f);
 #else
   XCopyArea (FRAME_X_DISPLAY (f),
-         FRAME_X_WINDOW (f), FRAME_X_WINDOW (f),
+             FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f),
          f->output_data.x->normal_gc,
          x, from_y,
          width, height,
@@ -7769,6 +7815,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
               SET_FRAME_VISIBLE (f, 1);
               SET_FRAME_ICONIFIED (f, false);
               f->output_data.x->has_been_visible = true;
+              font_flush_frame_caches (f);
               SET_FRAME_GARBAGED (f);
             }
           else
@@ -8437,7 +8484,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
             {
               if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height
                   || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width)
-                SET_FRAME_GARBAGED (f);
+                {
+                  font_flush_frame_caches (f);
+                  SET_FRAME_GARBAGED (f);
+                }
               FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height;
               FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width;
             }
@@ -8463,7 +8513,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
               || configureEvent.xconfigure.height != FRAME_PIXEL_HEIGHT (f))
             {
               change_frame_size (f, width, height, false, true, false, true);
-          x_clear_under_internal_border (f);
+              x_clear_under_internal_border (f);
+              font_flush_frame_caches (f);
           SET_FRAME_GARBAGED (f);
               cancel_mouse_face (f);
             }
@@ -8880,7 +8931,7 @@ x_draw_hollow_cursor (struct window *w, struct glyph_row *row)
   if (dpyinfo->scratch_cursor_gc)
     XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv);
   else
-    dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_WINDOW (f),
+    dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f),
                         GCForeground, &xgcv);
   gc = dpyinfo->scratch_cursor_gc;

@@ -8937,7 +8988,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text
   else
     {
       Display *dpy = FRAME_X_DISPLAY (f);
-      Window window = FRAME_X_WINDOW (f);
+      Drawable drawable = FRAME_X_DRAWABLE (f);
       GC gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc;
       unsigned long mask = GCForeground | GCBackground | GCGraphicsExposures;
       struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
@@ -8958,7 +9009,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text
     XChangeGC (dpy, gc, mask, &xgcv);
       else
     {
-      gc = XCreateGC (dpy, window, mask, &xgcv);
+          gc = XCreateGC (dpy, drawable, mask, &xgcv);
       FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
     }

@@ -11271,7 +11322,14 @@ x_free_frame_resources (struct frame *f)
 #endif /* USE_GTK */

       if (FRAME_X_WINDOW (f))
-    XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
+        {
+#ifdef HAVE_XDBE
+          if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+            XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f),
+                                          FRAME_X_DRAWABLE (f));
+#endif
+          XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
+        }
 #endif /* !USE_X_TOOLKIT */

       unload_color (f, FRAME_FOREGROUND_PIXEL (f));
@@ -12111,7 +12169,17 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
     }
   else
     dpyinfo->cmap = XCreateColormap (dpyinfo->display, dpyinfo->root_window,
-                     dpyinfo->visual, AllocNone);
+                                     dpyinfo->visual, AllocNone);
+
+#ifdef HAVE_XDBE
+  dpyinfo->supports_xdbe = false;
+    {
+      int xdbe_major;
+      int xdbe_minor;
+      if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor))
+        dpyinfo->supports_xdbe = true;
+    }
+#endif

 #ifdef HAVE_XFT
   {
@@ -12590,6 +12658,7 @@ x_create_terminal (struct x_display_info *dpyinfo)
   terminal->toggle_invisible_pointer_hook = XTtoggle_invisible_pointer;
   terminal->update_begin_hook = x_update_begin;
   terminal->update_end_hook = x_update_end;
+  terminal->redisplay_end_hook = x_redisplay_end;
   terminal->read_socket_hook = XTread_socket;
   terminal->frame_up_to_date_hook = XTframe_up_to_date;
   terminal->mouse_position_hook = XTmouse_position;
diff --git a/src/xterm.h b/src/xterm.h
index 675a484..cb1aa1d 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -475,6 +475,10 @@ struct x_display_info
 #ifdef USE_XCB
   xcb_connection_t *xcb_connection;
 #endif
+
+#ifdef HAVE_XDBE
+  bool supports_xdbe;
+#endif
 };

 #ifdef HAVE_X_I18N
@@ -527,6 +531,17 @@ struct x_output
      and the X window has not yet been created.  */
   Window window_desc;

+#ifdef HAVE_XDBE
+  /* The drawable to which we're rendering.  In the single-buffered
+     base, the window itself.  In the double-buffered case, the
+     window's back buffer.  */
+  Drawable draw_desc;
+
+  /* Set to true when we need a buffer flip.  We do a buffer flip only
+     at the end of redisplay in order to minimize flicker.  */
+  bool need_buffer_flip;
+#endif
+
   /* The X window used for the bitmap icon;
      or 0 if we don't have a bitmap icon.  */
   Window icon_desc;
@@ -737,6 +752,18 @@ enum
 /* Return the X window used for displaying data in frame F.  */
 #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc)

+/* Return the drawable used for rendering to frame F.  */
+#ifdef HAVE_XDBE
+#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc)
+#else
+#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f)))
+#endif
+
+/* Return the need-buffer-flip flag for frame F.  */
+#ifdef HAVE_XDBE
+#define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip)
+#endif
+
 /* Return the outermost X window associated with the frame F.  */
 #ifdef USE_X_TOOLKIT
 #define FRAME_OUTER_WINDOW(f) ((f)->output_data.x->widget ?             \
@@ -1140,6 +1167,8 @@ extern bool x_wm_supports (struct frame *, Atom);
 extern void x_wait_for_event (struct frame *, int);
 extern void x_clear_under_internal_border (struct frame *f);

+extern void set_up_x_back_buffer (struct frame* f);
+
 /* Defined in xselect.c.  */

 extern void x_handle_property_notify (const XPropertyEvent *);

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione
  2016-10-21  2:27 ` Lars Ingebrigtsen
  2016-10-21  3:24 ` Óscar Fuentes
@ 2016-10-21  3:56 ` Clément Pit--Claudel
  2016-10-21  8:49 ` Eli Zaretskii
  3 siblings, 0 replies; 31+ messages in thread
From: Clément Pit--Claudel @ 2016-10-21  3:56 UTC (permalink / raw)
  To: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 42616 bytes --]

Hi Daniel,

This is brillant!  I applied the patch and everything seems to work great: all of the cases in which I usually see flickering (I tried resizing windows, running a process in the background, and isearch) seemed fixed :)

Congrats on this very nice patch :)
Clément.

On 2016-10-20 21:32, Daniel Colascione wrote:
> This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to avoid showing the user incomplete drawing results. Without this patch, I can make Emacs flicker like crazy by running isearch for a piece of text unique in a buffer and holding down C-s. With this patch, Emacs does not flicker no matter what I do to it.
> 
> The patch also stops flickering that occurs when using the "solid resizing" feature of some window managers --- i.e., when the WM redraws windows as the user drags their edges, as opposed to displaying some kind of bounding-box in lieu of the actual window contents.
> 
> I've tried to avoid changing the logic in the single-buffered case (which we still support), and I've also tried to avoid touching Cairo rendering. This patch should work with any toolkit --- I've tested GTK+3 and no toolkit at all.
> 
> A few notes:
> 
> * We do a buffer flip at the end of redisplay instead of in x_update_end() so the user never sees the completely-cleared state that we enter immediately after clear_garbaged_frames(). x_update_end() does do a buffer flip if it's called outside redisplay. I've added a new terminal hook to support this hack.
> 
> * The DBE documentation claims that XClearWindow and XClearArea clear both the front and back buffers. It's a lie. In my experiments, these functions clear only the front buffer.
> 
> * XFT stops drawing after we give XftCreateDraw a DBE back-buffer and the size of that back buffer changes. To work around this problem, we discard any caches XftDraw object we might have to a frame after that frame changes size. I haven't noticed any performance problems.
> 
> commit 15fdd8f63533201f05627ede634a8f5ae4757d7e
> Author: Daniel Colascione <dancol@dancol.org>
> Date:   Thu Oct 20 16:50:54 2016 -0700
> 
>     Add double-buffered output support to Emacs
> 
> diff --git a/configure.ac b/configure.ac
> index cd11b10..4716b43 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -3712,6 +3712,24 @@ AC_DEFUN
>  AC_SUBST(XFIXES_CFLAGS)
>  AC_SUBST(XFIXES_LIBS)
> 
> +### Use Xdbe (-lXdbe) if available
> +HAVE_XDBE=no
> +if test "${HAVE_X11}" = "yes"; then
> +  AC_CHECK_HEADER(X11/extensions/Xdbe.h,
> +    [AC_CHECK_LIB(Xext, XdbeAllocateBackBufferName, HAVE_XDBE=yes)],
> +    [],
> +    [#include <X11/Xlib.h>
> +    ])
> +  if test $HAVE_XDBE = yes; then
> +    XDBE_LIBS=-lXext
> +  fi
> +  if test $HAVE_XDBE = yes; then
> +    AC_DEFINE(HAVE_XDBE, 1, [Define to 1 if you have the Xdbe extension.])
> +  fi
> +fi
> +AC_SUBST(XDBE_CFLAGS)
> +AC_SUBST(XDBE_LIBS)
> +
>  ### Use libxml (-lxml2) if available
>  ### mingw32 doesn't use -lxml2, since it loads the library dynamically.
>  HAVE_LIBXML2=no
> diff --git a/src/Makefile.in b/src/Makefile.in
> index 89f7a92..dc0bfff 100644
> --- a/src/Makefile.in
> +++ b/src/Makefile.in
> @@ -254,6 +254,9 @@ XINERAMA_CFLAGS =
>  XFIXES_LIBS = @XFIXES_LIBS@
>  XFIXES_CFLAGS = @XFIXES_CFLAGS@
> 
> +XDBE_LIBS = @XDBE_LIBS@
> +XDBE_CFLAGS = @XDBE_CFLAGS@
> +
>  ## widget.o if USE_X_TOOLKIT, otherwise empty.
>  WIDGET_OBJ=@WIDGET_OBJ@
> 
> @@ -372,7 +375,7 @@ ALL_CFLAGS=
>    $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \
>    $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
>    $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \
> -  $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \
> +  $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \
>    $(WEBKIT_CFLAGS) \
>    $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
>    $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
> @@ -489,6 +492,7 @@ LIBES =
>     $(WEBKIT_LIBS) \
>     $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
>     $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \
> +   $(XDBE_LIBS) \
>     $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \
>     $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
>     $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
> diff --git a/src/dispnew.c b/src/dispnew.c
> index 70d4de0..8f81cee 100644
> --- a/src/dispnew.c
> +++ b/src/dispnew.c
> @@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f)
>  {
>    /* Error if F has no glyphs.  */
>    eassert (f->glyphs_initialized_p);
> +  font_flush_frame_caches (f);
>    update_begin (f);
>    if (FRAME_MSDOS_P (f))
>      FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f));
> diff --git a/src/font.c b/src/font.c
> index f8e6794..033995e 100644
> --- a/src/font.c
> +++ b/src/font.c
> @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, Lisp_Object arg, Lisp_Object result)
>  }
> 
>  void
> +font_flush_frame_caches (struct frame *f)
> +{
> +  struct font_driver_list *list;
> +
> +  for (list = f->font_driver_list; list; list = list->next)
> +    if (list->on && list->driver->flush_frame_caches)
> +      list->driver->flush_frame_caches (f);
> +}
> +
> +void
>  syms_of_font (void)
>  {
>    sort_shift_bits[FONT_TYPE_INDEX] = 0;
> diff --git a/src/font.h b/src/font.h
> index cf47729..961e9c4 100644
> --- a/src/font.h
> +++ b/src/font.h
> @@ -763,6 +763,12 @@ struct font_driver
>       Return non-nil if the driver support rendering of combining
>       characters for FONT according to Unicode combining class.  */
>    Lisp_Object (*combining_capability) (struct font *font);
> +
> +  /* Optional
> +
> +     Called when frame F is redrawn from scratch.  Font engines may
> +     invalidate certain caches in this case.  */
> +  void (*flush_frame_caches) (struct frame *f);
>  };
> 
> 
> @@ -862,7 +868,9 @@ extern void *font_get_frame_data (struct frame *f, Lisp_Object);
>  extern void font_filter_properties (Lisp_Object font,
>                      Lisp_Object alist,
>                      const char *const boolean_properties[],
> -                    const char *const non_boolean_properties[]);
> +                                    const char *const non_boolean_properties[]);
> +
> +extern void font_flush_frame_caches (struct frame *f);
> 
>  #ifdef HAVE_FREETYPE
>  extern struct font_driver ftfont_driver;
> diff --git a/src/ftxfont.c b/src/ftxfont.c
> index f49d44f..bfdeb40 100644
> --- a/src/ftxfont.c
> +++ b/src/ftxfont.c
> @@ -95,7 +95,7 @@ ftxfont_get_gcs (struct frame *f, unsigned long foreground, unsigned long backgr
>        if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &color))
>      break;
>        xgcv.foreground = color.pixel;
> -      new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +      new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                     GCForeground, &xgcv);
>      }
>    unblock_input ();
> @@ -139,14 +139,14 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font,
>          p[n[0]].y = y - bitmap.top + i;
>          if (++n[0] == size)
>            {
> -            XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +                    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                   gc_fore, p, size, CoordModeOrigin);
>              n[0] = 0;
>            }
>            }
>      }
>        if (flush && n[0] > 0)
> -    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +        XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>               gc_fore, p, n[0], CoordModeOrigin);
>      }
>    else
> @@ -168,7 +168,7 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font,
>            pp[n[idx]].y = y - bitmap.top + i;
>            if (++(n[idx]) == size)
>              {
> -              XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +                      XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                     idx == 6 ? gc_fore : gcs[idx], pp, size,
>                     CoordModeOrigin);
>                n[idx] = 0;
> @@ -180,10 +180,10 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font,
>      {
>        for (i = 0; i < 6; i++)
>          if (n[i] > 0)
> -          XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +              XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                 gcs[i], p + 0x100 * i, n[i], CoordModeOrigin);
>        if (n[6] > 0)
> -        XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +            XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>               gc_fore, p + 0x600, n[6], CoordModeOrigin);
>      }
>      }
> @@ -203,7 +203,7 @@ ftxfont_draw_background (struct frame *f, struct font *font, GC gc, int x, int y
>    XGetGCValues (FRAME_X_DISPLAY (f), gc,
>          GCForeground | GCBackground, &xgcv);
>    XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background);
> -  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc,
> +  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc,
>            x, y - FONT_BASE (font), width, FONT_HEIGHT (font));
>    XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground);
>  }
> diff --git a/src/gtkutil.c b/src/gtkutil.c
> index 88e6d30..f81940b 100644
> --- a/src/gtkutil.c
> +++ b/src/gtkutil.c
> @@ -48,6 +48,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
>  #include "emacsgtkfixed.h"
>  #endif
> 
> +#ifdef HAVE_XDBE
> +#include <X11/extensions/Xdbe.h>
> +#endif
> +
>  #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
>  #define gtk_widget_set_has_window(w, b) \
>    (gtk_fixed_set_has_window (GTK_FIXED (w), b))
> @@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f)
>       by callers of this function.  */
>    gtk_widget_realize (wfixed);
>    FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
> +  set_up_x_back_buffer (f);
> 
>    /* Since GTK clears its window by filling with the background color,
>       we must keep X and GTK background in sync.  */
> @@ -1296,6 +1301,15 @@ xg_free_frame_widgets (struct frame *f)
>        if (tbinfo)
>          xfree (tbinfo);
> 
> +#ifdef HAVE_XDBE
> +      if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
> +        {
> +          XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f),
> +                                        FRAME_X_DRAWABLE (f));
> +          FRAME_X_DRAWABLE (f) = 0;
> +        }
> +#endif
> +
>        gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
>        FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
>        FRAME_GTK_OUTER_WIDGET (f) = 0;
> diff --git a/src/image.c b/src/image.c
> index 9bd2455..1303a93 100644
> --- a/src/image.c
> +++ b/src/image.c
> @@ -220,7 +220,7 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi
> 
>  #ifdef HAVE_X_WINDOWS
>    Pixmap bitmap;
> -  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                    bits, width, height);
>    if (! bitmap)
>      return -1;
> @@ -327,7 +327,7 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file)
> 
>    filename = SSDATA (found);
> 
> -  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                  filename, &width, &height, &bitmap, &xhot, &yhot);
>    if (result != BitmapSuccess)
>      return -1;
> @@ -1952,7 +1952,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
>  {
>  #ifdef HAVE_X_WINDOWS
>    Display *display = FRAME_X_DISPLAY (f);
> -  Window window = FRAME_X_WINDOW (f);
> +  Drawable drawable = FRAME_X_DRAWABLE (f);
>    Screen *screen = FRAME_X_SCREEN (f);
> 
>    eassert (input_blocked_p ());
> @@ -1981,7 +1981,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
>    (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);
> 
>    /* Allocate a pixmap of the same size.  */
> -  *pixmap = XCreatePixmap (display, window, width, height, depth);
> +  *pixmap = XCreatePixmap (display, drawable, width, height, depth);
>    if (*pixmap == NO_PIXMAP)
>      {
>        x_destroy_x_image (*ximg);
> @@ -2742,7 +2742,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data,
>    img->pixmap =
>     (x_check_image_size (0, img->width, img->height)
>      ? XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f),
> -                   FRAME_X_WINDOW (f),
> +                                   FRAME_X_DRAWABLE (f),
>                     data,
>                     img->width, img->height,
>                     fg, bg,
> @@ -3520,7 +3520,7 @@ x_create_bitmap_from_xpm_data (struct frame *f, const char **bits)
>    xpm_init_color_cache (f, &attrs);
>  #endif
> 
> -  rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +  rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                  (char **) bits, &bitmap, &mask, &attrs);
>    if (rc != XpmSuccess)
>      {
> @@ -3758,7 +3758,7 @@ xpm_load (struct frame *f, struct image *img)
>  #ifdef HAVE_X_WINDOWS
>    if (rc == XpmSuccess)
>      {
> -      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                     img->ximg->width, img->ximg->height,
>                     img->ximg->depth);
>        if (img->pixmap == NO_PIXMAP)
> @@ -3768,7 +3768,7 @@ xpm_load (struct frame *f, struct image *img)
>      }
>        else if (img->mask_img)
>      {
> -      img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +          img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                       img->mask_img->width,
>                       img->mask_img->height,
>                       img->mask_img->depth);
> @@ -9541,7 +9541,7 @@ gs_load (struct frame *f, struct image *img)
>      {
>        /* Only W32 version did BLOCK_INPUT here.  ++kfs */
>        block_input ();
> -      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>                     img->width, img->height,
>                     DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
>        unblock_input ();
> @@ -9557,7 +9557,7 @@ gs_load (struct frame *f, struct image *img)
>       if successful.  We do not record_unwind_protect here because
>       other places in redisplay like calling window scroll functions
>       don't either.  Let the Lisp loader use `unwind-protect' instead.  */
> -  printnum1 = FRAME_X_WINDOW (f);
> +  printnum1 = FRAME_X_DRAWABLE (f);
>    printnum2 = img->pixmap;
>    window_and_pixmap_id
>      = make_formatted_string (buffer, "%"pMu" %"pMu, printnum1, printnum2);
> diff --git a/src/termhooks.h b/src/termhooks.h
> index ff74d99..c8d7fae 100644
> --- a/src/termhooks.h
> +++ b/src/termhooks.h
> @@ -477,6 +477,7 @@ struct terminal
> 
>    void (*update_begin_hook) (struct frame *);
>    void (*update_end_hook) (struct frame *);
> +  void (*redisplay_end_hook) (struct frame *);
>    void (*set_terminal_window_hook) (struct frame *, int);
> 
>    /* Multi-frame and mouse support hooks.  */
> diff --git a/src/xdisp.c b/src/xdisp.c
> index 3af5ea4..04f0ca1 100644
> --- a/src/xdisp.c
> +++ b/src/xdisp.c
> @@ -2501,7 +2501,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect)
> 
>    /* Visible feedback for debugging.  */
>  #if false && defined HAVE_X_WINDOWS
> -  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>            f->output_data.x->normal_gc,
>            gx, gy, width, height);
>  #endif
> @@ -14211,6 +14211,13 @@ redisplay_internal (void)
>        windows_or_buffers_changed = 0;
>      }
> 
> +  FOR_EACH_FRAME (tail, frame)
> +    {
> +      struct frame *f = XFRAME (frame);
> +      if (FRAME_TERMINAL (f)->redisplay_end_hook)
> +        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
> +    }
> +
>    /* Start SIGIO interrupts coming again.  Having them off during the
>       code above makes it less likely one will discard output, but not
>       impossible, since there might be stuff in the system buffer here.
> @@ -24608,7 +24615,7 @@ init_glyph_string (struct glyph_string *s,
>    s->hdc = hdc;
>  #endif
>    s->display = FRAME_X_DISPLAY (s->f);
> -  s->window = FRAME_X_WINDOW (s->f);
> +  s->window = FRAME_X_DRAWABLE (s->f);
>    s->char2b = char2b;
>    s->hl = hl;
>    s->row = row;
> diff --git a/src/xfaces.c b/src/xfaces.c
> index 5837f35..accb98b 100644
> --- a/src/xfaces.c
> +++ b/src/xfaces.c
> @@ -495,7 +495,7 @@ x_create_gc (struct frame *f, unsigned long mask, XGCValues *xgcv)
>  {
>    GC gc;
>    block_input ();
> -  gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), mask, xgcv);
> +  gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), mask, xgcv);
>    unblock_input ();
>    IF_DEBUG (++ngcs);
>    return gc;
> diff --git a/src/xfns.c b/src/xfns.c
> index 8571d0e..2098036 100644
> --- a/src/xfns.c
> +++ b/src/xfns.c
> @@ -53,6 +53,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
>  #include "gtkutil.h"
>  #endif
> 
> +#ifdef HAVE_XDBE
> +#include <X11/extensions/Xdbe.h>
> +#endif
> +
>  #ifdef USE_X_TOOLKIT
>  #include <X11/Shell.h>
> 
> @@ -2483,6 +2487,29 @@ xic_set_xfontset (struct frame *f, const char *base_fontname)
> 
> 
>  \f
> +
> +void
> +set_up_x_back_buffer (struct frame* f)
> +{
> +  FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
> +#ifdef HAVE_XDBE
> +  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
> +    {
> +      /* If allocating a back buffer fails, just use single-buffered
> +         rendering.  */
> +      x_sync (f);
> +      x_catch_errors (FRAME_X_DISPLAY (f));
> +      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
> +        FRAME_X_DISPLAY (f),
> +        FRAME_X_WINDOW (f),
> +        XdbeCopied);
> +      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
> +        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
> +      x_uncatch_errors_after_check ();
> +    }
> +#endif
> +}
> +
>  #ifdef USE_X_TOOLKIT
> 
>  /* Create and set up the X widget for frame F.  */
> @@ -2638,7 +2665,7 @@ x_window (struct frame *f, long window_prompting)
>               f->output_data.x->parent_desc, 0, 0);
> 
>    FRAME_X_WINDOW (f) = XtWindow (frame_widget);
> -
> +  set_up_x_back_buffer (f);
>    validate_x_resource_name ();
> 
>    class_hints.res_name = SSDATA (Vx_resource_name);
> @@ -2784,7 +2811,8 @@ x_window (struct frame *f)
>               CopyFromParent, /* depth */
>               InputOutput, /* class */
>               FRAME_X_VISUAL (f),
> -             attribute_mask, &attributes);
> +                     attribute_mask, &attributes);
> +  set_up_x_back_buffer (f);
> 
>  #ifdef HAVE_X_I18N
>    if (use_xim)
> @@ -2938,7 +2966,7 @@ x_make_gc (struct frame *f)
>    gc_values.line_width = 0;    /* Means 1 using fast algorithm.  */
>    f->output_data.x->normal_gc
>      = XCreateGC (FRAME_X_DISPLAY (f),
> -         FRAME_X_WINDOW (f),
> +                 FRAME_X_DRAWABLE (f),
>           GCLineWidth | GCForeground | GCBackground,
>           &gc_values);
> 
> @@ -2947,7 +2975,7 @@ x_make_gc (struct frame *f)
>    gc_values.background = FRAME_FOREGROUND_PIXEL (f);
>    f->output_data.x->reverse_gc
>      = XCreateGC (FRAME_X_DISPLAY (f),
> -         FRAME_X_WINDOW (f),
> +                 FRAME_X_DRAWABLE (f),
>           GCForeground | GCBackground | GCLineWidth,
>           &gc_values);
> 
> @@ -2956,7 +2984,7 @@ x_make_gc (struct frame *f)
>    gc_values.background = f->output_data.x->cursor_pixel;
>    gc_values.fill_style = FillOpaqueStippled;
>    f->output_data.x->cursor_gc
> -    = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +    = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>           (GCForeground | GCBackground
>            | GCFillStyle | GCLineWidth),
>           &gc_values);
> @@ -5636,7 +5664,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms)
>                 /* Border.  */
>                 f->border_width,
>                 CopyFromParent, InputOutput, CopyFromParent,
> -               mask, &attrs);
> +                       mask, &attrs);
> +    set_up_x_back_buffer (f);
>      XChangeProperty (FRAME_X_DISPLAY (f), tip_window,
>                       FRAME_DISPLAY_INFO (f)->Xatom_net_window_type,
>                       XA_ATOM, 32, PropModeReplace,
> diff --git a/src/xfont.c b/src/xfont.c
> index 45b0e0a..c2b7317 100644
> --- a/src/xfont.c
> +++ b/src/xfont.c
> @@ -1057,20 +1057,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y,
>      {
>        if (s->padding_p)
>          for (i = 0; i < len; i++)
> -          XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
> +              XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
>                  gc, x + i, y, str + i, 1);
>        else
> -        XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
> +            XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
>                    gc, x, y, str, len);
>      }
>        else
>      {
>        if (s->padding_p)
>          for (i = 0; i < len; i++)
> -          XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
> +              XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
>                 gc, x + i, y, str + i, 1);
>        else
> -        XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
> +            XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
>               gc, x, y, str, len);
>      }
>        unblock_input ();
> @@ -1083,20 +1083,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y,
>      {
>        if (s->padding_p)
>      for (i = 0; i < len; i++)
> -      XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
> +          XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
>                    gc, x + i, y, s->char2b + from + i, 1);
>        else
> -    XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
> +        XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
>                  gc, x, y, s->char2b + from, len);
>      }
>    else
>      {
>        if (s->padding_p)
>      for (i = 0; i < len; i++)
> -      XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
> +          XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
>               gc, x + i, y, s->char2b + from + i, 1);
>        else
> -    XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
> +        XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
>                 gc, x, y, s->char2b + from, len);
>      }
>    unblock_input ();
> diff --git a/src/xftfont.c b/src/xftfont.c
> index 34c6f7d..447adf6 100644
> --- a/src/xftfont.c
> +++ b/src/xftfont.c
> @@ -586,7 +586,7 @@ xftfont_get_xft_draw (struct frame *f)
>      {
>        block_input ();
>        xft_draw= XftDrawCreate (FRAME_X_DISPLAY (f),
> -                   FRAME_X_WINDOW (f),
> +                               FRAME_X_DRAWABLE (f),
>                     FRAME_X_VISUAL (f),
>                     FRAME_X_COLORMAP (f));
>        unblock_input ();
> @@ -695,6 +695,13 @@ xftfont_end_for_frame (struct frame *f)
>    return 0;
>  }
> 
> +static void
> +xftfont_flush_frame_caches (struct frame *f)
> +{
> +  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
> +    xftfont_end_for_frame (f);
> +}
> +
>  static bool
>  xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object,
>                          Lisp_Object entity)
> @@ -777,6 +784,9 @@ This is needed with some fonts to correct vertical overlap of glyphs.  */);
>  #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF)
>    xftfont_driver.shape = xftfont_shape;
>  #endif
> +  // When using X double buffering, the XftDraw structure we
> +  // build seems to be useless once a frame is resized, so
> +  xftfont_driver.flush_frame_caches = xftfont_flush_frame_caches;
> 
>    register_font_driver (&xftfont_driver, NULL);
>  }
> diff --git a/src/xterm.c b/src/xterm.c
> index 7476694..7116784 100644
> --- a/src/xterm.c
> +++ b/src/xterm.c
> @@ -45,6 +45,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
>  #include <X11/extensions/Xrender.h>
>  #endif
> 
> +#ifdef HAVE_XDBE
> +#include <X11/extensions/Xdbe.h>
> +#endif
> +
>  /* Load sys/types.h if not already loaded.
>     In some systems loading it twice is suicidal.  */
>  #ifndef makedev
> @@ -360,7 +364,7 @@ x_begin_cr_clip (struct frame *f, GC gc)
>          {
>            cairo_surface_t *surface;
>            surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
> -                                               FRAME_X_WINDOW (f),
> +                                               FRAME_X_DRAWABLE (f),
>                                                 FRAME_DISPLAY_INFO (f)->visual,
>                                                 FRAME_PIXEL_WIDTH (f),
>                                                 FRAME_PIXEL_HEIGHT (f));
> @@ -722,7 +726,7 @@ x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height)
>    cairo_fill (cr);
>    x_end_cr_clip (f);
>  #else
> -  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>            gc, x, y, width, height);
>  #endif
>  }
> @@ -740,7 +744,7 @@ x_draw_rectangle (struct frame *f, GC gc, int x, int y, int width, int height)
>    cairo_stroke (cr);
>    x_end_cr_clip (f);
>  #else
> -  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>            gc, x, y, width, height);
>  #endif
>  }
> @@ -756,7 +760,10 @@ x_clear_window (struct frame *f)
>    cairo_paint (cr);
>    x_end_cr_clip (f);
>  #else
> -  XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
> +  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
> +    x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
> +  else
> +    XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
>  #endif
>  }
> 
> @@ -1067,7 +1074,7 @@ x_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
>  #ifdef USE_CAIRO
>    x_fill_rectangle (f, f->output_data.x->normal_gc, x, y0, 1, y1 - y0);
>  #else
> -  XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +  XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
>           f->output_data.x->normal_gc, x, y0, x, y1);
>  #endif
>  }
> @@ -1179,6 +1186,21 @@ x_update_window_end (struct window *w, bool cursor_on_p,
>  /* End update of frame F.  This function is installed as a hook in
>     update_end.  */
> 
> +#if defined (HAVE_XDBE)
> +static void
> +show_back_buffer (struct frame *f)
> +{
> +  if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
> +    {
> +      XdbeSwapInfo swap_info;
> +      memset (&swap_info, 0, sizeof (swap_info));
> +      swap_info.swap_window = FRAME_X_WINDOW (f);
> +      swap_info.swap_action = XdbeCopied;
> +      XdbeSwapBuffers (FRAME_X_DISPLAY (f), &swap_info, 1);
> +    }
> +}
> +#endif
> +
>  static void
>  x_update_end (struct frame *f)
>  {
> @@ -1207,7 +1229,7 @@ x_update_end (struct frame *f)
>            if (! FRAME_EXTERNAL_MENU_BAR (f))
>              height += FRAME_MENU_BAR_HEIGHT (f);
>            surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
> -                                               FRAME_X_WINDOW (f),
> +                                               FRAME_X_DRAWABLE (f),
>                                                 FRAME_DISPLAY_INFO (f)->visual,
>                                                 width,
>                                                 height);
> @@ -1220,7 +1242,14 @@ x_update_end (struct frame *f)
>        cairo_destroy (cr);
>        unblock_input ();
>      }
> -#endif /* USE_CAIRO */
> +#endif
> +
> +#ifdef HAVE_XDBE
> +  if (redisplaying_p)
> +    FRAME_X_NEED_BUFFER_FLIP (f) = true;
> +  else
> +    show_back_buffer (f);
> +#endif
> 
>  #ifndef XFlush
>    block_input ();
> @@ -1229,6 +1258,17 @@ x_update_end (struct frame *f)
>  #endif
>  }
> 
> +static void
> +x_redisplay_end (struct frame *f)
> +{
> +#ifdef HAVE_XDBE
> +  if (FRAME_X_NEED_BUFFER_FLIP (f))
> +    {
> +      show_back_buffer (f);
> +      FRAME_X_NEED_BUFFER_FLIP (f) = false;
> +    }
> +#endif
> +}
> 
>  /* This function is called from various places in xdisp.c
>     whenever a complete update has been performed.  */
> @@ -1354,7 +1394,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring
>  #else  /* not USE_CAIRO */
>    if (p->which)
>      {
> -      Window window = FRAME_X_WINDOW (f);
> +      Drawable drawable = FRAME_X_DRAWABLE (f);
>        char *bits;
>        Pixmap pixmap, clipmask = (Pixmap) 0;
>        int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f));
> @@ -1367,7 +1407,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring
> 
>        /* Draw the bitmap.  I believe these small pixmaps can be cached
>       by the server.  */
> -      pixmap = XCreatePixmapFromBitmapData (display, window, bits, p->wd, p->h,
> +      pixmap = XCreatePixmapFromBitmapData (display, drawable, bits, p->wd, p->h,
>                          (p->cursor_p
>                           ? (p->overlay_p ? face->background
>                          : f->output_data.x->cursor_pixel)
> @@ -1386,7 +1426,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring
>        XChangeGC (display, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
>      }
> 
> -      XCopyArea (display, pixmap, window, gc, 0, 0,
> +      XCopyArea (display, pixmap, drawable, gc, 0, 0,
>           p->wd, p->h, p->x, p->y);
>        XFreePixmap (display, pixmap);
> 
> @@ -2565,7 +2605,7 @@ x_setup_relief_color (struct frame *f, struct relief *relief, double factor,
>      {
>        xgcv.stipple = dpyinfo->gray;
>        mask |= GCStipple;
> -      relief->gc = XCreateGC (dpy, FRAME_X_WINDOW (f), mask, &xgcv);
> +      relief->gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), mask, &xgcv);
>      }
>    else
>      XChangeGC (dpy, relief->gc, mask, &xgcv);
> @@ -2696,7 +2736,7 @@ x_draw_relief_rect (struct frame *f,
>    x_reset_clip_rectangles (f, bottom_right_gc);
>  #else
>    Display *dpy = FRAME_X_DISPLAY (f);
> -  Window window = FRAME_X_WINDOW (f);
> +  Drawable drawable = FRAME_X_DRAWABLE (f);
>    int i;
>    GC gc;
> 
> @@ -2715,12 +2755,12 @@ x_draw_relief_rect (struct frame *f,
>    if (top_p)
>      {
>        if (width == 1)
> -    XDrawLine (dpy, window, gc,
> +        XDrawLine (dpy, drawable, gc,
>             left_x + left_p, top_y,
>             right_x + !right_p, top_y);
> 
>        for (i = 1; i < width; ++i)
> -    XDrawLine (dpy, window, gc,
> +        XDrawLine (dpy, drawable, gc,
>             left_x  + i * left_p, top_y + i,
>             right_x + 1 - i * right_p, top_y + i);
>      }
> @@ -2729,13 +2769,13 @@ x_draw_relief_rect (struct frame *f,
>    if (left_p)
>      {
>        if (width == 1)
> -    XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y);
> +        XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y);
> 
> -      XClearArea (dpy, window, left_x, top_y, 1, 1, False);
> -      XClearArea (dpy, window, left_x, bottom_y, 1, 1, False);
> +      x_clear_area(f, left_x, top_y, 1, 1);
> +      x_clear_area(f, left_x, bottom_y, 1, 1);
> 
>        for (i = (width > 1 ? 1 : 0); i < width; ++i)
> -    XDrawLine (dpy, window, gc,
> +        XDrawLine (dpy, drawable, gc,
>             left_x + i, top_y + (i + 1) * top_p,
>             left_x + i, bottom_y + 1 - (i + 1) * bot_p);
>      }
> @@ -2751,23 +2791,23 @@ x_draw_relief_rect (struct frame *f,
>      {
>        /* Outermost top line.  */
>        if (top_p)
> -    XDrawLine (dpy, window, gc,
> +        XDrawLine (dpy, drawable, gc,
>             left_x  + left_p, top_y,
>             right_x + !right_p, top_y);
> 
>        /* Outermost left line.  */
>        if (left_p)
> -    XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y);
> +        XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y);
>      }
> 
>    /* Bottom.  */
>    if (bot_p)
>      {
> -      XDrawLine (dpy, window, gc,
> +      XDrawLine (dpy, drawable, gc,
>           left_x + left_p, bottom_y,
>           right_x + !right_p, bottom_y);
>        for (i = 1; i < width; ++i)
> -    XDrawLine (dpy, window, gc,
> +        XDrawLine (dpy, drawable, gc,
>             left_x  + i * left_p, bottom_y - i,
>             right_x + 1 - i * right_p, bottom_y - i);
>      }
> @@ -2775,10 +2815,10 @@ x_draw_relief_rect (struct frame *f,
>    /* Right.  */
>    if (right_p)
>      {
> -      XClearArea (dpy, window, right_x, top_y, 1, 1, False);
> -      XClearArea (dpy, window, right_x, bottom_y, 1, 1, False);
> +      x_clear_area(f, right_x, top_y, 1, 1);
> +      x_clear_area(f, right_x, bottom_y, 1, 1);
>        for (i = 0; i < width; ++i)
> -    XDrawLine (dpy, window, gc,
> +        XDrawLine (dpy, drawable, gc,
>             right_x - i, top_y + (i + 1) * top_p,
>             right_x - i, bottom_y + 1 - (i + 1) * bot_p);
>      }
> @@ -3741,7 +3781,7 @@ x_shift_glyphs_for_insert (struct frame *f, int x, int y, int width, int height,
>  /* Never called on a GUI frame, see
>     http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html
>  */
> -  XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), FRAME_X_WINDOW (f),
> +  XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f),
>           f->output_data.x->normal_gc,
>           x, y, width, height,
>           x + shift_by, y);
> @@ -3782,8 +3822,14 @@ x_clear_area (struct frame *f, int x, int y, int width, int height)
>    cairo_fill (cr);
>    x_end_cr_clip (f);
>  #else
> -  x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> -         x, y, width, height, False);
> +    if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
> +      XFillRectangle (FRAME_X_DISPLAY (f),
> +                      FRAME_X_DRAWABLE (f),
> +                      f->output_data.x->reverse_gc,
> +                      x, y, width, height);
> +  else
> +    x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
> +                   x, y, width, height, False);
>  #endif
>  }
> 
> @@ -4109,7 +4155,7 @@ x_scroll_run (struct window *w, struct run *run)
>    SET_FRAME_GARBAGED (f);
>  #else
>    XCopyArea (FRAME_X_DISPLAY (f),
> -         FRAME_X_WINDOW (f), FRAME_X_WINDOW (f),
> +             FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f),
>           f->output_data.x->normal_gc,
>           x, from_y,
>           width, height,
> @@ -7769,6 +7815,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
>                SET_FRAME_VISIBLE (f, 1);
>                SET_FRAME_ICONIFIED (f, false);
>                f->output_data.x->has_been_visible = true;
> +              font_flush_frame_caches (f);
>                SET_FRAME_GARBAGED (f);
>              }
>            else
> @@ -8437,7 +8484,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
>              {
>                if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height
>                    || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width)
> -                SET_FRAME_GARBAGED (f);
> +                {
> +                  font_flush_frame_caches (f);
> +                  SET_FRAME_GARBAGED (f);
> +                }
>                FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height;
>                FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width;
>              }
> @@ -8463,7 +8513,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
>                || configureEvent.xconfigure.height != FRAME_PIXEL_HEIGHT (f))
>              {
>                change_frame_size (f, width, height, false, true, false, true);
> -          x_clear_under_internal_border (f);
> +              x_clear_under_internal_border (f);
> +              font_flush_frame_caches (f);
>            SET_FRAME_GARBAGED (f);
>                cancel_mouse_face (f);
>              }
> @@ -8880,7 +8931,7 @@ x_draw_hollow_cursor (struct window *w, struct glyph_row *row)
>    if (dpyinfo->scratch_cursor_gc)
>      XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv);
>    else
> -    dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_WINDOW (f),
> +    dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f),
>                          GCForeground, &xgcv);
>    gc = dpyinfo->scratch_cursor_gc;
> 
> @@ -8937,7 +8988,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text
>    else
>      {
>        Display *dpy = FRAME_X_DISPLAY (f);
> -      Window window = FRAME_X_WINDOW (f);
> +      Drawable drawable = FRAME_X_DRAWABLE (f);
>        GC gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc;
>        unsigned long mask = GCForeground | GCBackground | GCGraphicsExposures;
>        struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
> @@ -8958,7 +9009,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text
>      XChangeGC (dpy, gc, mask, &xgcv);
>        else
>      {
> -      gc = XCreateGC (dpy, window, mask, &xgcv);
> +          gc = XCreateGC (dpy, drawable, mask, &xgcv);
>        FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
>      }
> 
> @@ -11271,7 +11322,14 @@ x_free_frame_resources (struct frame *f)
>  #endif /* USE_GTK */
> 
>        if (FRAME_X_WINDOW (f))
> -    XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
> +        {
> +#ifdef HAVE_XDBE
> +          if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
> +            XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f),
> +                                          FRAME_X_DRAWABLE (f));
> +#endif
> +          XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
> +        }
>  #endif /* !USE_X_TOOLKIT */
> 
>        unload_color (f, FRAME_FOREGROUND_PIXEL (f));
> @@ -12111,7 +12169,17 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
>      }
>    else
>      dpyinfo->cmap = XCreateColormap (dpyinfo->display, dpyinfo->root_window,
> -                     dpyinfo->visual, AllocNone);
> +                                     dpyinfo->visual, AllocNone);
> +
> +#ifdef HAVE_XDBE
> +  dpyinfo->supports_xdbe = false;
> +    {
> +      int xdbe_major;
> +      int xdbe_minor;
> +      if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor))
> +        dpyinfo->supports_xdbe = true;
> +    }
> +#endif
> 
>  #ifdef HAVE_XFT
>    {
> @@ -12590,6 +12658,7 @@ x_create_terminal (struct x_display_info *dpyinfo)
>    terminal->toggle_invisible_pointer_hook = XTtoggle_invisible_pointer;
>    terminal->update_begin_hook = x_update_begin;
>    terminal->update_end_hook = x_update_end;
> +  terminal->redisplay_end_hook = x_redisplay_end;
>    terminal->read_socket_hook = XTread_socket;
>    terminal->frame_up_to_date_hook = XTframe_up_to_date;
>    terminal->mouse_position_hook = XTmouse_position;
> diff --git a/src/xterm.h b/src/xterm.h
> index 675a484..cb1aa1d 100644
> --- a/src/xterm.h
> +++ b/src/xterm.h
> @@ -475,6 +475,10 @@ struct x_display_info
>  #ifdef USE_XCB
>    xcb_connection_t *xcb_connection;
>  #endif
> +
> +#ifdef HAVE_XDBE
> +  bool supports_xdbe;
> +#endif
>  };
> 
>  #ifdef HAVE_X_I18N
> @@ -527,6 +531,17 @@ struct x_output
>       and the X window has not yet been created.  */
>    Window window_desc;
> 
> +#ifdef HAVE_XDBE
> +  /* The drawable to which we're rendering.  In the single-buffered
> +     base, the window itself.  In the double-buffered case, the
> +     window's back buffer.  */
> +  Drawable draw_desc;
> +
> +  /* Set to true when we need a buffer flip.  We do a buffer flip only
> +     at the end of redisplay in order to minimize flicker.  */
> +  bool need_buffer_flip;
> +#endif
> +
>    /* The X window used for the bitmap icon;
>       or 0 if we don't have a bitmap icon.  */
>    Window icon_desc;
> @@ -737,6 +752,18 @@ enum
>  /* Return the X window used for displaying data in frame F.  */
>  #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc)
> 
> +/* Return the drawable used for rendering to frame F.  */
> +#ifdef HAVE_XDBE
> +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc)
> +#else
> +#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f)))
> +#endif
> +
> +/* Return the need-buffer-flip flag for frame F.  */
> +#ifdef HAVE_XDBE
> +#define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip)
> +#endif
> +
>  /* Return the outermost X window associated with the frame F.  */
>  #ifdef USE_X_TOOLKIT
>  #define FRAME_OUTER_WINDOW(f) ((f)->output_data.x->widget ?             \
> @@ -1140,6 +1167,8 @@ extern bool x_wm_supports (struct frame *, Atom);
>  extern void x_wait_for_event (struct frame *, int);
>  extern void x_clear_under_internal_border (struct frame *f);
> 
> +extern void set_up_x_back_buffer (struct frame* f);
> +
>  /* Defined in xselect.c.  */
> 
>  extern void x_handle_property_notify (const XPropertyEvent *);
> 
> 
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  3:31   ` Clément Pit--Claudel
  2016-10-21  3:41     ` Óscar Fuentes
@ 2016-10-21  8:23     ` Andreas Schwab
  2016-10-21  8:25       ` Andreas Schwab
  1 sibling, 1 reply; 31+ messages in thread
From: Andreas Schwab @ 2016-10-21  8:23 UTC (permalink / raw)
  To: Clément Pit--Claudel; +Cc: emacs-devel

On Okt 20 2016, Clément Pit--Claudel <clement.pit@gmail.com> wrote:

> On 2016-10-20 23:24, Óscar Fuentes wrote:
>> I'm eager to try your code, but `patch' complains about
>> 
>> oscar@qcore:~/dev/emacs/stable/source$ patch -p 1 < ~/p.patch 
>> patching file configure.ac
>> patching file src/Makefile.in
>> patch: **** malformed patch at line 60: $(LIBSELINUX_LIBS) \
>
> The patch applies fine for me with  git apply --reject --ignore-space-change --ignore-whitespace double-buffer.patch.  Maybe that works for you too?

You should not use format=flowed when posting patches.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  8:23     ` Andreas Schwab
@ 2016-10-21  8:25       ` Andreas Schwab
  0 siblings, 0 replies; 31+ messages in thread
From: Andreas Schwab @ 2016-10-21  8:25 UTC (permalink / raw)
  To: Clément Pit--Claudel; +Cc: emacs-devel

On Okt 21 2016, Andreas Schwab <schwab@linux-m68k.org> wrote:

> On Okt 20 2016, Clément Pit--Claudel <clement.pit@gmail.com> wrote:
>
>> On 2016-10-20 23:24, Óscar Fuentes wrote:
>>> I'm eager to try your code, but `patch' complains about
>>> 
>>> oscar@qcore:~/dev/emacs/stable/source$ patch -p 1 < ~/p.patch 
>>> patching file configure.ac
>>> patching file src/Makefile.in
>>> patch: **** malformed patch at line 60: $(LIBSELINUX_LIBS) \
>>
>> The patch applies fine for me with  git apply --reject --ignore-space-change --ignore-whitespace double-buffer.patch.  Maybe that works for you too?
>
> You should not use format=flowed when posting patches.

That "you" was meant to address the OP, sorry.

Andreas.

-- 
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione
                   ` (2 preceding siblings ...)
  2016-10-21  3:56 ` Clément Pit--Claudel
@ 2016-10-21  8:49 ` Eli Zaretskii
  2016-10-21 11:04   ` Daniel Colascione
  3 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2016-10-21  8:49 UTC (permalink / raw)
  To: Daniel Colascione; +Cc: emacs-devel

> From: Daniel Colascione <dancol@dancol.org>
> Date: Thu, 20 Oct 2016 18:32:01 -0700
> 
> This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to 
> avoid showing the user incomplete drawing results. Without this patch, I 
> can make Emacs flicker like crazy by running isearch for a piece of text 
> unique in a buffer and holding down C-s. With this patch, Emacs does not 
> flicker no matter what I do to it.
> 
> The patch also stops flickering that occurs when using the "solid 
> resizing" feature of some window managers --- i.e., when the WM redraws 
> windows as the user drags their edges, as opposed to displaying some 
> kind of bounding-box in lieu of the actual window contents.

Thanks!

> * We do a buffer flip at the end of redisplay instead of in 
> x_update_end() so the user never sees the completely-cleared state that 
> we enter immediately after clear_garbaged_frames(). x_update_end() does 
> do a buffer flip if it's called outside redisplay. I've added a new 
> terminal hook to support this hack.

What happens in the case where redisplay_internal did its job, but
update_frame was interrupted by incoming input?  Won't that flip the
buffers unnecessarily?

I have some comments below.  Apologies if some of them stem from my
relative ignorance of the issues involved in this changeset.

> commit 15fdd8f63533201f05627ede634a8f5ae4757d7e
> Author: Daniel Colascione <dancol@dancol.org>
> Date:   Thu Oct 20 16:50:54 2016 -0700
> 
>      Add double-buffered output support to Emacs

Please add ChangeLog style commit log entries about each new and
modified function/macro.

Please also provide a NEWS entry about the new feature, in the
"Installation Changes" section.

> +AC_SUBST(XDBE_CFLAGS)
> +AC_SUBST(XDBE_LIBS)

Do we need XDBE_CFLAGS?  They seem to be unused?

Also, I think the fact that Xdbe extensions are used should be shown
in the summary displayed by the configure script when it finishes, and
XDBE should be added to the list in config_emacs_features, so that it
appears in EMACS_CONFIG_FEATURES when appropriate.

> diff --git a/src/dispnew.c b/src/dispnew.c
> index 70d4de0..8f81cee 100644
> --- a/src/dispnew.c
> +++ b/src/dispnew.c
> @@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f)
>   {
>     /* Error if F has no glyphs.  */
>     eassert (f->glyphs_initialized_p);
> +  font_flush_frame_caches (f);
>     update_begin (f);
>     if (FRAME_MSDOS_P (f))
>       FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f));
> diff --git a/src/font.c b/src/font.c
> index f8e6794..033995e 100644
> --- a/src/font.c
> +++ b/src/font.c
> @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, 
> Lisp_Object arg, Lisp_Object result)
>   }
> 
>   void
> +font_flush_frame_caches (struct frame *f)
> +{
> +  struct font_driver_list *list;
> +
> +  for (list = f->font_driver_list; list; list = list->next)
> +    if (list->on && list->driver->flush_frame_caches)
> +      list->driver->flush_frame_caches (f);
> +}

Why do we need this flushing?  Is it relevant to the patch, and if so
why?

I'm asking because we had some bad effects with some fonts due to
flushing the frame's font caches, see bugs 24634 and 24565, for
example, so my bother is that this will reintroduce those problems.

Also, the call to font_flush_frame_caches is unconditional, although
only one font back-end supports it.  That seems to incur a gratuitous
overhead of a function call for the other font back-ends.

In any case, please add a commentary to each function you add with a
summary of what it does.

> @@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f)
>        by callers of this function.  */
>     gtk_widget_realize (wfixed);
>     FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
> +  set_up_x_back_buffer (f);

There's some strange misalignment of indentation here (and in a few
other places).

> +  FOR_EACH_FRAME (tail, frame)
> +    {
> +      struct frame *f = XFRAME (frame);
> +      if (FRAME_TERMINAL (f)->redisplay_end_hook)
> +        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
> +    }

This will call the hook for each frame, every redisplay cycle.  By
contrast, redisplay_internal many times updates only the selected
window of the selected frame.  Is calling the hook for all the frames
really needed, or should we only call it for the selected frame in the
latter case, or maybe just for frames that got updated?  Also, should
we distinguish between visible and iconified frames?

> +#ifdef HAVE_XDBE
> +  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
> +    {
> +      /* If allocating a back buffer fails, just use single-buffered
> +         rendering.  */
> +      x_sync (f);
> +      x_catch_errors (FRAME_X_DISPLAY (f));
> +      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
> +        FRAME_X_DISPLAY (f),
> +        FRAME_X_WINDOW (f),
> +        XdbeCopied);
> +      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
> +        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);

Shouldn't we turn off the supports_xdbe flag in the case of failure?

> +#ifdef HAVE_XDBE
> +  dpyinfo->supports_xdbe = false;
> +    {
> +      int xdbe_major;
> +      int xdbe_minor;
> +      if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor))
> +        dpyinfo->supports_xdbe = true;
> +    }
> +#endif

No need for braces here, since we now require a C99 compiler.

> diff --git a/src/xterm.h b/src/xterm.h
> index 675a484..cb1aa1d 100644
> --- a/src/xterm.h
> +++ b/src/xterm.h
> @@ -475,6 +475,10 @@ struct x_display_info
>   #ifdef USE_XCB
>     xcb_connection_t *xcb_connection;
>   #endif
> +
> +#ifdef HAVE_XDBE
> +  bool supports_xdbe;
> +#endif
>   };
> 
>   #ifdef HAVE_X_I18N
> @@ -527,6 +531,17 @@ struct x_output
>        and the X window has not yet been created.  */
>     Window window_desc;
> 
> +#ifdef HAVE_XDBE
> +  /* The drawable to which we're rendering.  In the single-buffered
> +     base, the window itself.  In the double-buffered case, the
> +     window's back buffer.  */
> +  Drawable draw_desc;
> +
> +  /* Set to true when we need a buffer flip.  We do a buffer flip only
> +     at the end of redisplay in order to minimize flicker.  */
> +  bool need_buffer_flip;
> +#endif

We had bad experience with conditionally compiled struct members.  Is
it possible to have these members always defined, and set them to some
no-op values when XDBE is not supported?  (In general, run-time tests
should IMO always be preferred to compile-time tests, as the former
make extensions and future development easier.)

> +/* Return the drawable used for rendering to frame F.  */
> +#ifdef HAVE_XDBE
> +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc)
> +#else
> +#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f)))
                               ^^^^^^^^^^^^^^^^^^^^^^^^

Why such a strange definition?  Won't it cause compiler warnings in
some cases, like when it's used as an lvalue?  We use some quite
paranoid warning switches on master.

If there are problems to have a simple definition for this macro in
the non-XDBE case, I'd prefer to have inline function(s) for the
problematic cases (e.g., comparison), rather than a tricky macro.

Thanks again for working on this.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21  8:49 ` Eli Zaretskii
@ 2016-10-21 11:04   ` Daniel Colascione
  2016-10-21 17:43     ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel Colascione @ 2016-10-21 11:04 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

On 10/21/2016 01:49 AM, Eli Zaretskii wrote:
 >> From: Daniel Colascione <dancol@dancol.org>
 >> Date: Thu, 20 Oct 2016 18:32:01 -0700
 >>
 >> This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to
 >> avoid showing the user incomplete drawing results. Without this 
patch, I
 >> can make Emacs flicker like crazy by running isearch for a piece of 
text
 >> unique in a buffer and holding down C-s. With this patch, Emacs does 
not
 >> flicker no matter what I do to it.
 >>
 >> The patch also stops flickering that occurs when using the "solid
 >> resizing" feature of some window managers --- i.e., when the WM redraws
 >> windows as the user drags their edges, as opposed to displaying some
 >> kind of bounding-box in lieu of the actual window contents.
 >
 > Thanks!

Thanks for taking a look.

 >
 >> * We do a buffer flip at the end of redisplay instead of in
 >> x_update_end() so the user never sees the completely-cleared state that
 >> we enter immediately after clear_garbaged_frames(). x_update_end() does
 >> do a buffer flip if it's called outside redisplay. I've added a new
 >> terminal hook to support this hack.
 >
 > What happens in the case where redisplay_internal did its job, but
 > update_frame was interrupted by incoming input?  Won't that flip the
 > buffers unnecessarily?
 >
 > I have some comments below.  Apologies if some of them stem from my
 > relative ignorance of the issues involved in this changeset.
 >
 >> commit 15fdd8f63533201f05627ede634a8f5ae4757d7e
 >> Author: Daniel Colascione <dancol@dancol.org>
 >> Date:   Thu Oct 20 16:50:54 2016 -0700
 >>
 >>      Add double-buffered output support to Emacs
 >
 > Please add ChangeLog style commit log entries about each new and
 > modified function/macro.

Oh, of course. I figured it was overkill for the first version. I'll add 
the usual ceremonial bits for the next version

 >
 > Please also provide a NEWS entry about the new feature, in the
 > "Installation Changes" section.
 >
 >> +AC_SUBST(XDBE_CFLAGS)
 >> +AC_SUBST(XDBE_LIBS)
 >
 > Do we need XDBE_CFLAGS?  They seem to be unused?

Symmetry with the other extension clauses?

 > Also, I think the fact that Xdbe extensions are used should be shown
 > in the summary displayed by the configure script when it finishes, and
 > XDBE should be added to the list in config_emacs_features, so that it
 > appears in EMACS_CONFIG_FEATURES when appropriate.

I thought about that, but didn't want to overwhelm users with noise. 
I'll add some more user visibility here.

The other question I had for myself is whether it makes sense to let 
users enable and disable this functionality at runtime: it's hard to 
think of a legitimate use case that doesn't involve a bug somewhere, and 
I'd rather fix the bugs.

 >
 >> diff --git a/src/dispnew.c b/src/dispnew.c
 >> index 70d4de0..8f81cee 100644
 >> --- a/src/dispnew.c
 >> +++ b/src/dispnew.c
 >> @@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f)
 >>   {
 >>     /* Error if F has no glyphs.  */
 >>     eassert (f->glyphs_initialized_p);
 >> +  font_flush_frame_caches (f);
 >>     update_begin (f);
 >>     if (FRAME_MSDOS_P (f))
 >>       FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f));
 >> diff --git a/src/font.c b/src/font.c
 >> index f8e6794..033995e 100644
 >> --- a/src/font.c
 >> +++ b/src/font.c
 >> @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action,
 >> Lisp_Object arg, Lisp_Object result)
 >>   }
 >>
 >>   void
 >> +font_flush_frame_caches (struct frame *f)
 >> +{
 >> +  struct font_driver_list *list;
 >> +
 >> +  for (list = f->font_driver_list; list; list = list->next)
 >> +    if (list->on && list->driver->flush_frame_caches)
 >> +      list->driver->flush_frame_caches (f);
 >> +}
 >
 > Why do we need this flushing?  Is it relevant to the patch, and if so
 > why?
 >
 > I'm asking because we had some bad effects with some fonts due to
 > flushing the frame's font caches, see bugs 24634 and 24565, for
 > example, so my bother is that this will reintroduce those problems.

Maybe I need to come up with a better name for what this hook does. It's 
specific to the XftDraw surface issue I mentioned in the preamble to the 
patch: without this code, rendering of text stops working (text 
rendering silently fails to happen) after a frame resize.

We're not flushing the caches implicated in bigs 24634 and 24565. I'll 
add some comments explaining what's going on.

I suspect it's a bad interaction between XRenderCreatePicture (which Xft 
uses internally) and the behind-the-scenes pixmap adjustment that DBE is 
doing that drives us to this hack.

 > Also, the call to font_flush_frame_caches is unconditional, although
 > only one font back-end supports it.  That seems to incur a gratuitous
 > overhead of a function call for the other font back-ends.

We're testing whether the hook function is non-NULL before calling into 
it, so only that one backend gets the call. In any case, the overhead is 
trivial --- it's one indirect function call compared to all of 
redisplay. (Every call into a shared library is the same indirect jump.)

 > In any case, please add a commentary to each function you add with a
 > summary of what it does.
 >
 >> @@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f)
 >>        by callers of this function.  */
 >>     gtk_widget_realize (wfixed);
 >>     FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
 >> +  set_up_x_back_buffer (f);
 >
 > There's some strange misalignment of indentation here (and in a few
 > other places).

I blame my email client.

 >> +  FOR_EACH_FRAME (tail, frame)
 >> +    {
 >> +      struct frame *f = XFRAME (frame);
 >> +      if (FRAME_TERMINAL (f)->redisplay_end_hook)
 >> +        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
 >> +    }
 >
 > This will call the hook for each frame, every redisplay cycle.  By
 > contrast, redisplay_internal many times updates only the selected
 > window of the selected frame.  Is calling the hook for all the frames
 > really needed, or should we only call it for the selected frame in the
 > latter case, or maybe just for frames that got updated?

The hook only does something in the case where someone called update_end 
and we demurred on actually flipping the buffer because we knew we were 
in redisplay and would be getting redisplay_end_hook shortly. That is, 
if we update only one frame, we're only going to do one buffer flip.

Or are you worried about the function call overhead? That, as I 
mentioned above, is trivial.

 > Also, should
 > we distinguish between visible and iconified frames?

If we do, we should do it in the code that performs the updates, not the 
code (the snippet immediately above) that publishes the updates we've 
already done.

 >
 >> +#ifdef HAVE_XDBE
 >> +  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
 >> +    {
 >> +      /* If allocating a back buffer fails, just use single-buffered
 >> +         rendering.  */
 >> +      x_sync (f);
 >> +      x_catch_errors (FRAME_X_DISPLAY (f));
 >> +      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
 >> +        FRAME_X_DISPLAY (f),
 >> +        FRAME_X_WINDOW (f),
 >> +        XdbeCopied);
 >> +      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
 >> +        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
 >
 > Shouldn't we turn off the supports_xdbe flag in the case of failure?

supports_xdbe is whether XDBE is supported on a connection at all. What 
if XdbeAllocateBackBufferName fails transiently?


 >> +#ifdef HAVE_XDBE
 >> +  dpyinfo->supports_xdbe = false;
 >> +    {
 >> +      int xdbe_major;
 >> +      int xdbe_minor;
 >> +      if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, 
&xdbe_minor))
 >> +        dpyinfo->supports_xdbe = true;
 >> +    }
 >> +#endif
 >
 > No need for braces here, since we now require a C99 compiler.

If we put xdbe_major and xdbe_minor at function level, the names leak 
into function scope. With braces, they exist only around the call to 
XdbeQueryExtension

 >
 >> diff --git a/src/xterm.h b/src/xterm.h
 >> index 675a484..cb1aa1d 100644
 >> --- a/src/xterm.h
 >> +++ b/src/xterm.h
 >> @@ -475,6 +475,10 @@ struct x_display_info
 >>   #ifdef USE_XCB
 >>     xcb_connection_t *xcb_connection;
 >>   #endif
 >> +
 >> +#ifdef HAVE_XDBE
 >> +  bool supports_xdbe;
 >> +#endif
 >>   };
 >>
 >>   #ifdef HAVE_X_I18N
 >> @@ -527,6 +531,17 @@ struct x_output
 >>        and the X window has not yet been created.  */
 >>     Window window_desc;
 >>
 >> +#ifdef HAVE_XDBE
 >> +  /* The drawable to which we're rendering.  In the single-buffered
 >> +     base, the window itself.  In the double-buffered case, the
 >> +     window's back buffer.  */
 >> +  Drawable draw_desc;
 >> +
 >> +  /* Set to true when we need a buffer flip.  We do a buffer flip only
 >> +     at the end of redisplay in order to minimize flicker.  */
 >> +  bool need_buffer_flip;
 >> +#endif
 >
 > We had bad experience with conditionally compiled struct members.  Is
 > it possible to have these members always defined, and set them to some
 > no-op values when XDBE is not supported?  (In general, run-time tests
 > should IMO always be preferred to compile-time tests, as the former
 > make extensions and future development easier.)

I was just reluctant to further bloat the structure. Making these 
members exist unconditionally is fine.

 >
 >> +/* Return the drawable used for rendering to frame F.  */
 >> +#ifdef HAVE_XDBE
 >> +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc)
 >> +#else
 >> +#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f)))
 >                                ^^^^^^^^^^^^^^^^^^^^^^^^
 >
 > Why such a strange definition?  Won't it cause compiler warnings in
 > some cases, like when it's used as an lvalue?  We use some quite
 > paranoid warning switches on master.

The point is to make the build fail in the non-XDBE case if you try to 
use it as an lvalue. If draw_desc is going to actually exist in the 
structure in all cases, the strange definition is moot, since we'll 
always use the other one.




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21 11:04   ` Daniel Colascione
@ 2016-10-21 17:43     ` Eli Zaretskii
  2016-10-21 18:27       ` Daniel Colascione
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2016-10-21 17:43 UTC (permalink / raw)
  To: Daniel Colascione; +Cc: emacs-devel

> Cc: emacs-devel@gnu.org
> From: Daniel Colascione <dancol@dancol.org>
> Date: Fri, 21 Oct 2016 04:04:21 -0700
> 
>  > Also, the call to font_flush_frame_caches is unconditional, although
>  > only one font back-end supports it.  That seems to incur a gratuitous
>  > overhead of a function call for the other font back-ends.
> 
> We're testing whether the hook function is non-NULL before calling into 
> it, so only that one backend gets the call. In any case, the overhead is 
> trivial --- it's one indirect function call compared to all of 
> redisplay. (Every call into a shared library is the same indirect jump.)

The code is more clear with the test before the call, otherwise the
code reader needs to look in the hook to understand when it's a no-op.
So I'd prefer to have the test outside of the call, or even outside of
the loop.

>  >> +  FOR_EACH_FRAME (tail, frame)
>  >> +    {
>  >> +      struct frame *f = XFRAME (frame);
>  >> +      if (FRAME_TERMINAL (f)->redisplay_end_hook)
>  >> +        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
>  >> +    }
>  >
>  > This will call the hook for each frame, every redisplay cycle.  By
>  > contrast, redisplay_internal many times updates only the selected
>  > window of the selected frame.  Is calling the hook for all the frames
>  > really needed, or should we only call it for the selected frame in the
>  > latter case, or maybe just for frames that got updated?
> 
> The hook only does something in the case where someone called update_end 
> and we demurred on actually flipping the buffer because we knew we were 
> in redisplay and would be getting redisplay_end_hook shortly. That is, 
> if we update only one frame, we're only going to do one buffer flip.

That might be so, but what you say above is in no way apparent from
looking at the code.  IME, it is very important to have the idea when
something is being done and when not just by looking at the code in
redisplay_internal; having to find that out by realizing that some
flag is set in update_end and then tested in the hook makes the code
more subtle and its maintenance harder.  It's not like keeping this
detail from redisplay_internal makes this detail local to some
functions, or somesuch, so there's really no reason to conceal it,
IMO.

> Or are you worried about the function call overhead? That, as I 
> mentioned above, is trivial.

No, I worry about maintainability of the display code and about
lowering the risk of bugs introduced due to such subtleties.

>  > Also, should
>  > we distinguish between visible and iconified frames?
> 
> If we do, we should do it in the code that performs the updates, not the 
> code (the snippet immediately above) that publishes the updates we've 
> already done.

See above: I don't like such dependencies and find them in general an
obstacle to understanding the overall logic of the display code.  I
don't mind adding a test in update_frame and friends, but that
shouldn't prevent us from having a similar (or even identical) test
here.

>  >> +#ifdef HAVE_XDBE
>  >> +  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
>  >> +    {
>  >> +      /* If allocating a back buffer fails, just use single-buffered
>  >> +         rendering.  */
>  >> +      x_sync (f);
>  >> +      x_catch_errors (FRAME_X_DISPLAY (f));
>  >> +      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
>  >> +        FRAME_X_DISPLAY (f),
>  >> +        FRAME_X_WINDOW (f),
>  >> +        XdbeCopied);
>  >> +      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
>  >> +        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
>  >
>  > Shouldn't we turn off the supports_xdbe flag in the case of failure?
> 
> supports_xdbe is whether XDBE is supported on a connection at all. What 
> if XdbeAllocateBackBufferName fails transiently?

How can it fail transiently?  And why turning off supports_xdbe in
that case would mean trouble?

>  >> +#ifdef HAVE_XDBE
>  >> +  dpyinfo->supports_xdbe = false;
>  >> +    {
>  >> +      int xdbe_major;
>  >> +      int xdbe_minor;
>  >> +      if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, 
> &xdbe_minor))
>  >> +        dpyinfo->supports_xdbe = true;
>  >> +    }
>  >> +#endif
>  >
>  > No need for braces here, since we now require a C99 compiler.
> 
> If we put xdbe_major and xdbe_minor at function level, the names leak 
> into function scope. With braces, they exist only around the call to 
> XdbeQueryExtension

We use that in many other places, so I think these precautions are
misguided and generally make our coding style less apparent.

Thanks.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21 17:43     ` Eli Zaretskii
@ 2016-10-21 18:27       ` Daniel Colascione
  2016-10-21 19:27         ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel Colascione @ 2016-10-21 18:27 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> Cc: emacs-devel@gnu.org
>> From: Daniel Colascione <dancol@dancol.org>
>> Date: Fri, 21 Oct 2016 04:04:21 -0700
>> 
>>  > Also, the call to font_flush_frame_caches is unconditional, although
>>  > only one font back-end supports it.  That seems to incur a gratuitous
>>  > overhead of a function call for the other font back-ends.
>> 
>> We're testing whether the hook function is non-NULL before calling into 
>> it, so only that one backend gets the call. In any case, the overhead is 
>> trivial --- it's one indirect function call compared to all of 
>> redisplay. (Every call into a shared library is the same indirect jump.)
>
> The code is more clear with the test before the call, otherwise the
> code reader needs to look in the hook to understand when it's a no-op.

But the test (of whether the hook exists) _is_ before the call. What are
you proposing?

    some_backend_needs_flush = False
    for backend in font_back_ends:
      if has_flush_hook(backend):
        some_back_end_needs_flush = True
    if some_back_end_needs_flush:
      for backend in font_back_ends:
        if has_flush_hook(backend):
          backend.flush_hook()
    
That's silly. Can you elaborate on what you consider a cleaner approach?

> So I'd prefer to have the test outside of the call, or even outside of
> the loop.

See below. I don't think that giving font back-ends an opportunity to do
_something_ in response to a frame being garbaged in certain ways is a
bad thing.

>>  >> +  FOR_EACH_FRAME (tail, frame)
>>  >> +    {
>>  >> +      struct frame *f = XFRAME (frame);
>>  >> +      if (FRAME_TERMINAL (f)->redisplay_end_hook)
>>  >> +        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
>>  >> +    }
>>  >
>>  > This will call the hook for each frame, every redisplay cycle.  By
>>  > contrast, redisplay_internal many times updates only the selected
>>  > window of the selected frame.  Is calling the hook for all the frames
>>  > really needed, or should we only call it for the selected frame in the
>>  > latter case, or maybe just for frames that got updated?
>> 
>> The hook only does something in the case where someone called update_end 
>> and we demurred on actually flipping the buffer because we knew we were 
>> in redisplay and would be getting redisplay_end_hook shortly. That is, 
>> if we update only one frame, we're only going to do one buffer flip.
>
> That might be so, but what you say above is in no way apparent from
> looking at the code.  IME, it is very important to have the idea when
> something is being done and when not just by looking at the code in
> redisplay_internal; having to find that out by realizing that some
> flag is set in update_end and then tested in the hook makes the code
> more subtle and its maintenance harder.  It's not like keeping this
> detail from redisplay_internal makes this detail local to some
> functions, or somesuch, so there's really no reason to conceal it,
> IMO.

This sentiment strikes me as being analogous to the old "no use of hooks
in Emacs internals" line --- yet we have facilities like syntax-ppss
that rely on hooks in core, and it's worked out fine.

We need to do a buffer flip at the end of redisplay for each frame on
which update_end was called during redisplay.  If someone calls
update_end _outside_ redisplay, we should do a buffer flip
immediately. The code I've sent is the cleanest way of implementing this
model short of changing how update_begin and update_end work.

I think thhat what you're proposing is a layering violation. It will
make maintenance harder. The only facility that cares about the
has-a-frame-been-updated state is the X11 double-buffered back-end, so
making xdisp track this state makes everything more complicated,
especially because xdisp already has a "needs redisplay" flag and
shouldn't need to keep track of a separate "needs buffer flip" flag.
It shouldn't have to care. That's not its job.

>> Or are you worried about the function call overhead? That, as I 
>> mentioned above, is trivial.
>
> No, I worry about maintainability of the display code and about
> lowering the risk of bugs introduced due to such subtleties.
>

I'm also worried about maintainability: that's why I don't want to make
redisplay_internal any more of a big ball of mud than it already is. I
think it's cleaner to have xterm keep track of state only xterm needs.

>>  > Also, should
>>  > we distinguish between visible and iconified frames?
>> 
>> If we do, we should do it in the code that performs the updates, not the 
>> code (the snippet immediately above) that publishes the updates we've 
>> already done.
>
> See above: I don't like such dependencies and find them in general an
> obstacle to understanding the overall logic of the display code.  I
> don't mind adding a test in update_frame and friends, but that
> shouldn't prevent us from having a similar (or even identical) test
> here.

What dependency? You're proposing adding a lot of complexity to the loop
that calls redisplay_end_hook, and I still have no idea what this
complexity is supposed to accomplish.

>
>>  >> +#ifdef HAVE_XDBE
>>  >> +  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
>>  >> +    {
>>  >> +      /* If allocating a back buffer fails, just use single-buffered
>>  >> +         rendering.  */
>>  >> +      x_sync (f);
>>  >> +      x_catch_errors (FRAME_X_DISPLAY (f));
>>  >> +      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
>>  >> +        FRAME_X_DISPLAY (f),
>>  >> +        FRAME_X_WINDOW (f),
>>  >> +        XdbeCopied);
>>  >> +      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
>>  >> +        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
>>  >
>>  > Shouldn't we turn off the supports_xdbe flag in the case of failure?
>> 
>> supports_xdbe is whether XDBE is supported on a connection at all. What 
>> if XdbeAllocateBackBufferName fails transiently?
>
> How can it fail transiently?  And why turning off supports_xdbe in
> that case would mean trouble?

It's an allocation. Allocations can fail. And XDBE isn't guaranteed to
work for all visuals.

>
>>  >> +#ifdef HAVE_XDBE
>>  >> +  dpyinfo->supports_xdbe = false;
>>  >> +    {
>>  >> +      int xdbe_major;
>>  >> +      int xdbe_minor;
>>  >> +      if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, 
>> &xdbe_minor))
>>  >> +        dpyinfo->supports_xdbe = true;
>>  >> +    }
>>  >> +#endif
>>  >
>>  > No need for braces here, since we now require a C99 compiler.
>> 
>> If we put xdbe_major and xdbe_minor at function level, the names leak 
>> into function scope. With braces, they exist only around the call to 
>> XdbeQueryExtension
>
> We use that in many other places, so I think these precautions are
> misguided and generally make our coding style less apparent.

Fine.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21 18:27       ` Daniel Colascione
@ 2016-10-21 19:27         ` Eli Zaretskii
  2016-10-23 20:51           ` Daniel Colascione
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2016-10-21 19:27 UTC (permalink / raw)
  To: Daniel Colascione; +Cc: emacs-devel

> From: Daniel Colascione <dancol@dancol.org>
> Cc: emacs-devel@gnu.org
> Date: Fri, 21 Oct 2016 11:27:29 -0700
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> >> Cc: emacs-devel@gnu.org
> >> From: Daniel Colascione <dancol@dancol.org>
> >> Date: Fri, 21 Oct 2016 04:04:21 -0700
> >> 
> >>  > Also, the call to font_flush_frame_caches is unconditional, although
> >>  > only one font back-end supports it.  That seems to incur a gratuitous
> >>  > overhead of a function call for the other font back-ends.
> >> 
> >> We're testing whether the hook function is non-NULL before calling into 
> >> it, so only that one backend gets the call. In any case, the overhead is 
> >> trivial --- it's one indirect function call compared to all of 
> >> redisplay. (Every call into a shared library is the same indirect jump.)
> >
> > The code is more clear with the test before the call, otherwise the
> > code reader needs to look in the hook to understand when it's a no-op.
> 
> But the test (of whether the hook exists) _is_ before the call. What are
> you proposing?
> 
>     some_backend_needs_flush = False
>     for backend in font_back_ends:
>       if has_flush_hook(backend):
>         some_back_end_needs_flush = True
>     if some_back_end_needs_flush:
>       for backend in font_back_ends:
>         if has_flush_hook(backend):
>           backend.flush_hook()
>     
> That's silly.

Let's assume that neither of us makes silly proposals, okay?

> Can you elaborate on what you consider a cleaner approach?

I meant the test for the lone font backend that needs this kludge, not
for the existence of the method.  And also a comment that tells it's a
kludge whose need we understand only empirically.  I'd even prefer a
direct call to the function when the font backend is the one which
needs it, rather than having a method created for the benefit of a
single backend, whose necessity we don't really understand.

When I read the code which tests whether a backend has a method, I
need to look up the methods of the backend(s) I'm interested in to
know whether they have such a method.  When a method is defined for a
single backend, most of those searches will produce a trivial result,
i.e. a waste of my time.  I prefer that we make this evident at the
place of the call instead.

> >>  >> +  FOR_EACH_FRAME (tail, frame)
> >>  >> +    {
> >>  >> +      struct frame *f = XFRAME (frame);
> >>  >> +      if (FRAME_TERMINAL (f)->redisplay_end_hook)
> >>  >> +        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
> >>  >> +    }
> >>  >
> >>  > This will call the hook for each frame, every redisplay cycle.  By
> >>  > contrast, redisplay_internal many times updates only the selected
> >>  > window of the selected frame.  Is calling the hook for all the frames
> >>  > really needed, or should we only call it for the selected frame in the
> >>  > latter case, or maybe just for frames that got updated?
> >> 
> >> The hook only does something in the case where someone called update_end 
> >> and we demurred on actually flipping the buffer because we knew we were 
> >> in redisplay and would be getting redisplay_end_hook shortly. That is, 
> >> if we update only one frame, we're only going to do one buffer flip.
> >
> > That might be so, but what you say above is in no way apparent from
> > looking at the code.  IME, it is very important to have the idea when
> > something is being done and when not just by looking at the code in
> > redisplay_internal; having to find that out by realizing that some
> > flag is set in update_end and then tested in the hook makes the code
> > more subtle and its maintenance harder.  It's not like keeping this
> > detail from redisplay_internal makes this detail local to some
> > functions, or somesuch, so there's really no reason to conceal it,
> > IMO.
> 
> This sentiment strikes me as being analogous to the old "no use of hooks
> in Emacs internals" line --- yet we have facilities like syntax-ppss
> that rely on hooks in core, and it's worked out fine.

I'm not saying the code won't work.  I'm saying it could make its
purpose more evident, which is better for long-term maintainability.

> We need to do a buffer flip at the end of redisplay for each frame on
> which update_end was called during redisplay.

Even if update_end was called for a frame whose update was "paused",
i.e. whose redisplay was interrupted by incoming input?

> If someone calls update_end _outside_ redisplay, we should do a
> buffer flip immediately. The code I've sent is the cleanest way of
> implementing this model short of changing how update_begin and
> update_end work.

I'm asking whether flipping the buffer for a frame that wasn't updated
or was updated partially can do some harm, like produce incorrect
display.

> I think thhat what you're proposing is a layering violation. It will
> make maintenance harder. The only facility that cares about the
> has-a-frame-been-updated state is the X11 double-buffered back-end,

My problems start with the name of the hook, which doesn't hint at all
that only double-buffered X11 back-end cares about that.  If the hook
was called something like double_buffer_flip_hook, or was compiled
only if HAVE_XDBE is defined, this issue would go away.

> making xdisp track this state makes everything more complicated,
> especially because xdisp already has a "needs redisplay" flag and
> shouldn't need to keep track of a separate "needs buffer flip" flag.
> It shouldn't have to care. That's not its job.

I don't know why you decided that it isn't xdisp.c's job.  It already
has a lot of flags that other parts set to get xdisp.c do or not do
certain parts of its job.  Why not let it test a flag here as well?
Such a test will tell the reader that the call to the hook is only
done when a flag (with hopefully some descriptive name) is set, so the
reader can understand what the hook is doing and under what
circumstances without actually looking at the hook, which involves
first finding out which frame types support it, and what exactly does
it do.

IOW, if the code speaks for itself, it makes maintenance of this
hyper-convoluted piece of Emacs easier, less time consuming, and less
error prone.

> >> Or are you worried about the function call overhead? That, as I 
> >> mentioned above, is trivial.
> >
> > No, I worry about maintainability of the display code and about
> > lowering the risk of bugs introduced due to such subtleties.
> >
> 
> I'm also worried about maintainability: that's why I don't want to make
> redisplay_internal any more of a big ball of mud than it already is.

It's too late for that.  It's already an extremely complex and hard to
understand piece of code.  Hiding more information from its logic
makes the code harder to maintain, not easier.  We are not talking
about highly-modular package, where the logic is kept local to each
module, and the interfaces between them are kept to the absolute
minimum.  xdisp.c _knows_ a lot about how xterm.c and xfns.c work.
You cannot disengage them without a complete rewrite.

So the principles you are trying to apply, while a good idea in
general, in this particular case make code harder to understand and
develop.

Of course, it's not a catastrophe, so if you are going to go to the
barricades over this, I won't fight you.  I just hope you take my gray
hair from many readings of redisplay_internal as some evidence that I
know what I'm talking about.  The number of times I needed to go deep
into the called functions just to realize that the code does something
completely different from what it looked on the redisplay_internal
level is beyond imagination.

> >>  >> +#ifdef HAVE_XDBE
> >>  >> +  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
> >>  >> +    {
> >>  >> +      /* If allocating a back buffer fails, just use single-buffered
> >>  >> +         rendering.  */
> >>  >> +      x_sync (f);
> >>  >> +      x_catch_errors (FRAME_X_DISPLAY (f));
> >>  >> +      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
> >>  >> +        FRAME_X_DISPLAY (f),
> >>  >> +        FRAME_X_WINDOW (f),
> >>  >> +        XdbeCopied);
> >>  >> +      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
> >>  >> +        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
> >>  >
> >>  > Shouldn't we turn off the supports_xdbe flag in the case of failure?
> >> 
> >> supports_xdbe is whether XDBE is supported on a connection at all. What 
> >> if XdbeAllocateBackBufferName fails transiently?
> >
> > How can it fail transiently?  And why turning off supports_xdbe in
> > that case would mean trouble?
> 
> It's an allocation. Allocations can fail. And XDBE isn't guaranteed to
> work for all visuals.

How much memory is being allocated, and correspondingly what is the
probability this allocation could fail on a healthy system?

And what about my second question?

The downside of your proposed code is that we will try to turn on XDBE
each time hence, and each attempt means a potentially costly series of
calls to catch X errors, and the XdbeAllocateBackBufferName itself.
I'm asking whether it wouldn't be better to simply give up on XDBE in
such a case for that frame.  It could be much cheaper, no?  Just a
thought.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-21 19:27         ` Eli Zaretskii
@ 2016-10-23 20:51           ` Daniel Colascione
  2016-10-24  8:05             ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel Colascione @ 2016-10-23 20:51 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Daniel Colascione <dancol@dancol.org>
>> Cc: emacs-devel@gnu.org
>> Date: Fri, 21 Oct 2016 11:27:29 -0700
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> >> Cc: emacs-devel@gnu.org
>> >> From: Daniel Colascione <dancol@dancol.org>
>> >> Date: Fri, 21 Oct 2016 04:04:21 -0700
>> >> 
>> >>  > Also, the call to font_flush_frame_caches is unconditional, although
>> >>  > only one font back-end supports it.  That seems to incur a gratuitous
>> >>  > overhead of a function call for the other font back-ends.
>> >> 
>> >> We're testing whether the hook function is non-NULL before calling into 
>> >> it, so only that one backend gets the call. In any case, the overhead is 
>> >> trivial --- it's one indirect function call compared to all of 
>> >> redisplay. (Every call into a shared library is the same indirect jump.)
>> >
>> > The code is more clear with the test before the call, otherwise the
>> > code reader needs to look in the hook to understand when it's a no-op.
>> 
>> But the test (of whether the hook exists) _is_ before the call. What are
>> you proposing?
>> 
>>     some_backend_needs_flush = False
>>     for backend in font_back_ends:
>>       if has_flush_hook(backend):
>>         some_back_end_needs_flush = True
>>     if some_back_end_needs_flush:
>>       for backend in font_back_ends:
>>         if has_flush_hook(backend):
>>           backend.flush_hook()
>>     
>> That's silly.
>
> Let's assume that neither of us makes silly proposals, okay?

Sorry --- I meant the comment in a more syllogistic way:

  1. I understand the proposal to be silly
  2. We don't make silly proposals

  Therefore, my understanding is flawed

>> Can you elaborate on what you consider a cleaner approach?
>
> I meant the test for the lone font backend that needs this kludge, not
> for the existence of the method.  And also a comment that tells it's a
> kludge whose need we understand only empirically.  I'd even prefer a
> direct call to the function when the font backend is the one which
> needs it, rather than having a method created for the benefit of a
> single backend, whose necessity we don't really understand.

So we want to write?

  for font_backend in frame.font_backends:
    if instanceof(font_backend, XftBackend):
      xft_refresh_hack()

That still strikes me as less clean, especially considering we'll have
to provide the xft_refresh_hack to general-purpose code.

> When I read the code which tests whether a backend has a method, I
> need to look up the methods of the backend(s) I'm interested in to
> know whether they have such a method.  When a method is defined for a
> single backend, most of those searches will produce a trivial result,
> i.e. a waste of my time.  I prefer that we make this evident at the
> place of the call instead.
>
>> >>  >> +  FOR_EACH_FRAME (tail, frame)
>> >>  >> +    {
>> >>  >> +      struct frame *f = XFRAME (frame);
>> >>  >> +      if (FRAME_TERMINAL (f)->redisplay_end_hook)
>> >>  >> +        (*FRAME_TERMINAL (f)->redisplay_end_hook) (f);
>> >>  >> +    }
>> >>  >
>> >>  > This will call the hook for each frame, every redisplay cycle.  By
>> >>  > contrast, redisplay_internal many times updates only the selected
>> >>  > window of the selected frame.  Is calling the hook for all the frames
>> >>  > really needed, or should we only call it for the selected frame in the
>> >>  > latter case, or maybe just for frames that got updated?
>> >> 
>> >> The hook only does something in the case where someone called update_end 
>> >> and we demurred on actually flipping the buffer because we knew we were 
>> >> in redisplay and would be getting redisplay_end_hook shortly. That is, 
>> >> if we update only one frame, we're only going to do one buffer flip.
>> >
>> > That might be so, but what you say above is in no way apparent from
>> > looking at the code.  IME, it is very important to have the idea when
>> > something is being done and when not just by looking at the code in
>> > redisplay_internal; having to find that out by realizing that some
>> > flag is set in update_end and then tested in the hook makes the code
>> > more subtle and its maintenance harder.  It's not like keeping this
>> > detail from redisplay_internal makes this detail local to some
>> > functions, or somesuch, so there's really no reason to conceal it,
>> > IMO.
>> 
>> This sentiment strikes me as being analogous to the old "no use of hooks
>> in Emacs internals" line --- yet we have facilities like syntax-ppss
>> that rely on hooks in core, and it's worked out fine.
>
> I'm not saying the code won't work.  I'm saying it could make its
> purpose more evident, which is better for long-term maintainability.

Longer-term, I think we need to move toward greater, not
lesser, modularity. With some explanatory comments,

Besides, there's no reason other window systems can't use this
hook. Looking briefly at ns_update_end, for example, I see that we call
flushWindow; we shouldn't have to do that until redisplay_end_hook.

>> We need to do a buffer flip at the end of redisplay for each frame on
>> which update_end was called during redisplay.
>
> Even if update_end was called for a frame whose update was "paused",
> i.e. whose redisplay was interrupted by incoming input?

Right now, we do a buffer flip unconditionally except in the case that
we `goto end_of_redisplay`. I think that's a bug actually --- but I have
to understand more of what this logic is actually doing.

Why _do_ we have a path that short-circuits the rest of the redisplay
code? What would happen if we just removed it? It appears to be some
kind of optimization, and I'm not sure it's actually necessary
(especially since, according to the comment, we disable it anyway in the
case of a blinking cursor).

I suppose you could make an argument for not doing a buffer flip if
redisplay is interrupted by input, but I'm honestly not sure what might
go wrong there, and my preference would be to act as much like the
single-buffered case as possible and default, if we're unsure, to
showing the results of our painting efforts to the user.

>> If someone calls update_end _outside_ redisplay, we should do a
>> buffer flip immediately. The code I've sent is the cleanest way of
>> implementing this model short of changing how update_begin and
>> update_end work.
>
> I'm asking whether flipping the buffer for a frame that wasn't updated
> or was updated partially can do some harm, like produce incorrect
> display.

Probably, but I don't think it can be worse than what we currently
have. I'm trying to be conservative and hold as few updates in the back
buffer as possible.  The only reason I added the end_of_redisplay stuff
is that the resulting flicker reduction in the window-resize case is so
dramatic that I think it's worth the extra complexity.

>> I think thhat what you're proposing is a layering violation. It will
>> make maintenance harder. The only facility that cares about the
>> has-a-frame-been-updated state is the X11 double-buffered back-end,
>
> My problems start with the name of the hook, which doesn't hint at all
> that only double-buffered X11 back-end cares about that.  If the hook
> was called something like double_buffer_flip_hook, or was compiled
> only if HAVE_XDBE is defined, this issue would go away.

So let's call it double_buffer_flip_hook.

>> making xdisp track this state makes everything more complicated,
>> especially because xdisp already has a "needs redisplay" flag and
>> shouldn't need to keep track of a separate "needs buffer flip" flag.
>> It shouldn't have to care. That's not its job.
>
> I don't know why you decided that it isn't xdisp.c's job.  It already
> has a lot of flags that other parts set to get xdisp.c do or not do
> certain parts of its job.  Why not let it test a flag here as well?
> Such a test will tell the reader that the call to the hook is only
> done when a flag (with hopefully some descriptive name) is set, so the
> reader can understand what the hook is doing and under what
> circumstances without actually looking at the hook, which involves
> first finding out which frame types support it, and what exactly does
> it do.
>
> IOW, if the code speaks for itself, it makes maintenance of this
> hyper-convoluted piece of Emacs easier, less time consuming, and less
> error prone.

This code is an adventure.

>> >> Or are you worried about the function call overhead? That, as I 
>> >> mentioned above, is trivial.
>> >
>> > No, I worry about maintainability of the display code and about
>> > lowering the risk of bugs introduced due to such subtleties.
>> >
>> 
>> I'm also worried about maintainability: that's why I don't want to make
>> redisplay_internal any more of a big ball of mud than it already is.
>
> It's too late for that.  It's already an extremely complex and hard to
> understand piece of code.  Hiding more information from its logic
> makes the code harder to maintain, not easier.  We are not talking
> about highly-modular package, where the logic is kept local to each
> module, and the interfaces between them are kept to the absolute
> minimum.  xdisp.c _knows_ a lot about how xterm.c and xfns.c work.
> You cannot disengage them without a complete rewrite.

Well, it also works with w32term and nsterm, so the coupling can't be
complete. :-)

> So the principles you are trying to apply, while a good idea in
> general, in this particular case make code harder to understand and
> develop.
>
> Of course, it's not a catastrophe, so if you are going to go to the
> barricades over this, I won't fight you.  I just hope you take my gray
> hair from many readings of redisplay_internal as some evidence that I
> know what I'm talking about.  The number of times I needed to go deep
> into the called functions just to realize that the code does something
> completely different from what it looked on the redisplay_internal
> level is beyond imagination.

I'm beginning to see your point. I just don't agree that the problem is
intractable. We're going to need to clean this stuff up, especially if
(in the distant future) we want to support multiple window systems in
the same Emacs build.

>
>> >>  >> +#ifdef HAVE_XDBE
>> >>  >> +  if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
>> >>  >> +    {
>> >>  >> +      /* If allocating a back buffer fails, just use single-buffered
>> >>  >> +         rendering.  */
>> >>  >> +      x_sync (f);
>> >>  >> +      x_catch_errors (FRAME_X_DISPLAY (f));
>> >>  >> +      FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
>> >>  >> +        FRAME_X_DISPLAY (f),
>> >>  >> +        FRAME_X_WINDOW (f),
>> >>  >> +        XdbeCopied);
>> >>  >> +      if (x_had_errors_p (FRAME_X_DISPLAY (f)))
>> >>  >> +        FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
>> >>  >
>> >>  > Shouldn't we turn off the supports_xdbe flag in the case of failure?
>> >> 
>> >> supports_xdbe is whether XDBE is supported on a connection at all. What 
>> >> if XdbeAllocateBackBufferName fails transiently?
>> >
>> > How can it fail transiently?  And why turning off supports_xdbe in
>> > that case would mean trouble?
>> 
>> It's an allocation. Allocations can fail. And XDBE isn't guaranteed to
>> work for all visuals.
>
> How much memory is being allocated, and correspondingly what is the
> probability this allocation could fail on a healthy system?

I don't know --- I've never seen it fail. I'm just applying general
memory-allocation heuristics here.  DBE can also fail if the visual
we're using for a window 

>
> And what about my second question?
>
> The downside of your proposed code is that we will try to turn on XDBE
> each time hence, and each attempt means a potentially costly series of
> calls to catch X errors, and the XdbeAllocateBackBufferName itself.
> I'm asking whether it wouldn't be better to simply give up on XDBE in
> such a case for that frame.  It could be much cheaper, no?  Just a
> thought.

It's only on frame creation though, and frame creation is both rare and
already expensive.

By the way: aren't most calls to x_catch_errors already buggy? AFAICT,
they don't generally call x_sync first.  The purpose of this mechanism
is to run a section of code in such a way that Emacs doesn't die if
something goes wrong in Xlib, but since Xlib error reporting is
asynchronous anyway, use of x_catch_errors without a preceding x_sync
(as I have in my code) can silently swallow X errors that we _do_ want
to kill Emacs.

Anyway, the double-buffer code has a few kinks I need to work out. I'll
send another version of the patch after I figure out what's going on.

  1. [must fix] during frame creation, on one of my computers but not
  the other, I momentarily see an all-black frame with a small white
  square, and only a few hundred ms later does the normal frame content
  get draws

  2. [must fix] on the same system, and not the other, after resuming
  the system from sleep, Emacs frames momentarily display all white
  before the usual frame contents get filled in

  3. [should fix] on the same system, and not the other, resizing the a
  frame interactively still produces some flicking, particulary in the
  modeline, but much less than without the patch entirely.
  This flickering appears to have something to do with XRender, since if
  I force everything to use X11 core rendering instead, I don't see any
  flickering at all.

  



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-23 20:51           ` Daniel Colascione
@ 2016-10-24  8:05             ` Eli Zaretskii
  2016-10-24 18:43               ` Ken Raeburn
  2016-10-27 19:06               ` dancol
  0 siblings, 2 replies; 31+ messages in thread
From: Eli Zaretskii @ 2016-10-24  8:05 UTC (permalink / raw)
  To: Daniel Colascione, Ken Raeburn; +Cc: emacs-devel

> From: Daniel Colascione <dancol@dancol.org>
> Cc: emacs-devel@gnu.org
> Date: Sun, 23 Oct 2016 13:51:01 -0700
> 
> > I meant the test for the lone font backend that needs this kludge, not
> > for the existence of the method.  And also a comment that tells it's a
> > kludge whose need we understand only empirically.  I'd even prefer a
> > direct call to the function when the font backend is the one which
> > needs it, rather than having a method created for the benefit of a
> > single backend, whose necessity we don't really understand.
> 
> So we want to write?
> 
>   for font_backend in frame.font_backends:
>     if instanceof(font_backend, XftBackend):
>       xft_refresh_hack()
> 
> That still strikes me as less clean, especially considering we'll have
> to provide the xft_refresh_hack to general-purpose code.

At the very least let's have a comment there saying that this is a
kludge that's actually done only for that font driver.  (Comments tend
to fall out of sync with the code, so making code speak for itself is
better, but a comment is infinitely better than nothing at all.)

> > I'm not saying the code won't work.  I'm saying it could make its
> > purpose more evident, which is better for long-term maintainability.
> 
> Longer-term, I think we need to move toward greater, not
> lesser, modularity.

I agree, but IME such a move cannot be piecemeal in this case, it must
be done in one go, or in a small number of large steps.  IOW, it's a
significant job on its own.

> >> We need to do a buffer flip at the end of redisplay for each frame on
> >> which update_end was called during redisplay.
> >
> > Even if update_end was called for a frame whose update was "paused",
> > i.e. whose redisplay was interrupted by incoming input?
> 
> Right now, we do a buffer flip unconditionally except in the case that
> we `goto end_of_redisplay`. I think that's a bug actually --- but I have
> to understand more of what this logic is actually doing.
> 
> Why _do_ we have a path that short-circuits the rest of the redisplay
> code? What would happen if we just removed it? It appears to be some
> kind of optimization, and I'm not sure it's actually necessary
> (especially since, according to the comment, we disable it anyway in the
> case of a blinking cursor).

It's an optimization for the case that nothing needs to be
redisplayed.

But I wasn't asking about that, I was asking about this part:

		  pending |= update_frame (f, false, false);

If update_frame returns non-zero, we don't mark all the windows as
having accurate display, which will cause redisplay to be re-entered
again on the first opportunity, and it will then try to redisplay all
of them again.  I was asking whether flipping in this case is TRT.

Without double buffer, what happens in this case is that we could
momentarily flash a partially redrawn window.

> I suppose you could make an argument for not doing a buffer flip if
> redisplay is interrupted by input, but I'm honestly not sure what might
> go wrong there, and my preference would be to act as much like the
> single-buffered case as possible and default, if we're unsure, to
> showing the results of our painting efforts to the user.

OK, we can always wait for bug reports, I guess, and take it from
there.

> > My problems start with the name of the hook, which doesn't hint at all
> > that only double-buffered X11 back-end cares about that.  If the hook
> > was called something like double_buffer_flip_hook, or was compiled
> > only if HAVE_XDBE is defined, this issue would go away.
> 
> So let's call it double_buffer_flip_hook.

Great, let's.

> > IOW, if the code speaks for itself, it makes maintenance of this
> > hyper-convoluted piece of Emacs easier, less time consuming, and less
> > error prone.
> 
> This code is an adventure.

It is.

> >> I'm also worried about maintainability: that's why I don't want to make
> >> redisplay_internal any more of a big ball of mud than it already is.
> >
> > It's too late for that.  It's already an extremely complex and hard to
> > understand piece of code.  Hiding more information from its logic
> > makes the code harder to maintain, not easier.  We are not talking
> > about highly-modular package, where the logic is kept local to each
> > module, and the interfaces between them are kept to the absolute
> > minimum.  xdisp.c _knows_ a lot about how xterm.c and xfns.c work.
> > You cannot disengage them without a complete rewrite.
> 
> Well, it also works with w32term and nsterm, so the coupling can't be
> complete. :-)

That's why w32term and nsterm are largely modeled on xterm (and the
same with xfns), right to the point of literally repeating many code
fragments.

> By the way: aren't most calls to x_catch_errors already buggy? AFAICT,
> they don't generally call x_sync first.  The purpose of this mechanism
> is to run a section of code in such a way that Emacs doesn't die if
> something goes wrong in Xlib, but since Xlib error reporting is
> asynchronous anyway, use of x_catch_errors without a preceding x_sync
> (as I have in my code) can silently swallow X errors that we _do_ want
> to kill Emacs.

That's for some X expert to answer, I really don't know enough about
this stuff.  Ken, can you comment on this?

>   1. [must fix] during frame creation, on one of my computers but not
>   the other, I momentarily see an all-black frame with a small white
>   square, and only a few hundred ms later does the normal frame content
>   get draws

Possibly related to the fact that when Emacs starts, it first defines
a small 10x10 frame, before resizing it to the actual size.  Look for
"10" in make_frame.

>   2. [must fix] on the same system, and not the other, after resuming
>   the system from sleep, Emacs frames momentarily display all white
>   before the usual frame contents get filled in

Could be Emacs waits for an expose event before redrawing the frame,
and meanwhile you show an empty buffer?

>   3. [should fix] on the same system, and not the other, resizing the a
>   frame interactively still produces some flicking, particulary in the
>   modeline, but much less than without the patch entirely.
>   This flickering appears to have something to do with XRender, since if
>   I force everything to use X11 core rendering instead, I don't see any
>   flickering at all.

If the flickering is on the modeline, a breakpoint in
redisplay_mode_lines should tell you why it happens.

Thanks.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-24  8:05             ` Eli Zaretskii
@ 2016-10-24 18:43               ` Ken Raeburn
  2016-10-27 19:06               ` dancol
  1 sibling, 0 replies; 31+ messages in thread
From: Ken Raeburn @ 2016-10-24 18:43 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Daniel Colascione, Emacs-Devel devel

> 
>> By the way: aren't most calls to x_catch_errors already buggy? AFAICT,
>> they don't generally call x_sync first.  The purpose of this mechanism
>> is to run a section of code in such a way that Emacs doesn't die if
>> something goes wrong in Xlib, but since Xlib error reporting is
>> asynchronous anyway, use of x_catch_errors without a preceding x_sync
>> (as I have in my code) can silently swallow X errors that we _do_ want
>> to kill Emacs.
> 
> That's for some X expert to answer, I really don't know enough about
> this stuff.  Ken, can you comment on this?

x_catch_errors itself starts with an XSync call to ensure errors on previous requests are processed with the default handler. Then x_uncatch_errors does another at the end of the region to make sure any errors generated by requests within the catching region are processed.  There’s also x_uncatch_errors_after_check, which I added specifically for cases where x_uncatch_errors was being called immediately after x_check_errors, which also does an XSync, so we could drop one round-trip (which was a performance issue for me in certain cases).


^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-24  8:05             ` Eli Zaretskii
  2016-10-24 18:43               ` Ken Raeburn
@ 2016-10-27 19:06               ` dancol
  2016-10-27 19:36                 ` Eli Zaretskii
  2016-10-27 22:18                 ` Paul Eggert
  1 sibling, 2 replies; 31+ messages in thread
From: dancol @ 2016-10-27 19:06 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Daniel Colascione, Ken Raeburn, emacs-devel

Below is a new much-improved version of the patch. It should address the
legibility concerns while probably adding more. It addresses all the bugs
I've been able to find and on my machine, produces buttery smooth editing
and resizing.

The patch has become a bit broader now: the biggest change is making GTK
toolkit scrollbars into independent X windows, as was the original
intention of the code, and getting rid of a lot of logic that tried to
order Emacs and GTK paints in such a way that we wouldn't clobber what GTK
drew.

Now that GTK draws its scrollbars in its own X windows, we can update the
main X window without having to queue or wait for GTK to do its drawing.
Specifically, we don't have to worry about buffer flips clearing
scrollbars off the screen or trying to get GTK to render scrollbars onto
our back buffer. This independent-X-window scheme was always the
intention, AFAICT, because we put GTK scrollbars in event boxes --- but
it'd bitrotted over the years thanks to GDK optimizing GdkWindow to not
create an X window unless you specifically ask for one, which we do now.

>> From: Daniel Colascione <dancol@dancol.org>
>> Cc: emacs-devel@gnu.org
>> Date: Sun, 23 Oct 2016 13:51:01 -0700
>>
>> > I meant the test for the lone font backend that needs this kludge, not
>> > for the existence of the method.  And also a comment that tells it's a
>> > kludge whose need we understand only empirically.  I'd even prefer a
>> > direct call to the function when the font backend is the one which
>> > needs it, rather than having a method created for the benefit of a
>> > single backend, whose necessity we don't really understand.
>>
>> So we want to write?
>>
>>   for font_backend in frame.font_backends:
>>     if instanceof(font_backend, XftBackend):
>>       xft_refresh_hack()
>>
>> That still strikes me as less clean, especially considering we'll have
>> to provide the xft_refresh_hack to general-purpose code.
>
> At the very least let's have a comment there saying that this is a
> kludge that's actually done only for that font driver.  (Comments tend
> to fall out of sync with the code, so making code speak for itself is
> better, but a comment is infinitely better than nothing at all.)

I've renamed the xftfont hook to something that reflects what's actually
going on --- dropping XRender surfaces. I noticed that the Cairo back-end
has similar calls for a similar purpose --- see the calls to
x_cr_destroy_surface.

>> >> We need to do a buffer flip at the end of redisplay for each frame on
>> >> which update_end was called during redisplay.
>> >
>> > Even if update_end was called for a frame whose update was "paused",
>> > i.e. whose redisplay was interrupted by incoming input?
>>
>> Right now, we do a buffer flip unconditionally except in the case that
>> we `goto end_of_redisplay`. I think that's a bug actually --- but I have
>> to understand more of what this logic is actually doing.

There was indeed a bug: we were forgetting to do a buffer flip after
redisplay if nothing changed except mouse highlighting. (But that bug was
masked by others.) Now, in the double buffer case, we bypass the "goto
end_of_redisplay" case by putting the frame up for redisplay if we change
the mouse face.

>> Why _do_ we have a path that short-circuits the rest of the redisplay
>> code? What would happen if we just removed it? It appears to be some
>> kind of optimization, and I'm not sure it's actually necessary
>> (especially since, according to the comment, we disable it anyway in the
>> case of a blinking cursor).
>
> It's an optimization for the case that nothing needs to be
> redisplayed.

Isn't the "goto update" path fast enough?

>
> But I wasn't asking about that, I was asking about this part:
>
> 		  pending |= update_frame (f, false, false);
>
> If update_frame returns non-zero, we don't mark all the windows as
> having accurate display, which will cause redisplay to be re-entered
> again on the first opportunity, and it will then try to redisplay all
> of them again.  I was asking whether flipping in this case is TRT.
>
> Without double buffer, what happens in this case is that we could
> momentarily flash a partially redrawn window.

I don't think it is --- now I've eliminated the terminal hook I added.
It's sufficient to rely on frame_up_to_date_hook and calls to flush_frame
to do buffer flips.

>> > My problems start with the name of the hook, which doesn't hint at all
>> > that only double-buffered X11 back-end cares about that.  If the hook
>> > was called something like double_buffer_flip_hook, or was compiled
>> > only if HAVE_XDBE is defined, this issue would go away.
>>
>> So let's call it double_buffer_flip_hook.

Now, we don't need an additional hook.

> >> By the way: aren't most calls to x_catch_errors already buggy?

As Ken mentioned, I just missed the XSync call in x_catch_errors, so Emacs
is fine as it is.

>>   1. [must fix] during frame creation, on one of my computers but not
>>   the other, I momentarily see an all-black frame with a small white
>>   square, and only a few hundred ms later does the normal frame content
>>   get draws
>
> Possibly related to the fact that when Emacs starts, it first defines
> a small 10x10 frame, before resizing it to the actual size.  Look for
> "10" in make_frame.

This bug ended up being a deep rabbit hole. The initial white square is a
200x200 default for GtkWindow's idea of its own size in the case that it
has no children with non-zero size. Early in frame creation, GTK and X
disagree on the size of the frame; GTK thinks it's 200x200, so when it
paints its background in response to the first Expose event, it draws a
200x200 white square. It's not just my patch: Emacs always behaves this
way. You usually never see it, though, because we XClearWindow on wfixed
immediately after, and if you're not unlucky, the X server coalesces GTK's
weird 200x200 paint and Emacs's background clearing, so the user never
sees anything but a white background.

If you take trunk Emacs today, run it with GDK_SYNCHRONIZE=1 in the
environment, and step through xg_create_frame_widgets, you can see the
same initial white square.

My patch doesn't fix the problem. It works around it the same way Emacs
did previously, by clearing the window quickly on startup. The fix falls
out of fixing the Expose-event handling logic.

We really should figure out why GTK is so confused about the size of its
window. If we do, we won't have to rely on winning a race to present a
good experience to users.

>>   2. [must fix] on the same system, and not the other, after resuming
>>   the system from sleep, Emacs frames momentarily display all white
>>   before the usual frame contents get filled in
>
> Could be Emacs waits for an expose event before redrawing the frame,
> and meanwhile you show an empty buffer?

We weren't doing a buffer flip in response to Expose events.

>>   3. [should fix] on the same system, and not the other, resizing the a
>>   frame interactively still produces some flicking, particulary in the
>>   modeline, but much less than without the patch entirely.
>>   This flickering appears to have something to do with XRender, since if
>>   I force everything to use X11 core rendering instead, I don't see any
>>   flickering at all.
>
> If the flickering is on the modeline, a breakpoint in
> redisplay_mode_lines should tell you why it happens.

The flickering was coming from a combination of things: we weren't
clearing the Xft surface cache aggressively enough, and redisplay had a
longstanding bug where we'd lose track of whether we'd drawn the cursor
just after drawing a garbaged frame.


diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..38cdecc
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,132 @@
+2016-10-27  Daniel Colascione  <dancol@dancol.org>
+
+	* src/xterm.h (struct x_display_info): New member supports_xdbe.
+	(struct x_output): New member draw_desc.
+	(FRAME_X_DRAWABLE, FRAME_X_DOUBLE_BUFFERED_P)
+	(FRAME_X_NEED_BUFFER_FLIP): New macros.
+	(set_up_x_back_buffer, tear_down_x_back_buffer)
+	(initial_set_up_x_back_buffer): Declare.
+
+	* src/xterm.c: Include Xdbe.h.
+	(x_begin_cr_clip, x_fill_rectangle, x_draw_rectangle)
+	(x_draw_vertical_window_border, x_update_end)
+	(x_setup_relief_color, x_draw_relief_rect)
+	(x_draw_fringe_bitmap, x_shift_glyphs_for_insert)
+	(x_scroll_run, x_draw_hollow_cursor, x_draw_bar_cursor): Use
+	FRAME_X_DRAWABLE instead of FRAME_X_WINDOW; rename local
+	variables appropriately; substitute calls to XClearArea with
+	x_clear_area, which DTRT for double buffering.
+	(x_clear_window, x_clear_area): In double-buffering mode, use
+	rect-drawing X functions instead of XClearWindow and
+	XClearArea, which always operate on the front buffer.
+	(show_back_buffer): New function.
+	(XTframe_up_to_date): Call show_back_buffer when done.
+	(x_clear_frame, x_clear_frame_area): Remove obsolete calls to
+	gtk_widget_queue_draw to refresh scroll bars; scroll bars are
+	now independent X windows.
+	(handle_one_xevent): Call font_drop_xrender_surfaces when XftDraw
+	might need regenerating; perform buffer flip when responding
+	to Expose events; issue front-buffer clearing commands as
+	stopgap while we wait for redisplay.
+	(x_make_frame_visible): Un-bitrot comment; move XSETFRAME
+	earlier in function.
+	(x_free_frame_resources): Call tear_down_x_back_buffer when
+	destroying frame.
+	(x_term_init): Attempt to initialize double buffer extension.
+	(x_flip_and_flush): New function.
+	(x_redisplay_interface): Point to x_flip_and_flush instead of
+	x_flip directly.
+
+	* src/xftfont.c (xftfont_drop_xrender_surfaces): Use
+	FRAME_X_DRAWABLE instead of FRAME_X_WINDOW.
+	(xftfont_drop_xrender_surfaces): New function.
+	(syms_of_xftfont): Register it.
+
+	* src/xfont.c (xfont_draw): Use FRAME_X_DRAWABLE instead of
+	FRAME_X_WINDOW.
+
+	* src/xfns.c: Include Xdbe.h.
+	(x_set_inhibit_double_buffering, set_up_x_back_buffer)
+	(Fx_double_buffered_p): New functions.
+	(x_window): Call initial_set_up_x_back_buffer.
+	(x_make_gc): Use FRAME_X_DRAWABLE instead of FRAME_X_WINDOW.
+	(Fx_create_frame): Configure `inhibit-double-buffering'
+	frame parameter.
+	(x_create_tip_frame): Call initial_set_up_x_back_buffer.
+	(x_frame_parm_handlers): Register
+	x_set_inhibit_double_buffering.
+	(syms_of_xfns): Register Sx_double_buffered_p.
+
+	* src/xfaces.c (x_create_gc): Use FRAME_X_DRAWABLE instead of
+	FRAME_X_WINDOW.
+
+	* src/xdisp.c (remember_mouse_glyph, init_glyph_string): Use
+	FRAME_X_DRAWABLE instead of FRAME_X_WINDOW.
+	(redisplay_internal): Restart redisplay if a frame is garbaged
+	during updating; explain why.
+	(redisplay_preserve_echo_area): Call flush_frame
+	conditionally, depending on `double_buffered'.
+	(show_mouse_face): In the `double_buffered' case, queue frame
+	for redisplay (and thus buffer flip), because immediate
+	drawing will have no effect.
+
+	* src/w32fns.c (w32_frame_parm_handlers): Add placeholder for
+	x_set_inhibit_double_buffering.
+
+	* src/termhooks.h (struct terminal): Explain why calling
+	`frame_up_to_date_hook' is important.
+
+	* src/nsfns.m (ns_frame_parm_handlers): Add placeholder for
+	x_set_inhibit_double_buffering.
+
+	* src/image.c (x_create_bitmap_from_data)
+	(x_create_bitmap_from_file, x_create_x_image_and_pixmap)
+	(Create_Pixmap_From_Bitmap_Data)
+	(x_create_bitmap_from_xpm_data, xpm_load, gs_load): Use
+	FRAME_X_DRAWABLE instead of FRAME_X_WINDOW; rename local
+	variables appropriately.
+
+	* src/gtkutil.c: Include Xdbe.h.
+	(xg_get_widget_from_map): Forward declare.
+	(xg_clear_under_internal_border): Remove obsolete calls to
+	refresh scroll bars.
+	(xg_create_frame_widgets): Call initial_set_up_x_back_buffer.
+	(xg_free_frame_widgets): Call tear_down_x_back_buffer; reset
+	FRAME_X_DRAWABLE as well as FRAME_X_WINDOW and for the
+	same reason.
+	(xg_set_background_color): Set scroll bar background colors.
+	(xg_finish_scroll_bar_creation): New function with common
+	logic of xg_create_scroll_bar, xg_create_horizontal_scroll_bar. Force
+	scroll bars to be real X11 windows.
+	(xg_create_scroll_bar, xg_create_horizontal_scroll_bar): Call
+	xg_finish_scroll_bar_creation.
+	(xg_update_scrollbar_pos, xg_update_horizontal_scrollbar_pos):
+	Remove obsolete calls to refresh scroll bars; fix comments.
+
+	* src/ftxfont.c (ftxfont_get_gcs, ftxfont_draw_bitmap,
+	(ftxfont_draw_background): Use FRAME_X_DRAWABLE instead of
+	FRAME_X_WINDOW.
+
+	* src/frame.h (struct frame): Add double_buffered bitfield.
+
+	* src/frame.c (frame_parms): Add table entry for new
+	`inhibit-double-buffering' frame parameter
+	(syms_of_frame): Register Qinhibit_double_buffering.
+
+	* src/font.h (struct font_driver): Add new
+	`flush_frame_caches' hook.
+	(font_drop_xrender_surfaces): Declare.
+
+	* src/font.c (font_drop_xrender_surfaces): New function.
+
+	* src/dispnew.c (redraw_frame): Call font_drop_xrender_surfaces.
+
+	* src/Makefile.in (XDBE_LIBS, XDBE_CFLAGS): Substitute.
+
+	* etc/NEWS: Mention use of double buffering
+
+	* doc/lispref/frames.texi (Management Parameters): Document
+	`inhibit-double-buffering' frame parameters.
+	(Visibility of Frames): Document `x-double-buffered-p'.
+
+	* configure.ac: Check for the X double buffer extension
diff --git a/configure.ac b/configure.ac
index 46fd434..f67fe83 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3712,6 +3712,24 @@ AC_DEFUN
 AC_SUBST(XFIXES_CFLAGS)
 AC_SUBST(XFIXES_LIBS)

+### Use Xdbe (-lXdbe) if available
+HAVE_XDBE=no
+if test "${HAVE_X11}" = "yes"; then
+  AC_CHECK_HEADER(X11/extensions/Xdbe.h,
+    [AC_CHECK_LIB(Xext, XdbeAllocateBackBufferName, HAVE_XDBE=yes)],
+    [],
+    [#include <X11/Xlib.h>
+    ])
+  if test $HAVE_XDBE = yes; then
+    XDBE_LIBS=-lXext
+  fi
+  if test $HAVE_XDBE = yes; then
+    AC_DEFINE(HAVE_XDBE, 1, [Define to 1 if you have the Xdbe extension.])
+  fi
+fi
+AC_SUBST(XDBE_CFLAGS)
+AC_SUBST(XDBE_LIBS)
+
 ### Use libxml (-lxml2) if available
 ### mingw32 doesn't use -lxml2, since it loads the library dynamically.
 HAVE_LIBXML2=no
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 7736438..90f8e35 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -1539,6 +1539,13 @@ Management Parameters
 If non-@code{nil}, the frame is visible on all virtual desktops on systems
 with virtual desktops.

+@vindex inhibit-double-buffering, a frame parameter
+@item inhibit-double-buffering
+If non-@code{nil}, the frame is drawn to the screen without double
buffering.
+Emacs normally attempts to use double buffering, where available, to
+reduce flicker.  Set this property if you experience display bugs or
+pine for that retro, flicker-y feeling.
+
 @ignore
 @vindex parent-id, a frame parameter
 @item parent-id
@@ -2210,6 +2217,12 @@ Visibility of Frames
 any control, but Emacs does provide events that you can use to keep
 track of such changes.  @xref{Misc Events}.

+@defun x-double-buffered-p &optional frame
+This function returns non-@code{nil} if @var{frame} is currently
+being rendered with double buffering.  @var{frame} defaults to the
+selected frame.
+@end defun
+
 @node Raising and Lowering
 @section Raising and Lowering Frames

diff --git a/etc/NEWS b/etc/NEWS
index a160f81..aad59fc 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -61,6 +61,12 @@ affected by this, as SGI stopped supporting IRIX in
December 2013.
 \f
 * Changes in Emacs 26.1

++++
+** Emacs now uses double buffering to reduce flicker when editing and
+resizing graphical Emacs frames on the X Window System.  This support
+requires the DOUBLE-BUFFER extension, which major X servers have
+supported for many years.
+
 ---
 The group 'wp', whose label was "text", is now deprecated.
 Use the new group 'text', which inherits from 'wp', instead.
diff --git a/src/Makefile.in b/src/Makefile.in
index 89f7a92..dc0bfff 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -254,6 +254,9 @@ XINERAMA_CFLAGS =
 XFIXES_LIBS = @XFIXES_LIBS@
 XFIXES_CFLAGS = @XFIXES_CFLAGS@

+XDBE_LIBS = @XDBE_LIBS@
+XDBE_CFLAGS = @XDBE_CFLAGS@
+
 ## widget.o if USE_X_TOOLKIT, otherwise empty.
 WIDGET_OBJ=@WIDGET_OBJ@

@@ -372,7 +375,7 @@ ALL_CFLAGS=
   $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \
   $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
   $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \
-  $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \
+  $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \
   $(WEBKIT_CFLAGS) \
   $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
   $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
@@ -489,6 +492,7 @@ LIBES =
    $(WEBKIT_LIBS) \
    $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \
    $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \
+   $(XDBE_LIBS) \
    $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \
    $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
    $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
diff --git a/src/dispnew.c b/src/dispnew.c
index 70d4de0..7f5012a 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -2999,6 +2999,8 @@ redraw_frame (struct frame *f)
 {
   /* Error if F has no glyphs.  */
   eassert (f->glyphs_initialized_p);
+  if (f->double_buffered)
+    font_drop_xrender_surfaces (f);
   update_begin (f);
   if (FRAME_MSDOS_P (f))
     FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f));
diff --git a/src/font.c b/src/font.c
index f8e6794..ce63233 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, Lisp_Object
arg, Lisp_Object result)
 }

 void
+font_drop_xrender_surfaces (struct frame *f)
+{
+  struct font_driver_list *list;
+
+  for (list = f->font_driver_list; list; list = list->next)
+    if (list->on && list->driver->drop_xrender_surfaces)
+      list->driver->drop_xrender_surfaces (f);
+}
+
+void
 syms_of_font (void)
 {
   sort_shift_bits[FONT_TYPE_INDEX] = 0;
diff --git a/src/font.h b/src/font.h
index cf47729..c14823b 100644
--- a/src/font.h
+++ b/src/font.h
@@ -763,6 +763,13 @@ struct font_driver
      Return non-nil if the driver support rendering of combining
      characters for FONT according to Unicode combining class.  */
   Lisp_Object (*combining_capability) (struct font *font);
+
+  /* Optional
+
+     Called when frame F is double-buffered and its size changes; Xft
+     relies on this hook to throw away its old XftDraw (which won't
+     work after the size change) and get a new one.  */
+  void (*drop_xrender_surfaces) (struct frame *f);
 };


@@ -864,6 +871,8 @@ extern void font_filter_properties (Lisp_Object font,
 				    const char *const boolean_properties[],
                                     const char *const
non_boolean_properties[]);

+extern void font_drop_xrender_surfaces (struct frame *f);
+
 #ifdef HAVE_FREETYPE
 extern struct font_driver ftfont_driver;
 extern void syms_of_ftfont (void);
diff --git a/src/frame.c b/src/frame.c
index f3a548c..3a2d009 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -3128,6 +3128,7 @@ static const struct frame_parm_table frame_parms[] =
   {"alpha",			SYMBOL_INDEX (Qalpha)},
   {"sticky",			SYMBOL_INDEX (Qsticky)},
   {"tool-bar-position",		SYMBOL_INDEX (Qtool_bar_position)},
+  {"inhibit-double-buffering",  SYMBOL_INDEX (Qinhibit_double_buffering)},
 };

 #ifdef HAVE_WINDOW_SYSTEM
@@ -5044,6 +5045,7 @@ syms_of_frame (void)
   DEFSYM (Qvertical_scroll_bars, "vertical-scroll-bars");
   DEFSYM (Qvisibility, "visibility");
   DEFSYM (Qwait_for_wm, "wait-for-wm");
+  DEFSYM (Qinhibit_double_buffering, "inhibit-double-buffering");

   {
     int i;
diff --git a/src/frame.h b/src/frame.h
index 5e3ee68..a318b41 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -342,6 +342,10 @@ struct frame
   /* Non-zero if this frame's faces need to be recomputed.  */
   bool_bf face_change : 1;

+  /* Non-zero if frame accumulates drawing operations and flushes
+     them all at once.  */
+  bool_bf double_buffered : 1;
+
   /* Bitfield area ends here.  */

   /* Number of lines (rounded up) of tool bar.  REMOVE THIS  */
diff --git a/src/ftxfont.c b/src/ftxfont.c
index f49d44f..bfdeb40 100644
--- a/src/ftxfont.c
+++ b/src/ftxfont.c
@@ -95,7 +95,7 @@ ftxfont_get_gcs (struct frame *f, unsigned long
foreground, unsigned long backgr
       if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &color))
 	break;
       xgcv.foreground = color.pixel;
-      new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE
(f),
 				   GCForeground, &xgcv);
     }
   unblock_input ();
@@ -139,14 +139,14 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC
*gcs, struct font *font,
 		p[n[0]].y = y - bitmap.top + i;
 		if (++n[0] == size)
 		  {
-		    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 				 gc_fore, p, size, CoordModeOrigin);
 		    n[0] = 0;
 		  }
 	      }
 	}
       if (flush && n[0] > 0)
-	XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+        XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 		     gc_fore, p, n[0], CoordModeOrigin);
     }
   else
@@ -168,7 +168,7 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC
*gcs, struct font *font,
 		  pp[n[idx]].y = y - bitmap.top + i;
 		  if (++(n[idx]) == size)
 		    {
-		      XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+                      XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE
(f),
 				   idx == 6 ? gc_fore : gcs[idx], pp, size,
 				   CoordModeOrigin);
 		      n[idx] = 0;
@@ -180,10 +180,10 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC
*gcs, struct font *font,
 	{
 	  for (i = 0; i < 6; i++)
 	    if (n[i] > 0)
-	      XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+              XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 			   gcs[i], p + 0x100 * i, n[i], CoordModeOrigin);
 	  if (n[6] > 0)
-	    XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+            XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 			 gc_fore, p + 0x600, n[6], CoordModeOrigin);
 	}
     }
@@ -203,7 +203,7 @@ ftxfont_draw_background (struct frame *f, struct font
*font, GC gc, int x, int y
   XGetGCValues (FRAME_X_DISPLAY (f), gc,
 		GCForeground | GCBackground, &xgcv);
   XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background);
-  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc,
+  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc,
 		  x, y - FONT_BASE (font), width, FONT_HEIGHT (font));
   XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground);
 }
diff --git a/src/gtkutil.c b/src/gtkutil.c
index 88e6d30..d4151a0 100644
--- a/src/gtkutil.c
+++ b/src/gtkutil.c
@@ -48,6 +48,10 @@ along with GNU Emacs.  If not, see
<http://www.gnu.org/licenses/>.  */
 #include "emacsgtkfixed.h"
 #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
 #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW
 #define gtk_widget_set_has_window(w, b) \
   (gtk_fixed_set_has_window (GTK_FIXED (w), b))
@@ -143,6 +147,8 @@ struct xg_frame_tb_info
   GtkTextDirection dir;
 };

+static GtkWidget * xg_get_widget_from_map (ptrdiff_t idx);
+
 \f
 /***********************************************************************
                       Display handling functions
@@ -815,12 +821,6 @@ xg_clear_under_internal_border (struct frame *f)
 {
   if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
     {
-#ifndef USE_CAIRO
-      GtkWidget *wfixed = f->output_data.x->edit_widget;
-
-      gtk_widget_queue_draw (wfixed);
-      gdk_window_process_all_updates ();
-#endif
       x_clear_area (f, 0, 0,
 		    FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f));

@@ -1233,6 +1233,7 @@ xg_create_frame_widgets (struct frame *f)
      by callers of this function.  */
   gtk_widget_realize (wfixed);
   FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed);
+  initial_set_up_x_back_buffer (f);

   /* Since GTK clears its window by filling with the background color,
      we must keep X and GTK background in sync.  */
@@ -1296,8 +1297,11 @@ xg_free_frame_widgets (struct frame *f)
       if (tbinfo)
         xfree (tbinfo);

+      /* x_free_frame_resources should have taken care of it */
+      eassert (!FRAME_X_DOUBLE_BUFFERED_P (f));
       gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f));
       FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */
+      FRAME_X_DRAWABLE (f) = 0;
       FRAME_GTK_OUTER_WIDGET (f) = 0;
 #ifdef USE_GTK_TOOLTIP
       if (x->ttip_lbl)
@@ -1440,6 +1444,18 @@ xg_set_background_color (struct frame *f, unsigned
long bg)
     {
       block_input ();
       xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL
(f));
+
+      Lisp_Object bar;
+      for (bar = FRAME_SCROLL_BARS (f);
+           !NILP (bar);
+           bar = XSCROLL_BAR (bar)->next)
+        {
+          GtkWidget* scrollbar =
+            xg_get_widget_from_map (XSCROLL_BAR (bar)->x_window);
+          GtkWidget* webox = gtk_widget_get_parent (scrollbar);
+          xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
+        }
+
       unblock_input ();
     }
 }
@@ -2265,7 +2281,6 @@ xg_mark_data (void)
     }
 }

-
 /* Callback called when a menu item is destroyed.  Used to free data.
    W is the widget that is being destroyed (not used).
    CLIENT_DATA points to the xg_menu_item_cb_data associated with the W.  */
@@ -3569,44 +3584,23 @@ xg_gtk_scroll_destroy (GtkWidget *widget, gpointer
data)
   xg_remove_widget_from_map (id);
 }

-/* Create a scroll bar widget for frame F.  Store the scroll bar
-   in BAR.
-   SCROLL_CALLBACK is the callback to invoke when the value of the
-   bar changes.
-   END_CALLBACK is the callback to invoke when scrolling ends.
-   SCROLL_BAR_NAME is the name we use for the scroll bar.  Can be used
-   to set resources for the widget.  */
-
-void
-xg_create_scroll_bar (struct frame *f,
+static void
+xg_finish_scroll_bar_creation (struct frame *f,
+                               GtkWidget *wscroll,
                                struct scroll_bar *bar,
                                GCallback scroll_callback,
                                GCallback end_callback,
                                const char *scroll_bar_name)
 {
-  GtkWidget *wscroll;
-  GtkWidget *webox;
-  intptr_t scroll_id;
-#ifdef HAVE_GTK3
-  GtkAdjustment *vadj;
-#else
-  GtkObject *vadj;
-#endif
-
-  /* Page, step increment values are not so important here, they
-     will be corrected in x_set_toolkit_scroll_bar_thumb. */
-  vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
-                             0.1, 0.1, 0.1);
+  GtkWidget *webox = gtk_event_box_new ();

-  wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT
(vadj));
-  webox = gtk_event_box_new ();
   gtk_widget_set_name (wscroll, scroll_bar_name);
 #ifndef HAVE_GTK3
   gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
 #endif
   g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);

-  scroll_id = xg_store_widget_in_map (wscroll);
+  ptrdiff_t scroll_id = xg_store_widget_in_map (wscroll);

   g_signal_connect (G_OBJECT (wscroll),
                     "destroy",
@@ -3630,11 +3624,52 @@ xg_create_scroll_bar (struct frame *f,
   gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
   gtk_container_add (GTK_CONTAINER (webox), wscroll);

+  xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f));
+
+  /* N.B. The event box doesn't become a real X11 window until we ask
+     for its XID via GTK_WIDGET_TO_X_WIN.  If the event box is not a
+     real X window, it and its scroll-bar child try to draw on the
+     Emacs main window, which we draw over using Xlib.  */
+  gtk_widget_realize (webox);
+  GTK_WIDGET_TO_X_WIN (webox);

   /* Set the cursor to an arrow.  */
   xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);

   bar->x_window = scroll_id;
+}
+
+/* Create a scroll bar widget for frame F.  Store the scroll bar
+   in BAR.
+   SCROLL_CALLBACK is the callback to invoke when the value of the
+   bar changes.
+   END_CALLBACK is the callback to invoke when scrolling ends.
+   SCROLL_BAR_NAME is the name we use for the scroll bar.  Can be used
+   to set resources for the widget.  */
+
+void
+xg_create_scroll_bar (struct frame *f,
+                      struct scroll_bar *bar,
+                      GCallback scroll_callback,
+                      GCallback end_callback,
+                      const char *scroll_bar_name)
+{
+  GtkWidget *wscroll;
+#ifdef HAVE_GTK3
+  GtkAdjustment *vadj;
+#else
+  GtkObject *vadj;
+#endif
+
+  /* Page, step increment values are not so important here, they
+     will be corrected in x_set_toolkit_scroll_bar_thumb. */
+  vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX,
+                             0.1, 0.1, 0.1);
+
+  wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT
(vadj));
+
+  xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback,
+                                 end_callback, scroll_bar_name);
   bar->horizontal = 0;
 }

@@ -3652,8 +3687,6 @@ xg_create_horizontal_scroll_bar (struct frame *f,
 				 const char *scroll_bar_name)
 {
   GtkWidget *wscroll;
-  GtkWidget *webox;
-  intptr_t scroll_id;
 #ifdef HAVE_GTK3
   GtkAdjustment *hadj;
 #else
@@ -3666,42 +3699,9 @@ xg_create_horizontal_scroll_bar (struct frame *f,
                              0.1, 0.1, 0.1);

   wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT
(hadj));
-  webox = gtk_event_box_new ();
-  gtk_widget_set_name (wscroll, scroll_bar_name);
-#ifndef HAVE_GTK3
-  gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS);
-#endif
-  g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f);

-  scroll_id = xg_store_widget_in_map (wscroll);
-
-  g_signal_connect (G_OBJECT (wscroll),
-                    "destroy",
-                    G_CALLBACK (xg_gtk_scroll_destroy),
-                    (gpointer) scroll_id);
-  g_signal_connect (G_OBJECT (wscroll),
-                    "change-value",
-                    scroll_callback,
-                    (gpointer) bar);
-  g_signal_connect (G_OBJECT (wscroll),
-                    "button-release-event",
-                    end_callback,
-                    (gpointer) bar);
-
-  /* The scroll bar widget does not draw on a window of its own.  Instead
-     it draws on the parent window, in this case the edit widget.  So
-     whenever the edit widget is cleared, the scroll bar needs to redraw
-     also, which causes flicker.  Put an event box between the edit widget
-     and the scroll bar, so the scroll bar instead draws itself on the
-     event box window.  */
-  gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1);
-  gtk_container_add (GTK_CONTAINER (webox), wscroll);
-
-
-  /* Set the cursor to an arrow.  */
-  xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor);
-
-  bar->x_window = scroll_id;
+  xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback,
+                                 end_callback, scroll_bar_name);
   bar->horizontal = 1;
 }

@@ -3770,15 +3770,9 @@ xg_update_scrollbar_pos (struct frame *f,
           gtk_widget_show_all (wparent);
           gtk_widget_set_size_request (wscroll, width, height);
         }
-#ifndef USE_CAIRO
-      gtk_widget_queue_draw (wfixed);
-      gdk_window_process_all_updates ();
-#endif
       if (oldx != -1 && oldw > 0 && oldh > 0)
         {
-          /* Clear under old scroll bar position.  This must be done after
-             the gtk_widget_queue_draw and gdk_window_process_all_updates
-             above.  */
+          /* Clear under old scroll bar position.  */
           oldw += (scale - 1) * oldw;
 	  oldx -= (scale - 1) * oldw;
           x_clear_area (f, oldx, oldy, oldw, oldh);
@@ -3841,14 +3835,9 @@ xg_update_horizontal_scrollbar_pos (struct frame *f,
           gtk_widget_show_all (wparent);
           gtk_widget_set_size_request (wscroll, width, height);
         }
-      gtk_widget_queue_draw (wfixed);
-      gdk_window_process_all_updates ();
       if (oldx != -1 && oldw > 0 && oldh > 0)
-	/* Clear under old scroll bar position.  This must be done after
-	   the gtk_widget_queue_draw and gdk_window_process_all_updates
-	   above.  */
-	x_clear_area (f,
-		      oldx, oldy, oldw, oldh);
+        /* Clear under old scroll bar position.  */
+        x_clear_area (f, oldx, oldy, oldw, oldh);

       /* GTK does not redraw until the main loop is entered again, but
          if there are no X events pending we will not enter it.  So we sync
diff --git a/src/image.c b/src/image.c
index 9bd2455..1303a93 100644
--- a/src/image.c
+++ b/src/image.c
@@ -220,7 +220,7 @@ x_create_bitmap_from_data (struct frame *f, char
*bits, unsigned int width, unsi

 #ifdef HAVE_X_WINDOWS
   Pixmap bitmap;
-  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 				  bits, width, height);
   if (! bitmap)
     return -1;
@@ -327,7 +327,7 @@ x_create_bitmap_from_file (struct frame *f,
Lisp_Object file)

   filename = SSDATA (found);

-  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 			    filename, &width, &height, &bitmap, &xhot, &yhot);
   if (result != BitmapSuccess)
     return -1;
@@ -1952,7 +1952,7 @@ x_create_x_image_and_pixmap (struct frame *f, int
width, int height, int depth,
 {
 #ifdef HAVE_X_WINDOWS
   Display *display = FRAME_X_DISPLAY (f);
-  Window window = FRAME_X_WINDOW (f);
+  Drawable drawable = FRAME_X_DRAWABLE (f);
   Screen *screen = FRAME_X_SCREEN (f);

   eassert (input_blocked_p ());
@@ -1981,7 +1981,7 @@ x_create_x_image_and_pixmap (struct frame *f, int
width, int height, int depth,
   (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);

   /* Allocate a pixmap of the same size.  */
-  *pixmap = XCreatePixmap (display, window, width, height, depth);
+  *pixmap = XCreatePixmap (display, drawable, width, height, depth);
   if (*pixmap == NO_PIXMAP)
     {
       x_destroy_x_image (*ximg);
@@ -2742,7 +2742,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f,
struct image *img, char *data,
   img->pixmap =
    (x_check_image_size (0, img->width, img->height)
     ? XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f),
-				   FRAME_X_WINDOW (f),
+                                   FRAME_X_DRAWABLE (f),
 				   data,
 				   img->width, img->height,
 				   fg, bg,
@@ -3520,7 +3520,7 @@ x_create_bitmap_from_xpm_data (struct frame *f,
const char **bits)
   xpm_init_color_cache (f, &attrs);
 #endif

-  rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 				(char **) bits, &bitmap, &mask, &attrs);
   if (rc != XpmSuccess)
     {
@@ -3758,7 +3758,7 @@ xpm_load (struct frame *f, struct image *img)
 #ifdef HAVE_X_WINDOWS
   if (rc == XpmSuccess)
     {
-      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE
(f),
 				   img->ximg->width, img->ximg->height,
 				   img->ximg->depth);
       if (img->pixmap == NO_PIXMAP)
@@ -3768,7 +3768,7 @@ xpm_load (struct frame *f, struct image *img)
 	}
       else if (img->mask_img)
 	{
-	  img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+          img->mask = XCreatePixmap (FRAME_X_DISPLAY (f),
FRAME_X_DRAWABLE (f),
 				     img->mask_img->width,
 				     img->mask_img->height,
 				     img->mask_img->depth);
@@ -9541,7 +9541,7 @@ gs_load (struct frame *f, struct image *img)
     {
       /* Only W32 version did BLOCK_INPUT here.  ++kfs */
       block_input ();
-      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+      img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE
(f),
 				   img->width, img->height,
 				   DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
       unblock_input ();
@@ -9557,7 +9557,7 @@ gs_load (struct frame *f, struct image *img)
      if successful.  We do not record_unwind_protect here because
      other places in redisplay like calling window scroll functions
      don't either.  Let the Lisp loader use `unwind-protect' instead.  */
-  printnum1 = FRAME_X_WINDOW (f);
+  printnum1 = FRAME_X_DRAWABLE (f);
   printnum2 = img->pixmap;
   window_and_pixmap_id
     = make_formatted_string (buffer, "%"pMu" %"pMu, printnum1, printnum2);
diff --git a/src/nsfns.m b/src/nsfns.m
index ce2622c..b826460 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -971,6 +971,7 @@ Turn the input menu (an NSMenu) into a lisp list for
tracking on lisp side
   x_set_alpha,
   0, /* x_set_sticky */
   0, /* x_set_tool_bar_position */
+  0, /* x_set_inhibit_double_buffering */
 };


diff --git a/src/termhooks.h b/src/termhooks.h
index ff74d99..960323a 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -628,7 +628,12 @@ struct terminal
   int (*read_socket_hook) (struct terminal *terminal,
                            struct input_event *hold_quit);

-  /* Called when a frame's display becomes entirely up to date.  */
+  /* Called when a frame's display becomes entirely up to date.
+
+     N.B. On some double-buffered window systems, frame updates may be
+     hidden from the user until frame_up_to_date_hook or flush_frame
+     is called.  Redisplay automatically runs this hook.
+     */
   void (*frame_up_to_date_hook) (struct frame *);

 \f
diff --git a/src/w32fns.c b/src/w32fns.c
index 2b07bb2..1d83b02 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -9757,6 +9757,7 @@ frame_parm_handler w32_frame_parm_handlers[] =
   x_set_alpha,
   0, /* x_set_sticky */
   0, /* x_set_tool_bar_position */
+  0, /* x_set_inhibit_double_buffering */
 };

 void
diff --git a/src/xdisp.c b/src/xdisp.c
index 1a27c4c..9e51d58 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2501,7 +2501,7 @@ remember_mouse_glyph (struct frame *f, int gx, int
gy, NativeRectangle *rect)

   /* Visible feedback for debugging.  */
 #if false && defined HAVE_X_WINDOWS
-  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 		  f->output_data.x->normal_gc,
 		  gx, gy, width, height);
 #endif
@@ -14072,6 +14072,17 @@ redisplay_internal (void)
 		  if (!f_redisplay_flag && f->redisplay)
                     goto retry_frame;

+                  /* In some case (e.g., window resize), we notice
+                     only during window updating that the window
+                     content changed unpredictably (e.g., a GTK
+                     scrollbar moved) and that our previous estimation
+                     of the frame content was garbage.  We have to
+                     start over.  These cases should be rare, so going
+                     all the way back to the top of redisplay should
+                     be good enough.  */
+                  if (FRAME_GARBAGED_P (f))
+                    goto retry;
+
 		  /* Prevent various kinds of signals during display
 		     update.  stdio is not robust about handling
 		     signals, which can cause an apparent I/O error.  */
@@ -14325,7 +14336,17 @@ redisplay_preserve_echo_area (int from_where)
   else
     redisplay_internal ();

-  flush_frame (SELECTED_FRAME ());
+  /* Avoid flush_frame unconditionally for double-buffered frames,
+     since flush_frame will cause a buffer flip, and we don't want to
+     do buffer flips unless we know something has changed.  If we were
+     to call flush_frame unconditionally, we'd issue an unceasing
+     series of useless buffer flips in response to mouse movement over
+     inert parts of buffers.  */
+  block_input ();
+  struct frame *f = SELECTED_FRAME ();
+  if (!f->double_buffered)
+    flush_frame (f);
+  unblock_input ();
 }


@@ -24626,7 +24647,7 @@ init_glyph_string (struct glyph_string *s,
   s->hdc = hdc;
 #endif
   s->display = FRAME_X_DISPLAY (s->f);
-  s->window = FRAME_X_WINDOW (s->f);
+  s->window = FRAME_X_DRAWABLE (s->f);
   s->char2b = char2b;
   s->hl = hl;
   s->row = row;
@@ -28786,6 +28807,12 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum
draw_glyphs_face draw)
 	  unblock_input ();
 #endif	/* HAVE_WINDOW_SYSTEM */
 	}
+
+      /* In a double buffered frame, the drawing commands above will
+         have no immediate effect, so tell redisplay to do a buffer
+         flip.  */
+      if (FRAME_WINDOW_P (f) && f->double_buffered)
+        fset_redisplay (f);
     }

 #ifdef HAVE_WINDOW_SYSTEM
diff --git a/src/xfaces.c b/src/xfaces.c
index 5837f35..accb98b 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -495,7 +495,7 @@ x_create_gc (struct frame *f, unsigned long mask,
XGCValues *xgcv)
 {
   GC gc;
   block_input ();
-  gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), mask, xgcv);
+  gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), mask, xgcv);
   unblock_input ();
   IF_DEBUG (++ngcs);
   return gc;
diff --git a/src/xfns.c b/src/xfns.c
index 8571d0e..6026043 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -53,6 +53,10 @@ along with GNU Emacs.  If not, see
<http://www.gnu.org/licenses/>.  */
 #include "gtkutil.h"
 #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
 #ifdef USE_X_TOOLKIT
 #include <X11/Shell.h>

@@ -701,6 +705,35 @@ x_set_tool_bar_position (struct frame *f,
     wrong_choice (choice, new_value);
 }

+static void
+x_set_inhibit_double_buffering (struct frame *f,
+                                Lisp_Object new_value,
+                                Lisp_Object old_value)
+{
+  block_input ();
+  if (FRAME_X_WINDOW (f) && !EQ (new_value, old_value))
+    {
+      bool want_double_buffering = NILP (new_value);
+      bool was_double_buffered = FRAME_X_DOUBLE_BUFFERED_P (f);
+      /* font_drop_xrender_surfaces in xftfont does something only if
+         we're double-buffered, so call font_drop_xrender_surfaces before
+         and after any potential change.  One of the calls will end up
+         being a no-op.  */
+      if (want_double_buffering != was_double_buffered)
+        font_drop_xrender_surfaces (f);
+      if (FRAME_X_DOUBLE_BUFFERED_P (f) && !want_double_buffering)
+        tear_down_x_back_buffer (f);
+      else if (!FRAME_X_DOUBLE_BUFFERED_P (f) && want_double_buffering)
+        set_up_x_back_buffer (f);
+      if (FRAME_X_DOUBLE_BUFFERED_P (f) != was_double_buffered)
+        {
+          SET_FRAME_GARBAGED (f);
+          font_drop_xrender_surfaces (f);
+        }
+    }
+  unblock_input ();
+}
+
 #ifdef USE_GTK

 /* Set icon from FILE for frame F.  By using GTK functions the icon
@@ -2483,6 +2516,69 @@ xic_set_xfontset (struct frame *f, const char
*base_fontname)


 \f
+
+void
+set_up_x_back_buffer (struct frame *f)
+{
+#ifdef HAVE_XDBE
+  block_input ();
+  if (FRAME_X_WINDOW (f) && !FRAME_X_DOUBLE_BUFFERED_P (f))
+    {
+      eassert (!f->double_buffered);
+      FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
+      if (FRAME_DISPLAY_INFO (f)->supports_xdbe)
+        {
+          /* If allocating a back buffer fails, either because the
+             server ran out of memory or we don't have the right kind
+             of visual, just use single-buffered rendering.  */
+          x_catch_errors (FRAME_X_DISPLAY (f));
+          FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName (
+            FRAME_X_DISPLAY (f),
+            FRAME_X_WINDOW (f),
+            XdbeCopied);
+          if (x_had_errors_p (FRAME_X_DISPLAY (f)))
+            FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
+          else
+              f->double_buffered = true;
+          x_uncatch_errors_after_check ();
+        }
+    }
+  unblock_input ();
+#endif
+}
+
+void
+tear_down_x_back_buffer (struct frame *f)
+{
+#ifdef HAVE_XDBE
+  block_input ();
+  if (FRAME_X_WINDOW (f) && FRAME_X_DOUBLE_BUFFERED_P (f))
+    {
+      if (FRAME_X_DOUBLE_BUFFERED_P (f))
+        {
+          XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f),
+                                        FRAME_X_DRAWABLE (f));
+          FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
+          f->double_buffered = false;
+        }
+    }
+  unblock_input ();
+#endif
+}
+
+/* Set up double buffering if the frame parameters don't prohibit
+   it.  */
+void
+initial_set_up_x_back_buffer (struct frame *f)
+{
+  block_input ();
+  eassert (FRAME_X_WINDOW (f));
+  FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f);
+  if (NILP (CDR (Fassq (Qinhibit_double_buffering, f->param_alist))))
+    set_up_x_back_buffer (f);
+  unblock_input ();
+}
+
 #ifdef USE_X_TOOLKIT

 /* Create and set up the X widget for frame F.  */
@@ -2638,7 +2734,7 @@ x_window (struct frame *f, long window_prompting)
 		     f->output_data.x->parent_desc, 0, 0);

   FRAME_X_WINDOW (f) = XtWindow (frame_widget);
-
+  initial_set_up_x_back_buffer (f);
   validate_x_resource_name ();

   class_hints.res_name = SSDATA (Vx_resource_name);
@@ -2785,6 +2881,7 @@ x_window (struct frame *f)
 		     InputOutput, /* class */
 		     FRAME_X_VISUAL (f),
                      attribute_mask, &attributes);
+  initial_set_up_x_back_buffer (f);

 #ifdef HAVE_X_I18N
   if (use_xim)
@@ -2938,7 +3035,7 @@ x_make_gc (struct frame *f)
   gc_values.line_width = 0;	/* Means 1 using fast algorithm.  */
   f->output_data.x->normal_gc
     = XCreateGC (FRAME_X_DISPLAY (f),
-		 FRAME_X_WINDOW (f),
+                 FRAME_X_DRAWABLE (f),
 		 GCLineWidth | GCForeground | GCBackground,
 		 &gc_values);

@@ -2947,7 +3044,7 @@ x_make_gc (struct frame *f)
   gc_values.background = FRAME_FOREGROUND_PIXEL (f);
   f->output_data.x->reverse_gc
     = XCreateGC (FRAME_X_DISPLAY (f),
-		 FRAME_X_WINDOW (f),
+                 FRAME_X_DRAWABLE (f),
 		 GCForeground | GCBackground | GCLineWidth,
 		 &gc_values);

@@ -2956,7 +3053,7 @@ x_make_gc (struct frame *f)
   gc_values.background = f->output_data.x->cursor_pixel;
   gc_values.fill_style = FillOpaqueStippled;
   f->output_data.x->cursor_gc
-    = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+    = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 		 (GCForeground | GCBackground
 		  | GCFillStyle | GCLineWidth),
 		 &gc_values);
@@ -3463,6 +3560,9 @@ This function is an internal primitive--use
`make-frame' instead.  */)
 		       "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN);
   x_default_parameter (f, parms, Qtool_bar_position,
                        FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL);
+  x_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+                       "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+                       RES_TYPE_BOOLEAN);

   /* Compute the size of the X window.  */
   window_prompting = x_figure_window_size (f, parms, true, &x_width,
&x_height);
@@ -5637,6 +5737,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo,
Lisp_Object parms)
 		       f->border_width,
 		       CopyFromParent, InputOutput, CopyFromParent,
                        mask, &attrs);
+    initial_set_up_x_back_buffer (f);
     XChangeProperty (FRAME_X_DISPLAY (f), tip_window,
                      FRAME_DISPLAY_INFO (f)->Xatom_net_window_type,
                      XA_ATOM, 32, PropModeReplace,
@@ -6213,6 +6314,15 @@ Value is t if tooltip was open, nil otherwise.  */)
   return x_hide_tip (!tooltip_reuse_hidden_frame);
 }

+DEFUN ("x-double-buffered-p", Fx_double_buffered_p, Sx_double_buffered_p,
+       0, 1, 0,
+       doc: /* Return t if FRAME is being double buffered.  */)
+     (Lisp_Object frame)
+{
+  struct frame *f = decode_live_frame (frame);
+  return FRAME_X_DOUBLE_BUFFERED_P (f) ? Qt : Qnil;
+}
+
 \f
 /***********************************************************************
 			File selection dialog
@@ -6864,6 +6974,7 @@ frame_parm_handler x_frame_parm_handlers[] =
   x_set_alpha,
   x_set_sticky,
   x_set_tool_bar_position,
+  x_set_inhibit_double_buffering,
 };

 void
@@ -7080,6 +7191,7 @@ When using Gtk+ tooltips, the tooltip face is not
used.  */);

   defsubr (&Sx_show_tip);
   defsubr (&Sx_hide_tip);
+  defsubr (&Sx_double_buffered_p);
   tip_timer = Qnil;
   staticpro (&tip_timer);
   tip_frame = Qnil;
diff --git a/src/xfont.c b/src/xfont.c
index 45b0e0a..c2b7317 100644
--- a/src/xfont.c
+++ b/src/xfont.c
@@ -1057,20 +1057,20 @@ xfont_draw (struct glyph_string *s, int from, int
to, int x, int y,
 	{
 	  if (s->padding_p)
 	    for (i = 0; i < len; i++)
-	      XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+              XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE
(s->f),
 				gc, x + i, y, str + i, 1);
 	  else
-	    XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+            XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE
(s->f),
 			      gc, x, y, str, len);
 	}
       else
 	{
 	  if (s->padding_p)
 	    for (i = 0; i < len; i++)
-	      XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+              XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
 			   gc, x + i, y, str + i, 1);
 	  else
-	    XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+            XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
 			 gc, x, y, str, len);
 	}
       unblock_input ();
@@ -1083,20 +1083,20 @@ xfont_draw (struct glyph_string *s, int from, int
to, int x, int y,
     {
       if (s->padding_p)
 	for (i = 0; i < len; i++)
-	  XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+          XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE
(s->f),
 			      gc, x + i, y, s->char2b + from + i, 1);
       else
-	XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+        XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
 			    gc, x, y, s->char2b + from, len);
     }
   else
     {
       if (s->padding_p)
 	for (i = 0; i < len; i++)
-	  XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+          XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
 			 gc, x + i, y, s->char2b + from + i, 1);
       else
-	XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f),
+        XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f),
 		       gc, x, y, s->char2b + from, len);
     }
   unblock_input ();
diff --git a/src/xftfont.c b/src/xftfont.c
index 34c6f7d..e9c4fde 100644
--- a/src/xftfont.c
+++ b/src/xftfont.c
@@ -586,7 +586,7 @@ xftfont_get_xft_draw (struct frame *f)
     {
       block_input ();
       xft_draw= XftDrawCreate (FRAME_X_DISPLAY (f),
-			       FRAME_X_WINDOW (f),
+                               FRAME_X_DRAWABLE (f),
 			       FRAME_X_VISUAL (f),
 			       FRAME_X_COLORMAP (f));
       unblock_input ();
@@ -600,6 +600,8 @@ static int
 xftfont_draw (struct glyph_string *s, int from, int to, int x, int y,
               bool with_background)
 {
+  block_input ();
+
   struct frame *f = s->f;
   struct face *face = s->face;
   struct xftfont_info *xftfont_info = (struct xftfont_info *) s->font;
@@ -614,7 +616,6 @@ xftfont_draw (struct glyph_string *s, int from, int
to, int x, int y,
     xftface_info = (struct xftface_info *) face->extra;
   xftfont_get_colors (f, face, s->gc, xftface_info,
 		      &fg, with_background ? &bg : NULL);
-  block_input ();
   if (s->num_clips > 0)
     XftDrawSetClipRectangles (xft_draw, 0, 0, s->clip, s->num_clips);
   else
@@ -654,7 +655,6 @@ xftfont_draw (struct glyph_string *s, int from, int
to, int x, int y,
     XftDrawGlyphs (xft_draw, &fg, xftfont_info->xftfont,
 		   x, y, code, len);
   unblock_input ();
-
   return len;
 }

@@ -678,13 +678,10 @@ xftfont_shape (Lisp_Object lgstring)
 static int
 xftfont_end_for_frame (struct frame *f)
 {
+  block_input ();
   XftDraw *xft_draw;

-  /* Don't do anything if display is dead */
-  if (FRAME_X_DISPLAY (f) == NULL) return 0;
-
   xft_draw = font_get_frame_data (f, Qxft);
-
   if (xft_draw)
     {
       block_input ();
@@ -692,9 +689,19 @@ xftfont_end_for_frame (struct frame *f)
       unblock_input ();
       font_put_frame_data (f, Qxft, NULL);
     }
+  unblock_input ();
   return 0;
 }

+static void
+xftfont_drop_xrender_surfaces (struct frame *f)
+{
+  block_input ();
+  if (FRAME_X_DOUBLE_BUFFERED_P (f))
+    xftfont_end_for_frame (f);
+  unblock_input ();
+}
+
 static bool
 xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object,
                         Lisp_Object entity)
@@ -777,6 +784,9 @@ This is needed with some fonts to correct vertical
overlap of glyphs.  */);
 #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF)
   xftfont_driver.shape = xftfont_shape;
 #endif
+  // When using X double buffering, the XftDraw structure we
+  // build seems to be useless once a frame is resized, so
+  xftfont_driver.drop_xrender_surfaces = xftfont_drop_xrender_surfaces;

   register_font_driver (&xftfont_driver, NULL);
 }
diff --git a/src/xterm.c b/src/xterm.c
index 7476694..96dc8e6 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -45,6 +45,10 @@ along with GNU Emacs.  If not, see
<http://www.gnu.org/licenses/>.  */
 #include <X11/extensions/Xrender.h>
 #endif

+#ifdef HAVE_XDBE
+#include <X11/extensions/Xdbe.h>
+#endif
+
 /* Load sys/types.h if not already loaded.
    In some systems loading it twice is suicidal.  */
 #ifndef makedev
@@ -360,7 +364,7 @@ x_begin_cr_clip (struct frame *f, GC gc)
         {
           cairo_surface_t *surface;
           surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
-                                               FRAME_X_WINDOW (f),
+                                               FRAME_X_DRAWABLE (f),
                                                FRAME_DISPLAY_INFO
(f)->visual,
                                                FRAME_PIXEL_WIDTH (f),
                                                FRAME_PIXEL_HEIGHT (f));
@@ -722,7 +726,7 @@ x_fill_rectangle (struct frame *f, GC gc, int x, int
y, int width, int height)
   cairo_fill (cr);
   x_end_cr_clip (f);
 #else
-  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 		  gc, x, y, width, height);
 #endif
 }
@@ -740,7 +744,7 @@ x_draw_rectangle (struct frame *f, GC gc, int x, int
y, int width, int height)
   cairo_stroke (cr);
   x_end_cr_clip (f);
 #else
-  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 		  gc, x, y, width, height);
 #endif
 }
@@ -756,6 +760,9 @@ x_clear_window (struct frame *f)
   cairo_paint (cr);
   x_end_cr_clip (f);
 #else
+  if (FRAME_X_DOUBLE_BUFFERED_P (f))
+    x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
+  else
     XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
 #endif
 }
@@ -1067,7 +1074,7 @@ x_draw_vertical_window_border (struct window *w, int
x, int y0, int y1)
 #ifdef USE_CAIRO
   x_fill_rectangle (f, f->output_data.x->normal_gc, x, y0, 1, y1 - y0);
 #else
-  XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
+  XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 	     f->output_data.x->normal_gc, x, y0, x, y1);
 #endif
 }
@@ -1175,6 +1182,39 @@ x_update_window_end (struct window *w, bool
cursor_on_p,
     }
 }

+/* Show the frame back buffer.  If frame is double-buffered,
+   atomically publish to the user's screen graphics updates made since
+   the last call to show_back_buffer.  */
+static void
+show_back_buffer (struct frame *f)
+{
+  block_input ();
+  if (FRAME_X_DOUBLE_BUFFERED_P (f))
+    {
+#ifdef HAVE_XDBE
+      XdbeSwapInfo swap_info;
+      memset (&swap_info, 0, sizeof (swap_info));
+      swap_info.swap_window = FRAME_X_WINDOW (f);
+      swap_info.swap_action = XdbeCopied;
+      XdbeSwapBuffers (FRAME_X_DISPLAY (f), &swap_info, 1);
+#else
+      eassert (!"should have back-buffer only with XDBE");
+#endif
+      /* If we don't flush here, we don't respond in a timely way to
+         interactive frame resizing.  */
+      x_flush (f);
+    }
+  unblock_input ();
+}
+
+/* Updates back buffer and flushes changes to display.  Called
+   from minibuf read code.  */
+static void
+x_flip_and_flush (struct frame *f)
+{
+  show_back_buffer (f);
+  x_flush (f);
+}

 /* End update of frame F.  This function is installed as a hook in
    update_end.  */
@@ -1207,7 +1247,7 @@ x_update_end (struct frame *f)
           if (! FRAME_EXTERNAL_MENU_BAR (f))
             height += FRAME_MENU_BAR_HEIGHT (f);
           surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f),
-                                               FRAME_X_WINDOW (f),
+                                               FRAME_X_DRAWABLE (f),
                                                FRAME_DISPLAY_INFO
(f)->visual,
                                                width,
                                                height);
@@ -1220,7 +1260,7 @@ x_update_end (struct frame *f)
       cairo_destroy (cr);
       unblock_input ();
     }
-#endif /* USE_CAIRO */
+#endif

 #ifndef XFlush
   block_input ();
@@ -1229,15 +1269,17 @@ x_update_end (struct frame *f)
 #endif
 }

-
 /* This function is called from various places in xdisp.c
    whenever a complete update has been performed.  */

 static void
 XTframe_up_to_date (struct frame *f)
 {
+  block_input ();
   if (FRAME_X_P (f))
     FRAME_MOUSE_UPDATE (f);
+  show_back_buffer (f);
+  unblock_input ();
 }


@@ -1354,7 +1396,7 @@ x_draw_fringe_bitmap (struct window *w, struct
glyph_row *row, struct draw_fring
 #else  /* not USE_CAIRO */
   if (p->which)
     {
-      Window window = FRAME_X_WINDOW (f);
+      Drawable drawable = FRAME_X_DRAWABLE (f);
       char *bits;
       Pixmap pixmap, clipmask = (Pixmap) 0;
       int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f));
@@ -1367,7 +1409,7 @@ x_draw_fringe_bitmap (struct window *w, struct
glyph_row *row, struct draw_fring

       /* Draw the bitmap.  I believe these small pixmaps can be cached
 	 by the server.  */
-      pixmap = XCreatePixmapFromBitmapData (display, window, bits, p->wd,
p->h,
+      pixmap = XCreatePixmapFromBitmapData (display, drawable, bits,
p->wd, p->h,
 					    (p->cursor_p
 					     ? (p->overlay_p ? face->background
 						: f->output_data.x->cursor_pixel)
@@ -1386,7 +1428,7 @@ x_draw_fringe_bitmap (struct window *w, struct
glyph_row *row, struct draw_fring
 	  XChangeGC (display, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin,
&gcv);
 	}

-      XCopyArea (display, pixmap, window, gc, 0, 0,
+      XCopyArea (display, pixmap, drawable, gc, 0, 0,
 		 p->wd, p->h, p->x, p->y);
       XFreePixmap (display, pixmap);

@@ -2565,7 +2607,7 @@ x_setup_relief_color (struct frame *f, struct relief
*relief, double factor,
     {
       xgcv.stipple = dpyinfo->gray;
       mask |= GCStipple;
-      relief->gc = XCreateGC (dpy, FRAME_X_WINDOW (f), mask, &xgcv);
+      relief->gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), mask, &xgcv);
     }
   else
     XChangeGC (dpy, relief->gc, mask, &xgcv);
@@ -2696,7 +2738,7 @@ x_draw_relief_rect (struct frame *f,
   x_reset_clip_rectangles (f, bottom_right_gc);
 #else
   Display *dpy = FRAME_X_DISPLAY (f);
-  Window window = FRAME_X_WINDOW (f);
+  Drawable drawable = FRAME_X_DRAWABLE (f);
   int i;
   GC gc;

@@ -2715,12 +2757,12 @@ x_draw_relief_rect (struct frame *f,
   if (top_p)
     {
       if (width == 1)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
 		   left_x + left_p, top_y,
 		   right_x + !right_p, top_y);

       for (i = 1; i < width; ++i)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
 		   left_x  + i * left_p, top_y + i,
 		   right_x + 1 - i * right_p, top_y + i);
     }
@@ -2729,13 +2771,13 @@ x_draw_relief_rect (struct frame *f,
   if (left_p)
     {
       if (width == 1)
-	XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y);
+        XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y);

-      XClearArea (dpy, window, left_x, top_y, 1, 1, False);
-      XClearArea (dpy, window, left_x, bottom_y, 1, 1, False);
+      x_clear_area(f, left_x, top_y, 1, 1);
+      x_clear_area(f, left_x, bottom_y, 1, 1);

       for (i = (width > 1 ? 1 : 0); i < width; ++i)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
 		   left_x + i, top_y + (i + 1) * top_p,
 		   left_x + i, bottom_y + 1 - (i + 1) * bot_p);
     }
@@ -2751,23 +2793,23 @@ x_draw_relief_rect (struct frame *f,
     {
       /* Outermost top line.  */
       if (top_p)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
 		   left_x  + left_p, top_y,
 		   right_x + !right_p, top_y);

       /* Outermost left line.  */
       if (left_p)
-	XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y);
+        XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y);
     }

   /* Bottom.  */
   if (bot_p)
     {
-      XDrawLine (dpy, window, gc,
+      XDrawLine (dpy, drawable, gc,
 		 left_x + left_p, bottom_y,
 		 right_x + !right_p, bottom_y);
       for (i = 1; i < width; ++i)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
 		   left_x  + i * left_p, bottom_y - i,
 		   right_x + 1 - i * right_p, bottom_y - i);
     }
@@ -2775,10 +2817,10 @@ x_draw_relief_rect (struct frame *f,
   /* Right.  */
   if (right_p)
     {
-      XClearArea (dpy, window, right_x, top_y, 1, 1, False);
-      XClearArea (dpy, window, right_x, bottom_y, 1, 1, False);
+      x_clear_area(f, right_x, top_y, 1, 1);
+      x_clear_area(f, right_x, bottom_y, 1, 1);
       for (i = 0; i < width; ++i)
-	XDrawLine (dpy, window, gc,
+        XDrawLine (dpy, drawable, gc,
 		   right_x - i, top_y + (i + 1) * top_p,
 		   right_x - i, bottom_y + 1 - (i + 1) * bot_p);
     }
@@ -3741,7 +3783,7 @@ x_shift_glyphs_for_insert (struct frame *f, int x,
int y, int width, int height,
 /* Never called on a GUI frame, see
    http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html
 */
-  XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), FRAME_X_WINDOW (f),
+  XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE
(f),
 	     f->output_data.x->normal_gc,
 	     x, y, width, height,
 	     x + shift_by, y);
@@ -3782,6 +3824,12 @@ x_clear_area (struct frame *f, int x, int y, int
width, int height)
   cairo_fill (cr);
   x_end_cr_clip (f);
 #else
+    if (FRAME_X_DOUBLE_BUFFERED_P (f))
+      XFillRectangle (FRAME_X_DISPLAY (f),
+                      FRAME_X_DRAWABLE (f),
+                      f->output_data.x->reverse_gc,
+                      x, y, width, height);
+  else
     x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
                    x, y, width, height, False);
 #endif
@@ -3805,13 +3853,6 @@ x_clear_frame (struct frame *f)
      something like that, then they should be notified.  */
   x_scroll_bar_clear (f);

-#if defined (USE_GTK) && defined (USE_TOOLKIT_SCROLL_BARS)
-  /* Make sure scroll bars are redrawn.  As they aren't redrawn by
-     redisplay, do it here.  */
-  if (FRAME_GTK_WIDGET (f))
-    gtk_widget_queue_draw (FRAME_GTK_WIDGET (f));
-#endif
-
   XFlush (FRAME_X_DISPLAY (f));

   unblock_input ();
@@ -4109,7 +4150,7 @@ x_scroll_run (struct window *w, struct run *run)
   SET_FRAME_GARBAGED (f);
 #else
   XCopyArea (FRAME_X_DISPLAY (f),
-	     FRAME_X_WINDOW (f), FRAME_X_WINDOW (f),
+             FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f),
 	     f->output_data.x->normal_gc,
 	     x, from_y,
 	     width, height,
@@ -7766,12 +7807,35 @@ handle_one_xevent (struct x_display_info *dpyinfo,
         {
           if (!FRAME_VISIBLE_P (f))
             {
+              block_input ();
               SET_FRAME_VISIBLE (f, 1);
               SET_FRAME_ICONIFIED (f, false);
+              if (FRAME_X_DOUBLE_BUFFERED_P (f))
+                font_drop_xrender_surfaces (f);
               f->output_data.x->has_been_visible = true;
               SET_FRAME_GARBAGED (f);
+              unblock_input ();
             }
-          else
+          else if (FRAME_GARBAGED_P (f))
+            {
+#ifdef USE_GTK
+              /* Go around the back buffer and manually clear the
+                 window the first time we show it.  This way, we avoid
+                 showing users the sanity-defying horror of whatever
+                 GtkWindow is rendering beneath us.  We've garbaged
+                 the frame, so we'll redraw the whole thing on next
+                 redisplay anyway.  */
+              x_clear_area1 (
+                FRAME_X_DISPLAY (f),
+                FRAME_X_WINDOW (f),
+                event->xexpose.x, event->xexpose.y,
+                event->xexpose.width, event->xexpose.height,
+                0);
+#endif
+            }
+
+
+          if (!FRAME_GARBAGED_P (f))
             {
 #ifdef USE_GTK
               /* This seems to be needed for GTK 2.6 and later, see
@@ -7783,6 +7847,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
               expose_frame (f, event->xexpose.x, event->xexpose.y,
 			    event->xexpose.width, event->xexpose.height);
             }
+
+          if (!FRAME_GARBAGED_P (f))
+            show_back_buffer (f);
         }
       else
         {
@@ -7822,10 +7889,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                    available.  */
       f = x_window_to_frame (dpyinfo, event->xgraphicsexpose.drawable);
       if (f)
+        {
           expose_frame (f, event->xgraphicsexpose.x,
                         event->xgraphicsexpose.y,
                         event->xgraphicsexpose.width,
                         event->xgraphicsexpose.height);
+          show_back_buffer (f);
+        }
 #ifdef USE_X_TOOLKIT
       else
         goto OTHER;
@@ -8410,7 +8480,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
           else
 	    configureEvent = next_event;
         }
+
       f = x_top_window_to_frame (dpyinfo, configureEvent.xconfigure.window);
+      /* Unfortunately, we need to call font_drop_xrender_surfaces for
+         _all_ ConfigureNotify events, otherwise we miss some and
+         flicker.  Don't try to optimize these calls by looking only
+         for size changes: that's not sufficient.  We miss some
+         surface invalidations and flicker.  */
+      block_input ();
+      if (f && FRAME_X_DOUBLE_BUFFERED_P (f))
+        font_drop_xrender_surfaces (f);
+      unblock_input ();
 #ifdef USE_CAIRO
       if (f) x_cr_destroy_surface (f);
 #endif
@@ -8419,6 +8499,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
           && (f = any)
           && configureEvent.xconfigure.window == FRAME_X_WINDOW (f))
         {
+          block_input ();
+          if (FRAME_X_DOUBLE_BUFFERED_P (f))
+            font_drop_xrender_surfaces (f);
+          unblock_input ();
           xg_frame_resized (f, configureEvent.xconfigure.width,
                             configureEvent.xconfigure.height);
 #ifdef USE_CAIRO
@@ -8429,6 +8513,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
       if (f)
         {
+
           x_net_wm_state (f, configureEvent.xconfigure.window);

 #ifdef USE_X_TOOLKIT
@@ -8437,7 +8522,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
             {
               if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height
                   || FRAME_PIXEL_WIDTH (f) !=
configureEvent.xconfigure.width)
+                {
                   SET_FRAME_GARBAGED (f);
+                }
               FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height;
               FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width;
             }
@@ -8880,7 +8967,7 @@ x_draw_hollow_cursor (struct window *w, struct
glyph_row *row)
   if (dpyinfo->scratch_cursor_gc)
     XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv);
   else
-    dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_WINDOW (f),
+    dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f),
 					    GCForeground, &xgcv);
   gc = dpyinfo->scratch_cursor_gc;

@@ -8937,7 +9024,7 @@ x_draw_bar_cursor (struct window *w, struct
glyph_row *row, int width, enum text
   else
     {
       Display *dpy = FRAME_X_DISPLAY (f);
-      Window window = FRAME_X_WINDOW (f);
+      Drawable drawable = FRAME_X_DRAWABLE (f);
       GC gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc;
       unsigned long mask = GCForeground | GCBackground |
GCGraphicsExposures;
       struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
@@ -8958,7 +9045,7 @@ x_draw_bar_cursor (struct window *w, struct
glyph_row *row, int width, enum text
 	XChangeGC (dpy, gc, mask, &xgcv);
       else
 	{
-	  gc = XCreateGC (dpy, window, mask, &xgcv);
+          gc = XCreateGC (dpy, drawable, mask, &xgcv);
 	  FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
 	}

@@ -9028,11 +9115,6 @@ static void
 x_clear_frame_area (struct frame *f, int x, int y, int width, int height)
 {
   x_clear_area (f, x, y, width, height);
-#ifdef USE_GTK
-  /* Must queue a redraw, because scroll bars might have been cleared.  */
-  if (FRAME_GTK_WIDGET (f))
-    gtk_widget_queue_draw (FRAME_GTK_WIDGET (f));
-#endif
 }


@@ -10889,7 +10971,7 @@ x_make_frame_visible (struct frame *f)

   if (! FRAME_VISIBLE_P (f))
     {
-      /* We test FRAME_GARBAGED_P here to make sure we don't
+      /* We test asked_for_visible here to make sure we don't
          call x_set_offset a second time
          if we get to x_make_frame_visible a second time
 	 before the window gets really visible.  */
@@ -10935,6 +11017,8 @@ x_make_frame_visible (struct frame *f)
        will set it when they are handled.  */
     bool previously_visible = f->output_data.x->has_been_visible;

+    XSETFRAME (frame, f);
+
     original_left = f->left_pos;
     original_top = f->top_pos;

@@ -10981,8 +11065,6 @@ x_make_frame_visible (struct frame *f)
 	unblock_input ();
       }

-    XSETFRAME (frame, f);
-
     /* Process X events until a MapNotify event has been seen.  */
     while (!FRAME_VISIBLE_P (f))
       {
@@ -11227,6 +11309,7 @@ x_free_frame_resources (struct frame *f)
 	 font-driver (e.g. xft) access a window while finishing a
 	 face.  */
       free_frame_faces (f);
+      tear_down_x_back_buffer (f);

       if (f->output_data.x->icon_desc)
 	XDestroyWindow (FRAME_X_DISPLAY (f), f->output_data.x->icon_desc);
@@ -11270,6 +11353,7 @@ x_free_frame_resources (struct frame *f)
       xg_free_frame_widgets (f);
 #endif /* USE_GTK */

+      tear_down_x_back_buffer (f);
       if (FRAME_X_WINDOW (f))
           XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
 #endif /* !USE_X_TOOLKIT */
@@ -12113,6 +12197,14 @@ x_term_init (Lisp_Object display_name, char
*xrm_option, char *resource_name)
     dpyinfo->cmap = XCreateColormap (dpyinfo->display, dpyinfo->root_window,
                                      dpyinfo->visual, AllocNone);

+#ifdef HAVE_XDBE
+  dpyinfo->supports_xdbe = false;
+  int xdbe_major;
+  int xdbe_minor;
+  if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor))
+    dpyinfo->supports_xdbe = true;
+#endif
+
 #ifdef HAVE_XFT
   {
     /* If we are using Xft, the following precautions should be made:
@@ -12462,7 +12554,7 @@ static struct redisplay_interface
x_redisplay_interface =
     x_after_update_window_line,
     x_update_window_begin,
     x_update_window_end,
-    x_flush,
+    x_flip_and_flush,
     x_clear_window_mouse_face,
     x_get_glyph_overhangs,
     x_fix_overlapping_area,
diff --git a/src/xterm.h b/src/xterm.h
index 675a484..734e58b 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -475,6 +475,10 @@ struct x_display_info
 #ifdef USE_XCB
   xcb_connection_t *xcb_connection;
 #endif
+
+#ifdef HAVE_XDBE
+  bool supports_xdbe;
+#endif
 };

 #ifdef HAVE_X_I18N
@@ -527,6 +531,11 @@ struct x_output
      and the X window has not yet been created.  */
   Window window_desc;

+  /* The drawable to which we're rendering.  In the single-buffered
+     base, the window itself.  In the double-buffered case, the
+     window's back buffer.  */
+  Drawable draw_desc;
+
   /* The X window used for the bitmap icon;
      or 0 if we don't have a bitmap icon.  */
   Window icon_desc;
@@ -737,6 +746,15 @@ enum
 /* Return the X window used for displaying data in frame F.  */
 #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc)

+/* Return the drawable used for rendering to frame F.  */
+#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc)
+
+#define FRAME_X_DOUBLE_BUFFERED_P(f)            \
+  (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f))
+
+/* Return the need-buffer-flip flag for frame F.  */
+#define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip)
+
 /* Return the outermost X window associated with the frame F.  */
 #ifdef USE_X_TOOLKIT
 #define FRAME_OUTER_WINDOW(f) ((f)->output_data.x->widget ?             \
@@ -1140,6 +1158,10 @@ extern bool x_wm_supports (struct frame *, Atom);
 extern void x_wait_for_event (struct frame *, int);
 extern void x_clear_under_internal_border (struct frame *f);

+extern void set_up_x_back_buffer (struct frame* f);
+extern void tear_down_x_back_buffer (struct frame* f);
+extern void initial_set_up_x_back_buffer (struct frame* f);
+
 /* Defined in xselect.c.  */

 extern void x_handle_property_notify (const XPropertyEvent *);





^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-27 19:06               ` dancol
@ 2016-10-27 19:36                 ` Eli Zaretskii
       [not found]                   ` <db81befd-7a72-58d9-b7a8-107df89bcab3@dancol.org>
  2016-10-27 22:18                 ` Paul Eggert
  1 sibling, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2016-10-27 19:36 UTC (permalink / raw)
  To: dancol; +Cc: raeburn, emacs-devel

> Date: Thu, 27 Oct 2016 12:06:25 -0700
> From: dancol@dancol.org
> Cc: Daniel Colascione <dancol@dancol.org>, Ken Raeburn <raeburn@raeburn.org>,
> 	emacs-devel@gnu.org
> 
> Below is a new much-improved version of the patch. It should address the
> legibility concerns while probably adding more. It addresses all the bugs
> I've been able to find and on my machine, produces buttery smooth editing
> and resizing.

Thanks.

> >> Why _do_ we have a path that short-circuits the rest of the redisplay
> >> code? What would happen if we just removed it? It appears to be some
> >> kind of optimization, and I'm not sure it's actually necessary
> >> (especially since, according to the comment, we disable it anyway in the
> >> case of a blinking cursor).
> >
> > It's an optimization for the case that nothing needs to be
> > redisplayed.
> 
> Isn't the "goto update" path fast enough?

That would still call hscroll_windows and update_frame, which is just
waste of cycles when we know nothing's changed.

> @@ -14072,6 +14072,17 @@ redisplay_internal (void)
>  		  if (!f_redisplay_flag && f->redisplay)
>                      goto retry_frame;
> 
> +                  /* In some case (e.g., window resize), we notice
> +                     only during window updating that the window
> +                     content changed unpredictably (e.g., a GTK
> +                     scrollbar moved) and that our previous estimation
> +                     of the frame content was garbage.  We have to
> +                     start over.  These cases should be rare, so going
> +                     all the way back to the top of redisplay should
> +                     be good enough.  */
> +                  if (FRAME_GARBAGED_P (f))
> +                    goto retry;

This worries me a bit: FRAME_GARBAGED_P returns non-zero for TTY
frames very frequently, whereas at least the comment seems to imply
this is needed only for GUI frames, perhaps even only in GTK builds.
If that is correct, then perhaps add a FRAME_WINDOW_P test here, to
avoid unnecessarily slowing down redisplay.

Thanks again for working on this.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
       [not found]                   ` <db81befd-7a72-58d9-b7a8-107df89bcab3@dancol.org>
@ 2016-10-27 19:56                     ` Daniel Colascione
  2016-10-28  6:31                       ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel Colascione @ 2016-10-27 19:56 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: raeburn, emacs-devel

On 10/27/2016 12:44 PM, Daniel Colascione wrote:
> On 10/27/2016 12:36 PM, Eli Zaretskii wrote:
>>> Date: Thu, 27 Oct 2016 12:06:25 -0700
>>> From: dancol@dancol.org
>>> Cc: Daniel Colascione <dancol@dancol.org>, Ken Raeburn
>>> <raeburn@raeburn.org>,
>>>     emacs-devel@gnu.org
>>>
>>> Below is a new much-improved version of the patch. It should address the
>>> legibility concerns while probably adding more. It addresses all the
>>> bugs
>>> I've been able to find and on my machine, produces buttery smooth
>>> editing
>>> and resizing.
>>
>> Thanks.
>>
>>>>> Why _do_ we have a path that short-circuits the rest of the redisplay
>>>>> code? What would happen if we just removed it? It appears to be some
>>>>> kind of optimization, and I'm not sure it's actually necessary
>>>>> (especially since, according to the comment, we disable it anyway
>>>>> in the
>>>>> case of a blinking cursor).
>>>>
>>>> It's an optimization for the case that nothing needs to be
>>>> redisplayed.
>>>
>>> Isn't the "goto update" path fast enough?
>>
>> That would still call hscroll_windows and update_frame, which is just
>> waste of cycles when we know nothing's changed.
>>
>>> @@ -14072,6 +14072,17 @@ redisplay_internal (void)
>>>            if (!f_redisplay_flag && f->redisplay)
>>>                      goto retry_frame;
>>>
>>> +                  /* In some case (e.g., window resize), we notice
>>> +                     only during window updating that the window
>>> +                     content changed unpredictably (e.g., a GTK
>>> +                     scrollbar moved) and that our previous estimation
>>> +                     of the frame content was garbage.  We have to
>>> +                     start over.  These cases should be rare, so going
>>> +                     all the way back to the top of redisplay should
>>> +                     be good enough.  */
>>> +                  if (FRAME_GARBAGED_P (f))
>>> +                    goto retry;
>>
>> This worries me a bit: FRAME_GARBAGED_P returns non-zero for TTY
>> frames very frequently, whereas at least the comment seems to imply
>> this is needed only for GUI frames, perhaps even only in GTK builds.
>> If that is correct, then perhaps add a FRAME_WINDOW_P test here, to
>> avoid unnecessarily slowing down redisplay.
>
> Sure.
>

By the way: why would we ever _want_ to allow redisplay to return 
without having redrawn all garbaged frames? The design of redisplay 
suggests that a postcondition on redisplay_internal should be that 
there's nothing left to draw, at least in the case that we weren't 
interrupted by input.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-27 19:06               ` dancol
  2016-10-27 19:36                 ` Eli Zaretskii
@ 2016-10-27 22:18                 ` Paul Eggert
  2016-10-27 22:46                   ` Clément Pit--Claudel
  2016-10-27 23:10                   ` Daniel Colascione
  1 sibling, 2 replies; 31+ messages in thread
From: Paul Eggert @ 2016-10-27 22:18 UTC (permalink / raw)
  To: Daniel Colascione; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1155 bytes --]

Thanks for working on this.

I tried building with that patch, and had problems because your email 
client munged the patch. I cleaned it up by hand and arrived at the 
attached patch instead. (It needs a better commit message of course.) 
Maybe next time you could attach the output of git-format-patch.

Although the resulting system builds under Fedora 24 x86-64 with GTK, I 
can't tell any difference in flickering. I don't see much flicker 
without the patch, or with it. (I am using X, not Wayland.) Maybe my 
eyes are not good enough....

I don't see the need to expose double-buffering to Lisp via 
x-double-buffered-p or inhibit-double-buffering. Unless there's a 
demonstrated need, I'd remove these from the Lisp interface, to simplify it.

The configure.ac change should use AC_CACHE_CHECK.

set_up_x_back_buffer should be a static function, since it's not used 
outside its module.

In C code we typically use comment /* like this */, not // like this. 
Also, the spacing for pointer decls is typically 'type *id', not 'type * 
id' or 'type* id'.

You can assume C99 for loop control, e.g., 'for (int i = 0; i < n; i++) 
...'.

Thanks again.

[-- Attachment #2: 0001-flicker-free-double-buffering.patch --]
[-- Type: application/x-patch, Size: 61830 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-27 22:18                 ` Paul Eggert
@ 2016-10-27 22:46                   ` Clément Pit--Claudel
  2016-10-28 13:14                     ` Stefan Monnier
  2016-10-27 23:10                   ` Daniel Colascione
  1 sibling, 1 reply; 31+ messages in thread
From: Clément Pit--Claudel @ 2016-10-27 22:46 UTC (permalink / raw)
  To: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 471 bytes --]

On 2016-10-27 18:18, Paul Eggert wrote:
> Although the resulting system builds under Fedora 24 x86-64 with GTK,
> I can't tell any difference in flickering. I don't see much flicker
> without the patch, or with it. (I am using X, not Wayland.) Maybe my
> eyes are not good enough....

I get a lot of flickering myself on Linux Mint with Gtk; often in font-lock heavy buffers or while M-x compile is running.  This patch solves all of it :)

Cheers,
Clément.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-27 22:18                 ` Paul Eggert
  2016-10-27 22:46                   ` Clément Pit--Claudel
@ 2016-10-27 23:10                   ` Daniel Colascione
  2016-10-28  2:07                     ` Paul Eggert
  1 sibling, 1 reply; 31+ messages in thread
From: Daniel Colascione @ 2016-10-27 23:10 UTC (permalink / raw)
  To: Paul Eggert; +Cc: emacs-devel

Paul Eggert <eggert@cs.ucla.edu> writes:

> Thanks for working on this.
>
> I tried building with that patch, and had problems because your email
> client munged the patch. I cleaned it up by hand and arrived at the
> attached patch instead. (It needs a better commit message of course.)
> Maybe next time you could attach the output of git-format-patch.

I need to get Gnus working on that machine. I'd hoped Squirrelmail would
have been too simplistic to munge the patch, but I was wrong.

> Although the resulting system builds under Fedora 24 x86-64 with GTK,
> I can't tell any difference in flickering. I don't see much flicker
> without the patch, or with it. (I am using X, not Wayland.) Maybe my
> eyes are not good enough....

I've found the flickering while editing to be very machine-dependent. It
was driving me nuts on mine, which is why I wrote the patch. Flickering
while resizing seems to be a more general problem though.

I've found I can reliably reproduce the flickering by isearching for a
string that appears in a buffer only once, then leaning on C-s.

> I don't see the need to expose double-buffering to Lisp via
> x-double-buffered-p or inhibit-double-buffering. Unless there's a
> demonstrated need, I'd remove these from the Lisp interface, to
> simplify it.

I'll feel more comfortable about removing the lisp interface (and so any
way for users to this the thing off) once we're sure it's not
causing problems.

> The configure.ac change should use AC_CACHE_CHECK.

None of the other X extension checks do. I'm sure AC_CACHE_CHECK is
great, but it feels out of scope.

> set_up_x_back_buffer should be a static function, since it's not used
> outside its module.

Thanks; it wasn't always internal to that file. I'll fix the function.

> In C code we typically use comment /* like this */, not // like
> this. Also, the spacing for pointer decls is typically 'type *id', not
> 'type * id' or 'type* id'.

Thanks. I thought I'd gotten rid of all of those. Speaking of comments
though: if we're C99-only anyway now, we might as well allow
C99 comments.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-27 23:10                   ` Daniel Colascione
@ 2016-10-28  2:07                     ` Paul Eggert
  2016-10-28  7:19                       ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Paul Eggert @ 2016-10-28  2:07 UTC (permalink / raw)
  To: Daniel Colascione; +Cc: emacs-devel

Daniel Colascione wrote:

> I've found the flickering while editing to be very machine-dependent....
> I've found I can reliably reproduce the flickering by isearching for a
> string that appears in a buffer only once, then leaning on C-s.

Weird. I can't reproduce the flickering problem on either Fedora 24 GTK x86-64 
or Ubuntu 16.04.1 GTK x86-64, even though I tried your recipe. I wonder what's 
different about my platforms? Anyway, if the code is fixing a real problem on 
your platforms and doesn't cause trouble elsewhere, we should install it.

> I'll feel more comfortable about removing the lisp interface (and so any
> way for users to this the thing off) once we're sure it's not
> causing problems.

Perhaps mark it as deprecated so that the user knows it might go away?

> if we're C99-only anyway now, we might as well allow
> C99 comments.

Maybe, but that would be a different topic, and should be addressed in a 
separate patch. For example, we might also need to change our C-reading programs.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-27 19:56                     ` Daniel Colascione
@ 2016-10-28  6:31                       ` Eli Zaretskii
  0 siblings, 0 replies; 31+ messages in thread
From: Eli Zaretskii @ 2016-10-28  6:31 UTC (permalink / raw)
  To: Daniel Colascione; +Cc: raeburn, emacs-devel

> Cc: raeburn@raeburn.org, emacs-devel@gnu.org
> From: Daniel Colascione <dancol@dancol.org>
> Date: Thu, 27 Oct 2016 12:56:41 -0700
> 
> why would we ever _want_ to allow redisplay to return 
> without having redrawn all garbaged frames? The design of redisplay 
> suggests that a postcondition on redisplay_internal should be that 
> there's nothing left to draw, at least in the case that we weren't 
> interrupted by input.

Not at that point in the code, which is before update_frame was
called.  In particular, the place that detects whether input is
available is inside update_frame.

IOW, redisplay is not going to return there, it is going to call
update_frame, which is the last stage of the redisplay cycle: it
delivers stuff to the glass as needed.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-28  2:07                     ` Paul Eggert
@ 2016-10-28  7:19                       ` Eli Zaretskii
  2016-11-06  3:51                         ` Paul Eggert
  0 siblings, 1 reply; 31+ messages in thread
From: Eli Zaretskii @ 2016-10-28  7:19 UTC (permalink / raw)
  To: Paul Eggert; +Cc: dancol, emacs-devel

> From: Paul Eggert <eggert@cs.ucla.edu>
> Date: Thu, 27 Oct 2016 19:07:09 -0700
> Cc: emacs-devel@gnu.org
> 
> Daniel Colascione wrote:
> 
> > I've found the flickering while editing to be very machine-dependent....
> > I've found I can reliably reproduce the flickering by isearching for a
> > string that appears in a buffer only once, then leaning on C-s.
> 
> Weird. I can't reproduce the flickering problem on either Fedora 24 GTK x86-64 
> or Ubuntu 16.04.1 GTK x86-64, even though I tried your recipe. I wonder what's 
> different about my platforms?

My guess is either your systems are too slow, or the keyboard
auto-repeat rate is too high.  This stuff is very timing-dependent,
and in this particular case it depends on the relative speed of
redisplay and keyboard auto-repeat.

> > if we're C99-only anyway now, we might as well allow
> > C99 comments.
> 
> Maybe, but that would be a different topic, and should be addressed in a 
> separate patch. For example, we might also need to change our C-reading programs.

Please note that we already have "//" comments in a few places, so if
we don't yet want to allow them, we should remove those.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-27 22:46                   ` Clément Pit--Claudel
@ 2016-10-28 13:14                     ` Stefan Monnier
  2016-11-01  0:03                       ` Dmitry Gutov
  0 siblings, 1 reply; 31+ messages in thread
From: Stefan Monnier @ 2016-10-28 13:14 UTC (permalink / raw)
  To: emacs-devel

> I get a lot of flickering myself on Linux Mint with Gtk; often in font-lock
> heavy buffers or while M-x compile is running.  This patch solves all of
> it :)

I also welcome this patch, of course, but for the record, such
flickering should be solved by getting rid of those extra "spurious"
updates, rather than by hiding them behind double buffering.


        Stefan




^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-28 13:14                     ` Stefan Monnier
@ 2016-11-01  0:03                       ` Dmitry Gutov
  0 siblings, 0 replies; 31+ messages in thread
From: Dmitry Gutov @ 2016-11-01  0:03 UTC (permalink / raw)
  To: Stefan Monnier, emacs-devel

On 28.10.2016 16:14, Stefan Monnier wrote:
>> I get a lot of flickering myself on Linux Mint with Gtk; often in font-lock
>> heavy buffers or while M-x compile is running.  This patch solves all of
>> it :)
>
> I also welcome this patch, of course, but for the record, such
> flickering should be solved by getting rid of those extra "spurious"
> updates, rather than by hiding them behind double buffering.

Spurious updates or not, according to a previous discussion, there's no 
reliable way to avoid flickers other than double-buffering:

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=16621#38

Even if we could optimize them out in some cases. As such, I very much 
welcome this patch as well.



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-10-28  7:19                       ` Eli Zaretskii
@ 2016-11-06  3:51                         ` Paul Eggert
  2016-11-06 15:46                           ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Paul Eggert @ 2016-11-06  3:51 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: emacs-devel

Eli Zaretskii wrote:
> Please note that we already have "//" comments in a few places, so if
> we don't yet want to allow them, we should remove those.

OK, I did that in the emacs-25 and master branches. If we start adding these 
comments, I suggest limiting them to code in the master branch and making the 
style change a conscious decision (e.g., when should code use "/*" vs "//" 
comments?).



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: RFC: flicker-free double-buffered Emacs under X11
  2016-11-06  3:51                         ` Paul Eggert
@ 2016-11-06 15:46                           ` Eli Zaretskii
  0 siblings, 0 replies; 31+ messages in thread
From: Eli Zaretskii @ 2016-11-06 15:46 UTC (permalink / raw)
  To: Paul Eggert; +Cc: emacs-devel

> Cc: emacs-devel@gnu.org
> From: Paul Eggert <eggert@cs.ucla.edu>
> Date: Sat, 5 Nov 2016 20:51:11 -0700
> 
> Eli Zaretskii wrote:
> > Please note that we already have "//" comments in a few places, so if
> > we don't yet want to allow them, we should remove those.
> 
> OK, I did that in the emacs-25 and master branches.

Thanks.

> If we start adding these comments, I suggest limiting them to code
> in the master branch and making the style change a conscious
> decision (e.g., when should code use "/*" vs "//" comments?).

Agreed.



^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2016-11-06 15:46 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-10-21  1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione
2016-10-21  2:27 ` Lars Ingebrigtsen
2016-10-21  2:31   ` Daniel Colascione
2016-10-21  3:24 ` Óscar Fuentes
2016-10-21  3:31   ` Clément Pit--Claudel
2016-10-21  3:41     ` Óscar Fuentes
2016-10-21  3:50       ` Clément Pit--Claudel
2016-10-21  8:23     ` Andreas Schwab
2016-10-21  8:25       ` Andreas Schwab
2016-10-21  3:56 ` Clément Pit--Claudel
2016-10-21  8:49 ` Eli Zaretskii
2016-10-21 11:04   ` Daniel Colascione
2016-10-21 17:43     ` Eli Zaretskii
2016-10-21 18:27       ` Daniel Colascione
2016-10-21 19:27         ` Eli Zaretskii
2016-10-23 20:51           ` Daniel Colascione
2016-10-24  8:05             ` Eli Zaretskii
2016-10-24 18:43               ` Ken Raeburn
2016-10-27 19:06               ` dancol
2016-10-27 19:36                 ` Eli Zaretskii
     [not found]                   ` <db81befd-7a72-58d9-b7a8-107df89bcab3@dancol.org>
2016-10-27 19:56                     ` Daniel Colascione
2016-10-28  6:31                       ` Eli Zaretskii
2016-10-27 22:18                 ` Paul Eggert
2016-10-27 22:46                   ` Clément Pit--Claudel
2016-10-28 13:14                     ` Stefan Monnier
2016-11-01  0:03                       ` Dmitry Gutov
2016-10-27 23:10                   ` Daniel Colascione
2016-10-28  2:07                     ` Paul Eggert
2016-10-28  7:19                       ` Eli Zaretskii
2016-11-06  3:51                         ` Paul Eggert
2016-11-06 15:46                           ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).