GtkFileChooser is disabled in gvim, because gvim exits gtk_main() level
1 every time an event is received, causing GTK to write to the disk
excessively.

gtk_main() has always stored the clipboard on the same trigger, so it's
fair to say that exiting gtk_main() constantly is not the way the GTK
developers expected an application to work. People who know stuff about
GTK agree:

http://mail.gnome.org/archives/gtk-list/2008-July/msg00131.html

My interest in this started when I discovered that GtkFileChooser had
been disabled. I missed it:

https://bugs.launchpad.net/ubuntu/+source/vim/+bug/365860

It's been 7 months now since that plaintive cry, so I gave up waiting
and wrote a patch. This is my first GTK project but it looks pretty
straightforward. I read the source code for gtk_main() and some other
things.

Emmanuele Bassi's advice was to create a separate GMainLoop:

http://mail.gnome.org/archives/gtk-list/2008-July/msg00146.html

That turns out to be unnecessary, since all GMainLoop does is calls
g_main_context_iteration() repeatedly, until it's time to exit. The
actual task of registering event sources and calling select() is done by
GMainContext, which exists whether or not a GMainLoop has been created.
Thus by calling g_main_context_iteration() at the relevant places, vim
can leave main_loop() unmodified.

My patch does this, and removes all calls to gtk_main_quit() except the
ones in modal dialogs. Modal dialogs are now implemented by calling
gtk_main() once only, and allowing the button handlers to call
gtk_main_quit(), just like in a regular GTK application.

We don't seem to be missing anything substantial by never calling
gtk_main(). You can register callbacks to be called when gtk_main()
exits, but luckily vim isn't doing that or else they'd be called on the
first keypress and then never again. Of course we miss out on that final
sync of recently-used.xbel, but it seems that it's already synced every
time it's changed, via the "changed" signal (see
gtk_recent_manager_changed() and its callers in gtk/gtkrecentmanager.c).

I haven't done cross-version tests on this patch, but
g_main_context_iteration() exists back to at least GLib 1.3.9 (September
2001).

Attached and online at:
http://tstarling.com/stuff/fix-gtk-main.patch

-- Tim Starling

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---

Index: src/gui_gtk.c
===================================================================
--- src/gui_gtk.c	(revision 1658)
+++ src/gui_gtk.c	(working copy)
@@ -596,10 +596,6 @@
     if (!gui.in_focus)
 	gui_focus_change(TRUE);
 # endif
-
-    /* make sure the menu action is taken immediately */
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 
 # if defined(FEAT_TOOLBAR) && !defined(HAVE_GTK2)
@@ -1145,9 +1141,6 @@
     }
 
     gui_drag_scrollbar(sb, value, dragging);
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 
 /* SBAR_VERT or SBAR_HORIZ */
@@ -1194,10 +1187,7 @@
  * Implementation of the file selector related stuff
  */
 #if defined(HAVE_GTK2) && GTK_CHECK_VERSION(2,4,0)
-/* This has been disabled, because the GTK library rewrites
- * ~/.recently-used.xbel every time the main loop is quit.  For Vim that means
- * on just about any event. */
-/* # define USE_FILE_CHOOSER */
+# define USE_FILE_CHOOSER
 #endif
 
 #ifndef USE_FILE_CHOOSER
@@ -1212,8 +1202,6 @@
     vw->browse_fname = (char_u *)g_strdup(gtk_file_selection_get_filename(
 					GTK_FILE_SELECTION(vw->filedlg)));
     gtk_widget_hide(vw->filedlg);
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 
     static void
@@ -1227,8 +1215,6 @@
 	vw->browse_fname = NULL;
     }
     gtk_widget_hide(vw->filedlg);
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 
     static gboolean
@@ -1240,10 +1226,7 @@
 	gui.browse_fname = NULL;
     }
     gui.filedlg = NULL;
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-
+    gtk_main_quit();
     return FALSE;
 }
 #endif
@@ -1349,8 +1332,7 @@
 # endif
 
     gtk_widget_show(gui.filedlg);
