emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: RFC: flicker-free double-buffered Emacs under X11


From: dancol
Subject: Re: RFC: flicker-free double-buffered Emacs under X11
Date: Thu, 27 Oct 2016 12:06:25 -0700
User-agent: SquirrelMail/1.4.23 [SVN]

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 <address@hidden>
>> Cc: address@hidden
>> 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  <address@hidden>
+
+       * 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 address@hidden, the frame is visible on all virtual desktops on systems
 with virtual desktops.

address@hidden inhibit-double-buffering, a frame parameter
address@hidden inhibit-double-buffering
+If address@hidden, 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}.

address@hidden x-double-buffered-p &optional frame
+This function returns address@hidden if @var{frame} is currently
+being rendered with double buffering.  @var{frame} defaults to the
+selected frame.
address@hidden 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.
 
 * 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.
 address@hidden@

@@ -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);
+
 
 /***********************************************************************
                       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 *);

 
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)


 
+
+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;
+}
+
 
 /***********************************************************************
                        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 *);





reply via email to

[Prev in Thread] Current Thread [Next in Thread]