Commit: patch 9.2.0865: GTK4: non-hardware accelerated UI is too slow

Christian Brabandt <[email protected]> Mon, 27 Jul 2026 23:00:05 +0200
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
patch 9.2.0865: GTK4: non-hardware accelerated UI is too slow

Commit: https://github.com/vim/vim/commit/b56b4a3a6691a610f53f6d7a1ba721af3ad46b35
Author: Foxe Chen <[email protected]>
Date:   Mon Jul 27 20:43:18 2026 +0000

    patch 9.2.0865: GTK4: non-hardware accelerated UI is too slow
    
    Problem:  GTK4: non-hardware accelerated UI is too slow
    Solution: Remove the sofware rendering (cairo) backend, make the GTK4
              hardware accelerated rendering the default (Foxe Chen).
    
    closes: #20844
    
    Signed-off-by: Foxe Chen <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/gui_x11.txt b/runtime/doc/gui_x11.txt
index 58467023a..8f37a154c 100644
--- a/runtime/doc/gui_x11.txt
+++ b/runtime/doc/gui_x11.txt
@@ -1,4 +1,4 @@
-*gui_x11.txt*	For Vim version 9.2.  Last change: 2026 Jun 29
+*gui_x11.txt*	For Vim version 9.2.  Last change: 2026 Jul 27
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -371,14 +371,6 @@ to use software rendering: >
     $ GSK_RENDERER=cairo gvim
     $ LIBGL_ALWAYS_SOFTWARE=true gvim
 <
-							*gtk4-hwaccel*
-If Vim is compiled with the configure option "--enable-gtk4-hwaccel" set, then
-the GTK4 GUI will use GtkSnapshot instead of Cairo, allowing for hardware
-accelerated rendering, which is much faster.  Enabling this configure option
-also makes the GTK4 GUI use |+image_gdk| instead of |+image_cairo| for
-rendering popup window images.  Note that this feature is currently
-experimental.
-
 Tooltip Colors ~
 							*gtk-tooltip-colors*
 Example, which sets the tooltip colors to black on light-yellow: >
diff --git a/runtime/doc/tags b/runtime/doc/tags
index b01ae4204..28c30f03c 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -8260,7 +8260,6 @@ gt	tabpage.txt	/*gt*
 gtk-css	gui_x11.txt	/*gtk-css*
 gtk-tooltip-colors	gui_x11.txt	/*gtk-tooltip-colors*
 gtk3-slow	gui_x11.txt	/*gtk3-slow*
-gtk4-hwaccel	gui_x11.txt	/*gtk4-hwaccel*
 gtk4-menu-navigation	gui_x11.txt	/*gtk4-menu-navigation*
 gtk4-slow	gui_x11.txt	/*gtk4-slow*
 gu	change.txt	/*gu*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index efe88e949..738e95bdb 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt*	For Vim version 9.2.  Last change: 2026 Jul 22
+*version9.txt*	For Vim version 9.2.  Last change: 2026 Jul 27
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -52663,7 +52663,6 @@ Platform specific ~
 -----------------
 - support OpenType font features in 'guifont' for DirectWrite (Win32)
 - Include strptime() fallback for MS-Windows
-- Hardware-accelerated rendering for the GTK4 GUI via |gtk4-hwaccel|.
 - The NSIS installer has been reworked (Win32):
   - Support per-user and per-machine install.
   - Change the install directory structure.
diff --git a/src/auto/configure b/src/auto/configure
index ffdff9662..8fd4ba3b7 100755
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -862,7 +862,6 @@ enable_gui
 enable_gtk2_check
 enable_gnome_check
 enable_gtk4_check
-enable_gtk4_hwaccel
 enable_gtk3_check
 enable_motif_check
 enable_gtktest
@@ -1542,7 +1541,6 @@ Optional Features:
   --enable-gtk2-check     If auto-select GUI, check for GTK+ 2 default=yes
   --enable-gnome-check    If GTK GUI, check for GNOME default=no
   --enable-gtk4-check     If auto-select GUI, check for GTK 4 default=yes
-  --enable-gtk4-hwaccel  Use hardware accelerated rendering backend for GTK4 default=no
   --enable-gtk3-check     If auto-select GUI, check for GTK+ 3 default=yes
   --enable-motif-check    If auto-select GUI, check for Motif default=yes
   --disable-gtktest       Do not try to compile and run a test GTK program
@@ -10583,28 +10581,6 @@ printf "%s
" "$enable_gtk4_check" >&6; }
   fi
 fi
 
-if test "x$SKIP_GTK4" != "xYES" -a "$enable_gui_canon" = "gtk4"; then
-  { printf "%s
" "$as_me:${as_lineno-$LINENO}: checking --enable-gtk4-hwaccel argument" >&5
-printf %s "checking --enable-gtk4-hwaccel argument... " >&6; }
-  # Check whether --enable-gtk4-hwaccel was given.
-if test ${enable_gtk4_hwaccel+y}
-then :
-  enableval=$enable_gtk4_hwaccel;
-else case e in #(
-  e) enable_gtk4_hwaccel="no" ;;
-esac
-fi
-
-  { printf "%s
" "$as_me:${as_lineno-$LINENO}: result: $enable_gtk4_hwaccel" >&5
-printf "%s
" "$enable_gtk4_hwaccel" >&6; }
-
-  if test "x$enable_gtk4_hwaccel" = "xyes"; then
-    printf "%s
" "#define USE_GTK4_SNAPSHOT 1" >>confdefs.h
-
-  fi
-fi
-
-
 if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then
   { printf "%s
" "$as_me:${as_lineno-$LINENO}: checking whether or not to look for GTK+ 3" >&5
 printf %s "checking whether or not to look for GTK+ 3... " >&6; }
diff --git a/src/config.h.in b/src/config.h.in
index bb16501ea..d26bd2078 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -499,9 +499,6 @@
 /* Define if GTK GUI is to be linked against GTK 4 */
 #undef USE_GTK4
 
-/* Define if GTK4 GUI should use hardware accelerated backend */
-#undef USE_GTK4_SNAPSHOT
-
 /* Define if we have isinf() */
 #undef HAVE_ISINF
 
diff --git a/src/configure.ac b/src/configure.ac
index 0cf75308b..654959874 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -2715,19 +2715,6 @@ if test "x$SKIP_GTK4" != "xYES" -a "$enable_gui_canon" != "gtk4"; then
   fi
 fi
 
