# HG changeset patch # User Christian Brabandt # Date 1706559305 -3600 # Node ID 995b539939c41e17c6f7ef6d3710083c440d497f # Parent ee5646ba068949851736327e08f71c1ffd47405e patch 9.1.0064: No Wayland support Commit: https://github.com/vim/vim/commit/6e0a18f82bd04a45e9682523c42af9cbbc37684c Author: lilydjwg Date: Mon Jan 29 20:54:28 2024 +0100 patch 9.1.0064: No Wayland support Problem: No Wayland support Solution: Add Wayland UI support (lilydjwg) closes: #9639 Signed-off-by: lilydjwg Signed-off-by: Christian Brabandt diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.1. Last change: 2024 Jan 25 +*builtin.txt* For Vim version 9.1. Last change: 2024 Jan 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -4424,14 +4424,16 @@ getwinpos([{timeout}]) *getwinpos()* getwinposx() The result is a Number, which is the X coordinate in pixels of the left hand side of the GUI Vim window. Also works for an xterm (uses a timeout of 100 msec). - The result will be -1 if the information is not available. + The result will be -1 if the information is not available + (e.g. on the Wayland backend). The value can be used with `:winpos`. *getwinposy()* getwinposy() The result is a Number, which is the Y coordinate in pixels of the top of the GUI Vim window. Also works for an xterm (uses a timeout of 100 msec). - The result will be -1 if the information is not available. + The result will be -1 if the information is not available + (e.g. on the Wayland backend). The value can be used with `:winpos`. getwinvar({winnr}, {varname} [, {def}]) *getwinvar()* diff --git a/src/gui.c b/src/gui.c --- a/src/gui.c +++ b/src/gui.c @@ -1589,8 +1589,11 @@ again: // Only comparing Rows and Columns may be sufficient, but let's stay on // the safe side. if (gui.num_rows != screen_Rows || gui.num_cols != screen_Columns - || gui.num_rows != Rows || gui.num_cols != Columns) + || gui.num_rows != Rows || gui.num_cols != Columns || gui.force_redraw) + { shell_resized(); + gui.force_redraw = 0; + } #ifdef FEAT_GUI_HAIKU vim_unlock_screen(); diff --git a/src/gui.h b/src/gui.h --- a/src/gui.h +++ b/src/gui.h @@ -259,6 +259,7 @@ typedef struct Gui int scrollbar_height; // Height of horizontal scrollbar int left_sbar_x; // Calculated x coord for left scrollbar int right_sbar_x; // Calculated x coord for right scrollbar + int force_redraw; // Force a redraw even e.g. not resized #ifdef FEAT_MENU # ifndef FEAT_GUI_GTK diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -793,6 +793,36 @@ draw_event(GtkWidget *widget UNUSED, return FALSE; } + +# if GTK_CHECK_VERSION(3,10,0) + static gboolean +scale_factor_event(GtkWidget *widget, + GParamSpec* pspec UNUSED, + gpointer user_data UNUSED) +{ + if (gui.surface != NULL) + cairo_surface_destroy(gui.surface); + + int w, h; + gtk_window_get_size(GTK_WINDOW(gui.mainwin), &w, &h); + gui.surface = gdk_window_create_similar_surface( + gtk_widget_get_window(widget), + CAIRO_CONTENT_COLOR_ALPHA, + w, h); + + int usable_height = h; + if (gtk_socket_id != 0) + usable_height -= (gui.char_height - (gui.char_height/2)); // sic. + + gui_gtk_form_freeze(GTK_FORM(gui.formwin)); + gui.force_redraw = 1; + gui_resize_shell(w, usable_height); + gui_gtk_form_thaw(GTK_FORM(gui.formwin)); + + return TRUE; +} +# endif // GTK_CHECK_VERSION(3,10,0) + #else // !GTK_CHECK_VERSION(3,0,0) static gint expose_event(GtkWidget *widget UNUSED, @@ -1667,11 +1697,12 @@ selection_get_cb(GtkWidget *widget U int gui_mch_early_init_check(int give_message) { - char_u *p; + char_u *p, *q; // Guess that when $DISPLAY isn't set the GUI can't start. p = mch_getenv((char_u *)"DISPLAY"); - if (p == NULL || *p == NUL) + q = mch_getenv((char_u *)"WAYLAND_DISPLAY"); + if ((p == NULL || *p == NUL) && (q == NULL || *q == NUL)) { gui.dying = TRUE; if (give_message) @@ -1704,7 +1735,10 @@ gui_mch_init_check(void) #if GTK_CHECK_VERSION(3,10,0) // Vim currently assumes that Gtk means X11, so it cannot use native Gtk // support for other backends such as Wayland. - gdk_set_allowed_backends ("x11"); + // + // Use an environment variable to enable unfinished Wayland support. + if (getenv("GVIM_ENABLE_WAYLAND") == NULL) + gdk_set_allowed_backends ("x11"); #endif #ifdef FEAT_GUI_GNOME @@ -2024,6 +2058,10 @@ scroll_event(GtkWidget *widget, { int button; int_u vim_modifiers; +#if GTK_CHECK_VERSION(3,4,0) + static double acc_x, acc_y; + static guint32 last_smooth_event_time; +#endif if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget)) gtk_widget_grab_focus(widget); @@ -2042,6 +2080,16 @@ scroll_event(GtkWidget *widget, case GDK_SCROLL_RIGHT: button = MOUSE_6; break; +#if GTK_CHECK_VERSION(3,4,0) + case GDK_SCROLL_SMOOTH: + if (event->time - last_smooth_event_time > 50) + // reset our accumulations after 50ms of silence + acc_x = acc_y = 0; + acc_x += event->delta_x; + acc_y += event->delta_y; + last_smooth_event_time = event->time; + break; +#endif default: // This shouldn't happen return FALSE; } @@ -2054,8 +2102,38 @@ scroll_event(GtkWidget *widget, vim_modifiers = modifiers_gdk2mouse(event->state); - gui_send_mouse_event(button, (int)event->x, (int)event->y, - FALSE, vim_modifiers); +#if GTK_CHECK_VERSION(3,4,0) + if (event->direction == GDK_SCROLL_SMOOTH) + { + while (acc_x > 1.0) + { // right + acc_x = MAX(0.0, acc_x - 1.0); + gui_send_mouse_event(MOUSE_6, (int)event->x, (int)event->y, + FALSE, vim_modifiers); + } + while (acc_x < -1.0) + { // left + acc_x = MIN(0.0, acc_x + 1.0); + gui_send_mouse_event(MOUSE_7, (int)event->x, (int)event->y, + FALSE, vim_modifiers); + } + while (acc_y > 1.0) + { // down + acc_y = MAX(0.0, acc_y - 1.0); + gui_send_mouse_event(MOUSE_5, (int)event->x, (int)event->y, + FALSE, vim_modifiers); + } + while (acc_y < -1.0) + { // up + acc_y = MIN(0.0, acc_y + 1.0); + gui_send_mouse_event(MOUSE_4, (int)event->x, (int)event->y, + FALSE, vim_modifiers); + } + } + else +#endif + gui_send_mouse_event(button, (int)event->x, (int)event->y, + FALSE, vim_modifiers); return TRUE; } @@ -2509,10 +2587,12 @@ setup_save_yourself(void) // Fall back to old method // first get the existing value + Display * dpy = gui_mch_get_display(); + if (!dpy) + return; + GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin); - - if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(mainwin_win), - GDK_WINDOW_XID(mainwin_win), + if (XGetWMProtocols(dpy, GDK_WINDOW_XID(mainwin_win), &existing_atoms, &count)) { Atom *new_atoms; @@ -2620,7 +2700,10 @@ mainwin_realize(GtkWidget *widget UNUSED // When started with "--echo-wid" argument, write window ID on stdout. if (echo_wid_arg) { - printf("WID: %ld\n", (long)GDK_WINDOW_XID(mainwin_win)); + if (gui_mch_get_display()) + printf("WID: %ld\n", (long)GDK_WINDOW_XID(mainwin_win)); + else + printf("WID: 0\n"); fflush(stdout); } @@ -2655,27 +2738,30 @@ mainwin_realize(GtkWidget *widget UNUSED setup_save_yourself(); #ifdef FEAT_CLIENTSERVER - if (serverName == NULL && serverDelayedStartName != NULL) - { - // This is a :gui command in a plain vim with no previous server - commWindow = GDK_WINDOW_XID(mainwin_win); - - (void)serverRegisterName(GDK_WINDOW_XDISPLAY(mainwin_win), - serverDelayedStartName); - } - else - { - /* - * Cannot handle "XLib-only" windows with gtk event routines, we'll - * have to change the "server" registration to that of the main window - * If we have not registered a name yet, remember the window. - */ - serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(mainwin_win), - GDK_WINDOW_XID(mainwin_win)); - } - gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK); - g_signal_connect(G_OBJECT(gui.mainwin), "property-notify-event", - G_CALLBACK(property_event), NULL); + if (gui_mch_get_display()) + { + if (serverName == NULL && serverDelayedStartName != NULL) + { + // This is a :gui command in a plain vim with no previous server + commWindow = GDK_WINDOW_XID(mainwin_win); + + (void)serverRegisterName(GDK_WINDOW_XDISPLAY(mainwin_win), + serverDelayedStartName); + } + else + { + /* + * Cannot handle "XLib-only" windows with gtk event routines, we'll + * have to change the "server" registration to that of the main window + * If we have not registered a name yet, remember the window. + */ + serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(mainwin_win), + GDK_WINDOW_XID(mainwin_win)); + } + gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK); + g_signal_connect(G_OBJECT(gui.mainwin), "property-notify-event", + G_CALLBACK(property_event), NULL); + } #endif } @@ -3919,6 +4005,9 @@ gui_mch_init(void) GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | +#if GTK_CHECK_VERSION(3,4,0) + GDK_SMOOTH_SCROLL_MASK | +#endif GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_POINTER_MOTION_MASK | @@ -4520,6 +4609,10 @@ gui_mch_open(void) #endif g_signal_connect(G_OBJECT(gui.formwin), "configure-event", G_CALLBACK(form_configure_event), NULL); +#if GTK_CHECK_VERSION(3,10,0) + g_signal_connect(G_OBJECT(gui.formwin), "notify::scale-factor", + G_CALLBACK(scale_factor_event), NULL); +#endif #ifdef FEAT_DND // Set up for receiving DND items. @@ -4603,8 +4696,12 @@ gui_mch_exit(int rc UNUSED) int gui_mch_get_winpos(int *x, int *y) { - gtk_window_get_position(GTK_WINDOW(gui.mainwin), x, y); - return OK; + if (gui_mch_get_display()) + { + gtk_window_get_position(GTK_WINDOW(gui.mainwin), x, y); + return OK; + } + return FAIL; } /* @@ -6229,9 +6326,10 @@ gui_mch_haskey(char_u *name) int gui_get_x11_windis(Window *win, Display **dis) { - if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL) - { - *dis = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)); + Display * dpy = gui_mch_get_display(); + if (dpy) + { + *dis = dpy; *win = GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin)); return OK; } @@ -6242,18 +6340,18 @@ gui_get_x11_windis(Window *win, Display } #endif -#if defined(FEAT_CLIENTSERVER) \ - || (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO) - Display * gui_mch_get_display(void) { - if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL) + if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL +#if GTK_CHECK_VERSION(3,0,0) + && GDK_IS_X11_DISPLAY(gtk_widget_get_display(gui.mainwin)) +#endif + ) return GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)); else return NULL; } -#endif void gui_mch_beep(void) @@ -6915,9 +7013,10 @@ clip_mch_request_selection(Clipboard_T * return; } - // Final fallback position - use the X CUT_BUFFER0 store - yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)), - cbd); + if (gui_mch_get_display()) + // Final fallback position - use the X CUT_BUFFER0 store + yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)), + cbd); } /* @@ -7083,9 +7182,11 @@ gui_mch_setmouse(int x, int y) // Sorry for the Xlib call, but we can't avoid it, since there is no // internal GDK mechanism present to accomplish this. (and for good // reason...) - XWarpPointer(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.drawarea)), - (Window)0, GDK_WINDOW_XID(gtk_widget_get_window(gui.drawarea)), - 0, 0, 0U, 0U, x, y); + Display * dpy = gui_mch_get_display(); + if (dpy) + XWarpPointer(dpy, (Window)0, + GDK_WINDOW_XID(gtk_widget_get_window(gui.drawarea)), + 0, 0, 0U, 0U, x, y); } diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -2320,12 +2320,11 @@ mch_settitle(char_u *title, char_u *icon #ifdef FEAT_X11 if (get_x11_windis() == OK) type = 1; -#else -# if defined(FEAT_GUI_PHOTON) \ +#endif +#if defined(FEAT_GUI_PHOTON) \ || defined(FEAT_GUI_GTK) || defined(FEAT_GUI_HAIKU) if (gui.in_use) type = 1; -# endif #endif /* diff --git a/src/testdir/check.vim b/src/testdir/check.vim --- a/src/testdir/check.vim +++ b/src/testdir/check.vim @@ -160,6 +160,14 @@ func CheckEnv(name) endif endfunc +" Command to Check for pure X11 (no Wayland) +command -nargs=0 CheckX11 call CheckX11() +func CheckX11() + if !empty($WAYLAND_DISPLAY) || empty($DISPLAY) + throw 'Skipped: not pure X11 environment' + endif +endfunc + " Command to check that we are using the GUI command CheckGui call CheckGui() func CheckGui() diff --git a/src/testdir/test_clientserver.vim b/src/testdir/test_clientserver.vim --- a/src/testdir/test_clientserver.vim +++ b/src/testdir/test_clientserver.vim @@ -13,7 +13,7 @@ source shared.vim func Check_X11_Connection() if has('x11') - CheckEnv DISPLAY + CheckX11 try call remote_send('xxx', '') catch diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim --- a/src/testdir/test_gui.vim +++ b/src/testdir/test_gui.vim @@ -111,6 +111,8 @@ func Test_getfontname_without_arg() endfunc func Test_getwinpos() + CheckX11 + call assert_match('Window position: X \d\+, Y \d\+', execute('winpos')) call assert_true(getwinposx() >= 0) call assert_true(getwinposy() >= 0) @@ -897,7 +899,7 @@ func Test_set_term() endfunc func Test_windowid_variable() - if g:x11_based_gui || has('win32') + if (g:x11_based_gui && empty($WAYLAND_DISPLAY)) || has('win32') call assert_true(v:windowid > 0) else call assert_equal(0, v:windowid) diff --git a/src/testdir/test_quotestar.vim b/src/testdir/test_quotestar.vim --- a/src/testdir/test_quotestar.vim +++ b/src/testdir/test_quotestar.vim @@ -139,8 +139,8 @@ func Test_quotestar() if has('macunix') let skipped = Do_test_quotestar_for_macunix() elseif has('x11') - if empty($DISPLAY) - let skipped = "Test can only run when $DISPLAY is set." + if empty($DISPLAY) || !empty($WAYLAND_DISPLAY) + let skipped = "Test can only run when $DISPLAY is set and $WAYLAND_DISPLAY is not set." else let skipped = Do_test_quotestar_for_x11() endif diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -518,9 +518,10 @@ func Test_geometry() call writefile([&columns, &lines, getwinposx(), getwinposy(), string(getwinpos())], "Xtest_geometry") qall [CODE] + " Hide menu because gtk insists to make the window wide enough to show it completely " Some window managers have a bar at the top that pushes windows down, " need to use at least 130, let's do 150 - if RunVim([], after, '-f -g -geometry 31x13+41+150') + if RunVim(['set guioptions-=m'], after, '-f -g -geometry 31x13+41+150') let lines = readfile('Xtest_geometry') " Depending on the GUI library and the windowing system the final size " might be a bit different, allow for some tolerance. Tuned based on @@ -529,9 +530,12 @@ func Test_geometry() " for some reason, the window may contain fewer lines than requested " for GTK, so allow some tolerance call assert_inrange(8, 13, str2nr(lines[1])) - call assert_equal('41', lines[2]) - call assert_equal('150', lines[3]) - call assert_equal('[41, 150]', lines[4]) + " on Wayland there is no way to set or retrieve window positions + if empty($WAYLAND_DISPLAY) + call assert_equal('41', lines[2]) + call assert_equal('150', lines[3]) + call assert_equal('[41, 150]', lines[4]) + endif endif endif diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -3409,7 +3409,7 @@ def Test_remote_foreground() CheckFeature clientserver # remote_foreground() doesn't fail on MS-Windows CheckNotMSWindows - CheckEnv DISPLAY + CheckX11 v9.CheckDefAndScriptFailure(['remote_foreground(10)'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1']) assert_fails('remote_foreground("NonExistingServer")', 'E241:') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 64, +/**/ 63, /**/ 62,