RFC: flicker-free double-buffered Emacs under X11

From: Daniel Colascione Subject: RFC: flicker-free double-buffered Emacs under X11 Date: Thu, 20 Oct 2016 18:32:01 -0700 User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.3.0

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 <address@hidden> 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. 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..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

#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

#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)

+ +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

#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 *);

reply via email to