-    while (gui.filedlg && GTK_WIDGET_DRAWABLE(gui.filedlg))
-	gtk_main_iteration_do(TRUE);
+    gtk_main();
 #endif
 
 # ifdef HAVE_GTK2
@@ -1642,8 +1624,7 @@
 dlg_destroy_cb(int *p)
 {
     *p = TRUE;		/* set dialog_destroyed to break out of the loop */
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
+    gtk_main_quit();
 }
 
     int
@@ -1942,12 +1923,8 @@
 					    GTK_WIDGET(dialog), VW_POS_MOUSE);
 
     gtk_widget_show(dialog);
+    gtk_main();
 
-    /* loop here until the dialog goes away */
-    while (dialog_status == -1 && !dialog_destroyed
-					       && GTK_WIDGET_DRAWABLE(dialog))
-	gtk_main_iteration_do(TRUE);
-
     if (dialog_status < 0)
 	dialog_status = 0;
     if (dialog_status != 1 && textfield != NULL)
@@ -2990,9 +2967,6 @@
     CONVERT_FROM_UTF8_FREE(repl_text);
     CONVERT_FROM_UTF8_FREE(find_text);
 #endif
-
-    if (rc && gtk_main_level() > 0)
-	gtk_main_quit(); /* make sure cmd will be handled immediately */
 }
 
 /* our usual callback function */
Index: src/gui_gtk_x11.c
===================================================================
--- src/gui_gtk_x11.c	(revision 1658)
+++ src/gui_gtk_x11.c	(working copy)
@@ -698,9 +698,6 @@
 	xev.xproperty.window = commWindow;
 	xev.xproperty.state = PropertyNewValue;
 	serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev);
-
-	if (gtk_main_level() > 0)
-	    gtk_main_quit();
     }
     return FALSE;
 }
@@ -834,10 +831,6 @@
     if (widget != gui.drawarea)
 	gtk_widget_grab_focus(gui.drawarea);
 
-    /* make sure the input buffer is read */
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-
     return TRUE;
 }
 
@@ -851,10 +844,6 @@
     if (blink_state != BLINK_NONE)
 	gui_mch_stop_blink();
 
-    /* make sure the input buffer is read */
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-
     return TRUE;
 }
 
@@ -1235,9 +1224,6 @@
     if (p_mh)
 	gui_mch_mousehide(TRUE);
 
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-
     return TRUE;
 }
 
@@ -1271,9 +1257,6 @@
     else
 	clip_lose_selection(&clip_star);
 
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-
     return TRUE;
 }
 
@@ -1311,9 +1294,6 @@
 	received_selection = RS_FAIL;
 	/* clip_free_selection(cbd); ??? */
 
-	if (gtk_main_level() > 0)
-	    gtk_main_quit();
-
 	return;
     }
 
@@ -1439,9 +1419,6 @@
 #ifdef HAVE_GTK2
     g_free(tmpbuf_utf8);
 #endif
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 
 /*
@@ -1722,9 +1699,6 @@
     /* inform the editor engine about the occurrence of this event */
     gui_send_mouse_event(button, x, y, FALSE, vim_modifiers);
 
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-
     /*
      * Auto repeat timer handling.
      */
@@ -1913,8 +1887,6 @@
     vim_modifiers = modifiers_gdk2mouse(event->state);
 
     gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
-    if (gtk_main_level() > 0)
-	gtk_main_quit(); /* make sure the above will be handled immediately */
 
     return TRUE;
 }
@@ -1958,9 +1930,6 @@
     gui_send_mouse_event(button, (int)event->x, (int)event->y,
 							FALSE, vim_modifiers);
 
-    if (gtk_main_level() > 0)
-	gtk_main_quit(); /* make sure the above will be handled immediately */
-
     return TRUE;
 }
 #endif /* HAVE_GTK2 */
@@ -1989,8 +1958,6 @@
     vim_modifiers = modifiers_gdk2mouse(event->state);
 
     gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, vim_modifiers);
