changeset 8218:3456e2ebebd4 v7.4.1402

commit https://github.com/vim/vim/commit/9892189d2e7ab94b750f99e6da4cbfc3c8014517 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Feb 23 17:14:37 2016 +0100 patch 7.4.1402 Problem: GTK 3 is not supported. Solution: Add GTK 3 support. (Kazunobu Kuriyama)
author Christian Brabandt <cb@256bit.org>
date Tue, 23 Feb 2016 17:15:05 +0100
parents c52abf35df88
children 46cb926dd589
files runtime/doc/eval.txt runtime/doc/gui.txt runtime/doc/gui_x11.txt src/auto/configure src/channel.c src/config.h.in src/configure.in src/eval.c src/gui.h src/gui_beval.c src/gui_beval.h src/gui_gtk.c src/gui_gtk_f.c src/gui_gtk_f.h src/gui_gtk_x11.c src/if_mzsch.c src/mbyte.c src/netbeans.c src/structs.h src/version.c
diffstat 20 files changed, 3104 insertions(+), 104 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -7315,6 +7315,7 @@ gui_athena		Compiled with Athena GUI.
 gui_gnome		Compiled with Gnome support (gui_gtk is also defined).
 gui_gtk			Compiled with GTK+ GUI (any version).
 gui_gtk2		Compiled with GTK+ 2 GUI (gui_gtk is also defined).
+gui_gtk3		Compiled with GTK+ 3 GUI (gui_gtk is also defined).
 gui_mac			Compiled with Macintosh GUI.
 gui_motif		Compiled with Motif GUI.
 gui_photon		Compiled with Photon GUI.
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -25,7 +25,7 @@ 1. Starting the GUI				*gui-start* *E229
 
 First you must make sure you actually have a version of Vim with the GUI code
 included.  You can check this with the ":version" command, it says "with xxx
-GUI", where "xxx" is X11-Motif, X11-Athena, Photon, GTK, GTK2, etc., or
+GUI", where "xxx" is X11-Motif, X11-Athena, Photon, GTK2, GTK3, etc., or
 "MS-Windows 32 bit GUI version".
 
 How to start the GUI depends on the system used.  Mostly you can run the
@@ -514,11 +514,14 @@ a menu entry.  Hit <Enter> to execute it
 This does require the |+menu| feature enabled at compile time.
 
 							*tear-off-menus*
-GTK+ and Motif support Tear-off menus.  These are sort of sticky menus or
+GTK+ 2 and Motif support Tear-off menus.  These are sort of sticky menus or
 pop-up menus that are present all the time.  If the resizing does not work
 correctly, this may be caused by using something like "Vim*geometry" in the
 defaults.  Use "Vim.geometry" instead.
 
+As to GTK+ 3, tear-off menus have been deprecated since GTK+ 3.4.
+Accordingly, they are disabled if gvim is linked against GTK+ 3.4 or later.
+
 The Win32 GUI version emulates Motif's tear-off menus.  Actually, a Motif user
 will spot the differences easily, but hopefully they're just as useful.  You
 can also use the |:tearoff| command together with |hidden-menus| to create
@@ -650,8 +653,8 @@ When no or zero priority is given, 500 i
 The priority for the PopUp menu is not used.
 
 The Help menu will be placed on the far right side of the menu bar on systems
-which support this (Motif and GTK+).  For GTK+ 2, this is not done anymore
-because right-aligning the Help menu is now discouraged UI design.
+which support this (Motif and GTK+).  For GTK+ 2 and 3, this is not done
+anymore because right-aligning the Help menu is now discouraged UI design.
 
 You can use a priority higher than 9999, to make it go after the Help menu,
 but that is non-standard and is discouraged.  The highest possible priority is
--- a/runtime/doc/gui_x11.txt
+++ b/runtime/doc/gui_x11.txt
@@ -369,6 +369,16 @@ Write this in the file ~/.gtkrc and it w
 you might have to use the file ~/.gtkrc-2.0 instead, depending on your
 distribution.
 
+For GTK+ 3, an effect similar to the above can be obtained by adding the
+following snippet of CSS code to $XDG_HOME_DIR/gtk-3.0/gtk.css (usually,
+$HOME/.config/gtk-3.0/gtk.css):
+ >
+	.tooltip {
+		background-color: #ffffcc;
+		color: #000000;
+	}
+<
+
 Using Vim as a GTK+ plugin				*gui-gtk-socketid*
 
 When the GTK+ version of Vim starts up normally, it creates its own top level
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -821,6 +821,7 @@ with_x
 enable_gui
 enable_gtk2_check
 enable_gnome_check
+enable_gtk3_check
 enable_motif_check
 enable_athena_check
 enable_nextaw_check
@@ -1481,9 +1482,10 @@ Optional Features:
   --enable-hangulinput    Include Hangul input support.
   --enable-xim            Include XIM input support.
   --enable-fontset        Include X fontset output support.
-  --enable-gui=OPTS     X11 GUI default=auto OPTS=auto/no/gtk2/gnome2/motif/athena/neXtaw/photon/carbon
+  --enable-gui=OPTS     X11 GUI default=auto OPTS=auto/no/gtk2/gnome2/gtk3/motif/athena/neXtaw/photon/carbon
   --enable-gtk2-check     If auto-select GUI, check for GTK+ 2 default=yes
   --enable-gnome-check    If GTK GUI, check for GNOME default=no
+  --enable-gtk3-check     If auto-select GUI, check for GTK+ 3 default=yes
   --enable-motif-check    If auto-select GUI, check for Motif default=yes
   --enable-athena-check   If auto-select GUI, check for Athena default=yes
   --enable-nextaw-check   If auto-select GUI, check for neXtaw default=yes
@@ -4355,7 +4357,7 @@ fi
 
 
     if test "x$CARBON" = "xyes"; then
-      if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2; then
+      if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then
 	with_x=no
       fi
     fi
@@ -8606,6 +8608,9 @@ else
 $as_echo "GNOME 2.x GUI support" >&6; }
 		SKIP_GNOME=
 		SKIP_GTK2=;;
+    gtk3)	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: GTK+ 3.x GUI support" >&5
+$as_echo "GTK+ 3.x GUI support" >&6; }
+		SKIP_GTK3=;;
     motif)	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Motif GUI support" >&5
 $as_echo "Motif GUI support" >&6; }
 		SKIP_MOTIF=;;
@@ -8657,6 +8662,23 @@ fi
   fi
 fi
 
+if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for GTK+ 3" >&5
+$as_echo_n "checking whether or not to look for GTK+ 3... " >&6; }
+  # Check whether --enable-gtk3-check was given.
+if test "${enable_gtk3_check+set}" = set; then :
+  enableval=$enable_gtk3_check;
+else
+  enable_gtk3_check="yes"
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gtk3_check" >&5
+$as_echo "$enable_gtk3_check" >&6; }
+  if test "x$enable_gtk3_check" = "xno"; then
+    SKIP_GTK3=YES
+  fi
+fi
+
 if test "x$SKIP_MOTIF" != "xYES" -a "$enable_gui_canon" != "motif"; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for Motif" >&5
 $as_echo_n "checking whether or not to look for Motif... " >&6; }
@@ -8831,13 +8853,13 @@ fi
 
   if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then
   {
-    min_gtk_version=2.2.0
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
-$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
     no_gtk=""
     if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
 	  && $PKG_CONFIG --exists gtk+-2.0; then
     {
+      min_gtk_version=2.2.0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
+$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
                         GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-2.0`
       GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-2.0`
       GTK_LIBS=`$PKG_CONFIG --libs gtk+-2.0`
@@ -8848,6 +8870,23 @@ fi
       gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
 	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'`
     }
+    elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
+	  && $PKG_CONFIG --exists gtk+-3.0; then
+    {
+      min_gtk_version=2.2.0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
+$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
+
+      GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0`
+      GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0`
+      GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0`
+      gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'`
+      gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'`
+      gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'`
+    }
     else
       no_gtk=yes
     fi
@@ -8943,6 +8982,7 @@ fi
   rm -f conf.gtktest
 
     if test "x$GTK_CFLAGS" != "x"; then
+      SKIP_GTK3=YES
       SKIP_ATHENA=YES
       SKIP_NEXTAW=YES
       SKIP_MOTIF=YES
@@ -9044,6 +9084,218 @@ fi
   fi
 fi
 
+
+if test -z "$SKIP_GTK3"; then
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-gtktest argument" >&5
+$as_echo_n "checking --disable-gtktest argument... " >&6; }
+  # Check whether --enable-gtktest was given.
+if test "${enable_gtktest+set}" = set; then :
+  enableval=$enable_gtktest;
+else
+  enable_gtktest=yes
+fi
+
+  if test "x$enable_gtktest" = "xyes" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: gtk test enabled" >&5
+$as_echo "gtk test enabled" >&6; }
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: gtk test disabled" >&5
+$as_echo "gtk test disabled" >&6; }
+  fi
+
+  if test "X$PKG_CONFIG" = "X"; then
+    # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+
+  if test "x$PKG_CONFIG" != "xno"; then
+
+  if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then
+  {
+    no_gtk=""
+    if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
+	  && $PKG_CONFIG --exists gtk+-2.0; then
+    {
+      min_gtk_version=3.0.0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
+$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
+                        GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-2.0`
+      GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-2.0`
+      GTK_LIBS=`$PKG_CONFIG --libs gtk+-2.0`
+      gtk_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'`
+      gtk_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'`
+      gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'`
+    }
+    elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
+	  && $PKG_CONFIG --exists gtk+-3.0; then
+    {
+      min_gtk_version=3.0.0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
+$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
+
+      GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0`
+      GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0`
+      GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0`
+      gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'`
+      gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'`
+      gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'`
+    }
+    else
+      no_gtk=yes
+    fi
+
+    if test "x$enable_gtktest" = "xyes" -a "x$no_gtk" = "x"; then
+    {
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $GTK_CFLAGS"
+      LIBS="$LIBS $GTK_LIBS"
+
+                        rm -f conf.gtktest
+      if test "$cross_compiling" = yes; then :
+  echo $ac_n "cross compiling; assumed OK... $ac_c"
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#endif
+
+int
+main ()
+{
+int major, minor, micro;
+char *tmp_version;
+
+system ("touch conf.gtktest");
+
+/* HP/UX 9 (%@#!) writes to sscanf strings */
+tmp_version = g_strdup("$min_gtk_version");
+if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+   printf("%s, bad version string\n", "$min_gtk_version");
+   exit(1);
+ }
+
+if ((gtk_major_version > major) ||
+    ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
+    ((gtk_major_version == major) && (gtk_minor_version == minor) &&
+				     (gtk_micro_version >= micro)))
+{
+    return 0;
+}
+return 1;
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  no_gtk=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+      CFLAGS="$ac_save_CFLAGS"
+      LIBS="$ac_save_LIBS"
+    }
+    fi
+    if test "x$no_gtk" = x ; then
+      if test "x$enable_gtktest" = "xyes"; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5
+$as_echo "yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; }
+      else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5
+$as_echo "found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; }
+      fi
+      GUI_LIB_LOC="$GTK_LIBDIR"
+		 GTK_LIBNAME="$GTK_LIBS"
+		GUI_INC_LOC="$GTK_CFLAGS"
+    else
+    {
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+      GTK_CFLAGS=""
+      GTK_LIBS=""
+      :
+    }
+    fi
+  }
+  else
+    GTK_CFLAGS=""
+    GTK_LIBS=""
+    :
+  fi
+
+
+  rm -f conf.gtktest
+
+    if test "x$GTK_CFLAGS" != "x"; then
+      SKIP_GTK2=YES
+      SKIP_GNOME=YES
+      SKIP_ATHENA=YES
+      SKIP_NEXTAW=YES
+      SKIP_MOTIF=YES
+      GUITYPE=GTK
+
+      $as_echo "#define HAVE_GTK_MULTIHEAD 1" >>confdefs.h
+
+      $as_echo "#define USE_GTK3 1" >>confdefs.h
+
+    fi
+  fi
+fi
+
 if test "x$GUITYPE" = "xGTK"; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking version of Gdk-Pixbuf" >&5
 $as_echo_n "checking version of Gdk-Pixbuf... " >&6; }
@@ -9546,7 +9798,7 @@ done
 fi
 
 
-if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2"; then
+if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2" -o -z "$SKIP_GTK3"; then
     cppflags_save=$CPPFLAGS
   CPPFLAGS="$CPPFLAGS $X_CFLAGS"
   for ac_header in X11/xpm.h X11/Sunkeysym.h
--- a/src/channel.c
+++ b/src/channel.c
@@ -361,6 +361,17 @@ messageFromNetbeans(XtPointer clientData
 #endif
 
 #ifdef FEAT_GUI_GTK
+# if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+messageFromNetbeans(GIOChannel *unused1 UNUSED,
+		    GIOCondition unused2 UNUSED,
+		    gpointer clientData)
+{
+    channel_read_fd(GPOINTER_TO_INT(clientData));
+    return TRUE; /* Return FALSE instead in case the event source is to
+		  * be removed after this function returns. */
+}
+# else
     static void
 messageFromNetbeans(gpointer clientData,
 		    gint unused1 UNUSED,
@@ -368,6 +379,7 @@ messageFromNetbeans(gpointer clientData,
 {
     channel_read_fd((int)(long)clientData);
 }
+# endif
 #endif
 
     static void
@@ -388,12 +400,27 @@ channel_gui_register_one(channel_T *chan
     /* Tell gdk we are interested in being called when there
      * is input on the editor connection socket. */
     if (channel->ch_part[part].ch_inputHandler == 0)
+#   if GTK_CHECK_VERSION(3,0,0)
+    {
+	GIOChannel *chnnl = g_io_channel_unix_new(
+		(gint)channel->ch_part[part].ch_fd);
+
+	channel->ch_part[part].ch_inputHandler = g_io_add_watch(
+		chnnl,
+		G_IO_IN|G_IO_HUP|G_IO_ERR|G_IO_PRI,
+		messageFromNetbeans,
+		GINT_TO_POINTER(channel->ch_part[part].ch_fd));
+
+	g_io_channel_unref(chnnl);
+    }
+#   else
 	channel->ch_part[part].ch_inputHandler = gdk_input_add(
 		(gint)channel->ch_part[part].ch_fd,
 		(GdkInputCondition)
 			     ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION),
 		messageFromNetbeans,
 		(gpointer)(long)channel->ch_part[part].ch_fd);
+#   endif
 #  else
 #   ifdef FEAT_GUI_W32
     /* Tell Windows we are interested in receiving message when there
@@ -457,7 +484,11 @@ channel_gui_unregister(channel_T *channe
 #  ifdef FEAT_GUI_GTK
 	if (channel->ch_part[part].ch_inputHandler != 0)
 	{
+#   if GTK_CHECK_VERSION(3,0,0)
+	    g_source_remove(channel->ch_part[part].ch_inputHandler);
+#   else
 	    gdk_input_remove(channel->ch_part[part].ch_inputHandler);
+#   endif
 	    channel->ch_part[part].ch_inputHandler = 0;
 	}
 #  else
@@ -606,7 +637,7 @@ channel_open(
 	    fd_set		wfds;
 #if defined(__APPLE__) && __APPLE__ == 1
 # define PASS_RFDS
-	    fd_set          rfds;
+	    fd_set	    rfds;
 
 	    FD_ZERO(&rfds);
 	    FD_SET(sd, &rfds);
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -460,3 +460,6 @@
 
 /* Define if GResource is used to load icons */
 #undef USE_GRESOURCE
+
+/* Define if GTK+ GUI is to be linked against GTK+ 3 */
+#undef USE_GTK3
--- a/src/configure.in
+++ b/src/configure.in
@@ -213,7 +213,7 @@ if test "`(uname) 2>/dev/null`" = Darwin
     dnl or Motif, Athena or GTK GUI is used.
     AC_CHECK_HEADER(Carbon/Carbon.h, CARBON=yes)
     if test "x$CARBON" = "xyes"; then
-      if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2; then
+      if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then
 	with_x=no
       fi
     fi
@@ -2198,7 +2198,7 @@ test "x$with_x" = xno -a "x$MACOSX" != "
 
 AC_MSG_CHECKING(--enable-gui argument)
 AC_ARG_ENABLE(gui,
- [  --enable-gui[=OPTS]     X11 GUI [default=auto] [OPTS=auto/no/gtk2/gnome2/motif/athena/neXtaw/photon/carbon]], , enable_gui="auto")
+ [  --enable-gui[=OPTS]     X11 GUI [default=auto] [OPTS=auto/no/gtk2/gnome2/gtk3/motif/athena/neXtaw/photon/carbon]], , enable_gui="auto")
 
 dnl Canonicalize the --enable-gui= argument so that it can be easily compared.
 dnl Do not use character classes for portability with old tools.
@@ -2256,6 +2256,8 @@ else
     gnome2)	AC_MSG_RESULT(GNOME 2.x GUI support)
 		SKIP_GNOME=
 		SKIP_GTK2=;;
+    gtk3)	AC_MSG_RESULT(GTK+ 3.x GUI support)
+		SKIP_GTK3=;;
     motif)	AC_MSG_RESULT(Motif GUI support)
 		SKIP_MOTIF=;;
     athena)	AC_MSG_RESULT(Athena GUI support)
@@ -2291,6 +2293,17 @@ if test "x$SKIP_GNOME" != "xYES" -a "$en
   fi
 fi
 
