Conversation
Add GTK4 GUI backend as separate source files (gui_gtk4.c, gui_gtk4_x11.c, gui_gtk4_f.c) rather than adding #if branches to existing GTK2/GTK3 code. GTK4 changes are too fundamental for conditional compilation to be maintainable. New files: - gui_gtk4_f.c/h: GtkForm widget (GtkWidget-based, no GdkWindow) - gui_gtk4_x11.c: Main GUI (event controllers, cairo drawing) - gui_gtk4.c: Scrollbars, dialogs, menu stubs Build system: - configure.ac: --enable-gui=gtk4, pkg-config gtk4, USE_GTK4 - Makefile: GTK4_SRC/OBJ/DEFS variables, GUITYPE=GTK4 Key GTK4 adaptations: - GtkEventController for keyboard/mouse/scroll/focus - gtk_drawing_area_set_draw_func for rendering - GtkFontDialog (4.10+) for :set guifont=* - cairo_image_surface for off-screen drawing - GskTransform for child widget positioning - No GdkWindow, GtkContainer, gtk_dialog_run, GdkAtom Co-authored-by: Claude <noreply@anthropic.com>
- Cursor blinking with timeout_add/timeout_remove - Hollow cursor and part cursor drawing (cairo) - Screen scrolling via surface_copy_rect (delete/insert lines) - Mouse events: click, drag, release, scroll wheel - Visual bell (screen invert with CAIRO_OPERATOR_DIFFERENCE) - File open/save dialog via GtkFileDialog - Directory browser via GtkFileDialog - Message dialog via GtkAlertDialog - Scrollbar value-changed signal connection Co-authored-by: Claude <noreply@anthropic.com>
- Clipboard via GdkClipboard (PRIMARY and CLIPBOARD) - Sign icons with GdkPixbuf + cairo - Tabline update (GtkNotebook page management) - Mouse cursor shape with gdk_cursor_new_from_name - Socket server for --remote (FEAT_SOCKETSERVER) - File open/save/directory dialogs via GtkFileDialog - Message dialog via GtkAlertDialog - Balloon eval via tooltip - Geometry parsing (XParseGeometry) - gui_mch_haskey implementation - Find/Replace fallback to command-line - Clean up menu stubs with descriptive comments All TODO stubs are now implemented or have proper no-op with documentation for features requiring further work (menus). Co-authored-by: Claude <noreply@anthropic.com>
Use 80x24 as the default GUI size instead of inheriting the terminal size, matching GTK3 gvim behavior. Co-authored-by: Claude <noreply@anthropic.com>
- Drag-and-drop via GtkDropTarget (files and text) - Geometry parsing with XParseGeometry - gui_mch_haskey using special_keys table - Tabline update implementation - Balloon eval via tooltip - Sign icons via GdkPixbuf + cairo - Menu stubs cleaned up with descriptive comments - Find/Replace fallback to command-line Co-authored-by: Claude <noreply@anthropic.com>
Enable FEAT_XIM for GTK4 builds. Adapt gui_xim.c for GTK4: - Use gtk_im_context_set_client_widget() instead of set_client_window() - Use gtk_im_context_filter_keypress() in key_press_event - Skip xim_queue_key_press_event (not needed with GTK4 event model) - GTK4-compatible preedit window (no POPUP type, no gtk_window_move) - Replace deprecated gtk_window_resize/gtk_widget_show_all - Replace GdkScreen DPI query with fixed 96 DPI fallback - Use gtk_css_provider_load_from_string for GTK4 Co-authored-by: Claude <noreply@anthropic.com>
Native Find/Replace dialog using GtkWindow + GtkGrid + GtkEntry + GtkCheckButton. Supports: - Find what / Replace with entries - Whole word / Match case checkboxes - Direction Up/Down radio buttons - Find Next / Replace / Replace All / Close buttons - Enter key activates Find Next - Dialog reuse (raises existing dialog) Co-authored-by: Claude <noreply@anthropic.com>
Toolbar using GtkBox + GtkButton + GtkImage: - Icon lookup from themed icon names and custom icon files - Tooltip support - Separator items - Destroy/cleanup support - Show/hide via gui_mch_show_toolbar Co-authored-by: Claude <noreply@anthropic.com>
- Remove mainwin notify::default-width/height handler that caused a resize feedback loop (set_shellsize -> notify -> gui_resize_shell -> set_text_area_pos shrinking width to 0) - Recreate cairo surface in gui_mch_set_text_area_pos when text area size changes - Use gtk_drawing_area_set_content_width/height to ensure GtkDrawingArea reports correct size to draw_func Co-authored-by: Claude <noreply@anthropic.com>
Menu system using GMenu + GSimpleActionGroup + GtkPopoverMenuBar: - gui_mch_add_menu creates GMenu submenus - gui_mch_add_menu_item creates GMenuItem with GSimpleAction - GtkPopoverMenuBar from GMenu model for menubar - Popup menu via GtkPopoverMenu - Dynamic action creation with unique names - Menu grey/hidden stubs (GMenu items can't be individually hidden) Co-authored-by: Claude <noreply@anthropic.com>
- Use idle callback for window resize notification to avoid re-entering GTK layout during notify::default-width/height - Add notify::maximized and notify::fullscreened handlers - Guard with in_set_shellsize to prevent resize feedback loop - Initialize gui.scrollbar_width/height to SB_DEFAULT_WIDTH - Calculate scrollbar padding from formwin and text area sizes - Track text area size for correct padding computation Known limitation: vertical shrink by manual drag does not yet resize the text area (content_height constraint). Co-authored-by: Claude <noreply@anthropic.com>
Reset drawarea's size_request and content_width/height to 1x1 in resize_idle_cb before calling gui_resize_shell(). This allows GTK to shrink the window freely. gui_resize_shell() then triggers gui_mch_set_text_area_pos() which sets the correct sizes back. Co-authored-by: Claude <noreply@anthropic.com>
|
wow, very nice. Can you share a screenshot of vim --clean -g ? |
|
I think the GTK4 port of GVim should focus more on Wayland instead of X11, which is already deprecated and will be removed in GTK5. I suppose there are issues with keyboard layout detection, but there a much more users that have it working fine. Having a 2800 line file called "gui_gtk4_x11.c" does not feel right... |
Replace manual resize management with GTK4's layout system: - drawarea placed as GtkOverlay child with vexpand/hexpand - formwin (scrollbars) as overlay on top - drawarea auto-fits to window size via GTK4 layout - form_size_allocate notifies Vim via idle callback - gui_mch_set_shellsize only sets initial window size - surface resized in draw_event to match drawarea - No more resize feedback loops Co-authored-by: Claude <noreply@anthropic.com>
|
Currently the only X11 dependency in the GTK4 backend is |
|
This also means we could rename |
Co-authored-by: Claude <noreply@anthropic.com>
The GTK4 backend has no X11 dependency, so the _x11 suffix was misleading. Merge gui_gtk4.c (menus, scrollbars, dialogs) and gui_gtk4_x11.c (main GUI) into a single gui_gtk4.c file. Also merge proto/gui_gtk4_x11.pro into proto/gui_gtk4.pro and update Makefile, proto.h, and Filelist accordingly. Co-authored-by: Claude <noreply@anthropic.com>
Align #ifdef USE_GTK4 directives with surrounding nesting level to pass CI preprocessor indent check. Co-authored-by: Claude <noreply@anthropic.com>
Set EGL_LOG_LEVEL=fatal to suppress noisy EGL warnings on systems where GL/Vulkan is not available (e.g. WSL2 with cairo renderer). Co-authored-by: Claude <noreply@anthropic.com>
Add proper NULL and type checks in gui_mch_set_scrollbar_thumb and gui_mch_create_scrollbar to prevent GTK CRITICAL assertions when scrollbar operations are called before the widget is ready. Co-authored-by: Claude <noreply@anthropic.com>
- Replace XParseGeometry with vim_parse_geometry (pure C) - Remove #include <X11/Xutil.h> - Remove GDK_BACKEND=x11 default (let GTK4 choose) - Add Display/Window/Atom typedefs for proto file compatibility - Clear X_PRE_LIBS/X_EXTRA_LIBS/X_LIB in configure for GTK4 (keep -lX11 for GTK4's indirect X11 backend dependency) The GTK4 backend no longer uses any X11 API directly. Co-authored-by: Claude <noreply@anthropic.com>
|
Since I'm developing this on WSL2, I haven't been able to test the Broadway backend yet. If you have a native Linux environment, you should be able to run gvim in a browser:
You should see gvim running inside the browser. I'd appreciate it if someone could give it a try! |
|
Hello, Thanks for the information about broadway, I will take a look when possible. I still can't really test it properly, but following the recent GTK4 updates, the --nofork issue is now resolved and it's not needed anymore, starts well by default without problems. Small things that I quickly detect when I used it: 1. Empty WM_CLASS (X11) When running in an X11 environment, the WM_CLASS attribute is empty. This prevents window managers (I use dwm) from correctly identifying the application, applying window rules etc.. 2. Crash when setting guioptions=k Setting the k flag (Keep window size) in guioptions causes an immediate crash. 3. Ligatures are not rendering (works with gtk2, gktk3) Even with a supported font and the guiligatures option set, symbols are not displayed as expected. 4. X11 Feature missing in :version (maybe it's correct) The :version output shows -X11. While this is a GTK4 build, it is unclear if certain X11-specific integrations (like clipboard) are intentionally disabled or if this is an expecting setting in the build as you said before (no X11 deps). Thank you, this looks promising! |
gui_mch_newfont() was calling gui_set_shellsize() which called gui_mch_newfont() back, causing infinite recursion and a crash. Use gui_resize_shell() instead to recalculate Rows/Columns from the current window size without re-entering gui_set_shellsize(). Also add a FIXME comment to gui.c where gui_mch_newfont() is called even when the font hasn't changed (e.g. just setting guioptions=k).
Without this, WM_CLASS is empty on X11, which prevents window managers like dwm from identifying gvim windows for placement rules.
Port the ligature rendering logic from gui_gtk_x11.c (GTK3) to GTK4. Previously ligature characters were rendered individually through the fast ASCII glyph cache path, ignoring the ligatures_map entirely. Split gui_gtk_draw_string into a wrapper that segments the string into ASCII and ligature/UTF-8 parts, and gui_gtk_draw_string_ext that does the actual drawing with proper Pango shaping via pango_shape_full(). Also port helper functions for cluster-based glyph width calculation, combining character handling, and guifontwide support.
Two issues prevented mouse events from reaching Vim: 1. gui.formwin (GtkForm overlay for scrollbars) was layered on top of gui.drawarea via GtkOverlay, intercepting all mouse events. Fix: set can_target=FALSE on formwin so events pass through. 2. Drag detection used `if (mouse_pressed_button)` but MOUSE_LEFT is 0x00, so left-button drags were never detected. Fix: use -1 as "no button pressed" sentinel and check >= 0.
gui_mch_flush() only called gdk_display_flush() which flushes the display buffer but does not notify GTK that the offscreen surface content has changed. Add gtk_widget_queue_draw() so the draw_event callback is triggered to paint the updated surface to the widget.
|
@gonzaru Thank you for the detailed report! The three issues you reported have been fixed:
Regarding |
When focus moved to another widget (e.g. menubar, toolbar), the drawing area did not regain focus on mouse re-enter, leaving the cursor in its unfocused (hollow) shape. Add gtk_widget_grab_focus() in enter_notify_event, matching the GTK3 behavior.
When the drawing area was resized (e.g. after :vsp), the old surface content was copied to the new surface. If Vim's redraw didn't cover all areas, stale content like the intro screen text remained as ghost artifacts. Fix by filling the new surface with the background color instead of copying old content, since gui_resize_shell() triggers a full redraw anyway. Also remove duplicate surface resizing logic from draw_event, centralizing it in drawarea_resize_cb.
I can confirm that 2. and 3 are resolved. The crash is fixed and the ligatures work! About 1. the WM_CLASS now reports: WM_CLASS(STRING) = "gvim", "gvim" Needs to be: WM_CLASS(STRING) = "gvim", "Gvim" This is how gtk2 and gkt3 also report it, as the class name (the second field) the standard is to capitalize it. The spec states that WM_CLASS must contain two consecutive null-terminated strings: (4.1.2.5. WM_CLASS Property) I will try to check more things this weekend and report it if necessary. Regards |
WM_CLASS is an X11 property that GTK4 no longer sets directly. The correct way to associate windows with the application in GTK4 is through the StartupWMClass field in the desktop entry file.
|
@gonzaru Thanks for the clarification on WM_CLASS. WM_CLASS is an X11-specific property that GTK4 no longer sets directly. The correct way to handle this in GTK4 is through the |
|
@mattn Yes I understand to do not focus on x11 specific for now and those things can be talked latelly. As you said, the .desktop way can helps to this as it will add the correct class name on the fly for Gvim.. Just in case, another small bug: (crash on console dialogs)
It will show a console dialog, but the gvim freeze an does not respond anymore. With the gui dialog: (with :set guioptions-=c)
Works, but the text of the dialog are not labeled correctly and can be confusing. (especially on vs no)
-- About ligatures support, there is already a present bug in gtk2/gtk3 (also gtk4) versions, so maybe now can also be fixed: Ticket: #12901 |
When 'guioptions' includes "c" and 'confirm' is set, :q on a modified buffer sets exiting=TRUE before calling do_dialog(). The GTK4-specific check for "exiting" in gui_mch_wait_for_chars() caused it to bail out immediately without waiting for key input, resulting in an infinite loop. Remove the "exiting" check to match the GTK3 implementation.
The button label strings parsed from the "&Yes\n&No\n&Cancel" format were stored as pointers into a buffer that was freed before gtk_alert_dialog_set_buttons() copied them. Defer vim_free(buf) and CONVERT_TO_UTF8_FREE() until after the dialog is done.
|
@gonzaru Thank you for the detailed reports! The following two issues have been fixed and pushed:
Regarding the ligature issue at cursor position (#12901): this is a pre-existing bug that affects GTK2/GTK3 as well, not specific to GTK4. The cursor is drawn with |
|
@mattn Confirmed both (gui and non-gui dialogs) fixed. Thanks! Here a few more small ones that I've just found: (1) Font completion (:set guifont=<TAB>)
In other gtks it shows a popup to select the gui font. -- This can be considered more cosmetic than a real bug. (2) Menu greying (disabled items) The gtk4 version treats all menu items as active, so even if there is no copy/cut/paste etc, it does not show the menu as "empty/grey" like in gtk3. -- (3) (:winpos always returns 0) :winpos The winpos command can be useful for sessions with :set sessionoptions+=winpos etc and in the previous gtk's it returns the correct window position. |
Implement gui_mch_menu_grey() for GTK4 using g_simple_action_set_enabled() to grey out disabled menu items (e.g. Copy/Paste when unavailable). Store the GAction name in menu->label for later lookup. Also fix the popover menu not closing after selecting an item. GTK4's GtkPopoverMenuBar marks popovers as not-visible but Vim's custom main loop does not process the rendering update. Use gtk_widget_unrealize() to force the popover surface to close.
GTK4 does not provide a window position API (removed by design for Wayland compatibility). Return FAIL instead of reporting 0,0 which is misleading.
List monospace font families from Pango context for :set guifont=<TAB> completion, matching the GTK3 implementation.
|
@gonzaru The remaining three items from your latest report have been fixed and pushed:
|
Use GtkPrintOperation with Pango/Cairo rendering instead of PostScript + lpr. Guarded by FEAT_GUI_GTK_PRINT ifdef.












Add GTK4 GUI backend using separate source files instead of adding conditional branches to the existing GTK2/GTK3 code. GTK4's API changes are too fundamental for
#ifbranching to be maintainable.This PR was developed with the assistance of Claude Code (Anthropic).
Build
Requires GTK 4.10+ (for
GtkFontDialog,GtkFileDialog,GtkAlertDialog).New files
gui_gtk4.c— Main GUI implementation (window, events, drawing, fonts, colors, menus, scrollbars, dialogs, toolbar)gui_gtk4_f.c/h— GtkForm widget (extends GtkWidget, uses GskTransform)No direct X11 dependency
The GTK4 backend does not use any X11 API directly. All X11 libraries shown by
lddare indirect dependencies from libgtk-4.so.Features
:set guifont=*) via GtkFontDialog:browse) via GtkFileDialogconfirm()) via GtkAlertDialog:promptfind/:promptrepl)has("gui_gtk4")feature flag:q!exitKnown limitations
GSK_RENDERER=cairobecause GL/Vulkan renderers may not be available in all environments. Can be overridden via environment variable.EGL_LOG_LEVEL=fatal) when GL is unavailable.This is a work in progress.