Mercurial > vim
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 |