* RFC: flicker-free double-buffered Emacs under X11 @ 2016-10-21 1:32 Daniel Colascione 2016-10-21 2:27 ` Lars Ingebrigtsen ` (3 more replies) 0 siblings, 4 replies; 31+ messages in thread From: Daniel Colascione @ 2016-10-21 1:32 UTC (permalink / raw) To: emacs-devel This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to avoid showing the user incomplete drawing results. Without this patch, I can make Emacs flicker like crazy by running isearch for a piece of text unique in a buffer and holding down C-s. With this patch, Emacs does not flicker no matter what I do to it. The patch also stops flickering that occurs when using the "solid resizing" feature of some window managers --- i.e., when the WM redraws windows as the user drags their edges, as opposed to displaying some kind of bounding-box in lieu of the actual window contents. I've tried to avoid changing the logic in the single-buffered case (which we still support), and I've also tried to avoid touching Cairo rendering. This patch should work with any toolkit --- I've tested GTK+3 and no toolkit at all. A few notes: * We do a buffer flip at the end of redisplay instead of in x_update_end() so the user never sees the completely-cleared state that we enter immediately after clear_garbaged_frames(). x_update_end() does do a buffer flip if it's called outside redisplay. I've added a new terminal hook to support this hack. * The DBE documentation claims that XClearWindow and XClearArea clear both the front and back buffers. It's a lie. In my experiments, these functions clear only the front buffer. * XFT stops drawing after we give XftCreateDraw a DBE back-buffer and the size of that back buffer changes. To work around this problem, we discard any caches XftDraw object we might have to a frame after that frame changes size. I haven't noticed any performance problems. commit 15fdd8f63533201f05627ede634a8f5ae4757d7e Author: Daniel Colascione <dancol@dancol.org> Date: Thu Oct 20 16:50:54 2016 -0700 Add double-buffered output support to Emacs diff --git a/configure.ac b/configure.ac index cd11b10..4716b43 100644 --- a/configure.ac +++ b/configure.ac @@ -3712,6 +3712,24 @@ AC_DEFUN AC_SUBST(XFIXES_CFLAGS) AC_SUBST(XFIXES_LIBS) +### Use Xdbe (-lXdbe) if available +HAVE_XDBE=no +if test "${HAVE_X11}" = "yes"; then + AC_CHECK_HEADER(X11/extensions/Xdbe.h, + [AC_CHECK_LIB(Xext, XdbeAllocateBackBufferName, HAVE_XDBE=yes)], + [], + [#include <X11/Xlib.h> + ]) + if test $HAVE_XDBE = yes; then + XDBE_LIBS=-lXext + fi + if test $HAVE_XDBE = yes; then + AC_DEFINE(HAVE_XDBE, 1, [Define to 1 if you have the Xdbe extension.]) + fi +fi +AC_SUBST(XDBE_CFLAGS) +AC_SUBST(XDBE_LIBS) + ### Use libxml (-lxml2) if available ### mingw32 doesn't use -lxml2, since it loads the library dynamically. HAVE_LIBXML2=no diff --git a/src/Makefile.in b/src/Makefile.in index 89f7a92..dc0bfff 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -254,6 +254,9 @@ XINERAMA_CFLAGS = XFIXES_LIBS = @XFIXES_LIBS@ XFIXES_CFLAGS = @XFIXES_CFLAGS@ +XDBE_LIBS = @XDBE_LIBS@ +XDBE_CFLAGS = @XDBE_CFLAGS@ + ## widget.o if USE_X_TOOLKIT, otherwise empty. WIDGET_OBJ=@WIDGET_OBJ@ @@ -372,7 +375,7 @@ ALL_CFLAGS= $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \ $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \ $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \ - $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \ + $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \ $(WEBKIT_CFLAGS) \ $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ @@ -489,6 +492,7 @@ LIBES = $(WEBKIT_LIBS) \ $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \ $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \ + $(XDBE_LIBS) \ $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \ $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ diff --git a/src/dispnew.c b/src/dispnew.c index 70d4de0..8f81cee 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f) { /* Error if F has no glyphs. */ eassert (f->glyphs_initialized_p); + font_flush_frame_caches (f); update_begin (f); if (FRAME_MSDOS_P (f)) FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f)); diff --git a/src/font.c b/src/font.c index f8e6794..033995e 100644 --- a/src/font.c +++ b/src/font.c @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, Lisp_Object arg, Lisp_Object result) } void +font_flush_frame_caches (struct frame *f) +{ + struct font_driver_list *list; + + for (list = f->font_driver_list; list; list = list->next) + if (list->on && list->driver->flush_frame_caches) + list->driver->flush_frame_caches (f); +} + +void syms_of_font (void) { sort_shift_bits[FONT_TYPE_INDEX] = 0; diff --git a/src/font.h b/src/font.h index cf47729..961e9c4 100644 --- a/src/font.h +++ b/src/font.h @@ -763,6 +763,12 @@ struct font_driver Return non-nil if the driver support rendering of combining characters for FONT according to Unicode combining class. */ Lisp_Object (*combining_capability) (struct font *font); + + /* Optional + + Called when frame F is redrawn from scratch. Font engines may + invalidate certain caches in this case. */ + void (*flush_frame_caches) (struct frame *f); }; @@ -862,7 +868,9 @@ extern void *font_get_frame_data (struct frame *f, Lisp_Object); extern void font_filter_properties (Lisp_Object font, Lisp_Object alist, const char *const boolean_properties[], - const char *const non_boolean_properties[]); + const char *const non_boolean_properties[]); + +extern void font_flush_frame_caches (struct frame *f); #ifdef HAVE_FREETYPE extern struct font_driver ftfont_driver; diff --git a/src/ftxfont.c b/src/ftxfont.c index f49d44f..bfdeb40 100644 --- a/src/ftxfont.c +++ b/src/ftxfont.c @@ -95,7 +95,7 @@ ftxfont_get_gcs (struct frame *f, unsigned long foreground, unsigned long backgr if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &color)) break; xgcv.foreground = color.pixel; - new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), GCForeground, &xgcv); } unblock_input (); @@ -139,14 +139,14 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, p[n[0]].y = y - bitmap.top + i; if (++n[0] == size) { - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p, size, CoordModeOrigin); n[0] = 0; } } } if (flush && n[0] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p, n[0], CoordModeOrigin); } else @@ -168,7 +168,7 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, pp[n[idx]].y = y - bitmap.top + i; if (++(n[idx]) == size) { - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), idx == 6 ? gc_fore : gcs[idx], pp, size, CoordModeOrigin); n[idx] = 0; @@ -180,10 +180,10 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, { for (i = 0; i < 6; i++) if (n[i] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gcs[i], p + 0x100 * i, n[i], CoordModeOrigin); if (n[6] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p + 0x600, n[6], CoordModeOrigin); } } @@ -203,7 +203,7 @@ ftxfont_draw_background (struct frame *f, struct font *font, GC gc, int x, int y XGetGCValues (FRAME_X_DISPLAY (f), gc, GCForeground | GCBackground, &xgcv); XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background); - XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc, + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y - FONT_BASE (font), width, FONT_HEIGHT (font)); XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground); } diff --git a/src/gtkutil.c b/src/gtkutil.c index 88e6d30..f81940b 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -48,6 +48,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "emacsgtkfixed.h" #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW #define gtk_widget_set_has_window(w, b) \ (gtk_fixed_set_has_window (GTK_FIXED (w), b)) @@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f) by callers of this function. */ gtk_widget_realize (wfixed); FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed); + set_up_x_back_buffer (f); /* Since GTK clears its window by filling with the background color, we must keep X and GTK background in sync. */ @@ -1296,6 +1301,15 @@ xg_free_frame_widgets (struct frame *f) if (tbinfo) xfree (tbinfo); +#ifdef HAVE_XDBE + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + { + XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f)); + FRAME_X_DRAWABLE (f) = 0; + } +#endif + gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f)); FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */ FRAME_GTK_OUTER_WIDGET (f) = 0; diff --git a/src/image.c b/src/image.c index 9bd2455..1303a93 100644 --- a/src/image.c +++ b/src/image.c @@ -220,7 +220,7 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi #ifdef HAVE_X_WINDOWS Pixmap bitmap; - bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), bits, width, height); if (! bitmap) return -1; @@ -327,7 +327,7 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file) filename = SSDATA (found); - result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), filename, &width, &height, &bitmap, &xhot, &yhot); if (result != BitmapSuccess) return -1; @@ -1952,7 +1952,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, { #ifdef HAVE_X_WINDOWS Display *display = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); Screen *screen = FRAME_X_SCREEN (f); eassert (input_blocked_p ()); @@ -1981,7 +1981,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height); /* Allocate a pixmap of the same size. */ - *pixmap = XCreatePixmap (display, window, width, height, depth); + *pixmap = XCreatePixmap (display, drawable, width, height, depth); if (*pixmap == NO_PIXMAP) { x_destroy_x_image (*ximg); @@ -2742,7 +2742,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data, img->pixmap = (x_check_image_size (0, img->width, img->height) ? XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), data, img->width, img->height, fg, bg, @@ -3520,7 +3520,7 @@ x_create_bitmap_from_xpm_data (struct frame *f, const char **bits) xpm_init_color_cache (f, &attrs); #endif - rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), (char **) bits, &bitmap, &mask, &attrs); if (rc != XpmSuccess) { @@ -3758,7 +3758,7 @@ xpm_load (struct frame *f, struct image *img) #ifdef HAVE_X_WINDOWS if (rc == XpmSuccess) { - img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->ximg->width, img->ximg->height, img->ximg->depth); if (img->pixmap == NO_PIXMAP) @@ -3768,7 +3768,7 @@ xpm_load (struct frame *f, struct image *img) } else if (img->mask_img) { - img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->mask_img->width, img->mask_img->height, img->mask_img->depth); @@ -9541,7 +9541,7 @@ gs_load (struct frame *f, struct image *img) { /* Only W32 version did BLOCK_INPUT here. ++kfs */ block_input (); - img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->width, img->height, DefaultDepthOfScreen (FRAME_X_SCREEN (f))); unblock_input (); @@ -9557,7 +9557,7 @@ gs_load (struct frame *f, struct image *img) if successful. We do not record_unwind_protect here because other places in redisplay like calling window scroll functions don't either. Let the Lisp loader use `unwind-protect' instead. */ - printnum1 = FRAME_X_WINDOW (f); + printnum1 = FRAME_X_DRAWABLE (f); printnum2 = img->pixmap; window_and_pixmap_id = make_formatted_string (buffer, "%"pMu" %"pMu, printnum1, printnum2); diff --git a/src/termhooks.h b/src/termhooks.h index ff74d99..c8d7fae 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -477,6 +477,7 @@ struct terminal void (*update_begin_hook) (struct frame *); void (*update_end_hook) (struct frame *); + void (*redisplay_end_hook) (struct frame *); void (*set_terminal_window_hook) (struct frame *, int); /* Multi-frame and mouse support hooks. */ diff --git a/src/xdisp.c b/src/xdisp.c index 3af5ea4..04f0ca1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -2501,7 +2501,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) /* Visible feedback for debugging. */ #if false && defined HAVE_X_WINDOWS - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, gx, gy, width, height); #endif @@ -14211,6 +14211,13 @@ redisplay_internal (void) windows_or_buffers_changed = 0; } + FOR_EACH_FRAME (tail, frame) + { + struct frame *f = XFRAME (frame); + if (FRAME_TERMINAL (f)->redisplay_end_hook) + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); + } + /* Start SIGIO interrupts coming again. Having them off during the code above makes it less likely one will discard output, but not impossible, since there might be stuff in the system buffer here. @@ -24608,7 +24615,7 @@ init_glyph_string (struct glyph_string *s, s->hdc = hdc; #endif s->display = FRAME_X_DISPLAY (s->f); - s->window = FRAME_X_WINDOW (s->f); + s->window = FRAME_X_DRAWABLE (s->f); s->char2b = char2b; s->hl = hl; s->row = row; diff --git a/src/xfaces.c b/src/xfaces.c index 5837f35..accb98b 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -495,7 +495,7 @@ x_create_gc (struct frame *f, unsigned long mask, XGCValues *xgcv) { GC gc; block_input (); - gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), mask, xgcv); + gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), mask, xgcv); unblock_input (); IF_DEBUG (++ngcs); return gc; diff --git a/src/xfns.c b/src/xfns.c index 8571d0e..2098036 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -53,6 +53,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "gtkutil.h" #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + #ifdef USE_X_TOOLKIT #include <X11/Shell.h> @@ -2483,6 +2487,29 @@ xic_set_xfontset (struct frame *f, const char *base_fontname) \f + +void +set_up_x_back_buffer (struct frame* f) +{ + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); +#ifdef HAVE_XDBE + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) + { + /* If allocating a back buffer fails, just use single-buffered + rendering. */ + x_sync (f); + x_catch_errors (FRAME_X_DISPLAY (f)); + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( + FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + XdbeCopied); + if (x_had_errors_p (FRAME_X_DISPLAY (f))) + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); + x_uncatch_errors_after_check (); + } +#endif +} + #ifdef USE_X_TOOLKIT /* Create and set up the X widget for frame F. */ @@ -2638,7 +2665,7 @@ x_window (struct frame *f, long window_prompting) f->output_data.x->parent_desc, 0, 0); FRAME_X_WINDOW (f) = XtWindow (frame_widget); - + set_up_x_back_buffer (f); validate_x_resource_name (); class_hints.res_name = SSDATA (Vx_resource_name); @@ -2784,7 +2811,8 @@ x_window (struct frame *f) CopyFromParent, /* depth */ InputOutput, /* class */ FRAME_X_VISUAL (f), - attribute_mask, &attributes); + attribute_mask, &attributes); + set_up_x_back_buffer (f); #ifdef HAVE_X_I18N if (use_xim) @@ -2938,7 +2966,7 @@ x_make_gc (struct frame *f) gc_values.line_width = 0; /* Means 1 using fast algorithm. */ f->output_data.x->normal_gc = XCreateGC (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), GCLineWidth | GCForeground | GCBackground, &gc_values); @@ -2947,7 +2975,7 @@ x_make_gc (struct frame *f) gc_values.background = FRAME_FOREGROUND_PIXEL (f); f->output_data.x->reverse_gc = XCreateGC (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), GCForeground | GCBackground | GCLineWidth, &gc_values); @@ -2956,7 +2984,7 @@ x_make_gc (struct frame *f) gc_values.background = f->output_data.x->cursor_pixel; gc_values.fill_style = FillOpaqueStippled; f->output_data.x->cursor_gc - = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), (GCForeground | GCBackground | GCFillStyle | GCLineWidth), &gc_values); @@ -5636,7 +5664,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) /* Border. */ f->border_width, CopyFromParent, InputOutput, CopyFromParent, - mask, &attrs); + mask, &attrs); + set_up_x_back_buffer (f); XChangeProperty (FRAME_X_DISPLAY (f), tip_window, FRAME_DISPLAY_INFO (f)->Xatom_net_window_type, XA_ATOM, 32, PropModeReplace, diff --git a/src/xfont.c b/src/xfont.c index 45b0e0a..c2b7317 100644 --- a/src/xfont.c +++ b/src/xfont.c @@ -1057,20 +1057,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y, { if (s->padding_p) for (i = 0; i < len; i++) - XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, str + i, 1); else - XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, str, len); } else { if (s->padding_p) for (i = 0; i < len; i++) - XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, str + i, 1); else - XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, str, len); } unblock_input (); @@ -1083,20 +1083,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y, { if (s->padding_p) for (i = 0; i < len; i++) - XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, s->char2b + from + i, 1); else - XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, s->char2b + from, len); } else { if (s->padding_p) for (i = 0; i < len; i++) - XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, s->char2b + from + i, 1); else - XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, s->char2b + from, len); } unblock_input (); diff --git a/src/xftfont.c b/src/xftfont.c index 34c6f7d..447adf6 100644 --- a/src/xftfont.c +++ b/src/xftfont.c @@ -586,7 +586,7 @@ xftfont_get_xft_draw (struct frame *f) { block_input (); xft_draw= XftDrawCreate (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_X_VISUAL (f), FRAME_X_COLORMAP (f)); unblock_input (); @@ -695,6 +695,13 @@ xftfont_end_for_frame (struct frame *f) return 0; } +static void +xftfont_flush_frame_caches (struct frame *f) +{ + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + xftfont_end_for_frame (f); +} + static bool xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object, Lisp_Object entity) @@ -777,6 +784,9 @@ This is needed with some fonts to correct vertical overlap of glyphs. */); #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF) xftfont_driver.shape = xftfont_shape; #endif + // When using X double buffering, the XftDraw structure we + // build seems to be useless once a frame is resized, so + xftfont_driver.flush_frame_caches = xftfont_flush_frame_caches; register_font_driver (&xftfont_driver, NULL); } diff --git a/src/xterm.c b/src/xterm.c index 7476694..7116784 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -45,6 +45,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <X11/extensions/Xrender.h> #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + /* Load sys/types.h if not already loaded. In some systems loading it twice is suicidal. */ #ifndef makedev @@ -360,7 +364,7 @@ x_begin_cr_clip (struct frame *f, GC gc) { cairo_surface_t *surface; surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_DISPLAY_INFO (f)->visual, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); @@ -722,7 +726,7 @@ x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) cairo_fill (cr); x_end_cr_clip (f); #else - XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y, width, height); #endif } @@ -740,7 +744,7 @@ x_draw_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) cairo_stroke (cr); x_end_cr_clip (f); #else - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y, width, height); #endif } @@ -756,7 +760,10 @@ x_clear_window (struct frame *f) cairo_paint (cr); x_end_cr_clip (f); #else - XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); + else + XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); #endif } @@ -1067,7 +1074,7 @@ x_draw_vertical_window_border (struct window *w, int x, int y0, int y1) #ifdef USE_CAIRO x_fill_rectangle (f, f->output_data.x->normal_gc, x, y0, 1, y1 - y0); #else - XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, y0, x, y1); #endif } @@ -1179,6 +1186,21 @@ x_update_window_end (struct window *w, bool cursor_on_p, /* End update of frame F. This function is installed as a hook in update_end. */ +#if defined (HAVE_XDBE) +static void +show_back_buffer (struct frame *f) +{ + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + { + XdbeSwapInfo swap_info; + memset (&swap_info, 0, sizeof (swap_info)); + swap_info.swap_window = FRAME_X_WINDOW (f); + swap_info.swap_action = XdbeCopied; + XdbeSwapBuffers (FRAME_X_DISPLAY (f), &swap_info, 1); + } +} +#endif + static void x_update_end (struct frame *f) { @@ -1207,7 +1229,7 @@ x_update_end (struct frame *f) if (! FRAME_EXTERNAL_MENU_BAR (f)) height += FRAME_MENU_BAR_HEIGHT (f); surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_DISPLAY_INFO (f)->visual, width, height); @@ -1220,7 +1242,14 @@ x_update_end (struct frame *f) cairo_destroy (cr); unblock_input (); } -#endif /* USE_CAIRO */ +#endif + +#ifdef HAVE_XDBE + if (redisplaying_p) + FRAME_X_NEED_BUFFER_FLIP (f) = true; + else + show_back_buffer (f); +#endif #ifndef XFlush block_input (); @@ -1229,6 +1258,17 @@ x_update_end (struct frame *f) #endif } +static void +x_redisplay_end (struct frame *f) +{ +#ifdef HAVE_XDBE + if (FRAME_X_NEED_BUFFER_FLIP (f)) + { + show_back_buffer (f); + FRAME_X_NEED_BUFFER_FLIP (f) = false; + } +#endif +} /* This function is called from various places in xdisp.c whenever a complete update has been performed. */ @@ -1354,7 +1394,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring #else /* not USE_CAIRO */ if (p->which) { - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); char *bits; Pixmap pixmap, clipmask = (Pixmap) 0; int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); @@ -1367,7 +1407,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring /* Draw the bitmap. I believe these small pixmaps can be cached by the server. */ - pixmap = XCreatePixmapFromBitmapData (display, window, bits, p->wd, p->h, + pixmap = XCreatePixmapFromBitmapData (display, drawable, bits, p->wd, p->h, (p->cursor_p ? (p->overlay_p ? face->background : f->output_data.x->cursor_pixel) @@ -1386,7 +1426,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring XChangeGC (display, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv); } - XCopyArea (display, pixmap, window, gc, 0, 0, + XCopyArea (display, pixmap, drawable, gc, 0, 0, p->wd, p->h, p->x, p->y); XFreePixmap (display, pixmap); @@ -2565,7 +2605,7 @@ x_setup_relief_color (struct frame *f, struct relief *relief, double factor, { xgcv.stipple = dpyinfo->gray; mask |= GCStipple; - relief->gc = XCreateGC (dpy, FRAME_X_WINDOW (f), mask, &xgcv); + relief->gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), mask, &xgcv); } else XChangeGC (dpy, relief->gc, mask, &xgcv); @@ -2696,7 +2736,7 @@ x_draw_relief_rect (struct frame *f, x_reset_clip_rectangles (f, bottom_right_gc); #else Display *dpy = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); int i; GC gc; @@ -2715,12 +2755,12 @@ x_draw_relief_rect (struct frame *f, if (top_p) { if (width == 1) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, top_y, right_x + !right_p, top_y); for (i = 1; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i * left_p, top_y + i, right_x + 1 - i * right_p, top_y + i); } @@ -2729,13 +2769,13 @@ x_draw_relief_rect (struct frame *f, if (left_p) { if (width == 1) - XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y); + XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y); - XClearArea (dpy, window, left_x, top_y, 1, 1, False); - XClearArea (dpy, window, left_x, bottom_y, 1, 1, False); + x_clear_area(f, left_x, top_y, 1, 1); + x_clear_area(f, left_x, bottom_y, 1, 1); for (i = (width > 1 ? 1 : 0); i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i, top_y + (i + 1) * top_p, left_x + i, bottom_y + 1 - (i + 1) * bot_p); } @@ -2751,23 +2791,23 @@ x_draw_relief_rect (struct frame *f, { /* Outermost top line. */ if (top_p) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, top_y, right_x + !right_p, top_y); /* Outermost left line. */ if (left_p) - XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y); + XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y); } /* Bottom. */ if (bot_p) { - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, bottom_y, right_x + !right_p, bottom_y); for (i = 1; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i * left_p, bottom_y - i, right_x + 1 - i * right_p, bottom_y - i); } @@ -2775,10 +2815,10 @@ x_draw_relief_rect (struct frame *f, /* Right. */ if (right_p) { - XClearArea (dpy, window, right_x, top_y, 1, 1, False); - XClearArea (dpy, window, right_x, bottom_y, 1, 1, False); + x_clear_area(f, right_x, top_y, 1, 1); + x_clear_area(f, right_x, bottom_y, 1, 1); for (i = 0; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, right_x - i, top_y + (i + 1) * top_p, right_x - i, bottom_y + 1 - (i + 1) * bot_p); } @@ -3741,7 +3781,7 @@ x_shift_glyphs_for_insert (struct frame *f, int x, int y, int width, int height, /* Never called on a GUI frame, see http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html */ - XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), + XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, y, width, height, x + shift_by, y); @@ -3782,8 +3822,14 @@ x_clear_area (struct frame *f, int x, int y, int width, int height) cairo_fill (cr); x_end_cr_clip (f); #else - x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - x, y, width, height, False); + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + XFillRectangle (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f), + f->output_data.x->reverse_gc, + x, y, width, height); + else + x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + x, y, width, height, False); #endif } @@ -4109,7 +4155,7 @@ x_scroll_run (struct window *w, struct run *run) SET_FRAME_GARBAGED (f); #else XCopyArea (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, from_y, width, height, @@ -7769,6 +7815,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, SET_FRAME_VISIBLE (f, 1); SET_FRAME_ICONIFIED (f, false); f->output_data.x->has_been_visible = true; + font_flush_frame_caches (f); SET_FRAME_GARBAGED (f); } else @@ -8437,7 +8484,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, { if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width) - SET_FRAME_GARBAGED (f); + { + font_flush_frame_caches (f); + SET_FRAME_GARBAGED (f); + } FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height; FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width; } @@ -8463,7 +8513,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, || configureEvent.xconfigure.height != FRAME_PIXEL_HEIGHT (f)) { change_frame_size (f, width, height, false, true, false, true); - x_clear_under_internal_border (f); + x_clear_under_internal_border (f); + font_flush_frame_caches (f); SET_FRAME_GARBAGED (f); cancel_mouse_face (f); } @@ -8880,7 +8931,7 @@ x_draw_hollow_cursor (struct window *w, struct glyph_row *row) if (dpyinfo->scratch_cursor_gc) XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv); else - dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_WINDOW (f), + dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), GCForeground, &xgcv); gc = dpyinfo->scratch_cursor_gc; @@ -8937,7 +8988,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text else { Display *dpy = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); GC gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc; unsigned long mask = GCForeground | GCBackground | GCGraphicsExposures; struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id); @@ -8958,7 +9009,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text XChangeGC (dpy, gc, mask, &xgcv); else { - gc = XCreateGC (dpy, window, mask, &xgcv); + gc = XCreateGC (dpy, drawable, mask, &xgcv); FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc; } @@ -11271,7 +11322,14 @@ x_free_frame_resources (struct frame *f) #endif /* USE_GTK */ if (FRAME_X_WINDOW (f)) - XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); + { +#ifdef HAVE_XDBE + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f)); +#endif + XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); + } #endif /* !USE_X_TOOLKIT */ unload_color (f, FRAME_FOREGROUND_PIXEL (f)); @@ -12111,7 +12169,17 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) } else dpyinfo->cmap = XCreateColormap (dpyinfo->display, dpyinfo->root_window, - dpyinfo->visual, AllocNone); + dpyinfo->visual, AllocNone); + +#ifdef HAVE_XDBE + dpyinfo->supports_xdbe = false; + { + int xdbe_major; + int xdbe_minor; + if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor)) + dpyinfo->supports_xdbe = true; + } +#endif #ifdef HAVE_XFT { @@ -12590,6 +12658,7 @@ x_create_terminal (struct x_display_info *dpyinfo) terminal->toggle_invisible_pointer_hook = XTtoggle_invisible_pointer; terminal->update_begin_hook = x_update_begin; terminal->update_end_hook = x_update_end; + terminal->redisplay_end_hook = x_redisplay_end; terminal->read_socket_hook = XTread_socket; terminal->frame_up_to_date_hook = XTframe_up_to_date; terminal->mouse_position_hook = XTmouse_position; diff --git a/src/xterm.h b/src/xterm.h index 675a484..cb1aa1d 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -475,6 +475,10 @@ struct x_display_info #ifdef USE_XCB xcb_connection_t *xcb_connection; #endif + +#ifdef HAVE_XDBE + bool supports_xdbe; +#endif }; #ifdef HAVE_X_I18N @@ -527,6 +531,17 @@ struct x_output and the X window has not yet been created. */ Window window_desc; +#ifdef HAVE_XDBE + /* The drawable to which we're rendering. In the single-buffered + base, the window itself. In the double-buffered case, the + window's back buffer. */ + Drawable draw_desc; + + /* Set to true when we need a buffer flip. We do a buffer flip only + at the end of redisplay in order to minimize flicker. */ + bool need_buffer_flip; +#endif + /* The X window used for the bitmap icon; or 0 if we don't have a bitmap icon. */ Window icon_desc; @@ -737,6 +752,18 @@ enum /* Return the X window used for displaying data in frame F. */ #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc) +/* Return the drawable used for rendering to frame F. */ +#ifdef HAVE_XDBE +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc) +#else +#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f))) +#endif + +/* Return the need-buffer-flip flag for frame F. */ +#ifdef HAVE_XDBE +#define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip) +#endif + /* Return the outermost X window associated with the frame F. */ #ifdef USE_X_TOOLKIT #define FRAME_OUTER_WINDOW(f) ((f)->output_data.x->widget ? \ @@ -1140,6 +1167,8 @@ extern bool x_wm_supports (struct frame *, Atom); extern void x_wait_for_event (struct frame *, int); extern void x_clear_under_internal_border (struct frame *f); +extern void set_up_x_back_buffer (struct frame* f); + /* Defined in xselect.c. */ extern void x_handle_property_notify (const XPropertyEvent *); ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione @ 2016-10-21 2:27 ` Lars Ingebrigtsen 2016-10-21 2:31 ` Daniel Colascione 2016-10-21 3:24 ` Óscar Fuentes ` (2 subsequent siblings) 3 siblings, 1 reply; 31+ messages in thread From: Lars Ingebrigtsen @ 2016-10-21 2:27 UTC (permalink / raw) To: Daniel Colascione; +Cc: emacs-devel Daniel Colascione <dancol@dancol.org> writes: > This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to > avoid showing the user incomplete drawing results. Without this patch, > I can make Emacs flicker like crazy by running isearch for a piece of > text unique in a buffer and holding down C-s. With this patch, Emacs > does not flicker no matter what I do to it. That sounds just wonderful. Will it also remove flickers when watching animated GIFs? -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 2:27 ` Lars Ingebrigtsen @ 2016-10-21 2:31 ` Daniel Colascione 0 siblings, 0 replies; 31+ messages in thread From: Daniel Colascione @ 2016-10-21 2:31 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: emacs-devel On October 20, 2016 7:27:04 PM PDT, Lars Ingebrigtsen <larsi@gnus.org> wrote: >Daniel Colascione <dancol@dancol.org> writes: > >> This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension >to >> avoid showing the user incomplete drawing results. Without this >patch, >> I can make Emacs flicker like crazy by running isearch for a piece of >> text unique in a buffer and holding down C-s. With this patch, Emacs >> does not flicker no matter what I do to it. > >That sounds just wonderful. Will it also remove flickers when watching >animated GIFs? Probably? TIAS ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione 2016-10-21 2:27 ` Lars Ingebrigtsen @ 2016-10-21 3:24 ` Óscar Fuentes 2016-10-21 3:31 ` Clément Pit--Claudel 2016-10-21 3:56 ` Clément Pit--Claudel 2016-10-21 8:49 ` Eli Zaretskii 3 siblings, 1 reply; 31+ messages in thread From: Óscar Fuentes @ 2016-10-21 3:24 UTC (permalink / raw) To: emacs-devel Daniel Colascione <dancol@dancol.org> writes: > This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to > avoid showing the user incomplete drawing results. Without this patch, > I can make Emacs flicker like crazy by running isearch for a piece of > text unique in a buffer and holding down C-s. With this patch, Emacs > does not flicker no matter what I do to it. [snip] For me Emacs flickers by just writing or scrolling around. It is quite annoying to work with unless I activate the tearing prevention feature on KDE's settings, which I'll like to avoid because it is buggy. I'm eager to try your code, but `patch' complains about oscar@qcore:~/dev/emacs/stable/source$ patch -p 1 < ~/p.patch patching file configure.ac patching file src/Makefile.in patch: **** malformed patch at line 60: $(LIBSELINUX_LIBS) \ And indeed, the patch shows: $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \ $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ It looks like the typical text wrap problem. There are more cases like this. Could you send the patch as an attachment, please? ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 3:24 ` Óscar Fuentes @ 2016-10-21 3:31 ` Clément Pit--Claudel 2016-10-21 3:41 ` Óscar Fuentes 2016-10-21 8:23 ` Andreas Schwab 0 siblings, 2 replies; 31+ messages in thread From: Clément Pit--Claudel @ 2016-10-21 3:31 UTC (permalink / raw) To: emacs-devel [-- Attachment #1.1: Type: text/plain, Size: 475 bytes --] On 2016-10-20 23:24, Óscar Fuentes wrote: > I'm eager to try your code, but `patch' complains about > > oscar@qcore:~/dev/emacs/stable/source$ patch -p 1 < ~/p.patch > patching file configure.ac > patching file src/Makefile.in > patch: **** malformed patch at line 60: $(LIBSELINUX_LIBS) \ The patch applies fine for me with git apply --reject --ignore-space-change --ignore-whitespace double-buffer.patch. Maybe that works for you too? Cheers, Clément. [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 3:31 ` Clément Pit--Claudel @ 2016-10-21 3:41 ` Óscar Fuentes 2016-10-21 3:50 ` Clément Pit--Claudel 2016-10-21 8:23 ` Andreas Schwab 1 sibling, 1 reply; 31+ messages in thread From: Óscar Fuentes @ 2016-10-21 3:41 UTC (permalink / raw) To: emacs-devel Clément Pit--Claudel <clement.pit@gmail.com> writes: > The patch applies fine for me with git apply --reject > --ignore-space-change --ignore-whitespace double-buffer.patch. Maybe > that works for you too? Hmmm... no: oscar@qcore:~/dev/emacs/stable/source$ git apply --reject --ignore-space-change --ignore-whitespace ~/p.patch fatal: corrupt patch at line 66 oscar@qcore:~/dev/emacs/stable/source$ git --version git version 2.9.3 ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 3:41 ` Óscar Fuentes @ 2016-10-21 3:50 ` Clément Pit--Claudel 0 siblings, 0 replies; 31+ messages in thread From: Clément Pit--Claudel @ 2016-10-21 3:50 UTC (permalink / raw) To: emacs-devel [-- Attachment #1.1.1: Type: text/plain, Size: 593 bytes --] On 2016-10-20 23:41, Óscar Fuentes wrote: > Clément Pit--Claudel <clement.pit@gmail.com> writes: > >> The patch applies fine for me with git apply --reject >> --ignore-space-change --ignore-whitespace double-buffer.patch. Maybe >> that works for you too? > > Hmmm... no: > > oscar@qcore:~/dev/emacs/stable/source$ git apply --reject --ignore-space-change --ignore-whitespace ~/p.patch > fatal: corrupt patch at line 66 > > oscar@qcore:~/dev/emacs/stable/source$ git --version > git version 2.9.3 I've attached the patch that applied correctly for me (I use git 2.7.4). [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1.1.2: double-buffer.patch --] [-- Type: text/x-diff; name="double-buffer.patch", Size: 38590 bytes --] commit 15fdd8f63533201f05627ede634a8f5ae4757d7e1010 Authorf: Daniel Colascione <dancol@dancol.org> Date: Thu Oct 20 16:50:54 2016 -0700 Add double-buffered output support to Emacs diff --git a/configure.ac b/configure.ac index cd11b10..4716b43 100644 --- a/configure.ac +++ b/configure.ac @@ -3712,6 +3712,24 @@ AC_DEFUN AC_SUBST(XFIXES_CFLAGS) AC_SUBST(XFIXES_LIBS) +### Use Xdbe (-lXdbe) if available +HAVE_XDBE=no +if test "${HAVE_X11}" = "yes"; then + AC_CHECK_HEADER(X11/extensions/Xdbe.h, + [AC_CHECK_LIB(Xext, XdbeAllocateBackBufferName, HAVE_XDBE=yes)], + [], + [#include <X11/Xlib.h> + ]) + if test $HAVE_XDBE = yes; then + XDBE_LIBS=-lXext + fi + if test $HAVE_XDBE = yes; then + AC_DEFINE(HAVE_XDBE, 1, [Define to 1 if you have the Xdbe extension.]) + fi +fi +AC_SUBST(XDBE_CFLAGS) +AC_SUBST(XDBE_LIBS) + ### Use libxml (-lxml2) if available ### mingw32 doesn't use -lxml2, since it loads the library dynamically. HAVE_LIBXML2=no diff --git a/src/Makefile.in b/src/Makefile.in index 89f7a92..dc0bfff 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -254,6 +254,9 @@ XINERAMA_CFLAGS = XFIXES_LIBS = @XFIXES_LIBS@ XFIXES_CFLAGS = @XFIXES_CFLAGS@ +XDBE_LIBS = @XDBE_LIBS@ +XDBE_CFLAGS = @XDBE_CFLAGS@ + ## widget.o if USE_X_TOOLKIT, otherwise empty. WIDGET_OBJ=@WIDGET_OBJ@ @@ -372,7 +375,7 @@ ALL_CFLAGS= $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \ $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \ $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \ - $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \ + $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \ $(WEBKIT_CFLAGS) \ $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ @@ -489,6 +492,7 @@ LIBES = $(WEBKIT_LIBS) \ $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \ $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \ + $(XDBE_LIBS) \ $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \ $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ diff --git a/src/dispnew.c b/src/dispnew.c index 70d4de0..8f81cee 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f) { /* Error if F has no glyphs. */ eassert (f->glyphs_initialized_p); + font_flush_frame_caches (f); update_begin (f); if (FRAME_MSDOS_P (f)) FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f)); diff --git a/src/font.c b/src/font.c index f8e6794..033995e 100644 --- a/src/font.c +++ b/src/font.c @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, Lisp_Object arg, Lisp_Object result) } void +font_flush_frame_caches (struct frame *f) +{ + struct font_driver_list *list; + + for (list = f->font_driver_list; list; list = list->next) + if (list->on && list->driver->flush_frame_caches) + list->driver->flush_frame_caches (f); +} + +void syms_of_font (void) { sort_shift_bits[FONT_TYPE_INDEX] = 0; diff --git a/src/font.h b/src/font.h index cf47729..961e9c4 100644 --- a/src/font.h +++ b/src/font.h @@ -763,6 +763,12 @@ struct font_driver Return non-nil if the driver support rendering of combining characters for FONT according to Unicode combining class. */ Lisp_Object (*combining_capability) (struct font *font); + + /* Optional + + Called when frame F is redrawn from scratch. Font engines may + invalidate certain caches in this case. */ + void (*flush_frame_caches) (struct frame *f); }; @@ -862,7 +868,9 @@ extern void *font_get_frame_data (struct frame *f, Lisp_Object); extern void font_filter_properties (Lisp_Object font, Lisp_Object alist, const char *const boolean_properties[], - const char *const non_boolean_properties[]); + const char *const non_boolean_properties[]); + +extern void font_flush_frame_caches (struct frame *f); #ifdef HAVE_FREETYPE extern struct font_driver ftfont_driver; diff --git a/src/ftxfont.c b/src/ftxfont.c index f49d44f..bfdeb40 100644 --- a/src/ftxfont.c +++ b/src/ftxfont.c @@ -95,7 +95,7 @@ ftxfont_get_gcs (struct frame *f, unsigned long foreground, unsigned long backgr if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &color)) break; xgcv.foreground = color.pixel; - new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), GCForeground, &xgcv); } unblock_input (); @@ -139,14 +139,14 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, p[n[0]].y = y - bitmap.top + i; if (++n[0] == size) { - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p, size, CoordModeOrigin); n[0] = 0; } } } if (flush && n[0] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p, n[0], CoordModeOrigin); } else @@ -168,7 +168,7 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, pp[n[idx]].y = y - bitmap.top + i; if (++(n[idx]) == size) { - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), idx == 6 ? gc_fore : gcs[idx], pp, size, CoordModeOrigin); n[idx] = 0; @@ -180,10 +180,10 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, { for (i = 0; i < 6; i++) if (n[i] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gcs[i], p + 0x100 * i, n[i], CoordModeOrigin); if (n[6] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p + 0x600, n[6], CoordModeOrigin); } } @@ -203,7 +203,7 @@ ftxfont_draw_background (struct frame *f, struct font *font, GC gc, int x, int y XGetGCValues (FRAME_X_DISPLAY (f), gc, GCForeground | GCBackground, &xgcv); XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background); - XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc, + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y - FONT_BASE (font), width, FONT_HEIGHT (font)); XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground); } diff --git a/src/gtkutil.c b/src/gtkutil.c index 88e6d30..f81940b 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -48,6 +48,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "emacsgtkfixed.h" #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW #define gtk_widget_set_has_window(w, b) \ (gtk_fixed_set_has_window (GTK_FIXED (w), b)) @@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f) by callers of this function. */ gtk_widget_realize (wfixed); FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed); + set_up_x_back_buffer (f); /* Since GTK clears its window by filling with the background color, we must keep X and GTK background in sync. */ @@ -1296,6 +1301,15 @@ xg_free_frame_widgets (struct frame *f) if (tbinfo) xfree (tbinfo); +#ifdef HAVE_XDBE + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + { + XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f)); + FRAME_X_DRAWABLE (f) = 0; + } +#endif + gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f)); FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */ FRAME_GTK_OUTER_WIDGET (f) = 0; diff --git a/src/image.c b/src/image.c index 9bd2455..1303a93 100644 --- a/src/image.c +++ b/src/image.c @@ -220,7 +220,7 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi #ifdef HAVE_X_WINDOWS Pixmap bitmap; - bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), bits, width, height); if (! bitmap) return -1; @@ -327,7 +327,7 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file) filename = SSDATA (found); - result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), filename, &width, &height, &bitmap, &xhot, &yhot); if (result != BitmapSuccess) return -1; @@ -1952,7 +1952,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, { #ifdef HAVE_X_WINDOWS Display *display = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); Screen *screen = FRAME_X_SCREEN (f); eassert (input_blocked_p ()); @@ -1981,7 +1981,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height); /* Allocate a pixmap of the same size. */ - *pixmap = XCreatePixmap (display, window, width, height, depth); + *pixmap = XCreatePixmap (display, drawable, width, height, depth); if (*pixmap == NO_PIXMAP) { x_destroy_x_image (*ximg); @@ -2742,7 +2742,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data, img->pixmap = (x_check_image_size (0, img->width, img->height) ? XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), data, img->width, img->height, fg, bg, @@ -3520,7 +3520,7 @@ x_create_bitmap_from_xpm_data (struct frame *f, const char **bits) xpm_init_color_cache (f, &attrs); #endif - rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), (char **) bits, &bitmap, &mask, &attrs); if (rc != XpmSuccess) { @@ -3758,7 +3758,7 @@ xpm_load (struct frame *f, struct image *img) #ifdef HAVE_X_WINDOWS if (rc == XpmSuccess) { - img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->ximg->width, img->ximg->height, img->ximg->depth); if (img->pixmap == NO_PIXMAP) @@ -3768,7 +3768,7 @@ xpm_load (struct frame *f, struct image *img) } else if (img->mask_img) { - img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->mask_img->width, img->mask_img->height, img->mask_img->depth); @@ -9541,7 +9541,7 @@ gs_load (struct frame *f, struct image *img) { /* Only W32 version did BLOCK_INPUT here. ++kfs */ block_input (); - img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->width, img->height, DefaultDepthOfScreen (FRAME_X_SCREEN (f))); unblock_input (); @@ -9557,7 +9557,7 @@ gs_load (struct frame *f, struct image *img) if successful. We do not record_unwind_protect here because other places in redisplay like calling window scroll functions don't either. Let the Lisp loader use `unwind-protect' instead. */ - printnum1 = FRAME_X_WINDOW (f); + printnum1 = FRAME_X_DRAWABLE (f); printnum2 = img->pixmap; window_and_pixmap_id = make_formatted_string (buffer, "%"pMu" %"pMu, printnum1, printnum2); diff --git a/src/termhooks.h b/src/termhooks.h index ff74d99..c8d7fae 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -477,6 +477,7 @@ struct terminal void (*update_begin_hook) (struct frame *); void (*update_end_hook) (struct frame *); + void (*redisplay_end_hook) (struct frame *); void (*set_terminal_window_hook) (struct frame *, int); /* Multi-frame and mouse support hooks. */ diff --git a/src/xdisp.c b/src/xdisp.c index 3af5ea4..04f0ca1 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -2501,7 +2501,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) /* Visible feedback for debugging. */ #if false && defined HAVE_X_WINDOWS - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, gx, gy, width, height); #endif @@ -14211,6 +14211,13 @@ redisplay_internal (void) windows_or_buffers_changed = 0; } + FOR_EACH_FRAME (tail, frame) + { + struct frame *f = XFRAME (frame); + if (FRAME_TERMINAL (f)->redisplay_end_hook) + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); + } + /* Start SIGIO interrupts coming again. Having them off during the code above makes it less likely one will discard output, but not impossible, since there might be stuff in the system buffer here. @@ -24608,7 +24615,7 @@ init_glyph_string (struct glyph_string *s, s->hdc = hdc; #endif s->display = FRAME_X_DISPLAY (s->f); - s->window = FRAME_X_WINDOW (s->f); + s->window = FRAME_X_DRAWABLE (s->f); s->char2b = char2b; s->hl = hl; s->row = row; diff --git a/src/xfaces.c b/src/xfaces.c index 5837f35..accb98b 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -495,7 +495,7 @@ x_create_gc (struct frame *f, unsigned long mask, XGCValues *xgcv) { GC gc; block_input (); - gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), mask, xgcv); + gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), mask, xgcv); unblock_input (); IF_DEBUG (++ngcs); return gc; diff --git a/src/xfns.c b/src/xfns.c index 8571d0e..2098036 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -53,6 +53,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "gtkutil.h" #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + #ifdef USE_X_TOOLKIT #include <X11/Shell.h> @@ -2483,6 +2487,29 @@ xic_set_xfontset (struct frame *f, const char *base_fontname) \f + +void +set_up_x_back_buffer (struct frame* f) +{ + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); +#ifdef HAVE_XDBE + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) + { + /* If allocating a back buffer fails, just use single-buffered + rendering. */ + x_sync (f); + x_catch_errors (FRAME_X_DISPLAY (f)); + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( + FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + XdbeCopied); + if (x_had_errors_p (FRAME_X_DISPLAY (f))) + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); + x_uncatch_errors_after_check (); + } +#endif +} + #ifdef USE_X_TOOLKIT /* Create and set up the X widget for frame F. */ @@ -2638,7 +2665,7 @@ x_window (struct frame *f, long window_prompting) f->output_data.x->parent_desc, 0, 0); FRAME_X_WINDOW (f) = XtWindow (frame_widget); - + set_up_x_back_buffer (f); validate_x_resource_name (); class_hints.res_name = SSDATA (Vx_resource_name); @@ -2784,7 +2811,8 @@ x_window (struct frame *f) CopyFromParent, /* depth */ InputOutput, /* class */ FRAME_X_VISUAL (f), - attribute_mask, &attributes); + attribute_mask, &attributes); + set_up_x_back_buffer (f); #ifdef HAVE_X_I18N if (use_xim) @@ -2938,7 +2966,7 @@ x_make_gc (struct frame *f) gc_values.line_width = 0; /* Means 1 using fast algorithm. */ f->output_data.x->normal_gc = XCreateGC (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), GCLineWidth | GCForeground | GCBackground, &gc_values); @@ -2947,7 +2975,7 @@ x_make_gc (struct frame *f) gc_values.background = FRAME_FOREGROUND_PIXEL (f); f->output_data.x->reverse_gc = XCreateGC (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), GCForeground | GCBackground | GCLineWidth, &gc_values); @@ -2956,7 +2984,7 @@ x_make_gc (struct frame *f) gc_values.background = f->output_data.x->cursor_pixel; gc_values.fill_style = FillOpaqueStippled; f->output_data.x->cursor_gc - = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), (GCForeground | GCBackground | GCFillStyle | GCLineWidth), &gc_values); @@ -5636,7 +5664,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) /* Border. */ f->border_width, CopyFromParent, InputOutput, CopyFromParent, - mask, &attrs); + mask, &attrs); + set_up_x_back_buffer (f); XChangeProperty (FRAME_X_DISPLAY (f), tip_window, FRAME_DISPLAY_INFO (f)->Xatom_net_window_type, XA_ATOM, 32, PropModeReplace, diff --git a/src/xfont.c b/src/xfont.c index 45b0e0a..c2b7317 100644 --- a/src/xfont.c +++ b/src/xfont.c @@ -1057,20 +1057,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y, { if (s->padding_p) for (i = 0; i < len; i++) - XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, str + i, 1); else - XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, str, len); } else { if (s->padding_p) for (i = 0; i < len; i++) - XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, str + i, 1); else - XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, str, len); } unblock_input (); @@ -1083,20 +1083,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y, { if (s->padding_p) for (i = 0; i < len; i++) - XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, s->char2b + from + i, 1); else - XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, s->char2b + from, len); } else { if (s->padding_p) for (i = 0; i < len; i++) - XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, s->char2b + from + i, 1); else - XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, s->char2b + from, len); } unblock_input (); diff --git a/src/xftfont.c b/src/xftfont.c index 34c6f7d..447adf6 100644 --- a/src/xftfont.c +++ b/src/xftfont.c @@ -586,7 +586,7 @@ xftfont_get_xft_draw (struct frame *f) { block_input (); xft_draw= XftDrawCreate (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_X_VISUAL (f), FRAME_X_COLORMAP (f)); unblock_input (); @@ -695,6 +695,13 @@ xftfont_end_for_frame (struct frame *f) return 0; } +static void +xftfont_flush_frame_caches (struct frame *f) +{ + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + xftfont_end_for_frame (f); +} + static bool xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object, Lisp_Object entity) @@ -777,6 +784,9 @@ This is needed with some fonts to correct vertical overlap of glyphs. */); #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF) xftfont_driver.shape = xftfont_shape; #endif + // When using X double buffering, the XftDraw structure we + // build seems to be useless once a frame is resized, so + xftfont_driver.flush_frame_caches = xftfont_flush_frame_caches; register_font_driver (&xftfont_driver, NULL); } diff --git a/src/xterm.c b/src/xterm.c index 7476694..7116784 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -45,6 +45,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <X11/extensions/Xrender.h> #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + /* Load sys/types.h if not already loaded. In some systems loading it twice is suicidal. */ #ifndef makedev @@ -360,7 +364,7 @@ x_begin_cr_clip (struct frame *f, GC gc) { cairo_surface_t *surface; surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_DISPLAY_INFO (f)->visual, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); @@ -722,7 +726,7 @@ x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) cairo_fill (cr); x_end_cr_clip (f); #else - XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y, width, height); #endif } @@ -740,7 +744,7 @@ x_draw_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) cairo_stroke (cr); x_end_cr_clip (f); #else - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y, width, height); #endif } @@ -756,7 +760,10 @@ x_clear_window (struct frame *f) cairo_paint (cr); x_end_cr_clip (f); #else - XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); + else + XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); #endif } @@ -1067,7 +1074,7 @@ x_draw_vertical_window_border (struct window *w, int x, int y0, int y1) #ifdef USE_CAIRO x_fill_rectangle (f, f->output_data.x->normal_gc, x, y0, 1, y1 - y0); #else - XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, y0, x, y1); #endif } @@ -1179,6 +1186,21 @@ x_update_window_end (struct window *w, bool cursor_on_p, /* End update of frame F. This function is installed as a hook in update_end. */ +#if defined (HAVE_XDBE) +static void +show_back_buffer (struct frame *f) +{ + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + { + XdbeSwapInfo swap_info; + memset (&swap_info, 0, sizeof (swap_info)); + swap_info.swap_window = FRAME_X_WINDOW (f); + swap_info.swap_action = XdbeCopied; + XdbeSwapBuffers (FRAME_X_DISPLAY (f), &swap_info, 1); + } +} +#endif + static void x_update_end (struct frame *f) { @@ -1207,7 +1229,7 @@ x_update_end (struct frame *f) if (! FRAME_EXTERNAL_MENU_BAR (f)) height += FRAME_MENU_BAR_HEIGHT (f); surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_DISPLAY_INFO (f)->visual, width, height); @@ -1220,7 +1242,14 @@ x_update_end (struct frame *f) cairo_destroy (cr); unblock_input (); } -#endif /* USE_CAIRO */ +#endif + +#ifdef HAVE_XDBE + if (redisplaying_p) + FRAME_X_NEED_BUFFER_FLIP (f) = true; + else + show_back_buffer (f); +#endif #ifndef XFlush block_input (); @@ -1229,6 +1258,17 @@ x_update_end (struct frame *f) #endif } +static void +x_redisplay_end (struct frame *f) +{ +#ifdef HAVE_XDBE + if (FRAME_X_NEED_BUFFER_FLIP (f)) + { + show_back_buffer (f); + FRAME_X_NEED_BUFFER_FLIP (f) = false; + } +#endif +} /* This function is called from various places in xdisp.c whenever a complete update has been performed. */ @@ -1354,7 +1394,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring #else /* not USE_CAIRO */ if (p->which) { - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); char *bits; Pixmap pixmap, clipmask = (Pixmap) 0; int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); @@ -1367,7 +1407,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring /* Draw the bitmap. I believe these small pixmaps can be cached by the server. */ - pixmap = XCreatePixmapFromBitmapData (display, window, bits, p->wd, p->h, + pixmap = XCreatePixmapFromBitmapData (display, drawable, bits, p->wd, p->h, (p->cursor_p ? (p->overlay_p ? face->background : f->output_data.x->cursor_pixel) @@ -1386,7 +1426,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring XChangeGC (display, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv); } - XCopyArea (display, pixmap, window, gc, 0, 0, + XCopyArea (display, pixmap, drawable, gc, 0, 0, p->wd, p->h, p->x, p->y); XFreePixmap (display, pixmap); @@ -2565,7 +2605,7 @@ x_setup_relief_color (struct frame *f, struct relief *relief, double factor, { xgcv.stipple = dpyinfo->gray; mask |= GCStipple; - relief->gc = XCreateGC (dpy, FRAME_X_WINDOW (f), mask, &xgcv); + relief->gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), mask, &xgcv); } else XChangeGC (dpy, relief->gc, mask, &xgcv); @@ -2696,7 +2736,7 @@ x_draw_relief_rect (struct frame *f, x_reset_clip_rectangles (f, bottom_right_gc); #else Display *dpy = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); int i; GC gc; @@ -2715,12 +2755,12 @@ x_draw_relief_rect (struct frame *f, if (top_p) { if (width == 1) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, top_y, right_x + !right_p, top_y); for (i = 1; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i * left_p, top_y + i, right_x + 1 - i * right_p, top_y + i); } @@ -2729,13 +2769,13 @@ x_draw_relief_rect (struct frame *f, if (left_p) { if (width == 1) - XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y); + XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y); - XClearArea (dpy, window, left_x, top_y, 1, 1, False); - XClearArea (dpy, window, left_x, bottom_y, 1, 1, False); + x_clear_area(f, left_x, top_y, 1, 1); + x_clear_area(f, left_x, bottom_y, 1, 1); for (i = (width > 1 ? 1 : 0); i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i, top_y + (i + 1) * top_p, left_x + i, bottom_y + 1 - (i + 1) * bot_p); } @@ -2751,23 +2791,23 @@ x_draw_relief_rect (struct frame *f, { /* Outermost top line. */ if (top_p) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, top_y, right_x + !right_p, top_y); /* Outermost left line. */ if (left_p) - XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y); + XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y); } /* Bottom. */ if (bot_p) { - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, bottom_y, right_x + !right_p, bottom_y); for (i = 1; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i * left_p, bottom_y - i, right_x + 1 - i * right_p, bottom_y - i); } @@ -2775,10 +2815,10 @@ x_draw_relief_rect (struct frame *f, /* Right. */ if (right_p) { - XClearArea (dpy, window, right_x, top_y, 1, 1, False); - XClearArea (dpy, window, right_x, bottom_y, 1, 1, False); + x_clear_area(f, right_x, top_y, 1, 1); + x_clear_area(f, right_x, bottom_y, 1, 1); for (i = 0; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, right_x - i, top_y + (i + 1) * top_p, right_x - i, bottom_y + 1 - (i + 1) * bot_p); } @@ -3741,7 +3781,7 @@ x_shift_glyphs_for_insert (struct frame *f, int x, int y, int width, int height, /* Never called on a GUI frame, see http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html */ - XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), + XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, y, width, height, x + shift_by, y); @@ -3782,8 +3822,14 @@ x_clear_area (struct frame *f, int x, int y, int width, int height) cairo_fill (cr); x_end_cr_clip (f); #else - x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), - x, y, width, height, False); + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + XFillRectangle (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f), + f->output_data.x->reverse_gc, + x, y, width, height); + else + x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + x, y, width, height, False); #endif } @@ -4109,7 +4155,7 @@ x_scroll_run (struct window *w, struct run *run) SET_FRAME_GARBAGED (f); #else XCopyArea (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, from_y, width, height, @@ -7769,6 +7815,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, SET_FRAME_VISIBLE (f, 1); SET_FRAME_ICONIFIED (f, false); f->output_data.x->has_been_visible = true; + font_flush_frame_caches (f); SET_FRAME_GARBAGED (f); } else @@ -8437,7 +8484,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, { if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width) - SET_FRAME_GARBAGED (f); + { + font_flush_frame_caches (f); + SET_FRAME_GARBAGED (f); + } FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height; FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width; } @@ -8463,7 +8513,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, || configureEvent.xconfigure.height != FRAME_PIXEL_HEIGHT (f)) { change_frame_size (f, width, height, false, true, false, true); - x_clear_under_internal_border (f); + x_clear_under_internal_border (f); + font_flush_frame_caches (f); SET_FRAME_GARBAGED (f); cancel_mouse_face (f); } @@ -8880,7 +8931,7 @@ x_draw_hollow_cursor (struct window *w, struct glyph_row *row) if (dpyinfo->scratch_cursor_gc) XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv); else - dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_WINDOW (f), + dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), GCForeground, &xgcv); gc = dpyinfo->scratch_cursor_gc; @@ -8937,7 +8988,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text else { Display *dpy = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); GC gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc; unsigned long mask = GCForeground | GCBackground | GCGraphicsExposures; struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id); @@ -8958,7 +9009,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text XChangeGC (dpy, gc, mask, &xgcv); else { - gc = XCreateGC (dpy, window, mask, &xgcv); + gc = XCreateGC (dpy, drawable, mask, &xgcv); FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc; } @@ -11271,7 +11322,14 @@ x_free_frame_resources (struct frame *f) #endif /* USE_GTK */ if (FRAME_X_WINDOW (f)) - XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); + { +#ifdef HAVE_XDBE + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f)); +#endif + XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); + } #endif /* !USE_X_TOOLKIT */ unload_color (f, FRAME_FOREGROUND_PIXEL (f)); @@ -12111,7 +12169,17 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) } else dpyinfo->cmap = XCreateColormap (dpyinfo->display, dpyinfo->root_window, - dpyinfo->visual, AllocNone); + dpyinfo->visual, AllocNone); + +#ifdef HAVE_XDBE + dpyinfo->supports_xdbe = false; + { + int xdbe_major; + int xdbe_minor; + if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor)) + dpyinfo->supports_xdbe = true; + } +#endif #ifdef HAVE_XFT { @@ -12590,6 +12658,7 @@ x_create_terminal (struct x_display_info *dpyinfo) terminal->toggle_invisible_pointer_hook = XTtoggle_invisible_pointer; terminal->update_begin_hook = x_update_begin; terminal->update_end_hook = x_update_end; + terminal->redisplay_end_hook = x_redisplay_end; terminal->read_socket_hook = XTread_socket; terminal->frame_up_to_date_hook = XTframe_up_to_date; terminal->mouse_position_hook = XTmouse_position; diff --git a/src/xterm.h b/src/xterm.h index 675a484..cb1aa1d 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -475,6 +475,10 @@ struct x_display_info #ifdef USE_XCB xcb_connection_t *xcb_connection; #endif + +#ifdef HAVE_XDBE + bool supports_xdbe; +#endif }; #ifdef HAVE_X_I18N @@ -527,6 +531,17 @@ struct x_output and the X window has not yet been created. */ Window window_desc; +#ifdef HAVE_XDBE + /* The drawable to which we're rendering. In the single-buffered + base, the window itself. In the double-buffered case, the + window's back buffer. */ + Drawable draw_desc; + + /* Set to true when we need a buffer flip. We do a buffer flip only + at the end of redisplay in order to minimize flicker. */ + bool need_buffer_flip; +#endif + /* The X window used for the bitmap icon; or 0 if we don't have a bitmap icon. */ Window icon_desc; @@ -737,6 +752,18 @@ enum /* Return the X window used for displaying data in frame F. */ #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc) +/* Return the drawable used for rendering to frame F. */ +#ifdef HAVE_XDBE +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc) +#else +#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f))) +#endif + +/* Return the need-buffer-flip flag for frame F. */ +#ifdef HAVE_XDBE +#define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip) +#endif + /* Return the outermost X window associated with the frame F. */ #ifdef USE_X_TOOLKIT #define FRAME_OUTER_WINDOW(f) ((f)->output_data.x->widget ? \ @@ -1140,6 +1167,8 @@ extern bool x_wm_supports (struct frame *, Atom); extern void x_wait_for_event (struct frame *, int); extern void x_clear_under_internal_border (struct frame *f); +extern void set_up_x_back_buffer (struct frame* f); + /* Defined in xselect.c. */ extern void x_handle_property_notify (const XPropertyEvent *); [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 3:31 ` Clément Pit--Claudel 2016-10-21 3:41 ` Óscar Fuentes @ 2016-10-21 8:23 ` Andreas Schwab 2016-10-21 8:25 ` Andreas Schwab 1 sibling, 1 reply; 31+ messages in thread From: Andreas Schwab @ 2016-10-21 8:23 UTC (permalink / raw) To: Clément Pit--Claudel; +Cc: emacs-devel On Okt 20 2016, Clément Pit--Claudel <clement.pit@gmail.com> wrote: > On 2016-10-20 23:24, Óscar Fuentes wrote: >> I'm eager to try your code, but `patch' complains about >> >> oscar@qcore:~/dev/emacs/stable/source$ patch -p 1 < ~/p.patch >> patching file configure.ac >> patching file src/Makefile.in >> patch: **** malformed patch at line 60: $(LIBSELINUX_LIBS) \ > > The patch applies fine for me with git apply --reject --ignore-space-change --ignore-whitespace double-buffer.patch. Maybe that works for you too? You should not use format=flowed when posting patches. Andreas. -- Andreas Schwab, schwab@linux-m68k.org GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5 "And now for something completely different." ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 8:23 ` Andreas Schwab @ 2016-10-21 8:25 ` Andreas Schwab 0 siblings, 0 replies; 31+ messages in thread From: Andreas Schwab @ 2016-10-21 8:25 UTC (permalink / raw) To: Clément Pit--Claudel; +Cc: emacs-devel On Okt 21 2016, Andreas Schwab <schwab@linux-m68k.org> wrote: > On Okt 20 2016, Clément Pit--Claudel <clement.pit@gmail.com> wrote: > >> On 2016-10-20 23:24, Óscar Fuentes wrote: >>> I'm eager to try your code, but `patch' complains about >>> >>> oscar@qcore:~/dev/emacs/stable/source$ patch -p 1 < ~/p.patch >>> patching file configure.ac >>> patching file src/Makefile.in >>> patch: **** malformed patch at line 60: $(LIBSELINUX_LIBS) \ >> >> The patch applies fine for me with git apply --reject --ignore-space-change --ignore-whitespace double-buffer.patch. Maybe that works for you too? > > You should not use format=flowed when posting patches. That "you" was meant to address the OP, sorry. Andreas. -- Andreas Schwab, schwab@linux-m68k.org GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5 "And now for something completely different." ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione 2016-10-21 2:27 ` Lars Ingebrigtsen 2016-10-21 3:24 ` Óscar Fuentes @ 2016-10-21 3:56 ` Clément Pit--Claudel 2016-10-21 8:49 ` Eli Zaretskii 3 siblings, 0 replies; 31+ messages in thread From: Clément Pit--Claudel @ 2016-10-21 3:56 UTC (permalink / raw) To: emacs-devel [-- Attachment #1.1: Type: text/plain, Size: 42616 bytes --] Hi Daniel, This is brillant! I applied the patch and everything seems to work great: all of the cases in which I usually see flickering (I tried resizing windows, running a process in the background, and isearch) seemed fixed :) Congrats on this very nice patch :) Clément. On 2016-10-20 21:32, Daniel Colascione wrote: > This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to avoid showing the user incomplete drawing results. Without this patch, I can make Emacs flicker like crazy by running isearch for a piece of text unique in a buffer and holding down C-s. With this patch, Emacs does not flicker no matter what I do to it. > > The patch also stops flickering that occurs when using the "solid resizing" feature of some window managers --- i.e., when the WM redraws windows as the user drags their edges, as opposed to displaying some kind of bounding-box in lieu of the actual window contents. > > I've tried to avoid changing the logic in the single-buffered case (which we still support), and I've also tried to avoid touching Cairo rendering. This patch should work with any toolkit --- I've tested GTK+3 and no toolkit at all. > > A few notes: > > * We do a buffer flip at the end of redisplay instead of in x_update_end() so the user never sees the completely-cleared state that we enter immediately after clear_garbaged_frames(). x_update_end() does do a buffer flip if it's called outside redisplay. I've added a new terminal hook to support this hack. > > * The DBE documentation claims that XClearWindow and XClearArea clear both the front and back buffers. It's a lie. In my experiments, these functions clear only the front buffer. > > * XFT stops drawing after we give XftCreateDraw a DBE back-buffer and the size of that back buffer changes. To work around this problem, we discard any caches XftDraw object we might have to a frame after that frame changes size. I haven't noticed any performance problems. > > commit 15fdd8f63533201f05627ede634a8f5ae4757d7e > Author: Daniel Colascione <dancol@dancol.org> > Date: Thu Oct 20 16:50:54 2016 -0700 > > Add double-buffered output support to Emacs > > diff --git a/configure.ac b/configure.ac > index cd11b10..4716b43 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -3712,6 +3712,24 @@ AC_DEFUN > AC_SUBST(XFIXES_CFLAGS) > AC_SUBST(XFIXES_LIBS) > > +### Use Xdbe (-lXdbe) if available > +HAVE_XDBE=no > +if test "${HAVE_X11}" = "yes"; then > + AC_CHECK_HEADER(X11/extensions/Xdbe.h, > + [AC_CHECK_LIB(Xext, XdbeAllocateBackBufferName, HAVE_XDBE=yes)], > + [], > + [#include <X11/Xlib.h> > + ]) > + if test $HAVE_XDBE = yes; then > + XDBE_LIBS=-lXext > + fi > + if test $HAVE_XDBE = yes; then > + AC_DEFINE(HAVE_XDBE, 1, [Define to 1 if you have the Xdbe extension.]) > + fi > +fi > +AC_SUBST(XDBE_CFLAGS) > +AC_SUBST(XDBE_LIBS) > + > ### Use libxml (-lxml2) if available > ### mingw32 doesn't use -lxml2, since it loads the library dynamically. > HAVE_LIBXML2=no > diff --git a/src/Makefile.in b/src/Makefile.in > index 89f7a92..dc0bfff 100644 > --- a/src/Makefile.in > +++ b/src/Makefile.in > @@ -254,6 +254,9 @@ XINERAMA_CFLAGS = > XFIXES_LIBS = @XFIXES_LIBS@ > XFIXES_CFLAGS = @XFIXES_CFLAGS@ > > +XDBE_LIBS = @XDBE_LIBS@ > +XDBE_CFLAGS = @XDBE_CFLAGS@ > + > ## widget.o if USE_X_TOOLKIT, otherwise empty. > WIDGET_OBJ=@WIDGET_OBJ@ > > @@ -372,7 +375,7 @@ ALL_CFLAGS= > $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \ > $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \ > $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \ > - $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \ > + $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \ > $(WEBKIT_CFLAGS) \ > $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ > $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ > @@ -489,6 +492,7 @@ LIBES = > $(WEBKIT_LIBS) \ > $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \ > $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \ > + $(XDBE_LIBS) \ > $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \ > $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ > $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ > diff --git a/src/dispnew.c b/src/dispnew.c > index 70d4de0..8f81cee 100644 > --- a/src/dispnew.c > +++ b/src/dispnew.c > @@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f) > { > /* Error if F has no glyphs. */ > eassert (f->glyphs_initialized_p); > + font_flush_frame_caches (f); > update_begin (f); > if (FRAME_MSDOS_P (f)) > FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f)); > diff --git a/src/font.c b/src/font.c > index f8e6794..033995e 100644 > --- a/src/font.c > +++ b/src/font.c > @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, Lisp_Object arg, Lisp_Object result) > } > > void > +font_flush_frame_caches (struct frame *f) > +{ > + struct font_driver_list *list; > + > + for (list = f->font_driver_list; list; list = list->next) > + if (list->on && list->driver->flush_frame_caches) > + list->driver->flush_frame_caches (f); > +} > + > +void > syms_of_font (void) > { > sort_shift_bits[FONT_TYPE_INDEX] = 0; > diff --git a/src/font.h b/src/font.h > index cf47729..961e9c4 100644 > --- a/src/font.h > +++ b/src/font.h > @@ -763,6 +763,12 @@ struct font_driver > Return non-nil if the driver support rendering of combining > characters for FONT according to Unicode combining class. */ > Lisp_Object (*combining_capability) (struct font *font); > + > + /* Optional > + > + Called when frame F is redrawn from scratch. Font engines may > + invalidate certain caches in this case. */ > + void (*flush_frame_caches) (struct frame *f); > }; > > > @@ -862,7 +868,9 @@ extern void *font_get_frame_data (struct frame *f, Lisp_Object); > extern void font_filter_properties (Lisp_Object font, > Lisp_Object alist, > const char *const boolean_properties[], > - const char *const non_boolean_properties[]); > + const char *const non_boolean_properties[]); > + > +extern void font_flush_frame_caches (struct frame *f); > > #ifdef HAVE_FREETYPE > extern struct font_driver ftfont_driver; > diff --git a/src/ftxfont.c b/src/ftxfont.c > index f49d44f..bfdeb40 100644 > --- a/src/ftxfont.c > +++ b/src/ftxfont.c > @@ -95,7 +95,7 @@ ftxfont_get_gcs (struct frame *f, unsigned long foreground, unsigned long backgr > if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &color)) > break; > xgcv.foreground = color.pixel; > - new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > GCForeground, &xgcv); > } > unblock_input (); > @@ -139,14 +139,14 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, > p[n[0]].y = y - bitmap.top + i; > if (++n[0] == size) > { > - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > gc_fore, p, size, CoordModeOrigin); > n[0] = 0; > } > } > } > if (flush && n[0] > 0) > - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > gc_fore, p, n[0], CoordModeOrigin); > } > else > @@ -168,7 +168,7 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, > pp[n[idx]].y = y - bitmap.top + i; > if (++(n[idx]) == size) > { > - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > idx == 6 ? gc_fore : gcs[idx], pp, size, > CoordModeOrigin); > n[idx] = 0; > @@ -180,10 +180,10 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, > { > for (i = 0; i < 6; i++) > if (n[i] > 0) > - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > gcs[i], p + 0x100 * i, n[i], CoordModeOrigin); > if (n[6] > 0) > - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > gc_fore, p + 0x600, n[6], CoordModeOrigin); > } > } > @@ -203,7 +203,7 @@ ftxfont_draw_background (struct frame *f, struct font *font, GC gc, int x, int y > XGetGCValues (FRAME_X_DISPLAY (f), gc, > GCForeground | GCBackground, &xgcv); > XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background); > - XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc, > + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, > x, y - FONT_BASE (font), width, FONT_HEIGHT (font)); > XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground); > } > diff --git a/src/gtkutil.c b/src/gtkutil.c > index 88e6d30..f81940b 100644 > --- a/src/gtkutil.c > +++ b/src/gtkutil.c > @@ -48,6 +48,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ > #include "emacsgtkfixed.h" > #endif > > +#ifdef HAVE_XDBE > +#include <X11/extensions/Xdbe.h> > +#endif > + > #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW > #define gtk_widget_set_has_window(w, b) \ > (gtk_fixed_set_has_window (GTK_FIXED (w), b)) > @@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f) > by callers of this function. */ > gtk_widget_realize (wfixed); > FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed); > + set_up_x_back_buffer (f); > > /* Since GTK clears its window by filling with the background color, > we must keep X and GTK background in sync. */ > @@ -1296,6 +1301,15 @@ xg_free_frame_widgets (struct frame *f) > if (tbinfo) > xfree (tbinfo); > > +#ifdef HAVE_XDBE > + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) > + { > + XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), > + FRAME_X_DRAWABLE (f)); > + FRAME_X_DRAWABLE (f) = 0; > + } > +#endif > + > gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f)); > FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */ > FRAME_GTK_OUTER_WIDGET (f) = 0; > diff --git a/src/image.c b/src/image.c > index 9bd2455..1303a93 100644 > --- a/src/image.c > +++ b/src/image.c > @@ -220,7 +220,7 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi > > #ifdef HAVE_X_WINDOWS > Pixmap bitmap; > - bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > bits, width, height); > if (! bitmap) > return -1; > @@ -327,7 +327,7 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file) > > filename = SSDATA (found); > > - result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > filename, &width, &height, &bitmap, &xhot, &yhot); > if (result != BitmapSuccess) > return -1; > @@ -1952,7 +1952,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, > { > #ifdef HAVE_X_WINDOWS > Display *display = FRAME_X_DISPLAY (f); > - Window window = FRAME_X_WINDOW (f); > + Drawable drawable = FRAME_X_DRAWABLE (f); > Screen *screen = FRAME_X_SCREEN (f); > > eassert (input_blocked_p ()); > @@ -1981,7 +1981,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, > (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height); > > /* Allocate a pixmap of the same size. */ > - *pixmap = XCreatePixmap (display, window, width, height, depth); > + *pixmap = XCreatePixmap (display, drawable, width, height, depth); > if (*pixmap == NO_PIXMAP) > { > x_destroy_x_image (*ximg); > @@ -2742,7 +2742,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data, > img->pixmap = > (x_check_image_size (0, img->width, img->height) > ? XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f), > - FRAME_X_WINDOW (f), > + FRAME_X_DRAWABLE (f), > data, > img->width, img->height, > fg, bg, > @@ -3520,7 +3520,7 @@ x_create_bitmap_from_xpm_data (struct frame *f, const char **bits) > xpm_init_color_cache (f, &attrs); > #endif > > - rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > (char **) bits, &bitmap, &mask, &attrs); > if (rc != XpmSuccess) > { > @@ -3758,7 +3758,7 @@ xpm_load (struct frame *f, struct image *img) > #ifdef HAVE_X_WINDOWS > if (rc == XpmSuccess) > { > - img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > img->ximg->width, img->ximg->height, > img->ximg->depth); > if (img->pixmap == NO_PIXMAP) > @@ -3768,7 +3768,7 @@ xpm_load (struct frame *f, struct image *img) > } > else if (img->mask_img) > { > - img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > img->mask_img->width, > img->mask_img->height, > img->mask_img->depth); > @@ -9541,7 +9541,7 @@ gs_load (struct frame *f, struct image *img) > { > /* Only W32 version did BLOCK_INPUT here. ++kfs */ > block_input (); > - img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > img->width, img->height, > DefaultDepthOfScreen (FRAME_X_SCREEN (f))); > unblock_input (); > @@ -9557,7 +9557,7 @@ gs_load (struct frame *f, struct image *img) > if successful. We do not record_unwind_protect here because > other places in redisplay like calling window scroll functions > don't either. Let the Lisp loader use `unwind-protect' instead. */ > - printnum1 = FRAME_X_WINDOW (f); > + printnum1 = FRAME_X_DRAWABLE (f); > printnum2 = img->pixmap; > window_and_pixmap_id > = make_formatted_string (buffer, "%"pMu" %"pMu, printnum1, printnum2); > diff --git a/src/termhooks.h b/src/termhooks.h > index ff74d99..c8d7fae 100644 > --- a/src/termhooks.h > +++ b/src/termhooks.h > @@ -477,6 +477,7 @@ struct terminal > > void (*update_begin_hook) (struct frame *); > void (*update_end_hook) (struct frame *); > + void (*redisplay_end_hook) (struct frame *); > void (*set_terminal_window_hook) (struct frame *, int); > > /* Multi-frame and mouse support hooks. */ > diff --git a/src/xdisp.c b/src/xdisp.c > index 3af5ea4..04f0ca1 100644 > --- a/src/xdisp.c > +++ b/src/xdisp.c > @@ -2501,7 +2501,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) > > /* Visible feedback for debugging. */ > #if false && defined HAVE_X_WINDOWS > - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > f->output_data.x->normal_gc, > gx, gy, width, height); > #endif > @@ -14211,6 +14211,13 @@ redisplay_internal (void) > windows_or_buffers_changed = 0; > } > > + FOR_EACH_FRAME (tail, frame) > + { > + struct frame *f = XFRAME (frame); > + if (FRAME_TERMINAL (f)->redisplay_end_hook) > + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); > + } > + > /* Start SIGIO interrupts coming again. Having them off during the > code above makes it less likely one will discard output, but not > impossible, since there might be stuff in the system buffer here. > @@ -24608,7 +24615,7 @@ init_glyph_string (struct glyph_string *s, > s->hdc = hdc; > #endif > s->display = FRAME_X_DISPLAY (s->f); > - s->window = FRAME_X_WINDOW (s->f); > + s->window = FRAME_X_DRAWABLE (s->f); > s->char2b = char2b; > s->hl = hl; > s->row = row; > diff --git a/src/xfaces.c b/src/xfaces.c > index 5837f35..accb98b 100644 > --- a/src/xfaces.c > +++ b/src/xfaces.c > @@ -495,7 +495,7 @@ x_create_gc (struct frame *f, unsigned long mask, XGCValues *xgcv) > { > GC gc; > block_input (); > - gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), mask, xgcv); > + gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), mask, xgcv); > unblock_input (); > IF_DEBUG (++ngcs); > return gc; > diff --git a/src/xfns.c b/src/xfns.c > index 8571d0e..2098036 100644 > --- a/src/xfns.c > +++ b/src/xfns.c > @@ -53,6 +53,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ > #include "gtkutil.h" > #endif > > +#ifdef HAVE_XDBE > +#include <X11/extensions/Xdbe.h> > +#endif > + > #ifdef USE_X_TOOLKIT > #include <X11/Shell.h> > > @@ -2483,6 +2487,29 @@ xic_set_xfontset (struct frame *f, const char *base_fontname) > > > \f > + > +void > +set_up_x_back_buffer (struct frame* f) > +{ > + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); > +#ifdef HAVE_XDBE > + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) > + { > + /* If allocating a back buffer fails, just use single-buffered > + rendering. */ > + x_sync (f); > + x_catch_errors (FRAME_X_DISPLAY (f)); > + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( > + FRAME_X_DISPLAY (f), > + FRAME_X_WINDOW (f), > + XdbeCopied); > + if (x_had_errors_p (FRAME_X_DISPLAY (f))) > + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); > + x_uncatch_errors_after_check (); > + } > +#endif > +} > + > #ifdef USE_X_TOOLKIT > > /* Create and set up the X widget for frame F. */ > @@ -2638,7 +2665,7 @@ x_window (struct frame *f, long window_prompting) > f->output_data.x->parent_desc, 0, 0); > > FRAME_X_WINDOW (f) = XtWindow (frame_widget); > - > + set_up_x_back_buffer (f); > validate_x_resource_name (); > > class_hints.res_name = SSDATA (Vx_resource_name); > @@ -2784,7 +2811,8 @@ x_window (struct frame *f) > CopyFromParent, /* depth */ > InputOutput, /* class */ > FRAME_X_VISUAL (f), > - attribute_mask, &attributes); > + attribute_mask, &attributes); > + set_up_x_back_buffer (f); > > #ifdef HAVE_X_I18N > if (use_xim) > @@ -2938,7 +2966,7 @@ x_make_gc (struct frame *f) > gc_values.line_width = 0; /* Means 1 using fast algorithm. */ > f->output_data.x->normal_gc > = XCreateGC (FRAME_X_DISPLAY (f), > - FRAME_X_WINDOW (f), > + FRAME_X_DRAWABLE (f), > GCLineWidth | GCForeground | GCBackground, > &gc_values); > > @@ -2947,7 +2975,7 @@ x_make_gc (struct frame *f) > gc_values.background = FRAME_FOREGROUND_PIXEL (f); > f->output_data.x->reverse_gc > = XCreateGC (FRAME_X_DISPLAY (f), > - FRAME_X_WINDOW (f), > + FRAME_X_DRAWABLE (f), > GCForeground | GCBackground | GCLineWidth, > &gc_values); > > @@ -2956,7 +2984,7 @@ x_make_gc (struct frame *f) > gc_values.background = f->output_data.x->cursor_pixel; > gc_values.fill_style = FillOpaqueStippled; > f->output_data.x->cursor_gc > - = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > (GCForeground | GCBackground > | GCFillStyle | GCLineWidth), > &gc_values); > @@ -5636,7 +5664,8 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) > /* Border. */ > f->border_width, > CopyFromParent, InputOutput, CopyFromParent, > - mask, &attrs); > + mask, &attrs); > + set_up_x_back_buffer (f); > XChangeProperty (FRAME_X_DISPLAY (f), tip_window, > FRAME_DISPLAY_INFO (f)->Xatom_net_window_type, > XA_ATOM, 32, PropModeReplace, > diff --git a/src/xfont.c b/src/xfont.c > index 45b0e0a..c2b7317 100644 > --- a/src/xfont.c > +++ b/src/xfont.c > @@ -1057,20 +1057,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y, > { > if (s->padding_p) > for (i = 0; i < len; i++) > - XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), > + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), > gc, x + i, y, str + i, 1); > else > - XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), > + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), > gc, x, y, str, len); > } > else > { > if (s->padding_p) > for (i = 0; i < len; i++) > - XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), > + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), > gc, x + i, y, str + i, 1); > else > - XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), > + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), > gc, x, y, str, len); > } > unblock_input (); > @@ -1083,20 +1083,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y, > { > if (s->padding_p) > for (i = 0; i < len; i++) > - XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), > + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), > gc, x + i, y, s->char2b + from + i, 1); > else > - XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), > + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), > gc, x, y, s->char2b + from, len); > } > else > { > if (s->padding_p) > for (i = 0; i < len; i++) > - XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), > + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), > gc, x + i, y, s->char2b + from + i, 1); > else > - XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), > + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), > gc, x, y, s->char2b + from, len); > } > unblock_input (); > diff --git a/src/xftfont.c b/src/xftfont.c > index 34c6f7d..447adf6 100644 > --- a/src/xftfont.c > +++ b/src/xftfont.c > @@ -586,7 +586,7 @@ xftfont_get_xft_draw (struct frame *f) > { > block_input (); > xft_draw= XftDrawCreate (FRAME_X_DISPLAY (f), > - FRAME_X_WINDOW (f), > + FRAME_X_DRAWABLE (f), > FRAME_X_VISUAL (f), > FRAME_X_COLORMAP (f)); > unblock_input (); > @@ -695,6 +695,13 @@ xftfont_end_for_frame (struct frame *f) > return 0; > } > > +static void > +xftfont_flush_frame_caches (struct frame *f) > +{ > + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) > + xftfont_end_for_frame (f); > +} > + > static bool > xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object, > Lisp_Object entity) > @@ -777,6 +784,9 @@ This is needed with some fonts to correct vertical overlap of glyphs. */); > #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF) > xftfont_driver.shape = xftfont_shape; > #endif > + // When using X double buffering, the XftDraw structure we > + // build seems to be useless once a frame is resized, so > + xftfont_driver.flush_frame_caches = xftfont_flush_frame_caches; > > register_font_driver (&xftfont_driver, NULL); > } > diff --git a/src/xterm.c b/src/xterm.c > index 7476694..7116784 100644 > --- a/src/xterm.c > +++ b/src/xterm.c > @@ -45,6 +45,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ > #include <X11/extensions/Xrender.h> > #endif > > +#ifdef HAVE_XDBE > +#include <X11/extensions/Xdbe.h> > +#endif > + > /* Load sys/types.h if not already loaded. > In some systems loading it twice is suicidal. */ > #ifndef makedev > @@ -360,7 +364,7 @@ x_begin_cr_clip (struct frame *f, GC gc) > { > cairo_surface_t *surface; > surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), > - FRAME_X_WINDOW (f), > + FRAME_X_DRAWABLE (f), > FRAME_DISPLAY_INFO (f)->visual, > FRAME_PIXEL_WIDTH (f), > FRAME_PIXEL_HEIGHT (f)); > @@ -722,7 +726,7 @@ x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) > cairo_fill (cr); > x_end_cr_clip (f); > #else > - XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > gc, x, y, width, height); > #endif > } > @@ -740,7 +744,7 @@ x_draw_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) > cairo_stroke (cr); > x_end_cr_clip (f); > #else > - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > gc, x, y, width, height); > #endif > } > @@ -756,7 +760,10 @@ x_clear_window (struct frame *f) > cairo_paint (cr); > x_end_cr_clip (f); > #else > - XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); > + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) > + x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); > + else > + XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); > #endif > } > > @@ -1067,7 +1074,7 @@ x_draw_vertical_window_border (struct window *w, int x, int y0, int y1) > #ifdef USE_CAIRO > x_fill_rectangle (f, f->output_data.x->normal_gc, x, y0, 1, y1 - y0); > #else > - XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), > f->output_data.x->normal_gc, x, y0, x, y1); > #endif > } > @@ -1179,6 +1186,21 @@ x_update_window_end (struct window *w, bool cursor_on_p, > /* End update of frame F. This function is installed as a hook in > update_end. */ > > +#if defined (HAVE_XDBE) > +static void > +show_back_buffer (struct frame *f) > +{ > + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) > + { > + XdbeSwapInfo swap_info; > + memset (&swap_info, 0, sizeof (swap_info)); > + swap_info.swap_window = FRAME_X_WINDOW (f); > + swap_info.swap_action = XdbeCopied; > + XdbeSwapBuffers (FRAME_X_DISPLAY (f), &swap_info, 1); > + } > +} > +#endif > + > static void > x_update_end (struct frame *f) > { > @@ -1207,7 +1229,7 @@ x_update_end (struct frame *f) > if (! FRAME_EXTERNAL_MENU_BAR (f)) > height += FRAME_MENU_BAR_HEIGHT (f); > surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), > - FRAME_X_WINDOW (f), > + FRAME_X_DRAWABLE (f), > FRAME_DISPLAY_INFO (f)->visual, > width, > height); > @@ -1220,7 +1242,14 @@ x_update_end (struct frame *f) > cairo_destroy (cr); > unblock_input (); > } > -#endif /* USE_CAIRO */ > +#endif > + > +#ifdef HAVE_XDBE > + if (redisplaying_p) > + FRAME_X_NEED_BUFFER_FLIP (f) = true; > + else > + show_back_buffer (f); > +#endif > > #ifndef XFlush > block_input (); > @@ -1229,6 +1258,17 @@ x_update_end (struct frame *f) > #endif > } > > +static void > +x_redisplay_end (struct frame *f) > +{ > +#ifdef HAVE_XDBE > + if (FRAME_X_NEED_BUFFER_FLIP (f)) > + { > + show_back_buffer (f); > + FRAME_X_NEED_BUFFER_FLIP (f) = false; > + } > +#endif > +} > > /* This function is called from various places in xdisp.c > whenever a complete update has been performed. */ > @@ -1354,7 +1394,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring > #else /* not USE_CAIRO */ > if (p->which) > { > - Window window = FRAME_X_WINDOW (f); > + Drawable drawable = FRAME_X_DRAWABLE (f); > char *bits; > Pixmap pixmap, clipmask = (Pixmap) 0; > int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); > @@ -1367,7 +1407,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring > > /* Draw the bitmap. I believe these small pixmaps can be cached > by the server. */ > - pixmap = XCreatePixmapFromBitmapData (display, window, bits, p->wd, p->h, > + pixmap = XCreatePixmapFromBitmapData (display, drawable, bits, p->wd, p->h, > (p->cursor_p > ? (p->overlay_p ? face->background > : f->output_data.x->cursor_pixel) > @@ -1386,7 +1426,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring > XChangeGC (display, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv); > } > > - XCopyArea (display, pixmap, window, gc, 0, 0, > + XCopyArea (display, pixmap, drawable, gc, 0, 0, > p->wd, p->h, p->x, p->y); > XFreePixmap (display, pixmap); > > @@ -2565,7 +2605,7 @@ x_setup_relief_color (struct frame *f, struct relief *relief, double factor, > { > xgcv.stipple = dpyinfo->gray; > mask |= GCStipple; > - relief->gc = XCreateGC (dpy, FRAME_X_WINDOW (f), mask, &xgcv); > + relief->gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), mask, &xgcv); > } > else > XChangeGC (dpy, relief->gc, mask, &xgcv); > @@ -2696,7 +2736,7 @@ x_draw_relief_rect (struct frame *f, > x_reset_clip_rectangles (f, bottom_right_gc); > #else > Display *dpy = FRAME_X_DISPLAY (f); > - Window window = FRAME_X_WINDOW (f); > + Drawable drawable = FRAME_X_DRAWABLE (f); > int i; > GC gc; > > @@ -2715,12 +2755,12 @@ x_draw_relief_rect (struct frame *f, > if (top_p) > { > if (width == 1) > - XDrawLine (dpy, window, gc, > + XDrawLine (dpy, drawable, gc, > left_x + left_p, top_y, > right_x + !right_p, top_y); > > for (i = 1; i < width; ++i) > - XDrawLine (dpy, window, gc, > + XDrawLine (dpy, drawable, gc, > left_x + i * left_p, top_y + i, > right_x + 1 - i * right_p, top_y + i); > } > @@ -2729,13 +2769,13 @@ x_draw_relief_rect (struct frame *f, > if (left_p) > { > if (width == 1) > - XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y); > + XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y); > > - XClearArea (dpy, window, left_x, top_y, 1, 1, False); > - XClearArea (dpy, window, left_x, bottom_y, 1, 1, False); > + x_clear_area(f, left_x, top_y, 1, 1); > + x_clear_area(f, left_x, bottom_y, 1, 1); > > for (i = (width > 1 ? 1 : 0); i < width; ++i) > - XDrawLine (dpy, window, gc, > + XDrawLine (dpy, drawable, gc, > left_x + i, top_y + (i + 1) * top_p, > left_x + i, bottom_y + 1 - (i + 1) * bot_p); > } > @@ -2751,23 +2791,23 @@ x_draw_relief_rect (struct frame *f, > { > /* Outermost top line. */ > if (top_p) > - XDrawLine (dpy, window, gc, > + XDrawLine (dpy, drawable, gc, > left_x + left_p, top_y, > right_x + !right_p, top_y); > > /* Outermost left line. */ > if (left_p) > - XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y); > + XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y); > } > > /* Bottom. */ > if (bot_p) > { > - XDrawLine (dpy, window, gc, > + XDrawLine (dpy, drawable, gc, > left_x + left_p, bottom_y, > right_x + !right_p, bottom_y); > for (i = 1; i < width; ++i) > - XDrawLine (dpy, window, gc, > + XDrawLine (dpy, drawable, gc, > left_x + i * left_p, bottom_y - i, > right_x + 1 - i * right_p, bottom_y - i); > } > @@ -2775,10 +2815,10 @@ x_draw_relief_rect (struct frame *f, > /* Right. */ > if (right_p) > { > - XClearArea (dpy, window, right_x, top_y, 1, 1, False); > - XClearArea (dpy, window, right_x, bottom_y, 1, 1, False); > + x_clear_area(f, right_x, top_y, 1, 1); > + x_clear_area(f, right_x, bottom_y, 1, 1); > for (i = 0; i < width; ++i) > - XDrawLine (dpy, window, gc, > + XDrawLine (dpy, drawable, gc, > right_x - i, top_y + (i + 1) * top_p, > right_x - i, bottom_y + 1 - (i + 1) * bot_p); > } > @@ -3741,7 +3781,7 @@ x_shift_glyphs_for_insert (struct frame *f, int x, int y, int width, int height, > /* Never called on a GUI frame, see > http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html > */ > - XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), > + XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), > f->output_data.x->normal_gc, > x, y, width, height, > x + shift_by, y); > @@ -3782,8 +3822,14 @@ x_clear_area (struct frame *f, int x, int y, int width, int height) > cairo_fill (cr); > x_end_cr_clip (f); > #else > - x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > - x, y, width, height, False); > + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) > + XFillRectangle (FRAME_X_DISPLAY (f), > + FRAME_X_DRAWABLE (f), > + f->output_data.x->reverse_gc, > + x, y, width, height); > + else > + x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), > + x, y, width, height, False); > #endif > } > > @@ -4109,7 +4155,7 @@ x_scroll_run (struct window *w, struct run *run) > SET_FRAME_GARBAGED (f); > #else > XCopyArea (FRAME_X_DISPLAY (f), > - FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), > + FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), > f->output_data.x->normal_gc, > x, from_y, > width, height, > @@ -7769,6 +7815,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, > SET_FRAME_VISIBLE (f, 1); > SET_FRAME_ICONIFIED (f, false); > f->output_data.x->has_been_visible = true; > + font_flush_frame_caches (f); > SET_FRAME_GARBAGED (f); > } > else > @@ -8437,7 +8484,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, > { > if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height > || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width) > - SET_FRAME_GARBAGED (f); > + { > + font_flush_frame_caches (f); > + SET_FRAME_GARBAGED (f); > + } > FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height; > FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width; > } > @@ -8463,7 +8513,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, > || configureEvent.xconfigure.height != FRAME_PIXEL_HEIGHT (f)) > { > change_frame_size (f, width, height, false, true, false, true); > - x_clear_under_internal_border (f); > + x_clear_under_internal_border (f); > + font_flush_frame_caches (f); > SET_FRAME_GARBAGED (f); > cancel_mouse_face (f); > } > @@ -8880,7 +8931,7 @@ x_draw_hollow_cursor (struct window *w, struct glyph_row *row) > if (dpyinfo->scratch_cursor_gc) > XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv); > else > - dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_WINDOW (f), > + dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), > GCForeground, &xgcv); > gc = dpyinfo->scratch_cursor_gc; > > @@ -8937,7 +8988,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text > else > { > Display *dpy = FRAME_X_DISPLAY (f); > - Window window = FRAME_X_WINDOW (f); > + Drawable drawable = FRAME_X_DRAWABLE (f); > GC gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc; > unsigned long mask = GCForeground | GCBackground | GCGraphicsExposures; > struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id); > @@ -8958,7 +9009,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text > XChangeGC (dpy, gc, mask, &xgcv); > else > { > - gc = XCreateGC (dpy, window, mask, &xgcv); > + gc = XCreateGC (dpy, drawable, mask, &xgcv); > FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc; > } > > @@ -11271,7 +11322,14 @@ x_free_frame_resources (struct frame *f) > #endif /* USE_GTK */ > > if (FRAME_X_WINDOW (f)) > - XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); > + { > +#ifdef HAVE_XDBE > + if (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) > + XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), > + FRAME_X_DRAWABLE (f)); > +#endif > + XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); > + } > #endif /* !USE_X_TOOLKIT */ > > unload_color (f, FRAME_FOREGROUND_PIXEL (f)); > @@ -12111,7 +12169,17 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) > } > else > dpyinfo->cmap = XCreateColormap (dpyinfo->display, dpyinfo->root_window, > - dpyinfo->visual, AllocNone); > + dpyinfo->visual, AllocNone); > + > +#ifdef HAVE_XDBE > + dpyinfo->supports_xdbe = false; > + { > + int xdbe_major; > + int xdbe_minor; > + if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor)) > + dpyinfo->supports_xdbe = true; > + } > +#endif > > #ifdef HAVE_XFT > { > @@ -12590,6 +12658,7 @@ x_create_terminal (struct x_display_info *dpyinfo) > terminal->toggle_invisible_pointer_hook = XTtoggle_invisible_pointer; > terminal->update_begin_hook = x_update_begin; > terminal->update_end_hook = x_update_end; > + terminal->redisplay_end_hook = x_redisplay_end; > terminal->read_socket_hook = XTread_socket; > terminal->frame_up_to_date_hook = XTframe_up_to_date; > terminal->mouse_position_hook = XTmouse_position; > diff --git a/src/xterm.h b/src/xterm.h > index 675a484..cb1aa1d 100644 > --- a/src/xterm.h > +++ b/src/xterm.h > @@ -475,6 +475,10 @@ struct x_display_info > #ifdef USE_XCB > xcb_connection_t *xcb_connection; > #endif > + > +#ifdef HAVE_XDBE > + bool supports_xdbe; > +#endif > }; > > #ifdef HAVE_X_I18N > @@ -527,6 +531,17 @@ struct x_output > and the X window has not yet been created. */ > Window window_desc; > > +#ifdef HAVE_XDBE > + /* The drawable to which we're rendering. In the single-buffered > + base, the window itself. In the double-buffered case, the > + window's back buffer. */ > + Drawable draw_desc; > + > + /* Set to true when we need a buffer flip. We do a buffer flip only > + at the end of redisplay in order to minimize flicker. */ > + bool need_buffer_flip; > +#endif > + > /* The X window used for the bitmap icon; > or 0 if we don't have a bitmap icon. */ > Window icon_desc; > @@ -737,6 +752,18 @@ enum > /* Return the X window used for displaying data in frame F. */ > #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc) > > +/* Return the drawable used for rendering to frame F. */ > +#ifdef HAVE_XDBE > +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc) > +#else > +#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f))) > +#endif > + > +/* Return the need-buffer-flip flag for frame F. */ > +#ifdef HAVE_XDBE > +#define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip) > +#endif > + > /* Return the outermost X window associated with the frame F. */ > #ifdef USE_X_TOOLKIT > #define FRAME_OUTER_WINDOW(f) ((f)->output_data.x->widget ? \ > @@ -1140,6 +1167,8 @@ extern bool x_wm_supports (struct frame *, Atom); > extern void x_wait_for_event (struct frame *, int); > extern void x_clear_under_internal_border (struct frame *f); > > +extern void set_up_x_back_buffer (struct frame* f); > + > /* Defined in xselect.c. */ > > extern void x_handle_property_notify (const XPropertyEvent *); > > > [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione ` (2 preceding siblings ...) 2016-10-21 3:56 ` Clément Pit--Claudel @ 2016-10-21 8:49 ` Eli Zaretskii 2016-10-21 11:04 ` Daniel Colascione 3 siblings, 1 reply; 31+ messages in thread From: Eli Zaretskii @ 2016-10-21 8:49 UTC (permalink / raw) To: Daniel Colascione; +Cc: emacs-devel > From: Daniel Colascione <dancol@dancol.org> > Date: Thu, 20 Oct 2016 18:32:01 -0700 > > This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to > avoid showing the user incomplete drawing results. Without this patch, I > can make Emacs flicker like crazy by running isearch for a piece of text > unique in a buffer and holding down C-s. With this patch, Emacs does not > flicker no matter what I do to it. > > The patch also stops flickering that occurs when using the "solid > resizing" feature of some window managers --- i.e., when the WM redraws > windows as the user drags their edges, as opposed to displaying some > kind of bounding-box in lieu of the actual window contents. Thanks! > * We do a buffer flip at the end of redisplay instead of in > x_update_end() so the user never sees the completely-cleared state that > we enter immediately after clear_garbaged_frames(). x_update_end() does > do a buffer flip if it's called outside redisplay. I've added a new > terminal hook to support this hack. What happens in the case where redisplay_internal did its job, but update_frame was interrupted by incoming input? Won't that flip the buffers unnecessarily? I have some comments below. Apologies if some of them stem from my relative ignorance of the issues involved in this changeset. > commit 15fdd8f63533201f05627ede634a8f5ae4757d7e > Author: Daniel Colascione <dancol@dancol.org> > Date: Thu Oct 20 16:50:54 2016 -0700 > > Add double-buffered output support to Emacs Please add ChangeLog style commit log entries about each new and modified function/macro. Please also provide a NEWS entry about the new feature, in the "Installation Changes" section. > +AC_SUBST(XDBE_CFLAGS) > +AC_SUBST(XDBE_LIBS) Do we need XDBE_CFLAGS? They seem to be unused? Also, I think the fact that Xdbe extensions are used should be shown in the summary displayed by the configure script when it finishes, and XDBE should be added to the list in config_emacs_features, so that it appears in EMACS_CONFIG_FEATURES when appropriate. > diff --git a/src/dispnew.c b/src/dispnew.c > index 70d4de0..8f81cee 100644 > --- a/src/dispnew.c > +++ b/src/dispnew.c > @@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f) > { > /* Error if F has no glyphs. */ > eassert (f->glyphs_initialized_p); > + font_flush_frame_caches (f); > update_begin (f); > if (FRAME_MSDOS_P (f)) > FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f)); > diff --git a/src/font.c b/src/font.c > index f8e6794..033995e 100644 > --- a/src/font.c > +++ b/src/font.c > @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, > Lisp_Object arg, Lisp_Object result) > } > > void > +font_flush_frame_caches (struct frame *f) > +{ > + struct font_driver_list *list; > + > + for (list = f->font_driver_list; list; list = list->next) > + if (list->on && list->driver->flush_frame_caches) > + list->driver->flush_frame_caches (f); > +} Why do we need this flushing? Is it relevant to the patch, and if so why? I'm asking because we had some bad effects with some fonts due to flushing the frame's font caches, see bugs 24634 and 24565, for example, so my bother is that this will reintroduce those problems. Also, the call to font_flush_frame_caches is unconditional, although only one font back-end supports it. That seems to incur a gratuitous overhead of a function call for the other font back-ends. In any case, please add a commentary to each function you add with a summary of what it does. > @@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f) > by callers of this function. */ > gtk_widget_realize (wfixed); > FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed); > + set_up_x_back_buffer (f); There's some strange misalignment of indentation here (and in a few other places). > + FOR_EACH_FRAME (tail, frame) > + { > + struct frame *f = XFRAME (frame); > + if (FRAME_TERMINAL (f)->redisplay_end_hook) > + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); > + } This will call the hook for each frame, every redisplay cycle. By contrast, redisplay_internal many times updates only the selected window of the selected frame. Is calling the hook for all the frames really needed, or should we only call it for the selected frame in the latter case, or maybe just for frames that got updated? Also, should we distinguish between visible and iconified frames? > +#ifdef HAVE_XDBE > + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) > + { > + /* If allocating a back buffer fails, just use single-buffered > + rendering. */ > + x_sync (f); > + x_catch_errors (FRAME_X_DISPLAY (f)); > + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( > + FRAME_X_DISPLAY (f), > + FRAME_X_WINDOW (f), > + XdbeCopied); > + if (x_had_errors_p (FRAME_X_DISPLAY (f))) > + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); Shouldn't we turn off the supports_xdbe flag in the case of failure? > +#ifdef HAVE_XDBE > + dpyinfo->supports_xdbe = false; > + { > + int xdbe_major; > + int xdbe_minor; > + if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor)) > + dpyinfo->supports_xdbe = true; > + } > +#endif No need for braces here, since we now require a C99 compiler. > diff --git a/src/xterm.h b/src/xterm.h > index 675a484..cb1aa1d 100644 > --- a/src/xterm.h > +++ b/src/xterm.h > @@ -475,6 +475,10 @@ struct x_display_info > #ifdef USE_XCB > xcb_connection_t *xcb_connection; > #endif > + > +#ifdef HAVE_XDBE > + bool supports_xdbe; > +#endif > }; > > #ifdef HAVE_X_I18N > @@ -527,6 +531,17 @@ struct x_output > and the X window has not yet been created. */ > Window window_desc; > > +#ifdef HAVE_XDBE > + /* The drawable to which we're rendering. In the single-buffered > + base, the window itself. In the double-buffered case, the > + window's back buffer. */ > + Drawable draw_desc; > + > + /* Set to true when we need a buffer flip. We do a buffer flip only > + at the end of redisplay in order to minimize flicker. */ > + bool need_buffer_flip; > +#endif We had bad experience with conditionally compiled struct members. Is it possible to have these members always defined, and set them to some no-op values when XDBE is not supported? (In general, run-time tests should IMO always be preferred to compile-time tests, as the former make extensions and future development easier.) > +/* Return the drawable used for rendering to frame F. */ > +#ifdef HAVE_XDBE > +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc) > +#else > +#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f))) ^^^^^^^^^^^^^^^^^^^^^^^^ Why such a strange definition? Won't it cause compiler warnings in some cases, like when it's used as an lvalue? We use some quite paranoid warning switches on master. If there are problems to have a simple definition for this macro in the non-XDBE case, I'd prefer to have inline function(s) for the problematic cases (e.g., comparison), rather than a tricky macro. Thanks again for working on this. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 8:49 ` Eli Zaretskii @ 2016-10-21 11:04 ` Daniel Colascione 2016-10-21 17:43 ` Eli Zaretskii 0 siblings, 1 reply; 31+ messages in thread From: Daniel Colascione @ 2016-10-21 11:04 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel On 10/21/2016 01:49 AM, Eli Zaretskii wrote: >> From: Daniel Colascione <dancol@dancol.org> >> Date: Thu, 20 Oct 2016 18:32:01 -0700 >> >> This patch teaches Emacs how to use the X11 DOUBLE-BUFFER extension to >> avoid showing the user incomplete drawing results. Without this patch, I >> can make Emacs flicker like crazy by running isearch for a piece of text >> unique in a buffer and holding down C-s. With this patch, Emacs does not >> flicker no matter what I do to it. >> >> The patch also stops flickering that occurs when using the "solid >> resizing" feature of some window managers --- i.e., when the WM redraws >> windows as the user drags their edges, as opposed to displaying some >> kind of bounding-box in lieu of the actual window contents. > > Thanks! Thanks for taking a look. > >> * We do a buffer flip at the end of redisplay instead of in >> x_update_end() so the user never sees the completely-cleared state that >> we enter immediately after clear_garbaged_frames(). x_update_end() does >> do a buffer flip if it's called outside redisplay. I've added a new >> terminal hook to support this hack. > > What happens in the case where redisplay_internal did its job, but > update_frame was interrupted by incoming input? Won't that flip the > buffers unnecessarily? > > I have some comments below. Apologies if some of them stem from my > relative ignorance of the issues involved in this changeset. > >> commit 15fdd8f63533201f05627ede634a8f5ae4757d7e >> Author: Daniel Colascione <dancol@dancol.org> >> Date: Thu Oct 20 16:50:54 2016 -0700 >> >> Add double-buffered output support to Emacs > > Please add ChangeLog style commit log entries about each new and > modified function/macro. Oh, of course. I figured it was overkill for the first version. I'll add the usual ceremonial bits for the next version > > Please also provide a NEWS entry about the new feature, in the > "Installation Changes" section. > >> +AC_SUBST(XDBE_CFLAGS) >> +AC_SUBST(XDBE_LIBS) > > Do we need XDBE_CFLAGS? They seem to be unused? Symmetry with the other extension clauses? > Also, I think the fact that Xdbe extensions are used should be shown > in the summary displayed by the configure script when it finishes, and > XDBE should be added to the list in config_emacs_features, so that it > appears in EMACS_CONFIG_FEATURES when appropriate. I thought about that, but didn't want to overwhelm users with noise. I'll add some more user visibility here. The other question I had for myself is whether it makes sense to let users enable and disable this functionality at runtime: it's hard to think of a legitimate use case that doesn't involve a bug somewhere, and I'd rather fix the bugs. > >> diff --git a/src/dispnew.c b/src/dispnew.c >> index 70d4de0..8f81cee 100644 >> --- a/src/dispnew.c >> +++ b/src/dispnew.c >> @@ -2999,6 +2999,7 @@ redraw_frame (struct frame *f) >> { >> /* Error if F has no glyphs. */ >> eassert (f->glyphs_initialized_p); >> + font_flush_frame_caches (f); >> update_begin (f); >> if (FRAME_MSDOS_P (f)) >> FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f)); >> diff --git a/src/font.c b/src/font.c >> index f8e6794..033995e 100644 >> --- a/src/font.c >> +++ b/src/font.c >> @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, >> Lisp_Object arg, Lisp_Object result) >> } >> >> void >> +font_flush_frame_caches (struct frame *f) >> +{ >> + struct font_driver_list *list; >> + >> + for (list = f->font_driver_list; list; list = list->next) >> + if (list->on && list->driver->flush_frame_caches) >> + list->driver->flush_frame_caches (f); >> +} > > Why do we need this flushing? Is it relevant to the patch, and if so > why? > > I'm asking because we had some bad effects with some fonts due to > flushing the frame's font caches, see bugs 24634 and 24565, for > example, so my bother is that this will reintroduce those problems. Maybe I need to come up with a better name for what this hook does. It's specific to the XftDraw surface issue I mentioned in the preamble to the patch: without this code, rendering of text stops working (text rendering silently fails to happen) after a frame resize. We're not flushing the caches implicated in bigs 24634 and 24565. I'll add some comments explaining what's going on. I suspect it's a bad interaction between XRenderCreatePicture (which Xft uses internally) and the behind-the-scenes pixmap adjustment that DBE is doing that drives us to this hack. > Also, the call to font_flush_frame_caches is unconditional, although > only one font back-end supports it. That seems to incur a gratuitous > overhead of a function call for the other font back-ends. We're testing whether the hook function is non-NULL before calling into it, so only that one backend gets the call. In any case, the overhead is trivial --- it's one indirect function call compared to all of redisplay. (Every call into a shared library is the same indirect jump.) > In any case, please add a commentary to each function you add with a > summary of what it does. > >> @@ -1233,6 +1237,7 @@ xg_create_frame_widgets (struct frame *f) >> by callers of this function. */ >> gtk_widget_realize (wfixed); >> FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed); >> + set_up_x_back_buffer (f); > > There's some strange misalignment of indentation here (and in a few > other places). I blame my email client. >> + FOR_EACH_FRAME (tail, frame) >> + { >> + struct frame *f = XFRAME (frame); >> + if (FRAME_TERMINAL (f)->redisplay_end_hook) >> + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); >> + } > > This will call the hook for each frame, every redisplay cycle. By > contrast, redisplay_internal many times updates only the selected > window of the selected frame. Is calling the hook for all the frames > really needed, or should we only call it for the selected frame in the > latter case, or maybe just for frames that got updated? The hook only does something in the case where someone called update_end and we demurred on actually flipping the buffer because we knew we were in redisplay and would be getting redisplay_end_hook shortly. That is, if we update only one frame, we're only going to do one buffer flip. Or are you worried about the function call overhead? That, as I mentioned above, is trivial. > Also, should > we distinguish between visible and iconified frames? If we do, we should do it in the code that performs the updates, not the code (the snippet immediately above) that publishes the updates we've already done. > >> +#ifdef HAVE_XDBE >> + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) >> + { >> + /* If allocating a back buffer fails, just use single-buffered >> + rendering. */ >> + x_sync (f); >> + x_catch_errors (FRAME_X_DISPLAY (f)); >> + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( >> + FRAME_X_DISPLAY (f), >> + FRAME_X_WINDOW (f), >> + XdbeCopied); >> + if (x_had_errors_p (FRAME_X_DISPLAY (f))) >> + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); > > Shouldn't we turn off the supports_xdbe flag in the case of failure? supports_xdbe is whether XDBE is supported on a connection at all. What if XdbeAllocateBackBufferName fails transiently? >> +#ifdef HAVE_XDBE >> + dpyinfo->supports_xdbe = false; >> + { >> + int xdbe_major; >> + int xdbe_minor; >> + if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor)) >> + dpyinfo->supports_xdbe = true; >> + } >> +#endif > > No need for braces here, since we now require a C99 compiler. If we put xdbe_major and xdbe_minor at function level, the names leak into function scope. With braces, they exist only around the call to XdbeQueryExtension > >> diff --git a/src/xterm.h b/src/xterm.h >> index 675a484..cb1aa1d 100644 >> --- a/src/xterm.h >> +++ b/src/xterm.h >> @@ -475,6 +475,10 @@ struct x_display_info >> #ifdef USE_XCB >> xcb_connection_t *xcb_connection; >> #endif >> + >> +#ifdef HAVE_XDBE >> + bool supports_xdbe; >> +#endif >> }; >> >> #ifdef HAVE_X_I18N >> @@ -527,6 +531,17 @@ struct x_output >> and the X window has not yet been created. */ >> Window window_desc; >> >> +#ifdef HAVE_XDBE >> + /* The drawable to which we're rendering. In the single-buffered >> + base, the window itself. In the double-buffered case, the >> + window's back buffer. */ >> + Drawable draw_desc; >> + >> + /* Set to true when we need a buffer flip. We do a buffer flip only >> + at the end of redisplay in order to minimize flicker. */ >> + bool need_buffer_flip; >> +#endif > > We had bad experience with conditionally compiled struct members. Is > it possible to have these members always defined, and set them to some > no-op values when XDBE is not supported? (In general, run-time tests > should IMO always be preferred to compile-time tests, as the former > make extensions and future development easier.) I was just reluctant to further bloat the structure. Making these members exist unconditionally is fine. > >> +/* Return the drawable used for rendering to frame F. */ >> +#ifdef HAVE_XDBE >> +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc) >> +#else >> +#define FRAME_X_DRAWABLE(f) (0,(FRAME_X_WINDOW (f))) > ^^^^^^^^^^^^^^^^^^^^^^^^ > > Why such a strange definition? Won't it cause compiler warnings in > some cases, like when it's used as an lvalue? We use some quite > paranoid warning switches on master. The point is to make the build fail in the non-XDBE case if you try to use it as an lvalue. If draw_desc is going to actually exist in the structure in all cases, the strange definition is moot, since we'll always use the other one. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 11:04 ` Daniel Colascione @ 2016-10-21 17:43 ` Eli Zaretskii 2016-10-21 18:27 ` Daniel Colascione 0 siblings, 1 reply; 31+ messages in thread From: Eli Zaretskii @ 2016-10-21 17:43 UTC (permalink / raw) To: Daniel Colascione; +Cc: emacs-devel > Cc: emacs-devel@gnu.org > From: Daniel Colascione <dancol@dancol.org> > Date: Fri, 21 Oct 2016 04:04:21 -0700 > > > Also, the call to font_flush_frame_caches is unconditional, although > > only one font back-end supports it. That seems to incur a gratuitous > > overhead of a function call for the other font back-ends. > > We're testing whether the hook function is non-NULL before calling into > it, so only that one backend gets the call. In any case, the overhead is > trivial --- it's one indirect function call compared to all of > redisplay. (Every call into a shared library is the same indirect jump.) The code is more clear with the test before the call, otherwise the code reader needs to look in the hook to understand when it's a no-op. So I'd prefer to have the test outside of the call, or even outside of the loop. > >> + FOR_EACH_FRAME (tail, frame) > >> + { > >> + struct frame *f = XFRAME (frame); > >> + if (FRAME_TERMINAL (f)->redisplay_end_hook) > >> + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); > >> + } > > > > This will call the hook for each frame, every redisplay cycle. By > > contrast, redisplay_internal many times updates only the selected > > window of the selected frame. Is calling the hook for all the frames > > really needed, or should we only call it for the selected frame in the > > latter case, or maybe just for frames that got updated? > > The hook only does something in the case where someone called update_end > and we demurred on actually flipping the buffer because we knew we were > in redisplay and would be getting redisplay_end_hook shortly. That is, > if we update only one frame, we're only going to do one buffer flip. That might be so, but what you say above is in no way apparent from looking at the code. IME, it is very important to have the idea when something is being done and when not just by looking at the code in redisplay_internal; having to find that out by realizing that some flag is set in update_end and then tested in the hook makes the code more subtle and its maintenance harder. It's not like keeping this detail from redisplay_internal makes this detail local to some functions, or somesuch, so there's really no reason to conceal it, IMO. > Or are you worried about the function call overhead? That, as I > mentioned above, is trivial. No, I worry about maintainability of the display code and about lowering the risk of bugs introduced due to such subtleties. > > Also, should > > we distinguish between visible and iconified frames? > > If we do, we should do it in the code that performs the updates, not the > code (the snippet immediately above) that publishes the updates we've > already done. See above: I don't like such dependencies and find them in general an obstacle to understanding the overall logic of the display code. I don't mind adding a test in update_frame and friends, but that shouldn't prevent us from having a similar (or even identical) test here. > >> +#ifdef HAVE_XDBE > >> + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) > >> + { > >> + /* If allocating a back buffer fails, just use single-buffered > >> + rendering. */ > >> + x_sync (f); > >> + x_catch_errors (FRAME_X_DISPLAY (f)); > >> + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( > >> + FRAME_X_DISPLAY (f), > >> + FRAME_X_WINDOW (f), > >> + XdbeCopied); > >> + if (x_had_errors_p (FRAME_X_DISPLAY (f))) > >> + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); > > > > Shouldn't we turn off the supports_xdbe flag in the case of failure? > > supports_xdbe is whether XDBE is supported on a connection at all. What > if XdbeAllocateBackBufferName fails transiently? How can it fail transiently? And why turning off supports_xdbe in that case would mean trouble? > >> +#ifdef HAVE_XDBE > >> + dpyinfo->supports_xdbe = false; > >> + { > >> + int xdbe_major; > >> + int xdbe_minor; > >> + if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, > &xdbe_minor)) > >> + dpyinfo->supports_xdbe = true; > >> + } > >> +#endif > > > > No need for braces here, since we now require a C99 compiler. > > If we put xdbe_major and xdbe_minor at function level, the names leak > into function scope. With braces, they exist only around the call to > XdbeQueryExtension We use that in many other places, so I think these precautions are misguided and generally make our coding style less apparent. Thanks. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 17:43 ` Eli Zaretskii @ 2016-10-21 18:27 ` Daniel Colascione 2016-10-21 19:27 ` Eli Zaretskii 0 siblings, 1 reply; 31+ messages in thread From: Daniel Colascione @ 2016-10-21 18:27 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> Cc: emacs-devel@gnu.org >> From: Daniel Colascione <dancol@dancol.org> >> Date: Fri, 21 Oct 2016 04:04:21 -0700 >> >> > Also, the call to font_flush_frame_caches is unconditional, although >> > only one font back-end supports it. That seems to incur a gratuitous >> > overhead of a function call for the other font back-ends. >> >> We're testing whether the hook function is non-NULL before calling into >> it, so only that one backend gets the call. In any case, the overhead is >> trivial --- it's one indirect function call compared to all of >> redisplay. (Every call into a shared library is the same indirect jump.) > > The code is more clear with the test before the call, otherwise the > code reader needs to look in the hook to understand when it's a no-op. But the test (of whether the hook exists) _is_ before the call. What are you proposing? some_backend_needs_flush = False for backend in font_back_ends: if has_flush_hook(backend): some_back_end_needs_flush = True if some_back_end_needs_flush: for backend in font_back_ends: if has_flush_hook(backend): backend.flush_hook() That's silly. Can you elaborate on what you consider a cleaner approach? > So I'd prefer to have the test outside of the call, or even outside of > the loop. See below. I don't think that giving font back-ends an opportunity to do _something_ in response to a frame being garbaged in certain ways is a bad thing. >> >> + FOR_EACH_FRAME (tail, frame) >> >> + { >> >> + struct frame *f = XFRAME (frame); >> >> + if (FRAME_TERMINAL (f)->redisplay_end_hook) >> >> + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); >> >> + } >> > >> > This will call the hook for each frame, every redisplay cycle. By >> > contrast, redisplay_internal many times updates only the selected >> > window of the selected frame. Is calling the hook for all the frames >> > really needed, or should we only call it for the selected frame in the >> > latter case, or maybe just for frames that got updated? >> >> The hook only does something in the case where someone called update_end >> and we demurred on actually flipping the buffer because we knew we were >> in redisplay and would be getting redisplay_end_hook shortly. That is, >> if we update only one frame, we're only going to do one buffer flip. > > That might be so, but what you say above is in no way apparent from > looking at the code. IME, it is very important to have the idea when > something is being done and when not just by looking at the code in > redisplay_internal; having to find that out by realizing that some > flag is set in update_end and then tested in the hook makes the code > more subtle and its maintenance harder. It's not like keeping this > detail from redisplay_internal makes this detail local to some > functions, or somesuch, so there's really no reason to conceal it, > IMO. This sentiment strikes me as being analogous to the old "no use of hooks in Emacs internals" line --- yet we have facilities like syntax-ppss that rely on hooks in core, and it's worked out fine. We need to do a buffer flip at the end of redisplay for each frame on which update_end was called during redisplay. If someone calls update_end _outside_ redisplay, we should do a buffer flip immediately. The code I've sent is the cleanest way of implementing this model short of changing how update_begin and update_end work. I think thhat what you're proposing is a layering violation. It will make maintenance harder. The only facility that cares about the has-a-frame-been-updated state is the X11 double-buffered back-end, so making xdisp track this state makes everything more complicated, especially because xdisp already has a "needs redisplay" flag and shouldn't need to keep track of a separate "needs buffer flip" flag. It shouldn't have to care. That's not its job. >> Or are you worried about the function call overhead? That, as I >> mentioned above, is trivial. > > No, I worry about maintainability of the display code and about > lowering the risk of bugs introduced due to such subtleties. > I'm also worried about maintainability: that's why I don't want to make redisplay_internal any more of a big ball of mud than it already is. I think it's cleaner to have xterm keep track of state only xterm needs. >> > Also, should >> > we distinguish between visible and iconified frames? >> >> If we do, we should do it in the code that performs the updates, not the >> code (the snippet immediately above) that publishes the updates we've >> already done. > > See above: I don't like such dependencies and find them in general an > obstacle to understanding the overall logic of the display code. I > don't mind adding a test in update_frame and friends, but that > shouldn't prevent us from having a similar (or even identical) test > here. What dependency? You're proposing adding a lot of complexity to the loop that calls redisplay_end_hook, and I still have no idea what this complexity is supposed to accomplish. > >> >> +#ifdef HAVE_XDBE >> >> + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) >> >> + { >> >> + /* If allocating a back buffer fails, just use single-buffered >> >> + rendering. */ >> >> + x_sync (f); >> >> + x_catch_errors (FRAME_X_DISPLAY (f)); >> >> + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( >> >> + FRAME_X_DISPLAY (f), >> >> + FRAME_X_WINDOW (f), >> >> + XdbeCopied); >> >> + if (x_had_errors_p (FRAME_X_DISPLAY (f))) >> >> + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); >> > >> > Shouldn't we turn off the supports_xdbe flag in the case of failure? >> >> supports_xdbe is whether XDBE is supported on a connection at all. What >> if XdbeAllocateBackBufferName fails transiently? > > How can it fail transiently? And why turning off supports_xdbe in > that case would mean trouble? It's an allocation. Allocations can fail. And XDBE isn't guaranteed to work for all visuals. > >> >> +#ifdef HAVE_XDBE >> >> + dpyinfo->supports_xdbe = false; >> >> + { >> >> + int xdbe_major; >> >> + int xdbe_minor; >> >> + if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, >> &xdbe_minor)) >> >> + dpyinfo->supports_xdbe = true; >> >> + } >> >> +#endif >> > >> > No need for braces here, since we now require a C99 compiler. >> >> If we put xdbe_major and xdbe_minor at function level, the names leak >> into function scope. With braces, they exist only around the call to >> XdbeQueryExtension > > We use that in many other places, so I think these precautions are > misguided and generally make our coding style less apparent. Fine. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 18:27 ` Daniel Colascione @ 2016-10-21 19:27 ` Eli Zaretskii 2016-10-23 20:51 ` Daniel Colascione 0 siblings, 1 reply; 31+ messages in thread From: Eli Zaretskii @ 2016-10-21 19:27 UTC (permalink / raw) To: Daniel Colascione; +Cc: emacs-devel > From: Daniel Colascione <dancol@dancol.org> > Cc: emacs-devel@gnu.org > Date: Fri, 21 Oct 2016 11:27:29 -0700 > > Eli Zaretskii <eliz@gnu.org> writes: > > >> Cc: emacs-devel@gnu.org > >> From: Daniel Colascione <dancol@dancol.org> > >> Date: Fri, 21 Oct 2016 04:04:21 -0700 > >> > >> > Also, the call to font_flush_frame_caches is unconditional, although > >> > only one font back-end supports it. That seems to incur a gratuitous > >> > overhead of a function call for the other font back-ends. > >> > >> We're testing whether the hook function is non-NULL before calling into > >> it, so only that one backend gets the call. In any case, the overhead is > >> trivial --- it's one indirect function call compared to all of > >> redisplay. (Every call into a shared library is the same indirect jump.) > > > > The code is more clear with the test before the call, otherwise the > > code reader needs to look in the hook to understand when it's a no-op. > > But the test (of whether the hook exists) _is_ before the call. What are > you proposing? > > some_backend_needs_flush = False > for backend in font_back_ends: > if has_flush_hook(backend): > some_back_end_needs_flush = True > if some_back_end_needs_flush: > for backend in font_back_ends: > if has_flush_hook(backend): > backend.flush_hook() > > That's silly. Let's assume that neither of us makes silly proposals, okay? > Can you elaborate on what you consider a cleaner approach? I meant the test for the lone font backend that needs this kludge, not for the existence of the method. And also a comment that tells it's a kludge whose need we understand only empirically. I'd even prefer a direct call to the function when the font backend is the one which needs it, rather than having a method created for the benefit of a single backend, whose necessity we don't really understand. When I read the code which tests whether a backend has a method, I need to look up the methods of the backend(s) I'm interested in to know whether they have such a method. When a method is defined for a single backend, most of those searches will produce a trivial result, i.e. a waste of my time. I prefer that we make this evident at the place of the call instead. > >> >> + FOR_EACH_FRAME (tail, frame) > >> >> + { > >> >> + struct frame *f = XFRAME (frame); > >> >> + if (FRAME_TERMINAL (f)->redisplay_end_hook) > >> >> + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); > >> >> + } > >> > > >> > This will call the hook for each frame, every redisplay cycle. By > >> > contrast, redisplay_internal many times updates only the selected > >> > window of the selected frame. Is calling the hook for all the frames > >> > really needed, or should we only call it for the selected frame in the > >> > latter case, or maybe just for frames that got updated? > >> > >> The hook only does something in the case where someone called update_end > >> and we demurred on actually flipping the buffer because we knew we were > >> in redisplay and would be getting redisplay_end_hook shortly. That is, > >> if we update only one frame, we're only going to do one buffer flip. > > > > That might be so, but what you say above is in no way apparent from > > looking at the code. IME, it is very important to have the idea when > > something is being done and when not just by looking at the code in > > redisplay_internal; having to find that out by realizing that some > > flag is set in update_end and then tested in the hook makes the code > > more subtle and its maintenance harder. It's not like keeping this > > detail from redisplay_internal makes this detail local to some > > functions, or somesuch, so there's really no reason to conceal it, > > IMO. > > This sentiment strikes me as being analogous to the old "no use of hooks > in Emacs internals" line --- yet we have facilities like syntax-ppss > that rely on hooks in core, and it's worked out fine. I'm not saying the code won't work. I'm saying it could make its purpose more evident, which is better for long-term maintainability. > We need to do a buffer flip at the end of redisplay for each frame on > which update_end was called during redisplay. Even if update_end was called for a frame whose update was "paused", i.e. whose redisplay was interrupted by incoming input? > If someone calls update_end _outside_ redisplay, we should do a > buffer flip immediately. The code I've sent is the cleanest way of > implementing this model short of changing how update_begin and > update_end work. I'm asking whether flipping the buffer for a frame that wasn't updated or was updated partially can do some harm, like produce incorrect display. > I think thhat what you're proposing is a layering violation. It will > make maintenance harder. The only facility that cares about the > has-a-frame-been-updated state is the X11 double-buffered back-end, My problems start with the name of the hook, which doesn't hint at all that only double-buffered X11 back-end cares about that. If the hook was called something like double_buffer_flip_hook, or was compiled only if HAVE_XDBE is defined, this issue would go away. > making xdisp track this state makes everything more complicated, > especially because xdisp already has a "needs redisplay" flag and > shouldn't need to keep track of a separate "needs buffer flip" flag. > It shouldn't have to care. That's not its job. I don't know why you decided that it isn't xdisp.c's job. It already has a lot of flags that other parts set to get xdisp.c do or not do certain parts of its job. Why not let it test a flag here as well? Such a test will tell the reader that the call to the hook is only done when a flag (with hopefully some descriptive name) is set, so the reader can understand what the hook is doing and under what circumstances without actually looking at the hook, which involves first finding out which frame types support it, and what exactly does it do. IOW, if the code speaks for itself, it makes maintenance of this hyper-convoluted piece of Emacs easier, less time consuming, and less error prone. > >> Or are you worried about the function call overhead? That, as I > >> mentioned above, is trivial. > > > > No, I worry about maintainability of the display code and about > > lowering the risk of bugs introduced due to such subtleties. > > > > I'm also worried about maintainability: that's why I don't want to make > redisplay_internal any more of a big ball of mud than it already is. It's too late for that. It's already an extremely complex and hard to understand piece of code. Hiding more information from its logic makes the code harder to maintain, not easier. We are not talking about highly-modular package, where the logic is kept local to each module, and the interfaces between them are kept to the absolute minimum. xdisp.c _knows_ a lot about how xterm.c and xfns.c work. You cannot disengage them without a complete rewrite. So the principles you are trying to apply, while a good idea in general, in this particular case make code harder to understand and develop. Of course, it's not a catastrophe, so if you are going to go to the barricades over this, I won't fight you. I just hope you take my gray hair from many readings of redisplay_internal as some evidence that I know what I'm talking about. The number of times I needed to go deep into the called functions just to realize that the code does something completely different from what it looked on the redisplay_internal level is beyond imagination. > >> >> +#ifdef HAVE_XDBE > >> >> + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) > >> >> + { > >> >> + /* If allocating a back buffer fails, just use single-buffered > >> >> + rendering. */ > >> >> + x_sync (f); > >> >> + x_catch_errors (FRAME_X_DISPLAY (f)); > >> >> + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( > >> >> + FRAME_X_DISPLAY (f), > >> >> + FRAME_X_WINDOW (f), > >> >> + XdbeCopied); > >> >> + if (x_had_errors_p (FRAME_X_DISPLAY (f))) > >> >> + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); > >> > > >> > Shouldn't we turn off the supports_xdbe flag in the case of failure? > >> > >> supports_xdbe is whether XDBE is supported on a connection at all. What > >> if XdbeAllocateBackBufferName fails transiently? > > > > How can it fail transiently? And why turning off supports_xdbe in > > that case would mean trouble? > > It's an allocation. Allocations can fail. And XDBE isn't guaranteed to > work for all visuals. How much memory is being allocated, and correspondingly what is the probability this allocation could fail on a healthy system? And what about my second question? The downside of your proposed code is that we will try to turn on XDBE each time hence, and each attempt means a potentially costly series of calls to catch X errors, and the XdbeAllocateBackBufferName itself. I'm asking whether it wouldn't be better to simply give up on XDBE in such a case for that frame. It could be much cheaper, no? Just a thought. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-21 19:27 ` Eli Zaretskii @ 2016-10-23 20:51 ` Daniel Colascione 2016-10-24 8:05 ` Eli Zaretskii 0 siblings, 1 reply; 31+ messages in thread From: Daniel Colascione @ 2016-10-23 20:51 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Daniel Colascione <dancol@dancol.org> >> Cc: emacs-devel@gnu.org >> Date: Fri, 21 Oct 2016 11:27:29 -0700 >> >> Eli Zaretskii <eliz@gnu.org> writes: >> >> >> Cc: emacs-devel@gnu.org >> >> From: Daniel Colascione <dancol@dancol.org> >> >> Date: Fri, 21 Oct 2016 04:04:21 -0700 >> >> >> >> > Also, the call to font_flush_frame_caches is unconditional, although >> >> > only one font back-end supports it. That seems to incur a gratuitous >> >> > overhead of a function call for the other font back-ends. >> >> >> >> We're testing whether the hook function is non-NULL before calling into >> >> it, so only that one backend gets the call. In any case, the overhead is >> >> trivial --- it's one indirect function call compared to all of >> >> redisplay. (Every call into a shared library is the same indirect jump.) >> > >> > The code is more clear with the test before the call, otherwise the >> > code reader needs to look in the hook to understand when it's a no-op. >> >> But the test (of whether the hook exists) _is_ before the call. What are >> you proposing? >> >> some_backend_needs_flush = False >> for backend in font_back_ends: >> if has_flush_hook(backend): >> some_back_end_needs_flush = True >> if some_back_end_needs_flush: >> for backend in font_back_ends: >> if has_flush_hook(backend): >> backend.flush_hook() >> >> That's silly. > > Let's assume that neither of us makes silly proposals, okay? Sorry --- I meant the comment in a more syllogistic way: 1. I understand the proposal to be silly 2. We don't make silly proposals Therefore, my understanding is flawed >> Can you elaborate on what you consider a cleaner approach? > > I meant the test for the lone font backend that needs this kludge, not > for the existence of the method. And also a comment that tells it's a > kludge whose need we understand only empirically. I'd even prefer a > direct call to the function when the font backend is the one which > needs it, rather than having a method created for the benefit of a > single backend, whose necessity we don't really understand. So we want to write? for font_backend in frame.font_backends: if instanceof(font_backend, XftBackend): xft_refresh_hack() That still strikes me as less clean, especially considering we'll have to provide the xft_refresh_hack to general-purpose code. > When I read the code which tests whether a backend has a method, I > need to look up the methods of the backend(s) I'm interested in to > know whether they have such a method. When a method is defined for a > single backend, most of those searches will produce a trivial result, > i.e. a waste of my time. I prefer that we make this evident at the > place of the call instead. > >> >> >> + FOR_EACH_FRAME (tail, frame) >> >> >> + { >> >> >> + struct frame *f = XFRAME (frame); >> >> >> + if (FRAME_TERMINAL (f)->redisplay_end_hook) >> >> >> + (*FRAME_TERMINAL (f)->redisplay_end_hook) (f); >> >> >> + } >> >> > >> >> > This will call the hook for each frame, every redisplay cycle. By >> >> > contrast, redisplay_internal many times updates only the selected >> >> > window of the selected frame. Is calling the hook for all the frames >> >> > really needed, or should we only call it for the selected frame in the >> >> > latter case, or maybe just for frames that got updated? >> >> >> >> The hook only does something in the case where someone called update_end >> >> and we demurred on actually flipping the buffer because we knew we were >> >> in redisplay and would be getting redisplay_end_hook shortly. That is, >> >> if we update only one frame, we're only going to do one buffer flip. >> > >> > That might be so, but what you say above is in no way apparent from >> > looking at the code. IME, it is very important to have the idea when >> > something is being done and when not just by looking at the code in >> > redisplay_internal; having to find that out by realizing that some >> > flag is set in update_end and then tested in the hook makes the code >> > more subtle and its maintenance harder. It's not like keeping this >> > detail from redisplay_internal makes this detail local to some >> > functions, or somesuch, so there's really no reason to conceal it, >> > IMO. >> >> This sentiment strikes me as being analogous to the old "no use of hooks >> in Emacs internals" line --- yet we have facilities like syntax-ppss >> that rely on hooks in core, and it's worked out fine. > > I'm not saying the code won't work. I'm saying it could make its > purpose more evident, which is better for long-term maintainability. Longer-term, I think we need to move toward greater, not lesser, modularity. With some explanatory comments, Besides, there's no reason other window systems can't use this hook. Looking briefly at ns_update_end, for example, I see that we call flushWindow; we shouldn't have to do that until redisplay_end_hook. >> We need to do a buffer flip at the end of redisplay for each frame on >> which update_end was called during redisplay. > > Even if update_end was called for a frame whose update was "paused", > i.e. whose redisplay was interrupted by incoming input? Right now, we do a buffer flip unconditionally except in the case that we `goto end_of_redisplay`. I think that's a bug actually --- but I have to understand more of what this logic is actually doing. Why _do_ we have a path that short-circuits the rest of the redisplay code? What would happen if we just removed it? It appears to be some kind of optimization, and I'm not sure it's actually necessary (especially since, according to the comment, we disable it anyway in the case of a blinking cursor). I suppose you could make an argument for not doing a buffer flip if redisplay is interrupted by input, but I'm honestly not sure what might go wrong there, and my preference would be to act as much like the single-buffered case as possible and default, if we're unsure, to showing the results of our painting efforts to the user. >> If someone calls update_end _outside_ redisplay, we should do a >> buffer flip immediately. The code I've sent is the cleanest way of >> implementing this model short of changing how update_begin and >> update_end work. > > I'm asking whether flipping the buffer for a frame that wasn't updated > or was updated partially can do some harm, like produce incorrect > display. Probably, but I don't think it can be worse than what we currently have. I'm trying to be conservative and hold as few updates in the back buffer as possible. The only reason I added the end_of_redisplay stuff is that the resulting flicker reduction in the window-resize case is so dramatic that I think it's worth the extra complexity. >> I think thhat what you're proposing is a layering violation. It will >> make maintenance harder. The only facility that cares about the >> has-a-frame-been-updated state is the X11 double-buffered back-end, > > My problems start with the name of the hook, which doesn't hint at all > that only double-buffered X11 back-end cares about that. If the hook > was called something like double_buffer_flip_hook, or was compiled > only if HAVE_XDBE is defined, this issue would go away. So let's call it double_buffer_flip_hook. >> making xdisp track this state makes everything more complicated, >> especially because xdisp already has a "needs redisplay" flag and >> shouldn't need to keep track of a separate "needs buffer flip" flag. >> It shouldn't have to care. That's not its job. > > I don't know why you decided that it isn't xdisp.c's job. It already > has a lot of flags that other parts set to get xdisp.c do or not do > certain parts of its job. Why not let it test a flag here as well? > Such a test will tell the reader that the call to the hook is only > done when a flag (with hopefully some descriptive name) is set, so the > reader can understand what the hook is doing and under what > circumstances without actually looking at the hook, which involves > first finding out which frame types support it, and what exactly does > it do. > > IOW, if the code speaks for itself, it makes maintenance of this > hyper-convoluted piece of Emacs easier, less time consuming, and less > error prone. This code is an adventure. >> >> Or are you worried about the function call overhead? That, as I >> >> mentioned above, is trivial. >> > >> > No, I worry about maintainability of the display code and about >> > lowering the risk of bugs introduced due to such subtleties. >> > >> >> I'm also worried about maintainability: that's why I don't want to make >> redisplay_internal any more of a big ball of mud than it already is. > > It's too late for that. It's already an extremely complex and hard to > understand piece of code. Hiding more information from its logic > makes the code harder to maintain, not easier. We are not talking > about highly-modular package, where the logic is kept local to each > module, and the interfaces between them are kept to the absolute > minimum. xdisp.c _knows_ a lot about how xterm.c and xfns.c work. > You cannot disengage them without a complete rewrite. Well, it also works with w32term and nsterm, so the coupling can't be complete. :-) > So the principles you are trying to apply, while a good idea in > general, in this particular case make code harder to understand and > develop. > > Of course, it's not a catastrophe, so if you are going to go to the > barricades over this, I won't fight you. I just hope you take my gray > hair from many readings of redisplay_internal as some evidence that I > know what I'm talking about. The number of times I needed to go deep > into the called functions just to realize that the code does something > completely different from what it looked on the redisplay_internal > level is beyond imagination. I'm beginning to see your point. I just don't agree that the problem is intractable. We're going to need to clean this stuff up, especially if (in the distant future) we want to support multiple window systems in the same Emacs build. > >> >> >> +#ifdef HAVE_XDBE >> >> >> + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) >> >> >> + { >> >> >> + /* If allocating a back buffer fails, just use single-buffered >> >> >> + rendering. */ >> >> >> + x_sync (f); >> >> >> + x_catch_errors (FRAME_X_DISPLAY (f)); >> >> >> + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( >> >> >> + FRAME_X_DISPLAY (f), >> >> >> + FRAME_X_WINDOW (f), >> >> >> + XdbeCopied); >> >> >> + if (x_had_errors_p (FRAME_X_DISPLAY (f))) >> >> >> + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); >> >> > >> >> > Shouldn't we turn off the supports_xdbe flag in the case of failure? >> >> >> >> supports_xdbe is whether XDBE is supported on a connection at all. What >> >> if XdbeAllocateBackBufferName fails transiently? >> > >> > How can it fail transiently? And why turning off supports_xdbe in >> > that case would mean trouble? >> >> It's an allocation. Allocations can fail. And XDBE isn't guaranteed to >> work for all visuals. > > How much memory is being allocated, and correspondingly what is the > probability this allocation could fail on a healthy system? I don't know --- I've never seen it fail. I'm just applying general memory-allocation heuristics here. DBE can also fail if the visual we're using for a window > > And what about my second question? > > The downside of your proposed code is that we will try to turn on XDBE > each time hence, and each attempt means a potentially costly series of > calls to catch X errors, and the XdbeAllocateBackBufferName itself. > I'm asking whether it wouldn't be better to simply give up on XDBE in > such a case for that frame. It could be much cheaper, no? Just a > thought. It's only on frame creation though, and frame creation is both rare and already expensive. By the way: aren't most calls to x_catch_errors already buggy? AFAICT, they don't generally call x_sync first. The purpose of this mechanism is to run a section of code in such a way that Emacs doesn't die if something goes wrong in Xlib, but since Xlib error reporting is asynchronous anyway, use of x_catch_errors without a preceding x_sync (as I have in my code) can silently swallow X errors that we _do_ want to kill Emacs. Anyway, the double-buffer code has a few kinks I need to work out. I'll send another version of the patch after I figure out what's going on. 1. [must fix] during frame creation, on one of my computers but not the other, I momentarily see an all-black frame with a small white square, and only a few hundred ms later does the normal frame content get draws 2. [must fix] on the same system, and not the other, after resuming the system from sleep, Emacs frames momentarily display all white before the usual frame contents get filled in 3. [should fix] on the same system, and not the other, resizing the a frame interactively still produces some flicking, particulary in the modeline, but much less than without the patch entirely. This flickering appears to have something to do with XRender, since if I force everything to use X11 core rendering instead, I don't see any flickering at all. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-23 20:51 ` Daniel Colascione @ 2016-10-24 8:05 ` Eli Zaretskii 2016-10-24 18:43 ` Ken Raeburn 2016-10-27 19:06 ` dancol 0 siblings, 2 replies; 31+ messages in thread From: Eli Zaretskii @ 2016-10-24 8:05 UTC (permalink / raw) To: Daniel Colascione, Ken Raeburn; +Cc: emacs-devel > From: Daniel Colascione <dancol@dancol.org> > Cc: emacs-devel@gnu.org > Date: Sun, 23 Oct 2016 13:51:01 -0700 > > > I meant the test for the lone font backend that needs this kludge, not > > for the existence of the method. And also a comment that tells it's a > > kludge whose need we understand only empirically. I'd even prefer a > > direct call to the function when the font backend is the one which > > needs it, rather than having a method created for the benefit of a > > single backend, whose necessity we don't really understand. > > So we want to write? > > for font_backend in frame.font_backends: > if instanceof(font_backend, XftBackend): > xft_refresh_hack() > > That still strikes me as less clean, especially considering we'll have > to provide the xft_refresh_hack to general-purpose code. At the very least let's have a comment there saying that this is a kludge that's actually done only for that font driver. (Comments tend to fall out of sync with the code, so making code speak for itself is better, but a comment is infinitely better than nothing at all.) > > I'm not saying the code won't work. I'm saying it could make its > > purpose more evident, which is better for long-term maintainability. > > Longer-term, I think we need to move toward greater, not > lesser, modularity. I agree, but IME such a move cannot be piecemeal in this case, it must be done in one go, or in a small number of large steps. IOW, it's a significant job on its own. > >> We need to do a buffer flip at the end of redisplay for each frame on > >> which update_end was called during redisplay. > > > > Even if update_end was called for a frame whose update was "paused", > > i.e. whose redisplay was interrupted by incoming input? > > Right now, we do a buffer flip unconditionally except in the case that > we `goto end_of_redisplay`. I think that's a bug actually --- but I have > to understand more of what this logic is actually doing. > > Why _do_ we have a path that short-circuits the rest of the redisplay > code? What would happen if we just removed it? It appears to be some > kind of optimization, and I'm not sure it's actually necessary > (especially since, according to the comment, we disable it anyway in the > case of a blinking cursor). It's an optimization for the case that nothing needs to be redisplayed. But I wasn't asking about that, I was asking about this part: pending |= update_frame (f, false, false); If update_frame returns non-zero, we don't mark all the windows as having accurate display, which will cause redisplay to be re-entered again on the first opportunity, and it will then try to redisplay all of them again. I was asking whether flipping in this case is TRT. Without double buffer, what happens in this case is that we could momentarily flash a partially redrawn window. > I suppose you could make an argument for not doing a buffer flip if > redisplay is interrupted by input, but I'm honestly not sure what might > go wrong there, and my preference would be to act as much like the > single-buffered case as possible and default, if we're unsure, to > showing the results of our painting efforts to the user. OK, we can always wait for bug reports, I guess, and take it from there. > > My problems start with the name of the hook, which doesn't hint at all > > that only double-buffered X11 back-end cares about that. If the hook > > was called something like double_buffer_flip_hook, or was compiled > > only if HAVE_XDBE is defined, this issue would go away. > > So let's call it double_buffer_flip_hook. Great, let's. > > IOW, if the code speaks for itself, it makes maintenance of this > > hyper-convoluted piece of Emacs easier, less time consuming, and less > > error prone. > > This code is an adventure. It is. > >> I'm also worried about maintainability: that's why I don't want to make > >> redisplay_internal any more of a big ball of mud than it already is. > > > > It's too late for that. It's already an extremely complex and hard to > > understand piece of code. Hiding more information from its logic > > makes the code harder to maintain, not easier. We are not talking > > about highly-modular package, where the logic is kept local to each > > module, and the interfaces between them are kept to the absolute > > minimum. xdisp.c _knows_ a lot about how xterm.c and xfns.c work. > > You cannot disengage them without a complete rewrite. > > Well, it also works with w32term and nsterm, so the coupling can't be > complete. :-) That's why w32term and nsterm are largely modeled on xterm (and the same with xfns), right to the point of literally repeating many code fragments. > By the way: aren't most calls to x_catch_errors already buggy? AFAICT, > they don't generally call x_sync first. The purpose of this mechanism > is to run a section of code in such a way that Emacs doesn't die if > something goes wrong in Xlib, but since Xlib error reporting is > asynchronous anyway, use of x_catch_errors without a preceding x_sync > (as I have in my code) can silently swallow X errors that we _do_ want > to kill Emacs. That's for some X expert to answer, I really don't know enough about this stuff. Ken, can you comment on this? > 1. [must fix] during frame creation, on one of my computers but not > the other, I momentarily see an all-black frame with a small white > square, and only a few hundred ms later does the normal frame content > get draws Possibly related to the fact that when Emacs starts, it first defines a small 10x10 frame, before resizing it to the actual size. Look for "10" in make_frame. > 2. [must fix] on the same system, and not the other, after resuming > the system from sleep, Emacs frames momentarily display all white > before the usual frame contents get filled in Could be Emacs waits for an expose event before redrawing the frame, and meanwhile you show an empty buffer? > 3. [should fix] on the same system, and not the other, resizing the a > frame interactively still produces some flicking, particulary in the > modeline, but much less than without the patch entirely. > This flickering appears to have something to do with XRender, since if > I force everything to use X11 core rendering instead, I don't see any > flickering at all. If the flickering is on the modeline, a breakpoint in redisplay_mode_lines should tell you why it happens. Thanks. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-24 8:05 ` Eli Zaretskii @ 2016-10-24 18:43 ` Ken Raeburn 2016-10-27 19:06 ` dancol 1 sibling, 0 replies; 31+ messages in thread From: Ken Raeburn @ 2016-10-24 18:43 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Daniel Colascione, Emacs-Devel devel > >> By the way: aren't most calls to x_catch_errors already buggy? AFAICT, >> they don't generally call x_sync first. The purpose of this mechanism >> is to run a section of code in such a way that Emacs doesn't die if >> something goes wrong in Xlib, but since Xlib error reporting is >> asynchronous anyway, use of x_catch_errors without a preceding x_sync >> (as I have in my code) can silently swallow X errors that we _do_ want >> to kill Emacs. > > That's for some X expert to answer, I really don't know enough about > this stuff. Ken, can you comment on this? x_catch_errors itself starts with an XSync call to ensure errors on previous requests are processed with the default handler. Then x_uncatch_errors does another at the end of the region to make sure any errors generated by requests within the catching region are processed. There’s also x_uncatch_errors_after_check, which I added specifically for cases where x_uncatch_errors was being called immediately after x_check_errors, which also does an XSync, so we could drop one round-trip (which was a performance issue for me in certain cases). ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-24 8:05 ` Eli Zaretskii 2016-10-24 18:43 ` Ken Raeburn @ 2016-10-27 19:06 ` dancol 2016-10-27 19:36 ` Eli Zaretskii 2016-10-27 22:18 ` Paul Eggert 1 sibling, 2 replies; 31+ messages in thread From: dancol @ 2016-10-27 19:06 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Daniel Colascione, Ken Raeburn, emacs-devel Below is a new much-improved version of the patch. It should address the legibility concerns while probably adding more. It addresses all the bugs I've been able to find and on my machine, produces buttery smooth editing and resizing. The patch has become a bit broader now: the biggest change is making GTK toolkit scrollbars into independent X windows, as was the original intention of the code, and getting rid of a lot of logic that tried to order Emacs and GTK paints in such a way that we wouldn't clobber what GTK drew. Now that GTK draws its scrollbars in its own X windows, we can update the main X window without having to queue or wait for GTK to do its drawing. Specifically, we don't have to worry about buffer flips clearing scrollbars off the screen or trying to get GTK to render scrollbars onto our back buffer. This independent-X-window scheme was always the intention, AFAICT, because we put GTK scrollbars in event boxes --- but it'd bitrotted over the years thanks to GDK optimizing GdkWindow to not create an X window unless you specifically ask for one, which we do now. >> From: Daniel Colascione <dancol@dancol.org> >> Cc: emacs-devel@gnu.org >> Date: Sun, 23 Oct 2016 13:51:01 -0700 >> >> > I meant the test for the lone font backend that needs this kludge, not >> > for the existence of the method. And also a comment that tells it's a >> > kludge whose need we understand only empirically. I'd even prefer a >> > direct call to the function when the font backend is the one which >> > needs it, rather than having a method created for the benefit of a >> > single backend, whose necessity we don't really understand. >> >> So we want to write? >> >> for font_backend in frame.font_backends: >> if instanceof(font_backend, XftBackend): >> xft_refresh_hack() >> >> That still strikes me as less clean, especially considering we'll have >> to provide the xft_refresh_hack to general-purpose code. > > At the very least let's have a comment there saying that this is a > kludge that's actually done only for that font driver. (Comments tend > to fall out of sync with the code, so making code speak for itself is > better, but a comment is infinitely better than nothing at all.) I've renamed the xftfont hook to something that reflects what's actually going on --- dropping XRender surfaces. I noticed that the Cairo back-end has similar calls for a similar purpose --- see the calls to x_cr_destroy_surface. >> >> We need to do a buffer flip at the end of redisplay for each frame on >> >> which update_end was called during redisplay. >> > >> > Even if update_end was called for a frame whose update was "paused", >> > i.e. whose redisplay was interrupted by incoming input? >> >> Right now, we do a buffer flip unconditionally except in the case that >> we `goto end_of_redisplay`. I think that's a bug actually --- but I have >> to understand more of what this logic is actually doing. There was indeed a bug: we were forgetting to do a buffer flip after redisplay if nothing changed except mouse highlighting. (But that bug was masked by others.) Now, in the double buffer case, we bypass the "goto end_of_redisplay" case by putting the frame up for redisplay if we change the mouse face. >> Why _do_ we have a path that short-circuits the rest of the redisplay >> code? What would happen if we just removed it? It appears to be some >> kind of optimization, and I'm not sure it's actually necessary >> (especially since, according to the comment, we disable it anyway in the >> case of a blinking cursor). > > It's an optimization for the case that nothing needs to be > redisplayed. Isn't the "goto update" path fast enough? > > But I wasn't asking about that, I was asking about this part: > > pending |= update_frame (f, false, false); > > If update_frame returns non-zero, we don't mark all the windows as > having accurate display, which will cause redisplay to be re-entered > again on the first opportunity, and it will then try to redisplay all > of them again. I was asking whether flipping in this case is TRT. > > Without double buffer, what happens in this case is that we could > momentarily flash a partially redrawn window. I don't think it is --- now I've eliminated the terminal hook I added. It's sufficient to rely on frame_up_to_date_hook and calls to flush_frame to do buffer flips. >> > My problems start with the name of the hook, which doesn't hint at all >> > that only double-buffered X11 back-end cares about that. If the hook >> > was called something like double_buffer_flip_hook, or was compiled >> > only if HAVE_XDBE is defined, this issue would go away. >> >> So let's call it double_buffer_flip_hook. Now, we don't need an additional hook. > >> By the way: aren't most calls to x_catch_errors already buggy? As Ken mentioned, I just missed the XSync call in x_catch_errors, so Emacs is fine as it is. >> 1. [must fix] during frame creation, on one of my computers but not >> the other, I momentarily see an all-black frame with a small white >> square, and only a few hundred ms later does the normal frame content >> get draws > > Possibly related to the fact that when Emacs starts, it first defines > a small 10x10 frame, before resizing it to the actual size. Look for > "10" in make_frame. This bug ended up being a deep rabbit hole. The initial white square is a 200x200 default for GtkWindow's idea of its own size in the case that it has no children with non-zero size. Early in frame creation, GTK and X disagree on the size of the frame; GTK thinks it's 200x200, so when it paints its background in response to the first Expose event, it draws a 200x200 white square. It's not just my patch: Emacs always behaves this way. You usually never see it, though, because we XClearWindow on wfixed immediately after, and if you're not unlucky, the X server coalesces GTK's weird 200x200 paint and Emacs's background clearing, so the user never sees anything but a white background. If you take trunk Emacs today, run it with GDK_SYNCHRONIZE=1 in the environment, and step through xg_create_frame_widgets, you can see the same initial white square. My patch doesn't fix the problem. It works around it the same way Emacs did previously, by clearing the window quickly on startup. The fix falls out of fixing the Expose-event handling logic. We really should figure out why GTK is so confused about the size of its window. If we do, we won't have to rely on winning a race to present a good experience to users. >> 2. [must fix] on the same system, and not the other, after resuming >> the system from sleep, Emacs frames momentarily display all white >> before the usual frame contents get filled in > > Could be Emacs waits for an expose event before redrawing the frame, > and meanwhile you show an empty buffer? We weren't doing a buffer flip in response to Expose events. >> 3. [should fix] on the same system, and not the other, resizing the a >> frame interactively still produces some flicking, particulary in the >> modeline, but much less than without the patch entirely. >> This flickering appears to have something to do with XRender, since if >> I force everything to use X11 core rendering instead, I don't see any >> flickering at all. > > If the flickering is on the modeline, a breakpoint in > redisplay_mode_lines should tell you why it happens. The flickering was coming from a combination of things: we weren't clearing the Xft surface cache aggressively enough, and redisplay had a longstanding bug where we'd lose track of whether we'd drawn the cursor just after drawing a garbaged frame. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..38cdecc --- /dev/null +++ b/ChangeLog @@ -0,0 +1,132 @@ +2016-10-27 Daniel Colascione <dancol@dancol.org> + + * src/xterm.h (struct x_display_info): New member supports_xdbe. + (struct x_output): New member draw_desc. + (FRAME_X_DRAWABLE, FRAME_X_DOUBLE_BUFFERED_P) + (FRAME_X_NEED_BUFFER_FLIP): New macros. + (set_up_x_back_buffer, tear_down_x_back_buffer) + (initial_set_up_x_back_buffer): Declare. + + * src/xterm.c: Include Xdbe.h. + (x_begin_cr_clip, x_fill_rectangle, x_draw_rectangle) + (x_draw_vertical_window_border, x_update_end) + (x_setup_relief_color, x_draw_relief_rect) + (x_draw_fringe_bitmap, x_shift_glyphs_for_insert) + (x_scroll_run, x_draw_hollow_cursor, x_draw_bar_cursor): Use + FRAME_X_DRAWABLE instead of FRAME_X_WINDOW; rename local + variables appropriately; substitute calls to XClearArea with + x_clear_area, which DTRT for double buffering. + (x_clear_window, x_clear_area): In double-buffering mode, use + rect-drawing X functions instead of XClearWindow and + XClearArea, which always operate on the front buffer. + (show_back_buffer): New function. + (XTframe_up_to_date): Call show_back_buffer when done. + (x_clear_frame, x_clear_frame_area): Remove obsolete calls to + gtk_widget_queue_draw to refresh scroll bars; scroll bars are + now independent X windows. + (handle_one_xevent): Call font_drop_xrender_surfaces when XftDraw + might need regenerating; perform buffer flip when responding + to Expose events; issue front-buffer clearing commands as + stopgap while we wait for redisplay. + (x_make_frame_visible): Un-bitrot comment; move XSETFRAME + earlier in function. + (x_free_frame_resources): Call tear_down_x_back_buffer when + destroying frame. + (x_term_init): Attempt to initialize double buffer extension. + (x_flip_and_flush): New function. + (x_redisplay_interface): Point to x_flip_and_flush instead of + x_flip directly. + + * src/xftfont.c (xftfont_drop_xrender_surfaces): Use + FRAME_X_DRAWABLE instead of FRAME_X_WINDOW. + (xftfont_drop_xrender_surfaces): New function. + (syms_of_xftfont): Register it. + + * src/xfont.c (xfont_draw): Use FRAME_X_DRAWABLE instead of + FRAME_X_WINDOW. + + * src/xfns.c: Include Xdbe.h. + (x_set_inhibit_double_buffering, set_up_x_back_buffer) + (Fx_double_buffered_p): New functions. + (x_window): Call initial_set_up_x_back_buffer. + (x_make_gc): Use FRAME_X_DRAWABLE instead of FRAME_X_WINDOW. + (Fx_create_frame): Configure `inhibit-double-buffering' + frame parameter. + (x_create_tip_frame): Call initial_set_up_x_back_buffer. + (x_frame_parm_handlers): Register + x_set_inhibit_double_buffering. + (syms_of_xfns): Register Sx_double_buffered_p. + + * src/xfaces.c (x_create_gc): Use FRAME_X_DRAWABLE instead of + FRAME_X_WINDOW. + + * src/xdisp.c (remember_mouse_glyph, init_glyph_string): Use + FRAME_X_DRAWABLE instead of FRAME_X_WINDOW. + (redisplay_internal): Restart redisplay if a frame is garbaged + during updating; explain why. + (redisplay_preserve_echo_area): Call flush_frame + conditionally, depending on `double_buffered'. + (show_mouse_face): In the `double_buffered' case, queue frame + for redisplay (and thus buffer flip), because immediate + drawing will have no effect. + + * src/w32fns.c (w32_frame_parm_handlers): Add placeholder for + x_set_inhibit_double_buffering. + + * src/termhooks.h (struct terminal): Explain why calling + `frame_up_to_date_hook' is important. + + * src/nsfns.m (ns_frame_parm_handlers): Add placeholder for + x_set_inhibit_double_buffering. + + * src/image.c (x_create_bitmap_from_data) + (x_create_bitmap_from_file, x_create_x_image_and_pixmap) + (Create_Pixmap_From_Bitmap_Data) + (x_create_bitmap_from_xpm_data, xpm_load, gs_load): Use + FRAME_X_DRAWABLE instead of FRAME_X_WINDOW; rename local + variables appropriately. + + * src/gtkutil.c: Include Xdbe.h. + (xg_get_widget_from_map): Forward declare. + (xg_clear_under_internal_border): Remove obsolete calls to + refresh scroll bars. + (xg_create_frame_widgets): Call initial_set_up_x_back_buffer. + (xg_free_frame_widgets): Call tear_down_x_back_buffer; reset + FRAME_X_DRAWABLE as well as FRAME_X_WINDOW and for the + same reason. + (xg_set_background_color): Set scroll bar background colors. + (xg_finish_scroll_bar_creation): New function with common + logic of xg_create_scroll_bar, xg_create_horizontal_scroll_bar. Force + scroll bars to be real X11 windows. + (xg_create_scroll_bar, xg_create_horizontal_scroll_bar): Call + xg_finish_scroll_bar_creation. + (xg_update_scrollbar_pos, xg_update_horizontal_scrollbar_pos): + Remove obsolete calls to refresh scroll bars; fix comments. + + * src/ftxfont.c (ftxfont_get_gcs, ftxfont_draw_bitmap, + (ftxfont_draw_background): Use FRAME_X_DRAWABLE instead of + FRAME_X_WINDOW. + + * src/frame.h (struct frame): Add double_buffered bitfield. + + * src/frame.c (frame_parms): Add table entry for new + `inhibit-double-buffering' frame parameter + (syms_of_frame): Register Qinhibit_double_buffering. + + * src/font.h (struct font_driver): Add new + `flush_frame_caches' hook. + (font_drop_xrender_surfaces): Declare. + + * src/font.c (font_drop_xrender_surfaces): New function. + + * src/dispnew.c (redraw_frame): Call font_drop_xrender_surfaces. + + * src/Makefile.in (XDBE_LIBS, XDBE_CFLAGS): Substitute. + + * etc/NEWS: Mention use of double buffering + + * doc/lispref/frames.texi (Management Parameters): Document + `inhibit-double-buffering' frame parameters. + (Visibility of Frames): Document `x-double-buffered-p'. + + * configure.ac: Check for the X double buffer extension diff --git a/configure.ac b/configure.ac index 46fd434..f67fe83 100644 --- a/configure.ac +++ b/configure.ac @@ -3712,6 +3712,24 @@ AC_DEFUN AC_SUBST(XFIXES_CFLAGS) AC_SUBST(XFIXES_LIBS) +### Use Xdbe (-lXdbe) if available +HAVE_XDBE=no +if test "${HAVE_X11}" = "yes"; then + AC_CHECK_HEADER(X11/extensions/Xdbe.h, + [AC_CHECK_LIB(Xext, XdbeAllocateBackBufferName, HAVE_XDBE=yes)], + [], + [#include <X11/Xlib.h> + ]) + if test $HAVE_XDBE = yes; then + XDBE_LIBS=-lXext + fi + if test $HAVE_XDBE = yes; then + AC_DEFINE(HAVE_XDBE, 1, [Define to 1 if you have the Xdbe extension.]) + fi +fi +AC_SUBST(XDBE_CFLAGS) +AC_SUBST(XDBE_LIBS) + ### Use libxml (-lxml2) if available ### mingw32 doesn't use -lxml2, since it loads the library dynamically. HAVE_LIBXML2=no diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 7736438..90f8e35 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -1539,6 +1539,13 @@ Management Parameters If non-@code{nil}, the frame is visible on all virtual desktops on systems with virtual desktops. +@vindex inhibit-double-buffering, a frame parameter +@item inhibit-double-buffering +If non-@code{nil}, the frame is drawn to the screen without double buffering. +Emacs normally attempts to use double buffering, where available, to +reduce flicker. Set this property if you experience display bugs or +pine for that retro, flicker-y feeling. + @ignore @vindex parent-id, a frame parameter @item parent-id @@ -2210,6 +2217,12 @@ Visibility of Frames any control, but Emacs does provide events that you can use to keep track of such changes. @xref{Misc Events}. +@defun x-double-buffered-p &optional frame +This function returns non-@code{nil} if @var{frame} is currently +being rendered with double buffering. @var{frame} defaults to the +selected frame. +@end defun + @node Raising and Lowering @section Raising and Lowering Frames diff --git a/etc/NEWS b/etc/NEWS index a160f81..aad59fc 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -61,6 +61,12 @@ affected by this, as SGI stopped supporting IRIX in December 2013. \f * Changes in Emacs 26.1 ++++ +** Emacs now uses double buffering to reduce flicker when editing and +resizing graphical Emacs frames on the X Window System. This support +requires the DOUBLE-BUFFER extension, which major X servers have +supported for many years. + --- The group 'wp', whose label was "text", is now deprecated. Use the new group 'text', which inherits from 'wp', instead. diff --git a/src/Makefile.in b/src/Makefile.in index 89f7a92..dc0bfff 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -254,6 +254,9 @@ XINERAMA_CFLAGS = XFIXES_LIBS = @XFIXES_LIBS@ XFIXES_CFLAGS = @XFIXES_CFLAGS@ +XDBE_LIBS = @XDBE_LIBS@ +XDBE_CFLAGS = @XDBE_CFLAGS@ + ## widget.o if USE_X_TOOLKIT, otherwise empty. WIDGET_OBJ=@WIDGET_OBJ@ @@ -372,7 +375,7 @@ ALL_CFLAGS= $(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \ $(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \ $(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \ - $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \ + $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) $(XDBE_CFLAGS) \ $(WEBKIT_CFLAGS) \ $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ @@ -489,6 +492,7 @@ LIBES = $(WEBKIT_LIBS) \ $(LIB_EACCESS) $(LIB_FDATASYNC) $(LIB_TIMER_TIME) $(DBUS_LIBS) \ $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \ + $(XDBE_LIBS) \ $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \ $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ diff --git a/src/dispnew.c b/src/dispnew.c index 70d4de0..7f5012a 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -2999,6 +2999,8 @@ redraw_frame (struct frame *f) { /* Error if F has no glyphs. */ eassert (f->glyphs_initialized_p); + if (f->double_buffered) + font_drop_xrender_surfaces (f); update_begin (f); if (FRAME_MSDOS_P (f)) FRAME_TERMINAL (f)->set_terminal_modes_hook (FRAME_TERMINAL (f)); diff --git a/src/font.c b/src/font.c index f8e6794..ce63233 100644 --- a/src/font.c +++ b/src/font.c @@ -5275,6 +5275,16 @@ font_deferred_log (const char *action, Lisp_Object arg, Lisp_Object result) } void +font_drop_xrender_surfaces (struct frame *f) +{ + struct font_driver_list *list; + + for (list = f->font_driver_list; list; list = list->next) + if (list->on && list->driver->drop_xrender_surfaces) + list->driver->drop_xrender_surfaces (f); +} + +void syms_of_font (void) { sort_shift_bits[FONT_TYPE_INDEX] = 0; diff --git a/src/font.h b/src/font.h index cf47729..c14823b 100644 --- a/src/font.h +++ b/src/font.h @@ -763,6 +763,13 @@ struct font_driver Return non-nil if the driver support rendering of combining characters for FONT according to Unicode combining class. */ Lisp_Object (*combining_capability) (struct font *font); + + /* Optional + + Called when frame F is double-buffered and its size changes; Xft + relies on this hook to throw away its old XftDraw (which won't + work after the size change) and get a new one. */ + void (*drop_xrender_surfaces) (struct frame *f); }; @@ -864,6 +871,8 @@ extern void font_filter_properties (Lisp_Object font, const char *const boolean_properties[], const char *const non_boolean_properties[]); +extern void font_drop_xrender_surfaces (struct frame *f); + #ifdef HAVE_FREETYPE extern struct font_driver ftfont_driver; extern void syms_of_ftfont (void); diff --git a/src/frame.c b/src/frame.c index f3a548c..3a2d009 100644 --- a/src/frame.c +++ b/src/frame.c @@ -3128,6 +3128,7 @@ static const struct frame_parm_table frame_parms[] = {"alpha", SYMBOL_INDEX (Qalpha)}, {"sticky", SYMBOL_INDEX (Qsticky)}, {"tool-bar-position", SYMBOL_INDEX (Qtool_bar_position)}, + {"inhibit-double-buffering", SYMBOL_INDEX (Qinhibit_double_buffering)}, }; #ifdef HAVE_WINDOW_SYSTEM @@ -5044,6 +5045,7 @@ syms_of_frame (void) DEFSYM (Qvertical_scroll_bars, "vertical-scroll-bars"); DEFSYM (Qvisibility, "visibility"); DEFSYM (Qwait_for_wm, "wait-for-wm"); + DEFSYM (Qinhibit_double_buffering, "inhibit-double-buffering"); { int i; diff --git a/src/frame.h b/src/frame.h index 5e3ee68..a318b41 100644 --- a/src/frame.h +++ b/src/frame.h @@ -342,6 +342,10 @@ struct frame /* Non-zero if this frame's faces need to be recomputed. */ bool_bf face_change : 1; + /* Non-zero if frame accumulates drawing operations and flushes + them all at once. */ + bool_bf double_buffered : 1; + /* Bitfield area ends here. */ /* Number of lines (rounded up) of tool bar. REMOVE THIS */ diff --git a/src/ftxfont.c b/src/ftxfont.c index f49d44f..bfdeb40 100644 --- a/src/ftxfont.c +++ b/src/ftxfont.c @@ -95,7 +95,7 @@ ftxfont_get_gcs (struct frame *f, unsigned long foreground, unsigned long backgr if (! x_alloc_nearest_color (f, FRAME_X_COLORMAP (f), &color)) break; xgcv.foreground = color.pixel; - new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + new->gcs[i - 1] = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), GCForeground, &xgcv); } unblock_input (); @@ -139,14 +139,14 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, p[n[0]].y = y - bitmap.top + i; if (++n[0] == size) { - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p, size, CoordModeOrigin); n[0] = 0; } } } if (flush && n[0] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p, n[0], CoordModeOrigin); } else @@ -168,7 +168,7 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, pp[n[idx]].y = y - bitmap.top + i; if (++(n[idx]) == size) { - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), idx == 6 ? gc_fore : gcs[idx], pp, size, CoordModeOrigin); n[idx] = 0; @@ -180,10 +180,10 @@ ftxfont_draw_bitmap (struct frame *f, GC gc_fore, GC *gcs, struct font *font, { for (i = 0; i < 6; i++) if (n[i] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gcs[i], p + 0x100 * i, n[i], CoordModeOrigin); if (n[6] > 0) - XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawPoints (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc_fore, p + 0x600, n[6], CoordModeOrigin); } } @@ -203,7 +203,7 @@ ftxfont_draw_background (struct frame *f, struct font *font, GC gc, int x, int y XGetGCValues (FRAME_X_DISPLAY (f), gc, GCForeground | GCBackground, &xgcv); XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.background); - XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), gc, + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y - FONT_BASE (font), width, FONT_HEIGHT (font)); XSetForeground (FRAME_X_DISPLAY (f), gc, xgcv.foreground); } diff --git a/src/gtkutil.c b/src/gtkutil.c index 88e6d30..d4151a0 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -48,6 +48,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "emacsgtkfixed.h" #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + #ifndef HAVE_GTK_WIDGET_SET_HAS_WINDOW #define gtk_widget_set_has_window(w, b) \ (gtk_fixed_set_has_window (GTK_FIXED (w), b)) @@ -143,6 +147,8 @@ struct xg_frame_tb_info GtkTextDirection dir; }; +static GtkWidget * xg_get_widget_from_map (ptrdiff_t idx); + \f /*********************************************************************** Display handling functions @@ -815,12 +821,6 @@ xg_clear_under_internal_border (struct frame *f) { if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0) { -#ifndef USE_CAIRO - GtkWidget *wfixed = f->output_data.x->edit_widget; - - gtk_widget_queue_draw (wfixed); - gdk_window_process_all_updates (); -#endif x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_INTERNAL_BORDER_WIDTH (f)); @@ -1233,6 +1233,7 @@ xg_create_frame_widgets (struct frame *f) by callers of this function. */ gtk_widget_realize (wfixed); FRAME_X_WINDOW (f) = GTK_WIDGET_TO_X_WIN (wfixed); + initial_set_up_x_back_buffer (f); /* Since GTK clears its window by filling with the background color, we must keep X and GTK background in sync. */ @@ -1296,8 +1297,11 @@ xg_free_frame_widgets (struct frame *f) if (tbinfo) xfree (tbinfo); + /* x_free_frame_resources should have taken care of it */ + eassert (!FRAME_X_DOUBLE_BUFFERED_P (f)); gtk_widget_destroy (FRAME_GTK_OUTER_WIDGET (f)); FRAME_X_WINDOW (f) = 0; /* Set to avoid XDestroyWindow in xterm.c */ + FRAME_X_DRAWABLE (f) = 0; FRAME_GTK_OUTER_WIDGET (f) = 0; #ifdef USE_GTK_TOOLTIP if (x->ttip_lbl) @@ -1440,6 +1444,18 @@ xg_set_background_color (struct frame *f, unsigned long bg) { block_input (); xg_set_widget_bg (f, FRAME_GTK_WIDGET (f), FRAME_BACKGROUND_PIXEL (f)); + + Lisp_Object bar; + for (bar = FRAME_SCROLL_BARS (f); + !NILP (bar); + bar = XSCROLL_BAR (bar)->next) + { + GtkWidget* scrollbar = + xg_get_widget_from_map (XSCROLL_BAR (bar)->x_window); + GtkWidget* webox = gtk_widget_get_parent (scrollbar); + xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f)); + } + unblock_input (); } } @@ -2265,7 +2281,6 @@ xg_mark_data (void) } } - /* Callback called when a menu item is destroyed. Used to free data. W is the widget that is being destroyed (not used). CLIENT_DATA points to the xg_menu_item_cb_data associated with the W. */ @@ -3569,44 +3584,23 @@ xg_gtk_scroll_destroy (GtkWidget *widget, gpointer data) xg_remove_widget_from_map (id); } -/* Create a scroll bar widget for frame F. Store the scroll bar - in BAR. - SCROLL_CALLBACK is the callback to invoke when the value of the - bar changes. - END_CALLBACK is the callback to invoke when scrolling ends. - SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used - to set resources for the widget. */ - -void -xg_create_scroll_bar (struct frame *f, +static void +xg_finish_scroll_bar_creation (struct frame *f, + GtkWidget *wscroll, struct scroll_bar *bar, GCallback scroll_callback, GCallback end_callback, const char *scroll_bar_name) { - GtkWidget *wscroll; - GtkWidget *webox; - intptr_t scroll_id; -#ifdef HAVE_GTK3 - GtkAdjustment *vadj; -#else - GtkObject *vadj; -#endif - - /* Page, step increment values are not so important here, they - will be corrected in x_set_toolkit_scroll_bar_thumb. */ - vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, - 0.1, 0.1, 0.1); + GtkWidget *webox = gtk_event_box_new (); - wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj)); - webox = gtk_event_box_new (); gtk_widget_set_name (wscroll, scroll_bar_name); #ifndef HAVE_GTK3 gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS); #endif g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f); - scroll_id = xg_store_widget_in_map (wscroll); + ptrdiff_t scroll_id = xg_store_widget_in_map (wscroll); g_signal_connect (G_OBJECT (wscroll), "destroy", @@ -3630,11 +3624,52 @@ xg_create_scroll_bar (struct frame *f, gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1); gtk_container_add (GTK_CONTAINER (webox), wscroll); + xg_set_widget_bg (f, webox, FRAME_BACKGROUND_PIXEL (f)); + + /* N.B. The event box doesn't become a real X11 window until we ask + for its XID via GTK_WIDGET_TO_X_WIN. If the event box is not a + real X window, it and its scroll-bar child try to draw on the + Emacs main window, which we draw over using Xlib. */ + gtk_widget_realize (webox); + GTK_WIDGET_TO_X_WIN (webox); /* Set the cursor to an arrow. */ xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor); bar->x_window = scroll_id; +} + +/* Create a scroll bar widget for frame F. Store the scroll bar + in BAR. + SCROLL_CALLBACK is the callback to invoke when the value of the + bar changes. + END_CALLBACK is the callback to invoke when scrolling ends. + SCROLL_BAR_NAME is the name we use for the scroll bar. Can be used + to set resources for the widget. */ + +void +xg_create_scroll_bar (struct frame *f, + struct scroll_bar *bar, + GCallback scroll_callback, + GCallback end_callback, + const char *scroll_bar_name) +{ + GtkWidget *wscroll; +#ifdef HAVE_GTK3 + GtkAdjustment *vadj; +#else + GtkObject *vadj; +#endif + + /* Page, step increment values are not so important here, they + will be corrected in x_set_toolkit_scroll_bar_thumb. */ + vadj = gtk_adjustment_new (XG_SB_MIN, XG_SB_MIN, XG_SB_MAX, + 0.1, 0.1, 0.1); + + wscroll = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, GTK_ADJUSTMENT (vadj)); + + xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback, + end_callback, scroll_bar_name); bar->horizontal = 0; } @@ -3652,8 +3687,6 @@ xg_create_horizontal_scroll_bar (struct frame *f, const char *scroll_bar_name) { GtkWidget *wscroll; - GtkWidget *webox; - intptr_t scroll_id; #ifdef HAVE_GTK3 GtkAdjustment *hadj; #else @@ -3666,42 +3699,9 @@ xg_create_horizontal_scroll_bar (struct frame *f, 0.1, 0.1, 0.1); wscroll = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_ADJUSTMENT (hadj)); - webox = gtk_event_box_new (); - gtk_widget_set_name (wscroll, scroll_bar_name); -#ifndef HAVE_GTK3 - gtk_range_set_update_policy (GTK_RANGE (wscroll), GTK_UPDATE_CONTINUOUS); -#endif - g_object_set_data (G_OBJECT (wscroll), XG_FRAME_DATA, (gpointer)f); - scroll_id = xg_store_widget_in_map (wscroll); - - g_signal_connect (G_OBJECT (wscroll), - "destroy", - G_CALLBACK (xg_gtk_scroll_destroy), - (gpointer) scroll_id); - g_signal_connect (G_OBJECT (wscroll), - "change-value", - scroll_callback, - (gpointer) bar); - g_signal_connect (G_OBJECT (wscroll), - "button-release-event", - end_callback, - (gpointer) bar); - - /* The scroll bar widget does not draw on a window of its own. Instead - it draws on the parent window, in this case the edit widget. So - whenever the edit widget is cleared, the scroll bar needs to redraw - also, which causes flicker. Put an event box between the edit widget - and the scroll bar, so the scroll bar instead draws itself on the - event box window. */ - gtk_fixed_put (GTK_FIXED (f->output_data.x->edit_widget), webox, -1, -1); - gtk_container_add (GTK_CONTAINER (webox), wscroll); - - - /* Set the cursor to an arrow. */ - xg_set_cursor (webox, FRAME_DISPLAY_INFO (f)->xg_cursor); - - bar->x_window = scroll_id; + xg_finish_scroll_bar_creation (f, wscroll, bar, scroll_callback, + end_callback, scroll_bar_name); bar->horizontal = 1; } @@ -3770,15 +3770,9 @@ xg_update_scrollbar_pos (struct frame *f, gtk_widget_show_all (wparent); gtk_widget_set_size_request (wscroll, width, height); } -#ifndef USE_CAIRO - gtk_widget_queue_draw (wfixed); - gdk_window_process_all_updates (); -#endif if (oldx != -1 && oldw > 0 && oldh > 0) { - /* Clear under old scroll bar position. This must be done after - the gtk_widget_queue_draw and gdk_window_process_all_updates - above. */ + /* Clear under old scroll bar position. */ oldw += (scale - 1) * oldw; oldx -= (scale - 1) * oldw; x_clear_area (f, oldx, oldy, oldw, oldh); @@ -3841,14 +3835,9 @@ xg_update_horizontal_scrollbar_pos (struct frame *f, gtk_widget_show_all (wparent); gtk_widget_set_size_request (wscroll, width, height); } - gtk_widget_queue_draw (wfixed); - gdk_window_process_all_updates (); if (oldx != -1 && oldw > 0 && oldh > 0) - /* Clear under old scroll bar position. This must be done after - the gtk_widget_queue_draw and gdk_window_process_all_updates - above. */ - x_clear_area (f, - oldx, oldy, oldw, oldh); + /* Clear under old scroll bar position. */ + x_clear_area (f, oldx, oldy, oldw, oldh); /* GTK does not redraw until the main loop is entered again, but if there are no X events pending we will not enter it. So we sync diff --git a/src/image.c b/src/image.c index 9bd2455..1303a93 100644 --- a/src/image.c +++ b/src/image.c @@ -220,7 +220,7 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi #ifdef HAVE_X_WINDOWS Pixmap bitmap; - bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + bitmap = XCreateBitmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), bits, width, height); if (! bitmap) return -1; @@ -327,7 +327,7 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file) filename = SSDATA (found); - result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + result = XReadBitmapFile (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), filename, &width, &height, &bitmap, &xhot, &yhot); if (result != BitmapSuccess) return -1; @@ -1952,7 +1952,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, { #ifdef HAVE_X_WINDOWS Display *display = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); Screen *screen = FRAME_X_SCREEN (f); eassert (input_blocked_p ()); @@ -1981,7 +1981,7 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, (*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height); /* Allocate a pixmap of the same size. */ - *pixmap = XCreatePixmap (display, window, width, height, depth); + *pixmap = XCreatePixmap (display, drawable, width, height, depth); if (*pixmap == NO_PIXMAP) { x_destroy_x_image (*ximg); @@ -2742,7 +2742,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data, img->pixmap = (x_check_image_size (0, img->width, img->height) ? XCreatePixmapFromBitmapData (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), data, img->width, img->height, fg, bg, @@ -3520,7 +3520,7 @@ x_create_bitmap_from_xpm_data (struct frame *f, const char **bits) xpm_init_color_cache (f, &attrs); #endif - rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), (char **) bits, &bitmap, &mask, &attrs); if (rc != XpmSuccess) { @@ -3758,7 +3758,7 @@ xpm_load (struct frame *f, struct image *img) #ifdef HAVE_X_WINDOWS if (rc == XpmSuccess) { - img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->ximg->width, img->ximg->height, img->ximg->depth); if (img->pixmap == NO_PIXMAP) @@ -3768,7 +3768,7 @@ xpm_load (struct frame *f, struct image *img) } else if (img->mask_img) { - img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->mask = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->mask_img->width, img->mask_img->height, img->mask_img->depth); @@ -9541,7 +9541,7 @@ gs_load (struct frame *f, struct image *img) { /* Only W32 version did BLOCK_INPUT here. ++kfs */ block_input (); - img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), img->width, img->height, DefaultDepthOfScreen (FRAME_X_SCREEN (f))); unblock_input (); @@ -9557,7 +9557,7 @@ gs_load (struct frame *f, struct image *img) if successful. We do not record_unwind_protect here because other places in redisplay like calling window scroll functions don't either. Let the Lisp loader use `unwind-protect' instead. */ - printnum1 = FRAME_X_WINDOW (f); + printnum1 = FRAME_X_DRAWABLE (f); printnum2 = img->pixmap; window_and_pixmap_id = make_formatted_string (buffer, "%"pMu" %"pMu, printnum1, printnum2); diff --git a/src/nsfns.m b/src/nsfns.m index ce2622c..b826460 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -971,6 +971,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side x_set_alpha, 0, /* x_set_sticky */ 0, /* x_set_tool_bar_position */ + 0, /* x_set_inhibit_double_buffering */ }; diff --git a/src/termhooks.h b/src/termhooks.h index ff74d99..960323a 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -628,7 +628,12 @@ struct terminal int (*read_socket_hook) (struct terminal *terminal, struct input_event *hold_quit); - /* Called when a frame's display becomes entirely up to date. */ + /* Called when a frame's display becomes entirely up to date. + + N.B. On some double-buffered window systems, frame updates may be + hidden from the user until frame_up_to_date_hook or flush_frame + is called. Redisplay automatically runs this hook. + */ void (*frame_up_to_date_hook) (struct frame *); \f diff --git a/src/w32fns.c b/src/w32fns.c index 2b07bb2..1d83b02 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -9757,6 +9757,7 @@ frame_parm_handler w32_frame_parm_handlers[] = x_set_alpha, 0, /* x_set_sticky */ 0, /* x_set_tool_bar_position */ + 0, /* x_set_inhibit_double_buffering */ }; void diff --git a/src/xdisp.c b/src/xdisp.c index 1a27c4c..9e51d58 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -2501,7 +2501,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) /* Visible feedback for debugging. */ #if false && defined HAVE_X_WINDOWS - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, gx, gy, width, height); #endif @@ -14072,6 +14072,17 @@ redisplay_internal (void) if (!f_redisplay_flag && f->redisplay) goto retry_frame; + /* In some case (e.g., window resize), we notice + only during window updating that the window + content changed unpredictably (e.g., a GTK + scrollbar moved) and that our previous estimation + of the frame content was garbage. We have to + start over. These cases should be rare, so going + all the way back to the top of redisplay should + be good enough. */ + if (FRAME_GARBAGED_P (f)) + goto retry; + /* Prevent various kinds of signals during display update. stdio is not robust about handling signals, which can cause an apparent I/O error. */ @@ -14325,7 +14336,17 @@ redisplay_preserve_echo_area (int from_where) else redisplay_internal (); - flush_frame (SELECTED_FRAME ()); + /* Avoid flush_frame unconditionally for double-buffered frames, + since flush_frame will cause a buffer flip, and we don't want to + do buffer flips unless we know something has changed. If we were + to call flush_frame unconditionally, we'd issue an unceasing + series of useless buffer flips in response to mouse movement over + inert parts of buffers. */ + block_input (); + struct frame *f = SELECTED_FRAME (); + if (!f->double_buffered) + flush_frame (f); + unblock_input (); } @@ -24626,7 +24647,7 @@ init_glyph_string (struct glyph_string *s, s->hdc = hdc; #endif s->display = FRAME_X_DISPLAY (s->f); - s->window = FRAME_X_WINDOW (s->f); + s->window = FRAME_X_DRAWABLE (s->f); s->char2b = char2b; s->hl = hl; s->row = row; @@ -28786,6 +28807,12 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum draw_glyphs_face draw) unblock_input (); #endif /* HAVE_WINDOW_SYSTEM */ } + + /* In a double buffered frame, the drawing commands above will + have no immediate effect, so tell redisplay to do a buffer + flip. */ + if (FRAME_WINDOW_P (f) && f->double_buffered) + fset_redisplay (f); } #ifdef HAVE_WINDOW_SYSTEM diff --git a/src/xfaces.c b/src/xfaces.c index 5837f35..accb98b 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -495,7 +495,7 @@ x_create_gc (struct frame *f, unsigned long mask, XGCValues *xgcv) { GC gc; block_input (); - gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), mask, xgcv); + gc = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), mask, xgcv); unblock_input (); IF_DEBUG (++ngcs); return gc; diff --git a/src/xfns.c b/src/xfns.c index 8571d0e..6026043 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -53,6 +53,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include "gtkutil.h" #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + #ifdef USE_X_TOOLKIT #include <X11/Shell.h> @@ -701,6 +705,35 @@ x_set_tool_bar_position (struct frame *f, wrong_choice (choice, new_value); } +static void +x_set_inhibit_double_buffering (struct frame *f, + Lisp_Object new_value, + Lisp_Object old_value) +{ + block_input (); + if (FRAME_X_WINDOW (f) && !EQ (new_value, old_value)) + { + bool want_double_buffering = NILP (new_value); + bool was_double_buffered = FRAME_X_DOUBLE_BUFFERED_P (f); + /* font_drop_xrender_surfaces in xftfont does something only if + we're double-buffered, so call font_drop_xrender_surfaces before + and after any potential change. One of the calls will end up + being a no-op. */ + if (want_double_buffering != was_double_buffered) + font_drop_xrender_surfaces (f); + if (FRAME_X_DOUBLE_BUFFERED_P (f) && !want_double_buffering) + tear_down_x_back_buffer (f); + else if (!FRAME_X_DOUBLE_BUFFERED_P (f) && want_double_buffering) + set_up_x_back_buffer (f); + if (FRAME_X_DOUBLE_BUFFERED_P (f) != was_double_buffered) + { + SET_FRAME_GARBAGED (f); + font_drop_xrender_surfaces (f); + } + } + unblock_input (); +} + #ifdef USE_GTK /* Set icon from FILE for frame F. By using GTK functions the icon @@ -2483,6 +2516,69 @@ xic_set_xfontset (struct frame *f, const char *base_fontname) \f + +void +set_up_x_back_buffer (struct frame *f) +{ +#ifdef HAVE_XDBE + block_input (); + if (FRAME_X_WINDOW (f) && !FRAME_X_DOUBLE_BUFFERED_P (f)) + { + eassert (!f->double_buffered); + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); + if (FRAME_DISPLAY_INFO (f)->supports_xdbe) + { + /* If allocating a back buffer fails, either because the + server ran out of memory or we don't have the right kind + of visual, just use single-buffered rendering. */ + x_catch_errors (FRAME_X_DISPLAY (f)); + FRAME_X_DRAWABLE (f) = XdbeAllocateBackBufferName ( + FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + XdbeCopied); + if (x_had_errors_p (FRAME_X_DISPLAY (f))) + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); + else + f->double_buffered = true; + x_uncatch_errors_after_check (); + } + } + unblock_input (); +#endif +} + +void +tear_down_x_back_buffer (struct frame *f) +{ +#ifdef HAVE_XDBE + block_input (); + if (FRAME_X_WINDOW (f) && FRAME_X_DOUBLE_BUFFERED_P (f)) + { + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + { + XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f)); + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); + f->double_buffered = false; + } + } + unblock_input (); +#endif +} + +/* Set up double buffering if the frame parameters don't prohibit + it. */ +void +initial_set_up_x_back_buffer (struct frame *f) +{ + block_input (); + eassert (FRAME_X_WINDOW (f)); + FRAME_X_DRAWABLE (f) = FRAME_X_WINDOW (f); + if (NILP (CDR (Fassq (Qinhibit_double_buffering, f->param_alist)))) + set_up_x_back_buffer (f); + unblock_input (); +} + #ifdef USE_X_TOOLKIT /* Create and set up the X widget for frame F. */ @@ -2638,7 +2734,7 @@ x_window (struct frame *f, long window_prompting) f->output_data.x->parent_desc, 0, 0); FRAME_X_WINDOW (f) = XtWindow (frame_widget); - + initial_set_up_x_back_buffer (f); validate_x_resource_name (); class_hints.res_name = SSDATA (Vx_resource_name); @@ -2785,6 +2881,7 @@ x_window (struct frame *f) InputOutput, /* class */ FRAME_X_VISUAL (f), attribute_mask, &attributes); + initial_set_up_x_back_buffer (f); #ifdef HAVE_X_I18N if (use_xim) @@ -2938,7 +3035,7 @@ x_make_gc (struct frame *f) gc_values.line_width = 0; /* Means 1 using fast algorithm. */ f->output_data.x->normal_gc = XCreateGC (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), GCLineWidth | GCForeground | GCBackground, &gc_values); @@ -2947,7 +3044,7 @@ x_make_gc (struct frame *f) gc_values.background = FRAME_FOREGROUND_PIXEL (f); f->output_data.x->reverse_gc = XCreateGC (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), GCForeground | GCBackground | GCLineWidth, &gc_values); @@ -2956,7 +3053,7 @@ x_make_gc (struct frame *f) gc_values.background = f->output_data.x->cursor_pixel; gc_values.fill_style = FillOpaqueStippled; f->output_data.x->cursor_gc - = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + = XCreateGC (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), (GCForeground | GCBackground | GCFillStyle | GCLineWidth), &gc_values); @@ -3463,6 +3560,9 @@ This function is an internal primitive--use `make-frame' instead. */) "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN); x_default_parameter (f, parms, Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL); + x_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); /* Compute the size of the X window. */ window_prompting = x_figure_window_size (f, parms, true, &x_width, &x_height); @@ -5637,6 +5737,7 @@ x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms) f->border_width, CopyFromParent, InputOutput, CopyFromParent, mask, &attrs); + initial_set_up_x_back_buffer (f); XChangeProperty (FRAME_X_DISPLAY (f), tip_window, FRAME_DISPLAY_INFO (f)->Xatom_net_window_type, XA_ATOM, 32, PropModeReplace, @@ -6213,6 +6314,15 @@ Value is t if tooltip was open, nil otherwise. */) return x_hide_tip (!tooltip_reuse_hidden_frame); } +DEFUN ("x-double-buffered-p", Fx_double_buffered_p, Sx_double_buffered_p, + 0, 1, 0, + doc: /* Return t if FRAME is being double buffered. */) + (Lisp_Object frame) +{ + struct frame *f = decode_live_frame (frame); + return FRAME_X_DOUBLE_BUFFERED_P (f) ? Qt : Qnil; +} + \f /*********************************************************************** File selection dialog @@ -6864,6 +6974,7 @@ frame_parm_handler x_frame_parm_handlers[] = x_set_alpha, x_set_sticky, x_set_tool_bar_position, + x_set_inhibit_double_buffering, }; void @@ -7080,6 +7191,7 @@ When using Gtk+ tooltips, the tooltip face is not used. */); defsubr (&Sx_show_tip); defsubr (&Sx_hide_tip); + defsubr (&Sx_double_buffered_p); tip_timer = Qnil; staticpro (&tip_timer); tip_frame = Qnil; diff --git a/src/xfont.c b/src/xfont.c index 45b0e0a..c2b7317 100644 --- a/src/xfont.c +++ b/src/xfont.c @@ -1057,20 +1057,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y, { if (s->padding_p) for (i = 0; i < len; i++) - XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, str + i, 1); else - XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, str, len); } else { if (s->padding_p) for (i = 0; i < len; i++) - XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, str + i, 1); else - XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, str, len); } unblock_input (); @@ -1083,20 +1083,20 @@ xfont_draw (struct glyph_string *s, int from, int to, int x, int y, { if (s->padding_p) for (i = 0; i < len; i++) - XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, s->char2b + from + i, 1); else - XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawImageString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, s->char2b + from, len); } else { if (s->padding_p) for (i = 0; i < len; i++) - XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x + i, y, s->char2b + from + i, 1); else - XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_WINDOW (s->f), + XDrawString16 (FRAME_X_DISPLAY (s->f), FRAME_X_DRAWABLE (s->f), gc, x, y, s->char2b + from, len); } unblock_input (); diff --git a/src/xftfont.c b/src/xftfont.c index 34c6f7d..e9c4fde 100644 --- a/src/xftfont.c +++ b/src/xftfont.c @@ -586,7 +586,7 @@ xftfont_get_xft_draw (struct frame *f) { block_input (); xft_draw= XftDrawCreate (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_X_VISUAL (f), FRAME_X_COLORMAP (f)); unblock_input (); @@ -600,6 +600,8 @@ static int xftfont_draw (struct glyph_string *s, int from, int to, int x, int y, bool with_background) { + block_input (); + struct frame *f = s->f; struct face *face = s->face; struct xftfont_info *xftfont_info = (struct xftfont_info *) s->font; @@ -614,7 +616,6 @@ xftfont_draw (struct glyph_string *s, int from, int to, int x, int y, xftface_info = (struct xftface_info *) face->extra; xftfont_get_colors (f, face, s->gc, xftface_info, &fg, with_background ? &bg : NULL); - block_input (); if (s->num_clips > 0) XftDrawSetClipRectangles (xft_draw, 0, 0, s->clip, s->num_clips); else @@ -654,7 +655,6 @@ xftfont_draw (struct glyph_string *s, int from, int to, int x, int y, XftDrawGlyphs (xft_draw, &fg, xftfont_info->xftfont, x, y, code, len); unblock_input (); - return len; } @@ -678,13 +678,10 @@ xftfont_shape (Lisp_Object lgstring) static int xftfont_end_for_frame (struct frame *f) { + block_input (); XftDraw *xft_draw; - /* Don't do anything if display is dead */ - if (FRAME_X_DISPLAY (f) == NULL) return 0; - xft_draw = font_get_frame_data (f, Qxft); - if (xft_draw) { block_input (); @@ -692,9 +689,19 @@ xftfont_end_for_frame (struct frame *f) unblock_input (); font_put_frame_data (f, Qxft, NULL); } + unblock_input (); return 0; } +static void +xftfont_drop_xrender_surfaces (struct frame *f) +{ + block_input (); + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + xftfont_end_for_frame (f); + unblock_input (); +} + static bool xftfont_cached_font_ok (struct frame *f, Lisp_Object font_object, Lisp_Object entity) @@ -777,6 +784,9 @@ This is needed with some fonts to correct vertical overlap of glyphs. */); #if defined (HAVE_M17N_FLT) && defined (HAVE_LIBOTF) xftfont_driver.shape = xftfont_shape; #endif + // When using X double buffering, the XftDraw structure we + // build seems to be useless once a frame is resized, so + xftfont_driver.drop_xrender_surfaces = xftfont_drop_xrender_surfaces; register_font_driver (&xftfont_driver, NULL); } diff --git a/src/xterm.c b/src/xterm.c index 7476694..96dc8e6 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -45,6 +45,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ #include <X11/extensions/Xrender.h> #endif +#ifdef HAVE_XDBE +#include <X11/extensions/Xdbe.h> +#endif + /* Load sys/types.h if not already loaded. In some systems loading it twice is suicidal. */ #ifndef makedev @@ -360,7 +364,7 @@ x_begin_cr_clip (struct frame *f, GC gc) { cairo_surface_t *surface; surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_DISPLAY_INFO (f)->visual, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); @@ -722,7 +726,7 @@ x_fill_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) cairo_fill (cr); x_end_cr_clip (f); #else - XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XFillRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y, width, height); #endif } @@ -740,7 +744,7 @@ x_draw_rectangle (struct frame *f, GC gc, int x, int y, int width, int height) cairo_stroke (cr); x_end_cr_clip (f); #else - XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawRectangle (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), gc, x, y, width, height); #endif } @@ -756,6 +760,9 @@ x_clear_window (struct frame *f) cairo_paint (cr); x_end_cr_clip (f); #else + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + x_clear_area (f, 0, 0, FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f)); + else XClearWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); #endif } @@ -1067,7 +1074,7 @@ x_draw_vertical_window_border (struct window *w, int x, int y0, int y1) #ifdef USE_CAIRO x_fill_rectangle (f, f->output_data.x->normal_gc, x, y0, 1, y1 - y0); #else - XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), + XDrawLine (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, y0, x, y1); #endif } @@ -1175,6 +1182,39 @@ x_update_window_end (struct window *w, bool cursor_on_p, } } +/* Show the frame back buffer. If frame is double-buffered, + atomically publish to the user's screen graphics updates made since + the last call to show_back_buffer. */ +static void +show_back_buffer (struct frame *f) +{ + block_input (); + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + { +#ifdef HAVE_XDBE + XdbeSwapInfo swap_info; + memset (&swap_info, 0, sizeof (swap_info)); + swap_info.swap_window = FRAME_X_WINDOW (f); + swap_info.swap_action = XdbeCopied; + XdbeSwapBuffers (FRAME_X_DISPLAY (f), &swap_info, 1); +#else + eassert (!"should have back-buffer only with XDBE"); +#endif + /* If we don't flush here, we don't respond in a timely way to + interactive frame resizing. */ + x_flush (f); + } + unblock_input (); +} + +/* Updates back buffer and flushes changes to display. Called + from minibuf read code. */ +static void +x_flip_and_flush (struct frame *f) +{ + show_back_buffer (f); + x_flush (f); +} /* End update of frame F. This function is installed as a hook in update_end. */ @@ -1207,7 +1247,7 @@ x_update_end (struct frame *f) if (! FRAME_EXTERNAL_MENU_BAR (f)) height += FRAME_MENU_BAR_HEIGHT (f); surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_DISPLAY_INFO (f)->visual, width, height); @@ -1220,7 +1260,7 @@ x_update_end (struct frame *f) cairo_destroy (cr); unblock_input (); } -#endif /* USE_CAIRO */ +#endif #ifndef XFlush block_input (); @@ -1229,15 +1269,17 @@ x_update_end (struct frame *f) #endif } - /* This function is called from various places in xdisp.c whenever a complete update has been performed. */ static void XTframe_up_to_date (struct frame *f) { + block_input (); if (FRAME_X_P (f)) FRAME_MOUSE_UPDATE (f); + show_back_buffer (f); + unblock_input (); } @@ -1354,7 +1396,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring #else /* not USE_CAIRO */ if (p->which) { - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); char *bits; Pixmap pixmap, clipmask = (Pixmap) 0; int depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f)); @@ -1367,7 +1409,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring /* Draw the bitmap. I believe these small pixmaps can be cached by the server. */ - pixmap = XCreatePixmapFromBitmapData (display, window, bits, p->wd, p->h, + pixmap = XCreatePixmapFromBitmapData (display, drawable, bits, p->wd, p->h, (p->cursor_p ? (p->overlay_p ? face->background : f->output_data.x->cursor_pixel) @@ -1386,7 +1428,7 @@ x_draw_fringe_bitmap (struct window *w, struct glyph_row *row, struct draw_fring XChangeGC (display, gc, GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv); } - XCopyArea (display, pixmap, window, gc, 0, 0, + XCopyArea (display, pixmap, drawable, gc, 0, 0, p->wd, p->h, p->x, p->y); XFreePixmap (display, pixmap); @@ -2565,7 +2607,7 @@ x_setup_relief_color (struct frame *f, struct relief *relief, double factor, { xgcv.stipple = dpyinfo->gray; mask |= GCStipple; - relief->gc = XCreateGC (dpy, FRAME_X_WINDOW (f), mask, &xgcv); + relief->gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), mask, &xgcv); } else XChangeGC (dpy, relief->gc, mask, &xgcv); @@ -2696,7 +2738,7 @@ x_draw_relief_rect (struct frame *f, x_reset_clip_rectangles (f, bottom_right_gc); #else Display *dpy = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); int i; GC gc; @@ -2715,12 +2757,12 @@ x_draw_relief_rect (struct frame *f, if (top_p) { if (width == 1) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, top_y, right_x + !right_p, top_y); for (i = 1; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i * left_p, top_y + i, right_x + 1 - i * right_p, top_y + i); } @@ -2729,13 +2771,13 @@ x_draw_relief_rect (struct frame *f, if (left_p) { if (width == 1) - XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y); + XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y); - XClearArea (dpy, window, left_x, top_y, 1, 1, False); - XClearArea (dpy, window, left_x, bottom_y, 1, 1, False); + x_clear_area(f, left_x, top_y, 1, 1); + x_clear_area(f, left_x, bottom_y, 1, 1); for (i = (width > 1 ? 1 : 0); i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i, top_y + (i + 1) * top_p, left_x + i, bottom_y + 1 - (i + 1) * bot_p); } @@ -2751,23 +2793,23 @@ x_draw_relief_rect (struct frame *f, { /* Outermost top line. */ if (top_p) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, top_y, right_x + !right_p, top_y); /* Outermost left line. */ if (left_p) - XDrawLine (dpy, window, gc, left_x, top_y + 1, left_x, bottom_y); + XDrawLine (dpy, drawable, gc, left_x, top_y + 1, left_x, bottom_y); } /* Bottom. */ if (bot_p) { - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + left_p, bottom_y, right_x + !right_p, bottom_y); for (i = 1; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, left_x + i * left_p, bottom_y - i, right_x + 1 - i * right_p, bottom_y - i); } @@ -2775,10 +2817,10 @@ x_draw_relief_rect (struct frame *f, /* Right. */ if (right_p) { - XClearArea (dpy, window, right_x, top_y, 1, 1, False); - XClearArea (dpy, window, right_x, bottom_y, 1, 1, False); + x_clear_area(f, right_x, top_y, 1, 1); + x_clear_area(f, right_x, bottom_y, 1, 1); for (i = 0; i < width; ++i) - XDrawLine (dpy, window, gc, + XDrawLine (dpy, drawable, gc, right_x - i, top_y + (i + 1) * top_p, right_x - i, bottom_y + 1 - (i + 1) * bot_p); } @@ -3741,7 +3783,7 @@ x_shift_glyphs_for_insert (struct frame *f, int x, int y, int width, int height, /* Never called on a GUI frame, see http://lists.gnu.org/archive/html/emacs-devel/2015-05/msg00456.html */ - XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), + XCopyArea (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, y, width, height, x + shift_by, y); @@ -3782,6 +3824,12 @@ x_clear_area (struct frame *f, int x, int y, int width, int height) cairo_fill (cr); x_end_cr_clip (f); #else + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + XFillRectangle (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f), + f->output_data.x->reverse_gc, + x, y, width, height); + else x_clear_area1 (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f), x, y, width, height, False); #endif @@ -3805,13 +3853,6 @@ x_clear_frame (struct frame *f) something like that, then they should be notified. */ x_scroll_bar_clear (f); -#if defined (USE_GTK) && defined (USE_TOOLKIT_SCROLL_BARS) - /* Make sure scroll bars are redrawn. As they aren't redrawn by - redisplay, do it here. */ - if (FRAME_GTK_WIDGET (f)) - gtk_widget_queue_draw (FRAME_GTK_WIDGET (f)); -#endif - XFlush (FRAME_X_DISPLAY (f)); unblock_input (); @@ -4109,7 +4150,7 @@ x_scroll_run (struct window *w, struct run *run) SET_FRAME_GARBAGED (f); #else XCopyArea (FRAME_X_DISPLAY (f), - FRAME_X_WINDOW (f), FRAME_X_WINDOW (f), + FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), f->output_data.x->normal_gc, x, from_y, width, height, @@ -7766,12 +7807,35 @@ handle_one_xevent (struct x_display_info *dpyinfo, { if (!FRAME_VISIBLE_P (f)) { + block_input (); SET_FRAME_VISIBLE (f, 1); SET_FRAME_ICONIFIED (f, false); + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + font_drop_xrender_surfaces (f); f->output_data.x->has_been_visible = true; SET_FRAME_GARBAGED (f); + unblock_input (); } - else + else if (FRAME_GARBAGED_P (f)) + { +#ifdef USE_GTK + /* Go around the back buffer and manually clear the + window the first time we show it. This way, we avoid + showing users the sanity-defying horror of whatever + GtkWindow is rendering beneath us. We've garbaged + the frame, so we'll redraw the whole thing on next + redisplay anyway. */ + x_clear_area1 ( + FRAME_X_DISPLAY (f), + FRAME_X_WINDOW (f), + event->xexpose.x, event->xexpose.y, + event->xexpose.width, event->xexpose.height, + 0); +#endif + } + + + if (!FRAME_GARBAGED_P (f)) { #ifdef USE_GTK /* This seems to be needed for GTK 2.6 and later, see @@ -7783,6 +7847,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, expose_frame (f, event->xexpose.x, event->xexpose.y, event->xexpose.width, event->xexpose.height); } + + if (!FRAME_GARBAGED_P (f)) + show_back_buffer (f); } else { @@ -7822,10 +7889,13 @@ handle_one_xevent (struct x_display_info *dpyinfo, available. */ f = x_window_to_frame (dpyinfo, event->xgraphicsexpose.drawable); if (f) + { expose_frame (f, event->xgraphicsexpose.x, event->xgraphicsexpose.y, event->xgraphicsexpose.width, event->xgraphicsexpose.height); + show_back_buffer (f); + } #ifdef USE_X_TOOLKIT else goto OTHER; @@ -8410,7 +8480,17 @@ handle_one_xevent (struct x_display_info *dpyinfo, else configureEvent = next_event; } + f = x_top_window_to_frame (dpyinfo, configureEvent.xconfigure.window); + /* Unfortunately, we need to call font_drop_xrender_surfaces for + _all_ ConfigureNotify events, otherwise we miss some and + flicker. Don't try to optimize these calls by looking only + for size changes: that's not sufficient. We miss some + surface invalidations and flicker. */ + block_input (); + if (f && FRAME_X_DOUBLE_BUFFERED_P (f)) + font_drop_xrender_surfaces (f); + unblock_input (); #ifdef USE_CAIRO if (f) x_cr_destroy_surface (f); #endif @@ -8419,6 +8499,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, && (f = any) && configureEvent.xconfigure.window == FRAME_X_WINDOW (f)) { + block_input (); + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + font_drop_xrender_surfaces (f); + unblock_input (); xg_frame_resized (f, configureEvent.xconfigure.width, configureEvent.xconfigure.height); #ifdef USE_CAIRO @@ -8429,6 +8513,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, #endif if (f) { + x_net_wm_state (f, configureEvent.xconfigure.window); #ifdef USE_X_TOOLKIT @@ -8437,7 +8522,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, { if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width) + { SET_FRAME_GARBAGED (f); + } FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height; FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width; } @@ -8880,7 +8967,7 @@ x_draw_hollow_cursor (struct window *w, struct glyph_row *row) if (dpyinfo->scratch_cursor_gc) XChangeGC (dpy, dpyinfo->scratch_cursor_gc, GCForeground, &xgcv); else - dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_WINDOW (f), + dpyinfo->scratch_cursor_gc = XCreateGC (dpy, FRAME_X_DRAWABLE (f), GCForeground, &xgcv); gc = dpyinfo->scratch_cursor_gc; @@ -8937,7 +9024,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text else { Display *dpy = FRAME_X_DISPLAY (f); - Window window = FRAME_X_WINDOW (f); + Drawable drawable = FRAME_X_DRAWABLE (f); GC gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc; unsigned long mask = GCForeground | GCBackground | GCGraphicsExposures; struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id); @@ -8958,7 +9045,7 @@ x_draw_bar_cursor (struct window *w, struct glyph_row *row, int width, enum text XChangeGC (dpy, gc, mask, &xgcv); else { - gc = XCreateGC (dpy, window, mask, &xgcv); + gc = XCreateGC (dpy, drawable, mask, &xgcv); FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc; } @@ -9028,11 +9115,6 @@ static void x_clear_frame_area (struct frame *f, int x, int y, int width, int height) { x_clear_area (f, x, y, width, height); -#ifdef USE_GTK - /* Must queue a redraw, because scroll bars might have been cleared. */ - if (FRAME_GTK_WIDGET (f)) - gtk_widget_queue_draw (FRAME_GTK_WIDGET (f)); -#endif } @@ -10889,7 +10971,7 @@ x_make_frame_visible (struct frame *f) if (! FRAME_VISIBLE_P (f)) { - /* We test FRAME_GARBAGED_P here to make sure we don't + /* We test asked_for_visible here to make sure we don't call x_set_offset a second time if we get to x_make_frame_visible a second time before the window gets really visible. */ @@ -10935,6 +11017,8 @@ x_make_frame_visible (struct frame *f) will set it when they are handled. */ bool previously_visible = f->output_data.x->has_been_visible; + XSETFRAME (frame, f); + original_left = f->left_pos; original_top = f->top_pos; @@ -10981,8 +11065,6 @@ x_make_frame_visible (struct frame *f) unblock_input (); } - XSETFRAME (frame, f); - /* Process X events until a MapNotify event has been seen. */ while (!FRAME_VISIBLE_P (f)) { @@ -11227,6 +11309,7 @@ x_free_frame_resources (struct frame *f) font-driver (e.g. xft) access a window while finishing a face. */ free_frame_faces (f); + tear_down_x_back_buffer (f); if (f->output_data.x->icon_desc) XDestroyWindow (FRAME_X_DISPLAY (f), f->output_data.x->icon_desc); @@ -11270,6 +11353,7 @@ x_free_frame_resources (struct frame *f) xg_free_frame_widgets (f); #endif /* USE_GTK */ + tear_down_x_back_buffer (f); if (FRAME_X_WINDOW (f)) XDestroyWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f)); #endif /* !USE_X_TOOLKIT */ @@ -12113,6 +12197,14 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) dpyinfo->cmap = XCreateColormap (dpyinfo->display, dpyinfo->root_window, dpyinfo->visual, AllocNone); +#ifdef HAVE_XDBE + dpyinfo->supports_xdbe = false; + int xdbe_major; + int xdbe_minor; + if (XdbeQueryExtension (dpyinfo->display, &xdbe_major, &xdbe_minor)) + dpyinfo->supports_xdbe = true; +#endif + #ifdef HAVE_XFT { /* If we are using Xft, the following precautions should be made: @@ -12462,7 +12554,7 @@ static struct redisplay_interface x_redisplay_interface = x_after_update_window_line, x_update_window_begin, x_update_window_end, - x_flush, + x_flip_and_flush, x_clear_window_mouse_face, x_get_glyph_overhangs, x_fix_overlapping_area, diff --git a/src/xterm.h b/src/xterm.h index 675a484..734e58b 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -475,6 +475,10 @@ struct x_display_info #ifdef USE_XCB xcb_connection_t *xcb_connection; #endif + +#ifdef HAVE_XDBE + bool supports_xdbe; +#endif }; #ifdef HAVE_X_I18N @@ -527,6 +531,11 @@ struct x_output and the X window has not yet been created. */ Window window_desc; + /* The drawable to which we're rendering. In the single-buffered + base, the window itself. In the double-buffered case, the + window's back buffer. */ + Drawable draw_desc; + /* The X window used for the bitmap icon; or 0 if we don't have a bitmap icon. */ Window icon_desc; @@ -737,6 +746,15 @@ enum /* Return the X window used for displaying data in frame F. */ #define FRAME_X_WINDOW(f) ((f)->output_data.x->window_desc) +/* Return the drawable used for rendering to frame F. */ +#define FRAME_X_DRAWABLE(f) ((f)->output_data.x->draw_desc) + +#define FRAME_X_DOUBLE_BUFFERED_P(f) \ + (FRAME_X_WINDOW (f) != FRAME_X_DRAWABLE (f)) + +/* Return the need-buffer-flip flag for frame F. */ +#define FRAME_X_NEED_BUFFER_FLIP(f) ((f)->output_data.x->need_buffer_flip) + /* Return the outermost X window associated with the frame F. */ #ifdef USE_X_TOOLKIT #define FRAME_OUTER_WINDOW(f) ((f)->output_data.x->widget ? \ @@ -1140,6 +1158,10 @@ extern bool x_wm_supports (struct frame *, Atom); extern void x_wait_for_event (struct frame *, int); extern void x_clear_under_internal_border (struct frame *f); +extern void set_up_x_back_buffer (struct frame* f); +extern void tear_down_x_back_buffer (struct frame* f); +extern void initial_set_up_x_back_buffer (struct frame* f); + /* Defined in xselect.c. */ extern void x_handle_property_notify (const XPropertyEvent *); ^ permalink raw reply related [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-27 19:06 ` dancol @ 2016-10-27 19:36 ` Eli Zaretskii [not found] ` <db81befd-7a72-58d9-b7a8-107df89bcab3@dancol.org> 2016-10-27 22:18 ` Paul Eggert 1 sibling, 1 reply; 31+ messages in thread From: Eli Zaretskii @ 2016-10-27 19:36 UTC (permalink / raw) To: dancol; +Cc: raeburn, emacs-devel > Date: Thu, 27 Oct 2016 12:06:25 -0700 > From: dancol@dancol.org > Cc: Daniel Colascione <dancol@dancol.org>, Ken Raeburn <raeburn@raeburn.org>, > emacs-devel@gnu.org > > Below is a new much-improved version of the patch. It should address the > legibility concerns while probably adding more. It addresses all the bugs > I've been able to find and on my machine, produces buttery smooth editing > and resizing. Thanks. > >> Why _do_ we have a path that short-circuits the rest of the redisplay > >> code? What would happen if we just removed it? It appears to be some > >> kind of optimization, and I'm not sure it's actually necessary > >> (especially since, according to the comment, we disable it anyway in the > >> case of a blinking cursor). > > > > It's an optimization for the case that nothing needs to be > > redisplayed. > > Isn't the "goto update" path fast enough? That would still call hscroll_windows and update_frame, which is just waste of cycles when we know nothing's changed. > @@ -14072,6 +14072,17 @@ redisplay_internal (void) > if (!f_redisplay_flag && f->redisplay) > goto retry_frame; > > + /* In some case (e.g., window resize), we notice > + only during window updating that the window > + content changed unpredictably (e.g., a GTK > + scrollbar moved) and that our previous estimation > + of the frame content was garbage. We have to > + start over. These cases should be rare, so going > + all the way back to the top of redisplay should > + be good enough. */ > + if (FRAME_GARBAGED_P (f)) > + goto retry; This worries me a bit: FRAME_GARBAGED_P returns non-zero for TTY frames very frequently, whereas at least the comment seems to imply this is needed only for GUI frames, perhaps even only in GTK builds. If that is correct, then perhaps add a FRAME_WINDOW_P test here, to avoid unnecessarily slowing down redisplay. Thanks again for working on this. ^ permalink raw reply [flat|nested] 31+ messages in thread
[parent not found: <db81befd-7a72-58d9-b7a8-107df89bcab3@dancol.org>]
* Re: RFC: flicker-free double-buffered Emacs under X11 [not found] ` <db81befd-7a72-58d9-b7a8-107df89bcab3@dancol.org> @ 2016-10-27 19:56 ` Daniel Colascione 2016-10-28 6:31 ` Eli Zaretskii 0 siblings, 1 reply; 31+ messages in thread From: Daniel Colascione @ 2016-10-27 19:56 UTC (permalink / raw) To: Eli Zaretskii; +Cc: raeburn, emacs-devel On 10/27/2016 12:44 PM, Daniel Colascione wrote: > On 10/27/2016 12:36 PM, Eli Zaretskii wrote: >>> Date: Thu, 27 Oct 2016 12:06:25 -0700 >>> From: dancol@dancol.org >>> Cc: Daniel Colascione <dancol@dancol.org>, Ken Raeburn >>> <raeburn@raeburn.org>, >>> emacs-devel@gnu.org >>> >>> Below is a new much-improved version of the patch. It should address the >>> legibility concerns while probably adding more. It addresses all the >>> bugs >>> I've been able to find and on my machine, produces buttery smooth >>> editing >>> and resizing. >> >> Thanks. >> >>>>> Why _do_ we have a path that short-circuits the rest of the redisplay >>>>> code? What would happen if we just removed it? It appears to be some >>>>> kind of optimization, and I'm not sure it's actually necessary >>>>> (especially since, according to the comment, we disable it anyway >>>>> in the >>>>> case of a blinking cursor). >>>> >>>> It's an optimization for the case that nothing needs to be >>>> redisplayed. >>> >>> Isn't the "goto update" path fast enough? >> >> That would still call hscroll_windows and update_frame, which is just >> waste of cycles when we know nothing's changed. >> >>> @@ -14072,6 +14072,17 @@ redisplay_internal (void) >>> if (!f_redisplay_flag && f->redisplay) >>> goto retry_frame; >>> >>> + /* In some case (e.g., window resize), we notice >>> + only during window updating that the window >>> + content changed unpredictably (e.g., a GTK >>> + scrollbar moved) and that our previous estimation >>> + of the frame content was garbage. We have to >>> + start over. These cases should be rare, so going >>> + all the way back to the top of redisplay should >>> + be good enough. */ >>> + if (FRAME_GARBAGED_P (f)) >>> + goto retry; >> >> This worries me a bit: FRAME_GARBAGED_P returns non-zero for TTY >> frames very frequently, whereas at least the comment seems to imply >> this is needed only for GUI frames, perhaps even only in GTK builds. >> If that is correct, then perhaps add a FRAME_WINDOW_P test here, to >> avoid unnecessarily slowing down redisplay. > > Sure. > By the way: why would we ever _want_ to allow redisplay to return without having redrawn all garbaged frames? The design of redisplay suggests that a postcondition on redisplay_internal should be that there's nothing left to draw, at least in the case that we weren't interrupted by input. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-27 19:56 ` Daniel Colascione @ 2016-10-28 6:31 ` Eli Zaretskii 0 siblings, 0 replies; 31+ messages in thread From: Eli Zaretskii @ 2016-10-28 6:31 UTC (permalink / raw) To: Daniel Colascione; +Cc: raeburn, emacs-devel > Cc: raeburn@raeburn.org, emacs-devel@gnu.org > From: Daniel Colascione <dancol@dancol.org> > Date: Thu, 27 Oct 2016 12:56:41 -0700 > > why would we ever _want_ to allow redisplay to return > without having redrawn all garbaged frames? The design of redisplay > suggests that a postcondition on redisplay_internal should be that > there's nothing left to draw, at least in the case that we weren't > interrupted by input. Not at that point in the code, which is before update_frame was called. In particular, the place that detects whether input is available is inside update_frame. IOW, redisplay is not going to return there, it is going to call update_frame, which is the last stage of the redisplay cycle: it delivers stuff to the glass as needed. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-27 19:06 ` dancol 2016-10-27 19:36 ` Eli Zaretskii @ 2016-10-27 22:18 ` Paul Eggert 2016-10-27 22:46 ` Clément Pit--Claudel 2016-10-27 23:10 ` Daniel Colascione 1 sibling, 2 replies; 31+ messages in thread From: Paul Eggert @ 2016-10-27 22:18 UTC (permalink / raw) To: Daniel Colascione; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1155 bytes --] Thanks for working on this. I tried building with that patch, and had problems because your email client munged the patch. I cleaned it up by hand and arrived at the attached patch instead. (It needs a better commit message of course.) Maybe next time you could attach the output of git-format-patch. Although the resulting system builds under Fedora 24 x86-64 with GTK, I can't tell any difference in flickering. I don't see much flicker without the patch, or with it. (I am using X, not Wayland.) Maybe my eyes are not good enough.... I don't see the need to expose double-buffering to Lisp via x-double-buffered-p or inhibit-double-buffering. Unless there's a demonstrated need, I'd remove these from the Lisp interface, to simplify it. The configure.ac change should use AC_CACHE_CHECK. set_up_x_back_buffer should be a static function, since it's not used outside its module. In C code we typically use comment /* like this */, not // like this. Also, the spacing for pointer decls is typically 'type *id', not 'type * id' or 'type* id'. You can assume C99 for loop control, e.g., 'for (int i = 0; i < n; i++) ...'. Thanks again. [-- Attachment #2: 0001-flicker-free-double-buffering.patch --] [-- Type: application/x-patch, Size: 61830 bytes --] ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-27 22:18 ` Paul Eggert @ 2016-10-27 22:46 ` Clément Pit--Claudel 2016-10-28 13:14 ` Stefan Monnier 2016-10-27 23:10 ` Daniel Colascione 1 sibling, 1 reply; 31+ messages in thread From: Clément Pit--Claudel @ 2016-10-27 22:46 UTC (permalink / raw) To: emacs-devel [-- Attachment #1.1: Type: text/plain, Size: 471 bytes --] On 2016-10-27 18:18, Paul Eggert wrote: > Although the resulting system builds under Fedora 24 x86-64 with GTK, > I can't tell any difference in flickering. I don't see much flicker > without the patch, or with it. (I am using X, not Wayland.) Maybe my > eyes are not good enough.... I get a lot of flickering myself on Linux Mint with Gtk; often in font-lock heavy buffers or while M-x compile is running. This patch solves all of it :) Cheers, Clément. [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-27 22:46 ` Clément Pit--Claudel @ 2016-10-28 13:14 ` Stefan Monnier 2016-11-01 0:03 ` Dmitry Gutov 0 siblings, 1 reply; 31+ messages in thread From: Stefan Monnier @ 2016-10-28 13:14 UTC (permalink / raw) To: emacs-devel > I get a lot of flickering myself on Linux Mint with Gtk; often in font-lock > heavy buffers or while M-x compile is running. This patch solves all of > it :) I also welcome this patch, of course, but for the record, such flickering should be solved by getting rid of those extra "spurious" updates, rather than by hiding them behind double buffering. Stefan ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-28 13:14 ` Stefan Monnier @ 2016-11-01 0:03 ` Dmitry Gutov 0 siblings, 0 replies; 31+ messages in thread From: Dmitry Gutov @ 2016-11-01 0:03 UTC (permalink / raw) To: Stefan Monnier, emacs-devel On 28.10.2016 16:14, Stefan Monnier wrote: >> I get a lot of flickering myself on Linux Mint with Gtk; often in font-lock >> heavy buffers or while M-x compile is running. This patch solves all of >> it :) > > I also welcome this patch, of course, but for the record, such > flickering should be solved by getting rid of those extra "spurious" > updates, rather than by hiding them behind double buffering. Spurious updates or not, according to a previous discussion, there's no reliable way to avoid flickers other than double-buffering: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=16621#38 Even if we could optimize them out in some cases. As such, I very much welcome this patch as well. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-27 22:18 ` Paul Eggert 2016-10-27 22:46 ` Clément Pit--Claudel @ 2016-10-27 23:10 ` Daniel Colascione 2016-10-28 2:07 ` Paul Eggert 1 sibling, 1 reply; 31+ messages in thread From: Daniel Colascione @ 2016-10-27 23:10 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel Paul Eggert <eggert@cs.ucla.edu> writes: > Thanks for working on this. > > I tried building with that patch, and had problems because your email > client munged the patch. I cleaned it up by hand and arrived at the > attached patch instead. (It needs a better commit message of course.) > Maybe next time you could attach the output of git-format-patch. I need to get Gnus working on that machine. I'd hoped Squirrelmail would have been too simplistic to munge the patch, but I was wrong. > Although the resulting system builds under Fedora 24 x86-64 with GTK, > I can't tell any difference in flickering. I don't see much flicker > without the patch, or with it. (I am using X, not Wayland.) Maybe my > eyes are not good enough.... I've found the flickering while editing to be very machine-dependent. It was driving me nuts on mine, which is why I wrote the patch. Flickering while resizing seems to be a more general problem though. I've found I can reliably reproduce the flickering by isearching for a string that appears in a buffer only once, then leaning on C-s. > I don't see the need to expose double-buffering to Lisp via > x-double-buffered-p or inhibit-double-buffering. Unless there's a > demonstrated need, I'd remove these from the Lisp interface, to > simplify it. I'll feel more comfortable about removing the lisp interface (and so any way for users to this the thing off) once we're sure it's not causing problems. > The configure.ac change should use AC_CACHE_CHECK. None of the other X extension checks do. I'm sure AC_CACHE_CHECK is great, but it feels out of scope. > set_up_x_back_buffer should be a static function, since it's not used > outside its module. Thanks; it wasn't always internal to that file. I'll fix the function. > In C code we typically use comment /* like this */, not // like > this. Also, the spacing for pointer decls is typically 'type *id', not > 'type * id' or 'type* id'. Thanks. I thought I'd gotten rid of all of those. Speaking of comments though: if we're C99-only anyway now, we might as well allow C99 comments. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-27 23:10 ` Daniel Colascione @ 2016-10-28 2:07 ` Paul Eggert 2016-10-28 7:19 ` Eli Zaretskii 0 siblings, 1 reply; 31+ messages in thread From: Paul Eggert @ 2016-10-28 2:07 UTC (permalink / raw) To: Daniel Colascione; +Cc: emacs-devel Daniel Colascione wrote: > I've found the flickering while editing to be very machine-dependent.... > I've found I can reliably reproduce the flickering by isearching for a > string that appears in a buffer only once, then leaning on C-s. Weird. I can't reproduce the flickering problem on either Fedora 24 GTK x86-64 or Ubuntu 16.04.1 GTK x86-64, even though I tried your recipe. I wonder what's different about my platforms? Anyway, if the code is fixing a real problem on your platforms and doesn't cause trouble elsewhere, we should install it. > I'll feel more comfortable about removing the lisp interface (and so any > way for users to this the thing off) once we're sure it's not > causing problems. Perhaps mark it as deprecated so that the user knows it might go away? > if we're C99-only anyway now, we might as well allow > C99 comments. Maybe, but that would be a different topic, and should be addressed in a separate patch. For example, we might also need to change our C-reading programs. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-28 2:07 ` Paul Eggert @ 2016-10-28 7:19 ` Eli Zaretskii 2016-11-06 3:51 ` Paul Eggert 0 siblings, 1 reply; 31+ messages in thread From: Eli Zaretskii @ 2016-10-28 7:19 UTC (permalink / raw) To: Paul Eggert; +Cc: dancol, emacs-devel > From: Paul Eggert <eggert@cs.ucla.edu> > Date: Thu, 27 Oct 2016 19:07:09 -0700 > Cc: emacs-devel@gnu.org > > Daniel Colascione wrote: > > > I've found the flickering while editing to be very machine-dependent.... > > I've found I can reliably reproduce the flickering by isearching for a > > string that appears in a buffer only once, then leaning on C-s. > > Weird. I can't reproduce the flickering problem on either Fedora 24 GTK x86-64 > or Ubuntu 16.04.1 GTK x86-64, even though I tried your recipe. I wonder what's > different about my platforms? My guess is either your systems are too slow, or the keyboard auto-repeat rate is too high. This stuff is very timing-dependent, and in this particular case it depends on the relative speed of redisplay and keyboard auto-repeat. > > if we're C99-only anyway now, we might as well allow > > C99 comments. > > Maybe, but that would be a different topic, and should be addressed in a > separate patch. For example, we might also need to change our C-reading programs. Please note that we already have "//" comments in a few places, so if we don't yet want to allow them, we should remove those. ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-10-28 7:19 ` Eli Zaretskii @ 2016-11-06 3:51 ` Paul Eggert 2016-11-06 15:46 ` Eli Zaretskii 0 siblings, 1 reply; 31+ messages in thread From: Paul Eggert @ 2016-11-06 3:51 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Eli Zaretskii wrote: > Please note that we already have "//" comments in a few places, so if > we don't yet want to allow them, we should remove those. OK, I did that in the emacs-25 and master branches. If we start adding these comments, I suggest limiting them to code in the master branch and making the style change a conscious decision (e.g., when should code use "/*" vs "//" comments?). ^ permalink raw reply [flat|nested] 31+ messages in thread
* Re: RFC: flicker-free double-buffered Emacs under X11 2016-11-06 3:51 ` Paul Eggert @ 2016-11-06 15:46 ` Eli Zaretskii 0 siblings, 0 replies; 31+ messages in thread From: Eli Zaretskii @ 2016-11-06 15:46 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel > Cc: emacs-devel@gnu.org > From: Paul Eggert <eggert@cs.ucla.edu> > Date: Sat, 5 Nov 2016 20:51:11 -0700 > > Eli Zaretskii wrote: > > Please note that we already have "//" comments in a few places, so if > > we don't yet want to allow them, we should remove those. > > OK, I did that in the emacs-25 and master branches. Thanks. > If we start adding these comments, I suggest limiting them to code > in the master branch and making the style change a conscious > decision (e.g., when should code use "/*" vs "//" comments?). Agreed. ^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2016-11-06 15:46 UTC | newest] Thread overview: 31+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2016-10-21 1:32 RFC: flicker-free double-buffered Emacs under X11 Daniel Colascione 2016-10-21 2:27 ` Lars Ingebrigtsen 2016-10-21 2:31 ` Daniel Colascione 2016-10-21 3:24 ` Óscar Fuentes 2016-10-21 3:31 ` Clément Pit--Claudel 2016-10-21 3:41 ` Óscar Fuentes 2016-10-21 3:50 ` Clément Pit--Claudel 2016-10-21 8:23 ` Andreas Schwab 2016-10-21 8:25 ` Andreas Schwab 2016-10-21 3:56 ` Clément Pit--Claudel 2016-10-21 8:49 ` Eli Zaretskii 2016-10-21 11:04 ` Daniel Colascione 2016-10-21 17:43 ` Eli Zaretskii 2016-10-21 18:27 ` Daniel Colascione 2016-10-21 19:27 ` Eli Zaretskii 2016-10-23 20:51 ` Daniel Colascione 2016-10-24 8:05 ` Eli Zaretskii 2016-10-24 18:43 ` Ken Raeburn 2016-10-27 19:06 ` dancol 2016-10-27 19:36 ` Eli Zaretskii [not found] ` <db81befd-7a72-58d9-b7a8-107df89bcab3@dancol.org> 2016-10-27 19:56 ` Daniel Colascione 2016-10-28 6:31 ` Eli Zaretskii 2016-10-27 22:18 ` Paul Eggert 2016-10-27 22:46 ` Clément Pit--Claudel 2016-10-28 13:14 ` Stefan Monnier 2016-11-01 0:03 ` Dmitry Gutov 2016-10-27 23:10 ` Daniel Colascione 2016-10-28 2:07 ` Paul Eggert 2016-10-28 7:19 ` Eli Zaretskii 2016-11-06 3:51 ` Paul Eggert 2016-11-06 15:46 ` Eli Zaretskii
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.