comparison src/gui_gtk_x11.c @ 28503:bae90eec6d14 v8.2.4776

patch 8.2.4776: GTK: 'lines' and 'columns' may change during startup Commit: https://github.com/vim/vim/commit/9f53e7bd7f0865585a5447fa3665c5d4c4f91408 Author: Ernie Rael <errael@raelity.com> 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)
author Bram Moolenaar <Bram@vim.org>
date Sun, 17 Apr 2022 19:30:03 +0200
parents 4dcccb2673fe
children 278a14b00896
comparison
equal deleted inserted replaced
28502:2a6e6f8ece07 28503:bae90eec6d14
395 #else 395 #else
396 # define using_gnome 0 396 # define using_gnome 0
397 #endif 397 #endif
398 398
399 /* 399 /*
400 * Keep a short term resize history so that stale gtk responses can be
401 * discarded.
402 * When a gtk_window_resize() request is sent to gtk, the width/height of
403 * the request is saved. Recent stale requests are kept around in a list.
404 * See https://github.com/vim/vim/issues/10123
405 */
406 #if 0 // Change to 1 to enable ch_log() calls for debugging.
407 # ifdef FEAT_JOB_CHANNEL
408 # define ENABLE_RESIZE_HISTORY_LOG
409 # endif
410 #endif
411
412 /*
413 * History item of a resize request.
414 * Width and height are of gui.mainwin.
415 */
416 typedef struct resize_history {
417 int used; // If true, can't match for discard. Only matches once.
418 int width;
419 int height;
420 #ifdef ENABLE_RESIZE_HISTORY_LOG
421 int seq; // for ch_log messages
422 #endif
423 struct resize_history *next;
424 } resize_hist_T;
425
426 // never NULL during execution
427 static resize_hist_T *latest_resize_hist;
428 // list of stale resize requests
429 static resize_hist_T *old_resize_hists;
430
431 /*
432 * Used when calling gtk_window_resize().
433 * Create a resize request history item, put previous request on stale list.
434 * Width/height are the size of the request for the gui.mainwin.
435 */
436 static void
437 alloc_resize_hist(int width, int height)
438 {
439 // alloc a new resize hist, save current in list of old history
440 resize_hist_T *prev_hist = latest_resize_hist;
441 resize_hist_T *new_hist = ALLOC_CLEAR_ONE(resize_hist_T);
442
443 new_hist->width = width;
444 new_hist->height = height;
445 latest_resize_hist = new_hist;
446
447 // previous hist item becomes head of list
448 prev_hist->next = old_resize_hists;
449 old_resize_hists = prev_hist;
450
451 #ifdef ENABLE_RESIZE_HISTORY_LOG
452 new_hist->seq = prev_hist->seq + 1;
453 ch_log(NULL, "gui_gtk: New resize seq %d (%d, %d) [%d, %d]",
454 new_hist->seq, width, height, (int)Columns, (int)Rows);
455 #endif
456 }
457
458 /*
459 * Free everything on the stale resize history list.
460 * This list is empty when there are no outstanding resize requests.
461 */
462 static void
463 clear_resize_hists()
464 {
465 #ifdef ENABLE_RESIZE_HISTORY_LOG
466 int i = 0;
467 #endif
468
469 if (latest_resize_hist)
470 latest_resize_hist->used = TRUE;
471 while (old_resize_hists != NULL)
472 {
473 resize_hist_T *next_hist = old_resize_hists->next;
474
475 vim_free(old_resize_hists);
476 old_resize_hists = next_hist;
477 #ifdef ENABLE_RESIZE_HISTORY_LOG
478 i++;
479 #endif
480 }
481 #ifdef ENABLE_RESIZE_HISTORY_LOG
482 ch_log(NULL, "gui_gtk: free %d hists", i);
483 #endif
484 }
485
486 // true if hist item is unused and matches w,h
487 #define MATCH_WIDTH_HEIGHT(hist, w, h) \
488 (!hist->used && hist->width == w && hist->height == h)
489
490 /*
491 * Search the resize hist list.
492 * Return true if the specified width,height match an item in the list that
493 * has never matched before. Mark the matching item as used so it will
494 * not match again.
495 */
496 static int
497 match_stale_width_height(int width, int height)
498 {
499 resize_hist_T *hist = old_resize_hists;
500
501 for (hist = old_resize_hists; hist != NULL; hist = hist->next)
502 if (MATCH_WIDTH_HEIGHT(hist, width, height))
503 {
504 #ifdef ENABLE_RESIZE_HISTORY_LOG
505 ch_log(NULL, "gui_gtk: discard seq %d, cur seq %d",
506 hist->seq, latest_resize_hist->seq);
507 #endif
508 hist->used = TRUE;
509 return TRUE;
510 }
511 return FALSE;
512 }
513
514 #if defined(EXITFREE)
515 static void
516 free_all_resize_hist()
517 {
518 clear_resize_hists();
519 vim_free(latest_resize_hist);
520 }
521 #endif
522
523 /*
400 * GTK doesn't set the GDK_BUTTON1_MASK state when dragging a touch. Add this 524 * GTK doesn't set the GDK_BUTTON1_MASK state when dragging a touch. Add this
401 * state when dragging. 525 * state when dragging.
402 */ 526 */
403 static guint dragging_button_state = 0; 527 static guint dragging_button_state = 0;
404 528
591 { 715 {
592 vim_free(gui_argv); 716 vim_free(gui_argv);
593 #if defined(USE_GNOME_SESSION) 717 #if defined(USE_GNOME_SESSION)
594 vim_free(abs_restart_command); 718 vim_free(abs_restart_command);
595 #endif 719 #endif
720 free_all_resize_hist();
596 } 721 }
597 #endif 722 #endif
598 723
599 #if !GTK_CHECK_VERSION(3,0,0) 724 #if !GTK_CHECK_VERSION(3,0,0)
600 /* 725 /*
707 xev.xproperty.type = PropertyNotify; 832 xev.xproperty.type = PropertyNotify;
708 xev.xproperty.atom = commProperty; 833 xev.xproperty.atom = commProperty;
709 xev.xproperty.window = commWindow; 834 xev.xproperty.window = commWindow;
710 xev.xproperty.state = PropertyNewValue; 835 xev.xproperty.state = PropertyNewValue;
711 serverEventProc(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget)), 836 serverEventProc(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget)),
712 &xev, 0); 837 &xev, 0);
713 } 838 }
714 return FALSE; 839 return FALSE;
715 } 840 }
716 #endif // defined(FEAT_CLIENTSERVER) 841 #endif // defined(FEAT_CLIENTSERVER)
717 842
718 /* 843 /*
719 * Handle changes to the "Xft/DPI" setting 844 * Handle changes to the "Xft/DPI" setting
720 */ 845 */
721 static void 846 static void
722 gtk_settings_xft_dpi_changed_cb(GtkSettings *gtk_settings UNUSED, 847 gtk_settings_xft_dpi_changed_cb(GtkSettings *gtk_settings UNUSED,
723 GParamSpec *pspec UNUSED, 848 GParamSpec *pspec UNUSED,
724 gpointer data UNUSED) 849 gpointer data UNUSED)
725 { 850 {
726 // Create a new PangoContext for this screen, and initialize it 851 // Create a new PangoContext for this screen, and initialize it
727 // with the current font if necessary. 852 // with the current font if necessary.
728 if (gui.text_context != NULL) 853 if (gui.text_context != NULL)
729 g_object_unref(gui.text_context); 854 g_object_unref(gui.text_context);
4004 static gint 4129 static gint
4005 form_configure_event(GtkWidget *widget UNUSED, 4130 form_configure_event(GtkWidget *widget UNUSED,
4006 GdkEventConfigure *event, 4131 GdkEventConfigure *event,
4007 gpointer data UNUSED) 4132 gpointer data UNUSED)
4008 { 4133 {
4009 int usable_height = event->height; 4134 int usable_height = event->height;
4135 // Resize requests are made for gui.mainwin,
4136 // get it's dimensions for searching if this event
4137 // is a response to a vim request.
4138 GdkWindow *win = gtk_widget_get_window(gui.mainwin);
4139 int w = gdk_window_get_width(win);
4140 int h = gdk_window_get_height(win);
4141
4142 #ifdef ENABLE_RESIZE_HISTORY_LOG
4143 ch_log(NULL, "gui_gtk: form_configure_event: (%d, %d) [%d, %d]",
4144 w, h, (int)Columns, (int)Rows);
4145 #endif
4146
4147 // Look through history of recent vim resize reqeusts.
4148 // If this event matches:
4149 // - "latest resize hist" We're caught up;
4150 // clear the history and process this event.
4151 // If history is, old to new, 100, 99, 100, 99. If this event is
4152 // 99 for the stale, it is matched against the current. History
4153 // is cleared, we my bounce, but no worse than before.
4154 // - "older/stale hist" If match an unused event in history,
4155 // then discard this event, and mark the matching event as used.
4156 // - "no match" Figure it's a user resize event, clear history.
4157 // NOTE: clear history is default, then all incoming events are processed
4158
4159 if (!MATCH_WIDTH_HEIGHT(latest_resize_hist, w, h)
4160 && match_stale_width_height(w, h))
4161 // discard stale event
4162 return TRUE;
4163 clear_resize_hists();
4010 4164
4011 #if GTK_CHECK_VERSION(3,22,2) && !GTK_CHECK_VERSION(3,22,4) 4165 #if GTK_CHECK_VERSION(3,22,2) && !GTK_CHECK_VERSION(3,22,4)
4012 // As of 3.22.2, GdkWindows have started distributing configure events to 4166 // As of 3.22.2, GdkWindows have started distributing configure events to
4013 // their "native" children (https://git.gnome.org/browse/gtk+/commit/?h=gtk-3-22&id=12579fe71b3b8f79eb9c1b80e429443bcc437dd0). 4167 // their "native" children (https://git.gnome.org/browse/gtk+/commit/?h=gtk-3-22&id=12579fe71b3b8f79eb9c1b80e429443bcc437dd0).
4014 // 4168 //
4327 * We connect this signal deferred finally after anything is in place, 4481 * We connect this signal deferred finally after anything is in place,
4328 * since this is intended to handle resizements coming from the window 4482 * since this is intended to handle resizements coming from the window
4329 * manager upon us and should not interfere with what VIM is requesting 4483 * manager upon us and should not interfere with what VIM is requesting
4330 * upon startup. 4484 * upon startup.
4331 */ 4485 */
4486 latest_resize_hist = ALLOC_CLEAR_ONE(resize_hist_T);
4332 g_signal_connect(G_OBJECT(gui.formwin), "configure-event", 4487 g_signal_connect(G_OBJECT(gui.formwin), "configure-event",
4333 G_CALLBACK(form_configure_event), NULL); 4488 G_CALLBACK(form_configure_event), NULL);
4334 4489
4335 #ifdef FEAT_DND 4490 #ifdef FEAT_DND
4336 // Set up for receiving DND items. 4491 // Set up for receiving DND items.
4337 gui_gtk_set_dnd_targets(); 4492 gui_gtk_set_dnd_targets();
4338 4493
4339 g_signal_connect(G_OBJECT(gui.drawarea), "drag-data-received", 4494 g_signal_connect(G_OBJECT(gui.drawarea), "drag-data-received",
4340 G_CALLBACK(drag_data_received_cb), NULL); 4495 G_CALLBACK(drag_data_received_cb), NULL);
4341 #endif 4496 #endif
4342 4497
4343 // With GTK+ 2, we need to iconify the window before calling show() 4498 // With GTK+ 2, we need to iconify the window before calling show()
4344 // to avoid mapping the window for a short time. 4499 // to avoid mapping the window for a short time.
4345 if (found_iconic_arg && gtk_socket_id == 0) 4500 if (found_iconic_arg && gtk_socket_id == 0)
4514 // the window. So let's do it the other way around and resize the 4669 // the window. So let's do it the other way around and resize the
4515 // main window instead. 4670 // main window instead.
4516 width += get_menu_tool_width(); 4671 width += get_menu_tool_width();
4517 height += get_menu_tool_height(); 4672 height += get_menu_tool_height();
4518 4673
4674 alloc_resize_hist(width, height); // track the resize request
4519 if (gtk_socket_id == 0) 4675 if (gtk_socket_id == 0)
4520 gtk_window_resize(GTK_WINDOW(gui.mainwin), width, height); 4676 gtk_window_resize(GTK_WINDOW(gui.mainwin), width, height);
4521 else 4677 else
4522 update_window_manager_hints(width, height); 4678 update_window_manager_hints(width, height);
4523 4679