-    if (gtk_main_level() > 0)
-	gtk_main_quit();	/* make sure it will be handled immediately */
 
     return TRUE;
 }
@@ -2163,9 +2130,6 @@
 	add_to_input_buf(dropkey, (int)sizeof(dropkey));
     else
 	add_to_input_buf(dropkey + 3, (int)(sizeof(dropkey) - 3));
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 
 /*
@@ -3210,9 +3174,6 @@
 {
     /* Add the string cmd into input buffer */
     send_tabline_menu_event(clicked_page, (int)(long)user_data);
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 
     static void
@@ -3288,8 +3249,7 @@
 	    {
 		/* Click after all tabs moves to next tab page.  When "x" is
 		 * small guess it's the left button. */
-		if (send_tabline_event(x < 50 ? -1 : 0) && gtk_main_level() > 0)
-		    gtk_main_quit();
+		send_tabline_event(x < 50 ? -1 : 0);
 	    }
 #ifndef HAVE_GTK2
 	    else
@@ -3315,8 +3275,7 @@
 {
     if (!ignore_tabline_evt)
     {
-	if (send_tabline_event(idx + 1) && gtk_main_level() > 0)
-	    gtk_main_quit();
+	send_tabline_event(idx + 1);
     }
 }
 
@@ -4301,9 +4260,6 @@
 {
     if (gui.mainwin != NULL)
 	gtk_widget_destroy(gui.mainwin);
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 
 /*
@@ -4590,8 +4546,7 @@
 
     vw->fontname = (char_u *)gtk_font_selection_dialog_get_font_name(fs);
     gtk_widget_hide(vw->fontdlg);
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
+    gtk_main_quit();
 }
 
     static void
@@ -4600,8 +4555,7 @@
     gui_T *vw = (gui_T *)cbdata;
 
     gtk_widget_hide(vw->fontdlg);
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
+    gtk_main_quit();
 }
 
     static void
@@ -4610,8 +4564,7 @@
     gui_T *vw = (gui_T *)cbdata;
 
     vw->fontdlg = NULL;
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
+    gtk_main_quit();
 }
 #endif /* !HAVE_GTK2 */
 
@@ -4783,8 +4736,7 @@
     }
 
     /* Wait for the font dialog to be closed. */
-    while (gui.fontdlg && GTK_WIDGET_DRAWABLE(gui.fontdlg))
-	gtk_main_iteration_do(TRUE);
+    gtk_main();
 
     if (gui.fontname != NULL)
     {
@@ -6485,8 +6437,8 @@
     void
 gui_mch_update(void)
 {
-    while (gtk_events_pending() && !vim_is_input_buf_full())
-	gtk_main_iteration_do(FALSE);
+    while (g_main_context_pending(NULL) && !vim_is_input_buf_full())
+	g_main_context_iteration(NULL, TRUE);
 }
 
     static gint
@@ -6497,9 +6449,6 @@
     /* Just inform the caller about the occurrence of it */
     *timed_out = TRUE;
 
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-
     return FALSE;		/* don't happen again */
 }
 
@@ -6516,9 +6465,6 @@
     static char_u bytes[3] = {CSI, (int)KS_EXTRA, (int)KE_SNIFF};
 
     add_to_input_buf(bytes, 3);
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
 }
 #endif
 
@@ -6593,7 +6539,7 @@
 	 * situations, sort of race condition).
 	 */
 	if (!input_available())
-	    gtk_main();
+	    g_main_context_iteration(NULL, TRUE);
 
 	/* Got char, return immediately */
 	if (input_available())
@@ -6789,7 +6735,7 @@
 	 * during the FocusGained event. */
 	start = time(NULL);
 	while (received_selection == RS_NONE && time(NULL) < start + 3)
-	    gtk_main();	/* wait for selection_received_cb */
+	    g_main_context_iteration(NULL, TRUE);	/* wait for selection_received_cb */
 
 	if (received_selection != RS_FAIL)
 	    return;

Raspunde prin e-mail lui