# HG changeset patch # User Bram Moolenaar # Date 1650216603 -7200 # Node ID bae90eec6d14bbe9a2a494f700b5826886f8d498 # Parent 2a6e6f8ece076a329c8658f76ba562ac786671f0 patch 8.2.4776: GTK: 'lines' and 'columns' may change during startup Commit: https://github.com/vim/vim/commit/9f53e7bd7f0865585a5447fa3665c5d4c4f91408 Author: Ernie Rael Date: Sun Apr 17 18:27:49 2022 +0100 patch 8.2.4776: GTK: 'lines' and 'columns' may change during startup Problem: GTK: 'lines' and 'columns' may change during startup. Solution: Ignore stale GTK resize events. (Ernie Rael, closes https://github.com/vim/vim/issues/10179) 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 @@ -397,6 +397,130 @@ static int using_gnome = 0; #endif /* + * Keep a short term resize history so that stale gtk responses can be + * discarded. + * When a gtk_window_resize() request is sent to gtk, the width/height of + * the request is saved. Recent stale requests are kept around in a list. + * See https://github.com/vim/vim/issues/10123 + */ +#if 0 // Change to 1 to enable ch_log() calls for debugging. +# ifdef FEAT_JOB_CHANNEL +# define ENABLE_RESIZE_HISTORY_LOG +# endif +#endif + +/* + * History item of a resize request. + * Width and height are of gui.mainwin. + */ +typedef struct resize_history { + int used; // If true, can't match for discard. Only matches once. + int width; + int height; +#ifdef ENABLE_RESIZE_HISTORY_LOG + int seq; // for ch_log messages +#endif + struct resize_history *next; +} resize_hist_T; + +// never NULL during execution +static resize_hist_T *latest_resize_hist; +// list of stale resize requests +static resize_hist_T *old_resize_hists; + +/* + * Used when calling gtk_window_resize(). + * Create a resize request history item, put previous request on stale list. + * Width/height are the size of the request for the gui.mainwin. + */ + static void +alloc_resize_hist(int width, int height) +{ + // alloc a new resize hist, save current in list of old history + resize_hist_T *prev_hist = latest_resize_hist; + resize_hist_T *new_hist = ALLOC_CLEAR_ONE(resize_hist_T); + + new_hist->width = width; + new_hist->height = height; + latest_resize_hist = new_hist; + + // previous hist item becomes head of list + prev_hist->next = old_resize_hists; + old_resize_hists = prev_hist; + +#ifdef ENABLE_RESIZE_HISTORY_LOG + new_hist->seq = prev_hist->seq + 1; + ch_log(NULL, "gui_gtk: New resize seq %d (%d, %d) [%d, %d]", + new_hist->seq, width, height, (int)Columns, (int)Rows); +#endif +} + +/* + * Free everything on the stale resize history list. + * This list is empty when there are no outstanding resize requests. + */ + static void +clear_resize_hists() +{ +#ifdef ENABLE_RESIZE_HISTORY_LOG + int i = 0; +#endif + + if (latest_resize_hist) + latest_resize_hist->used = TRUE; + while (old_resize_hists != NULL) + { + resize_hist_T *next_hist = old_resize_hists->next; + + vim_free(old_resize_hists); + old_resize_hists = next_hist; +#ifdef ENABLE_RESIZE_HISTORY_LOG + i++; +#endif + } +#ifdef ENABLE_RESIZE_HISTORY_LOG + ch_log(NULL, "gui_gtk: free %d hists", i); +#endif +} + +// true if hist item is unused and matches w,h +#define MATCH_WIDTH_HEIGHT(hist, w, h) \ + (!hist->used && hist->width == w && hist->height == h) + +/* + * Search the resize hist list. + * Return true if the specified width,height match an item in the list that + * has never matched before. Mark the matching item as used so it will + * not match again. + */ + static int +match_stale_width_height(int width, int height) +{ + resize_hist_T *hist = old_resize_hists; + + for (hist = old_resize_hists; hist != NULL; hist = hist->next) + if (MATCH_WIDTH_HEIGHT(hist, width, height)) + { +#ifdef ENABLE_RESIZE_HISTORY_LOG + ch_log(NULL, "gui_gtk: discard seq %d, cur seq %d", + hist->seq, latest_resize_hist->seq); +#endif + hist->used = TRUE; + return TRUE; + } + return FALSE; +} + +#if defined(EXITFREE) + static void +free_all_resize_hist() +{ + clear_resize_hists(); + vim_free(latest_resize_hist); +} +#endif + +/* * GTK doesn't set the GDK_BUTTON1_MASK state when dragging a touch. Add this * state when dragging. */ @@ -593,6 +717,7 @@ gui_mch_free_all(void) #if defined(USE_GNOME_SESSION) vim_free(abs_restart_command); #endif + free_all_resize_hist(); } #endif @@ -709,7 +834,7 @@ property_event(GtkWidget *widget, xev.xproperty.window = commWindow; xev.xproperty.state = PropertyNewValue; serverEventProc(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget)), - &xev, 0); + &xev, 0); } return FALSE; } @@ -720,8 +845,8 @@ property_event(GtkWidget *widget, */ static void gtk_settings_xft_dpi_changed_cb(GtkSettings *gtk_settings UNUSED, - GParamSpec *pspec UNUSED, - gpointer data UNUSED) + GParamSpec *pspec UNUSED, + gpointer data UNUSED) { // Create a new PangoContext for this screen, and initialize it // with the current font if necessary. @@ -4006,7 +4131,36 @@ form_configure_event(GtkWidget *widget U GdkEventConfigure *event, gpointer data UNUSED) { - int usable_height = event->height; + int usable_height = event->height; + // Resize requests are made for gui.mainwin, + // get it's dimensions for searching if this event + // is a response to a vim request. + GdkWindow *win = gtk_widget_get_window(gui.mainwin); + int w = gdk_window_get_width(win); + int h = gdk_window_get_height(win); + +#ifdef ENABLE_RESIZE_HISTORY_LOG + ch_log(NULL, "gui_gtk: form_configure_event: (%d, %d) [%d, %d]", + w, h, (int)Columns, (int)Rows); +#endif + + // Look through history of recent vim resize reqeusts. + // If this event matches: + // - "latest resize hist" We're caught up; + // clear the history and process this event. + // If history is, old to new, 100, 99, 100, 99. If this event is + // 99 for the stale, it is matched against the current. History + // is cleared, we my bounce, but no worse than before. + // - "older/stale hist" If match an unused event in history, + // then discard this event, and mark the matching event as used. + // - "no match" Figure it's a user resize event, clear history. + // NOTE: clear history is default, then all incoming events are processed + + if (!MATCH_WIDTH_HEIGHT(latest_resize_hist, w, h) + && match_stale_width_height(w, h)) + // discard stale event + return TRUE; + clear_resize_hists(); #if GTK_CHECK_VERSION(3,22,2) && !GTK_CHECK_VERSION(3,22,4) // As of 3.22.2, GdkWindows have started distributing configure events to @@ -4329,15 +4483,16 @@ gui_mch_open(void) * manager upon us and should not interfere with what VIM is requesting * upon startup. */ + latest_resize_hist = ALLOC_CLEAR_ONE(resize_hist_T); g_signal_connect(G_OBJECT(gui.formwin), "configure-event", - G_CALLBACK(form_configure_event), NULL); + G_CALLBACK(form_configure_event), NULL); #ifdef FEAT_DND // Set up for receiving DND items. gui_gtk_set_dnd_targets(); g_signal_connect(G_OBJECT(gui.drawarea), "drag-data-received", - G_CALLBACK(drag_data_received_cb), NULL); + G_CALLBACK(drag_data_received_cb), NULL); #endif // With GTK+ 2, we need to iconify the window before calling show() @@ -4516,6 +4671,7 @@ gui_mch_set_shellsize(int width, int hei width += get_menu_tool_width(); height += get_menu_tool_height(); + alloc_resize_hist(width, height); // track the resize request if (gtk_socket_id == 0) gtk_window_resize(GTK_WINDOW(gui.mainwin), width, height); else diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4776, +/**/ 4775, /**/ 4774,