-if test "x$SKIP_GTK4" != "xYES" -a "$enable_gui_canon" = "gtk4"; then
-  AC_MSG_CHECKING(--enable-gtk4-hwaccel argument)
-  AC_ARG_ENABLE(gtk4-hwaccel,
-	[  --enable-gtk4-hwaccel  Use hardware accelerated rendering backend for GTK4 [default=no]],
-	, enable_gtk4_hwaccel="no")
-  AC_MSG_RESULT($enable_gtk4_hwaccel)
-
-  if test "x$enable_gtk4_hwaccel" = "xyes"; then
-    AC_DEFINE(USE_GTK4_SNAPSHOT)
-  fi
-fi
-
-
 if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then
   AC_MSG_CHECKING(whether or not to look for GTK+ 3)
   AC_ARG_ENABLE(gtk3-check,
diff --git a/src/feature.h b/src/feature.h
index 3a0b6931c..5d5512615 100644
--- a/src/feature.h
+++ b/src/feature.h
@@ -1132,7 +1132,7 @@
 #endif
 
 #if defined(FEAT_IMAGE) && defined(FEAT_GUI_GTK)
-# ifdef USE_GTK4_SNAPSHOT
+# ifdef USE_GTK4
 #  define FEAT_IMAGE_GDK
 # else
 #  define FEAT_IMAGE_CAIRO
diff --git a/src/gui.c b/src/gui.c
index d58f9f2cc..5d6eae105 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -24,7 +24,7 @@ static void set_guifontwide(char_u *font_name);
 static void gui_check_pos(void);
 static void gui_reset_scroll_region(void);
 static void gui_outstr(char_u *, int);
-#ifndef USE_GTK4_SNAPSHOT
+#ifndef USE_GTK4
 static int gui_screenchar(int off, int flags, guicolor_T fg, guicolor_T bg, int back);
 #endif
 static int gui_outstr_nowrap(char_u *s, int len, int flags, guicolor_T fg, guicolor_T bg, int back);
@@ -1199,7 +1199,7 @@ gui_update_cursor(
     int		cattr;		// cursor attributes
     int		attr;
     attrentry_T *aep = NULL;
-#if (defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MSWIN)) && !defined(USE_GTK4_SNAPSHOT)
+#if (defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MSWIN)) && !defined(USE_GTK4)
     bool	lig_left = false, lig_right = false;
 #endif
 
@@ -1367,7 +1367,7 @@ gui_update_cursor(
      */
     if (!gui.in_focus)
     {
-#ifdef USE_GTK4_SNAPSHOT
+#ifdef USE_GTK4
 	gui_gtk4_draw_cursor(cbg, cfg, -1, -1);
 #else
 	gui_mch_draw_hollow_cursor(cbg);
@@ -1375,7 +1375,7 @@ gui_update_cursor(
 	return;
     }
 
-#ifdef USE_GTK4_SNAPSHOT
+#ifdef USE_GTK4
     // Make sure that character underneath is drawn again in case it is part of
     // a ligature.
     gui_redraw_block(gui.row, gui.col, gui.row, gui.col, GUI_MON_NOCLEAR);
@@ -1392,7 +1392,7 @@ gui_update_cursor(
 	    {
 		gui_redraw_block(gui.row, c + 1, gui.row, gui.col - 1,
 			GUI_MON_NOCLEAR);
-# ifndef USE_GTK4_SNAPSHOT
+# ifndef USE_GTK4
 		lig_left = true;
 # endif
 	    }
@@ -1406,7 +1406,7 @@ gui_update_cursor(
 	    {
 		gui_redraw_block(gui.row, gui.col + 1, gui.row, c - 1,
 			GUI_MON_NOCLEAR);
-# ifndef USE_GTK4_SNAPSHOT
+# ifndef USE_GTK4
 		lig_right = true;
 # endif
 	    }
@@ -1416,7 +1416,7 @@ gui_update_cursor(
     // it again.
     gui.cursor_is_valid = true;
 
-# ifndef USE_GTK4_SNAPSHOT
+# ifndef USE_GTK4
     if ((lig_left || lig_right) && shape->shape != SHAPE_BLOCK)
     {
 	// Because the cursor is not drawn with gui_screenchar(), must blit the
@@ -1435,7 +1435,7 @@ gui_update_cursor(
     old_hl_mask = gui.highlight_mask;
     if (shape->shape == SHAPE_BLOCK)
     {
-#ifdef USE_GTK4_SNAPSHOT
+#ifdef USE_GTK4
 	gui_gtk4_draw_cursor(cbg, cfg, 0, 0);
 #else
 	/*
@@ -1484,7 +1484,7 @@ gui_update_cursor(
 	    }
 #endif
 	}
-#ifdef USE_GTK4_SNAPSHOT
+#ifdef USE_GTK4
 	gui_gtk4_draw_cursor(cbg, cfg, cur_width, cur_height);
 #else
 	gui_mch_draw_part_cursor(cur_width, cur_height, cbg);
@@ -1496,7 +1496,7 @@ gui_update_cursor(
 
 	// Doesn't seem to work for MSWindows. We call gui_redraw_block() above
 	// for GtkSnapshot.
-#if !defined(FEAT_GUI_MSWIN) && !defined(USE_GTK4_SNAPSHOT)
+#if !defined(FEAT_GUI_MSWIN) && !defined(USE_GTK4)
 	gui.highlight_mask = ScreenAttrs[LineOffset[gui.row] + gui.col];
 	(void)gui_screenchar(LineOffset[gui.row] + gui.col,
 		GUI_MON_TRS_CURSOR | GUI_MON_NOCLEAR,
@@ -1689,14 +1689,14 @@ again:
     gui.num_cols = (pixel_width - gui_get_base_width()) / gui.char_width;
     gui.num_rows = (pixel_height - gui_get_base_height()) / gui.char_height;
 
-#ifdef USE_GTK4_SNAPSHOT
+#ifdef USE_GTK4
     gui_gtk4_update_size();
 #endif
 
     gui_position_components(pixel_width);
     gui_reset_scroll_region();
 
-#if defined(FEAT_GUI_GTK) && defined(USE_GTK4) && !defined(USE_GTK4_SNAPSHOT)
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4) && !defined(USE_GTK4)
     // We do not resize the draw area via the "resize" signal. This is because
     // when the window is resized, the form widget is the one that is resized,
     // so let that call gui_resize_shell() which will allocate the surface and
@@ -1726,7 +1726,7 @@ again:
 
     gui_update_scrollbars(TRUE);
     gui_update_cursor(FALSE, TRUE);
-#if defined(FEAT_GUI_GTK) && defined(USE_GTK4_SNAPSHOT)
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4)
     gui_gtk4_calculate_bleed(pixel_width, pixel_height);
 #endif
 #if defined(FEAT_XIM) && !defined(FEAT_GUI_GTK)
@@ -1899,7 +1899,7 @@ gui_set_shellsize(
     gui_update_scrollbars(TRUE);
     gui_reset_scroll_region();
 
-#if defined(FEAT_GUI_GTK) && defined(USE_GTK4_SNAPSHOT)
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4)
     gui_gtk4_calculate_bleed(width, height);
 #endif
 }
@@ -2293,7 +2293,7 @@ gui_outstr(char_u *s, int len)
     }
 }
 
-#ifndef USE_GTK4_SNAPSHOT
+#ifndef USE_GTK4
 /*
  * Output one character (may be one or two display cells).
  * Caller must check for valid "off".
diff --git a/src/gui.h b/src/gui.h
index 16f13e477..e2232982e 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -285,7 +285,7 @@ typedef struct Gui
 #ifdef FEAT_DIRECTX
     bool	directx_enabled;    // DirectX (DirectWrite) rendering active
 #endif
-#if defined(FEAT_GUI_GTK) && defined(USE_GTK4_SNAPSHOT)
+#if defined(FEAT_GUI_GTK) && defined(USE_GTK4)
     int		bleed_right;	    // Number of pixels to bleed bg color right
     int		bleed_bot;	    // Number of pixels to bleed bg color down
 #endif
@@ -400,11 +400,9 @@ typedef struct Gui
     GdkColor	*bgcolor;	    // GDK-styled background color
     GdkColor	*spcolor;	    // GDK-styled special color
 # endif
-# if defined(USE_GTK3) || defined(USE_GTK4)
-#  ifndef USE_GTK4_SNAPSHOT
+# if defined(USE_GTK3) && !defined(USE_GTK4)
     cairo_surface_t *surface;       // drawarea surface
-#  endif
-# else
+# elif !defined(USE_GTK4)
     GdkGC	*text_gc;	    // cached GC for normal text
 # endif
     PangoContext     *text_context; // the context used for all text
diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c
index 6e5dbfda1..93851af7c 100644
--- a/src/gui_gtk4.c
+++ b/src/gui_gtk4.c
@@ -30,9 +30,7 @@
 #include <gtk/gtk.h>
 #include "gui_gtk4_f.h"
 #include "gui_gtk4_cb.h"
-#ifdef USE_GTK4_SNAPSHOT
-# include "gui_gtk4_da.h"
-#endif
+#include "gui_gtk4_da.h"
 #ifdef FEAT_TOOLBAR
 # include "gui_gtk4_tb.h"
 #endif
@@ -271,9 +269,6 @@ modifiers_gdk2vim(guint state)
 static GtkWidget *vbox;		// the main vertical box
 
 // Forward declarations for event callbacks
-#ifndef USE_GTK4_SNAPSHOT
-static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer data);
-#endif
 static gboolean key_press_event(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, gpointer data);
 static void key_release_event(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, gpointer data);
 static void button_press_event(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data);
@@ -299,9 +294,6 @@ static int query_pointer_pos(int *x, int *y, GdkModifierType *state);
 static void mainwin_fullscreened_cb(GObject *obj, GParamSpec *pspec, gpointer user_data);
 static void drawarea_realize_cb(GtkWidget *widget, gpointer data);
 static void drawarea_unrealize_cb(GtkWidget *widget, gpointer data);
-#ifndef USE_GTK4_SNAPSHOT
-static cairo_surface_t *create_backing_surface(int width, int height);
-#endif
 static void scale_factor_cb(GdkSurface *surface, GParamSpec *pspec, void *udata);
 static void clipboard_changed_cb(GdkClipboard *clipboard, gpointer user_data);
 #ifdef FEAT_MENU
@@ -554,22 +546,12 @@ gui_mch_init(void)
     gtk_box_append(GTK_BOX(vbox), gui.formwin);
 
     // The drawing area for the editor content.
-#ifdef USE_GTK4_SNAPSHOT
     gui.drawarea = vim_draw_area_new();
-#else
-    gui.drawarea = gtk_drawing_area_new();
-    gui.surface = NULL;
-#endif
     gtk_widget_set_focusable(gui.drawarea, TRUE);
     gtk_widget_set_vexpand(gui.drawarea, TRUE);
     gtk_widget_set_hexpand(gui.drawarea, TRUE);
     vim_form_put(VIM_FORM(gui.formwin), gui.drawarea, 0, 0);
 
-#ifndef USE_GTK4_SNAPSHOT
-    // Set up drawing.
-    gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(gui.drawarea),
-	    (GtkDrawingAreaDrawFunc)draw_event, NULL, NULL);
-#endif
     g_signal_connect(G_OBJECT(gui.drawarea), "realize",
 		     G_CALLBACK(drawarea_realize_cb), NULL);
     g_signal_connect(G_OBJECT(gui.drawarea), "unrealize",
@@ -664,31 +646,9 @@ gui_mch_init(void)
     return OK;
 }
 
-#ifndef USE_GTK4_SNAPSHOT
-/*
- * Called when the foreground or background color has been changed.
- */
-    static void
-surface_fill_bg(void)
-{
-    if (gui.surface != NULL)
-    {
-	cairo_t *cr = cairo_create(gui.surface);
-	cairo_set_source_rgba(cr,
-		gui.bgcolor->red, gui.bgcolor->green,
-		gui.bgcolor->blue, gui.bgcolor->alpha);
-	cairo_paint(cr);
-	cairo_destroy(cr);
-    }
-}
-#endif
-
     void
 gui_mch_new_colors(void)
 {
-#ifndef USE_GTK4_SNAPSHOT
-    surface_fill_bg();
-#endif
     if (gui.drawarea != NULL && gtk_widget_get_realized(gui.drawarea))
 	gtk_widget_queue_draw(gui.drawarea);
 }
@@ -1427,7 +1387,6 @@ gui_mch_get_rgb(guicolor_T pixel)
  * ============================================================
  */
 
-#ifdef USE_GTK4_SNAPSHOT
     void
 gui_gtk4_update_size(void)
 {
@@ -1435,7 +1394,7 @@ gui_gtk4_update_size(void)
 	    gui.num_rows, gui.num_cols);
 }
 
-# ifdef FEAT_NETBEANS_INTG
+#ifdef FEAT_NETBEANS_INTG
     void
 gui_gtk4_add_multisign(
 	cairo_surface_t *surf,
@@ -1447,90 +1406,13 @@ gui_gtk4_add_multisign(
     vim_draw_area_add_multisign(VIM_DRAW_AREA(gui.drawarea), surf, row, col,
 	    width, height);
 }
-# endif
-#else // USE_GTK4_SNAPSHOT
-static void set_cairo_source_from_pixel(cairo_t *cr, guicolor_T pixel);
-
-    static void
-draw_event(GtkDrawingArea *area UNUSED, cairo_t *cr,
-	int width, int height, gpointer data UNUSED)
-{
-    // Surface creation/resizing is handled by drawarea_resize_cb.
-    // Here we only paint the surface to the widget.
-
-    // Fill background with Vim's background color
-    set_cairo_source_from_pixel(cr, gui.back_pixel);
-    cairo_rectangle(cr, 0, 0, width, height);
-    cairo_fill(cr);
-
-    // Paint the Vim surface on top
-    if (gui.surface != NULL)
-    {
-	cairo_set_source_surface(cr, gui.surface, 0, 0);
-	cairo_paint(cr);
-    }
-}
-
-    static void
-set_cairo_source_from_pixel(cairo_t *cr, guicolor_T pixel)
-{
-    cairo_set_source_rgb(cr,
-	    ((pixel & 0xff0000) >> 16) / 255.0,
-	    ((pixel & 0xff00) >> 8) / 255.0,
-	    (pixel & 0xff) / 255.0);
-}
-
-    static int
-get_drawarea_scale(void)
-{
-    int scale = 1;
-
-    if (gui.drawarea != NULL)
-	scale = gtk_widget_get_scale_factor(gui.drawarea);
-    if (scale < 1)
-	scale = 1;
-    return scale;
-}
-
-    static cairo_surface_t *
-create_backing_surface(int width, int height)
-{
-    cairo_surface_t *surf;
-    int		    scale;
-
-    if (width <= 0 || height <= 0)
-	return NULL;
-
-    scale = get_drawarea_scale();
-    surf = cairo_image_surface_create(
-	    CAIRO_FORMAT_ARGB32, width * scale, height * scale);
-    cairo_surface_set_device_scale(surf, (double)scale, (double)scale);
-    return surf;
-}
-#endif // !USE_GTK4_SNAPSHOT
+#endif
 
     void
 gui_mch_clear_block(int row1, int col1, int row2, int col2)
 {
-#ifdef USE_GTK4_SNAPSHOT
     vim_draw_area_clear_block(VIM_DRAW_AREA(gui.drawarea), row1,
 	    col1, row2, col2);
-#else
-    cairo_t *cr;
-
-    if (gui.surface == NULL)
-	return;
-
-    cr = cairo_create(gui.surface);
-    set_cairo_source_from_pixel(cr, gui.back_pixel);
-    cairo_rectangle(cr,
-	    FILL_X(col1), FILL_Y(row1),
-	    (col2 - col1 + 1) * gui.char_width,
-	    (row2 - row1 + 1) * gui.char_height);
-    cairo_fill(cr);
-    cairo_destroy(cr);
-#endif
-
     if (gui.drawarea != NULL)
 	gtk_widget_queue_draw(gui.drawarea);
 }
@@ -1538,20 +1420,7 @@ gui_mch_clear_block(int row1, int col1, int row2, int col2)
     void
 gui_mch_clear_all(void)
 {
-#ifdef USE_GTK4_SNAPSHOT
     vim_draw_area_clear(VIM_DRAW_AREA(gui.drawarea));
-#else
-    cairo_t *cr;
-
-    if (gui.surface == NULL)
-	return;
-
-    cr = cairo_create(gui.surface);
-    set_cairo_source_from_pixel(cr, gui.back_pixel);
-    cairo_paint(cr);
-    cairo_destroy(cr);
-#endif
-
     if (gui.drawarea != NULL)
 	gtk_widget_queue_draw(gui.drawarea);
 }
@@ -1687,52 +1556,12 @@ gui_mch_draw_popup_image(
 }
 #endif // FEAT_IMAGE_CAIRO
 
-#ifndef USE_GTK4_SNAPSHOT
-    static void
-surface_copy_rect(int dest_x, int dest_y,
-	int src_x, int src_y,
-	int width, int height)
-{
-    cairo_t *cr;
-    cairo_surface_t *tmp;
-
-    if (gui.surface == NULL || width <= 0 || height <= 0)
-	return;
-
-    // Use a temporary surface to avoid overlap issues
-    tmp = create_backing_surface(width, height);
-    if (tmp == NULL)
-	return;
-    cr = cairo_create(tmp);
-    cairo_set_source_surface(cr, gui.surface, -src_x, -src_y);
-    cairo_paint(cr);
-    cairo_destroy(cr);
-
-    cr = cairo_create(gui.surface);
-    cairo_set_source_surface(cr, tmp, dest_x, dest_y);
-    cairo_paint(cr);
-    cairo_destroy(cr);
-    cairo_surface_destroy(tmp);
-}
-#endif
-
     void
 gui_mch_delete_lines(int row, int num_lines)
 {
-#ifdef USE_GTK4_SNAPSHOT
     vim_draw_area_move_block(VIM_DRAW_AREA(gui.drawarea),
 	    row, row + num_lines, gui.scroll_region_bot,
 	    gui.scroll_region_left, gui.scroll_region_right);
-#else
-    int ncols = gui.scroll_region_right - gui.scroll_region_left + 1;
-    int nrows = gui.scroll_region_bot - row + 1;
-    int src_nrows = nrows - num_lines;
-
-    surface_copy_rect(
-	    FILL_X(gui.scroll_region_left), FILL_Y(row),
-	    FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
-	    gui.char_width * ncols + 1, gui.char_height * src_nrows);
-#endif
     gui_clear_block(
 	    gui.scroll_region_bot - num_lines + 1, gui.scroll_region_left,
 	    gui.scroll_region_bot, gui.scroll_region_right);
@@ -1743,20 +1572,9 @@ gui_mch_delete_lines(int row, int num_lines)
     void
 gui_mch_insert_lines(int row, int num_lines)
 {
-#ifdef USE_GTK4_SNAPSHOT
     vim_draw_area_move_block(VIM_DRAW_AREA(gui.drawarea),
 	    row + num_lines, row, gui.scroll_region_bot - num_lines,
 	    gui.scroll_region_left, gui.scroll_region_right);
-#else
-    int ncols = gui.scroll_region_right - gui.scroll_region_left + 1;
-    int nrows = gui.scroll_region_bot - row + 1;
-    int src_nrows = nrows - num_lines;
-
-    surface_copy_rect(
-	    FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
-	    FILL_X(gui.scroll_region_left), FILL_Y(row),
-	    gui.char_width * ncols + 1, gui.char_height * src_nrows);
-#endif
     gui_clear_block(
 	    row, gui.scroll_region_left,
 	    row + num_lines - 1, gui.scroll_region_right);
@@ -1764,7 +1582,6 @@ gui_mch_insert_lines(int row, int num_lines)
     gtk_widget_queue_draw(gui.drawarea);
 }
 
-#if defined(USE_GTK4_SNAPSHOT) || defined(PROTO)
 /*
  * See vim_draw_area_set_cursor() for more information
  */
@@ -1776,70 +1593,11 @@ gui_gtk4_draw_cursor(guicolor_T bg, guicolor_T fg, int w, int h)
     vim_draw_area_set_cursor(VIM_DRAW_AREA(gui.drawarea), w, h);
     gtk_widget_queue_draw(gui.drawarea);
 }
-#endif
-
-#if !defined(USE_GTK4_SNAPSHOT) || defined(PROTO)
-    void
-gui_mch_draw_hollow_cursor(guicolor_T color)
-{
-    cairo_t *cr;
-    int i = 1;
-
-    if (gui.surface == NULL)
-	return;
-
-    cr = cairo_create(gui.surface);
-    gui_mch_set_fg_color(color);
-    cairo_set_source_rgba(cr,
-	    gui.fgcolor->red, gui.fgcolor->green,
-	    gui.fgcolor->blue, gui.fgcolor->alpha);
-    if (mb_lefthalve(gui.row, gui.col))
-	i = 2;
-    cairo_set_line_width(cr, 1.0);
-    cairo_rectangle(cr,
-	    FILL_X(gui.col) + 0.5, FILL_Y(gui.row) + 0.5,
-	    i * gui.char_width - 1, gui.char_height - 1);
-    cairo_stroke(cr);
-    cairo_destroy(cr);
-
-    gtk_widget_queue_draw(gui.drawarea);
-}
-
-    void
-gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
-{
-    cairo_t *cr;
-
-    if (gui.surface == NULL)
-	return;
-
-    gui_mch_set_fg_color(color);
-    cr = cairo_create(gui.surface);
-    cairo_set_source_rgba(cr,
-	    gui.fgcolor->red, gui.fgcolor->green,
-	    gui.fgcolor->blue, gui.fgcolor->alpha);
-    cairo_rectangle(cr,
-# ifdef FEAT_RIGHTLEFT
-	    CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
-# endif
-	    FILL_X(gui.col), FILL_Y(gui.row) + gui.char_height - h,
-	    w, h);
-    cairo_fill(cr);
-    cairo_destroy(cr);
-
-    gtk_widget_queue_draw(gui.drawarea);
-}
-#endif
 
     void
 gui_mch_flash(int msec)
 {
     // Invert the screen, wait, then invert back
-#ifndef USE_GTK4_SNAPSHOT
-    if (gui.surface == NULL)
-	return;
-#endif
-
     gui_mch_invert_rectangle(0, 0, (int)Rows - 1, (int)Columns - 1);
     gui_mch_flush();
     ui_delay((long)msec, TRUE);
@@ -1849,24 +1607,7 @@ gui_mch_flash(int msec)
     void
 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
 {
-#ifdef USE_GTK4_SNAPSHOT
     vim_draw_area_invert_block(VIM_DRAW_AREA(gui.drawarea), r, c, nr, nc);
-#else
-    cairo_t *cr;
-
-    if (gui.surface == NULL)
-	return;
-
-    cr = cairo_create(gui.surface);
-    cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE);
-    cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
-    cairo_rectangle(cr,
-	    FILL_X(c), FILL_Y(r),
-	    (nc + 1) * gui.char_width, (nr + 1) * gui.char_height);
-    cairo_fill(cr);
-    cairo_destroy(cr);
-#endif
-
     gtk_widget_queue_draw(gui.drawarea);
 }
 
@@ -2318,39 +2059,15 @@ focus_out_event(GtkEventControllerFocus *controller UNUSED,
     static void
 drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
 {
-#ifndef USE_GTK4_SNAPSHOT
-    int w, h;
-
-    // Use formwin size since drawarea may not have its final size yet
-    if (gui.formwin != NULL)
-    {
-	w = gtk_widget_get_width(gui.formwin);
-	h = gtk_widget_get_height(gui.formwin);
-    }
-    else
-    {
-	w = gtk_widget_get_width(widget);
-	h = gtk_widget_get_height(widget);
-    }
-
-    if (w <= 0) w = 800;
-    if (h <= 0) h = 600;
+    // Use GdkSurface, as that handles fractional scale values.
+    GdkSurface *surface = gtk_native_get_surface(
+	    gtk_widget_get_native(gui.drawarea));
+    double old = gui.scale;
 
-    if (gui.surface != NULL)
-	cairo_surface_destroy(gui.surface);
-    gui.surface = create_backing_surface(w, h);
-#endif
-    {
-	// Use GdkSurface, as that handles fractional scale values.
-	GdkSurface *surface = gtk_native_get_surface(
-		gtk_widget_get_native(gui.drawarea));
-	double old = gui.scale;
-
-	gui.scale = gdk_surface_get_scale(surface);
-	popup_update_scale(old);
-	g_signal_connect(G_OBJECT(surface), "notify::scale",
-		G_CALLBACK(scale_factor_cb), NULL);
-    }
+    gui.scale = gdk_surface_get_scale(surface);
+    popup_update_scale(old);
+    g_signal_connect(G_OBJECT(surface), "notify::scale",
+	    G_CALLBACK(scale_factor_cb), NULL);
 
     gui_mch_new_colors();
 }
@@ -2361,98 +2078,18 @@ drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
 #ifdef FEAT_XIM
     im_shutdown();
 #endif
-#ifndef USE_GTK4_SNAPSHOT
-    if (gui.surface != NULL)
-    {
-	cairo_surface_destroy(gui.surface);
-	gui.surface = NULL;
-    }
-#endif
-}
-
-#ifndef USE_GTK4_SNAPSHOT
-    void
-gui_gtk4_resize(int width, int height)
-{
-    cairo_t *cr;
-    cairo_surface_t *old_surface;
-    int scale = get_drawarea_scale();
-
-    if (width <= 0 || height <= 0)
-	return;
-
-    // Keep the backing surface in sync with the drawing area so GTK keeps
-    // showing the previous frame. Re-creating it preserves the old
-    // contents.
-    if (gui.surface != NULL)
-    {
-	int sw = cairo_image_surface_get_width(gui.surface) / scale;
-	int sh = cairo_image_surface_get_height(gui.surface) / scale;
-	if (sw != width || sh != height)
-	{
-	    old_surface = gui.surface;
-	    gui.surface = create_backing_surface(width, height);
-	    if (gui.surface != NULL)
-	    {
-		cr = cairo_create(gui.surface);
-		set_cairo_source_from_pixel(cr, gui.back_pixel);
-		cairo_paint(cr);
-		cairo_set_source_surface(cr, old_surface, 0, 0);
-		cairo_paint(cr);
-		cairo_destroy(cr);
-	    }
-	    cairo_surface_destroy(old_surface);
-	}
-    }
-    else
-    {
-	gui.surface = create_backing_surface(width, height);
-	if (gui.surface != NULL)
-	{
-	    cr = cairo_create(gui.surface);
-	    set_cairo_source_from_pixel(cr, gui.back_pixel);
-	    cairo_paint(cr);
-	    cairo_destroy(cr);
-	}
-    }
 }
-#endif
 
     static void
 scale_factor_cb(GdkSurface  *surface,
 	GParamSpec	    *pspec UNUSED,
 	void		    *udata UNUSED)
 {
-#ifndef USE_GTK4_SNAPSHOT
-    int	w, h;
-
-    w = gtk_widget_get_width(gui.drawarea);
-    h = gtk_widget_get_height(gui.drawarea);
-    if (w <= 0 || h <= 0)
-	return;
-
-    if (gui.surface != NULL)
-	cairo_surface_destroy(gui.surface);
-    gui.surface = create_backing_surface(w, h);
-
-    if (gui.surface != NULL)
-    {
-	cairo_t *cr = cairo_create(gui.surface);
-	set_cairo_source_from_pixel(cr, gui.back_pixel);
-	cairo_paint(cr);
-	cairo_destroy(cr);
-    }
-    gtk_widget_queue_draw(gui.drawarea);
-    if (gui.in_use)
-	redraw_all_later(UPD_CLEAR);
-#endif
 #if defined(FEAT_IMAGE)
-    {
-	double old = gui.scale;
+    double old = gui.scale;
 
-	gui.scale = gdk_surface_get_scale(surface);
-	popup_update_scale(old);
-    }
+    gui.scale = gdk_surface_get_scale(surface);
+    popup_update_scale(old);
 #endif
 }
 
@@ -3454,7 +3091,6 @@ tabline_menu_press_event(
     void
 gui_mch_drawsign(int row, int col, int typenr)
 {
-# ifdef USE_GTK4_SNAPSHOT
     GdkTexture *sign;
 
     sign = (GdkTexture *)sign_get_image(typenr);
@@ -3463,42 +3099,6 @@ gui_mch_drawsign(int row, int col, int typenr)
 
     vim_draw_area_add_sign(VIM_DRAW_AREA(gui.drawarea), sign,
 	    row, col, SIGN_WIDTH, SIGN_HEIGHT);
-# else
-    GdkPixbuf	*sign;
-    cairo_t	*cr;
-    int		width, height;
-
-    sign = (GdkPixbuf *)sign_get_image(typenr);
-    if (sign == NULL || gui.surface == NULL)
-	return;
-
-    cr = cairo_create(gui.surface);
-
-    width = gdk_pixbuf_get_width(sign);
-    height = gdk_pixbuf_get_height(sign);
-
-    // Scale to fit the sign area if needed
-    if (width != SIGN_WIDTH || height != SIGN_HEIGHT)
-    {
-	GdkPixbuf *scaled = gdk_pixbuf_scale_simple(sign,
-		SIGN_WIDTH, SIGN_HEIGHT, GDK_INTERP_BILINEAR);
-	if (scaled != NULL)
-	{
-	    gdk_cairo_set_source_pixbuf(cr, scaled,
-		    FILL_X(col), FILL_Y(row));
-	    g_object_unref(scaled);
-	}
-	else
-	    gdk_cairo_set_source_pixbuf(cr, sign,
-		    FILL_X(col), FILL_Y(row));
-    }
-    else
-	gdk_cairo_set_source_pixbuf(cr, sign,
-		FILL_X(col), FILL_Y(row));
-
-    cairo_paint(cr);
-    cairo_destroy(cr);
-# endif
 
     gtk_widget_queue_draw(gui.drawarea);
 }
@@ -3508,21 +3108,13 @@ gui_mch_register_sign(char_u *signfile)
 {
     if (signfile[0] != NUL && signfile[0] != '-' && gui.in_use)
     {
-	GError *error = NULL;
-# ifdef USE_GTK4_SNAPSHOT
+	GError	    *error = NULL;
 	GdkTexture  *sign;
 
 	sign = gdk_texture_new_from_filename((const char *)signfile,
 							    &error);
 	if (sign != NULL)
 	    return sign;
-# else
-	GdkPixbuf *sign;
-
-	sign = gdk_pixbuf_new_from_file((const char *)signfile, &error);
-	if (error == NULL)
-	    return sign;
-# endif
 
 	semsg("E255: %s", error->message);
 	g_error_free(error);
@@ -3544,417 +3136,6 @@ gui_mch_destroy_sign(void *sign)
  * ============================================================
  */
 
-#ifndef USE_GTK4_SNAPSHOT
-/*
- * Ligature and text drawing support.
- * Ported from gui_gtk_x11.c (GTK3) to support 'guiligatures' in GTK4.
- */
-
-# define INSERT_PANGO_ATTR(Attribute, AttrList, Start, End)  \
-    G_STMT_START{					    \
-	PangoAttribute *tmp_attr_;			    \
-	tmp_attr_ = (Attribute);			    \
-	tmp_attr_->start_index = (Start);		    \
-	tmp_attr_->end_index = (End);			    \
-	pango_attr_list_insert((AttrList), tmp_attr_);	    \
-    }G_STMT_END
-
-/*
- * Apply the 'guifontwide' font to double-width characters in the string.
- */
-    static void
-apply_wide_font_attr(char_u *s, int len, PangoAttrList *attr_list)
-{
-    char_u  *start = NULL;
-    char_u  *p;
-    int	    uc;
-
-    for (p = s; p < s + len; p += utf_byte2len(*p))
-    {
-	uc = utf_ptr2char(p);
-
-	if (start == NULL)
-	{
-	    if (uc >= 0x80 && utf_char2cells(uc) == 2)
-		start = p;
-	}
-	else if (uc < 0x80
-		 || (utf_char2cells(uc) != 2 && !utf_iscomposing(uc)))
-	{
-	    INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font),
-			      attr_list, start - s, p - s);
-	    start = NULL;
-	}
-    }
-
-    if (start != NULL)
-	INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font),
-			  attr_list, start - s, len);
-}
-
-/*
- * Count the number of display cells occupied by a glyph cluster.
- */
-    static int
-count_cluster_cells(char_u *s, PangoItem *item,
-		    PangoGlyphString *glyphs, int i,
-		    int *cluster_width,
-		    int *last_glyph_rbearing)
-{
-    char_u  *p;
-    int	    next;
-    int	    start, end;
-    int	    width;
-    int	    uc;
-    int	    cellcount = 0;
-
-    width = glyphs->glyphs[i].geometry.width;
-
-    for (next = i + 1; next < glyphs->num_glyphs; ++next)
-    {
-	if (glyphs->glyphs[next].attr.is_cluster_start)
-	    break;
-	else if (glyphs->glyphs[next].geometry.width > width)
-	    width = glyphs->glyphs[next].geometry.width;
-    }
-
-    start = item->offset + glyphs->log_clusters[i];
-    end   = item->offset + ((next < glyphs->num_glyphs) ?
-			    glyphs->log_clusters[next] : item->length);
-
-    for (p = s + start; p < s + end; p += utf_byte2len(*p))
-    {
-	uc = utf_ptr2char(p);
-	if (uc < 0x80)
-	    ++cellcount;
-	else if (!utf_iscomposing(uc))
-	    cellcount += utf_char2cells(uc);
-    }
-
-    if (last_glyph_rbearing != NULL
-	    && cellcount > 0 && next == glyphs->num_glyphs)
-    {
-	PangoRectangle ink_rect;
-
-	pango_font_get_glyph_extents(item->analysis.font,
-				     glyphs->glyphs[i].glyph,
-				     &ink_rect, NULL);
-
-	if (PANGO_RBEARING(ink_rect) > 0)
-	    *last_glyph_rbearing = PANGO_RBEARING(ink_rect);
-    }
-
-    if (cellcount > 0)
-	*cluster_width = width;
-
-    return cellcount;
-}
-
-/*
- * Handle combining characters that form a zero-width cluster.
- */
-    static void
-setup_zero_width_cluster(PangoItem *item, PangoGlyphInfo *glyph,
-			 int last_cellcount, int last_cluster_width,
-			 int last_glyph_rbearing)
-{
-    PangoRectangle  ink_rect;
-    PangoRectangle  logical_rect;
-    int		    width;
-
-    width = last_cellcount * gui.char_width * PANGO_SCALE;
-    glyph->geometry.x_offset = -width + MAX(0, width - last_cluster_width) / 2;
-    glyph->geometry.width = 0;
-
-    pango_font_get_glyph_extents(item->analysis.font,
-				 glyph->glyph,
-				 &ink_rect, &logical_rect);
-    if (ink_rect.x < 0)
-    {
-	glyph->geometry.x_offset += last_glyph_rbearing;
-	glyph->geometry.y_offset  = logical_rect.height
-		- (gui.char_height - p_linespace) * PANGO_SCALE;
-    }
-    else
-	glyph->geometry.x_offset = -width + MAX(0, width - ink_rect.width) / 2;
-}
-
-/*
- * Draw a single glyph string segment: background, foreground, and fake bold.
- */
-    static void
-draw_glyph_string(int row, int col, int num_cells, int flags,
-		  PangoFont *font, PangoGlyphString *glyphs,
-		  cairo_t *cr)
-{
-    if (!(flags & DRAW_TRANSP))
-    {
-	cairo_set_source_rgba(cr,
-		gui.bgcolor->red, gui.bgcolor->green, gui.bgcolor->blue,
-		gui.bgcolor->alpha);
-	cairo_rectangle(cr,
-			FILL_X(col), FILL_Y(row),
-			num_cells * gui.char_width, gui.char_height);
-	cairo_fill(cr);
-    }
-
-    cairo_set_source_rgba(cr,
-	    gui.fgcolor->red, gui.fgcolor->green, gui.fgcolor->blue,
-	    gui.fgcolor->alpha);
-    cairo_move_to(cr, TEXT_X(col), TEXT_Y(row));
-    pango_cairo_show_glyph_string(cr, font, glyphs);
-
-    // Redraw with offset of 1 to emulate bold
-    if ((flags & DRAW_BOLD) && !gui.font_can_bold)
-    {
-	cairo_move_to(cr, TEXT_X(col) + 1, TEXT_Y(row));
-	pango_cairo_show_glyph_string(cr, font, glyphs);
-    }
-}
-
-/*
- * Draw underline, undercurl, and strikethrough decorations.
- */
-    static void
-draw_under(int flags, int row, int col, int cells, cairo_t *cr)
-{
-    // Draw underline
-    if (flags & DRAW_UNDERL)
-    {
-	int y = FILL_Y(row + 1) - 1;
-	cairo_set_source_rgba(cr,
-		gui.fgcolor->red, gui.fgcolor->green,
-		gui.fgcolor->blue, gui.fgcolor->alpha);
-	cairo_set_line_width(cr, 1.0);
-	cairo_move_to(cr, FILL_X(col), y + 0.5);
-	cairo_line_to(cr, FILL_X(col + cells), y + 0.5);
-	cairo_stroke(cr);
-    }
-
-    // Draw undercurl
-    if (flags & DRAW_UNDERC)
-    {
-	static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2};
-	int y = FILL_Y(row + 1) - 1;
-	int i, offset;
-
-	cairo_set_line_width(cr, 1.0);
-	cairo_set_source_rgba(cr,
-		gui.spcolor->red, gui.spcolor->green,
-		gui.spcolor->blue, gui.spcolor->alpha);
-	cairo_move_to(cr, FILL_X(col) + 1, y - 2 + 0.5);
-	for (i = FILL_X(col) + 1; i < FILL_X(col + cells); ++i)
-	{
-	    offset = val[i % 8];
-	    cairo_line_to(cr, i, y - offset + 0.5);
-	}
-	cairo_stroke(cr);
-    }
-
-    // Draw strikethrough
-    if (flags & DRAW_STRIKE)
-    {
-	int y = FILL_Y(row) + gui.char_height / 2;
-	cairo_set_source_rgba(cr,
-		gui.fgcolor->red, gui.fgcolor->green,
-		gui.fgcolor->blue, gui.fgcolor->alpha);
-	cairo_set_line_width(cr, 1.0);
-	cairo_move_to(cr, FILL_X(col), y + 0.5);
-	cairo_line_to(cr, FILL_X(col + cells), y + 0.5);
-	cairo_stroke(cr);
-    }
-}
-
-/*
- * Draw a string of characters on the screen.
- * "force_pango" is set when ligature characters require Pango shaping
- * instead of the fast ASCII glyph cache path.
- * Returns the number of display cells used.
- */
-    int
-gui_gtk_draw_string_ext(
-	int	row,
-	int	col,
-	char_u	*s,
-	int	len,
-	int	flags,
-	int	force_pango)
-{
-    PangoGlyphString	*glyphs;
-    int			column_offset = 0;
-    int			i;
-    GdkRectangle	area;
-    cairo_t		*cr;
-
-    if (gui.text_context == NULL || gui.surface == NULL)
-	return len;
-
-    // Restrict all drawing to the current screen line.
-    area.x = gui.border_offset;
-    area.y = FILL_Y(row);
-    area.width	= gui.num_cols * gui.char_width;
-    area.height = gui.char_height;
-
-    cr = cairo_create(gui.surface);
-    cairo_rectangle(cr, area.x, area.y, area.width, area.height);
-    cairo_clip(cr);
-
-    glyphs = pango_glyph_string_new();
-
-    // Fast path for pure ASCII: use cached glyph table.
-    // Skip this path when force_pango is set (ligatures need shaping).
-    if (!(flags & DRAW_ITALIC)
-	    && !((flags & DRAW_BOLD) && gui.font_can_bold)
-	    && gui.ascii_glyphs != NULL
-	    && !force_pango)
-    {
-	char_u *p;
-
-	for (p = s; p < s + len; ++p)
-	    if (*p & 0x80)
-		goto not_ascii;
-
-	pango_glyph_string_set_size(glyphs, len);
-
-	for (i = 0; i < len; ++i)
-	{
-	    glyphs->glyphs[i] = gui.ascii_glyphs->glyphs[2 * s[i]];
-	    glyphs->log_clusters[i] = i;
-	}
-
-	draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs, cr);
-
-	column_offset = len;
-    }
-    else
-    {
-not_ascii:;
-	PangoAttrList	*attr_list;
-	GList		*item_list;
-	int		cluster_width;
-	int		last_glyph_rbearing;
-	int		cells = 0;
-
-	// Safety check: pango crashes with invalid utf-8.
-	if (!utf_valid_string(s, s + len))
-	{
-	    column_offset = len;
-	    goto skipitall;
-	}
-
-	cluster_width = PANGO_SCALE * gui.char_width;
-	last_glyph_rbearing = PANGO_SCALE * gui.char_width;
-
-	attr_list = pango_attr_list_new();
-
-	// If 'guifontwide' is set then use that for double-width characters.
-	if (gui.wide_font != NULL)
-	    apply_wide_font_attr(s, len, attr_list);
-
-	if ((flags & DRAW_BOLD) && gui.font_can_bold)
-	    INSERT_PANGO_ATTR(pango_attr_weight_new(PANGO_WEIGHT_BOLD),
-			      attr_list, 0, len);
-	if (flags & DRAW_ITALIC)
-	    INSERT_PANGO_ATTR(pango_attr_style_new(PANGO_STYLE_ITALIC),
-			      attr_list, 0, len);
-
-	item_list = pango_itemize(gui.text_context,
-				  (const char *)s, 0, len, attr_list, NULL);
-
-	while (item_list != NULL)
-	{
-	    PangoItem	*item;
-	    int		item_cells = 0;
-
-	    item = (PangoItem *)item_list->data;
-	    item_list = g_list_delete_link(item_list, item_list);
-
-	    // Force LTR direction; Vim handles bidi on its own.
-	    item->analysis.level = (item->analysis.level + 1) & (~1U);
-
-	    pango_shape_full((const char *)s + item->offset, item->length,
-		    (const char *)s, len, &item->analysis, glyphs);
-
-	    // Fixed-width hack: assign a fixed width to each glyph based on
-	    // the number of cells it occupies, handling composing characters
-	    // and cluster boundaries properly.
-	    for (i = 0; i < glyphs->num_glyphs; ++i)
-	    {
-		PangoGlyphInfo *glyph;
-
-		glyph = &glyphs->glyphs[i];
-
-		if (glyph->attr.is_cluster_start)
-		{
-		    int cellcount;
-
-		    cellcount = count_cluster_cells(
-			    s, item, glyphs, i, &cluster_width,
-			    (item_list != NULL) ? &last_glyph_rbearing : NULL);
-
-		    if (cellcount > 0)
-		    {
-			int width;
-
-			width = cellcount * gui.char_width * PANGO_SCALE;
-			glyph->geometry.x_offset +=
-					    MAX(0, width - cluster_width) / 2;
-			glyph->geometry.width = width;
-		    }
-		    else
-		    {
-			setup_zero_width_cluster(item, glyph, cells,
-						 cluster_width,
-						 last_glyph_rbearing);
-		    }
-
-		    item_cells += cellcount;
-		    cells = cellcount;
-		}
-		else if (i > 0)
-		{
-		    int width;
-
-		    if (glyph->geometry.x_offset >= 0)
-		    {
-			glyphs->glyphs[i].geometry.width =
-					 glyphs->glyphs[i - 1].geometry.width;
-			glyphs->glyphs[i - 1].geometry.width = 0;
-		    }
-		    width = cells * gui.char_width * PANGO_SCALE;
-		    glyph->geometry.x_offset +=
-					    MAX(0, width - cluster_width) / 2;
-		}
-		else
-		{
-		    glyph->geometry.width = 0;
-		}
-	    }
-
-	    draw_glyph_string(row, col + column_offset, item_cells,
-		    flags, item->analysis.font, glyphs, cr);
-
-	    pango_item_free(item);
-
-	    column_offset += item_cells;
-	}
-
-	pango_attr_list_unref(attr_list);
-    }
-
-skipitall:
-    draw_under(flags, row, col, column_offset, cr);
-    cairo_destroy(cr);
-
-    pango_glyph_string_free(glyphs);
-
-    if (gui.drawarea != NULL)
-	gtk_widget_queue_draw(gui.drawarea);
-
-    return column_offset;
-}
-#endif // !USE_GTK4_SNAPSHOT
-
 /*
  * Draw a string of characters on the screen using the current font and colors.
  * Splits the string into ASCII and ligature/UTF-8 segments so that ligature
@@ -3965,7 +3146,6 @@ skipitall:
     int
 gui_gtk_draw_string(int row, int col, char_u *s, int len, int flags)
 {
-#ifdef USE_GTK4_SNAPSHOT
     int cells;
 
     cells = vim_draw_area_draw_string(VIM_DRAW_AREA(gui.drawarea),
@@ -3975,120 +3155,6 @@ gui_gtk_draw_string(int row, int col, char_u *s, int len, int flags)
 	gtk_widget_queue_draw(gui.drawarea);
 
     return cells;
-#else
-    char_u	*conv_buf = NULL;
-    int		convlen;
-    int		len_sum;
-    int		byte_sum;
-    char_u	*cs;
-    int		needs_pango;
-    int		should_need_pango = FALSE;
-    int		slen;
-    int		is_ligature;
-    int		is_utf8;
-    char_u	backup_ch;
-
-    if (gui.text_context == NULL || gui.surface == NULL)
-	return len;
-
-    if (output_conv.vc_type != CONV_NONE)
-    {
-	convlen = len;
-	conv_buf = string_convert(&output_conv, s, &convlen);
-	if (conv_buf != NULL)
-	{
-	    s = conv_buf;
-	    len = convlen;
-	}
-    }
-
-    /*
-     * Ligature support:
-     * Split the string into segments that are either pure ASCII (fast path)
-     * or ligature/UTF-8 (Pango path).  A single ligature character between
-     * ASCII characters is treated as ASCII since it can't form a ligature
-     * on its own.
-     */
-    len_sum = 0;
-    byte_sum = 0;
-    cs = s;
-
-    // First char decides starting mode.
-    is_utf8 = (*cs & 0x80);
-    is_ligature = gui.ligatures_map[*cs] && (len > 1);
-    if (is_ligature)
-	is_ligature = gui.ligatures_map[*(cs + 1)];
-    if (!is_utf8 && len > 1)
-	is_utf8 = (*(cs + 1) & 0x80) != 0;
-    needs_pango = is_utf8 || is_ligature;
-
-    while (cs < s + len)
-    {
-	slen = 0;
-	while (slen < (len - byte_sum))
-	{
-	    is_ligature = gui.ligatures_map[*(cs + slen)];
-	    // Look ahead: single ligature char between ASCII is ASCII.
-	    if (is_ligature && !needs_pango)
-	    {
-		if ((slen + 1) < (len - byte_sum))
-		    is_ligature = gui.ligatures_map[*(cs + slen + 1)];
-		else
-		    is_ligature = 0;
-	    }
-	    is_utf8 = *(cs + slen) & 0x80;
-	    // ASCII followed by UTF-8 could be combining.
-	    if ((!is_utf8) && ((slen + 1) < (len - byte_sum)))
-		is_utf8 = (*(cs + slen + 1) & 0x80);
-	    should_need_pango = (is_ligature || is_utf8);
-	    if (needs_pango != should_need_pango)
-		break;
-	    if (needs_pango)
-	    {
-		if (is_ligature)
-		{
-		    slen++;
-		}
-		else
-		{
-		    if ((*(cs + slen) & 0xC0) == 0x80)
-		    {
-			while ((slen < (len - byte_sum))
-					    && ((*(cs + slen) & 0xC0) == 0x80))
-			    slen++;
-		    }
-		    else if ((*(cs + slen) & 0xE0) == 0xC0)
-			slen++;
-		    else if ((*(cs + slen) & 0xF0) == 0xE0)
-			slen += 2;
-		    else if ((*(cs + slen) & 0xF8) == 0xF0)
-			slen += 3;
-		    else
-			slen++;
-		}
-	    }
-	    else
-	    {
-		slen++;
-	    }
-	}
-
-	if (slen < len)
-	{
-	    backup_ch = *(cs + slen);
-	    *(cs + slen) = NUL;
-	}
-	len_sum += gui_gtk_draw_string_ext(row, col + len_sum, cs, slen,
-					    flags, needs_pango);
-	if (slen < len)
-	    *(cs + slen) = backup_ch;
-	cs += slen;
-	byte_sum += slen;
-	needs_pango = should_need_pango;
-    }
-    vim_free(conv_buf);
-    return len_sum;
-#endif
 }
 
     int
@@ -5012,7 +4078,6 @@ gui_mch_set_text_area_pos(int x, int y, int w, int h)
     vim_form_move_resize(VIM_FORM(gui.formwin), gui.drawarea, x, y, w, h);
 }
 
-#ifdef USE_GTK4_SNAPSHOT
 /*
  * Calculate the number of pixels to bleed background color to. Should be called
  * after all UI elements are positioned and resized.
@@ -5043,7 +4108,6 @@ gui_gtk4_calculate_bleed(int width, int height)
     if (old_right != gui.bleed_right || old_bot != gui.bleed_bot)
 	gtk_widget_queue_draw(GTK_WIDGET(gui.drawarea));
 }
-#endif
 
 /*
  * ============================================================
diff --git a/src/gui_gtk4_da.c b/src/gui_gtk4_da.c
index 7a082cedf..80d3044cb 100644
--- a/src/gui_gtk4_da.c
+++ b/src/gui_gtk4_da.c
@@ -9,8 +9,6 @@
 
 #include "vim.h"
 
-#ifdef USE_GTK4_SNAPSHOT
-
 #include <gtk/gtk.h>
 #include "gui_gtk4_da.h"
 
@@ -2024,5 +2022,3 @@ vim_draw_area_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
     }
 #endif
 }
-
-#endif // USE_GTK4_SNAPSHOT
diff --git a/src/gui_gtk4_da.h b/src/gui_gtk4_da.h
index a214729a8..5708849f9 100644
--- a/src/gui_gtk4_da.h
+++ b/src/gui_gtk4_da.h
@@ -12,11 +12,9 @@
 
 #include "vim.h"
 
-#ifdef USE_GTK4_SNAPSHOT
+#include <gtk/gtk.h>
 
-# include <gtk/gtk.h>
-
-# define VIM_TYPE_DRAW_AREA (vim_draw_area_get_type())
+#define VIM_TYPE_DRAW_AREA (vim_draw_area_get_type())
 G_DECLARE_FINAL_TYPE(VimDrawArea, vim_draw_area, VIM, DRAW_AREA, GtkWidget)
 
 GtkWidget *vim_draw_area_new(void);
@@ -27,17 +25,15 @@ void vim_draw_area_clear(VimDrawArea *self);
 void vim_draw_area_move_block(VimDrawArea *self, int to, int row1, int row2, int col1, int col2);
 void vim_draw_area_set_cursor(VimDrawArea *self, int w, int h);
 void vim_draw_area_invert_block(VimDrawArea *self, int row, int col, int nrows, int ncols);
-# if defined(FEAT_SIGN_ICONS)
+#if defined(FEAT_SIGN_ICONS)
 void vim_draw_area_add_sign(VimDrawArea *self, GdkTexture *sign, int row, int col, int width, int height);
-# endif
-# ifdef FEAT_NETBEANS_INTG
+#endif
+#ifdef FEAT_NETBEANS_INTG
 void vim_draw_area_add_multisign(VimDrawArea *self, cairo_surface_t *surf, int row, int col, int width, int height);
-# endif
-# ifdef FEAT_IMAGE_GDK
+#endif
+#ifdef FEAT_IMAGE_GDK
 void vim_draw_area_add_image(VimDrawArea *self, GdkTexture *image, int row, int col, double src_x, double src_y, double draw_w, double draw_h, int zindex, int id);
 void vim_draw_area_remove_image(VimDrawArea *self, int id);
-# endif
-
 #endif
 
 #endif
diff --git a/src/netbeans.c b/src/netbeans.c
index abe5d11bc..9aec66490 100644
--- a/src/netbeans.c
+++ b/src/netbeans.c
@@ -3100,7 +3100,7 @@ netbeans_draw_multisign_indicator(int row)
 # else
     GdkDrawable *drawable = gui.drawarea->window;
 # endif
-# ifdef USE_GTK4_SNAPSHOT
+# ifdef USE_GTK4
     cairo_surface_t *surf;
 # endif
 
@@ -3108,14 +3108,14 @@ netbeans_draw_multisign_indicator(int row)
 	return;
 
     x = 0;
-# ifdef USE_GTK4_SNAPSHOT
+# ifdef USE_GTK4
     y = 2;
 # else
     y = row * gui.char_height + 2;
 # endif
 
 # if GTK_CHECK_VERSION(3,0,0)
-#  ifdef USE_GTK4_SNAPSHOT
+#  ifdef USE_GTK4
     surf = cairo_image_surface_create(
 	    CAIRO_FORMAT_ARGB32, 5, gui.char_height);
     cr = cairo_create(surf);
@@ -3152,7 +3152,7 @@ netbeans_draw_multisign_indicator(int row)
     gdk_draw_point(drawable, gui.text_gc, x+2, y);
 # endif
 
-# ifdef USE_GTK4_SNAPSHOT
+# ifdef USE_GTK4
     cairo_fill(cr);
     gui_gtk4_add_multisign(surf, row, 0, 5, gui.char_height);
 # endif
@@ -3160,7 +3160,7 @@ netbeans_draw_multisign_indicator(int row)
 # if GTK_CHECK_VERSION(3,0,0)
     cairo_destroy(cr);
 # endif
-# ifdef USE_GTK4_SNAPSHOT
+# ifdef USE_GTK4
     cairo_surface_destroy(surf);
 # endif
 }
diff --git a/src/po/vim.pot b/src/po/vim.pot
index 358f4bb61..b6ded2132 100644
--- a/src/po/vim.pot
+++ b/src/po/vim.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Vim
"
 "Report-Msgid-Bugs-To: [email protected]
"
-"POT-Creation-Date: 2026-07-23 20:13+0000
"
+"POT-Creation-Date: 2026-07-27 20:48+0000
"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE
"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>
"
 "Language-Team: LANGUAGE <[email protected]>
"
@@ -3469,9 +3469,6 @@ msgstr ""
 msgid "without GUI."
 msgstr ""
 
-msgid "with GTK4 GUI (hwaccel)."
-msgstr ""
-
 msgid "with GTK4 GUI."
 msgstr ""
 
diff --git a/src/proto/gui_gtk4.pro b/src/proto/gui_gtk4.pro
index 1541e4564..3c5f7a925 100644
--- a/src/proto/gui_gtk4.pro
+++ b/src/proto/gui_gtk4.pro
@@ -49,11 +49,8 @@ void gui_mch_draw_popup_image(win_T *wp, int row, int col, int src_x, int src_y,
 void gui_mch_delete_lines(int row, int num_lines);
 void gui_mch_insert_lines(int row, int num_lines);
 void gui_gtk4_draw_cursor(guicolor_T bg, guicolor_T fg, int w, int h);
-void gui_mch_draw_hollow_cursor(guicolor_T color);
-void gui_mch_draw_part_cursor(int w, int h, guicolor_T color);
 void gui_mch_flash(int msec);
 void gui_mch_invert_rectangle(int r, int c, int nr, int nc);
-void gui_gtk4_resize(int width, int height);
 void gui_gtk_set_dnd_targets(void);
 void gui_mch_update(void);
 int gui_mch_wait_for_chars(long wtime);
@@ -78,7 +75,6 @@ void gui_mch_set_curtab(int nr);
 void gui_mch_drawsign(int row, int col, int typenr);
 void *gui_mch_register_sign(char_u *signfile);
 void gui_mch_destroy_sign(void *sign);
-int gui_gtk_draw_string_ext(int row, int col, char_u *s, int len, int flags, int force_pango);
 int gui_gtk_draw_string(int row, int col, char_u *s, int len, int flags);
 int gui_get_x11_windis(Window *win, Display **dis);
 void gui_gtk_set_mnemonics(int enable);
diff --git a/src/version.c b/src/version.c
index 692175247..3f239da85 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    865,
 /**/
     864,
 /**/
@@ -2835,11 +2837,7 @@ list_version(void)
     msg_puts(_("without GUI."));
 #elif defined(FEAT_GUI_GTK)
 # if defined(USE_GTK4)
-#  ifdef USE_GTK4_SNAPSHOT
-    msg_puts(_("with GTK4 GUI (hwaccel)."));
-#  else
     msg_puts(_("with GTK4 GUI."));
-#  endif
 # elif defined(USE_GTK3)
     msg_puts(_("with GTK3 GUI."));
 # elif defined(FEAT_GUI_GNOME)

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups "vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1woSQf-00HEGs-EI%40256bit.org.