]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0897: GTK3 X11 redraws are not coalesced v9.2.0897
authorChristoffer Aasted <dezzadk@gmail.com>
Sun, 2 Aug 2026 18:01:14 +0000 (18:01 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 2 Aug 2026 18:01:14 +0000 (18:01 +0000)
Problem:  GTK3 X11 redraws are not coalesced
Solution: Improve redrawing logic, unify Wayland with X11 redraw
          code (Christoffer Aasted)

closes: #20909

Signed-off-by: Christoffer Aasted <dezzadk@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/gui_gtk_x11.c
src/version.c

index 0647d9b16b37f61d9a019ba434ecb3f0d2b5eeea..f2db6d1da8460196f78477fb198ce1dac47d9024 100644 (file)
@@ -778,25 +778,7 @@ draw_event(GtkWidget *widget UNUSED,
                                // for GTK+ 3, may induce other draw events.
 
     cairo_set_source_surface(cr, gui.surface, 0, 0);
-
-    {
-       cairo_rectangle_list_t *list = NULL;
-
-       list = cairo_copy_clip_rectangle_list(cr);
-       if (list->status != CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
-       {
-           int i;
-
-           for (i = 0; i < list->num_rectangles; i++)
-           {
-               const cairo_rectangle_t *rect = &list->rectangles[i];
-               cairo_rectangle(cr, rect->x, rect->y,
-                                                   rect->width, rect->height);
-               cairo_fill(cr);
-           }
-       }
-       cairo_rectangle_list_destroy(list);
-    }
+    cairo_paint(cr);
 
     return FALSE;
 }
@@ -3758,54 +3740,6 @@ gui_gtk_set_dnd_targets(void)
                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
 }
 
-#ifdef GDK_WINDOWING_WAYLAND
-static struct {
-    int left;
-    int top;
-    int right;
-    int bottom;
-    bool active;
-} wl_dirty_rect = {0, 0, 0, 0, false};
-
-    static void
-wl_queue_dirty_area(int x, int y, int width, int height)
-{
-    if (!wl_dirty_rect.active)
-    {
-       wl_dirty_rect.left = x;
-       wl_dirty_rect.top = y;
-       wl_dirty_rect.right = x + width;
-       wl_dirty_rect.bottom = y + height;
-       wl_dirty_rect.active = true;
-    }
-    else
-    {
-       // Expand to append further changes
-       if (x < wl_dirty_rect.left)   wl_dirty_rect.left = x;
-       if (y < wl_dirty_rect.top)    wl_dirty_rect.top = y;
-       if (x + width > wl_dirty_rect.right)  wl_dirty_rect.right = x + width;
-       if (y + height > wl_dirty_rect.bottom) wl_dirty_rect.bottom = y + height;
-    }
-}
-
-    static void
-wl_flush(void)
-{
-    if (!wl_dirty_rect.active)
-       return;
-    int draw_x = wl_dirty_rect.left;
-    int draw_y = wl_dirty_rect.top;
-    int draw_w = wl_dirty_rect.right - wl_dirty_rect.left;
-    int draw_h = wl_dirty_rect.bottom - wl_dirty_rect.top;
-
-    if (draw_w > 0 && draw_h > 0)
-    {
-       gtk_widget_queue_draw_area(gui.drawarea, draw_x, draw_y, draw_w, draw_h);
-    }
-    wl_dirty_rect.active = false;
-}
-#endif
-
 /*
  * Initialize the GUI. Create all the windows, set up all the callbacks etc.
  * Returns OK for success, FAIL when the GUI can't be started.
@@ -6287,20 +6221,28 @@ gui_gtk_draw_string(int row, int col, char_u *s, int len, int flags)
     return len_sum;
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+static cairo_region_t *dirty_region = NULL;
+
     static void
-queue_draw_area(
-       int     x,
-       int     y,
-       int     width,
-       int     height)
+queue_draw_area(int x, int y, int width, int height)
 {
-#ifdef GDK_WINDOWING_WAYLAND
-    if (gui.is_wayland)
-       wl_queue_dirty_area(x, y, width, height);
+    cairo_rectangle_int_t rect;
+
+    if (width <= 0 || height <= 0 || gui.drawarea == NULL)
+       return;
+
+    rect.x = x;
+    rect.y = y;
+    rect.width = width;
+    rect.height = height;
+
+    if (dirty_region == NULL)
+       dirty_region = cairo_region_create_rectangle(&rect);
     else
-#endif
-       gtk_widget_queue_draw_area(gui.drawarea, x, y, width, height);
+       cairo_region_union_rectangle(dirty_region, &rect);
 }
+#endif
 
     int
 gui_gtk_draw_string_ext(
@@ -6554,8 +6496,8 @@ skipitall:
 
 #if GTK_CHECK_VERSION(3,0,0)
     cairo_destroy(cr);
-    queue_draw_area(area.x, area.y,
-           area.width, area.height);
+    queue_draw_area(FILL_X(col), FILL_Y(row),
+           column_offset * gui.char_width + 1, gui.char_height);
 #else
     gdk_gc_set_clip_rectangle(gui.text_gc, NULL);
 #endif
@@ -6698,8 +6640,7 @@ gui_mch_invert_rectangle(int r, int c, int nr, int nc)
 
     cairo_destroy(cr);
 
-    queue_draw_area(rect.x, rect.y,
-           rect.width, rect.height);
+    queue_draw_area(rect.x, rect.y, rect.width, rect.height);
 #else
     GdkGCValues values;
     GdkGC *invert_gc;
@@ -6852,17 +6793,14 @@ gui_mch_update(void)
 {
     int cnt = 0;       // prevent endless loop
     while (g_main_context_pending(NULL) && !vim_is_input_buf_full()
-                                                               && ++cnt < 100)
+           && ++cnt < 100)
     {
-#ifdef GDK_WINDOWING_WAYLAND
-       if (gui.is_wayland)
-       {
-           int prio = 0;
-           g_main_context_prepare(NULL, &prio);
-           // peek internal scheduling of redraw
-           if (prio == GDK_PRIORITY_REDRAW)
-               gui_may_flush(); // prepares redraw: g_main_context_iteration
-       }
+#if GTK_CHECK_VERSION(3,0,0)
+       int prio = 0;
+       g_main_context_prepare(NULL, &prio);
+       // peek internal scheduling of redraw
+       if (prio == GDK_PRIORITY_REDRAW)
+           gui_may_flush(); // prepares redraw: g_main_context_iteration
 #endif
        g_main_context_iteration(NULL, TRUE);
     }
@@ -6997,18 +6935,19 @@ theend:
     void
 gui_mch_flush(void)
 {
-    if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin))
+    if (gui.mainwin == NULL || !gtk_widget_get_realized(gui.mainwin))
+       return;
+#if GTK_CHECK_VERSION(3,0,0)
+    if (dirty_region != NULL && gui.drawarea != NULL)
     {
-#ifdef GDK_WINDOWING_WAYLAND
-       if (gui.is_wayland)
-           return wl_flush();
-#endif
-#if GTK_CHECK_VERSION(2,4,0)
-       gdk_display_flush(gtk_widget_get_display(gui.mainwin));
+       gtk_widget_queue_draw_region(gui.drawarea, dirty_region);
+       cairo_region_destroy(dirty_region);
+       dirty_region = NULL;
+    }
 #else
-       gdk_display_sync(gtk_widget_get_display(gui.mainwin));
+       gdk_display_flush(gtk_widget_get_display(gui.mainwin));
+       return;
 #endif
-    }
 }
 
 /*
@@ -7060,8 +6999,7 @@ gui_mch_clear_block(int row1arg, int col1arg, int row2arg, int col2arg)
        cairo_fill(cr);
        cairo_destroy(cr);
 
-       queue_draw_area(
-               rect.x, rect.y, rect.width, rect.height);
+       queue_draw_area(rect.x, rect.y, rect.width, rect.height);
     }
 #else // !GTK_CHECK_VERSION(3,0,0)
     gdk_gc_set_foreground(gui.text_gc, &color);
@@ -7097,8 +7035,7 @@ gui_gtk_window_clear(GdkWindow *win)
     cairo_fill(cr);
     cairo_destroy(cr);
 
-    queue_draw_area(
-           rect.x, rect.y, rect.width, rect.height);
+    queue_draw_area(rect.x, rect.y, rect.width, rect.height);
 }
 #else
 # define gui_gtk_window_clear(win)  gdk_window_clear(win)
@@ -7156,8 +7093,7 @@ gui_mch_draw_popup_image(
 # if GTK_CHECK_VERSION(3,0,0)
     cairo_popup_image_paint(wp, gui.surface, x, y,
            src_x, src_y, draw_w, draw_h);
-    if (gui.drawarea != NULL)
-       queue_draw_area(x, y, draw_w, draw_h);
+    queue_draw_area(x, y, draw_w, draw_h);
 # else
     cairo_popup_image_paint(wp, gui.drawarea->window, x, y,
            src_x, src_y, draw_w, draw_h);
index 30b9a310bed69ea1193bb5039bcb3248b5a1b553..7c831d2ae25b3684d338898eb874d05d826b20a4 100644 (file)
@@ -758,6 +758,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    897,
 /**/
     896,
 /**/