From b87e5eea1dd7c7345d0a9f82759eedfd7c9a8099 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Mitsuharu Date: Sun, 19 May 2019 08:35:40 +0900 Subject: [PATCH] Avoid triple buffering with Xdbe in cairo * src/xterm.h (struct x_output): Remove member cr_surface. Add members cr_surface_desired_width and cr_surface_desired_height. (x_cr_destroy_frame_context) [USE_CAIRO]: Add extern. * src/xterm.c (x_free_cr_resources): Remove function. (FRAME_CR_SURFACE) [USE_CAIRO]: Remove macro. (FRAME_CR_SURFACE_DESIRED_WIDTH, FRAME_CR_SURFACE_DESIRED_HEIGHT) [USE_CAIRO]: New macros. (x_cr_destroy_frame_context) [USE_CAIRO]: Rename from x_cr_destroy_surface. All Uses changed. Don't use FRAME_CR_SURFACE. Make non-static. (x_cr_update_surface_desired_size) [USE_CAIRO]: New function. (x_begin_cr_clip) [USE_CAIRO]: Create Xlib surface if Xdbe is in use. Use FRAME_CR_SURFACE_DESIRED_WIDTH and FRAME_CR_SURFACE_DESIRED_HEIGHT. (x_end_cr_clip) [USE_CAIRO]: Call x_mark_frame_dirty if Xdbe is in use. (x_cr_draw_frame, x_cr_export_frames) [USE_CAIRO]: Save and restore cairo context instead of freeing and clearing it. (x_update_begin) [USE_CAIRO]: Don't create cairo surface here. (show_back_buffer) [USE_CAIRO]: Call cairo_surface_flush before swapping. (x_update_end) [USE_CAIRO]: Don't copy image surface if Xdbe is in use. Get image surface by cairo_get_target instead of FRAME_CR_SURFACE. (x_scroll_run) [USE_CAIRO]: Use XCopyArea if Xdbe is in use. (handle_one_xevent) [USE_CAIRO] : Call x_cr_update_surface_desired_size instead of x_cr_destroy_surface. (x_free_frame_resources) [USE_CAIRO]: Call x_cr_destroy_frame_context instead of x_free_cr_resources. * src/xfns.c (set_up_x_back_buffer, tear_down_x_back_buffer) [USE_CAIRO]: Call x_cr_destroy_frame_context. --- src/xfns.c | 6 ++ src/xterm.c | 211 +++++++++++++++++++++++----------------------------- src/xterm.h | 6 +- 3 files changed, 103 insertions(+), 120 deletions(-) diff --git a/src/xfns.c b/src/xfns.c index 2ceb55a30ad..c8cc1704a47 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -2784,6 +2784,9 @@ set_up_x_back_buffer (struct frame *f) block_input (); if (FRAME_X_WINDOW (f) && !FRAME_X_DOUBLE_BUFFERED_P (f)) { +#ifdef USE_CAIRO + x_cr_destroy_frame_context (f); +#endif FRAME_X_RAW_DRAWABLE (f) = FRAME_X_WINDOW (f); if (FRAME_DISPLAY_INFO (f)->supports_xdbe) { @@ -2813,6 +2816,9 @@ tear_down_x_back_buffer (struct frame *f) { if (FRAME_X_DOUBLE_BUFFERED_P (f)) { +#ifdef USE_CAIRO + x_cr_destroy_frame_context (f); +#endif XdbeDeallocateBackBufferName (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f)); FRAME_X_RAW_DRAWABLE (f) = FRAME_X_WINDOW (f); diff --git a/src/xterm.c b/src/xterm.c index 9371d47c95e..4f4a1d6d02a 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -201,7 +201,6 @@ enum xembed_message XEMBED_ACTIVATE_ACCELERATOR = 14 }; -static void x_free_cr_resources (struct frame *); static bool x_alloc_nearest_color_1 (Display *, Colormap, XColor *); static void x_raise_frame (struct frame *); static void x_lower_frame (struct frame *); @@ -298,7 +297,10 @@ record_event (char *locus, int type) #ifdef USE_CAIRO #define FRAME_CR_CONTEXT(f) ((f)->output_data.x->cr_context) -#define FRAME_CR_SURFACE(f) ((f)->output_data.x->cr_surface) +#define FRAME_CR_SURFACE_DESIRED_WIDTH(f) \ + ((f)->output_data.x->cr_surface_desired_width) +#define FRAME_CR_SURFACE_DESIRED_HEIGHT(f) \ + ((f)->output_data.x->cr_surface_desired_height) static struct x_gc_ext_data * x_gc_get_ext_data (struct frame *f, GC gc, int create_if_not_found_p) @@ -333,19 +335,28 @@ x_extension_initialize (struct x_display_info *dpyinfo) dpyinfo->ext_codes = ext_codes; } -static void -x_cr_destroy_surface (struct frame *f) +void +x_cr_destroy_frame_context (struct frame *f) { - if (FRAME_CR_SURFACE (f)) + if (FRAME_CR_CONTEXT (f)) { - cairo_t *cr = FRAME_CR_CONTEXT (f); - cairo_surface_destroy (FRAME_CR_SURFACE (f)); - FRAME_CR_SURFACE (f) = 0; - if (cr) cairo_destroy (cr); + cairo_destroy (FRAME_CR_CONTEXT (f)); FRAME_CR_CONTEXT (f) = NULL; } } +static void +x_cr_update_surface_desired_size (struct frame *f, int width, int height) +{ + if (FRAME_CR_SURFACE_DESIRED_WIDTH (f) != width + || FRAME_CR_SURFACE_DESIRED_HEIGHT (f) != height) + { + x_cr_destroy_frame_context (f); + FRAME_CR_SURFACE_DESIRED_WIDTH (f) = width; + FRAME_CR_SURFACE_DESIRED_HEIGHT (f) = height; + } +} + cairo_t * x_begin_cr_clip (struct frame *f, GC gc) { @@ -353,21 +364,19 @@ x_begin_cr_clip (struct frame *f, GC gc) if (!cr) { - - if (! FRAME_CR_SURFACE (f)) - { - int scale = 1; -#ifdef USE_GTK - scale = xg_get_scale (f); -#endif - - FRAME_CR_SURFACE (f) = - cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - scale * FRAME_PIXEL_WIDTH (f), - scale * FRAME_PIXEL_HEIGHT (f)); - } - cr = cairo_create (FRAME_CR_SURFACE (f)); - FRAME_CR_CONTEXT (f) = cr; + int width = FRAME_CR_SURFACE_DESIRED_WIDTH (f); + int height = FRAME_CR_SURFACE_DESIRED_HEIGHT (f); + cairo_surface_t *surface; + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), + FRAME_X_RAW_DRAWABLE (f), + FRAME_X_VISUAL (f), + width, height); + else + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + width, height); + cr = FRAME_CR_CONTEXT (f) = cairo_create (surface); + cairo_surface_destroy (surface); } cairo_save (cr); @@ -395,6 +404,8 @@ void x_end_cr_clip (struct frame *f) { cairo_restore (FRAME_CR_CONTEXT (f)); + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + x_mark_frame_dirty (f); } void @@ -532,11 +543,11 @@ x_cr_draw_frame (cairo_t *cr, struct frame *f) width = FRAME_PIXEL_WIDTH (f); height = FRAME_PIXEL_HEIGHT (f); - x_free_cr_resources (f); + cairo_t *saved_cr = FRAME_CR_CONTEXT (f); FRAME_CR_CONTEXT (f) = cr; x_clear_area (f, 0, 0, width, height); expose_frame (f, 0, 0, width, height); - FRAME_CR_CONTEXT (f) = NULL; + FRAME_CR_CONTEXT (f) = saved_cr; } static cairo_status_t @@ -615,11 +626,11 @@ x_cr_export_frames (Lisp_Object frames, cairo_surface_type_t surface_type) while (1) { - x_free_cr_resources (f); + cairo_t *saved_cr = FRAME_CR_CONTEXT (f); FRAME_CR_CONTEXT (f) = cr; x_clear_area (f, 0, 0, width, height); expose_frame (f, 0, 0, width, height); - FRAME_CR_CONTEXT (f) = NULL; + FRAME_CR_CONTEXT (f) = saved_cr; if (NILP (frames)) break; @@ -653,35 +664,6 @@ x_cr_export_frames (Lisp_Object frames, cairo_surface_type_t surface_type) #endif /* USE_CAIRO */ -static void -x_free_cr_resources (struct frame *f) -{ -#ifdef USE_CAIRO - if (f == NULL) - { - Lisp_Object rest, frame; - FOR_EACH_FRAME (rest, frame) - if (FRAME_X_P (XFRAME (frame))) - x_free_cr_resources (XFRAME (frame)); - } - else - { - cairo_t *cr = FRAME_CR_CONTEXT (f); - - if (cr) - { - cairo_surface_t *surface = cairo_get_target (cr); - - if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_XLIB) - { - cairo_destroy (cr); - FRAME_CR_CONTEXT (f) = NULL; - } - } - } -#endif -} - static void x_set_clip_rectangles (struct frame *f, GC gc, XRectangle *rectangles, int n) { @@ -996,41 +978,7 @@ x_set_frame_alpha (struct frame *f) static void x_update_begin (struct frame *f) { -#ifdef USE_CAIRO - if (FRAME_TOOLTIP_P (f) && !FRAME_VISIBLE_P (f)) - return; - - if (! FRAME_CR_SURFACE (f)) - { - int width, height; -#ifdef USE_GTK - if (FRAME_GTK_WIDGET (f)) - { - GdkWindow *w = gtk_widget_get_window (FRAME_GTK_WIDGET (f)); - int scale = xg_get_scale (f); - width = scale * gdk_window_get_width (w); - height = scale * gdk_window_get_height (w); - } - else -#endif - { - width = FRAME_PIXEL_WIDTH (f); - height = FRAME_PIXEL_HEIGHT (f); - if (! FRAME_EXTERNAL_TOOL_BAR (f)) - height += FRAME_TOOL_BAR_HEIGHT (f); - if (! FRAME_EXTERNAL_MENU_BAR (f)) - height += FRAME_MENU_BAR_HEIGHT (f); - } - - if (width > 0 && height > 0) - { - block_input(); - FRAME_CR_SURFACE (f) = cairo_image_surface_create - (CAIRO_FORMAT_ARGB32, width, height); - unblock_input(); - } - } -#endif /* USE_CAIRO */ + /* Nothing to do. */ } /* Draw a vertical window border from (x,y0) to (x,y1) */ @@ -1122,6 +1070,11 @@ show_back_buffer (struct frame *f) if (FRAME_X_DOUBLE_BUFFERED_P (f)) { #ifdef HAVE_XDBE +#ifdef USE_CAIRO + cairo_t *cr = FRAME_CR_CONTEXT (f); + if (cr) + cairo_surface_flush (cairo_get_target (cr)); +#endif XdbeSwapInfo swap_info; memset (&swap_info, 0, sizeof (swap_info)); swap_info.swap_window = FRAME_X_WINDOW (f); @@ -1158,30 +1111,33 @@ x_update_end (struct frame *f) MOUSE_HL_INFO (f)->mouse_face_defer = false; #ifdef USE_CAIRO - if (FRAME_CR_SURFACE (f)) + if (!FRAME_X_DOUBLE_BUFFERED_P (f)) { - cairo_t *cr; - cairo_surface_t *surface; - int width, height; - block_input (); - width = FRAME_PIXEL_WIDTH (f); - height = FRAME_PIXEL_HEIGHT (f); - if (! FRAME_EXTERNAL_TOOL_BAR (f)) - height += FRAME_TOOL_BAR_HEIGHT (f); - if (! FRAME_EXTERNAL_MENU_BAR (f)) - height += FRAME_MENU_BAR_HEIGHT (f); - surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), - FRAME_X_DRAWABLE (f), - FRAME_DISPLAY_INFO (f)->visual, - width, - height); - cr = cairo_create (surface); - cairo_surface_destroy (surface); - - cairo_set_source_surface (cr, FRAME_CR_SURFACE (f), 0, 0); - cairo_paint (cr); - cairo_destroy (cr); + cairo_surface_t *source_surface = cairo_get_target (FRAME_CR_CONTEXT (f)); + if (source_surface) + { + cairo_t *cr; + cairo_surface_t *surface; + int width, height; + + width = FRAME_PIXEL_WIDTH (f); + height = FRAME_PIXEL_HEIGHT (f); + if (! FRAME_EXTERNAL_TOOL_BAR (f)) + height += FRAME_TOOL_BAR_HEIGHT (f); + if (! FRAME_EXTERNAL_MENU_BAR (f)) + height += FRAME_MENU_BAR_HEIGHT (f); + surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f), + FRAME_X_VISUAL (f), + width, height); + cr = cairo_create (surface); + cairo_surface_destroy (surface); + + cairo_set_source_surface (cr, source_surface, 0, 0); + cairo_paint (cr); + cairo_destroy (cr); + } unblock_input (); } #endif @@ -4253,7 +4209,21 @@ x_scroll_run (struct window *w, struct run *run) gui_clear_cursor (w); #ifdef USE_CAIRO - if (FRAME_CR_CONTEXT (f)) + if (FRAME_X_DOUBLE_BUFFERED_P (f)) + { + cairo_t *cr = FRAME_CR_CONTEXT (f); + if (cr) + cairo_surface_flush (cairo_get_target (cr)); + XCopyArea (FRAME_X_DISPLAY (f), + FRAME_X_DRAWABLE (f), FRAME_X_DRAWABLE (f), + f->output_data.x->normal_gc, + x, from_y, + width, height, + x, to_y); + if (cr) + cairo_surface_mark_dirty (cairo_get_target (cr)); + } + else if (FRAME_CR_CONTEXT (f)) { cairo_surface_t *s = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); @@ -8711,7 +8681,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, font_drop_xrender_surfaces (f); unblock_input (); #ifdef USE_CAIRO - if (f) x_cr_destroy_surface (f); + if (f) + x_cr_update_surface_desired_size (f, configureEvent.xconfigure.width, + configureEvent.xconfigure.height); #endif #ifdef USE_GTK if (!f @@ -8725,7 +8697,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, xg_frame_resized (f, configureEvent.xconfigure.width, configureEvent.xconfigure.height); #ifdef USE_CAIRO - x_cr_destroy_surface (f); + x_cr_update_surface_desired_size (f, configureEvent.xconfigure.width, + configureEvent.xconfigure.height); #endif f = 0; } @@ -11835,7 +11808,9 @@ x_free_frame_resources (struct frame *f) free_frame_xic (f); #endif - x_free_cr_resources (f); +#ifdef USE_CAIRO + x_cr_destroy_frame_context (f); +#endif #ifdef USE_X_TOOLKIT if (f->output_data.x->widget) { diff --git a/src/xterm.h b/src/xterm.h index 266a42afa08..84030d5c25e 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -725,8 +725,9 @@ struct x_output #ifdef USE_CAIRO /* Cairo drawing context. */ cairo_t *cr_context; - /* Cairo surface for double buffering */ - cairo_surface_t *cr_surface; + /* Width and height reported by the last ConfigureNotify event. + They are used when creating the cairo surface next time. */ + int cr_surface_desired_width, cr_surface_desired_height; #endif }; @@ -1107,6 +1108,7 @@ extern int x_dispatch_event (XEvent *, Display *); #endif extern int x_x_to_emacs_modifiers (struct x_display_info *, int); #ifdef USE_CAIRO +extern void x_cr_destroy_frame_context (struct frame *); extern cairo_t *x_begin_cr_clip (struct frame *, GC); extern void x_end_cr_clip (struct frame *); extern void x_set_cr_source_with_gc_foreground (struct frame *, GC); -- 2.39.2