+if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then
+  AC_MSG_CHECKING(whether or not to look for GTK+ 3)
+  AC_ARG_ENABLE(gtk3-check,
+	[  --enable-gtk3-check     If auto-select GUI, check for GTK+ 3 [default=yes]],
+	, enable_gtk3_check="yes")
+  AC_MSG_RESULT($enable_gtk3_check)
+  if test "x$enable_gtk3_check" = "xno"; then
+    SKIP_GTK3=YES
+  fi
+fi
+
 if test "x$SKIP_MOTIF" != "xYES" -a "$enable_gui_canon" != "motif"; then
   AC_MSG_CHECKING(whether or not to look for Motif)
   AC_ARG_ENABLE(motif-check,
@@ -2379,12 +2392,12 @@ AC_DEFUN(AM_PATH_GTK,
 [
   if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then
   {
-    min_gtk_version=ifelse([$1], ,2.2.0,$1)
-    AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
     no_gtk=""
     if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
 	  && $PKG_CONFIG --exists gtk+-2.0; then
     {
+      min_gtk_version=ifelse([$1], ,2.2.0,$1)
+      AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
       dnl We should be using PKG_CHECK_MODULES() instead of this hack.
       dnl But I guess the dependency on pkgconfig.m4 is not wanted or
       dnl something like that.
@@ -2398,6 +2411,22 @@ AC_DEFUN(AM_PATH_GTK,
       gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
 	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
     }
+    elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
+	  && $PKG_CONFIG --exists gtk+-3.0; then
+    {
+      min_gtk_version=ifelse([$1], ,3.0.0,$1)
+      AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
+
+      GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0`
+      GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0`
+      GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0`
+      gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+      gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+      gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+    }
     else
       no_gtk=yes
     fi
@@ -2573,6 +2602,7 @@ if test -z "$SKIP_GTK2"; then
 		 GTK_LIBNAME="$GTK_LIBS"
 		GUI_INC_LOC="$GTK_CFLAGS"], )
     if test "x$GTK_CFLAGS" != "x"; then
+      SKIP_GTK3=YES
       SKIP_ATHENA=YES
       SKIP_NEXTAW=YES
       SKIP_MOTIF=YES
@@ -2601,6 +2631,44 @@ if test -z "$SKIP_GTK2"; then
   fi
 fi
 
+
+dnl ---------------------------------------------------------------------------
+dnl Check for GTK3.
+dnl ---------------------------------------------------------------------------
+if test -z "$SKIP_GTK3"; then
+
+  AC_MSG_CHECKING(--disable-gtktest argument)
+  AC_ARG_ENABLE(gtktest, [  --disable-gtktest       Do not try to compile and run a test GTK program],
+	, enable_gtktest=yes)
+  if test "x$enable_gtktest" = "xyes" ; then
+    AC_MSG_RESULT(gtk test enabled)
+  else
+    AC_MSG_RESULT(gtk test disabled)
+  fi
+
+  if test "X$PKG_CONFIG" = "X"; then
+    AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+  fi
+
+  if test "x$PKG_CONFIG" != "xno"; then
+    AM_PATH_GTK(3.0.0,
+		[GUI_LIB_LOC="$GTK_LIBDIR"
+		 GTK_LIBNAME="$GTK_LIBS"
+		GUI_INC_LOC="$GTK_CFLAGS"], )
+    if test "x$GTK_CFLAGS" != "x"; then
+      SKIP_GTK2=YES
+      SKIP_GNOME=YES
+      SKIP_ATHENA=YES
+      SKIP_NEXTAW=YES
+      SKIP_MOTIF=YES
+      GUITYPE=GTK
+      AC_SUBST(GTK_LIBNAME)
+      AC_DEFINE(HAVE_GTK_MULTIHEAD)
+      AC_DEFINE(USE_GTK3)
+    fi
+  fi
+fi
+
 dnl Check the version of Gdk-Pixbuf.  If the version is 2.31 or later and
 dnl glib-compile-resources is found in PATH, use GResource.
 if test "x$GUITYPE" = "xGTK"; then
@@ -2823,7 +2891,7 @@ if test "$enable_xsmp" = "yes"; then
 fi
 
 
-if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2"; then
+if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2" -o -z "$SKIP_GTK3"; then
   dnl Check for X11/xpm.h and X11/Sunkeysym.h with the GUI include path
   cppflags_save=$CPPFLAGS
   CPPFLAGS="$CPPFLAGS $X_CFLAGS"
--- a/src/eval.c
+++ b/src/eval.c
@@ -13671,7 +13671,11 @@ f_has(typval_T *argvars, typval_T *rettv
 #endif
 #ifdef FEAT_GUI_GTK
 	"gui_gtk",
+# ifdef USE_GTK3
+	"gui_gtk3",
+# else
 	"gui_gtk2",
+# endif
 #endif
 #ifdef FEAT_GUI_GNOME
 	"gui_gnome",
--- a/src/gui.h
+++ b/src/gui.h
@@ -359,7 +359,9 @@ typedef struct Gui
 #endif
 
 #ifdef FEAT_GUI_GTK
+# ifndef USE_GTK3
     int		visibility;	    /* Is shell partially/fully obscured? */
+# endif
     GdkCursor	*blank_pointer;	    /* Blank pointer */
 
     /* X Resources */
@@ -381,7 +383,12 @@ typedef struct Gui
     GdkColor	*fgcolor;	    /* GDK-styled foreground color */
     GdkColor	*bgcolor;	    /* GDK-styled background color */
     GdkColor	*spcolor;	    /* GDK-styled special color */
+# ifdef USE_GTK3
+    cairo_surface_t *surface;       /* drawarea surface */
+    gboolean	     by_signal;     /* cause of draw operation */
+# else
     GdkGC	*text_gc;	    /* cached GC for normal text */
+# endif
     PangoContext     *text_context; /* the context used for all text */
     PangoFont	     *ascii_font;   /* cached font for ASCII strings */
     PangoGlyphString *ascii_glyphs; /* cached code point -> glyph map */
--- a/src/gui_beval.c
+++ b/src/gui_beval.c
@@ -122,7 +122,11 @@ general_beval_cb(BalloonEval *beval, int
 #if !defined(FEAT_GUI_W32) || defined(PROTO)
 
 #ifdef FEAT_GUI_GTK
-# include <gdk/gdkkeysyms.h>
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # include <gtk/gtk.h>
 #else
 # include <X11/keysym.h>
@@ -164,8 +168,16 @@ static gint target_event_cb(GtkWidget *,
 static gint mainwin_event_cb(GtkWidget *, GdkEvent *, gpointer);
 static void pointer_event(BalloonEval *, int, int, unsigned);
 static void key_event(BalloonEval *, unsigned, int);
+# if GTK_CHECK_VERSION(3,0,0)
+static gboolean timeout_cb(gpointer);
+# else
 static gint timeout_cb(gpointer);
-static gint balloon_expose_event_cb(GtkWidget *, GdkEventExpose *, gpointer);
+# endif
+# if GTK_CHECK_VERSION(3,0,0)
+static gboolean balloon_draw_event_cb (GtkWidget *, cairo_t *, gpointer);
+# else
+static gint balloon_expose_event_cb (GtkWidget *, GdkEventExpose *, gpointer);
+# endif
 #else
 static void addEventHandler(Widget, BalloonEval *);
 static void removeEventHandler(BalloonEval *);
@@ -459,10 +471,16 @@ addEventHandler(GtkWidget *target, Ballo
      * This allows us to catch events independently of the signal handlers
      * in gui_gtk_x11.c.
      */
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(target), "event",
+		     G_CALLBACK(target_event_cb),
+		     beval);
+# else
     /* Should use GTK_OBJECT() here, but that causes a lint warning... */
     gtk_signal_connect((GtkObject*)(target), "event",
 		       GTK_SIGNAL_FUNC(target_event_cb),
 		       beval);
+# endif
     /*
      * Nasty:  Key press events go to the main window thus the drawing area
      * will never see them.  This means we have to connect to the main window
@@ -471,9 +489,15 @@ addEventHandler(GtkWidget *target, Ballo
     if (gtk_socket_id == 0 && gui.mainwin != NULL
 	    && gtk_widget_is_ancestor(target, gui.mainwin))
     {
+# if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.mainwin), "event",
+			 G_CALLBACK(mainwin_event_cb),
+			 beval);
+# else
 	gtk_signal_connect((GtkObject*)(gui.mainwin), "event",
 			   GTK_SIGNAL_FUNC(mainwin_event_cb),
 			   beval);
+# endif
     }
 }
 
@@ -481,17 +505,29 @@ addEventHandler(GtkWidget *target, Ballo
 removeEventHandler(BalloonEval *beval)
 {
     /* LINTED: avoid warning: dubious operation on enum */
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_handlers_disconnect_by_func(G_OBJECT(beval->target),
+					 G_CALLBACK(target_event_cb),
+					 beval);
+# else
     gtk_signal_disconnect_by_func((GtkObject*)(beval->target),
 				  GTK_SIGNAL_FUNC(target_event_cb),
 				  beval);
+# endif
 
     if (gtk_socket_id == 0 && gui.mainwin != NULL
 	    && gtk_widget_is_ancestor(beval->target, gui.mainwin))
     {
 	/* LINTED: avoid warning: dubious operation on enum */
+# if GTK_CHECK_VERSION(3,0,0)
+	g_signal_handlers_disconnect_by_func(G_OBJECT(gui.mainwin),
+					     G_CALLBACK(mainwin_event_cb),
+					     beval);
+# else
 	gtk_signal_disconnect_by_func((GtkObject*)(gui.mainwin),
 				      GTK_SIGNAL_FUNC(mainwin_event_cb),
 				      beval);
+# endif
     }
 }
 
@@ -517,7 +553,17 @@ target_event_cb(GtkWidget *widget, GdkEv
 		 * GDK_POINTER_MOTION_HINT_MASK is set, thus we cannot obtain
 		 * the coordinates from the GdkEventMotion struct directly.
 		 */
+# if GTK_CHECK_VERSION(3,0,0)
+		{
+		    GdkWindow * const win = gtk_widget_get_window(widget);
+		    GdkDisplay * const dpy = gdk_window_get_display(win);
+		    GdkDeviceManager * const mngr = gdk_display_get_device_manager(dpy);
+		    GdkDevice * const dev = gdk_device_manager_get_client_pointer(mngr);
+		    gdk_window_get_device_position(win, dev , &x, &y, &state);
+		}
+# else
 		gdk_window_get_pointer(widget->window, &x, &y, &state);
+# endif
 		pointer_event(beval, x, y, (unsigned int)state);
 	    }
 	    else
@@ -609,8 +655,13 @@ pointer_event(BalloonEval *beval, int x,
 	    }
 	    else
 	    {
+# if GTK_CHECK_VERSION(3,0,0)
+		beval->timerID = g_timeout_add((guint)p_bdlay,
+					       &timeout_cb, beval);
+# else
 		beval->timerID = gtk_timeout_add((guint32)p_bdlay,
 						 &timeout_cb, beval);
+# endif
 	    }
 	}
     }
@@ -647,7 +698,11 @@ key_event(BalloonEval *beval, unsigned k
 	cancelBalloon(beval);
 }
 
+# if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+# else
     static gint
+# endif
 timeout_cb(gpointer data)
 {
     BalloonEval *beval = (BalloonEval *)data;
@@ -663,6 +718,37 @@ timeout_cb(gpointer data)
     return FALSE; /* don't call me again */
 }
 
+# if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+balloon_draw_event_cb(GtkWidget *widget,
+		      cairo_t	*cr,
+		      gpointer	 data UNUSED)
+{
+    GtkStyleContext *context = NULL;
+    gint width = -1, height = -1;
+
+    if (widget == NULL)
+	return TRUE;
+
+    context = gtk_widget_get_style_context(widget);
+    width = gtk_widget_get_allocated_width(widget);
+    height = gtk_widget_get_allocated_height(widget);
+
+    gtk_style_context_save(context);
+
+    gtk_style_context_add_class(context, "tooltip");
+    gtk_style_context_set_state(context, GTK_STATE_FLAG_NORMAL);
+
+    cairo_save(cr);
+    gtk_render_frame(context, cr, 0, 0, width, height);
+    gtk_render_background(context, cr, 0, 0, width, height);
+    cairo_restore(cr);
+
+    gtk_style_context_restore(context);
+
+    return FALSE;
+}
+# else
     static gint
 balloon_expose_event_cb(GtkWidget *widget,
 			GdkEventExpose *event,
@@ -675,6 +761,7 @@ balloon_expose_event_cb(GtkWidget *widge
 
     return FALSE; /* continue emission */
 }
+# endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 #else /* !FEAT_GUI_GTK */
 
@@ -957,8 +1044,37 @@ set_printable_label_text(GtkLabel *label
 	aep = syn_gui_attr2entry(hl_attr(HLF_8));
 	pixel = (aep != NULL) ? aep->ae_u.gui.fg_color : INVALCOLOR;
 	if (pixel != INVALCOLOR)
+# if GTK_CHECK_VERSION(3,0,0)
+	{
+	    GdkVisual * const visual = gtk_widget_get_visual(gui.drawarea);
+
+	    if (visual == NULL)
+	    {
+		color.red = 0;
+		color.green = 0;
+		color.blue = 0;
+	    }
+	    else
+	    {
+		guint32 r_mask, g_mask, b_mask;
+		gint r_shift, g_shift, b_shift;
+
+		gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift,
+						 NULL);
+		gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift,
+						   NULL);
+		gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift,
+						  NULL);
+
+		color.red = ((pixel & r_mask) >> r_shift) / 255.0 * 65535;
+		color.green = ((pixel & g_mask) >> g_shift) / 255.0 * 65535;
+		color.blue = ((pixel & b_mask) >> b_shift) / 255.0 * 65535;
+	    }
+	}
+# else
 	    gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea),
 				     (unsigned long)pixel, &color);
+# endif
 
 	pdest = buf;
 	p = text;
@@ -1059,8 +1175,10 @@ drawBalloon(BalloonEval *beval)
 	screen_w = gdk_screen_width();
 	screen_h = gdk_screen_height();
 # endif
+# if !GTK_CHECK_VERSION(3,0,0)
 	gtk_widget_ensure_style(beval->balloonShell);
 	gtk_widget_ensure_style(beval->balloonLabel);
+# endif
 
 	set_printable_label_text(GTK_LABEL(beval->balloonLabel), beval->msg);
 	/*
@@ -1081,10 +1199,18 @@ drawBalloon(BalloonEval *beval)
 				    MAX(20, screen_w - 20)));
 
 	/* Calculate the balloon's width and height. */
+# if GTK_CHECK_VERSION(3,0,0)
+	gtk_widget_get_preferred_size(beval->balloonShell, &requisition, NULL);
+# else
 	gtk_widget_size_request(beval->balloonShell, &requisition);
+# endif
 
 	/* Compute position of the balloon area */
+# if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_get_origin(gtk_widget_get_window(beval->target), &x, &y);
+# else
 	gdk_window_get_origin(beval->target->window, &x, &y);
+# endif
 	x += beval->x;
 	y += beval->y;
 
@@ -1099,7 +1225,11 @@ drawBalloon(BalloonEval *beval)
 	y = CLAMP(y + y_offset, 0, MAX(0, screen_h - requisition.height));
 
 	/* Show the balloon */
+# if GTK_CHECK_VERSION(3,0,0)
+	gtk_window_move(GTK_WINDOW(beval->balloonShell), x, y);
+# else
 	gtk_widget_set_uposition(beval->balloonShell, x, y);
+# endif
 	gtk_widget_show(beval->balloonShell);
 
 	beval->showState = ShS_SHOWING;
@@ -1126,7 +1256,11 @@ cancelBalloon(BalloonEval *beval)
 
     if (beval->timerID != 0)
     {
+# if GTK_CHECK_VERSION(3,0,0)
+	g_source_remove(beval->timerID);
+# else
 	gtk_timeout_remove(beval->timerID);
+# endif
 	beval->timerID = 0;
     }
     beval->showState = ShS_NEUTRAL;
@@ -1138,17 +1272,42 @@ createBalloonEvalWindow(BalloonEval *bev
     beval->balloonShell = gtk_window_new(GTK_WINDOW_POPUP);
 
     gtk_widget_set_app_paintable(beval->balloonShell, TRUE);
+# if GTK_CHECK_VERSION(3,0,0)
+    gtk_window_set_resizable(GTK_WINDOW(beval->balloonShell), FALSE);
+# else
     gtk_window_set_policy(GTK_WINDOW(beval->balloonShell), FALSE, FALSE, TRUE);
+# endif
     gtk_widget_set_name(beval->balloonShell, "gtk-tooltips");
+# if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(beval->balloonShell), 4);
+# else
     gtk_container_border_width(GTK_CONTAINER(beval->balloonShell), 4);
+# endif
 
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(beval->balloonShell), "draw",
+		     G_CALLBACK(balloon_draw_event_cb), NULL);
+# else
     gtk_signal_connect((GtkObject*)(beval->balloonShell), "expose_event",
 		       GTK_SIGNAL_FUNC(balloon_expose_event_cb), NULL);
+# endif
     beval->balloonLabel = gtk_label_new(NULL);
 
     gtk_label_set_line_wrap(GTK_LABEL(beval->balloonLabel), FALSE);
     gtk_label_set_justify(GTK_LABEL(beval->balloonLabel), GTK_JUSTIFY_LEFT);
+# if GTK_CHECK_VERSION(3,16,0)
+    gtk_label_set_xalign(GTK_LABEL(beval->balloonLabel), 0.5);
+    gtk_label_set_yalign(GTK_LABEL(beval->balloonLabel), 0.5);
+# elif GTK_CHECK_VERSION(3,14,0)
+    GValue align_val = G_VALUE_INIT;
+    g_value_init(&align_val, G_TYPE_FLOAT);
+    g_value_set_float(&align_val, 0.5);
+    g_object_set_property(G_OBJECT(beval->balloonLabel), "xalign", &align_val);
+    g_object_set_property(G_OBJECT(beval->balloonLabel), "yalign", &align_val);
+    g_value_unset(&align_val);
+# else
     gtk_misc_set_alignment(GTK_MISC(beval->balloonLabel), 0.5f, 0.5f);
+# endif
     gtk_widget_set_name(beval->balloonLabel, "vim-balloon-label");
     gtk_widget_show(beval->balloonLabel);
 
--- a/src/gui_beval.h
+++ b/src/gui_beval.h
@@ -11,7 +11,11 @@
 #define GUI_BEVAL_H
 
 #ifdef FEAT_GUI_GTK
-# include <gtk/gtkwidget.h>
+# ifdef USE_GTK3
+#  include <gtk/gtk.h>
+# else
+#  include <gtk/gtkwidget.h>
+# endif
 #else
 # if defined(FEAT_GUI_X11)
 #  include <X11/Intrinsic.h>
--- a/src/gui_gtk.c
+++ b/src/gui_gtk.c
@@ -22,8 +22,17 @@
  *
  * Best supporting actor (He helped somewhat, aesthetically speaking):
  * Maxime Romano <verbophobe@hotmail.com>
+ *
+ * Support for GTK+ 3 was added by:
+ *
+ * 2016  Kazunobu Kuriyama  <kazunobu.kuriyama@gmail.com>
+ *
+ * With the help of Marius Gedminas and the word of Bram Moolenaar,
+ * "Let's give this some time to mature."
  */
 
+#include "vim.h"
+
 #ifdef FEAT_GUI_GTK
 # include "gui_gtk_f.h"
 #endif
@@ -37,8 +46,6 @@
 # undef MAX
 #endif
 
-#include "vim.h"
-
 #ifdef FEAT_GUI_GNOME
 /* Gnome redefines _() and N_().  Grrr... */
 # ifdef _
@@ -63,7 +70,11 @@
 #endif
 
 #ifdef FEAT_GUI_GTK
-# include <gdk/gdkkeysyms.h>
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # include <gdk/gdk.h>
 # ifdef WIN3264
 #  include <gdk/gdkwin32.h>
@@ -104,6 +115,70 @@ static void recent_func_log_func(
  * match toolbar_names[] in menu.c!  All stock icons including the "vim-*"
  * ones can be overridden in your gtkrc file.
  */
+# if GTK_CHECK_VERSION(3,10,0)
+static const char * const menu_themed_names[] =
+{
+    /* 00 */ "document-new",		/* sub. GTK_STOCK_NEW */
+    /* 01 */ "document-open",		/* sub. GTK_STOCK_OPEN */
+    /* 02 */ "document-save",		/* sub. GTK_STOCK_SAVE */
+    /* 03 */ "edit-undo",		/* sub. GTK_STOCK_UNDO */
+    /* 04 */ "edit-redo",		/* sub. GTK_STOCK_REDO */
+    /* 05 */ "edit-cut",		/* sub. GTK_STOCK_CUT */
+    /* 06 */ "edit-copy",		/* sub. GTK_STOCK_COPY */
+    /* 07 */ "edit-paste",		/* sub. GTK_STOCK_PASTE */
+    /* 08 */ "document-print",		/* sub. GTK_STOCK_PRINT */
+    /* 09 */ "help-browser",		/* sub. GTK_STOCK_HELP */
+    /* 10 */ "edit-find",		/* sub. GTK_STOCK_FIND */
+#  if GTK_CHECK_VERSION(3,14,0)
+    /* Use the file names in gui_gtk_res.xml, cutting off the extension.
+     * Similar changes follow. */
+    /* 11 */ "stock_vim_save_all",
+    /* 12 */ "stock_vim_session_save",
+    /* 13 */ "stock_vim_session_new",
+    /* 14 */ "stock_vim_session_load",
+#  else
+    /* 11 */ "vim-save-all",
+    /* 12 */ "vim-session-save",
+    /* 13 */ "vim-session-new",
+    /* 14 */ "vim-session-load",
+#  endif
+    /* 15 */ "system-run",		/* sub. GTK_STOCK_EXECUTE */
+    /* 16 */ "edit-find-replace",	/* sub. GTK_STOCK_FIND_AND_REPLACE */
+    /* 17 */ "window-close",		/* sub. GTK_STOCK_CLOSE, FIXME: fuzzy */
+#  if GTK_CHECK_VERSION(3,14,0)
+    /* 18 */ "stock_vim_window_maximize",
+    /* 19 */ "stock_vim_window_minimize",
+    /* 20 */ "stock_vim_window_split",
+    /* 21 */ "stock_vim_shell",
+#  else
+    /* 18 */ "vim-window-maximize",
+    /* 19 */ "vim-window-minimize",
+    /* 20 */ "vim-window-split",
+    /* 21 */ "vim-shell",
+#  endif
+    /* 22 */ "go-previous",		/* sub. GTK_STOCK_GO_BACK */
+    /* 23 */ "go-next",			/* sub. GTK_STOCK_GO_FORWARD */
+#  if GTK_CHECK_VERSION(3,14,0)
+    /* 24 */ "stock_vim_find_help",
+#  else
+    /* 24 */ "vim-find-help",
+#  endif
+    /* 25 */ "gtk-convert",		/* sub. GTK_STOCK_CONVERT */
+    /* 26 */ "go-jump",			/* sub. GTK_STOCK_JUMP_TO */
+#  if GTK_CHECK_VERSION(3,14,0)
+    /* 27 */ "stock_vim_build_tags",
+    /* 28 */ "stock_vim_window_split_vertical",
+    /* 29 */ "stock_vim_window_maximize_width",
+    /* 30 */ "stock_vim_window_minimize_width",
+#  else
+    /* 27 */ "vim-build-tags",
+    /* 28 */ "vim-window-split-vertical",
+    /* 29 */ "vim-window-maximize-width",
+    /* 30 */ "vim-window-minimize-width",
+#  endif
+    /* 31 */ "application-exit",	/* GTK_STOCK_QUIT */
+};
+# else /* !GTK_CHECK_VERSION(3,10,0) */
 static const char * const menu_stock_ids[] =
 {
     /* 00 */ GTK_STOCK_NEW,
@@ -139,8 +214,10 @@ static const char * const menu_stock_ids
     /* 30 */ "vim-window-minimize-width",
     /* 31 */ GTK_STOCK_QUIT
 };
+# endif /* !GTK_CHECK_VERSION(3,10,0) */
 
-#ifdef USE_GRESOURCE
+# ifdef USE_GRESOURCE
+#  if !GTK_CHECK_VERSION(3,10,0)
 typedef struct IconNames {
     const char *icon_name;
     const char *file_name;
@@ -162,9 +239,10 @@ static IconNames stock_vim_icons[] = {
     { "vim-window-split-vertical", "stock_vim_window_split_vertical.png" },
     { NULL, NULL }
 };
-#endif
+#  endif
+# endif /* USE_G_RESOURCE */
 
-#ifndef USE_GRESOURCE
+# ifndef USE_GRESOURCE
     static void
 add_stock_icon(GtkIconFactory	*factory,
 	       const char	*stock_id,
@@ -182,7 +260,7 @@ add_stock_icon(GtkIconFactory	*factory,
     gtk_icon_set_unref(icon_set);
     g_object_unref(pixbuf);
 }
-#endif
+# endif
 
     static int
 lookup_menu_iconfile(char_u *iconfile, char_u *dest)
@@ -214,6 +292,52 @@ lookup_menu_iconfile(char_u *iconfile, c
 load_menu_iconfile(char_u *name, GtkIconSize icon_size)
 {
     GtkWidget	    *image = NULL;
+# if GTK_CHECK_VERSION(3,10,0)
+    int		     pixel_size = -1;
+
+    switch (icon_size)
+    {
+	case GTK_ICON_SIZE_MENU:
+	    pixel_size = 16;
+	    break;
+	case GTK_ICON_SIZE_SMALL_TOOLBAR:
+	    pixel_size = 16;
+	    break;
+	case GTK_ICON_SIZE_LARGE_TOOLBAR:
+	    pixel_size = 24;
+	    break;
+	case GTK_ICON_SIZE_BUTTON:
+	    pixel_size = 16;
+	    break;
+	case GTK_ICON_SIZE_DND:
+	    pixel_size = 32;
+	    break;
+	case GTK_ICON_SIZE_DIALOG:
+	    pixel_size = 48;
+	    break;
+	case GTK_ICON_SIZE_INVALID:
+	    /* FALLTHROUGH */
+	default:
+	    pixel_size = 0;
+	    break;
+    }
+
+    if (pixel_size > 0 || pixel_size == -1)
+    {
+	GdkPixbuf * const pixbuf
+	    = gdk_pixbuf_new_from_file_at_scale((const char *)name,
+		pixel_size, pixel_size, TRUE, NULL);
+	if (pixbuf != NULL)
+	{
+	    image = gtk_image_new_from_pixbuf(pixbuf);
+	    g_object_unref(pixbuf);
+	}
+    }
+    if (image == NULL)
+	image = gtk_image_new_from_icon_name("image-missing", icon_size);
+
+    return image;
+# else /* !GTK_CHECK_VERSION(3,10,0) */
     GtkIconSet	    *icon_set;
     GtkIconSource   *icon_source;
 
@@ -234,6 +358,7 @@ load_menu_iconfile(char_u *name, GtkIcon
     gtk_icon_set_unref(icon_set);
 
     return image;
+# endif /* !GTK_CHECK_VERSION(3,10,0) */
 }
 
     static GtkWidget *
@@ -254,6 +379,17 @@ create_menu_icon(vimmenu_T *menu, GtkIco
     /* Still not found?  Then use a builtin icon, a blank one as fallback. */
     if (image == NULL)
     {
+# if GTK_CHECK_VERSION(3,10,0)
+	const char *icon_name = NULL;
+	const int   n_names = G_N_ELEMENTS(menu_themed_names);
+
+	if (menu->iconidx >= 0 && menu->iconidx < n_names)
+	    icon_name = menu_themed_names[menu->iconidx];
+	if (icon_name == NULL)
+	    icon_name = "image-missing";
+
+	image = gtk_image_new_from_icon_name(icon_name, icon_size);
+# else
 	const char  *stock_id;
 	const int   n_ids = G_N_ELEMENTS(menu_stock_ids);
 
@@ -263,6 +399,7 @@ create_menu_icon(vimmenu_T *menu, GtkIco
 	    stock_id = GTK_STOCK_MISSING_IMAGE;
 
 	image = gtk_image_new_from_stock(stock_id, icon_size);
+# endif
     }
 
     return image;
@@ -288,12 +425,12 @@ toolbar_button_focus_in_event(GtkWidget 
     void
 gui_gtk_register_stock_icons(void)
 {
-#ifndef USE_GRESOURCE
-# include "../pixmaps/stock_icons.h"
+# ifndef USE_GRESOURCE
+#  include "../pixmaps/stock_icons.h"
     GtkIconFactory *factory;
 
     factory = gtk_icon_factory_new();
-# define ADD_ICON(Name, Data) add_stock_icon(factory, Name, Data, (int)sizeof(Data))
+#  define ADD_ICON(Name, Data) add_stock_icon(factory, Name, Data, (int)sizeof(Data))
 
     ADD_ICON("vim-build-tags",		  stock_vim_build_tags);
     ADD_ICON("vim-find-help",		  stock_vim_find_help);
@@ -309,35 +446,91 @@ gui_gtk_register_stock_icons(void)
     ADD_ICON("vim-window-split",	  stock_vim_window_split);
     ADD_ICON("vim-window-split-vertical", stock_vim_window_split_vertical);
 
-# undef ADD_ICON
-#else
-    GtkIconFactory * const factory = gtk_icon_factory_new();
+#  undef ADD_ICON
+
+    gtk_icon_factory_add_default(factory);
+    g_object_unref(factory);
+# else /* defined(USE_GRESOURCE) */
     const char * const path_prefix = "/org/vim/gui/icon";
+#  if GTK_CHECK_VERSION(3,14,0)
+    GdkScreen    *screen = NULL;
+    GtkIconTheme *icon_theme = NULL;
+
+    if (GTK_IS_WIDGET(gui.mainwin))
+	screen = gtk_widget_get_screen(gui.mainwin);
+    else
+	screen = gdk_screen_get_default();
+    icon_theme = gtk_icon_theme_get_for_screen(screen);
+    gtk_icon_theme_add_resource_path(icon_theme, path_prefix);
+#  elif GTK_CHECK_VERSION(3,0,0)
     IconNames *names;
 
     for (names = stock_vim_icons; names->icon_name != NULL; names++)
     {
-        char path[MAXPATHL];
-        GdkPixbuf *pixbuf;
+	char path[MAXPATHL];
 
-        vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
-        pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
-        if (pixbuf != NULL)
-        {
-            GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
-            gtk_icon_factory_add(factory, names->icon_name, icon_set);
-            gtk_icon_set_unref(icon_set);
-            g_object_unref(pixbuf);
-        }
+	vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
+	GdkPixbuf *pixbuf = NULL;
+	pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
+	if (pixbuf != NULL)
+	{
+	    const gint size = MAX(gdk_pixbuf_get_width(pixbuf),
+				  gdk_pixbuf_get_height(pixbuf));
+	    if (size > 16)
+	    {
+		/* An icon theme is supposed to provide fixed-size
+		 * image files for each size, e.g., 16, 22, 24, ...
+		 * Naturally, in contrast to GtkIconSet, GtkIconTheme
+		 * won't prepare size variants for us out of a single
+		 * fixed-size image.
+		 *
+		 * Currently, Vim provides 24x24 images only while the
+		 * icon size on the menu and the toolbar is set to 16x16
+		 * by default.
+		 *
+		 * Resize them by ourselves until we have our own fully
+		 * fledged icon theme. */
+		GdkPixbuf *src = pixbuf;
+		pixbuf = gdk_pixbuf_scale_simple(src,
+						 16, 16,
+						 GDK_INTERP_BILINEAR);
+		if (pixbuf == NULL)
+		    pixbuf = src;
+		else
+		    g_object_unref(src);
+	    }
+	    gtk_icon_theme_add_builtin_icon(names->icon_name, size, pixbuf);
+	    g_object_unref(pixbuf);
+	}
     }
-#endif
+#  else /* !GTK_CHECK_VERSION(3,0.0) */
+    GtkIconFactory * const factory = gtk_icon_factory_new();
+    IconNames *names;
+
+    for (names = stock_vim_icons; names->icon_name != NULL; names++)
+    {
+	char path[MAXPATHL];
+	GdkPixbuf *pixbuf;
+
+	vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
+	pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
+	if (pixbuf != NULL)
+	{
+	    GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
+	    gtk_icon_factory_add(factory, names->icon_name, icon_set);
+	    gtk_icon_set_unref(icon_set);
+	    g_object_unref(pixbuf);
+	}
+    }
+
     gtk_icon_factory_add_default(factory);
     g_object_unref(factory);
+#  endif /* !GTK_CHECK_VERSION(3,0,0) */
+# endif /* defined(USE_GRESOURCE) */
 }
 
 #endif /* FEAT_TOOLBAR */
 
-
 #if defined(FEAT_MENU) || defined(PROTO)
 
 /*
@@ -408,7 +601,12 @@ menu_item_new(vimmenu_T *menu, GtkWidget
      * changes to Vim's menu system.  Not to mention that all the translations
      * had to be updated. */
     menu->id = gtk_menu_item_new();
+# if GTK_CHECK_VERSION(3,2,0)
+    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 20);
+    gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
+# else
     box = gtk_hbox_new(FALSE, 20);
+# endif
 
     use_mnemonic = (p_wak[0] != 'n' || !GTK_IS_MENU_BAR(parent_widget));
     text = translate_mnemonic_tag(menu->name, use_mnemonic);
@@ -465,10 +663,17 @@ gui_mch_add_menu(vimmenu_T *menu, int id
     gtk_menu_set_accel_group(GTK_MENU(menu->submenu_id), gui.accel_group);
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->id), menu->submenu_id);
 
+# if !GTK_CHECK_VERSION(3,4,0)
     menu->tearoff_handle = gtk_tearoff_menu_item_new();
     if (vim_strchr(p_go, GO_TEAROFF) != NULL)
 	gtk_widget_show(menu->tearoff_handle);
+#  if GTK_CHECK_VERSION(3,0,0)
+    gtk_menu_shell_prepend(GTK_MENU_SHELL(menu->submenu_id),
+	    menu->tearoff_handle);
+#  else
     gtk_menu_prepend(GTK_MENU(menu->submenu_id), menu->tearoff_handle);
+#  endif
+# endif
 }
 
     static void
@@ -494,7 +699,17 @@ gui_mch_add_menu_item(vimmenu_T *menu, i
 
 	if (menu_is_separator(menu->name))
 	{
+#  if GTK_CHECK_VERSION(3,0,0)
+	    GtkToolItem *item = gtk_separator_tool_item_new();
+	    gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(item),
+		    TRUE);
+	    gtk_tool_item_set_expand(GTK_TOOL_ITEM(item), FALSE);
+	    gtk_widget_show(GTK_WIDGET(item));
+
+	    gtk_toolbar_insert(toolbar, item, idx);
+#  else
 	    gtk_toolbar_insert_space(toolbar, idx);
+#  endif
 	    menu->id = NULL;
 	}
 	else
@@ -509,6 +724,24 @@ gui_mch_add_menu_item(vimmenu_T *menu, i
 		 * a nasty GTK error message, skip the tooltip. */
 		CONVERT_TO_UTF8_FREE(tooltip);
 
+#  if GTK_CHECK_VERSION(3,0,0)
+	    {
+		GtkWidget *icon;
+		GtkToolItem *item;
+
+		icon = create_menu_icon(menu,
+			gtk_toolbar_get_icon_size(toolbar));
+		item = gtk_tool_button_new(icon, (const gchar *)text);
+		gtk_tool_item_set_tooltip_text(item, (const gchar *)tooltip);
+		g_signal_connect(G_OBJECT(item), "clicked",
+			G_CALLBACK(&menu_item_activate), menu);
+		gtk_widget_show_all(GTK_WIDGET(item));
+
+		gtk_toolbar_insert(toolbar, item, idx);
+
+		menu->id = GTK_WIDGET(item);
+	    }
+#  else
 	    menu->id = gtk_toolbar_insert_item(
 		    toolbar,
 		    (const char *)text,
@@ -518,10 +751,16 @@ gui_mch_add_menu_item(vimmenu_T *menu, i
 		    G_CALLBACK(&menu_item_activate),
 		    menu,
 		    idx);
+#  endif
 
 	    if (gtk_socket_id != 0)
+#  if GTK_CHECK_VERSION(3,0,0)
+		g_signal_connect(G_OBJECT(menu->id), "focus-in-event",
+			G_CALLBACK(toolbar_button_focus_in_event), NULL);
+#  else
 		gtk_signal_connect(GTK_OBJECT(menu->id), "focus_in_event",
 			GTK_SIGNAL_FUNC(toolbar_button_focus_in_event), NULL);
+#  endif
 
 	    CONVERT_TO_UTF8_FREE(text);
 	    CONVERT_TO_UTF8_FREE(tooltip);
@@ -545,7 +784,12 @@ gui_mch_add_menu_item(vimmenu_T *menu, i
 	    menu->id = gtk_menu_item_new();
 	    gtk_widget_set_sensitive(menu->id, FALSE);
 	    gtk_widget_show(menu->id);
+# if GTK_CHECK_VERSION(3,0,0)
+	    gtk_menu_shell_insert(GTK_MENU_SHELL(parent->submenu_id),
+		    menu->id, idx);
+# else
 	    gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
+# endif
 
 	    return;
 	}
@@ -553,11 +797,21 @@ gui_mch_add_menu_item(vimmenu_T *menu, i
 	/* Add textual menu item. */
 	menu_item_new(menu, parent->submenu_id);
 	gtk_widget_show(menu->id);
+# if GTK_CHECK_VERSION(3,0,0)
+	gtk_menu_shell_insert(GTK_MENU_SHELL(parent->submenu_id),
+		menu->id, idx);
+# else
 	gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
+# endif
 
 	if (menu->id != NULL)
+# if GTK_CHECK_VERSION(3,0,0)
+	    g_signal_connect(G_OBJECT(menu->id), "activate",
+			     G_CALLBACK(menu_item_activate), menu);
+# else
 	    gtk_signal_connect(GTK_OBJECT(menu->id), "activate",
 			       GTK_SIGNAL_FUNC(menu_item_activate), menu);
+# endif
     }
 }
 #endif /* FEAT_MENU */
@@ -592,6 +846,7 @@ gui_gtk_set_mnemonics(int enable)
     }
 }
 
+# if !GTK_CHECK_VERSION(3,4,0)
     static void
 recurse_tearoffs(vimmenu_T *menu, int val)
 {
@@ -608,12 +863,21 @@ recurse_tearoffs(vimmenu_T *menu, int va
 	recurse_tearoffs(menu->children, val);
     }
 }
+# endif
 
+# if GTK_CHECK_VERSION(3,4,0)
+    void
+gui_mch_toggle_tearoffs(int enable UNUSED)
+{
+    /* Do nothing */
+}
+# else
     void
 gui_mch_toggle_tearoffs(int enable)
 {
     recurse_tearoffs(root_menu, enable);
 }
+# endif
 #endif /* FEAT_MENU */
 
 #if defined(FEAT_TOOLBAR)
@@ -644,10 +908,15 @@ gui_mch_menu_set_tip(vimmenu_T *menu)
 	char_u *tooltip;
 
 	tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
-	if (tooltip == NULL || utf_valid_string(tooltip, NULL))
+	if (tooltip != NULL && utf_valid_string(tooltip, NULL))
+# if GTK_CHECK_VERSION(3,0,0)
 	    /* Only set the tooltip when it's valid utf-8. */
-	gtk_tooltips_set_tip(GTK_TOOLBAR(gui.toolbar)->tooltips,
-			     menu->id, (const char *)tooltip, NULL);
+	    gtk_widget_set_tooltip_text(menu->id, (const gchar *)tooltip);
+# else
+	    /* Only set the tooltip when it's valid utf-8. */
+	    gtk_tooltips_set_tip(GTK_TOOLBAR(gui.toolbar)->tooltips,
+				 menu->id, (const char *)tooltip, NULL);
+# endif
 	CONVERT_TO_UTF8_FREE(tooltip);
     }
 }
@@ -676,8 +945,20 @@ gui_mch_destroy_menu(vimmenu_T *menu)
     if (menu->parent != NULL && menu_is_toolbar(menu->parent->name))
     {
 	if (menu_is_separator(menu->name))
+#  if GTK_CHECK_VERSION(3,0,0)
+	{
+	    GtkToolItem *item = NULL;
+
+	    item = gtk_toolbar_get_nth_item(GTK_TOOLBAR(gui.toolbar),
+					    get_menu_position(menu));
+	    if (item != NULL)
+		gtk_container_remove(GTK_CONTAINER(gui.toolbar),
+				     GTK_WIDGET(item));
+	}
+#  else
 	    gtk_toolbar_remove_space(GTK_TOOLBAR(gui.toolbar),
 				     get_menu_position(menu));
+#  endif
 	else if (menu->id != NULL)
 	    gtk_widget_destroy(menu->id);
     }
@@ -711,18 +992,42 @@ gui_mch_set_scrollbar_thumb(scrollbar_T 
 
 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
 
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_adjustment_set_lower(adjustment, 0.0);
+	gtk_adjustment_set_value(adjustment, val);
+	gtk_adjustment_set_upper(adjustment, max + 1);
+	gtk_adjustment_set_page_size(adjustment, size);
+	gtk_adjustment_set_page_increment(adjustment,
+					  size < 3L ? 1L : size - 2L);
+	gtk_adjustment_set_step_increment(adjustment, 1.0);
+#else
 	adjustment->lower = 0.0;
 	adjustment->value = val;
 	adjustment->upper = max + 1;
 	adjustment->page_size = size;
 	adjustment->page_increment = size < 3L ? 1L : size - 2L;
 	adjustment->step_increment = 1.0;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_handler_block(G_OBJECT(adjustment),
+						      (gulong)sb->handler_id);
+#else
 	g_signal_handler_block(GTK_OBJECT(adjustment),
 						      (gulong)sb->handler_id);
+#endif
+
+#if !GTK_CHECK_VERSION(3,18,0)
 	gtk_adjustment_changed(adjustment);
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_handler_unblock(G_OBJECT(adjustment),
+						      (gulong)sb->handler_id);
+#else
 	g_signal_handler_unblock(GTK_OBJECT(adjustment),
 						      (gulong)sb->handler_id);
+#endif
     }
 }
 
@@ -750,7 +1055,12 @@ adjustment_value_changed(GtkAdjustment *
 #endif
 
     sb = gui_find_scrollbar((long)data);
+#if GTK_CHECK_VERSION(3,0,0)
+    value = gtk_adjustment_get_value(adjustment);
+#else
     value = (long)adjustment->value;
+#endif
+#if !GTK_CHECK_VERSION(3,0,0)
     /*
      * The dragging argument must be right for the scrollbar to work with
      * closed folds.  This isn't documented, hopefully this will keep on
@@ -793,7 +1103,7 @@ adjustment_value_changed(GtkAdjustment *
 	    }
 	}
     }
-
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
     gui_drag_scrollbar(sb, value, dragging);
 }
 
@@ -802,23 +1112,42 @@ adjustment_value_changed(GtkAdjustment *
 gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
 {
     if (orient == SBAR_HORIZ)
+#if GTK_CHECK_VERSION(3,2,0)
+	sb->id = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL);
+#else
 	sb->id = gtk_hscrollbar_new(NULL);
+#endif
     else if (orient == SBAR_VERT)
+#if GTK_CHECK_VERSION(3,2,0)
+	sb->id = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL);
+#else
 	sb->id = gtk_vscrollbar_new(NULL);
+#endif
 
     if (sb->id != NULL)
     {
 	GtkAdjustment *adjustment;
 
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_widget_set_can_focus(sb->id, FALSE);
+#else
 	GTK_WIDGET_UNSET_FLAGS(sb->id, GTK_CAN_FOCUS);
+#endif
 	gtk_form_put(GTK_FORM(gui.formwin), sb->id, 0, 0);
 
 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
 
+#if GTK_CHECK_VERSION(3,0,0)
+	sb->handler_id = g_signal_connect(
+			     G_OBJECT(adjustment), "value-changed",
+			     G_CALLBACK(adjustment_value_changed),
+			     GINT_TO_POINTER(sb->ident));
+#else
 	sb->handler_id = gtk_signal_connect(
 			     GTK_OBJECT(adjustment), "value_changed",
 			     GTK_SIGNAL_FUNC(adjustment_value_changed),
 			     GINT_TO_POINTER(sb->ident));
+#endif
 	gui_mch_update();
     }
 }
@@ -932,8 +1261,13 @@ gui_mch_browse(int saving UNUSED,
 	    GTK_WINDOW(gui.mainwin),
 	    saving ? GTK_FILE_CHOOSER_ACTION_SAVE
 					   : GTK_FILE_CHOOSER_ACTION_OPEN,
+# if GTK_CHECK_VERSION(3,10,0)
+	    _("_Cancel"), GTK_RESPONSE_CANCEL,
+	    saving ? _("_Save") : _("_Open"), GTK_RESPONSE_ACCEPT,
+# else
 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 	    saving ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+# endif
 	    NULL);
     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc),
 						       (const gchar *)dirbuf);
@@ -991,7 +1325,7 @@ gui_mch_browse(int saving UNUSED,
     }
     gtk_widget_destroy(GTK_WIDGET(fc));
 
-#else
+#else /* !USE_FILE_CHOOSER */
 
     if (gui.filedlg == NULL)
     {
@@ -1027,7 +1361,7 @@ gui_mch_browse(int saving UNUSED,
 
     gtk_widget_show(gui.filedlg);
     gtk_main();
-#endif
+#endif /* !USE_FILE_CHOOSER */
     g_log_remove_handler(domain, log_handler);
 
     CONVERT_TO_UTF8_FREE(title);
@@ -1062,8 +1396,13 @@ gui_mch_browsedir(
 	    (const gchar *)title,
 	    GTK_WINDOW(gui.mainwin),
 	    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+#  if GTK_CHECK_VERSION(3,10,0)
+	    _("_Cancel"), GTK_RESPONSE_CANCEL,
+	    _("_OK"), GTK_RESPONSE_ACCEPT,
+#  else
 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 	    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+#  endif
 	    NULL);
 
     CONVERT_TO_UTF8_FREE(title);
@@ -1096,10 +1435,10 @@ gui_mch_browsedir(
     g_free(dirname);
     return p;
 
-# else
+# else /* !defined(GTK_FILE_CHOOSER) */
     /* For GTK 2.2 and earlier: fall back to ordinary file selector. */
     return gui_mch_browse(0, title, NULL, NULL, initdir, NULL);
-# endif
+# endif /* !defined(GTK_FILE_CHOOSER) */
 }
 
 
@@ -1266,6 +1605,11 @@ dialog_add_buttons(GtkDialog *dialog, ch
     /* Check 'v' flag in 'guioptions': vertical button placement. */
     if (vim_strchr(p_go, GO_VERTICAL) != NULL)
     {
+# if GTK_CHECK_VERSION(3,0,0)
+	/* Add GTK+ 3 code if necessary. */
+	/* N.B. GTK+ 3 doesn't allow you to access vbox and action_area via
+	 * the C API. */
+# else
 	GtkWidget	*vbutton_box;
 
 	vbutton_box = gtk_vbutton_box_new();
@@ -1274,6 +1618,7 @@ dialog_add_buttons(GtkDialog *dialog, ch
 						 vbutton_box, TRUE, FALSE, 0);
 	/* Overrule the "action_area" value, hopefully this works... */
 	GTK_DIALOG(dialog)->action_area = vbutton_box;
+# endif
     }
 
     /*
@@ -1308,6 +1653,16 @@ dialog_add_buttons(GtkDialog *dialog, ch
 	 */
 	if (ok != NULL && ync != NULL) /* almost impossible to fail */
 	{
+# if GTK_CHECK_VERSION(3,10,0)
+	    if	    (button_equal(label, ok[0]))    label = _("OK");
+	    else if (button_equal(label, ync[0]))   label = _("Yes");
+	    else if (button_equal(label, ync[1]))   label = _("No");
+	    else if (button_equal(label, ync[2]))   label = _("Cancel");
+	    else if (button_equal(label, "Ok"))     label = _("OK");
+	    else if (button_equal(label, "Yes"))    label = _("Yes");
+	    else if (button_equal(label, "No"))     label = _("No");
+	    else if (button_equal(label, "Cancel")) label = _("Canccl");
+# else
 	    if	    (button_equal(label, ok[0]))    label = GTK_STOCK_OK;
 	    else if (button_equal(label, ync[0]))   label = GTK_STOCK_YES;
 	    else if (button_equal(label, ync[1]))   label = GTK_STOCK_NO;
@@ -1316,6 +1671,7 @@ dialog_add_buttons(GtkDialog *dialog, ch
 	    else if (button_equal(label, "Yes"))    label = GTK_STOCK_YES;
 	    else if (button_equal(label, "No"))     label = GTK_STOCK_NO;
 	    else if (button_equal(label, "Cancel")) label = GTK_STOCK_CANCEL;
+# endif
 	}
 	label8 = CONVERT_TO_UTF8((char_u *)label);
 	gtk_dialog_add_button(dialog, (const gchar *)label8, idx);
@@ -1408,14 +1764,32 @@ gui_mch_dialog(int	type,	    /* type of 
 	gtk_entry_set_text(GTK_ENTRY(entry), (const char *)text);
 	CONVERT_TO_UTF8_FREE(text);
 
+# if GTK_CHECK_VERSION(3,14,0)
+	gtk_widget_set_halign(GTK_WIDGET(entry), GTK_ALIGN_CENTER);
+	gtk_widget_set_valign(GTK_WIDGET(entry), GTK_ALIGN_CENTER);
+	gtk_widget_set_hexpand(GTK_WIDGET(entry), TRUE);
+	gtk_widget_set_vexpand(GTK_WIDGET(entry), TRUE);
+
+	alignment = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+# else
 	alignment = gtk_alignment_new((float)0.5, (float)0.5,
 						      (float)1.0, (float)1.0);
+# endif
 	gtk_container_add(GTK_CONTAINER(alignment), entry);
 	gtk_container_set_border_width(GTK_CONTAINER(alignment), 5);
 	gtk_widget_show(alignment);
 
+# if GTK_CHECK_VERSION(3,0,0)
+	{
+	    GtkWidget * const vbox
+		= gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+	    gtk_box_pack_start(GTK_BOX(vbox),
+		    alignment, TRUE, FALSE, 0);
+	}
+# else
 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
 			   alignment, TRUE, FALSE, 0);
+# endif
 	dialoginfo.noalt = FALSE;
     }
     else
@@ -1473,6 +1847,7 @@ gui_mch_show_popupmenu(vimmenu_T *menu)
      * Append a submenu for selecting an input method.	This is
      * currently the only way to switch input methods at runtime.
      */
+#  if !GTK_CHECK_VERSION(3,10,0)
     if (xic != NULL && g_object_get_data(G_OBJECT(menu->submenu_id),
 					 "vim-has-im-menu") == NULL)
     {
@@ -1499,6 +1874,7 @@ gui_mch_show_popupmenu(vimmenu_T *menu)
 	g_object_set_data(G_OBJECT(menu->submenu_id),
 			  "vim-has-im-menu", GINT_TO_POINTER(TRUE));
     }
+#  endif
 # endif /* FEAT_XIM */
 
     gtk_menu_popup(GTK_MENU(menu->submenu_id),
@@ -1524,7 +1900,11 @@ popup_menu_position_func(GtkMenu *menu U
 			 gboolean *push_in UNUSED,
 			 gpointer user_data UNUSED)
 {
+# if GTK_CHECK_VERSION(3,0,0)
+    gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), x, y);
+# else
     gdk_window_get_origin(gui.drawarea->window, x, y);
+# endif
 
     if (popup_mouse_pos)
     {
@@ -1534,7 +1914,12 @@ popup_menu_position_func(GtkMenu *menu U
 	*x += mx;
 	*y += my;
     }
+# if GTK_CHECK_VERSION(3,0,0)
+    else if (curwin != NULL && gui.drawarea != NULL &&
+	     gtk_widget_get_window(gui.drawarea) != NULL)
+# else
     else if (curwin != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
+# endif
     {
 	/* Find the cursor position in the current window */
 	*x += FILL_X(W_WINCOL(curwin) + curwin->w_wcol + 1) + 1;
@@ -1612,7 +1997,13 @@ find_key_press_event(
 }
 
     static GtkWidget *
-create_image_button(const char *stock_id, const char *label)
+#if GTK_CHECK_VERSION(3,10,0)
+create_image_button(const char *stock_id UNUSED,
+		    const char *label)
+#else
+create_image_button(const char *stock_id,
+		    const char *label)
+#endif
 {
     char_u	*text;
     GtkWidget	*box;
@@ -1621,18 +2012,35 @@ create_image_button(const char *stock_id
 
     text = CONVERT_TO_UTF8((char_u *)label);
 
+#if GTK_CHECK_VERSION(3,2,0)
+    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
+    gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
+#else
     box = gtk_hbox_new(FALSE, 3);
-    gtk_box_pack_start(GTK_BOX(box),
-		       gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON),
-		       FALSE, FALSE, 0);
+#endif
+#if !GTK_CHECK_VERSION(3,10,0)
+    if (stock_id != NULL)
+	gtk_box_pack_start(GTK_BOX(box),
+			   gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON),
+			   FALSE, FALSE, 0);
+#endif
     gtk_box_pack_start(GTK_BOX(box),
 		       gtk_label_new((const char *)text),
 		       FALSE, FALSE, 0);
 
     CONVERT_TO_UTF8_FREE(text);
 
+#if GTK_CHECK_VERSION(3,14,0)
+    gtk_widget_set_halign(GTK_WIDGET(box), GTK_ALIGN_CENTER);
+    gtk_widget_set_valign(GTK_WIDGET(box), GTK_ALIGN_CENTER);
+    gtk_widget_set_hexpand(GTK_WIDGET(box), TRUE);
+    gtk_widget_set_vexpand(GTK_WIDGET(box), TRUE);
+
+    alignment = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+#else
     alignment = gtk_alignment_new((float)0.5, (float)0.5,
 						      (float)0.0, (float)0.0);
+#endif
     gtk_container_add(GTK_CONTAINER(alignment), box);
     gtk_widget_show_all(alignment);
 
@@ -1695,10 +2103,17 @@ find_replace_dialog_create(char_u *arg, 
 	if (entry_text != NULL)
 	{
 	    gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
+#if GTK_CHECK_VERSION(3,0,0)
+	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->wword),
+							     (gboolean)wword);
+	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->mcase),
+							     (gboolean)mcase);
+#else
 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
 							     (gboolean)wword);
 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
 							     (gboolean)mcase);
+#endif
 	}
 	gtk_window_present(GTK_WINDOW(frdp->dialog));
 	vim_free(entry_text);
@@ -1706,7 +2121,11 @@ find_replace_dialog_create(char_u *arg, 
     }
 
     frdp->dialog = gtk_dialog_new();
+#if GTK_CHECK_VERSION(3,0,0)
+    /* Nothing equivalent to gtk_dialog_set_has_separator() in GTK+ 3. */
+#else
     gtk_dialog_set_has_separator(GTK_DIALOG(frdp->dialog), FALSE);
+#endif
     gtk_window_set_transient_for(GTK_WINDOW(frdp->dialog), GTK_WINDOW(gui.mainwin));
     gtk_window_set_destroy_with_parent(GTK_WINDOW(frdp->dialog), TRUE);
 
@@ -1721,164 +2140,402 @@ find_replace_dialog_create(char_u *arg, 
 			     CONV(_("VIM - Search...")));
     }
 
+#if GTK_CHECK_VERSION(3,2,0)
+    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+    gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
+#else
     hbox = gtk_hbox_new(FALSE, 0);
+#endif
     gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+	GtkWidget * const dialog_vbox
+	    = gtk_dialog_get_content_area(GTK_DIALOG(frdp->dialog));
+	gtk_container_add(GTK_CONTAINER(dialog_vbox), hbox);
+    }
+#else
     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(frdp->dialog)->vbox), hbox);
+#endif
 
     if (do_replace)
+#if GTK_CHECK_VERSION(3,4,0)
+	table = gtk_grid_new();
+#else
 	table = gtk_table_new(1024, 4, FALSE);
+#endif
     else
+#if GTK_CHECK_VERSION(3,4,0)
+	table = gtk_grid_new();
+#else
 	table = gtk_table_new(1024, 3, FALSE);
+#endif
     gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
+#else
     gtk_container_border_width(GTK_CONTAINER(table), 4);
+#endif
 
     tmp = gtk_label_new(CONV(_("Find what:")));
+#if GTK_CHECK_VERSION(3,16,0)
+    gtk_label_set_xalign(GTK_LABEL(tmp), 0.0);
+    gtk_label_set_yalign(GTK_LABEL(tmp), 0.5);
+#elif GTK_CHECK_VERSION(3,14,0)
+    {
+	GValue align_val = G_VALUE_INIT;
+
+	g_value_init(&align_val, G_TYPE_FLOAT);
+
+	g_value_set_float(&align_val, 0.0);
+	g_object_set_property(G_OBJECT(tmp), "xalign", &align_val);
+
+	g_value_set_float(&align_val, 0.5);
+	g_object_set_property(G_OBJECT(tmp), "yalign", &align_val);
+
+	g_value_unset(&align_val);
+    }
+#else
     gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+    gtk_grid_attach(GTK_GRID(table), tmp, 0, 0, 2, 1);
+#else
     gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 0, 1,
 		     GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
     frdp->what = gtk_entry_new();
     sensitive = (entry_text != NULL && entry_text[0] != NUL);
     if (entry_text != NULL)
 	gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(frdp->what), "changed",
+		     G_CALLBACK(entry_changed_cb), frdp->dialog);
+    g_signal_connect_after(G_OBJECT(frdp->what), "key-press-event",
+			   G_CALLBACK(find_key_press_event),
+			   (gpointer) frdp);
+#else
     gtk_signal_connect(GTK_OBJECT(frdp->what), "changed",
 		       GTK_SIGNAL_FUNC(entry_changed_cb), frdp->dialog);
     gtk_signal_connect_after(GTK_OBJECT(frdp->what), "key_press_event",
 				 GTK_SIGNAL_FUNC(find_key_press_event),
 				 (gpointer) frdp);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+    gtk_grid_attach(GTK_GRID(table), frdp->what, 2, 0, 5, 1);
+#else
     gtk_table_attach(GTK_TABLE(table), frdp->what, 1, 1024, 0, 1,
 		     GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 
     if (do_replace)
     {
 	tmp = gtk_label_new(CONV(_("Replace with:")));
+#if GTK_CHECK_VERSION(3,16,0)
+	gtk_label_set_xalign(GTK_LABEL(tmp), 0.0);
+	gtk_label_set_yalign(GTK_LABEL(tmp), 0.5);
+#elif GTK_CHECK_VERSION(3,14,0)
+	{
+	    GValue align_val = G_VALUE_INIT;
+
+	    g_value_init(&align_val, G_TYPE_FLOAT);
+
+	    g_value_set_float(&align_val, 0.0);
+	    g_object_set_property(G_OBJECT(tmp), "xalign", &align_val);
+
+	    g_value_set_float(&align_val, 0.5);
+	    g_object_set_property(G_OBJECT(tmp), "yalign", &align_val);
+
+	    g_value_unset(&align_val);
+	}
+#else
 	gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+	gtk_grid_attach(GTK_GRID(table), tmp, 0, 1, 2, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 1, 2,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 	frdp->with = gtk_entry_new();
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->with), "activate",
+			 G_CALLBACK(find_replace_cb),
+			 GINT_TO_POINTER(FRD_R_FINDNEXT));
+	g_signal_connect_after(G_OBJECT(frdp->with), "key-press-event",
+			       G_CALLBACK(find_key_press_event),
+			       (gpointer) frdp);
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->with), "activate",
 			   GTK_SIGNAL_FUNC(find_replace_cb),
 			   GINT_TO_POINTER(FRD_R_FINDNEXT));
 	gtk_signal_connect_after(GTK_OBJECT(frdp->with), "key_press_event",
 				 GTK_SIGNAL_FUNC(find_key_press_event),
 				 (gpointer) frdp);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+	gtk_grid_attach(GTK_GRID(table), frdp->with, 2, 1, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->with, 1, 1024, 1, 2,
 			 GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 
 	/*
 	 * Make the entry activation only change the input focus onto the
 	 * with item.
 	 */
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->what), "activate",
+			 G_CALLBACK(entry_activate_cb), frdp->with);
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
 			   GTK_SIGNAL_FUNC(entry_activate_cb), frdp->with);
+#endif
     }
     else
     {
 	/*
 	 * Make the entry activation do the search.
 	 */
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->what), "activate",
+			 G_CALLBACK(find_replace_cb),
+			 GINT_TO_POINTER(FRD_FINDNEXT));
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
 			   GTK_SIGNAL_FUNC(find_replace_cb),
 			   GINT_TO_POINTER(FRD_FINDNEXT));
+#endif
     }
 
     /* whole word only button */
     frdp->wword = gtk_check_button_new_with_label(CONV(_("Match whole word only")));
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->wword),
+							(gboolean)wword);
+#else
     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
 							(gboolean)wword);
+#endif
     if (do_replace)
+#if GTK_CHECK_VERSION(3,4,0)
+	gtk_grid_attach(GTK_GRID(table), frdp->wword, 0, 2, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 2, 3,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
     else
+#if GTK_CHECK_VERSION(3,4,0)
+	gtk_grid_attach(GTK_GRID(table), frdp->wword, 0, 3, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 1, 2,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 
     /* match case button */
     frdp->mcase = gtk_check_button_new_with_label(CONV(_("Match case")));
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->mcase),
+							     (gboolean)mcase);
+#else
     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
 							     (gboolean)mcase);
+#endif
     if (do_replace)
+#if GTK_CHECK_VERSION(3,4,0)
+	gtk_grid_attach(GTK_GRID(table), frdp->mcase, 0, 3, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 3, 4,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
     else
+#if GTK_CHECK_VERSION(3,4,0)
+	gtk_grid_attach(GTK_GRID(table), frdp->mcase, 0, 4, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 2, 3,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 
     tmp = gtk_frame_new(CONV(_("Direction")));
     if (do_replace)
+#if GTK_CHECK_VERSION(3,4,0)
+	gtk_grid_attach(GTK_GRID(table), tmp, 5, 2, 2, 4);
+#else
 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 2, 4,
 			 GTK_FILL, GTK_FILL, 2, 2);
+#endif
     else
+#if GTK_CHECK_VERSION(3,4,0)
+	gtk_grid_attach(GTK_GRID(table), tmp, 5, 2, 1, 3);
+#else
 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 1, 3,
 			 GTK_FILL, GTK_FILL, 2, 2);
+#endif
+#if GTK_CHECK_VERSION(3,2,0)
+    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+    gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
+#else
     vbox = gtk_vbox_new(FALSE, 0);
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
+#else
     gtk_container_border_width(GTK_CONTAINER(vbox), 0);
+#endif
     gtk_container_add(GTK_CONTAINER(tmp), vbox);
 
     /* 'Up' and 'Down' buttons */
     frdp->up = gtk_radio_button_new_with_label(NULL, CONV(_("Up")));
     gtk_box_pack_start(GTK_BOX(vbox), frdp->up, TRUE, TRUE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+    frdp->down = gtk_radio_button_new_with_label(
+			gtk_radio_button_get_group(GTK_RADIO_BUTTON(frdp->up)),
+			CONV(_("Down")));
+#else
     frdp->down = gtk_radio_button_new_with_label(
 			gtk_radio_button_group(GTK_RADIO_BUTTON(frdp->up)),
 			CONV(_("Down")));
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
+#else
     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
+#endif
     gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
     gtk_box_pack_start(GTK_BOX(vbox), frdp->down, TRUE, TRUE, 0);
 
     /* vbox to hold the action buttons */
+#if GTK_CHECK_VERSION(3,2,0)
+    actionarea = gtk_button_box_new(GTK_ORIENTATION_VERTICAL);
+#else
     actionarea = gtk_vbutton_box_new();
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(actionarea), 2);
+#else
     gtk_container_border_width(GTK_CONTAINER(actionarea), 2);
+#endif
     gtk_box_pack_end(GTK_BOX(hbox), actionarea, FALSE, FALSE, 0);
 
     /* 'Find Next' button */
+#if GTK_CHECK_VERSION(3,10,0)
+    frdp->find = create_image_button(NULL, _("Find Next"));
+#else
     frdp->find = create_image_button(GTK_STOCK_FIND, _("Find Next"));
+#endif
     gtk_widget_set_sensitive(frdp->find, sensitive);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(frdp->find), "clicked",
+		     G_CALLBACK(find_replace_cb),
+		     (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
+				  : GINT_TO_POINTER(FRD_FINDNEXT));
+#else
     gtk_signal_connect(GTK_OBJECT(frdp->find), "clicked",
 		       GTK_SIGNAL_FUNC(find_replace_cb),
 		       (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
 				    : GINT_TO_POINTER(FRD_FINDNEXT));
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_can_default(frdp->find, TRUE);
+#else
     GTK_WIDGET_SET_FLAGS(frdp->find, GTK_CAN_DEFAULT);
+#endif
     gtk_box_pack_start(GTK_BOX(actionarea), frdp->find, FALSE, FALSE, 0);
     gtk_widget_grab_default(frdp->find);
 
     if (do_replace)
     {
 	/* 'Replace' button */
+#if GTK_CHECK_VERSION(3,10,0)
+	frdp->replace = create_image_button(NULL, _("Replace"));
+#else
 	frdp->replace = create_image_button(GTK_STOCK_CONVERT, _("Replace"));
+#endif
 	gtk_widget_set_sensitive(frdp->replace, sensitive);
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_widget_set_can_default(frdp->find, TRUE);
+#else
 	GTK_WIDGET_SET_FLAGS(frdp->replace, GTK_CAN_DEFAULT);
+#endif
 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->replace, FALSE, FALSE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->replace), "clicked",
+			 G_CALLBACK(find_replace_cb),
+			 GINT_TO_POINTER(FRD_REPLACE));
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->replace), "clicked",
 			   GTK_SIGNAL_FUNC(find_replace_cb),
 			   GINT_TO_POINTER(FRD_REPLACE));
+#endif
 
 	/* 'Replace All' button */
+#if GTK_CHECK_VERSION(3,10,0)
+	frdp->all = create_image_button(NULL, _("Replace All"));
+#else
 	frdp->all = create_image_button(GTK_STOCK_CONVERT, _("Replace All"));
+#endif
 	gtk_widget_set_sensitive(frdp->all, sensitive);
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_widget_set_can_default(frdp->all, TRUE);
+#else
 	GTK_WIDGET_SET_FLAGS(frdp->all, GTK_CAN_DEFAULT);
+#endif
 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->all, FALSE, FALSE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->all), "clicked",
+			 G_CALLBACK(find_replace_cb),
+			 GINT_TO_POINTER(FRD_REPLACEALL));
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->all), "clicked",
 			   GTK_SIGNAL_FUNC(find_replace_cb),
 			   GINT_TO_POINTER(FRD_REPLACEALL));
+#endif
     }
 
     /* 'Cancel' button */
+#if GTK_CHECK_VERSION(3,10,0)
+    tmp = gtk_button_new_with_mnemonic(_("_Close"));
+#else
     tmp = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_can_default(tmp, TRUE);
+#else
     GTK_WIDGET_SET_FLAGS(tmp, GTK_CAN_DEFAULT);
+#endif
     gtk_box_pack_end(GTK_BOX(actionarea), tmp, FALSE, FALSE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect_swapped(G_OBJECT(tmp),
+			     "clicked", G_CALLBACK(gtk_widget_hide),
+			     G_OBJECT(frdp->dialog));
+    g_signal_connect_swapped(G_OBJECT(frdp->dialog),
+			     "delete-event", G_CALLBACK(gtk_widget_hide_on_delete),
+			     G_OBJECT(frdp->dialog));
+#else
     gtk_signal_connect_object(GTK_OBJECT(tmp),
 			      "clicked", GTK_SIGNAL_FUNC(gtk_widget_hide),
 			      GTK_OBJECT(frdp->dialog));
     gtk_signal_connect_object(GTK_OBJECT(frdp->dialog),
 			      "delete_event", GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete),
 			      GTK_OBJECT(frdp->dialog));
+#endif
 
+#if GTK_CHECK_VERSION(3,2,0)
+    tmp = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
+#else
     tmp = gtk_vseparator_new();
+#endif
     gtk_box_pack_end(GTK_BOX(hbox), tmp, FALSE, FALSE, 10);
 
     /* Suppress automatic show of the unused action area */
+#if GTK_CHECK_VERSION(3,0,0)
+# if !GTK_CHECK_VERSION(3,12,0)
+    gtk_widget_hide(gtk_dialog_get_action_area(GTK_DIALOG(frdp->dialog)));
+# endif
+#else
     gtk_widget_hide(GTK_DIALOG(frdp->dialog)->action_area);
+#endif
     gtk_widget_show_all(hbox);
     gtk_widget_show(frdp->dialog);
 
@@ -1928,11 +2585,23 @@ find_replace_cb(GtkWidget *widget UNUSED
     }
 
     find_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(sfr->what));
+#if GTK_CHECK_VERSION(3,0,0)
+    direction_down = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->down));
+#else
     direction_down = GTK_TOGGLE_BUTTON(sfr->down)->active;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->wword)))
+#else
     if (GTK_TOGGLE_BUTTON(sfr->wword)->active)
+#endif
 	flags |= FRD_WHOLE_WORD;
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->mcase)))
+#else
     if (GTK_TOGGLE_BUTTON(sfr->mcase)->active)
+#endif
 	flags |= FRD_MATCH_CASE;
 
     repl_text = CONVERT_FROM_UTF8(repl_text);
--- a/src/gui_gtk_f.c
+++ b/src/gui_gtk_f.c
@@ -19,13 +19,19 @@
  * children at arbitrary positions width arbitrary sizes.  This finally puts
  * an end on our resize problems with which we where struggling for such a
  * long time.
+ *
+ * Support for GTK+ 3 was added by:
+ *
+ * 2016  Kazunobu Kuriyama  <kazunobu.kuriyama@gmail.com>
  */
 
 #include "vim.h"
 #include <gtk/gtk.h>	/* without this it compiles, but gives errors at
 			   runtime! */
 #include "gui_gtk_f.h"
-#include <gtk/gtksignal.h>
+#if !GTK_CHECK_VERSION(3,0,0)
+# include <gtk/gtksignal.h>
+#endif
 #ifdef WIN3264
 # include <gdk/gdkwin32.h>
 #else
@@ -52,10 +58,23 @@ static void gtk_form_unrealize(GtkWidget
 static void gtk_form_map(GtkWidget *widget);
 static void gtk_form_size_request(GtkWidget *widget,
 				  GtkRequisition *requisition);
+#if GTK_CHECK_VERSION(3,0,0)
+static void gtk_form_get_preferred_width(GtkWidget *widget,
+					 gint *minimal_width,
+					 gint *natural_width);
+static void gtk_form_get_preferred_height(GtkWidget *widget,
+					  gint *minimal_height,
+					  gint *natural_height);
+#endif
 static void gtk_form_size_allocate(GtkWidget *widget,
 				   GtkAllocation *allocation);
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean gtk_form_draw(GtkWidget *widget,
+			      cairo_t *cr);
+#else
 static gint gtk_form_expose(GtkWidget *widget,
 			    GdkEventExpose *event);
+#endif
 
 static void gtk_form_remove(GtkContainer *container,
 			    GtkWidget *widget);
@@ -73,22 +92,27 @@ static void gtk_form_position_child(GtkF
 				    gboolean force_allocate);
 static void gtk_form_position_children(GtkForm *form);
 
+#if !GTK_CHECK_VERSION(3,0,0)
 static GdkFilterReturn gtk_form_filter(GdkXEvent *gdk_xevent,
 				       GdkEvent *event,
 				       gpointer data);
 static GdkFilterReturn gtk_form_main_filter(GdkXEvent *gdk_xevent,
 					    GdkEvent *event,
 					    gpointer data);
-
+#endif
+#if !GTK_CHECK_VERSION(3,16,0)
 static void gtk_form_set_static_gravity(GdkWindow *window,
 					gboolean use_static);
+#endif
 
 static void gtk_form_send_configure(GtkForm *form);
 
 static void gtk_form_child_map(GtkWidget *widget, gpointer user_data);
 static void gtk_form_child_unmap(GtkWidget *widget, gpointer user_data);
 
+#if !GTK_CHECK_VERSION(3,0,0)
 static GtkWidgetClass *parent_class = NULL;
+#endif
 
 /* Public interface
  */
@@ -98,7 +122,11 @@ gtk_form_new(void)
 {
     GtkForm *form;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    form = g_object_new(GTK_TYPE_FORM, NULL);
+#else
     form = gtk_type_new(gtk_form_get_type());
+#endif
 
     return GTK_WIDGET(form);
 }
@@ -120,8 +148,12 @@ gtk_form_put(GtkForm	*form,
     child->window = NULL;
     child->x = x;
     child->y = y;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_size_request(child->widget, -1, -1);
+#else
     child->widget->requisition.width = 0;
     child->widget->requisition.height = 0;
+#endif
     child->mapped = FALSE;
 
     form->children = g_list_append(form->children, child);
@@ -131,13 +163,24 @@ gtk_form_put(GtkForm	*form,
      * that gtk_widget_set_parent() realizes the widget if it's visible
      * and its parent is mapped.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_realized(GTK_WIDGET(form)))
+#else
     if (GTK_WIDGET_REALIZED(form))
+#endif
 	gtk_form_attach_child_window(form, child);
 
     gtk_widget_set_parent(child_widget, GTK_WIDGET(form));
+#if !GTK_CHECK_VERSION(3,0,0)
     gtk_widget_size_request(child->widget, NULL);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_realized(GTK_WIDGET(form))
+	    && !gtk_widget_get_realized(child_widget))
+#else
     if (GTK_WIDGET_REALIZED(form) && !GTK_WIDGET_REALIZED(child_widget))
+#endif
 	gtk_form_realize_child(form, child);
 
     gtk_form_position_child(form, child, TRUE);
@@ -193,6 +236,9 @@ gtk_form_thaw(GtkForm *form)
 
 /* Basic Object handling procedures
  */
+#if GTK_CHECK_VERSION(3,0,0)
+G_DEFINE_TYPE(GtkForm, gtk_form, GTK_TYPE_CONTAINER)
+#else
     GtkType
 gtk_form_get_type(void)
 {
@@ -213,6 +259,7 @@ gtk_form_get_type(void)
     }
     return form_type;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
     static void
 gtk_form_class_init(GtkFormClass *klass)
@@ -223,14 +270,25 @@ gtk_form_class_init(GtkFormClass *klass)
     widget_class = (GtkWidgetClass *) klass;
     container_class = (GtkContainerClass *) klass;
 
+#if !GTK_CHECK_VERSION(3,0,0)
     parent_class = gtk_type_class(gtk_container_get_type());
+#endif
 
     widget_class->realize = gtk_form_realize;
     widget_class->unrealize = gtk_form_unrealize;
     widget_class->map = gtk_form_map;
+#if GTK_CHECK_VERSION(3,0,0)
+    widget_class->get_preferred_width = gtk_form_get_preferred_width;
+    widget_class->get_preferred_height = gtk_form_get_preferred_height;
+#else
     widget_class->size_request = gtk_form_size_request;
+#endif
     widget_class->size_allocate = gtk_form_size_allocate;
+#if GTK_CHECK_VERSION(3,0,0)
+    widget_class->draw = gtk_form_draw;
+#else
     widget_class->expose_event = gtk_form_expose;
+#endif
 
     container_class->remove = gtk_form_remove;
     container_class->forall = gtk_form_forall;
@@ -239,15 +297,22 @@ gtk_form_class_init(GtkFormClass *klass)
     static void
 gtk_form_init(GtkForm *form)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_has_window(GTK_WIDGET(form), TRUE);
+#endif
     form->children = NULL;
 
+#if !GTK_CHECK_VERSION(3,0,0)
     form->width = 1;
     form->height = 1;
+#endif
 
     form->bin_window = NULL;
 
+#if !GTK_CHECK_VERSION(3,0,0)
     form->configure_serial = 0;
     form->visibility = GDK_VISIBILITY_PARTIAL;
+#endif
 
     form->freeze_count = 0;
 }
@@ -267,40 +332,92 @@ gtk_form_realize(GtkWidget *widget)
     g_return_if_fail(GTK_IS_FORM(widget));
 
     form = GTK_FORM(widget);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_realized(widget, TRUE);
+#else
     GTK_WIDGET_SET_FLAGS(form, GTK_REALIZED);
+#endif
 
     attributes.window_type = GDK_WINDOW_CHILD;
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+	GtkAllocation allocation;
+	gtk_widget_get_allocation(widget, &allocation);
+	attributes.x = allocation.x;
+	attributes.y = allocation.y;
+	attributes.width = allocation.width;
+	attributes.height = allocation.height;
+    }
+#else
     attributes.x = widget->allocation.x;
     attributes.y = widget->allocation.y;
     attributes.width = widget->allocation.width;
     attributes.height = widget->allocation.height;
+#endif
     attributes.wclass = GDK_INPUT_OUTPUT;
     attributes.visual = gtk_widget_get_visual(widget);
+#if GTK_CHECK_VERSION(3,0,0)
+    attributes.event_mask = GDK_EXPOSURE_MASK;
+#else
     attributes.colormap = gtk_widget_get_colormap(widget);
     attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+#else
     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_window(widget,
+			  gdk_window_new(gtk_widget_get_parent_window(widget),
+					 &attributes, attributes_mask));
+    gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
+#else
     widget->window = gdk_window_new(gtk_widget_get_parent_window(widget),
 				    &attributes, attributes_mask);
     gdk_window_set_user_data(widget->window, widget);
+#endif
 
     attributes.x = 0;
     attributes.y = 0;
     attributes.event_mask = gtk_widget_get_events(widget);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    form->bin_window = gdk_window_new(gtk_widget_get_window(widget),
+				      &attributes, attributes_mask);
+#else
     form->bin_window = gdk_window_new(widget->window,
 				      &attributes, attributes_mask);
+#endif
     gdk_window_set_user_data(form->bin_window, widget);
 
+#if !GTK_CHECK_VERSION(3,16,0)
     gtk_form_set_static_gravity(form->bin_window, TRUE);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+	GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
+
+	gtk_style_context_add_class(sctx, "gtk-form");
+	gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
+# if !GTK_CHECK_VERSION(3,18,0)
+	gtk_style_context_set_background(sctx, gtk_widget_get_window(widget));
+	gtk_style_context_set_background(sctx, form->bin_window);
+# endif
+    }
+#else
     widget->style = gtk_style_attach(widget->style, widget->window);
     gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
     gtk_style_set_background(widget->style, form->bin_window, GTK_STATE_NORMAL);
+#endif
 
+#if !GTK_CHECK_VERSION(3,0,0)
     gdk_window_add_filter(widget->window, gtk_form_main_filter, form);
     gdk_window_add_filter(form->bin_window, gtk_form_filter, form);
+#endif
 
     for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
     {
@@ -308,7 +425,11 @@ gtk_form_realize(GtkWidget *widget)
 
 	gtk_form_attach_child_window(form, child);
 
+#if GTK_CHECK_VERSION(3,0,0)
+	if (gtk_widget_get_visible(child->widget))
+#else
 	if (GTK_WIDGET_VISIBLE(child->widget))
+#endif
 	    gtk_form_realize_child(form, child);
     }
 }
@@ -332,17 +453,30 @@ gtk_form_map(GtkWidget *widget)
 
     form = GTK_FORM(widget);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_mapped(widget, TRUE);
+#else
     GTK_WIDGET_SET_FLAGS(widget, GTK_MAPPED);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gdk_window_show(gtk_widget_get_window(widget));
+#else
     gdk_window_show(widget->window);
+#endif
     gdk_window_show(form->bin_window);
 
     for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
     {
 	GtkFormChild *child = tmp_list->data;
 
+#if GTK_CHECK_VERSION(3,0,0)
+	if (gtk_widget_get_visible(child->widget)
+		&& !gtk_widget_get_mapped(child->widget))
+#else
 	if (GTK_WIDGET_VISIBLE(child->widget)
 		&& !GTK_WIDGET_MAPPED(child->widget))
+#endif
 	    gtk_widget_map(child->widget);
     }
 }
@@ -369,12 +503,21 @@ gtk_form_unrealize(GtkWidget *widget)
 
 	if (child->window != NULL)
 	{
+#if GTK_CHECK_VERSION(3,0,0)
+	    g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
+		    G_CALLBACK(gtk_form_child_map),
+		    child);
+	    g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
+		    G_CALLBACK(gtk_form_child_unmap),
+		    child);
+#else
 	    gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget),
 					  GTK_SIGNAL_FUNC(gtk_form_child_map),
 					  child);
 	    gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget),
 					  GTK_SIGNAL_FUNC(gtk_form_child_unmap),
 					  child);
+#endif
 
 	    gdk_window_set_user_data(child->window, NULL);
 	    gdk_window_destroy(child->window);
@@ -385,20 +528,33 @@ gtk_form_unrealize(GtkWidget *widget)
 	tmp_list = tmp_list->next;
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize)
+	 (* GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize) (widget);
+#else
     if (GTK_WIDGET_CLASS (parent_class)->unrealize)
 	 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+#endif
 }
 
     static void
 gtk_form_size_request(GtkWidget *widget, GtkRequisition *requisition)
 {
+#if !GTK_CHECK_VERSION(3,0,0)
     GList *tmp_list;
     GtkForm *form;
+#endif
 
     g_return_if_fail(GTK_IS_FORM(widget));
 
+#if !GTK_CHECK_VERSION(3,0,0)
     form = GTK_FORM(widget);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    requisition->width = 1;
+    requisition->height = 1;
+#else
     requisition->width = form->width;
     requisition->height = form->height;
 
@@ -410,25 +566,71 @@ gtk_form_size_request(GtkWidget *widget,
 	gtk_widget_size_request(child->widget, NULL);
 	tmp_list = tmp_list->next;
     }
+#endif
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gtk_form_get_preferred_width(GtkWidget *widget,
+			     gint      *minimal_width,
+			     gint      *natural_width)
+{
+    GtkRequisition requisition;
+
+    gtk_form_size_request(widget, &requisition);
+
+    *minimal_width = requisition.width;
+    *natural_width = requisition.width;
+}
+
+    static void
+gtk_form_get_preferred_height(GtkWidget *widget,
+			      gint	*minimal_height,
+			      gint	*natural_height)
+{
+    GtkRequisition requisition;
+
+    gtk_form_size_request(widget, &requisition);
+
+    *minimal_height = requisition.height;
+    *natural_height = requisition.height;
+}
+#endif /* GTK_CHECK_VERSION(3,0,0) */
+
     static void
 gtk_form_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
 {
     GList *tmp_list;
     GtkForm *form;
     gboolean need_reposition;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkAllocation cur_alloc;
+#endif
 
     g_return_if_fail(GTK_IS_FORM(widget));
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_get_allocation(widget, &cur_alloc);
+
+    if (cur_alloc.x == allocation->x
+	    && cur_alloc.y == allocation->y
+	    && cur_alloc.width == allocation->width
+	    && cur_alloc.height == allocation->height)
+#else
     if (widget->allocation.x == allocation->x
 	    && widget->allocation.y == allocation->y
 	    && widget->allocation.width == allocation->width
 	    && widget->allocation.height == allocation->height)
+#endif
 	return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    need_reposition = cur_alloc.width != allocation->width
+		   || cur_alloc.height != allocation->height;
+#else
     need_reposition = widget->allocation.width != allocation->width
 		   || widget->allocation.height != allocation->height;
+#endif
     form = GTK_FORM(widget);
 
     if (need_reposition)
@@ -444,20 +646,81 @@ gtk_form_size_allocate(GtkWidget *widget
 	}
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_realized(widget))
+#else
     if (GTK_WIDGET_REALIZED(widget))
+#endif
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_move_resize(gtk_widget_get_window(widget),
+			       allocation->x, allocation->y,
+			       allocation->width, allocation->height);
+#else
 	gdk_window_move_resize(widget->window,
 			       allocation->x, allocation->y,
 			       allocation->width, allocation->height);
+#endif
 	gdk_window_move_resize(GTK_FORM(widget)->bin_window,
 			       0, 0,
 			       allocation->width, allocation->height);
     }
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_allocation(widget, allocation);
+#else
     widget->allocation = *allocation;
+#endif
     if (need_reposition)
 	gtk_form_send_configure(form);
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gtk_form_render_background(GtkWidget *widget, cairo_t *cr)
+{
+    gtk_render_background(gtk_widget_get_style_context(widget), cr,
+			  0, 0,
+			  gtk_widget_get_allocated_width(widget),
+			  gtk_widget_get_allocated_height(widget));
+}
+
+    static gboolean
+gtk_form_draw(GtkWidget *widget, cairo_t *cr)
+{
+    GList   *tmp_list = NULL;
+    GtkForm *form     = NULL;
+
+    g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
+
+    gtk_form_render_background(widget, cr);
+
+    form = GTK_FORM(widget);
+    for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
+    {
+	GtkFormChild * const formchild = tmp_list->data;
+
+	if (!gtk_widget_get_has_window(formchild->widget) &&
+		gtk_cairo_should_draw_window(cr, formchild->window))
+	{
+	    /* To get gtk_widget_draw() to work, it is required to call
+	     * gtk_widget_size_allocate() in advance with a well-posed
+	     * allocation for a given child widget in order to set a
+	     * certain private GtkWidget variable, called
+	     * widget->priv->alloc_need, to the proper value; othewise,
+	     * gtk_widget_draw() fails and the relevant scrollbar won't
+	     * appear on the screen.
+	     *
+	     * Calling gtk_form_position_child() like this is one of ways
+	     * to make sure of that. */
+	    gtk_form_position_child(form, formchild, TRUE);
+
+	    gtk_form_render_background(formchild->widget, cr);
+	}
+    }
+
+    return GTK_WIDGET_CLASS(gtk_form_parent_class)->draw(widget, cr);
+}
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     static gint
 gtk_form_expose(GtkWidget *widget, GdkEventExpose *event)
 {
@@ -497,6 +760,7 @@ gtk_form_expose(GtkWidget *widget, GdkEv
 
     return FALSE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 /* Container method
  */
@@ -522,12 +786,22 @@ gtk_form_remove(GtkContainer *container,
 
     if (tmp_list)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	const gboolean was_visible = gtk_widget_get_visible(widget);
+#endif
 	if (child->window)
 	{
+#if GTK_CHECK_VERSION(3,0,0)
+	    g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
+					  G_CALLBACK(&gtk_form_child_map), child);
+	    g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
+					  G_CALLBACK(&gtk_form_child_unmap), child);
+#else
 	    gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget),
 					  GTK_SIGNAL_FUNC(&gtk_form_child_map), child);
 	    gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget),
 					  GTK_SIGNAL_FUNC(&gtk_form_child_unmap), child);
+#endif
 
 	    /* FIXME: This will cause problems for reparenting NO_WINDOW
 	     * widgets out of a GtkForm
@@ -536,7 +810,10 @@ gtk_form_remove(GtkContainer *container,
 	    gdk_window_destroy(child->window);
 	}
 	gtk_widget_unparent(widget);
-
+#if GTK_CHECK_VERSION(3,0,0)
+	if (was_visible)
+	    gtk_widget_queue_resize(GTK_WIDGET(container));
+#endif
 	form->children = g_list_remove_link(form->children, tmp_list);
 	g_list_free_1(tmp_list);
 	g_free(child);
@@ -577,7 +854,11 @@ gtk_form_attach_child_window(GtkForm *fo
     if (child->window != NULL)
 	return; /* been there, done that */
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (!gtk_widget_get_has_window(child->widget))
+#else
     if (GTK_WIDGET_NO_WINDOW(child->widget))
+#endif
     {
 	GtkWidget	*widget;
 	GdkWindowAttr	attributes;
@@ -588,34 +869,75 @@ gtk_form_attach_child_window(GtkForm *fo
 	attributes.window_type = GDK_WINDOW_CHILD;
 	attributes.x = child->x;
 	attributes.y = child->y;
+#if GTK_CHECK_VERSION(3,0,0)
+	{
+	    GtkRequisition requisition;
+
+	    gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
+
+	    attributes.width = requisition.width;
+	    attributes.height = requisition.height;
+	}
+#else
 	attributes.width = child->widget->requisition.width;
 	attributes.height = child->widget->requisition.height;
+#endif
 	attributes.wclass = GDK_INPUT_OUTPUT;
 	attributes.visual = gtk_widget_get_visual(widget);
+#if !GTK_CHECK_VERSION(3,0,0)
 	attributes.colormap = gtk_widget_get_colormap(widget);
+#endif
 	attributes.event_mask = GDK_EXPOSURE_MASK;
 
+#if GTK_CHECK_VERSION(3,0,0)
+	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+#else
 	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+#endif
 	child->window = gdk_window_new(form->bin_window,
 				       &attributes, attributes_mask);
 	gdk_window_set_user_data(child->window, widget);
 
+#if GTK_CHECK_VERSION(3,0,0)
+	{
+	    GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
+
+	    gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
+# if !GTK_CHECK_VERSION(3,18,0)
+	    gtk_style_context_set_background(sctx, child->window);
+# endif
+	}
+#else
 	gtk_style_set_background(widget->style,
 				 child->window,
 				 GTK_STATE_NORMAL);
+#endif
 
 	gtk_widget_set_parent_window(child->widget, child->window);
+#if !GTK_CHECK_VERSION(3,16,0)
 	gtk_form_set_static_gravity(child->window, TRUE);
+#endif
 	/*
 	 * Install signal handlers to map/unmap child->window
 	 * alongside with the actual widget.
 	 */
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(child->widget), "map",
+			 G_CALLBACK(&gtk_form_child_map), child);
+	g_signal_connect(G_OBJECT(child->widget), "unmap",
+			 G_CALLBACK(&gtk_form_child_unmap), child);
+#else
 	gtk_signal_connect(GTK_OBJECT(child->widget), "map",
 			   GTK_SIGNAL_FUNC(&gtk_form_child_map), child);
 	gtk_signal_connect(GTK_OBJECT(child->widget), "unmap",
 			   GTK_SIGNAL_FUNC(&gtk_form_child_unmap), child);
+#endif
     }
+#if GTK_CHECK_VERSION(3,0,0)
+    else if (!gtk_widget_get_realized(child->widget))
+#else
     else if (!GTK_WIDGET_REALIZED(child->widget))
+#endif
     {
 	gtk_widget_set_parent_window(child->widget, form->bin_window);
     }
@@ -627,8 +949,14 @@ gtk_form_realize_child(GtkForm *form, Gt
     gtk_form_attach_child_window(form, child);
     gtk_widget_realize(child->widget);
 
+#if !GTK_CHECK_VERSION(3,16,0)
     if (child->window == NULL) /* might be already set, see above */
+# if GTK_CHECK_VERSION(3,0,0)
+	gtk_form_set_static_gravity(gtk_widget_get_window(child->widget), TRUE);
+# else
 	gtk_form_set_static_gravity(child->widget->window, TRUE);
+# endif
+#endif
 }
 
     static void
@@ -646,9 +974,18 @@ gtk_form_position_child(GtkForm *form, G
     {
 	if (!child->mapped)
 	{
+#if GTK_CHECK_VERSION(3,0,0)
+	    if (gtk_widget_get_mapped(GTK_WIDGET(form))
+		    && gtk_widget_get_visible(child->widget))
+#else
 	    if (GTK_WIDGET_MAPPED(form) && GTK_WIDGET_VISIBLE(child->widget))
+#endif
 	    {
+#if GTK_CHECK_VERSION(3,0,0)
+		if (!gtk_widget_get_mapped(child->widget))
+#else
 		if (!GTK_WIDGET_MAPPED(child->widget))
+#endif
 		    gtk_widget_map(child->widget);
 
 		child->mapped = TRUE;
@@ -659,15 +996,31 @@ gtk_form_position_child(GtkForm *form, G
 	if (force_allocate)
 	{
 	    GtkAllocation allocation;
+#if GTK_CHECK_VERSION(3,0,0)
+	    GtkRequisition requisition;
 
+	    gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+	    if (!gtk_widget_get_has_window(child->widget))
+#else
 	    if (GTK_WIDGET_NO_WINDOW(child->widget))
+#endif
 	    {
 		if (child->window)
 		{
+#if GTK_CHECK_VERSION(3,0,0)
+		    gdk_window_move_resize(child->window,
+			    x, y,
+			    requisition.width,
+			    requisition.height);
+#else
 		    gdk_window_move_resize(child->window,
 			    x, y,
 			    child->widget->requisition.width,
 			    child->widget->requisition.height);
+#endif
 		}
 
 		allocation.x = 0;
@@ -679,8 +1032,13 @@ gtk_form_position_child(GtkForm *form, G
 		allocation.y = y;
 	    }
 
+#if GTK_CHECK_VERSION(3,0,0)
+	    allocation.width = requisition.width;
+	    allocation.height = requisition.height;
+#else
 	    allocation.width = child->widget->requisition.width;
 	    allocation.height = child->widget->requisition.height;
+#endif
 
 	    gtk_widget_size_allocate(child->widget, &allocation);
 	}
@@ -691,7 +1049,11 @@ gtk_form_position_child(GtkForm *form, G
 	{
 	    child->mapped = FALSE;
 
+#if GTK_CHECK_VERSION(3,0,0)
+	    if (gtk_widget_get_mapped(child->widget))
+#else
 	    if (GTK_WIDGET_MAPPED(child->widget))
+#endif
 		gtk_widget_unmap(child->widget);
 	}
     }
@@ -717,6 +1079,7 @@ gtk_form_position_children(GtkForm *form
  * them or discards them, depending on whether we are obscured
  * or not.
  */
+#if !GTK_CHECK_VERSION(3,0,0)
     static GdkFilterReturn
 gtk_form_filter(GdkXEvent *gdk_xevent, GdkEvent *event UNUSED, gpointer data)
 {
@@ -783,7 +1146,9 @@ gtk_form_main_filter(GdkXEvent *gdk_xeve
     }
     return GDK_FILTER_CONTINUE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
+#if !GTK_CHECK_VERSION(3,16,0)
     static void
 gtk_form_set_static_gravity(GdkWindow *window, gboolean use_static)
 {
@@ -791,13 +1156,18 @@ gtk_form_set_static_gravity(GdkWindow *w
      * results in an annoying assertion error message. */
     gdk_window_set_static_gravities(window, use_static);
 }
+#endif /* !GTK_CHECK_VERSION(3,16,0) */
 
     void
 gtk_form_move_resize(GtkForm *form, GtkWidget *widget,
 		     gint x, gint y, gint w, gint h)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_size_request(widget, w, h);
+#else
     widget->requisition.width  = w;
     widget->requisition.height = h;
+#endif
 
     gtk_form_move(form, widget, x, y);
 }
@@ -811,11 +1181,24 @@ gtk_form_send_configure(GtkForm *form)
     widget = GTK_WIDGET(form);
 
     event.type = GDK_CONFIGURE;
+#if GTK_CHECK_VERSION(3,0,0)
+    event.window = gtk_widget_get_window(widget);
+    {
+	GtkAllocation allocation;
+
+	gtk_widget_get_allocation(widget, &allocation);
+	event.x = allocation.x;
+	event.y = allocation.y;
+	event.width = allocation.width;
+	event.height = allocation.height;
+    }
+#else
     event.window = widget->window;
     event.x = widget->allocation.x;
     event.y = widget->allocation.y;
     event.width = widget->allocation.width;
     event.height = widget->allocation.height;
+#endif
 
     gtk_main_do_event((GdkEvent*)&event);
 }
@@ -841,4 +1224,3 @@ gtk_form_child_unmap(GtkWidget *widget U
     child->mapped = FALSE;
     gdk_window_hide(child->window);
 }
-
--- a/src/gui_gtk_f.h
+++ b/src/gui_gtk_f.h
@@ -9,8 +9,12 @@
 #ifndef __GTK_FORM_H__
 #define __GTK_FORM_H__
 
+#ifdef USE_GTK3
+#include <gtk/gtk.h>
+#else
 #include <gdk/gdk.h>
 #include <gtk/gtkcontainer.h>
+#endif
 
 
 #ifdef __cplusplus
@@ -18,10 +22,17 @@ extern "C" {
 #endif
 
 #define GTK_TYPE_FORM		       (gtk_form_get_type ())
+#ifdef USE_GTK3
+#define GTK_FORM(obj)		       (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_FORM, GtkForm))
+#define GTK_FORM_CLASS(klass)	       (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_FORM, GtkFormClass))
+#define GTK_IS_FORM(obj)	       (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_FORM))
+#define GTK_IS_FORM_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_FORM))
+#else
 #define GTK_FORM(obj)		       (GTK_CHECK_CAST ((obj), GTK_TYPE_FORM, GtkForm))
 #define GTK_FORM_CLASS(klass)	       (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_FORM, GtkFormClass))
 #define GTK_IS_FORM(obj)	       (GTK_CHECK_TYPE ((obj), GTK_TYPE_FORM))
 #define GTK_IS_FORM_CLASS(klass)       (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FORM))
+#endif
 
 
 typedef struct _GtkForm GtkForm;
@@ -33,13 +44,17 @@ struct _GtkForm
 
     GList *children;
 
+#ifndef USE_GTK3
     guint width;
     guint height;
+#endif
 
     GdkWindow *bin_window;
 
+#ifndef USE_GTK3
     GdkVisibilityState visibility;
     gulong configure_serial;
+#endif
 
     gint freeze_count;
 };
@@ -49,7 +64,11 @@ struct _GtkFormClass
     GtkContainerClass parent_class;
 };
 
+#ifdef USE_GTK3
+GType gtk_form_get_type(void);
+#else
 GtkType gtk_form_get_type(void);
+#endif
 
 GtkWidget *gtk_form_new(void);
 
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -19,6 +19,10 @@
  *
  * (C) 2002,2003  Jason Hildebrand  <jason@peaceworks.ca>
  *		  Daniel Elstner  <daniel.elstner@gmx.net>
+ *
+ * Support for GTK+ 3 was added by:
+ *
+ * 2016  Kazunobu Kuriyama  <kazunobu.kuriyama@gmail.com>
  */
 
 #include "vim.h"
@@ -75,14 +79,18 @@ extern void bonobo_dock_item_set_behavio
 # define GdkEventConfigure int
 # define GdkEventClient int
 #else
-# include <gdk/gdkkeysyms.h>
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+#  include <gtk/gtkx.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # include <gdk/gdk.h>
 # ifdef WIN3264
 #  include <gdk/gdkwin32.h>
 # else
 #  include <gdk/gdkx.h>
 # endif
-
 # include <gtk/gtk.h>
 # include "gui_gtk_f.h"
 #endif
@@ -580,6 +588,7 @@ gui_mch_free_all(void)
 }
 #endif
 
+#if !GTK_CHECK_VERSION(3,0,0)
 /*
  * This should be maybe completely removed.
  * Doesn't seem possible, since check_copy_area() relies on
@@ -601,10 +610,93 @@ visibility_event(GtkWidget *widget UNUSE
 			     gui.visibility != GDK_VISIBILITY_UNOBSCURED);
     return FALSE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 /*
  * Redraw the corresponding portions of the screen.
  */
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean is_key_pressed = FALSE;
+
+static gboolean gui_gtk_is_blink_on(void);
+static gboolean gui_gtk_is_no_blink(void);
+static void gui_gtk_window_clear(GdkWindow *win);
+
+    static void
+gui_gtk3_redraw(int x, int y, int width, int height)
+{
+    gui_redraw_block(Y_2_ROW(y), X_2_COL(x),
+	    Y_2_ROW(y + height - 1), X_2_COL(x + width - 1),
+	    GUI_MON_NOCLEAR);
+}
+
+    static void
+gui_gtk3_update_cursor(cairo_t *cr)
+{
+    if (gui.row == gui.cursor_row)
+    {
+	gui.by_signal = TRUE;
+	gui_update_cursor(TRUE, TRUE);
+	gui.by_signal = FALSE;
+	cairo_paint(cr);
+    }
+}
+
+    static gboolean
+gui_gtk3_should_draw_cursor(void)
+{
+    unsigned int cond = 0;
+    cond |= gui_gtk_is_blink_on();
+    cond |= is_key_pressed;
+    cond |= gui.in_focus == FALSE;
+    cond |= gui_gtk_is_no_blink();
+    return  cond;
+}
+
+    static gboolean
+draw_event(GtkWidget *widget,
+	   cairo_t   *cr,
+	   gpointer   user_data UNUSED)
+{
+    /* Skip this when the GUI isn't set up yet, will redraw later. */
+    if (gui.starting)
+	return FALSE;
+
+    out_flush();		/* make sure all output has been processed */
+				/* for GTK+ 3, may induce other draw events. */
+
+    cairo_set_source_surface(cr, gui.surface, 0, 0);
+
+    /* Draw the window without the cursor. */
+    gui.by_signal = TRUE;
+    {
+	cairo_rectangle_list_t *list = NULL;
+
+	gui_gtk_window_clear(gtk_widget_get_window(widget));
+
+	list = cairo_copy_clip_rectangle_list(cr);
+	if (list->status != CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+	{
+	    int i;
+	    for (i = 0; i < list->num_rectangles; i++)
+	    {
+		const cairo_rectangle_t rect = list->rectangles[i];
+		gui_gtk3_redraw(rect.x, rect.y, rect.width, rect.height);
+	    }
+	}
+	cairo_rectangle_list_destroy(list);
+
+	cairo_paint(cr);
+    }
+    gui.by_signal = FALSE;
+
+    /* Add the cursor to the window if necessary.*/
+    if (gui_gtk3_should_draw_cursor())
+	gui_gtk3_update_cursor(cr);
+
+    return FALSE;
+}
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     static gint
 expose_event(GtkWidget *widget UNUSED,
 	     GdkEventExpose *event,
@@ -631,6 +723,7 @@ expose_event(GtkWidget *widget UNUSED,
 
     return FALSE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 #ifdef FEAT_CLIENTSERVER
 /*
@@ -643,7 +736,11 @@ property_event(GtkWidget *widget,
 {
     if (event->type == GDK_PROPERTY_NOTIFY
 	    && event->state == (int)GDK_PROPERTY_NEW_VALUE
+# if GTK_CHECK_VERSION(3,0,0)
+	    && GDK_WINDOW_XID(event->window) == commWindow
+# else
 	    && GDK_WINDOW_XWINDOW(event->window) == commWindow
+# endif
 	    && GET_X_ATOM(event->atom) == commProperty)
     {
 	XEvent xev;
@@ -653,11 +750,16 @@ property_event(GtkWidget *widget,
 	xev.xproperty.atom = commProperty;
 	xev.xproperty.window = commWindow;
 	xev.xproperty.state = PropertyNewValue;
+# if GTK_CHECK_VERSION(3,0,0)
+	serverEventProc(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget)),
+		&xev, 0);
+# else
 	serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev, 0);
+# endif
     }
     return FALSE;
 }
-#endif
+#endif /* defined(FEAT_CLIENTSERVER) */
 
 
 /****************************************************************************
@@ -682,6 +784,20 @@ static long_u blink_ontime = 400;
 static long_u blink_offtime = 250;
 static guint blink_timer = 0;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+gui_gtk_is_blink_on(void)
+{
+    return blink_state == BLINK_ON;
+}
+
+    static gboolean
+gui_gtk_is_no_blink(void)
+{
+    return blink_waittime == 0 || blink_ontime == 0 || blink_offtime == 0;
+}
+#endif
+
     void
 gui_mch_set_blinking(long waittime, long on, long off)
 {
@@ -698,7 +814,11 @@ gui_mch_stop_blink(void)
 {
     if (blink_timer)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_source_remove(blink_timer);
+#else
 	gtk_timeout_remove(blink_timer);
+#endif
 	blink_timer = 0;
     }
     if (blink_state == BLINK_OFF)
@@ -706,22 +826,36 @@ gui_mch_stop_blink(void)
     blink_state = BLINK_NONE;
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 blink_cb(gpointer data UNUSED)
 {
     if (blink_state == BLINK_ON)
     {
 	gui_undraw_cursor();
 	blink_state = BLINK_OFF;
+#if GTK_CHECK_VERSION(3,0,0)
+	blink_timer = g_timeout_add((guint)blink_offtime,
+				   (GSourceFunc) blink_cb, NULL);
+#else
 	blink_timer = gtk_timeout_add((guint32)blink_offtime,
 				   (GtkFunction) blink_cb, NULL);
+#endif
     }
     else
     {
 	gui_update_cursor(TRUE, FALSE);
 	blink_state = BLINK_ON;
+#if GTK_CHECK_VERSION(3,0,0)
+	blink_timer = g_timeout_add((guint)blink_ontime,
+				   (GSourceFunc) blink_cb, NULL);
+#else
 	blink_timer = gtk_timeout_add((guint32)blink_ontime,
 				   (GtkFunction) blink_cb, NULL);
+#endif
     }
 
     return FALSE;		/* don't happen again */
@@ -736,14 +870,23 @@ gui_mch_start_blink(void)
 {
     if (blink_timer)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_source_remove(blink_timer);
+#else
 	gtk_timeout_remove(blink_timer);
+#endif
 	blink_timer = 0;
     }
     /* Only switch blinking on if none of the times is zero */
     if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	blink_timer = g_timeout_add((guint)blink_waittime,
+				   (GSourceFunc) blink_cb, NULL);
+#else
 	blink_timer = gtk_timeout_add((guint32)blink_waittime,
 				   (GtkFunction) blink_cb, NULL);
+#endif
 	blink_state = BLINK_ON;
 	gui_update_cursor(TRUE, FALSE);
     }
@@ -758,7 +901,11 @@ enter_notify_event(GtkWidget *widget UNU
 	gui_mch_start_blink();
 
     /* make sure keyboard input goes there */
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_socket_id == 0 || !gtk_widget_has_focus(gui.drawarea))
+#else
     if (gtk_socket_id == 0 || !GTK_WIDGET_HAS_FOCUS(gui.drawarea))
+#endif
 	gtk_widget_grab_focus(gui.drawarea);
 
     return FALSE;
@@ -938,6 +1085,11 @@ key_press_event(GtkWidget *widget UNUSED
     guint	state;
     char_u	*s, *d;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    is_key_pressed = TRUE;
+    gui_mch_stop_blink();
+#endif
+
     gui.event_time = event->time;
     key_sym = event->keyval;
     state = event->state;
@@ -1127,12 +1279,17 @@ key_press_event(GtkWidget *widget UNUSED
     return TRUE;
 }
 
-#if defined(FEAT_XIM)
+#if defined(FEAT_XIM) || GTK_CHECK_VERSION(3,0,0)
     static gboolean
 key_release_event(GtkWidget *widget UNUSED,
 		  GdkEventKey *event,
 		  gpointer data UNUSED)
 {
+# if GTK_CHECK_VERSION(3,0,0)
+    is_key_pressed = FALSE;
+    gui_mch_start_blink();
+# endif
+# if defined(FEAT_XIM)
     gui.event_time = event->time;
     /*
      * GTK+ 2 input methods may do fancy stuff on key release events too.
@@ -1140,6 +1297,9 @@ key_release_event(GtkWidget *widget UNUS
      * by holding down CTRL-SHIFT and typing hexadecimal digits.
      */
     return xim_queue_key_press_event(event, FALSE);
+# else
+    return TRUE;
+# endif
 }
 #endif
 
@@ -1179,13 +1339,22 @@ selection_received_cb(GtkWidget		*widget
     int		    len;
     int		    motion_type = MAUTO;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_selection(data) == clip_plus.gtk_sel_atom)
+#else
     if (data->selection == clip_plus.gtk_sel_atom)
+#endif
 	cbd = &clip_plus;
     else
 	cbd = &clip_star;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    text = (char_u *)gtk_selection_data_get_data(data);
+    len = gtk_selection_data_get_length(data);
+#else
     text = (char_u *)data->data;
     len  = data->length;
+#endif
 
     if (text == NULL || len <= 0)
     {
@@ -1195,13 +1364,20 @@ selection_received_cb(GtkWidget		*widget
 	return;
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_data_type(data) == vim_atom)
+#else
     if (data->type == vim_atom)
+#endif
     {
 	motion_type = *text++;
 	--len;
     }
-
+#if GTK_CHECK_VERSION(3,0,0)
+    else if (gtk_selection_data_get_data_type(data) == vimenc_atom)
+#else
     else if (data->type == vimenc_atom)
+#endif
     {
 	char_u		*enc;
 	vimconv_T	conv;
@@ -1292,7 +1468,12 @@ selection_get_cb(GtkWidget	    *widget U
     GdkAtom	    type;
     VimClipboard    *cbd;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_selection(selection_data)
+	    == clip_plus.gtk_sel_atom)
+#else
     if (selection_data->selection == clip_plus.gtk_sel_atom)
+#endif
 	cbd = &clip_plus;
     else
 	cbd = &clip_star;
@@ -1361,8 +1542,12 @@ selection_get_cb(GtkWidget	    *widget U
 	    string = tmpbuf;
 	    length += 2;
 
+#if !GTK_CHECK_VERSION(3,0,0)
+	    /* Looks redandunt even for GTK2 because these values are
+	     * overwritten by gtk_selection_data_set() that follows. */
 	    selection_data->type = selection_data->target;
 	    selection_data->format = 16;	/* 16 bits per char */
+#endif
 	    gtk_selection_data_set(selection_data, html_atom, 16,
 							      string, length);
 	    vim_free(string);
@@ -1411,9 +1596,12 @@ selection_get_cb(GtkWidget	    *widget U
 
     if (string != NULL)
     {
+#if !GTK_CHECK_VERSION(3,0,0)
+	/* Looks redandunt even for GTK2 because these values are
+	 * overwritten by gtk_selection_data_set() that follows. */
 	selection_data->type = selection_data->target;
 	selection_data->format = 8;	/* 8 bits per char */
-
+#endif
 	gtk_selection_data_set(selection_data, type, 8, string, length);
 	vim_free(string);
     }
@@ -1493,7 +1681,11 @@ static int mouse_timed_out = TRUE;
 /*
  * Timer used to recognize multiple clicks of the mouse button
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 mouse_click_timer_cb(gpointer data)
 {
     /* we don't use this information currently */
@@ -1505,13 +1697,20 @@ mouse_click_timer_cb(gpointer data)
 
 static guint motion_repeat_timer  = 0;
 static int   motion_repeat_offset = FALSE;
+#ifdef GTK_DEST_DEFAULT_ALL
+static gboolean  motion_repeat_timer_cb(gpointer);
+#else
 static gint  motion_repeat_timer_cb(gpointer);
+#endif
 
     static void
 process_motion_notify(int x, int y, GdkModifierType state)
 {
     int	    button;
     int_u   vim_modifiers;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkAllocation allocation;
+#endif
 
     button = (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
 		       GDK_BUTTON3_MASK | GDK_BUTTON4_MASK |
@@ -1538,9 +1737,17 @@ process_motion_notify(int x, int y, GdkM
     /*
      * Auto repeat timer handling.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_get_allocation(gui.drawarea, &allocation);
+
+    if (x < 0 || y < 0
+	    || x >= allocation.width
+	    || y >= allocation.height)
+#else
     if (x < 0 || y < 0
 	    || x >= gui.drawarea->allocation.width
 	    || y >= gui.drawarea->allocation.height)
+#endif
     {
 
 	int dx;
@@ -1551,8 +1758,13 @@ process_motion_notify(int x, int y, GdkM
 	/* Calculate the maximal distance of the cursor from the drawing area.
 	 * (offshoot can't become negative here!).
 	 */
+#if GTK_CHECK_VERSION(3,0,0)
+	dx = x < 0 ? -x : x - allocation.width;
+	dy = y < 0 ? -y : y - allocation.height;
+#else
 	dx = x < 0 ? -x : x - gui.drawarea->allocation.width;
 	dy = y < 0 ? -y : y - gui.drawarea->allocation.height;
+#endif
 
 	offshoot = dx > dy ? dx : dy;
 
@@ -1577,22 +1789,66 @@ process_motion_notify(int x, int y, GdkM
 
 	/* shoot again */
 	if (!motion_repeat_timer)
+#if GTK_CHECK_VERSION(3,0,0)
+	    motion_repeat_timer = g_timeout_add((guint)delay,
+						motion_repeat_timer_cb, NULL);
+#else
 	    motion_repeat_timer = gtk_timeout_add((guint32)delay,
 						motion_repeat_timer_cb, NULL);
-    }
-}
+#endif
+    }
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+    static GdkDevice *
+gui_gtk_get_pointer_device(GtkWidget *widget)
+{
+    GdkWindow * const win = gtk_widget_get_window(widget);
+    GdkDisplay * const dpy = gdk_window_get_display(win);
+    GdkDeviceManager * const mngr = gdk_display_get_device_manager(dpy);
+    return gdk_device_manager_get_client_pointer(mngr);
+}
+
+    static GdkWindow *
+gui_gtk_get_pointer(GtkWidget       *widget,
+		    gint	    *x,
+		    gint	    *y,
+		    GdkModifierType *state)
+{
+    GdkWindow * const win = gtk_widget_get_window(widget);
+    GdkDevice * const dev = gui_gtk_get_pointer_device(widget);
+    return gdk_window_get_device_position(win, dev , x, y, state);
+}
+
+    static GdkWindow *
+gui_gtk_window_at_position(GtkWidget *widget,
+			   gint      *x,
+			   gint      *y)
+{
+    GdkDevice * const dev = gui_gtk_get_pointer_device(widget);
+    return gdk_device_get_window_at_position(dev, x, y);
+}
+#endif
 
 /*
  * Timer used to recognize multiple clicks of the mouse button.
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 motion_repeat_timer_cb(gpointer data UNUSED)
 {
     int		    x;
     int		    y;
     GdkModifierType state;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gui_gtk_get_pointer(gui.drawarea, &x, &y, &state);
+#else
     gdk_window_get_pointer(gui.drawarea->window, &x, &y, &state);
+#endif
 
     if (!(state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
 		   GDK_BUTTON3_MASK | GDK_BUTTON4_MASK |
@@ -1637,7 +1893,11 @@ motion_notify_event(GtkWidget *widget,
 	int		y;
 	GdkModifierType	state;
 
+#if GTK_CHECK_VERSION(3,0,0)
+	gui_gtk_get_pointer(widget, &x, &y, &state);
+#else
 	gdk_window_get_pointer(widget->window, &x, &y, &state);
+#endif
 	process_motion_notify(x, y, state);
     }
     else
@@ -1668,7 +1928,11 @@ button_press_event(GtkWidget *widget,
     gui.event_time = event->time;
 
     /* Make sure we have focus now we've been selected */
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget))
+#else
     if (gtk_socket_id != 0 && !GTK_WIDGET_HAS_FOCUS(widget))
+#endif
 	gtk_widget_grab_focus(widget);
 
     /*
@@ -1684,14 +1948,23 @@ button_press_event(GtkWidget *widget,
     /* Handle multiple clicks */
     if (!mouse_timed_out && mouse_click_timer)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_source_remove(mouse_click_timer);
+#else
 	gtk_timeout_remove(mouse_click_timer);
+#endif
 	mouse_click_timer = 0;
 	repeated_click = TRUE;
     }
 
     mouse_timed_out = FALSE;
+#if GTK_CHECK_VERSION(3,0,0)
+    mouse_click_timer = g_timeout_add((guint)p_mouset,
+				  mouse_click_timer_cb, &mouse_timed_out);
+#else
     mouse_click_timer = gtk_timeout_add((guint32)p_mouset,
 				  mouse_click_timer_cb, &mouse_timed_out);
+#endif
 
     switch (event->button)
     {
@@ -1730,7 +2003,11 @@ scroll_event(GtkWidget *widget,
     int	    button;
     int_u   vim_modifiers;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget))
+#else
     if (gtk_socket_id != 0 && !GTK_WIDGET_HAS_FOCUS(widget))
+#endif
 	gtk_widget_grab_focus(widget);
 
     switch (event->direction)
@@ -1781,7 +2058,11 @@ button_release_event(GtkWidget *widget U
        area .*/
     if (motion_repeat_timer)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_source_remove(motion_repeat_timer);
+#else
 	gtk_timeout_remove(motion_repeat_timer);
+#endif
 	motion_repeat_timer = 0;
     }
 
@@ -1896,7 +2177,13 @@ drag_handle_uri_list(GdkDragContext	*con
     char_u  **fnames;
     int	    nfiles = 0;
 
+# if GTK_CHECK_VERSION(3,0,0)
+    fnames = parse_uri_list(&nfiles,
+	    (char_u *)gtk_selection_data_get_data(data),
+	    gtk_selection_data_get_length(data));
+# else
     fnames = parse_uri_list(&nfiles, data->data, data->length);
+# endif
 
     if (fnames != NULL && nfiles > 0)
     {
@@ -1923,10 +2210,19 @@ drag_handle_text(GdkDragContext	    *con
     int	    len;
     char_u  *tmpbuf = NULL;
 
+# if GTK_CHECK_VERSION(3,0,0)
+    text = (char_u *)gtk_selection_data_get_data(data);
+    len = gtk_selection_data_get_length(data);
+# else
     text = data->data;
     len  = data->length;
-
+# endif
+
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_data_type(data) == utf8_string_atom)
+# else
     if (data->type == utf8_string_atom)
+# endif
     {
 	if (input_conv.vc_type != CONV_NONE)
 	    tmpbuf = string_convert(&input_conv, text, &len);
@@ -1962,10 +2258,21 @@ drag_data_received_cb(GtkWidget		*widget
     GdkModifierType state;
 
     /* Guard against trash */
+# if GTK_CHECK_VERSION(3,0,0)
+    const guchar * const data_data = gtk_selection_data_get_data(data);
+    const gint data_length = gtk_selection_data_get_length(data);
+    const gint data_format = gtk_selection_data_get_format(data);
+
+    if (data_data == NULL
+	    || data_length <= 0
+	    || data_format != 8
+	    || data_data[data_length] != '\0')
+# else
     if (data->data == NULL
 	    || data->length <= 0
 	    || data->format != 8
 	    || data->data[data->length] != '\0')
+# endif
     {
 	gtk_drag_finish(context, FALSE, FALSE, time_);
 	return;
@@ -1973,7 +2280,11 @@ drag_data_received_cb(GtkWidget		*widget
 
     /* Get the current modifier state for proper distinguishment between
      * different operations later. */
+#if GTK_CHECK_VERSION(3,0,0)
+    gui_gtk_get_pointer(widget, NULL, NULL, &state);
+# else
     gdk_window_get_pointer(widget->window, NULL, NULL, &state);
+# endif
 
     /* Not sure about the role of "text/plain" here... */
     if (info == (guint)TARGET_TEXT_URI_LIST)
@@ -2253,7 +2564,7 @@ setup_save_yourself(void)
     Atom    *existing_atoms = NULL;
     int	    count = 0;
 
-#ifdef USE_XSMP
+# ifdef USE_XSMP
     if (xsmp_icefd != -1)
     {
 	/*
@@ -2264,16 +2575,25 @@ setup_save_yourself(void)
 
 	g_io_add_watch(g_io, G_IO_IN | G_IO_ERR | G_IO_HUP,
 				  local_xsmp_handle_requests, (gpointer)g_io);
+	g_io_channel_unref(g_io);
     }
     else
-#endif
+# endif
     {
 	/* Fall back to old method */
 
 	/* first get the existing value */
+# if GTK_CHECK_VERSION(3,0,0)
+	GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
+
+	if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(mainwin_win),
+		    GDK_WINDOW_XID(mainwin_win),
+		    &existing_atoms, &count))
+# else
 	if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 		    GDK_WINDOW_XWINDOW(gui.mainwin->window),
 		    &existing_atoms, &count))
+# endif
 	{
 	    Atom	*new_atoms;
 	    Atom	save_yourself_xatom;
@@ -2295,8 +2615,13 @@ setup_save_yourself(void)
 		{
 		    memcpy(new_atoms, existing_atoms, count * sizeof(Atom));
 		    new_atoms[count] = save_yourself_xatom;
+# if GTK_CHECK_VERSION(3,0,0)
+		    XSetWMProtocols(GDK_WINDOW_XDISPLAY(mainwin_win),
+			    GDK_WINDOW_XID(mainwin_win),
+# else
 		    XSetWMProtocols(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 			    GDK_WINDOW_XWINDOW(gui.mainwin->window),
+# endif
 			    new_atoms, count + 1);
 		    vim_free(new_atoms);
 		}
@@ -2341,8 +2666,13 @@ global_event_filter(GdkXEvent *xev,
 	 * know we are done saving ourselves.  We don't want to be
 	 * restarted, thus set argv to NULL.
 	 */
+# if GTK_CHECK_VERSION(3,0,0)
+	XSetCommand(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
+		    GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin)),
+# else
 	XSetCommand(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 		    GDK_WINDOW_XWINDOW(gui.mainwin->window),
+# endif
 		    NULL, 0);
 	return GDK_FILTER_REMOVE;
     }
@@ -2376,10 +2706,18 @@ mainwin_realize(GtkWidget *widget UNUSED
 #undef magick
 # undef static
 
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
+#endif
+
     /* When started with "--echo-wid" argument, write window ID on stdout. */
     if (echo_wid_arg)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	printf("WID: %ld\n", (long)GDK_WINDOW_XID(mainwin_win));
+#else
 	printf("WID: %ld\n", (long)GDK_WINDOW_XWINDOW(gui.mainwin->window));
+#endif
 	fflush(stdout);
     }
 
@@ -2416,10 +2754,17 @@ mainwin_realize(GtkWidget *widget UNUSED
     if (serverName == NULL && serverDelayedStartName != NULL)
     {
 	/* This is a :gui command in a plain vim with no previous server */
+# if GTK_CHECK_VERSION(3,0,0)
+	commWindow = GDK_WINDOW_XID(mainwin_win);
+
+	(void)serverRegisterName(GDK_WINDOW_XDISPLAY(mainwin_win),
+				 serverDelayedStartName);
+# else
 	commWindow = GDK_WINDOW_XWINDOW(gui.mainwin->window);
 
 	(void)serverRegisterName(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 				 serverDelayedStartName);
+# endif
     }
     else
     {
@@ -2428,12 +2773,22 @@ mainwin_realize(GtkWidget *widget UNUSED
 	 * have to change the "server" registration to that of the main window
 	 * If we have not registered a name yet, remember the window
 	 */
+# if GTK_CHECK_VERSION(3,0,0)
+	serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(mainwin_win),
+				     GDK_WINDOW_XID(mainwin_win));
+# else
 	serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 				     GDK_WINDOW_XWINDOW(gui.mainwin->window));
+# endif
     }
     gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK);
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.mainwin), "property-notify-event",
+		     G_CALLBACK(property_event), NULL);
+# else
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "property_notify_event",
 		       GTK_SIGNAL_FUNC(property_event), NULL);
+# endif
 #endif
 }
 
@@ -2441,21 +2796,60 @@ mainwin_realize(GtkWidget *widget UNUSED
 create_blank_pointer(void)
 {
     GdkWindow	*root_window = NULL;
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkPixbuf   *blank_mask;
+#else
     GdkPixmap	*blank_mask;
+#endif
     GdkCursor	*cursor;
     GdkColor	color = { 0, 0, 0, 0 };
+#if !GTK_CHECK_VERSION(3,0,0)
     char	blank_data[] = { 0x0 };
+#endif
 
 #ifdef HAVE_GTK_MULTIHEAD
+# if GTK_CHECK_VERSION(3,12,0)
+    {
+	GdkWindow * const win = gtk_widget_get_window(gui.mainwin);
+	GdkScreen * const scrn = gdk_window_get_screen(win);
+	root_window = gdk_screen_get_root_window(scrn);
+    }
+# else
     root_window = gtk_widget_get_root_window(gui.mainwin);
+# endif
 #endif
 
     /* Create a pseudo blank pointer, which is in fact one pixel by one pixel
      * in size. */
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+	cairo_surface_t *surf;
+	cairo_t		*cr;
+
+	surf = cairo_image_surface_create(CAIRO_FORMAT_A1, 1, 1);
+	cr = cairo_create(surf);
+
+	cairo_set_source_rgb(cr,
+			     color.red / 65535.0,
+			     color.green / 65535.0,
+			     color.blue / 65535.0);
+	cairo_rectangle(cr, 0, 0, 1, 1);
+	cairo_fill(cr);
+	cairo_destroy(cr);
+
+	blank_mask = gdk_pixbuf_get_from_surface(surf, 0, 0, 1, 1);
+	cairo_surface_destroy(surf);
+
+	cursor = gdk_cursor_new_from_pixbuf(gdk_window_get_display(root_window),
+					    blank_mask, 0, 0);
+	g_object_unref(blank_mask);
+    }
+#else
     blank_mask = gdk_bitmap_create_from_data(root_window, blank_data, 1, 1);
     cursor = gdk_cursor_new_from_pixmap(blank_mask, blank_mask,
 					&color, &color, 0, 0);
     gdk_bitmap_unref(blank_mask);
+#endif
 
     return cursor;
 }
@@ -2473,12 +2867,22 @@ mainwin_screen_changed_cb(GtkWidget  *wi
      * Recreate the invisible mouse cursor.
      */
     if (gui.blank_pointer != NULL)
+# if GTK_CHECK_VERSION(3,0,0)
+	g_object_unref(G_OBJECT(gui.blank_pointer));
+# else
 	gdk_cursor_unref(gui.blank_pointer);
+# endif
 
     gui.blank_pointer = create_blank_pointer();
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gui.pointer_hidden && gtk_widget_get_window(gui.drawarea) != NULL)
+	gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea),
+		gui.blank_pointer);
+# else
     if (gui.pointer_hidden && gui.drawarea->window != NULL)
 	gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
+# endif
 
     /*
      * Create a new PangoContext for this screen, and initialize it
@@ -2509,28 +2913,54 @@ mainwin_screen_changed_cb(GtkWidget  *wi
 drawarea_realize_cb(GtkWidget *widget, gpointer data UNUSED)
 {
     GtkWidget *sbar;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkAllocation allocation;
+#endif
 
 #ifdef FEAT_XIM
     xim_init();
 #endif
     gui_mch_new_colors();
+#if GTK_CHECK_VERSION(3,0,0)
+    gui.surface = gdk_window_create_similar_surface(
+	    gtk_widget_get_window(widget),
+	    CAIRO_CONTENT_COLOR_ALPHA,
+	    gtk_widget_get_allocated_width(widget),
+	    gtk_widget_get_allocated_height(widget));
+#else
     gui.text_gc = gdk_gc_new(gui.drawarea->window);
+#endif
 
     gui.blank_pointer = create_blank_pointer();
     if (gui.pointer_hidden)
+#if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_set_cursor(gtk_widget_get_window(widget), gui.blank_pointer);
+#else
 	gdk_window_set_cursor(widget->window, gui.blank_pointer);
+#endif
 
     /* get the actual size of the scrollbars, if they are realized */
     sbar = firstwin->w_scrollbars[SBAR_LEFT].id;
     if (!sbar || (!gui.which_scrollbars[SBAR_LEFT]
 				    && firstwin->w_scrollbars[SBAR_RIGHT].id))
 	sbar = firstwin->w_scrollbars[SBAR_RIGHT].id;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_get_allocation(sbar, &allocation);
+    if (sbar && gtk_widget_get_realized(sbar) && allocation.width)
+	gui.scrollbar_width = allocation.width;
+#else
     if (sbar && GTK_WIDGET_REALIZED(sbar) && sbar->allocation.width)
 	gui.scrollbar_width = sbar->allocation.width;
+#endif
 
     sbar = gui.bottom_sbar.id;
+#if GTK_CHECK_VERSION(3,0,0)
+    if (sbar && gtk_widget_get_realized(sbar) && allocation.height)
+	gui.scrollbar_height = allocation.height;
+#else
     if (sbar && GTK_WIDGET_REALIZED(sbar) && sbar->allocation.height)
 	gui.scrollbar_height = sbar->allocation.height;
+#endif
 }
 
 /*
@@ -2558,10 +2988,22 @@ drawarea_unrealize_cb(GtkWidget *widget 
     g_object_unref(gui.text_context);
     gui.text_context = NULL;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.surface != NULL)
+    {
+	cairo_surface_destroy(gui.surface);
+	gui.surface = NULL;
+    }
+#else
     g_object_unref(gui.text_gc);
     gui.text_gc = NULL;
-
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+    g_object_unref(G_OBJECT(gui.blank_pointer));
+#else
     gdk_cursor_unref(gui.blank_pointer);
+#endif
     gui.blank_pointer = NULL;
 }
 
@@ -2573,6 +3015,38 @@ drawarea_style_set_cb(GtkWidget	*widget 
     gui_mch_new_colors();
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+drawarea_configure_event_cb(GtkWidget	      *widget,
+			    GdkEventConfigure *event,
+			    gpointer	       data UNUSED)
+{
+    static int cur_width = 0;
+    static int cur_height = 0;
+
+    g_return_val_if_fail(event
+	    && event->width >= 1 && event->height >= 1, TRUE);
+
+    if (event->width == cur_width && event->height == cur_height)
+	return TRUE;
+
+    cur_width = event->width;
+    cur_height = event->height;
+
+    if (gui.surface != NULL)
+	cairo_surface_destroy(gui.surface);
+
+    gui.surface = gdk_window_create_similar_surface(
+	    gtk_widget_get_window(widget),
+	    CAIRO_CONTENT_COLOR_ALPHA,
+	    event->width, event->height);
+
+    gtk_widget_queue_draw(widget);
+
+    return TRUE;
+}
+#endif
+
 /*
  * Callback routine for the "delete_event" signal on the toplevel window.
  * Tries to vim gracefully, or refuses to exit with changed buffers.
@@ -2592,7 +3066,7 @@ get_item_dimensions(GtkWidget *widget, G
 {
     GtkOrientation item_orientation = GTK_ORIENTATION_HORIZONTAL;
 
-#ifdef FEAT_GUI_GNOME
+# ifdef FEAT_GUI_GNOME
     if (using_gnome && widget != NULL)
     {
 	GtkWidget *parent;
@@ -2611,16 +3085,34 @@ get_item_dimensions(GtkWidget *widget, G
 	    item_orientation = bonobo_dock_item_get_orientation(dockitem);
 	}
     }
-#endif
+# endif
+# if GTK_CHECK_VERSION(3,0,0)
+    if (widget != NULL
+	    && item_orientation == orientation
+	    && gtk_widget_get_realized(widget)
+	    && gtk_widget_get_visible(widget))
+# else
     if (widget != NULL
 	    && item_orientation == orientation
 	    && GTK_WIDGET_REALIZED(widget)
 	    && GTK_WIDGET_VISIBLE(widget))
-    {
+# endif
+    {
+# if GTK_CHECK_VERSION(3,0,0)
+	GtkAllocation allocation;
+
+	gtk_widget_get_allocation(widget, &allocation);
+
+	if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	    return allocation.height;
+	else
+	    return allocation.width;
+# else
 	if (orientation == GTK_ORIENTATION_HORIZONTAL)
 	    return widget->allocation.height;
 	else
 	    return widget->allocation.width;
+# endif
     }
     return 0;
 }
@@ -2774,6 +3266,17 @@ icon_size_changed_foreach(GtkWidget *wid
     {
 	GtkImage *image = (GtkImage *)widget;
 
+# if GTK_CHECK_VERSION(3,10,0)
+	if (gtk_image_get_storage_type(image) == GTK_IMAGE_ICON_NAME)
+	{
+	    const GtkIconSize icon_size = GPOINTER_TO_INT(user_data);
+	    const gchar *icon_name;
+
+	    gtk_image_get_icon_name(image, &icon_name, NULL);
+
+	    gtk_image_set_from_icon_name(image, icon_name, icon_size);
+	}
+# else
 	/* User-defined icons are stored in a GtkIconSet */
 	if (gtk_image_get_storage_type(image) == GTK_IMAGE_ICON_SET)
 	{
@@ -2787,6 +3290,7 @@ icon_size_changed_foreach(GtkWidget *wid
 	    gtk_image_set_from_icon_set(image, icon_set, icon_size);
 	    gtk_icon_set_unref(icon_set);
 	}
+# endif
     }
     else if (GTK_IS_CONTAINER(widget))
     {
@@ -2815,7 +3319,9 @@ set_toolbar_style(GtkToolbar *toolbar)
 	style = GTK_TOOLBAR_ICONS;
 
     gtk_toolbar_set_style(toolbar, style);
+# if !GTK_CHECK_VERSION(3,0,0)
     gtk_toolbar_set_tooltips(toolbar, (toolbar_flags & TOOLBAR_TOOLTIPS) != 0);
+# endif
 
     switch (tbis_flags)
     {
@@ -2847,7 +3353,9 @@ set_toolbar_style(GtkToolbar *toolbar)
 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
 static int ignore_tabline_evt = FALSE;
 static GtkWidget *tabline_menu;
+# if !GTK_CHECK_VERSION(3,0,0)
 static GtkTooltips *tabline_tooltip;
+# endif
 static int clicked_page;	    /* page clicked in tab line */
 
 /*
@@ -2872,9 +3380,15 @@ add_tabline_menu_item(GtkWidget *menu, c
     CONVERT_TO_UTF8_FREE(utf_text);
 
     gtk_container_add(GTK_CONTAINER(menu), item);
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(item), "activate",
+	    G_CALLBACK(tabline_menu_handler),
+	    GINT_TO_POINTER(resp));
+# else
     gtk_signal_connect(GTK_OBJECT(item), "activate",
 	    GTK_SIGNAL_FUNC(tabline_menu_handler),
 	    (gpointer)(long)resp);
+# endif
 }
 
 /*
@@ -2916,10 +3430,20 @@ on_tabline_menu(GtkWidget *widget, GdkEv
 	   )
 	    return TRUE;
 
+# if GTK_CHECK_VERSION(3,0,0)
+	tabwin = gui_gtk_window_at_position(gui.mainwin, &x, &y);
+# else
 	tabwin = gdk_window_at_pointer(&x, &y);
+# endif
+
 	gdk_window_get_user_data(tabwin, (gpointer)&tabwidget);
+# if GTK_CHECK_VERSION(3,0,0)
+	clicked_page = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tabwidget),
+							 "tab_num"));
+# else
 	clicked_page = (int)(long)gtk_object_get_user_data(
 						       GTK_OBJECT(tabwidget));
+# endif
 
 	/* If the event was generated for 3rd button popup the menu. */
 	if (bevent->button == 3)
@@ -2950,7 +3474,11 @@ on_tabline_menu(GtkWidget *widget, GdkEv
     static void
 on_select_tab(
 	GtkNotebook	*notebook UNUSED,
+# if GTK_CHECK_VERSION(3,0,0)
+	gpointer	*page UNUSED,
+# else
 	GtkNotebookPage *page UNUSED,
+# endif
 	gint		idx,
 	gpointer	data UNUSED)
 {
@@ -2975,7 +3503,11 @@ gui_mch_show_tabline(int showit)
 	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), showit);
 	update_window_manager_hints(0, 0);
 	if (showit)
+# if GTK_CHECK_VERSION(3,0,0)
+	    gtk_widget_set_can_focus(GTK_WIDGET(gui.tabline), FALSE);
+# else
 	    GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(gui.tabline), GTK_CAN_FOCUS);
+# endif
     }
 
     gui_mch_update();
@@ -3023,12 +3555,19 @@ gui_mch_update_tabline(void)
 	if (page == NULL)
 	{
 	    /* Add notebook page */
+# if GTK_CHECK_VERSION(3,2,0)
+	    page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+	    gtk_box_set_homogeneous(GTK_BOX(page), FALSE);
+# else
 	    page = gtk_vbox_new(FALSE, 0);
+# endif
 	    gtk_widget_show(page);
 	    event_box = gtk_event_box_new();
 	    gtk_widget_show(event_box);
 	    label = gtk_label_new("-Empty-");
+# if !GTK_CHECK_VERSION(3,14,0)
 	    gtk_misc_set_padding(GTK_MISC(label), 2, 2);
+# endif
 	    gtk_container_add(GTK_CONTAINER(event_box), label);
 	    gtk_widget_show(label);
 	    gtk_notebook_insert_page(GTK_NOTEBOOK(gui.tabline),
@@ -3038,9 +3577,18 @@ gui_mch_update_tabline(void)
 	}
 
 	event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(gui.tabline), page);
+# if GTK_CHECK_VERSION(3,0,0)
+	g_object_set_data(G_OBJECT(event_box), "tab_num",
+						     GINT_TO_POINTER(tab_num));
+# else
 	gtk_object_set_user_data(GTK_OBJECT(event_box),
 						     (gpointer)(long)tab_num);
+# endif
+# if GTK_CHECK_VERSION(3,0,0)
+	label = gtk_bin_get_child(GTK_BIN(event_box));
+# else
 	label = GTK_BIN(event_box)->child;
+# endif
 	get_tabline_label(tp, FALSE);
 	labeltext = CONVERT_TO_UTF8(NameBuff);
 	gtk_label_set_text(GTK_LABEL(label), (const char *)labeltext);
@@ -3048,8 +3596,12 @@ gui_mch_update_tabline(void)
 
 	get_tabline_label(tp, TRUE);
 	labeltext = CONVERT_TO_UTF8(NameBuff);
+# if GTK_CHECK_VERSION(3,0,0)
+	gtk_widget_set_tooltip_text(event_box, (const gchar *)labeltext);
+# else
 	gtk_tooltips_set_tip(GTK_TOOLTIPS(tabline_tooltip), event_box,
 			     (const char *)labeltext, NULL);
+# endif
 	CONVERT_TO_UTF8_FREE(labeltext);
     }
 
@@ -3057,8 +3609,13 @@ gui_mch_update_tabline(void)
     while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr) != NULL)
 	gtk_notebook_remove_page(GTK_NOTEBOOK(gui.tabline), nr);
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_notebook_get_current_page(GTK_NOTEBOOK(gui.tabline)) != curtabidx)
+	gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), curtabidx);
+# else
     if (gtk_notebook_current_page(GTK_NOTEBOOK(gui.tabline)) != curtabidx)
 	gtk_notebook_set_page(GTK_NOTEBOOK(gui.tabline), curtabidx);
+# endif
 
     /* Make sure everything is in place before drawing text. */
     gui_mch_update();
@@ -3076,8 +3633,13 @@ gui_mch_set_curtab(int nr)
 	return;
 
     ignore_tabline_evt = TRUE;
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_notebook_get_current_page(GTK_NOTEBOOK(gui.tabline)) != nr - 1)
+	gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), nr - 1);
+# else
     if (gtk_notebook_current_page(GTK_NOTEBOOK(gui.tabline)) != nr - 1)
 	gtk_notebook_set_page(GTK_NOTEBOOK(gui.tabline), nr - 1);
+# endif
     ignore_tabline_evt = FALSE;
 }
 
@@ -3187,8 +3749,10 @@ gui_mch_init(void)
 #endif
     /* FIXME: Need to install the classic icons and a gtkrc.classic file.
      * The hard part is deciding install locations and the Makefile magic. */
-#if 0
+#if !GTK_CHECK_VERSION(3,0,0)
+# if 0
     gtk_rc_parse("gtkrc");
+# endif
 #endif
 
     /* Initialize values */
@@ -3221,7 +3785,11 @@ gui_mch_init(void)
 #else
 	plug = gtk_plug_new(gtk_socket_id);
 #endif
+#if GTK_CHECK_VERSION(3,0,0)
+	if (plug != NULL && gtk_plug_get_socket_window(GTK_PLUG(plug)) != NULL)
+#else
 	if (plug != NULL && GTK_PLUG(plug)->socket_window != NULL)
+#endif
 	{
 	    gui.mainwin = plug;
 	}
@@ -3256,14 +3824,26 @@ gui_mch_init(void)
     gui.text_context = gtk_widget_create_pango_context(gui.mainwin);
     pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(gui.mainwin), 0);
+#else
     gtk_container_border_width(GTK_CONTAINER(gui.mainwin), 0);
+#endif
     gtk_widget_add_events(gui.mainwin, GDK_VISIBILITY_NOTIFY_MASK);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.mainwin), "delete-event",
+		     G_CALLBACK(&delete_event_cb), NULL);
+
+    g_signal_connect(G_OBJECT(gui.mainwin), "realize",
+		     G_CALLBACK(&mainwin_realize), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "delete_event",
 		       GTK_SIGNAL_FUNC(&delete_event_cb), NULL);
 
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "realize",
 		       GTK_SIGNAL_FUNC(&mainwin_realize), NULL);
+#endif
 #ifdef HAVE_GTK_MULTIHEAD
     g_signal_connect(G_OBJECT(gui.mainwin), "screen_changed",
 		     G_CALLBACK(&mainwin_screen_changed_cb), NULL);
@@ -3272,7 +3852,12 @@ gui_mch_init(void)
     gtk_window_add_accel_group(GTK_WINDOW(gui.mainwin), gui.accel_group);
 
     /* A vertical box holds the menubar, toolbar and main text window. */
+#if GTK_CHECK_VERSION(3,2,0)
+    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+    gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
+#else
     vbox = gtk_vbox_new(FALSE, 0);
+#endif
 
 #ifdef FEAT_GUI_GNOME
     if (using_gnome)
@@ -3335,11 +3920,17 @@ gui_mch_init(void)
      * Create the toolbar and handle
      */
     /* some aesthetics on the toolbar */
+# ifdef USE_GTK3
+    /* TODO: Add GTK+ 3 code here using GtkCssProvider if neccessary. */
+    /* N.B.  Since the default value of GtkToolbar::button-relief is
+     * GTK_RELIEF_NONE, there's no need to specify that, probably. */
+# else
     gtk_rc_parse_string(
 	    "style \"vim-toolbar-style\" {\n"
 	    "  GtkToolbar::button_relief = GTK_RELIEF_NONE\n"
 	    "}\n"
 	    "widget \"*.vim-toolbar\" style \"vim-toolbar-style\"\n");
+# endif
     gui.toolbar = gtk_toolbar_new();
     gtk_widget_set_name(gui.toolbar, "vim-toolbar");
     set_toolbar_style(GTK_TOOLBAR(gui.toolbar));
@@ -3381,42 +3972,77 @@ gui_mch_init(void)
     gtk_notebook_set_show_border(GTK_NOTEBOOK(gui.tabline), FALSE);
     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), FALSE);
     gtk_notebook_set_scrollable(GTK_NOTEBOOK(gui.tabline), TRUE);
+# if !GTK_CHECK_VERSION(3,0,0)
     gtk_notebook_set_tab_border(GTK_NOTEBOOK(gui.tabline), FALSE);
-
+# endif
+
+# if !GTK_CHECK_VERSION(3,0,0)
     tabline_tooltip = gtk_tooltips_new();
     gtk_tooltips_enable(GTK_TOOLTIPS(tabline_tooltip));
+# endif
 
     {
 	GtkWidget *page, *label, *event_box;
 
 	/* Add the first tab. */
+# if GTK_CHECK_VERSION(3,2,0)
+	page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+	gtk_box_set_homogeneous(GTK_BOX(page), FALSE);
+# else
 	page = gtk_vbox_new(FALSE, 0);
+# endif
 	gtk_widget_show(page);
 	gtk_container_add(GTK_CONTAINER(gui.tabline), page);
 	label = gtk_label_new("-Empty-");
 	gtk_widget_show(label);
 	event_box = gtk_event_box_new();
 	gtk_widget_show(event_box);
+# if GTK_CHECK_VERSION(3,0,0)
+	g_object_set_data(G_OBJECT(event_box), "tab_num", GINT_TO_POINTER(1L));
+# else
 	gtk_object_set_user_data(GTK_OBJECT(event_box), (gpointer)1L);
+# endif
+# if !GTK_CHECK_VERSION(3,14,0)
 	gtk_misc_set_padding(GTK_MISC(label), 2, 2);
+# endif
 	gtk_container_add(GTK_CONTAINER(event_box), label);
 	gtk_notebook_set_tab_label(GTK_NOTEBOOK(gui.tabline), page, event_box);
     }
 
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.tabline), "switch-page",
+		     G_CALLBACK(on_select_tab), NULL);
+# else
     gtk_signal_connect(GTK_OBJECT(gui.tabline), "switch_page",
 		       GTK_SIGNAL_FUNC(on_select_tab), NULL);
+# endif
 
     /* Create a popup menu for the tab line and connect it. */
     tabline_menu = create_tabline_menu();
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect_swapped(G_OBJECT(gui.tabline), "button-press-event",
+	    G_CALLBACK(on_tabline_menu), G_OBJECT(tabline_menu));
+# else
     gtk_signal_connect_object(GTK_OBJECT(gui.tabline), "button_press_event",
 	    GTK_SIGNAL_FUNC(on_tabline_menu), GTK_OBJECT(tabline_menu));
-#endif
+# endif
+#endif /* FEAT_GUI_TABLINE */
 
     gui.formwin = gtk_form_new();
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(gui.formwin), 0);
+#else
     gtk_container_border_width(GTK_CONTAINER(gui.formwin), 0);
+#endif
+#if !GTK_CHECK_VERSION(3,0,0)
     gtk_widget_set_events(gui.formwin, GDK_EXPOSURE_MASK);
+#endif
 
     gui.drawarea = gtk_drawing_area_new();
+#if GTK_CHECK_VERSION(3,0,0)
+    gui.surface = NULL;
+    gui.by_signal = FALSE;
+#endif
 
     /* Determine which events we will filter. */
     gtk_widget_set_events(gui.drawarea,
@@ -3438,18 +4064,35 @@ gui_mch_init(void)
 
     /* For GtkSockets, key-presses must go to the focus widget (drawarea)
      * and not the window. */
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect((gtk_socket_id == 0) ? G_OBJECT(gui.mainwin)
+					  : G_OBJECT(gui.drawarea),
+		       "key-press-event",
+		       G_CALLBACK(key_press_event), NULL);
+#else
     gtk_signal_connect((gtk_socket_id == 0) ? GTK_OBJECT(gui.mainwin)
 					    : GTK_OBJECT(gui.drawarea),
 		       "key_press_event",
 		       GTK_SIGNAL_FUNC(key_press_event), NULL);
-#if defined(FEAT_XIM)
+#endif
+#if defined(FEAT_XIM) || GTK_CHECK_VERSION(3,0,0)
     /* Also forward key release events for the benefit of GTK+ 2 input
      * modules.  Try CTRL-SHIFT-xdigits to enter a Unicode code point. */
     g_signal_connect((gtk_socket_id == 0) ? G_OBJECT(gui.mainwin)
 					  : G_OBJECT(gui.drawarea),
-		     "key_release_event",
+		     "key-release-event",
 		     G_CALLBACK(&key_release_event), NULL);
 #endif
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "realize",
+		     G_CALLBACK(drawarea_realize_cb), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "unrealize",
+		     G_CALLBACK(drawarea_unrealize_cb), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "configure-event",
+	    G_CALLBACK(drawarea_configure_event_cb), NULL);
+    g_signal_connect_after(G_OBJECT(gui.drawarea), "style-set",
+			   G_CALLBACK(&drawarea_style_set_cb), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "realize",
 		       GTK_SIGNAL_FUNC(drawarea_realize_cb), NULL);
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "unrealize",
@@ -3457,8 +4100,11 @@ gui_mch_init(void)
 
     gtk_signal_connect_after(GTK_OBJECT(gui.drawarea), "style_set",
 			     GTK_SIGNAL_FUNC(&drawarea_style_set_cb), NULL);
-
+#endif
+
+#if !GTK_CHECK_VERSION(3,0,0)
     gui.visibility = GDK_VISIBILITY_UNOBSCURED;
+#endif
 
 #if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION))
     wm_protocols_atom = gdk_atom_intern("WM_PROTOCOLS", FALSE);
@@ -3467,7 +4113,11 @@ gui_mch_init(void)
 
     if (gtk_socket_id != 0)
 	/* make sure keyboard input can go to the drawarea */
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_widget_set_can_focus(gui.drawarea, TRUE);
+#else
 	GTK_WIDGET_SET_FLAGS(gui.drawarea, GTK_CAN_FOCUS);
+#endif
 
     /*
      * Set clipboard specific atoms
@@ -3482,10 +4132,15 @@ gui_mch_init(void)
      */
     gui.border_offset = gui.border_width;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "draw",
+		     G_CALLBACK(draw_event), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "visibility_notify_event",
 		       GTK_SIGNAL_FUNC(visibility_event), NULL);
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "expose_event",
 		       GTK_SIGNAL_FUNC(expose_event), NULL);
+#endif
 
     /*
      * Only install these enter/leave callbacks when 'p' in 'guioptions'.
@@ -3493,10 +4148,17 @@ gui_mch_init(void)
      */
     if (vim_strchr(p_go, GO_POINTER) != NULL)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.drawarea), "leave-notify-event",
+			 G_CALLBACK(leave_notify_event), NULL);
+	g_signal_connect(G_OBJECT(gui.drawarea), "enter-notify-event",
+			 G_CALLBACK(enter_notify_event), NULL);
+#else
 	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "leave_notify_event",
 			   GTK_SIGNAL_FUNC(leave_notify_event), NULL);
 	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "enter_notify_event",
 			   GTK_SIGNAL_FUNC(enter_notify_event), NULL);
+#endif
     }
 
     /* Real windows can get focus ... GtkPlug, being a mere container can't,
@@ -3505,25 +4167,56 @@ gui_mch_init(void)
      */
     if (gtk_socket_id == 0)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.mainwin), "focus-out-event",
+			 G_CALLBACK(focus_out_event), NULL);
+	g_signal_connect(G_OBJECT(gui.mainwin), "focus-in-event",
+			 G_CALLBACK(focus_in_event), NULL);
+#else
 	gtk_signal_connect(GTK_OBJECT(gui.mainwin), "focus_out_event",
 			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
 	gtk_signal_connect(GTK_OBJECT(gui.mainwin), "focus_in_event",
 			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
+#endif
     }
     else
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.drawarea), "focus-out-event",
+			 G_CALLBACK(focus_out_event), NULL);
+	g_signal_connect(G_OBJECT(gui.drawarea), "focus-in-event",
+			 G_CALLBACK(focus_in_event), NULL);
+#else
 	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "focus_out_event",
 			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
 	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "focus_in_event",
 			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
+#endif
 #ifdef FEAT_GUI_TABLINE
+# if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.tabline), "focus-out-event",
+			 G_CALLBACK(focus_out_event), NULL);
+	g_signal_connect(G_OBJECT(gui.tabline), "focus-in-event",
+			 G_CALLBACK(focus_in_event), NULL);
+# else
 	gtk_signal_connect(GTK_OBJECT(gui.tabline), "focus_out_event",
 			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
 	gtk_signal_connect(GTK_OBJECT(gui.tabline), "focus_in_event",
 			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
+# endif
 #endif /* FEAT_GUI_TABLINE */
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "motion-notify-event",
+		     G_CALLBACK(motion_notify_event), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "button-press-event",
+		     G_CALLBACK(button_press_event), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "button-release-event",
+		     G_CALLBACK(button_release_event), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "scroll-event",
+		     G_CALLBACK(&scroll_event), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "motion_notify_event",
 		       GTK_SIGNAL_FUNC(motion_notify_event), NULL);
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "button_press_event",
@@ -3532,19 +4225,32 @@ gui_mch_init(void)
 		       GTK_SIGNAL_FUNC(button_release_event), NULL);
     g_signal_connect(G_OBJECT(gui.drawarea), "scroll_event",
 		     G_CALLBACK(&scroll_event), NULL);
+#endif
 
     /*
      * Add selection handler functions.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "selection-clear-event",
+		     G_CALLBACK(selection_clear_event), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "selection-received",
+		     G_CALLBACK(selection_received_cb), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_clear_event",
 		       GTK_SIGNAL_FUNC(selection_clear_event), NULL);
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_received",
 		       GTK_SIGNAL_FUNC(selection_received_cb), NULL);
+#endif
 
     gui_gtk_set_selection_targets();
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "selection-get",
+		     G_CALLBACK(selection_get_cb), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_get",
 		       GTK_SIGNAL_FUNC(selection_get_cb), NULL);
+#endif
 
     /* Pretend we don't have input focus, we will get an event if we do. */
     gui.in_focus = FALSE;
@@ -3572,6 +4278,67 @@ gui_mch_forked(void)
 }
 #endif /* FEAT_GUI_GNOME && FEAT_SESSION */
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gui_gtk_get_rgb_from_pixel(guint32 pixel, GdkRGBA *result)
+{
+    GdkVisual * const visual = gtk_widget_get_visual(gui.drawarea);
+    guint32 r_mask, g_mask, b_mask;
+    gint r_shift, g_shift, b_shift;
+
+    if (visual == NULL)
+    {
+	result->red = 0.0;
+	result->green = 0.0;
+	result->blue = 0.0;
+	result->alpha = 0.0;
+	return;
+    }
+
+    gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift, NULL);
+    gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift, NULL);
+    gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift, NULL);
+
+    result->red = ((pixel & r_mask) >> r_shift) / 255.0;
+    result->green = ((pixel & g_mask) >> g_shift) / 255.0;
+    result->blue = ((pixel & b_mask) >> b_shift) / 255.0;
+    result->alpha = 1.0;
+}
+
+/* Convert a GdRGBA into a pixel value using drawarea's visual */
+    static guint32
+gui_gtk_get_pixel_from_rgb(const GdkRGBA *rgba)
+{
+    GdkVisual * const visual = gtk_widget_get_visual(gui.drawarea);
+    guint32 r_mask, g_mask, b_mask;
+    gint r_shift, g_shift, b_shift;
+    guint32 r, g, b;
+
+    if (visual == NULL)
+	return 0;
+
+    gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift, NULL);
+    gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift, NULL);
+    gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift, NULL);
+
+    r = rgba->red * 65535;
+    g = rgba->green * 65535;
+    b = rgba->blue * 65535;
+
+    return ((r << r_shift) & r_mask) |
+	   ((g << g_shift) & g_mask) |
+	   ((b << b_shift) & b_mask);
+}
+
+    static void
+set_cairo_source_rgb_from_pixel(cairo_t *cr, guint32 pixel)
+{
+    GdkRGBA result;
+    gui_gtk_get_rgb_from_pixel(pixel, &result);
+    cairo_set_source_rgb(cr, result.red, result.green, result.blue);
+}
+#endif /* GTK_CHECK_VERSION(3,0,0) */
+
 /*
  * Called when the foreground or background color has been changed.
  * This used to change the graphics contexts directly but we are
@@ -3580,12 +4347,39 @@ gui_mch_forked(void)
     void
 gui_mch_new_colors(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkWindow * const da_win = gtk_widget_get_window(gui.drawarea);
+
+    if (gui.drawarea != NULL && gtk_widget_get_window(gui.drawarea) != NULL)
+#else
     if (gui.drawarea != NULL && gui.drawarea->window != NULL)
-    {
+#endif
+    {
+#if GTK_CHECK_VERSION(3,4,0)
+	GdkRGBA color;
+
+	gui_gtk_get_rgb_from_pixel(gui.back_pixel, &color);
+	{
+	    cairo_pattern_t * const pat = cairo_pattern_create_rgba(
+		    color.red, color.green, color.blue, color.alpha);
+	    if (pat != NULL)
+	    {
+		gdk_window_set_background_pattern(da_win, pat);
+		cairo_pattern_destroy(pat);
+	    }
+	    else
+		gdk_window_set_background_rgba(da_win, &color);
+	}
+#else /* !GTK_CHECK_VERSION(3,4,0) */
 	GdkColor color = { 0, 0, 0, 0 };
 
 	color.pixel = gui.back_pixel;
+# if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_set_background(da_win, &color);
+# else
 	gdk_window_set_background(gui.drawarea->window, &color);
+# endif
+#endif /* !GTK_CHECK_VERSION(3,4,0) */
     }
 }
 
@@ -3618,8 +4412,13 @@ form_configure_event(GtkWidget *widget U
  * We can't do much more here than to trying to preserve what had been done,
  * since the window is already inevitably going away.
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+mainwin_destroy_cb(GObject *object UNUSED, gpointer data UNUSED)
+#else
     static void
 mainwin_destroy_cb(GtkObject *object UNUSED, gpointer data UNUSED)
+#endif
 {
     /* Don't write messages to the GUI anymore */
     full_screen = FALSE;
@@ -3799,8 +4598,13 @@ gui_mch_open(void)
      * changed them). */
     highlight_gui_started();	/* re-init colors and fonts */
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.mainwin), "destroy",
+		     G_CALLBACK(mainwin_destroy_cb), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "destroy",
 		       GTK_SIGNAL_FUNC(mainwin_destroy_cb), NULL);
+#endif
 
 #ifdef FEAT_HANGULIN
     hangul_keyboard_set();
@@ -3816,15 +4620,25 @@ gui_mch_open(void)
      * manager upon us and should not interfere with what VIM is requesting
      * upon startup.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.formwin), "configure-event",
+		     G_CALLBACK(form_configure_event), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.formwin), "configure_event",
 		       GTK_SIGNAL_FUNC(form_configure_event), NULL);
+#endif
 
 #ifdef FEAT_DND
     /* Set up for receiving DND items. */
     gui_gtk_set_dnd_targets();
 
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "drag-data-received",
+		     G_CALLBACK(drag_data_received_cb), NULL);
+# else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "drag_data_received",
 		       GTK_SIGNAL_FUNC(drag_data_received_cb), NULL);
+# endif
 #endif
 
 	/* With GTK+ 2, we need to iconify the window before calling show()
@@ -3901,7 +4715,8 @@ gui_mch_set_winpos(int x, int y)
     gtk_window_move(GTK_WINDOW(gui.mainwin), x, y);
 }
 
-#if 0
+#if !GTK_CHECK_VERSION(3,0,0)
+# if 0
 static int resize_idle_installed = FALSE;
 /*
  * Idle handler to force resize.  Used by gui_mch_set_shellsize() to ensure
@@ -3937,7 +4752,8 @@ force_shell_resize_idle(gpointer data)
     resize_idle_installed = FALSE;
     return FALSE; /* don't call me again */
 }
-#endif
+# endif
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 /*
  * Return TRUE if the main window is maximized.
@@ -3945,9 +4761,15 @@ force_shell_resize_idle(gpointer data)
     int
 gui_mch_maximized(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    return (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL
+	    && (gdk_window_get_state(gtk_widget_get_window(gui.mainwin))
+					       & GDK_WINDOW_STATE_MAXIMIZED));
+#else
     return (gui.mainwin != NULL && gui.mainwin->window != NULL
 	    && (gdk_window_get_state(gui.mainwin->window)
 					       & GDK_WINDOW_STATE_MAXIMIZED));
+#endif
 }
 
 /*
@@ -4002,14 +4824,16 @@ gui_mch_set_shellsize(int width, int hei
     else
 	update_window_manager_hints(width, height);
 
-# if 0
+# if !GTK_CHECK_VERSION(3,0,0)
+#  if 0
     if (!resize_idle_installed)
     {
 	g_idle_add_full(GDK_PRIORITY_EVENTS + 10,
 			&force_shell_resize_idle, NULL, NULL);
 	resize_idle_installed = TRUE;
     }
-# endif
+#  endif
+# endif /* !GTK_CHECK_VERSION(3,0,0) */
     /*
      * Wait until all events are processed to prevent a crash because the
      * real size of the drawing area doesn't reflect Vim's internal ideas.
@@ -4084,7 +4908,11 @@ gui_mch_enable_menu(int showit)
 	widget = gui.menubar;
 
     /* Do not disable the menu while starting up, otherwise F10 doesn't work. */
+# if GTK_CHECK_VERSION(3,0,0)
+    if (!showit != !gtk_widget_get_visible(widget) && !gui.starting)
+# else
     if (!showit != !GTK_WIDGET_VISIBLE(widget) && !gui.starting)
+# endif
     {
 	if (showit)
 	    gtk_widget_show(widget);
@@ -4115,7 +4943,11 @@ gui_mch_show_toolbar(int showit)
     if (showit)
 	set_toolbar_style(GTK_TOOLBAR(gui.toolbar));
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (!showit != !gtk_widget_get_visible(widget))
+# else
     if (!showit != !GTK_WIDGET_VISIBLE(widget))
+# endif
     {
 	if (showit)
 	    gtk_widget_show(widget);
@@ -4200,6 +5032,17 @@ gui_mch_adjust_charheight(void)
     return OK;
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+/* Callback function used in gui_mch_font_dialog() */
+    static gboolean
+font_filter(const PangoFontFamily *family,
+	    const PangoFontFace   *face UNUSED,
+	    gpointer		   data UNUSED)
+{
+    return pango_font_family_is_monospace((PangoFontFamily *)family);
+}
+#endif
+
 /*
  * Put up a font dialog and return the selected font name in allocated memory.
  * "oldval" is the previous value.  Return NULL when cancelled.
@@ -4217,7 +5060,13 @@ gui_mch_font_dialog(char_u *oldval)
     char_u	*fontname = NULL;
     char_u	*oldname;
 
+#if GTK_CHECK_VERSION(3,2,0)
+    dialog = gtk_font_chooser_dialog_new(NULL, NULL);
+    gtk_font_chooser_set_filter_func(GTK_FONT_CHOOSER(dialog), font_filter,
+	    NULL, NULL);
+#else
     dialog = gtk_font_selection_dialog_new(NULL);
+#endif
 
     gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gui.mainwin));
     gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
@@ -4244,15 +5093,25 @@ gui_mch_font_dialog(char_u *oldval)
 	    }
 	}
 
+#if GTK_CHECK_VERSION(3,2,0)
+	gtk_font_chooser_set_font(
+		GTK_FONT_CHOOSER(dialog), (const gchar *)oldname);
+#else
 	gtk_font_selection_dialog_set_font_name(
 		GTK_FONT_SELECTION_DIALOG(dialog), (const char *)oldname);
+#endif
 
 	if (oldname != oldval)
 	    vim_free(oldname);
     }
     else
+#if GTK_CHECK_VERSION(3,2,0)
+	gtk_font_chooser_set_font(
+		GTK_FONT_CHOOSER(dialog), DEFAULT_FONT);
+#else
 	gtk_font_selection_dialog_set_font_name(
 		GTK_FONT_SELECTION_DIALOG(dialog), DEFAULT_FONT);
+#endif
 
     response = gtk_dialog_run(GTK_DIALOG(dialog));
 
@@ -4260,8 +5119,12 @@ gui_mch_font_dialog(char_u *oldval)
     {
 	char *name;
 
+#if GTK_CHECK_VERSION(3,2,0)
+	name = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
+#else
 	name = gtk_font_selection_dialog_get_font_name(
 			    GTK_FONT_SELECTION_DIALOG(dialog));
+#endif
 	if (name != NULL)
 	{
 	    char_u  *p;
@@ -4636,17 +5499,29 @@ gui_mch_get_color(char_u *name)
 
     while (name != NULL)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	GdkRGBA     color;
+#else
 	GdkColor    color;
+#endif
 	int	    parsed;
 	int	    i;
 
+#if GTK_CHECK_VERSION(3,0,0)
+	parsed = gdk_rgba_parse(&color, (const gchar *)name);
+#else
 	parsed = gdk_color_parse((const char *)name, &color);
+#endif
 
 	if (parsed)
 	{
+#if GTK_CHECK_VERSION(3,0,0)
+	    return (guicolor_T)gui_gtk_get_pixel_from_rgb(&color);
+#else
 	    gdk_colormap_alloc_color(gtk_widget_get_colormap(gui.drawarea),
 				     &color, FALSE, TRUE);
 	    return (guicolor_T)color.pixel;
+#endif
 	}
 	/* add a few builtin names and try again */
 	for (i = 0; ; ++i)
@@ -4838,12 +5713,26 @@ setup_zero_width_cluster(PangoItem *item
 	glyph->geometry.x_offset = -width + MAX(0, width - ink_rect.width) / 2;
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+draw_glyph_string(int row, int col, int num_cells, int flags,
+		  PangoFont *font, PangoGlyphString *glyphs,
+		  cairo_t *cr)
+#else
     static void
 draw_glyph_string(int row, int col, int num_cells, int flags,
 		  PangoFont *font, PangoGlyphString *glyphs)
+#endif
 {
     if (!(flags & DRAW_TRANSP))
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	set_cairo_source_rgb_from_pixel(cr, gui.bgcolor->pixel);
+	cairo_rectangle(cr,
+			FILL_X(col), FILL_Y(row),
+			num_cells * gui.char_width, gui.char_height);
+	cairo_fill(cr);
+#else
 	gdk_gc_set_foreground(gui.text_gc, gui.bgcolor);
 
 	gdk_draw_rectangle(gui.drawarea->window,
@@ -4853,8 +5742,14 @@ draw_glyph_string(int row, int col, int 
 			   FILL_Y(row),
 			   num_cells * gui.char_width,
 			   gui.char_height);
-    }
-
+#endif
+    }
+
+#if GTK_CHECK_VERSION(3,0,0)
+    set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+    cairo_move_to(cr, TEXT_X(col), TEXT_Y(row));
+    pango_cairo_show_glyph_string(cr, font, glyphs);
+#else
     gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
 
     gdk_draw_glyphs(gui.drawarea->window,
@@ -4863,22 +5758,36 @@ draw_glyph_string(int row, int col, int 
 		    TEXT_X(col),
 		    TEXT_Y(row),
 		    glyphs);
+#endif
 
     /* redraw the contents with an offset of 1 to emulate bold */
     if ((flags & DRAW_BOLD) && !gui.font_can_bold)
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+	set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+	cairo_move_to(cr, TEXT_X(col) + 1, TEXT_Y(row));
+	pango_cairo_show_glyph_string(cr, font, glyphs);
+    }
+#else
 	gdk_draw_glyphs(gui.drawarea->window,
 			gui.text_gc,
 			font,
 			TEXT_X(col) + 1,
 			TEXT_Y(row),
 			glyphs);
+#endif
 }
 
 /*
  * Draw underline and undercurl at the bottom of the character cell.
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+draw_under(int flags, int row, int col, int cells, cairo_t *cr)
+#else
     static void
 draw_under(int flags, int row, int col, int cells)
+#endif
 {
     int			i;
     int			offset;
@@ -4888,6 +5797,17 @@ draw_under(int flags, int row, int col, 
     /* Undercurl: draw curl at the bottom of the character cell. */
     if (flags & DRAW_UNDERC)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	cairo_set_line_width(cr, 1.0);
+	cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+	set_cairo_source_rgb_from_pixel(cr, gui.spcolor->pixel);
+	for (i = FILL_X(col); i < FILL_X(col + cells); ++i)
+	{
+	    offset = val[i % 8];
+	    cairo_line_to(cr, i, y - offset + 0.5);
+	}
+	cairo_stroke(cr);
+#else
 	gdk_gc_set_foreground(gui.text_gc, gui.spcolor);
 	for (i = FILL_X(col); i < FILL_X(col + cells); ++i)
 	{
@@ -4895,6 +5815,7 @@ draw_under(int flags, int row, int col, 
 	    gdk_draw_point(gui.drawarea->window, gui.text_gc, i, y - offset);
 	}
 	gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
+#endif
     }
 
     /* Underline: draw a line at the bottom of the character cell. */
@@ -4904,9 +5825,20 @@ draw_under(int flags, int row, int col, 
 	 * Otherwise put the line just below the character. */
 	if (p_linespace > 1)
 	    y -= p_linespace - 1;
+#if GTK_CHECK_VERSION(3,0,0)
+	{
+	    cairo_set_line_width(cr, 1.0);
+	    cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+	    set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+	    cairo_move_to(cr, FILL_X(col), y + 0.5);
+	    cairo_line_to(cr, FILL_X(col + cells), y + 0.5);
+	    cairo_stroke(cr);
+	}
+#else
 	gdk_draw_line(gui.drawarea->window, gui.text_gc,
 		      FILL_X(col), y,
 		      FILL_X(col + cells) - 1, y);
+#endif
     }
 }
 
@@ -4922,8 +5854,15 @@ gui_gtk2_draw_string(int row, int col, c
     int			convlen;
     char_u		*sp, *bp;
     int			plen;
-
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_t		*cr;
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.text_context == NULL || gtk_widget_get_window(gui.drawarea) == NULL)
+#else
     if (gui.text_context == NULL || gui.drawarea->window == NULL)
+#endif
 	return len;
 
     if (output_conv.vc_type != CONV_NONE)
@@ -4976,8 +5915,14 @@ gui_gtk2_draw_string(int row, int col, c
     area.width	= gui.num_cols * gui.char_width;
     area.height = gui.char_height;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cr = cairo_create(gui.surface);
+    cairo_rectangle(cr, area.x, area.y, area.width, area.height);
+    cairo_clip(cr);
+#else
     gdk_gc_set_clip_origin(gui.text_gc, 0, 0);
     gdk_gc_set_clip_rectangle(gui.text_gc, &area);
+#endif
 
     glyphs = pango_glyph_string_new();
 
@@ -5004,7 +5949,11 @@ gui_gtk2_draw_string(int row, int col, c
 	    glyphs->log_clusters[i] = i;
 	}
 
+#if GTK_CHECK_VERSION(3,0,0)
+	draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs, cr);
+#else
 	draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs);
+#endif
 
 	column_offset = len;
     }
@@ -5162,8 +6111,14 @@ not_ascii:
 	    }
 
 	    /*** Aaaaand action! ***/
+#if GTK_CHECK_VERSION(3,0,0)
+	    draw_glyph_string(row, col + column_offset, item_cells,
+			      flags, item->analysis.font, glyphs,
+			      cr);
+#else
 	    draw_glyph_string(row, col + column_offset, item_cells,
 			      flags, item->analysis.font, glyphs);
+#endif
 
 	    pango_item_free(item);
 
@@ -5175,12 +6130,23 @@ not_ascii:
 
 skipitall:
     /* Draw underline and undercurl. */
+#if GTK_CHECK_VERSION(3,0,0)
+    draw_under(flags, row, col, column_offset, cr);
+#else
     draw_under(flags, row, col, column_offset);
+#endif
 
     pango_glyph_string_free(glyphs);
     vim_free(conv_buf);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_destroy(cr);
+    if (!gui.by_signal)
+	gdk_window_invalidate_rect(gtk_widget_get_window(gui.drawarea),
+		&area, FALSE);
+#else
     gdk_gc_set_clip_rectangle(gui.text_gc, NULL);
+#endif
 
     return column_offset;
 }
@@ -5207,10 +6173,19 @@ gui_mch_haskey(char_u *name)
     int
 gui_get_x11_windis(Window *win, Display **dis)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL)
+#else
     if (gui.mainwin != NULL && gui.mainwin->window != NULL)
-    {
+#endif
+    {
+#if GTK_CHECK_VERSION(3,0,0)
+	*dis = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin));
+	*win = GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin));
+#else
 	*dis = GDK_WINDOW_XDISPLAY(gui.mainwin->window);
 	*win = GDK_WINDOW_XWINDOW(gui.mainwin->window);
+#endif
 	return OK;
     }
 
@@ -5226,8 +6201,13 @@ gui_get_x11_windis(Window *win, Display 
     Display *
 gui_mch_get_display(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL)
+	return GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin));
+#else
     if (gui.mainwin != NULL && gui.mainwin->window != NULL)
 	return GDK_WINDOW_XDISPLAY(gui.mainwin->window);
+#endif
     else
 	return NULL;
 }
@@ -5239,7 +6219,11 @@ gui_mch_beep(void)
 #ifdef HAVE_GTK_MULTIHEAD
     GdkDisplay *display;
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin))
+# else
     if (gui.mainwin != NULL && GTK_WIDGET_REALIZED(gui.mainwin))
+# endif
 	display = gtk_widget_get_display(gui.mainwin);
     else
 	display = gdk_display_get_default();
@@ -5254,6 +6238,10 @@ gui_mch_beep(void)
     void
 gui_mch_flash(int msec)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    /* TODO Replace GdkGC with Cairo */
+    (void)msec;
+#else
     GdkGCValues	values;
     GdkGC	*invert_gc;
 
@@ -5293,6 +6281,7 @@ gui_mch_flash(int msec)
 		       FILL_Y((int)Rows) + gui.border_offset);
 
     gdk_gc_destroy(invert_gc);
+#endif
 }
 
 /*
@@ -5301,6 +6290,10 @@ gui_mch_flash(int msec)
     void
 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    /* TODO Replace GdkGC with Cairo */
+    (void)r; (void)c; (void)nr; (void)nc;
+#else
     GdkGCValues values;
     GdkGC *invert_gc;
 
@@ -5322,6 +6315,7 @@ gui_mch_invert_rectangle(int r, int c, i
 		       FILL_X(c), FILL_Y(r),
 		       (nc) * gui.char_width, (nr) * gui.char_height);
     gdk_gc_destroy(invert_gc);
+#endif
 }
 
 /*
@@ -5351,19 +6345,44 @@ gui_mch_set_foreground(void)
 gui_mch_draw_hollow_cursor(guicolor_T color)
 {
     int		i = 1;
-
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_t    *cr;
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) == NULL)
+#else
     if (gui.drawarea->window == NULL)
+#endif
 	return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cr = cairo_create(gui.surface);
+#endif
+
     gui_mch_set_fg_color(color);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+#else
     gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
+#endif
     if (mb_lefthalve(gui.row, gui.col))
 	i = 2;
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_set_line_width(cr, 1.0);
+    cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+    cairo_rectangle(cr,
+	    FILL_X(gui.col) + 0.5, FILL_Y(gui.row) + 0.5,
+	    i * gui.char_width - 1, gui.char_height - 1);
+    cairo_stroke(cr);
+    cairo_destroy(cr);
+#else
     gdk_draw_rectangle(gui.drawarea->window, gui.text_gc,
 	    FALSE,
 	    FILL_X(gui.col), FILL_Y(gui.row),
 	    i * gui.char_width - 1, gui.char_height - 1);
+#endif
 }
 
 /*
@@ -5373,21 +6392,43 @@ gui_mch_draw_hollow_cursor(guicolor_T co
     void
 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) == NULL)
+#else
     if (gui.drawarea->window == NULL)
+#endif
 	return;
 
     gui_mch_set_fg_color(color);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+	cairo_t *cr;
+
+	cr = cairo_create(gui.surface);
+	set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+	cairo_rectangle(cr,
+# ifdef FEAT_RIGHTLEFT
+	    /* vertical line should be on the right of current point */
+	    CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
+# endif
+	    FILL_X(gui.col), FILL_Y(gui.row) + gui.char_height - h,
+	    w, h);
+	cairo_fill(cr);
+	cairo_destroy(cr);
+    }
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
     gdk_draw_rectangle(gui.drawarea->window, gui.text_gc,
 	    TRUE,
-#ifdef FEAT_RIGHTLEFT
+# ifdef FEAT_RIGHTLEFT
 	    /* vertical line should be on the right of current point */
 	    CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
-#endif
+# endif
 	    FILL_X(gui.col),
 	    FILL_Y(gui.row) + gui.char_height - h,
 	    w, h);
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 }
 
 
@@ -5404,7 +6445,11 @@ gui_mch_update(void)
 	g_main_context_iteration(NULL, TRUE);
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 input_timer_cb(gpointer data)
 {
     int *timed_out = (int *) data;
@@ -5473,7 +6518,11 @@ gui_mch_wait_for_chars(long wtime)
      * time */
 
     if (wtime > 0)
+#if GTK_CHECK_VERSION(3,0,0)
+	timer = g_timeout_add((guint)wtime, input_timer_cb, &timed_out);
+#else
 	timer = gtk_timeout_add((guint32)wtime, input_timer_cb, &timed_out);
+#endif
     else
 	timer = 0;
 
@@ -5507,7 +6556,11 @@ gui_mch_wait_for_chars(long wtime)
 	if (input_available())
 	{
 	    if (timer != 0 && !timed_out)
+#if GTK_CHECK_VERSION(3,0,0)
+		g_source_remove(timer);
+#else
 		gtk_timeout_remove(timer);
+#endif
 	    return OK;
 	}
     } while (wtime < 0 || !timed_out);
@@ -5531,15 +6584,24 @@ gui_mch_wait_for_chars(long wtime)
 gui_mch_flush(void)
 {
 #ifdef HAVE_GTK_MULTIHEAD
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin))
+# else
     if (gui.mainwin != NULL && GTK_WIDGET_REALIZED(gui.mainwin))
+# endif
 	gdk_display_sync(gtk_widget_get_display(gui.mainwin));
 #else
     gdk_flush(); /* historical misnomer: calls XSync(), not XFlush() */
 #endif
     /* This happens to actually do what gui_mch_flush() is supposed to do,
      * according to the comment above. */
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.drawarea != NULL && gtk_widget_get_window(gui.drawarea) != NULL)
+	gdk_window_process_updates(gtk_widget_get_window(gui.drawarea), FALSE);
+#else
     if (gui.drawarea != NULL && gui.drawarea->window != NULL)
 	gdk_window_process_updates(gui.drawarea->window, FALSE);
+#endif
 }
 
 /*
@@ -5549,13 +6611,42 @@ gui_mch_flush(void)
     void
 gui_mch_clear_block(int row1, int col1, int row2, int col2)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) == NULL)
+	return;
+#else
     GdkColor color;
 
     if (gui.drawarea->window == NULL)
 	return;
 
     color.pixel = gui.back_pixel;
-
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+	/* Add one pixel to the far right column in case a double-stroked
+	 * bold glyph may sit there. */
+	const GdkRectangle rect = {
+	    FILL_X(col1), FILL_Y(row1),
+	    (col2 - col1 + 1) * gui.char_width + (col2 == Columns - 1),
+	    (row2 - row1 + 1) * gui.char_height
+	};
+	GdkWindow * const win = gtk_widget_get_window(gui.drawarea);
+	cairo_t * const cr = cairo_create(gui.surface);
+	cairo_pattern_t * const pat = gdk_window_get_background_pattern(win);
+	if (pat != NULL)
+	    cairo_set_source(cr, pat);
+	else
+	    set_cairo_source_rgb_from_pixel(cr, gui.back_pixel);
+	gdk_cairo_rectangle(cr, &rect);
+	cairo_fill(cr);
+	cairo_destroy(cr);
+
+	if (!gui.by_signal)
+	    gdk_window_invalidate_rect(win, &rect, FALSE);
+    }
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     gdk_gc_set_foreground(gui.text_gc, &color);
 
     /* Clear one extra pixel at the far right, for when bold characters have
@@ -5565,15 +6656,44 @@ gui_mch_clear_block(int row1, int col1, 
 		       (col2 - col1 + 1) * gui.char_width
 						      + (col2 == Columns - 1),
 		       (row2 - row1 + 1) * gui.char_height);
-}
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gui_gtk_window_clear(GdkWindow *win)
+{
+    const GdkRectangle rect = {
+	0, 0, gdk_window_get_width(win), gdk_window_get_height(win)
+    };
+    cairo_t * const cr = cairo_create(gui.surface);
+    cairo_pattern_t * const pat = gdk_window_get_background_pattern(win);
+    if (pat != NULL)
+	cairo_set_source(cr, pat);
+    else
+	set_cairo_source_rgb_from_pixel(cr, gui.back_pixel);
+    gdk_cairo_rectangle(cr, &rect);
+    cairo_fill(cr);
+    cairo_destroy(cr);
+
+    if (!gui.by_signal)
+	gdk_window_invalidate_rect(win, &rect, FALSE);
+}
+#endif
 
     void
 gui_mch_clear_all(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) != NULL)
+	gui_gtk_window_clear(gtk_widget_get_window(gui.drawarea));
+#else
     if (gui.drawarea->window != NULL)
 	gdk_window_clear(gui.drawarea->window);
-}
-
+#endif
+}
+
+#if !GTK_CHECK_VERSION(3,0,0)
 /*
  * Redraw any text revealed by scrolling up/down.
  */
@@ -5610,6 +6730,27 @@ check_copy_area(void)
 
     gui_can_update_cursor();
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
+
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gui_gtk_surface_copy_rect(int dest_x, int dest_y,
+			  int src_x,  int src_y,
+			  int width,  int height)
+{
+    cairo_t * const cr = cairo_create(gui.surface);
+
+    cairo_rectangle(cr, dest_x, dest_y, width, height);
+    cairo_clip(cr);
+    cairo_push_group(cr);
+    cairo_set_source_surface(cr, gui.surface, dest_x - src_x, dest_y - src_y);
+    cairo_paint(cr);
+    cairo_pop_group_to_source(cr);
+    cairo_paint(cr);
+
+    cairo_destroy(cr);
+}
+#endif
 
 /*
  * Delete the given number of lines from the given row, scrolling up any
@@ -5618,6 +6759,26 @@ check_copy_area(void)
     void
 gui_mch_delete_lines(int row, int num_lines)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    const int ncols = gui.scroll_region_right - gui.scroll_region_left + 1;
+    const int nrows = gui.scroll_region_bot - row + 1;
+    const int src_nrows = nrows - num_lines;
+
+    gui_gtk_surface_copy_rect(
+	    FILL_X(gui.scroll_region_left), FILL_Y(row),
+	    FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
+	    gui.char_width * ncols + 1,     gui.char_height * src_nrows);
+    gui_clear_block(
+	    gui.scroll_region_bot - num_lines + 1, gui.scroll_region_left,
+	    gui.scroll_region_bot,		   gui.scroll_region_right);
+    gui_gtk3_redraw(
+	    FILL_X(gui.scroll_region_left), FILL_Y(row),
+	    gui.char_width * ncols + 1,     gui.char_height * nrows);
+    if (!gui.by_signal)
+	gtk_widget_queue_draw_area(gui.drawarea,
+		FILL_X(gui.scroll_region_left), FILL_Y(row),
+		gui.char_width * ncols + 1,	gui.char_height * nrows);
+#else
     if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
 	return;			/* Can't see the window */
 
@@ -5638,6 +6799,7 @@ gui_mch_delete_lines(int row, int num_li
 						       gui.scroll_region_left,
 		    gui.scroll_region_bot, gui.scroll_region_right);
     check_copy_area();
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 }
 
 /*
@@ -5647,6 +6809,26 @@ gui_mch_delete_lines(int row, int num_li
     void
 gui_mch_insert_lines(int row, int num_lines)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    const int ncols = gui.scroll_region_right - gui.scroll_region_left + 1;
+    const int nrows = gui.scroll_region_bot - row + 1;
+    const int src_nrows = nrows - num_lines;
+
+    gui_gtk_surface_copy_rect(
+	    FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
+	    FILL_X(gui.scroll_region_left), FILL_Y(row),
+	    gui.char_width * ncols + 1,     gui.char_height * src_nrows);
+    gui_mch_clear_block(
+	    row,		 gui.scroll_region_left,
+	    row + num_lines - 1, gui.scroll_region_right);
+    gui_gtk3_redraw(
+	    FILL_X(gui.scroll_region_left), FILL_Y(row),
+	    gui.char_width * ncols + 1,     gui.char_height * nrows);
+    if (!gui.by_signal)
+	gtk_widget_queue_draw_area(gui.drawarea,
+		FILL_X(gui.scroll_region_left), FILL_Y(row),
+		gui.char_width * ncols + 1,	gui.char_height * nrows);
+#else
     if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
 	return;			/* Can't see the window */
 
@@ -5665,6 +6847,7 @@ gui_mch_insert_lines(int row, int num_li
     gui_clear_block(row, gui.scroll_region_left,
 				row + num_lines - 1, gui.scroll_region_right);
     check_copy_area();
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 }
 
 /*
@@ -5700,7 +6883,12 @@ clip_mch_request_selection(VimClipboard 
     }
 
     /* Final fallback position - use the X CUT_BUFFER0 store */
+#if GTK_CHECK_VERSION(3,0,0)
+    yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
+	    cbd);
+#else
     yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gui.mainwin->window), cbd);
+#endif
 }
 
 /*
@@ -5758,7 +6946,11 @@ gui_mch_menu_grey(vimmenu_T *menu, int g
 
     gui_mch_menu_hidden(menu, FALSE);
     /* Be clever about bitfields versus true booleans here! */
+# if GTK_CHECK_VERSION(3,0,0)
+    if (!gtk_widget_get_sensitive(menu->id) == !grey)
+# else
     if (!GTK_WIDGET_SENSITIVE(menu->id) == !grey)
+# endif
     {
 	gtk_widget_set_sensitive(menu->id, !grey);
 	gui_mch_update();
@@ -5776,7 +6968,11 @@ gui_mch_menu_hidden(vimmenu_T *menu, int
 
     if (hidden)
     {
+# if GTK_CHECK_VERSION(3,0,0)
+	if (gtk_widget_get_visible(menu->id))
+# else
 	if (GTK_WIDGET_VISIBLE(menu->id))
+# endif
 	{
 	    gtk_widget_hide(menu->id);
 	    gui_mch_update();
@@ -5784,7 +6980,11 @@ gui_mch_menu_hidden(vimmenu_T *menu, int
     }
     else
     {
+# if GTK_CHECK_VERSION(3,0,0)
+	if (!gtk_widget_get_visible(menu->id))
+# else
 	if (!GTK_WIDGET_VISIBLE(menu->id))
+# endif
 	{
 	    gtk_widget_show(menu->id);
 	    gui_mch_update();
@@ -5812,10 +7012,14 @@ gui_mch_enable_scrollbar(scrollbar_T *sb
     if (sb->id == NULL)
 	return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_visible(sb->id, flag);
+#else
     if (flag)
 	gtk_widget_show(sb->id);
     else
 	gtk_widget_hide(sb->id);
+#endif
 
     update_window_manager_hints(0, 0);
 }
@@ -5828,8 +7032,18 @@ gui_mch_enable_scrollbar(scrollbar_T *sb
 gui_mch_get_rgb(guicolor_T pixel)
 {
     GdkColor color;
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkRGBA rgba;
+
+    gui_gtk_get_rgb_from_pixel(pixel, &rgba);
+
+    color.red = rgba.red * 65535;
+    color.green = rgba.green * 65535;
+    color.blue = rgba.blue * 65535;
+#else
     gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea),
 			     (unsigned long)pixel, &color);
+#endif
 
     return (((unsigned)color.red   & 0xff00) << 8)
 	 |  ((unsigned)color.green & 0xff00)
@@ -5842,7 +7056,11 @@ gui_mch_get_rgb(guicolor_T pixel)
     void
 gui_mch_getmouse(int *x, int *y)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    gui_gtk_get_pointer(gui.drawarea, x, y, NULL);
+#else
     gdk_window_get_pointer(gui.drawarea->window, x, y, NULL);
+#endif
 }
 
     void
@@ -5851,9 +7069,15 @@ 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...) */
+#if GTK_CHECK_VERSION(3,0,0)
+    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);
+#else
     XWarpPointer(GDK_WINDOW_XDISPLAY(gui.drawarea->window),
 		 (Window)0, GDK_WINDOW_XWINDOW(gui.drawarea->window),
 		 0, 0, 0U, 0U, x, y);
+#endif
 }
 
 
@@ -5874,10 +7098,19 @@ gui_mch_mousehide(int hide)
     if (gui.pointer_hidden != hide)
     {
 	gui.pointer_hidden = hide;
+#if GTK_CHECK_VERSION(3,0,0)
+	if (gtk_widget_get_window(gui.drawarea) && gui.blank_pointer != NULL)
+#else
 	if (gui.drawarea->window && gui.blank_pointer != NULL)
+#endif
 	{
 	    if (hide)
+#if GTK_CHECK_VERSION(3,0,0)
+		gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea),
+			gui.blank_pointer);
+#else
 		gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
+#endif
 	    else
 #ifdef FEAT_MOUSESHAPE
 		mch_set_mouse_shape(last_shape);
@@ -5919,11 +7152,20 @@ mch_set_mouse_shape(int shape)
     int		   id;
     GdkCursor	   *c;
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) == NULL)
+# else
     if (gui.drawarea->window == NULL)
+# endif
 	return;
 
     if (shape == MSHAPE_HIDE || gui.pointer_hidden)
+# if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea),
+		gui.blank_pointer);
+# else
 	gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
+# endif
     else
     {
 	if (shape >= MSHAPE_NUMBERED)
@@ -5944,8 +7186,16 @@ mch_set_mouse_shape(int shape)
 # else
 	c = gdk_cursor_new((GdkCursorType)id);
 # endif
+# if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea), c);
+# else
 	gdk_window_set_cursor(gui.drawarea->window, c);
+# endif
+# if GTK_CHECK_VERSION(3,0,0)
+	g_object_unref(G_OBJECT(c));
+# else
 	gdk_cursor_destroy(c); /* Unref, actually.  Bloody GTK+ 1. */
+# endif
     }
     if (shape != MSHAPE_HIDE)
 	last_shape = shape;
@@ -5972,7 +7222,12 @@ gui_mch_drawsign(int row, int col, int t
 
     sign = (GdkPixbuf *)sign_get_image(typenr);
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (sign != NULL && gui.drawarea != NULL
+	    && gtk_widget_get_window(gui.drawarea) != NULL)
+# else
     if (sign != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
+# endif
     {
 	int width;
 	int height;
@@ -6036,6 +7291,49 @@ gui_mch_drawsign(int row, int col, int t
 	xoffset = (width  - SIGN_WIDTH)  / 2;
 	yoffset = (height - SIGN_HEIGHT) / 2;
 
+# if GTK_CHECK_VERSION(3,0,0)
+	{
+	    cairo_t	    *cr;
+	    cairo_surface_t *bg_surf;
+	    cairo_t	    *bg_cr;
+	    cairo_surface_t *sign_surf;
+	    cairo_t	    *sign_cr;
+
+	    cr = cairo_create(gui.surface);
+
+	    bg_surf = cairo_surface_create_similar(gui.surface,
+		    cairo_surface_get_content(gui.surface),
+		    SIGN_WIDTH, SIGN_HEIGHT);
+	    bg_cr = cairo_create(bg_surf);
+	    set_cairo_source_rgb_from_pixel(bg_cr, gui.bgcolor->pixel);
+	    cairo_paint(bg_cr);
+
+	    sign_surf = cairo_surface_create_similar(gui.surface,
+		    cairo_surface_get_content(gui.surface),
+		    SIGN_WIDTH, SIGN_HEIGHT);
+	    sign_cr = cairo_create(sign_surf);
+	    gdk_cairo_set_source_pixbuf(sign_cr, sign, -xoffset, -yoffset);
+	    cairo_paint(sign_cr);
+
+	    cairo_set_operator(sign_cr, CAIRO_OPERATOR_DEST_OVER);
+	    cairo_set_source_surface(sign_cr, bg_surf, 0, 0);
+	    cairo_paint(sign_cr);
+
+	    cairo_set_source_surface(cr, sign_surf, FILL_X(col), FILL_Y(row));
+	    cairo_paint(cr);
+
+	    cairo_destroy(sign_cr);
+	    cairo_surface_destroy(sign_surf);
+	    cairo_destroy(bg_cr);
+	    cairo_surface_destroy(bg_surf);
+	    cairo_destroy(cr);
+
+	    if (!gui.by_signal)
+		gtk_widget_queue_draw_area(gui.drawarea,
+			FILL_X(col), FILL_Y(col), width, height);
+
+	}
+# else /* !GTK_CHECK_VERSION(3,0,0) */
 	gdk_gc_set_foreground(gui.text_gc, gui.bgcolor);
 
 	gdk_draw_rectangle(gui.drawarea->window,
@@ -6058,6 +7356,7 @@ gui_mch_drawsign(int row, int col, int t
 					    127,
 					    GDK_RGB_DITHER_NORMAL,
 					    0, 0);
+# endif /* !GTK_CHECK_VERSION(3,0,0) */
 	if (need_scale)
 	    g_object_unref(sign);
     }
--- a/src/if_mzsch.c
+++ b/src/if_mzsch.c
@@ -852,7 +852,11 @@ static int mz_threads_allow = 0;
 static void CALLBACK timer_proc(HWND, UINT, UINT, DWORD);
 static UINT timer_id = 0;
 #elif defined(FEAT_GUI_GTK)
+# if GTK_CHECK_VERSION(3,0,0)
+static gboolean timer_proc(gpointer);
+# else
 static gint timer_proc(gpointer);
+# endif
 static guint timer_id = 0;
 #elif defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
 static void timer_proc(XtPointer, XtIntervalId *);
@@ -892,7 +896,11 @@ static void remove_timer(void);
     static void CALLBACK
 timer_proc(HWND hwnd UNUSED, UINT uMsg UNUSED, UINT idEvent UNUSED, DWORD dwTime UNUSED)
 # elif defined(FEAT_GUI_GTK)
+#  if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#  else
     static gint
+#  endif
 timer_proc(gpointer data UNUSED)
 # elif defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
     static void
@@ -919,7 +927,11 @@ setup_timer(void)
 # if defined(FEAT_GUI_W32)
     timer_id = SetTimer(NULL, 0, p_mzq, timer_proc);
 # elif defined(FEAT_GUI_GTK)
+#  if GTK_CHECK_VERSION(3,0,0)
+    timer_id = g_timeout_add((guint)p_mzq, (GSourceFunc)timer_proc, NULL);
+#  else
     timer_id = gtk_timeout_add((guint32)p_mzq, (GtkFunction)timer_proc, NULL);
+#  endif
 # elif defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
     timer_id = XtAppAddTimeOut(app_context, p_mzq, timer_proc, NULL);
 # elif defined(FEAT_GUI_MAC)
@@ -935,7 +947,11 @@ remove_timer(void)
 # if defined(FEAT_GUI_W32)
     KillTimer(NULL, timer_id);
 # elif defined(FEAT_GUI_GTK)
+#  if GTK_CHECK_VERSION(3,0,0)
+    g_source_remove(timer_id);
+#  else
     gtk_timeout_remove(timer_id);
+#  endif
 # elif defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
     XtRemoveTimeOut(timer_id);
 # elif defined(FEAT_GUI_MAC)
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -109,7 +109,11 @@
 #endif
 
 #if defined(FEAT_GUI_GTK) && defined(FEAT_XIM)
-# include <gdk/gdkkeysyms.h>
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # ifdef WIN3264
 #  include <gdk/gdkwin32.h>
 # else
@@ -4941,7 +4945,11 @@ xim_init(void)
 #endif
 
     g_return_if_fail(gui.drawarea != NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+    g_return_if_fail(gtk_widget_get_window(gui.drawarea) != NULL);
+#else
     g_return_if_fail(gui.drawarea->window != NULL);
+#endif
 
     xic = gtk_im_multicontext_new();
     g_object_ref(xic);
@@ -4955,7 +4963,11 @@ xim_init(void)
     g_signal_connect(G_OBJECT(xic), "preedit_end",
 		     G_CALLBACK(&im_preedit_end_cb), NULL);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_im_context_set_client_window(xic, gtk_widget_get_window(gui.drawarea));
+#else
     gtk_im_context_set_client_window(xic, gui.drawarea->window);
+#endif
 }
 
     void
@@ -5054,12 +5066,21 @@ im_synthesize_keypress(unsigned int keyv
 
 #  ifdef HAVE_GTK_MULTIHEAD
     event = (GdkEventKey *)gdk_event_new(GDK_KEY_PRESS);
+#   if GTK_CHECK_VERSION(3,0,0)
+    g_object_ref(gtk_widget_get_window(gui.drawarea));
+					/* unreffed by gdk_event_free() */
+#   else
     g_object_ref(gui.drawarea->window); /* unreffed by gdk_event_free() */
+#   endif
 #  else
     event = (GdkEventKey *)g_malloc0((gulong)sizeof(GdkEvent));
     event->type = GDK_KEY_PRESS;
 #  endif
+#  if GTK_CHECK_VERSION(3,0,0)
+    event->window = gtk_widget_get_window(gui.drawarea);
+#  else
     event->window = gui.drawarea->window;
+#  endif
     event->send_event = TRUE;
     event->time = GDK_CURRENT_TIME;
     event->state  = state;
--- a/src/netbeans.c
+++ b/src/netbeans.c
@@ -3055,17 +3055,56 @@ netbeans_draw_multisign_indicator(int ro
     int i;
     int y;
     int x;
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_t *cr = NULL;
+#else
     GdkDrawable *drawable = gui.drawarea->window;
+#endif
 
     if (!NETBEANS_OPEN)
 	return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cr = cairo_create(gui.surface);
+    {
+	GdkVisual *visual = NULL;
+	guint32 r_mask, g_mask, b_mask;
+	gint r_shift, g_shift, b_shift;
+
+	visual = gdk_window_get_visual(gtk_widget_get_window(gui.drawarea));
+	if (visual != NULL)
+	{
+	    gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift, NULL);
+	    gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift, NULL);
+	    gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift, NULL);
+
+	    cairo_set_source_rgb(cr,
+		    ((gui.fgcolor->red & r_mask) >> r_shift) / 255.0,
+		    ((gui.fgcolor->green & g_mask) >> g_shift) / 255.0,
+		    ((gui.fgcolor->blue & b_mask) >> b_shift) / 255.0);
+	}
+    }
+#endif
+
     x = 0;
     y = row * gui.char_height + 2;
 
     for (i = 0; i < gui.char_height - 3; i++)
+#if GTK_CHECK_VERSION(3,0,0)
+	cairo_rectangle(cr, x+2, y++, 1, 1);
+#else
 	gdk_draw_point(drawable, gui.text_gc, x+2, y++);
-
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_rectangle(cr, x+0, y, 1, 1);
+    cairo_rectangle(cr, x+2, y, 1, 1);
+    cairo_rectangle(cr, x+4, y++, 1, 1);
+    cairo_rectangle(cr, x+1, y, 1, 1);
+    cairo_rectangle(cr, x+2, y, 1, 1);
+    cairo_rectangle(cr, x+3, y++, 1, 1);
+    cairo_rectangle(cr, x+2, y, 1, 1);
+#else
     gdk_draw_point(drawable, gui.text_gc, x+0, y);
     gdk_draw_point(drawable, gui.text_gc, x+2, y);
     gdk_draw_point(drawable, gui.text_gc, x+4, y++);
@@ -3073,6 +3112,11 @@ netbeans_draw_multisign_indicator(int ro
     gdk_draw_point(drawable, gui.text_gc, x+2, y);
     gdk_draw_point(drawable, gui.text_gc, x+3, y++);
     gdk_draw_point(drawable, gui.text_gc, x+2, y);
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_destroy(cr);
+#endif
 }
 #endif /* FEAT_GUI_GTK */
 
--- a/src/structs.h
+++ b/src/structs.h
@@ -2736,7 +2736,9 @@ struct VimMenu
 #ifdef FEAT_GUI_GTK
     GtkWidget	*id;		    /* Manage this to enable item */
     GtkWidget	*submenu_id;	    /* If this is submenu, add children here */
+# if defined(GTK_CHECK_VERSION) && !GTK_CHECK_VERSION(3,4,0)
     GtkWidget	*tearoff_handle;
+# endif
     GtkWidget   *label;		    /* Used by "set wak=" code. */
 #endif
 #ifdef FEAT_GUI_MOTIF
--- a/src/version.c
+++ b/src/version.c
@@ -749,6 +749,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1402,
+/**/
     1401,
 /**/
     1400,
@@ -3824,11 +3826,15 @@ list_version(void)
     MSG_PUTS(_("without GUI."));
 #else
 # ifdef FEAT_GUI_GTK
-#  ifdef FEAT_GUI_GNOME
-    MSG_PUTS(_("with GTK2-GNOME GUI."));
+#  ifdef USE_GTK3
+    MSG_PUTS(_("with GTK3 GUI."));
 #  else
-    MSG_PUTS(_("with GTK2 GUI."));
-#  endif
+#   ifdef FEAT_GUI_GNOME
+     MSG_PUTS(_("with GTK2-GNOME GUI."));
+#   else
+     MSG_PUTS(_("with GTK2 GUI."));
+#   endif
+# endif
 # else
 #  ifdef FEAT_GUI_MOTIF
     MSG_PUTS(_("with X11-Motif GUI."));