patch 9.2.0931: the GTK4 GUI is still experimental and untested by CI
Commit:
https://github.com/vim/vim/commit/1b7bd58115a5e7b788d01c51b90025ce58246c67
Author: Foxe Chen <
chen...@gmail.com>
Date: Mon Aug 10 20:08:09 2026 +0000
patch 9.2.0931: the GTK4 GUI is still experimental and untested by CI
Problem: the GTK4 GUI is still experimental and untested by CI
Solution: Stabilize the GTK4 code, Fix a few minor issues, include a
GTK4 CI job (Foxe Chen).
closes: #20920
Signed-off-by: Foxe Chen <
chen...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/.github/actions/build_vim_on_linux/action.yml b/.github/actions/build_vim_on_linux/action.yml
index c6486cd34..6006f5ec8 100644
--- a/.github/actions/build_vim_on_linux/action.yml
+++ b/.github/actions/build_vim_on_linux/action.yml
@@ -88,7 +88,6 @@ runs:
sudo cp ci/pinned-pkgs /etc/apt/preferences.d/pinned-pkgs
echo '::endgroup::'
- # TODO: switch to GTK4 GUI
- name: Install packages
shell: bash
run: |
@@ -103,6 +102,8 @@ runs:
wayland-utils \
libgtk-3-dev:${{ inputs.architecture }} \
libgtk-3-bin:${{ inputs.architecture }} \
+ libgtk-4-dev:${{ inputs.architecture }} \
+ libgtk-4-bin:${{ inputs.architecture }} \
desktop-file-utils \
libc6-dbgsym:${{ inputs.architecture }} \
libtool-bin \
@@ -125,6 +126,8 @@ runs:
libxcb1-dbgsym:${{ inputs.architecture }} \
libgtk-3-bin-dbgsym:${{ inputs.architecture }} \
libgtk-3-0t64-dbgsym:${{ inputs.architecture }} \
+ libgtk-4-bin-dbgsym:${{ inputs.architecture }} \
+ libgtk-4-0t64-dbgsym:${{ inputs.architecture }} \
libglib2.0-0t64-dbgsym:${{ inputs.architecture }} \
libglib2.0-bin-dbgsym:${{ inputs.architecture }} \
libglib2.0-dev-bin-dbgsym:${{ inputs.architecture }} \
@@ -233,6 +236,9 @@ runs:
if ${{ contains(fromJSON(inputs.extra), 'testgui') }}; then
echo "TEST=-C src testgui"
fi
+ if ${{ contains(fromJSON(inputs.extra), 'use_gtk4') }}; then
+ CONFOPT="${CONFOPT} --enable-gui=gtk4"
+ fi
if ${{ contains(fromJSON(inputs.extra), 'unittests') }}; then
echo "TEST=unittests"
fi
@@ -266,6 +272,9 @@ runs:
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
sudo usermod -a -G audio "${USER}"
sudo bash ci/setup-xvfb.sh
+ if ${{ contains(fromJSON(inputs.extra), 'use_gtk4') }}; then
+ sudo bash ci/setup-weston.sh
+ fi
echo '::endgroup::'
- name: Check autoconf
diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml
index 015d9d698..e91a267ac 100644
--- a/.github/workflows/ci-linux.yml
+++ b/.github/workflows/ci-linux.yml
@@ -27,6 +27,8 @@ jobs:
LOG_DIR: ${{ github.workspace }}/logs
TERM: xterm
DISPLAY: ":99"
+ WAYLAND_DISPLAY: "/tmp/weston_sock"
+ GTK_A11Y: "none"
DEBIAN_FRONTEND: noninteractive
strategy:
@@ -65,6 +67,11 @@ jobs:
coverage: true
interface: dynamic
extra: [uchar, testgui]
+ - features: huge
+ compiler: gcc
+ coverage: true
+ interface: dynamic
+ extra: [uchar, testgui, use_gtk4]
- features: huge
compiler: gcc
coverage: true
@@ -100,7 +107,7 @@ jobs:
coverage: ${{ matrix.coverage }}
- name: Test
- timeout-minutes: 20
+ timeout-minutes: 30
run: make ${SHADOWOPT} ${TEST}
# Enable to debug failing tests live and ssh into the CI runners
diff --git a/.github/workflows/ci-linux_asan.yml b/.github/workflows/ci-linux_asan.yml
index 45f69b1e7..cbba1c05a 100644
--- a/.github/workflows/ci-linux_asan.yml
+++ b/.github/workflows/ci-linux_asan.yml
@@ -25,6 +25,8 @@ jobs:
LOG_DIR: ${{ github.workspace }}/logs
TERM: xterm
DISPLAY: ":99"
+ WAYLAND_DISPLAY: "/tmp/weston_sock"
+ GTK_A11Y: "none"
DEBIAN_FRONTEND: noninteractive
strategy:
diff --git a/Filelist b/Filelist
index ca9e8a7b9..30971bacd 100644
--- a/Filelist
+++ b/Filelist
@@ -31,6 +31,7 @@ SRC_ALL = \
ci/if_feat_check.vim \
ci/lychee.toml \
ci/setup-xvfb.sh \
+ ci/setup-weston.sh \
ci/remove_snap.sh \
ci/ddebs.list \
ci/pinned-pkgs \
diff --git a/ci/setup-weston.sh b/ci/setup-weston.sh
new file mode 100644
index 000000000..f9f7d4a53
--- /dev/null
+++ b/ci/setup-weston.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+set -e
+
+apt-get install -y weston
+
+cat <<EOT >/etc/systemd/system/weston.service
+[Unit]
+Description=Weston Compositor Service
+After=network.target
+[Service]
+ExecStart=/usr/bin/weston --backend=headless --fake-seat --width=5000 --height=5000 --socket=/tmp/weston_sock
+[Install]
+WantedBy=multi-user.target
+EOT
+
+systemctl enable weston.service
+systemctl start weston.service
diff --git a/runtime/doc/gui_x11.txt b/runtime/doc/gui_x11.txt
index 3bb469cec..f199801ee 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 Jul 27
+*gui_x11.txt* For Vim version 9.2. Last change: 2026 Aug 10
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -304,7 +304,7 @@ If a name is used that exists on other systems, but not in X11, the default
"arrow" pointer is used.
==============================================================================
-5. GTK version *gui-gtk* *GTK+* *GTK* *GTK3*
+5. GTK version *gui-gtk* *GTK+* *GTK* *GTK3* *GTK4*
The GTK version of the GUI works a little bit different.
@@ -680,7 +680,11 @@ X11R5 with a library for X11R6 probably doesn't work (although the linking
won't give an error message, Vim will crash later).
*gui-wayland*
-Support for the Wayland display server protocol has landed in patch 9.1.0064.
+Support for the Wayland display server protocol has landed in patch 9.1.0064,
+for the GTK3 GUI. It is recommended to use the GTK4 GUI if you want a better
+Wayland experience however, as the GTK3 GUI is heavily built around X11
+specific features and API calls.
+
Note: The Wayland protocol is subject to some restrictions, so the following
functions won't work: |getwinpos()|, |getwinposx()|, |getwinposy()| and the
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 521446ada..869cc9f98 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt* For Vim version 9.2. Last change: 2026 Aug 04
+*options.txt* For Vim version 9.2. Last change: 2026 Aug 10
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -4494,7 +4494,7 @@ A jump table for the options with a short description can be found at |Q_op|.
*'guiheadroom'* *'ghr'*
'guiheadroom' 'ghr' number (default 50)
global
- {only for GTK and X11 GUI}
+ {only for GTK and X11 GUI, except GTK4 GUI}
The number of pixels subtracted from the screen height when fitting
the GUI window on the screen. Set this before the GUI is started,
e.g., in your |gvimrc| file. When zero, the whole screen height will
@@ -5030,6 +5030,9 @@ A jump table for the options with a short description can be found at |Q_op|.
< "S-space" means shift+space. This is the activation key for kinput2 +
canna (Japanese), and ami (Korean).
+ Note that when using the GTK4 GUI, this option is not supported. The
+ only valid value for this option will be an empty string.
+
*'imcmdline'* *'imc'* *'noimcmdline'* *'noimc'*
'imcmdline' 'imc' boolean (default off)
global
diff --git a/runtime/doc/tags b/runtime/doc/tags
index afb7a3626..92279726b 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -5740,6 +5740,7 @@ GNOME gui_x11.txt /*GNOME*
GTK gui_x11.txt /*GTK*
GTK+ gui_x11.txt /*GTK+*
GTK3 gui_x11.txt /*GTK3*
+GTK4 gui_x11.txt /*GTK4*
GUI gui.txt /*GUI*
GUI-X11 gui_x11.txt /*GUI-X11*
GUIEnter autocmd.txt /*GUIEnter*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 330b552db..ef371b569 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -52587,10 +52587,11 @@ work in progress.
GUI ~
-----
-- Added support for the GTK 4 GUI (via patch 9.2.0501). Enable with
- `--enable-gui=gtk4` configure switch at build time.
+- Added support for the GTK 4 GUI (via patch 9.2.0501), stablized as of
+ patch 9.2.0931. Enable with `--enable-gui=gtk4` configure switch at build
+ time.
- Add support for fullscreen mode in the GTK UI |'go-s'|.
-- Enable native printing diaglog for the GTK4 UI using pango library, see
+- Enable native printing dialog for the GTK4 UI using pango library, see
|pango-printing|.
Popups ~
diff --git a/src/Makefile b/src/Makefile
index 04917c064..98baecf6f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1924,7 +1924,6 @@ PROTO_FILES = \
proto/
gc.pro \
proto/
getchar.pro \
proto/
gui_beval.pro \
- proto/
gui_gtk_gresources.pro \
proto/
gui_xim.pro \
proto/
hardcopy.pro \
proto/
hardcopy_pango.pro \
@@ -2025,6 +2024,7 @@ PROTO_FILES = \
proto/
wayland.pro \
proto/
winclip.pro \
proto/
window.pro \
+ $(GRESOURCE_PRO) \
$(ALL_GUI_PRO)
# Default target is making the executable and tools
@@ -4546,7 +4546,7 @@ objects/gui_gtk4.o: auto/osdef.h gui_gtk4.c vim.h protodef.h auto/config.h featu
ascii.h keymap.h termdefs.h macros.h option.h beval.h \
structs.h regexp.h gui.h libvterm/include/vterm.h \
libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
- globals.h errors.h gui_gtk4_f.h auto/gui_gtk_gresources.h \
+ globals.h errors.h gui_gtk4_f.h \
gui_gtk4_cb.h gui_gtk4_da.h gui_gtk4_tb.h gui_gtk4_menu.h
objects/gui_gtk4_f.o: auto/osdef.h gui_gtk4_f.c vim.h protodef.h auto/config.h feature.h \
os_unix.h ascii.h keymap.h termdefs.h macros.h option.h \
diff --git a/src/auto/configure b/src/auto/configure
index 312ab6f6c..4e92948f4 100755
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -664,6 +664,7 @@ PANGO_CPPFLAGS
PANGO_CFLAGS
PANGO_LIBS
GRESOURCE_OBJ
+GRESOURCE_PRO
GRESOURCE_SRC
UPDATE_DESKTOP_DATABASE
GTK_UPDATE_ICON_CACHE
@@ -11388,6 +11389,7 @@ printf "%s
" "usable." >&6; }
printf "%s
" "#define USE_GRESOURCE 1" >>confdefs.h
GRESOURCE_SRC="auto/gui_gtk_gresources.c"
+ GRESOURCE_PRO="proto/
gui_gtk_gresources.pro"
GRESOURCE_OBJ="objects/gui_gtk_gresources.o"
fi
else
@@ -11545,6 +11547,7 @@ fi
+
{ printf "%s
" "$as_me:${as_lineno-$LINENO}: checking --enable-hardcopy-pango argument" >&5
printf %s "checking --enable-hardcopy-pango argument... " >&6; }
# Check whether --enable-hardcopy-pango was given.
diff --git a/src/
config.mk.in b/src/
config.mk.in
index 06a77aed6..76e5a2ff7 100644
--- a/src/
config.mk.in
+++ b/src/
config.mk.in
@@ -176,6 +176,7 @@ GTK_LIBNAME = @GTK_LIBNAME@
GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
GRESOURCE_SRC = @GRESOURCE_SRC@
+GRESOURCE_PRO = @GRESOURCE_PRO@
GRESOURCE_OBJ = @GRESOURCE_OBJ@
GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
diff --git a/src/
configure.ac b/src/
configure.ac
index 75035d754..a3d06d0e8 100644
--- a/src/
configure.ac
+++ b/src/
configure.ac
@@ -3112,6 +3112,7 @@ if test "x$GUITYPE" = "xGTK"; then
AC_MSG_RESULT([usable.])
AC_DEFINE(USE_GRESOURCE)
GRESOURCE_SRC="auto/gui_gtk_gresources.c"
+ GRESOURCE_PRO="proto/
gui_gtk_gresources.pro"
GRESOURCE_OBJ="objects/gui_gtk_gresources.o"
fi
else
@@ -3153,6 +3154,7 @@ if test "x$GUITYPE" = "xGTK"; then
fi
AC_SUBST(GLIB_COMPILE_RESOURCES)
AC_SUBST(GRESOURCE_SRC)
+AC_SUBST(GRESOURCE_PRO)
AC_SUBST(GRESOURCE_OBJ)
AC_SUBST(GTK_UPDATE_ICON_CACHE)
AC_SUBST(UPDATE_DESKTOP_DATABASE)
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 74ca2716e..23dc21e95 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -7186,7 +7186,7 @@ f_has(typval_T *argvars, typval_T *rettv)
#endif
},
{"gui_gtk2",
-#if defined(FEAT_GUI_GTK) && !defined(USE_GTK3)
+#if defined(FEAT_GUI_GTK) && !defined(USE_GTK3) && !defined(USE_GTK4)
1
#else
0
diff --git a/src/gui.h b/src/gui.h
index 653937cfe..5be8d0fd9 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -15,15 +15,7 @@
# ifdef VMS
# include "gui_gtk_vms.h"
# endif
-# ifdef USE_GTK4
-// Types used in proto files but not available without X11 headers
-typedef void *Widget;
-typedef void *XtAppContext;
-typedef void Display;
-typedef unsigned long Window;
-typedef unsigned long Atom;
-typedef GdkEvent GdkEventKey; // GTK4: GdkEventKey merged into GdkEvent
-# else
+# ifndef USE_GTK4
# include <X11/Intrinsic.h>
# endif
# pragma GCC diagnostic push
@@ -33,6 +25,15 @@ typedef GdkEvent GdkEventKey; // GTK4: GdkEventKey merged into GdkEvent
# endif
# include <gtk/gtk.h>
# pragma GCC diagnostic pop
+# ifdef USE_GTK4
+// Types used in proto files but not available without X11 headers
+typedef void *Widget;
+typedef void *XtAppContext;
+typedef void Display;
+typedef unsigned long Window;
+typedef unsigned long Atom;
+typedef GdkEvent GdkEventKey; // GTK4: GdkEventKey merged into GdkEvent
+# endif
#endif
#ifdef FEAT_GUI_HAIKU
diff --git a/src/gui_beval.c b/src/gui_beval.c
index c7357fb30..4e86bca8d 100644
--- a/src/gui_beval.c
+++ b/src/gui_beval.c
@@ -169,6 +169,7 @@ gui_mch_destroy_beval_area(BalloonEval *beval)
if (beval->vts)
vim_free(beval->vts);
# endif
+ vim_free(beval->msg);
vim_free(beval);
}
# endif
@@ -303,7 +304,7 @@ removeEventHandler(BalloonEval *beval)
# ifdef USE_GTK4
static gboolean
-event_cb(GtkEventController *controller, GdkEvent *event, BalloonEval *beval)
+event_cb(GtkEventController *controller UNUSED, GdkEvent *event, BalloonEval *beval)
{
int type = gdk_event_get_event_type(event);
diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c
index 40fa89d89..c2f2564e6 100644
--- a/src/gui_gtk4.c
+++ b/src/gui_gtk4.c
@@ -258,7 +258,9 @@ 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);
+#if defined(FEAT_IMAGE)
static void scale_factor_cb(GdkSurface *surface, GParamSpec *pspec, void *udata);
+#endif
static void clipboard_changed_cb(GdkClipboard *clipboard, gpointer user_data);
#ifdef FEAT_MENU
static void show_menubar_popover(void);
@@ -732,6 +734,7 @@ gui_mch_open(void)
guicolor_T bg_pixel = INVALCOLOR;
guint pixel_width;
guint pixel_height;
+ long columns = Columns, rows = Rows;
if (gui.geom != NULL)
{
@@ -741,12 +744,12 @@ gui_mch_open(void)
mask = vim_parse_geometry((char *)gui.geom, &w, &h);
if (mask & WidthValue)
- Columns = w;
+ columns = Columns = w;
if (mask & HeightValue)
{
if (p_window > (long)h - 1 || !option_was_set((char_u *)"window"))
p_window = h - 1;
- Rows = h;
+ rows = Rows = h;
}
limit_screen_size();
@@ -763,6 +766,12 @@ gui_mch_open(void)
pixel_width = (guint)(gui_get_base_width() + Columns * gui.char_width);
pixel_height = (guint)(gui_get_base_height() + Rows * gui.char_height);
+
+ pixel_width += get_menu_tool_width();
+ pixel_height += get_menu_tool_height();
+
+ // Dimensions may be smaller because of client side decorations, we handle
+ // that after we present the window.
gtk_window_set_default_size(GTK_WINDOW(gui.mainwin),
pixel_width, pixel_height);
@@ -795,8 +804,15 @@ gui_mch_open(void)
G_CALLBACK(mainwin_destroy_cb), NULL);
// Resize is handled by GtkForm's size_allocate callback.
- // Not sure if this needed but still do it I guess?
- gtk_widget_set_visible(gui.mainwin, TRUE);
+ gtk_window_present(GTK_WINDOW(gui.mainwin));
+
+ // Update so that we get the "gui.decor_height", which we can then use to
+ // set the exact dimensions of the window.
+ gui_mch_update();
+ Columns = columns;
+ Rows = rows;
+ gtk_window_set_default_size(GTK_WINDOW(gui.mainwin),
+ pixel_width, pixel_height + gui.decor_height);
// Make sure the drawing area gets keyboard focus.
gtk_widget_grab_focus(gui.drawarea);
@@ -951,6 +967,8 @@ gui_mch_set_shellsize(int width, int height,
height += gui.decor_height;
gtk_window_set_default_size(GTK_WINDOW(gui.mainwin), width, height);
+
+ gui_mch_update();
}
void
@@ -1290,9 +1308,13 @@ gui_mch_init_font(char_u *font_name, int fontset UNUSED)
ascii_glyph_table_init();
// im window position depends on cursor size which depends on font metrics
- // update the position after we've initialized font
- im_set_position(gui.row, gui.col);
-
+ // update the position after we've initialized font. Make sure to not go out
+ // of bounds of the screen.
+ {
+ int im_row = gui.row < screen_Rows ? gui.row : screen_Rows - 1;
+ int im_col = gui.col < screen_Columns ? gui.col : screen_Columns - 1;
+ im_set_position(im_row, im_col);
+ }
return OK;
}
@@ -1952,7 +1974,7 @@ static double prev_mouse_y = -1.0;
static GdkModifierType cur_state = 0;
static timeout_cb_type
-mouse_repeat_timer_cb(gpointer data)
+mouse_repeat_timer_cb(gpointer data UNUSED)
{
int x, y;
@@ -2130,6 +2152,7 @@ focus_out_event(GtkEventControllerFocus *controller UNUSED,
static void
drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
{
+#if defined(FEAT_IMAGE)
// Use GdkSurface, as that handles fractional scale values.
GdkSurface *surface = gtk_native_get_surface(
gtk_widget_get_native(gui.drawarea));
@@ -2139,7 +2162,7 @@ drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
popup_update_scale(old);
g_signal_connect(G_OBJECT(surface), "notify::scale",
G_CALLBACK(scale_factor_cb), NULL);
-
+#endif
gui_mch_new_colors();
}
@@ -2151,18 +2174,18 @@ drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
#endif
}
+#if defined(FEAT_IMAGE)
static void
scale_factor_cb(GdkSurface *surface,
GParamSpec *pspec UNUSED,
void *udata UNUSED)
{
-#if defined(FEAT_IMAGE)
double old = gui.scale;
gui.scale = gdk_surface_get_scale(surface);
popup_update_scale(old);
-#endif
}
+#endif
typedef enum
{
@@ -2334,7 +2357,7 @@ gui_gtk_set_dnd_targets(void)
* Handle textual DND data. Note that this does not finish the drop.
*/
static void
-drop_read_text(GdkDrop *drop, char_u *text)
+drop_read_text(GdkDrop *drop UNUSED, char_u *text)
{
GdkModifierType state;
char_u dropkey[6] = {
@@ -3080,7 +3103,7 @@ on_tab_reordered(
* Handle selecting an item in the tab line popup menu.
*/
static void
-tabline_menu_event_cb(VimMenuItem *item, VimMenuItemEvent event, void *udata)
+tabline_menu_event_cb(VimMenuItem *item UNUSED, VimMenuItemEvent event, void *udata)
{
if (event == VIM_MENU_ITEM_CLICKED)
send_tabline_menu_event(tabpage_hover, GPOINTER_TO_INT(udata));
@@ -3399,7 +3422,7 @@ get_menu_tool_height(void)
int height = 0;
- for (int i = 0; i < ARRAY_LENGTH(widgets); i++)
+ for (int i = 0; i < (int)ARRAY_LENGTH(widgets); i++)
{
GtkRequisition min;
GtkRequisition nat;
@@ -3690,6 +3713,9 @@ static int last_text_area_h = 0;
* ============================================================
*/
+#ifdef FEAT_MENU
+
+# ifdef FEAT_TOOLBAR
/*
* Icon name table for toolbar buttons.
* Must match toolbar_names[] in menu.c.
@@ -3774,10 +3800,11 @@ create_toolbar_icon(vimmenu_T *menu)
return image;
}
+# endif
static void
menu_button_clicked_cb(
- VimMenuItem *item,
+ VimMenuItem *item UNUSED,
VimMenuItemEvent event,
vimmenu_T *menu)
{
@@ -3870,7 +3897,7 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx)
{
vimmenu_T *parent = menu->parent;
-#ifdef FEAT_TOOLBAR
+# ifdef FEAT_TOOLBAR
if (parent != NULL && menu_is_toolbar(parent->name))
{
if (menu_is_separator(menu->name))
@@ -3904,7 +3931,7 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx)
}
return;
}
-#endif
+# endif
// Menu items (non-toolbar)
if (parent == NULL || parent->submenu_id == NULL)
@@ -3969,16 +3996,22 @@ gui_mch_destroy_menu(vimmenu_T *menu)
// For toolbar buttons and separators, remove from the toolbar box.
if (menu->parent != NULL && menu_is_toolbar(menu->parent->name))
{
- vim_toolbar_remove(VIM_TOOLBAR(gui.toolbar), menu->id);
- menu->id = NULL;
+ if (menu->id != NULL)
+ {
+ vim_toolbar_remove(VIM_TOOLBAR(gui.toolbar), menu->id);
+ menu->id = NULL;
+ }
return;
}
// For popup menus, unparent the menu as well
if (menu->name[0] == ']' || menu_is_popup(menu->name))
- gtk_widget_unparent(menu->submenu_id);
- else if (menu->parent == NULL)
- // Remove from menubar
+ {
+ if (menu->submenu_id != NULL)
+ gtk_widget_unparent(menu->submenu_id);
+ }
+ else if (menu->parent == NULL && menu->id != NULL)
+ // Remove from menubar, if not NULL.
vim_menu_bar_remove(VIM_MENU_BAR(gui.menubar), menu->id);
// "menu->id" is NULL for window toolbar
else if (menu->id != NULL)
@@ -4029,6 +4062,8 @@ show_menubar_popover(void)
gtk_popover_popup(GTK_POPOVER(menu));
}
+#endif // FEAT_MENU
+
/*
* ============================================================
* Scrollbar functions
@@ -4473,7 +4508,7 @@ typedef struct
} DialogState;
static void
-dialog_button_clicked_cb(GtkButton *button, DialogButtonState *state)
+dialog_button_clicked_cb(GtkButton *button UNUSED, DialogButtonState *state)
{
*state->response = state->but_idx;
*state->done = TRUE;
@@ -4481,9 +4516,9 @@ dialog_button_clicked_cb(GtkButton *button, DialogButtonState *state)
static gboolean
dialog_key_pressed_cb(
- GtkEventControllerKey *controller,
+ GtkEventControllerKey *controller UNUSED,
guint keyval,
- guint keycode,
+ guint keycode UNUSED,
GdkModifierType state,
DialogState *dstate)
{
@@ -4499,7 +4534,7 @@ dialog_key_pressed_cb(
}
static gboolean
-dialog_close_request_cb(GtkWindow *win, gboolean *win_closed)
+dialog_close_request_cb(GtkWindow *win UNUSED, gboolean *win_closed)
{
*win_closed = TRUE;
return FALSE;
@@ -4798,9 +4833,9 @@ entry_changed_cb(GtkWidget *entry, GtkWidget *dialog)
static gboolean
find_key_pressed_cb(
- GtkEventControllerKey *controller,
+ GtkEventControllerKey *controller UNUSED,
guint keyval,
- guint keycode,
+ guint keycode UNUSED,
GdkModifierType state,
SharedFindReplace *frdp)
{
diff --git a/src/gui_gtk4_cb.c b/src/gui_gtk4_cb.c
index e627ebeda..5a3a43800 100644
--- a/src/gui_gtk4_cb.c
+++ b/src/gui_gtk4_cb.c
@@ -55,7 +55,7 @@ vim_content_provider_class_init(VimContentProviderClass *class)
}
static void
-vim_content_provider_init(VimContentProvider *self)
+vim_content_provider_init(VimContentProvider *self UNUSED)
{
}
@@ -76,7 +76,7 @@ vim_content_provider_ref_formats(GdkContentProvider *cp UNUSED)
// html if user specified 'html' in 'clipboard' option.
GdkContentFormatsBuilder *builder = gdk_content_formats_builder_new();
- for (int i = 0; i < SUPPORTED_MIMES_LEN; i++)
+ for (size_t i = 0; i < SUPPORTED_MIMES_LEN; i++)
{
if (STRCMP(supported_mimes[i], "text/html") == 0 && !clip_html)
continue;
@@ -190,7 +190,7 @@ vim_content_provider_write_mime_type_async(
static gboolean
vim_content_provider_write_mime_type_finish(
- GdkContentProvider *cp,
+ GdkContentProvider *cp UNUSED,
GAsyncResult *result,
GError **error)
{
diff --git a/src/gui_gtk4_da.c b/src/gui_gtk4_da.c
index 80d3044cb..330706e86 100644
--- a/src/gui_gtk4_da.c
+++ b/src/gui_gtk4_da.c
@@ -162,7 +162,9 @@ struct _VimDrawArea
#endif
};
+#ifdef FEAT_IMAGE_GDK
static void draw_image_free(DrawImage *dimg);
+#endif
static void draw_row_init(DrawRow *drow, int row, int cols);
static void draw_row_clear(DrawRow *drow);
static void draw_row_dirty_layer(DrawRow *drow, DrawLayerType dlayer_t);
@@ -1034,6 +1036,7 @@ draw_row_render_text(DrawRow *drow, VimDrawArea *da)
empty_cells++;
continue;
}
+#if defined(FEAT_NETBEANS_INTG) || defined(FEAT_SIGN_ICONS)
else if (dglyphs->font == NULL)
{
// Add sign icon
@@ -1055,6 +1058,7 @@ draw_row_render_text(DrawRow *drow, VimDrawArea *da)
// loop
continue;
}
+#endif
else if (dglyphs->font != cur_font || cur_fg != dglyphs->fg_color)
{
FLUSH_NODE();
@@ -1157,7 +1161,7 @@ draw_row_render_special(DrawRow *drow, VimDrawArea *da)
{
dlayer->node = gsk_container_node_new(nodes, 2);
// gsk_container_node_new() takes its own ref
- for (int i = 0; i < ARRAY_LENGTH(nodes); i++)
+ for (int i = 0; i < (int)ARRAY_LENGTH(nodes); i++)
gsk_render_node_unref(nodes[i]);
}
diff --git a/src/gui_gtk4_f.c b/src/gui_gtk4_f.c
index 56949d997..195749d2a 100644
--- a/src/gui_gtk4_f.c
+++ b/src/gui_gtk4_f.c
@@ -81,9 +81,8 @@ vim_form_class_init(VimFormClass *class)
}
static void
-vim_form_init(VimForm *self)
+vim_form_init(VimForm *self UNUSED)
{
-
}
GtkWidget *
@@ -229,7 +228,7 @@ vim_form_size_allocate(
GtkWidget *widget,
int width,
int height,
- int baseline)
+ int baseline UNUSED)
{
VimForm *self = VIM_FORM(widget);
diff --git a/src/gui_gtk4_menu.c b/src/gui_gtk4_menu.c
index 4abb38624..7b534b255 100644
--- a/src/gui_gtk4_menu.c
+++ b/src/gui_gtk4_menu.c
@@ -881,7 +881,7 @@ vim_menu_init(VimMenu *self)
// Set all shortcut controllers in the window to not require a modifier for
// mnemonics.
controllers = gtk_widget_observe_controllers(GTK_WIDGET(self));
- for (int i = 0; i < g_list_model_get_n_items(controllers); i++)
+ for (guint i = 0; i < g_list_model_get_n_items(controllers); i++)
{
controller = g_list_model_get_item(controllers, i);
if (GTK_IS_SHORTCUT_CONTROLLER(controller))
diff --git a/src/gui_gtk4_tb.c b/src/gui_gtk4_tb.c
index 1971a4728..d2091f6ea 100644
--- a/src/gui_gtk4_tb.c
+++ b/src/gui_gtk4_tb.c
@@ -303,7 +303,11 @@ vim_toolbar_set_style(VimToolbar *self, int style, int iconsize)
void
vim_toolbar_remove(VimToolbar *self, GtkWidget *item)
{
- gtk_box_remove(GTK_BOX(self->strip),item);
+ if (gtk_widget_get_parent(item) == self->strip)
+ gtk_box_remove(GTK_BOX(self->strip), item);
+ else
+ gtk_box_remove(GTK_BOX(self->overflow_box), item);
+
self->items = g_list_remove(self->items, item);
g_object_unref(item);
gtk_widget_queue_allocate(GTK_WIDGET(self));
@@ -327,8 +331,10 @@ vim_toolbar_move_item_to(
from = overflow ? GTK_BOX(self->strip) : GTK_BOX(self->overflow_box);
+ g_object_ref(item);
gtk_box_remove(from, item);
gtk_box_append(to, item);
+ g_object_unref(item);
if (GTK_IS_SEPARATOR(item))
gtk_widget_set_visible(item, !overflow);
diff --git a/src/gui_xim.c b/src/gui_xim.c
index 0700ce41a..184945583 100644
--- a/src/gui_xim.c
+++ b/src/gui_xim.c
@@ -189,12 +189,10 @@ static int im_preedit_cursor = 0; // cursor offset in characters
static int im_preedit_trailing = 0; // number of characters after cursor
static unsigned long im_commit_handler_id = 0;
-# ifdef USE_GTK4
+# ifndef USE_GTK4
static unsigned int im_activatekey_keyval = GDK_KEY_VoidSymbol;
-# else
-static unsigned int im_activatekey_keyval = GDK_VoidSymbol;
-# endif
static unsigned int im_activatekey_state = 0;
+# endif
static GtkWidget *preedit_window = NULL;
static GtkWidget *preedit_label = NULL;
diff --git a/src/option.h b/src/option.h
index 326b48b63..e92bd5f0d 100644
--- a/src/option.h
+++ b/src/option.h
@@ -688,7 +688,7 @@ EXTERN int p_guipty; // 'guipty'
#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MSWIN)
EXTERN char_u *p_guiligatures; // 'guiligatures'
#endif
-#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
+#if (defined(FEAT_GUI_GTK) && !defined(USE_GTK4)) || defined(FEAT_GUI_X11)
EXTERN long p_ghr; // 'guiheadroom'
#endif
#ifdef CURSOR_SHAPE
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 187747368..10fbd82e0 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -1278,7 +1278,7 @@ static struct vimoption options[] =
{"guiheadroom", "ghr", P_NUM|P_VI_DEF,
-#if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_X11)
+#if (defined(FEAT_GUI_GTK) && !defined(USE_GTK4)) || defined(FEAT_GUI_X11)
(char_u *)&p_ghr, PV_NONE, NULL, NULL,
#else
(char_u *)NULL, PV_NONE, NULL, NULL,
diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim
index 83c43b117..0652d3de6 100644
--- a/src/testdir/test_gui.vim
+++ b/src/testdir/test_gui.vim
@@ -119,6 +119,9 @@ endfunc
func Test_quoteplus()
CheckX11BasedGui
+ " Doesn't work with GTK4 GUI, because theres no CUT BUFFER in Wayland, meaning
+ " when the GVim that is launched exits, the clipboard is also cleared as well.
+ CheckNotFeature gui_gtk4
let g:test_is_flaky = 1
@@ -667,6 +670,7 @@ endfunc
func Test_set_guiheadroom()
CheckX11BasedGui
+ CheckNotFeature gui_gtk4
" Since this script is to be read together with '-U NONE', the default
" value must be preserved.
diff --git a/src/testdir/test_gui_init.vim b/src/testdir/test_gui_init.vim
index 1a60a49ec..d5d1d0578 100644
--- a/src/testdir/test_gui_init.vim
+++ b/src/testdir/test_gui_init.vim
@@ -21,6 +21,7 @@ gui -f
func Test_set_guiheadroom()
CheckX11BasedGui
+ CheckNotFeature gui_gtk4
" The 'expected' value must be consistent with the value specified with
" gui_init.vim.
@@ -35,6 +36,7 @@ endfunc
func Test_set_guioptions_for_p()
CheckX11BasedGui
+ CheckNotFeature gui_gtk4
sleep 200ms
" Check if the 'p' option is included.
diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim
index bb8d86d7e..04e4447b0 100644
--- a/src/testdir/test_startup.vim
+++ b/src/testdir/test_startup.vim
@@ -461,6 +461,7 @@ endfunc
" Test the --echo-wid argument (for GTK GUI only).
func Test_echo_wid()
CheckCanRunGui
+ CheckNotFeature gui_gtk4
CheckFeature gui_gtk
if RunVim([], [], '-g --echo-wid -cq >Xtest_echo_wid')
@@ -475,6 +476,7 @@ endfunction
" Test the -reverse and +reverse arguments (for GUI only).
func Test_reverse()
CheckCanRunGui
+ CheckNotFeature gui_gtk4
CheckAnyOf Feature:gui_gtk Feature:gui_motif
let after =<< trim [CODE]
@@ -1427,7 +1429,9 @@ func Test_progname()
" Class: XmCascadeButton
" Illegal mnemonic character; Could not convert X KEYSYM to a keycode
" So don't check that stderr is empty with GUI Motif.
- if run_with_gui && !has('gui_motif')
+ "
+ " GTK4 also can output warnings, so don't do it as well
+ if run_with_gui && !has('gui_motif') && !has('gui_gtk4')
call assert_equal('', stdout_stderr, progname)
endif
call assert_equal(expectations[progname], readfile('Xprogname_out'), progname)
diff --git a/src/testdir/util/check.vim b/src/testdir/util/check.vim
index c69d39944..47fea7edf 100644
--- a/src/testdir/util/check.vim
+++ b/src/testdir/util/check.vim
@@ -301,7 +301,10 @@ endfunc
" Command to check for X11 based GUI
command CheckX11BasedGui call CheckX11BasedGui()
func CheckX11BasedGui()
- if !g:x11_based_gui
+ " GTK4 is technically not X11 based, but many tests still apply to it. However
+ " don't set g:x11_based_gui to true, because some tests (such as windowid)
+ " don't apply to GTK4.
+ if !g:x11_based_gui && !has('gui_gtk4')
throw 'Skipped: requires X11 based GUI'
endif
endfunc
diff --git a/src/testdir/util/gen_opt_test.vim b/src/testdir/util/gen_opt_test.vim
index f5d71ad18..738e76e94 100644
--- a/src/testdir/util/gen_opt_test.vim
+++ b/src/testdir/util/gen_opt_test.vim
@@ -237,7 +237,7 @@ let test_values = {
\ ['xxx', 'a,b']],
\ 'helplang': [['', 'de', 'de,it'], ['xxx']],
\ 'highlight': [['', 'e:Error'], ['xxx']],
- \ 'imactivatekey': [['', 'S-space'], ['xxx']],
+ \ 'imactivatekey': [[''], ['xxx']],
\ 'isfname': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'isident': [['', '@', '@,48-52'], ['xxx', '@48']],
\ 'iskeyword': [['', '@', '@,48-52'], ['xxx', '@48']],
@@ -395,6 +395,10 @@ let test_values = {
\ 'otherstring': [['', 'xxx'], []],
\}
+if !has('gui_gtk4')
+ let test_values['imactivatekey'] = [['', 'S-space'], ['xxx']]
+endif
+
if !has('clipboard')
" If +clipboard isn't enabled but +clipboard_provider is, then 'clipboard' is
" limited to "unnamed" and "unnamedplus"
diff --git a/src/version.c b/src/version.c
index ccd788a59..41fe299ee 100644
--- a/src/version.c
+++ b/src/version.c
@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 931,
/**/
930,
/**/